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

Feat: add an option to play without audio or video if audio or video failed #1624

Open
wants to merge 21 commits into
base: dev
Choose a base branch
from

Conversation

Florent-Bouisset
Copy link
Collaborator

Currently, the player throws an error if either all audio or all video tracks are not playable. This PR introduces two new options, onAudioTrackNotPlayable and onVideoTrackNotPlayable, to the loadVideo method. These options allow the player to continue playback even when all audio or video tracks are not playable. In such cases, the content will play without audio or video, respectively, instead of failing.

API

loadVideo(options) options object has two more keys:

  • [optional]onAudioTrackNotPlayable : "continue" or "error". Default if not set: "continue"
  • [optional]onVideoTrackNotPlayable : "continue" or "error". Default if not set: "continue"

Usage

  player.loadVideo({
    transport: "dash",
    url: "https://dash.akamaized.net/dash264/TestCasesDolby/1/Living_Room_1080p_20_96k_25fps.mpd",
    autoPlay: true,
    onAudioTrackNotPlayable: "continue",
    onVideoTrackNotPlayable: "error",
  });

Example

https://codesandbox.io/p/sandbox/amazing-pare-kdf65f

@Florent-Bouisset Florent-Bouisset added the API Relative to the RxPlayer's API label Jan 6, 2025
@Florent-Bouisset Florent-Bouisset force-pushed the feat/play-without-audio-or-video branch from 724b4a3 to 5a073f9 Compare January 6, 2025 09:30
@Florent-Bouisset
Copy link
Collaborator Author

relates to #1602

@peaBerberian
Copy link
Collaborator

onAudioTrackNotPlayable and onVideoTrackNotPlayable

Shouldn't it be "tracks" here?

Default if not set: "continue"

Doesn't it break the API this way? Its complex but it changes the default behavior on which some applications may rely? Wouldn't it be safer to default it to "error"?

@Florent-Bouisset
Copy link
Collaborator Author

onAudioTrackNotPlayable

Yes I think I should default it to "error" instead.

@Florent-Bouisset
Copy link
Collaborator Author

onAudioTrackNotPlayable and onVideoTrackNotPlayable

Shouldn't it be "tracks" here?

Default if not set: "continue"

Doesn't it break the API this way? Its complex but it changes the default behavior on which some applications may rely? Wouldn't it be safer to default it to "error"?

I have updated it.

@Florent-Bouisset Florent-Bouisset force-pushed the feat/play-without-audio-or-video branch from ef25c83 to 4b6acd7 Compare January 9, 2025 10:39
@peaBerberian
Copy link
Collaborator

peaBerberian commented Jan 9, 2025

As this adds a similar logic in the TrackStore + Manifest + Init, and as the manifest is more or less intended to just regroup metadata, I'm wondering if it would not be easier to follow if that option was mainly handled by a part of the Init (and TrackStore I guess for its own thing) : Like the Manifest would parse the content regardless, and the Init would investigate the issues seen while parsing and decide whether to error or warn and continue based on its options.

What do you think?

@Florent-Bouisset Florent-Bouisset force-pushed the feat/play-without-audio-or-video branch 2 times, most recently from 5e29ebe to dc7bfe7 Compare January 31, 2025 09:37
@canalplus canalplus deleted a comment from github-actions bot Feb 12, 2025
Copy link

Automated performance checks have been performed on commit 1b34a6119995d99e0732abd3996f0fce7e07590d with the base branch dev.

Tests results

✅ Tests have passed.

Performance tests 1st run output

No significative change in performance for tests:

Name Mean Median
loading 21.10ms -> 21.14ms (-0.037ms, z: 0.38653) 29.25ms -> 29.25ms
seeking 23.68ms -> 20.27ms (3.405ms, z: 1.50470) 11.70ms -> 11.55ms
audio-track-reload 28.00ms -> 28.13ms (-0.129ms, z: 0.56610) 40.80ms -> 40.95ms
cold loading multithread 51.53ms -> 50.05ms (1.481ms, z: 7.90735) 74.70ms -> 73.50ms
seeking multithread 30.21ms -> 26.14ms (4.070ms, z: 0.96041) 10.50ms -> 10.50ms
audio-track-reload multithread 27.91ms -> 27.77ms (0.140ms, z: 1.45816) 41.10ms -> 40.80ms
hot loading multithread 18.11ms -> 18.14ms (-0.026ms, z: 0.84623) 26.40ms -> 26.40ms

If you want to skip performance checks for latter commits, add the skip-performance-checks label to this Pull Request.

@Florent-Bouisset Florent-Bouisset force-pushed the feat/play-without-audio-or-video branch from 1b34a61 to d11562d Compare February 19, 2025 16:45
Copy link

Automated performance checks have been performed on commit d11562dbaee84888c15cb1f7b7ed4e4e3b8ba66e with the base branch dev.

Tests results

✅ Tests have passed.

Performance tests 1st run output

No significative change in performance for tests:

Name Mean Median
loading 19.27ms -> 19.31ms (-0.049ms, z: 0.03714) 26.55ms -> 26.40ms
seeking 15.23ms -> 10.58ms (4.641ms, z: 0.14786) 11.10ms -> 11.10ms
audio-track-reload 26.13ms -> 26.17ms (-0.042ms, z: 0.58250) 37.95ms -> 37.95ms
cold loading multithread 47.53ms -> 46.67ms (0.864ms, z: 13.13265) 69.45ms -> 68.10ms
seeking multithread 20.57ms -> 15.20ms (5.369ms, z: 1.28316) 10.35ms -> 10.35ms
audio-track-reload multithread 25.93ms -> 25.87ms (0.062ms, z: 1.08056) 38.10ms -> 37.95ms
hot loading multithread 16.27ms -> 15.99ms (0.282ms, z: 4.61710) 23.70ms -> 23.25ms

If you want to skip performance checks for latter commits, add the skip-performance-checks label to this Pull Request.

@peaBerberian peaBerberian added the Priority: 2 (Medium) This issue or PR has a medium priority. label Feb 26, 2025
Copy link

Automated performance checks have been performed on commit b1f4c12bc8cb49a7598cfcfc5473edbac5ee5817 with the base branch dev.

Tests results

✅ Tests have passed.

Performance tests 1st run output

No significative change in performance for tests:

Name Mean Median
loading 20.49ms -> 20.58ms (-0.097ms, z: 1.01770) 28.35ms -> 28.20ms
seeking 21.60ms -> 24.85ms (-3.252ms, z: 1.61675) 11.55ms -> 11.55ms
audio-track-reload 27.73ms -> 27.40ms (0.329ms, z: 2.29041) 40.20ms -> 39.90ms
cold loading multithread 49.84ms -> 48.97ms (0.875ms, z: 11.40648) 72.90ms -> 71.40ms
seeking multithread 36.17ms -> 28.17ms (8.001ms, z: 0.23911) 10.50ms -> 10.50ms
audio-track-reload multithread 27.54ms -> 27.33ms (0.211ms, z: 3.37909) 40.35ms -> 40.05ms
hot loading multithread 17.51ms -> 17.44ms (0.067ms, z: 1.93997) 25.40ms -> 25.20ms

If you want to skip performance checks for latter commits, add the skip-performance-checks label to this Pull Request.

tracks are playable.

- If neither the audio nor video tracks are playable, an error will be thrown regardless
of this setting.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Prettier did not seem to include the end of this sentence in the same list element (it misses indentation) - I don't know how it will render once transformed into HTML

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also, maybe we should tell here the error code in question, so that application developers can know how to handle those without having to reproduce it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Because this is inside a

I think the indentation does not matter (prettier does not want to add a tab)

I have added the error code

tracks are playable.

- If neither the audio nor video tracks are playable, an error will be thrown regardless
of this setting.
Copy link
Collaborator

Choose a reason for hiding this comment

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

also here


- `id`: (`"string"`): The ID of the period.

- `start`: (`"number"`): The start time of the period.
Copy link
Collaborator

Choose a reason for hiding this comment

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

First there's a line break between list elements then there's not :O, we must decide!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed


- `start`: (`"number"`): The start time of the period.
- `end`: (`"number" | undefined`): The end time of the period. (may be `undefined` if no
end time is set).
Copy link
Collaborator

Choose a reason for hiding this comment

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

More generally, end is also undefined for live contents where there's no end for now, which is a frequent case.

{ tracks: undefined },
);
}
hasSupportedMedia[ttype] = hasSupportedAdaptations;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is hasSupportedMedia used at all here?

@@ -153,7 +153,7 @@ export default function generateManifestParser(
return Promise.reject(cancelSignal.cancellationError);
}
const warnings: IPlayerError[] = [];
Copy link
Collaborator

Choose a reason for hiding this comment

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

From what I understand, warnings isn't used anywhere anymore? We could remove it from the transports API

// there is no track of this type to select.
log.debug(
`TS: The period does not have adaptation for ${ttype} there is no track to choose`,
);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm wondering if having this debug log for each Period without some text AdaptationSet at each update wouldn't be too much

if (
adaptationsForType.every((adapt) => adapt.supportStatus.hasSupportedCodec === false)
) {
const firstPlayableAdaptation = findFirstPlayableAdaptation(period, ttype);
Copy link
Collaborator

@peaBerberian peaBerberian Feb 27, 2025

Choose a reason for hiding this comment

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

I don't get it, how could firstPlayableAdaptation be set to anything other than undefined here, didn't we just check that no adaptation of that type is supported in that Period?

What possibility am I missing?

{ tracks: undefined },
);

if (
Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems that in this particular case, we do not have the noPlayableTrack event.

Is that wanted? From reading the API documentation, I would think that this case would lead to that event.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Though after doing more thinking, it seems that sending the noPlayableTrack event here might not work because we might not have advertised the Period yet through a newAvailablePeriods event, no?

textTypeInfo.storedSettings = storedSettings;
textTypeInfo.dispatcher?.updateTrack(storedSettings);
}
return;
Copy link
Collaborator

@peaBerberian peaBerberian Feb 27, 2025

Choose a reason for hiding this comment

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

By returning early, we don't send the trackUpdate event

if (this._isDisposed) {
return; // Someone disposed the `TracksStore` on the previous side-effect
}
this.trigger("error", noRepErr);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Note: I don't remember if we exchanged about this, but this means that we throw even if the application e.g. disable the video track on a noPlayableTrack event for video content - where it could theoretically play under the wanted settings.

I'm OK with this behavior, it's just to check whether we're on the same page on this.

if (isNullOrUndefined(typeInfo) || typeInfo.storedSettings !== storedSettings) {
return;
}
typeInfo.dispatcher?.updateTrack(storedSettings);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Aren't we in the risk of setting null for the video track when there's already no audio track and vice-versa here? Shouldn't we also check for the NO_AUDIO_VIDEO_TRACKS error?

};
const audioAdaptation = getSupportedAdaptations(period, "audio")[0] as
| IAdaptationMetadata
| undefined;
Copy link
Collaborator

Choose a reason for hiding this comment

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

The as is just to indicate that the value may be undefiined?
There may be better ways no? The problem I see with as is possible bugs if we make code changes while trusting TypeScript a little too much to save our back

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes it's just to indicate it could be undefined as typescript does not know that accessing through indexing can be undefined. I found a more clever way to handle it.
Note that using Array.at() correctly infer the type but this is ES2022 so I didn't use it there.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm wondering if TypeScript automatically translate at calls

if (textTypeInfo !== undefined) {
const storedSettings = null;
textTypeInfo.storedSettings = storedSettings;
textTypeInfo.dispatcher?.updateTrack(storedSettings);
Copy link
Collaborator

Choose a reason for hiding this comment

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

No need to do this if it was already null before no?

| undefined;
if (audioAdaptation === undefined) {
trackStorePeriod.audio.storedSettings = null;
this.onNoPlayableRepresentation(period, "audio");
Copy link
Collaborator

Choose a reason for hiding this comment

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

/!\ I'm under the impression that this method may lead to noPlayableTrack events which communicate about Periods not yet advertised through newAvailablePeriods events, so the application is generally not supposed to know about them in the order of events we had until now :/

@Florent-Bouisset Florent-Bouisset force-pushed the feat/play-without-audio-or-video branch from 12adc49 to e9717dc Compare March 4, 2025 09:20
@Florent-Bouisset Florent-Bouisset force-pushed the feat/play-without-audio-or-video branch from e9717dc to 40f9dfa Compare March 4, 2025 13:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API Relative to the RxPlayer's API Priority: 2 (Medium) This issue or PR has a medium priority.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants