Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix remuxing of AVC with bad PTS #3049

Merged
merged 2 commits into from Sep 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 7 additions & 18 deletions .travis.yml
Expand Up @@ -6,10 +6,6 @@ sudo: required
# don't connect to sauce labs unless running functional tests
before_install: if [ "${TRAVIS_MODE}" != "funcTests" ]; then unset SAUCE_USERNAME && unset SAUCE_ACCESS_KEY; fi
script: ./scripts/travis.sh
after_script: if [ "${TRAVIS_MODE}" = "funcTests" ]; then echo -n "travis_fold:start:sauce_logs\nSauce connect log:\n" && cat /home/travis/sauce-connect.log && echo -n "\ntravis_fold:end:sauce_logs\n"; fi
env:
global:
- SAUCE_USERNAME=mangui
stages:
- buildAndTest
- releaseAlpha
Expand Down Expand Up @@ -52,25 +48,18 @@ jobs:
env: TRAVIS_MODE=funcTests UA=chrome OS="Windows 10"
# Optional Func tests
- stage: testFuncOptional
env: TRAVIS_MODE=funcTests UA=firefox OS="Windows 10"
env: TRAVIS_MODE=funcTests UA=safari OS="OS X 10.15"
- stage: testFuncOptional
env: TRAVIS_MODE=funcTests UA=chrome OS="Windows 7"
env: TRAVIS_MODE=funcTests UA=firefox OS="Windows 10"
- stage: testFuncOptional
env: TRAVIS_MODE=funcTests UA=firefox OS="Windows 7"
env: TRAVIS_MODE=funcTests UA=chrome OS="OS X 10.11" UA_VERSION="79.0"
# - stage: testFuncOptional
# env: TRAVIS_MODE=funcTests UA=MicrosoftEdge OS="Windows 10"
- stage: testFuncOptional
env: TRAVIS_MODE=funcTests UA="internet explorer" OS="Windows 8.1" UA_VERSION="11.0"
- stage: testFuncOptional
env: TRAVIS_MODE=funcTests UA="internet explorer" OS="Windows 10"
- stage: testFuncOptional
env: TRAVIS_MODE=funcTests UA=chrome OS="OS X 10.11"
# - stage: testFuncOptional
# env: TRAVIS_MODE=funcTests UA=firefox OS="OS X 10.11"
env: TRAVIS_MODE=funcTests UA="internet explorer" OS="Windows 8.1" UA_VERSION="11.0"
- stage: testFuncOptional
env: TRAVIS_MODE=funcTests UA=chrome OS="Windows 7" UA_VERSION="69.0"
- stage: testFuncOptional
env: TRAVIS_MODE=funcTests UA=safari OS="OS X 10.12" UA_VERSION="10.1"
addons:
sauce_connect:
tunnel_domains: localhost
jwt:
secure: TxJT041jqRf4raCwtNJRb0rz2gGvEaADZjWO41UQND2+YIZ//S9qB2C4YyrL1BBsn8/ebdHr0cd18PwCzoBSEmoCdoAWXmqBaaLvM1DOeQkKJbU3+pFmWtv1qGqRXJLEAysNvzhG0sLdvBc0M7a/CWxqRfx1O3lGhLnTlAW33LlQndjJ8vh3SGQm8HxFR1503ujPd7V1jGwduVwaQp4zbAKTnQ4MLugmJf6UKiTc+YILMrVWOwipOIyYHh2GqbChd/v1PXff26XCNJXcaRZKJ8JosWyBpq5t4zlPO0qDfHpqbEuYK44xm4vzbZS94P/KF8BYzdtxQYLrxoS1UlnUYU7RmzqgL3y3AM7nzX/cXvJcoNXfUK2BpsB754XNyQfRmXOdRiHoC8+wwPqGkH/KCrmS4UIOqv4THfmDbrtewfcDTgKOzHxGcT1IsUq9BTxMNtxSwpHTHUXTXrzpS/UBDvrlc+9qPTqf+e6QL1aG+JT5sOg5REm2hMy0j18/Kr+HLXkehxEgJ6JrybyHUkkJrfcuWgVDu7Lv3cxlrtSMXi7TIwSB75NMoM8AE71GEVjXwpOw/0giwnmGsJNNi01ztod0UFe2V2rS+yPI1WNZIJ7Fw66U0oOKJ1rb4Iksl86n5Y2snHsxK8q5jhKaAaiWgGK5kVTAgF89t/GXYyNH6cI=
env: TRAVIS_MODE=funcTests UA=safari OS="OS X 10.12" UA_VERSION="10.1"
19 changes: 19 additions & 0 deletions package-lock.json

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

2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -37,6 +37,7 @@
"test:unit": "karma start karma.conf.js",
"test:unit:watch": "karma start karma.conf.js --auto-watch --no-single-run",
"test:func": "BABEL_ENV=development mocha --require @babel/register tests/functional/auto/setup.js --timeout 40000 --exit",
"test:func:sauce": "SAUCE=1 UA=MicrosoftEdge OS='Windows 10' BABEL_ENV=development mocha tests/functional/auto/setup.js --timeout 40000 --exit",
"type-check": "tsc --noEmit",
"type-check:watch": "npm run type-check -- --watch"
},
Expand Down Expand Up @@ -95,6 +96,7 @@
"karma-webpack": "^4.0.2",
"mocha": "^8.0.1",
"netlify-cli": "^2.36.0",
"sauce-connect-launcher": "^1.3.2",
"selenium-webdriver": "^3.1.0",
"semver": "^7.3.2",
"sinon": "^9.0.1",
Expand Down
2 changes: 1 addition & 1 deletion scripts/travis.sh
Expand Up @@ -23,7 +23,7 @@ if [ "${TRAVIS_MODE}" = "build" ]; then
elif [ "${TRAVIS_MODE}" = "unitTests" ]; then
npm run test:unit
elif [ "${TRAVIS_MODE}" = "funcTests" ]; then
npm run build
npm run build:ci
n=0
maxRetries=1
until [ $n -ge ${maxRetries} ]
Expand Down
8 changes: 6 additions & 2 deletions src/remux/mp4-remuxer.js
Expand Up @@ -279,12 +279,16 @@ class MP4Remuxer {
if (ptsDtsShift < 0) {
if (ptsDtsShift < averageSampleDuration * -2) {
// Fix for "CNN special report, with CC" in test-streams (including Safari browser)
logger.warn(`PTS < DTS detected in video samples, offsetting DTS to PTS ${toMsFromMpegTsClock(-averageSampleDuration, true)} ms`);
// With large PTS < DTS errors such as this, we want to correct CTS while maintaining increasing DTS values
logger.warn(`PTS < DTS detected in video samples, offsetting DTS from PTS by ${toMsFromMpegTsClock(-averageSampleDuration, true)} ms`);
let lastDts = ptsDtsShift;
for (let i = 0; i < nbSamples; i++) {
inputSamples[i].dts = inputSamples[i].pts - averageSampleDuration;
inputSamples[i].dts = lastDts = Math.max(lastDts, inputSamples[i].pts - averageSampleDuration);
inputSamples[i].pts = Math.max(lastDts, inputSamples[i].pts);
}
} else {
// Fix for "Custom IV with bad PTS DTS" in test-streams
// With smaller PTS < DTS errors we can simply move all DTS back. This increases CTS without causing buffer gaps or decode errors in Safari
logger.warn(`PTS < DTS detected in video samples, shifting DTS by ${toMsFromMpegTsClock(ptsDtsShift, true)} ms to overcome this issue`);
for (let i = 0; i < nbSamples; i++) {
inputSamples[i].dts = inputSamples[i].dts + ptsDtsShift;
Expand Down
72 changes: 62 additions & 10 deletions tests/functional/auto/setup.js
@@ -1,14 +1,15 @@
/* eslint-disable no-console */

const sauceConnectLauncher = require('sauce-connect-launcher');
const webdriver = require('selenium-webdriver');
const By = webdriver.By;
const until = webdriver.until;
// requiring this automatically adds the chromedriver binary to the PATH
// eslint-disable-next-line
const chromedriver = require('chromedriver');
require('chromedriver');
const HttpServer = require('http-server');
const streams = require('../../test-streams');
const onTravis = !!process.env.TRAVIS;
const useSauce = !!process.env.SAUCE || onTravis;
const chai = require('chai');
const expect = chai.expect;

Expand All @@ -25,7 +26,7 @@ let stream;
let printDebugLogs = false;

// Setup browser config data from env vars
if (onTravis) {
if (useSauce) {
let UA = process.env.UA;
if (!UA) {
throw new Error('No test browser name.');
Expand All @@ -47,15 +48,15 @@ if (onTravis) {

let browserDescription = browserConfig.name;

if (browserConfig.version) {
browserDescription += ` (${browserConfig.version})`;
if (browserConfig.version && browserConfig.version !== 'latest') {
browserDescription += ` ${browserConfig.version}`;
}

if (browserConfig.platform) {
browserDescription += `, ${browserConfig.platform}`;
}

let hostname = onTravis ? 'localhost' : '127.0.0.1';
let hostname = useSauce ? 'localhost' : '127.0.0.1';

// Launch static server
HttpServer.createServer({
Expand Down Expand Up @@ -278,6 +279,36 @@ async function testSeekBackToStart (url, config) {
expect(result, stringifyResult(result)).to.have.property('playing').which.is.true;
}

let sauceConnectProcess;
async function sauceConnect (tunnelIdentifier) {
return new Promise(function (resolve, reject) {
console.log(`Running sauce-connect-launcher. Tunnel id: ${tunnelIdentifier}`);
sauceConnectLauncher({
tunnelIdentifier
}, function (err, sauceConnectProcess) {
if (err) {
console.error(err.message);
reject(err);
return;
}
console.log('Sauce Connect ready');
resolve(sauceConnectProcess);
});
});
}

async function sauceDisconnect () {
return new Promise(function (resolve) {
if (!sauceConnectProcess) {
resolve();
}
sauceConnectProcess.close(function () {
console.log('Closed Sauce Connect process');
resolve();
});
});
}

describe(`testing hls.js playback in the browser on "${browserDescription}"`, function () {
before(async function () {
// high timeout because sometimes getSession() takes a while
Expand All @@ -286,8 +317,9 @@ describe(`testing hls.js playback in the browser on "${browserDescription}"`, fu
throw new Error('Stream not defined');
}

const labelBranch = process.env.TRAVIS_BRANCH || 'unknown';
let capabilities = {
name: `"${stream.description}" on "${browserDescription}"`,
name: `hls.js@${labelBranch} on "${browserDescription}"`,
browserName: browserConfig.name,
platform: browserConfig.platform,
version: browserConfig.version,
Expand All @@ -307,10 +339,16 @@ describe(`testing hls.js playback in the browser on "${browserDescription}"`, fu
if (onTravis) {
capabilities['tunnel-identifier'] = process.env.TRAVIS_JOB_NUMBER;
capabilities.build = 'HLSJS-' + process.env.TRAVIS_BUILD_NUMBER;
} else if (useSauce) {
capabilities['tunnel-identifier'] = `local-${Date.now()}`;
}
if (useSauce) {
sauceConnectProcess = await sauceConnect(capabilities['tunnel-identifier']);
capabilities.username = process.env.SAUCE_USERNAME;
capabilities.accessKey = process.env.SAUCE_ACCESS_KEY;
capabilities.avoidProxy = true;
browser = browser.usingServer(`http://${process.env.SAUCE_USERNAME}:${process.env.SAUCE_ACCESS_KEY}@ondemand.saucelabs.com:80/wd/hub`);
capabilities['record-screenshots'] = 'false';
browser = browser.usingServer(`https://${process.env.SAUCE_USERNAME}:${process.env.SAUCE_ACCESS_KEY}@ondemand.us-west-1.saucelabs.com:443/wd/hub`);
}

browser = browser.withCapabilities(capabilities).build();
Expand All @@ -325,13 +363,14 @@ describe(`testing hls.js playback in the browser on "${browserDescription}"`, fu
browser.getSession()
]);
console.log(`Retrieved session in ${Date.now() - start}ms`);
if (onTravis) {
if (useSauce) {
console.log(`Job URL: https://saucelabs.com/jobs/${session.getId()}`);
} else {
console.log(`WebDriver SessionID: ${session.getId()}`);
}
});
} catch (err) {
await sauceDisconnect();
throw new Error(`failed setting up session: ${err}`);
}
});
Expand Down Expand Up @@ -371,16 +410,29 @@ describe(`testing hls.js playback in the browser on "${browserDescription}"`, fu
});

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

after(async function () {
if (useSauce && this.currentTest && this.currentTest.parent) {
const tests = this.currentTest.parent.tests;
if (tests && tests.length && tests.every(test => test.isPassed())) {
browser.executeScript('sauce:job-result=passed');
}
}
console.log('Quitting browser...');
await browser.quit();
console.log('Browser quit.');
if (useSauce) {
await sauceDisconnect();
}
});

for (let name in streams) {
Expand Down