From 13eb526415c55048d3a2d8aa96e52e406aec606c Mon Sep 17 00:00:00 2001 From: davidkim9 Date: Fri, 15 Dec 2017 14:09:50 -0500 Subject: [PATCH] changed object to Map in fragment tracker --- src/controller/audio-stream-controller.js | 6 +- src/controller/buffer-controller.js | 4 +- src/controller/stream-controller.js | 10 +-- src/helper/fragment-tracker.js | 99 ++++++++++------------- src/hls.js | 1 + src/media-channels.js | 2 + 6 files changed, 57 insertions(+), 65 deletions(-) diff --git a/src/controller/audio-stream-controller.js b/src/controller/audio-stream-controller.js index 92bc7bbe65d..7d2ebb52db5 100644 --- a/src/controller/audio-stream-controller.js +++ b/src/controller/audio-stream-controller.js @@ -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); diff --git a/src/controller/buffer-controller.js b/src/controller/buffer-controller.js index 1c6ff6992c6..adb354019ec 100644 --- a/src/controller/buffer-controller.js +++ b/src/controller/buffer-controller.js @@ -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); } } diff --git a/src/controller/stream-controller.js b/src/controller/stream-controller.js index 7d26ef11a43..3ddf0ce9f32 100644 --- a/src/controller/stream-controller.js +++ b/src/controller/stream-controller.js @@ -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 @@ -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'); @@ -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}`); diff --git a/src/helper/fragment-tracker.js b/src/helper/fragment-tracker.js index a9f5030cf2b..b662f4eb942 100644 --- a/src/helper/fragment-tracker.js +++ b/src/helper/fragment-tracker.js @@ -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; } } - } } } @@ -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; @@ -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; } } } @@ -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; @@ -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); } } @@ -235,6 +224,6 @@ export class FragmentTracker extends EventHandler { */ removeFragment(fragment) { let fragKey = this.getFragmentKey(fragment); - delete this.fragments[fragKey]; + this.fragments.delete(fragKey); } } diff --git a/src/hls.js b/src/hls.js index 4bc5b916af0..ddb16e18977 100644 --- a/src/hls.js +++ b/src/hls.js @@ -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]; diff --git a/src/media-channels.js b/src/media-channels.js index a4de1cd5359..3e135af524d 100644 --- a/src/media-channels.js +++ b/src/media-channels.js @@ -1,6 +1,7 @@ /** * Audio channel constant for source buffers and tracks * eg. sourceBuffer[MediaChannels.AUDIO] instead of sourceBuffer.audio + * * @constant * @default * @type {string} @@ -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}