+2005-07-21 Wim Taymans <wim@fluendo.com>
+
+ * 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 <wim@fluendo.com>
* gst/gstmessage.c: (gst_message_new_segment_start),
-------
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:
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
-------------------------
/* 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;
}
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;
}
}
GstClockTime end_time;
gboolean have_discont;
- GstClockTime discont_start;
- GstClockTime discont_stop;
+ GstClockTimeDiff discont_start;
+ GstClockTimeDiff discont_stop;
gboolean eos;
gboolean need_preroll;
{
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;
#include "gstevent.h"
#include "gstinfo.h"
#include "gsterror.h"
+#include "gstutils.h"
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
gst_queue_link_src (GstPad * pad, GstPad * peer)
{
GstPadLinkReturn result = GST_PAD_LINK_OK;
+ GstQueue *queue;
+
+ queue = GST_QUEUE (gst_pad_get_parent (pad));
+
+ GST_DEBUG ("queue linking source pad");
- /* FIXME, see if we need to push or get pulled */
- if (GST_PAD_LINKFUNC (peer))
+ 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;
}
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);
/* 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 {
/* 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;
}
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;
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 */
}
/**
+ * 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.
* @orig: the #GstObject that initiated the error.
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);
/* 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;
}
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;
}
}
GstClockTime end_time;
gboolean have_discont;
- GstClockTime discont_start;
- GstClockTime discont_stop;
+ GstClockTimeDiff discont_start;
+ GstClockTimeDiff discont_stop;
gboolean eos;
gboolean need_preroll;
{
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;
#include "gstevent.h"
#include "gstinfo.h"
#include "gsterror.h"
+#include "gstutils.h"
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
gst_queue_link_src (GstPad * pad, GstPad * peer)
{
GstPadLinkReturn result = GST_PAD_LINK_OK;
+ GstQueue *queue;
+
+ queue = GST_QUEUE (gst_pad_get_parent (pad));
+
+ GST_DEBUG ("queue linking source pad");
- /* FIXME, see if we need to push or get pulled */
- if (GST_PAD_LINKFUNC (peer))
+ 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;
}
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);
/* 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 {
/* 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;
}
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;
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 */