Skip to content

Commit

Permalink
feat: Add segment URIs to segment-related errors (#6714)
Browse files Browse the repository at this point in the history
This makes it more feasible to debug and fix transmuxer bugs or other segment append failures.

Closes #6712
  • Loading branch information
joeyparrish authored May 31, 2024
1 parent 30ac8c0 commit 8d680e5
Show file tree
Hide file tree
Showing 12 changed files with 155 additions and 81 deletions.
6 changes: 4 additions & 2 deletions lib/media/content_workarounds.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ shaka.media.ContentWorkarounds = class {
* need this workaround.
*
* @param {!BufferSource} initSegmentBuffer
* @param {?string} uri
* @return {!Uint8Array}
* @see https://github.com/shaka-project/shaka-player/issues/2759
*/
static fakeEncryption(initSegmentBuffer) {
static fakeEncryption(initSegmentBuffer, uri) {
const ContentWorkarounds = shaka.media.ContentWorkarounds;
let initSegment = shaka.util.BufferUtils.toUint8(initSegmentBuffer);
let isEncrypted = false;
Expand Down Expand Up @@ -154,7 +155,8 @@ shaka.media.ContentWorkarounds = class {
throw new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MEDIA,
shaka.util.Error.Code.CONTENT_TRANSFORMATION_FAILED);
shaka.util.Error.Code.CONTENT_TRANSFORMATION_FAILED,
uri);
}

// Modify boxes in order from largest offset to smallest, so that earlier
Expand Down
97 changes: 66 additions & 31 deletions lib/media/media_source_engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,8 @@ shaka.media.MediaSourceEngine = class {
shaka.util.Error.Code.MEDIA_SOURCE_OPERATION_THREW,
exception,
'The mediaSource_ status was ' + this.mediaSource_.readyState +
' expected \'open\'');
' expected \'open\'',
null);
}

if (this.sequenceMode_) {
Expand Down Expand Up @@ -935,10 +936,12 @@ shaka.media.MediaSourceEngine = class {
if (this.attemptTimestampOffsetCalculation_) {
this.enqueueOperation_(
contentType,
() => this.abort_(contentType));
() => this.abort_(contentType),
null);
this.enqueueOperation_(
contentType,
() => this.setTimestampOffset_(contentType, timestampOffset));
() => this.setTimestampOffset_(contentType, timestampOffset),
null);
}
}
// Timestamps can only be reliably extracted from video, not audio.
Expand Down Expand Up @@ -983,7 +986,8 @@ shaka.media.MediaSourceEngine = class {
}

data = this.workAroundBrokenPlatforms_(
data, reference ? reference.startTime : null, contentType);
data, reference ? reference.startTime : null, contentType,
reference ? reference.getUris()[0] : null);

if (reference && this.sequenceMode_ && contentType != ContentType.TEXT) {
// In sequence mode, for non-text streams, if we just cleared the buffer
Expand All @@ -995,10 +999,14 @@ shaka.media.MediaSourceEngine = class {
// extended during unbuffered seeks or automatic adaptations; it is
// possible for the append state to be PARSING_MEDIA_SEGMENT from the
// previous SourceBuffer#appendBuffer() call.
this.enqueueOperation_(contentType, () => this.abort_(contentType));
this.enqueueOperation_(
contentType,
() => this.setTimestampOffset_(contentType, timestampOffset));
() => this.abort_(contentType),
null);
this.enqueueOperation_(
contentType,
() => this.setTimestampOffset_(contentType, timestampOffset),
null);
}
}

Expand All @@ -1009,7 +1017,7 @@ shaka.media.MediaSourceEngine = class {
bufferedBefore = this.getBuffered_(contentType);
}
this.append_(contentType, data, timestampOffset);
});
}, reference ? reference.getUris()[0] : null);

if (goog.DEBUG && reference && !reference.isPreload() && !isChunkedData) {
const bufferedAfter = this.getBuffered_(contentType);
Expand Down Expand Up @@ -1067,11 +1075,13 @@ shaka.media.MediaSourceEngine = class {
} else {
await this.enqueueOperation_(
contentType,
() => this.remove_(contentType, startTime, endTime));
() => this.remove_(contentType, startTime, endTime),
null);
if (this.needSplitMuxedContent_) {
await this.enqueueOperation_(
ContentType.AUDIO,
() => this.remove_(ContentType.AUDIO, startTime, endTime));
() => this.remove_(ContentType.AUDIO, startTime, endTime),
null);
}
}
}
Expand All @@ -1093,12 +1103,14 @@ shaka.media.MediaSourceEngine = class {
// Note that not all platforms allow clearing to Infinity.
await this.enqueueOperation_(
contentType,
() => this.remove_(contentType, 0, this.mediaSource_.duration));
() => this.remove_(contentType, 0, this.mediaSource_.duration),
null);
if (this.needSplitMuxedContent_) {
await this.enqueueOperation_(
ContentType.AUDIO,
() => this.remove_(
ContentType.AUDIO, 0, this.mediaSource_.duration));
ContentType.AUDIO, 0, this.mediaSource_.duration),
null);
}
}
}
Expand Down Expand Up @@ -1129,11 +1141,13 @@ shaka.media.MediaSourceEngine = class {
}
await this.enqueueOperation_(
contentType,
() => this.flush_(contentType));
() => this.flush_(contentType),
null);
if (this.needSplitMuxedContent_) {
await this.enqueueOperation_(
ContentType.AUDIO,
() => this.flush_(ContentType.AUDIO));
() => this.flush_(ContentType.AUDIO),
null);
}
}

Expand Down Expand Up @@ -1188,33 +1202,39 @@ shaka.media.MediaSourceEngine = class {
// set it.
operations.push(this.enqueueOperation_(
contentType,
() => this.abort_(contentType)));
() => this.abort_(contentType),
null));
if (this.needSplitMuxedContent_) {
operations.push(this.enqueueOperation_(
ContentType.AUDIO,
() => this.abort_(ContentType.AUDIO)));
() => this.abort_(ContentType.AUDIO),
null));
}
}
if (!ignoreTimestampOffset) {
operations.push(this.enqueueOperation_(
contentType,
() => this.setTimestampOffset_(contentType, timestampOffset)));
() => this.setTimestampOffset_(contentType, timestampOffset),
null));
if (this.needSplitMuxedContent_) {
operations.push(this.enqueueOperation_(
ContentType.AUDIO,
() => this.setTimestampOffset_(
ContentType.AUDIO, timestampOffset)));
ContentType.AUDIO, timestampOffset),
null));
}
}
operations.push(this.enqueueOperation_(
contentType,
() => this.setAppendWindow_(
contentType, appendWindowStart, appendWindowEnd)));
contentType, appendWindowStart, appendWindowEnd),
null));
if (this.needSplitMuxedContent_) {
operations.push(this.enqueueOperation_(
ContentType.AUDIO,
() => this.setAppendWindow_(
ContentType.AUDIO, appendWindowStart, appendWindowEnd)));
ContentType.AUDIO, appendWindowStart, appendWindowEnd),
null));
}

await Promise.all(operations);
Expand Down Expand Up @@ -1251,19 +1271,23 @@ shaka.media.MediaSourceEngine = class {
// set it.
this.enqueueOperation_(
contentType,
() => this.abort_(contentType));
() => this.abort_(contentType),
null);
if (this.needSplitMuxedContent_) {
this.enqueueOperation_(
ContentType.AUDIO,
() => this.abort_(ContentType.AUDIO));
() => this.abort_(ContentType.AUDIO),
null);
}
await this.enqueueOperation_(
contentType,
() => this.setTimestampOffset_(contentType, timestampOffset));
() => this.setTimestampOffset_(contentType, timestampOffset),
null);
if (this.needSplitMuxedContent_) {
await this.enqueueOperation_(
ContentType.AUDIO,
() => this.setTimestampOffset_(ContentType.AUDIO, timestampOffset));
() => this.setTimestampOffset_(ContentType.AUDIO, timestampOffset),
null);
}
}

Expand Down Expand Up @@ -1306,6 +1330,7 @@ shaka.media.MediaSourceEngine = class {
const dummyOperation = {
start: () => {},
p: new shaka.util.PublicPromise(),
uri: null,
};
this.queues_[contentType].unshift(dummyOperation);
}
Expand Down Expand Up @@ -1465,7 +1490,7 @@ shaka.media.MediaSourceEngine = class {
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MEDIA,
shaka.util.Error.Code.MEDIA_SOURCE_OPERATION_FAILED,
code));
code, operation.uri));
// Do not pop from queue. An 'updateend' event will fire next, and to
// avoid synchronizing these two event handlers, we will allow that one to
// pop from the queue as normal. Note that because the operation has
Expand Down Expand Up @@ -1497,14 +1522,16 @@ shaka.media.MediaSourceEngine = class {
*
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
* @param {function()} start
* @param {?string} uri
* @return {!Promise}
* @private
*/
enqueueOperation_(contentType, start) {
enqueueOperation_(contentType, start, uri) {
this.destroyer_.ensureNotDestroyed();
const operation = {
start: start,
p: new shaka.util.PublicPromise(),
uri,
};
this.queues_[contentType].push(operation);

Expand Down Expand Up @@ -1536,6 +1563,7 @@ shaka.media.MediaSourceEngine = class {
const operation = {
start: () => ready.resolve(),
p: ready,
uri: null,
};

this.queues_[contentType].push(operation);
Expand Down Expand Up @@ -1593,7 +1621,8 @@ shaka.media.MediaSourceEngine = class {
shaka.util.Error.Category.MEDIA,
shaka.util.Error.Code.MEDIA_SOURCE_OPERATION_THREW,
exception,
this.video_.error || 'No error in the media element');
this.video_.error || 'No error in the media element',
null);
} finally {
// Unblock the queues.
for (const contentType in this.sourceBuffers_) {
Expand Down Expand Up @@ -1637,7 +1666,8 @@ shaka.media.MediaSourceEngine = class {
shaka.util.Error.Category.MEDIA,
shaka.util.Error.Code.MEDIA_SOURCE_OPERATION_THREW,
exception,
this.video_.error || 'No error in the media element'));
this.video_.error || 'No error in the media element',
next.uri));
}
this.popFromQueue_(contentType);
}
Expand Down Expand Up @@ -1684,10 +1714,11 @@ shaka.media.MediaSourceEngine = class {
* @param {!BufferSource} segment
* @param {?number} startTime
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
* @param {?string} uri
* @return {!BufferSource}
* @private
*/
workAroundBrokenPlatforms_(segment, startTime, contentType) {
workAroundBrokenPlatforms_(segment, startTime, contentType, uri) {
const isInitSegment = startTime == null;
const encryptionExpected = this.expectedEncryption_[contentType];

Expand All @@ -1708,7 +1739,7 @@ shaka.media.MediaSourceEngine = class {
shaka.util.MimeUtils.getContainerType(
this.sourceBufferTypes_[contentType]) == 'mp4') {
shaka.log.debug('Forcing fake encryption information in init segment.');
segment = shaka.media.ContentWorkarounds.fakeEncryption(segment);
segment = shaka.media.ContentWorkarounds.fakeEncryption(segment, uri);
}

return segment;
Expand Down Expand Up @@ -1761,7 +1792,8 @@ shaka.media.MediaSourceEngine = class {
changeType(contentType, mimeType, transmuxer) {
return this.enqueueOperation_(
contentType,
() => this.change_(contentType, mimeType, transmuxer));
() => this.change_(contentType, mimeType, transmuxer),
null);
}

/**
Expand Down Expand Up @@ -2096,14 +2128,17 @@ shaka.media.MediaSourceEngine.createObjectURL = window.URL.createObjectURL;
/**
* @typedef {{
* start: function(),
* p: !shaka.util.PublicPromise
* p: !shaka.util.PublicPromise,
* uri: ?string
* }}
*
* @summary An operation in queue.
* @property {function()} start
* The function which starts the operation.
* @property {!shaka.util.PublicPromise} p
* The PublicPromise which is associated with this operation.
* @property {?string} uri
* A segment URI (if any) associated with this operation.
*/
shaka.media.MediaSourceEngine.Operation;

Expand Down
6 changes: 4 additions & 2 deletions lib/transmuxer/aac_transmuxer.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ shaka.transmuxer.AacTransmuxer = class {
return Promise.reject(new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MEDIA,
shaka.util.Error.Code.TRANSMUXING_FAILED));
shaka.util.Error.Code.TRANSMUXING_FAILED,
reference ? reference.getUris()[0] : null));
}
stream.audioSamplingRate = info.sampleRate;
stream.channelsCount = info.channelCount;
Expand All @@ -155,7 +156,8 @@ shaka.transmuxer.AacTransmuxer = class {
return Promise.reject(new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MEDIA,
shaka.util.Error.Code.TRANSMUXING_FAILED));
shaka.util.Error.Code.TRANSMUXING_FAILED,
reference ? reference.getUris()[0] : null));
}
const length = header.headerLength + header.frameLength;
if (offset + length <= uint8ArrayData.length) {
Expand Down
3 changes: 2 additions & 1 deletion lib/transmuxer/ac3_transmuxer.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ shaka.transmuxer.Ac3Transmuxer = class {
return Promise.reject(new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MEDIA,
shaka.util.Error.Code.TRANSMUXING_FAILED));
shaka.util.Error.Code.TRANSMUXING_FAILED,
reference ? reference.getUris()[0] : null));
}
stream.audioSamplingRate = frame.sampleRate;
stream.channelsCount = frame.channelCount;
Expand Down
3 changes: 2 additions & 1 deletion lib/transmuxer/ec3_transmuxer.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ shaka.transmuxer.Ec3Transmuxer = class {
return Promise.reject(new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MEDIA,
shaka.util.Error.Code.TRANSMUXING_FAILED));
shaka.util.Error.Code.TRANSMUXING_FAILED,
reference ? reference.getUris()[0] : null));
}
stream.audioSamplingRate = frame.sampleRate;
stream.channelsCount = frame.channelCount;
Expand Down
6 changes: 4 additions & 2 deletions lib/transmuxer/mp3_transmuxer.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ shaka.transmuxer.Mp3Transmuxer = class {
return Promise.reject(new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MEDIA,
shaka.util.Error.Code.TRANSMUXING_FAILED));
shaka.util.Error.Code.TRANSMUXING_FAILED,
reference ? reference.getUris()[0] : null));
}
if (!firstHeader) {
firstHeader = header;
Expand All @@ -155,7 +156,8 @@ shaka.transmuxer.Mp3Transmuxer = class {
return Promise.reject(new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MEDIA,
shaka.util.Error.Code.TRANSMUXING_FAILED));
shaka.util.Error.Code.TRANSMUXING_FAILED,
reference ? reference.getUris()[0] : null));
}
/** @type {number} */
const sampleRate = firstHeader.sampleRate;
Expand Down
3 changes: 2 additions & 1 deletion lib/transmuxer/mpeg_ts_transmuxer.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ shaka.transmuxer.MpegTsTransmuxer = class {
return Promise.reject(new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MEDIA,
shaka.util.Error.Code.TRANSMUXING_FAILED));
shaka.util.Error.Code.TRANSMUXING_FAILED,
reference ? reference.getUris()[0] : null));
}

let transmuxData = new Uint8Array([]);
Expand Down
Loading

0 comments on commit 8d680e5

Please sign in to comment.