@displayxr/inline3d 1.47.4 → 1.47.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.
@@ -10,10 +10,11 @@
10
10
  // fails with NotReadableError ("Device in use"). That device is skipped silently — a call must
11
11
  // never fail because an optional camera is busy.
12
12
  // - Labels are empty until one getUserMedia has succeeded. Without them, permission is primed on
13
- // an environment-facing camera where there is one (a phone's front lens is the head tracker's,
14
- // and opening it while the tracker holds it can reset the whole camera stack), then the list
15
- // is read again and a stereo device is opened by id BEFORE the default. The default is the
16
- // fallback.
13
+ // the DEFAULT camera with no constraints: in the DisplayXR Browser the runtime's 3D camera is
14
+ // listed first, so the prime opens the pair itself and no phone lens is opened at all (a plain
15
+ // open of one lens of the pair, or its teardown, crashes some phones' camera HAL; any facing or
16
+ // size constraint lets the engine pick a lens instead). Then the list is read again and a
17
+ // stereo device is opened by id BEFORE falling back. The primed default is the fallback.
17
18
  //
18
19
  // RECTIFICATION is not done here (P2): it needs the camera's calibration (intrinsics/extrinsics
19
20
  // for the ACTIVE device), which comes from a plug-in or the runtime. P1 sends the raw pair with
@@ -104,6 +105,17 @@ export async function openCamera(want = 'auto', o = {}) {
104
105
  log('camera-skip', { label, error: err.name });
105
106
  };
106
107
  const stop = (r) => r && r.stream.getTracks().forEach((t) => t.stop());
108
+ // Ask an open camera for another mode without a second start; keep it as it is if it refuses.
109
+ const withMode = async (r, constraints) => {
110
+ try {
111
+ await r.track.applyConstraints(constraints);
112
+ const st = r.track.getSettings();
113
+ if (st.width > 0 && st.height > 0) return { ...r, width: st.width, height: st.height };
114
+ } catch {
115
+ /* keep the mode it opened in */
116
+ }
117
+ return r;
118
+ };
107
119
  // A track that declares itself a side-by-side pair IS one, whatever its aspect (a phone's portrait
108
120
  // eyes make a 1440x1280 pair). Shape is only the guess for an unknown device, e.g. a USB pair.
109
121
  const isPair = (r) => !!r && (!!r.stereo || looksSbs(r.width, r.height));
@@ -170,15 +182,23 @@ export async function openCamera(want = 'auto', o = {}) {
170
182
  };
171
183
  let mono = null;
172
184
  if (want !== 'mono') {
173
- const listed = await listCameras();
174
- const pair = await tryNamedPairs(listed);
185
+ let listed = await listCameras();
186
+ let pair = await tryNamedPairs(listed);
175
187
  if (pair) return result(pair, 'sbs');
176
- // No labels yet (no permission). Prime it on an environment-facing camera, so a phone does not
177
- // open its front lens (the head tracker's) just to learn the names; then open a pair by id.
188
+ // Labels visible but no stereo-named device: a runtime 3D camera can join the list late (after
189
+ // a runtime restart), so read it once more before falling back.
190
+ if (listed.some((d) => d.label) && !listed.some((d) => stereoLabelHint(d.label))) {
191
+ listed = await listCameras();
192
+ pair = await tryNamedPairs(listed);
193
+ if (pair) return result(pair, 'sbs');
194
+ }
195
+ // No labels yet (no permission). Prime it on the default camera with NO constraints: in the
196
+ // DisplayXR Browser that is the 3D camera (listed first), so no phone lens is opened; a facing
197
+ // or size constraint would let the engine pick a lens instead. Then open a pair by id.
178
198
  if (listed.length && listed.every((d) => !d.label)) {
179
199
  let primed = null;
180
200
  try {
181
- primed = await open({ ...MONO_CONSTRAINTS, facingMode: { ideal: 'environment' } });
201
+ primed = await open(true);
182
202
  } catch (err) {
183
203
  skip('default', err);
184
204
  }
@@ -188,10 +208,8 @@ export async function openCamera(want = 'auto', o = {}) {
188
208
  stop(primed);
189
209
  return result(named, 'sbs');
190
210
  }
191
- // A camera that faces the user (or does not say: a desktop webcam) is the fallback as is; a
192
- // rear one is not what a self view falls back to, so the default is opened below instead.
193
- if (primed && primed.track?.getSettings?.().facingMode !== 'environment') mono = primed;
194
- else stop(primed);
211
+ // The primed default is the fallback as is (one camera start); ask it for the usual mode.
212
+ if (primed) mono = await withMode(primed, MONO_CONSTRAINTS);
195
213
  }
196
214
  }
197
215
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@displayxr/inline3d",
3
- "version": "1.47.4",
3
+ "version": "1.47.5",
4
4
  "description": "Turn any HTML <canvas> into a 3D window on a spatial display, inside an ordinary web page, for the DisplayXR Browser. Dependency-free; progressive enhancement (falls back to plain 2D everywhere else).",
5
5
  "type": "module",
6
6
  "types": "./index.d.ts",