Skip to content

Commit b282f03

Browse files
committed
Roll initPTS forward when needed by earlier segments
Fixes video-dev#7536
1 parent 6f91d50 commit b282f03

File tree

2 files changed

+69
-10
lines changed

2 files changed

+69
-10
lines changed

src/remux/mp4-remuxer.ts

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import { ErrorDetails, ErrorTypes } from '../errors';
44
import { Events } from '../events';
55
import { PlaylistLevelType } from '../types/loader';
66
import { type ILogger, Logger } from '../utils/logger';
7-
import { toMsFromMpegTsClock } from '../utils/timescale-conversion';
7+
import {
8+
timestampToString,
9+
toMsFromMpegTsClock,
10+
} from '../utils/timescale-conversion';
811
import type { HlsConfig } from '../config';
912
import type { HlsEventEmitter } from '../events';
1013
import type { SourceBufferName } from '../types/buffer';
@@ -107,7 +110,19 @@ export default class MP4Remuxer extends Logger implements Remuxer {
107110
}
108111

109112
resetTimeStamp(defaultTimeStamp: TimestampOffset | null) {
110-
this.log('initPTS & initDTS reset');
113+
const initPTS = this._initPTS;
114+
if (
115+
!initPTS ||
116+
!defaultTimeStamp ||
117+
defaultTimeStamp.trackId !== initPTS.trackId ||
118+
defaultTimeStamp.baseTime !== initPTS.baseTime ||
119+
defaultTimeStamp.timescale !== initPTS.timescale
120+
) {
121+
this.log(
122+
`Reset initPTS: ${initPTS ? timestampToString(initPTS) : initPTS} > ${defaultTimeStamp ? timestampToString(defaultTimeStamp) : defaultTimeStamp}`,
123+
);
124+
}
125+
111126
this._initPTS = this._initDTS = defaultTimeStamp;
112127
}
113128

@@ -334,6 +349,25 @@ export default class MP4Remuxer extends Logger implements Remuxer {
334349
};
335350
}
336351

352+
computeInitPts(
353+
basetime: number,
354+
timescale: number,
355+
presentationTime: number,
356+
type: 'audio' | 'video',
357+
): number {
358+
const offset = Math.round(presentationTime * timescale);
359+
let timestamp = normalizePts(basetime, offset);
360+
if (timestamp < offset + timescale) {
361+
this.log(
362+
`Adjusting PTS for rollover in timeline near ${(offset - timestamp) / timescale} ${type}`,
363+
);
364+
while (timestamp < offset + timescale) {
365+
timestamp += 8589934592;
366+
}
367+
}
368+
return timestamp - offset;
369+
}
370+
337371
generateIS(
338372
audioTrack: DemuxedAudioTrack,
339373
videoTrack: DemuxedVideoTrack,
@@ -395,8 +429,12 @@ export default class MP4Remuxer extends Logger implements Remuxer {
395429
timescale = audioTrack.inputTimeScale;
396430
if (!_initPTS || timescale !== _initPTS.timescale) {
397431
// remember first PTS of this demuxing context. for audio, PTS = DTS
398-
initPTS = initDTS =
399-
audioSamples[0].pts - Math.round(timescale * timeOffset);
432+
initPTS = initDTS = this.computeInitPts(
433+
audioSamples[0].pts,
434+
timescale,
435+
timeOffset,
436+
'audio',
437+
);
400438
} else {
401439
computePTSDTS = false;
402440
}
@@ -421,13 +459,22 @@ export default class MP4Remuxer extends Logger implements Remuxer {
421459
trackId = videoTrack.id;
422460
timescale = videoTrack.inputTimeScale;
423461
if (!_initPTS || timescale !== _initPTS.timescale) {
424-
const startPTS = this.getVideoStartPts(videoSamples);
425-
const startOffset = Math.round(timescale * timeOffset);
426-
initDTS = Math.min(
427-
initDTS as number,
428-
normalizePts(videoSamples[0].dts, startPTS) - startOffset,
462+
const basePTS = this.getVideoStartPts(videoSamples);
463+
const baseDTS = normalizePts(videoSamples[0].dts, basePTS);
464+
const videoInitDTS = this.computeInitPts(
465+
baseDTS,
466+
timescale,
467+
timeOffset,
468+
'video',
469+
);
470+
const videoInitPTS = this.computeInitPts(
471+
basePTS,
472+
timescale,
473+
timeOffset,
474+
'video',
429475
);
430-
initPTS = Math.min(initPTS as number, startPTS - startOffset);
476+
initDTS = Math.min(initDTS as number, videoInitDTS);
477+
initPTS = Math.min(initPTS as number, videoInitPTS);
431478
} else {
432479
computePTSDTS = false;
433480
}
@@ -897,11 +944,18 @@ export default class MP4Remuxer extends Logger implements Remuxer {
897944
});
898945

899946
if (!contiguous || nextAudioTs < 0) {
947+
const sampleCount = inputSamples.length;
900948
// filter out sample with negative PTS that are not playable anyway
901949
// if we don't remove these negative samples, they will shift all audio samples forward.
902950
// leading to audio overlap between current / next fragment
903951
inputSamples = inputSamples.filter((sample) => sample.pts >= 0);
904952

953+
if (sampleCount !== inputSamples.length) {
954+
this.warn(
955+
`Removed ${inputSamples.length - sampleCount} of ${sampleCount} samples (initPTS ${initTime} / ${inputTimeScale})`,
956+
);
957+
}
958+
905959
// in case all samples have negative PTS, and have been filtered out, return now
906960
if (!inputSamples.length) {
907961
return;

src/utils/timescale-conversion.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,8 @@ export function toMpegTsClockFromTimescale(
3939
): number {
4040
return toTimescaleFromBase(baseTime, MPEG_TS_CLOCK_FREQ_HZ, 1 / srcScale);
4141
}
42+
43+
export function timestampToString(timestamp: TimestampOffset): string {
44+
const { baseTime, timescale, trackId } = timestamp;
45+
return `${baseTime / timescale} (${baseTime}/${timescale}) trackId: ${trackId}`;
46+
}

0 commit comments

Comments
 (0)