mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-10 11:29:55 +00:00
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:
parent
f606249297
commit
b660e15f8d
7 changed files with 876 additions and 96 deletions
21
ChangeLog
21
ChangeLog
|
@ -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>
|
||||
|
||||
* gst-libs/gst/play/gstplay.c: (gst_play_seek_to_time): seek on video
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
#endif
|
||||
#include <gst/gst.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>
|
||||
|
||||
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 _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 {
|
||||
GstPad * pad; /* reference for this pad is held by element we belong to */
|
||||
|
||||
int serial;
|
||||
gint serial;
|
||||
ogg_stream_state stream;
|
||||
guint64 offset; /* end offset of last buffer */
|
||||
guint64 known_offset; /* last known offset */
|
||||
guint64 length; /* length of stream or 0 */
|
||||
glong pages;
|
||||
} 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 {
|
||||
GstElement element;
|
||||
|
||||
/* pads */
|
||||
/* pad */
|
||||
GstPad * sinkpad;
|
||||
GSList * srcpads; /* list of GstOggPad */
|
||||
|
||||
|
||||
/* state */
|
||||
GstOggState state;
|
||||
GArray * chains;
|
||||
gint current_chain;
|
||||
guint flags;
|
||||
|
||||
/* ogg stuff */
|
||||
ogg_sync_state sync;
|
||||
|
||||
/* seeking */
|
||||
GstOggPad * seek_pad;
|
||||
guint64 seek_to;
|
||||
};
|
||||
|
||||
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_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 void gst_ogg_demux_finalize (GObject * object);
|
||||
|
||||
static gboolean gst_ogg_demux_src_event (GstPad * pad,
|
||||
GstEvent * event);
|
||||
|
@ -134,32 +168,14 @@ static void gst_ogg_pad_push (GstOggDemux * ogg,
|
|||
|
||||
static GstCaps * gst_ogg_type_find (ogg_packet * packet);
|
||||
|
||||
static void gst_ogg_print (GstOggDemux * demux);
|
||||
|
||||
#define GST_OGG_SET_STATE(ogg, new_state) G_STMT_START{ \
|
||||
GST_DEBUG_OBJECT (ogg, "setting state to %s", G_STRINGIFY (new_state)); \
|
||||
ogg->state = new_state; \
|
||||
}G_STMT_END
|
||||
|
||||
static GstElementClass *parent_class = NULL;
|
||||
/* static guint gst_ogg_demux_signals[LAST_SIGNAL] = { 0 }; */
|
||||
|
||||
GType
|
||||
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;
|
||||
}
|
||||
GST_BOILERPLATE (GstOggDemux, gst_ogg_demux, GstElement, GST_TYPE_ELEMENT)
|
||||
|
||||
static void
|
||||
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));
|
||||
}
|
||||
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);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
|
||||
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
|
||||
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;
|
||||
|
||||
gobject_class->dispose = gst_ogg_demux_dispose;
|
||||
gobject_class->finalize = gst_ogg_demux_finalize;
|
||||
}
|
||||
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 */
|
||||
ogg->sinkpad = gst_pad_new_from_template(
|
||||
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));
|
||||
|
||||
/* 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);
|
||||
}
|
||||
static void
|
||||
gst_ogg_demux_dispose (GObject *object)
|
||||
gst_ogg_demux_finalize (GObject *object)
|
||||
{
|
||||
GstOggDemux *ogg;
|
||||
|
||||
ogg = GST_OGG_DEMUX (object);
|
||||
|
||||
/* srcpads are removed when going to READY */
|
||||
g_assert (ogg->srcpads == NULL);
|
||||
/* chains are removed when going to READY */
|
||||
g_assert (ogg->current_chain == -1);
|
||||
g_assert (ogg->chains->len == 0);
|
||||
g_array_free (ogg->chains, TRUE);
|
||||
}
|
||||
|
||||
static const GstEventMask*
|
||||
|
@ -233,20 +248,41 @@ gst_ogg_demux_get_query_types (GstPad *pad)
|
|||
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
|
||||
gst_ogg_demux_src_query (GstPad *pad, GstQueryType type,
|
||||
GstFormat *format, gint64 *value)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstOggDemux *ogg;
|
||||
|
||||
ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
|
||||
GstOggDemux *ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
|
||||
GstOggPad *cur = gst_ogg_get_pad_by_pad (ogg, pad);
|
||||
|
||||
switch (type) {
|
||||
case GST_QUERY_TOTAL: {
|
||||
if (*format == GST_FORMAT_DEFAULT) {
|
||||
*value = cur->known_offset;
|
||||
res = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GST_QUERY_POSITION:
|
||||
if (*format == GST_FORMAT_DEFAULT && cur->length != 0) {
|
||||
*value = cur->length;
|
||||
res = TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -254,39 +290,114 @@ gst_ogg_demux_src_query (GstPad *pad, GstQueryType type,
|
|||
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
|
||||
gst_ogg_demux_src_event (GstPad *pad, GstEvent *event)
|
||||
{
|
||||
GstOggDemux *ogg;
|
||||
GstOggPad *cur;
|
||||
|
||||
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)) {
|
||||
case GST_EVENT_SEEK:
|
||||
break;
|
||||
{
|
||||
gint64 offset;
|
||||
if (GST_EVENT_SEEK_FORMAT (event) != GST_FORMAT_DEFAULT)
|
||||
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:
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
gst_event_unref (event);
|
||||
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
|
||||
gst_ogg_demux_handle_event (GstPad *pad, GstEvent *event)
|
||||
{
|
||||
GstOggDemux *ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
|
||||
|
||||
ogg=ogg;
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
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;
|
||||
case GST_EVENT_EOS:
|
||||
if (ogg->srcpads)
|
||||
GST_WARNING_OBJECT (ogg, "got EOS in unfinished ogg stream");
|
||||
while (ogg->srcpads) {
|
||||
gst_ogg_pad_remove (ogg, (GstOggPad *) ogg->srcpads->data);
|
||||
if (ogg->state == GST_OGG_STATE_SETUP) {
|
||||
gst_ogg_start_playing (ogg);
|
||||
} else {
|
||||
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;
|
||||
default:
|
||||
gst_pad_event_default (pad, event);
|
||||
|
@ -294,12 +405,39 @@ gst_ogg_demux_handle_event (GstPad *pad, GstEvent *event)
|
|||
}
|
||||
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
|
||||
gst_ogg_demux_chain (GstPad *pad, GstData *buffer)
|
||||
{
|
||||
GstOggDemux *ogg;
|
||||
guint8 *data;
|
||||
int pageout_ret = 1;
|
||||
guint64 offset_end;
|
||||
|
||||
/* handle events */
|
||||
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));
|
||||
|
||||
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));
|
||||
memcpy (data, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
|
||||
if (ogg_sync_wrote (&ogg->sync, GST_BUFFER_SIZE (buffer)) != 0) {
|
||||
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;
|
||||
}
|
||||
offset_end = GST_BUFFER_OFFSET_IS_VALID (buffer) ?
|
||||
GST_BUFFER_OFFSET (buffer) + GST_BUFFER_SIZE (buffer) :
|
||||
(guint64) -1;
|
||||
gst_data_unref (buffer);
|
||||
while (pageout_ret != 0) {
|
||||
ogg_page page;
|
||||
|
@ -323,12 +471,76 @@ gst_ogg_demux_chain (GstPad *pad, GstData *buffer)
|
|||
pageout_ret = ogg_sync_pageout (&ogg->sync, &page);
|
||||
switch (pageout_ret) {
|
||||
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;
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
gst_ogg_demux_push (ogg, &page);
|
||||
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);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
GST_WARNING_OBJECT (ogg, "unknown return value %d from ogg_sync_pageout", pageout_ret);
|
||||
|
@ -336,12 +548,13 @@ gst_ogg_demux_chain (GstPad *pad, GstData *buffer)
|
|||
break;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return;
|
||||
}
|
||||
static GstOggPad *
|
||||
gst_ogg_pad_new (GstOggDemux *ogg, int serial)
|
||||
{
|
||||
GstOggPad *ret = g_new (GstOggPad, 1);
|
||||
GstOggPad *ret = g_new0 (GstOggPad, 1);
|
||||
|
||||
ret->serial = serial;
|
||||
if (ogg_stream_init (&ret->stream, serial) != 0) {
|
||||
|
@ -349,8 +562,6 @@ gst_ogg_pad_new (GstOggDemux *ogg, int serial)
|
|||
g_free (ret);
|
||||
return NULL;
|
||||
}
|
||||
ret->pad = NULL;
|
||||
ret->offset = 0;
|
||||
GST_LOG_OBJECT (ogg, "created new ogg src %p for stream with serial %d", ret, serial);
|
||||
|
||||
return ret;
|
||||
|
@ -358,9 +569,13 @@ gst_ogg_pad_new (GstOggDemux *ogg, int serial)
|
|||
static void
|
||||
gst_ogg_pad_remove (GstOggDemux *ogg, GstOggPad *pad)
|
||||
{
|
||||
ogg->srcpads = g_slist_remove (ogg->srcpads, pad);
|
||||
if (pad->pad) {
|
||||
gst_pad_push (pad->pad, GST_DATA (gst_event_new (GST_EVENT_EOS)));
|
||||
/* 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_element_remove_pad (GST_ELEMENT (ogg), pad->pad);
|
||||
}
|
||||
if (ogg_stream_clear (&pad->stream) != 0)
|
||||
|
@ -375,32 +590,37 @@ gst_ogg_demux_push (GstOggDemux *ogg, ogg_page* page)
|
|||
GstOggPad *cur;
|
||||
|
||||
/* find the stream */
|
||||
walk = ogg->srcpads;
|
||||
while (walk) {
|
||||
for (walk = CURRENT_CHAIN (ogg)->pads; walk; walk = g_slist_next (walk)) {
|
||||
cur = (GstOggPad *) walk->data;
|
||||
if (cur->serial == ogg_page_serialno (page)) {
|
||||
goto br;
|
||||
}
|
||||
walk = g_slist_next (walk);
|
||||
}
|
||||
cur = NULL;
|
||||
br:
|
||||
/* now we either have a stream (cur) or not */
|
||||
if (ogg_page_bos (page)) {
|
||||
if (cur) {
|
||||
/* huh? */
|
||||
GST_WARNING_OBJECT (ogg, "Ignoring error: ogg page declared as BOS while stream already existed.");
|
||||
GST_DEBUG_OBJECT (ogg, "ogg page declared as BOS while stream %d already existed."
|
||||
"Possibly a seek happened.", cur->serial);
|
||||
} else if (cur) {
|
||||
GST_DEBUG_OBJECT (ogg, "reactivating deactivated stream %d.", cur->serial);
|
||||
} else {
|
||||
/* FIXME: monitor if we are still in creation stage? */
|
||||
cur = gst_ogg_pad_new (ogg, ogg_page_serialno (page));
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
if (ogg_stream_pagein (&cur->stream, page) != 0) {
|
||||
|
@ -408,14 +628,47 @@ br:
|
|||
gst_ogg_pad_reset (ogg, cur);
|
||||
return;
|
||||
}
|
||||
gst_ogg_pad_push (ogg, cur);
|
||||
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);
|
||||
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
|
||||
/* Removing pads while PLAYING doesn't work with current schedulers */
|
||||
/* remove from list, as this will never be called again */
|
||||
if (ogg_page_eos (page)) {
|
||||
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
|
||||
}
|
||||
}
|
||||
static void
|
||||
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);
|
||||
break;
|
||||
case 1: {
|
||||
/* only push data when plazing, not during seek or similar */
|
||||
if (ogg->state != GST_OGG_STATE_PLAY)
|
||||
continue;
|
||||
if (!pad->pad) {
|
||||
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) {
|
||||
gst_element_error (ogg, STREAM, TYPE_NOT_FOUND, NULL, NULL);
|
||||
return;
|
||||
GST_WARNING_OBJECT (ogg, "couldn't find caps for stream with serial %d", pad->serial);
|
||||
caps = gst_caps_new_simple ("application/octet-stream", NULL);
|
||||
}
|
||||
pad->pad = gst_pad_new_from_template (
|
||||
gst_static_pad_template_get (&ogg_demux_src_template_factory),
|
||||
|
@ -454,12 +710,16 @@ gst_ogg_pad_push (GstOggDemux *ogg, GstOggPad *pad)
|
|||
gst_pad_set_active (pad->pad, TRUE);
|
||||
gst_element_add_pad (GST_ELEMENT (ogg), pad->pad);
|
||||
}
|
||||
/* optimization: use a bufferpool containing the ogg packet */
|
||||
buf = gst_buffer_new_and_alloc (packet.bytes);
|
||||
/* optimization: use a bufferpool containing the ogg packet? */
|
||||
buf = gst_pad_alloc_buffer (pad->pad, GST_BUFFER_OFFSET_NONE, packet.bytes);
|
||||
memcpy (buf->data, packet.packet, packet.bytes);
|
||||
GST_BUFFER_OFFSET (buf) = pad->offset;
|
||||
pad->offset = GST_BUFFER_OFFSET_END (buf) = packet.granulepos;
|
||||
gst_pad_push (pad->pad, GST_DATA (buf));
|
||||
if (pad->offset != -1)
|
||||
GST_BUFFER_OFFSET (buf) = pad->offset;
|
||||
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));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -476,6 +736,24 @@ gst_ogg_pad_reset (GstOggDemux *ogg, GstOggPad *pad)
|
|||
pad->offset = GST_BUFFER_OFFSET_NONE;
|
||||
/* 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
|
||||
gst_ogg_demux_change_state (GstElement *element)
|
||||
{
|
||||
|
@ -495,9 +773,10 @@ gst_ogg_demux_change_state (GstElement *element)
|
|||
case GST_STATE_PLAYING_TO_PAUSED:
|
||||
break;
|
||||
case GST_STATE_PAUSED_TO_READY:
|
||||
while (ogg->srcpads) {
|
||||
gst_ogg_pad_remove (ogg, (GstOggPad *) ogg->srcpads->data);
|
||||
}
|
||||
gst_ogg_chains_clear (ogg);
|
||||
GST_OGG_SET_STATE (ogg, GST_OGG_STATE_START);
|
||||
ogg->seek_pad = NULL;
|
||||
ogg->seek_to = 0;
|
||||
break;
|
||||
case GST_STATE_READY_TO_NULL:
|
||||
ogg_sync_clear (&ogg->sync);
|
||||
|
@ -590,3 +869,24 @@ GST_PLUGIN_DEFINE (
|
|||
GST_PACKAGE,
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
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)
|
||||
## AM_PATH_VORBIS also sets VORBISENC_LIBS
|
||||
libgstvorbis_la_LIBADD = $(VORBIS_LIBS) $(VORBISENC_LIBS) $(VORBISFILE_LIBS)
|
||||
libgstvorbis_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||
|
||||
noinst_HEADERS = vorbisenc.h
|
||||
noinst_HEADERS = vorbisenc.h vorbisdec.h
|
||||
|
|
|
@ -21,21 +21,27 @@
|
|||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <vorbisenc.h>
|
||||
#include "vorbisenc.h"
|
||||
#include "vorbisdec.h"
|
||||
|
||||
extern GType vorbisfile_get_type(void);
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin *plugin)
|
||||
{
|
||||
|
||||
if (!gst_library_load ("gstbytestream"))
|
||||
return FALSE;
|
||||
|
||||
if (!gst_library_load ("gsttags"))
|
||||
return FALSE;
|
||||
|
||||
if (!gst_element_register (plugin, "vorbisenc", GST_RANK_NONE, GST_TYPE_VORBISENC))
|
||||
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 TRUE;
|
||||
|
|
361
ext/vorbis/vorbisdec.c
Normal file
361
ext/vorbis/vorbisdec.c
Normal 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
73
ext/vorbis/vorbisdec.h
Normal 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__ */
|
|
@ -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");
|
||||
#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***/
|
||||
|
||||
typedef struct {
|
||||
|
@ -1276,6 +1293,8 @@ plugin_init (GstPlugin *plugin)
|
|||
compress_exts, "\037\235", 2, GST_TYPE_FIND_LIKELY);
|
||||
TYPE_FIND_REGISTER (plugin, "audio/x-vorbis", GST_RANK_PRIMARY,
|
||||
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,
|
||||
m4a_type_find, m4a_exts, AAC_CAPS, NULL);
|
||||
|
||||
|
|
Loading…
Reference in a new issue