shaka-player 4.7.12 → 4.7.14
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/AUTHORS +1 -0
- package/CONTRIBUTORS +1 -0
- package/dist/deps.js +3 -3
- package/dist/locales.js +34 -34
- package/dist/shaka-player.compiled.d.ts +3 -2
- package/dist/shaka-player.compiled.debug.d.ts +3 -2
- package/dist/shaka-player.compiled.debug.externs.js +4 -0
- package/dist/shaka-player.compiled.debug.js +265 -257
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +4 -0
- package/dist/shaka-player.compiled.js +1150 -1140
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +3 -2
- package/dist/shaka-player.ui.debug.d.ts +3 -2
- package/dist/shaka-player.ui.debug.externs.js +4 -0
- package/dist/shaka-player.ui.debug.js +394 -386
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +4 -0
- package/dist/shaka-player.ui.js +1306 -1296
- package/dist/shaka-player.ui.map +4 -4
- package/externs/shaka/manifest.js +11 -1
- package/externs/shaka/offline.js +7 -1
- package/lib/ads/client_side_ad_manager.js +4 -2
- package/lib/dash/dash_parser.js +10 -3
- package/lib/dash/segment_base.js +8 -1
- package/lib/dash/segment_list.js +24 -18
- package/lib/dash/segment_template.js +45 -3
- package/lib/hls/hls_parser.js +18 -1
- package/lib/media/drm_engine.js +17 -3
- package/lib/media/media_source_engine.js +87 -27
- package/lib/media/segment_index.js +31 -20
- package/lib/media/segment_reference.js +12 -0
- package/lib/media/streaming_engine.js +10 -28
- package/lib/mss/mss_parser.js +4 -0
- package/lib/offline/indexeddb/v1_storage_cell.js +2 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +2 -0
- package/lib/offline/manifest_converter.js +10 -3
- package/lib/offline/storage.js +2 -0
- package/lib/player.js +57 -17
- package/lib/text/ui_text_displayer.js +10 -1
- package/lib/util/mime_utils.js +10 -5
- package/lib/util/periods.js +5 -37
- package/lib/util/platform.js +31 -9
- package/lib/util/stream_utils.js +340 -273
- package/package.json +1 -1
- package/ui/resolution_selection.js +1 -0
|
@@ -363,26 +363,27 @@ shaka.media.SegmentIndex = class {
|
|
|
363
363
|
|
|
364
364
|
// Adjust the last SegmentReference.
|
|
365
365
|
const lastReference = this.references[this.references.length - 1];
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
366
|
+
const newReference = new shaka.media.SegmentReference(
|
|
367
|
+
lastReference.startTime,
|
|
368
|
+
/* endTime= */ windowEnd,
|
|
369
|
+
lastReference.getUrisInner,
|
|
370
|
+
lastReference.startByte,
|
|
371
|
+
lastReference.endByte,
|
|
372
|
+
lastReference.initSegmentReference,
|
|
373
|
+
lastReference.timestampOffset,
|
|
374
|
+
lastReference.appendWindowStart,
|
|
375
|
+
lastReference.appendWindowEnd,
|
|
376
|
+
lastReference.partialReferences,
|
|
377
|
+
lastReference.tilesLayout,
|
|
378
|
+
lastReference.tileDuration,
|
|
379
|
+
lastReference.syncTime,
|
|
380
|
+
lastReference.status,
|
|
381
|
+
lastReference.aes128Key,
|
|
382
|
+
);
|
|
383
|
+
newReference.mimeType = lastReference.mimeType;
|
|
384
|
+
newReference.codecs = lastReference.codecs;
|
|
385
|
+
newReference.discontinuitySequence = lastReference.discontinuitySequence;
|
|
386
|
+
this.references[this.references.length - 1] = newReference;
|
|
386
387
|
}
|
|
387
388
|
|
|
388
389
|
|
|
@@ -693,6 +694,16 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
|
|
|
693
694
|
this.indexes_ = [];
|
|
694
695
|
}
|
|
695
696
|
|
|
697
|
+
/**
|
|
698
|
+
* @override
|
|
699
|
+
* @export
|
|
700
|
+
*/
|
|
701
|
+
forEachTopLevelReference(fn) {
|
|
702
|
+
for (const index of this.indexes_) {
|
|
703
|
+
index.forEachTopLevelReference(fn);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
696
707
|
/**
|
|
697
708
|
* @override
|
|
698
709
|
* @export
|
|
@@ -598,6 +598,18 @@ shaka.media.SegmentReference = class {
|
|
|
598
598
|
this.offset(offset);
|
|
599
599
|
}
|
|
600
600
|
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Updates the init segment reference and propagates the update to all partial
|
|
604
|
+
* references.
|
|
605
|
+
* @param {shaka.media.InitSegmentReference} initSegmentReference
|
|
606
|
+
*/
|
|
607
|
+
updateInitSegmentReference(initSegmentReference) {
|
|
608
|
+
this.initSegmentReference = initSegmentReference;
|
|
609
|
+
for (const partialReference of this.partialReferences) {
|
|
610
|
+
partialReference.updateInitSegmentReference(initSegmentReference);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
601
613
|
};
|
|
602
614
|
|
|
603
615
|
|
|
@@ -881,7 +881,6 @@ shaka.media.StreamingEngine = class {
|
|
|
881
881
|
// The playhead might be seeking on startup, if a start time is set, so
|
|
882
882
|
// start "seeked" as true.
|
|
883
883
|
seeked: true,
|
|
884
|
-
needsResync: false,
|
|
885
884
|
recovering: false,
|
|
886
885
|
hasError: false,
|
|
887
886
|
operation: null,
|
|
@@ -1248,11 +1247,6 @@ shaka.media.StreamingEngine = class {
|
|
|
1248
1247
|
shaka.log.v1(
|
|
1249
1248
|
logPrefix, 'looking up segment from new stream endTime:', time);
|
|
1250
1249
|
|
|
1251
|
-
// Using a new iterator means we need to resync the stream in sequence
|
|
1252
|
-
// mode. The buffered range might not align perfectly with the last
|
|
1253
|
-
// segment end time, so we may end up repeating a segment. Resyncing
|
|
1254
|
-
// makes this safe to do.
|
|
1255
|
-
mediaState.needsResync = true;
|
|
1256
1250
|
mediaState.segmentIterator =
|
|
1257
1251
|
mediaState.stream.segmentIndex.getIteratorForTime(time);
|
|
1258
1252
|
const ref = mediaState.segmentIterator &&
|
|
@@ -1730,7 +1724,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1730
1724
|
const isResetMediaSourceNecessary =
|
|
1731
1725
|
mediaState.lastCodecs && mediaState.lastMimeType &&
|
|
1732
1726
|
this.playerInterface_.mediaSourceEngine.isResetMediaSourceNecessary(
|
|
1733
|
-
mediaState.type, mediaState.stream);
|
|
1727
|
+
mediaState.type, mediaState.stream, mimeType, codecs);
|
|
1734
1728
|
if (isResetMediaSourceNecessary) {
|
|
1735
1729
|
let otherState = null;
|
|
1736
1730
|
if (mediaState.type === ContentType.VIDEO) {
|
|
@@ -1739,9 +1733,16 @@ shaka.media.StreamingEngine = class {
|
|
|
1739
1733
|
otherState = this.mediaStates_.get(ContentType.VIDEO);
|
|
1740
1734
|
}
|
|
1741
1735
|
if (otherState) {
|
|
1736
|
+
// First, abort all operations in progress on the other stream.
|
|
1737
|
+
await this.abortOperations_(otherState).catch(() => {});
|
|
1738
|
+
// Then clear our cache of the last init segment, since MSE will be
|
|
1739
|
+
// reloaded and no init segment will be there post-reload.
|
|
1742
1740
|
otherState.lastInitSegmentReference = null;
|
|
1741
|
+
// Now force the existing buffer to be cleared. It is not necessary
|
|
1742
|
+
// to perform the MSE clear operation, but this has the side-effect
|
|
1743
|
+
// that our state for that stream will then match MSE's post-reload
|
|
1744
|
+
// state.
|
|
1743
1745
|
this.forceClearBuffer_(otherState);
|
|
1744
|
-
this.abortOperations_(otherState).catch(() => {});
|
|
1745
1746
|
}
|
|
1746
1747
|
}
|
|
1747
1748
|
|
|
@@ -1772,19 +1773,6 @@ shaka.media.StreamingEngine = class {
|
|
|
1772
1773
|
appendWindowEnd, ignoreTimestampOffset,
|
|
1773
1774
|
reference.mimeType || mediaState.stream.mimeType,
|
|
1774
1775
|
reference.codecs || mediaState.stream.codecs, streamsByType);
|
|
1775
|
-
if (isResetMediaSourceNecessary) {
|
|
1776
|
-
let otherState = null;
|
|
1777
|
-
if (mediaState.type === ContentType.VIDEO) {
|
|
1778
|
-
otherState = this.mediaStates_.get(ContentType.AUDIO);
|
|
1779
|
-
} else if (mediaState.type === ContentType.AUDIO) {
|
|
1780
|
-
otherState = this.mediaStates_.get(ContentType.VIDEO);
|
|
1781
|
-
}
|
|
1782
|
-
if (otherState) {
|
|
1783
|
-
otherState.waitingToClearBuffer = false;
|
|
1784
|
-
otherState.performingUpdate = false;
|
|
1785
|
-
this.forceClearBuffer_(otherState);
|
|
1786
|
-
}
|
|
1787
|
-
}
|
|
1788
1776
|
} catch (error) {
|
|
1789
1777
|
mediaState.lastAppendWindowStart = null;
|
|
1790
1778
|
mediaState.lastAppendWindowEnd = null;
|
|
@@ -1879,9 +1867,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1879
1867
|
// Across discontinuity bounds, we should resync timestamps for
|
|
1880
1868
|
// sequence mode playbacks. The next segment appended should
|
|
1881
1869
|
// land at its theoretical timestamp from the segment index.
|
|
1882
|
-
if (reference.discontinuitySequence != lastDiscontinuitySequence
|
|
1883
|
-
mediaState.needsResync) {
|
|
1884
|
-
mediaState.needsResync = false;
|
|
1870
|
+
if (reference.discontinuitySequence != lastDiscontinuitySequence) {
|
|
1885
1871
|
operations.push(this.playerInterface_.mediaSourceEngine.resync(
|
|
1886
1872
|
mediaState.type, reference.startTime));
|
|
1887
1873
|
}
|
|
@@ -2591,7 +2577,6 @@ shaka.media.StreamingEngine.PlayerInterface;
|
|
|
2591
2577
|
* clearBufferSafeMargin: number,
|
|
2592
2578
|
* clearingBuffer: boolean,
|
|
2593
2579
|
* seeked: boolean,
|
|
2594
|
-
* needsResync: boolean,
|
|
2595
2580
|
* adaptation: boolean,
|
|
2596
2581
|
* recovering: boolean,
|
|
2597
2582
|
* hasError: boolean,
|
|
@@ -2646,9 +2631,6 @@ shaka.media.StreamingEngine.PlayerInterface;
|
|
|
2646
2631
|
* True indicates that the presentation just seeked.
|
|
2647
2632
|
* @property {boolean} adaptation
|
|
2648
2633
|
* True indicates that the presentation just automatically switched variants.
|
|
2649
|
-
* @property {boolean} needsResync
|
|
2650
|
-
* True indicates that the stream needs to be resynced in sequence mode,
|
|
2651
|
-
* regardless of discontinuity sequence.
|
|
2652
2634
|
* @property {boolean} recovering
|
|
2653
2635
|
* True indicates that the last segment was not appended because it could not
|
|
2654
2636
|
* fit in the buffer.
|
package/lib/mss/mss_parser.js
CHANGED
|
@@ -19,6 +19,7 @@ goog.require('shaka.net.NetworkingEngine');
|
|
|
19
19
|
goog.require('shaka.util.Error');
|
|
20
20
|
goog.require('shaka.util.LanguageUtils');
|
|
21
21
|
goog.require('shaka.util.ManifestParserUtils');
|
|
22
|
+
goog.require('shaka.util.MimeUtils');
|
|
22
23
|
goog.require('shaka.util.Mp4Generator');
|
|
23
24
|
goog.require('shaka.util.OperationManager');
|
|
24
25
|
goog.require('shaka.util.Timer');
|
|
@@ -556,6 +557,7 @@ shaka.mss.MssParser = class {
|
|
|
556
557
|
accessibilityPurpose: null,
|
|
557
558
|
external: false,
|
|
558
559
|
fastSwitching: false,
|
|
560
|
+
fullMimeTypes: new Set(),
|
|
559
561
|
};
|
|
560
562
|
|
|
561
563
|
// This is specifically for text tracks.
|
|
@@ -634,6 +636,8 @@ shaka.mss.MssParser = class {
|
|
|
634
636
|
}
|
|
635
637
|
break;
|
|
636
638
|
}
|
|
639
|
+
stream.fullMimeTypes.add(shaka.util.MimeUtils.getFullType(
|
|
640
|
+
stream.mimeType, stream.codecs));
|
|
637
641
|
|
|
638
642
|
// Lazy-Load the segment index to avoid create all init segment at the
|
|
639
643
|
// same time
|
|
@@ -14,6 +14,7 @@ goog.require('shaka.media.SegmentIndex');
|
|
|
14
14
|
goog.require('shaka.media.SegmentReference');
|
|
15
15
|
goog.require('shaka.offline.OfflineUri');
|
|
16
16
|
goog.require('shaka.util.ManifestParserUtils');
|
|
17
|
+
goog.require('shaka.util.MimeUtils');
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -169,7 +170,7 @@ shaka.offline.ManifestConverter = class {
|
|
|
169
170
|
fromStreamDB_(streamDB, timeline) {
|
|
170
171
|
/** @type {!Array.<!shaka.media.SegmentReference>} */
|
|
171
172
|
const segments = streamDB.segments.map(
|
|
172
|
-
(segment, index) => this.fromSegmentDB_(index, segment));
|
|
173
|
+
(segment, index) => this.fromSegmentDB_(index, segment, streamDB));
|
|
173
174
|
|
|
174
175
|
timeline.notifySegments(segments);
|
|
175
176
|
|
|
@@ -212,6 +213,8 @@ shaka.offline.ManifestConverter = class {
|
|
|
212
213
|
accessibilityPurpose: null,
|
|
213
214
|
external: streamDB.external,
|
|
214
215
|
fastSwitching: streamDB.fastSwitching,
|
|
216
|
+
fullMimeTypes: new Set([shaka.util.MimeUtils.getFullType(
|
|
217
|
+
streamDB.mimeType, streamDB.codecs)]),
|
|
215
218
|
};
|
|
216
219
|
|
|
217
220
|
return stream;
|
|
@@ -220,10 +223,11 @@ shaka.offline.ManifestConverter = class {
|
|
|
220
223
|
/**
|
|
221
224
|
* @param {number} index
|
|
222
225
|
* @param {shaka.extern.SegmentDB} segmentDB
|
|
226
|
+
* @param {shaka.extern.StreamDB} streamDB
|
|
223
227
|
* @return {!shaka.media.SegmentReference}
|
|
224
228
|
* @private
|
|
225
229
|
*/
|
|
226
|
-
fromSegmentDB_(index, segmentDB) {
|
|
230
|
+
fromSegmentDB_(index, segmentDB, streamDB) {
|
|
227
231
|
/** @type {!shaka.offline.OfflineUri} */
|
|
228
232
|
const uri = shaka.offline.OfflineUri.segment(
|
|
229
233
|
this.mechanism_, this.cell_, segmentDB.dataKey);
|
|
@@ -231,7 +235,7 @@ shaka.offline.ManifestConverter = class {
|
|
|
231
235
|
const initSegmentReference = segmentDB.initSegmentKey != null ?
|
|
232
236
|
this.fromInitSegmentDB_(segmentDB.initSegmentKey) : null;
|
|
233
237
|
|
|
234
|
-
|
|
238
|
+
const ref = new shaka.media.SegmentReference(
|
|
235
239
|
segmentDB.startTime,
|
|
236
240
|
segmentDB.endTime,
|
|
237
241
|
() => [uri.toString()],
|
|
@@ -243,6 +247,9 @@ shaka.offline.ManifestConverter = class {
|
|
|
243
247
|
segmentDB.appendWindowEnd,
|
|
244
248
|
/* partialReferences= */ [],
|
|
245
249
|
segmentDB.tilesLayout || '');
|
|
250
|
+
ref.mimeType = segmentDB.mimeType || streamDB.mimeType || '';
|
|
251
|
+
ref.codecs = segmentDB.codecs || streamDB.codecs || '';
|
|
252
|
+
return ref;
|
|
246
253
|
}
|
|
247
254
|
|
|
248
255
|
/**
|
package/lib/offline/storage.js
CHANGED
|
@@ -1389,6 +1389,8 @@ shaka.offline.Storage = class {
|
|
|
1389
1389
|
tilesLayout: segment.tilesLayout,
|
|
1390
1390
|
pendingSegmentRefId,
|
|
1391
1391
|
dataKey: 0,
|
|
1392
|
+
mimeType: segment.mimeType,
|
|
1393
|
+
codecs: segment.codecs,
|
|
1392
1394
|
};
|
|
1393
1395
|
streamDb.segments.push(segmentDB);
|
|
1394
1396
|
groupId = (groupId + 1) % numberOfParallelDownloads;
|
package/lib/player.js
CHANGED
|
@@ -1977,31 +1977,49 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1977
1977
|
this.config_.playRangeStart,
|
|
1978
1978
|
this.config_.playRangeEnd);
|
|
1979
1979
|
|
|
1980
|
-
this.playhead_ = this.createPlayhead(this.startTime_);
|
|
1981
|
-
this.playheadObservers_ =
|
|
1982
|
-
this.createPlayheadObserversForMSE_(startTimeOfLoad);
|
|
1983
1980
|
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1981
|
+
const setupPlayhead = (startTime) => {
|
|
1982
|
+
this.playhead_ = this.createPlayhead(startTime);
|
|
1983
|
+
this.playheadObservers_ =
|
|
1984
|
+
this.createPlayheadObserversForMSE_(startTimeOfLoad);
|
|
1985
|
+
|
|
1986
|
+
// We need to start the buffer management code near the end because it
|
|
1987
|
+
// will set the initial buffering state and that depends on other
|
|
1988
|
+
// components being initialized.
|
|
1989
|
+
const rebufferThreshold = Math.max(
|
|
1990
|
+
this.manifest_.minBufferTime,
|
|
1991
|
+
this.config_.streaming.rebufferingGoal);
|
|
1992
|
+
this.startBufferManagement_(mediaElement, rebufferThreshold);
|
|
1993
|
+
};
|
|
1994
|
+
|
|
1995
|
+
if (!this.config_.streaming.startAtSegmentBoundary) {
|
|
1996
|
+
setupPlayhead(this.startTime_);
|
|
1997
|
+
}
|
|
1990
1998
|
|
|
1991
1999
|
// Now we can switch to the initial variant.
|
|
1992
2000
|
if (!activeVariant) {
|
|
1993
2001
|
goog.asserts.assert(initialVariant,
|
|
1994
2002
|
'Must have choosen an initial variant!');
|
|
1995
2003
|
|
|
1996
|
-
this.switchVariant_(initialVariant, /* fromAdaptation= */ true,
|
|
1997
|
-
/* clearBuffer= */ false, /* safeMargin= */ 0);
|
|
1998
|
-
|
|
1999
2004
|
// Now that we have initial streams, we may adjust the start time to
|
|
2000
2005
|
// align to a segment boundary.
|
|
2001
2006
|
if (this.config_.streaming.startAtSegmentBoundary) {
|
|
2002
|
-
this.
|
|
2003
|
-
|
|
2007
|
+
const timeline = this.manifest_.presentationTimeline;
|
|
2008
|
+
let initialTime = this.startTime_ || this.video_.currentTime;
|
|
2009
|
+
const seekRangeStart = timeline.getSeekRangeStart();
|
|
2010
|
+
const seekRangeEnd = timeline.getSeekRangeEnd();
|
|
2011
|
+
if (initialTime < seekRangeStart) {
|
|
2012
|
+
initialTime = seekRangeStart;
|
|
2013
|
+
} else if (initialTime > seekRangeEnd) {
|
|
2014
|
+
initialTime = seekRangeEnd;
|
|
2015
|
+
}
|
|
2016
|
+
const startTime = await this.adjustStartTime_(
|
|
2017
|
+
initialVariant, initialTime);
|
|
2018
|
+
setupPlayhead(startTime);
|
|
2004
2019
|
}
|
|
2020
|
+
|
|
2021
|
+
this.switchVariant_(initialVariant, /* fromAdaptation= */ true,
|
|
2022
|
+
/* clearBuffer= */ false, /* safeMargin= */ 0);
|
|
2005
2023
|
}
|
|
2006
2024
|
|
|
2007
2025
|
this.playhead_.ready();
|
|
@@ -2174,12 +2192,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2174
2192
|
accessibilityPurpose: null,
|
|
2175
2193
|
external: false,
|
|
2176
2194
|
fastSwitching: false,
|
|
2195
|
+
fullMimeTypes: new Set(),
|
|
2177
2196
|
},
|
|
2178
2197
|
bandwidth: 100,
|
|
2179
2198
|
allowedByApplication: true,
|
|
2180
2199
|
allowedByKeySystem: true,
|
|
2181
2200
|
decodingInfos: [],
|
|
2182
2201
|
};
|
|
2202
|
+
variant.video.fullMimeTypes.add(shaka.util.MimeUtils.getFullType(
|
|
2203
|
+
variant.video.mimeType, variant.video.codecs));
|
|
2183
2204
|
|
|
2184
2205
|
this.drmEngine_.setSrcEquals(/* srcEquals= */ true);
|
|
2185
2206
|
await this.drmEngine_.initForPlayback(
|
|
@@ -2313,6 +2334,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2313
2334
|
mediaElement.load();
|
|
2314
2335
|
}
|
|
2315
2336
|
|
|
2337
|
+
// In Safari using HLS won't load anything unless you call load()
|
|
2338
|
+
// explicitly, no matter the value of the preload attribute.
|
|
2339
|
+
// Note: this only happens when there are not autoplay.
|
|
2340
|
+
if (mediaElement.preload != 'none' && !mediaElement.autoplay &&
|
|
2341
|
+
shaka.util.MimeUtils.isHlsType(mimeType) &&
|
|
2342
|
+
shaka.util.Platform.safariVersion()) {
|
|
2343
|
+
mediaElement.load();
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2316
2346
|
// Set the load mode last so that we know that all our components are
|
|
2317
2347
|
// initialized.
|
|
2318
2348
|
this.loadMode_ = shaka.Player.LoadMode.SRC_EQUALS;
|
|
@@ -3620,12 +3650,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3620
3650
|
const currentTime = this.video_.currentTime;
|
|
3621
3651
|
const seekRange = this.seekRange();
|
|
3622
3652
|
const safeSeekOffset = this.config_.streaming.safeSeekOffset;
|
|
3653
|
+
|
|
3654
|
+
// Cancel trick play if we hit the beginning or end of the seekable
|
|
3655
|
+
// (Sub-second accuracy not required here)
|
|
3623
3656
|
if (rate > 0) {
|
|
3624
|
-
if (currentTime >= seekRange.end) {
|
|
3657
|
+
if (Math.floor(currentTime) >= Math.floor(seekRange.end)) {
|
|
3625
3658
|
this.cancelTrickPlay();
|
|
3626
3659
|
}
|
|
3627
3660
|
} else {
|
|
3628
|
-
if (currentTime <=
|
|
3661
|
+
if (Math.floor(currentTime) <=
|
|
3662
|
+
Math.floor(seekRange.start + safeSeekOffset)) {
|
|
3629
3663
|
this.cancelTrickPlay();
|
|
3630
3664
|
}
|
|
3631
3665
|
}
|
|
@@ -4804,6 +4838,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4804
4838
|
accessibilityPurpose: null,
|
|
4805
4839
|
external: true,
|
|
4806
4840
|
fastSwitching: false,
|
|
4841
|
+
fullMimeTypes: new Set([shaka.util.MimeUtils.getFullType(
|
|
4842
|
+
mimeType || '', codec || '')]),
|
|
4807
4843
|
};
|
|
4808
4844
|
|
|
4809
4845
|
const fullMimeType = shaka.util.MimeUtils.getFullType(
|
|
@@ -4960,6 +4996,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4960
4996
|
accessibilityPurpose: null,
|
|
4961
4997
|
external: true,
|
|
4962
4998
|
fastSwitching: false,
|
|
4999
|
+
fullMimeTypes: new Set([shaka.util.MimeUtils.getFullType(
|
|
5000
|
+
mimeType || '', '')]),
|
|
4963
5001
|
};
|
|
4964
5002
|
|
|
4965
5003
|
if (this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS) {
|
|
@@ -5390,6 +5428,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5390
5428
|
accessibilityPurpose: null,
|
|
5391
5429
|
external: false,
|
|
5392
5430
|
fastSwitching: false,
|
|
5431
|
+
fullMimeTypes: new Set([shaka.util.MimeUtils.getFullType(
|
|
5432
|
+
mimeType, '')]),
|
|
5393
5433
|
};
|
|
5394
5434
|
manifest.textStreams.push(textStream);
|
|
5395
5435
|
closedCaptionsSet.add(id);
|
|
@@ -6947,7 +6987,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6947
6987
|
* @export
|
|
6948
6988
|
*/
|
|
6949
6989
|
// eslint-disable-next-line no-useless-concat
|
|
6950
|
-
shaka.Player.version = 'v4.7.
|
|
6990
|
+
shaka.Player.version = 'v4.7.14' + '-uncompiled'; // x-release-please-version
|
|
6951
6991
|
|
|
6952
6992
|
// Initialize the deprecation system using the version string we just set
|
|
6953
6993
|
// on the player.
|
|
@@ -72,7 +72,9 @@ shaka.text.UITextDisplayer = class {
|
|
|
72
72
|
|
|
73
73
|
/** @private {shaka.util.Timer} */
|
|
74
74
|
this.captionsTimer_ = new shaka.util.Timer(() => {
|
|
75
|
-
this.
|
|
75
|
+
if (!this.video_.paused) {
|
|
76
|
+
this.updateCaptions_();
|
|
77
|
+
}
|
|
76
78
|
}).tickEvery(updatePeriod);
|
|
77
79
|
|
|
78
80
|
/**
|
|
@@ -93,6 +95,10 @@ shaka.text.UITextDisplayer = class {
|
|
|
93
95
|
this.updateCaptions_(/* forceUpdate= */ true);
|
|
94
96
|
});
|
|
95
97
|
|
|
98
|
+
this.eventManager_.listen(this.video_, 'seeking', () => {
|
|
99
|
+
this.updateCaptions_(/* forceUpdate= */ true);
|
|
100
|
+
});
|
|
101
|
+
|
|
96
102
|
// From: https://html.spec.whatwg.org/multipage/media.html#dom-video-videowidth
|
|
97
103
|
// Whenever the natural width or natural height of the video changes
|
|
98
104
|
// (including, for example, because the selected video track was changed),
|
|
@@ -339,6 +345,9 @@ shaka.text.UITextDisplayer = class {
|
|
|
339
345
|
const cueRegistry = this.currentCuesMap_.get(cue);
|
|
340
346
|
goog.asserts.assert(cueRegistry, 'cueRegistry should exist.');
|
|
341
347
|
if (cueRegistry.regionElement) {
|
|
348
|
+
if (cueRegistry.regionElement.contains(container)) {
|
|
349
|
+
cueRegistry.regionElement.removeChild(container);
|
|
350
|
+
}
|
|
342
351
|
container.appendChild(cueRegistry.regionElement);
|
|
343
352
|
cueRegistry.regionElement.appendChild(cueRegistry.cueElement);
|
|
344
353
|
} else {
|
package/lib/util/mime_utils.js
CHANGED
|
@@ -84,17 +84,22 @@ shaka.util.MimeUtils = class {
|
|
|
84
84
|
* beyond the container and codec type, when available.
|
|
85
85
|
*
|
|
86
86
|
* @param {shaka.extern.Stream} stream
|
|
87
|
+
* @param {string} mimeType
|
|
88
|
+
* @param {string} codecs
|
|
87
89
|
* @return {string}
|
|
88
90
|
*/
|
|
89
|
-
static getExtendedType(stream) {
|
|
90
|
-
const components = [
|
|
91
|
+
static getExtendedType(stream, mimeType, codecs) {
|
|
92
|
+
const components = [mimeType];
|
|
91
93
|
|
|
92
94
|
const extendedMimeParams = shaka.util.MimeUtils.EXTENDED_MIME_PARAMETERS_;
|
|
93
95
|
extendedMimeParams.forEach((mimeKey, streamKey) => {
|
|
94
96
|
const value = stream[streamKey];
|
|
95
|
-
if (streamKey == 'codecs'
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
if (streamKey == 'codecs') {
|
|
98
|
+
if (shaka.util.MimeUtils.RAW_FORMATS.includes(stream.mimeType)) {
|
|
99
|
+
// Skip codecs for raw formats
|
|
100
|
+
} else {
|
|
101
|
+
components.push('codecs="' + codecs + '"');
|
|
102
|
+
}
|
|
98
103
|
} else if (value) {
|
|
99
104
|
components.push(mimeKey + '="' + value + '"');
|
|
100
105
|
}
|
package/lib/util/periods.js
CHANGED
|
@@ -599,14 +599,6 @@ shaka.util.PeriodCombiner = class {
|
|
|
599
599
|
goog.asserts.assert(match.segmentIndex,
|
|
600
600
|
'stream should have a segmentIndex.');
|
|
601
601
|
if (match.segmentIndex) {
|
|
602
|
-
match.segmentIndex.forEachTopLevelReference((ref) => {
|
|
603
|
-
ref.codecs = match.codecs;
|
|
604
|
-
ref.mimeType = match.mimeType;
|
|
605
|
-
if (ref.initSegmentReference) {
|
|
606
|
-
ref.initSegmentReference.codecs = match.codecs;
|
|
607
|
-
ref.initSegmentReference.mimeType = match.mimeType;
|
|
608
|
-
}
|
|
609
|
-
});
|
|
610
602
|
outputStream.segmentIndex.appendSegmentIndex(match.segmentIndex);
|
|
611
603
|
}
|
|
612
604
|
}
|
|
@@ -721,39 +713,14 @@ shaka.util.PeriodCombiner = class {
|
|
|
721
713
|
|
|
722
714
|
if (used) {
|
|
723
715
|
unusedStreamsPerPeriod[i].delete(match);
|
|
724
|
-
// Add the
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
codecs.add(codec);
|
|
716
|
+
// Add the full mimetypes to the stream.
|
|
717
|
+
if (match.fullMimeTypes) {
|
|
718
|
+
for (const fullMimeType of match.fullMimeTypes.values()) {
|
|
719
|
+
outputStream.fullMimeTypes.add(fullMimeType);
|
|
729
720
|
}
|
|
730
721
|
}
|
|
731
|
-
const PeriodCombiner = shaka.util.PeriodCombiner;
|
|
732
|
-
outputStream.codecs = PeriodCombiner.filterDuplicateCodecs_(
|
|
733
|
-
Array.from(codecs)).join(',');
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
/**
|
|
739
|
-
* @param {!Array.<string>} codecs
|
|
740
|
-
* @return {!Array.<string>} codecs
|
|
741
|
-
* @private
|
|
742
|
-
*/
|
|
743
|
-
static filterDuplicateCodecs_(codecs) {
|
|
744
|
-
// Filter out duplicate codecs.
|
|
745
|
-
const seen = new Set();
|
|
746
|
-
const ret = [];
|
|
747
|
-
for (const codec of codecs) {
|
|
748
|
-
const shortCodec = shaka.util.MimeUtils.getCodecBase(codec);
|
|
749
|
-
if (!seen.has(shortCodec)) {
|
|
750
|
-
ret.push(codec);
|
|
751
|
-
seen.add(shortCodec);
|
|
752
|
-
} else {
|
|
753
|
-
shaka.log.debug('Ignoring duplicate codec');
|
|
754
722
|
}
|
|
755
723
|
}
|
|
756
|
-
return ret;
|
|
757
724
|
}
|
|
758
725
|
|
|
759
726
|
/**
|
|
@@ -1578,6 +1545,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1578
1545
|
accessibilityPurpose: null,
|
|
1579
1546
|
external: false,
|
|
1580
1547
|
fastSwitching: false,
|
|
1548
|
+
fullMimeTypes: new Set(),
|
|
1581
1549
|
};
|
|
1582
1550
|
}
|
|
1583
1551
|
|
package/lib/util/platform.js
CHANGED
|
@@ -149,10 +149,9 @@ shaka.util.Platform = class {
|
|
|
149
149
|
* @return {boolean}
|
|
150
150
|
*/
|
|
151
151
|
static isWebOS3() {
|
|
152
|
-
// See:
|
|
153
|
-
return shaka.util.Platform.
|
|
154
|
-
shaka.util.Platform.
|
|
155
|
-
'Chrome/38.0.2125.122 Safari/537.36');
|
|
152
|
+
// See: https://webostv.developer.lge.com/develop/specifications/web-api-and-web-engine#useragent-string
|
|
153
|
+
return shaka.util.Platform.isWebOS() &&
|
|
154
|
+
shaka.util.Platform.chromeVersion() === 38;
|
|
156
155
|
}
|
|
157
156
|
|
|
158
157
|
/**
|
|
@@ -161,18 +160,20 @@ shaka.util.Platform = class {
|
|
|
161
160
|
* @return {boolean}
|
|
162
161
|
*/
|
|
163
162
|
static isWebOS4() {
|
|
164
|
-
// See:
|
|
165
|
-
return
|
|
163
|
+
// See: https://webostv.developer.lge.com/develop/specifications/web-api-and-web-engine#useragent-string
|
|
164
|
+
return shaka.util.Platform.isWebOS() &&
|
|
165
|
+
shaka.util.Platform.chromeVersion() === 53;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
/**
|
|
169
|
-
* Check if the current platform is a WebOS
|
|
169
|
+
* Check if the current platform is a WebOS 5.
|
|
170
170
|
*
|
|
171
171
|
* @return {boolean}
|
|
172
172
|
*/
|
|
173
173
|
static isWebOS5() {
|
|
174
|
-
// See:
|
|
175
|
-
return
|
|
174
|
+
// See: https://webostv.developer.lge.com/develop/specifications/web-api-and-web-engine#useragent-string
|
|
175
|
+
return shaka.util.Platform.isWebOS() &&
|
|
176
|
+
shaka.util.Platform.chromeVersion() === 68;
|
|
176
177
|
}
|
|
177
178
|
|
|
178
179
|
/**
|
|
@@ -184,6 +185,17 @@ shaka.util.Platform = class {
|
|
|
184
185
|
return shaka.util.Platform.userAgentContains_('CrKey');
|
|
185
186
|
}
|
|
186
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Check if the current platform is a Google Chromecast with Android
|
|
190
|
+
* (i.e. Chromecast with GoogleTV).
|
|
191
|
+
*
|
|
192
|
+
* @return {boolean}
|
|
193
|
+
*/
|
|
194
|
+
static isAndroidCastDevice() {
|
|
195
|
+
const Platform = shaka.util.Platform;
|
|
196
|
+
return Platform.isChromecast() && Platform.isAndroid();
|
|
197
|
+
}
|
|
198
|
+
|
|
187
199
|
/**
|
|
188
200
|
* Returns a major version number for Chrome, or Chromium-based browsers.
|
|
189
201
|
*
|
|
@@ -414,6 +426,9 @@ shaka.util.Platform = class {
|
|
|
414
426
|
return navigator.userAgentData.platform.toLowerCase() == 'macos';
|
|
415
427
|
}
|
|
416
428
|
// Fall back to the old API, with less strict matching.
|
|
429
|
+
if (!navigator.platform) {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
417
432
|
return navigator.platform.toLowerCase().includes('mac');
|
|
418
433
|
}
|
|
419
434
|
|
|
@@ -428,6 +443,9 @@ shaka.util.Platform = class {
|
|
|
428
443
|
return navigator.userAgentData.platform.toLowerCase() == 'windows';
|
|
429
444
|
}
|
|
430
445
|
// Fall back to the old API, with less strict matching.
|
|
446
|
+
if (!navigator.platform) {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
431
449
|
return navigator.platform.toLowerCase().includes('windows');
|
|
432
450
|
}
|
|
433
451
|
|
|
@@ -535,6 +553,10 @@ shaka.util.Platform = class {
|
|
|
535
553
|
Platform.isWebOS4() || Platform.isWebOS5()) {
|
|
536
554
|
return false;
|
|
537
555
|
}
|
|
556
|
+
// Older chromecasts without GoogleTV seem to not support SMOOTH properly.
|
|
557
|
+
if (Platform.isChromecast() && !Platform.isAndroidCastDevice()) {
|
|
558
|
+
return false;
|
|
559
|
+
}
|
|
538
560
|
// See: https://chromium-review.googlesource.com/c/chromium/src/+/4577759
|
|
539
561
|
if (Platform.isWindows() && Platform.isEdge()) {
|
|
540
562
|
return false;
|