@gcorevideo/player 2.28.36 → 2.29.0

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 (42) hide show
  1. package/assets/media-control/media-control.scss +8 -6
  2. package/assets/multi-camera/multicamera.ejs +27 -23
  3. package/assets/multi-camera/style.scss +7 -34
  4. package/assets/style/main.scss +2 -2
  5. package/dist/core.js +8 -6
  6. package/dist/index.css +427 -449
  7. package/dist/index.embed.js +8 -45
  8. package/dist/index.js +77 -181
  9. package/docs/api/player.md +22 -9
  10. package/docs/api/player.mediacontrol.setkeepvisible.md +56 -0
  11. package/docs/api/player.multicamera.md +0 -28
  12. package/docs/api/player.multiccamerasourceinfo.md +27 -0
  13. package/docs/api/{player.multicamera.unbindevents.md → player.multisourcesmode.md} +4 -7
  14. package/docs/api/player.sourcecontroller.md +0 -37
  15. package/lib/playback/dash-playback/DashPlayback.d.ts +2 -1
  16. package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
  17. package/lib/playback/hls-playback/HlsPlayback.d.ts +2 -1
  18. package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
  19. package/lib/playback/types.d.ts +9 -0
  20. package/lib/playback/types.d.ts.map +1 -1
  21. package/lib/playback.types.d.ts +0 -6
  22. package/lib/playback.types.d.ts.map +1 -1
  23. package/lib/plugins/multi-camera/MultiCamera.d.ts +21 -4
  24. package/lib/plugins/multi-camera/MultiCamera.d.ts.map +1 -1
  25. package/lib/plugins/multi-camera/MultiCamera.js +70 -134
  26. package/lib/plugins/source-controller/SourceController.d.ts +0 -39
  27. package/lib/plugins/source-controller/SourceController.d.ts.map +1 -1
  28. package/lib/plugins/source-controller/SourceController.js +0 -39
  29. package/lib/utils/mediaSources.d.ts +4 -0
  30. package/lib/utils/mediaSources.d.ts.map +1 -1
  31. package/lib/utils/mediaSources.js +8 -6
  32. package/package.json +1 -1
  33. package/src/playback/dash-playback/DashPlayback.ts +1 -2
  34. package/src/playback/hls-playback/HlsPlayback.ts +1 -1
  35. package/src/playback/types.ts +10 -0
  36. package/src/playback.types.ts +0 -6
  37. package/src/plugins/multi-camera/MultiCamera.ts +103 -166
  38. package/src/plugins/source-controller/SourceController.ts +0 -39
  39. package/src/plugins/subtitles/ClosedCaptions.ts +1 -1
  40. package/src/utils/mediaSources.ts +10 -6
  41. package/tsconfig.tsbuildinfo +1 -1
  42. package/docs/api/player.multicamera.activebyid.md +0 -67
@@ -1,7 +1,11 @@
1
1
  import type { PlayerMediaSource, PlayerMediaSourceDesc, TransportPreference } from '../types';
2
+ export declare const MIME_TYPES_HLS: string[];
3
+ export declare const MIME_TYPE_HLS: string;
4
+ export declare const MIME_TYPE_DASH = "application/dash+xml";
2
5
  export declare function buildMediaSourcesList(sources: PlayerMediaSourceDesc[], priorityTransport?: TransportPreference): PlayerMediaSourceDesc[];
3
6
  export declare function unwrapSource(s: PlayerMediaSource): string;
4
7
  export declare function wrapSource(s: PlayerMediaSource): PlayerMediaSourceDesc;
8
+ export declare function guessMimeType(s: string): string | undefined;
5
9
  export declare function isDashSource(source: string, mimeType?: string): boolean;
6
10
  export declare function isHlsSource(source: string, mimeType?: string): boolean;
7
11
  //# sourceMappingURL=mediaSources.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mediaSources.d.ts","sourceRoot":"","sources":["../../src/utils/mediaSources.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACpB,MAAM,UAAU,CAAA;AAIjB,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,qBAAqB,EAAE,EAChC,iBAAiB,GAAE,mBAA4B,GAC9C,qBAAqB,EAAE,CAqCzB;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAEzD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,iBAAiB,GAAG,qBAAqB,CAEtE;AAYD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,WAK7D;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,WAO5D"}
1
+ {"version":3,"file":"mediaSources.d.ts","sourceRoot":"","sources":["../../src/utils/mediaSources.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACpB,MAAM,UAAU,CAAA;AAGjB,eAAO,MAAM,cAAc,UAA6D,CAAA;AACxF,eAAO,MAAM,aAAa,QAAoB,CAAA;AAC9C,eAAO,MAAM,cAAc,yBAAyB,CAAA;AAGpD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,qBAAqB,EAAE,EAChC,iBAAiB,GAAE,mBAA4B,GAC9C,qBAAqB,EAAE,CAqCzB;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAEzD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,iBAAiB,GAAG,qBAAqB,CAEtE;AAGD,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO3D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,WAK7D;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,WAO5D"}
@@ -1,5 +1,8 @@
1
1
  import { Loader } from '@clappr/core';
2
2
  import { trace } from '@gcorevideo/utils';
3
+ export const MIME_TYPES_HLS = ['application/x-mpegurl', 'application/vnd.apple.mpegurl'];
4
+ export const MIME_TYPE_HLS = MIME_TYPES_HLS[0];
5
+ export const MIME_TYPE_DASH = 'application/dash+xml';
3
6
  // TODO rewrite using the Playback classes and canPlay static methods
4
7
  export function buildMediaSourcesList(sources, priorityTransport = 'dash') {
5
8
  const playbacks = Loader.registeredPlaybacks;
@@ -40,24 +43,23 @@ export function unwrapSource(s) {
40
43
  export function wrapSource(s) {
41
44
  return typeof s === 'string' ? { source: s, mimeType: guessMimeType(s) } : s;
42
45
  }
43
- function guessMimeType(s) {
46
+ export function guessMimeType(s) {
44
47
  if (s.endsWith('.mpd')) {
45
- return 'application/dash+xml';
48
+ return MIME_TYPE_DASH;
46
49
  }
47
50
  if (s.endsWith('.m3u8')) {
48
- // return 'application/vnd.apple.mpegurl'
49
- return 'application/x-mpegurl';
51
+ return MIME_TYPE_HLS;
50
52
  }
51
53
  }
52
54
  export function isDashSource(source, mimeType) {
53
55
  if (mimeType) {
54
- return mimeType === 'application/dash+xml'; // TODO consider video/mp4
56
+ return mimeType === MIME_TYPE_DASH; // TODO consider video/mp4
55
57
  }
56
58
  return source.endsWith('.mpd');
57
59
  }
58
60
  export function isHlsSource(source, mimeType) {
59
61
  if (mimeType) {
60
- return ['application/vnd.apple.mpegurl', 'application/x-mpegurl'].includes(mimeType.toLowerCase());
62
+ return MIME_TYPES_HLS.includes(mimeType.toLowerCase());
61
63
  }
62
64
  return source.endsWith('.m3u8');
63
65
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gcorevideo/player",
3
- "version": "2.28.36",
3
+ "version": "2.29.0",
4
4
  "description": "Gcore JavaScript video player",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -30,11 +30,10 @@ import {
30
30
  QualityLevel,
31
31
  TimePosition,
32
32
  TimeValue,
33
- VTTCueInfo,
34
33
  } from '../../playback.types.js'
35
34
  import { isDashSource } from '../../utils/mediaSources.js'
36
35
  import { BasePlayback } from '../BasePlayback.js'
37
- import { PlaybackEvents } from '../types.js'
36
+ import { PlaybackEvents, VTTCueInfo } from '../types.js'
38
37
  import { AudioTrack } from '@clappr/core/types/base/playback/playback.js'
39
38
 
40
39
  const AUTO = -1
@@ -26,7 +26,6 @@ import {
26
26
  PlayerComponentType,
27
27
  QualityLevel,
28
28
  TimePosition,
29
- VTTCueInfo,
30
29
  } from '../../playback.types.js'
31
30
  import { PlaybackType } from '../../types.js'
32
31
  import { isHlsSource } from '../../utils/mediaSources.js'
@@ -35,6 +34,7 @@ import { BasePlayback } from '../BasePlayback.js'
35
34
 
36
35
  import { CLAPPR_VERSION } from '../../build.js'
37
36
  import { AudioTrack } from '@clappr/core/types/base/playback/playback.js'
37
+ import { VTTCueInfo } from '../types.js'
38
38
 
39
39
  const { now } = Utils
40
40
 
@@ -6,3 +6,13 @@ export enum PlaybackEvents {
6
6
  */
7
7
  PLAYBACK_RATE_CHANGED = 'playback:rate-changed',
8
8
  }
9
+
10
+ /**
11
+ * @internal
12
+ */
13
+ export type VTTCueInfo = {
14
+ id: string
15
+ start: number
16
+ end: number
17
+ text: string
18
+ }
@@ -145,9 +145,3 @@ export interface PlaybackError {
145
145
  }
146
146
  }
147
147
 
148
- export type VTTCueInfo = {
149
- id: string
150
- start: number
151
- end: number
152
- text: string
153
- }
@@ -7,6 +7,7 @@ import {
7
7
  UICorePlugin,
8
8
  } from '@clappr/core'
9
9
  import { reportError, trace } from '@gcorevideo/utils'
10
+ import { guessMimeType, MIME_TYPE_DASH } from '../../utils/mediaSources.js'
10
11
 
11
12
  import { CLAPPR_VERSION } from '../../build.js'
12
13
 
@@ -14,18 +15,28 @@ import pluginHtml from '../../../assets/multi-camera/multicamera.ejs'
14
15
  import '../../../assets/multi-camera/style.scss'
15
16
 
16
17
  import streamsIcon from '../../../assets/icons/old/streams.svg'
17
- import streamsMomentoIcon from '../../../assets/icons/old/language.svg'
18
- import streamsWhiteNightsIcon from '../../../assets/icons/old/wn.svg'
19
- import { ZeptoResult } from '../../types.js'
18
+ import { PlayerMediaSource, TransportPreference, ZeptoResult } from '../../types.js'
20
19
 
21
- type MultisourcesMode = 'one_first' | 'only_live' | 'show_all'
20
+ /**
21
+ * @beta
22
+ */
23
+ export type MultisourcesMode = 'one_first' | 'only_live' | 'show_all'
22
24
 
23
- type MediaSourceInfo = {
24
- live: boolean
25
- source: string
26
- id: number
25
+ /**
26
+ * Extended media source description
27
+ * @beta
28
+ */
29
+ export type MulticCameraSourceInfo = {
30
+ description: string
27
31
  dvr: boolean
32
+ hls_mpegts_url: string | null
33
+ id: number
34
+ live: boolean
28
35
  projection: string | null
36
+ screenshot: string | null
37
+ source: string
38
+ source_dash: string | null
39
+ title: string
29
40
  }
30
41
 
31
42
  const VERSION = '0.0.1'
@@ -37,13 +48,13 @@ const T = 'plugins.multicamera'
37
48
  * @beta
38
49
  */
39
50
  export class MultiCamera extends UICorePlugin {
40
- private currentCamera: MediaSourceInfo | null = null
51
+ private currentCamera: MulticCameraSourceInfo | null = null
41
52
 
42
53
  private currentTime: number = 0
43
54
 
44
55
  private playing = false
45
56
 
46
- private multicamera: MediaSourceInfo[] = []
57
+ private multicamera: MulticCameraSourceInfo[] = []
47
58
 
48
59
  private noActiveStreams = false
49
60
 
@@ -89,9 +100,11 @@ export class MultiCamera extends UICorePlugin {
89
100
  this.playing = this.options.multicameraPlay
90
101
  // Don't mutate the options, TODO check if some plugin observes the options.multicamera
91
102
  this.multicamera = this.options.multisources.map(
92
- (item: MediaSourceInfo) => ({ ...item }),
103
+ (item: MulticCameraSourceInfo) => ({ ...item }),
93
104
  )
94
105
  this.noActiveStreams = this.multicamera.every((item) => !item.live)
106
+ // TODO filter out non-live
107
+ this.core.options.sources = expandMediaSource(this.multicamera[0])
95
108
  }
96
109
 
97
110
  override bindEvents() {
@@ -113,19 +126,15 @@ export class MultiCamera extends UICorePlugin {
113
126
  )
114
127
  }
115
128
 
116
- unBindEvents() {
117
- // @ts-ignore
118
- this.stopListening(this.core, Events.CORE_READY)
119
- // @ts-ignore
129
+ private unBindEvents() {
130
+ this.stopListening(this.core, Events.CORE_READY, this.bindPlaybackEvents)
120
131
  this.stopListening(
121
132
  this.core.mediaControl,
122
133
  Events.MEDIACONTROL_CONTAINERCHANGED,
134
+ this.reload,
123
135
  )
124
- // @ts-ignore
125
- this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_RENDERED)
126
- // @ts-ignore
127
- this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_HIDE)
128
- // @ts-ignore
136
+ this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_RENDERED, this.render)
137
+ this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_HIDE, this.hideSelectLevelMenu)
129
138
  this.stopListening(
130
139
  this.core.activePlayback,
131
140
  Events.PLAYBACK_PLAY,
@@ -150,11 +159,7 @@ export class MultiCamera extends UICorePlugin {
150
159
  }
151
160
 
152
161
  private shouldRender() {
153
- if (!this.core.activeContainer || this.noActiveStreams) {
154
- return false
155
- }
156
-
157
- if (!this.core.activePlayback) {
162
+ if (this.noActiveStreams) {
158
163
  return false
159
164
  }
160
165
 
@@ -162,90 +167,61 @@ export class MultiCamera extends UICorePlugin {
162
167
  }
163
168
 
164
169
  override render() {
165
- if (this.shouldRender()) {
166
- let numActiveSources = 0
167
- // const currentSource = this.core.options.source
168
- const currentSource = this.core.activePlayback?.sourceMedia
169
-
170
- for (const item of this.multicamera) {
171
- if (item.live) {
172
- numActiveSources++
173
- }
174
- if (!this.currentCamera && item.source === currentSource) {
175
- this.currentCamera = item
176
- }
170
+ if (!this.core.activeContainer || !this.core.activePlayback) {
171
+ return this
172
+ }
173
+ if (!this.shouldRender()) {
174
+ return this
175
+ }
176
+
177
+ let numActiveSources = 0
178
+ const currentSource = this.core.activePlayback?.sourceMedia
179
+
180
+ for (const item of this.multicamera) {
181
+ if (item.live) {
182
+ numActiveSources++
183
+ }
184
+ if (!this.currentCamera && item.source === currentSource) {
185
+ this.currentCamera = item
177
186
  }
187
+ }
178
188
 
179
- // const mediaControl = this.core.getPlugin('media_control')
180
- if (
181
- this.currentTime &&
182
- // TODO check the last active playback type instead
183
- // !mediaControl.$el.hasClass('live') &&
184
- this.core.getPlaybackType() !== Playback.LIVE
185
- ) {
186
- if (this.currentTime < this.core.activePlayback.getDuration()) {
187
- this.core.activePlayback.seek(this.currentTime)
188
- }
189
-
190
- this.currentTime = 0
191
-
192
- // if (mediaControl.$el.hasClass('dvr')) {
193
- // this.core.activeContainer.dvrInUse = true;
194
- // }
189
+ if (
190
+ this.currentTime &&
191
+ this.core.getPlaybackType() !== Playback.LIVE
192
+ ) {
193
+ if (this.currentTime < this.core.activePlayback.getDuration()) {
194
+ this.core.activePlayback.seek(this.currentTime)
195
195
  }
196
196
 
197
- // TODO current source
198
- this.$el.html(
199
- this.template({
200
- streams: this.multicamera,
201
- multisources_mode: this.options.multisourcesMode,
202
- }),
203
- )
197
+ this.currentTime = 0
198
+ }
204
199
 
205
- if (
206
- (numActiveSources <= 1 &&
207
- this.options.multisourcesMode !== 'show_all') ||
208
- this.options.multisourcesMode === 'one_first'
209
- ) {
210
- this.$el.hide()
211
- } else {
212
- this.$el.show()
213
- }
200
+ this.$el.html(
201
+ this.template({
202
+ streams: this.multicamera,
203
+ multisources_mode: this.options.multisourcesMode,
204
+ }),
205
+ )
214
206
 
215
- if (
216
- this.core.mediaControl.$multiCameraSelector &&
217
- this.core.mediaControl.$multiCameraSelector.length > 0
218
- ) {
219
- this.core.mediaControl.$multiCameraSelector.append(this.el)
220
- } else {
221
- this.core.mediaControl.$('.media-control-right-panel').append(this.el)
222
- }
223
- if (
224
- Object.prototype.hasOwnProperty.call(
225
- this.core.mediaControl,
226
- '$multiCameraSelector',
227
- ) &&
228
- this.core.mediaControl.$multiCameraSelector.find(
229
- 'span.multicamera-icon',
230
- ).length > 0
231
- ) {
232
- if (~window.location.href.indexOf('whitenights.gcdn.co')) {
233
- this.core.mediaControl.$multiCameraSelector
234
- .find('span.multicamera-icon')
235
- .append(streamsWhiteNightsIcon)
236
- } else if (~window.location.href.indexOf('momentosolutions.gcdn.co')) {
237
- this.core.mediaControl.$multiCameraSelector
238
- .find('span.multicamera-icon')
239
- .append(streamsMomentoIcon)
240
- } else {
241
- this.core.mediaControl.$multiCameraSelector
242
- .find('span.multicamera-icon')
243
- .append(streamsIcon)
244
- }
245
- }
246
- this.highlightCurrentLevel()
207
+ if (
208
+ (numActiveSources < 2 &&
209
+ this.options.multisourcesMode !== 'show_all') ||
210
+ this.options.multisourcesMode === 'one_first'
211
+ ) {
212
+ this.$el.hide()
213
+ } else {
214
+ this.$el.show()
247
215
  }
248
216
 
217
+ const mediaControl = this.core.getPlugin('media_control')
218
+
219
+ mediaControl.slot('multicamera', this.$el)
220
+ this.$el
221
+ .find('span.multicamera-icon')
222
+ .html(streamsIcon)
223
+ this.highlightCurrentLevel()
224
+
249
225
  return this
250
226
  }
251
227
 
@@ -260,36 +236,6 @@ export class MultiCamera extends UICorePlugin {
260
236
  return false
261
237
  }
262
238
 
263
- activeById(id: number, active: boolean) {
264
- this.setLiveStatus(id, active)
265
-
266
- if (!this.currentCamera && !this.noActiveStreams) {
267
- return
268
- }
269
- if (this.noActiveStreams && !active) {
270
- return
271
- }
272
-
273
- if (this.currentCamera) {
274
- if (this.options.multisourcesMode === 'only_live') {
275
- this.behaviorLive(id, active)
276
- }
277
- if (this.options.multisourcesMode === 'one_first') {
278
- this.behaviorOne(id, active)
279
- }
280
- if (this.options.multisourcesMode === 'show_all') {
281
- this.behaviorAll(id, active)
282
- }
283
- } else {
284
- if (this.noActiveStreams && active) {
285
- this.changeById(id)
286
- this.noActiveStreams = false
287
- }
288
- }
289
-
290
- this.render()
291
- }
292
-
293
239
  private setLiveStatus(id: number, active: boolean) {
294
240
  try {
295
241
  const index = this.findIndexById(id)
@@ -355,7 +301,6 @@ export class MultiCamera extends UICorePlugin {
355
301
  this.currentCamera = null
356
302
  this.noActiveStreams = true
357
303
  this.core.trigger('core:multicamera:no_active_translation')
358
- // this.changeById(this.multicamera[nextIndex].id);
359
304
  }
360
305
 
361
306
  private showError() {
@@ -379,7 +324,7 @@ export class MultiCamera extends UICorePlugin {
379
324
 
380
325
  private hideError() {
381
326
  try {
382
- this.core.mediaControl.enableControlButton()
327
+ this.core.getPlugin('media_control')?.enableControlButton()
383
328
  } catch (error) {
384
329
  reportError(error)
385
330
  }
@@ -390,14 +335,9 @@ export class MultiCamera extends UICorePlugin {
390
335
  queueMicrotask(() => {
391
336
  const playbackOptions = this.core.options.playback || {}
392
337
 
393
- // TODO figure out what this does
338
+ // TODO figure out if it's needed
394
339
  playbackOptions.recycleVideo = Browser.isMobile
395
- this.currentCamera = this.findElementById(id) ?? null
396
- trace(`${T} changeById`, {
397
- id,
398
- currentCamera: this.currentCamera,
399
- multicamera: this.multicamera,
400
- })
340
+ this.currentCamera = this.findElementById(id)
401
341
 
402
342
  if (!this.currentCamera) {
403
343
  return
@@ -423,29 +363,17 @@ export class MultiCamera extends UICorePlugin {
423
363
  this.core.configure({
424
364
  playback: playbackOptions,
425
365
  source: this.currentCamera.source, // TODO ensure that the preferred transport is used
426
- video360: {
427
- // TODO
428
- projection: this.currentCamera.projection,
429
- },
430
366
  fullscreenDisable,
431
367
  autoPlay: this.playing,
432
368
  disableCanAutoPlay: true,
433
369
  })
434
- this.core.activeContainer.mediaControlDisabled = false
370
+ this.core.activeContainer?.enableMediaControl()
435
371
  })
436
372
  this.toggleContextMenu()
437
373
  }
438
374
 
439
- private getCamerasList() {
440
- return this.multicamera
441
- }
442
-
443
- private getCurrentCamera() {
444
- return this.currentCamera
445
- }
446
-
447
- private findElementById(id: number): MediaSourceInfo | undefined {
448
- return this.multicamera.find((element) => element.id === id)
375
+ private findElementById(id: number): MulticCameraSourceInfo | null {
376
+ return this.multicamera.find((element) => element.id === id) ?? null
449
377
  }
450
378
 
451
379
  private findIndexById(id: number): number {
@@ -457,24 +385,16 @@ export class MultiCamera extends UICorePlugin {
457
385
  }
458
386
 
459
387
  private hideSelectLevelMenu() {
460
- ; (this.$('.multicamera ul') as ZeptoResult).hide()
388
+ ; (this.$('ul') as ZeptoResult).hide()
461
389
  }
462
390
 
463
391
  private toggleContextMenu() {
464
- ; (this.$('.multicamera ul') as ZeptoResult).toggle()
392
+ ; (this.$('ul') as ZeptoResult).toggle()
465
393
  }
466
394
 
467
- // private buttonElement(): ZeptoResult {
468
- // return this.$('.multicamera button');
469
- // }
470
-
471
- // private buttonElementText(): ZeptoResult {
472
- // return this.$('.multicamera button .quality-text');
473
- // }
474
-
475
395
  private levelElement(id?: number): ZeptoResult {
476
396
  return this.$(
477
- '.multicamera ul li > div' +
397
+ 'ul .multicamera-item' +
478
398
  (id !== undefined
479
399
  ? '[data-multicamera-selector-select="' + id + '"]'
480
400
  : ''),
@@ -488,3 +408,20 @@ export class MultiCamera extends UICorePlugin {
488
408
  this.levelElement(this.currentCamera.id).addClass('multicamera-active')
489
409
  }
490
410
  }
411
+
412
+ function expandMediaSource(source: MulticCameraSourceInfo): PlayerMediaSource[] {
413
+ const result: PlayerMediaSource[] = [{
414
+ source: source.source,
415
+ mimeType: guessMimeType(source.source),
416
+ }]
417
+ if (source.source_dash) {
418
+ result.push({
419
+ source: source.source_dash,
420
+ mimeType: MIME_TYPE_DASH,
421
+ })
422
+ }
423
+ if (source.hls_mpegts_url) {
424
+ result.push(source.hls_mpegts_url)
425
+ }
426
+ return result
427
+ }
@@ -30,45 +30,6 @@ function noSync(cb: () => void) {
30
30
  * `PLUGIN` that is managing the automatic failover between media sources.
31
31
  * @public
32
32
  * @remarks
33
- * Have a look at the {@link https://miro.com/app/board/uXjVLiN15tY=/?share_link_id=390327585787 | source failover diagram} for the details
34
- * on how sources ordering and selection works. Below is a simplified diagram:
35
- *
36
- * ```markdown
37
- * sources_list:
38
- * - a.mpd | +--------------------+
39
- * - b.m3u8 |--->| init |
40
- * - ... | |--------------------|
41
- * | current_source = 0 |
42
- * +--------------------+
43
- * |
44
- * | source = a.mpd
45
- * | playback = dash.js
46
- * v
47
- * +------------------+
48
- * +-->| load source |
49
- * | +---------|--------+
50
- * | v
51
- * | +------------------+
52
- * | | play |
53
- * | +---------|--------+
54
- * | |
55
- * | v
56
- * | +-----------------------+
57
- * | | on playback_error |
58
- * | |-----------------------|
59
- * | | current_source = |
60
- * | | (current_source + 1) |
61
- * | | % len sources_list |
62
- * | | |
63
- * | | delay 1..3s |
64
- * | +---------------|-------+
65
- * | |
66
- * | source=b.m3u8 |
67
- * | playback=hls.js |
68
- * +-------------------+
69
- *
70
- * ```
71
- *
72
33
  * @example
73
34
  * ```ts
74
35
  * import { SourceController } from '@gcorevideo/player'
@@ -15,7 +15,7 @@ import { isFullscreen } from '../utils/fullscreen.js'
15
15
  import type { ZeptoResult } from '../../types.js'
16
16
  import { ExtendedEvents } from '../media-control/MediaControl.js'
17
17
  import { mediaControlClickaway } from '../../utils/clickaway.js'
18
- import { VTTCueInfo } from '../../playback.types.js'
18
+ import { VTTCueInfo } from '../../playback/types.js'
19
19
 
20
20
  const VERSION: string = '2.19.14'
21
21
 
@@ -7,6 +7,10 @@ import type {
7
7
  } from '../types'
8
8
  import { trace } from '@gcorevideo/utils'
9
9
 
10
+ export const MIME_TYPES_HLS = ['application/x-mpegurl', 'application/vnd.apple.mpegurl']
11
+ export const MIME_TYPE_HLS = MIME_TYPES_HLS[0]
12
+ export const MIME_TYPE_DASH = 'application/dash+xml'
13
+
10
14
  // TODO rewrite using the Playback classes and canPlay static methods
11
15
  export function buildMediaSourcesList(
12
16
  sources: PlayerMediaSourceDesc[],
@@ -58,26 +62,26 @@ export function wrapSource(s: PlayerMediaSource): PlayerMediaSourceDesc {
58
62
  return typeof s === 'string' ? { source: s, mimeType: guessMimeType(s) } : s
59
63
  }
60
64
 
61
- function guessMimeType(s: string): string | undefined {
65
+
66
+ export function guessMimeType(s: string): string | undefined {
62
67
  if (s.endsWith('.mpd')) {
63
- return 'application/dash+xml'
68
+ return MIME_TYPE_DASH
64
69
  }
65
70
  if (s.endsWith('.m3u8')) {
66
- // return 'application/vnd.apple.mpegurl'
67
- return 'application/x-mpegurl'
71
+ return MIME_TYPE_HLS
68
72
  }
69
73
  }
70
74
 
71
75
  export function isDashSource(source: string, mimeType?: string) {
72
76
  if (mimeType) {
73
- return mimeType === 'application/dash+xml' // TODO consider video/mp4
77
+ return mimeType === MIME_TYPE_DASH // TODO consider video/mp4
74
78
  }
75
79
  return source.endsWith('.mpd')
76
80
  }
77
81
 
78
82
  export function isHlsSource(source: string, mimeType?: string) {
79
83
  if (mimeType) {
80
- return ['application/vnd.apple.mpegurl', 'application/x-mpegurl'].includes(
84
+ return MIME_TYPES_HLS.includes(
81
85
  mimeType.toLowerCase(),
82
86
  )
83
87
  }