Skip to content

Commit

Permalink
Merge pull request #2930 from video-dev/bugfix/handle-wrapping-pts-ti…
Browse files Browse the repository at this point in the history
…meoffset-calculation

Fix PTS calculation on rollover.
  • Loading branch information
robwalch committed Aug 4, 2020
2 parents 29cbee7 + cf9a969 commit e9c9bf3
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 44 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 2 additions & 10 deletions src/demux/tsdemuxer.js
Expand Up @@ -487,22 +487,14 @@ class TSDemuxer {
(frag[11] & 0xFE) * 16384 +// 1 << 14
(frag[12] & 0xFF) * 128 +// 1 << 7
(frag[13] & 0xFE) / 2;
// check if greater than 2^32 -1
if (pesPts > 4294967295) {
// decrement 2^33
pesPts -= 8589934592;
}

if (pesFlags & 0x40) {
pesDts = (frag[14] & 0x0E) * 536870912 +// 1 << 29
(frag[15] & 0xFF) * 4194304 +// 1 << 22
(frag[16] & 0xFE) * 16384 +// 1 << 14
(frag[17] & 0xFF) * 128 +// 1 << 7
(frag[18] & 0xFE) / 2;
// check if greater than 2^32 -1
if (pesDts > 4294967295) {
// decrement 2^33
pesDts -= 8589934592;
}

if (pesPts - pesDts > 60 * 90000) {
logger.warn(`${Math.round((pesPts - pesDts) / 90000)}s delta between PTS and DTS, align them`);
pesPts = pesDts;
Expand Down
46 changes: 32 additions & 14 deletions src/remux/mp4-remuxer.js
Expand Up @@ -34,6 +34,25 @@ class MP4Remuxer {
this.ISGenerated = false;
}

getVideoStartPts (videoSamples) {
let rolloverDetected = false;
const startPTS = videoSamples.reduce((minPTS, sample) => {
const delta = sample.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 minPTS;
} else if (delta > 0) {
return minPTS;
} else {
return sample.pts;
}
}, videoSamples[0].pts);
if (rolloverDetected) {
logger.debug('PTS rollover detected');
}
return startPTS;
}

remux (audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) {
// generate Init Segment if needed
if (!this.ISGenerated) {
Expand All @@ -50,7 +69,7 @@ class MP4Remuxer {
// if first audio DTS is not aligned with first video DTS then we need to take that into account
// when providing timeOffset to remuxAudio / remuxVideo. if we don't do that, there might be a permanent / small
// drift between audio and video streams
const startPTS = videoTrack.samples.reduce((minPTS, sample) => Math.min(minPTS, sample.pts), videoTrack.samples[0].pts);
const startPTS = this.getVideoStartPts(videoTrack.samples);
const tsDelta = audioTrack.samples[0].pts - startPTS;
const audiovideoTimestampDelta = tsDelta / videoTrack.inputTimeScale;
audioTimeOffset += Math.max(0, audiovideoTimestampDelta);
Expand Down Expand Up @@ -163,7 +182,7 @@ class MP4Remuxer {
}
};
if (computePTSDTS) {
const startPTS = videoSamples.reduce((minPTS, sample) => Math.min(minPTS, sample.pts), videoSamples[0].pts);
const startPTS = this.getVideoStartPts(videoSamples);
const startOffset = Math.round(inputTimeScale * timeOffset);
initDTS = Math.min(initDTS, videoSamples[0].dts - startOffset);
initPTS = Math.min(initPTS, startPTS - startOffset);
Expand Down Expand Up @@ -213,7 +232,7 @@ class MP4Remuxer {

if (!contiguous) {
const pts = timeOffset * timeScale;
const cts = inputSamples[0].pts - inputSamples[0].dts;
const cts = inputSamples[0].pts - PTSNormalize(inputSamples[0].dts, inputSamples[0].pts);
// if not contiguous, let's use target timeOffset
nextAvcDts = pts - cts;
}
Expand Down Expand Up @@ -736,7 +755,7 @@ class MP4Remuxer {
this.remuxAudio(track, timeOffset, contiguous);
}

remuxID3 (track) {
remuxID3 (track, timeOffset) {
const length = track.samples.length;
if (!length) {
return;
Expand All @@ -749,8 +768,8 @@ class MP4Remuxer {
const sample = track.samples[index];
// setting id3 pts, dts to relative time
// using this._initPTS and this._initDTS to calculate relative time
sample.pts = ((sample.pts - initPTS) / inputTimeScale);
sample.dts = ((sample.dts - initDTS) / inputTimeScale);
sample.pts = PTSNormalize(sample.pts - initPTS, timeOffset * inputTimeScale) / inputTimeScale;
sample.dts = PTSNormalize(sample.dts - initDTS, timeOffset * inputTimeScale) / inputTimeScale;
}
this.observer.trigger(Event.FRAG_PARSING_METADATA, {
samples: track.samples
Expand All @@ -759,22 +778,21 @@ class MP4Remuxer {
track.samples = [];
}

remuxText (track) {
track.samples.sort(function (a, b) {
return (a.pts - b.pts);
});

let length = track.samples.length, sample;
remuxText (track, timeOffset) {
const length = track.samples.length;
const inputTimeScale = track.inputTimeScale;
const initPTS = this._initPTS;
// consume samples
if (length) {
for (let index = 0; index < length; index++) {
sample = track.samples[index];
const sample = track.samples[index];
// setting text pts, dts to relative time
// using this._initPTS and this._initDTS to calculate relative time
sample.pts = ((sample.pts - initPTS) / inputTimeScale);
sample.pts = PTSNormalize(sample.pts - initPTS, timeOffset * inputTimeScale) / inputTimeScale;
}
track.samples.sort(function (a, b) {
return (a.pts - b.pts);
});
this.observer.trigger(Event.FRAG_PARSING_USERDATA, {
samples: track.samples
});
Expand Down
34 changes: 21 additions & 13 deletions tests/functional/auto/setup.js
Expand Up @@ -22,6 +22,8 @@ const browserConfig = {
*/
let browser;
let stream;
let printDebugLogs = false;

// Setup browser config data from env vars
if (onTravis) {
let UA = process.env.UA;
Expand Down Expand Up @@ -123,18 +125,20 @@ async function testSmoothSwitch (url, config) {
const callback = arguments[arguments.length - 1];
window.startStream(url, config, callback);
const video = window.video;
window.hls.once(window.Hls.Events.FRAG_CHANGED, function (event, data) {
window.hls.once(window.Hls.Events.FRAG_CHANGED, function (eventName, data) {
console.log('[test] > ' + eventName + ' frag.level: ' + data.frag.level);
window.switchToHighestLevel('next');
});
window.hls.on(window.Hls.Events.LEVEL_SWITCHED, function (event, data) {
console.log('[test] > level switched: ' + data.level);
window.hls.on(window.Hls.Events.LEVEL_SWITCHED, function (eventName, data) {
console.log('[test] > ' + eventName + ' data.level: ' + data.level);
let currentTime = video.currentTime;
if (data.level === window.hls.levels.length - 1) {
console.log('[test] > switched on level: ' + data.level);
const highestLevel = (window.hls.levels.length - 1);
if (data.level === highestLevel) {
window.setTimeout(function () {
let newCurrentTime = video.currentTime;
console.log('[test] > currentTime delta : ' + (newCurrentTime - currentTime));
console.log('[test] > currentTime delta: ' + (newCurrentTime - currentTime));
callback({
highestLevel: highestLevel,
currentTimeDelta: newCurrentTime - currentTime,
logs: window.logString
});
Expand Down Expand Up @@ -335,15 +339,17 @@ describe(`testing hls.js playback in the browser on "${browserDescription}"`, fu
beforeEach(async function () {
try {
await retry(async () => {
console.log('Loading test page...');
if (printDebugLogs) {
console.log('Loading test page...');
}
try {
await browser.get(`http://${hostname}:8000/tests/functional/auto/index.html`);
} catch (e) {
throw new Error('failed to open test page');
}
console.log('Test page loaded.');

console.log('Locating ID \'hlsjs-functional-tests\'');
if (printDebugLogs) {
console.log('Test page loaded.');
}
try {
await browser.wait(
until.elementLocated(By.css('body#hlsjs-functional-tests')),
Expand All @@ -355,18 +361,20 @@ describe(`testing hls.js playback in the browser on "${browserDescription}"`, fu
console.log(source);
throw e;
}
console.log('Located the ID, page confirmed loaded');
if (printDebugLogs) {
console.log('Test harness found, page confirmed loaded');
}
});
} catch (e) {
throw new Error(`error getting test page loaded: ${e}`);
}
});

afterEach(async function () {
// if (onTravis || (!onTravis && this.currentTest.isFailed())) {
if (printDebugLogs || this.currentTest.isFailed()) {
const logString = await browser.executeScript('return logString');
console.log(`${onTravis ? 'travis_fold:start:debug_logs' : ''}\n${logString}\n${onTravis ? 'travis_fold:end:debug_logs' : ''}`);
// }
}
});

after(async function () {
Expand Down

0 comments on commit e9c9bf3

Please sign in to comment.