UNPKG

26.7 kBJavaScriptView Raw
1/**
2 * @file videojs-contrib-hls.js
3 *
4 * The main file for the HLS project.
5 * License: https://github.com/videojs/videojs-contrib-hls/blob/master/LICENSE
6 */
7'use strict';
8
9var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
10
11var _get = function get(_x4, _x5, _x6) { var _again = true; _function: while (_again) { var object = _x4, property = _x5, receiver = _x6; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x4 = parent; _x5 = property; _x6 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
12
13function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
14
15function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
16
17function _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; }
18
19var _globalDocument = require('global/document');
20
21var _globalDocument2 = _interopRequireDefault(_globalDocument);
22
23var _playlistLoader = require('./playlist-loader');
24
25var _playlistLoader2 = _interopRequireDefault(_playlistLoader);
26
27var _playlist = require('./playlist');
28
29var _playlist2 = _interopRequireDefault(_playlist);
30
31var _xhr = require('./xhr');
32
33var _xhr2 = _interopRequireDefault(_xhr);
34
35var _aesDecrypter = require('aes-decrypter');
36
37var _binUtils = require('./bin-utils');
38
39var _binUtils2 = _interopRequireDefault(_binUtils);
40
41var _videojsContribMediaSources = require('videojs-contrib-media-sources');
42
43var _m3u8Parser = require('m3u8-parser');
44
45var _m3u8Parser2 = _interopRequireDefault(_m3u8Parser);
46
47var _videoJs = require('video.js');
48
49var _videoJs2 = _interopRequireDefault(_videoJs);
50
51var _masterPlaylistController = require('./master-playlist-controller');
52
53var _config = require('./config');
54
55var _config2 = _interopRequireDefault(_config);
56
57var _renditionMixin = require('./rendition-mixin');
58
59var _renditionMixin2 = _interopRequireDefault(_renditionMixin);
60
61var _globalWindow = require('global/window');
62
63var _globalWindow2 = _interopRequireDefault(_globalWindow);
64
65var _playbackWatcher = require('./playback-watcher');
66
67var _playbackWatcher2 = _interopRequireDefault(_playbackWatcher);
68
69var _reloadSourceOnError = require('./reload-source-on-error');
70
71var _reloadSourceOnError2 = _interopRequireDefault(_reloadSourceOnError);
72
73var _playlistSelectorsJs = require('./playlist-selectors.js');
74
75// 0.5 MB/s
76var INITIAL_BANDWIDTH_DESKTOP = 4194304;
77// 0.0625 MB/s
78var INITIAL_BANDWIDTH_MOBILE = 500000;
79
80var Hls = {
81 PlaylistLoader: _playlistLoader2['default'],
82 Playlist: _playlist2['default'],
83 Decrypter: _aesDecrypter.Decrypter,
84 AsyncStream: _aesDecrypter.AsyncStream,
85 decrypt: _aesDecrypter.decrypt,
86 utils: _binUtils2['default'],
87
88 STANDARD_PLAYLIST_SELECTOR: _playlistSelectorsJs.lastBandwidthSelector,
89 comparePlaylistBandwidth: _playlistSelectorsJs.comparePlaylistBandwidth,
90 comparePlaylistResolution: _playlistSelectorsJs.comparePlaylistResolution,
91
92 xhr: (0, _xhr2['default'])()
93};
94
95// Define getter/setters for config properites
96['GOAL_BUFFER_LENGTH', 'MAX_GOAL_BUFFER_LENGTH', 'GOAL_BUFFER_LENGTH_RATE', 'BUFFER_LOW_WATER_LINE', 'MAX_BUFFER_LOW_WATER_LINE', 'BUFFER_LOW_WATER_LINE_RATE', 'BANDWIDTH_VARIANCE'].forEach(function (prop) {
97 Object.defineProperty(Hls, prop, {
98 get: function get() {
99 _videoJs2['default'].log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing');
100 return _config2['default'][prop];
101 },
102 set: function set(value) {
103 _videoJs2['default'].log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing');
104
105 if (typeof value !== 'number' || value < 0) {
106 _videoJs2['default'].log.warn('value of Hls.' + prop + ' must be greater than or equal to 0');
107 return;
108 }
109
110 _config2['default'][prop] = value;
111 }
112 });
113});
114
115/**
116 * Updates the selectedIndex of the QualityLevelList when a mediachange happens in hls.
117 *
118 * @param {QualityLevelList} qualityLevels The QualityLevelList to update.
119 * @param {PlaylistLoader} playlistLoader PlaylistLoader containing the new media info.
120 * @function handleHlsMediaChange
121 */
122var handleHlsMediaChange = function handleHlsMediaChange(qualityLevels, playlistLoader) {
123 var newPlaylist = playlistLoader.media();
124 var selectedIndex = -1;
125
126 for (var i = 0; i < qualityLevels.length; i++) {
127 if (qualityLevels[i].id === newPlaylist.uri) {
128 selectedIndex = i;
129 break;
130 }
131 }
132
133 qualityLevels.selectedIndex_ = selectedIndex;
134 qualityLevels.trigger({
135 selectedIndex: selectedIndex,
136 type: 'change'
137 });
138};
139
140/**
141 * Adds quality levels to list once playlist metadata is available
142 *
143 * @param {QualityLevelList} qualityLevels The QualityLevelList to attach events to.
144 * @param {Object} hls Hls object to listen to for media events.
145 * @function handleHlsLoadedMetadata
146 */
147var handleHlsLoadedMetadata = function handleHlsLoadedMetadata(qualityLevels, hls) {
148 hls.representations().forEach(function (rep) {
149 qualityLevels.addQualityLevel(rep);
150 });
151 handleHlsMediaChange(qualityLevels, hls.playlists);
152};
153
154// HLS is a source handler, not a tech. Make sure attempts to use it
155// as one do not cause exceptions.
156Hls.canPlaySource = function () {
157 return _videoJs2['default'].log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
158};
159
160/**
161 * Whether the browser has built-in HLS support.
162 */
163Hls.supportsNativeHls = (function () {
164 var video = _globalDocument2['default'].createElement('video');
165
166 // native HLS is definitely not supported if HTML5 video isn't
167 if (!_videoJs2['default'].getTech('Html5').isSupported()) {
168 return false;
169 }
170
171 // HLS manifests can go by many mime-types
172 var canPlay = [
173 // Apple santioned
174 'application/vnd.apple.mpegurl',
175 // Apple sanctioned for backwards compatibility
176 'audio/mpegurl',
177 // Very common
178 'audio/x-mpegurl',
179 // Very common
180 'application/x-mpegurl',
181 // Included for completeness
182 'video/x-mpegurl', 'video/mpegurl', 'application/mpegurl'];
183
184 return canPlay.some(function (canItPlay) {
185 return (/maybe|probably/i.test(video.canPlayType(canItPlay))
186 );
187 });
188})();
189
190/**
191 * HLS is a source handler, not a tech. Make sure attempts to use it
192 * as one do not cause exceptions.
193 */
194Hls.isSupported = function () {
195 return _videoJs2['default'].log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
196};
197
198var USER_AGENT = _globalWindow2['default'].navigator && _globalWindow2['default'].navigator.userAgent || '';
199
200/**
201 * Determines whether the browser supports a change in the audio configuration
202 * during playback. Currently only Firefox 48 and below do not support this.
203 * window.isSecureContext is a propterty that was added to window in firefox 49,
204 * so we can use it to detect Firefox 49+.
205 *
206 * @return {Boolean} Whether the browser supports audio config change during playback
207 */
208Hls.supportsAudioInfoChange_ = function () {
209 if (_videoJs2['default'].browser.IS_FIREFOX) {
210 var firefoxVersionMap = /Firefox\/([\d.]+)/i.exec(USER_AGENT);
211 var version = parseInt(firefoxVersionMap[1], 10);
212
213 return version >= 49;
214 }
215 return true;
216};
217
218var Component = _videoJs2['default'].getComponent('Component');
219
220/**
221 * The Hls Handler object, where we orchestrate all of the parts
222 * of HLS to interact with video.js
223 *
224 * @class HlsHandler
225 * @extends videojs.Component
226 * @param {Object} source the soruce object
227 * @param {Tech} tech the parent tech object
228 * @param {Object} options optional and required options
229 */
230
231var HlsHandler = (function (_Component) {
232 _inherits(HlsHandler, _Component);
233
234 function HlsHandler(source, tech, options) {
235 var _this = this;
236
237 _classCallCheck(this, HlsHandler);
238
239 _get(Object.getPrototypeOf(HlsHandler.prototype), 'constructor', this).call(this, tech, options.hls);
240
241 // tech.player() is deprecated but setup a reference to HLS for
242 // backwards-compatibility
243 if (tech.options_ && tech.options_.playerId) {
244 var _player = (0, _videoJs2['default'])(tech.options_.playerId);
245
246 if (!_player.hasOwnProperty('hls')) {
247 Object.defineProperty(_player, 'hls', {
248 get: function get() {
249 _videoJs2['default'].log.warn('player.hls is deprecated. Use player.tech_.hls instead.');
250 tech.trigger({ type: 'usage', name: 'hls-player-access' });
251 return _this;
252 }
253 });
254 }
255 }
256
257 this.tech_ = tech;
258 this.source_ = source;
259 this.stats = {};
260 this.ignoreNextSeekingEvent_ = false;
261 this.setOptions_();
262
263 // overriding native HLS only works if audio tracks have been emulated
264 // error early if we're misconfigured:
265 if (this.options_.overrideNative && (tech.featuresNativeVideoTracks || tech.featuresNativeAudioTracks)) {
266 throw new Error('Overriding native HLS requires emulated tracks. ' + 'See https://git.io/vMpjB');
267 }
268
269 // listen for fullscreenchange events for this player so that we
270 // can adjust our quality selection quickly
271 this.on(_globalDocument2['default'], ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'], function (event) {
272 var fullscreenElement = _globalDocument2['default'].fullscreenElement || _globalDocument2['default'].webkitFullscreenElement || _globalDocument2['default'].mozFullScreenElement || _globalDocument2['default'].msFullscreenElement;
273
274 if (fullscreenElement && fullscreenElement.contains(_this.tech_.el())) {
275 _this.masterPlaylistController_.fastQualityChange_();
276 }
277 });
278
279 this.on(this.tech_, 'seeking', function () {
280 if (this.ignoreNextSeekingEvent_) {
281 this.ignoreNextSeekingEvent_ = false;
282 return;
283 }
284
285 this.setCurrentTime(this.tech_.currentTime());
286 });
287 this.on(this.tech_, 'error', function () {
288 if (this.masterPlaylistController_) {
289 this.masterPlaylistController_.pauseLoading();
290 }
291 });
292
293 this.audioTrackChange_ = function () {
294 _this.masterPlaylistController_.setupAudio();
295 _this.tech_.trigger({ type: 'usage', name: 'hls-audio-change' });
296 };
297
298 this.textTrackChange_ = function () {
299 _this.masterPlaylistController_.setupSubtitles();
300 };
301
302 this.on(this.tech_, 'play', this.play);
303 }
304
305 /**
306 * The Source Handler object, which informs video.js what additional
307 * MIME types are supported and sets up playback. It is registered
308 * automatically to the appropriate tech based on the capabilities of
309 * the browser it is running in. It is not necessary to use or modify
310 * this object in normal usage.
311 */
312
313 _createClass(HlsHandler, [{
314 key: 'setOptions_',
315 value: function setOptions_() {
316 var _this2 = this;
317
318 // defaults
319 this.options_.withCredentials = this.options_.withCredentials || false;
320
321 if (typeof this.options_.blacklistDuration !== 'number') {
322 this.options_.blacklistDuration = 5 * 60;
323 }
324
325 // start playlist selection at a reasonable bandwidth for
326 // broadband internet (0.5 MB/s) or mobile (0.0625 MB/s)
327 if (typeof this.options_.bandwidth !== 'number') {
328 // only use Android for mobile because iOS does not support MSE (and uses
329 // native HLS)
330 this.options_.bandwidth = _videoJs2['default'].browser.IS_ANDROID ? INITIAL_BANDWIDTH_MOBILE : INITIAL_BANDWIDTH_DESKTOP;
331 }
332
333 // grab options passed to player.src
334 ['withCredentials', 'bandwidth'].forEach(function (option) {
335 if (typeof _this2.source_[option] !== 'undefined') {
336 _this2.options_[option] = _this2.source_[option];
337 }
338 });
339
340 this.bandwidth = this.options_.bandwidth;
341 }
342
343 /**
344 * called when player.src gets called, handle a new source
345 *
346 * @param {Object} src the source object to handle
347 */
348 }, {
349 key: 'src',
350 value: function src(_src) {
351 var _this3 = this;
352
353 // do nothing if the src is falsey
354 if (!_src) {
355 return;
356 }
357 this.setOptions_();
358 // add master playlist controller options
359 this.options_.url = this.source_.src;
360 this.options_.tech = this.tech_;
361 this.options_.externHls = Hls;
362
363 this.masterPlaylistController_ = new _masterPlaylistController.MasterPlaylistController(this.options_);
364 this.playbackWatcher_ = new _playbackWatcher2['default'](_videoJs2['default'].mergeOptions(this.options_, {
365 seekable: function seekable() {
366 return _this3.seekable();
367 }
368 }));
369
370 this.masterPlaylistController_.on('error', function () {
371 var player = _videoJs2['default'].players[_this3.tech_.options_.playerId];
372
373 player.error(_this3.masterPlaylistController_.error);
374 });
375
376 // `this` in selectPlaylist should be the HlsHandler for backwards
377 // compatibility with < v2
378 this.masterPlaylistController_.selectPlaylist = this.selectPlaylist ? this.selectPlaylist.bind(this) : Hls.STANDARD_PLAYLIST_SELECTOR.bind(this);
379
380 // re-expose some internal objects for backwards compatibility with < v2
381 this.playlists = this.masterPlaylistController_.masterPlaylistLoader_;
382 this.mediaSource = this.masterPlaylistController_.mediaSource;
383
384 // Proxy assignment of some properties to the master playlist
385 // controller. Using a custom property for backwards compatibility
386 // with < v2
387 Object.defineProperties(this, {
388 selectPlaylist: {
389 get: function get() {
390 return this.masterPlaylistController_.selectPlaylist;
391 },
392 set: function set(selectPlaylist) {
393 this.masterPlaylistController_.selectPlaylist = selectPlaylist.bind(this);
394 }
395 },
396 throughput: {
397 get: function get() {
398 return this.masterPlaylistController_.mainSegmentLoader_.throughput.rate;
399 },
400 set: function set(throughput) {
401 this.masterPlaylistController_.mainSegmentLoader_.throughput.rate = throughput;
402 // By setting `count` to 1 the throughput value becomes the starting value
403 // for the cumulative average
404 this.masterPlaylistController_.mainSegmentLoader_.throughput.count = 1;
405 }
406 },
407 bandwidth: {
408 get: function get() {
409 return this.masterPlaylistController_.mainSegmentLoader_.bandwidth;
410 },
411 set: function set(bandwidth) {
412 this.masterPlaylistController_.mainSegmentLoader_.bandwidth = bandwidth;
413 // setting the bandwidth manually resets the throughput counter
414 // `count` is set to zero that current value of `rate` isn't included
415 // in the cumulative average
416 this.masterPlaylistController_.mainSegmentLoader_.throughput = {
417 rate: 0,
418 count: 0
419 };
420 }
421 },
422 /**
423 * `systemBandwidth` is a combination of two serial processes bit-rates. The first
424 * is the network bitrate provided by `bandwidth` and the second is the bitrate of
425 * the entire process after that - decryption, transmuxing, and appending - provided
426 * by `throughput`.
427 *
428 * Since the two process are serial, the overall system bandwidth is given by:
429 * sysBandwidth = 1 / (1 / bandwidth + 1 / throughput)
430 */
431 systemBandwidth: {
432 get: function get() {
433 var invBandwidth = 1 / (this.bandwidth || 1);
434 var invThroughput = undefined;
435
436 if (this.throughput > 0) {
437 invThroughput = 1 / this.throughput;
438 } else {
439 invThroughput = 0;
440 }
441
442 var systemBitrate = Math.floor(1 / (invBandwidth + invThroughput));
443
444 return systemBitrate;
445 },
446 set: function set() {
447 _videoJs2['default'].log.error('The "systemBandwidth" property is read-only');
448 }
449 }
450 });
451
452 Object.defineProperties(this.stats, {
453 bandwidth: {
454 get: function get() {
455 return _this3.bandwidth || 0;
456 },
457 enumerable: true
458 },
459 mediaRequests: {
460 get: function get() {
461 return _this3.masterPlaylistController_.mediaRequests_() || 0;
462 },
463 enumerable: true
464 },
465 mediaRequestsAborted: {
466 get: function get() {
467 return _this3.masterPlaylistController_.mediaRequestsAborted_() || 0;
468 },
469 enumerable: true
470 },
471 mediaRequestsTimedout: {
472 get: function get() {
473 return _this3.masterPlaylistController_.mediaRequestsTimedout_() || 0;
474 },
475 enumerable: true
476 },
477 mediaRequestsErrored: {
478 get: function get() {
479 return _this3.masterPlaylistController_.mediaRequestsErrored_() || 0;
480 },
481 enumerable: true
482 },
483 mediaTransferDuration: {
484 get: function get() {
485 return _this3.masterPlaylistController_.mediaTransferDuration_() || 0;
486 },
487 enumerable: true
488 },
489 mediaBytesTransferred: {
490 get: function get() {
491 return _this3.masterPlaylistController_.mediaBytesTransferred_() || 0;
492 },
493 enumerable: true
494 },
495 mediaSecondsLoaded: {
496 get: function get() {
497 return _this3.masterPlaylistController_.mediaSecondsLoaded_() || 0;
498 },
499 enumerable: true
500 }
501 });
502
503 this.tech_.one('canplay', this.masterPlaylistController_.setupFirstPlay.bind(this.masterPlaylistController_));
504
505 this.masterPlaylistController_.on('sourceopen', function () {
506 _this3.tech_.audioTracks().addEventListener('change', _this3.audioTrackChange_);
507 _this3.tech_.remoteTextTracks().addEventListener('change', _this3.textTrackChange_);
508 });
509
510 this.masterPlaylistController_.on('selectedinitialmedia', function () {
511 // Add the manual rendition mix-in to HlsHandler
512 (0, _renditionMixin2['default'])(_this3);
513 });
514
515 this.masterPlaylistController_.on('audioupdate', function () {
516 // clear current audioTracks
517 _this3.tech_.clearTracks('audio');
518 _this3.masterPlaylistController_.activeAudioGroup().forEach(function (audioTrack) {
519 _this3.tech_.audioTracks().addTrack(audioTrack);
520 });
521 });
522
523 // the bandwidth of the primary segment loader is our best
524 // estimate of overall bandwidth
525 this.on(this.masterPlaylistController_, 'progress', function () {
526 this.tech_.trigger('progress');
527 });
528
529 // In the live case, we need to ignore the very first `seeking` event since
530 // that will be the result of the seek-to-live behavior
531 this.on(this.masterPlaylistController_, 'firstplay', function () {
532 this.ignoreNextSeekingEvent_ = true;
533 });
534
535 this.tech_.ready(function () {
536 return _this3.setupQualityLevels_();
537 });
538
539 // do nothing if the tech has been disposed already
540 // this can occur if someone sets the src in player.ready(), for instance
541 if (!this.tech_.el()) {
542 return;
543 }
544
545 this.tech_.src(_videoJs2['default'].URL.createObjectURL(this.masterPlaylistController_.mediaSource));
546 }
547
548 /**
549 * Initializes the quality levels and sets listeners to update them.
550 *
551 * @method setupQualityLevels_
552 * @private
553 */
554 }, {
555 key: 'setupQualityLevels_',
556 value: function setupQualityLevels_() {
557 var _this4 = this;
558
559 var player = _videoJs2['default'].players[this.tech_.options_.playerId];
560
561 if (player && player.qualityLevels) {
562 this.qualityLevels_ = player.qualityLevels();
563
564 this.masterPlaylistController_.on('selectedinitialmedia', function () {
565 handleHlsLoadedMetadata(_this4.qualityLevels_, _this4);
566 });
567
568 this.playlists.on('mediachange', function () {
569 handleHlsMediaChange(_this4.qualityLevels_, _this4.playlists);
570 });
571 }
572 }
573
574 /**
575 * a helper for grabbing the active audio group from MasterPlaylistController
576 *
577 * @private
578 */
579 }, {
580 key: 'activeAudioGroup_',
581 value: function activeAudioGroup_() {
582 return this.masterPlaylistController_.activeAudioGroup();
583 }
584
585 /**
586 * Begin playing the video.
587 */
588 }, {
589 key: 'play',
590 value: function play() {
591 this.masterPlaylistController_.play();
592 }
593
594 /**
595 * a wrapper around the function in MasterPlaylistController
596 */
597 }, {
598 key: 'setCurrentTime',
599 value: function setCurrentTime(currentTime) {
600 this.masterPlaylistController_.setCurrentTime(currentTime);
601 }
602
603 /**
604 * a wrapper around the function in MasterPlaylistController
605 */
606 }, {
607 key: 'duration',
608 value: function duration() {
609 return this.masterPlaylistController_.duration();
610 }
611
612 /**
613 * a wrapper around the function in MasterPlaylistController
614 */
615 }, {
616 key: 'seekable',
617 value: function seekable() {
618 return this.masterPlaylistController_.seekable();
619 }
620
621 /**
622 * Abort all outstanding work and cleanup.
623 */
624 }, {
625 key: 'dispose',
626 value: function dispose() {
627 if (this.playbackWatcher_) {
628 this.playbackWatcher_.dispose();
629 }
630 if (this.masterPlaylistController_) {
631 this.masterPlaylistController_.dispose();
632 }
633 if (this.qualityLevels_) {
634 this.qualityLevels_.dispose();
635 }
636 this.tech_.audioTracks().removeEventListener('change', this.audioTrackChange_);
637 this.tech_.remoteTextTracks().removeEventListener('change', this.textTrackChange_);
638 _get(Object.getPrototypeOf(HlsHandler.prototype), 'dispose', this).call(this);
639 }
640 }]);
641
642 return HlsHandler;
643})(Component);
644
645var HlsSourceHandler = function HlsSourceHandler(mode) {
646 return {
647 canHandleSource: function canHandleSource(srcObj) {
648 var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
649
650 var localOptions = _videoJs2['default'].mergeOptions(_videoJs2['default'].options, options);
651
652 // this forces video.js to skip this tech/mode if its not the one we have been
653 // overriden to use, by returing that we cannot handle the source.
654 if (localOptions.hls && localOptions.hls.mode && localOptions.hls.mode !== mode) {
655 return false;
656 }
657 return HlsSourceHandler.canPlayType(srcObj.type, localOptions);
658 },
659 handleSource: function handleSource(source, tech) {
660 var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
661
662 var localOptions = _videoJs2['default'].mergeOptions(_videoJs2['default'].options, options, { hls: { mode: mode } });
663
664 if (mode === 'flash') {
665 // We need to trigger this asynchronously to give others the chance
666 // to bind to the event when a source is set at player creation
667 tech.setTimeout(function () {
668 tech.trigger('loadstart');
669 }, 1);
670 }
671
672 tech.hls = new HlsHandler(source, tech, localOptions);
673 tech.hls.xhr = (0, _xhr2['default'])();
674
675 tech.hls.src(source.src);
676 return tech.hls;
677 },
678 canPlayType: function canPlayType(type) {
679 var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
680
681 var localOptions = _videoJs2['default'].mergeOptions(_videoJs2['default'].options, options);
682
683 if (HlsSourceHandler.canPlayType(type, localOptions)) {
684 return 'maybe';
685 }
686 return '';
687 }
688 };
689};
690
691HlsSourceHandler.canPlayType = function (type, options) {
692 // No support for IE 10 or below
693 if (_videoJs2['default'].browser.IE_VERSION && _videoJs2['default'].browser.IE_VERSION <= 10) {
694 return false;
695 }
696
697 var mpegurlRE = /^(audio|video|application)\/(x-|vnd\.apple\.)?mpegurl/i;
698
699 // favor native HLS support if it's available
700 if (!options.hls.overrideNative && Hls.supportsNativeHls) {
701 return false;
702 }
703 return mpegurlRE.test(type);
704};
705
706if (typeof _videoJs2['default'].MediaSource === 'undefined' || typeof _videoJs2['default'].URL === 'undefined') {
707 _videoJs2['default'].MediaSource = _videojsContribMediaSources.MediaSource;
708 _videoJs2['default'].URL = _videojsContribMediaSources.URL;
709}
710
711var flashTech = _videoJs2['default'].getTech('Flash');
712
713// register source handlers with the appropriate techs
714if (_videojsContribMediaSources.MediaSource.supportsNativeMediaSources()) {
715 _videoJs2['default'].getTech('Html5').registerSourceHandler(HlsSourceHandler('html5'), 0);
716}
717if (_globalWindow2['default'].Uint8Array && flashTech) {
718 flashTech.registerSourceHandler(HlsSourceHandler('flash'));
719}
720
721_videoJs2['default'].HlsHandler = HlsHandler;
722_videoJs2['default'].HlsSourceHandler = HlsSourceHandler;
723_videoJs2['default'].Hls = Hls;
724if (!_videoJs2['default'].use) {
725 _videoJs2['default'].registerComponent('Hls', Hls);
726}
727_videoJs2['default'].m3u8 = _m3u8Parser2['default'];
728_videoJs2['default'].options.hls = _videoJs2['default'].options.hls || {};
729
730if (_videoJs2['default'].registerPlugin) {
731 _videoJs2['default'].registerPlugin('reloadSourceOnError', _reloadSourceOnError2['default']);
732} else {
733 _videoJs2['default'].plugin('reloadSourceOnError', _reloadSourceOnError2['default']);
734}
735
736module.exports = {
737 Hls: Hls,
738 HlsHandler: HlsHandler,
739 HlsSourceHandler: HlsSourceHandler
740};
\No newline at end of file