shaka-player 3.2.0 → 3.2.4
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/workflows/build_and_test.yaml +73 -0
- package/.github/workflows/release.yaml +53 -0
- package/AUTHORS +3 -0
- package/CHANGELOG.md +182 -1
- package/CONTRIBUTORS +3 -0
- package/README.md +4 -4
- package/build/checkversion.py +1 -1
- package/build/generateExterns.js +1 -1
- package/build/misspellings.txt +1 -0
- package/build/shakaBuildHelpers.py +6 -5
- package/build/subprocessWindowsPatch.py +103 -0
- package/build/test.py +6 -0
- package/demo/common/assets.js +1 -1
- package/demo/index.html +1 -1
- package/dist/controls.css +2 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +1 -1
- package/dist/demo.compiled.js +1 -1
- package/dist/deps.js +8 -8
- package/dist/locales.js +37 -37
- package/dist/receiver.compiled.debug.js +1 -1
- package/dist/receiver.compiled.js +1 -1
- package/dist/shaka-player.compiled.d.ts +195 -5
- package/dist/shaka-player.compiled.debug.d.ts +195 -5
- package/dist/shaka-player.compiled.debug.externs.js +226 -14
- package/dist/shaka-player.compiled.debug.js +424 -399
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +226 -14
- package/dist/shaka-player.compiled.js +826 -812
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +197 -16
- package/dist/shaka-player.ui.debug.d.ts +197 -16
- package/dist/shaka-player.ui.debug.externs.js +230 -27
- package/dist/shaka-player.ui.debug.js +466 -442
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +230 -27
- package/dist/shaka-player.ui.js +946 -932
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +1 -1
- package/docs/tutorials/faq.md +14 -0
- package/docs/tutorials/ui.md +5 -5
- package/externs/ima.js +4 -0
- package/externs/shaka/ads.js +25 -0
- package/externs/shaka/player.js +8 -0
- package/externs/shaka/text.js +10 -0
- package/karma.conf.js +20 -15
- package/lib/ads/ad_manager.js +46 -26
- package/lib/ads/client_side_ad_manager.js +82 -88
- package/lib/ads/server_side_ad_manager.js +45 -13
- package/lib/cast/cast_proxy.js +6 -2
- package/lib/cast/cast_receiver.js +5 -1
- package/lib/cast/cast_sender.js +2 -2
- package/lib/cea/sei_processor.js +24 -17
- package/lib/dash/dash_parser.js +131 -24
- package/lib/dash/segment_list.js +1 -1
- package/lib/dash/segment_template.js +1 -1
- package/lib/hls/hls_parser.js +74 -17
- package/lib/hls/manifest_text_parser.js +1 -0
- package/lib/media/closed_caption_parser.js +3 -1
- package/lib/media/content_workarounds.js +12 -0
- package/lib/media/drm_engine.js +20 -1
- package/lib/media/gap_jumping_controller.js +8 -4
- package/lib/media/media_source_engine.js +1 -2
- package/lib/media/playhead.js +1 -1
- package/lib/media/region_observer.js +39 -33
- package/lib/media/region_timeline.js +17 -19
- package/lib/media/segment_index.js +2 -1
- package/lib/media/segment_reference.js +19 -1
- package/lib/media/streaming_engine.js +8 -4
- package/lib/net/networking_engine.js +6 -1
- package/lib/offline/download_manager.js +1 -2
- package/lib/offline/storage.js +8 -9
- package/lib/player.js +129 -59
- package/lib/polyfill/aria.js +12 -2
- package/lib/polyfill/encryption_scheme.js +2 -0
- package/lib/polyfill/fullscreen.js +2 -0
- package/lib/polyfill/mathround.js +2 -0
- package/lib/polyfill/media_capabilities.js +73 -21
- package/lib/polyfill/mediasource.js +8 -0
- package/lib/polyfill/orientation.js +3 -1
- package/lib/polyfill/patchedmediakeys_apple.js +6 -4
- package/lib/polyfill/patchedmediakeys_ms.js +6 -4
- package/lib/polyfill/patchedmediakeys_nop.js +2 -0
- package/lib/polyfill/patchedmediakeys_webkit.js +6 -4
- package/lib/polyfill/pip_webkit.js +2 -0
- package/lib/polyfill/storage_estimate.js +2 -0
- package/lib/polyfill/video_play_promise.js +2 -0
- package/lib/polyfill/videoplaybackquality.js +2 -0
- package/lib/polyfill/vttcue.js +2 -0
- package/lib/text/cue.js +6 -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 +135 -87
- package/lib/text/ui_text_displayer.js +213 -80
- package/lib/text/vtt_text_parser.js +4 -2
- package/lib/text/web_vtt_generator.js +21 -12
- package/lib/util/fake_event.js +53 -7
- package/lib/util/fake_event_target.js +21 -1
- package/lib/util/mp4_box_parsers.js +1 -1
- package/lib/util/periods.js +4 -1
- package/lib/util/platform.js +13 -1
- package/lib/util/stream_utils.js +105 -13
- package/lib/util/xml_utils.js +50 -12
- package/package.json +3 -6
- package/roadmap.md +24 -22
- package/test/cast/cast_proxy_unit.js +12 -6
- package/test/cast/cast_receiver_integration.js +1 -1
- package/test/cast/cast_utils_unit.js +1 -1
- package/test/cea/mp4_cea_parser_unit.js +26 -0
- package/test/dash/dash_parser_live_unit.js +43 -0
- package/test/dash/dash_parser_manifest_unit.js +222 -3
- package/test/hls/hls_parser_unit.js +79 -1
- package/test/media/closed_caption_parser_unit.js +20 -0
- package/test/media/drm_engine_integration.js +86 -2
- package/test/media/drm_engine_unit.js +57 -0
- package/test/media/playhead_unit.js +27 -1
- package/test/media/region_observer_unit.js +40 -5
- package/test/media/region_timeline_unit.js +40 -22
- package/test/media/streaming_engine_integration.js +2 -1
- package/test/media/streaming_engine_unit.js +145 -0
- package/test/player_integration.js +79 -8
- package/test/player_src_equals_integration.js +41 -8
- package/test/player_unit.js +14 -4
- 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/assets/empty_caption_video_init.mp4 +0 -0
- package/test/test/assets/empty_caption_video_segment.mp4 +0 -0
- package/test/test/util/fake_ad_manager.js +11 -1
- package/test/test/util/ttml_utils.js +81 -0
- package/test/test/util/util.js +10 -7
- package/test/text/mp4_ttml_parser_unit.js +11 -18
- package/test/text/ttml_text_parser_unit.js +542 -220
- package/test/text/ui_text_displayer_unit.js +93 -20
- package/test/text/vtt_text_parser_unit.js +17 -1
- package/test/text/web_vtt_generator_unit.js +58 -3
- package/test/ui/localization_unit.js +15 -14
- package/test/ui/text_displayer_layout_unit.js +21 -8
- package/test/util/mp4_box_parsers_unit.js +28 -0
- package/test/util/stream_utils_unit.js +60 -3
- package/ui/cast_button.js +2 -3
- package/ui/controls.js +11 -6
- package/ui/less/buttons.less +0 -4
- package/ui/less/containers.less +6 -7
- package/ui/locales/ru.json +6 -6
- package/ui/localization.js +40 -87
- package/ui/pip_button.js +2 -3
- package/ui/seek_bar.js +1 -2
- package/ui/text_selection.js +1 -0
package/lib/player.js
CHANGED
|
@@ -598,7 +598,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
598
598
|
enterNode: (node, has, wants) => {
|
|
599
599
|
this.dispatchEvent(this.makeEvent_(
|
|
600
600
|
/* name= */ shaka.Player.EventName.OnStateChange,
|
|
601
|
-
/* data= */
|
|
601
|
+
/* data= */ (new Map()).set('state', node.name)));
|
|
602
602
|
|
|
603
603
|
const action = actions.get(node);
|
|
604
604
|
return action(has, wants);
|
|
@@ -629,7 +629,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
629
629
|
onIdle: (node) => {
|
|
630
630
|
this.dispatchEvent(this.makeEvent_(
|
|
631
631
|
/* name= */ shaka.Player.EventName.OnStateIdle,
|
|
632
|
-
/* data= */
|
|
632
|
+
/* data= */ (new Map()).set('state', node.name)));
|
|
633
633
|
},
|
|
634
634
|
};
|
|
635
635
|
|
|
@@ -649,7 +649,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
649
649
|
|
|
650
650
|
/**
|
|
651
651
|
* @param {!shaka.Player.EventName} name
|
|
652
|
-
* @param {Object
|
|
652
|
+
* @param {Map.<string, Object>=} data
|
|
653
653
|
* @return {!shaka.util.FakeEvent}
|
|
654
654
|
* @private
|
|
655
655
|
*/
|
|
@@ -728,6 +728,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
728
728
|
await this.networkingEngine_.destroy();
|
|
729
729
|
this.networkingEngine_ = null;
|
|
730
730
|
}
|
|
731
|
+
|
|
732
|
+
// FakeEventTarget implements IReleasable
|
|
733
|
+
super.release();
|
|
731
734
|
}
|
|
732
735
|
|
|
733
736
|
/**
|
|
@@ -1270,6 +1273,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1270
1273
|
has.mediaElement = null;
|
|
1271
1274
|
}
|
|
1272
1275
|
|
|
1276
|
+
if (this.adManager_) {
|
|
1277
|
+
// The ad manager is specific to the video, so detach it too.
|
|
1278
|
+
this.adManager_.release();
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1273
1281
|
// Clear our cached copy of the media element.
|
|
1274
1282
|
this.video_ = null;
|
|
1275
1283
|
|
|
@@ -1606,18 +1614,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1606
1614
|
// initialize it now even through it appears a little out-of-place.
|
|
1607
1615
|
this.regionTimeline_ =
|
|
1608
1616
|
new shaka.media.RegionTimeline(() => this.seekRange());
|
|
1609
|
-
this.regionTimeline_.
|
|
1617
|
+
this.regionTimeline_.addEventListener('regionadd', (event) => {
|
|
1618
|
+
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
1619
|
+
const region = event['region'];
|
|
1610
1620
|
this.onRegionEvent_(shaka.Player.EventName.TimelineRegionAdded, region);
|
|
1621
|
+
|
|
1611
1622
|
if (this.adManager_) {
|
|
1612
1623
|
this.adManager_.onDashTimedMetadata(region);
|
|
1613
1624
|
}
|
|
1614
1625
|
});
|
|
1615
1626
|
|
|
1616
|
-
// TODO (#1391): Once filterManifest_ is async, remove this eslint disable.
|
|
1617
|
-
/* eslint-disable require-await */
|
|
1618
1627
|
const playerInterface = {
|
|
1619
1628
|
networkingEngine: networkingEngine,
|
|
1620
|
-
filter:
|
|
1629
|
+
filter: (manifest) => this.filterManifest_(manifest),
|
|
1621
1630
|
makeTextStreamsForClosedCaptions: (manifest) => {
|
|
1622
1631
|
return this.makeTextStreamsForClosedCaptions_(manifest);
|
|
1623
1632
|
},
|
|
@@ -1635,7 +1644,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1635
1644
|
this.configure('streaming.lowLatencyMode', true);
|
|
1636
1645
|
},
|
|
1637
1646
|
};
|
|
1638
|
-
/* eslint-enable require-await */
|
|
1639
1647
|
|
|
1640
1648
|
const startTime = Date.now() / 1000;
|
|
1641
1649
|
|
|
@@ -2003,7 +2011,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2003
2011
|
|
|
2004
2012
|
this.drmEngine_.configure(this.config_.drm);
|
|
2005
2013
|
|
|
2006
|
-
const uri =
|
|
2014
|
+
const uri = wants.uri || '';
|
|
2007
2015
|
const extension = shaka.media.ManifestParser.getExtension(uri);
|
|
2008
2016
|
let mimeType = shaka.Player.SRC_EQUAL_EXTENSIONS_TO_MIME_TYPES_[extension];
|
|
2009
2017
|
if (mimeType == 'application/x-mpegurl' && shaka.util.Platform.isApple()) {
|
|
@@ -2058,6 +2066,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2058
2066
|
decodingInfos: [],
|
|
2059
2067
|
};
|
|
2060
2068
|
|
|
2069
|
+
this.drmEngine_.setSrcEquals(/* srcEquals= */ true);
|
|
2061
2070
|
await this.drmEngine_.initForPlayback(
|
|
2062
2071
|
[variant], /* offlineSessionIds= */ []);
|
|
2063
2072
|
await this.drmEngine_.attach(has.mediaElement);
|
|
@@ -2414,12 +2423,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2414
2423
|
goog.asserts.assert(!endTime || startTime <= endTime,
|
|
2415
2424
|
'Metadata start time should be less or equal to the end time!');
|
|
2416
2425
|
const eventName = shaka.Player.EventName.Metadata;
|
|
2417
|
-
const data =
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
};
|
|
2426
|
+
const data = new Map()
|
|
2427
|
+
.set('startTime', startTime)
|
|
2428
|
+
.set('endTime', endTime)
|
|
2429
|
+
.set('metadataType', metadataType)
|
|
2430
|
+
.set('payload', payload);
|
|
2423
2431
|
this.dispatchEvent(this.makeEvent_(eventName, data));
|
|
2424
2432
|
}
|
|
2425
2433
|
|
|
@@ -2545,21 +2553,31 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2545
2553
|
// Create the region observer. This will allow us to notify the app when we
|
|
2546
2554
|
// move in and out of timeline regions.
|
|
2547
2555
|
const regionObserver = new shaka.media.RegionObserver(this.regionTimeline_);
|
|
2548
|
-
|
|
2556
|
+
|
|
2557
|
+
regionObserver.addEventListener('enter', (event) => {
|
|
2558
|
+
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
2559
|
+
const region = event['region'];
|
|
2549
2560
|
this.onRegionEvent_(shaka.Player.EventName.TimelineRegionEnter, region);
|
|
2550
|
-
};
|
|
2551
|
-
|
|
2561
|
+
});
|
|
2562
|
+
|
|
2563
|
+
regionObserver.addEventListener('exit', (event) => {
|
|
2564
|
+
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
2565
|
+
const region = event['region'];
|
|
2552
2566
|
this.onRegionEvent_(shaka.Player.EventName.TimelineRegionExit, region);
|
|
2553
|
-
};
|
|
2554
|
-
|
|
2567
|
+
});
|
|
2568
|
+
|
|
2569
|
+
regionObserver.addEventListener('skip', (event) => {
|
|
2570
|
+
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
2571
|
+
const region = event['region'];
|
|
2572
|
+
/** @type {boolean} */
|
|
2573
|
+
const seeking = event['seeking'];
|
|
2555
2574
|
// If we are seeking, we don't want to surface the enter/exit events since
|
|
2556
2575
|
// they didn't play through them.
|
|
2557
2576
|
if (!seeking) {
|
|
2558
2577
|
this.onRegionEvent_(shaka.Player.EventName.TimelineRegionEnter, region);
|
|
2559
2578
|
this.onRegionEvent_(shaka.Player.EventName.TimelineRegionExit, region);
|
|
2560
2579
|
}
|
|
2561
|
-
};
|
|
2562
|
-
regionObserver.setListeners(onEnterRegion, onExitRegion, onSkipRegion);
|
|
2580
|
+
});
|
|
2563
2581
|
|
|
2564
2582
|
// Now that we have all our observers, create a manager for them.
|
|
2565
2583
|
const manager = new shaka.media.PlayheadObserverManager(this.video_);
|
|
@@ -3318,6 +3336,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3318
3336
|
|
|
3319
3337
|
const tracks = [];
|
|
3320
3338
|
|
|
3339
|
+
let activeTracks = 0;
|
|
3340
|
+
|
|
3321
3341
|
// Convert each variant to a track.
|
|
3322
3342
|
for (const variant of this.manifest_.variants) {
|
|
3323
3343
|
if (!shaka.util.StreamUtils.isPlayable(variant)) {
|
|
@@ -3326,10 +3346,22 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3326
3346
|
|
|
3327
3347
|
const track = shaka.util.StreamUtils.variantToTrack(variant);
|
|
3328
3348
|
track.active = variant == currentVariant;
|
|
3349
|
+
if (!track.active && activeTracks != 1 && currentVariant != null &&
|
|
3350
|
+
variant.video == currentVariant.video &&
|
|
3351
|
+
variant.audio == currentVariant.audio) {
|
|
3352
|
+
track.active = true;
|
|
3353
|
+
}
|
|
3354
|
+
|
|
3355
|
+
if (track.active) {
|
|
3356
|
+
activeTracks++;
|
|
3357
|
+
}
|
|
3329
3358
|
|
|
3330
3359
|
tracks.push(track);
|
|
3331
3360
|
}
|
|
3332
3361
|
|
|
3362
|
+
goog.asserts.assert(activeTracks <= 1,
|
|
3363
|
+
'It should only have one active track');
|
|
3364
|
+
|
|
3333
3365
|
return tracks;
|
|
3334
3366
|
} else if (this.video_ && this.video_.audioTracks) {
|
|
3335
3367
|
// Safari's native HLS always shows a single element in videoTracks.
|
|
@@ -3438,7 +3470,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3438
3470
|
const height = fullImageHeight / rows;
|
|
3439
3471
|
const totalImages = columns * rows;
|
|
3440
3472
|
const segmentDuration = reference.trueEndTime - reference.startTime;
|
|
3441
|
-
const thumbnailDuration =
|
|
3473
|
+
const thumbnailDuration =
|
|
3474
|
+
reference.getTileDuration() || (segmentDuration / totalImages);
|
|
3442
3475
|
let thumbnailTime = reference.startTime;
|
|
3443
3476
|
let positionX = 0;
|
|
3444
3477
|
let positionY = 0;
|
|
@@ -3461,6 +3494,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3461
3494
|
positionY = Math.floor(thumbnailPosition / columns) * height;
|
|
3462
3495
|
}
|
|
3463
3496
|
return {
|
|
3497
|
+
imageHeight: fullImageHeight,
|
|
3498
|
+
imageWidth: fullImageWidth,
|
|
3464
3499
|
height: height,
|
|
3465
3500
|
positionX: positionX,
|
|
3466
3501
|
positionY: positionY,
|
|
@@ -3862,20 +3897,24 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3862
3897
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
3863
3898
|
const inputlanguage = LanguageUtils.normalize(language);
|
|
3864
3899
|
const chaptersTracks = this.getChaptersTracks_();
|
|
3865
|
-
const
|
|
3866
|
-
.
|
|
3867
|
-
if (!
|
|
3900
|
+
const chaptersTracksWithLanguage = chaptersTracks
|
|
3901
|
+
.filter((t) => LanguageUtils.normalize(t.language) == inputlanguage);
|
|
3902
|
+
if (!chaptersTracksWithLanguage || !chaptersTracksWithLanguage.length) {
|
|
3868
3903
|
return [];
|
|
3869
3904
|
}
|
|
3870
3905
|
const chapters = [];
|
|
3871
|
-
for (const
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3906
|
+
for (const chaptersTrack of chaptersTracksWithLanguage) {
|
|
3907
|
+
if (chaptersTrack && chaptersTrack.cues) {
|
|
3908
|
+
for (const cue of chaptersTrack.cues) {
|
|
3909
|
+
/** @type {shaka.extern.Chapter} */
|
|
3910
|
+
const chapter = {
|
|
3911
|
+
title: cue.text,
|
|
3912
|
+
startTime: cue.startTime,
|
|
3913
|
+
endTime: cue.endTime,
|
|
3914
|
+
};
|
|
3915
|
+
chapters.push(chapter);
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3879
3918
|
}
|
|
3880
3919
|
return chapters;
|
|
3881
3920
|
}
|
|
@@ -4373,12 +4412,20 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4373
4412
|
mimeType = await this.getTextMimetype_(uri);
|
|
4374
4413
|
}
|
|
4375
4414
|
|
|
4415
|
+
let adCuePoints = [];
|
|
4416
|
+
if (this.adManager_) {
|
|
4417
|
+
try {
|
|
4418
|
+
adCuePoints = this.adManager_.getServerSideCuePoints();
|
|
4419
|
+
} catch (error) {}
|
|
4420
|
+
}
|
|
4421
|
+
|
|
4376
4422
|
if (this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS) {
|
|
4377
4423
|
if (forced) {
|
|
4378
4424
|
// See: https://github.com/whatwg/html/issues/4472
|
|
4379
4425
|
kind = 'forced';
|
|
4380
4426
|
}
|
|
4381
|
-
await this.addSrcTrackElement_(uri, language, kind, mimeType, label
|
|
4427
|
+
await this.addSrcTrackElement_(uri, language, kind, mimeType, label || '',
|
|
4428
|
+
adCuePoints);
|
|
4382
4429
|
const textTracks = this.getTextTracks();
|
|
4383
4430
|
const srcTrack = textTracks.find((t) => {
|
|
4384
4431
|
return t.language == language &&
|
|
@@ -4408,6 +4455,18 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4408
4455
|
shaka.util.Error.Code.CANNOT_ADD_EXTERNAL_TEXT_TO_LIVE_STREAM);
|
|
4409
4456
|
}
|
|
4410
4457
|
|
|
4458
|
+
if (adCuePoints.length) {
|
|
4459
|
+
goog.asserts.assert(
|
|
4460
|
+
this.networkingEngine_, 'Need networking engine.');
|
|
4461
|
+
const data = await this.getTextData_(uri,
|
|
4462
|
+
this.networkingEngine_,
|
|
4463
|
+
this.config_.streaming.retryParameters);
|
|
4464
|
+
const vvtText = this.convertToWebVTT_(data, mimeType, adCuePoints);
|
|
4465
|
+
const blob = new Blob([vvtText], {type: 'text/vtt'});
|
|
4466
|
+
uri = shaka.media.MediaSourceEngine.createObjectURL(blob);
|
|
4467
|
+
mimeType = 'text/vtt';
|
|
4468
|
+
}
|
|
4469
|
+
|
|
4411
4470
|
/** @type {shaka.extern.Stream} */
|
|
4412
4471
|
const stream = {
|
|
4413
4472
|
id: this.nextExternalStreamId_++,
|
|
@@ -4480,8 +4539,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4480
4539
|
if (!mimeType) {
|
|
4481
4540
|
mimeType = await this.getTextMimetype_(uri);
|
|
4482
4541
|
}
|
|
4542
|
+
let adCuePoints = [];
|
|
4543
|
+
if (this.adManager_) {
|
|
4544
|
+
try {
|
|
4545
|
+
adCuePoints = this.adManager_.getServerSideCuePoints();
|
|
4546
|
+
} catch (error) {}
|
|
4547
|
+
}
|
|
4483
4548
|
await this.addSrcTrackElement_(uri, language, /* kind= */ 'chapters',
|
|
4484
|
-
mimeType);
|
|
4549
|
+
mimeType, /* label= */ '', adCuePoints);
|
|
4485
4550
|
const chaptersTracks = this.getChaptersTracks();
|
|
4486
4551
|
const chaptersTrack = chaptersTracks.find((t) => {
|
|
4487
4552
|
return t.language == language;
|
|
@@ -4544,17 +4609,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4544
4609
|
* @param {string} language
|
|
4545
4610
|
* @param {string} kind
|
|
4546
4611
|
* @param {string} mimeType
|
|
4547
|
-
* @param {string
|
|
4612
|
+
* @param {string} label
|
|
4613
|
+
* @param {!Array.<!shaka.extern.AdCuePoint>} adCuePoints
|
|
4548
4614
|
* @private
|
|
4549
4615
|
*/
|
|
4550
|
-
async addSrcTrackElement_(uri, language, kind, mimeType, label
|
|
4551
|
-
|
|
4616
|
+
async addSrcTrackElement_(uri, language, kind, mimeType, label,
|
|
4617
|
+
adCuePoints) {
|
|
4618
|
+
if (mimeType != 'text/vtt' || adCuePoints.length) {
|
|
4552
4619
|
goog.asserts.assert(
|
|
4553
4620
|
this.networkingEngine_, 'Need networking engine.');
|
|
4554
4621
|
const data = await this.getTextData_(uri,
|
|
4555
4622
|
this.networkingEngine_,
|
|
4556
4623
|
this.config_.streaming.retryParameters);
|
|
4557
|
-
const vvtText = this.convertToWebVTT_(data, mimeType);
|
|
4624
|
+
const vvtText = this.convertToWebVTT_(data, mimeType, adCuePoints);
|
|
4558
4625
|
const blob = new Blob([vvtText], {type: 'text/vtt'});
|
|
4559
4626
|
uri = shaka.media.MediaSourceEngine.createObjectURL(blob);
|
|
4560
4627
|
mimeType = 'text/vtt';
|
|
@@ -4562,7 +4629,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4562
4629
|
const trackElement =
|
|
4563
4630
|
/** @type {!HTMLTrackElement} */(document.createElement('track'));
|
|
4564
4631
|
trackElement.src = uri;
|
|
4565
|
-
trackElement.label = label
|
|
4632
|
+
trackElement.label = label;
|
|
4566
4633
|
trackElement.kind = kind;
|
|
4567
4634
|
trackElement.srclang = language;
|
|
4568
4635
|
// Because we're pulling in the text track file via Javascript, the
|
|
@@ -4601,10 +4668,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4601
4668
|
*
|
|
4602
4669
|
* @param {BufferSource} buffer
|
|
4603
4670
|
* @param {string} mimeType
|
|
4671
|
+
* @param {!Array.<!shaka.extern.AdCuePoint>} adCuePoints
|
|
4604
4672
|
* @return {string}
|
|
4605
4673
|
* @private
|
|
4606
4674
|
*/
|
|
4607
|
-
convertToWebVTT_(buffer, mimeType) {
|
|
4675
|
+
convertToWebVTT_(buffer, mimeType, adCuePoints) {
|
|
4608
4676
|
const factory = shaka.text.TextEngine.findParser(mimeType);
|
|
4609
4677
|
if (factory) {
|
|
4610
4678
|
const obj = factory();
|
|
@@ -4615,7 +4683,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4615
4683
|
};
|
|
4616
4684
|
const data = shaka.util.BufferUtils.toUint8(buffer);
|
|
4617
4685
|
const cues = obj.parseMedia(data, time);
|
|
4618
|
-
return shaka.text.WebVttGenerator.convert(cues);
|
|
4686
|
+
return shaka.text.WebVttGenerator.convert(cues, adCuePoints);
|
|
4619
4687
|
}
|
|
4620
4688
|
throw new shaka.util.Error(
|
|
4621
4689
|
shaka.util.Error.Severity.CRITICAL,
|
|
@@ -4992,7 +5060,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4992
5060
|
// Surface the buffering event so that the app knows if/when we are
|
|
4993
5061
|
// buffering.
|
|
4994
5062
|
const eventName = shaka.Player.EventName.Buffering;
|
|
4995
|
-
|
|
5063
|
+
const data = (new Map()).set('buffering', isBuffering);
|
|
5064
|
+
this.dispatchEvent(this.makeEvent_(eventName, data));
|
|
4996
5065
|
}
|
|
4997
5066
|
|
|
4998
5067
|
/**
|
|
@@ -5354,10 +5423,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5354
5423
|
onAdaptation_(from, to) {
|
|
5355
5424
|
// Delay the 'adaptation' event so that StreamingEngine has time to absorb
|
|
5356
5425
|
// the changes before the user tries to query it.
|
|
5357
|
-
const
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5426
|
+
const data = new Map()
|
|
5427
|
+
.set('oldTrack', from)
|
|
5428
|
+
.set('newTrack', to);
|
|
5429
|
+
const event = this.makeEvent_(shaka.Player.EventName.Adaptation, data);
|
|
5361
5430
|
this.delayDispatchEvent_(event);
|
|
5362
5431
|
}
|
|
5363
5432
|
|
|
@@ -5381,10 +5450,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5381
5450
|
onVariantChanged_(from, to) {
|
|
5382
5451
|
// Delay the 'variantchanged' event so StreamingEngine has time to absorb
|
|
5383
5452
|
// the changes before the user tries to query it.
|
|
5384
|
-
const
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5453
|
+
const data = new Map()
|
|
5454
|
+
.set('oldTrack', from)
|
|
5455
|
+
.set('newTrack', to);
|
|
5456
|
+
const event = this.makeEvent_(shaka.Player.EventName.VariantChanged, data);
|
|
5388
5457
|
this.delayDispatchEvent_(event);
|
|
5389
5458
|
}
|
|
5390
5459
|
|
|
@@ -5407,10 +5476,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5407
5476
|
|
|
5408
5477
|
/** @private */
|
|
5409
5478
|
onAbrStatusChanged_() {
|
|
5410
|
-
const
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
this.delayDispatchEvent_(event);
|
|
5479
|
+
const data = (new Map()).set('newStatus', this.config_.abr.enabled);
|
|
5480
|
+
this.delayDispatchEvent_(this.makeEvent_(
|
|
5481
|
+
shaka.Player.EventName.AbrStatusChanged, data));
|
|
5414
5482
|
}
|
|
5415
5483
|
|
|
5416
5484
|
/**
|
|
@@ -5427,7 +5495,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5427
5495
|
}
|
|
5428
5496
|
|
|
5429
5497
|
const eventName = shaka.Player.EventName.Error;
|
|
5430
|
-
const event = this.makeEvent_(eventName,
|
|
5498
|
+
const event = this.makeEvent_(eventName, (new Map()).set('detail', error));
|
|
5431
5499
|
this.dispatchEvent(event);
|
|
5432
5500
|
if (event.defaultPrevented) {
|
|
5433
5501
|
error.handled = true;
|
|
@@ -5456,7 +5524,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5456
5524
|
eventElement: region.eventElement,
|
|
5457
5525
|
};
|
|
5458
5526
|
|
|
5459
|
-
|
|
5527
|
+
const data = (new Map()).set('detail', clone);
|
|
5528
|
+
this.dispatchEvent(this.makeEvent_(eventName, data));
|
|
5460
5529
|
}
|
|
5461
5530
|
|
|
5462
5531
|
/**
|
|
@@ -6285,7 +6354,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6285
6354
|
* @define {string} A version number taken from git at compile time.
|
|
6286
6355
|
* @export
|
|
6287
6356
|
*/
|
|
6288
|
-
shaka.Player.version = 'v3.2.
|
|
6357
|
+
shaka.Player.version = 'v3.2.4-uncompiled'; // x-release-please-version
|
|
6289
6358
|
|
|
6290
6359
|
// Initialize the deprecation system using the version string we just set
|
|
6291
6360
|
// on the player.
|
|
@@ -6329,6 +6398,7 @@ shaka.Player.SRC_EQUAL_EXTENSIONS_TO_MIME_TYPES_ = {
|
|
|
6329
6398
|
'mpg': 'video/mpeg',
|
|
6330
6399
|
'mpeg': 'video/mpeg',
|
|
6331
6400
|
'm3u8': 'application/x-mpegurl',
|
|
6401
|
+
'mpd': 'application/dash+xml',
|
|
6332
6402
|
'mp3': 'audio/mpeg',
|
|
6333
6403
|
'aac': 'audio/aac',
|
|
6334
6404
|
'flac': 'audio/flac',
|
package/lib/polyfill/aria.js
CHANGED
|
@@ -16,9 +16,13 @@ goog.require('shaka.polyfill');
|
|
|
16
16
|
* for that platform, as it relies on getters and setters.
|
|
17
17
|
* @see https://w3c.github.io/aria/#ARIAMixin
|
|
18
18
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/Element
|
|
19
|
+
* @export
|
|
19
20
|
*/
|
|
20
21
|
shaka.polyfill.Aria = class {
|
|
21
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Install the polyfill if needed.
|
|
24
|
+
* @export
|
|
25
|
+
*/
|
|
22
26
|
static install() {
|
|
23
27
|
// eslint-disable-next-line no-restricted-syntax
|
|
24
28
|
if (Object.getOwnPropertyDescriptor(Element.prototype, 'ariaHidden')) {
|
|
@@ -48,7 +52,13 @@ shaka.polyfill.Aria = class {
|
|
|
48
52
|
* @private
|
|
49
53
|
*/
|
|
50
54
|
static addARIAMixinAttribute_(name) {
|
|
51
|
-
const
|
|
55
|
+
const baseName = name.toLowerCase().replace(/^aria/, '');
|
|
56
|
+
// NOTE: All the attributes listed in the method above begin with "aria".
|
|
57
|
+
// However, to add extra protection against the possibility of XSS attacks
|
|
58
|
+
// through this method, this enforces "aria-" at the beginning of the
|
|
59
|
+
// snake-case name, even if somehow "aria" were missing from the input.
|
|
60
|
+
const snakeCaseName = `aria-${baseName}`;
|
|
61
|
+
|
|
52
62
|
/* eslint-disable no-restricted-syntax */
|
|
53
63
|
Object.defineProperty(Element.prototype, name, {
|
|
54
64
|
get() {
|
|
@@ -13,12 +13,14 @@ goog.require('shaka.polyfill');
|
|
|
13
13
|
* @see https://wicg.github.io/encrypted-media-encryption-scheme/
|
|
14
14
|
* @see https://github.com/w3c/encrypted-media/pull/457
|
|
15
15
|
* @see https://github.com/google/eme-encryption-scheme-polyfill
|
|
16
|
+
* @export
|
|
16
17
|
*/
|
|
17
18
|
shaka.polyfill.EncryptionScheme = class {
|
|
18
19
|
/**
|
|
19
20
|
* Install the polyfill if needed.
|
|
20
21
|
*
|
|
21
22
|
* @suppress {missingRequire}
|
|
23
|
+
* @export
|
|
22
24
|
*/
|
|
23
25
|
static install() {
|
|
24
26
|
EncryptionSchemePolyfills.install();
|
|
@@ -14,10 +14,12 @@ goog.require('shaka.polyfill');
|
|
|
14
14
|
* Many browsers have prefixed fullscreen methods on Element and document.
|
|
15
15
|
* See {@link https://mzl.la/2K0xcHo Using fullscreen mode} on MDN for more
|
|
16
16
|
* information.
|
|
17
|
+
* @export
|
|
17
18
|
*/
|
|
18
19
|
shaka.polyfill.Fullscreen = class {
|
|
19
20
|
/**
|
|
20
21
|
* Install the polyfill if needed.
|
|
22
|
+
* @export
|
|
21
23
|
*/
|
|
22
24
|
static install() {
|
|
23
25
|
if (!window.Document) {
|
|
@@ -11,10 +11,12 @@ goog.require('shaka.polyfill');
|
|
|
11
11
|
/**
|
|
12
12
|
* @summary A polyfill to patch math round bug on some browsers.
|
|
13
13
|
* @see https://stackoverflow.com/q/12830742
|
|
14
|
+
* @export
|
|
14
15
|
*/
|
|
15
16
|
shaka.polyfill.MathRound = class {
|
|
16
17
|
/**
|
|
17
18
|
* Install the polyfill if needed.
|
|
19
|
+
* @export
|
|
18
20
|
*/
|
|
19
21
|
static install() {
|
|
20
22
|
shaka.log.debug('mathRound.install');
|
|
@@ -15,31 +15,49 @@ goog.require('shaka.util.Platform');
|
|
|
15
15
|
* @summary A polyfill to provide navigator.mediaCapabilities on all browsers.
|
|
16
16
|
* This is necessary for Tizen 3, Xbox One and possibly others we have yet to
|
|
17
17
|
* discover.
|
|
18
|
+
* @export
|
|
18
19
|
*/
|
|
19
20
|
shaka.polyfill.MediaCapabilities = class {
|
|
20
21
|
/**
|
|
21
22
|
* Install the polyfill if needed.
|
|
23
|
+
* @suppress {const}
|
|
24
|
+
* @export
|
|
22
25
|
*/
|
|
23
26
|
static install() {
|
|
24
|
-
shaka.log.debug('MediaCapabilities: install');
|
|
25
|
-
|
|
26
27
|
// Since MediaCapabilities is not fully supported on Chromecast yet, we
|
|
27
28
|
// should always install polyfill for Chromecast.
|
|
28
29
|
// TODO: re-evaluate MediaCapabilities in the future versions of Chromecast.
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
// Since MediaCapabilities implementation is buggy in Apple browsers, we
|
|
31
|
+
// should always install polyfill for Apple browsers.
|
|
32
|
+
// See: https://github.com/google/shaka-player/issues/3530
|
|
33
|
+
// TODO: re-evaluate MediaCapabilities in the future versions of Apple
|
|
34
|
+
// Browsers.
|
|
35
|
+
// Since MediaCapabilities implementation is buggy in PS5 browsers, we
|
|
36
|
+
// should always install polyfill for PS5 browsers.
|
|
37
|
+
// See: https://github.com/google/shaka-player/issues/3582
|
|
38
|
+
// TODO: re-evaluate MediaCapabilities in the future versions of PS5
|
|
39
|
+
// Browsers.
|
|
40
|
+
if (!shaka.util.Platform.isChromecast() &&
|
|
41
|
+
!shaka.util.Platform.isApple() &&
|
|
42
|
+
!shaka.util.Platform.isPS5() &&
|
|
43
|
+
navigator.mediaCapabilities) {
|
|
44
|
+
shaka.log.info(
|
|
31
45
|
'MediaCapabilities: Native mediaCapabilities support found.');
|
|
32
46
|
return;
|
|
33
|
-
} else if (!window.MediaSource) {
|
|
34
|
-
shaka.log.debug(
|
|
35
|
-
'MediaSource must be available to install mediaCapabilities ',
|
|
36
|
-
'polyfill.');
|
|
37
|
-
return;
|
|
38
47
|
}
|
|
39
48
|
|
|
49
|
+
shaka.log.info('MediaCapabilities: install');
|
|
50
|
+
|
|
40
51
|
if (!navigator.mediaCapabilities) {
|
|
41
52
|
navigator.mediaCapabilities = /** @type {!MediaCapabilities} */ ({});
|
|
42
53
|
}
|
|
54
|
+
|
|
55
|
+
// Keep the patched MediaCapabilities object from being garbage-collected in
|
|
56
|
+
// Safari.
|
|
57
|
+
// See https://github.com/google/shaka-player/issues/3696#issuecomment-1009472718
|
|
58
|
+
shaka.polyfill.MediaCapabilities.originalMcap =
|
|
59
|
+
navigator.mediaCapabilities;
|
|
60
|
+
|
|
43
61
|
navigator.mediaCapabilities.decodingInfo =
|
|
44
62
|
shaka.polyfill.MediaCapabilities.decodingInfo_;
|
|
45
63
|
}
|
|
@@ -62,21 +80,45 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
62
80
|
return res;
|
|
63
81
|
}
|
|
64
82
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const contentType = mediaDecodingConfig['video'].contentType;
|
|
68
|
-
const isSupported = MediaSource.isTypeSupported(contentType);
|
|
69
|
-
if (!isSupported) {
|
|
83
|
+
if (mediaDecodingConfig.type == 'media-source') {
|
|
84
|
+
if (!shaka.util.Platform.supportsMediaSource()) {
|
|
70
85
|
return res;
|
|
71
86
|
}
|
|
72
|
-
|
|
87
|
+
// Use 'MediaSource.isTypeSupported' to check if the stream is supported.
|
|
88
|
+
if (mediaDecodingConfig['video']) {
|
|
89
|
+
const contentType = mediaDecodingConfig['video'].contentType;
|
|
90
|
+
const isSupported = MediaSource.isTypeSupported(contentType);
|
|
91
|
+
if (!isSupported) {
|
|
92
|
+
return res;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
73
95
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
96
|
+
if (mediaDecodingConfig['audio']) {
|
|
97
|
+
const contentType = mediaDecodingConfig['audio'].contentType;
|
|
98
|
+
const isSupported = MediaSource.isTypeSupported(contentType);
|
|
99
|
+
if (!isSupported) {
|
|
100
|
+
return res;
|
|
101
|
+
}
|
|
79
102
|
}
|
|
103
|
+
} else if (mediaDecodingConfig.type == 'file') {
|
|
104
|
+
if (mediaDecodingConfig['video']) {
|
|
105
|
+
const contentType = mediaDecodingConfig['video'].contentType;
|
|
106
|
+
const isSupported = shaka.util.Platform.supportsMediaType(contentType);
|
|
107
|
+
if (!isSupported) {
|
|
108
|
+
return res;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (mediaDecodingConfig['audio']) {
|
|
113
|
+
const contentType = mediaDecodingConfig['audio'].contentType;
|
|
114
|
+
const isSupported = shaka.util.Platform.supportsMediaType(contentType);
|
|
115
|
+
if (!isSupported) {
|
|
116
|
+
return res;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
// Otherwise not supported.
|
|
121
|
+
return res;
|
|
80
122
|
}
|
|
81
123
|
|
|
82
124
|
if (!mediaDecodingConfig.keySystemConfiguration) {
|
|
@@ -118,7 +160,7 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
118
160
|
sessionTypes: mediaCapkeySystemConfig.sessionTypes,
|
|
119
161
|
};
|
|
120
162
|
|
|
121
|
-
// Only add the audio video
|
|
163
|
+
// Only add the audio video capabilities if they have valid data.
|
|
122
164
|
// Otherwise the query will fail.
|
|
123
165
|
if (audioCapabilities.length) {
|
|
124
166
|
mediaKeySystemConfig.audioCapabilities = audioCapabilities;
|
|
@@ -145,6 +187,16 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
145
187
|
}
|
|
146
188
|
};
|
|
147
189
|
|
|
190
|
+
/**
|
|
191
|
+
* A copy of the MediaCapabilities instance, to prevent Safari from
|
|
192
|
+
* garbage-collecting the polyfilled method on it. We make it public and export
|
|
193
|
+
* it to ensure that it is not stripped out by the compiler.
|
|
194
|
+
*
|
|
195
|
+
* @type {MediaCapabilities}
|
|
196
|
+
* @export
|
|
197
|
+
*/
|
|
198
|
+
shaka.polyfill.MediaCapabilities.originalMcap = null;
|
|
199
|
+
|
|
148
200
|
// Install at a lower priority than MediaSource polyfill, so that we have
|
|
149
201
|
// MediaSource available first.
|
|
150
202
|
shaka.polyfill.register(shaka.polyfill.MediaCapabilities.install, -1);
|
|
@@ -13,10 +13,12 @@ goog.require('shaka.util.Platform');
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* @summary A polyfill to patch MSE bugs.
|
|
16
|
+
* @export
|
|
16
17
|
*/
|
|
17
18
|
shaka.polyfill.MediaSource = class {
|
|
18
19
|
/**
|
|
19
20
|
* Install the polyfill if needed.
|
|
21
|
+
* @export
|
|
20
22
|
*/
|
|
21
23
|
static install() {
|
|
22
24
|
shaka.log.debug('MediaSource.install');
|
|
@@ -206,6 +208,12 @@ shaka.polyfill.MediaSource = class {
|
|
|
206
208
|
static patchVp09_() {
|
|
207
209
|
const originalIsTypeSupported = MediaSource.isTypeSupported;
|
|
208
210
|
|
|
211
|
+
if (shaka.util.Platform.isWebOS()) {
|
|
212
|
+
// Don't do this on LG webOS as otherwise it is unable
|
|
213
|
+
// to play vp09 at all.
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
209
217
|
MediaSource.isTypeSupported = (mimeType) => {
|
|
210
218
|
// Split the MIME type into its various parameters.
|
|
211
219
|
const pieces = mimeType.split(/ *; */);
|