shaka-player 4.12.4 → 4.12.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/deps.js +2 -2
- package/dist/locales.js +35 -35
- package/dist/shaka-player.compiled.d.ts +11 -3
- package/dist/shaka-player.compiled.debug.d.ts +11 -3
- package/dist/shaka-player.compiled.debug.externs.js +8 -5
- package/dist/shaka-player.compiled.debug.js +340 -331
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +8 -5
- package/dist/shaka-player.compiled.js +1383 -1378
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +11 -3
- package/dist/shaka-player.ui.debug.d.ts +11 -3
- package/dist/shaka-player.ui.debug.externs.js +8 -5
- package/dist/shaka-player.ui.debug.js +357 -348
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +8 -5
- package/dist/shaka-player.ui.js +1605 -1599
- package/dist/shaka-player.ui.map +3 -3
- package/externs/shaka/codecs.js +21 -0
- package/externs/shaka/offline.js +4 -0
- package/lib/cast/cast_utils.js +1 -0
- package/lib/dash/dash_parser.js +3 -0
- package/lib/hls/hls_parser.js +26 -5
- package/lib/media/drm_engine.js +70 -29
- package/lib/media/manifest_filterer.js +2 -1
- package/lib/media/media_source_engine.js +138 -129
- package/lib/media/segment_utils.js +50 -44
- package/lib/media/streaming_engine.js +9 -1
- package/lib/offline/download_info.js +5 -0
- package/lib/offline/download_manager.js +54 -0
- package/lib/offline/manifest_converter.js +1 -0
- package/lib/offline/storage.js +12 -3
- package/lib/player.js +52 -29
- package/lib/polyfill/media_capabilities.js +1 -1
- package/lib/text/simple_text_displayer.js +9 -1
- package/lib/text/ui_text_displayer.js +2 -14
- package/lib/transmuxer/aac_transmuxer.js +11 -3
- package/lib/transmuxer/ac3_transmuxer.js +11 -3
- package/lib/transmuxer/ec3_transmuxer.js +11 -3
- package/lib/transmuxer/h264.js +143 -70
- package/lib/transmuxer/mp3_transmuxer.js +11 -3
- package/lib/transmuxer/ts_transmuxer.js +68 -28
- package/lib/util/drm_utils.js +20 -0
- package/lib/util/mime_utils.js +3 -0
- package/lib/util/object_utils.js +83 -0
- package/lib/util/platform.js +48 -40
- package/lib/util/stream_utils.js +37 -113
- package/lib/util/ts_parser.js +111 -85
- package/package.json +1 -1
- package/ui/ad_counter.js +5 -0
- package/ui/controls.js +4 -2
- package/ui/fast_forward_button.js +7 -0
- package/ui/play_button.js +1 -1
- package/ui/playback_rate_selection.js +1 -1
- package/ui/rewind_button.js +7 -0
|
@@ -333,6 +333,11 @@ shaka.media.MediaSourceEngine = class {
|
|
|
333
333
|
for (const ref of stream.segmentIndex) {
|
|
334
334
|
const mimeType = ref.mimeType || stream.mimeType || '';
|
|
335
335
|
let codecs = ref.codecs || stream.codecs || '';
|
|
336
|
+
// Optimization for the case where the codecs and mimetype of the stream
|
|
337
|
+
// match the reference.
|
|
338
|
+
if (mimeType == stream.mimeType && codecs == stream.codecs) {
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
336
341
|
// Don't check the same combination of mimetype + codecs twice.
|
|
337
342
|
const combo = mimeType + ':' + codecs;
|
|
338
343
|
if (seenCombos.has(combo)) {
|
|
@@ -466,12 +471,12 @@ shaka.media.MediaSourceEngine = class {
|
|
|
466
471
|
cleanup.push(this.textEngine_.destroy());
|
|
467
472
|
}
|
|
468
473
|
|
|
474
|
+
await Promise.all(cleanup);
|
|
475
|
+
|
|
469
476
|
for (const contentType in this.transmuxers_) {
|
|
470
|
-
|
|
477
|
+
this.transmuxers_[contentType].destroy();
|
|
471
478
|
}
|
|
472
479
|
|
|
473
|
-
|
|
474
|
-
await Promise.all(cleanup);
|
|
475
480
|
if (this.eventManager_) {
|
|
476
481
|
this.eventManager_.release();
|
|
477
482
|
this.eventManager_ = null;
|
|
@@ -551,6 +556,13 @@ shaka.media.MediaSourceEngine = class {
|
|
|
551
556
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
552
557
|
|
|
553
558
|
await this.mediaSourceOpen_;
|
|
559
|
+
if (this.ended() || this.closed()) {
|
|
560
|
+
shaka.log.alwaysError('Expected MediaSource to be open during init(); ' +
|
|
561
|
+
'reopening the media source.');
|
|
562
|
+
this.mediaSourceOpen_ = new shaka.util.PublicPromise();
|
|
563
|
+
this.mediaSource_ = this.createMediaSource(this.mediaSourceOpen_);
|
|
564
|
+
await this.mediaSourceOpen_;
|
|
565
|
+
}
|
|
554
566
|
|
|
555
567
|
this.sequenceMode_ = sequenceMode;
|
|
556
568
|
this.manifestType_ = manifestType;
|
|
@@ -692,7 +704,8 @@ shaka.media.MediaSourceEngine = class {
|
|
|
692
704
|
* @return {boolean}
|
|
693
705
|
*/
|
|
694
706
|
isStreamingAllowed() {
|
|
695
|
-
return this.streamingAllowed_ && !this.usingRemotePlayback_
|
|
707
|
+
return this.streamingAllowed_ && !this.usingRemotePlayback_ &&
|
|
708
|
+
!this.reloadingMediaSource_;
|
|
696
709
|
}
|
|
697
710
|
|
|
698
711
|
/**
|
|
@@ -723,6 +736,17 @@ shaka.media.MediaSourceEngine = class {
|
|
|
723
736
|
return this.mediaSource_ ? this.mediaSource_.readyState == 'ended' : true;
|
|
724
737
|
}
|
|
725
738
|
|
|
739
|
+
/**
|
|
740
|
+
* @return {boolean} True if the MediaSource is in an "closed" state, or if
|
|
741
|
+
* the object has been destroyed.
|
|
742
|
+
*/
|
|
743
|
+
closed() {
|
|
744
|
+
if (this.reloadingMediaSource_) {
|
|
745
|
+
return false;
|
|
746
|
+
}
|
|
747
|
+
return this.mediaSource_ ? this.mediaSource_.readyState == 'closed' : true;
|
|
748
|
+
}
|
|
749
|
+
|
|
726
750
|
/**
|
|
727
751
|
* Gets the first timestamp in buffer for the given content type.
|
|
728
752
|
*
|
|
@@ -1209,7 +1233,16 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1209
1233
|
this.audioCompensation_.resolve(compensation);
|
|
1210
1234
|
}
|
|
1211
1235
|
}
|
|
1212
|
-
|
|
1236
|
+
let realTimestamp = timestamp;
|
|
1237
|
+
const RAW_FORMATS = shaka.util.MimeUtils.RAW_FORMATS;
|
|
1238
|
+
// For formats without containers and using segments mode, we need to
|
|
1239
|
+
// adjust TimestampOffset relative to 0 because segments do not have
|
|
1240
|
+
// any timestamp information.
|
|
1241
|
+
if (!this.sequenceMode_ &&
|
|
1242
|
+
RAW_FORMATS.includes(this.sourceBufferTypes_[contentType])) {
|
|
1243
|
+
realTimestamp = 0;
|
|
1244
|
+
}
|
|
1245
|
+
const calculatedTimestampOffset = reference.startTime - realTimestamp;
|
|
1213
1246
|
const timestampOffsetDifference =
|
|
1214
1247
|
Math.abs(timestampOffset - calculatedTimestampOffset);
|
|
1215
1248
|
if ((timestampOffsetDifference >= 0.001 || seeked || adaptation) &&
|
|
@@ -1566,6 +1599,13 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1566
1599
|
return;
|
|
1567
1600
|
}
|
|
1568
1601
|
|
|
1602
|
+
// Avoid changing timestampOffset when the difference is less than 100 ms
|
|
1603
|
+
// from the end of the current buffer.
|
|
1604
|
+
const bufferEnd = this.bufferEnd(contentType);
|
|
1605
|
+
if (bufferEnd && Math.abs(bufferEnd - timestampOffset) < 0.1) {
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1569
1609
|
// Queue an abort() to help MSE splice together overlapping segments.
|
|
1570
1610
|
// We set appendWindowEnd when we change periods in DASH content, and the
|
|
1571
1611
|
// period transition may result in overlap.
|
|
@@ -1607,7 +1647,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1607
1647
|
// don't call it again. Also do not call if readyState is
|
|
1608
1648
|
// 'closed' (not attached to video element) since it is not a
|
|
1609
1649
|
// valid operation.
|
|
1610
|
-
if (this.ended() || this.
|
|
1650
|
+
if (this.ended() || this.closed()) {
|
|
1611
1651
|
return;
|
|
1612
1652
|
}
|
|
1613
1653
|
// Tizen won't let us pass undefined, but it will let us omit the
|
|
@@ -1859,11 +1899,13 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1859
1899
|
|
|
1860
1900
|
/** @type {!Array.<!shaka.util.PublicPromise>} */
|
|
1861
1901
|
const allWaiters = [];
|
|
1902
|
+
/** @type {!Array.<!shaka.util.ManifestParserUtils.ContentType>} */
|
|
1903
|
+
const contentTypes = Object.keys(this.sourceBuffers_);
|
|
1862
1904
|
|
|
1863
1905
|
// Enqueue a 'wait' operation onto each queue.
|
|
1864
1906
|
// This operation signals its readiness when it starts.
|
|
1865
1907
|
// When all wait operations are ready, the real operation takes place.
|
|
1866
|
-
for (const contentType
|
|
1908
|
+
for (const contentType of contentTypes) {
|
|
1867
1909
|
const ready = new shaka.util.PublicPromise();
|
|
1868
1910
|
const operation = {
|
|
1869
1911
|
start: () => ready.resolve(),
|
|
@@ -1893,7 +1935,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1893
1935
|
// assert at the end of destroy passes. In compiled mode, the queues
|
|
1894
1936
|
// are wiped in destroy.
|
|
1895
1937
|
if (goog.DEBUG) {
|
|
1896
|
-
for (const contentType
|
|
1938
|
+
for (const contentType of contentTypes) {
|
|
1897
1939
|
if (this.queues_[contentType].length) {
|
|
1898
1940
|
goog.asserts.assert(
|
|
1899
1941
|
this.queues_[contentType].length == 1,
|
|
@@ -1910,7 +1952,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1910
1952
|
|
|
1911
1953
|
if (goog.DEBUG) {
|
|
1912
1954
|
// If we did it correctly, nothing is updating.
|
|
1913
|
-
for (const contentType
|
|
1955
|
+
for (const contentType of contentTypes) {
|
|
1914
1956
|
goog.asserts.assert(
|
|
1915
1957
|
this.sourceBuffers_[contentType].updating == false,
|
|
1916
1958
|
'SourceBuffers should not be updating after a blocking op!');
|
|
@@ -1930,7 +1972,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1930
1972
|
null);
|
|
1931
1973
|
} finally {
|
|
1932
1974
|
// Unblock the queues.
|
|
1933
|
-
for (const contentType
|
|
1975
|
+
for (const contentType of contentTypes) {
|
|
1934
1976
|
this.popFromQueue_(contentType);
|
|
1935
1977
|
}
|
|
1936
1978
|
}
|
|
@@ -1942,6 +1984,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1942
1984
|
* @private
|
|
1943
1985
|
*/
|
|
1944
1986
|
popFromQueue_(contentType) {
|
|
1987
|
+
goog.asserts.assert(this.queues_[contentType], 'Queue should exist');
|
|
1945
1988
|
// Remove the in-progress operation, which is now complete.
|
|
1946
1989
|
this.queues_[contentType].shift();
|
|
1947
1990
|
this.startOperation_(contentType);
|
|
@@ -2099,48 +2142,6 @@ shaka.media.MediaSourceEngine = class {
|
|
|
2099
2142
|
null);
|
|
2100
2143
|
}
|
|
2101
2144
|
|
|
2102
|
-
/**
|
|
2103
|
-
* Returns the source buffer parameters
|
|
2104
|
-
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
2105
|
-
* @return {?shaka.media.MediaSourceEngine.SourceBufferParams}
|
|
2106
|
-
* @private
|
|
2107
|
-
*/
|
|
2108
|
-
getSourceBufferParams_(contentType) {
|
|
2109
|
-
if (!this.sourceBuffers_[contentType]) {
|
|
2110
|
-
return null;
|
|
2111
|
-
}
|
|
2112
|
-
return {
|
|
2113
|
-
timestampOffset: this.sourceBuffers_[contentType].timestampOffset,
|
|
2114
|
-
appendWindowStart: this.sourceBuffers_[contentType].appendWindowStart,
|
|
2115
|
-
appendWindowEnd: this.sourceBuffers_[contentType].appendWindowEnd,
|
|
2116
|
-
};
|
|
2117
|
-
}
|
|
2118
|
-
|
|
2119
|
-
/**
|
|
2120
|
-
* Restore source buffer parameters
|
|
2121
|
-
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
2122
|
-
* @param {?shaka.media.MediaSourceEngine.SourceBufferParams} params
|
|
2123
|
-
* @private
|
|
2124
|
-
*/
|
|
2125
|
-
restoreSourceBufferParams_(contentType, params) {
|
|
2126
|
-
if (!params) {
|
|
2127
|
-
return;
|
|
2128
|
-
}
|
|
2129
|
-
|
|
2130
|
-
if (!this.sourceBuffers_[contentType]) {
|
|
2131
|
-
shaka.log.warning('Attempted to restore a non-existent source buffer');
|
|
2132
|
-
return;
|
|
2133
|
-
}
|
|
2134
|
-
|
|
2135
|
-
this.sourceBuffers_[contentType].timestampOffset =
|
|
2136
|
-
params.timestampOffset;
|
|
2137
|
-
// `end` needs to be set before `start`
|
|
2138
|
-
this.sourceBuffers_[contentType].appendWindowEnd =
|
|
2139
|
-
params.appendWindowEnd;
|
|
2140
|
-
this.sourceBuffers_[contentType].appendWindowStart =
|
|
2141
|
-
params.appendWindowStart;
|
|
2142
|
-
}
|
|
2143
|
-
|
|
2144
2145
|
/**
|
|
2145
2146
|
* Resets the MediaSource and re-adds source buffers due to codec mismatch
|
|
2146
2147
|
*
|
|
@@ -2149,7 +2150,6 @@ shaka.media.MediaSourceEngine = class {
|
|
|
2149
2150
|
* @private
|
|
2150
2151
|
*/
|
|
2151
2152
|
async reset_(streamsByType) {
|
|
2152
|
-
const Functional = shaka.util.Functional;
|
|
2153
2153
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
2154
2154
|
this.reloadingMediaSource_ = true;
|
|
2155
2155
|
this.needSplitMuxedContent_ = false;
|
|
@@ -2171,36 +2171,19 @@ shaka.media.MediaSourceEngine = class {
|
|
|
2171
2171
|
try {
|
|
2172
2172
|
this.eventManager_.removeAll();
|
|
2173
2173
|
|
|
2174
|
-
const cleanup = [];
|
|
2175
2174
|
for (const contentType in this.transmuxers_) {
|
|
2176
|
-
|
|
2177
|
-
}
|
|
2178
|
-
for (const contentType in this.queues_) {
|
|
2179
|
-
// Make a local copy of the queue and the first item.
|
|
2180
|
-
const q = this.queues_[contentType];
|
|
2181
|
-
const inProgress = q[0];
|
|
2182
|
-
|
|
2183
|
-
// Drop everything else out of the original queue.
|
|
2184
|
-
this.queues_[contentType] = q.slice(0, 1);
|
|
2185
|
-
|
|
2186
|
-
// We will wait for this item to complete/fail.
|
|
2187
|
-
if (inProgress) {
|
|
2188
|
-
cleanup.push(inProgress.p.catch(Functional.noop));
|
|
2189
|
-
}
|
|
2190
|
-
|
|
2191
|
-
// The rest will be rejected silently if possible.
|
|
2192
|
-
for (const item of q.slice(1)) {
|
|
2193
|
-
item.p.reject(shaka.util.Destroyer.destroyedError());
|
|
2194
|
-
}
|
|
2175
|
+
this.transmuxers_[contentType].destroy();
|
|
2195
2176
|
}
|
|
2196
2177
|
for (const contentType in this.sourceBuffers_) {
|
|
2197
2178
|
const sourceBuffer = this.sourceBuffers_[contentType];
|
|
2198
2179
|
try {
|
|
2199
2180
|
this.mediaSource_.removeSourceBuffer(sourceBuffer);
|
|
2200
|
-
} catch (e) {
|
|
2181
|
+
} catch (e) {
|
|
2182
|
+
shaka.log.debug('Exception on removeSourceBuffer', e);
|
|
2183
|
+
}
|
|
2201
2184
|
}
|
|
2202
|
-
await Promise.all(cleanup);
|
|
2203
2185
|
this.transmuxers_ = {};
|
|
2186
|
+
this.sourceBuffers_ = {};
|
|
2204
2187
|
|
|
2205
2188
|
const previousDuration = this.mediaSource_.duration;
|
|
2206
2189
|
this.mediaSourceOpen_ = new shaka.util.PublicPromise();
|
|
@@ -2231,23 +2214,17 @@ shaka.media.MediaSourceEngine = class {
|
|
|
2231
2214
|
onSourceBufferAdded);
|
|
2232
2215
|
|
|
2233
2216
|
for (const contentType of streamsByType.keys()) {
|
|
2234
|
-
const previousParams = this.getSourceBufferParams_(contentType);
|
|
2235
2217
|
const stream = streamsByType.get(contentType);
|
|
2236
2218
|
// eslint-disable-next-line no-await-in-loop
|
|
2237
2219
|
await this.initSourceBuffer_(contentType, stream, stream.codecs);
|
|
2238
|
-
if (this.needSplitMuxedContent_) {
|
|
2239
|
-
this.queues_[ContentType.AUDIO] = [];
|
|
2240
|
-
this.queues_[ContentType.VIDEO] = [];
|
|
2241
|
-
} else {
|
|
2242
|
-
this.queues_[contentType] = [];
|
|
2243
|
-
}
|
|
2244
|
-
|
|
2245
|
-
this.restoreSourceBufferParams_(contentType, previousParams);
|
|
2246
2220
|
}
|
|
2247
2221
|
const audio = streamsByType.get(ContentType.AUDIO);
|
|
2248
2222
|
if (audio && audio.isAudioMuxedInVideo) {
|
|
2249
2223
|
this.needSplitMuxedContent_ = true;
|
|
2250
2224
|
}
|
|
2225
|
+
if (this.needSplitMuxedContent_ && !this.queues_[ContentType.AUDIO]) {
|
|
2226
|
+
this.queues_[ContentType.AUDIO] = [];
|
|
2227
|
+
}
|
|
2251
2228
|
|
|
2252
2229
|
// Fake a seek to catchup the playhead.
|
|
2253
2230
|
this.video_.currentTime = currentTime;
|
|
@@ -2286,32 +2263,32 @@ shaka.media.MediaSourceEngine = class {
|
|
|
2286
2263
|
}
|
|
2287
2264
|
|
|
2288
2265
|
/**
|
|
2289
|
-
* Codec switch if necessary, this will not resolve until the codec
|
|
2290
|
-
* switch is over.
|
|
2291
2266
|
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
2292
2267
|
* @param {string} mimeType
|
|
2293
2268
|
* @param {string} codecs
|
|
2294
|
-
* @
|
|
2295
|
-
*
|
|
2296
|
-
*
|
|
2297
|
-
* false otherwise.
|
|
2269
|
+
* @return {{transmuxer: ?shaka.extern.Transmuxer,
|
|
2270
|
+
* transmuxerMuxed: boolean, basicType: string, codec: string,
|
|
2271
|
+
* mimeType: string}}
|
|
2298
2272
|
* @private
|
|
2299
2273
|
*/
|
|
2300
|
-
|
|
2274
|
+
getRealInfo_(contentType, mimeType, codecs) {
|
|
2301
2275
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
2302
|
-
if (contentType == ContentType.TEXT) {
|
|
2303
|
-
return false;
|
|
2304
|
-
}
|
|
2305
2276
|
const MimeUtils = shaka.util.MimeUtils;
|
|
2306
|
-
const currentCodec = MimeUtils.getNormalizedCodec(
|
|
2307
|
-
MimeUtils.getCodecs(this.sourceBufferTypes_[contentType]));
|
|
2308
|
-
const currentBasicType = MimeUtils.getBasicType(
|
|
2309
|
-
this.sourceBufferTypes_[contentType]);
|
|
2310
|
-
|
|
2311
2277
|
/** @type {?shaka.extern.Transmuxer} */
|
|
2312
2278
|
let transmuxer;
|
|
2313
2279
|
let transmuxerMuxed = false;
|
|
2314
|
-
|
|
2280
|
+
const audioCodec = shaka.util.ManifestParserUtils.guessCodecsSafe(
|
|
2281
|
+
ContentType.AUDIO, (codecs || '').split(','));
|
|
2282
|
+
const videoCodec = shaka.util.ManifestParserUtils.guessCodecsSafe(
|
|
2283
|
+
ContentType.VIDEO, (codecs || '').split(','));
|
|
2284
|
+
let codec = videoCodec;
|
|
2285
|
+
if (contentType == ContentType.AUDIO) {
|
|
2286
|
+
codec = audioCodec;
|
|
2287
|
+
}
|
|
2288
|
+
if (!codec) {
|
|
2289
|
+
codec = codecs;
|
|
2290
|
+
}
|
|
2291
|
+
let newMimeType = shaka.util.MimeUtils.getFullType(mimeType, codec);
|
|
2315
2292
|
let needTransmux = this.config_.forceTransmux;
|
|
2316
2293
|
if (!shaka.media.Capabilities.isTypeSupported(newMimeType) ||
|
|
2317
2294
|
(!this.sequenceMode_ &&
|
|
@@ -2321,36 +2298,79 @@ shaka.media.MediaSourceEngine = class {
|
|
|
2321
2298
|
const TransmuxerEngine = shaka.transmuxer.TransmuxerEngine;
|
|
2322
2299
|
if (needTransmux) {
|
|
2323
2300
|
const newMimeTypeWithAllCodecs =
|
|
2324
|
-
shaka.util.MimeUtils.getFullTypeWithAllCodecs(mimeType,
|
|
2301
|
+
shaka.util.MimeUtils.getFullTypeWithAllCodecs(mimeType, codec);
|
|
2325
2302
|
const transmuxerPlugin =
|
|
2326
2303
|
TransmuxerEngine.findTransmuxer(newMimeTypeWithAllCodecs);
|
|
2327
2304
|
if (transmuxerPlugin) {
|
|
2328
2305
|
transmuxer = transmuxerPlugin();
|
|
2329
|
-
const audioCodec = shaka.util.ManifestParserUtils.guessCodecsSafe(
|
|
2330
|
-
ContentType.AUDIO, (codecs || '').split(','));
|
|
2331
|
-
const videoCodec = shaka.util.ManifestParserUtils.guessCodecsSafe(
|
|
2332
|
-
ContentType.VIDEO, (codecs || '').split(','));
|
|
2333
2306
|
if (audioCodec && videoCodec) {
|
|
2334
2307
|
transmuxerMuxed = true;
|
|
2335
|
-
let codec = videoCodec;
|
|
2336
|
-
if (contentType == ContentType.AUDIO) {
|
|
2337
|
-
codec = audioCodec;
|
|
2338
|
-
}
|
|
2339
|
-
newMimeType = transmuxer.convertCodecs(contentType,
|
|
2340
|
-
shaka.util.MimeUtils.getFullTypeWithAllCodecs(mimeType, codec));
|
|
2341
|
-
} else {
|
|
2342
|
-
newMimeType =
|
|
2343
|
-
transmuxer.convertCodecs(contentType, newMimeTypeWithAllCodecs);
|
|
2344
2308
|
}
|
|
2309
|
+
newMimeType =
|
|
2310
|
+
transmuxer.convertCodecs(contentType, newMimeTypeWithAllCodecs);
|
|
2345
2311
|
}
|
|
2346
2312
|
}
|
|
2347
2313
|
|
|
2348
2314
|
const newCodec = MimeUtils.getNormalizedCodec(
|
|
2349
2315
|
MimeUtils.getCodecs(newMimeType));
|
|
2350
2316
|
const newBasicType = MimeUtils.getBasicType(newMimeType);
|
|
2317
|
+
return {
|
|
2318
|
+
transmuxer,
|
|
2319
|
+
transmuxerMuxed,
|
|
2320
|
+
basicType: newBasicType,
|
|
2321
|
+
codec: newCodec,
|
|
2322
|
+
mimeType: newMimeType,
|
|
2323
|
+
};
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
/**
|
|
2327
|
+
* Codec switch if necessary, this will not resolve until the codec
|
|
2328
|
+
* switch is over.
|
|
2329
|
+
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
2330
|
+
* @param {string} mimeType
|
|
2331
|
+
* @param {string} codecs
|
|
2332
|
+
* @param {!Map.<shaka.util.ManifestParserUtils.ContentType,
|
|
2333
|
+
* shaka.extern.Stream>} streamsByType
|
|
2334
|
+
* @return {!Promise.<boolean>} true if there was a codec switch,
|
|
2335
|
+
* false otherwise.
|
|
2336
|
+
* @private
|
|
2337
|
+
*/
|
|
2338
|
+
async codecSwitchIfNecessary_(contentType, mimeType, codecs, streamsByType) {
|
|
2339
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
2340
|
+
if (contentType == ContentType.TEXT) {
|
|
2341
|
+
return false;
|
|
2342
|
+
}
|
|
2343
|
+
const MimeUtils = shaka.util.MimeUtils;
|
|
2344
|
+
const currentCodec = MimeUtils.getNormalizedCodec(
|
|
2345
|
+
MimeUtils.getCodecs(this.sourceBufferTypes_[contentType]));
|
|
2346
|
+
const currentBasicType = MimeUtils.getBasicType(
|
|
2347
|
+
this.sourceBufferTypes_[contentType]);
|
|
2348
|
+
|
|
2349
|
+
const realInfo = this.getRealInfo_(contentType, mimeType, codecs);
|
|
2350
|
+
const transmuxer = realInfo.transmuxer;
|
|
2351
|
+
const transmuxerMuxed = realInfo.transmuxerMuxed;
|
|
2352
|
+
const newBasicType = realInfo.basicType;
|
|
2353
|
+
const newCodec = realInfo.codec;
|
|
2354
|
+
const newMimeType = realInfo.mimeType;
|
|
2355
|
+
|
|
2356
|
+
let muxedContentCheck = true;
|
|
2357
|
+
if (transmuxerMuxed) {
|
|
2358
|
+
const muxedRealInfo =
|
|
2359
|
+
this.getRealInfo_(ContentType.AUDIO, mimeType, codecs);
|
|
2360
|
+
const muxedCurrentCodec = MimeUtils.getNormalizedCodec(
|
|
2361
|
+
MimeUtils.getCodecs(this.sourceBufferTypes_[ContentType.AUDIO]));
|
|
2362
|
+
const muxedCurrentBasicType = MimeUtils.getBasicType(
|
|
2363
|
+
this.sourceBufferTypes_[ContentType.AUDIO]);
|
|
2364
|
+
muxedContentCheck = muxedCurrentCodec == muxedRealInfo.codec &&
|
|
2365
|
+
muxedCurrentBasicType == muxedRealInfo.basicType;
|
|
2366
|
+
if (muxedRealInfo.transmuxer) {
|
|
2367
|
+
muxedRealInfo.transmuxer.destroy();
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2351
2370
|
|
|
2352
2371
|
// Current/new codecs base and basic type match then no need to switch
|
|
2353
|
-
if (currentCodec === newCodec && currentBasicType === newBasicType
|
|
2372
|
+
if (currentCodec === newCodec && currentBasicType === newBasicType &&
|
|
2373
|
+
muxedContentCheck) {
|
|
2354
2374
|
if (this.transmuxers_[contentType] && !transmuxer) {
|
|
2355
2375
|
this.transmuxers_[contentType].destroy();
|
|
2356
2376
|
delete this.transmuxers_[contentType];
|
|
@@ -2393,13 +2413,12 @@ shaka.media.MediaSourceEngine = class {
|
|
|
2393
2413
|
* Returns true if it's necessary codec switch to load the new stream.
|
|
2394
2414
|
*
|
|
2395
2415
|
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
2396
|
-
* @param {shaka.extern.Stream} stream
|
|
2397
2416
|
* @param {string} refMimeType
|
|
2398
2417
|
* @param {string} refCodecs
|
|
2399
2418
|
* @return {boolean}
|
|
2400
2419
|
* @private
|
|
2401
2420
|
*/
|
|
2402
|
-
isCodecSwitchNecessary_(contentType,
|
|
2421
|
+
isCodecSwitchNecessary_(contentType, refMimeType, refCodecs) {
|
|
2403
2422
|
if (contentType == shaka.util.ManifestParserUtils.ContentType.TEXT) {
|
|
2404
2423
|
return false;
|
|
2405
2424
|
}
|
|
@@ -2443,13 +2462,12 @@ shaka.media.MediaSourceEngine = class {
|
|
|
2443
2462
|
* new stream.
|
|
2444
2463
|
*
|
|
2445
2464
|
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
2446
|
-
* @param {shaka.extern.Stream} stream
|
|
2447
2465
|
* @param {string} mimeType
|
|
2448
2466
|
* @param {string} codecs
|
|
2449
2467
|
* @return {boolean}
|
|
2450
2468
|
*/
|
|
2451
|
-
isResetMediaSourceNecessary(contentType,
|
|
2452
|
-
if (!this.isCodecSwitchNecessary_(contentType,
|
|
2469
|
+
isResetMediaSourceNecessary(contentType, mimeType, codecs) {
|
|
2470
|
+
if (!this.isCodecSwitchNecessary_(contentType, mimeType, codecs)) {
|
|
2453
2471
|
return false;
|
|
2454
2472
|
}
|
|
2455
2473
|
|
|
@@ -2540,12 +2558,3 @@ shaka.media.MediaSourceEngine.SourceBufferMode_ = {
|
|
|
2540
2558
|
* Called when an embedded 'emsg' box should trigger a manifest update.
|
|
2541
2559
|
*/
|
|
2542
2560
|
shaka.media.MediaSourceEngine.PlayerInterface;
|
|
2543
|
-
|
|
2544
|
-
/**
|
|
2545
|
-
* @typedef {{
|
|
2546
|
-
* timestampOffset: number,
|
|
2547
|
-
* appendWindowStart: number,
|
|
2548
|
-
* appendWindowEnd: number
|
|
2549
|
-
* }}
|
|
2550
|
-
*/
|
|
2551
|
-
shaka.media.MediaSourceEngine.SourceBufferParams;
|
|
@@ -48,9 +48,11 @@ shaka.media.SegmentUtils = class {
|
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
50
|
* @param {!BufferSource} data
|
|
51
|
+
* @param {boolean=} disableAudio
|
|
52
|
+
* @param {boolean=} disableVideo
|
|
51
53
|
* @return {?shaka.media.SegmentUtils.BasicInfo}
|
|
52
54
|
*/
|
|
53
|
-
static getBasicInfoFromTs(data) {
|
|
55
|
+
static getBasicInfoFromTs(data, disableAudio = false, disableVideo = false) {
|
|
54
56
|
const uint8ArrayData = shaka.util.BufferUtils.toUint8(data);
|
|
55
57
|
const tsParser = new shaka.util.TsParser().parse(uint8ArrayData);
|
|
56
58
|
const tsCodecs = tsParser.getCodecs();
|
|
@@ -58,50 +60,54 @@ shaka.media.SegmentUtils = class {
|
|
|
58
60
|
const codecs = [];
|
|
59
61
|
let hasAudio = false;
|
|
60
62
|
let hasVideo = false;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
63
|
+
if (!disableAudio) {
|
|
64
|
+
switch (tsCodecs.audio) {
|
|
65
|
+
case 'aac':
|
|
66
|
+
case 'aac-loas':
|
|
67
|
+
codecs.push('mp4a.40.2');
|
|
68
|
+
hasAudio = true;
|
|
69
|
+
break;
|
|
70
|
+
case 'mp3':
|
|
71
|
+
codecs.push('mp4a.40.34');
|
|
72
|
+
hasAudio = true;
|
|
73
|
+
break;
|
|
74
|
+
case 'ac3':
|
|
75
|
+
codecs.push('ac-3');
|
|
76
|
+
hasAudio = true;
|
|
77
|
+
break;
|
|
78
|
+
case 'ec3':
|
|
79
|
+
codecs.push('ec-3');
|
|
80
|
+
hasAudio = true;
|
|
81
|
+
break;
|
|
82
|
+
case 'opus':
|
|
83
|
+
codecs.push('opus');
|
|
84
|
+
hasAudio = true;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
83
87
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
88
|
+
if (!disableVideo) {
|
|
89
|
+
switch (tsCodecs.video) {
|
|
90
|
+
case 'avc':
|
|
91
|
+
if (videoInfo.codec) {
|
|
92
|
+
codecs.push(videoInfo.codec);
|
|
93
|
+
} else {
|
|
94
|
+
codecs.push('avc1.42E01E');
|
|
95
|
+
}
|
|
96
|
+
hasVideo = true;
|
|
97
|
+
break;
|
|
98
|
+
case 'hvc':
|
|
99
|
+
if (videoInfo.codec) {
|
|
100
|
+
codecs.push(videoInfo.codec);
|
|
101
|
+
} else {
|
|
102
|
+
codecs.push('hvc1.1.6.L93.90');
|
|
103
|
+
}
|
|
104
|
+
hasVideo = true;
|
|
105
|
+
break;
|
|
106
|
+
case 'av1':
|
|
107
|
+
codecs.push('av01.0.01M.08');
|
|
108
|
+
hasVideo = true;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
105
111
|
}
|
|
106
112
|
if (!codecs.length) {
|
|
107
113
|
return null;
|
|
@@ -2011,7 +2011,7 @@ shaka.media.StreamingEngine = class {
|
|
|
2011
2011
|
const isResetMediaSourceNecessary =
|
|
2012
2012
|
mediaState.lastCodecs && mediaState.lastMimeType &&
|
|
2013
2013
|
this.playerInterface_.mediaSourceEngine.isResetMediaSourceNecessary(
|
|
2014
|
-
mediaState.type,
|
|
2014
|
+
mediaState.type, mimeType, fullCodecs);
|
|
2015
2015
|
if (isResetMediaSourceNecessary) {
|
|
2016
2016
|
let otherState = null;
|
|
2017
2017
|
if (mediaState.type === ContentType.VIDEO) {
|
|
@@ -2025,6 +2025,10 @@ shaka.media.StreamingEngine = class {
|
|
|
2025
2025
|
// Then clear our cache of the last init segment, since MSE will be
|
|
2026
2026
|
// reloaded and no init segment will be there post-reload.
|
|
2027
2027
|
otherState.lastInitSegmentReference = null;
|
|
2028
|
+
// Clear cache of append window start and end, since they will need
|
|
2029
|
+
// to be reapplied post-reload by streaming engine.
|
|
2030
|
+
otherState.lastAppendWindowStart = null;
|
|
2031
|
+
otherState.lastAppendWindowEnd = null;
|
|
2028
2032
|
// Now force the existing buffer to be cleared. It is not necessary
|
|
2029
2033
|
// to perform the MSE clear operation, but this has the side-effect
|
|
2030
2034
|
// that our state for that stream will then match MSE's post-reload
|
|
@@ -2729,6 +2733,8 @@ shaka.media.StreamingEngine = class {
|
|
|
2729
2733
|
const audioMediaState = this.mediaStates_.get(ContentType.AUDIO);
|
|
2730
2734
|
if (audioMediaState) {
|
|
2731
2735
|
audioMediaState.lastInitSegmentReference = null;
|
|
2736
|
+
audioMediaState.lastAppendWindowStart = null;
|
|
2737
|
+
audioMediaState.lastAppendWindowEnd = null;
|
|
2732
2738
|
if (clearBuffer) {
|
|
2733
2739
|
this.forceClearBuffer_(audioMediaState);
|
|
2734
2740
|
}
|
|
@@ -2740,6 +2746,8 @@ shaka.media.StreamingEngine = class {
|
|
|
2740
2746
|
const videoMediaState = this.mediaStates_.get(ContentType.VIDEO);
|
|
2741
2747
|
if (videoMediaState) {
|
|
2742
2748
|
videoMediaState.lastInitSegmentReference = null;
|
|
2749
|
+
videoMediaState.lastAppendWindowStart = null;
|
|
2750
|
+
videoMediaState.lastAppendWindowEnd = null;
|
|
2743
2751
|
if (clearBuffer) {
|
|
2744
2752
|
this.forceClearBuffer_(videoMediaState);
|
|
2745
2753
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
goog.provide('shaka.offline.DownloadInfo');
|
|
8
8
|
|
|
9
9
|
goog.require('shaka.util.Networking');
|
|
10
|
+
goog.require('shaka.util.Uint8ArrayUtils');
|
|
10
11
|
goog.requireType('shaka.media.InitSegmentReference');
|
|
11
12
|
goog.requireType('shaka.media.SegmentReference');
|
|
12
13
|
|
|
@@ -43,6 +44,10 @@ shaka.offline.DownloadInfo = class {
|
|
|
43
44
|
* @return {string}
|
|
44
45
|
*/
|
|
45
46
|
static idForSegmentRef(ref) {
|
|
47
|
+
const segmentData = ref.getSegmentData();
|
|
48
|
+
if (segmentData) {
|
|
49
|
+
return shaka.util.Uint8ArrayUtils.toBase64(segmentData);
|
|
50
|
+
}
|
|
46
51
|
// Escape the URIs using encodeURI, to make sure that a weirdly formed URI
|
|
47
52
|
// cannot cause two unrelated refs to be considered equivalent.
|
|
48
53
|
const removeSprites = (uri) => {
|