diff --git a/ChangeLog b/ChangeLog index c84699f571..16d75dccf8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2005-02-21 Wim Taymans + + * gst/elements/gstfakesink.c: (gst_fakesink_init), + (gst_fakesink_finish_preroll), (gst_fakesink_event), + (gst_fakesink_chain_unlocked), (gst_fakesink_activate), + (gst_fakesink_change_state): + * gst/elements/gstfakesink.h: + * gst/gstpad.c: (gst_real_pad_class_init), (gst_real_pad_init), + (gst_real_pad_dispose), (gst_real_pad_finalize): + * gst/gstpad.h: + * tools/gst-launch.c: (main): + Add finalize method to RealPad. + Add new lock to correctly do the preroll, should probably not + be put here. + Make fakesink do preroll correctly. Emit a message when the + preroll sample is queued. + Add more info in gst-launch regarding state changes. + + 2005-02-21 Andy Wingo * gst/elements/gstfakesink.c: diff --git a/gst/elements/gstfakesink.c b/gst/elements/gstfakesink.c index 8d070a8d9a..85005c7aa0 100644 --- a/gst/elements/gstfakesink.c +++ b/gst/elements/gstfakesink.c @@ -198,12 +198,10 @@ gst_fakesink_class_init (GstFakeSinkClass * klass) static void gst_fakesink_init (GstFakeSink * fakesink) { - GstPad *pad; - - pad = + fakesink->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate), "sink"); - gst_element_add_pad (GST_ELEMENT (fakesink), pad); + gst_element_add_pad (GST_ELEMENT (fakesink), fakesink->sinkpad); fakesink->silent = FALSE; fakesink->dump = FALSE; @@ -212,7 +210,7 @@ gst_fakesink_init (GstFakeSink * fakesink) fakesink->state_error = FAKESINK_STATE_ERROR_NONE; fakesink->signal_handoffs = FALSE; fakesink->pad_mode = GST_ACTIVATE_NONE; - GST_RPAD_TASK (pad) = NULL; + GST_RPAD_TASK (fakesink->sinkpad) = NULL; } static void @@ -357,55 +355,66 @@ gst_fakesink_get_property (GObject * object, guint prop_id, GValue * value, } } -static gboolean -gst_fakesink_activate (GstPad * pad, GstActivateMode mode) +/* STREAM_LOCK should be held */ +GstFlowReturn +gst_fakesink_finish_preroll (GstFakeSink * fakesink, GstPad * pad) { - gboolean result = FALSE; - GstFakeSink *fakesink; + /* lock order is important */ + GST_STATE_LOCK (fakesink); + GST_PREROLL_LOCK (pad); + if (!fakesink->need_preroll) + goto no_preroll; - fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad)); + if (!fakesink->silent) { + g_free (fakesink->last_message); - switch (mode) { - case GST_ACTIVATE_PUSH: - g_return_val_if_fail (fakesink->has_chain, FALSE); - result = TRUE; - break; - case GST_ACTIVATE_PULL: - /* if we have a scheduler we can start the task */ - g_return_val_if_fail (fakesink->has_loop, FALSE); - if (GST_ELEMENT_SCHEDULER (fakesink)) { - GST_STREAM_LOCK (pad); - GST_RPAD_TASK (pad) = - gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (fakesink), - (GstTaskFunction) gst_fakesink_loop, pad); + fakesink->last_message = + g_strdup_printf ("preroll ******* (%s:%s)", GST_DEBUG_PAD_NAME (pad)); - gst_task_start (GST_RPAD_TASK (pad)); - GST_STREAM_UNLOCK (pad); - result = TRUE; - } - break; - case GST_ACTIVATE_NONE: - /* step 1, unblock clock sync (if any) */ - - /* step 2, make sure streaming finishes */ - GST_STREAM_LOCK (pad); - /* step 3, stop the task */ - if (GST_RPAD_TASK (pad)) { - gst_task_stop (GST_RPAD_TASK (pad)); - gst_object_unref (GST_OBJECT (GST_RPAD_TASK (pad))); - GST_RPAD_TASK (pad) = NULL; - } - GST_STREAM_UNLOCK (pad); - - result = TRUE; - break; + g_object_notify (G_OBJECT (fakesink), "last_message"); } - fakesink->pad_mode = mode; + fakesink->need_preroll = FALSE; + fakesink->have_preroll = TRUE; + gst_element_commit_state (GST_ELEMENT (fakesink)); + GST_STATE_UNLOCK (fakesink); - return result; + { + gboolean usable; + + GST_DEBUG ("element %s waiting to finish preroll", + GST_ELEMENT_NAME (fakesink)); + GST_PREROLL_WAIT (pad); + GST_DEBUG ("done preroll"); + + GST_LOCK (pad); + usable = !GST_RPAD_IS_FLUSHING (pad) && GST_RPAD_IS_ACTIVE (pad); + GST_UNLOCK (pad); + if (!usable) + goto unusable; + + GST_DEBUG ("done preroll"); + } + fakesink->have_preroll = FALSE; + + GST_PREROLL_UNLOCK (pad); + return GST_FLOW_OK; + +no_preroll: + { + GST_PREROLL_UNLOCK (pad); + GST_STATE_UNLOCK (fakesink); + return GST_FLOW_OK; + } +unusable: + { + GST_DEBUG ("pad is flushing"); + GST_PREROLL_UNLOCK (pad); + return GST_FLOW_UNEXPECTED; + } } + static gboolean gst_fakesink_event (GstPad * pad, GstEvent * event) { @@ -429,9 +438,15 @@ gst_fakesink_event (GstPad * pad, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: { - gst_element_finish_preroll (GST_ELEMENT (fakesink), pad); - gst_element_post_message (GST_ELEMENT (fakesink), - gst_message_new_eos (GST_OBJECT (fakesink))); + GstFlowReturn ret; + + ret = gst_fakesink_finish_preroll (fakesink, pad); + if (ret == GST_FLOW_OK) { + fakesink->eos = TRUE; + /* ok, we can post the message */ + gst_element_post_message (GST_ELEMENT (fakesink), + gst_message_new_eos (GST_OBJECT (fakesink))); + } break; } case GST_EVENT_DISCONTINUOUS: @@ -455,7 +470,7 @@ gst_fakesink_chain_unlocked (GstPad * pad, GstBuffer * buf) fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad)); - result = gst_element_finish_preroll (GST_ELEMENT (fakesink), pad); + result = gst_fakesink_finish_preroll (fakesink, pad); if (result != GST_FLOW_OK) goto exit; @@ -539,13 +554,67 @@ paused: goto exit; } +static gboolean +gst_fakesink_activate (GstPad * pad, GstActivateMode mode) +{ + gboolean result = FALSE; + GstFakeSink *fakesink; + + fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad)); + + switch (mode) { + case GST_ACTIVATE_PUSH: + g_return_val_if_fail (fakesink->has_chain, FALSE); + result = TRUE; + break; + case GST_ACTIVATE_PULL: + /* if we have a scheduler we can start the task */ + g_return_val_if_fail (fakesink->has_loop, FALSE); + if (GST_ELEMENT_SCHEDULER (fakesink)) { + GST_STREAM_LOCK (pad); + GST_RPAD_TASK (pad) = + gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (fakesink), + (GstTaskFunction) gst_fakesink_loop, pad); + + gst_task_start (GST_RPAD_TASK (pad)); + GST_STREAM_UNLOCK (pad); + result = TRUE; + } + break; + case GST_ACTIVATE_NONE: + /* step 1, unblock clock sync (if any) or any other blocking thing */ + + /* unlock preroll */ + GST_PREROLL_LOCK (pad); + GST_PREROLL_SIGNAL (pad); + GST_PREROLL_UNLOCK (pad); + + /* step 2, make sure streaming finishes */ + GST_STREAM_LOCK (pad); + /* step 3, stop the task */ + if (GST_RPAD_TASK (pad)) { + gst_task_stop (GST_RPAD_TASK (pad)); + gst_object_unref (GST_OBJECT (GST_RPAD_TASK (pad))); + GST_RPAD_TASK (pad) = NULL; + } + GST_STREAM_UNLOCK (pad); + + result = TRUE; + break; + } + fakesink->pad_mode = mode; + + return result; +} + static GstElementStateReturn gst_fakesink_change_state (GstElement * element) { GstElementStateReturn ret = GST_STATE_SUCCESS; GstFakeSink *fakesink = GST_FAKESINK (element); + GstElementState transition = GST_STATE_TRANSITION (element); - switch (GST_STATE_TRANSITION (element)) { + switch (transition) { case GST_STATE_NULL_TO_READY: if (fakesink->state_error == FAKESINK_STATE_ERROR_NULL_READY) goto error; @@ -553,17 +622,42 @@ gst_fakesink_change_state (GstElement * element) case GST_STATE_READY_TO_PAUSED: if (fakesink->state_error == FAKESINK_STATE_ERROR_READY_PAUSED) goto error; - /* need to complete preroll before this state change completes */ + /* need to complete preroll before this state change completes, there + * is no data flow in READY so we cqn safely assume we need to preroll. */ fakesink->offset = 0; + GST_PREROLL_LOCK (fakesink->sinkpad); + fakesink->need_preroll = TRUE; + fakesink->have_preroll = FALSE; + GST_PREROLL_UNLOCK (fakesink->sinkpad); ret = GST_STATE_ASYNC; break; case GST_STATE_PAUSED_TO_PLAYING: if (fakesink->state_error == FAKESINK_STATE_ERROR_PAUSED_PLAYING) goto error; + /* the state change completes when we are blocking on a preroll + * sample */ + GST_PREROLL_LOCK (fakesink->sinkpad); + if (!fakesink->have_preroll) { + fakesink->need_preroll = TRUE; + ret = GST_STATE_ASYNC; + } else { + /* now let it play */ + GST_PREROLL_SIGNAL (fakesink->sinkpad); + } + GST_PREROLL_UNLOCK (fakesink->sinkpad); break; case GST_STATE_PLAYING_TO_PAUSED: if (fakesink->state_error == FAKESINK_STATE_ERROR_PLAYING_PAUSED) goto error; + + GST_PREROLL_LOCK (fakesink->sinkpad); + fakesink->need_preroll = TRUE; + /* if we don't have a preroll buffer and we have not received EOS, + * we need to wait for a preroll */ + if (!fakesink->have_preroll && !fakesink->eos) { + ret = GST_STATE_ASYNC; + } + GST_PREROLL_UNLOCK (fakesink->sinkpad); break; case GST_STATE_PAUSED_TO_READY: if (fakesink->state_error == FAKESINK_STATE_ERROR_PAUSED_READY) @@ -575,10 +669,11 @@ gst_fakesink_change_state (GstElement * element) g_free (fakesink->last_message); fakesink->last_message = NULL; break; + default: + break; } - if (GST_ELEMENT_CLASS (parent_class)->change_state) - GST_ELEMENT_CLASS (parent_class)->change_state (element); + GST_ELEMENT_CLASS (parent_class)->change_state (element); return ret; diff --git a/gst/elements/gstfakesink.h b/gst/elements/gstfakesink.h index 6dd2834fbc..73b1f70d9c 100644 --- a/gst/elements/gstfakesink.h +++ b/gst/elements/gstfakesink.h @@ -68,6 +68,9 @@ struct _GstFakeSink { GstFakeSinkStateError state_error; GstActivateMode pad_mode; guint64 offset; + gboolean eos; + gboolean need_preroll; + gboolean have_preroll; gchar *last_message; }; diff --git a/gst/gstpad.c b/gst/gstpad.c index d1136b61aa..367496bd9b 100644 --- a/gst/gstpad.c +++ b/gst/gstpad.c @@ -159,6 +159,7 @@ enum static void gst_real_pad_class_init (GstRealPadClass * klass); static void gst_real_pad_init (GstRealPad * pad); static void gst_real_pad_dispose (GObject * object); +static void gst_real_pad_finalize (GObject * object); static void gst_real_pad_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); @@ -201,6 +202,7 @@ gst_real_pad_class_init (GstRealPadClass * klass) real_pad_parent_class = g_type_class_ref (GST_TYPE_PAD); gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_real_pad_dispose); + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_real_pad_finalize); gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_real_pad_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_real_pad_get_property); @@ -254,6 +256,9 @@ gst_real_pad_init (GstRealPad * pad) GST_FLAG_UNSET (pad, GST_PAD_ACTIVE); + pad->preroll_lock = g_mutex_new (); + pad->preroll_cond = g_cond_new (); + pad->stream_rec_lock = g_new (GStaticRecMutex, 1); g_static_rec_mutex_init (pad->stream_rec_lock); @@ -2539,11 +2544,6 @@ gst_real_pad_dispose (GObject * object) g_assert (rpad->ghostpads == NULL); } - if (rpad->stream_rec_lock) { - g_static_rec_mutex_free (rpad->stream_rec_lock); - rpad->stream_rec_lock = NULL; - } - /* clear the caps */ gst_caps_replace (&GST_RPAD_CAPS (pad), NULL); gst_caps_replace (&GST_RPAD_APPFILTER (pad), NULL); @@ -2558,6 +2558,31 @@ gst_real_pad_dispose (GObject * object) G_OBJECT_CLASS (real_pad_parent_class)->dispose (object); } +static void +gst_real_pad_finalize (GObject * object) +{ + GstRealPad *rpad; + + rpad = GST_REAL_PAD (object); + + if (rpad->stream_rec_lock) { + g_static_rec_mutex_free (rpad->stream_rec_lock); + rpad->stream_rec_lock = NULL; + } + if (rpad->preroll_lock) { + g_mutex_free (rpad->preroll_lock); + g_cond_free (rpad->preroll_cond); + rpad->preroll_lock = NULL; + rpad->preroll_cond = NULL; + } + if (rpad->block_cond) { + g_cond_free (rpad->block_cond); + rpad->block_cond = NULL; + } + + G_OBJECT_CLASS (real_pad_parent_class)->finalize (object); +} + #ifndef GST_DISABLE_LOADSAVE /* FIXME: why isn't this on a GstElement ? */ diff --git a/gst/gstpad.h b/gst/gstpad.h index 9f81e80241..efd3549650 100644 --- a/gst/gstpad.h +++ b/gst/gstpad.h @@ -207,6 +207,9 @@ struct _GstRealPad { /* streaming rec_lock */ GStaticRecMutex *stream_rec_lock; GstTask *task; + /*< public >*/ /* with PREROLL_LOCK */ + GMutex *preroll_lock; + GCond *preroll_cond; /*< public >*/ /* with LOCK */ /* block cond, mutex is from the object */ @@ -335,6 +338,17 @@ struct _GstGhostPadClass { #define GST_STREAM_TRYLOCK(pad) (g_static_rec_mutex_trylock(GST_STREAM_GET_LOCK(pad))) #define GST_STREAM_UNLOCK(pad) (g_static_rec_mutex_unlock(GST_STREAM_GET_LOCK(pad))) +#define GST_PREROLL_GET_LOCK(pad) (GST_PAD_REALIZE(pad)->preroll_lock) +#define GST_PREROLL_LOCK(pad) (g_mutex_lock(GST_PREROLL_GET_LOCK(pad))) +#define GST_PREROLL_TRYLOCK(pad) (g_mutex_trylock(GST_PREROLL_GET_LOCK(pad))) +#define GST_PREROLL_UNLOCK(pad) (g_mutex_unlock(GST_PREROLL_GET_LOCK(pad))) +#define GST_PREROLL_GET_COND(pad) (GST_PAD_REALIZE(pad)->preroll_cond) +#define GST_PREROLL_WAIT(pad) g_cond_wait (GST_PREROLL_GET_COND (pad), GST_PREROLL_GET_LOCK (pad)) +#define GST_PREROLL_TIMED_WAIT(pad, timeval) g_cond_timed_wait (GST_PREROLL_GET_COND (pad), GST_PREROLL_GET_LOCK (pad),\ + timeval) +#define GST_PREROLL_SIGNAL(pad) g_cond_signal (GST_PREROLL_GET_COND (pad)); +#define GST_PREROLL_BROADCAST(pad) g_cond_broadcast (GST_PREROLL_GET_COND (pad)); + #define GST_PAD_BLOCK_GET_COND(pad) (GST_PAD_REALIZE(pad)->block_cond) #define GST_PAD_BLOCK_WAIT(pad) (g_cond_wait(GST_PAD_BLOCK_GET_COND (pad), GST_GET_LOCK (pad))) #define GST_PAD_BLOCK_SIGNAL(pad) (g_cond_signal(GST_PAD_BLOCK_GET_COND (pad))) diff --git a/plugins/elements/gstfakesink.c b/plugins/elements/gstfakesink.c index 8d070a8d9a..85005c7aa0 100644 --- a/plugins/elements/gstfakesink.c +++ b/plugins/elements/gstfakesink.c @@ -198,12 +198,10 @@ gst_fakesink_class_init (GstFakeSinkClass * klass) static void gst_fakesink_init (GstFakeSink * fakesink) { - GstPad *pad; - - pad = + fakesink->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate), "sink"); - gst_element_add_pad (GST_ELEMENT (fakesink), pad); + gst_element_add_pad (GST_ELEMENT (fakesink), fakesink->sinkpad); fakesink->silent = FALSE; fakesink->dump = FALSE; @@ -212,7 +210,7 @@ gst_fakesink_init (GstFakeSink * fakesink) fakesink->state_error = FAKESINK_STATE_ERROR_NONE; fakesink->signal_handoffs = FALSE; fakesink->pad_mode = GST_ACTIVATE_NONE; - GST_RPAD_TASK (pad) = NULL; + GST_RPAD_TASK (fakesink->sinkpad) = NULL; } static void @@ -357,55 +355,66 @@ gst_fakesink_get_property (GObject * object, guint prop_id, GValue * value, } } -static gboolean -gst_fakesink_activate (GstPad * pad, GstActivateMode mode) +/* STREAM_LOCK should be held */ +GstFlowReturn +gst_fakesink_finish_preroll (GstFakeSink * fakesink, GstPad * pad) { - gboolean result = FALSE; - GstFakeSink *fakesink; + /* lock order is important */ + GST_STATE_LOCK (fakesink); + GST_PREROLL_LOCK (pad); + if (!fakesink->need_preroll) + goto no_preroll; - fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad)); + if (!fakesink->silent) { + g_free (fakesink->last_message); - switch (mode) { - case GST_ACTIVATE_PUSH: - g_return_val_if_fail (fakesink->has_chain, FALSE); - result = TRUE; - break; - case GST_ACTIVATE_PULL: - /* if we have a scheduler we can start the task */ - g_return_val_if_fail (fakesink->has_loop, FALSE); - if (GST_ELEMENT_SCHEDULER (fakesink)) { - GST_STREAM_LOCK (pad); - GST_RPAD_TASK (pad) = - gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (fakesink), - (GstTaskFunction) gst_fakesink_loop, pad); + fakesink->last_message = + g_strdup_printf ("preroll ******* (%s:%s)", GST_DEBUG_PAD_NAME (pad)); - gst_task_start (GST_RPAD_TASK (pad)); - GST_STREAM_UNLOCK (pad); - result = TRUE; - } - break; - case GST_ACTIVATE_NONE: - /* step 1, unblock clock sync (if any) */ - - /* step 2, make sure streaming finishes */ - GST_STREAM_LOCK (pad); - /* step 3, stop the task */ - if (GST_RPAD_TASK (pad)) { - gst_task_stop (GST_RPAD_TASK (pad)); - gst_object_unref (GST_OBJECT (GST_RPAD_TASK (pad))); - GST_RPAD_TASK (pad) = NULL; - } - GST_STREAM_UNLOCK (pad); - - result = TRUE; - break; + g_object_notify (G_OBJECT (fakesink), "last_message"); } - fakesink->pad_mode = mode; + fakesink->need_preroll = FALSE; + fakesink->have_preroll = TRUE; + gst_element_commit_state (GST_ELEMENT (fakesink)); + GST_STATE_UNLOCK (fakesink); - return result; + { + gboolean usable; + + GST_DEBUG ("element %s waiting to finish preroll", + GST_ELEMENT_NAME (fakesink)); + GST_PREROLL_WAIT (pad); + GST_DEBUG ("done preroll"); + + GST_LOCK (pad); + usable = !GST_RPAD_IS_FLUSHING (pad) && GST_RPAD_IS_ACTIVE (pad); + GST_UNLOCK (pad); + if (!usable) + goto unusable; + + GST_DEBUG ("done preroll"); + } + fakesink->have_preroll = FALSE; + + GST_PREROLL_UNLOCK (pad); + return GST_FLOW_OK; + +no_preroll: + { + GST_PREROLL_UNLOCK (pad); + GST_STATE_UNLOCK (fakesink); + return GST_FLOW_OK; + } +unusable: + { + GST_DEBUG ("pad is flushing"); + GST_PREROLL_UNLOCK (pad); + return GST_FLOW_UNEXPECTED; + } } + static gboolean gst_fakesink_event (GstPad * pad, GstEvent * event) { @@ -429,9 +438,15 @@ gst_fakesink_event (GstPad * pad, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: { - gst_element_finish_preroll (GST_ELEMENT (fakesink), pad); - gst_element_post_message (GST_ELEMENT (fakesink), - gst_message_new_eos (GST_OBJECT (fakesink))); + GstFlowReturn ret; + + ret = gst_fakesink_finish_preroll (fakesink, pad); + if (ret == GST_FLOW_OK) { + fakesink->eos = TRUE; + /* ok, we can post the message */ + gst_element_post_message (GST_ELEMENT (fakesink), + gst_message_new_eos (GST_OBJECT (fakesink))); + } break; } case GST_EVENT_DISCONTINUOUS: @@ -455,7 +470,7 @@ gst_fakesink_chain_unlocked (GstPad * pad, GstBuffer * buf) fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad)); - result = gst_element_finish_preroll (GST_ELEMENT (fakesink), pad); + result = gst_fakesink_finish_preroll (fakesink, pad); if (result != GST_FLOW_OK) goto exit; @@ -539,13 +554,67 @@ paused: goto exit; } +static gboolean +gst_fakesink_activate (GstPad * pad, GstActivateMode mode) +{ + gboolean result = FALSE; + GstFakeSink *fakesink; + + fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad)); + + switch (mode) { + case GST_ACTIVATE_PUSH: + g_return_val_if_fail (fakesink->has_chain, FALSE); + result = TRUE; + break; + case GST_ACTIVATE_PULL: + /* if we have a scheduler we can start the task */ + g_return_val_if_fail (fakesink->has_loop, FALSE); + if (GST_ELEMENT_SCHEDULER (fakesink)) { + GST_STREAM_LOCK (pad); + GST_RPAD_TASK (pad) = + gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (fakesink), + (GstTaskFunction) gst_fakesink_loop, pad); + + gst_task_start (GST_RPAD_TASK (pad)); + GST_STREAM_UNLOCK (pad); + result = TRUE; + } + break; + case GST_ACTIVATE_NONE: + /* step 1, unblock clock sync (if any) or any other blocking thing */ + + /* unlock preroll */ + GST_PREROLL_LOCK (pad); + GST_PREROLL_SIGNAL (pad); + GST_PREROLL_UNLOCK (pad); + + /* step 2, make sure streaming finishes */ + GST_STREAM_LOCK (pad); + /* step 3, stop the task */ + if (GST_RPAD_TASK (pad)) { + gst_task_stop (GST_RPAD_TASK (pad)); + gst_object_unref (GST_OBJECT (GST_RPAD_TASK (pad))); + GST_RPAD_TASK (pad) = NULL; + } + GST_STREAM_UNLOCK (pad); + + result = TRUE; + break; + } + fakesink->pad_mode = mode; + + return result; +} + static GstElementStateReturn gst_fakesink_change_state (GstElement * element) { GstElementStateReturn ret = GST_STATE_SUCCESS; GstFakeSink *fakesink = GST_FAKESINK (element); + GstElementState transition = GST_STATE_TRANSITION (element); - switch (GST_STATE_TRANSITION (element)) { + switch (transition) { case GST_STATE_NULL_TO_READY: if (fakesink->state_error == FAKESINK_STATE_ERROR_NULL_READY) goto error; @@ -553,17 +622,42 @@ gst_fakesink_change_state (GstElement * element) case GST_STATE_READY_TO_PAUSED: if (fakesink->state_error == FAKESINK_STATE_ERROR_READY_PAUSED) goto error; - /* need to complete preroll before this state change completes */ + /* need to complete preroll before this state change completes, there + * is no data flow in READY so we cqn safely assume we need to preroll. */ fakesink->offset = 0; + GST_PREROLL_LOCK (fakesink->sinkpad); + fakesink->need_preroll = TRUE; + fakesink->have_preroll = FALSE; + GST_PREROLL_UNLOCK (fakesink->sinkpad); ret = GST_STATE_ASYNC; break; case GST_STATE_PAUSED_TO_PLAYING: if (fakesink->state_error == FAKESINK_STATE_ERROR_PAUSED_PLAYING) goto error; + /* the state change completes when we are blocking on a preroll + * sample */ + GST_PREROLL_LOCK (fakesink->sinkpad); + if (!fakesink->have_preroll) { + fakesink->need_preroll = TRUE; + ret = GST_STATE_ASYNC; + } else { + /* now let it play */ + GST_PREROLL_SIGNAL (fakesink->sinkpad); + } + GST_PREROLL_UNLOCK (fakesink->sinkpad); break; case GST_STATE_PLAYING_TO_PAUSED: if (fakesink->state_error == FAKESINK_STATE_ERROR_PLAYING_PAUSED) goto error; + + GST_PREROLL_LOCK (fakesink->sinkpad); + fakesink->need_preroll = TRUE; + /* if we don't have a preroll buffer and we have not received EOS, + * we need to wait for a preroll */ + if (!fakesink->have_preroll && !fakesink->eos) { + ret = GST_STATE_ASYNC; + } + GST_PREROLL_UNLOCK (fakesink->sinkpad); break; case GST_STATE_PAUSED_TO_READY: if (fakesink->state_error == FAKESINK_STATE_ERROR_PAUSED_READY) @@ -575,10 +669,11 @@ gst_fakesink_change_state (GstElement * element) g_free (fakesink->last_message); fakesink->last_message = NULL; break; + default: + break; } - if (GST_ELEMENT_CLASS (parent_class)->change_state) - GST_ELEMENT_CLASS (parent_class)->change_state (element); + GST_ELEMENT_CLASS (parent_class)->change_state (element); return ret; diff --git a/plugins/elements/gstfakesink.h b/plugins/elements/gstfakesink.h index 6dd2834fbc..73b1f70d9c 100644 --- a/plugins/elements/gstfakesink.h +++ b/plugins/elements/gstfakesink.h @@ -68,6 +68,9 @@ struct _GstFakeSink { GstFakeSinkStateError state_error; GstActivateMode pad_mode; guint64 offset; + gboolean eos; + gboolean need_preroll; + gboolean have_preroll; gchar *last_message; }; diff --git a/tools/gst-launch.c b/tools/gst-launch.c index f3b924d9b8..1dda0b563d 100644 --- a/tools/gst-launch.c +++ b/tools/gst-launch.c @@ -567,8 +567,15 @@ main (int argc, char *argv[]) g_print (_("Execution ended after %" G_GUINT64_FORMAT " ns.\n"), diff); } - + fprintf (stderr, _("PAUSE pipeline ...\n")); + gst_element_set_state (pipeline, GST_STATE_PAUSED); + gst_element_get_state (pipeline, &state, &pending, NULL); + fprintf (stderr, _("READY pipeline ...\n")); + gst_element_set_state (pipeline, GST_STATE_READY); + gst_element_get_state (pipeline, &state, &pending, NULL); + fprintf (stderr, _("NULL pipeline ...\n")); gst_element_set_state (pipeline, GST_STATE_NULL); + gst_element_get_state (pipeline, &state, &pending, NULL); } end: