本文以SnapdragonCamera为例,分析骁龙Camera的拍照流程,其实现与camera2大致相同。

首先将SnapdragonCamera源码倒入android studio, 具体操作,可查看如何用Android Studio调试Android源码一文。打开camera,点击拍照,ShutterButton类的performClick()方法会被调用。(如何定位点击拍照会调用ShutterButton类的performClick()方法,请参考使用Android Studio中的HierarchyViewer 及UI Automator Viewer定位当前UI界面的代码位置。)

    @Overridepublic boolean performClick() {boolean result = super.performClick();if (mListener != null && getVisibility() == View.VISIBLE) {mListener.onShutterButtonClick();}return result;}

按Ctrl+Alt+B 看onShutterButtonClick()方法是在哪里实现的。如下图,有四个地方调用,由于我们只分析拍照流程,只用查看PhotoModule即可。

在PhotoModule中,onShutterButtonClick()的实现如下

    @Overridepublic synchronized void onShutterButtonClick() {if ((mCameraDevice == null)|| mPaused || mUI.collapseCameraControls()|| !mUI.mMenuInitialized|| (mCameraState == SWITCHING_CAMERA)|| (mCameraState == PREVIEW_STOPPED)|| (mCameraState == LONGSHOT)|| (null == mFocusManager)) return;...if (seconds > 0) {String zsl = mPreferences.getString(CameraSettings.KEY_ZSL,mActivity.getString(R.string.pref_camera_zsl_default));mUI.overrideSettings(CameraSettings.KEY_ZSL, zsl);mUI.startCountDown(seconds, playSound);} else {mSnapshotOnIdle = false;initiateSnap();//关键方法}}

为了便于查看,我们省略掉一些条件判断的代码,最终调用initiateSnap()方法。

  private void initiateSnap(){if(mPreferences.getString(CameraSettings.KEY_SELFIE_FLASH,mActivity.getString(R.string.pref_selfie_flash_default)).equalsIgnoreCase("on") &&mCameraId == CameraHolder.instance().getFrontCameraId()) {mUI.startSelfieFlash();if(selfieThread == null) {selfieThread = new SelfieThread();//若selfieThread == null,SelfieThread中的                                                        selfieThread.start();             //mFocusManager.doSnap()方法被调用}} else {mFocusManager.doSnap();//关键方法}}

接着查看doSnap()方法

    public void doSnap() {if (!mInitialized) return;// If the user has half-pressed the shutter and focus is completed, we// can take the photo right away. If the focus mode is infinity, we can// also take the photo.if (!needAutoFocusCall() || (mState == STATE_SUCCESS || mState == STATE_FAIL)) {capture(); // 关键方法,聚焦完成会调用此方法,} else if (mState == STATE_FOCUSING) {// Half pressing the shutter (i.e. the focus button event) will// already have requested AF for us, so just request capture on// focus here.mState = STATE_FOCUSING_SNAP_ON_FINISH;//开始聚焦未完成时,调用} else if (mState == STATE_IDLE) {// We didn't do focus. This can happen if the user press focus key// while the snapshot is still in progress. The user probably wants// the next snapshot as soon as possible, so we just do a snapshot// without focusing again.capture();//关键方法,没有聚焦时调用}}

我们分析第一种情况,第二种情况,涉及callShutterButtonFocus()的调用流程,这里暂不做分析,接着我们看PhotoModule中capture()函数的实现。

    @Overridepublic boolean capture() {// If we are already in the middle of taking a snapshot or the image save request// is full then ignore.if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS|| mCameraState == SWITCHING_CAMERA|| mActivity.getMediaSaveService() == null|| mActivity.getMediaSaveService().isQueueFull()) {return false;}...... //省略相关参数设置,及条件判断代码...if (mCameraState == LONGSHOT) {mLongShotCaptureCountLimit = SystemProperties.getInt("persist.camera.longshot.shotnum", 0);mLongShotCaptureCount = 1;if(mLongshotSave) {mCameraDevice.takePicture(mHandler,new LongshotShutterCallback(),mRawPictureCallback, mPostViewPictureCallback,new LongshotPictureCallback(loc));} else {mCameraDevice.takePicture(mHandler,new LongshotShutterCallback(),mRawPictureCallback, mPostViewPictureCallback,new JpegPictureCallback(loc));}} else {//关键代码mCameraDevice.takePicture(mHandler,new ShutterCallback(!animateBefore),mRawPictureCallback, mPostViewPictureCallback,new JpegPictureCallback(loc));setCameraState(SNAPSHOT_IN_PROGRESS);}mNamedImages.nameNewImage(mCaptureStartTime, mRefocus);if (mSnapshotMode != CameraInfo.CAMERA_SUPPORT_MODE_ZSL) {mFaceDetectionStarted = false;}UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,UsageStatistics.ACTION_CAPTURE_DONE, "Photo", 0,UsageStatistics.hashFileName(mNamedImages.mQueue.lastElement().title + ".jpg"));return true;}

其中关键代码为mCameraDevice.takePicture(),拍照后会回调JpegPictureCallback中的onPictureTaken()方法。将从底层返回的数据进行处理。

    @Overridepublic void onPictureTaken(byte [] jpegData, CameraProxy camera) {mUI.stopSelfieFlash();mUI.enableShutter(true);if (mUI.isPreviewCoverVisible()) {// When take picture request is sent before starting preview, onPreviewFrame()// callback doesn't happen so removing preview cover here, instead.mUI.hidePreviewCover();}if (mInstantCaptureSnapShot == true) {Log.v(TAG, "Instant capture picture taken!");mInstantCaptureSnapShot = false;}if (mPaused) {return;}if (mIsImageCaptureIntent) {if (!mRefocus) {stopPreview();}} else if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {mUI.showSwitcher();mUI.setSwipingEnabled(true);}mReceivedSnapNum = mReceivedSnapNum + 1;mJpegPictureCallbackTime = System.currentTimeMillis();if(mSnapshotMode == CameraInfo.CAMERA_SUPPORT_MODE_ZSL) {Log.v(TAG, "JpegPictureCallback : in zslmode");mParameters = mCameraDevice.getParameters();mBurstSnapNum = mParameters.getInt("num-snaps-per-shutter");}Log.v(TAG, "JpegPictureCallback: Received = " + mReceivedSnapNum +"Burst count = " + mBurstSnapNum);// If postview callback has arrived, the captured image is displayed// in postview callback. If not, the captured image is displayed in// raw picture callback.if (mPostViewPictureCallbackTime != 0) {mShutterToPictureDisplayedTime =mPostViewPictureCallbackTime - mShutterCallbackTime;mPictureDisplayedToJpegCallbackTime =mJpegPictureCallbackTime - mPostViewPictureCallbackTime;} else {mShutterToPictureDisplayedTime =mRawPictureCallbackTime - mShutterCallbackTime;mPictureDisplayedToJpegCallbackTime =mJpegPictureCallbackTime - mRawPictureCallbackTime;}Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "+ mPictureDisplayedToJpegCallbackTime + "ms");mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.boolean needRestartPreview = !mIsImageCaptureIntent&& !mPreviewRestartSupport&& (mCameraState != LONGSHOT)&& (mSnapshotMode != CameraInfo.CAMERA_SUPPORT_MODE_ZSL)&& (mReceivedSnapNum == mBurstSnapNum);if (needRestartPreview) {setupPreview();if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {mCameraDevice.cancelAutoFocus();}} else if ((mReceivedSnapNum == mBurstSnapNum)&& (mCameraState != LONGSHOT)){mFocusManager.resetTouchFocus();if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {mCameraDevice.cancelAutoFocus();}mUI.resumeFaceDetection();if (!mIsImageCaptureIntent) {setCameraState(IDLE);}startFaceDetection();}mLastPhotoTakenWithRefocus = mRefocus;if (mRefocus) {final String[] NAMES = { "00.jpg", "01.jpg", "02.jpg", "03.jpg","04.jpg", "DepthMapImage.y", "AllFocusImage.jpg" };try {FileOutputStream out = mActivity.openFileOutput(NAMES[mReceivedSnapNum - 1],Context.MODE_PRIVATE);out.write(jpegData, 0, jpegData.length);out.close();} catch (Exception e) {}}if (!mRefocus || (mRefocus && mReceivedSnapNum == 7)) {ExifInterface exif = Exif.getExif(jpegData);int orientation = Exif.getOrientation(exif);if(mCameraId == CameraHolder.instance().getFrontCameraId()) {IconListPreference selfieMirrorPref = (IconListPreference) mPreferenceGroup.findPreference(CameraSettings.KEY_SELFIE_MIRROR);if (selfieMirrorPref != null && selfieMirrorPref.getValue() != null &&selfieMirrorPref.getValue().equalsIgnoreCase("enable")) {jpegData = flipJpeg(jpegData);exif = Exif.getExif(jpegData);  //将图片信息存入Exif中exif.addOrientationTag(orientation);}}if (!mIsImageCaptureIntent) {// Burst snapshot. Generate new image name.if (mReceivedSnapNum > 1) {mNamedImages.nameNewImage(mCaptureStartTime, mRefocus);}// Calculate the width and the height of the jpeg.Size s = mParameters.getPictureSize();int width, height;if ((mJpegRotation + orientation) % 180 == 0) {width = s.width;height = s.height;} else {width = s.height;height = s.width;}String pictureFormat = mParameters.get(KEY_PICTURE_FORMAT);if (pictureFormat != null && !pictureFormat.equalsIgnoreCase(PIXEL_FORMAT_JPEG)) {// overwrite width and height if raw pictureString pair = mParameters.get(KEY_QC_RAW_PICUTRE_SIZE);if (pair != null) {int pos = pair.indexOf('x');if (pos != -1) {width = Integer.parseInt(pair.substring(0, pos));height = Integer.parseInt(pair.substring(pos + 1));}}}NamedEntity name = mNamedImages.getNextNameEntity();String title = (name == null) ? null : name.title;long date = (name == null) ? -1 : name.date;// Handle debug mode outputsif (mDebugUri != null) {// If using a debug uri, save jpeg there.saveToDebugUri(jpegData);// Adjust the title of the debug image shown in mediastore.if (title != null) {title = DEBUG_IMAGE_PREFIX + title;}}if (title == null) {Log.e(TAG, "Unbalanced name/data pair");} else {if (date == -1) {date = mCaptureStartTime;}if (mHeading >= 0) {// heading direction has been updated by the sensor.ExifTag directionRefTag = exif.buildTag(ExifInterface.TAG_GPS_IMG_DIRECTION_REF,ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);ExifTag directionTag = exif.buildTag(ExifInterface.TAG_GPS_IMG_DIRECTION,new Rational(mHeading, 1));exif.setTag(directionRefTag);exif.setTag(directionTag);}String mPictureFormat = mParameters.get(KEY_PICTURE_FORMAT);mActivity.getMediaSaveService().addImage(jpegData, title, date, mLocation, width, height,orientation, exif, mOnMediaSavedListener,mContentResolver, mPictureFormat);//将图片数据写入到文件和数据库中if (mRefocus && mReceivedSnapNum == 7) {mUI.showRefocusToast(mRefocus);}}// Animate capture with real jpeg data instead of a preview frame.if (mCameraState != LONGSHOT) {Size pic_size = mParameters.getPictureSize();if ((pic_size.width <= 352) && (pic_size.height<= 288)) {mUI.setDownFactor(2); //Downsample by 2 for CIF & below} else {mUI.setDownFactor(4);}if (mAnimateCapture) {mUI.animateCapture(jpegData);}} else {// In long shot mode, we do not want to update the preview thumbnail// for each snapshot, instead, keep the last jpeg data and orientation,// use it to show the final one at the end of long shot.mLastJpegData = jpegData;mLastJpegOrientation = orientation;}} else {stopPreview();mJpegImageData = jpegData;if (!mQuickCapture) {mUI.showCapturedImageForReview(jpegData, orientation, false);} else {onCaptureDone();}}if(!mLongshotActive) {mActivity.updateStorageSpaceAndHint(new CameraActivity.OnStorageUpdateDoneListener() {@Overridepublic void onStorageUpdateDone(long storageSpace) {mUI.updateRemainingPhotos(--mRemainingPhotos);}});} else {mUI.updateRemainingPhotos(--mRemainingPhotos);}long now = System.currentTimeMillis();mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;Log.v(TAG, "mJpegCallbackFinishTime = "+ mJpegCallbackFinishTime + "ms");if (mReceivedSnapNum == mBurstSnapNum) {mJpegPictureCallbackTime = 0;}if (mHiston && (mSnapshotMode ==CameraInfo.CAMERA_SUPPORT_MODE_ZSL)) {mActivity.runOnUiThread(new Runnable() {public void run() {if (mGraphView != null) {mGraphView.setVisibility(View.VISIBLE);mGraphView.PreviewChanged();}}});}if (mSnapshotMode == CameraInfo.CAMERA_SUPPORT_MODE_ZSL &&mCameraState != LONGSHOT &&mReceivedSnapNum == mBurstSnapNum &&!mIsImageCaptureIntent) {cancelAutoFocus();}}}

其中, mActivity.getMediaSaveService().addImage();方法将返回的图片数据存入文件和数据库中。MediaSaveService中的addImage()方法如下

    public void addImage(final byte[] data, String title, long date, Location loc,int width, int height, int orientation, ExifInterface exif,OnMediaSavedListener l, ContentResolver resolver, String pictureFormat) {if (isQueueFull()) {Log.e(TAG, "Cannot add image when the queue is full");return;}ImageSaveTask t = new ImageSaveTask(data, title, date,(loc == null) ? null : new Location(loc),width, height, orientation, exif, resolver, l, pictureFormat);mMemoryUse += data.length;if (isQueueFull()) {onQueueFull();}t.execute();}

最后通过ImageSaveTask异步任务,实现图片的存储,接着看ImageSaveTask中的doInBackground()方法

@Override
protected Uri doInBackground(Void... v) {if (width == 0 || height == 0) {// Decode boundsBitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeByteArray(data, 0, data.length, options);width = options.outWidth;height = options.outHeight;}return Storage.addImage(resolver, title, date, loc, orientation, exif, data, width, height, pictureFormat);
}

最后调用Storage.addImage()方法

   // Save the image with a given mimeType and add it the MediaStore.public static Uri addImage(ContentResolver resolver, String title, long date,Location location, int orientation, ExifInterface exif, byte[] jpeg, int width,int height, String mimeType) {String path = generateFilepath(title, mimeType);int size = writeFile(path, jpeg, exif, mimeType); //存文件// Try to get the real image size after add exif.File f = new File(path);if (f.exists() && f.isFile()) {size = (int) f.length();}return addImage(resolver, title, date, location, orientation,size, path, width, height, mimeType);//存数据库}

存完之后调用ImageSaveTask中的onPostExecute()方法,通知图片已经存完。

    @Overrideprotected void onPostExecute(Uri uri) {if (listener != null) listener.onMediaSaved(uri);boolean previouslyFull = isQueueFull();mMemoryUse -= data.length;if (isQueueFull() != previouslyFull) onQueueAvailable();}

以上是对拍照流程的简单分析,只是大致做了梳理,有很多细节为涉及,以后再慢慢讨论。

Android 骁龙Camera拍照流程梳理相关推荐

  1. 骁龙camera预览拍照录像流程 api1和api2

    首先说api1的预览流程 CameraActivity的onCreate先区分是不是安全模式(锁屏进入就是安全模式),如果是安全模式会设置flag indowManager.LayoutParams. ...

  2. android wifi连接流程,(九十三) Android O 连接WiFi AP流程梳理续——保存网络-Go语言中文社区...

    前言: 之前在(五十五)Android O 连接WiFi AP流程梳理 梳理连接流程梳理到SupplicantStaNetworkHal 然后没梳理的下去,现在继续梳理下. 之前梳理的时序图 1.流程 ...

  3. (九十三) Android O 连接WiFi AP流程梳理续——保存网络

    前言: 之前在(五十五)Android O 连接WiFi AP流程梳理 梳理连接流程梳理到SupplicantStaNetworkHal 然后没梳理的下去,现在继续梳理下. 之前梳理的时序图 1.流程 ...

  4. Android Studio:使用Camera拍照(三)为相机增加取景蒙板/浮层

    写在前面的话:每一个实例的代码都会附上相应的代码片或者图片,保证代码完整展示在博客中.最重要的是保证例程的完整性!!!方便自己也方便他人~欢迎大家交流讨论~本文为博主原创文章,未经博主允许不得转载. ...

  5. Android最新相机(Camera)拍照、拍视频全面总结

    介绍 利用系统相机 调用系统相机拍照 获取小图标 获取全尺寸图片 添加到相册 系统相机拍视频 自定义相机 自定义相机拍照 监测设备是否有相机可使用 利用SurfaceView创建拍照时预览界面 拍照并 ...

  6. Android开发技巧——Camera拍照功能

    本篇是我对开发项目的拍照功能过程中,对Camera拍照使用的总结.由于camera2是在api level 21(5.0.1)才引入的,而Camera到6.0仍可使用,所以暂未考虑camera2. 文 ...

  7. (九十七)Android O WiFi热点 开启流程梳理续(二)

    前言:从之前WiFi的连接流程可以知道WiFi最后一步会和服务端进行dhcp以获取ip地址,那么WiFi热点开启的时候应该也要配置相关dhcp属性,以供后续WiFi连接的时候ip分配,根据这块流程继续 ...

  8. (一百四十四)Android P WiFi 上网校验流程梳理

    前言:本文采用倒叙梳理,之前梳理流程没记下来忘了,现在再来一遍,所以说梳理什么的还是做个备忘比较好. 1.ConnectivityService 看网络校验相关log经常能看到如下log打印 log( ...

  9. (七十一)Android O WiFi热点 开启流程梳理

    前言:之前主要梳理了WiFi开启扫描连接的流程,现在梳理下WiFi 热点 的开启流程. 时序图mdj样式:https://download.csdn.net/download/sinat_200594 ...

最新文章

  1. rust(20)-字符
  2. Linux 下比较文件内容并相同部分、不同部分
  3. word List 50
  4. 8、Linux基础(四)
  5. python文件移动到文件夹_python – 将文件夹中的文件移动到顶级目录
  6. Turtlebot2 ROS配置超详细教程 ubuntu16.04
  7. linux给文件备份,Linux文件备份
  8. php表格中的caption,HTML中的caption属性是什么意思?caption标签在HTML中的用法(附实例)...
  9. 信息技术前言讲座报告
  10. Doctype作用?标准模式与兼容模式各有什么区别?
  11. 关于通用人工智能的思考
  12. python读取word内容复制粘贴_如何复制word文档的内容?
  13. python获取erp数据_python自学之路-数据读取
  14. 海思开发板海图科技测评,海图海思开发板怎么样
  15. 逍遥安卓模拟器卡android,逍遥安卓模拟器很卡怎么办?
  16. 小学6年级下册计算机课教案,小学六年级下册音乐教案大全
  17. 湖北计算机自考学校都有哪些,湖北省自考本科有哪些学校?
  18. sql server 2008 r2 产品密钥(18年11月底更新,实测可用)
  19. 超详细 | 贝叶斯网络基础——有图有真相
  20. egg.js 使用笔记

热门文章

  1. 前端面试经典题目合集(HTML+CSS)
  2. 电表远程抄表系统如何保证数据传输的准确性
  3. UltraEdit批量修改文件内容
  4. 快速认识联邦学习及其难点问题
  5. 邓白氏资信报告全-Sample-Report_Business-Information-Report_CH(附下载)
  6. DAO设计-原理和实现
  7. 一、BIRT简介及安装
  8. asp.net of BF-TECH软件工程师IT高端培训课程
  9. Got a packet bigger than ‘max_allowed_packet‘ bytes的解决办法
  10. Sober算子边缘检测与Harris角点检测1