mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-24 18:51:11 +00:00
446 lines
15 KiB
Markdown
446 lines
15 KiB
Markdown
|
# 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.
|