前面对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 > ) {
// 开始倒计时
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, );
//装配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流程的整个过程中的时序图:

android6.0源码分析之Camera API2.0下的Capture流程分析的更多相关文章

  1. 《Prism 5.0源码走读》Prism 5.0简介

    Prism是一个开发和设计模块化WPF应用的基础框架,里面包含了MVVM pattern和设计示例.当前最新的版本是Prism 5.0,官方网站:https://compositewpf.codepl ...

  2. spring boot 2.0 源码分析(二)

    在上一章学习了spring boot 2.0启动的大概流程以后,今天我们来深挖一下SpringApplication实例变量的run函数. 先把这段run函数的代码贴出来: /** * Run the ...

  3. spring boot 2.0 源码分析(四)

    在上一章的源码分析里,我们知道了spring boot 2.0中的环境是如何区分普通环境和web环境的,以及如何准备运行时环境和应用上下文的,今天我们继续分析一下run函数接下来又做了那些事情.先把r ...

  4. SiteServer CMS 5.0 源码入门

    开发者中心 STL 语言 文 档 博 客 论 坛 Github 二次开发 提示:文档中心正在完善中,我们将不断发布新文档,敬请期待...   新手入门 SiteServer CMS 能做什么 Site ...

  5. Android6.0 源码修改之 Contacts应用

    一.Contacts应用的主界面和联系人详情界面增加顶部菜单添加退出按钮 通过Hierarchy View 工具可以发现 主界面对应的类为 PeopleActivity 联系人详情界面对应的类为 Qu ...

  6. Android6.0 源码修改之 仿IOS添加全屏可拖拽浮窗返回按钮

    前言 之前写过屏蔽系统导航栏功能的文章,具体可看Android6.0 源码修改之屏蔽导航栏虚拟按键(Home和RecentAPP)/动态显示和隐藏NavigationBar 在某些特殊定制的版本中要求 ...

  7. 3 cocos2dx 3.0 源码分析-mainLoop详细

    简述:   我靠上面图是不是太大了, 有点看不清了.  总结一下过程: 之前说过的appController 之后经过了若干初始化, 最后调用了displayLinker 的定时调用, 这里调用了函数 ...

  8. AFNetWorking3.0源码分析

    分析: AFNetWorking(3.0)源码分析(一)——基本框架 AFNetworking源码解析 AFNetworking2.0源码解析<一> end

  9. Solr5.0源码分析-SolrDispatchFilter

    年初,公司开发法律行业的搜索引擎.当时,我作为整个系统的核心成员,选择solr,并在solr根据我们的要求做了相应的二次开发.但是,对solr的还没有进行认真仔细的研究.最近,事情比较清闲,翻翻sol ...

随机推荐

  1. Node.js全局对象

    Node.js的全局对象是具有全局性的,它们可在所有的模块中应用.我们并不需要包括这些对象在应用中,而可以直接使用它们.这些对象的模块,函数,字符串和对象本身,如下所述. __filename __f ...

  2. 对话Facebook人工智能实验室主任、深度学习专家Yann LeCun

    对话Facebook人工智能实验室主任.深度学习专家Yann LeCun Yann LeCun(燕乐存),Facebook人工智能实验室主任,NYU数据科学中心创始人,计算机科学.神经科学.电子电气科 ...

  3. Why GEMM is at the heart of deep learning

    Why GEMM is at the heart of deep learning I spend most of my time worrying about how to make deep le ...

  4. Linux配置支持高并发TCP连接(socket最大连接数)

    Linux配置支持高并发TCP连接(socket最大连接数) Linux配置支持高并发TCP连接(socket最大连接数)及优化内核参数 2011-08-09 15:20:58|  分类:LNMP&a ...

  5. (转载)调用ob_end_flush()网页仍旧不能显示有关问题

    (转载)http://www.myexception.cn/php/558638.html 调用ob_end_flush()网页仍旧不能显示问题?写了一个简单的demo,理论上调用ob_end_flu ...

  6. 动态规划(斜率优化):[CEOI2004]锯木厂选址

    锯木场选址(CEOI2004) 从山顶上到山底下沿着一条直线种植了n棵老树.当地的政府决定把他们砍下来.为了不浪费任何一棵木材,树被砍倒后要运送到锯木厂. 木材只能按照一个方向运输:朝山下运.山脚下有 ...

  7. 动态规划(背包问题):POJ 1742 Coins

    Coins Time Limit: 3000MS   Memory Limit: 30000K Total Submissions: 32955   Accepted: 11199 Descripti ...

  8. First Bad Version——LeetCode

    You are a product manager and currently leading a team to develop a new product. Unfortunately, the ...

  9. Single Number III——LeetCode

    Given an array of numbers nums, in which exactly two elements appear only once and all the other ele ...

  10. UVa11419 SAM I AM(构造最小点覆盖)

    题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=27475 [思路] 二分图的最小点覆盖以及构造最小覆盖. 二分图的最 ...