攝像頭Camera視頻源數據採集解析

一、前言

在視頻直播中一般都是兩種視頻數據源,一個是攝像頭數據,一個是錄製桌面數據,而一般來說美女妹子直播都是來自於攝像頭數據,遊戲直播都是錄製桌面數據的,那麼今天就來看看第一個數據源數據採集分析,Android中使用攝像頭的場景很多,在沒有直播這個行業出現之前,之前用到攝像頭的最多就兩個場景,一個是二維碼掃描,一個是美顏拍照類的應用。那麼這裏就來看看Android中的攝像頭的用法,以及如何進行數據採集進行數據的二次加工。

二、知識點概述

本篇文章主要通過如下幾個方向去介紹Android中的攝像頭Camera知識:
1、Camera攝像頭的基本操作
2、Camera攝像頭的前置和後置區分
3、Camera攝像頭的數據格式
4、Camera攝像頭方向和數據尺寸
5、Camera攝像頭的對焦拍照
6、Camera攝像頭的數據採集以及二次加工

先來看看一張效果圖:
這裏寫圖片描述

三、知識點詳解

通過上面的效果圖,可以看到,可以切換前置和後置攝像頭,可以對焦拍照,可以加水印效果。下面就來一一介紹內容

第一、Camera攝像頭的基本操作

Android中使用一個攝像頭其實很簡單,首先需要在AndroidManifest.xml中聲明權限:
這裏寫圖片描述
然後代碼中進行初始化操作即可:
這裏寫圖片描述
初始化操作比較簡單,就幾步:
1、第一步:打開攝像頭,使用open方法
這個方法有兩種形式,一種是無參數形式的,默認打開是後置攝像頭,還有一種方式是有參數像是,可以通過傳遞的參數來決定打開是前置還是後置攝像頭,0代表後置攝像頭,1代表前置攝像頭。
2、第二步:設置攝像頭的預覽數據界面
可以不進行設置的,如果預覽一般有兩種方式,一種是調用setPreviewDisplay方法設置SurfaceHolder,也就是和SurfaceView進行綁定了,還有一種就是調用setPreviewTexture方法設置SurfaceTexture的,這個就和GLSurfaceView以及TextureView綁定了,這兩種方式在介紹視頻直播的基礎知識的時候已經介紹了,還不瞭解的同學可以點擊這裏:基礎知識介紹。後續如果要做美顏效果的話GLSurfaceView用的就比較多了,因爲他本身集成了OpenGL的功能,而且二次處理的數據可以進行回顯的。
3、第三步:獲取到Camera.Parameters參數信息
通過getParameters方法獲取攝像頭已有的參數信息,然後進行相關設置,比如尺寸大小,方向,數據格式等信息。
4、第四步:在把添加好的參數信息設置回去,調用startPreview開始預覽效果了

同樣的攝像頭的銷燬方法也很簡單:
這裏寫圖片描述
也就四步:
1、第一步:將攝像頭的預覽清空
2、第二步:停止預覽效果
3、第三步:釋放攝像頭
因爲系統默認只能同時開啓一個攝像頭不管是前置攝像頭還是後置攝像頭,所以不用的時候一定要釋放
4、第四步:置空攝像頭對象

第二、Camera攝像頭的前置和後置區分

Android中的攝像頭Camera是區分前置和後置的,所以這裏就要做一個前置和後置攝像頭的切換功能了,我們可以通過一個方法來獲取當前系統的攝像頭個數,以及攝像頭的信息:
這裏寫圖片描述
這個方法先獲取當前設備中有多少個攝像頭,一般現在設備都是兩個,一個是前置,一個是後置的,我們得到信息之後在打印看看效果:
這裏寫圖片描述
看到了,只有兩個攝像頭,而且通過默認的旋轉角度得知,後置攝像頭是90,前置是270,他們始終相差180度,不過這些和後面要說到的設置攝像頭方向的效果沒關係的。
那麼切換攝像頭Android中的做法是把之前的攝像頭先銷燬在重新初始化下一個攝像頭:
這裏寫圖片描述
然後用一個全局的變量來記錄當前是前置還是後置,在初始化方法中直接使用這個變量:
這裏寫圖片描述

第三、Camera攝像頭的數據格式

Android中攝像頭的數據格式是指原生的每一幀數據,他是有指定格式的,而這些原生的每一幀數據可以有兩個地方獲取,一個是添加回調,在onPreviewFrame(byte[] data, Camera camera)回調方法中獲取,還有一個就是拍照的時候回調方法:onPictureTaken(byte[] data, Camera camera),當然如果想處理數據的話,肯定是在第一個回調方法中去進行操作了,第二種必須在拍照的時候把當前拍照的一幀數據拿到,這明顯不靠譜。不管是那種獲取一幀數據,這些都是有一定格式的,如果要後期處理的話,那麼必須要做一次格式轉化,Android中的攝像頭數據的格式有兩種,可以進行設置,當然直接使用代碼可以獲取到攝像頭可以支持的數據格式:在Camera.Parameters類的getSupportedPreviewFormats方法即可獲得:
這裏寫圖片描述
看一下運行結果:
這裏寫圖片描述
有兩種格式,這裏打印出來的是一個int值,如何查看這個int值對應的格式呢?可以去ImageFormat類中去查找:
第一種格式:
這裏寫圖片描述
這個就是17對應16進制值,也就是ImageFormat.NV21格式的
第二種格式:
這裏寫圖片描述

這個就是842094169對應的16進制值,也就是ImageFormat.YV12格式的。
上面瞭解了攝像頭只支持這兩種數據格式,那麼我們後續肯定需要做數據處理,數據處理一般是和ARGB,以及後續的視頻編碼格式YUV420之間的轉化,不過還好的是,這個轉化格式網上已經有很多的,而且後續看到一些美顏功能都是處理這些數據格式的,同時這些操作最好放到native層做,因爲效率會高一些。

第四、Camera攝像頭方向和數據尺寸

Android中攝像頭如果我們想要得到預期的數據的話,那麼方向和尺寸非常關鍵,Camera中提供了一些方法可以進行這些參數的設置的,首先來看一下攝像頭的方向問題:
這裏寫圖片描述
我們看到這裏有兩個方法可以來設置攝像頭的方向信息,一個是Camera類本身的setDisplayOrientation方法,一個是Camera.Parameters類的setRotation方法,那麼這兩個方法有什麼區別呢?
首先第一個方法:setDisplayOrientation方法是設置攝像頭數據預覽的方向的
就是我們用一個SurfaceView作爲預覽界面,在界面看到的方向。
Android中默認的預覽方向是:橫屏,旋轉度是:0,所以如果你把當前Activity設置成橫屏,不調用這個方法的話,效果是正常的:
這裏寫圖片描述
但是,如果現在不設置橫屏,因爲Activity默認是豎屏的:
這裏寫圖片描述
看到了效果,攝像頭的預覽方向沒有發生變化的,所以這時候就需要調用setDisplayOrientation進行設置了,這個方法是按照逆時針旋轉的,所以如果想讓預覽方向變正的話,就需要逆時針旋轉90度即可:
這裏寫圖片描述

好了,到這裏就知道了Camera的預覽方向設置了,默認是:橫屏方向,旋轉度是0,如果想豎着拍攝的話,需要逆時針旋轉90度即可。
還有一個設置方向,就是設置拍照的圖片方向方法:setRotation這個方法的原理和上面的預覽差不多,默認是:橫屏方向,旋轉度是0,如果想豎着拍照的話,需要逆時針旋轉90度就可以了,但是這個方法的作用是設置Camera在拍照之後圖片方向的,關於拍照後面會介紹。
說到這裏感覺還有一個方向不對,就是原生的每一幀數據的方向,的確,這裏有一個問題就是我們在後面處理每一幀數據的時候方向是橫向0旋轉度的效果,而且沒有方法可以進行設置,這個就比較蛋疼了,不過還好,因爲我們後續再拿到每一幀數據肯定要做處理,到時候再做一次旋轉就可以了

說完方向問題,下面繼續來看尺寸問題:
設置尺寸大小也有兩個方法,這兩個方法都是在Camera.Parameters類中的:setPreviewSize和setPictureSize,到這裏就知道了這兩個方法其實和上面方向的兩個方法是一致的,第一個方法setPreviewSize是設置視頻的預覽尺寸的,第二個方法setPictureSize是設置拍照之後的圖片尺寸大小的,不過這裏有一個好處就是每一幀原生數據的尺寸可以獲取到了,其實就是和預覽的尺寸保持一致的,不像上面的方向不能設置。關於尺寸問題,其實是有指定限制的,同樣可以通過一個方法來獲取Camera所支持的尺寸大小:getSupportedPreviewSizes
這裏寫圖片描述

運行結果:
這裏寫圖片描述
這是我的設備攝像頭支持這些尺寸的,我們在設置尺寸大小的時候會在這裏進行選擇的,比如這裏選擇了720*480尺寸,我們看一下效果:
這裏寫圖片描述
看到攝像頭採集的數據沒有失真,但是有一個問題就是壓縮了感覺,這個原因也很簡單,因爲720*480的,之前說過這些尺寸是按照橫屏定義的,所以我們現在是豎屏的,可以把尺寸調換位置就可以了,我們在每一幀數據中打印log:
這裏寫圖片描述
看一下結果:
這裏寫圖片描述
這裏的每一幀數據的尺寸就是上面設置的預覽數據的。

下面來看一下設置拍照圖片的尺寸,這裏設置400*400:setPictureSize(400, 400);
我們拍照,然後把圖片保存到本地,在使用ExifInterface類來讀取圖片的元數據,這個類很重要的,可以讀出圖片的所有詳細信息,比如在哪裏拍的,什麼時候拍的,方向,尺寸等信息,也就是傳說中的圖片的exif信息。
這裏寫圖片描述
打印結果如下:
這裏寫圖片描述
結果尺寸就是我們上面設置的拍照圖片的尺寸大小。
好了到這裏,我們知道了Camera也有兩個方法可以設置尺寸大小,一個是設置預覽的尺寸同時這個尺寸可以同步到每一幀數據的尺寸,還有就是拍照的尺寸大小。

第五、Camera攝像頭的對焦拍照

再來看看Camera如何進行對焦拍照功能,這個其實就是一個簡單的一個方法即可,在Camera類中的takePicture方法,這個方法的定義:takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg)
看到了,就是三個回調接口:
第一個回調接口用於:拍照之前的工作,比如現在系統的拍照有一個提示聲音,我們當然也可以自己定義一個,就可以在這個回調接口中進行播放我們想要的提示聲音即可。一般這個接口不怎麼用。
第二個回調接口用於:原生的數據,就是當你拍照的那一幀數據的原生格式也就是NV21或者是YV12格式的。
第三個回到接口用於:直接一張圖片數據,可以直接將數據保存成一張圖片即可,無需進行數據格式轉化。
代碼很簡單:
這裏寫圖片描述
因爲這裏只關心圖片,所以實現了最後一個接口:
這裏寫圖片描述
拿到data數據,直接保存圖片即可。然後在讀取這張圖片的exif信息。
但是我們在拍照的時候這麼做還是有一個問題的,那就是拍出來的圖片會模糊,因爲有抖動效果,所以這裏需要做一個聚焦效果,這個Camera也是有一個方法:autoFocus,同樣也是一個回調接口:autoFocus(AutoFocusCallback cb):
這裏寫圖片描述
這裏只有聚焦成功了之後纔會進行拍照的。通過前面知識點可以知道,通過setPictureSize方法可以設置這個圖片的大小尺寸的。通過setRotation方法可以設置圖片的方向,而且這裏還有一個點需要注意的是:拍完照之後需要再次調用Camera的startPreview方法,不然SurfaceView畫面就停留在了當前頁面了。

第六、Camera攝像頭的數據採集以及二次加工

說完上面的所有知識之後,接下來這個知識點就是本文的重點,也是後續視頻直播推流的重點核心,就是攝像頭Camera的數據採集和二次加工。後續推流會在這裏拿到每一幀數據進行傳遞,每一幀數據進行美化加水印都是在這裏做。
Android中的攝像頭Camera提供了兩個方式回調接口來獲取每一幀數據:
第一種方式:setPreviewCallback方法,設置回調接口:PreviewCallback
在回調方法:onPreviewFrame(byte[] data, Camera camera) 中處理每一幀數據
第二種方式:setPreviewCallbackWithBuffer方法,同樣設置回調接口:PreviewCallback,不過還需要一個方法配合使用:addCallbackBuffer,這個方法接受一個byte數組。

第二種方式和第一種方式唯一的區別:
第一種方式是onPreviewFrame回調方法會在每一幀數據準備好了就調用,但是第二種方式是在需要在前一幀的onPreviewFrame方法中調用addCallbackBuffer方法,下一幀的onPreviewFrame纔會調用,同時addCallbackBuffer方法的參數的byte數據就是每一幀的原數據。所以這麼一看就好理解了,就是第一種方法的onPreviewFrame調用是不可控制的,就是每一幀數據準備好了就回調,但是第二種方法是可控的,我們通過addCallbackBuffer的調用來控制onPreviewFrame的回調機制。

注意:
因爲第二種方式在調用的時候有點注意的地方:
1》在調用Camera.startPreview()接口前,我們需要setPreviewCallbackWithBuffer,而setPreviewCallbackWithBuffer之前我們需要重新addCallbackBuffer,因爲setPreviewCallbackWithBuffer 使用時需要指定一個字節數組作爲緩衝區,用於預覽圖像數據 即addCallbackBuffer,然後你在onPerviewFrame中的data纔會有值。
2》從上面看來,我們設置addCallbackBuffer的地方有兩個,一個是在startPreview之前,一個是在onPreviewFrame中,這兩個都需要調用,如果在onPreviewFrame中不調用,那麼,就無法繼續回調到onPreviewFrame中了。

本文直說第一種方式,下面就來看看如何通過獲取攝像頭的每一幀數據,進行二次處理:
這裏寫圖片描述

代碼處理其實也很簡單,主要分爲三步:
1、第一步:數據轉化
把NV21/YV12格式轉化成ARGB格式在生成一張圖片,這裏有兩種方式,一種是直接使用系統的YuvImage類進行轉化即可,要傳遞圖片的尺寸,這個可以通過getPreviewSize方法獲取到。還有一種方式就是使用網上的轉化工具類即可,不過像這種工具類轉化工作應該放到native層去做,效率高很多。
2、圖片二次處理
上面說到了,Camera中雖然有兩個可以設置方向的方法,但是一個是設置預覽方向的方法,一個是設置拍照圖片的方向,沒有設置每一幀原始數據的方向,所以這裏我們轉化得到的一張圖片應該是橫着的,因爲Camera默認的模式是:橫屏+0度旋轉,所以這裏如果想讓圖片方向正確的話,應該做一次圖片旋轉,而且前置攝像頭和後置攝像頭旋轉的角度不一樣,不過好理解的是,前置攝像頭和後置攝像頭就相差180。旋轉之後得到正確方向的圖片之後,開始添加水印效果了,添加水印效果非常簡單:

這裏寫圖片描述

直接使用Canvas畫布即可。
3、處理之後的圖片數據加工
上面處理了數據之後得到我們想要的圖片數據,那麼下面就可以進行後續操作了,這裏是直接進行展示,後續一篇文章會介紹把這數據推流到服務端。本文先不介紹了。

這裏看到了,核心的方法就是在這裏,我們先處理數據格式,然後加上我們想要的效果,比如水印,美顏,因爲美顏不是本文的重點,爲什麼呢?因爲美顏需要用到OpenGL相關的知識,後續會詳細介紹如何使用OpenGL來處理圖片。有了這個知識點,很多工作都可以做了:
1、美顏相機
首先得到每一幀圖片之後,進行處理美白,這裏還可以做人臉識別來添加美顏貼紙,這個就需要人臉識別算法了。不過這裏需要注意的一點是,美顏相機預覽的時候肯定使用的GLSurfaceView的,因爲我們在之前的一篇基礎知識:點擊這裏查看 瞭解到了GLSurfaceView和SurfaceView以及SurfaceTexture的區別就是,SurfaceView無法將二次處理之後的數據再次回顯到界面上了,而我們看到美顏相機在使用的時候,選着一種濾鏡的時候,畫面會立即改變的,其實這個就是使用GLSurfaceView來做的,因爲GLSurfaceView首先可以結合OpenGL,其次他可以把二次處理之後的圖片在回顯到界面中的。
2、二維碼掃描
得到每一幀數據之後立馬識別二維碼,識別成功立即退出即可。

項目案例下載:

四、技術總結

1、攝像頭Camera的基本操作:初始化操作和銷燬操作
1》初始化操作:調用open方法直接打開攝像頭,然後設置預覽載體,在設置攝像頭的一些參數信息,最後設置每一幀的回調接口,開始預覽效果。
2》銷燬操作:置空每一幀的回調接口,停止預覽效果,釋放攝像頭
2、攝像頭Camera的前置和後置效果
Android中的前置攝像頭和後置攝像頭切換是通過把之前的一個攝像頭釋放,然後在重新初始化下一個攝像頭,同時用一個全局的變量來標誌當前攝像頭的狀態即可,使用open帶有參數的形式來決定打開那種攝像頭。
3、攝像頭Camera的數據格式處理
Android中的Camera的數據格式是可以設置的,但是攝像頭只支持兩種格式NV21和YV12,所以我們在後續的數據二次處理就需要做數據格式轉化,一般都是把數據轉化成ARGB格式或者是視頻編碼的YUV420格式。
4、攝像頭Camera的尺寸和方向設置
1》Android中的Camera可以獲取到當前所支持的尺寸大小,但是需要注意的是,因爲Camera默認的方向模式是:橫屏+0度旋轉,所以寬度*高度尺寸是針對於橫屏來說的,所以看到這些尺寸都會發現寬度比高度值大。如果設備是豎屏的話,我們需要做一次尺寸調換。同時可以支持兩種方式設置尺寸大小的,一個是可以設置預覽的尺寸大小,而這種大小將會同步到到每一幀原始數據的尺寸大小的,還有一個就是可以設置拍照之後的圖片大小。
2》Android中的Camera的可以支持兩種方向設置的,一種是預覽方向設置,還有一種是拍照之後的圖片方向設置,這裏我們學習到了圖片的exif信息處理。但是唯獨沒有每一幀原始數據的方向設置,所以我們後續再處理每一幀數據的時候需要手動的做一次方向旋轉,旋轉的時候還要區分前置攝像頭和後置攝像頭的旋轉角度。
5、攝像頭Camera對焦拍照
Android中Camera可以通過autoFocus方法設置對焦回調方法,然後在使用takePicture方法設置回調方法獲取拍照之後的圖片數據,可以直接保存成圖片即可。無需數據格式轉化。
6、攝像頭Camera的原生每一幀數據採集
這個知識點是最重要的,是後續推流和編碼的核心點,他能夠獲取到攝像頭的每一幀數據,我們可以在這裏做二次處理,比如把原生的NV21/YV12數據格式轉化成ARGB格式,然後添加水印效果。

五、用途

瞭解到了上面的知識點之後,我們可以做的事情就很多了,比如美顏相機我們是可以實現的,二維碼掃描也是可以的。如果想做美顏的貼紙功能就需要人臉識別算法,如果想使用更多的濾鏡效果,那麼就需要強大的OpenGL來做圖片處理。

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