Fix some demuxer bugs that occurred on files by FFmpeg, fix packet marking logic

This commit is contained in:
Vanilagy
2026-01-15 08:51:07 +01:00
parent 0719a17c85
commit 8fea9671ea
2 changed files with 37 additions and 9 deletions
+17 -8
View File
@@ -135,8 +135,9 @@ export class MpegTsDemuxer extends Demuxer {
}
let currentPos = this.packetOffset;
let programMapPid: number | null = null;
// Some files contain these multiple times, but we only care about their first appearance
let hasProgramAssociationTable = false;
let hasProgramMap = false;
while (true) {
@@ -148,7 +149,7 @@ export class MpegTsDemuxer extends Demuxer {
const BYTES_BEFORE_SECTION_LENGTH = 3;
const BITS_IN_CRC_32 = 32; // Duh
if (section.pid === 0) {
if (section.pid === 0 && !hasProgramAssociationTable) {
const bitstream = new Bitstream(section.payload);
const pointerField = bitstream.readAlignedByte();
@@ -175,7 +176,9 @@ export class MpegTsDemuxer extends Demuxer {
if (programMapPid === null) {
throw new Error('Program Association Table must link to a Program Map Table.');
}
} else if (section.pid === programMapPid) {
hasProgramAssociationTable = true;
} else if (section.pid === programMapPid && !hasProgramMap) {
const bitstream = new Bitstream(section.payload);
const pointerField = bitstream.readAlignedByte();
@@ -1258,8 +1261,9 @@ class MpegTsVideoTrackBacking extends MpegTsTrackBacking implements InputVideoTr
let remaining = context.ensureBuffered(CHUNK_SIZE);
if (remaining instanceof Promise) remaining = await remaining;
// Search for start codes in the current chunk
for (let i = 0; i < remaining; i++) {
const startPos = context.currentPos;
while (context.currentPos - startPos < remaining) {
const byte = context.readU8();
// Look for 0x00 as potential start of a start code
@@ -1281,6 +1285,7 @@ class MpegTsVideoTrackBacking extends MpegTsTrackBacking implements InputVideoTr
context.seekTo(packetStartPos);
return context.supplyPacket(packetLength, 0);
}
return;
}
@@ -1394,14 +1399,16 @@ class MpegTsAudioTrackBacking extends MpegTsTrackBacking implements InputAudioTr
let remaining = context.ensureBuffered(CHUNK_SIZE);
if (remaining instanceof Promise) remaining = await remaining;
for (let i = 0; i < remaining; i++) {
const startPos = context.currentPos;
while (context.currentPos - startPos < remaining) {
const byte = context.readU8();
if (byte !== 0xff) {
continue;
}
context.skip(-1);
const startPos = context.currentPos;
const possibleHeaderStartPos = context.currentPos;
let remaining = context.ensureBuffered(MAX_FRAME_HEADER_SIZE);
if (remaining instanceof Promise) remaining = await remaining;
@@ -1414,7 +1421,7 @@ class MpegTsAudioTrackBacking extends MpegTsTrackBacking implements InputAudioTr
const header = readAdtsFrameHeader(FileSlice.tempFromBytes(headerBytes));
if (header) {
context.seekTo(startPos);
context.seekTo(possibleHeaderStartPos);
let remaining = context.ensureBuffered(header.frameLength);
if (remaining instanceof Promise) remaining = await remaining;
@@ -1423,6 +1430,8 @@ class MpegTsAudioTrackBacking extends MpegTsTrackBacking implements InputAudioTr
remaining,
Math.round(SAMPLES_PER_AAC_FRAME * TIMESCALE / this.elementaryStream.info.sampleRate),
);
} else {
context.seekTo(possibleHeaderStartPos + 1);
}
}
+20 -1
View File
@@ -1,6 +1,6 @@
import { expect, test } from 'vitest';
import { Input } from '../../src/input.js';
import { FilePathSource, ReadableStreamSource } from '../../src/source.js';
import { FilePathSource, ReadableStreamSource, UrlSource } from '../../src/source.js';
import path from 'node:path';
import fs from 'node:fs';
import { Readable } from 'node:stream';
@@ -450,3 +450,22 @@ test('MPEG-TS with unknown file size (ReadableStreamSource)', async () => {
expect((videoTrack._backing as unknown as MpegTsTrackBacking).referencePesPackets.length)
.toBeGreaterThanOrEqual(10);
});
test('MPEG-TS transmuxed by FFmpeg', async () => {
using input = new Input({
source: new UrlSource('https://pub-cf9fcfcb5c0a44e9b1bb5ff890e041ae.r2.dev/trim-buck-bunny-ffmpeg.ts'),
formats: ALL_FORMATS,
});
const videoTrack = await input.getPrimaryVideoTrack();
assert(videoTrack);
const audioTrack = await input.getPrimaryAudioTrack();
assert(audioTrack);
const videoPacketStats = await videoTrack.computePacketStats();
const audioPacketStats = await audioTrack.computePacketStats();
expect(videoPacketStats.packetCount).toBe(121);
expect(audioPacketStats.packetCount).toBe(235);
});