1、简介
在 WebRTC Android 中,已经兼容了 Camera 和 Camera2 原生 API 的相机采集,所以我们不必再单独实现一套采集功能。不过我们可以根据 RTC 的抽象 CameraCapturer 接口实现 CameraX (其实也是基于 Camera2的封装) 的相机采集,显然,这不是该篇的主题就不再多说了。该篇文章,主要为大家解析 WebRTC 的相机采集从 java 到 Jni 的一个调用过程。
先上一个整个调用的时序图,有条件的同学可以根据时序图来跟踪源码:
其实整个涉及到的文件还是比较多的
原图链接: http://devyk.top/2022/WebRTC相机采集流程.png

2、相机采集
WebRTC Android 中定义了一个抽象的视频采集接口为 VideoCapturer , 内部定义了 初始化(SurfaceTexture 渲染帮助类 、相机采集 VideoFrame 回调 ) 、开始/结束/释放 等采集生命周期 API, 最终实现有 Camera1Capturer 和 Camera2Capturer 2 个子类。
为了有一个更清晰的一个结构,下面可以看一下 VideoCapturer 类图:

下面我们分别来介绍 webrtc 是如何来对 Camera1/2 进行创建并使用的,首先我们先看下上面的类图, 最顶层是 VideoCapturer 抽象接口
// Base interface for all VideoCapturers to implement.
public interface VideoCapturer {
void initialize(SurfaceTextureHelper surfaceTextureHelper, Context applicationContext,
CapturerObserver capturerObserver);
void startCapture(int width, int height, int framerate);
void stopCapture() throws InterruptedException;
void changeCaptureFormat(int width, int height, int framerate);
void dispose();
boolean isScreencast();
}
它的直接实现为 CameraVideoCapturer 然后它具体的实现是一个抽象 CameraCapturer , 该抽象类封装了 Camera1/2 公共部分,当调用 startCapture 接口,内部实现了 createCameraSession 抽象函数,以供 Camera1/2 各自进行创建 Session,可以看下下面代码的调用:
abstract class CameraCapturer implements CameraVideoCapturer {
...
@Override
public void startCapture(int width, int height, int framerate) {
...
createSessionInternal(0);
}
private void createSessionInternal(int delayMs) {
cameraThreadHandler.postDelayed(new Runnable() {
@Override
public void run() {
createCameraSession(createSessionCallback, cameraSessionEventsHandler, applicationContext,
surfaceHelper, cameraName, width, height, framerate);
}
}, delayMs);
}
//Camera 1 and Camera1 实现
abstract protected void createCameraSession(CameraSession.CreateSessionCallback createSessionCallback, CameraSession.Events events,
Context applicationContext, SurfaceTextureHelper surfaceTextureHelper, String cameraName,
int width, int height, int framerate);
}

可以看到最终抽象的实现就是最上面类图中的 Camera1Capturer 和 Camera2Capturer ,我们还是分别进行分析。
2.1 Camera 1
如果最上层使用 Camera1 进行 startCapture, 最终将执行到如下代码:
public Camera1Capturer(
@Override
protected void createCameraSession(CameraSession.CreateSessionCallback createSessionCallback,
CameraSession.Events events, Context applicationContext,
SurfaceTextureHelper surfaceTextureHelper, String cameraName, int width, int height,
int framerate) {
Camera1Session.create(createSessionCallback, events, captureToTexture, applicationContext,surfaceTextureHelper, Camera1Enumerator.getCameraIndex(cameraName), width, height,framerate);
}
}
可以看到函数体中具体是 Camera1Session#create 的调用,我们跟进去看一下
class Camera1Session implements CameraSession {
...
public static void create(final CreateSessionCallback callback, final Events events,
final boolean captureToTexture, final Context applicationContext,
final SurfaceTextureHelper surfaceTextureHelper, final int cameraId, final int width,
final int height, final int framerate) {
final long constructionTimeNs = System.nanoTime();
...
final android.hardware.Camera camera;
camera = android.hardware.Camera.open(cameraId);
camera.setPreviewTexture(surfaceTextureHelper.getSurfaceTexture());
final android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
final CaptureFormat captureFormat;
final android.hardware.Camera.Parameters parameters = camera.getParameters();
captureFormat = findClosestCaptureFormat(parameters, width, height, framerate);
final Size pictureSize = findClosestPictureSize(parameters, width, height);
updateCameraParameters(camera, parameters, captureFormat, pictureSize, captureToTexture);
if (!captureToTexture) {
final int frameSize = captureFormat.frameSize();
for (int i = 0; i < NUMBER_OF_CAPTURE_BUFFERS; ++i) {
final ByteBuffer buffer = ByteBuffer.allocateDirect(frameSize);
camera.addCallbackBuffer(buffer.array());
}
}
// Calculate orientation manually and send it as CVO insted.
camera.setDisplayOrientation(0 /* degrees */);
//内部会设置采集的 NV21 or OES 回调
callback.onDone(new Camera1Session(events, captureToTexture, applicationContext,
surfaceTextureHelper, cameraId, camera, info, captureFormat, constructionTimeNs));
}
}
可以看到这一步就是通过 Camera.open 打开摄像头,并进行帧率、采集格式、预览等一些基础设置.
private Camera1Session(...) {
...
surfaceTextureHelper.setTextureSize(captureFormat.width, captureFormat.height);
startCapturing();
}
当实例化 Camera1Session 时,会首先设置 surfaceTexture 的缓冲大小,也就是分辨率,最后是调用 this.startCapturing()
在 startCapturing 实现体中,调用了 camera 的预览函数,我们一起来看下
private void startCapturing() {
Logging.d(TAG, "Start captur

本文详细分析了WebRTC Android中的Camera1和Camera2的采集流程,包括初始化、相机数据处理、预览及Native层的帧处理。通过时序图展示了从Java到JNI的调用过程,并探讨了Camera1和Camera2的差异。此外,文章还介绍了从源码层面如何进行相机数据的预览和渲染,并提供了一个实战Demo的GitHub链接。
312

被折叠的 条评论
为什么被折叠?



