RepresentationStream
Overview
The RepresentationStream
download and push segments linked to a given
Representation.
It constructs a list of segments to download, which depend on the current
playback conditions.
It then download and push them to a linked SegmentBuffer
(the media buffer
containing the segments for later decoding).
Multiple RepresentationStream
observables can be ran on the same
SegmentBuffer
without problems, as long as they are linked to different
Periods of the Manifest.
This allows for example smooth transitions between multiple periods.
Return value
The RepresentationStream
returns an Observable which emits multiple
notifications depending on what is happening at its core, like:
-
when segments are scheduled for download
-
when segments are pushed to the associated
SegmentBuffer
-
when the Manifest needs to be refreshed to obtain information on possible
-
whether the
RepresentationStream
finished to load segments until the end of the current Period. This can for example allow the creation of aRepresentationStream
for the next Period for pre-loading purposes. -
whether there are discontinuities: holes in the stream that won’t be filled by segments and can thus be skipped
Queue Algorithm
The RepresentationStream depends on a central algorithm to make sure that the right segments are scheduled for download at any time.
This algorithm constructs a queue of segments to download at any time, and regularly checks that the segment currently downloaded still corresponds to the currently most needed Segment.
This list of segments is based on a simple calculation between the current
position and the buffer size we want to achieve.
This list goes then through multiple filters to ensure we’re not queueing them
unnecessarly. Such cases would be, for example, if the segment is already
present in the SegmentBuffer
at a better quality.
For a clock based on various video events, the strategy is the following:
-
let
segmentQueue
be an empty array. -
On each clock tick, calculate
segmentsNeeded
, an Array of needed segments (read: not yet downloaded) from the current time to the buffer size goal.Note that the steps 2 to 5 can run multiple times while waiting for a request - happening in step 5 and 8. If that happens,
segmentQueue
should equal the last value it has been given. -
check if there’s a segment currently downloaded (launched in step 8)
3-1. If there is none, let segmentQueue be equal to
segmentsNeeded
3-2. If there is one but for a segment different than the first element in
segmentsNeeded
or ifsegmentsNeeded
is empty, abort this request and letsegmentQueue
be equal tosegmentsNeeded
.3-3. If there is one and is for the same segment than the first element in
segmentsNeeded
, letsegmentQueue
be equal tosegmentsNeeded
without its first element. -
if
segmentQueue
is empty, go back to 2. -
check if there’s a pending segment request (happening in step 8):
5-1. if there’s no segment request, continue
5-1. if there’s a pending segment request, go back to 2
-
Let
currentSegment
be the first segment ofsegmentQueue
-
Remove the first segment from
segmentQueue
(a.k.a.currentSegment
) -
perform a request for
currentSegment
and wait for it to finish. During this time, step 2 to 5 can run in parallel, and as suchSegmentQueue
can be mutated during this process. -
Once the request is finished, run those tasks in parallel:
9-1. Append the segment to the corresponding
SegmentBuffer
9-1. go back to step 4.