1. 程式人生 > >Camera2 開啟相機預覽介面

Camera2 開啟相機預覽介面

camera2 是21之後的api用於代替Camera,提供更加牛X的對相機hardware操作的api

此篇筆記主要是記錄開啟預覽介面
後面會記錄Camera開啟相機預覽的程式碼,對比一下

介面佈局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
>
<com.arvin.camera3.view.AutoFitTextureView android:id="@+id/surface" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/capturebtn" android:layout_width="wrap_content" android:layout_height
="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:text="拍照" />
<ImageView android:id="@+id/preview" android:layout_width="120dp" android:layout_height="120dp" android:layout_alignParentBottom
="true" />
</RelativeLayout>

AutoFitTextureView 繼承自 TextureVeiw

1.首先要初始化view,就是一些findviewbyid

2.示例程式碼是在onResume中,能否有更好方式????

if (surfaceView.isAvailable()) {
    // FIXME: 2016/6/13
    try {
        openCamera(surfaceView.getWidth(), surfaceView.getHeight());
    } catch (Exception e) {
        e.printStackTrace();
    }
} else {
    surfaceView.setSurfaceTextureListener(mSurfaceTextureListener);
}

surfaceView即TextureView

判斷TextureView狀態
可用,開啟相機,否則設定相應的回撥

先看開啟相機的程式碼

private void openCamera(int width, int height) throws Exception {
    Activity activity = getActivity();
    CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
    if (mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
        manager.openCamera(mCameraId,deviceStateCallback,mPreviewHandler );
    }
}

首先呼叫getSystemService(Context.CAMERA_SERVICE),拿到CameraManager例項,
注:Api 21 新增的,無相容包,估計也不會發布相容包吧,21之後 Camera已棄用
然後通過CameraManager例項的 openCamera()方法開啟相機預覽
我們看下openCamera的宣告

@RequiresPermission(android.Manifest.permission.CAMERA)
public void openCamera(@NonNull String cameraId,
        @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
        throws CameraAccessException {

    if (cameraId == null) {
        throw new IllegalArgumentException("cameraId was null");
    } else if (callback == null) {
        throw new IllegalArgumentException("callback was null");
    } else if (handler == null) {
        if (Looper.myLooper() != null) {
            handler = new Handler();
        } else {
            throw new IllegalArgumentException(
                    "Handler argument is null, but no looper exists in the calling thread");
        }
    }

    openCameraDeviceUserAsync(cameraId, callback, handler);
}

@RequiresPermission(android.Manifest.permission.CAMERA)

需要camera許可權,在camera宣告,android 6.0之後需要請求,屬於runtime permission

需要傳入三個引數
cameraId : “0”表示後置攝像頭,“1”表示前置攝像頭
callback : CameraDevice.StateCallback 相機狀態回撥
注:CameraDevice 例項代表一個相機
handler : hanlder

回頭看如果TextureView不可用的狀態下

surfaceView.setSurfaceTextureListener(mSurfaceTextureListener);

看下 mSurfaceTextureListener是個什麼東西

private final TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        try {
            openCamera(width, height);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {

    }
};

這個有點和SurfaceView的回撥相似了
設定回撥,然後當surface可用的時候openCamera(),回到上面的流程去了
包含四個方法,分別對應可用,尺寸發生變化,TextureView銷燬,以及更新的時候回撥

繼續看manager.openCamera();
已經說了第一個引數camreaId,“0”代表後置攝像頭,“1”代表前置攝像頭
第二個引數CameraDevice.StateCallback, 直接看

CameraDevice.StateCallback deviceStateCallback = new CameraDevice.StateCallback() {
    @Override
    public void onOpened(CameraDevice camera) {
        mCameraOpenCloseLock.release();
        mCameraDevice = camera;
        try {
            createCameraPreviewSession();
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onDisconnected(CameraDevice camera) {
        mCameraOpenCloseLock.release();
        camera.close();
        mCameraDevice = null;
    }

    @Override
    public void onError(CameraDevice camera, int error) {
        mCameraOpenCloseLock.release();
        camera.close();
        mCameraDevice = null;
        Toast.makeText(getActivity(), "onError,error--->" + error, Toast.LENGTH_SHORT).show();
    }
};

通過程式碼可知,CameraDevice.StateCallback 提供了三個回撥方法,分別對應於camrea開啟,失去連線,開啟錯誤的時候回撥,
在開啟的時候會呼叫createCameraPreviewSession() ,看一下方法定義

private void createCameraPreviewSession() throws CameraAccessException {
    initSurface();
    mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    mPreviewBuilder.addTarget(mSurface);

    mCameraDevice.createCaptureSession(Arrays.asList(mSurface), mCaptureSessionStateCallback, mPreviewHandler);
}

private void initSurface() {
    SurfaceTexture sufaceTexture = surfaceView.getSurfaceTexture();
    assert sufaceTexture != null;
    sufaceTexture.setDefaultBufferSize(surfaceView.getWidth(), surfaceView.getHeight());
    mSurface = new Surface(sufaceTexture);
}

首先通過TextureView的getSurfaceTexture() 拿到 對應 SurfaceTexture 是不是SurfaceView的getSurfaceHolder()很像
設定顯示大小,建立Surface例項
之後,呼叫 CameraDevice createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) 建立預覽請求
並設定target, CaptureRequest.Builder addTarget(Surface surface)
然後呼叫CameraDevice createCaptureSession(),建立一個相機回話

createCaptureSession()需要傳入三個引數
看下宣告

public abstract void createCaptureSession(@NonNull List<Surface> outputs,
        @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler)

List outputs 我的理解是預覽的輸出載體列表,在上面的程式碼中,我們傳入了new Surface(TextureView.getSurface()) 物件
ImageReader 也有 getSurface()方法,ImageReader也可以作為輸出載體

第二個引數CameraCaptureSession.StateCallback 攝像頭採集狀態回撥
直接看程式碼

private CameraCaptureSession.StateCallback mCaptureSessionStateCallback = new CameraCaptureSession.StateCallback() {
    @Override
    public void onConfigured(CameraCaptureSession session) {
        if (null == mCameraDevice) {
            return;
        }
        mSession = session;
        mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
        mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
        try {
            session.setRepeatingRequest(mPreviewBuilder.build(), mSessionCaptureCallback, mPreviewHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onConfigureFailed(CameraCaptureSession session) {

    }

};

設定拍攝模式,這裡設定了連拍,flash,其他的模式嘛,我也不清楚了

mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);


session.setRepeatingRequest(mPreviewBuilder.build(), mSessionCaptureCallback, mPreviewHandler);

設定重複請求,字面上這麼理解

同樣是三個引數
第一個是CaptureRequest.Builder buildI() 這個是前面採集請求builder 通過CameraDevice createCaptureRequest建立的
第二個是攝像頭採集回話採集狀態的回撥

private CameraCaptureSession.CaptureCallback mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() {
    @Override
    public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
        super.onCaptureCompleted(session, request, result);
    }

    @Override
    public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) {
        super.onCaptureProgressed(session, request, partialResult);
    }
};

包含兩個狀態,採集進行,採集完成
可以在這裡面去做處理,
貼上一段程式碼

private CameraCaptureSession.CaptureCallback mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() {

        @Override
        public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
            mSession = session;
            if (!PreferenceHelper.getCameraFormat(getActivity()).equals("DNG")) {
                checkState(result);
            }

        }

        @Override
        public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) {
            mSession = session;
            if (!PreferenceHelper.getCameraFormat(getActivity()).equals("DNG")) {
                checkState(partialResult);
            }
        }

        private void checkState(CaptureResult result) {
//            mFrameBitmap = mPreviewView.getBitmap();
//            mMainHandler.sendEmptyMessage(1);
            switch (mState) {
                case STATE_PREVIEW:
                    // NOTHING
                    break;
                case STATE_WAITING_CAPTURE:
                    int afState = result.get(CaptureResult.CONTROL_AF_STATE);
                    Log.i("checkState", "afState--->" + afState);
                    if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState
                            || CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED == afState || CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED == afState) {
                        Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
                        Log.i("checkState", "進來了一層,aeState--->" + aeState);
                        if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
                            Log.i("checkState", "進來了第二層");
                            mState = STATE_TRY_DO_CAPTURE;
                            doStillCapture();
                        } else {
                            mState = STATE_TRY_CAPTURE_AGAIN;
                            tryCaptureAgain();
                        }
                    }
                    break;
                case STATE_TRY_CAPTURE_AGAIN:
                    Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
                    if (aeState == null ||
                            aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
                            aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
                        mState = STATE_TRY_DO_CAPTURE;
                    }
                    break;
                case STATE_TRY_DO_CAPTURE:
                    aeState = result.get(CaptureResult.CONTROL_AE_STATE);
                    if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
                        mState = STATE_TRY_DO_CAPTURE;
                        doStillCapture();
                    }
                    break;
            }
        }
};

至此,開啟相機預覽的程式碼就結束了,後續比如拍照,錄影什麼的以後再分析啦,這裡的api還沒在學習中

最後附上Camera開啟預覽的程式碼,感覺要簡單好多呀

//佈局檔案
<SurfaceView
    android:id="@+id/surface"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Camera camera;
surfaceView = (SurfaceView) findViewById(R.id.surface);
holder = surfaceView.getHolder();
holder.addCallback(this);  //設定回撥

@Override
public void surfaceCreated(SurfaceHolder holder) {
    camera = Camera.open();
    try {
        camera.setPreviewDisplay(holder);
    } catch (IOException e) {
        e.printStackTrace();
    }
    camera.startPreview();
}

//將camera預覽介面繫結到SurfaceView
//startPreview()

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {

}

程式碼是不是比camera2簡單多了,不過這個預覽介面挺難看的,其他設定也沒有仔細研究,用到的時候再說吧

相關推薦

Camera2 開啟相機介面

camera2 是21之後的api用於代替Camera,提供更加牛X的對相機hardware操作的api 此篇筆記主要是記錄開啟預覽介面 後面會記錄Camera開啟相機預覽的程式碼,對比一下 介面佈局 <?xml version="1.0"

camera2 API 開啟相機後顯示黑屏問題

最近在嘗試用camera2 API自己寫一個相機程式,先搭了一個整體的框架,主要分為以下幾步: 開啟相機->開啟預覽--->關閉相機 整體框架寫好後編譯通過,在真機上測試也沒有報錯,但是再看真機的效果,發現預覽過程中並沒有出現預覽畫面。 這個問題搞了很久,最後對比Googl

(一)Android camera2 實現相機及獲取幀資料流

一、本文重點說明 本文基於 android camera2 實現視訊預覽,暫未相容 camera1 API,基礎實現可以參考 googlesample Camera2 例子 android-Camera2Basic ,本文以工具類形式實現一步呼叫。 谷歌例子中沒有具體指

自定義相機那些坑之介面適配及原理

0.思維導圖 1.問題的產生 當Activity的oritation設定為portrait時,前置/後置攝像頭預覽影象會逆時針旋轉90度展示,如圖: 2.問題產生的本質 相機影象資料來自影象感應器(物理器件)(Image Sensor),影象感應器再將資料輸出

微信呼叫圖片介面

$("img[typename='wxpic']").click(function(){ var url=$(this).attr("src"); if (window.WeixinJSBridge) {

關於 pyspider Web介面太小的解決方法

本人最近在學習pyspider時,遇到Web預覽介面太小而無法很好的進行開發,於是在網上搜索解決方法。   準備: css程式碼: body{margin:0;padding:0;height:100%;overflow:hidden}.warning{color:#f0ad4e}.e

玩轉Android Camera開發 四 介面四周暗中間亮,只拍攝矩形區域圖片 附完整原始碼

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

怎樣在Sublime Text3 中開啟瀏覽器效果

1、首先 ctrl+shift+P  選 Package Control: Install Package 找   SideBarEnhancements 。如果 沒安裝 Install Package 先安裝    

Android Camera開發 給攝像頭介面加個ZoomBar(附完整程式碼下載)

                廢話不說了,就是加個seekbar,拖動的話能夠調節焦距,讓畫面變大或縮小。下面是核心程式:一,camera的佈局檔案<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    x

CameraView Android 相機控制元件

Github地址:https://github.com/google/cameraview 該控制元件為Google開源,非官方,只為了開發人員輕鬆整合Camera功能。 混淆: 釋出release版本時,請在主module中得proguard-rules.pro

微信高階群發之五介面

/** * * sendPreview:(通過該介面傳送訊息給指定使用者,在手機端檢視訊息的樣式和排版。). * * @author HanKeQi * @param @par

微信高階群發之介面

例子一:/** *  * sendPreview:(通過該介面傳送訊息給指定使用者,在手機端檢視訊息的樣式和排版。).   *   * @author HanKeQi   * @param  @param openId  使用者唯一標示 不能為空 * @param  @par

Android自定義相機開始時自動對焦

      如果是呼叫系統相機不會存在對焦問題,要實現自己的相機在開始的時候自動自動對焦,只需要新增兩句程式碼:     parameters.setFocusMode(Camera.Paramet

相機2.1版本一下相容setDisplayOrientation

protected void setDisplayOrientation(Camera camera, int angle){ Method downPolymorphic; try { downPolymorphic = c

如何Android studio 的佈局XML時介面彈出的Rendering Problems?

如何Android studio 的佈局XML時預覽介面彈出的Rendering Problems? 問題圖預覽:   點選“Preview”預覽選單欄的“AppTheme”,從彈出的選單框中選

Android手勢識別 Camera 介面上顯示文字 佈局注意事項(merge佈局)

通常在Surfaceview作為預覽視訊幀的載體,有時需在上面顯示提示文字。以前我弄的都好好的,今天忽然發現疊加的TextView不管咋弄都出不來文字了,跟Surfaceview一起放在FrameLayout也不行,後來想到merge佈局,發現也不行。大爺的,奇了怪了,最

Android相機方向

 以前對Camera瞭解的不是太深入,最近有需求把人臉識別 整合到專案,必然會與Camera打交道,遇到一些坑,與大家分享一下。 一、預覽方向 Camera.CameraInfo info = new Camera.CameraInfo();   首先理解一下 inf

Android使用camera實現拍照後停留在介面的問題

camera實現拍照的程式碼: /** * 開始拍照 */ public void startCapture() { if (null != camera) { camera.takePicture(nu

TextureView 做相機黑屏

特麼使用TextureView 代替Surfaceview 來做相機預覽頁,因為用Surfaceview預覽的話傳一個SurfaceHolder進去,用Textureview預覽的話需要傳進去一個SurfaceTexture,其他的Camera流程不變。de

【騰訊優測乾貨分享】Android 相機方向及其適配探索

由於Android系統的開放策略,Android手機呈現碎片化的趨勢,相容性問題一直是Android App 開發者頭疼的難題。本文以Android相機預覽方向為例,探索在Android機型適配上的一些思路。 1. android相機簡介 由於And