Android(安卓)骁龙Camera拍照流程梳理
本文以SnapdragonCamera为例,分析骁龙Camera的拍照流程,其实现与camera2大致相同。
首先将SnapdragonCamera源码倒入android studio, 具体操作,可查看如何用Android Studio调试Android源码一文。打开camera,点击拍照,ShutterButton类的performClick()方法会被调用。(如何定位点击拍照会调用ShutterButton类的performClick()方法,请参考使用Android Studio中的HierarchyViewer 及UI Automator Viewer定位当前UI界面的代码位置。)
@Override public boolean performClick() { boolean result = super.performClick(); if (mListener != null && getVisibility() == View.VISIBLE) { mListener.onShutterButtonClick(); } return result; }
按Ctrl+Alt+B 看onShutterButtonClick()方法是在哪里实现的。如下图,有四个地方调用,由于我们只分析拍照流程,只用查看PhotoModule即可。
在PhotoModule中,onShutterButtonClick()的实现如下
@Override public 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()函数的实现。
@Override public 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()方法。将从底层返回的数据进行处理。
@Override public 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 picture String 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 outputs if (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() { @Override public 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()方法
@Overrideprotected Uri doInBackground(Void... v) { if (width == 0 || height == 0) { // Decode bounds BitmapFactory.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()方法,通知图片已经存完。
@Override protected void onPostExecute(Uri uri) { if (listener != null) listener.onMediaSaved(uri); boolean previouslyFull = isQueueFull(); mMemoryUse -= data.length; if (isQueueFull() != previouslyFull) onQueueAvailable(); }
以上是对拍照流程的简单分析,只是大致做了梳理,有很多细节为涉及,以后再慢慢讨论。
更多相关文章
- Android(安卓)PackageManagerService(二)下载安装详解
- ListView的item高度调整
- Android(安卓)API Demo实例解析
- WakeLock使用方法示例代码
- 全志A40i Android7永不休眠及不锁屏的修改方法
- android sensor framework
- android 运行时生成dex文件,并装载调用
- android面试题总结加强再加强版(三)
- Android4.4 Camera Gallery 分离