diff --git a/package-lock.json b/package-lock.json index d897954..4664eed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@snap/react-camera-kit", - "version": "0.4.0", + "version": "0.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@snap/react-camera-kit", - "version": "0.4.0", + "version": "0.5.0", "license": "MIT", "dependencies": { "stable-hash": "^0.0.6" diff --git a/package.json b/package.json index 21bd925..ea68024 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@snap/react-camera-kit", - "version": "0.4.0", + "version": "0.5.0", "description": "React Camera Kit for web applications", "type": "module", "main": "./dist/cjs/index.js", diff --git a/src/internal/sourceUtils.test.ts b/src/internal/sourceUtils.test.ts index 6e60b76..0faf433 100644 --- a/src/internal/sourceUtils.test.ts +++ b/src/internal/sourceUtils.test.ts @@ -348,7 +348,56 @@ describe("sourceUtils", () => { videoElement.dispatchEvent(new Event("canplay")); await promise; - expect(mockCreateVideoSource).toHaveBeenCalledWith(videoElement, undefined); + // createVideoSource is always called with an options object; trackingData is just undefined here. + const options = mockCreateVideoSource.mock.calls[0]?.[1]; + expect(options?.trackingData).toBeUndefined(); + }); + + it("should pass fpsLimit through", async () => { + const source = { kind: "video" as const, url: "https://example.com/video.mp4", fpsLimit: 30 }; + + const promise = createCameraKitSource(source); + videoElement.dispatchEvent(new Event("canplay")); + await promise; + + expect(mockCreateVideoSource.mock.calls[0]?.[1]?.fpsLimit).toBe(30); + }); + + it("should pass cameraFacing through as cameraType", async () => { + const source = { + kind: "video" as const, + url: "https://example.com/video.mp4", + cameraFacing: "environment" as const, + }; + + const promise = createCameraKitSource(source); + videoElement.dispatchEvent(new Event("canplay")); + await promise; + + expect(mockCreateVideoSource).toHaveBeenCalledWith(videoElement, { cameraType: "environment" }); + }); + + it("should combine cameraFacing and tracking data", async () => { + const buffer = new ArrayBuffer(8); + (globalThis as { fetch?: typeof fetch }).fetch = jest + .fn() + .mockResolvedValue({ ok: true, arrayBuffer: () => Promise.resolve(buffer) }) as typeof fetch; + + const source = { + kind: "video" as const, + url: "https://example.com/video.mp4", + trackingDataUrl: "https://example.com/clip.td", + cameraFacing: "environment" as const, + }; + + const promise = createCameraKitSource(source); + videoElement.dispatchEvent(new Event("canplay")); + await promise; + + expect(mockCreateVideoSource).toHaveBeenCalledWith(videoElement, { + trackingData: buffer, + cameraType: "environment", + }); }); it("should reject when tracking data fails to load", async () => { diff --git a/src/internal/sourceUtils.ts b/src/internal/sourceUtils.ts index 2233c7a..a232f9d 100644 --- a/src/internal/sourceUtils.ts +++ b/src/internal/sourceUtils.ts @@ -44,6 +44,8 @@ export async function createCameraKitSource(source: SourceInput): Promise((res, rej) => { autoplay = autoplay ?? true; @@ -136,7 +142,7 @@ function createCameraKitVideoSource({ const trackingData = trackingDataUrl ? await fetchTrackingData(trackingDataUrl) : undefined; if (autoplay) await videoInput.play(); res({ - cameraKitSource: createVideoSource(videoInput, trackingData ? { trackingData } : undefined), + cameraKitSource: createVideoSource(videoInput, { trackingData, cameraType: cameraFacing, fpsLimit }), transform: Transform2D.Identity, inputSize: [videoInput.videoWidth, videoInput.videoHeight], initializedSourceInput: { diff --git a/src/types.ts b/src/types.ts index 32732d5..15dbce5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -55,6 +55,17 @@ export type VideoSourceInput = { * previewing world-facing lenses from a recorded environment. */ trackingDataUrl?: string; + /** + * Front/rear camera semantic the video should be treated as. Passed to CameraKit as `cameraType`, + * which affects Lens feature behavior such as surface tracking. Defaults to "user". + * "user" is front-facing; "environment" is rear-facing. + */ + cameraFacing?: CameraFacing; + /** + * Optional cap on frames per second processed from the video. Passed to `createVideoSource` as + * `fpsLimit`. Defaults to the video's native frame rate when omitted. + */ + fpsLimit?: number; }; export type ImageSourceInput = { kind: "image"; url: string };