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

Decouple the Player from the Web Application #13

Merged
merged 12 commits into from
Jan 7, 2025
1 change: 1 addition & 0 deletions lib/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module.exports = {
plugins: ["@typescript-eslint", "prettier", "solid"],
root: true,
env: {
commonjs: true,
browser: true,
es2022: true,
worker: true,
Expand Down
2 changes: 2 additions & 0 deletions lib/common/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class Watch<T> {

constructor(init: T) {
this.#next = new Deferred<WatchNext<T>>()
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.#current = [init, this.#next.promise]
}

Expand All @@ -44,6 +45,7 @@ export class Watch<T> {
}

const next = new Deferred<WatchNext<T>>()
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.#current = [v, next.promise]
this.#next.resolve(this.#current)
this.#next = next
Expand Down
2 changes: 1 addition & 1 deletion lib/contribute/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export class Container {
this.#track = this.#mp4.addTrack(options)
if (!this.#track) throw new Error("failed to initialize MP4 track")

const buffer = MP4.ISOFile.writeInitializationSegment(this.#mp4.ftyp!, this.#mp4.moov!, 0, 0)
const buffer = MP4.ISOFile.writeInitializationSegment(this.#mp4.ftyp, this.#mp4.moov, 0, 0)
const data = new Uint8Array(buffer)

controller.enqueue({
Expand Down
4 changes: 2 additions & 2 deletions lib/media/mp4/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ MP4.BoxParser.dOpsBox.prototype.write = function (stream: MP4.Stream) {
stream.writeUint8(this.ChannelMappingFamily)

if (this.ChannelMappingFamily !== 0) {
stream.writeUint8(this.StreamCount!)
stream.writeUint8(this.CoupledCount!)
stream.writeUint8(this.StreamCount)
stream.writeUint8(this.CoupledCount)
for (const mapping of this.ChannelMapping!) {
stream.writeUint8(mapping)
}
Expand Down
48 changes: 40 additions & 8 deletions lib/package.json
Original file line number Diff line number Diff line change
@@ -1,29 +1,61 @@
{
"name": "@kixelated/moq",
"type": "module",
"version": "0.1.4",
"name": "moq-player",
"version": "0.0.1",
"description": "Media over QUIC library",
"license": "(MIT OR Apache-2.0)",
"repository": "github:kixelated/moq-js",
"entry": "playback/index.ts",
"main": "dist/moq-player.cjs.js",
"module": "dist/moq-player.esm.js",
"iife": "dist/moq-player.iife.js",
"types": "dist/types/moq-player.d.ts",
"scripts": {
"build": "tsc -b && cp ../LICENSE* ./dist && cp ./README.md ./dist && cp ./package.json ./dist",
"lint": "eslint .",
"build": "rollup -c",
"dev": "rollup -c -w",
"lint": "eslint . --ext .js,.ts,.jsx,.tsx",
"fmt": "prettier --write ."
},
"devDependencies": {
"@babel/core": "^7.26.0",
"@babel/preset-env": "^7.26.0",
"@babel/preset-typescript": "^7.26.0",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^28.0.1",
"@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.1",
"@types/audioworklet": "^0.0.50",
"@types/dom-mediacapture-transform": "^0.1.6",
"@types/dom-webcodecs": "^0.1.8",
"@typescript/lib-dom": "npm:@types/web@^0.0.115",
"@typescript-eslint/eslint-plugin": "^6.4.0",
"@typescript-eslint/parser": "^6.4.0",
"@typescript/lib-dom": "npm:@types/web@^0.0.115",
"cross-env": "^7.0.2",
"eslint": "^8.47.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.0.1",
"typescript": "^5.1.6"
"rollup": "^4.28.0",
"rollup-plugin-sourcemaps": "^0.6.2",
"rollup-plugin-web-worker-loader": "github:montevideo-tech/rollup-plugin-web-worker-loader",
"tslib": "^2.8.1",
"typescript": "^5.7.2"
},
"dependencies": {
"mp4box": "^0.5.2"
},
"browserslist": {
"production": [
"chrome >= 97",
"edge >= 98",
"firefox >= 130",
"opera >= 83",
"safari >= 18"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
9 changes: 2 additions & 7 deletions lib/playback/audio.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
/// <reference types="vite/client" />

import * as Message from "./worker/message"

// This is a non-standard way of importing worklet/workers.
// Unfortunately, it's the only option because of a Vite bug: https://github.com/vitejs/vite/issues/11823
import workletURL from "./worklet/index.ts?worker&url"
import registerMyAudioWorklet from "audio-worklet:./worklet/index.ts"

// NOTE: This must be on the main thread
export class Audio {
Expand All @@ -25,8 +21,7 @@ export class Audio {

private async load(config: Message.ConfigAudio): Promise<AudioWorkletNode> {
// Load the worklet source code.
await this.context.audioWorklet.addModule(workletURL)

await registerMyAudioWorklet(this.context)
const volume = this.context.createGain()
volume.gain.value = 2.0

Expand Down
9 changes: 4 additions & 5 deletions lib/playback/backend.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/// <reference types="vite/client" />

import * as Message from "./worker/message"
import { Audio } from "./audio"

import MediaWorker from "./worker?worker"
// import WebWorker from 'web-worker:./Worker.ts';
import MediaWorker from "web-worker:./worker/index.ts"

import { RingShared } from "../common/ring"
import { Root, isAudioTrack } from "../media/catalog"
import { GroupHeader } from "../transport/objects"
Expand All @@ -26,8 +26,7 @@ export default class Backend {

constructor(config: PlayerConfig) {
// TODO does this block the main thread? If so, make this async
// @ts-expect-error: The Vite typing is wrong https://github.com/vitejs/vite/blob/22bd67d70a1390daae19ca33d7de162140d533d6/packages/vite/client.d.ts#L182
this.#worker = new MediaWorker({ format: "es" })
this.#worker = new MediaWorker()
this.#worker.addEventListener("message", this.on.bind(this))

let sampleRate: number | undefined
Expand Down
2 changes: 1 addition & 1 deletion lib/playback/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export interface PlayerConfig {
}

// This class must be created on the main thread due to AudioContext.
export class Player {
export default class Player {
#backend: Backend

// A periodically updated timeline
Expand Down
67 changes: 67 additions & 0 deletions lib/rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* eslint-disable @typescript-eslint/no-var-requires */
"use strict"
const resolve = require("@rollup/plugin-node-resolve")
const commonjs = require("@rollup/plugin-commonjs")
const typescript = require("@rollup/plugin-typescript")
const workerLoader = require("rollup-plugin-web-worker-loader")
const babel = require("@rollup/plugin-babel")
const terser = require("@rollup/plugin-terser")
const sourceMaps = require("rollup-plugin-sourcemaps")
const pkg = require("./package.json")

const config = []

config.push({
input: pkg.entry,
output: {
file: pkg.iife,
format: "iife",
name: "moqplayer",
sourcemap: true,
},
plugins: [
resolve(),
commonjs({
include: [/node_modules/, /src/],
transformMixedEsModules: true,
}),
workerLoader({ preserveSource: true }),
typescript({
typescript: require("typescript"),
}),
sourceMaps(),
babel({
babelHelpers: "bundled",
presets: ["@babel/preset-env", "@babel/preset-typescript"],
exclude: "./node_modules/*",
}),
terser(),
],
})

config.push({
input: pkg.entry,
output: {
file: pkg.module,
format: "esm",
sourcemap: true,
},
external: [],
plugins: [
resolve(),
commonjs(),
workerLoader({ preserveSource: true }),
typescript({
typescript: require("typescript"),
}),
sourceMaps(),
babel({
babelHelpers: "bundled",
presets: ["@babel/preset-env", "@babel/preset-typescript"],
exclude: "./node_modules/*",
}),
terser(),
],
})

module.exports = config
2 changes: 1 addition & 1 deletion lib/transport/control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ export class Decoder {

private async msg(): Promise<Msg> {
const t = await this.r.u53()
switch (t) {
switch (t as Id) {
case Id.Subscribe:
return Msg.Subscribe
case Id.SubscribeOk:
Expand Down
46 changes: 11 additions & 35 deletions lib/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,42 +1,18 @@
{
"files": [], // don't build anything with these settings.
"compilerOptions": {
"target": "es2022",
"module": "es2022",
"sourceMap": true,
"moduleResolution": "node",
"rootDir": ".",
"module": "es2022",
"target": "es2022",
"lib": ["es2022", "dom", "dom.iterable"],
"outDir": "./dist",
"declaration": true,
"strict": true,
"composite": true,
"declarationMap": true,
"sourceMap": true,
"isolatedModules": true,
"types": [], // Don't automatically import any @types modules.
"lib": ["es2022", "dom"],
"typeRoots": ["./types", "../node_modules/@types"]
"allowJs": false,
"allowSyntheticDefaultImports": true,
"downlevelIteration": true,
"removeComments": false,
"isolatedModules": false,
"noEmit": false
},
"references": [
{
"path": "./common"
},
{
"path": "./playback"
},
{
"path": "./playback/worklet"
},
{
"path": "./contribute"
},
{
"path": "./transport"
},
{
"path": "./media"
}
],
"paths": {
"@/*": ["*"]
}
"exclude": ["node_modules"]
}
9 changes: 9 additions & 0 deletions lib/types/workers.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
declare module "web-worker:*" {
const WorkerFactory: new () => Worker
export default WorkerFactory
}

declare module "audio-worklet:*" {
const value: any
export default value
}
Loading
Loading