@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.
- package/assets/media-control/media-control.scss +8 -6
- package/assets/multi-camera/multicamera.ejs +27 -23
- package/assets/multi-camera/style.scss +7 -34
- package/assets/style/main.scss +2 -2
- package/dist/core.js +8 -6
- package/dist/index.css +427 -449
- package/dist/index.embed.js +8 -45
- package/dist/index.js +77 -181
- package/docs/api/player.md +22 -9
- package/docs/api/player.mediacontrol.setkeepvisible.md +56 -0
- package/docs/api/player.multicamera.md +0 -28
- package/docs/api/player.multiccamerasourceinfo.md +27 -0
- package/docs/api/{player.multicamera.unbindevents.md → player.multisourcesmode.md} +4 -7
- package/docs/api/player.sourcecontroller.md +0 -37
- package/lib/playback/dash-playback/DashPlayback.d.ts +2 -1
- package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
- package/lib/playback/hls-playback/HlsPlayback.d.ts +2 -1
- package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
- package/lib/playback/types.d.ts +9 -0
- package/lib/playback/types.d.ts.map +1 -1
- package/lib/playback.types.d.ts +0 -6
- package/lib/playback.types.d.ts.map +1 -1
- package/lib/plugins/multi-camera/MultiCamera.d.ts +21 -4
- package/lib/plugins/multi-camera/MultiCamera.d.ts.map +1 -1
- package/lib/plugins/multi-camera/MultiCamera.js +70 -134
- package/lib/plugins/source-controller/SourceController.d.ts +0 -39
- package/lib/plugins/source-controller/SourceController.d.ts.map +1 -1
- package/lib/plugins/source-controller/SourceController.js +0 -39
- package/lib/utils/mediaSources.d.ts +4 -0
- package/lib/utils/mediaSources.d.ts.map +1 -1
- package/lib/utils/mediaSources.js +8 -6
- package/package.json +1 -1
- package/src/playback/dash-playback/DashPlayback.ts +1 -2
- package/src/playback/hls-playback/HlsPlayback.ts +1 -1
- package/src/playback/types.ts +10 -0
- package/src/playback.types.ts +0 -6
- package/src/plugins/multi-camera/MultiCamera.ts +103 -166
- package/src/plugins/source-controller/SourceController.ts +0 -39
- package/src/plugins/subtitles/ClosedCaptions.ts +1 -1
- package/src/utils/mediaSources.ts +10 -6
- package/tsconfig.tsbuildinfo +1 -1
- 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;
|
|
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
|
|
48
|
+
return MIME_TYPE_DASH;
|
|
46
49
|
}
|
|
47
50
|
if (s.endsWith('.m3u8')) {
|
|
48
|
-
|
|
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 ===
|
|
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
|
|
62
|
+
return MIME_TYPES_HLS.includes(mimeType.toLowerCase());
|
|
61
63
|
}
|
|
62
64
|
return source.endsWith('.m3u8');
|
|
63
65
|
}
|
package/package.json
CHANGED
|
@@ -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
|
|
package/src/playback/types.ts
CHANGED
package/src/playback.types.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
20
|
+
/**
|
|
21
|
+
* @beta
|
|
22
|
+
*/
|
|
23
|
+
export type MultisourcesMode = 'one_first' | 'only_live' | 'show_all'
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
125
|
-
this.stopListening(this.core.mediaControl, Events.
|
|
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 (
|
|
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.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
this.core.
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
this.template({
|
|
200
|
-
streams: this.multicamera,
|
|
201
|
-
multisources_mode: this.options.multisourcesMode,
|
|
202
|
-
}),
|
|
203
|
-
)
|
|
197
|
+
this.currentTime = 0
|
|
198
|
+
}
|
|
204
199
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
this.options.multisourcesMode
|
|
209
|
-
)
|
|
210
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
this.
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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.
|
|
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
|
|
338
|
+
// TODO figure out if it's needed
|
|
394
339
|
playbackOptions.recycleVideo = Browser.isMobile
|
|
395
|
-
this.currentCamera = this.findElementById(id)
|
|
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
|
|
370
|
+
this.core.activeContainer?.enableMediaControl()
|
|
435
371
|
})
|
|
436
372
|
this.toggleContextMenu()
|
|
437
373
|
}
|
|
438
374
|
|
|
439
|
-
private
|
|
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.$('
|
|
388
|
+
; (this.$('ul') as ZeptoResult).hide()
|
|
461
389
|
}
|
|
462
390
|
|
|
463
391
|
private toggleContextMenu() {
|
|
464
|
-
; (this.$('
|
|
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
|
|
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
|
|
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
|
-
|
|
65
|
+
|
|
66
|
+
export function guessMimeType(s: string): string | undefined {
|
|
62
67
|
if (s.endsWith('.mpd')) {
|
|
63
|
-
return
|
|
68
|
+
return MIME_TYPE_DASH
|
|
64
69
|
}
|
|
65
70
|
if (s.endsWith('.m3u8')) {
|
|
66
|
-
|
|
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 ===
|
|
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
|
|
84
|
+
return MIME_TYPES_HLS.includes(
|
|
81
85
|
mimeType.toLowerCase(),
|
|
82
86
|
)
|
|
83
87
|
}
|