美文网首页
Media Module之Camera(四) 拍照 上层分析

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

作者: Lemon_Home | 来源:发表于2017-06-02 20:05 被阅读256次

    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;
        }
    

    相关文章

      网友评论

          本文标题:Media Module之Camera(四) 拍照 上层分析

          本文链接:https://www.haomeiwen.com/subject/yfbjfxtx.html