diff --git a/dev/demux.html b/dev/demux.html index 8c5310b..c0c8346 100644 --- a/dev/demux.html +++ b/dev/demux.html @@ -14,8 +14,8 @@ source: new Mediabunny.BlobSource(file), }); - const videoTrack = await input.getPrimaryVideoTrack(); - const sink = new Mediabunny.EncodedPacketSink(videoTrack); + const audioTrack = await input.getPrimaryAudioTrack(); + const sink = new Mediabunny.EncodedPacketSink(audioTrack); for await (const packet of sink.packets()) { console.log(packet.timestamp, packet.duration, packet.type) diff --git a/src/matroska/matroska-demuxer.ts b/src/matroska/matroska-demuxer.ts index 32880d5..9af38db 100644 --- a/src/matroska/matroska-demuxer.ts +++ b/src/matroska/matroska-demuxer.ts @@ -1374,9 +1374,17 @@ export class MatroskaDemuxer extends Demuxer { const relativeTimestamp = readI16Be(slice); const flags = readU8(slice); - const isKeyFrame = !!(flags & 0x80); const lacing = (flags >> 1) & 0x3 as BlockLacing; // If the block is laced, we'll expand it later + let isKeyFrame = !!(flags & 0x80); + if (trackData.track.info?.type === 'audio' && trackData.track.info.codec) { + // Some files don't mark their audio packets as key packets (I'm looking at you, Firefox). But, we + // can fix this in most cases: if we recognize the codec of the track, then we know every packet is + // necessarily a key packet, no matter what the container says. + // https://github.com/Vanilagy/mediabunny/issues/192 + isKeyFrame = true; + } + const blockData = readBytes(slice, size - (slice.filePos - dataStartPos)); const hasDecodingInstructions = trackData.track.decodingInstructions.length > 0; diff --git a/src/muxer.ts b/src/muxer.ts index b23356f..de2a36e 100644 --- a/src/muxer.ts +++ b/src/muxer.ts @@ -46,21 +46,21 @@ export abstract class Muxer { private trackTimestampInfo = new WeakMap(); - protected validateAndNormalizeTimestamp(track: OutputTrack, timestampInSeconds: number, isKeyFrame: boolean) { + protected validateAndNormalizeTimestamp(track: OutputTrack, timestampInSeconds: number, isKeyPacket: boolean) { timestampInSeconds += track.source._timestampOffset; let timestampInfo = this.trackTimestampInfo.get(track); if (!timestampInfo) { - if (!isKeyFrame) { - throw new Error('First frame must be a key frame.'); + if (!isKeyPacket) { + throw new Error('First packet must be a key packet.'); } timestampInfo = { maxTimestamp: timestampInSeconds, - maxTimestampBeforeLastKeyFrame: timestampInSeconds, + maxTimestampBeforeLastKeyPacket: timestampInSeconds, }; this.trackTimestampInfo.set(track, timestampInfo); } @@ -69,15 +69,15 @@ export abstract class Muxer { throw new Error(`Timestamps must be non-negative (got ${timestampInSeconds}s).`); } - if (isKeyFrame) { - timestampInfo.maxTimestampBeforeLastKeyFrame = timestampInfo.maxTimestamp; + if (isKeyPacket) { + timestampInfo.maxTimestampBeforeLastKeyPacket = timestampInfo.maxTimestamp; } - if (timestampInSeconds < timestampInfo.maxTimestampBeforeLastKeyFrame) { + if (timestampInSeconds < timestampInfo.maxTimestampBeforeLastKeyPacket) { throw new Error( - `Timestamps cannot be smaller than the highest timestamp of the previous GOP (a GOP begins with a key` - + ` frame and ends right before the next key frame). Got ${timestampInSeconds}s, but highest timestamp` - + ` is ${timestampInfo.maxTimestampBeforeLastKeyFrame}s.`, + `Timestamps cannot be smaller than the largest timestamp of the previous GOP (a GOP begins with a key` + + ` packet and ends right before the next key packet). Got ${timestampInSeconds}s, but largest` + + ` timestamp is ${timestampInfo.maxTimestampBeforeLastKeyPacket}s.`, ); }