playlist-selectors.test.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import { module, test } from 'qunit';
  2. import {
  3. simpleSelector,
  4. movingAverageBandwidthSelector,
  5. minRebufferMaxBandwidthSelector,
  6. lowestBitrateCompatibleVariantSelector
  7. } from '../src/playlist-selectors';
  8. import Config from '../src/config';
  9. module('Playlist Selectors', {
  10. beforeEach(assert) {
  11. const video = document.createElement('video');
  12. this.hls = {
  13. tech_: {
  14. el() {
  15. return video;
  16. }
  17. },
  18. playlists: {
  19. master: {
  20. playlists: []
  21. }
  22. }
  23. };
  24. },
  25. afterEach() {
  26. }
  27. });
  28. test('Exponential moving average has a configurable decay parameter', function(assert) {
  29. let playlist;
  30. const instantAverage = movingAverageBandwidthSelector(1.0);
  31. this.hls.playlists.master.playlists = [
  32. { attributes: { BANDWIDTH: 1 } },
  33. { attributes: { BANDWIDTH: 50 } },
  34. { attributes: { BANDWIDTH: 100 } }
  35. ];
  36. this.hls.systemBandwidth = 50 * Config.BANDWIDTH_VARIANCE + 1;
  37. playlist = instantAverage.call(this.hls);
  38. assert.equal(playlist.attributes.BANDWIDTH, 50, 'selected the middle playlist');
  39. this.hls.systemBandwidth = 100 * Config.BANDWIDTH_VARIANCE + 1;
  40. playlist = instantAverage.call(this.hls);
  41. assert.equal(playlist.attributes.BANDWIDTH, 100, 'selected the top playlist');
  42. const fiftyPercentDecay = movingAverageBandwidthSelector(0.5);
  43. this.hls.systemBandwidth = 100 * Config.BANDWIDTH_VARIANCE + 1;
  44. playlist = fiftyPercentDecay.call(this.hls);
  45. assert.equal(playlist.attributes.BANDWIDTH, 100, 'selected the top playlist');
  46. // average = decay * systemBandwidth + (1 - decay) * average
  47. // bandwidth = 0.5 * systemBandwidth + 0.5 * (100 * variance + 1)
  48. // 50 * variance + 1 = 0.5 * (systemBandwidth + (100 * variance + 1))
  49. // 2 * 50 * variance + 2 = systemBandwidth + (100 * variance + 1)
  50. // 100 * variance + 2 - (100 * variance + 1) = systemBandwidth
  51. // 1 = systemBandwidth
  52. this.hls.systemBandwidth = 1;
  53. playlist = fiftyPercentDecay.call(this.hls);
  54. assert.equal(playlist.attributes.BANDWIDTH, 50, 'selected the middle playlist');
  55. });
  56. test('minRebufferMaxBandwidthSelector picks highest rendition without rebuffering',
  57. function(assert) {
  58. let master = this.hls.playlists.master;
  59. let currentTime = 0;
  60. let bandwidth = 2000;
  61. let duration = 100;
  62. let segmentDuration = 10;
  63. let timeUntilRebuffer = 5;
  64. let currentTimeline = 0;
  65. let syncController = {
  66. getSyncPoint: (playlist) => playlist.syncPoint
  67. };
  68. const settings = () => {
  69. return {
  70. master,
  71. currentTime,
  72. bandwidth,
  73. duration,
  74. segmentDuration,
  75. timeUntilRebuffer,
  76. currentTimeline,
  77. syncController
  78. };
  79. };
  80. master.playlists = [
  81. { attributes: { BANDWIDTH: 100 }, syncPoint: false },
  82. { attributes: { BANDWIDTH: 500 }, syncPoint: false },
  83. { attributes: { BANDWIDTH: 1000 }, syncPoint: false },
  84. { attributes: { BANDWIDTH: 2000 }, syncPoint: true },
  85. { attributes: { BANDWIDTH: 5000 }, syncPoint: false }
  86. ];
  87. let result = minRebufferMaxBandwidthSelector(settings());
  88. assert.equal(result.playlist, master.playlists[1], 'selected the correct playlist');
  89. assert.equal(result.rebufferingImpact, 0, 'impact on rebuffering is 0');
  90. master.playlists = [
  91. { attributes: { BANDWIDTH: 100 }, syncPoint: false },
  92. { attributes: { BANDWIDTH: 500 }, syncPoint: false },
  93. { attributes: { BANDWIDTH: 1000 }, syncPoint: true },
  94. { attributes: { BANDWIDTH: 2000 }, syncPoint: true },
  95. { attributes: { BANDWIDTH: 5000 }, syncPoint: false }
  96. ];
  97. result = minRebufferMaxBandwidthSelector(settings());
  98. assert.equal(result.playlist, master.playlists[2], 'selected the corerct playlist');
  99. assert.equal(result.rebufferingImpact, 0, 'impact on rebuffering is 0');
  100. bandwidth = 500;
  101. timeUntilRebuffer = 3;
  102. result = minRebufferMaxBandwidthSelector(settings());
  103. assert.equal(result.playlist, master.playlists[0], 'selected the correct playlist');
  104. assert.equal(result.rebufferingImpact, 1, 'impact on rebuffering is 1 second');
  105. });
  106. test('lowestBitrateCompatibleVariantSelector picks lowest non-audio playlist',
  107. function(assert) {
  108. // Set this up out of order to make sure that the function sorts all
  109. // playlists by bandwidth
  110. this.hls.playlists.master.playlists = [
  111. { attributes: { BANDWIDTH: 10, CODECS: 'mp4a.40.2' } },
  112. { attributes: { BANDWIDTH: 100, CODECS: 'mp4a.40.2, avc1.4d400d' } },
  113. { attributes: { BANDWIDTH: 50, CODECS: 'mp4a.40.2, avc1.4d400d' } }
  114. ];
  115. const expectedPlaylist = this.hls.playlists.master.playlists[2];
  116. const testPlaylist = lowestBitrateCompatibleVariantSelector.call(this.hls);
  117. assert.equal(testPlaylist, expectedPlaylist,
  118. 'Selected lowest compatible playlist with video assets');
  119. });
  120. test('lowestBitrateCompatibleVariantSelector return null if no video exists',
  121. function(assert) {
  122. this.hls.playlists.master.playlists = [
  123. { attributes: { BANDWIDTH: 50, CODECS: 'mp4a.40.2' } },
  124. { attributes: { BANDWIDTH: 10, CODECS: 'mp4a.40.2' } },
  125. { attributes: { BANDWIDTH: 100, CODECS: 'mp4a.40.2' } }
  126. ];
  127. const testPlaylist = lowestBitrateCompatibleVariantSelector.call(this.hls);
  128. assert.equal(testPlaylist, null,
  129. 'Returned null playlist since no video assets exist');
  130. });
  131. test('simpleSelector switches up even without resolution information', function(assert) {
  132. let master = this.hls.playlists.master;
  133. master.playlists = [
  134. { attributes: { BANDWIDTH: 100 } },
  135. { attributes: { BANDWIDTH: 1000 } }
  136. ];
  137. const selectedPlaylist = simpleSelector(master, 2000, 1, 1);
  138. assert.equal(selectedPlaylist, master.playlists[1], 'selected the correct playlist');
  139. });