Android Camera之createCaptureSession()

前言 

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


        createCaprureSession的整个过程从App调用createCaptureSession()开始,时序图如下:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6I-c6bihVVA=,size_20,color_FFFFFF,t_70,g_se,x_16

        跟openCamera部分一样,也按照App、native framework、HAL这三个部分来解说createCaptureSession这个过程

一、App部分

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

    1、createCaptureSession(SessionConfiguration) 

  public void createCaptureSession(SessionConfiguration config)
            throws CameraAccessException {
        if (config == null) {
            throw new IllegalArgumentException("Invalid session configuration");
        }

        List<OutputConfiguration> 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()这个方法。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6I-c6bihVVA=,size_20,color_FFFFFF,t_70,g_se,x_16

                                            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<OutputConfiguration> outputs, int operatingMode, CaptureRequest sessionParams,
            long createSessionStartTime)
                    throws CameraAccessException {
        // Treat a null input the same an empty list
        if (outputs == null) {
            outputs = new ArrayList<OutputConfiguration>();
        }
        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<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs);
            // Streams to delete
            List<Integer> deleteList = new ArrayList<Integer>();

            // 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<Integer, InputConfiguration>(
                                REQUEST_ID_NONE, null);
                    }
                    if (inputConfig != null) {
                        int streamId = mRemoteDevice.createInputStream(inputConfig.getWidth(),
                                inputConfig.getHeight(), inputConfig.getFormat(),
                                inputConfig.isMultiResolution());
                        mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
                                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,逐一地去把它们创建出来。
       同时,呼叫mCallOnBusy(),表明当前这个Session正处于繁忙的状态。由上面的时序图可以看到,接下来主要分为两个部分:
       1、对每一路stream,都会根据这路stream的配置信息调用一次mRemoteDevice.createStream(),这个将在后面的native framework层具体讲述,这里需要注意的是:mRemoteDevice.createStream()这个方法只是创建了一路stream(即时序图中的红框部分),所以这次有多少路要新配置的stream,就会被调用多少次。这个每次调用成功后,都会返回关于这路stream的StreamId,然后将streamId和对应的outputConfiguration以类似于<Key,Value>的键值对的方式存储在CameraDeviceImpl::mConfiguredOutputs里。

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之间的关系图。watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjQ2MzQ4Mg==,size_16,color_FFFFFF,t_70

二、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。

        这里首先获取到OutputConfiguration里的所有GraphicBufferProducer,这个GraphicBufferProducer是Java surface跨进程传递下来的,有几个Java Surface就有几个GraphicBufferProducer。

  • createSurfaceFromGbp()

        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<sp<Surface>>& 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<int32_t> &sensorPixelModesUsed,
        std::vector<int> *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<Camera3OutputStream> 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<android_dataspace>(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里面。 

      mOutputStreams.add(mNextStreamId, newStream);
      *id = mNextStreamId++;

        在Android Camera当中,buffer是交由stream来管理的,Camera Stream相关类的关系图如下,其中定义Buffer相关的属性:比如 size、format 、usage 、dataSpace等等,也定义并实现了操作Buffer的相关接口。

5effbf084e7c4dd8a38d56b4e7769483.png

        可以看到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 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_buffers_format(mConsumer.get(),format)
  • native_window_set_buffers_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

 

 

 

 

 

 

 

 

 

 

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android Camera2 API是Android系统提供的一种新的相机框架,用于实现相机预览和采集功能。Camera2 API可以提供更强大、更灵活的相机控制和图像处理能力。 相比于传统的Camera API,Camera2 API具有以下优势: 1. 能够同时管理多个相机设备:Camera2 API支持同时管理多个相机设备,包括前置摄像头、后置摄像头、深度摄像头等,可以方便地进行相机切换和管理。 2. 提供更灵活的相机控制:Camera2 API提供了更丰富的相机控制选项,可以精确地设置曝光时间、ISO值、焦距等参数,以便于用户根据不同的拍摄场景进行调整。 3. 支持原生的相机预览和采集:Camera2 API提供了对原生的预览和采集数据的支持,可以更高效地获取图像数据,用户可以利用这些数据进行实时的图像处理或者保存。 4. 支持原生的相机回调:Camera2 API提供了原生的相机回调接口,用户可以通过设置回调函数来实时获取相机的状态和图像数据,从而实现一些特殊的相机功能,如实时人脸检测、连拍等。 总之,Android Camera2 API提供了更强大、更灵活的相机功能,可以满足开发者对相机预览和采集的各种需求。无论是在智能手机、平板电脑还是其他移动设备上,都可以通过Camera2 API来实现高质量的相机应用。 ### 回答2: Android Camera 2是Android系统中用于处理相机功能的全新API。相较于之前的Camera API,Camera 2更加强大和灵活,提供了更多的功能和优化。 在Android Camera 2中,预览采集是通过创建一个CameraDevice实例来实现的。首先,需要获取相机的相关信息,例如相机的ID、参数等。然后,通过调用CameraManager的openCamera方法来打开指定ID的相机。 在相机打开后,可以通过创建一个CaptureRequest.Builder对象来配置相机的预览设置。可以设置预览的目标Surface、预览图像的尺寸、帧率等。然后,通过调用createCaptureSession方法,传入预览的目标Surface和一个CameraCaptureSession.StateCallback来创建一个预览会话。 在预览会话创建后,可以调用setRepeatingRequest方法来开始预览。Camera2会自动将预览帧数据传输到指定的Surface上,并在Surface上更新预览图像。同时,可以通过设置回调函数来处理预览数据,例如实时显示预览图像、进行进一步的图像处理等。 在预览过程中,还可以根据需要调整相机的参数,例如调整曝光度、焦距、白平衡等。通过创建新的CaptureRequest.Builder对象并设置相应参数,然后调用Session的setRepeatingRequest方法,可以实现动态调整相机参数。 总而言之,Android Camera 2提供了更强大和灵活的方式来实现相机预览采集。通过合理地使用Camera2的API,我们可以轻松地实现各种功能,例如实时预览、图像处理、动态调整参数等。 ### 回答3: Android Camera2预览采集是通过使用Android操作系统的Camera2 API来实现的一种方式。Camera2 API是Android 5.0(Lollipop)版本引入的相机框架,它提供了更多的功能和灵活性,用于控制和管理Android设备上的相机。 使用Camera2 API进行预览采集是一种相对较新和高级的方法,相比传统的Camera API,Camera2 API提供了更多的控制选项和功能,以及更好的性能和稳定性。 通过Camera2 API,我们可以实时地从Android设备的摄像头获取图像,并将图像传输到屏幕上进行实时预览。预览采集可以在应用程序中使用,例如用于视频通话、拍照、视频录制等场景。 Camera2 API的预览采集过程主要涉及以下步骤: 1. 获取相机设备:通过CameraManager类获取设备的摄像头列表,并选择要使用的摄像头设备。 2. 创建相机会话:使用CameraDevice类进行连接和建立与相机设备的会话。 3. 创建预览请求:使用CaptureRequest.Builder类创建一个预览请求,并设置相应的参数,例如预览尺寸、帧率等。 4. 创建预览会话:使用CameraDevice类创建预览会话,并将预览请求设置为预览会话的目标。 5. 开启预览:将预览会话设置为活动状态,相机将开始实时地捕获图像,并通过指定的Surface进行预览。 通过这些步骤,我们可以在Android设备上实现相机预览采集功能,并根据需要进行自定义设置和扩展。预览采集可以进一步应用于更多的相机应用场景,例如人脸识别、图像处理等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值