1. 程式人生 > >Android camera fw學習(二)-open camera操作過程&準備工作分析。

Android camera fw學習(二)-open camera操作過程&準備工作分析。

備註:本文基於Android5.1、Camera hal3學習,預設情況下參考程式碼都是5.1的,如有其它版本的原始碼,我會標出來的。博文是看程式碼分析和一些工作經驗的總結,如有疑問,大家可以相互討論,共同進步。

  當前博文主要是為後續的StartPreview,takepicture等流程分析做鋪墊的,在open camera->start preview過程中,其實做了很多事情。這裡不去敘述上層app的事情,app五花八門不好統一介紹(等camera的博文整理完了,會結合一個案例分析Camera APP),這裡只介紹一些通用的程式碼。這樣後面看到其它版本的程式碼也不會那麼陌生。好了,開始學習!
  在進行程式碼流程之前我們現在來看看google給的一些猛料(camera.java註釋),好讓我們心裡準備一下。在進行閱讀原始碼之前,很有必要了解一下應用會做那些準備工作,以及正常的工作流程,google給的就是最權威文件,不好好看它的,那看誰的,話不多說,下面要翻譯了一部分,英語過了四級,翻譯不好別打我。

  The Camera class is used to set image capture settings, start/stop preview,
snap pictures, and retrieve frames for encoding for video. This class is a
client for the Camera service, which manages the actual camera hardware.
  To access the device camera, you must declare the
{@link android.Manifest.permission#CAMERA} permission in your Android
Manifest. Also be sure to include the uses-feature
manifest element to declare camera features used by your application.
For example, if you use the camera and auto-focus feature, your Manifest
should include the following:

**uses-permission android:name=”android.permission.CAMERA”
uses-feature android:name=”android.hardware.camera”
uses-feature android:name=”android.hardware.camera.autofocus”**

To take pictures with this class, use the following steps:
(為了用這個類拍照的話,請按著下面的步驟來)

  • 1.Obtain an instance of Camera from {@link #open(int)}.(使用open獲取一個camera.java物件)
  • 2.Get existing (default) settings with {@link #getParameters()}.(獲取預設的引數,這個不同平臺都是能獲取預設引數,例如預設支援的解析度大小和幀率等)
  • 3.If necessary, modify the returned {@link Camera.Parameters} object and call{@link #setParameters(Camera.Parameters)}.(如果需要的話,修改Parameters物件,並將引數傳送給hal)
  • 4.If desired, call {@link #setDisplayOrientation(int)}.(如果需要的話,設定螢幕顯示方向)
  • 5.Important: Pass a fully initialized {@link SurfaceHolder} to
    {@link #setPreviewDisplay(SurfaceHolder)}. Without a surface, the camera
    will be unable to start the preview.(這個是非常重要的,意思就是我們需要傳遞一個完全初始化的surfaceHolder物件給Camera.java,這樣Camera native才能拿到預覽buffer)
  • 6.Important: Call {@link #startPreview()} to start updating the
    preview surface. Preview must be started before you can take a picture.(開始預覽,拍照之前必須startPreview)
  • 7.When you want, call {@link #takePicture(Camera.ShutterCallback,
    Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)} to
    capture a photo. Wait for the callbacks to provide the actual image data.(當你呼叫了takePicture獲取一張圖片時,你必須等到圖片資料傳過來)
  • 8.After taking a picture, preview display will have stopped. To take more
    photos, call {@link #startPreview()} again first.(按下拍照後,preview將停下來,如果想拍更多圖片,必須重新startPreview,不過如果在ZSL模式下的話,是不需要停preview)
  • 9.Call {@link #stopPreview()} to stop updating the preview surface.(停止更新預覽介面)
  • 10.Important: Call {@link #release()} to release the camera for
    use by other applications. Applications should release the camera
    immediately in {@link android.app.Activity#onPause()} (and re-{@link #open()}
    it in {@link android.app.Activity#onResume()}).

To quickly switch to video recording mode, use these steps:
(為了快速切換錄影模式,使用下面模式)

  • 1.Obtain and initialize a Camera and start preview as described above.
  • 2.Call {@link #unlock()} to allow the media process to access the camera.
  • 3.Pass the camera to {@link android.media.MediaRecorder#setCamera(Camera)}.
    See {@link android.media.MediaRecorder} information about video recording.
  • 4.When finished recording, call {@link #reconnect()} to re-acquire
    and re-lock the camera.
  • 5.If desired, restart preview and take more photos or videos.
  • 6.Call {@link #stopPreview()} and {@link #release()} as described above.

This class is not thread-safe, and is meant for use from one event thread.
**Most long-running operations (preview, focus, photo capture, etc) happen
asynchronously and invoke callbacks as necessary. Callbacks will be invoked
on the event thread {@link #open(int)} was called from**. This class’s methods
must never be called from multiple threads at once.

Caution:Different Android-powered devices
may have different hardware specifications, such as megapixel ratings and
auto-focus capabilities. In order for your application to be compatible with
more devices, you should not make assumptions about the device camera
specifications.

有了上面的權威文件,就要按著它的來走,才不會錯。

一、java fw如何響應open camera

1.camera fw open

  java fw層open camera的操作都是在camera.java中實現的。如下2個open函式

//frameworks/base/core/java/android/hardware/camera.java
//第一開啟制定的cameraId的camera裝置
    public static Camera open(int cameraId) {
        return new Camera(cameraId);
    }

    /**
     * Creates a new Camera object to access the first back-facing camera on the
     * device. If the device does not have a back-facing camera, this returns
     * null.
     * @see #open(int)
     */
    public static Camera open() {
        int numberOfCameras = getNumberOfCameras();
        CameraInfo cameraInfo = new CameraInfo();
        for (int i = 0; i < numberOfCameras; i++) {
            getCameraInfo(i, cameraInfo);
            if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
                return new Camera(i);
            }
        }
        return null;
    }

上面2個open函式,第一個帶引數是開啟指定CameraID,並創建出java fw層的物件。第二個無參open方法預設開啟後攝,如下根據程式碼看下面巨集的解釋.此外getNumberOfCameras()獲取相機的個數,這個放到native 層去分析。

/**
 * The facing of the camera is opposite to that of the screen.
*/
 public static final int CAMERA_FACING_BACK = 0;
/**
 * The facing of the camera is the same as that of the screen.
 */
 public static final int CAMERA_FACING_FRONT = 1;

2.Java Camera建構函式

    /** used by Camera#open, Camera#open(int) */
    Camera(int cameraId) {
        int err = cameraInitNormal(cameraId);
        if (checkInitErrors(err)) {
            switch(err) {
                case EACCESS:
                    throw new RuntimeException("Fail to connect to camera service");
                case ENODEV:
                    throw new RuntimeException("Camera initialization failed");
                default:
                    // Should never hit this.
                    throw new RuntimeException("Unknown camera error");
            }
        }
    }

  Camera.java中有3個Camera建構函式,他們基本差不多,這裡我們只介紹上面這種方式(後面列舉的例子就是調的這個因為Camera.open(mCameraId);)不過最後都是呼叫了cameraInitNormal()方法。上面只是簡單直接呼叫cameraInitNormal()加上一些其他的引數檢查,異常丟擲的處理過程。

3.cameraInitNormal()

private int cameraInitNormal(int cameraId) {
        return cameraInitVersion(cameraId, CAMERA_HAL_API_VERSION_NORMAL_CONNECT);
}
private int cameraInitVersion(int cameraId, int halVersion) {
        mShutterCallback = null;
        mRawImageCallback = null;
        mJpegCallback = null;
        mPreviewCallback = null;
        mPostviewCallback = null;
        mUsingPreviewAllocation = false;
        mZoomListener = null;

        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.currentPackageName();

        return native_setup(new WeakReference<Camera>(this), cameraId, halVersion, packageName);
    }

  函式中初始化Camera的一些Callback,這些Callback都是camera應用中相關類來實現的。此外這裡出現了一個Looper.myLooper(),這個函式中會建立一個執行緒,來處理時間訊息。緊接著下面EventHandler物件,這個就是訊息處理函式,在函式中回撥我們之前註冊過來的callback函式。所以我們腦海中開啟可以有一個這樣的整體印象,如下圖所示。最後查詢查詢包名,進而呼叫native方法native_setup()做後續的初始化,這個方法我們放到下面分析。

二、jni&native fw open camera做了什麼

1.getNumberOfCameras()

原始碼路徑:/framework/base/core/jni/android_hardware_Camera.cpp

static jint android_hardware_Camera_getNumberOfCameras(JNIEnv *env, jobject thiz)
{
    return Camera::getNumberOfCameras();
}
//原始碼路徑:/frameworks/av/camera/CameraBase.cpp
int CameraBase<TCam, TCamTraits>::getNumberOfCameras() {
    const sp<ICameraService> cs = getCameraService();

    if (!cs.get()) {
        // as required by the public Java APIs
        return 0;
    }
    return cs->getNumberOfCameras();
}

  我們在呼叫java層無參open()函式時,會建立java層的camera物件,期間就會調到jni中的getNumberOfCameras(),該方法使用cameraservice代理物件,直接獲取硬體camera個數。詳情請看下面cameraService中如何處理的。
  cameraService中直接就返回了camera的數量,蒙圈了,其實在手機開機過程中,mediaServer已經探測過支援多少硬體Camera了.

int32_t CameraService::getNumberOfCameras() {
    return mNumberOfCameras;
}

當手機開機時mediaServer註冊CameraService時,在onFirstRef()已經探測過了。如果理解智慧指標RefBase.cpp類如何使用,想必這裡就好理解了。CameraService類繼承了RefBase類,那麼在建立CameraService物件時,onFirstRef()函式就會被呼叫。

void CameraService::onFirstRef()
{
    LOG1("CameraService::onFirstRef");
    BnCameraService::onFirstRef();
    //這裡載入Camera的硬體抽象層動態庫,系統Camera系統出現問題,最好先看看下面  //的錯誤log有沒有打印出來。
    if (hw_get_module(CAMERA_HARDWARE_MODULE_ID,
                (const hw_module_t **)&mModule) < 0) {
        ALOGE("Could not load camera HAL module");
        mNumberOfCameras = 0;
    }
    else {
        ALOGI("Loaded \"%s\" camera module", mModule->common.name);
//這裡呼叫對應平臺HAL的標準介面,獲取Camera數量,鄙人接觸過的
//展訊平臺HAL,會去逐個遍歷所有Camera,然後將sensor的註冊資訊,寫入一個配置檔案,開機後就不需要重新掃描了,緊接著就返回Camera的數量。
        mNumberOfCameras = mModule->get_number_of_cameras();
        if (mNumberOfCameras > MAX_CAMERAS) {
            ALOGE("Number of cameras(%d) > MAX_CAMERAS(%d).",
                    mNumberOfCameras, MAX_CAMERAS);
            mNumberOfCameras = MAX_CAMERAS;
        }
        for (int i = 0; i < mNumberOfCameras; i++) {
            setCameraFree(i);//釋放Camera忙原子變數
        }
        //這裡CameraService繼承了camera_module_callbacks_t 所以這裡
        //註冊到Hal的callback就是就是onDeviceStatusChanged(),主要是
        if (mModule->common.module_api_version >=
                CAMERA_MODULE_API_VERSION_2_1) {
            mModule->set_callbacks(this);
        }

        VendorTagDescriptor::clearGlobalVendorTagDescriptor();

        if (mModule->common.module_api_version >= CAMERA_MODULE_API_VERSION_2_2) {
            setUpVendorTags();
        }

        CameraDeviceFactory::registerService(this);
    }
}

上面函式主要做了硬體HAL模組載入和Camera數量探測等工作,概括就下面幾條

  • 1.hw_get_module(CAMERA_HARDWARE_MODULE_ID),這個函式就是專門用來載入動態庫的,如果大家想了解一些細節的話,可以關注之前的博文android camera動態庫載入過程.
  • 2.當Hal版本大於CAMERA_MODULE_API_VERSION_2_1就將fw的onDeviceStatusChanged()callback註冊到hal,用來hal通知fw,底層Camera的狀態。下面是這個callback的官方解釋。
    Callback to the framework to indicate that the state of a specific camera
    device has changed. At module load time, the framework will assume all
    camera devices are in the CAMERA_DEVICE_STATUS_PRESENT state. The HAL
    must call this method to inform the framework of any initially
    NOT_PRESENT devices.
  • 3.最後還有工廠類管理類,用來註冊當前服務,此外該類還用來自動的根據硬體版號建立Camera2Device,或者Camera3Device.詳情請看工廠類frameworks/av/services/camera/libcameraservice/CameraDeviceFactory.h
void CameraDeviceFactory::registerService(wp<CameraService> service) {
    ALOGV("%s: Registered service %p", __FUNCTION__,
          service.promote().get());

    sService = service;
}

2.android_hardware_Camera_native_setup()

static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
    jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName)
{
    // Convert jstring to String16
    const char16_t *rawClientName = env->GetStringChars(clientPackageName, NULL);
    jsize rawClientNameLen = env->GetStringLength(clientPackageName);
    String16 clientName(rawClientName, rawClientNameLen);
    env->ReleaseStringChars(clientPackageName, rawClientName);
    //上面是從Java虛擬機器執行環境中獲取,應用名字,包名,這裡我們不必過多關係。
    sp<Camera> camera;
    if (halVersion == CAMERA_HAL_API_VERSION_NORMAL_CONNECT) {
        // Default path: hal version is don't care, do normal camera connect.
        //這裡說它不關心,其實在載入Camera hal動態模組後,就可以獲取到硬體的版本號,進而建立對應版本的Camera_Device.
        camera = Camera::connect(cameraId, clientName,
                Camera::USE_CALLING_UID);
    } else {
        jint status = Camera::connectLegacy(cameraId, halVersion, clientName,
                Camera::USE_CALLING_UID, camera);
        if (status != NO_ERROR) {
            return status;
        }
    }

    if (camera == NULL) {
        return -EACCES;
    }
//這裡做了一些判斷Camera hal狀態是否正常,以及Java fw Camera物件是否出現異常檢測,此處省略程式碼--------------

    // We use a weak reference so the Camera object can be garbage collected.
    // The reference is only used as a proxy for callbacks.
    //這裡建立了一個Camera jni的裝置上下文,我們下面看看它的建構函式。
    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,將該物件儲存到java fw camera物件中的mNativeContext變數中,便於後續其他jni獲取該物件。
    env->SetLongField(thiz, fields.context, (jlong)context.get());
    return NO_ERROR;
}
上面主要做了下面幾件事情。
  • (1).由java虛擬機器執行環境獲取客戶端名字,包名等資訊,為下面Camera::connect()做準備。
  • (2).Camera::connect()獲取native層的camera.cpp物件camera,以及操作Camera並實現ICamera.h介面的BpCamera代理物件,該物件儲存到camera->mCamera成員中,便於後續與CameraService互動操作Camera.
  • (3).建立jni層JNICameraContext Camera上下文物件,該物件保證了將jni層收到的底層訊息和資料傳送給java camera物件,這樣訊息,資料通路就明顯了,hal->jni_native->java_fw。
  • (4).設定監聽函式,將由JNICameraContext實現的與java通訊的函式,註冊到native Camera的mListen成員中。然後在實現ICameraClient.h介面時,都是呼叫的mListen物件的方法,粗線條架構應該出現了。
    一旦執行完畢android_hardware_Camera_native_setup()函式,camera執行過程中重量級的物件都建立完畢了。目前個人覺得重要物件如下表所示。
目前需要關注的物件
Java framework 1.目前需要關注的只有open()new出來的Camera物件(Camera.java),以及成員變數和一些由Camera應用註冊過來的callback
JNI,native framework 1.camera(camera.cpp) – native Camera物件
2.camera->mCamera(BpCamera) – Connect後獲取到的BpCamera代理物件
3.camera->mListen(CameraListener) - -由JNICameraContext類實現的,並通過camera->setListener()設定給native camera
4.JNICameraContext物件 –專門負責與java Camera物件通訊的物件
5.JNICameraContext->mCamera,就是上面建立的camera.cpp物件
HAL 這裡不同平臺有不同的實現,無非就是各個硬體模組的抽象物件了.針對展訊HAL將HAL抽象成各個功能模組,preview,snapshot,isp,sensor等功能模組,每一個功能模組都對應一個物件,支援多例項

3.Camera::connect()

(1).BpCamera代理物件
  camera的連線過程使用cameraService代理物件的connect,並將BncameraClient對像,以及ICamera引用傳送給CameraService,目的分別是為給client傳送訊息、事件做準備,以及為控制camera、獲取Camera資訊做準備。

    // 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(cameraClient->asBinder());//這個在寫binder物件後,kernel就會建立一個binder_node,並掛接到當前客戶端程序的binder_proc中,同時建立引用了該binder_node物件的binder_ref物件,傳送給CameraService.
        data.writeInt32(cameraId);
        data.writeString16(clientPackageName);
        data.writeInt32(clientUid);
        remote()->transact(BnCameraService::CONNECT, data, &reply);//(使用IPCThreadState)

        if (readExceptionCode(reply)) return -EPROTO;
        status_t status = reply.readInt32();
        if (reply.readInt32() != 0) {
            device = interface_cast<ICamera>(reply.readStrongBinder());//這裡就返回了BpCamera代理物件,稍後我們去CameraService 裡面看看。
        }
        return status;
    }

(2).BnCamera本地物件
本地物件的onTransact()實現在ICameraService,這裡省略其它處理過程。

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()); //如我們上面說的,這裡拿到bpCameraClient代理物件,為後面給Camera客戶端程序發訊息用。
            int32_t cameraId = data.readInt32();
            const String16 clientName = data.readString16();
            int32_t clientUid = data.readInt32();
            sp<ICamera> camera;
            status_t status = connect(cameraClient, cameraId, //這裡本地connect()實現是在CameraService中。
                    clientName, clientUid, /*out*/camera);
            reply->writeNoException();
            reply->writeInt32(status);
            if (camera != NULL) {
                reply->writeInt32(1);
                reply->writeStrongBinder(camera->asBinder());//這裡拿到的是bnCamera(ICamera),注意和上面代理物件做對比理解。
            } else {
                reply->writeInt32(0);
            }
            return NO_ERROR;
        } break;
    //......

總體上幹了3件事情

  • 1.獲取BpCameraClient代理物件,為後續和Camera客戶端程序通訊做準備。
  • 2.呼叫CameraService中的Connect,獲取BnCamera本地物件。下面會進一步介紹
  • 3.將BnCamera本地物件,匿名註冊到mediaServer程序中的binder_proc中,然後將新生成的binder_ref物件,傳送給客戶端程序,以使客戶端拿到BpCamera.

(3).CameraService::connect()

status_t CameraService::connect(
        const sp<ICameraClient>& cameraClient, //還記得是bpCameraClient吧,注意是代理物件
        int cameraId,
        const String16& clientPackageName,
        int clientUid,
        /*out*/
        sp<ICamera>& device) { //引用

    String8 clientName8(clientPackageName);
    int callingPid = getCallingPid();

//去掉log和校驗連線的操作,詳情還是去看原始碼吧
    sp<Client> client;
    {
        Mutex::Autolock lock(mServiceLock);
        sp<BasicClient> clientTmp;
        if (!canConnectUnsafe(cameraId, clientPackageName, //檢查之前是否已經連線過,如果裝置忙,直接返回
                              cameraClient->asBinder(),
                              /*out*/clientTmp)) {
            return -EBUSY;
        } else if (client.get() != NULL) { //如果之前註冊過,而且裝置空閒,可以直接拿到bnCamera(camera2Client)物件。
            device = static_cast<Client*>(clientTmp.get());
            return OK;
        }
        status = connectHelperLocked(/*out*/client,//如果之前沒連結過,這裡會生成bnCamera物件,進去看看。
                                     cameraClient,
                                     cameraId,
                                     clientPackageName,
                                     clientUid,
                                     callingPid);
        if (status != OK) {
            return status;
        }

    }
    // 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 OK;
}

上面主要做的是檢查,確保物件唯一性,如果之前已經連線過Camera,而且裝置不忙,則直接返回BnCamera物件。反之如果之前根本沒註冊過,那麼就會重新new出來一個BnCamera物件,即Camera2client(),具體請看下面原始碼。

status_t CameraService::connectHelperLocked(
        /*out*/
        sp<Client>& client,
        /*in*/
        const sp<ICameraClient>& cameraClient,
        int cameraId,
        const String16& clientPackageName,
        int clientUid,
        int callingPid,
        int halVersion,
        bool legacyMode) {

    int facing = -1;
    int deviceVersion = getDeviceVersion(cameraId, &facing);

    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:
            client = new CameraClient(this, cameraClient,
                    clientPackageName, cameraId,
                    facing, callingPid, clientUid, getpid(), legacyMode);
            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:
          //這裡我是使用的google的HAL3例子來介紹的(詳情請看google給的camera HAL3 例子),這裡deviceVersion = CAMERA_DEVICE_API_VERSION_3_0
            client = new Camera2Client(this, cameraClient, 
                    clientPackageName, cameraId,
                    facing, callingPid, clientUid, getpid(), legacyMode);
            break;
          case -1:
            ALOGE("Invalid camera id %d", cameraId);
            return BAD_VALUE;
          default:
            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.
            client = new CameraClient(this, cameraClient,
                    clientPackageName, cameraId,
                    facing, callingPid, clientUid, getpid(), 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;
        }
    }
    //連線成功後,進行後續的初始化和註冊死亡通知處理函式binderDied()
    status_t status = connectFinishUnsafe(client, client->getRemote());//
    if (status != OK) {
        // this is probably not recoverable.. maybe the client can try again
        return status;
    }

    mClient[cameraId] = client; //根據CameraID,儲存當前本地物件
    LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId,
         getpid());

    return OK;
}

上面主要做了,根據硬體裝置的版本號,建立對應的Camera本地物件,並初始化裝置,以及繫結死亡通知函式。

status_t CameraService::connectFinishUnsafe(const sp<BasicClient>& client,
                                            const sp<IBinder>& remoteCallback) {
    status_t status = client->initialize(mModule); //這裡做了進一步初始化工作。
    if (status != OK) {
        ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__);
        return status;
    }
    if (remoteCallback != NULL) {
        remoteCallback->linkToDeath(this);//連線死亡通知函式指標。
    }

    return OK;
}

上面函式結構簡單,請看下面初始化函式。


status_t Camera2Client::initialize(camera_module_t *module)
{
    ATRACE_CALL();
    ALOGV("%s: Initializing client for camera %d", __FUNCTION__, mCameraId);
    status_t res;

    res = Camera2ClientBase::initialize(module);
    if (res != OK) {
        return res;
    }

    {
        SharedParameters::Lock l(mParameters);

        res = l.mParameters.initialize(&(mDevice->info()), mDeviceVersion);
        if (res != OK) {
            ALOGE("%s: Camera %d: unable to build defaults: %s (%d)",
                    __FUNCTION__, mCameraId, strerror(-res), res);
            return NO_INIT;
        }
    }

    String8 threadName;

    mStreamingProcessor = new StreamingProcessor(this);
    threadName = String8::format("C2-%d-StreamProc",
            mCameraId);
    mStreamingProcessor->run(threadName.string());

    mFrameProcessor = new FrameProcessor(mDevice, this);
    threadName = String8::format("C2-%d-FrameProc",
            mCameraId);
    mFrameProcessor->run(threadName.string());

    mCaptureSequencer = new CaptureSequencer(this);
    threadName = String8::format("C2-%d-CaptureSeq",
            mCameraId);
    mCaptureSequencer->run(threadName.string());

    mJpegProcessor = new JpegProcessor(this, mCaptureSequencer);
    threadName = String8::format("C2-%d-JpegProc",
            mCameraId);
    mJpegProcessor->run(threadName.string());

    switch (mDeviceVersion) {
        case CAMERA_DEVICE_API_VERSION_2_0: {
            sp<ZslProcessor> zslProc =
                    new ZslProcessor(this, mCaptureSequencer);
            mZslProcessor = zslProc;
            mZslProcessorThread = zslProc;
            break;
        }
        case CAMERA_DEVICE_API_VERSION_3_0:
        case CAMERA_DEVICE_API_VERSION_3_1:
        case CAMERA_DEVICE_API_VERSION_3_2: { //這裡我們是3_0
            sp<ZslProcessor3> zslProc =
                    new ZslProcessor3(this, mCaptureSequencer);
            mZslProcessor = zslProc;
            mZslProcessorThread = zslProc;
            break;
        }
        default:
            break;
    }
    threadName = String8::format("C2-%d-ZslProc",
            mCameraId);
    mZslProcessorThread->run(threadName.string());

    mCallbackProcessor = new CallbackProcessor(this);
    threadName = String8::format("C2-%d-CallbkProc",
            mCameraId);
    mCallbackProcessor->run(threadName.string());

    if (gLogLevel >= 1) {
        SharedParameters::Lock l(mParameters);
        ALOGD("%s: Default parameters converted from camera %d:", __FUNCTION__,
              mCameraId);
        ALOGD("%s", l.mParameters.paramsFlattened.string());
    }

    return OK;
}

  上面的初始化工作非常主要,這裡建立了Camera執行時需要的6大執行緒,每一個執行緒負責一個功能模組,這個後面我們會專門挑出幾個模組來分析,這裡只列出他們的大致功能。

  • 1.StreamingProcessor:用來處理preview和錄影 stream的執行緒。
  • 2.FrameProcessor:用來處理3A和人臉識別的執行緒。
  • 3.CaptureSequencer:拍照流程狀態機執行緒,拍照的場景非常多,後面會發出狀態機的運轉流程,這裡就不說了。
  • 4.JpegProcessor:用來處理拍照 jpeg stream 執行緒
  • 5.ZslProcessor3:這個處理零延時zsl stream使用的執行緒
  • 6.mCallbackProcessor:處理callback的執行緒,主要包含對 callback stream 的建立函式
    updateStream()以及處理 HAL 層傳上來的 callback stream 的執行緒

4.JNICameraContext建構函式

JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera)
{ //上面weak_this就是傳下來的java fw Camera弱引用,clazz
    mCameraJObjectWeak = env->NewGlobalRef(weak_this);
    mCameraJClass = (jclass)env->NewGlobalRef(clazz);//camera.java類的弱引用
    mCamera = camera;//native Camera物件儲存下來
  //查詢android/hardware.Camera類中的內部類Face物件的標識id.下面一些操作都是找到一些與Camera相關的類的id,進而在native可以建立java支援的並解析的資料型別。
    jclass faceClazz = env->FindClass("android/hardware/Camera$Face");
    mFaceClass = (jclass) env->NewGlobalRef(faceClazz);//

    jclass rectClazz = env->FindClass("android/graphics/Rect");
    mRectClass = (jclass) env->NewGlobalRef(rectClazz);

    jclass pointClazz = env->FindClass("android/graphics/Point");
    mPointClass = (jclass) env->NewGlobalRef(pointClazz);

    mManualBufferMode = false;
    mManualCameraCallbackSet = false;
}

  JNICameraContext構造中,做了一些很關鍵性的工作,除了儲存native Camera物件,最重要的就是找到Camera需要使用的相關類,使得Camera Client可以在native建立java能使用的資料型別(callback回去後,可以直接使用的)

5.非常重要的-設定監聽

  JNICameraContext類實現了CameraListener方法,而繼承CameraListener類需要實現下面3個虛擬函式,與此同時大家在看看ICameraClient介面類中的3個方法。你也許已經猜到他們之間有曖昧關係了。先不要講話,看原始碼再體會一下。

//原始碼路徑:frameworks/av/include/camera/camera.h
class CameraListener: virtual public RefBase
{
public:
    virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2) = 0;
    virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr,
                          camera_frame_metadata_t *metadata) = 0;
    virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) = 0;
};
//--------------------------------------------------------------
//原始碼路徑:frameworks/av/include/camera/ICameraClient.h
class ICameraClient: public IInterface
{
    /**
     * Keep up-to-date with ICameraClient.aidl in frameworks/base
     */
public:
    DECLARE_META_INTERFACE(CameraClient);

    virtual void            notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) = 0;
    virtual void            dataCallback(int32_t msgType, const sp<IMemory>& data,
                                         camera_frame_metadata_t *metadata) = 0;
    virtual void            dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& data) = 0;
};

  實際上它倆真的有曖昧關係,如下圖,我們知道CameraListener方法是與上層java fw通訊的,包括訊息,資料傳送。然而native camera 實現了ICameraClient本地方法,並在方法中間接呼叫了JNICameraContext註冊下來監聽方法。就這樣CameraService端的bpCameraClient就可以通過Binder通訊方式向camera native物件發訊息和資料,進而訊息又通過postEventFromNative)會轉發給java camera物件。整個鏈路如下圖所示。

  

三、hal 如何open camera

1.camera_module_t介紹

這裡就拿Android5.1 google提供的hal3模板說事吧。

extern "C" {
camera_module_t HAL_MODULE_INFO_SYM __attribute__ ((visibility("default"))) = {
    common : {
        tag                : HARDWARE_MODULE_TAG,
        module_api_version : CAMERA_MODULE_API_VERSION_2_2,
        hal_api_version    : HARDWARE_HAL_API_VERSION,
        id                 : CAMERA_HARDWARE_MODULE_ID,
        name               : "Default Camera HAL",
        author             : "The Android Open Source Project",
        methods            : &gCameraModuleMethods,
        dso                : NULL,
        reserved           : {0},
    },
    get_number_of_cameras : get_number_of_cameras,
    get_camera_info       : get_camera_info,
    set_callbacks         : set_callbacks,
    get_vendor_tag_ops    : get_vendor_tag_ops,
    open_legacy           : NULL,
    reserved              : {0},
};

主要上面結構體是每一個硬體HAL都必須實現的,google提供標準的模組介面和ops,各大終端廠商必須遵守。

  • 1.get_number_of_cameras:這個是載入模組時第一個被呼叫的介面,獲取Camera數量
  • 2.get_camera_info:這個在startPreview之前,獲取Camera硬體資訊。
  • 3.更重要的就是methods成員,當Android載入動態庫後,會通過methods open方法開啟裝置,進而獲取標準的camera3_device_ops_t結構體指標,這樣cameraService native才能控制Camera 模組。

裝置是在初始化Camera3Device時悄悄開啟的。這裡現在有些同學可能對程式碼還不是很熟,先了解一下就行,後面我們在分析,只要知道這樣下去Camera3Device已經拿到了hal3的camera3_device_ops_t物件了。

status_t Camera3Device::initialize(camera_module_t *module) {
    ATRACE_BEGIN("camera3->open");
    res = CameraService::filterOpenErrorCode(module->common.methods->open(
        &module->common, deviceName.string(),
        reinterpret_cast<hw_device_t**>(&device)));
    ATRACE_END();
}

2.camera3_device_ops_t簡介

const camera3_device_ops_t Camera::sOps = {
    .initialize = default_camera_hal::initialize,
    .configure_streams = default_camera_hal::configure_streams,
    .register_stream_buffers = default_camera_hal::register_stream_buffers,
    .construct_default_request_settings
        = default_camera_hal::construct_default_request_settings,
    .process_capture_request = default_camera_hal::process_capture_request,
    .get_metadata_vendor_tag_ops = NULL,
    .dump = default_camera_hal::dump,
    .flush = default_camera_hal::flush,
    .reserved = {0},
};

這是hal3標準介面,前面5個介面的呼叫順序正如他們的排列順序一樣,他們的詳細解釋最好看看google在標頭檔案中介紹hardware/libhardware/include/hardware/camera3.h介紹的非常好。

  • 1.construct_default_request_settings:在startpreview之前先獲取硬體的一些引數,幀率,preview_size,picture_size 將這些打包成camerametadata 供app使用
  • 2.configure_streams:在startpreview之前,native 要建立preview資料流,其中包含了當前請求的CameraMetadata,buffer fd及數量等資訊。這裡的configure_streams介面會在hal建立preview資料通道,目前發現高通和展訊的hal都是這樣的。
  • 3.process_capture_request:當執行環境準備好後,CameraService 會不斷髮送幀捕獲請求,期間包含當前配置資訊,幀buffer和數量,讓底層去消費buffer.

四、總結

open過程道路艱辛,主要為了後續Camera正常工作,添磚加瓦,鋪路。下面我們列舉一下,主要都準備了什麼。

  • 1.Camera應用將一些Callback函式,註冊到Camera.java中,以使線上程處理函式中可以呼叫到相應的回撥函式。
  • 2.camera connect成功後,建立了BpCamera代理物件和BnCameraClient本地物件。
  • 3.在JNICameraContext實現CameraListener介面,並將介面註冊到客戶端camera本地物件中,並在BnCameraClient本地物件中回撥這些介面。
  • 4.CameraService connect過程中,根據hal硬體版本,建立對應的CameraClient物件。在後續的初始化過程中,建立6大執行緒。
    最後以一個簡單的工作流程圖來結束博文

    加油……………………