From 809f8e8dd1e0b8373b9bbbd4f4981643571eb260 Mon Sep 17 00:00:00 2001 From: Paul Berberian Date: Mon, 13 Jan 2025 19:42:04 +0100 Subject: [PATCH] Perform data-serialization as sendMessage-time --- src/core/main/worker/content_preparer.ts | 16 +++------- src/multithread_types.ts | 40 ++++++++++++++++++++++-- src/worker_entry_point.ts | 36 ++++++++++++++++++++- 3 files changed, 77 insertions(+), 15 deletions(-) diff --git a/src/core/main/worker/content_preparer.ts b/src/core/main/worker/content_preparer.ts index 55a90b018f..4ecb50cf7a 100644 --- a/src/core/main/worker/content_preparer.ts +++ b/src/core/main/worker/content_preparer.ts @@ -1,6 +1,6 @@ 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"; @@ -13,7 +13,6 @@ import type { import { WorkerMessageType } from "../../../multithread_types"; import type { IPlayerError } from "../../../public_types"; import idGenerator from "../../../utils/id_generator"; -import objectAssign from "../../../utils/object_assign"; import type { CancellationError, CancellationSignal, @@ -76,7 +75,7 @@ export default class ContentPreparer { public initializeNewContent( sendMessage: (msg: IWorkerMessage, transferables?: Transferable[]) => void, context: IContentInitializationData, - ): Promise { + ): Promise { return new Promise((res, rej) => { this.disposeCurrentContent(); const contentCanceller = this._contentCanceller; @@ -222,7 +221,6 @@ export default class ContentPreparer { return; } - const sentManifest = manifest.getMetadataSnapshot(); manifest.addEventListener( "manifestUpdate", (updates) => { @@ -230,22 +228,16 @@ export default class ContentPreparer { // 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); } }); } diff --git a/src/multithread_types.ts b/src/multithread_types.ts index 9f96241e9b..b1f7248342 100644 --- a/src/multithread_types.ts +++ b/src/multithread_types.ts @@ -772,17 +772,53 @@ export interface IRepresentationChangeWorkerMessage { }; } +/** Message sent by the Worker when the Manifest is first loaded. */ export interface IManifestReadyWorkerMessage { + /** Identify this particular message. */ type: WorkerMessageType.ManifestReady; + /** The `contentId` linked to this Manifest. */ contentId: string; - value: { manifest: IManifestMetadata }; + value: { + /** + * The actual `Manifest` loaded. + * + * When possible, this should be a `Manifest` instance. + * + * Only if this is not possible (e.g. because the `Manifest` cannot be + * communicated as is between both Worker and main_thread) might you convert + * it to another object also respecting the `IManifestMetadata` interface. + * + * However doing this might lead to some loss of performance and minor + * features. + */ + manifest: IManifestMetadata; + }; } +/** Message sent by the Worker everytime the Manifest is updated. */ export interface IManifestUpdateWorkerMessage { + /** Identify this particular message. */ type: WorkerMessageType.ManifestUpdate; + /** The `contentId` linked to this Manifest. */ contentId: string | undefined; value: { - manifest: IManifestMetadata; // TODO only subpart that changed? + /** + * The new manifest once updated. + * + * When possible, this should be a `Manifest` instance to improve + * performance and allow some advanced features. + * + * Only if this is not possible (e.g. because the `Manifest` cannot be + * communicated as is between both Worker and main_thread) might you convert + * it to another object also respecting the `IManifestMetadata` interface. + * In that last case, you're also authorized to reset the `periods` property + * of that `IManifestMetadata` to an empty array to save up message-passing + * performance. + */ + manifest: IManifestMetadata; + /** + * Object describing what has changed in this update. + */ updates: IPeriodsUpdateResult; }; } diff --git a/src/worker_entry_point.ts b/src/worker_entry_point.ts index b6f3aa0874..3ee53b2f9c 100644 --- a/src/worker_entry_point.ts +++ b/src/worker_entry_point.ts @@ -14,7 +14,8 @@ import { } from "./core/main/worker/globals"; import log from "./experimental/tools/mediaCapabilitiesProber/log"; import features from "./features"; -import type { IWorkerMessage } from "./multithread_types"; +import Manifest from "./manifest/classes"; +import { WorkerMessageType, type IWorkerMessage } from "./multithread_types"; import DashFastJsParser from "./parsers/manifest/dash/fast-js-parser"; import DashWasmParser from "./parsers/manifest/dash/wasm-parser"; import createDashPipelines from "./transports/dash"; @@ -45,7 +46,15 @@ initializeWorkerMain( }, ); +/** + * Perform a `postMessage` to main thread with the given message. + * Arguments follow the `postMessage` API. + * @param {Object} msg + * @param {Array.} [transferables] + */ function sendMessage(msg: IWorkerMessage, transferables?: Transferable[]): void { + updateMessageFormat(msg); + log.debug("<--- Sending to Main:", msg.type); if (transferables === undefined) { postMessage(msg); @@ -57,3 +66,28 @@ function sendMessage(msg: IWorkerMessage, transferables?: Transferable[]): void ); } } + +/** + * Ensure that we're sending data that can be serialized, as this is a + * requirement for the `postMessage` browser API. + * + * If necessary, mutations are done in place. + * @param {Object} msg + */ +function updateMessageFormat(msg: IWorkerMessage): void { + if ( + msg.type === WorkerMessageType.ManifestReady || + msg.type === WorkerMessageType.ManifestUpdate + ) { + if (msg.value.manifest instanceof Manifest) { + msg.value.manifest = msg.value.manifest.getMetadataSnapshot(); + if (msg.type === WorkerMessageType.ManifestUpdate) { + // Remove `periods` key to reduce cost of an unnecessary manifest + // clone. + msg.value.manifest.periods = []; + } + } else { + log.warn("Worker: the Manifest instance should be communicated to `sendMessage`."); + } + } +}