Media Module之Camera(四) 拍照 上层分析

2017-06-02  本文已影响256人  Lemon_Home

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;
    }
上一篇下一篇

猜你喜欢

热点阅读