Skip to content

Commit 28dbe1f

Browse files
feat: let the possibilty to play without audio or video
This add "onAudioTracksNotPlayable" and "onVideoTracksNotPlayable" that defines the behavior of the player upon encountering a track that is not playable. With value "continue", the player will play the content without the unsupported track type. With value "error", the player will trigger an error. (behavior before this change)
1 parent c7af987 commit 28dbe1f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1624
-940
lines changed

demo/scripts/components/Options/TrackSwitch.tsx

Lines changed: 91 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,45 +13,67 @@ function TrackSwitchConfig({
1313
onDefaultAudioTrackSwitchingModeChange,
1414
onCodecSwitchChange,
1515
onEnableFastSwitchingChange,
16+
onAudioTracksNotPlayable,
17+
onAudioTracksNotPlayableChange,
18+
onVideoTracksNotPlayable,
19+
onVideoTracksNotPlayableChange,
1620
}: {
1721
defaultAudioTrackSwitchingMode: string;
1822
onDefaultAudioTrackSwitchingModeChange: (newVal: string) => void;
1923
enableFastSwitching: boolean;
2024
onCodecSwitch: string;
2125
onCodecSwitchChange: (val: string) => void;
2226
onEnableFastSwitchingChange: (val: boolean) => void;
27+
onAudioTracksNotPlayable: string;
28+
onAudioTracksNotPlayableChange: (val: string) => void;
29+
onVideoTracksNotPlayable: string;
30+
onVideoTracksNotPlayableChange: (val: string) => void;
2331
}): React.JSX.Element {
24-
let defaultAudioTrackSwitchingModeDescMsg;
25-
switch (defaultAudioTrackSwitchingMode) {
26-
case "reload":
27-
defaultAudioTrackSwitchingModeDescMsg =
28-
"Reloading by default when the audio track is changed";
29-
break;
30-
case "direct":
31-
defaultAudioTrackSwitchingModeDescMsg =
32-
"Directly audible transition when the audio track is changed";
33-
break;
34-
case "seamless":
35-
defaultAudioTrackSwitchingModeDescMsg =
36-
"Smooth transition when the audio track is changed";
37-
break;
38-
default:
39-
defaultAudioTrackSwitchingModeDescMsg = "Unknown value";
40-
break;
41-
}
32+
const defaultAudioTrackSwitchingModeDescMsg = React.useMemo(() => {
33+
switch (defaultAudioTrackSwitchingMode) {
34+
case "reload":
35+
return "Reloading by default when the audio track is changed";
36+
case "direct":
37+
return "Directly audible transition when the audio track is changed";
38+
case "seamless":
39+
return "Smooth transition when the audio track is changed";
40+
default:
41+
return "Unknown value";
42+
}
43+
}, [defaultAudioTrackSwitchingMode]);
4244

43-
let onCodecSwitchDescMsg;
44-
switch (onCodecSwitch) {
45-
case "reload":
46-
onCodecSwitchDescMsg = "Reloading buffers when the codec changes";
47-
break;
48-
case "continue":
49-
onCodecSwitchDescMsg = "Keeping the same buffers even when the codec changes";
50-
break;
51-
default:
52-
onCodecSwitchDescMsg = "Unknown value";
53-
break;
54-
}
45+
const onCodecSwitchDescMsg = React.useMemo(() => {
46+
switch (onCodecSwitch) {
47+
case "reload":
48+
return "Reloading buffers when the codec changes";
49+
case "continue":
50+
return "Keeping the same buffers even when the codec changes";
51+
default:
52+
return "Unknown value";
53+
}
54+
}, [onCodecSwitch]);
55+
56+
const onAudioTracksNotPlayableDescMsg = React.useMemo(() => {
57+
switch (onAudioTracksNotPlayable) {
58+
case "error":
59+
return "Throw an error if no audio track can be played";
60+
case "continue":
61+
return "Continue with video only if no audio track is playable";
62+
default:
63+
return "Unknown value";
64+
}
65+
}, [onAudioTracksNotPlayable]);
66+
67+
const onVideoTracksNotPlayableDescMsg = React.useMemo(() => {
68+
switch (onVideoTracksNotPlayable) {
69+
case "error":
70+
return "Throw an error if no video track can be played";
71+
case "continue":
72+
return "Continue with audio only if no video track is playable";
73+
default:
74+
return "Unknown value";
75+
}
76+
}, [onVideoTracksNotPlayable]);
5577

5678
const onCodecSwitchSelection = React.useCallback(
5779
({ value }: { value: string }) => onCodecSwitchChange(value),
@@ -63,6 +85,16 @@ function TrackSwitchConfig({
6385
[onDefaultAudioTrackSwitchingModeChange],
6486
);
6587

88+
const onAudioTracksNotPlayableSelection = React.useCallback(
89+
({ value }: { value: string }) => onAudioTracksNotPlayableChange(value),
90+
[onAudioTracksNotPlayableChange],
91+
);
92+
93+
const onVideoTracksNotPlayableSelection = React.useCallback(
94+
({ value }: { value: string }) => onVideoTracksNotPlayableChange(value),
95+
[onVideoTracksNotPlayableChange],
96+
);
97+
6698
return (
6799
<>
68100
<li>
@@ -109,6 +141,35 @@ function TrackSwitchConfig({
109141
</Select>
110142
<span className="option-desc">{onCodecSwitchDescMsg}</span>
111143
</li>
144+
<li className="featureWrapperWithSelectMode">
145+
<Select
146+
ariaLabel="Selecting the onAudioTracksNotPlayable attribute"
147+
disabled={false}
148+
className="playerOptionInput"
149+
name="onAudioTracksNotPlayable"
150+
onChange={onAudioTracksNotPlayableSelection}
151+
selected={{ value: onAudioTracksNotPlayable, index: undefined }}
152+
options={["continue", "error"]}
153+
>
154+
On Audio Tracks Not Playable
155+
</Select>
156+
<span className="option-desc">{onAudioTracksNotPlayableDescMsg}</span>
157+
</li>
158+
159+
<li className="featureWrapperWithSelectMode">
160+
<Select
161+
ariaLabel="Selecting the onVideoTracksNotPlayable attribute"
162+
disabled={false}
163+
className="playerOptionInput"
164+
name="onVideoTracksNotPlayable"
165+
onChange={onVideoTracksNotPlayableSelection}
166+
selected={{ value: onVideoTracksNotPlayable, index: undefined }}
167+
options={["continue", "error"]}
168+
>
169+
On Video Tracks Not Playable
170+
</Select>
171+
<span className="option-desc">{onVideoTracksNotPlayableDescMsg}</span>
172+
</li>
112173
</>
113174
);
114175
}

demo/scripts/controllers/Settings.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ function Settings({
6969
checkManifestIntegrity,
7070
requestConfig,
7171
onCodecSwitch,
72+
onAudioTracksNotPlayable,
73+
onVideoTracksNotPlayable,
7274
} = loadVideoOptions;
7375
const cmcdCommunicationMethod = cmcd?.communicationType ?? "disabled";
7476
const { manifest: manifestRequestConfig, segment: segmentRequestConfig } =
@@ -301,6 +303,30 @@ function Settings({
301303
[updateLoadVideoOptions],
302304
);
303305

306+
const onAudioTracksNotPlayableChange = useCallback(
307+
(value: string) => {
308+
updateLoadVideoOptions((prevOptions) => {
309+
if (value === prevOptions.onAudioTracksNotPlayable) {
310+
return prevOptions;
311+
}
312+
return Object.assign({}, prevOptions, { onAudioTracksNotPlayable: value });
313+
});
314+
},
315+
[updateLoadVideoOptions],
316+
);
317+
318+
const onVideoTracksNotPlayableChange = useCallback(
319+
(value: string) => {
320+
updateLoadVideoOptions((prevOptions) => {
321+
if (value === prevOptions.onVideoTracksNotPlayable) {
322+
return prevOptions;
323+
}
324+
return Object.assign({}, prevOptions, { onVideoTracksNotPlayable: value });
325+
});
326+
},
327+
[updateLoadVideoOptions],
328+
);
329+
304330
const onWantedBufferAheadChange = useCallback(
305331
(wantedBufferAhead: number) => {
306332
updatePlayerOptions((prevOptions) => {
@@ -424,6 +450,10 @@ function Settings({
424450
}
425451
onEnableFastSwitchingChange={onEnableFastSwitchingChange}
426452
onCodecSwitchChange={onCodecSwitchChange}
453+
onAudioTracksNotPlayable={onAudioTracksNotPlayable}
454+
onAudioTracksNotPlayableChange={onAudioTracksNotPlayableChange}
455+
onVideoTracksNotPlayable={onVideoTracksNotPlayable}
456+
onVideoTracksNotPlayableChange={onVideoTracksNotPlayableChange}
427457
/>
428458
</Option>
429459
<Option title="Buffer Options">

demo/scripts/lib/defaultOptionsValues.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ const defaultOptionsValues = {
3131
},
3232
},
3333
onCodecSwitch: "continue",
34+
onAudioTracksNotPlayable: "error",
35+
onVideoTracksNotPlayable: "error",
3436
},
3537
} satisfies {
3638
player: IConstructorOptions;

demo/scripts/modules/player/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ export interface IPlayerModuleState {
138138
livePosition: null | undefined | number;
139139
maximumPosition: null | undefined | number;
140140
minimumPosition: null | undefined | number;
141+
onAudioTracksNotPlayable: "continue" | "error";
142+
onVideoTracksNotPlayable: "continue" | "error";
141143
playbackRate: number;
142144
/** Try to play contents in "multithread" mode when possible. */
143145
relyOnWorker: boolean;
@@ -196,6 +198,8 @@ const PlayerModule = declareModule(
196198
livePosition: undefined,
197199
maximumPosition: undefined,
198200
minimumPosition: undefined,
201+
onAudioTracksNotPlayable: "error",
202+
onVideoTracksNotPlayable: "error",
199203
playbackRate: 1,
200204
relyOnWorker: false,
201205
useWorker: false,
@@ -289,6 +293,8 @@ const PlayerModule = declareModule(
289293
{
290294
mode: state.get("relyOnWorker") ? "auto" : "main",
291295
textTrackElement,
296+
onAudioTracksNotPlayable: state.get("onAudioTracksNotPlayable"),
297+
onVideoTracksNotPlayable: state.get("onVideoTracksNotPlayable"),
292298
},
293299
arg,
294300
) as ILoadVideoOptions,

doc/api/Loading_a_Content.md

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,61 @@ Those are the possible values for that option:
706706
More information about the `"RELOADING"` state can be found in
707707
[the player states documentation](./Player_States.md).
708708

709+
### onAudioTracksNotPlayable
710+
711+
_type_: `string|undefined`
712+
713+
_defaults_: `"error"`
714+
715+
Specifies the behavior when all audio tracks are not playable - This can occur if the
716+
device does not support the required audio codecs, or if the content cannot be decrypted,
717+
for example, due to an insufficient security level.
718+
719+
Those are the possible values for that option:
720+
721+
- `"continue"`: The player will proceed to play the content without audio.
722+
723+
- `"error"`: The player will throw an error to indicate that the audio tracks could not be
724+
played.
725+
726+
<div class="note">
727+
728+
- An event [`noPlayableTrack`](../api/Loading_a_Content) will be emitted if no audio
729+
tracks are playable.
730+
731+
- If neither the audio nor video tracks are playable, a `"NO_AUDIO_VIDEO_TRACKS"` error
732+
will be thrown regardless of this setting.
733+
734+
</div>
735+
736+
### onVideoTracksNotPlayable
737+
738+
_type_: `string|undefined`
739+
740+
_defaults_: `"error"`
741+
742+
Specifies the behavior when all video tracks are not playable - This can occur if the
743+
device does not support the required video codecs, or if the content cannot be decrypted,
744+
for example, due to an insufficient security level.
745+
746+
Those are the possible values for that option:
747+
748+
- `"continue"`: The player will proceed to play the content without video. (i.e.,
749+
audio-only playback).
750+
751+
- `"error"`: The player will throw an error to indicate that the video tracks could not be
752+
played.
753+
754+
<div class="note">
755+
756+
- An event [`noPlayableTrack`](../api/Loading_a_Content) will be emitted if no video
757+
tracks are playable.
758+
759+
- If neither the audio nor video tracks are playable, a `"NO_AUDIO_VIDEO_TRACKS"` error
760+
will be thrown regardless of this setting.
761+
762+
</div>
763+
709764
### lowLatencyMode
710765

711766
_type_: `Boolean|undefined`
@@ -927,7 +982,7 @@ The `serverSyncInfos` object contains two keys:
927982
<div class="note">
928983
The `performance.now()` API is used here because it is the main API to
929984
obtain a monotically increasing clock on the client-side.
930-
</div</div>
985+
</div>
931986

932987
Example:
933988

doc/api/Player_Errors.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ An error of `type` `MEDIA_ERROR` can have the following codes (`code` property):
191191
For those errors, you may be able to know the characteristics of the corresponding
192192
track(s) by inspecting the error's `tracksInfo` property, described below.
193193

194+
- `"NO_AUDIO_VIDEO_TRACKS"`: No audio and video tracks were selected. This can happen if
195+
the application has disabled both audio and video.
196+
194197
- `"MANIFEST_UPDATE_ERROR"`: This error should never be emitted as it is handled
195198
internally by the RxPlayer. Please open an issue if you encounter it.
196199

doc/api/Player_Events.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,48 @@ video track when in directfile mode to avoid that case (this is documented
536536
in the corresponding APIs).
537537
</div>
538538

539+
### noPlayableTrack
540+
541+
_payload type_: `Object`
542+
543+
Emitted when no tracks of a given type can be selected for a given period.
544+
545+
The payload is an object with the following properties:
546+
547+
- `trackType` (`"audio" | "video" | "text"`): The type of the track that appears to have
548+
no playable options available.
549+
550+
- `period`: (`Object`): The period in which the track is not playable. This object has the
551+
following properties:
552+
553+
- `id`: (`"string"`): The ID of the period.
554+
555+
- `start`: (`"number"`): The start time of the period.
556+
557+
- `end`: (`"number" | undefined`): The end time of the period. This value may be
558+
`undefined` either if not known or if the Period has no end yet (e.g. for live
559+
contents, the end might not be known for now)
560+
561+
<div class="note">
562+
563+
The `noPlayableTrack` event is emitted when none of the tracks for a specific type (audio,
564+
video, or subtitles) are playable for a given period.
565+
566+
For example, this might occur if a content only has audio codecs that the device does not
567+
support, or if a media cannot be decrypted due to an insufficient security level.
568+
569+
By listening for this event, the application can respond appropriately, such as notifying
570+
the user that the given track type is not playable, or taking corrective actions such as
571+
continuing playback with available tracks (e.g., playing video-only if no audio track is
572+
available).
573+
574+
The `loadVideo()` options
575+
[`onAudioTracksNotPlayable`](../api/Loading_a_Content.html#onaudiotracksnotplayable) and
576+
[`onVideoTracksNotPlayable`](../api/Loading_a_Content.html#onvideotracksnotplayable)
577+
defines the player behavior when a track is not playable.
578+
579+
</div>
580+
539581
## Representation selection events
540582

541583
This chapter describes events linked to the current audio, video or Representation /

doc/reference/API_Reference.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,12 @@ events and so on.
167167
- [`defaultAudioTrackSwitchingMode`](../api/Loading_a_Content.md#defaultaudiotrackswitchingmode):
168168
Default behavior when switching the audio track.
169169

170+
- [`onAudioTracksNotPlayable`](../api/Loading_a_Content.md#onaudiotracksnotplayable):
171+
Specifies the behavior when all audio tracks are not playable.
172+
173+
- [`onVideoTracksNotPlayable`](../api/Loading_a_Content.md#onvideotracksnotplayable):
174+
Specifies the behavior when all video tracks are not playable.
175+
170176
- [`lowLatencyMode`](../api/Loading_a_Content.md#lowlatencymode): Allows to play
171177
low-latency contents efficiently.
172178

0 commit comments

Comments
 (0)