index.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. /* global window document */
  2. /* eslint-disable vars-on-top, no-var, object-shorthand, no-console */
  3. (function(window) {
  4. var representationsEl = document.getElementById('representations');
  5. representationsEl.addEventListener('change', function() {
  6. var selectedIndex = representationsEl.selectedIndex;
  7. if (selectedIndex < 0 || !window.vhs) {
  8. return;
  9. }
  10. var selectedOption = representationsEl.options[representationsEl.selectedIndex];
  11. if (!selectedOption) {
  12. return;
  13. }
  14. var id = selectedOption.value;
  15. window.vhs.representations().forEach(function(rep) {
  16. rep.playlist.disabled = rep.id !== id;
  17. });
  18. window.pc.fastQualityChange_();
  19. });
  20. var isManifestObjectType = function(url) {
  21. return (/application\/vnd\.videojs\.vhs\+json/).test(url);
  22. };
  23. var hlsOptGroup = document.querySelector('[label="hls"]');
  24. var dashOptGroup = document.querySelector('[label="dash"]');
  25. var drmOptGroup = document.querySelector('[label="drm"]');
  26. var liveOptGroup = document.querySelector('[label="live"]');
  27. var llliveOptGroup = document.querySelector('[label="low latency live"]');
  28. var manifestOptGroup = document.querySelector('[label="json manifest object"]');
  29. var sourceList;
  30. var hlsDataManifest;
  31. var dashDataManifest;
  32. var addSourcesToDom = function() {
  33. if (!sourceList || !hlsDataManifest || !dashDataManifest) {
  34. return;
  35. }
  36. sourceList.forEach(function(source) {
  37. var option = document.createElement('option');
  38. option.innerText = source.name;
  39. option.value = source.uri;
  40. if (source.keySystems) {
  41. option.setAttribute('data-key-systems', JSON.stringify(source.keySystems, null, 2));
  42. }
  43. if (source.mimetype) {
  44. option.setAttribute('data-mimetype', source.mimetype);
  45. }
  46. if (source.features.indexOf('low-latency') !== -1) {
  47. llliveOptGroup.appendChild(option);
  48. } else if (source.features.indexOf('live') !== -1) {
  49. liveOptGroup.appendChild(option);
  50. } else if (source.keySystems) {
  51. drmOptGroup.appendChild(option);
  52. } else if (source.mimetype === 'application/x-mpegurl') {
  53. hlsOptGroup.appendChild(option);
  54. } else if (source.mimetype === 'application/dash+xml') {
  55. dashOptGroup.appendChild(option);
  56. }
  57. });
  58. var hlsOption = document.createElement('option');
  59. var dashOption = document.createElement('option');
  60. dashOption.innerText = 'Dash Manifest Object Test, does not survive page reload';
  61. dashOption.value = `data:application/vnd.videojs.vhs+json,${dashDataManifest}`;
  62. hlsOption.innerText = 'HLS Manifest Object Test, does not survive page reload';
  63. hlsOption.value = `data:application/vnd.videojs.vhs+json,${hlsDataManifest}`;
  64. manifestOptGroup.appendChild(hlsOption);
  65. manifestOptGroup.appendChild(dashOption);
  66. };
  67. var sourcesXhr = new window.XMLHttpRequest();
  68. sourcesXhr.addEventListener('load', function() {
  69. sourceList = JSON.parse(sourcesXhr.responseText);
  70. addSourcesToDom();
  71. });
  72. sourcesXhr.open('GET', './scripts/sources.json');
  73. sourcesXhr.send();
  74. var hlsManifestXhr = new window.XMLHttpRequest();
  75. hlsManifestXhr.addEventListener('load', function() {
  76. hlsDataManifest = hlsManifestXhr.responseText;
  77. addSourcesToDom();
  78. });
  79. hlsManifestXhr.open('GET', './scripts/hls-manifest-object.json');
  80. hlsManifestXhr.send();
  81. var dashManifestXhr = new window.XMLHttpRequest();
  82. dashManifestXhr.addEventListener('load', function() {
  83. dashDataManifest = dashManifestXhr.responseText;
  84. addSourcesToDom();
  85. });
  86. dashManifestXhr.open('GET', './scripts/dash-manifest-object.json');
  87. dashManifestXhr.send();
  88. // all relevant elements
  89. var urlButton = document.getElementById('load-url');
  90. var sources = document.getElementById('load-source');
  91. var stateEls = {};
  92. var getInputValue = function(el) {
  93. if (el.type === 'url' || el.type === 'text' || el.nodeName.toLowerCase() === 'textarea') {
  94. if (isManifestObjectType(el.value)) {
  95. return '';
  96. }
  97. return encodeURIComponent(el.value);
  98. } else if (el.type === 'select-one') {
  99. return el.options[el.selectedIndex].value;
  100. } else if (el.type === 'checkbox') {
  101. return el.checked;
  102. }
  103. console.warn('unhandled input type ' + el.type);
  104. return '';
  105. };
  106. var setInputValue = function(el, value) {
  107. if (el.type === 'url' || el.type === 'text' || el.nodeName.toLowerCase() === 'textarea') {
  108. el.value = decodeURIComponent(value);
  109. } else if (el.type === 'select-one') {
  110. for (var i = 0; i < el.options.length; i++) {
  111. if (el.options[i].value === value) {
  112. el.options[i].selected = true;
  113. }
  114. }
  115. } else {
  116. // get the `value` into a Boolean.
  117. el.checked = JSON.parse(value);
  118. }
  119. };
  120. var newEvent = function(name) {
  121. var event;
  122. if (typeof window.Event === 'function') {
  123. event = new window.Event(name);
  124. } else {
  125. event = document.createEvent('Event');
  126. event.initEvent(name, true, true);
  127. }
  128. return event;
  129. };
  130. // taken from video.js
  131. var getFileExtension = function(path) {
  132. var splitPathRe;
  133. var pathParts;
  134. if (typeof path === 'string') {
  135. splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]*?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i;
  136. pathParts = splitPathRe.exec(path);
  137. if (pathParts) {
  138. return pathParts.pop().toLowerCase();
  139. }
  140. }
  141. return '';
  142. };
  143. var saveState = function() {
  144. var query = '';
  145. if (!window.history.replaceState) {
  146. return;
  147. }
  148. Object.keys(stateEls).forEach(function(elName) {
  149. var symbol = query.length ? '&' : '?';
  150. query += symbol + elName + '=' + getInputValue(stateEls[elName]);
  151. });
  152. window.history.replaceState({}, 'vhs demo', query);
  153. };
  154. window.URLSearchParams = window.URLSearchParams || function(locationSearch) {
  155. this.get = function(name) {
  156. var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(locationSearch);
  157. return results ? decodeURIComponent(results[1]) : null;
  158. };
  159. };
  160. // eslint-disable-next-line
  161. var loadState = function() {
  162. var params = new window.URLSearchParams(window.location.search);
  163. return Object.keys(stateEls).reduce(function(acc, elName) {
  164. acc[elName] = typeof params.get(elName) !== 'object' ? params.get(elName) : getInputValue(stateEls[elName]);
  165. return acc;
  166. }, {});
  167. };
  168. // eslint-disable-next-line
  169. var reloadScripts = function(urls, cb) {
  170. var el = document.getElementById('reload-scripts');
  171. if (!el) {
  172. el = document.createElement('div');
  173. el.id = 'reload-scripts';
  174. document.body.appendChild(el);
  175. }
  176. while (el.firstChild) {
  177. el.removeChild(el.firstChild);
  178. }
  179. var loaded = [];
  180. var checkDone = function() {
  181. if (loaded.length === urls.length) {
  182. cb();
  183. }
  184. };
  185. urls.forEach(function(url) {
  186. var script = document.createElement('script');
  187. // scripts marked as defer will be loaded asynchronously but will be executed in the order they are in the DOM
  188. script.defer = true;
  189. // dynamically created scripts are async by default unless otherwise specified
  190. // async scripts are loaded asynchronously but also executed as soon as they are loaded
  191. // we want to load them in the order they are added therefore we want to turn off async
  192. script.async = false;
  193. script.src = url;
  194. script.onload = function() {
  195. loaded.push(url);
  196. checkDone();
  197. };
  198. el.appendChild(script);
  199. });
  200. };
  201. var regenerateRepresentations = function() {
  202. while (representationsEl.firstChild) {
  203. representationsEl.removeChild(representationsEl.firstChild);
  204. }
  205. var selectedIndex;
  206. window.vhs.representations().forEach(function(rep, i) {
  207. var option = document.createElement('option');
  208. option.value = rep.id;
  209. option.innerText = JSON.stringify({
  210. id: rep.id,
  211. videoCodec: rep.codecs.video,
  212. audioCodec: rep.codecs.audio,
  213. bandwidth: rep.bandwidth,
  214. heigth: rep.heigth,
  215. width: rep.width
  216. });
  217. if (window.pc.media().id === rep.id) {
  218. selectedIndex = i;
  219. }
  220. representationsEl.appendChild(option);
  221. });
  222. representationsEl.selectedIndex = selectedIndex;
  223. };
  224. function getBuffered(buffered) {
  225. var bufferedText = '';
  226. if (!buffered) {
  227. return bufferedText;
  228. }
  229. if (buffered.length) {
  230. bufferedText += buffered.start(0) + ' - ' + buffered.end(0);
  231. }
  232. for (var i = 1; i < buffered.length; i++) {
  233. bufferedText += ', ' + buffered.start(i) + ' - ' + buffered.end(i);
  234. }
  235. return bufferedText;
  236. }
  237. var setupSegmentMetadata = function(player) {
  238. // setup segment metadata
  239. var segmentMetadata = document.querySelector('#segment-metadata');
  240. player.one('loadedmetadata', function() {
  241. var tracks = player.textTracks();
  242. var segmentMetadataTrack;
  243. for (var i = 0; i < tracks.length; i++) {
  244. if (tracks[i].label === 'segment-metadata') {
  245. segmentMetadataTrack = tracks[i];
  246. }
  247. }
  248. while (segmentMetadata.children.length) {
  249. segmentMetadata.removeChild(segmentMetadata.firstChild);
  250. }
  251. if (segmentMetadataTrack) {
  252. segmentMetadataTrack.addEventListener('cuechange', function() {
  253. var cues = segmentMetadataTrack.activeCues || [];
  254. while (segmentMetadata.children.length) {
  255. segmentMetadata.removeChild(segmentMetadata.firstChild);
  256. }
  257. for (var j = 0; j < cues.length; j++) {
  258. var text = JSON.stringify(JSON.parse(cues[j].text), null, 2);
  259. var li = document.createElement('li');
  260. var pre = document.createElement('pre');
  261. pre.classList.add('border', 'rounded', 'p-2');
  262. pre.textContent = text;
  263. li.appendChild(pre);
  264. segmentMetadata.appendChild(li);
  265. }
  266. });
  267. }
  268. });
  269. };
  270. var setupPlayerStats = function(player) {
  271. player.on('dispose', () => {
  272. if (window.statsTimer) {
  273. window.clearInterval(window.statsTimer);
  274. window.statsTimer = null;
  275. }
  276. });
  277. var currentTimeStat = document.querySelector('.current-time-stat');
  278. var bufferedStat = document.querySelector('.buffered-stat');
  279. var videoBufferedStat = document.querySelector('.video-buffered-stat');
  280. var audioBufferedStat = document.querySelector('.audio-buffered-stat');
  281. var seekableStartStat = document.querySelector('.seekable-start-stat');
  282. var seekableEndStat = document.querySelector('.seekable-end-stat');
  283. var videoBitrateState = document.querySelector('.video-bitrate-stat');
  284. var measuredBitrateStat = document.querySelector('.measured-bitrate-stat');
  285. var videoTimestampOffset = document.querySelector('.video-timestampoffset');
  286. var audioTimestampOffset = document.querySelector('.audio-timestampoffset');
  287. player.on('timeupdate', function() {
  288. currentTimeStat.textContent = player.currentTime().toFixed(1);
  289. });
  290. window.statsTimer = window.setInterval(function() {
  291. var oldStart;
  292. var oldEnd;
  293. var seekable = player.seekable();
  294. if (seekable && seekable.length) {
  295. oldStart = seekableStartStat.textContent;
  296. if (seekable.start(0).toFixed(1) !== oldStart) {
  297. seekableStartStat.textContent = seekable.start(0).toFixed(1);
  298. }
  299. oldEnd = seekableEndStat.textContent;
  300. if (seekable.end(0).toFixed(1) !== oldEnd) {
  301. seekableEndStat.textContent = seekable.end(0).toFixed(1);
  302. }
  303. }
  304. // buffered
  305. bufferedStat.textContent = getBuffered(player.buffered());
  306. // exit early if no VHS
  307. if (!player.tech(true).vhs) {
  308. videoBufferedStat.textContent = '';
  309. audioBufferedStat.textContent = '';
  310. videoBitrateState.textContent = '';
  311. measuredBitrateStat.textContent = '';
  312. videoTimestampOffset.textContent = '';
  313. audioTimestampOffset.textContent = '';
  314. return;
  315. }
  316. videoBufferedStat.textContent = getBuffered(player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer &&
  317. player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer.buffered);
  318. // demuxed audio
  319. var audioBuffer = getBuffered(player.tech(true).vhs.playlistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer &&
  320. player.tech(true).vhs.playlistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer.buffered);
  321. // muxed audio
  322. if (!audioBuffer) {
  323. audioBuffer = getBuffered(player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer &&
  324. player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer.buffered);
  325. }
  326. audioBufferedStat.textContent = audioBuffer;
  327. if (player.tech(true).vhs.playlistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer) {
  328. audioTimestampOffset.textContent = player.tech(true).vhs.playlistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer.timestampOffset;
  329. } else if (player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer) {
  330. audioTimestampOffset.textContent = player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer.timestampOffset;
  331. }
  332. if (player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer) {
  333. videoTimestampOffset.textContent = player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer.timestampOffset;
  334. }
  335. // bitrates
  336. var playlist = player.tech_.vhs.playlists.media();
  337. if (playlist && playlist.attributes && playlist.attributes.BANDWIDTH) {
  338. videoBitrateState.textContent = (playlist.attributes.BANDWIDTH / 1024).toLocaleString(undefined, {
  339. maximumFractionDigits: 1
  340. }) + ' kbps';
  341. }
  342. if (player.tech_.vhs.bandwidth) {
  343. measuredBitrateStat.textContent = (player.tech_.vhs.bandwidth / 1024).toLocaleString(undefined, {
  344. maximumFractionDigits: 1
  345. }) + ' kbps';
  346. }
  347. }, 100);
  348. };
  349. var setupContentSteeringData = function(player) {
  350. var currentPathwayEl = document.querySelector('.current-pathway');
  351. var availablePathwaysEl = document.querySelector('.available-pathways');
  352. var steeringManifestEl = document.querySelector('.steering-manifest');
  353. player.one('loadedmetadata', function() {
  354. var steeringController = player.tech_.vhs.playlistController_.contentSteeringController_;
  355. if (!steeringController) {
  356. return;
  357. }
  358. var onContentSteering = function() {
  359. currentPathwayEl.textContent = steeringController.currentPathway;
  360. availablePathwaysEl.textContent = Array.from(steeringController.availablePathways_).join(', ');
  361. steeringManifestEl.textContent = JSON.stringify(steeringController.steeringManifest);
  362. };
  363. steeringController.on('content-steering', onContentSteering);
  364. });
  365. };
  366. [
  367. 'debug',
  368. 'autoplay',
  369. 'muted',
  370. 'fluid',
  371. 'minified',
  372. 'sync-workers',
  373. 'liveui',
  374. 'llhls',
  375. 'url',
  376. 'type',
  377. 'keysystems',
  378. 'buffer-water',
  379. 'exact-manifest-timings',
  380. 'pixel-diff-selector',
  381. 'network-info',
  382. 'dts-offset',
  383. 'override-native',
  384. 'preload',
  385. 'mirror-source',
  386. 'forced-subtitles'
  387. ].forEach(function(name) {
  388. stateEls[name] = document.getElementById(name);
  389. });
  390. window.startDemo = function(cb) {
  391. var state = loadState();
  392. Object.keys(state).forEach(function(elName) {
  393. setInputValue(stateEls[elName], state[elName]);
  394. });
  395. Array.prototype.forEach.call(sources.options, function(s, i) {
  396. if (s.value === state.url) {
  397. sources.selectedIndex = i;
  398. }
  399. });
  400. stateEls.fluid.addEventListener('change', function(event) {
  401. saveState();
  402. if (event.target.checked) {
  403. window['player-fixture'].style.aspectRatio = '16/9';
  404. window['player-fixture'].style.minHeight = 'initial';
  405. } else {
  406. window['player-fixture'].style.aspectRatio = '';
  407. window['player-fixture'].style.minHeight = '250px';
  408. }
  409. window.player.fluid(event.target.checked);
  410. });
  411. stateEls.muted.addEventListener('change', function(event) {
  412. saveState();
  413. window.player.muted(event.target.checked);
  414. });
  415. stateEls.autoplay.addEventListener('change', function(event) {
  416. saveState();
  417. window.player.autoplay(event.target.checked);
  418. });
  419. // stateEls that reload the player and scripts
  420. [
  421. 'mirror-source',
  422. 'sync-workers',
  423. 'preload',
  424. 'llhls',
  425. 'buffer-water',
  426. 'override-native',
  427. 'liveui',
  428. 'pixel-diff-selector',
  429. 'network-info',
  430. 'dts-offset',
  431. 'exact-manifest-timings',
  432. 'forced-subtitles'
  433. ].forEach(function(name) {
  434. stateEls[name].addEventListener('change', function(event) {
  435. saveState();
  436. stateEls.minified.dispatchEvent(newEvent('change'));
  437. });
  438. });
  439. [
  440. 'llhls'
  441. ].forEach(function(name) {
  442. stateEls[name].checked = true;
  443. });
  444. [
  445. 'exact-manifest-timings',
  446. 'pixel-diff-selector',
  447. 'buffer-water'
  448. ].forEach(function(name) {
  449. stateEls[name].checked = false;
  450. });
  451. stateEls.debug.addEventListener('change', function(event) {
  452. saveState();
  453. window.videojs.log.level(event.target.checked ? 'debug' : 'info');
  454. });
  455. stateEls.minified.addEventListener('change', function(event) {
  456. var urls = [
  457. 'node_modules/video.js/dist/alt/video.core',
  458. 'node_modules/videojs-contrib-eme/dist/videojs-contrib-eme',
  459. 'node_modules/videojs-contrib-quality-levels/dist/videojs-contrib-quality-levels',
  460. 'node_modules/videojs-http-source-selector/dist/videojs-http-source-selector'
  461. ].map(function(url) {
  462. return url + (event.target.checked ? '.min' : '') + '.js';
  463. });
  464. if (stateEls['sync-workers'].checked) {
  465. urls.push('dist/videojs-http-streaming-sync-workers.js');
  466. } else {
  467. urls.push('dist/videojs-http-streaming' + (event.target.checked ? '.min' : '') + '.js');
  468. }
  469. saveState();
  470. if (window.player) {
  471. window.player.dispose();
  472. delete window.player;
  473. }
  474. if (window.videojs) {
  475. delete window.videojs;
  476. }
  477. reloadScripts(urls, function() {
  478. var player;
  479. var fixture = document.getElementById('player-fixture');
  480. var videoEl = document.createElement('video-js');
  481. videoEl.setAttribute('controls', '');
  482. videoEl.setAttribute('preload', stateEls.preload.options[stateEls.preload.selectedIndex].value || 'auto');
  483. videoEl.className = 'vjs-default-skin';
  484. fixture.appendChild(videoEl);
  485. var mirrorSource = getInputValue(stateEls['mirror-source']);
  486. player = window.player = window.videojs(videoEl, {
  487. plugins: {
  488. httpSourceSelector: {
  489. default: 'auto'
  490. }
  491. },
  492. liveui: stateEls.liveui.checked,
  493. enableSourceset: mirrorSource,
  494. html5: {
  495. vhs: {
  496. overrideNative: getInputValue(stateEls['override-native']),
  497. bufferBasedABR: getInputValue(stateEls['buffer-water']),
  498. llhls: getInputValue(stateEls.llhls),
  499. exactManifestTimings: getInputValue(stateEls['exact-manifest-timings']),
  500. leastPixelDiffSelector: getInputValue(stateEls['pixel-diff-selector']),
  501. useNetworkInformationApi: getInputValue(stateEls['network-info']),
  502. useDtsForTimestampOffset: getInputValue(stateEls['dts-offset']),
  503. useForcedSubtitles: getInputValue(stateEls['forced-subtitles'])
  504. }
  505. }
  506. });
  507. setupPlayerStats(player);
  508. setupSegmentMetadata(player);
  509. setupContentSteeringData(player);
  510. // save player muted state interation
  511. player.on('volumechange', function() {
  512. if (stateEls.muted.checked !== player.muted()) {
  513. stateEls.muted.checked = player.muted();
  514. saveState();
  515. }
  516. });
  517. player.on('sourceset', function() {
  518. var source = player.currentSource();
  519. if (source.keySystems) {
  520. var copy = JSON.parse(JSON.stringify(source.keySystems));
  521. // have to delete pssh as it will often make keySystems too big
  522. // for a uri
  523. Object.keys(copy).forEach(function(key) {
  524. if (copy[key].hasOwnProperty('pssh')) {
  525. delete copy[key].pssh;
  526. }
  527. });
  528. stateEls.keysystems.value = JSON.stringify(copy, null, 2);
  529. }
  530. if (source.src) {
  531. stateEls.url.value = encodeURI(source.src);
  532. }
  533. if (source.type) {
  534. stateEls.type.value = source.type;
  535. }
  536. saveState();
  537. });
  538. player.width(640);
  539. player.height(264);
  540. // configure videojs-contrib-eme
  541. player.eme();
  542. stateEls.debug.dispatchEvent(newEvent('change'));
  543. stateEls.muted.dispatchEvent(newEvent('change'));
  544. stateEls.fluid.dispatchEvent(newEvent('change'));
  545. stateEls.autoplay.dispatchEvent(newEvent('change'));
  546. // run the load url handler for the intial source
  547. if (stateEls.url.value) {
  548. urlButton.dispatchEvent(newEvent('click'));
  549. } else {
  550. sources.dispatchEvent(newEvent('change'));
  551. }
  552. player.on('loadedmetadata', function() {
  553. if (player.tech_.vhs) {
  554. window.vhs = player.tech_.vhs;
  555. window.pc = player.tech_.vhs.playlistController_;
  556. window.pc.mainPlaylistLoader_.on('mediachange', regenerateRepresentations);
  557. regenerateRepresentations();
  558. } else {
  559. window.vhs = null;
  560. window.pc = null;
  561. }
  562. });
  563. cb(player);
  564. });
  565. });
  566. var urlButtonClick = function(event) {
  567. var ext;
  568. var type = stateEls.type.value;
  569. // reset type if it's a manifest object's type
  570. if (type === 'application/vnd.videojs.vhs+json') {
  571. type = '';
  572. }
  573. if (isManifestObjectType(stateEls.url.value)) {
  574. type = 'application/vnd.videojs.vhs+json';
  575. }
  576. if (!type.trim()) {
  577. ext = getFileExtension(stateEls.url.value);
  578. if (ext === 'mpd') {
  579. type = 'application/dash+xml';
  580. } else if (ext === 'm3u8') {
  581. type = 'application/x-mpegURL';
  582. }
  583. }
  584. saveState();
  585. var source = {
  586. src: stateEls.url.value,
  587. type: type
  588. };
  589. if (stateEls.keysystems.value) {
  590. source.keySystems = JSON.parse(stateEls.keysystems.value);
  591. }
  592. sources.selectedIndex = -1;
  593. Array.prototype.forEach.call(sources.options, function(s, i) {
  594. if (s.value === stateEls.url.value) {
  595. sources.selectedIndex = i;
  596. }
  597. });
  598. window.player.src(source);
  599. };
  600. urlButton.addEventListener('click', urlButtonClick);
  601. urlButton.addEventListener('tap', urlButtonClick);
  602. sources.addEventListener('change', function(event) {
  603. var selectedOption = sources.options[sources.selectedIndex];
  604. if (!selectedOption) {
  605. return;
  606. }
  607. var src = selectedOption.value;
  608. stateEls.url.value = src;
  609. stateEls.type.value = selectedOption.getAttribute('data-mimetype');
  610. stateEls.keysystems.value = selectedOption.getAttribute('data-key-systems');
  611. urlButton.dispatchEvent(newEvent('click'));
  612. });
  613. stateEls.url.addEventListener('keyup', function(event) {
  614. if (event.key === 'Enter') {
  615. urlButton.click();
  616. }
  617. });
  618. stateEls.url.addEventListener('input', function(event) {
  619. if (stateEls.type.value.length) {
  620. stateEls.type.value = '';
  621. }
  622. });
  623. stateEls.type.addEventListener('keyup', function(event) {
  624. if (event.key === 'Enter') {
  625. urlButton.click();
  626. }
  627. });
  628. // run the change handler for the first time
  629. stateEls.minified.dispatchEvent(newEvent('change'));
  630. };
  631. }(window));