diff --git a/ChangeLog b/ChangeLog index 684298d8fc..465226d282 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +2005-07-21 Wim Taymans + + * docs/design/part-seeking.txt: + Some small additions. + + * gst/base/gstbasesink.c: (gst_base_sink_handle_object), + (gst_base_sink_get_times), (gst_base_sink_do_sync), + (gst_base_sink_activate_push), (gst_base_sink_activate_pull): + * gst/base/gstbasesink.h: + discont values are gint64, handle the math correctly. + + * gst/base/gstbasesrc.c: (gst_base_src_loop): + Make the basesrc report error if the source pad is not linked. + + * gst/gstqueue.c: (gst_queue_link_src), (gst_queue_chain), + (gst_queue_loop), (gst_queue_handle_src_query), + (gst_queue_src_activate_push): + Make queue collect data even if the srcpad is not linked. + Start pushing out data as soon as it is linked. + + * gst/gstutils.c: (gst_element_unlink), (gst_flow_get_name): + * gst/gstutils.h: + Added gst_flow_get_name() to ease error reporting. + 2005-07-20 Wim Taymans * gst/gstmessage.c: (gst_message_new_segment_start), diff --git a/docs/design/part-seeking.txt b/docs/design/part-seeking.txt index b2c81c4aa3..c8c836752f 100644 --- a/docs/design/part-seeking.txt +++ b/docs/design/part-seeking.txt @@ -2,7 +2,7 @@ Seeking ------- Seeking in GStreamer means configuring the pipeline for playback of the -media between a certain start and stop time. +media between a certain start and stop time, called a segment. Different kinds of seeking exist: @@ -30,6 +30,10 @@ earliest element in the pipeline, typically a demuxer. After receiving the message, the application can reconnect the pipeline or issue other seek events in the pipeline. +The seek can also change the playback speed of the configured segment. +A speed of 1.0 is normal speed, 2.0 is double speed. Negative values +mean backward playback. + Generating seeking events ------------------------- diff --git a/gst/base/gstbasesink.c b/gst/base/gstbasesink.c index 815bc4b544..3d2197df22 100644 --- a/gst/base/gstbasesink.c +++ b/gst/base/gstbasesink.c @@ -472,8 +472,7 @@ gst_base_sink_handle_object (GstBaseSink * basesink, GstPad * pad, /* the discont event is needed to bring the buffer timestamps to the * stream time */ if (!gst_event_discont_get_value (event, GST_FORMAT_TIME, - (gint64 *) & basesink->discont_start, - (gint64 *) & basesink->discont_stop)) { + &basesink->discont_start, &basesink->discont_stop)) { basesink->discont_start = 0; basesink->discont_stop = 0; } @@ -730,15 +729,26 @@ gst_base_sink_get_times (GstBaseSink * basesink, GstBuffer * buffer, timestamp = GST_BUFFER_TIMESTAMP (buffer); if (GST_CLOCK_TIME_IS_VALID (timestamp)) { + GstClockTimeDiff diff; + /* bring timestamp to stream time using last * discont offset. */ - timestamp -= basesink->discont_start; + if ((diff = timestamp - basesink->discont_start) < 0) + goto too_late; + /* get duration to calculate end time */ duration = GST_BUFFER_DURATION (buffer); if (GST_CLOCK_TIME_IS_VALID (duration)) { - *end = timestamp + duration; + *end = diff + duration; } - *start = timestamp; + *start = diff; + } + return; + +too_late: + { + *start = GST_CLOCK_TIME_NONE; + *end = GST_CLOCK_TIME_NONE; } } diff --git a/gst/base/gstbasesink.h b/gst/base/gstbasesink.h index c7fc83e4d6..c9b2ffa872 100644 --- a/gst/base/gstbasesink.h +++ b/gst/base/gstbasesink.h @@ -67,8 +67,8 @@ struct _GstBaseSink { GstClockTime end_time; gboolean have_discont; - GstClockTime discont_start; - GstClockTime discont_stop; + GstClockTimeDiff discont_start; + GstClockTimeDiff discont_stop; gboolean eos; gboolean need_preroll; diff --git a/gst/base/gstbasesrc.c b/gst/base/gstbasesrc.c index cdacbd024d..77b1e2088a 100644 --- a/gst/base/gstbasesrc.c +++ b/gst/base/gstbasesrc.c @@ -702,11 +702,11 @@ pause: { GST_DEBUG_OBJECT (src, "pausing task"); gst_pad_pause_task (pad); - if (GST_FLOW_IS_FATAL (ret)) { + if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) { /* for fatal errors we post an error message */ GST_ELEMENT_ERROR (src, STREAM, STOPPED, - ("streaming stopped, reason %d", ret), - ("streaming stopped, reason %d", ret)); + ("streaming stopped, reason %s", gst_flow_get_name (ret)), + ("streaming stopped, reason %s", gst_flow_get_name (ret))); gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS)); } return; diff --git a/gst/gstqueue.c b/gst/gstqueue.c index 971d4c4518..74bbd7040b 100644 --- a/gst/gstqueue.c +++ b/gst/gstqueue.c @@ -29,6 +29,7 @@ #include "gstevent.h" #include "gstinfo.h" #include "gsterror.h" +#include "gstutils.h" static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -406,10 +407,28 @@ static GstPadLinkReturn gst_queue_link_src (GstPad * pad, GstPad * peer) { GstPadLinkReturn result = GST_PAD_LINK_OK; + GstQueue *queue; - /* FIXME, see if we need to push or get pulled */ - if (GST_PAD_LINKFUNC (peer)) + queue = GST_QUEUE (gst_pad_get_parent (pad)); + + GST_DEBUG ("queue linking source pad"); + + if (GST_PAD_LINKFUNC (peer)) { result = GST_PAD_LINKFUNC (peer) (peer, pad); + } + + if (GST_PAD_LINK_SUCCESSFUL (result)) { + GST_QUEUE_MUTEX_LOCK (queue); + if (queue->srcresult == GST_FLOW_OK) { + gst_pad_start_task (pad, (GstTaskFunction) gst_queue_loop, pad); + GST_DEBUG ("starting task as pad is linked"); + } else { + GST_DEBUG ("not starting task reason %s", + gst_flow_get_name (queue->srcresult)); + } + GST_QUEUE_MUTEX_UNLOCK (queue); + } + gst_object_unref (queue); return result; } @@ -673,9 +692,10 @@ out_unref: out_flushing: { GstFlowReturn ret = queue->srcresult; + const gchar *flowname = gst_flow_get_name (ret); GST_CAT_LOG_OBJECT (queue_dataflow, queue, - "exit because task paused, reason: %d", ret); + "exit because task paused, reason: %s", flowname); GST_QUEUE_MUTEX_UNLOCK (queue); gst_buffer_unref (buffer); @@ -746,13 +766,18 @@ restart: /* can opt to check for srcresult here but the push should * return an error value that is more accurate */ if (result != GST_FLOW_OK) { + const gchar *flowname; + + flowname = gst_flow_get_name (result); + queue->srcresult = result; if (GST_FLOW_IS_FATAL (result)) { GST_ELEMENT_ERROR (queue, STREAM, STOPPED, - ("streaming stopped, reason %d", result), - ("streaming stopped, reason %d", result)); + ("streaming stopped, reason %s", flowname), + ("streaming stopped, reason %s", flowname)); gst_pad_push_event (queue->srcpad, gst_event_new (GST_EVENT_EOS)); } + GST_DEBUG ("pausing queue, reason %s", flowname); gst_pad_pause_task (queue->srcpad); } } else { @@ -760,6 +785,7 @@ restart: /* all incomming data is now unexpected */ queue->srcresult = GST_FLOW_UNEXPECTED; /* and we don't need to process anymore */ + GST_DEBUG ("pausing queue, we're EOS now"); gst_pad_pause_task (queue->srcpad); restart = FALSE; } @@ -780,8 +806,10 @@ restart: out_flushing: { + const gchar *flowname = gst_flow_get_name (queue->srcresult); + GST_CAT_LOG_OBJECT (queue_dataflow, queue, - "exit because task paused, reason: %d", queue->srcresult); + "exit because task paused, reason: %s", flowname); GST_QUEUE_MUTEX_UNLOCK (queue); return; @@ -890,7 +918,13 @@ gst_queue_src_activate_push (GstPad * pad, gboolean active) if (active) { GST_QUEUE_MUTEX_LOCK (queue); queue->srcresult = GST_FLOW_OK; - result = gst_pad_start_task (pad, (GstTaskFunction) gst_queue_loop, pad); + /* we do not start the task yet if the pad is not connected */ + if (gst_pad_is_linked (pad)) + result = gst_pad_start_task (pad, (GstTaskFunction) gst_queue_loop, pad); + else { + GST_DEBUG ("not starting task as pad is not linked"); + result = TRUE; + } GST_QUEUE_MUTEX_UNLOCK (queue); } else { /* step 1, unblock chain and loop functions */ diff --git a/gst/gstutils.c b/gst/gstutils.c index 2ef1b2a755..e8bfc64241 100644 --- a/gst/gstutils.c +++ b/gst/gstutils.c @@ -1671,6 +1671,41 @@ gst_pad_get_parent_element (GstPad * pad) return GST_ELEMENT_CAST (p); } +/** + * gst_flow_get_name: + * @state: a #GstFlowReturn to get the name of. + * + * Gets a string representing the given flow return. + * + * Returns: a string with the name of the flow return. + */ +G_CONST_RETURN gchar * +gst_flow_get_name (GstFlowReturn ret) +{ + switch (ret) { + case GST_FLOW_RESEND: + return "need to resend buffer"; + case GST_FLOW_OK: + return "OK"; + /* expected failures */ + case GST_FLOW_NOT_LINKED: + return "pad not linked"; + case GST_FLOW_WRONG_STATE: + return "pad in wrong state"; + /* error cases */ + case GST_FLOW_UNEXPECTED: + return "unexpected data on pad"; + case GST_FLOW_NOT_NEGOTIATED: + return "pad not negotiated"; + case GST_FLOW_ERROR: + return "fatal error occured"; + case GST_FLOW_NOT_SUPPORTED: + return "unsupported function called"; + default: + return "unknown error"; + } +} + /** * gst_object_default_error: * @object: a #GObject that signalled the error. diff --git a/gst/gstutils.h b/gst/gstutils.h index 048753c75c..e4d0ecabf1 100644 --- a/gst/gstutils.h +++ b/gst/gstutils.h @@ -280,6 +280,10 @@ gboolean gst_pad_proxy_setcaps (GstPad * pad, GstCaps * caps); GstElement* gst_pad_get_parent_element (GstPad *pad); +/* flow */ +G_CONST_RETURN gchar* gst_flow_get_name (GstFlowReturn ret); + + /* util query functions */ gboolean gst_pad_query_position (GstPad *pad, GstFormat *format, gint64 *cur, gint64 *end); diff --git a/libs/gst/base/gstbasesink.c b/libs/gst/base/gstbasesink.c index 815bc4b544..3d2197df22 100644 --- a/libs/gst/base/gstbasesink.c +++ b/libs/gst/base/gstbasesink.c @@ -472,8 +472,7 @@ gst_base_sink_handle_object (GstBaseSink * basesink, GstPad * pad, /* the discont event is needed to bring the buffer timestamps to the * stream time */ if (!gst_event_discont_get_value (event, GST_FORMAT_TIME, - (gint64 *) & basesink->discont_start, - (gint64 *) & basesink->discont_stop)) { + &basesink->discont_start, &basesink->discont_stop)) { basesink->discont_start = 0; basesink->discont_stop = 0; } @@ -730,15 +729,26 @@ gst_base_sink_get_times (GstBaseSink * basesink, GstBuffer * buffer, timestamp = GST_BUFFER_TIMESTAMP (buffer); if (GST_CLOCK_TIME_IS_VALID (timestamp)) { + GstClockTimeDiff diff; + /* bring timestamp to stream time using last * discont offset. */ - timestamp -= basesink->discont_start; + if ((diff = timestamp - basesink->discont_start) < 0) + goto too_late; + /* get duration to calculate end time */ duration = GST_BUFFER_DURATION (buffer); if (GST_CLOCK_TIME_IS_VALID (duration)) { - *end = timestamp + duration; + *end = diff + duration; } - *start = timestamp; + *start = diff; + } + return; + +too_late: + { + *start = GST_CLOCK_TIME_NONE; + *end = GST_CLOCK_TIME_NONE; } } diff --git a/libs/gst/base/gstbasesink.h b/libs/gst/base/gstbasesink.h index c7fc83e4d6..c9b2ffa872 100644 --- a/libs/gst/base/gstbasesink.h +++ b/libs/gst/base/gstbasesink.h @@ -67,8 +67,8 @@ struct _GstBaseSink { GstClockTime end_time; gboolean have_discont; - GstClockTime discont_start; - GstClockTime discont_stop; + GstClockTimeDiff discont_start; + GstClockTimeDiff discont_stop; gboolean eos; gboolean need_preroll; diff --git a/libs/gst/base/gstbasesrc.c b/libs/gst/base/gstbasesrc.c index cdacbd024d..77b1e2088a 100644 --- a/libs/gst/base/gstbasesrc.c +++ b/libs/gst/base/gstbasesrc.c @@ -702,11 +702,11 @@ pause: { GST_DEBUG_OBJECT (src, "pausing task"); gst_pad_pause_task (pad); - if (GST_FLOW_IS_FATAL (ret)) { + if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) { /* for fatal errors we post an error message */ GST_ELEMENT_ERROR (src, STREAM, STOPPED, - ("streaming stopped, reason %d", ret), - ("streaming stopped, reason %d", ret)); + ("streaming stopped, reason %s", gst_flow_get_name (ret)), + ("streaming stopped, reason %s", gst_flow_get_name (ret))); gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS)); } return; diff --git a/plugins/elements/gstqueue.c b/plugins/elements/gstqueue.c index 971d4c4518..74bbd7040b 100644 --- a/plugins/elements/gstqueue.c +++ b/plugins/elements/gstqueue.c @@ -29,6 +29,7 @@ #include "gstevent.h" #include "gstinfo.h" #include "gsterror.h" +#include "gstutils.h" static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -406,10 +407,28 @@ static GstPadLinkReturn gst_queue_link_src (GstPad * pad, GstPad * peer) { GstPadLinkReturn result = GST_PAD_LINK_OK; + GstQueue *queue; - /* FIXME, see if we need to push or get pulled */ - if (GST_PAD_LINKFUNC (peer)) + queue = GST_QUEUE (gst_pad_get_parent (pad)); + + GST_DEBUG ("queue linking source pad"); + + if (GST_PAD_LINKFUNC (peer)) { result = GST_PAD_LINKFUNC (peer) (peer, pad); + } + + if (GST_PAD_LINK_SUCCESSFUL (result)) { + GST_QUEUE_MUTEX_LOCK (queue); + if (queue->srcresult == GST_FLOW_OK) { + gst_pad_start_task (pad, (GstTaskFunction) gst_queue_loop, pad); + GST_DEBUG ("starting task as pad is linked"); + } else { + GST_DEBUG ("not starting task reason %s", + gst_flow_get_name (queue->srcresult)); + } + GST_QUEUE_MUTEX_UNLOCK (queue); + } + gst_object_unref (queue); return result; } @@ -673,9 +692,10 @@ out_unref: out_flushing: { GstFlowReturn ret = queue->srcresult; + const gchar *flowname = gst_flow_get_name (ret); GST_CAT_LOG_OBJECT (queue_dataflow, queue, - "exit because task paused, reason: %d", ret); + "exit because task paused, reason: %s", flowname); GST_QUEUE_MUTEX_UNLOCK (queue); gst_buffer_unref (buffer); @@ -746,13 +766,18 @@ restart: /* can opt to check for srcresult here but the push should * return an error value that is more accurate */ if (result != GST_FLOW_OK) { + const gchar *flowname; + + flowname = gst_flow_get_name (result); + queue->srcresult = result; if (GST_FLOW_IS_FATAL (result)) { GST_ELEMENT_ERROR (queue, STREAM, STOPPED, - ("streaming stopped, reason %d", result), - ("streaming stopped, reason %d", result)); + ("streaming stopped, reason %s", flowname), + ("streaming stopped, reason %s", flowname)); gst_pad_push_event (queue->srcpad, gst_event_new (GST_EVENT_EOS)); } + GST_DEBUG ("pausing queue, reason %s", flowname); gst_pad_pause_task (queue->srcpad); } } else { @@ -760,6 +785,7 @@ restart: /* all incomming data is now unexpected */ queue->srcresult = GST_FLOW_UNEXPECTED; /* and we don't need to process anymore */ + GST_DEBUG ("pausing queue, we're EOS now"); gst_pad_pause_task (queue->srcpad); restart = FALSE; } @@ -780,8 +806,10 @@ restart: out_flushing: { + const gchar *flowname = gst_flow_get_name (queue->srcresult); + GST_CAT_LOG_OBJECT (queue_dataflow, queue, - "exit because task paused, reason: %d", queue->srcresult); + "exit because task paused, reason: %s", flowname); GST_QUEUE_MUTEX_UNLOCK (queue); return; @@ -890,7 +918,13 @@ gst_queue_src_activate_push (GstPad * pad, gboolean active) if (active) { GST_QUEUE_MUTEX_LOCK (queue); queue->srcresult = GST_FLOW_OK; - result = gst_pad_start_task (pad, (GstTaskFunction) gst_queue_loop, pad); + /* we do not start the task yet if the pad is not connected */ + if (gst_pad_is_linked (pad)) + result = gst_pad_start_task (pad, (GstTaskFunction) gst_queue_loop, pad); + else { + GST_DEBUG ("not starting task as pad is not linked"); + result = TRUE; + } GST_QUEUE_MUTEX_UNLOCK (queue); } else { /* step 1, unblock chain and loop functions */