shaka-player 4.2.3 → 4.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/workflows/sync-labels.yaml +1 -1
- package/.github/workflows/update-issues.yaml +1 -1
- package/AUTHORS +5 -0
- package/CHANGELOG.md +85 -39
- package/CONTRIBUTORS +5 -0
- package/README.md +14 -0
- package/demo/common/assets.js +58 -0
- package/demo/common/message_ids.js +22 -1
- package/demo/config.js +89 -5
- package/demo/demo.less +7 -1
- package/demo/index.html +8 -1
- package/demo/locales/en.json +21 -1
- package/demo/locales/source.json +82 -2
- package/demo/main.js +57 -1
- package/demo/search.js +2 -0
- package/demo/visualizer.js +337 -0
- package/demo/visualizer_button.js +103 -0
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +83 -58
- package/dist/demo.compiled.debug.map +4 -4
- package/dist/demo.compiled.js +83 -58
- package/dist/demo.compiled.map +4 -4
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +18 -11
- package/dist/locales.js +32 -32
- package/dist/receiver.compiled.debug.js +23 -19
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +23 -19
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +178 -27
- package/dist/shaka-player.compiled.debug.d.ts +178 -27
- package/dist/shaka-player.compiled.debug.externs.js +163 -32
- package/dist/shaka-player.compiled.debug.js +488 -422
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +163 -32
- package/dist/shaka-player.compiled.js +895 -857
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +178 -27
- package/dist/shaka-player.ui.debug.d.ts +178 -27
- package/dist/shaka-player.ui.debug.externs.js +163 -32
- package/dist/shaka-player.ui.debug.js +519 -450
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +163 -32
- package/dist/shaka-player.ui.js +1053 -1015
- package/dist/shaka-player.ui.map +4 -4
- package/docs/design/lcevc-architecture.png +0 -0
- package/docs/design/lcevc-demo.png +0 -0
- package/docs/design/lcevc-integration.md +72 -0
- package/docs/tutorials/fairplay.md +7 -0
- package/docs/tutorials/faq.md +1 -1
- package/docs/tutorials/ui.md +7 -7
- package/docs/tutorials/upgrade.md +6 -0
- package/externs/ima.js +13 -1
- package/externs/lcevc.js +81 -0
- package/externs/mux.js +4 -42
- package/externs/shaka/ads.js +11 -1
- package/externs/shaka/manifest_parser.js +5 -1
- package/externs/shaka/player.js +178 -15
- package/lib/abr/simple_abr_manager.js +7 -0
- package/lib/ads/ad_manager.js +0 -8
- package/lib/ads/client_side_ad.js +15 -0
- package/lib/ads/client_side_ad_manager.js +21 -8
- package/lib/ads/server_side_ad.js +15 -0
- package/lib/ads/server_side_ad_manager.js +3 -3
- package/lib/cast/cast_proxy.js +2 -1
- package/lib/cast/cast_receiver.js +19 -5
- package/lib/cast/cast_sender.js +2 -1
- package/lib/cast/cast_utils.js +18 -6
- package/lib/cea/dummy_cea_parser.js +28 -0
- package/lib/cea/i_cea_parser.js +14 -2
- package/lib/cea/mp4_cea_parser.js +87 -5
- package/lib/config/auto_show_text.js +33 -0
- package/lib/dash/dash_parser.js +1 -1
- package/lib/hls/hls_parser.js +799 -376
- package/lib/lcevc/lcevc_dil.js +152 -0
- package/lib/media/closed_caption_parser.js +9 -6
- package/lib/media/drm_engine.js +14 -6
- package/lib/media/media_source_engine.js +112 -26
- package/lib/media/playhead.js +23 -1
- package/lib/media/presentation_timeline.js +26 -11
- package/lib/media/segment_reference.js +49 -1
- package/lib/media/streaming_engine.js +145 -24
- package/lib/media/transmuxer.js +79 -18
- package/lib/offline/storage.js +1 -0
- package/lib/player.js +477 -85
- package/lib/polyfill/patchedmediakeys_apple.js +55 -1
- package/lib/text/ui_text_displayer.js +5 -5
- package/lib/text/vtt_text_parser.js +35 -1
- package/lib/util/dom_utils.js +0 -1
- package/lib/util/error.js +17 -0
- package/lib/util/fake_event.js +2 -0
- package/lib/util/id3_utils.js +300 -0
- package/lib/util/manifest_parser_utils.js +4 -0
- package/lib/util/mime_utils.js +3 -6
- package/lib/util/mp4_box_parsers.js +15 -5
- package/lib/util/platform.js +9 -1
- package/lib/util/player_configuration.js +23 -1
- package/lib/util/ts_parser.js +499 -0
- package/maintained-branches.md +2 -2
- package/package.json +4 -1
- package/roadmap.md +27 -7
- package/test/ads/ad_manager_unit.js +180 -29
- package/test/cast/cast_receiver_integration.js +97 -16
- package/test/cast/cast_receiver_unit.js +17 -12
- package/test/cast/cast_utils_unit.js +4 -1
- package/test/dash/dash_parser_content_protection_unit.js +1 -0
- package/test/dash/dash_parser_live_unit.js +1 -0
- package/test/dash/dash_parser_manifest_unit.js +93 -1
- package/test/dash/dash_parser_segment_base_unit.js +1 -0
- package/test/dash/dash_parser_segment_list_unit.js +1 -0
- package/test/dash/dash_parser_segment_template_unit.js +1 -0
- package/test/demo/demo_unit.js +2 -1
- package/test/hls/hls_live_unit.js +253 -19
- package/test/hls/hls_parser_unit.js +500 -12
- package/test/media/closed_caption_parser_unit.js +2 -1
- package/test/media/drm_engine_integration.js +3 -1
- package/test/media/media_source_engine_integration.js +102 -6
- package/test/media/media_source_engine_unit.js +10 -5
- package/test/media/playhead_unit.js +41 -0
- package/test/media/streaming_engine_integration.js +17 -1
- package/test/media/streaming_engine_unit.js +180 -24
- package/test/media/transmuxer_integration.js +21 -10
- package/test/player_integration.js +224 -82
- package/test/player_unit.js +92 -2
- package/test/polyfill/patchedmediakeys_apple_unit.js +124 -0
- package/test/test/assets/id3-metadata.aac +0 -0
- package/test/test/assets/id3-metadata.ts +0 -0
- package/test/test/assets/thumbnails-sprites.vtt +10 -0
- package/test/test/assets/thumbnails.vtt +10 -0
- package/test/test/util/dash_parser_util.js +2 -0
- package/test/test/util/fake_ad.js +21 -0
- package/test/test/util/fake_media_source_engine.js +7 -0
- package/test/test/util/id3_generator.js +100 -0
- package/test/test/util/manifest_generator.js +3 -6
- package/test/test/util/simple_fakes.js +7 -0
- package/test/test/util/stream_generator.js +38 -0
- package/test/test/util/test_scheme.js +43 -14
- package/test/text/vtt_text_parser_unit.js +11 -0
- package/test/ui/text_displayer_layout_unit.js +14 -0
- package/test/util/id3_utils_unit.js +116 -0
- package/test/util/ts_parser_unit.js +77 -0
- package/ui/controls.js +4 -1
- package/ui/less/containers.less +7 -0
- package/ui/settings_menu.js +11 -2
- package/ui/ui.js +43 -4
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
goog.provide('shaka.lcevc.Dil');
|
|
8
|
+
goog.require('shaka.log');
|
|
9
|
+
goog.require('shaka.util.IReleasable');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @summary
|
|
13
|
+
* lcevcDil - (MPEG-5 Part 2 LCEVC - Decoder Integration Layer) provides
|
|
14
|
+
* all the operations related to the enhancement and rendering
|
|
15
|
+
* of LCEVC enabled streams and on to a canvas.
|
|
16
|
+
* @implements {shaka.util.IReleasable}
|
|
17
|
+
* @export
|
|
18
|
+
*/
|
|
19
|
+
shaka.lcevc.Dil = class {
|
|
20
|
+
/**
|
|
21
|
+
* @param {HTMLVideoElement} media The video element that will be attached to
|
|
22
|
+
* LCEVC Dil for input.
|
|
23
|
+
* @param {HTMLCanvasElement} canvas The canvas element that will be attached
|
|
24
|
+
* to LCEVC Dil to render the enhanced frames.
|
|
25
|
+
* @param {shaka.extern.LcevcConfiguration} dilConfig The LCEVC DIL
|
|
26
|
+
* config object to initialize the LCEVC DIL.
|
|
27
|
+
*/
|
|
28
|
+
constructor(media, canvas, dilConfig) {
|
|
29
|
+
/** @private {?LcevcDil.LcevcDIL} */
|
|
30
|
+
this.dil_ = null;
|
|
31
|
+
|
|
32
|
+
/** @private {number} */
|
|
33
|
+
this.variantId_ = -1;
|
|
34
|
+
|
|
35
|
+
/** @private {HTMLVideoElement} */
|
|
36
|
+
this.media_ = media;
|
|
37
|
+
|
|
38
|
+
/** @private {HTMLCanvasElement} */
|
|
39
|
+
this.canvas_ = canvas;
|
|
40
|
+
|
|
41
|
+
/** @private {shaka.extern.LcevcConfiguration} */
|
|
42
|
+
this.dilConfig_ = dilConfig;
|
|
43
|
+
|
|
44
|
+
this.create_();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Append data to the LCEVC Dil.
|
|
49
|
+
* @param {BufferSource} data
|
|
50
|
+
*/
|
|
51
|
+
appendBuffer(data) {
|
|
52
|
+
if (this.dil_) {
|
|
53
|
+
this.dil_.appendBuffer(data, 'video', this.variantId_);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Hide the canvas specifically in the case of a DRM Content
|
|
59
|
+
*/
|
|
60
|
+
hideCanvas() {
|
|
61
|
+
if (this.dil_) {
|
|
62
|
+
this.canvas_.classList.add('shaka-hidden');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Create LCEVC Dil.
|
|
68
|
+
* @private
|
|
69
|
+
*/
|
|
70
|
+
create_() {
|
|
71
|
+
if (this.isSupported_() && !this.dil_) {
|
|
72
|
+
if (LcevcDil.SupportObject.webGLSupport(this.canvas_)) {
|
|
73
|
+
// Make sure the canvas is not hidden from a previous playback session.
|
|
74
|
+
this.canvas_.classList.remove('shaka-hidden');
|
|
75
|
+
this.dil_ = new LcevcDil.LcevcDil(
|
|
76
|
+
this.media_,
|
|
77
|
+
this.canvas_,
|
|
78
|
+
this.dilConfig_);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Close LCEVC Dil.
|
|
85
|
+
* @override
|
|
86
|
+
* @export
|
|
87
|
+
*/
|
|
88
|
+
release() {
|
|
89
|
+
if (this.dil_) {
|
|
90
|
+
this.dil_.close();
|
|
91
|
+
this.dil_ = null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Check if the LCEVC Dil lib is present and is supported by the browser.
|
|
97
|
+
* @return {boolean}
|
|
98
|
+
* @private
|
|
99
|
+
*/
|
|
100
|
+
isSupported_() {
|
|
101
|
+
if (typeof libDPIModule === 'undefined') {
|
|
102
|
+
shaka.log.alwaysWarn(
|
|
103
|
+
'Could not Find LCEVC Library dependencies on this page');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (typeof LcevcDil === 'undefined') {
|
|
107
|
+
shaka.log.alwaysWarn('Could not Find LCEVC Library on this page');
|
|
108
|
+
} else {
|
|
109
|
+
if (!LcevcDil.SupportObject.SupportStatus) {
|
|
110
|
+
shaka.log.alwaysWarn(LcevcDil.SupportObject.SupportError);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return typeof LcevcDil !== 'undefined' &&
|
|
115
|
+
typeof libDPIModule !== 'undefined' &&
|
|
116
|
+
this.canvas_ instanceof HTMLCanvasElement &&
|
|
117
|
+
LcevcDil.SupportObject.SupportStatus;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Update current active variant
|
|
122
|
+
* @param {shaka.extern.Track} track
|
|
123
|
+
*/
|
|
124
|
+
updateVariant(track) {
|
|
125
|
+
let containerFormat = shaka.lcevc.Dil.ContainerFormat.MPEG2_TS;
|
|
126
|
+
switch (track.mimeType) {
|
|
127
|
+
case 'video/webm': {
|
|
128
|
+
containerFormat = shaka.lcevc.Dil.ContainerFormat.WEBM;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
case 'video/mp4': {
|
|
132
|
+
containerFormat = shaka.lcevc.Dil.ContainerFormat.MP4;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (this.dil_) {
|
|
137
|
+
this.variantId_ = track.id;
|
|
138
|
+
this.dil_.setLevelSwitching(track.id, true);
|
|
139
|
+
this.dil_.setContainerFormat(containerFormat);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Container Formats.
|
|
146
|
+
* @const @enum {number}
|
|
147
|
+
*/
|
|
148
|
+
shaka.lcevc.Dil.ContainerFormat = {
|
|
149
|
+
MPEG2_TS: 0,
|
|
150
|
+
WEBM: 1,
|
|
151
|
+
MP4: 2,
|
|
152
|
+
};
|
|
@@ -8,6 +8,7 @@ goog.provide('shaka.media.ClosedCaptionParser');
|
|
|
8
8
|
goog.provide('shaka.media.IClosedCaptionParser');
|
|
9
9
|
|
|
10
10
|
goog.require('shaka.cea.CeaDecoder');
|
|
11
|
+
goog.require('shaka.cea.DummyCeaParser');
|
|
11
12
|
goog.require('shaka.cea.Mp4CeaParser');
|
|
12
13
|
goog.require('shaka.util.BufferUtils');
|
|
13
14
|
goog.requireType('shaka.cea.ICaptionDecoder');
|
|
@@ -54,12 +55,14 @@ shaka.media.IClosedCaptionParser = class {
|
|
|
54
55
|
*/
|
|
55
56
|
shaka.media.ClosedCaptionParser = class {
|
|
56
57
|
/** */
|
|
57
|
-
constructor() {
|
|
58
|
-
/**
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
constructor(mimeType) {
|
|
59
|
+
/** @private {!shaka.cea.ICeaParser} */
|
|
60
|
+
this.ceaParser_ = new shaka.cea.DummyCeaParser();
|
|
61
|
+
|
|
62
|
+
if (mimeType.includes('video/mp4')) {
|
|
63
|
+
// MP4 Parser to extract closed caption packets from H.264 video.
|
|
64
|
+
this.ceaParser_ = new shaka.cea.Mp4CeaParser();
|
|
65
|
+
}
|
|
63
66
|
|
|
64
67
|
/**
|
|
65
68
|
* Decoder for decoding CEA-X08 data from closed caption packets.
|
package/lib/media/drm_engine.js
CHANGED
|
@@ -845,7 +845,7 @@ shaka.media.DrmEngine = class {
|
|
|
845
845
|
if (shaka.media.Transmuxer.isSupported(realMimeType)) {
|
|
846
846
|
// This will be handled by the Transmuxer, so use the MIME type that the
|
|
847
847
|
// Transmuxer will produce.
|
|
848
|
-
return shaka.media.Transmuxer.
|
|
848
|
+
return shaka.media.Transmuxer.convertCodecs(stream.type, realMimeType);
|
|
849
849
|
}
|
|
850
850
|
return realMimeType;
|
|
851
851
|
}
|
|
@@ -1113,13 +1113,21 @@ shaka.media.DrmEngine = class {
|
|
|
1113
1113
|
const keys = [];
|
|
1114
1114
|
const keyIds = [];
|
|
1115
1115
|
|
|
1116
|
-
clearKeys.forEach((
|
|
1117
|
-
|
|
1118
|
-
|
|
1116
|
+
clearKeys.forEach((key, keyId) => {
|
|
1117
|
+
let kid = keyId;
|
|
1118
|
+
if (kid.length != 22) {
|
|
1119
|
+
kid = Uint8ArrayUtils.toBase64(
|
|
1120
|
+
Uint8ArrayUtils.fromHex(keyId), false);
|
|
1121
|
+
}
|
|
1122
|
+
let k = key;
|
|
1123
|
+
if (k.length != 22) {
|
|
1124
|
+
k = Uint8ArrayUtils.toBase64(
|
|
1125
|
+
Uint8ArrayUtils.fromHex(key), false);
|
|
1126
|
+
}
|
|
1119
1127
|
const keyObj = {
|
|
1120
1128
|
kty: 'oct',
|
|
1121
|
-
kid:
|
|
1122
|
-
k:
|
|
1129
|
+
kid: kid,
|
|
1130
|
+
k: k,
|
|
1123
1131
|
};
|
|
1124
1132
|
|
|
1125
1133
|
keys.push(keyObj);
|
|
@@ -9,20 +9,25 @@ goog.provide('shaka.media.MediaSourceEngine');
|
|
|
9
9
|
goog.require('goog.asserts');
|
|
10
10
|
goog.require('shaka.log');
|
|
11
11
|
goog.require('shaka.media.ContentWorkarounds');
|
|
12
|
+
goog.require('shaka.media.ClosedCaptionParser');
|
|
12
13
|
goog.require('shaka.media.IClosedCaptionParser');
|
|
13
14
|
goog.require('shaka.media.SegmentReference');
|
|
14
15
|
goog.require('shaka.media.TimeRangesUtils');
|
|
15
16
|
goog.require('shaka.media.Transmuxer');
|
|
16
17
|
goog.require('shaka.text.TextEngine');
|
|
18
|
+
goog.require('shaka.util.BufferUtils');
|
|
17
19
|
goog.require('shaka.util.Destroyer');
|
|
18
20
|
goog.require('shaka.util.Error');
|
|
19
21
|
goog.require('shaka.util.EventManager');
|
|
20
22
|
goog.require('shaka.util.Functional');
|
|
21
23
|
goog.require('shaka.util.IDestroyable');
|
|
24
|
+
goog.require('shaka.util.Id3Utils');
|
|
22
25
|
goog.require('shaka.util.ManifestParserUtils');
|
|
23
26
|
goog.require('shaka.util.MimeUtils');
|
|
24
27
|
goog.require('shaka.util.Platform');
|
|
25
28
|
goog.require('shaka.util.PublicPromise');
|
|
29
|
+
goog.require('shaka.util.TsParser');
|
|
30
|
+
goog.require('shaka.lcevc.Dil');
|
|
26
31
|
|
|
27
32
|
|
|
28
33
|
/**
|
|
@@ -38,21 +43,22 @@ shaka.media.MediaSourceEngine = class {
|
|
|
38
43
|
/**
|
|
39
44
|
* @param {HTMLMediaElement} video The video element, whose source is tied to
|
|
40
45
|
* MediaSource during the lifetime of the MediaSourceEngine.
|
|
41
|
-
* @param {!shaka.media.IClosedCaptionParser} closedCaptionParser
|
|
42
|
-
* The closed caption parser that should be used to parser closed captions
|
|
43
|
-
* from the video stream. MediaSourceEngine takes ownership of the parser.
|
|
44
|
-
* When MediaSourceEngine is destroyed, it will destroy the parser.
|
|
45
46
|
* @param {!shaka.extern.TextDisplayer} textDisplayer
|
|
46
47
|
* The text displayer that will be used with the text engine.
|
|
47
48
|
* MediaSourceEngine takes ownership of the displayer. When
|
|
48
49
|
* MediaSourceEngine is destroyed, it will destroy the displayer.
|
|
49
50
|
* @param {!function(!Array.<shaka.extern.ID3Metadata>, number, ?number)=}
|
|
50
51
|
* onMetadata
|
|
52
|
+
* @param {?shaka.lcevc.Dil} [lcevcDil] Optional - LCEVC Dil Object
|
|
53
|
+
*
|
|
51
54
|
*/
|
|
52
|
-
constructor(video,
|
|
55
|
+
constructor(video, textDisplayer, onMetadata, lcevcDil) {
|
|
53
56
|
/** @private {HTMLMediaElement} */
|
|
54
57
|
this.video_ = video;
|
|
55
58
|
|
|
59
|
+
/** @private {?shaka.extern.MediaSourceConfiguration} */
|
|
60
|
+
this.config_ = null;
|
|
61
|
+
|
|
56
62
|
/** @private {shaka.extern.TextDisplayer} */
|
|
57
63
|
this.textDisplayer_ = textDisplayer;
|
|
58
64
|
|
|
@@ -81,6 +87,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
81
87
|
number, ?number)} */
|
|
82
88
|
this.onMetadata_ = onMetadata || onMetadataNoOp;
|
|
83
89
|
|
|
90
|
+
/** @private {?shaka.lcevc.Dil} */
|
|
91
|
+
this.lcevcDil_ = lcevcDil || null;
|
|
92
|
+
|
|
84
93
|
/**
|
|
85
94
|
* @private {!Object.<string,
|
|
86
95
|
* !Array.<shaka.media.MediaSourceEngine.Operation>>}
|
|
@@ -93,8 +102,8 @@ shaka.media.MediaSourceEngine = class {
|
|
|
93
102
|
/** @private {!Object.<string, !shaka.media.Transmuxer>} */
|
|
94
103
|
this.transmuxers_ = {};
|
|
95
104
|
|
|
96
|
-
/** @private {shaka.media.IClosedCaptionParser} */
|
|
97
|
-
this.captionParser_ =
|
|
105
|
+
/** @private {?shaka.media.IClosedCaptionParser} */
|
|
106
|
+
this.captionParser_ = null;
|
|
98
107
|
|
|
99
108
|
/** @private {!shaka.util.PublicPromise} */
|
|
100
109
|
this.mediaSourceOpen_ = new shaka.util.PublicPromise();
|
|
@@ -291,6 +300,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
291
300
|
this.video_ = null;
|
|
292
301
|
}
|
|
293
302
|
|
|
303
|
+
this.config_ = null;
|
|
294
304
|
this.mediaSource_ = null;
|
|
295
305
|
this.textEngine_ = null;
|
|
296
306
|
this.textDisplayer_ = null;
|
|
@@ -305,6 +315,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
305
315
|
}
|
|
306
316
|
}
|
|
307
317
|
this.queues_ = {};
|
|
318
|
+
|
|
319
|
+
// This object is owned by Player
|
|
320
|
+
this.lcevcDil_ = null;
|
|
308
321
|
}
|
|
309
322
|
|
|
310
323
|
/**
|
|
@@ -325,15 +338,16 @@ shaka.media.MediaSourceEngine = class {
|
|
|
325
338
|
* shaka.extern.Stream>} streamsByType
|
|
326
339
|
* A map of content types to streams. All streams must be supported
|
|
327
340
|
* according to MediaSourceEngine.isStreamSupported.
|
|
328
|
-
* @param {boolean}
|
|
329
|
-
* If true, this will transmux TS content even if it is natively
|
|
341
|
+
* @param {boolean} forceTransmux
|
|
342
|
+
* If true, this will transmux AAC and TS content even if it is natively
|
|
343
|
+
* supported.
|
|
330
344
|
* @param {boolean=} sequenceMode
|
|
331
345
|
* If true, the media segments are appended to the SourceBuffer in strict
|
|
332
346
|
* sequence.
|
|
333
347
|
*
|
|
334
348
|
* @return {!Promise}
|
|
335
349
|
*/
|
|
336
|
-
async init(streamsByType,
|
|
350
|
+
async init(streamsByType, forceTransmux, sequenceMode=false) {
|
|
337
351
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
338
352
|
|
|
339
353
|
await this.mediaSourceOpen_;
|
|
@@ -351,14 +365,15 @@ shaka.media.MediaSourceEngine = class {
|
|
|
351
365
|
if (contentType == ContentType.TEXT) {
|
|
352
366
|
this.reinitText(mimeType, sequenceMode);
|
|
353
367
|
} else {
|
|
354
|
-
if ((
|
|
368
|
+
if ((forceTransmux || !MediaSource.isTypeSupported(mimeType)) &&
|
|
355
369
|
shaka.media.Transmuxer.isSupported(mimeType, contentType)) {
|
|
356
|
-
this.transmuxers_[contentType] =
|
|
370
|
+
this.transmuxers_[contentType] =
|
|
371
|
+
new shaka.media.Transmuxer(mimeType);
|
|
357
372
|
mimeType =
|
|
358
|
-
shaka.media.Transmuxer.
|
|
373
|
+
shaka.media.Transmuxer.convertCodecs(contentType, mimeType);
|
|
359
374
|
}
|
|
360
|
-
|
|
361
|
-
const sourceBuffer = this.mediaSource_.addSourceBuffer(
|
|
375
|
+
const type = mimeType + this.config_.sourceBufferExtraFeatures;
|
|
376
|
+
const sourceBuffer = this.mediaSource_.addSourceBuffer(type);
|
|
362
377
|
|
|
363
378
|
this.eventManager_.listen(
|
|
364
379
|
sourceBuffer, 'error',
|
|
@@ -374,6 +389,16 @@ shaka.media.MediaSourceEngine = class {
|
|
|
374
389
|
}
|
|
375
390
|
}
|
|
376
391
|
|
|
392
|
+
/**
|
|
393
|
+
* Called by the Player to provide an updated configuration any time it
|
|
394
|
+
* changes. Must be called at least once before init().
|
|
395
|
+
*
|
|
396
|
+
* @param {shaka.extern.MediaSourceConfiguration} config
|
|
397
|
+
*/
|
|
398
|
+
configure(config) {
|
|
399
|
+
this.config_ = config;
|
|
400
|
+
}
|
|
401
|
+
|
|
377
402
|
/**
|
|
378
403
|
* Reinitialize the TextEngine for a new text type.
|
|
379
404
|
* @param {string} mimeType
|
|
@@ -509,6 +534,17 @@ shaka.media.MediaSourceEngine = class {
|
|
|
509
534
|
}
|
|
510
535
|
}
|
|
511
536
|
|
|
537
|
+
/**
|
|
538
|
+
* Create a new closed caption parser. This will ONLY be replaced by tests as
|
|
539
|
+
* a way to inject fake closed caption parser instances.
|
|
540
|
+
*
|
|
541
|
+
* @param {string} mimeType
|
|
542
|
+
* @return {!shaka.media.IClosedCaptionParser}
|
|
543
|
+
*/
|
|
544
|
+
getCaptionParser(mimeType) {
|
|
545
|
+
return new shaka.media.ClosedCaptionParser(mimeType);
|
|
546
|
+
}
|
|
547
|
+
|
|
512
548
|
/**
|
|
513
549
|
* Enqueue an operation to append data to the SourceBuffer.
|
|
514
550
|
* Start and end times are needed for TextEngine, but not for MediaSource.
|
|
@@ -544,7 +580,44 @@ shaka.media.MediaSourceEngine = class {
|
|
|
544
580
|
return;
|
|
545
581
|
}
|
|
546
582
|
|
|
583
|
+
const uint8ArrayData = shaka.util.BufferUtils.toUint8(data);
|
|
584
|
+
let mimeType = this.sourceBufferTypes_[contentType];
|
|
585
|
+
if (this.transmuxers_[contentType]) {
|
|
586
|
+
mimeType = this.transmuxers_[contentType].getOrginalMimeType();
|
|
587
|
+
}
|
|
588
|
+
if (shaka.util.TsParser.probe(uint8ArrayData)) {
|
|
589
|
+
const metadata = new shaka.util.TsParser().parse(uint8ArrayData)
|
|
590
|
+
.getMetadata();
|
|
591
|
+
if (metadata.length) {
|
|
592
|
+
const timestampOffset =
|
|
593
|
+
this.sourceBuffers_[contentType].timestampOffset;
|
|
594
|
+
this.onMetadata_(metadata, timestampOffset,
|
|
595
|
+
reference ? reference.endTime : null);
|
|
596
|
+
}
|
|
597
|
+
} else {
|
|
598
|
+
if (shaka.media.MediaSourceEngine.RAW_FORMATS.includes(mimeType)) {
|
|
599
|
+
const frames = shaka.util.Id3Utils.getID3Frames(uint8ArrayData);
|
|
600
|
+
if (frames.length && reference) {
|
|
601
|
+
/** @private {shaka.extern.ID3Metadata} */
|
|
602
|
+
const metadata = {
|
|
603
|
+
cueTime: reference.startTime,
|
|
604
|
+
data: uint8ArrayData,
|
|
605
|
+
frames: frames,
|
|
606
|
+
dts: reference.startTime,
|
|
607
|
+
pts: reference.startTime,
|
|
608
|
+
};
|
|
609
|
+
this.onMetadata_([metadata], /* offset= */ 0, reference.endTime);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
547
614
|
if (this.transmuxers_[contentType]) {
|
|
615
|
+
// When seeked we should reset the transmuxer captionstreams
|
|
616
|
+
// so it does not ignores the captions from previous segments
|
|
617
|
+
if (seeked) {
|
|
618
|
+
this.transmuxers_[contentType].resetCaptions();
|
|
619
|
+
}
|
|
620
|
+
|
|
548
621
|
const transmuxedData =
|
|
549
622
|
await this.transmuxers_[contentType].transmux(data);
|
|
550
623
|
// For HLS CEA-608/708 CLOSED-CAPTIONS, text data is embedded in
|
|
@@ -552,15 +625,6 @@ shaka.media.MediaSourceEngine = class {
|
|
|
552
625
|
if (!this.textEngine_) {
|
|
553
626
|
this.reinitText('text/vtt', this.sequenceMode_);
|
|
554
627
|
}
|
|
555
|
-
|
|
556
|
-
if (transmuxedData.metadata) {
|
|
557
|
-
const timestampOffset =
|
|
558
|
-
this.sourceBuffers_[contentType].timestampOffset;
|
|
559
|
-
this.onMetadata_(
|
|
560
|
-
transmuxedData.metadata,
|
|
561
|
-
timestampOffset,
|
|
562
|
-
reference ? reference.endTime : null);
|
|
563
|
-
}
|
|
564
628
|
// This doesn't work for native TS support (ex. Edge/Chromecast),
|
|
565
629
|
// since no transmuxing is needed for native TS.
|
|
566
630
|
if (transmuxedData.captions && transmuxedData.captions.length) {
|
|
@@ -576,10 +640,13 @@ shaka.media.MediaSourceEngine = class {
|
|
|
576
640
|
}
|
|
577
641
|
|
|
578
642
|
data = transmuxedData.data;
|
|
579
|
-
} else if (hasClosedCaptions) {
|
|
643
|
+
} else if (hasClosedCaptions && contentType == ContentType.VIDEO) {
|
|
580
644
|
if (!this.textEngine_) {
|
|
581
645
|
this.reinitText('text/vtt', this.sequenceMode_);
|
|
582
646
|
}
|
|
647
|
+
if (!this.captionParser_) {
|
|
648
|
+
this.captionParser_ = this.getCaptionParser(mimeType);
|
|
649
|
+
}
|
|
583
650
|
// If it is the init segment for closed captions, initialize the closed
|
|
584
651
|
// caption parser.
|
|
585
652
|
if (!reference) {
|
|
@@ -773,7 +840,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
773
840
|
* Fully reset the state of the caption parser owned by MediaSourceEngine.
|
|
774
841
|
*/
|
|
775
842
|
resetCaptionParser() {
|
|
776
|
-
this.captionParser_
|
|
843
|
+
if (this.captionParser_) {
|
|
844
|
+
this.captionParser_.reset();
|
|
845
|
+
}
|
|
777
846
|
}
|
|
778
847
|
|
|
779
848
|
/**
|
|
@@ -912,6 +981,15 @@ shaka.media.MediaSourceEngine = class {
|
|
|
912
981
|
* @private
|
|
913
982
|
*/
|
|
914
983
|
append_(contentType, data) {
|
|
984
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
985
|
+
|
|
986
|
+
// Append only video data to the LCEVC Dil.
|
|
987
|
+
if (contentType == ContentType.VIDEO && this.lcevcDil_) {
|
|
988
|
+
// Append video buffers to the LCEVC Dil for parsing and storing
|
|
989
|
+
// of LCEVC data.
|
|
990
|
+
this.lcevcDil_.appendBuffer(data);
|
|
991
|
+
}
|
|
992
|
+
|
|
915
993
|
// This will trigger an 'updateend' event.
|
|
916
994
|
this.sourceBuffers_[contentType].appendBuffer(data);
|
|
917
995
|
}
|
|
@@ -1277,6 +1355,14 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1277
1355
|
|
|
1278
1356
|
return segment;
|
|
1279
1357
|
}
|
|
1358
|
+
|
|
1359
|
+
/**
|
|
1360
|
+
* Update LCEVC DIL object when ready for LCEVC Decodes
|
|
1361
|
+
* @param {?shaka.lcevc.Dil} lcevcDil
|
|
1362
|
+
*/
|
|
1363
|
+
updateLcevcDil(lcevcDil) {
|
|
1364
|
+
this.lcevcDil_ = lcevcDil;
|
|
1365
|
+
}
|
|
1280
1366
|
};
|
|
1281
1367
|
|
|
1282
1368
|
|
package/lib/media/playhead.js
CHANGED
|
@@ -33,6 +33,14 @@ goog.requireType('shaka.media.PresentationTimeline');
|
|
|
33
33
|
* @interface
|
|
34
34
|
*/
|
|
35
35
|
shaka.media.Playhead = class {
|
|
36
|
+
/**
|
|
37
|
+
* Called when the Player is ready to begin playback. Anything that depends
|
|
38
|
+
* on setStartTime() should be done here, not in the constructor.
|
|
39
|
+
*
|
|
40
|
+
* @see https://github.com/shaka-project/shaka-player/issues/4244
|
|
41
|
+
*/
|
|
42
|
+
ready() {}
|
|
43
|
+
|
|
36
44
|
/**
|
|
37
45
|
* Set the start time. If the content has already started playback, this will
|
|
38
46
|
* be ignored.
|
|
@@ -90,6 +98,14 @@ shaka.media.SrcEqualsPlayhead = class {
|
|
|
90
98
|
|
|
91
99
|
/** @private {shaka.util.EventManager} */
|
|
92
100
|
this.eventManager_ = new shaka.util.EventManager();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** @override */
|
|
104
|
+
ready() {
|
|
105
|
+
goog.asserts.assert(
|
|
106
|
+
this.mediaElement_ != null,
|
|
107
|
+
'Playhead should not be released before calling ready()',
|
|
108
|
+
);
|
|
93
109
|
|
|
94
110
|
// We listen for the loaded-data-event so that we know when we can
|
|
95
111
|
// interact with |currentTime|.
|
|
@@ -102,6 +118,7 @@ shaka.media.SrcEqualsPlayhead = class {
|
|
|
102
118
|
this.eventManager_.listenOnce(this.mediaElement_, 'seeking', () => {
|
|
103
119
|
this.started_ = true;
|
|
104
120
|
});
|
|
121
|
+
|
|
105
122
|
const currentTime = this.mediaElement_.currentTime;
|
|
106
123
|
// Using the currentTime allows using a negative number in Live HLS
|
|
107
124
|
const newTime = Math.max(0, currentTime + this.startTime_);
|
|
@@ -239,7 +256,12 @@ shaka.media.MediaSourcePlayhead = class {
|
|
|
239
256
|
/** @type {shaka.util.Timer} */
|
|
240
257
|
this.checkWindowTimer_ = new shaka.util.Timer(() => {
|
|
241
258
|
this.onPollWindow_();
|
|
242
|
-
})
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** @override */
|
|
263
|
+
ready() {
|
|
264
|
+
this.checkWindowTimer_.tickEvery(/* seconds= */ 0.25);
|
|
243
265
|
}
|
|
244
266
|
|
|
245
267
|
/** @override */
|
|
@@ -118,6 +118,20 @@ shaka.media.PresentationTimeline = class {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Sets the presentation's start time.
|
|
123
|
+
*
|
|
124
|
+
* @param {number} presentationStartTime The wall-clock time, in seconds,
|
|
125
|
+
* when the presentation started or will start. Only required for live.
|
|
126
|
+
* @export
|
|
127
|
+
*/
|
|
128
|
+
setPresentationStartTime(presentationStartTime) {
|
|
129
|
+
goog.asserts.assert(presentationStartTime >= 0,
|
|
130
|
+
'presentationStartTime must be >= 0');
|
|
131
|
+
this.presentationStartTime_ = presentationStartTime;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
121
135
|
/**
|
|
122
136
|
* Sets the presentation's duration.
|
|
123
137
|
*
|
|
@@ -207,10 +221,10 @@ shaka.media.PresentationTimeline = class {
|
|
|
207
221
|
|
|
208
222
|
|
|
209
223
|
/**
|
|
210
|
-
* Gives PresentationTimeline
|
|
224
|
+
* Gives PresentationTimeline an array of segments so it can size and position
|
|
211
225
|
* the segment availability window, and account for missing segment
|
|
212
|
-
* information.
|
|
213
|
-
*
|
|
226
|
+
* information. These segments do not necessarily need to all be from the
|
|
227
|
+
* same stream.
|
|
214
228
|
*
|
|
215
229
|
* @param {!Array.<!shaka.media.SegmentReference>} references
|
|
216
230
|
* @export
|
|
@@ -220,15 +234,16 @@ shaka.media.PresentationTimeline = class {
|
|
|
220
234
|
return;
|
|
221
235
|
}
|
|
222
236
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
237
|
+
let firstReferenceStartTime = references[0].startTime;
|
|
238
|
+
let lastReferenceEndTime = references[0].endTime;
|
|
239
|
+
for (const reference of references) {
|
|
240
|
+
firstReferenceStartTime = Math.min(
|
|
241
|
+
firstReferenceStartTime, reference.startTime);
|
|
242
|
+
lastReferenceEndTime = Math.max(lastReferenceEndTime, reference.endTime);
|
|
243
|
+
this.maxSegmentDuration_ = Math.max(
|
|
244
|
+
this.maxSegmentDuration_, reference.endTime - reference.startTime);
|
|
245
|
+
}
|
|
226
246
|
this.notifyMinSegmentStartTime(firstReferenceStartTime);
|
|
227
|
-
|
|
228
|
-
this.maxSegmentDuration_ = references.reduce(
|
|
229
|
-
(max, r) => { return Math.max(max, r.endTime - r.startTime); },
|
|
230
|
-
this.maxSegmentDuration_);
|
|
231
|
-
|
|
232
247
|
this.maxSegmentEndTime_ =
|
|
233
248
|
Math.max(this.maxSegmentEndTime_, lastReferenceEndTime);
|
|
234
249
|
|
|
@@ -28,8 +28,9 @@ shaka.media.InitSegmentReference = class {
|
|
|
28
28
|
* segment extends to the end of the resource.
|
|
29
29
|
* @param {null|shaka.extern.MediaQualityInfo=} mediaQuality Information about
|
|
30
30
|
* the quality of the media associated with this init segment.
|
|
31
|
+
* @param {number=} timescale
|
|
31
32
|
*/
|
|
32
|
-
constructor(uris, startByte, endByte, mediaQuality = null) {
|
|
33
|
+
constructor(uris, startByte, endByte, mediaQuality = null, timescale) {
|
|
33
34
|
/** @type {function():!Array.<string>} */
|
|
34
35
|
this.getUris = uris;
|
|
35
36
|
|
|
@@ -41,6 +42,9 @@ shaka.media.InitSegmentReference = class {
|
|
|
41
42
|
|
|
42
43
|
/** @const {shaka.extern.MediaQualityInfo|null} */
|
|
43
44
|
this.mediaQuality = mediaQuality;
|
|
45
|
+
|
|
46
|
+
/** @type {number|undefined} */
|
|
47
|
+
this.timescale = timescale;
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
/**
|
|
@@ -239,6 +243,9 @@ shaka.media.SegmentReference = class {
|
|
|
239
243
|
|
|
240
244
|
/** @type {?shaka.extern.HlsAes128Key} */
|
|
241
245
|
this.hlsAes128Key = hlsAes128Key;
|
|
246
|
+
|
|
247
|
+
/** @type {?shaka.media.SegmentReference.ThumbnailSprite} */
|
|
248
|
+
this.thumbnailSprite = null;
|
|
242
249
|
}
|
|
243
250
|
|
|
244
251
|
/**
|
|
@@ -353,6 +360,26 @@ shaka.media.SegmentReference = class {
|
|
|
353
360
|
markAsUnavailable() {
|
|
354
361
|
this.status = shaka.media.SegmentReference.Status.UNAVAILABLE;
|
|
355
362
|
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Set the segment's thumbnail sprite.
|
|
366
|
+
*
|
|
367
|
+
* @param {shaka.media.SegmentReference.ThumbnailSprite} thumbnailSprite
|
|
368
|
+
* @export
|
|
369
|
+
*/
|
|
370
|
+
setThumbnailSprite(thumbnailSprite) {
|
|
371
|
+
this.thumbnailSprite = thumbnailSprite;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Returns the segment's thumbnail sprite.
|
|
376
|
+
*
|
|
377
|
+
* @return {?shaka.media.SegmentReference.ThumbnailSprite}
|
|
378
|
+
* @export
|
|
379
|
+
*/
|
|
380
|
+
getThumbnailSprite() {
|
|
381
|
+
return this.thumbnailSprite;
|
|
382
|
+
}
|
|
356
383
|
};
|
|
357
384
|
|
|
358
385
|
|
|
@@ -376,3 +403,24 @@ shaka.media.SegmentReference.Status = {
|
|
|
376
403
|
* @typedef {shaka.media.InitSegmentReference|shaka.media.SegmentReference}
|
|
377
404
|
*/
|
|
378
405
|
shaka.media.AnySegmentReference;
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* @typedef {{
|
|
410
|
+
* height: number,
|
|
411
|
+
* positionX: number,
|
|
412
|
+
* positionY: number,
|
|
413
|
+
* width: number
|
|
414
|
+
* }}
|
|
415
|
+
*
|
|
416
|
+
* @property {number} height
|
|
417
|
+
* The thumbnail height in px.
|
|
418
|
+
* @property {number} positionX
|
|
419
|
+
* The thumbnail left position in px.
|
|
420
|
+
* @property {number} positionY
|
|
421
|
+
* The thumbnail top position in px.
|
|
422
|
+
* @property {number} width
|
|
423
|
+
* The thumbnail width in px.
|
|
424
|
+
* @export
|
|
425
|
+
*/
|
|
426
|
+
shaka.media.SegmentReference.ThumbnailSprite;
|