驍龍camera預覽拍照錄像流程 api1和api2

首先說api1的預覽流程

CameraActivity的onCreate先區分是不是安全模式(鎖屏進入就是安全模式),如果是安全模式會設置flag

indowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED這個flag會在鎖屏的時候顯示出來
        if (mSecureCamera) {
            // Change the window flags so that secure camera can show when locked
            Window win = getWindow();
            WindowManager.LayoutParams params = win.getAttributes();
            params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
            if (intent.getComponent().getClassName().equals(GESTURE_CAMERA_NAME)) {
                params.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
                PowerManager pm = ((PowerManager) getSystemService(POWER_SERVICE));
                mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
                mWakeLock.acquire();
                Log.d(TAG, "acquire wake lock");
            }
            win.setAttributes(params);

        }

然後給moduleIndex賦值,第一次進入會賦值ModuleSwitcher.PHOTO_MODULE_INDEX

然後這裏會有個api2的選擇,如果你把開發者選項(設置裏的紅眼消除按五下)裏的Camera2 Mode打開就會選擇api2,也就是

moduleIndex 的值賦了 ModuleSwitcher.CAPTURE_MODULE_INDEX;
        boolean cam2on = mSettingsManager.isCamera2On();
        if (cam2on && moduleIndex == ModuleSwitcher.PHOTO_MODULE_INDEX)
            moduleIndex = ModuleSwitcher.CAPTURE_MODULE_INDEX;

然後進入SetModuleFromIndex,通過moduleIndex給實例賦值,這裏以拍照的module爲例,如果是第一次進入就會走

mPhotoModule.init(this, mCameraPhotoModuleRootView);這裏的PhotoModule就是mvc裏的m.
            case ModuleSwitcher.PHOTO_MODULE_INDEX:
                if (mPhotoModule == null) {
                    mPhotoModule = new PhotoModule();
                    mPhotoModule.init(this, mCameraPhotoModuleRootView);
                } else {
                    mPhotoModule.reinit();
                }
                mCurrentModule = mPhotoModule;
                mCameraPhotoModuleRootView.setVisibility(View.VISIBLE);
                break;
然後我們進入PhotoModule的init,在進入之前先看一下PhotoModule的定義,實現了10個接口,再看每一個類之前最好有看定義的習慣,能大體的知道這個類裏面都幹了什麼
public class PhotoModule
        implements 
        CameraModule,//拍照,錄像都要實現的接口,裝飾者模式的
        PhotoController,// 按鈕的點擊事件處理接口ShutterButton.OnShutterButtonListener
        FocusOverlayManager.Listener,//處理所有焦點的類
        CameraPreference.OnPreferenceChangedListener,//處理
        ShutterButton.OnShutterButtonListener,//按鈕的點擊事件處理接口
        MediaSaveService.Listener,//
        OnCountDownFinishedListener,//
        LocationManager.Listener,//
        SensorEventListener,//
        MakeupLevelListener {//

現在看init,就是做了初始化ui和開啓openCamera線程.

        mUI = new PhotoUI(activity, this, parent);
        if (mOpenCameraThread == null) {
            mOpenCameraThread = new OpenCameraThread();
            mOpenCameraThread.start();
        }

這裏的ui就是mvc裏的view,負責顯示所有的控件,包括surfaceview和mSurfaceHolder的初始化

 mSurfaceView = (SurfaceView) mRootView.findViewById(R.id.mdp_preview_content);
 mSurfaceView.setVisibility(View.VISIBLE);
 mSurfaceHolder = mSurfaceView.getHolder();
 mSurfaceHolder.addCallback(this);

這裏要說一下SurfaceView和SurfaceHolder,surface的關係,SurfaceView可以理解成比普通的view多個surface,然後SurfaceView通過SurfaceHolder控制surface,surface就是用來顯示camera的圖像的,SurfaceHolder有三個callback,分別是

surfaceChanged(),surface首次顯示在屏幕上調用該方法

surfaceCreated(),surfaceview的視圖層級結構被放到屏幕上時調用該方法

surfaceDestroyed(),surface銷燬時調用

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.v(TAG, "surfaceChanged: width =" + width + ", height = " + height);
        RectF r = new RectF(mSurfaceView.getLeft(), mSurfaceView.getTop(),
                mSurfaceView.getRight(), mSurfaceView.getBottom());
        mController.onPreviewRectChanged(CameraUtil.rectFToRect(r));
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.v(TAG, "surfaceCreated");
        mSurfaceHolder = holder;
        mController.onPreviewUIReady();
        mActivity.updateThumbnail(mThumbnail);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.v(TAG, "surfaceDestroyed");
        mSurfaceHolder = null;
        mController.onPreviewUIDestroyed();
    }

然後我們繼續看mOpenCameraThread,這個thread調用了openCamera

    private class OpenCameraThread extends Thread {
        @Override
        public void run() {
            openCamera();
            startPreview();
        }
    }

openCamera裏調用了

        mCameraDevice = CameraUtil.openCamera(
                mActivity, mCameraId, mHandler,
                mActivity.getCameraOpenErrorCallback());

然後CameraUtil裏調用了CameraHolder.instance().open(handler, cameraId, cb);

    public static CameraManager.CameraProxy openCamera(
            Activity activity, final int cameraId,
            Handler handler, final CameraManager.CameraOpenErrorCallback cb) {
        try {
            throwIfCameraDisabled(activity);
            return CameraHolder.instance().open(handler, cameraId, cb);
        } catch (CameraDisabledException ex) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    cb.onCameraDisabled(cameraId);
                }
            });
        }
        return null;
    }

CameraHolder的open又調用了

mCameraDevice = CameraManagerFactory .getAndroidCameraManager().cameraOpen(handler, cameraId, cb);

                mCameraDevice = CameraManagerFactory
                        .getAndroidCameraManager().cameraOpen(handler, cameraId, cb);
而這個CameraManagerFactory .getAndroidCameraManager()返回的是AndroidCameraManagerImpl的實例
public class CameraManagerFactory {

    private static AndroidCameraManagerImpl sAndroidCameraManager;

    /**
     * Returns the android camera implementation of {@link CameraManager}.
     *
     * @return The {@link CameraManager} to control the camera device.
     */
    public static synchronized CameraManager getAndroidCameraManager() {
        if (sAndroidCameraManager == null) {
            sAndroidCameraManager = new AndroidCameraManagerImpl();
        }
        return sAndroidCameraManager;
    }
}
所以openCamera其實最終調用的是AndroidCameraManagerImpl裏的cameraOpen方法,也就是通過反射調用的camera的openLegacy,就是api1的標準接口,現在就和底層通信上了
                        try {
                            Method openMethod = Class.forName("android.hardware.Camera").getMethod(
                                    "openLegacy", int.class, int.class);
                            mCamera = (android.hardware.Camera) openMethod.invoke(
                                    null, msg.arg1, CAMERA_HAL_API_VERSION_1_0);
                        } catch (Exception e) {
                            /* Retry with open if openLegacy doesn't exist/fails */
                            Log.v(TAG, "openLegacy failed due to " + e.getMessage()
                                    + ", using open instead");
                            mCamera = android.hardware.Camera.open(msg.arg1);
                        }

然後子線程繼續調用startPreview,主要做的就是1,把camera和SurfaceHolder綁定上,mCameraDevice.setPreviewDisplay(sh);2,設置camera的參數setCameraParameters. 3開始預覽mCameraDevice.startPreview();

private void startPreview() {
        if (mPaused || mCameraDevice == null || mParameters == null) {
            return;
        }

        synchronized (mCameraDevice) {
            SurfaceHolder sh = null;
            Log.v(TAG, "startPreview: SurfaceHolder (MDP path)");
            if (mUI != null) {
                sh = mUI.getSurfaceHolder();
            }

            // Let UI set its expected aspect ratio
            mCameraDevice.setPreviewDisplay(sh);//camera和SurfaceHolder綁定上
        }

        if (!mCameraPreviewParamsReady) {
            Log.w(TAG, "startPreview: parameters for preview are not ready.");
            return;
        }
        mErrorCallback.setActivity(mActivity);
        mCameraDevice.setErrorCallback(mErrorCallback);
        // ICS camera frameworks has a bug. Face detection state is not cleared 1589
        // after taking a picture. Stop the preview to work around it. The bug
        // was fixed in JB.
        if (mCameraState != PREVIEW_STOPPED && mCameraState != INIT) {
            stopPreview();
        }

        if (!mSnapshotOnIdle) {
            mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
        }

        setCameraParameters(UPDATE_PARAM_ALL);
        mCameraDevice.setOneShotPreviewCallback(mHandler,
                new CameraManager.CameraPreviewDataCallback() {
                    @Override
                    public void onPreviewFrame(byte[] data, CameraProxy camera) {
                        mUI.hidePreviewCover();
                    }
                });
        Log.w("fy","startPreview--"+Log.getStackTraceString(new Throwable()));
        mCameraDevice.startPreview();

        mHandler.sendEmptyMessage(ON_PREVIEW_STARTED);

        setDisplayOrientation();

        if (!mSnapshotOnIdle && !mInstantCaptureSnapShot) {
            // If the focus mode is continuous autofocus, call cancelAutoFocus to
            // resume it because it may have been paused by autoFocus call.
            if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
                mCameraDevice.cancelAutoFocus();
            }
        } else {
            Log.v(TAG, "Trigger snapshot from start preview.");
            mHandler.post(mDoSnapRunnable);
        }
    }

這裏的setCameraParameters會調用了updateCameraParametersPreference,主要的功能就是將數據庫中的數據

PictureSize,PreviewSize,flash,照片質量等數據設置給camera,mCameraDevice.setParameters(mParameters);

這個時候主線程也同時調用cameraActivity的onResume,會調用mCurrentModule.onResumeBeforeSuper();和

mCurrentModule.onResumeAfterSuper();

    @Override
    public void onResume() {
...
        mCurrentModule.onResumeBeforeSuper();
        super.onResume();
        mPaused = false;
        mCurrentModule.onResumeAfterSuper();

在PhotoModule的onResumeAfterSuper裏調用了onResumeTasks();


    @Override
    public void onResumeAfterSuper() {
        mLastPhotoTakenWithRefocus = false;
        mUI.showSurfaceView();
        // Add delay on resume from lock screen only, in order to to speed up
        // the onResume --> onPause --> onResume cycle from lock screen.
        // Don't do always because letting go of thread can cause delay.
        String action = mActivity.getIntent().getAction();
        if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
                || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
            Log.v(TAG, "On resume, from lock screen.");

            // Check if there is a need to take a snapshot without
            // waiting for the shutter click
            if (isInstantCaptureEnabled()) {
                mInstantCaptureSnapShot = true;
            }

            // Note: onPauseAfterSuper() will delete this runnable, so we will
            // at most have 1 copy queued up.
            mHandler.postDelayed(new Runnable() {
                public void run() {
                    onResumeTasks();
                }
            }, ON_RESUME_TASKS_DELAY_MSEC);
        } else {
            Log.v(TAG, "On resume.");
            onResumeTasks();
        }

而onResumeTasks裏主要是調用了initializeSecondTime,作用是初始化MediaSaveService和NameImage用來保存的

    private void initializeSecondTime() {
        // Start location update if needed.
        boolean recordLocation = RecordLocationPreference.get(mPreferences);
        mLocationManager.recordLocation(recordLocation);
        MediaSaveService s = mActivity.getMediaSaveService();
        if (s != null) {
            s.setListener(this);
        }
        mNamedImages = new NamedImages();
        if (!mIsImageCaptureIntent) {
            mUI.showSwitcher();
        }
        mUI.initializeSecondTime(mParameters);
    }

這樣預覽的整個流程就走下來了。

總結:
1在xml佈局中定義一個SurfaceView,用於預覽相機採集的數據
2給SurfaceHolder添加回調,在surfaceCreated(holder: SurfaceHolder?)回調中打開相機
3成功打開相機後,設置相機參數。比如:對焦模式,預覽大小,照片保存大小等等
4設置相機預覽時的旋轉角度,然後調用startPreview()開始預覽

拍照流程先看ShutterButton的performClick,調用了mListener.onShutterButtonClick();

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

而PhotoModule實現了這個接口,最終調用了PhotoModule的onShutterButtonClick

先看是不是倒計時拍照,不是就調用initiateSnap();


    @Override
    public synchronized void onShutterButtonClick() {
...
        String timer = mPreferences.getString(
                CameraSettings.KEY_TIMER,
                mActivity.getString(R.string.pref_camera_timer_default));
        boolean playSound = mPreferences.getString(CameraSettings.KEY_TIMER_SOUND_EFFECTS,
                mActivity.getString(R.string.pref_camera_timer_sound_default))
                .equals(mActivity.getString(R.string.setting_on_value));

        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) {
            String zsl = mPreferences.getString(CameraSettings.KEY_ZSL,
                    mActivity.getString(R.string.pref_camera_zsl_default));
            mUI.overrideSettings(CameraSettings.KEY_ZSL, zsl);
            mUI.startCountDown(seconds, isShutterSoundOn());
        } else {
            mSnapshotOnIdle = false;
            initiateSnap();
        }
    }
然後在initiateSnap中要看是不是自拍,是自拍要啓線程調用mFocusManager.doSnap();不是自拍直接調用mFocusManager.doSnap()
    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.start();
            }
        } else {
            mFocusManager.doSnap();
        }
    }

然後doSnap會調用capture,會最終調用mListener.capture().

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

這個mListener是PhotoModule,所以調用了PhotoModule的capture()

首先調用了mCaptureStartTime = System.currentTimeMillis();記錄系統的當前時間

mReceivedSnapNum = 0;是當前的張數,最終調用了mCameraDevice.takePicture(mHandler,
        new ShutterCallback(!animateBefore),
        mRawPictureCallback, mPostViewPictureCallback,
        new JpegPictureCallback(loc));
@Override
    public boolean capture() {
        // If we are already in the middle of taking a snapshot or the image save request
        // is full then ignore.
        Log.w(TAG,"capture");
        if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
                || mCameraState == SWITCHING_CAMERA
                || mActivity.getMediaSaveService() == null
                || mActivity.getMediaSaveService().isQueueFull()) {
            return false;
        }
        mCaptureStartTime = System.currentTimeMillis();
        mPostViewPictureCallbackTime = 0;
        mJpegImageData = null;

        final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
        if(mHiston) {
            if (mSnapshotMode != CameraInfo.CAMERA_SUPPORT_MODE_ZSL) {
                mHiston = false;
                mCameraDevice.setHistogramMode(null);
            }
            mActivity.runOnUiThread(new Runnable() {
                public void run() {
                    if(mGraphView != null)
                        mGraphView.setVisibility(View.INVISIBLE);
                }
            });
        }

        if (animateBefore) {
            animateAfterShutter();
        }

        if (mCameraState == LONGSHOT) {
            mCameraDevice.setLongshot(true);
        }

        // Set rotation and gps data.
        int orientation = mOrientation;
        mJpegRotation = CameraUtil.getJpegRotation(mCameraId, orientation);
        String pictureFormat = mParameters.get(KEY_PICTURE_FORMAT);
        Location loc = getLocationAccordPictureFormat(pictureFormat);

        synchronized (mCameraDevice) {
            if (mCameraId == CAMERA_ID_MAIN) {
                mParameters.setFlashMode(Parameters.FLASH_MODE_OFF);
            }
            mParameters.setRotation(mJpegRotation);
            CameraUtil.setGpsParameters(mParameters, loc);

            if (mRefocus) {
                mParameters.set(CameraSettings.KEY_QC_LEGACY_BURST,
                        CameraSettings.KEY_QC_RE_FOCUS_COUNT);
            } else {
                mParameters.remove(CameraSettings.KEY_QC_LEGACY_BURST);
            }

            // Unlock AE&AWB, if they continue
            // to be locked during snapshot, then
            // side effects could be triggered w.r.t.
            // flash.
            mFocusManager.setAeAwbLock(false);
            setAutoExposureLockIfSupported();
            setAutoWhiteBalanceLockIfSupported();

            mCameraDevice.setParameters(mParameters);
            mParameters = mCameraDevice.getParameters();
        }

        mBurstSnapNum = mParameters.getInt("num-snaps-per-shutter");
        mReceivedSnapNum = 0;
        mPreviewRestartSupport = SystemProperties.getBoolean(
                PERSIST_PREVIEW_RESTART, false);
        mPreviewRestartSupport &= CameraSettings.isInternalPreviewSupported(
                mParameters);
        mPreviewRestartSupport &= (mBurstSnapNum == 1);
        // Restart is needed  if HDR is enabled
        mPreviewRestartSupport &= !CameraUtil.SCENE_MODE_HDR.equals(mSceneMode);
        mPreviewRestartSupport &= PIXEL_FORMAT_JPEG.equalsIgnoreCase(
                pictureFormat);

        // We don't want user to press the button again while taking a
        // multi-second HDR photo. For longshot, no need to disable.
        if (mCameraState != LONGSHOT) {
            mUI.enableShutter(false);
        }

        if (!isShutterSoundOn()) {
            mCameraDevice.enableShutterSound(false);
        } else {
            mCameraDevice.enableShutterSound(!mRefocus);
        }
        Log.w(TAG,"mCameraState="+mCameraState);
        if (mCameraState == LONGSHOT) {
            mLongShotCaptureCountLimit = SystemProperties.getInt(
                                    "persist.camera.longshot.shotnum", 0);
            mLongShotCaptureCount = 1;
            Log.w(TAG,"mLongshotSave1="+mLongshotSave);
            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;
    }

這裏的takePicture(shutter, raw, postView, jpeg)裏面的參數分別是
shutter是在加工處理原始圖像數據且沒有存儲之前,圖像捕捉時刻的回調,或爲空
raw原始(未壓縮)圖像數據的回調
postview包含postview圖像數據的回調(一般需要硬件支持)
 jpeg jpeg圖像數據的回調

驍龍camera裏只實現了shutter和jpeg數據的回調,shutter主要做的事情

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章