Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 215 additions & 0 deletions apps/simple-camera/__tests__/visioncamera.constraints.harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,221 @@ describe('VisionCamera - Constraints', () => {
await session.stop()
})

it('keeps 4k video at 60 fps when a preview output is attached', async (context) => {
const targetResolution = CommonResolutions.UHD_16_9
const targetShortEdge = Math.min(
targetResolution.width,
targetResolution.height,
)
const targetLongEdge = Math.max(
targetResolution.width,
targetResolution.height,
)
const supportsUhdVideo = backDevice
.getSupportedResolutions('video')
.some((resolution) => {
const shortEdge = Math.min(resolution.width, resolution.height)
const longEdge = Math.max(resolution.width, resolution.height)
return shortEdge === targetShortEdge && longEdge === targetLongEdge
})
if (!supportsUhdVideo) {
return context.skip('4k video resolution not supported on this device')
}
if (!backDevice.supportsFPS(60)) {
return context.skip('fps: 60 not supported on this device')
}

async function resolveVideoSession(includePreview: boolean) {
const session = await VisionCamera.createCameraSession(false)
const videoOutput = VisionCamera.createVideoOutput({
targetResolution,
enableAudio: false,
enableHigherResolutionCodecs: true,
})
const previewOutput = includePreview
? VisionCamera.createPreviewOutput()
: undefined
let selectedConfig: CameraSessionConfig | undefined

await session.configure([
{
input: backDevice,
outputs:
previewOutput == null
? [{ output: videoOutput, mirrorMode: 'auto' }]
: [
{ output: previewOutput, mirrorMode: 'auto' },
{ output: videoOutput, mirrorMode: 'auto' },
],
constraints:
previewOutput == null
? [{ fps: 60 }, { resolutionBias: videoOutput }]
: [
{ fps: 60 },
{ resolutionBias: previewOutput },
{ resolutionBias: videoOutput },
],
onSessionConfigSelected: (config) => {
selectedConfig = config
},
},
])
await waitUntil(() => selectedConfig != null, { timeout: 5_000 })

await session.start()
try {
await waitUntil(() => videoOutput.currentResolution != null, {
timeout: 10_000,
})
const currentResolution = videoOutput.currentResolution
if (selectedConfig == null) throw new Error('no selected config')
if (currentResolution == null) throw new Error('no video resolution')
return {
selectedFPS: selectedConfig.selectedFPS,
resolution: currentResolution,
}
} finally {
await session.stop()
}
}

const videoOnly = await resolveVideoSession(false)
const videoOnlyShortEdge = Math.min(
videoOnly.resolution.width,
videoOnly.resolution.height,
)
const videoOnlyLongEdge = Math.max(
videoOnly.resolution.width,
videoOnly.resolution.height,
)
if (
videoOnly.selectedFPS !== 60 ||
videoOnlyShortEdge !== targetShortEdge ||
videoOnlyLongEdge !== targetLongEdge
) {
return context.skip(
`device resolves video-only 4k@60 to ${videoOnly.resolution.width}x${videoOnly.resolution.height}@${videoOnly.selectedFPS ?? 'default'}fps`,
)
}

const withPreview = await resolveVideoSession(true)
const previewShortEdge = Math.min(
withPreview.resolution.width,
withPreview.resolution.height,
)
const previewLongEdge = Math.max(
withPreview.resolution.width,
withPreview.resolution.height,
)
console.log(
`4k@60 video-only=${videoOnly.resolution.width}x${videoOnly.resolution.height}@${videoOnly.selectedFPS ?? 'default'}fps ` +
`with-preview=${withPreview.resolution.width}x${withPreview.resolution.height}@${withPreview.selectedFPS ?? 'default'}fps`,
)

expect(withPreview.selectedFPS).toBe(60)
expect(previewShortEdge).toBe(targetShortEdge)
expect(previewLongEdge).toBe(targetLongEdge)
})

it('keeps a low video target when preview resolution bias is attached first', async (context) => {
const targetResolution = CommonResolutions.HD_16_9
const maxAllowedResolution = CommonResolutions.FHD_16_9
const uhdResolution = CommonResolutions.UHD_16_9

const getEdges = (resolution: { width: number; height: number }) => ({
short: Math.min(resolution.width, resolution.height),
long: Math.max(resolution.width, resolution.height),
})

const targetEdges = getEdges(targetResolution)
const maxAllowedEdges = getEdges(maxAllowedResolution)
const uhdEdges = getEdges(uhdResolution)
const supportedVideoResolutions = backDevice.getSupportedResolutions('video')
const supportsTargetResolution = supportedVideoResolutions.some(
(resolution) => {
const edges = getEdges(resolution)
return edges.short === targetEdges.short && edges.long === targetEdges.long
},
)
const supportsUhdResolution = supportedVideoResolutions.some(
(resolution) => {
const edges = getEdges(resolution)
return edges.short === uhdEdges.short && edges.long === uhdEdges.long
},
)
if (!supportsTargetResolution) {
return context.skip('720p video resolution not supported on this device')
}
if (!supportsUhdResolution) {
return context.skip('4k video resolution not supported on this device')
}

async function resolveLowTargetSession(includePreview: boolean) {
const session = await VisionCamera.createCameraSession(false)
const videoOutput = VisionCamera.createVideoOutput({
targetResolution,
enableAudio: false,
enableHigherResolutionCodecs: true,
})
const previewOutput = includePreview
? VisionCamera.createPreviewOutput()
: undefined

await session.configure([
{
input: backDevice,
outputs:
previewOutput == null
? [{ output: videoOutput, mirrorMode: 'auto' }]
: [
{ output: previewOutput, mirrorMode: 'auto' },
{ output: videoOutput, mirrorMode: 'auto' },
],
constraints:
previewOutput == null
? [{ resolutionBias: videoOutput }]
: [
{ resolutionBias: previewOutput },
{ resolutionBias: videoOutput },
],
},
])

await session.start()
try {
await waitUntil(() => videoOutput.currentResolution != null, {
timeout: 10_000,
})
const currentResolution = videoOutput.currentResolution
if (currentResolution == null) throw new Error('no video resolution')
return currentResolution
} finally {
await session.stop()
}
}

const videoOnly = await resolveLowTargetSession(false)
const videoOnlyEdges = getEdges(videoOnly)
if (
videoOnlyEdges.short > maxAllowedEdges.short ||
videoOnlyEdges.long > maxAllowedEdges.long
) {
return context.skip(
`device resolves video-only 720p target to ${videoOnly.width}x${videoOnly.height}`,
)
}

const withPreview = await resolveLowTargetSession(true)
const previewEdges = getEdges(withPreview)
console.log(
`720p target video-only=${videoOnly.width}x${videoOnly.height} ` +
`with-preview=${withPreview.width}x${withPreview.height}`,
)

expect(previewEdges.short).toBeLessThanOrEqual(maxAllowedEdges.short)
expect(previewEdges.long).toBeLessThanOrEqual(maxAllowedEdges.long)
})

it('resolves photoHDR: true when the device supports photo HDR', async (context) => {
if (!backDevice.supportsPhotoHDR) {
return context.skip('photoHDR: not supported on this device')
Expand Down
Loading