mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 01:00:37 +00:00
Import all GStreamer design doc and convert them to markdown
https://bugzilla.gnome.org/show_bug.cgi?id=775667
This commit is contained in:
parent
bae66ab278
commit
2fdd87e282
59 changed files with 12011 additions and 0 deletions
424
markdown/design/MT-refcounting.md
Normal file
424
markdown/design/MT-refcounting.md
Normal file
|
@ -0,0 +1,424 @@
|
|||
# Conventions for thread a safe API
|
||||
|
||||
The GStreamer API is designed to be thread safe. This means that API functions
|
||||
can be called from multiple threads at the same time. GStreamer internally uses
|
||||
threads to perform the data passing and various asynchronous services such as
|
||||
the clock can also use threads.
|
||||
|
||||
This design decision has implications for the usage of the API and the objects
|
||||
which this document explains.
|
||||
|
||||
## MT safety techniques
|
||||
|
||||
Several design patterns are used to guarantee object consistency in GStreamer.
|
||||
This is an overview of the methods used in various GStreamer subsystems.
|
||||
|
||||
### Refcounting:
|
||||
|
||||
All shared objects have a refcount associated with them. Each reference
|
||||
obtained to the object should increase the refcount and each reference lost
|
||||
should decrease the refcount.
|
||||
|
||||
The refcounting is used to make sure that when another thread destroys the
|
||||
object, the ones which still hold a reference to the object do not read from
|
||||
invalid memory when accessing the object.
|
||||
|
||||
Refcounting is also used to ensure that mutable data structures are only
|
||||
modified when they are owned by the calling code.
|
||||
|
||||
It is a requirement that when two threads have a handle on an object, the
|
||||
refcount must be more than one. This means that when one thread passes an
|
||||
object to another thread it must increase the refcount. This requirement makes
|
||||
sure that one thread cannot suddenly dispose the object making the other
|
||||
thread crash when it tries to access the pointer to invalid memory.
|
||||
|
||||
### Shared data structures and writability:
|
||||
|
||||
All objects have a refcount associated with them. Each reference obtained to
|
||||
the object should increase the refcount and each reference lost should
|
||||
decrease the refcount.
|
||||
|
||||
Each thread having a refcount to the object can safely read from the object.
|
||||
but modifications made to the object should be preceded with a
|
||||
`_get_writable()` function call. This function will check the refcount of the
|
||||
object and if the object is referenced by more than one instance, a copy is
|
||||
made of the object that is then by definition only referenced from the calling
|
||||
thread. This new copy is then modifiable without being visible to other
|
||||
refcount holders.
|
||||
|
||||
This technique is used for information objects that, once created, never
|
||||
change their values. The lifetime of these objects is generally short, the
|
||||
objects are usually simple and cheap to copy/create.
|
||||
|
||||
The advantage of this method is that no reader/writers locks are needed. all
|
||||
threads can concurrently read but writes happen locally on a new copy. In most
|
||||
cases `_get_writable()` can avoid a real copy because the calling method is the
|
||||
only one holding a reference, which makes read/write very cheap.
|
||||
|
||||
The drawback is that sometimes 1 needless copy can be done. This would happen
|
||||
when N threads call `_get_writable()` at the same time, all seeing that N
|
||||
references are held on the object. In this case 1 copy too many will be done.
|
||||
This is not a problem in any practical situation because the copy operation is
|
||||
fast.
|
||||
|
||||
### Mutable substructures:
|
||||
|
||||
Special techniques are necessary to ensure the consistency of compound shared
|
||||
objects. As mentioned above, shared objects need to have a reference count of
|
||||
1 if they are to be modified. Implicit in this assumption is that all parts of
|
||||
the shared object belong only to the object. For example, a GstStructure in
|
||||
one GstCaps object should not belong to any other GstCaps object. This
|
||||
condition suggests a parent-child relationship: structures can only be added
|
||||
to parent object if they do not already have a parent object.
|
||||
|
||||
In addition, these substructures must not be modified while more than one code
|
||||
segment has a reference on the parent object. For example, if the user creates
|
||||
a GstStructure, adds it to a GstCaps, and the GstCaps is then referenced by
|
||||
other code segments, the GstStructure should then become immutable, so that
|
||||
changes to that data structure do not affect other parts of the code. This
|
||||
means that the child is only mutable when the parent's reference count is 1,
|
||||
as well as when the child structure has no parent.
|
||||
|
||||
The general solution to this problem is to include a field in child structures
|
||||
pointing to the parent's atomic reference count. When set to NULL, this
|
||||
indicates that the child has no parent. Otherwise, procedures that modify the
|
||||
child structure must check if the parent's refcount is 1, and otherwise must
|
||||
cause an error to be signaled.
|
||||
|
||||
Note that this is an internal implementation detail; application or plugin
|
||||
code that calls `_get_writable()` on an object is guaranteed to receive an
|
||||
object of refcount 1, which must then be writable. The only trick is that a
|
||||
pointer to a child structure of an object is only valid while the calling code
|
||||
has a reference on the parent object, because the parent is the owner of the
|
||||
child.
|
||||
|
||||
### Object locking:
|
||||
|
||||
For objects that contain state information and generally have a longer
|
||||
lifetime, object locking is used to update the information contained in the
|
||||
object.
|
||||
|
||||
All readers and writers acquire the lock before accessing the object. Only one
|
||||
thread is allowed access the protected structures at a time.
|
||||
|
||||
Object locking is used for all objects extending from GstObject such as
|
||||
GstElement, GstPad.
|
||||
|
||||
Object locking can be done with recursive locks or regular mutexes. Object
|
||||
locks in GStreamer are implemented with mutexes which cause deadlocks when
|
||||
locked recursively from the same thread. This is done because regular mutexes
|
||||
are cheaper.
|
||||
|
||||
### Atomic operations
|
||||
|
||||
Atomic operations are operations that are performed as one consistent
|
||||
operation even when executed by multiple threads. They do however not use the
|
||||
conventional aproach of using mutexes to protect the critical section but rely
|
||||
on CPU features and instructions.
|
||||
|
||||
The advantages are mostly speed related since there are no heavyweight locks
|
||||
involved. Most of these instructions also do not cause a context switch in case
|
||||
of concurrent access but use a retry mechanism or spinlocking.
|
||||
|
||||
Disadvantages are that each of these instructions usually cause a cache flush
|
||||
on multi-CPU machines when two processors perform concurrent access.
|
||||
|
||||
Atomic operations are generally used for refcounting and for the allocation of
|
||||
small fixed size objects in a memchunk. They can also be used to implement a
|
||||
lockfree list or stack.
|
||||
|
||||
### Compare and swap
|
||||
|
||||
As part of the atomic operations, compare-and-swap (CAS) can be used to access
|
||||
or update a single property or pointer in an object without having to take a
|
||||
lock.
|
||||
|
||||
This technique is currently not used in GStreamer but might be added in the
|
||||
future in performance critical places.
|
||||
|
||||
|
||||
## Objects
|
||||
|
||||
### Locking involved:
|
||||
|
||||
- atomic operations for refcounting
|
||||
- object locking
|
||||
|
||||
All objects should have a lock associated with them. This lock is used to keep
|
||||
internal consistency when multiple threads call API function on the object.
|
||||
|
||||
For objects that extend the GStreamer base object class this lock can be
|
||||
obtained with the macros `GST_OBJECT_LOCK()` and `GST_OBJECT_UNLOCK()`. For other object that do
|
||||
not extend from the base GstObject class these macros can be different.
|
||||
|
||||
### refcounting
|
||||
|
||||
All new objects created have the FLOATING flag set. This means that the object
|
||||
is not owned or managed yet by anybody other than the one holding a reference
|
||||
to the object. The object in this state has a reference count of 1.
|
||||
|
||||
Various object methods can take ownership of another object, this means that
|
||||
after calling a method on object A with an object B as an argument, the object
|
||||
B is made sole property of object A. This means that after the method call you
|
||||
are not allowed to access the object anymore unless you keep an extra
|
||||
reference to the object. An example of such a method is the `_bin_add()` method.
|
||||
As soon as this function is called in a Bin, the element passed as an argument
|
||||
is owned by the bin and you are not allowed to access it anymore without
|
||||
taking a `_ref()` before adding it to the bin. The reason being that after the
|
||||
`_bin_add()` call disposing the bin also destroys the element.
|
||||
|
||||
Taking ownership of an object happens through the process of "sinking" the
|
||||
object. the `_sink()` method on an object will decrease the refcount of the
|
||||
object if the FLOATING flag is set. The act of taking ownership of an object
|
||||
is then performed as a `_ref()` followed by a `_sink()` call on the object.
|
||||
|
||||
The float/sink process is very useful when initializing elements that will
|
||||
then be placed under control of a parent. The floating ref keeps the object
|
||||
alive until it is parented, and once the object is parented you can forget
|
||||
about it.
|
||||
|
||||
also see [relations](design/relations.md)
|
||||
|
||||
### parent-child relations
|
||||
|
||||
One can create parent-child relationships with the `_object_set_parent()`
|
||||
method. This method refs and sinks the object and assigns its parent property
|
||||
to that of the managing parent.
|
||||
|
||||
The child is said to have a weak link to the parent since the refcount of the
|
||||
parent is not increased in this process. This means that if the parent is
|
||||
disposed it has to unset itself as the parent of the object before disposing
|
||||
itself, else the child object holds a parent pointer to invalid memory.
|
||||
|
||||
The responsibilities for an object that sinks other objects are summarised as:
|
||||
|
||||
- taking ownership of the object
|
||||
- call `_object_set_parent()` to set itself as the object parent, this call
|
||||
will `_ref()` and `_sink()` the object.
|
||||
- keep reference to object in a datastructure such as a list or array.
|
||||
|
||||
- on dispose
|
||||
- call `_object_unparent()` to reset the parent property and unref the
|
||||
object.
|
||||
- remove the object from the list.
|
||||
|
||||
also see [relations](design/relations.md)
|
||||
|
||||
### Properties
|
||||
|
||||
Most objects also expose state information with public properties in the
|
||||
object. Two types of properties might exist: accessible with or without
|
||||
holding the object lock. All properties should only be accessed with their
|
||||
corresponding macros. The public object properties are marked in the .h files
|
||||
with /*< public >*/. The public properties that require a lock to be held are
|
||||
marked with `/*< public >*/` `/* with <lock_type> */`, where `<lock_type>` can be
|
||||
`LOCK` or `STATE_LOCK` or any other lock to mark the type(s) of lock to be
|
||||
held.
|
||||
|
||||
**Example**:
|
||||
|
||||
in GstPad there is a public property `direction`. It can be found in the
|
||||
section marked as public and requiring the LOCK to be held. There exists
|
||||
also a macro to access the property.
|
||||
|
||||
struct _GstRealPad {
|
||||
...
|
||||
/*< public >*/ /* with LOCK */
|
||||
...
|
||||
GstPadDirection direction;
|
||||
...
|
||||
};
|
||||
|
||||
#define GST_RPAD_DIRECTION(pad) (GST_REAL_PAD_CAST(pad)->direction)
|
||||
|
||||
Accessing the property is therefore allowed with the following code example:
|
||||
|
||||
GST_OBJECT_LOCK (pad);
|
||||
direction = GST_RPAD_DIRECTION (pad);
|
||||
GST_OBJECT_UNLOCK (pad);
|
||||
|
||||
### Property lifetime
|
||||
|
||||
All properties requiring a lock can change after releasing the associated
|
||||
lock. This means that as long as you hold the lock, the state of the
|
||||
object regarding the locked properties is consistent with the information
|
||||
obtained. As soon as the lock is released, any values acquired from the
|
||||
properties might not be valid anymore and can as best be described as a
|
||||
snapshot of the state when the lock was held.
|
||||
|
||||
This means that all properties that require access beyond the scope of the
|
||||
critial section should be copied or refcounted before releasing the lock.
|
||||
|
||||
Most object provide a `_get_<property>()` method to get a copy or refcounted
|
||||
instance of the property value. The caller should not wory about any locks
|
||||
but should unref/free the object after usage.
|
||||
|
||||
**Example**:
|
||||
|
||||
the following example correctly gets the peer pad of an element. It is
|
||||
required to increase the refcount of the peer pad because as soon as the
|
||||
lock is released, the peer could be unreffed and disposed, making the
|
||||
pointer obtained in the critical section point to invalid memory.
|
||||
|
||||
``` c
|
||||
GST_OBJECT_LOCK (pad);
|
||||
peer = GST_RPAD_PEER (pad);
|
||||
if (peer)
|
||||
gst_object_ref (GST_OBJECT (peer));
|
||||
GST_OBJECT_UNLOCK (pad);
|
||||
... use peer ...
|
||||
|
||||
if (peer)
|
||||
gst_object_unref (GST_OBJECT (peer));
|
||||
```
|
||||
|
||||
Note that after releasing the lock the peer might not actually be the peer
|
||||
anymore of the pad. If you need to be sure it is, you need to extend the
|
||||
critical section to include the operations on the peer.
|
||||
|
||||
The following code is equivalent to the above but with using the functions
|
||||
to access object properties.
|
||||
|
||||
``` c
|
||||
peer = gst_pad_get_peer (pad);
|
||||
if (peer) {
|
||||
... use peer ...
|
||||
|
||||
gst_object_unref (GST_OBJECT (peer));
|
||||
}
|
||||
```
|
||||
|
||||
**Example**:
|
||||
|
||||
Accessing the name of an object makes a copy of the name. The caller of the
|
||||
function should g_free() the name after usage.
|
||||
|
||||
``` c
|
||||
GST_OBJECT_LOCK (object)
|
||||
name = g_strdup (GST_OBJECT_NAME (object));
|
||||
GST_OBJECT_UNLOCK (object)
|
||||
... use name ...
|
||||
|
||||
g_free (name);
|
||||
```
|
||||
|
||||
or:
|
||||
|
||||
``` c
|
||||
name = gst_object_get_name (object);
|
||||
|
||||
... use name ...
|
||||
|
||||
g_free (name);
|
||||
```
|
||||
|
||||
### Accessor methods
|
||||
|
||||
For aplications it is encouraged to use the public methods of the object. Most
|
||||
useful operations can be performed with the methods so it is seldom required
|
||||
to access the public fields manually.
|
||||
|
||||
All accessor methods that return an object should increase the refcount of the
|
||||
returned object. The caller should `_unref()` the object after usage. Each
|
||||
method should state this refcounting policy in the documentation.
|
||||
|
||||
### Accessing lists
|
||||
|
||||
If the object property is a list, concurrent list iteration is needed to get
|
||||
the contents of the list. GStreamer uses the cookie mechanism to mark the last
|
||||
update of a list. The list and the cookie are protected by the same lock. Each
|
||||
update to a list requires the following actions:
|
||||
|
||||
- acquire lock
|
||||
- update list
|
||||
- update cookie
|
||||
- release lock
|
||||
|
||||
Updating the cookie is usually done by incrementing its value by one. Since
|
||||
cookies use guint32 its wraparound is for all practical reasons is not a
|
||||
problem.
|
||||
|
||||
Iterating a list can safely be done by surrounding the list iteration with a
|
||||
lock/unlock of the lock.
|
||||
|
||||
In some cases it is not a good idea to hold the lock for a long time while
|
||||
iterating the list. The state change code for a bin in GStreamer, for example,
|
||||
has to iterate over each element and perform a blocking call on each of them
|
||||
potentially causing infinite bin locking. In this case the cookie can be used
|
||||
to iterate a list.
|
||||
|
||||
**Example**:
|
||||
|
||||
The following algorithm iterates a list and reverses the updates in the
|
||||
case a concurrent update was done to the list while iterating. The idea is
|
||||
that whenever we reacquire the lock, we check for updates to the cookie to
|
||||
decide if we are still iterating the right list.
|
||||
|
||||
``` c
|
||||
GST_OBJECT_LOCK (lock);
|
||||
/* grab list and cookie */
|
||||
cookie = object->list_cookie;
|
||||
list = object-list;
|
||||
while (list) {
|
||||
GstObject *item = GST_OBJECT (list->data);
|
||||
/* need to ref the item before releasing the lock */
|
||||
gst_object_ref (item);
|
||||
GST_OBJECT_UNLOCK (lock);
|
||||
|
||||
... use/change item here...
|
||||
|
||||
/* release item here */
|
||||
gst_object_unref (item);
|
||||
|
||||
GST_OBJECT_LOCK (lock);
|
||||
if (cookie != object->list_cookie) {
|
||||
/* handle rollback caused by concurrent modification
|
||||
* of the list here */
|
||||
|
||||
...rollback changes to items...
|
||||
|
||||
/* grab new cookie and list */
|
||||
cookie = object->list_cookie;
|
||||
list = object->list;
|
||||
}
|
||||
else {
|
||||
list = g_list_next (list);
|
||||
}
|
||||
}
|
||||
GST_OBJECT_UNLOCK (lock);
|
||||
```
|
||||
|
||||
### GstIterator
|
||||
|
||||
GstIterator provides an easier way of retrieving elements in a concurrent
|
||||
list. The following code example is equivalent to the previous example.
|
||||
|
||||
**Example**:
|
||||
|
||||
``` c
|
||||
it = _get_iterator(object);
|
||||
while (!done) {
|
||||
switch (gst_iterator_next (it, &item)) {
|
||||
case GST_ITERATOR_OK:
|
||||
|
||||
... use/change item here...
|
||||
|
||||
/* release item here */
|
||||
gst_object_unref (item);
|
||||
break;
|
||||
case GST_ITERATOR_RESYNC:
|
||||
/* handle rollback caused by concurrent modification
|
||||
* of the list here */
|
||||
|
||||
...rollback changes to items...
|
||||
|
||||
/* resync iterator to start again */
|
||||
gst_iterator_resync (it);
|
||||
break;
|
||||
case GST_ITERATOR_DONE:
|
||||
done = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
gst_iterator_free (it);
|
||||
```
|
96
markdown/design/TODO.md
Normal file
96
markdown/design/TODO.md
Normal file
|
@ -0,0 +1,96 @@
|
|||
# TODO - Future Development
|
||||
|
||||
## API/ABI
|
||||
|
||||
- implement return values from events in addition to the gboolean.
|
||||
This should be done by making the event contain a GstStructure with
|
||||
input/output values, similar to GstQuery. A typical use case is
|
||||
performing a non-accurate seek to a keyframe, after the seek you
|
||||
want to get the new stream time that will actually be used to update
|
||||
the slider bar.
|
||||
|
||||
- make gst\_pad\_push\_event() return a GstFlowReturn
|
||||
|
||||
- GstEvent, GstMessage register like GstFormat or GstQuery.
|
||||
|
||||
- query POSITION/DURATION return accuracy. Just a flag or accuracy
|
||||
percentage.
|
||||
|
||||
- use | instead of + as divider in serialization of Flags
|
||||
(gstvalue/gststructure)
|
||||
|
||||
- rethink how we handle dynamic replugging wrt segments and other
|
||||
events that already got pushed and need to be pushed again. Might
|
||||
need GstFlowReturn from gst\_pad\_push\_event(). FIXED in 0.11 with
|
||||
sticky events.
|
||||
|
||||
- Optimize negotiation. We currently do a get\_caps() call when we
|
||||
link pads, which could potentially generate a huge list of caps and
|
||||
all their combinations, we need to avoid generating these huge lists
|
||||
by generating them We also need to incrementally return
|
||||
intersections etc, for this. somewhat incrementally when needed. We
|
||||
can do this with a gst\_pad\_iterate\_caps() call. We also need to
|
||||
incrementally return intersections etc, for this. FIXED in 0.11 with
|
||||
a filter on getcaps functions.
|
||||
|
||||
- Elements in a bin have no clue about the final state of the parent
|
||||
element since the bin sets the target state on its children in small
|
||||
steps. This causes problems for elements that like to know the final
|
||||
state (rtspsrc going to PAUSED or READY is different in that we can
|
||||
avoid sending the useless PAUSED request).
|
||||
|
||||
- Make serialisation of structures more consistent, readable and nicer
|
||||
code-wise.
|
||||
|
||||
- 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/ reason that blocked the pad.
|
||||
|
||||
- it only blocks on datapassing. When EOS, the block never happens
|
||||
but ideally should because pad block should inform the app when
|
||||
there is no dataflow.
|
||||
|
||||
- the same goes for segment seeks that don’t push in-band EOS
|
||||
events. Maybe segment seeks should also send an EOS event when
|
||||
they’re done.
|
||||
|
||||
- blocking should only happen from one thread. If one thread does
|
||||
pad\_alloc and another a push, the push might be busy while the
|
||||
block callback is done.
|
||||
|
||||
- maybe this name is overloaded. We need to look at some more use
|
||||
cases before trying to fix this. FIXED in 0.11 with BLOCKING
|
||||
probes.
|
||||
|
||||
- rethink the way we do upstream renegotiation. Currently it’s done
|
||||
with pad\_alloc but this has many issues such as only being able to
|
||||
suggest 1 format and the need to allocate a buffer of this suggested
|
||||
format (some elements such as capsfilter only know about the format,
|
||||
not the size). We would ideally like to let upstream renegotiate a
|
||||
new format just like it did when it started. This could, for
|
||||
example, easily be triggered with a RENEGOTIATE event. FIXED in 0.11
|
||||
with RECONFIGURE events.
|
||||
|
||||
- Remove the result format value in queries. FIXED in 0.11
|
||||
|
||||
- Try to minimize the amount of acceptcaps calls when pushing buffers
|
||||
around. The element pushing the buffer usually negotiated already
|
||||
and decided on the format. The element receiving the buffer usually
|
||||
has to accept the caps anyway.
|
||||
|
||||
## IMPLEMENTATION
|
||||
|
||||
- implement more QOS, [qos](design/qos.md).
|
||||
|
||||
- implement BUFFERSIZE.
|
||||
|
||||
## DESIGN
|
||||
|
||||
- unlinking pads in the PAUSED state needs to make sure the stream
|
||||
thread is not executing code. Can this be done with a flush to
|
||||
unlock all downstream chain functions? Do we do this automatically
|
||||
or let the app handle this?
|
88
markdown/design/activation.md
Normal file
88
markdown/design/activation.md
Normal file
|
@ -0,0 +1,88 @@
|
|||
# Pad (de)activation
|
||||
|
||||
## Activation
|
||||
|
||||
When changing states, a bin will set the state on all of its children in
|
||||
sink-to-source order. As elements undergo the READY→PAUSED transition,
|
||||
their pads are activated so as to prepare for data flow. Some pads will
|
||||
start tasks to drive the data flow.
|
||||
|
||||
An element activates its pads from sourcepads to sinkpads. This to make
|
||||
sure that when the sinkpads are activated and ready to accept data, the
|
||||
sourcepads are already active to pass the data downstream.
|
||||
|
||||
Pads can be activated in one of two modes, PUSH and PULL. PUSH pads are
|
||||
the normal case, where the source pad in a link sends data to the sink
|
||||
pad via `gst_pad_push()`. PULL pads instead have sink pads request data
|
||||
from the source pads via `gst_pad_pull_range()`.
|
||||
|
||||
To activate a pad, the core will call `gst_pad_set_active()` with a
|
||||
TRUE argument, indicating that the pad should be active. If the pad is
|
||||
already active, be it in a PUSH or PULL mode, `gst_pad_set_active()`
|
||||
will return without doing anything. Otherwise it will call the
|
||||
activation function of the pad.
|
||||
|
||||
Because the core does not know in which mode to activate a pad (PUSH or
|
||||
PULL), it delegates that choice to a method on the pad, activate(). The
|
||||
activate() function of a pad should choose whether to operate in PUSH or
|
||||
PULL mode. Once the choice is made, it should call ``activate_mode()`` with
|
||||
the selected activation mode. The default activate() function will call
|
||||
`activate_mode()` with ``#GST_PAD_MODE_PUSH``, as it is the default
|
||||
mechanism for data flow. A sink pad that supports either mode of
|
||||
operation might call `activate_mode(PULL)` if the SCHEDULING query
|
||||
upstream contains the `#GST_PAD_MODE_PULL` scheduling mode, and
|
||||
`activate_mode(PUSH)` otherwise.
|
||||
|
||||
Consider the case `fakesrc ! fakesink`, where fakesink is configured to
|
||||
operate in PULL mode. State changes in the pipeline will start with
|
||||
fakesink, which is the most downstream element. The core will call
|
||||
`activate()` on fakesink’s sink pad. For fakesink to go into PULL mode, it
|
||||
needs to implement a custom activate() function that will call
|
||||
`activate_mode(PULL)` on its sink pad (because the default is to use PUSH
|
||||
mode). activate_mode(PULL) is then responsible for starting the task
|
||||
that pulls from fakesrc:src. Clearly, fakesrc needs to be notified that
|
||||
fakesrc is about to pull on its src pad, even though the pipeline has
|
||||
not yet changed fakesrc’s state. For this reason, GStreamer will first
|
||||
call call `activate_mode(PULL)` on fakesink:sink’s peer before calling
|
||||
`activate_mode(PULL)` on fakesink:sinks.
|
||||
|
||||
In short, upstream elements operating in PULL mode must be ready to
|
||||
produce data in READY, after having `activate_mode(PULL)` called on their
|
||||
source pad. Also, a call to `activate_mode(PULL)` needs to propagate
|
||||
through the pipeline to every pad that a `gst_pad_pull()` will reach. In
|
||||
the case `fakesrc ! identity ! fakesink`, calling `activate_mode(PULL)`
|
||||
on identity’s source pad would need to activate its sink pad in pull
|
||||
mode as well, which should propagate all the way to fakesrc.
|
||||
|
||||
If, on the other hand, `fakesrc ! fakesink` is operating in PUSH mode,
|
||||
the activation sequence is different. First, activate() on fakesink:sink
|
||||
calls `activate_mode(PUSH)` on fakesink:sink. Then fakesrc’s pads are
|
||||
activated: sources first, then sinks (of which fakesrc has none).
|
||||
fakesrc:src’s activation function is then called.
|
||||
|
||||
Note that it does not make sense to set an activation function on a
|
||||
source pad. The peer of a source pad is downstream, meaning it should
|
||||
have been activated first. If it was activated in PULL mode, the source
|
||||
pad should have already had `activate_mode(PULL)` called on it, and thus
|
||||
needs no further activation. Otherwise it should be in PUSH mode, which
|
||||
is the choice of the default activation function.
|
||||
|
||||
So, in the PUSH case, the default activation function chooses PUSH mode,
|
||||
which calls `activate_mode(PUSH),` which will then start a task on the
|
||||
source pad and begin pushing. In this way PUSH scheduling is a bit
|
||||
easier, because it follows the order of state changes in a pipeline.
|
||||
fakesink is already in PAUSED with an active sink pad by the time
|
||||
fakesrc starts pushing data.
|
||||
|
||||
## Deactivation
|
||||
|
||||
Pad deactivation occurs when its parent goes into the READY state or
|
||||
when the pad is deactivated explicitly by the application or element.
|
||||
`gst_pad_set_active()` is called with a FALSE argument, which then
|
||||
calls `activate_mode(PUSH)` or `activate_mode(PULL)` with a FALSE
|
||||
argument, depending on the current activation mode of the pad.
|
||||
|
||||
## Mode switching
|
||||
|
||||
Changing from push to pull modes needs a bit of thought. This is
|
||||
actually possible and implemented but not yet documented here.
|
137
markdown/design/buffer.md
Normal file
137
markdown/design/buffer.md
Normal file
|
@ -0,0 +1,137 @@
|
|||
# GstBuffer
|
||||
|
||||
This document describes the design for buffers.
|
||||
|
||||
A GstBuffer is the object that is passed from an upstream element to a
|
||||
downstream element and contains memory and metadata information.
|
||||
|
||||
## Requirements
|
||||
|
||||
- It must be fast
|
||||
- allocation, free, low fragmentation
|
||||
- Must be able to attach multiple memory blocks to the buffer
|
||||
- Must be able to attach arbitrary metadata to buffers
|
||||
- efficient handling of subbuffer, copy, span, trim
|
||||
|
||||
## Lifecycle
|
||||
|
||||
GstMemory extends from GstMiniObject and therefore uses its lifecycle
|
||||
management (See [miniobject](design/miniobject.md)).
|
||||
|
||||
## Writability
|
||||
|
||||
When a Buffers is writable as returned from `gst_buffer_is_writable()`:
|
||||
|
||||
- metadata can be added/removed and the metadata can be changed
|
||||
|
||||
- GstMemory blocks can be added/removed
|
||||
|
||||
The individual memory blocks have their own locking and READONLY flags
|
||||
that might influence their writability.
|
||||
|
||||
Buffers can be made writable with `gst_buffer_make_writable()`. This
|
||||
will copy the buffer with the metadata and will ref the memory in the
|
||||
buffer. This means that the memory is not automatically copied when
|
||||
copying buffers.
|
||||
|
||||
# Managing GstMemory
|
||||
|
||||
A GstBuffer contains an array of pointers to GstMemory objects.
|
||||
|
||||
When the buffer is writable, `gst_buffer_insert_memory()` can be used
|
||||
to add a new GstMemory object to the buffer. When the array of memory is
|
||||
full, memory will be merged to make room for the new memory object.
|
||||
|
||||
`gst_buffer_n_memory()` is used to get the amount of memory blocks on
|
||||
the `GstBuffer`.
|
||||
|
||||
With `gst_buffer_peek_memory(),` memory can be retrieved from the
|
||||
memory array. The desired access pattern for the memory block should be
|
||||
specified so that appropriate checks can be made and, in case of
|
||||
`GST_MAP_WRITE`, a writable copy can be constructed when needed.
|
||||
|
||||
`gst_buffer_remove_memory_range()` and `gst_buffer_remove_memory()`
|
||||
can be used to remove memory from the GstBuffer.
|
||||
|
||||
# Subbuffers
|
||||
|
||||
Subbuffers are made by copying only a region of the memory blocks and
|
||||
copying all of the metadata.
|
||||
|
||||
# Span
|
||||
|
||||
Spanning will merge together the data of 2 buffers into a new
|
||||
buffer
|
||||
|
||||
# Data access
|
||||
|
||||
Accessing the data of the buffer can happen by retrieving the individual
|
||||
GstMemory objects in the GstBuffer or by using the `gst_buffer_map()` and
|
||||
`gst_buffer_unmap()` functions.
|
||||
|
||||
The `_map` and `_unmap` functions will always return the memory of all blocks as
|
||||
one large contiguous region of memory. Using the `_map` and `_unmap` functions
|
||||
might be more convenient than accessing the individual memory blocks at the
|
||||
expense of being more expensive because it might perform memcpy operations.
|
||||
|
||||
For buffers with only one GstMemory object (the most common case), `_map` and
|
||||
`_unmap` have no performance penalty at all.
|
||||
|
||||
- **Read access with 1 memory block**: The memory block is accessed and mapped
|
||||
for read access. The memory block is unmapped after usage
|
||||
|
||||
- **write access with 1 memory block**: The buffer should be writable or this
|
||||
operation will fail. The memory block is accessed. If the memory block is
|
||||
readonly, a copy is made and the original memory block is replaced with this
|
||||
copy. Then the memory block is mapped in write mode and unmapped after usage.
|
||||
|
||||
- **Read access with multiple memory blocks**: The memory blocks are combined
|
||||
into one large memory block. If the buffer is writable, the memory blocks are
|
||||
replaced with this new combined block. If the buffer is not writable, the
|
||||
memory is returned as is. The memory block is then mapped in read mode.
|
||||
When the memory is unmapped after usage and the buffer has multiple memory
|
||||
blocks, this means that the map operation was not able to store the combined
|
||||
buffer and it thus returned memory that should be freed. Otherwise, the memory
|
||||
is unmapped.
|
||||
|
||||
- **Write access with multiple memory blocks**: The buffer should be writable
|
||||
or the operation fails. The memory blocks are combined into one large memory
|
||||
block and the existing blocks are replaced with this new block. The memory is
|
||||
then mapped in write mode and unmapped after usage.
|
||||
|
||||
# Use cases
|
||||
|
||||
## Generating RTP packets from h264 video
|
||||
|
||||
We receive as input a GstBuffer with an encoded h264 image and we need
|
||||
to create RTP packets containing this h264 data as the payload. We
|
||||
typically need to fragment the h264 data into multiple packets, each
|
||||
with their own RTP and payload specific
|
||||
header.
|
||||
|
||||
```
|
||||
+-------+-------+---------------------------+--------+
|
||||
input H264 buffer: | NALU1 | NALU2 | ..... | NALUx |
|
||||
+-------+-------+---------------------------+--------+
|
||||
|
|
||||
V
|
||||
array of +-+ +-------+ +-+ +-------+ +-+ +-------+
|
||||
output buffers: | | | NALU1 | | | | NALU2 | .... | | | NALUx |
|
||||
+-+ +-------+ +-+ +-------+ +-+ +-------+
|
||||
: : : :
|
||||
\-----------/ \-----------/
|
||||
buffer 1 buffer 2
|
||||
```
|
||||
|
||||
The output buffer array consists of x buffers consisting of an RTP
|
||||
payload header and a subbuffer of the original input H264 buffer. Since
|
||||
the rtp headers and the h264 data don’t need to be contiguous in memory,
|
||||
they are added to the buffer as separate GstMemory blocks and we can
|
||||
avoid to memcpy the h264 data into contiguous memory.
|
||||
|
||||
A typical udpsink will then use something like sendmsg to send the
|
||||
memory regions on the network inside one UDP packet. This will further
|
||||
avoid having to memcpy data into contiguous memory.
|
||||
|
||||
Using bufferlists, the complete array of output buffers can be pushed in
|
||||
one operation to the peer element.
|
310
markdown/design/buffering.md
Normal file
310
markdown/design/buffering.md
Normal file
|
@ -0,0 +1,310 @@
|
|||
# Buffering
|
||||
|
||||
This document outlines the buffering policy used in the GStreamer core
|
||||
that can be used by plugins and applications.
|
||||
|
||||
The purpose of buffering is to accumulate enough data in a pipeline so
|
||||
that playback can occur smoothly and without interruptions. It is
|
||||
typically done when reading from a (slow) non-live network source but
|
||||
can also be used for live sources.
|
||||
|
||||
We want to be able to implement the following features:
|
||||
|
||||
- buffering up to a specific amount of data, in memory, before
|
||||
starting playback so that network fluctuations are minimized.
|
||||
|
||||
- download of the network file to a local disk with fast seeking in
|
||||
the downloaded data. This is similar to the quicktime/youtube
|
||||
players.
|
||||
|
||||
- caching of semi-live streams to a local, on disk, ringbuffer with
|
||||
seeking in the cached area. This is similar to tivo-like
|
||||
timeshifting.
|
||||
|
||||
- progress report about the buffering operations
|
||||
|
||||
- the possibility for the application to do more complex buffering
|
||||
|
||||
Some use cases:
|
||||
|
||||
- Stream buffering:
|
||||
|
||||
+---------+ +---------+ +-------+
|
||||
| httpsrc | | buffer | | demux |
|
||||
| src - sink src - sink ....
|
||||
+---------+ +---------+ +-------+
|
||||
|
||||
In this case we are reading from a slow network source into a buffer element
|
||||
(such as queue2).
|
||||
|
||||
The buffer element has a low and high watermark expressed in bytes. The
|
||||
buffer uses the watermarks as follows:
|
||||
|
||||
- The buffer element will post `BUFFERING` messages until the high
|
||||
watermark is hit. This instructs the application to keep the
|
||||
pipeline PAUSED, which will eventually block the srcpad from
|
||||
pushing while data is prerolled in the sinks.
|
||||
|
||||
- When the high watermark is hit, a `BUFFERING` message with 100%
|
||||
will be posted, which instructs the application to continue
|
||||
playback.
|
||||
|
||||
- When the low watermark is hit during playback, the queue will
|
||||
start posting `BUFFERING` messages again, making the application
|
||||
PAUSE the pipeline again until the high watermark is hit again.
|
||||
This is called the rebuffering stage.
|
||||
|
||||
- During playback, the queue level will fluctuate between the high
|
||||
and low watermarks as a way to compensate for network
|
||||
irregularities.
|
||||
|
||||
This buffering method is usable when the demuxer operates in push mode.
|
||||
Seeking in the stream requires the seek to happen in the network source.
|
||||
It is mostly desirable when the total duration of the file is not known, such
|
||||
as in live streaming or when efficient seeking is not possible/required.
|
||||
|
||||
- Incremental download
|
||||
|
||||
+---------+ +---------+ +-------+
|
||||
| httpsrc | | buffer | | demux |
|
||||
| src - sink src - sink ....
|
||||
+---------+ +----|----+ +-------+
|
||||
V
|
||||
file
|
||||
|
||||
In this case, we know the server is streaming a fixed length file to the
|
||||
client. The application can choose to download the file to disk. The buffer
|
||||
element will provide a push or pull based srcpad to the demuxer to navigate in
|
||||
the downloaded file.
|
||||
|
||||
This mode is only suitable when the client can determine the length of the
|
||||
file on the server.
|
||||
|
||||
In this case, buffering messages will be emitted as usual when the requested
|
||||
range is not within the downloaded area + buffersize. The buffering message
|
||||
will also contain an indication that incremental download is being performed.
|
||||
This flag can be used to let the application control the buffering in a more
|
||||
intelligent way, using the `BUFFERING` query, for example.
|
||||
|
||||
The application can use the `BUFFERING` query to get the estimated download time
|
||||
and match this time to the current/remaining playback time to control when
|
||||
playback should start to have a non-interrupted playback experience.
|
||||
|
||||
- Timeshifting
|
||||
|
||||
+---------+ +---------+ +-------+
|
||||
| httpsrc | | buffer | | demux |
|
||||
| src - sink src - sink ....
|
||||
+---------+ +----|----+ +-------+
|
||||
V
|
||||
file-ringbuffer
|
||||
|
||||
In this mode, a fixed size ringbuffer is kept to download the server content.
|
||||
This allows for seeking in the buffered data. Depending on the size of the
|
||||
buffer one can seek further back in time.
|
||||
|
||||
This mode is suitable for all live streams.
|
||||
|
||||
As with the incremental download mode, buffering messages are emitted along
|
||||
with an indication that timeshifting download is in progress.
|
||||
|
||||
- Live buffering
|
||||
|
||||
In live pipelines we usually introduce some latency between the capture and
|
||||
the playback elements. This latency can be introduced by a queue (such as a
|
||||
jitterbuffer) or by other means (in the audiosink).
|
||||
|
||||
Buffering messages can be emitted in those live pipelines as well and serve as
|
||||
an indication to the user of the latency buffering. The application usually
|
||||
does not react to these buffering messages with a state change.
|
||||
|
||||
## Messages
|
||||
|
||||
A `GST_MESSAGE_BUFFERING` must be posted on the bus when playback
|
||||
temporarily stops to buffer and when buffering finishes. When the
|
||||
percentage field in the `BUFFERING` message is 100, buffering is done.
|
||||
Values less than 100 mean that buffering is in progress.
|
||||
|
||||
The `BUFFERING` message should be intercepted and acted upon by the
|
||||
application. The message contains at least one field that is sufficient
|
||||
for basic functionality:
|
||||
|
||||
* **`buffer-percent`**, G_TYPE_INT: between 0 and 100
|
||||
|
||||
Several more clever ways of dealing with the buffering messages can be
|
||||
used when in incremental or timeshifting download mode. For this purpose
|
||||
additional fields are added to the buffering message:
|
||||
|
||||
* **`buffering-mode`**, `GST_TYPE_BUFFERING_MODE`: `enum { "stream", "download",
|
||||
"timeshift", "live" }`: Buffering mode in use. See above for an explanation of the different
|
||||
alternatives. This field can be used to let the application have more control
|
||||
over the buffering process.
|
||||
|
||||
* **`avg-in-rate`**, G_TYPE_INT: Average input buffering speed in bytes/second.
|
||||
-1 is unknown. This is the average number of bytes per second that is received
|
||||
on the buffering element input (sink) pads. It is a measurement of the network
|
||||
speed in most cases.
|
||||
|
||||
* **`avg-out-rate`**, G_TYPE_INT: Average consumption speed in bytes/second. -1
|
||||
is unknown. This is the average number of bytes per second that is consumed by
|
||||
the downstream element of the buffering element.
|
||||
|
||||
* **`buffering-left`**, G_TYPE_INT64: Estimated time that buffering will take
|
||||
in milliseconds. -1 is unknown. This is measured based on the avg-in-rate and
|
||||
the filled level of the queue. The application can use this hint to update the
|
||||
GUI about the estimated remaining time that buffering will take.
|
||||
|
||||
## Application
|
||||
|
||||
While data is buffered the pipeline should remain in the PAUSED state.
|
||||
It is also possible that more data should be buffered while the pipeline
|
||||
is PLAYING, in which case the pipeline should be PAUSED until the
|
||||
buffering finishes.
|
||||
|
||||
`BUFFERING` messages can be posted while the pipeline is prerolling. The
|
||||
application should not set the pipeline to PLAYING before a `BUFFERING`
|
||||
message with a 100 percent value is received, which might only happen
|
||||
after the pipeline prerolls.
|
||||
|
||||
An exception is made for live pipelines. The application may not change
|
||||
the state of a live pipeline when a buffering message is received.
|
||||
Usually these buffering messages contain the "buffering-mode" = "live".
|
||||
|
||||
The buffering message can also instruct the application to switch to a
|
||||
periodical `BUFFERING` query instead, so it can more precisely control the
|
||||
buffering process. The application can, for example, choose not to act
|
||||
on the `BUFFERING` complete message (buffer-percent = 100) to resume
|
||||
playback but use the estimated download time instead, resuming playback
|
||||
when it has determined that it should be able to provide uninterrupted
|
||||
playback.
|
||||
|
||||
## Buffering Query
|
||||
|
||||
In addition to the `BUFFERING` messages posted by the buffering elements,
|
||||
we want to be able to query the same information from the application.
|
||||
We also want to be able to present the user with information about the
|
||||
downloaded range in the file so that the GUI can react on it.
|
||||
|
||||
In addition to all the fields present in the buffering message, the
|
||||
`BUFFERING` query contains the following field, which indicates the
|
||||
available downloaded range in a specific format and the estimated time
|
||||
to complete:
|
||||
|
||||
* **`busy`**, G_TYPE_BOOLEAN: if buffering was busy. This flag allows the
|
||||
application to pause the pipeline by using the query only.
|
||||
|
||||
* **`format`**, GST_TYPE_FORMAT: the format of the "start" and "stop" values
|
||||
below
|
||||
|
||||
* **`start`**, G_TYPE_INT64, -1 unknown: the start position of the available
|
||||
data. If there are multiple ranges, this field contains the start position of
|
||||
the currently downloading range.
|
||||
|
||||
* **`stop`**, G_TYPE_INT64, -1 unknown: the stop position of the available
|
||||
data. If there are multiple ranges, this field contains the stop position of
|
||||
the currently downloading range.
|
||||
|
||||
* **`estimated-total`**, G_TYPE_INT64: gives the estimated download time in
|
||||
milliseconds. -1 unknown. When the size of the downloaded file is known, this
|
||||
value will contain the latest estimate of the remaining download time of the
|
||||
currently downloading range. This value is usually only filled for the
|
||||
"download" buffering mode. The application can use this information to estimate
|
||||
the amount of remaining time to download till the end of the file.
|
||||
|
||||
* **`buffering-ranges`**, G_TYPE_ARRAY of GstQueryBufferingRange: contains
|
||||
optionally the downloaded areas in the format given above. One of the ranges
|
||||
contains the same start/stop position as above:
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gint64 start;
|
||||
gint64 stop;
|
||||
} GstQueryBufferingRange;
|
||||
|
||||
For the `download` and `timeshift` buffering-modes, the start and stop
|
||||
positions specify the ranges where efficient seeking in the downloaded
|
||||
media is possible. Seeking outside of these ranges might be slow or not
|
||||
at all possible.
|
||||
|
||||
For the `stream` and `live` mode the start and stop values describe the
|
||||
oldest and newest item (expressed in `format`) in the buffer.
|
||||
|
||||
## Defaults
|
||||
|
||||
Some defaults for common elements:
|
||||
|
||||
A GstBaseSrc with random access replies to the `BUFFERING` query with:
|
||||
|
||||
"buffer-percent" = 100
|
||||
"buffering-mode" = "stream"
|
||||
"avg-in-rate" = -1
|
||||
"avg-out-rate" = -1
|
||||
"buffering-left" = 0
|
||||
"format" = GST_FORMAT_BYTES
|
||||
"start" = 0
|
||||
"stop" = the total filesize
|
||||
"estimated-total" = 0
|
||||
"buffering-ranges" = NULL
|
||||
|
||||
A GstBaseSrc in push mode replies to the `BUFFERING` query with:
|
||||
|
||||
"buffer-percent" = 100
|
||||
"buffering-mode" = "stream"
|
||||
"avg-in-rate" = -1
|
||||
"avg-out-rate" = -1
|
||||
"buffering-left" = 0
|
||||
"format" = a valid GST_TYPE_FORMAT
|
||||
"start" = current position
|
||||
"stop" = current position
|
||||
"estimated-total" = -1
|
||||
"buffering-ranges" = NULL
|
||||
|
||||
## Buffering strategies
|
||||
|
||||
Buffering strategies are specific implementations based on the buffering
|
||||
message and query described above.
|
||||
|
||||
Most strategies have to balance buffering time versus maximal playback
|
||||
experience.
|
||||
|
||||
### Simple buffering
|
||||
|
||||
NON-live pipelines are kept in the paused state while buffering messages with
|
||||
a percent < 100% are received.
|
||||
|
||||
This buffering strategy relies on the buffer size and low/high watermarks of
|
||||
the element. It can work with a fixed size buffer in memory or on disk.
|
||||
|
||||
The size of the buffer is usually expressed in a fixed amount of time units
|
||||
and the estimated bitrate of the upstream source is used to convert this time
|
||||
to bytes.
|
||||
|
||||
All GStreamer applications must implement this strategy. Failure to do so
|
||||
will result in starvation at the sink.
|
||||
|
||||
### No-rebuffer strategy
|
||||
|
||||
This strategy tries to buffer as much data as possible so that playback can
|
||||
continue without any further rebuffering.
|
||||
|
||||
This strategy is initially similar to simple buffering, the difference is in
|
||||
deciding on the condition to continue playback. When a 100% buffering message
|
||||
has been received, the application will not yet start the playback but it will
|
||||
start a periodic buffering query, which will return the estimated amount of
|
||||
buffering time left. When the estimated time left is less than the remaining
|
||||
playback time, playback can continue.
|
||||
|
||||
This strategy requires a unlimited buffer size in memory or on disk, such as
|
||||
provided by elements that implement the incremental download buffering mode.
|
||||
|
||||
Usually, the application can choose to start playback even before the
|
||||
remaining buffer time elapsed in order to more quickly start the playback at
|
||||
the expense of a possible rebuffering phase.
|
||||
|
||||
### Incremental rebuffering
|
||||
|
||||
The application implements the simple buffering strategy but with each
|
||||
rebuffering phase, it increases the size of the buffer.
|
||||
|
||||
This strategy has quick, fixed time startup times but incrementally longer
|
||||
rebuffering times if the network is slower than the media bitrate.
|
365
markdown/design/bufferpool.md
Normal file
365
markdown/design/bufferpool.md
Normal file
|
@ -0,0 +1,365 @@
|
|||
# Bufferpool
|
||||
|
||||
This document details the design of how buffers are be allocated and
|
||||
managed in pools.
|
||||
|
||||
Bufferpools increase performance by reducing allocation overhead and
|
||||
improving possibilities to implement zero-copy memory transfer.
|
||||
|
||||
Together with the ALLOCATION query, elements can negotiate allocation
|
||||
properties and bufferpools between themselves. This also allows elements
|
||||
to negotiate buffer metadata between themselves.
|
||||
|
||||
# Requirements
|
||||
|
||||
- Provide a GstBufferPool base class to help the efficient
|
||||
implementation of a list of reusable GstBuffer objects.
|
||||
|
||||
- Let upstream elements initiate the negotiation of a bufferpool and
|
||||
its configuration. Allow downstream elements provide bufferpool
|
||||
properties and/or a bufferpool. This includes the following
|
||||
properties:
|
||||
|
||||
- have minimum and maximum amount of buffers with the option of
|
||||
preallocating buffers.
|
||||
|
||||
- allocator, alignment and padding support
|
||||
|
||||
- buffer metadata
|
||||
|
||||
- arbitrary extra options
|
||||
|
||||
- Integrate with dynamic caps renegotiation.
|
||||
|
||||
- Notify upstream element of new bufferpool availability. This is
|
||||
important when a new element, that can provide a bufferpool, is
|
||||
dynamically linked
|
||||
downstream.
|
||||
|
||||
# GstBufferPool
|
||||
|
||||
The bufferpool object manages a list of buffers with the same properties such
|
||||
as size, padding and alignment.
|
||||
|
||||
The bufferpool has two states: active and inactive. In the inactive
|
||||
state, the bufferpool can be configured with the required allocation
|
||||
preferences. In the active state, buffers can be retrieved from and
|
||||
returned to the pool.
|
||||
|
||||
The default implementation of the bufferpool is able to allocate buffers
|
||||
from any allocator with arbitrary alignment and padding/prefix.
|
||||
|
||||
Custom implementations of the bufferpool can override the allocation and
|
||||
free algorithms of the buffers from the pool. This should allow for
|
||||
different allocation strategies such as using shared memory or hardware
|
||||
mapped memory.
|
||||
|
||||
# Negotiation
|
||||
|
||||
After a particular media format has been negotiated between two pads (using the
|
||||
CAPS event), they must agree on how to allocate buffers.
|
||||
|
||||
The srcpad will always take the initiative to negotiate the allocation
|
||||
properties. It starts with creating a GST_QUERY_ALLOCATION with the negotiated
|
||||
caps.
|
||||
|
||||
The srcpad can set the need-pool flag to TRUE in the query to optionally make the
|
||||
peer pad allocate a bufferpool. It should only do this if it is able to use
|
||||
the peer provided bufferpool.
|
||||
|
||||
It will then inspect the returned results and configure the returned pool or
|
||||
create a new pool with the returned properties when needed.
|
||||
|
||||
Buffers are then allocated by the srcpad from the negotiated pool and pushed to
|
||||
the peer pad as usual.
|
||||
|
||||
The allocation query can also return an allocator object when the buffers are of
|
||||
different sizes and can't be allocated from a pool.
|
||||
|
||||
# Allocation query
|
||||
|
||||
The allocation query has the following fields:
|
||||
|
||||
* (in) **`caps`**, GST_TYPE_CAPS: the caps that was negotiated
|
||||
|
||||
* (in) **`need-pool`**, G_TYPE_BOOLEAN: if a GstBufferPool is requested
|
||||
|
||||
* (out) **`pool`**, G_TYPE_ARRAY of structure: an array of pool configurations:
|
||||
|
||||
`` c
|
||||
struct {
|
||||
GstBufferPool *pool;
|
||||
guint size;
|
||||
guint min_buffers;
|
||||
guint max_buffers;
|
||||
}
|
||||
``
|
||||
|
||||
Use `gst_query_parse_nth_allocation_pool()` to get the values.
|
||||
|
||||
The allocator can contain multiple pool configurations. If need-pool
|
||||
was TRUE, the pool member might contain a GstBufferPool when the
|
||||
downstream element can provide one.
|
||||
|
||||
Size contains the size of the bufferpool's buffers and is never 0.
|
||||
|
||||
min_buffers and max_buffers contain the suggested min and max amount of
|
||||
buffers that should be managed by the pool.
|
||||
|
||||
The upstream element can choose to use the provided pool or make its own
|
||||
pool when none was provided or when the suggested pool was not
|
||||
acceptable.
|
||||
|
||||
The pool can then be configured with the suggested min and max amount of
|
||||
buffers or a downstream element might choose different values.
|
||||
|
||||
* (out) **`allocator`**, G_TYPE_ARRAY of structure: an array of allocator parameters that can be used.
|
||||
|
||||
``` c
|
||||
struct {
|
||||
GstAllocator *allocator;
|
||||
GstAllocationParams params;
|
||||
}
|
||||
```
|
||||
|
||||
Use `gst_query_parse_nth_allocation_param()` to get the values.
|
||||
|
||||
The element performing the query can use the allocators and its
|
||||
parameters to allocate memory for the downstream element.
|
||||
|
||||
It is also possible to configure the allocator in a provided pool.
|
||||
|
||||
* (out) **`metadata`**, G_TYPE_ARRAY of structure: an array of metadata params that can be accepted.
|
||||
|
||||
``` c
|
||||
struct {
|
||||
GType api;
|
||||
GstStructure *params;
|
||||
}
|
||||
```
|
||||
|
||||
Use `gst_query_parse_nth_allocation_meta(`) to get the values.
|
||||
|
||||
These metadata items can be accepted by the downstream element when
|
||||
placed on buffers. There is also an arbitrary `GstStructure` associated
|
||||
with the metadata that contains metadata-specific options.
|
||||
|
||||
Some bufferpools have options to enable metadata on the buffers
|
||||
allocated by the pool.
|
||||
|
||||
# Allocating from pool
|
||||
|
||||
Buffers are allocated from the pool of a pad:
|
||||
|
||||
``` c
|
||||
res = gst_buffer_pool_acquire_buffer (pool, &buffer, ¶ms);
|
||||
```
|
||||
|
||||
A `GstBuffer` that is allocated from the pool will always be writable (have a
|
||||
refcount of 1) and it will also have its pool member point to the `GstBufferPool`
|
||||
that created the buffer.
|
||||
|
||||
Buffers are refcounted in the usual way. When the refcount of the buffer
|
||||
reaches 0, the buffer is automatically returned to the pool.
|
||||
|
||||
Since all the buffers allocated from the pool keep a reference to the pool,
|
||||
when nothing else is holding a refcount to the pool, it will be finalized
|
||||
when all the buffers from the pool are unreffed. By setting the pool to
|
||||
the inactive state we can drain all buffers from the pool.
|
||||
|
||||
When the pool is in the inactive state, `gst_buffer_pool_acquire_buffer()` will
|
||||
return `GST_FLOW_FLUSHING` immediately.
|
||||
|
||||
Extra parameters can be given to the `gst_buffer_pool_acquire_buffer()` method to
|
||||
influence the allocation decision. `GST_BUFFER_POOL_FLAG_KEY_UNIT` and
|
||||
`GST_BUFFER_POOL_FLAG_DISCONT` serve as hints.
|
||||
|
||||
When the bufferpool is configured with a maximum number of buffers, allocation
|
||||
will block when all buffers are outstanding until a buffer is returned to the
|
||||
pool. This behaviour can be changed by specifying the
|
||||
`GST_BUFFER_POOL_FLAG_DONTWAIT` flag in the parameters. With this flag set,
|
||||
allocation will return `GST_FLOW_EOS` when the pool is empty.
|
||||
|
||||
# Renegotiation
|
||||
|
||||
Renegotiation of the bufferpool might need to be performed when the
|
||||
configuration of the pool changes. Changes can be in the buffer size
|
||||
(because of a caps change), alignment or number of
|
||||
buffers.
|
||||
|
||||
## Downstream
|
||||
|
||||
When the upstream element wants to negotiate a new format, it might need
|
||||
to renegotiate a new bufferpool configuration with the downstream element.
|
||||
This can, for example, happen when the buffer size changes.
|
||||
|
||||
We can not just reconfigure the existing bufferpool because there might
|
||||
still be outstanding buffers from the pool in the pipeline. Therefore we
|
||||
need to create a new bufferpool for the new configuration while we let the
|
||||
old pool drain.
|
||||
|
||||
Implementations can choose to reuse the same bufferpool object and wait for
|
||||
the drain to finish before reconfiguring the pool.
|
||||
|
||||
The element that wants to renegotiate a new bufferpool uses exactly the same
|
||||
algorithm as when it first started. It will negotiate caps first then use the
|
||||
ALLOCATION query to get and configure the new pool.
|
||||
|
||||
## upstream
|
||||
|
||||
When a downstream element wants to negotiate a new format, it will send a
|
||||
RECONFIGURE event upstream. This instructs upstream to renegotiate both
|
||||
the format and the bufferpool when needed.
|
||||
|
||||
A pipeline reconfiguration happens when new elements are added or removed from
|
||||
the pipeline or when the topology of the pipeline changes. Pipeline
|
||||
reconfiguration also triggers possible renegotiation of the bufferpool and
|
||||
caps.
|
||||
|
||||
A RECONFIGURE event tags each pad it travels on as needing reconfiguration.
|
||||
The next buffer allocation will then require the renegotiation or
|
||||
reconfiguration of a pool.
|
||||
|
||||
# Shutting down
|
||||
|
||||
In push mode, a source pad is responsible for setting the pool to the
|
||||
inactive state when streaming stops. The inactive state will unblock any pending
|
||||
allocations so that the element can shut down.
|
||||
|
||||
In pull mode, the sink element should set the pool to the inactive state when
|
||||
shutting down so that the peer `_get_range()` function can unblock.
|
||||
|
||||
In the inactive state, all the buffers that are returned to the pool will
|
||||
automatically be freed by the pool and new allocations will fail.
|
||||
|
||||
# Use cases
|
||||
|
||||
## - `videotestsrc ! xvimagesink`
|
||||
|
||||
* Before videotestsrc can output a buffer, it needs to negotiate caps and
|
||||
a bufferpool with the downstream peer pad.
|
||||
|
||||
* First it will negotiate a suitable format with downstream according to the
|
||||
normal rules. It will send a CAPS event downstream with the negotiated
|
||||
configuration.
|
||||
|
||||
* Then it does an ALLOCATION query. It will use the returned bufferpool or
|
||||
configures its own bufferpool with the returned parameters. The bufferpool is
|
||||
initially in the inactive state.
|
||||
|
||||
* The ALLOCATION query lists the desired configuration of the downstream
|
||||
xvimagesink, which can have specific alignment and/or min/max amount of
|
||||
buffers.
|
||||
|
||||
* videotestsrc updates the configuration of the bufferpool, it will likely set
|
||||
the min buffers to 1 and the size of the desired buffers. It then updates the
|
||||
bufferpool configuration with the new properties.
|
||||
|
||||
* When the configuration is successfully updated, videotestsrc sets the
|
||||
bufferpool to the active state. This preallocates the buffers in the pool (if
|
||||
needed). This operation can fail when there is not enough memory available.
|
||||
Since the bufferpool is provided by xvimagesink, it will allocate buffers
|
||||
backed by an XvImage and pointing to shared memory with the X server.
|
||||
|
||||
* If the bufferpool is successfully activated, videotestsrc can acquire
|
||||
a buffer from the pool, fill in the data and push it out to xvimagesink.
|
||||
|
||||
* xvimagesink can know that the buffer originated from its pool by following
|
||||
the pool member.
|
||||
|
||||
* when shutting down, videotestsrc will set the pool to the inactive state,
|
||||
this will cause further allocations to fail and currently allocated buffers to
|
||||
be freed. videotestsrc will then free the pool and stop streaming.
|
||||
|
||||
## - ``videotestsrc ! queue ! myvideosink``
|
||||
|
||||
* In this second use case we have a videosink that can at most allocate 3 video
|
||||
buffers.
|
||||
|
||||
* Again videotestsrc will have to negotiate a bufferpool with the peer element.
|
||||
For this it will perform the ALLOCATION query which queue will proxy to its
|
||||
downstream peer element.
|
||||
|
||||
* The bufferpool returned from myvideosink will have a max_buffers set to 3.
|
||||
queue and videotestsrc can operate with this upper limit because none of those
|
||||
elements require more than that amount of buffers for temporary storage.
|
||||
|
||||
* Myvideosink's bufferpool will then be configured with the size of the buffers
|
||||
for the negotiated format and according to the padding and alignment rules.
|
||||
When videotestsrc sets the pool to active, the 3 video buffers will be
|
||||
preallocated in the pool.
|
||||
|
||||
* videotestsrc acquires a buffer from the configured pool on its srcpad and
|
||||
pushes this into the queue. When videotestsrc has acquired and pushed 3 frames,
|
||||
the next call to gst_buffer_pool_acquire_buffer() will block (assuming the
|
||||
GST_BUFFER_POOL_FLAG_DONTWAIT is not specified).
|
||||
|
||||
* When the queue has pushed out a buffer and the sink has rendered it, the
|
||||
refcount of the buffer reaches 0 and the buffer is recycled in the pool. This
|
||||
will wake up the videotestsrc that was blocked, waiting for more buffers and
|
||||
will make it produce the next buffer.
|
||||
|
||||
* In this setup, there are at most 3 buffers active in the pipeline and the
|
||||
videotestsrc is rate limited by the rate at which buffers are recycled in the
|
||||
bufferpool.
|
||||
|
||||
* When shutting down, videotestsrc will first set the bufferpool on the srcpad
|
||||
to inactive. This causes any pending (blocked) acquire to return with
|
||||
a FLUSHING result and causes the streaming thread to pause.
|
||||
|
||||
## - `.. ! myvideodecoder ! queue ! fakesink`
|
||||
|
||||
* In this case, the myvideodecoder requires buffers to be aligned to 128 bytes
|
||||
and padded with 4096 bytes. The pipeline starts out with the decoder linked to
|
||||
a fakesink but we will then dynamically change the sink to one that can provide
|
||||
a bufferpool.
|
||||
|
||||
* When myvideodecoder negotiates the size with the downstream fakesink element,
|
||||
it will receive a NULL bufferpool because fakesink does not provide
|
||||
a bufferpool. It will then select its own custom bufferpool to start the data
|
||||
transfer.
|
||||
|
||||
* At some point we block the queue srcpad, unlink the queue from the fakesink,
|
||||
link a new sink and set the new sink to the PLAYING state. Linking the new sink
|
||||
would automatically send a RECONFIGURE event upstream and, through queue,
|
||||
inform myvideodecoder that it should renegotiate its bufferpool because
|
||||
downstream has been reconfigured.
|
||||
|
||||
* Before pushing the next buffer, myvideodecoder has to renegotiate a new
|
||||
bufferpool. To do this, it performs the usual bufferpool negotiation algorithm.
|
||||
If it can obtain and configure a new bufferpool from downstream, it sets its
|
||||
own (old) pool to inactive and unrefs it. This will eventually drain and unref
|
||||
the old bufferpool.
|
||||
|
||||
* The new bufferpool is set as the new bufferpool for the srcpad and sinkpad of
|
||||
the queue and set to the active state.
|
||||
|
||||
## - `.. ! myvideodecoder ! queue ! myvideosink `
|
||||
|
||||
* myvideodecoder has negotiated a bufferpool with the downstream myvideosink to
|
||||
handle buffers of size 320x240. It has now detected a change in the video
|
||||
format and needs to renegotiate to a resolution of 640x480. This requires it to
|
||||
negotiate a new bufferpool with a larger buffer size.
|
||||
|
||||
* When myvideodecoder needs to get the bigger buffer, it starts the negotiation
|
||||
of a new bufferpool. It queries a bufferpool from downstream, reconfigures it
|
||||
with the new configuration (which includes the bigger buffer size) and sets the
|
||||
bufferpool to active. The old pool is inactivated and unreffed, which causes
|
||||
the old format to drain.
|
||||
|
||||
* It then uses the new bufferpool for allocating new buffers of the new
|
||||
dimension.
|
||||
|
||||
* If at some point, the decoder wants to switch to a lower resolution again, it
|
||||
can choose to use the current pool (which has buffers that are larger than the
|
||||
required size) or it can choose to renegotiate a new bufferpool.
|
||||
|
||||
## - `.. ! myvideodecoder ! videoscale ! myvideosink`
|
||||
|
||||
* myvideosink is providing a bufferpool for upstream elements and wants to
|
||||
change the resolution.
|
||||
|
||||
* myvideosink sends a RECONFIGURE event upstream to notify upstream that a new
|
||||
format is desirable. Upstream elements try to negotiate a new format and
|
||||
bufferpool before pushing out a new buffer. The old bufferpools are drained in
|
||||
the regular way.
|
141
markdown/design/caps.md
Normal file
141
markdown/design/caps.md
Normal file
|
@ -0,0 +1,141 @@
|
|||
# Caps
|
||||
|
||||
Caps are lightweight refcounted objects describing media types. They are
|
||||
composed of an array of GstStructures plus, optionally, a
|
||||
GstCapsFeatures set for the GstStructure.
|
||||
|
||||
Caps are exposed on GstPadTemplates to describe all possible types a
|
||||
given pad can handle. They are also stored in the registry along with a
|
||||
description of the element.
|
||||
|
||||
Caps are exposed on the element pads via CAPS and `ACCEPT_CAPS` queries.
|
||||
|
||||
This function describes the possible types that the pad can handle or
|
||||
produce ([negotiation](design/negotiation.md)).
|
||||
|
||||
Various methods exist to work with the media types such as subtracting
|
||||
or intersecting.
|
||||
|
||||
## Operations
|
||||
|
||||
# Fixating
|
||||
|
||||
Caps are fixed if they only contain a single structure and this
|
||||
structure is fixed. A structure is fixed if none of the fields of the
|
||||
structure is an unfixed type, for example a range, list or array.
|
||||
|
||||
For fixating caps only the first structure is kept as the order of
|
||||
structures is meant to express the preferences for the different
|
||||
structures. Afterwards, each unfixed field of this structure is set to
|
||||
the value that makes most sense for the media format by the element or
|
||||
pad implementation and then every remaining unfixed field is set to an
|
||||
arbitrary value that is a subset of the unfixed field’s values.
|
||||
|
||||
EMPTY caps are fixed caps and ANY caps are not. Caps with ANY caps
|
||||
features are not fixed.
|
||||
|
||||
# Subset
|
||||
|
||||
One caps "A" is a subset of another caps "B" if for each structure in
|
||||
"A" there exists a structure in "B" that is a superset of the structure
|
||||
in "A".
|
||||
|
||||
A structure "a" is the subset of a structure "b" if it has the same
|
||||
structure name, the same caps features and each field in "b" exists in
|
||||
"a" and the value of the field in "a" is a subset of the value of the
|
||||
field in "b". "a" can have additional fields that are not in "b".
|
||||
|
||||
EMPTY caps are a subset of every other caps. Every caps are a subset of
|
||||
ANY caps.
|
||||
|
||||
# Equality
|
||||
|
||||
Caps "A" and "B" are equal if "A" is a subset of "B" and "B" is a subset
|
||||
of "A". This means that both caps are expressing the same possibilities
|
||||
but their structures can still be different if they contain unfixed
|
||||
fields.
|
||||
|
||||
# Intersection
|
||||
|
||||
The intersection of caps "A" and caps "B" are the caps that contain the
|
||||
intersection of all their structures with each other.
|
||||
|
||||
The intersection of structure "a" and structure "b" is empty if their
|
||||
structure name or their caps features are not equal, or if "a" and "b"
|
||||
contain the same field but the intersection of both field values is
|
||||
empty. If one structure contains a field that is not existing in the
|
||||
other structure it will be copied over to the intersection with the same
|
||||
value.
|
||||
|
||||
The intersection with ANY caps is always the other caps and the
|
||||
intersection with EMPTY caps is always EMPTY.
|
||||
|
||||
# Union
|
||||
|
||||
The union of caps "A" and caps "B" are the caps that contain the union
|
||||
of all their structures with each other.
|
||||
|
||||
The union of structure "a" and structure "b" are the two structures "a"
|
||||
and "b" if the structure names or caps features are not equal.
|
||||
Otherwise, the union is the structure that contains the union of each
|
||||
fields value. If a field is only in one of the two structures it is not
|
||||
contained in the union.
|
||||
|
||||
The union with ANY caps is always ANY and the union with EMPTY caps is
|
||||
always the other caps.
|
||||
|
||||
# Subtraction
|
||||
|
||||
The subtraction of caps "A" from caps "B" is the most generic subset of
|
||||
"B" that has an empty intersection with "A" but only contains structures
|
||||
with names and caps features that are existing in "B".
|
||||
|
||||
## Basic Rules
|
||||
|
||||
# Semantics of caps and their usage
|
||||
|
||||
A caps can contain multiple structures, in which case any of the
|
||||
structures would be acceptable. The structures are in the preferred
|
||||
order of the creator of the caps, with the preferred structure being
|
||||
first and during negotiation of caps this order should be considered to
|
||||
select the most optimal structure.
|
||||
|
||||
Each of these structures has a name that specifies the media type, e.g.
|
||||
"video/x-theora" to specify Theora video. Additional fields in the
|
||||
structure add additional constraints and/or information about the media
|
||||
type, like the width and height of a video frame, or the codec profile
|
||||
that is used. These fields can be non-fixed (e.g. ranges) for non-fixed
|
||||
caps but must be fixated to a fixed value during negotiation. If a field
|
||||
is included in the caps returned by a pad via the CAPS query, it imposes
|
||||
an additional constraint during negotiation. The caps in the end must
|
||||
have this field with a value that is a subset of the non-fixed value.
|
||||
Additional fields that are added in the negotiated caps give additional
|
||||
information about the media but are treated as optional. Information
|
||||
that can change for every buffer and is not relevant during negotiation
|
||||
must not be stored inside the caps.
|
||||
|
||||
For each of the structures in caps it is possible to store caps
|
||||
features. The caps features are expressing additional requirements for a
|
||||
specific structure, and only structures with the same name *and* equal
|
||||
caps features are considered compatible. Caps features can be used to
|
||||
require a specific memory representation or a specific meta to be set on
|
||||
buffers, for example a pad could require for a specific structure that
|
||||
it is passed EGLImage memory or buffers with the video meta. If no caps
|
||||
features are provided for a structure, it is assumed that system memory
|
||||
is required unless later negotiation steps (e.g. the ALLOCATION query)
|
||||
detect that something else can be used. The special ANY caps features
|
||||
can be used to specify that any caps feature would be accepted, for
|
||||
example if the buffer memory is not touched at all.
|
||||
|
||||
# Compatibility of caps
|
||||
|
||||
Pads can be linked when the caps of both pads are compatible. This is
|
||||
the case when their intersection is not empty.
|
||||
|
||||
For checking if a pad actually supports a fixed caps an intersection is
|
||||
not enough. Instead the fixed caps must be at least a subset of the
|
||||
pad’s caps but pads can introduce additional constraints which would
|
||||
be checked in the `ACCEPT_CAPS` query handler.
|
||||
|
||||
Data flow can only happen after pads have decided on common fixed caps.
|
||||
These caps are distributed to both pads with the CAPS event.
|
83
markdown/design/clocks.md
Normal file
83
markdown/design/clocks.md
Normal file
|
@ -0,0 +1,83 @@
|
|||
# Clocks
|
||||
|
||||
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 expressed in nanoseconds. Since the
|
||||
baseline of the clock is undefined, the clock time returned is not
|
||||
meaningful in itself, what matters are the deltas between two clock
|
||||
times. The time reported by the clock is called the `absolute_time`.
|
||||
|
||||
## Clock Selection
|
||||
|
||||
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 the pipeline goes to PLAYING. Whenever
|
||||
an element is added/removed from the pipeline, this selection will be
|
||||
redone in the next state change to PLAYING. Adding an element that can
|
||||
provide a clock will post a `GST_MESSAGE_CLOCK_PROVIDE` message on the
|
||||
bus to inform parent bins of the fact that a clock recalculation is
|
||||
needed.
|
||||
|
||||
When a clock is selected, a `NEW_CLOCK` message is posted on the bus
|
||||
signaling the clock to the application.
|
||||
|
||||
When the element that provided the clock is removed from the pipeline, a
|
||||
`CLOCK_LOST` message is posted. The application must then set the
|
||||
pipeline to PAUSED and PLAYING again in order to let the pipeline select
|
||||
a new clock and distribute a new base time.
|
||||
|
||||
The clock selection is performed as part of the state change from PAUSED
|
||||
to PLAYING and is described in [states](design/states.md).
|
||||
|
||||
## 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 `gstclock_id_wait_async()`. Both these
|
||||
calls can be interrupted with the `gst_clock_id_unschedule()` call. If
|
||||
the blocking wait is unscheduled a 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 owner is
|
||||
responsible for unreffing the ids itself. This holds true for both
|
||||
periodic and single shot notifications. The reason being that the owner
|
||||
of the ClockID has to keep a handle to the ID to unblock the wait on
|
||||
FLUSHING events or state changes and if we unref it automatically, the
|
||||
handle might be invalid.
|
||||
|
||||
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. Some clocks however do not progress when the element
|
||||
that provided the clock is not PLAYING.
|
||||
|
||||
## Clock implementations
|
||||
|
||||
The GStreamer core provides a GstSystemClock based on the system time.
|
||||
Asynchronous callbacks are scheduled from an internal thread.
|
||||
|
||||
Clock implementers 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.
|
61
markdown/design/context.md
Normal file
61
markdown/design/context.md
Normal file
|
@ -0,0 +1,61 @@
|
|||
# Context
|
||||
|
||||
GstContext is a container object, containing a type string and a generic
|
||||
GstStructure. It is used to store and propagate context information in a
|
||||
pipeline, like device handles, display server connections and other
|
||||
information that should be shared between multiple elements in a
|
||||
pipeline.
|
||||
|
||||
For sharing context objects and distributing them between application
|
||||
and elements in a pipeline, there are downstream queries, upstream
|
||||
queries, messages and functions to set a context on a complete pipeline.
|
||||
|
||||
## Context types
|
||||
|
||||
Context type names should be unique and be put in appropriate
|
||||
namespaces, to prevent name conflicts, e.g. "gst.egl.EGLDisplay". Only
|
||||
one specific type is allowed per context type name.
|
||||
|
||||
## Elements
|
||||
|
||||
Elements that need a specific context for their operation would do the
|
||||
following steps until one succeeds:
|
||||
|
||||
1) Check if the element already has a context of the specific type,
|
||||
i.e. it was previously set via gst_element_set_context().
|
||||
|
||||
2) Query downstream with GST_QUERY_CONTEXT for the context and check if
|
||||
downstream already has a context of the specific type
|
||||
|
||||
3) Query upstream with GST_QUERY_CONTEXT for the context and check if
|
||||
upstream already has a context of the specific type
|
||||
|
||||
4) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with the required
|
||||
context types and afterwards check if a usable context was set now
|
||||
as in 1). The message could be handled by the parent bins of the
|
||||
element and the application.
|
||||
|
||||
4) Create a context by itself and post a GST_MESSAGE_HAVE_CONTEXT message
|
||||
on the bus.
|
||||
|
||||
Bins will propagate any context that is set on them to their child
|
||||
elements via gst\_element\_set\_context(). Even to elements added after
|
||||
a given context has been set.
|
||||
|
||||
Bins can handle the GST\_MESSAGE\_NEED\_CONTEXT message, can filter both
|
||||
messages and can also set different contexts for different pipeline
|
||||
parts.
|
||||
|
||||
## Applications
|
||||
|
||||
Applications can set a specific context on a pipeline or elements inside
|
||||
a pipeline with gst\_element\_set\_context().
|
||||
|
||||
If an element inside the pipeline needs a specific context, it will post
|
||||
a GST\_MESSAGE\_NEED\_CONTEXT message on the bus. The application can
|
||||
now create a context of the requested type or pass an already existing
|
||||
context to the element (or to the complete pipeline).
|
||||
|
||||
Whenever an element creates a context internally it will post a
|
||||
GST\_MESSAGE\_HAVE\_CONTEXT message on the bus. Bins will cache these
|
||||
contexts and pass them to any future element that requests them.
|
65
markdown/design/controller.md
Normal file
65
markdown/design/controller.md
Normal file
|
@ -0,0 +1,65 @@
|
|||
# Controller
|
||||
|
||||
The controller subsystem allows to automate element property changes. It
|
||||
works so that all parameter changes are time based and elements request
|
||||
property updates at processing time.
|
||||
|
||||
## Element view
|
||||
|
||||
Elements don’t need to do much. They need to: - mark object properties
|
||||
that can be changed while processing with GST\_PARAM\_CONTROLLABLE -
|
||||
call gst\_object\_sync\_values (self, timestamp) in the processing
|
||||
function before accessing the parameters.
|
||||
|
||||
All ordered property types can be automated (int, double, boolean,
|
||||
enum). Other property types can also be automated by using special
|
||||
control bindings. One can e.g. write a control-binding that updates a
|
||||
text property based on timestamps.
|
||||
|
||||
## Application view
|
||||
|
||||
Applications need to setup the property automation. For that they need
|
||||
to create a GstControlSource and attach it to a property using
|
||||
GstControlBinding. Various control-sources and control-bindings exist.
|
||||
All control sources produce control value sequences in the form of
|
||||
gdouble values. The control bindings map them to the value range and
|
||||
type of the bound property.
|
||||
|
||||
One control-source can be attached to one or more properties at the same
|
||||
time. If it is attached multiple times, then each control-binding will
|
||||
scale and convert the control values to the target property type and
|
||||
range.
|
||||
|
||||
One can create complex control-curves by using a
|
||||
GstInterpolationControlSource. This allows the classic user editable
|
||||
control-curve (often seen in audio/video editors). Another way is to use
|
||||
computed control curves. GstLFOControlSource can generate various
|
||||
repetitive signals. Those can be made more complex by chaining the
|
||||
control sources. One can attach another control-source to e.g. modulate
|
||||
the frequency of the first GstLFOControlSource.
|
||||
|
||||
In most cases GstControlBindingDirect will be the binding to be used.
|
||||
Other control bindings are there to handle special cases, such as having
|
||||
1-4 control- sources and combine their values into a single guint to
|
||||
control a rgba-color property.
|
||||
|
||||
## TODO
|
||||
|
||||
* control-source value ranges - control sources should ideally emit values
|
||||
between \[0.0 and 1.0\] - right now lfo-control-sources emits values
|
||||
between \[-1.0 and 1.0\] - we can make control-sources announce that or
|
||||
fix it in a lfo2-control-source
|
||||
|
||||
* ranged-control-binding - it might be a nice thing to have a
|
||||
control-binding that has scale and offset properties - when attaching a
|
||||
control-source to e.g. volume, one needs to be aware that the values go
|
||||
from \[0.0 to 4.0\] - we can also have a "mapping-mode"={AS\_IS,
|
||||
TRANSFORMED} on direct-control-binding and two extra properties that are
|
||||
used in TRANSFORMED mode
|
||||
|
||||
* control-setup descriptions - it would be nice to have a way to parse a
|
||||
textual control-setup description. This could be used in gst-launch and
|
||||
in presets. It needs to be complemented with a formatter (for the preset
|
||||
storage or e.g. for debug logging). - this could be function-style:
|
||||
direct(control-source=lfo(waveform=*sine*,offset=0.5)) or gst-launch
|
||||
style (looks weird) lfo wave=sine offset=0.5 \! direct .control-source
|
78
markdown/design/conventions.md
Normal file
78
markdown/design/conventions.md
Normal file
|
@ -0,0 +1,78 @@
|
|||
# Documentation conventions
|
||||
|
||||
Due to the potential for exponential growth, several abbreviating
|
||||
conventions will be used throughout this documentation. These
|
||||
conventions have grown primarily from extremely in-depth discussions of
|
||||
the architecture in IRC. This has verified the safety of these
|
||||
conventions, if used properly. There are no known namespace conflicts as
|
||||
long as context is rigorously observed.
|
||||
|
||||
## Object classes
|
||||
|
||||
Since everything starts with Gst, we will generally refer to objects by
|
||||
the shorter name, i.e. Element or Pad. These names will always have
|
||||
their first letter capitalized.
|
||||
|
||||
## Function names
|
||||
|
||||
Within the context of a given object, functions defined in that object’s
|
||||
header and/or source file will have their object-specific prefix
|
||||
stripped. For instance, gst\_element\_add\_pad() would be referred to as
|
||||
simply *add\_pad(). Note that the trailing parentheses should always be
|
||||
present, but sometimes may not be. A prefixing underscore (*) will
|
||||
always tell you it’s a function, however, regardless of the presence or
|
||||
absence of the trailing parentheses.
|
||||
|
||||
## defines and enums
|
||||
|
||||
Values and macros defined as enums and preprocessor macros will be
|
||||
referred to in all capitals, as per their definition. This includes
|
||||
object flags and element states, as well as general enums. Examples are
|
||||
the states NULL, READY, PLAYING, and PAUSED; the element flags
|
||||
LOCKED\_STATE , and state return values SUCCESS, FAILURE, and ASYNC.
|
||||
Where there is a prefix, as in the element flags, it is usually dropped
|
||||
and implied. Note however that element flags should be cross-checked
|
||||
with the header, as there are currently two conventions in use: with and
|
||||
without *FLAGS* in the middle.
|
||||
|
||||
## Drawing conventions
|
||||
|
||||
When drawing pictures the following conventions apply:
|
||||
|
||||
### objects
|
||||
|
||||
Objects are drawn with a box like:
|
||||
|
||||
+------+
|
||||
| |
|
||||
+------+
|
||||
|
||||
### pointers
|
||||
|
||||
a pointer to an object.
|
||||
|
||||
```
|
||||
+-----+
|
||||
*--->| |
|
||||
+-----+
|
||||
```
|
||||
|
||||
an invalid pointer, this is a pointer that should not be used.
|
||||
|
||||
*-//->
|
||||
|
||||
### elements
|
||||
|
||||
```
|
||||
+----------+
|
||||
| name |
|
||||
sink src
|
||||
+----------+
|
||||
```
|
||||
|
||||
### pad links
|
||||
|
||||
-----+ +---
|
||||
| |
|
||||
src--sink
|
||||
-----+ +---
|
215
markdown/design/draft-klass.md
Normal file
215
markdown/design/draft-klass.md
Normal file
|
@ -0,0 +1,215 @@
|
|||
# Element Klass definition
|
||||
|
||||
## Purpose
|
||||
|
||||
Applications should be able to retrieve elements from the registry of
|
||||
existing elements based on specific capabilities or features of the
|
||||
element.
|
||||
|
||||
A playback application might want to retrieve all the elements that can
|
||||
be used for visualisation, for example, or a video editor might want to
|
||||
select all video effect filters.
|
||||
|
||||
The topic of defining the klass of elements should be based on use
|
||||
cases.
|
||||
|
||||
A list of classes that are used in a installation can be generated
|
||||
using: gst-inspect-1.0 -a | grep -ho Class:.\* | cut -c8- | sed
|
||||
"s/\\//\\\\n/g" | sort | uniq
|
||||
|
||||
## Proposal
|
||||
|
||||
The GstElementDetails contains a field named klass that is a pointer to
|
||||
a string describing the element type.
|
||||
|
||||
In this document we describe the format and contents of the string.
|
||||
Elements should adhere to this specification although that is not
|
||||
enforced to allow for wild (application specific) customisation.
|
||||
|
||||
###string format
|
||||
|
||||
<keyword>['/'<keyword]*
|
||||
|
||||
The string consists of an _unordered_ list of keywords separated with a '/'
|
||||
character. While the / suggests a hierarchy, this is _not_ the case.
|
||||
|
||||
### keyword categories
|
||||
|
||||
- functional
|
||||
|
||||
Categories are base on _intended usage_ of the element. Some elements
|
||||
might have other side-effects (especially for filers/effects). The purpose
|
||||
is to list enough keywords so that applications can do meaningful filtering,
|
||||
not to completely describe the functionality, that is expressed in caps etc..
|
||||
|
||||
- Source : produces data
|
||||
|
||||
- Sink : consumes data
|
||||
|
||||
- Filter : filters/transforms data, no modification on the data is
|
||||
intended (although it might be unavoidable). The filter can
|
||||
decide on input and output caps independently of the stream
|
||||
contents (GstBaseTransform).
|
||||
|
||||
- Effect : applies an effect to some data, changes to data are
|
||||
intended. Examples are colorbalance, volume. These elements can
|
||||
also be implemented with GstBaseTransform.
|
||||
|
||||
- Demuxer : splits audio, video, … from a stream
|
||||
|
||||
- Muxer : interleave audio, video, … into one stream, this is like
|
||||
mixing but without losing or degrading each separate input
|
||||
stream. The reverse operation is possible with a Demuxer that
|
||||
reproduces the exact same input streams.
|
||||
|
||||
- Decoder : decodes encoded data into a raw format, there is
|
||||
typically no relation between input caps and output caps. The
|
||||
output caps are defined in the stream data. This separates the
|
||||
Decoder from the Filter and Effect.
|
||||
|
||||
- Encoder : encodes raw data into an encoded format.
|
||||
|
||||
- Mixer : combine audio, video, .. this is like muxing but with
|
||||
applying some algorithm so that the individual streams are not
|
||||
extractable anymore, there is therefore no reverse operation to
|
||||
mixing. (audio mixer, video mixer, …)
|
||||
|
||||
- Converter : convert audio into video, text to audio, … The
|
||||
converter typically works on raw types only. The source media
|
||||
type is listed first.
|
||||
|
||||
- Analyzer : reports about the stream contents.
|
||||
|
||||
- Control : controls some aspect of a hardware device
|
||||
|
||||
- Extracter : extracts tags/headers from a stream
|
||||
|
||||
- Formatter : adds tags/headers to a stream
|
||||
|
||||
- Connector : allows for new connections in the pipeline. (tee, …)
|
||||
|
||||
- …
|
||||
|
||||
- Based on media type
|
||||
|
||||
Purpose is to make a selection for elements operating on the different
|
||||
types of media. An audio application must be able to filter out the
|
||||
elements operating on audio, for example.
|
||||
|
||||
- Audio : operates on audio data
|
||||
|
||||
- Video : operates on video data
|
||||
|
||||
- Image : operates on image data. Usually this media type can also
|
||||
be used to make a video stream in which case it is added
|
||||
together with the Video media type.
|
||||
|
||||
- Text : operates on text data
|
||||
|
||||
- Metadata : operates on metadata
|
||||
|
||||
- …
|
||||
|
||||
- Extra features
|
||||
|
||||
The purpose is to further specialize the element, mostly for
|
||||
application specific needs.
|
||||
|
||||
- Network : element is used in networked situations
|
||||
|
||||
- Protocol : implements some protocol (RTSP, HTTP, …)
|
||||
|
||||
- Payloader : encapsulate as payload (RTP, RDT,.. )
|
||||
|
||||
- Depayloader : strip a payload (RTP, RDT,.. )
|
||||
|
||||
- RTP : intended to be used in RTP applications
|
||||
|
||||
- Device : operates on some hardware device (disk, network, audio
|
||||
card, video card, usb, …)
|
||||
|
||||
- Visualisation : intended to be used for audio visualisation
|
||||
|
||||
- Debug : intended usage is more for debugging purposes.
|
||||
|
||||
- Categories found, but not yet in one of the above lists
|
||||
|
||||
- Bin : playbin, decodebin, bin, pipeline
|
||||
|
||||
- Codec : lots of decoders, encoder, demuxers should be removed?
|
||||
|
||||
- Generic : should be removed?
|
||||
|
||||
- File : like network, should go to Extra?
|
||||
|
||||
- Editor : gnonlin, textoverlays
|
||||
|
||||
- DVD, GDP, LADSPA, Parser, Player, Subtitle, Testing, …
|
||||
|
||||
3\) suggested order:
|
||||
|
||||
<functional>[/<media type>]*[/<extra...>]*
|
||||
|
||||
4\) examples:
|
||||
|
||||
apedemux : Extracter/Metadata
|
||||
audiotestsrc : Source/Audio
|
||||
autoaudiosink : Sink/Audio/Device
|
||||
cairotimeoverlay : Mixer/Video/Text
|
||||
dvdec : Decoder/Video
|
||||
dvdemux : Demuxer
|
||||
goom : Converter/Audio/Video
|
||||
id3demux : Extracter/Metadata
|
||||
udpsrc : Source/Network/Protocol/Device
|
||||
videomixer : Mixer/Video
|
||||
videoconvert : Filter/Video (intended use to convert video with as little
|
||||
visible change as possible)
|
||||
vertigotv : Effect/Video (intended use is to change the video)
|
||||
volume : Effect/Audio (intended use is to change the audio data)
|
||||
vorbisdec : Decoder/Audio
|
||||
vorbisenc : Encoder/Audio
|
||||
oggmux : Muxer
|
||||
adder : Mixer/Audio
|
||||
videobox : Effect/Video
|
||||
alsamixer : Control/Audio/Device
|
||||
audioconvert : Filter/Audio
|
||||
audioresample : Filter/Audio
|
||||
xvimagesink : Sink/Video/Device
|
||||
navseek : Filter/Debug
|
||||
decodebin : Decoder/Demuxer
|
||||
level : Filter/Analyzer/Audio
|
||||
tee : Connector/Debug
|
||||
|
||||
### open issues:
|
||||
|
||||
- how to differentiate physical devices from logical ones?
|
||||
autoaudiosink : Sink/Audio/Device alsasink : Sink/Audio/Device
|
||||
|
||||
## Use cases
|
||||
|
||||
- get a list of all elements implementing a video effect (pitivi):
|
||||
|
||||
klass.contains (Effect & Video)
|
||||
|
||||
- get list of muxers (pitivi):
|
||||
|
||||
klass.contains (Muxer)
|
||||
|
||||
- get list of video encoders (pitivi):
|
||||
|
||||
klass.contains (Encoder & video)
|
||||
|
||||
- Get a list of all audio/video visualisations (totem):
|
||||
|
||||
klass.contains (Visualisation)
|
||||
|
||||
- Get a list of all decoders/demuxer/metadata parsers/vis (playbin):
|
||||
|
||||
klass.contains (Visualisation | Demuxer | Decoder | (Extractor & Metadata))
|
||||
|
||||
- Get a list of elements that can capture from an audio device
|
||||
(gst-properties):
|
||||
|
||||
klass.contains (Source & Audio & Device)
|
||||
|
||||
- filters out audiotestsrc, since it is not a device
|
194
markdown/design/draft-metadata.md
Normal file
194
markdown/design/draft-metadata.md
Normal file
|
@ -0,0 +1,194 @@
|
|||
# Metadata
|
||||
|
||||
This draft recaps the current metadata handling in GStreamer and
|
||||
proposes some additions.
|
||||
|
||||
## Supported Metadata standards
|
||||
|
||||
The paragraphs below list supported native metadata standards sorted by
|
||||
type and then in alphabetical order. Some standards have been extended
|
||||
to support additional metadata. GStreamer already supports all of those
|
||||
to some extend. This is showns in the table below as either \[--\],
|
||||
\[r-\], \[-w\] or \[rw\] depending on read/write support (08.Feb.2010).
|
||||
|
||||
### Audio
|
||||
- mp3
|
||||
* ID3v2: \[rw]
|
||||
* http://www.id3.org/Developer_Information
|
||||
* ID3v1: [rw]
|
||||
* http://www.id3.org/ID3v1
|
||||
* XMP: \[--] (inside ID3v2 PRIV tag of owner XMP)
|
||||
* http://www.adobe.com/devnet/xmp/
|
||||
- ogg/vorbis
|
||||
* vorbiscomment: \[rw]
|
||||
* http://www.xiph.org/vorbis/doc/v-comment.html
|
||||
* http://wiki.xiph.org/VorbisComment
|
||||
- wav
|
||||
* LIST/INFO chunk: \[rw]
|
||||
* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/RIFF.html#Info
|
||||
* http://www.kk.iij4u.or.jp/~kondo/wave/mpidata.txt
|
||||
* XMP: \[--]
|
||||
* http://www.adobe.com/devnet/xmp/
|
||||
|
||||
### Video
|
||||
- 3gp
|
||||
* {moov,trak}.udta: \[rw]
|
||||
* http://www.3gpp.org/ftp/Specs/html-info/26244.htm
|
||||
* ID3V2: \[--]
|
||||
* http://www.3gpp.org/ftp/Specs/html-info/26244.htm
|
||||
* http://www.mp4ra.org/specs.html#id3v2
|
||||
- avi
|
||||
* LIST/INFO chunk: \[rw]
|
||||
* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/RIFF.html#Info
|
||||
* http://www.kk.iij4u.or.jp/~kondo/wave/mpidata.txt
|
||||
* XMP: \[--] (inside "_PMX" chunk)
|
||||
* http://www.adobe.com/devnet/xmp/
|
||||
- asf
|
||||
* ??:
|
||||
* XMP: \[--]
|
||||
* http://www.adobe.com/devnet/xmp/
|
||||
- flv \[--]
|
||||
* XMP: (inside onXMPData script data tag)
|
||||
* http://www.adobe.com/devnet/xmp/
|
||||
- mkv
|
||||
* tags: \[rw]
|
||||
* http://www.matroska.org/technical/specs/tagging/index.html
|
||||
- mov
|
||||
* XMP: \[--] (inside moov.udta.XMP_ box)
|
||||
* http://www.adobe.com/devnet/xmp/
|
||||
- mp4
|
||||
* {moov,trak}.udta: \[rw]
|
||||
* http://standards.iso.org/ittf/PubliclyAvailableStandards/c051533_ISO_IEC_14496-12_2008.zip
|
||||
* moov.udta.meta.ilst: \[rw]
|
||||
* http://atomicparsley.sourceforge.net/
|
||||
* http://atomicparsley.sourceforge.net/mpeg-4files.html
|
||||
* ID3v2: \[--]
|
||||
* http://www.mp4ra.org/specs.html#id3v2
|
||||
* XMP: \[--] (inside UUID box)
|
||||
* http://www.adobe.com/devnet/xmp/
|
||||
- mxf
|
||||
* ??
|
||||
|
||||
### Images
|
||||
- gif
|
||||
* XMP: \[--]
|
||||
* http://www.adobe.com/devnet/xmp/
|
||||
- jpg
|
||||
* jif: \[rw] (only comments)
|
||||
* EXIF: \[rw] (via metadata plugin)
|
||||
* http://www.exif.org/specifications.html
|
||||
* IPTC: \[rw] (via metadata plugin)
|
||||
* http://www.iptc.org/IPTC4XMP/
|
||||
* XMP: \[rw] (via metadata plugin)
|
||||
* http://www.adobe.com/devnet/xmp/
|
||||
- png
|
||||
* XMP: \[--]
|
||||
* http://www.adobe.com/devnet/xmp/
|
||||
|
||||
### further Links:
|
||||
|
||||
http://age.hobba.nl/audio/tag_frame_reference.html
|
||||
http://wiki.creativecommons.org/Tracker_CC_Indexing
|
||||
|
||||
## Current Metadata handling
|
||||
|
||||
When reading files, demuxers or parsers extract the metadata. It will be
|
||||
sent a GST\_EVENT\_TAG to downstream elements. When a sink element
|
||||
receives a tag event, it will post a GST\_MESSAGE\_TAG message on the
|
||||
bus with the contents of the tag event.
|
||||
|
||||
Elements receiving GST\_EVENT\_TAG events can mangle them, mux them into
|
||||
the buffers they send or just pass them through. Usually is muxers that
|
||||
will format the tag data into the form required by the format they mux.
|
||||
Such elements would also implement the GstTagSetter interface to receive
|
||||
tags from the application.
|
||||
|
||||
```
|
||||
+----------+
|
||||
| demux |
|
||||
sink src --> GstEvent(tag) over GstPad to downstream element
|
||||
+----------+
|
||||
|
||||
method call over GstTagSetter interface from application
|
||||
|
|
||||
v
|
||||
+----------+
|
||||
| mux |
|
||||
GstEvent(tag) over GstPad from upstream element --> sink src
|
||||
+----------+
|
||||
```
|
||||
|
||||
The data used in all those interfaces is GstTagList. It is based on a
|
||||
GstStructure which is like a hash table with differently typed entries.
|
||||
The key is always a string/GQuark. Many keys are predefined in GStreamer
|
||||
core. More keys are defined in gst-plugins-base/gst-libs/gst/tag/tag.h.
|
||||
If elements and applications use predefined types, it is possible to
|
||||
transcode a file from one format into another while preserving all known
|
||||
and mapped metadata.
|
||||
|
||||
## Issues
|
||||
|
||||
### Unknown/Unmapped metadata
|
||||
|
||||
Right now GStreamer can lose metadata when transcoding, remuxing
|
||||
content. This can happend as we don’t map all metadata fields to generic
|
||||
ones.
|
||||
|
||||
We should probably also add the whole metadata blob to the GstTagList.
|
||||
We would need a GST\_TAG\_SYSTEM\_xxx define (e.g.
|
||||
GST\_TAG\_SYSTEM\_ID3V2) for each standard. The content is not printable
|
||||
and should be treated as binary if not known. The tag is not mergeable -
|
||||
call gst\_tag\_register() with GstTagMergeFunc=NULL. Also the tag data
|
||||
is only useful for upstream elements, not for the application.
|
||||
|
||||
A muxer would first scan a taglist for known system tags. Unknown tags
|
||||
are ignored as already. It would first populate its own metadata store
|
||||
with the entries from the system tag and the update the entries with the
|
||||
data in normal tags.
|
||||
|
||||
Below is an initial list of tag systems: ID3V1 - GST\_TAG\_SYSTEM\_ID3V1
|
||||
ID3V2 - GST\_TAG\_SYSTEM\_ID3V2 RIFF\_INFO -
|
||||
GST\_TAG\_SYSTEM\_RIFF\_INFO XMP - GST\_TAG\_SYSTEM\_XMP
|
||||
|
||||
We would basically need this for each container format.
|
||||
|
||||
See also <https://bugzilla.gnome.org/show_bug.cgi?id=345352>
|
||||
|
||||
### Lost metadata
|
||||
|
||||
A case slighly different from the previous is that when an application
|
||||
sets a GstTagList on a pipeline. Right elements consuming tags do not
|
||||
report which tags have been consumed. Especially when using elements
|
||||
that make metadata persistent, we have no means of knowing which of the
|
||||
tags made it into the target stream and which were not serialized.
|
||||
Ideally the application would like to know which kind of metadata is
|
||||
accepted by a pipleine to reflect that in the UI.
|
||||
|
||||
Although it is in practise so that elements implementing GstTagSetter
|
||||
are the ones that serialize, this does not have to be so. Otherwise we
|
||||
could add a means to that interface, where elements add the tags they
|
||||
have serialized. The application could build one list from all the tag
|
||||
messages and then query all the serialized tags from tag-setters. The
|
||||
delta tells what has not been serialized.
|
||||
|
||||
A different approach would be to query the list of supported tags in
|
||||
advance. This could be a query (GST\_QUERY\_TAG\_SUPPORT). The query
|
||||
result could be a list of elements and their tags. As a convenience we
|
||||
could flatten the list of tags for the top-level element (if the query
|
||||
was sent to a bin) and add that.
|
||||
|
||||
### Tags are per Element
|
||||
|
||||
In many cases we want tags per stream. Even metadata standards like
|
||||
mp4/3gp metadata supports that. Right now GST\_MESSAGE\_SRC(tags) is the
|
||||
element. We tried changing that to the pad, but that broke applications.
|
||||
Also we miss the symmetric functionality in GstTagSetter. This interface
|
||||
is usually implemented by
|
||||
elements.
|
||||
|
||||
### Open bugs
|
||||
|
||||
<https://bugzilla.gnome.org/buglist.cgi?query_format=advanced;short_desc=tag;bug_status=UNCONFIRMED;bug_status=NEW;bug_status=ASSIGNED;bug_status=REOPENED;bug_status=NEEDINFO;short_desc_type=allwordssubstr;product=GStreamer>
|
||||
|
||||
Add GST\_TAG\_MERGE\_REMOVE
|
||||
<https://bugzilla.gnome.org/show_bug.cgi?id=560302>
|
117
markdown/design/draft-push-pull.md
Normal file
117
markdown/design/draft-push-pull.md
Normal file
|
@ -0,0 +1,117 @@
|
|||
# DRAFT push-pull scheduling
|
||||
|
||||
Status
|
||||
|
||||
DRAFT. DEPRECATED by better current implementation.
|
||||
|
||||
Observations:
|
||||
|
||||
- The main scheduling mode is chain based scheduling where the source
|
||||
element pushes buffers through the pipeline to the sinks. this is
|
||||
called the push model
|
||||
|
||||
- In the pull model, some plugin pulls buffers from an upstream peer
|
||||
element before consuming and/or pushing them further downstream.
|
||||
|
||||
Usages of pull based scheduling:
|
||||
|
||||
- sinks that pull in data, possibly at fixed intervals driven by some
|
||||
hardware device (audiocard, videodevice, …).
|
||||
|
||||
- Efficient random access to resources. Especially useful for certain
|
||||
types of demuxers.
|
||||
|
||||
API for pull-based scheduling:
|
||||
|
||||
- an element that wants to pull data from a peer element needs to call
|
||||
the pull\_range() method. This methods requires an offset and a
|
||||
size. It is possible to leave the offset and size at -1, indicating
|
||||
that any offset or size is acceptable, this of course removes the
|
||||
advantages of getrange based scheduling.
|
||||
|
||||
Types of pull based scheduling:
|
||||
|
||||
- some sources can do random access (file source, …)
|
||||
|
||||
- some sources can read a random number of bytes but not at a random
|
||||
offset. (audio cards, …) Audio cards using a ringbuffer can however
|
||||
do random access in the ringbuffer.
|
||||
|
||||
- some sources can do random access in a range of bytes but not in
|
||||
another range. (a caching network source).
|
||||
|
||||
- some sources can do a fixed size data and without an offset. (video
|
||||
sources, …)
|
||||
|
||||
Current scheduling decision:
|
||||
|
||||
- core selects scheduling type starting on sinks by looking at
|
||||
existence of loop function on sinkpad and calling
|
||||
\_check\_pull\_range() on the source pad to activate the pads in
|
||||
push/pull mode.
|
||||
|
||||
- element proxies pull mode pad activation to peer pad.
|
||||
|
||||
Problems:
|
||||
|
||||
- core makes a tough desicion without knowing anything about the
|
||||
element. Some elements are able to deal with a pull\_range() without
|
||||
offset while others need full random access.
|
||||
|
||||
Requirements:
|
||||
|
||||
- element should be able to select scheduling method itself based on
|
||||
how it can use the peer element pull\_range. This includes if the
|
||||
peer can operate with or without offset/size. This also means that
|
||||
the core does not need to select the scheduling method anymore and
|
||||
allows for more efficient scheduling methods adjusted for the
|
||||
particular element.
|
||||
|
||||
Proposition:
|
||||
|
||||
- pads are activated without the core selecting a method.
|
||||
|
||||
- pads queries scheduling mode of peer pad. This query is rather
|
||||
finegrained and allows the element to know if the peer supports
|
||||
offsets and sizes in the get\_range function. A proposition for the
|
||||
query is outlined in draft-query.txt.
|
||||
|
||||
- pad selects scheduling mode and informs the peer pad of this
|
||||
decision.
|
||||
|
||||
Things to query:
|
||||
|
||||
- pad can do real random access (downstream peer can ask for offset
|
||||
\!= -1)
|
||||
|
||||
- min offset
|
||||
|
||||
- suggest sequential access
|
||||
|
||||
- max offset
|
||||
|
||||
- align: all offsets should be aligned with this value.
|
||||
|
||||
- pad can give ranges from A to B length (peer can ask for A ⇐ length
|
||||
⇐ B)
|
||||
|
||||
- min length
|
||||
|
||||
- suggested length
|
||||
|
||||
- max length
|
||||
|
||||
Use cases:
|
||||
|
||||
- An audio source can provide random access to the samples queued in
|
||||
its DMA buffer, it however suggests sequential access method. An
|
||||
audio source can provide a random number of samples but prefers
|
||||
reading from the hardware using a fixed segment size.
|
||||
|
||||
- A caching network source would suggest sequential access but is
|
||||
seekable in the cached region. Applications can query for the
|
||||
already downloaded portion and update the GUI, a seek can be done in
|
||||
that area.
|
||||
|
||||
- a live video source can only provide buffers sequentialy. It exposes
|
||||
offsets as -1. lengths are also -1.
|
107
markdown/design/draft-tagreading.md
Normal file
107
markdown/design/draft-tagreading.md
Normal file
|
@ -0,0 +1,107 @@
|
|||
# Tagreading
|
||||
|
||||
The tagreading (metadata reading) use case for mediacenter applications
|
||||
is not too well supported by the current GStreamer architecture. It uses
|
||||
demuxers on the files, which generally said takes too long (building
|
||||
seek-index, prerolling). What we want is specialized elements / parsing
|
||||
modes that just do the tag-reading.
|
||||
|
||||
The idea is to define a TagReadIFace. Tag-demuxers, classic demuxers and
|
||||
decoder plugins can just implement the interface or provide a separate
|
||||
element that implements the interface.
|
||||
|
||||
In addition we need a tagreadbin, that similar to decodebin does a
|
||||
typefind and then plugs the right tagread element(s). If will only look
|
||||
at elements that implement the interface. It can plug serval if
|
||||
possible.
|
||||
|
||||
For optimal performance typefind and tagread could share the list of
|
||||
already peeked buffers (a queue element after sink, but that would
|
||||
change pull to push).
|
||||
|
||||
## Design
|
||||
|
||||
The plan is that applications can do the following: pipeline = "filesrc
|
||||
\! tagbin" for (file\_path in list\_of\_files) {
|
||||
filesrc.location=file\_path pipeline.set\_state(PAUSED) // wait for TAGS
|
||||
& EOS pipeline.set\_state(READY) }
|
||||
|
||||
- it should have one sinkpad of type ANY
|
||||
|
||||
- it should send EOS when all metadata has been read "done"-signal
|
||||
from all tagread-elements
|
||||
|
||||
- special tagread-elements should have RANK\_NONE to be not
|
||||
autoplugged by decodebin
|
||||
|
||||
## Interface
|
||||
|
||||
- gboolean iface property "tag-reading" Switches the element to
|
||||
tagreading mode. Needed if normal element implement that behaviour.
|
||||
Elements will skip parsing unneeded data, don’t build a seeking
|
||||
index, etc.
|
||||
|
||||
- signal "done" Equivalent of EOS.
|
||||
|
||||
## Use Cases
|
||||
|
||||
- mp3 with id3- and apetags
|
||||
|
||||
- plug id3demux \! apedemux
|
||||
|
||||
- avi with vorbis audio
|
||||
|
||||
- plug avidemux
|
||||
|
||||
- new pad → audio/vorbis
|
||||
|
||||
- plug vorbisdec or special vorbiscomment reader
|
||||
|
||||
## Additional Thoughts
|
||||
|
||||
- would it make sense to have 2-phase tag-reading (property on tagbin
|
||||
and/or tagread elements)
|
||||
|
||||
- 1st phase: get tag-data that are directly embedded in the data
|
||||
|
||||
- 2nd phase: get tag-data that has to be generated
|
||||
|
||||
- e.g. album-art via web, video-thumbnails
|
||||
|
||||
- what about caching backends
|
||||
|
||||
- it would be good to allow applications to supply tagbin with a
|
||||
tagcache- object instance. Whenever tagbin gets a *location* to
|
||||
tagread, it consults the cache first. whenever there is a cache-miss
|
||||
it will tag-read and then store in the
|
||||
cache
|
||||
|
||||
``` c
|
||||
GstTagList *gst_tag_cache_load_tag_data (GstTagCache *self, const gchar *uri);
|
||||
gst_tag_cache_store_tag_data (GstTagCache *self, const gchar *uri, GstTagList *tags);
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
- write a generic test for parsers/demuxers to ensure they send tags
|
||||
until they reached PAUSED (elements need to parse file for
|
||||
prerolling anyway): set pipeline to paused, check for tags, set to
|
||||
playing, error out if tags come after paused
|
||||
|
||||
## Code Locations
|
||||
|
||||
- tagreadbin → gst-plugins-base/gst/tagread
|
||||
|
||||
- tagreaderiface → gst-plugins-base/gst-libs/gst/tag
|
||||
|
||||
## Reuse
|
||||
|
||||
- ogg : gst-plugins-base/ext/ogg
|
||||
|
||||
- avi : gst-plugins-good/gst/avi
|
||||
|
||||
- mp3 : gst-plugins-good/gst/id3demux
|
||||
|
||||
- wav : gst-plugins-good/gst/wavparse
|
||||
|
||||
- qt : gst-plugins-bad/gst/qtdemux
|
14
markdown/design/dynamic.md
Normal file
14
markdown/design/dynamic.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Dynamic pipelines
|
||||
|
||||
This document describes many use cases for dynamically constructing and
|
||||
manipulating a running or paused pipeline and the features provided by
|
||||
GStreamer.
|
||||
|
||||
When constructing dynamic pipelines it is important to understand the
|
||||
following features of gstreamer:
|
||||
|
||||
- pad blocking
|
||||
|
||||
- playback segments.
|
||||
|
||||
- streaming vs application threads.
|
290
markdown/design/element-sink.md
Normal file
290
markdown/design/element-sink.md
Normal file
|
@ -0,0 +1,290 @@
|
|||
# Sink elements
|
||||
|
||||
Sink elements consume data and normally have no source pads.
|
||||
|
||||
Typical sink elements include:
|
||||
|
||||
- audio/video renderers
|
||||
|
||||
- network sinks
|
||||
|
||||
- filesinks
|
||||
|
||||
Sinks are harder to construct than other element types as they are
|
||||
treated specially by the GStreamer core.
|
||||
|
||||
## state changes
|
||||
|
||||
A sink always returns ASYNC from the state change to PAUSED, this
|
||||
includes a state change from READY→PAUSED and PLAYING→PAUSED. The reason
|
||||
for this is that this way we can detect when the first buffer or event
|
||||
arrives in the sink when the state change completes.
|
||||
|
||||
A sink should block on the first EOS event or buffer received in the
|
||||
READY→PAUSED state before commiting the state to PAUSED.
|
||||
|
||||
FLUSHING events have to be handled out of sync with the buffer flow and
|
||||
take no part in the preroll procedure.
|
||||
|
||||
Events other than EOS do not complete the preroll stage.
|
||||
|
||||
## sink overview
|
||||
|
||||
- TODO: PREROLL\_LOCK can be removed and we can safely use the STREAM\_LOCK.
|
||||
|
||||
``````
|
||||
# Commit the state. We return TRUE if we can continue
|
||||
# streaming, FALSE in the case we go to a READY or NULL state.
|
||||
# if we go to PLAYING, we don't need to block on preroll.
|
||||
commit
|
||||
{
|
||||
LOCK
|
||||
switch (pending)
|
||||
case PLAYING:
|
||||
need_preroll = FALSE
|
||||
break
|
||||
case PAUSED:
|
||||
break
|
||||
case READY:
|
||||
case NULL:
|
||||
return FALSE
|
||||
case VOID:
|
||||
return TRUE
|
||||
|
||||
# update state
|
||||
state = pending
|
||||
next = VOID
|
||||
pending = VOID
|
||||
UNLOCK
|
||||
return TRUE
|
||||
}
|
||||
|
||||
# Sync an object. We have to wait for the element to reach
|
||||
# the PLAYING state before we can wait on the clock.
|
||||
# Some items do not need synchronisation (most events) so the
|
||||
# get_times method returns FALSE (not syncable)
|
||||
# need_preroll indicates that we are not in the PLAYING state
|
||||
# and therefore need to commit and potentially block on preroll
|
||||
# if our clock_wait got interrupted we commit and block again.
|
||||
# The reason for this is that the current item being rendered is
|
||||
# not yet finished and we can use that item to finish preroll.
|
||||
do_sync (obj)
|
||||
{
|
||||
# get timing information for this object
|
||||
syncable = get_times (obj, &start, &stop)
|
||||
if (!syncable)
|
||||
return OK;
|
||||
again:
|
||||
while (need_preroll)
|
||||
if (need_commit)
|
||||
need_commit = FALSE
|
||||
if (!commit)
|
||||
return FLUSHING
|
||||
|
||||
if (need_preroll)
|
||||
# release PREROLL_LOCK and wait. prerolled can be observed
|
||||
# and will be TRUE
|
||||
prerolled = TRUE
|
||||
PREROLL_WAIT (releasing PREROLL_LOCK)
|
||||
prerolled = FALSE
|
||||
if (flushing)
|
||||
return FLUSHING
|
||||
|
||||
if (valid (start || stop))
|
||||
PREROLL_UNLOCK
|
||||
end_time = stop
|
||||
ret = wait_clock (obj,start)
|
||||
PREROLL_LOCK
|
||||
if (flushing)
|
||||
return FLUSHING
|
||||
# if the clock was unscheduled, we redo the
|
||||
# preroll
|
||||
if (ret == UNSCHEDULED)
|
||||
goto again
|
||||
}
|
||||
|
||||
# render a prerollable item (EOS or buffer). It is
|
||||
# always called with the PREROLL_LOCK helt.
|
||||
render_object (obj)
|
||||
{
|
||||
ret = do_sync (obj)
|
||||
if (ret != OK)
|
||||
return ret;
|
||||
|
||||
# preroll and syncing done, now we can render
|
||||
render(obj)
|
||||
}
|
||||
| # sinks that sync on buffer contents do like this
|
||||
| while (more_to_render)
|
||||
| ret = render
|
||||
| if (ret == interrupted)
|
||||
| prerolled = TRUE
|
||||
render (buffer) ----->| PREROLL_WAIT (releasing PREROLL_LOCK)
|
||||
| prerolled = FALSE
|
||||
| if (flushing)
|
||||
| return FLUSHING
|
||||
|
|
||||
|
||||
# queue a prerollable item (EOS or buffer). It is
|
||||
# always called with the PREROLL_LOCK helt.
|
||||
# This function will commit the state when receiving the
|
||||
# first prerollable item.
|
||||
# items are then added to the rendering queue or rendered
|
||||
# right away if no preroll is needed.
|
||||
queue (obj, prerollable)
|
||||
{
|
||||
if (need_preroll)
|
||||
if (prerollable)
|
||||
queuelen++
|
||||
|
||||
# first item in the queue while we need preroll
|
||||
# will complete state change and call preroll
|
||||
if (queuelen == 1)
|
||||
preroll (obj)
|
||||
if (need_commit)
|
||||
need_commit = FALSE
|
||||
if (!commit)
|
||||
return FLUSHING
|
||||
|
||||
# then see if we need more preroll items before we
|
||||
# can block
|
||||
if (need_preroll)
|
||||
if (queuelen <= maxqueue)
|
||||
queue.add (obj)
|
||||
return OK
|
||||
|
||||
# now clear the queue and render each item before
|
||||
# rendering the current item.
|
||||
while (queue.hasItem)
|
||||
render_object (queue.remove())
|
||||
|
||||
render_object (obj)
|
||||
queuelen = 0
|
||||
}
|
||||
|
||||
# various event functions
|
||||
event
|
||||
EOS:
|
||||
# events must complete preroll too
|
||||
STREAM_LOCK
|
||||
PREROLL_LOCK
|
||||
if (flushing)
|
||||
return FALSE
|
||||
ret = queue (event, TRUE)
|
||||
if (ret == FLUSHING)
|
||||
return FALSE
|
||||
PREROLL_UNLOCK
|
||||
STREAM_UNLOCK
|
||||
break
|
||||
SEGMENT:
|
||||
# the segment must be used to clip incoming
|
||||
# buffers. Then then go into the queue as non-prerollable
|
||||
# items used for syncing the buffers
|
||||
STREAM_LOCK
|
||||
PREROLL_LOCK
|
||||
if (flushing)
|
||||
return FALSE
|
||||
set_clip
|
||||
ret = queue (event, FALSE)
|
||||
if (ret == FLUSHING)
|
||||
return FALSE
|
||||
PREROLL_UNLOCK
|
||||
STREAM_UNLOCK
|
||||
break
|
||||
FLUSH_START:
|
||||
# set flushing and unblock all that is waiting
|
||||
event ----> subclasses can interrupt render
|
||||
PREROLL_LOCK
|
||||
flushing = TRUE
|
||||
unlock_clock
|
||||
PREROLL_SIGNAL
|
||||
PREROLL_UNLOCK
|
||||
STREAM_LOCK
|
||||
lost_state
|
||||
STREAM_UNLOCK
|
||||
break
|
||||
FLUSH_END:
|
||||
# unset flushing and clear all data and eos
|
||||
STREAM_LOCK
|
||||
event
|
||||
PREROLL_LOCK
|
||||
queue.clear
|
||||
queuelen = 0
|
||||
flushing = FALSE
|
||||
eos = FALSE
|
||||
PREROLL_UNLOCK
|
||||
STREAM_UNLOCK
|
||||
break
|
||||
|
||||
# the chain function checks the buffer falls within the
|
||||
# configured segment and queues the buffer for preroll and
|
||||
# rendering
|
||||
chain
|
||||
STREAM_LOCK
|
||||
PREROLL_LOCK
|
||||
if (flushing)
|
||||
return FLUSHING
|
||||
if (clip)
|
||||
queue (buffer, TRUE)
|
||||
PREROLL_UNLOCK
|
||||
STREAM_UNLOCK
|
||||
|
||||
state
|
||||
switch (transition)
|
||||
READY_PAUSED:
|
||||
# no datapassing is going on so we always return ASYNC
|
||||
ret = ASYNC
|
||||
need_commit = TRUE
|
||||
eos = FALSE
|
||||
flushing = FALSE
|
||||
need_preroll = TRUE
|
||||
prerolled = FALSE
|
||||
break
|
||||
PAUSED_PLAYING:
|
||||
# we grab the preroll lock. This we can only do if the
|
||||
# chain function is either doing some clock sync, we are
|
||||
# waiting for preroll or the chain function is not being called.
|
||||
PREROLL_LOCK
|
||||
if (prerolled || eos)
|
||||
ret = OK
|
||||
need_commit = FALSE
|
||||
need_preroll = FALSE
|
||||
if (eos)
|
||||
post_eos
|
||||
else
|
||||
PREROLL_SIGNAL
|
||||
else
|
||||
need_preroll = TRUE
|
||||
need_commit = TRUE
|
||||
ret = ASYNC
|
||||
PREROLL_UNLOCK
|
||||
break
|
||||
PLAYING_PAUSED:
|
||||
---> subclass can interrupt render
|
||||
# we grab the preroll lock. This we can only do if the
|
||||
# chain function is either doing some clock sync
|
||||
# or the chain function is not being called.
|
||||
PREROLL_LOCK
|
||||
need_preroll = TRUE
|
||||
unlock_clock
|
||||
if (prerolled || eos)
|
||||
ret = OK
|
||||
else
|
||||
ret = ASYNC
|
||||
PREROLL_UNLOCK
|
||||
break
|
||||
PAUSED_READY:
|
||||
---> subclass can interrupt render
|
||||
# we grab the preroll lock. Set to flushing and unlock
|
||||
# everything. This should exit the chain functions and stop
|
||||
# streaming.
|
||||
PREROLL_LOCK
|
||||
flushing = TRUE
|
||||
unlock_clock
|
||||
queue.clear
|
||||
queuelen = 0
|
||||
PREROLL_SIGNAL
|
||||
ret = OK
|
||||
PREROLL_UNLOCK
|
||||
break
|
||||
```
|
132
markdown/design/element-source.md
Normal file
132
markdown/design/element-source.md
Normal file
|
@ -0,0 +1,132 @@
|
|||
# Source elements
|
||||
|
||||
A source element is an element that provides data to the pipeline. It
|
||||
does typically not have any sink (input) pads.
|
||||
|
||||
Typical source elements include:
|
||||
|
||||
- file readers
|
||||
|
||||
- network elements (live or not)
|
||||
|
||||
- capture elements (video/audio/…)
|
||||
|
||||
- generators (signals/video/audio/…)
|
||||
|
||||
## Live sources
|
||||
|
||||
A source is said to be a live source when it has the following property:
|
||||
|
||||
- temporarily stopping reading from the source causes data to be lost.
|
||||
|
||||
In general when this property holds, the source also produces data at a
|
||||
fixed rate. Most sources have a limit on the rate at which they can
|
||||
deliver data, which might be faster or slower than the consumption rate.
|
||||
This property however does not make them a live source.
|
||||
|
||||
Let’s look at some example sources.
|
||||
|
||||
- file readers: you can PAUSE without losing data. There is however a
|
||||
limit to how fast you can read from this source. This limit is
|
||||
usually much higher than the consumption rate. In some cases it
|
||||
might be slower (an NFS share, for example) in which case you might
|
||||
need to use some buffering (see [buffering](design/buffering.md)).
|
||||
|
||||
- HTTP network element: you can PAUSE without data loss. Depending on
|
||||
the available network bandwidth, consumption rate might be higher
|
||||
than production rate in which case buffering should be used (see
|
||||
[buffering](design/buffering.md)).
|
||||
|
||||
- audio source: pausing the audio capture will lead to lost data. this
|
||||
source is therefore definatly live. In addition, an audio source
|
||||
will produce data at a fixed rate (the samplerate). Also depending
|
||||
on the buffersize, this source will introduce a latency (see
|
||||
[latency](design/latency.md)).
|
||||
|
||||
- udp network source: Pausing the receiving part will lead to lost
|
||||
data. This source is therefore a live source. Also in a typical case
|
||||
the udp packets will be received at a certain rate, which might be
|
||||
difficult to guess because of network jitter. This source does not
|
||||
necessarily introduce latency on its own.
|
||||
|
||||
- dvb source: PAUSING this element will lead to data loss, it’s a live
|
||||
source similar to a UDP source.
|
||||
|
||||
## Source types
|
||||
|
||||
A source element can operate in three ways:
|
||||
|
||||
- it is fully seekable, this means that random access can be performed
|
||||
on it in an efficient way. (a file reader,…). This also typically
|
||||
means that the source is not live.
|
||||
|
||||
- data can be obtained from it with a variable size. This means that
|
||||
the source can give N bytes of data. An example is an audio source.
|
||||
A video source always provides the same amount of data (one video
|
||||
frame). Note that this is not a fully seekable source.
|
||||
|
||||
- it is a live source, see above.
|
||||
|
||||
When writing a source, one has to look at how the source can operate to
|
||||
decide on the scheduling methods to implement on the source.
|
||||
|
||||
- fully seekable sources implement a getrange function on the source
|
||||
pad.
|
||||
|
||||
- sources that can give N bytes but cannot do seeking also implement a
|
||||
getrange function but state that they cannot do random access.
|
||||
|
||||
- sources that are purely live sources implement a task to push out
|
||||
data.
|
||||
|
||||
Any source that has a getrange function must also implement a push based
|
||||
scheduling mode. In this mode the source starts a task that gets N bytes
|
||||
and pushes them out. Whenever possible, the peer element will select the
|
||||
getrange based scheduling method of the source, though.
|
||||
|
||||
A source with a getrange function must activate itself in the pad
|
||||
activate function. This is needed because the downstream peer element
|
||||
will decide and activate the source element in its state change function
|
||||
before the source’s state change function is called.
|
||||
|
||||
## Source base classes
|
||||
|
||||
GstBaseSrc:
|
||||
|
||||
This base class provides an implementation of a random access source and
|
||||
is very well suited for file reader like sources.
|
||||
|
||||
GstPushSrc:
|
||||
|
||||
Base class for block-based sources. This class is mostly useful for
|
||||
elements that cannot do random access, or at least very slowly. The
|
||||
source usually prefers to push out a fixed size buffer.
|
||||
|
||||
Classes extending this base class will usually be scheduled in a push
|
||||
based mode. If the peer accepts to operate without offsets and within
|
||||
the limits of the allowed block size, this class can operate in getrange
|
||||
based mode automatically.
|
||||
|
||||
The subclass should extend the methods from the baseclass in addition to
|
||||
the create method. If the source is seekable, it needs to override
|
||||
GstBaseSrc::event() in addition to GstBaseSrc::is\_seekable() in order
|
||||
to retrieve the seek offset, which is the offset of the next buffer to
|
||||
be requested.
|
||||
|
||||
Flushing, scheduling and sync is all handled by this base class.
|
||||
|
||||
## Timestamps
|
||||
|
||||
A non-live source should timestamp the buffers it produces starting from
|
||||
0. If it is not possible to timestamp every buffer (filesrc), the source
|
||||
is allowed to only timestamp the first buffer (as 0).
|
||||
|
||||
Live sources only produce data in the PLAYING state, when the clock is
|
||||
running. They should timestamp each buffer they produce with the current
|
||||
running\_time of the pipeline, which is expressed as:
|
||||
|
||||
absolute_time - base_time
|
||||
|
||||
With absolute\_time the time obtained from the global pipeline with
|
||||
gst\_clock\_get\_time() and base\_time being the time of that clock when
|
||||
the pipeline was last set to PLAYING.
|
327
markdown/design/element-transform.md
Normal file
327
markdown/design/element-transform.md
Normal file
|
@ -0,0 +1,327 @@
|
|||
# Transform elements
|
||||
|
||||
Transform elements transform input buffers to output buffers based on
|
||||
the sink and source caps.
|
||||
|
||||
An important requirement for a transform is that the output caps are
|
||||
completely defined by the input caps and vice versa. This means that a
|
||||
typical decoder element can NOT be implemented with a transform element,
|
||||
this is because the output caps like width and height of the
|
||||
decompressed video frame, for example, are encoded in the stream and
|
||||
thus not defined by the input caps.
|
||||
|
||||
Typical transform elements include:
|
||||
|
||||
- audio convertors (audioconvert, audioresample,…)
|
||||
|
||||
- video convertors (colorspace, videoscale, …)
|
||||
|
||||
- filters (capsfilter, volume, colorbalance, …)
|
||||
|
||||
The implementation of the transform element has to take care of the
|
||||
following things:
|
||||
|
||||
- efficient negotiation both up and downstream
|
||||
|
||||
- efficient buffer alloc and other buffer management
|
||||
|
||||
Some transform elements can operate in different modes:
|
||||
|
||||
- passthrough (no changes are done on the input buffers)
|
||||
|
||||
- in-place (changes made directly to the incoming buffers without
|
||||
requiring a copy or new buffer allocation)
|
||||
|
||||
- metadata changes only
|
||||
|
||||
Depending on the mode of operation the buffer allocation strategy might
|
||||
change.
|
||||
|
||||
The transform element should at any point be able to renegotiate sink
|
||||
and src caps as well as change the operation mode.
|
||||
|
||||
In addition, the transform element will typically take care of the
|
||||
following things as well:
|
||||
|
||||
- flushing, seeking
|
||||
|
||||
- state changes
|
||||
|
||||
- timestamping, this is typically done by copying the input timestamps
|
||||
to the output buffers but subclasses should be able to override
|
||||
this.
|
||||
|
||||
- QoS, avoiding calls to the subclass transform function
|
||||
|
||||
- handle scheduling issues such as push and pull based operation.
|
||||
|
||||
In the next sections, we will describe the behaviour of the transform
|
||||
element in each of the above use cases. We focus mostly on the buffer
|
||||
allocation strategies and caps negotiation.
|
||||
|
||||
## Processing
|
||||
|
||||
A transform has 2 main processing functions:
|
||||
|
||||
- **`transform()`**: Transform the input buffer to the output buffer. The
|
||||
output buffer is guaranteed to be writable and different from the input buffer.
|
||||
|
||||
- **`transform_ip()`**: Transform the input buffer in-place. The input buffer
|
||||
is writable and of bigger or equal size than the output buffer.
|
||||
|
||||
A transform can operate in the following modes:
|
||||
|
||||
- *passthrough*: The element will not make changes to the buffers, buffers are
|
||||
pushed straight through, caps on both sides need to be the same. The element
|
||||
can optionally implement a transform_ip() function to take a look at the data,
|
||||
the buffer does not have to be writable.
|
||||
|
||||
- *in-place*: Changes can be made to the input buffer directly to obtain the
|
||||
output buffer. The transform must implement a transform_ip() function.
|
||||
|
||||
- *copy-transform*: The transform is performed by copying and transforming the
|
||||
input buffer to a new output buffer. The transform must implement a transform()
|
||||
function.
|
||||
|
||||
When no `transform()` function is provided, only in-place and passthrough
|
||||
operation is allowed, this means that source and destination caps must
|
||||
be equal or that the source buffer size is bigger or equal than the
|
||||
destination buffer.
|
||||
|
||||
When no `transform_ip()` function is provided, only passthrough and
|
||||
copy-transforms are supported. Providing this function is an
|
||||
optimisation that can avoid a buffer copy.
|
||||
|
||||
When no functions are provided, we can only process in passthrough mode.
|
||||
|
||||
## Negotiation
|
||||
|
||||
Typical (re)negotiation of the transform element in push mode always
|
||||
goes from sink to src, this means triggers the following sequence:
|
||||
|
||||
- the sinkpad receives a new caps event.
|
||||
|
||||
- the transform function figures out what it can convert these caps
|
||||
to.
|
||||
|
||||
- try to see if we can configure the caps unmodified on the peer. We
|
||||
need to do this because we prefer to not do anything.
|
||||
|
||||
- the transform configures itself to transform from the new sink caps
|
||||
to the target src caps
|
||||
|
||||
- the transform processes and sets the output caps on the src pad
|
||||
|
||||
We call this downstream negotiation (DN) and it goes roughly like this:
|
||||
|
||||
```
|
||||
sinkpad transform srcpad
|
||||
CAPS event | | |
|
||||
------------>| find_transform() | |
|
||||
|------------------->| |
|
||||
| | CAPS event |
|
||||
| |--------------------->|
|
||||
| <configure caps> <-| |
|
||||
```
|
||||
|
||||
These steps configure the element for a transformation from the input
|
||||
caps to the output caps.
|
||||
|
||||
The transform has 3 function to perform the negotiation:
|
||||
|
||||
- **`transform_caps()`**: Transform the caps on a certain pad to all the
|
||||
possible supported caps on the other pad. The input caps are guaranteed to be
|
||||
a simple caps with just one structure. The caps do not have to be fixed.
|
||||
|
||||
- **`fixate_caps()`**: Given a caps on one pad, fixate the caps on the other
|
||||
pad. The target caps are writable.
|
||||
|
||||
- **`set_caps()`**: Configure the transform for a transformation between src
|
||||
caps and dest caps. Both caps are guaranteed to be fixed caps.
|
||||
|
||||
If no `transform_caps()` is defined, we can only perform the identity
|
||||
transform, by default.
|
||||
|
||||
If no `set_caps()` is defined, we don’t care about caps. In that case we
|
||||
also assume nothing is going to write to the buffer and we don’t enforce
|
||||
a writable buffer for the `transform_ip` function, when present.
|
||||
|
||||
One common function that we need for the transform element is to find
|
||||
the best transform from one format (src) to another (dest). Some
|
||||
requirements of this function are:
|
||||
|
||||
- has a fixed src caps
|
||||
|
||||
- finds a fixed dest caps that the transform element can transform to
|
||||
|
||||
- the dest caps are compatible and can be accepted by peer elements
|
||||
|
||||
- the transform function prefers to make src caps == dest caps
|
||||
|
||||
- the transform function can optionally fixate dest caps.
|
||||
|
||||
The `find_transform()` function goes like this:
|
||||
|
||||
- start from src aps, these caps are fixed.
|
||||
|
||||
- check if the caps are acceptable for us as src caps. This is usually
|
||||
enforced by the padtemplate of the element.
|
||||
|
||||
- calculate all caps we can transform too with `transform_caps()`
|
||||
|
||||
- if the original caps are a subset of the transforms, try to see if
|
||||
the the caps are acceptable for the peer. If this is possible, we
|
||||
can perform passthrough and make src == dest. This is performed by
|
||||
simply calling gst\_pad\_peer\_accept\_caps().
|
||||
|
||||
- if the caps are not fixed, we need to fixate it, start by taking the
|
||||
peer caps and intersect with them.
|
||||
|
||||
- for each of the transformed caps retrieved with transform\_caps():
|
||||
|
||||
- try to fixate the caps with fixate\_caps()
|
||||
|
||||
- if the caps are fixated, check if the peer accepts them with
|
||||
`_peer_accept_caps()`, if the peer accepts, we have found a dest caps.
|
||||
|
||||
- if we run out of caps, we fail to find a transform.
|
||||
|
||||
- if we found a destination caps, configure the transform with
|
||||
set\_caps().
|
||||
|
||||
After this negotiation process, the transform element is usually in a
|
||||
steady state. We can identify these steady states:
|
||||
|
||||
- src and sink pads both have the same caps. Note that when the caps
|
||||
are equal on both pads, the input and output buffers automatically
|
||||
have the same size. The element can operate on the buffers in the
|
||||
following ways: (Same caps, SC)
|
||||
|
||||
- passthrough: buffers are inspected but no metadata or buffer data is
|
||||
changed. The input buffers don’t need to be writable. The input
|
||||
buffer is simply pushed out again without modifications. (SCP)
|
||||
|
||||
```
|
||||
sinkpad transform srcpad
|
||||
chain() | | |
|
||||
------------>| handle_buffer() | |
|
||||
|------------------->| pad_push() |
|
||||
| |--------------------->|
|
||||
| | |
|
||||
```
|
||||
|
||||
- in-place: buffers are modified in-place, this means that the input
|
||||
buffer is modified to produce a new output buffer. This requires the
|
||||
input buffer to be writable. If the input buffer is not writable, a
|
||||
new buffer has to be allocated from the bufferpool. (SCI)
|
||||
|
||||
```
|
||||
sinkpad transform srcpad
|
||||
chain() | | |
|
||||
------------>| handle_buffer() | |
|
||||
|------------------->| |
|
||||
| | [!writable] |
|
||||
| | alloc buffer |
|
||||
| .-| |
|
||||
| <transform_ip> | | |
|
||||
| '>| |
|
||||
| | pad_push() |
|
||||
| |--------------------->|
|
||||
| | |
|
||||
```
|
||||
|
||||
- copy transform: a new output buffer is allocate from the bufferpool
|
||||
and data from the input buffer is transformed into the output
|
||||
buffer. (SCC)
|
||||
|
||||
```
|
||||
sinkpad transform srcpad
|
||||
chain() | | |
|
||||
------------>| handle_buffer() | |
|
||||
|------------------->| |
|
||||
| | alloc buffer |
|
||||
| .-| |
|
||||
| <transform> | | |
|
||||
| '>| |
|
||||
| | pad_push() |
|
||||
| |--------------------->|
|
||||
| | |
|
||||
```
|
||||
|
||||
- src and sink pads have different caps. The element can operate on
|
||||
the buffers in the following way: (Different Caps, DC)
|
||||
|
||||
- in-place: input buffers are modified in-place. This means that the
|
||||
input buffer has a size that is larger or equal to the output size.
|
||||
The input buffer will be resized to the size of the output buffer.
|
||||
If the input buffer is not writable or the output size is bigger
|
||||
than the input size, we need to pad-alloc a new buffer. (DCI)
|
||||
|
||||
```
|
||||
sinkpad transform srcpad
|
||||
chain() | | |
|
||||
------------>| handle_buffer() | |
|
||||
|------------------->| |
|
||||
| | [!writable || !size] |
|
||||
| | alloc buffer |
|
||||
| .-| |
|
||||
| <transform_ip> | | |
|
||||
| '>| |
|
||||
| | pad_push() |
|
||||
| |--------------------->|
|
||||
| | |
|
||||
```
|
||||
|
||||
- copy transform: a new output buffer is allocated and the data from
|
||||
the input buffer is transformed into the output buffer. The flow is
|
||||
exactly the same as the case with the same-caps negotiation. (DCC)
|
||||
|
||||
We can immediately observe that the copy transform states will need to
|
||||
allocate a new buffer from the bufferpool. When the transform element is
|
||||
receiving a non-writable buffer in the in-place state, it will also need
|
||||
to perform an allocation. There is no reason why the passthrough state
|
||||
would perform an allocation.
|
||||
|
||||
This steady state changes when one of the following actions occur:
|
||||
|
||||
- the sink pad receives new caps, this triggers the above downstream
|
||||
renegotation process, see above for the flow.
|
||||
|
||||
- the transform element wants to renegotiate (because of changed
|
||||
properties, for example). This essentially clears the current steady
|
||||
state and triggers the downstream and upstream renegotiation
|
||||
process. This situation also happens when a RECONFIGURE event was
|
||||
received on the transform srcpad.
|
||||
|
||||
## Allocation
|
||||
|
||||
After the transform element is configured with caps, a bufferpool needs
|
||||
to be negotiated to perform the allocation of buffers. We have 2 cases:
|
||||
|
||||
- The element is operating in passthrough we don’t need to allocate a
|
||||
buffer in the transform element.
|
||||
|
||||
- The element is not operating in passthrough and needs to allocation
|
||||
an output buffer.
|
||||
|
||||
In case 1, we don’t query and configure a pool. We let upstream decide
|
||||
if it wants to use a bufferpool and then we will proxy the bufferpool
|
||||
from downstream to upstream.
|
||||
|
||||
In case 2, we query and set a bufferpool on the srcpad that will be used
|
||||
for doing the allocations.
|
||||
|
||||
In order to perform allocation, we need to be able to get the size of
|
||||
the output buffer after the transform. We need additional function to
|
||||
retrieve the size. There are two functions:
|
||||
|
||||
- `transform_size()`: Given a caps and a size on one pad, and a caps on the
|
||||
other pad, calculate the size of the other buffer. This function is able to
|
||||
perform all size transforms and is the preferred method of transforming
|
||||
a size.
|
||||
|
||||
- `get_unit_size()`: When the input size and output size are always
|
||||
a multiple of each other (audio conversion, ..) we can define a more simple
|
||||
get_unit_size() function. The transform will use this function to get the
|
||||
same amount of units in the source and destination buffers. For performance
|
||||
reasons, the mapping between caps and size is kept in a cache.
|
282
markdown/design/events.md
Normal file
282
markdown/design/events.md
Normal file
|
@ -0,0 +1,282 @@
|
|||
# Events
|
||||
|
||||
Events are objects passed around in parallel to the buffer dataflow to
|
||||
notify elements of various events.
|
||||
|
||||
Events are received on pads using the event function. Some events should
|
||||
be interleaved with the data stream so they require taking the
|
||||
STREAM_LOCK, others don’t.
|
||||
|
||||
Different types of events exist to implement various functionalities.
|
||||
|
||||
* `GST_EVENT_FLUSH_START`: data is to be discarded
|
||||
* `GST_EVENT_FLUSH_STOP`: data is allowed again
|
||||
* `GST_EVENT_CAPS`: Format information about the following buffers
|
||||
* `GST_EVENT_SEGMENT`: Timing information for the following buffers
|
||||
* `GST_EVENT_TAG`: Stream metadata.
|
||||
* `GST_EVENT_BUFFERSIZE`: Buffer size requirements
|
||||
* `GST_EVENT_SINK_MESSAGE`: An event turned into a message by sinks
|
||||
* `GST_EVENT_EOS`: no more data is to be expected on a pad.
|
||||
* `GST_EVENT_QOS`: A notification of the quality of service of the stream
|
||||
* `GST_EVENT_SEEK`: A seek should be performed to a new position in the stream
|
||||
* `GST_EVENT_NAVIGATION`: A navigation event.
|
||||
* `GST_EVENT_LATENCY`: Configure the latency in a pipeline
|
||||
* `GST_EVENT_STEP`: Stepping event
|
||||
* `GST_EVENT_RECONFIGURE`: stream reconfigure event
|
||||
|
||||
- `GST_EVENT_DRAIN`: Play all data downstream before returning.
|
||||
> not yet implemented, under investigation, might be needed to do
|
||||
still frames in DVD.
|
||||
|
||||
# src pads
|
||||
|
||||
A `gst_pad_push_event()` on a srcpad will first store the sticky event
|
||||
in the sticky array before sending the event to the peer pad. If there
|
||||
is no peer pad and the event was not stored in the sticky array, FALSE
|
||||
is returned.
|
||||
|
||||
Flushing pads will refuse the events and will not store the sticky
|
||||
events.
|
||||
|
||||
# sink pads
|
||||
|
||||
A `gst_pad_send_event()`i on a sinkpad will call the event function on
|
||||
the pad. If the event function returns success, the sticky event is
|
||||
stored in the sticky event array and the event is marked for update.
|
||||
|
||||
When the pad is flushing, the `_send_event()` function returns FALSE
|
||||
immediately.
|
||||
|
||||
When the next data item is pushed, the pending events are pushed first.
|
||||
|
||||
This ensures that the event function is never called for flushing pads
|
||||
and that the sticky array only contains events for which the event
|
||||
function returned success.
|
||||
|
||||
# pad link
|
||||
|
||||
When linking pads, the srcpad sticky events are marked for update when
|
||||
they are different from the sinkpad events. The next buffer push will
|
||||
push the events to the sinkpad.
|
||||
|
||||
## FLUSH_START/STOP
|
||||
|
||||
A flush event is sent both downstream and upstream to clear any pending
|
||||
data from the pipeline. This might be needed to make the graph more
|
||||
responsive when the normal dataflow gets interrupted by for example a
|
||||
seek event.
|
||||
|
||||
Flushing happens in two stages.
|
||||
|
||||
1) a source element sends the FLUSH_START event to the downstream peer element.
|
||||
The downstream element starts rejecting buffers from the upstream elements. It
|
||||
sends the flush event further downstream and discards any buffers it is
|
||||
holding as well as return from the chain function as soon as possible.
|
||||
This makes sure that all upstream elements get unblocked.
|
||||
This event is not synchronized with the STREAM_LOCK and can be done in the
|
||||
application thread.
|
||||
|
||||
2) a source element sends the FLUSH_STOP event to indicate
|
||||
that the downstream element can accept buffers again. The downstream
|
||||
element sends the flush event to its peer elements. After this step dataflow
|
||||
continues. The FLUSH_STOP call is synchronized with the STREAM_LOCK so any
|
||||
data used by the chain function can safely freed here if needed. Any
|
||||
pending EOS events should be discarded too.
|
||||
|
||||
After the flush completes the second stage, data is flowing again in the
|
||||
pipeline and all buffers are more recent than those before the flush.
|
||||
|
||||
For elements that use the pullrange function, they send both flush
|
||||
events to the upstream pads in the same way to make sure that the
|
||||
pullrange function unlocks and any pending buffers are cleared in the
|
||||
upstream elements.
|
||||
|
||||
A `FLUSH_START` may instruct the pipeline to distribute a new base_time
|
||||
to elements so that the running_time is reset to 0. (see
|
||||
[clocks](design/clocks.md) and [synchronisation](design/synchronisation.md)).
|
||||
|
||||
## EOS
|
||||
|
||||
The EOS event can only be sent on a sinkpad. It is typically emitted by
|
||||
the source element when it has finished sending data. This event is
|
||||
mainly sent 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 elements. This way the event will eventually reach the sinks which
|
||||
should then post an EOS message on the bus when in PLAYING.
|
||||
|
||||
An element might want to flush its internally queued data before
|
||||
forwarding the EOS event downstream. This flushing can be done in the
|
||||
same thread as the one handling the EOS event.
|
||||
|
||||
For elements with multiple sink pads it might be possible to wait for
|
||||
EOS on all the pads before forwarding the event.
|
||||
|
||||
The EOS event should always be interleaved with the data flow, therefore
|
||||
the GStreamer core will take the `STREAM_LOCK`.
|
||||
|
||||
Sometimes the EOS event is generated by another element than the source,
|
||||
for example a demuxer element can generate an EOS event before the
|
||||
source element. This is not a problem, the demuxer does not send an EOS
|
||||
event to the upstream element but returns `GST_FLOW_EOS`, causing the
|
||||
source element to stop sending data.
|
||||
|
||||
An element that sends EOS on a pad should stop sending data on that pad.
|
||||
Source elements typically pause() their task for that purpose.
|
||||
|
||||
By default, a GstBin collects all EOS messages from all its sinks before
|
||||
posting the EOS message to its parent.
|
||||
|
||||
The EOS is only posted on the bus by the sink elements in the PLAYING
|
||||
state. If the EOS event is received in the PAUSED state, it is queued
|
||||
until the element goes to PLAYING.
|
||||
|
||||
A `FLUSH_STOP` event on an element flushes the EOS state and all pending
|
||||
EOS messages.
|
||||
|
||||
## SEGMENT
|
||||
|
||||
A segment 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 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
|
||||
pipeline using the SEGMENT event.
|
||||
|
||||
Before sending buffers, an element must send a SEGMENT event. An element
|
||||
is free to refuse buffers if they were not preceded by a SEGMENT event.
|
||||
|
||||
Elements that sync to the clock should store the SEGMENT start and end
|
||||
values and subtract the start value from the buffer timestamp before
|
||||
comparing it against the stream time (see [clocks](design/clocks.md)).
|
||||
|
||||
An element is allowed to send out buffers with the SEGMENT start time
|
||||
already subtracted from the timestamp. If it does so, it needs to send a
|
||||
corrected SEGMENT downstream, ie, one with start time 0.
|
||||
|
||||
A SEGMENT event should be generated as soon as possible in the pipeline
|
||||
and is usually generated by a demuxer or source. The event is generated
|
||||
before pushing the first buffer and after a seek, right before pushing
|
||||
the new buffer.
|
||||
|
||||
The SEGMENT event should be sent from the streaming thread and should be
|
||||
serialized with the buffers.
|
||||
|
||||
Buffers should be clipped within the range indicated by the newsegment
|
||||
event start and stop values. Sinks must drop buffers with timestamps out
|
||||
of the indicated segment range.
|
||||
|
||||
## TAG
|
||||
|
||||
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 system. A tag is serialized with buffers.
|
||||
|
||||
## BUFFERSIZE
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> This event is not yet implemented.
|
||||
|
||||
An element can suggest a buffersize for downstream elements. This is
|
||||
typically done by elements that produce data on multiple source pads
|
||||
such as demuxers.
|
||||
|
||||
## QOS
|
||||
|
||||
A QOS, or quality of service message, is generated in an element to
|
||||
report to the upstream elements about the current quality of real-time
|
||||
performance of the stream. This is typically done by the sinks that
|
||||
measure the amount of framedrops they have. (see [qos](design/qos.md))
|
||||
|
||||
## SEEK
|
||||
|
||||
A seek event is issued by the application to configure the playback
|
||||
range of a stream. It is called form the application thread and travels
|
||||
upstream.
|
||||
|
||||
The seek event contains the new start and stop position of playback
|
||||
after the seek is performed. Optionally the stop position can be left at
|
||||
-1 to continue playback to the end of the stream. The seek event also
|
||||
contains the new playback rate of the stream, 1.0 is normal playback,
|
||||
2.0 double speed and negative values mean backwards playback.
|
||||
|
||||
A seek usually flushes the graph to minimize latency after the seek.
|
||||
This behaviour is triggered by using the `SEEK_FLUSH` flag on the seek
|
||||
event.
|
||||
|
||||
The seek event usually starts from the sink elements and travels
|
||||
upstream from element to element until it reaches an element that can
|
||||
perform the seek. No intermediate element is allowed to assume that a
|
||||
seek to this location will happen. It is allowed to modify the start and
|
||||
stop times if it needs to do so. this is typically the case if a seek is
|
||||
requested for a non-time position.
|
||||
|
||||
The actual seek is performed in the application thread so that success
|
||||
or failure can be reported as a return value of the seek event. It is
|
||||
therefore important that before executing the seek, the element acquires
|
||||
the `STREAM_LOCK` so that the streaming thread and the seek get
|
||||
serialized.
|
||||
|
||||
The general flow of executing the seek with FLUSH is as follows:
|
||||
|
||||
1) unblock the streaming threads, they could be blocked in a chain
|
||||
function. This is done by sending a FLUSH_START on all srcpads or by pausing
|
||||
the streaming task, depending on the seek FLUSH flag.
|
||||
The flush will make sure that all downstream elements unlock and
|
||||
that control will return to this element chain/loop function.
|
||||
We cannot lock the STREAM_LOCK before doing this since it might
|
||||
cause a deadlock.
|
||||
|
||||
2) acquire the STREAM_LOCK. This will work since the chain/loop function
|
||||
was unlocked/paused in step 1).
|
||||
|
||||
3) perform the seek. since the STREAM_LOCK is held, the streaming thread
|
||||
will wait for the seek to complete. Most likely, the stream thread
|
||||
will pause because the peer elements are flushing.
|
||||
|
||||
4) send a FLUSH_STOP event to all peer elements to allow streaming again.
|
||||
|
||||
5) create a SEGMENT event to signal the new buffer timestamp base time.
|
||||
This event must be queued to be sent by the streaming thread.
|
||||
|
||||
6) start stopped tasks and unlock the STREAM_LOCK, dataflow will continue
|
||||
now from the new position.
|
||||
|
||||
More information about the different seek types can be found in
|
||||
[seeking](design/seeking.md).
|
||||
|
||||
## NAVIGATION
|
||||
|
||||
A navigation event is generated by a sink element to signal the elements
|
||||
of a navigation event such as a mouse movement or button click.
|
||||
Navigation events travel upstream.
|
||||
|
||||
## LATENCY
|
||||
|
||||
A latency event is used to configure a certain latency in the pipeline.
|
||||
It contains a single GstClockTime with the required latency. The latency
|
||||
value is calculated by the pipeline and distributed to all sink elements
|
||||
before they are set to PLAYING. The sinks will add the configured
|
||||
latency value to the timestamps of the buffer in order to delay their
|
||||
presentation. (See also [latency](design/latency.md)).
|
||||
|
||||
## DRAIN
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> This event is not yet implemented.
|
||||
|
||||
Drain event indicates that upstream is about to perform a real-time
|
||||
event, such as pausing to present an interactive menu or such, and needs
|
||||
to wait for all data it has sent to be played-out in the sink.
|
||||
|
||||
Drain should only be used by live elements, as it may otherwise occur
|
||||
during prerolling.
|
||||
|
||||
Usually after draining the pipeline, an element either needs to modify
|
||||
timestamps, or FLUSH to prevent subsequent data being discarded at the
|
||||
sinks for arriving late (only applies during playback scenarios).
|
246
markdown/design/framestep.md
Normal file
246
markdown/design/framestep.md
Normal file
|
@ -0,0 +1,246 @@
|
|||
# Frame step
|
||||
|
||||
This document outlines the details of the frame stepping functionality
|
||||
in GStreamer.
|
||||
|
||||
The stepping functionality operates on the current playback segment,
|
||||
position and rate as it was configured with a regular seek event. In
|
||||
contrast to the seek event, it operates very closely to the sink and
|
||||
thus has a very low latency and is not slowed down by queues and does
|
||||
not actually perform any seeking logic. For this reason we want to
|
||||
include a new API instead of reusing the seek API.
|
||||
|
||||
The following requirements are needed:
|
||||
|
||||
- The ability to walk forwards and backwards in the stream.
|
||||
|
||||
- Arbitrary increments in any supported format (time, frames, bytes …)
|
||||
|
||||
- High speed, minimal overhead. This mechanism is not more expensive
|
||||
than simple playback.
|
||||
|
||||
- switching between forwards and backwards stepping should be fast.
|
||||
|
||||
- Maintain synchronisation between streams.
|
||||
|
||||
- Get feedback of the amount of skipped data.
|
||||
|
||||
- Ability to play a certain amount of data at an arbitrary speed.
|
||||
|
||||
We want a system where we can step frames in PAUSED as well as play
|
||||
short segments of data in PLAYING.
|
||||
|
||||
## Use Cases
|
||||
|
||||
* frame stepping in video only pipeline in PAUSED
|
||||
|
||||
```
|
||||
.-----. .-------. .------. .-------.
|
||||
| src | | demux | .-----. | vdec | | vsink |
|
||||
| src->sink src1->|queue|->sink src->sink |
|
||||
'-----' '-------' '-----' '------' '-------'
|
||||
``````
|
||||
|
||||
*
|
||||
- app sets the pipeline to PAUSED to block on the preroll picture
|
||||
|
||||
- app seeks to required position in the stream. This can be done
|
||||
with a positive or negative rate depending on the required frame
|
||||
stepping direction.
|
||||
|
||||
- app steps frames (in `GST_FORMAT_DEFAULT` or `GST_FORMAT_BUFFER)`. The
|
||||
pipeline loses its PAUSED state until the required number of frames have been
|
||||
skipped, it then prerolls again. This skipping is purely done in the sink.
|
||||
|
||||
- sink posts `STEP_DONE` with amount of frames stepped and
|
||||
corresponding time interval.
|
||||
|
||||
* frame stepping in audio/video pipeline in PAUSED
|
||||
|
||||
```
|
||||
.-----. .-------. .------. .-------.
|
||||
| src | | demux | .-----. | vdec | | vsink |
|
||||
| src->sink src1->|queue|->sink src->sink |
|
||||
'-----' | | '-----' '------' '-------'
|
||||
| | .------. .-------.
|
||||
| | .-----. | adec | | asink |
|
||||
| src2->|queue|->sink src->sink |
|
||||
'-------' '-----' '------' '-------'
|
||||
```
|
||||
|
||||
*
|
||||
- app sets the pipeline to PAUSED to block on the preroll picture
|
||||
|
||||
- app seeks to required position in the stream. This can be done
|
||||
with a positive or negative rate depending on the required frame
|
||||
stepping direction.
|
||||
|
||||
- app steps frames (in `GST_FORMAT_DEFAULT` or `GST_FORMAT_BUFFER`) or an
|
||||
amount of time on the video sink. The pipeline loses its PAUSED state until
|
||||
the required number of frames have been skipped, it then prerolls again. This
|
||||
skipping is purely done in the sink.
|
||||
|
||||
- sink posts `STEP_DONE` with amount of frames stepped and
|
||||
corresponding time interval.
|
||||
|
||||
- the app skips the same amount of time on the audiosink to align
|
||||
the streams again. When huge amount of video frames are skipped,
|
||||
there needs to be enough queueing in the pipeline to compensate
|
||||
for the accumulated audio.
|
||||
|
||||
- frame stepping in audio/video pipeline in PLAYING
|
||||
|
||||
- app sets the pipeline to PAUSED to block on the preroll picture
|
||||
|
||||
- app seeks to required position in the stream. This can be done
|
||||
with a positive or negative rate depending on the required frame
|
||||
stepping direction.
|
||||
|
||||
- app configures frames steps (in `GST_FORMAT_DEFAULT` or
|
||||
`GST_FORMAT_BUFFER` or an amount of time on the sink. The step event has
|
||||
a flag indicating live stepping so that the stepping will only happens in
|
||||
PLAYING.
|
||||
|
||||
- app sets pipeline to PLAYING. The pipeline continues PLAYING
|
||||
until it consumed the amount of time.
|
||||
|
||||
- sink posts `STEP_DONE` with amount of frames stepped and
|
||||
corresponding time interval. The sink will then wait for another
|
||||
step event. Since the `STEP_DONE` message was emitted by the sink
|
||||
when it handed off the buffer to the device, there is usually
|
||||
sufficient time to queue a new STEP event so that one can
|
||||
seamlessly continue stepping.
|
||||
|
||||
## events
|
||||
|
||||
A new `GST_EVENT_STEP` event is introduced to start the step operation.
|
||||
The step event is created with the following fields in the structure:
|
||||
|
||||
* **`format`** GST_TYPE_FORMAT: The format of the step units
|
||||
|
||||
* **`amount`** G_TYPE_UINT64: The amount of units to step. A 0 amount
|
||||
immediately completes and can be used to cancel the current step and resume
|
||||
normal non-stepping behaviour to the end of the segment. A -1 amount steps
|
||||
until the end of the segment.
|
||||
|
||||
* **`rate`** G_TYPE_DOUBLE: The rate at which the frames should be stepped in
|
||||
PLAYING mode. 1.0 is the normal playback speed and direction of the segment,
|
||||
2.0 is double speed. A speed of 0.0 is not allowed. When performing a flushing
|
||||
step, the speed is not relevant. Note that we don't allow negative rates here,
|
||||
use a seek with a negative rate first to reverse the playback direction.
|
||||
|
||||
* **`flush`** G_TYPE_BOOLEAN: when flushing is TRUE, the step is performed
|
||||
immediately:
|
||||
|
||||
*
|
||||
- In the PAUSED state the pipeline loses the PAUSED state, the
|
||||
requested amount of data is skipped and the pipeline prerolls again
|
||||
when a non-intermediate step completes. When the pipeline was
|
||||
stepping while the event is sent, the current step operation is
|
||||
updated with the new amount and format. The sink will do a best
|
||||
effort to comply with the new amount.
|
||||
|
||||
- In the PLAYING state, the pipeline loses the PLAYING state, the
|
||||
requested amount of data is skipped (not rendered) from the previous
|
||||
STEP request or from the position of the last PAUSED if no previous
|
||||
STEP operation was performed. The pipeline goes back to the PLAYING
|
||||
state when a non-intermediate step completes.
|
||||
|
||||
- When flushing is FALSE, the step will be performed later.
|
||||
|
||||
- In the PAUSED state the step will be done when going to PLAYING. Any
|
||||
previous step operation will be overridden with the new STEP event.
|
||||
|
||||
- In the PLAYING state the step operation will be performed after the
|
||||
current step operation completes. If there was no previous step
|
||||
operation, the step operation will be performed from the position of
|
||||
the last PAUSED state.
|
||||
|
||||
* **`intermediate`** G_TYPE_BOOLEAN: Signal that this step operation is an
|
||||
intermediate step, part of a series of step operations. It is mostly
|
||||
interesting for stepping in the PAUSED state because the sink will only perform
|
||||
a preroll after a non-intermediate step operation completes. Intermediate steps
|
||||
are useful to flush out data from other sinks in order to not cause excessive
|
||||
queueing. In the PLAYING state the intermediate flag has no visual effect. In
|
||||
all states, the intermediate flag is passed to the corresponding
|
||||
GST_MESSAGE_STEP_DONE.
|
||||
|
||||
The application will create a STEP event to start or stop the stepping
|
||||
operation. Both stepping in PAUSED and PLAYING can be performed by means
|
||||
of the flush flag.
|
||||
|
||||
The event is usually sent to the pipeline, which will typically
|
||||
distribute the event to all of its sinks. For some use cases, like frame
|
||||
stepping on video frames only, the event should only be sent to the
|
||||
video sink and upon reception of the `STEP_DONE` message, one can step
|
||||
the other sinks to align the streams again.
|
||||
|
||||
For large stepping amounts, there needs to be enough queueing in front
|
||||
of all the sinks. If large steps need to be performed, they can be split
|
||||
up into smaller step operations using the "intermediate" flag on the
|
||||
step.
|
||||
|
||||
Since the step event does not update the `base_time` of any of the
|
||||
elements, the sinks should keep track of the amount of stepped data in
|
||||
order to remain synchronized against the clock.
|
||||
|
||||
## messages
|
||||
|
||||
A `GST_MESSAGE_STEP_START` is created. It contains the following
|
||||
fields.
|
||||
|
||||
* **`active`**: If the step was queued or activated.
|
||||
|
||||
* **`format`** GST_TYPE_FORMAT: The format of the step units that queued/activated.
|
||||
|
||||
* **`amount`** G_TYPE_UINT64: The amount of units that were queued/activated.
|
||||
|
||||
* **`rate`** G_TYPE_DOUBLE: The rate and direction at which the frames were queued/activated.
|
||||
|
||||
* **`flush`** G_TYPE_BOOLEAN: If the queued/activated frames will be flushed.
|
||||
|
||||
* **`intermediate`** G_TYPE_BOOLEAN: If this is an intermediate step operation
|
||||
that queued/activated.
|
||||
|
||||
The `STEP_START` message is emitted 2 times:
|
||||
|
||||
- first when an element received the STEP event and queued it. The
|
||||
"active" field will be FALSE in this case.
|
||||
|
||||
- second when the step operation started in the streaming thread. The
|
||||
"active" field is TRUE in this case. After this message is emitted,
|
||||
the application can queue a new step operation.
|
||||
|
||||
The purpose of this message is to find out how many elements participate
|
||||
in the step operation and to queue new step operations at the earliest
|
||||
possible moment.
|
||||
|
||||
A new `GST_MESSAGE_STEP_DONE` message is created. It contains the
|
||||
following fields:
|
||||
|
||||
* **`format`** GST_TYPE_FORMAT: The format of the step units that completed.
|
||||
* **`amount`** G_TYPE_UINT64: The amount of units that were stepped.
|
||||
* **`rate`** G_TYPE_DOUBLE: The rate and direction at which the frames were stepped.
|
||||
* **`flush`** G_TYPE_BOOLEAN: If the stepped frames were flushed.
|
||||
* **`intermediate`** G_TYPE_BOOLEAN: If this is an intermediate step operation that completed.
|
||||
* **`duration`** G_TYPE_UINT64: The total duration of the stepped units in `GST_FORMAT_TIME`.
|
||||
* **`eos`** G_TYPE_BOOLEAN: The step ended because of EOS.
|
||||
|
||||
The message is emitted by the element that performs the step operation.
|
||||
The purpose is to return the duration in `GST_FORMAT_TIME` of the
|
||||
stepped media. This especially interesting to align other stream in case
|
||||
of stepping frames on the video sink element.
|
||||
|
||||
## Direction switch
|
||||
|
||||
When quickly switching between a forwards and a backwards step of, for
|
||||
example, one video frame, we need either:
|
||||
|
||||
1) issue a new seek to change the direction from the current position.
|
||||
2) cache a certain number of stepped frames and walk the cache.
|
||||
|
||||
option 1) might be very slow. For option 2) we would ideally like to
|
||||
offload this caching functionality to a separate element, which means
|
||||
that we need to forward the STEP event upstream. It’s unclear how this
|
||||
could work in a generic way. What is a demuxer supposed to do when it
|
||||
received a step event? a flushing seek to what stream position?
|
105
markdown/design/gstbin.md
Normal file
105
markdown/design/gstbin.md
Normal file
|
@ -0,0 +1,105 @@
|
|||
# GstBin
|
||||
|
||||
GstBin is a container element for other GstElements. This makes it
|
||||
possible to group elements together so that they can be treated as one
|
||||
single GstElement. A GstBin provides a GstBus for the children and
|
||||
collates messages from them.
|
||||
|
||||
## Add/removing elements
|
||||
|
||||
The basic functionality of a bin is to add and remove GstElements
|
||||
to/from it. `gst_bin_add()` and `gst_bin_remove()` perform these
|
||||
operations respectively.
|
||||
|
||||
The bin maintains a parent-child relationship with its elements (see
|
||||
[relations](design/relations.md)).
|
||||
|
||||
## Retrieving elements
|
||||
|
||||
GstBin provides a number of functions to retrieve one or more children
|
||||
from itself. A few examples of the provided functions:
|
||||
|
||||
* `gst_bin_get_by_name()` retrieves an element by name.
|
||||
* `gst_bin_iterate_elements()` returns an iterator to all the children.
|
||||
|
||||
## element management
|
||||
|
||||
The most important function of the GstBin is to distribute all
|
||||
GstElement operations on itself to all of its children. This includes:
|
||||
|
||||
- state changes
|
||||
|
||||
- index get/set
|
||||
|
||||
- clock get/set
|
||||
|
||||
The state change distribution is the most complex and is explained in
|
||||
[states](design/states.md).
|
||||
|
||||
## GstBus
|
||||
|
||||
The GstBin creates a GstBus for its children and distributes it when
|
||||
child elements are added to the bin. The bin attaches a sync handler to
|
||||
receive messages from children. The bus for receiving messages from
|
||||
children is distinct from the bin’s own externally-visible GstBus.
|
||||
|
||||
Messages received from children are forwarded intact onto the bin’s
|
||||
external message bus, except for EOS and SEGMENT_START/DONE which are
|
||||
handled specially.
|
||||
|
||||
ASYNC_START/ASYNC_STOP messages received from the children are used to
|
||||
trigger a recalculation of the current state of the bin, as described in
|
||||
[states](design/states.md).
|
||||
|
||||
The application can retrieve the external GstBus and integrate it in the
|
||||
mainloop or it can just `pop()` messages off in its own thread.
|
||||
|
||||
When a bin goes to READY it will clear all cached messages.
|
||||
|
||||
## EOS
|
||||
|
||||
The sink elements will post an EOS message on the bus when they reach
|
||||
EOS. The EOS message is only posted to the bus when the sink element is
|
||||
in PLAYING.
|
||||
|
||||
The bin collects all EOS messages and forwards it to the application as
|
||||
soon as all the sinks have posted an EOS.
|
||||
|
||||
The list of queued EOS messages is cleared when the bin goes to PAUSED
|
||||
again. This means that all elements should repost the EOS message when
|
||||
going to PLAYING again.
|
||||
|
||||
## SEGMENT_START/DONE
|
||||
|
||||
A bin collects `SEGMENT_START` messages but does not post them to the
|
||||
application. It counts the number of `SEGMENT_START` messages and posts a
|
||||
`SEGMENT_STOP` message to the application when an equal number of
|
||||
`SEGMENT_STOP` messages where received.
|
||||
|
||||
The cached SEGMENT_START/STOP messages are cleared when going to READY.
|
||||
|
||||
## DURATION
|
||||
|
||||
When a DURATION query is performed on a bin, it will forward the query
|
||||
to all its sink elements. The bin will calculate the total duration as
|
||||
the MAX of all returned durations and will then cache the result so that
|
||||
any further query can use the cached version. The reason for caching the
|
||||
result is because the duration of a stream typically does not change
|
||||
that often.
|
||||
|
||||
A `GST_MESSAGE_DURATION_CHANGED` posted by an element will clear the
|
||||
cached duration value so that the bin will query the sinks again. This
|
||||
message is typically posted by elements that calculate the duration of
|
||||
the stream based on some average bitrate, which might change while
|
||||
playing the stream. The `DURATION_CHANGED` message is posted to the
|
||||
application, which can then fetch the updated DURATION.
|
||||
|
||||
## Subclassing
|
||||
|
||||
Subclasses of GstBin are free to implement their own add/remove
|
||||
implementations. It is a good idea to update the GList of children so
|
||||
that the `_iterate()` functions can still be used if the custom bin
|
||||
allows access to its children.
|
||||
|
||||
Any bin subclass can also implement a custom message handler by
|
||||
overriding the default message handler.
|
41
markdown/design/gstbus.md
Normal file
41
markdown/design/gstbus.md
Normal file
|
@ -0,0 +1,41 @@
|
|||
# GstBus
|
||||
|
||||
The GstBus is an object responsible for delivering GstMessages in a
|
||||
first-in first-out way from the streaming threads to the application.
|
||||
|
||||
Since the application typically only wants to deal with delivery of
|
||||
these messages from one thread, the GstBus will marshall the messages
|
||||
between different threads. This is important since the actual streaming
|
||||
of media is done in another threads (streaming threads) than the
|
||||
application. It is also important to not block the streaming threads
|
||||
while the application deals with the message.
|
||||
|
||||
The GstBus provides support for GSource based notifications. This makes
|
||||
it possible to handle the delivery in the glib mainloop. Different
|
||||
GSources can be added to the same bin provided they listen to different
|
||||
message types.
|
||||
|
||||
A message is posted on the bus with the `gst_bus_post()` method. With
|
||||
the `gst_bus_peek()` and `_pop()` methods one can look at or retrieve a
|
||||
previously posted message.
|
||||
|
||||
The bus can be polled with the `gst_bus_poll()` method. This methods
|
||||
blocks up to the specified timeout value until one of the specified
|
||||
messages types is posted on the bus. The application can then `_pop()`
|
||||
the messages from the bus to handle them.
|
||||
|
||||
It is also possible to get messages from the bus without any thread
|
||||
marshalling with the `gst_bus_set_sync_handler()` method. This makes
|
||||
it possible to react to a message in the same thread that posted the
|
||||
message on the bus. This should only be used if the application is able
|
||||
to deal with messages from different threads.
|
||||
|
||||
If no messages are popped from the bus with either a GSource or
|
||||
`gst_bus_pop()`, they remain on the bus.
|
||||
|
||||
When a pipeline or bin goes from READY into NULL state, it will set its
|
||||
bus to flushing, ie. the bus will drop all existing and new messages on
|
||||
the bus, This is necessary because bus messages hold references to the
|
||||
bin/pipeline or its elements, so there are circular references that need
|
||||
to be broken if one ever wants to be able to destroy a bin or pipeline
|
||||
properly.
|
61
markdown/design/gstelement.md
Normal file
61
markdown/design/gstelement.md
Normal file
|
@ -0,0 +1,61 @@
|
|||
# GstElement
|
||||
|
||||
The Element is the most important object in the entire GStreamer system,
|
||||
as it defines the structure of the pipeline. Elements include sources,
|
||||
filters, sinks, and containers (Bins). They may be an intrinsic part of
|
||||
the core GStreamer library, or may be loaded from a plugin. In some
|
||||
cases they’re even fabricated from completely different systems (see the
|
||||
LADSPA plugin). They are generally created from a GstElementFactory,
|
||||
which will be covered in another chapter, but for the intrinsic types
|
||||
they can be created with specific functions.
|
||||
|
||||
Elements contains GstPads (also covered in another chapter), which are
|
||||
subsequently used to connect the Elements together to form a pipeline
|
||||
capable of passing and processing data. They have a parent, which must
|
||||
be another Element. This allows deeply nested pipelines, and the
|
||||
possibility of "black-box" meta-elements.
|
||||
|
||||
## Name
|
||||
|
||||
All elements are named, and while they should ideally be unique in any
|
||||
given pipeline, they do not have to be. The only guaranteed unique name
|
||||
for an element is its complete path in the object hierarchy. In other
|
||||
words, an element’s name is unique inside its parent. (This follows from
|
||||
GstObject’s name explanation)
|
||||
|
||||
This uniqueness is guaranteed through all functions where either
|
||||
parentage or name of an element is changed.
|
||||
|
||||
## Pads
|
||||
|
||||
GstPads are the property of a given GstElement. They provide the
|
||||
connection capability, with allowing arbitrary structure in the graph.
|
||||
For any Element but a source or sink, there will be at least 2 Pads
|
||||
owned by the Element. These pads are stored in a single GList within the
|
||||
Element. Several counters are kept in order to allow quicker
|
||||
determination of the type and properties of a given Element.
|
||||
|
||||
Pads may be added to an element with `_add_pad.` Retrieval is via
|
||||
`_get_static_pad()`, which operates on the name of the Pad (the unique
|
||||
key). This means that all Pads owned by a given Element must have unique
|
||||
names. A pointer to the GList of pads may be obtained with
|
||||
`_iterate_pads`.
|
||||
|
||||
`gst_element_add_pad(element,pads)`: Sets the element as the parent of
|
||||
the pad, then adds the pad to the element’s list of pads, keeping the
|
||||
counts of total, src, and sink pads up to date. Emits the `new_pad`
|
||||
signal with the pad as argument. Fails if either the element or pad are
|
||||
either NULL or not what they claim to be. Should fail if the pad already
|
||||
has a parent. Should fail if the pad is already owned by the element.
|
||||
Should fail if there’s already a pad by that name in the list of pads.
|
||||
|
||||
`pad = gst_element_get_pad(element, "padname")`: Searches through the
|
||||
list of pads
|
||||
|
||||
## Ghost Pads
|
||||
|
||||
More info in [ghostpad](design/gstghostpad.md).
|
||||
|
||||
## State
|
||||
|
||||
An element has a state. More info in [state](design/states.md).
|
451
markdown/design/gstghostpad.md
Normal file
451
markdown/design/gstghostpad.md
Normal file
|
@ -0,0 +1,451 @@
|
|||
# Ghostpads
|
||||
|
||||
GhostPads are used to build complex compound elements out of existing
|
||||
elements. They are used to expose internal element pads on the complex
|
||||
element.
|
||||
|
||||
## Some design requirements
|
||||
|
||||
- Must look like a real GstPad on both sides.
|
||||
- target of Ghostpad must be changeable
|
||||
- target can be initially NULL
|
||||
|
||||
- a GhostPad is implemented using a private GstProxyPad class:
|
||||
|
||||
```
|
||||
GstProxyPad
|
||||
(------------------)
|
||||
| GstPad |
|
||||
|------------------|
|
||||
| GstPad *target |
|
||||
(------------------)
|
||||
| GstPad *internal |
|
||||
(------------------)
|
||||
|
||||
GstGhostPad
|
||||
(------------------) -\
|
||||
| GstPad | |
|
||||
|------------------| |
|
||||
| GstPad *target | > GstProxyPad
|
||||
|------------------| |
|
||||
| GstPad *internal | |
|
||||
|------------------| -/
|
||||
| <private data> |
|
||||
(------------------)
|
||||
```
|
||||
|
||||
A GstGhostPad (X) is _always_ created together with a GstProxyPad (Y).
|
||||
The internal pad pointers are set to point to the eachother. The
|
||||
GstProxyPad pairs have opposite directions, the GstGhostPad has the same
|
||||
direction as the (future) ghosted pad (target).
|
||||
|
||||
(- X --------)
|
||||
| |
|
||||
| target * |
|
||||
|------------|
|
||||
| internal *----+
|
||||
(------------) |
|
||||
^ V
|
||||
| (- Y --------)
|
||||
| | |
|
||||
| | target * |
|
||||
| |------------|
|
||||
+----* internal |
|
||||
(------------)
|
||||
|
||||
Which we will abbreviate to:
|
||||
|
||||
(- X --------)
|
||||
| |
|
||||
| target *--------->//
|
||||
(------------)
|
||||
|
|
||||
(- Y --------)
|
||||
| target *----->//
|
||||
(------------)
|
||||
|
||||
The GstGhostPad (X) is also set as the parent of the GstProxyPad (Y).
|
||||
|
||||
The target is a pointer to the internal pads peer. It is an optimisation to
|
||||
quickly get to the peer of a ghostpad without having to dereference the
|
||||
internal->peer.
|
||||
|
||||
Some use case follow with a description of how the datastructure
|
||||
is modified.
|
||||
|
||||
## Creating a ghostpad with a target:
|
||||
|
||||
gst_ghost_pad_new (char *name, GstPad *target)
|
||||
|
||||
1) create new GstGhostPad X + GstProxyPad Y
|
||||
2) X name set to @name
|
||||
3) X direction is the same as the target, Y is opposite.
|
||||
4) the target of X is set to @target
|
||||
5) Y is linked to @target
|
||||
6) link/unlink and activate functions are set up
|
||||
on GstGhostPad.
|
||||
|
||||
```
|
||||
(--------------
|
||||
(- X --------) |
|
||||
| | |------)
|
||||
| target *------------------> | sink |
|
||||
(------------) -------> |------)
|
||||
| / (--------------
|
||||
(- Y --------) / (pad link)
|
||||
//<-----* target |/
|
||||
(------------)
|
||||
```
|
||||
|
||||
- Automatically takes same direction as target.
|
||||
- target is filled in automatically.
|
||||
|
||||
## Creating a ghostpad without a target
|
||||
|
||||
```
|
||||
gst_ghost_pad_new_no_target (char *name, GstPadDirection dir)
|
||||
```
|
||||
|
||||
1) create new GstGhostPad X + GstProxyPad Y
|
||||
2) X name set to @name
|
||||
3) X direction is @dir
|
||||
5) link/unlink and activate functions are set up on GstGhostPad.
|
||||
|
||||
```
|
||||
(- X --------)
|
||||
| |
|
||||
| target *--------->//
|
||||
(------------)
|
||||
|
|
||||
(- Y --------)
|
||||
| target *----->//
|
||||
(------------)
|
||||
```
|
||||
|
||||
- allows for setting the target later
|
||||
|
||||
## Setting target on an untargetted unlinked ghostpad
|
||||
|
||||
```
|
||||
gst_ghost_pad_set_target (char *name, GstPad *newtarget)
|
||||
|
||||
(- X --------)
|
||||
| |
|
||||
| target *--------->//
|
||||
(------------)
|
||||
|
|
||||
(- Y --------)
|
||||
| target *----->//
|
||||
(------------)
|
||||
```
|
||||
|
||||
1) assert direction of newtarget == X direction
|
||||
2) target is set to newtarget
|
||||
3) internal pad Y is linked to newtarget
|
||||
|
||||
```
|
||||
(--------------
|
||||
(- X --------) |
|
||||
| | |------)
|
||||
| target *------------------> | sink |
|
||||
(------------) -------> |------)
|
||||
| / (--------------
|
||||
(- Y --------) / (pad link)
|
||||
//<-----* target |/
|
||||
(------------)
|
||||
```
|
||||
|
||||
## Setting target on a targetted unlinked ghostpad
|
||||
|
||||
```
|
||||
gst_ghost_pad_set_target (char *name, GstPad *newtarget)
|
||||
|
||||
(--------------
|
||||
(- X --------) |
|
||||
| | |-------)
|
||||
| target *------------------> | sink1 |
|
||||
(------------) -------> |-------)
|
||||
| / (--------------
|
||||
(- Y --------) / (pad link)
|
||||
//<-----* target |/
|
||||
(------------)
|
||||
```
|
||||
|
||||
1) assert direction of newtarget (sink2) == X direction
|
||||
2) unlink internal pad Y and oldtarget
|
||||
3) target is set to newtarget (sink2)
|
||||
4) internal pad Y is linked to newtarget
|
||||
|
||||
```
|
||||
(--------------
|
||||
(- X --------) |
|
||||
| | |-------)
|
||||
| target *------------------> | sink2 |
|
||||
(------------) -------> |-------)
|
||||
| / (--------------
|
||||
(- Y --------) / (pad link)
|
||||
//<-----* target |/
|
||||
(------------)
|
||||
```
|
||||
|
||||
- Linking a pad to an untargetted ghostpad:
|
||||
|
||||
```
|
||||
gst_pad_link (src, X)
|
||||
|
||||
(- X --------)
|
||||
| |
|
||||
| target *--------->//
|
||||
(------------)
|
||||
|
|
||||
(- Y --------)
|
||||
| target *----->//
|
||||
(------------)
|
||||
-------)
|
||||
|
|
||||
(-----|
|
||||
| src |
|
||||
(-----|
|
||||
-------)
|
||||
```
|
||||
|
||||
X is a sink GstGhostPad without a target. The internal GstProxyPad Y has
|
||||
the same direction as the src pad (peer).
|
||||
|
||||
1) link function is called
|
||||
- Y direction is same as @src
|
||||
- Y target is set to @src
|
||||
- Y is activated in the same mode as X
|
||||
- core makes link from @src to X
|
||||
|
||||
```
|
||||
(- X --------)
|
||||
| |
|
||||
| target *----->//
|
||||
>(------------)
|
||||
(real pad link) / |
|
||||
/ (- Y ------)
|
||||
/ -----* target |
|
||||
-------) / / (----------)
|
||||
| / /
|
||||
(-----|/ /
|
||||
| src |<----
|
||||
(-----|
|
||||
-------)
|
||||
```
|
||||
|
||||
## Linking a pad to a targetted ghostpad:
|
||||
|
||||
```
|
||||
gst_pad_link (src, X)
|
||||
|
||||
(--------
|
||||
(- X --------) |
|
||||
| | |------)
|
||||
| target *------------->| sink |
|
||||
(------------) >|------)
|
||||
| / (--------
|
||||
| /
|
||||
| /
|
||||
-------) | / (real pad link)
|
||||
| (- Y ------) /
|
||||
(-----| | |/
|
||||
| src | //<----* target |
|
||||
(-----| (----------)
|
||||
-------)
|
||||
```
|
||||
|
||||
1) link function is called
|
||||
- Y direction is same as @src
|
||||
- Y target is set to @src
|
||||
- Y is activated in the same mode as X
|
||||
- core makes link from @src to X
|
||||
|
||||
```
|
||||
(--------
|
||||
(- X --------) |
|
||||
| | |------)
|
||||
| target *------------->| sink |
|
||||
>(------------) >|------)
|
||||
(real pad link) / | / (--------
|
||||
/ | /
|
||||
/ | /
|
||||
-------) / | / (real pad link)
|
||||
| / (- Y ------) /
|
||||
(-----|/ | |/
|
||||
| src |<-------------* target |
|
||||
(-----| (----------)
|
||||
-------)
|
||||
```
|
||||
|
||||
## Setting target on untargetted linked ghostpad:
|
||||
|
||||
```
|
||||
gst_ghost_pad_set_target (char *name, GstPad *newtarget)
|
||||
|
||||
(- X --------)
|
||||
| |
|
||||
| target *------>//
|
||||
>(------------)
|
||||
(real pad link) / |
|
||||
/ |
|
||||
/ |
|
||||
-------) / |
|
||||
| / (- Y ------)
|
||||
(-----|/ | |
|
||||
| src |<-------------* target |
|
||||
(-----| (----------)
|
||||
-------)
|
||||
```
|
||||
|
||||
1) assert direction of @newtarget == X direction
|
||||
2) X target is set to @newtarget
|
||||
3) Y is linked to @newtarget
|
||||
|
||||
```
|
||||
(--------
|
||||
(- X --------) |
|
||||
| | |------)
|
||||
| target *------------->| sink |
|
||||
>(------------) >|------)
|
||||
(real pad link) / | / (--------
|
||||
/ | /
|
||||
/ | /
|
||||
-------) / | / (real pad link)
|
||||
| / (- Y ------) /
|
||||
(-----|/ | |/
|
||||
| src |<-------------* target |
|
||||
(-----| (----------)
|
||||
-------)
|
||||
```
|
||||
|
||||
## Setting target on targetted linked ghostpad:
|
||||
|
||||
```
|
||||
gst_ghost_pad_set_target (char *name, GstPad *newtarget)
|
||||
|
||||
(--------
|
||||
(- X --------) |
|
||||
| | |-------)
|
||||
| target *------------->| sink1 |
|
||||
>(------------) >|-------)
|
||||
(real pad link) / | / (--------
|
||||
/ | /
|
||||
/ | /
|
||||
-------) / | / (real pad link)
|
||||
| / (- Y ------) /
|
||||
(-----|/ | |/
|
||||
| src |<-------------* target |
|
||||
(-----| (----------)
|
||||
-------)
|
||||
```
|
||||
|
||||
1) assert direction of @newtarget == X direction
|
||||
2) Y and X target are unlinked
|
||||
2) X target is set to @newtarget
|
||||
3) Y is linked to @newtarget
|
||||
|
||||
```
|
||||
(--------
|
||||
(- X --------) |
|
||||
| | |-------)
|
||||
| target *------------->| sink2 |
|
||||
>(------------) >|-------)
|
||||
(real pad link) / | / (--------
|
||||
/ | /
|
||||
/ | /
|
||||
-------) / | / (real pad link)
|
||||
| / (- Y ------) /
|
||||
(-----|/ | |/
|
||||
| src |<-------------* target |
|
||||
(-----| (----------)
|
||||
-------)
|
||||
```
|
||||
|
||||
## Activation
|
||||
|
||||
Sometimes ghost pads should proxy activation functions. This thingie
|
||||
attempts to explain how it should work in the different cases.
|
||||
|
||||
```
|
||||
+---+ +----+ +----+ +----+
|
||||
| A +-----+ B | | C |-------+ D |
|
||||
+---+ +---=+ +=---+ +----+
|
||||
+--=-----------------------------=-+
|
||||
| +=---+ +----+ +----+ +---=+ |
|
||||
| | a +---+ b ==== c +--+ d | |
|
||||
| +----+ +----+ +----+ +----+ |
|
||||
| |
|
||||
+----------------------------------+
|
||||
state change goes from right to left
|
||||
<-----------------------------------------------------------
|
||||
```
|
||||
|
||||
All of the labeled boxes are pads. The dashes (---) show pad links, and
|
||||
the double-lines (===) are internal connections. The box around a, b, c,
|
||||
and d is a bin. B and C are ghost pads, and a and d are proxy pads. The
|
||||
arrow represents the direction of a state change algorithm. Not counting
|
||||
the bin, there are three elements involved here — the parent of D, the
|
||||
parent of A, and the parent of b and c.
|
||||
|
||||
Now, in the state change from READY to PAUSED, assuming the pipeline
|
||||
does not have a live source, all of the pads will end up activated at
|
||||
the end. There are 4 possible activation modes:
|
||||
|
||||
1) AD and ab in PUSH, cd and CD in PUSH
|
||||
2) AD and ab in PUSH, cd and CD in PULL
|
||||
3) AD and ab in PULL, cd and CD in PUSH
|
||||
4) AD and ab in PULL, cd and CD in PULL
|
||||
|
||||
When activating (1), the state change algorithm will first visit the
|
||||
parent of D and activate D in push mode. Then it visits the bin. The bin
|
||||
will first change the state of its child before activating its pads.
|
||||
That means c will be activated in push mode. \[\*\] At this point, d and
|
||||
C should also be active in push mode, because it could be that
|
||||
activating c in push mode starts a thread, which starts pushing to pads
|
||||
which aren’t ready yet. Then b is activated in push mode. Then, the bin
|
||||
activates C in push mode, which should already be in push mode, so
|
||||
nothing is done. It then activates B in push mode, which activates b in
|
||||
push mode, but it’s already there, then activates a in push mode as
|
||||
well. The order of activating a and b does not matter in this case.
|
||||
Then, finally, the state change algorithm moves to the parent of A,
|
||||
activates A in push mode, and dataflow begins.
|
||||
|
||||
\[\*\] Not yet implemented.
|
||||
|
||||
Activation mode (2) is implausible, so we can ignore it for now. That
|
||||
leaves us with the rest.
|
||||
|
||||
(3) is the same as (1) until you get to activating b. Activating b will
|
||||
proxy directly to activating a, which will activate B and A as well.
|
||||
Then when the state change algorithm gets to B and A it sees that they
|
||||
are already active, so it ignores them.
|
||||
|
||||
Similarly in (4), activating D will cause the activation of all of the
|
||||
rest of the pads, in this order: C d c b a B A. Then when the state
|
||||
change gets to the other elements they are already active, and in fact
|
||||
data flow is already occurring.
|
||||
|
||||
So, from these scenarios, we can distill how ghost pad activation
|
||||
functions should work:
|
||||
|
||||
Ghost source pads (e.g. C): push: called by: element state change
|
||||
handler behavior: just return TRUE pull: called by: peer’s activatepull
|
||||
behavior: change the internal pad, which proxies to its peer e.g. C
|
||||
changes d which changes c.
|
||||
|
||||
Internal sink pads (e.g. d): push: called by: nobody (doesn’t seem
|
||||
possible) behavior: n/a pull: called by: ghost pad behavior: proxy to
|
||||
peer first
|
||||
|
||||
Internal src pads (e.g. a): push: called by: ghost pad behavior:
|
||||
activate peer in push mode pull: called by: peer’s activatepull
|
||||
behavior: proxy to ghost pad, which proxies to its peer (e.g. a calls B
|
||||
which calls A)
|
||||
|
||||
Ghost sink pads (e.g. B): push: called by: element state change handler
|
||||
behavior: change the internal pad, which proxies to peer (e.g. B changes
|
||||
a which changes b) pull: called by: internal pad behavior: proxy to peer
|
||||
|
||||
It doesn’t really make sense to have activation functions on proxy pads
|
||||
that aren’t part of a ghost pad arrangement.
|
79
markdown/design/gstobject.md
Normal file
79
markdown/design/gstobject.md
Normal file
|
@ -0,0 +1,79 @@
|
|||
# GstObject
|
||||
|
||||
The base class for the entire GStreamer hierarchy is the GstObject.
|
||||
|
||||
## Parentage
|
||||
|
||||
A pointer is available to store the current parent of the object. This
|
||||
is one of the two fundamental requirements for a hierarchical system
|
||||
such as GStreamer (for the other, read up on GstBin). Three functions
|
||||
are provided: `_set_parent()`, `_get_parent()`, and `_unparent()`. The
|
||||
third is required because there is an explicit check in `_set_parent()`:
|
||||
an object must not already have a parent if you wish to set one. You
|
||||
must unparent the object first. This allows for new additions later.
|
||||
|
||||
- GstObject’s that can be parented: GstElement (inside a bin) GstPad (inside
|
||||
an element)
|
||||
|
||||
## Naming
|
||||
|
||||
- names of objects cannot be changed when they are parented
|
||||
- names of objects should be unique across parent
|
||||
- set_name() can fail because of this
|
||||
- as can gst_element_add_pad()/gst_bin_add_element()
|
||||
- gst_object_set_name() only changes the object’s name
|
||||
- objects also have a name_prefix that is used to prefix the object
|
||||
name during debugging and identification
|
||||
- there are object-specific set_name’s() which also set the
|
||||
name_prefix on the object. This is useful for debugging purposes to
|
||||
give the object a more identifiable name. Typically a parent will
|
||||
call _set_name_prefix on children, taking a lock on them to do
|
||||
so.
|
||||
|
||||
## Locking
|
||||
|
||||
The GstObject contains the necessary primitives to lock the object in a
|
||||
thread-safe manner. This will be used to provide general thread-safety
|
||||
as needed. However, this lock is generic, i.e. it covers the whole
|
||||
object.
|
||||
|
||||
The object LOCK is a very lowlevel lock that should only be held to
|
||||
access the object properties for short periods of code.
|
||||
|
||||
All members of the GstObject structure marked as `/**< public >**/ /*
|
||||
with LOCK */` are protected by this lock. These members can only be
|
||||
accessed for reading or writing while the lock is held. All members
|
||||
should be copied or reffed if they are used after releasing the LOCK.
|
||||
|
||||
Note that this does **not** mean that no other thread can modify the
|
||||
object at the same time that the lock is held. It only means that any
|
||||
two sections of code that obey the lock are guaranteed to not be running
|
||||
simultaneously. "The lock is voluntary and cooperative".
|
||||
|
||||
This lock will ideally be used for parentage, flags and naming, which is
|
||||
reasonable, since they are the only possible things to protect in the
|
||||
GstObject.
|
||||
|
||||
## Locking order
|
||||
|
||||
In parent-child situations the lock of the parent must always be taken
|
||||
first before taking the lock of the child. It is NOT allowed to hold the
|
||||
child lock before taking the parent lock.
|
||||
|
||||
This policy allows for parents to iterate their children and setting
|
||||
properties on them.
|
||||
|
||||
Whenever a nested lock needs to be taken on objects not involved in a
|
||||
parent-child relation (eg. pads), an explictic locking order has to be
|
||||
defined.
|
||||
|
||||
## Path Generation
|
||||
|
||||
Due to the base nature of the GstObject, it becomes the only reasonable
|
||||
place to put this particular function (_get_path_string). It will
|
||||
generate a string describing the parent hierarchy of a given GstObject.
|
||||
|
||||
## Flags
|
||||
|
||||
Each object in the GStreamer object hierarchy can have flags associated
|
||||
with it, which are used to describe a state or a feature of the object.
|
79
markdown/design/gstpipeline.md
Normal file
79
markdown/design/gstpipeline.md
Normal file
|
@ -0,0 +1,79 @@
|
|||
# GstPipeline
|
||||
|
||||
A GstPipeline is usually a toplevel bin and provides all of its children
|
||||
with a clock.
|
||||
|
||||
A GstPipeline also provides a toplevel GstBus (see [gstbus](design/gstbus.md))
|
||||
|
||||
The pipeline also calculates the running\_time based on the selected
|
||||
clock (see also clocks.txt and [synchronisation](design/synchronisation.md)).
|
||||
|
||||
The pipeline will calculate a global latency for the elements in the
|
||||
pipeline. (See also [latency](design/latency.md)).
|
||||
|
||||
## State changes
|
||||
|
||||
In addition to the normal state change procedure of its parent class
|
||||
GstBin, the pipeline performs the following actions during a state
|
||||
change:
|
||||
|
||||
- NULL → READY:
|
||||
- set the bus to non-flushing
|
||||
- READY → PAUSED:
|
||||
- reset the running_time to 0
|
||||
- PAUSED → PLAYING:
|
||||
- Select and a clock.
|
||||
- calculate base_time using the running_time.
|
||||
- calculate and distribute latency.
|
||||
- set clock and base_time on all elements before performing the state
|
||||
change.
|
||||
- PLAYING → PAUSED:
|
||||
- calculate the running_time when the pipeline was PAUSED.
|
||||
- READY → NULL:
|
||||
- set the bus to flushing (when auto-flushing is enabled)
|
||||
|
||||
The running_time represents the total elapsed time, measured in clock
|
||||
units, that the pipeline spent in the PLAYING state (see
|
||||
[synchronisation](design/synchronisation.md)). The running_time is set to 0 after a
|
||||
flushing seek.
|
||||
|
||||
## Clock selection
|
||||
|
||||
Since all of the children of a GstPipeline must use the same clock, the
|
||||
pipeline must select a clock. This clock selection happens when the
|
||||
pipeline goes to the PLAYING state.
|
||||
|
||||
The default clock selection algorithm works as follows:
|
||||
|
||||
- If the application selected a clock, use that clock. (see below)
|
||||
|
||||
- Use the clock of most upstream element that can provide a clock.
|
||||
This selection is performed by iterating the element starting from
|
||||
the sinks going upstream.
|
||||
- since this selection procedure happens in the PAUSED→PLAYING
|
||||
state change, all the sinks are prerolled and we can thus be
|
||||
sure that each sink is linked to some upstream element.
|
||||
- in the case of a live pipeline (`NO_PREROLL`), the sink will not
|
||||
yet be prerolled and the selection process will select the clock
|
||||
of a more upstream element.
|
||||
|
||||
- use GstSystemClock, this only happens when no element provides a
|
||||
usable clock.
|
||||
|
||||
The application can influence this clock selection with two methods:
|
||||
`gst_pipeline_use_clock()` and `gst_pipeline_auto_clock()`.
|
||||
|
||||
The `_use_clock()` method forces the use of a specific clock on the
|
||||
pipeline regardless of what clock providers are children of the
|
||||
pipeline. Setting NULL disables the clock completely and makes the
|
||||
pipeline run as fast as possible.
|
||||
|
||||
The `_auto_clock()` method removes the fixed clock and reactivates the
|
||||
auto- matic clock selection algorithm described above.
|
||||
|
||||
## GstBus
|
||||
|
||||
A GstPipeline provides a GstBus to the application. The bus can be
|
||||
retrieved with `gst_pipeline_get_bus()` and can then be used to
|
||||
retrieve messages posted by the elements in the pipeline (see
|
||||
[gstbus](design/gstbus.md)).
|
6
markdown/design/index.md
Normal file
6
markdown/design/index.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
# GStreamer design documents
|
||||
|
||||
This section gathers the various GStreamer design documents.
|
||||
Those documents are the technical documents that have been produce while
|
||||
developing or refactoring parts of the GStreamer design to explain the
|
||||
problem and the design solution we came up to solve them.
|
409
markdown/design/latency.md
Normal file
409
markdown/design/latency.md
Normal file
|
@ -0,0 +1,409 @@
|
|||
# Latency
|
||||
|
||||
The latency is the time it takes for a sample captured at timestamp 0 to
|
||||
reach the sink. This time is measured against the clock in the pipeline.
|
||||
For pipelines where the only elements that synchronize against the clock
|
||||
are the sinks, the latency is always 0 since no other element is
|
||||
delaying the buffer.
|
||||
|
||||
For pipelines with live sources, a latency is introduced, mostly because
|
||||
of the way a live source works. Consider an audio source, it will start
|
||||
capturing the first sample at time 0. If the source pushes buffers with
|
||||
44100 samples at a time at 44100Hz it will have collected the buffer at
|
||||
second 1. Since the timestamp of the buffer is 0 and the time of the
|
||||
clock is now \>= 1 second, the sink will drop this buffer because it is
|
||||
too late. Without any latency compensation in the sink, all buffers will
|
||||
be dropped.
|
||||
|
||||
The situation becomes more complex in the presence of:
|
||||
|
||||
- 2 live sources connected to 2 live sinks with different latencies
|
||||
- audio/video capture with synchronized live preview.
|
||||
- added latencies due to effects (delays, resamplers…)
|
||||
|
||||
- 1 live source connected to 2 live sinks
|
||||
- firewire DV
|
||||
- RTP, with added latencies because of jitter buffers.
|
||||
|
||||
- mixed live source and non-live source scenarios.
|
||||
- synchronized audio capture with non-live playback. (overdubs,..)
|
||||
|
||||
- clock slaving in the sinks due to the live sources providing their
|
||||
own clocks.
|
||||
|
||||
To perform the needed latency corrections in the above scenarios, we
|
||||
must develop an algorithm to calculate a global latency for the
|
||||
pipeline. The algorithm must be extensible so that it can optimize the
|
||||
latency at runtime. It must also be possible to disable or tune the
|
||||
algorithm based on specific application needs (required minimal
|
||||
latency).
|
||||
|
||||
## Pipelines without latency compensation
|
||||
|
||||
We show some examples to demonstrate the problem of latency in typical
|
||||
capture pipelines.
|
||||
|
||||
### Example 1
|
||||
|
||||
An audio capture/playback pipeline.
|
||||
|
||||
* asrc: audio source, provides a clock
|
||||
* asink audio sink, provides a clock
|
||||
|
||||
.--------------------------.
|
||||
| pipeline |
|
||||
| .------. .-------. |
|
||||
| | asrc | | asink | |
|
||||
| | src -> sink | |
|
||||
| '------' '-------' |
|
||||
'--------------------------'
|
||||
|
||||
* *NULL→READY*:
|
||||
* asink: *NULL→READY*: probes device, returns `SUCCESS`
|
||||
* asrc: *NULL→READY*: probes device, returns `SUCCESS`
|
||||
|
||||
* *READY→PAUSED*:
|
||||
* asink: *READY:→PAUSED* open device, returns `ASYNC`
|
||||
* asrc: *READY→PAUSED*: open device, returns `NO_PREROLL`
|
||||
|
||||
- Since the source is a live source, it will only produce data in
|
||||
the `PLAYING` state. To note this fact, it returns `NO_PREROLL`
|
||||
from the state change function.
|
||||
|
||||
- This sink returns `ASYNC` because it can only complete the state
|
||||
change to `PAUSED` when it receives the first buffer.
|
||||
|
||||
At this point the pipeline is not processing data and the clock is not
|
||||
running. Unless a new action is performed on the pipeline, this situation will
|
||||
never change.
|
||||
|
||||
* *PAUSED→PLAYING*: asrc clock selected because it is the most upstream clock
|
||||
provider. asink can only provide a clock when it received the first buffer and
|
||||
configured the device with the samplerate in the caps.
|
||||
|
||||
* sink: *PAUSED:→PLAYING*, sets pending state to `PLAYING`, returns `ASYNC` because it
|
||||
is not prerolled. The sink will commit state to `PLAYING` when it prerolls.
|
||||
* src: *PAUSED→PLAYING*: starts pushing buffers.
|
||||
|
||||
- since the sink is still performing a state change from `READY→PAUSED`, it remains ASYNC. The pending state will be set to
|
||||
PLAYING.
|
||||
|
||||
- The clock starts running as soon as all the elements have been
|
||||
set to PLAYING.
|
||||
|
||||
- the source is a live source with a latency. Since it is
|
||||
synchronized with the clock, it will produce a buffer with
|
||||
timestamp 0 and duration D after time D, ie. it will only be
|
||||
able to produce the last sample of the buffer (with timestamp D)
|
||||
at time D. This latency depends on the size of the buffer.
|
||||
|
||||
- the sink will receive the buffer with timestamp 0 at time \>= D.
|
||||
At this point the buffer is too late already and might be
|
||||
dropped. This state of constantly dropping data will not change
|
||||
unless a constant latency correction is added to the incoming
|
||||
buffer timestamps.
|
||||
|
||||
The problem is due to the fact that the sink is set to (pending) PLAYING
|
||||
without being prerolled, which only happens in live pipelines.
|
||||
|
||||
### Example 2
|
||||
|
||||
An audio/video capture/playback pipeline. We capture both audio and video and
|
||||
have them played back synchronized again.
|
||||
|
||||
* asrc: audio source, provides a clock
|
||||
* asink audio sink, provides a clock
|
||||
* vsrc: video source
|
||||
* vsink video sink
|
||||
|
||||
.--------------------------.
|
||||
| pipeline |
|
||||
| .------. .-------. |
|
||||
| | asrc | | asink | |
|
||||
| | src -> sink | |
|
||||
| '------' '-------' |
|
||||
| .------. .-------. |
|
||||
| | vsrc | | vsink | |
|
||||
| | src -> sink | |
|
||||
| '------' '-------' |
|
||||
'--------------------------'
|
||||
|
||||
The state changes happen in the same way as example 1. Both sinks end up with
|
||||
pending state of `PLAYING` and a return value of ASYNC until they receive the
|
||||
first buffer.
|
||||
|
||||
For audio and video to be played in sync, both sinks must compensate for the
|
||||
latency of its source but must also use exactly the same latency correction.
|
||||
|
||||
Suppose asrc has a latency of 20ms and vsrc a latency of 33ms, the total
|
||||
latency in the pipeline has to be at least 33ms. This also means that the
|
||||
pipeline must have at least a 33 - 20 = 13ms buffering on the audio stream or
|
||||
else the audio src will underrun while the audiosink waits for the previous
|
||||
sample to play.
|
||||
|
||||
### Example 3
|
||||
|
||||
An example of the combination of a non-live (file) and a live source (vsrc)
|
||||
connected to live sinks (vsink, sink).
|
||||
|
||||
.--------------------------.
|
||||
| pipeline |
|
||||
| .------. .-------. |
|
||||
| | file | | sink | |
|
||||
| | src -> sink | |
|
||||
| '------' '-------' |
|
||||
| .------. .-------. |
|
||||
| | vsrc | | vsink | |
|
||||
| | src -> sink | |
|
||||
| '------' '-------' |
|
||||
'--------------------------'
|
||||
|
||||
The state changes happen in the same way as example 1. Except sink will be
|
||||
able to preroll (commit its state to PAUSED).
|
||||
|
||||
In this case sink will have no latency but vsink will. The total latency
|
||||
should be that of vsink.
|
||||
|
||||
Note that because of the presence of a live source (vsrc), the pipeline can be
|
||||
set to playing before sink is able to preroll. Without compensation for the
|
||||
live source, this might lead to synchronisation problems because the latency
|
||||
should be configured in the element before it can go to PLAYING.
|
||||
|
||||
### Example 4
|
||||
|
||||
An example of the combination of a non-live and a live source. The non-live
|
||||
source is connected to a live sink and the live source to a non-live sink.
|
||||
|
||||
.--------------------------.
|
||||
| pipeline |
|
||||
| .------. .-------. |
|
||||
| | file | | sink | |
|
||||
| | src -> sink | |
|
||||
| '------' '-------' |
|
||||
| .------. .-------. |
|
||||
| | vsrc | | files | |
|
||||
| | src -> sink | |
|
||||
| '------' '-------' |
|
||||
'--------------------------'
|
||||
|
||||
The state changes happen in the same way as example 3. Sink will be
|
||||
able to preroll (commit its state to PAUSED). files will not be able to
|
||||
preroll.
|
||||
|
||||
sink will have no latency since it is not connected to a live source. files
|
||||
does not do synchronisation so it does not care about latency.
|
||||
|
||||
The total latency in the pipeline is 0. The vsrc captures in sync with the
|
||||
playback in sink.
|
||||
|
||||
As in example 3, sink can only be set to `PLAYING` after it successfully
|
||||
prerolled.
|
||||
|
||||
## State Changes
|
||||
|
||||
A Sink is never set to `PLAYING` before it is prerolled. In order to do
|
||||
this, the pipeline (at the GstBin level) keeps track of all elements
|
||||
that require preroll (the ones that return ASYNC from the state change).
|
||||
These elements posted a `ASYNC_START` message without a matching
|
||||
`ASYNC_DONE` message.
|
||||
|
||||
The pipeline will not change the state of the elements that are still
|
||||
doing an ASYNC state change.
|
||||
|
||||
When an ASYNC element prerolls, it commits its state to PAUSED and posts
|
||||
an `ASYNC_DONE` message. The pipeline notices this `ASYNC_DONE` message
|
||||
and matches it with the `ASYNC_START` message it cached for the
|
||||
corresponding element.
|
||||
|
||||
When all `ASYNC_START` messages are matched with an `ASYNC_DONE` message,
|
||||
the pipeline proceeds with setting the elements to the final state
|
||||
again.
|
||||
|
||||
The base time of the element was already set by the pipeline when it
|
||||
changed the NO\_PREROLL element to PLAYING. This operation has to be
|
||||
performed in the separate async state change thread (like the one
|
||||
currently used for going from `PAUSED→PLAYING` in a non-live pipeline).
|
||||
|
||||
## Query
|
||||
|
||||
The pipeline latency is queried with the LATENCY query.
|
||||
|
||||
* **`live`** G_TYPE_BOOLEAN (default FALSE): - if a live element is found upstream
|
||||
|
||||
* **`min-latency`** G_TYPE_UINT64 (default 0, must not be NONE): - the minimum
|
||||
latency in the pipeline, meaning the minimum time downstream elements
|
||||
synchronizing to the clock have to wait until they can be sure that all data
|
||||
for the current running time has been received.
|
||||
|
||||
Elements answering the latency query and introducing latency must
|
||||
set this to the maximum time for which they will delay data, while
|
||||
considering upstream's minimum latency. As such, from an element's
|
||||
perspective this is *not* its own minimum latency but its own
|
||||
maximum latency.
|
||||
Considering upstream's minimum latency in general means that the
|
||||
element's own value is added to upstream's value, as this will give
|
||||
the overall minimum latency of all elements from the source to the
|
||||
current element:
|
||||
|
||||
min_latency = upstream_min_latency + own_min_latency
|
||||
|
||||
* **`max-latency`** G_TYPE_UINT64 (default 0, NONE meaning infinity): - the
|
||||
maximum latency in the pipeline, meaning the maximum time an element
|
||||
synchronizing to the clock is allowed to wait for receiving all data for the
|
||||
current running time. Waiting for a longer time will result in data loss,
|
||||
overruns and underruns of buffers and in general breaks synchronized data flow
|
||||
in the pipeline.
|
||||
|
||||
Elements answering the latency query should set this to the maximum
|
||||
time for which they can buffer upstream data without blocking or
|
||||
dropping further data. For an element this value will generally be
|
||||
its own minimum latency, but might be bigger than that if it can
|
||||
buffer more data. As such, queue elements can be used to increase
|
||||
the maximum latency.
|
||||
|
||||
The value set in the query should again consider upstream's maximum
|
||||
latency:
|
||||
|
||||
- If the current element has blocking buffering, i.e. it does not drop data by
|
||||
itself when its internal buffer is full, it should just add its own maximum
|
||||
latency (i.e. the size of its internal buffer) to upstream's value. If
|
||||
upstream's maximum latency, or the elements internal maximum latency was NONE
|
||||
(i.e. infinity), it will be set to infinity.
|
||||
|
||||
|
||||
if (upstream_max_latency == NONE || own_max_latency == NONE)
|
||||
max_latency = NONE;
|
||||
else
|
||||
max_latency = upstream_max_latency + own_max_latency
|
||||
|
||||
|
||||
If the element has multiple sinkpads, the minimum upstream latency is
|
||||
the maximum of all live upstream minimum latencies.
|
||||
|
||||
If the current element has leaky buffering, i.e. it drops data by itself
|
||||
when its internal buffer is full, it should take the minimum of its own
|
||||
maximum latency and upstream’s. Examples for such elements are audio sinks
|
||||
and sources with an internal ringbuffer, leaky queues and in general live
|
||||
sources with a limited amount of internal buffers that can be used.
|
||||
|
||||
max_latency = MIN (upstream_max_latency, own_max_latency)
|
||||
|
||||
> Note: many GStreamer base classes allow subclasses to set a
|
||||
> minimum and maximum latency and handle the query themselves. These
|
||||
> base classes assume non-leaky (i.e. blocking) buffering for the
|
||||
> maximum latency. The base class' default query handler needs to be
|
||||
> overridden to correctly handle leaky buffering.
|
||||
|
||||
If the element has multiple sinkpads, the maximum upstream latency is the
|
||||
minimum of all live upstream maximum latencies.
|
||||
|
||||
## Event
|
||||
|
||||
The latency in the pipeline is configured with the LATENCY event, which
|
||||
contains the following fields:
|
||||
|
||||
* **`latency`** G_TYPE_UINT64: the configured latency in the pipeline
|
||||
|
||||
## Latency compensation
|
||||
|
||||
Latency calculation and compensation is performed before the pipeline
|
||||
proceeds to the `PLAYING` state.
|
||||
|
||||
When the pipeline collected all `ASYNC_DONE` messages it can calculate
|
||||
the global latency as follows:
|
||||
|
||||
- perform a latency query on all sinks
|
||||
- sources set their minimum and maximum latency
|
||||
- other elements add their own values as described above
|
||||
- latency = MAX (all min latencies)
|
||||
- if MIN (all max latencies) \< latency we have an impossible
|
||||
situation and we must generate an error indicating that this
|
||||
pipeline cannot be played. This usually means that there is not
|
||||
enough buffering in some chain of the pipeline. A queue can be added
|
||||
to those chains.
|
||||
|
||||
The sinks gather this information with a LATENCY query upstream.
|
||||
Intermediate elements pass the query upstream and add the amount of
|
||||
latency they add to the result.
|
||||
|
||||
ex1: sink1: \[20 - 20\] sink2: \[33 - 40\]
|
||||
|
||||
MAX (20, 33) = 33
|
||||
MIN (20, 40) = 20 < 33 -> impossible
|
||||
|
||||
ex2: sink1: \[20 - 50\] sink2: \[33 - 40\]
|
||||
|
||||
MAX (20, 33) = 33
|
||||
MIN (50, 40) = 40 >= 33 -> latency = 33
|
||||
|
||||
The latency is set on the pipeline by sending a LATENCY event to the
|
||||
sinks in the pipeline. This event configures the total latency on the
|
||||
sinks. The sink forwards this LATENCY event upstream so that
|
||||
intermediate elements can configure themselves as well.
|
||||
|
||||
After this step, the pipeline continues setting the pending state on its
|
||||
elements.
|
||||
|
||||
A sink adds the latency value, received in the LATENCY event, to the
|
||||
times used for synchronizing against the clock. This will effectively
|
||||
delay the rendering of the buffer with the required latency. Since this
|
||||
delay is the same for all sinks, all sinks will render data relatively
|
||||
synchronised.
|
||||
|
||||
## Flushing a playing pipeline
|
||||
|
||||
We can implement resynchronisation after an uncontrolled FLUSH in (part
|
||||
of) a pipeline in the same way. Indeed, when a flush is performed on a
|
||||
PLAYING live element, a new base time must be distributed to this
|
||||
element.
|
||||
|
||||
A flush in a pipeline can happen in the following cases:
|
||||
|
||||
- flushing seek in the pipeline
|
||||
|
||||
- performed by the application on the pipeline
|
||||
|
||||
- performed by the application on an element
|
||||
|
||||
- flush preformed by an element
|
||||
|
||||
- after receiving a navigation event (DVD, …)
|
||||
|
||||
When a playing sink is flushed by a `FLUSH_START` event, an `ASYNC_START`
|
||||
message is posted by the element. As part of the message, the fact that
|
||||
the element got flushed is included. The element also goes to a pending
|
||||
PAUSED state and has to be set to the `PLAYING` state again later.
|
||||
|
||||
The `ASYNC_START` message is kept by the parent bin. When the element
|
||||
prerolls, it posts an `ASYNC_DONE` message.
|
||||
|
||||
When all `ASYNC_START` messages are matched with an `ASYNC_DONE` message,
|
||||
the bin will capture a new base\_time from the clock and will bring all
|
||||
the sinks back to `PLAYING` after setting the new base time on them. It’s
|
||||
also possible to perform additional latency calculations and adjustments
|
||||
before doing this.
|
||||
|
||||
## Dynamically adjusting latency
|
||||
|
||||
An element that want to change the latency in the pipeline can do this
|
||||
by posting a LATENCY message on the bus. This message instructs the
|
||||
pipeline to:
|
||||
|
||||
- query the latency in the pipeline (which might now have changed)
|
||||
with a LATENCY query.
|
||||
|
||||
- redistribute a new global latency to all elements with a LATENCY
|
||||
event.
|
||||
|
||||
A use case where the latency in a pipeline can change could be a network
|
||||
element that observes an increased inter packet arrival jitter or
|
||||
excessive packet loss and decides to increase its internal buffering
|
||||
(and thus the latency). The element must post a LATENCY message and
|
||||
perform the additional latency adjustments when it receives the LATENCY
|
||||
event from the downstream peer element.
|
||||
|
||||
In a similar way can the latency be decreased when network conditions
|
||||
are improving again.
|
||||
|
||||
Latency adjustments will introduce glitches in playback in the sinks and
|
||||
must only be performed in special conditions.
|
53
markdown/design/live-source.md
Normal file
53
markdown/design/live-source.md
Normal file
|
@ -0,0 +1,53 @@
|
|||
# Live sources
|
||||
|
||||
A live source is a source that cannot be arbitrarily `PAUSED` without
|
||||
losing data.
|
||||
|
||||
A live source such as an element capturing audio or video need to be
|
||||
handled in a special way. It does not make sense to start the dataflow
|
||||
in the `PAUSED` state for those devices as the user might wait a long time
|
||||
between going from `PAUSED` to PLAYING, making the previously captured
|
||||
buffers irrelevant.
|
||||
|
||||
A live source therefore only produces buffers in the PLAYING state. This
|
||||
has implications for sinks waiting for a buffer to complete the preroll
|
||||
state since such a buffer might never arrive.
|
||||
|
||||
Live sources return `NO_PREROLL` when going to the `PAUSED` state to inform
|
||||
the bin/pipeline that this element will not be able to produce data in
|
||||
the `PAUSED` state. `NO_PREROLL` should be returned for both READY→PAUSED
|
||||
and PLAYING→PAUSED.
|
||||
|
||||
When performing a get\_state() on a bin with a non-zero timeout value,
|
||||
the bin must be sure that there are no live sources in the pipeline
|
||||
because else the get\_state() function would block on the sinks.
|
||||
|
||||
A gstbin therefore always performs a zero timeout get\_state() on its
|
||||
elements to discover the `NO_PREROLL` (and ERROR) elements before
|
||||
performing a blocking wait.
|
||||
|
||||
## Scheduling
|
||||
|
||||
Live sources will not produce data in the paused state. They block in
|
||||
the getrange function or in the loop function until they go to PLAYING.
|
||||
|
||||
## Latency
|
||||
|
||||
The live source timestamps its data with the time of the clock at the
|
||||
time the data was captured. Normally it will take some time to capture
|
||||
the first sample of data and the last sample. This means that when the
|
||||
buffer arrives at the sink, it will already be late and will be dropped.
|
||||
|
||||
The latency is the time it takes to construct one buffer of data. This
|
||||
latency is exposed with a `LATENCY` query.
|
||||
|
||||
See [latency](design/latency.md)
|
||||
|
||||
## Timestamps
|
||||
|
||||
Live sources always timestamp their buffers with the running\_time of
|
||||
the pipeline. This is needed to be able to match the timestamps of
|
||||
different live sources in order to synchronize them.
|
||||
|
||||
This is in contrast to non-live sources, which timestamp their buffers
|
||||
starting from running\_time 0.
|
165
markdown/design/memory.md
Normal file
165
markdown/design/memory.md
Normal file
|
@ -0,0 +1,165 @@
|
|||
# GstMemory
|
||||
|
||||
This document describes the design of the memory objects.
|
||||
|
||||
GstMemory objects are usually added to GstBuffer objects and contain the
|
||||
multimedia data passed around in the pipeline.
|
||||
|
||||
## Requirements
|
||||
|
||||
- It must be possible to have different memory allocators
|
||||
- It must be possible to efficiently share memory objects, copy, span and trim.
|
||||
|
||||
## Memory layout
|
||||
|
||||
`GstMemory` manages a memory region. The accessible part of the managed region is
|
||||
defined by an offset relative to the start of the region and a size. This
|
||||
means that the managed region can be larger than what is visible to the user of
|
||||
GstMemory API.
|
||||
|
||||
Schematically, GstMemory has a pointer to a memory region of _maxsize_. The area
|
||||
starting from `offset` and `size` is accessible.
|
||||
|
||||
```
|
||||
memory
|
||||
GstMemory ->*----------------------------------------------------*
|
||||
^----------------------------------------------------^
|
||||
maxsize
|
||||
^--------------------------------------^
|
||||
offset size
|
||||
```
|
||||
|
||||
The current properties of the accessible memory can be retrieved with:
|
||||
|
||||
``` c
|
||||
gsize gst_memory_get_sizes (GstMemory *mem, gsize *offset, gsize *maxsize);
|
||||
```
|
||||
|
||||
The offset and size can be changed with:
|
||||
|
||||
``` c
|
||||
void gst_memory_resize (GstMemory *mem, gssize offset, gsize size);
|
||||
```
|
||||
|
||||
## Allocators
|
||||
|
||||
GstMemory objects are created by allocators. Allocators are a subclass
|
||||
of GstObject and can be subclassed to make custom allocators.
|
||||
|
||||
``` c
|
||||
struct _GstAllocator {
|
||||
GstObject object;
|
||||
|
||||
const gchar *mem_type;
|
||||
|
||||
GstMemoryMapFunction mem_map;
|
||||
GstMemoryUnmapFunction mem_unmap;
|
||||
GstMemoryCopyFunction mem_copy;
|
||||
GstMemoryShareFunction mem_share;
|
||||
GstMemoryIsSpanFunction mem_is_span;
|
||||
};
|
||||
```
|
||||
|
||||
The allocator class has 2 virtual methods. One to create a GstMemory,
|
||||
another to free it again.
|
||||
|
||||
``` c
|
||||
struct _GstAllocatorClass {
|
||||
GstObjectClass object_class;
|
||||
|
||||
GstMemory * (*alloc) (GstAllocator *allocator, gsize size,
|
||||
GstAllocationParams *params);
|
||||
void (*free) (GstAllocator *allocator, GstMemory *memory);
|
||||
};
|
||||
```
|
||||
|
||||
Allocators are refcounted. It is also possible to register the allocator to the
|
||||
GStreamer system. This way, the allocator can be retrieved by name.
|
||||
|
||||
After an allocator is created, new GstMemory can be created with
|
||||
|
||||
``` c
|
||||
GstMemory * gst_allocator_alloc (const GstAllocator * allocator,
|
||||
gsize size,
|
||||
GstAllocationParams *params);
|
||||
```
|
||||
|
||||
GstAllocationParams contain extra info such as flags, alignment, prefix and
|
||||
padding.
|
||||
|
||||
The GstMemory object is a refcounted object that must be freed with
|
||||
gst_memory_unref ().
|
||||
|
||||
The GstMemory keeps a ref to the allocator that allocated it. Inside the
|
||||
allocator are the most common GstMemory operations listed. Custom
|
||||
GstAllocator implementations must implement the various operations on
|
||||
the memory they allocate.
|
||||
|
||||
It is also possible to create a new GstMemory object that wraps existing
|
||||
memory with:
|
||||
|
||||
``` c
|
||||
GstMemory * gst_memory_new_wrapped (GstMemoryFlags flags,
|
||||
gpointer data, gsize maxsize,
|
||||
gsize offset, gsize size,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify);
|
||||
```
|
||||
|
||||
## Lifecycle
|
||||
|
||||
GstMemory extends from GstMiniObject and therefore uses its lifecycle
|
||||
management (See [miniobject](design/miniobject.md)).
|
||||
|
||||
## Data Access
|
||||
|
||||
Access to the memory region is always controlled with a map and unmap method
|
||||
call. This allows the implementation to monitor the access patterns or set up
|
||||
the required memory mappings when needed.
|
||||
|
||||
The access of the memory object is controlled with the locking mechanism on
|
||||
GstMiniObject (See [miniobject](design/miniobject.md)).
|
||||
|
||||
Mapping a memory region requires the caller to specify the access method: READ
|
||||
and/or WRITE. Mapping a memory region will first try to get a lock on the
|
||||
memory in the requested access mode. This means that the map operation can
|
||||
fail when WRITE access is requested on a non-writable memory object (it has
|
||||
an exclusive counter > 1, the memory is already locked in an incompatible
|
||||
access mode or the memory is marked readonly).
|
||||
|
||||
After the data has been accessed in the object, the unmap call must be
|
||||
performed, which will unlock the memory again.
|
||||
|
||||
It is allowed to recursively map multiple times with the same or narrower
|
||||
access modes. For each of the map calls, a corresponding unmap call needs to
|
||||
be made. WRITE-only memory cannot be mapped in READ mode and READ-only memory
|
||||
cannot be mapped in WRITE mode.
|
||||
|
||||
The memory pointer returned from the map call is guaranteed to remain valid in
|
||||
the requested mapping mode until the corresponding unmap call is performed on
|
||||
the pointer.
|
||||
|
||||
When multiple map operations are nested and return the same pointer, the pointer
|
||||
is valid until the last unmap call is done.
|
||||
|
||||
When the final reference on a memory object is dropped, all outstanding
|
||||
mappings should have been unmapped.
|
||||
|
||||
Resizing a GstMemory does not influence any current mappings in any way.
|
||||
|
||||
## Copy
|
||||
|
||||
A GstMemory copy can be made with the `gst_memory_copy()` call. Normally,
|
||||
allocators will implement a custom version of this function to make a copy of
|
||||
the same kind of memory as the original one.
|
||||
|
||||
This is what the fallback version of the copy function does, albeit slower
|
||||
than what a custom implementation could do.
|
||||
|
||||
The copy operation is only required to copy the visible range of the memory
|
||||
block.
|
||||
|
||||
## Share
|
||||
|
||||
A memory region can be shared between GstMemory object with the
|
||||
`gst_memory_share()` operation.
|
107
markdown/design/messages.md
Normal file
107
markdown/design/messages.md
Normal file
|
@ -0,0 +1,107 @@
|
|||
# Messages
|
||||
|
||||
Messages are refcounted lightweight objects to signal the application of
|
||||
pipeline events.
|
||||
|
||||
Messages are implemented as a subclass of GstMiniObject with a generic
|
||||
GstStructure as the content. This allows for writing custom messages
|
||||
without requiring an API change while allowing a wide range of different
|
||||
types of messages.
|
||||
|
||||
Messages are posted by objects in the pipeline and are passed to the
|
||||
application using the GstBus (See also [gstbus](design/gstbus.md)
|
||||
and [gstpipeline](design/gstpipeline.md)).
|
||||
|
||||
## Message types
|
||||
|
||||
**`GST_MESSAGE_EOS`**: Posted by sink elements. This message is posted to the
|
||||
application when all the sinks in a pipeline have posted an EOS message. When
|
||||
performing a flushing seek, the EOS state of the pipeline and sinks is reset.
|
||||
|
||||
**`GST_MESSAGE_ERROR`**: An element in the pipeline got into an error state.
|
||||
The message carries a GError and a debug string describing the error. This
|
||||
usually means that part of the pipeline is not streaming anymore.
|
||||
|
||||
**`GST_MESSAGE_WARNING`**: An element in the pipeline encountered a condition
|
||||
that made it produce a warning. This could be a recoverable decoding error or
|
||||
some other non fatal event. The pipeline continues streaming after a warning.
|
||||
|
||||
**`GST_MESSAGE_INFO`**: An element produced an informational message.
|
||||
|
||||
**`GST_MESSAGE_TAG`**: An element decoded metadata about the stream. The
|
||||
message carries a GstTagList with the tag information.
|
||||
|
||||
**`GST_MESSAGE_BUFFERING`**: An element is buffering data and that could
|
||||
potentially take some time. This message is typically emitted by elements that
|
||||
perform some sort of network buffering. While the pipeline is buffering it
|
||||
should remain in the PAUSED state. When the buffering is finished, it can
|
||||
resume PLAYING.
|
||||
|
||||
**`GST_MESSAGE_STATE_CHANGED`**: An element changed state in the pipeline.
|
||||
The message carries the old, new and pending state of the element.
|
||||
|
||||
**`GST_MESSAGE_STATE_DIRTY`**: An internal message used to instruct
|
||||
a pipeline hierarchy that a state recalculation must be performed because of an
|
||||
ASYNC state change completed. This message is not used anymore.
|
||||
|
||||
**`GST_MESSAGE_STEP_DONE`**: An element stepping frames has finished. This is
|
||||
currently not used.
|
||||
|
||||
**`GST_MESSAGE_CLOCK_PROVIDE`**: An element notifies its capability of
|
||||
providing a clock for the pipeline.
|
||||
|
||||
**`GST_MESSAGE_CLOCK_LOST`**: The current clock, as selected by the pipeline,
|
||||
became unusable. The pipeline will select a new clock on the next PLAYING state
|
||||
change.
|
||||
|
||||
**`GST_MESSAGE_NEW_CLOCK`**: A new clock was selected for the pipeline.
|
||||
|
||||
**`GST_MESSAGE_STRUCTURE_CHANGE`**: The pipeline changed its structure, This
|
||||
means elements were added or removed or pads were linked or unlinked. This
|
||||
message is not yet used.
|
||||
|
||||
**`GST_MESSAGE_STREAM_STATUS`**: Posted by an element when it
|
||||
starts/stops/pauses a streaming task. It contains information about the reason
|
||||
why the stream state changed along with the thread id. The application can use
|
||||
this information to detect failures in streaming threads and/or to adjust
|
||||
streaming thread priorities.
|
||||
|
||||
**`GST_MESSAGE_APPLICATION`**: The application posted a message. This message
|
||||
must be used when the application posts a message on the bus.
|
||||
|
||||
**`GST_MESSAGE_ELEMENT`**: Element-specific message. See the specific
|
||||
element's documentation
|
||||
|
||||
**`GST_MESSAGE_SEGMENT_START`**: An element started playback of a new
|
||||
segment. This message is not forwarded to applications but is used internally
|
||||
to schedule SEGMENT_DONE messages.
|
||||
|
||||
**`GST_MESSAGE_SEGMENT_DONE`**: An element or bin completed playback of
|
||||
a segment. This message is only posted on the bus if a SEGMENT seek is
|
||||
performed on a pipeline.
|
||||
|
||||
**`GST_MESSAGE_DURATION_CHANGED`**: An element posts this message when it has
|
||||
detected or updated the stream duration.
|
||||
|
||||
**`GST_MESSAGE_ASYNC_START`**: Posted by sinks when they start an
|
||||
asynchronous state change.
|
||||
|
||||
**`GST_MESSAGE_ASYNC_DONE`**: Posted by sinks when they receive the first
|
||||
data buffer and complete the asynchronous state change.
|
||||
|
||||
**`GST_MESSAGE_LATENCY`**: Posted by elements when the latency in a pipeline
|
||||
changed and a new global latency should be calculated by the pipeline or
|
||||
application.
|
||||
|
||||
**`GST_MESSAGE_REQUEST_STATE`**: Posted by elements when they want to change
|
||||
the state of the pipeline they are in. A typical use case would be an audio
|
||||
sink that requests the pipeline to pause in order to play a higher priority
|
||||
stream.
|
||||
|
||||
**`GST_MESSAGE_STEP_START`**: A Stepping operation has started.
|
||||
|
||||
**`GST_MESSAGE_QOS`**: A buffer was dropped or an element changed its
|
||||
processing strategy for Quality of Service reasons.
|
||||
|
||||
**`GST_MESSAGE_PROGRESS`**: A progress message was posted. Progress messages
|
||||
inform the application about the state of asynchronous operations.
|
410
markdown/design/meta.md
Normal file
410
markdown/design/meta.md
Normal file
|
@ -0,0 +1,410 @@
|
|||
# GstMeta
|
||||
|
||||
This document describes the design for arbitrary per-buffer metadata.
|
||||
|
||||
Buffer metadata typically describes the low level properties of the
|
||||
buffer content. These properties are commonly not negotiated with caps
|
||||
but they are negotiated in the bufferpools.
|
||||
|
||||
Some examples of metadata:
|
||||
|
||||
- interlacing information
|
||||
|
||||
- video alignment, cropping, panning information
|
||||
|
||||
- extra container information such as granulepos, …
|
||||
|
||||
- extra global buffer properties
|
||||
|
||||
## Requirements
|
||||
|
||||
- It must be fast
|
||||
|
||||
- allocation, free, low fragmentation
|
||||
|
||||
- access to the metadata fields, preferably not much slower than
|
||||
directly accessing a C structure field
|
||||
|
||||
- It must be extensible. Elements should be able to add new arbitrary
|
||||
metadata without requiring much effort. Also new metadata fields
|
||||
should not break API or ABI.
|
||||
|
||||
- It plays nice with subbuffers. When a subbuffer is created, the
|
||||
various buffer metadata should be copied/updated correctly.
|
||||
|
||||
- We should be able to negotiate metadata between elements
|
||||
|
||||
# Use cases
|
||||
|
||||
- **Video planes**: Video data is sometimes allocated in non-contiguous planes
|
||||
for the Y and the UV data. We need to be able to specify the data on a buffer
|
||||
using multiple pointers in memory. We also need to be able to specify the
|
||||
stride for these planes.
|
||||
|
||||
- **Extra buffer data**: Some elements might need to store extra data for
|
||||
a buffer. This is typically done when the resources are allocated from another
|
||||
subsystem such as OMX or X11.
|
||||
|
||||
- **Processing information**: Pan and crop information can be added to the
|
||||
buffer data when the downstream element can understand and use this metadata.
|
||||
An imagesink can, for example, use the pan and cropping information when
|
||||
blitting the image on the screen with little overhead.
|
||||
|
||||
## GstMeta
|
||||
|
||||
A GstMeta is a structure as follows:
|
||||
|
||||
``` c
|
||||
struct _GstMeta {
|
||||
GstMetaFlags flags;
|
||||
const GstMetaInfo *info; /* tag and info for the meta item */
|
||||
};
|
||||
```
|
||||
|
||||
The purpose of the this structure is to serve as a common header for all
|
||||
metadata information that we can attach to a buffer. Specific metadata,
|
||||
such as timing metadata, will have this structure as the first field.
|
||||
For example:
|
||||
|
||||
``` c
|
||||
struct _GstMetaTiming {
|
||||
GstMeta meta; /* common meta header */
|
||||
|
||||
GstClockTime dts; /* decoding timestamp */
|
||||
GstClockTime pts; /* presentation timestamp */
|
||||
GstClockTime duration; /* duration of the data */
|
||||
GstClockTime clock_rate; /* clock rate for the above values */
|
||||
};
|
||||
```
|
||||
|
||||
Or another example for the video memory regions that consists of both
|
||||
fields and methods.
|
||||
|
||||
``` c
|
||||
#define GST_VIDEO_MAX_PLANES 4
|
||||
|
||||
struct GstMetaVideo {
|
||||
GstMeta meta;
|
||||
|
||||
GstBuffer *buffer;
|
||||
|
||||
GstVideoFlags flags;
|
||||
GstVideoFormat format;
|
||||
guint id
|
||||
guint width;
|
||||
guint height;
|
||||
|
||||
guint n_planes;
|
||||
gsize offset[GST_VIDEO_MAX_PLANES]; /* offset in the buffer memory region of the
|
||||
* first pixel. */
|
||||
gint stride[GST_VIDEO_MAX_PLANES]; /* stride of the image lines. Can be negative when
|
||||
* the image is upside-down */
|
||||
|
||||
gpointer (*map) (GstMetaVideo *meta, guint plane, gpointer * data, gint *stride,
|
||||
GstMapFlags flags);
|
||||
gboolean (*unmap) (GstMetaVideo *meta, guint plane, gpointer data);
|
||||
};
|
||||
|
||||
gpointer gst_meta_video_map (GstMetaVideo *meta, guint plane, gpointer * data,
|
||||
gint *stride, GstMapflags flags);
|
||||
gboolean gst_meta_video_unmap (GstMetaVideo *meta, guint plane, gpointer data);
|
||||
```
|
||||
|
||||
GstMeta derived structures define the API of the metadata. The API can
|
||||
consist of fields and/or methods. It is possible to have different
|
||||
implementations for the same GstMeta structure.
|
||||
|
||||
The implementation of the GstMeta API would typically add more fields to
|
||||
the public structure that allow it to implement the API.
|
||||
|
||||
GstMetaInfo will point to more information about the metadata and looks
|
||||
like this:
|
||||
|
||||
``` c
|
||||
struct _GstMetaInfo {
|
||||
GType api; /* api type */
|
||||
GType type; /* implementation type */
|
||||
gsize size; /* size of the structure */
|
||||
|
||||
GstMetaInitFunction init_func;
|
||||
GstMetaFreeFunction free_func;
|
||||
GstMetaTransformFunction transform_func;
|
||||
};
|
||||
```
|
||||
|
||||
api will contain a GType of the metadata API. A repository of registered
|
||||
MetaInfo will be maintained by the core. We will register some common
|
||||
metadata structures in core and some media specific info for
|
||||
audio/video/text in -base. Plugins can register additional custom
|
||||
metadata.
|
||||
|
||||
For each implementation of api, there will thus be a unique GstMetaInfo.
|
||||
In the case of metadata with a well defined API, the implementation
|
||||
specific init function will setup the methods in the metadata structure.
|
||||
A unique GType will be made for each implementation and stored in the
|
||||
type field.
|
||||
|
||||
Along with the metadata description we will have functions to
|
||||
initialize/free (and/or refcount) a specific GstMeta instance. We also
|
||||
have the possibility to add a custom transform function that can be used
|
||||
to modify the metadata when a transformation happens.
|
||||
|
||||
There are no explicit methods to serialize and deserialize the metadata.
|
||||
Since each type has a GType, we can reuse the GValue transform functions
|
||||
for this.
|
||||
|
||||
The purpose of the separate MetaInfo is to not have to carry the
|
||||
free/init functions in each buffer instance but to define them globally.
|
||||
We still want quick access to the info so we need to make the buffer
|
||||
metadata point to the info.
|
||||
|
||||
Technically we could also specify the field and types in the MetaInfo
|
||||
and provide a generic API to retrieve the metadata fields without the
|
||||
need for a header file. We will not do this yet.
|
||||
|
||||
Allocation of the GstBuffer structure will result in the allocation of a
|
||||
memory region of a customizable size (512 bytes). Only the first sizeof
|
||||
(GstBuffer) bytes of this region will initially be used. The remaining
|
||||
bytes will be part of the free metadata region of the buffer. Different
|
||||
implementations are possible and are invisible in the API or ABI.
|
||||
|
||||
The complete buffer with metadata could, for example, look as follows:
|
||||
|
||||
```
|
||||
+-------------------------------------+
|
||||
GstMiniObject | GType (GstBuffer) |
|
||||
| refcount, flags, copy/disp/free |
|
||||
+-------------------------------------+
|
||||
GstBuffer | pool,pts,dts,duration,offsets |
|
||||
| <private data> |
|
||||
+.....................................+
|
||||
| next ---+
|
||||
+- | info ------> GstMetaInfo
|
||||
GstMetaTiming | | | |
|
||||
| | dts | |
|
||||
| | pts | |
|
||||
| | duration | |
|
||||
+- | clock_rate | |
|
||||
+ . . . . . . . . . . . . . . . . . . + |
|
||||
| next <--+
|
||||
GstMetaVideo +- +- | info ------> GstMetaInfo
|
||||
| | | | |
|
||||
| | | flags | |
|
||||
| | | n_planes | |
|
||||
| | | planes[] | |
|
||||
| | | map | |
|
||||
| | | unmap | |
|
||||
+- | | | |
|
||||
| | private fields | |
|
||||
GstMetaVideoImpl | | ... | |
|
||||
| | ... | |
|
||||
+- | | |
|
||||
+ . . . . . . . . . . . . . . . . . . + .
|
||||
. .
|
||||
```
|
||||
|
||||
## API examples
|
||||
|
||||
Buffers are created using the normal gst\_buffer\_new functions. The
|
||||
standard fields are initialized as usual. A memory area that is bigger
|
||||
than the structure size is allocated for the buffer metadata.
|
||||
|
||||
``` c
|
||||
gst_buffer_new ();
|
||||
```
|
||||
|
||||
After creating a buffer, the application can set caps and add metadata
|
||||
information.
|
||||
|
||||
To add or retrieve metadata, a handle to a GstMetaInfo structure needs
|
||||
to be obtained. This defines the implementation and API of the metadata.
|
||||
Usually, a handle to this info structure can be obtained by calling a
|
||||
public `_get\_info()` method from a shared library (for shared metadata).
|
||||
|
||||
The following defines can usually be found in the shared .h file.
|
||||
|
||||
``` c
|
||||
GstMetaInfo * gst_meta_timing_get_info();
|
||||
#define GST_META_TIMING_INFO (gst_meta_timing_get_info())
|
||||
```
|
||||
|
||||
Adding metadata to a buffer can be done with the
|
||||
`gst_buffer_add_meta()` call. This function will create new metadata
|
||||
based on the implementation specified by the GstMetaInfo. It is also
|
||||
possible to pass a generic pointer to the `add_meta()` function that can
|
||||
contain parameters to initialize the new metadata fields.
|
||||
|
||||
Retrieving the metadata on a buffer can be done with the
|
||||
`gst_buffer_meta_get()` method. This function retrieves an existing
|
||||
metadata conforming to the API specified in the given info. When no such
|
||||
metadata exists, the function will return NULL.
|
||||
|
||||
``` c
|
||||
GstMetaTiming *timing;
|
||||
|
||||
timing = gst_buffer_get_meta (buffer, GST_META_TIMING_INFO);
|
||||
```
|
||||
|
||||
Once a reference to the info has been obtained, the associated metadata
|
||||
can be added or modified on a buffer.
|
||||
|
||||
``` c
|
||||
timing->timestamp = 0;
|
||||
timing->duration = 20 * GST_MSECOND;
|
||||
```
|
||||
|
||||
Other convenience macros can be made to simplify the above code:
|
||||
|
||||
``` c
|
||||
#define gst_buffer_get_meta_timing(b) \
|
||||
((GstMetaTiming *) gst_buffer_get_meta ((b), GST_META_TIMING_INFO)
|
||||
```
|
||||
|
||||
This makes the code look like this:
|
||||
|
||||
``` c
|
||||
GstMetaTiming *timing;
|
||||
|
||||
timing = gst_buffer_get_meta_timing (buffer);
|
||||
timing->timestamp = 0;
|
||||
timing->duration = 20 * GST_MSECOND;
|
||||
```
|
||||
|
||||
To iterate the different metainfo structures, one can use the
|
||||
`gst_buffer_meta_get_next()` methods.
|
||||
|
||||
``` c
|
||||
GstMeta *current = NULL;
|
||||
|
||||
/* passing NULL gives the first entry */
|
||||
current = gst_buffer_meta_get_next (buffer, current);
|
||||
|
||||
/* passing a GstMeta returns the next */
|
||||
current = gst_buffer_meta_get_next (buffer, current);
|
||||
```
|
||||
|
||||
## Memory management
|
||||
|
||||
### allocation
|
||||
|
||||
We initially allocate a reasonable sized GstBuffer structure (say 512 bytes).
|
||||
|
||||
Since the complete buffer structure, including a large area for metadata, is
|
||||
allocated in one go, we can reduce the number of memory allocations while still
|
||||
providing dynamic metadata.
|
||||
|
||||
When adding metadata, we need to call the init function of the associated
|
||||
metadata info structure. Since adding the metadata requires the caller to pass
|
||||
a handle to the info, this operation does not require table lookups.
|
||||
|
||||
Per-metadata memory initialisation is needed because not all metadata is
|
||||
initialized in the same way. We need to, for example, set the timestamps to
|
||||
NONE in the MetaTiming structures.
|
||||
|
||||
The init/free functions can also be used to implement refcounting for a metadata
|
||||
structure. This can be useful when a structure is shared between buffers.
|
||||
|
||||
When the free_size of the GstBuffer is exhausted, we will allocate new memory
|
||||
for each newly added Meta and use the next pointers to point to this. It
|
||||
is expected that this does not occur often and we might be able to optimize
|
||||
this transparently in the future.
|
||||
|
||||
### free
|
||||
|
||||
When a GstBuffer is freed, we potentially might have to call a custom free
|
||||
function on the metadata info. In the case of the Memory metadata, we need to
|
||||
call the associated free function to free the memory.
|
||||
|
||||
When freeing a GstBuffer, the custom buffer free function will iterate all of
|
||||
the metadata in the buffer and call the associated free functions in the
|
||||
MetaInfo associated with the entries. Usually, this function will be NULL.
|
||||
|
||||
## Serialization
|
||||
|
||||
When buffer should be sent over the wire or be serialized in GDP, we
|
||||
need a way to perform custom serialization and deserialization on the
|
||||
metadata.
|
||||
|
||||
for this we can use the GValue transform functions.
|
||||
|
||||
## Transformations
|
||||
|
||||
After certain transformations, the metadata on a buffer might not be
|
||||
relevant anymore.
|
||||
|
||||
Consider, for example, metadata that lists certain regions of interest
|
||||
on the video data. If the video is scaled or rotated, the coordinates
|
||||
might not make sense anymore. A transform element should be able to
|
||||
adjust or remove the associated metadata when it becomes invalid.
|
||||
|
||||
We can make the transform element aware of the metadata so that it can
|
||||
adjust or remove in an intelligent way. Since we allow arbitrary
|
||||
metadata, we can’t do this for all metadata and thus we need some other
|
||||
way.
|
||||
|
||||
One proposition is to tag the metadata type with keywords that specify
|
||||
what it functionally refers too. We could, for example, tag the metadata
|
||||
for the regions of interest with a tag that notes that the metadata
|
||||
refers to absolute pixel positions. A transform could then know that the
|
||||
metadata is not valid anymore when the position of the pixels changed
|
||||
(due to rotation, flipping, scaling and so on).
|
||||
|
||||
## Subbuffers
|
||||
|
||||
Subbuffers are implemented with a generic copy. Parameters to the copy
|
||||
are the offset and size. This allows each metadata structure to
|
||||
implement the actions needed to update the metadata of the subbuffer.
|
||||
|
||||
It might not make sense for some metadata to work with subbuffers. For
|
||||
example when we take a subbuffer of a buffer with a video frame, the
|
||||
GstMetaVideo simply becomes invalid and is removed from the new
|
||||
subbuffer.
|
||||
|
||||
## Relationship with GstCaps
|
||||
|
||||
The difference between GstCaps, used in negotiation, and the metadata is
|
||||
not clearly defined.
|
||||
|
||||
We would like to think of the GstCaps containing the information needed
|
||||
to functionally negotiate the format between two elements. The Metadata
|
||||
should then only contain variables that can change between each buffer.
|
||||
|
||||
For example, for video we would have width/height/framerate in the caps
|
||||
but then have the more technical details, such as stride, data pointers,
|
||||
pan/crop/zoom etc in the metadata.
|
||||
|
||||
A scheme like this would still allow us to functionally specify the
|
||||
desired video resolution while the implementation details would be
|
||||
inside the metadata.
|
||||
|
||||
## Relationship with GstMiniObject qdata
|
||||
|
||||
qdata on a miniobject is element private and is not visible to other
|
||||
element. Therefore qdata never contains essential information that
|
||||
describes the buffer content.
|
||||
|
||||
## Compatibility
|
||||
|
||||
We need to make sure that elements exchange metadata that they both
|
||||
understand, This is particularly important when the metadata describes
|
||||
the data layout in memory (such as strides).
|
||||
|
||||
The ALLOCATION query is used to let upstream know what metadata we can
|
||||
suport.
|
||||
|
||||
It is also possible to have a bufferpool add certain metadata to the
|
||||
buffers from the pool. This feature is activated by enabling a buffer
|
||||
option when configuring the pool.
|
||||
|
||||
## Notes
|
||||
|
||||
Some structures that we need to be able to add to buffers.
|
||||
|
||||
- Clean Aperture
|
||||
- Arbitrary Matrix Transform
|
||||
- Aspect ratio
|
||||
- Pan/crop/zoom
|
||||
- Video strides
|
||||
|
||||
Some of these overlap, we need to find a minimal set of metadata
|
||||
structures that allows us to define all use cases.
|
199
markdown/design/miniobject.md
Normal file
199
markdown/design/miniobject.md
Normal file
|
@ -0,0 +1,199 @@
|
|||
# GstMiniObject
|
||||
|
||||
This document describes the design of the miniobject base class.
|
||||
|
||||
The miniobject abstract base class is used to construct lightweight
|
||||
refcounted and boxed types that are frequently created and destroyed.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Be lightweight
|
||||
- Refcounted
|
||||
- I must be possible to control access to the object, ie. when the
|
||||
object is readable and writable.
|
||||
- Subclasses must be able to use their own allocator for the memory.
|
||||
|
||||
## Usage
|
||||
|
||||
Users of the GstMiniObject infrastructure will need to define a
|
||||
structure that includes the GstMiniObject structure as the first field.
|
||||
|
||||
``` c
|
||||
struct {
|
||||
GstMiniObject mini_object;
|
||||
|
||||
/* my fields */
|
||||
...
|
||||
} MyObject
|
||||
```
|
||||
|
||||
The subclass should then implement a constructor method where it
|
||||
allocates the memory for its structure and initializes the miniobject
|
||||
structure with `gst\_mini\_object\_init()`. Copy and Free functions are
|
||||
provided to the `gst\_mini\_object\_init()` function.
|
||||
|
||||
``` c
|
||||
MyObject *
|
||||
my_object_new()
|
||||
{
|
||||
MyObject *res = g_slice_new (MyObject);
|
||||
|
||||
gst_mini_object_init (GST_MINI_OBJECT_CAST (res), 0,
|
||||
MY_TYPE_OBJECT,
|
||||
(GstMiniObjectCopyFunction) _my_object_copy,
|
||||
(GstMiniObjectDisposeFunction) NULL,
|
||||
(GstMiniObjectFreeFunction) _my_object_free);
|
||||
|
||||
/* other init */
|
||||
.....
|
||||
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
The Free function is responsible for freeing the allocated memory for
|
||||
the structure.
|
||||
|
||||
``` c
|
||||
static void
|
||||
_my_object_free (MyObject *obj)
|
||||
{
|
||||
/* other cleanup */
|
||||
...
|
||||
|
||||
g_slice_free (MyObject, obj);
|
||||
}
|
||||
```
|
||||
|
||||
## Lifecycle
|
||||
|
||||
GstMiniObject is refcounted. When a GstMiniObject is first created, it
|
||||
has a refcount of 1.
|
||||
|
||||
Each variable holding a reference to a GstMiniObject is responsible for
|
||||
updating the refcount. This includes incrementing the refcount with
|
||||
`gst\_mini\_object\_ref()` when a reference is kept to a miniobject or
|
||||
`gst\_mini\_object\_unref()` when a reference is released.
|
||||
|
||||
When the refcount reaches 0, and thus no objects hold a reference to the
|
||||
miniobject anymore, we can free the miniobject.
|
||||
|
||||
When freeing the miniobject, first the GstMiniObjectDisposeFunction is
|
||||
called. This function is allowed to revive the object again by
|
||||
incrementing the refcount, in which case it should return FALSE from the
|
||||
dispose function. The dispose function is used by GstBuffer to revive
|
||||
the buffer back into the GstBufferPool when needed.
|
||||
|
||||
When the dispose function returns TRUE, the GstMiniObjectFreeFunction
|
||||
will be called and the miniobject will be freed.
|
||||
|
||||
## Copy
|
||||
|
||||
A miniobject can be copied with `gst\_mini\_object\_copy()`. This function
|
||||
will call the custom copy function that was provided when registering
|
||||
the new GstMiniObject subclass.
|
||||
|
||||
The copy function should try to preserve as much info from the original
|
||||
object as possible.
|
||||
|
||||
The new copy should be writable.
|
||||
|
||||
## Access management
|
||||
|
||||
GstMiniObject can be shared between multiple threads. It is important
|
||||
that when a thread writes to a GstMiniObject that the other threads
|
||||
don’t not see the changes.
|
||||
|
||||
To avoid exposing changes from one thread to another thread, the
|
||||
miniobjects are managed in a Copy-On-Write way. A copy is only made when
|
||||
it is known that the object is shared between multiple objects or
|
||||
threads.
|
||||
|
||||
There are 2 methods implemented for controlling access to the
|
||||
miniobject.
|
||||
|
||||
- A first method relies on the refcount of the object to control
|
||||
writability. Objects using this method have the LOCKABLE flag unset.
|
||||
|
||||
- A second method relies on a separate counter for controlling the
|
||||
access to the object. Objects using this method have the LOCKABLE
|
||||
flag set.
|
||||
You can check if an object is writable with gst_mini_object_is_writable() and
|
||||
you can make any miniobject writable with gst_mini_object_make_writable().
|
||||
This will create a writable copy when the object was not writable.
|
||||
|
||||
### non-LOCKABLE GstMiniObjects
|
||||
|
||||
These GstMiniObjects have the LOCKABLE flag unset. They use the refcount value
|
||||
to control writability of the object.
|
||||
|
||||
When the refcount of the miniobject is > 1, the objects it referenced by at
|
||||
least 2 objects and is thus considered unwritable. A copy must be made before a
|
||||
modification to the object can be done.
|
||||
|
||||
Using the refcount to control writability is problematic for many language
|
||||
bindings that can keep additional references to the objects. This method is
|
||||
mainly for historical reasons until all users of the miniobjects are
|
||||
converted to use the LOCAKBLE flag.
|
||||
|
||||
### LOCKABLE GstMiniObjects
|
||||
|
||||
These GstMiniObjects have the LOCKABLE flag set. They use a separate counter
|
||||
for controlling writability and access to the object.
|
||||
|
||||
It consists of 2 components:
|
||||
|
||||
#### exclusive counter
|
||||
|
||||
Each object that wants to keep a reference to a GstMiniObject and doesn't want to
|
||||
see the changes from other owners of the same GstMiniObject needs to lock the
|
||||
GstMiniObject in EXCLUSIVE mode, which will increase the exclusive counter.
|
||||
|
||||
The exclusive counter counts the amount of objects that share this
|
||||
GstMiniObject. The counter is initially 0, meaning that the object is not shared with
|
||||
any object.
|
||||
|
||||
When a reference to a GstMiniObject release, both the ref count and the
|
||||
exclusive counter will be decreased with `gst_mini_object_unref()` and
|
||||
`gst_mini_object_unlock()` respectively.
|
||||
|
||||
#### locking
|
||||
|
||||
All read and write access must be performed between a `gst_mini_object_lock()`
|
||||
and `gst_mini_object_unlock()` pair with the requested access method.
|
||||
|
||||
A `gst_mini_object_lock()` can fail when a `WRITE` lock is requested and the
|
||||
exclusive counter is > 1. Indeed a GstMiniObject object with an exclusive
|
||||
counter > 1 is locked EXCLUSIVELY by at least 2 objects and is therefore not
|
||||
writable.
|
||||
|
||||
Once the GstMiniObject is locked with a certain access mode, it can be
|
||||
recursively locked with the same or narrower access mode. For example, first
|
||||
locking the GstMiniObject in READWRITE mode allows you to recusively lock the
|
||||
GstMiniObject in READWRITE, READ and WRITE mode. Memory locked in READ mode
|
||||
cannot be locked recursively in WRITE or READWRITE mode.
|
||||
|
||||
Note that multiple threads can READ lock the GstMiniObject concurrently but
|
||||
cannot lock the object in WRITE mode because the exclusive counter must be > 1.
|
||||
|
||||
All calls to `gst_mini_object_lock()` need to be paired with one
|
||||
`gst_mini_object_unlock()` call with the same access mode. When the last
|
||||
refcount of the object is removed, there should be no more outstanding locks.
|
||||
|
||||
Note that a shared counter of both 0 and 1 leaves the GstMiniObject writable.
|
||||
The reason is to make it easy to create and pass ownership of the GstMiniObject
|
||||
to another object while keeping it writable. When the GstMiniObject is created
|
||||
with a shared count of 0, it is writable. When the GstMiniObject is then added
|
||||
to another object, the shared count is incremented to 1 and the GstMiniObject
|
||||
remains writable. The 0 share counter has a similar purpose as the floating
|
||||
reference in GObject.
|
||||
|
||||
## Weak references
|
||||
|
||||
GstMiniObject has support for weak references. A callback will be called
|
||||
when the object is freed for all registered weak references.
|
||||
|
||||
## QData
|
||||
|
||||
Extra data can be associated with a GstMiniObject by using the QData
|
||||
API.
|
257
markdown/design/missing-plugins.md
Normal file
257
markdown/design/missing-plugins.md
Normal file
|
@ -0,0 +1,257 @@
|
|||
# What to do when a plugin is missing
|
||||
|
||||
The mechanism and API described in this document requires GStreamer core
|
||||
and gst-plugins-base versions \>= 0.10.12. Further information on some
|
||||
aspects of this document can be found in the libgstbaseutils API
|
||||
reference.
|
||||
|
||||
We only discuss playback pipelines for now.
|
||||
|
||||
A three step process:
|
||||
|
||||
1\) GStreamer level
|
||||
|
||||
Elements will use a "missing-plugin" element message to report
|
||||
missing plugins, with the following fields set:
|
||||
|
||||
* **`type`**: (string) { "urisource", "urisink", "decoder", "encoder",
|
||||
"element" } (we do not distinguish between demuxer/decoders/parsers etc.)
|
||||
|
||||
* **`detail`**: (string) or (caps) depending on the type { ANY } ex: "mms,
|
||||
"mmsh", "audio/x-mp3,rate=48000,…"
|
||||
|
||||
* **`name`**: (string) { ANY } ex: "MMS protocol handler",..
|
||||
|
||||
## missing uri handler
|
||||
|
||||
ex. mms://foo.bar/file.asf
|
||||
|
||||
When no protocol handler is installed for mms://, the application will not be
|
||||
able to instantiate an element for that uri (gst_element_make_from_uri()
|
||||
returns NULL).
|
||||
|
||||
Playbin will post a "missing-plugin" element message with the type set to
|
||||
"urisource", detail set to "mms". Optionally the friendly name can be filled
|
||||
in as well.
|
||||
|
||||
## missing typefind function
|
||||
|
||||
We don't recognize the type of the file, this should normally not happen
|
||||
because all the typefinders are in the basic GStreamer installation.
|
||||
There is not much useful information we can give about how to resolve this
|
||||
issue. It is possible to use the first N bytes of the data to determine the
|
||||
type (and needed plugin) on the server. We don't explore this option in this
|
||||
document yet, but the proposal is flexible enough to accommodate this in the
|
||||
future should the need arise.
|
||||
|
||||
## missing demuxer
|
||||
|
||||
Typically after running typefind on the data we determine the type of the
|
||||
file. If there is no plugin found for the type, a "missing-plugin" element
|
||||
message is posted by decodebin with the following fields: Type set to
|
||||
"decoder", detail set to the caps for witch no plugin was found. Optionally
|
||||
the friendly name can be filled in as well.
|
||||
|
||||
## missing decoder
|
||||
|
||||
The demuxer will dynamically create new pads with specific caps while it
|
||||
figures out the contents of the container format. Decodebin tries to find the
|
||||
decoders for these formats in the registry. If there is no decoder found, a
|
||||
"missing-plugin" element message is posted by decodebin with the following
|
||||
fields: Type set to "decoder", detail set to the caps for which no plugin
|
||||
was found. Optionally the friendly name can be filled in as well. There is
|
||||
no distinction made between the missing demuxer and decoder at the
|
||||
application level.
|
||||
|
||||
## missing element
|
||||
|
||||
Decodebin and playbin will create a set of helper elements when they set up
|
||||
their decoding pipeline. These elements are typically colorspace, sample rate,
|
||||
audio sinks,... Their presence on the system is required for the functionality
|
||||
of decodebin. It is typically a package dependency error if they are not
|
||||
present but in case of a corrupted system the following "missing-plugin"
|
||||
element message will be emitted: type set to "element", detail set to the
|
||||
element factory name and the friendly name optionally set to a description
|
||||
of the element's functionality in the decoding pipeline.
|
||||
|
||||
Except for reporting the missing plugins, no further policy is enforced at the
|
||||
GStreamer level. It is up to the application to decide whether a missing
|
||||
plugin constitutes a problem or not.
|
||||
|
||||
# Application level
|
||||
|
||||
The application's job is to listen for the "missing-plugin" element messages
|
||||
and to decide on a policy to handle them. Following cases exist:
|
||||
|
||||
## partially missing plugins
|
||||
|
||||
The application will be able to complete a state change to PAUSED but there
|
||||
will be a "missing-plugin" element message on the GstBus.
|
||||
|
||||
This means that it will be possible to play back part of the media file but not
|
||||
all of it.
|
||||
|
||||
For example: suppose we have an .avi file with mp3 audio and divx video. If we
|
||||
have the mp3 audio decoder but not the divx video decoder, it will be possible
|
||||
to play only the audio part but not the video part. For an audio playback
|
||||
application, this is not a problem but a video player might want to decide on:
|
||||
|
||||
- require the use to install the additionally required plugins.
|
||||
- inform the user that only the audio will be played back
|
||||
- ask the user if it should download the additional codec or only play
|
||||
the audio part.
|
||||
- …
|
||||
|
||||
## completely unplayable stream
|
||||
|
||||
The application will receive an ERROR message from GStreamer informing it that
|
||||
playback stopped (before it could reach PAUSED). This happens because none of
|
||||
the streams is connected to a decoder. The error code and domain should be one
|
||||
of the following in this case:
|
||||
|
||||
- `GST_CORE_ERROR_MISSING_PLUGIN` (domain: GST_CORE_ERROR)
|
||||
- `GST_STREAM_ERROR_CODEC_NOT_FOUND` (domain: GST_STREAM_ERROR)
|
||||
|
||||
The application can then see that there are a set of "missing-plugin" element
|
||||
messages on the GstBus and can decide to trigger the download procedure. It
|
||||
does that as described in the following section.
|
||||
|
||||
"missing-plugin" element messages can be identified using the function
|
||||
gst_is_missing_plugin_message().
|
||||
|
||||
# Plugin download stage
|
||||
|
||||
At this point the application has
|
||||
- collected one or more "missing-plugin" element messages
|
||||
- made a decision that additional plugins should be installed
|
||||
|
||||
It will call a GStreamer utility function to convert each "missing-plugin"
|
||||
message into an identifier string describing the missing capability. This is
|
||||
done using the function `gst_missing_plugin_message_get_installer_detail()`.
|
||||
|
||||
The application will then pass these strings to `gst_install_plugins_async()`
|
||||
or `gst_install_plugins_sync()` to initiate the download. See the API
|
||||
documentation there (`libgstbaseutils`, part of `gst-plugins-base`) for more
|
||||
details.
|
||||
|
||||
When new plugins have been installed, the application will have to initiate
|
||||
a re-scan of the GStreamer plugin registry using gst_update_registry().
|
||||
|
||||
# Format of the (UTF-8) string ID passed to the external installer system
|
||||
|
||||
The string is made up of several fields, separated by '|' characters.
|
||||
The fields are:
|
||||
|
||||
- plugin system identifier, ie. "gstreamer" This identifier determines
|
||||
the format of the rest of the detail string. Automatic plugin
|
||||
installers should not process detail strings with unknown
|
||||
identifiers. This allows other plugin-based libraries to use the
|
||||
same mechanism for their automatic plugin installation needs, or for
|
||||
the format to be changed should it turn out to be insufficient.
|
||||
|
||||
- plugin system version, e.g. "1.0" This is required so that when
|
||||
there is a GStreamer-2.0 or GStreamer-3.0 at some point in future,
|
||||
the different major versions can still co-exist and use the same
|
||||
plugin install mechanism in the same way.
|
||||
|
||||
- application identifier, e.g. "totem" This may also be in the form of
|
||||
"pid/12345" if the program name can’t be obtained for some reason.
|
||||
|
||||
- human-readable localised description of the required component, e.g.
|
||||
"Vorbis audio decoder"
|
||||
|
||||
- identifier string for the required component, e.g.
|
||||
|
||||
- urisource-(PROTOCOL_REQUIRED) e.g. `urisource-http` or `urisource-mms`
|
||||
|
||||
- element-(ELEMENT_REQUIRED), e.g. `element-videoconvert`
|
||||
|
||||
- decoder-(CAPS_REQUIRED) e.g. `decoder-audio/x-vorbis` or
|
||||
`decoder-application/ogg` or `decoder-audio/mpeg, mpegversion=(int)4` or
|
||||
`decoder-video/mpeg, systemstream=(boolean)true, mpegversion=(int)2`
|
||||
|
||||
- encoder-(CAPS_REQUIRED) e.g. `encoder-audio/x-vorbis`
|
||||
|
||||
- optional further fields not yet specified
|
||||
|
||||
* An entire ID string might then look like this, for example:
|
||||
`gstreamer|0.10|totem|Vorbis audio decoder|decoder-audio/x-vorbis`
|
||||
|
||||
* Plugin installers parsing this ID string should expect further fields also
|
||||
separated by '|' symbols and either ignore them, warn the user, or error
|
||||
out when encountering them.
|
||||
|
||||
* The human-readable description string is provided by the libgstbaseutils
|
||||
library that can be found in gst-plugins-base versions >= 0.10.12 and can
|
||||
also be used by demuxers to find out the codec names for taglists from given
|
||||
caps in a unified and consistent way.
|
||||
|
||||
* Applications can create these detail strings using the function
|
||||
`gst_missing_plugin_message_get_installer_detail()` on a given missing-plugin
|
||||
message.
|
||||
|
||||
# Using missing-plugin messages for error reporting:
|
||||
|
||||
Missing-plugin messages are also useful for error reporting purposes, either in
|
||||
the case where the application does not support libgimme-codec, or the external
|
||||
installer is not available or not able to install the required plugins.
|
||||
|
||||
When creating error messages, applications may use the function
|
||||
gst_missing_plugin_message_get_description() to obtain a possibly translated
|
||||
description from each missing-plugin message (e.g. "Matroska demuxer" or
|
||||
"Theora video depayloader"). This can be used to report to the user exactly
|
||||
what it is that is missing.
|
||||
|
||||
# Notes for packagers
|
||||
|
||||
An easy way to introspect plugin .so files is:
|
||||
|
||||
```
|
||||
$ gst-inspect --print-plugin-auto-install-info /path/to/libgstfoo.so
|
||||
```
|
||||
|
||||
The output will be something like:
|
||||
|
||||
```
|
||||
decoder-audio/x-vorbis
|
||||
element-vorbisdec
|
||||
element-vorbisenc
|
||||
element-vorbisparse
|
||||
element-vorbistag
|
||||
encoder-audio/x-vorbis
|
||||
```
|
||||
|
||||
BUT could also be like this (from the faad element in this case):
|
||||
|
||||
```
|
||||
decoder-audio/mpeg, mpegversion=(int){ 2, 4 }
|
||||
```
|
||||
|
||||
NOTE that this does not exactly match the caps string that the installer
|
||||
will get from the application. The application will always ever ask for
|
||||
one of
|
||||
|
||||
```
|
||||
decoder-audio/mpeg, mpegversion=(int)2
|
||||
decoder-audio/mpeg, mpegversion=(int)4
|
||||
```
|
||||
|
||||
When introspecting, keep in mind that there are GStreamer plugins
|
||||
that in turn load external plugins. Examples of these are pitfdll,
|
||||
ladspa, or the GStreamer libvisual plugin. Those plugins will only
|
||||
announce elements for the currently installed external plugins at
|
||||
the time of introspection\! With the exception of pitfdll, this is
|
||||
not really relevant to the playback case, but may become an issue in
|
||||
future when applications like buzztard, jokosher or pitivi start
|
||||
requestion elements by name, for example ladspa effect elements or
|
||||
so.
|
||||
|
||||
This case could be handled if those wrapper plugins would also provide a
|
||||
`gst-install-xxx-plugins-helper`, where xxx={ladspa|visual|...}. Thus if the
|
||||
distro specific `gst-install-plugins-helper` can't resolve a request for e.g.
|
||||
`element-bml-sonicverb` it can forward the request to
|
||||
`gst-install-bml-plugins-helper` (bml is the buzz machine loader).
|
||||
|
||||
# Further references:
|
||||
|
||||
<http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-base-libs/html/gstreamer-base-utils.html>
|
333
markdown/design/negotiation.md
Normal file
333
markdown/design/negotiation.md
Normal file
|
@ -0,0 +1,333 @@
|
|||
# Negotiation
|
||||
|
||||
Capabilities negotiation is the process of deciding on an adequate
|
||||
format for dataflow within a GStreamer pipeline. Ideally, negotiation
|
||||
(also known as "capsnego") transfers information from those parts of the
|
||||
pipeline that have information to those parts of the pipeline that are
|
||||
flexible, constrained by those parts of the pipeline that are not
|
||||
flexible.
|
||||
|
||||
## Basic rules
|
||||
|
||||
These simple rules must be followed:
|
||||
|
||||
1) downstream suggests formats
|
||||
2) upstream decides on format
|
||||
|
||||
There are 4 queries/events used in caps negotiation:
|
||||
|
||||
1) `GST_QUERY_CAPS`: get possible formats
|
||||
2) `GST_QUERY_ACCEPT_CAPS`: check if format is possible
|
||||
3) `GST_EVENT_CAPS`: configure format (downstream)
|
||||
4) `GST_EVENT_RECONFIGURE`: inform upstream of possibly new caps
|
||||
|
||||
# Queries
|
||||
|
||||
A pad can ask the peer pad for its supported GstCaps. It does this with
|
||||
the CAPS query. The list of supported caps can be used to choose an
|
||||
appropriate GstCaps for the data transfer. The CAPS query works
|
||||
recursively, elements should take their peers into consideration when
|
||||
constructing the possible caps. Because the result caps can be very
|
||||
large, the filter can be used to restrict the caps. Only the caps that
|
||||
match the filter will be returned as the result caps. The order of the
|
||||
filter caps gives the order of preference of the caller and should be
|
||||
taken into account for the returned caps.
|
||||
|
||||
* **`filter`** (in) GST_TYPE_CAPS (default NULL): - a GstCaps to filter the results against
|
||||
* **`caps`** (out) GST_TYPE_CAPS (default NULL): - the result caps
|
||||
|
||||
A pad can ask the peer pad if it supports a given caps. It does this
|
||||
with the ACCEPT\_CAPS query. The caps must be fixed. The ACCEPT\_CAPS
|
||||
query is not required to work recursively, it can simply return TRUE if
|
||||
a subsequent CAPS event with those caps would return success.
|
||||
|
||||
* **`caps`** (in) GST_TYPE_CAPS: - a GstCaps to check, must be fixed
|
||||
* **`result`** (out) G_TYPE_BOOLEAN (default FALSE): - TRUE if the caps are accepted
|
||||
|
||||
## Events
|
||||
|
||||
When a media format is negotiated, peer elements are notified of the
|
||||
GstCaps with the CAPS event. The caps must be fixed.
|
||||
|
||||
* **`caps`** GST_TYPE_CAPS: - the negotiated GstCaps, must be fixed
|
||||
|
||||
## Operation
|
||||
|
||||
GStreamer’s two scheduling modes, push mode and pull mode, lend
|
||||
themselves to different mechanisms to achieve this goal. As it is more
|
||||
common we describe push mode negotiation first.
|
||||
|
||||
## Push-mode negotiation
|
||||
|
||||
Push-mode negotiation happens when elements want to push buffers and
|
||||
need to decide on the format. This is called downstream negotiation
|
||||
because the upstream element decides the format for the downstream
|
||||
element. This is the most common case.
|
||||
|
||||
Negotiation can also happen when a downstream element wants to receive
|
||||
another data format from an upstream element. This is called upstream
|
||||
negotiation.
|
||||
|
||||
The basics of negotiation are as follows:
|
||||
|
||||
- GstCaps (see [caps](design/caps.md)) are refcounted before they are pushed as
|
||||
an event to describe the contents of the following buffer.
|
||||
|
||||
- An element should reconfigure itself to the new format received as a
|
||||
CAPS event before processing the following buffers. If the data type
|
||||
in the caps event is not acceptable, the element should refuse the
|
||||
event. The element should also refuse the next buffers by returning
|
||||
an appropriate GST\_FLOW\_NOT\_NEGOTIATED return value from the
|
||||
chain function.
|
||||
|
||||
- Downstream elements can request a format change of the stream by
|
||||
sending a RECONFIGURE event upstream. Upstream elements will
|
||||
renegotiate a new format when they receive a RECONFIGURE event.
|
||||
|
||||
The general flow for a source pad starting the negotiation.
|
||||
|
||||
```
|
||||
src sink
|
||||
| |
|
||||
| querycaps? |
|
||||
|---------------->|
|
||||
| caps |
|
||||
select caps |< - - - - - - - -|
|
||||
from the | |
|
||||
candidates | |
|
||||
| |-.
|
||||
| accepts? | |
|
||||
type A |---------------->| | optional
|
||||
| yes | |
|
||||
|< - - - - - - - -| |
|
||||
| |-'
|
||||
| send_event() |
|
||||
send CAPS |---------------->| Receive type A, reconfigure to
|
||||
event A | | process type A.
|
||||
| |
|
||||
| push |
|
||||
push buffer |---------------->| Process buffer of type A
|
||||
| |
|
||||
```
|
||||
|
||||
One possible implementation in pseudo code:
|
||||
|
||||
```
|
||||
[element wants to create a buffer]
|
||||
if not format
|
||||
# see what we can do
|
||||
ourcaps = gst_pad_query_caps (srcpad)
|
||||
# see what the peer can do filtered against our caps
|
||||
candidates = gst_pad_peer_query_caps (srcpad, ourcaps)
|
||||
|
||||
foreach candidate in candidates
|
||||
# make sure the caps is fixed
|
||||
fixedcaps = gst_pad_fixate_caps (srcpad, candidate)
|
||||
|
||||
# see if the peer accepts it
|
||||
if gst_pad_peer_accept_caps (srcpad, fixedcaps)
|
||||
# store the caps as the negotiated caps, this will
|
||||
# call the setcaps function on the pad
|
||||
gst_pad_push_event (srcpad, gst_event_new_caps (fixedcaps))
|
||||
break
|
||||
endif
|
||||
done
|
||||
endif
|
||||
```
|
||||
|
||||
# Negotiate allocator/bufferpool with the ALLOCATION query
|
||||
|
||||
buffer = gst_buffer_new_allocate (NULL, size, 0);
|
||||
# fill buffer and push
|
||||
|
||||
The general flow for a sink pad starting a renegotiation.
|
||||
|
||||
|
||||
```
|
||||
src sink
|
||||
| |
|
||||
| accepts? |
|
||||
|<----------------| type B
|
||||
| yes |
|
||||
|- - - - - - - - >|-.
|
||||
| | | suggest B caps next
|
||||
| |<'
|
||||
| |
|
||||
| push_event() |
|
||||
mark .-|<----------------| send RECONFIGURE event
|
||||
renegotiate| | |
|
||||
'>| |
|
||||
| querycaps() |
|
||||
renegotiate |---------------->|
|
||||
| suggest B |
|
||||
|< - - - - - - - -|
|
||||
| |
|
||||
| send_event() |
|
||||
send CAPS |---------------->| Receive type B, reconfigure to
|
||||
event B | | process type B.
|
||||
| |
|
||||
| push |
|
||||
push buffer |---------------->| Process buffer of type B
|
||||
| |
|
||||
```
|
||||
|
||||
# Use case:
|
||||
|
||||
## `videotestsrc ! xvimagesink`
|
||||
|
||||
* Who decides what format to use?
|
||||
- src pad always decides, by convention. sinkpad can suggest a format
|
||||
by putting it high in the caps query result GstCaps.
|
||||
- since the src decides, it can always choose something that it can do,
|
||||
so this step can only fail if the sinkpad stated it could accept
|
||||
something while later on it couldn't.
|
||||
|
||||
* When does negotiation happen?
|
||||
- before srcpad does a push, it figures out a type as stated in 1), then
|
||||
it pushes a caps event with the type. The sink checks the media type and
|
||||
configures itself for this type.
|
||||
- the source then usually does an ALLOCATION query to negotiate a bufferpool
|
||||
with the sink. It then allocates a buffer from the pool and pushes it to
|
||||
the sink. since the sink accepted the caps, it can create a pool for the
|
||||
format.
|
||||
- since the sink stated in 1) it could accept the type, it will be able to
|
||||
handle it.
|
||||
|
||||
* How can sink request another format?
|
||||
- sink asks if new format is possible for the source.
|
||||
- sink pushes RECONFIGURE event upstream
|
||||
- src receives the RECONFIGURE event and marks renegotiation
|
||||
- On the next buffer push, the source renegotiates the caps and the
|
||||
bufferpool. The sink will put the new new preferred format high in the list
|
||||
of caps it returns from its caps query.
|
||||
|
||||
## `videotestsrc ! queue ! xvimagesink`
|
||||
|
||||
- queue proxies all accept and caps queries to the other peer pad.
|
||||
- queue proxies the bufferpool
|
||||
- queue proxies the RECONFIGURE event
|
||||
- queue stores CAPS event in the queue. This means that the queue can
|
||||
contain buffers with different types.
|
||||
|
||||
## Pull-mode negotiation
|
||||
|
||||
### Rationale
|
||||
|
||||
A pipeline in pull mode has different negotiation needs than one
|
||||
activated in push mode. Push mode is optimized for two use cases:
|
||||
|
||||
- Playback of media files, in which the demuxers and the decoders are
|
||||
the points from which format information should disseminate to the
|
||||
rest of the pipeline; and
|
||||
|
||||
- Recording from live sources, in which users are accustomed to
|
||||
putting a capsfilter directly after the source element; thus the
|
||||
caps information flow proceeds from the user, through the potential
|
||||
caps of the source, to the sinks of the pipeline.
|
||||
|
||||
In contrast, pull mode has other typical use cases:
|
||||
|
||||
- Playback from a lossy source, such as RTP, in which more knowledge
|
||||
about the latency of the pipeline can increase quality; or
|
||||
|
||||
- Audio synthesis, in which audio APIs are tuned to produce only the
|
||||
necessary number of samples, typically driven by a hardware
|
||||
interrupt to fill a DMA buffer or a Jack[0] port buffer.
|
||||
|
||||
- Low-latency effects processing, whereby filters should be applied as
|
||||
data is transferred from a ring buffer to a sink instead of
|
||||
beforehand. For example, instead of using the internal alsasink
|
||||
ringbuffer thread in push-mode wavsrc \! volume \! alsasink, placing
|
||||
the volume inside the sound card writer thread via wavsrc \!
|
||||
audioringbuffer \! volume \! alsasink.
|
||||
|
||||
[0] <http://jackit.sf.net>
|
||||
|
||||
The problem with pull mode is that the sink has to know the format in
|
||||
order to know how many bytes to pull via `gst_pad_pull_range()`. This
|
||||
means that before pulling, the sink must initiate negotation to decide
|
||||
on a format.
|
||||
|
||||
Recalling the principles of capsnego, whereby information must flow from
|
||||
those that have it to those that do not, we see that the three named use
|
||||
cases have different negotiation requirements:
|
||||
|
||||
- RTP and low-latency playback are both like the normal playback case,
|
||||
in which information flows downstream.
|
||||
|
||||
- In audio synthesis, the part of the pipeline that has the most
|
||||
information is the sink, constrained by the capabilities of the
|
||||
graph that feeds it. However the caps are not completely specified;
|
||||
at some point the user has to intervene to choose the sample rate,
|
||||
at least. This can be done externally to gstreamer, as in the jack
|
||||
elements, or internally via a capsfilter, as is customary with live
|
||||
sources.
|
||||
|
||||
Given that sinks potentially need the input of sources, as in the RTP
|
||||
case and at least as a filter in the synthesis case, there must be a
|
||||
negotiation phase before the pull thread is activated. Also, given the
|
||||
low latency offered by pull mode, we want to avoid capsnego from within
|
||||
the pulling thread, in case it causes us to miss our scheduling
|
||||
deadlines.
|
||||
|
||||
The pull thread is usually started in the PAUSED→PLAYING state change.
|
||||
We must be able to complete the negotiation before this state change
|
||||
happens.
|
||||
|
||||
The time to do capsnego, then, is after the SCHEDULING query has
|
||||
succeeded, but before the sink has spawned the pulling thread.
|
||||
|
||||
### Mechanism
|
||||
|
||||
The sink determines that the upstream elements support pull based
|
||||
scheduling by doing a SCHEDULING query.
|
||||
|
||||
The sink initiates the negotiation process by intersecting the results
|
||||
of `gst_pad_query_caps()` on its sink pad and its peer src pad. This is
|
||||
the operation performed by `gst_pad_get_allowed_caps()` In the simple
|
||||
passthrough case, the peer pad’s caps query should return the
|
||||
intersection of calling `get_allowed_caps()` on all of its sink pads. In
|
||||
this way the sink element knows the capabilities of the entire pipeline.
|
||||
|
||||
The sink element then fixates the resulting caps, if necessary,
|
||||
resulting in the flow caps. From now on, the caps query of the sinkpad
|
||||
will only return these fixed caps meaning that upstream elements will
|
||||
only be able to produce this format.
|
||||
|
||||
If the sink element could not set caps on its sink pad, it should post
|
||||
an error message on the bus indicating that negotiation was not
|
||||
possible.
|
||||
|
||||
When negotiation succeeded, the sinkpad and all upstream internally
|
||||
linked pads are activated in pull mode. Typically, this operation will
|
||||
trigger negotiation on the downstream elements, which will now be forced
|
||||
to negotiate to the final fixed desired caps of the sinkpad.
|
||||
|
||||
After these steps, the sink element returns ASYNC from the state change
|
||||
function. The state will commit to PAUSED when the first buffer is
|
||||
received in the sink. This is needed to provide a consistent API to the
|
||||
applications that expect ASYNC return values from sinks but it also
|
||||
allows us to perform the remainder of the negotiation outside of the
|
||||
context of the pulling thread.
|
||||
|
||||
## Patterns
|
||||
|
||||
We can identify 3 patterns in negotiation:
|
||||
|
||||
* Fixed : Can't choose the output format
|
||||
- Caps encoded in the stream
|
||||
- A video/audio decoder
|
||||
- usually uses gst_pad_use_fixed_caps()
|
||||
|
||||
* Transform
|
||||
- Caps not modified (passthrough)
|
||||
- can do caps transform based on element property
|
||||
- fixed caps get transformed into fixed caps
|
||||
- videobox
|
||||
|
||||
* Dynamic : can choose output format
|
||||
- A converter element
|
||||
- depends on downstream caps, needs to do a CAPS query to find
|
||||
transform.
|
||||
- usually prefers to use the identity transform
|
||||
- fixed caps can be transformed into unfixed caps.
|
568
markdown/design/overview.md
Normal file
568
markdown/design/overview.md
Normal file
|
@ -0,0 +1,568 @@
|
|||
# Overview
|
||||
|
||||
This part gives an overview of the design of GStreamer with references
|
||||
to the more detailed explanations of the different topics.
|
||||
|
||||
This document is intented for people that want to have a global overview
|
||||
of the inner workings of GStreamer.
|
||||
|
||||
## Introduction
|
||||
|
||||
GStreamer is a set of libraries and plugins that can be used to
|
||||
implement various multimedia applications ranging from desktop players,
|
||||
audio/video recorders, multimedia servers, transcoders, etc.
|
||||
|
||||
Applications are built by constructing a pipeline composed of elements.
|
||||
An element is an object that performs some action on a multimedia stream
|
||||
such as:
|
||||
|
||||
- read a file
|
||||
- decode or encode between formats
|
||||
- capture from a hardware device
|
||||
- render to a hardware device
|
||||
- mix or multiplex multiple streams
|
||||
|
||||
Elements have input and output pads called sink and source pads in
|
||||
GStreamer. An application links elements together on pads to construct a
|
||||
pipeline. Below is 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 the data and sends the compressed audio data to the vorbisdec
|
||||
element. The vorbisdec element decodes the compressed data and sends it
|
||||
to the alsasink element. The alsasink element sends the samples to the
|
||||
audio card for playback.
|
||||
|
||||
Downstream and upstream are the terms used to describe the direction in
|
||||
the Pipeline. From source to sink is called "downstream" and "upstream"
|
||||
is from sink to source. Dataflow always happens downstream.
|
||||
|
||||
The task of the application is to construct a pipeline as above using
|
||||
existing elements. This is further explained in the pipeline building
|
||||
topic.
|
||||
|
||||
The application does not have to manage any of the complexities of the
|
||||
actual dataflow/decoding/conversions/synchronisation etc. but only calls
|
||||
high level functions on the pipeline object such as PLAY/PAUSE/STOP.
|
||||
|
||||
The application also receives messages and notifications from the
|
||||
pipeline such as metadata, warning, error and EOS messages.
|
||||
|
||||
If the application needs more control over the graph it is possible to
|
||||
directly access the elements and pads in the pipeline.
|
||||
|
||||
## Design overview
|
||||
|
||||
GStreamer design goals include:
|
||||
|
||||
- Process large amounts of data quickly
|
||||
- Allow fully multithreaded processing
|
||||
- Ability to deal with multiple formats
|
||||
- Synchronize different dataflows
|
||||
- Ability to deal with multiple devices
|
||||
|
||||
The capabilities presented to the application depends on the number of
|
||||
elements installed on the system and their functionality.
|
||||
|
||||
The GStreamer core is designed to be media agnostic but provides many
|
||||
features to elements to describe media formats.
|
||||
|
||||
## Elements
|
||||
|
||||
The smallest building blocks in a pipeline are elements. An element
|
||||
provides a number of pads which can be source or sinkpads. Sourcepads
|
||||
provide data and sinkpads consume data. Below is an example of an ogg
|
||||
demuxer element that has one pad that takes (sinks) data and two source
|
||||
pads that produce data.
|
||||
|
||||
```
|
||||
+-----------+
|
||||
| oggdemux |
|
||||
| src0
|
||||
sink src1
|
||||
+-----------+
|
||||
```
|
||||
|
||||
An element can be in four different states: NULL, READY, PAUSED,
|
||||
PLAYING. In the NULL and READY state, the element is not processing any
|
||||
data. In the PLAYING state it is processing data. The intermediate
|
||||
PAUSED state is used to preroll data in the pipeline. A state change can
|
||||
be performed with `gst_element_set_state()`.
|
||||
|
||||
An element always goes through all the intermediate state changes. This
|
||||
means that when en element is in the READY state and is put to PLAYING,
|
||||
it will first go through the intermediate PAUSED state.
|
||||
|
||||
An element state change to PAUSED will activate the pads of the element.
|
||||
First the source pads are activated, then the sinkpads. When the pads
|
||||
are activated, the pad activate function is called. Some pads will start
|
||||
a thread (GstTask) or some other mechanism to start producing or
|
||||
consuming data.
|
||||
|
||||
The PAUSED state is special as it is used to preroll data in the
|
||||
pipeline. The purpose is to fill all connected elements in the pipeline
|
||||
with data so that the subsequent PLAYING state change happens very
|
||||
quickly. Some elements will therefore not complete the state change to
|
||||
PAUSED before they have received enough data. Sink elements are required
|
||||
to only complete the state change to PAUSED after receiving the first
|
||||
data.
|
||||
|
||||
Normally the state changes of elements are coordinated by the pipeline
|
||||
as explained in [states](design/states.md).
|
||||
|
||||
Different categories of elements exist:
|
||||
|
||||
- *source elements*: these are elements that do not consume data but
|
||||
only provide data for the pipeline.
|
||||
|
||||
- *sink elements*: these are elements that do not produce data but
|
||||
renders data to an output device.
|
||||
|
||||
- *transform elements*: these elements transform an input stream in a
|
||||
certain format into a stream of another format.
|
||||
Encoder/decoder/converters are examples.
|
||||
|
||||
- *demuxer elements*: these elements parse a stream and produce several
|
||||
output streams.
|
||||
|
||||
- *mixer/muxer elements*: combine several input streams into one output
|
||||
stream.
|
||||
|
||||
Other categories of elements can be constructed (see [klass](design/draft-klass.md)).
|
||||
|
||||
## Bins
|
||||
|
||||
A bin is an element subclass and acts as a container for other elements
|
||||
so that multiple elements can be combined into one element.
|
||||
|
||||
A bin coordinates its children’s state changes as explained later. It
|
||||
also distributes events and various other functionality to elements.
|
||||
|
||||
A bin can have its own source and sinkpads by ghostpadding one or more
|
||||
of its children’s pads to itself.
|
||||
|
||||
Below is a picture of a bin with two elements. The sinkpad of one
|
||||
element is ghostpadded to the bin.
|
||||
|
||||
```
|
||||
+---------------------------+
|
||||
| bin |
|
||||
| +--------+ +-------+ |
|
||||
| | | | | |
|
||||
| /sink src-sink | |
|
||||
sink +--------+ +-------+ |
|
||||
+---------------------------+
|
||||
```
|
||||
|
||||
## Pipeline
|
||||
|
||||
A pipeline is a special bin subclass that provides the following
|
||||
features to its children:
|
||||
|
||||
- Select and manage a global clock for all its children.
|
||||
- Manage running\_time based on the selected clock. Running\_time is
|
||||
the elapsed time the pipeline spent in the PLAYING state and is used
|
||||
for synchronisation.
|
||||
- Manage latency in the pipeline.
|
||||
- Provide means for elements to comunicate with the application by the
|
||||
GstBus.
|
||||
- Manage the global state of the elements such as Errors and
|
||||
end-of-stream.
|
||||
|
||||
Normally the application creates one pipeline that will manage all the
|
||||
elements in the application.
|
||||
|
||||
## Dataflow and buffers
|
||||
|
||||
GStreamer supports two possible types of dataflow, the push and pull
|
||||
model. In the push model, an upstream element sends data to a downstream
|
||||
element by calling a method on a sinkpad. In the pull model, a
|
||||
downstream element requests data from an upstream element by calling a
|
||||
method on a source pad.
|
||||
|
||||
The most common dataflow is the push model. The pull model can be used
|
||||
in specific circumstances by demuxer elements. The pull model can also
|
||||
be used by low latency audio applications.
|
||||
|
||||
The data passed between pads is encapsulated in Buffers. The buffer
|
||||
contains pointers to the actual memory and also metadata describing the
|
||||
memory. This metadata includes:
|
||||
|
||||
- timestamp of the data, this is the time instance at which the data
|
||||
was captured or the time at which the data should be played back.
|
||||
|
||||
- offset of the data: a media specific offset, this could be samples
|
||||
for audio or frames for video.
|
||||
|
||||
- the duration of the data in time.
|
||||
|
||||
- additional flags describing special properties of the data such as
|
||||
discontinuities or delta units.
|
||||
|
||||
- additional arbitrary metadata
|
||||
|
||||
When an element whishes to send a buffer to another element is does this
|
||||
using one of the pads that is linked to a pad of the other element. In
|
||||
the push model, a buffer is pushed to the peer pad with
|
||||
`gst_pad_push()`. In the pull model, a buffer is pulled from the peer
|
||||
with the `gst_pad_pull_range()` function.
|
||||
|
||||
Before an element pushes out a buffer, it should make sure that the peer
|
||||
element can understand the buffer contents. It does this by querying the
|
||||
peer element for the supported formats and by selecting a suitable
|
||||
common format. The selected format is then first sent to the peer
|
||||
element with a CAPS event before pushing the buffer (see
|
||||
[negotiation](design/negotiation.md)).
|
||||
|
||||
When an element pad receives a CAPS event, it has to check if it
|
||||
understand the media type. The element must refuse following buffers if
|
||||
the media type preceding it was not accepted.
|
||||
|
||||
Both `gst_pad_push()` and `gst_pad_pull_range()` have a return value
|
||||
indicating whether the operation succeeded. An error code means that no
|
||||
more data should be sent to that pad. A source element that initiates
|
||||
the data flow in a thread typically pauses the producing thread when
|
||||
this happens.
|
||||
|
||||
A buffer can be created with `gst_buffer_new()` or by requesting a
|
||||
usable buffer from a buffer pool using
|
||||
`gst_buffer_pool_acquire_buffer()`. Using the second method, it is
|
||||
possible for the peer element to implement a custom buffer allocation
|
||||
algorithm.
|
||||
|
||||
The process of selecting a media type is called caps negotiation.
|
||||
|
||||
## Caps
|
||||
|
||||
A media type (Caps) is described using a generic list of key/value
|
||||
pairs. The key is a string and the value can be a single/list/range of
|
||||
int/float/string.
|
||||
|
||||
Caps that have no ranges/list or other variable parts are said to be
|
||||
fixed and can be used to put on a buffer.
|
||||
|
||||
Caps with variables in them are used to describe possible media types
|
||||
that can be handled by a pad.
|
||||
|
||||
## Dataflow and events
|
||||
|
||||
Parallel to the dataflow is a flow of events. Unlike the buffers, events
|
||||
can pass both upstream and downstream. Some events only travel upstream
|
||||
others only downstream.
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
The application starts by creating a Pipeline element using
|
||||
`gst_pipeline_new ()`. Elements are added to and removed from the
|
||||
pipeline with `gst_bin_add()` and `gst_bin_remove()`.
|
||||
|
||||
After adding the elements, the pads of an element can be retrieved with
|
||||
`gst_element_get_pad()`. Pads can then be linked together with
|
||||
`gst_pad_link()`.
|
||||
|
||||
Some elements create new pads when actual dataflow is happening in the
|
||||
pipeline. With `g_signal_connect()` one can receive a notification when
|
||||
an element has created a pad. These new pads can then be linked to other
|
||||
unlinked pads.
|
||||
|
||||
Some elements cannot be linked together because they operate on
|
||||
different incompatible data types. The possible datatypes a pad can
|
||||
provide or consume can be retrieved with `gst_pad_get_caps()`.
|
||||
|
||||
Below is a simple mp3 playback pipeline that we constructed. We will use
|
||||
this pipeline in further examples.
|
||||
|
||||
+-------------------------------------------+
|
||||
| pipeline |
|
||||
| +---------+ +----------+ +----------+ |
|
||||
| | filesrc | | mp3dec | | alsasink | |
|
||||
| | src-sink src-sink | |
|
||||
| +---------+ +----------+ +----------+ |
|
||||
+-------------------------------------------+
|
||||
|
||||
## Pipeline clock
|
||||
|
||||
One of the important functions of the pipeline is to select a global
|
||||
clock for all the elements in the pipeline.
|
||||
|
||||
The purpose of the clock is to provide a stricly increasing value at the
|
||||
rate of one `GST_SECOND` per second. Clock values are expressed in
|
||||
nanoseconds. Elements use the clock time to synchronize the playback of
|
||||
data.
|
||||
|
||||
Before the pipeline is set to PLAYING, the pipeline asks each element if
|
||||
they can provide a clock. The clock is selected in the following order:
|
||||
|
||||
- If the application selected a clock, use that one.
|
||||
|
||||
- If a source element provides a clock, use that clock.
|
||||
|
||||
- Select a clock from any other element that provides a clock, start
|
||||
with the sinks.
|
||||
|
||||
- If no element provides a clock a default system clock is used for
|
||||
the pipeline.
|
||||
|
||||
In a typical playback pipeline this algorithm will select the clock
|
||||
provided by a sink element such as an audio sink.
|
||||
|
||||
In capture pipelines, this will typically select the clock of the data
|
||||
producer, which in most cases can not control the rate at which it
|
||||
produces data.
|
||||
|
||||
## Pipeline states
|
||||
|
||||
When all the pads are linked and signals have been connected, the
|
||||
pipeline can be put in the PAUSED state to start dataflow.
|
||||
|
||||
When a bin (and hence a pipeline) performs a state change, it will
|
||||
change the state of all its children. The pipeline will change the state
|
||||
of its children from the sink elements to the source elements, this to
|
||||
make sure that no upstream element produces data to an element that is
|
||||
not yet ready to accept it.
|
||||
|
||||
In the mp3 playback pipeline, the state of the elements is changed in
|
||||
the order alsasink, mp3dec, filesrc.
|
||||
|
||||
All intermediate states are traversed for each element resulting in the
|
||||
following chain of state changes:
|
||||
|
||||
* alsasink to READY: the audio device is probed
|
||||
|
||||
* mp3dec to READY: nothing happens.
|
||||
|
||||
* filesrc to READY: the file is probed
|
||||
|
||||
* alsasink to PAUSED: the audio device is opened. alsasink is a sink and returns ASYNC because it did not receive data yet. mp3dec to PAUSED: the decoding library is initialized
|
||||
|
||||
* filesrc to PAUSED: the file is opened and a thread is started to push data to mp3dec
|
||||
|
||||
At this point data flows from filesrc to mp3dec and alsasink. Since
|
||||
mp3dec is PAUSED, it accepts the data from filesrc on the sinkpad and
|
||||
starts decoding the compressed data to raw audio samples.
|
||||
|
||||
The mp3 decoder figures out the samplerate, the number of channels and
|
||||
other audio properties of the raw audio samples and sends out a caps
|
||||
event with the media type.
|
||||
|
||||
Alsasink then receives the caps event, inspects the caps and
|
||||
reconfigures itself to process the media type.
|
||||
|
||||
mp3dec then puts the decoded samples into a Buffer and pushes this
|
||||
buffer to the next element.
|
||||
|
||||
Alsasink receives the buffer with samples. Since it received the first
|
||||
buffer of samples, it completes the state change to the PAUSED state. At
|
||||
this point the pipeline is prerolled and all elements have samples.
|
||||
Alsasink is now also capable of providing a clock to the pipeline.
|
||||
|
||||
Since alsasink is now in the PAUSED state it blocks while receiving the
|
||||
first buffer. This effectively blocks both mp3dec and filesrc in their
|
||||
gst\_pad\_push().
|
||||
|
||||
Since all elements now return SUCCESS from the
|
||||
gst\_element\_get\_state() function, the pipeline can be put in the
|
||||
PLAYING state.
|
||||
|
||||
Before going to PLAYING, the pipeline select a clock and samples the
|
||||
current time of the clock. This is the base\_time. It then distributes
|
||||
this time to all elements. Elements can then synchronize against the
|
||||
clock using the buffer running\_time
|
||||
base\_time (See also [synchronisation](design/synchronisation.md)).
|
||||
|
||||
The following chain of state changes then takes place:
|
||||
|
||||
* alsasink to PLAYING: the samples are played to the audio device
|
||||
|
||||
* mp3dec to PLAYING: nothing happens
|
||||
|
||||
* filesrc to PLAYING: nothing happens
|
||||
|
||||
## Pipeline status
|
||||
|
||||
The pipeline informs the application of any special events that occur in
|
||||
the pipeline with the bus. The bus is an object that the pipeline
|
||||
provides and that can be retrieved with `gst_pipeline_get_bus()`.
|
||||
|
||||
The bus can be polled or added to the glib mainloop.
|
||||
|
||||
The bus is distributed to all elements added to the pipeline. The
|
||||
elements use the bus to post messages on. Various message types exist
|
||||
such as ERRORS, WARNINGS, EOS, `STATE_CHANGED`, etc..
|
||||
|
||||
The pipeline handles EOS messages received from elements in a special
|
||||
way. It will only forward the message to the application when all sink
|
||||
elements have posted an EOS message.
|
||||
|
||||
Other methods for obtaining the pipeline status include the Query
|
||||
functionality that can be performed with `gst_element_query()` on the
|
||||
pipeline. This type of query is useful for obtaining information about
|
||||
the current position and total time of the pipeline. It can also be used
|
||||
to query for the supported seeking formats and ranges.
|
||||
|
||||
## Pipeline EOS
|
||||
|
||||
When the source filter encounters the end of the stream, it sends an EOS
|
||||
event to the peer element. This event will then travel downstream to all
|
||||
of the connected elements to inform them of the EOS. The element is not
|
||||
supposed to accept any more data after receiving an EOS event on a
|
||||
sinkpad.
|
||||
|
||||
The element providing the streaming thread stops sending data after
|
||||
sending the EOS event.
|
||||
|
||||
The EOS event will eventually arrive in the sink element. The sink will
|
||||
then post an EOS message on the bus to inform the pipeline that a
|
||||
particular stream has finished. When all sinks have reported EOS, the
|
||||
pipeline forwards the EOS message to the application. The EOS message is
|
||||
only forwarded to the application in the PLAYING state.
|
||||
|
||||
When in EOS, the pipeline remains in the PLAYING state, it is the
|
||||
applications responsability to PAUSE or READY the pipeline. The
|
||||
application can also issue a seek, for example.
|
||||
|
||||
## Pipeline READY
|
||||
|
||||
When a running pipeline is set from the PLAYING to READY state, the
|
||||
following actions occur in the pipeline:
|
||||
|
||||
* alsasink to PAUSED: alsasink blocks and completes the state change on the
|
||||
next sample. If the element was EOS, it does not wait for a sample to complete
|
||||
the state change.
|
||||
* mp3dec to PAUSED: nothing
|
||||
* filesrc to PAUSED: nothing
|
||||
|
||||
Going to the intermediate PAUSED state will block all elements in the
|
||||
`_push()` functions. This happens because the sink element blocks on the
|
||||
first buffer it receives.
|
||||
|
||||
Some elements might be performing blocking operations in the PLAYING
|
||||
state that must be unblocked when they go into the PAUSED state. This
|
||||
makes sure that the state change happens very fast.
|
||||
|
||||
In the next PAUSED to READY state change the pipeline has to shut down
|
||||
and all streaming threads must stop sending data. This happens in the
|
||||
following sequence:
|
||||
|
||||
* alsasink to READY: alsasink unblocks from the `_chain()` function and returns
|
||||
a FLUSHING return value to the peer element. The sinkpad is deactivated and
|
||||
becomes unusable for sending more data.
|
||||
* mp3dec to READY: the pads are deactivated and the state change completes
|
||||
when mp3dec leaves its `_chain()` function.
|
||||
* filesrc to READY: the pads are deactivated and the thread is paused.
|
||||
|
||||
The upstream elements finish their chain() function because the
|
||||
downstream element returned an error code (FLUSHING) from the `_push()`
|
||||
functions. These error codes are eventually returned to the element that
|
||||
started the streaming thread (filesrc), which pauses the thread and
|
||||
completes the state change.
|
||||
|
||||
This sequence of events ensure that all elements are unblocked and all
|
||||
streaming threads stopped.
|
||||
|
||||
## Pipeline seeking
|
||||
|
||||
Seeking in the pipeline requires a very specific order of operations to
|
||||
make sure that the elements remain synchronized and that the seek is
|
||||
performed with a minimal amount of latency.
|
||||
|
||||
An application issues a seek event on the pipeline using
|
||||
`gst_element_send_event()` on the pipeline element. The event can be a
|
||||
seek event in any of the formats supported by the elements.
|
||||
|
||||
The pipeline first pauses the pipeline to speed up the seek operations.
|
||||
|
||||
The pipeline then issues the seek event to all sink elements. The sink
|
||||
then forwards the seek event upstream until some element can perform the
|
||||
seek operation, which is typically the source or demuxer element. All
|
||||
intermediate elements can transform the requested seek offset to another
|
||||
format, this way a decoder element can transform a seek to a frame
|
||||
number to a timestamp, for example.
|
||||
|
||||
When the seek event reaches an element that will perform the seek
|
||||
operation, that element performs the following steps.
|
||||
|
||||
1) send a FLUSH_START event to all downstream and upstream peer elements.
|
||||
2) make sure the streaming thread is not running. The streaming thread will
|
||||
always stop because of step 1).
|
||||
3) perform the seek operation
|
||||
4) send a FLUSH done event to all downstream and upstream peer elements.
|
||||
5) send SEGMENT event to inform all elements of the new position and to complete
|
||||
the seek.
|
||||
|
||||
In step 1) all downstream elements have to return from any blocking
|
||||
operations and have to refuse any further buffers or events different
|
||||
from a FLUSH done.
|
||||
|
||||
The first step ensures that the streaming thread eventually unblocks and
|
||||
that step 2) can be performed. At this point, dataflow is completely
|
||||
stopped in the pipeline.
|
||||
|
||||
In step 3) the element performs the seek to the requested position.
|
||||
|
||||
In step 4) all peer elements are allowed to accept data again and
|
||||
streaming can continue from the new position. A FLUSH done event is sent
|
||||
to all the peer elements so that they accept new data again and restart
|
||||
their streaming threads.
|
||||
|
||||
Step 5) informs all elements of the new position in the stream. After
|
||||
that the event function returns back to the application. and the
|
||||
streaming threads start to produce new data.
|
||||
|
||||
Since the pipeline is still PAUSED, this will preroll the next media
|
||||
sample in the sinks. The application can wait for this preroll to
|
||||
complete by performing a `_get_state()` on the pipeline.
|
||||
|
||||
The last step in the seek operation is then to adjust the stream
|
||||
running_time of the pipeline to 0 and to set the pipeline back to
|
||||
PLAYING.
|
||||
|
||||
The sequence of events in our mp3 playback example.
|
||||
|
||||
```
|
||||
| a) seek on pipeline
|
||||
| b) PAUSE pipeline
|
||||
+----------------------------------V--------+
|
||||
| pipeline | c) seek on sink
|
||||
| +---------+ +----------+ +---V------+ |
|
||||
| | filesrc | | mp3dec | | alsasink | |
|
||||
| | src-sink src-sink | |
|
||||
| +---------+ +----------+ +----|-----+ |
|
||||
+-----------------------------------|-------+
|
||||
<------------------------+
|
||||
d) seek travels upstream
|
||||
|
||||
--------------------------> 1) FLUSH event
|
||||
| 2) stop streaming
|
||||
| 3) perform seek
|
||||
--------------------------> 4) FLUSH done event
|
||||
--------------------------> 5) SEGMENT event
|
||||
|
||||
| e) update running_time to 0
|
||||
| f) PLAY pipeline
|
||||
```
|
55
markdown/design/preroll.md
Normal file
55
markdown/design/preroll.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
# Preroll
|
||||
|
||||
A sink element can only complete the state change to `PAUSED` after a
|
||||
buffer has been queued on the input pad or pads. This process is called
|
||||
prerolling and is needed to fill the pipeline with buffers so that the
|
||||
transition to `PLAYING` goes as fast as possible with no visual delay for
|
||||
the user.
|
||||
|
||||
Preroll is also crucial in maintaining correct audio and video
|
||||
synchronisation and ensuring that no buffers are dropped in the sinks.
|
||||
|
||||
After receiving a buffer (or EOS) on a pad the chain/event function
|
||||
should wait to render the buffers or in the EOS case, wait to post the
|
||||
EOS message. While waiting, the sink will wait for the preroll cond to
|
||||
be signalled.
|
||||
|
||||
Several things can happen that require the preroll cond to be signalled.
|
||||
This include state changes or flush events. The prerolling is
|
||||
implemented in sinks (see [element-sink](design/element-sink.md)
|
||||
|
||||
## Committing the state
|
||||
|
||||
When going to `PAUSED` and `PLAYING` a buffer should be queued in the pad.
|
||||
We also make this requirement for going to `PLAYING` since a flush event
|
||||
in the `PAUSED` state could unqueue the buffer again.
|
||||
|
||||
The state is commited in the following conditions:
|
||||
|
||||
- a buffer is received on a sinkpad
|
||||
- an GAP event is received on a sinkpad.
|
||||
- an EOS event is received on a sinkpad.
|
||||
|
||||
We require the state change to be commited in EOS as well since an EOS
|
||||
means by definition that no buffer is going to arrive anymore.
|
||||
|
||||
After the state is commited, a blocking wait should be performed for the
|
||||
next event. Some sinks might render the preroll buffer before starting
|
||||
this blocking wait.
|
||||
|
||||
## Unlocking the preroll
|
||||
|
||||
The following conditions unlock the preroll:
|
||||
|
||||
- a state change
|
||||
- a flush event
|
||||
|
||||
When the preroll is unlocked by a flush event, a return value of
|
||||
`GST_FLOW_FLUSHING` is to be returned to the peer pad.
|
||||
|
||||
When preroll is unlocked by a state change to `PLAYING`, playback and
|
||||
rendering of the buffers shall start.
|
||||
|
||||
When preroll is unlocked by a state change to READY, the buffer is to be
|
||||
discarded and a `GST_FLOW_FLUSHING` shall be returned to the peer
|
||||
element.
|
363
markdown/design/probes.md
Normal file
363
markdown/design/probes.md
Normal file
|
@ -0,0 +1,363 @@
|
|||
# Probes
|
||||
|
||||
Probes are callbacks that can be installed by the application and will notify
|
||||
the application about the states of the dataflow.
|
||||
|
||||
# Requirements
|
||||
|
||||
Applications should be able to monitor and control the dataflow on pads.
|
||||
We identify the following types:
|
||||
|
||||
- be notified when the pad is/becomes idle and make sure the pad stays
|
||||
idle. This is essential to be able to implement dynamic relinking of
|
||||
elements without breaking the dataflow.
|
||||
|
||||
- be notified when data, events or queries are pushed or sent on a
|
||||
pad. It should also be possible to inspect and modify the data.
|
||||
|
||||
- be able to drop, pass and block on data based on the result of the
|
||||
callback.
|
||||
|
||||
- be able to drop, pass data on blocking pads based on methods
|
||||
performed by the application
|
||||
thread.
|
||||
|
||||
# Overview
|
||||
|
||||
The function gst_pad_add_probe() is used to add a probe to a pad. It accepts a
|
||||
probe type mask and a callback.
|
||||
|
||||
``` c
|
||||
gulong gst_pad_add_probe (GstPad *pad,
|
||||
GstPadProbeType mask,
|
||||
GstPadProbeCallback callback,
|
||||
gpointer user_data,
|
||||
GDestroyNotify destroy_data);
|
||||
```
|
||||
|
||||
The function returns a gulong that uniquely identifies the probe and that can
|
||||
be used to remove the probe with gst_pad_remove_probe():
|
||||
|
||||
``` c
|
||||
void gst_pad_remove_probe (GstPad *pad, gulong id);
|
||||
```
|
||||
|
||||
The mask parameter is a bitwise or of the following flags:
|
||||
|
||||
``` c
|
||||
typedef enum
|
||||
{
|
||||
GST_PAD_PROBE_TYPE_INVALID = 0,
|
||||
|
||||
/* flags to control blocking */
|
||||
GST_PAD_PROBE_TYPE_IDLE = (1 << 0),
|
||||
GST_PAD_PROBE_TYPE_BLOCK = (1 << 1),
|
||||
|
||||
/* flags to select datatypes */
|
||||
GST_PAD_PROBE_TYPE_BUFFER = (1 << 4),
|
||||
GST_PAD_PROBE_TYPE_BUFFER_LIST = (1 << 5),
|
||||
GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM = (1 << 6),
|
||||
GST_PAD_PROBE_TYPE_EVENT_UPSTREAM = (1 << 7),
|
||||
GST_PAD_PROBE_TYPE_EVENT_FLUSH = (1 << 8),
|
||||
GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM = (1 << 9),
|
||||
GST_PAD_PROBE_TYPE_QUERY_UPSTREAM = (1 << 10),
|
||||
|
||||
/* flags to select scheduling mode */
|
||||
GST_PAD_PROBE_TYPE_PUSH = (1 << 12),
|
||||
GST_PAD_PROBE_TYPE_PULL = (1 << 13),
|
||||
} GstPadProbeType;
|
||||
```
|
||||
|
||||
When adding a probe with the IDLE or BLOCK flag, the probe will become a
|
||||
blocking probe (see below). Otherwise the probe will be a DATA probe.
|
||||
|
||||
The datatype and scheduling selector flags are used to select what kind of
|
||||
datatypes and scheduling modes should be allowed in the callback.
|
||||
|
||||
The blocking flags must match the triggered probe exactly.
|
||||
|
||||
The probe callback is defined as:
|
||||
|
||||
``` c
|
||||
GstPadProbeReturn (*GstPadProbeCallback) (GstPad *pad, GstPadProbeInfo *info,
|
||||
gpointer user_data);
|
||||
```
|
||||
|
||||
A probe info structure is passed as an argument and its type is guaranteed
|
||||
to match the mask that was used to register the callback. The data item in the
|
||||
info contains type specific data, which is usually the data item that is blocked
|
||||
or NULL when no data item is present.
|
||||
|
||||
The probe can return any of the following return values:
|
||||
|
||||
``` c
|
||||
typedef enum
|
||||
{
|
||||
GST_PAD_PROBE_DROP,
|
||||
GST_PAD_PROBE_OK,
|
||||
GST_PAD_PROBE_REMOVE,
|
||||
GST_PAD_PROBE_PASS,
|
||||
} GstPadProbeReturn;
|
||||
```
|
||||
|
||||
`GST_PAD_PROBE_OK` is the normal return value. DROP will drop the item that is
|
||||
currently being probed. GST_PAD_PROBE_REMOVE the currently executing probe from the
|
||||
list of probes.
|
||||
|
||||
`GST_PAD_PROBE_PASS` is relevant for blocking probes and will temporarily unblock the
|
||||
pad and let the item trough, it will then block again on the next item.
|
||||
|
||||
# Blocking probes
|
||||
|
||||
Blocking probes are probes with BLOCK or IDLE flags set. They will always
|
||||
block the dataflow and trigger the callback according to the following rules:
|
||||
|
||||
When the IDLE flag is set, the probe callback is called as soon as no data is
|
||||
flowing over the pad. If at the time of probe registration, the pad is idle,
|
||||
the callback will be called immediately from the current thread. Otherwise,
|
||||
the callback will be called as soon as the pad becomes idle in the streaming
|
||||
thread.
|
||||
|
||||
The IDLE probe is useful to perform dynamic linking, it allows to wait for for
|
||||
a safe moment when an unlink/link operation can be done. Since the probe is a
|
||||
blocking probe, it will also make sure that the pad stays idle until the probe
|
||||
is removed.
|
||||
|
||||
When the BLOCK flag is set, the probe callback will be called when new data
|
||||
arrives on the pad and right before the pad goes into the blocking state. This
|
||||
callback is thus only called when there is new data on the pad.
|
||||
|
||||
The blocking probe is removed with gst_pad_remove_probe() or when the probe
|
||||
callback return GST_PAD_PROBE_REMOVE. In both cases, and if this was the last
|
||||
blocking probe on the pad, the pad is unblocked and dataflow can continue.
|
||||
|
||||
# Non-Blocking probes
|
||||
|
||||
Non-blocking probes or DATA probes are probes triggered when data is flowing
|
||||
over the pad. The are called after the blocking probes are run and always with
|
||||
data.
|
||||
|
||||
# Push dataflow
|
||||
|
||||
Push probes have the GST\_PAD\_PROBE\_TYPE\_PUSH flag set in the
|
||||
callbacks.
|
||||
|
||||
In push based scheduling, the blocking probe is called first with the
|
||||
data item. Then the data probes are called before the peer pad chain or
|
||||
event function is called.
|
||||
|
||||
The data probes are called before the peer pad is checked. This allows
|
||||
for linking the pad in either the BLOCK or DATA probes on the pad.
|
||||
|
||||
Before the peerpad chain or event function is called, the peer pad block
|
||||
and data probes are called.
|
||||
|
||||
Finally, the IDLE probe is called on the pad after the data was sent to
|
||||
the peer pad.
|
||||
|
||||
The push dataflow probe behavior is the same for buffers and
|
||||
bidirectional events.
|
||||
|
||||
```
|
||||
pad peerpad
|
||||
| |
|
||||
gst_pad_push() / | |
|
||||
gst_pad_push_event() | |
|
||||
-------------------->O |
|
||||
O |
|
||||
flushing? O |
|
||||
FLUSHING O |
|
||||
< - - - - - - O |
|
||||
O-> do BLOCK probes |
|
||||
O |
|
||||
O-> do DATA probes |
|
||||
no peer? O |
|
||||
NOT_LINKED O |
|
||||
< - - - - - - O |
|
||||
O gst_pad_chain() / |
|
||||
O gst_pad_send_event() |
|
||||
O------------------------------>O
|
||||
O flushing? O
|
||||
O FLUSHING O
|
||||
O< - - - - - - - - - - - - - - -O
|
||||
O O-> do BLOCK probes
|
||||
O O
|
||||
O O-> do DATA probes
|
||||
O O
|
||||
O O---> chainfunc /
|
||||
O O eventfunc
|
||||
O< - - - - - - - - - - - - - - -O
|
||||
O |
|
||||
O-> do IDLE probes |
|
||||
O |
|
||||
< - - - - - - O |
|
||||
| |
|
||||
```
|
||||
|
||||
# Pull dataflow
|
||||
|
||||
Pull probes have the `GST_PAD_PROBE_TYPE_PULL` flag set in the
|
||||
callbacks.
|
||||
|
||||
The `gst_pad_pull_range()` call will first trigger the BLOCK probes
|
||||
without a DATA item. This allows the pad to be linked before the peer
|
||||
pad is resolved. It also allows the callback to set a data item in the
|
||||
probe info.
|
||||
|
||||
After the blocking probe and the getrange function is called on the peer
|
||||
pad and there is a data item, the DATA probes are called.
|
||||
|
||||
When control returns to the sinkpad, the IDLE callbacks are called. The
|
||||
IDLE callback is called without a data item so that it will also be
|
||||
called when there was an error.
|
||||
|
||||
If there is a valid DATA item, the DATA probes are called for the item.
|
||||
|
||||
```
|
||||
srcpad sinkpad
|
||||
| |
|
||||
| | gst_pad_pull_range()
|
||||
| O<---------------------
|
||||
| O
|
||||
| O flushing?
|
||||
| O FLUSHING
|
||||
| O - - - - - - - - - - >
|
||||
| do BLOCK probes <-O
|
||||
| O no peer?
|
||||
| O NOT_LINKED
|
||||
| O - - - - - - - - - - >
|
||||
| gst_pad_get_range() O
|
||||
O<------------------------------O
|
||||
O O
|
||||
O flushing? O
|
||||
O FLUSHING O
|
||||
O- - - - - - - - - - - - - - - >O
|
||||
do BLOCK probes <-O O
|
||||
O O
|
||||
getrangefunc <---O O
|
||||
O flow error? O
|
||||
O- - - - - - - - - - - - - - - >O
|
||||
O O
|
||||
do DATA probes <-O O
|
||||
O- - - - - - - - - - - - - - - >O
|
||||
| O
|
||||
| do IDLE probes <-O
|
||||
| O flow error?
|
||||
| O - - - - - - - - - - >
|
||||
| O
|
||||
| do DATA probes <-O
|
||||
| O - - - - - - - - - - >
|
||||
| |
|
||||
```
|
||||
|
||||
# Queries
|
||||
|
||||
Query probes have the GST_PAD_PROBE_TYPE_QUERY_* flag set in the
|
||||
callbacks.
|
||||
|
||||
```
|
||||
pad peerpad
|
||||
| |
|
||||
gst_pad_peer_query() | |
|
||||
-------------------->O |
|
||||
O |
|
||||
O-> do BLOCK probes |
|
||||
O |
|
||||
O-> do QUERY | PUSH probes |
|
||||
no peer? O |
|
||||
FALSE O |
|
||||
< - - - - - - O |
|
||||
O gst_pad_query() |
|
||||
O------------------------------>O
|
||||
O O-> do BLOCK probes
|
||||
O O
|
||||
O O-> do QUERY | PUSH probes
|
||||
O O
|
||||
O O---> queryfunc
|
||||
O error O
|
||||
<- - - - - - - - - - - - - - - - - - - - - - -O
|
||||
O O
|
||||
O O-> do QUERY | PULL probes
|
||||
O< - - - - - - - - - - - - - - -O
|
||||
O |
|
||||
O-> do QUERY | PULL probes |
|
||||
O |
|
||||
< - - - - - - O |
|
||||
| |
|
||||
```
|
||||
|
||||
For queries, the PUSH ProbeType is set when the query is traveling to
|
||||
the object that will answer the query and the PULL type is set when the
|
||||
query contains the answer.
|
||||
|
||||
# Use-cases
|
||||
|
||||
## Prerolling a partial pipeline
|
||||
|
||||
```
|
||||
.---------. .---------. .----------.
|
||||
| filesrc | | demuxer | .-----. | decoder1 |
|
||||
| src -> sink src1 ->|queue|-> sink src
|
||||
'---------' | | '-----' '----------' X
|
||||
| | .----------.
|
||||
| | .-----. | decoder2 |
|
||||
| src2 ->|queue|-> sink src
|
||||
'---------' '-----' '----------' X
|
||||
```
|
||||
|
||||
The purpose is to create the pipeline dynamically up to the decoders but
|
||||
not yet connect them to a sink and without losing any data.
|
||||
|
||||
To do this, the source pads of the decoders is blocked so that no events
|
||||
or buffers can escape and we don’t interrupt the stream.
|
||||
|
||||
When all of the dynamic pad are created (no-more-pads emitted by the
|
||||
branching point, ie, the demuxer or the queues filled) and the pads are
|
||||
blocked (blocked callback received) the pipeline is completely
|
||||
prerolled.
|
||||
|
||||
It should then be possible to perform the following actions on the
|
||||
prerolled pipeline:
|
||||
|
||||
- query duration/position
|
||||
|
||||
- perform a flushing seek to preroll a new position
|
||||
|
||||
- connect other elements and unblock the blocked pads.
|
||||
|
||||
## dynamically switching an element in a PLAYING pipeline
|
||||
|
||||
```
|
||||
.----------. .----------. .----------.
|
||||
| element1 | | element2 | | element3 |
|
||||
... src -> sink src -> sink ...
|
||||
'----------' '----------' '----------'
|
||||
.----------.
|
||||
| element4 |
|
||||
sink src
|
||||
'----------'
|
||||
```
|
||||
|
||||
The purpose is to replace element2 with element4 in the PLAYING
|
||||
pipeline.
|
||||
|
||||
1) block element1 src pad.
|
||||
2) inside the block callback nothing is flowing between
|
||||
element1 and element2 and nothing will flow until unblocked.
|
||||
3) unlink element1 and element2
|
||||
4) optional step: make sure data is flushed out of element2:
|
||||
4a) pad event probe on element2 src
|
||||
4b) send EOS to element2, this makes sure that element2 flushes out the last bits of data it holds.
|
||||
4c) wait for EOS to appear in the probe, drop the EOS.
|
||||
4d) remove the EOS pad event probe.
|
||||
5) unlink element2 and element3
|
||||
5a) optionally element2 can now be set to NULL and/or removed from the pipeline.
|
||||
6) link element4 and element3
|
||||
7) link element1 and element4
|
||||
8) make sure element4 is in the same state as the rest of the elements. The
|
||||
element should at least be PAUSED.
|
||||
9) unblock element1 src
|
||||
|
||||
The same flow can be used to replace an element in a PAUSED pipeline. Of
|
||||
course in a PAUSED pipeline there might not be dataflow so the block
|
||||
might not immediately happen.
|
222
markdown/design/progress.md
Normal file
222
markdown/design/progress.md
Normal file
|
@ -0,0 +1,222 @@
|
|||
# Progress Reporting
|
||||
|
||||
This document describes the design and use cases for the progress
|
||||
reporting messages.
|
||||
|
||||
PROGRESS messages are posted on the bus to inform the application about
|
||||
the progress of asynchronous operations in the pipeline. This should not
|
||||
be confused with asynchronous state changes.
|
||||
|
||||
We accommodate for the following requirements:
|
||||
|
||||
- Application is informed when an async operation starts and
|
||||
completes.
|
||||
|
||||
- It should be possible for the application to generically detect
|
||||
common operations and incorporate their progress into the GUI.
|
||||
|
||||
- Applications can cancel pending operations by doing regular state
|
||||
changes.
|
||||
|
||||
- Applications should be able to wait for completion of async
|
||||
operations.
|
||||
|
||||
We allow for the following scenarios:
|
||||
|
||||
- Elements want to inform the application about asynchronous DNS
|
||||
lookups and pending network requests. This includes starting and
|
||||
completing the lookup.
|
||||
|
||||
- Elements opening devices and resources asynchronously.
|
||||
|
||||
- Applications having more freedom to implement timeout and
|
||||
cancelation of operations that currently block the state changes or
|
||||
happen invisibly behind the scenes.
|
||||
|
||||
## Rationale
|
||||
|
||||
The main reason for adding these extra progress notifications is
|
||||
twofold:
|
||||
|
||||
### to give the application more information of what is going on
|
||||
|
||||
When there are well defined progress information codes, applications
|
||||
can let the user know about the status of the progress. We anticipate to
|
||||
have at least DNS resolving and server connections and requests be well
|
||||
defined.
|
||||
|
||||
### To make the state changes non-blocking and cancellable.
|
||||
|
||||
Currently state changes such as going to the READY or PAUSED state often do
|
||||
blocking calls such as resolving DNS or connecting to a remote server. These
|
||||
operations often block the main thread and are often not cancellable, causing
|
||||
application lockups.
|
||||
|
||||
We would like to make the state change function, instead, start a separate
|
||||
thread that performs the blocking operations in a cancellable way. When going
|
||||
back to the NULL state, all pending operations would be canceled immediately.
|
||||
|
||||
For downward state changes, we want to let the application implement its own
|
||||
timeout mechanism. For example: when stopping an RTSP stream, the clients
|
||||
needs to send a TEARDOWN request to the server. This can however take an
|
||||
unlimited amount of time in case of network problems. We want to give the
|
||||
application an opportunity to wait (and timeout) for the completion of the
|
||||
async operation before setting the element to the final NULL state.
|
||||
|
||||
Progress updates are very similar to buffering messages in the same way
|
||||
that the application can decide to wait for the completion of the
|
||||
buffering process before performing the next state change. It might make
|
||||
sense to implement buffering with the progress messages in the future.
|
||||
|
||||
## Async state changes
|
||||
|
||||
GStreamer currently has a `GST_STATE_CHANGE_ASYNC` return value to note
|
||||
to the application that a state change is happening asynchronously.
|
||||
|
||||
The main purpose of this return value is to make the pipeline wait for
|
||||
preroll and delay a future (upwards) state changes until the sinks are
|
||||
prerolled.
|
||||
|
||||
In the case of async operations on source, this will automatically force
|
||||
sinks to stay async because they will not preroll before the source can
|
||||
produce data.
|
||||
|
||||
The fact that other asynchronous operations happen behind the scenes is
|
||||
irrelevant for the prerolling process so it is not implemented with the
|
||||
ASYNC state change return value in order to not complicate the state
|
||||
changes and mix concepts.
|
||||
|
||||
## Use cases
|
||||
|
||||
### RTSP client (but also HTTP, MMS, …)
|
||||
|
||||
When the client goes from the READY to the PAUSED state, it opens a socket,
|
||||
performs a DNS lookup, retrieves the SDP and negotiates the streams. All these
|
||||
operations currently block the state change function for an indefinite amount
|
||||
of time and while they are blocking cannot be canceled.
|
||||
|
||||
Instead, a thread would be started to perform these operations asynchronously
|
||||
and the state change would complete with the usual NO_PREROLL return value.
|
||||
Before starting the thread a PROGRESS message would be posted to mark the
|
||||
start of the async operation.
|
||||
|
||||
As the DNS lookup completes and the connection is established, PROGRESS
|
||||
messages are posted on the bus to inform the application of the progress. When
|
||||
something fails, an error is posted and a PROGRESS CANCELED message is posted.
|
||||
The application can then stop the pipeline.
|
||||
|
||||
If there are no errors and the setup of the streams completed successfully, a
|
||||
PROGRESS COMPLETED is posted on the bus. The thread then goes to sleep and the
|
||||
asynchronous operation completed.
|
||||
|
||||
The RTSP protocol requires to send a TEARDOWN request to the server
|
||||
before closing the connection and destroying the socket. A state change to the
|
||||
READY state will issue the TEARDOWN request in the background and notify the
|
||||
application of this pending request with a PROGRESS message.
|
||||
|
||||
The application might want to only go to the NULL state after it got confirmation
|
||||
that the TEARDOWN request completed or it might choose to go to NULL after a
|
||||
timeout. It might also be possible that the application just want to close the
|
||||
socket as fast as possible without waiting for completion of the TEARDOWN request.
|
||||
|
||||
### Network performance measuring
|
||||
|
||||
DNS lookup and connection times can be measured by calculating the elapsed
|
||||
time between the various PROGRESS messages.
|
||||
|
||||
## Messages
|
||||
|
||||
A new `PROGRESS` message will be created.
|
||||
|
||||
The following fields will be contained in the message:
|
||||
|
||||
- **`type`**, GST_TYPE_PROGRESS_TYPE: a set of types to define the type of progress
|
||||
* GST_PROGRESS_TYPE_START: A new task is started in the background
|
||||
* GST_PROGRESS_TYPE_CONTINUE: The previous tasks completed and a new one
|
||||
continues. This is done so that the application can follow a set of
|
||||
continuous tasks and react to COMPLETE only when the element completely
|
||||
finished. * GST_PROGRESS_TYPE_CANCELED: A task is canceled by the user.
|
||||
* GST_PROGRESS_TYPE_ERROR: A task stopped because of an error. In case of
|
||||
an error, an error message will have been posted before.
|
||||
* GST_PROGRESS_TYPE_COMPLETE: A task completed successfully.
|
||||
|
||||
- **`code`**, G_TYPE_STRING: A generic extensible string that can be used to
|
||||
programmatically determine the action that is in progress. Some standard
|
||||
predefined codes will be defined.
|
||||
|
||||
- **`text`**, G_TYPE_STRING: A user visible string detailing the action.
|
||||
|
||||
- **`percent`**, G_TYPE_INT: between 0 and 100 Progress of the action as
|
||||
a percentage, the following values are allowed:
|
||||
- GST_PROGRESS_TYPE_START always has a 0% value.
|
||||
- GST_PROGRESS_TYPE_CONTINUE have a value between 0 and 100
|
||||
- GST_PROGRESS_TYPE_CANCELED, GST_PROGRESS_TYPE_ERROR and
|
||||
GST_PROGRESS_TYPE_COMPLETE always have a 100% value.
|
||||
|
||||
- **`timeout`**, G_TYPE_INT in milliseconds: The timeout of the async
|
||||
operation. -1 if unknown/unlimited.. This field can be interesting to the
|
||||
application when it wants to display some sort of progress indication.
|
||||
|
||||
- ….
|
||||
|
||||
Depending on the code, more fields can be put here.
|
||||
|
||||
## Implementation
|
||||
|
||||
Elements should not do blocking operations from the state change
|
||||
function. Instead, elements should post an appropriate progress message
|
||||
with the right code and of type `GST_PROGRESS_TYPE_START` and then
|
||||
start a thread to perform the blocking calls in a cancellable manner.
|
||||
|
||||
It is highly recommended to only start async operations from the READY
|
||||
to PAUSED state and onwards and not from the NULL to READY state. The
|
||||
reason for this is that streaming threads are usually started in the
|
||||
READY to PAUSED state and that the current NULL to READY state change is
|
||||
used to perform a blocking check for the presence of devices.
|
||||
|
||||
The progress message needs to be posted from the state change function
|
||||
so that the application can immediately take appropriate action after
|
||||
setting the state.
|
||||
|
||||
The threads will usually perform many blocking calls with different
|
||||
codes in a row, a client might first do a DNS query and then continue
|
||||
with establishing a connection to the server. For this purpose the
|
||||
`GST_PROGRESS_TYPE_CONTINUE` must be used.
|
||||
|
||||
Usually, the thread used to perform the blocking operations can be used
|
||||
to implement the streaming threads when needed.
|
||||
|
||||
Upon downward state changes, operations that are busy in the thread are
|
||||
canceled and `GST_PROGRESS_TYPE_CANCELED` is posted.
|
||||
|
||||
The application can know about pending tasks because they received the
|
||||
`GST_PROGRESS_TYPE_START` messages that didn’t complete with a
|
||||
`GST_PROGRESS_TYPE_COMPLETE` message, got canceled with a
|
||||
`GST_PROGRESS_TYPE_CANCELED` or errored with
|
||||
`GST_PROGRESS_TYPE_ERROR.` Applications should be able to choose if
|
||||
they wait for the pending operation or cancel them.
|
||||
|
||||
If an async operation fails, an error message is posted first before the
|
||||
`GST_PROGRESS_TYPE_ERROR` progress message.
|
||||
|
||||
## Categories
|
||||
|
||||
We want to propose some standard codes here:
|
||||
|
||||
* "open" : A resource is being opened
|
||||
|
||||
* "close" : A resource is being closed
|
||||
|
||||
* "name-lookup" : A DNS lookup.
|
||||
|
||||
* "connect" : A socket connection is established
|
||||
|
||||
* "disconnect" : a socket connection is closed
|
||||
|
||||
* "request" : A request is sent to a server and we are waiting for a reply.
|
||||
This message is posted right before the request is sent and completed when the
|
||||
reply has arrived completely. * "mount" : A volume is being mounted
|
||||
|
||||
* "unmount" : A volume is being unmounted
|
||||
|
||||
More codes can be posted by elements and can be made official later.
|
43
markdown/design/push-pull.md
Normal file
43
markdown/design/push-pull.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# push-pull
|
||||
|
||||
Normally a source element will push data to the downstream element using
|
||||
the `gst_pad_push()` method. The downstream peer pad will receive the
|
||||
buffer in the Chain function. In the push mode, the source element is
|
||||
the driving force in the pipeline as it initiates data transport.
|
||||
|
||||
It is also possible for an element to pull data from an upstream
|
||||
element. The downstream element does this by calling
|
||||
`gst_pad_pull_range()` on one of its sinkpads. In this mode, the
|
||||
downstream element is the driving force in the pipeline as it initiates
|
||||
data transfer.
|
||||
|
||||
It is important that the elements are in the correct state to handle a
|
||||
push() or a `pull_range()` from the peer element. For push() based
|
||||
elements this means that all downstream elements should be in the
|
||||
correct state and for `pull_range()` based elements this means the
|
||||
upstream elements should be in the correct state.
|
||||
|
||||
Most sinkpads implement a chain function. This is the most common case.
|
||||
sinkpads implementing a loop function will be the exception. Likewise
|
||||
srcpads implementing a getrange function will be the exception.
|
||||
|
||||
## state changes
|
||||
|
||||
The GstBin sets the state of all the sink elements. These are the
|
||||
elements without source pads.
|
||||
|
||||
Setting the state on an element will first activate all the srcpads and
|
||||
then the sinkpads. For each of the sinkpads,
|
||||
`gst_pad_check_pull_range()` is performed. If the sinkpad supports a
|
||||
loopfunction and the peer pad returns TRUE from the GstPadCheckPullRange
|
||||
function, then the peer pad is activated first as it must be in the
|
||||
right state to handle a `_pull_range().` Note that the state change of
|
||||
the element is not yet performed, just the activate function is called
|
||||
on the source pad. This means that elements that implement a getrange
|
||||
function must be prepared to get their activate function called before
|
||||
their state change function.
|
||||
|
||||
Elements that have multiple sinkpads that require all of them to operate
|
||||
in the same mode (push/pull) can use the `_check_pull_range()` on all
|
||||
their pads and can then remove the loop functions if one of the pads
|
||||
does not support pull based mode.
|
445
markdown/design/qos.md
Normal file
445
markdown/design/qos.md
Normal file
|
@ -0,0 +1,445 @@
|
|||
# Quality-of-Service
|
||||
|
||||
Quality of service is about measuring and adjusting the real-time
|
||||
performance of a pipeline.
|
||||
|
||||
The real-time performance is always measured relative to the pipeline
|
||||
clock and typically happens in the sinks when they synchronize buffers
|
||||
against the clock.
|
||||
|
||||
The measurements result in QOS events that aim to adjust the datarate in
|
||||
one or more upstream elements. Two types of adjustments can be made:
|
||||
|
||||
- short time "emergency" corrections based on latest observation in
|
||||
the sinks.
|
||||
|
||||
- long term rate corrections based on trends observed in the sinks.
|
||||
|
||||
It is also possible for the application to artificially introduce delay
|
||||
between synchronized buffers, this is called throttling. It can be used
|
||||
to reduce the framerate, for example.
|
||||
|
||||
## Sources of quality problems
|
||||
|
||||
- High CPU load
|
||||
|
||||
- Network problems
|
||||
|
||||
- Other resource problems such as disk load, memory bottlenecks etc.
|
||||
|
||||
- application level throttling
|
||||
|
||||
## QoS event
|
||||
|
||||
The QoS event is generated by an element that synchronizes against the
|
||||
clock. It travels upstream and contains the following fields:
|
||||
|
||||
* **`type`**: `GST\_TYPE\_QOS\_TYPE:` The type of the QoS event, we have the
|
||||
following types and the default type is `GST\_QOS\_TYPE\_UNDERFLOW`:
|
||||
|
||||
* GST_QOS_TYPE_OVERFLOW: an element is receiving buffers too fast and can't
|
||||
keep up processing them. Upstream should reduce the rate.
|
||||
|
||||
* GST_QOS_TYPE_UNDERFLOW: an element is receiving buffers too slowly
|
||||
and has to drop them because they are too late. Upstream should
|
||||
increase the processing rate.
|
||||
|
||||
* GST_QOS_TYPE_THROTTLE: the application is asking to add extra delay
|
||||
between buffers, upstream is allowed to drop buffers
|
||||
|
||||
* **`timestamp`**: G\_TYPE\_UINT64: The timestamp on the buffer that
|
||||
generated the QoS event. These timestamps are expressed in total
|
||||
running\_time in the sink so that the value is ever increasing.
|
||||
|
||||
* **`jitter`**: G\_TYPE\_INT64: The difference of that timestamp against the
|
||||
current clock time. Negative values mean the timestamp was on time.
|
||||
Positive values indicate the timestamp was late by that amount. When
|
||||
buffers are received in time and throttling is not enabled, the QoS
|
||||
type field is set to OVERFLOW. When throttling, the jitter contains
|
||||
the throttling delay added by the application and the type is set to
|
||||
THROTTLE.
|
||||
|
||||
* **`proportion`**: G\_TYPE\_DOUBLE: Long term prediction of the ideal rate
|
||||
relative to normal rate to get optimal quality.
|
||||
|
||||
The rest of this document deals with how these values can be calculated
|
||||
in a sink and how the values can be used by other elements to adjust
|
||||
their operations.
|
||||
|
||||
## QoS message
|
||||
|
||||
A QOS message is posted on the bus whenever an element decides to:
|
||||
|
||||
- drop a buffer because of QoS reasons
|
||||
|
||||
- change its processing strategy because of QoS reasons (quality)
|
||||
|
||||
It should be expected that creating and posting the QoS message is
|
||||
reasonably fast and does not significantly contribute to the QoS
|
||||
problems. Options to disable this feature could also be presented on
|
||||
elements.
|
||||
|
||||
This message can be posted by a sink/src that performs synchronisation
|
||||
against the clock (live) or it could be posted by an upstream element
|
||||
that performs QoS because of QOS events received from a downstream
|
||||
element (\!live).
|
||||
|
||||
The `GST\_MESSAGE\_QOS` contains at least the following info:
|
||||
|
||||
* **`live`**: G\_TYPE\_BOOLEAN: If the QoS message was dropped by a live
|
||||
element such as a sink or a live source. If the live property is
|
||||
FALSE, the QoS message was generated as a response to a QoS event in
|
||||
a non-live element.
|
||||
|
||||
* **`running-time`**: G\_TYPE\_UINT64: The running\_time of the buffer that
|
||||
generated the QoS message.
|
||||
|
||||
* **`stream-time`**: G\_TYPE\_UINT64: The stream\_time of the buffer that
|
||||
generated the QoS message.
|
||||
|
||||
* **`timestamp`**: G\_TYPE\_UINT64: The timestamp of the buffer that
|
||||
generated the QoS message.
|
||||
|
||||
* **`duration`**: G\_TYPE\_UINT64: The duration of the buffer that generated
|
||||
the QoS message.
|
||||
|
||||
* **`jitter`**: G\_TYPE\_INT64: The difference of the running-time against
|
||||
the deadline. Negative values mean the timestamp was on time.
|
||||
Positive values indicate the timestamp was late (and dropped) by
|
||||
that amount. The deadline can be a realtime running\_time or an
|
||||
estimated running\_time.
|
||||
|
||||
* **`proportion`**: G\_TYPE\_DOUBLE: Long term prediction of the ideal rate
|
||||
relative to normal rate to get optimal quality.
|
||||
|
||||
* **`quality`**: G\_TYPE\_INT: An element dependent integer value that
|
||||
specifies the current quality level of the element. The default
|
||||
maximum quality is 1000000.
|
||||
|
||||
* **`format`**: GST\_TYPE\_FORMAT Units of the *processed* and *dropped*
|
||||
fields. Video sinks and video filters will use GST\_FORMAT\_BUFFERS
|
||||
(frames). Audio sinks and audio filters will likely use
|
||||
GST\_FORMAT\_DEFAULT (samples).
|
||||
|
||||
* **`processed`**: G\_TYPE\_UINT64: Total number of units correctly
|
||||
processed since the last state change to READY or a flushing
|
||||
operation.
|
||||
|
||||
* **`dropped`**: G\_TYPE\_UINT64: Total number of units dropped since the
|
||||
last state change to READY or a flushing operation.
|
||||
|
||||
The *running-time* and *processed* fields can be used to estimate the
|
||||
average processing rate (framerate for video).
|
||||
|
||||
Elements might add additional fields in the message which are documented
|
||||
in the relevant elements or baseclasses.
|
||||
|
||||
## Collecting statistics
|
||||
|
||||
A buffer with timestamp B1 arrives in the sink at time T1. The buffer
|
||||
timestamp is then synchronized against the clock which yields a jitter
|
||||
J1 return value from the clock. The jitter J1 is simply calculated as
|
||||
|
||||
J1 = CT - B1
|
||||
|
||||
Where CT is the clock time when the entry arrives in the sink. This
|
||||
value is calculated inside the clock when we perform
|
||||
`gst\_clock\_id\_wait()`.
|
||||
|
||||
If the jitter is negative, the entry arrived in time and can be rendered
|
||||
after waiting for the clock to reach time B1 (which is also CT - J1).
|
||||
|
||||
If the jitter is positive however, the entry arrived too late in the
|
||||
sink and should therefore be dropped. J1 is the amount of time the entry
|
||||
was late.
|
||||
|
||||
Any buffer that arrives in the sink should generate a QoS event
|
||||
upstream.
|
||||
|
||||
Using the jitter we can calculate the time when the buffer arrived in
|
||||
the sink:
|
||||
|
||||
T1 = B1 + J1. (1)
|
||||
|
||||
The time the buffer leaves the sink after synchronisation is measured
|
||||
as:
|
||||
|
||||
T2 = B1 + (J1 < 0 ? 0 : J1) (2)
|
||||
|
||||
For buffers that arrive in time (J1 \< 0) the buffer leaves after
|
||||
synchronisation which is exactly B1. Late buffers (J1 \>= 0) leave the
|
||||
sink when they arrive, whithout any synchronisation, which is T2 = T1 =
|
||||
B1 + J1.
|
||||
|
||||
Using a previous T0 and a new T1, we can calculate the time it took for
|
||||
upstream to generate a buffer with timestamp B1.
|
||||
|
||||
PT1 = T1 - T0 (3)
|
||||
|
||||
We call PT1 the processing time needed to generate buffer with timestamp
|
||||
B1.
|
||||
|
||||
Moreover, given the duration of the buffer D1, the current data rate
|
||||
(DR1) of the upstream element is given as:
|
||||
|
||||
```
|
||||
PT1 T1 - T0
|
||||
DR1 = --- = ------- (4)
|
||||
D1 D1
|
||||
```
|
||||
|
||||
For values 0.0 \< DR1 ⇐ 1.0 the upstream element is producing faster
|
||||
than real-time. If DR1 is exactly 1.0, the element is running at a
|
||||
perfect speed.
|
||||
|
||||
Values DR1 \> 1.0 mean that the upstream element cannot produce buffers
|
||||
of duration D1 in real-time. It is exactly DR1 that tells the amount of
|
||||
speedup we require from upstream to regain real-time performance.
|
||||
|
||||
An element that is not receiving enough data is said to be underflowed.
|
||||
|
||||
## Element measurements
|
||||
|
||||
In addition to the measurements of the datarate of the upstream element,
|
||||
a typical element must also measure its own performance. Global pipeline
|
||||
performance problems can indeed also be caused by the element itself
|
||||
when it receives too much data it cannot process in time. The element is
|
||||
then said to be overflowed.
|
||||
|
||||
# Short term correction
|
||||
|
||||
The timestamp and jitter serve as short term correction information for
|
||||
upstream elements. Indeed, given arrival time T1 as given in (1) we can
|
||||
be certain that buffers with a timestamp B2 \< T1 will be too late in
|
||||
the sink.
|
||||
|
||||
In case of a positive jitter we can therefore send a QoS event with a
|
||||
timestamp B1, jitter J1 and proportion given by (4).
|
||||
|
||||
This allows an upstream element to not generate any data with timestamps
|
||||
B2 \< T1, where the element can derive T1 as B1 + J1.
|
||||
|
||||
This will effectively result in frame drops.
|
||||
|
||||
The element can even do a better estimation of the next valid timestamp
|
||||
it should output.
|
||||
|
||||
Indeed, given the element generated a buffer with timestamp B0 that
|
||||
arrived in time in the sink but then received a QoS event stating B1
|
||||
arrived J1 too late. This means generating B1 took (B1 + J1) - B0 = T1 -
|
||||
T0 = PT1, as given in (3). Given the buffer B1 had a duration D1 and
|
||||
assuming that generating a new buffer B2 will take the same amount of
|
||||
processing time, a better estimation for B2 would then be:
|
||||
|
||||
```
|
||||
B2 = T1 + D2 * DR1
|
||||
```
|
||||
|
||||
expanding gives:
|
||||
|
||||
```
|
||||
B2 = (B1 + J1) + D2 * (B1 + J1 - B0)
|
||||
--------------
|
||||
D1
|
||||
```
|
||||
|
||||
assuming the durations of the frames are equal and thus D1 = D2:
|
||||
|
||||
```
|
||||
B2 = (B1 + J1) + (B1 + J1 - B0)
|
||||
|
||||
B2 = 2 * (B1 + J1) - B0
|
||||
```
|
||||
|
||||
also:
|
||||
|
||||
```
|
||||
B0 = B1 - D1
|
||||
```
|
||||
|
||||
so:
|
||||
|
||||
```
|
||||
B2 = 2 * (B1 + J1) - (B1 - D1)
|
||||
```
|
||||
|
||||
Which yields a more accurate prediction for the next buffer given as:
|
||||
|
||||
```
|
||||
B2 = B1 + 2 * J1 + D1 (5)
|
||||
```
|
||||
|
||||
# Long term correction
|
||||
|
||||
The datarate used to calculate (5) for the short term prediction is
|
||||
based on a single observation. A more accurate datarate can be obtained
|
||||
by creating a running average over multiple datarate observations.
|
||||
|
||||
This average is less susceptible to sudden changes that would only
|
||||
influence the datarate for a very short period.
|
||||
|
||||
A running average is calculated over the observations given in (4) and
|
||||
is used as the proportion member in the QoS event that is sent upstream.
|
||||
|
||||
Receivers of the QoS event should permanently reduce their datarate as
|
||||
given by the proportion member. Failure to do so will certainly lead to
|
||||
more dropped frames and a generally worse QoS.
|
||||
|
||||
# Throttling
|
||||
|
||||
In throttle mode, the time distance between buffers is kept to a
|
||||
configurable throttle interval. This means that effectively the buffer
|
||||
rate is limited to 1 buffer per throttle interval. This can be used to
|
||||
limit the framerate, for example.
|
||||
|
||||
When an element is configured in throttling mode (this is usually only
|
||||
implemented on sinks) it should produce QoS events upstream with the
|
||||
jitter field set to the throttle interval. This should instruct upstream
|
||||
elements to skip or drop the remaining buffers in the configured
|
||||
throttle interval.
|
||||
|
||||
The proportion field is set to the desired slowdown needed to get the
|
||||
desired throttle interval. Implementations can use the QoS Throttle
|
||||
type, the proportion and the jitter member to tune their
|
||||
implementations.
|
||||
|
||||
# QoS strategies
|
||||
|
||||
Several strategies exist to reduce processing delay that might affect
|
||||
real time performance.
|
||||
|
||||
- lowering quality
|
||||
|
||||
- dropping frames (reduce CPU/bandwidth usage)
|
||||
|
||||
- switch to a lower decoding/encoding quality (reduce algorithmic
|
||||
complexity)
|
||||
|
||||
- switch to a lower quality source (reduce network usage)
|
||||
|
||||
- increasing thread priorities
|
||||
|
||||
- switch to real-time scheduling
|
||||
|
||||
- assign more CPU cycles to critial pipeline parts
|
||||
|
||||
- assign more CPU(s) to critical pipeline parts
|
||||
|
||||
# QoS implementations
|
||||
|
||||
Here follows a small overview of how QoS can be implemented in a range
|
||||
of different types of elements.
|
||||
|
||||
# GstBaseSink
|
||||
|
||||
The primary implementor of QoS is GstBaseSink. It will calculate the
|
||||
following values:
|
||||
|
||||
- upstream running average of processing time (5) in stream time.
|
||||
|
||||
- running average of buffer durations.
|
||||
|
||||
- running average of render time (in system time)
|
||||
|
||||
- rendered/dropped buffers
|
||||
|
||||
The processing time and the average buffer durations will be used to
|
||||
calculate a proportion.
|
||||
|
||||
The processing time in system time is compared to render time to decide
|
||||
if the majority of the time is spend upstream or in the sink itself.
|
||||
This value is used to decide overflow or underflow.
|
||||
|
||||
The number of rendered and dropped buffers is used to query stats on the
|
||||
sink.
|
||||
|
||||
A QoS event with the most current values is sent upstream for each
|
||||
buffer that was received by the sink.
|
||||
|
||||
Normally QoS is only enabled for video pipelines. The reason being that
|
||||
drops in audio are more disturbing than dropping video frames. Also
|
||||
video requires in general more processing than audio.
|
||||
|
||||
Normally there is a threshold for when buffers get dropped in a video
|
||||
sink. Frames that arrive 20 milliseconds late are still rendered as it
|
||||
is not noticeable for the human eye.
|
||||
|
||||
A QoS message is posted whenever a (part of a) buffer is dropped.
|
||||
|
||||
In throttle mode, the sink sends QoS event upstream with the timestamp
|
||||
set to the running\_time of the latest buffer and the jitter set to the
|
||||
throttle interval. If the throttled buffer is late, the lateness is
|
||||
subtracted from the throttle interval in order to keep the desired
|
||||
throttle interval.
|
||||
|
||||
# GstBaseTransform
|
||||
|
||||
Transform elements can entirely skip the transform based on the
|
||||
timestamp and jitter values of recent QoS event since these buffers will
|
||||
certainly arrive too late.
|
||||
|
||||
With any intermediate element, the element should measure its
|
||||
performance to decide if it is responsible for the quality problems or
|
||||
any upstream/downstream element.
|
||||
|
||||
some transforms can reduce the complexity of their algorithms. Depending
|
||||
on the algorithm, the changes in quality may have disturbing visual or
|
||||
audible effect that should be avoided.
|
||||
|
||||
A QoS message should be posted when a frame is dropped or when the
|
||||
quality of the filter is reduced. The quality member in the QOS message
|
||||
should reflect the quality setting of the filter.
|
||||
|
||||
# Video Decoders
|
||||
|
||||
A video decoder can, based on the codec in use, decide to not decode
|
||||
intermediate frames. A typical codec can for example skip the decoding
|
||||
of B-frames to reduce the CPU usage and framerate.
|
||||
|
||||
If each frame is independantly decodable, any arbitrary frame can be
|
||||
skipped based on the timestamp and jitter values of the latest QoS
|
||||
event. In addition can the proportion member be used to permanently skip
|
||||
frames.
|
||||
|
||||
It is suggested to adjust the quality field of the QoS message with the
|
||||
expected amount of dropped frames (skipping B and/or P frames). This
|
||||
depends on the particular spacing of B and P frames in the stream. If
|
||||
the quality control would result in half of the frames to be dropped
|
||||
(typical B frame skipping), the quality field would be set to ``1000000 *
|
||||
1/2 = 500000``. If a typical I frame spacing of 18 frames is used,
|
||||
skipping B and P frames would result in 17 dropped frames or 1 decoded
|
||||
frame every 18 frames. The quality member should be set to `1000000 *
|
||||
1/18 = 55555`.
|
||||
|
||||
- skipping B frames: quality = 500000
|
||||
|
||||
- skipping P/B frames: quality = 55555 (for I-frame spacing of 18
|
||||
frames)
|
||||
|
||||
# Demuxers
|
||||
|
||||
Demuxers usually cannot do a lot regarding QoS except for skipping
|
||||
frames to the next keyframe when a lateness QoS event arrives on a
|
||||
source pad.
|
||||
|
||||
A demuxer can however measure if the performance problems are upstream
|
||||
or downstream and forward an updated QoS event upstream.
|
||||
|
||||
Most demuxers that have multiple output pads might need to combine the
|
||||
QoS events on all the pads and derive an aggregated QoS event for the
|
||||
upstream element.
|
||||
|
||||
# Sources
|
||||
|
||||
The QoS events only apply to push based sources since pull based sources
|
||||
are entirely controlled by another downstream element.
|
||||
|
||||
Sources can receive a overflow or underflow event that can be used to
|
||||
switch to less demanding source material. In case of a network stream, a
|
||||
switch could be done to a lower or higher quality stream or additional
|
||||
enhancement layers could be used or ignored.
|
||||
|
||||
Live sources will automatically drop data when it takes too long to
|
||||
process the data that the element pushes out.
|
||||
|
||||
Live sources should post a QoS message when data is dropped.
|
71
markdown/design/query.md
Normal file
71
markdown/design/query.md
Normal file
|
@ -0,0 +1,71 @@
|
|||
# Query
|
||||
|
||||
## Purpose
|
||||
|
||||
Queries are used to get information about the stream. A query is started
|
||||
on a specific pad and travels up or downstream.
|
||||
|
||||
## Requirements
|
||||
|
||||
- multiple return values, grouped together when they make sense.
|
||||
|
||||
- one pad function to perform the query
|
||||
|
||||
- extensible queries.
|
||||
|
||||
## Implementation
|
||||
|
||||
- GstQuery extends GstMiniObject and contains a GstStructure (see
|
||||
GstMessage)
|
||||
|
||||
- some standard query types are defined below
|
||||
|
||||
- methods to create and parse the results in the GstQuery.
|
||||
|
||||
- define pad
|
||||
method:
|
||||
|
||||
``` c
|
||||
gboolean (*GstPadQueryFunction) (GstPad *pad,
|
||||
GstObject *parent,
|
||||
GstQuery *query);
|
||||
```
|
||||
|
||||
pad returns result in query structure and TRUE as result or FALSE when query is
|
||||
not supported.
|
||||
|
||||
## Query types
|
||||
|
||||
**`GST_QUERY_POSITION`**: get info on current position of the stream in stream_time.
|
||||
|
||||
**`GST_QUERY_DURATION`**: get info on the total duration of the stream.
|
||||
|
||||
**`GST_QUERY_LATENCY`**: get amount of latency introduced in the pipeline. (See [latency](design/latency.md))
|
||||
|
||||
**`GST_QUERY_RATE`**: get the current playback rate of the pipeline
|
||||
|
||||
**`GST_QUERY_SEEKING`**: get info on how seeking can be done
|
||||
- getrange, with/without offset/size
|
||||
- ranges where seeking is efficient (for caching network sources)
|
||||
- flags describing seeking behaviour (forward, backward, segments,
|
||||
play backwards, ...)
|
||||
|
||||
**`GST_QUERY_SEGMENT`**: get info about the currently configured playback segment.
|
||||
|
||||
**`GST_QUERY_CONVERT`**: convert format/value to another format/value pair.
|
||||
|
||||
**`GST_QUERY_FORMATS`**: return list of supported formats that can be used for GST_QUERY_CONVERT.
|
||||
|
||||
**`GST_QUERY_BUFFERING`**: query available media for efficient seeking (See [buffering](design/buffering.md))
|
||||
|
||||
**`GST_QUERY_CUSTOM`**: a custom query, the name of the query defines the properties of the query.
|
||||
|
||||
**`GST_QUERY_URI`**: query the uri of the source or sink element
|
||||
|
||||
**`GST_QUERY_ALLOCATION`**: the buffer allocation properties (See [bufferpool](design/bufferpool.md))
|
||||
|
||||
**`GST_QUERY_SCHEDULING`**: the scheduling properties (See [scheduling](design/scheduling.md))
|
||||
|
||||
**`GST_QUERY_ACCEPT_CAPS`**: check if caps are supported (See [negotiation](design/negotiation.md))
|
||||
|
||||
**`GST_QUERY_CAPS`**: get the possible caps (See [negotiation](design/negotiation.md))
|
522
markdown/design/relations.md
Normal file
522
markdown/design/relations.md
Normal file
|
@ -0,0 +1,522 @@
|
|||
# Object relation types
|
||||
|
||||
This document describes the relations between objects that exist in
|
||||
GStreamer. It will also describe the way of handling the relation wrt
|
||||
locking and refcounting.
|
||||
|
||||
## parent-child relation
|
||||
|
||||
```
|
||||
+---------+ +-------+
|
||||
| parent | | child |
|
||||
*--->| *----->| |
|
||||
| F1|<-----* 1|
|
||||
+---------+ +-------+
|
||||
```
|
||||
|
||||
### properties
|
||||
- parent has references to multiple children
|
||||
- child has reference to parent
|
||||
- reference fields protected with LOCK
|
||||
- the reference held by each child to the parent is NOT reflected in
|
||||
the refcount of the parent.
|
||||
- the parent removes the floating flag of the child when taking
|
||||
ownership.
|
||||
- the application has valid reference to parent
|
||||
- creation/destruction requires two unnested locks and 1 refcount.
|
||||
|
||||
### usage in GStreamer
|
||||
* GstBin -> GstElement
|
||||
* GstElement -> GstRealPad
|
||||
|
||||
### lifecycle
|
||||
|
||||
#### object creation
|
||||
|
||||
The application creates two object and holds a pointer
|
||||
to them. The objects are initially FLOATING with a refcount of 1.
|
||||
|
||||
```
|
||||
+---------+ +-------+
|
||||
*--->| parent | *--->| child |
|
||||
| * | | |
|
||||
| F1| | * F1|
|
||||
+---------+ +-------+
|
||||
```
|
||||
|
||||
#### establishing the parent-child relationship
|
||||
|
||||
The application then calls a method on the parent object to take ownership of
|
||||
the child object. The parent performs the following actions:
|
||||
|
||||
```
|
||||
result = _set_parent (child, parent);
|
||||
if (result) {
|
||||
lock (parent);
|
||||
ref_pointer = child;
|
||||
|
||||
1. update other data structures .. unlock (parent);
|
||||
} else {
|
||||
|
||||
2. child had parent ..
|
||||
}
|
||||
```
|
||||
|
||||
the `_set_parent()` method performs the following actions:
|
||||
|
||||
```
|
||||
lock (child);
|
||||
if (child->parent != null) {
|
||||
unlock (child);
|
||||
return false;
|
||||
}
|
||||
if (is_floating (child)) {
|
||||
unset (child, floating);
|
||||
}
|
||||
else {
|
||||
_ref (child);
|
||||
}
|
||||
child->parent = parent;
|
||||
unlock (child);
|
||||
_signal (parent_set, child, parent);
|
||||
return true;
|
||||
```
|
||||
|
||||
The function atomically checks if the child has no parent yet
|
||||
and will set the parent if not. It will also sink the child, meaning
|
||||
all floating references to the child are invalid now as it takes
|
||||
over the refcount of the object.
|
||||
|
||||
Visually:
|
||||
|
||||
after `_set_parent()` returns TRUE:
|
||||
|
||||
```
|
||||
+---------+ +-------+
|
||||
*---->| parent | *-//->| child |
|
||||
| * | | |
|
||||
| F1|<-------------* 1|
|
||||
+---------+ +-------+
|
||||
```
|
||||
|
||||
after parent updates ref_pointer to child.
|
||||
|
||||
```
|
||||
+---------+ +-------+
|
||||
*---->| parent | *-//->| child |
|
||||
| *--------->| |
|
||||
| F1|<---------* 1|
|
||||
+---------+ +-------+
|
||||
```
|
||||
|
||||
- only one parent is able to \_sink the same object because the
|
||||
\_set\_parent() method is atomic.
|
||||
|
||||
- since only one parent is able to \_set\_parent() the object, only
|
||||
one will add a reference to the object.
|
||||
|
||||
- since the parent can hold multiple references to children, we don’t
|
||||
need to lock the parent when locking the child. Many threads can
|
||||
call \_set\_parent() on the children with the same parent, the
|
||||
parent can then add all those to its lists.
|
||||
|
||||
> Note: that the signal is emitted before the parent has added the
|
||||
> element to its internal data structures. This is not a problem
|
||||
> since the parent usually has his own signal to inform the app that
|
||||
> the child was reffed. One possible solution would be to update the
|
||||
> internal structure first and then perform a rollback if the \_set_parent()
|
||||
> failed. This is not a good solution as iterators might grab the
|
||||
> 'half-added' child too soon.
|
||||
|
||||
#### using the parent-child relationship
|
||||
|
||||
- since the initial floating reference to the child object became
|
||||
invalid after giving it to the parent, any reference to a child has
|
||||
at least a refcount \> 1.
|
||||
|
||||
- this means that unreffing a child object cannot decrease the
|
||||
refcount to 0. In fact, only the parent can destroy and dispose the
|
||||
child object.
|
||||
|
||||
- given a reference to the child object, the parent pointer is only
|
||||
valid when holding the child LOCK. Indeed, after unlocking the child
|
||||
LOCK, the parent can unparent the child or the parent could even
|
||||
become disposed. To avoid the parent dispose problem, when obtaining
|
||||
the parent pointer, if should be reffed before releasing the child
|
||||
LOCK.
|
||||
|
||||
* getting a reference to the parent.
|
||||
- a referece is held to the child, so it cannot be disposed.
|
||||
|
||||
``` c
|
||||
LOCK (child);
|
||||
parent = _ref (child->parent);
|
||||
UNLOCK (child);
|
||||
|
||||
.. use parent ..
|
||||
|
||||
_unref (parent);
|
||||
```
|
||||
|
||||
* getting a reference to a child
|
||||
|
||||
- a reference to a child can be obtained by reffing it before adding
|
||||
it to the parent or by querying the parent.
|
||||
|
||||
- when requesting a child from the parent, a reference is held to the
|
||||
parent so it cannot be disposed. The parent will use its internal
|
||||
data structures to locate the child element and will return a
|
||||
reference to it with an incremented refcount. The requester should
|
||||
\_unref() the child after usage.
|
||||
|
||||
* destroying the parent-child relationship
|
||||
|
||||
- only the parent can actively destroy the parent-child relationship
|
||||
this typically happens when a method is called on the parent to
|
||||
release ownership of the child.
|
||||
|
||||
- a child shall never remove itself from the parent.
|
||||
|
||||
- since calling a method on the parent with the child as an argument
|
||||
requires the caller to obtain a valid reference to the child, the
|
||||
child refcount is at least \> 1.
|
||||
|
||||
- the parent will perform the folowing actions:
|
||||
|
||||
``` c
|
||||
LOCK (parent);
|
||||
if (ref_pointer == child) {
|
||||
ref_pointer = NULL;
|
||||
|
||||
..update other data structures ..
|
||||
UNLOCK (parent);
|
||||
|
||||
_unparent (child);
|
||||
} else {
|
||||
UNLOCK (parent);
|
||||
.. not our child ..
|
||||
}
|
||||
```
|
||||
|
||||
The `_unparent()` method performs the following actions:
|
||||
|
||||
``` c
|
||||
LOCK (child);
|
||||
if (child->parent != NULL) {
|
||||
child->parent = NULL;
|
||||
UNLOCK (child);
|
||||
_signal (PARENT_UNSET, child, parent);
|
||||
|
||||
_unref (child);
|
||||
} else {
|
||||
UNLOCK (child);
|
||||
}
|
||||
```
|
||||
|
||||
Since the `_unparent()` method unrefs the child object, it is possible that
|
||||
the child pointer is invalid after this function. If the parent wants to
|
||||
perform other actions on the child (such as signal emmision) it should
|
||||
`_ref()` the child first.
|
||||
|
||||
## single-reffed relation
|
||||
|
||||
```
|
||||
+---------+ +---------+
|
||||
*--->| object1 | *--->| object2 |
|
||||
| *--------->| |
|
||||
| 1| | 2|
|
||||
+---------+ +---------+
|
||||
```
|
||||
|
||||
### properties
|
||||
- one object has a reference to another
|
||||
- reference field protected with LOCK
|
||||
- the reference held by the object is reflected in the refcount of the
|
||||
other object.
|
||||
- typically the other object can be shared among multiple other
|
||||
objects where each ref is counted for in the refcount.
|
||||
- no object has ownership of the other.
|
||||
- either shared state or copy-on-write.
|
||||
- creation/destruction requires one lock and one refcount.
|
||||
|
||||
### usage
|
||||
|
||||
GstRealPad -> GstCaps
|
||||
GstBuffer -> GstCaps
|
||||
GstEvent -> GstCaps
|
||||
GstEvent -> GstObject
|
||||
GstMessage -> GstCaps
|
||||
GstMessage -> GstObject
|
||||
|
||||
### lifecycle
|
||||
|
||||
#### Two objects exist unlinked.
|
||||
|
||||
```
|
||||
+---------+ +---------+
|
||||
*--->| object1 | *--->| object2 |
|
||||
| * | | |
|
||||
| 1| | 1|
|
||||
+---------+ +---------+
|
||||
```
|
||||
|
||||
#### establishing the single-reffed relationship
|
||||
|
||||
The second object is attached to the first one using a method
|
||||
on the first object. The second object is reffed and a pointer
|
||||
is updated in the first object using the following algorithm:
|
||||
|
||||
``` c
|
||||
LOCK (object1);
|
||||
if (object1->pointer)
|
||||
_unref (object1->pointer);
|
||||
object1->pointer = _ref (object2);
|
||||
UNLOCK (object1);
|
||||
```
|
||||
|
||||
After releasing the lock on the first object is is not sure that
|
||||
object2 is still reffed from object1.
|
||||
|
||||
```
|
||||
+---------+ +---------+
|
||||
*--->| object1 | *--->| object2 |
|
||||
| *--------->| |
|
||||
| 1| | 2|
|
||||
+---------+ +---------+
|
||||
```
|
||||
|
||||
#### using the single-reffed relationship
|
||||
|
||||
The only way to access object2 is by holding a ref to it or by
|
||||
getting the reference from object1.
|
||||
Reading the object pointed to by object1 can be done like this:
|
||||
|
||||
``` c
|
||||
LOCK (object1);
|
||||
object2 = object1->pointer;
|
||||
_ref (object2);
|
||||
UNLOCK (object1);
|
||||
|
||||
… use object2 …
|
||||
_unref (object2);
|
||||
```
|
||||
|
||||
Depending on the type of the object, modifications can be done either with
|
||||
copy-on-write or directly into the object.
|
||||
|
||||
Copy on write can practically only be done like this:
|
||||
|
||||
``` c
|
||||
LOCK (object1);
|
||||
object2 = object1->pointer;
|
||||
object2 = _copy_on_write (object2);
|
||||
... make modifications to object2 ...
|
||||
UNLOCK (object1);
|
||||
|
||||
Releasing the lock has only a very small window where the copy_on_write
|
||||
actually does not perform a copy:
|
||||
|
||||
LOCK (object1);
|
||||
object2 = object1->pointer;
|
||||
_ref (object2);
|
||||
UNLOCK (object1);
|
||||
|
||||
/* object2 now has at least 2 refcounts making the next
|
||||
copy-on-write make a real copy, unless some other thread writes
|
||||
another object2 to object1 here … */
|
||||
|
||||
object2 = _copy_on_write (object2);
|
||||
|
||||
/* make modifications to object2 … */
|
||||
|
||||
LOCK (object1);
|
||||
if (object1->pointer != object2) {
|
||||
if (object1->pointer)
|
||||
_unref (object1->pointer);
|
||||
object1->pointer = gst_object_ref (object2);
|
||||
}
|
||||
UNLOCK (object1);
|
||||
```
|
||||
|
||||
#### destroying the single-reffed relationship
|
||||
|
||||
The folowing algorithm removes the single-reffed link between
|
||||
object1 and object2.
|
||||
|
||||
``` c
|
||||
LOCK (object1);
|
||||
_unref (object1->pointer);
|
||||
object1->pointer = NULL;
|
||||
UNLOCK (object1);
|
||||
```
|
||||
|
||||
Which yields the following initial state again:
|
||||
|
||||
```
|
||||
+---------+ +---------+
|
||||
*--->| object1 | *--->| object2 |
|
||||
| * | | |
|
||||
| 1| | 1|
|
||||
+---------+ +---------+
|
||||
```
|
||||
|
||||
## unreffed relation
|
||||
|
||||
```
|
||||
+---------+ +---------+
|
||||
*--->| object1 | *--->| object2 |
|
||||
| *--------->| |
|
||||
| 1|<---------* 1|
|
||||
+---------+ +---------+
|
||||
```
|
||||
|
||||
### properties
|
||||
|
||||
- two objects have references to each other
|
||||
- both objects can only have 1 reference to another object.
|
||||
- reference fields protected with LOCK
|
||||
- the references held by each object are NOT reflected in the refcount
|
||||
of the other object.
|
||||
- no object has ownership of the other.
|
||||
- typically each object is owned by a different parent.
|
||||
- creation/destruction requires two nested locks and no refcounts.
|
||||
|
||||
### usage
|
||||
|
||||
- This type of link is used when the link is less important than the
|
||||
existance of the objects, If one of the objects is disposed, so is
|
||||
the link.
|
||||
|
||||
GstRealPad <-> GstRealPad (srcpad lock taken first)
|
||||
|
||||
### lifecycle
|
||||
|
||||
#### Two objects exist unlinked.
|
||||
|
||||
```
|
||||
+---------+ +---------+
|
||||
*--->| object1 | *--->| object2 |
|
||||
| * | | |
|
||||
| 1| | * 1|
|
||||
+---------+ +---------+
|
||||
```
|
||||
|
||||
#### establishing the unreffed relationship
|
||||
|
||||
Since we need to take two locks, the order in which these locks are
|
||||
taken is very important or we might cause deadlocks. This lock order
|
||||
must be defined for all unreffed relations. In these examples we always
|
||||
lock object1 first and then object2.
|
||||
|
||||
``` c
|
||||
LOCK (object1);
|
||||
LOCK (object2);
|
||||
object2->refpointer = object1;
|
||||
object1->refpointer = object2;
|
||||
UNLOCK (object2);
|
||||
UNLOCK (object1);
|
||||
```
|
||||
|
||||
#### using the unreffed relationship
|
||||
|
||||
Reading requires taking one of the locks and reading the corresponing
|
||||
object. Again we need to ref the object before releasing the lock.
|
||||
|
||||
``` c
|
||||
LOCK (object1);
|
||||
object2 = _ref (object1->refpointer);
|
||||
UNLOCK (object1);
|
||||
|
||||
.. use object2 ..
|
||||
_unref (object2);
|
||||
```
|
||||
|
||||
#### destroying the unreffed relationship
|
||||
|
||||
Because of the lock order we need to be careful when destroying this
|
||||
Relation.
|
||||
|
||||
When only a reference to object1 is held:
|
||||
|
||||
``` c
|
||||
LOCK (object1);
|
||||
LOCK (object2);
|
||||
object1->refpointer->refpointer = NULL;
|
||||
object1->refpointer = NULL;
|
||||
UNLOCK (object2);
|
||||
UNLOCK (object1);
|
||||
```
|
||||
|
||||
When only a reference to object2 is held we need to get a handle to the
|
||||
other object fist so that we can lock it first. There is a window where
|
||||
we need to release all locks and the relation could be invalid. To solve
|
||||
this we check the relation after grabbing both locks and retry if the
|
||||
relation changed.
|
||||
|
||||
``` c
|
||||
retry:
|
||||
LOCK (object2);
|
||||
object1 = _ref (object2->refpointer);
|
||||
UNLOCK (object2);
|
||||
.. things can change here ..
|
||||
LOCK (object1);
|
||||
LOCK (object2);
|
||||
if (object1 == object2->refpointer) {
|
||||
/* relation unchanged */
|
||||
object1->refpointer->refpointer = NULL;
|
||||
object1->refpointer = NULL;
|
||||
}
|
||||
else {
|
||||
/* relation changed.. retry */
|
||||
UNLOCK (object2);
|
||||
UNLOCK (object1);
|
||||
_unref (object1);
|
||||
goto retry;
|
||||
}
|
||||
UNLOCK (object2);
|
||||
UNLOCK (object1);
|
||||
_unref (object1);
|
||||
|
||||
/* When references are held to both objects. Note that it is not possible to
|
||||
get references to both objects with the locks released since when the
|
||||
references are taken and the locks are released, a concurrent update might
|
||||
have changed the link, making the references not point to linked objects. */
|
||||
|
||||
LOCK (object1);
|
||||
LOCK (object2);
|
||||
if (object1->refpointer == object2) {
|
||||
object2->refpointer = NULL;
|
||||
object1->refpointer = NULL;
|
||||
}
|
||||
else {
|
||||
.. objects are not linked ..
|
||||
}
|
||||
UNLOCK (object2);
|
||||
UNLOCK (object1);
|
||||
```
|
||||
|
||||
## double-reffed relation
|
||||
|
||||
```
|
||||
+---------+ +---------+
|
||||
*--->| object1 | *--->| object2 |
|
||||
| *--------->| |
|
||||
| 2|<---------* 2|
|
||||
+---------+ +---------+
|
||||
```
|
||||
|
||||
### properties
|
||||
|
||||
- two objects have references to each other
|
||||
- reference fields protected with LOCK
|
||||
- the references held by each object are reflected in the refcount of
|
||||
the other object.
|
||||
- no object has ownership of the other.
|
||||
- typically each object is owned by a different parent.
|
||||
- creation/destruction requires two locks and two refcounts.
|
||||
|
||||
#### usage
|
||||
|
||||
Not used in GStreamer.
|
||||
|
||||
### lifecycle
|
246
markdown/design/scheduling.md
Normal file
246
markdown/design/scheduling.md
Normal file
|
@ -0,0 +1,246 @@
|
|||
# Scheduling
|
||||
|
||||
The scheduling in GStreamer is based on pads actively pushing
|
||||
(producing) data or pad pulling in data (consuming) from other pads.
|
||||
|
||||
## Pushing
|
||||
|
||||
A pad can produce data and push it to the next pad. A pad that behaves
|
||||
this way exposes a loop function that will be called repeatedly until it
|
||||
returns false. The loop function is allowed to block whenever it wants.
|
||||
When the pad is deactivated the loop function should unblock though.
|
||||
|
||||
A pad operating in the push mode can only produce data to a pad that
|
||||
exposes a chain function. This chain function will be called with the
|
||||
buffer produced by the pushing pad.
|
||||
|
||||
This method of producing data is called the streaming mode since the
|
||||
producer produces a constant stream of data.
|
||||
|
||||
## Pulling
|
||||
|
||||
Pads that operate in pulling mode can only pull data from a pad that
|
||||
exposes the pull\_range function. In this case, the sink pad exposes a
|
||||
loop function that will be called repeatedly until the task is stopped.
|
||||
|
||||
After pulling data from the peer pad, the loop function will typically
|
||||
call the push function to push the result to the peer sinkpad.
|
||||
|
||||
## Deciding the scheduling mode
|
||||
|
||||
When a pad is activated, the \_activate() function is called. The pad
|
||||
can then choose to activate itself in push or pull mode depending on
|
||||
upstream capabilities.
|
||||
|
||||
The GStreamer core will by default activate pads in push mode when there
|
||||
is no activate function for the pad.
|
||||
|
||||
## The chain function
|
||||
|
||||
The chain function will be called when a upstream element performs a
|
||||
\_push() on the pad. The upstream element can be another chain based
|
||||
element or a pushing source.
|
||||
|
||||
## The getrange function
|
||||
|
||||
The getrange function is called when a peer pad performs a
|
||||
\_pull\_range() on the pad. This downstream pad can be a pulling element
|
||||
or another \_pull\_range() based element.
|
||||
|
||||
## Scheduling Query
|
||||
|
||||
A sinkpad can ask the upstream srcpad for its scheduling attributes. It
|
||||
does this with the SCHEDULING query.
|
||||
|
||||
* (out) **`modes`**: G_TYPE_ARRAY (default NULL): an array of GST_TYPE_PAD_MODE enums. Contains all the supported scheduling modes.
|
||||
|
||||
* (out) **`flags`**, GST_TYPE_SCHEDULING_FLAGS (default 0):
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
GST_SCHEDULING_FLAG_SEEKABLE = (1 << 0),
|
||||
GST_SCHEDULING_FLAG_SEQUENTIAL = (1 << 1),
|
||||
GST_SCHEDULING_FLAG_BANDWIDTH_LIMITED = (1 << 2)
|
||||
} GstSchedulingFlags;
|
||||
```
|
||||
|
||||
*
|
||||
* **`_SEEKABLE`**: the offset of a pull operation can be specified, if this
|
||||
flag is false, the offset should be -1,
|
||||
|
||||
* **` _SEQUENTIAL`**: suggest sequential access to the data. If`` _SEEKABLE`` is
|
||||
specified, seeks are allowed but should be avoided. This is common for network
|
||||
streams.
|
||||
|
||||
* **`_BANDWIDTH_LIMITED`**: suggest the element supports buffering data for
|
||||
downstream to cope with bandwidth limitations. If this flag is on the
|
||||
downstream element might ask for more data than necessary for normal playback.
|
||||
This use-case is interesting for on-disk buffering scenarios for instance. Seek
|
||||
operations might be slow as well so downstream elements should take this into
|
||||
consideration.
|
||||
|
||||
* (out) **`minsize`**: G_TYPE_INT (default 1): the suggested minimum size of pull requests
|
||||
* (out) **`maxsize`**: G_TYPE_INT (default -1, unlimited): the suggested maximum size of pull requests
|
||||
* (out) **`align`**: G_TYPE_INT (default 0): the suggested alignment for the pull requests.
|
||||
|
||||
## Plug-in techniques
|
||||
|
||||
### Multi-sink elements
|
||||
|
||||
Elements with multiple sinks can either expose a loop function on each
|
||||
of the pads to actively pull\_range data or they can expose a chain
|
||||
function on each pad.
|
||||
|
||||
Implementing a chain function is usually easy and allows for all
|
||||
possible scheduling methods.
|
||||
|
||||
# Pad select
|
||||
|
||||
If the chain based sink wants to wait for one of the pads to receive a buffer, just
|
||||
implement the action to perform in the chain function. Be aware that the action could
|
||||
be performed in different threads and possibly simultaneously so grab the STREAM_LOCK.
|
||||
|
||||
# Collect pads
|
||||
|
||||
If the chain based sink pads all require one buffer before the element can operate on
|
||||
the data, collect all the buffers in the chain function and perform the action when
|
||||
all chainpads received the buffer.
|
||||
|
||||
In this case you probably also don't want to accept more data on a pad that has a buffer
|
||||
queued. This can easily be done with the following code snippet:
|
||||
|
||||
``` c
|
||||
static GstFlowReturn _chain (GstPad *pad, GstBuffer *buffer)
|
||||
{
|
||||
LOCK (mylock);
|
||||
while (pad->store != NULL) {
|
||||
WAIT (mycond, mylock);
|
||||
}
|
||||
pad->store = buffer;
|
||||
SIGNAL (mycond);
|
||||
UNLOCK (mylock);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static void _pull (GstPad *pad, GstBuffer **buffer)
|
||||
{
|
||||
LOCK (mylock);
|
||||
while (pad->store == NULL) {
|
||||
WAIT (mycond, mylock);
|
||||
}
|
||||
**buffer = pad->store;
|
||||
pad->store = NULL;
|
||||
SIGNAL (mycond);
|
||||
UNLOCK (mylock);
|
||||
}
|
||||
```
|
||||
|
||||
## Cases
|
||||
|
||||
Inside the braces below the pads is stated what function the pad
|
||||
support:
|
||||
|
||||
* l: exposes a loop function, so it can act as a pushing source.
|
||||
* g: exposes a getrange function
|
||||
* c: exposes a chain function
|
||||
|
||||
Following scheduling decisions are made based on the scheduling methods exposed
|
||||
by the pads:
|
||||
|
||||
* (g) - (l): sinkpad will pull data from src
|
||||
* (l) - (c): srcpad actively pushes data to sinkpad
|
||||
* () - (c): srcpad will push data to sinkpad.
|
||||
|
||||
* () - () : not schedulable.
|
||||
* () - (l): not schedulable.
|
||||
* (g) - () : not schedulable.
|
||||
* (g) - (c): not schedulable.
|
||||
* (l) - () : not schedulable.
|
||||
* (l) - (l): not schedulable
|
||||
|
||||
* () - (g): impossible
|
||||
* (g) - (g): impossible.
|
||||
* (l) - (g): impossible
|
||||
* (c) - () : impossible
|
||||
* (c) - (g): impossible
|
||||
* (c) - (l): impossible
|
||||
* (c) - (c): impossible
|
||||
|
||||
```
|
||||
+---------+ +------------+ +-----------+
|
||||
| filesrc | | mp3decoder | | audiosink |
|
||||
| src--sink src--sink |
|
||||
+---------+ +------------+ +-----------+
|
||||
(l-g) (c) () (c)
|
||||
```
|
||||
|
||||
When activating the pads:
|
||||
|
||||
- audiosink has a chain function and the peer pad has no loop
|
||||
function, no scheduling is done.
|
||||
|
||||
- mp3decoder and filesrc expose an (l) - (c) connection, a thread is
|
||||
created to call the srcpad loop function.
|
||||
|
||||
```
|
||||
+---------+ +------------+ +----------+
|
||||
| filesrc | | avidemuxer | | fakesink |
|
||||
| src--sink src--sink |
|
||||
+---------+ +------------+ +----------+
|
||||
(l-g) (l) () (c)
|
||||
```
|
||||
|
||||
- fakesink has a chain function and the peer pad has no loop function,
|
||||
no scheduling is done.
|
||||
|
||||
- avidemuxer and filesrc expose an (g) - (l) connection, a thread is
|
||||
created to call the sinkpad loop function.
|
||||
|
||||
```
|
||||
+---------+ +----------+ +------------+ +----------+
|
||||
| filesrc | | identity | | avidemuxer | | fakesink |
|
||||
| src--sink src--sink src--sink |
|
||||
+---------+ +----------+ +------------+ +----------+
|
||||
(l-g) (c) () (l) () (c)
|
||||
```
|
||||
|
||||
- fakesink has a chain function and the peer pad has no loop function,
|
||||
no scheduling is done.
|
||||
|
||||
- avidemuxer and identity expose no schedulable connection so this
|
||||
pipeline is not schedulable.
|
||||
|
||||
```
|
||||
+---------+ +----------+ +------------+ +----------+
|
||||
| filesrc | | identity | | avidemuxer | | fakesink |
|
||||
| src--sink src--sink src--sink |
|
||||
+---------+ +----------+ +------------+ +----------+
|
||||
(l-g) (c-l) (g) (l) () (c)
|
||||
```
|
||||
|
||||
- fakesink has a chain function and the peer pad has no loop function,
|
||||
no scheduling is done.
|
||||
|
||||
- avidemuxer and identity expose an (g) - (l) connection, a thread is
|
||||
created to call the sinkpad loop function.
|
||||
|
||||
- identity knows the srcpad is getrange based and uses the thread from
|
||||
avidemux to getrange data from filesrc.
|
||||
|
||||
```
|
||||
+---------+ +----------+ +------------+ +----------+
|
||||
| filesrc | | identity | | oggdemuxer | | fakesink |
|
||||
| src--sink src--sink src--sink |
|
||||
+---------+ +----------+ +------------+ +----------+
|
||||
(l-g) (c) () (l-c) () (c)
|
||||
```
|
||||
|
||||
- fakesink has a chain function and the peer pad has no loop function,
|
||||
no scheduling is done.
|
||||
|
||||
- oggdemuxer and identity expose an () - (l-c) connection, oggdemux
|
||||
has to operate in chain mode.
|
||||
|
||||
- identity chan only work chain based and so filesrc creates a thread
|
||||
to push data to identity.
|
229
markdown/design/seeking.md
Normal file
229
markdown/design/seeking.md
Normal file
|
@ -0,0 +1,229 @@
|
|||
# Seeking
|
||||
Seeking in GStreamer means configuring the pipeline for playback of the
|
||||
media between a certain start and stop time, called the playback
|
||||
segment. By default a pipeline will play from position 0 to the total
|
||||
duration of the media at a rate of 1.0.
|
||||
|
||||
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.
|
||||
|
||||
When performing a seek, the start and stop values of the segment can be
|
||||
specified as absolute positions or relative to the currently configured
|
||||
playback segment. Note that it is not possible to seek relative to the
|
||||
current playback position. To seek relative to the current playback
|
||||
position, one must query the position first and then perform an absolute
|
||||
seek to the desired position.
|
||||
|
||||
Feedback of the seek operation can be immediately 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
|
||||
immediately.
|
||||
|
||||
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.
|
||||
|
||||
The seeking can be performed to a nearby key unit or to the exact
|
||||
(estimated) unit in the media (`GST_SEEK_FLAG_KEY_UNIT`). See below
|
||||
for more details on this.
|
||||
|
||||
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. See below for more details on this.
|
||||
|
||||
Non segment seeking will make the pipeline emit EOS when the configured
|
||||
segment has been played.
|
||||
|
||||
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.
|
||||
|
||||
<!-- FIXME # Seeking in push based elements-->
|
||||
|
||||
|
||||
## Generating seeking events
|
||||
|
||||
A seek event is created with `gst_event_new_seek ()`.
|
||||
|
||||
## Seeking variants
|
||||
|
||||
The different kinds of seeking methods and their internal workings are
|
||||
described below.
|
||||
|
||||
### FLUSH seeking
|
||||
|
||||
This is the most common way of performing a seek in a playback
|
||||
application. The application issues a seek on the pipeline and the new
|
||||
media is immediately played after the seek call returns.
|
||||
|
||||
### seeking without FLUSH
|
||||
|
||||
This seek type is typically performed after issuing segment seeks to
|
||||
finish the playback of the pipeline.
|
||||
|
||||
Performing a non-flushing seek in a PAUSED pipeline blocks until the
|
||||
pipeline is set to playing again since all data passing is blocked in
|
||||
the prerolled sinks.
|
||||
|
||||
### segment seeking with FLUSH
|
||||
|
||||
This seek is typically performed when starting seamless looping.
|
||||
|
||||
### segment seeking without FLUSH
|
||||
|
||||
This seek is typically performed when continuing seamless looping.
|
||||
|
||||
Demuxer/parser behaviour and `SEEK_FLAG_KEY_UNIT` and
|
||||
`SEEK_FLAG_ACCURATE`
|
||||
|
||||
This section aims to explain the behaviour expected by an element with
|
||||
regard to the KEY_UNIT and ACCURATE seek flags using the example of a
|
||||
parser or demuxer.
|
||||
|
||||
#### DEFAULT BEHAVIOUR:
|
||||
|
||||
When a seek to a certain position is requested, the demuxer/parser will
|
||||
do two things (ignoring flushing and segment seeks, and simplified for
|
||||
illustration purposes):
|
||||
|
||||
- send a segment event with a new start position
|
||||
|
||||
- start pushing data/buffers again
|
||||
|
||||
To ensure that the data corresponding to the requested seek position can
|
||||
actually be decoded, a demuxer or parser needs to start pushing data
|
||||
from a keyframe/keyunit at or before the requested seek position.
|
||||
|
||||
Unless requested differently (via the KEY_UNIT flag), the start of the
|
||||
segment event should be the requested seek position.
|
||||
|
||||
So by default a demuxer/parser will then start pushing data from
|
||||
position DATA and send a segment event with start position SEG_START,
|
||||
and DATA ⇐ SEG_START.
|
||||
|
||||
If DATA < SEG_START, a well-behaved video decoder will start decoding
|
||||
frames from DATA, but take into account the segment configured by the
|
||||
demuxer via the segment event, and only actually output decoded video
|
||||
frames from SEG_START onwards, dropping all decoded frames that are
|
||||
before the segment start and adjusting the timestamp/duration of the
|
||||
buffer that overlaps the segment start ("clipping"). A
|
||||
not-so-well-behaved video decoder will start decoding frames from DATA
|
||||
and push decoded video frames out starting from position DATA, in which
|
||||
case the frames that are before the configured segment start will
|
||||
usually be dropped/clipped downstream (e.g. by the video sink).
|
||||
|
||||
#### GST_SEEK_FLAG_KEY_UNIT:
|
||||
|
||||
If the KEY_UNIT flag is specified, the demuxer/parser should adjust the
|
||||
segment start to the position of the key frame closest to the requested
|
||||
seek position and then start pushing out data from there. The nearest
|
||||
key frame may be before or after the requested seek position, but many
|
||||
implementations will only look for the closest keyframe before the
|
||||
requested position.
|
||||
|
||||
Most media players and thumbnailers do (and should be doing) KEY_UNIT
|
||||
seeks by default, for performance reasons, to ensure almost-instant
|
||||
responsiveness when scrubbing (dragging the seek slider in PAUSED or
|
||||
PLAYING mode). This works well for most media, but results in suboptimal
|
||||
behaviour for a small number of *odd* files (e.g. files that only have
|
||||
one keyframe at the very beginning, or only a few keyframes throughout
|
||||
the entire stream). At the time of writing, a solution for this still
|
||||
needs to be found, but could be implemented demuxer/parser-side, e.g.
|
||||
make demuxers/parsers ignore the KEY_UNIT flag if the position
|
||||
adjustment would be larger than 1/10th of the duration or somesuch.
|
||||
|
||||
Flags can be used to influence snapping direction for those cases where
|
||||
it matters. SNAP_BEFORE will select the preceding position to the seek
|
||||
target, and SNAP_AFTER will select the following one. If both flags are
|
||||
set, the nearest one to the seek target will be used. If none of these
|
||||
flags are set, the seeking implemention is free to select whichever it
|
||||
wants.
|
||||
|
||||
#### Summary:
|
||||
|
||||
- if the KEY_UNIT flag is **not** specified, the demuxer/parser
|
||||
should start pushing data from a key unit preceding the seek
|
||||
position (or from the seek position if that falls on a key unit),
|
||||
and the start of the new segment should be the requested seek
|
||||
position.
|
||||
|
||||
- if the KEY_UNIT flag is specified, the demuxer/parser should start
|
||||
pushing data from the key unit nearest the seek position (or from
|
||||
the seek position if that falls on a key unit), and the start of the
|
||||
new segment should be adjusted to the position of that key unit
|
||||
which was nearest the requested seek position (ie. the new segment
|
||||
start should be the position from which data is pushed).
|
||||
|
||||
### GST_SEEK_FLAG_ACCURATE:
|
||||
|
||||
If the ACCURATE flag is specified in a seek request, the demuxer/parser
|
||||
is asked to do whatever it takes (!) to make sure that the position
|
||||
seeked to is accurate in relation to the beginning of the stream. This
|
||||
means that it is not acceptable to just approximate the position (e.g.
|
||||
using an average bitrate). The achieved position must be exact. In the
|
||||
worst case, the demuxer or parser needs to push data from the beginning
|
||||
of the file and let downstream clip everything before the requested
|
||||
segment start.
|
||||
|
||||
The ACCURATE flag does not affect what the segment start should be in
|
||||
relation to the requested seek position. Only the KEY_UNIT flag (or its
|
||||
absence) has any effect on that.
|
||||
|
||||
Video editors and frame-stepping applications usually use the ACCURATE
|
||||
flag.
|
||||
|
||||
#### Summary:
|
||||
|
||||
- if the ACCURATE flag is **not** specified, it is up to the
|
||||
demuxer/parser to decide how exact the seek should be. If the flag
|
||||
is not specified, the expectation is that the demuxer/parser does a
|
||||
resonable best effort attempt, trading speed for accuracy. In the
|
||||
absence of an index, the seek position may be approximated.
|
||||
|
||||
- if the ACCURATE flag is specified, absolute accuracy is required,
|
||||
and speed is of no concern. It is not acceptable to just approximate
|
||||
the seek position in that case.
|
||||
|
||||
- the ACCURATE flag does not imply that the segment starts at the
|
||||
requested seek position or should be adjusted to the nearest
|
||||
keyframe, only the KEY_UNIT flag determines that.
|
||||
|
||||
### ACCURATE and KEY_UNIT combinations:
|
||||
|
||||
All combinations of these two flags are valid:
|
||||
|
||||
- neither flag specified: segment starts at seek position, send data
|
||||
from preceding key frame (or earlier), feel free to approximate the
|
||||
seek position
|
||||
|
||||
- only KEY_UNIT specified: segment starts from position of nearest
|
||||
keyframe, send data from nearest keyframe, feel free to approximate
|
||||
the seek position
|
||||
|
||||
- only ACCURATE specified: segment starts at seek position, send data
|
||||
from preceding key frame (or earlier), do not approximate the seek
|
||||
position under any circumstances
|
||||
|
||||
- ACCURATE | KEY_UNIT specified: segment starts from position of
|
||||
nearest keyframe, send data from nearest key frame, do not
|
||||
approximate the seek position under any circumstances
|
108
markdown/design/segments.md
Normal file
108
markdown/design/segments.md
Normal file
|
@ -0,0 +1,108 @@
|
|||
# Segments
|
||||
|
||||
A segment in GStreamer denotes a set of media samples that must be
|
||||
processed. A segment has a start time, a stop time and a processing
|
||||
rate.
|
||||
|
||||
A media stream has a start and a stop time. The start time is always 0
|
||||
and the stop time is the total duration (or -1 if unknown, for example a
|
||||
live stream). We call this the complete media stream.
|
||||
|
||||
The segment of the complete media stream can be played by issuing a seek
|
||||
on the stream. The seek has a start time, a stop time and a processing
|
||||
rate.
|
||||
|
||||
```
|
||||
complete stream
|
||||
+------------------------------------------------+
|
||||
0 duration
|
||||
segment
|
||||
|--------------------------|
|
||||
start stop
|
||||
```
|
||||
|
||||
The playback of a segment starts with a source or demuxer element
|
||||
pushing a segment event containing the start time, stop time and rate of
|
||||
the segment. The purpose of this segment is to inform downstream
|
||||
elements of the requested segment positions. Some elements might produce
|
||||
buffers that fall outside of the segment and that might therefore be
|
||||
discarded or
|
||||
clipped.
|
||||
|
||||
## Use case: FLUSHING seek
|
||||
|
||||
ex. `filesrc ! avidemux ! videodecoder ! videosink`
|
||||
|
||||
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 FLUSHING. 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 segment with 1 and 5 as start and stop times. The stream_time in
|
||||
the segment is also 1 as this is the position we seek to.
|
||||
|
||||
The video decoder stores these values internally and forwards them to the
|
||||
next downstream element (videosink, which also stores the values)
|
||||
|
||||
Since second 1 does not contain a keyframe, the avi demuxer starts sending
|
||||
data from the previous keyframe which is at timestamp 0.
|
||||
|
||||
The video decoder decodes the keyframe but knows it should not push the
|
||||
video frame yet as it falls outside of the configured segment.
|
||||
|
||||
When the video decoder receives the frame with timestamp 1, it is able to
|
||||
decode this frame as it received and decoded the data up to the previous
|
||||
keyframe. It then continues to decode and push frames with timestamps >= 1.
|
||||
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 segment and aplies the following (simplified) formula:
|
||||
|
||||
```
|
||||
render_time = BUFFER_TIMESTAMP - segment_start + element->base_time
|
||||
```
|
||||
|
||||
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 (simplified):
|
||||
|
||||
```
|
||||
current_position = clock_time - element->base_time + segment_time
|
||||
```
|
||||
|
||||
See [synchronisation](design/synchronisation.md) 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 immediately after the seek and the current_position will be
|
||||
the stream_time of the seek that was performed.
|
||||
|
||||
The stop time is important when the video format contains B frames. The
|
||||
video decoder receives a P frame first, which it can decode but not push yet.
|
||||
When it receives a B frame, it can decode the B frame and push the B frame
|
||||
followed by the previously decoded P frame. If the P frame is outside of the
|
||||
segment, the decoder knows it should not send the P frame.
|
||||
|
||||
Avidemux stops sending data after pushing a frame with timestamp 5 and
|
||||
returns GST_FLOW_EOS from the chain function to make the upstream
|
||||
elements perform the EOS logic.
|
||||
|
||||
## Use case: live stream
|
||||
|
||||
## Use case: segment looping
|
||||
|
||||
Consider the case of a wav file with raw audio.
|
||||
|
||||
```
|
||||
filesrc ! wavparse ! alsasink
|
||||
```
|
||||
|
||||
FIXME!
|
85
markdown/design/seqnums.md
Normal file
85
markdown/design/seqnums.md
Normal file
|
@ -0,0 +1,85 @@
|
|||
# Seqnums (Sequence numbers)
|
||||
|
||||
Seqnums are integers associated to events and messages. They are used to
|
||||
identify a group of events and messages as being part of the same
|
||||
*operation* over the pipeline.
|
||||
|
||||
Whenever a new event or message is created, a seqnum is set into them.
|
||||
This seqnum is created from an ever increasing source (starting from 0
|
||||
and it might wrap around), so each new event and message gets a new and
|
||||
hopefully unique seqnum.
|
||||
|
||||
Suppose an element receives an event A and, as part of the logic of
|
||||
handling the event A, creates a new event B. B should have its seqnum to
|
||||
the same as A, because they are part of the same operation. The same
|
||||
logic applies if this element had to create multiple events or messages,
|
||||
all of those should have the seqnum set to the value on the received
|
||||
event. For example, when a sink element receives an EOS event and
|
||||
creates a new EOS message to post, it should copy the seqnum from the
|
||||
event to the message because the EOS message is a consequence of the EOS
|
||||
event being received.
|
||||
|
||||
Preserving the seqnums accross related events and messages allows the
|
||||
elements and applications to identify a set of events/messages as being
|
||||
part of a single operation on the pipeline. For example, flushes,
|
||||
segments and EOS that are related to a seek event started by the
|
||||
application.
|
||||
|
||||
Seqnums are also useful for elements to discard duplicated events,
|
||||
avoiding handling them again.
|
||||
|
||||
Below are some scenarios as examples of how to handle seqnums when
|
||||
receving events:
|
||||
|
||||
# Forcing EOS on the pipeline
|
||||
|
||||
The application has a pipeline running and does a
|
||||
`gst_element_send_event` to the pipeline with an EOS event. All the
|
||||
sources in the pipeline will have their `send_event` handlers called and
|
||||
will receive the event from the application.
|
||||
|
||||
When handling this event, the sources will push either the same EOS
|
||||
downstream or create their own EOS event and push. In the later case,
|
||||
the source should copy the seqnum from the original EOS to the newly
|
||||
created. This same logic applies to all elements that receive the EOS
|
||||
downstream, either push the same event or, if creating a new one, copy
|
||||
the seqnum.
|
||||
|
||||
When the EOS reaches the sink, it will create an EOS message, copy the
|
||||
seqnum to the message and post to the bus. The application receives the
|
||||
message and can compare the seqnum of the message with the one from the
|
||||
original event sent to the pipeline. If they match, it knows that this
|
||||
EOS message was caused by the event it pushed and not from other reason
|
||||
(input finished or configured segment was over).
|
||||
|
||||
# Seeking
|
||||
|
||||
A seek event sent to the pipeline is forwarded to all sinks in it. Those
|
||||
sinks, then, push the seek event upstream until they reach an element
|
||||
that is capable of handling it. If the element handling the seek has
|
||||
multiple source pads (tipically a demuxer is handling the seek) it might
|
||||
receive the same seek event on all pads. To prevent handling the same
|
||||
seek event multiple times, the seqnum can be used to identify those
|
||||
events as being the same and only handle the first received.
|
||||
|
||||
Also, when handling the seek, the element might push flush-start,
|
||||
flush-stop and a segment event. All those events should have the same
|
||||
seqnum of the seek event received. When this segment is over and an
|
||||
EOS/Segment-done event is going to be pushed, it also should have the
|
||||
same seqnum of the seek that originated the segment to be played.
|
||||
|
||||
Having the same seqnum as the seek on the segment-done or EOS events is
|
||||
important for the application to identify that the segment requested by
|
||||
its seek has finished playing.
|
||||
|
||||
# Questions
|
||||
|
||||
What happens if the application has sent a seek to the pipeline and,
|
||||
while the segment relative to this seek is playing, it sends an EOS
|
||||
event? Should the EOS pushed by the source have the seqnum of the
|
||||
segment or the EOS from the application?
|
||||
|
||||
If the EOS was received from the application before the segment ended,
|
||||
it should have the EOS from the application event. If the segment ends
|
||||
before the application event is received/handled, it should have the
|
||||
seek/segment seqnum.
|
110
markdown/design/sparsestreams.md
Normal file
110
markdown/design/sparsestreams.md
Normal file
|
@ -0,0 +1,110 @@
|
|||
# DRAFT Sparse Streams
|
||||
|
||||
## Introduction
|
||||
|
||||
In 0.8, there was some support for Sparse Streams through the use of
|
||||
FILLER events. These were used to mark gaps between buffers so that
|
||||
downstream elements could know not to expect any more data for that gap.
|
||||
|
||||
In 0.10, segment information conveyed through SEGMENT events can be used
|
||||
for the same purpose.
|
||||
|
||||
In 1.0, there is a GAP event that works in a similar fashion as the
|
||||
FILLER event in 0.8.
|
||||
|
||||
## Use cases
|
||||
|
||||
1) Sub-title streams Sub-title information from muxed formats such as
|
||||
Matroska or MPEG consist of irregular buffers spaced far apart compared
|
||||
to the other streams (audio and video). Since these usually only appear
|
||||
when someone speaks or some other action in the video/audio needs
|
||||
describing, they can be anywhere from 1-2 seconds to several minutes
|
||||
apart. Downstream elements that want to mix sub-titles and video (and muxers)
|
||||
have no way of knowing whether to process a video packet or wait a moment
|
||||
for a corresponding sub-title to be delivered on another pad.
|
||||
|
||||
2) Still frame/menu support In DVDs (and other formats), there are
|
||||
still-frame regions where the current video frame should be retained and
|
||||
no audio played for a period. In DVD, these are described either as a
|
||||
fixed duration, or infinite duration still frame.
|
||||
|
||||
3) Avoiding processing silence from audio generators Imagine a source
|
||||
that from time to time produces empty buffers (silence or blank images).
|
||||
If the pipeline has many elements next, it is better to optimise the
|
||||
obsolete data processing in this case. Examples for such sources are
|
||||
sound-generators (simsyn in gst-buzztard) or a source in a voip
|
||||
application that uses noise-gating (to save bandwith).
|
||||
|
||||
## Details
|
||||
|
||||
### Sub-title streams
|
||||
|
||||
The main requirement here is to avoid stalling the
|
||||
pipeline between sub-title packets, and is effectively updating the
|
||||
minimum-timestamp for that
|
||||
stream.
|
||||
|
||||
A demuxer can do this by sending an 'update' SEGMENT with a new start time
|
||||
to the subtitle pad. For example, every time the SCR in MPEG data
|
||||
advances more than 0.5 seconds, the MPEG demuxer can issue a SEGMENT with
|
||||
(update=TRUE, start=SCR ). Downstream elements can then be aware not to
|
||||
expect any data older than the new start time.
|
||||
|
||||
The same holds true for any element that knows the current position in the
|
||||
stream - once the element knows that there is no more data to be presented
|
||||
until time 'n' it can advance the start time of the current segment to 'n'.
|
||||
|
||||
This technique can also be used, for example, to represent a stream of
|
||||
MIDI events spaced to a clock period. When there is no event present for
|
||||
a clock time, a SEGMENT update can be sent in its place.
|
||||
|
||||
### Still frame/menu support
|
||||
|
||||
Still frames in DVD menus are not the same,
|
||||
in that they do not introduce a gap in the timestamps of the data.
|
||||
Instead, they represent a pause in the presentation of a stream.
|
||||
Correctly performing the wait requires some synchronisation with
|
||||
downstream elements.
|
||||
|
||||
In this scenario, an upstream element that wants to execute a still frame
|
||||
performs the following steps:
|
||||
|
||||
- Send all data before the still frame wait
|
||||
|
||||
- Send a DRAIN event to ensure that all data has been played
|
||||
downstream.
|
||||
|
||||
- wait on the clock for the required duration, possibly interrupting
|
||||
if necessary due to an intervening activity (such as a user
|
||||
navigation)
|
||||
|
||||
- FLUSH the pipeline using a normal flush sequence (FLUSH\_START,
|
||||
chain-lock, FLUSH\_STOP)
|
||||
|
||||
- Send a SEGMENT to restart playback with the next timestamp in the
|
||||
stream.
|
||||
|
||||
The upstream element performing the wait must only do so when in the PLAYING
|
||||
state. During PAUSED, the clock will not be running, and may not even have
|
||||
been distributed to the element yet.
|
||||
|
||||
DRAIN is a new event that will block on a src pad until all data downstream
|
||||
has been played out.
|
||||
|
||||
Flushing after completing the still wait is to ensure that data after the wait
|
||||
is played correctly. Without it, sinks will consider the first buffers
|
||||
(x seconds, where x is the duration of the wait that occurred) to be
|
||||
arriving late at the sink, and they will be discarded instead of played.
|
||||
|
||||
### For audio
|
||||
|
||||
It is the same case as the first one - there is a *gap* in the audio
|
||||
data that needs to be presented, and this can be done by sending a
|
||||
SEGMENT update that moves the start time of the segment to the next
|
||||
timestamp when data will be sent.
|
||||
|
||||
For video, however it is slightly different. Video frames are typically
|
||||
treated at the moment as continuing to be displayed after their indicated
|
||||
duration if no new frame arrives. Here, it is desired to display a blank
|
||||
frame instead, in which case at least one blank frame should be sent before
|
||||
updating the start time of the segment.
|
51
markdown/design/standards.md
Normal file
51
markdown/design/standards.md
Normal file
|
@ -0,0 +1,51 @@
|
|||
# Ownership of dynamic objects
|
||||
|
||||
Any object-oriented system or language that doesn’t have automatic
|
||||
garbage collection has many potential pitfalls as far as the pointers
|
||||
go. Therefore, some standards must be adhered to as far as who owns
|
||||
what.
|
||||
|
||||
## Strings
|
||||
|
||||
Arguments passed into a function are owned by the caller, and the
|
||||
function will make a copy of the string for its own internal use. The
|
||||
string should be const gchar \*. Strings returned from a function are
|
||||
always a copy of the original and should be freed after usage by the
|
||||
caller.
|
||||
|
||||
ex:
|
||||
|
||||
``` c
|
||||
name = gst_element_get_name (element); /* copy of name is made */
|
||||
.. use name ..
|
||||
g_free (name); /* free after usage */
|
||||
```
|
||||
|
||||
## Objects
|
||||
|
||||
Objects passed into a function are owned by the caller, any additional
|
||||
reference held to the object after leaving the function should increase
|
||||
the refcount of that object.
|
||||
|
||||
Objects returned from a function are owned by the caller. This means
|
||||
that the called should \_free() or \_unref() the object after usage.
|
||||
|
||||
ex:
|
||||
|
||||
``` c
|
||||
peer = gst_pad_get_peer (pad); /* peer with increased refcount */
|
||||
if (peer) {
|
||||
.. use peer ..
|
||||
gst_object_unref (GST_OBJECT (peer)); /* unref peer after usage */
|
||||
}
|
||||
```
|
||||
|
||||
## Iterators
|
||||
|
||||
When retrieving multiple objects from an object an iterator should be
|
||||
used. The iterator allows you to access the objects one after another
|
||||
while making sure that the set of objects retrieved remains consistent.
|
||||
|
||||
Each object retrieved from an iterator has its refcount increased or is
|
||||
a copy of the original. In any case the object should be unreffed or
|
||||
freed after usage.
|
404
markdown/design/states.md
Normal file
404
markdown/design/states.md
Normal file
|
@ -0,0 +1,404 @@
|
|||
# States
|
||||
|
||||
Both elements and pads can be in different states. The states of the
|
||||
pads are linked to the state of the element so the design of the states
|
||||
is mainly focused around the element states.
|
||||
|
||||
An element can be in 4 states. NULL, READY, PAUSED and PLAYING. When an
|
||||
element is initially instantiated, it is in the NULL state.
|
||||
|
||||
## State definitions
|
||||
|
||||
- NULL: This is the initial state of an element.
|
||||
|
||||
- READY: The element should be prepared to go to PAUSED.
|
||||
|
||||
- PAUSED: The element should be ready to accept and process data. Sink
|
||||
elements however only accept one buffer and then block.
|
||||
|
||||
- PLAYING: The same as PAUSED except for live sources and sinks. Sinks
|
||||
accept and render data. Live sources produce data.
|
||||
|
||||
We call the sequence NULL→PLAYING an upwards state change and
|
||||
PLAYING→NULL a downwards state change.
|
||||
|
||||
## State transitions
|
||||
|
||||
the following state changes are possible:
|
||||
|
||||
* *NULL -> READY*:
|
||||
- The element must check if the resources it needs are available.
|
||||
Device sinks and sources typically try to probe the device to constrain
|
||||
their caps.
|
||||
- The element opens the device, this is needed if the previous step requires
|
||||
the device to be opened.
|
||||
|
||||
* *READY -> PAUSED*:
|
||||
- The element pads are activated in order to receive data in PAUSED.
|
||||
Streaming threads are started.
|
||||
- Some elements might need to return `ASYNC` and complete the state change
|
||||
when they have enough information. It is a requirement for sinks to
|
||||
return `ASYNC` and complete the state change when they receive the first
|
||||
buffer or EOS event (preroll). Sinks also block the dataflow when in PAUSED.
|
||||
- A pipeline resets the running_time to 0.
|
||||
- Live sources return NO_PREROLL and don't generate data.
|
||||
|
||||
* *PAUSED -> PLAYING*:
|
||||
- Most elements ignore this state change.
|
||||
- The pipeline selects a clock and distributes this to all the children
|
||||
before setting them to PLAYING. This means that it is only allowed to
|
||||
synchronize on the clock in the PLAYING state.
|
||||
- The pipeline uses the clock and the running_time to calculate the base_time.
|
||||
The base_time is distributed to all children when performing the state
|
||||
change.
|
||||
- Sink elements stop blocking on the preroll buffer or event and start
|
||||
rendering the data.
|
||||
- Sinks can post the EOS message in the PLAYING state. It is not allowed to
|
||||
post EOS when not in the PLAYING state.
|
||||
- While streaming in PAUSED or PLAYING elements can create and remove
|
||||
sometimes pads.
|
||||
- Live sources start generating data and return SUCCESS.
|
||||
|
||||
* *PLAYING -> PAUSED*:
|
||||
- Most elements ignore this state change.
|
||||
- The pipeline calculates the running_time based on the last selected clock
|
||||
and the base_time. It stores this information to continue playback when
|
||||
going back to the PLAYING state.
|
||||
- Sinks unblock any clock wait calls.
|
||||
- When a sink does not have a pending buffer to play, it returns `ASYNC` from
|
||||
this state change and completes the state change when it receives a new
|
||||
buffer or an EOS event.
|
||||
- Any queued EOS messages are removed since they will be reposted when going
|
||||
back to the PLAYING state. The EOS messages are queued in GstBins.
|
||||
- Live sources stop generating data and return NO_PREROLL.
|
||||
|
||||
* *PAUSED -> READY*:
|
||||
- Sinks unblock any waits in the preroll.
|
||||
- Elements unblock any waits on devices
|
||||
- Chain or get_range functions return FLUSHING.
|
||||
- The element pads are deactivated so that streaming becomes impossible and
|
||||
all streaming threads are stopped.
|
||||
- The sink forgets all negotiated formats
|
||||
- Elements remove all sometimes pads
|
||||
|
||||
* *READY -> NULL*:
|
||||
- Elements close devices
|
||||
- Elements reset any internal state.
|
||||
|
||||
## State variables
|
||||
|
||||
An element has 4 state variables that are protected with the object LOCK:
|
||||
|
||||
- *STATE*
|
||||
- *STATE_NEXT*
|
||||
- *STATE_PENDING*
|
||||
- *STATE_RETURN*
|
||||
|
||||
The STATE always reflects the current state of the element. The
|
||||
STATE\_NEXT reflects the next state the element will go to. The
|
||||
STATE\_PENDING always reflects the required state of the element. The
|
||||
STATE\_RETURN reflects the last return value of a state change.
|
||||
|
||||
The STATE\_NEXT and STATE\_PENDING can be VOID\_PENDING if the element
|
||||
is in the right state.
|
||||
|
||||
An element has a special lock to protect against concurrent invocations
|
||||
of set\_state(), called the STATE\_LOCK.
|
||||
|
||||
## Setting state on elements
|
||||
|
||||
The state of an element can be changed with \_element\_set\_state().
|
||||
When changing the state of an element all intermediate states will also
|
||||
be set on the element until the final desired state is set.
|
||||
|
||||
The `set\_state()` function can return 3 possible values:
|
||||
|
||||
* *GST_STATE_FAILURE*: The state change failed for some reason. The plugin should
|
||||
have posted an error message on the bus with information.
|
||||
|
||||
* *GST_STATE_SUCCESS*: The state change is completed successfully.
|
||||
|
||||
* *GST_STATE_ASYNC*: The state change will complete later on. This can happen
|
||||
when the element needs a long time to perform the state change or for sinks
|
||||
that need to receive the first buffer before they can complete the state change
|
||||
(preroll).
|
||||
|
||||
* *GST_STATE_NO_PREROLL*: The state change is completed successfully but the
|
||||
element will not be able to produce data in the PAUSED state.
|
||||
|
||||
In the case of an `ASYNC` state change, it is possible to proceed to the
|
||||
next state before the current state change completed, however, the
|
||||
element will only get to this next state before completing the previous
|
||||
`ASYNC` state change. After receiving an `ASYNC` return value, you can use
|
||||
`element\_get\_state()` to poll the status of the element. If the
|
||||
polling returns `SUCCESS`, the element completed the state change to the
|
||||
last requested state with `set\_state()`.
|
||||
|
||||
When setting the state of an element, the STATE\_PENDING is set to the
|
||||
required state. Then the state change function of the element is called
|
||||
and the result of that function is used to update the STATE and
|
||||
STATE\_RETURN fields, STATE\_NEXT, STATE\_PENDING and STATE\_RETURN
|
||||
fields. If the function returned `ASYNC`, this result is immediately
|
||||
returned to the caller.
|
||||
|
||||
## Getting state of elements
|
||||
|
||||
The get\_state() function takes 3 arguments, two pointers that will
|
||||
hold the current and pending state and one GstClockTime that holds a
|
||||
timeout value. The function returns a GstElementStateReturn.
|
||||
|
||||
- If the element returned `SUCCESS` to the previous \_set\_state()
|
||||
function, this function will return the last state set on the
|
||||
element and VOID\_PENDING in the pending state value. The function
|
||||
returns GST\_STATE\_SUCCESS.
|
||||
|
||||
- If the element returned NO\_PREROLL to the previous \_set\_state()
|
||||
function, this function will return the last state set on the
|
||||
element and VOID\_PENDING in the pending state value. The function
|
||||
returns GST\_STATE\_NO\_PREROLL.
|
||||
|
||||
- If the element returned FAILURE to the previous \_set\_state() call,
|
||||
this function will return FAILURE with the state set to the current
|
||||
state of the element and the pending state set to the value used in
|
||||
the last call of \_set\_state().
|
||||
|
||||
- If the element returned `ASYNC` to the previous \_set\_state() call,
|
||||
this function will wait for the element to complete its state change
|
||||
up to the amount of time specified in the GstClockTime.
|
||||
|
||||
- If the element does not complete the state change in the
|
||||
specified amount of time, this function will return `ASYNC` with
|
||||
the state set to the current state and the pending state set to
|
||||
the pending state.
|
||||
|
||||
- If the element completes the state change within the specified
|
||||
timeout, this function returns the updated state and
|
||||
VOID\_PENDING as the pending state.
|
||||
|
||||
- If the element aborts the `ASYNC` state change due to an error
|
||||
within the specified timeout, this function returns FAILURE with
|
||||
the state set to last successful state and pending set to the
|
||||
last attempt. The element should also post an error message on
|
||||
the bus with more information about the problem.
|
||||
|
||||
## States in GstBin
|
||||
|
||||
A GstBin manages the state of its children. It does this by propagating
|
||||
the state changes performed on it to all of its children. The
|
||||
\_set\_state() function on a bin will call the \_set\_state() function
|
||||
on all of its children, that are not already in the target state or in a
|
||||
change state to the target state.
|
||||
|
||||
The children are iterated from the sink elements to the source elements.
|
||||
This makes sure that when changing the state of an element, the
|
||||
downstream elements are in the correct state to process the eventual
|
||||
buffers. In the case of a downwards state change, the sink elements will
|
||||
shut down first which makes the upstream elements shut down as well
|
||||
since the \_push() function returns a GST\_FLOW\_FLUSHING error.
|
||||
|
||||
If all the children return `SUCCESS`, the function returns `SUCCESS` as
|
||||
well.
|
||||
|
||||
If one of the children returns FAILURE, the function returns FAILURE as
|
||||
well. In this state it is possible that some elements successfully
|
||||
changed state. The application can check which elements have a changed
|
||||
state, which were in error and which were not affected by iterating the
|
||||
elements and calling \_get\_state() on the elements.
|
||||
|
||||
If after calling the state function on all children, one of the children
|
||||
returned `ASYNC`, the function returns `ASYNC` as well.
|
||||
|
||||
If after calling the state function on all children, one of the children
|
||||
returned NO\_PREROLL, the function returns NO\_PREROLL as well.
|
||||
|
||||
If both NO\_PREROLL and `ASYNC` children are present, NO\_PREROLL is
|
||||
returned.
|
||||
|
||||
The current state of the bin can be retrieved with \_get\_state().
|
||||
|
||||
If the bin is performing an `ASYNC` state change, it will automatically
|
||||
update its current state fields when it receives state messages from the
|
||||
children.
|
||||
|
||||
## Implementing states in elements
|
||||
|
||||
### READY
|
||||
|
||||
## upward state change
|
||||
|
||||
Upward state changes always return `ASYNC` either if the STATE\_PENDING is
|
||||
reached or not.
|
||||
|
||||
Element:
|
||||
|
||||
* A -> B => `SUCCESS`
|
||||
- commit state
|
||||
|
||||
* A -> B => `ASYNC`
|
||||
- no commit state
|
||||
- element commits state `ASYNC`
|
||||
|
||||
* A -> B while `ASYNC`
|
||||
- update STATE_PENDING state
|
||||
- no commit state
|
||||
- no change_state called on element
|
||||
|
||||
Bin:
|
||||
|
||||
* A->B: all elements `SUCCESS`
|
||||
- commit state
|
||||
|
||||
* A->B: some elements `ASYNC`
|
||||
- no commit state
|
||||
- listen for commit messages on bus
|
||||
- for each commit message, poll elements, this happens in another
|
||||
thread.
|
||||
- if no `ASYNC` elements, commit state, continue state change
|
||||
to STATE_PENDING
|
||||
|
||||
## downward state change
|
||||
|
||||
Downward state changes only return `ASYNC` if the final state is ASYNC.
|
||||
This is to make sure that it’s not needed to wait for an element to
|
||||
complete the preroll or other `ASYNC` state changes when one only wants to
|
||||
shut down an element.
|
||||
|
||||
Element:
|
||||
|
||||
A -> B => `SUCCESS`
|
||||
- commit state
|
||||
|
||||
A -> B => `ASYNC` not final state
|
||||
- commit state on behalf of element
|
||||
|
||||
A -> B => `ASYNC` final state
|
||||
- element will commit `ASYNC`
|
||||
|
||||
Bin:
|
||||
|
||||
A -> B -> `SUCCESS`
|
||||
- commit state
|
||||
|
||||
A -> B -> `ASYNC` not final state
|
||||
- commit state on behalf of element, continue state change
|
||||
|
||||
A -> B => `ASYNC` final state
|
||||
- no commit state
|
||||
- listen for commit messages on bus
|
||||
- for each commit message, poll elements
|
||||
- if no `ASYNC` elements, commit state
|
||||
|
||||
## Locking overview (element)
|
||||
|
||||
- Element committing `SUCCESS`
|
||||
|
||||
- STATE\_LOCK is taken in set\_state
|
||||
|
||||
- change state is called if `SUCCESS`, commit state is called
|
||||
|
||||
- commit state calls change\_state to next state change.
|
||||
|
||||
- if final state is reached, stack unwinds and result is returned
|
||||
to set\_state and
|
||||
caller.
|
||||
|
||||
```
|
||||
set_state(element) change_state (element) commit_state
|
||||
|
||||
| | |
|
||||
| | |
|
||||
STATE_LOCK | |
|
||||
| | |
|
||||
|------------------------>| |
|
||||
| | |
|
||||
| | |
|
||||
| | (do state change) |
|
||||
| | |
|
||||
| | |
|
||||
| | if `SUCCESS` |
|
||||
| |---------------------->|
|
||||
| | | post message
|
||||
| | |
|
||||
| |<----------------------| if (!final) change_state (next)
|
||||
| | | else SIGNAL
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
|<------------------------| |
|
||||
| `SUCCESS`
|
||||
|
|
||||
STATE_UNLOCK
|
||||
|
|
||||
`SUCCESS`
|
||||
```
|
||||
|
||||
- Element committing `ASYNC`
|
||||
|
||||
- STATE\_LOCK is taken in set\_state
|
||||
|
||||
- change state is called and returns `ASYNC`
|
||||
|
||||
- `ASYNC` returned to the caller.
|
||||
|
||||
- element takes LOCK in streaming thread.
|
||||
|
||||
- element calls commit\_state in streaming thread.
|
||||
|
||||
- commit state calls change\_state to next state
|
||||
change.
|
||||
|
||||
```
|
||||
set_state(element) change_state (element) stream_thread commit_state (element)
|
||||
|
||||
| | | |
|
||||
| | | |
|
||||
STATE_LOCK | | |
|
||||
| | | |
|
||||
|------------------------>| | |
|
||||
| | | |
|
||||
| | | |
|
||||
| | (start_task) | |
|
||||
| | | |
|
||||
| | STREAM_LOCK |
|
||||
| | |... |
|
||||
|<------------------------| | |
|
||||
| ASYNC STREAM_UNLOCK |
|
||||
STATE_UNLOCK | |
|
||||
| .....sync........ STATE_LOCK |
|
||||
ASYNC |----------------->|
|
||||
| |
|
||||
| |---> post_message()
|
||||
| |---> if (!final) change_state (next)
|
||||
| | else SIGNAL
|
||||
|<-----------------|
|
||||
STATE_UNLOCK
|
||||
|
|
||||
STREAM_LOCK
|
||||
| ...
|
||||
STREAM_UNLOCK
|
||||
```
|
||||
|
||||
## Remarks
|
||||
|
||||
set\_state cannot be called from multiple threads at the same time. The
|
||||
STATE\_LOCK prevents this.
|
||||
|
||||
State variables are protected with the LOCK.
|
||||
|
||||
Calling set\_state while gst\_state is called should unlock the
|
||||
get\_state with an error. The cookie will do that.
|
||||
|
||||
``` c
|
||||
set_state(element)
|
||||
|
||||
STATE_LOCK
|
||||
|
||||
LOCK
|
||||
update current, next, pending state
|
||||
cookie++
|
||||
UNLOCK
|
||||
|
||||
change_state
|
||||
|
||||
STATE_UNLOCK
|
||||
```
|
580
markdown/design/stream-selection.md
Normal file
580
markdown/design/stream-selection.md
Normal file
|
@ -0,0 +1,580 @@
|
|||
# Stream selection
|
||||
|
||||
History
|
||||
```
|
||||
v0.1: Jun 11th 2015
|
||||
Initial Draft
|
||||
v0.2: Sep 18th 2015
|
||||
Update to reflect design changes
|
||||
v1.0: Jun 28th 2016
|
||||
Pre-commit revision
|
||||
```
|
||||
|
||||
This document describes the events and objects involved in stream
|
||||
selection in GStreamer pipelines, elements and applications
|
||||
|
||||
## Background
|
||||
|
||||
This new API is intended to address the use cases described in
|
||||
this section:
|
||||
|
||||
1) As a user/app I want an overview and control of the media streams
|
||||
that can be configured within a pipeline for processing, even
|
||||
when some streams are mutually exclusive or logical constructs only.
|
||||
|
||||
2) The user/app can disable entirely streams it's not interested
|
||||
in so they don't occupy memory or processing power - discarded
|
||||
as early as possible in the pipeline. The user/app can also
|
||||
(re-)enable them at a later time.
|
||||
|
||||
3) If the set of possible stream configurations is changing,
|
||||
the user/app should be aware of the pending change and
|
||||
be able to make configuration choices for the new set of streams,
|
||||
as well as possibly still reconfiguring the old set
|
||||
|
||||
4) Elements that have some other internal mechanism for triggering
|
||||
stream selections (DVD, or maybe some scripted playback
|
||||
playlist) should be able to trigger 'selection' of some particular
|
||||
stream.
|
||||
|
||||
5) Indicate known relationships between streams - for example that
|
||||
2 separate video feeds represent the 2 views of a stereoscopic
|
||||
view, or that certain streams are mutually exclusive.
|
||||
|
||||
> Note: the streams that are "available" are not automatically
|
||||
> the ones active, or present in the pipeline as pads. Think HLS/DASH
|
||||
> alternate streams.
|
||||
|
||||
Use case examples:
|
||||
|
||||
1) Playing an MPEG-TS multi-program stream, we want to tell the
|
||||
app that there are multiple programs that could be extracted
|
||||
from the incoming feed. Further, we want to provide a mechanism
|
||||
for the app to select which program(s) to decode, and once
|
||||
that is known to further tell the app which elementary streams
|
||||
are then available within those program(s) so the app/user can
|
||||
choose which audio track(s) to decode and/or use.
|
||||
|
||||
2) A new PMT arrives for an MPEG-TS stream, due to a codec or
|
||||
channel change. The pipeline will need to reconfigure to
|
||||
play the desired streams from new program. Equally, there
|
||||
may be multiple seconds of content buffered from the old
|
||||
program and it should still be possible to switch (for example)
|
||||
subtitle tracks responsively in the draining out data, as
|
||||
well as selecting which subs track to play from the new feed.
|
||||
This same scenario applies when doing gapless transition to a
|
||||
new source file/URL, except that likely the element providing
|
||||
the list of streams also changes as a new demuxer is installed.
|
||||
|
||||
3) When playing a multi-angle DVD, the DVD Virtual Machine needs to
|
||||
extract 1 angle from the data for presentation. It can publish
|
||||
the available angles as logical streams, even though only one
|
||||
stream can be chosen.
|
||||
|
||||
4) When playing a DVD, the user can make stream selections from the
|
||||
DVD menu to choose audio or sub-picture tracks, or the DVD VM
|
||||
can trigger automatic selections. In addition, the player UI
|
||||
should be able to show which audio/subtitle tracks are available
|
||||
and allow direct selection in a GUI the same as for normal
|
||||
files with subtitle tracks in them.
|
||||
|
||||
5) Playing a SCHC (3DTV) feed, where one view is MPEG-2 and the other
|
||||
is H.264 and they should be combined for 3D presentation, or
|
||||
not bother decoding 1 stream if displaying 2D.
|
||||
(bug https://bugzilla.gnome.org/show_bug.cgi?id=719333)
|
||||
|
||||
FIXME - need some use cases indicating what alternate streams in
|
||||
HLS might require - what are the possibilities?
|
||||
|
||||
## Design Overview
|
||||
|
||||
Stream selection in GStreamer is implemented in several parts:
|
||||
1) Objects describing streams : GstStream
|
||||
2) Objects describing a collection of streams : GstStreamCollection
|
||||
3) Events from the app allowing selection and activation of some streams:
|
||||
GST_EVENT_SELECT_STREAMS
|
||||
4) Messages informing the user/application about the available
|
||||
streams and current status:
|
||||
GST_MESSAGE_STREAM_COLLECTION
|
||||
GST_MESSAGE_STREAMS_SELECTED
|
||||
|
||||
## GstStream objects
|
||||
|
||||
* API: GstStream
|
||||
* API: gst_stream_new(..)
|
||||
* API: gst_stream_get_\*(...)
|
||||
* API: gst_stream_set_\*()
|
||||
* API: gst_event_set_stream(...)
|
||||
* API: gst_event_parse_stream(...)
|
||||
|
||||
GstStream objects are a high-level convenience object containing
|
||||
information regarding a possible data stream that can be exposed by
|
||||
GStreamer elements.
|
||||
|
||||
They are mostly the aggregation of information present in other
|
||||
GStreamer components (STREAM_START, CAPS, TAGS event) but are not
|
||||
tied to the presence of a GstPad, and for some use-cases provide
|
||||
information that the existing components don't provide.
|
||||
|
||||
The various properties of a GstStream object are:
|
||||
- stream_id (from the STREAM_START event)
|
||||
- flags (from the STREAM_START event)
|
||||
- caps
|
||||
- tags
|
||||
- type (high-level type of stream: Audio, Video, Container,...)
|
||||
|
||||
GstStream objects can be subclassed so that they can be re-used by
|
||||
elements already using the notion of stream (which is common for
|
||||
example in demuxers).
|
||||
|
||||
Elements that create GstStream should also set it on the
|
||||
GST_EVENT_STREAM_START event of the relevant pad. This helps
|
||||
downstream elements to have all information in one location.
|
||||
|
||||
## Exposing collections of streams
|
||||
|
||||
* API: GstStreamCollection
|
||||
* API: gst_stream_collection_new(...)
|
||||
* API: gst_stream_collection_add_stream(...)
|
||||
* API: gst_stream_collection_get_size(...)
|
||||
* API: gst_stream_collection_get_stream(...)
|
||||
* API: GST_MESSAGE_STREAM_COLLECTION
|
||||
* API: gst_message_new_stream_collection(...)
|
||||
* API: gst_message_parse_stream_collection(...)
|
||||
* API: GST_EVENT_STREAM_COLLECTION
|
||||
* API: gst_event_new_stream_collection(...)
|
||||
* API: gst_event_parse_stream_collection(...)
|
||||
|
||||
Elements that create new streams (such as demuxers) or can create
|
||||
new streams (like the HLS/DASH alternative streams) can list the
|
||||
streams they can make available with the GstStreamCollection object.
|
||||
|
||||
Other elements that might generate GstStreamCollections are the
|
||||
DVD-VM, which handles internal switching of tracks, or parsebin and
|
||||
decodebin3 when it aggregates and presents multiple internal stream
|
||||
sources as a single configurable collection.
|
||||
|
||||
The GstStreamCollection object is a flat listing of GstStream objects.
|
||||
|
||||
The various properties of a GstStreamCollection are:
|
||||
- 'identifier'
|
||||
- the identifier of the collection (unique name)
|
||||
- Generated from the 'upstream stream id' (or stream ids, plural)
|
||||
- the list of GstStreams in the collection.
|
||||
- (Not implemented) : Flags -
|
||||
For now, the only flag is 'INFORMATIONAL' - used by container parsers to
|
||||
publish information about detected streams without allowing selection of
|
||||
the streams.
|
||||
- (Not implemented yet) : The relationship between the various streams
|
||||
This specifies which streams are exclusive (can not be selected at the
|
||||
same time), are related (such as LINKED_VIEW or ENHANCEMENT), or need to
|
||||
be selected together.
|
||||
|
||||
An element will inform outside components about that collection via:
|
||||
|
||||
* a GST_MESSAGE_STREAM_COLLECTION message on the bus.
|
||||
* a GST_EVENT_STREAM_COLLECTION on each source pads.
|
||||
|
||||
Applications and container bin elements can listen and collect the
|
||||
various stream collections to know the full range of streams
|
||||
available within a bin/pipeline.
|
||||
|
||||
Once posted on the bus, a GstStreamCollection is immutable. It is
|
||||
updated by subsequent messages with a matching identifier.
|
||||
|
||||
If the element that provided the collection goes away, there is no way
|
||||
to know that the streams are no longer valid (without having the
|
||||
user/app track that element). The exception to that is if the bin
|
||||
containing that element (such as parsebin or decodebin3) informs that
|
||||
the next collection is a replacement of the former one.
|
||||
|
||||
The mutual exclusion and relationship lists use stream-ids
|
||||
rather than GstStream references in order to avoid circular
|
||||
referencing problems.
|
||||
|
||||
### Usage from elements
|
||||
|
||||
When a demuxer knows the list of streams it can expose, it
|
||||
creates a new GstStream for each stream it can provide with the
|
||||
appropriate information (stream id, flag, tags, caps, ...).
|
||||
|
||||
The demuxer then creates a GstStreamCollection object in which it
|
||||
will put the list of GstStream it can expose. That collection is
|
||||
then both posted on the bus (via a GST_MESSAGE_COLLECTION) and on
|
||||
each pad (via a GST_EVENT_STREAM_COLLECTION).
|
||||
|
||||
That new collection must be posted on the bus *before* the changes
|
||||
are made available. i.e. before pads corresponding to that selection
|
||||
are added/removed.
|
||||
|
||||
In order to be backwards-compatible and support elements that don't
|
||||
create streams/collection yet, the new 'parsebin' element used by
|
||||
decodebin3 will automatically create those if not provided.
|
||||
|
||||
### Usage from application
|
||||
|
||||
Applications can know what streams are available by listening to the
|
||||
GST_MESSAGE_STREAM_COLLECTION messages posted on the bus.
|
||||
|
||||
The application can list the available streams per-type (such as all
|
||||
the audio streams, or all the video streams) by iterating the
|
||||
streams available in the collection by GST_STREAM_TYPE.
|
||||
|
||||
The application will also be able to use these stream information to
|
||||
decide which streams should be activated or not (see the stream
|
||||
selection event below).
|
||||
|
||||
### Backwards compatibility
|
||||
|
||||
Not all demuxers will create the various GstStream and
|
||||
GstStreamCollection objects. In order to remain backwards
|
||||
compatible, a parent bin (parsebin in decodebin3) will create the
|
||||
GstStream and GstStreamCollection based on the pads being
|
||||
added/removed from an element.
|
||||
|
||||
This allows providing stream listing/selection for any demuxer-like
|
||||
element even if it doesn't implement the GstStreamCollection usage.
|
||||
|
||||
## Stream selection event
|
||||
|
||||
* API: GST_EVENT_SELECT_STREAMS
|
||||
* API: gst_event_new_select_streams(...)
|
||||
* API: gst_event_parse_select_streams(...)
|
||||
|
||||
Stream selection events are generated by the application and
|
||||
sent into the pipeline to configure the streams.
|
||||
|
||||
The event carries:
|
||||
* List of GstStreams to activate - a subset of the GstStreamCollection
|
||||
* (Not implemented) - List of GstStreams to be kept discarded - a
|
||||
subset of streams for which hot-swapping will not be desired,
|
||||
allowing elements (such as decodebin3, demuxers, ...) to not parse or
|
||||
buffer those streams at all.
|
||||
|
||||
### Usage from application
|
||||
|
||||
There are two use-cases where an application needs to specify in a
|
||||
generic fashion which streams it wants in output:
|
||||
|
||||
1) When there are several present streams of which it only wants a
|
||||
subset (such as one audio, one video and one subtitle
|
||||
stream). Those streams are demuxed and present in the pipeline.
|
||||
2) When the stream the user wants require some element to undertake
|
||||
some action to expose that stream in the pipeline (such as
|
||||
DASH/HLS alternative streams).
|
||||
|
||||
From the point of view of the application, those two use-cases are
|
||||
treated identically. The streams are all available through the
|
||||
GstStreamCollection posted on the bus, and it will select a subset.
|
||||
|
||||
The application can select the streams it wants by creating a
|
||||
GST_EVENT_SELECT_STREAMS event with the list of stream-id of the
|
||||
streams it wants. That event is then sent on the pipeline,
|
||||
eventually traveling all the way upstream from each sink.
|
||||
|
||||
In some cases, selecting one stream may trigger the availability of
|
||||
other dependent streams, resulting in new GstStreamCollection
|
||||
messages. This can happen in the case where choosing a different DVB
|
||||
channel would create a new single-program collection.
|
||||
|
||||
### Usage in elements
|
||||
|
||||
Elements that receive the GST_EVENT_SELECT_STREAMS event and that
|
||||
can activate/deactivate streams need to look at the list of
|
||||
stream-id contained in the event and decide if they need to do some
|
||||
action.
|
||||
|
||||
In the standard demuxer case (demuxing and exposing all streams),
|
||||
there is nothing to do by default.
|
||||
|
||||
In decodebin3, activating or deactivating streams is taken care of by
|
||||
linking only the streams present in the event to decoders and output
|
||||
ghostpad.
|
||||
|
||||
In the case of elements that can expose alternate streams that are
|
||||
not present in the pipeline as pads, they will take the appropriate
|
||||
action to add/remove those streams.
|
||||
|
||||
Containers that receive the event should pass it to any elements
|
||||
with no downstream peers, so that streams can be configured during
|
||||
pre-roll before a pipeline is completely linked down to sinks.
|
||||
|
||||
## decodebin3 usage and example
|
||||
|
||||
This is an example of how decodebin3 works by using the
|
||||
above-mentioned objects/events/messages.
|
||||
|
||||
For clarity/completeness, we will consider a mpeg-ts stream that has
|
||||
multiple audio streams. Furthermore that stream might have changes
|
||||
at some point (switching video codec, or adding/removing audio
|
||||
streams).
|
||||
|
||||
### Initial differences
|
||||
|
||||
decodebin3 is different, compared to decodebin2, in the sense that, by
|
||||
default:
|
||||
* it will only expose as output ghost source pads one stream of each
|
||||
type (one audio, one video, ..).
|
||||
* It will only decode the exposed streams
|
||||
|
||||
The multiqueue element is still used and takes in all elementary
|
||||
(non-decoded) streams. If parsers are needed/present they are placed
|
||||
before the multiqueue. This is needed in order for multiqueue to
|
||||
work only with packetized and properly timestamped streams.
|
||||
|
||||
Note that the whole typefinding of streams, and optional depayloading,
|
||||
demuxing and parsing are done in a new 'parsebin' element.
|
||||
|
||||
Just like the current implementation, demuxers will expose all
|
||||
streams present within a program as source pads. They will connect
|
||||
to parsers and multiqueue.
|
||||
|
||||
Initial setup. 1 video stream, 2 audio streams.
|
||||
|
||||
```
|
||||
+---------------------+
|
||||
| parsebin |
|
||||
| --------- | +-------------+
|
||||
| | demux |--[parser]-+-| multiqueue |--[videodec]---[
|
||||
]-+-| |--[parser]-+-| |
|
||||
| | |--[parser]-+-| |--[audiodec]---[
|
||||
| --------- | +-------------+
|
||||
+---------------------+
|
||||
```
|
||||
|
||||
### GstStreamCollection
|
||||
|
||||
When parsing the initial PAT/PMT, the demuxer will:
|
||||
1) create the various GstStream objects for each stream.
|
||||
2) create the GstStreamCollection for that initial PMT
|
||||
3) post the GST_MESSAGE_STREAM_COLLECTION Decodebin will intercept that message
|
||||
and know what the demuxer will be exposing.
|
||||
4) The demuxer creates the various pads and sends the corresponding
|
||||
STREAM_START event (with the same stream-id as the corresponding
|
||||
GstStream objects), CAPS event, and TAGS event.
|
||||
|
||||
* parsebin will add all relevant parsers and expose those streams.
|
||||
|
||||
* Decodebin will be able to correlate, based on STREAM_START event
|
||||
stream-id, what pad corresponds to which stream. It links each stream
|
||||
from parsebin to multiqueue.
|
||||
|
||||
* Decodebin knows all the streams that will be available. Since by
|
||||
default it is configured to only expose a stream of each type, it
|
||||
will pick a stream of each for which it will complete the
|
||||
auto-plugging (finding a decoder and then exposing that stream as a
|
||||
source ghostpad.
|
||||
|
||||
> Note: If the demuxer doesn't create/post the GstStreamCollection,
|
||||
> parsebin will create it on itself, as explained in section 2.3
|
||||
> above.
|
||||
|
||||
### Changing the active selection from the application
|
||||
|
||||
The user wants to change the audio track. The application received
|
||||
the GST_MESSAGE_STREAM_COLLECTION containing the list of available
|
||||
streams. For clarity, we will assume those stream-ids are
|
||||
"video-main", "audio-english" and "audio-french".
|
||||
|
||||
The user prefers to use the french soundtrack (which it knows based
|
||||
on the language tag contained in the GstStream objects).
|
||||
|
||||
The application will create and send a GST_EVENT_SELECT_STREAM event
|
||||
containing the list of streams: "video-main", "audio-french".
|
||||
|
||||
That event gets sent on the pipeline, the sinks send it upstream and
|
||||
eventually reach decodebin.
|
||||
|
||||
Decodebin compares:
|
||||
* The currently active selection ("video-main", "audio-english")
|
||||
* The available stream collection ("video-main", "audio-english",
|
||||
"audio-french")
|
||||
* The list of streams in the event ("video-main", "audio-french")
|
||||
|
||||
Decodebin determines that no change is required for "video-main",
|
||||
but sees that it needs to deactivate "audio-english" and activate
|
||||
"audio-french".
|
||||
|
||||
It unlinks the multiqueue source pad connected to the audiodec. Then
|
||||
it queries audiodec, using the GST_QUERY_ACCEPT_CAPS, whether it can
|
||||
accept as-is the caps from the "audio-french" stream.
|
||||
1) If it does, the multiqueue source pad corresponding to
|
||||
"audio-french" is linked to the decoder.
|
||||
2) If it does not, the existing audio decoder is removed,
|
||||
a new decoder is selected (like during initial
|
||||
auto-plugging), and replaces the old audio decoder element.
|
||||
|
||||
The newly selected stream gets decoded and output through the same
|
||||
pad as the previous audio stream.
|
||||
|
||||
Note:
|
||||
The default behaviour would be to only expose one stream of each
|
||||
type. But nothing prevents decodebin from outputting more/less of
|
||||
each type if the GST_EVENT_SELECT_STREAM event specifies that. This
|
||||
allows covering more use-case than the simple playback one.
|
||||
Such examples could be :
|
||||
* Wanting just a video stream or just an audio stream
|
||||
* Wanting all decoded streams
|
||||
* Wanting all audio streams
|
||||
...
|
||||
|
||||
### Changes coming from upstream
|
||||
|
||||
At some point in time, a PMT change happens. Let's assume a change
|
||||
in video-codec and/or PID.
|
||||
|
||||
The demuxer creates a new GstStream for the changed/new stream,
|
||||
creates a new GstStreamCollection for the updated PMT and posts it.
|
||||
|
||||
Decodebin sees the new GstStreamCollection message.
|
||||
|
||||
The demuxer (and parsebin) then adds and removes pads.
|
||||
1) decodebin will match the new pads to GstStream in the "new"
|
||||
GstStreamCollection the same way it did for the initial pads in
|
||||
section 4.2 above.
|
||||
2) decodebin will see whether the new stream can re-use a multiqueue
|
||||
slot used by a stream of the same type no longer present (it
|
||||
compares the old collection to the new collection).
|
||||
In this case, decodebin sees that the new video stream can re-use
|
||||
the same slot as the previous video stream.
|
||||
3) If the new stream is going to be active by default (in this case
|
||||
it does because we are replacing the only video stream, which was
|
||||
active), it will check whether the caps are compatible with the
|
||||
existing videodec (in the same way it was done for the audio
|
||||
decoder switch in section 4.3).
|
||||
|
||||
Eventually, the stream that switched will be decoded and output
|
||||
through the same pad as the previous video stream in a gapless fashion.
|
||||
|
||||
### Further examples
|
||||
|
||||
##### HLS alternates
|
||||
|
||||
There is a main (multi-bitrate or not) stream with audio and
|
||||
video interleaved in mpeg-ts. The manifest also indicates the
|
||||
presence of alternate language audio-only streams.
|
||||
HLS would expose one collection containing:
|
||||
1) The main A+V CONTAINER stream (mpeg-ts), initially active,
|
||||
downloaded and exposed as a pad
|
||||
2) The alternate A-only streams, initially inactive and not exposed as pads
|
||||
the tsdemux element connected to the first stream will also expose
|
||||
a collection containing
|
||||
1.1) A video stream
|
||||
1.2) An audio stream
|
||||
|
||||
```
|
||||
[ Collection 1 ] [ Collection 2 ]
|
||||
[ (hlsdemux) ] [ (tsdemux) ]
|
||||
[ upstream:nil ] /----[ upstream:main]
|
||||
[ ] / [ ]
|
||||
[ "main" (A+V) ]<-/ [ "video" (V) ] viddec1 : "video"
|
||||
[ "fre" (A) ] [ "eng" (A) ] auddec1 : "eng"
|
||||
[ "kor" (A) ] [ ]
|
||||
```
|
||||
|
||||
The user might want to use the korean audio track instead of the
|
||||
default english one.
|
||||
=> SELECT_STREAMS ("video", "kor")
|
||||
|
||||
1) decodebin3 receives and sends the event further upstream
|
||||
2) tsdemux sees that "video" is part of its current upstream,
|
||||
so adds the corresponding stream-id ("main") to the event
|
||||
and sends it upstream ("main", "video", "kor")
|
||||
3) hlsdemux receives the event
|
||||
=> It activates "kor" in addition to "main"
|
||||
4) The event travels back to decodebin3 which will remember the
|
||||
requested selection. If "kor" is already present it will switch
|
||||
the "eng" stream from the audio decoder to the "kor" stream.
|
||||
If it appears a bit later, it will wait until that "kor" stream
|
||||
is available before switching
|
||||
|
||||
#### multi-program MPEG-TS
|
||||
|
||||
Assuming the case of a mpeg-ts stream which contains multiple
|
||||
programs.
|
||||
There would be three "levels" of collection:
|
||||
1) The collection of programs presents in the stream
|
||||
2) The collection of elementary streams presents in a stream
|
||||
3) The collection of streams decodebin can expose
|
||||
|
||||
Initially tsdemux exposes the first program present (default)
|
||||
|
||||
```
|
||||
[ Collection 1 ] [ Collection 2 ] [ Collection 3 ]
|
||||
[ (tsdemux) ] [ (tsdemux) ] [ (decodebin) ]
|
||||
[ id:Programs ]<-\ [ id:BBC1 ]<-\ [ id:BBC1-decoded ]
|
||||
[ upstream:nil ] \-----[ upstream:Programs] \----[ upstream:BBC1 ]
|
||||
[ ] [ ] [ ]
|
||||
[ "BBC1" (C) ] [ id:"bbcvideo"(V) ] [ id:"bbcvideo"(V)]
|
||||
[ "ITV" (C) ] [ id:"bbcaudio"(A) ] [ id:"bbcaudio"(A)]
|
||||
[ "NBC" (C) ] [ ] [ ]
|
||||
```
|
||||
|
||||
At some point the user wants to switch to ITV (of which we do not
|
||||
know the topology at this point in time. A SELECT_STREAMS event
|
||||
is sent with "ITV" in it and the pointer to the Collection1.
|
||||
1) The event travels up the pipeline until tsdemux receives it
|
||||
and begins the switch.
|
||||
2) tsdemux publishes a new 'Collection 2a/ITV' and marks 'Collection 2/BBC'
|
||||
as replaced.
|
||||
2a) App may send a SELECT_STREAMS event configuring which demuxer output
|
||||
streams should be selected (parsed)
|
||||
3) tsdemux adds/removes pads as needed (flushing pads as it removes them?)
|
||||
4) Decodebin feeds new pad streams through existing parsers/decoders as
|
||||
needed. As data from the new collection arrives out each decoder,
|
||||
decodebin sends new GstStreamCollection messages to the app so it
|
||||
can know that the new streams are now switchable at that level.
|
||||
4a) As new GstStreamCollections are published, the app may override
|
||||
the default decodebin stream selection to expose more/fewer streams.
|
||||
The default is to decode and output 1 stream of each type.
|
||||
|
||||
Final state:
|
||||
|
||||
```
|
||||
[ Collection 1 ] [ Collection 4 ] [ Collection 5 ]
|
||||
[ (tsdemux) ] [ (tsdemux) ] [ (decodebin) ]
|
||||
[ id:Programs ]<-\ [ id:ITV ]<-\ [ id:ITV-decoded ]
|
||||
[ upstream:nil ] \-----[ upstream:Programs] \----[ upstream:ITV ]
|
||||
[ ] [ ] [ ]
|
||||
[ "BBC1" (C) ] [ id:"itvvideo"(V) ] [ id:"itvvideo"(V)]
|
||||
[ "ITV" (C) ] [ id:"itvaudio"(A) ] [ id:"itvaudio"(A)]
|
||||
[ "NBC" (C) ] [ ] [ ]
|
||||
```
|
||||
|
||||
### TODO
|
||||
|
||||
- Add missing implementation
|
||||
|
||||
- Add flags to GstStreamCollection
|
||||
|
||||
- Add mutual-exclusion and relationship API to GstStreamCollection
|
||||
|
||||
- Add helper API to figure out whether a collection is a replacement
|
||||
of another or a completely new one. This will require a more generic
|
||||
system to know whether a certain stream-id is a replacement of
|
||||
another or not.
|
||||
|
||||
### OPEN QUESTIONS
|
||||
|
||||
- Is a FLUSHING flag for stream-selection required or not ? This would
|
||||
make the handler of the SELECT\_STREAMS event send FLUSH START/STOP
|
||||
before switching to the other streams. This is tricky when dealing
|
||||
where situations where we keep some streams and only switch some
|
||||
others. Do we flush all streams ? Do we only flush the new streams,
|
||||
potentially resulting in delay to fully switch ? Furthermore, due to
|
||||
efficient buffering in decodebin3, the switching time has been
|
||||
minimized extensively, to the point where flushing might not bring a
|
||||
noticeable improvement.
|
||||
|
||||
- Store the stream collection in bins/pipelines ? A Bin/Pipeline could
|
||||
store all active collection internally, so that it could be queried
|
||||
later on. This could be useful to then get, on any pipeline, at any
|
||||
point in time, the full list of collections available without having
|
||||
to listen to all COLLECTION messages on the bus. This would require
|
||||
fixing the "is a collection a replacement or not" issue first.
|
||||
|
||||
- When switching to new collections, should decodebin3 make any effort
|
||||
to *map* corresponding streams from the old to new PMT - that is,
|
||||
try and stick to the *english* language audio track, for example?
|
||||
Alternatively, rely on the app to do such smarts with stream-select
|
||||
messages ?
|
106
markdown/design/stream-status.md
Normal file
106
markdown/design/stream-status.md
Normal file
|
@ -0,0 +1,106 @@
|
|||
# Stream Status
|
||||
|
||||
This document describes the design and use cases for the stream status
|
||||
messages.
|
||||
|
||||
STREAM_STATUS messages are posted on the bus when the state of a
|
||||
streaming thread changes. The purpose of this message is to allow the
|
||||
application to interact with the streaming thread properties, such as
|
||||
the thread priority or the threadpool to use.
|
||||
|
||||
We accommodate for the following requirements:
|
||||
|
||||
- Application is informed when a streaming thread is about to be
|
||||
created. It should be possible for the application to suggest a
|
||||
custom GstTaskPool.
|
||||
|
||||
- Application is informed when the status of a streaming thread is
|
||||
changed. This can be interesting for GUI application that want to
|
||||
visualize the status of the streaming threads
|
||||
(playing/paused/stopped)
|
||||
|
||||
- Application is informed when a streaming thread is destroyed.
|
||||
|
||||
We allow for the following scenarios:
|
||||
|
||||
- Elements require a specific (internal) streaming thread to operate
|
||||
or the application can create/specify a thread for the element.
|
||||
|
||||
- Elements allow the application to configure a priority on the
|
||||
threads.
|
||||
|
||||
## Use cases
|
||||
|
||||
- boost the priority of the udp receiver streaming thread
|
||||
|
||||
```
|
||||
.--------. .-------. .------. .-------.
|
||||
| udpsrc | | depay | | adec | | asink |
|
||||
| src->sink src->sink src->sink |
|
||||
'--------' '-------' '------' '-------'
|
||||
```
|
||||
|
||||
- when going from READY to PAUSED state, udpsrc will require a
|
||||
streaming thread for pushing data into the depayloader. It will
|
||||
post a STREAM_STATUS message indicating its requirement for a
|
||||
streaming thread.
|
||||
|
||||
- The application will usually react to the STREAM_STATUS
|
||||
messages with a sync bus handler.
|
||||
|
||||
- The application can configure the GstTask with a custom
|
||||
GstTaskPool to manage the streaming thread or it can ignore the
|
||||
message which will make the element use its default GstTaskPool.
|
||||
|
||||
- The application can react to the ENTER/LEAVE stream status
|
||||
message to configure the thread right before it is
|
||||
started/stopped. This can be used to configure the thread
|
||||
priority.
|
||||
|
||||
- Before the GstTask is changed state (start/pause/stop) a
|
||||
STREAM_STATUS message is posted that can be used by the
|
||||
application to keep track of the running streaming threads.
|
||||
|
||||
## Messages
|
||||
|
||||
The existing STREAM_STATUS message will be further defined and implemented in
|
||||
(selected) elements. The following fields will be contained in the message:
|
||||
|
||||
- **`type`**, GST_TYPE_STREAM_STATUS_TYPE:
|
||||
|
||||
- a set of types to control the lifecycle of the thread:
|
||||
GST_STREAM_STATUS_TYPE_CREATE: a new streaming thread is going
|
||||
to be created. The application has the chance to configure a custom
|
||||
thread. GST_STREAM_STATUS_TYPE_ENTER: the streaming thread is
|
||||
about to enter its loop function for the first time.
|
||||
GST_STREAM_STATUS_TYPE_LEAVE: the streaming thread is about to
|
||||
leave its loop. GST_STREAM_STATUS_TYPE_DESTROY: a streaming
|
||||
thread is destroyed
|
||||
|
||||
- A set of types to control the state of the threads:
|
||||
GST_STREAM_STATUS_TYPE_START: a streaming thread is started
|
||||
GST_STREAM_STATUS_TYPE_PAUSE: a streaming thread is paused
|
||||
GST_STREAM_STATUS_TYPE_STOP: a streaming thread is stopped
|
||||
|
||||
- **`owner`**: GST_TYPE_ELEMENT: The owner element of the thread. The
|
||||
message source will contain the pad (or one of the pads) that will
|
||||
produce data by this thread. If this thread does not produce data on
|
||||
a pad, the message source will contain the owner as well. The idea
|
||||
is that the application should be able to see from the element/pad
|
||||
what function this thread has in the context of the application and
|
||||
configure the thread appropriatly.
|
||||
|
||||
- **`object`**: G_TYPE, GstTask/GThread: A GstTask/GThread controlling
|
||||
this streaming thread.
|
||||
|
||||
- **`flow-return`**: GstFlowReturn: A status code for why the thread state
|
||||
changed. when threads are created and started, this is usually
|
||||
GST_FLOW_OK but when they are stopping it contains the reason code
|
||||
why it stopped.
|
||||
|
||||
- **`reason`**: G_TYPE_STRING: A string describing the reason why the
|
||||
thread started/stopped/paused. Can be NULL if no reason is given.
|
||||
|
||||
## Events
|
||||
|
||||
FIXME
|
82
markdown/design/streams.md
Normal file
82
markdown/design/streams.md
Normal file
|
@ -0,0 +1,82 @@
|
|||
# Streams
|
||||
|
||||
This document describes the objects that are passed from element to
|
||||
element in the streaming thread.
|
||||
|
||||
## Stream objects
|
||||
|
||||
The following objects are to be expected in the streaming thread:
|
||||
|
||||
- events
|
||||
- STREAM_START (START)
|
||||
- SEGMENT (SEGMENT)
|
||||
- EOS * (EOS)
|
||||
- TAG (T)
|
||||
- buffers * (B)
|
||||
|
||||
Objects marked with * need to be synchronised to the clock in sinks and
|
||||
live sources.
|
||||
|
||||
## Typical stream
|
||||
|
||||
A typical stream starts with a stream start event that marks the
|
||||
start of the stream, followed by a segment event that marks the
|
||||
buffer timestamp range. After that buffers are sent one after the
|
||||
other. After the last buffer an EOS marks the end of the stream. No
|
||||
more buffers are to be processed after the EOS event.
|
||||
|
||||
```
|
||||
+-----+-------+ +-++-+ +-+ +---+
|
||||
|START|SEGMENT| |B||B| ... |B| |EOS|
|
||||
+-----+-------+ +-++-+ +-+ +---+
|
||||
```
|
||||
|
||||
1) **`STREAM_START`**
|
||||
- marks the start of a stream; unlike the SEGMENT event, there
|
||||
will be no STREAM_START event after flushing seeks.
|
||||
|
||||
2) **`SEGMENT`**, rate, start/stop, time
|
||||
- marks valid buffer timestamp range (start, stop)
|
||||
- marks stream_time of buffers (time). This is the stream time of buffers
|
||||
with a timestamp of S.start.
|
||||
- marks playback rate (rate). This is the required playback rate.
|
||||
- marks applied rate (applied_rate). This is the already applied playback
|
||||
rate. (See also [trickmodes](design/trickmodes.md))
|
||||
- marks running_time of buffers. This is the time used to synchronize
|
||||
against the clock.
|
||||
|
||||
3) **N buffers**
|
||||
- displayable buffers are between start/stop of the SEGMENT (S). Buffers
|
||||
outside the segment range should be dropped or clipped.
|
||||
|
||||
- running_time:
|
||||
|
||||
```
|
||||
if (S.rate > 0.0)
|
||||
running_time = (B.timestamp - S.start) / ABS (S.rate) + S.base
|
||||
else
|
||||
running_time = (S.stop - B.timestamp) / ABS (S.rate) + S.base
|
||||
```
|
||||
|
||||
- a monotonically increasing value that can be used to synchronize
|
||||
against the clock (See also
|
||||
[synchronisation](design/synchronisation.md)).
|
||||
|
||||
- stream_time:
|
||||
* current position in stream between 0 and duration.
|
||||
|
||||
```
|
||||
stream_time = (B.timestamp - S.start) * ABS (S.applied_rate) + S.time
|
||||
```
|
||||
|
||||
|
||||
4) **`EOS`**
|
||||
- marks the end of data, nothing is to be expected after EOS, elements
|
||||
should refuse more data and return GST_FLOW_EOS. A FLUSH_STOP
|
||||
event clears the EOS state of an element.
|
||||
|
||||
## Elements
|
||||
|
||||
These events are generated typically either by the GstBaseSrc class for
|
||||
sources operating in push mode, or by a parser/demuxer operating in
|
||||
pull-mode and pushing parsed/demuxed data downstream.
|
271
markdown/design/synchronisation.md
Normal file
271
markdown/design/synchronisation.md
Normal file
|
@ -0,0 +1,271 @@
|
|||
# Synchronisation
|
||||
|
||||
This document outlines the techniques used for doing synchronised
|
||||
playback of multiple streams.
|
||||
|
||||
Synchronisation in a GstPipeline is achieved using the following 3
|
||||
components:
|
||||
|
||||
- a GstClock, which is global for all elements in a GstPipeline.
|
||||
|
||||
- Timestamps on a GstBuffer.
|
||||
|
||||
- the SEGMENT event preceding the buffers.
|
||||
|
||||
## A GstClock
|
||||
|
||||
This object provides a counter that represents the current time in
|
||||
nanoseconds. This value is called the absolute\_time.
|
||||
|
||||
Different sources exist for this counter:
|
||||
|
||||
- the system time (with g\_get\_current\_time() and with microsecond
|
||||
accuracy)
|
||||
|
||||
- monotonic time (with g\_get\_monotonic\_time () with microsecond
|
||||
accuracy)
|
||||
|
||||
- an audio device (based on number of samples played)
|
||||
|
||||
- a network source based on packets received + timestamps in those
|
||||
packets (a typical example is an RTP source)
|
||||
|
||||
- …
|
||||
|
||||
In GStreamer any element can provide a GstClock object that can be used
|
||||
in the pipeline. The GstPipeline object will select a clock from all the
|
||||
providers and will distribute it to all other elements (see
|
||||
[gstpipeline](design/gstpipeline.md)).
|
||||
|
||||
A GstClock always counts time upwards and does not necessarily start at
|
||||
0.
|
||||
|
||||
While it is possible, it is not recommended to create a clock derived
|
||||
from the contents of a stream (for example, create a clock from the PCR
|
||||
in an mpeg-ts stream).
|
||||
|
||||
## Running time
|
||||
|
||||
After a pipeline selected a clock it will maintain the running\_time
|
||||
based on the selected clock. This running\_time represents the total
|
||||
time spent in the PLAYING state and is calculated as follows:
|
||||
|
||||
- If the pipeline is NULL/READY, the running\_time is undefined.
|
||||
|
||||
- In PAUSED, the running\_time remains at the time when it was last
|
||||
PAUSED. When the stream is PAUSED for the first time, the
|
||||
running\_time is 0.
|
||||
|
||||
- In PLAYING, the running\_time is the delta between the
|
||||
absolute\_time and the base time. The base time is defined as the
|
||||
absolute\_time minus the running\_time at the time when the pipeline
|
||||
is set to PLAYING.
|
||||
|
||||
- after a flushing seek, the running\_time is set to 0 (see
|
||||
[seeking](design/seeking.md)). This is accomplished by redistributing a new
|
||||
base\_time to the elements that got flushed.
|
||||
|
||||
This algorithm captures the running\_time when the pipeline is set from
|
||||
PLAYING to PAUSED and restores this time based on the current
|
||||
absolute\_time when going back to PLAYING. This allows for both clocks
|
||||
that progress when in the PAUSED state (systemclock) and clocks that
|
||||
don’t (audioclock).
|
||||
|
||||
The clock and pipeline now provide a running\_time to all elements that
|
||||
want to perform synchronisation. Indeed, the running time can be
|
||||
observed in each element (during the PLAYING state) as:
|
||||
|
||||
```
|
||||
C.running_time = absolute_time - base_time
|
||||
```
|
||||
|
||||
We note C.running\_time as the running\_time obtained by looking at the
|
||||
clock. This value is monotonically increasing at the rate of the clock.
|
||||
|
||||
## Timestamps
|
||||
|
||||
The GstBuffer timestamps and the preceding SEGMENT event (See
|
||||
[streams](design/streams.md)) define a transformation of the buffer timestamps to
|
||||
running\_time as follows:
|
||||
|
||||
The following notation is used:
|
||||
|
||||
**B**: GstBuffer
|
||||
- B.timestamp = buffer timestamp (GST_BUFFER_PTS or GST_BUFFER_DTS)
|
||||
|
||||
**S**: SEGMENT event preceding the buffers.
|
||||
- S.start: start field in the SEGMENT event. This is the lowest allowed
|
||||
timestamp.
|
||||
- S.stop: stop field in the SEGMENT event. This is the highers allowed
|
||||
timestamp.
|
||||
- S.rate: rate field of SEGMENT event. This is the playback rate.
|
||||
- S.base: a base time for the time. This is the total elapsed running_time of any
|
||||
previous segments.
|
||||
- S.offset: an offset to apply to S.start or S.stop. This is the amount that
|
||||
has already been elapsed in the segment.
|
||||
|
||||
Valid buffers for synchronisation are those with B.timestamp between
|
||||
S.start and S.stop (after applying the S.offset). All other buffers
|
||||
outside this range should be dropped or clipped to these boundaries (see
|
||||
also [segments](design/segments.md)).
|
||||
|
||||
The following transformation to running_time exist:
|
||||
|
||||
```
|
||||
if (S.rate > 0.0)
|
||||
B.running_time = (B.timestamp - (S.start + S.offset)) / ABS (S.rate) + S.base
|
||||
=>
|
||||
B.timestamp = (B.running_time - S.base) * ABS (S.rate) + S.start + S.offset
|
||||
else
|
||||
B.running_time = ((S.stop - S.offset) - B.timestamp) / ABS (S.rate) + S.base
|
||||
=>
|
||||
B.timestamp = S.stop - S.offset - ((B.running_time - S.base) * ABS (S.rate))
|
||||
```
|
||||
|
||||
We write B.running_time as the running_time obtained from the SEGMENT
|
||||
event and the buffers of that segment.
|
||||
|
||||
The first displayable buffer will yield a value of 0 (since B.timestamp
|
||||
== S.start and S.offset and S.base == 0).
|
||||
|
||||
For S.rate \> 1.0, the timestamps will be scaled down to increase the
|
||||
playback rate. Likewise, a rate between 0.0 and 1.0 will slow down
|
||||
playback.
|
||||
|
||||
For negative rates, timestamps are received stop S.stop to S.start so
|
||||
that the first buffer received will be transformed into B.running\_time
|
||||
of 0 (B.timestamp == S.stop and S.base == 0).
|
||||
|
||||
This makes it so that B.running\_time is always monotonically increasing
|
||||
starting from 0 with both positive and negative rates.
|
||||
|
||||
## Synchronisation
|
||||
|
||||
As we have seen, we can get a running\_time:
|
||||
|
||||
- using the clock and the element’s base\_time with:
|
||||
|
||||
```
|
||||
C.running_time = absolute_time - base_time
|
||||
```
|
||||
|
||||
- using the buffer timestamp and the preceding SEGMENT event as (assuming
|
||||
positive playback rate):
|
||||
|
||||
```
|
||||
B.running_time = (B.timestamp - (S.start + S.offset)) / ABS (S.rate) + S.base
|
||||
```
|
||||
|
||||
We prefix C. and B. before the two running times to note how they were
|
||||
calculated.
|
||||
|
||||
The task of synchronized playback is to make sure that we play a buffer
|
||||
with B.running\_time at the moment when the clock reaches the same
|
||||
C.running\_time.
|
||||
|
||||
Thus the following must hold:
|
||||
|
||||
```
|
||||
B.running_time = C.running_time
|
||||
```
|
||||
|
||||
expaning:
|
||||
|
||||
```
|
||||
B.running_time = absolute_time - base_time
|
||||
```
|
||||
|
||||
or:
|
||||
|
||||
```
|
||||
absolute_time = B.running_time + base_time
|
||||
```
|
||||
|
||||
The absolute\_time when a buffer with B.running\_time should be played
|
||||
is noted with B.sync\_time. Thus:
|
||||
|
||||
```
|
||||
B.sync_time = B.running_time + base_time
|
||||
```
|
||||
|
||||
One then waits for the clock to reach B.sync\_time before rendering the
|
||||
buffer in the sink (See also [clocks](design/clocks.md)).
|
||||
|
||||
For multiple streams this means that buffers with the same running\_time
|
||||
are to be displayed at the same time.
|
||||
|
||||
A demuxer must make sure that the SEGMENT it emits on its output pads
|
||||
yield the same running\_time for buffers that should be played
|
||||
synchronized. This usually means sending the same SEGMENT on all pads
|
||||
and making sure that the synchronized buffers have the same timestamps.
|
||||
|
||||
## Stream time
|
||||
|
||||
The stream time is also known as the position in the stream and is a
|
||||
value between 0 and the total duration of the media file.
|
||||
|
||||
It is the stream time that is used for:
|
||||
|
||||
- report the POSITION query in the pipeline
|
||||
|
||||
- the position used in seek events/queries
|
||||
|
||||
- the position used to synchronize controller values
|
||||
|
||||
Additional fields in the SEGMENT are used:
|
||||
|
||||
- S.time: time field in the SEGMENT event. This the stream-time of
|
||||
S.start
|
||||
|
||||
- S.applied\_rate: The rate already applied to the segment.
|
||||
|
||||
Stream time is calculated using the buffer times and the preceding
|
||||
SEGMENT event as follows:
|
||||
|
||||
```
|
||||
stream_time = (B.timestamp - S.start) * ABS (S.applied_rate) + S.time
|
||||
=> B.timestamp = (stream_time - S.time) / ABS(S.applied_rate) + S.start
|
||||
```
|
||||
|
||||
For negative rates, B.timestamp will go backwards from S.stop to
|
||||
S.start, making the stream time go backwards:
|
||||
|
||||
```
|
||||
stream_time = (S.stop - B.timestamp) * ABS(S.applied_rate) + S.time
|
||||
=> B.timestamp = S.stop - (stream_time - S.time) / ABS(S.applied_rate)
|
||||
```
|
||||
|
||||
In the PLAYING state, it is also possible to use the pipeline clock to
|
||||
derive the current stream\_time.
|
||||
|
||||
Give the two formulas above to match the clock times with buffer
|
||||
timestamps allows us to rewrite the above formula for stream\_time (and
|
||||
for positive rates).
|
||||
|
||||
```
|
||||
C.running_time = absolute_time - base_time
|
||||
B.running_time = (B.timestamp - (S.start + S.offset)) / ABS (S.rate) + S.base
|
||||
|
||||
=>
|
||||
(B.timestamp - (S.start + S.offset)) / ABS (S.rate) + S.base = absolute_time - base_time;
|
||||
|
||||
=>
|
||||
(B.timestamp - (S.start + S.offset)) / ABS (S.rate) = absolute_time - base_time - S.base;
|
||||
|
||||
=>
|
||||
(B.timestamp - (S.start + S.offset)) = (absolute_time - base_time - S.base) * ABS (S.rate)
|
||||
|
||||
=>
|
||||
(B.timestamp - S.start) = S.offset + (absolute_time - base_time - S.base) * ABS (S.rate)
|
||||
|
||||
filling (B.timestamp - S.start) in the above formule for stream time
|
||||
|
||||
=>
|
||||
stream_time = (S.offset + (absolute_time - base_time - S.base) * ABS (S.rate)) * ABS (S.applied_rate) + S.time
|
||||
```
|
||||
|
||||
This last formula is typically used in sinks to report the current
|
||||
position in an accurate and efficient way.
|
||||
|
||||
Note that the stream time is never used for synchronisation against the
|
||||
clock.
|
226
markdown/design/toc.md
Normal file
226
markdown/design/toc.md
Normal file
|
@ -0,0 +1,226 @@
|
|||
# Implementing GstToc support in GStreamer elements
|
||||
|
||||
## General info about GstToc structure
|
||||
|
||||
GstToc introduces a general way to handle chapters within multimedia
|
||||
formats. GstToc can be represented as tree structure with arbitrary
|
||||
hierarchy. Tree item can be either of two types: sequence or
|
||||
alternative. Sequence types acts like a part of the media data, for
|
||||
example audio track in CUE sheet, or part of the movie. Alternative
|
||||
types acts like some kind of selection to process a different version of
|
||||
the media content, for example DVD angles. GstToc has one constraint on
|
||||
the tree structure: it does not allow different entry types on the same
|
||||
level of the hierarchy, i.e. you shouldn’t have editions and chapters
|
||||
mixed together. Here is an example of right TOC:
|
||||
|
||||
```
|
||||
------- TOC -------
|
||||
/ \
|
||||
edition1 edition2
|
||||
| |
|
||||
-chapter1 -chapter3
|
||||
-chapter2
|
||||
```
|
||||
|
||||
Here are two editions (alternatives), the first contains two chapters
|
||||
(sequence type), and the second has only one chapter. And here is an
|
||||
example of invalid TOC:
|
||||
|
||||
```
|
||||
------- TOC -------
|
||||
/ \
|
||||
edition1 chapter1
|
||||
|
|
||||
-chapter1
|
||||
-chapter2
|
||||
```
|
||||
|
||||
Here you have edition1 and chapter1 mixed on the same level of
|
||||
hierarchy, and such TOC will be considered broken.
|
||||
|
||||
GstToc has *entries* field of GList type which consists of children
|
||||
items. Each item is of type GstTocEntry. Also GstToc has list of tags
|
||||
and GstStructure called *info*. Please, use GstToc.info and
|
||||
GstTocEntry.info fields this way: create a GstStructure, put all info
|
||||
related to your element there and put this structure into the *info*
|
||||
field under the name of your element. Some fields in the *info*
|
||||
structure can be used for internal purposes, so you should use it in the
|
||||
way described above to not to overwrite already existent fields.
|
||||
|
||||
Let’s look at GstTocEntry a bit closer. One of the most important fields
|
||||
is *uid*, which must be unique for each item within the TOC. This is
|
||||
used to identify each item inside TOC, especially when element receives
|
||||
TOC select event with UID to seek on. Field *subentries* of type GList
|
||||
contains children items of type GstTocEntry. Thus you can achieve
|
||||
arbitrary hierarchy level. Field *type* can be either
|
||||
GST\_TOC\_ENTRY\_TYPE\_CHAPTER or GST\_TOC\_ENTRY\_TYPE\_EDITION which
|
||||
corresponds to chapter or edition type of item respectively. Field
|
||||
*tags* is a list of tags related to the item. And field *info* is
|
||||
similar to GstToc.info described above.
|
||||
|
||||
So, a little more about managing GstToc. Use gst\_toc\_new() and
|
||||
gst\_toc\_unref() to create/free it. GstTocEntry can be created using
|
||||
gst\_toc\_entry\_new(). While building GstToc you can set start and stop
|
||||
timestamps for each item using gst\_toc\_entry\_set\_start\_stop() and
|
||||
loop\_type and repeat\_count using gst\_toc\_entry\_set\_loop(). The
|
||||
best way to process already created GstToc is to recursively go through
|
||||
the *entries* and *subentries* fields.
|
||||
|
||||
Applications and plugins should not rely on TOCs having a certain kind
|
||||
of structure, but should allow for different alternatives. For example,
|
||||
a simple CUE sheet embedded in a file may be presented as a flat list of
|
||||
track entries, or could have a top-level edition node (or some other
|
||||
alternative type entry) with track entries underneath that node; or even
|
||||
multiple top-level edition nodes (or some other alternative type
|
||||
entries) each with track entries underneath, in case the source file has
|
||||
extracted a track listing from different sources).
|
||||
|
||||
## TOC scope: global and current
|
||||
|
||||
There are two main consumers for TOC information: applications and
|
||||
elements in the pipeline that are TOC writers (such as e.g.
|
||||
matroskamux).
|
||||
|
||||
Applications typically want to know the entire table of contents (TOC)
|
||||
with all entries that can possibly be selected.
|
||||
|
||||
TOC writers in the pipeline, however, would not want to write a TOC for
|
||||
all possible/available streams, but only for the current stream.
|
||||
|
||||
When transcoding a title from a DVD, for example, the application would
|
||||
still want to know the entire TOC, with all titles, the chapters for
|
||||
each title, and the available angles. When transcoding to a file, we
|
||||
only want the TOC information that is relevant to the transcoded stream
|
||||
to be written into the file structure, e.g. the chapters of the title
|
||||
being transcoded (or possibly only chapters 5-7 if only those have been
|
||||
selected for playback/ transcoding).
|
||||
|
||||
This is why we may need to create two different TOCs for those two types
|
||||
of consumers.
|
||||
|
||||
Elements that extract TOC information should send TOC events downstream.
|
||||
|
||||
Like with tags, sinks will post a TOC message on the bus for the
|
||||
application with the global TOC, once a global TOC event reaches the
|
||||
sink.
|
||||
|
||||
## Working with GstMessage
|
||||
|
||||
If a table of contents is available, applications will receive a TOC
|
||||
message on the pipeline’s GstBus.
|
||||
|
||||
A TOC message will be posted on the bus by sinks when the receive a TOC
|
||||
event containing a TOC with global scope. Elements extracting TOCs
|
||||
should not post a TOC message themselves, but send a TOC event
|
||||
downstream.
|
||||
|
||||
The reason for this is that there may be cascades of TOCs (e.g. a zip
|
||||
archive containing multiple matroska files, each with a TOC).
|
||||
|
||||
GstMessage with GstToc can be created using gst\_message\_new\_toc() and
|
||||
parsed with gst\_message\_parse\_toc(). The *updated* parameter in these
|
||||
methods indicates whether the TOC was just discovered (set to false) or
|
||||
TOC was already found and have been updated (set to true). This message
|
||||
will typically be posted by sinks to pipeline in case you have
|
||||
discovered TOC data within your element.
|
||||
|
||||
## Working with GstEvent
|
||||
|
||||
There are two types of TOC-related events:
|
||||
|
||||
- downstream TOC events that contain TOC information and travel
|
||||
downstream
|
||||
|
||||
- toc-select events that travel upstream and can be used to select a
|
||||
certain TOC entry for playback (similar to seek events)
|
||||
|
||||
GstToc supports select event through GstEvent infrastructure. The idea
|
||||
is the following: when you receive TOC select event, parse it with
|
||||
gst\_event\_parse\_toc\_select() and seek stream (if it is not
|
||||
streamable) for specified TOC UID (you can use gst\_toc\_find\_entry()
|
||||
to find entry in TOC by UID). To create TOC select event use
|
||||
gst\_event\_new\_toc\_select(). The common action on such event is to
|
||||
seek to specified UID within your element.
|
||||
|
||||
## Implementation coverage, Specifications, …
|
||||
|
||||
Below is a list of container formats, links to documentation and a
|
||||
summary of toc related features. Each section title also indicates
|
||||
whether reading/writing a toc is implemented. Below hollow bullet point
|
||||
*o* indicate no support and filled bullets *\*\* indicate that this
|
||||
feature is handled.
|
||||
|
||||
### AIFC: -/-
|
||||
<http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/AIFF/Docs/AIFF-1.3.pdf>
|
||||
o *MARK* o *INST*
|
||||
|
||||
The *MARK* chunk defines a list of (cue-id, position\_in\_samples,
|
||||
label).
|
||||
|
||||
The *INST* chunk contains a sustainLoop and releaseLoop, each consisting
|
||||
of (loop-type, cue-begin, cue-end)
|
||||
|
||||
### FLAC: read/write
|
||||
|
||||
<http://xiph.org/flac/format.html#metadata_block_cuesheet> \*
|
||||
METADATA\_BLOCK\_CUESHEET \* CUESHEET\_TRACK o CUESHEET\_TRACK\_INDEX
|
||||
|
||||
Both CUESHEET\_TRACK and CUESHEET\_TRACK\_INDEX have a (relative) offset
|
||||
in samples. CUESHEET\_TRACK has ISRC metadata.
|
||||
|
||||
### MKV: read/write
|
||||
|
||||
<http://matroska.org/technical/specs/chapters/index.html> \* Chapters
|
||||
and Editions each having a uid \* Chapter have start/end time and
|
||||
metadata: ChapString, ChapLanguage, ChapCountry
|
||||
|
||||
### MP4: \* elst
|
||||
|
||||
The *elst* atom contains a list of edits. Each edit consists of (length,
|
||||
start, play-back speed).
|
||||
|
||||
### OGG: -/- <https://wiki.xiph.org/Chapter_Extension> o VorbisComment
|
||||
|
||||
fields called CHAPTERxxx and CHAPTERxxxNAME with xxx being a number
|
||||
between 000 and 999.
|
||||
|
||||
### WAV: read/write <http://www.sonicspot.com/guide/wavefiles.html> \* *cue
|
||||
' o 'plst* \* *adtl* \* *labl* \* *note* o *ltxt* o *smpl*
|
||||
|
||||
The *cue ' chunk defines a list of markers in the stream with 'cue-id’s.
|
||||
The 'smpl* chunk defines a list of regions in the stream with 'cue-id’s
|
||||
in the same namespace (?).
|
||||
|
||||
The various *adtl* chunks: *labl*, *note* and *ltxt* refer to the
|
||||
'cue-id’s.
|
||||
|
||||
A *plst* chunk defines a sequence of segments (cue-id, length\_samples,
|
||||
repeats). The *smpl* chunk defines a list of loops (cue-id, beg, end,
|
||||
loop-type, repeats).
|
||||
|
||||
## Conclusion/Ideas/Future work
|
||||
|
||||
Based on the data of chapter 5, a few thoughts and observations that can
|
||||
be used to extend and refine our API. These things below are not
|
||||
reflecting the current implementation.
|
||||
|
||||
All formats have table of \[cue-id, cue-start, (cue-end), (extra tags)\]
|
||||
- cue-id is commonly represented as and unsigned int 32bit - cue-end is
|
||||
optional - extra tags could be represented as a structure/taglist
|
||||
|
||||
Many formats have metadata that references the cue-table. - loops in
|
||||
instruments in wav, aifc - edit lists in wav, mp4
|
||||
|
||||
For mp4.edtl, wav.plst we could expose two editions. 1) the edit list is
|
||||
flattened: default, for playback 2) the stream has the raw data and the
|
||||
edit list is there as chapter markers: useful for editing software
|
||||
|
||||
We might want to introduce a new GST\_TOC\_ENTRY\_TYPE\_MARKER or \_CUE.
|
||||
This would be a sequence entry-type and it would not be used for
|
||||
navigational purposes, but to attach data to a point in time (envelopes,
|
||||
loops, …).
|
||||
|
||||
API wise there is some overlap between: - exposing multiple audio/video
|
||||
tracks as pads or as ToC editions. For ToC editions, we have the
|
||||
TocSelect event. - exposing subtitles as a sparse stream or as as ToC
|
||||
sequence of markers with labels
|
405
markdown/design/tracing.md
Normal file
405
markdown/design/tracing.md
Normal file
|
@ -0,0 +1,405 @@
|
|||
# Tracing
|
||||
|
||||
This subsystem will provide a mechanism to get structured tracing info
|
||||
from GStreamer applications. This can be used for post-run analysis as
|
||||
well as for live introspection.
|
||||
|
||||
# Use cases
|
||||
|
||||
- I’d like to get statistics from a running application.
|
||||
|
||||
- I’d like to to understand which parts of my pipeline use how many
|
||||
resources.
|
||||
|
||||
- I’d like to know which parts of the pipeline use how much memory.
|
||||
|
||||
- I’d like to know about ref-counts of parts in the pipeline to find
|
||||
ref-count issues.
|
||||
|
||||
# Non use-cases
|
||||
|
||||
- Some element in the pipeline does not play along the rules, find out
|
||||
which one. This could be done with generic tests.
|
||||
|
||||
# Design
|
||||
|
||||
The system brings the following new items: core hooks: probes in the
|
||||
core api, that will expose internal state when tracing is in use
|
||||
tracers: plugin features that can process data from the hooks and emit a
|
||||
log tracing front-ends: applications that consume logs from tracers
|
||||
|
||||
Like the logging, the tracer hooks can be compiled out and if not use a
|
||||
local condition to check if active.
|
||||
|
||||
Certain GStreamer core function (such as gst_pad_push or
|
||||
gst_element_add_pad) will call into the tracer subsystem to dispatch
|
||||
into active tracing modules. Developers will be able to select a list of
|
||||
plugins by setting an environment variable, such as
|
||||
GST_TRACERS="meminfo;dbus". One can also pass parameters to plugins:
|
||||
GST_TRACERS="log(events,buffers);stats(all)". When then plugins are
|
||||
loaded, we’ll add them to certain hooks according to which they are
|
||||
interested in.
|
||||
|
||||
Right now tracing info is logged as GstStructures to the TRACE level.
|
||||
Idea: Another env var GST_TRACE_CHANNEL could be used to send the
|
||||
tracing to a file or a socket. See
|
||||
<https://bugzilla.gnome.org/show_bug.cgi?id=733188> for discussion on
|
||||
these environment variables.
|
||||
|
||||
# Hook api
|
||||
|
||||
We’ll wrap interesting api calls with two macros, e.g. gst_pad_push():
|
||||
|
||||
GstFlowReturn gst_pad_push (GstPad * pad, GstBuffer * buffer) {
|
||||
GstFlowReturn res;
|
||||
|
||||
``` c
|
||||
g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
|
||||
g_return_val_if_fail (GST_PAD_IS_SRC (pad), GST_FLOW_ERROR);
|
||||
g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
|
||||
|
||||
GST_TRACER_PAD_PUSH_PRE (pad, buffer);
|
||||
res = gst_pad_push_data (pad,
|
||||
GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH, buffer);
|
||||
GST_TRACER_PAD_PUSH_POST (pad, res);
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
TODO(ensonic): gcc has some magic for wrapping functions -
|
||||
<http://gcc.gnu.org/onlinedocs/gcc/Constructing-Calls.html> -
|
||||
<http://www.clifford.at/cfun/gccfeat/#gccfeat05.c>
|
||||
|
||||
TODO(ensonic): we should eval if we can use something like jump_label
|
||||
in the kernel - <http://lwn.net/Articles/412072/> +
|
||||
<http://lwn.net/Articles/435215/> -
|
||||
<http://lxr.free-electrons.com/source/kernel/jump_label.c> -
|
||||
<http://lxr.free-electrons.com/source/include/linux/jump_label.h> -
|
||||
<http://lxr.free-electrons.com/source/arch/x86/kernel/jump_label.c>
|
||||
TODO(ensonic): liblttng-ust provides such a mechanism for user-space -
|
||||
but this is mostly about logging traces - it is linux specific :/
|
||||
|
||||
In addition to api hooks we should also provide timer hooks. Interval
|
||||
timers are useful to get e.g. resource usage snapshots. Also absolute
|
||||
timers might make sense. All this could be implemented with a clock
|
||||
thread. We can use another env-var GST_TRACE_TIMERS="100ms,75ms" to
|
||||
configure timers and then pass them to the tracers like,
|
||||
GST_TRACERS="rusage(timer=100ms);meminfo(timer=75ms)". Maybe we can
|
||||
create them ad-hoc and avoid the GST_TRACE_TIMERS var.
|
||||
|
||||
Hooks (* already implemented)
|
||||
|
||||
* gst_bin_add
|
||||
* gst_bin_remove
|
||||
* gst_element_add_pad
|
||||
* gst_element_post_message
|
||||
* gst_element_query
|
||||
* gst_element_remove_pad
|
||||
* gst_element_factory_make
|
||||
* gst_pad_link
|
||||
* gst_pad_pull_range
|
||||
* gst_pad_push
|
||||
* gst_pad_push_list
|
||||
* gst_pad_push_event
|
||||
* gst_pad_unlink
|
||||
|
||||
## Tracer api
|
||||
|
||||
Tracers are plugin features. They have a simple api:
|
||||
|
||||
class init Here the tracers describe the data the will emit.
|
||||
|
||||
instance init Tracers attach handlers to one or more hooks using
|
||||
gst_tracing_register_hook(). In case the are configurable, they can
|
||||
read the options from the *params* property. This is the extra detail
|
||||
from the environment var.
|
||||
|
||||
hook functions Hooks marshal the parameters given to a trace hook into
|
||||
varargs and also add some extra into such as a timestamp. Hooks will be
|
||||
called from misc threads. The trace plugins should only consume (=read)
|
||||
the provided data. Expensive computation should be avoided to not affect
|
||||
the execution too much. Most trace plugins will log data to a trace
|
||||
channel.
|
||||
|
||||
instance destruction Tracers can output results and release data. This
|
||||
would ideally be done at the end of the applications, but gst_deinit()
|
||||
is not mandatory. gst_tracelib was using a gcc_destructor. Ideally
|
||||
tracer modules log data as they have them and leave aggregation to a
|
||||
tool that processes the log.
|
||||
|
||||
## tracer event classes
|
||||
|
||||
Most tracers will log some kind of *events* : a data transfer, an event,
|
||||
a message, a query or a measurement. Every tracers should describe the
|
||||
data format. This way tools that process tracer logs can show the data
|
||||
in a meaningful way without having to know about the tracer plugin.
|
||||
|
||||
One way would be to introspect the data from the plugin. This has the
|
||||
disadvantage that the postprocessing app needs to load the plugins or
|
||||
talk to the gstreamer registry. An alternative is to also log the format
|
||||
description into the log. Right now we’re logging several nested
|
||||
GstStructure from the `tracer_class_init()` function (except in the
|
||||
log tracer).
|
||||
|
||||
```
|
||||
gst_tracer_record_new ("thread-rusage.class",
|
||||
// value in the log record (order does not matter)
|
||||
// *thread-id* is a *key* to related the record to something as indicated
|
||||
// by *scope* substructure "thread-id",
|
||||
GST_TYPE_STRUCTURE, gst_structure_new ("scope", "type",
|
||||
G_TYPE_GTYPE, G_TYPE_GUINT64, "related-to",
|
||||
GST_TYPE_TRACER_VALUE_SCOPE, GST_TRACER_VALUE_SCOPE_THREAD,
|
||||
NULL),
|
||||
// next value in the record // *average-cpuload* is a measurement as indicated by the *value*
|
||||
// substructure "average-cpuload",
|
||||
GST_TYPE_STRUCTURE, gst_structure_new ("value", // value type
|
||||
"type", G_TYPE_GTYPE, G_TYPE_UINT,
|
||||
// human readable description, that can be used as a graph label
|
||||
"description", G_TYPE_STRING, "average cpu usage per thread",
|
||||
// flags that help to use the right graph type
|
||||
// flags { aggregated, windowed, cumulative, … }
|
||||
"flags", GST_TYPE_TRACER_VALUE_FLAGS, GST_TRACER_VALUE_FLAGS_AGGREGATED,
|
||||
// value range
|
||||
"min", G_TYPE_UINT, 0, "max", G_TYPE_UINT, 100, NULL),
|
||||
… NULL);
|
||||
```
|
||||
|
||||
|
||||
A few ideas that are not yet in the above spec: - it would be nice to
|
||||
describe the unit of values - putting it into the description is not
|
||||
flexible though, e.g. time would be a guint64 but a ui would reformat it
|
||||
to e.g. h:m:s.ms - other units are e.g.: percent, per-mille, or kbit/s -
|
||||
we’d like to have some metadata on scopes - e.g. we’d like to log the
|
||||
thread-names, so that a UI can show that instead of thread-ids - the
|
||||
stats tracer logs *new-element* and *new-pad* messages - they add a
|
||||
unique *ix* to each instance as the memory ptr can be reused for new
|
||||
instances, the data is attached to the objects as qdata - the latency
|
||||
tracer would like to also reference this metadata - right now we log the
|
||||
classes as structures - this is important so that the log is self
|
||||
contained - it would be nice to add them to the registry, so that
|
||||
gst-inspect can show them
|
||||
|
||||
We could also consider to add each value as a READABLE gobject property.
|
||||
The property has name/description. We could use qdata for scope and
|
||||
flags (or have some new property flags). We would also need a new
|
||||
"notify" signal, so that value-change notifications would include a
|
||||
time-stamp. This way the tracers would not needs to be aware of the
|
||||
logging. The core tracer would register the notify handlers and emit the
|
||||
log. Or we just add a gst_tracer_class_install_event() and that
|
||||
mimics the g_object_class_install_property().
|
||||
|
||||
Frontends can: - do an events over time histogram - plot curves of
|
||||
values over time or deltas - show gauges - collect statistics (min, max,
|
||||
avg, …)
|
||||
|
||||
We can have some under gstreamer/plugins/tracers/
|
||||
|
||||
## latency
|
||||
|
||||
- register to buffer and event flow
|
||||
|
||||
- send custom event on buffer flow at source elements
|
||||
|
||||
- catch events on event transfer at sink elements
|
||||
|
||||
## meminfo (not yet implemented)
|
||||
|
||||
- register to an interval-timer hook.
|
||||
- call mallinfo() and log memory usage
|
||||
|
||||
rusage
|
||||
|
||||
- register to an interval-timer hook.
|
||||
|
||||
- call getrusage() and log resource usage
|
||||
|
||||
## dbus (not yet implemented)
|
||||
|
||||
- provide a dbus iface to announce applications that are traced
|
||||
- tracing UIs can use the dbus iface to find the channels where logging and
|
||||
tracing is getting logged to
|
||||
- one would start the tracing UI first and when the application is started with
|
||||
tracing activated, the dbus plugin will announce the new application,
|
||||
upon which the tracing UI can start reading from the log channels, this avoid
|
||||
missing some data
|
||||
|
||||
## topology (not yet implemented)
|
||||
|
||||
- register to pipeline topology hooks
|
||||
|
||||
- tracing UIs can show a live pipeline graph
|
||||
|
||||
## stats
|
||||
|
||||
- register to buffer, event, message and query flow
|
||||
|
||||
- tracing apps can do e.g. statistics
|
||||
|
||||
## refcounts (not yet implemented)
|
||||
|
||||
- log ref-counts of objects
|
||||
- just logging them outside of glib/gobject would still make it hard to detect
|
||||
issues though
|
||||
|
||||
## opengl (not yet implemented)
|
||||
|
||||
- upload/download times
|
||||
|
||||
- there is not hardware agnostic way to get e.g. memory usage info (gl
|
||||
extensions)
|
||||
|
||||
## memory (not yet implemented)
|
||||
|
||||
- trace live instance (and pointer to the memory)
|
||||
- use an atexit handler to dump leaked instance
|
||||
https://bugzilla.gnome.org/show_bug.cgi?id=756760#c6
|
||||
|
||||
## leaks
|
||||
|
||||
- track creation/destruction of GstObject and GstMiniObject
|
||||
|
||||
- log those which are still alive when app is exiting and raise an
|
||||
error if any
|
||||
|
||||
- If the GST_LEAKS_TRACER_SIG env variable is defined the tracer
|
||||
will handle the following UNIX signals:
|
||||
|
||||
- SIGUSR1: log alive objects
|
||||
|
||||
- SIGUSR2: create a checkpoint and print a list of objects created and
|
||||
destroyed since the previous checkpoint.
|
||||
|
||||
- If the GST_LEAKS_TRACER_STACK_TRACE env variable is defined log
|
||||
the creation stack trace of leaked objects. This may significantly
|
||||
increase memory consumption.
|
||||
|
||||
## gst-debug-viewer
|
||||
|
||||
gst-debug-viewer could be given the trace log in addition to the debug
|
||||
log (or a combined log). Alternatively it would show a dialog that shows
|
||||
all local apps (if the dbus plugin is loaded) and read the log streams
|
||||
from the sockets/files that are configured for the app.
|
||||
|
||||
## gst-tracer
|
||||
|
||||
Counterpart of gst-tracelib-ui.
|
||||
|
||||
## gst-stats
|
||||
|
||||
A terminal app that shows summary/running stats like the summary
|
||||
gst-tracelib shows at the end of a run. Currently only shows an
|
||||
aggregated status.
|
||||
|
||||
## live-graphers
|
||||
|
||||
Maybe we can even feed the log into existing live graphers, with a
|
||||
little driver * <https://github.com/dkogan/feedgnuplot>
|
||||
|
||||
- should tracers log into the debug.log or into a separate log?
|
||||
|
||||
- separate log
|
||||
|
||||
- use a binary format?
|
||||
|
||||
- worse performance (we’re writing two logs at the same time)
|
||||
|
||||
- need to be careful when people to GST_DEBUG_CHANNEL=stderr and
|
||||
GST_TRACE_CHANNEL=stderr (use a shared channel, but what about the
|
||||
formats?)
|
||||
|
||||
- debug log
|
||||
|
||||
- the tracer subsystem would need to log the GST_TRACE at a level
|
||||
that is active
|
||||
|
||||
- should the tracer call gst_debug_category_set_threshold() to
|
||||
ensure things work, even though the levels don’t make a lot of sense
|
||||
here
|
||||
|
||||
- make logging a tracer (a hook in gst_debug_log_valist, move
|
||||
gst_debug_log_default() to the tracer module)
|
||||
|
||||
- log all debug log to the tracer log, some of the current logging
|
||||
statements can be replaced by generic logging as shown in the
|
||||
log-tracer
|
||||
|
||||
- add tools/gst-debug to extract a human readable debug log from the
|
||||
trace log
|
||||
|
||||
- we could maintain a list of log functions, where
|
||||
gst_tracer_log_trace() is the default one. This way e.g.
|
||||
gst-validate could consume the traces directly.
|
||||
|
||||
- when hooking into a timer, should we just have some predefined
|
||||
intervals?
|
||||
|
||||
- can we add a tracer module that registers the timer hook? then we
|
||||
could do GST_TRACER="timer(10ms);rusage" right now the tracer hooks
|
||||
are defined as an enum though.
|
||||
|
||||
- when connecting to a running app, we can’t easily get the *current*
|
||||
state if logging is using a socket, as past events are not
|
||||
explicitly stored, we could determine the current topology and emit
|
||||
events with GST_CLOCK_TIME_NONE as ts to indicate that the events
|
||||
are synthetic.
|
||||
|
||||
- we need stable ids for scopes (threads, elements, pads)
|
||||
|
||||
- the address can be reused
|
||||
|
||||
- we can use gst_util_seqnum_next()
|
||||
|
||||
- something like gst_object_get_path_string() won’t work as
|
||||
objects are initially without parent
|
||||
|
||||
- right now the tracing-hooks are enabled/disabled from configure with
|
||||
--{enable,disable}-gst-tracer-hooks The tracer code and the plugins
|
||||
are still built though. We should add a
|
||||
--{enable,disable}-gst-tracer to disabled the whole system,
|
||||
allthough this is a bit confusing with the --{enable,disable}-trace
|
||||
option we have already.
|
||||
|
||||
## Try it
|
||||
|
||||
### Traces for buffer flow in TRACE level:
|
||||
|
||||
GST_DEBUG="GST_TRACER:7,GST_BUFFER*:7,GST_EVENT:7,GST_MESSAGE:7"
|
||||
GST_TRACERS=log gst-launch-1.0 fakesrc num-buffers=10 ! fakesink -
|
||||
|
||||
### Print some pipeline stats on exit:
|
||||
|
||||
GST_DEBUG="GST_TRACER:7" GST_TRACERS="stats;rusage"
|
||||
GST_DEBUG_FILE=trace.log gst-launch-1.0 fakesrc num-buffers=10
|
||||
sizetype=fixed ! queue ! fakesink && gst-stats-1.0 trace.log
|
||||
|
||||
### get ts, average-cpuload, current-cpuload, time and plot
|
||||
|
||||
GST_DEBUG="GST_TRACER:7" GST_TRACERS="stats;rusage"
|
||||
GST_DEBUG_FILE=trace.log /usr/bin/gst-play-1.0 $HOME/Videos/movie.mp4 &&
|
||||
./scripts/gst-plot-traces.sh --format=png | gnuplot eog trace.log.*.png
|
||||
|
||||
### print processing latencies
|
||||
|
||||
GST_DEBUG="GST_TRACER:7" GST_TRACERS=latency gst-launch-1.0 \
|
||||
audiotestsrc num-buffers=10 ! audioconvert ! volume volume=0.7 ! \
|
||||
autoaudiosink
|
||||
|
||||
### Raise a warning if a leak is detected
|
||||
|
||||
GST_TRACERS="leaks" gst-launch-1.0 videotestsrc num-buffers=10 !
|
||||
fakesink
|
||||
|
||||
### check if any GstEvent or GstMessage is leaked and raise a warning
|
||||
|
||||
GST_DEBUG="GST_TRACER:7" GST_TRACERS="leaks(GstEvent,GstMessage)"
|
||||
gst-launch-1.0 videotestsrc num-buffers=10 ! fakesink
|
||||
|
||||
# Performance
|
||||
|
||||
run ./tests/benchmarks/tracing.sh <tracer(s)> <media>
|
||||
|
||||
egrep -c "(proc|thread)-rusage" trace.log 658618 grep -c
|
||||
"gst_tracer_log_trace" trace.log 823351
|
||||
|
||||
- we can optimize most of it by using quarks in structures or
|
||||
eventually avoid structures totally
|
235
markdown/design/trickmodes.md
Normal file
235
markdown/design/trickmodes.md
Normal file
|
@ -0,0 +1,235 @@
|
|||
# Trickmodes
|
||||
|
||||
GStreamer provides API for performing various trickmode playback. This
|
||||
includes:
|
||||
|
||||
- server side trickmodes
|
||||
|
||||
- client side fast/slow forward playback
|
||||
|
||||
- client side fast/slow backwards playback
|
||||
|
||||
Server side trickmodes mean that a source (network source) can provide a
|
||||
stream with different playback speed and direction. The client does not
|
||||
have to perform any special algorithms to decode this stream.
|
||||
|
||||
Client side trickmodes mean that the decoding client (GStreamer)
|
||||
performs the needed algorithms to change the direction and speed of the
|
||||
media file.
|
||||
|
||||
Seeking can both be done in a playback pipeline and a transcoding
|
||||
pipeline.
|
||||
|
||||
## General seeking overview
|
||||
|
||||
Consider a typical playback pipeline:
|
||||
|
||||
```
|
||||
.---------. .------.
|
||||
.-------. | decoder |->| sink |
|
||||
.--------. | |-->'---------' '------'
|
||||
| source |->| demux |
|
||||
'--------' | |-->.---------. .------.
|
||||
'-------' | decoder |->| sink |
|
||||
'---------' '------'
|
||||
```
|
||||
|
||||
The pipeline is initially configured to play back at speed 1.0 starting
|
||||
from position 0 and stopping at the total duration of the file.
|
||||
|
||||
When performing a seek, the following steps have to be taken by the
|
||||
application:
|
||||
|
||||
### Create a seek event
|
||||
|
||||
The seek event contains:
|
||||
|
||||
- various flags describing:
|
||||
|
||||
- where to seek to (KEY\_UNIT)
|
||||
|
||||
- how accurate the seek should be (ACCURATE)
|
||||
|
||||
- how to perform the seek (FLUSH)
|
||||
|
||||
- what to do when the stop position is reached (SEGMENT).
|
||||
|
||||
- extra playback options (SKIP)
|
||||
|
||||
- a format to seek in, this can be time, bytes, units (frames,
|
||||
samples), …
|
||||
|
||||
- a playback rate, 1.0 is normal playback speed, positive values
|
||||
bigger than 1.0 mean fast playback. negative values mean reverse
|
||||
playback. A playback speed of 0.0 is not allowed (but is equivalent
|
||||
to PAUSING the pipeline).
|
||||
|
||||
- a start position, this value has to be between 0 and the total
|
||||
duration of the file. It can also be relative to the previously
|
||||
configured start value.
|
||||
|
||||
- a stop position, this value has to be between 0 and the total
|
||||
duration. It can also be relative to the previously configured stop
|
||||
value.
|
||||
|
||||
See also gst\_event\_new\_seek().
|
||||
|
||||
### Send the seek event
|
||||
|
||||
Send the new seek event to the pipeline with
|
||||
gst\_element\_send\_event().
|
||||
|
||||
By default the pipeline will send the event to all sink elements. By
|
||||
default an element will forward the event upstream on all sinkpads.
|
||||
Elements can modify the format of the seek event. The most common format
|
||||
is GST\_FORMAT\_TIME.
|
||||
|
||||
One element will actually perform the seek, this is usually the demuxer
|
||||
or source element. For more information on how to perform the different
|
||||
seek types see [seeking](design/seeking.md).
|
||||
|
||||
For client side trickmode a SEGMENT event will be sent downstream with
|
||||
the new rate and start/stop positions. All elements prepare themselves
|
||||
to handle the rate (see below). The applied rate of the SEGMENT event
|
||||
will be set to 1.0 to indicate that no rate adjustment has been done.
|
||||
|
||||
for server side trick mode a 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.
|
||||
|
||||
## Server side trickmode
|
||||
|
||||
The source element operates in push mode. It can reopen a server
|
||||
connection requesting a new byte or time position and a new playback
|
||||
speed. The capabilities can be queried from the server when the
|
||||
connection is opened.
|
||||
|
||||
We assume the source element is derived from the GstPushSrc base class.
|
||||
The base source should be configured with gst\_base\_src\_set\_format
|
||||
(src, GST\_FORMAT\_TIME).
|
||||
|
||||
The do\_seek method will be called on the push src subclass with the
|
||||
seek information passed in the GstSegment argument.
|
||||
|
||||
The rate value in the segment should be used to reopen the connection to
|
||||
the server 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. 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
|
||||
do_seek | |
|
||||
----------->| |
|
||||
| reopen connection |
|
||||
|-------------------->|
|
||||
| .
|
||||
| success .
|
||||
|<--------------------|
|
||||
modify | |
|
||||
rate to 1.0 | |
|
||||
| |
|
||||
return | |
|
||||
TRUE | |
|
||||
| |
|
||||
```
|
||||
|
||||
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. 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 SEGMENT event is sent downstream
|
||||
with a rate different from 1.0. Plugins receiving the SEGMENT can decide
|
||||
to perform the rate conversion of the media data (retimestamp video
|
||||
frames, resample audio, …).
|
||||
|
||||
If a plugin decides to resample or retimestamp, it should modify the
|
||||
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 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.
|
||||
|
||||
When the SKIP flag is set, frames can be dropped in the elements. If S
|
||||
is the speedup factor, a good algorithm for implementing frame skipping
|
||||
is to send audio in chunks of Nms (usually 300ms is good) and then skip
|
||||
((S-1) \* Nns) of audio data. For the video we send only the keyframes
|
||||
in the (S \* Nns) interval. In this case, the demuxer would scale the
|
||||
timestamps and would set an applied rate of S.
|
||||
|
||||
## client side backwards trickmode
|
||||
|
||||
For backwards playback the following rules apply:
|
||||
|
||||
- the rate in the SEGMENT is less than 0.0.
|
||||
|
||||
- the SEGMENT start position is less than the stop position, playback
|
||||
will however happen from stop to start in reverse.
|
||||
|
||||
- the time member in the SEGMENT is set to the stream time of the
|
||||
start position.
|
||||
|
||||
For plugins the following rules apply:
|
||||
|
||||
- A source plugin sends data in chunks starting from the last chunk of
|
||||
the file. The actual bytes are not reversed. Each chunk that is not
|
||||
forward continuous with the previous chunk is marked with a DISCONT
|
||||
flag.
|
||||
|
||||
- A demuxer accumulates the chunks. As soon as a keyframe is found,
|
||||
everything starting from the keyframe up to the accumulated data is
|
||||
sent downstream. Timestamps on the buffers are set starting from the
|
||||
stop position to start, effectively going backwards. Chunks are
|
||||
marked with DISCONT when they are not forward continuous with the
|
||||
previous buffer.
|
||||
|
||||
- A video decoder decodes and accumulates all decoded frames. If a
|
||||
buffer with a DISCONT, SEGMENT or EOS is received, all accumulated
|
||||
frames are sent downsteam in reverse.
|
||||
|
||||
- An audio decoder decodes and accumulates all decoded audio. If a
|
||||
buffer with a DISCONT, SEGMENT or EOS is received, all accumulated
|
||||
audio is sent downstream in reverse order. Some audio codecs need
|
||||
the previous data buffer to decode the current one, in that case,
|
||||
the previous DISCONT buffer needs to be combined with the last
|
||||
non-DISCONT buffer to generate the last bit of output.
|
||||
|
||||
- A sink reverses (for audio) and retimestamps (audio, video) the
|
||||
buffers before playing them back. Retimestamping occurs relative to
|
||||
the stop position, making the timestamps increase again and suitable
|
||||
for synchronizing against the clock. Audio sinks also have to
|
||||
perform simple resampling before playing the samples.
|
||||
|
||||
- for transcoding, audio and video resamplers can be used to reverse,
|
||||
resample and retimestamp the buffers. Any rate adjustments performed
|
||||
on the media must be added to the applied\_rate and subtracted from
|
||||
the rate members in the SEGMENT
|
||||
event.
|
||||
|
||||
In SKIP mode, the same algorithm as for forward SKIP mode can be used.
|
||||
|
||||
## Notes
|
||||
|
||||
- The clock/running\_time keeps running forward.
|
||||
|
||||
- backwards playback potentially uses a lot of memory as frames and
|
||||
undecoded data gets buffered.
|
57
sitemap.txt
57
sitemap.txt
|
@ -137,3 +137,60 @@ index.md
|
|||
splitup.md
|
||||
licensing.md
|
||||
rtp.md
|
||||
design/index.md
|
||||
design/MT-refcounting.md
|
||||
design/TODO.md
|
||||
design/activation.md
|
||||
design/buffer.md
|
||||
design/buffering.md
|
||||
design/bufferpool.md
|
||||
design/caps.md
|
||||
design/clocks.md
|
||||
design/context.md
|
||||
design/controller.md
|
||||
design/conventions.md
|
||||
design/dynamic.md
|
||||
design/element-sink.md
|
||||
design/element-source.md
|
||||
design/element-transform.md
|
||||
design/events.md
|
||||
design/framestep.md
|
||||
design/gstbin.md
|
||||
design/gstbus.md
|
||||
design/gstelement.md
|
||||
design/gstghostpad.md
|
||||
design/gstobject.md
|
||||
design/gstpipeline.md
|
||||
design/draft-klass.md
|
||||
design/latency.md
|
||||
design/live-source.md
|
||||
design/memory.md
|
||||
design/messages.md
|
||||
design/meta.md
|
||||
design/draft-metadata.md
|
||||
design/miniobject.md
|
||||
design/missing-plugins.md
|
||||
design/negotiation.md
|
||||
design/overview.md
|
||||
design/preroll.md
|
||||
design/probes.md
|
||||
design/progress.md
|
||||
design/push-pull.md
|
||||
design/qos.md
|
||||
design/query.md
|
||||
design/relations.md
|
||||
design/scheduling.md
|
||||
design/seeking.md
|
||||
design/segments.md
|
||||
design/seqnums.md
|
||||
design/sparsestreams.md
|
||||
design/standards.md
|
||||
design/states.md
|
||||
design/stream-selection.md
|
||||
design/stream-status.md
|
||||
design/streams.md
|
||||
design/synchronisation.md
|
||||
design/draft-tagreading.md
|
||||
design/toc.md
|
||||
design/tracing.md
|
||||
design/trickmodes.md
|
||||
|
|
Loading…
Reference in a new issue