解读Android 4.0 Camera原生应用程序的设计思路
解讀Android 4.0 Camera原生應用程序的設計思路
一篇很不錯的文章: http://my.oschina.net/jerikc/blog/907911. 設置攝像頭方向
2. 打開線程與預覽線程?
3. 設置參數
4. Camera外設按鍵
5. 自動對焦與觸摸對焦
6. 拍照
7. 人臉檢測
8. 位置管理
9. 旋轉管理
10. 變焦
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設備節點。
總結
以上是生活随笔為你收集整理的解读Android 4.0 Camera原生应用程序的设计思路的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c++复习日记3 模板和流
- 下一篇: 这道笔试题竟然运行不出错