Skip to content
Merged
Show file tree
Hide file tree
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
23 changes: 23 additions & 0 deletions apps/simple-camera/__tests__/visioncamera.devices.harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { beforeAll, describe, expect, it } from 'react-native-harness'
import type {
CameraDevice,
CameraDeviceFactory,
TargetCameraPosition,
} from 'react-native-vision-camera'
import { VisionCamera } from 'react-native-vision-camera'

Expand All @@ -25,6 +26,28 @@ describe('VisionCamera - Devices', () => {
expect(hasFront).toBe(true)
})

it('returns the default camera for each available target position', () => {
const positions: TargetCameraPosition[] = ['back', 'front', 'external']

for (const position of positions) {
const defaultCamera = factory.getDefaultCamera(position)
const devicesAtPosition = factory.cameraDevices.filter(
(d) => d.position === position,
)

if (devicesAtPosition.length === 0) {
expect(defaultCamera).toBe(undefined)
continue
}

expect(defaultCamera).toBeDefined()
expect(defaultCamera?.position).toBe(position)
expect(devicesAtPosition.some((d) => d.id === defaultCamera?.id)).toBe(
true,
)
}
})

it('logs external cameras when present (optional)', (context) => {
const external = factory.cameraDevices.filter(
(d) => d.position === 'external',
Expand Down
4 changes: 2 additions & 2 deletions apps/simple-camera/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ PODS:
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- Yoga
- NitroModules (0.35.9):
- NitroModules (0.36.1):
- hermes-engine
- RCTRequired
- RCTTypeSafety
Expand Down Expand Up @@ -2762,7 +2762,7 @@ SPEC CHECKSUMS:
MLKitVision: 39a5a812db83c4a0794445088e567f3631c11961
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
NitroImage: 33c4698f86b081bd3d0245223f34ea561b0934ea
NitroModules: 16bc17a076b12304d608f7c915b9d321f56dfc19
NitroModules: b4174dd303728e16ad1afb79f64c1a5c69a3b373
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
RCTDeprecation: a4c521821fab57cbb125b36effe84d897d0dfa12
RCTRequired: 9f3a7e5645d4bc3f551593de7550bb66ab6e42bc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package com.margelo.nitro.camera
import android.content.Context
import android.content.SharedPreferences
import android.util.Log
import androidx.annotation.OptIn
import androidx.camera.camera2.adapter.CameraInfoAdapter.Companion.cameraId
import androidx.camera.core.CameraIdentifier
import androidx.camera.core.CameraPresenceListener
import androidx.camera.core.CameraSelector
import androidx.camera.core.ExperimentalLensFacing
import androidx.camera.extensions.ExtensionMode
import androidx.camera.extensions.ExtensionsManager
import androidx.camera.lifecycle.ProcessCameraProvider
Expand Down Expand Up @@ -108,12 +110,17 @@ class HybridCameraDeviceFactory(
}
}

override fun getDefaultCamera(position: CameraPosition): HybridCameraDeviceSpec? {
@OptIn(ExperimentalLensFacing::class)
override fun getDefaultCamera(position: TargetCameraPosition): HybridCameraDeviceSpec? {
val selector =
when (position) {
CameraPosition.FRONT -> CameraSelector.DEFAULT_FRONT_CAMERA
CameraPosition.BACK -> CameraSelector.DEFAULT_BACK_CAMERA
else -> return null
TargetCameraPosition.FRONT -> CameraSelector.DEFAULT_FRONT_CAMERA
TargetCameraPosition.BACK -> CameraSelector.DEFAULT_BACK_CAMERA
TargetCameraPosition.EXTERNAL ->
CameraSelector
.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_EXTERNAL)
.build()
}
try {
val defaultCamera = cameraProvider.getCameraInfo(selector)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,4 @@ extension CameraPosition {
self = .unspecified
}
}

func toAVCaptureDevicePosition() -> AVCaptureDevice.Position {
switch self {
case .back:
return .back
case .front:
return .front
case .unspecified:
return .unspecified
case .external:
// External is not supported on iOS
return .unspecified
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,27 +73,28 @@ final class HybridCameraDeviceFactory: HybridCameraDeviceFactorySpec {
return HybridCameraDevice(device: device)
}

func getDefaultCamera(position: CameraPosition) throws -> (any HybridCameraDeviceSpec)? {
if #available(iOS 17.0, *), position == .external {
func getDefaultCamera(position: TargetCameraPosition) throws -> (any HybridCameraDeviceSpec)? {
guard let device = getDefaultAVCaptureDevice(at: position) else {
return nil
}
return HybridCameraDevice(device: device)
}

private func getDefaultAVCaptureDevice(at position: TargetCameraPosition) -> AVCaptureDevice? {
switch position {
case .back:
// Get default wide-angle at .back
return AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
case .front:
// Get default wide-angle at .front
return AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front)
case .external:
// On iOS, Position "external" is for some reason reflected on the .deviceType, not on .position.
let device = AVCaptureDevice.default(
.external,
for: .video,
position: .unspecified)
guard let device else {
guard #available(iOS 17.0, *) else {
return nil
}
return HybridCameraDevice(device: device)
return AVCaptureDevice.default(.external, for: .video, position: .unspecified)
}

let device = AVCaptureDevice.default(
.builtInWideAngleCamera,
for: .video,
position: position.toAVCaptureDevicePosition())
guard let device else {
return nil
}
return HybridCameraDevice(device: device)
}

func getSupportedExtensions(camera: any HybridCameraDeviceSpec) throws -> Promise<
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading