解讀Android 4.0 Camera原生應用程序的設計思路

1. 設置攝像頭方向 html

2. 打開線程與預覽線程  java

3. 設置參數 linux

4. Camera外設按鍵 android

5. 自動對焦與觸摸對焦 數據結構

6. 拍照 架構

7. 人臉檢測 框架

8. 位置管理 ide

9. 旋轉管理 函數

10. 變焦 spa

11. 錄像



Camera的架構爲典型的C/S架構,Client端,用戶的行爲,是爲應用程序進程,Server端,設備的功能,是爲Camera服務守護進 程,客戶端進程承載用戶的需求,由Binder進程間通訊送往服務端實現設備的功能,服務端由回調函數和消息機制反饋數據返還給用戶。

ps查看進程,相似 com.android.camera是爲客戶端Camera進程,/system/bin/mediaserver是爲服務端守護進程,由系統啓動時開啓。


1. 設置攝像頭方向

Framework框架層的Camera對象(camera.java)裏有一個類class CameraInfo,裏面存放了兩個公有成員變量facing和orientation,即咱們要討論的先後置和方向。 程序第一次初始化時initializeFirstTime(),經過getCameraInfo()獲得先後置和方向的信息,客戶端發送請求 getCameraInfo()詢問服務端,服務端調用抽象層拿數據,抽象層參考底層camera sensor驅動的數據facing和orientation,這兩個值在驅動裏是寫死的,不能由用戶改變,camera程序啓動之後就把它們做爲全局變 量存放起來。

1.1 前置與後置

後置back camera背對手機屏幕,朝外,像素高,前置front camera,面對本身,朝內,像素低。

1.2 方向

攝像頭模組有長邊和短邊,好比採集圖像的比例爲4:3,那麼4爲長邊,3爲短邊。設備屏幕也有長邊和短邊,理論上,攝像頭模組的長邊不能與屏幕的長邊垂直,至於爲何呢,我語文水平太差,沒辦法很好地表達出來...總之目的就是爲了顯示效果。


2 打開線程與預覽線程

onCreate()裏會前後開啓CameraOpenThread和CameraPreviewThread。

打開camera還須要線程?CameraOpenThread名爲打開,實爲C/S connect鏈接服務端,binder進程間通訊,開銷較大。預覽線程必須在打開線程完成之後執行,它貫穿始終直到進程消亡爲止,整個預覽過程相對復 雜,在抽象層和底層驅動實現,歸納講,預覽線程再開啓兩個線程,一個拿sensor的frame,一個送往framebuffer經 surfaceflinger顯示出來。


3 設置參數

預覽拍照錄像以前,用戶須要設置不少參數,好比閃光,白平衡,場景,對比度等。

程序裏這些參數保存在SharedPreferences共享優選項和Parameters兩個地方,Preferences包含 Parameters,打開程序讀取優選項參數,關閉程序保存優選項參數,考慮到用戶常常會調整參數,引入Parameters來保存從打開之後到關閉以 前這個中間過程的參數變量,Parameters的鍵值由抽象層根據硬件sensor的能力決定。


4. Camera外設按鍵

Manifest裏註冊broadcast receiver,

        <receiver android:name="com.android.camera.CameraButtonIntentReceiver">
            <intent-filter>
                <action android:name="android.intent.action.CAMERA_BUTTON"/>
            </intent-filter>
        </receiver>

有些手機上有camera按鍵,用戶按下按鍵,android輸入系統有兩種實現方法,

1)發送廣播CAMERA_BUTTON,收到廣播後開啓主Activity。

2)上報鍵值KEYCODE_CAMERA,程序收到消息,可自定義實現功能,好比拍照。

    public boolean onKeyDown(int keyCode, KeyEvent event) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_CAMERA:
                if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
                    onShutterButtonClick();
                }
                return true;


5. 自動對焦與觸摸對焦

外界事物由光線經凸透鏡聚焦到sensor上成像,camera模組開啓馬達先後平移鏡頭取得最佳成像效果,這個過程稱之爲對焦。

5.1 自動對焦

1) camera模組會因感光強度變化而自動開啓對焦,驅動控制。

2) 用戶長按快門,軟件控制自動對焦。

3) 用戶按下快門拍照,拍攝前自動對焦。

程序裏,Camera對象實現類ShutterButton的接口OnShutterButtonListener裏的方法 onShutterButtonFocus(boolean pressed)和onShutterButtonClick(),後者是拍照,下節討論,先看 onShutterButtonFocus(boolean pressed),這個pressed判斷是否爲一次有效的長按,若是是的話,執行autoFocus(),這個autoFocus()也是Camera 對象實現類FocusManager的接口Listener裏的方法,由binder交給camera service,最後在底層驅動實現自動對焦。

5.2 觸摸對焦

自動對焦的對焦區域位於屏幕正中,用戶也可觸摸特定區域對焦。

Camera對象實現類View的接口OnTouchListener裏的方法onTouch(),輸入系統上報MotionEvent的xy座標,保存在Parameters,執行autoFocus(),抽象層讀取Paramters的觸摸點座標,實現區域對焦。


6. 拍照

拍照分四步,對焦,拍照,接收圖片,保存圖片。

mCameraDevice.takePicture(mShutterCallback, mRawPictureCallback,
                mPostViewPictureCallback, new JpegPictureCallback(loc));

須要理解四個回調函數,參考上一篇文章。

1)對焦

拍照前若是已經區域對焦,則取消自動對焦,反之,開啓一次自動對焦。對焦完成後,底層發送對焦成功與否的消息給camera對 象,FocusManager把狀態mState保存起來,若是正在對焦未完成(mState == STATE_FOCUSING)則不可拍照,直到對焦完畢。

2)拍照

onShutterButtonClick() -> doSnap() -> capture() -> takePicture(),具體實如今抽象層和底層驅動,實質就是拿一張預覽的圖像,抽象層讀取拍照時的Parameters參數配置,包括用戶選擇的 照片大小。

3)接收圖片

經過回調,由底層發送圖片給camera對象。

RawPictureCallback,獲得原始圖片,須要軟件壓縮Jpeg。(YUV轉Jpeg)

JpegPictureCallback,直接獲得Jpeg圖片,須要硬件壓縮Jpeg。

PostViewPictureCallback,拍完後預覽圖片。

4)保存圖片

交由線程ImageSaver保存圖片和生成thumbnails。

默認路徑位於/mnt/sdcard/DCIM/Camera/


7. 人臉檢測

人臉檢測能夠軟件實現,能夠硬件實現,軟件實現增長CPU開銷,硬件實現增大耗電,鼓勵硬件實現...

上層Camera對象實現了 framework層Camera的接口FaceDetectionListener的方法onFaceDetection(Face[] faces, Camera camera),回調機制同上,硬件sensor識別臉部信息,發送face給camera對象,framework定義face的特徵,好比眼睛,嘴 巴,上層保存mFaces數據,更新UI。


8. 位置管理

位置管理LocationManager用來記錄拍攝圖片的GPS位置信息(經維度),存入JPEG頭部插入的Exif裏。

用戶在菜單「相機設置」裏的"保存所在位置"選擇打開(前提是GPS已開啓),屏幕左上方會出現一個GPS圖標,表示如今能夠記錄GPS信息了。

程序裏,Camera對象實現了位置管理監聽器LocationManager.Listener的接口showGpsOnScreenIndicator()和hideGpsOnScreenIndicator(),顯示或者隱藏GPS圖標。

程序第一次初始化時initializeFirstTime(),經過讀取優選項Preference獲得bool值 recordLocation,判斷是否須要記錄GPS信息,若是須要,在拍照capture()裏調用LocationManager的方法獲得 Location loc,並將其存入Exif。


9. 旋轉管理

假設一臺手機,camera正常安裝,豎直方向做爲默認方向(orientation == 0)拍攝照片,即拍攝「肖像照」(portrait),獲得的照片顯示在屏幕上也是豎直方向。

若是把手機旋轉90度水平過來拍攝「山水照」(landscape),對於camera sensor來講,沒有旋轉的概念,因此軟件上要把圖片旋轉90度回來。


軟件上,須要藉助方向監聽器隨時更新方向信息,並保存在Parameters裏,抽象層實現拍照功能時從Parameters裏讀取方向。

具體的,camera對象內部類MyOrientationEventListener的方法onOrientationChanged()保存方 向orientation的值,MyOrientationEventListener繼承 OrientationEventListener,OrientationEventListener的onSensorChanged()把從 sensorManager拿到的xyz座標轉換成方向。

程序啓動,註冊sensor監聽器並使能,sensorManager不斷上報底層sensor數據,經過消息機制發送到camera對象,後者計 算座標數據獲得方向orientation的值(實際外包給orientationListener完成),最後保存Parameters。


10. 變焦

用戶拖動Zoom橫條可放大縮小預覽畫面連續變焦,另外一種所謂狀態變焦,其原理是同樣的。

camera對象的內部類ZoomChangeListener實現ZoomControl的方法,實質是把變焦的任務全權交給 ZoomControl。ZoomControl監聽處理用戶的觸摸事件dispatchTouchEvent(),用來獲得並處理變焦倍數 mListener.onZoomValueChanged(index),它由mCameraDevice.startSmoothZoom()經過 binder交給camera service,camera service再經過sendComand命令機制交給抽象層實現變焦,抽象層開啓變焦線程,變焦改變預覽,經過回調機制發送消息 CAMERA_MSG_ZOOM把變焦倍數返還給camera對象,最終camera對象收到消息 後,ZoomListener.onZoomChange()把變焦倍數保存到Parameters.


11. 錄像

ModePicker負責切換模式,一共有三種模式,普通模式,錄像模式和全景模式,Manifest裏依次聲明這三個activity。

切換模式,銷燬原有activity,開啓新activity,伴隨關閉preview,重啓preview,保存配置,讀取配置,開銷很大。

錄像VideoCamera.java同預覽Camera.java的思路相似,按下錄像按鈕,程序監聽用戶事件,開啓錄像,錄像交給MediaRecoder,StagefrightRecorder負責。


1. Overview

1.1 物理架構

1.2 Android架構

2. CameraService

3. HAL

4. Overlay

5. Video for Linux

1. Overview

 本文以Freescale IMX爲例剖析camera攝像頭的系統架構。

 

1.1 物理架構

 硬件方面,camera系統分爲主控制器和攝像頭設備,功能上主要有preview預覽,takePicture拍照和recording錄像。

1) IPU - Image Process Unit 圖像處理單元,用於控制攝像機和顯示屏。

2)圖像採集 - 由camera採集的圖像數據信息經過IPU的CSI接口控制。

3)DMA映射到內存 - IPU將採集到得數據經過DMA映射到一段內存。

4)隊列機制 - 爲了更高效地傳送數據,將內存中的數據取出加入一隊列,並傳送到另外一隊列。

5)視頻輸出 - 將視頻數據從隊列中取出,經過IPU控制這段獨立顯存,最終將視頻顯示出來。

 

1.2 Android架構

Android的camera系統架構自上而下分別爲應用層-框架層-硬件抽象層-linux驅動層。

1) APP - Framework

應用層與java框架層的間主要由Binder機制進行通訊。

系統初始化時會開啓一個CameraService的守護進程,爲上層應用提供camera對的功能接口。

2) Framework - HAL

框架層與硬件抽象層間經過回調函數傳遞數據。

3) Overlay

Overlay層由ServiceFlinger和OverlayHal組成,實現視頻輸出功能,只有camera框架層或者視頻框架層能調用它,上層沒法直接調用。

4) HAL - driver

抽象層位於用戶空間,經過系統調用如open(),read(),ioctl()等與內核空間進行數據傳遞。

 

2 CameraService

 Camera的主要功能有取景Preview,拍照takePicture和攝影Recording,下文以取景爲例,剖析camera系統架構。

 

要實現取景Preview功能,主要須調用CameraService::Client::startPreview()和 CameraService::Client::setOverlay(),前者經過mHardware->startPreview();調用 cameraHal硬件抽象層以實現取景的整個流程,後者經過mSurface->createOverlay();調用 surfaceFlinger層建立overlay_object對象。

 

3 HAL

 

startPreview主要完成三項任務,配置圖象,配置內存,開啓兩個存取buf隊列的線程。

1) cameraPreviewConfig()配置預覽圖象參數

CameraOpen() - 經過打開設備節點/dev/video0得以由系統接口與設備驅動交互。

S_FMT - ioctl()的指令,設置圖象像素格式,將數據由硬件抽象層傳遞至Linux驅動,這裏也就是v4l2。

G_FMT - 獲得圖象像素格式,將數據由底層驅動v4l2返回至硬件抽象層。

S_PARM - 設置模式的指令,這個指令傳到底層後,將會實現對camera硬件的控制。

2) cameraPreviewStart()開啓預覽,實際上配置了內存

REQBUFS - 申請內存,經過dma_alloc_coherent()爲camera申請一端連續的dma內存。

QUERYBUF - 詢問內存,將申請到內存的物理地址,虛擬地址等數據從內核空間傳遞到用戶空間。

QBUF - 加入隊列,將經過詢問獲得的buf加入一個隊列。

3) PreviewShowFrameThread()和PreviewShowFrameThread()

PreviewCaptureFrameThread()捕捉一幀數據的線程,經過DQBUF,從隊列中取出一個buf數據,這裏,一個buf即一幀數據即一張圖片。注意,若是camera沒有采集到圖片,這個線程會在DQBUF阻塞。

PreviewShowFrameThread()顯示一幀數據的線程。

mDataCb() - 回調函數,將採集到的圖象數據傳回CameraService,再由CameraService傳遞給上層應用。

mOverlay->dequeueBuffer() - 調用Overlay層,從Overlay層獲得一個空閒的overlaybuffer,將圖象數據拷貝到這個buffer裏。至於這個buffer後續的工做,即視頻輸出,則交給了Overlay去完成。

QUERYBUF & QBUF - 因爲已經從隊列裏取出了一個buf,須要再詢問並加入另外一個buf到隊列裏。

4) Overlay

CameraService::Client::startPreview()完成mHardware->startPreview();後 便去執行CameraService::Client::setOverlay(),若是沒有任何overlay,則建立一個新的,經過 mHardware->setOverlay(new Overlay(mOverlayRef))調用到SurfaceFlinger層,再由 overlay_dev->createOverlay();調用到overlay的硬件抽象層,抽象層建立並初始化overlay對象,與 cameraHal相似,經過ioctl()指令與底層v4l2通訊,配置視頻參數和內存空間。隨後開啓一個overlay線程,用於存取隊列中的視頻數 據。

注意,SurfaceFlinger裏也會開啓一個處理overlay的surfaceFlinger線程,用於等待用戶事件,做相應的overlay控制。

 

5 Video for Linux

 

v4l2 - video for linux 2是linux爲視頻驅動抽象出的一層統一的接口,數據結構以下,

v4l2做爲master主設備由(*attach)與camera從設備進行綁定。

初始化函數probe()以下,

1) init_camera_struct()初始化v4l2主設備的數據結構,實現open(), read(), ioctl(), mmap()等操做。

2) v4l2_int_device_register(),註冊v4l2主設備,綁定camera從設備。

3) video_register_device()註冊linux video設備,創建/dev/video0設備節點。

相關文章
相關標籤/搜索