shaka-player 4.7.10 → 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.
Files changed (51) hide show
  1. package/README.md +2 -4
  2. package/dist/controls.css +1 -1
  3. package/dist/controls.css.map +1 -1
  4. package/dist/deps.js +4 -5
  5. package/dist/locales.js +31 -31
  6. package/dist/shaka-player.compiled.d.ts +12 -10
  7. package/dist/shaka-player.compiled.debug.d.ts +12 -10
  8. package/dist/shaka-player.compiled.debug.externs.js +78 -64
  9. package/dist/shaka-player.compiled.debug.js +190 -183
  10. package/dist/shaka-player.compiled.debug.map +4 -4
  11. package/dist/shaka-player.compiled.externs.js +78 -64
  12. package/dist/shaka-player.compiled.js +1122 -1115
  13. package/dist/shaka-player.compiled.map +4 -4
  14. package/dist/shaka-player.ui.d.ts +12 -10
  15. package/dist/shaka-player.ui.debug.d.ts +12 -10
  16. package/dist/shaka-player.ui.debug.externs.js +78 -64
  17. package/dist/shaka-player.ui.debug.js +200 -193
  18. package/dist/shaka-player.ui.debug.map +4 -4
  19. package/dist/shaka-player.ui.externs.js +78 -64
  20. package/dist/shaka-player.ui.js +1250 -1243
  21. package/dist/shaka-player.ui.map +4 -4
  22. package/externs/shaka/player.js +32 -19
  23. package/lib/abr/simple_abr_manager.js +21 -26
  24. package/lib/ads/ad_manager.js +1 -1
  25. package/lib/cast/cast_proxy.js +1 -3
  26. package/lib/cast/cast_sender.js +8 -11
  27. package/lib/cea/cea608_memory.js +1 -1
  28. package/lib/cea/ts_cea_parser.js +10 -1
  29. package/lib/dash/dash_parser.js +3 -0
  30. package/lib/deprecate/deprecate.js +1 -1
  31. package/lib/hls/hls_parser.js +40 -19
  32. package/lib/media/drm_engine.js +6 -3
  33. package/lib/media/manifest_parser.js +3 -2
  34. package/lib/media/media_source_engine.js +25 -12
  35. package/lib/media/presentation_timeline.js +23 -0
  36. package/lib/media/segment_utils.js +10 -1
  37. package/lib/player.js +19 -6
  38. package/lib/polyfill/media_capabilities.js +20 -1
  39. package/lib/polyfill/mediasource.js +35 -43
  40. package/lib/text/vtt_text_parser.js +53 -52
  41. package/lib/transmuxer/h264.js +3 -3
  42. package/lib/transmuxer/h265.js +3 -3
  43. package/lib/transmuxer/ts_transmuxer.js +42 -3
  44. package/lib/util/error.js +1 -0
  45. package/lib/util/manifest_parser_utils.js +1 -0
  46. package/lib/util/mp4_parser.js +57 -0
  47. package/lib/util/platform.js +4 -0
  48. package/lib/util/ts_parser.js +101 -12
  49. package/package.json +2 -2
  50. package/ui/less/containers.less +1 -0
  51. package/lib/polyfill/storage_estimate.js +0 -57
package/lib/player.js CHANGED
@@ -742,7 +742,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
742
742
  // player having been initialized.
743
743
  if (mediaElement) {
744
744
  shaka.Deprecate.deprecateFeature(5,
745
- 'Player',
745
+ 'Player w/ mediaElement',
746
746
  'Please migrate from initializing Player with a mediaElement; ' +
747
747
  'use the attach method instead.');
748
748
  this.attach(mediaElement, /* initializeMediaSource= */ true);
@@ -1858,19 +1858,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1858
1858
  this.abrManager_ = abrFactory();
1859
1859
  if (typeof this.abrManager_.setMediaElement != 'function') {
1860
1860
  shaka.Deprecate.deprecateFeature(5,
1861
- 'AbrManager',
1861
+ 'AbrManager w/o setMediaElement',
1862
1862
  'Please use an AbrManager with setMediaElement function.');
1863
1863
  this.abrManager_.setMediaElement = () => {};
1864
1864
  }
1865
1865
  if (typeof this.abrManager_.setCmsdManager != 'function') {
1866
1866
  shaka.Deprecate.deprecateFeature(5,
1867
- 'AbrManager',
1867
+ 'AbrManager w/o setCmsdManager',
1868
1868
  'Please use an AbrManager with setCmsdManager function.');
1869
1869
  this.abrManager_.setCmsdManager = () => {};
1870
1870
  }
1871
1871
  if (typeof this.abrManager_.trySuggestStreams != 'function') {
1872
1872
  shaka.Deprecate.deprecateFeature(5,
1873
- 'AbrManager',
1873
+ 'AbrManager w/o trySuggestStreams',
1874
1874
  'Please use an AbrManager with trySuggestStreams function.');
1875
1875
  this.abrManager_.trySuggestStreams = () => {};
1876
1876
  }
@@ -4494,7 +4494,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4494
4494
 
4495
4495
  if (this.manifest_) {
4496
4496
  const timeline = this.manifest_.presentationTimeline;
4497
- const startTime = timeline.getPresentationStartTime();
4497
+ const startTime = timeline.getInitialProgramDateTime() ||
4498
+ timeline.getPresentationStartTime();
4498
4499
  return new Date(/* ms= */ (startTime + presentationTime) * 1000);
4499
4500
  } else if (this.video_ && this.video_.getStartDate) {
4500
4501
  // Apple's native HLS gives us getStartDate(), which is only available if
@@ -6099,6 +6100,18 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6099
6100
  // change.
6100
6101
  if (this.playhead_) {
6101
6102
  this.playhead_.notifyOfBufferingChange();
6103
+ // Skip the initial buffer gap
6104
+ const startTime = this.mediaSourceEngine_.bufferStart(contentType);
6105
+ if (
6106
+ !this.isLive() &&
6107
+ // If not paused then GapJumpingController will handle this gap.
6108
+ this.video_.paused &&
6109
+ startTime != null &&
6110
+ startTime > 0 &&
6111
+ this.playhead_.getTime() < startTime
6112
+ ) {
6113
+ this.playhead_.setStartTime(startTime);
6114
+ }
6102
6115
  }
6103
6116
  this.pollBufferState_();
6104
6117
 
@@ -6934,7 +6947,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6934
6947
  * @export
6935
6948
  */
6936
6949
  // eslint-disable-next-line no-useless-concat
6937
- shaka.Player.version = 'v4.7.10' + '-uncompiled'; // x-release-please-version
6950
+ shaka.Player.version = 'v4.7.12' + '-uncompiled'; // x-release-please-version
6938
6951
 
6939
6952
  // Initialize the deprecation system using the version string we just set
6940
6953
  // on the player.
@@ -300,7 +300,17 @@ shaka.polyfill.MediaCapabilities = class {
300
300
  // this query is specifically for HDR.
301
301
  displayType += '; eotf=smpte2084';
302
302
  }
303
- return cast.__platform__.canDisplayType(displayType);
303
+ let result = false;
304
+ if (displayType in shaka.polyfill.MediaCapabilities
305
+ .memoizedCanDisplayTypeRequests_) {
306
+ result = shaka.polyfill.MediaCapabilities
307
+ .memoizedCanDisplayTypeRequests_[displayType];
308
+ } else {
309
+ result = cast.__platform__.canDisplayType(displayType);
310
+ shaka.polyfill.MediaCapabilities
311
+ .memoizedCanDisplayTypeRequests_[displayType] = result;
312
+ }
313
+ return result;
304
314
  }
305
315
 
306
316
  /**
@@ -337,6 +347,15 @@ shaka.polyfill.MediaCapabilities.originalMcap = null;
337
347
  */
338
348
  shaka.polyfill.MediaCapabilities.memoizedMediaKeySystemAccessRequests_ = {};
339
349
 
350
+ /**
351
+ * A cache that stores the canDisplayType result of calling
352
+ * `cast.__platform__.canDisplayType`.
353
+ *
354
+ * @type {(Object<(!string), (!boolean)>)}
355
+ * @export
356
+ */
357
+ shaka.polyfill.MediaCapabilities.memoizedCanDisplayTypeRequests_ = {};
358
+
340
359
  // Install at a lower priority than MediaSource polyfill, so that we have
341
360
  // MediaSource available first.
342
361
  shaka.polyfill.register(shaka.polyfill.MediaCapabilities.install, -1);
@@ -30,13 +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 (window.cast && cast.__platform__ &&
36
- cast.__platform__.canDisplayType) {
37
- shaka.log.info('Patching Chromecast MSE bugs.');
38
- // Chromecast cannot make accurate determinations via isTypeSupported.
39
- shaka.polyfill.MediaSource.patchCastIsTypeSupported_();
40
35
  } else if (safariVersion) {
41
36
  // NOTE: shaka.Player.isBrowserSupported() has its own restrictions on
42
37
  // Safari version.
@@ -72,11 +67,6 @@ shaka.polyfill.MediaSource = class {
72
67
  // Bug filed: https://bugs.webkit.org/show_bug.cgi?id=165342
73
68
  shaka.polyfill.MediaSource.stubAbort_();
74
69
  }
75
- } else if (shaka.util.Platform.isEdge()) {
76
- shaka.log.info('Rejecting TS container.');
77
- // TS content is broken on Edge in general.
78
- // See https://github.com/shaka-project/shaka-player/issues/4955
79
- shaka.polyfill.MediaSource.rejectContainer_('mp2t');
80
70
  } else if (shaka.util.Platform.isTizen2() ||
81
71
  shaka.util.Platform.isTizen3() ||
82
72
  shaka.util.Platform.isTizen4()) {
@@ -89,6 +79,14 @@ shaka.polyfill.MediaSource = class {
89
79
  shaka.log.info('Using native MSE as-is.');
90
80
  }
91
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
+
92
90
  if (window.MediaSource &&
93
91
  MediaSource.isTypeSupported('video/webm; codecs="vp9"') &&
94
92
  !MediaSource.isTypeSupported('video/webm; codecs="vp09.00.10.08"')) {
@@ -157,12 +155,25 @@ shaka.polyfill.MediaSource = class {
157
155
  * @private
158
156
  */
159
157
  static rejectContainer_(container) {
160
- const isTypeSupported = MediaSource.isTypeSupported;
158
+ const isTypeSupported =
159
+ // eslint-disable-next-line no-restricted-syntax
160
+ MediaSource.isTypeSupported.bind(MediaSource);
161
161
 
162
162
  MediaSource.isTypeSupported = (mimeType) => {
163
163
  const actualContainer = shaka.util.MimeUtils.getContainerType(mimeType);
164
164
  return actualContainer != container && isTypeSupported(mimeType);
165
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
+ }
166
177
  }
167
178
 
168
179
  /**
@@ -174,44 +185,25 @@ shaka.polyfill.MediaSource = class {
174
185
  * @private
175
186
  */
176
187
  static rejectCodec_(codec) {
177
- const isTypeSupported = MediaSource.isTypeSupported;
188
+ const isTypeSupported =
189
+ // eslint-disable-next-line no-restricted-syntax
190
+ MediaSource.isTypeSupported.bind(MediaSource);
178
191
 
179
192
  MediaSource.isTypeSupported = (mimeType) => {
180
193
  const actualCodec = shaka.util.MimeUtils.getCodecBase(mimeType);
181
194
  return actualCodec != codec && isTypeSupported(mimeType);
182
195
  };
183
- }
184
196
 
185
- /**
186
- * Patch isTypeSupported() to chain to a private API on the Chromecast which
187
- * can query for support of detailed content parameters.
188
- *
189
- * @private
190
- */
191
- static patchCastIsTypeSupported_() {
192
- const originalIsTypeSupported = MediaSource.isTypeSupported;
193
-
194
- MediaSource.isTypeSupported = (mimeType) => {
195
- // Parse the basic MIME type from its parameters.
196
- const pieces = mimeType.split(/ *; */);
197
- pieces.shift(); // Remove basic MIME type from pieces.
197
+ if (window.ManagedMediaSource) {
198
+ const isTypeSupportedManaged =
199
+ // eslint-disable-next-line no-restricted-syntax
200
+ ManagedMediaSource.isTypeSupported.bind(ManagedMediaSource);
198
201
 
199
- const hasCodecs = pieces.some((piece) => piece.startsWith('codecs='));
200
- if (!hasCodecs) {
201
- // Though the original reason for this special case was not documented,
202
- // it is presumed to be because the platform won't accept a MIME type
203
- // without codecs in canDisplayType. It is valid, however, in
204
- // isTypeSupported.
205
- return originalIsTypeSupported(mimeType);
206
- }
207
-
208
- // Only canDisplayType can check extended MIME type parameters on this
209
- // platform (such as frame rate, resolution, etc).
210
- // In previous versions of this polyfill, the MIME type parameters were
211
- // manipulated, filtered, or extended. This is no longer true, so we pass
212
- // the full MIME type to the platform as we received it.
213
- return cast.__platform__.canDisplayType(mimeType);
214
- };
202
+ window.ManagedMediaSource.isTypeSupported = (mimeType) => {
203
+ const actualCodec = shaka.util.MimeUtils.getCodecBase(mimeType);
204
+ return actualCodec != codec && isTypeSupportedManaged(mimeType);
205
+ };
206
+ }
215
207
  }
216
208
 
217
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' in sequence mode.
88
- // Note that an offset based on the first video
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
- // In we use HLS segments mode, with the presence of this tag we need
130
- // calculate the offset from the segment startTime.
131
- offset = time.segmentStart;
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
  *
@@ -687,6 +694,8 @@ shaka.text.VttTextParser = class {
687
694
  refCue.fontFamily, cue.fontFamily);
688
695
  cue.fontSize = VttTextParser.getOrDefault_(
689
696
  refCue.fontSize, cue.fontSize);
697
+ cue.textShadow = VttTextParser.getOrDefault_(
698
+ refCue.textShadow, cue.textShadow);
690
699
 
691
700
  // Overwrite with new values as unable to determine
692
701
  // if new value is set or not
@@ -695,7 +704,6 @@ shaka.text.VttTextParser = class {
695
704
  cue.opacity = refCue.opacity;
696
705
  cue.rubyTag = refCue.rubyTag;
697
706
  cue.textCombineUpright = refCue.textCombineUpright;
698
- cue.textShadow = refCue.textShadow;
699
707
  cue.wrapLine = refCue.wrapLine;
700
708
  }
701
709
 
@@ -1060,13 +1068,6 @@ shaka.text.VttTextParser = class {
1060
1068
  */
1061
1069
  shaka.text.VttTextParser.MPEG_TIMESCALE_ = 90000;
1062
1070
 
1063
- /**
1064
- * At this value, timestamps roll over in TS content.
1065
- * @const {number}
1066
- * @private
1067
- */
1068
- shaka.text.VttTextParser.TS_ROLLOVER_ = 0x200000000;
1069
-
1070
1071
  /**
1071
1072
  * @const
1072
1073
  * @private {!RegExp}
@@ -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 < 16) {
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(shaka.util.Uint8ArrayUtils.concat(
271
- naluLength, nalu.fullData));
270
+ nalusData.push(naluLength);
271
+ nalusData.push(nalu.fullData);
272
272
  }
273
273
  }
274
274
  if (!nalusData.length) {
@@ -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 < 16) {
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(shaka.util.Uint8ArrayUtils.concat(
656
- naluLength, nalu.fullData));
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/error.js CHANGED
@@ -527,6 +527,7 @@ shaka.util.Error.Code = {
527
527
  * <li>Configure the server to accept a HEAD request for the manifest.
528
528
  * </ul>
529
529
  * <br> error.data[0] is the manifest URI.
530
+ * <br> error.data[1] is the detected or specified MIME type.
530
531
  */
531
532
  'UNABLE_TO_GUESS_MANIFEST_TYPE': 4000,
532
533
 
@@ -203,6 +203,7 @@ shaka.util.ManifestParserUtils.AUDIO_CODEC_REGEXPS_ = [
203
203
  /^[ae]c-3$/,
204
204
  /^ac-4$/,
205
205
  /^dts[cex]$/, // DTS Digital Surround (dtsc), DTS Express (dtse), DTS:X (dtsx)
206
+ /^iamf/,
206
207
  ];
207
208
 
208
209
 
@@ -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.
@@ -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