From cca414a9b5138c60b9174194dcbd10169b30d6d1 Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Fri, 31 Mar 2006 16:43:43 +0000 Subject: [PATCH] ext/vorbis/vorbisparse.c (gst_vorbis_parse_init) Original commit message from CVS: 2006-03-31 Andy Wingo * ext/vorbis/vorbisparse.c (gst_vorbis_parse_init) (vorbis_parse_sink_event): Add an event function to flush our state on a seek, and to drain buffers on a premature EOS. (vorbis_parse_push_headers, vorbis_parse_clear_queue) (vorbis_parse_push_buffer, vorbis_parse_drain_queue_prematurely) (vorbis_parse_chain, vorbis_parse_queue_buffer) (vorbis_parse_drain_queue): Queue up buffers until we can set their timestamps and granulepos values. * ext/vorbis/vorbisparse.h: Include the vorbis decoder headers, and keep track of data needed for deriving granulepos and timestamps for buffers. --- ChangeLog | 15 +++ common | 2 +- ext/vorbis/vorbisparse.c | 281 ++++++++++++++++++++++++++++++++++++++++------- ext/vorbis/vorbisparse.h | 10 ++ 4 files changed, 267 insertions(+), 41 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7415f3f..52d25d7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2006-03-31 Andy Wingo + + * ext/vorbis/vorbisparse.c (gst_vorbis_parse_init) + (vorbis_parse_sink_event): Add an event function to flush our + state on a seek, and to drain buffers on a premature EOS. + (vorbis_parse_push_headers, vorbis_parse_clear_queue) + (vorbis_parse_push_buffer, vorbis_parse_drain_queue_prematurely) + (vorbis_parse_chain, vorbis_parse_queue_buffer) + (vorbis_parse_drain_queue): Queue up buffers until we can set + their timestamps and granulepos values. + + * ext/vorbis/vorbisparse.h: Include the vorbis decoder headers, + and keep track of data needed for deriving granulepos and + timestamps for buffers. + 2006-03-29 Wim Taymans Patch by: j^ diff --git a/common b/common index 45cc64e..c18b429 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 45cc64e522d61410eb8d1a3e7ef67569851cd77a +Subproject commit c18b429f2a3698b6fc5e849a637aa0c8b91e82b9 diff --git a/ext/vorbis/vorbisparse.c b/ext/vorbis/vorbisparse.c index 9fd53ef..80ec867 100644 --- a/ext/vorbis/vorbisparse.c +++ b/ext/vorbis/vorbisparse.c @@ -90,6 +90,7 @@ GST_BOILERPLATE (GstVorbisParse, gst_vorbis_parse, GstElement, static GstFlowReturn vorbis_parse_chain (GstPad * pad, GstBuffer * buffer); static GstStateChangeReturn vorbis_parse_change_state (GstElement * element, GstStateChange transition); +static gboolean vorbis_parse_sink_event (GstPad * pad, GstEvent * event); static void gst_vorbis_parse_base_init (gpointer g_class) @@ -117,6 +118,7 @@ gst_vorbis_parse_init (GstVorbisParse * parse, GstVorbisParseClass * g_class) parse->sinkpad = gst_pad_new_from_static_template (&vorbis_parse_sink_factory, "sink"); gst_pad_set_chain_function (parse->sinkpad, vorbis_parse_chain); + gst_pad_set_event_function (parse->sinkpad, vorbis_parse_sink_event); gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad); parse->srcpad = @@ -168,6 +170,190 @@ vorbis_parse_set_header_on_caps (GstVorbisParse * parse, GstCaps * caps) g_value_unset (&array); } +static void +vorbis_parse_push_headers (GstVorbisParse * parse) +{ + /* mark and put on caps */ + GstCaps *caps; + GstBuffer *outbuf; + ogg_packet packet; + + /* get the headers into the caps, passing them to vorbis as we go */ + caps = gst_caps_make_writable (gst_pad_get_caps (parse->srcpad)); + vorbis_parse_set_header_on_caps (parse, caps); + GST_DEBUG_OBJECT (parse, "here are the caps: %" GST_PTR_FORMAT, caps); + gst_pad_set_caps (parse->srcpad, caps); + gst_caps_unref (caps); + + /* push out buffers, ignoring return value... */ + outbuf = GST_BUFFER_CAST (parse->streamheader->data); + packet.packet = GST_BUFFER_DATA (outbuf); + packet.bytes = GST_BUFFER_SIZE (outbuf); + packet.granulepos = GST_BUFFER_OFFSET_END (outbuf); + packet.packetno = 1; + packet.e_o_s = 0; + vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet); + parse->sample_rate = parse->vi.rate; + + gst_buffer_set_caps (outbuf, GST_PAD_CAPS (parse->srcpad)); + gst_pad_push (parse->srcpad, outbuf); + + outbuf = GST_BUFFER_CAST (parse->streamheader->next->data); + packet.packet = GST_BUFFER_DATA (outbuf); + packet.bytes = GST_BUFFER_SIZE (outbuf); + packet.granulepos = GST_BUFFER_OFFSET_END (outbuf); + packet.packetno = 2; + packet.e_o_s = 0; + vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet); + + gst_buffer_set_caps (outbuf, GST_PAD_CAPS (parse->srcpad)); + gst_pad_push (parse->srcpad, outbuf); + + outbuf = GST_BUFFER_CAST (parse->streamheader->next->next->data); + packet.packet = GST_BUFFER_DATA (outbuf); + packet.bytes = GST_BUFFER_SIZE (outbuf); + packet.granulepos = GST_BUFFER_OFFSET_END (outbuf); + packet.packetno = 3; + packet.e_o_s = 0; + vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet); + + gst_buffer_set_caps (outbuf, GST_PAD_CAPS (parse->srcpad)); + gst_pad_push (parse->srcpad, outbuf); + + g_list_free (parse->streamheader); + parse->streamheader = NULL; + + parse->streamheader_sent = TRUE; +} + +static void +vorbis_parse_clear_queue (GstVorbisParse * parse) +{ + while (parse->buffer_queue->length) { + GstBuffer *buf; + + buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue)); + gst_buffer_unref (buf); + } +} + +static GstFlowReturn +vorbis_parse_push_buffer (GstVorbisParse * parse, GstBuffer * buf, + gint64 granulepos) +{ + guint64 samples; + + /* our hack as noted below */ + samples = GST_BUFFER_OFFSET (buf); + + GST_BUFFER_OFFSET_END (buf) = granulepos; + GST_BUFFER_DURATION (buf) = samples * GST_SECOND / parse->sample_rate; + GST_BUFFER_OFFSET (buf) = granulepos * GST_SECOND / parse->sample_rate; + GST_BUFFER_TIMESTAMP (buf) = + GST_BUFFER_OFFSET (buf) - GST_BUFFER_DURATION (buf); + + gst_buffer_set_caps (buf, GST_PAD_CAPS (parse->srcpad)); + + return gst_pad_push (parse->srcpad, buf); +} + +static GstFlowReturn +vorbis_parse_drain_queue_prematurely (GstVorbisParse * parse) +{ + GstFlowReturn ret = GST_FLOW_OK; + gint64 granulepos = MAX (parse->prev_granulepos, 0); + + /* got an EOS event, make sure to push out any buffers that were in the queue + * -- won't normally be the case, but this catches the + * didn't-get-a-granulepos-on-the-last-packet case. Assuming a continuous + * stream. */ + + while (!g_queue_is_empty (parse->buffer_queue)) { + GstBuffer *buf; + + buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue)); + + granulepos += GST_BUFFER_OFFSET (buf); + ret = vorbis_parse_push_buffer (parse, buf, granulepos); + + if (ret != GST_FLOW_OK) + goto done; + } + + parse->prev_granulepos = granulepos; + +done: + return ret; +} + +static GstFlowReturn +vorbis_parse_drain_queue (GstVorbisParse * parse, gint64 granulepos) +{ + GstFlowReturn ret; + GList *walk; + gint64 cur = granulepos; + gint64 gp; + + for (walk = parse->buffer_queue->head; walk; walk = walk->next) + cur -= GST_BUFFER_OFFSET (walk->data); + + if (parse->prev_granulepos != -1) + cur = MAX (cur, parse->prev_granulepos); + + while (!g_queue_is_empty (parse->buffer_queue)) { + GstBuffer *buf; + + buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue)); + + cur += GST_BUFFER_OFFSET (buf); + gp = CLAMP (cur, 0, granulepos); + + ret = vorbis_parse_push_buffer (parse, buf, gp); + + if (ret != GST_FLOW_OK) + goto done; + } + + parse->prev_granulepos = granulepos; + +done: + return ret; +} + +static GstFlowReturn +vorbis_parse_queue_buffer (GstVorbisParse * parse, GstBuffer * buf) +{ + GstFlowReturn ret = GST_FLOW_OK; + long blocksize; + ogg_packet packet; + + buf = gst_buffer_make_metadata_writable (buf); + + packet.packet = GST_BUFFER_DATA (buf); + packet.bytes = GST_BUFFER_SIZE (buf); + packet.granulepos = GST_BUFFER_OFFSET_END (buf); + packet.packetno = parse->packetno + parse->buffer_queue->length; + packet.e_o_s = 0; + + blocksize = vorbis_packet_blocksize (&parse->vi, &packet); + + /* temporarily store the sample count in OFFSET -- we overwrite this later */ + + if (parse->prev_blocksize < 0) + GST_BUFFER_OFFSET (buf) = 0; + else + GST_BUFFER_OFFSET (buf) = (blocksize + parse->prev_blocksize) / 4; + + parse->prev_blocksize = blocksize; + + g_queue_push_tail (parse->buffer_queue, buf); + + if (GST_BUFFER_OFFSET_END_IS_VALID (buf)) + ret = vorbis_parse_drain_queue (parse, GST_BUFFER_OFFSET_END (buf)); + + return ret; +} + static GstFlowReturn vorbis_parse_chain (GstPad * pad, GstBuffer * buffer) { @@ -175,55 +361,52 @@ vorbis_parse_chain (GstPad * pad, GstBuffer * buffer) GstBuffer *buf; GstVorbisParse *parse; - parse = GST_VORBIS_PARSE (GST_PAD_PARENT (pad)); + parse = GST_VORBIS_PARSE (gst_pad_get_parent (pad)); buf = GST_BUFFER (buffer); parse->packetno++; - /* if 1 <= packetno <= 3, it's streamheader, - * so put it on the streamheader list and return */ if (parse->packetno <= 3) { + /* if 1 <= packetno <= 3, it's streamheader, + * so put it on the streamheader list and return */ parse->streamheader = g_list_append (parse->streamheader, buf); - return GST_FLOW_OK; + ret = GST_FLOW_OK; + } else { + if (!parse->streamheader_sent) + vorbis_parse_push_headers (parse); + + ret = vorbis_parse_queue_buffer (parse, buf); } - /* else, if we haven't sent streamheader buffers yet, - * set caps again, and send out the streamheader buffers */ - if (!parse->streamheader_sent) { - /* mark and put on caps */ - GstCaps *padcaps, *caps; - GstBuffer *outbuf; - - padcaps = gst_pad_get_caps (parse->srcpad); - caps = gst_caps_make_writable (padcaps); - gst_caps_unref (padcaps); - - vorbis_parse_set_header_on_caps (parse, caps); - - /* negotiate with these caps */ - GST_DEBUG_OBJECT (parse, "here are the caps: %" GST_PTR_FORMAT, caps); - gst_pad_set_caps (parse->srcpad, caps); - gst_caps_unref (caps); - - /* push out buffers, ignoring return value... */ - outbuf = GST_BUFFER_CAST (parse->streamheader->data); - gst_buffer_set_caps (outbuf, GST_PAD_CAPS (parse->srcpad)); - gst_pad_push (parse->srcpad, outbuf); - outbuf = GST_BUFFER_CAST (parse->streamheader->next->data); - gst_buffer_set_caps (outbuf, GST_PAD_CAPS (parse->srcpad)); - gst_pad_push (parse->srcpad, outbuf); - outbuf = GST_BUFFER_CAST (parse->streamheader->next->next->data); - gst_buffer_set_caps (outbuf, GST_PAD_CAPS (parse->srcpad)); - gst_pad_push (parse->srcpad, outbuf); - - g_list_free (parse->streamheader); - parse->streamheader = NULL; - - parse->streamheader_sent = TRUE; + gst_object_unref (parse); + + return ret; +} + +static gboolean +vorbis_parse_sink_event (GstPad * pad, GstEvent * event) +{ + gboolean ret; + GstVorbisParse *parse; + + parse = GST_VORBIS_PARSE (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_START: + vorbis_parse_clear_queue (parse); + parse->prev_granulepos = -1; + parse->prev_blocksize = -1; + break; + case GST_EVENT_EOS: + vorbis_parse_drain_queue_prematurely (parse); + ret = gst_pad_event_default (pad, event); + break; + default: + ret = gst_pad_event_default (pad, event); + break; } - /* just send on buffer by default */ - gst_buffer_set_caps (buf, GST_PAD_CAPS (parse->srcpad)); - ret = gst_pad_push (parse->srcpad, buf); + + gst_object_unref (parse); return ret; } @@ -236,13 +419,31 @@ vorbis_parse_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: + vorbis_info_init (&parse->vi); + vorbis_comment_init (&parse->vc); + parse->prev_granulepos = -1; + parse->prev_blocksize = -1; parse->packetno = 0; parse->streamheader_sent = FALSE; + parse->buffer_queue = g_queue_new (); break; default: break; } + ret = parent_class->change_state (element, transition); + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + vorbis_info_clear (&parse->vi); + vorbis_comment_clear (&parse->vc); + vorbis_parse_clear_queue (parse); + g_queue_free (parse->buffer_queue); + parse->buffer_queue = NULL; + break; + default: + break; + } + return ret; } diff --git a/ext/vorbis/vorbisparse.h b/ext/vorbis/vorbisparse.h index 32d8727..23df077 100644 --- a/ext/vorbis/vorbisparse.h +++ b/ext/vorbis/vorbisparse.h @@ -24,6 +24,7 @@ #include +#include G_BEGIN_DECLS @@ -55,6 +56,15 @@ struct _GstVorbisParse { guint packetno; gboolean streamheader_sent; GList * streamheader; + + GQueue * buffer_queue; + + vorbis_info vi; + vorbis_comment vc; + + gint64 prev_granulepos; + gint32 prev_blocksize; + guint32 sample_rate; }; struct _GstVorbisParseClass { -- 2.7.4