3 exports.__esModule = true;
5 var _templateObject = _taggedTemplateLiteralLoose(['Text Tracks are being loaded from another origin but the crossorigin attribute isn\'t used.\n This may prevent text tracks from loading.'], ['Text Tracks are being loaded from another origin but the crossorigin attribute isn\'t used.\n This may prevent text tracks from loading.']);
7 var _tech = require('./tech.js');
9 var _tech2 = _interopRequireDefault(_tech);
11 var _component = require('../component');
13 var _component2 = _interopRequireDefault(_component);
15 var _dom = require('../utils/dom.js');
17 var Dom = _interopRequireWildcard(_dom);
19 var _url = require('../utils/url.js');
21 var Url = _interopRequireWildcard(_url);
23 var _fn = require('../utils/fn.js');
25 var Fn = _interopRequireWildcard(_fn);
27 var _log = require('../utils/log.js');
29 var _log2 = _interopRequireDefault(_log);
31 var _tsml = require('tsml');
33 var _tsml2 = _interopRequireDefault(_tsml);
35 var _browser = require('../utils/browser.js');
37 var browser = _interopRequireWildcard(_browser);
39 var _document = require('global/document');
41 var _document2 = _interopRequireDefault(_document);
43 var _window = require('global/window');
45 var _window2 = _interopRequireDefault(_window);
47 var _obj = require('../utils/obj');
49 var _mergeOptions = require('../utils/merge-options.js');
51 var _mergeOptions2 = _interopRequireDefault(_mergeOptions);
53 var _toTitleCase = require('../utils/to-title-case.js');
55 var _toTitleCase2 = _interopRequireDefault(_toTitleCase);
57 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
59 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
61 function _taggedTemplateLiteralLoose(strings, raw) { strings.raw = raw; return strings; }
63 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
65 function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
67 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
73 * HTML5 Media Controller - Wrapper for HTML5 Media API
75 * @mixes Tech~SouceHandlerAdditions
78 var Html5 = function (_Tech) {
79 _inherits(Html5, _Tech);
82 * Create an instance of this Tech.
84 * @param {Object} [options]
85 * The key/value store of player options.
87 * @param {Component~ReadyCallback} ready
88 * Callback function to call when the `HTML5` Tech is ready.
90 function Html5(options, ready) {
91 _classCallCheck(this, Html5);
93 var _this = _possibleConstructorReturn(this, _Tech.call(this, options, ready));
95 var source = options.source;
96 var crossoriginTracks = false;
98 // Set the source if one is provided
99 // 1) Check if the source is new (if not, we want to keep the original so playback isn't interrupted)
100 // 2) Check to see if the network state of the tag was failed at init, and if so, reset the source
101 // anyway so the error gets fired.
102 if (source && (_this.el_.currentSrc !== source.src || options.tag && options.tag.initNetworkState_ === 3)) {
103 _this.setSource(source);
105 _this.handleLateInit_(_this.el_);
108 if (_this.el_.hasChildNodes()) {
110 var nodes = _this.el_.childNodes;
111 var nodesLength = nodes.length;
112 var removeNodes = [];
114 while (nodesLength--) {
115 var node = nodes[nodesLength];
116 var nodeName = node.nodeName.toLowerCase();
118 if (nodeName === 'track') {
119 if (!_this.featuresNativeTextTracks) {
120 // Empty video tag tracks so the built-in player doesn't use them also.
121 // This may not be fast enough to stop HTML5 browsers from reading the tags
122 // so we'll need to turn off any default tracks if we're manually doing
123 // captions and subtitles. videoElement.textTracks
124 removeNodes.push(node);
126 // store HTMLTrackElement and TextTrack to remote list
127 _this.remoteTextTrackEls().addTrackElement_(node);
128 _this.remoteTextTracks().addTrack_(node.track);
129 if (!crossoriginTracks && !_this.el_.hasAttribute('crossorigin') && Url.isCrossOrigin(node.src)) {
130 crossoriginTracks = true;
136 for (var i = 0; i < removeNodes.length; i++) {
137 _this.el_.removeChild(removeNodes[i]);
141 // TODO: add text tracks into this list
142 var trackTypes = ['audio', 'video'];
144 // ProxyNative Video/Audio Track
145 trackTypes.forEach(function (type) {
146 var elTracks = _this.el()[type + 'Tracks'];
147 var techTracks = _this[type + 'Tracks']();
148 var capitalType = (0, _toTitleCase2['default'])(type);
150 if (!_this['featuresNative' + capitalType + 'Tracks'] || !elTracks || !elTracks.addEventListener) {
154 _this['handle' + capitalType + 'TrackChange_'] = function (e) {
158 currentTarget: techTracks,
159 srcElement: techTracks
163 _this['handle' + capitalType + 'TrackAdd_'] = function (e) {
164 return techTracks.addTrack(e.track);
166 _this['handle' + capitalType + 'TrackRemove_'] = function (e) {
167 return techTracks.removeTrack(e.track);
170 elTracks.addEventListener('change', _this['handle' + capitalType + 'TrackChange_']);
171 elTracks.addEventListener('addtrack', _this['handle' + capitalType + 'TrackAdd_']);
172 elTracks.addEventListener('removetrack', _this['handle' + capitalType + 'TrackRemove_']);
173 _this['removeOld' + capitalType + 'Tracks_'] = function (e) {
174 return _this.removeOldTracks_(techTracks, elTracks);
177 // Remove (native) tracks that are not used anymore
178 _this.on('loadstart', _this['removeOld' + capitalType + 'Tracks_']);
181 if (_this.featuresNativeTextTracks) {
182 if (crossoriginTracks) {
183 _log2['default'].warn((0, _tsml2['default'])(_templateObject));
186 _this.handleTextTrackChange_ = Fn.bind(_this, _this.handleTextTrackChange);
187 _this.handleTextTrackAdd_ = Fn.bind(_this, _this.handleTextTrackAdd);
188 _this.handleTextTrackRemove_ = Fn.bind(_this, _this.handleTextTrackRemove);
189 _this.proxyNativeTextTracks_();
192 // Determine if native controls should be used
193 // Our goal should be to get the custom controls on mobile solid everywhere
194 // so we can remove this all together. Right now this will block custom
195 // controls on touch enabled laptops like the Chrome Pixel
196 if ((browser.TOUCH_ENABLED || browser.IS_IPHONE || browser.IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) {
197 _this.setControls(true);
200 // on iOS, we want to proxy `webkitbeginfullscreen` and `webkitendfullscreen`
201 // into a `fullscreenchange` event
202 _this.proxyWebkitFullscreen_();
204 _this.triggerReady();
209 * Dispose of `HTML5` media element and remove all tracks.
213 Html5.prototype.dispose = function dispose() {
216 // Un-ProxyNativeTracks
217 ['audio', 'video', 'text'].forEach(function (type) {
218 var capitalType = (0, _toTitleCase2['default'])(type);
219 var tl = _this2.el_[type + 'Tracks'];
221 if (tl && tl.removeEventListener) {
222 tl.removeEventListener('change', _this2['handle' + capitalType + 'TrackChange_']);
223 tl.removeEventListener('addtrack', _this2['handle' + capitalType + 'TrackAdd_']);
224 tl.removeEventListener('removetrack', _this2['handle' + capitalType + 'TrackRemove_']);
227 // Stop removing old text tracks
229 _this2.off('loadstart', _this2['removeOld' + capitalType + 'Tracks_']);
233 Html5.disposeMediaElement(this.el_);
234 // tech will handle clearing of the emulated track list
235 _Tech.prototype.dispose.call(this);
239 * Create the `Html5` Tech's DOM element.
242 * The element that gets created.
246 Html5.prototype.createEl = function createEl() {
247 var el = this.options_.tag;
249 // Check if this browser supports moving the element into the box.
250 // On the iPhone video will break if you move the element,
251 // So we have to create a brand new element.
252 // If we ingested the player div, we do not need to move the media element.
253 if (!el || !(this.options_.playerElIngest || this.movingMediaElementInDOM)) {
255 // If the original tag is still there, clone and remove it.
257 var clone = el.cloneNode(true);
260 el.parentNode.insertBefore(clone, el);
262 Html5.disposeMediaElement(el);
265 el = _document2['default'].createElement('video');
267 // determine if native controls should be used
268 var tagAttributes = this.options_.tag && Dom.getElAttributes(this.options_.tag);
269 var attributes = (0, _mergeOptions2['default'])({}, tagAttributes);
271 if (!browser.TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) {
272 delete attributes.controls;
275 Dom.setElAttributes(el, (0, _obj.assign)(attributes, {
276 id: this.options_.techId,
281 el.playerId = this.options_.playerId;
284 // Update specific tag settings, in case they were overridden
285 var settingsAttrs = ['autoplay', 'preload', 'loop', 'muted'];
287 for (var i = settingsAttrs.length - 1; i >= 0; i--) {
288 var attr = settingsAttrs[i];
289 var overwriteAttrs = {};
291 if (typeof this.options_[attr] !== 'undefined') {
292 overwriteAttrs[attr] = this.options_[attr];
294 Dom.setElAttributes(el, overwriteAttrs);
301 * This will be triggered if the loadstart event has already fired, before videojs was
302 * ready. Two known examples of when this can happen are:
303 * 1. If we're loading the playback object after it has started loading
304 * 2. The media is already playing the (often with autoplay on) then
306 * This function will fire another loadstart so that videojs can catchup.
308 * @fires Tech#loadstart
310 * @return {undefined}
315 Html5.prototype.handleLateInit_ = function handleLateInit_(el) {
316 if (el.networkState === 0 || el.networkState === 3) {
317 // The video element hasn't started loading the source yet
318 // or didn't find a source
322 if (el.readyState === 0) {
323 // NetworkState is set synchronously BUT loadstart is fired at the
324 // end of the current stack, usually before setInterval(fn, 0).
325 // So at this point we know loadstart may have already fired or is
326 // about to fire, and either way the player hasn't seen it yet.
327 // We don't want to fire loadstart prematurely here and cause a
328 // double loadstart so we'll wait and see if it happens between now
329 // and the next loop, and fire it if not.
330 // HOWEVER, we also want to make sure it fires before loadedmetadata
331 // which could also happen between now and the next loop, so we'll
332 // watch for that also.
333 var loadstartFired = false;
334 var setLoadstartFired = function setLoadstartFired() {
335 loadstartFired = true;
338 this.on('loadstart', setLoadstartFired);
340 var triggerLoadstart = function triggerLoadstart() {
341 // We did miss the original loadstart. Make sure the player
342 // sees loadstart before loadedmetadata
343 if (!loadstartFired) {
344 this.trigger('loadstart');
348 this.on('loadedmetadata', triggerLoadstart);
350 this.ready(function () {
351 this.off('loadstart', setLoadstartFired);
352 this.off('loadedmetadata', triggerLoadstart);
354 if (!loadstartFired) {
355 // We did miss the original native loadstart. Fire it now.
356 this.trigger('loadstart');
363 // From here on we know that loadstart already fired and we missed it.
364 // The other readyState events aren't as much of a problem if we double
365 // them, so not going to go to as much trouble as loadstart to prevent
366 // that unless we find reason to.
367 var eventsToTrigger = ['loadstart'];
369 // loadedmetadata: newly equal to HAVE_METADATA (1) or greater
370 eventsToTrigger.push('loadedmetadata');
372 // loadeddata: newly increased to HAVE_CURRENT_DATA (2) or greater
373 if (el.readyState >= 2) {
374 eventsToTrigger.push('loadeddata');
377 // canplay: newly increased to HAVE_FUTURE_DATA (3) or greater
378 if (el.readyState >= 3) {
379 eventsToTrigger.push('canplay');
382 // canplaythrough: newly equal to HAVE_ENOUGH_DATA (4)
383 if (el.readyState >= 4) {
384 eventsToTrigger.push('canplaythrough');
387 // We still need to give the player time to add event listeners
388 this.ready(function () {
389 eventsToTrigger.forEach(function (type) {
396 * Add event listeners to native text track events. This adds the native text tracks
397 * to our emulated {@link TextTrackList}.
401 Html5.prototype.proxyNativeTextTracks_ = function proxyNativeTextTracks_() {
402 var tt = this.el().textTracks;
405 // Add tracks - if player is initialised after DOM loaded, textTracks
406 // will not trigger addtrack
407 for (var i = 0; i < tt.length; i++) {
408 this.textTracks().addTrack_(tt[i]);
411 if (tt.addEventListener) {
412 tt.addEventListener('change', this.handleTextTrackChange_);
413 tt.addEventListener('addtrack', this.handleTextTrackAdd_);
414 tt.addEventListener('removetrack', this.handleTextTrackRemove_);
417 // Remove (native) texttracks that are not used anymore
418 this.on('loadstart', this.removeOldTextTracks_);
423 * Handle any {@link TextTrackList} `change` event.
425 * @param {EventTarget~Event} e
426 * The `change` event that caused this to run.
428 * @listens TextTrackList#change
432 Html5.prototype.handleTextTrackChange = function handleTextTrackChange(e) {
433 var tt = this.textTracks();
435 this.textTracks().trigger({
444 * Handle any {@link TextTrackList} `addtrack` event.
446 * @param {EventTarget~Event} e
447 * The `addtrack` event that caused this to run.
449 * @listens TextTrackList#addtrack
453 Html5.prototype.handleTextTrackAdd = function handleTextTrackAdd(e) {
454 this.textTracks().addTrack_(e.track);
458 * Handle any {@link TextTrackList} `removetrack` event.
460 * @param {EventTarget~Event} e
461 * The `removetrack` event that caused this to run.
463 * @listens TextTrackList#removetrack
467 Html5.prototype.handleTextTrackRemove = function handleTextTrackRemove(e) {
468 this.textTracks().removeTrack_(e.track);
472 * This function removes any {@link AudioTrack}s, {@link VideoTrack}s, or
473 * {@link TextTrack}s that are not in the media elements TrackList.
475 * @param {TrackList} techTracks
476 * HTML5 Tech's TrackList to search through
478 * @param {TrackList} elTracks
479 * HTML5 media elements TrackList to search trough.
485 Html5.prototype.removeOldTracks_ = function removeOldTracks_(techTracks, elTracks) {
486 // This will loop over the techTracks and check if they are still used by the HTML5 media element
487 // If not, they will be removed from the emulated list
488 var removeTracks = [];
494 for (var i = 0; i < techTracks.length; i++) {
495 var techTrack = techTracks[i];
498 for (var j = 0; j < elTracks.length; j++) {
499 if (elTracks[j] === techTrack) {
506 removeTracks.push(techTrack);
510 for (var _i = 0; _i < removeTracks.length; _i++) {
511 var track = removeTracks[_i];
513 techTracks.removeTrack_(track);
518 * Remove {@link TextTrack}s that dont exist in the native track list from our
519 * emulated {@link TextTrackList}.
521 * @listens Tech#loadstart
525 Html5.prototype.removeOldTextTracks_ = function removeOldTextTracks_(e) {
526 var techTracks = this.textTracks();
527 var elTracks = this.el().textTracks;
529 this.removeOldTracks_(techTracks, elTracks);
533 * Called by {@link Player#play} to play using the `Html5` `Tech`.
537 Html5.prototype.play = function play() {
538 var playPromise = this.el_.play();
540 // Catch/silence error when a pause interrupts a play request
541 // on browsers which return a promise
542 if (playPromise !== undefined && typeof playPromise.then === 'function') {
543 playPromise.then(null, function (e) {});
548 * Set current time for the `HTML5` tech.
550 * @param {number} seconds
551 * Set the current time of the media to this.
555 Html5.prototype.setCurrentTime = function setCurrentTime(seconds) {
557 this.el_.currentTime = seconds;
559 (0, _log2['default'])(e, 'Video is not ready. (Video.js)');
560 // this.warning(VideoJS.warnings.videoNotReady);
565 * Get the current duration of the HTML5 media element.
568 * The duration of the media or 0 if there is no duration.
572 Html5.prototype.duration = function duration() {
575 // Android Chrome will report duration as Infinity for VOD HLS until after
576 // playback has started, which triggers the live display erroneously.
577 // Return NaN if playback has not started and trigger a durationupdate once
578 // the duration can be reliably known.
579 if (this.el_.duration === Infinity && browser.IS_ANDROID && browser.IS_CHROME) {
580 if (this.el_.currentTime === 0) {
581 // Wait for the first `timeupdate` with currentTime > 0 - there may be
583 var checkProgress = function checkProgress() {
584 if (_this3.el_.currentTime > 0) {
585 // Trigger durationchange for genuinely live video
586 if (_this3.el_.duration === Infinity) {
587 _this3.trigger('durationchange');
589 _this3.off('timeupdate', checkProgress);
593 this.on('timeupdate', checkProgress);
597 return this.el_.duration || NaN;
601 * Get the current width of the HTML5 media element.
604 * The width of the HTML5 media element.
608 Html5.prototype.width = function width() {
609 return this.el_.offsetWidth;
613 * Get the current height of the HTML5 media element.
616 * The heigth of the HTML5 media element.
620 Html5.prototype.height = function height() {
621 return this.el_.offsetHeight;
625 * Proxy iOS `webkitbeginfullscreen` and `webkitendfullscreen` into
626 * `fullscreenchange` event.
629 * @fires fullscreenchange
630 * @listens webkitendfullscreen
631 * @listens webkitbeginfullscreen
632 * @listens webkitbeginfullscreen
636 Html5.prototype.proxyWebkitFullscreen_ = function proxyWebkitFullscreen_() {
639 if (!('webkitDisplayingFullscreen' in this.el_)) {
643 var endFn = function endFn() {
644 this.trigger('fullscreenchange', { isFullscreen: false });
647 var beginFn = function beginFn() {
648 this.one('webkitendfullscreen', endFn);
650 this.trigger('fullscreenchange', { isFullscreen: true });
653 this.on('webkitbeginfullscreen', beginFn);
654 this.on('dispose', function () {
655 _this4.off('webkitbeginfullscreen', beginFn);
656 _this4.off('webkitendfullscreen', endFn);
661 * Check if fullscreen is supported on the current playback device.
664 * - True if fullscreen is supported.
665 * - False if fullscreen is not supported.
669 Html5.prototype.supportsFullScreen = function supportsFullScreen() {
670 if (typeof this.el_.webkitEnterFullScreen === 'function') {
671 var userAgent = _window2['default'].navigator && _window2['default'].navigator.userAgent || '';
673 // Seems to be broken in Chromium/Chrome && Safari in Leopard
674 if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) {
682 * Request that the `HTML5` Tech enter fullscreen.
686 Html5.prototype.enterFullScreen = function enterFullScreen() {
687 var video = this.el_;
689 if (video.paused && video.networkState <= video.HAVE_METADATA) {
690 // attempt to prime the video element for programmatic access
691 // this isn't necessary on the desktop but shouldn't hurt
694 // playing and pausing synchronously during the transition to fullscreen
695 // can get iOS ~6.1 devices into a play/pause loop
696 this.setTimeout(function () {
698 video.webkitEnterFullScreen();
701 video.webkitEnterFullScreen();
706 * Request that the `HTML5` Tech exit fullscreen.
710 Html5.prototype.exitFullScreen = function exitFullScreen() {
711 this.el_.webkitExitFullScreen();
715 * A getter/setter for the `Html5` Tech's source object.
716 * > Note: Please use {@link Html5#setSource}
718 * @param {Tech~SourceObject} [src]
719 * The source object you want to set on the `HTML5` techs element.
721 * @return {Tech~SourceObject|undefined}
722 * - The current source object when a source is not passed in.
723 * - undefined when setting
725 * @deprecated Since version 5.
729 Html5.prototype.src = function src(_src) {
730 if (_src === undefined) {
734 // Setting src through `src` instead of `setSrc` will be deprecated
739 * Reset the tech by removing all sources and then calling
740 * {@link Html5.resetMediaElement}.
744 Html5.prototype.reset = function reset() {
745 Html5.resetMediaElement(this.el_);
749 * Get the current source on the `HTML5` Tech. Falls back to returning the source from
750 * the HTML5 media element.
752 * @return {Tech~SourceObject}
753 * The current source object from the HTML5 tech. With a fallback to the
758 Html5.prototype.currentSrc = function currentSrc() {
759 if (this.currentSource_) {
760 return this.currentSource_.src;
762 return this.el_.currentSrc;
766 * Set controls attribute for the HTML5 media Element.
768 * @param {string} val
769 * Value to set the controls attribute to
773 Html5.prototype.setControls = function setControls(val) {
774 this.el_.controls = !!val;
778 * Create and returns a remote {@link TextTrack} object.
780 * @param {string} kind
781 * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
783 * @param {string} [label]
784 * Label to identify the text track
786 * @param {string} [language]
787 * Two letter language abbreviation
789 * @return {TextTrack}
790 * The TextTrack that gets created.
794 Html5.prototype.addTextTrack = function addTextTrack(kind, label, language) {
795 if (!this.featuresNativeTextTracks) {
796 return _Tech.prototype.addTextTrack.call(this, kind, label, language);
799 return this.el_.addTextTrack(kind, label, language);
803 * Creates either native TextTrack or an emulated TextTrack depending
804 * on the value of `featuresNativeTextTracks`
806 * @param {Object} options
807 * The object should contain the options to intialize the TextTrack with.
809 * @param {string} [options.kind]
810 * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
812 * @param {string} [options.label].
813 * Label to identify the text track
815 * @param {string} [options.language]
816 * Two letter language abbreviation.
818 * @param {boolean} [options.default]
819 * Default this track to on.
821 * @param {string} [options.id]
822 * The internal id to assign this track.
824 * @param {string} [options.src]
825 * A source url for the track.
827 * @return {HTMLTrackElement}
828 * The track element that gets created.
832 Html5.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) {
833 if (!this.featuresNativeTextTracks) {
834 return _Tech.prototype.createRemoteTextTrack.call(this, options);
836 var htmlTrackElement = _document2['default'].createElement('track');
839 htmlTrackElement.kind = options.kind;
842 htmlTrackElement.label = options.label;
844 if (options.language || options.srclang) {
845 htmlTrackElement.srclang = options.language || options.srclang;
847 if (options['default']) {
848 htmlTrackElement['default'] = options['default'];
851 htmlTrackElement.id = options.id;
854 htmlTrackElement.src = options.src;
857 return htmlTrackElement;
861 * Creates a remote text track object and returns an html track element.
863 * @param {Object} options The object should contain values for
864 * kind, language, label, and src (location of the WebVTT file)
865 * @param {Boolean} [manualCleanup=true] if set to false, the TextTrack will be
866 * automatically removed from the video element whenever the source changes
867 * @return {HTMLTrackElement} An Html Track Element.
868 * This can be an emulated {@link HTMLTrackElement} or a native one.
869 * @deprecated The default value of the "manualCleanup" parameter will default
870 * to "false" in upcoming versions of Video.js
874 Html5.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
875 var htmlTrackElement = _Tech.prototype.addRemoteTextTrack.call(this, options, manualCleanup);
877 if (this.featuresNativeTextTracks) {
878 this.el().appendChild(htmlTrackElement);
881 return htmlTrackElement;
885 * Remove remote `TextTrack` from `TextTrackList` object
887 * @param {TextTrack} track
888 * `TextTrack` object to remove
892 Html5.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
893 _Tech.prototype.removeRemoteTextTrack.call(this, track);
895 if (this.featuresNativeTextTracks) {
896 var tracks = this.$$('track');
898 var i = tracks.length;
901 if (track === tracks[i] || track === tracks[i].track) {
902 this.el().removeChild(tracks[i]);
909 }(_tech2['default']);
911 /* HTML5 Support Testing ---------------------------------------------------- */
916 * Element for testing browser HTML5 media capabilities
922 Html5.TEST_VID = _document2['default'].createElement('video');
923 var track = _document2['default'].createElement('track');
925 track.kind = 'captions';
926 track.srclang = 'en';
927 track.label = 'English';
928 Html5.TEST_VID.appendChild(track);
932 * Check if HTML5 media is supported by this browser/device.
935 * - True if HTML5 media is supported.
936 * - False if HTML5 media is not supported.
938 Html5.isSupported = function () {
939 // IE9 with no Media Player is a LIAR! (#984)
941 Html5.TEST_VID.volume = 0.5;
946 return !!(Html5.TEST_VID && Html5.TEST_VID.canPlayType);
950 * Check if the volume can be changed in this browser/device.
951 * Volume cannot be changed in a lot of mobile devices.
952 * Specifically, it can't be changed from 1 on iOS.
955 * - True if volume can be controlled
958 Html5.canControlVolume = function () {
959 // IE will error if Windows Media Player not installed #3315
961 var volume = Html5.TEST_VID.volume;
963 Html5.TEST_VID.volume = volume / 2 + 0.1;
964 return volume !== Html5.TEST_VID.volume;
971 * Check if the playback rate can be changed in this browser/device.
974 * - True if playback rate can be controlled
977 Html5.canControlPlaybackRate = function () {
978 // Playback rate API is implemented in Android Chrome, but doesn't do anything
979 // https://github.com/videojs/video.js/issues/3180
980 if (browser.IS_ANDROID && browser.IS_CHROME) {
983 // IE will error if Windows Media Player not installed #3315
985 var playbackRate = Html5.TEST_VID.playbackRate;
987 Html5.TEST_VID.playbackRate = playbackRate / 2 + 0.1;
988 return playbackRate !== Html5.TEST_VID.playbackRate;
995 * Check to see if native `TextTrack`s are supported by this browser/device.
998 * - True if native `TextTrack`s are supported.
1001 Html5.supportsNativeTextTracks = function () {
1002 return browser.IS_ANY_SAFARI;
1006 * Check to see if native `VideoTrack`s are supported by this browser/device
1009 * - True if native `VideoTrack`s are supported.
1012 Html5.supportsNativeVideoTracks = function () {
1013 return !!(Html5.TEST_VID && Html5.TEST_VID.videoTracks);
1017 * Check to see if native `AudioTrack`s are supported by this browser/device
1020 * - True if native `AudioTrack`s are supported.
1023 Html5.supportsNativeAudioTracks = function () {
1024 return !!(Html5.TEST_VID && Html5.TEST_VID.audioTracks);
1028 * An array of events available on the Html5 tech.
1033 Html5.Events = ['loadstart', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough', 'playing', 'waiting', 'seeking', 'seeked', 'ended', 'durationchange', 'timeupdate', 'progress', 'play', 'pause', 'ratechange', 'volumechange'];
1036 * Boolean indicating whether the `Tech` supports volume control.
1039 * @default {@link Html5.canControlVolume}
1041 Html5.prototype.featuresVolumeControl = Html5.canControlVolume();
1044 * Boolean indicating whether the `Tech` supports changing the speed at which the media
1046 * - Set player to play 2x (twice) as fast
1047 * - Set player to play 0.5x (half) as fast
1050 * @default {@link Html5.canControlPlaybackRate}
1052 Html5.prototype.featuresPlaybackRate = Html5.canControlPlaybackRate();
1055 * Boolean indicating whether the `HTML5` tech currently supports the media element
1056 * moving in the DOM. iOS breaks if you move the media element, so this is set this to
1057 * false there. Everywhere else this should be true.
1062 Html5.prototype.movingMediaElementInDOM = !browser.IS_IOS;
1064 // TODO: Previous comment: No longer appears to be used. Can probably be removed.
1067 * Boolean indicating whether the `HTML5` tech currently supports automatic media resize
1068 * when going into fullscreen.
1073 Html5.prototype.featuresFullscreenResize = true;
1076 * Boolean indicating whether the `HTML5` tech currently supports the progress event.
1077 * If this is false, manual `progress` events will be triggred instead.
1082 Html5.prototype.featuresProgressEvents = true;
1085 * Boolean indicating whether the `HTML5` tech currently supports the timeupdate event.
1086 * If this is false, manual `timeupdate` events will be triggred instead.
1090 Html5.prototype.featuresTimeupdateEvents = true;
1093 * Boolean indicating whether the `HTML5` tech currently supports native `TextTrack`s.
1096 * @default {@link Html5.supportsNativeTextTracks}
1098 Html5.prototype.featuresNativeTextTracks = Html5.supportsNativeTextTracks();
1101 * Boolean indicating whether the `HTML5` tech currently supports native `VideoTrack`s.
1104 * @default {@link Html5.supportsNativeVideoTracks}
1106 Html5.prototype.featuresNativeVideoTracks = Html5.supportsNativeVideoTracks();
1109 * Boolean indicating whether the `HTML5` tech currently supports native `AudioTrack`s.
1112 * @default {@link Html5.supportsNativeAudioTracks}
1114 Html5.prototype.featuresNativeAudioTracks = Html5.supportsNativeAudioTracks();
1116 // HTML5 Feature detection and Device Fixes --------------------------------- //
1117 var canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType;
1118 var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i;
1119 var mp4RE = /^video\/mp4/i;
1121 Html5.patchCanPlayType = function () {
1123 // Android 4.0 and above can play HLS to some extent but it reports being unable to do so
1124 if (browser.ANDROID_VERSION >= 4.0 && !browser.IS_FIREFOX) {
1125 Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
1126 if (type && mpegurlRE.test(type)) {
1129 return canPlayType.call(this, type);
1132 // Override Android 2.2 and less canPlayType method which is broken
1133 } else if (browser.IS_OLD_ANDROID) {
1134 Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
1135 if (type && mp4RE.test(type)) {
1138 return canPlayType.call(this, type);
1143 Html5.unpatchCanPlayType = function () {
1144 var r = Html5.TEST_VID.constructor.prototype.canPlayType;
1146 Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType;
1150 // by default, patch the media element
1151 Html5.patchCanPlayType();
1153 Html5.disposeMediaElement = function (el) {
1158 if (el.parentNode) {
1159 el.parentNode.removeChild(el);
1162 // remove any child track or source nodes to prevent their loading
1163 while (el.hasChildNodes()) {
1164 el.removeChild(el.firstChild);
1167 // remove any src reference. not setting `src=''` because that causes a warning
1169 el.removeAttribute('src');
1171 // force the media element to update its loading state by calling load()
1172 // however IE on Windows 7N has a bug that throws an error so need a try/catch (#793)
1173 if (typeof el.load === 'function') {
1174 // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
1185 Html5.resetMediaElement = function (el) {
1190 var sources = el.querySelectorAll('source');
1191 var i = sources.length;
1194 el.removeChild(sources[i]);
1197 // remove any src reference.
1198 // not setting `src=''` because that throws an error
1199 el.removeAttribute('src');
1201 if (typeof el.load === 'function') {
1202 // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
1213 /* Native HTML5 element property wrapping ----------------------------------- */
1214 // Wrap native properties with a getter
1217 * Get the value of `paused` from the media element. `paused` indicates whether the media element
1218 * is currently paused or not.
1220 * @method Html5#paused
1222 * The value of `paused` from the media element.
1224 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused}
1229 * Get the value of `currentTime` from the media element. `currentTime` indicates
1230 * the current second that the media is at in playback.
1232 * @method Html5#currentTime
1234 * The value of `currentTime` from the media element.
1236 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime}
1241 * Get the value of `buffered` from the media element. `buffered` is a `TimeRange`
1242 * object that represents the parts of the media that are already downloaded and
1243 * available for playback.
1245 * @method Html5#buffered
1246 * @return {TimeRange}
1247 * The value of `buffered` from the media element.
1249 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered}
1254 * Get the value of `volume` from the media element. `volume` indicates
1255 * the current playback volume of audio for a media. `volume` will be a value from 0
1256 * (silent) to 1 (loudest and default).
1258 * @method Html5#volume
1260 * The value of `volume` from the media element. Value will be between 0-1.
1262 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
1267 * Get the value of `muted` from the media element. `muted` indicates
1268 * that the volume for the media should be set to silent. This does not actually change
1269 * the `volume` attribute.
1271 * @method Html5#muted
1273 * - True if the value of `volume` should be ignored and the audio set to silent.
1274 * - False if the value of `volume` should be used.
1276 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
1281 * Get the value of `poster` from the media element. `poster` indicates
1282 * that the url of an image file that can/will be shown when no media data is available.
1284 * @method Html5#poster
1286 * The value of `poster` from the media element. Value will be a url to an
1289 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster}
1294 * Get the value of `preload` from the media element. `preload` indicates
1295 * what should download before the media is interacted with. It can have the following
1297 * - none: nothing should be downloaded
1298 * - metadata: poster and the first few frames of the media may be downloaded to get
1299 * media dimensions and other metadata
1300 * - auto: allow the media and metadata for the media to be downloaded before
1303 * @method Html5#preload
1305 * The value of `preload` from the media element. Will be 'none', 'metadata',
1308 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
1313 * Get the value of `autoplay` from the media element. `autoplay` indicates
1314 * that the media should start to play as soon as the page is ready.
1316 * @method Html5#autoplay
1318 * - The value of `autoplay` from the media element.
1319 * - True indicates that the media should start as soon as the page loads.
1320 * - False indicates that the media should not start as soon as the page loads.
1322 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
1327 * Get the value of `controls` from the media element. `controls` indicates
1328 * whether the native media controls should be shown or hidden.
1330 * @method Html5#controls
1332 * - The value of `controls` from the media element.
1333 * - True indicates that native controls should be showing.
1334 * - False indicates that native controls should be hidden.
1336 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls}
1341 * Get the value of `loop` from the media element. `loop` indicates
1342 * that the media should return to the start of the media and continue playing once
1343 * it reaches the end.
1345 * @method Html5#loop
1347 * - The value of `loop` from the media element.
1348 * - True indicates that playback should seek back to start once
1349 * the end of a media is reached.
1350 * - False indicates that playback should not loop back to the start when the
1351 * end of the media is reached.
1353 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
1358 * Get the value of the `error` from the media element. `error` indicates any
1359 * MediaError that may have occured during playback. If error returns null there is no
1362 * @method Html5#error
1363 * @return {MediaError|null}
1364 * The value of `error` from the media element. Will be `MediaError` if there
1365 * is a current error and null otherwise.
1367 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error}
1372 * Get the value of `seeking` from the media element. `seeking` indicates whether the
1373 * media is currently seeking to a new position or not.
1375 * @method Html5#seeking
1377 * - The value of `seeking` from the media element.
1378 * - True indicates that the media is currently seeking to a new position.
1379 * - Flase indicates that the media is not seeking to a new position at this time.
1381 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking}
1386 * Get the value of `seekable` from the media element. `seekable` returns a
1387 * `TimeRange` object indicating ranges of time that can currently be `seeked` to.
1389 * @method Html5#seekable
1390 * @return {TimeRange}
1391 * The value of `seekable` from the media element. A `TimeRange` object
1392 * indicating the current ranges of time that can be seeked to.
1394 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable}
1399 * Get the value of `ended` from the media element. `ended` indicates whether
1400 * the media has reached the end or not.
1402 * @method Html5#ended
1404 * - The value of `ended` from the media element.
1405 * - True indicates that the media has ended.
1406 * - False indicates that the media has not ended.
1408 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended}
1413 * Get the value of `defaultMuted` from the media element. `defaultMuted` indicates
1414 * whether the media should start muted or not. Only changes the default state of the
1415 * media. `muted` and `defaultMuted` can have different values. `muted` indicates the
1418 * @method Html5#defaultMuted
1420 * - The value of `defaultMuted` from the media element.
1421 * - True indicates that the media should start muted.
1422 * - False indicates that the media should not start muted
1424 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
1429 * Get the value of `playbackRate` from the media element. `playbackRate` indicates
1430 * the rate at which the media is currently playing back. Examples:
1431 * - if playbackRate is set to 2, media will play twice as fast.
1432 * - if playbackRate is set to 0.5, media will play half as fast.
1434 * @method Html5#playbackRate
1436 * The value of `playbackRate` from the media element. A number indicating
1437 * the current playback speed of the media, where 1 is normal speed.
1439 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
1444 * Get the value of `played` from the media element. `played` returns a `TimeRange`
1445 * object representing points in the media timeline that have been played.
1447 * @method Html5#played
1448 * @return {TimeRange}
1449 * The value of `played` from the media element. A `TimeRange` object indicating
1450 * the ranges of time that have been played.
1452 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played}
1457 * Get the value of `networkState` from the media element. `networkState` indicates
1458 * the current network state. It returns an enumeration from the following list:
1459 * - 0: NETWORK_EMPTY
1461 * - 2: NETWORK_LOADING
1462 * - 3: NETWORK_NO_SOURCE
1464 * @method Html5#networkState
1466 * The value of `networkState` from the media element. This will be a number
1467 * from the list in the description.
1469 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate}
1474 * Get the value of `readyState` from the media element. `readyState` indicates
1475 * the current state of the media element. It returns an enumeration from the
1478 * - 1: HAVE_METADATA
1479 * - 2: HAVE_CURRENT_DATA
1480 * - 3: HAVE_FUTURE_DATA
1481 * - 4: HAVE_ENOUGH_DATA
1483 * @method Html5#readyState
1485 * The value of `readyState` from the media element. This will be a number
1486 * from the list in the description.
1488 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states}
1493 * Get the value of `videoWidth` from the video element. `videoWidth` indicates
1494 * the current width of the video in css pixels.
1496 * @method Html5#videoWidth
1498 * The value of `videoWidth` from the video element. This will be a number
1501 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
1506 * Get the value of `videoHeight` from the video element. `videoHeigth` indicates
1507 * the current height of the video in css pixels.
1509 * @method Html5#videoHeight
1511 * The value of `videoHeight` from the video element. This will be a number
1514 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
1516 'videoHeight'].forEach(function (prop) {
1517 Html5.prototype[prop] = function () {
1518 return this.el_[prop];
1522 // Wrap native properties with a setter in this format:
1523 // set + toTitleCase(name)
1526 * Set the value of `volume` on the media element. `volume` indicates the current
1527 * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and
1530 * @method Html5#setVolume
1531 * @param {number} percentAsDecimal
1532 * The volume percent as a decimal. Valid range is from 0-1.
1534 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
1539 * Set the value of `muted` on the media element. `muted` indicates the current
1540 * audio level should be silent.
1542 * @method Html5#setMuted
1543 * @param {boolean} muted
1544 * - True if the audio should be set to silent
1547 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
1552 * Set the value of `src` on the media element. `src` indicates the current
1553 * {@link Tech~SourceObject} for the media.
1555 * @method Html5#setSrc
1556 * @param {Tech~SourceObject} src
1557 * The source object to set as the current source.
1559 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src}
1564 * Set the value of `poster` on the media element. `poster` is the url to
1565 * an image file that can/will be shown when no media data is available.
1567 * @method Html5#setPoster
1568 * @param {string} poster
1569 * The url to an image that should be used as the `poster` for the media
1572 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster}
1577 * Set the value of `preload` on the media element. `preload` indicates
1578 * what should download before the media is interacted with. It can have the following
1580 * - none: nothing should be downloaded
1581 * - metadata: poster and the first few frames of the media may be downloaded to get
1582 * media dimensions and other metadata
1583 * - auto: allow the media and metadata for the media to be downloaded before
1586 * @method Html5#setPreload
1587 * @param {string} preload
1588 * The value of `preload` to set on the media element. Must be 'none', 'metadata',
1591 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
1596 * Set the value of `autoplay` on the media element. `autoplay` indicates
1597 * that the media should start to play as soon as the page is ready.
1599 * @method Html5#setAutoplay
1600 * @param {boolean} autoplay
1601 * - True indicates that the media should start as soon as the page loads.
1602 * - False indicates that the media should not start as soon as the page loads.
1604 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
1609 * Set the value of `loop` on the media element. `loop` indicates
1610 * that the media should return to the start of the media and continue playing once
1611 * it reaches the end.
1613 * @method Html5#setLoop
1614 * @param {boolean} loop
1615 * - True indicates that playback should seek back to start once
1616 * the end of a media is reached.
1617 * - False indicates that playback should not loop back to the start when the
1618 * end of the media is reached.
1620 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
1625 * Set the value of `playbackRate` on the media element. `playbackRate` indicates
1626 * the rate at which the media should play back. Examples:
1627 * - if playbackRate is set to 2, media will play twice as fast.
1628 * - if playbackRate is set to 0.5, media will play half as fast.
1630 * @method Html5#setPlaybackRate
1632 * The value of `playbackRate` from the media element. A number indicating
1633 * the current playback speed of the media, where 1 is normal speed.
1635 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
1637 'playbackRate'].forEach(function (prop) {
1638 Html5.prototype['set' + (0, _toTitleCase2['default'])(prop)] = function (v) {
1643 // wrap native functions with a function
1646 * A wrapper around the media elements `pause` function. This will call the `HTML5`
1647 * media elements `pause` function.
1649 * @method Html5#pause
1650 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause}
1655 * A wrapper around the media elements `load` function. This will call the `HTML5`s
1656 * media element `load` function.
1658 * @method Html5#load
1659 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load}
1661 'load'].forEach(function (prop) {
1662 Html5.prototype[prop] = function () {
1663 return this.el_[prop]();
1667 _tech2['default'].withSourceHandlers(Html5);
1670 * Native source handler for Html5, simply passes the source to the media element.
1672 * @proprety {Tech~SourceObject} source
1675 * @proprety {Html5} tech
1676 * The instance of the HTML5 tech.
1678 Html5.nativeSourceHandler = {};
1681 * Check if the media element can play the given mime type.
1683 * @param {string} type
1684 * The mimetype to check
1687 * 'probably', 'maybe', or '' (empty string)
1689 Html5.nativeSourceHandler.canPlayType = function (type) {
1690 // IE9 on Windows 7 without MediaPlayer throws an error here
1691 // https://github.com/videojs/video.js/issues/519
1693 return Html5.TEST_VID.canPlayType(type);
1700 * Check if the media element can handle a source natively.
1702 * @param {Tech~SourceObject} source
1705 * @param {Object} [options]
1706 * Options to be passed to the tech.
1709 * 'probably', 'maybe', or '' (empty string).
1711 Html5.nativeSourceHandler.canHandleSource = function (source, options) {
1713 // If a type was provided we should rely on that
1715 return Html5.nativeSourceHandler.canPlayType(source.type);
1717 // If no type, fall back to checking 'video/[EXTENSION]'
1718 } else if (source.src) {
1719 var ext = Url.getFileExtension(source.src);
1721 return Html5.nativeSourceHandler.canPlayType('video/' + ext);
1728 * Pass the source to the native media element.
1730 * @param {Tech~SourceObject} source
1733 * @param {Html5} tech
1734 * The instance of the Html5 tech
1736 * @param {Object} [options]
1737 * The options to pass to the source
1739 Html5.nativeSourceHandler.handleSource = function (source, tech, options) {
1740 tech.setSrc(source.src);
1744 * A noop for the native dispose function, as cleanup is not needed.
1746 Html5.nativeSourceHandler.dispose = function () {};
1748 // Register the native source handler
1749 Html5.registerSourceHandler(Html5.nativeSourceHandler);
1751 _component2['default'].registerComponent('Html5', Html5);
1752 _tech2['default'].registerTech('Html5', Html5);
1753 exports['default'] = Html5;