Android camera createCaptureSession分析

在openCamera() 成功打开相机后,会通过CameraDevice.StateCallback回调接口onOpened()方法返回一个CameraDevice对象给应用层,而这个CameraDevice对象是一个CameraDeviceImpl,那么接下来的createCaptureSession就是调用它来实现的。

/frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java

    @Override
520    public void createCaptureSession(List<Surface> outputs,
521            CameraCaptureSession.StateCallback callback, Handler handler)
522            throws CameraAccessException {
523        List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
524        for (Surface surface : outputs) {
525            outConfigurations.add(new OutputConfiguration(surface));
526        }
527        createCaptureSessionInternal(null, outConfigurations, callback,
528                checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
529                /*sessionParams*/ null);
    }

第一个参数是一个范型为Surface的List,这里的Surface就是我们用来创建流的,一般如果没有特殊的要求,那我们只需要下两个Surface,一个提供预览,一个提供拍照,预览的Surface就是相机预览区域,buffer轮转时,预览区的buffer就是要从这个预览Surface当中获取的,这个Surface一定要正确,否则就会导致session创建失败,预览区就会黑屏了;而至于拍照Surface,我们一般使用ImageReader对象来获取,ImageReader是系统提供的一个类,它的创建过程已经为我们创建好了一个Surface,我们直接使用它来当作拍照Surface,当拍照成功后,我们就可以从ImageReader.OnImageAvailableListener内部类的onImageAvailable回调方法中获取到一个ImageReader对象,再调用getPlanes()获取到Plane数组,一般取第一个Plane,继续调用getBuffer()就可以获取到拍摄的照片的byte数组了

第二个参数callback的类型为frameworks\base\core\java\android\hardware\camera2\CameraCaptureSession.java类的内部类StateCallback,和openCamera一样,当session创建成功后,framework也会通过这个回调接口的onConfigured()方法返回一个CameraCaptureSession对象,而真正的实现是一个CameraCaptureSessionImpl对象,我们可以使用它来作很多的工作,比如断开session连接调用abortCaptures();拍照调用capture();预览调用setRepeatingRequest();停预览调用stopRepeating(),这里的设计和openCamera是完全一样的。

第三个参数Handler的作用和openCamera也一样,还是为了保证线程不发生切换,我们在应用进程的哪个工作线程中执行createCaptureSession,那么framework回调时也会通过这个handler把回调消息发送到当前handler线程的Looper循环上。

createCaptureSession()实际就是调用createCaptureSessionInternal()方法进一步处理的,这里的会把我们传入的surface列表进行一下转换,转换为OutputConfiguration对象,调用createCaptureSessionInternal方法时的第一个参数inputConfig一般为空,我们只需要关注outputConfig。

649    private void createCaptureSessionInternal(InputConfiguration inputConfig,
650            List<OutputConfiguration> outputConfigurations,
651            CameraCaptureSession.StateCallback callback, Executor executor,
652            int operatingMode, CaptureRequest sessionParams) throws CameraAccessException {
653        synchronized(mInterfaceLock) {
654            if (DEBUG) {
655                Log.d(TAG, "createCaptureSessionInternal");
656            }
657
658            checkIfCameraClosedOrInError();
659
660            boolean isConstrainedHighSpeed =
661                    (operatingMode == ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE);
662            if (isConstrainedHighSpeed && inputConfig != null) {
663                throw new IllegalArgumentException("Constrained high speed session doesn't support"
664                        + " input configuration yet.");
665            }
666
667            // Notify current session that it's going away, before starting camera operations
668            // After this call completes, the session is not allowed to call into CameraDeviceImpl
669            if (mCurrentSession != null) {
670                mCurrentSession.replaceSessionClose();
671            }
672
673            // TODO: dont block for this
674            boolean configureSuccess = true;
675            CameraAccessException pendingException = null;
676            Surface input = null;
677            try {
678                // configure streams and then block until IDLE

//执行surface配置, !!重要!!
679                configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,
680                        operatingMode, sessionParams);
681                if (configureSuccess == true && inputConfig != null) {
682                    input = mRemoteDevice.getInputSurface();
683                }
684            } catch (CameraAccessException e) {
685                configureSuccess = false;
686                pendingException = e;
687                input = null;
688                if (DEBUG) {
689                    Log.v(TAG, "createCaptureSession - failed with exception ", e);
690                }
691            }
692
693            // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
694            CameraCaptureSessionCore newSession = null;
695            if (isConstrainedHighSpeed) {
696                ArrayList<Surface> surfaces = new ArrayList<>(outputConfigurations.size());
697                for (OutputConfiguration outConfig : outputConfigurations) {
698                    surfaces.add(outConfig.getSurface());
699                }
700                StreamConfigurationMap config =
701                    getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
702                SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config);
703
704                newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
705                        callback, executor, this, mDeviceExecutor, configureSuccess,
706                        mCharacteristics);
707            } else {

//创建CameraCaptureSessionImpl对象
708                newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
709                        callback, executor, this, mDeviceExecutor, configureSuccess);
710            }
711
712            // TODO: wait until current session closes, then create the new session
713            mCurrentSession = newSession;
714
715            if (pendingException != null) {
716                throw pendingException;
717            }
718
719            mSessionStateCallback = mCurrentSession.getDeviceStateCallback();
720        }
721    }

/frameworks\base\core\java\android\hardware\camera2\impl\CameraCaptureSessionImpl.java

CameraCaptureSessionImpl构造函数调用mStateCallback.onConfigured(this)通知应用层,session已经创建成功,同时将当前的impl实现类对象返回给应用层。

下面看一下configureStreamsChecked方法的实现:

    /**
382     * Attempt to configure the input and outputs; the device goes to idle and then configures the
383     * new input and outputs if possible.
384     *
385     * <p>The configuration may gracefully fail, if input configuration is not supported,
386     * if there are too many outputs, if the formats are not supported, or if the sizes for that
387     * format is not supported. In this case this function will return {@code false} and the
388     * unconfigured callback will be fired.</p>
389     *
390     * <p>If the configuration succeeds (with 1 or more outputs with or without an input),
391     * then the idle callback is fired. Unconfiguring the device always fires the idle callback.</p>
392     *
393     * @param inputConfig input configuration or {@code null} for no input
394     * @param outputs a list of one or more surfaces, or {@code null} to unconfigure
395     * @param operatingMode If the stream configuration is for a normal session,
396     *     a constrained high speed session, or something else.
397     * @param sessionParams Session parameters.
398     * @return whether or not the configuration was successful
399     *
400     * @throws CameraAccessException if there were any unexpected problems during configuration
     */
    public boolean configureStreamsChecked(InputConfiguration inputConfig,
403            List<OutputConfiguration> outputs, int operatingMode, CaptureRequest sessionParams)
404                    throws CameraAccessException {
405        // Treat a null input the same an empty list
406        if (outputs == null) {
407            outputs = new ArrayList<OutputConfiguration>();
408        }
409        if (outputs.size() == 0 && inputConfig != null) {
410            throw new IllegalArgumentException("cannot configure an input stream without " +
411                    "any output streams");
412        }
413
414        checkInputConfiguration(inputConfig);
415
416        boolean success = false;
417
418        synchronized(mInterfaceLock) {
419            checkIfCameraClosedOrInError();
420            // Streams to create
421            HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs);
422            // Streams to delete
423            List<Integer> deleteList = new ArrayList<Integer>();
424
425            // Determine which streams need to be created, which to be deleted
426            for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
427                int streamId = mConfiguredOutputs.keyAt(i);
428                OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i);
429
430                if (!outputs.contains(outConfig) || outConfig.isDeferredConfiguration()) {
431                    // Always delete the deferred output configuration when the session
432                    // is created, as the deferred output configuration doesn't have unique surface
433                    // related identifies.
434                    deleteList.add(streamId);
435                } else {
436                    addSet.remove(outConfig);  // Don't create a stream previously created
437                }
438            }

//往DeviceHandler的Looper循环上发一个CallOnBusy的消息,表示接下来要开始配置surface
//处理繁忙状态了,然后调用stopRepeating()停预览
440            mDeviceExecutor.execute(mCallOnBusy);
441            stopRepeating();
442
443            try {
444                waitUntilIdle();

//通知CameraServer进程中的binder服务端对象,开始配置
//mRemoteDevice实际上就是CameraDeviceClient对象
446                mRemoteDevice.beginConfigure();
447
448                // reconfigure the input stream if the input configuration is different.
449                InputConfiguration currentInputConfig = mConfiguredInput.getValue();
450                if (inputConfig != currentInputConfig &&
451                        (inputConfig == null || !inputConfig.equals(currentInputConfig))) {
452                    if (currentInputConfig != null) {
453                        mRemoteDevice.deleteStream(mConfiguredInput.getKey());
454                        mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
455                                RE
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值