From 78e78e5b4f3ff92603a01c540d7b7a9bbb694e65 Mon Sep 17 00:00:00 2001 From: Vanilagy <1696106+Vanilagy@users.noreply.github.com> Date: Mon, 12 Jan 2026 14:36:51 +0100 Subject: [PATCH] Make _flushOrWaitForOngoingClose set closingPromise (fixes #270) --- src/media-source.ts | 10 ++++----- test/browser/media-sources.test.ts | 35 ++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 test/browser/media-sources.test.ts diff --git a/src/media-source.ts b/src/media-source.ts index 7b5cae6..ddf746b 100644 --- a/src/media-source.ts +++ b/src/media-source.ts @@ -134,12 +134,10 @@ export abstract class MediaSource { /** @internal */ async _flushOrWaitForOngoingClose(forceClose: boolean) { - if (this._closingPromise) { - // Since closing also flushes, we don't want to do it twice - return this._closingPromise; - } else { - return this._flushAndClose(forceClose); - } + return this._closingPromise ??= (async () => { + await this._flushAndClose(forceClose); + this._closed = true; + })(); } } diff --git a/test/browser/media-sources.test.ts b/test/browser/media-sources.test.ts new file mode 100644 index 0000000..cd218ba --- /dev/null +++ b/test/browser/media-sources.test.ts @@ -0,0 +1,35 @@ +import { test } from 'vitest'; +import { Output } from '../../src/output.js'; +import { WebMOutputFormat } from '../../src/output-format.js'; +import { BufferTarget } from '../../src/target.js'; +import { VideoSampleSource } from '../../src/media-source.js'; +import { VideoSample } from '../../src/sample.js'; +import { QUALITY_MEDIUM } from '../../src/encode.js'; + +test('VideoSampleSource.close() should be idempotent after finalize()', async () => { + const output = new Output({ + format: new WebMOutputFormat(), + target: new BufferTarget(), + }); + + const videoSource = new VideoSampleSource({ + codec: 'vp8', + bitrate: QUALITY_MEDIUM, + }); + + output.addVideoTrack(videoSource); + await output.start(); + + const canvas = new OffscreenCanvas(100, 100); + const ctx = canvas.getContext('2d')!; + ctx.fillStyle = 'red'; + ctx.fillRect(0, 0, 100, 100); + + const sample = new VideoSample(canvas, { timestamp: 0, duration: 1 / 30 }); + await videoSource.add(sample); + sample.close(); + + await output.finalize(); + + videoSource.close(); // This previously threw +});