1. 程式人生 > >Android Camera 系列(三)Camera API 詳解

Android Camera 系列(三)Camera API 詳解

概述

Camera 可能是接下來個人想深入學習的課題,準備新起一個系列,從個人的角度總結闡述自己對於 Android Camera 的研究過程,希望也能夠對其他想學習 Camera 的同學一些幫助。

本小節內容為 Android Camera 官方文件 的精要翻譯,原文請參考:

正文

Android Framework 包括對裝置上可用的攝像頭和攝像頭功能的支援,以達到在應用程式中 拍照和錄製視訊 的目的。 本文件討論了一種快速,簡單的拍照和錄製視訊的實現方法,並概述了為使用者建立 自定義相機體驗 的高階使用說明。

請注意:本文示例程式碼使用的是已棄用的Camera類。 Google官方建議使用新的API Camera2

,它適用於Android 5.0(API級別21)或更高版本——但是國內的Rom似乎對Camera2的支援都不是非常友好,並且僅支援API21以上的Android系統,因此Camera依然有研究學習的價值。

一、注意事項

您的APP在Android裝置上使用Camera相關API之前,應該注意的:

1.攝像頭要求

您應該在Manifest.xml中宣告Camera的使用,以保證安裝App的Android裝置都有Camera的硬體支援。

2.優先考慮使用系統相機

如果您只想拍攝快照錄製視訊,而不會使用Camera的其它API,請考慮使用系統相機。

3.前臺服務要求

在Android 9(API級別28)及更高版本中,在後臺執行的應用程式無法訪問攝像頭。因此,您應該在應用程式位於前臺或作為前臺服務的一部分時使用相機。

4.儲存

您的應用程式生成的 影象或視訊是否僅對您的應用程式 可見或共享,以便其他應用程式(比如 相簿 或者其他應用)可以使用它們?即使您的應用程式已解除安裝,您是否希望圖片和視訊同樣被刪除?

二、基礎

Android FrameWork 支援通過Camera2 API或相機Intent捕獲影象和視訊。 以下是相關官方API文件(注意:下面連結請自備梯子):

控制Android裝置攝像頭的主要API。 在構建相機應用程式時,它可用於拍攝照片或視訊。

用於控制裝置相機,較舊的、已棄用的API。

相機捕獲的影象都會在展示在上面預覽

用於錄製來自攝像機的視訊。

直接通過Intent訪問系統相機,而無需呼叫Camera

物件。

三、使用系統相機

上面2個link對應的中文部落格請參考這篇:

四、構建相機應用

建立一個自定義的相機步驟如下:

  • 檢測並獲取相機
  • 建立一個預覽類:建立一個繼承自SurfaceView的類並實現SurfaceHolder介面。這個類預覽相機的實時影象。
  • 構建一個預覽介面:建立完相機預覽類以後,建立一個佈局以展示預覽。
  • 增加監聽:增加對應的監聽,以操作Camera的行為,比如點選按鈕拍照。
  • 採集並儲存檔案 :將拍照圖片或視訊儲存。
  • 釋放相機資源 -使用完相機以後,必須正確釋放相機資源,以供其他應用繼續使用

1.檢測相機硬體

/** 檢查相機是否可用 **/
fun cameraHardwareAvailable() =
        packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)

2.訪問相機

如果您確定Android裝置有攝像頭,則必須通過獲取Camera例項來使用相關API(除非您使用Intent訪問系統相機)。

要訪問主攝像頭,請使用Camera.open()方法並確保捕獲任何異常,如下面的程式碼所示:

/** A safe way to get an instance of the Camera object. */
fun getCameraInstance(): Camera? {
    return try {
        Camera.open() // attempt to get a Camera instance
    } catch (e: Exception) {
        // Camera is not available (in use or does not exist)
        null // returns null if camera is unavailable
    }
}

警告:使用Camera.open()時,請務必檢查異常。 如果相機正在使用或不存在,則無法檢查異常將導致崩潰。

在 Android 2.3 以上,你可以使用Camera.open(int)獲取特定的相機,它將在有多個相機的裝置上獲取第一個後置攝像頭

3.檢查相機特性

得到Camera例項以後,你可以使用Camera.getParameters()方法獲取關於相機的更多屬性引數Parameters物件。

4.建立一個預覽類

以下的示例程式碼展示瞭如何建立一個基本的相機預覽類:

/** A basic Camera preview class */
class CameraPreview(
        context: Context,
        private val mCamera: Camera
) : SurfaceView(context), SurfaceHolder.Callback {

    private val mHolder: SurfaceHolder = holder.apply {
        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        addCallback(this@CameraPreview)
        // deprecated setting, but required on Android versions prior to 3.0
        setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)
    }

    override fun surfaceCreated(holder: SurfaceHolder) {
        // The Surface has been created, now tell the camera where to draw the preview.
        mCamera.apply {
            try {
                setPreviewDisplay(holder)
                startPreview()
            } catch (e: IOException) {
                Log.d(TAG, "Error setting camera preview: ${e.message}")
            }
        }
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.
        if (mHolder.surface == null) {
            // preview surface does not exist
            return
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview()
        } catch (e: Exception) {
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here

        // start preview with new settings
        mCamera.apply {
            try {
                setPreviewDisplay(mHolder)
                startPreview()
            } catch (e: Exception) {
                Log.d(TAG, "Error starting camera preview: ${e.message}")
            }
        }
    }
}

如果要為相機的預覽設定特定大小,請在surfaceChanged()方法中進行設定,如上面的註釋中所述。 設定預覽大小時,必須使用getSupportedPreviewSizes()中的值。不要在setPreviewSize()方法中隨意地設定值。

注意:隨著Android 7.0(API24)及更高級別中的多視窗功能的引入,即使在呼叫setDisplayOrientation()之後,您也不能再認為預覽的寬高比與您的活動相同。 根據視窗大小和寬高比,您可能需要使用特定佈局將寬相機預覽適合縱向佈局,反之亦然。

5.將相機預覽展示出來

以下程式碼展示如何進行預覽。其中FrameLayout用來作為相機預覽類的佈局容器:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
  <FrameLayout
    android:id="@+id/camera_preview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1"
    />

  <Button
    android:id="@+id/button_capture"
    android:text="Capture"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    />
</LinearLayout>

在大多數裝置上,相機的預設物理方向為橫屏:

<activity android:name=".CameraActivity"
          android:label="@string/app_name"
          android:screenOrientation="landscape"/>

接下來的程式碼展示了如何部署展示你的相機介面

class CameraActivity : Activity() {

    private var mCamera: Camera? = null
    private var mPreview: CameraPreview? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 該函式程式碼,請參考上面的【訪問相機】小節
        mCamera = getCameraInstance()

        mPreview = mCamera?.let {
            // Create our Preview view
            CameraPreview(this, it)
        }

        // Set the Preview view as the content of our activity.
        mPreview?.also {
            val preview: FrameLayout = findViewById(R.id.camera_preview)
            preview.addView(it)
        }
    }
}

6.拍照

實現拍照,請使用Camera.takePicture()方法。 該方法採用三個從攝像機接收資料的引數, 要以JPEG格式接收資料,必須實現Camera.PictureCallback介面以接收影象資料並將其寫入檔案。以下程式碼顯示了Camera.PictureCallback介面的基本實現,用於儲存從相機接收的影象:

private val mPicture = Camera.PictureCallback { data, _ ->
    val pictureFile: File = getOutputMediaFile(MEDIA_TYPE_IMAGE) ?: run {
        Log.d(TAG, ("Error creating media file, check storage permissions"))
        return@PictureCallback
    }

    try {
        val fos = FileOutputStream(pictureFile)
        fos.write(data)
        fos.close()
    } catch (e: FileNotFoundException) {
        Log.d(TAG, "File not found: ${e.message}")
    } catch (e: IOException) {
        Log.d(TAG, "Error accessing file: ${e.message}")
    }
}

然後將這個Callback在合理的時機使用:

val captureButton: Button = findViewById(R.id.button_capture)
captureButton.setOnClickListener {
    // get an image from the camera
    mCamera?.takePicture(null, null, mPicture)
}

7.錄製視訊

錄製視訊的實現相對複雜,而官方文件提供的三言兩語略顯單薄,因此該內容不放在本小節中,我將在之後的一篇文章中進行闡述。

8.釋放Camera資源

class CameraActivity : Activity() {
    private var mCamera: Camera?
    private var mPreview: SurfaceView?
    private var mMediaRecorder: MediaRecorder?

    override fun onPause() {
        super.onPause()
        releaseMediaRecorder() // if you are using MediaRecorder, release it first
        releaseCamera() // release the camera immediately on pause event
    }

    private fun releaseMediaRecorder() {
        mMediaRecorder?.reset() // clear recorder configuration
        mMediaRecorder?.release() // release the recorder object
        mMediaRecorder = null
        mCamera?.lock() // lock camera for later use
    }

    private fun releaseCamera() {
        mCamera?.release() // release the camera for other applications
        mCamera = null
    }
}

上述三篇文章主要來自Google官方的Camera文件,令人不解的是,官方的幾篇文件互相之間連結不上,很多地方還有令人看不懂的引數,因此僅作為系統入門文章勉強適合,接下來的文章將會從不同角度闡述Camera使用開發過程中的一些疑點難點。

相關推薦

Android Camera 系列Camera API

概述 Camera 可能是接下來個人想深入學習的課題,準備新起一個系列,從個人的角度總結闡述自己對於 Android Camera 的研究過程,希望也能夠對其他想學習 Camera 的同學一些幫助。 本小節內容為 Android Camera 官方文件 的精要

OSGI學習系列MANIFEST.MF

<一>在osgi專案中META-INF目錄下有一個MANIFEST.MF檔案,是載入bundle時必不可少的,如下圖所示: <二>下面簡單解釋一下其中的幾個元素 #幾個必須的

Kafka 系列—— Kafka 生產者

一、生產者傳送訊息的過程 首先介紹一下 Kafka 生產者傳送訊息的過程: Kafka 會將傳送訊息包裝為 ProducerRecord 物件, ProducerRecord 物件包含了目標主題和要傳送的內容,同時還可以指定鍵和分割槽。在傳送 ProducerRecord 物件前,生產者會先把鍵和值物件序列

Android Studio系列使用Version Control管理多倉庫多分支原始碼

開發android系統原始碼的同學都知道,我們的工作都是很多人協同工作,因此git版本管理及歷史修改查閱異常重要!甚至比開發app重要的多! 此文旨在介紹一下用AS中自帶的Version Control工具來管理android系統原始碼,鑑於android原始

Centos7系列邏輯卷

centos7 邏輯卷 博主QQ:819594300博客地址:http://zpf666.blog.51cto.com/有什麽疑問的朋友可以聯系博主,博主會幫你們解答,謝謝支持!Centos7可以用xfs_growfs來擴大XFS文件系統,用resize2fs 來擴大ext4文件系統,註意的是 XFS

Java8學習筆記--Stream API[轉]

有效 編程效率 實時處理 phaser 綜合 files -- bin 並發模式 為什麽要使用StreamStream 作為 Java 8 的一大亮點,它與 java.io 包裏的 InputStream 和 OutputStream 是完全不同的概念。它也不同於 StAX

Hibernate API

delet hibernate load 類型變量 nbsp ria 每次 transacti llb 一、Configuration類 用來加載默認文件路徑下的配置文件(hibernate.properties)。 調用configure()方法會加載默認文件路徑下的xm

探索 SpringBoot 啟動流程(下)

探索 SpringBoot (三) 啟動流程詳解(下) 文章目錄 探索 SpringBoot (三) 啟動流程詳解(下) 4 SpringBoot 執行階段 4.1 Spring 應用執行監聽者的載入和執行 4.2

python標準庫常用模組-----------------------------OS模組,呼叫系統的命令

OS模組是呼叫系統命令的模組,和執行cmd的命令相似。 1.獲取當前的工作目錄:getcwd() 2.改變當前指令碼的工作目錄:chdir(path) 3.返回當前目錄:curdir() 4.獲取當前目錄的父目錄:pardir() 5.若目錄為空,則刪除,遞迴到上一

kaldi筆記train_mono.sh

train_mono.sh 是音素訓練指令碼,下面詳細介紹各個功能: 1.首先是初始化GMM,使用的指令碼是/kaldi-trunk/src/gmmbin/gmm-init-mono,輸出是0.mdl和tree檔案; 2.compile training graphs,使用的指令碼是/kal

xlwt模組--合併單元格

 本篇部落格主要展示了xlwt中合併單元格的操作 程式碼如下(純屬自學期間,如有更好的方法,請在下面評論區留下意見或建議,多謝!): #!/usr/bin/env python3.6 # encod

解讀ASP.NET 5 & MVC6系列6:Middleware

在第1章專案結構分析中,我們提到Startup.cs作為整個程式的入口點,等同於傳統的Global.asax檔案,即:用於初始化系統級的資訊(例如,MVC中的路由配置)。本章我們就來一一分析,在這裡如何初始化這些系統級的資訊。 新舊版本之間的Pipeline區別 ASP.NET 5和之前版本的最大區別是對HT

目標檢測與分割:SSD

SSD github : https://github.com/weiliu89/caffe/tree/ssd SSD paper : https://arxiv.org/abs/1512.02325 SSD eccv2016 slide pdf : http://d

Mybatis學習————— 對映檔案

   前面說了全域性配置檔案中內容的詳解,大家應該清楚了,現在來說說這對映檔案,這章就對輸入對映、輸出對映、動態sql這幾個知識點進行說明,其中高階對映(一對一,一對多,多對多對映)在下一章進行說明。   一、輸入對映       輸入對映:配置statement中輸入引數的型別。有四種  

MVC之前的那點事兒系列3:HttpRuntime分析

文章內容 話說,經過各種各樣複雜的我們不知道的內部處理,非託管程式碼正式開始呼叫ISPAIRuntime的ProcessRequest方法了(ISPAIRuntime繼承了IISPAIRuntime介面,該介面可以和COM進行互動,並且暴露了ProcessRequest介面方法)。至於為什麼要呼叫這個方法,

MVC之前的那點事兒系列2:HttpRuntime分析

文章內容 從上章文章都知道,asp.net是執行在HttpRuntime裡的,但是從CLR如何進入HttpRuntime的,可能大家都不太清晰。本章節就是通過深入分析.Net4的原始碼來展示其中的重要步驟。請先看下圖:   首先,CLR在初始化載入的時候,會載入一個非常重要的類AppManagerApp

Spring Cloud Eureka 入門 服務消費者

摘要: 原創出處:www.bysocket.com 泥瓦匠BYSocket 希望轉載,保留摘要,謝謝! “真正的進步,不在於學習,而在於反思”  「Spring Cloud Eureka 入門系列」本文提綱 1.  springcloud-eureka-sample 工程介紹 2. 執行 spring

MySQL基礎SELECT語句

1.基本select語句SELECT name,age FROM employee;2.數學符號條件SELECT name,age FROM employee WHERE age>25;SELECT name,age,phone FROM employee WHERE

VSCode外掛開發全攻略package.json

package.json 在詳細介紹vscode外掛開發細節之前,這裡我們先詳細介紹一下vscode外掛的package.json寫法,但是建議先只需要隨便看一下,瞭解個大概,等後面講到具體細節的時候再回過頭來看。 如下是package.json檔案的常用配置,當然這裡還不是全部: { // 外掛的

SpringBoot 2.0 系列:流程

寫在前面 本節將詳細介紹如何使用Spring Boot。它涵蓋了諸如專案管理及自動構建工具、自動配置以及如何執行應用程式等主題。我們還介紹了一些Spring Boot最佳實踐。Spring Boot沒有什麼特別之處(它只是另一個我們可以使用的庫),但是有一些約