Android Camera 之预览和拍照

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

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

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

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

Android Camera 之预览和拍照_第2张图片

首先判断这次发送的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()

1.1,这里面要先做一些合法性检查,以及筛选和更新一些meta的信息。 比如:
1.2,input stream没有配置时、Repeating Request、multicam这三种情况不能有reprocess;
request带下来的surface target不能为空;
1.3,检查这一组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,其中这里的CaptureRequest是定义在
Camera3Device.h Camera3Device::HalInterface::CaptureRequest,其类图大致如下:

Android Camera 之预览和拍照_第3张图片

status_t Camera3Device::convertMetadataListToRequestListLocked(
        const List &metadataList,
        const std::list &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_iterator metadataIt = metadataList.begin();
    std::list::const_iterator surfaceMapIt = surfaceMaps.begin();
    for (; metadataIt != metadataList.end() && surfaceMapIt != surfaceMaps.end();
            ++metadataIt, ++surfaceMapIt) {
        sp 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 Id
        newRequest->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,接下来看看其大致的流程

if (repeating) {
      res = mRequestThread->setRepeatingRequests(requestList, lastFrameNumber);
  } else {
      res = mRequestThread->queueRequestList(requestList, lastFrameNumber);
  }

Android Camera 之预览和拍照_第4张图片
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,它的数据结构如下:
Android Camera 之预览和拍照_第5张图片
在开头有说过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=insertAt(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,则FameRequestW下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。

 camera_metadata_entry bufMgrMode =
 mDeviceInfo.find(ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION);
 if (bufMgrMode.count > 0) {
 mUseHalBufManager = (bufMgrMode.data.u8[0] ==
 ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
 }

registerInflight()

mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput,
            hasAppCallback, maxExpectedDuration, physicalCameraIds, isStillCapture, isZslCapture,
            rotateAndCropAuto, cameraIdsWithZoom, requestTimeNs, outputSurfaces));

将这个Request的信息封装成InFlightRequest,并注册进mInFlightMap,它里面的Requests是表示当前Hal层还未处理或者未处理完的Request,关于InFlightRequest这个类的信息可以看下面这个类图。
Android Camera 之预览和拍照_第6张图片
3.4 sendRequestsBatch()

wrapAsHidlRequest(),

captureRequest->frameNumber = request->frame_number;        
captureRequest->fmqSettingsSize = 0;

captureRequest->inputBuffer.streamId = streamId;              
captureRequest->inputBuffer.bufferId = bufferId;
captureRequest->inputBuffer.buffer = (isNewBuffer) ? buf : nullptr;
captureRequest->inputBuffer.status = BufferStatus::OK;
captureRequest->inputBuffer.acquireFence = acquireFence;
captureRequest->inputBuffer.releaseFence = nullptr;

inflightBuffers->push_back(std::make_pair(captureRequest->frameNumber, streamId));

将每个Request都包装成Hal规范的Request,Hal的Request大致的数据结构图如下图所示:
Android Camera 之预览和拍照_第7张图片

这个函数里会构造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值也会是不断地在重复。
Android Camera 之预览和拍照_第8张图片
processCaptureRequest_3_x()
这个函数,即mHidlSession->processCaptureRequest_3_x(),就是将前面的一个(组)Hal request送进Hal层,然后就等待Hal层处理了。
这个接口定义在:

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

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

总结:

整个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循环。

你可能感兴趣的:(camera,android,java,开发语言)