You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

313 lines
13 KiB

feat: Consider object-fit when selecting playlist by player size (#1051) * Make simple playlist selecor easier to extend Refactor from positional parameters to a single `settings` argument. This makes it clearer what each argument means in calls of the function. Additional parameters can now be added without making the argument list overly long. Key names were chosen to match those of `minRebufferMaxBandwidthSelector` to align the signatures. * Make simpleSelector test easier to understand Inline the passed bandwidth value instead of referencing the config constant since the concrete value is needed to understand why the expected playlist is chosen. Also if the config constant should ever change the test will fail for no good reason. * Consider object-fit when selecting playlist by player size So far, when `limitRenditionByPlayerDimensions` is `true`, `simpleSelector` tried to either find a rendition with a resolution that matches the size of the player exactly or, if that does not exist, a rendition with the smallest resolution that has either greater width or greater height than the player. This makes sense since by default the video will be scaled to fit inside the media element. So every resolution that exceeds player size in at least one dimension will be scaled down. Most browsers support [1] customizing this scaling behavior via the `object-fit` CSS property [2]. If it set to `cover`, the video will instead be scaled up if video and player aspect ratio do not match. The previous behavior caused renditions with low resolution to be selected for players with small width (e.g. portrait phone aspect ratio) even when videos were then scaled up to cover the whole player. We therefore detect if `object-fit` is set to `cover` and instead select the smallest rendition with a resolution that exceeds player dimensions in both width and height. [1] https://caniuse.com/?search=object-fit [2] https://developer.mozilla.org/de/docs/Web/CSS/object-fit * Add usePlayerObjectFit option Only consider `object-fit` CSS property when selecting playlists based on play size when `usePlayerObjectFit` option is `true` to make new behavior an opt-in. * chore: add object-fit option to the demo page --------- Co-authored-by: Dzianis Dashkevich <98566601+dzianis-dashkevich@users.noreply.github.com>
6 months ago
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>videojs-http-streaming Demo</title>
  6. <link rel="icon" href="logo.svg">
  7. <link href="node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet">
  8. <link href="node_modules/video.js/dist/video-js.css" rel="stylesheet">
  9. <link rel="stylesheet" href="node_modules/videojs-contrib-quality-menu/dist/videojs-contrib-quality-menu.css">
  10. <style>
  11. .form-check {
  12. background-color: hsl(0, 0%, 90%);
  13. margin-block: 0.5rem;
  14. padding: 0.25em 0.25em 0.25em 1.75em;
  15. width: 700px;
  16. width: fit-content;
  17. }
  18. #player-fixture {
  19. min-height: 250px;
  20. }
  21. #segment-metadata {
  22. list-style: none;
  23. }
  24. #segment-metadata pre {
  25. overflow: scroll;
  26. }
  27. button.btn-outline-secondary:hover svg,
  28. button.btn-success svg,
  29. button.btn-danger svg {
  30. fill: white;
  31. }
  32. </style>
  33. </head>
  34. <body class="m-4">
  35. <header class="container-fluid">
  36. <a href="https://github.com/videojs/http-streaming" class="d-flex align-items-center pb-3 mb-5 border-bottom" style="height: 4em">
  37. <img src="./logo.svg" alt="VHS logo showcasing a VHS tape with the Video.js logo on the label" class="rounded mh-100">
  38. <span class="fs-1 ps-2">VHS: videojs-http-streaming</span>
  39. </a>
  40. </header>
  41. <div id="player-fixture" class="container-fluid pb-3 mb-3"></div>
  42. <ul class="nav nav-tabs container-fluid mb-3" id="myTab" role="tablist">
  43. <li class="nav-item" role="presentation">
  44. <button class="nav-link active" id="home-tab" data-bs-toggle="tab" data-bs-target="#sources" type="button" role="tab" aria-selected="true">Sources</button>
  45. </li>
  46. <li class="nav-item" role="presentation">
  47. <button class="nav-link" id="contact-tab" data-bs-toggle="tab" data-bs-target="#options" type="button" role="tab" aria-selected="false">Options</button>
  48. </li>
  49. <li class="nav-item" role="presentation">
  50. <button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#levels" type="button" role="tab" aria-selected="false">Representations</button>
  51. </li>
  52. <li class="nav-item" role="presentation">
  53. <button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#player-stats" type="button" role="tab" aria-selected="false">Player Stats</button>
  54. </li>
  55. <li class="nav-item" role="presentation">
  56. <button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#content-steering" type="button" role="tab" aria-selected="false">Content Steering</button>
  57. </li>
  58. <li class="nav-item" role="presentation">
  59. <button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#export-logs" type="button" role="tab" aria-selected="false">Logs</button>
  60. </li>
  61. </ul>
  62. <div class="tab-content container-fluid">
  63. <div class="tab-pane active" id="sources" role="tabpanel">
  64. <div class="input-group mb-2">
  65. <span class="input-group-text"><label for=load-source>Preloaded Sources</label></span>
  66. <select id=load-source class="form-select">
  67. <optgroup label="hls">
  68. </optgroup>
  69. <optgroup label="dash">
  70. </optgroup>
  71. <optgroup label="drm">
  72. </optgroup>
  73. <optgroup label="live">
  74. </optgroup>
  75. <optgroup label="low latency live">
  76. </optgroup>
  77. <optgroup label="json manifest object">
  78. </optgroup>
  79. </select>
  80. </div>
  81. <label for=url class="form-label">Source URL</label>
  82. <div class="input-group">
  83. <span class="input-group-text"><label for=url>Url</label></span>
  84. <input id=url type=url class="form-control">
  85. </div>
  86. <label for=type class="form-label">Source Type (uses url extension if blank, usually application/x-mpegURL or application/dash+xml)</label>
  87. <div class="input-group">
  88. <span class="input-group-text"><label for=type>Type</label></span>
  89. <input id=type type=text class="form-control">
  90. </div>
  91. <label for="keysystems" class="form-label">Optional Keystems JSON:</label>
  92. <div class="input-group">
  93. <span class="input-group-text"><label for=keysystems>keySystems JSON</label></span>
  94. <textarea id=keysystems cols=100 rows=5 class="form-control"></textarea>
  95. </div>
  96. <button id=load-url type=button class="btn btn-primary my-2">Load</button>
  97. </div>
  98. <div class="tab-pane" id="options" role="tabpanel">
  99. <div class="options">
  100. <div class="form-check">
  101. <input id=minified type="checkbox" class="form-check-input">
  102. <label class="form-check-label" for="minified">Minified VHS (reloads player)</label>
  103. </div>
  104. <div class="form-check">
  105. <input id=sync-workers type="checkbox" class="form-check-input">
  106. <label class="form-check-label" for="sync-workers">Synchronous Web Workers (reloads player)</label>
  107. </div>
  108. <div class="form-check">
  109. <input id=liveui type="checkbox" class="form-check-input" checked>
  110. <label class="form-check-label" for="liveui">Enable the live UI (reloads player)</label>
  111. </div>
  112. <div class="form-check">
  113. <input id=fluid type="checkbox" class="form-check-input">
  114. <label class="form-check-label" for="fluid">Fluid mode</label>
  115. </div>
  116. <div class="form-check">
  117. <input id=debug type="checkbox" class="form-check-input">
  118. <label class="form-check-label" for="debug">Debug Logging</label>
  119. </div>
  120. <div class="form-check">
  121. <input id=muted type="checkbox" class="form-check-input">
  122. <label class="form-check-label" for="muted">Muted</label>
  123. </div>
  124. <div class="form-check">
  125. <input id=autoplay type="checkbox" class="form-check-input">
  126. <label class="form-check-label" for="autoplay">Autoplay</label>
  127. </div>
  128. <div class="form-check">
  129. <input id=network-info type="checkbox" class="form-check-input" checked>
  130. <label class="form-check-label" for="network-info">Use networkInfo API for bandwidth estimations (reloads player)</label>
  131. </div>
  132. <div class="form-check">
  133. <input id=dts-offset type="checkbox" class="form-check-input">
  134. <label class="form-check-label" for="dts-offset">Use DTS instead of PTS for Timestamp Offset calculation (reloads player)</label>
  135. </div>
  136. <div class="form-check">
  137. <input id=llhls type="checkbox" class="form-check-input">
  138. <label class="form-check-label" for="llhls">[EXPERIMENTAL] Enables support for ll-hls (reloads player)</label>
  139. </div>
  140. <div class="form-check">
  141. <input id=buffer-water type="checkbox" class="form-check-input">
  142. <label class="form-check-label" for="buffer-water">[EXPERIMENTAL] Use Buffer Level for ABR (reloads player)</label>
  143. </div>
  144. <div class="form-check">
  145. <input id=exact-manifest-timings type="checkbox" class="form-check-input">
  146. <label class="form-check-label" for="exact-manifest-timings">[EXPERIMENTAL] Use exact manifest timings for segment choices (reloads player)</label>
  147. </div>
  148. <div class="form-check">
  149. <input id=pixel-diff-selector type="checkbox" class="form-check-input">
  150. <label class="form-check-label" for="pixel-diff-selector">[EXPERIMENTAL] Use the Pixel difference resolution selector (reloads player)</label>
  151. </div>
  152. <div class="form-check">
  153. <input id=object-fit type="checkbox" class="form-check-input">
  154. <label class="form-check-label" for="object-fit">Account Object-fit for resolution selection (reloads player)</label>
  155. </div>
  156. <div class="form-check">
  157. <input id=override-native type="checkbox" class="form-check-input" checked>
  158. <label class="form-check-label" for="override-native">Override Native (reloads player)</label>
  159. </div>
  160. <div class="form-check">
  161. <input id=use-mms type="checkbox" class="form-check-input" checked>
  162. <label class="form-check-label" for="use-mms">[EXPERIMENTAL] Use ManagedMediaSource if available. Use in combination with override native (reloads player)</label>
  163. </div>
  164. <div class="form-check">
  165. <input id=mirror-source type="checkbox" class="form-check-input" checked>
  166. <label class="form-check-label" for="mirror-source">Mirror sources from player.src (reloads player, uses EXPERIMENTAL sourceset option)</label>
  167. </div>
  168. <div class="form-check">
  169. <input id="forced-subtitles" type="checkbox" class="form-check-input">
  170. <label class="form-check-label" for="forced-subtitles">Use Forced Subtitles (reloads player)</label>
  171. </div>
  172. <div class="form-check">
  173. <input id="native-text-tracks" type="checkbox" class="form-check-input">
  174. <label class="form-check-label" for="native-text-tracks">Use native text tracks (reloads player)</label>
  175. </div>
  176. <div class="input-group">
  177. <span class="input-group-text"><label for=preload>Preload (reloads player)</label></span>
  178. <select id=preload class="form-select">
  179. <option selected>auto</option>
  180. <option>none</option>
  181. <option>metadata</option>
  182. </select>
  183. </div>
  184. </div>
  185. </div>
  186. <div class="tab-pane" id="levels" role="tabpanel">
  187. <div class="input-group">
  188. <span class="input-group-text"><label for=representations>Representations</label></span>
  189. <select id='representations' class="form-select"></select>
  190. </div>
  191. </div>
  192. <div class="tab-pane" id="player-stats" role="tabpanel">
  193. <div class="row">
  194. <div class="player-stats col-4">
  195. <dl>
  196. <dt>Current Time:</dt>
  197. <dd class="current-time-stat">0</dd>
  198. <dt>Buffered:</dt>
  199. <dd class="buffered-stat">-</dd>
  200. <dt>Video Buffered:</dt>
  201. <dd class="video-buffered-stat">-</dd>
  202. <dt>Audio Buffered:</dt>
  203. <dd class="audio-buffered-stat">-</dd>
  204. <dt>Seekable:</dt>
  205. <dd><span class="seekable-start-stat">-</span> - <span class="seekable-end-stat">-</span></dd>
  206. <dt>Video Bitrate:</dt>
  207. <dd class="video-bitrate-stat">0 kbps</dd>
  208. <dt>Measured Bitrate:</dt>
  209. <dd class="measured-bitrate-stat">0 kbps</dd>
  210. <dt>Video Timestamp Offset</dt>
  211. <dd class="video-timestampoffset">0</dd>
  212. <dt>Audio Timestamp Offset</dt>
  213. <dd class="audio-timestampoffset">0</dd>
  214. </dl>
  215. </div>
  216. <ul id="segment-metadata" class="col-8"></ul>
  217. </div>
  218. </div>
  219. <div class="tab-pane" id="content-steering" role="tabpanel">
  220. <div class="row">
  221. <div class="content-steering col-8">
  222. <dl>
  223. <dt>Current Pathway:</dt>
  224. <dd class="current-pathway"></dd>
  225. <dt>Available Pathways:</dt>
  226. <dd class="available-pathways"></dd>
  227. <dt>Steering Manifest:</dt>
  228. <dd class="steering-manifest"></dd>
  229. </dl>
  230. </div>
  231. </div>
  232. </div>
  233. <div class="tab-pane" id="export-logs" role="historypanel">
  234. <div class="row">
  235. <div class="export-logs col-8">
  236. <p>Download or copy the player logs, which should be included when submitting a playback issue.</p>
  237. <p>To insert a comment into the player log, use <code>player.log()</code> in the console, e.g. <code>player.log('Seeking to 500');player.currentTime(500);</code></p>
  238. <button id="download-logs" class="btn btn-outline-secondary">
  239. <span class="icon">
  240. <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368">
  241. <path d="M480-320 280-520l56-58 104 104v-326h80v326l104-104 56 58-200 200ZM240-160q-33 0-56.5-23.5T160-240v-120h80v120h480v-120h80v120q0 33-23.5 56.5T720-160H240Z"/>
  242. </svg>
  243. </span>
  244. Download player logs
  245. </button>
  246. <button id="copy-logs" class="btn btn-outline-secondary">
  247. <span class="icon">
  248. <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368">
  249. <path d="M360-240q-33 0-56.5-23.5T280-320v-480q0-33 23.5-56.5T360-880h360q33 0 56.5 23.5T800-800v480q0 33-23.5 56.5T720-240H360Zm0-80h360v-480H360v480ZM200-80q-33 0-56.5-23.5T120-160v-560h80v560h440v80H200Zm160-240v-480 480Z"/>
  250. </svg>
  251. </span>
  252. Copy player logs to clipboard
  253. </button>
  254. </div>
  255. </div>
  256. </div>
  257. </div>
  258. <footer class="text-center p-3" id=unit-test-link>
  259. <a href="test/debug.html">Run unit tests</a>
  260. </footer>
  261. <script>
  262. var unitTestLink = document.getElementById('unit-test-link');
  263. // removal test run link on netlify, as we cannot run tests there.
  264. if ((/netlify.app/).test(window.location.host)) {
  265. unitTestLink.remove();
  266. }
  267. </script>
  268. <script src="node_modules/bootstrap/dist/js/bootstrap.js"></script>
  269. <script src="scripts/index.js"></script>
  270. <script>
  271. window.startDemo(function(player) {
  272. // do something with setup player
  273. });
  274. </script>
  275. </body>
  276. </html>