Skip to content

Commit 4cf7b7a

Browse files
gsasson-scclaude
andcommitted
feat(video): support cameraFacing on VideoSourceInput
Add optional `cameraFacing` ("user" | "environment") to VideoSourceInput, passed to `createVideoSource` as `cameraType`. The camera type propagates to the lens (DeviceCamera front/back-facing) and engine tracking, so back-facing / world video sources can be flagged correctly. Defaults to "user" (front). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 2990cc8 commit 4cf7b7a

3 files changed

Lines changed: 51 additions & 1 deletion

File tree

src/internal/sourceUtils.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,43 @@ describe("sourceUtils", () => {
351351
expect(mockCreateVideoSource).toHaveBeenCalledWith(videoElement, undefined);
352352
});
353353

354+
it("should pass cameraFacing through as cameraType", async () => {
355+
const source = {
356+
kind: "video" as const,
357+
url: "https://example.com/video.mp4",
358+
cameraFacing: "environment" as const,
359+
};
360+
361+
const promise = createCameraKitSource(source);
362+
videoElement.dispatchEvent(new Event("canplay"));
363+
await promise;
364+
365+
expect(mockCreateVideoSource).toHaveBeenCalledWith(videoElement, { cameraType: "environment" });
366+
});
367+
368+
it("should combine cameraFacing and tracking data", async () => {
369+
const buffer = new ArrayBuffer(8);
370+
(globalThis as { fetch?: typeof fetch }).fetch = jest
371+
.fn()
372+
.mockResolvedValue({ ok: true, arrayBuffer: () => Promise.resolve(buffer) }) as typeof fetch;
373+
374+
const source = {
375+
kind: "video" as const,
376+
url: "https://example.com/video.mp4",
377+
trackingDataUrl: "https://example.com/clip.td",
378+
cameraFacing: "environment" as const,
379+
};
380+
381+
const promise = createCameraKitSource(source);
382+
videoElement.dispatchEvent(new Event("canplay"));
383+
await promise;
384+
385+
expect(mockCreateVideoSource).toHaveBeenCalledWith(videoElement, {
386+
trackingData: buffer,
387+
cameraType: "environment",
388+
});
389+
});
390+
354391
it("should reject when tracking data fails to load", async () => {
355392
(globalThis as { fetch?: typeof fetch }).fetch = jest
356393
.fn()

src/internal/sourceUtils.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export async function createCameraKitSource(source: SourceInput): Promise<Source
4444
videoUrl: source.url,
4545
autoplay: source.autoplay,
4646
trackingDataUrl: source.trackingDataUrl,
47+
cameraFacing: source.cameraFacing,
4748
});
4849
} else if (source.kind === "image") {
4950
return createCameraKitImageSource({
@@ -110,10 +111,12 @@ function createCameraKitVideoSource({
110111
videoUrl,
111112
autoplay,
112113
trackingDataUrl,
114+
cameraFacing,
113115
}: {
114116
videoUrl: string;
115117
autoplay?: boolean;
116118
trackingDataUrl?: string;
119+
cameraFacing?: CameraFacing;
117120
}) {
118121
return new Promise<SourceApplication>((res, rej) => {
119122
autoplay = autoplay ?? true;
@@ -135,8 +138,12 @@ function createCameraKitVideoSource({
135138
// before any playback has started, so there is no orphaned playing video to clean up.
136139
const trackingData = trackingDataUrl ? await fetchTrackingData(trackingDataUrl) : undefined;
137140
if (autoplay) await videoInput.play();
141+
const videoSourceOptions =
142+
trackingData || cameraFacing
143+
? { ...(trackingData ? { trackingData } : {}), ...(cameraFacing ? { cameraType: cameraFacing } : {}) }
144+
: undefined;
138145
res({
139-
cameraKitSource: createVideoSource(videoInput, trackingData ? { trackingData } : undefined),
146+
cameraKitSource: createVideoSource(videoInput, videoSourceOptions),
140147
transform: Transform2D.Identity,
141148
inputSize: [videoInput.videoWidth, videoInput.videoHeight],
142149
initializedSourceInput: {

src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ export type VideoSourceInput = {
5555
* previewing world-facing lenses from a recorded environment.
5656
*/
5757
trackingDataUrl?: string;
58+
/**
59+
* Camera the video should be treated as coming from. Passed to `createVideoSource` as `cameraType`,
60+
* which surfaces to the lens (e.g. front/back-facing behavior) and engine tracking. Defaults to
61+
* `"user"` (front). Set `"environment"` for back-facing / world content. `"user"` ↔ front, `"environment"` ↔ back.
62+
*/
63+
cameraFacing?: CameraFacing;
5864
};
5965
export type ImageSourceInput = { kind: "image"; url: string };
6066

0 commit comments

Comments
 (0)