1. 程式人生 > >Android Camera Subsystem 架構(Binder機制)及顯示分析(3)

Android Camera Subsystem 架構(Binder機制)及顯示分析(3)

Camera Display

對於AP層使用者,通過Camera介面, 以最直觀的方式能夠從Camera顯示視窗中看到的Camera Display主要包括三部分。各部分的具體細節如下所示。

(1)Camera Preview Display


對於Camera Preview Display, 從Camera應用程式的角度來看,AP層需要實現Android.hardware.Camera(Framework層)類提供給上層應用的preview相關介面,完成相應的邏輯處理。但Preview作為大資料量交換的處理過程,系統不可能通過回撥方式,最終將底層Camera硬體裝置採集的資料交給AP層來處理。所以,上層應用只需要做一些簡單的狀態和UI顯示處理,AP不需要接收處理真正的Preview資料,而是將資料接收和處理的過程交由ISurface介面來完成,確切的說是由ISurface的實現類SurfaceFlinger來完成。

AP層通過呼叫Android.hardware.Camera類提供的startPreview函式,最終通過libcamera_client.so的Camera類,利用Binder IPC,通知CameraService, CameraService通過呼叫其內部類Clien,內部類再通過呼叫HAL層提供的Camera功能完成相應的操作。

CameraService層針對Camera Client端得請求,都將交與其內部類Client來完成。CameraService層的startPreview的具體實現由CameraService::Client類的startPreviewMode函式完成相應的操作。

status_t CameraService::Client::startPreviewMode() {

    // if preview has been enabled, nothing needs to be done

    if (mHardware->previewEnabled()) {

        return NO_ERROR;

    }

    // start preview mode

    status_t ret = NO_ERROR;

    if (mUseOverlay) {

              ….

    } else {

        mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME);

        ret = mHardware->startPreview();

        if (ret != NO_ERROR) return ret;

        // If preview display has been set, register preview buffers now.

        if (mSurface != 0) {

           // Unregister here because the surface registered with raw heap.

           mSurface->unregisterBuffers();

           ret = registerPreviewBuffers();

        }

    }

    return ret;

}

status_t CameraService::Client::registerPreviewBuffers()

{

    int width, height;

    CameraParameters params(mHardware->getParameters());

    params.getPreviewSize(&width, &height);

    // don't use a hardcoded format here

    ISurface::BufferHeap buffers(width, height, width, height,

PIXEL_FORMAT_RGB_565,

                            mOrientation, 0,

mHardware->getPreviewHeap());

    status_t ret = mSurface->registerBuffers(buffers);

    if (ret != NO_ERROR) {

        LOGE("registerBuffers failed with status %d", ret);

    }

    return ret;

}

 從上面的程式碼我們可以看到,ISurface為Camera Preview Display專門註冊了顯示緩衝區,該緩衝區對映到mHardware->getPreviewHeap()指向的記憶體空間,緩衝區接收的資料格式為PIXEL_FORMAT_RGB_565

CameraService已經為Camera Preview Display準備好了顯示緩衝區,哪誰為註冊的這塊Buffer填充Preview資料呢?為了完成Camera Preview,Camera HAL層的CameraHardware類提供了previewThread執行緒,來完成底層資料到ISurface緩衝區的投遞。通過執行緒Loop最終將Camera硬體裝置採集的資料不斷送到顯示緩衝區,最後顯示到AP介面上。

int CameraHardware::previewThread()

{

    int width, height;

    mParameters.getPreviewSize(&width, &height);

    if (!previewStopped && mPreviewBuffer != 0 && mRawBuffer !=0 && mDataFn) {

          int delay = (int)(1000000.0f / float(previewFrameRate));

              ①mCamera.GrabRawFrame(mHeap->getBase());

              if (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME){

                     ②mCamera.GrabPreviewFrame((mHeap->getBase(),

mPreviewHeap->getBase());

                ③mDataFn(CAMERA_MSG_PREVIEW_FRAME,

mPreviewBuffer, mUser);

                     delay-=100;

              }

              ………

    }

    return NO_ERROR;

}

從程式碼中我們可以看到,previewThread執行緒針對Preview Display,主要完成三個步驟的操作,

首先呼叫V4L2Camera的GrabRawFrame函式,通過V4L2驅動取得Camera採集的一幀資料,將該幀資料放到指定緩衝區中;

其次將取得的原始資料通過資料格式轉化函式convert,將採集到得原始資料轉化成與Preview Buffer註冊型別相一致的資料型別,也就是RGB565。在我們的系統中,Camera hardware採集的原始資料時YUYV(YUV4:2:2)格式的。所以我們必須將YUYV格式的原始資料轉化為RGB565格式的資料,然後放到PreviewBuffer中。

最後,通過回撥函式,向CameraService傳送Preview訊息CAMERA_MSG_PREVIEW_FRAME,CameraService收到該訊息之後呼叫Preview處理函式handlePreviewData,通過呼叫postBuffer函式,最終由SurfaceFlinger完成對PreviewBuffer的Draw工作。

void CameraService::Client::handlePreviewData(const sp<IMemory>& mem) {

    ssize_t offset,size;

    sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);

    if (!mUseOverlay)

    {

        Mutex::Autolock surfaceLock(mSurfaceLock);

        if (mSurface != NULL) {

            mSurface->postBuffer(offset);

        }

    }

    int flags = mPreviewCallbackFlag;

    if (!(flags & FRAME_CALLBACK_FLAG_ENABLE_MASK)) {

        LOGV("frame callback is diabled");

        return;

}

……..

}

 如上所示,HAL層將資料投遞到framebuffer中,然後通知CameraService呼叫postBuffer對資料進行顯示。Preview模式下,底層並不需要向上層AP通知自己的狀態,所以postBuffer之後直接返回不再向上通知CAMERA_MSG_PREVIEW_FRAME訊息。


在Preview模式下,通過ClickShutterButton,Camera由Previw模式進入TakePicture模式。在進入TakePicture模式之前,需要關閉Camera的預覽功能,即停止Camera Preview Display。在TakePicture模式下,AP層首先會發送一個Focus請求,Focus請求會直接到達CameraHardware層,CameraHardware呼叫底層硬體提供的介面完成Focus功能。並通過回撥函式notify,傳送CAMERA_MSG_FOCUS訊息,完成Focus,在完成Focus之前,系統並不會停止Preview Display,只有上層接到底層返回的FOCUS成功訊息之後,才真正的進入TakePicture模式。在進入TakePicture之後,Camera主要完成三個功能。

int CameraHardware::pictureThread() {

   int width, height;

   if (mMsgEnabled & CAMERA_MSG_SHUTTER) {

        ①mNotifyFn(CAMERA_MSG_SHUTTER, 0, 0, mUser);

    }

    mParameters.getPictureSize(&width, &height);

       ②mCamera.GrabRawFrame(mHeap->getBase());

    if (mMsgEnabled & CAMERA_MSG_RAW_IMAGE) {

              ②mCamera.convert((unsigned char *)mHeap->getBase(),

(uint8_t *)mRawHeap->getBase(), width, height);

        ②mDataFn(CAMERA_MSG_RAW_IMAGE, mRawBuffer,mUser);

    }

    if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) {

        ③mDataFn(CAMERA_MSG_COMPRESSED_IMAGE,

                                          camera.GrabJpegFrame(mHeap->getBase()),mUser);

    }

    return NO_ERROR;

}

Step1:上層通過呼叫takepicture函式,最終通過Camera HAL層的CameraHardware啟動captureThread執行緒,啟動Capture執行緒之後,CameraHardware在第一時間會通過回撥函式notify,向上傳送CAMERA_MSG_SHUTTER訊息給CameraService,同時CameraService將該訊息返回給上層AP。

void CameraService::Client::handleShutter(image_rect_type *size) {

    // Play shutter sound.

       ……

    // Screen goes black after the buffer is unregistered.

    if (mSurface != 0 && !mUseOverlay) {

        mSurface->unregisterBuffers();

    }

    sp<ICameraClient> c = mCameraClient;

    if (c != NULL) {

        c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0);

    }

    mHardware->disableMsgType(CAMERA_MSG_SHUTTER);

    if (mSurface != 0 && !mUseOverlay) {

        int width, height;

        CameraParameters params(mHardware->getParameters());

        width = size->width;

        height = size->height;

        ISurface::BufferHeap buffers(width, height, width, height,

                                                       PIXEL_FORMAT_RGB_565,

                                                        mOrientation, 0, mHardware->getRawHeap());

        mSurface->registerBuffers(buffers);

        IPCThreadState::self()->flushCommands();

    }

}

如上圖所示,CameraService收到CAMERA_MSG_SHUTTER訊息之後,首先通過回撥函式,將該訊息再轉發給上層AP,然後根據Picture大小呼叫ISurface註冊用於Capture Image Display的Capture Image Display Buffer。

Step2:CameraHardware開始真正的Capture Frame,首先呼叫底層GrabRawFrame函式獲取Capture Image Frame,接下來要完成兩件事:

一是將獲取的原始Capture Frame資料轉化為與顯示緩衝區一直的資料格式,然後將轉化後的資料投遞到之前註冊的Capture Image Display緩衝區中。

二是通過回撥函式,向CameraService傳送Capture Image Display訊息CAMERA_MSG_RAW_IMAGE,CameraService收到該訊息之後呼叫RawPicture處理函式handleRawPicture,通過呼叫postBuffer函式,最終由SurfaceFlinger完成對RawBuffer的Draw工作。

void CameraService::Client::handleRawPicture(const sp<IMemory>& mem)

{

    ssize_t offset;

    size_t size;

    sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);

    // Put the YUV version of the snapshot in the preview display.

    if (mSurface != 0 && !mUseOverlay) {

        mSurface->postBuffer(offset);

    }

    sp<ICameraClient> c = mCameraClient;

    if (c != NULL) {

        c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem);

    }

    mHardware->disableMsgType(CAMERA_MSG_RAW_IMAGE);

}

private final class RawPictureCallback implements PictureCallback {

     public void onPictureTaken(

              byte [] rawData, Android.hardware.Camera camera) {

          mRawPictureCallbackTime = System.currentTimeMillis();

     }

}

與Preview訊息不同的是,CameraService會將該訊息繼續發給上層AP,上層針對該訊息會做一些簡單的處理,主要是記錄訊息到達的系統時間。

Step3:獲取JPEG Image,並存儲JPEG Image,生成JPEG Image的縮圖顯示在Camera視窗的右上角。在GrabRawFrame函式的基礎上,我們需要將獲取的原始資料轉化成JPEG格式的資料,所以我們將之前GrabRawFrame獲得的原始資料傳給GrabJpegFrame(void * buffer)函式,返回滿足要求的JPEG格式的資料。隨後通過回撥函式,向CameraService傳送CAMERA_MSG_COMPRESSED_IMAGE訊息,CameraService收到該訊息之後呼叫JPEG Image處理函式handleCompressedPicture。

void CameraService::Client::handleCompressedPicture(const sp<IMemory>& mem)

{

    sp<ICameraClient> c = mCameraClient;

    if (c != NULL) {

        c->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem);

    }

    mHardware->disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE);

}

handleCompressedPicture和之前的處理函式不同,而是將訊息和資料直接(其實是間接,還要通過Camera Client端,Camera JNI, Android.hardware.Camera)交給上層AP來處理。包括JPEG Image Storage和 Create Image Thumbnail。

在定義的延遲時間內,在Preview Layout中顯示Capture image,之後通過呼叫restartPreview,恢復到Preview Mode狀態下。

(3)VideoCamera Preview Display

當由Capture Preview狀態切換到VideoPreview狀態進入VideoCamera模式時,首先VideoCamera模式提供給使用者的是Preview Display,這部分同Camera Preview Display。

int CameraHardware::previewThread()

{

    mLock.lock();

    int previewFrameRate = mParameters.getPreviewFrameRate();

    mLock.unlock();

    Mutex::Autolock lock(mPreviewLock);

    if (!previewStopped && mPreviewBuffer != 0

&& mRawBuffer !=0 && mDataFn) {

          int delay = (int)(1000000.0f / float(previewFrameRate));

              ①mCamera.GrabRawFrame(mHeap->getBase());

              if (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME){

                     ②mCamera.GrabPreviewFrame(mHeap->getBase(),

mPreviewHeap->getBase());

                ③mDataFn(CAMERA_MSG_PREVIEW_FRAME,

mPreviewBuffer, mUser);

                     delay-=100;

              }

              ①mRecordingLock.lock();

              if ((mMsgEnabled & CAMERA_MSG_VIDEO_FRAME)

&& mRecordBuffer != 0 && mTimestampFn) {

                  ②mCamera.GrabRecordFrame(mHeap->getBase(),

                                         mRecordHeap->getBase());

                     ③nsecs_t timeStamp = systemTime(SYSTEM_TIME_MONOTONIC);

                     ④mTimestampFn(timeStamp,

CAMERA_MSG_VIDEO_FRAME, mRecordBuffer, mUser);

                     delay-=100;

        }

              mRecordingLock.unlock();

        usleep(delay);

    }

    return NO_ERROR;

}

 

在VideoCamera模式下,當用戶通過ClickShutterButton進入Camera Recording狀態時,使用者看到的是Camera Recording Preview Display,從資料的顯示過程同Camera Preview Display,但在此狀態下,需要考慮到向Opencore框架,也就是CameraInput模組投遞由Camera硬體採集的Record資料。CameraHardware不僅需要向Preview緩衝區投遞資料,同時還要向CameraInput傳送資料。如上圖所示。

在Camera Recording狀態下,libcamera_client.so端的回撥函式(也就是CameraListener類定義的回撥函式)不再由JNICameraContext類來實現,而是由AndroidCameraInputListener實現,換句話說,在CameraInput中的mListener成員變數是由AndroidCameraInputListener來初始化的。

Location:frameworks/base/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) = 0;

    virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType,

const sp<IMemory>& dataPtr) = 0;

};

 

 在整個Androi系統中,總共有三個類來分別實現這個抽象類,他們分別是JNICameraContext、AndroidCameraInputListener、CameraSourceListener類。

①Location:frameworks/base/core/jni/Android_hardware_Camera.cpp

class JNICameraContext: public CameraListener

②Location:external/opencore/Android/author/Android_camera_input.h

class AndroidCameraInputListener: public CameraListener

③Location:frameworks/base/core/jni/Android_hardware_Camera.cpp

class CameraSourceListener: public CameraListener

 

而具體實現上,由於CameraInput只需要帶時間戳的資料,所以AndroidCameraInputListener只實現了postDataTimestamp回撥函式。

void AndroidCameraInputListener::postDataTimestamp(nsecs_t timestamp,

int32_t msgType, const sp<IMemory>& dataPtr)

{

    if ((mCameraInput != NULL)

&& (msgType == CAMERA_MSG_VIDEO_FRAME)) {

        mCameraInput->postWriteAsync(timestamp, dataPtr);

    }

}

 

根據Opencore框架中CameraInput中的實現,CameraInput可以作為CameraClient端得一個例項,通過Camera和CameraService架構實現包括預覽,錄影在內的所有功能。

但在目前的Camera子系統中,Camera Recording模式與Camera Capture模式共享同一個CameraDevice例項,Camera Recording的預覽是通過使用Camera Capture模式下的Surface實現的,即Camera Recording通過呼叫Camera Capture中的setPreviewDisplay和底層CameraHardware層的previewThread執行緒實現Camera Recording預覽。