Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Partial fix for FireFox MP - can now seek into period no end event prevents stream switches from automatically happening. 
Reduces code for setting index handler time for MP or track switch.
  • Loading branch information
Dan Sparacio committed Mar 8, 2016
1 parent 1819750 commit ddd2642
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 94 deletions.
5 changes: 1 addition & 4 deletions src/streaming/Stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,6 @@ function Stream(config) {

function initialize(StreamInfo, ProtectionController) {
streamInfo = StreamInfo;

//TODO will need to separate this once DRM is optional.
protectionController = ProtectionController;
if (protectionController) {
eventBus.on(Events.KEY_ERROR, onProtectionError, instance);
Expand Down Expand Up @@ -318,15 +316,14 @@ function Stream(config) {
adapter: adapter,
manifestModel: manifestModel
});
var allMediaForType = adapter.getAllMediaInfoForType(manifest, streamInfo, mediaInfo.type);

var allMediaForType = adapter.getAllMediaInfoForType(manifest, streamInfo, mediaInfo.type);
streamProcessor.initialize(getMimeTypeOrType(mediaInfo), fragmentController, mediaSource, instance, eventController);
abrController.updateTopQualityIndex(mediaInfo);

if (optionalSettings) {
streamProcessor.setBuffer(optionalSettings.buffer);
streamProcessors[optionalSettings.replaceIdx] = streamProcessor;
streamProcessor.setIndexHandlerTime(optionalSettings.currentTime);
} else {
streamProcessors.push(streamProcessor);
}
Expand Down
15 changes: 1 addition & 14 deletions src/streaming/StreamProcessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@

import AbrController from './controllers/AbrController.js';
import BufferController from './controllers/BufferController.js';
import PlaybackController from './controllers/PlaybackController.js';
import StreamController from './controllers/StreamController.js';
import MediaController from './controllers/MediaController.js';
import TextController from './controllers/TextController.js';
Expand Down Expand Up @@ -88,6 +87,7 @@ function StreamProcessor(config) {
fragmentController = FragmentController;
dynamic = stream.getStreamInfo().manifestInfo.isDynamic;

indexHandler.initialize(this);

abrController = AbrController(context).getInstance();
abrController.initialize(type, this);
Expand All @@ -114,9 +114,6 @@ function StreamProcessor(config) {
requestModifier: RequestModifier(context).getInstance()
});

indexHandler.initialize(this);
indexHandler.setCurrentTime(PlaybackController(context).getInstance().getStreamStartTime(getStreamInfo()));

representationController = RepresentationController(context).create();
representationController.initialize(this);

Expand Down Expand Up @@ -244,14 +241,6 @@ function StreamProcessor(config) {
scheduleController.stop();
}

function getIndexHandlerTime() {
return adapter.getIndexHandlerTime(this);
}

function setIndexHandlerTime(value) {
adapter.setIndexHandlerTime(this, value);
}

function getCurrentRepresentationInfo() {
return adapter.getCurrentRepresentationInfo(manifestModel.getValue(), representationController);
}
Expand Down Expand Up @@ -311,8 +300,6 @@ function StreamProcessor(config) {
getFragmentController: getFragmentController,
getRepresentationController: getRepresentationController,
getIndexHandler: getIndexHandler,
getIndexHandlerTime: getIndexHandlerTime,
setIndexHandlerTime: setIndexHandlerTime,
getCurrentRepresentationInfo: getCurrentRepresentationInfo,
getRepresentationInfoForQuality: getRepresentationInfoForQuality,
isBufferingCompleted: isBufferingCompleted,
Expand Down
88 changes: 23 additions & 65 deletions src/streaming/controllers/PlaybackController.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ function PlaybackController() {
liveStartTime,
wallclockTimeIntervalId,
commonEarliestTime,
firstAppended,
streamInfo,
isDynamic,
mediaPlayerModel,
Expand All @@ -68,7 +67,6 @@ function PlaybackController() {
wallclockTimeIntervalId = null;
isDynamic = null;
commonEarliestTime = {};
firstAppended = {};
playOnceInitialized = false;
mediaPlayerModel = MediaPlayerModel(context).getInstance();
}
Expand All @@ -92,7 +90,7 @@ function PlaybackController() {
}

function getTimeToStreamEnd() {
return ((getStreamStartTime(streamInfo) + streamInfo.duration) - getTime());
return ((getStreamStartTime(streamInfo, true) + streamInfo.duration) - getTime());
}

function isPlaybackStarted() {
Expand Down Expand Up @@ -194,7 +192,7 @@ function PlaybackController() {
eventBus.off(Events.DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this);
eventBus.off(Events.BUFFER_LEVEL_STATE_CHANGED, onBufferLevelStateChanged, this);
eventBus.off(Events.LIVE_EDGE_SEARCH_COMPLETED, onLiveEdgeSearchCompleted, this);
eventBus.off(Events.BYTES_APPENDED, onBytesAppended, this);
//eventBus.off(Events.BYTES_APPENDED, onBytesAppended, this);
stopUpdatingWallclockTime();
removeAllListeners();
}
Expand Down Expand Up @@ -239,12 +237,11 @@ function PlaybackController() {
* @returns {Number} object
* @memberof PlaybackController#
*/
function getStreamStartTime(streamInfo) {
var presentationStartTime;
var startTimeOffset = parseInt(URIQueryAndFragmentModel(context).getInstance().getURIFragmentData().s, 10);
function getStreamStartTime(streamInfo, ignoreStartOffset) {
let presentationStartTime;
let startTimeOffset = !ignoreStartOffset ? parseInt(URIQueryAndFragmentModel(context).getInstance().getURIFragmentData().s, 10) : NaN;

if (isDynamic) {

if (!isNaN(startTimeOffset) && startTimeOffset > 1262304000) {

presentationStartTime = startTimeOffset - (streamInfo.manifestInfo.availableFrom.getTime() / 1000);
Expand All @@ -261,7 +258,8 @@ function PlaybackController() {
if (!isNaN(startTimeOffset) && startTimeOffset < streamInfo.duration && startTimeOffset >= 0) {
presentationStartTime = startTimeOffset;
} else {
presentationStartTime = streamInfo.start;
let cet = commonEarliestTime[streamInfo.id] || 0.0;
presentationStartTime = Math.max(cet, streamInfo.start);
}
}

Expand Down Expand Up @@ -300,14 +298,12 @@ function PlaybackController() {
wallclockTimeIntervalId = null;
}

function initialStart() {
if (firstAppended[streamInfo.id] || isSeeking()) {
return;
function seekToStartTimeOffset() {
let initialSeekTime = getStreamStartTime(streamInfo, false);
if (!isSeeking() && initialSeekTime > 0) {
log('Starting playback at offset: ' + initialSeekTime);
seek(initialSeekTime);
}

let initialSeekTime = getStreamStartTime(streamInfo);
eventBus.trigger(Events.PLAYBACK_SEEKING, {seekTime: initialSeekTime});
log('Starting playback at offset: ' + initialSeekTime);
}

function updateCurrentTime() {
Expand All @@ -333,12 +329,12 @@ function PlaybackController() {
}

function onLiveEdgeSearchCompleted(e) {
//This is here to catch the case when live edge search takes longer than playback metadata
if (e.error || element.readyState === 0) return;

initialStart();
seekToStartTimeOffset();
}

function onCanPlay(/*e*/) {
function onCanPlay() {
eventBus.trigger(Events.CAN_PLAY);
}

Expand Down Expand Up @@ -381,17 +377,7 @@ function PlaybackController() {

function onPlaybackProgress() {
//log("Native video element event: progress");
var ranges = element.buffered;
var lastRange,
bufferEndTime,
remainingUnbufferedDuration;

if (ranges.length) {
lastRange = ranges.length - 1;
bufferEndTime = ranges.end(lastRange);
remainingUnbufferedDuration = getStreamStartTime(streamInfo) + streamInfo.duration - bufferEndTime;
}
eventBus.trigger(Events.PLAYBACK_PROGRESS, { bufferedRanges: element.buffered, remainingUnbufferedDuration: remainingUnbufferedDuration });
eventBus.trigger(Events.PLAYBACK_PROGRESS);
}

function onPlaybackRateChanged() {
Expand All @@ -403,7 +389,7 @@ function PlaybackController() {
function onPlaybackMetaDataLoaded() {
log('Native video element event: loadedmetadata');
if (!isDynamic || timelineConverter.isTimeSyncCompleted()) {
initialStart();
seekToStartTimeOffset();
}
eventBus.trigger(Events.PLAYBACK_METADATA_LOADED);
startUpdatingWallclockTime();
Expand All @@ -426,42 +412,14 @@ function PlaybackController() {
}

function onBytesAppended(e) {
var bufferedStart;
var ranges = e.bufferedRanges;
var id = streamInfo.id;
var time = getTime();
var sp = e.sender.getStreamProcessor();
var type = sp.getType();
var stream = streamController.getStreamById(streamInfo.id);
var streamStart = getStreamStartTime(streamInfo);
var segStart = e.startTime;
var currentEarliestTime = commonEarliestTime[id];

// if index is zero it means that the first segment of the Period has been appended
if (segStart === streamStart) {
firstAppended[id] = firstAppended[id] || {};
firstAppended[id][type] = true;
firstAppended[id].ready = !((stream.hasMedia('audio') && !firstAppended[id].audio) || (stream.hasMedia('video') && !firstAppended[id].video));
}

if (!ranges || !ranges.length || (firstAppended[id] && firstAppended[id].seekCompleted)) return;

bufferedStart = Math.max(ranges.start(0), streamInfo.start);
commonEarliestTime[id] = (commonEarliestTime[id] === undefined) ? bufferedStart : Math.max(commonEarliestTime[id], bufferedStart);
let ranges = e.bufferedRanges;
let bufferedStart = Math.max(ranges.start(0), streamInfo.start);
commonEarliestTime[streamInfo.id] = commonEarliestTime[streamInfo.id] === undefined ? bufferedStart : Math.max(commonEarliestTime[streamInfo.id], bufferedStart);

// do nothing if common earliest time has not changed or if the first segment has not been appended or if current
// time exceeds the common earliest time
if ((currentEarliestTime === commonEarliestTime[id] && (time === currentEarliestTime)) || !firstAppended[id] || !firstAppended[id].ready || (time > commonEarliestTime[id])) return;

//reset common earliest time every time user seeks
//to avoid mismatches when buffers have been discarded/pruned
if (isSeeking()) {
if (isSeeking()){
commonEarliestTime = {};
} else {
// seek to the max of period start or start of buffered range to avoid stalling caused by a shift between audio and video media time
seek(Math.max(commonEarliestTime[id], streamStart));
// prevents seeking the second time for the same Period
firstAppended[id].seekCompleted = true;
} else if (getTime() < commonEarliestTime[streamInfo.id]) {
seek(getStreamStartTime(streamInfo, true));
}
}

Expand Down
7 changes: 3 additions & 4 deletions src/streaming/controllers/ScheduleController.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,19 +354,18 @@ function ScheduleController(config) {
}

function onPlaybackSeeking(e) {

seekTarget = e.seekTime;

if (!initialPlayback) {
isFragmentLoading = false;
}

if (isStopped) {
start();
}

let metrics = metricsModel.getMetricsFor('stream');
let manifestUpdateInfo = dashMetrics.getCurrentManifestUpdate(metrics);

seekTarget = e.seekTime;

let latency = currentRepresentationInfo.DVRWindow ? currentRepresentationInfo.DVRWindow.end - playbackController.getTime() : NaN;
metricsModel.updateManifestUpdateInfo(manifestUpdateInfo, {latency: latency});
}
Expand Down
18 changes: 11 additions & 7 deletions src/streaming/controllers/StreamController.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ function StreamController() {
function onEnded(/*e*/) {
var nextStream = getNextStream();

switchStream(activeStream, nextStream);
switchStream(activeStream, nextStream, NaN);

flushPlaylistMetrics(
nextStream ?
Expand Down Expand Up @@ -343,23 +343,27 @@ function StreamController() {
return null;
}

function switchStream(from, to, seekTo) {
function switchStream(from, to, seekTime) {

if (isStreamSwitchingInProgress || !from || !to || from === to) return;

fireSwitchEvent(Events.PERIOD_SWITCH_STARTED, from, to);
isStreamSwitchingInProgress = true;

var onMediaSourceReady = function () {
if (seekTo !== undefined) {
playbackController.seek(seekTo);
function onMediaSourceReady() {
if (!isNaN(seekTime)) {
playbackController.seek(seekTime);
} else {
let startTime = playbackController.getStreamStartTime(activeStream.getStreamInfo(), true);
activeStream.getProcessors().forEach(p => {
adapter.setIndexHandlerTime(p, startTime);
});
}

playbackController.play();
activeStream.startEventController();
isStreamSwitchingInProgress = false;
fireSwitchEvent(Events.PERIOD_SWITCH_COMPLETED, from, to);
};
}

from.deactivate();
activeStream = to;
Expand Down

0 comments on commit ddd2642

Please sign in to comment.