mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-28 04:31:06 +00:00
263 lines
12 KiB
Markdown
263 lines
12 KiB
Markdown
|
# RTP
|
||
|
|
||
|
These design docs detail some of the lower-level mechanism of certain parts
|
||
|
of GStreamer's RTP stack. For a higher-level overview see the [RTP and RTSP
|
||
|
support](rtp.md) section.
|
||
|
|
||
|
# RTP auxiliary stream design
|
||
|
|
||
|
## Auxiliary elements
|
||
|
|
||
|
There are two kind of auxiliary elements, sender and receiver. Let's
|
||
|
call them rtpauxsend and rtpauxreceive.
|
||
|
|
||
|
rtpauxsend has always one sink pad and can have unlimited requested src
|
||
|
pads. If only src pad then it works in SSRC-multiplexed mode, if several
|
||
|
src pads then it works in session multiplexed mode.
|
||
|
|
||
|
rtpauxreceive has always one ssrc pad and can have unlimited requested
|
||
|
sink pads. If only one sink pad then it works in SSRC-multiplexed mode,
|
||
|
if several sink pads then it works in session multiplexed mode.
|
||
|
|
||
|
## Rtpbin and auxiliary elements
|
||
|
|
||
|
### Basic mechanism
|
||
|
|
||
|
rtpbin knows for which session ids the given auxiliary element belong
|
||
|
to. It's done through "set-aux-send", for rtpauxsend kind, and through
|
||
|
"set-aux-receive" for rtpauxreceive kind. You can call those signals as
|
||
|
much as needed for each auxiliary element. So for aux elements that work
|
||
|
in SSRC-multiplexed mode this signal action is called only one time.
|
||
|
|
||
|
The user has to call those action signals before to request the
|
||
|
differents rtpbin pads. rtpbin is in charge to link those auxiliary
|
||
|
elements with the sessions, and on receiver side, rtpbin has also to
|
||
|
handle the link with ssrcdemux.
|
||
|
|
||
|
rtpbin never knows if the given rtpauxsend is actually a rtprtxsend
|
||
|
element or another aux element. rtpbin never knows if the given
|
||
|
rtpauxreceive is actually a rtprtxreceive element or another aux
|
||
|
element. rtpbin has to be kept generic so that more aux elements can be
|
||
|
added later without changing rtpbin.
|
||
|
|
||
|
It's currently not possible to use rtpbin with auxiliary stream from
|
||
|
gst-launch. We can discuss about having the ability for rtpbin to
|
||
|
instanciate itself the special aux elements rtprtxsend and rtprtxreceive
|
||
|
but they need to be configured ("payload-type" and "payload-types"
|
||
|
properties) to make retransmission work. So having several rtprtxsend
|
||
|
and rtprtxreceive in a rtpbin would require a lot of properties to
|
||
|
manage them form rtpbin. And for each auxiliary elements.
|
||
|
|
||
|
If you want to use rtprtxreceive and rtprtpsend from gst-launch you have
|
||
|
to use rtpsession, ssrcdemux and rtpjitterbuffer elements yourself. See
|
||
|
gtk-doc of rtprtxreceive for an example.
|
||
|
|
||
|
### Requesting the rtpbin's pads on the pipeline receiver side
|
||
|
|
||
|
If rtpauxreceive is set for session, i, j, k then it has to call
|
||
|
rtpbin::"set-aux-receive" 3 times giving those ids and this aux element.
|
||
|
It has to be done before requesting the recv\_rtp\_sink\_i,
|
||
|
recv\_rtp\_sink\_j, recv\_rtp\_sink\_k. For a concrete case
|
||
|
rtprtxreceive, if the user wants it for session i, then it has to call
|
||
|
rtpbin::"set-aux-receive" one time giving i and this aux element. Then
|
||
|
the user can request recv\_rtp\_sink\_i pad.
|
||
|
|
||
|
Calling rtpbin::"set-aux-receive" does not create the session. It add
|
||
|
the given session id and aux element to a hashtable(key:session id,
|
||
|
value: aux element). Then when the user ask for
|
||
|
rtpbin.recv\_rtp\_sink\_i, rtpbin lookup if there is an aux element for
|
||
|
this i session id. If yes it requests a sink pad to this aux element and
|
||
|
links it with the recv\_rtp\_src pad of the new gstrtpsession. rtpbin
|
||
|
also checks that this aux element is connected only one time to
|
||
|
ssrcdemux. Because rtpauxreceive has only one source pad. Each call to
|
||
|
request rtpbin.recv\_rtp\_sink\_k will also creates
|
||
|
rtpbin.recv\_rtp\_src\_k\_ssrc\_pt as usual. So that the user have it
|
||
|
when then it requests rtpbin. (from gst-launch) or using
|
||
|
on\_rtpbinreceive\_pad\_added callback from an application.
|
||
|
|
||
|
### Requesting the rtpbin's pads on the pipeline sender side
|
||
|
|
||
|
For the sender this is similar but a bit more complicated to implement.
|
||
|
When the user asks for rtpbin.send\_rtp\_sink\_i, rtpbin will lookup in
|
||
|
its second map (key:session id, value: aux send element). If there is
|
||
|
one aux element, then it will set the sink pad of this aux sender
|
||
|
element to be the ghost pad rtpbin.send\_rtp\_sink\_i that the user
|
||
|
asked. rtpbin will also request a src pad of this aux element to connect
|
||
|
it to gstrtpsession\_i. It will automatically create
|
||
|
rtpbin.send\_rtp\_src\_i the usuall way. Then if the user asks
|
||
|
rtpbin.send\_rtp\_src\_k, then rtpbin will also lookup in that map and
|
||
|
request another source pad of the aux element and connect it to the new
|
||
|
gstrtpsession\_k.
|
||
|
|
||
|
# RTP collision design
|
||
|
|
||
|
## GstRTPCollision
|
||
|
|
||
|
Custon upstream event which contains the ssrc marked as collided.
|
||
|
|
||
|
This event is generated on both pipeline sender and receiver side by the
|
||
|
gstrtpsession element when it detects a conflict between ssrc. (same
|
||
|
session id and same ssrc)
|
||
|
|
||
|
It's an upstream event so that means this event is for now only useful
|
||
|
on pipeline sender side. Because elements generating packets with the
|
||
|
collided SSRC are placed upstream from the gstrtpsession.
|
||
|
|
||
|
## rtppayloader
|
||
|
|
||
|
When handling a GstRTPCollision event, the rtppayloader has to choose
|
||
|
another ssrc.
|
||
|
|
||
|
## BYE only the corresponding source, not the whole session.
|
||
|
|
||
|
When a collision happens for the given ssrc, the associated source is
|
||
|
marked bye. But we make sure that the whole session is not itself set
|
||
|
bye. Because internally, gstrtpsession can manages several sources and
|
||
|
all have their own distinct ssrc.
|
||
|
|
||
|
# 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 mechanism
|
||
|
|
||
|
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.
|
||
|
|
||
|
### Retransmission 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 mechanism
|
||
|
|
||
|
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.
|