ext/ogg/gstoggdemux.c: lots of changes - mainly support for chained bitstreams, seeking, querying and bugfixes of course

Original commit message from CVS:
2004-01-29  Benjamin Otte  <in7y118@public.uni-hamburg.de>

* ext/ogg/gstoggdemux.c:
lots of changes - mainly support for chained bitstreams, seeking,
querying and bugfixes of course
* ext/vorbis/Makefile.am:
* ext/vorbis/vorbisdec.c:
* ext/vorbis/vorbisdec.h:
add vorbisdec raw vorbis decoder
* ext/vorbis/vorbis.c: (plugin_init):
register vorbisdec as PRIMARY, vorbisfile as SECONDARY
* gst/intfloat/Makefile.am:
* gst/intfloat/float22int.c:
* gst/intfloat/float22int.h:
* gst/intfloat/gstintfloatconvert.c: (plugin_init):
add float2intnew plugin. It converts multichannel interleaved float to
multichannel interleaved int. The name should probably be changed.
* gst/typefind/gsttypefindfunctions.c: (theora_type_find),
(plugin_init):
add typefinding for raw theora video so oggdemux can detect it.
This commit is contained in:
Benjamin Otte 2004-01-29 02:50:20 +00:00
parent f606249297
commit b660e15f8d
7 changed files with 876 additions and 96 deletions

View file

@ -1,3 +1,24 @@
2004-01-29 Benjamin Otte <in7y118@public.uni-hamburg.de>
* ext/ogg/gstoggdemux.c:
lots of changes - mainly support for chained bitstreams, seeking,
querying and bugfixes of course
* ext/vorbis/Makefile.am:
* ext/vorbis/vorbisdec.c:
* ext/vorbis/vorbisdec.h:
add vorbisdec raw vorbis decoder
* ext/vorbis/vorbis.c: (plugin_init):
register vorbisdec as PRIMARY, vorbisfile as SECONDARY
* gst/intfloat/Makefile.am:
* gst/intfloat/float22int.c:
* gst/intfloat/float22int.h:
* gst/intfloat/gstintfloatconvert.c: (plugin_init):
add float2intnew plugin. It converts multichannel interleaved float to
multichannel interleaved int. The name should probably be changed.
* gst/typefind/gsttypefindfunctions.c: (theora_type_find),
(plugin_init):
add typefinding for raw theora video so oggdemux can detect it.
2004-01-28 Julien MOUTTE <julien@moutte.net> 2004-01-28 Julien MOUTTE <julien@moutte.net>
* gst-libs/gst/play/gstplay.c: (gst_play_seek_to_time): seek on video * gst-libs/gst/play/gstplay.c: (gst_play_seek_to_time): seek on video

View file

@ -24,7 +24,8 @@
#endif #endif
#include <gst/gst.h> #include <gst/gst.h>
#include <ogg/ogg.h> #include <ogg/ogg.h>
/* memcpy - if someone knows a way to get rid of it, please speak up */ /* memcpy - if someone knows a way to get rid of it, please speak up
* note: the ogg docs even say you ned this... */
#include <string.h> #include <string.h>
GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_debug); GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_debug);
@ -39,23 +40,61 @@ GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_debug);
typedef struct _GstOggDemux GstOggDemux; typedef struct _GstOggDemux GstOggDemux;
typedef struct _GstOggDemuxClass GstOggDemuxClass; typedef struct _GstOggDemuxClass GstOggDemuxClass;
typedef enum {
/* just because you shouldn't make a valid enum value 0 */
GST_OGG_STATE_INAVLID,
/* just started, we need to decide if we should do setup */
GST_OGG_STATE_START,
/* setup is analyzing the stream, getting lengths and so on */
GST_OGG_STATE_SETUP,
/* after a seek, during resyncing */
GST_OGG_STATE_SEEK,
/* normal playback */
GST_OGG_STATE_PLAY
} GstOggState;
/* all information needed for one ogg stream */
typedef struct { typedef struct {
GstPad * pad; /* reference for this pad is held by element we belong to */ GstPad * pad; /* reference for this pad is held by element we belong to */
int serial; gint serial;
ogg_stream_state stream; ogg_stream_state stream;
guint64 offset; /* end offset of last buffer */ guint64 offset; /* end offset of last buffer */
guint64 known_offset; /* last known offset */
guint64 length; /* length of stream or 0 */
glong pages;
} GstOggPad; } GstOggPad;
/* all information needed for one ogg chain (relevant for chained bitstreams) */
typedef struct {
GSList * pads; /* list of GstOggPad */
} GstOggChain;
#define CURRENT_CHAIN(ogg) (&g_array_index ((ogg)->chains, GstOggChain, (ogg)->current_chain))
typedef enum {
GST_OGG_FLAG_BOS = GST_ELEMENT_FLAG_LAST,
GST_OGG_FLAG_EOS,
GST_OGG_FLAG_WAIT_FOR_DISCONT
} GstOggFlag;
struct _GstOggDemux { struct _GstOggDemux {
GstElement element; GstElement element;
/* pads */ /* pad */
GstPad * sinkpad; GstPad * sinkpad;
GSList * srcpads; /* list of GstOggPad */
/* state */
GstOggState state;
GArray * chains;
gint current_chain;
guint flags;
/* ogg stuff */ /* ogg stuff */
ogg_sync_state sync; ogg_sync_state sync;
/* seeking */
GstOggPad * seek_pad;
guint64 seek_to;
}; };
struct _GstOggDemuxClass { struct _GstOggDemuxClass {
@ -99,12 +138,7 @@ GST_STATIC_PAD_TEMPLATE (
); );
static void gst_ogg_demux_base_init (gpointer g_class); static void gst_ogg_demux_finalize (GObject * object);
static void gst_ogg_demux_class_init (gpointer g_class,
gpointer class_data);
static void gst_ogg_demux_init (GTypeInstance * instance,
gpointer g_class);
static void gst_ogg_demux_dispose (GObject * object);
static gboolean gst_ogg_demux_src_event (GstPad * pad, static gboolean gst_ogg_demux_src_event (GstPad * pad,
GstEvent * event); GstEvent * event);
@ -134,32 +168,14 @@ static void gst_ogg_pad_push (GstOggDemux * ogg,
static GstCaps * gst_ogg_type_find (ogg_packet * packet); static GstCaps * gst_ogg_type_find (ogg_packet * packet);
static void gst_ogg_print (GstOggDemux * demux);
static GstElementClass *parent_class = NULL; #define GST_OGG_SET_STATE(ogg, new_state) G_STMT_START{ \
/* static guint gst_ogg_demux_signals[LAST_SIGNAL] = { 0 }; */ GST_DEBUG_OBJECT (ogg, "setting state to %s", G_STRINGIFY (new_state)); \
ogg->state = new_state; \
}G_STMT_END
GType GST_BOILERPLATE (GstOggDemux, gst_ogg_demux, GstElement, GST_TYPE_ELEMENT)
gst_ogg_demux_get_type (void)
{
static GType ogg_demux_type = 0;
if (!ogg_demux_type) {
static const GTypeInfo ogg_demux_info = {
sizeof (GstOggDemuxClass),
gst_ogg_demux_base_init,
NULL,
gst_ogg_demux_class_init,
NULL,
NULL,
sizeof (GstOggDemux),
0,
gst_ogg_demux_init,
};
ogg_demux_type = g_type_register_static(GST_TYPE_ELEMENT, "GstOggDemux", &ogg_demux_info, 0);
}
return ogg_demux_type;
}
static void static void
gst_ogg_demux_base_init (gpointer g_class) gst_ogg_demux_base_init (gpointer g_class)
@ -174,22 +190,18 @@ gst_ogg_demux_base_init (gpointer g_class)
gst_static_pad_template_get (&ogg_demux_src_template_factory)); gst_static_pad_template_get (&ogg_demux_src_template_factory));
} }
static void static void
gst_ogg_demux_class_init (gpointer g_class, gpointer class_data) gst_ogg_demux_class_init (GstOggDemuxClass *klass)
{ {
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (g_class); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
gstelement_class->change_state = gst_ogg_demux_change_state; gstelement_class->change_state = gst_ogg_demux_change_state;
gobject_class->dispose = gst_ogg_demux_dispose; gobject_class->finalize = gst_ogg_demux_finalize;
} }
static void static void
gst_ogg_demux_init (GTypeInstance *instance, gpointer g_class) gst_ogg_demux_init (GstOggDemux *ogg)
{ {
GstOggDemux *ogg = GST_OGG_DEMUX (instance);
/* create the sink pad */ /* create the sink pad */
ogg->sinkpad = gst_pad_new_from_template( ogg->sinkpad = gst_pad_new_from_template(
gst_static_pad_template_get (&ogg_demux_sink_template_factory), "sink"); gst_static_pad_template_get (&ogg_demux_sink_template_factory), "sink");
@ -197,19 +209,22 @@ gst_ogg_demux_init (GTypeInstance *instance, gpointer g_class)
gst_pad_set_chain_function (ogg->sinkpad, GST_DEBUG_FUNCPTR (gst_ogg_demux_chain)); gst_pad_set_chain_function (ogg->sinkpad, GST_DEBUG_FUNCPTR (gst_ogg_demux_chain));
/* initalize variables */ /* initalize variables */
ogg->srcpads = NULL; GST_OGG_SET_STATE (ogg, GST_OGG_STATE_START);
ogg->chains = g_array_new (TRUE, TRUE, sizeof (GstOggChain));
GST_FLAG_SET (ogg, GST_ELEMENT_EVENT_AWARE); GST_FLAG_SET (ogg, GST_ELEMENT_EVENT_AWARE);
} }
static void static void
gst_ogg_demux_dispose (GObject *object) gst_ogg_demux_finalize (GObject *object)
{ {
GstOggDemux *ogg; GstOggDemux *ogg;
ogg = GST_OGG_DEMUX (object); ogg = GST_OGG_DEMUX (object);
/* srcpads are removed when going to READY */ /* chains are removed when going to READY */
g_assert (ogg->srcpads == NULL); g_assert (ogg->current_chain == -1);
g_assert (ogg->chains->len == 0);
g_array_free (ogg->chains, TRUE);
} }
static const GstEventMask* static const GstEventMask*
@ -233,20 +248,41 @@ gst_ogg_demux_get_query_types (GstPad *pad)
return gst_ogg_demux_src_query_types; return gst_ogg_demux_src_query_types;
} }
static GstOggPad *
gst_ogg_get_pad_by_pad (GstOggDemux *ogg, GstPad *pad)
{
GSList *walk;
GstOggPad *cur;
for (walk = CURRENT_CHAIN (ogg)->pads; walk; walk = g_slist_next (walk)) {
cur = (GstOggPad *) walk->data;
if (cur->pad == pad)
return cur;
}
return NULL;
}
static gboolean static gboolean
gst_ogg_demux_src_query (GstPad *pad, GstQueryType type, gst_ogg_demux_src_query (GstPad *pad, GstQueryType type,
GstFormat *format, gint64 *value) GstFormat *format, gint64 *value)
{ {
gboolean res = FALSE; gboolean res = FALSE;
GstOggDemux *ogg; GstOggDemux *ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
GstOggPad *cur = gst_ogg_get_pad_by_pad (ogg, pad);
ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
switch (type) { switch (type) {
case GST_QUERY_TOTAL: { case GST_QUERY_TOTAL: {
if (*format == GST_FORMAT_DEFAULT) {
*value = cur->known_offset;
res = TRUE;
}
break; break;
} }
case GST_QUERY_POSITION: case GST_QUERY_POSITION:
if (*format == GST_FORMAT_DEFAULT && cur->length != 0) {
*value = cur->length;
res = TRUE;
}
break; break;
default: default:
break; break;
@ -254,39 +290,114 @@ gst_ogg_demux_src_query (GstPad *pad, GstQueryType type,
return res; return res;
} }
/* The current seeking implementation is the most simple I could come up with:
* - when seeking forwards, just discard data until desired position is reached
* - when seeking backwards, seek to beginning and seek forward from there
* Anyone is free to improve this algorithm as it is quite stupid and probably
* really slow.
*/
static gboolean static gboolean
gst_ogg_demux_src_event (GstPad *pad, GstEvent *event) gst_ogg_demux_src_event (GstPad *pad, GstEvent *event)
{ {
GstOggDemux *ogg; GstOggDemux *ogg;
GstOggPad *cur;
ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
cur = gst_ogg_get_pad_by_pad (ogg, pad);
/* FIXME: optimize this so events from inactive chains work?
* in theory there shouldn't be an exisiting pad for inactive chains */
if (cur == NULL) return FALSE;
switch (GST_EVENT_TYPE (event)) { switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK: case GST_EVENT_SEEK:
{
gint64 offset;
if (GST_EVENT_SEEK_FORMAT (event) != GST_FORMAT_DEFAULT)
break; break;
offset = GST_EVENT_SEEK_OFFSET (event);
switch (GST_EVENT_SEEK_METHOD (event)) {
case GST_SEEK_METHOD_END:
if (cur->length == 0 || offset > 0)
goto out;
offset = cur->length + offset;
break;
case GST_SEEK_METHOD_CUR:
offset += cur->known_offset;
break;
case GST_SEEK_METHOD_SET:
break;
default:
g_warning ("invalid seek method in seek event");
break;
}
if (offset < cur->known_offset) {
GstEvent *restart = gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET, 0);
if (!gst_pad_send_event (GST_PAD_PEER (ogg->sinkpad), restart))
break;
}
GST_OGG_SET_STATE (ogg, GST_OGG_STATE_SEEK);
ogg->seek_pad = cur;
ogg->seek_to = offset;
gst_event_unref (event);
return TRUE;
}
default: default:
break; break;
} }
out:
gst_event_unref (event); gst_event_unref (event);
return FALSE; return FALSE;
} }
static void
gst_ogg_start_playing (GstOggDemux *ogg)
{
GST_DEBUG_OBJECT (ogg, "got EOS in setup, changing to playback now");
if (!gst_pad_send_event (GST_PAD_PEER (ogg->sinkpad),
gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET, 0))) {
gst_element_error (ogg, CORE, SEEK, (NULL),
("cannot seek to start after EOS"));
}
ogg->current_chain = 0;
GST_FLAG_UNSET (ogg, GST_OGG_FLAG_EOS);
GST_FLAG_SET (ogg, GST_OGG_FLAG_WAIT_FOR_DISCONT);
GST_OGG_SET_STATE (ogg, GST_OGG_STATE_PLAY);
gst_ogg_print (ogg);
}
static void static void
gst_ogg_demux_handle_event (GstPad *pad, GstEvent *event) gst_ogg_demux_handle_event (GstPad *pad, GstEvent *event)
{ {
GstOggDemux *ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); GstOggDemux *ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
ogg=ogg;
switch (GST_EVENT_TYPE (event)) { switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_DISCONTINUOUS: case GST_EVENT_DISCONTINUOUS:
GST_DEBUG_OBJECT (ogg, "got a discont event");
ogg_sync_reset (&ogg->sync);
gst_event_unref (event);
GST_FLAG_UNSET (ogg, GST_OGG_FLAG_WAIT_FOR_DISCONT);
break; break;
case GST_EVENT_EOS: case GST_EVENT_EOS:
if (ogg->srcpads) if (ogg->state == GST_OGG_STATE_SETUP) {
GST_WARNING_OBJECT (ogg, "got EOS in unfinished ogg stream"); gst_ogg_start_playing (ogg);
while (ogg->srcpads) { } else {
gst_ogg_pad_remove (ogg, (GstOggPad *) ogg->srcpads->data); guint i;
GSList *walk;
GST_DEBUG_OBJECT (ogg, "got EOS");
ogg->current_chain = -1;
for (i = 0; i < ogg->chains->len; i++) {
GstOggChain *chain = &g_array_index (ogg->chains, GstOggChain, i);
for (walk = chain->pads; walk; walk = g_slist_next (walk)) {
GstOggPad *pad = (GstOggPad *) walk->data;
gst_event_ref (event);
if (GST_PAD_IS_USABLE (pad->pad))
gst_pad_push (pad->pad, GST_DATA (event));
}
} }
gst_element_set_eos (GST_ELEMENT (ogg)); gst_element_set_eos (GST_ELEMENT (ogg));
}
gst_event_unref (event);
break; break;
default: default:
gst_pad_event_default (pad, event); gst_pad_event_default (pad, event);
@ -294,12 +405,39 @@ gst_ogg_demux_handle_event (GstPad *pad, GstEvent *event)
} }
return; return;
} }
/* get the pad with the given serial in the current stream or NULL if none */
static GstOggPad *
gst_ogg_pad_get_in_current_chain (GstOggDemux *ogg, int serial)
{
GSList *walk;
if (ogg->current_chain == -1) return NULL;
g_return_val_if_fail (ogg->current_chain < ogg->chains->len, NULL);
for (walk = CURRENT_CHAIN (ogg)->pads; walk; walk = g_slist_next (walk)) {
GstOggPad *pad = (GstOggPad *) walk->data;
if (pad->serial == serial)
return pad;
}
return NULL;
}
static void
gst_ogg_add_chain (GstOggDemux *ogg)
{
GST_LOG_OBJECT (ogg, "adding chain %u", ogg->chains->len);
ogg->current_chain = ogg->chains->len;
g_array_set_size (ogg->chains, ogg->chains->len + 1);
}
static void static void
gst_ogg_demux_chain (GstPad *pad, GstData *buffer) gst_ogg_demux_chain (GstPad *pad, GstData *buffer)
{ {
GstOggDemux *ogg; GstOggDemux *ogg;
guint8 *data; guint8 *data;
int pageout_ret = 1; int pageout_ret = 1;
guint64 offset_end;
/* handle events */ /* handle events */
if (GST_IS_EVENT (buffer)) { if (GST_IS_EVENT (buffer)) {
@ -309,13 +447,23 @@ gst_ogg_demux_chain (GstPad *pad, GstData *buffer)
ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
if (GST_FLAG_IS_SET (ogg, GST_OGG_FLAG_WAIT_FOR_DISCONT)) {
GST_LOG_OBJECT (ogg, "waiting for discont event, discarding buffer");
gst_data_unref (buffer);
return;
}
GST_LOG_OBJECT (ogg, "queueing buffer %p with offset %llu", buffer, GST_BUFFER_OFFSET (buffer));
data = (guint8 *) ogg_sync_buffer(&ogg->sync, GST_BUFFER_SIZE (buffer)); data = (guint8 *) ogg_sync_buffer(&ogg->sync, GST_BUFFER_SIZE (buffer));
memcpy (data, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer)); memcpy (data, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
if (ogg_sync_wrote (&ogg->sync, GST_BUFFER_SIZE (buffer)) != 0) { if (ogg_sync_wrote (&ogg->sync, GST_BUFFER_SIZE (buffer)) != 0) {
gst_data_unref (buffer); gst_data_unref (buffer);
gst_element_error (ogg, LIBRARY, TOO_LAZY, NULL, ("ogg_sync_wrote failed")); gst_element_error (ogg, LIBRARY, FAILED /* MEMORY */, NULL, ("ogg_sync_wrote failed"));
return; return;
} }
offset_end = GST_BUFFER_OFFSET_IS_VALID (buffer) ?
GST_BUFFER_OFFSET (buffer) + GST_BUFFER_SIZE (buffer) :
(guint64) -1;
gst_data_unref (buffer); gst_data_unref (buffer);
while (pageout_ret != 0) { while (pageout_ret != 0) {
ogg_page page; ogg_page page;
@ -323,25 +471,90 @@ gst_ogg_demux_chain (GstPad *pad, GstData *buffer)
pageout_ret = ogg_sync_pageout (&ogg->sync, &page); pageout_ret = ogg_sync_pageout (&ogg->sync, &page);
switch (pageout_ret) { switch (pageout_ret) {
case -1: case -1:
/* FIXME: need some kind of discont here, we don't know any values */ /* FIXME: need some kind of discont here, we don't know any values to send though,
* we only have the END_OFFSET */
break; break;
case 0: case 0:
break; break;
case 1: case 1:
GST_LOG_OBJECT (ogg, "processing ogg page (serial %d, packet %ld, granule pos %llu",
ogg_page_serialno (&page), ogg_page_pageno (&page), ogg_page_granulepos (&page));
switch (ogg->state) {
case GST_OGG_STATE_SETUP:
if (ogg_page_eos (&page)) {
guint64 length;
GstFormat format = GST_FORMAT_BYTES;
GstOggPad *cur = gst_ogg_pad_get_in_current_chain (ogg, ogg_page_serialno (&page));
GST_FLAG_SET (ogg, GST_OGG_FLAG_EOS);
if (!cur) {
GST_ERROR_OBJECT (ogg, "unknown serial %d", ogg_page_serialno (&page));
} else {
cur->pages = ogg_page_pageno (&page);
cur->length = ogg_page_granulepos (&page);
}
if (!gst_pad_query (GST_PAD_PEER (ogg->sinkpad), GST_QUERY_TOTAL, &format, &length))
length = 0;
if (length >= offset_end) {
gst_ogg_start_playing (ogg);
goto out;
}
} else {
if (GST_FLAG_IS_SET (ogg, GST_OGG_FLAG_EOS) && ogg_page_bos (&page)) {
gst_ogg_add_chain (ogg);
}
GST_FLAG_UNSET (ogg, GST_OGG_FLAG_EOS);
}
if (ogg_page_bos (&page)) {
if (gst_ogg_pad_get_in_current_chain (ogg, ogg_page_serialno (&page))) {
GST_ERROR_OBJECT (ogg, "multiple BOS page for serial %d (page %ld)",
ogg_page_serialno (&page), ogg_page_pageno (&page));
} else {
GstOggPad *pad = gst_ogg_pad_new (ogg, ogg_page_serialno (&page));
CURRENT_CHAIN (ogg)->pads = g_slist_prepend (CURRENT_CHAIN (ogg)->pads, pad);
}
GST_FLAG_SET (ogg, GST_OGG_FLAG_BOS);
} else {
GST_FLAG_UNSET (ogg, GST_OGG_FLAG_BOS);
}
break;
case GST_OGG_STATE_START:
if (gst_pad_send_event (GST_PAD_PEER (ogg->sinkpad),
gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_END, 0))) {
GST_OGG_SET_STATE (ogg, GST_OGG_STATE_SETUP);
GST_DEBUG_OBJECT (ogg, "stream can seek, try setup now");
if (!gst_pad_send_event (GST_PAD_PEER (ogg->sinkpad),
gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET, 0))) {
gst_element_error (ogg, CORE, SEEK, NULL,
("stream can seek to end, but not to start. Can't handle that."));
}
gst_ogg_add_chain (ogg);
GST_FLAG_SET (ogg, GST_OGG_FLAG_WAIT_FOR_DISCONT);
goto out;
}
GST_OGG_SET_STATE (ogg, GST_OGG_STATE_PLAY);
/* fall through */
case GST_OGG_STATE_SEEK:
case GST_OGG_STATE_PLAY:
gst_ogg_demux_push (ogg, &page); gst_ogg_demux_push (ogg, &page);
break; break;
default:
g_assert_not_reached ();
break;
}
break;
default: default:
GST_WARNING_OBJECT (ogg, "unknown return value %d from ogg_sync_pageout", pageout_ret); GST_WARNING_OBJECT (ogg, "unknown return value %d from ogg_sync_pageout", pageout_ret);
pageout_ret = 0; pageout_ret = 0;
break; break;
} }
} }
out:
return; return;
} }
static GstOggPad * static GstOggPad *
gst_ogg_pad_new (GstOggDemux *ogg, int serial) gst_ogg_pad_new (GstOggDemux *ogg, int serial)
{ {
GstOggPad *ret = g_new (GstOggPad, 1); GstOggPad *ret = g_new0 (GstOggPad, 1);
ret->serial = serial; ret->serial = serial;
if (ogg_stream_init (&ret->stream, serial) != 0) { if (ogg_stream_init (&ret->stream, serial) != 0) {
@ -349,8 +562,6 @@ gst_ogg_pad_new (GstOggDemux *ogg, int serial)
g_free (ret); g_free (ret);
return NULL; return NULL;
} }
ret->pad = NULL;
ret->offset = 0;
GST_LOG_OBJECT (ogg, "created new ogg src %p for stream with serial %d", ret, serial); GST_LOG_OBJECT (ogg, "created new ogg src %p for stream with serial %d", ret, serial);
return ret; return ret;
@ -358,9 +569,13 @@ gst_ogg_pad_new (GstOggDemux *ogg, int serial)
static void static void
gst_ogg_pad_remove (GstOggDemux *ogg, GstOggPad *pad) gst_ogg_pad_remove (GstOggDemux *ogg, GstOggPad *pad)
{ {
ogg->srcpads = g_slist_remove (ogg->srcpads, pad);
if (pad->pad) { if (pad->pad) {
/* FIXME:
* we do it in the EOS signal already - EOS handling needs to be better thought out.
* Correct way would be pushing EOS on eos page, but scheduler doesn't like that
if (GST_PAD_IS_USEABLE (pad->pad))
gst_pad_push (pad->pad, GST_DATA (gst_event_new (GST_EVENT_EOS))); gst_pad_push (pad->pad, GST_DATA (gst_event_new (GST_EVENT_EOS)));
*/
gst_element_remove_pad (GST_ELEMENT (ogg), pad->pad); gst_element_remove_pad (GST_ELEMENT (ogg), pad->pad);
} }
if (ogg_stream_clear (&pad->stream) != 0) if (ogg_stream_clear (&pad->stream) != 0)
@ -375,32 +590,37 @@ gst_ogg_demux_push (GstOggDemux *ogg, ogg_page* page)
GstOggPad *cur; GstOggPad *cur;
/* find the stream */ /* find the stream */
walk = ogg->srcpads; for (walk = CURRENT_CHAIN (ogg)->pads; walk; walk = g_slist_next (walk)) {
while (walk) {
cur = (GstOggPad *) walk->data; cur = (GstOggPad *) walk->data;
if (cur->serial == ogg_page_serialno (page)) { if (cur->serial == ogg_page_serialno (page)) {
goto br; goto br;
} }
walk = g_slist_next (walk);
} }
cur = NULL; cur = NULL;
br: br:
/* now we either have a stream (cur) or not */ /* now we either have a stream (cur) or not */
if (ogg_page_bos (page)) { if (ogg_page_bos (page)) {
if (cur) { if (cur) {
/* huh? */ GST_DEBUG_OBJECT (ogg, "ogg page declared as BOS while stream %d already existed."
GST_WARNING_OBJECT (ogg, "Ignoring error: ogg page declared as BOS while stream already existed."); "Possibly a seek happened.", cur->serial);
} else if (cur) {
GST_DEBUG_OBJECT (ogg, "reactivating deactivated stream %d.", cur->serial);
} else { } else {
/* FIXME: monitor if we are still in creation stage? */ /* FIXME: monitor if we are still in creation stage? */
cur = gst_ogg_pad_new (ogg, ogg_page_serialno (page)); cur = gst_ogg_pad_new (ogg, ogg_page_serialno (page));
if (!cur) { if (!cur) {
gst_element_error (ogg, LIBRARY, TOO_LAZY, NULL, ("Creating ogg_stream struct failed.")); gst_element_error (ogg, LIBRARY, FAILED, NULL, ("Creating ogg_stream struct failed."));
return;
} }
ogg->srcpads = g_slist_prepend (ogg->srcpads, cur); if (ogg->current_chain == -1) {
/* add new one at the end */
gst_ogg_add_chain (ogg);
}
CURRENT_CHAIN (ogg)->pads = g_slist_prepend (CURRENT_CHAIN (ogg)->pads, cur);
} }
} }
if (cur == NULL) { if (cur == NULL) {
gst_element_error (ogg, LIBRARY, TOO_LAZY, NULL, ("invalid ogg stream serial no")); gst_element_error (ogg, STREAM, DECODE, NULL, ("invalid ogg stream serial no"));
return; return;
} }
if (ogg_stream_pagein (&cur->stream, page) != 0) { if (ogg_stream_pagein (&cur->stream, page) != 0) {
@ -408,15 +628,48 @@ br:
gst_ogg_pad_reset (ogg, cur); gst_ogg_pad_reset (ogg, cur);
return; return;
} }
switch (ogg->state) {
case GST_OGG_STATE_SEEK:
GST_LOG_OBJECT (ogg, "in seek - offset now: %"G_GUINT64_FORMAT" (pad %d) - desired offset %"
G_GUINT64_FORMAT" (pad %d)", cur->known_offset, cur->serial,
ogg->seek_to, ogg->seek_pad->serial);
if (cur == ogg->seek_pad) {
if (ogg_page_granulepos (page) > ogg->seek_to) {
GST_OGG_SET_STATE (ogg, GST_OGG_STATE_PLAY);
ogg->seek_pad = NULL;
ogg->seek_to = 0;
/* send discont everywhere */
for (walk = CURRENT_CHAIN (ogg)->pads; walk; walk = g_slist_next (walk)) {
GstOggPad *send = (GstOggPad *) walk->data;
GstEvent *event = gst_event_new_discontinuous (FALSE,
GST_FORMAT_DEFAULT, send->known_offset);
if (GST_PAD_IS_USABLE (send->pad))
gst_pad_push (send->pad, GST_DATA (event));
}
}
}
/* fallthrough */
case GST_OGG_STATE_PLAY:
cur->known_offset = ogg_page_granulepos (page);
gst_ogg_pad_push (ogg, cur); gst_ogg_pad_push (ogg, cur);
break;
default:
g_assert_not_reached ();
break;
}
if (ogg_page_eos (page)) {
GST_DEBUG_OBJECT (ogg, "got EOS for stream with serial %d, sending EOS now",
cur->serial);
#if 0 #if 0
/* Removing pads while PLAYING doesn't work with current schedulers */ /* Removing pads while PLAYING doesn't work with current schedulers */
/* remove from list, as this will never be called again */ /* remove from list, as this will never be called again */
if (ogg_page_eos (page)) {
gst_ogg_pad_remove (ogg, cur); gst_ogg_pad_remove (ogg, cur);
} /* this is also not possible because sending EOS this way confuses the scheduler */
gst_pad_push (cur->pad, GST_DATA (gst_event_new (GST_EVENT_EOS)));
#else
#endif #endif
} }
}
static void static void
gst_ogg_pad_push (GstOggDemux *ogg, GstOggPad *pad) gst_ogg_pad_push (GstOggDemux *ogg, GstOggPad *pad)
{ {
@ -433,13 +686,16 @@ gst_ogg_pad_push (GstOggDemux *ogg, GstOggPad *pad)
gst_ogg_pad_reset (ogg, pad); gst_ogg_pad_reset (ogg, pad);
break; break;
case 1: { case 1: {
/* only push data when plazing, not during seek or similar */
if (ogg->state != GST_OGG_STATE_PLAY)
continue;
if (!pad->pad) { if (!pad->pad) {
GstCaps *caps = gst_ogg_type_find (&packet); GstCaps *caps = gst_ogg_type_find (&packet);
gchar *name = g_strdup_printf ("serial %d", pad->serial); gchar *name = g_strdup_printf ("serial_%d", pad->serial);
if (caps == NULL) { if (caps == NULL) {
gst_element_error (ogg, STREAM, TYPE_NOT_FOUND, NULL, NULL); GST_WARNING_OBJECT (ogg, "couldn't find caps for stream with serial %d", pad->serial);
return; caps = gst_caps_new_simple ("application/octet-stream", NULL);
} }
pad->pad = gst_pad_new_from_template ( pad->pad = gst_pad_new_from_template (
gst_static_pad_template_get (&ogg_demux_src_template_factory), gst_static_pad_template_get (&ogg_demux_src_template_factory),
@ -454,11 +710,15 @@ gst_ogg_pad_push (GstOggDemux *ogg, GstOggPad *pad)
gst_pad_set_active (pad->pad, TRUE); gst_pad_set_active (pad->pad, TRUE);
gst_element_add_pad (GST_ELEMENT (ogg), pad->pad); gst_element_add_pad (GST_ELEMENT (ogg), pad->pad);
} }
/* optimization: use a bufferpool containing the ogg packet */ /* optimization: use a bufferpool containing the ogg packet? */
buf = gst_buffer_new_and_alloc (packet.bytes); buf = gst_pad_alloc_buffer (pad->pad, GST_BUFFER_OFFSET_NONE, packet.bytes);
memcpy (buf->data, packet.packet, packet.bytes); memcpy (buf->data, packet.packet, packet.bytes);
if (pad->offset != -1)
GST_BUFFER_OFFSET (buf) = pad->offset; GST_BUFFER_OFFSET (buf) = pad->offset;
pad->offset = GST_BUFFER_OFFSET_END (buf) = packet.granulepos; if (packet.granulepos != -1)
GST_BUFFER_OFFSET_END (buf) = packet.granulepos;
pad->offset = packet.granulepos;
if (GST_PAD_IS_USABLE (pad->pad))
gst_pad_push (pad->pad, GST_DATA (buf)); gst_pad_push (pad->pad, GST_DATA (buf));
break; break;
} }
@ -476,6 +736,24 @@ gst_ogg_pad_reset (GstOggDemux *ogg, GstOggPad *pad)
pad->offset = GST_BUFFER_OFFSET_NONE; pad->offset = GST_BUFFER_OFFSET_NONE;
/* FIXME: need a discont here */ /* FIXME: need a discont here */
} }
static void
gst_ogg_chains_clear (GstOggDemux *ogg)
{
gint i;
GSList *walk;
for (i = ogg->chains->len - 1; i >= 0; i--) {
GstOggChain *cur = &g_array_index (ogg->chains, GstOggChain, i);
for (walk = cur->pads; walk; walk = g_slist_next (walk)) {
gst_ogg_pad_remove (ogg, (GstOggPad *) walk->data);
}
g_slist_free (cur->pads);
g_array_remove_index (ogg->chains, i);
}
ogg->current_chain = -1;
}
static GstElementStateReturn static GstElementStateReturn
gst_ogg_demux_change_state (GstElement *element) gst_ogg_demux_change_state (GstElement *element)
{ {
@ -495,9 +773,10 @@ gst_ogg_demux_change_state (GstElement *element)
case GST_STATE_PLAYING_TO_PAUSED: case GST_STATE_PLAYING_TO_PAUSED:
break; break;
case GST_STATE_PAUSED_TO_READY: case GST_STATE_PAUSED_TO_READY:
while (ogg->srcpads) { gst_ogg_chains_clear (ogg);
gst_ogg_pad_remove (ogg, (GstOggPad *) ogg->srcpads->data); GST_OGG_SET_STATE (ogg, GST_OGG_STATE_START);
} ogg->seek_pad = NULL;
ogg->seek_to = 0;
break; break;
case GST_STATE_READY_TO_NULL: case GST_STATE_READY_TO_NULL:
ogg_sync_clear (&ogg->sync); ogg_sync_clear (&ogg->sync);
@ -590,3 +869,24 @@ GST_PLUGIN_DEFINE (
GST_PACKAGE, GST_PACKAGE,
GST_ORIGIN GST_ORIGIN
) )
/* prints all info about the element */
static void
gst_ogg_print (GstOggDemux *ogg)
{
#if 0
guint i;
GSList *walk;
for (i = 0; i < ogg->chains->len; i++) {
GstOggChain *chain = &g_array_index (ogg->chains, GstOggChain, i);
g_print ("chain %d (%u streams):\n", i, g_slist_length (chain->pads));
for (walk = chain->pads; walk; walk = g_slist_next (walk)) {
GstOggPad *pad = (GstOggPad *) walk->data;
g_print (" stream %d:\n", pad->serial);
g_print (" length %"G_GUINT64_FORMAT"\n", pad->length);
g_print (" pages %ld\n", pad->pages);
}
}
#endif
}

View file

@ -1,10 +1,10 @@
plugin_LTLIBRARIES = libgstvorbis.la plugin_LTLIBRARIES = libgstvorbis.la
libgstvorbis_la_SOURCES = vorbis.c vorbisenc.c vorbisfile.c libgstvorbis_la_SOURCES = vorbis.c vorbisdec.c vorbisenc.c vorbisfile.c
libgstvorbis_la_CFLAGS = $(GST_CFLAGS) $(VORBIS_CFLAGS) libgstvorbis_la_CFLAGS = $(GST_CFLAGS) $(VORBIS_CFLAGS)
## AM_PATH_VORBIS also sets VORBISENC_LIBS ## AM_PATH_VORBIS also sets VORBISENC_LIBS
libgstvorbis_la_LIBADD = $(VORBIS_LIBS) $(VORBISENC_LIBS) $(VORBISFILE_LIBS) libgstvorbis_la_LIBADD = $(VORBIS_LIBS) $(VORBISENC_LIBS) $(VORBISFILE_LIBS)
libgstvorbis_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstvorbis_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
noinst_HEADERS = vorbisenc.h noinst_HEADERS = vorbisenc.h vorbisdec.h

View file

@ -21,21 +21,27 @@
#include "config.h" #include "config.h"
#endif #endif
#include <vorbisenc.h> #include "vorbisenc.h"
#include "vorbisdec.h"
extern GType vorbisfile_get_type(void); extern GType vorbisfile_get_type(void);
static gboolean static gboolean
plugin_init (GstPlugin *plugin) plugin_init (GstPlugin *plugin)
{ {
if (!gst_library_load ("gstbytestream")) if (!gst_library_load ("gstbytestream"))
return FALSE; return FALSE;
if (!gst_library_load ("gsttags"))
return FALSE;
if (!gst_element_register (plugin, "vorbisenc", GST_RANK_NONE, GST_TYPE_VORBISENC)) if (!gst_element_register (plugin, "vorbisenc", GST_RANK_NONE, GST_TYPE_VORBISENC))
return FALSE; return FALSE;
if (!gst_element_register (plugin, "vorbisfile", GST_RANK_PRIMARY + 1, vorbisfile_get_type ())) if (!gst_element_register (plugin, "vorbisfile", GST_RANK_SECONDARY, vorbisfile_get_type ()))
return FALSE;
if (!gst_element_register (plugin, "vorbisdec", GST_RANK_PRIMARY, gst_vorbis_dec_get_type ()))
return FALSE; return FALSE;
return TRUE; return TRUE;

361
ext/vorbis/vorbisdec.c Normal file
View file

@ -0,0 +1,361 @@
/* GStreamer
* Copyright (C) 2004 Benjamin Otte <in7y118@public.uni-hamburg.de>
*
* 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 "vorbisdec.h"
#include <string.h>
#include <gst/tag/tag.h>
static GstElementDetails vorbis_dec_details = {
"VorbisDec",
"Filter/Decoder/Audio",
"decode raw vorbis streams to float audio",
"Benjamin Otte <in7y118@public.uni-hamburg.de>",
};
/* Filter signals and args */
enum {
/* FILL ME */
LAST_SIGNAL
};
enum {
ARG_0
};
static GstStaticPadTemplate vorbis_dec_src_factory =
GST_STATIC_PAD_TEMPLATE (
"src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (
"audio/x-raw-float, "
"rate = (int) [ 11025, 48000 ], "
"channels = (int) [ 1, 2 ], "
"endianness = (int) BYTE_ORDER, "
#ifdef GST_VORBIS_DEC_SEQUENTIAL
"layout = \"sequential\", "
#endif
"width = (int) 32, "
"buffer-frames = (int) 0"
)
);
static GstStaticPadTemplate vorbis_dec_sink_factory =
GST_STATIC_PAD_TEMPLATE (
"sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (
"audio/x-vorbis"
)
);
GST_BOILERPLATE (GstVorbisDec, gst_vorbis_dec, GstElement, GST_TYPE_ELEMENT);
static void vorbis_dec_chain (GstPad * pad,
GstData * data);
static GstElementStateReturn
vorbis_dec_change_state (GstElement * element);
static gboolean vorbis_dec_src_event (GstPad * pad,
GstEvent * event);
static void
gst_vorbis_dec_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&vorbis_dec_src_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&vorbis_dec_sink_factory));
gst_element_class_set_details (element_class, &vorbis_dec_details);
}
static void
gst_vorbis_dec_class_init (GstVorbisDecClass *klass)
{
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
gstelement_class->change_state = vorbis_dec_change_state;
}
static GstPadLinkReturn
vorbis_dec_link (GstPad *pad, const GstCaps *caps)
{
GstVorbisDec *dec = GST_VORBIS_DEC (gst_pad_get_parent (pad));
if (dec->packetno < 3)
return GST_PAD_LINK_DELAYED;
return GST_PAD_LINK_OK;
}
static GstCaps *
vorbis_dec_getcaps (GstPad *pad)
{
GstVorbisDec *dec = GST_VORBIS_DEC (gst_pad_get_parent (pad));
if (dec->packetno < 3)
return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
return gst_caps_new_simple ("audio/x-raw-float",
"rate", G_TYPE_INT, dec->vi.rate,
"channels", G_TYPE_INT, dec->vi.channels,
"endianness", G_TYPE_INT, G_BYTE_ORDER,
"width", G_TYPE_INT, 32,
#ifdef GST_VORBIS_DEC_SEQUENTIAL
"layout", G_TYPE_STRING, "sequential",
#endif
"buffer-frames", G_TYPE_INT, 0,
NULL);
}
static void
gst_vorbis_dec_init (GstVorbisDec *dec)
{
dec->sinkpad = gst_pad_new_from_template(
gst_static_pad_template_get (&vorbis_dec_sink_factory), "sink");
gst_pad_set_chain_function (dec->sinkpad, vorbis_dec_chain);
gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
dec->srcpad = gst_pad_new_from_template(
gst_static_pad_template_get (&vorbis_dec_src_factory), "src");
gst_pad_set_link_function (dec->srcpad, vorbis_dec_link);
gst_pad_set_getcaps_function (dec->srcpad, vorbis_dec_getcaps);
gst_pad_set_event_function (dec->srcpad, vorbis_dec_src_event);
gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
GST_FLAG_SET (dec, GST_ELEMENT_EVENT_AWARE);
}
static gboolean
vorbis_dec_to_granulepos (GstVorbisDec *dec, GstFormat format, guint64 from, guint64 *to)
{
if (dec->packetno < 1) return FALSE;
switch (format) {
case GST_FORMAT_TIME:
*to = from * dec->vi.rate / GST_SECOND;
return TRUE;
case GST_FORMAT_DEFAULT:
*to = from;
return TRUE;
case GST_FORMAT_BYTES:
*to = from / sizeof (float) / dec->vi.channels;
return TRUE;
default:
return FALSE;
}
}
static gboolean
vorbis_dec_src_event (GstPad *pad, GstEvent *event)
{
gboolean res = TRUE;
GstVorbisDec *dec;
dec = GST_VORBIS_DEC (gst_pad_get_parent (pad));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK: {
guint64 value;
res = vorbis_dec_to_granulepos (dec, GST_EVENT_SEEK_FORMAT (event),
GST_EVENT_SEEK_OFFSET (event), &value);
if (res) {
GstEvent *real_seek = gst_event_new_seek (
(GST_EVENT_SEEK_TYPE (event) & ~GST_SEEK_FORMAT_MASK) | GST_FORMAT_DEFAULT,
value);
res = gst_pad_send_event (GST_PAD_PEER (dec->sinkpad), real_seek);
}
break;
}
default:
res = gst_pad_event_default (pad, event);
break;
}
gst_event_unref (event);
return res;
}
static void
vorbis_dec_event (GstVorbisDec *dec, GstEvent *event)
{
guint64 value;
GST_LOG_OBJECT (dec, "handling event");
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_DISCONTINUOUS:
if (gst_event_discont_get_value (event, GST_FORMAT_DEFAULT, &value)) {
dec->granulepos = value;
dec->packetno = 3;
GST_DEBUG_OBJECT (dec, "setting granuleposition to %"G_GUINT64_FORMAT" after discont\n", value);
} else {
GST_WARNING_OBJECT (dec,
"discont event didn't include offset, we might set it wrong now");
}
break;
default:
break;
}
gst_pad_event_default (dec->sinkpad, event);
}
static void
vorbis_dec_chain (GstPad *pad, GstData *data)
{
GstBuffer *buf;
GstVorbisDec *vd;
ogg_packet packet; /* lol */
vd = GST_VORBIS_DEC (gst_pad_get_parent (pad));
if (GST_IS_EVENT (data)) {
vorbis_dec_event (vd, GST_EVENT (data));
return;
}
buf = GST_BUFFER (data);
/* make ogg_packet out of the buffer */
packet.packet = GST_BUFFER_DATA (buf);
packet.bytes = GST_BUFFER_SIZE (buf);
packet.granulepos = GST_BUFFER_OFFSET_END (buf);
packet.packetno = vd->packetno ++;
/* switch depending on packet type */
if (packet.packet[0] & 1) {
/* header packet */
if (packet.packet[0] / 2 != packet.packetno) {
/* FIXME: just skip? */
gst_element_error (GST_ELEMENT (vd), STREAM, DECODE,
(NULL), ("unexpected packet type %d", (gint) packet.packet[0]));
gst_data_unref (data);
return;
}
if (vorbis_synthesis_headerin (&vd->vi, &vd->vc, &packet)) {
gst_element_error (GST_ELEMENT (vd), STREAM, DECODE,
(NULL), ("couldn't read header packet"));
gst_data_unref (data);
return;
}
if (packet.packetno == 1) {
GstTagList *list = gst_tag_list_from_vorbiscomment_buffer (buf, "\003vorbis", 7, NULL);
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_ENCODER_VERSION, vd->vi.version, NULL);
if (vd->vi.bitrate_upper)
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_MAXIMUM_BITRATE, (guint) vd->vi.bitrate_upper, NULL);
if (vd->vi.bitrate_nominal)
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_BITRATE, (guint) vd->vi.bitrate_nominal, NULL);
if (vd->vi.bitrate_lower)
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_MINIMUM_BITRATE, (guint) vd->vi.bitrate_lower, NULL);
gst_element_found_tags_for_pad (GST_ELEMENT (vd), vd->srcpad, 0, list);
} else if (packet.packetno == 2) {
/* done */
vorbis_synthesis_init (&vd->vd, &vd->vi);
vorbis_block_init (&vd->vd, &vd->vb);
if (gst_pad_is_linked (vd->srcpad))
gst_pad_renegotiate (vd->srcpad);
}
} else {
float **pcm;
guint sample_count;
/* normal data packet */
if (vorbis_synthesis (&vd->vb, &packet)) {
gst_element_error (GST_ELEMENT (vd), STREAM, DECODE,
(NULL), ("couldn't read data packet"));
gst_data_unref (data);
return;
}
if (vorbis_synthesis_blockin (&vd->vd, &vd->vb) < 0) {
gst_element_error (GST_ELEMENT (vd), STREAM, DECODE,
(NULL), ("vorbis decoder did not accept data packet"));
gst_data_unref (data);
return;
}
sample_count = vorbis_synthesis_pcmout (&vd->vd, &pcm);
if (sample_count > 0) {
int i, j;
GstBuffer *out = gst_pad_alloc_buffer (vd->srcpad, GST_BUFFER_OFFSET_NONE,
sample_count * vd->vi.channels * sizeof (float));
float *out_data = (float *) GST_BUFFER_DATA (out);
#ifdef GST_VORBIS_DEC_SEQUENTIAL
for (i = 0; i < vd->vi.channels; i++) {
memcpy (out_data, pcm[i], sample_count * sizeof (float));
out_data += sample_count;
}
#else
for (j = 0; j < sample_count; j++) {
for (i = 0; i < vd->vi.channels; i++) {
*out_data = pcm[i][j];
out_data++;
}
}
#endif
GST_BUFFER_OFFSET (out) = vd->granulepos;
GST_BUFFER_OFFSET_END (out) = vd->granulepos + sample_count;
GST_BUFFER_TIMESTAMP (out) = vd->granulepos * GST_SECOND / vd->vi.rate;
GST_BUFFER_DURATION (out) = sample_count * GST_SECOND / vd->vi.rate;
gst_pad_push (vd->srcpad, GST_DATA (out));
vorbis_synthesis_read (&vd->vd, sample_count);
vd->granulepos += sample_count;
}
}
gst_data_unref (data);
}
static GstElementStateReturn
vorbis_dec_change_state (GstElement *element)
{
GstVorbisDec *vd = GST_VORBIS_DEC (element);
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_NULL_TO_READY:
break;
case GST_STATE_READY_TO_PAUSED:
vorbis_info_init (&vd->vi);
vorbis_comment_init (&vd->vc);
break;
case GST_STATE_PAUSED_TO_PLAYING:
break;
case GST_STATE_PLAYING_TO_PAUSED:
break;
case GST_STATE_PAUSED_TO_READY:
vorbis_block_clear (&vd->vb);
vorbis_dsp_clear (&vd->vd);
vorbis_comment_clear (&vd->vc);
vorbis_info_clear (&vd->vi);
vd->packetno = 0;
vd->granulepos = 0;
break;
case GST_STATE_READY_TO_NULL:
break;
default:
g_assert_not_reached ();
break;
}
return parent_class->change_state (element);
}

73
ext/vorbis/vorbisdec.h Normal file
View file

@ -0,0 +1,73 @@
/* -*- c-basic-offset: 2 -*-
* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* 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_VORBIS_DEC_H__
#define __GST_VORBIS_DEC_H__
#include <gst/gst.h>
#include <vorbis/codec.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define GST_TYPE_VORBIS_DEC \
(gst_vorbis_dec_get_type())
#define GST_VORBIS_DEC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VORBIS_DEC,GstVorbisDec))
#define GST_VORBIS_DEC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VORBIS_DEC,GstVorbisDec))
#define GST_IS_VORBIS_DEC(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VORBIS_DEC))
#define GST_IS_VORBIS_DEC_CLASS(obj) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VORBIS_DEC))
typedef struct _GstVorbisDec GstVorbisDec;
typedef struct _GstVorbisDecClass GstVorbisDecClass;
struct _GstVorbisDec {
GstElement element;
GstPad * sinkpad;
GstPad * srcpad;
vorbis_dsp_state vd;
vorbis_info vi;
vorbis_comment vc;
vorbis_block vb;
guint packetno;
guint64 granulepos;
};
struct _GstVorbisDecClass {
GstElementClass parent_class;
};
GType gst_vorbis_dec_get_type(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __GST_VORBIS_DEC_H__ */

View file

@ -1048,7 +1048,7 @@ dv_type_find (GstTypeFind *tf, gpointer private)
} }
} }
/*** application/x-vorbis *****************************************************/ /*** audio/x-vorbis ***********************************************************/
static GstStaticCaps vorbis_caps = GST_STATIC_CAPS ("audio/x-vorbis"); static GstStaticCaps vorbis_caps = GST_STATIC_CAPS ("audio/x-vorbis");
#define VORBIS_CAPS gst_caps_copy(gst_static_caps_get(&vorbis_caps)) #define VORBIS_CAPS gst_caps_copy(gst_static_caps_get(&vorbis_caps))
@ -1084,6 +1084,23 @@ vorbis_type_find (GstTypeFind *tf, gpointer private)
} }
} }
/*** video/x-theora ***********************************************************/
static GstStaticCaps theora_caps = GST_STATIC_CAPS ("video/x-theora");
#define THEORA_CAPS gst_caps_copy(gst_static_caps_get(&theora_caps))
static void
theora_type_find (GstTypeFind *tf, gpointer private)
{
guint8 *data = gst_type_find_peek (tf, 0, 7); //42);
if (data) {
if (data[0] != 0x80) return;
if (memcmp (&data[1], "theora", 6) != 0) return;
/* FIXME: make this more reliable when specs are out */
}
gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, THEORA_CAPS);
}
/*** generic typefind for streams that have some data at a specific position***/ /*** generic typefind for streams that have some data at a specific position***/
typedef struct { typedef struct {
@ -1276,6 +1293,8 @@ plugin_init (GstPlugin *plugin)
compress_exts, "\037\235", 2, GST_TYPE_FIND_LIKELY); compress_exts, "\037\235", 2, GST_TYPE_FIND_LIKELY);
TYPE_FIND_REGISTER (plugin, "audio/x-vorbis", GST_RANK_PRIMARY, TYPE_FIND_REGISTER (plugin, "audio/x-vorbis", GST_RANK_PRIMARY,
vorbis_type_find, NULL, VORBIS_CAPS, NULL); vorbis_type_find, NULL, VORBIS_CAPS, NULL);
TYPE_FIND_REGISTER (plugin, "video/x-theora", GST_RANK_PRIMARY,
theora_type_find, NULL, THEORA_CAPS, NULL);
TYPE_FIND_REGISTER (plugin, "audio/x-m4a", GST_RANK_PRIMARY, TYPE_FIND_REGISTER (plugin, "audio/x-m4a", GST_RANK_PRIMARY,
m4a_type_find, m4a_exts, AAC_CAPS, NULL); m4a_type_find, m4a_exts, AAC_CAPS, NULL);