diff --git a/dev/convert.html b/dev/convert.html index 96c013a..9f18bfd 100644 --- a/dev/convert.html +++ b/dev/convert.html @@ -26,7 +26,7 @@ chunked: true, chunkSize: 2**20 }); - const outputFormat = new Mediabunny.Mp4OutputFormat({}); + const outputFormat = new Mediabunny.Mp4OutputFormat({fastStart: 'fragmented'}); const button = document.createElement('button'); button.textContent = 'Cancel'; diff --git a/dev/demux.html b/dev/demux.html index 233aae3..baa5541 100644 --- a/dev/demux.html +++ b/dev/demux.html @@ -18,10 +18,11 @@ }); const videoTrack = await input.getPrimaryVideoTrack(); - const sink = new Mediabunny.VideoSampleSink(videoTrack); + const sink = new Mediabunny.EncodedPacketSink(videoTrack); - const sample = await sink.getSample(await videoTrack.getFirstTimestamp()); - console.log(sample); + for await (const packet of sink.packets()) { + console.log(packet.timestamp); + } /* diff --git a/dev/live.html b/dev/live.html index bdd2fc2..d21faec 100644 --- a/dev/live.html +++ b/dev/live.html @@ -19,7 +19,7 @@ button.addEventListener('click', async () => { const stream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: false }); const videoTrack = stream.getVideoTracks()[0]; - const audioTrack = null;//stream.getAudioTracks()[0]; + const audioTrack = stream.getAudioTracks()[0]; const output = new Mediabunny.Output({ target: new Mediabunny.BufferTarget(), @@ -29,7 +29,7 @@ let audioSource = null; if (videoTrack) { videoSource = new Mediabunny.MediaStreamVideoTrackSource(videoTrack, { - codec: 'avc', + codec: 'vp9', bitrate: Mediabunny.QUALITY_MEDIUM, sizeChangeBehavior: 'passThrough', }); @@ -40,13 +40,13 @@ } if (audioTrack) { audioSource = new Mediabunny.MediaStreamAudioTrackSource(audioTrack, { - codec: 'mp3', + codec: 'opus', bitrate: Mediabunny.QUALITY_MEDIUM }); audioSource.errorPromise.catch((d) => console.log("Hello!!???", d)); - //output.addAudioTrack(audioSource); + output.addAudioTrack(audioSource); } await output.start(); diff --git a/package-lock.json b/package-lock.json index 3c36243..a36ae7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mediabunny", - "version": "1.38.0", + "version": "1.38.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mediabunny", - "version": "1.38.0", + "version": "1.38.1", "license": "MPL-2.0", "workspaces": [ "packages/*" @@ -7751,9 +7751,9 @@ } }, "node_modules/mediabunny": { - "version": "1.37.0", - "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.37.0.tgz", - "integrity": "sha512-eV7M9IJ29pr/8RNL1sYtIxNbdMfDMN1hMwMaOFfNLhwuKKGSC+eKwiJFpdVjEJ3zrMA4LGerF4Hps0SENFSAlg==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.38.0.tgz", + "integrity": "sha512-FiZtTWLY4rmEw4jBx8r5Tk+sE+PcBb03UYUZNv9DujEy9wdmX7JaQOvHNUZLVTxPWT0h+LYmWFnWKRkl4KmzyA==", "license": "MPL-2.0", "peer": true, "workspaces": [ @@ -12077,7 +12077,7 @@ }, "packages/aac-encoder": { "name": "@mediabunny/aac-encoder", - "version": "1.38.0", + "version": "1.38.1", "license": "MPL-2.0", "devDependencies": { "@types/emscripten": "^1.40.1" @@ -12092,7 +12092,7 @@ }, "packages/ac3": { "name": "@mediabunny/ac3", - "version": "1.38.0", + "version": "1.38.1", "license": "MPL-2.0", "devDependencies": { "@types/emscripten": "^1.40.1" @@ -12107,7 +12107,7 @@ }, "packages/flac-encoder": { "name": "@mediabunny/flac-encoder", - "version": "1.38.0", + "version": "1.38.1", "license": "MPL-2.0", "devDependencies": { "@types/emscripten": "^1.40.1" @@ -12122,7 +12122,7 @@ }, "packages/mp3-encoder": { "name": "@mediabunny/mp3-encoder", - "version": "1.38.0", + "version": "1.38.1", "license": "MPL-2.0", "devDependencies": { "@types/emscripten": "^1.40.1" diff --git a/package.json b/package.json index 669a648..424c03e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mediabunny", "author": "Vanilagy", - "version": "1.38.0", + "version": "1.38.1", "description": "Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.", "type": "module", "workspaces": [ diff --git a/packages/aac-encoder/package.json b/packages/aac-encoder/package.json index 786a388..e25728b 100644 --- a/packages/aac-encoder/package.json +++ b/packages/aac-encoder/package.json @@ -1,7 +1,7 @@ { "name": "@mediabunny/aac-encoder", "author": "Vanilagy", - "version": "1.38.0", + "version": "1.38.1", "description": "AAC encoder extension for Mediabunny, based on FFmpeg.", "main": "./dist/bundles/mediabunny-aac-encoder.mjs", "module": "./dist/bundles/mediabunny-aac-encoder.mjs", diff --git a/packages/ac3/package.json b/packages/ac3/package.json index de7d2d4..f353e54 100644 --- a/packages/ac3/package.json +++ b/packages/ac3/package.json @@ -1,7 +1,7 @@ { "name": "@mediabunny/ac3", "author": "Vanilagy", - "version": "1.38.0", + "version": "1.38.1", "description": "AC-3 and E-AC-3 (Dolby Digital) decoder and encoder extension for Mediabunny, based on FFmpeg.", "main": "./dist/bundles/mediabunny-ac3.mjs", "module": "./dist/bundles/mediabunny-ac3.mjs", diff --git a/packages/flac-encoder/package.json b/packages/flac-encoder/package.json index ef0109d..4a8da15 100644 --- a/packages/flac-encoder/package.json +++ b/packages/flac-encoder/package.json @@ -1,7 +1,7 @@ { "name": "@mediabunny/flac-encoder", "author": "Vanilagy", - "version": "1.38.0", + "version": "1.38.1", "description": "FLAC encoder extension for Mediabunny, based on libFLAC.", "main": "./dist/bundles/mediabunny-flac-encoder.mjs", "module": "./dist/bundles/mediabunny-flac-encoder.mjs", diff --git a/packages/mp3-encoder/package.json b/packages/mp3-encoder/package.json index 6128c54..125a384 100644 --- a/packages/mp3-encoder/package.json +++ b/packages/mp3-encoder/package.json @@ -1,7 +1,7 @@ { "name": "@mediabunny/mp3-encoder", "author": "Vanilagy", - "version": "1.38.0", + "version": "1.38.1", "description": "MP3 encoder extension for Mediabunny, based on LAME.", "main": "./dist/bundles/mediabunny-mp3-encoder.mjs", "module": "./dist/bundles/mediabunny-mp3-encoder.mjs", diff --git a/src/isobmff/isobmff-muxer.ts b/src/isobmff/isobmff-muxer.ts index 4256f53..b7a6158 100644 --- a/src/isobmff/isobmff-muxer.ts +++ b/src/isobmff/isobmff-muxer.ts @@ -1245,11 +1245,13 @@ export class IsobmffMuxer extends Muxer { override async onTrackClose(track: OutputTrack) { const release = await this.mutex.acquire(); - if (track.type === 'subtitle' && track.source._codec === 'webvtt') { - const trackData = this.trackDatas.find(x => x.track === track) as IsobmffSubtitleTrackData; - if (trackData) { + const trackData = this.trackDatas.find(x => x.track === track); + if (trackData) { + if (trackData.type === 'subtitle' && track.source._codec === 'webvtt') { await this.processWebVTTCues(trackData, Infinity); } + + this.processTimestamps(trackData); } if (this.allTracksAreKnown()) { @@ -1274,19 +1276,15 @@ export class IsobmffMuxer extends Muxer { if (trackData.type === 'subtitle' && trackData.track.source._codec === 'webvtt') { await this.processWebVTTCues(trackData, Infinity); } + + this.processTimestamps(trackData); } if (this.isFragmented) { await this.interleaveSamples(true); - - for (const trackData of this.trackDatas) { - this.processTimestamps(trackData); - } - await this.finalizeFragment(false); // Don't flush the last fragment as we will flush it with the mfra box } else { for (const trackData of this.trackDatas) { - this.processTimestamps(trackData); await this.finalizeCurrentChunk(trackData); } } diff --git a/src/media-source.ts b/src/media-source.ts index c3b4604..e47679b 100644 --- a/src/media-source.ts +++ b/src/media-source.ts @@ -1076,6 +1076,8 @@ export class MediaStreamVideoTrackSource extends VideoSource { private _lastVideoFrame: VideoFrame | null = null; /** @internal */ private _timerHandle: UnthrottledTimerHandle | null = null; + /** @internal */ + private _videoElement: HTMLVideoElement | null = null; /** A promise that rejects upon any error within this source. This promise never resolves. */ get errorPromise() { @@ -1160,12 +1162,13 @@ export class MediaStreamVideoTrackSource extends VideoSource { while (now - lastFrameTime > 1000 / frameRate) { lastFrameTime += 1000 / frameRate; const timestamp = firstVideoFrameTimestamp + frameCount / frameRate; - const clone = new VideoFrame(this._lastVideoFrame, { + + const frame = new VideoFrame(this._videoElement ?? this._lastVideoFrame, { timestamp: 1e6 * timestamp, duration: 1e6 / frameRate, }); - addVideoFrame(clone, now); + addVideoFrame(frame, now); } }; @@ -1298,8 +1301,44 @@ export class MediaStreamVideoTrackSource extends VideoSource { }; mediaStreamTrackProcessorWorker!.addEventListener('message', this._workerListener); + } else if (frameRate !== null) { + // No MediaStreamTrackProcessor support at all (e.g. Firefox), but we have a frame rate, so we can + // manually sample from a hidden