Fix infinite buffering when locking audio/video on *TrackChange or periodChange events #1606
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
An application signaled to us that there was infinite rebuffering happening when calling the
lockVideoRepresentation
API directly in avideoTrackChange
orperiodChange
event listener.It's actually a sort of reentrancy issue:
The
periodChange
ANDvideoTrackChange
events are sent as soon as all initial video+audio+text Representations are kown for the current Period.The RxPlayer knows when it is the case when its
AdaptationStream
modules call theirrepresentationChange
callback (a sort of event emitter system) for each type of buffer (video, audio and text).All this happens synchronously: as soon as the last of the 3
representationChange
callback is called, we're emitting bothvideoTrackChange
andperiodChange
events.The application calls the
lockVideoRepresentations
API synchronously after step1
by listening to the aforementioned eventsThat
lockVideoRepresentations
call leads to a different choice ofRepresentations
, so theAdaptationStream
re-call the function that calls therepresentationChange
callback (note that neitherperiodChange
norvideoTrackChange
are re-sent here, we know that we already sent both).As everything was called synchronously here, we continue the execution of the corresponding
AdaptationStream
's function called in step3
but also, once this is done, the execution of that same function in step1
(with the previous Representation's context).So technically, the application and the RxPlayer want the Representation set in step
3
, but for theAdaptationStream
, it most recently did work to have the one wanted in step1
.What's worse is that some parts of the RxPlayer knew it was the one in step
3
that was wanted, yet other parts wanted to load the one in step1
, leading to nothing being done in the end (video segment requests were cancelled and not restarting).We know very well this type of issues because a media player is heavy on events handlers we do not control (i.e. in which the application may do what it wants), but it turns out that we relied on the wrong "
CancellationSignal
" here.The
CancellationSignal
is the main way with which we protect the code against this type of issue by "activating" one when we want to cancel the execution of some logic (here the one from step1
).Here we were, after the
representationChange
call from step1
, checking theCancellationSignal
which is linked to the wholeAdaptationStream
module. As theAdaptationStream
is not interrupted by a change ofRepresentation
, that signal was not activated here.What we probably wanted instead was to rely on
fnCancelSignal
, which is the one linked to the scope of that function.