Skip to content

Commit

Permalink
fixed fragment tracker alternative audio type checking
Browse files Browse the repository at this point in the history
  • Loading branch information
davidkim9 committed Dec 13, 2017
1 parent 3c63815 commit 54717c7
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 74 deletions.
2 changes: 1 addition & 1 deletion src/controller/audio-stream-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -348,9 +348,9 @@ class AudioStreamController extends EventHandler {
if (!isNaN(frag.sn)) {
this.nextLoadPosition = frag.start + frag.duration;
}
frag.audioOnly = true;
hls.trigger(Event.FRAG_LOADING, {frag: frag});
this.state = State.FRAG_LOADING;
} else {
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/controller/stream-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ class StreamController extends EventHandler {
} else {
logger.log(`Loading ${frag.sn} of [${levelDetails.startSN} ,${levelDetails.endSN}],level ${level}, currentTime:${pos.toFixed(3)},bufferEnd:${bufferEnd.toFixed(3)}`);
// Check if fragment is not loaded
let ftState = this.fragmentTracker.getState(frag);
let fragState = this.fragmentTracker.getState(frag);

this.fragCurrent = frag;
this.startFragRequested = true;
Expand All @@ -508,9 +508,10 @@ class StreamController extends EventHandler {
}

// Allow backtracked fragments to load
if(frag.backtracked || ftState === FragmentState.NOT_LOADED) {
if(frag.backtracked || fragState === FragmentState.NOT_LOADED) {
frag.autoLevel = this.hls.autoLevelEnabled;
frag.bitrateTest = this.bitrateTest;
frag.audioOnly = false;

this.hls.trigger(Event.FRAG_LOADING, {frag: frag});
// lazy demuxer init, as this could take some time ... do it during frag loading
Expand Down
141 changes: 75 additions & 66 deletions src/helper/fragment-tracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,19 @@ export class FragmentTracker extends EventHandler {
* @param {Object} timeRange TimeRange object from a sourceBuffer
*/
detectEvictedFragments(type, timeRange) {
let fragmentObject, fragmentTimes, time;
let fragmentEntity, fragmentTimes, time;
// Check if any flagged fragments have been unloaded
for (let fragKey in this.fragments) {
if (this.fragments.hasOwnProperty(fragKey)) {
fragmentObject = this.fragments[fragKey];
if(fragmentObject.state === FragmentState.PARTIAL || fragmentObject.state === FragmentState.OK) {
fragmentTimes = fragmentObject.range[type].time;
fragmentEntity = this.fragments[fragKey];
if(fragmentEntity.buffered === true) {
fragmentTimes = fragmentEntity.range[type].time;
for (let i = 0; i < fragmentTimes.length; i++) {
time = fragmentTimes[i];

if(this.isTimeBuffered(time.startPTS, time.endPTS, timeRange) === false) {
// Unregister partial fragment as it needs to load again to be reused
this.removeFragment(fragmentObject.body);
this.removeFragment(fragmentEntity.body);
break;
}
}
Expand All @@ -60,21 +60,28 @@ export class FragmentTracker extends EventHandler {
}
}

isTimeBuffered(startPTS, endPTS, timeRange) {
let startTime, endTime;
for (let i = 0; i < timeRange.length; i++) {
startTime = timeRange.start(i) - this.bufferPadding;
endTime = timeRange.end(i) + this.bufferPadding;
if (startPTS >= startTime && endPTS <= endTime) {
return true;
}
if(endPTS <= startTime) {
// No need to check the rest of the timeRange as it is in order
return false;
/**
* Checks if the fragment passed in is loaded in the buffer properly
* Partially loaded fragments will be registered as a partial fragment
* @param {Object} fragment Check the fragment against all sourceBuffers loaded
*/
detectPartialFragments(fragment) {
let fragmentBuffered;
let fragKey = this.getFragmentKey(fragment);
let fragmentEntity = this.fragments[fragKey];
fragmentEntity.buffered = true;
let timeRange;
for(let type in this.timeRanges) {
if (this.timeRanges.hasOwnProperty(type)) {
if((fragment.audioOnly === true && type === 'audio') || fragment.audioOnly === false) {
timeRange = this.timeRanges[type];
// Check for malformed fragments
fragmentBuffered = [];
// Gaps need to be calculated for each type
fragmentEntity.range[type] = this.getBufferedTimes(fragment.startPTS, fragment.endPTS, timeRange);
}
}
}

return false;
}

getBufferedTimes(startPTS, endPTS, timeRange) {
Expand Down Expand Up @@ -112,36 +119,6 @@ export class FragmentTracker extends EventHandler {
};
}

/**
* Checks if the fragment passed in is loaded in the buffer properly
* Partially loaded fragments will be registered as a partial fragment
* @param {Object} fragment Check the fragment against all sourceBuffers loaded
*/
detectPartialFragments(fragment) {
let fragmentBuffered;
let fragKey = this.getFragmentKey(fragment);
let fragmentObject = this.fragments[fragKey];
let timeRange;
let fragmentPartial = false;

for(let type in this.timeRanges) {
if (this.timeRanges.hasOwnProperty(type)) {
if(fragment.type === 'main' || fragment.type === type) {
timeRange = this.timeRanges[type];
// Check for malformed fragments
fragmentBuffered = [];
// Gaps need to still be calculated for each type
let bufferedTimes = this.getBufferedTimes(fragment.startPTS, fragment.endPTS, timeRange);
fragmentObject.range[type] = bufferedTimes;
if(bufferedTimes.partial === true) {
fragmentPartial = true;
}
}
}
}
fragmentObject.state = fragmentPartial ? FragmentState.PARTIAL : FragmentState.OK;
}

getFragmentKey(fragment) {
return `${fragment.type}_${fragment.level}_${fragment.sn}`;
}
Expand All @@ -152,20 +129,20 @@ export class FragmentTracker extends EventHandler {
* @returns {Object} fragment Returns a partial fragment at a time or null if there is no partial fragment
*/
getPartialFragment(time) {
let fragmentObject, timePadding, startTime, endTime;
let fragmentEntity, timePadding, startTime, endTime;
let bestFragment = null;
let bestOverlap = 0;
for (let fragKey in this.fragments) {
if (this.fragments.hasOwnProperty(fragKey)) {
fragmentObject = this.fragments[fragKey];
if(fragmentObject.state === FragmentState.PARTIAL) {
startTime = fragmentObject.body.startPTS - this.bufferPadding;
endTime = fragmentObject.body.endPTS + this.bufferPadding;
fragmentEntity = this.fragments[fragKey];
if(this.isPartial(fragmentEntity)) {
startTime = fragmentEntity.body.startPTS - this.bufferPadding;
endTime = fragmentEntity.body.endPTS + this.bufferPadding;
if(time >= startTime && time <= endTime) {
// Use the fragment that has the most padding from start and end time
timePadding = Math.min(time - startTime, endTime - time);
if(bestOverlap <= timePadding) {
bestFragment = fragmentObject.body;
bestFragment = fragmentEntity.body;
bestOverlap = timePadding;
}
}
Expand All @@ -180,20 +157,43 @@ export class FragmentTracker extends EventHandler {
* @returns {String} Returns the fragment state when a fragment never loaded or if it partially loaded
*/
getState(fragment) {
let fragKey = this.getFragmentKey(fragment);
if (this.fragments[fragKey]) {
return this.fragments[fragKey].state;
let fragmentEntity = this.fragments[this.getFragmentKey(fragment)];
let state = FragmentState.NOT_LOADED;

if(fragmentEntity !== undefined) {
if(!fragmentEntity.buffered) {
state = FragmentState.APPENDING;
} else if(this.isPartial(fragmentEntity)) {
state = FragmentState.PARTIAL;
} else {
state = FragmentState.OK;
}
}
return FragmentState.NOT_LOADED;

return state;
}

/**
* Remove a fragment from fragment tracker until it is loaded again
* @param {Object} fragment The fragment to remove
*/
removeFragment(fragment) {
let fragKey = this.getFragmentKey(fragment);
delete this.fragments[fragKey];
isPartial(fragmentEntity) {
return fragmentEntity.buffered === true &&
((fragmentEntity.range.video !== undefined && fragmentEntity.range.video.partial === true) ||
(fragmentEntity.range.audio !== undefined && fragmentEntity.range.audio.partial === true));
}

isTimeBuffered(startPTS, endPTS, timeRange) {
let startTime, endTime;
for (let i = 0; i < timeRange.length; i++) {
startTime = timeRange.start(i) - this.bufferPadding;
endTime = timeRange.end(i) + this.bufferPadding;
if (startPTS >= startTime && endPTS <= endTime) {
return true;
}
if(endPTS <= startTime) {
// No need to check the rest of the timeRange as it is in order
return false;
}
}

return false;
}

/**
Expand All @@ -205,7 +205,7 @@ export class FragmentTracker extends EventHandler {
this.fragments[fragKey] = {
body: fragment,
range: {},
state: FragmentState.APPENDING
buffered: false,
};
}

Expand All @@ -230,4 +230,13 @@ export class FragmentTracker extends EventHandler {
onFragBuffered(e) {
this.detectPartialFragments(e.frag);
}

/**
* Remove a fragment from fragment tracker until it is loaded again
* @param {Object} fragment The fragment to remove
*/
removeFragment(fragment) {
let fragKey = this.getFragmentKey(fragment);
delete this.fragments[fragKey];
}
}
10 changes: 5 additions & 5 deletions tests/unit/helper/fragment-tracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('FragmentTracker', () => {
endPTS: 1,
sn: 1,
level: 1,
type: 'main'
audioOnly: false
};
hls.trigger(Event.FRAG_LOADED, { frag: fragment });

Expand Down Expand Up @@ -73,7 +73,7 @@ describe('FragmentTracker', () => {
endPTS: 1,
sn: 1,
level: 0,
type: 'video'
audioOnly: false
};
hls.trigger(Event.FRAG_LOADED, { frag: fragment });
};
Expand Down Expand Up @@ -170,7 +170,7 @@ describe('FragmentTracker', () => {
endPTS: 1,
sn: 1,
level: 1,
type: 'main'
audioOnly: false
};
hls.trigger(Event.FRAG_LOADED, { frag: fragment });

Expand Down Expand Up @@ -201,7 +201,7 @@ describe('FragmentTracker', () => {
endPTS: 1,
sn: 1,
level: 1,
type: 'main'
audioOnly: false
};
hls.trigger(Event.FRAG_LOADED, { frag: fragment });
hls.trigger(Event.BUFFER_APPENDED, {
Expand Down Expand Up @@ -231,7 +231,7 @@ describe('FragmentTracker', () => {
endPTS: 1,
sn: 1,
level: 1,
type: 'audio'
audioOnly: true
};
hls.trigger(Event.FRAG_LOADED, { frag: fragment });
hls.trigger(Event.BUFFER_APPENDED, {
Expand Down

0 comments on commit 54717c7

Please sign in to comment.