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

[POC] [WIP] Remove MediaSourceContentInitializer, add CoreInterface #1627

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from
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
63 changes: 33 additions & 30 deletions src/core/main/worker/content_preparer.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@
import features from "../../../features";
import log from "../../../log";
import type { IManifest, IManifestMetadata } from "../../../manifest";
import type { IManifest } from "../../../manifest";
import { createRepresentationFilterFromFnString } from "../../../manifest";
import type { IMediaSourceInterface } from "../../../mse";
import MainMediaSourceInterface from "../../../mse/main_media_source_interface";
import WorkerMediaSourceInterface from "../../../mse/worker_media_source_interface";
import type {
IAttachMediaSourceWorkerMessagePayload,
IContentInitializationData,
IWorkerMessage,
} from "../../../multithread_types";
import { WorkerMessageType } from "../../../multithread_types";
import type { IPlayerError } from "../../../public_types";
import assert from "../../../utils/assert";
import idGenerator from "../../../utils/id_generator";
import objectAssign from "../../../utils/object_assign";
import type {
CancellationError,
CancellationSignal,
} from "../../../utils/task_canceller";
import TaskCanceller from "../../../utils/task_canceller";
import type { IRepresentationEstimator } from "../../adaptive";
import createAdaptiveRepresentationSelector from "../../adaptive";
import type { IRepresentationEstimatorThrottlers } from "../../adaptive/adaptive_representation_selector";
import CmcdDataBuilder from "../../cmcd";
import type { IManifestRefreshSettings } from "../../fetchers";
import { ManifestFetcher, SegmentQueueCreator } from "../../fetchers";
import SegmentSinksStore from "../../segment_sinks";
import type { INeedsMediaSourceReloadPayload } from "../../stream";
import DecipherabilityFreezeDetector from "../common/DecipherabilityFreezeDetector";
import { limitVideoResolution, throttleVideoBitrate } from "./globals";
import sendMessage, { formatErrorForSender } from "./send_message";
import TrackChoiceSetter from "./track_choice_setter";
import { formatErrorForSender } from "./utils";
import WorkerTextDisplayerInterface from "./worker_text_displayer_interface";

const generateMediaSourceId = idGenerator();
Expand Down Expand Up @@ -74,8 +73,11 @@ export default class ContentPreparer {
}

public initializeNewContent(
sendMessage: (msg: IWorkerMessage, transferables?: Transferable[]) => void,
context: IContentInitializationData,
): Promise<IManifestMetadata> {
/** Allows to filter which Representations can be choosen. */
throttlers: IRepresentationEstimatorThrottlers,
): Promise<IManifest> {
return new Promise((res, rej) => {
this.disposeCurrentContent();
const contentCanceller = this._contentCanceller;
Expand All @@ -84,19 +86,24 @@ export default class ContentPreparer {

currentMediaSourceCanceller.linkToSignal(contentCanceller.signal);

const { contentId, url, hasText, transportOptions } = context;
const { contentId, url, hasText, transport, transportOptions } = context;
let manifest: IManifest | null = null;

// TODO better way
assert(
features.transports.dash !== undefined,
"Multithread RxPlayer should have access to the DASH feature",
);
const transportFn = features.transports[transport];
if (typeof transportFn !== "function") {
rej(
new Error(
`transport "${transport}" not supported. ` +
"Did you add the corresponding feature?",
),
);
return;
}
const representationFilter =
typeof transportOptions.representationFilter === "string"
? createRepresentationFilterFromFnString(transportOptions.representationFilter)
: undefined;
const dashPipelines = features.transports.dash({
: transportOptions.representationFilter;
const transportPipelines = transportFn({
...transportOptions,
representationFilter,
});
Expand All @@ -105,7 +112,7 @@ export default class ContentPreparer {
context.cmcd === undefined ? null : new CmcdDataBuilder(context.cmcd);
const manifestFetcher = new ManifestFetcher(
url === undefined ? undefined : [url],
dashPipelines,
transportPipelines,
{
cmcdDataBuilder,
...context.manifestRetryOptions,
Expand All @@ -117,10 +124,7 @@ export default class ContentPreparer {
video: context.initialVideoBitrate ?? 0,
},
lowLatencyMode: transportOptions.lowLatencyMode,
throttlers: {
limitResolution: { video: limitVideoResolution },
throttleBitrate: { video: throttleVideoBitrate },
},
throttlers,
});

const unbindRejectOnCancellation = currentMediaSourceCanceller.signal.register(
Expand All @@ -130,7 +134,7 @@ export default class ContentPreparer {
);

const segmentQueueCreator = new SegmentQueueCreator(
dashPipelines,
transportPipelines,
cmcdDataBuilder,
context.segmentRetryOptions,
contentCanceller.signal,
Expand All @@ -140,6 +144,7 @@ export default class ContentPreparer {

const [mediaSource, segmentSinksStore, workerTextSender] =
createMediaSourceAndBuffersStore(
sendMessage,
contentId,
{
hasMseInWorker: this._hasMseInWorker,
Expand Down Expand Up @@ -219,30 +224,23 @@ export default class ContentPreparer {
return;
}

const sentManifest = manifest.getMetadataSnapshot();
manifest.addEventListener(
"manifestUpdate",
(updates) => {
if (manifest === null) {
// TODO log warn?
return;
}

// Remove `periods` key to reduce cost of an unnecessary manifest
// clone.
const snapshot = objectAssign(manifest.getMetadataSnapshot(), {
periods: [],
});
sendMessage({
type: WorkerMessageType.ManifestUpdate,
contentId,
value: { manifest: snapshot, updates },
value: { manifest, updates },
});
},
contentCanceller.signal,
);
unbindRejectOnCancellation();
res(sentManifest);
res(manifest);
}
});
}
Expand All @@ -255,7 +253,10 @@ export default class ContentPreparer {
this._currentContent?.manifestFetcher.scheduleManualRefresh(settings);
}

public reloadMediaSource(reloadInfo: INeedsMediaSourceReloadPayload): Promise<void> {
public reloadMediaSource(
sendMessage: (msg: IWorkerMessage, transferables?: Transferable[]) => void,
reloadInfo: INeedsMediaSourceReloadPayload,
): Promise<void> {
this._currentMediaSourceCanceller.cancel();
if (this._currentContent === null) {
return Promise.reject(new Error("CP: No content anymore"));
Expand All @@ -274,6 +275,7 @@ export default class ContentPreparer {

const [mediaSource, segmentSinksStore, workerTextSender] =
createMediaSourceAndBuffersStore(
sendMessage,
this._currentContent.contentId,
{
hasMseInWorker: this._hasMseInWorker,
Expand Down Expand Up @@ -380,6 +382,7 @@ export interface IPreparedContentData {
* @returns {Array.<Object>}
*/
function createMediaSourceAndBuffersStore(
sendMessage: (msg: IWorkerMessage, transferables?: Transferable[]) => void,
contentId: string,
capabilities: {
hasMseInWorker: boolean;
Expand Down
38 changes: 0 additions & 38 deletions src/core/main/worker/globals.ts

This file was deleted.

2 changes: 2 additions & 0 deletions src/core/main/worker/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
import type { IMessageReceiverCallback } from "./worker_main";
import initializeWorkerMain from "./worker_main";
export default initializeWorkerMain;
export type { IMessageReceiverCallback };
28 changes: 0 additions & 28 deletions src/core/main/worker/send_message.ts

This file was deleted.

11 changes: 11 additions & 0 deletions src/core/main/worker/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { formatError } from "../../../errors";
import type { ISentError } from "../../../multithread_types";

export function formatErrorForSender(error: unknown): ISentError {
const formattedError = formatError(error, {
defaultCode: "NONE",
defaultReason: "An unknown error stopped content playback.",
});

return formattedError.serialize();
}
Loading
Loading