From 6eed8ca285111a98c198dcdcd7c6729d9ae81ac4 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 20 Oct 2008 15:35:37 +0000 Subject: [PATCH] gst-libs/gst/audio/gstaudiosink.c: Implement a separate activate functions to start monitoring the segments or, in pu... Original commit message from CVS: * gst-libs/gst/audio/gstaudiosink.c: (gst_audioringbuffer_class_init), (gst_audioringbuffer_acquire), (gst_audioringbuffer_activate), (gst_audioringbuffer_release), (gst_audioringbuffer_stop): Implement a separate activate functions to start monitoring the segments or, in pull mode, pulling in data. * gst-libs/gst/audio/gstbaseaudiosink.c: (gst_base_audio_sink_init), (gst_base_audio_sink_dispose), (gst_base_audio_sink_query_pad), (gst_base_audio_sink_query), (gst_base_audio_sink_setcaps), (gst_base_audio_sink_callback), (gst_base_audio_sink_activate_pull), (gst_base_audio_sink_async_play), (gst_base_audio_sink_change_state): Implement pad and element convert query function. Activate the ringbuffer. Use the segment last_stop value as the offset to pull. Use new basesink _do_preroll() method to preroll in the pulling thread. Take appropriate locking in the pulling thread. * gst-libs/gst/audio/gstringbuffer.h: Update some docs. --- ChangeLog | 25 ++++++ gst-libs/gst/audio/gstaudiosink.c | 80 ++++++++++------- gst-libs/gst/audio/gstbaseaudiosink.c | 159 +++++++++++++++++++++++++++++----- gst-libs/gst/audio/gstringbuffer.h | 3 +- 4 files changed, 209 insertions(+), 58 deletions(-) diff --git a/ChangeLog b/ChangeLog index f2f4f2a..6dcf7d9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2008-10-20 Wim Taymans + + * gst-libs/gst/audio/gstaudiosink.c: + (gst_audioringbuffer_class_init), (gst_audioringbuffer_acquire), + (gst_audioringbuffer_activate), (gst_audioringbuffer_release), + (gst_audioringbuffer_stop): + Implement a separate activate functions to start monitoring the segments + or, in pull mode, pulling in data. + + * gst-libs/gst/audio/gstbaseaudiosink.c: + (gst_base_audio_sink_init), (gst_base_audio_sink_dispose), + (gst_base_audio_sink_query_pad), (gst_base_audio_sink_query), + (gst_base_audio_sink_setcaps), (gst_base_audio_sink_callback), + (gst_base_audio_sink_activate_pull), + (gst_base_audio_sink_async_play), + (gst_base_audio_sink_change_state): + Implement pad and element convert query function. + Activate the ringbuffer. + Use the segment last_stop value as the offset to pull. + Use new basesink _do_preroll() method to preroll in the pulling thread. + Take appropriate locking in the pulling thread. + + * gst-libs/gst/audio/gstringbuffer.h: + Update some docs. + 2008-10-20 Sebastian Dröge * gst/typefind/gsttypefindfunctions.c: (mxf_type_find): diff --git a/gst-libs/gst/audio/gstaudiosink.c b/gst-libs/gst/audio/gstaudiosink.c index dd868ca..b4b4571 100644 --- a/gst-libs/gst/audio/gstaudiosink.c +++ b/gst-libs/gst/audio/gstaudiosink.c @@ -129,6 +129,8 @@ static gboolean gst_audioringbuffer_start (GstRingBuffer * buf); static gboolean gst_audioringbuffer_pause (GstRingBuffer * buf); static gboolean gst_audioringbuffer_stop (GstRingBuffer * buf); static guint gst_audioringbuffer_delay (GstRingBuffer * buf); +static gboolean gst_audioringbuffer_activate (GstRingBuffer * buf, + gboolean active); /* ringbuffer abstract base class */ static GType @@ -187,6 +189,8 @@ gst_audioringbuffer_class_init (GstAudioRingBufferClass * klass) gstringbuffer_class->stop = GST_DEBUG_FUNCPTR (gst_audioringbuffer_stop); gstringbuffer_class->delay = GST_DEBUG_FUNCPTR (gst_audioringbuffer_delay); + gstringbuffer_class->activate = + GST_DEBUG_FUNCPTR (gst_audioringbuffer_activate); } typedef guint (*WriteFunc) (GstAudioSink * sink, gpointer data, guint length); @@ -360,16 +364,13 @@ gst_audioringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) { GstAudioSink *sink; GstAudioSinkClass *csink; - GstAudioRingBuffer *abuf; gboolean result = FALSE; - GError *error = NULL; sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); csink = GST_AUDIO_SINK_GET_CLASS (sink); if (csink->prepare) result = csink->prepare (sink, spec); - if (!result) goto could_not_prepare; @@ -379,37 +380,61 @@ gst_audioringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize); memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data)); - abuf = GST_AUDIORING_BUFFER_CAST (buf); - abuf->running = TRUE; - - GST_DEBUG_OBJECT (sink, "starting thread"); - sink->thread = - g_thread_create ((GThreadFunc) audioringbuffer_thread_func, buf, TRUE, - &error); - if (!sink->thread || error != NULL) - goto thread_failed; - - GST_DEBUG_OBJECT (sink, "waiting for thread"); - /* the object lock is taken */ - GST_AUDIORING_BUFFER_WAIT (buf); - GST_DEBUG_OBJECT (sink, "thread is started"); - - return result; + return TRUE; + /* ERRORS */ could_not_prepare: { GST_DEBUG_OBJECT (sink, "could not prepare device"); return FALSE; } +} + +static gboolean +gst_audioringbuffer_activate (GstRingBuffer * buf, gboolean active) +{ + GstAudioSink *sink; + GstAudioRingBuffer *abuf; + GError *error = NULL; + + sink = GST_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + abuf = GST_AUDIORING_BUFFER_CAST (buf); + + if (active) { + abuf->running = TRUE; + + GST_DEBUG_OBJECT (sink, "starting thread"); + sink->thread = + g_thread_create ((GThreadFunc) audioringbuffer_thread_func, buf, TRUE, + &error); + if (!sink->thread || error != NULL) + goto thread_failed; + + GST_DEBUG_OBJECT (sink, "waiting for thread"); + /* the object lock is taken */ + GST_AUDIORING_BUFFER_WAIT (buf); + GST_DEBUG_OBJECT (sink, "thread is started"); + } else { + abuf->running = FALSE; + GST_DEBUG_OBJECT (sink, "signal wait"); + GST_AUDIORING_BUFFER_SIGNAL (buf); + + GST_OBJECT_UNLOCK (buf); + + /* join the thread */ + g_thread_join (sink->thread); + + GST_OBJECT_LOCK (buf); + } + return TRUE; + + /* ERRORS */ thread_failed: { if (error) GST_ERROR_OBJECT (sink, "could not create thread %s", error->message); else GST_ERROR_OBJECT (sink, "could not create thread for unknown reason"); - /* still unprepare */ - if (csink->unprepare) - result = csink->unprepare (sink); return FALSE; } } @@ -427,17 +452,6 @@ gst_audioringbuffer_release (GstRingBuffer * buf) csink = GST_AUDIO_SINK_GET_CLASS (sink); abuf = GST_AUDIORING_BUFFER_CAST (buf); - abuf->running = FALSE; - GST_DEBUG_OBJECT (sink, "signal wait"); - GST_AUDIORING_BUFFER_SIGNAL (buf); - - GST_OBJECT_UNLOCK (buf); - - /* join the thread */ - g_thread_join (sink->thread); - - GST_OBJECT_LOCK (buf); - /* free the buffer */ gst_buffer_unref (buf->data); buf->data = NULL; diff --git a/gst-libs/gst/audio/gstbaseaudiosink.c b/gst-libs/gst/audio/gstbaseaudiosink.c index 09055b3..302e469 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.c +++ b/gst-libs/gst/audio/gstbaseaudiosink.c @@ -148,6 +148,9 @@ static gboolean gst_base_audio_sink_setcaps (GstBaseSink * bsink, GstCaps * caps); static void gst_base_audio_sink_fixate (GstBaseSink * bsink, GstCaps * caps); +static gboolean gst_base_audio_sink_query_pad (GstPad * pad, GstQuery * query); + + /* static guint gst_base_audio_sink_signals[LAST_SIGNAL] = { 0 }; */ static void @@ -239,6 +242,10 @@ gst_base_audio_sink_init (GstBaseAudioSink * baseaudiosink, /* FIXME, enable pull mode when segments, latency, state changes, negotiation * and clock slaving are figured out */ GST_BASE_SINK (baseaudiosink)->can_activate_pull = FALSE; + + /* install some custom pad_query functions */ + gst_pad_set_query_function (GST_BASE_SINK_PAD (baseaudiosink), + GST_DEBUG_FUNCPTR (gst_base_audio_sink_query_pad)); } static void @@ -260,6 +267,7 @@ gst_base_audio_sink_dispose (GObject * object) G_OBJECT_CLASS (parent_class)->dispose (object); } + static GstClock * gst_base_audio_sink_provide_clock (GstElement * elem) { @@ -299,11 +307,47 @@ clock_disabled: } static gboolean +gst_base_audio_sink_query_pad (GstPad * pad, GstQuery * query) +{ + gboolean res = FALSE; + GstBaseAudioSink *basesink; + + basesink = GST_BASE_AUDIO_SINK (gst_pad_get_parent (pad)); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONVERT: + { + GstFormat src_fmt, dest_fmt; + gint64 src_val, dest_val; + + GST_LOG_OBJECT (pad, "query convert"); + + if (basesink->ringbuffer) { + gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, NULL); + res = gst_ring_buffer_convert (basesink->ringbuffer, src_fmt, src_val, + dest_fmt, &dest_val); + if (res) { + gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); + } + } + break; + } + default: + break; + } + + gst_object_unref (basesink); + + return res; +} + +static gboolean gst_base_audio_sink_query (GstElement * element, GstQuery * query) { gboolean res = FALSE; + GstBaseAudioSink *basesink; - GstBaseAudioSink *basesink = GST_BASE_AUDIO_SINK (element); + basesink = GST_BASE_AUDIO_SINK (element); switch (GST_QUERY_TYPE (query)) { case GST_QUERY_LATENCY: @@ -358,6 +402,23 @@ gst_base_audio_sink_query (GstElement * element, GstQuery * query) } break; } + case GST_QUERY_CONVERT: + { + GstFormat src_fmt, dest_fmt; + gint64 src_val, dest_val; + + GST_LOG_OBJECT (basesink, "query convert"); + + if (basesink->ringbuffer) { + gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, NULL); + res = gst_ring_buffer_convert (basesink->ringbuffer, src_fmt, src_val, + dest_fmt, &dest_val); + if (res) { + gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); + } + } + break; + } default: res = GST_ELEMENT_CLASS (parent_class)->query (element, query); break; @@ -559,6 +620,7 @@ gst_base_audio_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) GST_DEBUG_OBJECT (sink, "release old ringbuffer"); /* release old ringbuffer */ + gst_ring_buffer_activate (sink->ringbuffer, FALSE); gst_ring_buffer_release (sink->ringbuffer); GST_DEBUG_OBJECT (sink, "parse caps"); @@ -572,11 +634,15 @@ gst_base_audio_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) gst_ring_buffer_debug_spec_buff (spec); - GST_DEBUG_OBJECT (sink, "acquire new ringbuffer"); - + GST_DEBUG_OBJECT (sink, "acquire ringbuffer"); if (!gst_ring_buffer_acquire (sink->ringbuffer, spec)) goto acquire_error; + if (bsink->pad_mode == GST_ACTIVATE_PUSH) { + GST_DEBUG_OBJECT (sink, "activate ringbuffer"); + gst_ring_buffer_activate (sink->ringbuffer, TRUE); + } + /* calculate actual latency and buffer times. * FIXME: In 0.11, store the latency_time internally in ns */ spec->latency_time = gst_util_uint64_scale (spec->segsize, @@ -1523,25 +1589,6 @@ gst_base_audio_sink_create_ringbuffer (GstBaseAudioSink * sink) return buffer; } -static gboolean -gst_base_audio_sink_activate_pull (GstBaseSink * basesink, gboolean active) -{ - gboolean ret; - GstBaseAudioSink *sink = GST_BASE_AUDIO_SINK (basesink); - - if (active) { - gst_ring_buffer_set_callback (sink->ringbuffer, - gst_base_audio_sink_callback, sink); - ret = gst_ring_buffer_start (sink->ringbuffer); - } else { - gst_ring_buffer_set_callback (sink->ringbuffer, NULL, NULL); - /* stop thread */ - ret = gst_ring_buffer_release (sink->ringbuffer); - } - - return ret; -} - static void gst_base_audio_sink_callback (GstRingBuffer * rbuf, guint8 * data, guint len, gpointer user_data) @@ -1554,11 +1601,15 @@ gst_base_audio_sink_callback (GstRingBuffer * rbuf, guint8 * data, guint len, basesink = GST_BASE_SINK (user_data); sink = GST_BASE_AUDIO_SINK (user_data); + GST_PAD_STREAM_LOCK (basesink->sinkpad); + /* would be nice to arrange for pad_alloc_buffer to return data -- as it is we will copy twice, once into data, once into DMA */ GST_LOG_OBJECT (basesink, "pulling %d bytes offset %" G_GUINT64_FORMAT " to fill audio buffer", len, basesink->offset); - ret = gst_pad_pull_range (basesink->sinkpad, basesink->offset, len, &buf); + ret = + gst_pad_pull_range (basesink->sinkpad, basesink->segment.last_stop, len, + &buf); if (ret != GST_FLOW_OK) { if (ret == GST_FLOW_UNEXPECTED) @@ -1567,15 +1618,27 @@ gst_base_audio_sink_callback (GstRingBuffer * rbuf, guint8 * data, guint len, goto error; } + GST_PAD_PREROLL_LOCK (basesink->sinkpad); + if (basesink->flushing) + goto flushing; + + /* complete preroll and wait for PLAYING */ + ret = gst_base_sink_do_preroll (basesink, GST_MINI_OBJECT_CAST (buf)); + if (ret != GST_FLOW_OK) + goto preroll_error; + if (len != GST_BUFFER_SIZE (buf)) { GST_INFO_OBJECT (basesink, "short read pulling from sink pad: %d<%d", len, GST_BUFFER_SIZE (buf)); len = MIN (GST_BUFFER_SIZE (buf), len); } - basesink->offset += len; + basesink->segment.last_stop += len; memcpy (data, GST_BUFFER_DATA (buf), len); + GST_PAD_PREROLL_UNLOCK (basesink->sinkpad); + + GST_PAD_STREAM_UNLOCK (basesink->sinkpad); return; @@ -1583,6 +1646,8 @@ error: { GST_WARNING_OBJECT (basesink, "Got flow error but can't return it: %d", ret); + gst_ring_buffer_pause (rbuf); + GST_PAD_STREAM_UNLOCK (basesink->sinkpad); return; } eos: @@ -1594,9 +1659,49 @@ eos: gst_element_post_message (GST_ELEMENT_CAST (sink), gst_message_new_eos (GST_OBJECT_CAST (sink))); gst_base_audio_sink_drain (sink); + gst_ring_buffer_pause (rbuf); + GST_PAD_STREAM_UNLOCK (basesink->sinkpad); + } +flushing: + { + GST_DEBUG_OBJECT (sink, "we are flushing"); + gst_ring_buffer_pause (rbuf); + GST_PAD_PREROLL_UNLOCK (basesink->sinkpad); + GST_PAD_STREAM_UNLOCK (basesink->sinkpad); + return; + } +preroll_error: + { + GST_DEBUG_OBJECT (sink, "error %s", gst_flow_get_name (ret)); + gst_ring_buffer_pause (rbuf); + GST_PAD_PREROLL_UNLOCK (basesink->sinkpad); + GST_PAD_STREAM_UNLOCK (basesink->sinkpad); + return; } } +static gboolean +gst_base_audio_sink_activate_pull (GstBaseSink * basesink, gboolean active) +{ + gboolean ret; + GstBaseAudioSink *sink = GST_BASE_AUDIO_SINK (basesink); + + if (active) { + GST_DEBUG_OBJECT (basesink, "activating pull"); + + gst_ring_buffer_set_callback (sink->ringbuffer, + gst_base_audio_sink_callback, sink); + + ret = gst_ring_buffer_activate (sink->ringbuffer, TRUE); + } else { + GST_DEBUG_OBJECT (basesink, "deactivating pull"); + gst_ring_buffer_set_callback (sink->ringbuffer, NULL, NULL); + ret = gst_ring_buffer_activate (sink->ringbuffer, FALSE); + } + + return ret; +} + /* should be called with the LOCK */ static GstStateChangeReturn gst_base_audio_sink_async_play (GstBaseSink * basesink) @@ -1608,6 +1713,10 @@ gst_base_audio_sink_async_play (GstBaseSink * basesink) GST_DEBUG_OBJECT (sink, "ringbuffer may start now"); sink->priv->sync_latency = TRUE; gst_ring_buffer_may_start (sink->ringbuffer, TRUE); + if (basesink->pad_mode == GST_ACTIVATE_PULL) { + /* we always start the ringbuffer in pull mode immediatly */ + gst_ring_buffer_start (sink->ringbuffer); + } return GST_STATE_CHANGE_SUCCESS; } @@ -1676,6 +1785,7 @@ gst_base_audio_sink_change_state (GstElement * element, gst_clock_set_master (sink->provided_clock, NULL); break; case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_ring_buffer_activate (sink->ringbuffer, FALSE); gst_ring_buffer_release (sink->ringbuffer); break; case GST_STATE_CHANGE_READY_TO_NULL: @@ -1683,6 +1793,7 @@ gst_base_audio_sink_change_state (GstElement * element, * caps, which happens before we commit the state to PAUSED and thus the * PAUSED->READY state change (see above, where we release the ringbuffer) * might not be called when we get here. */ + gst_ring_buffer_activate (sink->ringbuffer, FALSE); gst_ring_buffer_release (sink->ringbuffer); gst_ring_buffer_close_device (sink->ringbuffer); break; diff --git a/gst-libs/gst/audio/gstringbuffer.h b/gst-libs/gst/audio/gstringbuffer.h index be0b27c..f7342e2 100644 --- a/gst-libs/gst/audio/gstringbuffer.h +++ b/gst-libs/gst/audio/gstringbuffer.h @@ -298,7 +298,8 @@ struct _GstRingBuffer { * @resume: resume processing of samples after pause * @stop: stop processing of samples * @delay: get number of samples queued in device - * @activate: activate the thread that starts pulling. Since 0.10.22 + * @activate: activate the thread that starts pulling and monitoring the + * consumed segments in the device. Since 0.10.22 * * The vmethods that subclasses can override to implement the ringbuffer. */ -- 2.7.4