Android Camera setRepeatingRequest
前情回顾
前面我们我们看了一遍createCaptureSession的过程,知道它的作用是维护会话状态,为后续的捕获请求提供基础,那现在就开始看看setRepeatingRequest,也就是起预览的流程,这个流程是跟配流过程强相关的。
APP 层
既然提到app层,那我们肯定要看下上层如何使用,也就是调用API2的接口的,为了方便描述,还是将完整的打开相机,配流,以及起预览过程代码都写一下,我按顺序摆放了一下,可以直接按顺序看就行,流程一目了然:
// 重要变量需要注意 private CameraDevice mCameraDevice; private CameraCaptureSession mCaptureSession; private CaptureRequest.Builder mPreviewRequestBuilder; // 打开相机 private void openCamera() { if (mCameraId == null || mCameraManager == null) { return; } try { // 检查权限 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { return; } // 打开相机,传入相机ID、状态回调和后台处理器 mCameraManager.openCamera(mCameraId, mStateCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); runOnUiThread(() -> Toast.makeText(this, \"打开相机失败\", Toast.LENGTH_SHORT).show()); } } // 相机设备状态回调,处理相机打开、关闭、错误等状态,在成功打开回调方法里开始创建捕获会话 private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice camera) { // 相机成功打开,保存相机设备实例并配置流 mCameraDevice = camera; createCameraCaptureSession(); } @Override public void onDisconnected(@NonNull CameraDevice camera) { camera.close(); mCameraDevice = null; } @Override public void onError(@NonNull CameraDevice camera, int error) { camera.close(); mCameraDevice = null; runOnUiThread(() -> Toast.makeText(Camera2PreviewActivity.this, \"相机打开失败: \" + error, Toast.LENGTH_SHORT).show()); } }; // 创建相机捕获会话(配流过程) private void createCameraCaptureSession() { if (mCameraDevice == null || !mTextureView.isAvailable()) { return; } try { // 获取TextureView的SurfaceTexture并创建Surface SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture(); surfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); Surface previewSurface = new Surface(surfaceTexture); // 创建预览请求构建器 mPreviewRequestBuilder = mCameraDevice.createCaptureRequest( CameraDevice.TEMPLATE_PREVIEW); // 将Surface作为预览的目标(配流:指定数据输出到哪里) mPreviewRequestBuilder.addTarget(previewSurface); // 准备输出表面列表(当前只有预览一个流) List<Surface> outputSurfaces = new ArrayList(); outputSurfaces.add(previewSurface); // 创建相机捕获会话,配置输出流 mCameraDevice.createCaptureSession(outputSurfaces, mSessionCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } // 捕获会话状态回调,处理会话创建结果 private final CameraCaptureSession.StateCallback mSessionCallback = new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { // 会话配置成功,开始预览 mCaptureSession = session; startPreview(); } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { runOnUiThread(() -> Toast.makeText(Camera2PreviewActivity.this, \"预览配置失败\", Toast.LENGTH_SHORT).show()); } }; // 启动预览,这也是这篇文章需要说明的对象 private void startPreview() { if (mCameraDevice == null || mCaptureSession == null || mPreviewRequestBuilder == null) { return; } try { // 配置自动对焦模式 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 配置自动曝光模式 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); // 发送重复捕获请求以持续预览 mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, mBackgroundHandler); runOnUiThread(() -> Toast.makeText(Camera2PreviewActivity.this, \"预览已启动\", Toast.LENGTH_SHORT).show()); } catch (CameraAccessException e) { e.printStackTrace(); } }
到这里应用层的代码实现已经走完了,接下来就是Framework层代码了
Framework
按惯例,我们需要了解一下什么时候回调的onConfigured
frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
private void createCaptureSessionInternal(InputConfiguration inputConfig, List<OutputConfiguration> outputConfigurations, CameraCaptureSession.StateCallback callback, Executor executor, int operatingMode, CaptureRequest sessionParams) throws CameraAccessException { //让我们回顾一下配流过程,先配流,配流成功返回状态 try { configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations, operatingMode, sessionParams, createSessionStartTime); } catch (CameraAccessException e) { } // 官方注释引路,说明是在下面代码里面实现了onConfigured回调调用 // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise. // isConstrainedHighSpeed一般都是false,除非开启特殊慢录模式,这 //个我们不讨论,但CameraConstrainedHighSpeedCaptureSessionImpl //里面也是最终会调用到onConfigured,这里不再赘述,感兴趣的可以自行看下 if (isConstrainedHighSpeed) { newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++, callback, executor, this, mDeviceExecutor, configureSuccess, mCharacteristics); } else { newSession = new CameraCaptureSessionImpl(mNextSessionId++, input, callback, executor, this, mDeviceExecutor, configureSuccess); } }
可以看到下面是实例化了CameraCaptureSessionImpl,那这里面是怎么调用到onConfigured的呢,接着往下看它的构造函数:
frameworks/base/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
CameraCaptureSessionImpl(int id, Surface input, CameraCaptureSession.StateCallback callback, Executor stateExecutor, android.hardware.camera2.impl.CameraDeviceImpl deviceImpl, Executor deviceStateExecutor, boolean configureSuccess) { //那传入的configureSuccess肯定是true啊,那顺理成章就会回调到onConfigured了 if (configureSuccess) { mStateCallback.onConfigured(this); if (DEBUG) Log.v(TAG, mIdString + \"Created session successfully\"); mConfigureSuccess = true; } else { mStateCallback.onConfigureFailed(this); mClosed = true; // do not fire any other callbacks, do not allow any other work Log.e(TAG, mIdString + \"Failed to create capture session; configuration failed\"); mConfigureSuccess = false; } }
OK,看到这里就知道何时调用到onConfigured,如何调用到onConfigured,并且知道回调给的参数其实就是CameraCaptureSessionImpl,也就是app层提到的mCaptureSession是CameraCaptureSessionImpl实例, 这里我们可以开始我们的主线任务了,在回调到onConfigured之后,就会调用到关键函数setRepeatingRequest,对应app层代码里面的
// 发送重复捕获请求以持续预览 mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, mBackgroundHandler);
那就继续看下面的代码
frameworks/base/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@Override public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback, Handler handler) throws CameraAccessException { checkRepeatingRequest(request); synchronized (mDeviceImpl.mInterfaceLock) { checkNotClosed(); handler = checkHandler(handler, callback); if (DEBUG) { Log.v(TAG, mIdString + \"setRepeatingRequest - request \" + request + \", callback \" + callback + \" handler\" + \" \" + handler); } return addPendingSequence(mDeviceImpl.setRepeatingRequest(request, createCaptureCallbackProxy(handler, callback), mDeviceExecutor)); } }
需要注意的是他有一个请求,预览请求相关变量是CameraDevice.TEMPLATE_PREVIEW,前面app层也展现过,想了解可以回顾一下,那继续以主线跳转往下跟踪
/** * Notify the session that a pending capture sequence has just been queued. * * <p>During a shutdown/close, the session waits until all pending sessions are finished * before taking any further steps to shut down itself.</p> * * @see #finishPendingSequence */ private int addPendingSequence(int sequenceId) { mSequenceDrainer.taskStarted(sequenceId); return sequenceId; }
到这里咋不按套路出牌了呢,看注释意思是一个待处理的捕获序列刚刚已入队,啥啊,这里我们先放一下,回头看看这具体干啥的,先回头看看上一步是不是漏了什么
return addPendingSequence(mDeviceImpl.setRepeatingRequest(request, createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
可以看到上一步里面其实传了个看起来像是起预览的操作,只不过是调用mDeviceImpl里面的setRepeatingRequest函数,那我们直接过去看看怎么个事儿
frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback, Executor executor) throws CameraAccessException { List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); requestList.add(request); return submitCaptureRequest(requestList, callback, executor, /*streaming*/true); }
继续
private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, Executor executor, boolean repeating) throws CameraAccessException { //这里会看请求是不是带了surface的,没带的会执行报错的,所以app层的addTarget()是必要步骤 // Make sure that there all requests have at least 1 surface; all surfaces are non-null; for (CaptureRequest request : requestList) { if (request.getTargets().isEmpty()) { throw new IllegalArgumentException( \"Each request must have at least one Surface target\"); } for (Surface surface : request.getTargets()) { if (surface == null) { throw new IllegalArgumentException(\"Null Surface targets are not allowed\"); } } } //传入的是true,需要先停止前面的repeating if (repeating) { stopRepeating(); } //关键函数,后面继续从这里跟踪起预览过程,这里就开始进入native服务了,不再多解释, //不了解的话可以重新跟一下前面的文章,我想早点“下班”了,休假咋都在写文章啊我 requestInfo = mRemoteDevice.submitRequestList(requestArray, repeating); return requestInfo.getRequestId(); }
然后就通过binder调用到这里了
frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
binder::Status CameraDeviceClient::submitRequestList( const std::vector<hardware::camera2::CaptureRequest>& requests, bool streaming, /*out*/ hardware::camera2::utils::SubmitInfo *submitInfo) { if (streaming) { //预览环节 err = mDevice->setStreamingRequestList(metadataRequestList, surfaceMapList, &(submitInfo->mLastFrameNumber)); } else { //拍照环节 err = mDevice->captureList(metadataRequestList, surfaceMapList, &(submitInfo->mLastFrameNumber)); } return res;}
直接上高速,加速中
frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp
status_t Camera3Device::setStreamingRequestList( const List<const PhysicalCameraSettingsList> &requestsList, const std::list<const SurfaceMap> &surfaceMaps, int64_t *lastFrameNumber) { ATRACE_CALL(); return submitRequestsHelper(requestsList, surfaceMaps, /*repeating*/true, lastFrameNumber);}
status_t Camera3Device::submitRequestsHelper( const List<const PhysicalCameraSettingsList> &requests, const std::list<const SurfaceMap> &surfaceMaps, bool repeating, /*out*/ int64_t *lastFrameNumber) { if (repeating) { //预览流程 res = mRequestThread->setRepeatingRequests(requestList, lastFrameNumber); } else { //拍照流程 res = mRequestThread->queueRequestList(requestList, lastFrameNumber); } return res; }
继续预览流程
status_t Camera3Device::RequestThread::setRepeatingRequests( const RequestList &requests, /*out*/ int64_t *lastFrameNumber) { ATRACE_CALL(); Mutex::Autolock l(mRequestLock); if (lastFrameNumber != NULL) { *lastFrameNumber = mRepeatingLastFrameNumber; } mRepeatingRequests.clear(); mFirstRepeating = true; // mRepeatingRequests: 类型为RequestList,预览请求队列,这次请求添加之后,等待后续执行消费 mRepeatingRequests.insert(mRepeatingRequests.begin(), requests.begin(), requests.end()); unpauseForNewRequests(); mRepeatingLastFrameNumber = hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES; return OK;}
所以相当于到这里先告一段落了,现在要去跟踪怎么去消费添加到队列的预览请求
frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp
bool Camera3Device::RequestThread::threadLoop() { //省略n行代码,直接找重点 submitRequestSuccess = sendRequestsBatch(); return submitRequestSuccess; }
然后看sendRequestsBatch对应实现
bool Camera3Device::RequestThread::sendRequestsBatch() { res = mInterface->processBatchCaptureRequests(requests, &numRequestProcessed); return true;}
status_t Camera3Device::HalInterface::processBatchCaptureRequests( std::vector<camera_capture_request_t*>& requests,/*out*/uint32_t* numRequestProcessed) { err = mHidlSession->processCaptureRequest(captureRequests, cachesToRemove, resultCallback);}
HAL 层
熟悉吧,这是跳转到最终函数
hardware/qcom/camera/msm8998/QCamera2/HAL3/QCamera3HWI.cpp
int QCamera3HardwareInterface::processCaptureRequest( camera3_capture_request_t *request, List<InternalRequest> &internallyRequestedStreams){ if (mRawDumpChannel) { rc = mRawDumpChannel->initialize(IS_TYPE_NONE); if (rc != NO_ERROR) { LOGE(\"Error: Raw Dump Channel init failed\"); pthread_mutex_unlock(&mMutex); goto error_exit; } } if (mHdrPlusRawSrcChannel) { rc = mHdrPlusRawSrcChannel->initialize(IS_TYPE_NONE); if (rc != NO_ERROR) { LOGE(\"Error: HDR+ RAW Source Channel init failed\"); pthread_mutex_unlock(&mMutex); goto error_exit; } } if (mSupportChannel) { rc = mSupportChannel->initialize(IS_TYPE_NONE); if (rc < 0) { LOGE(\"Support channel initialization failed\"); pthread_mutex_unlock(&mMutex); goto error_exit; } } if (mAnalysisChannel) { rc = mAnalysisChannel->initialize(IS_TYPE_NONE); if (rc < 0) { LOGE(\"Analysis channel initialization failed\"); pthread_mutex_unlock(&mMutex); goto error_exit; } } if (mDummyBatchChannel) { rc = mDummyBatchChannel->setBatchSize(mBatchSize); if (rc < 0) { LOGE(\"mDummyBatchChannel setBatchSize failed\"); pthread_mutex_unlock(&mMutex); goto error_exit; } rc = mDummyBatchChannel->initialize(IS_TYPE_NONE); if (rc < 0) { LOGE(\"mDummyBatchChannel initialization failed\"); pthread_mutex_unlock(&mMutex); goto error_exit; } } if (mState == CONFIGURED && mChannelHandle) { //Then start them. LOGH(\"Start META Channel\"); rc = mMetadataChannel->start(); if (rc < 0) { LOGE(\"META channel start failed\"); pthread_mutex_unlock(&mMutex); return rc; } if (mAnalysisChannel) { rc = mAnalysisChannel->start(); if (rc < 0) { LOGE(\"Analysis channel start failed\"); mMetadataChannel->stop(); pthread_mutex_unlock(&mMutex); return rc; } } if (mSupportChannel) { rc = mSupportChannel->start(); if (rc < 0) { LOGE(\"Support channel start failed\"); mMetadataChannel->stop(); /* Although support and analysis are mutually exclusive today adding it in anycase for future proofing */ if (mAnalysisChannel) { mAnalysisChannel->stop(); } pthread_mutex_unlock(&mMutex); return rc; } for (List<stream_info_t *>::iterator it = mStreamInfo.begin();it != mStreamInfo.end(); it++) { QCamera3Channel *channel = (QCamera3Channel *)(*it)->stream->priv; LOGH(\"Start Processing Channel mask=%d\", channel->getStreamTypeMask()); rc = channel->start(); //最终在这里开启预览通道 if (rc < 0) { LOGE(\"channel start failed\"); pthread_mutex_unlock(&mMutex); return rc; } } } }
说明一下上面开启的几个辅助通道:
分析通道(Analysis Channel,即代码中的 mAnalysisChannel):用于将相机数据传递给上层进行算法分析(如人脸检测、条码识别等),不直接参与屏幕显示。
元数据通道(Metadata Channel,即 mMetadataChannel):专门处理相机的元数据(如曝光时间、ISO、对焦距离等),用于控制和反馈相机参数。
支持通道(Support Channel,mSupportChannel):辅助功能通道,可能用于特定硬件的扩展功能(如多摄像头协同、特殊模式处理等)。
通道的启动思路:先启动控制 / 辅助通道:确保元数据处理、算法分析等基础能力就绪。
再启动数据流通道,在辅助通道就绪后,启动实际的图像数据传输(如预览、拍照),保证数据处理链路完整。
QCamera3RegularChannel,QCamera3PicChannel这些通道存储在 stream->priv 中,每个通道对应一个具体的相机流(如预览流、拍照流),负责处理实际的图像数据传输(如预览画面、照片数据)
收工!
总结
终于算是告一段落了,后面有时间再优化一下,目前先这样,此文仅仅是了解起预览的过程,再后面就是拍照和录制视频流程了,看了前面的几章,我们基本了解相机子系统了。