mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-27 02:30:35 +00:00
150 lines
6.5 KiB
Text
150 lines
6.5 KiB
Text
|
RTP retransmission design
|
||
|
|
||
|
|
||
|
GstRTPRetransmissionRequest
|
||
|
---------------------------
|
||
|
|
||
|
Custom upstream event which mainly contains the ssrc and the seqnum of the
|
||
|
packet which is asked to be retransmisted.
|
||
|
|
||
|
On the pipeline receiver side this event is generated by the
|
||
|
gstrtpjitterbuffer element. Then it is translated to a NACK to be sent over
|
||
|
the network.
|
||
|
|
||
|
On the pipeline sender side, this event is generated by the gstrtpsession
|
||
|
element when it receives a NACK from the network.
|
||
|
|
||
|
|
||
|
rtprtxsend element
|
||
|
------------------
|
||
|
|
||
|
-- basic mecansim
|
||
|
|
||
|
rtprtxsend keeps a history of rtp packets that it has already sent.
|
||
|
When it receives the event GstRTPRetransmissionRequest from the downstream
|
||
|
gstrtpsession element, it loopkup the requested seqnum in its stored packets.
|
||
|
If the packet is present in its history, it will create a RTX packet according
|
||
|
to RFC 4588. Then this rtx packet is pushed to its src pad as other packets.
|
||
|
|
||
|
rtprtxsend works in SSRC-multiplexed mode, so it has one always sink and
|
||
|
src pad.
|
||
|
|
||
|
-- building retransmission packet fron original packet
|
||
|
|
||
|
A rtx packet is mostly the same as an orignal packet, except it has its own
|
||
|
ssrc and its own seqnum. That's why rtprtxsend works in SSRC-multiplexed mode.
|
||
|
It also means that the same session is used.
|
||
|
Another difference between rtx packet and its original is that it inserts the
|
||
|
original seqnum (OSN: 2 bytes) at the beginning of the payload.
|
||
|
Also rtprtxsend builds rtx packet without padding, to let other elements do that.
|
||
|
The last difference is the payload type. For now the user has to set it through
|
||
|
the rtx-payload-type property. Later it will be automatically retreive this
|
||
|
information from SDP. See fmtp field as specifies in the RPC4588
|
||
|
(a=fmtp:99 apt=98) fmtp is the payload type of the retransmission stream
|
||
|
and apt the payload type of its associated master stream.
|
||
|
|
||
|
-- restransmission ssrc and seqnum
|
||
|
|
||
|
To choose rtx_ssrc it randomly selects a number between 0 and 2^32-1 until
|
||
|
it is different than master_ssrc.
|
||
|
rtx_seqnum is randomly selected between 0 and 2^16-1
|
||
|
|
||
|
- deeper in the stored buffer history
|
||
|
|
||
|
For the history it uses a GSequence with 2^15-1 as its maximum size.
|
||
|
Which is resonable as the default value is 100.
|
||
|
It contains the packets in reverse order they have been sent
|
||
|
(head:newest, tail:oldest)
|
||
|
GSequence allows to add and remove an element in constant time (like a queue).
|
||
|
Also GSequence allows to do a binary search when rtprtxsend lookup in its
|
||
|
history.
|
||
|
It's important if it receives a lot of requests or if the history is large.
|
||
|
|
||
|
-- pending rtx packets
|
||
|
|
||
|
When looking up in its history, if seqnum is found then it pushes the buffer
|
||
|
into a GQueue to its tail.
|
||
|
Before to send the current master stream packet, rtprtxsend sends all the
|
||
|
buffers which are in this GQueue. Taking care of converting them to rtx
|
||
|
packets.
|
||
|
This way, rtx packets are sent in the same order they have been requested.
|
||
|
(g_list_foreach traverse the queue from head to tail)
|
||
|
The GQueue is cleared between sending 2 master stream packets.
|
||
|
So for this GQueue to contain more than one element, it means that rtprtxsend
|
||
|
receives more than one rtx request between sending 2 master packets.
|
||
|
|
||
|
-- collision
|
||
|
|
||
|
When handling a GstRTPCollision event, if the ssrc is its rtx ssrc then
|
||
|
rtprtxsend clear its history and its pending retransmission queue.
|
||
|
Then it chooses a rtx_ssrc until it's different than master ssrc.
|
||
|
If the GstRTPCollision event does not contain its rtx ssrc, for example
|
||
|
its master ssrc or other, then it just forwards the event to upstream.
|
||
|
So that it can be handled by the rtppayloader.
|
||
|
|
||
|
|
||
|
rtprtxreceive element
|
||
|
------------------
|
||
|
|
||
|
-- basic mecanims
|
||
|
|
||
|
The same rtprtxreceive instance can receive several master streams and several
|
||
|
retransmission streams.
|
||
|
So it will try to dynamically associate a rtx ssrc with its master ssrc.
|
||
|
So that it can reconstruct the original from the proper rtx packet.
|
||
|
|
||
|
The algorithm is based on the fact that seqnums of different streams
|
||
|
(considering all master and all rtx streams) evolve at a different rate.
|
||
|
It means that the initial seqnum is random for each one and the offset could
|
||
|
also be different. So that they are statistically all different at a given
|
||
|
time. If bad luck then the association is delayed to the next rtx request.
|
||
|
|
||
|
The algorithm also needs to know if a given packet is a rtx packet or not.
|
||
|
To know this information there is the rtx-payload-types property. For now the
|
||
|
user as to configure it but later it will be automatically retreive this
|
||
|
information from SDP.
|
||
|
It needs to know if the current packet is rtx or not in order to know if
|
||
|
it can extract the OSN from the payload. Otherwise it would extract the OSN
|
||
|
even on master streams which means nothing and so it could do bad things.
|
||
|
In theory maybe it could work but we have this information in SDP so why not
|
||
|
using it to avoid bad associations.
|
||
|
|
||
|
Note that it also means that several master streams can have the same payload
|
||
|
type. And also several rtx streams can have the same payload type.
|
||
|
So the information from SDP which gives us which rtx payload type belong to
|
||
|
a give master payload type is not enough to do the association between rtx ssrc
|
||
|
and master ssrc.
|
||
|
|
||
|
rtprtxreceive works in SSRC-multiplexed mode, so it has one always sink and
|
||
|
src pad.
|
||
|
|
||
|
-- deeper in the association algorithm
|
||
|
|
||
|
When it receives a GstRTPRetransmissionRequest event it will remember the ssrc
|
||
|
and the seqnum from this request.
|
||
|
|
||
|
On incoming packets, if the packet has its ssrc already associated then it
|
||
|
knows if the ssrc is an rtx ssrc or a master stream ssrc.
|
||
|
If this is a rtx packet then it recontructs the original and pushs the result to
|
||
|
src pad as if it was a master packet.
|
||
|
|
||
|
If the ssrc is not yet associated rtprtxreceive checks the payload type.
|
||
|
if the packet has its payload type marked as rtx then it will extract the OSN
|
||
|
(original seqnum number) and lookup in its stored requests if a seqnum matchs.
|
||
|
If found, then it associates the current ssrc to the master ssrc marked in the
|
||
|
request. If not found it just drops the packet.
|
||
|
Then it removes the request from the stored requests.
|
||
|
|
||
|
If there are 2 requests with the same seqnum and different ssrc, then the
|
||
|
couple seqnum,ssrc is removed from the stored requests.
|
||
|
A stored request actually means that actually the couple seqnum,ssrc is stored.
|
||
|
If it's happens the request is droped but it avoids to do bad associations.
|
||
|
In this case the association is just delayed to the next request.
|
||
|
|
||
|
- building original packet from rtx packet
|
||
|
|
||
|
Header, extensions, payload and padding are mostly the same. Except that the
|
||
|
OSN is removed from the payload. Then ssrc, seqnum, and original payload type
|
||
|
are correctly set. Original payload type is actually also stored when the
|
||
|
rtx request is handled.
|