Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix multiperiod #3360

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
2433e0b
Move handling for non initial stream start time to getStreamForTime
dsilhavy Jul 16, 2020
37d9a35
Remove unused period switch handler from BolaRule
dsilhavy Jul 16, 2020
d8a5d1f
Fix type in variable
dsilhavy Jul 16, 2020
0c379ae
Check for manifest duration before assigning it to the media source
dsilhavy Jul 16, 2020
1009ff0
WiP: Multiperiod gap handling
dsilhavy Jul 17, 2020
2781df8
WiP: Multiperiod gap handling
dsilhavy Jul 20, 2020
fce76e2
WiP: Multiperiod gap handling
dsilhavy Jul 22, 2020
97efc89
Merge branch 'development' of https://github.com/Dash-Industry-Forum/…
dsilhavy Jul 22, 2020
ca5bba7
DashIF multiperiod sample working again
dsilhavy Jul 22, 2020
0854436
Elemental stream worked for a single testrun and video only
dsilhavy Jul 27, 2020
67e5030
Video only prebuffering multiple streams instead of a single one
dsilhavy Jul 27, 2020
7b11a01
Only update timestampOffset if buffering is not completed
dsilhavy Jul 27, 2020
3fc832e
First working version with audio in Chrome
dsilhavy Jul 29, 2020
7f0856f
Prebuffering with audio should work now. Adjusted BufferLevelRule bec…
dsilhavy Jul 29, 2020
e21d646
Add appendWindow update again and improve logic to calculate next str…
dsilhavy Jul 29, 2020
9d4c596
Potential fix for VoD streams not starting at 0
dsilhavy Aug 3, 2020
6b85a04
Remove console logs
dsilhavy Aug 3, 2020
b465c48
Do not deactivate stream when next stream is prebuffered
dsilhavy Aug 3, 2020
f13ed35
Use global setting for appendWindow
dsilhavy Aug 3, 2020
86da2b2
Add try catch for Sourcebuffer appendWindow reset
dsilhavy Aug 3, 2020
113a878
Fix a bug in which the DashHandler was stuck when no liveEdge was found
dsilhavy Aug 3, 2020
4bd740c
Add support for gap jumping when playback has stalled. This covers si…
dsilhavy Aug 6, 2020
5f364f2
Instead of jumping to the end of the current period jump to the start…
dsilhavy Aug 7, 2020
3cf7fe3
Gap jumping before moving to a separate GapController
dsilhavy Aug 10, 2020
240bc40
Move gap handling to GapController.js
dsilhavy Aug 10, 2020
faf8268
Change buffer level rule for audio
dsilhavy Aug 10, 2020
23da926
Specific event for gap caused playback seek
dsilhavy Aug 10, 2020
5f462bd
Deactivate postponeTimePeriod for multiperiod, need to understand the…
dsilhavy Aug 10, 2020
0312d1c
Fix an error for the bitrate metric in the ref client when playing mu…
dsilhavy Aug 10, 2020
d98baed
Fix linting errors
dsilhavy Aug 10, 2020
ea392e6
Merge development branch
dsilhavy Aug 10, 2020
c084b01
Move setting of availabilityWindow to BufferController.js
dsilhavy Aug 11, 2020
edf0f69
Fix BufferLevelRule unit test
dsilhavy Aug 11, 2020
6104e02
Add availabilityWindow to TextBufferController.js
dsilhavy Aug 11, 2020
d186c23
Check if interval handler already exist
dsilhavy Aug 11, 2020
b979f84
Small changes in the GapController.js
dsilhavy Aug 11, 2020
630c985
Change log level for ref client
dsilhavy Aug 12, 2020
079a174
Fix wrong JSDoc in Settings.js
dsilhavy Aug 12, 2020
d169ab7
Rename useAppendWindow in index.d.ts
dsilhavy Aug 12, 2020
eddd101
Add jumpLargeGaps to JsDoc and index.d.ts
dsilhavy Aug 12, 2020
3d268de
Fix JsDoc description for gap caused playback seek
dsilhavy Aug 12, 2020
b6a7739
Remove unnecessary reference to streamInfo
dsilhavy Aug 12, 2020
73b71df
Check for stalls using settings wallclock time parameter
dsilhavy Aug 12, 2020
5db2fa7
Remove streamInfo parameter from TextBufferController.js
dsilhavy Aug 12, 2020
2723d75
Use constants for interval handler
dsilhavy Aug 12, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ declare namespace dashjs {
bufferToKeep?: number;
bufferAheadToKeep?: number;
jumpGaps?: boolean;
jumpLargeGaps?: boolean;
smallGapLimit?: number;
stableBufferTime?: number;
bufferTimeAtTopQuality?: number;
Expand All @@ -125,7 +126,7 @@ declare namespace dashjs {
keepProtectionMediaKeys?: boolean;
useManifestDateHeaderTimeSource?: boolean;
useSuggestedPresentationDelay?: boolean;
useAppendWindowEnd?: boolean,
useAppendWindow?: boolean,
manifestUpdateRetryInterval?: number;
liveCatchUpMinDrift?: number;
liveCatchUpMaxDrift?: number;
Expand Down
11 changes: 6 additions & 5 deletions samples/dash-if-reference-player/app/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ app.controller('DashController', function ($scope, sources, contributors, dashif
// Add provider to beginning of each Vector
var provider = data.provider;
$scope.availableStreams.forEach(function (item) {
if(item && item.submenu && item.submenu.length > 0) {
if (item && item.submenu && item.submenu.length > 0) {
item.submenu.forEach(function (subitem) {
if(subitem && subitem.name && subitem.provider && provider[subitem.provider] && provider[subitem.provider].acronym) {
subitem.name = '[' + provider[subitem.provider].acronym + '] ' + subitem.name;
}
if (subitem && subitem.name && subitem.provider && provider[subitem.provider] && provider[subitem.provider].acronym) {
subitem.name = '[' + provider[subitem.provider].acronym + '] ' + subitem.name;
}
});
}
});
Expand Down Expand Up @@ -853,7 +853,8 @@ app.controller('DashController', function ($scope, sources, contributors, dashif
var dashAdapter = $scope.player.getDashAdapter();

if (dashMetrics && $scope.streamInfo) {
var periodIdx = $scope.streamInfo.index;
var period = dashAdapter.getPeriodById($scope.streamInfo.id);
var periodIdx = period ? period.index : $scope.streamInfo.index;

var maxIndex = dashAdapter.getMaxIndexForBufferType(type, periodIdx);
var repSwitch = dashMetrics.getCurrentRepresentationSwitch(type, true);
Expand Down
2 changes: 1 addition & 1 deletion samples/dash-if-reference-player/dashjs_config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"debug": {
"logLevel": 4
"logLevel": 5
},
"streaming": {
"metricsMaxListDepth": 50,
Expand Down
11 changes: 7 additions & 4 deletions src/core/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest';
* bufferToKeep: 20,
* bufferAheadToKeep: 80,
* jumpGaps: true,
* jumpLargeGaps: true,
* smallGapLimit: 1.5,
* stableBufferTime: 12,
* bufferTimeAtTopQuality: 30,
Expand All @@ -72,7 +73,7 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest';
* keepProtectionMediaKeys: false,
* useManifestDateHeaderTimeSource: true,
* useSuggestedPresentationDelay: true,
* useAppendWindowEnd: true,
* useAppendWindow: true,
* manifestUpdateRetryInterval: 100,
* liveCatchUpMinDrift: 0.02,
* liveCatchUpMaxDrift: 0,
Expand Down Expand Up @@ -243,6 +244,7 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest';
* Allows you to modify the buffer ahead of current time position that is kept in source buffer in seconds.
* <pre>0|--------|currentTime|-----bufferAheadToKeep----|----bufferToPrune-----------|end|</pre>
* @property {boolean} [jumpGaps=true] Sets whether player should jump small gaps (discontinuities) in the buffer.
* @property {boolean} [jumpLargeGaps=true] Sets whether player should jump large gaps (discontinuities) in the buffer.
* @property {number} [smallGapLimit=1.8] Time in seconds for a gap to be considered small.
* @property {number} [stableBufferTime=12]
* The time that the internal buffer target will be set to post startup/seeks (NOT top quality).
Expand Down Expand Up @@ -270,8 +272,8 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest';
* use of the date header will happen only after the other timing source that take precedence fail or are omitted as described.
* @property {boolean} [useSuggestedPresentationDelay=true]
* <p>Set to true if you would like to override the default live delay and honor the SuggestedPresentationDelay attribute in by the manifest.</p>
* @property {boolean} [useAppendWindowEnd=true]
* Specifies if the appendWindowEnd attribute of the MSE SourceBuffers should be set according to content duration from manifest.
* @property {boolean} [useAppendWindow=true]
* Specifies if the appendWindow attributes of the MSE SourceBuffers should be set according to content duration from manifest.
* @property {number} [manifestUpdateRetryInterval=100]
* For live streams, set the interval-frequency in milliseconds at which
* dash.js will check if the current manifest is still processed before
Expand Down Expand Up @@ -392,6 +394,7 @@ function Settings() {
bufferToKeep: 20,
bufferAheadToKeep: 80,
jumpGaps: true,
jumpLargeGaps: true,
smallGapLimit: 1.5,
stableBufferTime: 12,
bufferTimeAtTopQuality: 30,
Expand All @@ -402,7 +405,7 @@ function Settings() {
keepProtectionMediaKeys: false,
useManifestDateHeaderTimeSource: true,
useSuggestedPresentationDelay: true,
useAppendWindowEnd: true,
useAppendWindow: true,
manifestUpdateRetryInterval: 100,
liveCatchUpMinDrift: 0.02,
liveCatchUpMaxDrift: 0,
Expand Down
31 changes: 26 additions & 5 deletions src/dash/DashAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -635,14 +635,14 @@ function DashAdapter() {
/**
* Returns the bandwidth for a given representation id
* @param {number} representationId
* @param {number} periodId
* @param {number} periodIdx
* @returns {number} bandwidth
* @memberOf module:DashAdapter
* @instance
*/
function getBandwidthForRepresentation(representationId, periodId) {
function getBandwidthForRepresentation(representationId, periodIdx) {
let representation;
let period = getPeriod(periodId);
let period = getPeriod(periodIdx);

representation = findRepresentation(period, representationId);

Expand Down Expand Up @@ -678,6 +678,26 @@ function DashAdapter() {
return findMaxBufferIndex(period, bufferType);
}

/**
* Returns the voPeriod object for a given id
* @param {String} id
* @returns {object|null}
*/
function getPeriodById(id) {
if (!id || voPeriods.length === 0) {
return null;
}
const periods = voPeriods.filter((p) => {
return p.id === id;
});

if (periods && periods.length > 0) {
return periods[0];
}

return null;
}

function reset() {
voPeriods = [];
voAdaptations = {};
Expand Down Expand Up @@ -821,8 +841,8 @@ function DashAdapter() {
}
}

function getPeriod(periodId) {
return voPeriods.length > 0 ? voPeriods[0].mpd.manifest.Period_asArray[periodId] : null;
function getPeriod(periodIdx) {
return voPeriods.length > 0 ? voPeriods[0].mpd.manifest.Period_asArray[periodIdx] : null;
}

function findRepresentationIndex(period, representationId) {
Expand Down Expand Up @@ -915,6 +935,7 @@ function DashAdapter() {
getCodec: getCodec,
getVoAdaptations: getVoAdaptations,
getVoPeriods: getVoPeriods,
getPeriodById,
setCurrentMediaInfo: setCurrentMediaInfo,
reset: reset
};
Expand Down
3 changes: 1 addition & 2 deletions src/dash/DashHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,6 @@ function DashHandler(config) {
isFinished = endTime >= duration;
}
}

return isFinished;
}

Expand Down Expand Up @@ -329,7 +328,7 @@ function DashHandler(config) {
// check that there is a segment in this index
const segment = segmentsController.getSegmentByIndex(representation, indexToRequest, lastSegment ? lastSegment.mediaStartTime : -1);
if (!segment && isEndlessMedia(representation) && !dynamicStreamCompleted) {
logger.debug('No segment found at index: ' + indexToRequest + '. Wait for next loop');
logger.debug(getType() + ' No segment found at index: ' + indexToRequest + '. Wait for next loop');
return null;
} else {
if (segment) {
Expand Down
2 changes: 1 addition & 1 deletion src/dash/controllers/RepresentationController.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ function RepresentationController(config) {
err,
repSwitch;

if (r.adaptation.period.mpd.manifest.type === dashConstants.DYNAMIC && !r.adaptation.period.mpd.manifest.ignorePostponeTimePeriod) {
if (r.adaptation.period.mpd.manifest.type === dashConstants.DYNAMIC && !r.adaptation.period.mpd.manifest.ignorePostponeTimePeriod && playbackController.getStreamController().getStreams().length <= 1) {
// We must put things to sleep unless till e.g. the startTime calculation in ScheduleController.onLiveEdgeSearchCompleted fall after the segmentAvailabilityRange.start
postponeTimePeriod = getRepresentationUpdatePostponeTimePeriod(r, streamInfo);
}
Expand Down
18 changes: 18 additions & 0 deletions src/streaming/MediaPlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import DashConstants from '../dash/constants/DashConstants';
import MetricsConstants from './constants/MetricsConstants';
import PlaybackController from './controllers/PlaybackController';
import StreamController from './controllers/StreamController';
import GapController from './controllers/GapController';
import MediaController from './controllers/MediaController';
import BaseURLController from './controllers/BaseURLController';
import ManifestLoader from './ManifestLoader';
Expand Down Expand Up @@ -145,6 +146,7 @@ function MediaPlayer() {
baseURLController,
capabilities,
streamController,
gapController,
playbackController,
dashMetrics,
manifestModel,
Expand Down Expand Up @@ -196,6 +198,9 @@ function MediaPlayer() {
if (config.streamController) {
streamController = config.streamController;
}
if (config.gapController) {
gapController = config.gapController;
}
if (config.playbackController) {
playbackController = config.playbackController;
}
Expand Down Expand Up @@ -272,6 +277,10 @@ function MediaPlayer() {
streamController = StreamController(context).getInstance();
}

if (!gapController) {
gapController = GapController(context).getInstance();
}

adapter = DashAdapter(context).getInstance();

manifestModel = ManifestModel(context).getInstance();
Expand Down Expand Up @@ -1903,6 +1912,7 @@ function MediaPlayer() {
streamingInitialized = false;
adapter.reset();
streamController.reset();
gapController.reset();
playbackController.reset();
abrController.reset();
mediaController.reset();
Expand Down Expand Up @@ -1951,6 +1961,13 @@ function MediaPlayer() {
baseURLController: baseURLController
});

gapController.setConfig({
settings,
playbackController,
streamController,
videoModel
});

playbackController.setConfig({
streamController: streamController,
dashMetrics: dashMetrics,
Expand Down Expand Up @@ -1990,6 +2007,7 @@ function MediaPlayer() {

// initialises controller
streamController.initialize(autoPlay, protectionData);
gapController.initialize();
cmcdModel.initialize();
}

Expand Down
6 changes: 6 additions & 0 deletions src/streaming/MediaPlayerEvents.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,12 @@ class MediaPlayerEvents extends EventsBase {
* @event MediaPlayerEvents#MANIFEST_VALIDITY_CHANGED
*/
this.MANIFEST_VALIDITY_CHANGED = 'manifestValidityChanged';

/**
* A gap occured in the timeline which requires a seek
* @event MediaPlayerEvents#MANIFEST_VALIDITY_CHANGED
*/
this.GAP_CAUSED_PLAYBACK_SEEK = 'gapCausedPlaybackSeek';
}
}

Expand Down
46 changes: 35 additions & 11 deletions src/streaming/SourceBufferSink.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const MAX_ALLOWED_DISCONTINUITY = 0.1; // 100 milliseconds
* @ignore
* @implements FragmentSink
*/
function SourceBufferSink(mediaSource, mediaInfo, onAppendedCallback, useAppendWindowEnd, oldBuffer) {
function SourceBufferSink(mediaSource, mediaInfo, onAppendedCallback, oldBuffer) {
const context = this.context;
const eventBus = EventBus(context).getInstance();

Expand All @@ -56,7 +56,6 @@ function SourceBufferSink(mediaSource, mediaInfo, onAppendedCallback, useAppendW
let callbacks = [];
let appendQueue = [];
let onAppended = onAppendedCallback;
let setAppendWindowEnd = (useAppendWindowEnd === false) ? false : true;

function setup() {
logger = Debug(context).getInstance().getLogger(instance);
Expand All @@ -77,10 +76,6 @@ function SourceBufferSink(mediaSource, mediaInfo, onAppendedCallback, useAppendW
buffer.changeType(codec);
}

if (setAppendWindowEnd && buffer) {
buffer.appendWindowEnd = mediaSource.duration;
}

const CHECK_INTERVAL = 50;
// use updateend event if possible
if (typeof buffer.addEventListener === 'function') {
Expand Down Expand Up @@ -116,7 +111,12 @@ function SourceBufferSink(mediaSource, mediaInfo, onAppendedCallback, useAppendW
buffer.removeEventListener('abort', errHandler, false);
}
clearInterval(intervalId);
buffer.appendWindowEnd = Infinity;
try {
buffer.appendWindowEnd = Infinity;
buffer.appendWindowStart = 0;
} catch (e) {
logger.error('Failed to reset append window');
}
if (!keepBuffer) {
try {
if (!buffer.getClassName || buffer.getClassName() !== 'TextSourceBuffer') {
Expand Down Expand Up @@ -181,11 +181,34 @@ function SourceBufferSink(mediaSource, mediaInfo, onAppendedCallback, useAppendW
function updateTimestampOffset(MSETimeOffset) {
if (buffer.timestampOffset !== MSETimeOffset && !isNaN(MSETimeOffset)) {
waitForUpdateEnd(() => {
if (MSETimeOffset < 0) {
MSETimeOffset += 0.001;
}
buffer.timestampOffset = MSETimeOffset;
});
}
}

function updateAppendWindow(sInfo) {
if (!buffer) {
return;
}
waitForUpdateEnd(() => {
let appendWindowEnd = mediaSource.duration;
let appendWindowStart = 0;
if (sInfo.start && sInfo.duration && isFinite(sInfo.duration)) {
appendWindowEnd = sInfo.start + sInfo.duration;
}
if (sInfo.start) {
appendWindowStart = sInfo.start;
}
buffer.appendWindowStart = 0;
buffer.appendWindowEnd = appendWindowEnd;
buffer.appendWindowStart = appendWindowStart;
logger.debug(`Updated append window for ${mediaInfo.type}. Set start to ${buffer.appendWindowStart} and end to ${buffer.appendWindowEnd}`);
});
}

function remove(start, end, forceRemoval) {
const sourceBufferSink = this;
// make sure that the given time range is correct. Otherwise we will get InvalidAccessError
Expand Down Expand Up @@ -221,7 +244,7 @@ function SourceBufferSink(mediaSource, mediaInfo, onAppendedCallback, useAppendW
if (appendQueue.length > 0) {
isAppendingInProgress = true;
const nextChunk = appendQueue[0];
appendQueue.splice(0,1);
appendQueue.splice(0, 1);
let oldRanges = [];
const afterSuccess = function () {
// Safari sometimes drops a portion of a buffer after appending. Handle these situations here
Expand Down Expand Up @@ -284,10 +307,10 @@ function SourceBufferSink(mediaSource, mediaInfo, onAppendedCallback, useAppendW
}

function isChunkAlignedWithRange(oldRanges, chunk) {
for (let i = 0; i < oldRanges.length; i++ ) {
for (let i = 0; i < oldRanges.length; i++) {
const start = Math.round(oldRanges.start(i));
const end = Math.round(oldRanges.end(i));
if (end === chunk.start || start === chunk.end || (chunk.start >= start && chunk.end <= end) ) {
if (end === chunk.start || start === chunk.end || (chunk.start >= start && chunk.end <= end)) {
return true;
}
}
Expand Down Expand Up @@ -354,7 +377,8 @@ function SourceBufferSink(mediaSource, mediaInfo, onAppendedCallback, useAppendW
reset: reset,
updateTimestampOffset: updateTimestampOffset,
hasDiscontinuitiesAfter: hasDiscontinuitiesAfter,
waitForUpdateEnd: waitForUpdateEnd
waitForUpdateEnd: waitForUpdateEnd,
updateAppendWindow
};

setup();
Expand Down
Loading