shaka-player 4.4.0 → 4.4.2
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/deflake.yaml +8 -5
- package/.github/workflows/release-please.yaml +64 -16
- package/.github/workflows/report-incremental-coverage.yaml +2 -2
- package/.github/workflows/shaka-bot-commands/command-help.sh +27 -0
- package/.github/workflows/shaka-bot-commands/command-test.sh +56 -0
- package/.github/workflows/shaka-bot-commands/lib.sh +83 -0
- package/.github/workflows/shaka-bot-commands/main.sh +51 -0
- package/.github/workflows/talk-to-shaka-bot.yaml +29 -0
- package/.github/workflows/update-issues.yaml +2 -1
- package/.github/workflows/validate-pr-title.yaml +3 -11
- package/CHANGELOG.md +33 -0
- package/CONTRIBUTORS +1 -0
- package/demo/custom.js +5 -5
- package/dist/demo.compiled.debug.js +3 -3
- package/dist/demo.compiled.debug.map +1 -1
- package/dist/demo.compiled.js +3 -3
- package/dist/demo.compiled.map +1 -1
- package/dist/deps.js +2 -2
- package/dist/locales.js +32 -32
- package/dist/shaka-player.compiled.d.ts +18 -2
- package/dist/shaka-player.compiled.debug.d.ts +18 -2
- package/dist/shaka-player.compiled.debug.externs.js +38 -1
- package/dist/shaka-player.compiled.debug.js +289 -280
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +38 -1
- package/dist/shaka-player.compiled.js +944 -940
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +18 -2
- package/dist/shaka-player.ui.debug.d.ts +18 -2
- package/dist/shaka-player.ui.debug.externs.js +38 -1
- package/dist/shaka-player.ui.debug.js +352 -343
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +38 -1
- package/dist/shaka-player.ui.js +1037 -1034
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +8 -0
- package/docs/tutorials/faq.md +1 -1
- package/externs/shaka/namespace.js +2 -2
- package/externs/shaka/text.js +7 -0
- package/externs/shaka/transmuxer.js +2 -1
- package/lib/dash/segment_template.js +3 -0
- package/lib/hls/hls_parser.js +9 -4
- package/lib/media/media_source_engine.js +23 -3
- package/lib/mss/mss_parser.js +1 -0
- package/lib/player.js +8 -57
- package/lib/text/lrc_text_parser.js +8 -0
- package/lib/text/mp4_ttml_parser.js +8 -0
- package/lib/text/mp4_vtt_parser.js +8 -0
- package/lib/text/sbv_text_parser.js +8 -0
- package/lib/text/simple_text_displayer.js +0 -1
- package/lib/text/srt_text_parser.js +8 -0
- package/lib/text/ssa_text_parser.js +8 -0
- package/lib/text/text_engine.js +8 -1
- package/lib/text/ttml_text_parser.js +10 -1
- package/lib/text/ui_text_displayer.js +49 -3
- package/lib/text/vtt_text_parser.js +21 -5
- package/lib/transmuxer/aac_transmuxer.js +1 -1
- package/lib/transmuxer/ac3_transmuxer.js +1 -0
- package/lib/transmuxer/ec3_transmuxer.js +1 -0
- package/lib/transmuxer/mp3_transmuxer.js +1 -0
- package/lib/transmuxer/ts_transmuxer.js +84 -100
- package/lib/util/error.js +16 -10
- package/lib/util/mp4_generator.js +27 -20
- package/lib/util/player_configuration.js +2 -2
- package/lib/util/stream_utils.js +13 -11
- package/lib/util/ts_parser.js +137 -117
- package/package.json +2 -2
- package/test/hls/hls_parser_unit.js +12 -13
- package/test/media/media_source_engine_integration.js +2 -2
- package/test/mss/mss_player_integration.js +1 -1
- package/test/text/text_engine_unit.js +4 -2
- package/test/text/ttml_text_parser_unit.js +3 -4
- package/test/text/vtt_text_parser_unit.js +17 -2
- package/test/text/web_vtt_layout_integration.js +0 -1
- package/ui/language_utils.js +0 -15
|
@@ -232,3 +232,11 @@ player.getNetworkingEngine()
|
|
|
232
232
|
player.configure('drm.initDataTransform',
|
|
233
233
|
FairPlayUtils.expressplayInitDataTransform);
|
|
234
234
|
```
|
|
235
|
+
|
|
236
|
+
#### Nagra (legacy Apple Media Keys)
|
|
237
|
+
|
|
238
|
+
For integration with Nagra the following can be used:
|
|
239
|
+
|
|
240
|
+
```js
|
|
241
|
+
shaka.polyfill.PatchedMediaKeysApple.install();
|
|
242
|
+
```
|
package/docs/tutorials/faq.md
CHANGED
|
@@ -153,7 +153,7 @@ In other environments, for example Electron, it is appropriate.
|
|
|
153
153
|
In those cases, before Shaka Player loads a manifest, you can register the
|
|
154
154
|
existing http plugin for `file://` requests:
|
|
155
155
|
```js
|
|
156
|
-
shaka.net.NetworkingEngine.registerScheme('file', shaka.net.HttpXHRPlugin);
|
|
156
|
+
shaka.net.NetworkingEngine.registerScheme('file', shaka.net.HttpXHRPlugin.parse);
|
|
157
157
|
```
|
|
158
158
|
|
|
159
159
|
<hr>
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @externs
|
|
9
|
-
* @suppress {duplicate} To prevent compiler errors with the
|
|
10
|
-
* being declared both here and by goog.provide in the library.
|
|
9
|
+
* @suppress {constantProperty, duplicate} To prevent compiler errors with the
|
|
10
|
+
* namespace being declared both here and by goog.provide in the library.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
/** @namespace */
|
package/externs/shaka/text.js
CHANGED
|
@@ -46,9 +46,10 @@ shaka.extern.Transmuxer = class {
|
|
|
46
46
|
* @param {?shaka.media.SegmentReference} reference The segment reference, or
|
|
47
47
|
* null for init segments
|
|
48
48
|
* @param {number} duration
|
|
49
|
+
* @param {string} contentType
|
|
49
50
|
* @return {!Promise.<!Uint8Array>}
|
|
50
51
|
*/
|
|
51
|
-
transmux(data, stream, reference, duration) {}
|
|
52
|
+
transmux(data, stream, reference, duration, contentType) {}
|
|
52
53
|
};
|
|
53
54
|
|
|
54
55
|
|
|
@@ -868,6 +868,9 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
|
|
|
868
868
|
.unscaledPresentationTimeOffset + range.unscaledStart;
|
|
869
869
|
|
|
870
870
|
const createUrisCb = () => {
|
|
871
|
+
if (!this.templateInfo_) {
|
|
872
|
+
return [];
|
|
873
|
+
}
|
|
871
874
|
return shaka.dash.TimelineSegmentIndex
|
|
872
875
|
.createUris_(
|
|
873
876
|
this.templateInfo_.mediaTemplate,
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -900,6 +900,7 @@ shaka.hls.HlsParser = class {
|
|
|
900
900
|
const uint8ArrayData = shaka.util.BufferUtils.toUint8(response.data);
|
|
901
901
|
const tsParser = new shaka.util.TsParser().parse(uint8ArrayData);
|
|
902
902
|
const tsCodecs = tsParser.getCodecs();
|
|
903
|
+
const videoInfo = tsParser.getVideoInfo();
|
|
903
904
|
const codecs = [];
|
|
904
905
|
let hasAudio = false;
|
|
905
906
|
let hasVideo = false;
|
|
@@ -923,7 +924,11 @@ shaka.hls.HlsParser = class {
|
|
|
923
924
|
}
|
|
924
925
|
switch (tsCodecs.video) {
|
|
925
926
|
case 'avc':
|
|
926
|
-
|
|
927
|
+
if (videoInfo.codec) {
|
|
928
|
+
codecs.push(videoInfo.codec);
|
|
929
|
+
} else {
|
|
930
|
+
codecs.push('avc1.42E01E');
|
|
931
|
+
}
|
|
927
932
|
hasVideo = true;
|
|
928
933
|
break;
|
|
929
934
|
case 'hvc':
|
|
@@ -935,14 +940,13 @@ shaka.hls.HlsParser = class {
|
|
|
935
940
|
return null;
|
|
936
941
|
}
|
|
937
942
|
const onlyAudio = hasAudio && !hasVideo;
|
|
938
|
-
const videoResolution = tsParser.getVideoResolution();
|
|
939
943
|
return {
|
|
940
944
|
type: onlyAudio ? 'audio' : 'video',
|
|
941
945
|
mimeType: 'video/mp2t',
|
|
942
946
|
codecs: codecs.join(', '),
|
|
943
947
|
language: null,
|
|
944
|
-
height:
|
|
945
|
-
width:
|
|
948
|
+
height: videoInfo.height,
|
|
949
|
+
width: videoInfo.width,
|
|
946
950
|
channelCount: null,
|
|
947
951
|
sampleRate: null,
|
|
948
952
|
};
|
|
@@ -1466,6 +1470,7 @@ shaka.hls.HlsParser = class {
|
|
|
1466
1470
|
// and no video codec, so it should be audio.
|
|
1467
1471
|
type = ContentType.AUDIO;
|
|
1468
1472
|
shaka.log.debug('Guessing audio-only.');
|
|
1473
|
+
ignoreStream = res.audio.length > 0;
|
|
1469
1474
|
} else if (!streamInfos.length && audioCodecs && videoCodecs) {
|
|
1470
1475
|
// There are both audio and video codecs, so assume multiplexed content.
|
|
1471
1476
|
// Note that the default used when CODECS is missing assumes multiple
|
|
@@ -492,7 +492,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
492
492
|
this.textEngine_ = new shaka.text.TextEngine(this.textDisplayer_);
|
|
493
493
|
}
|
|
494
494
|
this.textEngine_.initParser(mimeType, sequenceMode,
|
|
495
|
-
external || this.segmentRelativeVttTiming_);
|
|
495
|
+
external || this.segmentRelativeVttTiming_, this.manifestType_);
|
|
496
496
|
}
|
|
497
497
|
|
|
498
498
|
/**
|
|
@@ -821,7 +821,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
821
821
|
|
|
822
822
|
if (this.transmuxers_[contentType]) {
|
|
823
823
|
data = await this.transmuxers_[contentType].transmux(
|
|
824
|
-
data, stream, reference, this.mediaSource_.duration);
|
|
824
|
+
data, stream, reference, this.mediaSource_.duration, contentType);
|
|
825
825
|
}
|
|
826
826
|
|
|
827
827
|
data = this.workAroundBrokenPlatforms_(
|
|
@@ -1562,16 +1562,34 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1562
1562
|
* @private
|
|
1563
1563
|
*/
|
|
1564
1564
|
async reset_(streamsByType) {
|
|
1565
|
+
const Functional = shaka.util.Functional;
|
|
1565
1566
|
this.reloadingMediaSource_ = true;
|
|
1566
1567
|
const currentTime = this.video_.currentTime;
|
|
1567
1568
|
try {
|
|
1568
1569
|
this.eventManager_.removeAll();
|
|
1569
|
-
this.sourceBuffers_ = {};
|
|
1570
1570
|
|
|
1571
1571
|
const cleanup = [];
|
|
1572
1572
|
for (const contentType in this.transmuxers_) {
|
|
1573
1573
|
cleanup.push(this.transmuxers_[contentType].destroy());
|
|
1574
1574
|
}
|
|
1575
|
+
for (const contentType in this.queues_) {
|
|
1576
|
+
// Make a local copy of the queue and the first item.
|
|
1577
|
+
const q = this.queues_[contentType];
|
|
1578
|
+
const inProgress = q[0];
|
|
1579
|
+
|
|
1580
|
+
// Drop everything else out of the original queue.
|
|
1581
|
+
this.queues_[contentType] = q.slice(0, 1);
|
|
1582
|
+
|
|
1583
|
+
// We will wait for this item to complete/fail.
|
|
1584
|
+
if (inProgress) {
|
|
1585
|
+
cleanup.push(inProgress.p.catch(Functional.noop));
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
// The rest will be rejected silently if possible.
|
|
1589
|
+
for (const item of q.slice(1)) {
|
|
1590
|
+
item.p.reject(shaka.util.Destroyer.destroyedError());
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1575
1593
|
for (const contentType in this.sourceBuffers_) {
|
|
1576
1594
|
const sourceBuffer = this.sourceBuffers_[contentType];
|
|
1577
1595
|
try {
|
|
@@ -1580,6 +1598,8 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1580
1598
|
}
|
|
1581
1599
|
await Promise.all(cleanup);
|
|
1582
1600
|
this.transmuxers_ = {};
|
|
1601
|
+
this.queues_ = {};
|
|
1602
|
+
this.sourceBuffers_ = {};
|
|
1583
1603
|
|
|
1584
1604
|
const previousDuration = this.mediaSource_.duration;
|
|
1585
1605
|
this.mediaSourceOpen_ = new shaka.util.PublicPromise();
|
package/lib/mss/mss_parser.js
CHANGED
package/lib/player.js
CHANGED
|
@@ -2663,16 +2663,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2663
2663
|
return;
|
|
2664
2664
|
}
|
|
2665
2665
|
|
|
2666
|
-
this.selectAudioLanguage(preferredAudioLanguage);
|
|
2667
|
-
|
|
2668
2666
|
const preferredVariantRole = this.config_.preferredVariantRole;
|
|
2669
|
-
|
|
2670
|
-
// If the user has not selected a role preference, the previous match is
|
|
2671
|
-
// selected.
|
|
2672
|
-
if (preferredVariantRole == '') {
|
|
2673
|
-
return;
|
|
2674
|
-
}
|
|
2675
|
-
|
|
2676
2667
|
this.selectAudioLanguage(preferredAudioLanguage, preferredVariantRole);
|
|
2677
2668
|
}
|
|
2678
2669
|
|
|
@@ -2683,7 +2674,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2683
2674
|
*/
|
|
2684
2675
|
setupPreferredTextOnSrc_() {
|
|
2685
2676
|
const preferredTextLanguage = this.config_.preferredTextLanguage;
|
|
2686
|
-
const preferForcedSubs = this.config_.preferForcedSubs;
|
|
2687
2677
|
|
|
2688
2678
|
// If the user has not selected a preference, the browser preference is
|
|
2689
2679
|
// left.
|
|
@@ -2691,16 +2681,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2691
2681
|
return;
|
|
2692
2682
|
}
|
|
2693
2683
|
|
|
2694
|
-
this.
|
|
2695
|
-
|
|
2684
|
+
const preferForcedSubs = this.config_.preferForcedSubs;
|
|
2696
2685
|
const preferredTextRole = this.config_.preferredTextRole;
|
|
2697
2686
|
|
|
2698
|
-
// If the user has not selected a role preference, the previous match is
|
|
2699
|
-
// selected.
|
|
2700
|
-
if (preferredTextRole == '') {
|
|
2701
|
-
return;
|
|
2702
|
-
}
|
|
2703
|
-
|
|
2704
2687
|
this.selectTextLanguage(preferredTextLanguage, preferredTextRole,
|
|
2705
2688
|
preferForcedSubs);
|
|
2706
2689
|
}
|
|
@@ -4199,8 +4182,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4199
4182
|
* @export
|
|
4200
4183
|
*/
|
|
4201
4184
|
selectAudioLanguage(language, role, channelsCount = 0, safeMargin = 0) {
|
|
4202
|
-
const LanguageUtils = shaka.util.LanguageUtils;
|
|
4203
|
-
|
|
4204
4185
|
if (this.manifest_ && this.playhead_) {
|
|
4205
4186
|
this.currentAdaptationSetCriteria_ =
|
|
4206
4187
|
new shaka.media.PreferenceBasedCriteria(language, role || '',
|
|
@@ -4238,33 +4219,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4238
4219
|
// If we haven't switched yet, just use ABR to find a new track.
|
|
4239
4220
|
this.chooseVariantAndSwitch_();
|
|
4240
4221
|
} else if (this.video_ && this.video_.audioTracks) {
|
|
4241
|
-
const
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
let languageAndRoleMatch = null;
|
|
4246
|
-
|
|
4247
|
-
for (const audioTrack of audioTracks) {
|
|
4248
|
-
const track = shaka.util.StreamUtils.html5AudioTrackToTrack(audioTrack);
|
|
4249
|
-
|
|
4250
|
-
if (LanguageUtils.normalize(track.language) == selectedLanguage) {
|
|
4251
|
-
languageMatch = audioTrack;
|
|
4252
|
-
|
|
4253
|
-
if (role) {
|
|
4254
|
-
if (track.roles.includes(role)) {
|
|
4255
|
-
languageAndRoleMatch = audioTrack;
|
|
4256
|
-
}
|
|
4257
|
-
} else { // no role
|
|
4258
|
-
if (track.roles.length == 0) {
|
|
4259
|
-
languageAndRoleMatch = audioTrack;
|
|
4260
|
-
}
|
|
4261
|
-
}
|
|
4262
|
-
}
|
|
4263
|
-
}
|
|
4264
|
-
if (languageAndRoleMatch) {
|
|
4265
|
-
this.switchHtml5Track_(languageAndRoleMatch);
|
|
4266
|
-
} else if (languageMatch) {
|
|
4267
|
-
this.switchHtml5Track_(languageMatch);
|
|
4222
|
+
const track = shaka.util.StreamUtils.filterStreamsByLanguageAndRole(
|
|
4223
|
+
this.getVariantTracks(), language, role || '', false)[0];
|
|
4224
|
+
if (track) {
|
|
4225
|
+
this.selectVariantTrack(track);
|
|
4268
4226
|
}
|
|
4269
4227
|
}
|
|
4270
4228
|
}
|
|
@@ -4280,8 +4238,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4280
4238
|
* @export
|
|
4281
4239
|
*/
|
|
4282
4240
|
selectTextLanguage(language, role, forced = false) {
|
|
4283
|
-
const LanguageUtils = shaka.util.LanguageUtils;
|
|
4284
|
-
|
|
4285
4241
|
if (this.manifest_ && this.playhead_) {
|
|
4286
4242
|
this.currentTextLanguage_ = language;
|
|
4287
4243
|
this.currentTextRole_ = role || '';
|
|
@@ -4302,13 +4258,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4302
4258
|
}
|
|
4303
4259
|
}
|
|
4304
4260
|
} else {
|
|
4305
|
-
const
|
|
4306
|
-
|
|
4307
|
-
const track = this.getTextTracks().find((t) => {
|
|
4308
|
-
return LanguageUtils.normalize(t.language) == selectedLanguage &&
|
|
4309
|
-
(!role || t.roles.includes(role)) && t.forced == forced;
|
|
4310
|
-
});
|
|
4311
|
-
|
|
4261
|
+
const track = shaka.util.StreamUtils.filterStreamsByLanguageAndRole(
|
|
4262
|
+
this.getTextTracks(), language, role || '', forced)[0];
|
|
4312
4263
|
if (track) {
|
|
4313
4264
|
this.selectTextTrack(track);
|
|
4314
4265
|
}
|
|
@@ -7224,7 +7175,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
7224
7175
|
* @define {string} A version number taken from git at compile time.
|
|
7225
7176
|
* @export
|
|
7226
7177
|
*/
|
|
7227
|
-
shaka.Player.version = 'v4.4.
|
|
7178
|
+
shaka.Player.version = 'v4.4.2-uncompiled';
|
|
7228
7179
|
|
|
7229
7180
|
// Initialize the deprecation system using the version string we just set
|
|
7230
7181
|
// on the player.
|
package/lib/text/text_engine.js
CHANGED
|
@@ -132,8 +132,9 @@ shaka.text.TextEngine = class {
|
|
|
132
132
|
* @param {string} mimeType
|
|
133
133
|
* @param {boolean} sequenceMode
|
|
134
134
|
* @param {boolean} segmentRelativeVttTiming
|
|
135
|
+
* @param {string} manifestType
|
|
135
136
|
*/
|
|
136
|
-
initParser(mimeType, sequenceMode, segmentRelativeVttTiming) {
|
|
137
|
+
initParser(mimeType, sequenceMode, segmentRelativeVttTiming, manifestType) {
|
|
137
138
|
// No parser for CEA, which is extracted from video and side-loaded
|
|
138
139
|
// into TextEngine and TextDisplayer.
|
|
139
140
|
if (mimeType == shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE ||
|
|
@@ -152,6 +153,12 @@ shaka.text.TextEngine = class {
|
|
|
152
153
|
shaka.log.alwaysWarn(
|
|
153
154
|
'Text parsers should have a "setSequenceMode" method!');
|
|
154
155
|
}
|
|
156
|
+
if (this.parser_.setManifestType) {
|
|
157
|
+
this.parser_.setManifestType(manifestType);
|
|
158
|
+
} else {
|
|
159
|
+
shaka.log.alwaysWarn(
|
|
160
|
+
'Text parsers should have a "setManifestType" method!');
|
|
161
|
+
}
|
|
155
162
|
this.segmentRelativeVttTiming_ = segmentRelativeVttTiming;
|
|
156
163
|
}
|
|
157
164
|
|
|
@@ -38,6 +38,14 @@ shaka.text.TtmlTextParser = class {
|
|
|
38
38
|
// Unused.
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* @override
|
|
43
|
+
* @export
|
|
44
|
+
*/
|
|
45
|
+
setManifestType(manifestType) {
|
|
46
|
+
// Unused.
|
|
47
|
+
}
|
|
48
|
+
|
|
41
49
|
/**
|
|
42
50
|
* @override
|
|
43
51
|
* @export
|
|
@@ -214,11 +222,12 @@ shaka.text.TtmlTextParser = class {
|
|
|
214
222
|
}
|
|
215
223
|
}
|
|
216
224
|
|
|
217
|
-
const parentIsContent = isContent;
|
|
218
225
|
if (cueNode.nodeName == 'p' || imageElement) {
|
|
219
226
|
isContent = true;
|
|
220
227
|
}
|
|
221
228
|
|
|
229
|
+
const parentIsContent = isContent;
|
|
230
|
+
|
|
222
231
|
const spaceStyle = cueElement.getAttribute('xml:space') ||
|
|
223
232
|
(whitespaceTrim ? 'default' : 'preserve');
|
|
224
233
|
|
|
@@ -481,6 +481,43 @@ shaka.text.UITextDisplayer = class {
|
|
|
481
481
|
this.currentCuesMap_.set(cue, {cueElement, wrapper, regionElement});
|
|
482
482
|
}
|
|
483
483
|
|
|
484
|
+
/**
|
|
485
|
+
* Compute cue position alignment
|
|
486
|
+
* See https://www.w3.org/TR/webvtt1/#webvtt-cue-position-alignment
|
|
487
|
+
*
|
|
488
|
+
* @param {!shaka.text.Cue} cue
|
|
489
|
+
* @private
|
|
490
|
+
*/
|
|
491
|
+
computeCuePositionAlignment_(cue) {
|
|
492
|
+
const Cue = shaka.text.Cue;
|
|
493
|
+
const {direction, positionAlign, textAlign} = cue;
|
|
494
|
+
|
|
495
|
+
if (positionAlign !== Cue.positionAlign.AUTO) {
|
|
496
|
+
// Position align is not AUTO: use it
|
|
497
|
+
return positionAlign;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// Position align is AUTO: use text align to compute its value
|
|
501
|
+
|
|
502
|
+
if (textAlign === Cue.textAlign.LEFT ||
|
|
503
|
+
(textAlign === Cue.textAlign.START &&
|
|
504
|
+
direction === Cue.direction.HORIZONTAL_LEFT_TO_RIGHT) ||
|
|
505
|
+
(textAlign === Cue.textAlign.END &&
|
|
506
|
+
direction === Cue.direction.HORIZONTAL_RIGHT_TO_LEFT)) {
|
|
507
|
+
return Cue.positionAlign.LEFT;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (textAlign === Cue.textAlign.RIGHT ||
|
|
511
|
+
(textAlign === Cue.textAlign.START &&
|
|
512
|
+
direction === Cue.direction.HORIZONTAL_RIGHT_TO_LEFT) ||
|
|
513
|
+
(textAlign === Cue.textAlign.END &&
|
|
514
|
+
direction === Cue.direction.HORIZONTAL_LEFT_TO_RIGHT)) {
|
|
515
|
+
return Cue.positionAlign.RIGHT;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return Cue.positionAlign.CENTER;
|
|
519
|
+
}
|
|
520
|
+
|
|
484
521
|
/**
|
|
485
522
|
* @param {!HTMLElement} cueElement
|
|
486
523
|
* @param {!shaka.text.Cue} cue
|
|
@@ -617,7 +654,15 @@ shaka.text.UITextDisplayer = class {
|
|
|
617
654
|
// TODO: Implement line alignment with line number.
|
|
618
655
|
// TODO: Implement lineAlignment of 'CENTER'.
|
|
619
656
|
if (cue.line != null) {
|
|
620
|
-
|
|
657
|
+
let lineInterpretation = cue.lineInterpretation;
|
|
658
|
+
// HACK: the current implementation of UITextDisplayer only handled
|
|
659
|
+
// PERCENTAGE, as cue.line=0 is zero, we can interpret it as percentage
|
|
660
|
+
// safely.
|
|
661
|
+
if (cue.line == 0 &&
|
|
662
|
+
lineInterpretation == Cue.lineInterpretation.LINE_NUMBER) {
|
|
663
|
+
lineInterpretation = Cue.lineInterpretation.PERCENTAGE;
|
|
664
|
+
}
|
|
665
|
+
if (lineInterpretation == Cue.lineInterpretation.PERCENTAGE) {
|
|
621
666
|
style.position = 'absolute';
|
|
622
667
|
if (cue.writingMode == Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) {
|
|
623
668
|
style.width = '100%';
|
|
@@ -658,9 +703,10 @@ shaka.text.UITextDisplayer = class {
|
|
|
658
703
|
|
|
659
704
|
// The positionAlign attribute is an alignment for the text container in
|
|
660
705
|
// the dimension of the writing direction.
|
|
661
|
-
|
|
706
|
+
const computedPositionAlign = this.computeCuePositionAlignment_(cue);
|
|
707
|
+
if (computedPositionAlign == Cue.positionAlign.LEFT) {
|
|
662
708
|
style.cssFloat = 'left';
|
|
663
|
-
} else if (
|
|
709
|
+
} else if (computedPositionAlign == Cue.positionAlign.RIGHT) {
|
|
664
710
|
style.cssFloat = 'right';
|
|
665
711
|
}
|
|
666
712
|
|
|
@@ -8,6 +8,7 @@ goog.provide('shaka.text.VttTextParser');
|
|
|
8
8
|
|
|
9
9
|
goog.require('goog.asserts');
|
|
10
10
|
goog.require('shaka.log');
|
|
11
|
+
goog.require('shaka.media.ManifestParser');
|
|
11
12
|
goog.require('shaka.text.Cue');
|
|
12
13
|
goog.require('shaka.text.CueRegion');
|
|
13
14
|
goog.require('shaka.text.TextEngine');
|
|
@@ -26,6 +27,9 @@ shaka.text.VttTextParser = class {
|
|
|
26
27
|
constructor() {
|
|
27
28
|
/** @private {boolean} */
|
|
28
29
|
this.sequenceMode_ = false;
|
|
30
|
+
|
|
31
|
+
/** @private {string} */
|
|
32
|
+
this.manifestType_ = shaka.media.ManifestParser.UNKNOWN;
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
/**
|
|
@@ -44,6 +48,14 @@ shaka.text.VttTextParser = class {
|
|
|
44
48
|
this.sequenceMode_ = sequenceMode;
|
|
45
49
|
}
|
|
46
50
|
|
|
51
|
+
/**
|
|
52
|
+
* @override
|
|
53
|
+
* @export
|
|
54
|
+
*/
|
|
55
|
+
setManifestType(manifestType) {
|
|
56
|
+
this.manifestType_ = manifestType;
|
|
57
|
+
}
|
|
58
|
+
|
|
47
59
|
/**
|
|
48
60
|
* @override
|
|
49
61
|
* @export
|
|
@@ -72,10 +84,9 @@ shaka.text.VttTextParser = class {
|
|
|
72
84
|
// HLS content, which should use X-TIMESTAMP-MAP and periodStart instead.
|
|
73
85
|
let offset = time.vttOffset;
|
|
74
86
|
|
|
75
|
-
// Only use 'X-TIMESTAMP-MAP' in sequence mode
|
|
76
|
-
//
|
|
87
|
+
// Only use 'X-TIMESTAMP-MAP' in sequence mode.
|
|
88
|
+
// Note that an offset based on the first video
|
|
77
89
|
// timestamp has already been extracted, and appears in periodStart.
|
|
78
|
-
// The relative offset from X-TIMESTAMP-MAP will be added to that for HLS.
|
|
79
90
|
if (blocks[0].includes('X-TIMESTAMP-MAP') && this.sequenceMode_) {
|
|
80
91
|
// https://bit.ly/2K92l7y
|
|
81
92
|
// The 'X-TIMESTAMP-MAP' header is used in HLS to align text with
|
|
@@ -113,6 +124,11 @@ shaka.text.VttTextParser = class {
|
|
|
113
124
|
|
|
114
125
|
offset = time.periodStart + mpegTime / mpegTimescale - cueTime;
|
|
115
126
|
}
|
|
127
|
+
} else if (blocks[0].includes('X-TIMESTAMP-MAP') &&
|
|
128
|
+
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;
|
|
116
132
|
}
|
|
117
133
|
|
|
118
134
|
// Parse VTT regions.
|
|
@@ -401,8 +417,6 @@ shaka.text.VttTextParser = class {
|
|
|
401
417
|
cue = new shaka.text.Cue(start, end, '');
|
|
402
418
|
}
|
|
403
419
|
|
|
404
|
-
VttTextParser.parseCueStyles(payload, cue, styles);
|
|
405
|
-
|
|
406
420
|
// Parse optional settings.
|
|
407
421
|
parser.skipWhitespace();
|
|
408
422
|
let word = parser.readWord();
|
|
@@ -416,6 +430,8 @@ shaka.text.VttTextParser = class {
|
|
|
416
430
|
word = parser.readWord();
|
|
417
431
|
}
|
|
418
432
|
|
|
433
|
+
VttTextParser.parseCueStyles(payload, cue, styles);
|
|
434
|
+
|
|
419
435
|
if (id != null) {
|
|
420
436
|
cue.id = id;
|
|
421
437
|
}
|
|
@@ -145,7 +145,6 @@ shaka.transmuxer.AacTransmuxer = class {
|
|
|
145
145
|
}
|
|
146
146
|
stream.audioSamplingRate = info.sampleRate;
|
|
147
147
|
stream.channelsCount = info.channelCount;
|
|
148
|
-
stream.codecs = info.codec;
|
|
149
148
|
|
|
150
149
|
/** @type {!Array.<shaka.util.Mp4Generator.Mp4Sample>} */
|
|
151
150
|
const samples = [];
|
|
@@ -189,6 +188,7 @@ shaka.transmuxer.AacTransmuxer = class {
|
|
|
189
188
|
const streamInfo = {
|
|
190
189
|
id: stream.id,
|
|
191
190
|
type: shaka.util.ManifestParserUtils.ContentType.AUDIO,
|
|
191
|
+
codecs: info.codec,
|
|
192
192
|
encrypted: stream.encrypted && stream.drmInfos.length > 0,
|
|
193
193
|
timescale: sampleRate,
|
|
194
194
|
duration: duration,
|
|
@@ -177,6 +177,7 @@ shaka.transmuxer.Ac3Transmuxer = class {
|
|
|
177
177
|
const streamInfo = {
|
|
178
178
|
id: stream.id,
|
|
179
179
|
type: shaka.util.ManifestParserUtils.ContentType.AUDIO,
|
|
180
|
+
codecs: 'ac-3',
|
|
180
181
|
encrypted: stream.encrypted && stream.drmInfos.length > 0,
|
|
181
182
|
timescale: sampleRate,
|
|
182
183
|
duration: duration,
|
|
@@ -177,6 +177,7 @@ shaka.transmuxer.Ec3Transmuxer = class {
|
|
|
177
177
|
const streamInfo = {
|
|
178
178
|
id: stream.id,
|
|
179
179
|
type: shaka.util.ManifestParserUtils.ContentType.AUDIO,
|
|
180
|
+
codecs: 'ec-3',
|
|
180
181
|
encrypted: stream.encrypted && stream.drmInfos.length > 0,
|
|
181
182
|
timescale: sampleRate,
|
|
182
183
|
duration: duration,
|
|
@@ -169,6 +169,7 @@ shaka.transmuxer.Mp3Transmuxer = class {
|
|
|
169
169
|
const streamInfo = {
|
|
170
170
|
id: stream.id,
|
|
171
171
|
type: shaka.util.ManifestParserUtils.ContentType.AUDIO,
|
|
172
|
+
codecs: 'mp3',
|
|
172
173
|
encrypted: stream.encrypted && stream.drmInfos.length > 0,
|
|
173
174
|
timescale: sampleRate,
|
|
174
175
|
duration: duration,
|