android Camera api1與api2 ZSL簡要說明

ZSL的概念

ZSL (zero shutter lag) 中文名稱爲零延時拍照,是爲了減少拍照延時,讓拍照&回顯瞬間完成的一種技術。

Single Shot
當開始預覽後,sensor 和 VFE 會產生 preview 和 snapshot幀, 而最新的snapshot 幀數據會被存儲在buffer 中。當拍照被觸發,系統計算實際的拍照時間,找出在buffer中的相應幀,然後返回幀到用戶,這就是所謂的“ZERO”。

系統計算出shutter lag的時間,然後把某個幀認作是拍照實時的那幀數據。

ZSL的實現機制

因爲ZSL實現需要實現一下幾點:

  1. 一個surfaceView用於預覽

  2. 一個隊列緩存snapshot的數據

  3. 拍照動作獲取隊列某楨數據作爲拍照數據輸出

  4. 輸出的照片需要YUV->JPEG數據的轉碼

首先說一下ZSL功能在android4.4和android5.0上實現的區別。

Android4.4的實現對於2)步和3)步都是在HAL層實現,HAL層在維護緩存隊列,當接收倒take_picture 命令時直接取得某楨緩存數據,進行轉碼,然後以正常拍照的流程利用@link android.hardware.Camera.PictureCallback通知應用層拍照的數據。

Android5.0的實現對於2)步和3)步都是在應用層實現,應用層在啓動預覽時給HAL層傳遞2個surface給HAL層,HAL層利用其中一個surface用於預覽數據填充,一個surface用於填充snapshot的數據填充。應用層不斷讀取surface中snapshot的數據去維護一個緩存隊列,當用戶執行take_picture,讀取緩存隊列的數據作爲拍照數據。
Android5.0中的應用層已經有實現ZSL類:

src/com/android/camera/one/v2/OneCameraZslImpl.java

默認該方法是沒有被調用,因爲HAL層默認是不支持,因爲HAL層是沒有實現代碼的,需要各大不同廠商去實現實現後設置不同的才支持。

暫時不去考慮應用層如何去調用OneCameraZslImpl.java,直接帶大家瞭解OneCameraZslImpl如何利用Camera API2.0實現ZSL拍照功能。

Camera API2.0之ZSL預覽

在文件OneCameraZslImpl.java文件中可以找到啓動startpreview的代碼,代碼如下:

@Override

    public void startPreview(Surface previewSurface, CaptureReadyCallback listener) {

        mPreviewSurface = previewSurface;

        setupAsync(mPreviewSurface, listener);

    }

在SetupAsync傳遞兩個參數,第一個參數mPreviewSurface爲預覽的surface,第二個爲一個回調,從名稱可以看出是一種爲拍照準備完畢的回調。

*在android5.0的camera應用、camera framework四處有這種類似實現機制,似乎就是故意讓人看不懂代碼的。

在SetupAsync方法中會異步調用setup,啓動預覽:

private void setupAsync(final Surface previewSurface, 

                               final CaptureReadyCallback listener) {

        mCameraHandler.post(new Runnable() {

                @Override

            public void run() {

                setup(previewSurface, listener);

            }

        });

        }

現在可以查看setup方法,這個纔是和HAL層交互的關鍵,也是應用層開始緩存拍照隊列數據的關鍵。

  private void setup(Surface previewSurface, final CaptureReadyCallback listener) {

        .......

            List<Surface> outputSurfaces = new ArrayList<Surface>(2);

            outputSurfaces.add(previewSurface);//用於預覽的surface

            outputSurfaces.add(mCaptureImageReader.getSurface()); //用於拍照的surface

            //mDevice爲Framework的CameraDeviceImpl.java對象,

            //也是app層和HAL層交互的對象

            mDevice.createCaptureSession(outputSurfaces,

                               new CameraCaptureSession.StateCallback() {

                    @Override

                public void onConfigureFailed(CameraCaptureSession session) {

                    ......

                }

                    @Override

                public void onConfigured(CameraCaptureSession session) {

                    .......//成功開始的操作

                }

直接到framework查看createCaptureSession方法,在該方法中會創建新的CapureSession,創建成功以後會回調lisnter的onConfigured方法, 這樣應用也可以獲得新Sesssion,下面是createCaptureSession創建CapureSession的方法:

frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java

 

 @Override

    public void createCaptureSession(List<Surface> outputs,

            CameraCaptureSession.StateCallback callback, Handler handler)

            throws CameraAccessException {

       .........

//創建新的Session

            CameraCaptureSessionImpl newSession =

                    new CameraCaptureSessionImpl(mNextSessionId++,

                            outputs, callback, handler, this, mDeviceHandler,

                            configureSuccess);

......

        }

    }

查看應用的onConfigured方法,該方法中調用sendRepeatingCaptureRequest:

 mCaptureSession = session;

                    .......

                    //執行緩存隊列的處理

                    boolean success = sendRepeatingCaptureRequest();

                    if (success) {

                        mReadyStateManager.setInput(ReadyStateRequirement

                                   .CAPTURE_NOT_IN_PROGRESS,

                                true);

                        mReadyStateManager.notifyListeners();

                        listener.onReadyForCapture();

                    } else {

                        listener.onSetupFailed();

                    }

sendRepeatingCaptureRequest方法中利用CameraDeviceImpl創建拍照請求並設置重複的拍照命令,保證開始更新緩存。

private boolean sendRepeatingCaptureRequest() {

            builder = mDevice.

             createCaptureRequest(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);

             //TEMPLATE_ZERO_SHUTTER_LAG 該參數很重要HAL層依據該參數

             //確認是否需要啓動ZSL數據格式

 

            builder.addTarget(mPreviewSurface);

            builder.addTarget(mCaptureImageReader.getSurface());

            // 通知HAL執行重複的請求

            mCaptureSession.setRepeatingRequest(builder.build(), mCaptureManager,

                    mCameraHandler);

            return true;

   }

Camera API2.0之ZSL拍照

根據前面的說明執行拍照命令其實去獲取緩存隊列中的數據,ZSL的緩存數據是利用framework提供的工具類ImageReader。

查看ImageReader的實例化:

mCaptureImageReader = ImageReader.newInstance(pictureSize.getWidth(),

                pictureSize.getHeight(),

                sCaptureImageFormat, MAX_CAPTURE_IMAGES);

 

其中sCaptureImageFormat的定義如下:

 

    /**

     * Set to ImageFormat.JPEG to use the hardware encoder, or

     * ImageFormat.YUV_420_888 to use the software encoder. No other image

     * formats are supported.

     */

    private static final int sCaptureImageFormat = ImageFormat.YUV_420_888;

 

其中sCaptureImageFormat可以定義爲JPEG,也可以定義爲YUV_420_888,其中JEPG需要HAL轉碼,轉碼涉及到效率問題,設置爲YUV_420_888則需要應用層轉碼,如果應用分配資源小,也可能直接導致應用over。

MAX_CAPTURE_IMAGES爲定義的緩存數量。

  /**

     * The maximum number of images to store in the full-size ZSL ring buffer.

     * <br>

     * TODO: Determine this number dynamically based on available memory and the

     * size of frames.

     */

    private static final int MAX_CAPTURE_IMAGES = 10;

 

 

文件OneCameraZslImpl.java文件有takePicture方法,方法介紹如下:

 @Override

    public void takePicture(final PhotoCaptureParameters params, 

                        final CaptureSession session) {

        // 停止讀取緩存

        mReadyStateManager.setInput(

                ReadyStateRequirement.CAPTURE_NOT_IN_PROGRESS, false);

 

//直接讀取緩存圖片

 boolean capturedPreviousFrame = mCaptureManager.tryCaptureExistingImage(

                    new ImageCaptureTask(params, session), zslConstraints);

}

 

ImageCaptureTask實現了ImageCaptureListener 接口,實現了onImageCaptured方法:

 @Override

        public void onImageCaptured(Image image, TotalCaptureResult

                captureResult) {

......

              mReadyStateManager.setInput(

                    ReadyStateRequirement.CAPTURE_NOT_IN_PROGRESS, true);

            mSession.startEmpty();

            savePicture(image, mParams, mSession);

......

            mParams.callback.onPictureTaken(mSession);

        }

 

 

現在去查看如何實現拍照的在ImageCaptureManager類中方法如下:

 

    public boolean tryCaptureExistingImage(final ImageCaptureListener onImageCaptured,

            final List<CapturedImageConstraint> constraints) {

......

             //創建選擇的image對象

             selector = new Selector<ImageCaptureManager.CapturedImage>() {

                    @Override

                public boolean select(CapturedImage e) {

                   ......

            };

        //這就是取得拍照數據的地方

        final Pair<Long, CapturedImage> toCapture =

 mCapturedImageBuffer.tryPinGreatestSelected(selector);

        return tryExecuteCaptureOrRelease(toCapture, onImageCaptured);

}

 

 

在tryExecuteCaptureOrRelease方法中回調ImageCaptureTask的onImageCaptured方法,然後在onImageCaptured方法中調用savePicture方法保存數據,查看savePicture方法:

 

private void savePicture(Image image, final PhotoCaptureParameters captureParams,

            CaptureSession session) {

       

        // TODO: Add more exif tags here.

        //

        session.saveAndFinish(acquireJpegBytes(image), width, height, rotation, exif,

                new OnMediaSavedListener() {

                @Override

                    public void onMediaSaved(Uri uri) {

                        captureParams.callback.onPictureSaved(uri);

                    }

                });

    }

因爲圖片是由應用生成,應用應該負責文件Header和tag信息, 在該savePicture方法中填充圖片的GSP、角度、高度和寬度信息。

在保存數據時需要把YUV轉爲jpeg,google 爲了該問題專門做了一個so庫該代碼就在Camera的jni目錄下,在Camera的Android.mk 文件中

LOCAL_JNI_SHARED_LIBRARIES := libjni_tinyplanet libjni_jpegutil

然後查看jni下的Android.mk 文件

# JpegUtil

include $(CLEAR_VARS)

 

LOCAL_CFLAGS := -std=c++11

LOCAL_NDK_STL_VARIANT := c++_static

LOCAL_LDFLAGS   := -llog -ldl

LOCAL_SDK_VERSION := 9

LOCAL_MODULE    := libjni_jpegutil

LOCAL_SRC_FILES := jpegutil.cpp jpegutilnative.cpp

Camera API2.0之ZSL的問題點

不可否認Camera API2.0 給了應用更大的操作空間,對於以後的實時渲染有更多的操作性。但是會存在如下問題:

1.佔用應用層太多的內存,ZSL需要在應用層存儲10個buffer保存圖片,勢必容易造成資源問題,所以google在代碼中強制追加了用於ZSL的圖片size不同大於1080P.

YUV倒JPEG的轉碼對CPU計算能力要求,如果CPU計算能力不強,會導致慢的問題。Google在這裏追加了緩存數據就是JPEG的處理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章