mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 11:41:09 +00:00
Added more docs.
Original commit message from CVS: * docs/design/part-clocks.txt: * docs/design/part-events.txt: * gst/elements/gstfakesrc.c: (gst_fakesrc_get_event_mask), (gst_fakesrc_event_handler), (gst_fakesrc_loop), (gst_fakesrc_activate): * gst/gstclock.c: (gst_clock_id_ref), (gst_clock_id_unref), (gst_clock_id_compare_func), (gst_clock_id_wait), (gst_clock_id_wait_async), (gst_clock_init), (gst_clock_adjust_unlocked), (gst_clock_get_time): * gst/gstevent.c: (gst_event_new_discontinuous_valist), (gst_event_discont_get_value), (gst_event_new_segment_seek): * gst/gstevent.h: * gst/gstpipeline.c: (gst_pipeline_init), (is_eos), (pipeline_bus_handler), (gst_pipeline_change_state): * gst/gstpipeline.h: * gst/gstsystemclock.c: (gst_system_clock_init), (gst_system_clock_async_thread), (gst_system_clock_id_wait_unlocked), (gst_system_clock_id_wait_async), (gst_system_clock_id_unschedule): * libs/gst/dataprotocol/dataprotocol.c: (gst_dp_packet_from_event), (gst_dp_event_from_packet): * testsuite/clock/Makefile.am: * testsuite/clock/clock4.c: (gst_clock_debug), (ok_callback), (error_callback), (main): Added more docs. Remove more silly events. Implement periodic clock notifications. Add async testsuite.
This commit is contained in:
parent
98e3377631
commit
484d9a3224
16 changed files with 407 additions and 70 deletions
31
ChangeLog
31
ChangeLog
|
@ -1,3 +1,34 @@
|
||||||
|
2005-02-11 Wim Taymans <wim@fluendo.com>
|
||||||
|
|
||||||
|
* docs/design/part-clocks.txt:
|
||||||
|
* docs/design/part-events.txt:
|
||||||
|
* gst/elements/gstfakesrc.c: (gst_fakesrc_get_event_mask),
|
||||||
|
(gst_fakesrc_event_handler), (gst_fakesrc_loop),
|
||||||
|
(gst_fakesrc_activate):
|
||||||
|
* gst/gstclock.c: (gst_clock_id_ref), (gst_clock_id_unref),
|
||||||
|
(gst_clock_id_compare_func), (gst_clock_id_wait),
|
||||||
|
(gst_clock_id_wait_async), (gst_clock_init),
|
||||||
|
(gst_clock_adjust_unlocked), (gst_clock_get_time):
|
||||||
|
* gst/gstevent.c: (gst_event_new_discontinuous_valist),
|
||||||
|
(gst_event_discont_get_value), (gst_event_new_segment_seek):
|
||||||
|
* gst/gstevent.h:
|
||||||
|
* gst/gstpipeline.c: (gst_pipeline_init), (is_eos),
|
||||||
|
(pipeline_bus_handler), (gst_pipeline_change_state):
|
||||||
|
* gst/gstpipeline.h:
|
||||||
|
* gst/gstsystemclock.c: (gst_system_clock_init),
|
||||||
|
(gst_system_clock_async_thread),
|
||||||
|
(gst_system_clock_id_wait_unlocked),
|
||||||
|
(gst_system_clock_id_wait_async), (gst_system_clock_id_unschedule):
|
||||||
|
* libs/gst/dataprotocol/dataprotocol.c: (gst_dp_packet_from_event),
|
||||||
|
(gst_dp_event_from_packet):
|
||||||
|
* testsuite/clock/Makefile.am:
|
||||||
|
* testsuite/clock/clock4.c: (gst_clock_debug), (ok_callback),
|
||||||
|
(error_callback), (main):
|
||||||
|
Added more docs.
|
||||||
|
Remove more silly events.
|
||||||
|
Implement periodic clock notifications.
|
||||||
|
Add async testsuite.
|
||||||
|
|
||||||
2005-02-11 Andy Wingo <wingo@pobox.com>
|
2005-02-11 Andy Wingo <wingo@pobox.com>
|
||||||
|
|
||||||
* tools/gst-inspect.c (print_field): Change prototype for
|
* tools/gst-inspect.c (print_field): Change prototype for
|
||||||
|
|
102
docs/design/part-clocks.txt
Normal file
102
docs/design/part-clocks.txt
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
Clocks
|
||||||
|
------
|
||||||
|
|
||||||
|
To synchronize the different elements, the GstPipeline is responsible for
|
||||||
|
selecting and distributing a global GstClock for all the elements in it.
|
||||||
|
|
||||||
|
This selection happens whenever an element is added or removed from the
|
||||||
|
pipeline. Whever the clock changes in a pipeline, a message is posted on
|
||||||
|
the bus signaling the new clock to the application.
|
||||||
|
|
||||||
|
The GstClock returns a monotonically increasing time with the method
|
||||||
|
_get_time(). Its accuracy and base time depends on the specific clock
|
||||||
|
implementation but time is always expessed in nanoseconds. Since the
|
||||||
|
baseline of the clock is undefined, the clock time returned is not
|
||||||
|
meaningfull in itself, what matters are the deltas between two clock
|
||||||
|
times.
|
||||||
|
The time reported by the clock is called the absolute time.
|
||||||
|
|
||||||
|
|
||||||
|
Time in GStreamer
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
The absolute time is used to calculate the stream time. The stream time
|
||||||
|
is defined as follows:
|
||||||
|
|
||||||
|
- If the pipeline is NULL/READY, the stream time is undefined.
|
||||||
|
- In PAUSED, the stream time remains at the time when it was last
|
||||||
|
PAUSED. When the stream is PAUSED for the first time, the stream time
|
||||||
|
is 0.
|
||||||
|
- In PLAYING, the stream time is the delta between the absolute time
|
||||||
|
and the base time. The base time is defined as the absolute time minus
|
||||||
|
the stream time at the time when the pipeline is set to PLAYING.
|
||||||
|
- after a seek, the stream time is set to 0 again.
|
||||||
|
|
||||||
|
|
||||||
|
Timestamps
|
||||||
|
----------
|
||||||
|
|
||||||
|
Timestamps on buffers are always expressed in stream time. This means that
|
||||||
|
all elements that require synchronizing to the clock need to be aware of
|
||||||
|
the clock base time in order to know the absolute time of the timestamp.
|
||||||
|
Converting a timestamp (in stream time) to absolute time is performed using
|
||||||
|
the following formula:
|
||||||
|
|
||||||
|
AT = BT + ST where: AT = absolute time
|
||||||
|
BT = base time
|
||||||
|
ST = stream time
|
||||||
|
|
||||||
|
The pipeline base time is propagated to all the element during the PAUSED
|
||||||
|
to PLAYING state change. All elements are therefore able to convert the
|
||||||
|
stream time to the absolute time. It is possible to specify an aditional
|
||||||
|
delay to the base time to compensate for the delay it takes to perform
|
||||||
|
the state change.
|
||||||
|
|
||||||
|
|
||||||
|
Clock features
|
||||||
|
--------------
|
||||||
|
|
||||||
|
The clock supports periodic and single shot clock notifications both
|
||||||
|
synchronous and asynchronous.
|
||||||
|
|
||||||
|
One first needs to create a GstClockID for the periodic or single shot
|
||||||
|
notification using _clock_new_single_shot_id() or _clock_new_periodic_id().
|
||||||
|
|
||||||
|
To perform a blocking wait for the specific time of the GstClockID use the
|
||||||
|
gst_clock_id_wait(). To receive a callback when the specific time is reached
|
||||||
|
in the clock use gst_clock_id_wait_async(). Both these calls can be interrupted
|
||||||
|
with the gst_clock_id_unschedule() call. If the blocking wait is unscheduled
|
||||||
|
a return value of GST_CLOCK_UNSCHEDULED is returned.
|
||||||
|
|
||||||
|
The async callbacks can happen from any thread, either provided by the
|
||||||
|
core or from a streaming thread. The application should be prepared for this.
|
||||||
|
|
||||||
|
A GstClockID that has been unscheduled cannot be used again for any wait
|
||||||
|
operation.
|
||||||
|
|
||||||
|
It is possible to perform a blocking wait on the same ID from multiple
|
||||||
|
threads. However, registering the same ID for multiple async notifications is
|
||||||
|
not possible, the callback will only be called once.
|
||||||
|
|
||||||
|
None of the wait operations unref the GstClockID, the application is
|
||||||
|
responsible for unreffing the ids itself.
|
||||||
|
|
||||||
|
These clock operations do not operate on the stream time, so the callbacks
|
||||||
|
will also occur when not in PLAYING state as if the clock just keeps on
|
||||||
|
running.
|
||||||
|
|
||||||
|
|
||||||
|
Clock implementations
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The GStreamer core provides a GstSystemClock based on the system time.
|
||||||
|
Asynchronous callbacks are scheduled from an internal thread.
|
||||||
|
|
||||||
|
Clock implementors are encouraged to subclass this systemclock as it
|
||||||
|
implements the async notification.
|
||||||
|
|
||||||
|
Subclasses can however override all of the important methods for sync and
|
||||||
|
async notifications to implement their own callback methods or blocking
|
||||||
|
wait operations.
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,9 @@ Different types of events exist to implement various functionalities.
|
||||||
EOS
|
EOS
|
||||||
---
|
---
|
||||||
|
|
||||||
The EOS event can only be send on a sinkpad. It is typically emited by the
|
The EOS event can only be sent on a sinkpad. It is typically emited by the
|
||||||
source element when it has finished sending data. This event is mainly send
|
source element when it has finished sending data. This event is mainly sent
|
||||||
in the streaming thread but can also be send from the application thread.
|
in the streaming thread but can also be sent from the application thread.
|
||||||
|
|
||||||
The downstream element should forward the EOS event to its downstream peer
|
The downstream element should forward the EOS event to its downstream peer
|
||||||
elements. This way the event will eventually reach the renderers which should
|
elements. This way the event will eventually reach the renderers which should
|
||||||
|
@ -58,7 +58,7 @@ goes to PLAYING.
|
||||||
FLUSH
|
FLUSH
|
||||||
-----
|
-----
|
||||||
|
|
||||||
A flush event is send both downstream and upstream to clear any pending data
|
A flush event is sent both downstream and upstream to clear any pending data
|
||||||
from the pipeline. This might be neede to make the graph more responsive
|
from the pipeline. This might be neede to make the graph more responsive
|
||||||
when the normal dataflow gets interrupted by for example a seek event.
|
when the normal dataflow gets interrupted by for example a seek event.
|
||||||
|
|
||||||
|
@ -90,10 +90,10 @@ unlocks and any pending buffers are cleared in the upstream elements.
|
||||||
DISCONTINUOUS
|
DISCONTINUOUS
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
A discont event is send downstream by an element to indicate that the following
|
A discont event is sent downstream by an element to indicate that the following
|
||||||
group of buffers start and end at the specified time.
|
group of buffers start and end at the specified time.
|
||||||
|
|
||||||
After a seek event for example, a discont event is send.
|
After a seek event for example, a discont event is sent.
|
||||||
|
|
||||||
|
|
||||||
SEEK
|
SEEK
|
||||||
|
@ -168,7 +168,7 @@ Navigation events travel downstream.
|
||||||
TAG
|
TAG
|
||||||
---
|
---
|
||||||
|
|
||||||
The tag event is send downstream when an element has discovered metadata
|
The tag event is sent downstream when an element has discovered metadata
|
||||||
tags in a media file. Encoders can use this event to adjust their tagging
|
tags in a media file. Encoders can use this event to adjust their tagging
|
||||||
system.
|
system.
|
||||||
|
|
||||||
|
|
|
@ -419,8 +419,7 @@ static const GstEventMask *
|
||||||
gst_fakesrc_get_event_mask (GstPad * pad)
|
gst_fakesrc_get_event_mask (GstPad * pad)
|
||||||
{
|
{
|
||||||
static const GstEventMask masks[] = {
|
static const GstEventMask masks[] = {
|
||||||
{GST_EVENT_SEEK, GST_SEEK_FLAG_FLUSH},
|
{GST_EVENT_SEEK, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT_LOOP},
|
||||||
{GST_EVENT_SEEK_SEGMENT, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT_LOOP},
|
|
||||||
{GST_EVENT_FLUSH, 0},
|
{GST_EVENT_FLUSH, 0},
|
||||||
{0, 0},
|
{0, 0},
|
||||||
};
|
};
|
||||||
|
@ -437,18 +436,12 @@ gst_fakesrc_event_handler (GstPad * pad, GstEvent * event)
|
||||||
|
|
||||||
switch (GST_EVENT_TYPE (event)) {
|
switch (GST_EVENT_TYPE (event)) {
|
||||||
case GST_EVENT_SEEK:
|
case GST_EVENT_SEEK:
|
||||||
src->buffer_count = GST_EVENT_SEEK_OFFSET (event);
|
|
||||||
|
|
||||||
if (!GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* else we do a flush too */
|
|
||||||
case GST_EVENT_SEEK_SEGMENT:
|
|
||||||
src->segment_start = GST_EVENT_SEEK_OFFSET (event);
|
src->segment_start = GST_EVENT_SEEK_OFFSET (event);
|
||||||
src->segment_end = GST_EVENT_SEEK_ENDOFFSET (event);
|
src->segment_end = GST_EVENT_SEEK_ENDOFFSET (event);
|
||||||
src->buffer_count = src->segment_start;
|
src->buffer_count = src->segment_start;
|
||||||
src->segment_loop =
|
src->segment_loop =
|
||||||
GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_SEGMENT_LOOP;
|
GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_SEGMENT_LOOP;
|
||||||
|
src->need_flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH;
|
||||||
break;
|
break;
|
||||||
case GST_EVENT_FLUSH:
|
case GST_EVENT_FLUSH:
|
||||||
src->need_flush = TRUE;
|
src->need_flush = TRUE;
|
||||||
|
@ -795,7 +788,7 @@ gst_fakesrc_loop (GstPad * pad)
|
||||||
|
|
||||||
if (src->buffer_count == src->segment_end) {
|
if (src->buffer_count == src->segment_end) {
|
||||||
if (src->segment_loop) {
|
if (src->segment_loop) {
|
||||||
gst_pad_push_event (pad, gst_event_new (GST_EVENT_SEGMENT_DONE));
|
//gst_pad_push_event (pad, gst_event_new (GST_EVENT_SEGMENT_DONE));
|
||||||
} else {
|
} else {
|
||||||
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
|
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
|
||||||
gst_task_pause (src->task);
|
gst_task_pause (src->task);
|
||||||
|
|
|
@ -211,7 +211,14 @@ gst_clock_id_compare_func (gconstpointer id1, gconstpointer id2)
|
||||||
entry1 = (GstClockEntry *) id1;
|
entry1 = (GstClockEntry *) id1;
|
||||||
entry2 = (GstClockEntry *) id2;
|
entry2 = (GstClockEntry *) id2;
|
||||||
|
|
||||||
return GST_CLOCK_ENTRY_TIME (entry1) - GST_CLOCK_ENTRY_TIME (entry2);
|
if (GST_CLOCK_ENTRY_TIME (entry1) > GST_CLOCK_ENTRY_TIME (entry2)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (GST_CLOCK_ENTRY_TIME (entry1) < GST_CLOCK_ENTRY_TIME (entry2)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry1 - entry2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -262,6 +269,9 @@ gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
|
||||||
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
|
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
|
||||||
goto invalid_time;
|
goto invalid_time;
|
||||||
|
|
||||||
|
if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED))
|
||||||
|
goto unscheduled;
|
||||||
|
|
||||||
clock = GST_CLOCK_ENTRY_CLOCK (entry);
|
clock = GST_CLOCK_ENTRY_CLOCK (entry);
|
||||||
cclass = GST_CLOCK_GET_CLASS (clock);
|
cclass = GST_CLOCK_GET_CLASS (clock);
|
||||||
|
|
||||||
|
@ -276,6 +286,9 @@ gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
|
||||||
|
|
||||||
*jitter = now - requested;
|
*jitter = now - requested;
|
||||||
}
|
}
|
||||||
|
if (entry->type == GST_CLOCK_ENTRY_PERIODIC) {
|
||||||
|
entry->time += entry->interval;
|
||||||
|
}
|
||||||
|
|
||||||
if (clock->stats) {
|
if (clock->stats) {
|
||||||
gst_clock_update_stats (clock);
|
gst_clock_update_stats (clock);
|
||||||
|
@ -291,6 +304,11 @@ invalid_time:
|
||||||
GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _BADTIME");
|
GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _BADTIME");
|
||||||
return GST_CLOCK_BADTIME;
|
return GST_CLOCK_BADTIME;
|
||||||
}
|
}
|
||||||
|
unscheduled:
|
||||||
|
{
|
||||||
|
GST_CAT_DEBUG (GST_CAT_CLOCK, "entry was unscheduled return _UNSCHEDULED");
|
||||||
|
return GST_CLOCK_UNSCHEDULED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -329,6 +347,9 @@ gst_clock_id_wait_async (GstClockID id,
|
||||||
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
|
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
|
||||||
goto invalid_time;
|
goto invalid_time;
|
||||||
|
|
||||||
|
if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED))
|
||||||
|
goto unscheduled;
|
||||||
|
|
||||||
cclass = GST_CLOCK_GET_CLASS (clock);
|
cclass = GST_CLOCK_GET_CLASS (clock);
|
||||||
|
|
||||||
if (cclass->wait_async) {
|
if (cclass->wait_async) {
|
||||||
|
@ -348,6 +369,11 @@ invalid_time:
|
||||||
GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _BADTIME");
|
GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _BADTIME");
|
||||||
return GST_CLOCK_BADTIME;
|
return GST_CLOCK_BADTIME;
|
||||||
}
|
}
|
||||||
|
unscheduled:
|
||||||
|
{
|
||||||
|
GST_CAT_DEBUG (GST_CAT_CLOCK, "entry was unscheduled return _UNSCHEDULED");
|
||||||
|
return GST_CLOCK_UNSCHEDULED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -356,6 +382,8 @@ invalid_time:
|
||||||
*
|
*
|
||||||
* Cancel an outstanding request with the given ID. This can either
|
* Cancel an outstanding request with the given ID. This can either
|
||||||
* be an outstanding async notification or a pending sync notification.
|
* be an outstanding async notification or a pending sync notification.
|
||||||
|
* After this call, the @id cannot be used anymore to receive sync or
|
||||||
|
* async notifications, you need to create a new GstClockID.
|
||||||
*
|
*
|
||||||
* MT safe.
|
* MT safe.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -354,7 +354,7 @@ gst_event_new_segment_seek (GstSeekType type, gint64 start, gint64 stop)
|
||||||
|
|
||||||
g_return_val_if_fail (start < stop, NULL);
|
g_return_val_if_fail (start < stop, NULL);
|
||||||
|
|
||||||
event = gst_event_new (GST_EVENT_SEEK_SEGMENT);
|
event = gst_event_new (GST_EVENT_SEEK);
|
||||||
|
|
||||||
GST_EVENT_SEEK_TYPE (event) = type;
|
GST_EVENT_SEEK_TYPE (event) = type;
|
||||||
GST_EVENT_SEEK_OFFSET (event) = start;
|
GST_EVENT_SEEK_OFFSET (event) = start;
|
||||||
|
|
|
@ -41,8 +41,6 @@ typedef enum {
|
||||||
GST_EVENT_DISCONTINUOUS = 3,
|
GST_EVENT_DISCONTINUOUS = 3,
|
||||||
GST_EVENT_QOS = 4,
|
GST_EVENT_QOS = 4,
|
||||||
GST_EVENT_SEEK = 5,
|
GST_EVENT_SEEK = 5,
|
||||||
GST_EVENT_SEEK_SEGMENT = 6,
|
|
||||||
GST_EVENT_SEGMENT_DONE = 7,
|
|
||||||
GST_EVENT_SIZE = 8,
|
GST_EVENT_SIZE = 8,
|
||||||
GST_EVENT_RATE = 9,
|
GST_EVENT_RATE = 9,
|
||||||
GST_EVENT_NAVIGATION = 10,
|
GST_EVENT_NAVIGATION = 10,
|
||||||
|
|
|
@ -134,6 +134,7 @@ gst_pipeline_init (GTypeInstance * instance, gpointer g_class)
|
||||||
gst_bus_set_sync_handler (bus,
|
gst_bus_set_sync_handler (bus,
|
||||||
(GstBusSyncHandler) pipeline_bus_handler, pipeline);
|
(GstBusSyncHandler) pipeline_bus_handler, pipeline);
|
||||||
pipeline->eosed = NULL;
|
pipeline->eosed = NULL;
|
||||||
|
pipeline->delay = 0;
|
||||||
/* we are our own manager */
|
/* we are our own manager */
|
||||||
GST_ELEMENT_MANAGER (pipeline) = pipeline;
|
GST_ELEMENT_MANAGER (pipeline) = pipeline;
|
||||||
gst_element_set_bus (GST_ELEMENT (pipeline), bus);
|
gst_element_set_bus (GST_ELEMENT (pipeline), bus);
|
||||||
|
@ -288,13 +289,17 @@ gst_pipeline_change_state (GstElement * element)
|
||||||
}
|
}
|
||||||
case GST_STATE_PAUSED_TO_PLAYING:
|
case GST_STATE_PAUSED_TO_PLAYING:
|
||||||
if (element->clock) {
|
if (element->clock) {
|
||||||
/* we set time slightly ahead because of context switches */
|
GstClockTime start_time = gst_clock_get_time (element->clock);
|
||||||
pipeline->start_time = gst_clock_get_time (element->clock); // + 10*GST_MSECOND;
|
|
||||||
element->base_time = pipeline->start_time - pipeline->stream_time;
|
element->base_time = start_time -
|
||||||
|
pipeline->stream_time + pipeline->delay;
|
||||||
|
GST_DEBUG ("stream_time=%" GST_TIME_FORMAT ", start_time=%"
|
||||||
|
GST_TIME_FORMAT, GST_TIME_ARGS (pipeline->stream_time),
|
||||||
|
GST_TIME_ARGS (start_time));
|
||||||
|
} else {
|
||||||
|
element->base_time = 0;
|
||||||
|
GST_DEBUG ("no clock, using base time of 0");
|
||||||
}
|
}
|
||||||
GST_DEBUG ("stream_time=%" GST_TIME_FORMAT ", start_time=%"
|
|
||||||
GST_TIME_FORMAT, GST_TIME_ARGS (pipeline->stream_time),
|
|
||||||
GST_TIME_ARGS (pipeline->start_time));
|
|
||||||
break;
|
break;
|
||||||
case GST_STATE_PLAYING_TO_PAUSED:
|
case GST_STATE_PLAYING_TO_PAUSED:
|
||||||
case GST_STATE_PAUSED_TO_READY:
|
case GST_STATE_PAUSED_TO_READY:
|
||||||
|
@ -315,9 +320,8 @@ gst_pipeline_change_state (GstElement * element)
|
||||||
pipeline->stream_time = gst_clock_get_time (element->clock) -
|
pipeline->stream_time = gst_clock_get_time (element->clock) -
|
||||||
element->base_time;
|
element->base_time;
|
||||||
}
|
}
|
||||||
GST_DEBUG ("stream_time=%" GST_TIME_FORMAT ", start_time=%"
|
GST_DEBUG ("stream_time=%" GST_TIME_FORMAT,
|
||||||
GST_TIME_FORMAT, GST_TIME_ARGS (pipeline->stream_time),
|
GST_TIME_ARGS (pipeline->stream_time));
|
||||||
GST_TIME_ARGS (pipeline->start_time));
|
|
||||||
break;
|
break;
|
||||||
case GST_STATE_PAUSED_TO_READY:
|
case GST_STATE_PAUSED_TO_READY:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -50,8 +50,8 @@ struct _GstPipeline {
|
||||||
|
|
||||||
/*< public >*/ /* with LOCK */
|
/*< public >*/ /* with LOCK */
|
||||||
GstClock *fixed_clock; /* fixed clock if any */
|
GstClock *fixed_clock; /* fixed clock if any */
|
||||||
GstClockTime start_time;
|
|
||||||
GstClockTime stream_time;
|
GstClockTime stream_time;
|
||||||
|
GstClockTime delay;
|
||||||
|
|
||||||
GList *eosed; /* list of elements that posted EOS */
|
GList *eosed; /* list of elements that posted EOS */
|
||||||
|
|
||||||
|
|
|
@ -244,6 +244,7 @@ gst_system_clock_async_thread (GstClock * clock)
|
||||||
goto next_entry;
|
goto next_entry;
|
||||||
case GST_CLOCK_OK:
|
case GST_CLOCK_OK:
|
||||||
case GST_CLOCK_EARLY:
|
case GST_CLOCK_EARLY:
|
||||||
|
{
|
||||||
/* entry timed out normally, fire the callback and move to the next
|
/* entry timed out normally, fire the callback and move to the next
|
||||||
* entry */
|
* entry */
|
||||||
GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unlocked", entry);
|
GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unlocked", entry);
|
||||||
|
@ -251,7 +252,18 @@ gst_system_clock_async_thread (GstClock * clock)
|
||||||
entry->func (clock, entry->time, (GstClockID) entry,
|
entry->func (clock, entry->time, (GstClockID) entry,
|
||||||
entry->user_data);
|
entry->user_data);
|
||||||
}
|
}
|
||||||
goto next_entry;
|
if (entry->type == GST_CLOCK_ENTRY_PERIODIC) {
|
||||||
|
/* adjust time now */
|
||||||
|
entry->time += entry->interval;
|
||||||
|
/* and resort the list now */
|
||||||
|
clock->entries =
|
||||||
|
g_list_sort (clock->entries, gst_clock_id_compare_func);
|
||||||
|
/* and restart */
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
goto next_entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
case GST_CLOCK_BUSY:
|
case GST_CLOCK_BUSY:
|
||||||
/* somebody unlocked the entry but is was not canceled, This means that
|
/* somebody unlocked the entry but is was not canceled, This means that
|
||||||
* either a new entry was added in front of the queue or some other entry
|
* either a new entry was added in front of the queue or some other entry
|
||||||
|
@ -313,6 +325,7 @@ gst_system_clock_id_wait_unlocked (GstClock * clock, GstClockEntry * entry)
|
||||||
GstClockTime real, current, target;
|
GstClockTime real, current, target;
|
||||||
GstClockTimeDiff diff;
|
GstClockTimeDiff diff;
|
||||||
|
|
||||||
|
/* need to call the overridden method */
|
||||||
real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock);
|
real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock);
|
||||||
target = GST_CLOCK_ENTRY_TIME (entry);
|
target = GST_CLOCK_ENTRY_TIME (entry);
|
||||||
|
|
||||||
|
@ -389,7 +402,7 @@ gst_system_clock_id_wait_async (GstClock * clock, GstClockEntry * entry)
|
||||||
|
|
||||||
/* only need to send the signal if the entry was added to the
|
/* only need to send the signal if the entry was added to the
|
||||||
* front, else the thread is just waiting for another entry and
|
* front, else the thread is just waiting for another entry and
|
||||||
* will discard this entry automatically. */
|
* will get to this entry automatically. */
|
||||||
if (clock->entries->data == entry) {
|
if (clock->entries->data == entry) {
|
||||||
GST_CAT_DEBUG (GST_CAT_CLOCK, "send signal");
|
GST_CAT_DEBUG (GST_CAT_CLOCK, "send signal");
|
||||||
GST_CLOCK_SIGNAL (clock);
|
GST_CLOCK_SIGNAL (clock);
|
||||||
|
|
|
@ -330,15 +330,6 @@ gst_dp_packet_from_event (const GstEvent * event, GstDPHeaderFlag flags,
|
||||||
*payload = NULL;
|
*payload = NULL;
|
||||||
break;
|
break;
|
||||||
case GST_EVENT_SEEK:
|
case GST_EVENT_SEEK:
|
||||||
pl_length = 4 + 8 + 4;
|
|
||||||
*payload = g_malloc0 (pl_length);
|
|
||||||
GST_WRITE_UINT32_BE (*payload, (guint32) GST_EVENT_SEEK_TYPE (event));
|
|
||||||
GST_WRITE_UINT64_BE (*payload + 4,
|
|
||||||
(guint64) GST_EVENT_SEEK_OFFSET (event));
|
|
||||||
GST_WRITE_UINT32_BE (*payload + 12,
|
|
||||||
(guint32) GST_EVENT_SEEK_ACCURACY (event));
|
|
||||||
break;
|
|
||||||
case GST_EVENT_SEEK_SEGMENT:
|
|
||||||
pl_length = 4 + 8 + 8 + 4;
|
pl_length = 4 + 8 + 8 + 4;
|
||||||
*payload = g_malloc0 (pl_length);
|
*payload = g_malloc0 (pl_length);
|
||||||
GST_WRITE_UINT32_BE (*payload, (guint32) GST_EVENT_SEEK_TYPE (event));
|
GST_WRITE_UINT32_BE (*payload, (guint32) GST_EVENT_SEEK_TYPE (event));
|
||||||
|
@ -350,7 +341,6 @@ gst_dp_packet_from_event (const GstEvent * event, GstDPHeaderFlag flags,
|
||||||
(guint32) GST_EVENT_SEEK_ACCURACY (event));
|
(guint32) GST_EVENT_SEEK_ACCURACY (event));
|
||||||
break;
|
break;
|
||||||
case GST_EVENT_QOS:
|
case GST_EVENT_QOS:
|
||||||
case GST_EVENT_SEGMENT_DONE:
|
|
||||||
case GST_EVENT_SIZE:
|
case GST_EVENT_SIZE:
|
||||||
case GST_EVENT_RATE:
|
case GST_EVENT_RATE:
|
||||||
case GST_EVENT_NAVIGATION:
|
case GST_EVENT_NAVIGATION:
|
||||||
|
@ -490,20 +480,6 @@ gst_dp_event_from_packet (guint header_length, const guint8 * header,
|
||||||
GST_EVENT_TIMESTAMP (event) = GST_DP_HEADER_TIMESTAMP (header);
|
GST_EVENT_TIMESTAMP (event) = GST_DP_HEADER_TIMESTAMP (header);
|
||||||
break;
|
break;
|
||||||
case GST_EVENT_SEEK:
|
case GST_EVENT_SEEK:
|
||||||
{
|
|
||||||
GstSeekType type;
|
|
||||||
gint64 offset;
|
|
||||||
GstSeekAccuracy accuracy;
|
|
||||||
|
|
||||||
type = (GstSeekType) GST_READ_UINT32_BE (payload);
|
|
||||||
offset = (gint64) GST_READ_UINT64_BE (payload + 4);
|
|
||||||
accuracy = (GstSeekAccuracy) GST_READ_UINT32_BE (payload + 12);
|
|
||||||
event = gst_event_new_seek (type, offset);
|
|
||||||
GST_EVENT_TIMESTAMP (event) = GST_DP_HEADER_TIMESTAMP (header);
|
|
||||||
GST_EVENT_SEEK_ACCURACY (event) = accuracy;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GST_EVENT_SEEK_SEGMENT:
|
|
||||||
{
|
{
|
||||||
GstSeekType type;
|
GstSeekType type;
|
||||||
gint64 offset, endoffset;
|
gint64 offset, endoffset;
|
||||||
|
@ -519,7 +495,6 @@ gst_dp_event_from_packet (guint header_length, const guint8 * header,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GST_EVENT_QOS:
|
case GST_EVENT_QOS:
|
||||||
case GST_EVENT_SEGMENT_DONE:
|
|
||||||
case GST_EVENT_SIZE:
|
case GST_EVENT_SIZE:
|
||||||
case GST_EVENT_RATE:
|
case GST_EVENT_RATE:
|
||||||
case GST_EVENT_NAVIGATION:
|
case GST_EVENT_NAVIGATION:
|
||||||
|
|
|
@ -419,8 +419,7 @@ static const GstEventMask *
|
||||||
gst_fakesrc_get_event_mask (GstPad * pad)
|
gst_fakesrc_get_event_mask (GstPad * pad)
|
||||||
{
|
{
|
||||||
static const GstEventMask masks[] = {
|
static const GstEventMask masks[] = {
|
||||||
{GST_EVENT_SEEK, GST_SEEK_FLAG_FLUSH},
|
{GST_EVENT_SEEK, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT_LOOP},
|
||||||
{GST_EVENT_SEEK_SEGMENT, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT_LOOP},
|
|
||||||
{GST_EVENT_FLUSH, 0},
|
{GST_EVENT_FLUSH, 0},
|
||||||
{0, 0},
|
{0, 0},
|
||||||
};
|
};
|
||||||
|
@ -437,18 +436,12 @@ gst_fakesrc_event_handler (GstPad * pad, GstEvent * event)
|
||||||
|
|
||||||
switch (GST_EVENT_TYPE (event)) {
|
switch (GST_EVENT_TYPE (event)) {
|
||||||
case GST_EVENT_SEEK:
|
case GST_EVENT_SEEK:
|
||||||
src->buffer_count = GST_EVENT_SEEK_OFFSET (event);
|
|
||||||
|
|
||||||
if (!GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* else we do a flush too */
|
|
||||||
case GST_EVENT_SEEK_SEGMENT:
|
|
||||||
src->segment_start = GST_EVENT_SEEK_OFFSET (event);
|
src->segment_start = GST_EVENT_SEEK_OFFSET (event);
|
||||||
src->segment_end = GST_EVENT_SEEK_ENDOFFSET (event);
|
src->segment_end = GST_EVENT_SEEK_ENDOFFSET (event);
|
||||||
src->buffer_count = src->segment_start;
|
src->buffer_count = src->segment_start;
|
||||||
src->segment_loop =
|
src->segment_loop =
|
||||||
GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_SEGMENT_LOOP;
|
GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_SEGMENT_LOOP;
|
||||||
|
src->need_flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH;
|
||||||
break;
|
break;
|
||||||
case GST_EVENT_FLUSH:
|
case GST_EVENT_FLUSH:
|
||||||
src->need_flush = TRUE;
|
src->need_flush = TRUE;
|
||||||
|
@ -795,7 +788,7 @@ gst_fakesrc_loop (GstPad * pad)
|
||||||
|
|
||||||
if (src->buffer_count == src->segment_end) {
|
if (src->buffer_count == src->segment_end) {
|
||||||
if (src->segment_loop) {
|
if (src->segment_loop) {
|
||||||
gst_pad_push_event (pad, gst_event_new (GST_EVENT_SEGMENT_DONE));
|
//gst_pad_push_event (pad, gst_event_new (GST_EVENT_SEGMENT_DONE));
|
||||||
} else {
|
} else {
|
||||||
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
|
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
|
||||||
gst_task_pause (src->task);
|
gst_task_pause (src->task);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
include ../Rules
|
include ../Rules
|
||||||
|
|
||||||
tests_pass = signedness clock1 clock2 clock3
|
tests_pass = signedness clock1 clock2 clock3 clock4
|
||||||
tests_fail =
|
tests_fail =
|
||||||
tests_ignore =
|
tests_ignore =
|
||||||
|
|
100
tests/old/testsuite/clock/clock4.c
Normal file
100
tests/old/testsuite/clock/clock4.c
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* testsuite program to test clock behaviour
|
||||||
|
*
|
||||||
|
* creates a fakesrc ! identity ! fakesink pipeline
|
||||||
|
* registers a callback on fakesrc and one on fakesink
|
||||||
|
* also register a normal GLib timeout which should not be reached
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
|
||||||
|
static GstClock *clock = NULL;
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_clock_debug (GstClock * clock)
|
||||||
|
{
|
||||||
|
GstClockTime time;
|
||||||
|
|
||||||
|
time = gst_clock_get_time (clock);
|
||||||
|
|
||||||
|
g_print ("Clock info: time %" G_GUINT64_FORMAT "\n", time);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ok_callback (GstClock * clock, GstClockTime time,
|
||||||
|
GstClockID id, gpointer user_data)
|
||||||
|
{
|
||||||
|
g_print ("unlocked async id %p\n", id);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
error_callback (GstClock * clock, GstClockTime time,
|
||||||
|
GstClockID id, gpointer user_data)
|
||||||
|
{
|
||||||
|
g_print ("unlocked unscheduled async id %p, this is wrong\n", id);
|
||||||
|
g_assert_not_reached ();
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
GstClockID id, id2;
|
||||||
|
GstClockTime base;
|
||||||
|
GstClockReturn result;
|
||||||
|
|
||||||
|
gst_init (&argc, &argv);
|
||||||
|
|
||||||
|
clock = gst_system_clock_obtain ();
|
||||||
|
g_assert (clock != NULL);
|
||||||
|
|
||||||
|
gst_clock_debug (clock);
|
||||||
|
base = gst_clock_get_time (clock);
|
||||||
|
|
||||||
|
/* signal every half a second */
|
||||||
|
id = gst_clock_new_periodic_id (clock, base + GST_SECOND, GST_SECOND / 2);
|
||||||
|
g_assert (id);
|
||||||
|
|
||||||
|
g_print ("waiting one second\n");
|
||||||
|
result = gst_clock_id_wait (id, NULL);
|
||||||
|
gst_clock_debug (clock);
|
||||||
|
g_assert (result == GST_CLOCK_OK);
|
||||||
|
|
||||||
|
g_print ("waiting for the next\n");
|
||||||
|
result = gst_clock_id_wait (id, NULL);
|
||||||
|
gst_clock_debug (clock);
|
||||||
|
g_assert (result == GST_CLOCK_OK);
|
||||||
|
|
||||||
|
g_print ("waiting for the next async %p\n", id);
|
||||||
|
result = gst_clock_id_wait_async (id, ok_callback, NULL);
|
||||||
|
g_assert (result == GST_CLOCK_OK);
|
||||||
|
g_usleep (2 * G_USEC_PER_SEC);
|
||||||
|
|
||||||
|
g_print ("waiting some more for the next async %p\n", id);
|
||||||
|
result = gst_clock_id_wait_async (id, ok_callback, NULL);
|
||||||
|
g_assert (result == GST_CLOCK_OK);
|
||||||
|
g_usleep (2 * G_USEC_PER_SEC);
|
||||||
|
|
||||||
|
id2 = gst_clock_new_periodic_id (clock, base + GST_SECOND, GST_SECOND / 2);
|
||||||
|
g_assert (id2);
|
||||||
|
|
||||||
|
g_print ("waiting some more for another async %p\n", id2);
|
||||||
|
result = gst_clock_id_wait_async (id2, ok_callback, NULL);
|
||||||
|
g_assert (result == GST_CLOCK_OK);
|
||||||
|
g_usleep (2 * G_USEC_PER_SEC);
|
||||||
|
|
||||||
|
g_print ("unschedule %p\n", id);
|
||||||
|
gst_clock_id_unschedule (id);
|
||||||
|
|
||||||
|
/* entry cannot be used again */
|
||||||
|
result = gst_clock_id_wait_async (id, error_callback, NULL);
|
||||||
|
g_assert (result == GST_CLOCK_UNSCHEDULED);
|
||||||
|
result = gst_clock_id_wait (id, NULL);
|
||||||
|
g_assert (result == GST_CLOCK_UNSCHEDULED);
|
||||||
|
g_usleep (2 * G_USEC_PER_SEC);
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
include ../Rules
|
include ../Rules
|
||||||
|
|
||||||
tests_pass = signedness clock1 clock2 clock3
|
tests_pass = signedness clock1 clock2 clock3 clock4
|
||||||
tests_fail =
|
tests_fail =
|
||||||
tests_ignore =
|
tests_ignore =
|
||||||
|
|
100
testsuite/clock/clock4.c
Normal file
100
testsuite/clock/clock4.c
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* testsuite program to test clock behaviour
|
||||||
|
*
|
||||||
|
* creates a fakesrc ! identity ! fakesink pipeline
|
||||||
|
* registers a callback on fakesrc and one on fakesink
|
||||||
|
* also register a normal GLib timeout which should not be reached
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
|
||||||
|
static GstClock *clock = NULL;
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_clock_debug (GstClock * clock)
|
||||||
|
{
|
||||||
|
GstClockTime time;
|
||||||
|
|
||||||
|
time = gst_clock_get_time (clock);
|
||||||
|
|
||||||
|
g_print ("Clock info: time %" G_GUINT64_FORMAT "\n", time);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ok_callback (GstClock * clock, GstClockTime time,
|
||||||
|
GstClockID id, gpointer user_data)
|
||||||
|
{
|
||||||
|
g_print ("unlocked async id %p\n", id);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
error_callback (GstClock * clock, GstClockTime time,
|
||||||
|
GstClockID id, gpointer user_data)
|
||||||
|
{
|
||||||
|
g_print ("unlocked unscheduled async id %p, this is wrong\n", id);
|
||||||
|
g_assert_not_reached ();
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
GstClockID id, id2;
|
||||||
|
GstClockTime base;
|
||||||
|
GstClockReturn result;
|
||||||
|
|
||||||
|
gst_init (&argc, &argv);
|
||||||
|
|
||||||
|
clock = gst_system_clock_obtain ();
|
||||||
|
g_assert (clock != NULL);
|
||||||
|
|
||||||
|
gst_clock_debug (clock);
|
||||||
|
base = gst_clock_get_time (clock);
|
||||||
|
|
||||||
|
/* signal every half a second */
|
||||||
|
id = gst_clock_new_periodic_id (clock, base + GST_SECOND, GST_SECOND / 2);
|
||||||
|
g_assert (id);
|
||||||
|
|
||||||
|
g_print ("waiting one second\n");
|
||||||
|
result = gst_clock_id_wait (id, NULL);
|
||||||
|
gst_clock_debug (clock);
|
||||||
|
g_assert (result == GST_CLOCK_OK);
|
||||||
|
|
||||||
|
g_print ("waiting for the next\n");
|
||||||
|
result = gst_clock_id_wait (id, NULL);
|
||||||
|
gst_clock_debug (clock);
|
||||||
|
g_assert (result == GST_CLOCK_OK);
|
||||||
|
|
||||||
|
g_print ("waiting for the next async %p\n", id);
|
||||||
|
result = gst_clock_id_wait_async (id, ok_callback, NULL);
|
||||||
|
g_assert (result == GST_CLOCK_OK);
|
||||||
|
g_usleep (2 * G_USEC_PER_SEC);
|
||||||
|
|
||||||
|
g_print ("waiting some more for the next async %p\n", id);
|
||||||
|
result = gst_clock_id_wait_async (id, ok_callback, NULL);
|
||||||
|
g_assert (result == GST_CLOCK_OK);
|
||||||
|
g_usleep (2 * G_USEC_PER_SEC);
|
||||||
|
|
||||||
|
id2 = gst_clock_new_periodic_id (clock, base + GST_SECOND, GST_SECOND / 2);
|
||||||
|
g_assert (id2);
|
||||||
|
|
||||||
|
g_print ("waiting some more for another async %p\n", id2);
|
||||||
|
result = gst_clock_id_wait_async (id2, ok_callback, NULL);
|
||||||
|
g_assert (result == GST_CLOCK_OK);
|
||||||
|
g_usleep (2 * G_USEC_PER_SEC);
|
||||||
|
|
||||||
|
g_print ("unschedule %p\n", id);
|
||||||
|
gst_clock_id_unschedule (id);
|
||||||
|
|
||||||
|
/* entry cannot be used again */
|
||||||
|
result = gst_clock_id_wait_async (id, error_callback, NULL);
|
||||||
|
g_assert (result == GST_CLOCK_UNSCHEDULED);
|
||||||
|
result = gst_clock_id_wait (id, NULL);
|
||||||
|
g_assert (result == GST_CLOCK_UNSCHEDULED);
|
||||||
|
g_usleep (2 * G_USEC_PER_SEC);
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in a new issue