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

[Feature Request] Add initial audio codec support #1623

Open
KunXi-Fox opened this issue Jan 3, 2025 · 13 comments
Open

[Feature Request] Add initial audio codec support #1623

KunXi-Fox opened this issue Jan 3, 2025 · 13 comments

Comments

@KunXi-Fox
Copy link

Context

Currently rx-player use the first audio codec as default audio codec:

function getFirstDeclaredMimeType(adaptation: IAdaptation): string {
const representations = adaptation.representations.filter(
(r) => r.isPlayable() !== false,
);
if (representations.length === 0) {
const noRepErr = new MediaError(
"NO_PLAYABLE_REPRESENTATION",
"No Representation in the chosen " + adaptation.type + " Adaptation can be played",
{ tracks: [toTaggedTrack(adaptation)] },
);
throw noRepErr;
}
return representations[0].getMimeTypeString();
}

If I want to use another audio codec based on user preferences, I need to manually call setAudioTrack method. This is works at most of cases except the device that not support seamless audio codec switch, which means every audio codec changes will lead to a reload.

Solution

Provide a initialAudioCodec option and change getFirstDeclaredMimeType method to based on initialAudioCodec, if no initialAudioCodec specified, then fallback to first available audio codec.

@peaBerberian
Copy link
Collaborator

Hi @KunXi-Fox,

Normally you can set the audio track based on codecs before the RxPlayer has made any choice (and thus not have a reload) by reacting to the newAvailablePeriods event. For example:

player.addEventListener("newAvailablePeriods", (periods) => {
  for (const period of periods) {
    const audioTracks = player.getAvailableAudioTracks(period.id);
    
    // use external logic to select the wanted audio track based on the codec
    const chosenAudioTrack = chooseAudioTrack(audioTracks);

    if (chosenAudioTrack) {
      player.setAudioTrack({
        periodId: period.id,
        trackId: chosenAudioTrack.id,
      });
    }
  }
});

https://developers.canal-plus.com/rx-player/doc/api/Track_Selection/setAudioTrack.html#setting-the-audio-track-as-soon-as-possible
https://developers.canal-plus.com/rx-player/doc/api/Player_Events.html#newavailableperiods

At what point do you update the audio track? If it's e.g. at the "LOADED" state it may be late because the RxPlayer already has loaded audio data (from the previous track).

@KunXi-Fox
Copy link
Author

Yeah, just as you said, I set the initial audio codec at LOADED state. I'll try set it when first newAvailablePeriods event fired and see if it solve the problem.

@KunXi-Fox
Copy link
Author

Unfortunately, seems newAvailablePeriods not been fired for single period assets.

@KunXi-Fox
Copy link
Author

availableAudioTracksChange seems works fine, Thanks.

@peaBerberian
Copy link
Collaborator

Unfortunately, seems newAvailablePeriods not been fired for single period assets.

I'm not sure if I understand the problem. newAvailablePeriods is the intended event for the initial track and quality selections and should be sent for all contents even those with a single Period (whereas availableAudioTracksChange indicate that the choice for the currently-played Period is available or has changed).

For availableAudioTracksChange the RxPlayer may have already chosen an audio track and loading it before starting that Period so it may be too late for an initial track choice.
That event is more about the menu you show to your user so he/she can select the track he/she wants from a list once it already began playing (as the user generally just cares/know about the Period that is currently playing, not about future or past ones).

You don't have a newAvailablePeriods event that is being triggered on your content?

@KunXi-Fox
Copy link
Author

KunXi-Fox commented Feb 6, 2025

Hi @peaBerberian , with newAvailablePeriods I'm able to set initial audio data, unfortunately for some reason, sometimes I saw below error, then during period switch, I notice a infinite loading.

TS: Subject already added for audio and Period 30

Do you have any idea why this happens?

@peaBerberian
Copy link
Collaborator

OK you seem to be encountering an issue.

What is the code you're calling on newAvailablePeriods?

@KunXi-Fox
Copy link
Author

KunXi-Fox commented Feb 7, 2025

something like ⬇ , one more thing is the issue seems only happen on lower end devices, e.g. LG2019, Hisense U4

function handleNewAvailablePeriods(periods) {
    for(const period of periods) {
        const allTracks = this.getAvailableAudioTracks(
            period.id
        );

        const changeToTrack = allTracks.find(track => track.representations[0]?.codec === this.currentCodec);

        if (changeToTrack) {
            this.setAudioTrack({
                periodId: period.id,
                trackId: changeToTrack.id.toString(),
                switchingMode: changeToTrack.active ? 'seamless' : 'reload',
            })
        }
    }
}

@peaBerberian
Copy link
Collaborator

Even if we shouldn't have a problem with that, you shouldn't have to call setAudioTrack if that audio track is already set to active.

Can you retry without that? I check in the meantime if I can see what happens.

@KunXi-Fox
Copy link
Author

The issue still there even prevent call setAudioTrack when track is active. I add log for call track, hopefully it will provide some information.

The step I reproduce the issue is:

  1. Play a multiple periods + multiple audio track asset
  2. After at least one period switch, then switch to another audio track (use reload way)
  3. During period switch, the issue will happens in most of cases.
Image Image

rx-player.es5.js.zip

@peaBerberian
Copy link
Collaborator

OK thanks.

It could also be helpful to have the full debug logs for that scenario. Can you set:

RxPlayer.LogLevel = "DEBUG"
RxPlayer.LogFormat = "full"

Reproduce and communicate back logs?

peaBerberian added a commit that referenced this issue Feb 7, 2025
…cated

As #1623 reported, there seem to be an issue seen on lower-end
devices where the `TracksStore` gets multiple track references for the
same buffer type (`audio`, `video`...) and Period combination.

This should __NEVER__ happen as per the `TracksStore` API, there should
only be one reference linked to the `TracksStore` for any such
combination - if the external logic wanted to update the track
reference, it had to call `removeTrackReference` with the previous one
(or `resetPeriodObjects` to just reset everything) before adding the new
one.

Turns out it does happen in some unknown and difficult to reproduce
scenario. I guess a race condition allows for some cleaning-up phase to
be lost somewhere (either a reset is being skipped - which mostly happens
when `RELOADING`, or either some `PeriodStream` cleaning is being missed,
which happens when Period switching).

So what I propose here isn't a real fix for that problem, but to add
some resilience into the TracksStore by considering the last track
reference for a buffer type + Period combination - which IMO
makes more sense API-wise than keeping the first one and should "fix"
cases of infinite rebuffering (as I suppose some code awaited
indefinitely that a track choice be performed through that new track
reference).

The absence of clearing of the previous reference still indicates a
leak (though the leak should not matter anymore for the `TracksStore`
once the new one is added) and still indicate that something went
wrong somewhere, so I kept the `log.error` call which will help us
debug other issues that may have their source in that same weird
race condition.

I'm still looking how such race condition is even possible in the
meantime.
peaBerberian added a commit that referenced this issue Feb 7, 2025
…cated

As #1623 reported, there seem to be an issue seen on lower-end
devices where the `TracksStore` gets multiple track references for the
same buffer type (`audio`, `video`...) and Period combination.

This should __NEVER__ happen as per the `TracksStore` API, there should
only be one reference linked to the `TracksStore` for any such
combination - if the external logic wanted to update the track
reference, it had to call `removeTrackReference` with the previous one
(or `resetPeriodObjects` to just reset everything) before adding the new
one.

Turns out it does happen in some unknown and difficult to reproduce
scenario. I guess a race condition allows for some cleaning-up phase to
be lost somewhere (either a reset is being skipped - which mostly happens
when `RELOADING`, or either some `PeriodStream` cleaning is being missed,
which happens when Period switching).

So what I propose here isn't a real fix for that problem, but to add
some resilience into the TracksStore by considering the last track
reference for a buffer type + Period combination - which IMO
makes more sense API-wise than keeping the first one and should "fix"
cases of infinite rebuffering (as I suppose some code awaited
indefinitely that a track choice be performed through that new track
reference).

The absence of clearing of the previous reference still indicates a
leak (though the leak should not matter anymore for the `TracksStore`
once the new one is added) and still indicate that something went
wrong somewhere, so I kept the `log.error` call which will help us
debug other issues that may have their source in that same weird
race condition.

I'm still looking how such race condition is even possible in the
meantime.
@KunXi-Fox
Copy link
Author

Hi @peaBerberian , below was the detail log:

1738983734378.log

@KunXi-Fox
Copy link
Author

BTW, this PR did fix the infinite loading issue even when I saw the error on console.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants