Android camera

Media Module之Camera(二) 初始化

2017-05-31  本文已影响2040人  Lemon_Home

2. 初始化

初始化主要分为几个部分,SM注册service,app层和framework层去打开相机操作,底层的服务的绑定,底层server端的初始化,HAL层去open。

2.1 注册service

手机开机后,会走init.rc流程,init.rc会启动MediaServer Service。
system/core/rootdir/init.rc

service media /system/bin/mediaserver
    class main
    user media
    group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm qcom_diag
    ioprio rt 4

在启动MediaServer之后会进入其main方法中:
frameworks\av\media\mediaserver\main_mediaserver.cpp

int main(int argc __unused, char** argv)
{
    ... ...
    if (doLog && (childPid = fork()) != 0) {
        ... ...
    } else {
        CameraService::instantiate();
    }
}

对于CameraService::instantiate()方法来说,是CameraService的父类BinderService来实现的:
frameworks\native\include\binder\BinderService.h

namespace android {

template<typename SERVICE>
class BinderService
{
public:
    static status_t publish(bool allowIsolated = false) {
        sp<IServiceManager> sm(defaultServiceManager());
        return sm->addService(
                String16(SERVICE::getServiceName()),
                new SERVICE(), allowIsolated);
    }
... ...
    static void instantiate() { publish(); }
... ...

可以发现在publish()函数中,CameraService完成服务的注册 。SERVICE是个模板,这里是注册CameraService,所以可用CameraService代替
return sm->addService(String16(CameraService::getServiceName()), new CameraService());

这样,Camera就在ServiceManager完成服务注册,提供给client随时使用。因为由init.rc在启动时调用,所以在设备开机的时候Camera就会注册一个服务,用作binder通信。

2.2 从launcher进入相机

主要流程

在onCreate 可以看到从SharedPreferences 文件中获取初始化的模式,根据SharedPreferences文件中的filterMode的值决定初始化模式是普通还是全景还是特效模式;

@Override
    public void onCreate(Bundle state) {
        super.onCreate(state);
        System.out.println(888);
.................
.................
.................

/**
紧接着下面对其他应用访问相机根据其intent做了判断处理,如果拍照就进入拍照模式,录像就进入录像模式;
**/
int moduleIndex = -1;
        if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())|| MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
            moduleIndex = ModuleSwitcher.VIDEO_MODULE_INDEX;
        } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(getIntent().getAction())
                || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(getIntent()
.getAction())) {
            moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (prefs.getInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, -1) == ModuleSwitcher.GCAM_MODULE_INDEX && GcamHelper.hasGcamCapture()) {
                moduleIndex = ModuleSwitcher.GCAM_MODULE_INDEX;
            }
        } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
                || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
            moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;//信息访问相机拍照(android.media.action.IMAGE_CAPTURE);
        } else {
                moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
        }

setModuleFromIndex(moduleIndex);
mCurrentModule.init(this, mCameraModuleRootView) ;
.................
.................
.................
registerSDcardMountedReceiver();//注册SD卡挂载的监听,弹出提示是否保存到SD卡

MediaStore这个类是android系统提供的一个多媒体数据库,android中多媒体信息都可以从这里提取。这个MediaStore包括了多媒体数据库的所有信息,包括音频,视频和图像,android把所有的多媒体数据库接口进行了封装,所有的数据库不用自己进行创建,直接调用利用ContentResolver去调用那些封装好的接口就可以进行数据库的操作了。

oncreate中还注册了一些监听、观察者,动态注册SD卡是否挂载的广播;初始化一些服务;以及安全模式与非安全模式的查看已拍摄图片的一些处理;有兴趣自己可以查看;

安全模式:有密码锁屏;非安全模式:无密码设置;

初次进入默认的是moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;

onCreate 调用setModuleFromIndex 方法, 根据传进来的ID进行模式初始化;

private void setModuleFromIndex(int moduleIndex) {
        mCurrentModuleIndex = moduleIndex;
        switch (moduleIndex) {
            case ModuleSwitcher.VIDEO_MODULE_INDEX:
                mCurrentModule = new VideoModule();
                break;

            case ModuleSwitcher.PHOTO_MODULE_INDEX:
                mCurrentModule = new PhotoModule();
                break;

            .......
            .......
            .......
            default:
                // Fall back to photo mode.
                mCurrentModule = new PhotoModule();
                mCurrentModuleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
                break;
        }
    }

photoModule.java

@Override
    public void init(CameraActivity activity, View parent) {
        System.out.println(151515);
        mActivity = activity;
      ...........
      ...........
      ...........
        if (mOpenCameraThread == null) {
            mOpenCameraThread = new OpenCameraThread();
            mOpenCameraThread.start();
        }
        mUI = new PhotoUI(activity, this, parent);

        initializeControlByIntent();//根据传进来的intent进行初始化
      ...........
      ...........
      ...........
        Storage.setSaveSDCard(
            mPreferences.getString(CameraSettings.KEY_CAMERA_SAVEPATH, "0").equals("1"));//根据设置菜单的参数判断保存的位置是SD卡还是本机

        mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
        mRefocusSound = mSoundPool.load(mActivity, R.raw.camera_click_x5, 1);
    }

photoModule.java

private void initializeControlByIntent() {
        mUI.initializeControlByIntent();
        if (mIsImageCaptureIntent) {
            System.out.println("AAAA");
            setupCaptureParams();
        }
    }

photoUI.java

public void initializeControlByIntent() {
        System.out.println(171717);
        mBattery = (ImageView) mRootView.findViewById(R.id.battery_img);
        mBurstImg = (ImageView) mRootView.findViewById(R.id.burst_img);
        mThumbnail = (ImageView) mRootView.findViewById(R.id.preview_thumb);
        mThumbnail.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!CameraControls.isAnimating()
                        && mController.getCameraState() != PhotoController.SNAPSHOT_IN_PROGRESS)
                    mActivity.gotoGallery();//点击缩略图进入到图库
            }
        });
        mMenuButton = mRootView.findViewById(R.id.menu);
        mMenuButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if(mMenu != null){
                    System.out.println(212121);
                    mMenu.openFirstLevel();
                    mVideoMenu.openFirstLevel();
                }
            }
        });
        if (mController.isImageCaptureIntent()) {//其他应用(信息)访问会走进这里
            System.out.println(181818);
            hideSwitcher();
            mMenuButton.setVisibility(View.VISIBLE);
            mTimerSwitcher.setVisibility(View.VISIBLE);
            showTimerPickers();
            //yanglei modify for bug 67085 20150708 start
//            mSwitcher.setImageCaptureIntent(true);
            //yanglei modify for bug 67085 20150708 end
            ViewGroup cameraControls = (ViewGroup) mRootView.findViewById(R.id.camera_controls);
            mActivity.getLayoutInflater().inflate(R.layout.review_module_control, cameraControls);

            mReviewDoneButton = mRootView.findViewById(R.id.btn_done);
            mReviewCancelButton = mRootView.findViewById(R.id.btn_cancel);
            mReviewRetakeButton = mRootView.findViewById(R.id.btn_retake);
            mReviewImage = (ImageView) mRootView.findViewById(R.id.review_image);
            mReviewCancelButton.setVisibility(View.VISIBLE);
            mVideoButton.setVisibility(View.GONE);
            mModelSwitcher.setVisibility(View.GONE);

            mReviewDoneButton.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    mController.onCaptureDone();//拍照的监听
                }
            });
            mReviewCancelButton.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    mController.onCaptureCancelled();//取消拍照的监听
                }
            });

            mReviewRetakeButton.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    mController.onCaptureRetake();//拍完之后不满意重新拍的监听
                }
            });
        }
    }

photoModule.java

@Override
    public boolean isImageCaptureIntent() {
        String action = mActivity.getIntent().getAction();
        return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
                || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
    }

photoModule.java

private void setupCaptureParams() {
        Bundle myExtras = mActivity.getIntent().getExtras();
        if (myExtras != null) {
            mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
            mCropValue = myExtras.getString("crop");
        }
    }

在onResume()方法中进入相应的模式
CameraActivity.java

@Override
    public void onResume() {
        ..............
        ..............
        ..............
        mCurrentModule.onResumeBeforeSuper();
        super.onResume();
        mCurrentModule.onResumeAfterSuper();
        setSwipingEnabled(true);
        if (mResetToPreviewOnResume) {
            // Go to the preview on resume.
            mFilmStripView.getController().goToFirstItem();
        }
        // Default is showing the preview, unless disabled by explicitly
        // starting an activity we want to return from to the filmstrip rather
        // than the preview.
        mResetToPreviewOnResume = true;
        ..............
        ..............
    }

mFilmStripView胶片视图,可以看成用胶片录电影,最前面的是正在录制,后面的是已经录制完毕的; goToFirstItem()可以理解为到最开始录的胶片,即预览的界面;

然后会进入photoModule.java中,注意在cameraActivity中的onResumeBeforeSuper()与onResumeAfterSuper()方法的实现是在相应的模式中;
onResumeBeforeSuper()作用是做一些UI显示及动态注册广播,及判断SD卡是否存在可写,不可写则保存到内部存储,并将保存路径用sharedPreference保存起来;
onResumeAfterSuper():主要启动onResumeTasks();方法,更新剩余内存的大小
更新剩下可以拍摄的照片的数量

@Override
    public void onResumeAfterSuper() {
        // Add delay on resume from lock screen only, in order to to speed up
        // the onResume --> onPause --> onResume cycle from lock screen.
        // Don't do always because letting go of thread can cause delay.
        String action = mActivity.getIntent().getAction();
        if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
                || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
            Log.v(TAG, "On resume, from lock screen.");
            // Note: onPauseAfterSuper() will delete this runnable, so we will
            // at most have 1 copy queued up.
            mHandler.postDelayed(new Runnable() {
                public void run() {
                    onResumeTasks();
                }
            }, ON_RESUME_TASKS_DELAY_MSEC);
        } else {
            Log.v(TAG, "On resume.");
            onResumeTasks();
        }
        mHandler.post(new Runnable(){
            @Override
            public void run(){
                mActivity.updateStorageSpaceAndHint();//更新剩余内存的大小
                updateRemainingPhotos();//更新剩下可以拍摄的照片的数量
            }
        });}
    private void onResumeTasks() {
        Log.v(TAG, "Executing onResumeTasks.");
        System.out.println(333);
        if (mOpenCameraFail || mCameraDisabled) return;

        if (mOpenCameraThread == null) {
            mOpenCameraThread = new OpenCameraThread();
            mOpenCameraThread.start();
        }

        mJpegPictureCallbackTime = 0;
        mZoomValue = 0;

        // If first time initialization is not finished, put it in the
        // message queue.
        if (!mFirstTimeInitialized) {
            mHandler.sendEmptyMessage(FIRST_TIME_INIT);
        } else {
            initializeSecondTime();
   .............
   .............
   .............

通过onResumeTasks()方法,打开相机,开启预览

private class OpenCameraThread extends Thread {
        @Override
        public void run() {
            openCamera();// 第一步开启camera
            startPreview();// 第二部开始预览
        }
    }   
private void openCamera() {
        // We need to check whether the activity is paused before long
        // operations to ensure that onPause() can be done ASAP.
        if (mPaused) {
            return;
        }
        Log.v(TAG, "Open camera device.");
        mCameraDevice = CameraUtil.openCamera(
                mActivity, mCameraId, mHandler,
                mActivity.getCameraOpenErrorCallback());
        if (mCameraDevice == null) {
            Log.e(TAG, "Failed to open camera:" + mCameraId);
            mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL);
            return;
        }
       .......
       .......
       .......
    }

mCameraDevice = CameraUtil.openCamera(
mActivity, mCameraId, mHandler,
mActivity.getCameraOpenErrorCallback())
其中mCameraDevice是com.android.camera.CameraManager.CameraProxy;的实例。

CameraUtil属于公共的API,用于给不同模块提供对Camera的不同操作。
在它openCamera 方法里,执行:
CameraHolder.instance().open(handler, cameraId, cb);
其中CameraHolder.instance()单例模式。目的就是控制,相机开启的只有能是一个。再次开启前,要确认上次已经销毁。

b、 在CameraManager系统服务,获得camera设备对象为Camera设备提供操作的方法。

@Override
    public CameraManager.CameraProxy cameraOpen(
        Handler handler, int cameraId, CameraOpenErrorCallback callback) {
        mCameraHandler.obtainMessage(OPEN_CAMERA, cameraId, 0,
                CameraOpenErrorCallbackForward.getNewInstance(
                        handler, callback)).sendToTarget();
        mCameraHandler.waitDone();
        if (mCamera != null) {
            return new AndroidCameraProxyImpl();// ----->这个就是丢给前台的mCameraDevice

        } else {
            return null;
        }
    }

CameraProxy 里AndroidCameraProxyImpl类这个接口就是将对Camera 接受和发送的操作,送达到Camera设备。
这个接口的实现类是AndroidCameraProxyImpl,这个类属于AndroidCameraManagerImpl的内部类。在AndroidCameraManagerImpl.java 里面还有一个内部类CameraHandler,这个类属于Handler。在CameraHandler的handleMessage方法里,就是根据不同的消息参数来对android.hardware.Camera 进行控制。比如打开,释放,对焦,变焦等等

2.3 服务绑定

Binder服务已经在系统中注册,下面分析服务的客户端和服务端是如何绑定的。首先在app层中,调用了framework Camera.java中的open(int cameraId)方法:

public static Camera open(int cameraId) {
    return new Camera(cameraId);
}

调用了构造函数:

Camera(int cameraId) {
    int err = cameraInitNormal(cameraId);
... ...
}

在构造函数中调用了cameraInitNormal方法:

private int cameraInitNormal(int cameraId) {
    return cameraInitVersion(cameraId, CAMERA_HAL_API_VERSION_NORMAL_CONNECT);
    // 代表camera能够正常连接和打开的常量
}

接下来调用的是cameraInitVersion方法:

private int cameraInitVersion(int cameraId, int halVersion) {
    mShutterCallback = null;
    // 将各种回调设置初始值
... ...
    // 新建looper进行消息循环
    Looper looper;
    if ((looper = Looper.myLooper()) != null) {
        mEventHandler = new EventHandler(this, looper);
    } else if ((looper = Looper.getMainLooper()) != null) {
        mEventHandler = new EventHandler(this, looper);
    } else {
        mEventHandler = null;
    }

    String packageName = ActivityThread.currentOpPackageName();

    // 强制使用HAL1的版本,如果存在 HAL2异常的情况
 ... ...
    //调用native方法native_setup
    return native_setup(new WeakReference<Camera>(this), cameraId, halVersion, packageName);
}

可以看出,在构造方法的层级调用中,走到了native方法native_setup。下面分析此native方法:

static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
    jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName)
{
... ...
    sp<Camera> camera;
    if (halVersion == CAMERA_HAL_API_VERSION_NORMAL_CONNECT) {
        // Default path: hal version is don't care, do normal camera connect.
//clientName是通过将clientPackageName从jstring转换为String16格式得到;
//Camera::USE_CALLING_UID是定义在Camera.h中的枚举类型,其值为//ICameraService::USE_CALLING_UID(同样为枚举类型,值为-1)。
        camera = Camera::connect(cameraId, clientName,
                Camera::USE_CALLING_UID);
    } 
... ...
    // We use a weak reference so the Camera object can be garbage collected.
    // The reference is only used as a proxy for callbacks.
    //JNICameraContext这个类是一个监听类,用于处理底层Camera回调函数传来的数据和消息
    sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
    context->incStrong((void*)android_hardware_Camera_native_setup);
    camera->setListener(context);

    // save context in opaque field
    env->SetLongField(thiz, fields.context, (jlong)context.get());
    return NO_ERROR;
}

可以看到,在判断HAL的版本之后就调用了native层Camera的connect方法:
frameworks/av/camera/Camera.cpp

sp<Camera> Camera::connect(int cameraId, const String16& clientPackageName,
        int clientUid)
{
    return CameraBaseT::connect(cameraId, clientPackageName, clientUid);
}

在return返回的是CameraBaseT调用connect方法,Camera类继承模板类CameraBase,先找到CameraBaseT的头文件:
/frameworks/av/include/camera/CameraBase.h

static sp<TCam>      connect(int cameraId,
                             const String16& clientPackageName,
                             int clientUid);

发现是强引用类型TCam调用的方法,接下来在CameraBase.cpp中查看此方法:
frameworks/av/camera/CameraBase.cpp

template <typename TCam, typename TCamTraits>
sp<TCam> CameraBase<TCam, TCamTraits>::connect(int cameraId,
                                               const String16& clientPackageName,
                                               int clientUid)
{
    ALOGV("%s: connect", __FUNCTION__);
    sp<TCam> c = new TCam(cameraId);
    sp<TCamCallbacks> cl = c;
    status_t status = NO_ERROR;
    // 获取到cameraservice
    const sp<ICameraService>& cs = getCameraService();

    if (cs != 0) {
        TCamConnectService fnConnectService = TCamTraits::fnConnectService;
        //强引用,调用ICameraService的connect方法
        status = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, clientUid,
                                             /*out*/ c->mCamera);
    }
    if (status == OK && c->mCamera != 0) {
        IInterface::asBinder(c->mCamera)->linkToDeath(c);
        c->mStatus = NO_ERROR;
    } else {
        ALOGW("An error occurred while connecting to camera: %d", cameraId);
        c.clear();
    }
    return c;
}

const sp<ICameraService>& CameraBase<TCam, TCamTraits>::getCameraService()
{
    Mutex::Autolock _l(gLock);
    if (gCameraService.get() == 0) {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            binder = sm->getService(String16(kCameraServiceName));
            if (binder != 0) {
                break;
            }
            ALOGW("CameraService not published, waiting...");
            usleep(kCameraServicePollDelay);
        } while(true);
        if (gDeathNotifier == NULL) {
            gDeathNotifier = new DeathNotifier();
        }
        binder->linkToDeath(gDeathNotifier);
        //通过binder获取CameraService实例
        gCameraService = interface_cast<ICameraService>(binder);
    }
    ALOGE_IF(gCameraService == 0, "no CameraService!?");
    return gCameraService;
}

可以看出,在connect方法中获取了cameraservice的实例,然后connect()函数在BpCameraService和BnCameraService的父类ICameraService中声明为纯虚函数,在BpCameraService和CameraService中分别给出了实现,BpCameraService作为代理类,提供接口给客户端。
fnConnectService方法在Camera.cpp作为强引用,指向ICameraService::connect方法。

CameraTraits<Camera>::TCamConnectService CameraTraits<Camera>::fnConnectService =
        &ICameraService::connect;
... ...

在ICameraService.cpp的connect方法中,首先将传递过来的Camera对象cameraClient转换成IBinder类型,将调用的参数写到Parcel中,通过BpBinder的transact()函数发送消息,然后由BnCameraService去响应该连接,最后就是等待服务端返回,如果成功则生成一个BpCamera实例。
frameworks/av/camera/ICameraService.cpp

// connect to camera service (android.hardware.Camera)
virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId,
                         const String16 &clientPackageName, int clientUid,
                         /*out*/
                         sp<ICamera>& device)
{
    Parcel data, reply;
    data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
    data.writeStrongBinder(IInterface::asBinder(cameraClient));
    data.writeInt32(cameraId);
    data.writeString16(clientPackageName);
    data.writeInt32(clientUid);

    status_t status;
    status = remote()->transact(BnCameraService::CONNECT, data, &reply);
//BpBinder的transact()函数向IPCThreadState实例发送消息,通知其有消息要发送给binder driver

    if (status != OK) return status;

    if (readExceptionCode(reply)) return -EPROTO;
    status = reply.readInt32();
    if (reply.readInt32() != 0) {
        // client端读出server返回的binder
        device = interface_cast<ICamera>(reply.readStrongBinder());
    }
    return status;
}

发送的消息在BnCameraService接收,其中onTransact()函数负责解包收到的Parcel并执行client端的请求的方法。服务端收到CONNECT命令之后,
(1) 使用Camera的Binder对象生成Camera客户代理BpCameraClient实例;
(2) 将生成的BpCameraClient对象作为参数传递到CameraService(/frameworks/av/services/camera/libcameraservice/CameraService.cpp)的connect()函数中,该函数会返回一个BpCamera实例;
(3) 将在(2)中返回的实例对象以IBinder的形式打包到Parcel中返回。

status_t BnCameraService::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
        ... ...
        case CONNECT: {
            CHECK_INTERFACE(ICameraService, data, reply);
            sp<ICameraClient> cameraClient =
                    interface_cast<ICameraClient>(data.readStrongBinder());
            int32_t cameraId = data.readInt32();
            const String16 clientName = data.readString16();
            int32_t clientUid = data.readInt32();
            sp<ICamera> camera;
            status_t status = connect(cameraClient, cameraId,
                    clientName, clientUid, /*out*/camera);
            reply->writeNoException();
            reply->writeInt32(status);
            if (camera != NULL) {
                reply->writeInt32(1);
                reply->writeStrongBinder(IInterface::asBinder(camera));
            } else {
                reply->writeInt32(0);
            }
            return NO_ERROR;
        } break;
}

接下来看CameraService::connect()函数,该函数返回一个BpCamera实例。

status_t CameraService::connect(
        const sp<ICameraClient>& cameraClient,
        int cameraId,
        const String16& clientPackageName,
        int clientUid,
        /*out*/
        sp<ICamera>& device) {

    ATRACE_CALL();
    status_t ret = NO_ERROR;
    String8 id = String8::format("%d", cameraId);
    sp<Client> client = nullptr;
    ret = connectHelper<ICameraClient,Client>(cameraClient, id,
            CAMERA_HAL_API_VERSION_UNSPECIFIED,
            clientPackageName, clientUid, API_1, false, false, /*out*/client);

    if(ret != NO_ERROR) {
        logRejected(id, getCallingPid(), String8(clientPackageName),
                String8::format("%s (%d)", strerror(-ret), ret));
        return ret;
    }
    device = client;
    return NO_ERROR;
}

可以看到又调用了connectHelper去返回客户端实例。在CameraService.h中定义了此方法:

template<class CALLBACK, class CLIENT>
status_t CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
        int halVersion, const String16& clientPackageName, int clientUid,
        apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
        /*out*/sp<CLIENT>& device) {
    status_t ret = NO_ERROR;
    String8 clientName8(clientPackageName);
    int clientPid = getCallingPid();

    sp<CLIENT> client = nullptr;
    {
        // Acquire mServiceLock and prevent other clients from connecting
        std::unique_ptr<AutoConditionLock> lock =
                AutoConditionLock::waitAndAcquire(mServiceLockWrapper, DEFAULT_CONNECT_TIMEOUT_NS);

        if (lock == nullptr) {
            ALOGE("CameraService::connect X (PID %d) rejected (too many other clients connecting)."
                    , clientPid);
            return -EBUSY;
        }

        // Enforce client permissions and do basic sanity checks
        if((ret = validateConnectLocked(cameraId, /*inout*/clientUid)) != NO_ERROR) {
            return ret;
        }

        // Check the shim parameters after acquiring lock, if they have already been updated and
        // we were doing a shim update, return immediately
        if (shimUpdateOnly) {
            auto cameraState = getCameraState(cameraId);
            if (cameraState != nullptr) {
                if (!cameraState->getShimParams().isEmpty()) return NO_ERROR;
            }
        }

        sp<BasicClient> clientTmp = nullptr;
        std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>> partial;
        if ((ret = handleEvictionsLocked(cameraId, clientPid, effectiveApiLevel,
                IInterface::asBinder(cameraCb), clientName8, /*out*/&clientTmp,
                /*out*/&partial)) != NO_ERROR) {
            return ret;
        }

        if (clientTmp.get() != nullptr) {
            // Handle special case for API1 MediaRecorder where the existing client is returned
            device = static_cast<CLIENT*>(clientTmp.get());
            return NO_ERROR;
        }

        // give flashlight a chance to close devices if necessary.
        mFlashlight->prepareDeviceOpen(cameraId);

        // TODO: Update getDeviceVersion + HAL interface to use strings for Camera IDs
        int id = cameraIdToInt(cameraId);
        if (id == -1) {
            ALOGE("%s: Invalid camera ID %s, cannot get device version from HAL.", __FUNCTION__,
                    cameraId.string());
            return BAD_VALUE;
        }

        int facing = -1;
        int deviceVersion = getDeviceVersion(id, /*out*/&facing);
        sp<BasicClient> tmp = nullptr;
        if((ret = makeClient(this, cameraCb, clientPackageName, cameraId, facing, clientPid,
                clientUid, getpid(), legacyMode, halVersion, deviceVersion, effectiveApiLevel,
                /*out*/&tmp)) != NO_ERROR) {
            return ret;
        }
        client = static_cast<CLIENT*>(tmp.get());

        LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state",
                __FUNCTION__);

        if ((ret = client->initialize(mModule)) != OK) {
            ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__);
            return ret;
        }

        // Update shim paremeters for legacy clients
        if (effectiveApiLevel == API_1) {
            // Assume we have always received a Client subclass for API1
            sp<Client> shimClient = reinterpret_cast<Client*>(client.get());
            String8 rawParams = shimClient->getParameters();
            CameraParameters params(rawParams);

            auto cameraState = getCameraState(cameraId);
            if (cameraState != nullptr) {
                cameraState->setShimParams(params);
            } else {
                ALOGE("%s: Cannot update shim parameters for camera %s, no such device exists.",
                        __FUNCTION__, cameraId.string());
            }
        }

        if (shimUpdateOnly) {
            // If only updating legacy shim parameters, immediately disconnect client
            mServiceLock.unlock();
            client->disconnect();
            mServiceLock.lock();
        } else {
            // Otherwise, add client to active clients list
            finishConnectLocked(client, partial);
        }
    } // lock is destroyed, allow further connect calls

    // Important: release the mutex here so the client can call back into the service from its
    // destructor (can be at the end of the call)
    device = client;
    return NO_ERROR;
}

makeClient方法在CameraService.cpp中实现:

status_t CameraService::makeClient(const sp<CameraService>& cameraService,
        const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId,
        int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode,
        int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
        /*out*/sp<BasicClient>* client) {

    // TODO: Update CameraClients + HAL interface to use strings for Camera IDs
    int id = cameraIdToInt(cameraId);
    if (id == -1) {
        ALOGE("%s: Invalid camera ID %s, cannot convert to integer.", __FUNCTION__,
                cameraId.string());
        return BAD_VALUE;
    }

    if (halVersion < 0 || halVersion == deviceVersion) {
        // Default path: HAL version is unspecified by caller, create CameraClient
        // based on device version reported by the HAL.
        switch(deviceVersion) {
          case CAMERA_DEVICE_API_VERSION_1_0:
            if (effectiveApiLevel == API_1) {  // Camera1 API route
                sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
                *client = new CameraClient(cameraService, tmp, packageName, id, facing,
                        clientPid, clientUid, getpid(), legacyMode);
            } else { // Camera2 API route
                ALOGW("Camera using old HAL version: %d", deviceVersion);
                return -EOPNOTSUPP;
            }
            break;
          case CAMERA_DEVICE_API_VERSION_2_0:
          case CAMERA_DEVICE_API_VERSION_2_1:
          case CAMERA_DEVICE_API_VERSION_3_0:
          case CAMERA_DEVICE_API_VERSION_3_1:
          case CAMERA_DEVICE_API_VERSION_3_2:
          case CAMERA_DEVICE_API_VERSION_3_3:
            if (effectiveApiLevel == API_1) { // Camera1 API route
                sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
                *client = new Camera2Client(cameraService, tmp, packageName, id, facing,
                        clientPid, clientUid, servicePid, legacyMode);
            } else { // Camera2 API route
                sp<ICameraDeviceCallbacks> tmp =
                        static_cast<ICameraDeviceCallbacks*>(cameraCb.get());
                *client = new CameraDeviceClient(cameraService, tmp, packageName, id,
                        facing, clientPid, clientUid, servicePid);
            }
            break;
          default:
            // Should not be reachable
            ALOGE("Unknown camera device HAL version: %d", deviceVersion);
            return INVALID_OPERATION;
        }
    } else {
        // A particular HAL version is requested by caller. Create CameraClient
        // based on the requested HAL version.
        if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 &&
            halVersion == CAMERA_DEVICE_API_VERSION_1_0) {
            // Only support higher HAL version device opened as HAL1.0 device.
            sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
            *client = new CameraClient(cameraService, tmp, packageName, id, facing,
                    clientPid, clientUid, servicePid, legacyMode);
        } else {
            // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet.
            ALOGE("Invalid camera HAL version %x: HAL %x device can only be"
                    " opened as HAL %x device", halVersion, deviceVersion,
                    CAMERA_DEVICE_API_VERSION_1_0);
            return INVALID_OPERATION;
        }
    }
    return NO_ERROR;
}

由此,初始化了服务端的CameraClient实例,之后在客户端的操作都是调用了此实例。这样每一次客户端调用preview/takepicture,就直接调用的是CameraClient中的相关函数。

返回到CameraService::connect()函数中,处理完成之后的client实例赋值给了device,而这个device,就是在客户端CameraBase.cpp中c->mCamera,
status = (cs.get()->fnConnectService)(cl, cameraId, clientPackageName, clientUid,
/
out*/ c->mCamera);

这样就真正建立了客户端和服务端的关系。如Camera.cpp中的startPreview

status_t Camera::startPreview()
{
    ALOGV("startPreview");
    sp <ICamera> c = mCamera;
    if (c == 0) return NO_INIT;
    return c->startPreview();
}

其实直接就会调用到CameraService中CameraClient.cpp中

status_t CameraClient::startPreview() {
    LOG1("startPreview (pid %d)", getCallingPid());
    return startCameraMode(CAMERA_PREVIEW_MODE);
}

至此,对于camera的客户端如何连接到server端的已经介绍完毕。

2.4 server端的初始化

camera客户端与服务端的调用关系,就是camera.cpp中的函数,对应到CameraClient中的函数。下面我们再看看CameraClient的初始化过程。

在客户端连接过程中,connectHelper方法里,调用了方法:

if ((ret = client->initialize(mModule)) != OK) {
    ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__);
    return ret;
}

此方法调用了CameraClient的initialize函数:

status_t CameraClient::initialize(CameraModule *module) {
    int callingPid = getCallingPid();
    status_t res;

    // Verify ops permissions
    res = startCameraOps();
    if (res != OK) {
        return res;
    }

    char camera_device_name[10];
    snprintf(camera_device_name, sizeof(camera_device_name), "%d", mCameraId);
    //先实例化camera HAL的CameraHardwareInterface接口
    mHardware = new CameraHardwareInterface(camera_device_name);
    //调用initialize进入HAL层,打开camera底层驱动
    res = mHardware->initialize(module);
    if (res != OK) {
        ALOGE("%s: Camera %d: unable to initialize device: %s (%d)",
                __FUNCTION__, mCameraId, strerror(-res), res);
        mHardware.clear();
        return res;
    }

    mHardware->setCallbacks(notifyCallback,
            dataCallback,
            dataCallbackTimestamp,
            (void *)(uintptr_t)mCameraId);

    // Enable zoom, error, focus, and metadata messages by default
    enableMsgType(CAMERA_MSG_ERROR | CAMERA_MSG_ZOOM | CAMERA_MSG_FOCUS |
                  CAMERA_MSG_PREVIEW_METADATA | CAMERA_MSG_FOCUS_MOVE);
    return OK;
}

在上面的方法中,mHardware->initialize(module)中的module是一个结构体camera_module_t,接下来分析它的初始化过程。

在客户端发起连接的时候,在CameraBase中的connect方法中,调用了getCameraService方法,

sp<TCam> CameraBase<TCam, TCamTraits>::connect(int cameraId,
                                               const String16& clientPackageName,
                                               int clientUid)
{
    ALOGV("%s: connect", __FUNCTION__);
    sp<TCam> c = new TCam(cameraId);
    sp<TCamCallbacks> cl = c;
    status_t status = NO_ERROR;
    const sp<ICameraService>& cs = getCameraService();
    ... ...

这个时候初始化了一个CameraService实例,且用Sp包装,这个时候sp将新增计数,相应的CameraService实例里面onFirstRef()函数完成调用。该函数在强引用sp新增引用计数时调用。就是当有sp包装的类初始化的时候调用。

void CameraService::onFirstRef()
{
    BnCameraService::onFirstRef();
    // Update battery life tracking if service is restarting
    BatteryNotifier& notifier(BatteryNotifier::getInstance());
    notifier.noteResetCamera();
    notifier.noteResetFlashlight();
    camera_module_t *rawModule;
    int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID,
            (const hw_module_t **)&rawModule);
    mModule = new CameraModule(rawModule);
    ... ...

CAMERA_HARDWARE_MODULE_ID 获取Camera Hal层的代理stub,并赋值给mModule,后面就可通过操作mModule完成对Camera模块的控制。

至此,服务端的初始化也分析完毕。整个流程的总结如下:
->先是系统注册CameraService的服务
->AP层调用Camera.open()
->Camera.java调用JNI native_setup()
->JNI层调用 android_hardware_Camera_native_setup
-> HAL 客户端(Camera.cpp)调用connect与服务端(CameraService.cpp)连接,并得到CameraService中的CameraClient的一个实例
->服务端CameraClient的初始化,实例化Camera Hal接口 CameraHardwareInterface
->CameraHardwareInterface 打开Camera驱动,初始化完毕
对应的代码层次:
App--->framework-java--->jni--->framework-c++(Camera)--->binder---> framework-c++(CameraService)-->framework-c++(CameraService::Client)---> framework-c++(CameraClient) --->(CameraHardwareInterface) ---> HAL

2.5 HAL层openCamera

首先在CameraService中的onFirstRef()方法里实例化了HAL层的对象,然后调用了相关的方法,下面就从HAL层去分析是如何启动camera的。

mModule = new CameraModule(rawModule);
mNumberOfCameras = mModule->getNumberOfCameras();

可以看出,先是调用了HAL层的getNumberOfCameras()方法,返回支持的摄像头数量。

在HAL层,其调用到了QCamera2Factory.cpp中。在构造函数时调用了get_num_of_cameras()方法,然后getNumberOfCameras()方法返回这个成员变量。

QCamera2Factory::QCamera2Factory()
{
    mNumOfCameras = get_num_of_cameras();
}
int QCamera2Factory::getNumberOfCameras()
{
    return mNumOfCameras;
}

get_num_of_cameras()方法在mm_camera_interface.c中实现,在while循环中不断访问驱动,然后不满足条件之后跳出循环,返回camera数量。

之后通过HAL开启camera,在HAL层中,具体的实现是在QCamera2HWI.cpp中:

int QCamera2HardwareInterface::openCamera(struct hw_device_t **hw_device)
{
    ATRACE_CALL();
    int rc = NO_ERROR;

    if (!check_cam_access(mCameraId)) {
        ALOGE("%s: multiple simultaneous camera instance not supported", __func__);
        return -EUSERS;
    }

    if (mCameraOpened) {
        *hw_device = NULL;
        return PERMISSION_DENIED;
    }
    CDBG_HIGH("[KPI Perf] %s: E PROFILE_OPEN_CAMERA camera id %d", __func__,mCameraId);
    rc = openCamera();
    if (rc == NO_ERROR){
        *hw_device = &mCameraDevice.common;
        if (m_thermalAdapter.init(this) != 0) {
          ALOGE("Init thermal adapter failed");
        }
    }
    else
        *hw_device = NULL;
    return rc;
}

继续调用openCamera()方法,

int QCamera2HardwareInterface::openCamera()
{
    int32_t l_curr_width = 0;
    int32_t l_curr_height = 0;
    m_max_pic_width = 0;
    m_max_pic_height = 0;
    char value[PROPERTY_VALUE_MAX];
    int enable_4k2k;
    size_t i;

    if (mCameraHandle) {
        ALOGE("Failure: Camera already opened");
        return ALREADY_EXISTS;
    }
    mCameraHandle = camera_open((uint8_t)mCameraId);
    ... ...
    mCameraHandle->ops->register_event_notify(mCameraHandle->camera_handle,
                                          camEvtHandle,
                                          (void *) this);
    ... ...
    }

之后调用camera_open方法,此方法在mm_camera_interface.c中实现:

mm_camera_vtbl_t * camera_open(uint8_t camera_idx)
{
    int32_t rc = 0;
    mm_camera_obj_t* cam_obj = NULL;

    CDBG("%s: E camera_idx = %d\n", __func__, camera_idx);
    if (camera_idx >= g_cam_ctrl.num_cam) {
        CDBG_ERROR("%s: Invalid camera_idx (%d)", __func__, camera_idx);
        return NULL;
    }

    pthread_mutex_lock(&g_intf_lock);
    /* opened already */
    if(NULL != g_cam_ctrl.cam_obj[camera_idx]) {
        /* Add reference */
        g_cam_ctrl.cam_obj[camera_idx]->ref_count++;
        pthread_mutex_unlock(&g_intf_lock);
        CDBG("%s:  opened alreadyn", __func__);
        return &g_cam_ctrl.cam_obj[camera_idx]->vtbl;
    }

    cam_obj = (mm_camera_obj_t *)malloc(sizeof(mm_camera_obj_t));
    if(NULL == cam_obj) {
        pthread_mutex_unlock(&g_intf_lock);
        CDBG("%s:  no mem", __func__);
        return NULL;
    }

    /* initialize camera obj */
    memset(cam_obj, 0, sizeof(mm_camera_obj_t));
    cam_obj->ref_count++;
    cam_obj->my_hdl = mm_camera_util_generate_handler(camera_idx);
    cam_obj->vtbl.camera_handle = cam_obj->my_hdl; /* set handler */
    cam_obj->vtbl.ops = &mm_camera_ops;
    pthread_mutex_init(&cam_obj->cam_lock, NULL);

    rc = mm_camera_open(cam_obj);
    if(rc != 0) {
        CDBG_ERROR("%s: mm_camera_open err = %d", __func__, rc);
        pthread_mutex_destroy(&cam_obj->cam_lock);
        g_cam_ctrl.cam_obj[camera_idx] = NULL;
        free(cam_obj);
        cam_obj = NULL;
        pthread_mutex_unlock(&g_intf_lock);
        return NULL;
    }else{
        CDBG("%s: Open succeded\n", __func__);
        g_cam_ctrl.cam_obj[camera_idx] = cam_obj;
        pthread_mutex_unlock(&g_intf_lock);
        return &cam_obj->vtbl;
    }
}

之后继续调用mm_camera_open方法,此方法在mm_camera.c中实现:

int32_t mm_camera_open(mm_camera_obj_t *my_obj)
{
    char dev_name[MM_CAMERA_DEV_NAME_LEN];
    int32_t rc = 0;
    int8_t n_try=MM_CAMERA_DEV_OPEN_TRIES;
    uint8_t sleep_msec=MM_CAMERA_DEV_OPEN_RETRY_SLEEP;
    int cam_idx = 0;
    char t_devname[MM_CAMERA_DEV_NAME_LEN];
    const char *temp_dev_name = mm_camera_util_get_dev_name(my_obj->my_hdl);

    CDBG("%s:  begin\n", __func__);

    if (temp_dev_name == NULL) {
        CDBG_ERROR("%s: dev name is NULL",__func__);
        rc= -1;
        goto on_error;
    }
    strlcpy(t_devname, temp_dev_name, sizeof(t_devname));
    snprintf(dev_name, sizeof(dev_name), "/dev/%s",t_devname );
    sscanf(dev_name, "/dev/video%d", &cam_idx);
    CDBG_ERROR("%s: dev name = %s, cam_idx = %d", __func__, dev_name, cam_idx);

    do{
        n_try--;
        my_obj->ctrl_fd = open(dev_name, O_RDWR | O_NONBLOCK);
        CDBG("%s:  ctrl_fd = %d, errno == %d", __func__, my_obj->ctrl_fd, errno);
        if((my_obj->ctrl_fd > 0) || (errno != EIO) || (n_try <= 0 )) {
            CDBG_ERROR("%s:  opened, break out while loop", __func__);
            break;
        }
        CDBG("%s:failed with I/O error retrying after %d milli-seconds",
             __func__, sleep_msec);
        usleep(sleep_msec * 1000U);
    }while (n_try > 0);

    if (my_obj->ctrl_fd <= 0) {
        CDBG_ERROR("%s: cannot open control fd of '%s' (%s)\n",
                 __func__, dev_name, strerror(errno));
        rc = -1;
        goto on_error;
    }

    /* open domain socket*/
    n_try = MM_CAMERA_DEV_OPEN_TRIES;
    do {
        n_try--;
        my_obj->ds_fd = mm_camera_socket_create(cam_idx, MM_CAMERA_SOCK_TYPE_UDP);
        CDBG("%s:  ds_fd = %d, errno = %d", __func__, my_obj->ds_fd, errno);
        if((my_obj->ds_fd > 0) || (n_try <= 0 )) {
            CDBG("%s:  opened, break out while loop", __func__);
            break;
        }
        CDBG("%s:failed with I/O error retrying after %d milli-seconds",
             __func__, sleep_msec);
        usleep(sleep_msec * 1000U);
    } while (n_try > 0);

    if (my_obj->ds_fd <= 0) {
        CDBG_ERROR("%s: cannot open domain socket fd of '%s'(%s)\n",
                 __func__, dev_name, strerror(errno));
        rc = -1;
        goto on_error;
    }
    pthread_mutex_init(&my_obj->msg_lock, NULL);

    pthread_mutex_init(&my_obj->cb_lock, NULL);
    pthread_mutex_init(&my_obj->evt_lock, NULL);
    pthread_cond_init(&my_obj->evt_cond, NULL);

    CDBG("%s : Launch evt Thread in Cam Open",__func__);
    snprintf(my_obj->evt_thread.threadName, THREAD_NAME_SIZE, "CAM_Dispatch");
    mm_camera_cmd_thread_launch(&my_obj->evt_thread,
                                mm_camera_dispatch_app_event,
                                (void *)my_obj);

    /* launch event poll thread
     * we will add evt fd into event poll thread upon user first register for evt */
    CDBG("%s : Launch evt Poll Thread in Cam Open", __func__);
    snprintf(my_obj->evt_thread.threadName, THREAD_NAME_SIZE, "CAM_Poll");
    mm_camera_poll_thread_launch(&my_obj->evt_poll_thread,
                                 MM_CAMERA_POLL_TYPE_EVT);
    mm_camera_evt_sub(my_obj, TRUE);

    CDBG("%s:  end (rc = %d)\n", __func__, rc);
    /* we do not need to unlock cam_lock here before return
     * because for open, it's done within intf_lock */
    return rc;

on_error:
    if (my_obj->ctrl_fd > 0) {
        close(my_obj->ctrl_fd);
        my_obj->ctrl_fd = 0;
    }
    if (my_obj->ds_fd > 0) {
        mm_camera_socket_close(my_obj->ds_fd);
       my_obj->ds_fd = 0;
    }

    /* we do not need to unlock cam_lock here before return
     * because for open, it's done within intf_lock */
    return rc;
}

可以看出,在此方法中,打开了视频节点,创建了Socket,Launch相关的线程。

接着,回到QCamera2HWI.cpp中的openCamera()方法中,ops->register_event_notify去调用了mm_camera_interface.c中的方法:

static int32_t mm_camera_intf_register_event_notify(uint32_t camera_handle,
                                                    mm_camera_event_notify_t evt_cb,
                                                    void * user_data)
{
    int32_t rc = -1;
    mm_camera_obj_t * my_obj = NULL;

    CDBG("%s :E ", __func__);
    pthread_mutex_lock(&g_intf_lock);
    //获取camera对象
    my_obj = mm_camera_util_get_camera_by_handler(camera_handle);

    if(my_obj) {
        pthread_mutex_lock(&my_obj->cam_lock);
        pthread_mutex_unlock(&g_intf_lock);
        //真正去注册
        rc = mm_camera_register_event_notify(my_obj, evt_cb, user_data);
    } else {
        pthread_mutex_unlock(&g_intf_lock);
    }
    CDBG("%s :E rc = %d", __func__, rc);
    return rc;
}

可以看出,此方法是注册事件通知的,然后真正实现注册的位置是mm_camera.c中的mm_camera_register_event_notify方法:

int32_t mm_camera_register_event_notify(mm_camera_obj_t *my_obj,
                                        mm_camera_event_notify_t evt_cb,
                                        void * user_data)
{
    int rc = -1;
    rc = mm_camera_register_event_notify_internal(my_obj,
                                                  evt_cb,
                                                  user_data);
    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;
}

接下来,调用了mm_camera_register_event_notify_internal方法。

int32_t mm_camera_register_event_notify_internal(mm_camera_obj_t *my_obj,
                                                 mm_camera_event_notify_t evt_cb,
                                                 void * user_data)
{
    int i;
    int rc = -1;
    mm_camera_evt_obj_t *evt_array = NULL;

    pthread_mutex_lock(&my_obj->cb_lock);
    evt_array = &my_obj->evt;
    if(evt_cb) {
        /* this is reg case */
        for(i = 0; i < MM_CAMERA_EVT_ENTRY_MAX; i++) {
            if(evt_array->evt[i].user_data == NULL) {
                evt_array->evt[i].evt_cb = evt_cb;
                evt_array->evt[i].user_data = user_data;
                evt_array->reg_count++;
                rc = 0;
                break;
            }
        }
    } else {
        /* this is unreg case */
        for(i = 0; i < MM_CAMERA_EVT_ENTRY_MAX; i++) {
            if(evt_array->evt[i].user_data == user_data) {
                evt_array->evt[i].evt_cb = NULL;
                evt_array->evt[i].user_data = NULL;
                evt_array->reg_count--;
                rc = 0;
                break;
            }
        }
    }

    pthread_mutex_unlock(&my_obj->cb_lock);
    return rc;
}

此处将事件的回调注册完成。接着回到QCamera2HWI.cpp里,继续初始化、配置相关参数。
至此,打开camera的过程已经分析完成。

2.6 流程图

底层Camera初始化流程图
上一篇下一篇

猜你喜欢

热点阅读