-
-
Notifications
You must be signed in to change notification settings - Fork 229
Description
Last revised: Oct 2023
Problem
Currently, when we receive a packet, we first try to find an existing session and route the packet to it, then try to create a new session from the packet, and if both of these failed, we drop the packet.
We create sessions only when a source packet arrives and thus drop repair packets preceding the first delivered source packet. This may decrease the service quality at the session start a bit.
When we'll add RTCP and RTSP support, we might want to be able to disable session auto-creation at all and allow only explicitly announced sessions. In this case we will drop data packets until negotiation completes.
When we'll also add support for multiple sender ports, allowing different streams of the same session to have different source addresses, we will not be able to match the streams of the same session until we receive instructions from RTCP or RTSP. So in this case we will also drop data packets until negotiation completes.
When we'll add support for dynamic payload type, we also will not be able to handle and will drop packets until session negotiation completes.
In other words, there are numerous reasons why we will drop packets until session negotiation completes or, if it is disabled, until the first source packet arrives.
On the other hand, session negotiation may take time (RTSP connection establishment requires a few round-trips; RTCP packets may be lost and are not retried immediately).
In view of the above, this means that we will always drop some packets at the beginning of the session and so increase the "cold" (startup) latency.
Solution
The following simple solution is suggested, which solves all these problems:
- when a packet can't be routed, don't drop it, but instead place it into a ring buffer
- when a session is created, fetch all matching packets from the ring buffer and pass to the session
The ring buffer capacity should be enough to hold packets for max_latency * max_sessions
samples.
Implementation
-
Add a ring buffer to pipeline::ReceiverSessionGroup; we can use
core::List<packet::Packet>
as a container because we don't need random access. -
Find the place where ReceiverSessionGroup::route_packet() drops packets, and make it store the packet inside the ring buffer instead (add the packet to the end of the buffer; if the buffer capacity exceeds the limit, remove the very first packet from it).
-
Find the place where ReceiverSessionGroup creates a new session and, after creating the session, iterate over the ring buffer and try to pass every packet to the newly created session (sess->handle(packet)); if the session accepts the packet, remove it from the ring buffer.
Tests
We should cover this feature in receiver unit tests: https://github.com/roc-streaming/roc-toolkit/blob/master/src/tests/roc_pipeline/test_receiver_source.cpp
The following cases come into my mind:
-
N repair packets (N = ring_buffer_size) for the session A arrive before source packets and then a source packet arrives and the session is created; the newly created session should receive all the N repair packets
-
N repair packets (N > ring_buffer_size) for the session A arrive before source packets and then a source packet arrives and the session is created; the new session should receive only ring_buffer_size repair packets
-
N repair packets (N = ring_buffer_size) for the session A arrive before source packets, then K repair packets (K < ring_buffer_size) for the session B arrive, then source packets arrive for sessions A and B; the session B should receive all the K repair packets, and the session A should not receive any repair packets
Metadata
Metadata
Assignees
Labels
Type
Projects
Status