docs/design/part-overview.txt: Make upsteam/downstream concepts more clear.

Original commit message from CVS:
* docs/design/part-overview.txt:
Make upsteam/downstream concepts more clear.
Give an example of serialized/non-serialized events.
* docs/design/part-events.txt:
* docs/design/part-streams.txt:
Mention applied_rate.
* docs/design/part-trickmodes.txt:
Mention applied rate, flesh out some more use cases.
* gst/gstevent.c: (gst_event_new_new_segment),
(gst_event_parse_new_segment), (gst_event_new_new_segment_full),
(gst_event_parse_new_segment_full), (gst_event_new_tag),
(gst_event_parse_tag), (gst_event_new_buffer_size),
(gst_event_parse_buffer_size), (gst_event_new_qos),
(gst_event_parse_qos), (gst_event_parse_seek),
(gst_event_new_navigation):
* gst/gstevent.h:
Add applied_rate field to NEWSEGMENT event.
API: gst_event_new_new_segment_full()
API: gst_event_parse_new_segment_full()
* gst/gstsegment.c: (gst_segment_init), (gst_segment_set_seek),
(gst_segment_set_newsegment), (gst_segment_set_newsegment_full),
(gst_segment_to_stream_time), (gst_segment_to_running_time):
* gst/gstsegment.h:
Add applied_rate to GstSegment structure.
Make calculation of stream_time and running_time more correct
wrt rate/applied_rate.
Add some more docs.
API: GstSegment::applied_rate field
API: gst_segment_set_newsegment_full();
* libs/gst/base/gstbasesink.c: (gst_base_sink_configure_segment),
(gst_base_sink_get_sync_times), (gst_base_sink_get_position):
* libs/gst/base/gstbasetransform.c:
(gst_base_transform_sink_eventfunc),
(gst_base_transform_handle_buffer):
Parse and use applied_rate in the GstSegment field.
* tests/check/gst/gstevent.c: (GST_START_TEST):
Add check for applied_rate field.
* tests/check/gst/gstsegment.c: (GST_START_TEST),
(gstsegments_suite):
Add more checks for various GstSegment operations.
This commit is contained in:
Wim Taymans 2006-05-08 09:52:33 +00:00
parent 029c8d820b
commit b9dbb55105
13 changed files with 1180 additions and 98 deletions

View file

@ -1,3 +1,53 @@
2006-05-08 Wim Taymans <wim@fluendo.com>
* docs/design/part-overview.txt:
Make upsteam/downstream concepts more clear.
Give an example of serialized/non-serialized events.
* docs/design/part-events.txt:
* docs/design/part-streams.txt:
Mention applied_rate.
* docs/design/part-trickmodes.txt:
Mention applied rate, flesh out some more use cases.
* gst/gstevent.c: (gst_event_new_new_segment),
(gst_event_parse_new_segment), (gst_event_new_new_segment_full),
(gst_event_parse_new_segment_full), (gst_event_new_tag),
(gst_event_parse_tag), (gst_event_new_buffer_size),
(gst_event_parse_buffer_size), (gst_event_new_qos),
(gst_event_parse_qos), (gst_event_parse_seek),
(gst_event_new_navigation):
* gst/gstevent.h:
Add applied_rate field to NEWSEGMENT event.
API: gst_event_new_new_segment_full()
API: gst_event_parse_new_segment_full()
* gst/gstsegment.c: (gst_segment_init), (gst_segment_set_seek),
(gst_segment_set_newsegment), (gst_segment_set_newsegment_full),
(gst_segment_to_stream_time), (gst_segment_to_running_time):
* gst/gstsegment.h:
Add applied_rate to GstSegment structure.
Make calculation of stream_time and running_time more correct
wrt rate/applied_rate.
Add some more docs.
API: GstSegment::applied_rate field
API: gst_segment_set_newsegment_full();
* libs/gst/base/gstbasesink.c: (gst_base_sink_configure_segment),
(gst_base_sink_get_sync_times), (gst_base_sink_get_position):
* libs/gst/base/gstbasetransform.c:
(gst_base_transform_sink_eventfunc),
(gst_base_transform_handle_buffer):
Parse and use applied_rate in the GstSegment field.
* tests/check/gst/gstevent.c: (GST_START_TEST):
Add check for applied_rate field.
* tests/check/gst/gstsegment.c: (GST_START_TEST),
(gstsegments_suite):
Add more checks for various GstSegment operations.
2006-05-08 Wim Taymans <wim@fluendo.com>
* libs/gst/base/gstbasesink.c: (gst_base_sink_get_sync_times),

View file

@ -101,7 +101,7 @@ NEWSEGMENT
A newsegment event is sent downstream by an element to indicate that the following
group of buffers start and end at the specified positions. The newsegment event
also contains the playback speed of the stream.
also contains the playback speed and the applied rate of the stream.
Since the stream time is always set to 0 at start and after a seek, a 0
point for all next buffer's timestamps has to be propagated through the
@ -134,6 +134,7 @@ streamtime of the pipeline will not be reset to 0 so any element that syncs
to the clock must use the stop times of the previous newsegment events to
make the buffer timestamps increasing (part-segments.txt).
TAG
---

View file

@ -29,11 +29,15 @@ Introduction
an example of an ogg/vorbis playback pipeline.
+-----------------------------------------------------------+
| ----------> downstream -------------------> |
| |
| pipeline |
| +---------+ +----------+ +-----------+ +----------+ |
| | filesrc | | oggdemux | | vorbisdec | | alsasink | |
| | src-sink src-sink src-sink | |
| +---------+ +----------+ +-----------+ +----------+ |
| |
| <---------< upstream <-------------------< |
+-----------------------------------------------------------+
The filesrc element reads data from a file on disk. The oggdemux element parses
@ -244,7 +248,14 @@ Dataflow and events
the events are used to denote special conditions in the dataflow such as EOS or
to inform plugins of special events such as flushing or seeking.
Some events must be serialized with the buffer flow, others don't.
Some events must be serialized with the buffer flow, others don't. Serialized
events are inserted between the buffers. Non serialized events jump in front
of any buffers current being processed.
An example of a serialized event is a TAG event that is inserted between buffers
to mark metadata for those buffers.
An example of a non serialized event is the FLUSH event.
Pipeline construction

View file

@ -36,6 +36,7 @@ Typical stream
- marks valid buffer timestamp range (start, stop)
- marks stream_time of buffers (time)
- marks playback rate (rate)
- marks applied rate (applied_rate)
2) N buffers
- displayable buffers are between start/stop of the NEW_SEGMENT

View file

@ -69,11 +69,15 @@ When performing a seek, the following steps have to be taken by the application:
For client side trickmode a NEW_SEGMENT event will be sent downstream with
the new rate and start/stop positions. All elements prepare themselves to
handle the rate (see below).
handle the rate (see below). The applied rate of the NEW_SEGMENT event will
be set to 1.0 to indicate that no rate adjustment has been done.
for server side trick mode a NEW_SEGMENT event is sent downstream with a
rate of 1.0 and the start/stop positions. The elements will configure themselves
for normal playback speed since the server will perform the rate conversions.
The applied rate will be set to the rate that will be applied by the server. This
is done to insure that the position reporting performed in the sink is aware
of the trick mode.
When the seek succeeds, the _send_event() function will return TRUE.
@ -95,9 +99,12 @@ The rate value in the segment should be used to reopen the connection to the ser
requesting data at the new speed and possibly a new playback position.
When the server connection was successfully reopened, set the rate of the segment
to 1.0 so that the client side trickmode is not enabled. Alternatively a combination
of client side and serverside trickmode can be used, for example if the server does
not support certain rates, the client can perform rate conversion for the remainder.
to 1.0 so that the client side trickmode is not enabled. The applied rate in the
segment is set to the rate transformation done by the server.
Alternatively a combination of client side and serverside trickmode can be used, for
example if the server does not support certain rates, the client can perform rate
conversion for the remainder.
source server
@ -117,22 +124,27 @@ not support certain rates, the client can perform rate conversion for the remain
After performing the seek, the source will inform the downstream elements of the
new segment that is to be played back. Since the segment will have a rate of 1.0,
no client side trick modes are enabled.
no client side trick modes are enabled. The segment will have an applied rate
different from 1.0 to indicate that the media contains data with non-standard
playback speed or direction.
client side forward trickmodes
------------------------------
The seek happens as stated above. a NEW_SEGMENT event is sent downstream with a rate
bigger than 1.0. Plugins receiving the NEW_SEGMENT can decide to perform the
different from 1.0. Plugins receiving the NEW_SEGMENT can decide to perform the
rate conversion of the media data (retimestamp video frames, resample audio, ...).
A plugin can also decide to drop frames in the case of fast playback or use a more
efficient decoding algorithm (skip B frames, ...). If a plugin decides to resample
or retimestamp, it should modify the NEW_SEGMENT with a rate of 1.0 so that
downstream elements don't resample again.
or retimestamp, it should modify the NEW_SEGMENT with a rate of 1.0 and update the
applied rate so that downstream elements don't resample again but are aware that the
media has been modified.
The GStreamer base audio and video sinks will resample automatically if they receive
a NEW_SEGMENT event with a rate different from 1.0.
a NEW_SEGMENT event with a rate different from 1.0. The position reporting in the
base audio and video sinks will also depend on the applied rate of the segment
information.
client side backwards trickmode
@ -143,6 +155,8 @@ For backwards playback the following rules apply:
- the rate in the NEW_SEGMENT is less than 0.0.
- the NEW_SEGMENT start position is less than the stop position, playback will
however happen from stop to start in reverse.
- the time member in the NEW_SEGMENT is set to the stream time of the start
position.
For plugins the following rules apply:
@ -154,8 +168,8 @@ For plugins the following rules apply:
Timestamps on the buffers are set starting from the stop position to start,
effectively going backwards.
- A decoder decodes and accumulates all frames. If a new keyframe is received,
all accumulated frames are sent backwards.
- A decoder decodes and accumulates all frames. If a new keyframe or EOS is received,
all accumulated frames are sent downsteam in reverse.
- A sink reverses (for audio) and retimestamps (audio, video) the buffers before
playing them back. Retimestamping occurs relative to the stop position, making

View file

@ -416,31 +416,94 @@ gst_event_new_eos (void)
* @stop: the stop value of the segment
* @position: stream position
*
* Allocate a new newsegment event with the given format/values tripplets.
* Allocate a new newsegment event with the given format/values tripplets
*
* The newsegment event marks the range of buffers to be processed. All
* data not within the segment range is not to be processed. This can be
* used intelligently by plugins to use more efficient methods of skipping
* unneeded packets.
*
* The stream time of the segment is used to convert the buffer timestamps
* into the stream time again, this is usually done in sinks to report the
* current stream_time. @stream_time cannot be -1.
*
* @start cannot be -1, @stop can be -1. If there
* is a valid @stop given, it must be greater or equal than @start.
*
* After a newsegment event, the buffer stream time is calculated with:
*
* stream_time + (TIMESTAMP(buf) - start) * ABS (rate)
*
* Returns: A new newsegment event.
* This method calls gst_event_new_new_segment_full() passing a default
* value of 1.0 for applied_rate
*/
GstEvent *
gst_event_new_new_segment (gboolean update, gdouble rate, GstFormat format,
gint64 start, gint64 stop, gint64 position)
{
return gst_event_new_new_segment_full (update, rate, 1.0, format, start,
stop, position);
}
/**
* gst_event_parse_new_segment:
* @event: The event to query
* @update: A pointer to the update flag of the segment
* @rate: A pointer to the rate of the segment
* @format: A pointer to the format of the newsegment values
* @start: A pointer to store the start value in
* @stop: A pointer to store the stop value in
* @position: A pointer to store the stream time in
*
* Get the update flag, rate, format, start, stop and position in the
* newsegment event. In general, gst_event_parse_new_segment_full() should
* be used instead of this, to also retrieve the applied_rate value of the
* segment. See gst_event_new_new_segment_full() for a full description
* of the newsegment event.
*/
void
gst_event_parse_new_segment (GstEvent * event, gboolean * update,
gdouble * rate, GstFormat * format, gint64 * start,
gint64 * stop, gint64 * position)
{
gst_event_parse_new_segment_full (event, update, rate, NULL, format, start,
stop, position);
}
/**
* gst_event_new_new_segment_full:
* @update: Whether this segment is an update to a previous one
* @rate: A new rate for playback
* @applied_rate: The rate factor which has already been applied
* @format: The format of the segment values
* @start: The start value of the segment
* @stop: The stop value of the segment
* @position: stream position
*
* Allocate a new newsegment event with the given format/values triplets.
*
* The newsegment event marks the range of buffers to be processed. All
* data not within the segment range is not to be processed. This can be
* used intelligently by plugins to apply more efficient methods of skipping
* unneeded packets.
*
* The position value of the segment is used in conjunction with the start
* value to convert the buffer timestamps into the stream time. This is
* usually done in sinks to report the current stream_time.
* @position represents the stream_time of a buffer carrying a timestamp of
* @start. @position cannot be -1.
*
* @start cannot be -1, @stop can be -1. If there
* is a valid @stop given, it must be greater or equal the @start, including
* when the indicated playback @rate is < 0
*
* The @applied_rate value provides information about any rate adjustment that
* has already been made to the timestamps and content on the buffers of the
* stream. (@rate * @applied_rate) should always equal the rate that has been
* requested for playback. For example, if an element has an input segment
* with intended playback @rate of 2.0 and applied_rate of 1.0, it can adjust
* incoming timestamps and buffer content by half and output a newsegment event
* with @rate of 1.0 and @applied_rate of 2.0
*
* After a newsegment event, the buffer stream time is calculated with:
*
* position + (TIMESTAMP(buf) - start) * ABS (rate * applied_rate)
*
* Returns: A new newsegment event.
*
* Since: 0.10.6
*/
GstEvent *
gst_event_new_new_segment_full (gboolean update, gdouble rate,
gdouble applied_rate, GstFormat format, gint64 start, gint64 stop,
gint64 position)
{
g_return_val_if_fail (rate != 0.0, NULL);
g_return_val_if_fail (applied_rate != 0.0, NULL);
if (format == GST_FORMAT_TIME) {
GST_CAT_INFO (GST_CAT_EVENT,
@ -455,6 +518,7 @@ gst_event_new_new_segment (gboolean update, gdouble rate, GstFormat format,
"start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT ", position %"
G_GINT64_FORMAT, update, rate, format, start, stop, position);
}
g_return_val_if_fail (position != -1, NULL);
g_return_val_if_fail (start != -1, NULL);
if (stop != -1)
@ -464,6 +528,7 @@ gst_event_new_new_segment (gboolean update, gdouble rate, GstFormat format,
gst_structure_new ("GstEventNewsegment",
"update", G_TYPE_BOOLEAN, update,
"rate", G_TYPE_DOUBLE, rate,
"applied_rate", G_TYPE_DOUBLE, applied_rate,
"format", GST_TYPE_FORMAT, format,
"start", G_TYPE_INT64, start,
"stop", G_TYPE_INT64, stop,
@ -471,21 +536,26 @@ gst_event_new_new_segment (gboolean update, gdouble rate, GstFormat format,
}
/**
* gst_event_parse_new_segment:
* gst_event_parse_new_segment_full:
* @event: The event to query
* @update: A pointer to the update flag of the segment
* @rate: A pointer to the rate of the segment
* @applied_rate: A pointer to the applied_rate of the segment
* @format: A pointer to the format of the newsegment values
* @start: A pointer to store the start value in
* @stop: A pointer to store the stop value in
* @position: A pointer to store the stream time in
*
* Get the format, start, stop and position in the newsegment event.
* Get the update, rate, applied_rate, format, start, stop and
* position in the newsegment event. See gst_event_new_new_segment_full()
* for a full description of the newsegment event.
*
* Since: 0.10.6
*/
void
gst_event_parse_new_segment (GstEvent * event, gboolean * update,
gdouble * rate, GstFormat * format, gint64 * start,
gint64 * stop, gint64 * position)
gst_event_parse_new_segment_full (GstEvent * event, gboolean * update,
gdouble * rate, gdouble * applied_rate, GstFormat * format,
gint64 * start, gint64 * stop, gint64 * position)
{
const GstStructure *structure;
@ -493,18 +563,22 @@ gst_event_parse_new_segment (GstEvent * event, gboolean * update,
g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT);
structure = gst_event_get_structure (event);
if (update)
if (G_LIKELY (update))
*update =
g_value_get_boolean (gst_structure_get_value (structure, "update"));
if (rate)
if (G_LIKELY (rate))
*rate = g_value_get_double (gst_structure_get_value (structure, "rate"));
if (format)
if (G_LIKELY (applied_rate))
*applied_rate =
g_value_get_double (gst_structure_get_value (structure,
"applied_rate"));
if (G_LIKELY (format))
*format = g_value_get_enum (gst_structure_get_value (structure, "format"));
if (start)
if (G_LIKELY (start))
*start = g_value_get_int64 (gst_structure_get_value (structure, "start"));
if (stop)
if (G_LIKELY (stop))
*stop = g_value_get_int64 (gst_structure_get_value (structure, "stop"));
if (position)
if (G_LIKELY (position))
*position =
g_value_get_int64 (gst_structure_get_value (structure, "position"));
}
@ -521,7 +595,6 @@ GstEvent *
gst_event_new_tag (GstTagList * taglist)
{
g_return_val_if_fail (taglist != NULL, NULL);
return gst_event_new_custom (GST_EVENT_TAG, (GstStructure *) taglist);
}
@ -537,7 +610,6 @@ gst_event_parse_tag (GstEvent * event, GstTagList ** taglist)
{
g_return_if_fail (GST_IS_EVENT (event));
g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_TAG);
if (taglist)
*taglist = (GstTagList *) event->structure;
}
@ -565,7 +637,6 @@ gst_event_new_buffer_size (GstFormat format, gint64 minsize,
"creating buffersize format %d, minsize %" G_GINT64_FORMAT
", maxsize %" G_GINT64_FORMAT ", async %d", format,
minsize, maxsize, async);
return gst_event_new_custom (GST_EVENT_BUFFERSIZE,
gst_structure_new ("GstEventBufferSize",
"format", GST_TYPE_FORMAT, format,
@ -592,7 +663,6 @@ gst_event_parse_buffer_size (GstEvent * event, GstFormat * format,
g_return_if_fail (GST_IS_EVENT (event));
g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_BUFFERSIZE);
structure = gst_event_get_structure (event);
if (format)
*format = g_value_get_enum (gst_structure_get_value (structure, "format"));
@ -645,9 +715,9 @@ gst_event_new_qos (gdouble proportion, GstClockTimeDiff diff,
GstClockTime timestamp)
{
GST_CAT_INFO (GST_CAT_EVENT,
"creating qos proportion %lf, diff %" GST_TIME_FORMAT
"creating qos proportion %lf, diff %" G_GINT64_FORMAT
", timestamp %" GST_TIME_FORMAT, proportion,
GST_TIME_ARGS (diff), GST_TIME_ARGS (timestamp));
diff, GST_TIME_ARGS (timestamp));
return gst_event_new_custom (GST_EVENT_QOS,
gst_structure_new ("GstEventQOS",
@ -673,7 +743,6 @@ gst_event_parse_qos (GstEvent * event, gdouble * proportion,
g_return_if_fail (GST_IS_EVENT (event));
g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_QOS);
structure = gst_event_get_structure (event);
if (proportion)
*proportion =
@ -761,16 +830,14 @@ gst_event_new_seek (gdouble rate, GstFormat format, GstSeekFlags flags,
* Parses a seek @event and stores the results in the given result locations.
*/
void
gst_event_parse_seek (GstEvent * event, gdouble * rate, GstFormat * format,
GstSeekFlags * flags,
GstSeekType * cur_type, gint64 * cur,
GstSeekType * stop_type, gint64 * stop)
gst_event_parse_seek (GstEvent * event, gdouble * rate,
GstFormat * format, GstSeekFlags * flags, GstSeekType * cur_type,
gint64 * cur, GstSeekType * stop_type, gint64 * stop)
{
const GstStructure *structure;
g_return_if_fail (GST_IS_EVENT (event));
g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_SEEK);
structure = gst_event_get_structure (event);
if (rate)
*rate = g_value_get_double (gst_structure_get_value (structure, "rate"));
@ -802,6 +869,5 @@ GstEvent *
gst_event_new_navigation (GstStructure * structure)
{
g_return_val_if_fail (structure != NULL, NULL);
return gst_event_new_custom (GST_EVENT_NAVIGATION, structure);
}

View file

@ -368,12 +368,25 @@ GstEvent* gst_event_new_new_segment (gboolean update, gdouble rate,
GstFormat format,
gint64 start, gint64 stop,
gint64 position);
GstEvent* gst_event_new_new_segment_full (gboolean update, gdouble rate,
gdouble applied_rate,
GstFormat format,
gint64 start, gint64 stop,
gint64 position);
void gst_event_parse_new_segment (GstEvent *event,
gboolean *update,
gdouble *rate,
GstFormat *format,
gint64 *start, gint64 *stop,
gint64 *position);
void gst_event_parse_new_segment_full (GstEvent *event,
gboolean *update,
gdouble *rate,
gdouble *applied_rate,
GstFormat *format,
gint64 *start, gint64 *stop,
gint64 *position);
/* tag event */
GstEvent* gst_event_new_tag (GstTagList *taglist);
void gst_event_parse_tag (GstEvent *event, GstTagList **taglist);

View file

@ -49,11 +49,13 @@
* A segment structure is initialized with gst_segment_init(), which takes a #GstFormat
* that will be used as the format of the segment values. The segment will be configured
* with a start value of 0 and a stop/duration of -1, which is undefined. The default
* rate is 1.0.
* rate and applied_rate is 1.0.
*
* If the segment is used for managing seeks, the segment duration should be set with
* gst_segment_set_duration(). The public duration field contains the duration of the
* segment.
* segment. When using the segment for seeking, the start and time members should
* normally be left to their default 0 value. The stop position is left to -1 unless
* explicitly configured to a different value after a seek event.
*
* The current position in the segment should be set with the gst_segment_set_last_stop().
* The public last_stop field contains the last set stop position in the segment.
@ -71,13 +73,14 @@
*
* For elements that want to synchronize to the pipeline clock, gst_segment_to_running_time()
* can be used to convert a timestamp to a value that can be used to synchronize
* to the clock. This function takes into account all accumulated segments.
* to the clock. This function takes into account all accumulated segments as well as
* any rate or applied_rate conversions.
*
* For elements that need to perform operations on media data in stream_time,
* gst_segment_to_stream_time() can be used to convert a timestamp and the segment
* info to stream time (which is always between 0 and the duration of the stream).
*
* Last reviewed on 2006-03-12 (0.10.5)
* Last reviewed on 2006-05-03 (0.10.6)
*/
static GstSegment *
@ -154,6 +157,7 @@ gst_segment_init (GstSegment * segment, GstFormat format)
segment->rate = 1.0;
segment->abs_rate = 1.0;
segment->applied_rate = 1.0;
segment->format = format;
segment->flags = 0;
segment->start = 0;
@ -235,6 +239,10 @@ gst_segment_set_last_stop (GstSegment * segment, GstFormat format,
* from GST_SEEK_TYPE_NONE, the current position is not updated and
* streaming should continue from the last position, possibly with
* updated rate, flags or stop position.
*
* The applied rate of the segment will be set to 1.0 by default.
* If the caller can apply a rate change, it should update @segment
* rate and applied_rate after calling this function.
*/
void
gst_segment_set_seek (GstSegment * segment, gdouble rate,
@ -324,6 +332,7 @@ gst_segment_set_seek (GstSegment * segment, gdouble rate,
segment->rate = rate;
segment->abs_rate = ABS (rate);
segment->applied_rate = 1.0;
segment->flags = flags;
segment->start = cur;
if (update_start) {
@ -346,15 +355,41 @@ gst_segment_set_seek (GstSegment * segment, gdouble rate,
* @stop: the new stop value
* @time: the new stream time
*
* Update the segment structure with the field values of a new segment event.
* Update the segment structure with the field values of a new segment event and
* with a default applied_rate of 1.0.
*
* Since: 0.10.6
*/
void
gst_segment_set_newsegment (GstSegment * segment, gboolean update, gdouble rate,
GstFormat format, gint64 start, gint64 stop, gint64 time)
{
gst_segment_set_newsegment_full (segment, update, rate, 1.0, format, start,
stop, time);
}
/**
* gst_segment_set_newsegment_full:
* @segment: a #GstSegment structure.
* @update: flag indicating a new segment is started or updated
* @rate: the rate of the segment.
* @applied_rate: the applied rate of the segment.
* @format: the format of the segment.
* @start: the new start value
* @stop: the new stop value
* @time: the new stream time
*
* Update the segment structure with the field values of a new segment event.
*/
void
gst_segment_set_newsegment_full (GstSegment * segment, gboolean update,
gdouble rate, gdouble applied_rate, GstFormat format, gint64 start,
gint64 stop, gint64 time)
{
gint64 duration;
g_return_if_fail (rate != 0.0);
g_return_if_fail (applied_rate != 0.0);
g_return_if_fail (segment != NULL);
if (segment->format == GST_FORMAT_UNDEFINED)
@ -382,23 +417,29 @@ gst_segment_set_newsegment (GstSegment * segment, gboolean update, gdouble rate,
* segment. the accumulated time is used when syncing to the
* clock.
*/
if (GST_CLOCK_TIME_IS_VALID (segment->stop)) {
if (segment->stop != -1) {
duration = segment->stop - segment->start;
} else if (GST_CLOCK_TIME_IS_VALID (segment->last_stop)) {
} else if (segment->last_stop != -1) {
/* else use last seen timestamp as segment stop */
duration = segment->last_stop - segment->start;
} else {
/* else we don't know */
/* else we don't know and throw a warning.. really, this should
* be fixed in the element. */
g_warning ("closing segment of unknown duration, assuming duration of 0");
duration = 0;
}
}
/* use previous rate to calculate duration */
segment->accum += gst_gdouble_to_guint64 (
(gst_guint64_to_gdouble (duration) / segment->abs_rate));
if (segment->abs_rate != 1.0)
duration /= segment->abs_rate;
/* accumulate duration */
segment->accum += duration;
/* then update the current segment */
segment->rate = rate;
segment->abs_rate = ABS (rate);
segment->applied_rate = applied_rate;
segment->start = start;
segment->last_stop = start;
segment->stop = stop;
@ -417,8 +458,8 @@ gst_segment_set_newsegment (GstSegment * segment, gboolean update, gdouble rate,
*
* This function is typically used by elements that need to operate on
* the stream time of the buffers it receives, such as effect plugins.
* In those use cases, @position is typically the buffer timestamp that
* one wants to convert to the stream time.
* In those use cases, @position is typically the buffer timestamp or
* clock time that one wants to convert to the stream time.
* The stream time is always between 0 and the total duration of the
* media stream.
*
@ -429,22 +470,52 @@ gint64
gst_segment_to_stream_time (GstSegment * segment, GstFormat format,
gint64 position)
{
gint64 result, time;
gint64 result;
gdouble abs_applied_rate;
g_return_val_if_fail (segment != NULL, -1);
/* format does not matter for -1 */
if (position == -1)
return -1;
if (segment->format == GST_FORMAT_UNDEFINED)
segment->format = format;
else
g_return_val_if_fail (segment->format == format, -1);
if ((time = segment->time) == -1)
time = 0;
/* outside of the segment boundary stop */
if (segment->stop != -1 && position >= segment->stop)
return -1;
if (position != -1 && position >= segment->start)
result = ((position - segment->start) / segment->abs_rate) + time;
else
result = -1;
/* before the segment boundary */
if (position < segment->start)
return -1;
/* time must be known */
if (segment->time == -1)
return -1;
/* bring to uncorrected position in segment */
result = position - segment->start;
abs_applied_rate = ABS (segment->applied_rate);
/* correct for applied rate if needed */
if (abs_applied_rate != 1.0)
result *= abs_applied_rate;
/* add or subtract from segment time based on applied rate */
if (segment->applied_rate > 0.0) {
/* correct for segment time */
result += segment->time;
} else {
/* correct for segment time, clamp at 0 */
if (segment->time > result)
result = segment->time - result;
else
result = 0;
}
return result;
}
@ -456,14 +527,18 @@ gst_segment_to_stream_time (GstSegment * segment, GstFormat format,
* @position: the position in the segment
*
* Translate @position to the total running time using the currently configured
* and previously accumulated segments.
* and previously accumulated segments. Position is a value between @segment
* start and stop time.
*
* This function is typically used by elements that need to synchronize to the
* global clock in a pipeline. The runnning time is a constantly increasing value
* starting from 0. When gst_segment_init() is called, this value will reset to
* 0.
*
* Returns: the position as the total running time.
* This function returns -1 if the position is outside of @segment start and stop.
*
* Returns: the position as the total running time or -1 when an invalid position
* was given.
*/
gint64
gst_segment_to_running_time (GstSegment * segment, GstFormat format,
@ -473,15 +548,42 @@ gst_segment_to_running_time (GstSegment * segment, GstFormat format,
g_return_val_if_fail (segment != NULL, -1);
if (position == -1)
return -1;
if (segment->format == GST_FORMAT_UNDEFINED)
segment->format = format;
else if (segment->accum)
g_return_val_if_fail (segment->format == format, -1);
if (position != -1 && position >= segment->start)
result = ((position - segment->start) / segment->abs_rate) + segment->accum;
else
result = -1;
/* before the segment boundary */
if (position < segment->start)
return -1;
if (segment->rate > 0.0) {
/* outside of the segment boundary stop */
if (segment->stop != -1 && position >= segment->stop)
return -1;
/* bring to uncorrected position in segment */
result = position - segment->start;
} else {
/* cannot continue if no stop position set or outside of
* the segment. */
if (segment->stop == -1 || position >= segment->stop)
return -1;
/* bring to uncorrected position in segment */
result = segment->stop - position;
}
/* scale based on the rate, avoid division by and conversion to
* float when not needed */
if (segment->abs_rate != 1.0)
result /= segment->abs_rate;
/* correct for accumulated segments */
result += segment->accum;
return result;
}
@ -496,7 +598,8 @@ gst_segment_to_running_time (GstSegment * segment, GstFormat format,
* @clip_stop: the clipped stop position in the segment
*
* Clip the given @start and @stop values to the segment boundaries given
* in @segment.
* in @segment. @start and @stop are compared and clipped to @segment
* start and stop values.
*
* If the function returns FALSE, @start and @stop are known to fall
* outside of @segment and @clip_start and @clip_stop are not updated.

View file

@ -35,7 +35,7 @@ typedef struct _GstSegment GstSegment;
/**
* GstSegment:
* @rate: the rate of the segment
* @abs_rate: absolute value of the rate
* @abs_rate: absolute value of @rate
* @format: the format of the segment values
* @flags: flags for this segment
* @start: the start of the segment
@ -44,6 +44,7 @@ typedef struct _GstSegment GstSegment;
* @accum: accumulated segment
* @last_stop: last known stop time
* @duration: total duration of segment
* @applied_rate: the already applied rate to the segment
*
* A helper structure that holds the configured region of
* interest in a media file.
@ -62,8 +63,12 @@ struct _GstSegment {
gint64 last_stop;
gint64 duration;
/* API added 0.10.6 */
gdouble applied_rate;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
//gpointer _gst_reserved[GST_PADDING-2];
guint8 _gst_reserved[(sizeof (gpointer) * GST_PADDING) - sizeof (gdouble)];
};
GType gst_segment_get_type (void);
@ -84,6 +89,9 @@ void gst_segment_set_seek (GstSegment *segment, gdouble rate,
void gst_segment_set_newsegment (GstSegment *segment, gboolean update, gdouble rate,
GstFormat format, gint64 start, gint64 stop, gint64 time);
void gst_segment_set_newsegment_full (GstSegment *segment, gboolean update, gdouble rate,
gdouble applied_rate, GstFormat format, gint64 start,
gint64 stop, gint64 time);
gint64 gst_segment_to_stream_time (GstSegment *segment, GstFormat format, gint64 position);
gint64 gst_segment_to_running_time (GstSegment *segment, GstFormat format, gint64 position);

View file

@ -700,7 +700,7 @@ gst_base_sink_configure_segment (GstBaseSink * basesink, GstPad * pad,
GstEvent * event, GstSegment * segment)
{
gboolean update;
gdouble rate;
gdouble rate, arate;
GstFormat format;
gint64 start;
gint64 stop;
@ -708,7 +708,7 @@ gst_base_sink_configure_segment (GstBaseSink * basesink, GstPad * pad,
/* the newsegment event is needed to bring the buffer timestamps to the
* stream time and to drop samples outside of the playback segment. */
gst_event_parse_new_segment (event, &update, &rate, &format,
gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
&start, &stop, &time);
GST_OBJECT_LOCK (basesink);
@ -716,7 +716,8 @@ gst_base_sink_configure_segment (GstBaseSink * basesink, GstPad * pad,
if (segment->format != format)
gst_segment_init (segment, format);
gst_segment_set_newsegment (segment, update, rate, format, start, stop, time);
gst_segment_set_newsegment_full (segment, update, rate, arate, format, start,
stop, time);
if (format == GST_FORMAT_TIME) {
GST_DEBUG_OBJECT (basesink,
@ -2289,7 +2290,7 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
base = GST_ELEMENT_CAST (basesink)->base_time;
accum = basesink->segment.accum;
rate = basesink->segment.rate;
rate = basesink->segment.rate * basesink->segment.applied_rate;
gst_base_sink_get_position_last (basesink, &last);
gst_object_ref (clock);
@ -2301,7 +2302,8 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
now = gst_clock_get_time (clock);
/* subtract base time and accumulated time from the clock time.
* Make sure we don't go negative. This is the current time in
* the segment which we need to scale with the rate */
* the segment which we need to scale with the combined
* rate and applied rate. */
base += accum;
base = MIN (now, base);
/* for negative rate this will count back from the segment time */

View file

@ -1170,18 +1170,18 @@ gst_base_transform_sink_eventfunc (GstBaseTransform * trans, GstEvent * event)
case GST_EVENT_NEWSEGMENT:
{
GstFormat format;
gdouble rate;
gdouble rate, arate;
gint64 start, stop, time;
gboolean update;
gst_event_parse_new_segment (event, &update, &rate, &format, &start,
&stop, &time);
gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
&start, &stop, &time);
if (trans->segment.format != format)
gst_segment_init (&trans->segment, format);
gst_segment_set_newsegment (&trans->segment, update, rate, format, start,
stop, time);
gst_segment_set_newsegment_full (&trans->segment, update, rate, arate,
format, start, stop, time);
trans->have_newsegment = TRUE;

View file

@ -59,7 +59,7 @@ GST_START_TEST (create_custom_events)
}
/* NEWSEGMENT */
{
gdouble rate;
gdouble rate, applied_rate;
GstFormat format;
gint64 start, end, base;
gboolean update;
@ -82,8 +82,42 @@ GST_START_TEST (create_custom_events)
fail_unless (end == G_MAXINT64);
fail_unless (base == 0xdeadbeef);
/* Check that the new segment was created with applied_rate of 1.0 */
gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
&format, &start, &end, &base);
fail_unless (update == FALSE);
fail_unless (rate == 0.5);
fail_unless (applied_rate == 1.0);
fail_unless (format == GST_FORMAT_TIME);
fail_unless (start == 1);
fail_unless (end == G_MAXINT64);
gst_event_unref (event);
event =
gst_event_new_new_segment_full (TRUE, 0.75, 0.5, GST_FORMAT_BYTES, 0,
G_MAXINT64 - 1, 0xdeadbeef);
fail_if (event == NULL);
fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT);
fail_if (GST_EVENT_IS_UPSTREAM (event));
fail_unless (GST_EVENT_IS_DOWNSTREAM (event));
fail_unless (GST_EVENT_IS_SERIALIZED (event));
gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
&format, &start, &end, &base);
fail_unless (update == TRUE);
fail_unless (rate == 0.75);
fail_unless (applied_rate == 0.5);
fail_unless (format == GST_FORMAT_BYTES);
fail_unless (start == 0);
fail_unless (end == (G_MAXINT64 - 1));
gst_event_unref (event);
}
/* TAGS */
{
GstTagList *taglist = gst_tag_list_new ();

View file

@ -53,7 +53,7 @@ GST_START_TEST (segment_seek_nosize)
res = gst_segment_clip (&segment, GST_FORMAT_BYTES, 0, 50, &cstart, &cstop);
fail_unless (res == FALSE);
/* touching lower bound */
/* touching lower bound, still outside of the segment */
res = gst_segment_clip (&segment, GST_FORMAT_BYTES, 50, 100, &cstart, &cstop);
fail_unless (res == FALSE);
@ -81,7 +81,7 @@ GST_START_TEST (segment_seek_nosize)
res = gst_segment_clip (&segment, GST_FORMAT_BYTES, -1, 100, &cstart, &cstop);
fail_unless (res == FALSE);
/* start outside */
/* start outside, we don't know the stop */
res = gst_segment_clip (&segment, GST_FORMAT_BYTES, 50, -1, &cstart, &cstop);
fail_unless (res == TRUE);
fail_unless (cstart == 100);
@ -108,7 +108,8 @@ GST_START_TEST (segment_seek_nosize)
fail_unless (segment.stop == 300);
/* add 100 to start (to 300), set stop to 200, this is not allowed.
* nothing should be updated in the segment. */
* nothing should be updated in the segment. A g_warning is
* emited. */
ASSERT_CRITICAL (gst_segment_set_seek (&segment, 1.0,
GST_FORMAT_BYTES,
GST_SEEK_FLAG_NONE,
@ -417,7 +418,7 @@ GST_START_TEST (segment_newsegment_open)
fail_unless (segment.time == 100);
fail_unless (segment.accum == 100);
/* last_stop unknown, accum does not change */
/* last_stop 0, accum does not change */
gst_segment_set_newsegment (&segment, FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0);
fail_unless (segment.start == 0);
@ -498,6 +499,778 @@ GST_START_TEST (segment_newsegment_closed)
GST_END_TEST;
/* mess with the segment structure in the time format */
GST_START_TEST (segment_newsegment_streamtime)
{
GstSegment segment;
gint64 result;
gst_segment_init (&segment, GST_FORMAT_TIME);
/***************************
* Normal segment
***************************/
gst_segment_set_newsegment_full (&segment, FALSE, 1.0, 1.0,
GST_FORMAT_TIME, 0, 200, 0);
fail_unless (segment.rate == 1.0);
fail_unless (segment.applied_rate == 1.0);
fail_unless (segment.format == GST_FORMAT_TIME);
fail_unless (segment.flags == 0);
fail_unless (segment.start == 0);
fail_unless (segment.stop == 200);
fail_unless (segment.time == 0);
fail_unless (segment.accum == 0);
fail_unless (segment.last_stop == 0);
fail_unless (segment.duration == -1);
/* invalid time gives invalid result */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 0);
fail_unless (result == 0);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 100);
fail_unless (result == 100);
/* outside of the segment */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 200);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 300);
fail_unless (result == -1);
/*********************
* time shifted by 500
*********************/
gst_segment_set_newsegment_full (&segment, FALSE, 1.0, 1.0,
GST_FORMAT_TIME, 0, 200, 500);
fail_unless (segment.accum == 200);
/* invalid time gives invalid result */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 0);
fail_unless (result == 500);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 100);
fail_unless (result == 600);
/* outside of the segment */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 500);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 300);
fail_unless (result == -1);
/*********************
* time offset by 500
*********************/
gst_segment_set_newsegment_full (&segment, FALSE, 1.0, 1.0,
GST_FORMAT_TIME, 500, 700, 0);
fail_unless (segment.accum == 400);
/* invalid time gives invalid result */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
/* before segment is invalid */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 400);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 500);
fail_unless (result == 0);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 600);
fail_unless (result == 100);
/* outside of the segment */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 700);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 800);
fail_unless (result == -1);
/*************************************
* time offset by 500, shifted by 200
*************************************/
gst_segment_set_newsegment_full (&segment, FALSE, 1.0, 1.0,
GST_FORMAT_TIME, 500, 700, 200);
fail_unless (segment.accum == 600);
/* invalid time gives invalid result */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
/* before segment is invalid */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 400);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 500);
fail_unless (result == 200);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 600);
fail_unless (result == 300);
/* outside of the segment */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 700);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 800);
fail_unless (result == -1);
}
GST_END_TEST;
/* mess with the segment structure in the time format */
GST_START_TEST (segment_newsegment_streamtime_rate)
{
GstSegment segment;
gint64 result;
gst_segment_init (&segment, GST_FORMAT_TIME);
/***************************
* Normal segment rate 2.0
***************************/
gst_segment_set_newsegment_full (&segment, FALSE, 2.0, 1.0,
GST_FORMAT_TIME, 0, 200, 0);
fail_unless (segment.rate == 2.0);
fail_unless (segment.applied_rate == 1.0);
fail_unless (segment.format == GST_FORMAT_TIME);
fail_unless (segment.flags == 0);
fail_unless (segment.start == 0);
fail_unless (segment.stop == 200);
fail_unless (segment.time == 0);
fail_unless (segment.accum == 0);
fail_unless (segment.last_stop == 0);
fail_unless (segment.duration == -1);
/* invalid time gives invalid result */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 0);
fail_unless (result == 0);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 100);
fail_unless (result == 100);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 150);
fail_unless (result == 150);
/* outside of the segment */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 200);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 300);
fail_unless (result == -1);
/***************************************
* Normal segment rate 2.0, offset
***************************************/
gst_segment_set_newsegment_full (&segment, FALSE, 2.0, 1.0,
GST_FORMAT_TIME, 100, 300, 0);
fail_unless (segment.accum == 100);
/* invalid time gives invalid result */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 100);
fail_unless (result == 0);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 200);
fail_unless (result == 100);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 250);
fail_unless (result == 150);
/* outside of the segment */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 300);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 400);
fail_unless (result == -1);
/***************************************
* Normal segment rate -1.0, offset
***************************************/
/* buffers will arrive from 300 to 100 in a sink, stream time
* calculation is unaffected by the rate */
gst_segment_set_newsegment_full (&segment, FALSE, -1.0, 1.0,
GST_FORMAT_TIME, 100, 300, 0);
fail_unless (segment.accum == 200);
/* invalid time gives invalid result */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 100);
fail_unless (result == 0);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 200);
fail_unless (result == 100);
/***********************************************
* Normal segment rate -1.0, offset, time = 200
***********************************************/
gst_segment_set_newsegment_full (&segment, FALSE, -1.0, 1.0,
GST_FORMAT_TIME, 100, 300, 200);
/* invalid time gives invalid result */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 100);
fail_unless (result == 200);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 200);
fail_unless (result == 300);
/* outside of the segment */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 300);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 400);
fail_unless (result == -1);
}
GST_END_TEST;
/* mess with the segment structure in the time format */
GST_START_TEST (segment_newsegment_streamtime_applied_rate)
{
GstSegment segment;
gint64 result;
gst_segment_init (&segment, GST_FORMAT_TIME);
/***********************************************************
* Normal segment rate 1.0, applied rate -1.0
* This means the timestamps represents a stream going backwards
* starting from @time to 0.
************************************************************/
gst_segment_set_newsegment_full (&segment, FALSE, 1.0, -1.0,
GST_FORMAT_TIME, 0, 200, 200);
fail_unless (segment.rate == 1.0);
fail_unless (segment.applied_rate == -1.0);
fail_unless (segment.format == GST_FORMAT_TIME);
fail_unless (segment.flags == 0);
fail_unless (segment.start == 0);
fail_unless (segment.stop == 200);
fail_unless (segment.time == 200);
fail_unless (segment.accum == 0);
fail_unless (segment.last_stop == 0);
fail_unless (segment.duration == -1);
/* invalid time gives invalid result */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
/* we count backwards from 200 */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 0);
fail_unless (result == 200);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 100);
fail_unless (result == 100);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 150);
fail_unless (result == 50);
/* outside of the segment */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 200);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 300);
fail_unless (result == -1);
/***********************************************************
* Normal segment rate 1.0, applied rate 2.0
* This means the timestamps represents a stream at twice the
* normal rate
************************************************************/
gst_segment_set_newsegment_full (&segment, FALSE, 1.0, 2.0,
GST_FORMAT_TIME, 0, 200, 0);
fail_unless (segment.rate == 1.0);
fail_unless (segment.applied_rate == 2.0);
fail_unless (segment.format == GST_FORMAT_TIME);
fail_unless (segment.flags == 0);
fail_unless (segment.start == 0);
fail_unless (segment.stop == 200);
fail_unless (segment.time == 0);
fail_unless (segment.accum == 200);
fail_unless (segment.last_stop == 0);
fail_unless (segment.duration == -1);
/* invalid time gives invalid result */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 0);
fail_unless (result == 0);
/* the stream prepresents a stream going twice as fast, the position
* in the segment is therefore scaled by the applied rate */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 100);
fail_unless (result == 200);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 150);
fail_unless (result == 300);
/* outside of the segment */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 200);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 300);
fail_unless (result == -1);
/***********************************************************
* Normal segment rate 1.0, applied rate -2.0
* This means the timestamps represents a stream at twice the
* reverse rate
************************************************************/
gst_segment_set_newsegment_full (&segment, FALSE, 1.0, -2.0,
GST_FORMAT_TIME, 0, 200, 400);
fail_unless (segment.rate == 1.0);
fail_unless (segment.applied_rate == -2.0);
fail_unless (segment.format == GST_FORMAT_TIME);
fail_unless (segment.flags == 0);
fail_unless (segment.start == 0);
fail_unless (segment.stop == 200);
fail_unless (segment.time == 400);
/* previous segment lasted 200, rate of 2.0 was already applied */
fail_unless (segment.accum == 400);
fail_unless (segment.last_stop == 0);
fail_unless (segment.duration == -1);
/* invalid time gives invalid result */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
/* we count backwards from 400 */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 0);
fail_unless (result == 400);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 100);
fail_unless (result == 200);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 150);
fail_unless (result == 100);
/* outside of the segment */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 200);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 300);
fail_unless (result == -1);
/***********************************************************
* Normal segment rate 1.0, applied rate -2.0
* This means the timestamps represents a stream at twice the
* reverse rate, start time cannot compensate the complete
* duration of the segment so we stop at 0
************************************************************/
gst_segment_set_newsegment_full (&segment, FALSE, 1.0, -2.0,
GST_FORMAT_TIME, 0, 200, 200);
fail_unless (segment.rate == 1.0);
fail_unless (segment.applied_rate == -2.0);
fail_unless (segment.format == GST_FORMAT_TIME);
fail_unless (segment.flags == 0);
fail_unless (segment.start == 0);
fail_unless (segment.stop == 200);
fail_unless (segment.time == 200);
fail_unless (segment.accum == 600);
fail_unless (segment.last_stop == 0);
fail_unless (segment.duration == -1);
/* invalid time gives invalid result */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
/* we count backwards from 200 */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 0);
fail_unless (result == 200);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 100);
fail_unless (result == 0);
/* clamp at 0 */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 150);
fail_unless (result == 0);
/* outside of the segment */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 200);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 300);
fail_unless (result == -1);
}
GST_END_TEST;
/* mess with the segment structure in the time format */
GST_START_TEST (segment_newsegment_streamtime_applied_rate_rate)
{
GstSegment segment;
gint64 result;
gst_segment_init (&segment, GST_FORMAT_TIME);
/***********************************************************
* Segment rate 2.0, applied rate 2.0
* this means we have a double speed stream that we should
* speed up by a factor of 2.0 some more. the resulting
* stream will be played at four times the speed.
************************************************************/
gst_segment_set_newsegment_full (&segment, FALSE, 2.0, 2.0,
GST_FORMAT_TIME, 0, 200, 0);
fail_unless (segment.rate == 2.0);
fail_unless (segment.applied_rate == 2.0);
fail_unless (segment.format == GST_FORMAT_TIME);
fail_unless (segment.flags == 0);
fail_unless (segment.start == 0);
fail_unless (segment.stop == 200);
fail_unless (segment.time == 0);
fail_unless (segment.accum == 0);
fail_unless (segment.last_stop == 0);
fail_unless (segment.duration == -1);
/* invalid time gives invalid result */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
/* only applied rate affects our calculation of the stream time */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 0);
fail_unless (result == 0);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 100);
fail_unless (result == 200);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 150);
fail_unless (result == 300);
/* outside of the segment */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 200);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 300);
fail_unless (result == -1);
/***********************************************************
* Segment rate 2.0, applied rate -1.0
* this means we have a reverse stream that we should
* speed up by a factor of 2.0
************************************************************/
gst_segment_set_newsegment_full (&segment, FALSE, 2.0, -1.0,
GST_FORMAT_TIME, 0, 200, 200);
fail_unless (segment.rate == 2.0);
fail_unless (segment.applied_rate == -1.0);
fail_unless (segment.format == GST_FORMAT_TIME);
fail_unless (segment.flags == 0);
fail_unless (segment.start == 0);
fail_unless (segment.stop == 200);
fail_unless (segment.time == 200);
/* previous segment lasted 100 */
fail_unless (segment.accum == 100);
fail_unless (segment.last_stop == 0);
fail_unless (segment.duration == -1);
/* invalid time gives invalid result */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
/* only applied rate affects our calculation of the stream time */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 0);
fail_unless (result == 200);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 100);
fail_unless (result == 100);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 150);
fail_unless (result == 50);
/* outside of the segment */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 200);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 300);
fail_unless (result == -1);
/***********************************************************
* Segment rate -1.0, applied rate -1.0
* this means we have a reverse stream that we should
* reverse to get the normal stream again.
************************************************************/
gst_segment_set_newsegment_full (&segment, FALSE, -1.0, -1.0,
GST_FORMAT_TIME, 0, 200, 200);
fail_unless (segment.rate == -1.0);
fail_unless (segment.applied_rate == -1.0);
fail_unless (segment.format == GST_FORMAT_TIME);
fail_unless (segment.flags == 0);
fail_unless (segment.start == 0);
fail_unless (segment.stop == 200);
fail_unless (segment.time == 200);
/* accumulated 100 of previous segment to make 200 */
fail_unless (segment.accum == 200);
fail_unless (segment.last_stop == 0);
fail_unless (segment.duration == -1);
/* invalid time gives invalid result */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
/* only applied rate affects our calculation of the stream time */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 0);
fail_unless (result == 200);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 100);
fail_unless (result == 100);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 150);
fail_unless (result == 50);
/* outside of the segment */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 200);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 300);
fail_unless (result == -1);
/***********************************************************
* Segment rate -1.0, applied rate -1.0
* this means we have a reverse stream that we should
* reverse to get the normal stream again.
************************************************************/
gst_segment_set_newsegment_full (&segment, FALSE, -1.0, 2.0,
GST_FORMAT_TIME, 0, 200, 0);
fail_unless (segment.rate == -1.0);
fail_unless (segment.applied_rate == 2.0);
fail_unless (segment.format == GST_FORMAT_TIME);
fail_unless (segment.flags == 0);
fail_unless (segment.start == 0);
fail_unless (segment.stop == 200);
fail_unless (segment.time == 0);
fail_unless (segment.accum == 400);
fail_unless (segment.last_stop == 0);
fail_unless (segment.duration == -1);
/* invalid time gives invalid result */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
/* only applied rate affects our calculation of the stream time */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 0);
fail_unless (result == 0);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 100);
fail_unless (result == 200);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 150);
fail_unless (result == 300);
/* outside of the segment */
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 200);
fail_unless (result == -1);
result = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME, 300);
fail_unless (result == -1);
}
GST_END_TEST;
/* mess with the segment structure in the time format */
GST_START_TEST (segment_newsegment_runningtime)
{
GstSegment segment;
gint64 result;
gst_segment_init (&segment, GST_FORMAT_TIME);
/***************************
* Normal segment
***************************/
gst_segment_set_newsegment_full (&segment, FALSE, 1.0, 1.0,
GST_FORMAT_TIME, 0, 200, 0);
fail_unless (segment.rate == 1.0);
fail_unless (segment.applied_rate == 1.0);
fail_unless (segment.format == GST_FORMAT_TIME);
fail_unless (segment.flags == 0);
fail_unless (segment.start == 0);
fail_unless (segment.stop == 200);
fail_unless (segment.time == 0);
fail_unless (segment.accum == 0);
fail_unless (segment.last_stop == 0);
fail_unless (segment.duration == -1);
/* invalid time gives invalid result */
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 0);
fail_unless (result == 0);
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 100);
fail_unless (result == 100);
/* outside of the segment */
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 200);
fail_unless (result == -1);
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 300);
fail_unless (result == -1);
/***********************************************************
* time shifted by 500, check if accumulation worked.
* Rate convert to twice the speed which means scaling down
* all positions by 2.0 in this segment.
* Then time argument is not used at all here.
***********************************************************/
gst_segment_set_newsegment_full (&segment, FALSE, 2.0, 1.0,
GST_FORMAT_TIME, 0, 200, 500);
/* normal speed gives elapsed of 200 */
fail_unless (segment.accum == 200);
/* invalid time gives invalid result */
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 0);
fail_unless (result == 200);
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 100);
fail_unless (result == 250);
/* outside of the segment */
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 500);
fail_unless (result == -1);
/********************************************
* time offset by 500
* applied rate is not used for running time
********************************************/
gst_segment_set_newsegment_full (&segment, FALSE, 1.0, 2.0,
GST_FORMAT_TIME, 500, 700, 0);
/* previous segment played at double speed gives elapsed time of
* 100 added to previous accum of 200 gives 300. */
fail_unless (segment.accum == 300);
/* invalid time gives invalid result */
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
/* before segment is invalid */
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 400);
fail_unless (result == -1);
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 500);
fail_unless (result == 300);
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 600);
fail_unless (result == 400);
/* outside of the segment */
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 700);
fail_unless (result == -1);
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 800);
fail_unless (result == -1);
/**********************************************************
* time offset by 500, shifted by 200
* Negative rate makes the running time go backwards
* relative to the segment stop position. again time
* is ignored.
**********************************************************/
gst_segment_set_newsegment_full (&segment, FALSE, -1.0, 1.0,
GST_FORMAT_TIME, 500, 700, 200);
fail_unless (segment.accum == 500);
/* invalid time gives invalid result */
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
/* before segment is invalid */
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 400);
fail_unless (result == -1);
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 500);
fail_unless (result == 700);
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 600);
fail_unless (result == 600);
/* outside of the segment */
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 700);
fail_unless (result == -1);
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 800);
fail_unless (result == -1);
/**********************************************************
* time offset by 500, shifted by 200
* Negative rate makes the running time go backwards at
* twice speed relative to the segment stop position. again
* time is ignored.
**********************************************************/
gst_segment_set_newsegment_full (&segment, FALSE, -2.0, -2.0,
GST_FORMAT_TIME, 500, 700, 200);
fail_unless (segment.accum == 700);
/* invalid time gives invalid result */
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, -1);
fail_unless (result == -1);
/* before segment is invalid */
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 400);
fail_unless (result == -1);
/* total scaled segment time is 100, accum is 700, so we get 800 */
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 500);
fail_unless (result == 800);
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 600);
fail_unless (result == 750);
/* outside of the segment */
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 700);
fail_unless (result == -1);
result = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, 800);
fail_unless (result == -1);
/* see if negative rate closed segment correctly */
gst_segment_set_newsegment_full (&segment, FALSE, -2.0, -1.0,
GST_FORMAT_TIME, 500, 700, 200);
/* previous segment lasted 100, and was at 700 so we should get 800 */
fail_unless (segment.accum == 800);
}
GST_END_TEST;
Suite *
gstsegments_suite (void)
{
@ -511,6 +1284,12 @@ gstsegments_suite (void)
tcase_add_test (tc_chain, segment_seek_size);
tcase_add_test (tc_chain, segment_newsegment_open);
tcase_add_test (tc_chain, segment_newsegment_closed);
tcase_add_test (tc_chain, segment_newsegment_streamtime);
tcase_add_test (tc_chain, segment_newsegment_streamtime_rate);
tcase_add_test (tc_chain, segment_newsegment_streamtime_applied_rate);
tcase_add_test (tc_chain, segment_newsegment_streamtime_applied_rate_rate);
tcase_add_test (tc_chain, segment_newsegment_runningtime);
return s;
}