4.拍照
拍照的流程分为两个大部分,上层和底层。上层主要分析的是四种拍张方式:普通拍照、倒计时拍照、连拍和全景拍照;底层主要分析的是jni、native、hal和jpeg数据流。
先分析上层这块。
4.1 上层分析
4.1.1 普通拍照和倒计时拍照
关于拍照的主要操作是在PhotoModule.java中,主要流程如下图:
普通拍照
可以这样理解:所有的拍照都是倒计时拍照,普通拍照的倒计时时间是0而已;
如果是非0的倒计时拍照,首先在PhotoModule里面开启倒计时的线程,然后在PhotoUI里面对倒计时进行UI上的显示,最后在倒计时结束之后调用上面介绍的拍照流程。
下面简单看一下倒计时的有关操作:
@Override
public void onShutterButtonClick() {
.............
.............
.............
/**倒计时拍照相关代码*/
String timer = mPreferences.getString(
CameraSettings.KEY_TIMER, mActivity.getString(R.string.pref_camera_timer_default));//从sharedpreferences中获取倒计时长的值
String sound = mPreferences.getString(
CameraSettings.KEY_CAPTURESOUND_KEY,
mActivity.getString(R.string.pref_camera_capturesound_default));
boolean isPlay = (sound.equals("on"));
int seconds = Integer.parseInt(timer);
// When shutter button is pressed, check whether the previous countdown is finished. If not, cancel the previous countdown and start a new one.
if (mUI.isCountingDown()) {
mUI.cancelCountDown();
}
if (seconds > 0) {//如果倒计时的秒数大于0,则进行倒计时拍照
String zsl = mPreferences.getString(CameraSettings.KEY_ZSL,
mActivity.getString(R.string.pref_camera_zsl_default));//获取zsl是否打开
mUI.overrideSettings(CameraSettings.KEY_ZSL, zsl);
mUI.startCountDown(seconds, isPlay);//开始倒计时拍照实现一些UI显示
} else {//如果倒计时的秒数等于0,即普通拍照
if(mFocusManager != null) {
mSnapshotOnIdle = false;
mFocusManager.doSnap();//
}
}
mShutterPressing = false;
}
其中ZSL (zero shutter lag) 中文名称为零延时拍照,是为了减少拍照延时,让拍照&回显瞬间完成的一种技术。用在普通拍照;
在线程中倒计时结束之后调用onCountDownFinished方法,PhotoModule.java实现了倒计时结束的监听
@Override
public void onCountDownFinished() {
mSnapshotOnIdle = false;
mFocusManager.doSnap();
mFocusManager.onShutterUp();
mUI.overrideSettings(CameraSettings.KEY_ZSL, null);
}
doSnap()方法经过进一步调用,其具体的拍照逻辑:
@Override
public boolean capture() {
................
................
................
synchronized (mCameraDevice) {
mParameters.setRotation(mJpegRotation);// 设置旋转角度信息
CameraUtil.setGpsParameters(mParameters, loc);
................
................
................
if (mCameraState != LONGSHOT) {
mUI.enableShutter(false);
}
mCapturesound = mPreferences.getString(//获取设置中的拍照声音是否打开
CameraSettings.KEY_CAPTURESOUND_KEY,
mActivity.getString(R.string.pref_camera_capturesound_default));
if (mCapturesound.equals("on")){
mCameraDevice.enableShutterSound(true);//如果打开,则拍照的时候可以启动声音
}else{
mCameraDevice.enableShutterSound(false);
}
if (mCameraState == LONGSHOT) {
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);
}
................
保存图片的回调:
private final class JpegPictureCallback
implements CameraPictureCallback {
@Override
public void onPictureTaken(final byte [] jpegData, CameraProxy camera) {
if (mCameraState != LONGSHOT) {
mUI.enableShutter(true);
}
....................
....................
....................
System.out.println(33333);
mActivity.getMediaSaveService().addImage(jpegData, title, date, mLocation, width, height,orientation, exif, mOnMediaSavedListener, mContentResolver, mPictureFormat);
....................
通过异步任务进入storage.java中的addImage方法中将图片保存到Media数据库中。
// Add the image to media store.
public static Uri addImage(ContentResolver resolver, String title,
long date, Location location, int orientation, int jpegLength,
String path, int width, int height, String mimeType) {
// Insert into MediaStore.
ContentValues values =
getContentValuesForData(title, date, location, orientation, jpegLength, path,
width, height, mimeType);
return insertImage(resolver, values);
}
4.1.2 连拍
和普通拍照的区别就在于它在回调方法里面循环执行了onPictureTaken方法,直到达到限制的最大张数就停止连拍;
连拍流程图具体的逻辑与上面的基本相同,不过有两点需要注意一下:1)连拍的间隔;2)连拍的张数限制。
对于连拍的间隔,只是在线程中一直循环执行,并没有直接设置中间间隔时长:
@Override
public void onPictureTaken(
final byte[] data, android.hardware.Camera camera) {
final android.hardware.Camera currentCamera = mCamera.getCamera();
mHandler.post(new Runnable() {
@Override
public void run() {
if (currentCamera != null && currentCamera.equals(mCamera.getCamera())) {
mCallback.onPictureTaken(data, mCamera);
}
}
});
}
对于连拍张数限制问题,高通并未做任何张数限制,思路可以这样:记录每张照相动作的次数,设置最大张数的变量,分别在LongshotShutterCallback和JpegPictureCallback中对张数进行限制即可。
4.1.3 全景拍照
全景拍照/*开始拍照*/
public void startCapture() {
// Reset values so we can do this again.
.....................
.....................
.....................
mMosaicFrameProcessor.setProgressListener(new MosaicFrameProcessor.ProgressListener() {
@Override
public void onProgress(boolean isFinished, float panningRateX, float panningRateY,float progressX, float progressY) {
float accumulatedHorizontalAngle = progressX * mHorizontalViewAngle;
float accumulatedVerticalAngle = progressY * mVerticalViewAngle;
boolean isRotated =!(mDeviceOrientationAtCapture == mDeviceOrientation);
if (isFinished
|| (Math.abs(accumulatedHorizontalAngle) >= DEFAULT_SWEEP_ANGLE)
|| (Math.abs(accumulatedVerticalAngle) >= DEFAULT_SWEEP_ANGLE)
|| isRotated) {
stopCapture(false);
} else {
float panningRateXInDegree = panningRateX * mHorizontalViewAngle;
float panningRateYInDegree = panningRateY * mVerticalViewAngle;
if (mDeviceOrientation == 180 || mDeviceOrientation == 90) {
accumulatedHorizontalAngle = -accumulatedHorizontalAngle;
accumulatedVerticalAngle = -accumulatedVerticalAngle;
}
mUI.updateCaptureProgress(panningRateXInDegree, panningRateYInDegree, accumulatedHorizontalAngle, accumulatedVerticalAngle, PANNING_SPEED_THRESHOLD);//更新拍照的进度条
}
}
});
.....................
.....................
.....................
}
public void updateCaptureProgress(
float panningRateXInDegree, float panningRateYInDegree,
float progressHorizontalAngle, float progressVerticalAngle,
float maxPanningSpeed) {
if ((Math.abs(panningRateXInDegree) > maxPanningSpeed)
|| (Math.abs(panningRateYInDegree) > maxPanningSpeed)) {
showTooFastIndication();//显示速度太快
} else {
hideTooFastIndication();//隐藏速度太快
}
// progressHorizontalAngle and progressVerticalAngle are relative to the
// camera. Convert them to UI direction.
mProgressAngle[0] = progressHorizontalAngle;
mProgressAngle[1] = progressVerticalAngle;
mProgressDirectionMatrix.mapPoints(mProgressAngle);
int angleInMajorDirection =
(Math.abs(mProgressAngle[0]) > Math.abs(mProgressAngle[1]))
? (int) mProgressAngle[0]
: (int) mProgressAngle[1];
mCaptureProgressBar.setProgress((angleInMajorDirection));
}
private void stopCapture(boolean aborted) {
System.out.println(3333);
.............
.............
.............
runBackgroundThread(new Thread() {
@Override
public void run() {
MosaicJpeg jpeg = generateFinalMosaic(false);
if (jpeg != null && jpeg.isValid) {//若图片不为空,且存在
Bitmap bitmap = null;
bitmap = BitmapFactory.decodeByteArray(jpeg.data, 0, jpeg.data.length);
mMainHandler.sendMessage(mMainHandler.obtainMessage(
MSG_LOW_RES_FINAL_MOSAIC_READY, bitmap));//保存图片
} else {
mMainHandler.sendMessage(mMainHandler.obtainMessage(
MSG_END_DIALOG_RESET_TO_PREVIEW));//回到预览界面
}
}
});
}
keepScreenOnAwhile();
}
/*保存全景视图*/
private Uri savePanorama(byte[] jpegData, int width, int height, int orientation) {
System.out.println(44444);
if (jpegData != null) {
...........
...........
...........
int jpegLength = (int) (new File(filepath).length());
return Storage.addImage(mContentResolver, filename, mTimeTaken, loc, orientation,jpegLength, filepath, width, height, LocalData.MIME_TYPE_JPEG);//保存图片
}
return null;
}
网友评论