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.`,
);
}