shaka-player 3.1.4 → 3.3.0
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 +6 -5
- package/CHANGELOG.md +363 -0
- package/CONTRIBUTORS +4 -2
- package/README.md +4 -4
- package/build/types/core +3 -0
- package/build/types/offline +1 -0
- package/build/types/polyfill +2 -0
- package/build/types/ui +2 -0
- package/demo/common/asset.js +9 -7
- package/demo/common/assets.js +28 -1
- package/demo/common/message_ids.js +15 -2
- package/demo/config.js +35 -2
- package/demo/index.html +1 -1
- package/demo/locales/en.json +14 -1
- package/demo/locales/source.json +56 -4
- package/demo/main.js +2 -1
- package/dist/controls.css +26 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +24 -21
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +24 -21
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +33 -25
- package/dist/locales.js +31 -29
- package/dist/receiver.compiled.debug.js +14 -12
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +14 -12
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +258 -20
- package/dist/shaka-player.compiled.debug.d.ts +258 -20
- package/dist/shaka-player.compiled.debug.externs.js +1005 -695
- package/dist/shaka-player.compiled.debug.js +720 -640
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +1005 -695
- package/dist/shaka-player.compiled.js +850 -817
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +274 -21
- package/dist/shaka-player.ui.debug.d.ts +274 -21
- package/dist/shaka-player.ui.debug.externs.js +766 -429
- package/dist/shaka-player.ui.debug.js +863 -765
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +766 -429
- package/dist/shaka-player.ui.js +1042 -996
- 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/index.json +1 -0
- package/docs/tutorials/ui-customization.md +66 -0
- package/docs/tutorials/ui.md +5 -5
- package/externs/aria.js +6 -0
- package/externs/ima.js +3 -0
- package/externs/shaka/ads.js +29 -1
- package/externs/shaka/manifest.js +18 -2
- package/externs/shaka/manifest_parser.js +10 -1
- package/externs/shaka/net.js +20 -4
- package/externs/shaka/offline.js +36 -2
- package/externs/shaka/player.js +182 -13
- package/externs/shaka/text.js +24 -10
- package/externs/texttrack.js +3 -0
- package/externs/webcrypto.js +18 -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/ad_manager.js +16 -15
- package/lib/ads/client_side_ad.js +8 -0
- package/lib/ads/client_side_ad_manager.js +14 -6
- package/lib/ads/server_side_ad.js +7 -0
- package/lib/ads/server_side_ad_manager.js +19 -2
- package/lib/cast/cast_proxy.js +8 -0
- package/lib/cast/cast_utils.js +7 -1
- package/lib/cea/cea608_data_channel.js +56 -56
- package/lib/cea/cea608_memory.js +33 -33
- package/lib/cea/cea708_service.js +43 -43
- package/lib/cea/cea708_window.js +38 -38
- package/lib/cea/cea_decoder.js +13 -14
- package/lib/cea/cea_utils.js +23 -23
- package/lib/cea/dtvcc_packet_builder.js +7 -7
- package/lib/cea/i_caption_decoder.js +2 -3
- package/lib/cea/i_cea_parser.js +2 -2
- package/lib/cea/mp4_cea_parser.js +6 -6
- package/lib/dash/dash_parser.js +53 -14
- package/lib/dash/segment_base.js +25 -1
- package/lib/dash/segment_list.js +18 -9
- package/lib/dash/segment_template.js +23 -10
- package/lib/hls/hls_parser.js +210 -30
- package/lib/media/closed_caption_parser.js +1 -2
- package/lib/media/drm_engine.js +162 -192
- package/lib/media/quality_observer.js +291 -0
- package/lib/media/segment_index.js +9 -1
- package/lib/media/segment_reference.js +37 -5
- package/lib/media/streaming_engine.js +48 -10
- 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 +82 -18
- package/lib/offline/download_info.js +68 -0
- package/lib/offline/download_manager.js +14 -7
- package/lib/offline/indexeddb/base_storage_cell.js +35 -0
- package/lib/offline/indexeddb/storage_mechanism.js +1 -1
- package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v5_storage_cell.js +5 -0
- package/lib/offline/manifest_converter.js +3 -1
- package/lib/offline/storage.js +404 -297
- package/lib/offline/stored_content_utils.js +2 -0
- package/lib/player.js +622 -153
- package/lib/polyfill/aria.js +82 -0
- package/lib/polyfill/random_uuid.js +52 -0
- package/lib/routing/walker.js +2 -3
- package/lib/text/cue.js +12 -0
- package/lib/text/srt_text_parser.js +4 -4
- package/lib/text/text_engine.js +2 -2
- package/lib/text/ttml_text_parser.js +25 -2
- package/lib/text/ui_text_displayer.js +2 -2
- package/lib/text/vtt_text_parser.js +2 -2
- package/lib/text/web_vtt_generator.js +55 -12
- package/lib/util/cmcd_manager.js +826 -0
- package/lib/util/error.js +12 -0
- package/lib/util/language_utils.js +12 -0
- package/lib/util/manifest_parser_utils.js +1 -0
- package/lib/util/mp4_box_parsers.js +20 -20
- package/lib/util/multi_map.js +8 -0
- package/lib/util/mutex.js +52 -0
- package/lib/util/periods.js +98 -64
- package/lib/util/player_configuration.js +31 -1
- package/lib/util/stream_utils.js +140 -85
- package/lib/util/xml_utils.js +31 -12
- package/package.json +1 -1
- package/roadmap.md +24 -22
- package/shaka-player.uncompiled.js +4 -0
- package/test/cast/cast_utils_unit.js +1 -0
- package/test/cea/cea608_memory_unit.js +1 -1
- package/test/cea/cea708_service_unit.js +8 -8
- package/test/cea/cea708_window_unit.js +6 -6
- package/test/cea/cea_decoder_unit.js +1 -1
- package/test/cea/dtvcc_packet_builder_unit.js +2 -2
- 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 +78 -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 +252 -0
- package/test/media/drm_engine_integration.js +86 -12
- package/test/media/drm_engine_unit.js +310 -450
- 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 +2 -1
- package/test/media/streaming_engine_unit.js +2 -0
- package/test/net/http_plugin_unit.js +20 -15
- package/test/offline/manifest_convert_unit.js +2 -0
- package/test/offline/storage_integration.js +117 -23
- package/test/player_integration.js +151 -0
- package/test/player_src_equals_integration.js +52 -0
- package/test/player_unit.js +11 -33
- package/test/test/assets/chapters.srt +11 -0
- package/test/test/assets/chapters.vtt +10 -0
- package/test/test/util/cea_utils.js +12 -12
- 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 +7 -0
- package/test/test/util/fake_drm_engine.js +9 -2
- package/test/test/util/manifest_generator.js +5 -2
- package/test/test/util/manifest_parser_util.js +3 -1
- package/test/test/util/test_scheme.js +14 -1
- package/test/text/ttml_text_parser_unit.js +47 -0
- package/test/text/web_vtt_generator_unit.js +81 -7
- package/test/ui/ui_unit.js +250 -0
- package/test/util/cmcd_manager_unit.js +277 -0
- package/test/util/stream_utils_unit.js +118 -10
- package/ui/airplay_button.js +2 -1
- package/ui/audio_language_selection.js +9 -5
- package/ui/cast_button.js +5 -6
- package/ui/constants.js +0 -3
- package/ui/context_menu.js +109 -0
- package/ui/controls.js +41 -7
- 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 +16 -8
- package/ui/fullscreen_button.js +2 -3
- package/ui/language_utils.js +1 -1
- package/ui/less/buttons.less +0 -4
- package/ui/less/containers.less +74 -0
- 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/source.json +4 -0
- package/ui/loop_button.js +6 -3
- package/ui/mute_button.js +3 -4
- package/ui/overflow_menu.js +7 -28
- package/ui/pip_button.js +10 -7
- package/ui/play_button.js +2 -2
- package/ui/playback_rate_selection.js +11 -15
- package/ui/presentation_time.js +1 -3
- package/ui/resolution_selection.js +11 -7
- package/ui/rewind_button.js +17 -9
- package/ui/seek_bar.js +6 -6
- package/ui/settings_menu.js +36 -4
- package/ui/small_play_button.js +1 -0
- package/ui/spacer.js +1 -1
- package/ui/statistics_button.js +253 -0
- package/ui/text_selection.js +19 -15
- package/ui/ui.js +30 -0
- package/ui/ui_utils.js +1 -1
- package/ui/volume_bar.js +2 -4
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');
|
|
@@ -92,7 +93,8 @@ shaka.hls.HlsParser = class {
|
|
|
92
93
|
* timestamps, offsets, and to handle TS rollover.
|
|
93
94
|
*
|
|
94
95
|
* During parsing, used to avoid duplicates in the async methods
|
|
95
|
-
* createStreamInfoFromMediaTag_ and
|
|
96
|
+
* createStreamInfoFromMediaTag_, createStreamInfoFromImageTag_ and
|
|
97
|
+
* createStreamInfoFromVariantTag_.
|
|
96
98
|
*
|
|
97
99
|
* During parsing of updates, used by getStartTime_ to determine the start
|
|
98
100
|
* time of the first segment from existing segment references.
|
|
@@ -331,7 +333,8 @@ shaka.hls.HlsParser = class {
|
|
|
331
333
|
const segments = await this.createSegments_(
|
|
332
334
|
streamInfo.verbatimMediaPlaylistUri, playlist, stream.type,
|
|
333
335
|
stream.mimeType, streamInfo.mediaSequenceToStartTime, mediaVariables,
|
|
334
|
-
streamInfo.discontinuityToMediaSequence
|
|
336
|
+
streamInfo.discontinuityToMediaSequence, stream.codecs,
|
|
337
|
+
stream.bandwidth);
|
|
335
338
|
|
|
336
339
|
stream.segmentIndex.mergeAndEvict(
|
|
337
340
|
segments, this.presentationTimeline_.getSegmentAvailabilityStart());
|
|
@@ -403,6 +406,9 @@ shaka.hls.HlsParser = class {
|
|
|
403
406
|
/** @type {!Array.<!shaka.hls.Tag>} */
|
|
404
407
|
const variantTags = Utils.filterTagsByName(
|
|
405
408
|
playlist.tags, 'EXT-X-STREAM-INF');
|
|
409
|
+
/** @type {!Array.<!shaka.hls.Tag>} */
|
|
410
|
+
const imageTags = Utils.filterTagsByName(
|
|
411
|
+
playlist.tags, 'EXT-X-IMAGE-STREAM-INF');
|
|
406
412
|
|
|
407
413
|
this.parseCodecs_(variantTags);
|
|
408
414
|
|
|
@@ -437,6 +443,7 @@ shaka.hls.HlsParser = class {
|
|
|
437
443
|
this.parseClosedCaptions_(mediaTags);
|
|
438
444
|
const variants = await this.createVariantsForTags_(variantTags);
|
|
439
445
|
const textStreams = await this.parseTexts_(mediaTags);
|
|
446
|
+
const imageStreams = await this.parseImages_(imageTags);
|
|
440
447
|
|
|
441
448
|
// Make sure that the parser has not been destroyed.
|
|
442
449
|
if (!this.playerInterface_) {
|
|
@@ -555,7 +562,7 @@ shaka.hls.HlsParser = class {
|
|
|
555
562
|
presentationTimeline: this.presentationTimeline_,
|
|
556
563
|
variants,
|
|
557
564
|
textStreams,
|
|
558
|
-
imageStreams
|
|
565
|
+
imageStreams,
|
|
559
566
|
offlineSessionIds: [],
|
|
560
567
|
minBufferTime: 0,
|
|
561
568
|
};
|
|
@@ -688,6 +695,34 @@ shaka.hls.HlsParser = class {
|
|
|
688
695
|
return textStreams.filter((s) => s);
|
|
689
696
|
}
|
|
690
697
|
|
|
698
|
+
/**
|
|
699
|
+
* @param {!Array.<!shaka.hls.Tag>} imageTags from the playlist.
|
|
700
|
+
* @return {!Promise.<!Array.<!shaka.extern.Stream>>}
|
|
701
|
+
* @private
|
|
702
|
+
*/
|
|
703
|
+
async parseImages_(imageTags) {
|
|
704
|
+
// Create image stream for each image tag.
|
|
705
|
+
const imageStreamPromises = imageTags.map(async (tag) => {
|
|
706
|
+
const disableThumbnails = this.config_.disableThumbnails;
|
|
707
|
+
if (disableThumbnails) {
|
|
708
|
+
return null;
|
|
709
|
+
}
|
|
710
|
+
try {
|
|
711
|
+
const streamInfo = await this.createStreamInfoFromImageTag_(tag);
|
|
712
|
+
goog.asserts.assert(
|
|
713
|
+
streamInfo, 'Should always have a streamInfo for image');
|
|
714
|
+
return streamInfo.stream;
|
|
715
|
+
} catch (e) {
|
|
716
|
+
if (this.config_.hls.ignoreImageStreamFailures) {
|
|
717
|
+
return null;
|
|
718
|
+
}
|
|
719
|
+
throw e;
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
const imageStreams = await Promise.all(imageStreamPromises);
|
|
723
|
+
return imageStreams.filter((s) => s);
|
|
724
|
+
}
|
|
725
|
+
|
|
691
726
|
/**
|
|
692
727
|
* @param {!Array.<!shaka.hls.Tag>} mediaTags Media tags from the playlist.
|
|
693
728
|
* @private
|
|
@@ -730,7 +765,7 @@ shaka.hls.HlsParser = class {
|
|
|
730
765
|
const videoRange = tag.getAttributeValue('VIDEO-RANGE');
|
|
731
766
|
|
|
732
767
|
const streamInfos = await this.createStreamInfosForVariantTag_(tag,
|
|
733
|
-
resolution, frameRate);
|
|
768
|
+
resolution, frameRate, bandwidth);
|
|
734
769
|
|
|
735
770
|
if (streamInfos) {
|
|
736
771
|
goog.asserts.assert(streamInfos.audio.length ||
|
|
@@ -764,10 +799,11 @@ shaka.hls.HlsParser = class {
|
|
|
764
799
|
* @param {!shaka.hls.Tag} tag
|
|
765
800
|
* @param {?string} resolution
|
|
766
801
|
* @param {?string} frameRate
|
|
802
|
+
* @param {number} bandwidth
|
|
767
803
|
* @return {!Promise.<?shaka.hls.HlsParser.StreamInfos>}
|
|
768
804
|
* @private
|
|
769
805
|
*/
|
|
770
|
-
async createStreamInfosForVariantTag_(tag, resolution, frameRate) {
|
|
806
|
+
async createStreamInfosForVariantTag_(tag, resolution, frameRate, bandwidth) {
|
|
771
807
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
772
808
|
/** @type {!Array.<string>} */
|
|
773
809
|
let allCodecs = this.getCodecsForVariantTag_(tag);
|
|
@@ -837,7 +873,8 @@ shaka.hls.HlsParser = class {
|
|
|
837
873
|
let streamInfo;
|
|
838
874
|
if (!ignoreStream) {
|
|
839
875
|
streamInfo =
|
|
840
|
-
await this.createStreamInfoFromVariantTag_(tag, allCodecs, type
|
|
876
|
+
await this.createStreamInfoFromVariantTag_(tag, allCodecs, type,
|
|
877
|
+
bandwidth);
|
|
841
878
|
}
|
|
842
879
|
if (streamInfo) {
|
|
843
880
|
res[streamInfo.stream.type] = [streamInfo];
|
|
@@ -860,15 +897,12 @@ shaka.hls.HlsParser = class {
|
|
|
860
897
|
*/
|
|
861
898
|
getCodecsForVariantTag_(tag) {
|
|
862
899
|
// These are the default codecs to assume if none are specified.
|
|
863
|
-
// The video codec is H.264, with baseline profile and level 3.0.
|
|
864
|
-
// http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html
|
|
865
|
-
// The audio codec is "low-complexity" AAC.
|
|
866
900
|
const defaultCodecsArray = [];
|
|
867
901
|
if (!this.config_.disableVideo) {
|
|
868
|
-
defaultCodecsArray.push(
|
|
902
|
+
defaultCodecsArray.push(this.config_.hls.defaultVideoCodec);
|
|
869
903
|
}
|
|
870
904
|
if (!this.config_.disableAudio) {
|
|
871
|
-
defaultCodecsArray.push(
|
|
905
|
+
defaultCodecsArray.push(this.config_.hls.defaultAudioCodec);
|
|
872
906
|
}
|
|
873
907
|
const defaultCodecs = defaultCodecsArray.join(',');
|
|
874
908
|
|
|
@@ -1206,16 +1240,62 @@ shaka.hls.HlsParser = class {
|
|
|
1206
1240
|
return streamInfo;
|
|
1207
1241
|
}
|
|
1208
1242
|
|
|
1243
|
+
/**
|
|
1244
|
+
* Parse EXT-X-MEDIA media tag into a Stream object.
|
|
1245
|
+
*
|
|
1246
|
+
* @param {shaka.hls.Tag} tag
|
|
1247
|
+
* @return {!Promise.<?shaka.hls.HlsParser.StreamInfo>}
|
|
1248
|
+
* @private
|
|
1249
|
+
*/
|
|
1250
|
+
async createStreamInfoFromImageTag_(tag) {
|
|
1251
|
+
goog.asserts.assert(tag.name == 'EXT-X-IMAGE-STREAM-INF',
|
|
1252
|
+
'Should only be called on image tags!');
|
|
1253
|
+
/** @type {string} */
|
|
1254
|
+
const type = shaka.util.ManifestParserUtils.ContentType.IMAGE;
|
|
1255
|
+
|
|
1256
|
+
const verbatimImagePlaylistUri = this.variableSubstitution_(
|
|
1257
|
+
tag.getRequiredAttrValue('URI'), this.globalVariables_);
|
|
1258
|
+
const codecs = tag.getAttributeValue('CODECS', 'jpeg') || '';
|
|
1259
|
+
|
|
1260
|
+
// Check if the stream has already been created as part of another Variant
|
|
1261
|
+
// and return it if it has.
|
|
1262
|
+
if (this.uriToStreamInfosMap_.has(verbatimImagePlaylistUri)) {
|
|
1263
|
+
return this.uriToStreamInfosMap_.get(verbatimImagePlaylistUri);
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
const language = this.getLanguage_(tag);
|
|
1267
|
+
const name = tag.getAttributeValue('NAME');
|
|
1268
|
+
|
|
1269
|
+
const characteristics = tag.getAttributeValue('CHARACTERISTICS');
|
|
1270
|
+
|
|
1271
|
+
const streamInfo = await this.createStreamInfo_(
|
|
1272
|
+
verbatimImagePlaylistUri, codecs, type, language, /* primary= */ false,
|
|
1273
|
+
name, /* channelsCount= */ null, /* closedCaptions= */ null,
|
|
1274
|
+
characteristics, /* forced= */ false, /* spatialAudio= */ false);
|
|
1275
|
+
if (streamInfo == null) {
|
|
1276
|
+
return null;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// TODO: This check is necessary because of the possibility of multiple
|
|
1280
|
+
// calls to createStreamInfoFromImageTag_ before either has resolved.
|
|
1281
|
+
if (this.uriToStreamInfosMap_.has(verbatimImagePlaylistUri)) {
|
|
1282
|
+
return this.uriToStreamInfosMap_.get(verbatimImagePlaylistUri);
|
|
1283
|
+
}
|
|
1284
|
+
this.uriToStreamInfosMap_.set(verbatimImagePlaylistUri, streamInfo);
|
|
1285
|
+
return streamInfo;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1209
1288
|
/**
|
|
1210
1289
|
* Parse an EXT-X-STREAM-INF media tag into a Stream object.
|
|
1211
1290
|
*
|
|
1212
1291
|
* @param {!shaka.hls.Tag} tag
|
|
1213
1292
|
* @param {!Array.<string>} allCodecs
|
|
1214
1293
|
* @param {string} type
|
|
1294
|
+
* @param {number} bandwidth
|
|
1215
1295
|
* @return {!Promise.<?shaka.hls.HlsParser.StreamInfo>}
|
|
1216
1296
|
* @private
|
|
1217
1297
|
*/
|
|
1218
|
-
async createStreamInfoFromVariantTag_(tag, allCodecs, type) {
|
|
1298
|
+
async createStreamInfoFromVariantTag_(tag, allCodecs, type, bandwidth) {
|
|
1219
1299
|
goog.asserts.assert(tag.name == 'EXT-X-STREAM-INF',
|
|
1220
1300
|
'Should only be called on variant tags!');
|
|
1221
1301
|
const verbatimMediaPlaylistUri = this.variableSubstitution_(
|
|
@@ -1231,7 +1311,7 @@ shaka.hls.HlsParser = class {
|
|
|
1231
1311
|
codecs, type, /* language= */ 'und', /* primary= */ false,
|
|
1232
1312
|
/* name= */ null, /* channelcount= */ null, closedCaptions,
|
|
1233
1313
|
/* characteristics= */ null, /* forced= */ false,
|
|
1234
|
-
/* spatialAudio= */ false);
|
|
1314
|
+
/* spatialAudio= */ false, bandwidth);
|
|
1235
1315
|
if (streamInfo == null) {
|
|
1236
1316
|
return null;
|
|
1237
1317
|
}
|
|
@@ -1258,12 +1338,13 @@ shaka.hls.HlsParser = class {
|
|
|
1258
1338
|
* @param {?string} characteristics
|
|
1259
1339
|
* @param {boolean} forced
|
|
1260
1340
|
* @param {boolean} spatialAudio
|
|
1341
|
+
* @param {(number|undefined)} bandwidth
|
|
1261
1342
|
* @return {!Promise.<?shaka.hls.HlsParser.StreamInfo>}
|
|
1262
1343
|
* @private
|
|
1263
1344
|
*/
|
|
1264
1345
|
async createStreamInfo_(verbatimMediaPlaylistUri, codecs, type, language,
|
|
1265
1346
|
primary, name, channelsCount, closedCaptions, characteristics, forced,
|
|
1266
|
-
spatialAudio) {
|
|
1347
|
+
spatialAudio, bandwidth = undefined) {
|
|
1267
1348
|
// TODO: Refactor, too many parameters
|
|
1268
1349
|
let absoluteMediaPlaylistUri = shaka.hls.Utils.constructAbsoluteUri(
|
|
1269
1350
|
this.masterPlaylistUri_, verbatimMediaPlaylistUri);
|
|
@@ -1278,7 +1359,8 @@ shaka.hls.HlsParser = class {
|
|
|
1278
1359
|
response.data, absoluteMediaPlaylistUri);
|
|
1279
1360
|
|
|
1280
1361
|
if (playlist.type != shaka.hls.PlaylistType.MEDIA) {
|
|
1281
|
-
// EXT-X-MEDIA tags should point to media
|
|
1362
|
+
// EXT-X-MEDIA and EXT-X-IMAGE-STREAM-INF tags should point to media
|
|
1363
|
+
// playlists.
|
|
1282
1364
|
throw new shaka.util.Error(
|
|
1283
1365
|
shaka.util.Error.Severity.CRITICAL,
|
|
1284
1366
|
shaka.util.Error.Category.MANIFEST,
|
|
@@ -1380,7 +1462,7 @@ shaka.hls.HlsParser = class {
|
|
|
1380
1462
|
try {
|
|
1381
1463
|
segments = await this.createSegments_(verbatimMediaPlaylistUri,
|
|
1382
1464
|
playlist, type, mimeType, mediaSequenceToStartTime, mediaVariables,
|
|
1383
|
-
discontinuityToMediaSequence);
|
|
1465
|
+
discontinuityToMediaSequence, codecs, bandwidth);
|
|
1384
1466
|
} catch (error) {
|
|
1385
1467
|
if (error.code == shaka.util.Error.Code.HLS_INTERNAL_SKIP_STREAM) {
|
|
1386
1468
|
shaka.log.alwaysWarn('Skipping unsupported HLS stream',
|
|
@@ -1635,11 +1717,12 @@ shaka.hls.HlsParser = class {
|
|
|
1635
1717
|
* @param {!Map.<string, string>} variables
|
|
1636
1718
|
* @param {string} absoluteMediaPlaylistUri
|
|
1637
1719
|
* @return {!shaka.media.SegmentReference}
|
|
1720
|
+
* @param {string} type
|
|
1638
1721
|
* @private
|
|
1639
1722
|
*/
|
|
1640
1723
|
createSegmentReference_(
|
|
1641
1724
|
initSegmentReference, previousReference, hlsSegment, startTime,
|
|
1642
|
-
timestampOffset, variables, absoluteMediaPlaylistUri) {
|
|
1725
|
+
timestampOffset, variables, absoluteMediaPlaylistUri, type) {
|
|
1643
1726
|
const tags = hlsSegment.tags;
|
|
1644
1727
|
const absoluteSegmentUri = this.variableSubstitution_(
|
|
1645
1728
|
hlsSegment.absoluteUri, variables);
|
|
@@ -1730,6 +1813,17 @@ shaka.hls.HlsParser = class {
|
|
|
1730
1813
|
endByte = partialSegmentRefs[partialSegmentRefs.length - 1].endByte;
|
|
1731
1814
|
}
|
|
1732
1815
|
|
|
1816
|
+
let tilesLayout = '';
|
|
1817
|
+
if (type == shaka.util.ManifestParserUtils.ContentType.IMAGE) {
|
|
1818
|
+
// By default in HLS the tilesLayout is 1x1
|
|
1819
|
+
tilesLayout = '1x1';
|
|
1820
|
+
const tilesTag =
|
|
1821
|
+
shaka.hls.Utils.getFirstTagWithName(tags, 'EXT-X-TILES');
|
|
1822
|
+
if (tilesTag) {
|
|
1823
|
+
tilesLayout = tilesTag.getRequiredAttrValue('LAYOUT');
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1733
1827
|
return new shaka.media.SegmentReference(
|
|
1734
1828
|
startTime,
|
|
1735
1829
|
endTime,
|
|
@@ -1741,6 +1835,7 @@ shaka.hls.HlsParser = class {
|
|
|
1741
1835
|
/* appendWindowStart= */ 0,
|
|
1742
1836
|
/* appendWindowEnd= */ Infinity,
|
|
1743
1837
|
partialSegmentRefs,
|
|
1838
|
+
tilesLayout,
|
|
1744
1839
|
);
|
|
1745
1840
|
}
|
|
1746
1841
|
|
|
@@ -1795,11 +1890,14 @@ shaka.hls.HlsParser = class {
|
|
|
1795
1890
|
* @param {!Map.<number, number>} mediaSequenceToStartTime
|
|
1796
1891
|
* @param {!Map.<string, string>} variables
|
|
1797
1892
|
* @param {!Map.<number, number>} discontinuityToMediaSequence
|
|
1893
|
+
* @param {string} codecs
|
|
1894
|
+
* @param {(number|undefined)} bandwidth
|
|
1798
1895
|
* @return {!Promise<!Array.<!shaka.media.SegmentReference>>}
|
|
1799
1896
|
* @private
|
|
1800
1897
|
*/
|
|
1801
1898
|
async createSegments_(verbatimMediaPlaylistUri, playlist, type, mimeType,
|
|
1802
|
-
mediaSequenceToStartTime, variables, discontinuityToMediaSequence
|
|
1899
|
+
mediaSequenceToStartTime, variables, discontinuityToMediaSequence,
|
|
1900
|
+
codecs, bandwidth) {
|
|
1803
1901
|
/** @type {Array.<!shaka.hls.Segment>} */
|
|
1804
1902
|
const hlsSegments = playlist.segments;
|
|
1805
1903
|
goog.asserts.assert(hlsSegments.length, 'Playlist should have segments!');
|
|
@@ -1836,12 +1934,13 @@ shaka.hls.HlsParser = class {
|
|
|
1836
1934
|
initSegmentRef = this.getInitSegmentReference_(
|
|
1837
1935
|
playlist.absoluteUri, hlsSegments[0].tags, variables);
|
|
1838
1936
|
goog.asserts.assert(
|
|
1839
|
-
type != shaka.util.ManifestParserUtils.ContentType.TEXT
|
|
1937
|
+
type != shaka.util.ManifestParserUtils.ContentType.TEXT &&
|
|
1938
|
+
type != shaka.util.ManifestParserUtils.ContentType.IMAGE,
|
|
1840
1939
|
'Should only get start time from audio or video streams');
|
|
1841
1940
|
this.playlistStartTime_ = await this.getStartTime_(
|
|
1842
1941
|
verbatimMediaPlaylistUri, initSegmentRef, mimeType,
|
|
1843
1942
|
position, /* isDiscontinuity= */ false,
|
|
1844
|
-
hlsSegments[0], variables);
|
|
1943
|
+
hlsSegments[0], variables, type, codecs, bandwidth);
|
|
1845
1944
|
}
|
|
1846
1945
|
firstStartTime = this.playlistStartTime_;
|
|
1847
1946
|
}
|
|
@@ -1894,7 +1993,8 @@ shaka.hls.HlsParser = class {
|
|
|
1894
1993
|
// eslint-disable-next-line no-await-in-loop
|
|
1895
1994
|
timestampOffset = await this.getTimestampOffset_(
|
|
1896
1995
|
discontintuitySequenceNum, verbatimMediaPlaylistUri, initSegmentRef,
|
|
1897
|
-
mimeType, position, item, variables, startTime
|
|
1996
|
+
mimeType, position, item, variables, startTime, type, codecs,
|
|
1997
|
+
bandwidth);
|
|
1898
1998
|
}
|
|
1899
1999
|
|
|
1900
2000
|
// If the stream is low latency and the user has not configured the
|
|
@@ -1919,7 +2019,8 @@ shaka.hls.HlsParser = class {
|
|
|
1919
2019
|
startTime,
|
|
1920
2020
|
timestampOffset,
|
|
1921
2021
|
variables,
|
|
1922
|
-
playlist.absoluteUri
|
|
2022
|
+
playlist.absoluteUri,
|
|
2023
|
+
type);
|
|
1923
2024
|
|
|
1924
2025
|
references.push(reference);
|
|
1925
2026
|
} else if (!this.lowLatencyMode_) {
|
|
@@ -1946,13 +2047,17 @@ shaka.hls.HlsParser = class {
|
|
|
1946
2047
|
* @param {!shaka.hls.Segment} segment
|
|
1947
2048
|
* @param {!Map.<string, string>} variables
|
|
1948
2049
|
* @param {number} startTime
|
|
2050
|
+
* @param {string} type
|
|
2051
|
+
* @param {string} codecs
|
|
2052
|
+
* @param {number|undefined} bandwidth
|
|
1949
2053
|
* @return {!Promise.<number>}
|
|
1950
2054
|
* @throws {shaka.util.Error}
|
|
1951
2055
|
* @private
|
|
1952
2056
|
*/
|
|
1953
2057
|
async getTimestampOffset_(discontintuitySequenceNum,
|
|
1954
2058
|
verbatimMediaPlaylistUri, initSegmentRef,
|
|
1955
|
-
mimeType, mediaSequenceNumber, segment, variables, startTime
|
|
2059
|
+
mimeType, mediaSequenceNumber, segment, variables, startTime, type,
|
|
2060
|
+
codecs, bandwidth) {
|
|
1956
2061
|
let timestampOffset = 0;
|
|
1957
2062
|
if (this.discontinuityToTso_.has(discontintuitySequenceNum)) {
|
|
1958
2063
|
timestampOffset =
|
|
@@ -1961,7 +2066,7 @@ shaka.hls.HlsParser = class {
|
|
|
1961
2066
|
const mediaStartTime = await this.getStartTime_(
|
|
1962
2067
|
verbatimMediaPlaylistUri, initSegmentRef, mimeType,
|
|
1963
2068
|
mediaSequenceNumber, /* isDiscontinuity= */ true, segment,
|
|
1964
|
-
variables);
|
|
2069
|
+
variables, type, codecs, bandwidth);
|
|
1965
2070
|
timestampOffset = startTime - mediaStartTime;
|
|
1966
2071
|
shaka.log.v1('Segment timestampOffset =', timestampOffset);
|
|
1967
2072
|
this.discontinuityToTso_.set(
|
|
@@ -1975,10 +2080,14 @@ shaka.hls.HlsParser = class {
|
|
|
1975
2080
|
* segment if we have to.
|
|
1976
2081
|
*
|
|
1977
2082
|
* @param {!shaka.media.AnySegmentReference} reference
|
|
2083
|
+
* @param {string} type
|
|
2084
|
+
* @param {string} mimeType
|
|
2085
|
+
* @param {string} codecs
|
|
2086
|
+
* @param {number|undefined} bandwidth
|
|
1978
2087
|
* @return {!Promise.<shaka.extern.Response>}
|
|
1979
2088
|
* @private
|
|
1980
2089
|
*/
|
|
1981
|
-
async fetchStartOfSegment_(reference) {
|
|
2090
|
+
async fetchStartOfSegment_(reference, type, mimeType, codecs, bandwidth) {
|
|
1982
2091
|
const requestType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
|
|
1983
2092
|
|
|
1984
2093
|
// Create two requests:
|
|
@@ -1992,6 +2101,20 @@ shaka.hls.HlsParser = class {
|
|
|
1992
2101
|
reference.endByte,
|
|
1993
2102
|
this.config_.retryParameters);
|
|
1994
2103
|
|
|
2104
|
+
// We can only add partial CMCD data here because the stream
|
|
2105
|
+
// and manifest objects are still being created
|
|
2106
|
+
this.playerInterface_.modifySegmentRequest(
|
|
2107
|
+
fullRequest,
|
|
2108
|
+
{
|
|
2109
|
+
type: type,
|
|
2110
|
+
init: reference instanceof shaka.media.InitSegmentReference,
|
|
2111
|
+
duration: reference.endTime - reference.startTime,
|
|
2112
|
+
mimeType: mimeType,
|
|
2113
|
+
codecs: codecs,
|
|
2114
|
+
bandwidth: bandwidth,
|
|
2115
|
+
},
|
|
2116
|
+
);
|
|
2117
|
+
|
|
1995
2118
|
if (this.config_.hls.useFullSegmentsForStartTime) {
|
|
1996
2119
|
return this.makeNetworkRequest_(fullRequest, requestType);
|
|
1997
2120
|
}
|
|
@@ -2002,6 +2125,18 @@ shaka.hls.HlsParser = class {
|
|
|
2002
2125
|
reference.startByte + shaka.hls.HlsParser.START_OF_SEGMENT_SIZE_ - 1,
|
|
2003
2126
|
this.config_.retryParameters);
|
|
2004
2127
|
|
|
2128
|
+
this.playerInterface_.modifySegmentRequest(
|
|
2129
|
+
partialRequest,
|
|
2130
|
+
{
|
|
2131
|
+
type: type,
|
|
2132
|
+
init: reference instanceof shaka.media.InitSegmentReference,
|
|
2133
|
+
duration: reference.endTime - reference.startTime,
|
|
2134
|
+
mimeType: mimeType,
|
|
2135
|
+
codecs: codecs,
|
|
2136
|
+
bandwidth: bandwidth,
|
|
2137
|
+
},
|
|
2138
|
+
);
|
|
2139
|
+
|
|
2005
2140
|
// TODO(vaage): The need to do fall back requests is not likely to be unique
|
|
2006
2141
|
// to here. It would be nice if the fallback(s) could be included into
|
|
2007
2142
|
// the same abortable operation as the original request.
|
|
@@ -2048,12 +2183,15 @@ shaka.hls.HlsParser = class {
|
|
|
2048
2183
|
* @param {boolean} isDiscontinuity
|
|
2049
2184
|
* @param {!shaka.hls.Segment} segment
|
|
2050
2185
|
* @param {!Map.<string, string>} variables
|
|
2186
|
+
* @param {string} type
|
|
2187
|
+
* @param {string} codecs
|
|
2188
|
+
* @param {number|undefined} bandwidth
|
|
2051
2189
|
* @return {!Promise.<number>}
|
|
2052
2190
|
* @private
|
|
2053
2191
|
*/
|
|
2054
2192
|
async getStartTime_(
|
|
2055
2193
|
verbatimMediaPlaylistUri, initSegmentRef, mimeType, mediaSequenceNumber,
|
|
2056
|
-
isDiscontinuity, segment, variables) {
|
|
2194
|
+
isDiscontinuity, segment, variables, type, codecs, bandwidth) {
|
|
2057
2195
|
const segmentRef = this.createSegmentReference_(
|
|
2058
2196
|
initSegmentRef,
|
|
2059
2197
|
/* previousReference= */ null,
|
|
@@ -2061,7 +2199,8 @@ shaka.hls.HlsParser = class {
|
|
|
2061
2199
|
/* startTime= */ 0,
|
|
2062
2200
|
/* timestampOffset= */ 0,
|
|
2063
2201
|
variables,
|
|
2064
|
-
/* absoluteMediaPlaylistUri= */ ''
|
|
2202
|
+
/* absoluteMediaPlaylistUri= */ '',
|
|
2203
|
+
type);
|
|
2065
2204
|
// If we are updating the manifest, we can usually skip fetching the segment
|
|
2066
2205
|
// by examining the references we already have. This won't be possible if
|
|
2067
2206
|
// there was some kind of lag or delay updating the manifest on the server,
|
|
@@ -2120,10 +2259,22 @@ shaka.hls.HlsParser = class {
|
|
|
2120
2259
|
if (mimeType == 'video/mp4' || mimeType == 'audio/mp4') {
|
|
2121
2260
|
// We also need the init segment to get the correct timescale. But if the
|
|
2122
2261
|
// stream is self-initializing, use the same response for both.
|
|
2123
|
-
const fetches = [this.fetchStartOfSegment_(
|
|
2262
|
+
const fetches = [this.fetchStartOfSegment_(
|
|
2263
|
+
segmentRef,
|
|
2264
|
+
type,
|
|
2265
|
+
mimeType,
|
|
2266
|
+
codecs,
|
|
2267
|
+
bandwidth,
|
|
2268
|
+
)];
|
|
2124
2269
|
|
|
2125
2270
|
if (initSegmentRef) {
|
|
2126
|
-
fetches.push(this.fetchStartOfSegment_(
|
|
2271
|
+
fetches.push(this.fetchStartOfSegment_(
|
|
2272
|
+
initSegmentRef,
|
|
2273
|
+
type,
|
|
2274
|
+
mimeType,
|
|
2275
|
+
codecs,
|
|
2276
|
+
bandwidth,
|
|
2277
|
+
));
|
|
2127
2278
|
}
|
|
2128
2279
|
|
|
2129
2280
|
const responses = await Promise.all(fetches);
|
|
@@ -2139,7 +2290,13 @@ shaka.hls.HlsParser = class {
|
|
|
2139
2290
|
}
|
|
2140
2291
|
|
|
2141
2292
|
if (mimeType == 'video/mp2t') {
|
|
2142
|
-
const response = await this.fetchStartOfSegment_(
|
|
2293
|
+
const response = await this.fetchStartOfSegment_(
|
|
2294
|
+
segmentRef,
|
|
2295
|
+
type,
|
|
2296
|
+
mimeType,
|
|
2297
|
+
codecs,
|
|
2298
|
+
bandwidth,
|
|
2299
|
+
);
|
|
2143
2300
|
goog.asserts.assert(response.data, 'Should have a response body!');
|
|
2144
2301
|
return this.getStartTimeFromTsSegment_(
|
|
2145
2302
|
verbatimMediaPlaylistUri, response.uri, response.data);
|
|
@@ -2437,6 +2594,12 @@ shaka.hls.HlsParser = class {
|
|
|
2437
2594
|
}
|
|
2438
2595
|
}
|
|
2439
2596
|
|
|
2597
|
+
if (contentType == ContentType.IMAGE) {
|
|
2598
|
+
if (!codecs || codecs == 'jpeg') {
|
|
2599
|
+
return 'image/jpeg';
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2440
2603
|
// If unable to guess mime type, request a segment and try getting it
|
|
2441
2604
|
// from the response.
|
|
2442
2605
|
const headRequest = shaka.net.NetworkingEngine.makeRequest(
|
|
@@ -2511,6 +2674,9 @@ shaka.hls.HlsParser = class {
|
|
|
2511
2674
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
2512
2675
|
[absoluteUri], this.config_.retryParameters);
|
|
2513
2676
|
|
|
2677
|
+
const format = shaka.util.CmcdManager.StreamingFormat.HLS;
|
|
2678
|
+
this.playerInterface_.modifyManifestRequest(request, {format: format});
|
|
2679
|
+
|
|
2514
2680
|
return this.makeNetworkRequest_(request, requestType);
|
|
2515
2681
|
}
|
|
2516
2682
|
|
|
@@ -2807,6 +2973,19 @@ shaka.hls.HlsParser.TEXT_EXTENSIONS_TO_MIME_TYPES_ = {
|
|
|
2807
2973
|
};
|
|
2808
2974
|
|
|
2809
2975
|
|
|
2976
|
+
/**
|
|
2977
|
+
* @const {!Object.<string, string>}
|
|
2978
|
+
* @private
|
|
2979
|
+
*/
|
|
2980
|
+
shaka.hls.HlsParser.IMAGE_EXTENSIONS_TO_MIME_TYPES_ = {
|
|
2981
|
+
'jpg': 'image/jpeg',
|
|
2982
|
+
'png': 'image/png',
|
|
2983
|
+
'svg': 'image/svg+xml',
|
|
2984
|
+
'webp': 'image/webp',
|
|
2985
|
+
'avif': 'image/avif',
|
|
2986
|
+
};
|
|
2987
|
+
|
|
2988
|
+
|
|
2810
2989
|
/**
|
|
2811
2990
|
* @const {!Object.<string, !Object.<string, string>>}
|
|
2812
2991
|
* @private
|
|
@@ -2815,6 +2994,7 @@ shaka.hls.HlsParser.EXTENSION_MAP_BY_CONTENT_TYPE_ = {
|
|
|
2815
2994
|
'audio': shaka.hls.HlsParser.AUDIO_EXTENSIONS_TO_MIME_TYPES_,
|
|
2816
2995
|
'video': shaka.hls.HlsParser.VIDEO_EXTENSIONS_TO_MIME_TYPES_,
|
|
2817
2996
|
'text': shaka.hls.HlsParser.TEXT_EXTENSIONS_TO_MIME_TYPES_,
|
|
2997
|
+
'image': shaka.hls.HlsParser.IMAGE_EXTENSIONS_TO_MIME_TYPES_,
|
|
2818
2998
|
};
|
|
2819
2999
|
|
|
2820
3000
|
|
|
@@ -4,14 +4,13 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
goog.provide('shaka.media.IClosedCaptionParser');
|
|
8
7
|
goog.provide('shaka.media.ClosedCaptionParser');
|
|
8
|
+
goog.provide('shaka.media.IClosedCaptionParser');
|
|
9
9
|
|
|
10
10
|
goog.require('shaka.cea.CeaDecoder');
|
|
11
11
|
goog.require('shaka.cea.Mp4CeaParser');
|
|
12
12
|
goog.require('shaka.util.BufferUtils');
|
|
13
13
|
goog.requireType('shaka.cea.ICaptionDecoder');
|
|
14
|
-
goog.requireType('shaka.cea.ICaptionDecoder.ClosedCaption');
|
|
15
14
|
goog.requireType('shaka.cea.ICeaParser');
|
|
16
15
|
|
|
17
16
|
|