[vlc-devel] [PATCH 1/4] stream_filter/icy: Add ICY metadata stream filter

Marvin Scholz epirat07 at gmail.com
Wed Nov 1 05:16:33 CET 2017


---
 modules/stream_filter/Makefile.am |   3 +
 modules/stream_filter/icy.c       | 254 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 257 insertions(+)
 create mode 100644 modules/stream_filter/icy.c

diff --git a/modules/stream_filter/Makefile.am b/modules/stream_filter/Makefile.am
index 50d99faac9..d125556452 100644
--- a/modules/stream_filter/Makefile.am
+++ b/modules/stream_filter/Makefile.am
@@ -22,6 +22,9 @@ if HAVE_ZLIB
 stream_filter_LTLIBRARIES += libinflate_plugin.la
 endif
 
+libicy_plugin_la_SOURCES = stream_filter/icy.c
+stream_filter_LTLIBRARIES += libicy_plugin.la
+
 libprefetch_plugin_la_SOURCES = stream_filter/prefetch.c
 libprefetch_plugin_la_LIBADD = $(LIBPTHREAD)
 if !HAVE_WINSTORE
diff --git a/modules/stream_filter/icy.c b/modules/stream_filter/icy.c
new file mode 100644
index 0000000000..22e586128c
--- /dev/null
+++ b/modules/stream_filter/icy.c
@@ -0,0 +1,254 @@
+/*****************************************************************************
+ * icy.c: ICY metadata extraction module for VLC
+ *****************************************************************************
+ * Copyright (C) 2017 VLC authors and VideoLAN
+ *
+ * Authors: Marvin Scholz <epirat07 at gmail dot com>
+ * 
+ * This file reuses code from the old http access module,
+ * Authors: Laurent Aimar <fenrir at via.ecp.fr>
+ *          Christophe Massiot <massiot at via.ecp.fr>
+ *          RĂ©mi Denis-Courmont <rem # videolan.org>
+ *          Antoine Cellerier <dionoea at videolan dot org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_stream.h>
+#include <vlc_input.h>
+#include <vlc_charset.h>
+
+struct stream_sys_t
+{
+    int      icy_metaint;
+    char     *icy_title;
+
+    uint64_t offset;
+};
+
+static void ParseICYData(stream_t *stream, char *data)
+{
+    stream_sys_t *sys = stream->p_sys;
+    char *start = strcasestr(data, "StreamTitle=");
+
+    if (!start)
+        return;
+
+    start += strlen("StreamTitle=");
+    if (*start == '\'' || *start == '"')
+    {
+        char closing[] = { start[0], ';', '\0' };
+        char *end = strstr(&start[1], closing);
+        if (!end)
+            end = strchr(&start[1], ';');
+
+        if (end)
+            *end = '\0';
+    }
+    else
+    {
+        char *end = strchr( &start[1], ';' );
+        if (end)
+            *end = '\0';
+    }
+
+    if (!sys->icy_title || strcmp(sys->icy_title, &start[1]))
+    {
+        free(sys->icy_title);
+        char *tmp = strdup(&start[1]);
+        sys->icy_title = EnsureUTF8(tmp);
+        if (!sys->icy_title)
+            free(tmp);
+
+        msg_Dbg(stream, "New Icy-Title=%s", sys->icy_title);
+        input_thread_t *input = stream->p_input;
+        if (input)
+        {
+            input_item_t *input_item = input_GetItem(input);
+            if (input_item)
+                input_item_SetMeta( input_item, vlc_meta_NowPlaying, sys->icy_title );
+        }
+    }
+}
+
+static int ReadICYMeta( stream_t *stream )
+{
+    uint8_t buffer;
+    size_t data_read;
+    char *icy_data;
+
+    /* Read meta data length */
+    if (vlc_stream_Read(stream->p_source, &buffer, 1) != 1)
+        return VLC_EGENERIC;
+
+    const uint8_t icy_size = buffer << 4;
+
+    if (icy_size == 0)
+        return VLC_SUCCESS;
+
+    icy_data = malloc(icy_size + 1);
+    for (data_read = 0; data_read < icy_size;)
+    {
+        size_t tmp_read;
+
+        tmp_read = vlc_stream_Read(stream->p_source, &icy_data[data_read], icy_size - data_read);
+
+        if (tmp_read <= 0)
+        {
+            free(icy_data);
+            return VLC_EGENERIC;
+        }
+        data_read += tmp_read;
+    }
+    icy_data[data_read] = '\0'; /* Just in case */
+
+    /* Now parse the meta */
+    ParseICYData(stream, icy_data);
+
+    free(icy_data);
+
+    return VLC_SUCCESS;
+}
+
+static ssize_t Read(stream_t *stream, void *buf, size_t buflen)
+{
+    stream_sys_t *p_sys = stream->p_sys;
+    int i_read = 0;
+
+    if (p_sys->icy_metaint > 0 && p_sys->offset > 0)
+    {
+        int i_next = p_sys->icy_metaint - (p_sys->offset % p_sys->icy_metaint);
+
+        if (i_next == p_sys->icy_metaint)
+        {
+            p_sys->offset = 0;
+            if (ReadICYMeta(stream))
+                return 0;
+        }
+        if (buflen > (size_t)i_next)
+            buflen = i_next;
+    }
+
+    i_read = vlc_stream_Read(stream->p_source, buf, buflen);
+
+    if (i_read < 0)
+        return -1;
+
+    p_sys->offset += i_read;
+    return i_read;
+}
+
+static int ReadDir(stream_t *stream, input_item_node_t *node)
+{
+    (void) stream; (void) node;
+    return VLC_EGENERIC;
+}
+
+static int Seek(stream_t *stream, uint64_t offset)
+{
+    (void) stream; (void) offset;
+    return -1;
+}
+
+static int Control(stream_t *stream, int query, va_list args)
+{
+    switch (query)
+    {
+        case STREAM_CAN_SEEK:
+        case STREAM_CAN_FASTSEEK:
+            *va_arg(args, bool *) = false;
+            break;
+        case STREAM_CAN_PAUSE:
+        case STREAM_CAN_CONTROL_PACE:
+        case STREAM_GET_PTS_DELAY:
+        case STREAM_GET_META:
+        case STREAM_GET_CONTENT_TYPE:
+        case STREAM_GET_SIGNAL:
+        case STREAM_SET_PAUSE_STATE:
+            return vlc_stream_vaControl(stream->p_source, query, args);
+        case STREAM_IS_DIRECTORY:
+        case STREAM_GET_SIZE:
+        case STREAM_GET_TITLE_INFO:
+        case STREAM_GET_TITLE:
+        case STREAM_GET_SEEKPOINT:
+        case STREAM_SET_TITLE:
+        case STREAM_SET_SEEKPOINT:
+        case STREAM_SET_PRIVATE_ID_STATE:
+        case STREAM_SET_PRIVATE_ID_CA:
+        case STREAM_GET_PRIVATE_ID_STATE:
+            return VLC_EGENERIC;
+        default:
+            msg_Err(stream, "unimplemented query (%d) in control", query);
+            return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+static int Open(vlc_object_t *obj)
+{
+    stream_t *stream = (stream_t *)obj;
+
+    int metaint = var_InheritInteger(stream, "icy-metaint");
+    if (metaint < 0)
+        return VLC_EGENERIC;
+
+    config_PutInt(stream, "icy-metaint", -1);
+
+    stream_sys_t *sys = malloc(sizeof (*sys));
+    if (unlikely(sys == NULL))
+        return VLC_ENOMEM;
+
+    sys->offset = 0;
+    sys->icy_metaint = metaint;
+    sys->icy_title = NULL;
+
+    stream->p_sys = sys;
+    stream->pf_read = Read;
+    stream->pf_readdir = ReadDir;
+    stream->pf_seek = Seek;
+    stream->pf_control = Control;
+
+    return VLC_SUCCESS;
+}
+
+static void Close (vlc_object_t *obj)
+{
+    stream_t *stream = (stream_t *)obj;
+    stream_sys_t *sys = stream->p_sys;
+    free(sys->icy_title);
+    free(sys);
+}
+
+vlc_module_begin()
+    set_category(CAT_INPUT)
+    set_subcategory(SUBCAT_INPUT_STREAM_FILTER)
+    set_capability("stream_filter", 30)
+
+    set_description(N_("ICY metadata filter"))
+
+    add_integer("icy-metaint", -1, "ICY metaint", NULL, true)
+    change_volatile()
+
+    set_callbacks(Open, Close)
+vlc_module_end()
-- 
2.13.5 (Apple Git-94)



More information about the vlc-devel mailing list