You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于Camera2 Api同时运行两个不同的相机预览?

Dual Camera Preview Implementation Guide (Android)

Hey there! I’ve helped many developers work through dual camera preview setups, so let’s break this down step by step—covering both the framework-side code and whether you’ll need to touch the HAL layer.

First: Confirm Hardware Support

Before writing any code, make sure your device can actually run two camera streams at the same time:

  • Use the CameraManager to check concurrent camera groups:
    val cameraManager = getSystemService(CAMERA_SERVICE) as CameraManager
    val concurrentGroups = cameraManager.getConcurrentCameraIds()
    // Verify your target camera IDs (e.g., front + back) are in the same group
    
  • Also, check the camera’s characteristics for REQUEST_AVAILABLE_CAPABILITIES_CONCURRENT_STREAMS—this flag confirms the camera can participate in concurrent sessions.

Framework-Side Implementation Steps

1. Set Up Dual Preview UI

Use two TextureView widgets (or SurfaceView if you prefer) in your layout to display each camera’s feed:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <TextureView
        android:id="@+id/frontCameraPreview"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>

    <TextureView
        android:id="@+id/backCameraPreview"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>
</LinearLayout>

2. Open and Configure Both Cameras

Camera operations are asynchronous, so you’ll need to handle each camera’s setup separately:

// Helper to open a camera and start preview
fun initCameraPreview(cameraId: String, targetSurface: Surface) {
    val cameraManager = getSystemService(CAMERA_SERVICE) as CameraManager
    cameraManager.openCamera(cameraId, object : CameraDevice.StateCallback() {
        override fun onOpened(camera: CameraDevice) {
            // Create a capture session for this camera
            val surfaces = listOf(targetSurface)
            camera.createCaptureSession(surfaces, object : CameraCaptureSession.StateCallback() {
                override fun onConfigured(session: CameraCaptureSession) {
                    val previewRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {
                        addTarget(targetSurface)
                    }
                    // Start repeating preview
                    session.setRepeatingRequest(previewRequest.build(), null, null)
                }

                override fun onConfigureFailed(session: CameraCaptureSession) {
                    // Handle configuration failure (e.g., incompatible surface format)
                }
            }, null)
        }

        override fun onDisconnected(camera: CameraDevice) = camera.close()
        override fun onError(camera: CameraDevice, error: Int) = camera.close()
    }, null)
}

// Initialize front camera when its TextureView is ready
frontCameraPreview.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
    override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
        val frontCameraId = getCameraIdByLensFacing(CameraCharacteristics.LENS_FACING_FRONT)
        initCameraPreview(frontCameraId, Surface(surface))
    }
    // Implement other required listener methods (onSurfaceTextureSizeChanged, etc.)
}

// Initialize back camera similarly
backCameraPreview.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
    override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
        val backCameraId = getCameraIdByLensFacing(CameraCharacteristics.LENS_FACING_BACK)
        initCameraPreview(backCameraId, Surface(surface))
    }
    // Implement other required listener methods
}

// Helper to get camera ID by lens facing
fun getCameraIdByLensFacing(lensFacing: Int): String {
    val cameraManager = getSystemService(CAMERA_SERVICE) as CameraManager
    for (cameraId in cameraManager.cameraIdList) {
        val characteristics = cameraManager.getCameraCharacteristics(cameraId)
        if (characteristics.get(CameraCharacteristics.LENS_FACING) == lensFacing) {
            return cameraId
        }
    }
    throw IllegalArgumentException("No camera found with lens facing: $lensFacing")
}

3. Critical Tips to Avoid Issues

  • Match Preview Formats: Ensure both cameras use compatible preview resolutions and image formats (e.g., ImageFormat.YUV_420_888). Mismatches will break capture session configuration.
  • Permission Handling: Don’t forget to request the CAMERA permission (and handle runtime permissions on Android 6.0+).
  • Resource Cleanup: Always close CameraDevice and CameraCaptureSession when your activity/fragment is destroyed to prevent memory leaks.
  • Performance Tuning: Dual preview uses more system resources—test on target devices and lower preview resolutions if you see lag or crashes.

HAL Layer Modifications: Do You Need Them?

Short answer: Almost never, unless you’re building custom hardware.

  • If your device’s hardware (ISP, camera sensors, bus bandwidth) supports concurrent camera streams, the stock HAL implementation will handle it automatically. The Android Framework communicates with the HAL via standard interfaces to enable concurrent sessions.
  • If the hardware doesn’t support concurrent streams (e.g., a single ISP that can’t process two feeds at once), no HAL modification can fix this—it’s a fundamental hardware limitation.

To confirm, check your device’s official specs or reach out to the chipset manufacturer (like Qualcomm or MediaTek) to verify concurrent stream support.

内容的提问来源于stack exchange,提问作者Akshit Agarwal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 17:22:45