Android 13 多 App 摄像头隔离与共享完整方案(仅改 Framework)
·
前言
在 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 同时打开
- 开机自动加载虚拟摄像头
适用于:车载、平板、商显、直播设备等。
更多推荐
所有评论(0)