From cebe2ff70f36ad7383c11732519c62cdd648d2ae Mon Sep 17 00:00:00 2001 From: Guillaume du Pontavice Date: Tue, 29 Jan 2019 21:58:14 -0800 Subject: [PATCH 01/11] tsdemuxer: if PES does not contain PTS/DTS, use last PES PTS/DTS instead --- src/demux/tsdemuxer.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/demux/tsdemuxer.js b/src/demux/tsdemuxer.js index 81ae6e7b9e9..9e396b8a4da 100644 --- a/src/demux/tsdemuxer.js +++ b/src/demux/tsdemuxer.js @@ -184,7 +184,7 @@ class TSDemuxer { switch (pid) { case avcId: if (stt) { - if (avcData && (pes = parsePES(avcData)) && pes.pts !== undefined) { + if (avcData && (pes = parsePES(avcData))) { parseAVCPES(pes, false); } @@ -197,7 +197,7 @@ class TSDemuxer { break; case audioId: if (stt) { - if (audioData && (pes = parsePES(audioData)) && pes.pts !== undefined) { + if (audioData && (pes = parsePES(audioData))) { if (audioTrack.isAAC) { parseAACPES(pes); } else { @@ -213,7 +213,7 @@ class TSDemuxer { break; case id3Id: if (stt) { - if (id3Data && (pes = parsePES(id3Data)) && pes.pts !== undefined) { + if (id3Data && (pes = parsePES(id3Data))) { parseID3PES(pes); } @@ -279,7 +279,7 @@ class TSDemuxer { } } // try to parse last PES packets - if (avcData && (pes = parsePES(avcData)) && pes.pts !== undefined) { + if (avcData && (pes = parsePES(avcData))) { parseAVCPES(pes, true); avcTrack.pesData = null; } else { @@ -287,7 +287,7 @@ class TSDemuxer { avcTrack.pesData = avcData; } - if (audioData && (pes = parsePES(audioData)) && pes.pts !== undefined) { + if (audioData && (pes = parsePES(audioData))) { if (audioTrack.isAAC) { parseAACPES(pes); } else { @@ -304,7 +304,7 @@ class TSDemuxer { audioTrack.pesData = audioData; } - if (id3Data && (pes = parsePES(id3Data)) && pes.pts !== undefined) { + if (id3Data && (pes = parsePES(id3Data))) { parseID3PES(pes); id3Track.pesData = null; } else { @@ -534,6 +534,18 @@ class TSDemuxer { if (avcSample.units.length && avcSample.frame) { const samples = avcTrack.samples; const nbSamples = samples.length; + // if sample does not have PTS/DTS, patch with last sample PTS/DTS + if (isNaN(avcSample.pts)) { + if (nbSamples) { + const lastSample = samples[nbSamples - 1]; + avcSample.pts = lastSample.pts; + avcSample.dts = lastSample.dts; + } else { + // dropping samples, no timestamp found + avcTrack.dropped++; + return; + } + } // only push AVC sample if starting with a keyframe is not mandatory OR // if keyframe already found in this fragment OR // keyframe found in last fragment (track.sps) AND From 354b871a609c2eb50f3cf7e265053565cc1ab25b Mon Sep 17 00:00:00 2001 From: alex-gusev Date: Sat, 17 Aug 2019 18:46:11 +0300 Subject: [PATCH 02/11] load audio playlist on MANIFEST_PARSED --- src/controller/audio-track-controller.js | 35 ++++++++++------- .../unit/controller/audio-track-controller.js | 38 ++++++++++++++++++- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/controller/audio-track-controller.js b/src/controller/audio-track-controller.js index 8d57008f165..e440a05325c 100644 --- a/src/controller/audio-track-controller.js +++ b/src/controller/audio-track-controller.js @@ -91,6 +91,8 @@ class AudioTrackController extends TaskLoop { onManifestParsed (data) { const tracks = this.tracks = data.audioTracks || []; this.hls.trigger(Event.AUDIO_TRACKS_UPDATED, { audioTracks: tracks }); + + this._selectAudioGroup(this.hls.nextLoadLevel); } /** @@ -150,20 +152,7 @@ class AudioTrackController extends TaskLoop { * @param {*} data */ onLevelLoaded (data) { - // FIXME: crashes because currentLevel is undefined - // const levelInfo = this.hls.levels[this.hls.currentLevel]; - - const levelInfo = this.hls.levels[data.level]; - - if (!levelInfo.audioGroupIds) { - return; - } - - const audioGroupId = levelInfo.audioGroupIds[levelInfo.urlId]; - if (this.audioGroupId !== audioGroupId) { - this.audioGroupId = audioGroupId; - this._selectInitialAudioTrack(); - } + this._selectAudioGroup(data.level); } /** @@ -252,6 +241,24 @@ class AudioTrackController extends TaskLoop { this._updateTrack(this._trackId); } + /** + * @param levelId + * @private + */ + _selectAudioGroup (levelId) { + const levelInfo = this.hls.levels[levelId]; + + if (!levelInfo || !levelInfo.audioGroupIds) { + return; + } + + const audioGroupId = levelInfo.audioGroupIds[levelInfo.urlId]; + if (this.audioGroupId !== audioGroupId) { + this.audioGroupId = audioGroupId; + this._selectInitialAudioTrack(); + } + } + /** * Select initial track * @private diff --git a/tests/unit/controller/audio-track-controller.js b/tests/unit/controller/audio-track-controller.js index 6ecae5db444..d54b23c05eb 100644 --- a/tests/unit/controller/audio-track-controller.js +++ b/tests/unit/controller/audio-track-controller.js @@ -3,7 +3,7 @@ import Hls from '../../../src/hls'; const sinon = require('sinon'); -describe('AudioTrackController', function () { +describe.only('AudioTrackController', function () { const tracks = [{ groupId: '1', id: 0, @@ -80,6 +80,42 @@ describe('AudioTrackController', function () { }); }); + it.only('should select audioGroupId and trigger AUDIO_TRACK_SWITCHING', function (done) { + hls.on(Hls.Events.AUDIO_TRACK_SWITCHING, (event, data) => { + done(); + }); + + const levels = [ + { + urlId: 1, + audioGroupIds: ['1', '2'] + } + ]; + + hls.levelController = { + levels + }; + + const newLevelInfo = levels[0]; + const newGroupId = newLevelInfo.audioGroupIds[newLevelInfo.urlId]; + + audioTrackController.audioGroupId = '1'; + audioTrackController.tracks = tracks; + audioTrackController.audioTrack = 2; + + // current track name + const audioTrackName = tracks[audioTrackController.audioTrack].name; + + audioTrackController.onManifestParsed({ + audioTracks: tracks + }); + + // group has switched + expect(audioTrackController.audioGroupId).to.equal(newGroupId); + // name is still the same + expect(tracks[audioTrackController.audioTrack].name).to.equal(audioTrackName); + }); + describe('_needsTrackLoading', function () { it('should not need loading because the audioTrack is embedded in the main playlist', function () { expect(audioTrackController._needsTrackLoading({ details: { live: true } })).to.be.false; From 688944aa6bee1b9e2bd8dad14e6c9f627ebead29 Mon Sep 17 00:00:00 2001 From: alex-gusev Date: Sun, 18 Aug 2019 20:22:10 +0300 Subject: [PATCH 03/11] remove .only --- tests/unit/controller/audio-track-controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/controller/audio-track-controller.js b/tests/unit/controller/audio-track-controller.js index d54b23c05eb..8956d220f1c 100644 --- a/tests/unit/controller/audio-track-controller.js +++ b/tests/unit/controller/audio-track-controller.js @@ -3,7 +3,7 @@ import Hls from '../../../src/hls'; const sinon = require('sinon'); -describe.only('AudioTrackController', function () { +describe('AudioTrackController', function () { const tracks = [{ groupId: '1', id: 0, @@ -80,7 +80,7 @@ describe.only('AudioTrackController', function () { }); }); - it.only('should select audioGroupId and trigger AUDIO_TRACK_SWITCHING', function (done) { + it('should select audioGroupId and trigger AUDIO_TRACK_SWITCHING', function (done) { hls.on(Hls.Events.AUDIO_TRACK_SWITCHING, (event, data) => { done(); }); From 61ed761ff4ae750994ffa6a3bd264ccf49308809 Mon Sep 17 00:00:00 2001 From: alex-gusev Date: Sun, 6 Oct 2019 17:38:10 +0300 Subject: [PATCH 04/11] trigger ci From 3fd87fec09671e45affea864db0551d6f45fd2a6 Mon Sep 17 00:00:00 2001 From: jony89 Date: Sat, 18 Jan 2020 12:05:38 +0200 Subject: [PATCH 05/11] fix: computeLivePosition minimum value of media.currentTime --- src/controller/base-stream-controller.js | 3 ++- src/controller/stream-controller.js | 4 ++-- tests/unit/controller/stream-controller.js | 26 ++++++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/controller/base-stream-controller.js b/src/controller/base-stream-controller.js index ae3348d7cd1..ab3b066da18 100644 --- a/src/controller/base-stream-controller.js +++ b/src/controller/base-stream-controller.js @@ -128,6 +128,7 @@ export default class BaseStreamController extends TaskLoop { computeLivePosition (sliding, levelDetails) { let targetLatency = this.config.liveSyncDuration !== undefined ? this.config.liveSyncDuration : this.config.liveSyncDurationCount * levelDetails.targetduration; - return sliding + Math.max(0, levelDetails.totalduration - targetLatency); + const currentTime = this.media ? this.media.currentTime : null; + return Math.max(sliding + Math.max(0, levelDetails.totalduration - targetLatency), currentTime); } } diff --git a/src/controller/stream-controller.js b/src/controller/stream-controller.js index 701acc58b2f..d0869e372d0 100644 --- a/src/controller/stream-controller.js +++ b/src/controller/stream-controller.js @@ -280,9 +280,9 @@ class StreamController extends BaseStreamController { if (bufferEnd < Math.max(start - config.maxFragLookUpTolerance, end - maxLatency)) { let liveSyncPosition = this.liveSyncPosition = this.computeLivePosition(start, levelDetails); - logger.log(`buffer end: ${bufferEnd.toFixed(3)} is located too far from the end of live sliding playlist, reset currentTime to : ${liveSyncPosition.toFixed(3)}`); bufferEnd = liveSyncPosition; - if (media && !media.paused && media.readyState && media.duration > liveSyncPosition) { + if (media && !media.paused && media.readyState && media.duration > liveSyncPosition && liveSyncPosition > media.currentTime) { + logger.log(`buffer end: ${bufferEnd.toFixed(3)} is located too far from the end of live sliding playlist, reset currentTime to : ${liveSyncPosition.toFixed(3)}`); media.currentTime = liveSyncPosition; } diff --git a/tests/unit/controller/stream-controller.js b/tests/unit/controller/stream-controller.js index 3df11e27ca1..428b25a4bef 100644 --- a/tests/unit/controller/stream-controller.js +++ b/tests/unit/controller/stream-controller.js @@ -305,4 +305,30 @@ describe('StreamController', function () { }); }); }); + + describe('computeLivePosition', function () { + it('should return the current live sync position according to liveSyncDuration', function () { + streamController.config.liveSyncDuration = 2; + const levelDetails = { totalduration: 20 }; + const liveSyncPosition = streamController.computeLivePosition(8, levelDetails); + expect(liveSyncPosition).to.equal(26); + }); + + it('should return the current live sync position according to liveSyncDurationCount', function () { + streamController.config.liveSyncDurationCount = 2; + const levelDetails = { totalduration: 20, targetduration: 0.5 }; + const liveSyncPosition = streamController.computeLivePosition(8, levelDetails); + expect(liveSyncPosition).to.equal(27); + }); + + it('should not return value that is less than the video current time', function () { + streamController.media = { + currentTime: 25 + }; + streamController.config.liveSyncDuration = 1; + const levelDetails = { totalduration: 14 }; + const liveSyncPosition = streamController.computeLivePosition(8, levelDetails); + expect(liveSyncPosition).to.equal(25); + }); + }); }); From 9ee18eaa13140f3290f863790675e0c05784ae0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Go=C3=9Fe?= Date: Tue, 25 Feb 2020 09:33:25 +0100 Subject: [PATCH 06/11] Fix formatting of example --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 80f2ef378db..4fde9fe41d2 100644 --- a/README.md +++ b/README.md @@ -63,22 +63,22 @@ Find the commit on [https://github.com/video-dev/hls.js/blob/deployments/README.