movi-player 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +23 -12
  2. package/dist/core/MoviPlayer.d.ts +65 -1
  3. package/dist/core/MoviPlayer.d.ts.map +1 -1
  4. package/dist/core/MoviPlayer.js +245 -10
  5. package/dist/core/MoviPlayer.js.map +1 -1
  6. package/dist/decode/AudioDecoder.d.ts +4 -0
  7. package/dist/decode/AudioDecoder.d.ts.map +1 -1
  8. package/dist/decode/AudioDecoder.js +14 -0
  9. package/dist/decode/AudioDecoder.js.map +1 -1
  10. package/dist/decode/SoftwareAudioDecoder.d.ts +12 -0
  11. package/dist/decode/SoftwareAudioDecoder.d.ts.map +1 -1
  12. package/dist/decode/SoftwareAudioDecoder.js +91 -2
  13. package/dist/decode/SoftwareAudioDecoder.js.map +1 -1
  14. package/dist/decode/VideoDecoder.d.ts.map +1 -1
  15. package/dist/decode/VideoDecoder.js +7 -0
  16. package/dist/decode/VideoDecoder.js.map +1 -1
  17. package/dist/demuxer.cjs +1863 -1788
  18. package/dist/demuxer.js +1722 -1649
  19. package/dist/element.cjs +2987 -1933
  20. package/dist/element.d.ts +48 -0
  21. package/dist/element.d.ts.map +1 -1
  22. package/dist/element.js +8008 -6950
  23. package/dist/element.js.map +1 -1
  24. package/dist/index.cjs +2987 -1933
  25. package/dist/index.js +8008 -6950
  26. package/dist/player.cjs +3102 -2970
  27. package/dist/player.js +1812 -1680
  28. package/dist/render/AudioRenderer.d.ts +30 -0
  29. package/dist/render/AudioRenderer.d.ts.map +1 -1
  30. package/dist/render/AudioRenderer.js +127 -44
  31. package/dist/render/AudioRenderer.js.map +1 -1
  32. package/dist/render/CanvasRenderer.d.ts +31 -0
  33. package/dist/render/CanvasRenderer.d.ts.map +1 -1
  34. package/dist/render/CanvasRenderer.js +81 -25
  35. package/dist/render/CanvasRenderer.js.map +1 -1
  36. package/dist/render/DASHPlayerWrapper.d.ts.map +1 -1
  37. package/dist/render/DASHPlayerWrapper.js +3 -1
  38. package/dist/render/DASHPlayerWrapper.js.map +1 -1
  39. package/dist/render/HLSPlayerWrapper.d.ts.map +1 -1
  40. package/dist/render/HLSPlayerWrapper.js +3 -1
  41. package/dist/render/HLSPlayerWrapper.js.map +1 -1
  42. package/dist/render/MoviElement.d.ts +237 -0
  43. package/dist/render/MoviElement.d.ts.map +1 -1
  44. package/dist/render/MoviElement.js +2154 -232
  45. package/dist/render/MoviElement.js.map +1 -1
  46. package/dist/render/ShakaPlayerWrapper.d.ts.map +1 -1
  47. package/dist/render/ShakaPlayerWrapper.js +3 -1
  48. package/dist/render/ShakaPlayerWrapper.js.map +1 -1
  49. package/dist/source/HttpSource.d.ts +3 -0
  50. package/dist/source/HttpSource.d.ts.map +1 -1
  51. package/dist/source/HttpSource.js +81 -22
  52. package/dist/source/HttpSource.js.map +1 -1
  53. package/dist/utils/QoE.d.ts +126 -0
  54. package/dist/utils/QoE.d.ts.map +1 -0
  55. package/dist/utils/QoE.js +192 -0
  56. package/dist/utils/QoE.js.map +1 -0
  57. package/dist/utils/ThumbnailRenderer.d.ts +25 -0
  58. package/dist/utils/ThumbnailRenderer.d.ts.map +1 -1
  59. package/dist/utils/ThumbnailRenderer.js +195 -2
  60. package/dist/utils/ThumbnailRenderer.js.map +1 -1
  61. package/dist/wasm/bindings.d.ts +8 -0
  62. package/dist/wasm/bindings.d.ts.map +1 -1
  63. package/dist/wasm/bindings.js +31 -7
  64. package/dist/wasm/bindings.js.map +1 -1
  65. package/dist/wasm/movi.js +0 -0
  66. package/package.json +3 -2
@@ -867,6 +867,37 @@ export class CanvasRenderer {
867
867
  isVR360Enabled() {
868
868
  return this.vr360Enabled;
869
869
  }
870
+ /**
871
+ * The currently-displayed decoded VideoFrame (or null). Used as a fallback
872
+ * capture source when reading the WebGL canvas back via toDataURL comes out
873
+ * blank — some GPUs return an all-black buffer for hardware-decoded frames
874
+ * even with preserveDrawingBuffer. The frame is owned by the renderer and may
875
+ * be closed on the next present, so consume it synchronously.
876
+ */
877
+ getCurrentFrame() {
878
+ return this.lastRenderedFrame;
879
+ }
880
+ /**
881
+ * Snapshot the live 360° camera + projection so a thumbnail/preview can be
882
+ * reprojected to exactly what the user currently sees. Returns the CURRENT
883
+ * (rendered, post-spring) yaw/pitch/fov — i.e. the on-screen view, not the
884
+ * drag target. Null when 360 is off.
885
+ */
886
+ getVRView() {
887
+ if (!this.vr360Enabled)
888
+ return null;
889
+ return {
890
+ yaw: this.vrYaw,
891
+ pitch: this.vrPitch,
892
+ fov: this.vrFov,
893
+ aspect: this.height > 0 ? this.width / this.height : 16 / 9,
894
+ half: this.vrHalf,
895
+ fisheye: this.vrFisheye,
896
+ sbs: this.vrStereoSbs,
897
+ stereographic: this.vrStereographic,
898
+ texAspect: this.vrTexAspect,
899
+ };
900
+ }
870
901
  /** Choose the VR projection/layout:
871
902
  * - half: false = full 360° equirectangular, true = front-hemisphere (VR180).
872
903
  * - fisheye: true = equidistant fisheye instead of equirectangular.
@@ -1282,20 +1313,26 @@ export class CanvasRenderer {
1282
1313
  }
1283
1314
  // Update overlay dimensions
1284
1315
  if (this.subtitleOverlay) {
1285
- // Overlay matches container (unrotated visual area), not canvas buffer
1286
- // So we use the original input width/height (Container WxH)
1287
- const canvasWidth = width;
1288
- const canvasHeight = height;
1289
- // Calculate responsive bottom padding
1290
- const bottomPadding = CanvasRenderer.computeSubtitleBottomPadding(canvasHeight);
1316
+ // The overlay lives in the video's own (pre-rotation) coordinate space
1317
+ // and is then rotated to match the video, so captions ride the video's
1318
+ // bottom edge and turn with it. For 90°/270° the box is swapped (H×W)
1319
+ // and centred in the container so that, once rotated around its centre,
1320
+ // it maps back onto the video area.
1321
+ const rot = (((this.rotation ?? 0) % 360) + 360) % 360;
1322
+ const swapped = rot === 90 || rot === 270;
1323
+ const overlayWidth = swapped ? height : width;
1324
+ const overlayHeight = swapped ? width : height;
1325
+ // Responsive bottom padding is relative to the video's visual height
1326
+ // in its own frame (overlayHeight), not the container.
1327
+ const bottomPadding = CanvasRenderer.computeSubtitleBottomPadding(overlayHeight);
1291
1328
  // Reset overlay positioning to ensure it stays aligned with canvas
1292
1329
  this.subtitleOverlay.style.position = "absolute";
1293
- this.subtitleOverlay.style.top = "0";
1294
- this.subtitleOverlay.style.left = "0";
1295
1330
  this.subtitleOverlay.style.right = "auto";
1296
1331
  this.subtitleOverlay.style.bottom = "auto";
1297
- this.subtitleOverlay.style.width = `${canvasWidth}px`;
1298
- this.subtitleOverlay.style.height = `${canvasHeight}px`;
1332
+ this.subtitleOverlay.style.width = `${overlayWidth}px`;
1333
+ this.subtitleOverlay.style.height = `${overlayHeight}px`;
1334
+ this.subtitleOverlay.style.left = `${(width - overlayWidth) / 2}px`;
1335
+ this.subtitleOverlay.style.top = `${(height - overlayHeight) / 2}px`;
1299
1336
  this.subtitleOverlay.style.margin = "0";
1300
1337
  this.subtitleOverlay.style.padding = "0";
1301
1338
  const effectivePadding = this.subtitleControlsPadding > 0 ? this.subtitleControlsPadding : bottomPadding;
@@ -1304,7 +1341,8 @@ export class CanvasRenderer {
1304
1341
  this.subtitleOverlay.style.flexDirection = "column";
1305
1342
  this.subtitleOverlay.style.justifyContent = "flex-end";
1306
1343
  this.subtitleOverlay.style.alignItems = "center";
1307
- this.subtitleOverlay.style.transform = "none";
1344
+ this.subtitleOverlay.style.transformOrigin = "center center";
1345
+ this.subtitleOverlay.style.transform = rot ? `rotate(${rot}deg)` : "none";
1308
1346
  this.subtitleOverlay.style.boxSizing = "border-box";
1309
1347
  // Schedule a re-render so the on-screen subtitle picks up the
1310
1348
  // new dimensions. Coalesce via rAF — a window drag bursts
@@ -2352,23 +2390,31 @@ export class CanvasRenderer {
2352
2390
  // Override CSS defaults that might interfere
2353
2391
  // The overlay should cover the entire canvas area and not overflow
2354
2392
  // Position overlay at bottom center (above controls), same as text subtitles
2393
+ // Rotate the overlay to match the video (same approach as text captions),
2394
+ // swapping to the video's own frame dimensions for 90°/270° and centring
2395
+ // so it maps back onto the video once rotated.
2396
+ const rotImg = (((this.rotation ?? 0) % 360) + 360) % 360;
2397
+ const swappedImg = rotImg === 90 || rotImg === 270;
2398
+ const ovW = swappedImg ? canvasHeight : canvasWidth;
2399
+ const ovH = swappedImg ? canvasWidth : canvasHeight;
2355
2400
  this.subtitleOverlay.style.position = "absolute";
2356
- this.subtitleOverlay.style.top = "0";
2357
- this.subtitleOverlay.style.left = "0";
2358
2401
  this.subtitleOverlay.style.right = "auto";
2359
2402
  this.subtitleOverlay.style.bottom = "auto";
2360
- this.subtitleOverlay.style.width = `${canvasWidth}px`;
2361
- this.subtitleOverlay.style.height = `${canvasHeight}px`;
2403
+ this.subtitleOverlay.style.width = `${ovW}px`;
2404
+ this.subtitleOverlay.style.height = `${ovH}px`;
2405
+ this.subtitleOverlay.style.left = `${(canvasWidth - ovW) / 2}px`;
2406
+ this.subtitleOverlay.style.top = `${(canvasHeight - ovH) / 2}px`;
2362
2407
  this.subtitleOverlay.style.pointerEvents = "none";
2363
2408
  // zIndex controlled by CSS (.movi-subtitle-overlay)
2364
- this.subtitleOverlay.style.transform = "none";
2409
+ this.subtitleOverlay.style.transformOrigin = "center center";
2410
+ this.subtitleOverlay.style.transform = rotImg ? `rotate(${rotImg}deg)` : "none";
2365
2411
  this.subtitleOverlay.style.display = "flex";
2366
2412
  this.subtitleOverlay.style.flexDirection = "column";
2367
2413
  this.subtitleOverlay.style.justifyContent = "flex-end";
2368
2414
  this.subtitleOverlay.style.alignItems = "center";
2369
2415
  this.subtitleOverlay.style.overflow = "hidden"; // Prevent overflow outside canvas
2370
2416
  this.subtitleOverlay.style.padding = "0";
2371
- const bottomPaddingImg = CanvasRenderer.computeSubtitleBottomPadding(canvasHeight);
2417
+ const bottomPaddingImg = CanvasRenderer.computeSubtitleBottomPadding(ovH);
2372
2418
  const effectivePaddingImg = this.subtitleControlsPadding > 0 ? this.subtitleControlsPadding : bottomPaddingImg;
2373
2419
  this.subtitleOverlay.style.paddingBottom = `${effectivePaddingImg}px`;
2374
2420
  this.subtitleOverlay.style.textAlign = "center";
@@ -2408,8 +2454,8 @@ export class CanvasRenderer {
2408
2454
  imgElement.src = dataUrl;
2409
2455
  imgElement.style.width = `${scaledWidth}px`;
2410
2456
  imgElement.style.height = `${scaledHeight}px`;
2411
- imgElement.style.maxWidth = `${canvasWidth}px`; // Ensure image doesn't exceed canvas width
2412
- imgElement.style.maxHeight = `${canvasHeight}px`; // Ensure image doesn't exceed canvas height
2457
+ imgElement.style.maxWidth = `${ovW}px`; // Ensure image doesn't exceed the (rotated) video frame width
2458
+ imgElement.style.maxHeight = `${ovH}px`; // Ensure image doesn't exceed the (rotated) video frame height
2413
2459
  imgElement.style.objectFit = "contain"; // Preserve aspect ratio
2414
2460
  imgElement.style.display = "block";
2415
2461
  imgElement.style.visibility = "visible";
@@ -2593,19 +2639,29 @@ export class CanvasRenderer {
2593
2639
  const rect = canvasEl?.getBoundingClientRect();
2594
2640
  const overlayW = rect?.width || displayWidth;
2595
2641
  const overlayH = rect?.height || displayHeight;
2596
- const bottomPadding = CanvasRenderer.computeSubtitleBottomPadding(overlayH);
2642
+ // Rotate the caption overlay to ride the video: swap to the video's own
2643
+ // frame box for 90°/270° and centre it so, once rotated around its centre,
2644
+ // it lands back over the video (see resize() for the same approach). This
2645
+ // render path runs ~60×/s and previously forced transform:none, undoing
2646
+ // the rotation set on rotate — so the rotation has to be re-applied here.
2647
+ const rotTxt = (((this.rotation ?? 0) % 360) + 360) % 360;
2648
+ const swappedTxt = rotTxt === 90 || rotTxt === 270;
2649
+ const ovTxtW = swappedTxt ? overlayH : overlayW;
2650
+ const ovTxtH = swappedTxt ? overlayW : overlayH;
2651
+ const bottomPadding = CanvasRenderer.computeSubtitleBottomPadding(ovTxtH);
2597
2652
  this.subtitleOverlay.style.position = "absolute";
2598
- this.subtitleOverlay.style.top = "0";
2599
- this.subtitleOverlay.style.left = "0";
2600
2653
  this.subtitleOverlay.style.right = "auto";
2601
2654
  this.subtitleOverlay.style.bottom = "auto";
2602
- this.subtitleOverlay.style.width = `${overlayW}px`;
2603
- this.subtitleOverlay.style.height = `${overlayH}px`;
2655
+ this.subtitleOverlay.style.width = `${ovTxtW}px`;
2656
+ this.subtitleOverlay.style.height = `${ovTxtH}px`;
2657
+ this.subtitleOverlay.style.left = `${(overlayW - ovTxtW) / 2}px`;
2658
+ this.subtitleOverlay.style.top = `${(overlayH - ovTxtH) / 2}px`;
2604
2659
  this.subtitleOverlay.style.margin = "0";
2605
2660
  this.subtitleOverlay.style.padding = "0";
2606
2661
  const effectivePad = this.subtitleControlsPadding > 0 ? this.subtitleControlsPadding : bottomPadding;
2607
2662
  this.subtitleOverlay.style.paddingBottom = `${effectivePad}px`;
2608
- this.subtitleOverlay.style.transform = "none";
2663
+ this.subtitleOverlay.style.transformOrigin = "center center";
2664
+ this.subtitleOverlay.style.transform = rotTxt ? `rotate(${rotTxt}deg)` : "none";
2609
2665
  this.subtitleOverlay.style.boxSizing = "border-box";
2610
2666
  this.subtitleOverlay.style.display = "flex";
2611
2667
  this.subtitleOverlay.style.flexDirection = "column";