Camera開發系列之一 顯示攝像頭實時畫面

靚仔凌霄發表於2018-06-11

章節

Camera開發系列之一-顯示攝像頭實時畫面

Camera開發系列之二-相機預覽資料回撥

Camera開發系列之三-相機資料硬編碼為h264

Camera開發系列之四-使用MediaMuxer封裝編碼後的音視訊到mp4容器

Camera開發系列之五-使用MediaExtractor製作一個簡易播放器

Camera開發系列之六-使用mina框架實現視訊推流

Camera開發系列之七-使用GLSurfaceviw繪製Camera預覽畫面 最近一直在做安卓攝像頭方面的功能,不得不說這裡面的坑簡直多的一批,要注意的地方簡直不要太多,可以說是從入門到入土系列。總之,這玩意兒差點要我的老命。

本系列文章使用的都是使用android.hardware.Camera包下面的api,並沒有使用Camer2。主要是考慮到相容性問題,另外一個很重要的原因是我還沒看camera2的文件,啥都不會=_=。

本片文章主要分為以下幾個小點講解:

  1. 如何呼叫手機攝像頭並獲取實時資料
  2. 如何顯示攝像頭預覽畫面
  3. 在使用攝像頭時注意的地方

實現相機預覽

Mainfest許可權申明

在開發裝置相機之前,你需要在Mainfest中申明如下許可權:

<uses-permission android:name="android.permission.CAMERA" />
複製程式碼

當然,上面是最基本的許可權,如果你的應用需要保持照片或者視訊到裝置儲存中,你必須在Manifest指定檔案的寫許可權:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
複製程式碼

如果還需要錄影功能,則要新增錄音許可權:

<uses-permission android:name="android.permission.RECORD_AUDIO" />
複製程式碼

當然如果你需要拍攝的照片記錄地理位置,你同樣需要申請如下許可權:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
複製程式碼

申明許可權之後,就可以使用camera相關的api了

檢測相機硬體

在用相機之前,先要幹什麼呢?當然是要先檢測該裝置是否有相機硬體,別說你的手機都有,你做的應用是不是你一個人使用你心裡沒有一點13數麼?如果有相機硬體,才進一步去訪問相機,如下是檢測相機硬體是否存在是程式碼示例:

private boolean checkCameraHardware(Context context) {  if(context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
        return true;
    } else {
        return false;
    }
}
複製程式碼

Android 裝置可以有多個相機硬體,現在一般手機都是前後兩個camera,後置攝像頭一般裝置id為0,前置攝像頭一般裝置id為1。為了訪問相機基本功能,可以使用Camera的open()方法來獲得一個Camera的例項。

try {
    Camera camera = Camera.open(mCamerId);
}catch (Exception e){
    LogUtil.i("攝像頭被佔用");
    e.printStackTrace();
}
複製程式碼

這裡記得要捕獲一下異常,有可能其他應用程式佔用了相機而crash。

獲取&設定相機引數

一旦你可以成功訪問相機裝置,你可以使用Camera#getParameters()方法來獲取相機引數資訊,可以根據 返回值 Camera.Parameters 類來檢視當前camea支援哪些引數設定等。也可以使用Camera#setParameters方法給相機設定相應的引數。

private void initCamera(int width,int height){
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setPreviewFormat(ImageFormat.NV21);
        //根據設定的寬高 和手機支援的解析度對比計算出合適的寬高演算法
        Camera.Size optionSize = CameraUtil
                .getInstance(mCamera, mCamerId)
                .getOptimalPreviewSize(width, height);
        parameters.setPreviewSize(optionSize.width, optionSize.height);
        //設定照片尺寸
        parameters.setPictureSize(optionSize.width, optionSize.height);
        //設定實時對焦 部分手機不支援會crash
       
    parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
        mCamera.setParameters(parameters);
        //開啟預覽
        mCamera.startPreview();
    }
複製程式碼

顯示相機預覽畫面

相機準備就緒,但是,還差最後一步,為了有效的拍照或者錄影,我們必須在螢幕上能看到相機的預覽。一個相機預覽類是由SurfaceView控制元件來實時顯示來自camera的預覽資料,如此我們才能看到每一幀資料和捕獲圖片或者視訊。

mSurfaceView = findViewById(R.id.surfaceView);
mHolder = mSurfaceView.getHolder();
mHolder.addCallback(new SurfaceCallback());
複製程式碼

SurfaceCallback是個什麼呢?SurfaceCallback繼承SurfaceView.Callback介面類,並且需要實現裡面的介面方法以便監聽SurfaceView控制元件的建立以及銷燬事件的回撥,在回撥方法中關聯相機預覽顯示。

private class SurfaceCallback implements SurfaceHolder.Callback {

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            try {
                mCamera = Camera.open(mCamerId);
            }catch (Exception e){
                LogUtil.i("攝像頭被佔用");
                e.printStackTrace();
            }
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            try {
                initCamera(mSurfaceView.getWidth(),mSurfaceView.getHeight());
                mCamera.setPreviewDisplay(holder);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {

        }
    }
複製程式碼

佈局檔案很簡單:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
         />
</LinearLayout>
複製程式碼

現在就可以點選執行了,執行之後你會看到如下效果:

Camera開發系列之一  顯示攝像頭實時畫面

咦,怎麼回事,畫面怎麼是旋轉的,是程式碼有問題嗎?nonono,兄弟不要慌,camera 預覽預設的方向是橫屏的,故在該例子中佈局指定水平方向以及固定該應用為橫屏顯示。為了簡便渲染camera預覽,你可以在manifest配置檔案中指定Activity的方向為橫屏。

<activity android:name=".MainActivity" android:screenOrientation="landscape">
複製程式碼

也可以使用如下方法,在startPreview()呼叫之前呼叫該方法:

/**
     * 得到攝像頭預設旋轉角度後,旋轉回來  注意是逆時針旋轉
     *
     * @param activity
     */
    public void setCameraDisplayOrientation(Activity activity) {
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(mCameraId, info);
        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
        }
        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360; // compensate the mirror
        } else { // back-facing
            result = (info.orientation - degrees + 360) % 360;
        }
        LogUtil.i("攝像頭被旋轉的角度;" + result);
        mOrienta = result;//該值有其它用途
        mCamera.setDisplayOrientation(result);
    }
複製程式碼

為了美觀,弄個全屏主題~

<!--全屏-->
    <style name="FullScreenTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowFullscreen">true</item>
        <item name="android:windowNoTitle">true</item>
    </style>
複製程式碼

最後,使用完camera之後記得釋放camera資源。

if (null != camera) {
    camera.stopPreview();
    camera.release();
    camera = null;
}
複製程式碼

專案地址:github 歡迎start和fork

相關文章