2.5. Data Transfer¶
The Teleport Data Service is the real-time transport that carries the logical streams listed in Data Service. Two transports are supported by the reference code:
WebRTC (default): five SCTP data channels for application payloads plus zero or more RTP media tracks for audio (see Audio). Recommended for all new implementations.
SRT + EFP (optional/legacy): compiled in only when
TELEPORT_SUPPORT_SRTis defined; described at the end of this page for backward compatibility.
2.5.1. Reliability requirements¶
Whichever transport is used, the protocol assumes:
Lost packets on reliable streams are resent within a bounded time window.
The receiver can cope with out-of-order packets.
The receiver can detect corrupted or incomplete payloads.
The receiver can recover – e.g. by requesting a video keyframe or marking a resource lost.
2.5.2. WebRTC transport (default)¶
2.5.2.1. Negotiation¶
A WebRTC PeerConnection is established between client and server using the
SDP/ICE exchange described in Signaling. Once the PeerConnection
has reached the connected state, five SCTP data channels are opened
and, if audio is enabled (see Audio), one or more RTP audio
tracks are negotiated in the same SDP.
Each data channel has a fixed label and a recommended numeric id. Implementations match channels by label, so any implementation must use these exact strings:
Id |
Label |
Reliable |
Ordered |
Two-way |
Notes |
|---|---|---|---|---|---|
20 |
|
No |
No |
No |
Maximum chunk size 64 KiB; payloads parsed as AVC/HEVC Annex-B. |
40 |
|
No |
No |
No |
One framed payload per video frame. |
80 |
|
Yes |
Yes |
No |
Reliable, ordered. Payloads use the parser described in Geometry Payload. |
100 |
|
Yes |
Yes |
Yes |
Carries all server-to-client commands and all reliable client-to-server messages (e.g. |
120 |
|
No |
No |
Yes |
Per-frame poses, input states, latency pong. |
Audio does not use an SCTP data channel; it is carried as WebRTC media tracks (RTP / SRTP) negotiated alongside the channels above. See Audio for the full specification including codec, multi-party fan-out, and source identification.
Note
Earlier revisions of this protocol used a sixth SCTP data channel,
label audio_server_to_client (id 60), carrying raw PCM frames.
That channel is deprecated; new implementations MUST use the
media-track transport in Audio. Servers MAY continue to open
the data channel for one release cycle to preserve backward
compatibility, but MUST NOT depend on it.
2.5.2.2. Framing¶
WebRTC SCTP data channels deliver complete messages, so no
fragmentation header is needed at the protocol level. The server splits
a payload that exceeds chunkSize (64 KiB for most channels) into
multiple SCTP messages internally; the receiver re-assembles them
before passing the payload to the decoder for that channel. From the
application’s point of view, each send corresponds to one logical
payload (a command, a message, a video frame fragment, a geometry
chunk, etc.).
2.5.2.3. Recovery¶
Video: on any frame loss, the client sends a
KeyframeRequestmessage over the unreliable channel; the server forces the encoder to emit an IDR frame. See Video.Geometry: the server only retires a resource from the pending set when the client sends a
ReceivedResourcesmessage. If the client cannot decode a payload it can sendResourceLostto force resending.Reliable channels: SCTP itself guarantees delivery; no protocol-level recovery is required.
2.5.2.4. Reliable channel: dual transport¶
Payloads carried on the reliable data channel (id 100) MAY also be
exchanged as binary WebSocket frames on the signaling connection
described in Signaling. The payload bytes are identical on both
transports; the discriminator byte at the start of every payload is
sufficient to parse it.
Implementations MUST accept reliable-channel payloads on either
transport, and MUST send reliable-channel payloads on the WebRTC
reliable data channel when it is in the open state, falling back
to a binary WebSocket frame on the signaling connection otherwise. See
Reliable-channel transport fallback (binary WebSocket frames) for the full normative rules.
This does not apply to any other data channel or to the audio
media tracks: the geometry, video, video_tags and
unreliable channels, and the audio RTP tracks described in
Audio, have no signaling-WebSocket fallback and MUST be used
over WebRTC only.
2.5.3. Legacy transport: SRT + EFP¶
Note
This transport is only built when TELEPORT_SUPPORT_SRT is set
at compile time. New implementations should use WebRTC.
The Teleport Protocol can also be carried over the Secure Reliable Transport (SRT) wrapped in the Elastic Frame Protocol (EFP). SRT provides retransmission of lost UDP packets; EFP provides framing, fragmentation and per-stream sub-addressing inside SRT.
For the Teleport XR fork of SRT, see https://github.com/teleportxr/srt. For more on SRT, see https://www.haivision.com/resources/streaming-video-definitions/srt/. For EFP, see https://github.com/teleportxr/efp.
EFP groups packets into SuperFrames. The EFP library provides
sending and receiving of these via its own header on each packet, which
contains the SuperFrame id, fragment index, and a presentation
timestamp; Teleport reuses the timestamp field as a per-stream
stream-payload-id. A corrupted SuperFrame is still delivered (with
mBroken set) so the application can request retransmission –
the same recovery mechanisms described above for the WebRTC transport
apply.
2.5.3.1. EFP frame types and SuperFrames¶
EFP adds four different types of headers of varying size to each network packet. The types used depend on the total size of the payload and the Maximum Transmission Unit (MTU) size. Each payload will either have one packet with a ElasticFrameType1 header or multiple packets with ElasticFrameType2 headers. Both of these headers contain a uint64 member called hPTS (Presentation Timestamp). This is unused by EFP but used by Teleport to store the stream-payload-id, a unique identifier for a payload. All ElasticFrameType headers contain a uint8 member named hStreamID. This uniquely identifies the stream the packet belongs to. This allows for payloads from different streams to be sent to a client in parallel. EFP header types 1, 2 and 3 are currently only used but types 0 and 4 may be used in future versions. For a detailed breakdown and explanation about the different kinds of ElasticFrameType headers, see here: https://edgeware-my.sharepoint.com/:p:/g/personal/anders_cedronius_edgeware_tv/ERnSit7j6udBsZOqkQcMLrQBpKmnfdApG3lehRk4zE-qgQ?rtime=swkDU08M2kg.
An ElasticFrameType1 is of the form:
bytes |
type |
description |
|---|---|---|
1 |
uint8 |
hFrameType=1 |
1 |
uint8 |
hStreamID |
2 |
uint16 |
hSuperFrameNo |
2 |
uint16 |
hFragmentNo |
2 |
uint16 |
hOfFragmentNo |
An ElasticFrameType2 is of the form:
bytes |
type |
description |
|---|---|---|
1 |
uint8 |
hFrameType=2 |
1 |
uint8 |
hStreamID |
1 |
ElasticFrameContent |
hDataContent |
2 |
uint16 |
hSizeOfData |
2 |
uint16 |
hSuperFrameNo |
2 |
uint16 |
hOfFragmentNo |
2 |
uint16 |
hType1PacketSize |
8 |
uint64 |
hPts |
4 |
uint32 |
hDtsPtsDiff |
4 |
uint32 |
hCode |
An ElasticFrameType3 is of the form:
bytes |
type |
description |
|---|---|---|
1 |
uint8 |
hFrameType=3 |
1 |
uint8 |
hStreamID |
2 |
uint16 |
hSuperFrameNo |
2 |
uint16 |
hType1PacketSize |
2 |
uint16 |
hOfFragmentNo |
The hDataContent member of a ElasticFrameType2 is a ElasticFrameContent enum that refers to the type of data in the stream. The ElasticFrameContent enum includes common codecs such as HEVC and H264 for video and and also allows for the content type to specified as private or custom data. The stream-data-type is a ElasticFrameContent enum and is passed to the EFP Sender by the server.
EFP SuperFrames are an EFP class that are only used on the client by the ElasticFrameProtocolReceiver class which will be referred to as the EFP Recevier . SuperFrames store all the data for a payload as well as some metadata. SuperFrames are created by the EFP Recevier when the first fragment or packet of a payload is received and updated as new packets belonging to the same payload are received. When all packets have been received, the SuperFrame is complete and passed to the receiveCallback. The mBroken member flag of the SuperFrame is set to true by the EFP Recevier when the SuperFrame is corrupted. The SuperFrame is corrupted if not all frame fragments or packets are received before the user specified timeout or if some data received is invalid. A corrupted SuperFrame will still be passed to the receiveCallback to inform the client application of the corruption. It is then up to the client application to recover from the corruption such as by requesting the same payload again. The hSuperFrameNo field in the ElasticFrameType header is used by EFP to determine what SuperFrame a packet belongs to. The EFP Sender on the server maintains this value.
A SuperFrame is of the form:
bytes |
type |
description |
|---|---|---|
8 |
uint64 |
mFrameSize - content size in bytes |
1 |
uint8* |
pFrameData - pointer to content |
1 |
ElasticFrameContent |
mDataContent - format of content |
1 |
bool |
mBroken - corrupted data flag |
8 |
uint64 |
mPts - presentation timestamp |
4 |
uint64 |
mDts - decode timestamp |
4 |
uint32 |
mCode - data format code |
1 |
uint8 |
mStreamID - stream id |
1 |
uint8 |
mSource - index of EFP Sender |
1 |
uint8 |
mFlags - superFrame slags (Unusued) |
The network packet structure is of the form:
Network Packet |
|---|
UDP |
SRT |
EFP |
Content |
2.5.3.2. Initialization of EFP on the Server¶
On the start of a client-server session, the server initializes SRT and starts listening for messages on a user specified socket. An instance of an ElasticFrameProtocolSender class is created and the MTU size is passed to the constructor This is 1450 for UDP which is the protocol SRT is built on. One EFP sender is used for each client and only one sender is needed for all data streams. After construction, the sendCallback member of the ElasticFrameProtocolSender instance is set to a user defined callback. This callback is called in the sender’s packAndSendFromPtr function for each EFP packet created. The callback’s job is to actually send the data to the client. Any network transfer protocol can be used in the callback to send the data but Teleport exclusively uses SRT. Each payload sent to the client will have a unique identifier (stream-payload-id) for each stream. This is a uint64 value that is initialized to 0 on the start of the client-server session and incremented for each stream when a new payload is sent to the client. The stream-payload-id value is written to PTS of each EFP packet header.
2.5.3.3. Sending Data from the Server¶
On each frame or application update, the server will poll SRT to check the status of the connection for each client. If the server and client are connected, the server will do the following to transfer data to the client for each stream:
The stream’s data source is checked for any available data.
The available data buffer, buffer size, stream-data-type, stream-payload-id, and stream-id are passed to the EFP sender’s packAndSendFromPtr function.
The stream-payload-id is incremented.
EFP packAndSendFromPtr function assembles the data into multiple network packets with ElasticFrameType headers as described previously.
The sendCallback is called for each packet.
The sendCallback calls the SRT API’s srt_sendmsg2 function, passing the remote socket identifier, the network packet and packet size.
This function will add the SRT and UDP headers to the network packet and send the packet to the client.
2.5.3.4. Initialization of EFP on the Client¶
On receiving the SetupCommand, the client will initialize SRT and create and configure an SRT socket for receiving network packets from the server. Subsequently, the client will create an instance of an ElasticFrameProtocolReceiver or EFP Receiver and this will remain in use until the end of the server-client-session. A SuperFrame timeout and EFPReceiverMode are passed to the EFP Receiver constructor. The SuperFrame timeout specifies the length of time EFP will wait between receiving the first and last packet of a payload before marking the SuperFrame as broken. This timeout could vary depending on the application. Teleport sets the timeout to 100ms. There are two types of EFPReceiverMode, run-to-completion and threaded. When receiving a network packet in run-to-completion mode, EFP will update the SuperFrame and call the receiveCallback on the same thread that calls the receiver’s receiveFragmentFromPtr function. When receiving a network packet in theaded mode, EFP will update the SuperFrame and call the receiveCallback on a separate worker thread. If the receiveCallback is low overhead and the target device has 4 or more cpu cores, then run-to-completion mode is the best option. Otherwise threaded mode is recommended. The receiver’s receiveCallback is then assigned to a function that accepts a SuperFrame in its signature. This function will receive a completed SuperFrame containing the server’s payload for the application to process.
2.5.3.5. Receiving Data on the Client¶
On each frame or application update, the client will use srt_connect to try make a connection to the server using the server ip and server streaming port provided in the Setup Command. If a connection has been attempted, the client will poll SRT to check the status of the connection. If the server and client are connected, the client will do the following to process incoming packets from the server:
On a separate thread, obtain network packets from the socket using SRT API srt_recvmsg function until there are no packets available. A pointer to the buffer the packet is written to is passed to the function. The function returns 1 if there is a packet available, 0 if there’s no packet available and -1 if the connection to the server has been lost. The srt_recvmsg function reverses what srt_sendmsg2 does on the server, removing the UDP and SRT headers from the network packet.
Pass each packet to the receiveFragmentFromPtr function of the EFP Receiver. This function also requires a parameter for the MTU and the EFP Receiver index which can be set to 0.
EFP will process each packet to assemble a SuperFrame and pass each completed SuperFrame to the readCallback.
The callback creates a Payload Info structure from the mPTS, pFrameSize, pFrameData (payload) and mBroken members of the SuperFrame.
The payload’s stream is identified by the mStreamID member of the SuperFrame and the Payload Info structure is written to a designated thread-safe queue for the stream.
The main thread will read the Payload Info structure from the queue. If the value of mBroken is true, the application may request a new payload. If the data is not corrupted, the payload will be passed to the corresponding decoder for the stream. .