diff --git a/src/controller/stream-controller.js b/src/controller/stream-controller.js index d07e740302d..2cd0173765a 100644 --- a/src/controller/stream-controller.js +++ b/src/controller/stream-controller.js @@ -282,16 +282,16 @@ class StreamController extends EventHandler { } if(frag) { start = frag.start; - //logger.log('find SN matching with pos:' + bufferEnd + ':' + frag.sn); + logger.log('find SN matching with pos:' + bufferEnd + ':' + frag.sn); if (fragPrevious && frag.level === fragPrevious.level && frag.sn === fragPrevious.sn) { if (frag.sn < levelDetails.endSN) { let deltaPTS = fragPrevious.deltaPTS, curSNIdx = frag.sn - levelDetails.startSN; // if there is a significant delta between audio and video, larger than max allowed hole, - // it might be because video fragment does not start with a keyframe. + // and if previous remuxed fragment did not start with a keyframe. (fragPrevious.dropped) // let's try to load previous fragment again to get last keyframe // then we will reload again current fragment (that way we should be able to fill the buffer hole ...) - if (deltaPTS && deltaPTS > config.maxBufferHole) { + if (deltaPTS && deltaPTS > config.maxBufferHole && fragPrevious.dropped) { frag = fragments[curSNIdx-1]; logger.warn(`SN just loaded, with large PTS gap between audio and video, maybe frag is not starting with a keyframe ? load previous one to try to overcome this`); // decrement previous frag load counter to avoid frag loop loading error when next fragment will get reloaded @@ -796,7 +796,7 @@ class StreamController extends EventHandler { } } this.pendingAppending = 0; - logger.log(`Demuxing ${sn} of [${details.startSN} ,${details.endSN}],level ${level}`); + logger.log(`Demuxing ${sn} of [${details.startSN} ,${details.endSN}],level ${level}, cc ${fragCurrent.cc}`); let demuxer = this.demuxer; if (demuxer) { demuxer.push(data.payload, audioCodec, currentLevel.videoCodec, start, fragCurrent.cc, level, sn, duration, fragCurrent.decryptdata); @@ -892,12 +892,17 @@ class StreamController extends EventHandler { var level = this.levels[this.level], frag = this.fragCurrent; - logger.log(`parsed ${data.type},PTS:[${data.startPTS.toFixed(3)},${data.endPTS.toFixed(3)}],DTS:[${data.startDTS.toFixed(3)}/${data.endDTS.toFixed(3)}],nb:${data.nb}`); + logger.log(`parsed ${data.type},PTS:[${data.startPTS.toFixed(3)},${data.endPTS.toFixed(3)}],DTS:[${data.startDTS.toFixed(3)}/${data.endDTS.toFixed(3)}],nb:${data.nb},dropped:${data.dropped || 0}`); var drift = LevelHelper.updateFragPTSDTS(level.details,frag.sn,data.startPTS,data.endPTS,data.startDTS,data.endDTS), hls = this.hls; hls.trigger(Event.LEVEL_PTS_UPDATED, {details: level.details, level: this.level, drift: drift}); + // has remuxer dropped video frames located before first keyframe ? + if(data.type === 'video') { + frag.dropped = data.dropped; + } + [data.data1, data.data2].forEach(buffer => { if (buffer) { this.pendingAppending++; diff --git a/src/demux/demuxer-worker.js b/src/demux/demuxer-worker.js index 3d5f5c1aeb0..14825614de8 100644 --- a/src/demux/demuxer-worker.js +++ b/src/demux/demuxer-worker.js @@ -38,7 +38,7 @@ var DemuxerWorker = function (self) { }); observer.on(Event.FRAG_PARSING_DATA, function(ev, data) { - var objData = {event: ev, type: data.type, startPTS: data.startPTS, endPTS: data.endPTS, startDTS: data.startDTS, endDTS: data.endDTS, data1: data.data1.buffer, data2: data.data2.buffer, nb: data.nb}; + var objData = {event: ev, type: data.type, startPTS: data.startPTS, endPTS: data.endPTS, startDTS: data.startDTS, endDTS: data.endDTS, data1: data.data1.buffer, data2: data.data2.buffer, nb: data.nb, dropped : data.dropped}; // pass data1/data2 as transferable object (no copy) self.postMessage(objData, [objData.data1, objData.data2]); }); diff --git a/src/demux/demuxer.js b/src/demux/demuxer.js index 052f0c34d1c..6b075ccb495 100644 --- a/src/demux/demuxer.js +++ b/src/demux/demuxer.js @@ -88,7 +88,8 @@ class Demuxer { startDTS: data.startDTS, endDTS: data.endDTS, type: data.type, - nb: data.nb + nb: data.nb, + dropped: data.dropped, }); break; case Event.FRAG_PARSING_METADATA: diff --git a/src/demux/tsdemuxer.js b/src/demux/tsdemuxer.js index 5ed3c97518f..7a1e1534f5e 100644 --- a/src/demux/tsdemuxer.js +++ b/src/demux/tsdemuxer.js @@ -37,7 +37,7 @@ switchLevel() { this.pmtParsed = false; this._pmtId = -1; - this._avcTrack = {container : 'video/mp2t', type: 'video', id :-1, sequenceNumber: 0, samples : [], len : 0, nbNalu : 0}; + this._avcTrack = {container : 'video/mp2t', type: 'video', id :-1, sequenceNumber: 0, samples : [], len : 0, nbNalu : 0, dropped : 0}; this._aacTrack = {container : 'video/mp2t', type: 'audio', id :-1, sequenceNumber: 0, samples : [], len : 0}; this._id3Track = {type: 'id3', id :-1, sequenceNumber: 0, samples : [], len : 0}; this._txtTrack = {type: 'text', id: -1, sequenceNumber: 0, samples: [], len: 0}; @@ -355,6 +355,9 @@ samples.push(avcSample); track.len += length; track.nbNalu += units2.length; + } else { + // dropped samples, track it + track.dropped++; } units2 = []; length = 0; diff --git a/src/remux/mp4-remuxer.js b/src/remux/mp4-remuxer.js index 5e0de73afb8..1074a5a062e 100644 --- a/src/remux/mp4-remuxer.js +++ b/src/remux/mp4-remuxer.js @@ -247,8 +247,10 @@ class MP4Remuxer { } // next AVC sample DTS should be equal to last sample DTS + last sample duration this.nextAvcDts = dtsnorm + lastSampleDuration * pes2mp4ScaleFactor; + let dropped = track.dropped; track.len = 0; track.nbNalu = 0; + track.dropped = 0; if(samples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) { flags = samples[0].flags; // chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue @@ -267,7 +269,8 @@ class MP4Remuxer { startDTS: firstDTS / pesTimeScale, endDTS: this.nextAvcDts / pesTimeScale, type: 'video', - nb: samples.length + nb: samples.length, + dropped : dropped }); } diff --git a/src/remux/passthrough-remuxer.js b/src/remux/passthrough-remuxer.js index db454eb495f..97cb2cb38fe 100644 --- a/src/remux/passthrough-remuxer.js +++ b/src/remux/passthrough-remuxer.js @@ -62,7 +62,8 @@ class PassThroughRemuxer { startPTS: timeOffset, startDTS: timeOffset, type: 'audiovideo', - nb: 1 + nb: 1, + dropped : 0 }); } }