前言:

前面有介绍了CreateCaptureSession,当这一步成功完成后,就可以开始启动预览、拍照的流程了,即进入了Request阶段。跟前面介绍openCamera和CreateCaptureSession一样,我们也从上到下介绍这个Request 阶段,即从APP、native framework、HAL这三部分来介绍。

一、APP部分

APP会通过下表中的方法开启预览或者拍照。Capture对应就是拍照相关的场景,Repeating对应预览、录像这些场景。其中Capture会优先于Repeating。

Type 方法 说明
Repeating setRepeatingRequest() CameraService会向底层不停重复地发这一个Request
setSingleRepeatingRequest() 功能与setRepeatingRequest()一样,只不过setRepeatingRequest()用的是handler指定回调线程,setSingleRepeatingRequest()用的是executor去指定的
setRepeatingBurst() CameraService会向底层不停重复地发一组Request,常用于AOSP Slow Motion
Capture capture() CameraService只会向底层发送一个Request,并且只发一次
captureSingleRequest() 功能与capture()一样,只不过capture()用的是handler指定回调线程,captureSingleRequest()用的是executor去指定的
captureBurstRequests() 向底层发送一组Request(这组Request是连续的),并且只发送一次。比如:连拍

无论是调用上面哪种方法,函数里的参数里主要有两个:一个是CaptureRequest,里面包含有meta信息和Surafces,Surfaces必须是createCaptureSession时配置的那些surface的子集,CaptureRequest可以理解为让Camera底层知道该如何去处理这一帧数据;另外一个就是CallBack回调类,这个类里面有好几个方法,需要App去实作,以便于当Request处理完毕或者底层在处理Request时发生了异常时,能在对应的回调函数里做出对应的响应,比如onCaptureStarted()、onCaptureProgressed() onCaptureCompleted()、onCaptureFailed()等方法。

无论是调用上面哪种方法,所带的CaptureRequest最终都会在CameraDeviceImpl里被统一包装成List<CaptureRequest>,再统一通过submitCaptureRequest()继续往下传。其中submitCaptureRequest这个函数参数里有repeating这个变量来标识当前这个是一个(组)Repeating request还是Capture Request,传递的flow如下:

   首先判断这次发送的Request是否是Repeating类型,如果是,则先stopRepeating(),这其实是将前一次发送的RepeatingRequest清除掉

1、convertSurfaceToStreamId():

在config阶段,已经将每路stream的streamId和它对应的相关配置信息存储在了mConfigureOutputs里。这里是从mConfigureOutputs里找到这单个request所带下来的所有surface对应的streamId与surfaceId,其中surfaceId从0递增。有带下来多少个request这里就会调用多少次

2、mRemoteDevice.submitRequestList()

这里调用的是CameraDeviceClient::submitRequestList(),这一步已经是在呼叫native framework层了,里面做的事情在后面native framework部分来分析。这里需要说明的是,无论是capture request还是Repeating request都是通过这个接口送到native framework的,只不过接口中的参数里有一个叫repeating的变量,它标记了这个request的类型,所以看起来这两种类型的request虽然都是通过相同的接口送下去的,但是底层对它们的行为可能还是会有差异的。

二、Native Framework

1、CameraDeviceClient::submitRequestList()

这里面要先做一些合法性检查,以及筛选和更新一些meta的信息。 比如:

  • input stream没有配置时、Repeating Request、multicam这三种情况不能有reprocess;
  • request带下来的surface target不能为空
  • 检查这一组Request里,每一个Request所带下来的PhysicalCameraSetting中的每一个key是否都有在SupportedPhysicalRequestKey当中,会把不包含范围内的Key过滤掉。

2、setStreamingRequestList() & captureList()

如果这个Request是Repeating类型的,就会调用Camera3Device::setStreamingRequestList();否则就调用Camera3Device::captureList();前面的时序图中红框中的flow是Repeating request走的流程,绿框中的是Capture request走的流程。从时序图中对比可以看出,它们之间的flow其实大同小异,接下来就看看这两条flow里一些共同的东西。这里需要点一下这两条flow最后的结果:capture request会被放在mRequestQueue里;Repeating  Request会放在mRepeatingRequests,无论是看code还是上面的时序图都可以看到这个结果。

2.1 convertMetadataListToRequestListLocked()

从这个方法的名字可以看出来,是要将meta资讯转换为Request。根据前面每个Request里的meta,创建List<sp<CaptureRequest>>,其中这里的CaptureRequest是定义在Camera3Device::HalInterface::CaptureRequest,其类图大致如下:

status_t Camera3Device::convertMetadataListToRequestListLocked(const List<const PhysicalCameraSettingsList> &metadataList,const std::list<const SurfaceMap> &surfaceMaps,bool repeating, nsecs_t requestTimeNs,RequestList *requestList) {if (requestList == NULL) {CLOGE("requestList cannot be NULL.");return BAD_VALUE;}int32_t burstId = 0;List<const PhysicalCameraSettingsList>::const_iterator metadataIt = metadataList.begin();std::list<const SurfaceMap>::const_iterator surfaceMapIt = surfaceMaps.begin();for (; metadataIt != metadataList.end() && surfaceMapIt != surfaceMaps.end();++metadataIt, ++surfaceMapIt) {sp<CaptureRequest> newRequest = setUpRequestLocked(*metadataIt, *surfaceMapIt);if (newRequest == 0) {CLOGE("Can't create capture request");return BAD_VALUE;}newRequest->mRepeating = repeating;newRequest->mRequestTimeNs = requestTimeNs;// Setup burst Id and request IdnewRequest->mResultExtras.burstId = burstId++;auto requestIdEntry = metadataIt->begin()->metadata.find(ANDROID_REQUEST_ID);if (requestIdEntry.count == 0) {CLOGE("RequestID does not exist in metadata");return BAD_VALUE;}newRequest->mResultExtras.requestId = requestIdEntry.data.i32[0];requestList->push_back(newRequest);ALOGV("%s: requestId = %" PRId32, __FUNCTION__, newRequest->mResultExtras.requestId);}if (metadataIt != metadataList.end() || surfaceMapIt != surfaceMaps.end()) {ALOGE("%s: metadataList and surfaceMaps are not the same size!", __FUNCTION__);return BAD_VALUE;}// Setup batch size if this is a high speed video recording request.if (mIsConstrainedHighSpeedConfiguration && requestList->size() > 0) {auto firstRequest = requestList->begin();for (auto& outputStream : (*firstRequest)->mOutputStreams) {if (outputStream->isVideoStream()) {(*firstRequest)->mBatchSize = requestList->size();outputStream->setBatchSize(requestList->size());break;}}}return OK;
}

由这个函数可以看出,其实就是将这些Meta资讯包装成CaptureRequest,这些Request就像前面说的Repeating  Request会放在mRepeatingRequests;capture request会被放在mRequestQueue里。

3、RequestThread::threadLoop()

前面有说到:capture request会被放在mRequestQueue里;Repeating  Request会放在mRepeatingRequests。threadLoop这个线程主要就是在向Hal层送Request,接下来看看其大致的流程

   3.1waitForNextRequestBatch():

要想往底层送Request,首先就得有Request;waitForNextRequestBatch这个方法里所做的事情就是在获取Request,而这个方法又主要是通过waitForNextRequestLocked()完成的,waitForNextRequestLocked这个方法每次能拿到一个Request,如果说是有N(batch size)个Request,那就会call N次waitForNextRequestLocked()去拿到这些Request。

waitForNextRequestLocked():优先从mRequestQueue里去取Request,每从队头拿到一个Request后,就会把这个Request从队头删除;当mRequestQueue里没有request时,则会先从mRepeatingRequests里拿到第一个Request,再把mRepeatingRequests里剩余的Request复制到mRequestQueue里面(这样后面就可以从mRequestQueue里直接拿Request了);这个函数每呼叫一次就会拿到一个Request,如果bach size>1的话(比如Consrained High Speed模式,即慢动作模式,App会调用setRepeatingBurst()同时下发多个CaptureRequest),则会呼叫多次,每拿到一次Request,这个Request其对应的frameNo会自动加1。deque出来的每一个Request又会被包装成NextRequest,它的数据结构如下:          

在开头有说过capture要优先于Repeating,这个是怎么体现出来的呢?答案就在这个waitForNextRequestLocked()函数里:RequestThread会优先从mRequestQueue里去拿Request,而Capture的Request就是直接放在mRequestQueue里的;只有在mRequestQueue里空的时候,才会去从mRepeatingRequests里面去取Request,把Request复制到mRequestQueue里。最后从mRequestQueue里面将Request一个一个地push到RequestThread::mNextRequests里面。

3.2 updateSessionParameters()

这里是为了检查这次的Request所带的Session param相对上一次Request的Session params是否有发生变化。因为如果有发生变化的话,可能会造成reconfig;如果没有发生变化的话,则肯定不用reconfig,直接返回false。

如果当前的Request所携带的Session params相对于前面的Request来说,有发生变化的话,需要交由HAL层去决策有没有必要reconfig一次,其中呼叫HAL层的接口为isReconfigurationRequired(),这个函数会把前一次的Session params和当前这次的Session params送到HAL层,让HAL层自己决定。这个接口定义在:

/hardware/interfaces/camera/devices/3.5/iCameraDeviceSession.hal

3.3 prepareHalRequests()

  • NextRequest::outputBuffers=insert(camera_stream_buffer_t,0,captureRequest->mOutputStreams.size())

NextRequest::outputBuffers是一个保存stream buffer的容器, 这里就是要在outputBuffers的容器中序号0的后面插入captureRequest->mOutputStreams.size()个camera_stream_buffer对象,即这个Request有需要Hal层输出几块Buffer,这里就有多少个camera_stream_buffer。

  • 根据是否支持UseHalBufferManager决定是否要去getBuffer。

如果不支持UseHalBufferManager,则需为每一个outputStream获取Buffer,从BufferQueue当中去deque GraphicBuffer。关于UseHalBufManager:如果没有开启UseHalBufManager,则Famework下request的时候,streamBuffer会包含buffer handle和Buffer Id;如果有开启UseHalBufferManager,FrameWork还是会带StreamBuffer,但是StreamBuffer不会包含buffer handle和Buffer Id,HAL要通过requestStreamBuffers()这个API去向FrameWork请求buffer,目的是在要真正使用buffer的时候,才去拿这块Buffer,以达到节省memory的效果。它是在openCamera()时,在camera3Device::initialize()时,根据ANDROID_INFO_SUPPORT_MANAGEMENT_VERSION来决定的,只有这个TAG的值为:ANDROID_INFO_SUPPORT_MANAGEMENT_VERSION_HIDL_DEVICE_3_X时才会将mUseHalBufManager置为true。

  • registerInflight()

将这个Request的信息封装成InFlightRequest,并注册进mInFlightMap,它里面的Requests是表示当前Hal层还未处理或者未处理完的Request,关于InFlightRequest这个类的信息可以看下面这个类图。      

3.4 sendRequestsBatch()

  • wrapAsHidlRequest(),

将每个Request都包装成Hal规范的Request,Hal的Request大致的数据结构图如下图所示:

这个函数里会构造Hal request的frameNumber,每块Buffer的Buffer handle、Buffer Id、Buffer streamId等资讯。关于Stream Id就不过多解释了,接下来看看这个Buffer Handle与Buffer Id。

  • Buffer Handle:即buffer_handle_t,里面有包含这个Buffer的内存地址。
  • Buffer Id:每一块Buffer都有自己的Id,Camera3Device的mBufferRecords这个成员变量如下图所示,其有一个mBufferIdMaps的变量,它就记录了每个Stream中某块Buffer的Id,也就是说根据StreamId和Buffer地址,可以确定一个Id值。因为BufferQueue中的Buffer是在不断轮转的,所以这个Id值也会是不断地在重复。

  • processCaptureRequest()

这个函数,即mHidlSession->processCaptureRequest(),就是将前面的一个(组)Hal request送进Hal层,然后就等待Hal层处理了。这个接口定义在:

/hardware/interfaces/camera/devices/3.X/iCameraDeviceSession.hal

三 Hal层

native framrwork中用processCaptureRequest()这个接口,通过Hidl将Request送进了Hal层;那Hal层拿到这个Request,就会根据这个Request里所带的资讯开始处理了,同样还是因为每个厂商在这一层的实作有差异,就不详细介绍了。

Hal层处理完Request后,会通过processCaptureResult()将处理结果返回给上层,这里在后面单独拿一篇简单介绍一下。

总结:

整个Requets过程,就是APP调用一次下request的接口,不管是Capture request,还是Repeating request,最后都会被push进RequestQueue里面。只不过Capture Request是直接push进mRequestQueue里的;而Repeating Request是先放到mRepeatingRequests里,再push进mRequestQueue,这就实现处理request时,Capture request比Repeating request优先级更高。RequestThread不断地去将mRepeatingRequests里的所有request复制出一份,不断地push到mRequestQueue里,如此循环,以达到不断地向底层重复下Request的目的。

framework在向Hal输送request之前,需要对request进行转换和包装,使之成为Hal规范的Request,这个Hal Request会包含有这个request的frameNumber、metadata资讯,如果是没有支持UseHalBufferManager,Hal Request还会带Buffer Handle和Buffer Id到Hal层;如果有支持UseHalBufferManager,就不会带Buffer Handle和Buffer Id给Hal层,而是等Hal层需要使用到这些Buffer时,再通过requestStreamBuffers()这个API去向FrameWork请求buffer,这样可以达到节省memory的效果。把Request送给Hal之后,就等待Hal的处理结果,最后Hal会通过ProcessCaptureResult()将处理结果返回。

关于Repeating,APP每新发送一次Repeating Requests,FW会先stopRepeating(),即把上一次mRepeatingRequests里的Requests清除掉,然后把新下的Repeating Requests放进去,然后开始新的Repeating循环。

Android Camera 之预览和拍照相关推荐

  1. Android Camera相机预览,拍照 ,横屏竖屏的处理

    功能分别是: 1.竖屏的前置,后置显示 加 拍照 2.横屏的前置,后置显示 加 拍照 整个项目的代码贴出来了,里面加了有备注,不理解的也可以去参考官网点击跳转到官网 首先选择前置后置布局效果如下 布局 ...

  2. Android屏幕计算正方形,Android Camera 正方形预览(二)

    一.概述 为实现Camera正方形预览,本Demo使用GLSurfaceView显示Camera预览数据,本文也主要针对GLSurfaceView的使用做介绍. Android中的视图有View.Su ...

  3. Android Camera摄像头预览黑屏原因(二)

    在Android系统中,操作Camera摄像头非常容易出问题,特别是Camera.open()的时候.就算open正常,也可能会出现预览界面黑屏的情况. 出现黑屏的时候,如果没有明显的报错,很大概率是 ...

  4. Android Camera 打开预览流程分析(一)--打开camera的SDK流程

    Android系统应用场景中,Camera的使用场景变得越来越重要,在手机端不管是牌照美颜,还是拍小视频上传小视频平台.在其他领域,如车载,倒车视频,360全景影像也同样会用到Camera接口.那我们 ...

  5. Mtk Camera 对预览和拍照做Flip + Mirror

    (1)Sensor Drv做Flip + Mirror效果 这里列举几个示例(会影响所有输出流) path: /kernel-4.19/drivers/misc/mediatek/imgsensor/ ...

  6. Android开发实践:掌握Camera的预览方向和拍照方向

    Android的Camera相关应用开发中,有一个必须搞清楚的知识点,就是Camera的预览方向和拍照方向,本文就重点讨论一下这个问题. 图像的Sensor方向:手机Camera的图像数据都是来自于摄 ...

  7. android 预览和拍照成像方向不一致,Android相机预览方向深入探究

    原标题:Android相机预览方向深入探究 今日快讯 9月7日,北京市交通委召集各区管理部门和15家共享自行车企业负责人,听取共享自行车投放和管理的意见建议,研究决定并下发通知,暂停在本市新增投放共享 ...

  8. Android的camerax预览拍照

    前面讲了,camera1和camera2的简单使用.从前文可以看出来,API 的调用还是比较繁琐的,而且像一些最佳尺寸,角度等还得自己算.由于camera1支持的特性很少,camera2流程又很繁琐, ...

  9. Android 音视频开发(三) -- Camera2 实现预览、拍照功能

    音视频 系列文章 Android 音视频开发(一) – 使用AudioRecord 录制PCM(录音):AudioTrack播放音频 Android 音视频开发(二) – Camera1 实现预览.拍 ...

最新文章

  1. OSI第四层:传输层功能及作用
  2. pandas中的DataFrame数据结构
  3. 课时36:类与对象:给大家介绍对象
  4. java垃圾回收菜鸟_java垃圾回收机制
  5. 简而言之Java.io:22个案例研究
  6. 【linux】ubuntu下crontab无效解决方法
  7. java数组深拷贝和浅拷贝_java List复制:浅拷贝与深拷贝
  8. Real-time HTML Editor (实时网页编辑器)
  9. Linux MySQL主主复制(Replication)(MySQL数据双向同步)配置
  10. java环境变量的作用和含义_java环境变量的配置及各环境变量的含义 | 学步园
  11. Android emoji表情处理
  12. 使命召唤 高级战争 闪退_使命召唤:现代战争进入第5季
  13. html好看的侧滑效果,H5 - 侧滑效果实现
  14. 【使用两个队列实现栈】
  15. python分析链家二手房信息----数据分析实战(一)
  16. so-vits-svc3.0 中文详细安装、训练、推理使用教程
  17. PAT福尔摩斯的约会
  18. 社区宽带繁忙是什么意思_康平附近企业宽带价位,附近光纤宽带上门安装多少钱...
  19. js逆向学习记录某真气网
  20. 小区物业报修管理系统JAVA Mysql

热门文章

  1. 淘宝小游戏了解一下?技术引擎让你喜提2000条锦鲤
  2. 季度增速连续下滑,小米发起双十一价格战
  3. 千品网、去哪儿网、大街网、京东商城、惠买商城、wuliwuli面试总结
  4. Ubuntu修改登录密码
  5. 【矩阵论笔记】子空间直和
  6. 如何用FIgma制作个人作品集教学
  7. php 原生sql 分页查询,Laravel框架执行原生SQL语句及使用paginate分页的方法
  8. 魔兽世界角色换装分析
  9. 从小红书app开启逆向之旅(2)
  10. 学术-物理:彭罗斯楼梯