diff --git a/ChangeLog b/ChangeLog index 1be0c6ab20..9f69bed428 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2005-02-23 Wim Taymans + + * docs/design/part-states.txt: + * gst/base/gstbasesink.c: (gst_basesink_get_template), + (gst_basesink_base_init), (gst_basesink_class_init), + (gst_basesink_init), (gst_base_sink_get_template), + (gst_base_sink_get_caps), (gst_base_sink_set_caps), + (gst_base_sink_alloc_buffer), (gst_basesink_finish_preroll), + (gst_basesink_event), (gst_basesink_get_times), + (gst_basesink_do_sync), (gst_basesink_chain_unlocked), + (gst_basesink_activate), (gst_basesink_change_state): + * gst/base/gstbasesink.h: + * gst/elements/gstfakesink.c: (gst_fakesink_class_init), + (gst_fakesink_get_times), (gst_fakesink_event), + (gst_fakesink_preroll), (gst_fakesink_render): + Add clock and sync handling to the base sink class. + Make fakesink extend the base sink class. + Fix some typos. + 2005-02-23 Andy Wingo * check/Makefile.am (TESTS): Add pipelines/simple_launch_lines. diff --git a/docs/design/part-states.txt b/docs/design/part-states.txt index 55a6bfa343..23868cecbe 100644 --- a/docs/design/part-states.txt +++ b/docs/design/part-states.txt @@ -1,7 +1,7 @@ States ====== -Both elements an pads can be in different states. The states of the pads are +Both elements and pads can be in different states. The states of the pads are linked to the state of the element so the design of the states is mainly focused around the element states. diff --git a/gst/base/gstbasesink.c b/gst/base/gstbasesink.c index d7b228b7a2..e780b01230 100644 --- a/gst/base/gstbasesink.c +++ b/gst/base/gstbasesink.c @@ -71,6 +71,8 @@ static GstCaps *gst_base_sink_get_caps (GstBaseSink * sink); static gboolean gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps); static GstBuffer *gst_base_sink_alloc_buffer (GstBaseSink * sink, guint64 offset, guint size, GstCaps * caps); +static void gst_basesink_get_times (GstBaseSink * basesink, GstBuffer * buffer, + GstClockTime * start, GstClockTime * end); static GstElementStateReturn gst_basesink_change_state (GstElement * element); @@ -138,6 +140,7 @@ gst_basesink_class_init (GstBaseSinkClass * klass) klass->set_caps = GST_DEBUG_FUNCPTR (gst_base_sink_set_caps); klass->get_template = GST_DEBUG_FUNCPTR (gst_base_sink_get_template); klass->alloc_buffer = GST_DEBUG_FUNCPTR (gst_base_sink_alloc_buffer); + klass->get_times = GST_DEBUG_FUNCPTR (gst_basesink_get_times); } static void @@ -331,8 +334,6 @@ gst_basesink_event (GstPad * pad, GstEvent * event) basesink = GST_BASESINK (GST_OBJECT_PARENT (pad)); - GST_STREAM_LOCK (pad); - bclass = GST_BASESINK_GET_CLASS (basesink); if (bclass->event) @@ -343,28 +344,126 @@ gst_basesink_event (GstPad * pad, GstEvent * event) { GstFlowReturn ret; + GST_STREAM_LOCK (pad); ret = gst_basesink_finish_preroll (basesink, pad, NULL); if (ret == GST_FLOW_OK) { - basesink->eos = TRUE; - /* ok, we can post the message */ - gst_element_post_message (GST_ELEMENT (basesink), - gst_message_new_eos (GST_OBJECT (basesink))); + gboolean need_eos; + + GST_LOCK (basesink); + need_eos = basesink->eos = TRUE; + if (basesink->clock) { + /* wait for last buffer to finish if we have a valid end time */ + if (GST_CLOCK_TIME_IS_VALID (basesink->end_time)) { + basesink->clock_id = gst_clock_new_single_shot_id (basesink->clock, + basesink->end_time + GST_ELEMENT (basesink)->base_time); + GST_UNLOCK (basesink); + + gst_clock_id_wait (basesink->clock_id, NULL); + + GST_LOCK (basesink); + if (basesink->clock_id) { + gst_clock_id_unref (basesink->clock_id); + basesink->clock_id = NULL; + } + basesink->end_time = GST_CLOCK_TIME_NONE; + need_eos = basesink->eos; + } + } + GST_UNLOCK (basesink); + + /* if we are still EOS, we can post the EOS message */ + if (need_eos) { + /* ok, now we can post the message */ + gst_element_post_message (GST_ELEMENT (basesink), + gst_message_new_eos (GST_OBJECT (basesink))); + } } + GST_STREAM_UNLOCK (pad); break; } case GST_EVENT_DISCONTINUOUS: - if (basesink->sync && basesink->clock) { + GST_STREAM_LOCK (pad); + if (basesink->clock) { //gint64 value = GST_EVENT_DISCONT_OFFSET (event, 0).value; } + GST_STREAM_UNLOCK (pad); + case GST_EVENT_FLUSH: + /* make sure we are not blocked on the clock also clear any pending + * eos state. */ + GST_LOCK (basesink); + basesink->eos = FALSE; + if (basesink->clock_id) { + gst_clock_id_unschedule (basesink->clock_id); + } + GST_UNLOCK (basesink); + + /* unlock from a possible state change/preroll */ + GST_PREROLL_LOCK (pad); + GST_PREROLL_SIGNAL (pad); + GST_PREROLL_UNLOCK (pad); + /* now we are completely unblocked and the _chain method + * will return */ + break; default: result = gst_pad_event_default (pad, event); break; } - GST_STREAM_UNLOCK (pad); return result; } +static void +gst_basesink_get_times (GstBaseSink * basesink, GstBuffer * buffer, + GstClockTime * start, GstClockTime * end) +{ + GstClockTime timestamp, duration; + + timestamp = GST_BUFFER_TIMESTAMP (buffer); + if (GST_CLOCK_TIME_IS_VALID (timestamp)) { + duration = GST_BUFFER_DURATION (buffer); + if (GST_CLOCK_TIME_IS_VALID (duration)) { + *end = timestamp + duration; + } + *start = timestamp; + } +} + +static void +gst_basesink_do_sync (GstBaseSink * basesink, GstBuffer * buffer) +{ + if (basesink->clock) { + GstClockReturn ret; + GstClockTime start, end; + GstBaseSinkClass *bclass; + + bclass = GST_BASESINK_GET_CLASS (basesink); + start = end = -1; + if (bclass->get_times) + bclass->get_times (basesink, buffer, &start, &end); + + if (GST_CLOCK_TIME_IS_VALID (start)) { + /* save clock id so that we can unlock it if needed */ + GST_LOCK (basesink); + basesink->clock_id = gst_clock_new_single_shot_id (basesink->clock, + start + GST_ELEMENT (basesink)->base_time); + basesink->end_time = end; + GST_UNLOCK (basesink); + + ret = gst_clock_id_wait (basesink->clock_id, NULL); + + GST_LOCK (basesink); + if (basesink->clock_id) { + gst_clock_id_unref (basesink->clock_id); + basesink->clock_id = NULL; + } + basesink->end_time = GST_CLOCK_TIME_NONE; + GST_UNLOCK (basesink); + + GST_LOG_OBJECT (basesink, "clock entry done: %d", ret); + } + } +} + static GstFlowReturn gst_basesink_chain_unlocked (GstPad * pad, GstBuffer * buf) { @@ -378,11 +477,7 @@ gst_basesink_chain_unlocked (GstPad * pad, GstBuffer * buf) if (result != GST_FLOW_OK) goto exit; - /* doClock - if (basesink->sync && basesink->clock) { - gst_element_wait (GST_ELEMENT (basesink), GST_BUFFER_TIMESTAMP (buf)); - } - */ + gst_basesink_do_sync (basesink, buf); bclass = GST_BASESINK_GET_CLASS (basesink); if (bclass->render) @@ -470,6 +565,11 @@ gst_basesink_activate (GstPad * pad, GstActivateMode mode) break; case GST_ACTIVATE_NONE: /* step 1, unblock clock sync (if any) or any other blocking thing */ + GST_LOCK (basesink); + if (basesink->clock_id) { + gst_clock_id_unschedule (basesink->clock_id); + } + GST_UNLOCK (basesink); /* unlock preroll */ GST_PREROLL_LOCK (pad); @@ -528,15 +628,27 @@ gst_basesink_change_state (GstElement * element) GST_PREROLL_UNLOCK (basesink->sinkpad); break; case GST_STATE_PLAYING_TO_PAUSED: + { + gboolean eos; + + /* unlock clock wait if any */ + GST_LOCK (basesink); + if (basesink->clock_id) { + gst_clock_id_unschedule (basesink->clock_id); + } + eos = basesink->eos; + GST_UNLOCK (basesink); + GST_PREROLL_LOCK (basesink->sinkpad); basesink->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 (!basesink->have_preroll && !basesink->eos) { + if (!basesink->have_preroll && !eos) { ret = GST_STATE_ASYNC; } GST_PREROLL_UNLOCK (basesink->sinkpad); break; + } case GST_STATE_PAUSED_TO_READY: break; case GST_STATE_READY_TO_NULL: diff --git a/gst/base/gstbasesink.h b/gst/base/gstbasesink.h index 6717a4fde5..5fe29335d3 100644 --- a/gst/base/gstbasesink.h +++ b/gst/base/gstbasesink.h @@ -35,6 +35,9 @@ G_BEGIN_DECLS #define GST_IS_BASESINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASESINK)) #define GST_IS_BASESINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASESINK)) +#define GST_BASESINK_CLOCK(obj) (GST_BASESINK (obj)->clock) +#define GST_BASESINK_PAD(obj) (GST_BASESINK (obj)->sinkpad) + typedef struct _GstBaseSink GstBaseSink; typedef struct _GstBaseSinkClass GstBaseSinkClass; @@ -48,8 +51,9 @@ struct _GstBaseSink { gboolean has_loop; gboolean has_chain; - gboolean sync; GstClock *clock; + GstClockID clock_id; + GstClockTime end_time; gboolean eos; gboolean need_preroll; @@ -67,6 +71,9 @@ struct _GstBaseSinkClass { GstBuffer* (*alloc_buffer) (GstBaseSink *sink, guint64 offset, guint size, GstCaps *caps); + void (*get_times) (GstBaseSink *sink, GstBuffer *buffer, + GstClockTime *start, GstClockTime *end); + void (*event) (GstBaseSink *sink, GstEvent *event); GstFlowReturn (*preroll) (GstBaseSink *sink, GstBuffer *buffer); GstFlowReturn (*render) (GstBaseSink *sink, GstBuffer *buffer); diff --git a/gst/elements/gstfakesink.c b/gst/elements/gstfakesink.c index 4b0fcefb28..b8d483c6ff 100644 --- a/gst/elements/gstfakesink.c +++ b/gst/elements/gstfakesink.c @@ -117,6 +117,8 @@ static GstFlowReturn gst_fakesink_preroll (GstBaseSink * bsink, static GstFlowReturn gst_fakesink_render (GstBaseSink * bsink, GstBuffer * buffer); static void gst_fakesink_event (GstBaseSink * bsink, GstEvent * event); +static void gst_fakesink_get_times (GstBaseSink * bsink, GstBuffer * buffer, + GstClockTime * start, GstClockTime * end); static guint gst_fakesink_signals[LAST_SIGNAL] = { 0 }; @@ -179,6 +181,7 @@ gst_fakesink_class_init (GstFakeSinkClass * klass) gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_fakesink_event); gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_fakesink_preroll); gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_fakesink_render); + gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_fakesink_get_times); } static void @@ -255,6 +258,17 @@ gst_fakesink_get_property (GObject * object, guint prop_id, GValue * value, } } +static void +gst_fakesink_get_times (GstBaseSink * bsink, GstBuffer * buffer, + GstClockTime * start, GstClockTime * end) +{ + GstFakeSink *sink = GST_FAKESINK (bsink); + + if (sink->sync) { + GST_BASESINK_CLASS (parent_class)->get_times (bsink, buffer, start, end); + } +} + static void gst_fakesink_event (GstBaseSink * bsink, GstEvent * event) { diff --git a/libs/gst/base/gstbasesink.c b/libs/gst/base/gstbasesink.c index d7b228b7a2..e780b01230 100644 --- a/libs/gst/base/gstbasesink.c +++ b/libs/gst/base/gstbasesink.c @@ -71,6 +71,8 @@ static GstCaps *gst_base_sink_get_caps (GstBaseSink * sink); static gboolean gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps); static GstBuffer *gst_base_sink_alloc_buffer (GstBaseSink * sink, guint64 offset, guint size, GstCaps * caps); +static void gst_basesink_get_times (GstBaseSink * basesink, GstBuffer * buffer, + GstClockTime * start, GstClockTime * end); static GstElementStateReturn gst_basesink_change_state (GstElement * element); @@ -138,6 +140,7 @@ gst_basesink_class_init (GstBaseSinkClass * klass) klass->set_caps = GST_DEBUG_FUNCPTR (gst_base_sink_set_caps); klass->get_template = GST_DEBUG_FUNCPTR (gst_base_sink_get_template); klass->alloc_buffer = GST_DEBUG_FUNCPTR (gst_base_sink_alloc_buffer); + klass->get_times = GST_DEBUG_FUNCPTR (gst_basesink_get_times); } static void @@ -331,8 +334,6 @@ gst_basesink_event (GstPad * pad, GstEvent * event) basesink = GST_BASESINK (GST_OBJECT_PARENT (pad)); - GST_STREAM_LOCK (pad); - bclass = GST_BASESINK_GET_CLASS (basesink); if (bclass->event) @@ -343,28 +344,126 @@ gst_basesink_event (GstPad * pad, GstEvent * event) { GstFlowReturn ret; + GST_STREAM_LOCK (pad); ret = gst_basesink_finish_preroll (basesink, pad, NULL); if (ret == GST_FLOW_OK) { - basesink->eos = TRUE; - /* ok, we can post the message */ - gst_element_post_message (GST_ELEMENT (basesink), - gst_message_new_eos (GST_OBJECT (basesink))); + gboolean need_eos; + + GST_LOCK (basesink); + need_eos = basesink->eos = TRUE; + if (basesink->clock) { + /* wait for last buffer to finish if we have a valid end time */ + if (GST_CLOCK_TIME_IS_VALID (basesink->end_time)) { + basesink->clock_id = gst_clock_new_single_shot_id (basesink->clock, + basesink->end_time + GST_ELEMENT (basesink)->base_time); + GST_UNLOCK (basesink); + + gst_clock_id_wait (basesink->clock_id, NULL); + + GST_LOCK (basesink); + if (basesink->clock_id) { + gst_clock_id_unref (basesink->clock_id); + basesink->clock_id = NULL; + } + basesink->end_time = GST_CLOCK_TIME_NONE; + need_eos = basesink->eos; + } + } + GST_UNLOCK (basesink); + + /* if we are still EOS, we can post the EOS message */ + if (need_eos) { + /* ok, now we can post the message */ + gst_element_post_message (GST_ELEMENT (basesink), + gst_message_new_eos (GST_OBJECT (basesink))); + } } + GST_STREAM_UNLOCK (pad); break; } case GST_EVENT_DISCONTINUOUS: - if (basesink->sync && basesink->clock) { + GST_STREAM_LOCK (pad); + if (basesink->clock) { //gint64 value = GST_EVENT_DISCONT_OFFSET (event, 0).value; } + GST_STREAM_UNLOCK (pad); + case GST_EVENT_FLUSH: + /* make sure we are not blocked on the clock also clear any pending + * eos state. */ + GST_LOCK (basesink); + basesink->eos = FALSE; + if (basesink->clock_id) { + gst_clock_id_unschedule (basesink->clock_id); + } + GST_UNLOCK (basesink); + + /* unlock from a possible state change/preroll */ + GST_PREROLL_LOCK (pad); + GST_PREROLL_SIGNAL (pad); + GST_PREROLL_UNLOCK (pad); + /* now we are completely unblocked and the _chain method + * will return */ + break; default: result = gst_pad_event_default (pad, event); break; } - GST_STREAM_UNLOCK (pad); return result; } +static void +gst_basesink_get_times (GstBaseSink * basesink, GstBuffer * buffer, + GstClockTime * start, GstClockTime * end) +{ + GstClockTime timestamp, duration; + + timestamp = GST_BUFFER_TIMESTAMP (buffer); + if (GST_CLOCK_TIME_IS_VALID (timestamp)) { + duration = GST_BUFFER_DURATION (buffer); + if (GST_CLOCK_TIME_IS_VALID (duration)) { + *end = timestamp + duration; + } + *start = timestamp; + } +} + +static void +gst_basesink_do_sync (GstBaseSink * basesink, GstBuffer * buffer) +{ + if (basesink->clock) { + GstClockReturn ret; + GstClockTime start, end; + GstBaseSinkClass *bclass; + + bclass = GST_BASESINK_GET_CLASS (basesink); + start = end = -1; + if (bclass->get_times) + bclass->get_times (basesink, buffer, &start, &end); + + if (GST_CLOCK_TIME_IS_VALID (start)) { + /* save clock id so that we can unlock it if needed */ + GST_LOCK (basesink); + basesink->clock_id = gst_clock_new_single_shot_id (basesink->clock, + start + GST_ELEMENT (basesink)->base_time); + basesink->end_time = end; + GST_UNLOCK (basesink); + + ret = gst_clock_id_wait (basesink->clock_id, NULL); + + GST_LOCK (basesink); + if (basesink->clock_id) { + gst_clock_id_unref (basesink->clock_id); + basesink->clock_id = NULL; + } + basesink->end_time = GST_CLOCK_TIME_NONE; + GST_UNLOCK (basesink); + + GST_LOG_OBJECT (basesink, "clock entry done: %d", ret); + } + } +} + static GstFlowReturn gst_basesink_chain_unlocked (GstPad * pad, GstBuffer * buf) { @@ -378,11 +477,7 @@ gst_basesink_chain_unlocked (GstPad * pad, GstBuffer * buf) if (result != GST_FLOW_OK) goto exit; - /* doClock - if (basesink->sync && basesink->clock) { - gst_element_wait (GST_ELEMENT (basesink), GST_BUFFER_TIMESTAMP (buf)); - } - */ + gst_basesink_do_sync (basesink, buf); bclass = GST_BASESINK_GET_CLASS (basesink); if (bclass->render) @@ -470,6 +565,11 @@ gst_basesink_activate (GstPad * pad, GstActivateMode mode) break; case GST_ACTIVATE_NONE: /* step 1, unblock clock sync (if any) or any other blocking thing */ + GST_LOCK (basesink); + if (basesink->clock_id) { + gst_clock_id_unschedule (basesink->clock_id); + } + GST_UNLOCK (basesink); /* unlock preroll */ GST_PREROLL_LOCK (pad); @@ -528,15 +628,27 @@ gst_basesink_change_state (GstElement * element) GST_PREROLL_UNLOCK (basesink->sinkpad); break; case GST_STATE_PLAYING_TO_PAUSED: + { + gboolean eos; + + /* unlock clock wait if any */ + GST_LOCK (basesink); + if (basesink->clock_id) { + gst_clock_id_unschedule (basesink->clock_id); + } + eos = basesink->eos; + GST_UNLOCK (basesink); + GST_PREROLL_LOCK (basesink->sinkpad); basesink->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 (!basesink->have_preroll && !basesink->eos) { + if (!basesink->have_preroll && !eos) { ret = GST_STATE_ASYNC; } GST_PREROLL_UNLOCK (basesink->sinkpad); break; + } case GST_STATE_PAUSED_TO_READY: break; case GST_STATE_READY_TO_NULL: diff --git a/libs/gst/base/gstbasesink.h b/libs/gst/base/gstbasesink.h index 6717a4fde5..5fe29335d3 100644 --- a/libs/gst/base/gstbasesink.h +++ b/libs/gst/base/gstbasesink.h @@ -35,6 +35,9 @@ G_BEGIN_DECLS #define GST_IS_BASESINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASESINK)) #define GST_IS_BASESINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASESINK)) +#define GST_BASESINK_CLOCK(obj) (GST_BASESINK (obj)->clock) +#define GST_BASESINK_PAD(obj) (GST_BASESINK (obj)->sinkpad) + typedef struct _GstBaseSink GstBaseSink; typedef struct _GstBaseSinkClass GstBaseSinkClass; @@ -48,8 +51,9 @@ struct _GstBaseSink { gboolean has_loop; gboolean has_chain; - gboolean sync; GstClock *clock; + GstClockID clock_id; + GstClockTime end_time; gboolean eos; gboolean need_preroll; @@ -67,6 +71,9 @@ struct _GstBaseSinkClass { GstBuffer* (*alloc_buffer) (GstBaseSink *sink, guint64 offset, guint size, GstCaps *caps); + void (*get_times) (GstBaseSink *sink, GstBuffer *buffer, + GstClockTime *start, GstClockTime *end); + void (*event) (GstBaseSink *sink, GstEvent *event); GstFlowReturn (*preroll) (GstBaseSink *sink, GstBuffer *buffer); GstFlowReturn (*render) (GstBaseSink *sink, GstBuffer *buffer); diff --git a/plugins/elements/gstfakesink.c b/plugins/elements/gstfakesink.c index 4b0fcefb28..b8d483c6ff 100644 --- a/plugins/elements/gstfakesink.c +++ b/plugins/elements/gstfakesink.c @@ -117,6 +117,8 @@ static GstFlowReturn gst_fakesink_preroll (GstBaseSink * bsink, static GstFlowReturn gst_fakesink_render (GstBaseSink * bsink, GstBuffer * buffer); static void gst_fakesink_event (GstBaseSink * bsink, GstEvent * event); +static void gst_fakesink_get_times (GstBaseSink * bsink, GstBuffer * buffer, + GstClockTime * start, GstClockTime * end); static guint gst_fakesink_signals[LAST_SIGNAL] = { 0 }; @@ -179,6 +181,7 @@ gst_fakesink_class_init (GstFakeSinkClass * klass) gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_fakesink_event); gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_fakesink_preroll); gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_fakesink_render); + gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_fakesink_get_times); } static void @@ -255,6 +258,17 @@ gst_fakesink_get_property (GObject * object, guint prop_id, GValue * value, } } +static void +gst_fakesink_get_times (GstBaseSink * bsink, GstBuffer * buffer, + GstClockTime * start, GstClockTime * end) +{ + GstFakeSink *sink = GST_FAKESINK (bsink); + + if (sink->sync) { + GST_BASESINK_CLASS (parent_class)->get_times (bsink, buffer, start, end); + } +} + static void gst_fakesink_event (GstBaseSink * bsink, GstEvent * event) {