android 5.0 Camera 之 ZSL说明

来源:互联网 发布:5心淘宝店铺能卖多少钱 编辑:程序博客网 时间:2024/05/01 07:19

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.4android5.0上实现的区别。

Android4.4的实现对于2)步和3)步都是在HAL层实现,HAL层在维护缓存队列,当接收倒take_picture 命令时直接取得某桢缓存数据,进行转码,然后以正常拍照的流程利用@link android.hardware.Camera.PictureCallback通知应用层拍照的数据。

Android5.0的实现对于2)步和3)步都是在应用层实现,应用层在启动预览时给HAL层传递2surfaceHAL层,HAL层利用其中一个surface用于预览数据填充,一个surface用于填充snapshot的数据填充。应用层不断读取surfacesnapshot的数据去维护一个缓存队列,当用户执行take_picture,读取缓存队列的数据作为拍照数据。

 

 

 

 

 

 

 

 

Android5.0中的应用层已经有实现ZSL类:

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

 

默认该方法是没有被调用,因为HAL层默认是不支持,因为HAL层是没有实现代码的,需要各大不同厂商去实现实现后设置不同的才支持。

暂时不去考虑应用层如何去调用OneCameraZslImpl.java,直接带大家了解OneCameraZslImpl如何利用Camera API2.0实现ZSL拍照功能。

Camera API2.0ZSL预览

在文件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,创建成功以后会回调lisnteronConfigured方法, 这样应用也可以获得新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.0ZSL拍照

根据前面的说明执行拍照命令其实去获取缓存队列中的数据,ZSL的缓存数据是利用framework提供的工具类ImageReader。

查看ImageReader的实例化:

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

                pictureSize.getHeight(),

                sCaptureImageFormatMAX_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方法中回调ImageCaptureTaskonImageCaptured方法,然后在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.0ZSL的问题点

不可否认Camera API2.0 给了应用更大的操作空间,对于以后的实时渲染有更多的操作性。但是会存在如下问题:

1.占用应用层太多的内存,ZSL需要在应用层存储10buffer保存图片,势必容易造成资源问题,所以google在代码中强制追加了用于ZSL的图片size不同大于1080P.

YUVJPEG的转码对CPU计算能力要求,如果CPU计算能力不强,会导致慢的问题。Google在这里追加了缓存数据就是JPEG的处理。

0 0