Compare commits

...
6 Commits
Author SHA1 Message Date
Vanilagy c67c5e4072 Bump patch 2026-09-04 22:56:36 +02:00
hi.jack2eeandGitHub 6c88763a5c Terminate sample iterators before throwing so late-decoded samples are closed (#484)
for await never calls return() on an iterator whose next() threw, so the
error branches in mediaSamplesInRange / mediaSamplesAtTimestamps left the
iteration un-terminated: the pump's decoder callback kept queueing decoded
samples that no consumer or cleanup path could ever close, orphaning them
until GC ("A VideoSample was garbage collected without first being closed").

Set terminated (and ended, where the pump gates on it) before throwing in
both the InputDisposedError and out-of-band error branches of both sample
generators - samples emitted after the throw are then closed on arrival by
the existing checks in the decoder callbacks.

Observed on iOS Safari when the hardware VideoDecoder dies mid-iteration
("Decoding task did not complete"): 3-14 samples orphaned per failing
iterator; with this change, at most the single sample already in flight
during the error microtask window.
2026-09-04 10:01:18 +02:00
Vanilagy 5954a472fa Remove console.log 2026-09-04 10:00:33 +02:00
Vanilagy 6319bf2eee Bump patch 2026-09-03 10:48:32 +02:00
Vanilagy 5ffb5a0979 Add sponsor 2026-09-03 10:48:03 +02:00
Vanilagy cf2edfada2 Relax audio track assert for esds box (fixes #481) 2026-09-02 09:32:06 +02:00
13 changed files with 33 additions and 23 deletions
+1
View File
@@ -127,6 +127,7 @@ const sponsors = {
{ image: 'https://avatars.githubusercontent.com/u/2565549', name: 'MatthewNorton', url: 'https://github.com/MatthewNorton' },
{ image: 'https://avatars.githubusercontent.com/u/272247752', name: 'Motionik', url: 'https://github.com/Motionik' },
{ image: 'https://avatars.githubusercontent.com/u/2698271', name: 'Matthew Gardner', url: 'https://github.com/spheric' },
{ image: 'https://avatars.githubusercontent.com/u/41021374', name: 'arth', url: 'https://github.com/arthtyagi' },
{ image: 'https://avatars.githubusercontent.com/u/5475819', name: 'AJ Funk', url: 'https://github.com/AJFunk' },
{ image: 'https://avatars.githubusercontent.com/u/30229596', name: 'Pablo Bonilla', url: 'https://github.com/devPablo' },
{ image: 'https://avatars.githubusercontent.com/u/536008', name: 'Satish Goda', url: 'https://github.com/satishgoda' },
+10 -10
View File
@@ -1,12 +1,12 @@
{
"name": "mediabunny",
"version": "1.55.5",
"version": "1.55.7",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mediabunny",
"version": "1.55.5",
"version": "1.55.7",
"license": "MPL-2.0",
"workspaces": [
".",
@@ -12958,7 +12958,7 @@
},
"packages/aac-encoder": {
"name": "@mediabunny/aac-encoder",
"version": "1.55.5",
"version": "1.55.7",
"license": "MPL-2.0",
"devDependencies": {
"@types/emscripten": "^1.40.1"
@@ -12973,7 +12973,7 @@
},
"packages/ac3": {
"name": "@mediabunny/ac3",
"version": "1.55.5",
"version": "1.55.7",
"license": "MPL-2.0",
"devDependencies": {
"@types/emscripten": "^1.40.1"
@@ -12988,7 +12988,7 @@
},
"packages/dts": {
"name": "@mediabunny/dts",
"version": "1.55.5",
"version": "1.55.7",
"license": "MPL-2.0",
"devDependencies": {
"@types/emscripten": "^1.40.1"
@@ -13003,7 +13003,7 @@
},
"packages/flac-encoder": {
"name": "@mediabunny/flac-encoder",
"version": "1.55.5",
"version": "1.55.7",
"license": "MPL-2.0",
"devDependencies": {
"@types/emscripten": "^1.40.1"
@@ -13018,7 +13018,7 @@
},
"packages/mp3-encoder": {
"name": "@mediabunny/mp3-encoder",
"version": "1.55.5",
"version": "1.55.7",
"license": "MPL-2.0",
"devDependencies": {
"@types/emscripten": "^1.40.1"
@@ -13033,7 +13033,7 @@
},
"packages/prores": {
"name": "@mediabunny/prores",
"version": "1.55.5",
"version": "1.55.7",
"license": "MPL-2.0",
"dependencies": {
"turbores": "^1.2.2"
@@ -13048,10 +13048,10 @@
},
"packages/server": {
"name": "@mediabunny/server",
"version": "1.55.5",
"version": "1.55.7",
"license": "MPL-2.0",
"dependencies": {
"@mediabunny/prores": "^1.55.5",
"@mediabunny/prores": "^1.55.7",
"node-av": "^6.0.0"
},
"funding": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "mediabunny",
"author": "Vanilagy",
"version": "1.55.5",
"version": "1.55.7",
"description": "Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.",
"type": "module",
"workspaces": [
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@mediabunny/aac-encoder",
"author": "Vanilagy",
"version": "1.55.5",
"version": "1.55.7",
"description": "AAC encoder extension for Mediabunny, based on libavcodec.",
"main": "./dist/bundles/mediabunny-aac-encoder.mjs",
"module": "./dist/bundles/mediabunny-aac-encoder.mjs",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@mediabunny/ac3",
"author": "Vanilagy",
"version": "1.55.5",
"version": "1.55.7",
"description": "AC-3 and E-AC-3 (Dolby Digital) decoder and encoder extension for Mediabunny, based on libavcodec.",
"main": "./dist/bundles/mediabunny-ac3.mjs",
"module": "./dist/bundles/mediabunny-ac3.mjs",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@mediabunny/dts",
"author": "Vanilagy",
"version": "1.55.5",
"version": "1.55.7",
"description": "DTS decoder and encoder extension for Mediabunny, based on libavcodec.",
"main": "./dist/bundles/mediabunny-dts.mjs",
"module": "./dist/bundles/mediabunny-dts.mjs",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@mediabunny/flac-encoder",
"author": "Vanilagy",
"version": "1.55.5",
"version": "1.55.7",
"description": "FLAC encoder extension for Mediabunny, based on libFLAC.",
"main": "./dist/bundles/mediabunny-flac-encoder.mjs",
"module": "./dist/bundles/mediabunny-flac-encoder.mjs",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@mediabunny/mp3-encoder",
"author": "Vanilagy",
"version": "1.55.5",
"version": "1.55.7",
"description": "MP3 encoder extension for Mediabunny, based on LAME.",
"main": "./dist/bundles/mediabunny-mp3-encoder.mjs",
"module": "./dist/bundles/mediabunny-mp3-encoder.mjs",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@mediabunny/prores",
"author": "Vanilagy",
"version": "1.55.5",
"version": "1.55.7",
"description": "Apple ProRes decoder extension for Mediabunny, based on TurboRes.",
"main": "./dist/bundles/mediabunny-prores.mjs",
"module": "./dist/bundles/mediabunny-prores.mjs",
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@mediabunny/server",
"author": "Vanilagy",
"version": "1.55.5",
"version": "1.55.7",
"description": "Adds full video and audio decoder and encoder support to Mediabunny for use in server-side environments (Node, Bun, Deno). Based on NodeAV.",
"main": "./dist/bundles/mediabunny-server.cjs",
"module": "./dist/bundles/mediabunny-server.mjs",
@@ -35,7 +35,7 @@
},
"dependencies": {
"node-av": "^6.0.0",
"@mediabunny/prores": "^1.55.5"
"@mediabunny/prores": "^1.55.7"
},
"peerDependencies": {
"mediabunny": "^1.45.0"
+1 -2
View File
@@ -1558,10 +1558,9 @@ export class IsobmffDemuxer extends Demuxer {
case 'esds': {
const track = this.currentTrack;
if (!track) {
if (!track || track.info?.type !== 'audio') {
break;
}
assert(track.info?.type === 'audio');
slice.skip(4); // Version + flags
+12
View File
@@ -601,11 +601,18 @@ export abstract class BaseMediaSampleSink<
async next() {
while (true) {
if (track.input._disposed) {
// Once next() throws, the consumer will never call return(), so terminate the
// iteration here - otherwise, the pump keeps queueing decoded samples that
// nothing can ever close.
terminated = true;
ended = true;
closeSamples();
throw new InputDisposedError();
} else if (terminated) {
return { value: undefined, done: true };
} else if (hasOutOfBandError) {
terminated = true;
ended = true;
closeSamples();
throw outOfBandError;
} else if (sampleQueue.length > 0) {
@@ -827,11 +834,16 @@ export abstract class BaseMediaSampleSink<
async next() {
while (true) {
if (track.input._disposed) {
// Once next() throws, the consumer will never call return(), so terminate the
// iteration here - otherwise, the pump keeps queueing decoded samples that
// nothing can ever close.
terminated = true;
closeSamples();
throw new InputDisposedError();
} else if (terminated) {
return { value: undefined, done: true };
} else if (hasOutOfBandError) {
terminated = true;
closeSamples();
throw outOfBandError;
} else if (sampleQueue.length > 0) {
@@ -30,8 +30,6 @@ test('FLAC encoder, 24-bit', async () => {
expect(result.size).toBeGreaterThan(90_000);
console.log(result.size);
expect(sample.format).toBe('s32');
});