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

chore: update folder structure in the playback package #48

Merged
merged 1 commit into from
Jan 15, 2025
Merged
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
246 changes: 142 additions & 104 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"@nx/js": "20.3.1",
"@size-limit/preset-big-lib": "^11.1.6",
"@types/eslint__js": "^8.42.3",
"@types/react": "^19.0.6",
"@types/react": "^19.0.7",
"@types/react-dom": "^19.0.3",
"@typestrong/ts-mockito": "^2.7.12",
"@vitejs/plugin-react": "^4.3.4",
Expand All @@ -45,7 +45,7 @@
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-jsdoc": "^50.6.1",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-prettier": "^5.2.2",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.18",
"eslint-plugin-unicorn": "^56.0.1",
Expand Down
12 changes: 6 additions & 6 deletions packages/playback/.size-limit.json
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
[
{
"path": "dist/main-only/core/cjs/index.min.js",
"path": "dist/player/core/cjs/index.min.js",
"limit": "1 s"
},
{
"path": "dist/main-only/core/es/index.min.js",
"path": "dist/player/core/es/index.min.js",
"limit": "1 s"
},
{
"path": "dist/main-only/core/iife/index.min.js",
"path": "dist/player/core/iife/index.min.js",
"limit": "1 s"
},
{
"path": "dist/main-with-worker/core/cjs/index.min.js",
"path": "dist/player-with-worker/core/cjs/index.min.js",
"limit": "1 s"
},
{
"path": "dist/main-with-worker/core/es/index.min.js",
"path": "dist/player-with-worker/core/es/index.min.js",
"limit": "1 s"
},
{
"path": "dist/main-with-worker/core/iife/index.min.js",
"path": "dist/player-with-worker/core/iife/index.min.js",
"limit": "1 s"
}
]
38 changes: 18 additions & 20 deletions packages/playback/demo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,32 @@ import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';

import { Player as MainThreadOnlyPlayer } from '../../src/entry-points/main-only-core';
import { Player as MainThreadWithWorkerPlayer } from '../../src/entry-points/main-with-worker-core';
import { Player } from '../../src/entry-points/player';
import { Player as PlayerWithWorker } from '../../src/entry-points/player-with-worker';

const mainThreadOnlyPlayer = MainThreadOnlyPlayer.create();
const mainThreadWithWorkerPlayer = MainThreadWithWorkerPlayer.create();
const player = Player.create();
const playerWithWorker = PlayerWithWorker.create();

const MAIN_THREAD_ONLY_VIDEO_ID = 'main-thread-only-player';
const MAIN_THREAD_WITH_WORKER_VIDEO_ID = 'main-thread-with-worker-player';
const PLAYER_ID = 'player';
const PLAYER_WITH_WORKER_ID = 'player-with-worker';

/**
*
*/
function App(): JSX.Element {
useEffect(() => {
const mainThreadOnlyPlayerElem = document.getElementById(MAIN_THREAD_ONLY_VIDEO_ID) as HTMLVideoElement;
const mainThreadWithWorkerPlayerElem = document.getElementById(
MAIN_THREAD_WITH_WORKER_VIDEO_ID
) as HTMLVideoElement;
const playerElem = document.getElementById(PLAYER_ID) as HTMLVideoElement;
const playerWithWorkerElem = document.getElementById(PLAYER_WITH_WORKER_ID) as HTMLVideoElement;

mainThreadOnlyPlayer.updateConfiguration({ mse: { requiredBufferDuration: 15 } });
mainThreadWithWorkerPlayer.updateConfiguration({ mse: { requiredBufferDuration: 15 } });
player.updateConfiguration({ mse: { requiredBufferDuration: 15 } });
playerWithWorker.updateConfiguration({ mse: { requiredBufferDuration: 15 } });

mainThreadOnlyPlayer.attach(mainThreadOnlyPlayerElem);
mainThreadWithWorkerPlayer.attach(mainThreadWithWorkerPlayerElem);
player.attach(playerElem);
playerWithWorker.attach(playerWithWorkerElem);

return (): void => {
mainThreadOnlyPlayer.detach();
mainThreadWithWorkerPlayer.detach();
player.detach();
playerWithWorker.detach();
};
}, []);

Expand All @@ -51,12 +49,12 @@ function App(): JSX.Element {
</div>

<div className="player-container">
<h1>Main Thread Only Player</h1>
<video id="main-thread-only-player"></video>
<h1>Player</h1>
<video id="player"></video>
</div>
<div className="player-container">
<h1>Main Thread With Worker Player</h1>
<video id="main-thread-with-worker-player"></video>
<h1>Player with worker</h1>
<video id="player-with-worker"></video>
</div>
</>
);
Expand Down
34 changes: 10 additions & 24 deletions packages/playback/docs/walkthrough.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,55 +40,41 @@ There are 2 classes that extend the base `Player` class:

```mermaid
classDiagram
BasePlayer <|-- MainThreadOnlyPlayer
BasePlayer <|-- MainThreadWithWorkerPlayer
BasePlayer <|-- Player
BasePlayer <|-- PlayerWithWorker
```

Both subclasses are intended to be used on the main thread.

`MainThreadOnlyPlayer`: `src/lib/player/main-thread-only/main-thread-only-player.ts` does not have web worker and run fully on main thread.
`Player`: `src/lib/player/player.ts` does not have web worker and run fully on main thread.

> [!Note]
>
> `MainThreadOnlyPlayer` is a public API and should be re-exported in the `src/entry-points/main-core.ts`.
> `Player` is a public API and should be re-exported in the `src/entry-points/player.ts`.


`MainThreadWithWorkerPlayer`: `src/lib/player/main-thread-with-worker/main-thread-with-worker-player.ts` creates WebWorker and delegates most of the work to the worker thread, except work that is required to be executed on main thread (eg: EME, MSE is not available on worker thread, etc...).
`PlayerWithWorker`: `src/lib/player/player-with-worker.ts` creates WebWorker and delegates most of the work to the worker thread, except work that is required to be executed on main thread (eg: EME, MSE is not available on worker thread, etc...).

```mermaid
flowchart LR
MainThreadWithWorkerPlayer -->|creates| WebWorker
PlayerWithWorker -->|creates| WebWorker
```

> [!Note]
>
> `MainThreadWithWorkerPlayer` is a public API and should be re-exported in the `src/entry-points/main-with-worker-core.ts`.
> `PlayerWithWorker` is a public API and should be re-exported in the `src/entry-points/player-with-worker.ts`.
>
> This bundle is also listed as a main entry-point for the package in the `package.json` file.

The WebWorker script is available at `src/lib/player/main-thread-with-worker/worker/worker-script.ts` and this is the main entry point for all worker thread processes.
The WebWorker script is available at `src/lib/worker/worker-script.ts` and this is the main entry point for all worker thread processes.

Worker Script is injected into `MainThreadWithWorkerPlayer` bundle during the build process in the following manner:

```typescript

// __WORKER_CODE is the content of the worker-script.ts bundle and will be replaced during the build process
const workerScriptBlob = new Blob([__WORKER_CODE], { type: 'application/javascript' });
const workerScriptBlobUrl = URL.createObjectURL(workerScriptBlob);
const worker = new Worker(workerScriptBlobUrl);

```

> [!Note]
>
> By default `worker-script` bundle is injected into the `MainThreadWithWorkerPlayer` bundle. But we still want to expose it as a separate bundle, so we re-export it in the `src/entry-points/worker-script.ts`.

## Worker-Main Thread Communication

There are 2 classes that abstract the communication between the worker and the main thread:

- `MainToWorkerThreadMessageChannel`: `src/lib/player/main-thread-with-worker/messages/main-to-worker-thread-messages.ts`
- `WorkerToMainThreadMessageChannel`: `src/lib/player/main-thread-with-worker/messages/worker-to-main-thread-messages.ts`
- `MainToWorkerThreadMessageChannel`: `src/lib/player/worker/messages/main-to-worker-thread-messages.ts`
- `WorkerToMainThreadMessageChannel`: `src/lib/player/worker/messages/worker-to-main-thread-messages.ts`


#### Pipelines
Expand Down
40 changes: 20 additions & 20 deletions packages/playback/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,30 +41,30 @@
"dependencies": {
"@videojs/hls-parser": "*"
},
"main": "./dist/main-with-worker/core/cjs/index.min.js",
"module": "./dist/main-with-worker/core/es/index.min.js",
"es2015": "./dist/main-with-worker/core/es/index.min.js",
"types": "./dist/main-with-worker/core/types/index.min.d.ts",
"main": "./dist/player-with-worker/core/cjs/index.min.js",
"module": "./dist/player-with-worker/core/es/index.min.js",
"es2015": "./dist/player-with-worker/core/es/index.min.js",
"types": "./dist/player-with-worker/core/types/index.min.d.ts",
"exports": {
"./main-only/core": {
"types": "./dist/main-only/core/types/index.min.d.ts",
"require": "./dist/main-only/core/cjs/index.min.js",
"import": "./dist/main-only/core/es/index.min.js"
"./player/core": {
"types": "./dist/player/core/types/index.min.d.ts",
"require": "./dist/player/core/cjs/index.min.js",
"import": "./dist/player/core/es/index.min.js"
},
"./main-only/core/debug": {
"types": "./dist/main-only/core/types/index.min.d.ts",
"require": "./dist/main-only/core/cjs/index.debug.js",
"import": "./dist/main-only/core/es/index.debug.js"
"./player/core/debug": {
"types": "./dist/player/core/types/index.min.d.ts",
"require": "./dist/player/core/cjs/index.debug.js",
"import": "./dist/player/core/es/index.debug.js"
},
"./main-with-worker/core": {
"types": "./dist/main-with-worker/core/types/index.min.d.ts",
"require": "./dist/main-with-worker/core/cjs/index.min.js",
"import": "./dist/main-with-worker/core/es/index.min.js"
"./player-with-worker/core": {
"types": "./dist/player-with-worker/core/types/index.min.d.ts",
"require": "./dist/player-with-worker/core/cjs/index.min.js",
"import": "./dist/player-with-worker/core/es/index.min.js"
},
"./main-with-worker/core/debug": {
"types": "./dist/main-with-worker/core/types/index.min.d.ts",
"require": "./dist/main-with-worker/core/cjs/index.debug.js",
"import": "./dist/main-with-worker/core/es/index.debug.js"
"./player-with-worker/core/debug": {
"types": "./dist/player-with-worker/core/types/index.min.d.ts",
"require": "./dist/player-with-worker/core/cjs/index.debug.js",
"import": "./dist/player-with-worker/core/es/index.debug.js"
}
}
}
1 change: 0 additions & 1 deletion packages/playback/src/entry-points/main-only-core.ts

This file was deleted.

This file was deleted.

1 change: 1 addition & 0 deletions packages/playback/src/entry-points/player-with-worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Player } from '../lib/player/player-with-worker';
1 change: 1 addition & 0 deletions packages/playback/src/entry-points/player.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Player } from '../lib/player/player';
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// enums/consts
import { LoggerLevel } from '../../consts/logger-level';
import { PlayerEventType } from '../../consts/events';
import { LoggerLevel } from '../consts/logger-level';
import { PlayerEventType } from '../consts/events';
// types
import type { ILoadLocalSource, ILoadRemoteSource, IPlayerSource } from '../../types/source.declarations';
import type { IInterceptorsStorage, Interceptor } from '../../types/interceptors.declarations';
import type { ILogger } from '../../types/logger.declarations';
import type { PlayerConfiguration } from '../../types/configuration.declarations';
import type { DeepPartial } from '../../types/utility.declarations';
import type { IStore } from '../../types/store.declarations';
import type { EventListener, IEventEmitter } from '../../types/event-emitter.declarations';
import type { EventTypeToEventMap } from '../../types/mappers/event-type-to-event-map.declarations';
import type { ILoadLocalSource, ILoadRemoteSource, IPlayerSource } from '../types/source.declarations';
import type { IInterceptorsStorage, Interceptor } from '../types/interceptors.declarations';
import type { ILogger } from '../types/logger.declarations';
import type { PlayerConfiguration } from '../types/configuration.declarations';
import type { DeepPartial } from '../types/utility.declarations';
import type { IStore } from '../types/store.declarations';
import type { EventListener, IEventEmitter } from '../types/event-emitter.declarations';
import type { EventTypeToEventMap } from '../types/mappers/event-type-to-event-map.declarations';
// events
import {
ConfigurationChangedEvent,
Expand All @@ -20,35 +20,35 @@ import {
RateChangedEvent,
VolumeChangedEvent,
PlaybackStateChangedEvent,
} from '../../events/player-events';
} from '../events/player-events';
// models
import { PlayerSource } from '../../models/player-source';
import { PlayerSource } from '../models/player-source';
// errors
import { NoSupportedPipelineError, PipelineLoaderFailedToDeterminePipelineError } from '../../errors/pipeline-errors';
import { NoSupportedPipelineError, PipelineLoaderFailedToDeterminePipelineError } from '../errors/pipeline-errors';
// pipelines
import { InterceptorType } from '../../consts/interceptor-type';
import type { InterceptorTypeToInterceptorPayloadMap } from '../../types/mappers/interceptor-type-to-interceptor-map.declarations';
import { PlaybackState } from '../../consts/playback-state';
import { PlayerTimeRange } from '../../models/player-time-range';
import type { IPlayerTimeRange } from '../../types/player-time-range.declarations';
import type { IPlaybackStats } from '../../types/playback-stats.declarations';
import type { IPlayerAudioTrack } from '../../types/audio-track.declarations';
import type { IPlayerThumbnailTrack, IRemoteVttThumbnailTrackOptions } from '../../types/thumbnail-track.declarations';
import type { IQualityLevel } from '../../types/quality-level.declarations';
import type { VersionInfo } from '../../types/version-info.declarations';
import type { IPlayerTextTrack } from '../../types/text-track.declarations';
import { NativePipeline } from '../../pipelines/native/native-pipeline';
import type { IPipeline, IPipelineLoader, IPipelineLoaderFactory } from '../../types/pipeline.declarations';
import type { INetworkManager, INetworkRequestInfo, INetworkResponseInfo } from '../../types/network.declarations';
import { InterceptorType } from '../consts/interceptor-type';
import type { InterceptorTypeToInterceptorPayloadMap } from '../types/mappers/interceptor-type-to-interceptor-map.declarations';
import { PlaybackState } from '../consts/playback-state';
import { PlayerTimeRange } from '../models/player-time-range';
import type { IPlayerTimeRange } from '../types/player-time-range.declarations';
import type { IPlaybackStats } from '../types/playback-stats.declarations';
import type { IPlayerAudioTrack } from '../types/audio-track.declarations';
import type { IPlayerThumbnailTrack, IRemoteVttThumbnailTrackOptions } from '../types/thumbnail-track.declarations';
import type { IQualityLevel } from '../types/quality-level.declarations';
import type { VersionInfo } from '../types/version-info.declarations';
import type { IPlayerTextTrack } from '../types/text-track.declarations';
import { NativePipeline } from '../pipelines/native/native-pipeline';
import type { IPipeline, IPipelineLoader, IPipelineLoaderFactory } from '../types/pipeline.declarations';
import type { INetworkManager, INetworkRequestInfo, INetworkResponseInfo } from '../types/network.declarations';
import {
NetworkRequestAttemptCompletedSuccessfullyEvent,
NetworkRequestAttemptCompletedUnsuccessfullyEvent,
NetworkRequestAttemptFailedEvent,
NetworkRequestAttemptStartedEvent,
} from '../../events/network-events';
import type { IEmeManager, IEmeManagerDependencies } from '../../types/eme-manager.declarations';
import { EncryptedEvent, WaitingForKeyEvent } from '../../events/eme-events';
import type { PipelineLoaderFactoryStorage } from './pipeline-loader-factory-storage';
} from '../events/network-events';
import type { IEmeManager, IEmeManagerDependencies } from '../types/eme-manager.declarations';
import { EncryptedEvent, WaitingForKeyEvent } from '../events/eme-events';
import type { PipelineLoaderFactoryStorage } from '../utils/pipeline-loader-factory-storage';

declare const __COMMIT_HASH: string;
declare const __VERSION: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
// base
import { BasePlayer } from '../base/base-player';
import type { PlayerDependencies } from '../base/base-player';
import { BasePlayer } from './base-player';
import type { PlayerDependencies } from './base-player';
//enums
import type { LoggerLevel } from '../../consts/logger-level';
import type { LoggerLevel } from '../consts/logger-level';
// types
import type { DeepPartial } from '../../types/utility.declarations';
import type { PlayerConfiguration } from '../../types/configuration.declarations';
import { ServiceLocator } from '../../service-locator';
import type { DeepPartial } from '../types/utility.declarations';
import type { PlayerConfiguration } from '../types/configuration.declarations';
import { ServiceLocator } from '../service-locator';
import type {
EmitEventMessage,
RunInterceptorsMessage,
WorkerToMainMessage,
} from './messages/worker-to-main-thread-messages';
import { WorkerToMainMessageType } from './message-types/worker-to-main-message-type';
} from '../worker/messages/worker-to-main-thread-messages';
import { WorkerToMainMessageType } from '../worker/message-types/worker-to-main-message-type';
import type {
IMainToWorkerThreadMessageChannel,
LoadPipelineLoaderPayload,
} from '../../types/message-channels/main-to-worker-thread-message-channel';
import { MainToWorkerThreadMessageChannel } from './messages/main-to-worker-thread-messages';
import type { ILoadLocalSource, ILoadRemoteSource } from '../../types/source.declarations';
} from '../types/message-channels/main-to-worker-thread-message-channel';
import { MainToWorkerThreadMessageChannel } from '../worker/messages/main-to-worker-thread-messages';
import type { ILoadLocalSource, ILoadRemoteSource } from '../types/source.declarations';

import WorkerScript from './worker/worker-script?worker&inline';
import WorkerScript from '../worker/worker-script?worker&inline';

declare const __BUNDLED_WORKER_PIPELINE_LOADERS: Record<string, Array<string>>;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// base
import type { PlayerDependencies } from '../base/base-player';
import { BasePlayer } from '../base/base-player';
import { ServiceLocator } from '../../service-locator';
import type { PlayerDependencies } from './base-player';
import { BasePlayer } from './base-player';
import { ServiceLocator } from '../service-locator';

export class Player extends BasePlayer {
public static create(): Player {
Expand Down
2 changes: 1 addition & 1 deletion packages/playback/src/lib/service-locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { ConfigurationManager } from './configuration/configuration-manager';
import { EventEmitter } from './utils/event-emitter';
import { NetworkManager } from './network/network-manager';
import { PlayerEventType } from './consts/events';
import { PipelineLoaderFactoryStorage } from './player/base/pipeline-loader-factory-storage';
import { PipelineLoaderFactoryStorage } from './utils/pipeline-loader-factory-storage';

export class ServiceLocator {
public readonly logger: ILogger;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IPipelineFactoryConfiguration, IPipelineLoaderFactory } from '../../types/pipeline.declarations';
import type { IPipelineFactoryConfiguration, IPipelineLoaderFactory } from '../types/pipeline.declarations';

const normalizeMimeType = (mimeType: string): string => mimeType.toLowerCase();

Expand Down
Loading
Loading