shaka-player 3.2.1 → 3.3.1
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/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/.github/ISSUE_TEMPLATE/feature_request.md +1 -1
- package/.github/ISSUE_TEMPLATE/question.md +1 -1
- package/AUTHORS +9 -5
- package/CHANGELOG.md +358 -0
- package/CONTRIBUTORS +7 -2
- package/build/conformance.textproto +183 -169
- package/build/generateExterns.js +1 -1
- package/build/types/core +3 -0
- package/build/types/offline +1 -0
- package/build/types/polyfill +1 -0
- package/build/types/ui +2 -0
- package/demo/common/assets.js +19 -1
- package/demo/common/message_ids.js +13 -1
- package/demo/config.js +31 -1
- package/demo/index.html +1 -1
- package/demo/locales/en.json +12 -0
- package/demo/locales/source.json +48 -0
- package/dist/controls.css +26 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +20 -17
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +20 -17
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +19 -12
- package/dist/locales.js +39 -37
- package/dist/receiver.compiled.debug.js +12 -10
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +12 -10
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +231 -18
- package/dist/shaka-player.compiled.debug.d.ts +231 -18
- package/dist/shaka-player.compiled.debug.externs.js +279 -8
- package/dist/shaka-player.compiled.debug.js +512 -449
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +279 -8
- package/dist/shaka-player.compiled.js +854 -821
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +247 -19
- package/dist/shaka-player.ui.debug.d.ts +247 -19
- package/dist/shaka-player.ui.debug.externs.js +307 -9
- package/dist/shaka-player.ui.debug.js +662 -580
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +307 -9
- package/dist/shaka-player.ui.js +1047 -1000
- package/dist/shaka-player.ui.map +4 -4
- package/docs/design/bg-fetch-after.gv +29 -0
- package/docs/design/bg-fetch-after.gv.png +0 -0
- package/docs/design/bg-fetch-before.gv +18 -0
- package/docs/design/bg-fetch-before.gv.png +0 -0
- package/docs/design/bg-fetch.md +134 -0
- package/docs/tutorials/blob-url.md +15 -0
- package/docs/tutorials/fairplay.md +1 -1
- package/docs/tutorials/index.json +1 -0
- package/docs/tutorials/ui-customization.md +66 -0
- package/docs/tutorials/ui.md +5 -5
- package/externs/cmcd.js +186 -0
- package/externs/ima.js +3 -0
- package/externs/shaka/ads.js +5 -0
- package/externs/shaka/manifest.js +12 -1
- package/externs/shaka/manifest_parser.js +10 -1
- package/externs/shaka/net.js +20 -4
- package/externs/shaka/offline.js +31 -2
- package/externs/shaka/player.js +138 -5
- package/externs/shaka/text.js +24 -0
- package/externs/webcrypto.js +18 -0
- package/karma.conf.js +1 -0
- package/lib/abr/ewma.js +14 -0
- package/lib/abr/ewma_bandwidth_estimator.js +16 -2
- package/lib/abr/simple_abr_manager.js +6 -0
- package/lib/ads/client_side_ad.js +8 -0
- package/lib/ads/client_side_ad_manager.js +54 -90
- package/lib/ads/server_side_ad.js +7 -0
- package/lib/ads/server_side_ad_manager.js +7 -10
- package/lib/cast/cast_proxy.js +3 -2
- package/lib/cast/cast_receiver.js +2 -1
- package/lib/cast/cast_sender.js +2 -2
- package/lib/cast/cast_utils.js +1 -0
- package/lib/dash/dash_parser.js +141 -16
- package/lib/dash/segment_base.js +25 -1
- package/lib/dash/segment_list.js +19 -10
- package/lib/dash/segment_template.js +24 -11
- package/lib/hls/hls_parser.js +146 -40
- package/lib/media/drm_engine.js +7 -0
- package/lib/media/gap_jumping_controller.js +4 -2
- package/lib/media/media_source_engine.js +1 -2
- package/lib/media/quality_observer.js +291 -0
- package/lib/media/segment_index.js +8 -1
- package/lib/media/segment_reference.js +37 -4
- package/lib/media/streaming_engine.js +49 -7
- package/lib/net/http_fetch_plugin.js +31 -11
- package/lib/net/http_plugin_utils.js +1 -0
- package/lib/net/http_xhr_plugin.js +40 -14
- package/lib/net/networking_engine.js +83 -19
- package/lib/offline/download_info.js +68 -0
- package/lib/offline/download_manager.js +13 -5
- package/lib/offline/indexeddb/base_storage_cell.js +35 -0
- package/lib/offline/indexeddb/storage_mechanism.js +1 -1
- package/lib/offline/indexeddb/v5_storage_cell.js +5 -0
- package/lib/offline/storage.js +393 -284
- package/lib/offline/stored_content_utils.js +2 -0
- package/lib/player.js +249 -43
- package/lib/polyfill/media_capabilities.js +36 -15
- package/lib/polyfill/mediasource.js +6 -0
- package/lib/polyfill/orientation.js +1 -1
- package/lib/polyfill/patchedmediakeys_apple.js +4 -4
- package/lib/polyfill/patchedmediakeys_ms.js +4 -4
- package/lib/polyfill/patchedmediakeys_webkit.js +4 -4
- package/lib/polyfill/random_uuid.js +52 -0
- package/lib/text/cue.js +18 -0
- package/lib/text/mp4_ttml_parser.js +3 -0
- package/lib/text/text_engine.js +14 -2
- package/lib/text/ttml_text_parser.js +114 -60
- package/lib/text/ui_text_displayer.js +95 -41
- package/lib/util/cmcd_manager.js +672 -0
- package/lib/util/error.js +6 -0
- package/lib/util/fake_event.js +53 -7
- package/lib/util/language_utils.js +12 -0
- package/lib/util/mutex.js +52 -0
- package/lib/util/periods.js +102 -65
- package/lib/util/platform.js +13 -1
- package/lib/util/player_configuration.js +18 -0
- package/lib/util/stream_utils.js +99 -10
- package/lib/util/xml_utils.js +19 -0
- package/package.json +2 -2
- package/shaka-player.uncompiled.js +3 -0
- package/test/cast/cast_proxy_unit.js +12 -6
- package/test/cast/cast_utils_unit.js +1 -1
- package/test/dash/dash_parser_content_protection_unit.js +2 -0
- package/test/dash/dash_parser_live_unit.js +101 -1
- package/test/dash/dash_parser_manifest_unit.js +222 -2
- package/test/dash/dash_parser_segment_base_unit.js +2 -0
- package/test/dash/dash_parser_segment_list_unit.js +2 -1
- package/test/dash/dash_parser_segment_template_unit.js +2 -1
- package/test/hls/hls_live_unit.js +2 -0
- package/test/hls/hls_parser_unit.js +7 -1
- package/test/media/closed_caption_parser_unit.js +6 -1
- package/test/media/drm_engine_unit.js +47 -3
- package/test/media/quality_observer_unit.js +139 -0
- package/test/media/segment_reference_unit.js +16 -1
- package/test/media/streaming_engine_integration.js +4 -2
- package/test/media/streaming_engine_unit.js +147 -0
- package/test/net/http_plugin_unit.js +20 -15
- package/test/offline/storage_integration.js +108 -4
- package/test/player_integration.js +178 -8
- package/test/player_src_equals_integration.js +41 -8
- package/test/player_unit.js +19 -9
- package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
- package/test/test/assets/3675/dash_0.mpd +30 -0
- package/test/test/assets/3675/dash_1.mpd +30 -0
- package/test/test/assets/3675/dash_2.mpd +30 -0
- package/test/test/assets/3675/dash_3.mpd +30 -0
- package/test/test/assets/3675/dash_4.mpd +30 -0
- package/test/test/assets/3675/dash_5.mpd +26 -0
- package/test/test/assets/3675/init-stream0.m4s +0 -0
- package/test/test/assets/chapters.vtt +2 -2
- package/test/test/assets/chapters2.vtt +10 -0
- package/test/test/util/dash_parser_util.js +4 -0
- package/test/test/util/fake_ad.js +7 -0
- package/test/test/util/fake_ad_manager.js +1 -1
- package/test/test/util/fake_drm_engine.js +9 -2
- package/test/test/util/manifest_generator.js +3 -2
- package/test/test/util/test_scheme.js +14 -1
- package/test/test/util/ttml_utils.js +81 -0
- package/test/text/mp4_ttml_parser_unit.js +11 -18
- package/test/text/ttml_text_parser_unit.js +552 -227
- package/test/text/ui_text_displayer_unit.js +29 -17
- package/test/ui/localization_unit.js +15 -14
- package/test/ui/text_displayer_layout_unit.js +13 -1
- package/test/ui/ui_unit.js +250 -0
- package/test/util/cmcd_manager_unit.js +291 -0
- package/test/util/stream_utils_unit.js +45 -4
- package/third_party/closure-uri/uri.js +21 -0
- package/ui/airplay_button.js +1 -0
- package/ui/audio_language_selection.js +7 -0
- package/ui/cast_button.js +3 -3
- package/ui/context_menu.js +109 -0
- package/ui/controls.js +45 -13
- package/ui/controls.less +1 -0
- package/ui/enums.js +4 -0
- package/ui/externs/ui.js +28 -1
- package/ui/fast_forward_button.js +14 -5
- package/ui/fullscreen_button.js +1 -0
- package/ui/less/buttons.less +0 -4
- package/ui/less/containers.less +77 -7
- package/ui/less/overflow_menu.less +0 -10
- package/ui/less/tooltip.less +97 -0
- package/ui/locales/en.json +1 -0
- package/ui/locales/ru.json +6 -6
- package/ui/locales/source.json +4 -0
- package/ui/localization.js +28 -51
- package/ui/loop_button.js +5 -0
- package/ui/mute_button.js +2 -1
- package/ui/overflow_menu.js +5 -25
- package/ui/pip_button.js +7 -3
- package/ui/play_button.js +2 -2
- package/ui/playback_rate_selection.js +8 -9
- package/ui/resolution_selection.js +7 -0
- package/ui/rewind_button.js +15 -6
- package/ui/seek_bar.js +4 -2
- package/ui/settings_menu.js +36 -4
- package/ui/small_play_button.js +1 -0
- package/ui/statistics_button.js +253 -0
- package/ui/text_selection.js +12 -4
- package/ui/ui.js +30 -0
- package/ui/volume_bar.js +1 -1
- package/externs/media_capabilities.js +0 -16
package/lib/hls/hls_parser.js
CHANGED
|
@@ -25,6 +25,7 @@ goog.require('shaka.net.DataUriPlugin');
|
|
|
25
25
|
goog.require('shaka.net.NetworkingEngine');
|
|
26
26
|
goog.require('shaka.util.ArrayUtils');
|
|
27
27
|
goog.require('shaka.util.BufferUtils');
|
|
28
|
+
goog.require('shaka.util.CmcdManager');
|
|
28
29
|
goog.require('shaka.util.DataViewReader');
|
|
29
30
|
goog.require('shaka.util.Error');
|
|
30
31
|
goog.require('shaka.util.FakeEvent');
|
|
@@ -332,7 +333,8 @@ shaka.hls.HlsParser = class {
|
|
|
332
333
|
const segments = await this.createSegments_(
|
|
333
334
|
streamInfo.verbatimMediaPlaylistUri, playlist, stream.type,
|
|
334
335
|
stream.mimeType, streamInfo.mediaSequenceToStartTime, mediaVariables,
|
|
335
|
-
streamInfo.discontinuityToMediaSequence
|
|
336
|
+
streamInfo.discontinuityToMediaSequence, stream.codecs,
|
|
337
|
+
stream.bandwidth);
|
|
336
338
|
|
|
337
339
|
stream.segmentIndex.mergeAndEvict(
|
|
338
340
|
segments, this.presentationTimeline_.getSegmentAvailabilityStart());
|
|
@@ -418,18 +420,16 @@ shaka.hls.HlsParser = class {
|
|
|
418
420
|
const uri = tag.getAttributeValue('URI');
|
|
419
421
|
const language = tag.getAttributeValue('LANGUAGE');
|
|
420
422
|
const value = tag.getAttributeValue('VALUE');
|
|
421
|
-
const data =
|
|
422
|
-
'id': id,
|
|
423
|
-
};
|
|
423
|
+
const data = (new Map()).set('id', id);
|
|
424
424
|
if (uri) {
|
|
425
|
-
data.uri
|
|
426
|
-
shaka.hls.Utils.constructAbsoluteUri(this.masterPlaylistUri_, uri);
|
|
425
|
+
data.set('uri',
|
|
426
|
+
shaka.hls.Utils.constructAbsoluteUri(this.masterPlaylistUri_, uri));
|
|
427
427
|
}
|
|
428
428
|
if (language) {
|
|
429
|
-
data.language
|
|
429
|
+
data.set('language', language);
|
|
430
430
|
}
|
|
431
431
|
if (value) {
|
|
432
|
-
data.value
|
|
432
|
+
data.set('value', value);
|
|
433
433
|
}
|
|
434
434
|
const event = new shaka.util.FakeEvent('sessiondata', data);
|
|
435
435
|
if (this.playerInterface_) {
|
|
@@ -534,6 +534,30 @@ shaka.hls.HlsParser = class {
|
|
|
534
534
|
}
|
|
535
535
|
}
|
|
536
536
|
|
|
537
|
+
// Now that the content has been fit, notify segments.
|
|
538
|
+
this.segmentsToNotifyByStream_ = [];
|
|
539
|
+
const streamsToNotify = [];
|
|
540
|
+
for (const variant of variants) {
|
|
541
|
+
for (const stream of [variant.video, variant.audio]) {
|
|
542
|
+
if (stream) {
|
|
543
|
+
streamsToNotify.push(stream);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
await Promise.all(streamsToNotify.map(async (stream) => {
|
|
548
|
+
await stream.createSegmentIndex();
|
|
549
|
+
}));
|
|
550
|
+
for (const stream of streamsToNotify) {
|
|
551
|
+
this.segmentsToNotifyByStream_.push(stream.segmentIndex.references);
|
|
552
|
+
}
|
|
553
|
+
this.notifySegments_();
|
|
554
|
+
|
|
555
|
+
// This asserts that the live edge is being calculated from segment times.
|
|
556
|
+
// For VOD and event streams, this check should still pass.
|
|
557
|
+
goog.asserts.assert(
|
|
558
|
+
!this.presentationTimeline_.usingPresentationStartTime(),
|
|
559
|
+
'We should not be using the presentation start time in HLS!');
|
|
560
|
+
|
|
537
561
|
this.manifest_ = {
|
|
538
562
|
presentationTimeline: this.presentationTimeline_,
|
|
539
563
|
variants,
|
|
@@ -741,7 +765,7 @@ shaka.hls.HlsParser = class {
|
|
|
741
765
|
const videoRange = tag.getAttributeValue('VIDEO-RANGE');
|
|
742
766
|
|
|
743
767
|
const streamInfos = await this.createStreamInfosForVariantTag_(tag,
|
|
744
|
-
resolution, frameRate);
|
|
768
|
+
resolution, frameRate, bandwidth);
|
|
745
769
|
|
|
746
770
|
if (streamInfos) {
|
|
747
771
|
goog.asserts.assert(streamInfos.audio.length ||
|
|
@@ -775,10 +799,11 @@ shaka.hls.HlsParser = class {
|
|
|
775
799
|
* @param {!shaka.hls.Tag} tag
|
|
776
800
|
* @param {?string} resolution
|
|
777
801
|
* @param {?string} frameRate
|
|
802
|
+
* @param {number} bandwidth
|
|
778
803
|
* @return {!Promise.<?shaka.hls.HlsParser.StreamInfos>}
|
|
779
804
|
* @private
|
|
780
805
|
*/
|
|
781
|
-
async createStreamInfosForVariantTag_(tag, resolution, frameRate) {
|
|
806
|
+
async createStreamInfosForVariantTag_(tag, resolution, frameRate, bandwidth) {
|
|
782
807
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
783
808
|
/** @type {!Array.<string>} */
|
|
784
809
|
let allCodecs = this.getCodecsForVariantTag_(tag);
|
|
@@ -848,7 +873,8 @@ shaka.hls.HlsParser = class {
|
|
|
848
873
|
let streamInfo;
|
|
849
874
|
if (!ignoreStream) {
|
|
850
875
|
streamInfo =
|
|
851
|
-
await this.createStreamInfoFromVariantTag_(tag, allCodecs, type
|
|
876
|
+
await this.createStreamInfoFromVariantTag_(tag, allCodecs, type,
|
|
877
|
+
bandwidth);
|
|
852
878
|
}
|
|
853
879
|
if (streamInfo) {
|
|
854
880
|
res[streamInfo.stream.type] = [streamInfo];
|
|
@@ -871,15 +897,12 @@ shaka.hls.HlsParser = class {
|
|
|
871
897
|
*/
|
|
872
898
|
getCodecsForVariantTag_(tag) {
|
|
873
899
|
// These are the default codecs to assume if none are specified.
|
|
874
|
-
// The video codec is H.264, with baseline profile and level 3.0.
|
|
875
|
-
// http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html
|
|
876
|
-
// The audio codec is "low-complexity" AAC.
|
|
877
900
|
const defaultCodecsArray = [];
|
|
878
901
|
if (!this.config_.disableVideo) {
|
|
879
|
-
defaultCodecsArray.push(
|
|
902
|
+
defaultCodecsArray.push(this.config_.hls.defaultVideoCodec);
|
|
880
903
|
}
|
|
881
904
|
if (!this.config_.disableAudio) {
|
|
882
|
-
defaultCodecsArray.push(
|
|
905
|
+
defaultCodecsArray.push(this.config_.hls.defaultAudioCodec);
|
|
883
906
|
}
|
|
884
907
|
const defaultCodecs = defaultCodecsArray.join(',');
|
|
885
908
|
|
|
@@ -1184,7 +1207,7 @@ shaka.hls.HlsParser = class {
|
|
|
1184
1207
|
// "AUTOSELECT=YES". A value of "AUTOSELECT=NO" would imply that it may
|
|
1185
1208
|
// only be selected explicitly by the user, and we don't have a way to
|
|
1186
1209
|
// represent that in our model.
|
|
1187
|
-
const defaultAttrValue = tag.
|
|
1210
|
+
const defaultAttrValue = tag.getAttributeValue('DEFAULT');
|
|
1188
1211
|
const primary = defaultAttrValue == 'YES';
|
|
1189
1212
|
|
|
1190
1213
|
const channelsCount = type == 'audio' ? this.getChannelsCount_(tag) : null;
|
|
@@ -1258,6 +1281,30 @@ shaka.hls.HlsParser = class {
|
|
|
1258
1281
|
if (this.uriToStreamInfosMap_.has(verbatimImagePlaylistUri)) {
|
|
1259
1282
|
return this.uriToStreamInfosMap_.get(verbatimImagePlaylistUri);
|
|
1260
1283
|
}
|
|
1284
|
+
|
|
1285
|
+
// Parse misc attributes.
|
|
1286
|
+
const resolution = tag.getAttributeValue('RESOLUTION');
|
|
1287
|
+
if (resolution) {
|
|
1288
|
+
// The RESOLUTION tag represents the resolution of a single thumbnail, not
|
|
1289
|
+
// of the entire sheet at once (like we expect in the output).
|
|
1290
|
+
// So multiply by the layout size.
|
|
1291
|
+
|
|
1292
|
+
const reference = streamInfo.stream.segmentIndex.get(0);
|
|
1293
|
+
const layout = reference.getTilesLayout();
|
|
1294
|
+
if (layout) {
|
|
1295
|
+
streamInfo.stream.width =
|
|
1296
|
+
Number(resolution.split('x')[0]) * Number(layout.split('x')[0]);
|
|
1297
|
+
streamInfo.stream.height =
|
|
1298
|
+
Number(resolution.split('x')[1]) * Number(layout.split('x')[1]);
|
|
1299
|
+
// TODO: What happens if there are multiple grids, with different
|
|
1300
|
+
// layout sizes, inside this image stream?
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
const bandwidth = tag.getAttributeValue('BANDWIDTH');
|
|
1304
|
+
if (bandwidth) {
|
|
1305
|
+
streamInfo.stream.bandwidth = Number(bandwidth);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1261
1308
|
this.uriToStreamInfosMap_.set(verbatimImagePlaylistUri, streamInfo);
|
|
1262
1309
|
return streamInfo;
|
|
1263
1310
|
}
|
|
@@ -1268,10 +1315,11 @@ shaka.hls.HlsParser = class {
|
|
|
1268
1315
|
* @param {!shaka.hls.Tag} tag
|
|
1269
1316
|
* @param {!Array.<string>} allCodecs
|
|
1270
1317
|
* @param {string} type
|
|
1318
|
+
* @param {number} bandwidth
|
|
1271
1319
|
* @return {!Promise.<?shaka.hls.HlsParser.StreamInfo>}
|
|
1272
1320
|
* @private
|
|
1273
1321
|
*/
|
|
1274
|
-
async createStreamInfoFromVariantTag_(tag, allCodecs, type) {
|
|
1322
|
+
async createStreamInfoFromVariantTag_(tag, allCodecs, type, bandwidth) {
|
|
1275
1323
|
goog.asserts.assert(tag.name == 'EXT-X-STREAM-INF',
|
|
1276
1324
|
'Should only be called on variant tags!');
|
|
1277
1325
|
const verbatimMediaPlaylistUri = this.variableSubstitution_(
|
|
@@ -1287,7 +1335,7 @@ shaka.hls.HlsParser = class {
|
|
|
1287
1335
|
codecs, type, /* language= */ 'und', /* primary= */ false,
|
|
1288
1336
|
/* name= */ null, /* channelcount= */ null, closedCaptions,
|
|
1289
1337
|
/* characteristics= */ null, /* forced= */ false,
|
|
1290
|
-
/* spatialAudio= */ false);
|
|
1338
|
+
/* spatialAudio= */ false, bandwidth);
|
|
1291
1339
|
if (streamInfo == null) {
|
|
1292
1340
|
return null;
|
|
1293
1341
|
}
|
|
@@ -1314,12 +1362,13 @@ shaka.hls.HlsParser = class {
|
|
|
1314
1362
|
* @param {?string} characteristics
|
|
1315
1363
|
* @param {boolean} forced
|
|
1316
1364
|
* @param {boolean} spatialAudio
|
|
1365
|
+
* @param {(number|undefined)} bandwidth
|
|
1317
1366
|
* @return {!Promise.<?shaka.hls.HlsParser.StreamInfo>}
|
|
1318
1367
|
* @private
|
|
1319
1368
|
*/
|
|
1320
1369
|
async createStreamInfo_(verbatimMediaPlaylistUri, codecs, type, language,
|
|
1321
1370
|
primary, name, channelsCount, closedCaptions, characteristics, forced,
|
|
1322
|
-
spatialAudio) {
|
|
1371
|
+
spatialAudio, bandwidth = undefined) {
|
|
1323
1372
|
// TODO: Refactor, too many parameters
|
|
1324
1373
|
let absoluteMediaPlaylistUri = shaka.hls.Utils.constructAbsoluteUri(
|
|
1325
1374
|
this.masterPlaylistUri_, verbatimMediaPlaylistUri);
|
|
@@ -1437,7 +1486,7 @@ shaka.hls.HlsParser = class {
|
|
|
1437
1486
|
try {
|
|
1438
1487
|
segments = await this.createSegments_(verbatimMediaPlaylistUri,
|
|
1439
1488
|
playlist, type, mimeType, mediaSequenceToStartTime, mediaVariables,
|
|
1440
|
-
discontinuityToMediaSequence);
|
|
1489
|
+
discontinuityToMediaSequence, codecs, bandwidth);
|
|
1441
1490
|
} catch (error) {
|
|
1442
1491
|
if (error.code == shaka.util.Error.Code.HLS_INTERNAL_SKIP_STREAM) {
|
|
1443
1492
|
shaka.log.alwaysWarn('Skipping unsupported HLS stream',
|
|
@@ -1617,12 +1666,6 @@ shaka.hls.HlsParser = class {
|
|
|
1617
1666
|
}
|
|
1618
1667
|
|
|
1619
1668
|
this.notifySegments_();
|
|
1620
|
-
|
|
1621
|
-
// This asserts that the live edge is being calculated from segment times.
|
|
1622
|
-
// For VOD and event streams, this check should still pass.
|
|
1623
|
-
goog.asserts.assert(
|
|
1624
|
-
!this.presentationTimeline_.usingPresentationStartTime(),
|
|
1625
|
-
'We should not be using the presentation start time in HLS!');
|
|
1626
1669
|
}
|
|
1627
1670
|
|
|
1628
1671
|
/**
|
|
@@ -1795,6 +1838,7 @@ shaka.hls.HlsParser = class {
|
|
|
1795
1838
|
}
|
|
1796
1839
|
|
|
1797
1840
|
let tilesLayout = '';
|
|
1841
|
+
let tileDuration = null;
|
|
1798
1842
|
if (type == shaka.util.ManifestParserUtils.ContentType.IMAGE) {
|
|
1799
1843
|
// By default in HLS the tilesLayout is 1x1
|
|
1800
1844
|
tilesLayout = '1x1';
|
|
@@ -1802,6 +1846,10 @@ shaka.hls.HlsParser = class {
|
|
|
1802
1846
|
shaka.hls.Utils.getFirstTagWithName(tags, 'EXT-X-TILES');
|
|
1803
1847
|
if (tilesTag) {
|
|
1804
1848
|
tilesLayout = tilesTag.getRequiredAttrValue('LAYOUT');
|
|
1849
|
+
const duration = tilesTag.getAttributeValue('DURATION');
|
|
1850
|
+
if (duration) {
|
|
1851
|
+
tileDuration = Number(duration);
|
|
1852
|
+
}
|
|
1805
1853
|
}
|
|
1806
1854
|
}
|
|
1807
1855
|
|
|
@@ -1817,6 +1865,7 @@ shaka.hls.HlsParser = class {
|
|
|
1817
1865
|
/* appendWindowEnd= */ Infinity,
|
|
1818
1866
|
partialSegmentRefs,
|
|
1819
1867
|
tilesLayout,
|
|
1868
|
+
tileDuration,
|
|
1820
1869
|
);
|
|
1821
1870
|
}
|
|
1822
1871
|
|
|
@@ -1871,11 +1920,14 @@ shaka.hls.HlsParser = class {
|
|
|
1871
1920
|
* @param {!Map.<number, number>} mediaSequenceToStartTime
|
|
1872
1921
|
* @param {!Map.<string, string>} variables
|
|
1873
1922
|
* @param {!Map.<number, number>} discontinuityToMediaSequence
|
|
1923
|
+
* @param {string} codecs
|
|
1924
|
+
* @param {(number|undefined)} bandwidth
|
|
1874
1925
|
* @return {!Promise<!Array.<!shaka.media.SegmentReference>>}
|
|
1875
1926
|
* @private
|
|
1876
1927
|
*/
|
|
1877
1928
|
async createSegments_(verbatimMediaPlaylistUri, playlist, type, mimeType,
|
|
1878
|
-
mediaSequenceToStartTime, variables, discontinuityToMediaSequence
|
|
1929
|
+
mediaSequenceToStartTime, variables, discontinuityToMediaSequence,
|
|
1930
|
+
codecs, bandwidth) {
|
|
1879
1931
|
/** @type {Array.<!shaka.hls.Segment>} */
|
|
1880
1932
|
const hlsSegments = playlist.segments;
|
|
1881
1933
|
goog.asserts.assert(hlsSegments.length, 'Playlist should have segments!');
|
|
@@ -1918,7 +1970,7 @@ shaka.hls.HlsParser = class {
|
|
|
1918
1970
|
this.playlistStartTime_ = await this.getStartTime_(
|
|
1919
1971
|
verbatimMediaPlaylistUri, initSegmentRef, mimeType,
|
|
1920
1972
|
position, /* isDiscontinuity= */ false,
|
|
1921
|
-
hlsSegments[0], variables, type);
|
|
1973
|
+
hlsSegments[0], variables, type, codecs, bandwidth);
|
|
1922
1974
|
}
|
|
1923
1975
|
firstStartTime = this.playlistStartTime_;
|
|
1924
1976
|
}
|
|
@@ -1971,7 +2023,8 @@ shaka.hls.HlsParser = class {
|
|
|
1971
2023
|
// eslint-disable-next-line no-await-in-loop
|
|
1972
2024
|
timestampOffset = await this.getTimestampOffset_(
|
|
1973
2025
|
discontintuitySequenceNum, verbatimMediaPlaylistUri, initSegmentRef,
|
|
1974
|
-
mimeType, position, item, variables, startTime, type
|
|
2026
|
+
mimeType, position, item, variables, startTime, type, codecs,
|
|
2027
|
+
bandwidth);
|
|
1975
2028
|
}
|
|
1976
2029
|
|
|
1977
2030
|
// If the stream is low latency and the user has not configured the
|
|
@@ -2009,9 +2062,6 @@ shaka.hls.HlsParser = class {
|
|
|
2009
2062
|
}
|
|
2010
2063
|
}
|
|
2011
2064
|
|
|
2012
|
-
this.segmentsToNotifyByStream_.push(references);
|
|
2013
|
-
this.notifySegments_();
|
|
2014
|
-
|
|
2015
2065
|
return references;
|
|
2016
2066
|
}
|
|
2017
2067
|
|
|
@@ -2028,13 +2078,16 @@ shaka.hls.HlsParser = class {
|
|
|
2028
2078
|
* @param {!Map.<string, string>} variables
|
|
2029
2079
|
* @param {number} startTime
|
|
2030
2080
|
* @param {string} type
|
|
2081
|
+
* @param {string} codecs
|
|
2082
|
+
* @param {number|undefined} bandwidth
|
|
2031
2083
|
* @return {!Promise.<number>}
|
|
2032
2084
|
* @throws {shaka.util.Error}
|
|
2033
2085
|
* @private
|
|
2034
2086
|
*/
|
|
2035
2087
|
async getTimestampOffset_(discontintuitySequenceNum,
|
|
2036
2088
|
verbatimMediaPlaylistUri, initSegmentRef,
|
|
2037
|
-
mimeType, mediaSequenceNumber, segment, variables, startTime, type
|
|
2089
|
+
mimeType, mediaSequenceNumber, segment, variables, startTime, type,
|
|
2090
|
+
codecs, bandwidth) {
|
|
2038
2091
|
let timestampOffset = 0;
|
|
2039
2092
|
if (this.discontinuityToTso_.has(discontintuitySequenceNum)) {
|
|
2040
2093
|
timestampOffset =
|
|
@@ -2043,7 +2096,7 @@ shaka.hls.HlsParser = class {
|
|
|
2043
2096
|
const mediaStartTime = await this.getStartTime_(
|
|
2044
2097
|
verbatimMediaPlaylistUri, initSegmentRef, mimeType,
|
|
2045
2098
|
mediaSequenceNumber, /* isDiscontinuity= */ true, segment,
|
|
2046
|
-
variables, type);
|
|
2099
|
+
variables, type, codecs, bandwidth);
|
|
2047
2100
|
timestampOffset = startTime - mediaStartTime;
|
|
2048
2101
|
shaka.log.v1('Segment timestampOffset =', timestampOffset);
|
|
2049
2102
|
this.discontinuityToTso_.set(
|
|
@@ -2057,10 +2110,14 @@ shaka.hls.HlsParser = class {
|
|
|
2057
2110
|
* segment if we have to.
|
|
2058
2111
|
*
|
|
2059
2112
|
* @param {!shaka.media.AnySegmentReference} reference
|
|
2113
|
+
* @param {string} type
|
|
2114
|
+
* @param {string} mimeType
|
|
2115
|
+
* @param {string} codecs
|
|
2116
|
+
* @param {number|undefined} bandwidth
|
|
2060
2117
|
* @return {!Promise.<shaka.extern.Response>}
|
|
2061
2118
|
* @private
|
|
2062
2119
|
*/
|
|
2063
|
-
async fetchStartOfSegment_(reference) {
|
|
2120
|
+
async fetchStartOfSegment_(reference, type, mimeType, codecs, bandwidth) {
|
|
2064
2121
|
const requestType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
|
|
2065
2122
|
|
|
2066
2123
|
// Create two requests:
|
|
@@ -2074,6 +2131,20 @@ shaka.hls.HlsParser = class {
|
|
|
2074
2131
|
reference.endByte,
|
|
2075
2132
|
this.config_.retryParameters);
|
|
2076
2133
|
|
|
2134
|
+
// We can only add partial CMCD data here because the stream
|
|
2135
|
+
// and manifest objects are still being created
|
|
2136
|
+
this.playerInterface_.modifySegmentRequest(
|
|
2137
|
+
fullRequest,
|
|
2138
|
+
{
|
|
2139
|
+
type: type,
|
|
2140
|
+
init: reference instanceof shaka.media.InitSegmentReference,
|
|
2141
|
+
duration: reference.endTime - reference.startTime,
|
|
2142
|
+
mimeType: mimeType,
|
|
2143
|
+
codecs: codecs,
|
|
2144
|
+
bandwidth: bandwidth,
|
|
2145
|
+
},
|
|
2146
|
+
);
|
|
2147
|
+
|
|
2077
2148
|
if (this.config_.hls.useFullSegmentsForStartTime) {
|
|
2078
2149
|
return this.makeNetworkRequest_(fullRequest, requestType);
|
|
2079
2150
|
}
|
|
@@ -2084,6 +2155,18 @@ shaka.hls.HlsParser = class {
|
|
|
2084
2155
|
reference.startByte + shaka.hls.HlsParser.START_OF_SEGMENT_SIZE_ - 1,
|
|
2085
2156
|
this.config_.retryParameters);
|
|
2086
2157
|
|
|
2158
|
+
this.playerInterface_.modifySegmentRequest(
|
|
2159
|
+
partialRequest,
|
|
2160
|
+
{
|
|
2161
|
+
type: type,
|
|
2162
|
+
init: reference instanceof shaka.media.InitSegmentReference,
|
|
2163
|
+
duration: reference.endTime - reference.startTime,
|
|
2164
|
+
mimeType: mimeType,
|
|
2165
|
+
codecs: codecs,
|
|
2166
|
+
bandwidth: bandwidth,
|
|
2167
|
+
},
|
|
2168
|
+
);
|
|
2169
|
+
|
|
2087
2170
|
// TODO(vaage): The need to do fall back requests is not likely to be unique
|
|
2088
2171
|
// to here. It would be nice if the fallback(s) could be included into
|
|
2089
2172
|
// the same abortable operation as the original request.
|
|
@@ -2131,12 +2214,14 @@ shaka.hls.HlsParser = class {
|
|
|
2131
2214
|
* @param {!shaka.hls.Segment} segment
|
|
2132
2215
|
* @param {!Map.<string, string>} variables
|
|
2133
2216
|
* @param {string} type
|
|
2217
|
+
* @param {string} codecs
|
|
2218
|
+
* @param {number|undefined} bandwidth
|
|
2134
2219
|
* @return {!Promise.<number>}
|
|
2135
2220
|
* @private
|
|
2136
2221
|
*/
|
|
2137
2222
|
async getStartTime_(
|
|
2138
2223
|
verbatimMediaPlaylistUri, initSegmentRef, mimeType, mediaSequenceNumber,
|
|
2139
|
-
isDiscontinuity, segment, variables, type) {
|
|
2224
|
+
isDiscontinuity, segment, variables, type, codecs, bandwidth) {
|
|
2140
2225
|
const segmentRef = this.createSegmentReference_(
|
|
2141
2226
|
initSegmentRef,
|
|
2142
2227
|
/* previousReference= */ null,
|
|
@@ -2204,10 +2289,22 @@ shaka.hls.HlsParser = class {
|
|
|
2204
2289
|
if (mimeType == 'video/mp4' || mimeType == 'audio/mp4') {
|
|
2205
2290
|
// We also need the init segment to get the correct timescale. But if the
|
|
2206
2291
|
// stream is self-initializing, use the same response for both.
|
|
2207
|
-
const fetches = [this.fetchStartOfSegment_(
|
|
2292
|
+
const fetches = [this.fetchStartOfSegment_(
|
|
2293
|
+
segmentRef,
|
|
2294
|
+
type,
|
|
2295
|
+
mimeType,
|
|
2296
|
+
codecs,
|
|
2297
|
+
bandwidth,
|
|
2298
|
+
)];
|
|
2208
2299
|
|
|
2209
2300
|
if (initSegmentRef) {
|
|
2210
|
-
fetches.push(this.fetchStartOfSegment_(
|
|
2301
|
+
fetches.push(this.fetchStartOfSegment_(
|
|
2302
|
+
initSegmentRef,
|
|
2303
|
+
type,
|
|
2304
|
+
mimeType,
|
|
2305
|
+
codecs,
|
|
2306
|
+
bandwidth,
|
|
2307
|
+
));
|
|
2211
2308
|
}
|
|
2212
2309
|
|
|
2213
2310
|
const responses = await Promise.all(fetches);
|
|
@@ -2223,7 +2320,13 @@ shaka.hls.HlsParser = class {
|
|
|
2223
2320
|
}
|
|
2224
2321
|
|
|
2225
2322
|
if (mimeType == 'video/mp2t') {
|
|
2226
|
-
const response = await this.fetchStartOfSegment_(
|
|
2323
|
+
const response = await this.fetchStartOfSegment_(
|
|
2324
|
+
segmentRef,
|
|
2325
|
+
type,
|
|
2326
|
+
mimeType,
|
|
2327
|
+
codecs,
|
|
2328
|
+
bandwidth,
|
|
2329
|
+
);
|
|
2227
2330
|
goog.asserts.assert(response.data, 'Should have a response body!');
|
|
2228
2331
|
return this.getStartTimeFromTsSegment_(
|
|
2229
2332
|
verbatimMediaPlaylistUri, response.uri, response.data);
|
|
@@ -2601,6 +2704,9 @@ shaka.hls.HlsParser = class {
|
|
|
2601
2704
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
2602
2705
|
[absoluteUri], this.config_.retryParameters);
|
|
2603
2706
|
|
|
2707
|
+
const format = shaka.util.CmcdManager.StreamingFormat.HLS;
|
|
2708
|
+
this.playerInterface_.modifyManifestRequest(request, {format: format});
|
|
2709
|
+
|
|
2604
2710
|
return this.makeNetworkRequest_(request, requestType);
|
|
2605
2711
|
}
|
|
2606
2712
|
|
package/lib/media/drm_engine.js
CHANGED
|
@@ -1825,9 +1825,16 @@ shaka.media.DrmEngine = class {
|
|
|
1825
1825
|
// audio adaptations, so we shouldn't have to worry about checking
|
|
1826
1826
|
// robustness.
|
|
1827
1827
|
if (drm1.keySystem == drm2.keySystem) {
|
|
1828
|
+
/** @type {Array<shaka.extern.InitDataOverride>} */
|
|
1828
1829
|
let initData = [];
|
|
1829
1830
|
initData = initData.concat(drm1.initData || []);
|
|
1830
1831
|
initData = initData.concat(drm2.initData || []);
|
|
1832
|
+
initData = initData.filter((d, i) => {
|
|
1833
|
+
return d.keyId === undefined || i === initData.findIndex((d2) => {
|
|
1834
|
+
return d2.keyId === d.keyId;
|
|
1835
|
+
});
|
|
1836
|
+
});
|
|
1837
|
+
|
|
1831
1838
|
const keyIds = drm1.keyIds && drm2.keyIds ?
|
|
1832
1839
|
new Set([...drm1.keyIds, ...drm2.keyIds]) :
|
|
1833
1840
|
drm1.keyIds || drm2.keyIds;
|
|
@@ -217,8 +217,10 @@ shaka.media.GapJumpingController = class {
|
|
|
217
217
|
this.didFireLargeGap_ = true;
|
|
218
218
|
|
|
219
219
|
// Event firing is synchronous.
|
|
220
|
-
const
|
|
221
|
-
'
|
|
220
|
+
const data = new Map()
|
|
221
|
+
.set('currentTime', currentTime)
|
|
222
|
+
.set('gapSize', jumpSize);
|
|
223
|
+
const event = new shaka.util.FakeEvent('largegap', data);
|
|
222
224
|
event.cancelable = true;
|
|
223
225
|
this.onEvent_(event);
|
|
224
226
|
|
|
@@ -48,8 +48,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
48
48
|
* @param {!function(!Array.<shaka.extern.ID3Metadata>, number, ?number)=}
|
|
49
49
|
* onMetadata
|
|
50
50
|
*/
|
|
51
|
-
constructor(video, closedCaptionParser, textDisplayer,
|
|
52
|
-
onMetadata) {
|
|
51
|
+
constructor(video, closedCaptionParser, textDisplayer, onMetadata) {
|
|
53
52
|
/** @private {HTMLMediaElement} */
|
|
54
53
|
this.video_ = video;
|
|
55
54
|
|