Skip to content

Commit

Permalink
feat: Allow prefetch init segments
Browse files Browse the repository at this point in the history
  • Loading branch information
avelad committed Oct 27, 2023
1 parent 20b4abf commit f855bf1
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 37 deletions.
96 changes: 76 additions & 20 deletions lib/media/segment_prefetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ shaka.media.SegmentPrefetch = class {
this.fetchDispatcher_ = fetchDispatcher;

/**
* @private {!Map.<shaka.media.SegmentReference,
* !shaka.media.SegmentPrefetchOperation>}
* @private {!Map.<
* !(shaka.media.SegmentReference|shaka.media.InitSegmentReference),
* !shaka.media.SegmentPrefetchOperation>}
*/
this.segmentPrefetchMap_ = new Map();
}
Expand All @@ -50,9 +51,10 @@ shaka.media.SegmentPrefetch = class {
* Fetch next segments ahead of current segment.
*
* @param {(!shaka.media.SegmentReference)} startReference
* @param {boolean=} skipFirst
* @public
*/
prefetchSegments(startReference) {
prefetchSegments(startReference, skipFirst = false) {
goog.asserts.assert(this.prefetchLimit_ > 0,
'SegmentPrefetch can not be used when prefetchLimit <= 0.');

Expand All @@ -68,6 +70,14 @@ shaka.media.SegmentPrefetch = class {
return;
}
let reference = startReference;
if (skipFirst) {
reference = iterator.next().value;
if (reference &&
reference.startTime == startReference.startTime &&
reference.endTime == startReference.endTime) {
reference = null;
}
}
while (this.segmentPrefetchMap_.size < this.prefetchLimit_ &&
reference != null) {
// By default doesn't prefech preload partial segments when using
Expand All @@ -91,18 +101,45 @@ shaka.media.SegmentPrefetch = class {
}
}

/**
* Fetch init segment.
*
* @param {!shaka.media.InitSegmentReference} initSegmentReference
* @public
*/
prefetchInitSegment(initSegmentReference) {
goog.asserts.assert(this.prefetchLimit_ > 0,
'SegmentPrefetch can not be used when prefetchLimit <= 0.');

const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_);
if (!this.stream_.segmentIndex) {
shaka.log.info(logPrefix, 'missing segmentIndex');
return;
}

if (this.segmentPrefetchMap_.size < this.prefetchLimit_) {
if (!this.segmentPrefetchMap_.has(initSegmentReference)) {
const segmentPrefetchOperation =
new shaka.media.SegmentPrefetchOperation(this.fetchDispatcher_);
segmentPrefetchOperation.dispatchFetch(
initSegmentReference, this.stream_);
this.segmentPrefetchMap_.set(
initSegmentReference, segmentPrefetchOperation);
}
}
}

/**
* Get the result of prefetched segment if already exists.
* @param {(!shaka.media.SegmentReference)} reference
* @param {!(shaka.media.SegmentReference|shaka.media.InitSegmentReference)}
* reference
* @param {?function(BufferSource):!Promise=} streamDataCallback
* @return {?shaka.net.NetworkingEngine.PendingRequest} op
* @public
*/
getPrefetchedSegment(reference, streamDataCallback) {
goog.asserts.assert(this.prefetchLimit_ > 0,
'SegmentPrefetch can not be used when prefetchLimit <= 0.');
goog.asserts.assert(reference instanceof shaka.media.SegmentReference,
'getPrefetchedSegment is only used for shaka.media.SegmentReference.');

const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_);

Expand All @@ -112,16 +149,30 @@ shaka.media.SegmentPrefetch = class {
segmentPrefetchOperation.setStreamDataCallback(streamDataCallback);
}
this.segmentPrefetchMap_.delete(reference);
shaka.log.debug(
logPrefix,
'reused prefetched segment at time:', reference.startTime,
'mapSize', this.segmentPrefetchMap_.size);
if (reference instanceof shaka.media.SegmentReference) {
shaka.log.debug(
logPrefix,
'reused prefetched segment at time:', reference.startTime,
'mapSize', this.segmentPrefetchMap_.size);
} else {
shaka.log.debug(
logPrefix,
'reused prefetched init segment at time, mapSize',
this.segmentPrefetchMap_.size);
}
return segmentPrefetchOperation.getOperation();
} else {
shaka.log.debug(
logPrefix,
'missed segment at time:', reference.startTime,
'mapSize', this.segmentPrefetchMap_.size);
if (reference instanceof shaka.media.SegmentReference) {
shaka.log.debug(
logPrefix,
'reused prefetched segment at time:', reference.startTime,
'mapSize', this.segmentPrefetchMap_.size);
} else {
shaka.log.debug(
logPrefix,
'reused prefetched init segment at time, mapSize',
this.segmentPrefetchMap_.size);
}
return null;
}
}
Expand Down Expand Up @@ -177,7 +228,8 @@ shaka.media.SegmentPrefetch = class {

/**
* Remove a segment from prefetch map and abort it.
* @param {(!shaka.media.SegmentReference)} reference
* @param {!(shaka.media.SegmentReference|shaka.media.InitSegmentReference)}
* reference
* @private
*/
abortPrefetchedSegment_(reference) {
Expand All @@ -186,9 +238,13 @@ shaka.media.SegmentPrefetch = class {
this.segmentPrefetchMap_.delete(reference);
if (segmentPrefetchOperation) {
segmentPrefetchOperation.abort();
shaka.log.info(
logPrefix,
'pop and abort prefetched segment at time:', reference.startTime);
if (reference instanceof shaka.media.SegmentReference) {
shaka.log.info(
logPrefix,
'pop and abort prefetched segment at time:', reference.startTime);
} else {
shaka.log.info(logPrefix, 'pop and abort prefetched init segment');
}
}
}

Expand Down Expand Up @@ -224,8 +280,8 @@ shaka.media.SegmentPrefetchOperation = class {
/**
* Fetch a segments
*
* @param {!shaka.media.SegmentReference}
* reference
* @param {!(shaka.media.SegmentReference|shaka.media.InitSegmentReference)}
* reference
* @param {!shaka.extern.Stream} stream
* @public
*/
Expand Down
35 changes: 18 additions & 17 deletions lib/media/streaming_engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -1171,6 +1171,12 @@ shaka.media.StreamingEngine = class {
}

if (mediaState.segmentPrefetch && mediaState.segmentIterator) {
const initSegmentReference = reference.initSegmentReference;
if (initSegmentReference && (!mediaState.lastSegmentReference ||
!shaka.media.InitSegmentReference.equal(
initSegmentReference, mediaState.lastInitSegmentReference))) {
mediaState.segmentPrefetch.prefetchInitSegment(initSegmentReference);
}
mediaState.segmentPrefetch.prefetchSegments(reference);
}

Expand Down Expand Up @@ -1383,7 +1389,8 @@ shaka.media.StreamingEngine = class {
/* isChunkedData= */ true);

if (mediaState.segmentPrefetch && mediaState.segmentIterator) {
mediaState.segmentPrefetch.prefetchSegments(reference);
mediaState.segmentPrefetch.prefetchSegments(
reference, /* skipFirst= */ true);
}
}
};
Expand Down Expand Up @@ -1417,7 +1424,8 @@ shaka.media.StreamingEngine = class {
}

if (mediaState.segmentPrefetch && mediaState.segmentIterator) {
mediaState.segmentPrefetch.prefetchSegments(reference);
mediaState.segmentPrefetch.prefetchSegments(
reference, /* skipFirst= */ true);
}
} else {
if (this.config_.lowLatencyMode && !isReadableStreamSupported) {
Expand Down Expand Up @@ -1780,8 +1788,7 @@ shaka.media.StreamingEngine = class {
shaka.log.v1(logPrefix, 'fetching init segment');

const fetchInit =
this.fetch_(mediaState, reference.initSegmentReference,
/* streamDataCallback= */ undefined, /* isInit= */ true);
this.fetch_(mediaState, reference.initSegmentReference);
const append = async () => {
try {
const initSegment = await fetchInit;
Expand Down Expand Up @@ -2185,31 +2192,26 @@ shaka.media.StreamingEngine = class {
* @param {(!shaka.media.InitSegmentReference|!shaka.media.SegmentReference)}
* reference
* @param {?function(BufferSource):!Promise=} streamDataCallback
* @param {boolean=} isInit
*
* @return {!Promise.<BufferSource>}
* @private
* @suppress {strictMissingProperties}
*/
async fetch_(mediaState, reference, streamDataCallback, isInit) {
async fetch_(mediaState, reference, streamDataCallback) {
if (reference instanceof shaka.media.InitSegmentReference) {
const segmentData = reference.getSegmentData();
if (segmentData) {
return segmentData;
}
}
let op = null;
if (
mediaState.segmentPrefetch &&
reference instanceof shaka.media.SegmentReference
) {
if (mediaState.segmentPrefetch) {
op = mediaState.segmentPrefetch.getPrefetchedSegment(
reference, streamDataCallback);
}
if (!op) {
op = this.dispatchFetch_(
reference, mediaState.stream, streamDataCallback, isInit,
);
reference, mediaState.stream, streamDataCallback);
}

mediaState.operation = op;
Expand All @@ -2231,18 +2233,17 @@ shaka.media.StreamingEngine = class {
* @param {(!shaka.media.InitSegmentReference|!shaka.media.SegmentReference)}
* reference
* @param {?function(BufferSource):!Promise=} streamDataCallback
* @param {boolean=} isInit
*
* @return {!shaka.net.NetworkingEngine.PendingRequest}
* @private
*/
dispatchFetch_(reference, stream, streamDataCallback, isInit) {
dispatchFetch_(reference, stream, streamDataCallback) {
const requestType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
const type = isInit ?
shaka.net.NetworkingEngine.AdvancedRequestType.INIT_SEGMENT :
shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT;
const segment = reference instanceof shaka.media.SegmentReference ?
reference : undefined;
const type = segment ?
shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT :
shaka.net.NetworkingEngine.AdvancedRequestType.INIT_SEGMENT;
const request = shaka.util.Networking.createSegmentRequest(
reference.getUris(),
reference.startByte,
Expand Down
3 changes: 3 additions & 0 deletions test/test/util/fake_segment_prefetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,7 @@ shaka.test.FakeSegmentPrefetch = class {
}
return null;
}

/** @override */
prefetchInitSegment(reference) {}
};

0 comments on commit f855bf1

Please sign in to comment.