mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 12:11:13 +00:00
Add icydemux, and tests.
Original commit message from CVS: * configure.ac: * gst/icydemux/Makefile.am: * gst/icydemux/gsticydemux.c: (gst_icydemux_get_type), (gst_icydemux_base_init), (gst_icydemux_class_init), (gst_icydemux_reset), (gst_icydemux_init), (gst_icydemux_sink_setcaps), (gst_icydemux_dispose), (gst_icydemux_add_srcpad), (gst_icydemux_remove_srcpad), (unicodify), (gst_icydemux_unicodify), (gst_icydemux_parse_and_send_tags), (gst_icydemux_typefind_or_forward), (gst_icydemux_add_meta), (gst_icydemux_chain), (gst_icydemux_change_state), (gst_icydemux_send_tag_event), (plugin_init): * gst/icydemux/gsticydemux.h: * tests/check/Makefile.am: * tests/check/elements/icydemux.c: (typefind_succeed), (plugin_init), (icydemux_found_pad), (create_icydemux), (cleanup_icydemux), (push_data), (GST_START_TEST), (icydemux_suite), (main): Add icydemux, and tests.
This commit is contained in:
parent
946f3b7938
commit
a945c1582d
7 changed files with 915 additions and 1 deletions
22
ChangeLog
22
ChangeLog
|
@ -1,3 +1,25 @@
|
|||
2006-04-21 Michael Smith <msmith@fluendo.com>
|
||||
|
||||
* configure.ac:
|
||||
* gst/icydemux/Makefile.am:
|
||||
* gst/icydemux/gsticydemux.c: (gst_icydemux_get_type),
|
||||
(gst_icydemux_base_init), (gst_icydemux_class_init),
|
||||
(gst_icydemux_reset), (gst_icydemux_init),
|
||||
(gst_icydemux_sink_setcaps), (gst_icydemux_dispose),
|
||||
(gst_icydemux_add_srcpad), (gst_icydemux_remove_srcpad),
|
||||
(unicodify), (gst_icydemux_unicodify),
|
||||
(gst_icydemux_parse_and_send_tags),
|
||||
(gst_icydemux_typefind_or_forward), (gst_icydemux_add_meta),
|
||||
(gst_icydemux_chain), (gst_icydemux_change_state),
|
||||
(gst_icydemux_send_tag_event), (plugin_init):
|
||||
* gst/icydemux/gsticydemux.h:
|
||||
* tests/check/Makefile.am:
|
||||
* tests/check/elements/icydemux.c: (typefind_succeed),
|
||||
(plugin_init), (icydemux_found_pad), (create_icydemux),
|
||||
(cleanup_icydemux), (push_data), (GST_START_TEST),
|
||||
(icydemux_suite), (main):
|
||||
Add icydemux, and tests.
|
||||
|
||||
2006-04-20 Tim-Philipp Müller <tim at centricular dot net>
|
||||
|
||||
* ext/flac/gstflacdec.c: (gst_flac_dec_loop):
|
||||
|
|
|
@ -85,6 +85,7 @@ GST_PLUGINS_ALL="\
|
|||
debug \
|
||||
effectv \
|
||||
id3demux \
|
||||
icydemux \
|
||||
flx \
|
||||
goom \
|
||||
law \
|
||||
|
@ -646,6 +647,7 @@ gst/cutter/Makefile
|
|||
gst/debug/Makefile
|
||||
gst/effectv/Makefile
|
||||
gst/id3demux/Makefile
|
||||
gst/icydemux/Makefile
|
||||
gst/goom/Makefile
|
||||
gst/law/Makefile
|
||||
gst/level/Makefile
|
||||
|
|
8
gst/icydemux/Makefile.am
Normal file
8
gst/icydemux/Makefile.am
Normal file
|
@ -0,0 +1,8 @@
|
|||
plugin_LTLIBRARIES = libgsticydemux.la
|
||||
|
||||
libgsticydemux_la_SOURCES = gsticydemux.c
|
||||
libgsticydemux_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
|
||||
libgsticydemux_la_LIBADD = $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgsttag-@GST_MAJORMINOR@ $(ZLIB_LIBS)
|
||||
libgsticydemux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||
|
||||
noinst_HEADERS = gsticydemux.h
|
587
gst/icydemux/gsticydemux.c
Normal file
587
gst/icydemux/gsticydemux.c
Normal file
|
@ -0,0 +1,587 @@
|
|||
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
|
||||
/* Copyright 2005 Jan Schmidt <thaytan@mad.scientist.com>
|
||||
* 2006 Michael Smith <msmith@fluendo.com>
|
||||
* Copyright (C) 2003-2004 Benjamin Otte <otte@gnome.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:element-icydemux
|
||||
* @short_description: reads tag information from an Icy (Icecast/Shoutcast)
|
||||
* stream, outputting them as tag messages, and forwarding the enclosed data.
|
||||
*
|
||||
* <refsect2>
|
||||
* <para>
|
||||
* icydemux accepts data streams with ICY metadata at known intervals, as transmitted from an upstream element (usually read as response headers from an HTTP stream). The mime type of the data between the tag blocks is detected using typefind functions, and the appropriate output mime type set on outgoing buffers.
|
||||
* </para>
|
||||
* <title>Example launch line</title>
|
||||
* <para>
|
||||
* <programlisting>
|
||||
* gst-launch gnomevfssrc location=http://some.server/ ! icydemux ! fakesink -t
|
||||
* </programlisting>
|
||||
* This pipeline should read any available ICY tag information and output it. The contents of the stream should be detected, and the appropriate mime type set on buffers produced from icydemux.
|
||||
* </para>
|
||||
* </refsect2>
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include <gst/gst.h>
|
||||
#include <gst/gst-i18n-plugin.h>
|
||||
|
||||
#include "gsticydemux.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static GstElementDetails gst_icydemux_details =
|
||||
GST_ELEMENT_DETAILS ("ICY tag demuxer",
|
||||
"Codec/Demuxer/Metadata",
|
||||
"Read and output ICY tags while demuxing the contents",
|
||||
"Jan Schmidt <thaytan@mad.scientist.com>\n"
|
||||
"Michael Smith <msmith@fluendo.com>");
|
||||
|
||||
#define ICY_TYPE_FIND_MAX_SIZE (40*1024)
|
||||
|
||||
GST_DEBUG_CATEGORY (icydemux_debug);
|
||||
#define GST_CAT_DEFAULT (icydemux_debug)
|
||||
|
||||
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("application/x-icy, metadata-interval = (int)[0, MAX]")
|
||||
);
|
||||
|
||||
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_SOMETIMES,
|
||||
GST_STATIC_CAPS ("ANY")
|
||||
);
|
||||
|
||||
static void gst_icydemux_class_init (GstICYDemuxClass * klass);
|
||||
static void gst_icydemux_base_init (GstICYDemuxClass * klass);
|
||||
static void gst_icydemux_init (GstICYDemux * icydemux);
|
||||
static void gst_icydemux_dispose (GObject * object);
|
||||
|
||||
static GstFlowReturn gst_icydemux_chain (GstPad * pad, GstBuffer * buf);
|
||||
|
||||
static gboolean gst_icydemux_add_srcpad (GstICYDemux * icydemux,
|
||||
GstCaps * new_caps);
|
||||
static gboolean gst_icydemux_remove_srcpad (GstICYDemux * icydemux);
|
||||
|
||||
static GstStateChangeReturn gst_icydemux_change_state (GstElement * element,
|
||||
GstStateChange transition);
|
||||
static gboolean gst_icydemux_sink_setcaps (GstPad * pad, GstCaps * caps);
|
||||
|
||||
static void gst_icydemux_send_tag_event (GstICYDemux * icydemux,
|
||||
GstTagList * taglist);
|
||||
|
||||
static GstElementClass *parent_class = NULL;
|
||||
|
||||
GType
|
||||
gst_icydemux_get_type (void)
|
||||
{
|
||||
static GType plugin_type = 0;
|
||||
|
||||
if (!plugin_type) {
|
||||
static const GTypeInfo plugin_info = {
|
||||
sizeof (GstICYDemuxClass),
|
||||
(GBaseInitFunc) gst_icydemux_base_init,
|
||||
NULL,
|
||||
(GClassInitFunc) gst_icydemux_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof (GstICYDemux),
|
||||
0,
|
||||
(GInstanceInitFunc) gst_icydemux_init,
|
||||
};
|
||||
plugin_type = g_type_register_static (GST_TYPE_ELEMENT,
|
||||
"GstICYDemux", &plugin_info, 0);
|
||||
}
|
||||
return plugin_type;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_icydemux_base_init (GstICYDemuxClass * klass)
|
||||
{
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&src_factory));
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&sink_factory));
|
||||
gst_element_class_set_details (element_class, &gst_icydemux_details);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_icydemux_class_init (GstICYDemuxClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstElementClass *gstelement_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
gstelement_class = (GstElementClass *) klass;
|
||||
|
||||
parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
|
||||
|
||||
gobject_class->dispose = gst_icydemux_dispose;
|
||||
|
||||
gstelement_class->change_state = gst_icydemux_change_state;
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
gst_icydemux_reset (GstICYDemux * icydemux)
|
||||
{
|
||||
/* Unknown at the moment (this is a fatal error if don't have a value by the
|
||||
* time we get to our chain function)
|
||||
*/
|
||||
icydemux->meta_interval = -1;
|
||||
icydemux->remaining = 0;
|
||||
|
||||
icydemux->typefinding = TRUE;
|
||||
|
||||
gst_caps_replace (&(icydemux->src_caps), NULL);
|
||||
|
||||
gst_icydemux_remove_srcpad (icydemux);
|
||||
|
||||
if (icydemux->cached_tags) {
|
||||
gst_tag_list_free (icydemux->cached_tags);
|
||||
icydemux->cached_tags = NULL;
|
||||
}
|
||||
|
||||
if (icydemux->meta_adapter) {
|
||||
gst_adapter_clear (icydemux->meta_adapter);
|
||||
g_object_unref (icydemux->meta_adapter);
|
||||
icydemux->meta_adapter = NULL;
|
||||
}
|
||||
|
||||
if (icydemux->typefind_adapter) {
|
||||
gst_adapter_clear (icydemux->typefind_adapter);
|
||||
g_object_unref (icydemux->typefind_adapter);
|
||||
icydemux->typefind_adapter = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_icydemux_init (GstICYDemux * icydemux)
|
||||
{
|
||||
GstElementClass *klass = GST_ELEMENT_GET_CLASS (icydemux);
|
||||
|
||||
icydemux->sinkpad =
|
||||
gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
|
||||
"sink"), "sink");
|
||||
gst_pad_set_chain_function (icydemux->sinkpad,
|
||||
GST_DEBUG_FUNCPTR (gst_icydemux_chain));
|
||||
gst_pad_set_setcaps_function (icydemux->sinkpad,
|
||||
GST_DEBUG_FUNCPTR (gst_icydemux_sink_setcaps));
|
||||
gst_element_add_pad (GST_ELEMENT (icydemux), icydemux->sinkpad);
|
||||
|
||||
gst_icydemux_reset (icydemux);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_icydemux_sink_setcaps (GstPad * pad, GstCaps * caps)
|
||||
{
|
||||
GstICYDemux *icydemux = GST_ICYDEMUX (GST_PAD_PARENT (pad));
|
||||
GstStructure *structure = gst_caps_get_structure (caps, 0);
|
||||
|
||||
if (!gst_structure_get_int (structure, "metadata-interval",
|
||||
&icydemux->meta_interval))
|
||||
return FALSE;
|
||||
else {
|
||||
/* We have a meta interval, so initialise the rest */
|
||||
icydemux->remaining = icydemux->meta_interval;
|
||||
icydemux->meta_remaining = 0;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_icydemux_dispose (GObject * object)
|
||||
{
|
||||
GstICYDemux *icydemux = GST_ICYDEMUX (object);
|
||||
|
||||
gst_icydemux_reset (icydemux);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_icydemux_add_srcpad (GstICYDemux * icydemux, GstCaps * new_caps)
|
||||
{
|
||||
GstPad *srcpad = NULL;
|
||||
|
||||
if (icydemux->src_caps == NULL ||
|
||||
!gst_caps_is_equal (new_caps, icydemux->src_caps)) {
|
||||
gst_caps_replace (&(icydemux->src_caps), new_caps);
|
||||
if (icydemux->srcpad != NULL) {
|
||||
GST_DEBUG_OBJECT (icydemux, "Changing src pad caps to %" GST_PTR_FORMAT,
|
||||
icydemux->src_caps);
|
||||
|
||||
gst_pad_set_caps (icydemux->srcpad, icydemux->src_caps);
|
||||
}
|
||||
} else {
|
||||
/* Caps never changed */
|
||||
gst_caps_unref (new_caps);
|
||||
}
|
||||
|
||||
if (icydemux->srcpad == NULL) {
|
||||
srcpad = icydemux->srcpad =
|
||||
gst_pad_new_from_template (gst_element_class_get_pad_template
|
||||
(GST_ELEMENT_GET_CLASS (icydemux), "src"), "src");
|
||||
g_return_val_if_fail (icydemux->srcpad != NULL, FALSE);
|
||||
|
||||
gst_pad_use_fixed_caps (icydemux->srcpad);
|
||||
|
||||
if (icydemux->src_caps)
|
||||
gst_pad_set_caps (icydemux->srcpad, icydemux->src_caps);
|
||||
|
||||
GST_DEBUG_OBJECT (icydemux, "Adding src pad with caps %" GST_PTR_FORMAT,
|
||||
icydemux->src_caps);
|
||||
|
||||
if (!(gst_element_add_pad (GST_ELEMENT (icydemux), icydemux->srcpad)))
|
||||
return FALSE;
|
||||
gst_element_no_more_pads (GST_ELEMENT (icydemux));
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_icydemux_remove_srcpad (GstICYDemux * icydemux)
|
||||
{
|
||||
gboolean res = TRUE;
|
||||
|
||||
if (icydemux->srcpad != NULL) {
|
||||
res = gst_element_remove_pad (GST_ELEMENT (icydemux), icydemux->srcpad);
|
||||
g_return_val_if_fail (res != FALSE, FALSE);
|
||||
icydemux->srcpad = NULL;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
/* The following two charset mangling functions were copied from gnomevfssrc.
|
||||
* Preserve them under the unverified assumption that they do something vaguely
|
||||
* worthwhile.
|
||||
*/
|
||||
static char *
|
||||
unicodify (const char *str, int len, ...)
|
||||
{
|
||||
char *ret = NULL, *cset;
|
||||
va_list args;
|
||||
gsize bytes_read, bytes_written;
|
||||
|
||||
if (g_utf8_validate (str, len, NULL))
|
||||
return g_strndup (str, len >= 0 ? len : strlen (str));
|
||||
|
||||
va_start (args, len);
|
||||
while ((cset = va_arg (args, char *)) != NULL)
|
||||
{
|
||||
if (!strcmp (cset, "locale"))
|
||||
ret = g_locale_to_utf8 (str, len, &bytes_read, &bytes_written, NULL);
|
||||
else
|
||||
ret = g_convert (str, len, "UTF-8", cset,
|
||||
&bytes_read, &bytes_written, NULL);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
va_end (args);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *
|
||||
gst_icydemux_unicodify (const char *str)
|
||||
{
|
||||
return unicodify (str, -1, "locale", "ISO-8859-1", NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_icydemux_parse_and_send_tags (GstICYDemux * icydemux)
|
||||
{
|
||||
GstTagList *tags = gst_tag_list_new ();
|
||||
const guint8 *data;
|
||||
int length, i;
|
||||
gchar *buffer;
|
||||
gchar **strings;
|
||||
gboolean found_tag = FALSE;
|
||||
|
||||
length = gst_adapter_available (icydemux->meta_adapter);
|
||||
|
||||
data = gst_adapter_peek (icydemux->meta_adapter, length);
|
||||
|
||||
/* Now, copy this to a buffer where we can NULL-terminate it to make things
|
||||
* a bit easier, then do that parsing. */
|
||||
buffer = g_malloc (length + 1);
|
||||
memcpy (buffer, data, length);
|
||||
buffer[length] = 0;
|
||||
|
||||
strings = g_strsplit (buffer, "';", 0);
|
||||
|
||||
for (i = 0; strings[i]; i++) {
|
||||
if (!g_ascii_strncasecmp (strings[i], "StreamTitle=", 12)) {
|
||||
char *title = gst_icydemux_unicodify (strings[i] + 13);
|
||||
|
||||
if (title && *title) {
|
||||
gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE,
|
||||
title, NULL);
|
||||
g_free (title);
|
||||
found_tag = TRUE;
|
||||
}
|
||||
} else if (!g_ascii_strncasecmp (strings[i], "StreamUrl=", 10)) {
|
||||
char *url = gst_icydemux_unicodify (strings[i] + 11);
|
||||
|
||||
if (url) {
|
||||
/*
|
||||
gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_URL,
|
||||
url, NULL);
|
||||
found_tag = TRUE;
|
||||
*/
|
||||
g_free (url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_strfreev (strings);
|
||||
g_free (buffer);
|
||||
gst_adapter_clear (icydemux->meta_adapter);
|
||||
|
||||
if (found_tag) {
|
||||
if (icydemux->srcpad) {
|
||||
gst_icydemux_send_tag_event (icydemux, tags);
|
||||
} else {
|
||||
if (!icydemux->cached_tags) {
|
||||
icydemux->cached_tags = gst_tag_list_new ();
|
||||
}
|
||||
|
||||
gst_tag_list_insert (icydemux->cached_tags, tags,
|
||||
GST_TAG_MERGE_REPLACE_ALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_icydemux_typefind_or_forward (GstICYDemux * icydemux, GstBuffer * buf)
|
||||
{
|
||||
if (icydemux->typefinding) {
|
||||
GstBuffer *typefind_buf;
|
||||
const guint8 *data;
|
||||
GstCaps *caps;
|
||||
int size;
|
||||
guint prob;
|
||||
|
||||
if (!icydemux->typefind_adapter)
|
||||
icydemux->typefind_adapter = gst_adapter_new ();
|
||||
|
||||
gst_adapter_push (icydemux->typefind_adapter, buf);
|
||||
|
||||
size = gst_adapter_available (icydemux->typefind_adapter);
|
||||
typefind_buf = gst_buffer_new ();
|
||||
|
||||
data = gst_adapter_peek (icydemux->typefind_adapter, size);
|
||||
|
||||
GST_BUFFER_DATA (typefind_buf) = (guint8 *) data;
|
||||
GST_BUFFER_SIZE (typefind_buf) = size;
|
||||
|
||||
caps = gst_type_find_helper_for_buffer (GST_OBJECT (icydemux),
|
||||
typefind_buf, &prob);
|
||||
|
||||
if (caps == NULL) {
|
||||
if (size < ICY_TYPE_FIND_MAX_SIZE) {
|
||||
gst_buffer_unref (typefind_buf);
|
||||
/* Just break for more data */
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
/* We failed typefind */
|
||||
GST_ELEMENT_ERROR (icydemux, CORE, CAPS,
|
||||
("Could not determine the mime type of the file"),
|
||||
("No caps found for contents within an ICY stream"));
|
||||
gst_buffer_unref (typefind_buf);
|
||||
gst_adapter_clear (icydemux->typefind_adapter);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
if (!gst_icydemux_add_srcpad (icydemux, caps)) {
|
||||
GST_DEBUG_OBJECT (icydemux, "Failed to add srcpad");
|
||||
gst_caps_unref (caps);
|
||||
gst_buffer_unref (typefind_buf);
|
||||
gst_adapter_clear (icydemux->typefind_adapter);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
gst_caps_unref (caps);
|
||||
|
||||
if (icydemux->cached_tags) {
|
||||
gst_icydemux_send_tag_event (icydemux, icydemux->cached_tags);
|
||||
icydemux->cached_tags = NULL;
|
||||
}
|
||||
|
||||
/* Move onto streaming: call ourselves recursively with the typefind buffer
|
||||
* to get that forwarded. */
|
||||
icydemux->typefinding = FALSE;
|
||||
|
||||
data = gst_adapter_take (icydemux->typefind_adapter, size);
|
||||
GST_BUFFER_DATA (typefind_buf) = (guint8 *) data;
|
||||
GST_BUFFER_MALLOCDATA (typefind_buf) = (guint8 *) data;
|
||||
|
||||
return gst_icydemux_typefind_or_forward (icydemux, typefind_buf);
|
||||
} else {
|
||||
if (G_UNLIKELY (icydemux->srcpad == NULL)) {
|
||||
gst_buffer_unref (buf);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
gst_buffer_set_caps (buf, icydemux->src_caps);
|
||||
|
||||
/* Most things don't care, and it's a pain to track */
|
||||
GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE;
|
||||
|
||||
return gst_pad_push (icydemux->srcpad, buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_icydemux_add_meta (GstICYDemux * icydemux, GstBuffer * buf)
|
||||
{
|
||||
if (!icydemux->meta_adapter)
|
||||
icydemux->meta_adapter = gst_adapter_new ();
|
||||
|
||||
gst_adapter_push (icydemux->meta_adapter, buf);
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_icydemux_chain (GstPad * pad, GstBuffer * buf)
|
||||
{
|
||||
GstICYDemux *icydemux;
|
||||
guint size, chunk, offset;
|
||||
GstBuffer *sub;
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
|
||||
icydemux = GST_ICYDEMUX (GST_PAD_PARENT (pad));
|
||||
g_return_val_if_fail (GST_IS_ICYDEMUX (icydemux), GST_FLOW_ERROR);
|
||||
g_return_val_if_fail (icydemux->meta_interval >= 0, GST_FLOW_ERROR);
|
||||
|
||||
if (icydemux->meta_interval == 0) {
|
||||
ret = gst_icydemux_typefind_or_forward (icydemux, buf);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Go through the buffer, chopping it into appropriate chunks. Forward as
|
||||
* tags or buffers, as appropriate
|
||||
*/
|
||||
size = GST_BUFFER_SIZE (buf);
|
||||
offset = 0;
|
||||
while (size) {
|
||||
if (icydemux->remaining) {
|
||||
chunk = (size <= icydemux->remaining) ? size : icydemux->remaining;
|
||||
sub = gst_buffer_create_sub (buf, offset, chunk);
|
||||
offset += chunk;
|
||||
icydemux->remaining -= chunk;
|
||||
size -= chunk;
|
||||
|
||||
/* This buffer goes onto typefinding, and/or directly pushed out */
|
||||
ret = gst_icydemux_typefind_or_forward (icydemux, sub);
|
||||
if (ret != GST_FLOW_OK)
|
||||
goto done;
|
||||
} else if (icydemux->meta_remaining) {
|
||||
chunk = (size <= icydemux->meta_remaining) ?
|
||||
size : icydemux->meta_remaining;
|
||||
sub = gst_buffer_create_sub (buf, offset, chunk);
|
||||
gst_icydemux_add_meta (icydemux, sub);
|
||||
|
||||
offset += chunk;
|
||||
icydemux->meta_remaining -= chunk;
|
||||
size -= chunk;
|
||||
|
||||
if (icydemux->meta_remaining == 0) {
|
||||
/* Parse tags from meta_adapter, send off as tag messages */
|
||||
GST_DEBUG_OBJECT (icydemux, "No remaining metadata, parsing for tags");
|
||||
gst_icydemux_parse_and_send_tags (icydemux);
|
||||
|
||||
icydemux->remaining = icydemux->meta_interval;
|
||||
}
|
||||
} else {
|
||||
/* We need to read a single byte (always safe at this point in the loop)
|
||||
* to figure out how many bytes of metadata exist.
|
||||
* The 'spec' tells us to read 16 * (byte_value) bytes of metadata after
|
||||
* this (zero is common, and means the metadata hasn't changed).
|
||||
*/
|
||||
icydemux->meta_remaining = 16 * GST_BUFFER_DATA (buf)[offset];
|
||||
if (icydemux->meta_remaining == 0)
|
||||
icydemux->remaining = icydemux->meta_interval;
|
||||
|
||||
offset += 1;
|
||||
size -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
gst_buffer_unref (buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_icydemux_change_state (GstElement * element, GstStateChange transition)
|
||||
{
|
||||
GstStateChangeReturn ret;
|
||||
GstICYDemux *icydemux = GST_ICYDEMUX (element);
|
||||
|
||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
gst_icydemux_reset (icydemux);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_icydemux_send_tag_event (GstICYDemux * icydemux, GstTagList * tags)
|
||||
{
|
||||
GstEvent *event;
|
||||
|
||||
gst_element_post_message (GST_ELEMENT (icydemux),
|
||||
gst_message_new_tag (GST_OBJECT (icydemux), gst_tag_list_copy (tags)));
|
||||
|
||||
event = gst_event_new_tag (tags);
|
||||
GST_EVENT_TIMESTAMP (event) = 0;
|
||||
|
||||
GST_DEBUG_OBJECT (icydemux, "Sending tag event on src pad");
|
||||
gst_pad_push_event (icydemux->srcpad, event);
|
||||
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
GST_DEBUG_CATEGORY_INIT (icydemux_debug, "icydemux", 0,
|
||||
"GStreamer ICY tag demuxer");
|
||||
|
||||
return gst_element_register (plugin, "icydemux",
|
||||
GST_RANK_PRIMARY, GST_TYPE_ICYDEMUX);
|
||||
}
|
||||
|
||||
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
"icydemux",
|
||||
"Demux ICY tags from a stream",
|
||||
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
|
83
gst/icydemux/gsticydemux.h
Normal file
83
gst/icydemux/gsticydemux.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
/* Copyright 2005 Jan Schmidt <thaytan@mad.scientist.com>
|
||||
* 2006 Michael Smith <msmith@fluendo.com>
|
||||
* Copyright (C) 2003-2004 Benjamin Otte <otte@gnome.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_ICYDEMUX_H__
|
||||
#define __GST_ICYDEMUX_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstadapter.h>
|
||||
#include <gst/base/gsttypefindhelper.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_ICYDEMUX \
|
||||
(gst_icydemux_get_type())
|
||||
#define GST_ICYDEMUX(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ICYDEMUX,GstICYDemux))
|
||||
#define GST_ICYDEMUX_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ICYDEMUX,GstICYDemux))
|
||||
#define GST_IS_ICYDEMUX(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ICYDEMUX))
|
||||
#define GST_IS_ICYDEMUX_CLASS(obj) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ICYDEMUX))
|
||||
|
||||
typedef struct _GstICYDemux GstICYDemux;
|
||||
typedef struct _GstICYDemuxClass GstICYDemuxClass;
|
||||
|
||||
struct _GstICYDemux
|
||||
{
|
||||
GstElement element;
|
||||
|
||||
GstPad *sinkpad, *srcpad;
|
||||
|
||||
/* Interval between metadata updates */
|
||||
gint meta_interval;
|
||||
|
||||
/* Remaining bytes until the next metadata update */
|
||||
gint remaining;
|
||||
|
||||
/* When 'remaining' is zero, this holds the number of bytes of metadata we
|
||||
* still need to read, or zero if we don't yet know (which means we need to
|
||||
* read one byte, after which we can initialise this properly) */
|
||||
gint meta_remaining;
|
||||
|
||||
/* Caps for the data enclosed */
|
||||
GstCaps *src_caps;
|
||||
|
||||
/* True if we're still typefinding */
|
||||
gboolean typefinding;
|
||||
|
||||
GstTagList *cached_tags;
|
||||
|
||||
GstAdapter *meta_adapter;
|
||||
|
||||
GstAdapter *typefind_adapter;
|
||||
};
|
||||
|
||||
struct _GstICYDemuxClass
|
||||
{
|
||||
GstElementClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_icydemux_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_ICYDEMUX_H__ */
|
|
@ -21,7 +21,8 @@ check_PROGRAMS = \
|
|||
elements/level \
|
||||
elements/matroskamux \
|
||||
elements/cmmldec \
|
||||
elements/cmmlenc
|
||||
elements/cmmlenc \
|
||||
elements/icydemux
|
||||
|
||||
|
||||
# these tests don't even pass
|
||||
|
|
211
tests/check/elements/icydemux.c
Normal file
211
tests/check/elements/icydemux.c
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* icydemux.c - Test icydemux element
|
||||
* Copyright (C) 2006 Michael Smith <msmith@fluendo.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gst/check/gstcheck.h>
|
||||
|
||||
/* Chunk of data: 8 bytes, followed by a metadata-length byte of 2, followed by
|
||||
* some metadata (32 bytes), then some more data.
|
||||
*/
|
||||
#define TEST_METADATA \
|
||||
"Test metadata"
|
||||
#define ICY_METADATA \
|
||||
"StreamTitle='" TEST_METADATA "';\0\0\0\0"
|
||||
|
||||
#define ICY_DATA \
|
||||
"aaaaaaaa" \
|
||||
"\x02" \
|
||||
ICY_METADATA \
|
||||
"bbbbbbbb"
|
||||
|
||||
#define ICYCAPS "application/x-icy, metadata-interval = (int)8"
|
||||
|
||||
#define SRC_CAPS "application/x-icy, metadata-interval = (int)[0, MAX]"
|
||||
#define SINK_CAPS "ANY"
|
||||
|
||||
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (SRC_CAPS)
|
||||
);
|
||||
|
||||
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (SINK_CAPS)
|
||||
);
|
||||
|
||||
GstElement *icydemux;
|
||||
GstBus *bus;
|
||||
GstPad *srcpad, *sinkpad;
|
||||
|
||||
static GstStaticCaps typefind_caps =
|
||||
GST_STATIC_CAPS ("application/octet-stream");
|
||||
|
||||
static void
|
||||
typefind_succeed (GstTypeFind * tf, gpointer private)
|
||||
{
|
||||
gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM,
|
||||
gst_static_caps_get (&typefind_caps));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
gst_type_find_register (plugin, "success", GST_RANK_PRIMARY, typefind_succeed,
|
||||
NULL, gst_static_caps_get (&typefind_caps), NULL, NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GST_PLUGIN_DEFINE_STATIC (GST_VERSION_MAJOR, GST_VERSION_MINOR,
|
||||
"gst-test",
|
||||
"test plugin for icydemux",
|
||||
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
|
||||
|
||||
static void
|
||||
icydemux_found_pad (GstElement * src, GstPad * pad, gpointer data)
|
||||
{
|
||||
/* Turns out that this asserts a refcount which is wrong for this
|
||||
* case (adding the pad from a pad-added callback), so just do the same
|
||||
* thing inline... */
|
||||
/* sinkpad = gst_check_setup_sink_pad (icydemux, &sinktemplate, NULL); */
|
||||
sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
|
||||
fail_if (sinkpad == NULL, "Couldn't create sinkpad");
|
||||
srcpad = gst_element_get_pad (icydemux, "src");
|
||||
fail_if (srcpad == NULL, "Failed to get srcpad from icydemux");
|
||||
gst_pad_set_chain_function (sinkpad, gst_check_chain_func);
|
||||
fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK,
|
||||
"Failed to link pads");
|
||||
gst_object_unref (srcpad);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 2);
|
||||
}
|
||||
|
||||
static GstElement *
|
||||
create_icydemux (void)
|
||||
{
|
||||
icydemux = gst_check_setup_element ("icydemux");
|
||||
srcpad = gst_check_setup_src_pad (icydemux, &srctemplate, NULL);
|
||||
|
||||
g_signal_connect (icydemux, "pad-added", G_CALLBACK (icydemux_found_pad),
|
||||
NULL);
|
||||
|
||||
bus = gst_bus_new ();
|
||||
gst_element_set_bus (icydemux, bus);
|
||||
|
||||
fail_unless (gst_element_set_state (icydemux, GST_STATE_PLAYING) !=
|
||||
GST_STATE_CHANGE_FAILURE, "could not set to playing");
|
||||
|
||||
return icydemux;
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup_icydemux (void)
|
||||
{
|
||||
gst_bus_set_flushing (bus, TRUE);
|
||||
gst_object_unref (bus);
|
||||
|
||||
gst_check_teardown_src_pad (icydemux);
|
||||
gst_check_teardown_sink_pad (icydemux);
|
||||
gst_check_teardown_element (icydemux);
|
||||
}
|
||||
|
||||
static void
|
||||
push_data (guint8 * data, int len, GstCaps * caps)
|
||||
{
|
||||
GstFlowReturn res;
|
||||
GstBuffer *buffer = gst_buffer_new_and_alloc (len);
|
||||
|
||||
memcpy (GST_BUFFER_DATA (buffer), data, len);
|
||||
gst_buffer_set_caps (buffer, caps);
|
||||
|
||||
res = gst_pad_push (srcpad, buffer);
|
||||
|
||||
fail_unless (res == GST_FLOW_OK, "Failed pushing buffer: %d", res);
|
||||
}
|
||||
|
||||
GST_START_TEST (test_demux)
|
||||
{
|
||||
GstMessage *message;
|
||||
GstTagList *tags;
|
||||
const GValue *tag_val;
|
||||
const gchar *tag;
|
||||
GstCaps *caps;
|
||||
|
||||
caps = gst_caps_from_string (ICYCAPS);
|
||||
|
||||
create_icydemux ();
|
||||
|
||||
push_data ((guint8 *) ICY_DATA, sizeof (ICY_DATA), caps);
|
||||
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_TAG, -1);
|
||||
fail_unless (message != NULL);
|
||||
|
||||
gst_message_parse_tag (message, &tags);
|
||||
fail_unless (tags != NULL);
|
||||
|
||||
tag_val = gst_tag_list_get_value_index (tags, GST_TAG_TITLE, 0);
|
||||
fail_unless (tag_val != NULL);
|
||||
|
||||
tag = g_value_get_string (tag_val);
|
||||
fail_unless (tag != NULL);
|
||||
|
||||
fail_unless_equals_string (TEST_METADATA, (char *) tag);
|
||||
|
||||
gst_tag_list_free (tags);
|
||||
gst_message_unref (message);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
cleanup_icydemux ();
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
Suite *
|
||||
icydemux_suite ()
|
||||
{
|
||||
Suite *s = suite_create ("icydemux");
|
||||
TCase *tc_chain = tcase_create ("general");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
tcase_add_test (tc_chain, test_demux);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int nf;
|
||||
|
||||
Suite *s = icydemux_suite ();
|
||||
SRunner *sr = srunner_create (s);
|
||||
|
||||
gst_check_init (&argc, &argv);
|
||||
|
||||
srunner_run_all (sr, CK_NORMAL);
|
||||
nf = srunner_ntests_failed (sr);
|
||||
srunner_free (sr);
|
||||
|
||||
return nf;
|
||||
}
|
Loading…
Reference in a new issue