@gcorevideo/player 2.28.27 → 2.28.29
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/dist/core.js +173 -123
- package/dist/index.css +336 -336
- package/dist/index.embed.js +256 -199
- package/dist/index.js +229 -177
- package/lib/playback/BasePlayback.d.ts.map +1 -1
- package/lib/playback/dash-playback/DashPlayback.d.ts +25 -6
- package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
- package/lib/playback/dash-playback/DashPlayback.js +89 -46
- package/lib/playback/hls-playback/HlsPlayback.d.ts +32 -6
- package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
- package/lib/playback/hls-playback/HlsPlayback.js +78 -57
- package/lib/playback.types.d.ts +6 -0
- package/lib/playback.types.d.ts.map +1 -1
- package/lib/plugins/subtitles/ClosedCaptions.d.ts +5 -0
- package/lib/plugins/subtitles/ClosedCaptions.d.ts.map +1 -1
- package/lib/plugins/subtitles/ClosedCaptions.js +56 -54
- package/lib/testUtils.d.ts +2 -0
- package/lib/testUtils.d.ts.map +1 -1
- package/lib/testUtils.js +2 -0
- package/package.json +2 -2
- package/src/playback/BasePlayback.ts +2 -1
- package/src/playback/dash-playback/DashPlayback.ts +115 -55
- package/src/playback/hls-playback/HlsPlayback.ts +96 -87
- package/src/playback.types.ts +7 -0
- package/src/plugins/multi-camera/MultiCamera.ts +5 -5
- package/src/plugins/subtitles/ClosedCaptions.ts +64 -57
- package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +195 -196
- package/src/plugins/subtitles/__tests__/__snapshots__/ClosedCaptions.test.ts.snap +13 -1
- package/src/testUtils.ts +2 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -6,13 +6,22 @@
|
|
|
6
6
|
import { Events, Log, Playback, PlayerError, Utils, $ } from '@clappr/core'
|
|
7
7
|
import { trace } from '@gcorevideo/utils'
|
|
8
8
|
import assert from 'assert'
|
|
9
|
-
import
|
|
9
|
+
import {
|
|
10
10
|
ErrorEvent as DashErrorEvent,
|
|
11
|
+
MediaPlayer,
|
|
12
|
+
MediaPlayerClass,
|
|
11
13
|
MediaPlayerErrorEvent,
|
|
12
14
|
PlaybackErrorEvent as DashPlaybackErrorEvent,
|
|
13
|
-
|
|
15
|
+
MediaInfo as DashMediaInfo,
|
|
14
16
|
MetricEvent as DashMetricEvent,
|
|
15
17
|
IManifestInfo,
|
|
18
|
+
MediaInfo,
|
|
19
|
+
QualityChangeRenderedEvent,
|
|
20
|
+
TrackChangeRenderedEvent,
|
|
21
|
+
PlaybackRateChangedEvent,
|
|
22
|
+
Representation,
|
|
23
|
+
CueEnterEvent,
|
|
24
|
+
CueExitEvent,
|
|
16
25
|
} from 'dashjs'
|
|
17
26
|
|
|
18
27
|
import {
|
|
@@ -21,6 +30,7 @@ import {
|
|
|
21
30
|
QualityLevel,
|
|
22
31
|
TimePosition,
|
|
23
32
|
TimeValue,
|
|
33
|
+
VTTCueInfo,
|
|
24
34
|
} from '../../playback.types.js'
|
|
25
35
|
import { isDashSource } from '../../utils/mediaSources.js'
|
|
26
36
|
import { BasePlayback } from '../BasePlayback.js'
|
|
@@ -47,9 +57,9 @@ type LocalTimeCorrelation = {
|
|
|
47
57
|
const T = 'playback.dash'
|
|
48
58
|
|
|
49
59
|
export default class DashPlayback extends BasePlayback {
|
|
50
|
-
_levels: QualityLevel[]
|
|
60
|
+
_levels: QualityLevel[] = []
|
|
51
61
|
|
|
52
|
-
_currentLevel: number
|
|
62
|
+
_currentLevel: number = AUTO
|
|
53
63
|
|
|
54
64
|
// true when the actual duration is longer than hlsjs's live sync point
|
|
55
65
|
// when this is false playableRegionDuration will be the actual duration
|
|
@@ -78,7 +88,7 @@ export default class DashPlayback extends BasePlayback {
|
|
|
78
88
|
|
|
79
89
|
_programDateTime: TimeValue = 0
|
|
80
90
|
|
|
81
|
-
_dash:
|
|
91
|
+
_dash: MediaPlayerClass | null = null
|
|
82
92
|
|
|
83
93
|
_extrapolatedWindowDuration: number = 0
|
|
84
94
|
|
|
@@ -100,19 +110,19 @@ export default class DashPlayback extends BasePlayback {
|
|
|
100
110
|
|
|
101
111
|
_timeUpdateTimer: ReturnType<typeof setInterval> | null = null
|
|
102
112
|
|
|
113
|
+
oncueenter: ((e: VTTCueInfo) => void) | null = null
|
|
114
|
+
|
|
115
|
+
oncueexit: ((e: { id: string }) => void) | null = null
|
|
116
|
+
|
|
103
117
|
get name() {
|
|
104
118
|
return 'dash'
|
|
105
119
|
}
|
|
106
120
|
|
|
107
121
|
get levels(): QualityLevel[] {
|
|
108
|
-
return this._levels
|
|
122
|
+
return this._levels
|
|
109
123
|
}
|
|
110
124
|
|
|
111
125
|
get currentLevel(): number {
|
|
112
|
-
if (this._currentLevel === null) {
|
|
113
|
-
return AUTO
|
|
114
|
-
}
|
|
115
|
-
// 0 is a valid level ID
|
|
116
126
|
return this._currentLevel
|
|
117
127
|
}
|
|
118
128
|
|
|
@@ -143,7 +153,7 @@ export default class DashPlayback extends BasePlayback {
|
|
|
143
153
|
|
|
144
154
|
dash.updateSettings(settings)
|
|
145
155
|
if (id !== -1) {
|
|
146
|
-
this._dash.
|
|
156
|
+
this._dash.setRepresentationForTypeByIndex('video', id)
|
|
147
157
|
}
|
|
148
158
|
if (this._playbackType === Playback.VOD) {
|
|
149
159
|
const curr_time = this._dash.time()
|
|
@@ -159,6 +169,7 @@ export default class DashPlayback extends BasePlayback {
|
|
|
159
169
|
}
|
|
160
170
|
|
|
161
171
|
get _startTime() {
|
|
172
|
+
// TODO review
|
|
162
173
|
if (
|
|
163
174
|
this._playbackType === Playback.LIVE &&
|
|
164
175
|
this._playlistType !== 'EVENT'
|
|
@@ -226,7 +237,7 @@ export default class DashPlayback extends BasePlayback {
|
|
|
226
237
|
}
|
|
227
238
|
|
|
228
239
|
_setup() {
|
|
229
|
-
const dash =
|
|
240
|
+
const dash = MediaPlayer().create()
|
|
230
241
|
this._dash = dash
|
|
231
242
|
this._dash.initialize()
|
|
232
243
|
|
|
@@ -237,6 +248,7 @@ export default class DashPlayback extends BasePlayback {
|
|
|
237
248
|
streaming: {
|
|
238
249
|
text: {
|
|
239
250
|
defaultEnabled: false,
|
|
251
|
+
dispatchForManualRendering: true,
|
|
240
252
|
},
|
|
241
253
|
},
|
|
242
254
|
},
|
|
@@ -245,29 +257,29 @@ export default class DashPlayback extends BasePlayback {
|
|
|
245
257
|
this._dash.updateSettings(settings)
|
|
246
258
|
}
|
|
247
259
|
|
|
248
|
-
this._dash.attachView(this.el)
|
|
260
|
+
this._dash.attachView(this.el as HTMLMediaElement)
|
|
249
261
|
|
|
250
262
|
this._dash.setAutoPlay(false)
|
|
251
263
|
this._dash.attachSource(this.options.src)
|
|
252
264
|
|
|
253
|
-
this._dash.on(
|
|
265
|
+
this._dash.on(MediaPlayer.events.ERROR, this._onDASHJSSError)
|
|
254
266
|
this._dash.on(
|
|
255
|
-
|
|
267
|
+
MediaPlayer.events.PLAYBACK_ERROR,
|
|
256
268
|
this._onPlaybackError,
|
|
257
269
|
)
|
|
258
270
|
|
|
259
|
-
this._dash.on(
|
|
260
|
-
const bitrates = dash.
|
|
271
|
+
this._dash.on(MediaPlayer.events.STREAM_INITIALIZED, () => {
|
|
272
|
+
const bitrates = dash.getRepresentationsByType('video')
|
|
261
273
|
|
|
262
274
|
this._updatePlaybackType()
|
|
263
275
|
this._fillLevels(bitrates)
|
|
264
|
-
const currentLevel = dash.
|
|
265
|
-
if (currentLevel
|
|
266
|
-
this.trigger(Events.PLAYBACK_BITRATE, this.getLevel(currentLevel))
|
|
276
|
+
const currentLevel = dash.getCurrentRepresentationForType('video')
|
|
277
|
+
if (currentLevel) {
|
|
278
|
+
this.trigger(Events.PLAYBACK_BITRATE, this.getLevel(currentLevel.index))
|
|
267
279
|
}
|
|
268
280
|
|
|
269
|
-
dash.on(
|
|
270
|
-
const newLevel = this.getLevel(evt.
|
|
281
|
+
dash.on(MediaPlayer.events.QUALITY_CHANGE_REQUESTED, (evt) => {
|
|
282
|
+
const newLevel = this.getLevel(evt.newRepresentation.index)
|
|
271
283
|
this.onLevelSwitch(newLevel)
|
|
272
284
|
})
|
|
273
285
|
|
|
@@ -275,15 +287,15 @@ export default class DashPlayback extends BasePlayback {
|
|
|
275
287
|
})
|
|
276
288
|
|
|
277
289
|
this._dash.on(
|
|
278
|
-
|
|
279
|
-
(evt:
|
|
280
|
-
const currentLevel = this.getLevel(evt.
|
|
290
|
+
MediaPlayer.events.QUALITY_CHANGE_RENDERED,
|
|
291
|
+
(evt: QualityChangeRenderedEvent) => {
|
|
292
|
+
const currentLevel = this.getLevel(evt.newRepresentation.index)
|
|
281
293
|
this.onLevelSwitchEnd(currentLevel)
|
|
282
294
|
},
|
|
283
295
|
)
|
|
284
296
|
|
|
285
297
|
this._dash.on(
|
|
286
|
-
|
|
298
|
+
MediaPlayer.events.METRIC_ADDED,
|
|
287
299
|
(e: DashMetricEvent) => {
|
|
288
300
|
// Listen for the first manifest request in order to update player UI
|
|
289
301
|
if ((e.metric as string) === 'DVRInfo') {
|
|
@@ -302,20 +314,34 @@ export default class DashPlayback extends BasePlayback {
|
|
|
302
314
|
)
|
|
303
315
|
|
|
304
316
|
this._dash.on(
|
|
305
|
-
|
|
306
|
-
(e:
|
|
317
|
+
MediaPlayer.events.PLAYBACK_RATE_CHANGED,
|
|
318
|
+
(e: PlaybackRateChangedEvent) => {
|
|
307
319
|
this.trigger(PlaybackEvents.PLAYBACK_RATE_CHANGED, e.playbackRate)
|
|
308
320
|
},
|
|
309
321
|
)
|
|
310
322
|
|
|
311
|
-
this._dash.on(
|
|
312
|
-
if ((e as
|
|
323
|
+
this._dash.on(MediaPlayer.events.TRACK_CHANGE_RENDERED, (e: any) => {
|
|
324
|
+
if ((e as TrackChangeRenderedEvent).mediaType === 'audio') {
|
|
313
325
|
this.trigger(
|
|
314
326
|
Events.PLAYBACK_AUDIO_CHANGED,
|
|
315
327
|
toClapprTrack(e.newMediaInfo),
|
|
316
328
|
)
|
|
317
329
|
}
|
|
318
330
|
})
|
|
331
|
+
|
|
332
|
+
this._dash.on(MediaPlayer.events.CUE_ENTER, (e: CueEnterEvent) => {
|
|
333
|
+
this.oncueenter?.({
|
|
334
|
+
end: e.end,
|
|
335
|
+
id: e.id,
|
|
336
|
+
start: e.start,
|
|
337
|
+
text: e.text,
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
this._dash.on(MediaPlayer.events.CUE_EXIT, (e: CueExitEvent) => {
|
|
341
|
+
this.oncueexit?.({
|
|
342
|
+
id: e.id,
|
|
343
|
+
})
|
|
344
|
+
})
|
|
319
345
|
}
|
|
320
346
|
|
|
321
347
|
render() {
|
|
@@ -431,8 +457,7 @@ export default class DashPlayback extends BasePlayback {
|
|
|
431
457
|
}
|
|
432
458
|
|
|
433
459
|
private _onPlaybackError = (event: DashPlaybackErrorEvent) => {
|
|
434
|
-
|
|
435
|
-
trace(`${T} _onPlaybackError`, { event })
|
|
460
|
+
trace(`${T} _onPlaybackError`, { type: event.type, code: event.error.code, message: event.error.message })
|
|
436
461
|
}
|
|
437
462
|
|
|
438
463
|
private _onDASHJSSError = (event: DashErrorEvent) => {
|
|
@@ -442,17 +467,17 @@ export default class DashPlayback extends BasePlayback {
|
|
|
442
467
|
const e = (event as MediaPlayerErrorEvent).error
|
|
443
468
|
switch (e.code) {
|
|
444
469
|
// TODO test handling of these errors
|
|
445
|
-
case
|
|
446
|
-
case
|
|
447
|
-
case
|
|
448
|
-
case
|
|
449
|
-
case
|
|
470
|
+
case MediaPlayer.errors.MANIFEST_LOADER_PARSING_FAILURE_ERROR_CODE:
|
|
471
|
+
case MediaPlayer.errors.MANIFEST_LOADER_LOADING_FAILURE_ERROR_CODE:
|
|
472
|
+
case MediaPlayer.errors.DOWNLOAD_ERROR_ID_MANIFEST_CODE:
|
|
473
|
+
case MediaPlayer.errors.DOWNLOAD_ERROR_ID_CONTENT_CODE:
|
|
474
|
+
case MediaPlayer.errors.DOWNLOAD_ERROR_ID_INITIALIZATION_CODE:
|
|
450
475
|
// TODO these probably indicate a broken manifest and should be treated by removing the source
|
|
451
|
-
case
|
|
452
|
-
case
|
|
453
|
-
case
|
|
454
|
-
case
|
|
455
|
-
case
|
|
476
|
+
case MediaPlayer.errors.MANIFEST_ERROR_ID_NOSTREAMS_CODE:
|
|
477
|
+
case MediaPlayer.errors.MANIFEST_ERROR_ID_PARSE_CODE:
|
|
478
|
+
case MediaPlayer.errors.MANIFEST_ERROR_ID_MULTIPLEXED_CODE:
|
|
479
|
+
case MediaPlayer.errors.MEDIASOURCE_TYPE_UNSUPPORTED_CODE:
|
|
480
|
+
case MediaPlayer.errors.SEGMENT_BASE_LOADER_ERROR_CODE:
|
|
456
481
|
this.triggerError({
|
|
457
482
|
code: PlaybackErrorCode.MediaSourceUnavailable,
|
|
458
483
|
message: e.message,
|
|
@@ -525,7 +550,7 @@ export default class DashPlayback extends BasePlayback {
|
|
|
525
550
|
return false
|
|
526
551
|
}
|
|
527
552
|
return (
|
|
528
|
-
this._dash?.
|
|
553
|
+
this._dash?.getDvrWindow()?.size >= this._minDvrSize &&
|
|
529
554
|
this.getPlaybackType() === Playback.LIVE
|
|
530
555
|
)
|
|
531
556
|
}
|
|
@@ -572,16 +597,23 @@ export default class DashPlayback extends BasePlayback {
|
|
|
572
597
|
|
|
573
598
|
private destroyInstance() {
|
|
574
599
|
if (this._dash) {
|
|
575
|
-
this._dash.off(
|
|
600
|
+
this._dash.off(MediaPlayer.events.ERROR, this._onDASHJSSError)
|
|
576
601
|
this._dash.off(
|
|
577
|
-
|
|
602
|
+
MediaPlayer.events.PLAYBACK_ERROR,
|
|
578
603
|
this._onPlaybackError,
|
|
579
604
|
)
|
|
580
605
|
this._dash.off(
|
|
581
|
-
|
|
606
|
+
MediaPlayer.events.MANIFEST_LOADED,
|
|
582
607
|
this.getDuration,
|
|
583
608
|
)
|
|
609
|
+
const tracks = this._dash.getTracksFor('text')
|
|
610
|
+
tracks.forEach(track => {
|
|
611
|
+
if (track.id) {
|
|
612
|
+
this._dash!.removeExternalSubtitleById(track.id)
|
|
613
|
+
}
|
|
614
|
+
})
|
|
584
615
|
this._dash.reset()
|
|
616
|
+
this._dash.destroy()
|
|
585
617
|
this._dash = null
|
|
586
618
|
}
|
|
587
619
|
}
|
|
@@ -601,12 +633,11 @@ export default class DashPlayback extends BasePlayback {
|
|
|
601
633
|
}
|
|
602
634
|
}
|
|
603
635
|
|
|
604
|
-
_fillLevels(levels:
|
|
605
|
-
// TOOD check that levels[i].qualityIndex === i
|
|
636
|
+
private _fillLevels(levels: Representation[]) {
|
|
606
637
|
this._levels = levels.map((level) => {
|
|
607
638
|
return {
|
|
608
|
-
level: level.
|
|
609
|
-
bitrate: level.
|
|
639
|
+
level: level.index,
|
|
640
|
+
bitrate: level.bitrateInKbit * 1000,
|
|
610
641
|
width: level.width,
|
|
611
642
|
height: level.height,
|
|
612
643
|
}
|
|
@@ -635,10 +666,10 @@ export default class DashPlayback extends BasePlayback {
|
|
|
635
666
|
return this._playbackType === Playback.VOD || this.dvrEnabled
|
|
636
667
|
}
|
|
637
668
|
|
|
638
|
-
private getLevel(quality: number) {
|
|
639
|
-
const
|
|
640
|
-
assert.ok(
|
|
641
|
-
return
|
|
669
|
+
private getLevel(quality: number): QualityLevel {
|
|
670
|
+
const level = this.levels.find((level) => level.level === quality)
|
|
671
|
+
assert.ok(level, 'Invalid quality level')
|
|
672
|
+
return level
|
|
642
673
|
}
|
|
643
674
|
|
|
644
675
|
setPlaybackRate(rate: number) {
|
|
@@ -692,6 +723,35 @@ export default class DashPlayback extends BasePlayback {
|
|
|
692
723
|
super._handleTextTrackChange()
|
|
693
724
|
this._dash?.setTextTrack(this.closedCaptionsTrackId)
|
|
694
725
|
}
|
|
726
|
+
|
|
727
|
+
setTextTrack(id: number) {
|
|
728
|
+
this._dash?.setTextTrack(id)
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* @override
|
|
733
|
+
*/
|
|
734
|
+
get closedCaptionsTracks() {
|
|
735
|
+
const tt = this.getTextTracks()
|
|
736
|
+
return tt;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
private getTextTracks() {
|
|
740
|
+
return this._dash?.getTracksFor('text').map((t: DashMediaInfo, index: number) => ({
|
|
741
|
+
id: index,
|
|
742
|
+
name: getTextTrackLabel(t) || "",
|
|
743
|
+
track: {
|
|
744
|
+
id: index,
|
|
745
|
+
label: getTextTrackLabel(t) || "",
|
|
746
|
+
language: t.lang,
|
|
747
|
+
mode: "hidden",
|
|
748
|
+
},
|
|
749
|
+
})) || []
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
function getTextTrackLabel(t: DashMediaInfo) {
|
|
754
|
+
return t.labels.find((l) => !l.lang || l.lang === t.lang)?.text
|
|
695
755
|
}
|
|
696
756
|
|
|
697
757
|
DashPlayback.canPlay = function (resource, mimeType) {
|
|
@@ -707,7 +767,7 @@ DashPlayback.canPlay = function (resource, mimeType) {
|
|
|
707
767
|
return typeof ctor === 'function'
|
|
708
768
|
}
|
|
709
769
|
|
|
710
|
-
function toClapprTrack(t:
|
|
770
|
+
function toClapprTrack(t: MediaInfo): AudioTrack {
|
|
711
771
|
return {
|
|
712
772
|
id: t.id,
|
|
713
773
|
kind: t.roles && t.roles?.length > 0 ? t.roles[0] : 'main', // TODO
|