MTK Camera 從底層到應(yīng)用層一網(wǎng)打盡

轉(zhuǎn)載請注明出處(http://www.reibang.com/p/5f538820e370),您的打賞是小編繼續(xù)下去的動力

  1. Camera總體架構(gòu)

1.1 Android系統(tǒng)架構(gòu)

1.2 MTK Android Camera架構(gòu)分層及代碼結(jié)構(gòu)

2.Camera HAL層分析

2.1 Camera HAL層的主要類介紹

2.2 Camera硬件抽象層的三種業(yè)務(wù)

2.2.1取景器preview(使用YUV原始數(shù)據(jù)格式辖佣,發(fā)送到視頻輸出設(shè)備)

2.2.2拍攝照片(可以使用原始數(shù)據(jù)或者壓縮圖像數(shù)據(jù))

2.2.3 視頻錄制(將數(shù)據(jù)傳送給視頻編碼器程序)

3.Camera Framework層分析

3.1 Camera Framwork整體架構(gòu)和類關(guān)系簡介

3.2 Camera Framwork層代碼調(diào)用流程分析

3.2.1應(yīng)用層onCreate函數(shù)入口

3.2.2 Framework層的open方法

3.2.3 JNI層的調(diào)用

3.2.4 Camera connect到服務(wù)

3.2.5 Camera服務(wù)之Binder代理端

3.2.6 Camera服務(wù)之CameraService分析

3.2.7 Camera服務(wù)之Client分析

3.2.8 Camera服務(wù)之Callback分析

4.Camera 應(yīng)用層對Framework的封裝分析

4.1 應(yīng)用層對Framework層封裝的層次和類介紹

4.2 camera的調(diào)用流程

5.Camera 應(yīng)用初始化分析

6.Camera UI介紹

6.1 Camera UI總體預(yù)覽

6.2 ModePicker類

6.3 IndicatorManager類

6.4 RemainingManager類

6.5 PickerManager類

6.6 ThumbnailManager類

6.7 ShutterManager類

6.8 SettingManager類

6.9 FocusManager類

7.Camera 預(yù)覽流程

7.1 Camera預(yù)覽命令流程

7.1.1 App層流程

7.1.2 Framework層流程

7.1.3 Binder調(diào)用

7.1.4 HAL層流程

7.2 數(shù)據(jù)流程

7.3 函數(shù)調(diào)用流程

8.Camera拍照流程

8.1 CameraService初始化過程

8.2應(yīng)用程序鏈接相機(jī)服務(wù)過程

8.2.1 Camera連接整個(gè)過程

8.2.2 Camera takepicture過程

8.2.3 Camera takepicture過程總結(jié)

9.Camera Feature 介紹和API使用

9.1 Face detection人臉檢測

9.1.1人臉檢測主要介紹代碼實(shí)現(xiàn)要素

9.1.2調(diào)用流程郭脂,這里以普通模式為例

9.2人臉美化Face beauty

9.2.1功能介紹

9.2.2三個(gè)參數(shù)的客制化

9.2.3常見問題

9.2.4調(diào)用流程

9.3 ASD:自動場景檢測

9.3.1限制條件

9.3.2常見問題

9.3.3代碼實(shí)現(xiàn)要素

9.4 Continue Shot

9.4.1簡單介紹與使用方法

9.4.2 Continue Shot Spec and Limitation

9.4.3 Continue Shot效果調(diào)試/客制化參數(shù)

9.4.4 Continue Shot常見問題

9.5零延時(shí)拍照ZSD

9.5.1簡介

9.5.2 ZSD Spec and Limitation

9.5.3 ZSD客制化參數(shù)

9.5.4 ZSD常見問題

9.6笑臉模式

9.6.1簡介

9.6.2限制條件

9.6.3 FD常見問題

9.7物體動態(tài)追蹤Object Tracking

9.7.1簡介

9.7.2說明與限制條件

9.7.3 OT效果調(diào)試/客制化參數(shù)

9.8全景拍照

9.8.1簡介

9.8.2規(guī)則和限制條件

9.8.3全景拍照客制化參數(shù)

9.8.4全景拍照常見問題

9.8.5全景拍照相關(guān)問題debug流程

9.9多視角拍照(MAV)

9.9.1簡介

9.9.2限制與規(guī)則

9.9.3常見問題

9.10情景照片Live Photo

9.10.1簡介

9.10.2適用場景及使用方法

9.10.3限制條件

9.11 HDR

9.11.1簡介

9.11.2 HDR 規(guī)則和限制條件

9.11.3 HDR效果調(diào)試/客制化參數(shù)

9.11.4 HDR常見問題

10.Camera中第三方算法添加

10.1 Camera HAL層預(yù)覽

10.2 Camera圖像緩沖Queue

10.3第三方算法預(yù)覽部分

10.3.1 ExtImgProc介紹

10.3.2 相關(guān)類結(jié)構(gòu)圖

10.3.3 ExtImpProc處理過程

10.3.4三方算法對性能影響的簡單分析

10.4拍照的第三方算法添加

10.4.1移植capture的第三方算法

10.4.2對ZSD 做第三方算法處理:(CapBufShot.cpp)

10.5 新增一個(gè)Capture Mode

11.拍照赃份、錄像接口調(diào)用及開發(fā)

11.1 調(diào)用系統(tǒng)Camera APP實(shí)現(xiàn)功能

11.1.1 實(shí)現(xiàn)拍照

11.1.2 實(shí)現(xiàn)攝像

11.2調(diào)用Camera API實(shí)現(xiàn)相應(yīng)功能

11.2.1添加權(quán)限

11.2.2 實(shí)現(xiàn)拍照功能

11.2.3實(shí)現(xiàn)攝像功能

12.語音拍照詳解

12.1設(shè)置界面應(yīng)用是否增加開啟漓帅、關(guān)閉語言識別的開關(guān)

12.2增加第三方應(yīng)用支持設(shè)置

12.3增加第三方應(yīng)用支持的關(guān)鍵詞

12.4更改默認(rèn)設(shè)置

12.5更改默認(rèn)語言

12.6增加新的語言支持

12.7增加語言拍照和語言設(shè)置的關(guān)聯(lián)

13.Camera Performance問題分析初步

13.1 Camera startup time

13.1.1 Camera startup time 的不同模式差異分析

13.1.2 Camera startup time 分析LOG

13.1.3 Camera startup time 的參考數(shù)據(jù)

13.2 Shot to Shot/shutter delay

13.2.1 Shot to Shot/shutter delay分析LOG

13.2.2參考數(shù)據(jù)

13.2.3各階段受客制化影響的主要因素

13.3 main/sub sensor switch time

13.3.1 Shot to Shot/shutter delay分析LOG

13.3.2參考數(shù)據(jù)

13.4目前平臺可優(yōu)化的地方

13.4.1 Stop preview節(jié)省幀率

13.4.1拍照回顯定格時(shí)間較長

14.HQ Camera整體優(yōu)化方案介紹

14.1改進(jìn)思路

14.2用戶需求(競品機(jī)優(yōu)勢)

14.3 Camera基礎(chǔ)效果優(yōu)化(持續(xù)進(jìn)行中)

14.4軟件特效類 132

14.5其他提升體驗(yàn)的細(xì)節(jié)優(yōu)化(進(jìn)展)

14.6前攝選型要求

14.7夜拍效果提升選型要求

14.8提高拍照速度-修改原理及細(xì)節(jié)

14.9優(yōu)化暗光下MFLL和MBF功能閥值

14.10 零延時(shí)拍照

14.11下一步計(jì)劃

1. Camera總體架構(gòu)

1.1 Android系統(tǒng)架構(gòu)

要分析Android Camera架構(gòu)虱黄,首先從Android本身的架構(gòu)分析常挚。如下圖:

圖片.png

從Android框架看钉疫,分為四層:應(yīng)用層豁状、應(yīng)用框架層要出、庫層鸳君、內(nèi)核層。

應(yīng)用層主要工作在Android SDK之上患蹂,利用Android提供的API進(jìn)行開發(fā)或颊,生成APK包。

應(yīng)用框架層整合了Android自帶各種控件和類传于,為應(yīng)用開發(fā)提供高效囱挑、方便的API接口。這個(gè)對上實(shí)現(xiàn)統(tǒng)一的接口沼溜,對下也提供統(tǒng)一的標(biāo)準(zhǔn)方便各種庫的移入平挑。

庫層是Android與底層硬件通信接口,它封裝底層硬件接口實(shí)現(xiàn)該模塊的具體邏輯系草,并以服務(wù)的形式通過Binder通訊機(jī)制暴露給應(yīng)用框架通熄。

內(nèi)核層則是直接與硬件聯(lián)系的一層,可以理解為設(shè)備驅(qū)動找都。

1.2 MTK Android**** Camera架構(gòu)分層及代碼結(jié)構(gòu)

Android的Camera包含取景器(viewfinder)和拍攝照片(takepicture)的功能唇辨。目前MTK Android Camera程序的架構(gòu)分成客戶端和服務(wù)器兩個(gè)部分,它們建立在Android的進(jìn)程間通訊Binder的結(jié)構(gòu)上能耻。Camera模塊同樣遵循Android的框架赏枚,如下圖所示。

圖片.png

Camera 架構(gòu)主要分為以下幾個(gè)層次:

1.****應(yīng)用層

Camera的應(yīng)用層在Android上表現(xiàn)為直接調(diào)用SDK API開發(fā)的一個(gè)Camera 應(yīng)用APK包嚎京。代碼在\packages\apps\Camera下嗡贺。主要是Java寫的基于android.hardware.Camera類調(diào)用的封裝隐解,并且實(shí)現(xiàn)Camera應(yīng)用的業(yè)務(wù)邏輯和UI顯示鞍帝。android.hardware.Camera就是Android提供給上層調(diào)用的Camera類。這個(gè)類用來連接或斷開一個(gè)Camera服務(wù)煞茫,設(shè)置拍攝參數(shù)帕涌,開始摄凡、停止預(yù)覽,拍照等蚓曼。它也是Android Camera應(yīng)用框架封裝暴露出來的接口亲澡。一個(gè)Android應(yīng)用中若要使用這個(gè)類,需要在Manifest文件聲明Camera的權(quán)限纫版,另外還需要添加一些<uses-feature>元素來聲明應(yīng)用中的Camera特性床绪,如自動對焦等。具體做法可如下:

<uses-permission android:name="android.permission.CAMERA" />

<uses-feature android:name="android.hardware.camera" />

<uses-feature android:name="android.hardware.camera.autofocus" />

2.應(yīng)用框架層

Camera框架層將應(yīng)用與底層的實(shí)現(xiàn)隔離開來其弊,實(shí)現(xiàn)了一套Android定義的對上對下接口規(guī)范癞己,方便應(yīng)用及底層硬件的開發(fā)和移植。這一層對上以Java類的形式包裝出android.hardware.Camera梭伐,提供給應(yīng)用層調(diào)用痹雅;對下在CameraHardwareInterface.h頭文件中定義了Camera硬件抽象層的接口,這是一個(gè)包含純虛函數(shù)的類糊识,必須被實(shí)現(xiàn)類繼承才能使用绩社。這個(gè)實(shí)現(xiàn)類也即是下層中將講到的用戶庫層,它繼承CameraHardwareInterface接口赂苗,實(shí)例化對底層硬件驅(qū)動的封裝愉耙,最終生成libcamera.so供框架的libcameraservice.so調(diào)用。這樣做的好處是讓Camera的應(yīng)用框架代碼獨(dú)立拌滋,不受底層硬件驅(qū)動改變的影響劲阎,方便在不同平臺上porting 驅(qū)動代碼,而保持上層的代碼不用變化鸠真。

從代碼上看悯仙,這一層包含Java到JNI到C++的代碼。源代碼主要在以下路徑:

\android\frameworks\base\core\java\android\hardware\Camera.java

這個(gè)類作為Android SDK Camera部分提供給上層應(yīng)用吠卷,并通過JNI的方式調(diào)用本地C++代碼锡垄。

\android\frameworks\base\core\jni\android_hardware_Camera.cpp是Camera 的JAVA本地調(diào)用部分,是承接JAVA代碼到C++代碼的橋梁祭隔。編譯生成libandroid_runtime.so货岭。

\android\frameworks\base\libs\ui 包含文件:

Camera.cpp

CameraParameters.cpp

ICamera.cpp

ICameraClient.cpp

ICameraService.cpp

它們的頭文件在\android\frameworks\base\include\ui目錄下。這部分的內(nèi)容編譯生成libui.so疾渴。在Camera模塊的各個(gè)庫中千贯,libui.so位于核心的位置,作為Camera框架的Client客戶端部分搞坝,與另外一部分內(nèi)容服務(wù)端libcameraservice.so通過進(jìn)程間通訊(即Binder機(jī)制)的方式進(jìn)行通訊搔谴。\android\frameworks\base\camera\libcameraservice CameraService是Camera服務(wù)紊遵,Camera框架的中間層夯秃,用于鏈接CameraHardwareInterface 和 Client难咕,它通過調(diào)用實(shí)際的Camera硬件接口來實(shí)現(xiàn)功能回怜。這部分內(nèi)容被編譯成庫libcameraservice.so。

libandroid_runtime.so和libui.so兩個(gè)庫是公用的,其中除了Camera還有其他方面的功能芜果。整個(gè)Camera在運(yùn)行的時(shí)候鞠呈,可以大致上分成Client和Server兩個(gè)部分,它們分別在兩個(gè)進(jìn)程中運(yùn)行右钾,它們之間使用Binder機(jī)制實(shí)現(xiàn)進(jìn)程間通訊蚁吝。這樣在client調(diào)用接口,功能則在server中實(shí)現(xiàn)舀射,但是在client中調(diào)用就好像直接調(diào)用server中的功能灭将,進(jìn)程間通訊的部分對上層程序不可見。

當(dāng)Camera Client端通過Binder機(jī)制與Camera Server端通訊后控,Server端的實(shí)現(xiàn)傳遞到Client端庙曙。而Server端的實(shí)現(xiàn)又是調(diào)用硬件接口來實(shí)現(xiàn)。

3.**** HAL層

這個(gè)層次其實(shí)就是用戶空間的驅(qū)動代碼浩淘。前面有介紹過框架層對下在CameraHardwareInterface.h頭文件中定義了Camera硬件抽象層的接口捌朴,它是包含純虛函數(shù)的類,必須被實(shí)現(xiàn)類繼承才能使用张抄。HAL層正好繼承CameraHardwareInterface接口砂蔽,依據(jù)V4l2規(guī)范實(shí)例化底層硬件驅(qū)動,使用ioctl方式調(diào)用驅(qū)動署惯,最終生成libcamera.so供框架的libcameraservice.so調(diào)用左驾。

這層的代碼在\android\hardware\XXX\libcamera目錄下(也有可能在vendor目錄中對應(yīng)的libcamera下)。注意這里的XXX是不同廠商為不同產(chǎn)品(板子)而建的目錄极谊,以高通msm平臺為例诡右,這里XXX用msm7k表示,這樣高通msm平臺下這個(gè)HAL的目錄即為\android\hardware\msm7k\libcamera轻猖。不難看出帆吻,如果要在某硬件平臺上運(yùn)行Android,也就主要在這一層進(jìn)行修改咙边,因?yàn)樗侵苯雍偷讓佑布?qū)動相關(guān)的猜煮。上面也講過,應(yīng)用框架層對上對下都定義的標(biāo)準(zhǔn)接口败许,這樣做的目的也就是使上層的代碼獨(dú)立王带,在porting中不受影響。所以我們現(xiàn)在可以基本確定市殷,如果要改Camera的硬件愕撰,框架層以上的部分都可以不動,要改就改HAL到內(nèi)核層的部分,這也是Android底層開發(fā)的主要工作盟戏。

4.內(nèi)核層

這一層主要是基于Linux的設(shè)備驅(qū)動绪妹。對Camera來說甥桂,一般是按V4l2規(guī)范將Camera原子功能以ioctl的形式暴露出來供HAL層調(diào)用的實(shí)現(xiàn)柿究。主要功能的實(shí)現(xiàn)代碼在\android\kernel\drivers\media\video\XXX下。跟HAL層目錄一樣黄选,XXX是不同廠商不同平臺的目錄蝇摸,以高通msm平臺為例,這個(gè)目錄就是\android\kernel\drivers\media\video\msm办陷。所以要在Android平臺上添加硬件功能貌夕,首先考慮將它的驅(qū)動加到Android的Linux內(nèi)核中。

5.**** Camera庫文件

前面已提到Camera模塊主要包含libandroid_runtime.so民镜、libcamera_client.s0(libui.so)啡专、libcameraservice.so和一個(gè)與Camera硬件相關(guān)的硬件庫libcamera.so。其中l(wèi)ibandroid_runtime.so制圈、libcamera_client.s0(libui.so)是與android系統(tǒng)構(gòu)架相關(guān)的们童, 不需要對進(jìn)行其修改,libameraservice.so和libcamera.so則是和硬件設(shè)備相關(guān)聯(lián)的鲸鹦,而cameralib.so實(shí)際上就是設(shè)備的linxu驅(qū)動慧库,所以Camera設(shè)備的系統(tǒng)繼承主要是通過移植CameraLinux驅(qū)動和修改libcameraservice.so庫來完成。

Libcameraservice.so構(gòu)建規(guī)則中使用宏USE_CAMERA_STUB決定 是否使用真的Camera馋嗜,如果宏為真齐板,則使用CameraHardwareStub.cpp和FakeCamera.cpp構(gòu)造一個(gè)假的Camera,如果為假則使用libcamera來構(gòu)造一個(gè)實(shí)際上的Camera服務(wù)葛菇。

在CameraHardwareStub.cpp中定義了CameraHardwareStub類甘磨,它繼承并實(shí)現(xiàn)了抽象類CameraHardwareInterface中定義的真正操作Camera設(shè)備的所有的純虛函數(shù)。通過openCameraHardware()將返回一個(gè)CameraHardwareInterface類的對象眯停,但由于CameraHardwareInterface類是抽象類所以它并不能創(chuàng)建對象宽档,而它的派生類CameraHardwareStub完全實(shí)現(xiàn)了其父類的 純虛函數(shù)所以openCameraHardware()返回一個(gè)指向派生類對象的基類指針用于底層設(shè)備的操作。由于CameraHardwareStub類定義的函數(shù)是去操作一個(gè)假的Camera庵朝,故通過openCameraHardware返回的指針主要用于仿真環(huán)境對Camera的模擬操作吗冤,要想通過openCameraHardware返回的指針操作真正的硬件設(shè)備則需完成以下步驟:

⑴CameraHardwareInterface類中的所有純虛函數(shù)的聲明改為虛函數(shù)的聲明(即去掉虛函數(shù)聲明后的“=0”);

⑵編寫一個(gè)源文件去定義CameraHardwareInterface類中聲明的所有虛函數(shù);

⑶編寫Android.mk文件用于生成一個(gè)包含步驟⑵編寫的源文件和其他相關(guān)文件的libcamera.so文件;

⑷將宏USE_CAMERA_STUB改成false,這樣生成libcameraservice.so時(shí)就會包含libcamera.so庫九府。(注:如果CameraHardwareInterface類的成員函數(shù)并沒有直接操作硬件而是調(diào)用Camera的linux驅(qū)動來間接對硬件操作椎瘟,那么包含這樣的CameraHardwareInterface類的libcamera.so庫就相當(dāng)于一個(gè)HAL)。

以上Camera的結(jié)構(gòu)層次可以簡單的概括成如下流程圖:

圖片.png

2.Camera HAL層分析

2.1 Camera HAL層的主要類介紹

首先看一下Camera HAL層的主要類關(guān)系圖:

圖片.png

1. Camera****硬件抽象層

frameworks/base/include/camera/

主要文件為CameraHardwareInterface.h,需要各個(gè)系統(tǒng)根據(jù)自己的情況實(shí)現(xiàn)侄旬。

2. Camera****服務(wù)部分

frameworks/base/services/camera/libcameraservice

Camera服務(wù)是Andriod系統(tǒng)中一個(gè)單獨(dú)的部分肺蔚,通過調(diào)用camera硬件抽象層

來實(shí)現(xiàn)。

3. Camera ****的本地框架代碼

frameworks/base/libs/camera/

4. Camera****的硬件抽象層

Camera的硬件抽象層是位于V4L2驅(qū)動程序和CameraService之間的部分儡羔,這是一個(gè)C++的接口類,需要具體的實(shí)現(xiàn)者繼承這個(gè)類宣羊,并實(shí)現(xiàn)其中的各個(gè)純虛函數(shù)璧诵。純虛函數(shù)可以讓類先具有一個(gè)操作名稱,而沒有操作內(nèi)容仇冯,讓派生類在繼承時(shí)再去具體地給出定義之宿。硬件抽象層的主要的頭文件為:

CameraHardwareInterface.h,定義了C++的接口類需要根據(jù)系統(tǒng)的情況繼承實(shí)現(xiàn)。

camera.h 這是Camera系統(tǒng)本地對上層的接口苛坚。

CameraParameters.h定義Camera系統(tǒng)的參數(shù)比被,在本地代碼的各個(gè)層次中使用。

Camera硬件抽象層的實(shí)現(xiàn)通常需要生成動態(tài)庫libcamera.so泼舱。

5. CameraHardwareInterface****中定義了幾種回調(diào)函數(shù)

typedef void (*notify_callback)(int32_t msgType, //通知回調(diào)

                            int32_t ext1,

                            int32_t ext2,

                            void* user);

typedef void (*data_callback)(int32_t msgType, //數(shù)據(jù)回調(diào)

                          const sp<IMemory>& dataPtr,

                          void* user);

typedef void (*data_callback_timestamp)(nsecs_t timestamp,//帶有時(shí)間的數(shù)據(jù)回調(diào)

                                    int32_t msgType,

                                    const sp<IMemory>& dataPtr,

                                    void* user);

消息類型mstType 數(shù)據(jù)IMemory等缀,回調(diào)函數(shù)由setCallbacks(),enableMsgType()函數(shù)統(tǒng)一處理。

setCallbacks()可以設(shè)置三個(gè)類型的回調(diào)函數(shù)指針

/** Set the notification and data callbacks */

virtual void setCallbacks(notify_callback notify_cb,

                          data_callback data_cb,

                          data_callback_timestamp data_cb_timestamp,

                          void* user) = 0;

2.2 ** Camera****硬件抽象層的三種業(yè)務(wù)**

2.2.1****取景器****preview(****使用****YUV****原始數(shù)據(jù)格式娇昙,發(fā)送到視頻輸出設(shè)備****)

CameraHardware.cpp中:

1.在初始化過程中窒舟,建立preview的內(nèi)存隊(duì)列

void CameraHardware::initHeapLocked() {

// Create raw heap.  

**int** picture_width, picture_height;  

mParameters.getPictureSize(&picture_width, &picture_height);  

mRawHeap = **new** MemoryHeapBase(picture_width * picture_height * 2);  

**int** preview_width, preview_height;  

mParameters.getPreviewSize(&preview_width, &preview_height);  

**int** how_big = preview_width * preview_height*2;  

}

void CameraHardware::initDefaultParameters() {

CameraParameters p;  

p.**set**(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, "320x240");  

p.setPreviewSize(320, 240);//大小為320*240  

p.setPreviewFrameRate(15);//幀率為15bps  

p.setPreviewFormat(CameraParameters::PIXEL_FORMAT_RGB565);//preview 的格式為RGB565  

p.**set**(CameraParameters::KEY_ROTATION, 0);//90  

p.**set**(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, "320x240");  

p.setPictureSize(320, 240);  

p.setPictureFormat(CameraParameters::PIXEL_FORMAT_JPEG);// SET OUTPUT PIC TO BMP FORMAT  

**if** (setParameters(p) != NO_ERROR) {  

    LOGE("Failed to set default parameters?!");  

}  

}

2.在startPreview()的實(shí)現(xiàn)中杂抽,建立preview線程

status_t CameraHardware::startPreview() {

Mutex::Autolock **lock**(mLock);  

**if** (mPreviewThread != 0) {  

      **return** INVALID_OPERATION;  

}  

mPreviewThread = **new** PreviewThread(**this**);  

**return** NO_ERROR;  

}

3.在preview線程的循環(huán)中,等待視頻數(shù)據(jù)的到達(dá)

int CameraHardware::previewThread() {

**if**(mCamType == CAMTYPE_CMOS)   

        Ov965xCamera->getNextFrameAsRgb565((uint16_t *)frame);  

4.視頻幀到達(dá)后使用preview回調(diào)的機(jī)制CAMERA_MSG_PREVIEW_FRAME,將視頻

幀向上層傳送

// Notify the client of a new frame.

     **if** (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME)  

           mDataCb(CAMERA_MSG_PREVIEW_FRAME, buffer, mCallbackCookie);  

**2.2.2****拍攝照片(可以使用原始數(shù)據(jù)或者壓縮圖像數(shù)據(jù)) **

1.takePicture()函數(shù)表示開始拍攝,可以建立單獨(dú)的線程來處理

status_t CameraHardware::takePicture() {

stopPreview();  

**if** (createThread(beginPictureThread, **this**) == **false**)  

      **return** UNKNOWN_ERROR;  

**return** NO_ERROR;  

}

int CameraHardware::beginPictureThread(void *cookie) {

CameraHardware *c = (CameraHardware *)cookie;  

**return** c->pictureThread();  

}

int CameraHardware::pictureThread() {

**if** (mMsgEnabled & CAMERA_MSG_SHUTTER)  

      mNotifyCb(CAMERA_MSG_SHUTTER, 0, 0, mCallbackCookie);  

**if** (mMsgEnabled & CAMERA_MSG_RAW_IMAGE) {  

    //FIXME: use a canned YUV image!  

    // In the meantime just make another fake camera picture.  

    **int** w, h;  

    mParameters.getPictureSize(&w, &h);  

    sp<MemoryBase> mem = **new** MemoryBase(mRawHeap, 0, w * h * 2);  

    Ov965xCamera cam(w, h);  

    //cam.getNextFrameAsYuv420((uint8_t *)mRawHeap->base());  

    **if**(mCamType == CAMTYPE_CMOS)   {  

      cam.getNextFrameAsRgb565((uint16_t *)mRawHeap->**base**());   

    } **else** **if** (mCamType == CAMTYPE_USB)   {  

     //USBCamera cam1(w, h);  

    LOGE("%s, Taking picure using USB CAM", LOG_TAG);  

    //cam.getNextFrameAsRgb565((uint16_t *)mRawHeap->base()); 

    //cam1.getNextFrameAsYuv420((uint16_t *)mRawHeap->base());   

    }  

    mDataCb(CAMERA_MSG_RAW_IMAGE, mem, mCallbackCookie);  

}  

// ToDo: Release MemoryHeapBase  

// ToDo: Convert BMP to JPEG  

// Todo: Higher Resultion Support  

**if** (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) {  

LOGE("%s, COMPRESSED IMAGE", LOG_TAG);  

    **int** w, h;  

unsigned **int** DATA_OFFSET = 54;  

    uint16_t WIDTH = w;  

uint16_t HEIGHT = h;  

    mParameters.getPictureSize(&w, &h);  

Ov965xCamera* Ov965xCamera = mOv965xCamera;   

sp<MemoryHeapBase> heap = **new** MemoryHeapBase(DATA_OFFSET+w * h* 2);  

sp<MemoryBase> mem = **new** MemoryBase(heap, 0, DATA_OFFSET+w * h* 2); //16 bits for one pixel  

    uint8_t header[54] = { 0x42, // identity : B  

    0x4d, // identity : M  

    0, 0, 0, 0, // file size  

    0, 0, // reserved1  

    0, 0, // reserved2  

    54, 0, 0, 0, // RGB data offset  

    40, 0, 0, 0, // struct BITMAPINFOHEADER size  

    0, 0, 0, 0, // bmp height  

    0, 0, 0, 0, // bmp width  

    1, 0, // planes  

    16, 0, // bit per pixel  

    0, 0, 0, 0, // compression  

    0, 0, 0, 0, // data size  

    0, 0, 0, 0, // h resolution  

    0, 0, 0, 0, // v resolution  

    0, 0, 0, 0, // used colors  

    0, 0, 0, 0 // important colors  

    };  

    // file size offset 54  

uint16_t file_size = WIDTH * HEIGHT * 2 + DATA_OFFSET;  

header[2] = (uint8_t)(file_size & 0x000000ff);  

header[3] = (file_size >> 8) & 0x000000ff;  

header[4] = (file_size >> 16) & 0x000000ff;  

header[5] = (file_size >> 24) & 0x000000ff;  

// height  

header[18] = HEIGHT & 0x000000ff;  

header[19] = (HEIGHT >> 8) & 0x000000ff;  

header[20] = (HEIGHT >> 16) & 0x000000ff;  

header[21] = (HEIGHT >> 24) & 0x000000ff;  

// width  

header[22] = WIDTH & 0x000000ff;  

header[23] = (WIDTH >> 8) & 0x000000ff;  

header[24] = (WIDTH >> 16) & 0x000000ff;  

header[25] = (WIDTH >> 24) & 0x000000ff;  

LOGE("%s, Header Ready", LOG_TAG);  

unsigned **int** i;  

**for**(i=0;i<DATA_OFFSET;i++){  

    *((uint8_t*)heap->**base**()+i)=header[i];  

}  

Ov965xCamera->getNextFrameAsRgb565((uint16_t*)heap->**base**()+DATA_OFFSET/2);  

uint16_t *heap_base = (uint16_t*)heap->**base**();  

uint16_t pixel_data;  

uint8_t tail_data;  

**for**(i=DATA_OFFSET/2;i<DATA_OFFSET/2+WIDTH*HEIGHT;i++){  

  pixel_data = *(heap_base+i);  

  tail_data = (uint8_t)(pixel_data & 0x001f);  

  pixel_data = (pixel_data & 0xffc0)>>1 | tail_data;  

  *(heap_base+i)=pixel_data;  

}  

mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mem, mCallbackCookie);

heap=NULL;  

LOGE("%s, IMAGE SAVED!", LOG_TAG);  

}  

**return** NO_ERROR;  

}

2.使用回調(diào)機(jī)制傳送數(shù)據(jù)

如果得原始格式(通常是YUV的格式捺弦,如yuv422sp摩幔,這里是RGB565)的數(shù)據(jù)川陆,使用CAMERA_MSG_RAW_IMAGE將數(shù)據(jù)傳送;如果得到壓縮圖像(通常JPEG格式督暂,這里是BMP)使用CAMERA_MSG_COMPRESSED_IMAGE將數(shù)據(jù)傳送。

2.2.3****視頻錄制(將數(shù)據(jù)傳送給視頻編碼器程序)

1.在startRecording()的實(shí)現(xiàn)中皇拣,開始錄制的準(zhǔn)備严蓖,錄制視頻可以使用自己的線程,也可以使用preview線程氧急。

2.當(dāng)一個(gè)視頻幀到來的時(shí)候颗胡,通過錄制回調(diào)機(jī)制(使用CAMERA_MSG_VIDEO_FRAME)將視頻幀向上發(fā)送。

3.releaseRecordingFrame()被調(diào)用后吩坝,表示上層通知Camera硬件抽象層毒姨,這一幀的內(nèi)存已經(jīng)用完,可以進(jìn)行下一次的處理钉寝。

3.Camera Framework層分析

3.1 Camera Framwork整體架構(gòu)和類關(guān)系簡介

現(xiàn)在我們嘗試從最開始的啟動流程來熟悉android camera的整體framework流程弧呐。Camera預(yù)覽的數(shù)據(jù)流程如下:

圖片.png

主要類的繼承關(guān)系圖如下:

圖片.png

首先從上圖的各個(gè)步驟來逐一分析流程,后續(xù)會根據(jù)具體的一些點(diǎn)進(jìn)行內(nèi)容的添加和擴(kuò)充嵌纲。

1.****Camera.java

packages/apps/camera/src/com/android/

最上層的應(yīng)用就是從這個(gè)文件開始俘枫。

該文件集中了整個(gè)android上層應(yīng)用的所有相關(guān)內(nèi)容,當(dāng)然更多的則為界面的代碼實(shí)現(xiàn)逮走。如果出現(xiàn)了camera應(yīng)用界面的問題(當(dāng)然除了camera拍攝區(qū)域內(nèi)容外)鸠蚪,可以從android的代碼入手。

2.****Camera.java

frameworks\base\core\java\android\hardware\

該文件中主要是對native函數(shù)接口的調(diào)用茅信,當(dāng)然也包括一些本地的函數(shù)實(shí)現(xiàn)盾舌。也可以認(rèn)為該文件是實(shí)現(xiàn)了從java層調(diào)用c++層代碼函數(shù)接口。也就是我們需要去了解的一點(diǎn)JNI機(jī)制蘸鲸。

3.****android_hardware_Camera.cpp

該文件就是JNI的c++層的代碼實(shí)現(xiàn)妖谴。通過camera的類實(shí)例來調(diào)用camera類的相關(guān)接口。

4.****Camera.cpp/Camera.h

對于上層應(yīng)用來說棚贾,camera.cpp是最為直接的函數(shù)調(diào)用和實(shí)現(xiàn)窖维。繼承于ICameraClient類,典型的Client端的接口實(shí)例榆综。

5.****BnCameraClient/BpCameraClient

IPC通訊所需的函數(shù)接口實(shí)現(xiàn)妙痹,繼承于ICameraClient類。

6.****ICameraClient.cpp/ICameraClient.h

Client/Service模式下的Client端實(shí)現(xiàn)

7.****ICameraService.cpp/ICameraService.h

Client/Service模式下service端實(shí)現(xiàn)

8.**** BnCameraService/BpCameraService

IPC通訊所需的函數(shù)接口實(shí)現(xiàn)鼻疮,繼承于ICameraService類怯伊。

9.****CameraService.cpp/CameraService.h

繼承于BnCameraService類。是對BnCameraService函數(shù)接口的實(shí)現(xiàn)判沟,其本質(zhì)也是對CameraService的內(nèi)部類Client函數(shù)接口的調(diào)用耿芹。

10.****Client(CameraService****內(nèi)部類****)

該類才是真正的底層函數(shù)實(shí)現(xiàn),其通過openCameraHardware()得到camera硬件實(shí)例對象進(jìn)行操作挪哄。其繼承于ICamera類,是對ICamera類函數(shù)接口的實(shí)現(xiàn)吧秕。

3.2 Camera Framwork層代碼調(diào)用流程分析

接下來,我們通過對流程的步步分析來將camera整體串接起來:

3.2.1應(yīng)用層****onCreate****函數(shù)入口

首先則看看camera.java的 onCreate函數(shù)入口迹炼,針對android的所有應(yīng)用砸彬,onCreate函數(shù)入口作為跟蹤和了解應(yīng)用架構(gòu)的首選。

public void onCreate(Bundle icicle) {

    super.onCreate(icicle);

    devlatch = new CountDownLatch(1);

CountDownLatch()關(guān)于這個(gè)類斯入,可以簡單的理解為它是用來線程之間的等待處理砂碉,當(dāng)然這里采用的計(jì)數(shù)為1,則可以簡單理解為一個(gè)計(jì)數(shù)開關(guān)來控制調(diào)用了tlatch.await()函數(shù)的進(jìn)程刻两,方式就是將devlatch的計(jì)數(shù)減為0(countDown() )增蹭。

這里啟動了一個(gè)線程用來打開camera服務(wù),而打開過程則比較費(fèi)時(shí)(一般在2s左右)磅摹,故單獨(dú)啟用一個(gè)線程避免應(yīng)用線程阻塞滋迈。

Thread startPreviewThread = new Thread(new Runnable() {

        CountDownLatch tlatch = devlatch;

        public void run() {

            try {

                mStartPreviewFail = false;

                ensureCameraDevice();

                // Wait for framework initialization to be complete before

                // starting preview

                try {

                    tlatch.await();

                } catch (InterruptedException ie) {

                    mStartPreviewFail = true;

                }

                startPreview();

            } catch (CameraHardwareException e) {

                // In eng build, we throw the exception so that test tool

                // can detect it and report it

                if ("eng".equals(Build.TYPE)) {

                      throw new RuntimeException(e);

                }

                mStartPreviewFail = true;

            }

        }

    });

    startPreviewThread.start();

在這里,需要跟進(jìn)ensureCameraDevice();該函數(shù)户誓,可以看到其實(shí)現(xiàn)為:

private void ensureCameraDevice() throws CameraHardwareException {

    if (mCameraDevice == null) {

        mCameraDevice = CameraHolder.instance().open();

        mInitialParams = mCameraDevice.getParameters();

    }

}

當(dāng)前mCameraDevice()實(shí)例為null,則會調(diào)用CameraHolder.instance().open()函數(shù)來創(chuàng)建mCameraDevice對象實(shí)例饼灿。

private android.hardware.Camera mCameraDevice;

跟進(jìn)CameraHolder.instance().open(),進(jìn)入到了CameraHolder類中:

public synchronized android.hardware.Camera open()

        throws CameraHardwareException {

          Assert(mUsers == 0);

    if (mCameraDevice == null) {

        try {

            mCameraDevice = android.hardware.Camera.open();

        } catch (RuntimeException e) {

            Log.e(TAG, "fail to connect Camera", e);

            throw new CameraHardwareException(e);

        }

        mParameters = mCameraDevice.getParameters();

    } else {

……

下面大概介紹下我對CameraHolder的理解:

1.CameraHolder對mCameraDevice實(shí)例進(jìn)行短暫的保留(keep()函數(shù)中可以設(shè)定這個(gè)保留時(shí)長,一般默認(rèn)為3000ms)厅克,避免用戶在短暫退出camera又重新進(jìn)入時(shí)赔退,縮短camera啟動時(shí)長(正如之前所說,打開CameraDevice時(shí)間較長)

2.CameraHolder并有一個(gè)關(guān)鍵的計(jì)數(shù)mUsers用來保證open()和release()的配套調(diào)用,避免多次重復(fù)釋放或者打開(上層應(yīng)用的保護(hù)措施之一)硕旗。

3.2.2 Framework層的open方法

第一步的完成窗骑,進(jìn)而跳轉(zhuǎn)到了android.hardware.Camera類中的open()函數(shù)接口調(diào)用。

public static Camera open() {

return new Camera();

}

靜態(tài)函數(shù)漆枚,也就可以通過類名直接調(diào)用创译,open()函數(shù)中去創(chuàng)建一個(gè)Camera的實(shí)例。

Camera() {

    mShutterCallback = null;

    mRawImageCallback = null;

    mJpegCallback = null;

    mPreviewCallback = null;

    mPostviewCallback = null;

    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;

    }

    native_setup(new WeakReference<Camera>(this));

}

在Camera構(gòu)造函數(shù)中有這個(gè)關(guān)鍵的一步,最開始的一些callback可以認(rèn)為它們最終被底層調(diào)用到(至于具體流程后面會講到)墙基。EventHandler和Looper我們暫時(shí)跳過软族,知道它是消息處理就行了。最后也就是最為關(guān)鍵的函數(shù)接口調(diào)用:native_setup

private native final void native_setup(Object camera_this);

典型的native函數(shù)接口聲明残制,說明并非camera類的本地函數(shù)實(shí)現(xiàn)立砸,也就意味著會通過JNI(Java Native Interface)調(diào)用對用C++文件中的函數(shù)接口。

3.2.3 JNI層的調(diào)用

跳轉(zhuǎn)到android_hardware_Camera.cpp中尋找native_setup()所對應(yīng)的JNI函數(shù)接口:

   static JNINativeMethod camMethods[] = {

{ "native_setup",

"(Ljava/lang/Object;)V",

(void*)android_hardware_Camera_native_setup },

{ "native_release",

"()V",

(void*)android_hardware_Camera_release },

{ "setPreviewDisplay",

"(Landroid/view/Surface;)V",

(void *)android_hardware_Camera_setPreviewDisplay },

     ……

而camMethods[]在什么時(shí)候映射的那初茶?繼續(xù)看:

int register_android_hardware_Camera(JNIEnv *env) {

…..

// Register native functions

return AndroidRuntime::registerNativeMethods(env,

"android/hardware/Camera",

camMethods,NELEM(camMethods));

}

最終在AndroidRuntime.cpp中被調(diào)用:

REG_JNI(register_android_hardware_Camera),

說明如果我們自己要添加JNI接口實(shí)現(xiàn)的話颗祝,這些地方也需要添加相應(yīng)的代碼(具體在AndroidRuntime.cpp的細(xì)節(jié)我沒深看,也不做介紹)恼布。

簡單介紹: JNINativeMethod的第一個(gè)成員是一個(gè)字符 串螺戳,表示了JAVA本地調(diào)用方法的名稱,這個(gè)名稱是在JAVA程序中調(diào)用的名稱折汞;第二個(gè)成員也是一個(gè)字符串倔幼,表示JAVA本地調(diào)用方法的參數(shù)和返回值;第三個(gè)成員是JAVA本地調(diào)用方法對應(yīng)的C語言函數(shù)爽待。

跟進(jìn)觀察android_hardware_Camera_native_setup()函數(shù)的實(shí)現(xiàn):

// connect to camera service

static void android_hardware_Camera_native_setup(JNIEnv *env

, jobject thiz, jobject weak_this) {

sp<Camera> camera = Camera::connect();

if (camera == NULL) {

    jniThrowException(env, "java/lang/RuntimeException",

                      "Fail to connect to camera service");

    return;

}

….

}

初步可以認(rèn)為Camera::connect()的函數(shù)調(diào)用時(shí)返回了一個(gè)Camera 的實(shí)例對象损同。

3.2.4 Camera connect到服務(wù)

通過上述的跟進(jìn)流程來到了針對上層應(yīng)用而言最為直接的類:camera.cpp:

對Camera::connect函數(shù)的調(diào)用如下:

sp<Camera> Camera::connect(){

    LOGV("connect");

    sp<Camera> c = new Camera();

    const sp<ICameraService>& cs = getCameraService();

    if (cs != 0) {

        c->mCamera = cs->connect(c);

    }

    if (c->mCamera != 0) {

        c->mCamera->asBinder()->linkToDeath(c);

        c->mStatus = NO_ERROR;

    } else {

        c.clear();

    }

    return c;

}

首先是創(chuàng)建一個(gè)camera對象實(shí)例,然后通過調(diào)用getCameraService()去取得ICameraService的服務(wù)實(shí)例:

// establish binder interface to camera service

const sp<ICameraService>& Camera::getCameraService(){

Mutex::Autolock _l(mLock);

if (mCameraService.get() == 0) {

    sp<IServiceManager> sm = defaultServiceManager();

    sp<IBinder> binder;

    do {

        binder = sm->getService(String16("media.camera"));

        if (binder != 0)

           break;

        LOGW("CameraService not published, waiting...");

        usleep(500000); // 0.5 s

    } while(true);

    if (mDeathNotifier == NULL) {

        mDeathNotifier = new DeathNotifier();

    }

    binder->linkToDeath(mDeathNotifier);

    mCameraService = interface_cast<ICameraService>(binder);

}

LOGE_IF(mCameraService==0, "no CameraService!?");

return mCameraService;

}

這邊就涉及到了ServiceManager()對服務(wù)的管理堕伪,在這之前Camera的服務(wù)已經(jīng)注冊到了ServiceManager中揖庄,我們可以通過服務(wù)字串(media.camera)來獲得camera service(其本質(zhì)得到的是CameraService的實(shí)例對象,雖然通過類型上溯轉(zhuǎn)換成父類ICameraService欠雌,對ICameraService對象的函數(shù)調(diào)用本質(zhì)是調(diào)用到了CameraService的函數(shù)實(shí)現(xiàn))蹄梢。

在得到camera service后,返回之前的步驟:當(dāng)?shù)玫降腸s即cameraservice實(shí)例存在時(shí)富俄,通過調(diào)用cs->connect(c)去得到ICamera實(shí)例北戏,并賦值給了camera實(shí)例的一個(gè)類成員ICamera mCamera:

if (cs != 0) {

c->mCamera = cs->connect(c);

3.2.5 Camera服務(wù)之Binder代理端

接下來則涉及到ICamraService的相關(guān)調(diào)用關(guān)系查坪,其實(shí)這個(gè)地方需要去弄清楚一些函數(shù)接口的實(shí)現(xiàn)在具體哪些文件中无午,因?yàn)榇嬖谳^多的虛函數(shù)瘪松。

繼續(xù)流程,上一步走到了cs->connect()悠瞬,也就是ICameraService的connect()函數(shù)接口们豌。

class ICameraService : public IInterface{

public:

enum {

    CONNECT = IBinder::FIRST_CALL_TRANSACTION,

};

public:

DECLARE_META_INTERFACE(CameraService);

virtual sp<ICamera>     connect(const sp<ICameraClient>& cameraClient) = 0;

};

可以發(fā)現(xiàn)該connect()接口為一個(gè)純虛函數(shù)涯捻,需要ICameraService的子類對該接口進(jìn)行實(shí)現(xiàn),從而對connect()的調(diào)用則會映射到ICameraService子類的具體實(shí)現(xiàn)望迎。

關(guān)于ICameraService的實(shí)例問題障癌,目前暫時(shí)跳過(后面馬上就會講到),簡單認(rèn)為這個(gè)時(shí)候會調(diào)用到其一個(gè)子類的實(shí)現(xiàn):

class BpCameraService: public BpInterface<ICameraService>{

public:

BpCameraService(const sp<IBinder>& impl)

    : BpInterface<ICameraService>(impl) {

}

// connect to camera service

virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient){

    Parcel data, reply;

    data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());

    data.writeStrongBinder(cameraClient->asBinder());

    remote()->transact(BnCameraService::CONNECT, data, &reply);

    return interface_cast<ICamera>(reply.readStrongBinder());

}

};

BpCameraService為代理類辩尊,其主要用途為Binder通訊機(jī)制即進(jìn)程間的通訊(Client/Service)涛浙,最終還是會調(diào)用BnCameraService的具體實(shí)現(xiàn),即:

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

        sp<ICamera> camera = connect(cameraClient);

        reply->writeStrongBinder(camera->asBinder());

        return NO_ERROR;

    } break;

    default:

        return BBinder::onTransact(code, data, reply, flags);

}

}

而BnCameraService(為實(shí)現(xiàn)類)類繼承于ICameraService摄欲,并且也并沒有對connect()純虛函數(shù)進(jìn)行了實(shí)現(xiàn)轿亮,同樣意味著其實(shí)該調(diào)用的實(shí)質(zhì)是BnCameraService的子類實(shí)現(xiàn)。

畢竟虛函數(shù)的調(diào)用沒有實(shí)例肯定是沒有意義的胸墙,說明我們需要找到對connect()純虛函數(shù)的實(shí)現(xiàn)子類即繼承于BnCameraService我注。

3.2.6 Camera服務(wù)之****CameraService****分析

結(jié)合上面所述,可以尋找到了繼承于BnCameraService的子類CameraService.cpp:

這時(shí)雖然找到了CameraService該類劳秋,但是你肯定會問到該類實(shí)例的創(chuàng)建在什么地方哪仓手?再后頭看CameraService啟動注冊的地方:

int main(int argc, char** argv) {

sp<ProcessState> proc(ProcessState::self());

sp<IServiceManager> sm = defaultServiceManager();

LOGI("ServiceManager: %p", sm.get());

AudioFlinger::instantiate();

MediaPlayerService::instantiate();

CameraService::instantiate();

AudioPolicyService::instantiate();

ProcessState::self()->startThreadPool();

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

}

這個(gè)main函數(shù)位于main_mediaserver.cpp中胖齐,而mediaserver是在系統(tǒng)開始的時(shí)候就啟動起來的server端(MediaServer玻淑,在系統(tǒng)啟動時(shí)由init所啟動,具可參考init.rc文件)呀伙,進(jìn)而將相關(guān)的服務(wù)也創(chuàng)建了實(shí)例补履。

跟進(jìn)CameraService::instantiate()函數(shù)實(shí)現(xiàn),可以發(fā)現(xiàn):

void CameraService::instantiate() {

defaultServiceManager()->addService(

        String16("media.camera"), new CameraService());

}

創(chuàng)建了一個(gè)CameraService實(shí)例 剿另,并給定了CameraService的服務(wù)字串為”media.camera”箫锤,而之前在通過ServiceManager獲取CameraService的時(shí)候,所調(diào)用的接口為binder = sm->getService(String16("media.camera"));雨女,兩者保持了一樣的字符串谚攒。

if (mCameraService.get() == 0) {

    sp<IServiceManager> sm = defaultServiceManager();

    sp<IBinder> binder;

    do {

        binder = sm->getService(String16("media.camera"));

        if (binder != 0)

              break;

        LOGW("CameraService not published, waiting...");

        usleep(500000); // 0.5 s

    } while(true);

    if (mDeathNotifier == NULL) {

        mDeathNotifier = new DeathNotifier();

    }

    binder->linkToDeath(mDeathNotifier);

    mCameraService = interface_cast<ICameraService>(binder);

}

結(jié)合上述分析,此處的binder對象其實(shí)為CameraService類實(shí)例(多態(tài)類型轉(zhuǎn)換)氛堕。

interface_cast<ICameraService>(binder)宏映射馏臭,需要展開:

template<typename INTERFACE>

inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj) {

  return INTERFACE::asInterface(obj);

}

INTERFACE::asInterface(obj);宏映射,繼續(xù)展開可得:

sp<I##INTERFACE> I##INTERFACE::asInterface(const sp<IBinder>& obj) \

{ \

    sp<I##INTERFACE> intr;                                        \

    if (obj != NULL) {                                              \

        intr = static_cast<I##INTERFACE*>(                           \

              obj->queryLocalInterface(                               \

                      I##INTERFACE::descriptor).get());                 \

        if (intr == NULL) {                                          \

              intr = new Bp##INTERFACE(obj);                          \

        }                                                       \

    }                                                           \

    return intr;                                                   \

}

(其上的宏展開都是在IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService");中實(shí)現(xiàn)的)

此處又創(chuàng)建了一個(gè)BpCameraService(new Bp##INTERFACE)對象并將binder對象(obj)傳入到BpCameraService的構(gòu)造函數(shù)中讼稚。

雖然獲取的時(shí)候通過多態(tài)將CameraService實(shí)例轉(zhuǎn)換成了BnCameraService 也進(jìn)一步解釋了為什么ICameraService子類BnCameraservice中的connect函數(shù)實(shí)質(zhì)會調(diào)用到CameraService中函數(shù)實(shí)現(xiàn)了括儒。

于是就調(diào)用到了CameraService的connect函數(shù)接口:

sp<ICamera> CameraService::connect(const sp<ICameraClient>& cameraClient) {

…..

// create a new Client object

client = new Client(this, cameraClient, callingPid);

mClient = client;

if (client->mHardware == NULL) {

    client = NULL;

    mClient = NULL;

    return client;

}

創(chuàng)建了一個(gè)Client實(shí)例對象,并將該實(shí)例對象賦值給CameraSevice的類成員mClient,方便其實(shí)函數(shù)接口對Client的調(diào)用锐想。

在這之前需要提及它的一個(gè)內(nèi)部類Client帮寻,該類才是最為關(guān)鍵的函數(shù)實(shí)現(xiàn),CameraService的一些接口都會調(diào)用到其Client實(shí)例的具體函數(shù)。

3.2.7 Camera服務(wù)之Client分析

那么現(xiàn)在的關(guān)鍵就是Client類了赠摇。進(jìn)一步跟進(jìn):

CameraService::Client::Client(const sp<CameraService>& cameraService,

    const sp<ICameraClient>& cameraClient, pid_t clientPid) {

…..

mCameraService = cameraService;

mCameraClient = cameraClient;

mClientPid = clientPid;

mHardware = openCameraHardware();

}

將cameraService和cameraClient的實(shí)例分別賦值給了Client的類成員變量固逗。

另外openCameraHardware()是值得注意的地方浅蚪,也就是連接上層應(yīng)用和底層驅(qū)動的關(guān)鍵,通過調(diào)用openCameraHardware()得到了一個(gè)CameraHardwareInterface實(shí)例對象烫罩,并賦值給自己的類成員:

sp<CameraHardwareInterface> mHardware;

對hardware的操作就是通過該對象完成的掘鄙,所以說真正意義上的功能實(shí)現(xiàn)其實(shí)就是在這里,即client類的函數(shù)接口調(diào)用嗡髓。

對于hardware的東東咱們暫時(shí)不去關(guān)注吧操漠。

那么我們再次仔細(xì)研究下Client類的繼承關(guān)系(這些繼承關(guān)系很容易混亂,涉及到較多的多態(tài)類型轉(zhuǎn)換)饿这,這個(gè)其實(shí)往往都很關(guān)鍵:

圖片.png

Client繼承于BnCamera浊伙,而BnCamera則繼承于ICamera,也就是說Client繼承了ICamera,實(shí)現(xiàn)了ICamera中的函數(shù)长捧。

進(jìn)而發(fā)現(xiàn)嚣鄙,原來繞一個(gè)大圈,把最開始的圖簡化下:

圖片.png

流程圖可以概括如下:

圖片.png

3.2.8 Camera服務(wù)之Callback分析

先從單一函數(shù)去跟進(jìn)串结,看具體一些callback的實(shí)現(xiàn)流程:

// callback from camera service

void Camera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2){

sp<CameraListener> listener;

{

    Mutex::Autolock _l(mLock);

    listener = mListener;

}

if (listener != NULL) {

    listener->notify(msgType, ext1, ext2);

}

}

這是Camera類中一個(gè)callback函數(shù)實(shí)現(xiàn)哑子,但其本質(zhì)在哪?先看camera類的繼承關(guān)系:

圖片.png

通過以上的繼承關(guān)系肌割,繼續(xù)跟進(jìn)其父類ICameraClient:

class ICameraClient: public IInterface{

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) = 0;

virtual void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& data) = 0;

};

其中notifyCallback()又是純虛函數(shù),則同樣說明實(shí)現(xiàn)在其子類BpCameraClient中:

// generic callback from camera service to app

void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {

    LOGV("notifyCallback");

    Parcel data, reply;

    data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor());

    data.writeInt32(msgType);

    data.writeInt32(ext1);

    data.writeInt32(ext2);

  remote()->transact(NOTIFY_CALLBACK,data, &reply, IBinder::FLAG_ONEWAY);

}

然后通過Binder通訊調(diào)用到BnCameraClient中實(shí)現(xiàn):

status_t BnCameraClient::onTransact(

uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {

switch(code) {

    case NOTIFY_CALLBACK: {

        LOGV("NOTIFY_CALLBACK");

        CHECK_INTERFACE(ICameraClient, data, reply);

        int32_t msgType = data.readInt32();

        int32_t ext1 = data.readInt32();

        int32_t ext2 = data.readInt32();

        notifyCallback(msgType, ext1, ext2);

        return NO_ERROR;

    } break;

               ….

}

進(jìn)而調(diào)用到了Camera.cpp中的函數(shù)實(shí)現(xiàn)了卧蜓,但或許你有疑問,這些callback是涉及到一些驅(qū)動的callback把敞,哪怎么跟驅(qū)動聯(lián)系起來那弥奸?

結(jié)合之前對hardware接口調(diào)用的類Client,進(jìn)一步可以發(fā)現(xiàn)callback的處理同樣是在Client類實(shí)例化的時(shí)候:

CameraService::Client::Client(const sp<CameraService>& cameraService,

    const sp<ICameraClient>& cameraClient, pid_t clientPid) {

     …..

     mHardware->setCallbacks(notifyCallback,

                           dataCallback,

                           dataCallbackTimestamp,

                           mCameraService.get());

…..

}

調(diào)用了mHardware將callback傳入奋早,但此處的notifyCallback并不是camera.cpp中的函數(shù)盛霎,而是client類的notifyCallback函數(shù)。

再繼續(xù)看client類中的notifyCallback函數(shù)實(shí)現(xiàn):

void CameraService::Client::notifyCallback(int32_t msgType

, int32_t ext1,int32_t ext2, void* user){

       …..

    default:

        sp<ICameraClient> c = client->mCameraClient;

        if (c != NULL) {

              c->notifyCallback(msgType, ext1, ext2);

        }

        break;

     …..

}

通過得到ICameraClient實(shí)例進(jìn)而調(diào)用到了具體的對象Camera的notifyCallback()函數(shù)耽装。這個(gè)地方估計(jì)會遇見跟ICameraService函數(shù)調(diào)用一樣的問題愤炸,ICameraClient函數(shù)調(diào)用所需要的函數(shù)實(shí)例在哪?

記得上述ICameraService講到的connect()函數(shù)嘛掉奄?其中有一個(gè)參數(shù)不能被忽略掉的规个,就是ICameraClient,但它在真正傳入的時(shí)候卻是一個(gè)ICameraClient子類camera的實(shí)例對象挥萌。

CameraService:

sp<ICamera> CameraService::connect(const sp<ICameraClient>& cameraClient) {

               …..

     // create a new Client object

         client = new Client(this, cameraClient, callingPid);

               …..

}

Client:

CameraService::Client::Client(const sp<CameraService>& cameraService,

    const sp<ICameraClient>& cameraClient, pid_t clientPid){

….

mCameraService = cameraService;

mCameraClient = cameraClient;

….

}

這樣就清楚了绰姻,其實(shí)Client在調(diào)用設(shè)置callback的調(diào)用最終還是調(diào)用到了camera.cpp中的callback函數(shù),進(jìn)而將具體內(nèi)容通過callback反饋給上層應(yīng)用做出相應(yīng)的處理引瀑。

4.Camera 應(yīng)用層對Framework的封裝分析

4.1 應(yīng)用層對Framework層封裝的層次和類介紹

Android 4.0版本以前的camera在是camera 主activity中直接調(diào)用camera hal層的的接口(如android.hardware.camera.open(), android.hardware.camera.setPreviewDisplay(), android.hardware.camera..startPreview()等)與camera device通信狂芋。Android4.4之后的版本camera app對camera device的訪問又封裝了一次,對camera device的訪問必須經(jīng)過camera manager接口憨栽,camera manager接口定義了camera基本操作帜矾,這樣便于控制和管理camera device翼虫。下圖為相關(guān)類關(guān)系圖。

圖片.png

1.****CameraManager****接口

提供的訪問camera設(shè)備基本的操作屡萤,實(shí)現(xiàn)類必須調(diào)用CameraManager.cameraOpen獲取CameraManager.CameraProxy接口對象來控制camera珍剑,實(shí)現(xiàn)CameraManager接口的類必須擁有一個(gè)獨(dú)立于主線程的線程來實(shí)現(xiàn)對camera的操作,CameraManager接口也包裝回調(diào)函數(shù)死陆。

2.****CameraProxy****接口

封裝在CameraManager接口之內(nèi)招拙,接收對camera操作請求,發(fā)送消息到camer handle措译。所有對camera的操作都經(jīng)由改接口别凤,進(jìn)行異步處理。

3.****AndroidCameraManagerImpl****類

該類實(shí)現(xiàn)了CameraManager接口领虹,與camera framework層接口直接通信规哪,起到camera app 與camera framework對話中介的作用蚓再。

4.****AndroidCameraProxyImpl****類

AndroidCameraManagerImpl內(nèi)部類艺糜,實(shí)現(xiàn)CameraManager.CameraProxy接口,CameraProxy已經(jīng)介紹過了就是控制對camera的訪問缴允,AndroidCameraProxyImpl類實(shí)現(xiàn)了具體的操作最疆。

5.****CameraManagerFactory****類

這個(gè)類的實(shí)現(xiàn)很簡單杯巨,看到Factory,會想到軟件設(shè)計(jì)中的工廠模式肚菠,這里就是封裝了創(chuàng)建CameraManager對象的細(xì)節(jié)舔箭。

6.**** CameraHolder****類

看名字就可以看出這個(gè)類的基本功能了,就用來保存camera實(shí)力對象的蚊逢,用在不同的module之間快速切換。

4.2 camera的調(diào)用流程

時(shí)序圖如下

圖片.png

1.****CameraHolder .****instance()

public static synchronized CameraHolder instance() {

    if (sHolder == null) {

        sHolder = new CameraHolder();

    }

    return sHolder;

}

這個(gè)函數(shù)定義在Camera/src/com/android/cameraCameraHolder.java中

這個(gè)函數(shù)很簡單就是創(chuàng)建一個(gè)CameraHolder實(shí)例箫章,并保存在變量sHolder中烙荷,如果是第一次調(diào)用sHolder肯定是null,接著new 一個(gè)CameraHolder實(shí)例對象檬寂,這樣的設(shè)計(jì)在android中經(jīng)常見到终抽,一種單例設(shè)計(jì)模式。現(xiàn)在看看CameraHolder都做了些什么:

private CameraHolder() {

    HandlerThread ht = new HandlerThread("CameraHolder");

    ht.start();

    mHandler = new MyHandler(ht.getLooper());

    if (mMockCameraInfo != null) {

        mNumberOfCameras = mMockCameraInfo.length;

        mInfo = mMockCameraInfo;

    } else {

        mNumberOfCameras = android.hardware.Camera.getNumberOfCameras();

        mInfo = new CameraInfo[mNumberOfCameras];

        for (int i = 0; i < mNumberOfCameras; i++) {

            mInfo[i] = new CameraInfo();

            android.hardware.Camera.getCameraInfo(i, mInfo[i]);

        }

    }

    // get the first (smallest) back and first front camera id

    for (int i = 0; i < mNumberOfCameras; i++) {

        if (mBackCameraId == -1

&& mInfo[i].facing == CameraInfo.CAMERA_FACING_BACK) {

            mBackCameraId = i;

        } else if (mFrontCameraId == -1

&& mInfo[i].facing == CameraInfo.CAMERA_FACING_FRONT) {

            mFrontCameraId = i;

        }

    }

}

CameraHolder對象桶至,首先創(chuàng)建了一個(gè)HandlerThread線程昼伴,獨(dú)立于main thread,啟動該線程镣屹。接著創(chuàng)建自己MyHandler圃郊,用于在HandlerThread中處理自己的事物。最后初始化mInfo變量女蜈,保存camerainfo持舆。

2.****CameraHolder .open****()

public synchronized CameraProxy open(

        Handler handler, int cameraId,

        CameraManager.CameraOpenErrorCallback cb) {

  …

    Assert(!mCameraOpened);

    if (mCameraDevice != null && mCameraId != cameraId) {

        mCameraDevice.release();

        mCameraDevice = null;

        mCameraId = -1;

    }

    if (mCameraDevice == null) {

        Log.v(TAG, "open camera " + cameraId);

        if (mMockCameraInfo == null) {

            mCameraDevice

= CameraManagerFactory

                    .getAndroidCameraManager()

.cameraOpen(handler, cameraId, cb);

        } else {

            if (mMockCamera != null) {

                mCameraDevice = mMockCamera[cameraId];

            } else {

                mCameraDevice = null;

            }

        }

        if (mCameraDevice == null) {

            Log.e(TAG, "fail to connect Camera:" + mCameraId + ", aborting.");

            return null;

        }

        mCameraId = cameraId;

        mParameters = mCameraDevice.getCamera().getParameters();

    } else {

        if (!mCameraDevice.reconnect(handler, cb)) {

            Log.e(TAG, "fail to reconnect Camera:" + mCameraId + ", aborting.");

            return null;

        }

        mCameraDevice.setParameters(mParameters);

    }

    mCameraOpened = true;

    mHandler.removeMessages(RELEASE_CAMERA);

    mKeepBeforeTime = 0;

    return mCameraDevice;

}

該函數(shù)首先判斷當(dāng)前camera是否已經(jīng)打開色瘩,如果已經(jīng)打開。需要先調(diào)用mCameraDevice.release()釋放掉逸寓,在重新調(diào)用CameraManagerFactory獲取mCameraDevice實(shí)例對象居兆。

3.**** CameraManagerFactory****.**** getAndroidCameraManager ()

Public static synchronized CameraManager getAndroidCameraManager() {

    if (sAndroidCameraManager == null) {

        sAndroidCameraManager = new AndroidCameraManagerImpl();

    }

    return sAndroidCameraManager;

}

該函數(shù)定義在Camera/src/com/android/camera/ CameraManagerFactory.java文件中,代碼很簡單new AndroidCameraManagerImpl對象返回給調(diào)用者竹伸。

AndroidCameraManagerImpl() {

    HandlerThread ht = new HandlerThread("Camera Handler Thread");

    ht.start();

    mCameraHandler = new CameraHandler(ht.getLooper());

}

從這個(gè)類的初始化中可以看到泥栖,該對象內(nèi)部創(chuàng)建了自己的HandlerThread,并啟動勋篓,這個(gè)很重要聊倔,后期的client對camera的操作都在這個(gè)線程中完成的。后面遇到具體操作我們在分析生巡。

4.**** AndroidCameraManagerImpl****.**** cameraOpen ()

public CameraManager.CameraProxy cameraOpen(

    Handler handler, int cameraId, CameraOpenErrorCallback callback) {

    mCameraHandler.obtainMessage(OPEN_CAMERA, cameraId, 0,

            CameraOpenErrorCallbackForward.getNewInstance(

                    handler, callback)).sendToTarget();

    mCameraHandler.waitDone();

    if (mCamera != null) {

        return new AndroidCameraProxyImpl();

    } else {

        return null;

    }

}

這個(gè)函數(shù)定義在Camera/src/com/android/camera/ AndroidCameraManagerImpl.java文件中

函數(shù)首先通過mCameraHandler獲取OPEN_CAMERA消息并發(fā)送給mCameraHandler處理耙蔑,mCameraHandler處理是在AndroidCameraManagerImpl初始化的時(shí)候創(chuàng)建的HandlerThread線程中處理的。發(fā)送完消息之后調(diào)用mCameraHandler.waitDone()阻塞當(dāng)前主線程孤荣,等待framework層的camera開啟甸陌。

5.**** android.hardware.Camera.open

在上一步發(fā)送了OPEN_CAMERA,指令給mCameraHandler。mCameraHandler收到這個(gè)指令之后調(diào)用framework層camera接口android.hardware.Camera.open開啟camera device盐股。當(dāng)這一步完成之后钱豁,主線程會被喚醒,繼續(xù)執(zhí)行下一步疯汁。

6**** AndroidCameraProxyImpl

當(dāng)主線程再次被喚醒的時(shí)候牲尺,判斷camera device是否成功開啟,如果成功開啟幌蚊,將創(chuàng)建AndroidCameraProxyImpl實(shí)例谤碳,后續(xù)對camera所有的操作的都要經(jīng)過AndroidCameraProxyImpl統(tǒng)一發(fā)送到mCameraHandler進(jìn)行處理。

5.Camera 應(yīng)用初始化分析

Camera app提供的主要功能有:預(yù)覽溢豆,拍照蜒简,攝影,全景拍攝漩仙。

而這些功能統(tǒng)一組織在CameraActivity這個(gè)組件中搓茬,并細(xì)分到各個(gè)模塊逐步實(shí)現(xiàn)。其中涉及的類如下圖

圖片.png

PhotoModule:負(fù)責(zé)與拍照相關(guān)的部分;

VideoModule:負(fù)責(zé)與視頻錄制相關(guān)的部分;

WideAnglePanoramaModule:全景照片的拍攝;

接下來我們分析PhotoModule啟動過程队他,其他兩個(gè)模塊類似卷仑。

首先給出時(shí)序圖,結(jié)合時(shí)序圖麸折,分析每一步的實(shí)現(xiàn)锡凝。

圖片.png

1.CameraActivity.onCreate()

當(dāng)在launcher界面點(diǎn)擊camera圖標(biāo)的時(shí)候,該函數(shù)首先被調(diào)用磕谅。

public void onCreate(Bundle state) {

setContentView(R.layout.camera_filmstrip);

LayoutInflater inflater = getLayoutInflater();

    View rootLayout = inflater.inflate(R.layout.camera, null, false);

    mCameraModuleRootView

= rootLayout.findViewById(R.id.camera_app_root);

    mPanoStitchingPanel = findViewById(R.id.pano_stitching_progress_panel);

    mBottomProgress

= (ProgressBar) findViewById(R.id.pano_stitching_progress_bar);

    mCameraPreviewData = new CameraPreviewData(rootLayout,

            FilmStripView.ImageData.SIZE_FULL,

            FilmStripView.ImageData.SIZE_FULL);

    // Put a CameraPreviewData at the first position.

    mWrappedDataAdapter = new FixedFirstDataAdapter(

            new CameraDataAdapter(new ColorDrawable(

                    getResources().getColor(R.color.photo_placeholder))),

            mCameraPreviewData);

    setModuleFromIndex(moduleIndex);

    mCurrentModule.init(this, mCameraModuleRootView);

},

該函數(shù)主要設(shè)置activity視圖私爷,并根據(jù)傳過來的intent來判讀改啟動photomodule還是videomodule雾棺,當(dāng)從launcher啟動的時(shí)候moduleIndex肯定是0,所以會調(diào)用mCurrentModule = new PhotoModule();新建一個(gè)PhotoModule對象衬浑,該對象就是負(fù)責(zé)拍照相關(guān)的操作捌浩。

2.****PhotoModule()

對象創(chuàng)建之后,并沒有做實(shí)質(zhì)性的動作工秩,我們接著往下看

3.****mCurrentModule.init(this,mCameraModuleRootView)

mCurrentModule就是PhotoModule類的一個(gè)實(shí)例尸饺,

public void init(CameraActivity activity, View parent) {

    mActivity = activity;

    mRootView = parent;

    mUI = new PhotoUI(activity, this, parent);

    mPreferences = new ComboPreferences(mActivity);

    CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());

    mCameraId = getPreferredCameraId(mPreferences);

    mContentResolver = mActivity.getContentResolver();

    // Surface texture is from camera screen nail and startPreview needs it.

    // This must be done before startPreview.

    mIsImageCaptureIntent = isImageCaptureIntent();

    mPreferences.setLocalId(mActivity, mCameraId);

    CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());

    // we need to reset exposure for the preview

    resetExposureCompensation();

    initializeControlByIntent();

    mQuickCapture

= mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);

    mLocationManager = new LocationManager(mActivity, mUI);

    mSensorManager

= (SensorManager)(mActivity.getSystemService(Context.SENSOR_SERVICE));

    brightnessProgressBar = (ProgressBar)mRootView.findViewById(R.id.progress);

    if (brightnessProgressBar instanceof SeekBar) {

        SeekBar seeker = (SeekBar) brightnessProgressBar;

        seeker.setOnSeekBarChangeListener(mSeekListener);

    }

    brightnessProgressBar.setMax(MAXIMUM_BRIGHTNESS);

    brightnessProgressBar.setProgress(mbrightness);

    skinToneSeekBar = (SeekBar) mRootView.findViewById(R.id.skintoneseek);

    skinToneSeekBar.setOnSeekBarChangeListener(mskinToneSeekListener);

    skinToneSeekBar.setVisibility(View.INVISIBLE);

    Title = (TextView)mRootView.findViewById(R.id.skintonetitle);

    RightValue = (TextView)mRootView.findViewById(R.id.skintoneright);

    LeftValue = (TextView)mRootView.findViewById(R.id.skintoneleft);

    Storage.setSaveSDCard(

mPreferences.getString(CameraSettings.KEY_CAMERA_SAVEPATH, "0").equals("1"));

}

剛才介紹過Photomodule是負(fù)責(zé)camera照相功能的,主要協(xié)調(diào)界面的顯示助币,參數(shù)的配置浪听,方便用戶根據(jù)自己的愛好來設(shè)置相關(guān)參數(shù)。參數(shù)View parent眉菱,是拍照界面的根節(jié)點(diǎn)迹栓, 即在cameraactivity中inflate 的R.layout.camera的camera_app_root視圖。Init函數(shù)中做了很多重要的工作俭缓。創(chuàng)建UI視圖克伊,初始化基本參數(shù),

4.****new PhotoUI(activity, this, parent)

視圖的真正創(chuàng)建是在PhotoUI對象中华坦,我們接著往下看

public PhotoUI(CameraActivity activity, PhotoController controller, View parent) {

    mActivity = activity;

    mController = controller;

    mRootView = parent;

    mActivity.getLayoutInflater().inflate(R.layout.photo_module,

            (ViewGroup) mRootView, true);

    mRenderOverlay

= (RenderOverlay) mRootView.findViewById(R.id.render_overlay);

    mFlashOverlay = mRootView.findViewById(R.id.flash_overlay);

    mPreviewCover = mRootView.findViewById(R.id.preview_cover);

    // display the view

    mTextureView = (TextureView) mRootView.findViewById(R.id.preview_content);

    mTextureView.setSurfaceTextureListener(this);

    mTextureView.addOnLayoutChangeListener(mLayoutListener);

    initIndicators();

    mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button);

    mSwitcher = (ModuleSwitcher) mRootView.findViewById(R.id.camera_switcher);

    mSwitcher.setCurrentIndex(ModuleSwitcher.PHOTO_MODULE_INDEX);

    mSwitcher.setSwitchListener(mActivity);

    mMenuButton = mRootView.findViewById(R.id.menu);

    ViewStub faceViewStub = (ViewStub) mRootView

            .findViewById(R.id.face_view_stub);

    if (faceViewStub != null) {

        faceViewStub.inflate();

        mFaceView = (FaceView) mRootView.findViewById(R.id.face_view);

        setSurfaceTextureSizeChangedListener(mFaceView);

    }

    mCameraControls

= (CameraControls) mRootView.findViewById(R.id.camera_controls);

    mAnimationManager = new AnimationManager();

    mOrientationResize = false;

    mPrevOrientationResize = false;

}

函數(shù)首先inflate R.layout.photo_module視圖愿吹,掛在mRootView節(jié)點(diǎn)下,mRootView即是R.layout.camera中的

camera_app_root視圖惜姐,他是photomodule的根視圖犁跪。photo_module這個(gè)視圖包括了其他的一些重要視圖,其中比較重要的幾個(gè)mTextureView歹袁,mShutterButton坷衍,mSwitcher,faceViewStub宇攻,mCameraControls惫叛,mMenuButton等

mTextureView:是TextureView控件,和surfaceview功能差不多逞刷,只是實(shí)現(xiàn)機(jī)制不同,都是用來顯示流媒體的妻熊。Camera preview數(shù)據(jù)就在此顯示

mCameraControls:是個(gè)容器夸浅,包裹mShutterButton,mSwitcher扔役,PieMenuButton帆喇,等控件

mShutterButton:拍照按鈕,mSwitcher:模式切換亿胸,faceViewStub臉部撲捉。mMenuButton:設(shè)置參數(shù)相關(guān)的

5.cameraactivity.onresume()

界面和參數(shù)的初始化工作完成之后,camera activity進(jìn)去resume狀態(tài),

public void onResume() {

 …

    mOrientationListener.enable();

    mCurrentModule.onResumeBeforeSuper();

    super.onResume();

    mCurrentModule.onResumeAfterSuper();

    setSwipingEnabled(true);

 ….

}

這里掉用了一個(gè)重要的函數(shù)mCurrentModule.onResumeAfterSuper()签财;到目前為止我們還沒有看到camera設(shè)備被打開的過程脊另,什么原因,以前在oncreate的時(shí)候就打開了camera設(shè)備荆针,難道是我們在前面分析的時(shí)候漏掉了,客官別急,切往下看鲁豪。

6.****mCurrentModule.onResumeAfterSuper()

調(diào)用這個(gè)函數(shù)之后,又進(jìn)入到photomodule對象中了律秃,我們來看一下具體實(shí)現(xiàn)

public void onResumeAfterSuper() {

String action = mActivity.getIntent().getAction();

if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)

     || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)){

    Log.v(TAG, "On resume, from lock screen.");

    mHandler.postDelayed(new Runnable() {

        public void run() {

            onResumeTasks();

        }

    }, ON_RESUME_TASKS_DELAY_MSEC);

} else {

    Log.v(TAG, "On resume.");

    onResumeTasks();

}

mHandler.post(new Runnable(){

    @Override

    public void run(){

        mActivity.updateStorageSpaceAndHint();

    }

});

}

函數(shù)先根據(jù)action類型做不同的處理爬橡,最終都會調(diào)用onResumeTasks函數(shù)。

7.****onResumeTasks****()

private void onResumeTasks() {

    if (mOpenCameraFail || mCameraDisabled)

return;

    mJpegPictureCallbackTime = 0;

    mZoomValue = 0;

    resetExposureCompensation();

    if (!prepareCamera()) {

        // Camera failure.

        return;

    }

函數(shù)接著調(diào)用prepareCamera進(jìn)一步啟動camera設(shè)備

8.****prepareCamera()

private boolean prepareCamera() {

    mCameraDevice = CameraUtil.openCamera(

            mActivity, mCameraId, mHandler,

            mActivity.getCameraOpenErrorCallback());

    if (mCameraDevice == null) {

        Log.e(TAG, "Failed to open camera:" + mCameraId);

        return false;

    }

    mParameters = mCameraDevice.getParameters();

    initializeCapabilities();

    if (mFocusManager == null) initializeFocusManager();

    setCameraParameters(UPDATE_PARAM_ALL);

    mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);

    mCameraPreviewParamsReady = true;

    startPreview();

    mOnResumeTime = SystemClock.uptimeMillis();

    checkDisplayRotation();

    return true;

}

這個(gè)是很重要的函數(shù)棒动,函數(shù)先調(diào)用CameraUtil.openCamera

來獲取camera設(shè)備糙申,CameraUtil.openCamera我們在上一篇文章中介紹過了,這里不做介紹了船惨。獲取到camera設(shè)備后柜裸,接著設(shè)置camera初始化參數(shù),最后調(diào)用startPreview();啟動預(yù)覽掷漱。在啟動預(yù)覽的時(shí)候mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);向mHandler發(fā)送了一個(gè)CAMERA_OPEN_DONE消息粘室。Camera setting的部分測初始化是在這個(gè)階段完成的。

9.**** onCameraOpened

private void onCameraOpened() {

    View root = mUI.getRootView();

    int width = root.getWidth();

    int height = root.getHeight();

    mFocusManager.setPreviewSize(width, height);

    openCameraCommon();

    resizeForPreviewAspectRatio();

}

該函數(shù)還是比較簡單的卜范,設(shè)置mFocusManager區(qū)域大小衔统,調(diào)用openCameraCommon進(jìn)一步操作camera

10.****openCameraCommon()

private void openCameraCommon() {

    loadCameraPreferences();

    mUI.onCameraOpened(mPreferenceGroup, mPreferences, mParameters, this);

    if (mIsImageCaptureIntent) {

        mUI.overrideSettings(CameraSettings.KEY_CAMERA_HDR_PLUS,

                mActivity.getString(R.string.setting_off_value));

    }

    updateCameraSettings();

    showTapToFocusToastIfNeeded();

}

函數(shù)首先調(diào)用loadCameraPreferences函數(shù),來加載默認(rèn)初始數(shù)據(jù)R.xml.camera_preferences海雪,將這些初始數(shù)據(jù)加載到內(nèi)存中锦爵,最終保存在mPreferenceGroup變量中,被后面的UI空間使用奥裸。

11.****mUI.onCameraOpened(mPreferenceGroup, mPreferences, mParameters, this)

加載完初始數(shù)據(jù)之后险掀,下一步便是創(chuàng)建必要的UI控件

public void onCameraOpened(PreferenceGroup prefGroup, ComboPreferences prefs,

        Camera.Parameters params, OnPreferenceChangedListener listener) {

    if (mPieRenderer == null) {

        mPieRenderer = new PieRenderer(mActivity);

        mPieRenderer.setPieListener(this);

        mRenderOverlay.addRenderer(mPieRenderer);

    }

    if (mMenu == null) {

        mMenu = new PhotoMenu(mActivity, this, mPieRenderer);

        mMenu.setListener(listener);

    }

    mMenu.initialize(prefGroup);

    mMenuInitialized = true;

    if (mZoomRenderer == null) {

        mZoomRenderer = new ZoomRenderer(mActivity);

        mRenderOverlay.addRenderer(mZoomRenderer);

    }

    if (mGestures == null) {

        // this will handle gesture disambiguation and dispatching

        mGestures

= new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer);

        mRenderOverlay.setGestures(mGestures);

    }

    mGestures.setZoomEnabled(params.isZoomSupported());

    mGestures.setRenderOverlay(mRenderOverlay);

    mRenderOverlay.requestLayout();

    initializeZoom(params);

    updateOnScreenIndicators(params, prefGroup, prefs);

}

PhotoMenu這對象控制在屏幕上出現(xiàn)的各個(gè)小按鈕.視圖的大體層次見下圖

圖片.png

6.Camera UI介紹

6.1 Camera UI總體預(yù)覽

UI界面布局及主要控件說明:

圖片.png

6.2 ModePicker類

顯示支持的feature

1.Feature table中配置:

(1).參考FAQ:FAQ05993如何配置MT658X Camera的feature table

(2).配置KEY_CAPTURE_MODE

圖片.png

只有featuretable跟Camera AP都有配置的feature,才會在UI上顯示⊥逯妫可以通過LOG確認(rèn)feature table中配置的feature,抓取進(jìn)入Camera的main log,搜索【showParameters】,在打印出來的字串中找到cap-mode-values.

2.Camera AP配置

ModeCheck中樟氢,針對前后攝像頭配置支持的feature.

圖片.png

只有featuretable跟Camera AP都有配置的feature,才會在UI上顯示。

6.3 IndicatorManager類

該類主要負(fù)責(zé)顯示awb,scene mode,exposure,timelapse,selftimer,Voice的indicator等侠鳄。當(dāng)awb,scene mode,exposure,timelapse,selftimer,不是default值時(shí)埠啃,會顯示indicator;聲控拍照功能打開時(shí),會顯示indicator伟恶。

6.4 RemainingManager類

該類負(fù)責(zé)存儲卡相關(guān)信息的提示碴开,包含剩余拍照張數(shù)剩余錄像時(shí)長,SD卡狀態(tài)(已滿,沒有SD卡等)潦牛。

1.剩余拍照張數(shù)的計(jì)算以及顯示

在storage.java中眶掌,有定義一個(gè)PICTURE_SIZE_TABLE,該table中存儲不同拍照size以及不同quality,一張照片可能會占用的空間。根據(jù)SD卡的剩余空間巴碗,LOW_STORAGE_THRESHOLD,以及該估算值計(jì)算出剩余拍照張數(shù).

2.剩余錄像時(shí)長的計(jì)算

根據(jù)所錄制Video的VideoBitrate以及audioBitrate,SD卡剩余空間朴爬,LOW_STORAGE_THRESHOLD計(jì)算而來.計(jì)算方法可以參考RemainingManager的computeStorage方法。

6.5 PickerManager類

該類主要包含開啟/關(guān)閉HDR良价,開啟/關(guān)閉smile shot,SwitchCamera寝殴,F(xiàn)lash Mode的選擇。具體響應(yīng)流程如下:

圖片.png

6.6 ThumbnailManager類

該類主要用于縮略圖的顯示更新明垢,包含從launcher進(jìn)Camera蚣常,在圖庫中對圖片進(jìn)行操作之后返回Camera,拍照錄像之后更新縮略圖痊银。具體流程:

1.進(jìn)Camera顯示thumbnail,如果data/data/com.android.gallery3d/files/last_thumb存在抵蚊,會從該文件中decode thumbnail。如果該文件不存在溯革,會從mediaprovider通過調(diào)用getLastThumbFromContentResolver獲取贞绳。如下圖:

圖片.png

進(jìn)入圖庫編輯照片(比如刪除)之后,回到camera界面致稀,thumbnail的顯示流程冈闭,通過發(fā)送ACTION_UPDATE_PICTURE的廣播,在thumbnailmanager中監(jiān)聽此廣播抖单,更新thumbnail萎攒。如下圖:

圖片.png

拍照可錄像之后thumbnail的保存更新,如下圖:

圖片.png

SaveRequest包含所有要保存到數(shù)據(jù)庫或SD卡的數(shù)據(jù)單元矛绘;

FileSaver管理保存業(yè)務(wù)耍休,命名文件,保存文件信息到數(shù)據(jù)庫或SD卡货矮,創(chuàng)建Thumbnail羊精,通知監(jiān)聽;

ThumbnailManager用于在文件保存后更新Thumbnail囚玫;

Camera在prepareSaveRequest()設(shè)置全局參數(shù)到SaveRequest喧锦;

Actor通過調(diào)用Camera. prepareSaveRequest()得到SaveRequest,保存數(shù)值到SaveRequest抓督,通過調(diào)用SaveRequest.addRequest添加request到保存隊(duì)列裸违。

6.7 ShutterManager類

該類包含拍照、錄像本昏、確定、取消等按鈕的管理枪汪。

public static final int SHUTTER_TYPE_PHOTO_VIDEO = 0;

點(diǎn)擊camera圖標(biāo)進(jìn)入camera,不是通過彩信或其它應(yīng)用調(diào)用拍照或錄像涌穆;

public static final int SHUTTER_TYPE_PHOTO = 1;

通過Image Capture Intent調(diào)用Camera拍照怔昨;

public static final int SHUTTER_TYPE_VIDEO = 2;

通過Video Capture Intent調(diào)用Camera錄像;

public static final int SHUTTER_TYPE_OK_CANCEL = 3;

通過Image Capture Intent/Video Capture Intent調(diào)用Camera 拍照或錄像之后宿稀,全景拍照過程中趁舀;

public static final int SHUTTER_TYPE_CANCEL = 4;

MAV拍照過程中;

public static final int SHUTTER_TYPE_CANCEL_VIDEO = 5;

選擇Video Effect之后祝沸;

6.8 SettingManager類

負(fù)責(zé)設(shè)置菜單的顯示矮烹、隱藏點(diǎn)擊某一項(xiàng)的響應(yīng)。

圖片.png

點(diǎn)擊Item的響應(yīng)流程:

圖片.png

6.9 FocusManager類

該類負(fù)責(zé)touch focus的處理罩锐,根據(jù)touch的點(diǎn)奉狈,計(jì)算touch area以及metering area,做AE以及AF;根據(jù)對焦?fàn)顟B(tài)涩惑,顯示對焦框:idle,focusing,success,fail,focusing_snap_on_finfish仁期。

可以客制化為點(diǎn)擊屏幕拍照,點(diǎn)擊ShutterButton鍵對焦完成之后拍照等竭恬。

Touch focus流程:

1.響應(yīng)onSingleTapUp;

2.計(jì)算FocusArea以及MeteringArea跛蛋;

    calculateTapArea(focusWidth, focusHeight, 1f, x, y, previewWidth, previewHeight,

            mFocusArea.get(0).rect);

    calculateTapArea(focusWidth, focusHeight, 1.5f, x, y, previewWidth, previewHeight,

            mMeteringArea.get(0).rect);

3.stopFaceDetection();

4.mCamera.getCameraDevice().autoFocus(mAutoFocusCallback);

5. mAutoFocusCallback中返回對焦結(jié)果,更新focusUI痊硕。

7.Camera 預(yù)覽流程

7.1 Camera預(yù)覽命令流程

圖片.png

7.1.1 App層流程

打開Camera第一件事情就是預(yù)覽取景preview的動作赊级,我們先從Camera app分析起 。

packages/apps/Camera/src/com/android/camera/PhotoModule.java

private void startPreview() {

mCameraDevice.setPreviewDisplayAsync(mCameraSurfaceHolder);

mCameraDevice.startPreviewAsync();

}

packages/apps/Camera/src/com/android/camera/CameraManager.java

public void setPreviewDisplayAsync(final SurfaceHolder surfaceHolder) {

mCameraHandler.obtainMessage(SET_PREVIEW_DISPLAY_ASYNC

, surfaceHolder).sendToTarget();

}

public void startPreviewAsync() {

mCameraHandler.sendEmptyMessage(START_PREVIEW_ASYNC);

}

public void handleMessage(final Message msg) {

switch (msg.what) {

case SET_PREVIEW_DISPLAY_ASYNC:  

  try {  

     mCamera.setPreviewDisplay((SurfaceHolder) msg.obj);  

  } catch(IOException e) {  

      throw new RuntimeException(e);  

  }  

  return;  // no need to call mSig.open()  

case START_PREVIEW_ASYNC:  

  mCamera.startPreview();  

  return;  // no need to call mSig.open()  

}

}

7.1.2 Framework層流程

frameworks/base/core/java/android/hardware/Camera.java

public final void setPreviewDisplay(SurfaceHolder holder) throws IOException {

setPreviewDisplay(holder.getSurface());  

}

public native final void startPreview();

frameworks/base/core/jni/android_hardware_Camera.cpp

static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env

, jobject thiz, jobject jSurface){

sp<Camera> camera = get_native_camera(env, thiz, NULL);

if (camera->setPreviewDisplay(surface) != NO_ERROR) {

  jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed");  

}

}

static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz){

sp<Camera> camera = get_native_camera(env, thiz, NULL);

if (camera->startPreview() != NO_ERROR) {

jniThrowRuntimeException(env, "startPreview failed");  

return;  

}

}

frameworks/av/camera/Camera.cpp

status_t Camera::setPreviewDisplay(const sp<Surface>& surface){

sp <ICamera> c = mCamera;

return c->setPreviewDisplay(surface);

}

status_t Camera::startPreview(){

sp <ICamera> c = mCamera;

return c->startPreview();

}

7.1.3 Binder調(diào)用

frameworks/av/services/camera/libcameraservice/CameraClient.cpp

status_t CameraClient::setPreviewDisplay(const sp<Surface>& surface) {

return setPreviewWindow(binder, window);  

}

status_t CameraClient::startPreview() {

return startCameraMode(CAMERA_PREVIEW_MODE);  

}

status_t CameraClient::startCameraMode(camera_mode mode) {

switch(mode) {

case CAMERA_PREVIEW_MODE:  

  return startPreviewMode();  

}

}

status_t CameraClient::startPreviewMode() {

mHardware->setPreviewWindow(mPreviewWindow);

result = mHardware->startPreview();

}

frameworks/av/services/camera/libcameraservice/CameraHardwareInterface.h

status_t setPreviewWindow(const sp<ANativeWindow>& buf){

mPreviewWindow = buf;

return mDevice->ops->set_preview_window(mDevice,

                buf.get() ? &mHalPreviewWindow.nw : 0);  

}

status_t startPreview(){

return mDevice->ops->start_preview(mDevice);

}

7.1.4 HAL層流程

hardware/amlogic/camera/CameraHal_Module.cpp

int camera_set_preview_window(struct camera_device * device,

    struct preview_stream_ops *window){  

rv = gCameraHals[aml_dev->cameraid]->setPreviewWindow(window);  

}

int camera_start_preview(struct camera_device * device){

rv = gCameraHals[aml_dev->cameraid]->startPreview();  

}

hardware/amlogic/camera/CameraHal.cpp

status_t CameraHal::setPreviewWindow(struct preview_stream_ops *window){

mDisplayAdapter = new ANativeWindowDisplayAdapter();  

mDisplayAdapter->setFrameProvider(mCameraAdapter); //將frameCallbackRelay設(shè)置為BaseCameraAdapter的回調(diào)函數(shù)

mDisplayAdapter->setErrorHandler(mAppCallbackNotifier.get()); //設(shè)置BaseCameraAdapter錯(cuò)誤處理函數(shù)

ret = mDisplayAdapter->setPreviewWindow(window);//設(shè)置post的Surface

}

status_t CameraHal::startPreview(){

ret = allocPreviewBufs(mPreviewWidth, mPreviewHeight

, mParameters.getPreviewFormat(), required_buffer_count, max_queueble_buffers);

}

7.2 數(shù)據(jù)流程

圖片.png

hardware/amlogic/camera/V4LCameraAdapter/V4LCameraAdapter.cpp

int V4LCameraAdapter::previewThread(){

yuyv422_to_nv21(src,dest,width,height);

frame.mFrameMask |= CameraFrame::PREVIEW_FRAME_SYNC;

frame.mPixelFmt = mPixelFormat;

ret = sendFrameToSubscribers(&frame);

}

hardware/amlogic/camera/BaseCameraAdapter.cpp

status_t BaseCameraAdapter::sendFrameToSubscribers(CameraFrame *frame){

ret = __sendFrameToSubscribers(frame, &mFrameSubscribers

, CameraFrame::PREVIEW_FRAME_SYNC);

}

status_t BaseCameraAdapter::__sendFrameToSubscribers(CameraFrame* frame,

                                                 KeyedVector<int, frame_callback> *subscribers,  

                                                 CameraFrame::FrameType frameType){  

callback = (frame_callback) subscribers->valueAt(k);

callback(frame);

}

hardware/amlogic/camera/ANativeWindowDisplayAdapter.cpp

void ANativeWindowDisplayAdapter::frameCallbackRelay(CameraFrame* caFrame){

ANativeWindowDisplayAdapter *da

= (ANativeWindowDisplayAdapter*) caFrame->mCookie;

da->frameCallback(caFrame);

}

void ANativeWindowDisplayAdapter::frameCallback(CameraFrame* caFrame){

PostFrame(df);

}

status_t ANativeWindowDisplayAdapter

::PostFrame(ANativeWindowDisplayAdapter::DisplayFrame &dispFrame){

mANativeWindow->set_crop(mANativeWindow, xOff/bytesPerPixel, yOff,

                                 (xOff/bytesPerPixel)+mPreviewWidth, yOff+mPreviewHeight);  

ret = mANativeWindow->enqueue_buffer(mANativeWindow, mBufferHandleMap[i]);

}

frameworks/av/services/camera/libcameraservice/CameraHardwareInterface.h

static int __set_crop(struct preview_stream_ops *w,

                   int left, int top, int right, int bottom){  

ANativeWindow *a = anw(w);

return native_window_set_crop(a, &crop);

}

static int __enqueue_buffer(struct preview_stream_ops* w,

                   buffer_handle_t* buffer){  

ANativeWindow *a = anw(w);

return a->queueBuffer(a,

               container_of(buffer, ANativeWindowBuffer, handle), -1);  

}

7.3 函數(shù)調(diào)用流程

圖片.png

Camera在預(yù)覽時(shí)候都要實(shí)現(xiàn)SurfaceHolder.Callback接口岔绸,并實(shí)現(xiàn)其surfaceCreated理逊、 surfaceChanged、surfaceDestroyed三個(gè)函數(shù)亭螟,同時(shí)聲明一個(gè)用于預(yù)覽的窗口SurfaceView 挡鞍,以下是APP的源代碼

SurfaceView preview = (SurfaceView) findViewById(R.id.camera_preview);

SurfaceHolder holder = preview.getHolder();

holder.addCallback(this);

設(shè)置camera預(yù)覽的surface緩存區(qū) ,在surfaceChange()方法里面設(shè)置Camera的預(yù)覽區(qū),以供底層獲取的preview數(shù)據(jù)不斷投遞到這個(gè)surface緩存區(qū)內(nèi)残炮。

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {

mSurfaceHolder = holder;

if (mCameraDevice == null) return;

if (mPausing || isFinishing()) return;

if (mCameraState == PREVIEW_STOPPED) {

        startPreview();

        startFaceDetection();

} else {

if (Util.getDisplayRotation(this) != mDisplayRotation) {

         setDisplayOrientation();

}

        if (holder.isCreating()) {

setPreviewDisplay(holder);

}

}

設(shè)置好以上參數(shù)后呛每,就可以調(diào)用startPreview()進(jìn)行取景預(yù)覽。startPreview()也是一層層往下調(diào)用翘县,最后到Camera的服務(wù)端CameraService,我們看下它的過程

Camera.java(應(yīng)用)

------------->Camera.java(框 架)

------------->android_hardware_camera.cpp(JNI)

------------->Camera.cpp(客 戶端)

------------->CameraService.cpp(服務(wù) 端)

------------->CameraHarwareInterface(HAL接口)

在CameraService端將處理preview的請求并進(jìn)入HAL層

status_t CameraService::Client::startPreview() {

enableMsgType(CAMERA_MSG_PREVIEW_METADATA);

return startCameraMode(CAMERA_PREVIEW_MODE);

}

先是傳遞preview的消息到HAL層,然后執(zhí)行preview

status_t CameraService::Client::startCameraMode(camera_mode mode) {

switch(mode) {

case CAMERA_PREVIEW_MODE:

if (mSurface == 0 && mPreviewWindow == 0) {

LOG1("mSurface is not set yet.");

     // still able to start preview in this case.

}

return startPreviewMode();

}

}

status_t CameraService::Client::startPreviewMode() {

LOG1("startPreviewMode");

status_t result = NO_ERROR;

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

if (mHardware->previewEnabled()) {

return NO_ERROR;

}

if (mPreviewWindow != 0) {

native_window_set_scaling_mode(mPreviewWindow.get(),

NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);

native_window_set_buffers_transform(mPreviewWindow.get(),

mOrientation);

}

mHardware->setPreviewWindow(mPreviewWindow);

result = mHardware->startPreview();

return result;

}

然后就近去HAL層調(diào)用谴分,并通過回調(diào)函數(shù)源源不斷的將數(shù)據(jù)投遞到 surfaceview的緩存去锈麸,因?yàn)閜review的數(shù)據(jù)是比較大的,所以數(shù)據(jù)不會攜帶著傳上上層牺蹄,而是直接在兩個(gè)緩存區(qū)之間copy忘伞,一個(gè)是底層采集 數(shù)據(jù)的緩存區(qū),另一個(gè)是用于顯示的surfaceview緩存區(qū)。

我們看看preview的回調(diào)函數(shù)是怎么處理的氓奈。首先在Camera客戶端與服務(wù)端連接成功的時(shí)候就會設(shè)置一個(gè)回調(diào)函數(shù)dataCallBack

CameraService::Client::Client(const sp& cameraService,

const sp& cameraClient,

const sp& hardware,

int cameraId, int cameraFacing, int clientPid) {

......

mHardware->setCallbacks(notifyCallback,

dataCallback,

dataCallbackTimestamp,

(void *)cameraId);

}

在上篇有介紹到翘魄,client與server連接成功后就會new 一個(gè)client返回,在client的構(gòu)造函數(shù)中,就對camera設(shè)置了notifyCallback舀奶、dataCallback暑竟、 dataCallbackTimestamp三個(gè)回調(diào)函數(shù),用于返回底層數(shù)據(jù)用于處理育勺,看下它的處理方法:

void CameraService::Client::dataCallback(int32_t msgType,

const sp& dataPtr, camera_frame_metadata_t metadata, void user) {

switch (msgType & ~CAMERA_MSG_PREVIEW_METADATA) {

case CAMERA_MSG_PREVIEW_FRAME:

client->handlePreviewData(msgType, dataPtr, metadata);

break;

.......

}

繼續(xù)看handlePreviewData():

void CameraService::Client::handlePreviewData(int32_t msgType,

const sp& mem,

camera_frame_metadata_t *metadata) {

sp c = mCameraClient;

.......

if (c != 0) {

// Is the received frame copied out or not?

if (flags & CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {

LOG2("frame is copied");

copyFrameAndPostCopiedFrame(msgType, c, heap, offset, size, metadata);

} else {

LOG2("frame is forwarded");

mLock.unlock();

c->dataCallback(msgType, mem, metadata);

}

} else {

mLock.unlock();

}

}

copyFrameAndPostCopiedFrame就是這個(gè)函數(shù)執(zhí)行兩個(gè)buff區(qū)preview數(shù)據(jù)的投遞

void CameraService::Client::copyFrameAndPostCopiedFrame(

    int32_t msgType, const sp& client,

    const sp& heap, size_t offset, size_t size,

    camera_frame_metadata_t *metadata) {

......

previewBuffer = mPreviewBuffer;

memcpy(previewBuffer->base(), (uint8_t *)heap->base() + offset, size);

sp frame = new MemoryBase(previewBuffer, 0, size);

if (frame == 0) {

    LOGE("failed to allocate space for frame callback");

    mLock.unlock();

    return;

   }

mLock.unlock();

client->dataCallback(msgType, frame, metadata);

}

將數(shù)據(jù)處理成frame但荤,繼續(xù)調(diào)用客戶端client的回調(diào)函數(shù):

client->dataCallback(msgType, frame, metadata);

// callback from camera service when frame or image is ready

void Camera::dataCallback(int32_t msgType, const sp& dataPtr,

camera_frame_metadata_t *metadata)

{

sp listener;

{

    Mutex::Autolock _l(mLock);

    listener = mListener;

}

if (listener != NULL) {

    listener->postData(msgType, dataPtr, metadata);

}

}

初始化的時(shí)候,在jni里面有設(shè)置listener

static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,

jobject weak_this, jint cameraId)

{

sp context = new JNICameraContext(env, weak_this, clazz, camera);

context->incStrong(thiz);

camera->setListener(context);

}

繼續(xù) listener->postData(msgType, dataPtr, metadata);

void JNICameraContext::postData(int32_t msgType, const sp& dataPtr,

                            camera_frame_metadata_t *metadata)

{

......

 switch (dataMsgType) {

    case CAMERA_MSG_VIDEO_FRAME:

        // should never happen

        break;

      default:

        LOGV("dataCallback(%d, %p)", dataMsgType, dataPtr.get());

        copyAndPost(env, dataPtr, dataMsgType);

        break;

}

}

繼續(xù)copyAndPost(env, dataPtr, dataMsgType);

void JNICameraContext::copyAndPost(JNIEnv* env, const sp& dataPtr, int msgType)

{

jbyteArray obj = NULL;

// allocate Java byte array and copy data

if (dataPtr != NULL) {

.......

        } else {

            LOGV("Allocating callback buffer");

            obj = env->NewByteArray(size);

    .......

            env->SetByteArrayRegion(obj, 0, size, data);

        }

    } else {

LOGE("image heap is NULL");

    }

}

// post image data to Java

env->CallStaticVoidMethod(mCameraJClass, fields.post_event,

        mCameraJObjectWeak, msgType, 0, 0, obj);

if (obj) {

env->DeleteLocalRef(obj);

}

}

解釋一下標(biāo)紅的部分涧至,先建立一個(gè)byte數(shù)組obj腹躁,將data緩存數(shù)據(jù)存儲進(jìn)obj數(shù)組,CallStaticVoidMethod是C調(diào)用java函數(shù)化借,最后執(zhí)行實(shí)在Camera.java(框架)的postEventFromNative()

private static void postEventFromNative(Object camera_ref,

                                        int what, int arg1, int arg2, Object obj)

{

    Camera c = (Camera)((WeakReference)camera_ref).get();

    if (c == null)

return;

    if (c.mEventHandler != null) {

        Message m = c.mEventHandler.obtainMessage(what, arg1, arg2, obj);

         c.mEventHandler.sendMessage(m);

    }

}

還是handler處理地

public void handleMessage(Message msg) {

        switch(msg.what) {

         case CAMERA_MSG_SHUTTER:

            if (mShutterCallback != null) {

                mShutterCallback.onShutter();

            }

            return;

        case CAMERA_MSG_RAW_IMAGE:

            if (mRawImageCallback != null) {

                mRawImageCallback.onPictureTaken((byte[])msg.obj, mCamera);

            }

            return;

        case CAMERA_MSG_COMPRESSED_IMAGE:

            if (mJpegCallback != null) {

                mJpegCallback.onPictureTaken((byte[])msg.obj, mCamera);

            }

            return;

         case CAMERA_MSG_PREVIEW_FRAME:

            if (mPreviewCallback != null) {

                PreviewCallback cb = mPreviewCallback;

                if (mOneShot) {

                    mPreviewCallback = null;

                } else if (!mWithBuffer) {

                      setHasPreviewCallback(true, false);

                }

                cb.onPreviewFrame((byte[])msg.obj, mCamera);

            }

            return;

         }

    }

}

上面可以看出潜慎,這里處理了所有的回調(diào),快門回調(diào) mShutterCallback.onShutter()蓖康,RawImageCallback.onPictureTaken()拍照數(shù)據(jù)回調(diào)铐炫,自動對 焦回調(diào)等。蒜焊。默認(rèn)是沒有previewcallback這個(gè)回調(diào)的倒信,除非你的app設(shè)置了setPreviewCallback,可以看出preview 的數(shù)據(jù)還是可以向上層回調(diào)泳梆,只是系統(tǒng)默認(rèn)不回調(diào)鳖悠,另數(shù)據(jù)采集區(qū)與顯示區(qū)兩個(gè)緩存區(qū)buffer preview數(shù)據(jù)的投遞,以完成preview實(shí)時(shí)顯示是在HAL層完成的优妙。

8.Camera拍照流程

8.1 CameraService初始化過程

frameworks\base\media\mediaserver\Main_MediaServer.cpp

CameraService在MediaServer中初始化,代碼是MediaServer的main函數(shù),在該函數(shù)中初始化照相機(jī)服務(wù)乘综。

CameraService中的instantiate方法用來創(chuàng)建CameraService實(shí)例,并進(jìn)行相應(yīng)的初始化,這個(gè)函數(shù)定義在它的父類BinderService中:frameworks/base/include/binder/BinderService.

相機(jī)服務(wù)的初始化過程首先是創(chuàng)建CameraService實(shí)例,然后將其注冊到ServiceManager中,關(guān)于它的啟動是發(fā)生在init.rc中,通過media_server來啟動CameraService,具體代碼如下:

system/core/rootdir/init.rc

service servicemanager /system/bin/servicemanager

class core

user system

group system

critical

onrestart restart zygote

onrestart restart media

onrestart restart surfaceflinger

onrestart restart drm

service media /system/bin/mediaserver

class main

user media

group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc

ioprio rt 4

在cameraService注冊以及啟動過程中cameraService自身會執(zhí)行一些初始化的工作,主要涉及到如下工作

frameworks/base/services/camera/libcameraservice/CameraService.cpp

CameraService::CameraService()

:mSoundRef(0), mModule(0)

{

LOGI("CameraService started (pid=%d)", getpid());

gCameraService = this;

}

void CameraService::onFirstRef()

{

BnCameraService::onFirstRef();

if (hw_get_module(CAMERA_HARDWARE_MODULE_ID,

            (const hw_module_t **)&mModule) < 0) {

    LOGE("Could not load camera HAL module");

    mNumberOfCameras = 0;

}

else {

    mNumberOfCameras = mModule->get_number_of_cameras();

    if (mNumberOfCameras > MAX_CAMERAS) {

        LOGE("Number of cameras(%d) > MAX_CAMERAS(%d).",

                mNumberOfCameras, MAX_CAMERAS);

        mNumberOfCameras = MAX_CAMERAS;

    }

    for (int i = 0; i < mNumberOfCameras; i++) {

        setCameraFree(i);

    }

}

}

在上述初始化代碼中,先通過調(diào)用HAL硬件抽象層庫load camera HAL module,獲取支持的攝像頭個(gè)數(shù)并保存到mNumberOfCameras。

8.2應(yīng)用程序鏈接相機(jī)服務(wù)過程

8.2.1 Camera連接整個(gè)過程

在camera應(yīng)用程序啟動的時(shí)候首先會和CameraService建立連接,camera應(yīng)用程序代碼就不分析了,下面是一個(gè)簡單的流程圖套硼。

圖片.png

從上面的流程圖我們可以看出在請求服務(wù)的過程中多出用到了應(yīng)用框架層的camera類,該類定義在 frameworks/base/core/java/android/hardware/Camera.java文件當(dāng)中,這一個(gè)類正是Camera子系統(tǒng)中APP層和JNI層交換的接口,它對上為應(yīng)用程序提供了各種操作Camera的方法,向下訪問JNI實(shí)現(xiàn)它自身的接口Camera類定義如下:

public class Camera {

public static Camera open(int cameraId) {

      return new Camera(cameraId);

}

.................

Camera(int cameraId) {

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;

}

native_setup(new WeakReference<Camera>(this), cameraId);

}

}

8.2.2 Camera takepicture過程

下面就開始從app的takepicture逐步分析camera takepicture整個(gè)過程卡辰。在app中,takepicture是在capture方法中被調(diào)用的

packages/apps/OMAPCamera/src/com/ti/omap4/android/camera/Camera.java

@Override

public boolean capture() {

    synchronized (mCameraStateLock) {

        // If we are already in the middle of taking a snapshot then ignore.

        if (mCameraState == SNAPSHOT_IN_PROGRESS || mCameraDevice == null) {

              return false;

        }

        mCaptureStartTime = System.currentTimeMillis();

        mPostViewPictureCallbackTime = 0;

        mJpegImageData = null;

        // Set rotation and gps data.

        Util.setRotationParameter(mParameters, mCameraId, mOrientation);

        Location loc = mLocationManager.getCurrentLocation();

        Util.setGpsParameters(mParameters, loc);

        if (canSetParameters()) {

              mCameraDevice.setParameters(mParameters);

        }

        try {

              mCameraDevice.takePicture(mShutterCallback, mRawPictureCallback,

                    mPostViewPictureCallback, new JpegPictureCallback(loc));

        } catch (RuntimeException e ) {

            e.printStackTrace();

            return false;

        }

        mFaceDetectionStarted = false;

        setCameraState(SNAPSHOT_IN_PROGRESS);

        return true;

    }

}

這里調(diào)用的takePicture是在framework層中定義:

frameworks/base/core/java/android/hardware/Camera.java

public final void takePicture(ShutterCallback shutter, PictureCallback raw,

        PictureCallback postview, PictureCallback jpeg) {

    mShutterCallback = shutter;

    mRawImageCallback = raw;

    mPostviewCallback = postview;

    mJpegCallback = jpeg;

    // If callback is not set, do not send me callbacks.

    int msgType = 0;

    if (mShutterCallback != null) {

          msgType |= CAMERA_MSG_SHUTTER;

    }

    if (mRawImageCallback != null) {

          msgType |= CAMERA_MSG_RAW_IMAGE;

    }

    if (mPostviewCallback != null) {

          msgType |= CAMERA_MSG_POSTVIEW_FRAME;

    }

    if (mJpegCallback != null) {

          msgType |= CAMERA_MSG_COMPRESSED_IMAGE;

    }

    native_takePicture(msgType);

}

在這里設(shè)置callback函數(shù)邪意,并調(diào)用通過JNI調(diào)用takepicture方法:

frameworks/base/core/jni/android_hardware_Camera.cpp

static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz, int msgType)

{

LOGV("takePicture");

JNICameraContext* context;

sp<Camera> camera = get_native_camera(env, thiz, &context);

if (camera == 0) return;

if (msgType & CAMERA_MSG_RAW_IMAGE) {

    LOGV("Enable raw image callback buffer");

    if (!context->isRawImageCallbackBufferAvailable()) {

        LOGV("Enable raw image notification, since no callback buffer exists");

        msgType &= ~CAMERA_MSG_RAW_IMAGE;

        msgType |= CAMERA_MSG_RAW_IMAGE_NOTIFY;

    }

}

if (camera->takePicture(msgType) != NO_ERROR) {

    jniThrowRuntimeException(env, "takePicture failed");

    return;

}

}

這里調(diào)用camera 的takepicture九妈,即camera client 的takepicture方法:

frameworks/base/libs/camera/Camera.cpp

status_t Camera::takePicture(int msgType, const String8& params)

{

LOGV("takePicture: 0x%x", msgType);

sp <ICamera> c = mCamera;

if (c == 0) return NO_INIT;

return c->takePicture(msgType, params);

}

這里client 端的takepicture又調(diào)用到camera server端的takepicture:

frameworks/base/services/camera/libcameraservice/CameraService.cpp

// take a picture - image is returned in callback

ifdef OMAP_ENHANCEMENT_CPCAM

status_t CameraService::Client::takePicture(int msgType, const String8& params) {

else

status_t CameraService::Client::takePicture(int msgType) {

endif

LOG1("takePicture (pid %d): 0x%x", getCallingPid(), msgType);

Mutex::Autolock lock(mLock);

status_t result = checkPidAndHardware();

if (result != NO_ERROR) return result;

if ((msgType & CAMERA_MSG_RAW_IMAGE) &&

    (msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY)) {

    LOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY"

            " cannot be both enabled");

    return BAD_VALUE;

}

// We only accept picture related message types

// and ignore other types of messages for takePicture().

int picMsgType = msgType

                    & (CAMERA_MSG_SHUTTER |

                       CAMERA_MSG_POSTVIEW_FRAME |

                       CAMERA_MSG_RAW_IMAGE |

ifdef OMAP_ENHANCEMENT

                       CAMERA_MSG_RAW_BURST |

endif

                       CAMERA_MSG_RAW_IMAGE_NOTIFY |

                       CAMERA_MSG_COMPRESSED_IMAGE);

ifdef OMAP_ENHANCEMENT

picMsgType |= CAMERA_MSG_COMPRESSED_BURST_IMAGE;

endif

enableMsgType(picMsgType);

ifdef OMAP_ENHANCEMENT

// make sure the other capture messages are disabled

picMsgType = ~picMsgType &

             (CAMERA_MSG_SHUTTER |

              CAMERA_MSG_POSTVIEW_FRAME |

              CAMERA_MSG_RAW_IMAGE |

              CAMERA_MSG_RAW_BURST |

              CAMERA_MSG_RAW_IMAGE_NOTIFY |

              CAMERA_MSG_COMPRESSED_IMAGE |

              CAMERA_MSG_COMPRESSED_BURST_IMAGE);

disableMsgType(picMsgType);

endif

ifdef OMAP_ENHANCEMENT_CPCAM

return mHardware->takePicture(params);

else

return mHardware->takePicture();

endif

}

一些初始化之后,最終server端的takepicture方法會調(diào)用HAL層(硬件接口層)的takepicture方法:

frameworks/base/services/camera/libcameraservice/CameraHardwareInterface.h

ifdef OMAP_ENHANCEMENT_CPCAM

status_t takePicture(const ShotParameters ?ms)

{

    LOGV("%s(%s)", __FUNCTION__, mName.string());

    if (mDevice->ops->take_picture)

        return mDevice->ops->take_picture(mDevice,

                                          params.flatten().string());

    return INVALID_OPERATION;

}

else

status_t takePicture()

{

    LOGV("%s(%s)", __FUNCTION__, mName.string());

    if (mDevice->ops->take_picture)

        return mDevice->ops->take_picture(mDevice);

    return INVALID_OPERATION;

}

endif

下面的重點(diǎn)是分析數(shù)據(jù)回調(diào)過程雾鬼,這個(gè)過程是camera的最重點(diǎn)萌朱,在我看來也是最難點(diǎn)理解的地方,要多花點(diǎn)時(shí)間策菜,努把力了晶疼,現(xiàn)在就開始

首先還是必須先追溯到Camera客戶端與服務(wù)端連接的時(shí)候酒贬,由我的上一遍初始化的文章知道,Camera客戶端與服務(wù)端連接的時(shí)候冒晰,首先調(diào)用的是client端的connect方法同衣,client的connect方法首先getservice,然后調(diào)用server端的connect方法壶运,為了方便理解我再次把這部分代碼貼出:server的connect()函數(shù)定義在以下路徑:

frameworks/base/services/camera/libcameraservice/CameraService.cpp

sp<ICamera> CameraService::connect(

    const sp<ICameraClient>& cameraClient, int cameraId) {

int callingPid = getCallingPid();

sp<CameraHardwareInterface> hardware = NULL;

LOG1("CameraService::connect E (pid %d, id %d)", callingPid, cameraId);

if (!mModule) {

    LOGE("Camera HAL module not loaded");

    return NULL;

}

sp<Client> client;

if (cameraId < 0 || cameraId >= mNumberOfCameras) {

    LOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).",

        callingPid, cameraId);

    return NULL;

}

char value[PROPERTY_VALUE_MAX];

property_get("sys.secpolicy.camera.disabled", value, "0");

if (strcmp(value, "1") == 0) {

    // Camera is disabled by DevicePolicyManager.

    LOGI("Camera is disabled. connect X (pid %d) rejected", callingPid);

    return NULL;

}

Mutex::Autolock lock(mServiceLock);

if (mClient[cameraId] != 0) {

    client = mClient[cameraId].promote();

    if (client != 0) {

        if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) {

            LOG1("CameraService::connect X (pid %d) (the same client)",

                callingPid);

            return client;

        } else {

            LOGW("CameraService::connect X (pid %d) rejected (existing client).",

                callingPid);

            return NULL;

        }

    }

    mClient[cameraId].clear();

}

if (mBusy[cameraId]) {

    LOGW("CameraService::connect X (pid %d) rejected"

         " (camera %d is still busy).", callingPid, cameraId);

    return NULL;

}

struct camera_info info;

if (mModule->get_camera_info(cameraId, &info) != OK) {

    LOGE("Invalid camera id %d", cameraId);

    return NULL;

}

char camera_device_name[10];

snprintf(camera_device_name, sizeof(camera_device_name), "%d", cameraId);

hardware = new CameraHardwareInterface(camera_device_name);

if (hardware->initialize(&mModule->common) != OK) {

    hardware.clear();

    return NULL;

}

client = new Client(this, cameraClient, hardware, cameraId, info.facing, callingPid);

mClient[cameraId] = client;

LOG1("CameraService::connect X");

return client;

}

最重要的地方在上面標(biāo)注的綠色部分,這里在connect成功之后會new一個(gè)client浪秘,這個(gè)Client是CamereService類的內(nèi)部類蒋情,

這個(gè)時(shí)候便會調(diào)用client這個(gè)內(nèi)部類的client構(gòu)造函數(shù),而我們的回調(diào)函數(shù)也正是在這個(gè)時(shí)候被設(shè)置耸携,看看代碼:

CameraService::Client::Client(const sp<CameraService>& cameraService,

    const sp<ICameraClient>& cameraClient,

    const sp<CameraHardwareInterface>& hardware,

    int cameraId, int cameraFacing, int clientPid) {

int callingPid = getCallingPid();

LOG1("Client::Client E (pid %d)", callingPid);

mCameraService = cameraService;

mCameraClient = cameraClient;

mHardware = hardware;

mCameraId = cameraId;

mCameraFacing = cameraFacing;

mClientPid = clientPid;

mMsgEnabled = 0;

mSurface = 0;

mPreviewWindow = 0;

ifdef OMAP_ENHANCEMENT_CPCAM

mTapin = 0;

mTapinClient = 0;

mTapout = 0;

mTapoutClient = 0;

endif

mHardware->setCallbacks(notifyCallback,

                        dataCallback,

                        dataCallbackTimestamp,

                        (void *)cameraId);

// Enable zoom, error, focus, and metadata messages by default

enableMsgType(CAMERA_MSG_ERROR 

| CAMERA_MSG_ZOOM

| CAMERA_MSG_FOCUS

| CAMERA_MSG_PREVIEW_METADATA);

// Callback is disabled by default

mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP;

mOrientation = getOrientation(0, mCameraFacing == CAMERA_FACING_FRONT);

mPlayShutterSound = true;

cameraService->setCameraBusy(cameraId);

cameraService->loadSound();

LOG1("Client::Client X (pid %d)", callingPid);

}

上面就對camera設(shè)置了notifyCallback棵癣、dataCallback、dataCallbackTimestamp三個(gè)回調(diào)函數(shù)夺衍,用于返回底層數(shù)據(jù)用于處理狈谊,看下它的處理方法:

這里先針對其中的dataCallback回調(diào)方法做介紹,其他的回調(diào)方法以此類推沟沙,所以我們就先看一下dataCallback方法中都做了些什么事情:

這里的回調(diào)函數(shù)是camera server層的回調(diào)函數(shù):

frameworks/base/services/camera/libcameraservice/CameraService.cpp

void CameraService::Client::dataCallback(int32_t msgType,

    const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) {

LOG2("dataCallback(%d)", msgType);

sp<Client> client = getClientFromCookie(user);

if (client == 0) return;

if (!client->lockIfMessageWanted(msgType)) return;

if (dataPtr == 0 && metadata == NULL) {

    LOGE("Null data returned in data callback");

    client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);

    return;

}

switch (msgType & ~CAMERA_MSG_PREVIEW_METADATA) {

    case CAMERA_MSG_PREVIEW_FRAME:

        client->handlePreviewData(msgType, dataPtr, metadata);

        break;

    case CAMERA_MSG_POSTVIEW_FRAME:

        client->handlePostview(dataPtr);

        break;

    case CAMERA_MSG_RAW_IMAGE:

        client->handleRawPicture(dataPtr);

        break;

    case CAMERA_MSG_COMPRESSED_IMAGE:

        client->handleCompressedPicture(dataPtr);

        break;

ifdef OMAP_ENHANCEMENT

    case CAMERA_MSG_COMPRESSED_BURST_IMAGE:

        client->handleCompressedBurstPicture(dataPtr);

        break;

endif

    default:

        client->handleGenericData(msgType, dataPtr, metadata);

        break;

}

}

這里進(jìn)行分類處理河劝,因?yàn)閜review過程需要大量數(shù)據(jù)傳輸,而且容易大家理解矛紫,這里就針對preview數(shù)據(jù)回調(diào)過程進(jìn)行分析:

// preview callback - frame buffer update

void CameraService::Client::handlePreviewData(int32_t msgType,

                                          const sp<IMemory>& mem,

                                          camera_frame_metadata_t *metadata) {

ssize_t offset;

size_t size;

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

// local copy of the callback flags

int flags = mPreviewCallbackFlag;

// is callback enabled?

if (!(flags & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK)) {

    // If the enable bit is off, the copy-out and one-shot bits are ignored

    LOG2("frame callback is disabled");

    mLock.unlock();

    return;

}

// hold a strong pointer to the client

sp<ICameraClient> c = mCameraClient;

// clear callback flags if no client or one-shot mode

if (c == 0 || (mPreviewCallbackFlag 

& CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) {

    LOG2("Disable preview callback");

    mPreviewCallbackFlag &= ~(CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK |

                              CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK |

                              CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK);

    disableMsgType(CAMERA_MSG_PREVIEW_FRAME);

}

if (c != 0) {

    // Is the received frame copied out or not?

    if (flags & CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {

        LOG2("frame is copied");

        copyFrameAndPostCopiedFrame(msgType, c, heap, offset, size, metadata);

    } else {

        LOG2("frame is forwarded");

        mLock.unlock();

        c->dataCallback(msgType, mem, metadata);

    }

} else {

      mLock.unlock();

}

}

這里有兩個(gè)方向copyFrameAndPostCopiedFrame這個(gè)函數(shù)執(zhí)行兩個(gè)buff區(qū)preview數(shù)據(jù)的投遞赎瞎,通過它的具體實(shí)現(xiàn)過程,可以知道最終他也要調(diào)用dataCallback方法繼續(xù)調(diào)用客戶端client的回調(diào)函數(shù)

所以這里直接分析copyFrameAndPostCopiedFrame:

void CameraService::Client::copyFrameAndPostCopiedFrame(

    int32_t msgType, const sp<ICameraClient>& client,

    const sp<IMemoryHeap>& heap, size_t offset, size_t size,

    camera_frame_metadata_t *metadata) {

LOG2("copyFrameAndPostCopiedFrame");

sp<MemoryHeapBase> previewBuffer;

if (mPreviewBuffer == 0) {

    mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);

} else if (size > mPreviewBuffer->virtualSize()) {

    mPreviewBuffer.clear();

    mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);

}

if (mPreviewBuffer == 0) {

    LOGE("failed to allocate space for preview buffer");

    mLock.unlock();

    return;

}

previewBuffer = mPreviewBuffer;

memcpy(previewBuffer->base(), (uint8_t *)heap->base() + offset, size);

sp<MemoryBase> frame = new MemoryBase(previewBuffer, 0, size);

if (frame == 0) {

    LOGE("failed to allocate space for frame callback");

    mLock.unlock();

    return;

}

mLock.unlock();

client->dataCallback(msgType, frame, metadata);

}

從這里開始颊咬,回調(diào)函數(shù)進(jìn)入到camera client的回調(diào)函數(shù):

frameworks/base/libs/camera/Camera.cpp

// callback from camera service when frame or image is ready

void Camera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr,

                      camera_frame_metadata_t *metadata)

{

sp<CameraListener> listener;

{

    Mutex::Autolock _l(mLock);

    listener = mListener;

}

if (listener != NULL) {

    listener->postData(msgType, dataPtr, metadata);

}

}

這里的listener到底是什么务甥,還記得初始化的時(shí)候,在jni里面有設(shè)置listenerm嗎喳篇?我們還是從新再看一下吧:frameworks/base/core/jni/android_hardware_Camera.cpp

// connect to camera service

static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,

jobject weak_this, jint cameraId)

{

sp<Camera> camera = Camera::connect(cameraId);

if (camera == NULL) {

    jniThrowRuntimeException(env, "Fail to connect to camera service");

    return;

}

// make sure camera hardware is alive

if (camera->getStatus() != NO_ERROR) {

    jniThrowRuntimeException(env, "Camera initialization failed");

    return;

}

jclass clazz = env->GetObjectClass(thiz);

if (clazz == NULL) {

    jniThrowRuntimeException(env, "Can't find android/hardware/Camera");

    return;

}

// We use a weak reference so the Camera object can be garbage collected.

// The reference is only used as a proxy for callbacks.

sp<JNICameraContext> context 

= new JNICameraContext(env, weak_this, clazz, camera);

context->incStrong(thiz);

camera->setListener(context);

// save context in opaque field

env->SetIntField(thiz, fields.context, (int)context.get());

}

由上面可以看出JNICameraContext是個(gè)監(jiān)聽類敞临,同時(shí)set這個(gè)監(jiān)聽類,這個(gè)類的定義在:

frameworks/base/core/jni/android_hardware_Camera.cpp

// provides persistent context for calls from native code to Java

class JNICameraContext: public CameraListener

{

public:

JNICameraContext(JNIEnv* env, jobject weak_this,

jclass clazz, const sp<Camera>& camera);

~JNICameraContext() { release(); }

virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);

virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr,

                      camera_frame_metadata_t *metadata);

virtual void postDataTimestamp(nsecs_t timestamp

, int32_t msgType, const sp<IMemory>& dataPtr);

void postMetadata(JNIEnv *env

, int32_t msgType, camera_frame_metadata_t *metadata);

void addCallbackBuffer(JNIEnv *env, jbyteArray cbb, int msgType);

void setCallbackMode(JNIEnv *env, bool installed, bool manualMode);

sp<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; }

bool isRawImageCallbackBufferAvailable() const;

void release();

private:

void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType);

void clearCallbackBuffers_l(JNIEnv *env, Vector<jbyteArray> *buffers);

void clearCallbackBuffers_l(JNIEnv *env);

jbyteArray getCallbackBuffer(JNIEnv *env

, Vector<jbyteArray> *buffers, size_t bufferSize);

jobject mCameraJObjectWeak; // weak reference to java object

jclass mCameraJClass; // strong reference to java class

sp<Camera> mCamera; // strong reference to native object

jclass mFaceClass; // strong reference to Face class

jclass mRectClass; // strong reference to Rect class

Mutex mLock;

Vector<jbyteArray> mRawImageCallbackBuffers;

Vector<jbyteArray> mCallbackBuffers; 

bool mManualBufferMode; 

bool mManualCameraCallbackSet; 

};

標(biāo)注部分是我們在上面用到的postData麸澜,我們看一看postData的實(shí)現(xiàn)過程:

void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr,

                            camera_frame_metadata_t *metadata)

{

// VM pointer will be NULL if object is released

Mutex::Autolock _l(mLock);

JNIEnv *env = AndroidRuntime::getJNIEnv();

if (mCameraJObjectWeak == NULL) {

    LOGW("callback on dead camera object");

    return;

}

int32_t dataMsgType = msgType & ~CAMERA_MSG_PREVIEW_METADATA;

// return data based on callback type

switch (dataMsgType) {

    case CAMERA_MSG_VIDEO_FRAME:

        // should never happen

        break;

    // For backward-compatibility purpose, if there is no callback

    // buffer for raw image, the callback returns null.

    case CAMERA_MSG_RAW_IMAGE:

        LOGV("rawCallback");

        if (mRawImageCallbackBuffers.isEmpty()) {

            env->CallStaticVoidMethod(mCameraJClass, fields.post_event,

                    mCameraJObjectWeak, dataMsgType, 0, 0, NULL);

        } else {

            copyAndPost(env, dataPtr, dataMsgType);

        }

        break;

    // There is no data.

    case 0:

        break;

    default:

        LOGV("dataCallback(%d, %p)", dataMsgType, dataPtr.get());

        copyAndPost(env, dataPtr, dataMsgType);

        break;

}

// post frame metadata to Java

if (metadata && (msgType & CAMERA_MSG_PREVIEW_METADATA)) {

    postMetadata(env, CAMERA_MSG_PREVIEW_METADATA, metadata);

}

}

我們接著看看這個(gè)copyAndPost方法:

void JNICameraContext::copyAndPost(JNIEnv* env

, const sp<IMemory>& dataPtr, int msgType)

{

jbyteArray obj = NULL;

// allocate Java byte array and copy data

if (dataPtr != NULL) {

    ssize_t offset;

    size_t size;

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

    LOGV("copyAndPost: off=%ld, size=%d", offset, size);

    uint8_t *heapBase = (uint8_t*)heap->base();

    if (heapBase != NULL) {

        const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);

        if (msgType == CAMERA_MSG_RAW_IMAGE) {

              obj = getCallbackBuffer(env, &mRawImageCallbackBuffers, size);

        } else if (msgType == CAMERA_MSG_PREVIEW_FRAME 

&& mManualBufferMode) {

            obj = getCallbackBuffer(env, &mCallbackBuffers, size);

            if (mCallbackBuffers.isEmpty()) {

                LOGV("Out of buffers, clearing callback!");

                mCamera

->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_NOOP);

                mManualCameraCallbackSet = false;

                if (obj == NULL) {

                    return;

                }

            }

        } else {

            LOGV("Allocating callback buffer");

            obj = env->NewByteArray(size);

        }

        if (obj == NULL) {

            LOGE("Couldn't allocate byte array for JPEG data");

            env->ExceptionClear();

        } else {

              env->SetByteArrayRegion(obj, 0, size, data);

        }

    } else {

        LOGE("image heap is NULL");

    }

}

// post image data to Java

env->CallStaticVoidMethod(mCameraJClass, fields.post_event,

        mCameraJObjectWeak, msgType, 0, 0, obj);

if (obj) {

    env->DeleteLocalRef(obj);

}

}

以上先建立一個(gè)byte數(shù)組obj挺尿,將data緩存數(shù)據(jù)存儲進(jìn)obj數(shù)組,CallStaticVoidMethod是C調(diào)用java函數(shù)痰憎,最后執(zhí)行實(shí)在Camera.java(框架)的postEventFromNative()

從這里開始票髓,回調(diào)函數(shù)進(jìn)入到camera framework層

frameworks/base/core/java/android/hardware/Camera.java

private static void postEventFromNative(Object camera_ref,

                                        int what, int arg1, int arg2, Object obj)

{

    Camera c = (Camera)((WeakReference)camera_ref).get();

    if (c == null)

          return;

    if (c.mEventHandler != null) {

        Message m = c.mEventHandler.obtainMessage(what, arg1, arg2, obj);

        c.mEventHandler.sendMessage(m);

    }

}

sendMessage之后由handle進(jìn)行處理,定義同樣在framework層

private class EventHandler extends Handler

{

    private Camera mCamera;

    public EventHandler(Camera c, Looper looper) {

        super(looper);

        mCamera = c;

    }

    @Override

    public void handleMessage(Message msg) {

        switch(msg.what) {

        case CAMERA_MSG_SHUTTER:

            if (mShutterCallback != null) {

                mShutterCallback.onShutter();

            }

            return;

        case CAMERA_MSG_RAW_IMAGE:

            if (mRawImageCallback != null) {

                mRawImageCallback.onPictureTaken((byte[])msg.obj, mCamera);

            }

            return;

        case CAMERA_MSG_COMPRESSED_IMAGE:

            if (mJpegCallback != null) {

                mJpegCallback.onPictureTaken((byte[])msg.obj, mCamera);

            }

            return

        case CAMERA_MSG_PREVIEW_FRAME:

            if (mPreviewCallback != null) {

                PreviewCallback cb = mPreviewCallback;

                if (mOneShot) {

                    mPreviewCallback = null;

                } else if (!mWithBuffer) {

                    setHasPreviewCallback(true, false);

                }

                cb.onPreviewFrame((byte[])msg.obj, mCamera);

            }

            return;

        case CAMERA_MSG_POSTVIEW_FRAME:

            if (mPostviewCallback != null) {

                mPostviewCallback.onPictureTaken((byte[])msg.obj, mCamera);

            }

            return;

        case CAMERA_MSG_FOCUS:

            if (mAutoFocusCallback != null) {

                mAutoFocusCallback.onAutoFocus(msg.arg1 == 0 

? false : true, mCamera);

            }

            return;

        case CAMERA_MSG_ZOOM:

            if (mZoomListener != null) {

                mZoomListener.onZoomChange(msg.arg1, msg.arg2 != 0, mCamera);

            }

            return;

        case CAMERA_MSG_PREVIEW_METADATA:

            if (mFaceListener != null) {

                mFaceListener.onFaceDetection((Face[])msg.obj, mCamera);

            }

            return;

        case CAMERA_MSG_ERROR :

            Log.e(TAG, "Error " + msg.arg1);

            if (mErrorCallback != null) {

                mErrorCallback.onError(msg.arg1, mCamera);

            }

            return;

        default:

            Log.e(TAG, "Unknown message type " + msg.what);

            return;

        }

    }

}

上面可以看出铣耘,這里處理了所有的回調(diào)洽沟,快門回調(diào)mShutterCallback.onShutter(),RawImageCallback.onPictureTaken()拍照數(shù)據(jù)回調(diào)蜗细,自動對焦回調(diào)等裆操。

默認(rèn)是沒有previewcallback這個(gè)回調(diào)的怒详,除非你的app設(shè)置了setPreviewCallback,可以看出preview的數(shù)據(jù)還是可以向上層回調(diào)踪区,只是系統(tǒng)默認(rèn)不回調(diào)昆烁,這里再說深一些:

需要做以下事情,檢查PreviewCallback 這個(gè)在framework中定義的接口有沒有設(shè)置了setPreviewCallback缎岗,設(shè)置則調(diào)用静尼,這里接口中的onPreviewFrame方法需要開發(fā)者自己實(shí)現(xiàn),這里默認(rèn)是沒有實(shí)現(xiàn)的传泊,需要特殊使用的要自己添加鼠渺,這里是自己的理解,看一下PreviewCallback 接口的定義:frameworks/base/core/java/android/hardware/Camera.java

public interface PreviewCallback

{

    void onPreviewFrame(byte[] data, Camera camera);

};

另數(shù)據(jù)采集區(qū)與顯示區(qū)兩個(gè)緩存區(qū)buffer preview數(shù)據(jù)的投遞眷细,以完成preview實(shí)時(shí)顯示是在HAL層完成的拦盹。

takePicture()處理過程跟preview差不多,只是增加了回調(diào)函數(shù)返回時(shí)候存儲圖像的動作溪椎,這里分析一下takepicture的處理過程:

case CAMERA_MSG_COMPRESSED_IMAGE:

            if (mJpegCallback != null) {

                mJpegCallback.onPictureTaken((byte[])msg.obj, mCamera);

            }

            return;

mJpegCallback的定義

private PictureCallback mJpegCallback;

走到這里我們又不得不回頭看看最起初在調(diào)用takepicture的時(shí)候是怎么調(diào)用的

try {

            mCameraDevice.takePicture(mShutterCallback, mRawPictureCallback,

                    mPostViewPictureCallback, new JpegPictureCallback(loc));

} catch (RuntimeException e ) {

            e.printStackTrace();

            return false;

}

這里大家看到了標(biāo)準(zhǔn)部分就是要使用的mJpegCallback普舆,但是這個(gè)callback是JpegPictureCallback類,我們定義的mJpegCallback確是PictureCallback 類校读,不是同一個(gè)類

所以這個(gè)還是必須得說清楚一點(diǎn)沼侣,看看JpegPictureCallback類的定義吧

private final class JpegPictureCallback implements PictureCallback {

    Location mLocation;

    public JpegPictureCallback(Location loc) {

        mLocation = loc;

    }

    public void onPictureTaken(

            final byte [] jpegData, final android.hardware.Camera camera) {

        if (mPausing) {

            if (mBurstImages > 0) {

                resetBurst();

                mBurstImages = 0;

                mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA,

                                                 CAMERA_RELEASE_DELAY);

            }

            return;

        }

        FocusManager.TempBracketingStates

tempState = mFocusManager.getTempBracketingState();

        mJpegPictureCallbackTime = System.currentTimeMillis();

        if (mPostViewPictureCallbackTime != 0) {

            mShutterToPictureDisplayedTime =

                    mPostViewPictureCallbackTime - mShutterCallbackTime;

            mPictureDisplayedToJpegCallbackTime =

                    mJpegPictureCallbackTime - mPostViewPictureCallbackTime;

        } else {

            mShutterToPictureDisplayedTime =

                    mRawPictureCallbackTime - mShutterCallbackTime;

            mPictureDisplayedToJpegCallbackTime =

                    mJpegPictureCallbackTime - mRawPictureCallbackTime;

        }

        Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "

                + mPictureDisplayedToJpegCallbackTime + "ms");

        if (!mIsImageCaptureIntent) {

            enableCameraControls(true);

            if (( tempState != FocusManager.TempBracketingStates.RUNNING ) &&

                  !mCaptureMode.equals(mExposureBracketing) &&

                  !mCaptureMode.equals(mZoomBracketing) &&

                  !mBurstRunning == true) {

                long delay = 500 - mPictureDisplayedToJpegCallbackTime;

                if (delay < 0) {

                    startPreview(true);

                    startFaceDetection();

                } else {

                    mHandler.sendEmptyMessageDelayed(RESTART_PREVIEW, delay);

                }

            }

        }

        if (!mIsImageCaptureIntent) {

            Size s = mParameters.getPictureSize();

            mImageSaver.addImage(jpegData, mLocation, s.width, s.height);

        } else {

            mJpegImageData = jpegData;

            if (!mQuickCapture) {

                showPostCaptureAlert();

            } else {

                doAttach();

            }

        }

        checkStorage();

        if (!mHandler.hasMessages(RESTART_PREVIEW)) {

            long now = System.currentTimeMillis();

            mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;

            Log.v(TAG, "mJpegCallbackFinishTime = "

                    + mJpegCallbackFinishTime + "ms");

            mJpegPictureCallbackTime = 0;

        }

        if (mCaptureMode.equals(mExposureBracketing) ) {

            mBurstImages --;

            if (mBurstImages == 0 ) {

                mHandler.sendEmptyMessageDelayed(RESTART_PREVIEW, 0);

            }

        }

      //reset burst in case of exposure bracketing

        if (mCaptureMode.equals(mExposureBracketing) && mBurstImages == 0) {

            mBurstImages = EXPOSURE_BRACKETING_COUNT;

            mParameters.set(PARM_BURST, mBurstImages);

            mCameraDevice.setParameters(mParameters);

        }

        if (mCaptureMode.equals(mZoomBracketing) ) {

            mBurstImages --;

            if (mBurstImages == 0 ) {

                mHandler.sendEmptyMessageDelayed(RESTART_PREVIEW, 0);

            }

        }

      //reset burst in case of zoom bracketing

        if (mCaptureMode.equals(mZoomBracketing) && mBurstImages == 0) {

            mBurstImages = ZOOM_BRACKETING_COUNT;

            mParameters.set(PARM_BURST, mBurstImages);

            mCameraDevice.setParameters(mParameters);

        }

        if ( tempState == FocusManager.TempBracketingStates.RUNNING ) {

            mBurstImages --;

            if (mBurstImages == 0 ) {

                mHandler.sendEmptyMessageDelayed(RESTART_PREVIEW, 0);

                mTempBracketingEnabled = true;

                stopTemporalBracketing();

            }

        }

        if (mBurstRunning) {

            mBurstImages --;

            if (mBurstImages == 0) {

                resetBurst();

                mBurstRunning = false;

                mHandler.sendEmptyMessageDelayed(RESTART_PREVIEW, 0);

            }

        }

    }

}

原來他們是父子類之間的關(guān)系,那么自然父類可以可以轉(zhuǎn)換為子類的形式地熄,但是子類就不能向父類轉(zhuǎn)換了华临。而且這里子類重新實(shí)現(xiàn)了父類的方法onPictureTaken崖叫。這里這個(gè)函數(shù)不就是handle里面調(diào)用的函數(shù)了怕犁。takepicture最終將圖片保存下來了。

8.2.3 Camera takepicture過程總結(jié)

函數(shù)調(diào)用流程圖如下:

圖片.png

拍照命令時(shí)序圖:

圖片.png

拍照數(shù)據(jù)回調(diào)時(shí)序圖:

圖片.png
  1. 點(diǎn)擊拍照按鈕圖標(biāo)會執(zhí)行ShutterButton類中的drawableStateChanged方法但绕,接著調(diào)用callshutterButtonFocus(boolean flag)方法却特,接著會調(diào)用OnShutterButtonListener接口中的onShutterButtonFocus(boolean flag)方法扶供。

  2. 在PhotoModule類中執(zhí)行onShutterButtonFocus(boolean pressed)方法,在該方法中判斷按下的狀態(tài)裂明,如果按下就調(diào)用FocusOverlayManager類中的onShutterDown()方法椿浓,接著調(diào)用autoFocus()方法,在該方法中又調(diào)用Listener接口中的autoFocus()方法闽晦,回調(diào)PhotoModule類中的autoFocus()方法扳碍。

3.在autoFocus方法中調(diào)用setCameraState(FOCUSING),接下來會調(diào)用內(nèi)部類AutoFocusCallback的onAutoFocus(boolean focused, android.hardware.Camera camera)方法仙蛉,初始化相機(jī)后笋敞,接著調(diào)用FocusOverlayManager類中的onAutoFocus(boolean focused, boolean shutterButtonPressed)方法,在該方法中處理mState為STATE_FOCUSING的邏輯荠瘪。

4.執(zhí)行ShutterButton類中的performClick()方法夯巷,主要用來響應(yīng)按下ShutterButton事件赛惩,接著回調(diào)PhotoModule類中的onShutterButtonClick()方法,接著調(diào)用onSnap方法趁餐,接下來調(diào)用FocusOverlayManager類中的doSnap()方法喷兼,接著調(diào)用capture方法,接著又回調(diào)PhotoModule類中的capture方法(關(guān)鍵)后雷。

  1. 拍照按鈕的狀態(tài)發(fā)生變化季惯,pressed值由true變?yōu)閒alse,繼續(xù)調(diào)用ShutterButton類中的callShutterButtonFocus方法喷面,調(diào)用OnShutterButtonListener接口中的onShutterButtonFocus()方法星瘾,在PhotoModule類的onShutterButtonFocus(boolean pressed)方法中直接返回,回調(diào)實(shí)現(xiàn)了ShutterCallback接口的類ShutterCallback里面的onShutter方法惧辈。

UI層類之間調(diào)用流程

圖片.png

6.底層(HAL)調(diào)用UI(APP)的接口實(shí)現(xiàn)抽象類AbstractShotMode中的public void onPictureTaken(byte[] data, Camera camera)方法,回調(diào)PhotoModule類中的public void onLastPictureTaken(boolean isNeedStartPreview)方法磕瓷。

  1. 在onLastPictureTaken方法中又調(diào)用restartPreview()方法盒齿,接著調(diào)用setupPreview()方法,在該方法中調(diào)用startPreview方法困食,流程如下startPreview(int updateSet)->setCameraParameters(int updateSet) ->updateCameraParametersInitialize()-> setPreviewSize()边翁,在上述流程中的setCameraParameters方法中繼續(xù)調(diào)用updateCameraParametersPreference()方法(關(guān)鍵),底層回調(diào)FeaturePictureCallback接口中的onQuickThumbnail(byte[] data)方法硕盹,在該方法中調(diào)用PhotoModule類中的onQuickThumbnail(byte[] data, boolean isGenUrlEarlier)方法符匾,返回到startPreview方法里。

  2. 在抽象類AbstractShotMode中onPictureTaken方法(關(guān)鍵)中執(zhí)行FeaturePictureCallback接口中的onFakePictureTaken()方法瘩例,接著會執(zhí)行CommonFeaturePictureCallback回調(diào)接口中的onFakePictureTaken()方法啊胶,再調(diào)用PhotoModule類中的onFakePictureTaken方法,繼續(xù)調(diào)用接口中的storeImage(byte[] jpegData, int postWidth, int postHeight,boolean isHdr, boolean updateThumbnail)垛贤。

底層調(diào)用UI層處理流程

圖片.png

9.Camera Feature 介紹和API使用

9.1 Face detection人臉檢測

簡介:根據(jù)preview信息焰坪,通過算法檢測人的臉部,并用白框選中檢測到的人臉聘惦。

9.1.1人臉檢測主要介紹代碼實(shí)現(xiàn)要素

1創(chuàng)建face detection監(jiān)聽

不同平臺有所不同流程一致這里以6580為例

(6580主要是通過消息來實(shí)現(xiàn)face detection 的流程的實(shí)現(xiàn)代碼在CameraManager.java)

設(shè)置監(jiān)聽mCamera.setFaceDetectionListener(...)

2啟動face detection

代碼在CameraManager.java子類CameraProxy.java里面startFaceDetection()函數(shù)

3關(guān)閉face detection

stopFaceDetection()

9.1.2調(diào)用流程某饰,這里以普通模式為例

其它模式雷同,還是以6580為參考

1設(shè)置face detection監(jiān)聽photoActor.java

private FaceDetectionListener mFaceDetectionListener = new FaceDetectionListener() {

    @Override

    public void onFaceDetection(Face[] faces, android.hardware.Camera camera) {

....

mCameraActivity.getFrameView().setFaces(faces);//注意這里白框出現(xiàn)了哈哈

mModuleManager.onFaceDetected(faces);//這里是進(jìn)一步的人臉狀態(tài)動態(tài)處理看2

}

2 Preview 數(shù)據(jù)face detection 完后(faces>0) 轉(zhuǎn)到photoMode找到photoMode.java里面有對應(yīng)的face detection完成后的處理

protected boolean executeAction(ActionType type, Object... arg) {

...

case ACTION_FACE_DETECTED:

if (arg != null) {

Log.d(TAG, "faceLength = " + ((Face[]) arg).length);

onFaceDetected(((Face[]) arg).length);//根據(jù)你的臉的大小對應(yīng)白框的變化更新

....

}

3結(jié)束face detection

調(diào)用:onFaceDetected(0);

例如:關(guān)閉camera的時(shí)候需要調(diào)用這句話釋放face detection善绎,實(shí)例代碼在photoActor.java的

onCameraClose(){

onFaceDetected(0);

}

9.2人臉美化****Face beauty

9.2.1功能介紹

對人臉進(jìn)行美白黔漂、磨皮、瘦臉和大眼等美化功能禀酱;

Smooth:皮膚平滑度炬守,越高皮膚除;

Skin Color:皮膚.色比勉,low為做紅.處理劳较;

Sharp:瘦臉&大眼程度驹止,值越高表示程度越大。

9.2.2三個(gè)參數(shù)的客制化

客制化參數(shù)packages/apps/Camera/src/com/android/camera/res/values/arrays.xml

<string-array name = “pref_camera_facebeauty_turning_entryvalues”

translatable = “false”>

<item>-4</item>

<item>0</item>

<item>4</item>

</string-array>

-4到4共9個(gè)等級

9.2.3常見問題

1美膚拍照無美膚效果

確認(rèn)拍照時(shí)有無正確識別到人臉(人臉框是否顯示)观蜗,如未顯示請先check FD是否有正常啟用臊恋,若有請check cap-mode是否設(shè)置為FB,如果都正常請?zhí)峤籩-service并附上log處理墓捻。

2不保存美膚拍照原圖

– packages/apps/Camera/src/com/android/cameraFeatureSwitcher.java文件中將isFaceBeautyOriginalPictureSaved的返回值改為False

3 FB拍照異常退出發(fā)生NE

– 看拍照比例是否符合4:3抖仅、5:3、16:9這三種砖第,如果不符 合請修改為這幾種比例或申請patch:ALPS00801970

– 請check拍照size是否超過了5000*4000撤卢,即寬不能超過5000,高不能超過4000梧兼,如果超過請申請patch:ALPS00872370

– 其他情況請?zhí)峤籩-service處理

9.2.4調(diào)用流程

Facebeauty是MTK開發(fā)的一種mode放吩,一般模式類功能都到mediatek的mode文件夾里面去找,里面絕對有你想要的~羽杰。接下來就來看看這個(gè)FB模式吧渡紫。眾觀MTK寫的附加功能,估計(jì)有許多人寫考赛,好亂惕澎。但mode類寫得比較好,采用的是常用的MVC設(shè)計(jì)模式這里單獨(dú)把face beauty 模式拆解一下颜骤,到時(shí)后面的其它模式就簡單概括了哈唧喉,可以參考這里來看流程。也可以按照這個(gè)來自己添加一個(gè)模式忍抽。

單獨(dú)的功能模塊實(shí)現(xiàn):MVC模式設(shè)計(jì)

M(模型):找到FaceBeautyMode.java

V(視圖):1:FaceBeautyRotateLayout.java

2:FaceBeautyView.java

C(控制):1:FaceBeautyInfo.java

2:FaceBeautyPictureSizeRule.java

3:FaceBeautyParamentersHelper.java

4:FaceBeautyPreviewSize.java

5:VfbQualityRule.java

6:VideoFaceBeautyRule.java

當(dāng)用戶切換到face beauty時(shí)會發(fā)送一個(gè)參數(shù)到ModeFactory.java(這是個(gè)模式生產(chǎn)工廠)新建一個(gè)FaceBeautyMode實(shí)例八孝,模式的視圖View則在FaceBeautyMode.java通過調(diào)用getCameraView(SpecViewType.MODE_FACE_BEAUTY)向UI大管家CameraAppUiImpl.java發(fā)送一個(gè)參數(shù)來獲取。這里吐槽一下視圖的產(chǎn)生也是和mode的產(chǎn)生一下都是基于工廠設(shè)計(jì)模式的,mode的view生產(chǎn)的地方在最終是在ViewFactory.java這個(gè)工廠里面梯找。下面是偽流程圖唆阿。

圖片.png

9.3 Auto ASD:自動場景檢測

簡介:根據(jù)preview數(shù)據(jù)信息判斷當(dāng)前場景:一般是7種場景:正常場景,夜間場景锈锤,風(fēng)景驯鳖,人物等。

9.3.1限制條件

1人臉檢測結(jié)果

2原始圖像信息

3 3A信息

所以自動場景檢測并非100%準(zhǔn)確久免。

9.3.2常見問題

只能識別正常場景浅辙,沒有實(shí)現(xiàn)3A信息接口,直接找驅(qū)動阎姥。

9.3.3代碼實(shí)現(xiàn)要素

1 調(diào)用setCameraMode將camera mode 設(shè)置為CAMERA_MODE_MTK_PRV

2 打開人臉識別功能

3 將capture mode設(shè)置為asd

parameter.setCaptureMode(Parameter.CAPTURE_MODE_ASD);

4 創(chuàng)建ASD callback

這里參考MT6580代碼在ASD.java里面

Private final AsdListener mASDCaptureCallback = new AsdListener(){

...

}

5 注冊ASD callback

代碼在ASD.java里面mCameraDevice.setASDCallback(mASDCaptureCallback );

6 使用完后或關(guān)閉ASD時(shí)調(diào)用如下代碼回收

設(shè)置ASD CallBacl為null记舆;

mCameraDevice.setASDCallback(null);

9.4 **Continue Shot **

9.4.1簡單介紹****與使用方法

Continue Shot:相對單拍可以連續(xù)拍多張照片;

長按capture按鍵呼巴,直到設(shè)定的最大連拍張數(shù)或者松開capture按鍵為止泽腮。

9.4.2 Continue Shot Spec and Limitation

1.當(dāng)AP發(fā)現(xiàn)保存jpeg file到SD變慢時(shí)御蒲,會自動降低連拍速度,甚至停止連拍

– 當(dāng)SD卡寫速度太慢诊赊、底層callback給AP的jpeg file速度相對較快時(shí)厚满,AP層可用的緩存jpeg file的空間逐漸減小,當(dāng)減小到一定程度時(shí)碧磅,會降低連拍速度碘箍;若仍不能超出寫SD速度,則會停止連拍鲸郊。

2.連拍拍照音響次數(shù)與實(shí)際拍照張數(shù)并不匹配丰榴,是互相獨(dú)立的

– 連拍的拍照音在AP層播

– 連拍的實(shí)際動作在hal層完成

9.4.3 Continue Shot****效果調(diào)試****/****客制化參數(shù)

1.控制連拍速度

– There is a API in Camera AP

setContinuousShotSpeed (int speed), speed=3 means capture

speed = 3fps

– You could call it after takePicture() as needed

2.更換拍照音

– /system/media/audio/ui/ camera_shutter.ogg

9.4.4 Continue Shot****常見問題

1拍照綠屏,花屏(直接找驅(qū)動分析)

– 提供復(fù)現(xiàn)問題的mobile log以及dump圖像數(shù)據(jù)給Mediatek分析

? Cmd:adb shell setprop debug.camera.dump 1

? Dump圖像路徑:/sdcard/.raw /sdcard/.yuv:

2連拍達(dá)不到設(shè)定的固定張數(shù)

– 請check SD卡的讀寫速度是否可以穩(wěn)定在較高速率上秆撮,其次請

check手機(jī)RAM是否太小四濒,而capture size是否又太大;

– 除此职辨,請?zhí)峁?fù)現(xiàn)問題的mobile log到Mediatek分析

3松開capture鍵后峻黍,連拍還在繼續(xù)

– 請確認(rèn)手機(jī)touch Pannel是否有異常

– 若無異常,請?zhí)峁?fù)現(xiàn)問題的mobile log給Mediatek分析

4連拍速度太慢

– 首先拨匆,確認(rèn)sensor capture frame rate是否過低

– 其次,請check SD卡讀寫速度是否可穩(wěn)定在較高速率上

– 再者挽拂,請check手機(jī)RAM是否太小,而設(shè)置的capture size又太大

– 除此亏栈,請?zhí)峁?fù)現(xiàn)問題的mobile log給Mediatek分析

9.5零延時(shí)拍照ZSD

9.5.1簡介

ZSD模式下台腥,sensor的工作方式不區(qū)分preview與capture,始終以特定的size輸出绒北,一般為sensor full size芦鳍,也可根據(jù)需要調(diào)整小蛾扇。

當(dāng)觸發(fā)ZSD拍照時(shí),sensor無須切換mode,直接將拍照鍵抬起瞬間的sensor輸出壓縮成JPEG file

9.5.2 ZSD Spec and Limitation

ZSD模式下势誊,預(yù)覽可能不流暢

– ZSD模式下,sensor的工作方式不區(qū)分preview與capture颂郎,始終以特定的size輸出谒获,一般為sensor full size

– 由于sensor能力的限制,一些sensor在full size輸出時(shí)幀率較低业簿,預(yù)覽會有卡頓不順的感覺

9.5.3 ZSD****客制化參數(shù)

1.ZSD實(shí)現(xiàn)原理

– sensor在ZSD模式時(shí)輸出size始終不變瘤礁,hal層在preview狀態(tài)下時(shí)時(shí)保存最新的N幀(可客制),當(dāng)user觸發(fā)拍照事件梅尤,可在保存的N幀中選擇與按拍照鍵時(shí)間最接近的用于拍照

2.客制化參數(shù)

– N = Camera_custom_zsd.cpp中函數(shù)get_zsd_cap_stored_frame_cnt()的返回值柜思,默認(rèn)為3

– 若認(rèn)為ZSD效果不明顯岩调,shutter delay較大,可增大get_zsd_cap_stored_frame_cnt函數(shù)的返回值

– 增大此值的影響赡盘,需要耗更多的memory

–需多耗2倍capture size的memory or 1.25倍sensor output size的memory号枕,case by case

9.5.4 ZSD****常見問題

1.預(yù)覽畫面不流暢,幀率低

– 請先確認(rèn)sensor輸出幀率亡脑,若較低堕澄,一般為sensor原因

– 否則請抓取復(fù)現(xiàn)問題的mobile log,并提交e-service處理

2.零延時(shí)效果不明顯

– 參考二說明

3.拍照綠屏霉咨,花屏(直接找驅(qū)動)

– 提供dump數(shù)據(jù)與mobile log蛙紫,提供e-service處理

– cmd:adb shell setprop debug.camera.zsddump 1

– dump數(shù)據(jù)存儲路徑:/sdcard/zsd/

4.預(yù)覽綠屏,花屏(直接找驅(qū)動)

– 提供dump數(shù)據(jù)與mobile log途戒,提供e-service處理

– cmd:adb shell setprop camera.dumpbuffer.enable 1

– dump數(shù)據(jù)存儲路徑:/sdcard

9.6笑臉模式

9.6.1簡介

在camera預(yù)覽時(shí) preview 數(shù)據(jù)到底層分析通過算法檢測笑臉坑傅。當(dāng)檢測到笑臉時(shí)便會拍一張照片。

9.6.2限制條件

1.當(dāng)人臉五官模糊到一定程度或明顯偏暗時(shí),人臉偵測和追蹤quality會變差

2.圖像中人臉小于圖像高度1/10時(shí)喷斋,無法偵測到人臉唁毒;人臉高度在圖像高度1/5 -1/10時(shí),識別率也較低星爪。

3.戴帽子浆西、戴眼鏡等會影響識別率

4.FD過程中,可能會有人臉框漂移或跟蹤不是很準(zhǔn)確

– 人臉識別功能包含兩個(gè)階段

1.FD(Face Detection): 根據(jù)人臉特征檢測出圖像中的人臉

2.FT(Face Tracking): 對檢測到的人臉進(jìn)行追蹤

– 由于FT參考人臉膚色信息顽腾,因此有此限制(有方式改善)

FD效果調(diào)試與客制化近零,找驅(qū)動調(diào)試

關(guān)鍵代碼:

void get_fd_CustomizeData(FD_Customize_PARA *FDDataOut){

FDThreshold = 32 (default)

FDRefresh = 70(default)

....

}

這兩個(gè)參數(shù)調(diào)試如下:

1.****FDRefresh = 70(default)

– Usage:

? 由于FT算法只對已檢測到的人臉進(jìn)行追蹤,而預(yù)覽畫面中可能出現(xiàn)

新的人臉抄肖,所以需要定期檢查畫面中是否有新的人臉進(jìn)入久信。

FDRefresh即用來設(shè)定每隔多少frame重新檢測人臉。

? 重新獲取的人臉位置跟上一張frame FT的結(jié)果可能有些許不同漓摩,

user會有FD框更新的感覺裙士,頻率太高會感覺人臉框更新太過頻繁。

– Suggest Range:

? 30~120

– Tuning purposes:

? 如果希望新的人臉加入時(shí)即能馬上被檢測到管毙,此值可以設(shè)小腿椎。

– Description:

? Increasing value à 兩次FD算法間隔幀數(shù)增加

? Decreasing value à兩次FD算法間隔幀數(shù)減少

2.****FDThreshold = 32 (default)

– Usage:

? FD閥值,判斷某區(qū)域是否為人臉的標(biāo)準(zhǔn)锅风。值低即標(biāo)準(zhǔn)低酥诽,識別率提

高,誤判率也同步提高皱埠;值高即標(biāo)準(zhǔn)高肮帐,識別率降低,誤判率也同

步降低。

– Range:

? 29~35

– Tuning purposes:

? 可以根據(jù)實(shí)際的誤判狀況來調(diào)整FDThreshold训枢,如果某些物體不希

望被誤判為人臉托修,可以慢慢調(diào)高閥值,再看狀況是否改善

– Description

? Increasing valueà DR(Detection Rate)下降恒界,F(xiàn)PR(False Pass

Rate)下降

? Decreasing value à DR上升睦刃,F(xiàn)PR上升

9.6.3 FD****常見問題

? 四個(gè)方向均支持人臉識別

– MT6589 JB2 Patch ID: ALPS00943937

– MT6582 JB5 Patch ID: ALPS00957624

? FD誤判

– 可以通過調(diào)整FDThreshold改善

? FD識別框飄動

– 可以通過調(diào)整FDRefresh改善

9.7物體動態(tài)追蹤****Object Tracking

9.7.1簡介

在preview界面,點(diǎn)中物體長按十酣,當(dāng)出現(xiàn)綠框鎖定物體時(shí)涩拙,捕獲成功。當(dāng)物體移動時(shí)耸采,綠框會鎖定跟隨物體移動兴泥。

9.7.2說明與限制條件

1.太大顏色變化會track fail

2.顏色太相近會追錯(cuò)對象

3.Touch的位置會影響追蹤的結(jié)果

4.追蹤物體或者手機(jī)移動太快,導(dǎo)致追蹤物體在前后兩張frame中的成像區(qū)域沒有重疊虾宇,會導(dǎo)致tracking fail

9.7.3 OT****效果調(diào)試****/****客制化參數(shù)

找驅(qū)動調(diào)試搓彻。

代碼路徑:alps\mediatek\custom\common\hal\camera\camera\camera_custom_ot.cpp

Void get_ot_CustomizeData(OT_Customize_PARA *OTDataOut){

LtOcOb_ColorSimilarity_TH = 0.37;

Numiter_shape_F =1 (default);

OBLoseTrackingFrm = 90 (default);

OCLoseTrackingFrm = 90 (default);

......

}

上面幾個(gè)參數(shù)的調(diào)試如下:

1.LtOcOb_ColorSimilarity_TH = 0.37(default)

– Usage:

? Objecttracking算法閥值,閥值設(shè)低嘱朽,可以提高tracking的成功率旭贬,

較不易lose tracking,但是也容易追錯(cuò)object

– Range:

? 0.2~0.59

– Tuning purposes

? 如果tracking的時(shí)候很容易追錯(cuò)物體(例如: 從鮮紅色的物體追錯(cuò)成

粉紅色或暗紅色物體)搪泳,可以提高閥值

? 如果不希望一些AE變化引起lose tracking(例如: 暗紅色的物體因

為曝光時(shí)間增加變?yōu)榱良t色稀轨,而lose tracking),可以調(diào)低閥值

– Description

? Increasing valueà不容易追錯(cuò)物體岸军,容易lose tracking

? Decreasing value à容易追錯(cuò)物體靶端,不容易lose tracking

2.Numiter_shape_F =1 (default)

– Usage:

? 每幾個(gè)frame會對被追蹤物體做一次reshape,reshape根據(jù)被追蹤

物體成像大小的變化來更新tracking框的大小

? 若每個(gè)frame都做reshape凛膏,UI上的框會實(shí)時(shí)更新,但花費(fèi)較大的計(jì)

算量

– Range:

? 1 ~ 10

– Tuning purposes

? 如果不需要reshape很頻繁脏榆、更重視降低cpu loading時(shí)猖毫,可以將值

設(shè)大

– Description

? Increasing valueà Tracking框的大小更新的較慢,但計(jì)算量較小

? Decreasing value à Tracking框的大小更新的較快须喂,但計(jì)算量較大

3.OBLoseTrackingFrm = 90 (default)

? OCLoseTrackingFrm = 90 (default)

– Usage:

? OB (object boundary):表示由于物體移出畫面外而造成的lose

tracking吁断。

OBLoseTrackingFrm表示如果連續(xù)多少張frame都沒有再成功

tracking,則需要user重新指定欲追蹤的物體坞生。

? OC(object occlusion):表示由于result model和target model相差太

多仔役,導(dǎo)致追蹤失敗,通常是因?yàn)槲矬w被擋住是己,或是移動得太快速又兵,

造成算法追不到。

OCLoseTrackingFrm表示如果連續(xù)多少張frame都沒有再成功

tracking,則需要user重新指定欲追蹤的物體沛厨。

– Range:

? 10~ 120

9.8全景拍照

9.8.1簡介

全景拍照運(yùn)行在Camera預(yù)覽模式宙地,抓取連續(xù)Preview buffer,通過算法將連續(xù)圖像拼接合成一張圖像逆皮。全景拍照適合對遠(yuǎn)景/亮度均勻場景拍攝宅粥。(mode調(diào)用流程參考face beauty)

9.8.2規(guī)則和限制條件

1.對近景/室內(nèi)場景進(jìn)行全景拍照,會在連接處出現(xiàn)拼接物體錯(cuò)位問題

算法限制

– 原因:近景物體在不同視角下成像形狀會有差異电谣,導(dǎo)致拼接時(shí)會有少許錯(cuò)位秽梅。

2.對亮度差異較大的場景做全景拍照,效果會不理想

3.純色場景由于算法限制剿牺,全景拍照效果不理想

9.8.3全景拍照客制化參數(shù)

(驅(qū)動)

? 可以客制化拍照張數(shù)企垦,默認(rèn)拍攝9張?jiān)倨唇樱?/p>

– PanoramaActor.java: NUM_AUTORAMA_CAPTURE9(Default Value)

– autorama_hal_base.h: MINT32 gImgEV[9] (DefaultValue)

9.8.4全景拍照常見問題

? 全景拍照拼接出現(xiàn)波浪現(xiàn)象

– 存在于MT6589/72/82等platform的版本

– 申請Patch:ALPS00931987

9.8.5全景拍照相關(guān)問題****debug****流程

? 全景拍照照片效果問題

– 首先,檢查是否是拍攝近景的限制問題牢贸,此類問題目前無法解法

– 其次竹观,檢查是否是拼接出現(xiàn)波浪形,如果是潜索,請申請相關(guān)patch

? 全景拍照使用過程中出現(xiàn)其他異常(例如:NE/定屏)等問題臭增。

– 抓取復(fù)現(xiàn)問題的mobile log,并提交e-service處理

9.9多視角拍照(****MAV****)

9.9.1簡介

多視角拍照(Multi Angle View)

– 在Camera預(yù)覽模式抓取連續(xù)多張Preview frame竹习,將多個(gè)角度的圖像合成一張MPO圖片誊抛。

橫屏拿手機(jī),圍繞被攝對象平穩(wěn)移動手機(jī)拍攝

– 若想拍下來的視角更廣整陌,需要移動速度更快

9.9.2限制與規(guī)則

目前MAV可拍攝的最大角度為250度拗窃,拍照角度與拍攝者圍繞被攝對象的移動速度有關(guān)。點(diǎn)擊拍照鍵觸發(fā)拍攝第一張照片泌辫,同時(shí)算法將此張?jiān)O(shè)置為參考幀随夸。當(dāng)算法檢測到場景發(fā)生一定變化時(shí)會觸發(fā)拍第二張,算法會根據(jù)前兩照片的相關(guān)信息推算是拍攝者的移動速度震放,并根據(jù)此移動速度抓取后面需要拍攝的幀宾毒,最終合成MPO文件。

9.9.3常見問題

MAV icon沒有顯示

– 需要在feature table的capture mode中添加MAV

– 如何修改Feature table可以參考MTK on-line相關(guān)FAQ

9.10情景照片****Live Photo

9.10.1簡介

當(dāng)選擇Live Photo拍照模式時(shí)诈铛,Camera開始進(jìn)行后臺錄像。當(dāng)拍照時(shí)墨礁,會保存最近的1-4s視頻

? 保存的視頻播放效果

– 先顯示視頻最后一幀1.5s

– 用最后一幀做一個(gè)500ms的動畫幢竹,最后停留在左下角

– 播放錄制的3s視頻,300ms后左下角小圖會淺出簡介

重要功能:

– Live photo會在background預(yù)錄(1~4s)Video恩静,動畫幀是background video的最后一幀

– Live Photo在Video Player中會循環(huán)播放

– Live Photo的Resolution會貼近Screen Size

– Live Photo的thumbnail為用于做動畫的最后一幀

– Live Photo模式下能滑動到Gallery牙丽,進(jìn)入Gallery后會stop recording,

重回Camera后會重新start recording

– Live Photo錄制下來的Video以“LIV”開頭换途。

9.10.2適用場景及使用方法

? 適用場景:適用任何場景

? 使用方法

– 進(jìn)入camera,選擇live photo拍照模式

– 按拍照鍵冠句,會出現(xiàn)“saving live photo….”字符串

– 保存完成后自動返回到preview

9.10.3限制條件

|

Capture

|

item

|

Description

|
|

setting相容

| | |
| |

Flash

|

Live Photo是在background錄像,無法在錄像過程中切換flash幸乒,因此flash被置

為off

|
| |

Camera Tab

|

Live Photo正在錄像懦底,所以無法設(shè)置與Camera相關(guān)的Setting

(如:ZSD、FD罕扎、ASD聚唐、Self timer、CS腔召、Picture Size杆查、Preview Size、ISO臀蛛、

FB)

|
| |

Voice Capture

|

Live Photo支援Voice Capture

|
|

function

| | |
| |

Preview時(shí)可滑動到Gallery

|

滑動到Gallery后停止后臺錄像亲桦,從Gallery滑回到Camera重新啟動后臺錄像

|
| |

后臺錄像開始和結(jié)束時(shí)不能有提

示音

|

為了達(dá)到后臺錄像效果,不能有提示音播放出來

|
| |

Capture時(shí)有提示音

|

點(diǎn)擊Capture Button浊仆,會有拍照的提示音播放出來

|
| |

循環(huán)播放

|

拍下來的Live Photo在MTK Video Player中能循環(huán)播放

|
| |

四個(gè)方向錄像

|

Live Photo產(chǎn)生的Video方向由Capture時(shí)手持方向決定

|
| |

thumbnail與動畫幀一致

|

Camera右下角的thumbnail跟live photo動畫幀是一致的

|
| |

后臺錄制的Video最后一幀為I

Frame

|

Live Photo需要拿background video的最后一幀做動畫客峭,因?yàn)樾枰屪詈笠粠瑸?/p>

I Frame

|
| |

Preview Size由向screen size

靠近

|

live photo preview過程中不能修改quality,選取屏的大小為參考標(biāo)準(zhǔn)

|
|

feature相容

| | |
| |

Continuous Shot

|

不支援CS

|
|

performance

| | |
| |

resolution

|

錄制下來的Video最大resolution為720p

|
| |

FPS

|

錄制下來的Video最低fps為24

|
| | | |
| | | |

下面限制將導(dǎo)致7s2aving時(shí)長變長

1.動畫幀是background video的最后一幀抡柿,且嵌在live photo的每一幀中

2.GPU處理每一幀的能力舔琅、Video Player decode每一幀的能力Live Photo與其他Capture Mode切換時(shí),Preview有卡頓現(xiàn)象

原因:Live Photo需要在后臺錄像洲劣,從Video mode (live phone相當(dāng)于video mode)切換到Camera Mode需要做stop preview與 start preview

9.11 HDR

9.11.1簡介

從一組曝光圖像中選擇一張曝光良好的圖像备蚓。

9.11.2 HDR 規(guī)則和限制條件

目前HDR的拍照速度在4-5秒

– HDR要拍-2EV,0EV囱稽,+2EV的三張照片郊尝,算法處理時(shí)間較長,因此耗時(shí)較久

– 若將CUST_HDR_CAPTURE_ALGORITHM配置成1 or 2战惊,在拍2張的case時(shí)虚循,比拍攝3張節(jié)省1S

– 后續(xù)chip的HDR時(shí)間,有較大改善

9.11.3 HDR****效果調(diào)試****/****客制化參數(shù)

? CUST_HDR_CAPTURE_ALGORITHM=1 (Default)

– Usage

? 決定HDR拍攝2張or 3張不同亮度的圖片

– Range

? 0~2

– Tuning purpose

? CUST_HDR_CAPTURE_ALGORITHM=0样傍,Always take 3 pictures

? CUST_HDR_CAPTURE_ALGORITHM=1,算法自動判斷需要拍攝

兩張還是三張

? CUST_HDR_CAPTURE_ALGORITHM=2铺遂,Always take 2 pictures

9.11.4 HDR****常見問題

? 使用8M sensor衫哥,插值到12M或者更高分辨率拍HDR時(shí)出現(xiàn)內(nèi)存不足

– 解決方法:以sensor實(shí)際輸出size做HDR算法,之后scale up到拍照size

? 需要打上patch:ALPS00909176襟锐、ALPS942214.

? 然后將

alps\mediatek\custom\common\hal\inc\camera_custom_hdr.h,將

define CUST_HDR_CAPTURE_POLICY 0改為: #define CUST_HDR_CAPTURE_POLICY 1

? 因?yàn)槭终鸪贩辏腍DR出現(xiàn)模糊/鬼影

– 使用burstshot抓取HDR需要的3幀數(shù)據(jù)

– Patch id:ALPS00876463 for MT6589/MT658

HDR圖片在PC上看不到縮略圖

– 需打patch解決

? ALPS00671141 for MT6589

? ALPS00986932 for MT6572

? 后臺播放music,前臺進(jìn)行HDR拍照,music與拍照音都出現(xiàn)卡頓現(xiàn)象蚊荣、有時(shí)候自動退出camera

– 需打Patch解決

? ALPS00987671 for MT6572

? ALPS00431395 for MT6589

10.Camera中第三方算法添加

10.1 Camera HAL層預(yù)覽

Camera接口圖:

圖片.png

Camera組件關(guān)系表

圖片.png

Camera Hal層組件:Image Buffer Queue, Client,Adapter,Camera Device, Camera Device manager, Params Manager

10.2 Camera圖像緩沖Queue

圖片.png

TODO Queue:用于保存未處理和準(zhǔn)備處理的圖片緩存

DONE Queue: 用于保存已處理或取消的圖片緩存

圖片.png

流程如下:

1 客戶處理端提供一個(gè) TODO buffer 給處理器(Processor)初狰;

2處理供應(yīng)端從供應(yīng)隊(duì)列(provider)里取出一個(gè)buffer;

3 處理獲取到的buffer(通過imageio);

4 處理供應(yīng)端把處理好的buffer輸入到供應(yīng)隊(duì)列互例;

5 客戶處理端從處理器中得到這個(gè)DONE buffer

6 客戶端再做一些其他的處理奢入。

10.3第三方****算法預(yù)覽部分

10.3.1 ExtImgProc介紹

客戶經(jīng)常為一些camera feature增加第三方圖片處理。

ExImgProc為增加拓展圖片處理和圖片集中控制流提供了一個(gè)簡單的方法媳叨。

其文件目錄如下:

1.common folder

圖片.png

2.Platform folder

圖片.png

根據(jù)項(xiàng)目不同腥光,目錄可能略有變化。

10.3.2 相關(guān)****類****結(jié)****構(gòu)圖

圖片.png

10.3.3 ExtImpProc處理過程

這么看來糊秆,他們是ExtImgProc的兩塊不同的緩存處理方法:COMMON , platform buffers

他們的實(shí)現(xiàn)流程如下:

1.COMMON部分處理內(nèi)容為: Display, Record, and PreviewCBclients(Common Buffer)

圖片.png

(1)取出消息

(2)把消息發(fā)送給ExtImgProc處理

(3)等待ExtImgProc返回?cái)?shù)據(jù)

(4)把處理結(jié)果返回給 APP

2.添加三方算法處理的流程(Common buffer)

(1)設(shè)置需要處理的Image buffer type

圖片.png

(2)添加三方算法

圖片.png

添加三方算法Samplecode:將預(yù)覽圖轉(zhuǎn)換為黑白圖像顯示:

圖片.png

(3)調(diào)用第三方處理函數(shù)(Common buffer)

圖片.png

3.平臺 BUFFER 處理 (Platform 部分)

正常流程處理****情況****:

圖片.png

(1)sensor 把數(shù)據(jù)發(fā)送給ISP Pass 1

(2)ISP pass1 把數(shù)據(jù)發(fā)送給MEM

(3)把數(shù)據(jù)發(fā)送給ExtImgProcHw處理

(4)等待消息處理返回

(5)把消息發(fā)送給ISP pass2 做輸入處理武福。

Two Run Pass2****處理****流程****:

當(dāng)程序處理圖片的過程比較復(fù)雜時(shí)觸發(fā),我們通過這個(gè)方法來減少所用的時(shí)間痘番。

圖片.png

調(diào)用3 方處理流程(Platform Buffer)

(1)設(shè)置需要處理的Platform Buffer type

圖片.png

(2)添加三方算法

圖片.png

(3)調(diào)用第三方處理函數(shù)(doImgProc)

圖片.png

調(diào)整格式和Buffer大凶狡:

圖片.png

10.3.4三方算法對性能影響的簡單分析

數(shù)據(jù)流程說明圖:

圖片.png

(1).Sensor 預(yù)覽幀率上3M@30fps;

(2).對Common buffer汞舱,如果ExtImgProc 過程多于33ms將丟幀伍纫;

(3).對Platform buffer,如果ExtImgProcHW過程多于20ms將丟幀兵拢;

(4).如果Display和Record buffers需要同樣的image 處理過程翻斟,ExtImgProcHW進(jìn)程只有一個(gè)buffer會減少系統(tǒng)loading. ExtImgProc過程(Display和Record buffer相互獨(dú)立)會有很多的性能問題。參考下表:

圖片.png

10.4拍照****的第三方算法添加

10.4.1移植capture****的第三方算法

移植步驟:

1 在路徑

vendor/mediatek/proprietary/platform/mtXXXX/hardware/mtkcam/D1/v1/adapter/Scenario/Shot(可能項(xiàng)目不同會有所區(qū)別)下新建一個(gè)shotclass(可命名為VendorShot)

2 VendorShot類需要實(shí)現(xiàn)3方專用的拍照流程说铃,主要步驟如下

(1)調(diào)用底層相關(guān)接口獲取JPEG ENC之前的YUV buffer

(2)進(jìn)行三方算法處理

(3)獲取postview數(shù)據(jù)(經(jīng)過3方算法處理)并顯示

(4)調(diào)用jpeg enc方法生成圖片

3 具體修改:參考如下:

(1)VendorShot::doCapture()

圖片.png

(2)VendorShot::createYUVFrame()

這個(gè)方法會調(diào)用SingleShot or burstShot來拍照访惜。在只獲取一張圖像的時(shí)候使用single ,在獲取多張或者需要獲取不同EV的圖像時(shí),需要使用burstShot

SingleShot獲取一張圖像

圖片.png

BurstShot來獲取多張圖像

圖片.png

通過BurstShot來獲取不同的EV的圖像


圖片.png

10.4.2對****ZSD 做****第三方算法處理:****(****CapBufShot.cpp****)

1.單張YUV

圖片.png

主要流程為:

(1)申請YUV BUFFER

(2)向底層singleshot注冊申請好的YUV Buffer

(3)從BUF QUE中獲取到RAW DATA的BUF

(4)調(diào)用singleshot相關(guān)接口將RAW DATA轉(zhuǎn)化為YUV

(5)進(jìn)行3方算法處理

(6)調(diào)用jpeg encode方法生成jpeg和thumbnail

多張連續(xù)YUV處理只是申請多塊YUV buffer 調(diào)用多次singleshot其余都一樣

圖片.png

2.獲取不同EV的YUV方法

獲取多張不同的EV的yuv data需要先將ZSD preview緩存的buffer修改為不同的EV的raw buffer腻扇,其他步驟與case2相同债热。將緩存的raw buffer 修改為不同的EV的主要步驟如下:

-修改3A相關(guān)文件,提供設(shè)置不同EV的接口幼苛;

-修改DefaultCtrlNode新增precap_for_zsd()函數(shù)窒篱,在函數(shù)中調(diào)用設(shè)置不同EV的3A接口;

-修改MtkDefaultCamAdapter舶沿,針對ZSD拍照墙杯,在capture之前調(diào)用DefaultCtrlNode的precap_for_zsd()函數(shù)。

3.獲取不同焦距的YUV方法

獲取多張不同的EV的yuv data需要先將ZSD preview緩存的buffer修改為不同的焦距的raw buffer括荡,其他步驟與case2相同高镐。

10.5 新增一個(gè)Capture Mode

在如下文件添加相應(yīng)參數(shù):

圖片.png

11.拍照、錄像接口調(diào)用及開發(fā)

Android中Camera的使用畸冲,一是拍照嫉髓,二是攝像观腊,由于Android提供了強(qiáng)大的組件功能,為此對于在Android手機(jī)系統(tǒng)上進(jìn)行 Camera的開發(fā)算行,可以使用兩類方法:一是借助Intent和MediaStroe調(diào)用系統(tǒng)Camera App程序來實(shí)現(xiàn)拍照和攝像功能梧油,二是根據(jù)Camera API自寫Camera程序。

11.1 調(diào)用系統(tǒng)Camera APP實(shí)現(xiàn)功能

第三方APP用到Camera的需求就是獲取照片或者視頻州邢,比如社交SNS應(yīng)用儡陨、隨手記、短信等偷霉,借助Android系統(tǒng)強(qiáng)大的組件特性迄委,使得應(yīng)用開發(fā)者只需通過Intent就可以方便的打開系統(tǒng)自帶的Camera APP,并通過MediaStroe方便地獲取照片和視頻的文件路徑类少。

11.1.1 **實(shí)現(xiàn)拍照 **

在第三方應(yīng)用的菜單或按鈕的選擇操作中調(diào)用如下代碼叙身,開啟系統(tǒng)自帶Camera APP,并傳遞一個(gè)拍照存儲的路徑給系統(tǒng)應(yīng)用程序硫狞,實(shí)現(xiàn)如下:

imgPath = "/sdcard/test/img.jpg"; //必須確保文件夾路徑存在信轿,否則拍照后無法完成回調(diào)

File vFile = new File(imgPath);

if(!vFile.exists()) {

File vDirPath = vFile.getParentFile(); //new File(vFile.getParent());

vDirPath.mkdirs();

}

Uri uri = Uri.fromFile(vFile);

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);//

startActivityForResult(intent, SystemCapture);

上面使用的是startActivityForResult,所以最好需要重載void onActivityResult(int requestCode, int resultCode, Intent data)方法残吩,不過因?yàn)橐呀?jīng)傳入文件路徑的的原因财忽,data返回參數(shù)是null值,只要resultCode為RESULT_OK泣侮,則上述代碼中 /sdcard/test/img.jpg的圖片文件就是最新的照片文件即彪。所以只需使用如下簡單代碼,將其顯示到ImageView中

if (resultCode == RESULT_OK) {

iViewPic.setImageURI(Uri.fromFile(new File(imgPath)));

}

假設(shè)不傳參數(shù)MediaStore.EXTRA_OUTPUT的情況下活尊,onActivityResult函數(shù)在resultCode為RESULT_OK的情況下隶校,data返回的參數(shù)是經(jīng)過實(shí)際拍攝照片經(jīng)過縮放的圖像數(shù)據(jù),可以通過類似如下方法來打印縮放圖像的尺寸

if (resultCode == RESULT_OK) {

Bitmap bmp = (Bitmap)data.getExtras().get("data");

}

假如僅僅是調(diào)用系統(tǒng)照相機(jī)拍照蛹锰,不關(guān)心拍照結(jié)果深胳,則可以使用如下代碼

Intent intent = new Intent(); //調(diào)用照相機(jī)

intent.setAction("android.media.action.STILL_IMAGE_CAMERA");

startActivity(intent);

對于設(shè)置MediaStore.EXTRA_OUTPUT的方法,除了設(shè)定的路徑下有照片外铜犬,在手機(jī)存儲卡上也會保存一份照片舞终,默認(rèn)目錄為sdcard/dcim/camera下面。

11.1.2 **實(shí)現(xiàn)攝像 **

第三方應(yīng)用需要攝像功能時(shí)癣猾,通過設(shè)置MediaStore.EXTRA_OUTPUT以傳入類似拍照時(shí)的文件路徑敛劝,得到視頻文件是一個(gè)0k的空文件,說明不能通過同樣的方法調(diào)用視頻拍攝功能纷宇】涿耍可以通過如下代碼實(shí)現(xiàn)

Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);

intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);//參數(shù)設(shè)置可以省略

startActivityForResult(intent, SystemVideoRecord);

在onActivityResult函數(shù)中進(jìn)行如下代碼調(diào)用

Uri videoUri = data.getData();

//String[] projection = { MediaStore.Video.Media.DATA, MediaStore.Video.Media.SIZE };

Cursor cursor = managedQuery(videoUri, null, null, null, null);

cursor.moveToFirst();//這個(gè)必須加,否則下面讀取會報(bào)錯(cuò)

int num = cursor.getCount();

String recordedVideoFilePath

= cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA));

int recordedVideoFileSize

= cursor.getInt(cursor.getColumnIndex(MediaStore.Video.Media.SIZE));

iResultText.setText(recordedVideoFilePath);

返回參數(shù)data呐粘,也會因?yàn)槭欠裨O(shè)置MediaStore.EXTRA_OUTPUT參數(shù)而改變满俗,如果沒有通過EXTRA_OUTPUT設(shè)置路徑,data.getData返回的Uri為content://media/external/video/media/作岖,個(gè)數(shù)字唆垃,代表具體的記錄號,通過managedQuery可以獲取到路徑痘儡,如果設(shè)置了EXTRA_OUTPUT (比如/sdcard/test.3gp)辕万,則 data.getData返回的Uri則為[file:///sdcard/test.3gp,但是該文件大小為0](file:///sdcard/test.3gp沉删,但是該文件大小為0)渐尿。

11.2調(diào)用Camera API實(shí)現(xiàn)相應(yīng)功能

通過調(diào)用系統(tǒng)Camera App實(shí)現(xiàn)拍照和攝像功能,雖然能夠滿足需求矾瑰,但是畢竟自由度降低了砖茸,而且拍照的界面就是系統(tǒng)的樣子泰鸡,要實(shí)現(xiàn)個(gè)性的拍照可復(fù)雜的功能就需要根據(jù)SDK提供的Camera API來編寫應(yīng)用程序刽宪。

11.2.1添加權(quán)限

通過調(diào)用系統(tǒng)Camera App刁笙,不需要任何權(quán)限毫胜,因?yàn)闄?quán)限已經(jīng)在Camera APP中聲名赠制,但是如果使用Camera API惯悠,就必須在manifest清單文件中聲明使用權(quán)限躬络,通常由以下三項(xiàng)

<uses-permission android:name = "android.permission.CAMERA" />

<uses-feature android:name = "android.hardware.camera" />

<uses-feature android:name = "android.hardware.camera.autofocus" />

一般拍照和攝像的時(shí)候需要寫到sd卡上蜡吧,所以還有一向權(quán)限聲明如下

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

進(jìn)行攝像時(shí)休傍,需要音頻錄制和視頻錄制功能征绎,所以還需要下面兩項(xiàng)權(quán)限聲明

<uses-permission android:name="android.permission.RECORD_VIDEO"/>

<uses-permission android:name="android.permission.RECORD_AUDIO"/>

另外使用Camera API拍照或攝像,都需要用到預(yù)覽磨取,預(yù)覽就要用到SurfaceView人柿,為此Activity的布局中必須有SurfaceView。

11.2.2****實(shí)現(xiàn)拍照功能

(1)在Activity的OnCreate函數(shù)中設(shè)置好SurfaceView寝衫,包括設(shè)置SurfaceHolder.Callback對象和SurfaceHolder對象的類型顷扩,具體如下

SurfaceView mpreview = (SurfaceView) this.findViewById(R.id.camera_preview);

SurfaceHolder mSurfaceHolder = mpreview.getHolder();

mSurfaceHolder.addCallback(this);

mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

(2)在SurfaceHolder.Callback的surfaceCreated方法中,使用Camera的Open方法打開攝像頭硬件慰毅,這個(gè)API在 SDK 2.3之前隘截,是沒有參數(shù)的,2.3以后支持多攝像頭汹胃,所以開啟前可以通過getNumberOfCameras先獲取攝像頭數(shù)目婶芭,再通過 getCameraInfo得到需要開啟的攝像頭id,然后傳入Open函數(shù)開啟攝像頭着饥,假如攝像頭開啟成功則返回一個(gè)Camera對象犀农,否則就拋出異常。

(3)開啟成功的情況下宰掉,在SurfaceHolder.Callback的surfaceChanged函數(shù)中調(diào)用 getParameters方法得到已打開的攝像頭的配置參數(shù)Parameters對象呵哨,如果有需要就修改對象的參數(shù)赁濒,然后調(diào)用 setParameters函數(shù)設(shè)置進(jìn)去(SDK2.2以后,還可以通過Camera::setDisplayOrientation設(shè)置方向)孟害;

(4)同樣在surfaceChanged方法中拒炎,通過Camera::setPreviewDisplay為攝像頭設(shè)置SurfaceHolder對象,設(shè)置成功后調(diào)用Camera::startPreview方法開啟預(yù)覽功能挨务,上面3,4兩步的代碼如下所示

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {

//已經(jīng)獲得Surface的width和height击你,設(shè)置Camera的參數(shù)

Camera.Parameters parameters = camera.getParameters();

parameters.setPreviewSize(w, h);

List<Size> vSizeList = parameters.getSupportedPictureSizes();

for(int num = 0; num < vSizeList.size(); num++) {

Size vSize = vSizeList.get(num);

}

if(this.getResources().getConfiguration().orientation

!= Configuration.ORIENTATION_LANDSCAPE) {

//如果是豎屏

parameters.set("orientation", "portrait");

//在2.2以上可以使用

//camera.setDisplayOrientation(90);

} else {

parameters.set("orientation", "landscape");

//在2.2以上可以使用

//camera.setDisplayOrientation(0);

}

camera.setParameters(parameters);

try {

//設(shè)置顯示

camera.setPreviewDisplay(holder);

} catch (IOException exception) {

camera.release();

camera = null;

}

//開始預(yù)覽

camera.startPreview();

}

(5)如果要支持自動對焦功能,則在需要的時(shí)候谎柄,或者在上述surfaceChanged調(diào)用完startPreview方法后丁侄,可以調(diào)用 Camera::autoFocus方法來設(shè)置自動對焦回調(diào)函數(shù),該步是可選操作朝巫,有些設(shè)備可能不支持鸿摇,可以通過 Camera::getFocusMode函數(shù)查詢。代碼如下:

// 自動對焦

camera.autoFocus(new AutoFocusCallback() {

@Override

public void onAutoFocus(boolean success, Camera camera) {

if (success) {

// success為true表示對焦成功捍歪,改變對焦?fàn)顟B(tài)圖像

ivFocus.setImageResource(R.drawable.focus2);

}

}

});

(6)在需要拍照的時(shí)候户辱,調(diào)用takePicture(Camera.ShutterCallback, Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)方法來完成拍照,這個(gè)方法中有四個(gè)回調(diào)接口糙臼,ShutterCallback是快門按下的回調(diào)庐镐,在里面可以設(shè)置播放“咔嚓”聲之類的操作,后面有三個(gè)PictureCallback接口变逃,分別對應(yīng)三份圖像數(shù)據(jù)必逆,分別是原始圖像、縮放和壓縮圖像和JPG 圖像揽乱,圖像數(shù)據(jù)可以在PictureCallback接口的void onPictureTaken(byte[] data, Camera camera)中獲得名眉,三份數(shù)據(jù)相應(yīng)的三個(gè)回調(diào)正好按照參數(shù)順序調(diào)用,通常只關(guān)心JPG圖像數(shù)據(jù)凰棉,此時(shí)前面兩個(gè)PictureCallback接口參 數(shù)可以直接傳null损拢。

(7)每次調(diào)用takePicture獲取圖像后,攝像頭會停止預(yù)覽撒犀,如果需要繼續(xù)拍照福压,則在上面的PictureCallback的onPictureTaken方法末尾,再次調(diào)用Camera::startPreview方法或舞;

(8)在不需要拍照的時(shí)候荆姆,主動調(diào)用Camera::stopPreview方法停止預(yù)覽功能,并且調(diào)用Camera::release方法釋放 Camera映凳,以便其他應(yīng)用程序調(diào)用胆筒。SDK中建議放在Activity的Pause方法中,也可以放在surfaceDestroyed方法中诈豌,代碼如下

// 停止拍照時(shí)調(diào)用該方法

public void surfaceDestroyed(SurfaceHolder holder) {

// 釋放手機(jī)攝像頭

camera.release();

}

以上是實(shí)現(xiàn)拍照程序的的流程仆救,一般還可以還可以獲取預(yù)覽幀的圖像數(shù)據(jù)抒和,可以分別通過Camera::setPreviewCallback和 Camera::setOneShotPreviewCallback來設(shè)置每幀或下一幀圖像數(shù)據(jù)的回調(diào)。

11.2.3實(shí)現(xiàn)攝像功能

攝像也是需要預(yù)覽的彤蔽,而且流程上與拍照流程的(1)~(4)步和最后一步(即步驟(8))是一樣的构诚,唯一不同的是(6)和(7)兩個(gè)步驟,至于(5)自動對焦本身就是可選的铆惑,在攝像流程也沒必要。

(6)開啟視頻錄制送膳,需要創(chuàng)建一個(gè)MediaRecorder對象员魏,并調(diào)用Camera::unLock操作解鎖攝像頭,因?yàn)槟J(rèn)Camera都是鎖定的叠聋,只有解鎖后MediaRecorder等多媒體進(jìn)程調(diào)用撕阎,并設(shè)置一些參數(shù),然后調(diào)用MediaRecorder:: start開啟錄制碌补,參考如下代碼:

MediaRecorder mMediaRecorder = new MediaRecorder();

camera.unlock();

mMediaRecorder.setCamera(camera);

mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);

mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

mMediaRecorder.setProfile(mProfile);

mMediaRecorder.setMaxDuration(100000);//ms為單位

long dateTaken = System.currentTimeMillis();

Date date = new Date(dateTaken);

SimpleDateFormat dateFormat

= new SimpleDateFormat(getString(R.string.video_file_name_format));

String title = dateFormat.format(date);

String filename = title + ".3gp"; // Used when emailing.

String cameraDirPath = ImageManager.CAMERA_IMAGE_BUCKET_NAME;

String filePath = cameraDirPath + "/" + filename;

File cameraDir = new File(cameraDirPath);

cameraDir.mkdirs();

mMediaRecorder.setOutputFile(filePath);

try {

mMediaRecorder.prepare();

mMediaRecorder.start(); // Recording is now started

} catch (RuntimeException e) {

return;

}

(7)上面設(shè)置了最大間隔為100s虏束,當(dāng)100是視頻錄制結(jié)束,錄制就會被停止厦章,如果沒有設(shè)時(shí)長和文件大小限制镇匀,那么通常需要調(diào)用MediaRecorder:: stop函數(shù)主動停止視頻的錄制,并將Camera對象通過lock函數(shù)繼續(xù)加鎖袜啃,代碼如下

mMediaRecorder.stop();

mMediaRecorder.reset();

mMediaRecorder.release();

mMediaRecorder = null;

if(camera != null)

camera.lock();

之后的操作根據(jù)交互要么重新錄制要么就釋放Camera對象回到拍照流程的(8)步驟了汗侵。

12.語音拍照詳解

目前,語音識別功能可以支持的應(yīng)用有(phone,camera,Alarm 和voiceUnlock)在設(shè)置菜單里群发,這些項(xiàng)默認(rèn)都是關(guān)閉的晰韵。目前支持的語言有(Chiese-Mandarin,Chiese-Taiwan, and English 并且默認(rèn)為中文)。

圖片.png

語言拍照控制所涉及到的VoiceUi包括:

1設(shè)置各個(gè)應(yīng)用是否開啟語言識別的開關(guān)熟妓。

2增加第三方應(yīng)用支持設(shè)置雪猪。

3增加關(guān)鍵詞支持。

4更改默認(rèn)設(shè)置起愈。

5更改默認(rèn)開關(guān)只恨。

6增加多語言支持。

7增加語言拍照和語言設(shè)置的關(guān)聯(lián)告材。

12.1設(shè)置界面****應(yīng)用****是否增加****開啟****坤次、****關(guān)閉語言識別的開關(guān)

去除和添加的方法

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/res/xml/voiceprocessinfo.xml

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/assets/keyword/1.xml

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/assets/keyword/2.xml

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/assets/keyword/3.xml

圖片.png

keyword/1.xml

圖片.png

keyword/2.xml

圖片.png

keyword/3.xml

圖片.png

12.****2****增加****第三方****應(yīng)用****支持****設(shè)置

涉及到如下文件

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/res/xml/voiceprocessinfo.xml

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/assets/keyword/1.xml

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/assets/keyword/2.xml

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/assets/keyword/3.xml

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/src/com/mediatek/voicecommand/ui/settings/VoiceUiResUtil.java

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/res

1.****voiceprocessinfo.xml

圖片.png

2.****keyword/1.xml

圖片.png

3.****keyword/2.xml

圖片.png

4.****keyword/3.xml

圖片.png

5.****VoiceUiResUtil.java

圖片.png

各個(gè)voicecommond界面的字段(修改增加)方法如下:

(1).

圖片.png
圖片.png

(2).icon

圖片.png
圖片.png

(3).title app name

圖片.png
圖片.png

(4).title

圖片.png
圖片.png

12.3增加第三方****應(yīng)用****支持****的關(guān)鍵詞

涉及文件:

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/res/xml/voiceprocessinfo.xml

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/assets/keyword/1.xml

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/assets/keyword/2.xml

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/assets/keyword/3.xml

/trunk/vendor/mediatek/proprietary/frameworks/base/voicecommand/cfg/command/camera

/trunk/vendor/mediatek/proprietary/frameworks/base/voicecommand/cfg/voicecommand.mk

詳細(xì)介紹一下:

1.****voiceprocessinfo.xml

圖片.png

2.****keyword/1.xml

圖片.png

keyword/2.xml同keyword/1.xml

keyword/3.xml同keyword/1.xml

3.****/command/camera

圖片.png

4.****voicecommand.mk

圖片.png

12.4更改****默認(rèn)設(shè)置

涉及文件

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/res/xml/voiceprocessinfo.xml

圖片.png

12.****5****更改****默認(rèn)****語言

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/res/xml/voicelanguage.xml

圖片.png

12.6增加新****的語言支持

涉及文件

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/res/xml/voicelanguage.xml

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/assets/

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/cfg/command/

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/cfg/voicecommand.mk

1.****voicelanguage.xml

圖片.png

2.****assets/

圖片.png

3.****/cfg/command/

圖片.png

4.****voicecommand.mk

圖片.png

12.7增加語言****拍照****和****語言設(shè)置的關(guān)聯(lián)

涉及文件:

/trunk/vendor/mediatek/proprietary/packages/apps/VoiceCommand/res/xml/voicecustomization.xml

圖片.png

Note :如果這個(gè)設(shè)置成TRUE 了后面的defaultlanguage 不需要關(guān)注了

13.Camera Performance問題分析初步

13.1 Camera startup time

定義:從”Touch Camera Icon抬起手”到”屏幕上第一幀畫面顯示出來”的時(shí)間。

13.1.1Camera startup time 的不同模式差異分析

1.不同LCD size斥赋、全屏和非全屏的區(qū)別

從Camera Native和HAL層的角度看缰猴,他們會有不同的Preview Size,因此有不同的Preview Buffer大小疤剑,其它部分都一樣滑绒。

2.橫豎屏闷堡、解鎖進(jìn)入Camera的區(qū)別

主要是APP和Framework的行為差異(如AMS,WMS):原理上是啟動Activity疑故,onCreate()被執(zhí)行的時(shí)間差異杠览,Camera Native層以下的部分是相同的。

3.ZSD和Normal模式的主要區(qū)別

(1).start preview 的start階段需要allocate 幾塊capture buffer (數(shù)量可以客制化纵势,默認(rèn)為3).

(2).config sensor時(shí)踱阿,driver在normal時(shí)call preview setting,zsd時(shí)call capture setting。

(3).zsd pewview的幀率可能會低一些钦铁。

4.zsd cc與ncc的主要區(qū)別

在start preview 的start階段:zsd cc allocate的capture buffer 為3塊pass2 yuv(capture size)和1塊thumbnail yuv buffer(preview size)软舌。而zsd ncc為allocate 3塊pass1 raw(sensor full size)。

13.1.2Camera startup time 分析LOG

選取以下時(shí)間點(diǎn)的LOG(ZSD牛曹、Normal通用):

1.點(diǎn)擊icon后手抬起來:AP_PROF:AppLaunch_dispatchPtr:Up

2.Camera AP開始create:parseIntent() mPickType

3.開始connect CameraService:CameraService::connect E

4.connect CameraService 結(jié)束:CameraService::connect X

5.CameraService start preview開始:startPreview(pid)

6. CameraService start preview結(jié)束:[onJandleStartPreview]-

(ZSD時(shí)LOG為(StateIdle)[onStartPreview]-status)

7.first frame post from CameraService:[handleReturnBuffers] Show frame

8.first frame available for AP :onFirstFrameArrived()

每兩個(gè)時(shí)間點(diǎn)之間的時(shí)間命名如下,其中All是1->8總的時(shí)間佛点。紅色主要和AP有關(guān),藍(lán)色和底層CameraService有關(guān),綠色和AP,低層和CameraService都有關(guān)系,但主要是AP黎比。

圖片.png

13.1.3Camera startup time 的參考數(shù)據(jù)

1.Normal Mode超营,豎屏多次反復(fù)進(jìn)入Camera。

圖片.png

第一欄的橙色數(shù)據(jù)為開機(jī)后第一次進(jìn)入豎屏進(jìn)入Camera時(shí)的數(shù)據(jù)阅虫。

2.ZSD NCC mode演闭,豎屏多次反復(fù)進(jìn)入Camera
圖片.png

13.1.4Camera startup time各階段愛客制化影響的主要因素

1.Connect CS

(1).LoadSound時(shí)間,若有改動拍照聲音或錄像聲音文件颓帝,需要check此處:

查看main log:CameraService:[CameraService::loadSound]和最后一個(gè)

[CameraService::newMediaPlayer]-之間的時(shí)間間隔.

(2).Sensor上電時(shí)間 不同的Sensor,power on時(shí)間會有差異船响,查看kernel log:

[[kd_sensorlist]] [kdModulePowerOn]Profile = 16490.(單位為ns)

(3)Sensor driver open時(shí)間,不同的sensor,open的時(shí)間會有差異躲履,

查看kernel log:[[kd_sensorlist]][SensorOpen]Profile = 16834. (單位為ns)

2.CS Start Preview

(1)Preview delay frame數(shù)量:

delay frames for 3A statistic(raw:3,yuv:由sensor driver設(shè)定)见间。

圖片.png

delay frames for sensor stable.

由sensor driver 設(shè)定,一般在xxx_Sensor.c中

圖片.png

查看main log:MtkCam/PrvCQT(21541)[delay] delay(init) 3A(3)+sensor(1).

(2).Preview fps,可查看log:[updateOne]P2之間的時(shí)間間隔工猜。當(dāng)preview達(dá)到30fps時(shí)米诉,log:兩個(gè)相鄰[updateOne]P2的時(shí)間間隔為33ms.

(3).Driver preview(zsd時(shí):capture)settings,不同的Sensor所需要設(shè)定寄存器數(shù)量不同,并且里面可能會增加一些delay.

可以查看log:[start]與[init]meSensorDev之間的間隔時(shí)間篷帅。

3.First Frame

Preview fps峭沦,可以查看log:[updateOne]P2之間的時(shí)間間隔左胞。當(dāng)preview達(dá)到30fps時(shí),log:[upateOne]P2的時(shí)間間隔為33ms.

13.2 Shot to Shot/shutter delay

定義:

1.Shot2Shot:從”touch shutter button icon抬起手”到”回preview后屏幕上第一幀畫面顯示出來(shutter button再次被enable)”的時(shí)間。

2.Shutter delay:” touch shutter button icon抬起手時(shí)看到的那一幀”與”實(shí)際拍出來的圖片”的時(shí)間差.

13.2.1 Shot to Shot/shutter delay分析LOG

1.Normal mode選取以下時(shí)間點(diǎn)的log

(1).點(diǎn)擊icon后抬起手來:AP_PROF:AppLaunch_dispatchPtr:Up

(2).CameraService take picture start:takePicture(pid)

(3).Precapture start:[onHandlePreCapture]+ (YUV sensor會直接跳過)

(4).Stop preview start:[onHandleStopPreview] +

(5).Hal capture start:(StateIdle)[onCapture] +

(6).Create raw image end:createSensorRawImg]:(1-th)

(7).Create yuv image end:createYuvRawImg]: (2-th)

(8).Create jpeg image end:[handleJpegData] +

(9).Hal capture end:[ImpShot]-

(10).CameraService start preview開始:startPreview(pid)

(11).CameraService start preview結(jié)束:[onHandleStartPreview]-

(12).第一個(gè)preview frame被post:[handleReturnBuffers] Show frame

每兩個(gè)時(shí)間點(diǎn)之間的時(shí)間命名如下帖蔓,其中S2S是1->12總的時(shí)間埋涧,Shutter Delay是1->6的時(shí)間醇坝。紅色主要和AP有關(guān)宋距,藍(lán)色和低層CameraService有關(guān)壶唤。

圖片.png

2.ZSD NCC mode 選取以下時(shí)間點(diǎn)的log

(1).點(diǎn)擊icon后抬起手來:AP_PROF:AppLaunch_dispatchPtr:Up

(2).CameraService take picture start:takePicture(pid)

(3).Precapture start:[onHandlePreCapture]+ (若不打閃,什么都不做)

(4).Stop preview start:[onHandleStopPreview] +

(5).Hal capture start:(StateIdle)[onCapture] +

(與Normal不同的地方笑撞,沒有pass1)

(6).Create yuv image end:createYuvRawImg]: (2-th)

(7).Create jpeg image end:[handleJpegData] +

(8).Hal capture end:[ImpShot]-

(9).CameraService start preview開始:startPreview(pid)

(10).CameraService start preview結(jié)束:StateIdle)[onStartPreview]-status

(11).第一個(gè)preview frame被post:[handleReturnBuffers] Show frame

每兩個(gè)時(shí)間點(diǎn)之間的時(shí)間命名如下瞬铸,其中S2S是1->11總的時(shí)間拦宣,紅色主要和AP有關(guān)豆瘫,藍(lán)色和低層CameraService有關(guān)昵宇。

圖片.png

ZSD NCC是抓取存儲下來的pass1 raw buffer經(jīng)過pass2的處理去壓jpeg,可以通過調(diào)整存儲buffer的數(shù)量來改變shutter delay time.

3.ZSD CC mode選取以下時(shí)間點(diǎn)的LOG

(1).點(diǎn)擊icon后抬起手來:AP_PROF:AppLaunch_dispatchPtr:Up

(2).CameraService take picture start:takePicture(pid)

(3).Precapture start:[onHandlePreCapture]+ (若不打閃,什么都不做)

(4).Hal capture start:(StatePreCapture)[onCapture] +

(與NCC不同的地方雷恃,直接壓jpeg)

(5).Create jpeg image end:[handleJpegData] +

(6).Hal capture end:[ImpShot]-

(7).Camera:handleMessage:256

每兩個(gè)時(shí)間點(diǎn)之間的時(shí)間命名如下兜看,其中S2S是1->7總的時(shí)間契讲,紅色主要和AP有關(guān),藍(lán)色和低層CameraService有關(guān)状土。

圖片.png

ZSD NCC是抓取存儲下來的pass2 yuv buffer去壓jpeg,可以通過調(diào)整存儲buffer的數(shù)量來改變shutter delay time.

ZSD CC preview不停拉鹃,Shot2Shot time 主要和拍照時(shí)的動畫有關(guān)。

13.2.2參考數(shù)據(jù)

1.Normal Shot鼎姐,豎屏多次拍照(Shot to Shot/shutter delay time)

圖片.png

最后一欄的橙色數(shù)據(jù)為測試環(huán)境變暗時(shí)的拍照數(shù)據(jù)艘策。(幀率變低雀鹃,shot to shot 時(shí)間變長)

2.ZSD NCC Shot,豎屏多次拍照(Shot to Shot time)

圖片.png

13.2.3各階段受客制化影響的主要因素

1.Pre Capture(非ZSD mode,且為raw sensor)

Preview fps,可查看log:[updateOne] P2之間的時(shí)間間隔励两,preview達(dá)到30fps時(shí)黎茎,log:

[updateOne] P2的時(shí)間間隔為33ms.

2.Pass1(非ZSD mode)

(1).Driver capture settings,不同的sensor所需要設(shè)定的寄存數(shù)量不同,并且里面可能會有加一些delay,(在82/92上面main log已經(jīng)沒有打印出該信息当悔,需要自行在sensor driver文件中計(jì)算capture setting的時(shí)間)

(2).Capture delay frame數(shù)量(driver設(shè)定)

圖片.png

可查看main log:campipe/io:[skipFrame]+(u4SkipCount) = (1).

(3).Capture fps傅瞻,可查看log:campipe/io(140):[start]-和campipe/io(140):[stop]+之間的時(shí)間間隔,這是收一個(gè)frame的時(shí)間盲憎。

3.CS Start Preview(Normal,ZSD NCC mode)

(1).Preview fps:

(2).Preview delay frame數(shù)量:

(3).Driver Preview setting.

4.First Frame(Normal,ZSD NCC mode)

Preview fps

13.3 main/sub sensor switch time

定義:從”touch camera switch icon 抬起手”到”切換后屏幕上第一幀畫面顯示出來”的時(shí)間嗅骄。

13.3.1 Shot to Shot/shutter delay分析LOG

選取以下時(shí)間點(diǎn)的log:

(1).點(diǎn)擊icon后抬起手來:AP_PROF:AppLaunch_dispatchPtr:Up

(2).CameraService開始stop preview:stopPreview(pid)

(3). CameraService stop preview結(jié)束:[onHandleStopPreview]-

(4).開始disconnect CameraService: disconnect E

(5). disconnect CameraService結(jié)束: disconnect X

(6).開始connect CameraService: CameraService :: connect E

(7). connect CameraService結(jié)束: CameraService :: connect X

(8). CameraService 開始start preview開始:startPreview(pid)

(9). CameraService start preview結(jié)束:[onStartPreview]-

(10).get到第一個(gè)preview frame:[handleReturnBuffers] Show frame

每兩個(gè)時(shí)間點(diǎn)之間的時(shí)間命名如下,其中Main to Sub/Sub to Main是1->10總的時(shí)間饼疙。紅色主要和AP有關(guān)溺森,藍(lán)色和低層CameraService有關(guān),綠色和AP,低層和CameraService都有關(guān)系,但主要是AP窑眯。

圖片.png

客制化影響sensor switch time的主要因素

同camera startup 相同屏积。

13.3.2參考數(shù)據(jù)

相關(guān)信息:

圖片.png
圖片.png

上面數(shù)據(jù)為main to sub的切換。后兩欄橙色部分時(shí)間有變長磅甩,主要是因?yàn)闇y試環(huán)境變暗炊林,幀率下降。

13.4目前平臺可優(yōu)化的地方

13.4.1 Stop preview節(jié)省幀率

流程上在stop preview的地方有一處可以刪掉卷要,可節(jié)省一幀的時(shí)間(對shot to shot 和sensor switch的performance優(yōu)化有效)

在VSSScenario.cpp

(mediatek/proprietary/platform/mt6582/hardware/mtkcam/core/hwscenario)

MBOOL VSSScenario::stop()

{

CAM_TRACE_NAME("VSSScen::stop");

FUNCTION_LOG_START;

//

PortID rPortID;

mapPortCfg(eID_Pass1Out, rPortID);

PortQTBufInfo dummy(eID_Pass1Out);

CAM_TRACE_BEGIN("CamIOPipe::dequeOutBuf");

mpCamIOPipe->dequeOutBuf(rPortID, dummy.bufInfo);//此行可刪除

CAM_TRACE_END();

//

CAM_TRACE_BEGIN("CamIOPipe::stop");

if ( ! mpCamIOPipe->stop())

{

    MY_LOGE("mpCamIOPipe->stop() fail");

    CAM_TRACE_END();

    return MFALSE;

}

CAM_TRACE_END();

//

FUNCTION_LOG_END;

//

return MTRUE;

}

13.4.1拍照回顯定格時(shí)間較長

有的項(xiàng)目感覺拍照時(shí)回顯定格時(shí)間較久渣聚,shot to shot時(shí)間長,很晚才start preview,原因是mtk camera app會在jpeg call back之后delay 1秒多的時(shí)間才restart preview却妨。對應(yīng)代碼如下:

在PhotoActor.java(packages/app/camera/src/com/android/camera/actor)中:

private PictureCallback mJpegPictureCallback = new PictureCallback() {

    ……

            if (previewFeature.isDelayRestartPreview()) {

                 long delay = IMAGE_DISPLAY_DURATION
  • mPictureDisplayedToJpegCallbackTime;

                   if (delay <= 0) {
    
                       restartPreview(true);
    
                   } else {
    
                       Message msg = mHandler.obtainMessage(RESTART_PREVIEW);
    
                       mHandler.sendMessageDelayed(msg, delay);
    
                   }
    
              } else {
    
                  restartPreview(true);
    
              }
    
     ……
    

};

若不想delay,把如上代碼直接修改為restartPreview(true);即可.

14.HQ Camera整體優(yōu)化方案介紹

本章節(jié)主要是針對HQ在Camera上的一個(gè)不斷改進(jìn)的計(jì)劃.

14.1改進(jìn)思路

1.從用戶使用角度發(fā)掘Camera體驗(yàn)的改善點(diǎn)

2.Camera基礎(chǔ)效果優(yōu)化(持續(xù)進(jìn)行中)

3.預(yù)覽&拍照特效類優(yōu)化

4.其他影響體驗(yàn)的細(xì)節(jié)優(yōu)化

14.2用戶需求(競品機(jī)優(yōu)勢)

1.自拍功能(前攝)

– 大視角(80~85度)

– 進(jìn)入自拍速度快

– 自拍的效果好

– 有各種美化和修飾功能

– 方便的拍照方式

2.主拍照功能

– 優(yōu)秀的拍照效果

– 快速的對焦和拍照

– 針對不同場景的特定拍照模式

– 連拍功能和快速的連拍速度

14.3 Camera基礎(chǔ)效果優(yōu)化(持續(xù)進(jìn)行中)

| |

改善思路和當(dāng)前進(jìn)展

|
|

Camera基礎(chǔ)效果優(yōu)化

|

1.Camera器件選型(sensor&Lens&馬達(dá)饵逐。括眠。)

2.精細(xì)化的調(diào)試標(biāo)準(zhǔn)

3.專業(yè)的調(diào)試實(shí)驗(yàn)室和調(diào)試小組人員

4.精品試點(diǎn)項(xiàng)目落地導(dǎo)入

5.發(fā)現(xiàn)并彌補(bǔ)華勤AVL廠商和行業(yè)頂尖供應(yīng)商的技術(shù)及制程能力的差距(附AL900的改善報(bào)告)

|
|

當(dāng)前進(jìn)展

|

以各事業(yè)部的精品試產(chǎn)項(xiàng)目進(jìn)行落地彪标,從器件選型,調(diào)試和評測三部分保證camera圖像的品質(zhì)和質(zhì)量掷豺。

|

14.4 軟件特效類

| |

特效細(xì)節(jié)

|
|

自拍特效處理

|

特效類:

1.自拍實(shí)時(shí)美顏(前期)

2.自拍濾鏡捞烟。

3.二次美顏(多級美顏:眼睛放大薄声,痩臉,去紅眼题画,去黑眼圈默辨,去痘,人像濾鏡)

4.美妝(待開發(fā))

5.拼圖+分享功能(明確需要前置和后置都需要)(待開發(fā))

6.水硬韵ⅰ(預(yù)覽實(shí)時(shí)水印+二次水印缩幸,開發(fā)中)

7.自拍快門的操作方式(確定采用:眨眼控制)

|
|

主照相機(jī)特效處理

|

  1. 拍攝模式(風(fēng)景模式,夜景模式竞思,全景模式)

2其他特效模式

3.評估拍完照片后表谊,是否顯示回放動作(不顯示回放,從用戶感覺上可以極大的提高拍照速度)

|

14.5其他提升體驗(yàn)的細(xì)節(jié)優(yōu)化(進(jìn)展)

|

ITEM

|

識別的改進(jìn)項(xiàng)

|
|

明確前攝和夜拍選型要求

|

1.明確自拍的選型要求

|
|

2.明確夜拍效果提升的選型要求

|
|

提高對焦速度

|

MTK平臺主相機(jī)的對焦速度和對焦準(zhǔn)確性評估是否優(yōu)化

1)對比MTK和高通平臺的對焦精度的區(qū)別

2)對比HQ和競品機(jī)的對焦速度和精度的差距

|
|

關(guān)注新的對焦技術(shù)PDAF(快速對焦)的導(dǎo)入進(jìn)展

|
|

提高拍照速度

|

1)拍照時(shí)的快門聲音速度需要優(yōu)化盖喷,優(yōu)化從觸摸屏幕到拍照音響起的速度(減小延遲)

|
|

2)取消目前的拍照后的延時(shí)回顯動作.

|
|

3)對于取消回顯后爆办,拍完照仍會“停頓一下”的問題,修改capture原理课梳,改為“零延時(shí)拍照”

|

14.6前攝選型要求

| |

前攝選型要求

|
|

Camera選型

|

事業(yè)部

|

Sensor

|

Lens

|

項(xiàng)目

|
|

光圈

|

視角

|

調(diào)焦距離

|
|

200W

|

OV2355

|

F2.8

|

80度

|

30~100cm

| |
|

500W

|

OV5648

|

F2.4

|

80度

|

AL810已經(jīng)導(dǎo)入

|
|

800W

|

OV8858

|

F2.0/F2.2

|

80度

|

AW950已經(jīng)導(dǎo)入

|
|

調(diào)試重點(diǎn)

|

重點(diǎn)針對人臉及膚色進(jìn)行調(diào)試和優(yōu)化

|
|

特效及美顏模式

|

加入自拍相關(guān)的特效處理和自拍模式

|

14.7夜拍效果提升選型要求

|

內(nèi)容

|

細(xì)節(jié)

|

要求

|

說明

|
|

Camera選型要求

|

Sensor

|

【5M】: 1/4inch 1.4um*1.4um

|

影響夜拍效果的主要因素是單位像素的感光面積(pixel size)距辆,面積越大,夜拍效果越好

|
|

【5M】: 1/5inch 1.12um*1.12um

|
|

【8M】: 1/4 inch: 1.12um*1.12um

|
|

【8M】: 1/3.2 inch: 1.4um1.4um【推薦】*

|
|

【8M】: 1/3inch: 1.5um1.5um(Iphone5s) 【8M】: 1/2.5inch: 1.5um1.5um(iphone6)

|
|

【13M】: 1/3.0inch: 1.12um1.12um【推薦】*

|
|

LENS

|

【13M】: F2.0+5P

              F2.2+6P(vivo/MI4/iphone6等)

|

影響夜拍效果最主要的因素是光圈暮刃,光圈越大跨算,進(jìn)光量越多,效果越好

|
|

【8M】: F2.0/F2.2+5P

|
|

調(diào)試重點(diǎn)

|

幀率

|

調(diào)低幀率可以增加每幀的曝光時(shí)間椭懊,提升夜拍效果

|

調(diào)試重點(diǎn)是ISO漂彤,幀率,曝光時(shí)間的組合調(diào)試灾搏,找出最佳的比例搭配挫望。

|
|

ISO

|

ISO越高,靈敏度越高狂窑,夜拍會更明亮和流暢媳板,但是同時(shí)噪點(diǎn)也會增加,要求不超過ISO800泉哈,

|

14.8提高拍照速度-修改原理及細(xì)節(jié)

CaptureAnimManager.java

private static final float CAPTURE_ANIM_DURATION = 700; // milliseconds.

修改為

private static final float CAPTURE_ANIM_DURATION = 10;//此處為拍照回顯動畫時(shí)間


photoactor.java中

public void onPictureTaken(final byte[] jpegData,final android.hardware.Camera camera) {

Message msg = mHandler.obtainMessage(RESTART_PREVIEW);

mHandler.sendMessageDelayed(msg, delay);

修改為

mHandler.sendMessageDelayed(msg, 0);//此處為拍照后相機(jī)取消延時(shí)重新預(yù)覽


上層這個(gè)修改點(diǎn):

原因?yàn)镃MCC有測項(xiàng)要求拍照定格蛉幸,因此mtk camera app會在jpeg callback之后delay 1 秒多的時(shí)間才restart preview。修改點(diǎn)是將這個(gè)delay去掉直接進(jìn)行restart preview操作丛晦。入庫版本可以保留這個(gè)延遲奕纫,量產(chǎn)版本取消延時(shí),提升用戶體驗(yàn).//AL810(蘇寧/華碩已經(jīng)導(dǎo)入)

14.9優(yōu)化暗光下MFLL和MBF功能閥值

圖片.png
圖片.png

暗光下烫沙,由于幀率降低匹层,camera算法會拍四張照片進(jìn)行降噪合成處理,所以時(shí)間會慢锌蓄,通過優(yōu)化暗光的啟動閥值升筏,來優(yōu)化拍照速度

14.10 零延時(shí)拍照

圖片.png

要求sensor的幀率達(dá)到 全尺寸 30幀. 沒有30fps會有比較明顯的拖尾現(xiàn)象.

滿足“零延時(shí)”的sensor

|

像素

|

sensor

|

廠商

|

幀率

|

零延時(shí)是否滿足

|
|

2M

|

OV2680

|

OV

|

30fps

|

|
|

GC2355

|

格科微

|

30fps

|

|
|

5M

|

OV5670

|

OV

|

30fps

|

|
|

Hynix551

|

Hynix

|

30fps

|

|
|

OV5648

|

OV

|

15fps

|

X

|
|

8M

|

OV8858

|

OV

|

30fps

|

|
|

OV8865

|

OV

|

30fps

|

|
|

S5K3H7

|

samsung

|

30fps

|

|
|

IMX179

|

sony

|

30fps

|

|
|

S5K4H5

|

samsung

|

30fps

|

|
|

13M

|

OV13850

|

OV

|

30fps

|

|
|

IMX214

|

sony

|

30fps

|

|

14.11下一步計(jì)劃

|

序號

|

內(nèi)容

|

進(jìn)展

|
|

1

|

Camera基礎(chǔ)效果優(yōu)化

|

例行

|
|

2

|

拍照體驗(yàn)細(xì)節(jié)優(yōu)化

|

自動對焦速度和精度的優(yōu)化

|

方案已經(jīng)完成撑柔,待項(xiàng)目調(diào)試導(dǎo)入.

預(yù)研并關(guān)注新的對焦技術(shù)的進(jìn)展

|
|

夜拍效果的優(yōu)化方案

|

確認(rèn)夜拍選型和調(diào)試方案

|
|

3

|

拍照速度提高

|

1)拍照時(shí)的快門聲音速度需要優(yōu)化,優(yōu)化從觸摸屏幕到拍照音響起的速度(減小延遲)

|

AL810/AL820/SL830已經(jīng)導(dǎo)入

|
|

4

|

2)取消目前的拍照后的延時(shí)回顯動作.

|
|

5

|

3)對于取消回顯后您访,拍完照仍會“停頓一下”的問題铅忿,修改capture原理,默認(rèn)拍照模式改為“零延時(shí)拍照”

|

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末灵汪,一起剝皮案震驚了整個(gè)濱河市檀训,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌享言,老刑警劉巖肢扯,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異担锤,居然都是意外死亡蔚晨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門肛循,熙熙樓的掌柜王于貴愁眉苦臉地迎上來铭腕,“玉大人,你說我怎么就攤上這事多糠±巯希” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵夹孔,是天一觀的道長被盈。 經(jīng)常有香客問我,道長搭伤,這世上最難降的妖魔是什么只怎? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮怜俐,結(jié)果婚禮上身堡,老公的妹妹穿的比我還像新娘。我一直安慰自己拍鲤,他們只是感情好贴谎,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著季稳,像睡著了一般擅这。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上景鼠,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天仲翎,我揣著相機(jī)與錄音,去河邊找鬼。 笑死谭确,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的票渠。 我是一名探鬼主播逐哈,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼问顷!你這毒婦竟也來了昂秃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤杜窄,失蹤者是張志新(化名)和其女友劉穎肠骆,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體塞耕,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蚀腿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扫外。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片莉钙。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖筛谚,靈堂內(nèi)的尸體忽然破棺而出磁玉,到底是詐尸還是另有隱情,我是刑警寧澤驾讲,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響裕循,放射性物質(zhì)發(fā)生泄漏椎扬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一谓晌、第九天 我趴在偏房一處隱蔽的房頂上張望别垮。 院中可真熱鬧,春花似錦扎谎、人聲如沸碳想。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胧奔。三九已至,卻和暖如春预吆,著一層夾襖步出監(jiān)牢的瞬間龙填,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留岩遗,地道東北人扇商。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像宿礁,于是被迫代替她去往敵國和親案铺。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容