android6.0源碼分析之Camera API2.0下的Capture流程分析

来源:http://www.cnblogs.com/gumingyang/archive/2016/07/12/5662602.html
-Advertisement-
Play Games

本文主要對android6.0源碼中的Camera API2.0下的內置應用Camera2的caputre流程進行分析 ...


前面對Camera2的初始化以及預覽的相關流程進行了詳細分析,本文將會對Camera2的capture(拍照)流程進行分析。

 

前面分析preview的時候,當預覽成功後,會使能ShutterButton,即可以進行拍照,定位到ShutterButton的監聽事件為onShutterButtonClick方法:

//CaptureModule.java
@Override
public void onShutterButtonClick() {
    //Camera未打開
    if (mCamera == null) {
        return;
    }

    int countDownDuration = mSettingsManager.getInteger(SettingsManager
    .SCOPE_GLOBAL,Keys.KEY_COUNTDOWN_DURATION);
if (countDownDuration > 0) { // 開始倒計時 mAppController.getCameraAppUI().transitionToCancel(); mAppController.getCameraAppUI().hideModeOptions(); mUI.setCountdownFinishedListener(this); mUI.startCountdown(countDownDuration); // Will take picture later via listener callback. } else { //即刻拍照 takePictureNow(); } }

首先,讀取Camera的配置,判斷配置是否需要延時拍照,此處分析不需延時的情況,即調用takePictureNow方法:

//CaptureModule.java
private void takePictureNow() {
    if (mCamera == null) {
        Log.i(TAG, "Not taking picture since Camera is closed.");
        return;
    }
    //創建Capture會話並開啟會話
    CaptureSession session = createAndStartCaptureSession();
    //獲取Camera的方向
    int orientation = mAppController.getOrientationManager()
        .getDeviceOrientation().getDegrees();
    //初始化圖片參數,其中this(即CaptureModule)為PictureCallback的實現
    PhotoCaptureParameters params = new PhotoCaptureParameters(
            session.getTitle(), orientation, session.getLocation(),
            mContext.getExternalCacheDir(), this, mPictureSaverCallback,
            mHeadingSensor.getCurrentHeading(), mZoomValue, 0);
    //裝配Session
    decorateSessionAtCaptureTime(session);
    //拍照
    mCamera.takePicture(params, session);
}

它首先調用createAndStartCaptureSession來創建一個CaptureSession並且啟動會話,這裡並且會進行初始參數的設置,譬如設置CaptureModule(此處實參

為this)為圖片處理的回調(後面再分析):

//CaptureModule.java
private CaptureSession createAndStartCaptureSession() {
    //獲取會話時間
    long sessionTime = getSessionTime();
    //當前位置
    Location location = mLocationManager.getCurrentLocation();
    //設置picture name
    String title = CameraUtil.instance().createJpegName(sessionTime);
    //創建會話
    CaptureSession session = getServices().getCaptureSessionManager()
           .createNewSession(title, sessionTime, location);
    //開啟會話
    session.startEmpty(new CaptureStats(mHdrPlusEnabled),new Size(
        (int) mPreviewArea.width(), (int) mPreviewArea.height()));
    return session;
}

首先,獲取會話的相關參數,包括會話時間,拍照的照片名字以及位置信息等,然後調用Session管理來創建CaptureSession,最後將此CaptureSession

啟動。到這裡,會話就創建並啟動了,所以接著分析上面的拍照流程,它會調用OneCameraImpl的takePicture方法來進行拍照:

//OneCameraImpl.java
@Override
public void takePicture(final PhotoCaptureParameters params, final CaptureSession session) {
    ...
    // 除非拍照已經返回,否則就廣播一個未準備好狀態的廣播,即等待本次拍照結束
    broadcastReadyState(false);
    //創建一個線程
    mTakePictureRunnable = new Runnable() {
        @Override
        public void run() {
            //拍照
            takePictureNow(params, session);
        }
    };
    //設置回調,此回調後面將分析,它其實就是CaptureModule,它實現了PictureCallback
    mLastPictureCallback = params.callback;
    mTakePictureStartMillis = SystemClock.uptimeMillis();

    //如果需要自動聚焦
    if (mLastResultAFState == AutoFocusState.ACTIVE_SCAN) {
        mTakePictureWhenLensIsStopped = true;
    } else {
        //拍照
        takePictureNow(params, session);
    }
}

在拍照里,首先廣播一個未準備好的狀態廣播,然後進行拍照的回調設置,並且判斷是否有自動聚焦,如果是則將mTakePictureWhenLensIsStopped 設為ture,

即即刻拍照被停止了,否則則調用OneCameraImpl的takePictureNow方法來發起拍照請求:

//OneCameraImpl.java
public void takePictureNow(PhotoCaptureParameters params, CaptureSession 
        session) {
    long dt = SystemClock.uptimeMillis() - mTakePictureStartMillis;
    try {
        // 構造JPEG圖片拍照的請求
        CaptureRequest.Builder builder = mDevice.createCaptureRequest(
            CameraDevice.TEMPLATE_STILL_CAPTURE);
        builder.setTag(RequestTag.CAPTURE);
        addBaselineCaptureKeysToRequest(builder);

        // Enable lens-shading correction for even better DNGs.
        if (sCaptureImageFormat == ImageFormat.RAW_SENSOR) {
            builder.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
                CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE_ON);
        } else if (sCaptureImageFormat == ImageFormat.JPEG) {
            builder.set(CaptureRequest.JPEG_QUALITY, JPEG_QUALITY);
                .getJpegRotation(params.orientation, mCharacteristics));
        }
        //用於preview的控制項
        builder.addTarget(mPreviewSurface);
        //用於圖片顯示的控制項
        builder.addTarget(mCaptureImageReader.getSurface());
        CaptureRequest request = builder.build();

        if (DEBUG_WRITE_CAPTURE_DATA) {
            final String debugDataDir = makeDebugDir(params.debugDataFolder,
                        "normal_capture_debug");
            Log.i(TAG, "Writing capture data to: " + debugDataDir);
            CaptureDataSerializer.toFile("Normal Capture", request, 
                new File(debugDataDir,"capture.txt"));
        }
        //拍照,mCaptureCallback為回調
        mCaptureSession.capture(request, mCaptureCallback, mCameraHandler);
    } catch (CameraAccessException e) {
        Log.e(TAG, "Could not access camera for still image capture.");
        broadcastReadyState(true);
        params.callback.onPictureTakingFailed();
        return;
    }
    synchronized (mCaptureQueue) {
        mCaptureQueue.add(new InFlightCapture(params, session));
    }
}

與preview類似,都是通過CaptureRequest來與Camera進行通信的,通過session的capture來進行拍照,

並設置拍照的回調函數為mCaptureCallback:

//CameraCaptureSessionImpl.java
@Override
public synchronized int capture(CaptureRequest request,CaptureCallback callback,Handler handler)throws CameraAccessException{
    ...
    handler = checkHandler(handler,callback);
    return addPendingSequence(mDeviceImpl.capture(request,createCaptureCallbackProxy(handler,callback),mDeviceHandler));
}

代碼與preview中的類似,都是將請求加入到待處理的請求集,現在看CaptureCallback回調:

//OneCameraImpl.java
private final CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback(){
    @Override
    public void onCaptureStarted(CameraCaptureSession session,CaptureRequest request,long timestamp,long frameNumber){
     //與preview類似
if(request.getTag() == RequestTag.CAPTURE&&mLastPictureCallback!=null){ mLastPictureCallback.onQuickExpose(); } } ... @Override public void onCaptureCompleted(CameraCaptureSession session,CaptureRequest request,TotalCaptureResult result){ autofocusStateChangeDispatcher(result); if(result.get(CaptureResult.CONTROL_AF_STATE) == null){
       //檢查自動聚焦的狀態 AutoFocusHelper.checkControlAfState(result); } ...
if(request.getTag() == RequestTag.CAPTURE){ synchronized(mCaptureQueue){ if(mCaptureQueue.getFirst().setCaptureResult(result).isCaptureComplete()){ capture = mCaptureQueue.removeFirst(); } } if(capture != null){
         //拍照結束 OneCameraImpl.
this.onCaptureCompleted(capture); } } super.onCaptureCompleted(session,request,result); } ... }

這是Native層在處理請求時,會調用相應的回調,如capture開始時,會回調onCaptureStarted,具體的在preview中有過分析,當拍照結束時,會回調

onCaptureCompleted方法,其中會根據CaptureResult來檢查自動聚焦的狀態,並通過TAG判斷其是Capture動作時,再來看它是否是隊列中的第一個請求,

如果是,則將請求移除,因為請求已經處理成功,最後再調用OneCameraImpl的onCaptureCompleted方法來進行處理:

//OneCameraImpl.java
private void onCaptureCompleted(InFlightCapture capture){
    if(isCaptureImageFormat == ImageFormat.RAW_SENSOR){
        ...
        File dngFile = new File(RAW_DIRECTORY,capture.session.getTitle()+".dng");
        writeDngBytesAndClose(capture.image,capture.totalCaptureResult,mCharacteristics,dngFile);
    }else{
        //解析result中的圖片數據
        byte[] imageBytes = acquireJpegBytesAndClose(capture.image);
        //保存Jpeg圖片
        saveJpegPicture(imageBytes,capture.parameters,capture.session,capture.totalCaptureResult);
    }
    broadcastReadyState(true);
    //調用回調
    capture.parameters.callback.onPictureTaken(capture.session);
}

如代碼所示,首先,對result中的圖片數據進行瞭解析,然後調用saveJpegPicture方法將解析得到的圖片數據進行保存,最後再調用

裡面的回調(即CaptureModule,前面在初始化Parameters時說明瞭,它實現了PictureCallbak介面)的onPictureTaken方法,所以,

接下來先分析saveJpegPicture方法:

//OneCameraImpl.java
private void saveJpegPicture(byte[] jpegData,final PhotoCaptureParameters captureParams,CaptureSession session,CaptureResult result){
    ...
    ListenableFuture<Optional<Uri>> futureUri = session.saveAndFinish(jpegData,width,height,rotation,exif);
    Futures.addCallback(futureUri,new FutureCallback<Optional<Uri>>(){
        @Override
        public void onSuccess(Optional<Uri> uriOptional){
            captureParams.callback.onPictureSaved(mOptional.orNull());
        }
        
        @Override
        public void onFailure(Throwable throwable){
            captureParams.callback.onPictureSaved(null);
        }
    });
}

它最後會回調onPictureSaved方法來對圖片進行保存,所以需要分析CaptureModule的onPictureSaved方法:

//CaptureModule.java
@Override
public void onPictureSaved(Uri uri){
    mAppController.notifyNewMedia(uri);
}

mAppController的實現為CameraActivity,所以分析notifyNewMedia方法:

//CameraActivity.java
@Override
public void notifyNewMedia(Uri uri){
    ...
    if(FilmstripItemUtils.isMimeTypeVideo(mimeType)){
    //如果拍攝的是video sendBroadcast(
new Intent(CameraUtil.ACTION_NEW_VIDEO,uri)); newData = mVideoItemFactory.queryContentUri(uri); ... }else if(FilmstripItemUtils.isMimeTypeImage(mimeType)){
    //如果是拍攝圖片 CameraUtil.broadcastNewPicture(mAppContext,uri); newData
= mPhotoItemFactory.queryCotentUri(uri); ... }else{ return; } new AsyncTask<FilmstripItem,Void,FilmstripItem>(){ @Override protected FilmstripItem doInBackground(FilmstripItem... Params){ FilmstripItem data = params[0]; MetadataLoader.loadMetadata(getAndroidContet(),data); return data; } ... } }

由代碼可知,這裡有兩種數據的處理,一種是video,另一種是image。而我們這裡分析的是capture圖片數據,所以首先會根據在回調函數

傳入的參數Uri和PhotoItemFactory來查詢到相應的拍照數據,然後再開啟一個非同步的Task來對此數據進行處理,即通過MetadataLoader的

loadMetadata來載入數據,並返回。至此,capture的流程就基本分析結束了,下麵將給出capture流程的整個過程中的時序圖:

Camera2內置應用的capture流程的時序圖

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • undefined symbols for architecture x86_64 錯誤如下 因為提示文件非第三方文件,最初嘗試使用以下方式處理 iOS :undefined symbols for architecture x86_64錯誤解決方案 在搜索時發現相關的文件不存在,在xcode搜索卻 ...
  • 1.獲得當前運行的activity棧,同時也可以用此命令查看到具體某一頁面的activity名字 adb shell dumpsys activity|grep Run 2.獲得手機安裝的所有應用包名 adb shell pm list packages 3.獲得已安裝的應用的apk,導出到pc a ...
  • The IntentService class provides a straightforward structure for running an operation on a single background thread. This allows it to handle long run ...
  • 如果想自學Android,以下的文章可以作為參考: 如何自學Android(Gityuan) 那兩年煉就的Android內功修養(老羅的Android之旅) ...
  • 通知的機制是一對多,而block和delegate的機制是一對一,通知是好用,但小伙伴麽要記得通知比較耗性能哦~~~ 誰要發送消息,誰就發出通知,誰要接受消息,誰就銷毀通知. 下麵直接來看代碼: //發出通知 [[NSNotificationCenter defaultCenter] postNot ...
  • 讓代碼書寫更加簡便 【寫在開頭】 『在面向對象的編程中,封裝是其一個重要的特性。封裝將一個可供外部使用的介面暴露出來,隱藏了複雜的代碼邏輯實現。 外部就可以通過設置器setter和訪問器getter來對對象的屬性進行設置和訪問,而不是直接訪問對象的屬性-> --1-- 設置器和訪問器 1.1 set ...
  • 一:第一種方法 1:把ipa上傳到一個https類型的網站比如:https://worktile.com 2:更改plist中urrl 3:把plist也上傳到一個htts的網站 4:更改ISS的 4.1添加.ipa MIMI類型是:application/iphone 4.2添加plist MIM ...
  • 本人目前還不是游戲行業的圈內人士,並不懂得,游戲行業的生態圈,也不懂得,所謂的什麼“中國市場環境”。所以不敢發表關於這方面的見解,不過我在這裡想要插一句話,就是我認為啊,行業內,人與人之間還是有分層次的,什麼層次的人,看到什麼層次的東西,不同層次的人對“市場”的理解肯定不一樣,我覺得吧,不要一概而論... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...