@splinetool/viewer 2.0.8 → 2.0.10

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.
package/build/hana-ui.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /* @ts-self-types="./hana.d.ts" */
2
- import { loadImageFromBytes, loadImageFromUrl, loadVideoFromBytes, loadVideoFromUrl } from './snippets/hana-82e9eb82a79dead4/js-imports.js';
2
+ import { disposeVideo, loadImageFromBytes, loadImageFromUrl, loadVideoFromBytes, loadVideoFromUrl, videoFrameToken } from './snippets/hana-82e9eb82a79dead4/js-imports.js';
3
3
 
4
4
 
5
5
  export class DeviceLostPromise {
@@ -331,6 +331,22 @@ export class SceneController {
331
331
  const ret = wasm.scenecontroller_centerVectorData(this.__wbg_ptr);
332
332
  return takeObject(ret);
333
333
  }
334
+ /**
335
+ * Drop the export-time frame override for one video asset (see
336
+ * `setVideoExportFrame`): the renderer resumes sampling its `<video>`
337
+ * element and `seekVideos` drives it again. The export loop's per-video
338
+ * fallback for a WebCodecs source that stopped delivering mid-export.
339
+ * @param {ID} id
340
+ */
341
+ clearVideoExportFrame(id) {
342
+ wasm.scenecontroller_clearVideoExportFrame(this.__wbg_ptr, addHeapObject(id));
343
+ }
344
+ /**
345
+ * Drop every export-time video frame override (see `setVideoExportFrame`).
346
+ */
347
+ clearVideoExportFrames() {
348
+ wasm.scenecontroller_clearVideoExportFrames(this.__wbg_ptr);
349
+ }
334
350
  /**
335
351
  * @param {any} network
336
352
  * @param {Float2} size
@@ -898,8 +914,11 @@ export class SceneController {
898
914
  return takeObject(ret);
899
915
  }
900
916
  /**
917
+ * Resolves to whether any video seeked backward (wrapped across its
918
+ * loop point) — the export loop renders throwaway frames after a wrap
919
+ * to keep cold-path video→RGB conversion transients off captured frames.
901
920
  * @param {number} dt
902
- * @returns {Promise<void>}
921
+ * @returns {Promise<boolean>}
903
922
  */
904
923
  seekVideos(dt) {
905
924
  const ret = wasm.scenecontroller_seekVideos(this.__wbg_ptr, dt);
@@ -983,6 +1002,20 @@ export class SceneController {
983
1002
  setVectorTool(tool) {
984
1003
  wasm.scenecontroller_setVectorTool(this.__wbg_ptr, addHeapObject(tool));
985
1004
  }
1005
+ /**
1006
+ * Register an export-time frame override for a video asset: the renderer
1007
+ * uploads this WebCodecs frame instead of sampling the `<video>` element,
1008
+ * and `seekVideos` leaves the element alone. The caller keeps ownership
1009
+ * of the frame — it must stay unclosed until the override is replaced or
1010
+ * cleared, and `clearVideoExportFrames` must run before anything renders
1011
+ * outside the export loop. WebGPU backend only (`isWebgpu`); the WebGL
1012
+ * path still samples the element.
1013
+ * @param {ID} id
1014
+ * @param {VideoFrame} frame
1015
+ */
1016
+ setVideoExportFrame(id, frame) {
1017
+ wasm.scenecontroller_setVideoExportFrame(this.__wbg_ptr, addHeapObject(id), addHeapObject(frame));
1018
+ }
986
1019
  /**
987
1020
  * Snap a world-space resize handle position against other (non-selected) vertices.
988
1021
  * `snap_x`/`snap_y` indicate which axes may snap. Returns the snapped world position.
@@ -1191,6 +1224,27 @@ export class SceneController {
1191
1224
  vectorStopContinueSubpath() {
1192
1225
  wasm.scenecontroller_vectorStopContinueSubpath(this.__wbg_ptr);
1193
1226
  }
1227
+ /**
1228
+ * The video fills the export loop should decode with WebCodecs, as
1229
+ * `{ id, src }` objects (`src` is the loaded `<video>` element's source:
1230
+ * the asset's own URL for streamed loads, a `blob:` URL for byte-loaded
1231
+ * ones — either way the same URL the element itself fetched). Enumerated from the start events' video actions — the exact set
1232
+ * `seekVideos` drives — so the WebCodecs path animates precisely the
1233
+ * fills the element path would: a fill without a play action stays a
1234
+ * static frame on both paths and both backends. Scoped to actions
1235
+ * targeting `frame_id`'s subtree on purpose: enumerating the whole
1236
+ * document put a live decoder behind every video in the file (74 in one
1237
+ * real doc), turning a 5s export into 74 decode streams. Videos still
1238
+ * loading are omitted, and videos outside the subtree stay on the
1239
+ * seek-driven element path — both remain correct, just less
1240
+ * stable/deterministic.
1241
+ * @param {ID} frame_id
1242
+ * @returns {Array<any>}
1243
+ */
1244
+ videoFillSources(frame_id) {
1245
+ const ret = wasm.scenecontroller_videoFillSources(this.__wbg_ptr, addHeapObject(frame_id));
1246
+ return takeObject(ret);
1247
+ }
1194
1248
  /**
1195
1249
  * @param {Float2} pos
1196
1250
  * @returns {Float2}
@@ -1764,6 +1818,17 @@ function __wbg_get_imports() {
1764
1818
  __wbg_dispatchWorkgroups_afb2344298c62227: function(arg0, arg1, arg2, arg3) {
1765
1819
  getObject(arg0).dispatchWorkgroups(arg1 >>> 0, arg2 >>> 0, arg3 >>> 0);
1766
1820
  },
1821
+ __wbg_displayHeight_465673c1823a1db3: function(arg0) {
1822
+ const ret = getObject(arg0).displayHeight;
1823
+ return ret;
1824
+ },
1825
+ __wbg_displayWidth_73729fd8eb965076: function(arg0) {
1826
+ const ret = getObject(arg0).displayWidth;
1827
+ return ret;
1828
+ },
1829
+ __wbg_disposeVideo_c94c1ccd57a63f68: function(arg0) {
1830
+ disposeVideo(getObject(arg0));
1831
+ },
1767
1832
  __wbg_document_aceb08cd6489baf5: function(arg0) {
1768
1833
  const ret = getObject(arg0).document;
1769
1834
  return isLikeNone(ret) ? 0 : addHeapObject(ret);
@@ -2410,7 +2475,7 @@ function __wbg_get_imports() {
2410
2475
  const a = state0.a;
2411
2476
  state0.a = 0;
2412
2477
  try {
2413
- return __wasm_bindgen_func_elem_14112(a, state0.b, arg0, arg1);
2478
+ return __wasm_bindgen_func_elem_14186(a, state0.b, arg0, arg1);
2414
2479
  } finally {
2415
2480
  state0.a = a;
2416
2481
  }
@@ -2456,7 +2521,7 @@ function __wbg_get_imports() {
2456
2521
  const a = state0.a;
2457
2522
  state0.a = 0;
2458
2523
  try {
2459
- return __wasm_bindgen_func_elem_14112(a, state0.b, arg0, arg1);
2524
+ return __wasm_bindgen_func_elem_14186(a, state0.b, arg0, arg1);
2460
2525
  } finally {
2461
2526
  state0.a = a;
2462
2527
  }
@@ -2583,6 +2648,10 @@ function __wbg_get_imports() {
2583
2648
  __wbg_readPixels_d00c1cae608273d8: function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) {
2584
2649
  getObject(arg0).readPixels(arg1, arg2, arg3, arg4, arg5 >>> 0, arg6 >>> 0, arg7);
2585
2650
  }, arguments); },
2651
+ __wbg_readyState_77b1b1db1b8c2a5f: function(arg0) {
2652
+ const ret = getObject(arg0).readyState;
2653
+ return ret;
2654
+ },
2586
2655
  __wbg_reason_b02cd587d1a09948: function(arg0) {
2587
2656
  const ret = getObject(arg0).reason;
2588
2657
  return (__wbindgen_enum_GpuDeviceLostReason.indexOf(ret) + 1 || 3) - 1;
@@ -3313,6 +3382,13 @@ function __wbg_get_imports() {
3313
3382
  const ret = getObject(arg0).size;
3314
3383
  return ret;
3315
3384
  },
3385
+ __wbg_src_f0e9281991fbd919: function(arg0, arg1) {
3386
+ const ret = getObject(arg1).src;
3387
+ const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
3388
+ const len1 = WASM_VECTOR_LEN;
3389
+ getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
3390
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
3391
+ },
3316
3392
  __wbg_stack_3b0d974bbf31e44f: function(arg0, arg1) {
3317
3393
  const ret = getObject(arg1).stack;
3318
3394
  const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
@@ -3531,6 +3607,10 @@ function __wbg_get_imports() {
3531
3607
  __wbg_vertexAttribPointer_3bc50c0431082f73: function(arg0, arg1, arg2, arg3, arg4, arg5, arg6) {
3532
3608
  getObject(arg0).vertexAttribPointer(arg1 >>> 0, arg2, arg3 >>> 0, arg4 !== 0, arg5, arg6);
3533
3609
  },
3610
+ __wbg_videoFrameToken_6f05524e85fa2b75: function(arg0) {
3611
+ const ret = videoFrameToken(getObject(arg0));
3612
+ return ret;
3613
+ },
3534
3614
  __wbg_videoHeight_e267fd1e74bb1b06: function(arg0) {
3535
3615
  const ret = getObject(arg0).videoHeight;
3536
3616
  return ret;
@@ -3556,48 +3636,48 @@ function __wbg_get_imports() {
3556
3636
  getObject(arg0).writeTexture(getObject(arg1), getArrayU8FromWasm0(arg2, arg3), getObject(arg4), getObject(arg5));
3557
3637
  }, arguments); },
3558
3638
  __wbindgen_cast_0000000000000001: function(arg0, arg1) {
3559
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1052, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3560
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_14110);
3639
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1054, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3640
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_14184);
3561
3641
  return addHeapObject(ret);
3562
3642
  },
3563
3643
  __wbindgen_cast_0000000000000002: function(arg0, arg1) {
3564
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 32, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
3565
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_5086);
3644
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 34, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
3645
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_5119);
3566
3646
  return addHeapObject(ret);
3567
3647
  },
3568
3648
  __wbindgen_cast_0000000000000003: function(arg0, arg1) {
3569
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("GPUComputePipeline")], shim_idx: 927, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3570
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_11070);
3649
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("GPUComputePipeline")], shim_idx: 929, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3650
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_11140);
3571
3651
  return addHeapObject(ret);
3572
3652
  },
3573
3653
  __wbindgen_cast_0000000000000004: function(arg0, arg1) {
3574
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("GPUDevice")], shim_idx: 927, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3575
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_11070_3);
3654
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("GPUDevice")], shim_idx: 929, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3655
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_11140_3);
3576
3656
  return addHeapObject(ret);
3577
3657
  },
3578
3658
  __wbindgen_cast_0000000000000005: function(arg0, arg1) {
3579
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("GPUDeviceLostInfo")], shim_idx: 928, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
3580
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_11071);
3659
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("GPUDeviceLostInfo")], shim_idx: 930, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
3660
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_11141);
3581
3661
  return addHeapObject(ret);
3582
3662
  },
3583
3663
  __wbindgen_cast_0000000000000006: function(arg0, arg1) {
3584
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("GPURenderPipeline")], shim_idx: 927, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3585
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_11070_5);
3664
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("GPURenderPipeline")], shim_idx: 929, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3665
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_11140_5);
3586
3666
  return addHeapObject(ret);
3587
3667
  },
3588
3668
  __wbindgen_cast_0000000000000007: function(arg0, arg1) {
3589
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("any")], shim_idx: 927, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3590
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_11070_6);
3669
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("any")], shim_idx: 929, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3670
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_11140_6);
3591
3671
  return addHeapObject(ret);
3592
3672
  },
3593
3673
  __wbindgen_cast_0000000000000008: function(arg0, arg1) {
3594
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("undefined")], shim_idx: 927, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3595
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_11070_7);
3674
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("undefined")], shim_idx: 929, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3675
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_11140_7);
3596
3676
  return addHeapObject(ret);
3597
3677
  },
3598
3678
  __wbindgen_cast_0000000000000009: function(arg0, arg1) {
3599
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1035, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
3600
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_12477);
3679
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1037, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
3680
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_12547);
3601
3681
  return addHeapObject(ret);
3602
3682
  },
3603
3683
  __wbindgen_cast_000000000000000a: function(arg0) {
@@ -3671,22 +3751,22 @@ function __wbg_get_imports() {
3671
3751
  };
3672
3752
  }
3673
3753
 
3674
- function __wasm_bindgen_func_elem_12477(arg0, arg1) {
3675
- wasm.__wasm_bindgen_func_elem_12477(arg0, arg1);
3754
+ function __wasm_bindgen_func_elem_12547(arg0, arg1) {
3755
+ wasm.__wasm_bindgen_func_elem_12547(arg0, arg1);
3676
3756
  }
3677
3757
 
3678
- function __wasm_bindgen_func_elem_5086(arg0, arg1, arg2) {
3679
- wasm.__wasm_bindgen_func_elem_5086(arg0, arg1, addHeapObject(arg2));
3758
+ function __wasm_bindgen_func_elem_5119(arg0, arg1, arg2) {
3759
+ wasm.__wasm_bindgen_func_elem_5119(arg0, arg1, addHeapObject(arg2));
3680
3760
  }
3681
3761
 
3682
- function __wasm_bindgen_func_elem_11071(arg0, arg1, arg2) {
3683
- wasm.__wasm_bindgen_func_elem_11071(arg0, arg1, addHeapObject(arg2));
3762
+ function __wasm_bindgen_func_elem_11141(arg0, arg1, arg2) {
3763
+ wasm.__wasm_bindgen_func_elem_11141(arg0, arg1, addHeapObject(arg2));
3684
3764
  }
3685
3765
 
3686
- function __wasm_bindgen_func_elem_14110(arg0, arg1, arg2) {
3766
+ function __wasm_bindgen_func_elem_14184(arg0, arg1, arg2) {
3687
3767
  try {
3688
3768
  const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
3689
- wasm.__wasm_bindgen_func_elem_14110(retptr, arg0, arg1, addHeapObject(arg2));
3769
+ wasm.__wasm_bindgen_func_elem_14184(retptr, arg0, arg1, addHeapObject(arg2));
3690
3770
  var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
3691
3771
  var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
3692
3772
  if (r1) {
@@ -3697,10 +3777,10 @@ function __wasm_bindgen_func_elem_14110(arg0, arg1, arg2) {
3697
3777
  }
3698
3778
  }
3699
3779
 
3700
- function __wasm_bindgen_func_elem_11070(arg0, arg1, arg2) {
3780
+ function __wasm_bindgen_func_elem_11140(arg0, arg1, arg2) {
3701
3781
  try {
3702
3782
  const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
3703
- wasm.__wasm_bindgen_func_elem_11070(retptr, arg0, arg1, addHeapObject(arg2));
3783
+ wasm.__wasm_bindgen_func_elem_11140(retptr, arg0, arg1, addHeapObject(arg2));
3704
3784
  var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
3705
3785
  var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
3706
3786
  if (r1) {
@@ -3711,10 +3791,10 @@ function __wasm_bindgen_func_elem_11070(arg0, arg1, arg2) {
3711
3791
  }
3712
3792
  }
3713
3793
 
3714
- function __wasm_bindgen_func_elem_11070_3(arg0, arg1, arg2) {
3794
+ function __wasm_bindgen_func_elem_11140_3(arg0, arg1, arg2) {
3715
3795
  try {
3716
3796
  const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
3717
- wasm.__wasm_bindgen_func_elem_11070_3(retptr, arg0, arg1, addHeapObject(arg2));
3797
+ wasm.__wasm_bindgen_func_elem_11140_3(retptr, arg0, arg1, addHeapObject(arg2));
3718
3798
  var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
3719
3799
  var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
3720
3800
  if (r1) {
@@ -3725,10 +3805,10 @@ function __wasm_bindgen_func_elem_11070_3(arg0, arg1, arg2) {
3725
3805
  }
3726
3806
  }
3727
3807
 
3728
- function __wasm_bindgen_func_elem_11070_5(arg0, arg1, arg2) {
3808
+ function __wasm_bindgen_func_elem_11140_5(arg0, arg1, arg2) {
3729
3809
  try {
3730
3810
  const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
3731
- wasm.__wasm_bindgen_func_elem_11070_5(retptr, arg0, arg1, addHeapObject(arg2));
3811
+ wasm.__wasm_bindgen_func_elem_11140_5(retptr, arg0, arg1, addHeapObject(arg2));
3732
3812
  var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
3733
3813
  var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
3734
3814
  if (r1) {
@@ -3739,10 +3819,10 @@ function __wasm_bindgen_func_elem_11070_5(arg0, arg1, arg2) {
3739
3819
  }
3740
3820
  }
3741
3821
 
3742
- function __wasm_bindgen_func_elem_11070_6(arg0, arg1, arg2) {
3822
+ function __wasm_bindgen_func_elem_11140_6(arg0, arg1, arg2) {
3743
3823
  try {
3744
3824
  const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
3745
- wasm.__wasm_bindgen_func_elem_11070_6(retptr, arg0, arg1, addHeapObject(arg2));
3825
+ wasm.__wasm_bindgen_func_elem_11140_6(retptr, arg0, arg1, addHeapObject(arg2));
3746
3826
  var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
3747
3827
  var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
3748
3828
  if (r1) {
@@ -3753,10 +3833,10 @@ function __wasm_bindgen_func_elem_11070_6(arg0, arg1, arg2) {
3753
3833
  }
3754
3834
  }
3755
3835
 
3756
- function __wasm_bindgen_func_elem_11070_7(arg0, arg1, arg2) {
3836
+ function __wasm_bindgen_func_elem_11140_7(arg0, arg1, arg2) {
3757
3837
  try {
3758
3838
  const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
3759
- wasm.__wasm_bindgen_func_elem_11070_7(retptr, arg0, arg1, addHeapObject(arg2));
3839
+ wasm.__wasm_bindgen_func_elem_11140_7(retptr, arg0, arg1, addHeapObject(arg2));
3760
3840
  var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
3761
3841
  var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
3762
3842
  if (r1) {
@@ -3767,8 +3847,8 @@ function __wasm_bindgen_func_elem_11070_7(arg0, arg1, arg2) {
3767
3847
  }
3768
3848
  }
3769
3849
 
3770
- function __wasm_bindgen_func_elem_14112(arg0, arg1, arg2, arg3) {
3771
- wasm.__wasm_bindgen_func_elem_14112(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3));
3850
+ function __wasm_bindgen_func_elem_14186(arg0, arg1, arg2, arg3) {
3851
+ wasm.__wasm_bindgen_func_elem_14186(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3));
3772
3852
  }
3773
3853
 
3774
3854
 
Binary file
@@ -1,93 +1,220 @@
1
-
2
1
  export async function loadImageFromBytes(bytes) {
3
- const blob = new Blob([bytes])
4
- const url = URL.createObjectURL(blob)
5
- return createImageBitmap(blob).finally(() => {
6
- URL.revokeObjectURL(url)
7
- })
2
+ return createImageBitmap(new Blob([bytes]))
8
3
  }
9
4
 
10
5
  export async function loadImageFromUrl(url) {
11
- return fetch(url)
12
- .then(response => {
13
- if (!response.ok) {
14
- throw new Error(`Failed to fetch image: ${response.statusText}`);
15
- }
16
- return response.blob();
17
- })
18
- .then(blob => createImageBitmap(blob));
6
+ return fetch(url)
7
+ .then((response) => {
8
+ if (!response.ok) {
9
+ throw new Error(`Failed to fetch image: ${response.statusText}`)
10
+ }
11
+ return response.blob()
12
+ })
13
+ .then((blob) => createImageBitmap(blob))
19
14
  }
20
15
 
21
16
  // Video Imports
22
- function setupVideo(video) {
23
- video.preload = "auto";
24
- video.autoplay = true; //iOs hack to show the first frame
25
- video.loop = true;
26
- video.muted = true;
27
- video.playsInline = true;
28
- video.currentTime = 0.01;
29
- video.load();
30
-
31
- video.onloadeddata = () => {
32
- video.muted = true;
33
- // DOMException: The play() request was interrupted by a new load request.
34
- // https://developers.google.com/web/updates/2017/06/play-request-was-interrupted
35
- const playPromise = video.play();
36
-
37
- if (playPromise !== undefined) {
38
- playPromise
39
- .then((_) => {
40
- // Automatic playback started! Do nothing all good!
41
- })
42
- .catch(() => {
43
- // for some reason the video could not be played (The play() request was interrupted by a new load request.)
44
- // so let's try to play it again
45
- video.play();
46
- }).finally(() => {
47
- video.pause();
48
- });
49
- }
50
- };
51
-
52
- let copyVideo = false;
53
- video.addEventListener("playing", function() {
54
- copyVideo = true;
55
- }, true);
56
-
57
- return new Promise((resolve) => {
58
- const checkVideo = () => {
59
- if (copyVideo) {
60
- resolve(video);
61
- } else {
62
- setTimeout(checkVideo, 10);
63
- }
64
- };
65
- checkVideo();
66
- });
67
- }
68
17
 
18
+ let videoFrameSeq = 0
69
19
 
70
- export async function loadVideoFromBytes(bytes) {
71
- const blob = new Blob([bytes])
72
- const video = document.createElement('video');
73
- video.src = URL.createObjectURL(blob);
74
- return setupVideo(video);
20
+ // Fallback tokens (see `videoFrameToken`) are the media clock offset per element
21
+ // by this stride, so two elements' ranges stay disjoint for any media under ~11.5 days.
22
+ const MEDIA_CLOCK_STRIDE = 1e6
23
+
24
+ // `HTMLMediaElement.HAVE_CURRENT_DATA` — a decoded frame the renderer can sample.
25
+ const HAVE_CURRENT_DATA = 2
26
+
27
+ // Generous, because a slow decode is not a failure — but bounded, because a source
28
+ // the browser silently declines must not wedge the load forever.
29
+ const VIDEO_READY_TIMEOUT_MS = 30000
30
+
31
+ // Maintains a token that changes exactly when the element presents a new frame,
32
+ // so the renderer can tell whether a GPU copy is needed at all.
33
+ //
34
+ // The callback chain re-arms itself and keeps `video` reachable until
35
+ // `disposeVideo` cancels it. It only fires on a presented frame, so a paused or
36
+ // stalled element costs nothing.
37
+ function trackPresentedFrames(video) {
38
+ if (typeof video.requestVideoFrameCallback !== 'function') {
39
+ // No presented-frame signal: `videoFrameToken` falls back to the media clock,
40
+ // offset per element to keep the guarantee below.
41
+ video.hanaClockBase = ++videoFrameSeq * MEDIA_CLOCK_STRIDE
42
+ return
43
+ }
44
+
45
+ // One global sequence, so no two elements ever report the same token: when an
46
+ // asset is replaced, the token the renderer still holds for the displaced
47
+ // element cannot collide with the replacement's.
48
+ video.hanaPresentedFrames = ++videoFrameSeq
49
+ const bump = () => {
50
+ // Disposal detaches the source, which fires `seeked` on the way out.
51
+ if (video.hanaDisposed) return
52
+ video.hanaPresentedFrames = ++videoFrameSeq
53
+ }
54
+
55
+ // A seek repaints a paused element without necessarily running the frame
56
+ // callback, and the video export seeks frame by frame with the element paused —
57
+ // it must never be handed the texture from the previous seek.
58
+ video.addEventListener('seeked', bump)
59
+
60
+ const onFrame = () => {
61
+ bump()
62
+ // Never re-arm a disposed element: that resurrects the cancelled chain.
63
+ if (video.hanaDisposed) return
64
+ video.hanaFrameCallback = video.requestVideoFrameCallback(onFrame)
65
+ }
66
+ video.hanaFrameCallback = video.requestVideoFrameCallback(onFrame)
75
67
  }
76
68
 
69
+ // Read by the renderer once per frame per video. Where
70
+ // `requestVideoFrameCallback` is missing, falls back to the media clock: browsers
71
+ // advance `currentTime` at frame granularity, so it answers the same question. The
72
+ // per-element base keeps the cross-element guarantee across a reload, where
73
+ // `Assets::insert_video` replaces the element under the same id.
74
+ export function videoFrameToken(video) {
75
+ const presented = video.hanaPresentedFrames
76
+ return presented === undefined ? video.hanaClockBase + video.currentTime : presented
77
+ }
77
78
 
78
- export async function loadVideoFromUrl(url) {
79
- return fetch(url)
80
- .then(response => {
81
- if (!response.ok) {
82
- throw new Error(`Failed to fetch video: ${response.statusText}`);
83
- }
84
- return response.blob();
79
+ // Only for `Local(bytes)`, which has no URL to stream from. A blob: URL pins its
80
+ // Blob — the whole file — until revoked, so keep it on the element for
81
+ // `disposeVideo` to release.
82
+ function setVideoSourceFromBlob(video, blob) {
83
+ video.hanaObjectUrl = URL.createObjectURL(blob)
84
+ video.src = video.hanaObjectUrl
85
+ }
86
+
87
+ // Releases everything the element holds: the frame-callback chain, the blob: URL
88
+ // mapping, and the buffered media and decoder behind it. Runs from `OwnedVideo`'s
89
+ // `Drop` in `assets.rs`. Idempotent, and each step is independently conditional —
90
+ // an element loaded without a blob: URL still gets the rest of the teardown.
91
+ export function disposeVideo(video) {
92
+ if (video.hanaDisposed) return
93
+ // Set first: the steps below fire events that must not read as a live element.
94
+ video.hanaDisposed = true
95
+
96
+ const callback = video.hanaFrameCallback
97
+ if (callback !== undefined) {
98
+ video.hanaFrameCallback = undefined
99
+ // A throw here must not skip the rest of the teardown.
100
+ if (typeof video.cancelVideoFrameCallback === 'function') {
101
+ video.cancelVideoFrameCallback(callback)
102
+ }
103
+ }
104
+
105
+ const url = video.hanaObjectUrl
106
+ if (url !== undefined) {
107
+ video.hanaObjectUrl = undefined
108
+ URL.revokeObjectURL(url)
109
+ }
110
+
111
+ video.pause()
112
+ // Revoking only drops the URL -> Blob mapping. Detaching the source is what
113
+ // actually releases the media the element has buffered and its decoder.
114
+ video.removeAttribute('src')
115
+ video.load()
116
+ }
117
+
118
+ // Resolves once the element holds a frame the renderer can sample, rejects if it
119
+ // never will. Every branch settles: an unsettled load holds its `PendingLoadGuard`,
120
+ // and headless hosts poll `Assets::pending_loads` to know a document is ready.
121
+ //
122
+ // Readiness is `loadeddata`, not `playing`: the video export drives the element
123
+ // paused, so `playing` never fires there, and a blocked autoplay is not a failure.
124
+ function whenSampleable(video) {
125
+ const mediaError = (prefix) => {
126
+ const err = video.error
127
+ return new Error(err === null ? prefix : `${prefix} (code ${err.code}: ${err.message})`)
128
+ }
129
+ // A rejected load never becomes an `OwnedVideo`, so nothing else releases the
130
+ // element: it would pin its blob: URL for the page lifetime and, if it buffers
131
+ // after a timeout, decode invisibly on loop (`autoplay` and `loop` stay set). The
132
+ // error is built first — `disposeVideo`'s `load()` clears `video.error`.
133
+ const failure = (prefix) => {
134
+ const err = mediaError(prefix)
135
+ disposeVideo(video)
136
+ return err
137
+ }
138
+
139
+ if (video.error !== null) return Promise.reject(failure('video failed to load'))
140
+ if (video.readyState >= HAVE_CURRENT_DATA) return Promise.resolve(video)
141
+
142
+ return new Promise((resolve, reject) => {
143
+ // Armed first so `settle` can clear it; nothing below runs this tick.
144
+ const timer = setTimeout(
145
+ () => settle(reject, failure('video timed out before presenting a frame')),
146
+ VIDEO_READY_TIMEOUT_MS,
147
+ )
148
+ const settle = (finish, value) => {
149
+ clearTimeout(timer)
150
+ video.removeEventListener('loadeddata', onReady)
151
+ video.removeEventListener('canplay', onReady)
152
+ video.removeEventListener('error', onError)
153
+ finish(value)
154
+ }
155
+ const onReady = () => settle(resolve, video)
156
+ const onError = () => settle(reject, failure('video failed to load'))
157
+
158
+ video.addEventListener('loadeddata', onReady)
159
+ // A partly-loaded source can become sampleable without a fresh `loadeddata`.
160
+ video.addEventListener('canplay', onReady)
161
+ video.addEventListener('error', onError)
162
+ })
163
+ }
164
+
165
+ function setupVideo(video) {
166
+ video.preload = 'auto'
167
+ video.autoplay = true //iOs hack to show the first frame
168
+ video.loop = true
169
+ video.muted = true
170
+ video.playsInline = true
171
+ video.currentTime = 0.01
172
+ video.load()
173
+
174
+ video.onloadeddata = () => {
175
+ video.muted = true
176
+ // DOMException: The play() request was interrupted by a new load request.
177
+ // https://developers.google.com/web/updates/2017/06/play-request-was-interrupted
178
+ const playPromise = video.play()
179
+
180
+ if (playPromise !== undefined) {
181
+ playPromise
182
+ .then((_) => {
183
+ // Automatic playback started! Do nothing all good!
184
+ })
185
+ .catch(() => {
186
+ // for some reason the video could not be played (The play() request was interrupted by a new load request.)
187
+ // so let's try to play it again
188
+ video.play()
189
+ })
190
+ .finally(() => {
191
+ video.pause()
85
192
  })
86
- .then(async blob => {
87
- const video = document.createElement('video');
88
- video.src = URL.createObjectURL(blob);
89
- return setupVideo(video);
90
- });
193
+ }
194
+ }
195
+
196
+ trackPresentedFrames(video)
197
+
198
+ return whenSampleable(video)
91
199
  }
92
200
 
201
+ export async function loadVideoFromBytes(bytes) {
202
+ const video = document.createElement('video')
203
+ setVideoSourceFromBlob(video, new Blob([bytes]))
204
+ return setupVideo(video)
205
+ }
93
206
 
207
+ // Streams from `url` instead of fetching it into a Blob first, so the browser can
208
+ // range-request it, playback starts before the last byte lands, and the file never
209
+ // sits in memory whole.
210
+ //
211
+ // `crossOrigin` must be set before `src`: without it a cross-origin element is
212
+ // tainted and every texture upload throws a SecurityError. It costs no reach — the
213
+ // `fetch` this replaces was already a CORS request, so any URL that used to load
214
+ // still does.
215
+ export async function loadVideoFromUrl(url) {
216
+ const video = document.createElement('video')
217
+ video.crossOrigin = 'anonymous'
218
+ video.src = url
219
+ return setupVideo(video)
220
+ }