Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix infinite buffering when locking audio/video on *TrackChange or periodChange events #1606

Merged
merged 2 commits into from
Dec 19, 2024

Conversation

peaBerberian
Copy link
Collaborator

An application signaled to us that there was infinite rebuffering happening when calling the lockVideoRepresentation API directly in a videoTrackChange or periodChange event listener.

It's actually a sort of reentrancy issue:

  1. The periodChange AND videoTrackChange 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 their representationChange 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 both videoTrackChange and periodChange events.

  2. The application calls the lockVideoRepresentations API synchronously after step 1 by listening to the aforementioned events

  3. That lockVideoRepresentations call leads to a different choice of Representations, so the AdaptationStream re-call the function that calls the representationChange callback (note that neither periodChange nor videoTrackChange are re-sent here, we know that we already sent both).

  4. As everything was called synchronously here, we continue the execution of the corresponding AdaptationStream's function called in step 3 but also, once this is done, the execution of that same function in step 1 (with the previous Representation's context).

  5. So technically, the application and the RxPlayer want the Representation set in step 3, but for the AdaptationStream, it most recently did work to have the one wanted in step 1.

    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 step 1, 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 step 1).

Here we were, after the representationChange call from step 1, checking the CancellationSignal which is linked to the whole AdaptationStream module. As the AdaptationStream is not interrupted by a change of Representation, 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.

…riodChange events

An application signalled to us that there was infinite rebuffering
happening when calling the `lockVideoRepresentation` API directly in a
`videoTrackChange` or `periodChange` event listener.

It's actually a kind of reentrancy issue:

  1. The `periodChange` AND `videoTrackChange` events are sent as soon
     as all video+audio+text Representations are chosen for the current
     Period.

     The RxPlayer knows when it is the case when its `AdaptationStream`
     module call its `representationChange` 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` is called, we're emitting both
     `videoTrackChange` and `periodChange` events.

  2. The application calls the `lockVideoRepresentations` API
     synchronously after step `1` by listening to the aforementioned
     events

  3. That `lockVideoRepresentations` call leads to a different choice of
     `Representations`, so the `AdaptationStream` re-call the function
     that calls the `representationChange` callback (note that neither
     `periodChange` nor `videoTrackChange` are re-sent here, we know
     that we already sent both).

  4. As everything was called synchronously here, we continue the
     execution of the corresponding `AdaptationStream`'s' function
     called in step `3` **but also**, when this is done, the execution
     of that same function in step `1` (with the previous Representation's
     context).

  5. So technically, the application and the RxPlayer wants the
     Representation set in step `3`, but for the `AdaptationStream`, it most
     recently did work to have the one wanted in step `1`.

     What's worse is that some part of the RxPlayer knew it was the one
     in step `3` that was wanted, yet other part wanted to load the one
     in step `1`, 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 a function outside of it.

Here we were, after the `representationChange` call, checking the
`CancellationSignal` which is linked to the whole `AdaptationStream`
module. As the `AdaptationStream` is not interrupted by a change of
`Representation`, 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.
@peaBerberian peaBerberian added this to the 4.3.0 milestone Dec 18, 2024
@peaBerberian peaBerberian added the bug This is an RxPlayer issue (unexpected result when comparing to the API) label Dec 18, 2024
Copy link
Collaborator

@Florent-Bouisset Florent-Bouisset left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch 👍

@peaBerberian
Copy link
Collaborator Author

peaBerberian commented Dec 18, 2024

I think integration tests will be easy for that one (just call lockVideoRepresentations on a videoTrackChange and periodChange events and see if it plays) so I'm keeping it open until I wrote them

@peaBerberian peaBerberian merged commit 2ca74e6 into dev Dec 19, 2024
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This is an RxPlayer issue (unexpected result when comparing to the API)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants