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預覽。