Skip to content

Commit

Permalink
stream-controller: only force reloading previous fragment if last rem…
Browse files Browse the repository at this point in the history
…uxed fragment does not start with a keyframe

related to https://github.com/dailymotion/hls.js/issues/517
  • Loading branch information
mangui committed Jun 30, 2016
1 parent 8ca82e7 commit a49ba2b
Show file tree
Hide file tree
Showing 6 changed files with 23 additions and 10 deletions.
15 changes: 10 additions & 5 deletions src/controller/stream-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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++;
Expand Down
2 changes: 1 addition & 1 deletion src/demux/demuxer-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
});
Expand Down
3 changes: 2 additions & 1 deletion src/demux/demuxer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
5 changes: 4 additions & 1 deletion src/demux/tsdemuxer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -355,6 +355,9 @@
samples.push(avcSample);
track.len += length;
track.nbNalu += units2.length;
} else {
// dropped samples, track it
track.dropped++;
}
units2 = [];
length = 0;
Expand Down
5 changes: 4 additions & 1 deletion src/remux/mp4-remuxer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -267,7 +269,8 @@ class MP4Remuxer {
startDTS: firstDTS / pesTimeScale,
endDTS: this.nextAvcDts / pesTimeScale,
type: 'video',
nb: samples.length
nb: samples.length,
dropped : dropped
});
}

Expand Down
3 changes: 2 additions & 1 deletion src/remux/passthrough-remuxer.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ class PassThroughRemuxer {
startPTS: timeOffset,
startDTS: timeOffset,
type: 'audiovideo',
nb: 1
nb: 1,
dropped : 0
});
}
}
Expand Down

1 comment on commit a49ba2b

@sompylasar
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yay thanks for this fix! Just hit this bug today, maxBufferHole helped but felt wrong.

Please sign in to comment.