Skip to content

Commit

Permalink
Fix getVideoStartPts when timestamps wrap at MPEG-2 33-bit boundary
Browse files Browse the repository at this point in the history
  • Loading branch information
robwalch committed Dec 5, 2024
1 parent da67d31 commit 0af1391
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 7 deletions.
18 changes: 11 additions & 7 deletions src/remux/mp4-remuxer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,20 +100,24 @@ export default class MP4Remuxer implements Remuxer {
this.videoTrackConfig = undefined;
}

getVideoStartPts(videoSamples) {
getVideoStartPts(videoSamples: VideoSample[]) {
// Get the minimum PTS value relative to the first sample's PTS, normalized for 33-bit wrapping
let rolloverDetected = false;
const firstPts = videoSamples[0].pts;
const startPTS = videoSamples.reduce((minPTS, sample) => {
const delta = sample.pts - minPTS;
let pts = sample.pts;
let delta = pts - minPTS;
if (delta < -4294967296) {
// 2^32, see PTSNormalize for reasoning, but we're hitting a rollover here, and we don't want that to impact the timeOffset calculation
rolloverDetected = true;
return normalizePts(minPTS, sample.pts);
} else if (delta > 0) {
pts = normalizePts(pts, firstPts);
delta = pts - minPTS;
}
if (delta > 0) {
return minPTS;
} else {
return sample.pts;
}
}, videoSamples[0].pts);
return pts;
}, firstPts);
if (rolloverDetected) {
this.logger.debug('PTS rollover detected');
}
Expand Down
1 change: 1 addition & 0 deletions tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import './unit/loader/fragment-loader';
import './unit/loader/fragment';
import './unit/loader/level';
import './unit/loader/playlist-loader';
import './unit/remux/mp4-remuxer';
import './unit/utils/attr-list';
import './unit/utils/binary-search';
import './unit/utils/buffer-helper';
Expand Down
93 changes: 93 additions & 0 deletions tests/unit/remux/mp4-remuxer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import chai from 'chai';
import EventEmitter from 'eventemitter3';
import sinonChai from 'sinon-chai';
import { hlsDefaultConfig } from '../../../src/config';
import MP4Remuxer from '../../../src/remux/mp4-remuxer';
import { logger } from '../../../src/utils/logger';
import type { HlsEventEmitter } from '../../../src/events';
import type { VideoSample } from '../../../src/types/demuxer';
import type { TypeSupported } from '../../../src/utils/codecs';

chai.use(sinonChai);
const expect = chai.expect;

describe('mp4-remuxer', function () {
let mp4Remuxer: MP4Remuxer;

beforeEach(function () {
const observer: HlsEventEmitter = new EventEmitter() as HlsEventEmitter;
const config = { ...hlsDefaultConfig };
const typeSupported: TypeSupported = {
mpeg: true,
mp3: true,
ac3: true,
};
mp4Remuxer = new MP4Remuxer(observer, config, typeSupported, logger);
});

afterEach(function () {
mp4Remuxer.destroy();
});

it('should find the lowest PTS in video samples', function () {
const videoSamples = [ptsDts(0, 0), ptsDts(3003, 3003), ptsDts(6006, 6006)];
const minPts = mp4Remuxer.getVideoStartPts(videoSamples);
expect(minPts).to.eq(0);
});

it('should find the lowest PTS in video samples with decrementing PTS samples', function () {
const videoSamples = [
ptsDts(3003, 0),
ptsDts(1001, 3003),
ptsDts(6006, 6006),
];
const minPts = mp4Remuxer.getVideoStartPts(videoSamples);
expect(minPts).to.eq(1001);
});

it('should find the lowest normalized PTS in video samples with wrapping timestamps 1/2', function () {
const videoSamples = [
ptsDts(8589925344, 8589922341),
ptsDts(2765, 8589925344),
ptsDts(8589931350, 8589928347),
ptsDts(8589934353, 8589931350),
ptsDts(11774, 8589934353),
ptsDts(5768, 2765),
ptsDts(8771, 5768),
ptsDts(14777, 8771),
];
const minPts = mp4Remuxer.getVideoStartPts(videoSamples);
expect(minPts).to.eq(8589925344);
expect(minPts).to.lessThanOrEqual(8589925344);
});

it('should find the lowest normalized PTS in video samples with wrapping timestamps 2/2', function () {
const videoSamples = [
ptsDts(8589931350, 8589922341),
ptsDts(8589928347, 8589928347),
ptsDts(8589934353, 8589931350),
ptsDts(11774, 8589934353),
ptsDts(5768, 2765),
ptsDts(8771, 5768),
ptsDts(14777, 8771),
];
const minPts = mp4Remuxer.getVideoStartPts(videoSamples);
expect(minPts).to.eq(8589928347);
});
});

function ptsDts(pts: number, dts: number): VideoSample {
return {
dts,
pts,
key: true,
frame: true,
units: [
{
data: new Uint8Array(1),
type: 0,
},
],
length: 1,
};
}

0 comments on commit 0af1391

Please sign in to comment.