Skip to content

Commit 2e7e9a9

Browse files
authored
Fix an error when no buffer sink is available due to media segments have been preloaded (#4871)
1 parent c752672 commit 2e7e9a9

File tree

4 files changed

+79
-49
lines changed

4 files changed

+79
-49
lines changed

src/streaming/SourceBufferSink.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ function SourceBufferSink(config) {
265265

266266
function getAllBufferRanges() {
267267
try {
268-
return buffer.buffered;
268+
return buffer?.buffered;
269269
} catch (e) {
270270
logger.error('getAllBufferRanges exception: ' + e.message);
271271
return null;
@@ -461,7 +461,7 @@ function SourceBufferSink(config) {
461461
try {
462462
callbacks.push(callback);
463463

464-
if (!buffer.updating) {
464+
if (buffer && !buffer.updating) {
465465
_executeCallback();
466466
}
467467
} catch (e) {

src/streaming/Stream.js

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,14 @@ function Stream(config) {
190190
/**
191191
* Activates Stream by re-initializing some of its components
192192
* @param {MediaSource} mediaSource
193-
* @param {array} previousBufferSinks
193+
* @param {array} previousSourceBufferSinks
194194
* @param representationsFromPreviousPeriod
195195
* @memberof Stream#
196196
*/
197-
function activate(mediaSource, previousBufferSinks, representationsFromPreviousPeriod = []) {
197+
function activate(mediaSource, previousSourceBufferSinks, representationsFromPreviousPeriod = []) {
198198
return new Promise((resolve, reject) => {
199199
if (isActive) {
200-
resolve(previousBufferSinks);
200+
resolve();
201201
return;
202202
}
203203

@@ -206,21 +206,21 @@ function Stream(config) {
206206
eventBus.trigger(Events.STREAM_ACTIVATED, {
207207
streamInfo
208208
});
209-
resolve(previousBufferSinks);
209+
resolve();
210210
return;
211211
}
212212

213213

214-
_initializeMedia(mediaSource, previousBufferSinks, representationsFromPreviousPeriod)
215-
.then((bufferSinks) => {
214+
_initializeMedia(mediaSource, previousSourceBufferSinks, representationsFromPreviousPeriod)
215+
.then(() => {
216216
isActive = true;
217217
if (representationsFromPreviousPeriod && representationsFromPreviousPeriod.length > 0) {
218218
startScheduleControllers();
219219
}
220220
eventBus.trigger(Events.STREAM_ACTIVATED, {
221221
streamInfo
222222
});
223-
resolve(bufferSinks);
223+
resolve();
224224
})
225225
.catch((e) => {
226226
reject(e);
@@ -257,23 +257,23 @@ function Stream(config) {
257257
/**
258258
*
259259
* @param {object} mediaSource
260-
* @param {array} previousBufferSinks
260+
* @param {array} previousSourceBufferSinks
261261
* @param representationsFromPreviousPeriod
262262
* @return {Promise<Array>}
263263
* @private
264264
*/
265-
function _initializeMedia(mediaSource, previousBufferSinks, representationsFromPreviousPeriod = []) {
266-
return _commonMediaInitialization(mediaSource, previousBufferSinks, representationsFromPreviousPeriod);
265+
function _initializeMedia(mediaSource, previousSourceBufferSinks, representationsFromPreviousPeriod = []) {
266+
return _commonMediaInitialization(mediaSource, previousSourceBufferSinks, representationsFromPreviousPeriod);
267267
}
268268

269269
/**
270270
*
271271
* @param {object} mediaSource
272-
* @param {array} previousBufferSinks
272+
* @param {array} previousSourceBufferSinks
273273
* @return {Promise<array>}
274274
* @private
275275
*/
276-
function _commonMediaInitialization(mediaSource, previousBufferSinks, representationsFromPreviousPeriod) {
276+
function _commonMediaInitialization(mediaSource, previousSourceBufferSinks, representationsFromPreviousPeriod) {
277277
return new Promise((resolve, reject) => {
278278
checkConfig();
279279

@@ -294,7 +294,7 @@ function Stream(config) {
294294

295295
Promise.all(promises)
296296
.then(() => {
297-
return _createBufferSinks(previousBufferSinks)
297+
return _createBufferSinks(previousSourceBufferSinks)
298298
})
299299
.then((bufferSinks) => {
300300
if (streamProcessors.length === 0) {
@@ -522,16 +522,16 @@ function Stream(config) {
522522

523523
/**
524524
* Creates the SourceBufferSink objects for all StreamProcessors
525-
* @param {array} previousBuffersSinks
525+
* @param {array} previousSourceBufferSinks
526526
* @return {Promise<object>}
527527
* @private
528528
*/
529-
function _createBufferSinks(previousBuffersSinks) {
529+
function _createBufferSinks(previousSourceBufferSinks) {
530530
return new Promise((resolve) => {
531531
const buffers = {};
532532
const promises = streamProcessors.map((sp) => {
533533
const oldRepresentation = sp.getRepresentation();
534-
return sp.createBufferSinks(previousBuffersSinks, oldRepresentation);
534+
return sp.createBufferSinks(previousSourceBufferSinks, oldRepresentation);
535535
});
536536

537537
Promise.all(promises)

src/streaming/controllers/BufferController.js

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -161,18 +161,18 @@ function BufferController(config) {
161161
/**
162162
* Creates a SourceBufferSink object
163163
* @param {object} mediaInfo
164-
* @param {array} oldBufferSinks
164+
* @param {Map<any, any>} previousBufferSinks
165165
* @return {Promise<Object>} SourceBufferSink
166166
*/
167-
function createBufferSink(mediaInfo, oldBufferSinks = [], oldRepresentation) {
167+
function createBufferSink(mediaInfo, previousBufferSinks = new Map(), oldRepresentation) {
168168
return new Promise((resolve, reject) => {
169169
if (!initCache || !mediaInfo) {
170170
resolve(null);
171171
return;
172172
}
173173
if (mediaSource) {
174174
isPrebuffering = false;
175-
_initializeSinkForMseBuffering(mediaInfo, oldBufferSinks, oldRepresentation)
175+
_initializeSinkForMseBuffering(mediaInfo, previousBufferSinks, oldRepresentation)
176176
.then((sink) => {
177177
resolve(sink);
178178
})
@@ -205,14 +205,14 @@ function BufferController(config) {
205205
})
206206
}
207207

208-
function _initializeSinkForMseBuffering(mediaInfo, oldBufferSinks, oldRepresentation) {
208+
function _initializeSinkForMseBuffering(mediaInfo, previousBufferSinks, oldRepresentation) {
209209
return new Promise((resolve) => {
210210
sourceBufferSink = SourceBufferSink(context).create({
211211
mediaSource,
212212
textController,
213213
eventBus
214214
});
215-
_initializeSink(mediaInfo, oldBufferSinks, oldRepresentation)
215+
_initializeSink(mediaInfo, previousBufferSinks, oldRepresentation)
216216
.then(() => {
217217
return updateBufferTimestampOffset(representationController.getCurrentRepresentation());
218218
})
@@ -226,18 +226,23 @@ function BufferController(config) {
226226
})
227227
}
228228

229-
function _initializeSink(mediaInfo, oldBufferSinks, oldRepresentation) {
229+
function _initializeSink(mediaInfo, previousBufferSinks, oldRepresentation) {
230230
const newRepresentation = representationController.getCurrentRepresentation();
231+
let previousBufferSink = null;
231232

232-
if (oldBufferSinks && oldBufferSinks[type] && (type === Constants.VIDEO || type === Constants.AUDIO)) {
233-
return _initializeSinkForStreamSwitch(mediaInfo, newRepresentation, oldBufferSinks, oldRepresentation)
233+
if (type === Constants.VIDEO || type === Constants.AUDIO) {
234+
previousBufferSink = previousBufferSinks.get(type);
235+
}
236+
237+
if (previousBufferSink) {
238+
return _initializeSinkForBufferReuse(mediaInfo, newRepresentation, previousBufferSink, oldRepresentation)
234239
} else {
235240
return _initializeSinkForFirstUse(mediaInfo, newRepresentation);
236241
}
237242
}
238243

239-
function _initializeSinkForStreamSwitch(mediaInfo, newRepresentation, oldBufferSinks, oldRepresentation) {
240-
sourceBufferSink.initializeForStreamSwitch(mediaInfo, newRepresentation, oldBufferSinks[type]);
244+
function _initializeSinkForBufferReuse(mediaInfo, newRepresentation, previousBufferSink, oldRepresentation) {
245+
sourceBufferSink.initializeForStreamSwitch(mediaInfo, newRepresentation, previousBufferSink);
241246

242247
const promises = [];
243248
promises.push(sourceBufferSink.abortBeforeAppend());

src/streaming/controllers/StreamController.js

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ function StreamController() {
6464
protectionData, extUrlQueryInfoController,
6565
autoPlay, isStreamSwitchingInProgress, hasMediaError, hasInitialisationError, mediaSource, videoModel,
6666
playbackController, serviceDescriptionController, mediaPlayerModel, customParametersModel, isPaused,
67-
initialPlayback, initialSteeringRequest, playbackEndedTimerInterval, bufferSinks, preloadingStreams, settings,
67+
initialPlayback, initialSteeringRequest, playbackEndedTimerInterval, preloadingStreams, settings,
6868
firstLicenseIsFetched, waitForPlaybackStartTimeout, providedStartTime, errorInformation;
6969

7070
function setup() {
@@ -430,6 +430,7 @@ function StreamController() {
430430

431431
let keepBuffers = false;
432432
let representationsFromPreviousPeriod = [];
433+
let sourceBufferSinksFromPreviousPeriod = _getSourceBufferSinksFromPreviousPeriod(previousStream);
433434
activeStream = stream;
434435

435436
if (previousStream) {
@@ -451,9 +452,15 @@ function StreamController() {
451452

452453
// If we have a video element we are not preloading into a virtual buffer
453454
if (videoModel.getElement()) {
454-
_openMediaSource({ seekTime, keepBuffers, streamActivated: false, representationsFromPreviousPeriod });
455+
_openMediaSource({
456+
seekTime,
457+
keepBuffers,
458+
sourceBufferSinksFromPreviousPeriod,
459+
streamActivated: false,
460+
representationsFromPreviousPeriod
461+
});
455462
} else {
456-
_activateStream({ seekTime, keepBuffers });
463+
_activateStream({ seekTime, keepBuffers, sourceBufferSinksFromPreviousPeriod });
457464
}
458465
} catch (e) {
459466
isStreamSwitchingInProgress = false;
@@ -524,11 +531,8 @@ function StreamController() {
524531
*/
525532
function _activateStream(inputParameters) {
526533
const representationsFromPreviousPeriod = inputParameters.representationsFromPreviousPeriod || [];
527-
activeStream.activate(mediaSource, inputParameters.keepBuffers ? bufferSinks : undefined, representationsFromPreviousPeriod)
528-
.then((sinks) => {
529-
if (sinks) {
530-
bufferSinks = sinks;
531-
}
534+
activeStream.activate(mediaSource, inputParameters.sourceBufferSinksFromPreviousPeriod, representationsFromPreviousPeriod)
535+
.then(() => {
532536

533537
// Set the initial time for this stream in the StreamProcessor
534538
if (!isNaN(inputParameters.seekTime)) {
@@ -549,6 +553,25 @@ function StreamController() {
549553
})
550554
}
551555

556+
function _getSourceBufferSinksFromPreviousPeriod(previousStream) {
557+
const sourceBufferSinkMap = new Map();
558+
559+
if (!previousStream) {
560+
return sourceBufferSinkMap;
561+
}
562+
563+
const previousStreamProcessors = previousStream ? previousStream.getStreamProcessors() : [];
564+
565+
previousStreamProcessors.forEach((streamProcessor) => {
566+
const sourceBufferSink = streamProcessor.getBuffer();
567+
if (sourceBufferSink) {
568+
sourceBufferSinkMap.set(sourceBufferSink.getType(), sourceBufferSink);
569+
}
570+
})
571+
572+
return sourceBufferSinkMap
573+
}
574+
552575
/**
553576
* A playback seeking event was triggered. We need to disable the preloading streams and call the respective seeking handler.
554577
* We distinguish between inner period seeks and outer period seeks
@@ -693,17 +716,22 @@ function StreamController() {
693716
*/
694717
function _onStreamCanLoadNext(nextStream, previousStream = null) {
695718

696-
if (mediaSource && !nextStream.getPreloaded()) {
697-
let seamlessPeriodSwitch = _canSourceBuffersBeKept(nextStream, previousStream);
719+
if (!mediaSource || nextStream.getPreloaded()) {
720+
return;
721+
}
698722

699-
if (seamlessPeriodSwitch) {
700-
const representationsFromPreviousPeriod = _getRepresentationsFromPreviousPeriod(previousStream);
701-
nextStream.startPreloading(mediaSource, bufferSinks, representationsFromPreviousPeriod)
702-
.then(() => {
703-
preloadingStreams.push(nextStream);
704-
});
705-
}
723+
let seamlessPeriodSwitch = _canSourceBuffersBeKept(nextStream, previousStream);
724+
725+
if (!seamlessPeriodSwitch) {
726+
return;
706727
}
728+
729+
const representationsFromPreviousPeriod = _getRepresentationsFromPreviousPeriod(previousStream);
730+
const previousSourceBufferSinks = _getSourceBufferSinksFromPreviousPeriod(previousStream);
731+
nextStream.startPreloading(mediaSource, previousSourceBufferSinks, representationsFromPreviousPeriod)
732+
.then(() => {
733+
preloadingStreams.push(nextStream);
734+
});
707735
}
708736

709737
/**
@@ -904,9 +932,7 @@ function StreamController() {
904932

905933
// If the preloading for the current stream is not scheduled, but its predecessor has finished buffering we can start prebuffering this stream
906934
if (!stream.getPreloaded() && previousStream.getHasFinishedBuffering()) {
907-
if (mediaSource) {
908-
_onStreamCanLoadNext(stream, previousStream);
909-
}
935+
_onStreamCanLoadNext(stream, previousStream);
910936
}
911937
i += 1;
912938
}
@@ -1307,7 +1333,6 @@ function StreamController() {
13071333

13081334
let allUTCTimingSources = (!adapter.getIsDynamic()) ? manifestUTCTimingSources : manifestUTCTimingSources.concat(customParametersModel.getUTCTimingSources());
13091335
timeSyncController.attemptSync(allUTCTimingSources, adapter.getIsDynamic());
1310-
13111336
extUrlQueryInfoController.createFinalQueryStrings(manifest);
13121337
});
13131338
} else {

0 commit comments

Comments
 (0)