Updated seek example.

Original commit message from CVS:
* 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.
This commit is contained in:
Wim Taymans 2005-07-16 14:47:27 +00:00
parent 567802ca2c
commit 82dc411e33
14 changed files with 289 additions and 289 deletions

View file

@ -1,3 +1,48 @@
2005-07-16 Wim Taymans <wim@fluendo.com>
* 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 <wim@fluendo.com>
* gst/audioconvert/gstaudioconvert.c:

View file

@ -28,6 +28,7 @@ an implementation of an audio ringbuffer
@empty_seg:
@state:
@segdone:
@segbase:
@waiting:
<!-- ##### STRUCT GstRingBufferClass ##### -->

View file

@ -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");
}

View file

@ -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));

View file

@ -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->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);
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:

View file

@ -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);
#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;
if (vd->granulepos != -1) {
GST_BUFFER_OFFSET_END (out) = vd->granulepos + sample_count;
if (vd->granulepos != -1)
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);

View file

@ -26,10 +26,7 @@
#include <gst/gst.h>
#include <vorbis/codec.h>
#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__ */

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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 >*/

View file

@ -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 {

View file

@ -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 {

View file

@ -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");
}