From b48eb75565b73dd9dbd3520ef92d2ec9c088fc3b Mon Sep 17 00:00:00 2001 From: Theredbt Date: Mon, 11 May 2026 04:22:46 +0100 Subject: [PATCH] fix(input-format): recognise CMAF segments that start with sidx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sibling case to #308: when a fragmented MP4 segment begins with a sidx box (no ftyp/styp/moof at the file root), Mp4InputFormat._canReadInput returned false, causing Input.getTracks() to throw UnsupportedInputFormatError. This shape is standard in CMAF when the DASH on-demand profile is used or when HLS playlists are derived from one (Vimeo's vod-adaptive-ak CDN, for example). The segment is still a valid ISOBMFF fragment — the sidx just sits before the moof. Adds 'sidx' alongside the existing 'moof' check so the format probe accepts these segments, and a focused regression test that fails on the previous behaviour. --- src/input-format.ts | 3 ++- test/node/hls-input.test.ts | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/input-format.ts b/src/input-format.ts index 6c75c8d..e1866a1 100644 --- a/src/input-format.ts +++ b/src/input-format.ts @@ -120,7 +120,8 @@ export class Mp4InputFormat extends IsobmffInputFormat { if (slice instanceof Promise) slice = await slice; if (!slice) return false; - return readAscii(slice, 4) === 'moof'; // Seen in HLS for example + const fourCc = readAscii(slice, 4); + return fourCc === 'moof' || fourCc === 'sidx'; // Seen in HLS for example } get name() { diff --git a/test/node/hls-input.test.ts b/test/node/hls-input.test.ts index b49dc5c..3ed47ec 100644 --- a/test/node/hls-input.test.ts +++ b/test/node/hls-input.test.ts @@ -1,7 +1,7 @@ /* eslint-disable @stylistic/max-len */ import { ALL_FORMATS, BufferSource, EncodedPacketSink, Input, InputAudioTrack, InputVideoTrack, UrlSource } from 'mediabunny'; import { expect, test, vi } from 'vitest'; -import { HLS, HLS_FORMATS, HlsInputFormat } from '../../src/input-format.js'; +import { HLS, HLS_FORMATS, HlsInputFormat, MP4 } from '../../src/input-format.js'; import { assert, hexStringToBytes, rejectAfter } from '../../src/misc.js'; import { CustomPathedSource } from '../../src/source.js'; @@ -788,6 +788,27 @@ test.concurrent('Live HLS', { timeout: 30_000 }, async () => { expect(consoleSpy).not.toHaveBeenCalled(); }); +test.concurrent('Mp4InputFormat recognises a CMAF segment that starts with sidx', async () => { + // Sibling of the moof case fixed in #308: CMAF segments using the DASH + // on-demand profile (and HLS playlists derived from it) place a sidx + // before the moof. Without ftyp/styp at the file start the existing + // format probe used to bail out, producing UnsupportedInputFormatError. + // + // Repro shape (real Vimeo segment): 32 60 73 69 64 78 ... + // (size 0x32 60-byte sidx box header, four-cc 'sidx'). + const bytes = new Uint8Array(12); + new DataView(bytes.buffer).setUint32(0, 60); // box size + bytes.set([0x73, 0x69, 0x64, 0x78], 4); // 'sidx' + + using input = new Input({ + source: new BufferSource(bytes), + formats: ALL_FORMATS, + }); + + expect(await input.canRead()).toBe(true); + expect(await input.getFormat()).toBe(MP4); +}); + test.concurrent('#EXT-X-I-FRAME-STREAM-INF tags are parsed properly', async () => { const text = `#EXTM3U #EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=256000,CODECS="avc1.4D401E",RESOLUTION=480x270,URI="7f2459cb12854fdbbc7ec1e7279da179/f74fb10563564130a4702743d64112a3/index_11.m3u8"