WIP
This commit is contained in:
parent
032c49ab50
commit
c8c856d6b8
|
@ -0,0 +1,7 @@
|
||||||
|
module.exports = function (app) {
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
|
||||||
|
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
|
@ -8,6 +8,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@parcel/transformer-inline-string": "2.8.3",
|
"@parcel/transformer-inline-string": "2.8.3",
|
||||||
"@parcel/validator-typescript": "^2.6.0",
|
"@parcel/validator-typescript": "^2.6.0",
|
||||||
|
"@types/audioworklet": "^0.0.41",
|
||||||
"@types/dom-webcodecs": "^0.1.6",
|
"@types/dom-webcodecs": "^0.1.6",
|
||||||
"parcel": "^2.8.0",
|
"parcel": "^2.8.0",
|
||||||
"typescript": ">=3.0.0"
|
"typescript": ">=3.0.0"
|
||||||
|
|
|
@ -10,7 +10,7 @@ export class Decoder {
|
||||||
tracks: Map<string, Util.Deferred<Message.Init>>
|
tracks: Map<string, Util.Deferred<Message.Init>>
|
||||||
renderer: Renderer;
|
renderer: Renderer;
|
||||||
|
|
||||||
constructor(renderer: Renderer) {
|
constructor(config: Message.Config, renderer: Renderer) {
|
||||||
this.tracks = new Map();
|
this.tracks = new Map();
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ export class Decoder {
|
||||||
this.tracks.set(msg.track, track)
|
this.tracks.set(msg.track, track)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(msg.info)
|
||||||
if (msg.info.audioTracks.length != 1 || msg.info.videoTracks.length != 0) {
|
if (msg.info.audioTracks.length != 1 || msg.info.videoTracks.length != 0) {
|
||||||
throw new Error("Expected a single audio track")
|
throw new Error("Expected a single audio track")
|
||||||
}
|
}
|
||||||
|
@ -42,16 +43,19 @@ export class Decoder {
|
||||||
const audio = info.audioTracks[0]
|
const audio = info.audioTracks[0]
|
||||||
|
|
||||||
const decoder = new AudioDecoder({
|
const decoder = new AudioDecoder({
|
||||||
output: (frame: AudioFrame) => {
|
output: (frame: AudioData) => {
|
||||||
this.renderer.emit(frame)
|
this.renderer.emit(frame)
|
||||||
},
|
},
|
||||||
error: (err: Error) => {
|
error: (err: Error) => {
|
||||||
console.warn(err)
|
console.warn(err)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
console.log(audio)
|
||||||
|
|
||||||
decoder.configure({
|
decoder.configure({
|
||||||
codec: audio.codec,
|
codec: audio.codec,
|
||||||
|
numberOfChannels: audio.audio.channel_count,
|
||||||
|
sampleRate: audio.audio.sample_rate,
|
||||||
// optimizeForLatency: true
|
// optimizeForLatency: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -61,6 +65,7 @@ export class Decoder {
|
||||||
for (let sample of samples) {
|
for (let sample of samples) {
|
||||||
// TODO this assumes that timescale == sample rate
|
// TODO this assumes that timescale == sample rate
|
||||||
decoder.decode(new EncodedAudioChunk({
|
decoder.decode(new EncodedAudioChunk({
|
||||||
|
type: sample.is_sync ? "key" : "delta",
|
||||||
data: sample.data,
|
data: sample.data,
|
||||||
duration: sample.duration,
|
duration: sample.duration,
|
||||||
timestamp: sample.dts,
|
timestamp: sample.dts,
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
import * as Message from "./message"
|
import * as Message from "./message"
|
||||||
|
import { Renderer } from "./renderer"
|
||||||
|
import { Decoder } from "./decoder"
|
||||||
|
|
||||||
// Wrapper around the WebWorker API
|
|
||||||
export default class Audio {
|
export default class Audio {
|
||||||
worker: Worker;
|
renderer: Renderer;
|
||||||
|
decoder: Decoder;
|
||||||
|
|
||||||
constructor(config: Message.Config) {
|
constructor(config: Message.Config) {
|
||||||
this.worker = new Worker(new URL('worker.ts', import.meta.url), { type: "module" })
|
this.renderer = new Renderer(config)
|
||||||
this.worker.postMessage({ config }, [])
|
this.decoder = new Decoder(config, this.renderer)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(init: Message.Init) {
|
async init(init: Message.Init) {
|
||||||
this.worker.postMessage({ init }) // note: we copy the raw init bytes each time
|
await this.decoder.init(init)
|
||||||
}
|
}
|
||||||
|
|
||||||
segment(segment: Message.Segment) {
|
async segment(segment: Message.Segment) {
|
||||||
this.worker.postMessage({ segment }, [ segment.buffer.buffer, segment.reader ])
|
await this.decoder.decode(segment)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import * as MP4 from "../mp4"
|
import * as MP4 from "../mp4"
|
||||||
|
|
||||||
export interface Config {
|
export interface Config {
|
||||||
// temporarily empty
|
ctx: AudioContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Init {
|
export interface Init {
|
||||||
|
|
|
@ -3,7 +3,6 @@ import * as Message from "./message";
|
||||||
import Source from "./source";
|
import Source from "./source";
|
||||||
|
|
||||||
export class Renderer {
|
export class Renderer {
|
||||||
ctx: AudioContext;
|
|
||||||
source: Source;
|
source: Source;
|
||||||
|
|
||||||
render: number; // non-zero if requestAnimationFrame has been called
|
render: number; // non-zero if requestAnimationFrame has been called
|
||||||
|
@ -11,16 +10,15 @@ export class Renderer {
|
||||||
|
|
||||||
maxDuration: number; // the maximum duration allowed in the buffer
|
maxDuration: number; // the maximum duration allowed in the buffer
|
||||||
|
|
||||||
constructor() {
|
constructor(config: Message.Config) {
|
||||||
this.render = 0;
|
this.render = 0;
|
||||||
this.maxDuration = 10 * 1000
|
this.maxDuration = 10 * 1000
|
||||||
|
|
||||||
// TODO evaluate { latencyHint: "interactive" }
|
// TODO evaluate { latencyHint: "interactive" }
|
||||||
this.ctx = new AudioContext()
|
this.source = new Source(config.ctx)
|
||||||
this.source = new Source(this.ctx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(frame: AudioFrame) {
|
emit(frame: AudioData) {
|
||||||
this.source.emit(frame)
|
this.source.emit(frame)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -55,7 +55,7 @@ export default class Source {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(frame: AudioFrame) {
|
emit(frame: AudioData) {
|
||||||
for (let i = 0; i < frame.channels; i += 1) {
|
for (let i = 0; i < frame.channels; i += 1) {
|
||||||
const ring = this.channels[i]
|
const ring = this.channels[i]
|
||||||
ring.set(frame, i)
|
ring.set(frame, i)
|
||||||
|
|
|
@ -9,8 +9,8 @@ self.addEventListener('message', async (e: MessageEvent) => {
|
||||||
if (e.data.config) {
|
if (e.data.config) {
|
||||||
const config = e.data.config as Message.Config;
|
const config = e.data.config as Message.Config;
|
||||||
|
|
||||||
renderer = new Renderer()
|
renderer = new Renderer(config)
|
||||||
decoder = new Decoder(renderer)
|
decoder = new Decoder(config, renderer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.data.init) {
|
if (e.data.init) {
|
||||||
|
|
|
@ -29,12 +29,6 @@ body {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#vid {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
max-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
#controls {
|
#controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
|
@ -27,7 +27,9 @@ export class Player {
|
||||||
this.tracks = new Map();
|
this.tracks = new Map();
|
||||||
|
|
||||||
// TODO move these to another class so this only deals with the transport.
|
// TODO move these to another class so this only deals with the transport.
|
||||||
this.audio = new Audio({})
|
this.audio = new Audio({
|
||||||
|
ctx: new AudioContext(),
|
||||||
|
})
|
||||||
this.video = new Video({
|
this.video = new Video({
|
||||||
canvas: props.canvas.transferControlToOffscreen(),
|
canvas: props.canvas.transferControlToOffscreen(),
|
||||||
})
|
})
|
||||||
|
@ -133,13 +135,13 @@ export class Player {
|
||||||
throw new Error("expected a single track")
|
throw new Error("expected a single track")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.audioTracks) {
|
if (info.audioTracks.length) {
|
||||||
this.audio.init({
|
this.audio.init({
|
||||||
track: msg.id,
|
track: msg.id,
|
||||||
info: info,
|
info: info,
|
||||||
raw: track.raw,
|
raw: track.raw,
|
||||||
})
|
})
|
||||||
} else if (info.videoTracks) {
|
} else if (info.videoTracks.length) {
|
||||||
this.video.init({
|
this.video.init({
|
||||||
track: msg.id,
|
track: msg.id,
|
||||||
info: info,
|
info: info,
|
||||||
|
@ -160,13 +162,13 @@ export class Player {
|
||||||
// Wait until we learn if this is an audio or video track
|
// Wait until we learn if this is an audio or video track
|
||||||
const info = await track.info
|
const info = await track.info
|
||||||
|
|
||||||
if (info.audioTracks) {
|
if (info.audioTracks.length) {
|
||||||
this.audio.segment({
|
this.audio.segment({
|
||||||
track: msg.init,
|
track: msg.init,
|
||||||
buffer: stream.buffer,
|
buffer: stream.buffer,
|
||||||
reader: stream.reader,
|
reader: stream.reader,
|
||||||
})
|
})
|
||||||
} else if (info.videoTracks) {
|
} else if (info.videoTracks.length) {
|
||||||
this.video.segment({
|
this.video.segment({
|
||||||
track: msg.init,
|
track: msg.init,
|
||||||
buffer: stream.buffer,
|
buffer: stream.buffer,
|
||||||
|
|
|
@ -743,6 +743,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
|
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
|
||||||
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
|
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
|
||||||
|
|
||||||
|
"@types/audioworklet@^0.0.41":
|
||||||
|
version "0.0.41"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/audioworklet/-/audioworklet-0.0.41.tgz#ed2e67c7dcbe207974c54812e21819984c74a4f0"
|
||||||
|
integrity sha512-8BWffzGoSRz436IviQVPye75YYWfac4OKdcLgkZxb3APZxSmAOp2SMtsH1yuM1x57/z/J7bsm05Yq98Hzk1t/w==
|
||||||
|
|
||||||
"@types/dom-webcodecs@^0.1.6":
|
"@types/dom-webcodecs@^0.1.6":
|
||||||
version "0.1.6"
|
version "0.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/@types/dom-webcodecs/-/dom-webcodecs-0.1.6.tgz#204f2c8c2e6286114e1ec2997630bc0fd2081ffa"
|
resolved "https://registry.yarnpkg.com/@types/dom-webcodecs/-/dom-webcodecs-0.1.6.tgz#204f2c8c2e6286114e1ec2997630bc0fd2081ffa"
|
||||||
|
|
Loading…
Reference in New Issue