Skip to content

Commit

Permalink
Fix for issue Dash-Industry-Forum#1000 and cleans up code complexity …
Browse files Browse the repository at this point in the history
…for NextFragmentRule.
  • Loading branch information
Dan Sparacio committed Jan 27, 2016
1 parent d00e4fe commit 8684792
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 57 deletions.
34 changes: 23 additions & 11 deletions src/dash/DashHandler.js
Expand Up @@ -86,7 +86,7 @@ function DashHandler(config) {
return currentTime;
}

function getCurrentIndex () {
function getCurrentIndex() {
return index;
}

Expand Down Expand Up @@ -762,8 +762,8 @@ function DashHandler(config) {
frag = segments[i];
ft = frag.presentationStartTime;
fd = frag.duration;
epsilon = (timeThreshold === undefined || timeThreshold === null) ? fd / 2 : timeThreshold;

epsilon = (timeThreshold === undefined || timeThreshold === null) ? 0 : timeThreshold;
//Changing epsilon from fd/2 to 0 but leaving ability to send timeThreshold in to override. May break misaligned demuxed segments.
if ((time + epsilon) >= ft &&
(time - epsilon) < (ft + fd)) {
idx = frag.availabilityIdx;
Expand Down Expand Up @@ -868,17 +868,21 @@ function DashHandler(config) {
return null;
}

requestedTime = time;
if (requestedTime !== time) { // When playing at live edge with 0 delay we may loop back with same time and index until it is available. Reduces verbosness of logs.
requestedTime = time;
log('Getting the request for ' + type + ' time : ' + time);
}

log('Getting the request for ' + type + ' time : ' + time);
index = getIndexForSegments(time, representation, timeThreshold);
//Index may be -1 if getSegments needs to update. So after getSegments is called and updated then try to get index again.
getSegments(representation);
if (index < 0) {
index = getIndexForSegments(time, representation, timeThreshold);
}

log('Index for ' + type + ' time ' + time + ' is ' + index);
if (requestedTime !== time) {
log('Index for ' + type + ' time ' + time + ' is ' + index );
}

finished = !ignoreIsFinished ? isMediaFinished(representation) : false;
if (finished) {
Expand Down Expand Up @@ -912,30 +916,38 @@ function DashHandler(config) {
function getNextSegmentRequest(representation) {
var request,
segment,
finished,
idx;
finished;

if (!representation || index === -1) {
return null;
}

requestedTime = null;
index++;
idx = index;

log('Getting the next request at index: ' + index);

finished = isMediaFinished(representation);
if (finished) {
request = new FragmentRequest();
request.action = FragmentRequest.ACTION_COMPLETE;
request.index = idx;
request.index = index;
request.mediaType = type;
request.mediaInfo = streamProcessor.getMediaInfo();
log('Signal complete.');
} else {
getSegments(representation);
segment = getSegmentByIndex(idx, representation);
segment = getSegmentByIndex(index, representation);
request = getRequestForSegment(segment);
if (!segment && isDynamic) {
/*
Sometimes when playing dynamic streams with 0 fragment delay at live edge we ask for
an index before it is available so we decrement index back and send null request
which triggers the validate loop to rerun and the next time the segment should be
available.
*/
index--;
}
}

return request;
Expand Down
2 changes: 1 addition & 1 deletion src/streaming/ManifestUpdater.js
Expand Up @@ -114,7 +114,7 @@ function ManifestUpdater() {
var date = new Date();

manifestModel.setValue(manifest);
log('Manifest has been refreshed at ' + date + '[' + date.getTime() + '] ');
log('Manifest has been refreshed at ' + date + '[' + date.getTime() / 1000 + '] ');

delay = manifestExt.getRefreshDelay(manifest);
timeSinceLastUpdate = (new Date().getTime() - manifest.loadedTime.getTime()) / 1000;
Expand Down
6 changes: 4 additions & 2 deletions src/streaming/MediaPlayer.js
Expand Up @@ -71,6 +71,7 @@ import MediaPlayerFactory from '../streaming/MediaPlayerFactory.js';

/**
* @Module MediaPlayer
* @return {{initialize: initialize, on: module.on, off: module.off, extend: extend, attachView: module.attachView, attachSource: module.attachSource, isReady: module.isReady, play: module.play, isPaused: isPaused, pause: pause, isSeeking: isSeeking, seek: module.seek, setMute: setMute, isMuted: isMuted, setVolume: setVolume, getVolume: getVolume, time: module.time, duration: module.duration, timeAsUTC: module.timeAsUTC, durationAsUTC: module.durationAsUTC, getDVRWindowSize: module.getDVRWindowSize, getDVRSeekOffset: module.getDVRSeekOffset, convertToTimeCode: module.convertToTimeCode, formatUTC: module.formatUTC, getVersion: module.getVersion, getDebug: module.getDebug, getVideoModel: module.getVideoModel, getVideoContainer: module.getVideoContainer, setLiveDelayFragmentCount: module.setLiveDelayFragmentCount, useSuggestedPresentationDelay: module.useSuggestedPresentationDelay, enableLastBitrateCaching: module.enableLastBitrateCaching, enableLastMediaSettingsCaching: module.enableLastMediaSettingsCaching, setMaxAllowedBitrateFor: module.setMaxAllowedBitrateFor, getMaxAllowedBitrateFor: module.getMaxAllowedBitrateFor, setMaxAllowedRepresentationRatioFor: module.setMaxAllowedRepresentationRatioFor, getMaxAllowedRepresentationRatioFor: module.getMaxAllowedRepresentationRatioFor, setAutoPlay: module.setAutoPlay, getAutoPlay: module.getAutoPlay, setScheduleWhilePaused: module.setScheduleWhilePaused, getScheduleWhilePaused: module.getScheduleWhilePaused, getMetricsExt: module.getMetricsExt, getMetricsFor: module.getMetricsFor, getQualityFor: module.getQualityFor, setQualityFor: module.setQualityFor, getLimitBitrateByPortal: module.getLimitBitrateByPortal, setLimitBitrateByPortal: module.setLimitBitrateByPortal, setTextTrack: module.setTextTrack, getBitrateInfoListFor: module.getBitrateInfoListFor, setInitialBitrateFor: module.setInitialBitrateFor, getInitialBitrateFor: module.getInitialBitrateFor, setInitialRepresentationRatioFor: module.setInitialRepresentationRatioFor, getInitialRepresentationRatioFor: module.getInitialRepresentationRatioFor, getStreamsFromManifest: module.getStreamsFromManifest, getTracksFor: module.getTracksFor, getTracksForTypeFromManifest: module.getTracksForTypeFromManifest, getCurrentTrackFor: module.getCurrentTrackFor, setInitialMediaSettingsFor: module.setInitialMediaSettingsFor, getInitialMediaSettingsFor: module.getInitialMediaSettingsFor, setCurrentTrack: module.setCurrentTrack, getTrackSwitchModeFor: module.getTrackSwitchModeFor, setTrackSwitchModeFor: module.setTrackSwitchModeFor, setSelectionModeForInitialTrack: module.setSelectionModeForInitialTrack, getSelectionModeForInitialTrack: module.getSelectionModeForInitialTrack, getAutoSwitchQuality: module.getAutoSwitchQuality, setAutoSwitchQuality: module.setAutoSwitchQuality, getAutoSwitchQualityFor: module.getAutoSwitchQualityFor, setAutoSwitchQualityFor: module.setAutoSwitchQualityFor, setBandwidthSafetyFactor: module.setBandwidthSafetyFactor, getBandwidthSafetyFactor: module.getBandwidthSafetyFactor, setAbandonLoadTimeout: module.setAbandonLoadTimeout, retrieveManifest: module.retrieveManifest, addUTCTimingSource: module.addUTCTimingSource, removeUTCTimingSource: module.removeUTCTimingSource, clearDefaultUTCTimingSources: module.clearDefaultUTCTimingSources, restoreDefaultUTCTimingSources: module.restoreDefaultUTCTimingSources, setBufferToKeep: module.setBufferToKeep, setBufferPruningInterval: module.setBufferPruningInterval, setStableBufferTime: module.setStableBufferTime, setBufferTimeAtTopQuality: module.setBufferTimeAtTopQuality, setFragmentLoaderRetryAttempts: module.setFragmentLoaderRetryAttempts, setFragmentLoaderRetryInterval: module.setFragmentLoaderRetryInterval, setBufferTimeAtTopQualityLongForm: module.setBufferTimeAtTopQualityLongForm, setLongFormContentDurationThreshold: module.setLongFormContentDurationThreshold, setRichBufferThreshold: module.setRichBufferThreshold, getProtectionController: module.getProtectionController, attachProtectionController: module.attachProtectionController, setProtectionData: module.setProtectionData, enableManifestDateHeaderTimeSource: module.enableManifestDateHeaderTimeSource, displayCaptionsOnTop: module.displayCaptionsOnTop, attachVideoContainer: module.attachVideoContainer, attachTTMLRenderingDiv: module.attachTTMLRenderingDiv, reset: module.reset}|*}
*/
function MediaPlayer() {

Expand Down Expand Up @@ -153,7 +154,7 @@ function MediaPlayer() {
attachSource(source);
}

log('[dash.js ' + VERSION + '] ' + 'new MediaPlayer instance has been created');
log('[dash.js ' + VERSION + '] ' + 'MediaPlayer has been initialized');
}

/**
Expand Down Expand Up @@ -452,7 +453,8 @@ function MediaPlayer() {
}

/**
* TODO Need Docs
* @memberof module:MediaPlayer
* @instance
*/
function extend(parentNameString, childInstance, override) {
FactoryMaker.extend(parentNameString, childInstance, override, context);
Expand Down
61 changes: 31 additions & 30 deletions src/streaming/controllers/ScheduleController.js
Expand Up @@ -58,7 +58,6 @@ function ScheduleController(config) {
let mediaPlayerModel = config.mediaPlayerModel;

let instance,
fragmentsToLoad,
type,
ready,
fragmentModel,
Expand All @@ -83,7 +82,6 @@ function ScheduleController(config) {


function setup() {
fragmentsToLoad = 0;
initialPlayback = true;
isStopped = false;
playListMetrics = null;
Expand Down Expand Up @@ -143,7 +141,7 @@ function ScheduleController(config) {
}
}

function doStart() {
function start() {
if (!ready) return;
isStopped = false;
if (initialPlayback) {
Expand All @@ -162,10 +160,10 @@ function ScheduleController(config) {
addPlaylistMetrics(PlayList.INITIAL_PLAY_START_REASON);
}

doStart();
start();
}

function doStop() {
function stop() {
if (isStopped) return;
isStopped = true;
log('Schedule controller stopping for ' + type);
Expand Down Expand Up @@ -202,46 +200,50 @@ function ScheduleController(config) {

function validate() {
if (isStopped || (playbackController.isPaused() && (playbackController.getPlayedRanges().length > 0) && !scheduleWhilePaused)) return;
getRequiredFragmentCount(onGetRequiredFragmentCount);
getRequiredFragmentCount();
//log("validate", type);
}

function getRequiredFragmentCount(callback) {
var rules = scheduleRulesCollection.getRules(ScheduleRulesCollection.FRAGMENTS_TO_SCHEDULE_RULES);

rulesController.applyRules(rules, streamProcessor, callback, fragmentsToLoad, function (currentValue, newValue) {
function getRequiredFragmentCount() {
let rules = scheduleRulesCollection.getRules(ScheduleRulesCollection.FRAGMENTS_TO_SCHEDULE_RULES);
rulesController.applyRules(rules, streamProcessor, onGetRequiredFragmentCount, 0, function (currentValue, newValue) {
currentValue = currentValue === SwitchRequest.NO_CHANGE ? 0 : currentValue;
return Math.max(currentValue, newValue);
});
}

function onGetRequiredFragmentCount(result) {
fragmentsToLoad = result.value;
if (fragmentsToLoad > 0 && !isFragmentLoading && (manifestExt.getIsTextTrack(type) || !bufferController.getIsAppendingInProgress())) {
if (result.value === 1 && !isFragmentLoading && (manifestExt.getIsTextTrack(type) || !bufferController.getIsAppendingInProgress())) {
isFragmentLoading = true;
abrController.getPlaybackQuality(streamProcessor, getNextFragment(onGetNextFragment));
abrController.getPlaybackQuality(streamProcessor, getNextFragment());
} else {
validateTimeout = setTimeout(function () {
//log("timeout going back to validate")
validate();
}, 1000); //TODO should this be something based on fragment duration?
startValidateTimer(1000);
}
}

function getNextFragment(callback) {
var rules = scheduleRulesCollection.getRules(ScheduleRulesCollection.NEXT_FRAGMENT_RULES);

rulesController.applyRules(rules, streamProcessor, callback, null, function (currentValue, newValue) {
function getNextFragment() {
let rules = scheduleRulesCollection.getRules(ScheduleRulesCollection.NEXT_FRAGMENT_RULES);
rulesController.applyRules(rules, streamProcessor, onGetNextFragment, null, function (currentValue, newValue) {
return newValue;
});
}

function onGetNextFragment(result) {
if (result.value) {
fragmentModel.executeRequest(result.value);
} else {
isFragmentLoading = false;
startValidateTimer(1000);
}
}

function startValidateTimer(value) {
validateTimeout = setTimeout(function () {
//log("timeout going back to validate")
validate();
}, value);
}

function onQualityChanged(e) {
if (type !== e.mediaType || streamProcessor.getStreamInfo().id !== e.streamInfo.id) return;

Expand Down Expand Up @@ -285,7 +287,7 @@ function ScheduleController(config) {
isFragmentLoading = false;
}
if (!e.error) return;
doStop();
stop();
}

function onBytesAppended(e) {
Expand All @@ -297,7 +299,7 @@ function ScheduleController(config) {

function onDataUpdateStarted(e) {
if (e.sender.getStreamProcessor() !== streamProcessor) return;
doStop();
stop();
}

function onInitRequested(e) {
Expand All @@ -313,7 +315,7 @@ function ScheduleController(config) {
fragmentModel.removeExecutedRequestsBeforeTime(e.to);

if (e.hasEnoughSpaceToAppend && !bufferController.getIsBufferingCompleted()) {
doStart();
start();
}
}

Expand All @@ -326,7 +328,7 @@ function ScheduleController(config) {

function onQuotaExceeded(e) {
if (e.sender.getStreamProcessor() !== streamProcessor) return;
doStop();
stop();
}

function addPlaylistMetrics(stopReason) {
Expand Down Expand Up @@ -354,7 +356,7 @@ function ScheduleController(config) {
}

function onPlaybackStarted() {
doStart();
start();
}

function onPlaybackSeeking(e) {
Expand Down Expand Up @@ -454,10 +456,9 @@ function ScheduleController(config) {
eventBus.off(Events.TIMED_TEXT_REQUESTED, onTimedTextRequested, this);
}

doStop();
stop();
fragmentController.detachModel(fragmentModel);
isFragmentLoading = false;
fragmentsToLoad = 0;
timeToloadDelay = 0;
seekTarget = NaN;
playbackController = null;
Expand All @@ -472,8 +473,8 @@ function ScheduleController(config) {
setTimeToLoadDelay: setTimeToLoadDelay,
getTimeToLoadDelay: getTimeToLoadDelay,
replaceCanceledRequests: replaceCanceledRequests,
start: doStart,
stop: doStop,
start: start,
stop: stop,
reset: reset
};

Expand Down
4 changes: 2 additions & 2 deletions src/streaming/models/FragmentModel.js
Expand Up @@ -185,7 +185,7 @@ function FragmentModel(config) {

if (!request) return;

//Adds the ability to delay single fragment loading time to control buffer. Needed for Advanced ABR rules.
//Adds the ability to delay single fragment loading time to control buffer.
if (now < request.delayLoadingTime ) {
delayLoadingTimeout = setTimeout(function () {
executeRequest(request);
Expand All @@ -201,8 +201,8 @@ function FragmentModel(config) {
eventBus.trigger(Events.STREAM_COMPLETED, {request: request, fragmentModel: this});
break;
case FragmentRequest.ACTION_DOWNLOAD:
loadingRequests.push(request);
addSchedulingInfoMetrics(request, FRAGMENT_MODEL_LOADING);
loadingRequests.push(request);
loadCurrentFragment(request);
break;
default:
Expand Down
4 changes: 2 additions & 2 deletions src/streaming/rules/abr/AbandonRequestsRule.js
Expand Up @@ -83,7 +83,7 @@ function AbandonRequestsRule(/*config*/) {
fragmentInfo.segmentDuration = req.duration;
fragmentInfo.bytesTotal = req.bytesTotal;
fragmentInfo.id = req.index;
//log("XXX FRAG ID : " ,fragmentInfo.id, " *****************");
//log("FRAG ID : " ,fragmentInfo.id, " *****************");
}
//update info base on subsequent progress events until completed.
fragmentInfo.bytesLoaded = req.bytesLoaded;
Expand All @@ -94,7 +94,7 @@ function AbandonRequestsRule(/*config*/) {

fragmentInfo.measuredBandwidthInKbps = Math.round(fragmentInfo.bytesLoaded * 8 / fragmentInfo.elapsedTime);
fragmentInfo.estimatedTimeOfDownload = (fragmentInfo.bytesTotal * 8 * 0.001 / fragmentInfo.measuredBandwidthInKbps).toFixed(2);
//log("XXX","id: ",fragmentInfo.id, "kbps: ", fragmentInfo.measuredBandwidthInKbps, "etd: ",fragmentInfo.estimatedTimeOfDownload, "et: ", fragmentInfo.elapsedTime/1000);
//log("id: ",fragmentInfo.id, "kbps: ", fragmentInfo.measuredBandwidthInKbps, "etd: ",fragmentInfo.estimatedTimeOfDownload, "et: ", fragmentInfo.elapsedTime/1000);

if (fragmentInfo.estimatedTimeOfDownload < (fragmentInfo.segmentDuration * ABANDON_MULTIPLIER) || representationInfo.quality === 0) {
callback(switchRequest);
Expand Down
11 changes: 2 additions & 9 deletions src/streaming/rules/scheduling/NextFragmentRequestRule.js
Expand Up @@ -31,19 +31,18 @@

import SwitchRequest from '../SwitchRequest.js';
import FactoryMaker from '../../../core/FactoryMaker.js';
import FragmentRequest from '../../vo/FragmentRequest.js';

function NextFragmentRequestRule(config) {

let instance;
let context = this.context;

let adapter = config.adapter;
let sourceBufferExt = config.sourceBufferExt;
let virtualBuffer = config.virtualBuffer;
let textSourceBuffer = config.textSourceBuffer;

function execute(rulesContext, callback) {

var mediaType = rulesContext.getMediaInfo().type;
var mediaInfo = rulesContext.getMediaInfo();
var streamId = rulesContext.getStreamInfo().id;
Expand Down Expand Up @@ -81,13 +80,7 @@ function NextFragmentRequestRule(config) {
}

request = adapter.getFragmentRequestForTime(streamProcessor, representationInfo, time, {keepIdx: keepIdx});

while (request && streamProcessor.getFragmentModel().isFragmentLoaded(request)) {
if (request.action === FragmentRequest.ACTION_COMPLETE) {
request = null;
break;
}

if (request && streamProcessor.getFragmentModel().isFragmentLoaded(request)) {
request = adapter.getNextFragmentRequest(streamProcessor, representationInfo);
}

Expand Down

0 comments on commit 8684792

Please sign in to comment.