diff --git a/src/controller/audio-stream-controller.js b/src/controller/audio-stream-controller.js index bb45ca56a23..01c7400c96e 100644 --- a/src/controller/audio-stream-controller.js +++ b/src/controller/audio-stream-controller.js @@ -158,7 +158,7 @@ class AudioStreamController extends BaseStreamController { const audioSwitch = this.audioSwitch; const trackId = this.trackId; - // if buffer length is less than maxBufLen try to load a new fragment + // if buffer length is less than maxBufLen try to load a new fragment if ((bufferLen < maxBufLen || audioSwitch) && trackId < tracks.length) { trackDetails = tracks[trackId].details; // if track info not retrieved yet, switch state and wait for track retrieval @@ -262,7 +262,7 @@ class AudioStreamController extends BaseStreamController { // we force a frag loading in audio switch as fragment tracker might not have evicted previous frags in case of quick audio switch this.fragCurrent = frag; if (audioSwitch || this.fragmentTracker.getState(frag) === FragmentState.NOT_LOADED) { - logger.log(`Loading ${frag.sn}, cc: ${frag.cc} of [${trackDetails.startSN} ,${trackDetails.endSN}],track ${trackId}, currentTime:${pos},bufferEnd:${bufferEnd.toFixed(3)}`); + logger.log(`Loading ${frag.sn}, cc: ${frag.cc} of [${trackDetails.startSN} ,${trackDetails.endSN}],track ${trackId}, start:${frag.start.toFixed(2)}, currentTime:${pos},bufferEnd:${bufferEnd.toFixed(3)}`); if (frag.sn !== 'initSegment') { this.startFragRequested = true; @@ -352,7 +352,7 @@ class AudioStreamController extends BaseStreamController { media.addEventListener('seeking', this.onvseeking); media.addEventListener('ended', this.onvended); let config = this.config; - if (this.tracks && config.autoStartLoad) { + if (this.tracks && config.autoStartLoad && this.state === State.STOPPED) { this.startLoad(config.startPosition); } } @@ -388,7 +388,6 @@ class AudioStreamController extends BaseStreamController { this.fragCurrent = null; this.clearWaitingFragment(); - this.state = State.PAUSED; // destroy useless demuxer when switching audio to main if (!altAudio) { if (this.demuxer) { @@ -400,11 +399,18 @@ class AudioStreamController extends BaseStreamController { this.setInterval(TICK_INTERVAL); } + if (this.state === State.STOPPED) { + this.audioSwitch = !!altAudio; + return; + } + // should we switch tracks ? if (altAudio) { this.audioSwitch = true; // main audio track are handled by stream-controller, just do something if switching to alt audio track this.state = State.IDLE; + } else { + this.state = State.PAUSED; } this.tick(); } @@ -460,7 +466,7 @@ class AudioStreamController extends BaseStreamController { } this.nextLoadPosition = this.startPosition; } - // only switch batck to IDLE state if we were waiting for track to start downloading a new fragment + // only switch back to IDLE state if we were waiting for track to start downloading a new fragment if (this.state === State.WAITING_TRACK) { this.state = State.IDLE; } diff --git a/src/controller/buffer-controller.ts b/src/controller/buffer-controller.ts index 9a7d4e258d9..eb3cf90138d 100644 --- a/src/controller/buffer-controller.ts +++ b/src/controller/buffer-controller.ts @@ -259,6 +259,7 @@ class BufferController extends EventHandler { } private _onSBUpdateEnd = () => { + logger.log('sb update ended'); // update timestampOffset if (this.audioTimestampOffset && this.sourceBuffer.audio) { let audioBuffer = this.sourceBuffer.audio; @@ -268,8 +269,10 @@ class BufferController extends EventHandler { delete this.audioTimestampOffset; } - if (this._needsFlush) { + if (this._needsFlush && this.flushBufferCounter === 0) { this.doFlush(); + } else { + this._needsFlush = false; } if (this._needsEos) { @@ -636,7 +639,7 @@ class BufferController extends EventHandler { // reset sourceBuffer ended flag before appending segment sb.ended = false; - // logger.log(`appending ${segment.content} ${type} SB, size:${segment.data.length}, ${segment.parent}`); + logger.log(`appending ${segment.content} ${segment.type} SB, size:${segment.data.byteLength}, ${segment.parent}`); this.parent = segment.parent; sb.appendBuffer(segment.data); this.appendError = 0; @@ -684,7 +687,6 @@ class BufferController extends EventHandler { if (this.media) { currentTime = this.media.currentTime.toFixed(3); } - logger.log(`flushBuffer,pos/start/end: ${currentTime}/${startOffset}/${endOffset}`); // safeguard to avoid infinite looping : don't try to flush more than the nb of appended segments if (this.flushBufferCounter >= this.appended) { @@ -695,6 +697,7 @@ class BufferController extends EventHandler { const sb = sourceBuffer[sbType]; // we are going to flush buffer, mark source buffer as 'not ended' if (sb) { + logger.log(`flushBuffer,flush-counter/appending/time/start/end: ${this.flushBufferCounter}/${this.appending}/${currentTime}/${startOffset}/${endOffset}`); sb.ended = false; if (!sb.updating) { if (this.removeBufferRange(sbType, sb, startOffset, endOffset)) { @@ -705,9 +708,9 @@ class BufferController extends EventHandler { logger.warn('cannot flush, sb updating in progress'); return false; } + logger.log('buffer flushed'); } - logger.log('buffer flushed'); // everything flushed ! return true; } diff --git a/src/controller/stream-controller.js b/src/controller/stream-controller.js index d0e374eac9a..6102dbe9c51 100644 --- a/src/controller/stream-controller.js +++ b/src/controller/stream-controller.js @@ -447,7 +447,7 @@ class StreamController extends BaseStreamController { frag.autoLevel = this.hls.autoLevelEnabled; frag.bitrateTest = this.bitrateTest; - logger.log(`Loading ${frag.sn} of [${levelDetails.startSN} ,${levelDetails.endSN}],level ${this.level}, currentTime:${pos.toFixed(3)},bufferEnd:${bufferEnd.toFixed(3)}`); + logger.log(`Loading ${frag.sn} of [${levelDetails.startSN} ,${levelDetails.endSN}],level ${this.level}, start:${frag.start.toFixed(2)}, currentTime:${pos.toFixed(3)},bufferEnd:${bufferEnd.toFixed(3)}`); this.hls.trigger(Event.FRAG_LOADING, { frag }); // lazy demuxer init, as this could take some time ... do it during frag loading @@ -681,7 +681,7 @@ class StreamController extends BaseStreamController { media.addEventListener('seeked', this.onvseeked); media.addEventListener('ended', this.onvended); let config = this.config; - if (this.levels && config.autoStartLoad) { + if (this.levels && config.autoStartLoad && this.state === State.STOPPED) { this.hls.startLoad(config.startPosition); } @@ -765,7 +765,7 @@ class StreamController extends BaseStreamController { this.levels = data.levels; this.startFragRequested = false; let config = this.config; - if (config.autoStartLoad || this.forceStartLoad) { + if (config.autoStartLoad && this.state === State.STOPPED || this.forceStartLoad) { this.hls.startLoad(config.startPosition); } } @@ -1105,8 +1105,10 @@ class StreamController extends BaseStreamController { this.demuxer.destroy(); this.demuxer = null; } - // switch to IDLE state to load new fragment - this.state = State.IDLE; + if (this.state !== State.STOPPED) { + // switch to IDLE state to load new fragment + this.state = State.IDLE; + } } let hls = this.hls; // switching to main audio, flush all audio and trigger track switched diff --git a/src/demux/mp3demuxer.js b/src/demux/mp3demuxer.js index 3b595fb07d0..932b3ff2b5e 100644 --- a/src/demux/mp3demuxer.js +++ b/src/demux/mp3demuxer.js @@ -41,7 +41,7 @@ class MP3Demuxer { append (data, timeOffset, contiguous, accurateTimeOffset) { let id3Data = ID3.getID3Data(data, 0); let timestamp = ID3.getTimeStamp(id3Data); - let pts = timestamp !== undefined ? 90 * timestamp : timeOffset * 90000; + let pts = Number.isFinite(timestamp) ? timestamp * 90 : timeOffset * 90000; let offset = id3Data.length; let length = data.length; let frameIndex = 0, stamp = 0; diff --git a/src/remux/mp4-remuxer.js b/src/remux/mp4-remuxer.js index fb820406af6..ba26fce0e6a 100644 --- a/src/remux/mp4-remuxer.js +++ b/src/remux/mp4-remuxer.js @@ -668,13 +668,15 @@ class MP4Remuxer { mp4Sample.duration = lastSampleDuration; } if (nbSamples) { + logger.log(`Audio remuxer nextAudioPts in: ${this.nextAudioPts} inputTimeScale: ${track.inputTimeScale} timescale: ${track.timescale}`); // next audio sample PTS should be equal to last sample PTS + duration this.nextAudioPts = nextAudioPts = lastPTS + scaleFactor * lastSampleDuration; - // logger.log('Audio/PTS/PTSend:' + audioSample.pts.toFixed(0) + '/' + this.nextAacDts.toFixed(0)); + logger.log(`Audio remuxer nextAudioPts out: ${nextAudioPts}`); track.samples = outputSamples; if (rawMPEG) { moof = new Uint8Array(); } else { + logger.log(`Audio moof ${track.sequenceNumber} @${(toMsFromMpegTsClock(firstPTS / scaleFactor, true) / 1000).toFixed(3)}s (${firstPTS} / ${scaleFactor})`); moof = MP4.moof(track.sequenceNumber++, firstPTS / scaleFactor, track); } diff --git a/tests/functional/auto/setup.js b/tests/functional/auto/setup.js index 04f9b833db7..3f851d8acf3 100644 --- a/tests/functional/auto/setup.js +++ b/tests/functional/auto/setup.js @@ -377,8 +377,8 @@ describe(`testing hls.js playback in the browser on "${browserDescription}"`, fu for (let name in streams) { stream = streams[name]; - let url = stream.url; - let config = stream.config || {}; + const url = stream.url; + const config = stream.config || { enableWorker: false }; if ( !stream.blacklist_ua || stream.blacklist_ua.indexOf(browserConfig.name) === -1