Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/core/main/worker/worker_main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,8 @@ interface IBufferingInitializationInformation {
enableFastSwitching: boolean;
/** Behavior when a new video and/or audio codec is encountered. */
onCodecSwitch: "continue" | "reload";
/** Whether to reload the media source for the first incompatible period switch. */
reloadMediaSourceForFirstIncompatiblePeriodSwitch: boolean;
}

function loadOrReloadPreparedContent(
Expand Down Expand Up @@ -609,6 +611,8 @@ function loadOrReloadPreparedContent(
drmSystemId,
enableFastSwitching,
onCodecSwitch,
reloadMediaSourceForFirstIncompatiblePeriodSwitch:
val.reloadMediaSourceForFirstIncompatiblePeriodSwitch,
},
handleStreamOrchestratorCallbacks(),
currentLoadCanceller.signal,
Expand Down Expand Up @@ -898,6 +902,8 @@ function loadOrReloadPreparedContent(
drmSystemId: val.drmSystemId,
enableFastSwitching: val.enableFastSwitching,
onCodecSwitch: val.onCodecSwitch,
reloadMediaSourceForFirstIncompatiblePeriodSwitch:
val.reloadMediaSourceForFirstIncompatiblePeriodSwitch,
},
contentPreparer,
playbackObservationRef,
Expand Down
42 changes: 40 additions & 2 deletions src/core/stream/orchestrator/stream_orchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,29 @@ export default function StreamOrchestrator(
orchestratorCancelSignal: CancellationSignal,
): void {
const { manifest, initialPeriod } = content;
const { maxBufferAhead, maxBufferBehind, wantedBufferAhead, maxVideoBufferSize } =
options;

const {
maxBufferAhead,
maxBufferBehind,
wantedBufferAhead,
maxVideoBufferSize,
reloadMediaSourceForFirstIncompatiblePeriodSwitch,
} = options;

function isPeriodEncrypted(period: IPeriod): boolean {
const adaptations = period.adaptations.video;

return (
adaptations !== undefined &&
adaptations.some((adaptation) =>
adaptation.representations.some(
(representation) => representation.contentProtections !== undefined,
),
)
);
}

const isInitialPeriodEncrypted = isPeriodEncrypted(initialPeriod);

const {
MINIMUM_MAX_BUFFER_AHEAD,
Expand Down Expand Up @@ -471,6 +492,22 @@ export default function StreamOrchestrator(
if (basePeriod.containsTime(position.getWanted(), nextPeriod)) {
return;
}

const isNextPeriodEncrypted =
nextPeriod !== null && isPeriodEncrypted(nextPeriod);

if (
!isInitialPeriodEncrypted &&
isNextPeriodEncrypted &&
reloadMediaSourceForFirstIncompatiblePeriodSwitch
) {
callbacks.needsMediaSourceReload({
timeOffset: 0.1,
minimumPosition: undefined,
maximumPosition: undefined,
});
}

log.info(
"Stream: Destroying PeriodStream as the current playhead moved above it",
bufferType,
Expand Down Expand Up @@ -652,6 +689,7 @@ export type IStreamOrchestratorOptions = IPeriodStreamOptions & {
maxVideoBufferSize: IReadOnlySharedReference<number>;
maxBufferAhead: IReadOnlySharedReference<number>;
maxBufferBehind: IReadOnlySharedReference<number>;
reloadMediaSourceForFirstIncompatiblePeriodSwitch: boolean;
};

/** Callbacks called by the `StreamOrchestrator` on various events. */
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import isDebugModeEnabled from "./compat/is_debug_mode_enabled";
import patchWebkitSourceBuffer from "./compat/patch_webkit_source_buffer";
import { MULTI_THREAD } from "./experimental/features";
import {
DASH,
DIRECTFILE,
Expand Down
11 changes: 11 additions & 0 deletions src/main_thread/api/option_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ interface IParsedLoadVideoOptionsBase {
experimentalOptions: {
enableRepresentationAvoidance: boolean;
};
/** @see ILoadVideoOptions.reloadMediaSourceForFirstIncompatiblePeriodSwitch */
reloadMediaSourceForFirstIncompatiblePeriodSwitch: boolean;
__priv_manifestUpdateUrl?: string | undefined;
__priv_patchLastSegmentInSidx?: boolean | undefined;
}
Expand Down Expand Up @@ -309,6 +311,7 @@ function parseLoadVideoOptions(options: ILoadVideoOptions): IParsedLoadVideoOpti
let mode: IRxPlayerMode;
let textTrackElement: HTMLElement | undefined;
let startAt: IParsedStartAtOption | undefined;
let reloadMediaSourceForFirstIncompatiblePeriodSwitch: boolean;

const {
DEFAULT_AUTO_PLAY,
Expand Down Expand Up @@ -455,6 +458,13 @@ function parseLoadVideoOptions(options: ILoadVideoOptions): IParsedLoadVideoOpti
}
}

if (!isNullOrUndefined(options.reloadMediaSourceForFirstIncompatiblePeriodSwitch)) {
reloadMediaSourceForFirstIncompatiblePeriodSwitch =
options.reloadMediaSourceForFirstIncompatiblePeriodSwitch;
} else {
reloadMediaSourceForFirstIncompatiblePeriodSwitch = false;
}

const requestConfig = options.requestConfig ?? {};

// All those eslint disable are needed because the option is voluntarily
Expand Down Expand Up @@ -491,6 +501,7 @@ function parseLoadVideoOptions(options: ILoadVideoOptions): IParsedLoadVideoOpti
enableRepresentationAvoidance:
options.experimentalOptions?.enableRepresentationAvoidance === true,
},
reloadMediaSourceForFirstIncompatiblePeriodSwitch,
};
}

Expand Down
7 changes: 6 additions & 1 deletion src/main_thread/api/public_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
__priv_manifestUpdateUrl,
__priv_patchLastSegmentInSidx,
url,
reloadMediaSourceForFirstIncompatiblePeriodSwitch,
} = options;

// Perform multiple checks on the given options
Expand Down Expand Up @@ -879,7 +880,11 @@ class Player extends EventEmitter<IPublicAPIEvent> {
};

const bufferOptions = objectAssign(
{ enableFastSwitching, onCodecSwitch },
{
enableFastSwitching,
onCodecSwitch,
reloadMediaSourceForFirstIncompatiblePeriodSwitch,
},
this._priv_bufferOptions,
);

Expand Down
2 changes: 2 additions & 0 deletions src/main_thread/init/media_source_content_initializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,8 @@ export interface IInitializeArguments {
maxBufferAhead: IReadOnlySharedReference<number>;
/** Max buffer size before the current position, in seconds (we GC further down). */
maxBufferBehind: IReadOnlySharedReference<number>;
/** Whether to reload the media source for the first incompatible period switch. */
reloadMediaSourceForFirstIncompatiblePeriodSwitch: boolean;
/**
* Enable/Disable fastSwitching: allow to replace lower-quality segments by
* higher-quality ones to have a faster transition.
Expand Down
9 changes: 8 additions & 1 deletion src/main_thread/init/multi_thread_content_initializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1683,7 +1683,11 @@ export default class MultiThreadContentInitializer extends ContentInitializer {
this._settings.startAt,
);
log.debug("MTCI: Initial time calculated:", initialTime);
const { enableFastSwitching, onCodecSwitch } = this._settings.bufferOptions;
const {
enableFastSwitching,
onCodecSwitch,
reloadMediaSourceForFirstIncompatiblePeriodSwitch,
} = this._settings.bufferOptions;
const corePlaybackObserver = this._setUpModulesOnNewMediaSource(
{
initialTime,
Expand Down Expand Up @@ -1711,6 +1715,7 @@ export default class MultiThreadContentInitializer extends ContentInitializer {
drmSystemId: drmInitStatus.drmSystemId,
enableFastSwitching,
onCodecSwitch,
reloadMediaSourceForFirstIncompatiblePeriodSwitch,
},
});

Expand Down Expand Up @@ -1890,6 +1895,8 @@ export interface IInitializeArguments {
maxBufferAhead: IReadOnlySharedReference<number>;
/** Max buffer size before the current position, in seconds (we GC further down). */
maxBufferBehind: IReadOnlySharedReference<number>;
/** Whether to reload the media source for the first incompatible period switch. */
reloadMediaSourceForFirstIncompatiblePeriodSwitch: boolean;
/**
* Enable/Disable fastSwitching: allow to replace lower-quality segments by
* higher-quality ones to have a faster transition.
Expand Down
3 changes: 2 additions & 1 deletion src/multithread_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,8 @@ export interface IStartPreparedContentMessageValue {
enableFastSwitching: boolean;
/** Behavior when a new video and/or audio codec is encountered. */
onCodecSwitch: "continue" | "reload";

/** Whether to reload the media source for the first incompatible period switch. */
reloadMediaSourceForFirstIncompatiblePeriodSwitch: boolean;
// TODO prepare chosen Adaptations here?
// In which case the Period's `id` should probably be given instead of the
// `initialTime`
Expand Down
15 changes: 14 additions & 1 deletion src/public_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ export interface IConstructorOptions {
maxVideoBufferSize?: number;
videoResolutionLimit?: "videoElement" | "screen" | "none";
throttleVideoBitrateWhenHidden?: boolean;

// eslint-disable-next-line @typescript-eslint/no-restricted-types
videoElement?: HTMLMediaElement;
baseBandwidth?: number;
Expand Down Expand Up @@ -139,6 +138,20 @@ export interface ILoadVideoOptions {
*/
onCodecSwitch?: "continue" | "reload";

/**
* Behavior when period switch from non-drm content to drm content for first time.
*
* This value might depend on the device's capabilities.
*
* @remarks
* In some of legacy CDM implementations (e.g. Tizen 3.0), they try to decrypt the segment with first loaded init segment
* When the assets starts with non-drm protected content and switch to drm protected segment
* CDM trying to use the unencrypted init segment to decrypt the encrypted segment user will see the content with green artifacts or black screen
*
* To fix this issue, we need to reload the media source to make sure the CDM is using the correct init segment
*/
reloadMediaSourceForFirstIncompatiblePeriodSwitch?: boolean;

/**
* Whether we should check that an obtain segment is truncated and retry the
* request if that's the case.
Expand Down