shaka-player 4.7.11 → 4.7.12
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/README.md +2 -4
- package/dist/deps.js +3 -4
- package/dist/locales.js +31 -31
- package/dist/shaka-player.compiled.d.ts +12 -10
- package/dist/shaka-player.compiled.debug.d.ts +12 -10
- package/dist/shaka-player.compiled.debug.externs.js +78 -64
- package/dist/shaka-player.compiled.debug.js +168 -163
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +78 -64
- package/dist/shaka-player.compiled.js +1134 -1128
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +12 -10
- package/dist/shaka-player.ui.debug.d.ts +12 -10
- package/dist/shaka-player.ui.debug.externs.js +78 -64
- package/dist/shaka-player.ui.debug.js +178 -174
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +78 -64
- package/dist/shaka-player.ui.js +1264 -1258
- package/dist/shaka-player.ui.map +4 -4
- package/externs/shaka/player.js +32 -19
- package/lib/abr/simple_abr_manager.js +21 -26
- package/lib/cast/cast_proxy.js +1 -3
- package/lib/cast/cast_sender.js +8 -11
- package/lib/cea/cea608_memory.js +1 -1
- package/lib/cea/ts_cea_parser.js +10 -1
- package/lib/dash/dash_parser.js +3 -0
- package/lib/hls/hls_parser.js +18 -16
- package/lib/media/drm_engine.js +6 -3
- package/lib/media/media_source_engine.js +25 -12
- package/lib/media/presentation_timeline.js +23 -0
- package/lib/media/segment_utils.js +10 -1
- package/lib/player.js +15 -2
- package/lib/polyfill/media_capabilities.js +20 -1
- package/lib/polyfill/mediasource.js +37 -14
- package/lib/text/vtt_text_parser.js +51 -51
- package/lib/transmuxer/h264.js +3 -3
- package/lib/transmuxer/h265.js +3 -3
- package/lib/transmuxer/ts_transmuxer.js +42 -3
- package/lib/util/manifest_parser_utils.js +1 -0
- package/lib/util/mp4_parser.js +57 -0
- package/lib/util/platform.js +4 -0
- package/lib/util/ts_parser.js +99 -10
- package/package.json +1 -1
- package/lib/polyfill/storage_estimate.js +0 -57
|
@@ -30,14 +30,8 @@ shaka.polyfill.MediaSource = class {
|
|
|
30
30
|
// platform detection tricks to decide which patches to install.
|
|
31
31
|
const safariVersion = shaka.util.Platform.safariVersion();
|
|
32
32
|
|
|
33
|
-
if (!window.MediaSource) {
|
|
33
|
+
if (!window.MediaSource && !window.ManagedMediaSource) {
|
|
34
34
|
shaka.log.info('No MSE implementation available.');
|
|
35
|
-
} else if (shaka.util.Platform.isChromecast()) {
|
|
36
|
-
shaka.log.info('Patching Chromecast MSE bugs.');
|
|
37
|
-
// Chromecast fails on some TS content, even though it is supposed to
|
|
38
|
-
// support it. Better to transmux.
|
|
39
|
-
// See https://github.com/shaka-project/shaka-player/issues/5278
|
|
40
|
-
shaka.polyfill.MediaSource.rejectContainer_('mp2t');
|
|
41
35
|
} else if (safariVersion) {
|
|
42
36
|
// NOTE: shaka.Player.isBrowserSupported() has its own restrictions on
|
|
43
37
|
// Safari version.
|
|
@@ -73,11 +67,6 @@ shaka.polyfill.MediaSource = class {
|
|
|
73
67
|
// Bug filed: https://bugs.webkit.org/show_bug.cgi?id=165342
|
|
74
68
|
shaka.polyfill.MediaSource.stubAbort_();
|
|
75
69
|
}
|
|
76
|
-
} else if (shaka.util.Platform.isEdge()) {
|
|
77
|
-
shaka.log.info('Rejecting TS container.');
|
|
78
|
-
// TS content is broken on Edge in general.
|
|
79
|
-
// See https://github.com/shaka-project/shaka-player/issues/4955
|
|
80
|
-
shaka.polyfill.MediaSource.rejectContainer_('mp2t');
|
|
81
70
|
} else if (shaka.util.Platform.isTizen2() ||
|
|
82
71
|
shaka.util.Platform.isTizen3() ||
|
|
83
72
|
shaka.util.Platform.isTizen4()) {
|
|
@@ -90,6 +79,14 @@ shaka.polyfill.MediaSource = class {
|
|
|
90
79
|
shaka.log.info('Using native MSE as-is.');
|
|
91
80
|
}
|
|
92
81
|
|
|
82
|
+
if (window.MediaSource || window.ManagedMediaSource) {
|
|
83
|
+
// TS content is broken on all browsers in general.
|
|
84
|
+
// See https://github.com/shaka-project/shaka-player/issues/4955
|
|
85
|
+
// See https://github.com/shaka-project/shaka-player/issues/5278
|
|
86
|
+
// See https://github.com/shaka-project/shaka-player/issues/6334
|
|
87
|
+
shaka.polyfill.MediaSource.rejectContainer_('mp2t');
|
|
88
|
+
}
|
|
89
|
+
|
|
93
90
|
if (window.MediaSource &&
|
|
94
91
|
MediaSource.isTypeSupported('video/webm; codecs="vp9"') &&
|
|
95
92
|
!MediaSource.isTypeSupported('video/webm; codecs="vp09.00.10.08"')) {
|
|
@@ -158,12 +155,25 @@ shaka.polyfill.MediaSource = class {
|
|
|
158
155
|
* @private
|
|
159
156
|
*/
|
|
160
157
|
static rejectContainer_(container) {
|
|
161
|
-
const isTypeSupported =
|
|
158
|
+
const isTypeSupported =
|
|
159
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
160
|
+
MediaSource.isTypeSupported.bind(MediaSource);
|
|
162
161
|
|
|
163
162
|
MediaSource.isTypeSupported = (mimeType) => {
|
|
164
163
|
const actualContainer = shaka.util.MimeUtils.getContainerType(mimeType);
|
|
165
164
|
return actualContainer != container && isTypeSupported(mimeType);
|
|
166
165
|
};
|
|
166
|
+
|
|
167
|
+
if (window.ManagedMediaSource) {
|
|
168
|
+
const isTypeSupportedManaged =
|
|
169
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
170
|
+
ManagedMediaSource.isTypeSupported.bind(ManagedMediaSource);
|
|
171
|
+
|
|
172
|
+
window.ManagedMediaSource.isTypeSupported = (mimeType) => {
|
|
173
|
+
const actualContainer = shaka.util.MimeUtils.getContainerType(mimeType);
|
|
174
|
+
return actualContainer != container && isTypeSupportedManaged(mimeType);
|
|
175
|
+
};
|
|
176
|
+
}
|
|
167
177
|
}
|
|
168
178
|
|
|
169
179
|
/**
|
|
@@ -175,12 +185,25 @@ shaka.polyfill.MediaSource = class {
|
|
|
175
185
|
* @private
|
|
176
186
|
*/
|
|
177
187
|
static rejectCodec_(codec) {
|
|
178
|
-
const isTypeSupported =
|
|
188
|
+
const isTypeSupported =
|
|
189
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
190
|
+
MediaSource.isTypeSupported.bind(MediaSource);
|
|
179
191
|
|
|
180
192
|
MediaSource.isTypeSupported = (mimeType) => {
|
|
181
193
|
const actualCodec = shaka.util.MimeUtils.getCodecBase(mimeType);
|
|
182
194
|
return actualCodec != codec && isTypeSupported(mimeType);
|
|
183
195
|
};
|
|
196
|
+
|
|
197
|
+
if (window.ManagedMediaSource) {
|
|
198
|
+
const isTypeSupportedManaged =
|
|
199
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
200
|
+
ManagedMediaSource.isTypeSupported.bind(ManagedMediaSource);
|
|
201
|
+
|
|
202
|
+
window.ManagedMediaSource.isTypeSupported = (mimeType) => {
|
|
203
|
+
const actualCodec = shaka.util.MimeUtils.getCodecBase(mimeType);
|
|
204
|
+
return actualCodec != codec && isTypeSupportedManaged(mimeType);
|
|
205
|
+
};
|
|
206
|
+
}
|
|
184
207
|
}
|
|
185
208
|
|
|
186
209
|
/**
|
|
@@ -84,51 +84,16 @@ shaka.text.VttTextParser = class {
|
|
|
84
84
|
// HLS content, which should use X-TIMESTAMP-MAP and periodStart instead.
|
|
85
85
|
let offset = time.vttOffset;
|
|
86
86
|
|
|
87
|
-
// Only use 'X-TIMESTAMP-MAP'
|
|
88
|
-
|
|
89
|
-
// timestamp has already been extracted, and appears in periodStart.
|
|
90
|
-
if (blocks[0].includes('X-TIMESTAMP-MAP') && this.sequenceMode_) {
|
|
91
|
-
// https://bit.ly/2K92l7y
|
|
92
|
-
// The 'X-TIMESTAMP-MAP' header is used in HLS to align text with
|
|
93
|
-
// the rest of the media.
|
|
94
|
-
// The header format is 'X-TIMESTAMP-MAP=MPEGTS:n,LOCAL:m'
|
|
95
|
-
// (the attributes can go in any order)
|
|
96
|
-
// where n is MPEG-2 time and m is cue time it maps to.
|
|
97
|
-
// For example 'X-TIMESTAMP-MAP=LOCAL:00:00:00.000,MPEGTS:900000'
|
|
98
|
-
// means an offset of 10 seconds
|
|
99
|
-
// 900000/MPEG_TIMESCALE - cue time.
|
|
100
|
-
const cueTimeMatch =
|
|
101
|
-
blocks[0].match(/LOCAL:((?:(\d{1,}):)?(\d{2}):(\d{2})\.(\d{3}))/m);
|
|
102
|
-
|
|
103
|
-
const mpegTimeMatch = blocks[0].match(/MPEGTS:(\d+)/m);
|
|
104
|
-
if (cueTimeMatch && mpegTimeMatch) {
|
|
105
|
-
const parser = new shaka.util.TextParser(cueTimeMatch[1]);
|
|
106
|
-
const cueTime = shaka.text.VttTextParser.parseTime_(parser);
|
|
107
|
-
if (cueTime == null) {
|
|
108
|
-
throw new shaka.util.Error(
|
|
109
|
-
shaka.util.Error.Severity.CRITICAL,
|
|
110
|
-
shaka.util.Error.Category.TEXT,
|
|
111
|
-
shaka.util.Error.Code.INVALID_TEXT_HEADER);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
let mpegTime = Number(mpegTimeMatch[1]);
|
|
115
|
-
const mpegTimescale = shaka.text.VttTextParser.MPEG_TIMESCALE_;
|
|
116
|
-
|
|
117
|
-
const rolloverSeconds =
|
|
118
|
-
shaka.text.VttTextParser.TS_ROLLOVER_ / mpegTimescale;
|
|
119
|
-
let segmentStart = time.segmentStart - time.periodStart;
|
|
120
|
-
while (segmentStart >= rolloverSeconds) {
|
|
121
|
-
segmentStart -= rolloverSeconds;
|
|
122
|
-
mpegTime += shaka.text.VttTextParser.TS_ROLLOVER_;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
offset = time.periodStart + mpegTime / mpegTimescale - cueTime;
|
|
126
|
-
}
|
|
127
|
-
} else if (blocks[0].includes('X-TIMESTAMP-MAP') &&
|
|
87
|
+
// Only use 'X-TIMESTAMP-MAP' with HLS. This overrides offset above.
|
|
88
|
+
if (blocks[0].includes('X-TIMESTAMP-MAP') &&
|
|
128
89
|
this.manifestType_ == shaka.media.ManifestParser.HLS) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
90
|
+
if (this.sequenceMode_) {
|
|
91
|
+
// Compute a different, rollover-based offset for sequence mode.
|
|
92
|
+
offset = this.computeHlsSequenceModeOffset_(blocks[0], time);
|
|
93
|
+
} else {
|
|
94
|
+
// Calculate the offset from the segment startTime.
|
|
95
|
+
offset = time.segmentStart;
|
|
96
|
+
}
|
|
132
97
|
}
|
|
133
98
|
|
|
134
99
|
// Parse VTT regions.
|
|
@@ -159,6 +124,48 @@ shaka.text.VttTextParser = class {
|
|
|
159
124
|
return ret;
|
|
160
125
|
}
|
|
161
126
|
|
|
127
|
+
/**
|
|
128
|
+
* @param {string} headerBlock Contains X-TIMESTAMP-MAP.
|
|
129
|
+
* @param {shaka.extern.TextParser.TimeContext} time
|
|
130
|
+
* @return {number}
|
|
131
|
+
* @private
|
|
132
|
+
*/
|
|
133
|
+
computeHlsSequenceModeOffset_(headerBlock, time) {
|
|
134
|
+
// https://bit.ly/2K92l7y
|
|
135
|
+
// The 'X-TIMESTAMP-MAP' header is used in HLS to align text with
|
|
136
|
+
// the rest of the media.
|
|
137
|
+
// The header format is 'X-TIMESTAMP-MAP=MPEGTS:n,LOCAL:m'
|
|
138
|
+
// (the attributes can go in any order)
|
|
139
|
+
// where n is MPEG-2 time and m is cue time it maps to.
|
|
140
|
+
// For example 'X-TIMESTAMP-MAP=LOCAL:00:00:00.000,MPEGTS:900000'
|
|
141
|
+
// means an offset of 10 seconds
|
|
142
|
+
// 900000/MPEG_TIMESCALE - cue time.
|
|
143
|
+
const cueTimeMatch = headerBlock.match(
|
|
144
|
+
/LOCAL:((?:(\d{1,}):)?(\d{2}):(\d{2})\.(\d{3}))/m);
|
|
145
|
+
const mpegTimeMatch = headerBlock.match(/MPEGTS:(\d+)/m);
|
|
146
|
+
|
|
147
|
+
if (!cueTimeMatch || !mpegTimeMatch) {
|
|
148
|
+
throw new shaka.util.Error(
|
|
149
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
150
|
+
shaka.util.Error.Category.TEXT,
|
|
151
|
+
shaka.util.Error.Code.INVALID_TEXT_HEADER);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const parser = new shaka.util.TextParser(cueTimeMatch[1]);
|
|
155
|
+
const cueTime = shaka.text.VttTextParser.parseTime_(parser);
|
|
156
|
+
if (cueTime == null) {
|
|
157
|
+
throw new shaka.util.Error(
|
|
158
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
159
|
+
shaka.util.Error.Category.TEXT,
|
|
160
|
+
shaka.util.Error.Code.INVALID_TEXT_HEADER);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const mpegTime = Number(mpegTimeMatch[1]);
|
|
164
|
+
const mpegTimescale = shaka.text.VttTextParser.MPEG_TIMESCALE_;
|
|
165
|
+
|
|
166
|
+
return time.periodStart + mpegTime / mpegTimescale - cueTime;
|
|
167
|
+
}
|
|
168
|
+
|
|
162
169
|
/**
|
|
163
170
|
* Add default color
|
|
164
171
|
*
|
|
@@ -1061,13 +1068,6 @@ shaka.text.VttTextParser = class {
|
|
|
1061
1068
|
*/
|
|
1062
1069
|
shaka.text.VttTextParser.MPEG_TIMESCALE_ = 90000;
|
|
1063
1070
|
|
|
1064
|
-
/**
|
|
1065
|
-
* At this value, timestamps roll over in TS content.
|
|
1066
|
-
* @const {number}
|
|
1067
|
-
* @private
|
|
1068
|
-
*/
|
|
1069
|
-
shaka.text.VttTextParser.TS_ROLLOVER_ = 0x200000000;
|
|
1070
|
-
|
|
1071
1071
|
/**
|
|
1072
1072
|
* @const
|
|
1073
1073
|
* @private {!RegExp}
|
package/lib/transmuxer/h264.js
CHANGED
|
@@ -144,7 +144,7 @@ shaka.transmuxer.H264 = class {
|
|
|
144
144
|
const vSpacingTable = [
|
|
145
145
|
1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1,
|
|
146
146
|
];
|
|
147
|
-
if (aspectRatioIdc > 0 && aspectRatioIdc
|
|
147
|
+
if (aspectRatioIdc > 0 && aspectRatioIdc <= 16) {
|
|
148
148
|
hSpacing = hSpacingTable[aspectRatioIdc - 1];
|
|
149
149
|
vSpacing = vSpacingTable[aspectRatioIdc - 1];
|
|
150
150
|
} else if (aspectRatioIdc === 255) {
|
|
@@ -267,8 +267,8 @@ shaka.transmuxer.H264 = class {
|
|
|
267
267
|
naluLength[1] = (size >> 16) & 0xff;
|
|
268
268
|
naluLength[2] = (size >> 8) & 0xff;
|
|
269
269
|
naluLength[3] = size & 0xff;
|
|
270
|
-
nalusData.push(
|
|
271
|
-
|
|
270
|
+
nalusData.push(naluLength);
|
|
271
|
+
nalusData.push(nalu.fullData);
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
274
|
if (!nalusData.length) {
|
package/lib/transmuxer/h265.js
CHANGED
|
@@ -317,7 +317,7 @@ shaka.transmuxer.H265 = class {
|
|
|
317
317
|
const sarHeightTable = [
|
|
318
318
|
1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1,
|
|
319
319
|
];
|
|
320
|
-
if (aspectRatioIdc > 0 && aspectRatioIdc
|
|
320
|
+
if (aspectRatioIdc > 0 && aspectRatioIdc <= 16) {
|
|
321
321
|
sarWidth = sarWidthTable[aspectRatioIdc - 1];
|
|
322
322
|
sarHeight = sarHeightTable[aspectRatioIdc - 1];
|
|
323
323
|
} else if (aspectRatioIdc === 255) {
|
|
@@ -652,8 +652,8 @@ shaka.transmuxer.H265 = class {
|
|
|
652
652
|
naluLength[1] = (size >> 16) & 0xff;
|
|
653
653
|
naluLength[2] = (size >> 8) & 0xff;
|
|
654
654
|
naluLength[3] = size & 0xff;
|
|
655
|
-
nalusData.push(
|
|
656
|
-
|
|
655
|
+
nalusData.push(naluLength);
|
|
656
|
+
nalusData.push(nalu.fullData);
|
|
657
657
|
}
|
|
658
658
|
}
|
|
659
659
|
if (!nalusData.length) {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
goog.provide('shaka.transmuxer.TsTransmuxer');
|
|
8
8
|
|
|
9
9
|
goog.require('shaka.media.Capabilities');
|
|
10
|
+
goog.require('shaka.transmuxer.AacTransmuxer');
|
|
10
11
|
goog.require('shaka.transmuxer.Ac3');
|
|
11
12
|
goog.require('shaka.transmuxer.ADTS');
|
|
12
13
|
goog.require('shaka.transmuxer.Ec3');
|
|
@@ -16,6 +17,7 @@ goog.require('shaka.transmuxer.MpegAudio');
|
|
|
16
17
|
goog.require('shaka.transmuxer.TransmuxerEngine');
|
|
17
18
|
goog.require('shaka.util.BufferUtils');
|
|
18
19
|
goog.require('shaka.util.Error');
|
|
20
|
+
goog.require('shaka.util.Id3Utils');
|
|
19
21
|
goog.require('shaka.util.ManifestParserUtils');
|
|
20
22
|
goog.require('shaka.util.MimeUtils');
|
|
21
23
|
goog.require('shaka.util.Mp4Generator');
|
|
@@ -45,6 +47,9 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
45
47
|
|
|
46
48
|
/** @private {?shaka.util.TsParser} */
|
|
47
49
|
this.tsParser_ = null;
|
|
50
|
+
|
|
51
|
+
/** @private {?shaka.transmuxer.AacTransmuxer} */
|
|
52
|
+
this.aacTransmuxer_ = null;
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
|
|
@@ -54,6 +59,10 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
54
59
|
*/
|
|
55
60
|
destroy() {
|
|
56
61
|
this.initSegments.clear();
|
|
62
|
+
|
|
63
|
+
if (this.aacTransmuxer_) {
|
|
64
|
+
this.aacTransmuxer_.destroy();
|
|
65
|
+
}
|
|
57
66
|
}
|
|
58
67
|
|
|
59
68
|
|
|
@@ -187,14 +196,44 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
187
196
|
*/
|
|
188
197
|
transmux(data, stream, reference, duration, contentType) {
|
|
189
198
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
199
|
+
const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
|
|
200
|
+
|
|
201
|
+
const uint8ArrayData = shaka.util.BufferUtils.toUint8(data);
|
|
202
|
+
|
|
203
|
+
if (contentType == ContentType.AUDIO &&
|
|
204
|
+
!shaka.util.TsParser.probe(uint8ArrayData)) {
|
|
205
|
+
const id3Data = shaka.util.Id3Utils.getID3Data(uint8ArrayData);
|
|
206
|
+
let offset = id3Data.length;
|
|
207
|
+
for (; offset < uint8ArrayData.length; offset++) {
|
|
208
|
+
if (shaka.transmuxer.MpegAudio.probe(uint8ArrayData, offset)) {
|
|
209
|
+
return Promise.reject(new shaka.util.Error(
|
|
210
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
211
|
+
shaka.util.Error.Category.MEDIA,
|
|
212
|
+
shaka.util.Error.Code.TRANSMUXING_FAILED));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
offset = id3Data.length;
|
|
216
|
+
for (; offset < uint8ArrayData.length; offset++) {
|
|
217
|
+
if (shaka.transmuxer.ADTS.probe(uint8ArrayData, offset)) {
|
|
218
|
+
if (!this.aacTransmuxer_) {
|
|
219
|
+
this.aacTransmuxer_ =
|
|
220
|
+
new shaka.transmuxer.AacTransmuxer('audio/aac');
|
|
221
|
+
}
|
|
222
|
+
return this.aacTransmuxer_
|
|
223
|
+
.transmux(data, stream, reference, duration, contentType);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return Promise.reject(new shaka.util.Error(
|
|
227
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
228
|
+
shaka.util.Error.Category.MEDIA,
|
|
229
|
+
shaka.util.Error.Code.TRANSMUXING_FAILED));
|
|
230
|
+
}
|
|
231
|
+
|
|
190
232
|
if (!this.tsParser_) {
|
|
191
233
|
this.tsParser_ = new shaka.util.TsParser();
|
|
192
234
|
} else {
|
|
193
235
|
this.tsParser_.clearData();
|
|
194
236
|
}
|
|
195
|
-
const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
|
|
196
|
-
|
|
197
|
-
const uint8ArrayData = shaka.util.BufferUtils.toUint8(data);
|
|
198
237
|
|
|
199
238
|
const tsParser = this.tsParser_.parse(uint8ArrayData);
|
|
200
239
|
const streamInfos = [];
|
package/lib/util/mp4_parser.js
CHANGED
|
@@ -271,6 +271,63 @@ shaka.util.Mp4Parser = class {
|
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
|
|
274
|
+
/**
|
|
275
|
+
* A callback that tells the Mp4 parser to treat the body of a box as a audio
|
|
276
|
+
* sample entry. A audio sample entry has some fixed-sized fields
|
|
277
|
+
* describing the audio codec parameters, followed by an arbitrary number of
|
|
278
|
+
* appended children. Each child is a box.
|
|
279
|
+
*
|
|
280
|
+
* @param {!shaka.extern.ParsedBox} box
|
|
281
|
+
* @export
|
|
282
|
+
*/
|
|
283
|
+
static audioSampleEntry(box) {
|
|
284
|
+
// The "reader" starts at the payload, so we need to add the header to the
|
|
285
|
+
// start position. The header size varies.
|
|
286
|
+
const headerSize = shaka.util.Mp4Parser.headerSize(box);
|
|
287
|
+
|
|
288
|
+
// 6 bytes reserved
|
|
289
|
+
// 2 bytes data reference index
|
|
290
|
+
box.reader.skip(8);
|
|
291
|
+
// 2 bytes version
|
|
292
|
+
const version = box.reader.readUint16();
|
|
293
|
+
// 2 bytes revision (0, could be ignored)
|
|
294
|
+
// 4 bytes reserved
|
|
295
|
+
box.reader.skip(6);
|
|
296
|
+
|
|
297
|
+
if (version == 2) {
|
|
298
|
+
// 16 bytes hard-coded values with no comments
|
|
299
|
+
// 8 bytes sample rate
|
|
300
|
+
// 4 bytes channel count
|
|
301
|
+
// 4 bytes hard-coded values with no comments
|
|
302
|
+
// 4 bytes bits per sample
|
|
303
|
+
// 4 bytes lpcm flags
|
|
304
|
+
// 4 bytes sample size
|
|
305
|
+
// 4 bytes samples per packet
|
|
306
|
+
box.reader.skip(48);
|
|
307
|
+
} else {
|
|
308
|
+
// 2 bytes channel count
|
|
309
|
+
// 2 bytes bits per sample
|
|
310
|
+
// 2 bytes compression ID
|
|
311
|
+
// 2 bytes packet size
|
|
312
|
+
// 2 bytes sample rate
|
|
313
|
+
// 2 byte reserved
|
|
314
|
+
box.reader.skip(12);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (version == 1) {
|
|
318
|
+
// 4 bytes samples per packet
|
|
319
|
+
// 4 bytes bytes per packet
|
|
320
|
+
// 4 bytes bytes per frame
|
|
321
|
+
// 4 bytes bytes per sample
|
|
322
|
+
box.reader.skip(16);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
while (box.reader.hasMoreData() && !box.parser.done_) {
|
|
326
|
+
box.parser.parseNext(box.start + headerSize, box.reader, box.partialOkay);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
|
|
274
331
|
/**
|
|
275
332
|
* Create a callback that tells the Mp4 parser to treat the body of a box as a
|
|
276
333
|
* binary blob and to parse the body's contents using the provided callback.
|
package/lib/util/platform.js
CHANGED
|
@@ -535,6 +535,10 @@ shaka.util.Platform = class {
|
|
|
535
535
|
Platform.isWebOS4() || Platform.isWebOS5()) {
|
|
536
536
|
return false;
|
|
537
537
|
}
|
|
538
|
+
// See: https://chromium-review.googlesource.com/c/chromium/src/+/4577759
|
|
539
|
+
if (Platform.isWindows() && Platform.isEdge()) {
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
538
542
|
return true;
|
|
539
543
|
}
|
|
540
544
|
|
package/lib/util/ts_parser.js
CHANGED
|
@@ -10,6 +10,7 @@ goog.require('shaka.Deprecate');
|
|
|
10
10
|
goog.require('shaka.log');
|
|
11
11
|
goog.require('shaka.util.ExpGolomb');
|
|
12
12
|
goog.require('shaka.util.Id3Utils');
|
|
13
|
+
goog.require('shaka.util.StringUtils');
|
|
13
14
|
goog.require('shaka.util.Uint8ArrayUtils');
|
|
14
15
|
|
|
15
16
|
|
|
@@ -55,6 +56,12 @@ shaka.util.TsParser = class {
|
|
|
55
56
|
|
|
56
57
|
/** @private {!Array.<!Array.<Uint8Array>>} */
|
|
57
58
|
this.id3Data_ = [];
|
|
59
|
+
|
|
60
|
+
/** @private {?number} */
|
|
61
|
+
this.referencePts_ = null;
|
|
62
|
+
|
|
63
|
+
/** @private {?number} */
|
|
64
|
+
this.referenceDts_ = null;
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
/**
|
|
@@ -227,6 +234,7 @@ shaka.util.TsParser = class {
|
|
|
227
234
|
* @private
|
|
228
235
|
*/
|
|
229
236
|
parsePMT_(data, offset) {
|
|
237
|
+
const StringUtils = shaka.util.StringUtils;
|
|
230
238
|
const result = {
|
|
231
239
|
audio: -1,
|
|
232
240
|
video: -1,
|
|
@@ -249,26 +257,52 @@ shaka.util.TsParser = class {
|
|
|
249
257
|
case 0x06:
|
|
250
258
|
// stream_type 6 can mean a lot of different things in case of DVB.
|
|
251
259
|
// We need to look at the descriptors. Right now, we're only
|
|
252
|
-
// interested in
|
|
253
|
-
|
|
254
|
-
if (result.audio == -1 && esInfoLength > 0) {
|
|
260
|
+
// interested in a few audio and video formats,.
|
|
261
|
+
if (esInfoLength > 0) {
|
|
255
262
|
let parsePos = offset + 5;
|
|
256
263
|
let remaining = esInfoLength;
|
|
264
|
+
// Descriptor info: https://www.streamguru.de/mpeg-analyzer/supported-descriptor-list/
|
|
257
265
|
while (remaining > 2) {
|
|
258
266
|
const descriptorId = data[parsePos];
|
|
267
|
+
const descriptorLen = data[parsePos + 1] + 2;
|
|
259
268
|
switch (descriptorId) {
|
|
269
|
+
// Registration descriptor
|
|
270
|
+
case 0x05: {
|
|
271
|
+
const registrationData =
|
|
272
|
+
data.subarray(parsePos + 2, parsePos + descriptorLen);
|
|
273
|
+
const registration =
|
|
274
|
+
StringUtils.fromCharCode(registrationData);
|
|
275
|
+
if (result.audio == -1 && registration === 'Opus') {
|
|
276
|
+
result.audio = pid;
|
|
277
|
+
result.audioCodec = 'opus';
|
|
278
|
+
} else if (result.video == -1 && registration === 'AV01') {
|
|
279
|
+
result.video = pid;
|
|
280
|
+
result.videoCodec = 'av1';
|
|
281
|
+
}
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
260
284
|
// DVB Descriptor for AC-3
|
|
261
285
|
case 0x6a:
|
|
262
|
-
result.audio
|
|
263
|
-
|
|
286
|
+
if (result.audio == -1) {
|
|
287
|
+
result.audio = pid;
|
|
288
|
+
result.audioCodec = 'ac3';
|
|
289
|
+
}
|
|
264
290
|
break;
|
|
265
291
|
// DVB Descriptor for EC-3
|
|
266
292
|
case 0x7a:
|
|
267
|
-
result.audio
|
|
268
|
-
|
|
293
|
+
if (result.audio == -1) {
|
|
294
|
+
result.audio = pid;
|
|
295
|
+
result.audioCodec = 'ec3';
|
|
296
|
+
}
|
|
297
|
+
break;
|
|
298
|
+
// DVB Descriptor for AAC
|
|
299
|
+
case 0x7c:
|
|
300
|
+
if (result.audio == -1) {
|
|
301
|
+
result.audio = pid;
|
|
302
|
+
result.audioCodec = 'aac';
|
|
303
|
+
}
|
|
269
304
|
break;
|
|
270
305
|
}
|
|
271
|
-
const descriptorLen = data[parsePos + 1] + 2;
|
|
272
306
|
parsePos += descriptorLen;
|
|
273
307
|
remaining -= descriptorLen;
|
|
274
308
|
}
|
|
@@ -284,6 +318,13 @@ shaka.util.TsParser = class {
|
|
|
284
318
|
result.audioCodec = 'aac';
|
|
285
319
|
}
|
|
286
320
|
break;
|
|
321
|
+
// LOAS/LATM AAC
|
|
322
|
+
case 0x11:
|
|
323
|
+
if (result.audio == -1) {
|
|
324
|
+
result.audio = pid;
|
|
325
|
+
result.audioCodec = 'aac-loas';
|
|
326
|
+
}
|
|
327
|
+
break;
|
|
287
328
|
// Packetized metadata (ID3)
|
|
288
329
|
case 0x15:
|
|
289
330
|
if (result.id3 == -1) {
|
|
@@ -390,22 +431,36 @@ shaka.util.TsParser = class {
|
|
|
390
431
|
// the PTS and DTS are not written out directly. For information
|
|
391
432
|
// on how they are encoded, see
|
|
392
433
|
// http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
|
|
393
|
-
|
|
434
|
+
const pts =
|
|
394
435
|
(data[9] & 0x0e) * 536870912 + // 1 << 29
|
|
395
436
|
(data[10] & 0xff) * 4194304 + // 1 << 22
|
|
396
437
|
(data[11] & 0xfe) * 16384 + // 1 << 14
|
|
397
438
|
(data[12] & 0xff) * 128 + // 1 << 7
|
|
398
439
|
(data[13] & 0xfe) / 2;
|
|
399
440
|
|
|
441
|
+
if (this.referencePts_ == null) {
|
|
442
|
+
this.referencePts_ = pts;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
pes.pts = this.handleRollover_(pts, this.referencePts_);
|
|
446
|
+
this.referencePts_ = pes.pts;
|
|
447
|
+
|
|
400
448
|
pes.dts = pes.pts;
|
|
401
449
|
if (ptsDtsFlags & 0x40) {
|
|
402
|
-
|
|
450
|
+
const dts =
|
|
403
451
|
(data[14] & 0x0e) * 536870912 + // 1 << 29
|
|
404
452
|
(data[15] & 0xff) * 4194304 + // 1 << 22
|
|
405
453
|
(data[16] & 0xfe) * 16384 + // 1 << 14
|
|
406
454
|
(data[17] & 0xff) * 128 + // 1 << 7
|
|
407
455
|
(data[18] & 0xfe) / 2;
|
|
456
|
+
|
|
457
|
+
if (this.referenceDts_ == null) {
|
|
458
|
+
this.referenceDts_ = dts;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
pes.dts = this.handleRollover_(dts, this.referenceDts_);
|
|
408
462
|
}
|
|
463
|
+
this.referenceDts_ = pes.dts;
|
|
409
464
|
}
|
|
410
465
|
|
|
411
466
|
const pesHdrLen = data[8];
|
|
@@ -1069,6 +1124,40 @@ shaka.util.TsParser = class {
|
|
|
1069
1124
|
return ('0' + x.toString(16).toUpperCase()).slice(-2);
|
|
1070
1125
|
}
|
|
1071
1126
|
|
|
1127
|
+
/**
|
|
1128
|
+
* @param {number} value
|
|
1129
|
+
* @param {number} reference
|
|
1130
|
+
* @return {number}
|
|
1131
|
+
* @private
|
|
1132
|
+
*/
|
|
1133
|
+
handleRollover_(value, reference) {
|
|
1134
|
+
const MAX_TS = 8589934592;
|
|
1135
|
+
const RO_THRESH = 4294967296;
|
|
1136
|
+
|
|
1137
|
+
let direction = 1;
|
|
1138
|
+
|
|
1139
|
+
if (value > reference) {
|
|
1140
|
+
// If the current timestamp value is greater than our reference timestamp
|
|
1141
|
+
// and we detect a timestamp rollover, this means the roll over is
|
|
1142
|
+
// happening in the opposite direction.
|
|
1143
|
+
// Example scenario: Enter a long stream/video just after a rollover
|
|
1144
|
+
// occurred. The reference point will be set to a small number, e.g. 1.
|
|
1145
|
+
// The user then seeks backwards over the rollover point. In loading this
|
|
1146
|
+
// segment, the timestamp values will be very large, e.g. 2^33 - 1. Since
|
|
1147
|
+
// this comes before the data we loaded previously, we want to adjust the
|
|
1148
|
+
// time stamp to be `value - 2^33`.
|
|
1149
|
+
direction = -1;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// Note: A seek forwards or back that is greater than the RO_THRESH
|
|
1153
|
+
// (2^32, ~13 hours) will cause an incorrect adjustment.
|
|
1154
|
+
while (Math.abs(reference - value) > RO_THRESH) {
|
|
1155
|
+
value += (direction * MAX_TS);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
return value;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1072
1161
|
/**
|
|
1073
1162
|
* Check if the passed data corresponds to an MPEG2-TS
|
|
1074
1163
|
*
|
package/package.json
CHANGED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/*! @license
|
|
2
|
-
* Shaka Player
|
|
3
|
-
* Copyright 2016 Google LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
goog.provide('shaka.polyfill.StorageEstimate');
|
|
8
|
-
|
|
9
|
-
goog.require('shaka.polyfill');
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @summary A polyfill to provide navigator.storage.estimate in old
|
|
14
|
-
* webkit browsers.
|
|
15
|
-
* See: https://developers.google.com/web/updates/2017/08/estimating-available-storage-space#the-present
|
|
16
|
-
* @export
|
|
17
|
-
*/
|
|
18
|
-
shaka.polyfill.StorageEstimate = class {
|
|
19
|
-
/**
|
|
20
|
-
* Install the polyfill if needed.
|
|
21
|
-
* @export
|
|
22
|
-
*/
|
|
23
|
-
static install() {
|
|
24
|
-
if (navigator.storage && navigator.storage.estimate) {
|
|
25
|
-
// No need.
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (navigator.webkitTemporaryStorage &&
|
|
30
|
-
navigator.webkitTemporaryStorage.queryUsageAndQuota) {
|
|
31
|
-
if (!('storage' in navigator)) {
|
|
32
|
-
navigator.storage = /** @type {!StorageManager} */ ({});
|
|
33
|
-
}
|
|
34
|
-
navigator.storage.estimate =
|
|
35
|
-
shaka.polyfill.StorageEstimate.storageEstimate_;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* @this {StorageManager}
|
|
41
|
-
* @return {!Promise}
|
|
42
|
-
* @private
|
|
43
|
-
*/
|
|
44
|
-
static storageEstimate_() {
|
|
45
|
-
return new Promise((resolve, reject) => {
|
|
46
|
-
navigator.webkitTemporaryStorage.queryUsageAndQuota(
|
|
47
|
-
(usage, quota) => {
|
|
48
|
-
resolve({usage: usage, quota: quota});
|
|
49
|
-
},
|
|
50
|
-
reject,
|
|
51
|
-
);
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
shaka.polyfill.register(shaka.polyfill.StorageEstimate.install);
|