videojs-contrib-hls.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  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. var _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; }; })();
  9. var _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); } } };
  10. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  11. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  12. 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; }
  13. var _globalDocument = require('global/document');
  14. var _globalDocument2 = _interopRequireDefault(_globalDocument);
  15. var _playlistLoader = require('./playlist-loader');
  16. var _playlistLoader2 = _interopRequireDefault(_playlistLoader);
  17. var _playlist = require('./playlist');
  18. var _playlist2 = _interopRequireDefault(_playlist);
  19. var _xhr = require('./xhr');
  20. var _xhr2 = _interopRequireDefault(_xhr);
  21. var _aesDecrypter = require('aes-decrypter');
  22. var _binUtils = require('./bin-utils');
  23. var _binUtils2 = _interopRequireDefault(_binUtils);
  24. var _videojsContribMediaSources = require('videojs-contrib-media-sources');
  25. var _m3u8Parser = require('m3u8-parser');
  26. var _m3u8Parser2 = _interopRequireDefault(_m3u8Parser);
  27. var _videoJs = require('video.js');
  28. var _videoJs2 = _interopRequireDefault(_videoJs);
  29. var _masterPlaylistController = require('./master-playlist-controller');
  30. var _config = require('./config');
  31. var _config2 = _interopRequireDefault(_config);
  32. var _renditionMixin = require('./rendition-mixin');
  33. var _renditionMixin2 = _interopRequireDefault(_renditionMixin);
  34. var _globalWindow = require('global/window');
  35. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  36. var _playbackWatcher = require('./playback-watcher');
  37. var _playbackWatcher2 = _interopRequireDefault(_playbackWatcher);
  38. var _reloadSourceOnError = require('./reload-source-on-error');
  39. var _reloadSourceOnError2 = _interopRequireDefault(_reloadSourceOnError);
  40. var _playlistSelectorsJs = require('./playlist-selectors.js');
  41. var Hls = {
  42. PlaylistLoader: _playlistLoader2['default'],
  43. Playlist: _playlist2['default'],
  44. Decrypter: _aesDecrypter.Decrypter,
  45. AsyncStream: _aesDecrypter.AsyncStream,
  46. decrypt: _aesDecrypter.decrypt,
  47. utils: _binUtils2['default'],
  48. STANDARD_PLAYLIST_SELECTOR: _playlistSelectorsJs.lastBandwidthSelector,
  49. INITIAL_PLAYLIST_SELECTOR: _playlistSelectorsJs.lowestBitrateCompatibleVariantSelector,
  50. comparePlaylistBandwidth: _playlistSelectorsJs.comparePlaylistBandwidth,
  51. comparePlaylistResolution: _playlistSelectorsJs.comparePlaylistResolution,
  52. xhr: (0, _xhr2['default'])()
  53. };
  54. // 0.5 MB/s
  55. var INITIAL_BANDWIDTH = 4194304;
  56. // Define getter/setters for config properites
  57. ['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) {
  58. Object.defineProperty(Hls, prop, {
  59. get: function get() {
  60. _videoJs2['default'].log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing');
  61. return _config2['default'][prop];
  62. },
  63. set: function set(value) {
  64. _videoJs2['default'].log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing');
  65. if (typeof value !== 'number' || value < 0) {
  66. _videoJs2['default'].log.warn('value of Hls.' + prop + ' must be greater than or equal to 0');
  67. return;
  68. }
  69. _config2['default'][prop] = value;
  70. }
  71. });
  72. });
  73. /**
  74. * Updates the selectedIndex of the QualityLevelList when a mediachange happens in hls.
  75. *
  76. * @param {QualityLevelList} qualityLevels The QualityLevelList to update.
  77. * @param {PlaylistLoader} playlistLoader PlaylistLoader containing the new media info.
  78. * @function handleHlsMediaChange
  79. */
  80. var handleHlsMediaChange = function handleHlsMediaChange(qualityLevels, playlistLoader) {
  81. var newPlaylist = playlistLoader.media();
  82. var selectedIndex = -1;
  83. for (var i = 0; i < qualityLevels.length; i++) {
  84. if (qualityLevels[i].id === newPlaylist.uri) {
  85. selectedIndex = i;
  86. break;
  87. }
  88. }
  89. qualityLevels.selectedIndex_ = selectedIndex;
  90. qualityLevels.trigger({
  91. selectedIndex: selectedIndex,
  92. type: 'change'
  93. });
  94. };
  95. /**
  96. * Adds quality levels to list once playlist metadata is available
  97. *
  98. * @param {QualityLevelList} qualityLevels The QualityLevelList to attach events to.
  99. * @param {Object} hls Hls object to listen to for media events.
  100. * @function handleHlsLoadedMetadata
  101. */
  102. var handleHlsLoadedMetadata = function handleHlsLoadedMetadata(qualityLevels, hls) {
  103. hls.representations().forEach(function (rep) {
  104. qualityLevels.addQualityLevel(rep);
  105. });
  106. handleHlsMediaChange(qualityLevels, hls.playlists);
  107. };
  108. // HLS is a source handler, not a tech. Make sure attempts to use it
  109. // as one do not cause exceptions.
  110. Hls.canPlaySource = function () {
  111. return _videoJs2['default'].log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
  112. };
  113. /**
  114. * Whether the browser has built-in HLS support.
  115. */
  116. Hls.supportsNativeHls = (function () {
  117. var video = _globalDocument2['default'].createElement('video');
  118. // native HLS is definitely not supported if HTML5 video isn't
  119. if (!_videoJs2['default'].getTech('Html5').isSupported()) {
  120. return false;
  121. }
  122. // HLS manifests can go by many mime-types
  123. var canPlay = [
  124. // Apple santioned
  125. 'application/vnd.apple.mpegurl',
  126. // Apple sanctioned for backwards compatibility
  127. 'audio/mpegurl',
  128. // Very common
  129. 'audio/x-mpegurl',
  130. // Very common
  131. 'application/x-mpegurl',
  132. // Included for completeness
  133. 'video/x-mpegurl', 'video/mpegurl', 'application/mpegurl'];
  134. return canPlay.some(function (canItPlay) {
  135. return (/maybe|probably/i.test(video.canPlayType(canItPlay))
  136. );
  137. });
  138. })();
  139. /**
  140. * HLS is a source handler, not a tech. Make sure attempts to use it
  141. * as one do not cause exceptions.
  142. */
  143. Hls.isSupported = function () {
  144. return _videoJs2['default'].log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
  145. };
  146. var Component = _videoJs2['default'].getComponent('Component');
  147. /**
  148. * The Hls Handler object, where we orchestrate all of the parts
  149. * of HLS to interact with video.js
  150. *
  151. * @class HlsHandler
  152. * @extends videojs.Component
  153. * @param {Object} source the soruce object
  154. * @param {Tech} tech the parent tech object
  155. * @param {Object} options optional and required options
  156. */
  157. var HlsHandler = (function (_Component) {
  158. _inherits(HlsHandler, _Component);
  159. function HlsHandler(source, tech, options) {
  160. var _this = this;
  161. _classCallCheck(this, HlsHandler);
  162. _get(Object.getPrototypeOf(HlsHandler.prototype), 'constructor', this).call(this, tech, options.hls);
  163. // tech.player() is deprecated but setup a reference to HLS for
  164. // backwards-compatibility
  165. if (tech.options_ && tech.options_.playerId) {
  166. var _player = (0, _videoJs2['default'])(tech.options_.playerId);
  167. if (!_player.hasOwnProperty('hls')) {
  168. Object.defineProperty(_player, 'hls', {
  169. get: function get() {
  170. _videoJs2['default'].log.warn('player.hls is deprecated. Use player.tech_.hls instead.');
  171. tech.trigger({ type: 'usage', name: 'hls-player-access' });
  172. return _this;
  173. }
  174. });
  175. }
  176. }
  177. this.tech_ = tech;
  178. this.source_ = source;
  179. this.stats = {};
  180. this.ignoreNextSeekingEvent_ = false;
  181. this.setOptions_();
  182. // overriding native HLS only works if audio tracks have been emulated
  183. // error early if we're misconfigured:
  184. if (this.options_.overrideNative && (tech.featuresNativeVideoTracks || tech.featuresNativeAudioTracks)) {
  185. throw new Error('Overriding native HLS requires emulated tracks. ' + 'See https://git.io/vMpjB');
  186. }
  187. // listen for fullscreenchange events for this player so that we
  188. // can adjust our quality selection quickly
  189. this.on(_globalDocument2['default'], ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'], function (event) {
  190. var fullscreenElement = _globalDocument2['default'].fullscreenElement || _globalDocument2['default'].webkitFullscreenElement || _globalDocument2['default'].mozFullScreenElement || _globalDocument2['default'].msFullscreenElement;
  191. if (fullscreenElement && fullscreenElement.contains(_this.tech_.el())) {
  192. _this.masterPlaylistController_.fastQualityChange_();
  193. }
  194. });
  195. this.on(this.tech_, 'seeking', function () {
  196. if (this.ignoreNextSeekingEvent_) {
  197. this.ignoreNextSeekingEvent_ = false;
  198. return;
  199. }
  200. this.setCurrentTime(this.tech_.currentTime());
  201. });
  202. this.on(this.tech_, 'error', function () {
  203. if (this.masterPlaylistController_) {
  204. this.masterPlaylistController_.pauseLoading();
  205. }
  206. });
  207. this.on(this.tech_, 'play', this.play);
  208. }
  209. /**
  210. * The Source Handler object, which informs video.js what additional
  211. * MIME types are supported and sets up playback. It is registered
  212. * automatically to the appropriate tech based on the capabilities of
  213. * the browser it is running in. It is not necessary to use or modify
  214. * this object in normal usage.
  215. */
  216. _createClass(HlsHandler, [{
  217. key: 'setOptions_',
  218. value: function setOptions_() {
  219. var _this2 = this;
  220. // defaults
  221. this.options_.withCredentials = this.options_.withCredentials || false;
  222. if (typeof this.options_.blacklistDuration !== 'number') {
  223. this.options_.blacklistDuration = 5 * 60;
  224. }
  225. // start playlist selection at a reasonable bandwidth for
  226. // broadband internet (0.5 MB/s) or mobile (0.0625 MB/s)
  227. if (typeof this.options_.bandwidth !== 'number') {
  228. this.options_.bandwidth = INITIAL_BANDWIDTH;
  229. }
  230. // If the bandwidth number is unchanged from the initial setting
  231. // then this takes precedence over the enableLowInitialPlaylist option
  232. this.options_.enableLowInitialPlaylist = this.options_.enableLowInitialPlaylist && this.options_.bandwidth === INITIAL_BANDWIDTH;
  233. // grab options passed to player.src
  234. ['withCredentials', 'bandwidth', 'handleManifestRedirects'].forEach(function (option) {
  235. if (typeof _this2.source_[option] !== 'undefined') {
  236. _this2.options_[option] = _this2.source_[option];
  237. }
  238. });
  239. this.bandwidth = this.options_.bandwidth;
  240. }
  241. /**
  242. * called when player.src gets called, handle a new source
  243. *
  244. * @param {Object} src the source object to handle
  245. */
  246. }, {
  247. key: 'src',
  248. value: function src(_src) {
  249. var _this3 = this;
  250. // do nothing if the src is falsey
  251. if (!_src) {
  252. return;
  253. }
  254. this.setOptions_();
  255. // add master playlist controller options
  256. this.options_.url = this.source_.src;
  257. this.options_.tech = this.tech_;
  258. this.options_.externHls = Hls;
  259. this.masterPlaylistController_ = new _masterPlaylistController.MasterPlaylistController(this.options_);
  260. this.playbackWatcher_ = new _playbackWatcher2['default'](_videoJs2['default'].mergeOptions(this.options_, {
  261. seekable: function seekable() {
  262. return _this3.seekable();
  263. }
  264. }));
  265. this.masterPlaylistController_.on('error', function () {
  266. var player = _videoJs2['default'].players[_this3.tech_.options_.playerId];
  267. player.error(_this3.masterPlaylistController_.error);
  268. });
  269. // `this` in selectPlaylist should be the HlsHandler for backwards
  270. // compatibility with < v2
  271. this.masterPlaylistController_.selectPlaylist = this.selectPlaylist ? this.selectPlaylist.bind(this) : Hls.STANDARD_PLAYLIST_SELECTOR.bind(this);
  272. this.masterPlaylistController_.selectInitialPlaylist = Hls.INITIAL_PLAYLIST_SELECTOR.bind(this);
  273. // re-expose some internal objects for backwards compatibility with < v2
  274. this.playlists = this.masterPlaylistController_.masterPlaylistLoader_;
  275. this.mediaSource = this.masterPlaylistController_.mediaSource;
  276. // Proxy assignment of some properties to the master playlist
  277. // controller. Using a custom property for backwards compatibility
  278. // with < v2
  279. Object.defineProperties(this, {
  280. selectPlaylist: {
  281. get: function get() {
  282. return this.masterPlaylistController_.selectPlaylist;
  283. },
  284. set: function set(selectPlaylist) {
  285. this.masterPlaylistController_.selectPlaylist = selectPlaylist.bind(this);
  286. }
  287. },
  288. throughput: {
  289. get: function get() {
  290. return this.masterPlaylistController_.mainSegmentLoader_.throughput.rate;
  291. },
  292. set: function set(throughput) {
  293. this.masterPlaylistController_.mainSegmentLoader_.throughput.rate = throughput;
  294. // By setting `count` to 1 the throughput value becomes the starting value
  295. // for the cumulative average
  296. this.masterPlaylistController_.mainSegmentLoader_.throughput.count = 1;
  297. }
  298. },
  299. bandwidth: {
  300. get: function get() {
  301. return this.masterPlaylistController_.mainSegmentLoader_.bandwidth;
  302. },
  303. set: function set(bandwidth) {
  304. this.masterPlaylistController_.mainSegmentLoader_.bandwidth = bandwidth;
  305. // setting the bandwidth manually resets the throughput counter
  306. // `count` is set to zero that current value of `rate` isn't included
  307. // in the cumulative average
  308. this.masterPlaylistController_.mainSegmentLoader_.throughput = {
  309. rate: 0,
  310. count: 0
  311. };
  312. }
  313. },
  314. /**
  315. * `systemBandwidth` is a combination of two serial processes bit-rates. The first
  316. * is the network bitrate provided by `bandwidth` and the second is the bitrate of
  317. * the entire process after that - decryption, transmuxing, and appending - provided
  318. * by `throughput`.
  319. *
  320. * Since the two process are serial, the overall system bandwidth is given by:
  321. * sysBandwidth = 1 / (1 / bandwidth + 1 / throughput)
  322. */
  323. systemBandwidth: {
  324. get: function get() {
  325. var invBandwidth = 1 / (this.bandwidth || 1);
  326. var invThroughput = undefined;
  327. if (this.throughput > 0) {
  328. invThroughput = 1 / this.throughput;
  329. } else {
  330. invThroughput = 0;
  331. }
  332. var systemBitrate = Math.floor(1 / (invBandwidth + invThroughput));
  333. return systemBitrate;
  334. },
  335. set: function set() {
  336. _videoJs2['default'].log.error('The "systemBandwidth" property is read-only');
  337. }
  338. }
  339. });
  340. Object.defineProperties(this.stats, {
  341. bandwidth: {
  342. get: function get() {
  343. return _this3.bandwidth || 0;
  344. },
  345. enumerable: true
  346. },
  347. mediaRequests: {
  348. get: function get() {
  349. return _this3.masterPlaylistController_.mediaRequests_() || 0;
  350. },
  351. enumerable: true
  352. },
  353. mediaRequestsAborted: {
  354. get: function get() {
  355. return _this3.masterPlaylistController_.mediaRequestsAborted_() || 0;
  356. },
  357. enumerable: true
  358. },
  359. mediaRequestsTimedout: {
  360. get: function get() {
  361. return _this3.masterPlaylistController_.mediaRequestsTimedout_() || 0;
  362. },
  363. enumerable: true
  364. },
  365. mediaRequestsErrored: {
  366. get: function get() {
  367. return _this3.masterPlaylistController_.mediaRequestsErrored_() || 0;
  368. },
  369. enumerable: true
  370. },
  371. mediaTransferDuration: {
  372. get: function get() {
  373. return _this3.masterPlaylistController_.mediaTransferDuration_() || 0;
  374. },
  375. enumerable: true
  376. },
  377. mediaBytesTransferred: {
  378. get: function get() {
  379. return _this3.masterPlaylistController_.mediaBytesTransferred_() || 0;
  380. },
  381. enumerable: true
  382. },
  383. mediaSecondsLoaded: {
  384. get: function get() {
  385. return _this3.masterPlaylistController_.mediaSecondsLoaded_() || 0;
  386. },
  387. enumerable: true
  388. }
  389. });
  390. this.tech_.one('canplay', this.masterPlaylistController_.setupFirstPlay.bind(this.masterPlaylistController_));
  391. this.masterPlaylistController_.on('selectedinitialmedia', function () {
  392. // Add the manual rendition mix-in to HlsHandler
  393. (0, _renditionMixin2['default'])(_this3);
  394. });
  395. // the bandwidth of the primary segment loader is our best
  396. // estimate of overall bandwidth
  397. this.on(this.masterPlaylistController_, 'progress', function () {
  398. this.tech_.trigger('progress');
  399. });
  400. // In the live case, we need to ignore the very first `seeking` event since
  401. // that will be the result of the seek-to-live behavior
  402. this.on(this.masterPlaylistController_, 'firstplay', function () {
  403. this.ignoreNextSeekingEvent_ = true;
  404. });
  405. this.tech_.ready(function () {
  406. return _this3.setupQualityLevels_();
  407. });
  408. // do nothing if the tech has been disposed already
  409. // this can occur if someone sets the src in player.ready(), for instance
  410. if (!this.tech_.el()) {
  411. return;
  412. }
  413. this.tech_.src(_videoJs2['default'].URL.createObjectURL(this.masterPlaylistController_.mediaSource));
  414. }
  415. /**
  416. * Initializes the quality levels and sets listeners to update them.
  417. *
  418. * @method setupQualityLevels_
  419. * @private
  420. */
  421. }, {
  422. key: 'setupQualityLevels_',
  423. value: function setupQualityLevels_() {
  424. var _this4 = this;
  425. var player = _videoJs2['default'].players[this.tech_.options_.playerId];
  426. if (player && player.qualityLevels) {
  427. this.qualityLevels_ = player.qualityLevels();
  428. this.masterPlaylistController_.on('selectedinitialmedia', function () {
  429. handleHlsLoadedMetadata(_this4.qualityLevels_, _this4);
  430. });
  431. this.playlists.on('mediachange', function () {
  432. handleHlsMediaChange(_this4.qualityLevels_, _this4.playlists);
  433. });
  434. }
  435. }
  436. /**
  437. * Begin playing the video.
  438. */
  439. }, {
  440. key: 'play',
  441. value: function play() {
  442. this.masterPlaylistController_.play();
  443. }
  444. /**
  445. * a wrapper around the function in MasterPlaylistController
  446. */
  447. }, {
  448. key: 'setCurrentTime',
  449. value: function setCurrentTime(currentTime) {
  450. this.masterPlaylistController_.setCurrentTime(currentTime);
  451. }
  452. /**
  453. * a wrapper around the function in MasterPlaylistController
  454. */
  455. }, {
  456. key: 'duration',
  457. value: function duration() {
  458. return this.masterPlaylistController_.duration();
  459. }
  460. /**
  461. * a wrapper around the function in MasterPlaylistController
  462. */
  463. }, {
  464. key: 'seekable',
  465. value: function seekable() {
  466. return this.masterPlaylistController_.seekable();
  467. }
  468. /**
  469. * Abort all outstanding work and cleanup.
  470. */
  471. }, {
  472. key: 'dispose',
  473. value: function dispose() {
  474. if (this.playbackWatcher_) {
  475. this.playbackWatcher_.dispose();
  476. }
  477. if (this.masterPlaylistController_) {
  478. this.masterPlaylistController_.dispose();
  479. }
  480. if (this.qualityLevels_) {
  481. this.qualityLevels_.dispose();
  482. }
  483. _get(Object.getPrototypeOf(HlsHandler.prototype), 'dispose', this).call(this);
  484. }
  485. }]);
  486. return HlsHandler;
  487. })(Component);
  488. var HlsSourceHandler = function HlsSourceHandler(mode) {
  489. return {
  490. canHandleSource: function canHandleSource(srcObj) {
  491. var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
  492. var localOptions = _videoJs2['default'].mergeOptions(_videoJs2['default'].options, options);
  493. // this forces video.js to skip this tech/mode if its not the one we have been
  494. // overriden to use, by returing that we cannot handle the source.
  495. if (localOptions.hls && localOptions.hls.mode && localOptions.hls.mode !== mode) {
  496. return false;
  497. }
  498. return HlsSourceHandler.canPlayType(srcObj.type, localOptions);
  499. },
  500. handleSource: function handleSource(source, tech) {
  501. var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
  502. var localOptions = _videoJs2['default'].mergeOptions(_videoJs2['default'].options, options, { hls: { mode: mode } });
  503. if (mode === 'flash') {
  504. // We need to trigger this asynchronously to give others the chance
  505. // to bind to the event when a source is set at player creation
  506. tech.setTimeout(function () {
  507. tech.trigger('loadstart');
  508. }, 1);
  509. }
  510. tech.hls = new HlsHandler(source, tech, localOptions);
  511. tech.hls.xhr = (0, _xhr2['default'])();
  512. tech.hls.src(source.src);
  513. return tech.hls;
  514. },
  515. canPlayType: function canPlayType(type) {
  516. var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
  517. var localOptions = _videoJs2['default'].mergeOptions(_videoJs2['default'].options, options);
  518. if (HlsSourceHandler.canPlayType(type, localOptions)) {
  519. return 'maybe';
  520. }
  521. return '';
  522. }
  523. };
  524. };
  525. HlsSourceHandler.canPlayType = function (type, options) {
  526. // No support for IE 10 or below
  527. if (_videoJs2['default'].browser.IE_VERSION && _videoJs2['default'].browser.IE_VERSION <= 10) {
  528. return false;
  529. }
  530. var mpegurlRE = /^(audio|video|application)\/(x-|vnd\.apple\.)?mpegurl/i;
  531. // favor native HLS support if it's available
  532. if (!options.hls.overrideNative && Hls.supportsNativeHls) {
  533. return false;
  534. }
  535. return mpegurlRE.test(type);
  536. };
  537. if (typeof _videoJs2['default'].MediaSource === 'undefined' || typeof _videoJs2['default'].URL === 'undefined') {
  538. _videoJs2['default'].MediaSource = _videojsContribMediaSources.MediaSource;
  539. _videoJs2['default'].URL = _videojsContribMediaSources.URL;
  540. }
  541. var flashTech = _videoJs2['default'].getTech('Flash');
  542. // register source handlers with the appropriate techs
  543. if (_videojsContribMediaSources.MediaSource.supportsNativeMediaSources()) {
  544. _videoJs2['default'].getTech('Html5').registerSourceHandler(HlsSourceHandler('html5'), 0);
  545. }
  546. if (_globalWindow2['default'].Uint8Array && flashTech) {
  547. flashTech.registerSourceHandler(HlsSourceHandler('flash'));
  548. }
  549. _videoJs2['default'].HlsHandler = HlsHandler;
  550. _videoJs2['default'].HlsSourceHandler = HlsSourceHandler;
  551. _videoJs2['default'].Hls = Hls;
  552. if (!_videoJs2['default'].use) {
  553. _videoJs2['default'].registerComponent('Hls', Hls);
  554. }
  555. _videoJs2['default'].m3u8 = _m3u8Parser2['default'];
  556. _videoJs2['default'].options.hls = _videoJs2['default'].options.hls || {};
  557. if (_videoJs2['default'].registerPlugin) {
  558. _videoJs2['default'].registerPlugin('reloadSourceOnError', _reloadSourceOnError2['default']);
  559. } else {
  560. _videoJs2['default'].plugin('reloadSourceOnError', _reloadSourceOnError2['default']);
  561. }
  562. module.exports = {
  563. Hls: Hls,
  564. HlsHandler: HlsHandler,
  565. HlsSourceHandler: HlsSourceHandler
  566. };