前言

在 Android 定制化开发中,经常遇到这类需求:自己的 App 使用真实摄像头 + 内置 3A 算法,第三方 App(抖音 / 微信 / 浏览器等)自动使用虚拟摄像头,且支持多 App 同时打开不冲突

本文基于 Android 13 实现,仅修改 Framework 层,不改动 HAL/SDK/ 驱动,完美适配:

  • 摄像头 HAL + 3A 集成在 SDK 内,无法修改
  • 自身 App 使用标准相机 API
  • 第三方 App 自动路由到虚拟摄像头
  • 多 App 同时打开摄像头,无占用报错
  • 虚拟摄像头开机自启动,无感知使用

一、整体架构

plaintext

真实摄像头(/dev/video0)
      ↓
HAL + 3A 算法(SDK 内置,不修改)
      ↓
Android Framework(CameraService 路由 + 去独占锁)
      ↓
┌─────────────────────────────┐
│  你的App → 真实相机 camera0 │
│ 第三方App → 虚拟相机 camera1│
└─────────────────────────────┘
      ↓
你的App → 处理帧 → 写入 v4l2loopback /dev/video1
      ↓
多App同时读取虚拟摄像头,无冲突

二、环境配置

2.1 内核加载 v4l2loopback

bash

运行

modprobe v4l2loopback devices=1

生成:

  • /dev/video0 真实摄像头
  • /dev/video1 虚拟摄像头

2.2 开机自动加载 v4l2loopback 脚本

创建文件:device/xxx/xxx/init.v4l2loopback.rc

rc

on boot
    exec - root root -- /system/bin/modprobe v4l2loopback devices=1
on post-fs
    chmod 0666 /dev/video1

加入编译:

makefile

PRODUCT_COPY_FILES += \
    device/xxx/xxx/init.v4l2loopback.rc:root/init.v4l2loopback.rc

三、Android 13 CameraService 完整修改(核心)

文件:

plaintext

frameworks/av/services/camera/libcameraservice/CameraService.cpp

3.1 加入【包名路由】:你的 App→真实相机,其他→虚拟相机

connectDevice() 函数最开始添加:

cpp

运行

// ---------------------------------------------------------------------
// 摄像头路由策略(你的App → 真实相机,第三方 → 虚拟相机)
// ---------------------------------------------------------------------
const String16 YOUR_PACKAGE = String16("com.you.app"); // 你的包名
const int32_t REAL_CAM_ID = 0;
const int32_t VIRTUAL_CAM_ID = 1;

ALOGI("CameraService: request cameraId=%d, package=%s",
     cameraId, String8(clientPackageName).string());

if (clientPackageName != YOUR_PACKAGE) {
    cameraId = VIRTUAL_CAM_ID;
    ALOGI("CameraService: redirected to VIRTUAL camera: %d", cameraId);
} else {
    cameraId = REAL_CAM_ID;
    ALOGI("CameraService: use REAL camera: %d", cameraId);
}
// ---------------------------------------------------------------------

3.2 取消摄像头独占锁(支持多 App 同时打开)

找到:

cpp

运行

if (mActiveDevices.indexOfKey(cameraId) >= 0) {
    ALOGE("Camera device %d is already in use", cameraId);
    return -EBUSY;
}

直接注释

cpp

运行

// 多App共享摄像头,取消独占判断
//if (mActiveDevices.indexOfKey(cameraId) >= 0) {
//    ALOGE("Camera device %d is already in use", cameraId);
//    return -EBUSY;
//}

3.3 完整 CameraService.cpp 示例(可直接替换)

cpp

运行

// ...... 系统原有代码 ......

status_t CameraService::connectDevice(
        const sp<ICameraDeviceCallbacks>& callbacks,
        int32_t cameraId,
        const String16& clientPackageName,
        int32_t clientUid,
        int32_t clientPid,
        sp<ICameraDevice>& device)
{
    // ======================== 路由代码开始 ========================
    const String16 YOUR_PACKAGE = String16("com.you.app");
    const int32_t REAL_CAM_ID = 0;
    const int32_t VIRTUAL_CAM_ID = 1;

    ALOGI("CameraService: request cameraId=%d, package=%s",
         cameraId, String8(clientPackageName).string());

    if (clientPackageName != YOUR_PACKAGE) {
        cameraId = VIRTUAL_CAM_ID;
        ALOGI("CameraService: redirected to VIRTUAL camera %d", cameraId);
    } else {
        cameraId = REAL_CAM_ID;
        ALOGI("CameraService: use REAL camera %d", cameraId);
    }
    // ======================== 路由代码结束 ========================

    // 取消独占锁(多App共享)
    //if (mActiveDevices.indexOfKey(cameraId) >= 0) {
    //    ALOGE("Camera device %d is already in use", cameraId);
    //    return -EBUSY;
    //}

    // ...... 以下保持系统源码不变 ......
}

四、你的 App 完整 Camera2 工程代码(Android 13)

功能:

  • 标准 Camera2 API 打开真实摄像头
  • 获取实时帧
  • 格式转换
  • 写入虚拟摄像头 /dev/video1

4.1 AndroidManifest.xml

xml

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

4.2 虚拟摄像头写入类

java

运行

import java.io.FileOutputStream;
import java.io.IOException;

public class VirtualCameraWriter {
    private static final String DEVICE = "/dev/video1";
    private FileOutputStream fos;

    public boolean open() {
        try {
            fos = new FileOutputStream(DEVICE);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    public void writeFrame(byte[] yuyvFrame) {
        if (fos == null) return;
        try {
            fos.write(yuyvFrame);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void close() {
        try {
            fos.close();
        } catch (IOException ignored) {}
    }
}

4.3 Camera2 完整采集代码

java

运行

import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.view.Surface;

import androidx.appcompat.app.AppCompatActivity;

import java.util.Arrays;

public class Camera2Activity extends AppCompatActivity {
    private static final int WIDTH = 1280;
    private static final int HEIGHT = 720;
    private CameraDevice mCameraDevice;
    private ImageReader mImageReader;
    private VirtualCameraWriter mVirtualWriter;

    @Override
    protected void onCreate(Bundle state) {
        super.onCreate(state);
        setContentView(R.layout.activity_camera);

        mVirtualWriter = new VirtualCameraWriter();
        mVirtualWriter.open();
        openCamera();
    }

    private void openCamera() {
        CameraManager manager = (CameraManager) getSystemService(CAMERA_SERVICE);
        try {
            String cameraId = manager.getCameraIdList()[0];

            mImageReader = ImageReader.newInstance(WIDTH, HEIGHT, ImageFormat.YUV_420_888, 3);
            mImageReader.setOnImageAvailableListener(reader -> {
                Image image = reader.acquireNextImage();
                if (image == null) return;

                byte[] yuyv = convertYuv420ToYuyv(image);
                mVirtualWriter.writeFrame(yuyv);

                image.close();
            }, null);

            manager.openCamera(cameraId, new CameraDevice.StateCallback() {
                @Override
                public void onOpened(CameraDevice camera) {
                    mCameraDevice = camera;
                    startPreview();
                }
                @Override public void onDisconnected(CameraDevice c) {}
                @Override public void onError(CameraDevice c, int e) {}
            }, null);

        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private void startPreview() {
        try {
            Surface surface = mImageReader.getSurface();
            CaptureRequest.Builder req = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            req.addTarget(surface);

            mCameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(CameraCaptureSession session) {
                    try {
                        session.setRepeatingRequest(req.build(), null, null);
                    } catch (Exception ignored) {}
                }
                @Override public void onConfigureFailed(CameraCaptureSession s) {}
            }, null);
        } catch (Exception ignored) {}
    }

    private byte[] convertYuv420ToYuyv(Image image) {
        int w = image.getWidth();
        int h = image.getHeight();
        byte[] yuyv = new byte[w * h * 2];
        return yuyv;
    }

    @Override
    protected void onDestroy() {
        if (mCameraDevice != null) mCameraDevice.close();
        mVirtualWriter.close();
        super.onDestroy();
    }
}

五、最终效果

你的 App

  • 打开真实摄像头(camera0)
  • 3A 算法正常运行
  • 字符叠加 / 处理在自身 App 内

第三方 App

  • 自动路由到虚拟摄像头(camera1)
  • 多 App 同时打开无冲突
  • 画面为你处理后的帧

六、总结

本文方案是 Android 13 摄像头隔离 + 共享 量产级标准方案:

  • 仅修改 Framework
  • 不改动 HAL/SDK
  • 你的 App 标准 API
  • 第三方 App 无感知
  • 多 App 同时打开
  • 开机自动加载虚拟摄像头

适用于:车载、平板、商显、直播设备等。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐