docs/design/part-TODO.txt: Remove item from the todo list because it was fixed with the latency state change rewrites.

Original commit message from CVS:
* docs/design/part-TODO.txt:
Remove item from the todo list because it was fixed with the latency
state change rewrites.
* docs/design/part-seeking.txt:
* docs/design/part-segments.txt:
Update some docs.
* gst/gstevent.c: (gst_event_new_new_segment_full),
(gst_event_parse_new_segment_full), (gst_event_new_buffer_size),
(gst_event_parse_buffer_size), (gst_event_new_qos),
(gst_event_parse_qos), (gst_event_new_seek),
(gst_event_parse_seek), (gst_event_new_latency),
(gst_event_parse_latency):
Use quarks to construct and parse events.
* gst/gstquark.c: (_priv_gst_quarks_initialize):
* gst/gstquark.h:
Add some more quarks to the table.
Emit a warning when the quark tables are not in sync.
* tests/check/gst/gstbus.c: (GST_START_TEST):
Add an assert.
This commit is contained in:
Wim Taymans 2008-10-13 17:19:25 +00:00
parent 35a2a49c00
commit 97ec47cabb
8 changed files with 224 additions and 89 deletions

View file

@ -1,3 +1,29 @@
2008-10-13 Wim Taymans <wim.taymans@collabora.co.uk>
* docs/design/part-TODO.txt:
Remove item from the todo list because it was fixed with the latency
state change rewrites.
* docs/design/part-seeking.txt:
* docs/design/part-segments.txt:
Update some docs.
* gst/gstevent.c: (gst_event_new_new_segment_full),
(gst_event_parse_new_segment_full), (gst_event_new_buffer_size),
(gst_event_parse_buffer_size), (gst_event_new_qos),
(gst_event_parse_qos), (gst_event_new_seek),
(gst_event_parse_seek), (gst_event_new_latency),
(gst_event_parse_latency):
Use quarks to construct and parse events.
* gst/gstquark.c: (_priv_gst_quarks_initialize):
* gst/gstquark.h:
Add some more quarks to the table.
Emit a warning when the quark tables are not in sync.
* tests/check/gst/gstbus.c: (GST_START_TEST):
Add an assert.
2008-10-13 Stefan Kost <ensonic@users.sf.net>
* plugins/elements/Makefile.am:

View file

@ -59,12 +59,6 @@ API/ABI
- Make serialisation of structures more consistent, readable and nicer code-wise.
- When a seekable live source does a flushing seek, it needs a new base_time to
timestamp new data. The pipeline however does not know that there is a live
source in the pipeline and thus does not select a new base_time automatically.
There needs to be a mechanism for a live source to request a new base_time or
pipeline restart.
- pad block has several issues:
* can't block on selected things, like push, pull, pad_alloc, events, ...
* can't check why the block happened. We should also be able to get the item/

View file

@ -2,46 +2,63 @@ Seeking
-------
Seeking in GStreamer means configuring the pipeline for playback of the
media between a certain start and stop time, called a segment.
media between a certain start and stop time, called a segment. By default a
pipeline will play from position 0 to the total duration of the media at a rate
of 1.0.
Different kinds of seeking exist:
A seek is performed by sending a seek event to the sink elements of a
pipeline. Sending the seek event to a bin will by default forward
the event to all sinks in the bin.
- immediate seeking with low latency (FLUSH seek)
- seeking without flush, playback will start from the new
position after all the queues are emptied with old data.
- segment seeking with and without FLUSH, this can be used to
implement seamless looping or NLE functionality.
When performing a seek, the start and stop values of the segment can be
specified as absoulte positions or relative to the currently configured
playback segment. Note that it is not possible to seek relative to the current
playback position.
Feedback of the seek operation can be immediatly using the GST_SEEK_FLAG_FLUSH
flag. With this flag, all pending data in the pipeline is discarded and playback
starts from the new position immediatly.
When the FLUSH flag is not set, the seek will be queued and executed as
soon as possible, which might be after all queues are emptied.
Seeking can be performed in different formats such as time, frames
or samples.
Seeking can be performed to an absolute position or relative to the
currently configured segment.
The seeking can be performed to a nearby key unit or to the exact
(estimated) unit in the media (GST_SEEK_FLAG_KEY_UNIT).
For seeking to work reliably, all plugins in the pipeline need to follow
the well-defined rules in this document.
The seeking can be performed by using an estimated target position or in an
accurate way (GST_SEEK_FLAG_ACCURATE). For some formats this can result in
having to scan the complete file in order to accurately find the target unit.
Non segment seeking will make the pipeline emit EOS when the configured
segment has been played.
Segment seeking will not emit an EOS at the end of the range but will
post a SEGMENT_DONE message on the bus. This message is posted by the
earliest element in the pipeline, typically a demuxer. After receiving
the message, the application can reconnect the pipeline or issue other
seek events in the pipeline. Since the message is posted as early as
possible in the pipeline, the application has some time to issue a new
seek to make the transition seamless. Typically the allowed delay is
defined by the buffer sizes of the sinks as well as the size of any
queues in the pipeline.
Segment seeking (using the GST_SEEK_FLAG_SEGMENT) will not emit an EOS at
the end of the playback segment but will post a SEGMENT_DONE message on the
bus. This message is posted by the element driving the playback in the
pipeline, typically a demuxer. After receiving the message, the application
can reconnect the pipeline or issue other seek events in the pipeline.
Since the message is posted as early as possible in the pipeline, the
application has some time to issue a new seek to make the transition seamless.
Typically the allowed delay is defined by the buffer sizes of the sinks as well
as the size of any queues 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.
When performing a seek with a playback rate different from 1.0, the
GST_SEEK_FLAG_SKIP flag can be used to instruct decoders and demuxers that they
are allowed to skip decoding. This can be useful when resource consumption is
more important than accurately producing all frames.
Generating seeking events
-------------------------
A seek event is created with gst_event_new_seek ().

View file

@ -38,6 +38,13 @@ Use case: FLUSHING seek
When doing a seek in this pipeline for a segment 1 to 5 seconds, avidemux
will perform the seek.
Avidemux starts by sending a FLUSH_START event downstream and upstream. This
will cause its streaming task to PAUSED because _pad_pull_range() and
_pad_push() will return WRONG_STATE. It then waits for the STREAM_LOCK,
which will be unlocked when the streaming task pauses. At this point no
streaming is happening anymore in the pipeline and a FLUSH_STOP is sent
upstream and downstream.
When avidemux starts playback of the segment from second 1 to 5, it pushes
out a newsegment with 1 and 5 as start and stop times. The stream_time in
the newsegment is also 1 as this is the position we seek to.
@ -57,18 +64,21 @@ Use case: FLUSHING seek
When it reaches timestamp 5, it does not decode and push frames anymore.
The video sink receives a frame of timestamp 1. It takes the start value of
the previous newsegment and aplies the folowing formula:
the previous newsegment and aplies the folowing (simplified) formula:
render_time = BUFFER_TIMESTAMP - segment_start + element->base_time
It then syncs against the clock with this render_time. Not that
It then syncs against the clock with this render_time. Note that
BUFFER_TIMESTAMP is always >= segment_start or else it would fall outside of
the configure segment.
Videosink reports its current position as:
Videosink reports its current position as (simplified):
current_position = clock_time - element->base_time + segment_time
See part-synchronisation.txt for a more detailed and accurate explanation of
synchronisation and position reporting.
Since after a flushing seek the stream_time is reset to 0, the new buffer
will be rendered immediatly after the seek and the current_position will be
the stream_time of the seek that was performed.

View file

@ -83,6 +83,7 @@
#include "gstevent.h"
#include "gstenumtypes.h"
#include "gstutils.h"
#include "gstquark.h"
static void gst_event_init (GTypeInstance * instance, gpointer g_class);
static void gst_event_class_init (gpointer g_class, gpointer class_data);
@ -545,6 +546,9 @@ gst_event_new_new_segment_full (gboolean update, gdouble rate,
gdouble applied_rate, GstFormat format, gint64 start, gint64 stop,
gint64 position)
{
GstEvent *event;
GstStructure *structure;
g_return_val_if_fail (rate != 0.0, NULL);
g_return_val_if_fail (applied_rate != 0.0, NULL);
@ -568,15 +572,18 @@ gst_event_new_new_segment_full (gboolean update, gdouble rate,
if (stop != -1)
g_return_val_if_fail (start <= stop, NULL);
return gst_event_new_custom (GST_EVENT_NEWSEGMENT,
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,
"position", G_TYPE_INT64, position, NULL));
structure = gst_structure_empty_new ("GstEventNewsegment");
gst_structure_id_set (structure,
GST_QUARK (UPDATE), G_TYPE_BOOLEAN, update,
GST_QUARK (RATE), G_TYPE_DOUBLE, rate,
GST_QUARK (APPLIED_RATE), G_TYPE_DOUBLE, applied_rate,
GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
GST_QUARK (START), G_TYPE_INT64, start,
GST_QUARK (STOP), G_TYPE_INT64, stop,
GST_QUARK (POSITION), G_TYPE_INT64, position, NULL);
event = gst_event_new_custom (GST_EVENT_NEWSEGMENT, structure);
return event;
}
/**
@ -609,22 +616,32 @@ gst_event_parse_new_segment_full (GstEvent * event, gboolean * update,
structure = gst_event_get_structure (event);
if (G_LIKELY (update))
*update =
g_value_get_boolean (gst_structure_get_value (structure, "update"));
g_value_get_boolean (gst_structure_id_get_value (structure,
GST_QUARK (UPDATE)));
if (G_LIKELY (rate))
*rate = g_value_get_double (gst_structure_get_value (structure, "rate"));
*rate =
g_value_get_double (gst_structure_id_get_value (structure,
GST_QUARK (RATE)));
if (G_LIKELY (applied_rate))
*applied_rate =
g_value_get_double (gst_structure_get_value (structure,
"applied_rate"));
g_value_get_double (gst_structure_id_get_value (structure,
GST_QUARK (APPLIED_RATE)));
if (G_LIKELY (format))
*format = g_value_get_enum (gst_structure_get_value (structure, "format"));
*format =
g_value_get_enum (gst_structure_id_get_value (structure,
GST_QUARK (FORMAT)));
if (G_LIKELY (start))
*start = g_value_get_int64 (gst_structure_get_value (structure, "start"));
*start =
g_value_get_int64 (gst_structure_id_get_value (structure,
GST_QUARK (START)));
if (G_LIKELY (stop))
*stop = g_value_get_int64 (gst_structure_get_value (structure, "stop"));
*stop =
g_value_get_int64 (gst_structure_id_get_value (structure,
GST_QUARK (STOP)));
if (G_LIKELY (position))
*position =
g_value_get_int64 (gst_structure_get_value (structure, "position"));
g_value_get_int64 (gst_structure_id_get_value (structure,
GST_QUARK (POSITION)));
}
/**
@ -679,17 +696,23 @@ GstEvent *
gst_event_new_buffer_size (GstFormat format, gint64 minsize,
gint64 maxsize, gboolean async)
{
GstEvent *event;
GstStructure *structure;
GST_CAT_INFO (GST_CAT_EVENT,
"creating buffersize format %s, minsize %" G_GINT64_FORMAT
", maxsize %" G_GINT64_FORMAT ", async %d", gst_format_get_name (format),
minsize, maxsize, async);
return gst_event_new_custom (GST_EVENT_BUFFERSIZE,
gst_structure_new ("GstEventBufferSize",
"format", GST_TYPE_FORMAT, format,
"minsize", G_TYPE_INT64, minsize,
"maxsize", G_TYPE_INT64, maxsize,
"async", G_TYPE_BOOLEAN, async, NULL));
structure = gst_structure_empty_new ("GstEventBufferSize");
gst_structure_id_set (structure,
GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
GST_QUARK (MINSIZE), G_TYPE_INT64, minsize,
GST_QUARK (MAXSIZE), G_TYPE_INT64, maxsize,
GST_QUARK (ASYNC), G_TYPE_BOOLEAN, async, NULL);
event = gst_event_new_custom (GST_EVENT_BUFFERSIZE, structure);
return event;
}
/**
@ -713,15 +736,21 @@ gst_event_parse_buffer_size (GstEvent * event, GstFormat * format,
structure = gst_event_get_structure (event);
if (format)
*format = g_value_get_enum (gst_structure_get_value (structure, "format"));
*format =
g_value_get_enum (gst_structure_id_get_value (structure,
GST_QUARK (FORMAT)));
if (minsize)
*minsize =
g_value_get_int64 (gst_structure_get_value (structure, "minsize"));
g_value_get_int64 (gst_structure_id_get_value (structure,
GST_QUARK (MINSIZE)));
if (maxsize)
*maxsize =
g_value_get_int64 (gst_structure_get_value (structure, "maxsize"));
g_value_get_int64 (gst_structure_id_get_value (structure,
GST_QUARK (MAXSIZE)));
if (async)
*async = g_value_get_boolean (gst_structure_get_value (structure, "async"));
*async =
g_value_get_boolean (gst_structure_id_get_value (structure,
GST_QUARK (ASYNC)));
}
/**
@ -770,6 +799,9 @@ GstEvent *
gst_event_new_qos (gdouble proportion, GstClockTimeDiff diff,
GstClockTime timestamp)
{
GstEvent *event;
GstStructure *structure;
/* diff must be positive or timestamp + diff must be positive */
g_return_val_if_fail (diff >= 0 || -diff <= timestamp, NULL);
@ -778,11 +810,14 @@ gst_event_new_qos (gdouble proportion, GstClockTimeDiff diff,
", timestamp %" GST_TIME_FORMAT, proportion,
diff, GST_TIME_ARGS (timestamp));
return gst_event_new_custom (GST_EVENT_QOS,
gst_structure_new ("GstEventQOS",
"proportion", G_TYPE_DOUBLE, proportion,
"diff", G_TYPE_INT64, diff,
"timestamp", G_TYPE_UINT64, timestamp, NULL));
structure = gst_structure_empty_new ("GstEventQOS");
gst_structure_id_set (structure,
GST_QUARK (PROPORTION), G_TYPE_DOUBLE, proportion,
GST_QUARK (DIFF), G_TYPE_INT64, diff,
GST_QUARK (TIMESTAMP), G_TYPE_UINT64, timestamp, NULL);
event = gst_event_new_custom (GST_EVENT_QOS, structure);
return event;
}
/**
@ -807,12 +842,16 @@ gst_event_parse_qos (GstEvent * event, gdouble * proportion,
structure = gst_event_get_structure (event);
if (proportion)
*proportion =
g_value_get_double (gst_structure_get_value (structure, "proportion"));
g_value_get_double (gst_structure_id_get_value (structure,
GST_QUARK (PROPORTION)));
if (diff)
*diff = g_value_get_int64 (gst_structure_get_value (structure, "diff"));
*diff =
g_value_get_int64 (gst_structure_id_get_value (structure,
GST_QUARK (DIFF)));
if (timestamp)
*timestamp =
g_value_get_uint64 (gst_structure_get_value (structure, "timestamp"));
g_value_get_uint64 (gst_structure_id_get_value (structure,
GST_QUARK (TIMESTAMP)));
}
/**
@ -841,9 +880,9 @@ gst_event_parse_qos (GstEvent * event, gdouble * proportion,
* configured playback segment can be queried with #GST_QUERY_SEGMENT.
*
* @start_type and @stop_type specify how to adjust the currently configured
* start and stop fields in @segment. Adjustments can be made relative or
* absolute to the last configured values. A type of #GST_SEEK_TYPE_NONE means
* that the position should not be updated.
* start and stop fields in playback segment. Adjustments can be made relative
* or absolute to the last configured values. A type of #GST_SEEK_TYPE_NONE
* means that the position should not be updated.
*
* When the rate is positive and @start has been updated, playback will start
* from the newly configured start position.
@ -855,7 +894,7 @@ gst_event_parse_qos (GstEvent * event, gdouble * proportion,
* It is not possible to seek relative to the current playback position, to do
* this, PAUSE the pipeline, query the current playback position with
* #GST_QUERY_POSITION and update the playback segment current position with a
* #GST_SEEK_TYPE_SET to the desired position.
* #GST_SEEK_TYPE_SET to the desired position.
*
* Returns: A new seek event.
*/
@ -863,6 +902,9 @@ GstEvent *
gst_event_new_seek (gdouble rate, GstFormat format, GstSeekFlags flags,
GstSeekType start_type, gint64 start, GstSeekType stop_type, gint64 stop)
{
GstEvent *event;
GstStructure *structure;
g_return_val_if_fail (rate != 0.0, NULL);
if (format == GST_FORMAT_TIME) {
@ -881,14 +923,18 @@ gst_event_new_seek (gdouble rate, GstFormat format, GstSeekFlags flags,
stop);
}
return gst_event_new_custom (GST_EVENT_SEEK,
gst_structure_new ("GstEventSeek", "rate", G_TYPE_DOUBLE, rate,
"format", GST_TYPE_FORMAT, format,
"flags", GST_TYPE_SEEK_FLAGS, flags,
"cur_type", GST_TYPE_SEEK_TYPE, start_type,
"cur", G_TYPE_INT64, start,
"stop_type", GST_TYPE_SEEK_TYPE, stop_type,
"stop", G_TYPE_INT64, stop, NULL));
structure = gst_structure_empty_new ("GstEventSeek");
gst_structure_id_set (structure,
GST_QUARK (RATE), G_TYPE_DOUBLE, rate,
GST_QUARK (FORMAT), GST_TYPE_FORMAT, format,
GST_QUARK (FLAGS), GST_TYPE_SEEK_FLAGS, flags,
GST_QUARK (CUR_TYPE), GST_TYPE_SEEK_TYPE, start_type,
GST_QUARK (CUR), G_TYPE_INT64, start,
GST_QUARK (STOP_TYPE), GST_TYPE_SEEK_TYPE, stop_type,
GST_QUARK (STOP), G_TYPE_INT64, stop, NULL);
event = gst_event_new_custom (GST_EVENT_SEEK, structure);
return event;
}
/**
@ -916,21 +962,33 @@ gst_event_parse_seek (GstEvent * event, gdouble * rate,
structure = gst_event_get_structure (event);
if (rate)
*rate = g_value_get_double (gst_structure_get_value (structure, "rate"));
*rate =
g_value_get_double (gst_structure_id_get_value (structure,
GST_QUARK (RATE)));
if (format)
*format = g_value_get_enum (gst_structure_get_value (structure, "format"));
*format =
g_value_get_enum (gst_structure_id_get_value (structure,
GST_QUARK (FORMAT)));
if (flags)
*flags = g_value_get_flags (gst_structure_get_value (structure, "flags"));
*flags =
g_value_get_flags (gst_structure_id_get_value (structure,
GST_QUARK (FLAGS)));
if (start_type)
*start_type =
g_value_get_enum (gst_structure_get_value (structure, "cur_type"));
g_value_get_enum (gst_structure_id_get_value (structure,
GST_QUARK (CUR_TYPE)));
if (start)
*start = g_value_get_int64 (gst_structure_get_value (structure, "cur"));
*start =
g_value_get_int64 (gst_structure_id_get_value (structure,
GST_QUARK (CUR)));
if (stop_type)
*stop_type =
g_value_get_enum (gst_structure_get_value (structure, "stop_type"));
g_value_get_enum (gst_structure_id_get_value (structure,
GST_QUARK (STOP_TYPE)));
if (stop)
*stop = g_value_get_int64 (gst_structure_get_value (structure, "stop"));
*stop =
g_value_get_int64 (gst_structure_id_get_value (structure,
GST_QUARK (STOP)));
}
/**
@ -968,12 +1026,18 @@ gst_event_new_navigation (GstStructure * structure)
GstEvent *
gst_event_new_latency (GstClockTime latency)
{
GstEvent *event;
GstStructure *structure;
GST_CAT_INFO (GST_CAT_EVENT,
"creating latency event %" GST_TIME_FORMAT, GST_TIME_ARGS (latency));
return gst_event_new_custom (GST_EVENT_LATENCY,
gst_structure_new ("GstEventLatency",
"latency", G_TYPE_UINT64, latency, NULL));
structure = gst_structure_empty_new ("GstEventLatency");
gst_structure_id_set (structure,
GST_QUARK (LATENCY), G_TYPE_UINT64, latency, NULL);
event = gst_event_new_custom (GST_EVENT_LATENCY, structure);
return event;
}
/**
@ -996,5 +1060,6 @@ gst_event_parse_latency (GstEvent * event, GstClockTime * latency)
structure = gst_event_get_structure (event);
if (latency)
*latency =
g_value_get_uint64 (gst_structure_get_value (structure, "latency"));
g_value_get_uint64 (gst_structure_id_get_value (structure,
GST_QUARK (LATENCY)));
}

View file

@ -34,7 +34,10 @@ static const gchar *_quark_strings[] = {
"avg-in-rate", "avg-out-rate", "buffering-left",
"estimated-total", "old-state", "new-state", "pending-state",
"clock", "ready", "position", "new-base-time", "live", "min-latency",
"max-latency", "busy", "type", "owner"
"max-latency", "busy", "type", "owner", "update", "applied-rate",
"start", "stop", "minsize", "maxsize", "async", "proportion",
"diff", "timestamp", "flags", "cur-type", "cur", "stop-type",
"latency"
};
GQuark _priv_gst_quark_table[GST_QUARK_MAX];
@ -44,6 +47,10 @@ _priv_gst_quarks_initialize (void)
{
gint i;
if (G_N_ELEMENTS (_quark_strings) != GST_QUARK_MAX)
g_warning ("the quark table is not consistent! %ld != %d",
G_N_ELEMENTS (_quark_strings), GST_QUARK_MAX);
for (i = 0; i < GST_QUARK_MAX; i++) {
_priv_gst_quark_table[i] = g_quark_from_static_string (_quark_strings[i]);
}

View file

@ -62,8 +62,23 @@ typedef enum _GstQuarkId
GST_QUARK_BUSY = 33,
GST_QUARK_TYPE = 34,
GST_QUARK_OWNER = 35,
GST_QUARK_UPDATE = 36,
GST_QUARK_APPLIED_RATE = 37,
GST_QUARK_START = 38,
GST_QUARK_STOP = 39,
GST_QUARK_MINSIZE = 40,
GST_QUARK_MAXSIZE = 41,
GST_QUARK_ASYNC = 42,
GST_QUARK_PROPORTION = 43,
GST_QUARK_DIFF = 44,
GST_QUARK_TIMESTAMP = 45,
GST_QUARK_FLAGS = 46,
GST_QUARK_CUR_TYPE = 47,
GST_QUARK_CUR = 48,
GST_QUARK_STOP_TYPE = 49,
GST_QUARK_LATENCY = 50,
GST_QUARK_MAX = 36
GST_QUARK_MAX = 51
} GstQuarkId;
extern GQuark _priv_gst_quark_table[GST_QUARK_MAX];

View file

@ -165,6 +165,7 @@ GST_START_TEST (test_watch)
main_loop = g_main_loop_new (NULL, FALSE);
id = gst_bus_add_watch (test_bus, gst_bus_async_signal_func, NULL);
fail_if (id == 0);
g_signal_connect (test_bus, "message::eos", (GCallback) message_func_eos,
NULL);
g_signal_connect (test_bus, "message::application",