Camera2開發:CameraDevice類



0x00 說明

該文章爲本人個人學習的總結,如果遺漏或錯誤歡迎在評論區批評指出or補充。如對您有幫助可以細看之,如希望直接查看本人的學習資料來源,可直接跳到 0x05 相關資料 一節查看。

0x01 概覽

CameraDevice是連接在安卓設備上的單個相機的抽象表示, CameraDevice 支持在高幀率下對捕獲的圖像進行細粒度控制和後期處理.

在Camera2 API中,一個相機設備可能支持的硬件等級有以下幾種(按每個等級所支持的功能來排序):

LEVEL_3> FULL > LIMIT > LEGACY

按照 Google文檔 的說法,一般手機的相機可能會支持 FULL 或 LIMIT ,當一個設備支持到 LIMIT ,此時儘管 Camera2 API 具有更整潔和高效的接口,然而其只暴露出和舊的Camera API 功能相當的API,與使用舊的API無異。

而支持到 FULL 等級的設備,則提供了比舊的 Camera API 大大改進的功能,比如支持 30fps全高清連拍,支持幀之間的手動設置,支持RAW格式的圖片拍攝,快門零延遲以及視頻速拍等特性。 只有在支持 FULL 級別的設備上才能完全發揮 Camera2的特性。然而經過實測中發現,目前市面上大多安卓手機都只支持到最低級別 LEGACY (估計是爲了兼容android L 以下的系統?)......也就是說在這樣的手機上使用Camera API和Camera2 API並沒有太大區別,甚至Camera API的發揮可能還要好一些。不過,儘管如此,還是推薦使用 Camera2 API ,畢竟舊的 Camera API 已經被打上 deprecated 標籤,指不定在未來哪個android的版本中Camera API就被移除了。相機設備所支持的硬件等級是由硬件廠商決定的,可以通過以下 CameraCharacteristics 類獲取機子上的相機所支持的硬件等級參數:

CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
int hardwareLevel=characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);

其中 manager 爲 CameraManager 實例,關於 CameraManager 在上篇文章 Camera2開發(1) --CameraManager類 中有介紹。 hardwareLevel==1 爲 FULL , hardwareLevel==2 爲 LEGACY , hardwareLevel==0 爲 LIMIT , hardwareLevel==3 爲 LEVEL_3 。

0x02 內部類

StateCallback

該類用於接收相機的連接狀態的更新。這些狀態包括一系列的通知如 相機啓動結束(此時允許調用 CameraDevice.createCaptureSession(List, CameraCaptureSession.StateCallback, Handler) 創建一個捕獲會話), 相機斷開連接或關閉 以及相機 出現異常錯誤 等。

在調用 CameraDevice.openCamera(String,CameraDevice.StateCallback,Handler)方法打開相機時必須傳入一個 CameraDevice.StateCallback 實例,以用於接收相機狀態的更新和後續的處理。

典型的 CameraDevice.StateCallback 的聲明和使用如下:

private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback()
{
    @Override
    public void onOpened(@NonNull CameraDevice camera)
    {
        //當相機打開成功之後會回調此方法
        //一般在此進行獲取一個全局的CameraDevice實例,開啓相機預覽等操作
        mCameraDevice = camera;//獲取CameraDevice實例
        createCameraPreviewSession();//創建相機預覽會話
    } 
    @Override
    public void onDisconnected(@NonNull CameraDevice camera)
    {
        //相機設備失去連接(不能繼續使用)時回調此方法,同時當打開相機失敗時也會調用此方法而不會調用onOpened()
        //可在此關閉相機,清除CameraDevice引用
        camera.close();
        mCameraDevice = null;
    }
    @Override
    public void onError(@NonNull CameraDevice camera, int error)
    {
        //相機發生錯誤時調用此方法
        camera.close();
        mCameraDevice = null;
    }
    @Override
    public void onClosed(CameraDevice camera)
    {
        //相機完全關閉時回調此方法
        super.onClosed(camera);
    }
};

其中 onError(@NonNull CameraDevice camera, int error) 中的 error 參數代表出錯的代碼,可參考 Google文檔上的錯誤代碼說明。

0x03 常量

int TEMPLATE_MANUAL

Constant Value: 6 (0x00000006)

用於創建一個 直接控制相機拍照參數 的 CaptureRequest 請求。使用該模板創建的請求,相機的所有自動控制都會被禁用(例如自動曝光,自動白平衡,自動對焦等),並將後期處理參數設置成預覽質量。手動控制的參數(曝光,感光度等)將被設置成合理的默認值,用戶需要根據自己想要的預期效果來調整這些參數。

兼容性: TEMPLATE_MANUAL 在支持 MANUAL_SENSOR 功能的設備上可用。

int TEMPLATE_PREVIEW

Constant Value: 1 (0x00000001)

用於創建一個 相機預覽 請求。通常情況下,設置了該模板的預覽畫面,相機會優先保證預覽的幀率。該模板適用於相機畫面預覽,通常將該模板結合 setRepeatingRequest(CaptureRequest, CameraCaptureSession.CaptureCallback, Handler) 創建一個預覽請求。

兼容性: TEMPLATE_PREVIEW 在所有相機設備上均適用。

int TEMPLATE_RECORD

Constant Value: 3 (0x00000003)

用於創建 錄製視頻 的請求。使用該模板創建的請求將使用標準幀率,並且後期處理效果的質量將被設置成錄像級別。

兼容性: TEMPLATE_RECORD 可在所有的相機設備上使用.。

int TEMPLATE_STILL_CAPTURE

Constant Value: 2 (0x00000002)

用於創建一個 拍照 請求。使用該模板創建的請求會優先保證圖像的質量,以獲得最好的拍照效果。

兼容性:可在所有的相機設備上使用。

int TEMPLATE_VIDEO_SNAPSHOT

Constant Value: 4 (0x00000004)

用於創建 錄像時拍照 請求。使用該模板創建的請求可以最大化地保證照片的質量同時不會破壞正在錄製的視頻質量。該模板創建的請求通常與 CameraCaptureSession.capture(CaptureRequest request, CameraCaptureSession.CaptureCallback callback, Handler handler) 結合使用,其中這裏的 request 必須是基於 TEMPLATE_RECORD 模板創建。

兼容性:硬件支持級別高於 LEGACY 的所有相機設備可用。

int TEMPLATE_ZERO_SHUTTER_LAG

Constant Value: 5 (0x00000005)

用於創建 零延遲拍照 請求。使用該模板創建的請求將最大化地保證圖片質量並且不會損失預覽圖像的幀率。此時自動對焦/自動白平衡和自動曝光都應處於 auto 模式。

兼容性:該模板僅在支持 PRIVATE_PEPROCESSING 或 YUV_PEPROCESSING 特性的相機設備上適用。

0x04 常用的方法

close()

儘可能快地關閉當前相機設備的連接。

調用該方法之後,所有調用處於激活狀態的 session 和 CameraDevice 其方法的操作都將拋出一個 IllegalStateException 異常。當相機完全關閉後, CameraDevice.StateCallback.onClosed(CameraDevice) 方法會被調用,此時相機就緒以等待下一次被打開。

createCaptureRequest(int templateType)

創建一個用於構建capture request的CaptureRequest.Builder實例,並用templateType模板初始化該實例。

該設置會爲當前指定的 CameraDevice 適配最佳的選項,因此創建的 CaptureRequest 不建議重用到其他的 CameraDevice 以免出錯。

參數說明:

  • templateType:參考 0x03 常量 一節。

createCaptureSession(List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)

通過提供給相機設備的目標輸出surface集合創建一個新的相機捕獲會話(CameraCaptureSession)實例。

當前活動的 capture session 決定了相機的每一個 capture request 的輸出 surface (每個 capture request 的輸出 surface 都是 outputs 的一個子集)。比如當我需要將捕獲數據輸出到預覽 surface ,則需要將預覽 surface 添加到 outputs 作爲createCaptureSession 的參數。

在實際使用中可根據不同的用例和輸出目標來構建 surface 用於接收相機輸出的圖像數據,這裏只列出拍照功能可能用到的幾個,有關錄像的以後再補充:

  • 繪製到一個SurfaceView:

    使用 SurfaceView 來顯示預覽畫面時,應在 SurfaceView 的 Surface 創建成功後先通過 SurfaceHolder 設置其大小,再獲取 Surface 實例,如果不設置,則相機會自動爲其設置一個小於且最接近 1080p 的可用預覽尺寸:

SurfaceView mSurfaceView = (SurfaceView)findViewById(R.id.surfaceview);
SurfaceHolder mSurfaceHolder = mSurfaceView.getHolder();
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
//獲取所有可用的預覽尺寸
Size[] sizes=map.getOutputSizes(SurfaceHolder.class);
Size mPreviewSize=sizes[bestSizeIndex];
//設置surface尺寸
mSurfaceHolder.setFixedSize(mPreviewSize.getWidth(),mPreviewSize.getHeight());
//獲取預覽的surface
Surface surface=mSurfaceHolder.getSurface();
  • 通過SurfaceTexture來訪問OpenGL texture:

    使用 TextureView 來顯示預覽,要確保手機的硬件加速開啓才能進行。

    在創建 Surface 之前,需要先設置 SurfaceTexture 的大小,如果不設置,則相機會自動爲其設置一個小於且最接近 1080p 的可用預覽尺寸:

//獲取SurfaceTexture
SurfaceTexture texture = mTextureView.getSurfaceTexture();
//獲取所有可用預覽尺寸
Size[] sizes=map.getOutputSizes(SurfactTexture.class);
Size mPreviewSize=sizes[bestSizeIndex];
//設置SurfaceTexture大小
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
//獲取預覽的surface
Surface surface = new Surface(texture);
  • 結合MediaCodec進行錄像: 待補充
  • 結合MediaRecorder進行錄像: 待補充
  • 結合android.renderscript進行高效的YUV處理: 待補充
  • 在程序中訪問RAW格式或未壓縮的YUV或者壓縮的JPEG格式的圖像數據: 
    可創建一個 ImageReader 對象,通過 ImageReader 的 Surface 來接收相機圖像並保存圖像數據:
//************************
//**1.創建ImageReader對象**
//************************
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
//獲取指定格式的所有支持尺寸
Size[] sizes=map.getOutputSizes(ImageFormat.JPEG);
//計算期望的最佳尺寸,至於怎麼計算則看具體需求。如果該ImageReader的大小沒有在程序中設置,
//則當前相機設備將自動爲該ImageReader設置一個支持的小於1080p的尺寸。
Size largestSize = Collections.max(Arrays.asList(sizes), new CompareSizesByArea());
//創建ImageReader
ImageReader mImageReader = ImageReader.newInstance(largestSize.getWidth(), largestSize.getHeight(), ImageFormat.JPEG,/*maxImages*/2);

//...其他操作

/** 
* 摘自Google Sample:Camera2Basic
* Compares two {@code Size}s based on their areas. 
*/
static class CompareSizesByArea implements Comparator<Size>{
    @Override
    public int compare(Size lhs, Size rhs)
    {
        //We cast here to ensure the multiplications won't overflow
        return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());
    }
}

//...其他操作

//************************
//**2.設置ImageReader.OnImageAvailableListener監聽**
//************************
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener()
{
    @Override
    public void onImageAvailable(ImageReader reader)
    {
        //在這裏調用(reader.acquireNextImage()獲取圖像數據
        mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile));
    }
};

//...其他操作

mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);

//************************
//**3.隨後可通過ImageReader.getSurface()方法獲取Surface**
//************************
Surface surface=mImageReader.getSurface();

參數說明:

  • outputs 
    所有可用於捕獲相機輸出數據的 surface 集合。
  • callback 
    CameraCaptureSession.StateCallback實例,調用 CameraDevice.createCaptureSession 後會回調 callback 中相應的方法,可在該回調中處理創建 session 成功或失敗之後的進一步操作。
  • handler 
    指定處理該操作的 handler

getId()

獲取當前相機設備的id


來自http://www.qingpingshan.com/rjbc/az/



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