createCaptureSession()

前言
在open Camera成功之后,便会调用createCaptureSession()进行配置操作,CaptureSession是APP与Camera设备之间的通道,从camera获取图像,或者是Reprocess图像,APP对Camera设备的控制都是通过这个CaptureSession来完成

createCaprureSession的整个过程从App调用createCaptureSession()开始,时序图如下:
createCaptureSession()_第1张图片
跟openCamera部分一样,也按照App、native framework、HAL这三个部分来解说createCaptureSession这个过程

一、App部分
在这部分对应于图中的CameraDeviceImpl.java部分。在openCamera部分说过,APP在调用openCamera()后会在回调函数onOpened()里获得一个初始化过的Camera设备,即CameraDeviceImpl。App在拿到这个CameraDeviceImpl后,就会通过它去创建session。
1、createCaptureSession(SessionConfiguration config)

   public void createCaptureSession(SessionConfiguration config)
            throws CameraAccessException {
        if (config == null) {
            throw new IllegalArgumentException("Invalid session configuration");
        }
 
        List outputConfigs = config.getOutputConfigurations();
        if (outputConfigs == null) {
            throw new IllegalArgumentException("Invalid output configurations");
        }
        if (config.getExecutor() == null) {
            throw new IllegalArgumentException("Invalid executor");
        }
        createCaptureSessionInternal(config.getInputConfiguration(), outputConfigs,
                config.getStateCallback(), config.getExecutor(), config.getSessionType(),
                config.getSessionParameters());
    }

其参数为SessionConfiguration,里面包含了创建Session所需的具体参数,主要成员变量及其作用如下图和表所示。
createCaptureSession这个方法里会先检查参数的合法性,其中必须要有output和executor。
如果参数检查没有问题,就会继续走进createCaptureSessionInternal(),这里面最主要的就是configureStreamsChecked(),接下来直接看configureStreamsChecked()这个方法。

createCaptureSession()_第2张图片
SessionConfiguration
mSessinType 当前这个Seesion的类型。是Regular,还是High_speed的
mStateCallback 接收当前这个Session状态变化情况的回调类。里面有对应session状态变化情况所对应的回调函数,APP必须要去实作这些函数,以便当前的Session状态发生变化后,能做出对应的响应
mOutputConfigurations 所有输出流的配置信息。其中每一个OutputConfiguration对应HAL的一路Stream
mInputConfig 输入流的配置信息
mSessionParameter 创建Session所需的其他参数

SessionConfiguration
mSessinType 当前这个Seesion的类型。是Regular,还是High_speed的
mStateCallback 接收当前这个Session状态变化情况的回调类。里面有对应session状态变化情况所对应的回调函数,APP必须要去实作这些函数,以便当前的Session状态发生变化后,能做出对应的响应
mOutputConfigurations 所有输出流的配置信息。其中每一个OutputConfiguration对应HAL的一路Stream
mInputConfig 输入流的配置信息
mSessionParameter 创建Session所需的其他参数

2、configureStreamsChecked()

public boolean configureStreamsChecked(InputConfiguration inputConfig,
            List outputs, int operatingMode, CaptureRequest sessionParams,
            long createSessionStartTime)
                    throws CameraAccessException {
        // Treat a null input the same an empty list
        if (outputs == null) {
            outputs = new ArrayList();
        }
        if (outputs.size() == 0 && inputConfig != null) {
            throw new IllegalArgumentException("cannot configure an input stream without " +
                    "any output streams");
        }
 
        checkInputConfiguration(inputConfig);
 
        boolean success = false;
 
        synchronized(mInterfaceLock) {
            checkIfCameraClosedOrInError();
            // Streams to create
            HashSet addSet = new HashSet(outputs);
            // Streams to delete
            List deleteList = new ArrayList();
 
            // Determine which streams need to be created, which to be deleted
            for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
                int streamId = mConfiguredOutputs.keyAt(i);
                OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i);
 
                if (!outputs.contains(outConfig) || outConfig.isDeferredConfiguration()) {
                    // Always delete the deferred output configuration when the session
                    // is created, as the deferred output configuration doesn't have unique surface
                    // related identifies.
                    deleteList.add(streamId);
                } else {
                    addSet.remove(outConfig);  // Don't create a stream previously created
                }
            }
 
            mDeviceExecutor.execute(mCallOnBusy);
            stopRepeating();
 
            try {
                waitUntilIdle();
 
                mRemoteDevice.beginConfigure();
 
                // reconfigure the input stream if the input configuration is different.
                InputConfiguration currentInputConfig = mConfiguredInput.getValue();
                if (inputConfig != currentInputConfig &&
                        (inputConfig == null || !inputConfig.equals(currentInputConfig))) {
                    if (currentInputConfig != null) {
                        mRemoteDevice.deleteStream(mConfiguredInput.getKey());
                        mConfiguredInput = new SimpleEntry(
                                REQUEST_ID_NONE, null);
                    }
                    if (inputConfig != null) {
                        int streamId = mRemoteDevice.createInputStream(inputConfig.getWidth(),
                                inputConfig.getHeight(), inputConfig.getFormat(),
                                inputConfig.isMultiResolution());
                        mConfiguredInput = new SimpleEntry(
                                streamId, inputConfig);
                    }
                }
 
                // Delete all streams first (to free up HW resources)
                for (Integer streamId : deleteList) {
                    mRemoteDevice.deleteStream(streamId);
                    mConfiguredOutputs.delete(streamId);
                }
 
                // Add all new streams
                for (OutputConfiguration outConfig : outputs) {
                    if (addSet.contains(outConfig)) {
                        int streamId = mRemoteDevice.createStream(outConfig);
                        mConfiguredOutputs.put(streamId, outConfig);
                    }
                }
 
                int offlineStreamIds[];
                if (sessionParams != null) {
                    offlineStreamIds = mRemoteDevice.endConfigure(operatingMode,
                            sessionParams.getNativeCopy(), createSessionStartTime);
                } else {
                    offlineStreamIds = mRemoteDevice.endConfigure(operatingMode, null,
                            createSessionStartTime);
                }
 
                mOfflineSupport.clear();
                if ((offlineStreamIds != null) && (offlineStreamIds.length > 0)) {
                    for (int offlineStreamId : offlineStreamIds) {
                        mOfflineSupport.add(offlineStreamId);
                    }
                }
 
                success = true;
            } catch (IllegalArgumentException e) {
                // OK. camera service can reject stream config if it's not supported by HAL
                // This is only the result of a programmer misusing the camera2 api.
                Log.w(TAG, "Stream configuration failed due to: " + e.getMessage());
                return false;
            } catch (CameraAccessException e) {
                if (e.getReason() == CameraAccessException.CAMERA_IN_USE) {
                    throw new IllegalStateException("The camera is currently busy." +
                            " You must wait until the previous operation completes.", e);
                }
                throw e;
            } finally {
                if (success && outputs.size() > 0) {
                    mDeviceExecutor.execute(mCallOnIdle);
                } else {
                    // Always return to the 'unconfigured' state if we didn't hit a fatal error
                    mDeviceExecutor.execute(mCallOnUnconfigured);
                }
            }
        }
 
        return success;
    }

FrameWork中的Buffer是依靠Stream(流)来管理的,在这次会话中,可能多路流同时存在(比如预览流、拍照流、录像流等等),但是并不是说所有的stream都需要去config一次,
在configureStreamsChecked()里首先要做的是需要判断哪些stream需要去新建,哪些需要被删除,哪些需要继续保留(复用)。
因为对于有些Stream而言,在前一次的Seesion里可能有创建过一次和它一模一样的stream,那这一次就不用再去重新创建,直接复用就好了;
但是前一次session里有的stream在这次Session里已经不会用到了,那就需要删除它。
接下来就是针对这些要新创建的Stream,逐一地去把它们创建出来。

同时,呼叫mDeviceExecutor.execute(mCallOnBusy),表明当前这个Session正处于繁忙的状态。由上面的时序图可以看到,接下来主要分为两个部分:

1、对每一路stream,都会根据这路stream的配置信息调用一次mRemoteDevice.createStream(),这个将在后面的native framework层具体讲述,这里需要注意的是:mRemoteDevice.createStream()这个方法只是创建了一路stream(即时序图中的红框部分),所以这次有多少路要新配置的stream,就会被调用多少次。
这个每次调用成功后,都会返回关于这路stream的StreamId,然后将streamId和对应的outputConfiguration以类似于的键值对的方式存储在CameraDeviceImpl::mConfiguredOutputs里。

			// Add all new streams
            for (OutputConfiguration outConfig : outputs) {
                if (addSet.contains(outConfig)) {
                    int streamId = mRemoteDevice.createStream(outConfig);
                    mConfiguredOutputs.put(streamId, outConfig);
                }
            }

2、调用mRemoteDevice.endConfigure(),这主要是配置HAl层,以及与BufferQueue建立起连接。
在以上两部分完成后,configureStreamsChecked()也就基本完成了。在所有的stream都配置完成后,接下来就是构造CameraCaptureSession这个类了,构造完毕后会调用Session的状态回调函数oncConfigured(),将创建好的Seesion返回。
下图是CameraDevice和CameraCaptureSession之间的关系图。

createCaptureSession()_第3张图片
二、Native Framework
接下来看看在APP部分分别说到的很重要的两步操作,即mRemoteDevice.createStream()和mRemoteDevice.endConfiguration(),这两部分已经到了native framework中。
1、CreteStream()
mRemoteDevice.createStream()即CameraDeviceClient::createStream()

1.1、CameraDeviceClient::createStream()

前面说过一个OutputConfiguration就对应一路stream,一个OutputConfiguration里,可能有多个surface,这些Surface共享这一路stream,所以严格地说,不是一个surface对应一路stream,而是一个OutputConfiguration对应一路stream。

const std::vector& bufferProducers =
outputConfiguration.getGraphicBufferProducers();
这里首先获取到OutputConfiguration里的所有GraphicBufferProducer,这个GraphicBufferProducer是Java surface跨进程传递下来的,有几个Java Surface就有几个GraphicBufferProducer。

SessionConfigurationUtils::createSurfaceFromGbp(streamInfo,
isStreamInfoValid, surface, bufferProducer, mCameraIdStr,
mDevice->infoPhysical(physicalCameraId), sensorPixelModesUsed, mPrivilegedClient);

CameraDeviceClient::createStream()主要是由GraphicBufferProducer(由Java surface传下来的)创建出native surface。
具体来说就是为这个OutputConfiguration里面的每一个GraphicBufferProducer调用createSurfaceFromGbp() ,创建出对应的native suface,即有多少GraphicBufferProducer就会对应有几个native surface,也是以后的consumer,它们和Java surface是一 一对应的。
需要注意的是,这个Outputconfiguration里的这组surface必须要有相同的width、height、format,否则会失败。
接下来就是将这些surfaces作为参数,送给Camera3Device::createStream()。

1.2、Camera3Device::createStream()
这个函数的参数有很多,其中就有我们在前面创建出来的native surface,接下来看看这这函数大概是在做什么事情

status_t Camera3Device::createStream(const std::vector>& consumers,
        bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
        android_dataspace dataSpace, camera_stream_rotation_t rotation, int *id,
        const String8& physicalCameraId, const std::unordered_set &sensorPixelModesUsed,
        std::vector *surfaceIds, int streamSetId, bool isShared, bool isMultiResolution,
        uint64_t consumerUsage) {
    ATRACE_CALL();
 
    Mutex::Autolock il(mInterfaceLock);
    nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
    Mutex::Autolock l(mLock);
 
//创建一个Camera3OutStream
    sp newStream;
 
 //根据format、isShared等信息去决定创建哪种OutStream
    if (format == HAL_PIXEL_FORMAT_BLOB) {
        ssize_t blobBufferSize;
        if (dataSpace == HAL_DATASPACE_DEPTH) {
            blobBufferSize = getPointCloudBufferSize();
            if (blobBufferSize <= 0) {
                SET_ERR_L("Invalid point cloud buffer size %zd", blobBufferSize);
                return BAD_VALUE;
            }
        } else if (dataSpace == static_cast(HAL_DATASPACE_JPEG_APP_SEGMENTS)) {
            blobBufferSize = width * height;
        } else {
            blobBufferSize = getJpegBufferSize(width, height);
            if (blobBufferSize <= 0) {
                SET_ERR_L("Invalid jpeg buffer size %zd", blobBufferSize);
                return BAD_VALUE;
            }
        }
        newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
                width, height, blobBufferSize, format, dataSpace, rotation,
                mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
                isMultiResolution);
    } else if (format == HAL_PIXEL_FORMAT_RAW_OPAQUE) {
        bool maxResolution =
                sensorPixelModesUsed.find(ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) !=
                        sensorPixelModesUsed.end();
        ssize_t rawOpaqueBufferSize = getRawOpaqueBufferSize(width, height, maxResolution);
        if (rawOpaqueBufferSize <= 0) {
            SET_ERR_L("Invalid RAW opaque buffer size %zd", rawOpaqueBufferSize);
            return BAD_VALUE;
        }
        newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
                width, height, rawOpaqueBufferSize, format, dataSpace, rotation,
                mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
                isMultiResolution);
    } else if (isShared) {
        newStream = new Camera3SharedOutputStream(mNextStreamId, consumers,
                width, height, format, consumerUsage, dataSpace, rotation,
                mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
                mUseHalBufManager);
    } else if (consumers.size() == 0 && hasDeferredConsumer) {
        newStream = new Camera3OutputStream(mNextStreamId,
                width, height, format, consumerUsage, dataSpace, rotation,
                mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
                isMultiResolution);
    } else {
        newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
                width, height, format, dataSpace, rotation,
                mTimestampOffset, physicalCameraId, sensorPixelModesUsed, streamSetId,
                isMultiResolution);
    }
 
//获取每个surface的surfaceId
    size_t consumerCount = consumers.size();
    for (size_t i = 0; i < consumerCount; i++) {
        int id = newStream->getSurfaceId(consumers[i]);
        if (id < 0) {
            SET_ERR_L("Invalid surface id");
            return BAD_VALUE;
        }
        if (surfaceIds != nullptr) {
            surfaceIds->push_back(id);
        }
    }
 
    newStream->setStatusTracker(mStatusTracker);
 
    newStream->setBufferManager(mBufferManager);
 
    newStream->setImageDumpMask(mImageDumpMask);
 
//将新建的newStream添加到Camera3Device::mOutputStreams里面
    res = mOutputStreams.add(mNextStreamId, newStream);
    if (res < 0) {
        SET_ERR_L("Can't add new stream to set: %s (%d)", strerror(-res), res);
        return res;
    }
 
    mSessionStatsBuilder.addStream(mNextStreamId);
 
//streamId +1
    *id = mNextStreamId++;
    mNeedConfig = true;
 
    // Continue captures if active at start
    if (wasActive) {
        ALOGV("%s: Restarting activity to reconfigure streams", __FUNCTION__);
        // Reuse current operating mode and session parameters for new stream config
        res = configureStreamsLocked(mOperatingMode, mSessionParams);
        if (res != OK) {
            CLOGE("Can't reconfigure device for new stream %d: %s (%d)",
                    mNextStreamId, strerror(-res), res);
            return res;
        }
        internalResumeLocked();
    }
    ALOGV("Camera %s: Created new stream", mId.string());
    return OK;
}

从上面看容易看出来,Camera3Device中的createStream()主要是在创建Camera3OutputStream,并将新建的Camera3OutputStream添加到Camera3Device::mOutputStreams里面。

    //将新建的newStream添加到Camera3Device::mOutputStreams里面
    res = mOutputStreams.add(mNextStreamId, newStream);
    
    //streamId +1
    *id = mNextStreamId++;

在Android Camera当中,buffer是交由stream来管理的,Camera Stream相关类的关系图如下,其中定义Buffer相关的属性:比如 size、format 、usage 、dataSpace等等,也定义并实现了操作Buffer的相关接口。
createCaptureSession()_第4张图片可以看到createStream()做的工作还是在framework当中,还没有call到Hal层,下面的endConfigure()就会call到Hal层了。

2. mRemoteDevice.endConfigure()

mRemoteDevice.endConfigure()即CameraDeviceClient::endConfigure(),前面主要的流程是:
CameraDeviceClient::endConfigure()
------->Camera3Device::configureStreams()
------>Camera3Device::filterParamsAndConfigureLocked()
------>Camera3Device::ConfigureStreamsLocked()
------->Camera3Device::HalInterface::configureStreams()

对比可以发现,前面mRemoteDevice.createStream()是在create Stream,而mRemoteDevice.endConfigure()这里其实是在config Stream,接下来就从Camera3Device::HalInterface::configureStreams()开始分析。

2.1 Camera3Device::HalInterface::configureStreams()

先将stream的配置信息转换为StreamConfiguration,把StreamId、operationMode、streamType、width、height、usage、format、dataSpace、rotation、sessionParams这些信息包在StreamConfiguration里面。接下来StreamConfiguration这包参数将会通过mHidlSession_3_X -> configureStreams_3_x(StreamConfiguration,configStreamsCB),传递到HAL层(即通过HIDL),开启HAL层的配置,HAL这部分会因为各个厂商的实现有所差异,会在后面HAL部分简单说明一下。

HAL层配置完毕后,会通过回调函数configStreamsCB通知framework,并在回调函数会返回一个HalStreamConfiguration。
因为在Hal层的配置过程当中,可能会根据平台的情况,对某些Stream的format、dataSpace、Usage进行override,所以在Hal层返回后,后面native framework层会根据Hal层配置出来的HalStreamConfiguration里的dataSpace、format、Usage这些信息给到BufferQueue那边(在下面讲解)。

2.2 finishConfiguration()
从前面的时序图上可以看出:这里面最重要的事情是configureQueueLocked(),这个函数里面最重要的又是configureConsumerQueueLocked(),可以看出这里面做的事情主要是:

2.2.1 mConsumer->connect(NATIVE_WINDOW_API_CAMERA,mBufferProducerListerner)

这一步主要是为了把stream的BufferProducerListerner注册到BufferQueueCore里面。其中BufferProducerListerner的作用是:当Consumer这边有释放或者丢弃Buffer时,BufferQueueCore可以将这个资讯通过BufferProducerListerner通知到BufferQueueProducer。

2.2.2 设置native surface的属性

native_window_set_usage(mConsumer.get(),mUsage)
native_window_set_buffers_dimensions(mConsumer.get(), width, height)
native_window_set_format(mConsumer.get(),format)
native_window_set_data_space(mConsumer.get(),data_space)
native_window_set_buffer_count(mConsumer.get(),mTotalBufferCount)
native_window_set_buffers_transform(mConsumer.get(),mTransform)

从上面这些方法的名字可以看出来,它们分别是在设置surface里buffer的usage、size、format、dataSpace、总的Buffer数量、buffer的旋转角度。上面这些接口最后会调用到frameworks/native/libs/gui/Surface.cpp里面的surface::perform(),这里面根据操作类型去设置surface里对应的属性

2.2.3 mBufferManager->registerStream(stream,steamInfo)
从源码里可以看到,当这路stream的usage为GRALLOC_USAGE_HW_COMPOSER或者GRALLOC_USAGE_HW_TEXTURE时,是走不进来的,因为它们有自己管理Buffer的逻辑。所以只有当stream的Uasge不为以上两种之一时,才能走进来。

可以看到走进来后,主要是将这路stream及其streamInfo注册到Camera3BufferManager中, 注册成功后,Camera3BufferManager将会把分配buffer的任务从BufferQueue的手里接管过来。

3.native framework层总结
native framework层先创建出这次Session中的Stream,即createStream(),主要是在创建出Camea3Stream,它包含了这路Stream的streamId、size、usage、format、dataSpace、rotation等描述这路stream属性的信息;
然后endConfigure(),会把这些stream的信息和sessionParam带到Hal层,HAL层去建立pipeline,Hal层有可能根据平台的情况,会对format、usage和dataSpace等信息进行override;
当HAL层config完成后,会去设置BufferQueue(将BufferQueueProducer的BufferProducerListerner注册到BufferQueueCore)、surface的相关信息,并将stream和stream的相关信息注册到Camera3BufferManager里。

一个OutputConfiguration对应于一条Camera Hal stream的配置信息,但是OutputConfiguration里的Surfaces为什么可能会有多个呢?因为有些Surface可能会share一路stream,目前支持最多4个surface共享一个OutputConfiguration,这样可以使得APP surface的数量比Hal层多。比如Hal层如果只能支持同时出3路流,但是APP可以采用surface sharing的方式,使得APP可以不只三个Surface,可以是4个、5个,甚至更多。在以后的Request阶段,也可以看到这样做好处是在不打断现有Repeating CaptureRequest的情况下,App可以切换不同的Output Surface。

当然要满足surface sharing的这些surface还要满足一些条件才可以,这些Surface之间需要有相同的size、format、dataSpace。并且Format也有限制,即使这些surface的format都相同了,其实还不一定可以share,它们之间的format除了相同,还必须是如下的format才可以:

Android P以前:只有ImageFormat#PRIVATE可以被Share

Android P及以后:除ImageFormat#JPEG和ImageFormat#RAW_PRIVATE外的其他format

三、HAL
在上面的CameraDeviceClient::endconfigure()中有说到过,在configureStreams()时会将StreamConfiguration通过mHidlSession_3_X ->configureStreams_3_x(StreamConfiguration,configStreamsCB),传递到HAL层(即通过HIDL),这里就开启了HAL层的配置

configureStreams_3_x这个binder interface定义在

/hardware/interfaces/camera/device/3.x/ICameraDeviceSession.hal

HAL层这一步主要就是建立pipeline,各个厂商要去实作这个Interface,各个厂家的具体实作肯定也是有差异的,所以这里Hal层的配置就简单略过了。

总结
简单总结一下,createCaptureSession主要是在创建这次Seesion中所需要的Stream,以及配置HAL层建立pipeline;并建立起native suface与BufferQueue的连接,向Camera3BufferManager注册Stream信息。以上完成后,APP就可以得到一个创建好的CaptureSession了,APP就可以通过这个Session和底层通话了。

另外前面在介绍在CameraDeviceClient::createStream()时,有跳过介绍一个步骤,即:createDeferedSurfaceStreamLocked(),这个后面再单独用一篇简单介绍一下,见Android Camera之Deferred Surface。

你可能感兴趣的:(camera,前端,java,算法)