From 31e998a4f1dcccf5e4ce14d9591256a6f5e1484f Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Sat, 30 Sep 2023 13:48:05 -0700 Subject: [PATCH] Fix regression in M2TS probe introduced in #5503 Fixes #5857 --- src/controller/stream-controller.ts | 10 +++-- src/demux/audio/adts.ts | 8 ++-- src/demux/tsdemuxer.ts | 10 +++-- tests/unit/demuxer/adts.js | 60 +++++++++++++---------------- 4 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/controller/stream-controller.ts b/src/controller/stream-controller.ts index a95c4242c2b..175f431e859 100644 --- a/src/controller/stream-controller.ts +++ b/src/controller/stream-controller.ts @@ -576,9 +576,13 @@ export default class StreamController this.hls.trigger(Events.BUFFER_RESET, undefined); this.fragmentTracker.removeAllFragments(); this.couldBacktrack = false; - this.startPosition = this.lastCurrentTime = 0; - this.levels = this.fragPlaying = this.backtrackFragment = null; - this.altAudio = this.audioOnly = false; + this.startPosition = this.lastCurrentTime = this.fragLastKbps = 0; + this.levels = + this.fragPlaying = + this.backtrackFragment = + this.levelLastLoaded = + null; + this.altAudio = this.audioOnly = this.startFragRequested = false; } private onManifestParsed( diff --git a/src/demux/audio/adts.ts b/src/demux/audio/adts.ts index e76e8f67707..d5c1e5b3bec 100644 --- a/src/demux/audio/adts.ts +++ b/src/demux/audio/adts.ts @@ -26,7 +26,7 @@ type FrameHeader = { }; export function getAudioConfig( - observer, + observer: HlsEventEmitter, data: Uint8Array, offset: number, audioCodec: string, @@ -45,11 +45,13 @@ export function getAudioConfig( adtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1; const adtsSamplingIndex = (data[offset + 2] & 0x3c) >>> 2; if (adtsSamplingIndex > adtsSamplingRates.length - 1) { - observer.trigger(Events.ERROR, { + const error = new Error(`invalid ADTS sampling index:${adtsSamplingIndex}`); + observer.emit(Events.ERROR, Events.ERROR, { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_PARSING_ERROR, fatal: true, - reason: `invalid ADTS sampling index:${adtsSamplingIndex}`, + error, + reason: error.message, }); return; } diff --git a/src/demux/tsdemuxer.ts b/src/demux/tsdemuxer.ts index cd5e969ebf2..e45070f95ab 100644 --- a/src/demux/tsdemuxer.ts +++ b/src/demux/tsdemuxer.ts @@ -99,8 +99,7 @@ class TSDemuxer implements Demuxer { static syncOffset(data: Uint8Array): number { const length = data.length; - let scanwindow = - Math.min(PACKET_LENGTH * 5, data.length - PACKET_LENGTH) + 1; + let scanwindow = Math.min(PACKET_LENGTH * 5, length - PACKET_LENGTH) + 1; let i = 0; while (i < scanwindow) { // a TS init segment should contain at least 2 TS packets: PAT and PMT, each starting with 0x47 @@ -108,7 +107,10 @@ class TSDemuxer implements Demuxer { let packetStart = -1; let tsPackets = 0; for (let j = i; j < length; j += PACKET_LENGTH) { - if (data[j] === 0x47) { + if ( + data[j] === 0x47 && + (length - j === PACKET_LENGTH || data[j + PACKET_LENGTH] === 0x47) + ) { tsPackets++; if (packetStart === -1) { packetStart = j; @@ -134,7 +136,7 @@ class TSDemuxer implements Demuxer { return packetStart; } } else if (tsPackets) { - // Exit if sync word found, but does not contain contiguous packets (#5501) + // Exit if sync word found, but does not contain contiguous packets return -1; } else { break; diff --git a/tests/unit/demuxer/adts.js b/tests/unit/demuxer/adts.js index 18ba9765714..64506ce2ffd 100644 --- a/tests/unit/demuxer/adts.js +++ b/tests/unit/demuxer/adts.js @@ -1,3 +1,4 @@ +import EventEmitter from 'eventemitter3'; import { getAudioConfig, isHeaderPattern, @@ -14,24 +15,25 @@ import { ErrorTypes } from '../../../src/errors'; import sinon from 'sinon'; describe('getAudioConfig', function () { - it('should trigger a MEDIA_ERROR event if sample index is invalid', function () { - const observer = { - trigger: sinon.spy(), - }; + it('should emit a MEDIA_ERROR event if sample index is invalid', function () { + const observer = new EventEmitter(); + sinon.spy(observer, 'emit'); const data = new Uint8Array(new ArrayBuffer(4)); data[0] = 0xff; data[1] = 0xf0; // ID = 0 (MPEG-4), layer = 00, protection_absent = 0 data[2] = 0x34; // sampling_frequency_index = 14, which is a reserved value expect(getAudioConfig(observer, data, 0, 'mp4a.40.29')).to.not.exist; - expect(observer.trigger).to.have.been.calledOnce; - expect(observer.trigger.args[0][1].type).to.equal(ErrorTypes.MEDIA_ERROR); + expect(observer.emit).to.have.been.calledOnce; + expect(observer.emit.args[0][2].type).to.equal( + ErrorTypes.MEDIA_ERROR, + JSON.stringify(observer.emit.args, null, 2), + ); }); it('should return audio config for firefox if the specified sampling frequency > 24kHz', function () { - const observer = { - trigger: sinon.stub(navigator, 'userAgent').get(() => 'firefox'), - }; + const observer = new EventEmitter(); + sinon.stub(navigator, 'userAgent').get(() => 'firefox'); const data = new Uint8Array(new ArrayBuffer(4)); data[0] = 0xff; data[1] = 0xf0; // ID = 0 (MPEG-4), layer = 00, protection_absent = 0 @@ -47,9 +49,8 @@ describe('getAudioConfig', function () { }); it('should return audio config with a different extension sampling index for Firefox if sampling freq is low', function () { - const observer = { - trigger: sinon.stub(navigator, 'userAgent').get(() => 'Firefox'), - }; + const observer = new EventEmitter(); + sinon.stub(navigator, 'userAgent').get(() => 'Firefox'); const data = new Uint8Array(new ArrayBuffer(4)); data[0] = 0xff; data[1] = 0xf0; // ID = 0 (MPEG-4), layer = 00, protection_absent = 0 @@ -65,9 +66,8 @@ describe('getAudioConfig', function () { }); it('should return audio config for Android', function () { - const observer = { - trigger: sinon.stub(navigator, 'userAgent').get(() => 'Android'), - }; + const observer = new EventEmitter(); + sinon.stub(navigator, 'userAgent').get(() => 'Android'); const data = new Uint8Array(new ArrayBuffer(4)); data[0] = 0xff; data[1] = 0xf0; // ID = 0 (MPEG-4), layer = 00, protection_absent = 0 @@ -83,9 +83,8 @@ describe('getAudioConfig', function () { }); it('should return audio config for Chrome', function () { - const observer = { - trigger: sinon.stub(navigator, 'userAgent').get(() => 'Chrome'), - }; + const observer = new EventEmitter(); + sinon.stub(navigator, 'userAgent').get(() => 'Chrome'); const data = new Uint8Array(new ArrayBuffer(4)); data[0] = 0xff; data[1] = 0xf0; // ID = 0 (MPEG-4), layer = 00, protection_absent = 0 @@ -101,9 +100,8 @@ describe('getAudioConfig', function () { }); it('should return audio config for Chrome if there is no audio codec', function () { - const observer = { - trigger: sinon.stub(navigator, 'userAgent').get(() => 'Chrome'), - }; + const observer = new EventEmitter(); + sinon.stub(navigator, 'userAgent').get(() => 'Chrome'); const data = new Uint8Array(new ArrayBuffer(4)); data[0] = 0xff; data[1] = 0xf0; // ID = 0 (MPEG-4), layer = 00, protection_absent = 0 @@ -119,9 +117,8 @@ describe('getAudioConfig', function () { }); it('should return audio config for Chrome if there is no audio codec and freq is high enough', function () { - const observer = { - trigger: sinon.stub(navigator, 'userAgent').get(() => 'Chrome'), - }; + const observer = new EventEmitter(); + sinon.stub(navigator, 'userAgent').get(() => 'Chrome'); const data = new Uint8Array(new ArrayBuffer(4)); data[0] = 0xff; data[1] = 0xf0; // ID = 0 (MPEG-4), layer = 00, protection_absent = 0 @@ -137,9 +134,8 @@ describe('getAudioConfig', function () { }); it('should return audio config for Chrome if audio codec is "mp4a.40.5"', function () { - const observer = { - trigger: sinon.stub(navigator, 'userAgent').get(() => 'Chrome'), - }; + const observer = new EventEmitter(); + sinon.stub(navigator, 'userAgent').get(() => 'Chrome'); const data = new Uint8Array(new ArrayBuffer(4)); data[0] = 0xff; data[1] = 0xf0; // ID = 0 (MPEG-4), layer = 00, protection_absent = 0 @@ -155,9 +151,8 @@ describe('getAudioConfig', function () { }); it('should return audio config for Chrome if audio codec is "mp4a.40.2"', function () { - const observer = { - trigger: sinon.stub(navigator, 'userAgent').get(() => 'Chrome'), - }; + const observer = new EventEmitter(); + sinon.stub(navigator, 'userAgent').get(() => 'Chrome'); const data = new Uint8Array(new ArrayBuffer(4)); data[0] = 0xff; data[1] = 0xf0; // ID = 0 (MPEG-4), layer = 00, protection_absent = 0 @@ -174,9 +169,8 @@ describe('getAudioConfig', function () { }); it('should return audio config for Vivaldi', function () { - const observer = { - trigger: sinon.stub(navigator, 'userAgent').get(() => 'Vivaldi'), - }; + const observer = new EventEmitter(); + sinon.stub(navigator, 'userAgent').get(() => 'Vivaldi'); const data = new Uint8Array(new ArrayBuffer(4)); data[0] = 0xff; data[1] = 0xf0; // ID = 0 (MPEG-4), layer = 00, protection_absent = 0