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
|
@@ -8,6 +8,7 @@ goog.provide('shaka.util.PlayerConfiguration');
|
|
|
8
8
|
|
|
9
9
|
goog.require('goog.asserts');
|
|
10
10
|
goog.require('shaka.abr.SimpleAbrManager');
|
|
11
|
+
goog.require('shaka.config.AutoShowText');
|
|
11
12
|
goog.require('shaka.log');
|
|
12
13
|
goog.require('shaka.net.NetworkingEngine');
|
|
13
14
|
goog.require('shaka.util.ConfigUtils');
|
|
@@ -120,6 +121,7 @@ shaka.util.PlayerConfiguration = class {
|
|
|
120
121
|
[element],
|
|
121
122
|
element);
|
|
122
123
|
},
|
|
124
|
+
sequenceMode: false,
|
|
123
125
|
},
|
|
124
126
|
hls: {
|
|
125
127
|
ignoreTextStreamFailures: false,
|
|
@@ -129,6 +131,8 @@ shaka.util.PlayerConfiguration = class {
|
|
|
129
131
|
ignoreManifestProgramDateTime: false,
|
|
130
132
|
mediaPlaylistFullMimeType:
|
|
131
133
|
'video/mp2t; codecs="avc1.42E01E, mp4a.40.2"',
|
|
134
|
+
useSafariBehaviorForLive: true,
|
|
135
|
+
liveSegmentsDelay: 3,
|
|
132
136
|
},
|
|
133
137
|
};
|
|
134
138
|
|
|
@@ -153,7 +157,7 @@ shaka.util.PlayerConfiguration = class {
|
|
|
153
157
|
startAtSegmentBoundary: false,
|
|
154
158
|
gapDetectionThreshold: 0.5,
|
|
155
159
|
durationBackoff: 1,
|
|
156
|
-
|
|
160
|
+
forceTransmux: false,
|
|
157
161
|
// Offset by 5 seconds since Chromecast takes a few seconds to start
|
|
158
162
|
// playing after a seek, even when buffered.
|
|
159
163
|
safeSeekOffset: 5,
|
|
@@ -175,6 +179,7 @@ shaka.util.PlayerConfiguration = class {
|
|
|
175
179
|
dispatchAllEmsgBoxes: false,
|
|
176
180
|
observeQualityChanges: false,
|
|
177
181
|
maxDisabledTime: 30,
|
|
182
|
+
parsePrftBox: false,
|
|
178
183
|
};
|
|
179
184
|
|
|
180
185
|
// WebOS, Tizen, and Chromecast have long hardware pipelines that respond
|
|
@@ -249,6 +254,7 @@ shaka.util.PlayerConfiguration = class {
|
|
|
249
254
|
slowHalfLife: 5,
|
|
250
255
|
},
|
|
251
256
|
restrictToElementSize: false,
|
|
257
|
+
restrictToScreenSize: false,
|
|
252
258
|
ignoreDevicePixelRatio: false,
|
|
253
259
|
};
|
|
254
260
|
|
|
@@ -259,14 +265,29 @@ shaka.util.PlayerConfiguration = class {
|
|
|
259
265
|
useHeaders: false,
|
|
260
266
|
};
|
|
261
267
|
|
|
268
|
+
const lcevc = {
|
|
269
|
+
enabled: false,
|
|
270
|
+
dynamicPerformanceScaling: true,
|
|
271
|
+
logLevel: 0,
|
|
272
|
+
drawLogo: false,
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const mediaSource = {
|
|
276
|
+
sourceBufferExtraFeatures: '',
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const AutoShowText = shaka.config.AutoShowText;
|
|
280
|
+
|
|
262
281
|
/** @type {shaka.extern.PlayerConfiguration} */
|
|
263
282
|
const config = {
|
|
264
283
|
drm: drm,
|
|
265
284
|
manifest: manifest,
|
|
266
285
|
streaming: streaming,
|
|
286
|
+
mediaSource: mediaSource,
|
|
267
287
|
offline: offline,
|
|
268
288
|
abrFactory: () => new shaka.abr.SimpleAbrManager(),
|
|
269
289
|
abr: abr,
|
|
290
|
+
autoShowText: AutoShowText.IF_SUBTITLES_MAY_BE_NEEDED,
|
|
270
291
|
preferredAudioLanguage: '',
|
|
271
292
|
preferredTextLanguage: '',
|
|
272
293
|
preferredVariantRole: '',
|
|
@@ -292,6 +313,7 @@ shaka.util.PlayerConfiguration = class {
|
|
|
292
313
|
playRangeEnd: Infinity,
|
|
293
314
|
textDisplayFactory: () => null,
|
|
294
315
|
cmcd: cmcd,
|
|
316
|
+
lcevc: lcevc,
|
|
295
317
|
};
|
|
296
318
|
|
|
297
319
|
// Add this callback so that we can reference the preferred audio language
|
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
goog.provide('shaka.util.TsParser');
|
|
8
|
+
|
|
9
|
+
goog.require('goog.asserts');
|
|
10
|
+
goog.require('shaka.log');
|
|
11
|
+
goog.require('shaka.util.Id3Utils');
|
|
12
|
+
goog.require('shaka.util.Uint8ArrayUtils');
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @see https://en.wikipedia.org/wiki/MPEG_transport_stream
|
|
17
|
+
* @export
|
|
18
|
+
*/
|
|
19
|
+
shaka.util.TsParser = class {
|
|
20
|
+
/** */
|
|
21
|
+
constructor() {
|
|
22
|
+
/** @private {?number} */
|
|
23
|
+
this.pmtId_ = null;
|
|
24
|
+
|
|
25
|
+
/** @private {boolean} */
|
|
26
|
+
this.pmtParsed_ = false;
|
|
27
|
+
|
|
28
|
+
/** @private {?number} */
|
|
29
|
+
this.videoStartTime_ = null;
|
|
30
|
+
|
|
31
|
+
/** @private {?number} */
|
|
32
|
+
this.videoPid_ = null;
|
|
33
|
+
|
|
34
|
+
/** @private {?string} */
|
|
35
|
+
this.videoCodec_ = null;
|
|
36
|
+
|
|
37
|
+
/** @private {!Array.<Uint8Array>} */
|
|
38
|
+
this.videoData_ = [];
|
|
39
|
+
|
|
40
|
+
/** @private {?number} */
|
|
41
|
+
this.audioStartTime_ = null;
|
|
42
|
+
|
|
43
|
+
/** @private {?number} */
|
|
44
|
+
this.audioPid_ = null;
|
|
45
|
+
|
|
46
|
+
/** @private {?string} */
|
|
47
|
+
this.audioCodec_ = null;
|
|
48
|
+
|
|
49
|
+
/** @private {!Array.<Uint8Array>} */
|
|
50
|
+
this.audioData_ = [];
|
|
51
|
+
|
|
52
|
+
/** @private {?number} */
|
|
53
|
+
this.id3Pid_ = null;
|
|
54
|
+
|
|
55
|
+
/** @private {!Array.<Uint8Array>} */
|
|
56
|
+
this.id3Data_ = [];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Parse the given data
|
|
61
|
+
*
|
|
62
|
+
* @param {Uint8Array} data
|
|
63
|
+
* @return {!shaka.util.TsParser}
|
|
64
|
+
*/
|
|
65
|
+
parse(data) {
|
|
66
|
+
const timescale = shaka.util.TsParser.Timescale_;
|
|
67
|
+
const packetLength = shaka.util.TsParser.PacketLength_;
|
|
68
|
+
|
|
69
|
+
// A TS fragment should contain at least 3 TS packets, a PAT, a PMT, and
|
|
70
|
+
// one PID.
|
|
71
|
+
if (data.length < 3 * packetLength) {
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
const syncOffset = Math.max(0, shaka.util.TsParser.syncOffset(data));
|
|
75
|
+
|
|
76
|
+
const length = data.length - (data.length + syncOffset) % packetLength;
|
|
77
|
+
|
|
78
|
+
let unknownPIDs = false;
|
|
79
|
+
|
|
80
|
+
// loop through TS packets
|
|
81
|
+
for (let start = syncOffset; start < length; start += packetLength) {
|
|
82
|
+
if (data[start] === 0x47) {
|
|
83
|
+
const payloadUnitStartIndicator = !!(data[start + 1] & 0x40);
|
|
84
|
+
// pid is a 13-bit field starting at the last 5 bits of TS[1]
|
|
85
|
+
const pid = ((data[start + 1] & 0x1f) << 8) + data[start + 2];
|
|
86
|
+
const adaptationFieldControl = (data[start + 3] & 0x30) >> 4;
|
|
87
|
+
|
|
88
|
+
// if an adaption field is present, its length is specified by the
|
|
89
|
+
// fifth byte of the TS packet header.
|
|
90
|
+
let offset;
|
|
91
|
+
if (adaptationFieldControl > 1) {
|
|
92
|
+
offset = start + 5 + data[start + 4];
|
|
93
|
+
// continue if there is only adaptation field
|
|
94
|
+
if (offset === start + packetLength) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
offset = start + 4;
|
|
99
|
+
}
|
|
100
|
+
switch (pid) {
|
|
101
|
+
case 0:
|
|
102
|
+
if (payloadUnitStartIndicator) {
|
|
103
|
+
offset += data[offset] + 1;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this.pmtId_ = this.getPmtId_(data, offset);
|
|
107
|
+
break;
|
|
108
|
+
case 17:
|
|
109
|
+
case 0x1fff:
|
|
110
|
+
break;
|
|
111
|
+
case this.pmtId_: {
|
|
112
|
+
if (payloadUnitStartIndicator) {
|
|
113
|
+
offset += data[offset] + 1;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const parsedPIDs = this.parsePMT(data, offset);
|
|
117
|
+
|
|
118
|
+
// only update track id if track PID found while parsing PMT
|
|
119
|
+
// this is to avoid resetting the PID to -1 in case
|
|
120
|
+
// track PID transiently disappears from the stream
|
|
121
|
+
// this could happen in case of transient missing audio samples
|
|
122
|
+
// for example
|
|
123
|
+
// NOTE this is only the PID of the track as found in TS,
|
|
124
|
+
// but we are not using this for MP4 track IDs.
|
|
125
|
+
if (this.videoPid_ == null) {
|
|
126
|
+
this.videoPid_ = parsedPIDs.video;
|
|
127
|
+
this.videoCodec_ = parsedPIDs.videoCodec;
|
|
128
|
+
}
|
|
129
|
+
if (this.audioPid_ == null) {
|
|
130
|
+
this.audioPid_ = parsedPIDs.audio;
|
|
131
|
+
this.audioCodec_ = parsedPIDs.audioCodec;
|
|
132
|
+
}
|
|
133
|
+
if (this.id3Pid_ == null) {
|
|
134
|
+
this.id3Pid_ = parsedPIDs.id3;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (unknownPIDs && !this.pmtParsed_) {
|
|
138
|
+
shaka.log.debug('reparse from beginning');
|
|
139
|
+
unknownPIDs = false;
|
|
140
|
+
// we set it to -188, the += 188 in the for loop will reset
|
|
141
|
+
// start to 0
|
|
142
|
+
start = syncOffset - packetLength;
|
|
143
|
+
}
|
|
144
|
+
this.pmtParsed_ = true;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
case this.videoPid_: {
|
|
148
|
+
const videoData = data.subarray(offset, start + packetLength);
|
|
149
|
+
if (this.videoStartTime_ == null) {
|
|
150
|
+
const pes = this.parsePES(videoData);
|
|
151
|
+
if (pes && pes.pts != null) {
|
|
152
|
+
this.videoStartTime_ = pes.pts / timescale;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
this.videoData_.push(videoData);
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
case this.audioPid_: {
|
|
159
|
+
const audioData = data.subarray(offset, start + packetLength);
|
|
160
|
+
if (this.audioStartTime_ == null) {
|
|
161
|
+
const pes = this.parsePES(audioData);
|
|
162
|
+
if (pes && pes.pts != null) {
|
|
163
|
+
this.audioStartTime_ = pes.pts / timescale;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
this.audioData_.push(audioData);
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
case this.id3Pid_:
|
|
170
|
+
this.id3Data_.push(data.subarray(offset, start + packetLength));
|
|
171
|
+
break;
|
|
172
|
+
default:
|
|
173
|
+
unknownPIDs = true;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
shaka.log.warning('Found TS packet that do not start with 0x47');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return this;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Get the PMT ID from the PAT
|
|
185
|
+
*
|
|
186
|
+
* @param {Uint8Array} data
|
|
187
|
+
* @param {number} offset
|
|
188
|
+
* @return {number}
|
|
189
|
+
* @private
|
|
190
|
+
*/
|
|
191
|
+
getPmtId_(data, offset) {
|
|
192
|
+
// skip the PSI header and parse the first PMT entry
|
|
193
|
+
return ((data[offset + 10] & 0x1f) << 8) | data[offset + 11];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Parse PMT
|
|
198
|
+
*
|
|
199
|
+
* @param {Uint8Array} data
|
|
200
|
+
* @param {number} offset
|
|
201
|
+
* @return {!shaka.util.TsParser.PMT}
|
|
202
|
+
*/
|
|
203
|
+
parsePMT(data, offset) {
|
|
204
|
+
const result = {
|
|
205
|
+
audio: -1,
|
|
206
|
+
video: -1,
|
|
207
|
+
id3: -1,
|
|
208
|
+
audioCodec: '',
|
|
209
|
+
videoCodec: '',
|
|
210
|
+
};
|
|
211
|
+
const sectionLength = ((data[offset + 1] & 0x0f) << 8) | data[offset + 2];
|
|
212
|
+
const tableEnd = offset + 3 + sectionLength - 4;
|
|
213
|
+
// to determine where the table is, we have to figure out how
|
|
214
|
+
// long the program info descriptors are
|
|
215
|
+
const programInfoLength =
|
|
216
|
+
((data[offset + 10] & 0x0f) << 8) | data[offset + 11];
|
|
217
|
+
// advance the offset to the first entry in the mapping table
|
|
218
|
+
offset += 12 + programInfoLength;
|
|
219
|
+
while (offset < tableEnd) {
|
|
220
|
+
const pid = ((data[offset + 1] & 0x1f) << 8) | data[offset + 2];
|
|
221
|
+
switch (data[offset]) {
|
|
222
|
+
// SAMPLE-AES AAC
|
|
223
|
+
case 0xcf:
|
|
224
|
+
break;
|
|
225
|
+
// ISO/IEC 13818-7 ADTS AAC (MPEG-2 lower bit-rate audio)
|
|
226
|
+
case 0x0f:
|
|
227
|
+
if (result.audio === -1) {
|
|
228
|
+
result.audio = pid;
|
|
229
|
+
result.audioCodec = 'aac';
|
|
230
|
+
}
|
|
231
|
+
break;
|
|
232
|
+
// Packetized metadata (ID3)
|
|
233
|
+
case 0x15:
|
|
234
|
+
if (result.id3 === -1) {
|
|
235
|
+
result.id3 = pid;
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
// SAMPLE-AES AVC
|
|
239
|
+
case 0xdb:
|
|
240
|
+
break;
|
|
241
|
+
// ITU-T Rec. H.264 and ISO/IEC 14496-10 (lower bit-rate video)
|
|
242
|
+
case 0x1b:
|
|
243
|
+
if (result.video === -1) {
|
|
244
|
+
result.video = pid;
|
|
245
|
+
result.videoCodec = 'avc';
|
|
246
|
+
}
|
|
247
|
+
break;
|
|
248
|
+
// ISO/IEC 11172-3 (MPEG-1 audio)
|
|
249
|
+
// or ISO/IEC 13818-3 (MPEG-2 halved sample rate audio)
|
|
250
|
+
case 0x03:
|
|
251
|
+
case 0x04:
|
|
252
|
+
if (result.audio === -1) {
|
|
253
|
+
result.audio = pid;
|
|
254
|
+
result.audioCodec = 'mp3';
|
|
255
|
+
}
|
|
256
|
+
break;
|
|
257
|
+
// HEVC
|
|
258
|
+
case 0x24:
|
|
259
|
+
if (result.video === -1) {
|
|
260
|
+
result.video = pid;
|
|
261
|
+
result.videoCodec = 'hvc';
|
|
262
|
+
}
|
|
263
|
+
break;
|
|
264
|
+
default:
|
|
265
|
+
// shaka.log.warning('Unknown stream type:', data[offset]);
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
// move to the next table entry
|
|
269
|
+
// skip past the elementary stream descriptors, if present
|
|
270
|
+
offset += (((data[offset + 3] & 0x0f) << 8) | data[offset + 4]) + 5;
|
|
271
|
+
}
|
|
272
|
+
return result;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Parse PES
|
|
277
|
+
*
|
|
278
|
+
* @param {Uint8Array} data
|
|
279
|
+
* @return {?shaka.util.TsParser.PES}
|
|
280
|
+
*/
|
|
281
|
+
parsePES(data) {
|
|
282
|
+
const startPrefix = (data[0] << 16) | (data[1] << 8) | data[2];
|
|
283
|
+
// In certain live streams, the start of a TS fragment has ts packets
|
|
284
|
+
// that are frame data that is continuing from the previous fragment. This
|
|
285
|
+
// is to check that the pes data is the start of a new pes data
|
|
286
|
+
if (startPrefix !== 1) {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
/** @type {shaka.util.TsParser.PES} */
|
|
290
|
+
const pes = {
|
|
291
|
+
data: new Uint8Array(0),
|
|
292
|
+
// get the packet length, this will be 0 for video
|
|
293
|
+
packetLength: 6 + ((data[4] << 8) | data[5]),
|
|
294
|
+
pts: null,
|
|
295
|
+
dts: null,
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// PES packets may be annotated with a PTS value, or a PTS value
|
|
299
|
+
// and a DTS value. Determine what combination of values is
|
|
300
|
+
// available to work with.
|
|
301
|
+
const ptsDtsFlags = data[7];
|
|
302
|
+
|
|
303
|
+
// PTS and DTS are normally stored as a 33-bit number. Javascript
|
|
304
|
+
// performs all bitwise operations on 32-bit integers but javascript
|
|
305
|
+
// supports a much greater range (52-bits) of integer using standard
|
|
306
|
+
// mathematical operations.
|
|
307
|
+
// We construct a 31-bit value using bitwise operators over the 31
|
|
308
|
+
// most significant bits and then multiply by 4 (equal to a left-shift
|
|
309
|
+
// of 2) before we add the final 2 least significant bits of the
|
|
310
|
+
// timestamp (equal to an OR.)
|
|
311
|
+
if (ptsDtsFlags & 0xC0) {
|
|
312
|
+
// the PTS and DTS are not written out directly. For information
|
|
313
|
+
// on how they are encoded, see
|
|
314
|
+
// http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
|
|
315
|
+
pes.pts =
|
|
316
|
+
(data[9] & 0x0e) * 536870912 + // 1 << 29
|
|
317
|
+
(data[10] & 0xff) * 4194304 + // 1 << 22
|
|
318
|
+
(data[11] & 0xfe) * 16384 + // 1 << 14
|
|
319
|
+
(data[12] & 0xff) * 128 + // 1 << 7
|
|
320
|
+
(data[13] & 0xfe) / 2;
|
|
321
|
+
|
|
322
|
+
pes.dts = pes.pts;
|
|
323
|
+
if (ptsDtsFlags & 0x40) {
|
|
324
|
+
pes.dts =
|
|
325
|
+
(data[14] & 0x0e) * 536870912 + // 1 << 29
|
|
326
|
+
(data[15] & 0xff) * 4194304 + // 1 << 22
|
|
327
|
+
(data[16] & 0xfe) * 16384 + // 1 << 14
|
|
328
|
+
(data[17] & 0xff) * 128 + // 1 << 7
|
|
329
|
+
(data[18] & 0xfe) / 2;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// the data section starts immediately after the PES header.
|
|
333
|
+
// pes_header_data_length specifies the number of header bytes
|
|
334
|
+
// that follow the last byte of the field.
|
|
335
|
+
pes.data = data.subarray(9 + data[8]);
|
|
336
|
+
|
|
337
|
+
return pes;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Return the ID3 metadata
|
|
342
|
+
*
|
|
343
|
+
* @return {!Array.<shaka.extern.ID3Metadata>}
|
|
344
|
+
*/
|
|
345
|
+
getMetadata() {
|
|
346
|
+
const timescale = shaka.util.TsParser.Timescale_;
|
|
347
|
+
const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
|
|
348
|
+
const metadata = [];
|
|
349
|
+
let prevId3Data = new Uint8Array(0);
|
|
350
|
+
// parsePES() only works if the data begins on a PES boundary.
|
|
351
|
+
// Try the last data blob first, and if it doesn't begin on a
|
|
352
|
+
// PES boundary, prepend the previous blob and try again.
|
|
353
|
+
// This way, a successful parse will always begin and end on
|
|
354
|
+
// the correct boundary, and no data will be skipped.
|
|
355
|
+
for (let i = this.id3Data_.length - 1; i >= 0; i--) {
|
|
356
|
+
const data = this.id3Data_[i];
|
|
357
|
+
goog.asserts.assert(data, 'We should have a data');
|
|
358
|
+
const id3Data = Uint8ArrayUtils.concat(data, prevId3Data);
|
|
359
|
+
const pes = this.parsePES(id3Data);
|
|
360
|
+
if (pes) {
|
|
361
|
+
metadata.unshift({
|
|
362
|
+
cueTime: pes.pts ? pes.pts / timescale : null,
|
|
363
|
+
data: pes.data,
|
|
364
|
+
frames: shaka.util.Id3Utils.getID3Frames(pes.data),
|
|
365
|
+
dts: pes.dts,
|
|
366
|
+
pts: pes.pts,
|
|
367
|
+
});
|
|
368
|
+
prevId3Data = new Uint8Array(0);
|
|
369
|
+
} else {
|
|
370
|
+
prevId3Data = id3Data;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return metadata;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Return the start time for the audio and video
|
|
378
|
+
*
|
|
379
|
+
* @return {{audio: ?number, video: ?number}}
|
|
380
|
+
*/
|
|
381
|
+
getStartTime() {
|
|
382
|
+
return {
|
|
383
|
+
audio: this.audioStartTime_,
|
|
384
|
+
video: this.videoStartTime_,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Return the audio and video codecs
|
|
390
|
+
*
|
|
391
|
+
* @return {{audio: ?string, video: ?string}}
|
|
392
|
+
*/
|
|
393
|
+
getCodecs() {
|
|
394
|
+
return {
|
|
395
|
+
audio: this.audioCodec_,
|
|
396
|
+
video: this.videoCodec_,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Check if the passed data corresponds to an MPEG2-TS
|
|
402
|
+
*
|
|
403
|
+
* @param {Uint8Array} data
|
|
404
|
+
* @return {boolean}
|
|
405
|
+
*/
|
|
406
|
+
static probe(data) {
|
|
407
|
+
const syncOffset = shaka.util.TsParser.syncOffset(data);
|
|
408
|
+
if (syncOffset < 0) {
|
|
409
|
+
return false;
|
|
410
|
+
} else {
|
|
411
|
+
if (syncOffset > 0) {
|
|
412
|
+
shaka.log.warning('MPEG2-TS detected but first sync word found @ ' +
|
|
413
|
+
'offset ' + syncOffset + ', junk ahead ?');
|
|
414
|
+
}
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Returns the synchronization offset
|
|
421
|
+
*
|
|
422
|
+
* @param {Uint8Array} data
|
|
423
|
+
* @return {number}
|
|
424
|
+
*/
|
|
425
|
+
static syncOffset(data) {
|
|
426
|
+
const packetLength = shaka.util.TsParser.PacketLength_;
|
|
427
|
+
// scan 1000 first bytes
|
|
428
|
+
const scanwindow = Math.min(1000, data.length - 3 * packetLength);
|
|
429
|
+
let i = 0;
|
|
430
|
+
while (i < scanwindow) {
|
|
431
|
+
// a TS fragment should contain at least 3 TS packets, a PAT, a PMT, and
|
|
432
|
+
// one PID, each starting with 0x47
|
|
433
|
+
if (data[i] === 0x47 &&
|
|
434
|
+
data[i + packetLength] === 0x47 &&
|
|
435
|
+
data[i + 2 * packetLength] === 0x47) {
|
|
436
|
+
return i;
|
|
437
|
+
} else {
|
|
438
|
+
i++;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return -1;
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* @const {number}
|
|
448
|
+
* @private
|
|
449
|
+
*/
|
|
450
|
+
shaka.util.TsParser.PacketLength_ = 188;
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* @const {number}
|
|
455
|
+
* @private
|
|
456
|
+
*/
|
|
457
|
+
shaka.util.TsParser.Timescale_ = 90000;
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* @typedef {{
|
|
462
|
+
* audio: number,
|
|
463
|
+
* video: number,
|
|
464
|
+
* id3: number,
|
|
465
|
+
* audioCodec: string,
|
|
466
|
+
* videoCodec: string
|
|
467
|
+
* }}
|
|
468
|
+
*
|
|
469
|
+
* @summary PMT.
|
|
470
|
+
* @property {number} audio
|
|
471
|
+
* Audio PID
|
|
472
|
+
* @property {number} video
|
|
473
|
+
* Video PID
|
|
474
|
+
* @property {number} id3
|
|
475
|
+
* ID3 PID
|
|
476
|
+
* @property {string} audioCodec
|
|
477
|
+
* Audio codec
|
|
478
|
+
* @property {string} videoCodec
|
|
479
|
+
* Video codec
|
|
480
|
+
*/
|
|
481
|
+
shaka.util.TsParser.PMT;
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* @typedef {{
|
|
486
|
+
* data: Uint8Array,
|
|
487
|
+
* packetLength: number,
|
|
488
|
+
* pts: ?number,
|
|
489
|
+
* dts: ?number
|
|
490
|
+
* }}
|
|
491
|
+
*
|
|
492
|
+
* @summary PES.
|
|
493
|
+
* @property {Uint8Array} data
|
|
494
|
+
* @property {number} packetLength
|
|
495
|
+
* @property {?number} pts
|
|
496
|
+
* @property {?number} dts
|
|
497
|
+
*/
|
|
498
|
+
shaka.util.TsParser.PES;
|
|
499
|
+
|
package/maintained-branches.md
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shaka-player",
|
|
3
3
|
"description": "DASH/EME video player library",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.3.0",
|
|
5
5
|
"homepage": "https://github.com/shaka-project/shaka-player",
|
|
6
6
|
"author": "Google",
|
|
7
7
|
"maintainers": [
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"karma-sourcemap-loader": "^0.3.8",
|
|
47
47
|
"karma-spec-reporter": "^0.0.34",
|
|
48
48
|
"karma-webdriver-launcher": "^1.0.8",
|
|
49
|
+
"lcevc_dil.js": "^1.1.20",
|
|
49
50
|
"less": "https://gitpkg.now.sh/joeyparrish/less.js/packages/less?28c63a43",
|
|
50
51
|
"less-plugin-clean-css": "github:austingardner/less-plugin-clean-css#4e9e77bf",
|
|
51
52
|
"material-design-lite": "^1.3.0",
|
|
@@ -71,6 +72,8 @@
|
|
|
71
72
|
"eme-encryption-scheme-polyfill/index.js",
|
|
72
73
|
"es6-promise-polyfill/promise.min.js",
|
|
73
74
|
"google-closure-library/closure/goog/base.js",
|
|
75
|
+
"lcevc_dil.js/dist/lcevc_dil.min.js",
|
|
76
|
+
"lcevc_dil.js/dist/liblcevc_dpi.wasm",
|
|
74
77
|
"less/dist/less.js",
|
|
75
78
|
"material-design-lite/dist/material.indigo-blue.min.css",
|
|
76
79
|
"material-design-lite/dist/material.min.js",
|
package/roadmap.md
CHANGED
|
@@ -9,28 +9,48 @@ The goals of future milestones are fluid until we begin that development cycle,
|
|
|
9
9
|
so the exact milestone for future features is not pre-determined.
|
|
10
10
|
|
|
11
11
|
Priority features up next:
|
|
12
|
-
-
|
|
13
|
-
https://github.com/shaka-project/shaka-player/
|
|
12
|
+
- HLS improvements
|
|
13
|
+
https://github.com/shaka-project/shaka-player/projects/6
|
|
14
14
|
|
|
15
15
|
Candidate features for future release cycles:
|
|
16
|
+
- Preload API
|
|
17
|
+
https://github.com/shaka-project/shaka-player/issues/880
|
|
16
18
|
- Official TypeScript defs, generated from source
|
|
17
19
|
https://github.com/shaka-project/shaka-player/issues/1030
|
|
18
20
|
- Codec-switching
|
|
19
21
|
https://github.com/shaka-project/shaka-player/issues/1528
|
|
20
22
|
- Background fetch for offline storage
|
|
21
23
|
https://github.com/shaka-project/shaka-player/issues/879
|
|
22
|
-
- Support ID3 tags in mp4
|
|
23
|
-
https://github.com/shaka-project/shaka-player/issues/3351
|
|
24
24
|
- Smaller, more modular binary
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
=====
|
|
27
|
+
|
|
28
|
+
v4.3 - 2022 Q4
|
|
29
|
+
- Support ID3 tags in mp4
|
|
30
|
+
https://github.com/shaka-project/shaka-player/issues/3351
|
|
31
|
+
- Lazy-load HLS playlists on adaptation
|
|
32
|
+
https://github.com/shaka-project/shaka-player/issues/1936
|
|
33
|
+
- HLS key rotation
|
|
34
|
+
https://github.com/shaka-project/shaka-player/issues/741
|
|
35
|
+
|
|
36
|
+
v4.2 - 2022 Q3
|
|
37
|
+
- Automatic ABR quality restrictions based on size
|
|
38
|
+
https://github.com/shaka-project/shaka-player/issues/2333
|
|
39
|
+
- HLS support for AES-128 encryption
|
|
40
|
+
https://github.com/shaka-project/shaka-player/issues/850
|
|
41
|
+
|
|
42
|
+
v4.1 - 2022 Q2
|
|
43
|
+
- HLS support for EXT-X-GAP
|
|
44
|
+
https://github.com/shaka-project/shaka-player/issues/1308
|
|
45
|
+
- Temporarily disable the active variant after `HTTP_ERROR`
|
|
46
|
+
https://github.com/shaka-project/shaka-player/issues/1542
|
|
47
|
+
|
|
48
|
+
v4.0 - 2022 Q2
|
|
27
49
|
- MSE support for FairPlay
|
|
28
50
|
https://github.com/shaka-project/shaka-player/issues/3346
|
|
29
51
|
- Support containerless formats
|
|
30
52
|
https://github.com/shaka-project/shaka-player/issues/2337
|
|
31
53
|
|
|
32
|
-
=====
|
|
33
|
-
|
|
34
54
|
v3.3 - 2022 Q1
|
|
35
55
|
- Common Media Client Data (CMCD) logging support
|
|
36
56
|
https://github.com/shaka-project/shaka-player/issues/3619
|