Skip to content

Commit

Permalink
changed object to Map in fragment tracker
Browse files Browse the repository at this point in the history
  • Loading branch information
davidkim9 committed Dec 15, 2017
1 parent ea1ae42 commit 13eb526
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 65 deletions.
6 changes: 3 additions & 3 deletions src/controller/audio-stream-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -663,10 +663,10 @@ class AudioStreamController extends EventHandler {
data.endDTS = data.startDTS + fragCurrent.duration;
}

if(!fragCurrent.contentTypes) {
fragCurrent.contentTypes = new Set();
if(!fragCurrent.mediaChannels) {
fragCurrent.mediaChannels = new Set();
}
fragCurrent.contentTypes.add(data.type);
fragCurrent.mediaChannels.add(data.type);

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}`);
LevelHelper.updateFragPTSDTS(track.details,fragCurrent,data.startPTS,data.endPTS);
Expand Down
4 changes: 2 additions & 2 deletions src/controller/buffer-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,10 +224,10 @@ class BufferController extends EventHandler {


// this.sourceBuffer is better to use than media.buffered as it is closer to the PTS data from the fragments
let timeRanges = {};
let timeRanges = new Map();
for (let type in this.sourceBuffer) {
if (this.sourceBuffer.hasOwnProperty(type)) {
timeRanges[type] = this.sourceBuffer[type].buffered;
timeRanges.set(type, this.sourceBuffer[type].buffered);
}
}

Expand Down
10 changes: 5 additions & 5 deletions src/controller/stream-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ class StreamController extends EventHandler {
if ((frag.decryptdata && frag.decryptdata.uri != null) && (frag.decryptdata.key == null)) {
logger.log(`Loading key for ${frag.sn} of [${levelDetails.startSN} ,${levelDetails.endSN}],level ${level}`);
this.state = State.KEY_LOADING;
this.hls.trigger(Event.KEY_LOADING, {frag: frag});
this.hls.trigger(Event.KEY_LOADING, {frag});
} 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
Expand All @@ -512,7 +512,7 @@ class StreamController extends EventHandler {
frag.autoLevel = this.hls.autoLevelEnabled;
frag.bitrateTest = this.bitrateTest;

this.hls.trigger(Event.FRAG_LOADING, {frag: frag});
this.hls.trigger(Event.FRAG_LOADING, {frag});
// lazy demuxer init, as this could take some time ... do it during frag loading
if (!this.demuxer) {
this.demuxer = new Demuxer(this.hls,'main');
Expand Down Expand Up @@ -1100,10 +1100,10 @@ class StreamController extends EventHandler {
data.endDTS = data.startDTS + fragCurrent.duration;
}

if(!frag.contentTypes) {
frag.contentTypes = new Set();
if(!frag.mediaChannels) {
frag.mediaChannels = new Set();
}
frag.contentTypes.add(data.type);
frag.mediaChannels.add(data.type);

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}`);

Expand Down
99 changes: 44 additions & 55 deletions src/helper/fragment-tracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,43 +19,42 @@ export class FragmentTracker extends EventHandler {

this.bufferPadding = 0.2;

this.fragments = {};
this.fragments = new Map();
this.timeRanges = null;

this.timeRanges = {};
this.config = hls.config;
}

destroy() {
this.fragments = null;
this.timeRanges = null;
this.config = null;
EventHandler.prototype.destroy.call(this);
super.destroy();
}

/**
* Partial fragments effected by coded frame eviction will be removed
* The browser will unload parts of the buffer to free up memory for new buffer data
* Fragments will need to be reloaded when the buffer is freed up, removing partial fragments will allow them to reload(since there might be parts that are still playable)
* @param {String} type The type of media this is (eg. video, audio)
* @param {String} channel The channel of media this is (eg. MediaChannels.VIDEO, MediaChannels.AUDIO)
* @param {Object} timeRange TimeRange object from a sourceBuffer
*/
detectEvictedFragments(type, timeRange) {
let fragmentEntity, fragmentTimes, time;
detectEvictedFragments(channel, timeRange) {
let fragmentTimes, time;
// Check if any flagged fragments have been unloaded
for (let fragKey in this.fragments) {
if (this.fragments.hasOwnProperty(fragKey)) {
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(fragmentEntity.body);
break;
}
for (let fragmentEntity of this.fragments.values()) {
if(fragmentEntity.buffered === true) {
fragmentTimes = fragmentEntity.range[channel].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(fragmentEntity.body);
break;
}
}

}
}
}
Expand All @@ -67,24 +66,19 @@ export class FragmentTracker extends EventHandler {
*/
detectPartialFragments(fragment) {
let fragKey = this.getFragmentKey(fragment);
let fragmentEntity = this.fragments[fragKey];
let fragmentEntity = this.fragments.get(fragKey);
fragmentEntity.buffered = true;
let timeRange;
for(let type in this.timeRanges) {
if (this.timeRanges.hasOwnProperty(type)) {
if(fragment.contentTypes.has(type) === true) {
timeRange = this.timeRanges[type];
// Check for malformed fragments
// Gaps need to be calculated for each type
fragmentEntity.range[type] = this.getBufferedTimes(fragment.startPTS, fragment.endPTS, timeRange);
}
}
for (let [channel, timeRange] of this.timeRanges) {
// Check for malformed fragments
// Gaps need to be calculated for each channel
fragmentEntity.range[channel] = this.getBufferedTimes(fragment.startPTS, fragment.endPTS, timeRange);
}
}

getBufferedTimes(startPTS, endPTS, timeRange) {
let fragmentTimes = [];
let startTime, endTime, fragmentPartial = false;
let startTime, endTime;
let fragmentPartial = false;
for (let i = 0; i < timeRange.length; i++) {
startTime = timeRange.start(i) - this.bufferPadding;
endTime = timeRange.end(i) + this.bufferPadding;
Expand Down Expand Up @@ -127,22 +121,19 @@ 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 fragmentEntity, timePadding, startTime, endTime;
let timePadding, startTime, endTime;
let bestFragment = null;
let bestOverlap = 0;
for (let fragKey in this.fragments) {
if (this.fragments.hasOwnProperty(fragKey)) {
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 = fragmentEntity.body;
bestOverlap = timePadding;
}
for (let fragmentEntity of this.fragments.values()) {
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 = fragmentEntity.body;
bestOverlap = timePadding;
}
}
}
Expand All @@ -155,13 +146,14 @@ export class FragmentTracker extends EventHandler {
* @returns {String} Returns the fragment state when a fragment never loaded or if it partially loaded
*/
getState(fragment) {
let fragmentEntity = this.fragments[this.getFragmentKey(fragment)];
let fragKey = this.getFragmentKey(fragment);
let fragmentEntity = this.fragments.get(fragKey);
let state = FragmentState.NOT_LOADED;

if(fragmentEntity !== undefined) {
if(!fragmentEntity.buffered) {
state = FragmentState.APPENDING;
} else if(this.isPartial(fragmentEntity)) {
} else if(this.isPartial(fragmentEntity) === true) {
state = FragmentState.PARTIAL;
} else {
state = FragmentState.OK;
Expand Down Expand Up @@ -200,25 +192,22 @@ export class FragmentTracker extends EventHandler {
onFragLoaded(e) {
let fragment = e.frag;
let fragKey = this.getFragmentKey(fragment);
this.fragments[fragKey] = {
let fragmentEntity = {
body: fragment,
range: {},
buffered: false,
};
this.fragments.set(fragKey, fragmentEntity);
}

/**
* Fires when the buffer is updated
*/
onBufferAppended(e) {
let timeRange;
// Store the latest timeRanges loaded in the buffer
this.timeRanges = e.timeRanges;
for(let type in this.timeRanges) {
if (this.timeRanges.hasOwnProperty(type)) {
timeRange = this.timeRanges[type];
this.detectEvictedFragments(type, timeRange);
}
for (let [channel, timeRange] of this.timeRanges) {
this.detectEvictedFragments(channel, timeRange);
}
}

Expand All @@ -235,6 +224,6 @@ export class FragmentTracker extends EventHandler {
*/
removeFragment(fragment) {
let fragKey = this.getFragmentKey(fragment);
delete this.fragments[fragKey];
this.fragments.delete(fragKey);
}
}
1 change: 1 addition & 0 deletions src/hls.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export default class Hls {

// network controllers
const levelController = this.levelController = new LevelController(this);
// FragmentTracker must be defined before StreamController because the order of event handling is important
const fragmentTracker = new FragmentTracker(this);
const streamController = this.streamController = new StreamController(this, fragmentTracker);
let networkControllers = [levelController, streamController];
Expand Down
2 changes: 2 additions & 0 deletions src/media-channels.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* Audio channel constant for source buffers and tracks
* eg. sourceBuffer[MediaChannels.AUDIO] instead of sourceBuffer.audio
*
* @constant
* @default
* @type {string}
Expand All @@ -10,6 +11,7 @@ export const AUDIO = 'audio';
/**
* Video channel constant for source buffers and tracks
* eg. sourceBuffer[MediaChannels.VIDEO] instead of sourceBuffer.video
*
* @constant
* @default
* @type {string}
Expand Down

0 comments on commit 13eb526

Please sign in to comment.