diff --git a/ChangeLog b/ChangeLog index 69de6723bf..f3ee6eaf6d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2007-12-19 Wim Taymans + + * docs/design/part-synchronisation.txt: + Small updates. + + * gst/gstsegment.c: (gst_segment_set_seek), + (gst_segment_set_newsegment_full), (gst_segment_to_stream_time), + (gst_segment_to_running_time): + The seek format can be different from the segment format when the start + and stop values are not to be updated, when we only do a rate change for + example. + + * tests/check/gst/gstsegment.c: (GST_START_TEST), + (gst_segment_suite): + Add a testcase for the rate-only seeks, checking that the format is + correctly ignored when start and stop are not updated. + 2007-12-18 Sebastian Dröge Patch by: Matthias Bolte diff --git a/common b/common index 67b8f4e3c5..970759077c 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 67b8f4e3c576945f4d778c9040876af3a5a0756e +Subproject commit 970759077c95ee4e85650db023ac6f974e2aa5e1 diff --git a/docs/design/part-synchronisation.txt b/docs/design/part-synchronisation.txt index 5f61942c7c..1ac845340c 100644 --- a/docs/design/part-synchronisation.txt +++ b/docs/design/part-synchronisation.txt @@ -59,15 +59,18 @@ The clock and pipeline now provides a running_time to all elements that want to 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: @@ -76,6 +79,7 @@ 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 @@ -89,9 +93,12 @@ to these boundaries (see also part-segments.txt). 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). @@ -100,7 +107,7 @@ For NS.rate > 1.0, the timestamps will be scaled down to increase the playback 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). @@ -121,7 +128,7 @@ As we have seen, we can get a running_time: 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: @@ -162,7 +169,7 @@ between 0 and the total duration of the media file. 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 diff --git a/gst/gstsegment.c b/gst/gstsegment.c index 8ee6ef3ce5..9b26e47b80 100644 --- a/gst/gstsegment.c +++ b/gst/gstsegment.c @@ -274,15 +274,13 @@ gst_segment_set_seek (GstSegment * segment, gdouble rate, 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; @@ -290,13 +288,17 @@ gst_segment_set_seek (GstSegment * segment, gdouble rate, /* 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 { @@ -319,18 +321,24 @@ gst_segment_set_seek (GstSegment * segment, gdouble rate, 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; } @@ -373,8 +381,7 @@ gst_segment_set_seek (GstSegment * segment, gdouble rate, *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; diff --git a/tests/check/gst/gstsegment.c b/tests/check/gst/gstsegment.c index ba4e12e495..bc3d0b6409 100644 --- a/tests/check/gst/gstsegment.c +++ b/tests/check/gst/gstsegment.c @@ -435,6 +435,137 @@ GST_START_TEST (segment_seek_reverse) 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) { @@ -1505,6 +1636,7 @@ gst_segment_suite (void) 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);