perform synchronisation. Indeed, the running time can be observed in each
element (during the PLAYING state) as:
- running_time = absolute_time - base_time
+ C.running_time = absolute_time - base_time
+
+We note C.running_time as the running_time obtained by looking at the clock.
+This value is monotonically increasing at the rate of the clock.
Timestamps
----------
The GstBuffer timestamps and the preceeding NEW_SEGMENT event (See
-part-streams.txt) define a transformation of the buffers to running_time as
-follows:
+part-streams.txt) define a transformation of the buffer timestamps to
+running_time as follows:
The following notation is used:
NS: NEWSEGMENT event preceeding the buffers.
- NS.start: start field in the NEWSEGMENT event
+ - NS.stop: stop field in the NEWSEGMENT event
- NS.rate: rate field of NEWSEGMENT event
- NS.abs_rate: absolute value of rate field of NEWSEGMENT event
- NS.time: time field in the NEWSEGMENT event
The following transformation to running_time exist:
if (NS.rate > 0.0)
- running_time = (B.timestamp - NS.start) / NS.abs_rate + NS.accum
+ B.running_time = (B.timestamp - NS.start) / NS.abs_rate + NS.accum
else
- running_time = (NS.stop - B.timestamp) / NS.abs_rate + NS.accum
+ B.running_time = (NS.stop - B.timestamp) / NS.abs_rate + NS.accum
+
+We write B.running_time as the running_time obtained from the NEWSEGMENT event
+and the buffers of that segment.
The first displayable buffer will yield a value of 0 (since B.timestamp ==
NS.start and NS.accum == 0).
rate. Likewise, a rate between 0.0 and 1.0 will slow down playback.
For negative rates, timestamps are received stop NS.stop to NS.start so that the
-first buffer received will be transformed into running_time of 0 (B.timestamp ==
+first buffer received will be transformed into B.running_time of 0 (B.timestamp ==
NS.stop and NS.accum == 0).
We prefix C. and B. before the two running times to note how they were
calculated.
-The task of synchronized playback is to make sure that we play be buffer with
+The task of synchronized playback is to make sure that we play a buffer with
B.running_time at the moment when the clock reaches the same C.running_time.
Thus the following must hold:
It is the stream time that is used for:
- report the POSITION query in the pipeline
- - the position used in seek queries
+ - the position used in seek events/queries
- the position used to synchronize controller values
Stream time is calculated using the buffer times and the preceeding NEWSEGMENT
if (G_UNLIKELY (segment->format == GST_FORMAT_UNDEFINED))
segment->format = format;
- else
- g_return_if_fail (segment->format == format);
update_start = update_stop = TRUE;
- /* start is never invalid */
+ /* segment->start is never invalid */
switch (start_type) {
case GST_SEEK_TYPE_NONE:
- /* no update to segment */
+ /* no update to segment, take previous start */
start = segment->start;
update_start = FALSE;
break;
/* start holds desired position, map -1 to the start */
if (start == -1)
start = 0;
+ /* start must be 0 or the formats must match */
+ g_return_if_fail (start == 0 || segment->format == format);
break;
case GST_SEEK_TYPE_CUR:
- /* add start to currently configure segment */
+ g_return_if_fail (start == 0 || segment->format == format);
+ /* add start to currently configured segment */
start = segment->start + start;
break;
case GST_SEEK_TYPE_END:
if (segment->duration != -1) {
+ g_return_if_fail (start == 0 || segment->format == format);
/* add start to total length */
start = segment->duration + start;
} else {
update_stop = FALSE;
break;
case GST_SEEK_TYPE_SET:
- /* stop holds required value */
+ /* stop holds required value, if it's not -1, it must be of the same
+ * format as the segment. */
+ g_return_if_fail (stop == -1 || segment->format == format);
break;
case GST_SEEK_TYPE_CUR:
- if (segment->stop != -1)
+ if (segment->stop != -1) {
+ /* only add compatible formats or 0 */
+ g_return_if_fail (stop == 0 || segment->format == format);
stop = segment->stop + stop;
- else
+ } else
stop = -1;
break;
case GST_SEEK_TYPE_END:
- if (segment->duration != -1)
+ if (segment->duration != -1) {
+ /* only add compatible formats or 0 */
+ g_return_if_fail (stop == 0 || segment->format == format);
stop = segment->duration + stop;
- else {
+ } else {
stop = segment->stop;
update_stop = FALSE;
}
*update = last_stop != segment->last_stop;
/* update new position */
- if (last_stop != segment->last_stop)
- segment->last_stop = last_stop;
+ segment->last_stop = last_stop;
segment->time = start;
segment->stop = stop;
GST_END_TEST;
/* mess with the segment structure in the bytes format */
+GST_START_TEST (segment_seek_rate)
+{
+ GstSegment segment;
+ gboolean update;
+
+ gst_segment_init (&segment, GST_FORMAT_BYTES);
+
+ /* configure segment to rate 2.0, format does not matter when we don't specify
+ * a start or stop position. */
+ gst_segment_set_seek (&segment, 2.0,
+ GST_FORMAT_UNDEFINED,
+ GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_NONE, -1, &update);
+ fail_unless (segment.format == GST_FORMAT_BYTES);
+ fail_unless (segment.start == 0);
+ fail_unless (segment.stop == -1);
+ fail_unless (segment.rate == 2.0);
+ fail_unless (update == FALSE);
+
+ /* 0 is the same in all formats and should not fail */
+ gst_segment_set_seek (&segment, 2.0,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, -1, &update);
+ fail_unless (segment.format == GST_FORMAT_BYTES);
+
+ /* set to -1 means start from 0 */
+ gst_segment_set_seek (&segment, 2.0,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_SET, -1, GST_SEEK_TYPE_NONE, -1, &update);
+ fail_unless (segment.format == GST_FORMAT_BYTES);
+ fail_unless (segment.start == 0);
+
+ gst_segment_set_seek (&segment, 2.0,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_CUR, 0, GST_SEEK_TYPE_NONE, -1, &update);
+
+ gst_segment_set_seek (&segment, 2.0,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_END, 0, GST_SEEK_TYPE_NONE, -1, &update);
+
+ /* -1 for end is fine too in all formats */
+ gst_segment_set_seek (&segment, 2.0,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_SET, -1, &update);
+
+ /* 0 as relative end is fine too */
+ gst_segment_set_seek (&segment, 2.0,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_CUR, 0, &update);
+
+ gst_segment_set_seek (&segment, 2.0,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_END, 0, &update);
+
+ /* set a real stop position, this must happen in bytes */
+ gst_segment_set_seek (&segment, 3.0,
+ GST_FORMAT_BYTES,
+ GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_SET, 100, &update);
+ fail_unless (segment.format == GST_FORMAT_BYTES);
+ fail_unless (segment.start == 0);
+ fail_unless (segment.stop == 100);
+ fail_unless (segment.rate == 3.0);
+ /* no seek should happen, we just updated the stop position in forward
+ * playback mode.*/
+ fail_unless (update == FALSE);
+
+ /* 0 as relative end is fine too */
+ gst_segment_set_seek (&segment, 2.0,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_CUR, 0, &update);
+ fail_unless (segment.stop == 100);
+
+ gst_segment_set_seek (&segment, 2.0,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_END, 0, &update);
+ fail_unless (segment.stop == 100);
+
+ /* -1 for end is fine too in all formats */
+ gst_segment_set_seek (&segment, 2.0,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_SET, -1, &update);
+ fail_unless (segment.stop == -1);
+
+ /* set some duration, stop -1 END seeks will now work with the
+ * duration, if the formats match */
+ gst_segment_set_duration (&segment, GST_FORMAT_BYTES, 200);
+ fail_unless (segment.duration == 200);
+
+ /* seek to end in any format with 0 should set the stop to the
+ * duration */
+ gst_segment_set_seek (&segment, 2.0,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_END, 0, &update);
+ fail_unless (segment.stop == 200);
+ fail_unless (segment.duration == 200);
+
+ /* subtract 100 from the end */
+ gst_segment_set_seek (&segment, 2.0,
+ GST_FORMAT_BYTES, GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_END, -100, &update);
+ fail_unless (segment.stop == 100);
+ fail_unless (segment.duration == 200);
+
+ /* add 100 to the duration, this should be clamped to the duration */
+ gst_segment_set_seek (&segment, 2.0,
+ GST_FORMAT_BYTES, GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_END, 100, &update);
+ fail_unless (segment.stop == 200);
+ fail_unless (segment.duration == 200);
+
+ /* add 300 to the start, this should be clamped to the duration */
+ gst_segment_set_seek (&segment, 2.0,
+ GST_FORMAT_BYTES, GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_CUR, 300, GST_SEEK_TYPE_END, 0, &update);
+ fail_unless (segment.start == 200);
+ fail_unless (segment.stop == 200);
+ fail_unless (segment.duration == 200);
+
+ /* subtract 300 from the start, this should be clamped to 0 */
+ gst_segment_set_seek (&segment, 2.0,
+ GST_FORMAT_BYTES, GST_SEEK_FLAG_NONE,
+ GST_SEEK_TYPE_CUR, -300, GST_SEEK_TYPE_END, 0, &update);
+ fail_unless (segment.start == 0);
+ fail_unless (segment.stop == 200);
+ fail_unless (segment.duration == 200);
+}
+
+GST_END_TEST;
+
+/* mess with the segment structure in the bytes format */
GST_START_TEST (segment_newsegment_open)
{
GstSegment segment;
tcase_add_test (tc_chain, segment_seek_nosize);
tcase_add_test (tc_chain, segment_seek_size);
tcase_add_test (tc_chain, segment_seek_reverse);
+ tcase_add_test (tc_chain, segment_seek_rate);
tcase_add_test (tc_chain, segment_newsegment_open);
tcase_add_test (tc_chain, segment_newsegment_closed);
tcase_add_test (tc_chain, segment_newsegment_streamtime);