diff --git a/ChangeLog b/ChangeLog index d5e3754234..6ff35d3aaf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,48 @@ +2005-07-16 Wim Taymans + + * docs/libs/tmpl/gstringbuffer.sgml: + * examples/seeking/seek.c: (make_vorbis_theora_pipeline), + (query_rates), (query_positions_elems), (query_positions_pads), + (update_scale), (do_seek): + Updated seek example. + + * ext/ogg/gstoggdemux.c: (gst_ogg_pad_submit_packet), + (gst_ogg_pad_submit_page), (gst_ogg_demux_activate_chain), + (gst_ogg_demux_find_chains), (gst_ogg_demux_send_event), + (gst_ogg_demux_loop): + Push out correct discont values. + + * ext/theora/theoradec.c: (theora_dec_src_convert), + (theora_dec_sink_convert), (theora_dec_src_getcaps), + (theora_dec_sink_event), (theora_handle_type_packet), + (theora_handle_header_packet), (theora_dec_push), + (theora_handle_data_packet), (theora_dec_chain), + (theora_dec_change_state): + Better timestamping. + + * ext/vorbis/vorbisdec.c: (gst_vorbis_dec_init), + (vorbis_dec_sink_event), (vorbis_dec_push), + (vorbis_handle_data_packet), (vorbis_dec_chain): + * ext/vorbis/vorbisdec.h: + Better timestamping. + + * gst-libs/gst/audio/gstbaseaudiosink.c: + (gst_base_audio_sink_get_time), (gst_base_audio_sink_get_times), + (gst_base_audio_sink_event), (gst_base_audio_sink_render): + Handle syncing on timestamps instead of sample offsets. Make + use of DISCONT values as described in design docs. + + * gst-libs/gst/audio/gstbaseaudiosrc.c: + (gst_base_audio_src_get_time): + * gst-libs/gst/audio/gstringbuffer.c: (gst_ring_buffer_acquire), + (gst_ring_buffer_set_sample), (gst_ring_buffer_commit), + (gst_ring_buffer_read): + * gst-libs/gst/audio/gstringbuffer.h: + * sys/ximage/ximagesink.c: (gst_ximagesink_get_times), + (gst_ximagesink_show_frame): + * sys/xvimage/xvimagesink.c: (gst_xvimagesink_get_times): + Correcly convert buffer timestamp to stream time. + 2005-07-16 Wim Taymans * gst/audioconvert/gstaudioconvert.c: diff --git a/docs/libs/tmpl/gstringbuffer.sgml b/docs/libs/tmpl/gstringbuffer.sgml index fe309533bf..e1a6277f5d 100644 --- a/docs/libs/tmpl/gstringbuffer.sgml +++ b/docs/libs/tmpl/gstringbuffer.sgml @@ -28,6 +28,7 @@ an implementation of an audio ringbuffer @empty_seg: @state: @segdone: +@segbase: @waiting: diff --git a/examples/seeking/seek.c b/examples/seeking/seek.c index 6f8e284c39..e14bb685a8 100644 --- a/examples/seeking/seek.c +++ b/examples/seeking/seek.c @@ -21,7 +21,7 @@ static guint update_id; static guint seek_timeout_id = 0; static gulong changed_id; -//#define SOURCE "gnomevfssrc" +//#define SOURCE "filesrc" #define SOURCE "gnomevfssrc" #define ASINK "alsasink" //#define ASINK "osssink" @@ -33,8 +33,8 @@ static gulong changed_id; #define UPDATE_INTERVAL 500 /* number of milliseconds to play for after a seek */ -#define SCRUB_TIME 250 -#define SCRUB +//#define SCRUB_TIME 250 +//#define SCRUB #define THREAD #define PAD_SEEK @@ -376,6 +376,7 @@ make_vorbis_theora_pipeline (const gchar * location) GstElement *audiosink, *videosink; GstElement *a_queue, *v_queue; GstPad *seekable; + GstPad *pad; pipeline = gst_pipeline_new ("app"); @@ -405,8 +406,9 @@ make_vorbis_theora_pipeline (const gchar * location) gst_element_link (a_decoder, a_convert); gst_element_link (a_convert, audiosink); - gst_element_add_ghost_pad (audio_bin, gst_element_get_pad (a_queue, "sink"), - "sink"); + pad = gst_element_get_pad (a_queue, "sink"); + gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad)); + gst_object_unref (GST_OBJECT_CAST (pad)); setup_dynamic_link (demux, NULL, gst_element_get_pad (audio_bin, "sink"), NULL); @@ -427,8 +429,9 @@ make_vorbis_theora_pipeline (const gchar * location) gst_element_link_many (v_queue, v_decoder, v_convert, videosink, NULL); - gst_element_add_ghost_pad (video_bin, gst_element_get_pad (v_queue, "sink"), - "sink"); + pad = gst_element_get_pad (v_queue, "sink"); + gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad)); + gst_object_unref (GST_OBJECT_CAST (pad)); setup_dynamic_link (demux, NULL, gst_element_get_pad (video_bin, "sink"), NULL); @@ -933,6 +936,8 @@ update_scale (gpointer data) gtk_widget_queue_draw (hscale); } + gst_object_unref (clock); + return TRUE; } @@ -992,7 +997,7 @@ do_seek (GtkWidget * widget) } if (res) - GST_PIPELINE (pipeline)->stream_time = real; + gst_pipeline_set_new_stream_time (GST_PIPELINE (pipeline), 0); else g_print ("seek failed\n"); } diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c index a7a0fea43b..de3f438b7c 100644 --- a/ext/ogg/gstoggdemux.c +++ b/ext/ogg/gstoggdemux.c @@ -898,6 +898,7 @@ static gboolean gst_ogg_demux_sink_activate_pull (GstPad * sinkpad, static gboolean gst_ogg_demux_sink_activate_push (GstPad * sinkpad, gboolean active); static GstElementStateReturn gst_ogg_demux_change_state (GstElement * element); +static void gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event); static void gst_ogg_print (GstOggDemux * demux); @@ -1173,6 +1174,7 @@ gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain) gint i; GList *headers; GstOggPad *pad; + GstEvent *event; if (chain == ogg->current_chain) return TRUE; @@ -1188,6 +1190,13 @@ gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain) gst_element_no_more_pads (GST_ELEMENT (ogg)); ogg->current_chain = chain; + /* send the base time */ + event = gst_event_new_discontinuous (1.0, + GST_FORMAT_TIME, (gint64) chain->start_time, (gint64) chain->total_time, + NULL); + + gst_ogg_demux_send_event (ogg, event); + /* then send out the buffers */ for (i = 0; i < chain->streams->len; i++) { pad = g_array_index (chain->streams, GstOggPad *, i); @@ -1775,7 +1784,7 @@ gst_ogg_demux_find_chains (GstOggDemux * ogg) GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i); chain->total_time = 0; - chain->start_time = 0; + chain->start_time = G_MAXINT64; chain->last_time = 0; chain->begin_time = ogg->total_time; @@ -1904,7 +1913,7 @@ gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer) } static void -gst_ogg_demux_send_eos (GstOggDemux * ogg) +gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event) { GstOggChain *chain = ogg->current_chain; @@ -1914,9 +1923,11 @@ gst_ogg_demux_send_eos (GstOggDemux * ogg) for (i = 0; i < chain->streams->len; i++) { GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); - gst_pad_push_event (GST_PAD (pad), gst_event_new (GST_EVENT_EOS)); + gst_event_ref (event); + gst_pad_push_event (GST_PAD (pad), event); } } + gst_event_unref (event); } /* random access code @@ -1946,6 +1957,7 @@ gst_ogg_demux_loop (GstOggPad * pad) GST_CHAIN_UNLOCK (ogg); if (!got_chains) goto chain_read_failed; + ogg->need_chains = FALSE; ogg->offset = 0; } @@ -1953,7 +1965,7 @@ gst_ogg_demux_loop (GstOggPad * pad) GST_LOG_OBJECT (ogg, "pull data %lld", ogg->offset); if (ogg->offset == ogg->length) { ret = GST_FLOW_OK; - gst_ogg_demux_send_eos (ogg); + gst_ogg_demux_send_event (ogg, gst_event_new (GST_EVENT_EOS)); goto pause; } @@ -1982,7 +1994,7 @@ pause: GST_LOG_OBJECT (ogg, "pausing task, reason %d", ret); gst_pad_pause_task (ogg->sinkpad); if (GST_FLOW_IS_FATAL (ret)) { - gst_ogg_demux_send_eos (ogg); + gst_ogg_demux_send_event (ogg, gst_event_new (GST_EVENT_EOS)); GST_ELEMENT_ERROR (ogg, STREAM, STOPPED, ("stream stopped, reason %d", ret), ("stream stopped, reason %d", ret)); diff --git a/ext/theora/theoradec.c b/ext/theora/theoradec.c index 9a76becf70..d5d30c120c 100644 --- a/ext/theora/theoradec.c +++ b/ext/theora/theoradec.c @@ -54,10 +54,11 @@ struct _GstTheoraDec theora_info info; theora_comment comment; - guint packetno; + gboolean have_header; guint64 granulepos; - gboolean initialized; + GstClockTime last_timestamp; + guint64 frame_nr; gboolean need_keyframe; gint width, height; gint offset_x, offset_y; @@ -281,7 +282,7 @@ theora_dec_src_convert (GstPad * pad, dec = GST_THEORA_DEC (GST_PAD_PARENT (pad)); /* we need the info part before we can done something */ - if (dec->packetno < 1) + if (!dec->have_header) return FALSE; if (src_format == *dest_format) { @@ -347,7 +348,7 @@ theora_dec_sink_convert (GstPad * pad, dec = GST_THEORA_DEC (GST_PAD_PARENT (pad)); /* we need the info part before we can done something */ - if (dec->packetno < 1) + if (!dec->have_header) return FALSE; if (src_format == *dest_format) { @@ -581,69 +582,32 @@ theora_dec_src_getcaps (GstPad * pad) static gboolean theora_dec_sink_event (GstPad * pad, GstEvent * event) { - gint64 start_value, end_value, time, bytes; gboolean ret = TRUE; GstTheoraDec *dec; - dec = GST_THEORA_DEC (GST_PAD_PARENT (pad)); + dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); GST_LOG_OBJECT (dec, "handling event"); switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + GST_STREAM_LOCK (pad); + ret = gst_pad_push_event (dec->srcpad, event); + GST_STREAM_UNLOCK (pad); + break; case GST_EVENT_DISCONTINUOUS: GST_STREAM_LOCK (pad); - if (gst_event_discont_get_value (event, GST_FORMAT_DEFAULT, - &start_value, &end_value)) { - dec->granulepos = start_value; - GST_DEBUG_OBJECT (dec, - "setting granuleposition to %" G_GUINT64_FORMAT " after discont", - start_value); - } else { - GST_WARNING_OBJECT (dec, - "discont event didn't include offset, we might set it wrong now"); - dec->granulepos = -1; - } - if (dec->packetno < 3) { - if (dec->granulepos != 0) - GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), - ("can't handle discont before parsing first 3 packets")); - dec->packetno = 0; - gst_pad_push_event (dec->srcpad, gst_event_new_discontinuous (FALSE, - GST_FORMAT_TIME, (guint64) 0, GST_FORMAT_DEFAULT, - (guint64) 0, GST_FORMAT_BYTES, (guint64) 0, 0)); - } else { - GstFormat time_format, default_format, bytes_format; - - time_format = GST_FORMAT_TIME; - default_format = GST_FORMAT_DEFAULT; - bytes_format = GST_FORMAT_BYTES; - - /* if one of them works, all of them work */ - if (theora_dec_sink_convert (dec->sinkpad, GST_FORMAT_DEFAULT, - dec->granulepos, &time_format, &time) - && theora_dec_src_convert (dec->srcpad, GST_FORMAT_TIME, time, - &default_format, &start_value) - && theora_dec_src_convert (dec->srcpad, GST_FORMAT_TIME, time, - &bytes_format, &bytes)) { - gst_pad_push_event (dec->srcpad, - gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, - time, GST_FORMAT_DEFAULT, start_value, GST_FORMAT_BYTES, - bytes, 0)); - /* store new framenumber */ - dec->packetno = start_value + 3; - } else { - GST_ERROR_OBJECT (dec, - "failed to parse data for DISCONT event, not sending any"); - } - /* sync to keyframe */ - dec->need_keyframe = TRUE; - } + dec->need_keyframe = TRUE; + dec->granulepos = -1; + dec->last_timestamp = -1; + ret = gst_pad_push_event (dec->srcpad, event); GST_STREAM_UNLOCK (pad); - gst_event_unref (event); break; default: - ret = gst_pad_event_default (dec->sinkpad, event); + ret = gst_pad_push_event (dec->srcpad, event); break; } + gst_object_unref (dec); + return ret; } @@ -750,7 +714,7 @@ theora_handle_type_packet (GstTheoraDec * dec, ogg_packet * packet) gst_pad_set_caps (dec->srcpad, caps); gst_caps_unref (caps); - dec->initialized = TRUE; + dec->have_header = TRUE; return GST_FLOW_OK; } @@ -765,15 +729,18 @@ theora_handle_header_packet (GstTheoraDec * dec, ogg_packet * packet) if (theora_decode_header (&dec->info, &dec->comment, packet)) goto header_read_error; - switch (packet->packetno) { - case 1: + switch (packet->packet[0]) { + case 0x81: res = theora_handle_comment_packet (dec, packet); break; - case 2: + case 0x82: res = theora_handle_type_packet (dec, packet); break; default: /* ignore */ + g_warning ("unknown theora header packet found"); + case 0x80: + /* nothing special, this is the identification header */ res = GST_FLOW_OK; break; } @@ -792,8 +759,39 @@ static GstFlowReturn theora_dec_push (GstTheoraDec * dec, GstBuffer * buf) { GstFlowReturn result; + GstClockTime outtime = GST_BUFFER_TIMESTAMP (buf); - result = gst_pad_push (dec->srcpad, buf); + if (outtime == GST_CLOCK_TIME_NONE) { + dec->queued = g_list_append (dec->queued, buf); + GST_DEBUG_OBJECT (dec, "queued buffer"); + result = GST_FLOW_OK; + } else { + if (dec->queued) { + gint64 size; + GList *walk; + + GST_DEBUG_OBJECT (dec, "first buffer with time %" GST_TIME_FORMAT, + GST_TIME_ARGS (outtime)); + + size = g_list_length (dec->queued); + for (walk = dec->queued; walk; walk = g_list_next (walk)) { + GstBuffer *buffer = GST_BUFFER (walk->data); + GstClockTime time; + + time = outtime - ((size * GST_SECOND * dec->info.fps_denominator) + / dec->info.fps_numerator); + + GST_DEBUG_OBJECT (dec, "patch buffer %lld %lld", size, time); + GST_BUFFER_TIMESTAMP (buffer) = time; + /* ignore the result */ + gst_pad_push (dec->srcpad, buffer); + size--; + } + g_list_free (dec->queued); + dec->queued = NULL; + } + result = gst_pad_push (dec->srcpad, buf); + } return result; } @@ -813,7 +811,7 @@ theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet, gint cwidth, cheight; GstFlowReturn result; - if (!dec->initialized) + if (!dec->have_header) goto not_initialized; /* the second most significant bit of the first data byte is cleared @@ -894,8 +892,9 @@ theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet, } } - GST_BUFFER_OFFSET (out) = dec->packetno - 4; - GST_BUFFER_OFFSET_END (out) = dec->packetno - 3; + GST_BUFFER_OFFSET (out) = dec->frame_nr; + dec->frame_nr++; + GST_BUFFER_OFFSET_END (out) = dec->frame_nr; GST_BUFFER_DURATION (out) = GST_SECOND * ((gdouble) dec->info.fps_denominator) / dec->info.fps_numerator; @@ -909,7 +908,7 @@ theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet, not_initialized: { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, - (NULL), ("no header sent yet (packet no is %d)", packet->packetno)); + (NULL), ("no header sent yet")); return GST_FLOW_ERROR; } dropping: @@ -946,110 +945,50 @@ theora_dec_chain (GstPad * pad, GstBuffer * buf) { GstTheoraDec *dec; ogg_packet packet; - guint64 offset_end; - GstClockTime outtime; GstFlowReturn result = GST_FLOW_OK; - dec = GST_THEORA_DEC (GST_PAD_PARENT (pad)); - - if (dec->packetno >= 3) { - /* try timestamp first */ - outtime = GST_BUFFER_TIMESTAMP (buf); - - if (outtime == -1) { - gboolean need_flush = FALSE; - - /* the offset end field in theora is actually the time of - * this frame, not the end time */ - offset_end = GST_BUFFER_OFFSET_END (buf); - GST_DEBUG_OBJECT (dec, "got buffer with granule %lld", offset_end); - - if (offset_end != -1) { - if (dec->granulepos == -1) { - GST_DEBUG_OBJECT (dec, "first buffer with granule"); - need_flush = TRUE; - } - dec->granulepos = offset_end; - } - if (dec->granulepos == -1) { - /* we need to wait for a buffer with at least a timestamp or an - * offset before we can generate valid timestamps */ - dec->queued = g_list_append (dec->queued, buf); - GST_DEBUG_OBJECT (dec, "queued buffer"); - return GST_FLOW_OK; - } else { - /* granulepos to time */ - outtime = - GST_SECOND * theora_granule_time (&dec->state, dec->granulepos); - - GST_DEBUG_OBJECT (dec, "granulepos=%lld outtime=%" GST_TIME_FORMAT, - dec->granulepos, GST_TIME_ARGS (outtime)); - - if (need_flush) { - GList *walk; - GstClockTime patch; - gint64 len; - gint64 old_granule = dec->granulepos; - - dec->granulepos = -1; - - len = g_list_length (dec->queued); - - GST_DEBUG_OBJECT (dec, "first buffer with granule, flushing"); - - /* now resubmit all queued buffers with corrected timestamps */ - for (walk = dec->queued; walk; walk = g_list_next (walk)) { - GstBuffer *qbuffer = GST_BUFFER (walk->data); - - patch = outtime - (GST_SECOND * len * dec->info.fps_denominator) / - dec->info.fps_numerator, - GST_DEBUG_OBJECT (dec, "patch buffer %lld %lld", len, patch); - GST_BUFFER_TIMESTAMP (qbuffer) = patch; - - /* buffers are unreffed in chain function */ - theora_dec_chain (pad, qbuffer); - len--; - } - g_list_free (dec->queued); - dec->queued = NULL; - - dec->granulepos = old_granule; - } - } - } else { - GST_DEBUG_OBJECT (dec, "got buffer with timestamp %lld", outtime); - } - } else { - /* we don't know yet */ - outtime = -1; - } + dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); /* make ogg_packet out of the buffer */ packet.packet = GST_BUFFER_DATA (buf); packet.bytes = GST_BUFFER_SIZE (buf); - packet.granulepos = dec->granulepos; - packet.packetno = dec->packetno; - packet.b_o_s = (packet.packetno == 0) ? 1 : 0; + packet.granulepos = GST_BUFFER_OFFSET_END (buf); + packet.packetno = 0; /* we don't really care */ + packet.b_o_s = dec->have_header ? 0 : 1; packet.e_o_s = 0; + if (dec->have_header) { + if (packet.granulepos != -1) { + dec->granulepos = packet.granulepos; + dec->last_timestamp = + GST_SECOND * theora_granule_time (&dec->state, packet.granulepos); + } else if (dec->last_timestamp != -1) { + dec->last_timestamp += + (GST_SECOND * dec->info.fps_denominator) / dec->info.fps_numerator; + } + } else { + dec->last_timestamp = -1; + } + GST_DEBUG_OBJECT (dec, "header=%d packetno=%lld, outtime=%" GST_TIME_FORMAT, - packet.packet[0], packet.packetno, GST_TIME_ARGS (outtime)); + packet.packet[0], packet.packetno, GST_TIME_ARGS (dec->last_timestamp)); /* switch depending on packet type */ if (packet.packet[0] & 0x80) { - if (packet.packetno > 3) { + if (dec->have_header) { GST_WARNING_OBJECT (GST_OBJECT (dec), "Ignoring header"); goto done; } result = theora_handle_header_packet (dec, &packet); } else { - result = theora_handle_data_packet (dec, &packet, outtime); + result = theora_handle_data_packet (dec, &packet, dec->last_timestamp); } done: - dec->packetno++; _inc_granulepos (dec); + gst_object_unref (dec); + gst_buffer_unref (buf); return result; @@ -1070,8 +1009,9 @@ theora_dec_change_state (GstElement * element) case GST_STATE_READY_TO_PAUSED: theora_info_init (&dec->info); theora_comment_init (&dec->comment); + dec->have_header = FALSE; dec->need_keyframe = TRUE; - dec->initialized = FALSE; + dec->last_timestamp = -1; dec->granulepos = -1; break; case GST_STATE_PAUSED_TO_PLAYING: @@ -1089,7 +1029,7 @@ theora_dec_change_state (GstElement * element) theora_clear (&dec->state); theora_comment_clear (&dec->comment); theora_info_clear (&dec->info); - dec->packetno = 0; + dec->have_header = FALSE; dec->granulepos = -1; break; case GST_STATE_READY_TO_NULL: diff --git a/ext/vorbis/vorbisdec.c b/ext/vorbis/vorbisdec.c index c8f1e969a5..0ef244233a 100644 --- a/ext/vorbis/vorbisdec.c +++ b/ext/vorbis/vorbisdec.c @@ -171,6 +171,8 @@ gst_vorbis_dec_init (GstVorbisDec * dec) gst_pad_set_query_type_function (dec->srcpad, vorbis_get_query_types); gst_pad_set_query_function (dec->srcpad, vorbis_dec_src_query); gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); + + dec->queued = NULL; } static gboolean @@ -366,90 +368,33 @@ vorbis_dec_src_event (GstPad * pad, GstEvent * event) static gboolean vorbis_dec_sink_event (GstPad * pad, GstEvent * event) { - gint64 start_value, end_value, time, bytes; gboolean ret = TRUE; GstVorbisDec *dec; - dec = GST_VORBIS_DEC (GST_PAD_PARENT (pad)); + dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); GST_LOG_OBJECT (dec, "handling event"); switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + GST_STREAM_LOCK (pad); + ret = gst_pad_push_event (dec->srcpad, event); + GST_STREAM_UNLOCK (pad); + break; case GST_EVENT_DISCONTINUOUS: GST_STREAM_LOCK (pad); - if (gst_event_discont_get_value (event, GST_FORMAT_DEFAULT, - (gint64 *) & start_value, &end_value)) { - dec->granulepos = start_value; - GST_DEBUG_OBJECT (dec, - "setting granuleposition to %" G_GUINT64_FORMAT " after discont", - start_value); - } else { - if (gst_event_discont_get_value (event, GST_FORMAT_TIME, - (gint64 *) & start_value, &end_value)) { - dec->granulepos = start_value * dec->vi.rate / GST_SECOND; - GST_DEBUG_OBJECT (dec, - "setting granuleposition to %" G_GUINT64_FORMAT " after discont", - dec->granulepos); - } else { - GST_WARNING_OBJECT (dec, - "discont event didn't include offset, we might set it wrong now"); - dec->granulepos = -1; - } - } - GST_DEBUG ("vd: discont %" G_GUINT64_FORMAT, dec->granulepos); - dec->granulepos = -1; - - if (dec->packetno < 3) { - if (dec->granulepos != 0) - GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), - ("can't handle discont before parsing first 3 packets")); - dec->packetno = 0; -#if 0 - gst_pad_push_event (dec->srcpad, gst_event_new_discontinuous (FALSE, - GST_FORMAT_TIME, (guint64) 0, GST_FORMAT_DEFAULT, - (guint64) 0, GST_FORMAT_BYTES, (guint64) 0, 0)); -#endif - gst_event_ref (event); - gst_pad_push_event (dec->srcpad, event); - } else { - GstFormat time_format, default_format, bytes_format; - - time_format = GST_FORMAT_TIME; - default_format = GST_FORMAT_DEFAULT; - bytes_format = GST_FORMAT_BYTES; - - dec->packetno = 3; - /* if one of them works, all of them work */ - if (vorbis_dec_convert (dec->srcpad, GST_FORMAT_DEFAULT, - dec->granulepos, &time_format, &time) - && vorbis_dec_convert (dec->srcpad, GST_FORMAT_DEFAULT, - dec->granulepos, &bytes_format, &bytes)) { - - gst_event_ref (event); - gst_pad_push_event (dec->srcpad, event); - /* - gst_pad_push_event (dec->srcpad, - gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, - time, GST_FORMAT_DEFAULT, dec->granulepos, - GST_FORMAT_BYTES, bytes, 0)); - */ - } else { - GST_ERROR_OBJECT (dec, - "failed to parse data for DISCONT event, not sending any"); - gst_event_ref (event); - gst_pad_push_event (dec->srcpad, event); - } #ifdef HAVE_VORBIS_SYNTHESIS_RESTART - vorbis_synthesis_restart (&dec->vd); + vorbis_synthesis_restart (&dec->vd); #endif - } + ret = gst_pad_push_event (dec->srcpad, event); GST_STREAM_UNLOCK (pad); - gst_event_unref (event); break; default: - ret = gst_pad_event_default (pad, event); + ret = gst_pad_push_event (dec->srcpad, event); break; } + gst_object_unref (dec); + return ret; } @@ -640,6 +585,52 @@ copy_samples (float *out, float **in, guint samples, gint channels) #endif } +static GstFlowReturn +vorbis_dec_push (GstVorbisDec * dec, GstBuffer * buf) +{ + GstFlowReturn result; + gint64 outoffset = GST_BUFFER_OFFSET (buf); + + if (outoffset == -1) { + dec->queued = g_list_append (dec->queued, buf); + GST_DEBUG_OBJECT (dec, "queued buffer"); + result = GST_FLOW_OK; + } else { + if (dec->queued) { + gint64 size; + GList *walk; + + GST_DEBUG_OBJECT (dec, "first buffer with offset %lld", outoffset); + + size = g_list_length (dec->queued); + for (walk = g_list_last (dec->queued); walk; + walk = g_list_previous (walk)) { + GstBuffer *buffer = GST_BUFFER (walk->data); + + outoffset -= + GST_BUFFER_SIZE (buffer) / (sizeof (float) * dec->vi.channels); + + GST_BUFFER_OFFSET (buffer) = outoffset; + GST_BUFFER_TIMESTAMP (buffer) = outoffset * GST_SECOND / dec->vi.rate;; + GST_DEBUG_OBJECT (dec, "patch buffer %lld offset %lld", size, + outoffset); + size--; + } + for (walk = dec->queued; walk; walk = g_list_next (walk)) { + GstBuffer *buffer = GST_BUFFER (walk->data); + + /* ignore the result */ + gst_pad_push (dec->srcpad, buffer); + } + g_list_free (dec->queued); + dec->queued = NULL; + } + result = gst_pad_push (dec->srcpad, buf); + } + + return result; +} + static GstFlowReturn vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet) { @@ -671,14 +662,15 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet) copy_samples (out_data, pcm, sample_count, vd->vi.channels); GST_BUFFER_OFFSET (out) = vd->granulepos; - GST_BUFFER_OFFSET_END (out) = vd->granulepos + sample_count; - if (vd->granulepos != -1) + if (vd->granulepos != -1) { + GST_BUFFER_OFFSET_END (out) = vd->granulepos + sample_count; GST_BUFFER_TIMESTAMP (out) = vd->granulepos * GST_SECOND / vd->vi.rate; - else + } else { GST_BUFFER_TIMESTAMP (out) = -1; + } GST_BUFFER_DURATION (out) = sample_count * GST_SECOND / vd->vi.rate; - result = gst_pad_push (vd->srcpad, out); + result = vorbis_dec_push (vd, out); if (vd->granulepos != -1) vd->granulepos += sample_count; @@ -692,6 +684,10 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet) result = GST_FLOW_OK; } + /* granulepos is the last sample in the packet */ + if (packet->granulepos != -1) + vd->granulepos = packet->granulepos; + return result; /* ERRORS */ @@ -727,7 +723,7 @@ vorbis_dec_chain (GstPad * pad, GstBuffer * buffer) /* make ogg_packet out of the buffer */ packet.packet = GST_BUFFER_DATA (buffer); packet.bytes = GST_BUFFER_SIZE (buffer); - packet.granulepos = vd->granulepos; + packet.granulepos = GST_BUFFER_OFFSET_END (buffer); packet.packetno = vd->packetno++; /* * FIXME. Is there anyway to know that this is the last packet and @@ -750,10 +746,6 @@ vorbis_dec_chain (GstPad * pad, GstBuffer * buffer) GST_DEBUG ("offset end: %" G_GUINT64_FORMAT, GST_BUFFER_OFFSET_END (buffer)); - /* granulepos is the last sample in the packet */ - if (GST_BUFFER_OFFSET_END_IS_VALID (buffer)) - vd->granulepos = GST_BUFFER_OFFSET_END (buffer);; - done: gst_buffer_unref (buffer); diff --git a/ext/vorbis/vorbisdec.h b/ext/vorbis/vorbisdec.h index deaf8dcd15..bebf75fb76 100644 --- a/ext/vorbis/vorbisdec.h +++ b/ext/vorbis/vorbisdec.h @@ -26,10 +26,7 @@ #include #include -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - +G_BEGIN_DECLS #define GST_TYPE_VORBIS_DEC \ (gst_vorbis_dec_get_type()) @@ -59,6 +56,8 @@ struct _GstVorbisDec { guint64 granulepos; gboolean initialized; + + GList *queued; }; struct _GstVorbisDecClass { @@ -67,9 +66,6 @@ struct _GstVorbisDecClass { GType gst_vorbis_dec_get_type(void); -#ifdef __cplusplus -} -#endif /* __cplusplus */ - +G_END_DECLS #endif /* __GST_VORBIS_DEC_H__ */ diff --git a/gst-libs/gst/audio/gstbaseaudiosink.c b/gst-libs/gst/audio/gstbaseaudiosink.c index 00184143b0..8381c277a2 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.c +++ b/gst-libs/gst/audio/gstbaseaudiosink.c @@ -163,10 +163,10 @@ gst_base_audio_sink_get_time (GstClock * clock, GstBaseAudioSink * sink) if (sink->ringbuffer == NULL || sink->ringbuffer->spec.rate == 0) return 0; + /* our processed samples are always increasing */ samples = gst_ring_buffer_samples_done (sink->ringbuffer); result = samples * GST_SECOND / sink->ringbuffer->spec.rate; - result += GST_ELEMENT (sink)->base_time; return result; } @@ -270,9 +270,8 @@ static void gst_base_audio_sink_get_times (GstBaseSink * bsink, GstBuffer * buffer, GstClockTime * start, GstClockTime * end) { - /* ne need to sync to a clock here, we schedule the samples based - * on our own clock for the moment. FIXME, implement this when - * we are not using our own clock */ + /* our clock sync is a bit too much for the base class to handle so + * we implement it ourselves. */ *start = GST_CLOCK_TIME_NONE; *end = GST_CLOCK_TIME_NONE; } @@ -289,25 +288,6 @@ gst_base_audio_sink_event (GstBaseSink * bsink, GstEvent * event) gst_ring_buffer_pause (sink->ringbuffer); } break; - case GST_EVENT_DISCONTINUOUS: - { - gint64 time, sample; - - if (gst_event_discont_get_value (event, GST_FORMAT_DEFAULT, &sample, - NULL)) - goto have_value; - if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &time, NULL)) { - sample = time * sink->ringbuffer->spec.rate / GST_SECOND; - goto have_value; - } - g_warning ("discont without valid timestamp"); - sample = 0; - - have_value: - GST_DEBUG ("discont now at %lld", sample); - gst_ring_buffer_set_sample (sink->ringbuffer, sample); - break; - } default: break; } @@ -339,17 +319,38 @@ wrong_state: static GstFlowReturn gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf) { - guint64 offset; + guint64 render_offset, in_offset; + GstClockTime time, render_time; GstBaseAudioSink *sink = GST_BASE_AUDIO_SINK (bsink); - offset = GST_BUFFER_OFFSET (buf); - - GST_DEBUG ("in offset %llu, time %" GST_TIME_FORMAT, offset, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); + /* can't do anything when we don't have the device */ if (!gst_ring_buffer_is_acquired (sink->ringbuffer)) goto wrong_state; - gst_ring_buffer_commit (sink->ringbuffer, offset, + in_offset = GST_BUFFER_OFFSET (buf); + time = GST_BUFFER_TIMESTAMP (buf); + + GST_DEBUG ("time %" GST_TIME_FORMAT ", offset %llu, start %" GST_TIME_FORMAT, + GST_TIME_ARGS (time), in_offset, GST_TIME_ARGS (bsink->discont_start)); + + /* samples should be rendered based on their timestamp. All samples + * arriving before the discont_start are to be trown away */ + /* FIXME, for now we drop the sample completely, we should + * in fact clip the sample */ + if (time < bsink->discont_start) + return GST_FLOW_OK; + + /* bring buffer timestamp to stream time */ + render_time = time - bsink->discont_start; + /* add base time to get absolute clock time */ + render_time += gst_element_get_base_time (GST_ELEMENT (bsink)); + /* and bring the time to the offset in the buffer */ + render_offset = render_time * sink->ringbuffer->spec.rate / GST_SECOND; + + GST_DEBUG ("render time %" GST_TIME_FORMAT ", render offset %llu", + GST_TIME_ARGS (render_time), render_offset); + + gst_ring_buffer_commit (sink->ringbuffer, render_offset, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); return GST_FLOW_OK; diff --git a/gst-libs/gst/audio/gstbaseaudiosrc.c b/gst-libs/gst/audio/gstbaseaudiosrc.c index 787a688ff6..5a08a63703 100644 --- a/gst-libs/gst/audio/gstbaseaudiosrc.c +++ b/gst-libs/gst/audio/gstbaseaudiosrc.c @@ -153,7 +153,6 @@ gst_base_audio_src_get_time (GstClock * clock, GstBaseAudioSrc * src) samples = gst_ring_buffer_samples_done (src->ringbuffer); result = samples * GST_SECOND / src->ringbuffer->spec.rate; - result += GST_ELEMENT (src)->base_time; return result; } diff --git a/gst-libs/gst/audio/gstringbuffer.c b/gst-libs/gst/audio/gstringbuffer.c index c07b1d6efc..05eebb736a 100644 --- a/gst-libs/gst/audio/gstringbuffer.c +++ b/gst-libs/gst/audio/gstringbuffer.c @@ -374,8 +374,6 @@ gst_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) buf->empty_seg[i] = buf->spec.silence_sample[j]; j = (j + 1) % buf->spec.bytes_per_sample; } - /* set sample position to 0 */ - gst_ring_buffer_set_sample (buf, 0); } else { g_warning ("invalid bytes_per_sample from acquire ringbuffer, fix the element"); @@ -701,17 +699,20 @@ gst_ring_buffer_set_sample (GstRingBuffer * buf, guint64 sample) if (sample == -1) sample = 0; + if (buf->samples_per_seg == 0) + return; + /* FIXME, we assume the ringbuffer can restart at a random * position, round down to the beginning and keep track of * offset when calculating the processed samples. */ - buf->segdone = sample / buf->samples_per_seg; + buf->segbase = buf->segdone - sample / buf->samples_per_seg; buf->next_sample = sample; for (i = 0; i < buf->spec.segtotal; i++) { gst_ring_buffer_clear (buf, i); } - GST_DEBUG ("setting sample to %llu, segdone %d", sample, buf->segdone); + GST_DEBUG ("set sample to %llu, segbase %d", sample, buf->segbase); } static gboolean @@ -813,7 +814,7 @@ gst_ring_buffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data, gint diff; /* get the currently processed segment */ - segdone = g_atomic_int_get (&buf->segdone); + segdone = g_atomic_int_get (&buf->segdone) - buf->segbase; /* see how far away it is from the write segment */ diff = writeseg - segdone; @@ -931,7 +932,7 @@ gst_ring_buffer_read (GstRingBuffer * buf, guint64 sample, guchar * data, gint diff; /* get the currently processed segment */ - segdone = g_atomic_int_get (&buf->segdone); + segdone = g_atomic_int_get (&buf->segdone) - buf->segbase; /* see how far away it is from the read segment */ diff = segdone - readseg; diff --git a/gst-libs/gst/audio/gstringbuffer.h b/gst-libs/gst/audio/gstringbuffer.h index 8192975c1d..c879cf8463 100644 --- a/gst-libs/gst/audio/gstringbuffer.h +++ b/gst-libs/gst/audio/gstringbuffer.h @@ -161,6 +161,7 @@ struct _GstRingBuffer { /*< public >*/ /* ATOMIC */ gint state; /* state of the buffer */ gint segdone; /* number of segments processed since last start */ + gint segbase; /* segment corresponding to segment 0 */ gint waiting; /* when waiting for a segment to be freed */ /*< private >*/ diff --git a/sys/ximage/ximagesink.c b/sys/ximage/ximagesink.c index 20e5b4d38b..50daa4083b 100644 --- a/sys/ximage/ximagesink.c +++ b/sys/ximage/ximagesink.c @@ -1199,6 +1199,7 @@ gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf, if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { *start = GST_BUFFER_TIMESTAMP (buf); + *start -= bsink->discont_start; if (GST_BUFFER_DURATION_IS_VALID (buf)) { *end = *start + GST_BUFFER_DURATION (buf); } else { diff --git a/sys/xvimage/xvimagesink.c b/sys/xvimage/xvimagesink.c index d15f06ea55..e441d017bc 100644 --- a/sys/xvimage/xvimagesink.c +++ b/sys/xvimage/xvimagesink.c @@ -1496,6 +1496,7 @@ gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf, if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { *start = GST_BUFFER_TIMESTAMP (buf); + *start -= bsink->discont_start; if (GST_BUFFER_DURATION_IS_VALID (buf)) { *end = *start + GST_BUFFER_DURATION (buf); } else { diff --git a/tests/examples/seek/seek.c b/tests/examples/seek/seek.c index 6f8e284c39..e14bb685a8 100644 --- a/tests/examples/seek/seek.c +++ b/tests/examples/seek/seek.c @@ -21,7 +21,7 @@ static guint update_id; static guint seek_timeout_id = 0; static gulong changed_id; -//#define SOURCE "gnomevfssrc" +//#define SOURCE "filesrc" #define SOURCE "gnomevfssrc" #define ASINK "alsasink" //#define ASINK "osssink" @@ -33,8 +33,8 @@ static gulong changed_id; #define UPDATE_INTERVAL 500 /* number of milliseconds to play for after a seek */ -#define SCRUB_TIME 250 -#define SCRUB +//#define SCRUB_TIME 250 +//#define SCRUB #define THREAD #define PAD_SEEK @@ -376,6 +376,7 @@ make_vorbis_theora_pipeline (const gchar * location) GstElement *audiosink, *videosink; GstElement *a_queue, *v_queue; GstPad *seekable; + GstPad *pad; pipeline = gst_pipeline_new ("app"); @@ -405,8 +406,9 @@ make_vorbis_theora_pipeline (const gchar * location) gst_element_link (a_decoder, a_convert); gst_element_link (a_convert, audiosink); - gst_element_add_ghost_pad (audio_bin, gst_element_get_pad (a_queue, "sink"), - "sink"); + pad = gst_element_get_pad (a_queue, "sink"); + gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad)); + gst_object_unref (GST_OBJECT_CAST (pad)); setup_dynamic_link (demux, NULL, gst_element_get_pad (audio_bin, "sink"), NULL); @@ -427,8 +429,9 @@ make_vorbis_theora_pipeline (const gchar * location) gst_element_link_many (v_queue, v_decoder, v_convert, videosink, NULL); - gst_element_add_ghost_pad (video_bin, gst_element_get_pad (v_queue, "sink"), - "sink"); + pad = gst_element_get_pad (v_queue, "sink"); + gst_element_add_pad (video_bin, gst_ghost_pad_new ("sink", pad)); + gst_object_unref (GST_OBJECT_CAST (pad)); setup_dynamic_link (demux, NULL, gst_element_get_pad (video_bin, "sink"), NULL); @@ -933,6 +936,8 @@ update_scale (gpointer data) gtk_widget_queue_draw (hscale); } + gst_object_unref (clock); + return TRUE; } @@ -992,7 +997,7 @@ do_seek (GtkWidget * widget) } if (res) - GST_PIPELINE (pipeline)->stream_time = real; + gst_pipeline_set_new_stream_time (GST_PIPELINE (pipeline), 0); else g_print ("seek failed\n"); }