Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
/**
2
 * videojs-ogvjs
3
 * @version 1.0.0
4
 * @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
5
 * @license MIT
6
 */
7
define(['media_videojs/video-lazy', './local/ogv/ogv'], (function (videojs, ogv) { 'use strict';
8
 
9
	// We can access public classes either as ogv.OGVPlayer or just OGVPlayer.
10
	// But ogv.OGVPlayer will make the lint tools happier.
11
	const OGVCompat = ogv.OGVCompat;
12
	const OGVLoader = ogv.OGVLoader;
13
	const OGVPlayer = ogv.OGVPlayer;
14
	const Tech = videojs.getComponent('Tech');
15
 
16
	const androidOS = 'Android';
17
	const iPhoneOS = 'iPhoneOS';
18
	const iPadOS = 'iPadOS';
19
	const otherOS = 'Other';
20
 
21
	/**
22
	 * Object.defineProperty but "lazy", which means that the value is only set after
23
	 * it retrieved the first time, rather than being set right away.
24
	 *
25
	 * @param {Object} obj the object to set the property on.
26
	 * @param {string} key the key for the property to set.
27
	 * @param {Function} getValue the function used to get the value when it is needed.
28
	 * @param {boolean} setter whether a setter should be allowed or not.
29
	 */
30
	const defineLazyProperty = (obj, key, getValue, setter = true) => {
31
		const set = (value) => {
32
			Object.defineProperty(obj, key, {value, enumerable: true, writable: true});
33
		};
34
 
35
		const options = {
36
			configurable: true,
37
			enumerable: true,
38
			get() {
39
				const value = getValue();
40
 
41
				set(value);
42
				return value;
43
			}
44
		};
45
 
46
		if (setter) {
47
			options.set = set;
48
		}
49
 
50
		return Object.defineProperty(obj, key, options);
51
	};
52
 
53
	/**
54
	 * Get the device's OS.
55
	 *
56
	 * @return {string} Device's OS.
57
	 */
58
	const getDeviceOS = () => {
59
		/* global navigator */
60
		const ua = navigator.userAgent;
61
 
62
		if (/android/i.test(ua)) {
63
			return androidOS;
64
		} else if (/iPad|iPhone|iPod/.test(ua)) {
65
			return iPhoneOS;
66
		} else if ((navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) {
67
			return iPadOS;
68
		}
69
		return otherOS;
70
	};
71
 
72
	/**
73
	 * OgvJS Media Controller - Wrapper for ogv.js Media API
74
	 *
75
	 * @mixes Tech~SourceHandlerAdditions
76
	 * @extends Tech
77
	 */
78
	class OgvJS extends Tech {
79
 
80
		/**
81
		 * Create an instance of this Tech.
82
		 *
83
		 * @param {Object} [options] The key/value store of player options.
84
		 * @param {Component~ReadyCallback} ready Callback function to call when the `OgvJS` Tech is ready.
85
		 */
86
		constructor(options, ready) {
87
			super(options, ready);
88
 
89
			this.el_.src = options.source.src;
90
			OgvJS.setIfAvailable(this.el_, 'autoplay', options.autoplay);
91
			OgvJS.setIfAvailable(this.el_, 'loop', options.loop);
92
			OgvJS.setIfAvailable(this.el_, 'poster', options.poster);
93
			OgvJS.setIfAvailable(this.el_, 'preload', options.preload);
94
 
95
			this.on('loadedmetadata', () => {
96
				if (getDeviceOS() === iPhoneOS) {
97
					// iPhoneOS add some inline styles to the canvas, we need to remove it.
98
					const canvas = this.el_.getElementsByTagName('canvas')[0];
99
 
100
					canvas.style.removeProperty('width');
101
					canvas.style.removeProperty('margin');
102
				}
103
			});
104
 
105
			this.triggerReady();
106
		}
107
 
108
		/**
109
		 * Create the 'OgvJS' Tech's DOM element.
110
		 *
111
		 * @return {Element} The element that gets created.
112
		 */
113
		createEl() {
114
			const options = this.options_;
115
 
116
			if (options.base) {
117
				OGVLoader.base = options.base;
118
			} else {
119
				throw new Error('Please specify the base for the ogv.js library');
120
			}
121
 
122
			const el = new OGVPlayer(options);
123
 
124
			el.className += ' vjs-tech';
125
			options.tag = el;
126
 
127
			return el;
128
		}
129
 
130
		/**
131
		 * Start playback
132
		 *
133
		 * @method play
134
		 */
135
		play() {
136
			this.el_.play();
137
		}
138
 
139
		/**
140
		 * Get the current playback speed.
141
		 *
142
		 * @return {number}
143
		 * @method playbackRate
144
		 */
145
		playbackRate() {
146
			return this.el_.playbackRate || 1;
147
		}
148
 
149
		/**
150
		 * Set the playback speed.
151
		 *
152
		 * @param {number} val Speed for the player to play.
153
		 * @method setPlaybackRate
154
		 */
155
		setPlaybackRate(val) {
156
			if (this.el_.hasOwnProperty('playbackRate')) {
157
				this.el_.playbackRate = val;
158
			}
159
		}
160
 
161
		/**
162
		 * Returns a TimeRanges object that represents the ranges of the media resource that the user agent has played.
163
		 *
164
		 * @return {TimeRangeObject} the range of points on the media timeline that has been reached through normal playback
165
		 */
166
		played() {
167
			return this.el_.played;
168
		}
169
 
170
		/**
171
		 * Pause playback
172
		 *
173
		 * @method pause
174
		 */
175
		pause() {
176
			this.el_.pause();
177
		}
178
 
179
		/**
180
		 * Is the player paused or not.
181
		 *
182
		 * @return {boolean}
183
		 * @method paused
184
		 */
185
		paused() {
186
			return this.el_.paused;
187
		}
188
 
189
		/**
190
		 * Get current playing time.
191
		 *
192
		 * @return {number}
193
		 * @method currentTime
194
		 */
195
		currentTime() {
196
			return this.el_.currentTime;
197
		}
198
 
199
		/**
200
		 * Set current playing time.
201
		 *
202
		 * @param {number} seconds Current time of audio/video.
203
		 * @method setCurrentTime
204
		 */
205
		setCurrentTime(seconds) {
206
			try {
207
				this.el_.currentTime = seconds;
208
			} catch (e) {
209
				videojs.log(e, 'Media is not ready. (Video.JS)');
210
			}
211
		}
212
 
213
		/**
214
		 * Get media's duration.
215
		 *
216
		 * @return {number}
217
		 * @method duration
218
		 */
219
		duration() {
220
			if (this.el_.duration && this.el_.duration !== Infinity) {
221
				return this.el_.duration;
222
			}
223
 
224
			return 0;
225
		}
226
 
227
		/**
228
		 * Get a TimeRange object that represents the intersection
229
		 * of the time ranges for which the user agent has all
230
		 * relevant media.
231
		 *
232
		 * @return {TimeRangeObject}
233
		 * @method buffered
234
		 */
235
		buffered() {
236
			return this.el_.buffered;
237
		}
238
 
239
		/**
240
		 * Get current volume level.
241
		 *
242
		 * @return {number}
243
		 * @method volume
244
		 */
245
		volume() {
246
			return this.el_.hasOwnProperty('volume') ? this.el_.volume : 1;
247
		}
248
 
249
		/**
250
		 * Set current playing volume level.
251
		 *
252
		 * @param {number} percentAsDecimal Volume percent as a decimal.
253
		 * @method setVolume
254
		 */
255
		setVolume(percentAsDecimal) {
256
			// Apple does not allow iOS and iPadOS devices to set the volume on UI.
257
			if (getDeviceOS() !== iPhoneOS && getDeviceOS() !== iPadOS && this.el_.hasOwnProperty('volume')) {
258
				this.el_.volume = percentAsDecimal;
259
			}
260
		}
261
 
262
		/**
263
		 * Is the player muted or not.
264
		 *
265
		 * @return {boolean}
266
		 * @method muted
267
		 */
268
		muted() {
269
			return this.el_.muted;
270
		}
271
 
272
		/**
273
		 * Mute the player.
274
		 *
275
		 * @param {boolean} muted True to mute the player.
276
		 */
277
		setMuted(muted) {
278
			this.el_.muted = !!muted;
279
		}
280
 
281
		/**
282
		 * Is the player muted by default or not.
283
		 *
284
		 * @return {boolean}
285
		 * @method defaultMuted
286
		 */
287
		defaultMuted() {
288
			return this.el_.defaultMuted || false;
289
		}
290
 
291
		/**
292
		 * Get the player width.
293
		 *
294
		 * @return {number}
295
		 * @method width
296
		 */
297
		width() {
298
			return this.el_.offsetWidth;
299
		}
300
 
301
		/**
302
		 * Get the player height.
303
		 *
304
		 * @return {number}
305
		 * @method height
306
		 */
307
		height() {
308
			return this.el_.offsetHeight;
309
		}
310
 
311
		/**
312
		 * Get the video width.
313
		 *
314
		 * @return {number}
315
		 * @method videoWidth
316
		 */
317
		videoWidth() {
318
			return this.el_.videoWidth;
319
		}
320
 
321
		/**
322
		 * Get the video height.
323
		 *
324
		 * @return {number}
325
		 * @method videoHeight
326
		 */
327
		videoHeight() {
328
			return this.el_.videoHeight;
329
		}
330
 
331
		/**
332
		 * Get/set media source.
333
		 *
334
		 * @param {Object=} src Source object
335
		 * @return {Object}
336
		 * @method src
337
		 */
338
		src(src) {
339
			if (typeof src === 'undefined') {
340
				return this.el_.src;
341
			}
342
			this.el_.src = src;
343
		}
344
 
345
		/**
346
		 * Load the media into the player.
347
		 *
348
		 * @method load
349
		 */
350
		load() {
351
			this.el_.load();
352
		}
353
 
354
		/**
355
		 * Get current media source.
356
		 *
357
		 * @return {Object}
358
		 * @method currentSrc
359
		 */
360
		currentSrc() {
361
			if (this.currentSource_) {
362
				return this.currentSource_.src;
363
			}
364
			return this.el_.currentSrc;
365
		}
366
 
367
		/**
368
		 * Get media poster URL.
369
		 *
370
		 * @return {string}
371
		 * @method poster
372
		 */
373
		poster() {
374
			return this.el_.poster;
375
		}
376
 
377
		/**
378
		 * Set media poster URL.
379
		 *
380
		 * @param {string} url the poster image's url.
381
		 * @method
382
		 */
383
		setPoster(url) {
384
			this.el_.poster = url;
385
		}
386
 
387
		/**
388
		 * Is the media preloaded or not.
389
		 *
390
		 * @return {string}
391
		 * @method preload
392
		 */
393
		preload() {
394
			return this.el_.preload || 'none';
395
		}
396
 
397
		/**
398
		 * Set the media preload method.
399
		 *
400
		 * @param {string} val Value for preload attribute.
401
		 * @method setPreload
402
		 */
403
		setPreload(val) {
404
			if (this.el_.hasOwnProperty('preload')) {
405
				this.el_.preload = val;
406
			}
407
		}
408
 
409
		/**
410
		 * Is the media auto-played or not.
411
		 *
412
		 * @return {boolean}
413
		 * @method autoplay
414
		 */
415
		autoplay() {
416
			return this.el_.autoplay || false;
417
		}
418
 
419
		/**
420
		 * Set media autoplay method.
421
		 *
422
		 * @param {boolean} val Value for autoplay attribute.
423
		 * @method setAutoplay
424
		 */
425
		setAutoplay(val) {
426
			if (this.el_.hasOwnProperty('autoplay')) {
427
				this.el_.autoplay = !!val;
428
			}
429
		}
430
 
431
		/**
432
		 * Does the media has controls or not.
433
		 *
434
		 * @return {boolean}
435
		 * @method controls
436
		 */
437
		controls() {
438
			return this.el_.controls || false;
439
		}
440
 
441
		/**
442
		 * Set the media controls method.
443
		 *
444
		 * @param {boolean} val Value for controls attribute.
445
		 * @method setControls
446
		 */
447
		setControls(val) {
448
			if (this.el_.hasOwnProperty('controls')) {
449
				this.el_.controls = !!val;
450
			}
451
		}
452
 
453
		/**
454
		 * Is the media looped or not.
455
		 *
456
		 * @return {boolean}
457
		 * @method loop
458
		 */
459
		loop() {
460
			return this.el_.loop || false;
461
		}
462
 
463
		/**
464
		 * Set the media loop method.
465
		 *
466
		 * @param {boolean} val Value for loop attribute.
467
		 * @method setLoop
468
		 */
469
		setLoop(val) {
470
			if (this.el_.hasOwnProperty('loop')) {
471
				this.el_.loop = !!val;
472
			}
473
		}
474
 
475
		/**
476
		 * Get a TimeRanges object that represents the
477
		 * ranges of the media resource to which it is possible
478
		 * for the user agent to seek.
479
		 *
480
		 * @return {TimeRangeObject}
481
		 * @method seekable
482
		 */
483
		seekable() {
484
			return this.el_.seekable;
485
		}
486
 
487
		/**
488
		 * Is player in the "seeking" state or not.
489
		 *
490
		 * @return {boolean}
491
		 * @method seeking
492
		 */
493
		seeking() {
494
			return this.el_.seeking;
495
		}
496
 
497
		/**
498
		 * Is the media ended or not.
499
		 *
500
		 * @return {boolean}
501
		 * @method ended
502
		 */
503
		ended() {
504
			return this.el_.ended;
505
		}
506
 
507
		/**
508
		 * Get the current state of network activity
509
		 * NETWORK_EMPTY (numeric value 0)
510
		 * NETWORK_IDLE (numeric value 1)
511
		 * NETWORK_LOADING (numeric value 2)
512
		 * NETWORK_NO_SOURCE (numeric value 3)
513
		 *
514
		 * @return {number}
515
		 * @method networkState
516
		 */
517
		networkState() {
518
			return this.el_.networkState;
519
		}
520
 
521
		/**
522
		 * Get the current state of the player.
523
		 * HAVE_NOTHING (numeric value 0)
524
		 * HAVE_METADATA (numeric value 1)
525
		 * HAVE_CURRENT_DATA (numeric value 2)
526
		 * HAVE_FUTURE_DATA (numeric value 3)
527
		 * HAVE_ENOUGH_DATA (numeric value 4)
528
		 *
529
		 * @return {number}
530
		 * @method readyState
531
		 */
532
		readyState() {
533
			return this.el_.readyState;
534
		}
535
 
536
		/**
537
		 * Does the player support native fullscreen mode or not. (Mobile devices)
538
		 *
539
		 * @return {boolean}
540
		 */
541
		supportsFullScreen() {
542
			// iOS devices have some problem with HTML5 fullscreen api so we need to fallback to fullWindow mode.
543
			return false;
544
		}
545
 
546
		/**
547
		 * Get media player error.
548
		 *
549
		 * @return {string}
550
		 * @method error
551
		 */
552
		error() {
553
			return this.el_.error;
554
		}
555
 
556
	}
557
 
558
	/**
559
	 * List of available events of the media player.
560
	 *
561
	 * @private
562
	 * @type {Array}
563
	 */
564
	OgvJS.Events = [
565
		'loadstart',
566
		'suspend',
567
		'abort',
568
		'error',
569
		'emptied',
570
		'stalled',
571
		'loadedmetadata',
572
		'loadeddata',
573
		'canplay',
574
		'canplaythrough',
575
		'playing',
576
		'waiting',
577
		'seeking',
578
		'seeked',
579
		'ended',
580
		'durationchange',
581
		'timeupdate',
582
		'progress',
583
		'play',
584
		'pause',
585
		'ratechange',
586
		'resize',
587
		'volumechange'
588
	];
589
 
590
	/**
591
	 * Set the value for the player is it has that property.
592
	 *
593
	 * @param {Element} el
594
	 * @param {string} name
595
	 * @param value
596
	 */
597
	OgvJS.setIfAvailable = (el, name, value) => {
598
		if (el.hasOwnProperty(name)) {
599
			el[name] = value;
600
		}
601
	};
602
 
603
	/**
604
	 * Check if browser/device is supported by Ogv.JS.
605
	 *
606
	 * @return {boolean}
607
	 */
608
	OgvJS.isSupported = () => {
609
		return OGVCompat.supported('OGVPlayer');
610
	};
611
 
612
	/**
613
	 * Check if the tech can support the given type.
614
	 *
615
	 * @param {string} type The mimetype to check
616
	 * @return {string} 'probably', 'maybe', or '' (empty string)
617
	 */
618
	OgvJS.canPlayType = (type) => {
619
		return (type.indexOf('/ogg') !== -1 || type.indexOf('/webm')) ? 'maybe' : '';
620
	};
621
 
622
	/**
623
	 * Check if the tech can support the given source
624
	 *
625
	 * @param srcObj The source object
626
	 * @return {string} The options passed to the tech
627
	 */
628
	OgvJS.canPlaySource = (srcObj) => {
629
		return OgvJS.canPlayType(srcObj.type);
630
	};
631
 
632
	/**
633
	 * Check if the volume can be changed in this browser/device.
634
	 * Volume cannot be changed in a lot of mobile devices.
635
	 * Specifically, it can't be changed on iOS and iPadOS.
636
	 *
637
	 * @return {boolean} True if volume can be controlled.
638
	 */
639
	OgvJS.canControlVolume = () => {
640
		if (getDeviceOS() === iPhoneOS || getDeviceOS() === iPadOS) {
641
			return false;
642
		}
643
		const p = new OGVPlayer();
644
 
645
		return p.hasOwnProperty('volume');
646
	};
647
 
648
	/**
649
	 * Check if the volume can be muted in this browser/device.
650
	 *
651
	 * @return {boolean} True if volume can be muted.
652
	 */
653
	OgvJS.canMuteVolume = () => {
654
		return true;
655
	};
656
 
657
	/**
658
	 * Check if the playback rate can be changed in this browser/device.
659
	 *
660
	 * @return {boolean} True if playback rate can be controlled.
661
	 */
662
	OgvJS.canControlPlaybackRate = () => {
663
		return true;
664
	};
665
 
666
	/**
667
	 * Check to see if native 'TextTracks' are supported by this browser/device.
668
	 *
669
	 * @return {boolean} True if native 'TextTracks' are supported.
670
	 */
671
	OgvJS.supportsNativeTextTracks = () => {
672
		return false;
673
	};
674
 
675
	/**
676
	 * Check if the fullscreen resize is supported by this browser/device.
677
	 *
678
	 * @return {boolean} True if the fullscreen resize is supported.
679
	 */
680
	OgvJS.supportsFullscreenResize = () => {
681
		return true;
682
	};
683
 
684
	/**
685
	 * Check if the progress events is supported by this browser/device.
686
	 *
687
	 * @return {boolean} True if the progress events is supported.
688
	 */
689
	OgvJS.supportsProgressEvents = () => {
690
		return true;
691
	};
692
 
693
	/**
694
	 * Check if the time update events is supported by this browser/device.
695
	 *
696
	 * @return {boolean} True if the time update events is supported.
697
	 */
698
	OgvJS.supportsTimeupdateEvents = () => {
699
		return true;
700
	};
701
 
702
	/**
703
	 * Boolean indicating whether the 'OgvJS' tech supports volume control.
704
	 *
705
	 * @type {boolean}
706
	 * @default {@link OgvJS.canControlVolume}
707
	 */
708
	/**
709
	 * Boolean indicating whether the 'OgvJS' tech supports muting volume.
710
	 *
711
	 * @type {boolean}
712
	 * @default {@link OgvJS.canMuteVolume}
713
	 */
714
	/**
715
	 * Boolean indicating whether the 'OgvJS' tech supports changing the speed at which the media plays.
716
	 * Examples:
717
	 *   - Set player to play 2x (twice) as fast.
718
	 *   - Set player to play 0.5x (half) as fast.
719
	 *
720
	 * @type {boolean}
721
	 * @default {@link OgvJS.canControlPlaybackRate}
722
	 */
723
	/**
724
	 * Boolean indicating whether the 'OgvJS' tech currently supports native 'TextTracks'.
725
	 *
726
	 * @type {boolean}
727
	 * @default {@link OgvJS.supportsNativeTextTracks}
728
	 */
729
	/**
730
	 * Boolean indicating whether the 'OgvJS' tech currently supports fullscreen resize.
731
	 *
732
	 * @type {boolean}
733
	 * @default {@link OgvJS.supportsFullscreenResize}
734
	 */
735
	/**
736
	 * Boolean indicating whether the 'OgvJS' tech currently supports progress events.
737
	 *
738
	 * @type {boolean}
739
	 * @default {@link OgvJS.supportsProgressEvents}
740
	 */
741
	/**
742
	 * Boolean indicating whether the 'OgvJS' tech currently supports time update events.
743
	 *
744
	 * @type {boolean}
745
	 * @default {@link OgvJS.supportsTimeupdateEvents}
746
	 */
747
	[
748
		['featuresVolumeControl', 'canControlVolume'],
749
		['featuresMuteControl', 'canMuteVolume'],
750
		['featuresPlaybackRate', 'canControlPlaybackRate'],
751
		['featuresNativeTextTracks', 'supportsNativeTextTracks'],
752
		['featuresFullscreenResize', 'supportsFullscreenResize'],
753
		['featuresProgressEvents', 'supportsProgressEvents'],
754
		['featuresTimeupdateEvents', 'supportsTimeupdateEvents']
755
	].forEach(([key, fn]) => {
756
		defineLazyProperty(OgvJS.prototype, key, () => OgvJS[fn](), true);
757
	});
758
 
759
	Tech.registerTech('OgvJS', OgvJS);
760
 
761
	return OgvJS;
762
 
763
}));