文档章节

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

天王盖地虎626
 天王盖地虎626
发布于 06/05 08:15
字数 1508
阅读 4
收藏 0

前面分析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();
    //初始化图片参数
    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流程的整个过程中的时序图: 

本文转载自:https://blog.csdn.net/yangzhihuiguming/article/details/51822384

天王盖地虎626

天王盖地虎626

粉丝 28
博文 486
码字总数 20672
作品 0
南京
私信 提问
android6.0源码分析之Camera API2.0简介

前面几篇主要分析的是android Camera API1.0的架构以及初始化流程,而google在android5.0(Lollipop)开始对Camera的架构进行了调整,为了适应HAL3,新添加实现了CameraDeviceClient,而Camer...

天王盖地虎626
05/21
0
0
android6.0源码分析之Camera API2.0下的Preview(预览)流程分析

本文将基于android6.0的源码,对Camera API2.0下Camera的preview的流程进行分析。在文章android6.0源码分析之Camera API2.0下的初始化流程分析中,已经对Camera2内置应用的Open即初始化流程进...

天王盖地虎626
05/19
0
0
android6.0源码分析之Camera2 HAL分析

在上一篇文章对Camera API2.0的框架进行了简单的介绍,其中Camera HAL屏蔽了底层的实现细节,并且为上层提供了相应的接口,具体的HAL的原理,个人觉得老罗的文章Android硬件抽象层(HAL)概要...

天王盖地虎626
05/21
0
0
ANDROID6.0源码分析之CAMERA API2.0下的CAPTURE流程分析

前面对Camera2的初始化以及预览的相关流程进行了详细分析,本文将会对Camera2的capture(拍照)流程进行分析。 前面分析preview的时候,当预览成功后,会使能ShutterButton,即可以进行拍照,定...

天王盖地虎626
05/18
0
0
android camera2 API流程分析

Android camera2 API流程分析 Android5.0之后,新推出来了一个类,android.hardware.camera2,与原来的camera的类实现照相和拍视频的流程有所不同,原来的camera的类并没有深入分析。在做项目...

天王盖地虎626
05/21
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Oracle:本地表空间管理,字典表空间管理

本地管理表空间 一、概述 1、理解本地管理表空间的由来 2、理解什么是字典管理表空间及工作原理 3、理解本地管理表空间的优势(为什么要使用本地管理表空间) 4、理解本地管理表空间的内部结...

突突突酱
25分钟前
1
0
深度剖析Spring Boot源码,看完薪资敢要30K!

1 实例化SpringApplication SpringApplication.run(BootifulApplication.class, args); public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {ret......

Java架构资源分享
28分钟前
4
0
tornadofx控制台输出“三门问题”,蒙特卡洛算法

import tornadofx.*fun main() { val wins = intProperty() val lose = intProperty() val Msg = stringProperty()// 1000万次 val n = 10000000 (1..n......

oschina4cyy
34分钟前
1
0
你可能不知道的MySQL中的定点数类型

定点数类型 正因为用浮点数表示小数可能会有不精确的情况,在一些情况下我们必须保证小数是精确的,所以设计MySQL的大叔们提出一种称之为定点数的数据类型,它也是存储小数的一种方式: 其中...

爱编程的浪子
37分钟前
2
0
第十讲:Python爬取网页图片并保存到本地,包含次层页面

上一讲我们讲到了从昵图网的首页下载图片到本地,但是我们发现首页上面的大部分链接其实都可以进入到二级页面。 在二级页面里面,我们也可以同样进行图片的下载,通过层层循环我们可以把网址...

刘日辉
44分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部