原文詳見:
http://www.2cto.com/kf/201607/525000.html
一、前言
最近各種視頻直播app到處都是谭企,各種霸屏救拉,當(dāng)然我們也是需要體驗(yàn)的,關(guān)于視頻直播的軟件這里就不介紹了沙绝,在不是技術(shù)的人來看,直播是一種潮流鼠锈,是一種娛樂方式闪檬,但是作為一個(gè)高技術(shù)的,我們除了看看购笆,更重要的是學(xué)習(xí)技術(shù)粗悯,其實(shí)Android中的視頻技術(shù)沒什么說的,因?yàn)榫W(wǎng)上的資料很多同欠,但是之前的視頻技術(shù)大部分都出現(xiàn)在了視頻播放样傍,就是主流的視頻播放器横缔,那個(gè)最重要的一個(gè)技術(shù)就是視頻的編解碼,這個(gè)也會(huì)在后續(xù)文章中詳細(xì)介紹視頻的處理技術(shù)铭乾。但是現(xiàn)在直播的技術(shù)是在之前的視頻技術(shù)上又有了一個(gè)要求就是視頻錄制剪廉,現(xiàn)在錄制很多是借助于牛逼的硬件攝像頭娃循。但是除了這個(gè)技術(shù)炕檩,還有其他的我們使用移動(dòng)設(shè)備也可以去解決這個(gè)問題。這個(gè)后續(xù)也會(huì)說道如何使用設(shè)備去錄制視頻捌斧。
二笛质、知識(shí)概要
從這篇文章開始我們就來介紹一下關(guān)于Android中的視頻處理,這里主要包括:Android中的攝像頭技術(shù)捞蚂,錄制視頻妇押,視頻播放器等知識(shí)點(diǎn),本篇文章是介紹大體的知識(shí)點(diǎn)姓迅,為后續(xù)的章節(jié)知識(shí)點(diǎn)做鋪墊敲霍,其實(shí)以前在學(xué)習(xí)Android中最怕就是圖片和視頻處理方面的技術(shù),因?yàn)檫@些技術(shù)有一個(gè)基本的要求就是字節(jié)操作丁存,考慮很多字節(jié)流處理肩杈,這個(gè)在Android中有一個(gè)類ByteBuffer,這個(gè)類將會(huì)貫穿我們后續(xù)所有章節(jié)的知識(shí)點(diǎn)解寝,這個(gè)類也是我們下一篇文章的中點(diǎn)扩然,在視頻流處理中這個(gè)類將不可或缺的。
下面我們先來看一張Android中視頻的處理大綱圖解:
這張圖片太大了聋伦,如果看的不夠清楚可以下載夫偶,然后放大查看。下面就來一一介紹這張圖中涉及到的知識(shí)點(diǎn):
三觉增、知識(shí)結(jié)構(gòu)
第一兵拢、視頻編碼
Android中視頻編碼有兩種方式,主要是兩個(gè)核心的類逾礁,一個(gè)是MediaCodec和MediaRecorder卵佛,這兩個(gè)類有什么區(qū)別呢?其實(shí)很好理解敞斋,他們都可以對(duì)視頻進(jìn)行編碼截汪,但是唯一不同的是MediaCodec更偏向原生,而MediaRecorder偏向的上層封裝植捎。
1衙解、MediaCodec類
MediaCodec可以處理具體的視頻流,主要有這幾個(gè)方法:
getInputBuffers:獲取需要編碼數(shù)據(jù)的輸入流隊(duì)列焰枢,返回的是一個(gè)ByteBuffer數(shù)組
queueInputBuffer:輸入流入隊(duì)列
dequeueInputBuffer:從輸入流隊(duì)列中取數(shù)據(jù)進(jìn)行編碼操作
getOutputBuffers:獲取編解碼之后的數(shù)據(jù)輸出流隊(duì)列蚓峦,返回的是一個(gè)ByteBuffer數(shù)組
dequeueOutputBuffer:從輸出隊(duì)列中取出編碼操作之后的數(shù)據(jù)
releaseOutputBuffer:處理完成舌剂,釋放ByteBuffer數(shù)據(jù)
看如下圖:
這里看到:
視頻流有一個(gè)輸入隊(duì)列,和輸出隊(duì)列暑椰,分別對(duì)應(yīng)getInputBuffers和getOutputBuffers這兩個(gè)方法獲取這個(gè)隊(duì)列霍转,然后對(duì)于輸入流這端有兩個(gè)方法一個(gè)是queueInputBuffers是將視頻流入隊(duì)列,dequeueInputBuffer是從輸入流隊(duì)列中取出數(shù)據(jù)進(jìn)行編解碼操作一汽,在輸出端這邊有一個(gè)dequeueOutputBuffer方法從輸出隊(duì)列中獲取視頻數(shù)據(jù)避消,releaseOutputBuffers方法將處理完的輸出視頻流數(shù)據(jù)ByteBuffer放回視頻流輸出隊(duì)列中,再次循環(huán)使用召夹。這樣視頻流輸入端和輸出端分別對(duì)應(yīng)一個(gè)ByteBuffer隊(duì)列岩喷,這些ByteBuffer可以重復(fù)使用,在處理完數(shù)據(jù)之后再放回去即可监憎。
所以這里看到MediaCodec類處理視頻的時(shí)候可以接觸到視頻流數(shù)據(jù)的纱意,這里比如我們?nèi)绻幸恍┨厥庑枨螅热缫曨l的疊加技術(shù)鲸阔,添加字幕等就可以在這里處理了偷霉。同時(shí)MediaCodec有一個(gè)方法:createInputSurface可以設(shè)置視頻源輸入Surface類型,同時(shí)也是可以通過configure方法設(shè)置視頻輸出Surface類型褐筛。
2类少、MediaRecorder類
MediaRecorder這個(gè)類相對(duì)于MediaCodec簡單,因?yàn)樗庋b的很好死讹,直接就是幾個(gè)接口來完成視頻錄制瞒滴,比如視頻的編碼格式,視頻的保存路勁赞警,視頻來源等妓忍,用法簡單,但是有一個(gè)問題就是不能接觸到視頻流數(shù)據(jù)了愧旦,處理不了原生的視頻數(shù)據(jù)了世剖。這個(gè)也是他和MediaCodec最大的區(qū)別,他完成不了視頻的疊加技術(shù)的笤虫。
注意:
關(guān)于MediaRecorder這個(gè)類旁瘫,其實(shí)他和Android中的一個(gè)命令是相對(duì)應(yīng)的,就是:adb screenrecord琼蚯。這個(gè)類還有一個(gè)地方需要注意的就是他有一個(gè)方法:setVideoSource酬凳,可以設(shè)置視頻來源,代碼后續(xù)文章會(huì)介紹遭庶,主要就兩個(gè)來源:一個(gè)是來自于設(shè)備的攝像頭Camera宁仔,一個(gè)是來自于Surface,關(guān)于Surface這個(gè)類后面會(huì)介紹峦睡。
注意:
現(xiàn)在視頻編碼的格式都是H264的翎苫,關(guān)于H264格式說明如下:
H.264权埠,MPEG-4,MPEG-2等這些都是壓縮算法,畢竟帶寬是有限的煎谍,為了獲得更好的圖像的傳輸和顯示效果攘蔽,就不斷的想辦法去掉一些信息,轉(zhuǎn)換一些信息等等呐粘,這就是這些壓縮算法的做的事情满俗。H.264最大的優(yōu)勢(shì)是具有很高的數(shù)據(jù)壓縮比率,在同等圖像質(zhì)量的條件下事哭,H.264的壓縮比是MPEG-2的2倍以上漫雷,是MPEG-4的1.5~2倍瓜富。舉個(gè)例子鳍咱,原始文件的大小如果為88GB,采用MPEG-2壓縮標(biāo)準(zhǔn)壓縮后變成3.5GB与柑,壓縮比為25∶1谤辜,而采用H.264壓縮標(biāo)準(zhǔn)壓縮后變?yōu)?79MB,從88GB到879MB价捧,H.264的壓縮比達(dá)到驚人的102∶1丑念!H.264為什么有那么高的壓縮比?低碼率(Low Bit Rate)起了重要的作用结蟋,和MPEG-2和MPEG-4 ASP等壓縮技術(shù)相比脯倚,H.264壓縮技術(shù)將大大節(jié)省用戶的下載時(shí)間和數(shù)據(jù)流量收費(fèi)。尤其值得一提的是嵌屎,H.264在具有高壓縮比的同時(shí)還擁有高質(zhì)量流暢的圖像推正。寫了這么多,舉個(gè)例來說下宝惰,比如移動(dòng)電視植榕,我們接收的到的圖像信號(hào)一般是H.264格式的,移動(dòng)設(shè)備接收到后尼夺,需要先解碼成原始的YUV碼流尊残,然后又轉(zhuǎn)換成RGB碼流,將一幀一幀的RGB數(shù)據(jù)放到顯存上才能顯示出圖像淤堵。雖然傳輸快了寝衫,得是增加了設(shè)備的解碼成本,不過總體來講肯定是值得的」招埃現(xiàn)在PC上的顯卡慢慢都要集成H.264的硬件解碼慰毅,據(jù)說蘋果的最新產(chǎn)品IPAD也是有了這個(gè)硬解碼。而YUV到RGB的轉(zhuǎn)換庙睡,很多ARM芯片上都有了事富。
第二技俐、視頻數(shù)據(jù)源
這里說到的視頻數(shù)據(jù)源就是視頻編碼器需要編碼的視頻來源,在移動(dòng)設(shè)備中统台,我們獲取知道兩個(gè)地方可以獲取視頻雕擂,一個(gè)是來自于攝像頭Camera,一個(gè)是來自于設(shè)備屏幕(桌面)贱勃。
這里就需要介紹兩個(gè)類了:一個(gè)是攝像頭Camera井赌,一個(gè)是Android5.0新增的屏幕錄制類MediaProjection和VirtualDisplay圾结。
1月褥、Camear類
Camera這個(gè)類在現(xiàn)在的直播以及美顏相機(jī)等app很重要的,他是移動(dòng)設(shè)備采取視頻和圖片信息的一個(gè)重要渠道谆刨,他的用法很簡單戚绕,分為前置攝像頭和后置攝像頭纹坐,可以設(shè)置方向,大小等參數(shù)舞丛,最重要的是耘子,他還需要一個(gè)預(yù)覽界面,他一般預(yù)覽有兩個(gè)方法:setPreviewDisplay和setPreviewTexture球切,第一個(gè)方法是設(shè)置SurfaceHolder類型的谷誓,第二個(gè)方法是設(shè)置SurfaceTexture類型的關(guān)于這兩個(gè)類型,后面會(huì)說到的吨凑。但是這里會(huì)有一個(gè)疑惑了就是來自于攝像頭的數(shù)據(jù)會(huì)被預(yù)覽捍歪,但是我們想處理攝像頭的數(shù)據(jù)該怎么辦呢?這里就需要借助Camera的一個(gè)回調(diào)接口:PreviewCallback鸵钝,這個(gè)接口有一個(gè)回調(diào)方法:onPreviewFrame(byte[] data...)糙臼,看到這個(gè)方法我們都知道了這個(gè)是攝像頭采集的視頻數(shù)據(jù)的每一幀數(shù)據(jù),我們可以在這里獲取每一幀數(shù)據(jù)然后進(jìn)行處理蒋伦,像現(xiàn)在的美顏相機(jī)弓摘,就是在這里獲取到一幀數(shù)據(jù),然后在做濾鏡效果痕届,然后產(chǎn)生一張圖片即可韧献。當(dāng)然這里可以錄制美白視頻也是可以的哦。那么在這里我們就可以獲取到視頻的數(shù)據(jù)源了研叫。
那么上面的MediaCodec類可以使用getInputBuffer類獲取視頻流輸入隊(duì)列锤窑,我們可以在這個(gè)回調(diào)方法中獲取到數(shù)據(jù),然后傳入到這個(gè)隊(duì)列中嚷炉,進(jìn)行編碼操作渊啰,但是需要注意的是數(shù)據(jù)格式需要做一次轉(zhuǎn)化,后面會(huì)介紹到。同時(shí)MediaRecorder類可以通過setVideoSource方法直接設(shè)置視頻源绘证。
2隧膏、MediaProjection類和VirtualDisplay類
這兩個(gè)類主要是Android5.0新增的一個(gè)api,就是專門用來錄制設(shè)備視頻的嚷那,不過在使用的過程中需要權(quán)限授權(quán)的胞枕,如果不授權(quán)還是很危險(xiǎn)的,假如有惡意的軟件在后臺(tái)偷偷的錄制設(shè)備屏幕視頻魏宽,就知道你干了啥腐泻,那是很危險(xiǎn)的。需要通過MediaProjection這個(gè)類來獲取VirtualDiaplay類队询,同時(shí)需要傳入一個(gè)重要的參數(shù)派桩,就是錄制屏幕視頻預(yù)覽的Surface類。
那么這里就可以和上面的視頻編碼器聯(lián)系到一起了蚌斩,MediaCodec有一個(gè)createInputSurface方法可以設(shè)置視頻源類型的Surface铆惑,而且MediaRecoder這個(gè)類也是可以通過setVideoSource方法設(shè)置Surface類型的視頻輸入源的,在這里如果想實(shí)現(xiàn)設(shè)備屏幕錄制視頻可以通過上面的兩個(gè)視頻編碼類進(jìn)行操作然后保存即可凳寺。
第三鸭津、視頻數(shù)據(jù)格式
我們上面看到了兩種視頻源彤侍,一個(gè)來自于攝像頭肠缨,一個(gè)來自于屏幕,但是這兩個(gè)數(shù)據(jù)源都有自己的格式盏阶,所以這里還需要介紹一下數(shù)據(jù)格式晒奕,以及他們之間的轉(zhuǎn)化。我們平常接觸的一般都是ARGB顏色空間名斟,A代表透明度脑慧,RGB是三原色,但是在處理視頻的時(shí)候特別是在錄制移動(dòng)設(shè)備的時(shí)候視頻有一個(gè)顏色空間:YUV砰盐。它也是一種顏色空間闷袒,為什么要出現(xiàn)YUV,主要有兩個(gè)原因,一個(gè)是為了讓彩色信號(hào)兼容黑白電視機(jī)岩梳,另外一個(gè)原因是為了減少傳輸?shù)膸捘抑琛UV中,Y表示亮度冀值,U和V表示色度也物,總之它是將RGB信號(hào)進(jìn)行了一種處理,根據(jù)人對(duì)亮度更敏感些列疗,增加亮度的信號(hào)滑蚯,減少顏色的信號(hào),以這樣“欺騙”人的眼睛的手段來節(jié)省空間。YUV的格式也很多告材,不過常見的就是422和420格式坤次。在一般的技術(shù)開發(fā)中,常用的還是yCbCr,這是一種420格式斥赋,也稱作I420浙踢,注意這個(gè)YV12的數(shù)據(jù)排列剛好是相反的。
Y灿渴,U洛波,V它們之間是有一個(gè)比例,這個(gè)比例不是唯一的骚露,比如Y,U,V三個(gè)分量的數(shù)量比是4:1:1.也就是說每四個(gè)像素共用一對(duì)UV蹬挤。如果是一個(gè)3040的幀,那么有1200個(gè)Y分量棘幸,分別有300個(gè)U和300個(gè)V分量焰扳。總共有12001.5這么多個(gè)值误续。
1吨悍、N21/YV12
這個(gè)格式一般是設(shè)備的攝像頭Camera采集的數(shù)據(jù),就是我們上面說到的onPreviewFrame(byte[] data...)每一幀數(shù)據(jù)蹋嵌,其實(shí)是N21或者是YV12格式的育瓜,具體哪種格式,可以設(shè)置的栽烂。所以這里比如我們想獲取一幀數(shù)據(jù)進(jìn)行處理躏仇,一定要記得格式的轉(zhuǎn)化,比如這里想保存一張圖片腺办,那么這里就需要將NV21轉(zhuǎn)化成RGB格式的焰手,或者直接使用系統(tǒng)類YUVImage,產(chǎn)生一張圖片怀喉。
2书妻、YUV420P(I420)/YUV420SP(N12)
YUV420有打包格式(Packed),同時(shí)還有平面格式(Planar)躬拢,即Y躲履、U、V是分開存儲(chǔ)的估灿,每個(gè)分量占一塊地方崇呵,其中Y為width*height,而U馅袁、V合占Y的一半域慷,該種格式每個(gè)像素占12比特。根據(jù)U、V的順序犹褒,分出2種格式抵窒,U前V后即YUV420P,也叫I420叠骑,V前U后李皇,叫YV12(YV表示Y后面跟著V,12表示12bit)宙枷。另外掉房,還有一種半平面格式(Semi-planar),即Y單獨(dú)占一塊地方慰丛,但其后U卓囚、V又緊挨著排在一起,根據(jù)U诅病、V的順序哪亿,又有2種,U前V后叫NV12贤笆,在國內(nèi)好像很多人叫它為YUV420SP格式蝇棉;V前U后叫NV21。這種格式似乎比NV16稍受歡迎芥永。這種格式一般是錄制屏幕視頻源的格式篡殷,就是上面的MediaProjection類,所以我們上面提到的一個(gè)將錄制設(shè)備屏幕視頻然后進(jìn)行編碼保存的話恤左,就需要把攝像頭的N21/YV12格式轉(zhuǎn)化成編碼器識(shí)別的YUV420P/YUV420SP格式的贴唇。
第四、視頻預(yù)覽畫面
1飞袋、SurfaceView類
這個(gè)類,我們?cè)陂_發(fā)應(yīng)用的時(shí)候可能會(huì)用到的很少链患,在開發(fā)游戲中會(huì)用到一些巧鸭,我們?cè)陂_發(fā)應(yīng)用制作特殊動(dòng)畫的時(shí)候只需要繼承View類,然后在onDraw中開始繪制就好了麻捻,這個(gè)類也可以做到繪制功能纲仍,上面看到視頻源需要一個(gè)預(yù)覽功能,需要一個(gè)Surface贸毕,其實(shí)SurfaceView就是View+Surface結(jié)合體郑叠,Surface是圖像繪制數(shù)據(jù)層,而View只是一個(gè)展現(xiàn)層明棍,中間還有一個(gè)SurfaceHolder作為鏈接著乡革,他們的關(guān)系如下:
可以通過SurfaceHolder的getSurface方法獲取一個(gè)Surface類即可。
所以這里的我們?cè)谑褂肧urfaceView作為一個(gè)視頻預(yù)覽界面的時(shí)候,其實(shí)是獲取到Surface或者是SurfaceHolder即可沸版,比如攝像頭預(yù)覽界面可以通過Camera的setPreviewDisplay方法設(shè)置SurfaceHolder類型即可嘁傀,屏幕錄制界面可以通過VirtualDisplay類參數(shù)傳遞一個(gè)輸入Surface類型。
2视粮、TextureView類
TextureView在4.0(API level 14)中引入细办。它可以將內(nèi)容流直接投影到View中,可以用于實(shí)現(xiàn)Live preview等功能蕾殴。和SurfaceView不同笑撞,它不會(huì)在WMS中單獨(dú)創(chuàng)建窗口,而是作為View hierachy中的一個(gè)普通View钓觉,因此可以和其它普通View一樣進(jìn)行移動(dòng)娃殖,旋轉(zhuǎn),縮放议谷,動(dòng)畫等變化炉爆。值得注意的是TextureView必須在硬件加速的窗口中。它顯示的內(nèi)容流數(shù)據(jù)可以來自App進(jìn)程或是遠(yuǎn)端進(jìn)程卧晓。從類圖中可以看到芬首,TextureView繼承自View,它與其它的View一樣在View hierachy中管理與繪制逼裆。TextureView重載了draw()方法郁稍,其中主要把SurfaceTexture中收到的圖像數(shù)據(jù)作為紋理更新到對(duì)應(yīng)的HardwareLayer中。
SurfaceTexture.OnFrameAvailableListener用于通知TextureView內(nèi)容流有新圖像到來胜宇。SurfaceTextureListener接口用于讓TextureView的使用者知道SurfaceTexture已準(zhǔn)備好耀怜,這樣就可以把SurfaceTexture交給相應(yīng)的內(nèi)容源。Surface為BufferQueue的Producer接口實(shí)現(xiàn)類桐愉,使生產(chǎn)者可以通過它的軟件或硬件渲染接口為SurfaceTexture內(nèi)部的BufferQueue提供graphic buffer财破。
這個(gè)類其實(shí)和SurfaceView差不多,只是他內(nèi)部不是依賴于Surface和SurfacHolder了从诲,而是SurfaceTexture左痢,關(guān)于SurfaceTexture它的好處就很多了:
SurfaceTexture是從Android3.0(API 11)加入的一個(gè)新類。這個(gè)類跟SurfaceView很像系洛,可以從camera preview或者video decode里面獲取圖像流(image stream)俊性。但是,和SurfaceView不同的是描扯,SurfaceTexture在接收?qǐng)D像流之后定页,不需要顯示出來。有做過Android camera開發(fā)的人都知道绽诚,比較頭疼的一個(gè)問題就是典徊,從camera讀取到的預(yù)覽(preview)圖像流一定要輸出到一個(gè)可見的(Visible)SurfaceView上杭煎,然后通過Camera.PreviewCallback的onPreviewFrame(byte[] data, Camera camera)函數(shù)來獲得圖像幀數(shù)據(jù)的拷貝。這就存在一個(gè)問題宫峦,比如希望隱藏?cái)z像頭的預(yù)覽圖像或者對(duì)每一幀進(jìn)行一些處理再顯示到手機(jī)顯示屏上岔帽,那么在Android3.0之前是沒有辦法做到的,或者說你需要用一些小技巧导绷,比如用其他控件把SurfaceView給擋住犀勒,注意這個(gè)顯示原始camera圖像流的SurfaceView其實(shí)是依然存在的,也就是說被擋住的SurfaceView依然在接收從camera傳過來的圖像妥曲,而且一直按照一定幀率去刷新贾费,這是消耗cpu的,而且如果一些參數(shù)設(shè)置的不恰當(dāng)檐盟,后面隱藏的SurfaceView有可能會(huì)露出來褂萧,因此這些小技巧并不是好辦法。
但是葵萎,有了SurfaceTexture之后导犹,就好辦多了,因?yàn)镾urfaceTexture不需要顯示到屏幕上羡忘,因此我們可以用SurfaceTexture接收來自camera的圖像流谎痢,然后從SurfaceTexture中取得圖像幀的拷貝進(jìn)行處理,處理完畢后再送給另一個(gè)SurfaceView用于顯示即可卷雕。
而且SurfaceTexture可以輕松的獲取視頻的時(shí)間戳數(shù)據(jù)节猿,不需要我們?nèi)斯さ娜ビ?jì)算,同時(shí)他還有一個(gè)強(qiáng)大的功能就是和Render結(jié)合了漫雕,而Render是后面要說到GLSurfaceView的核心滨嘱,就是OpenGL技術(shù)了,對(duì)于后續(xù)圖片和視頻的濾鏡處理浸间,這個(gè)發(fā)揮著巨大的作用太雨,因?yàn)樗鼘?duì)圖像流的處理并不直接顯示,而是轉(zhuǎn)為GL外部紋理发框,因此可用于圖像流數(shù)據(jù)的二次處理(如Camera濾鏡躺彬,桌面特效等)。比如Camera的預(yù)覽數(shù)據(jù)梅惯,變成紋理后可以交給GLSurfaceView直接顯示,也可以通過SurfaceTexture交給TextureView作為View heirachy中的一個(gè)硬件加速層來顯示仿野。首先铣减,SurfaceTexture從圖像流(來自Camera預(yù)覽,視頻解碼脚作,GL繪制場景等)中獲得幀數(shù)據(jù)葫哗,當(dāng)調(diào)用updateTexImage()時(shí)缔刹,根據(jù)內(nèi)容流中最近的圖像更新SurfaceTexture對(duì)應(yīng)的GL紋理對(duì)象,對(duì)于Camera數(shù)據(jù)源的話可以通過setPreviewTexture方法來設(shè)置SurfaceTexture類型劣针,錄制屏幕數(shù)據(jù)源的話沒有入口可以設(shè)置校镐。
3、GLSurfaceView類
GLSurfaceView從Android 1.5(API level 3)開始加入捺典,作為SurfaceView的補(bǔ)充鸟廓。它可以看作是SurfaceView的一種典型使用模式。在SurfaceView的基礎(chǔ)上襟己,它加入了EGL的管理引谜,并自帶了渲染線程。另外它定義了用戶需要實(shí)現(xiàn)的Render接口擎浴,提供了用Strategy pattern更改具體Render行為的靈活性员咽。作為GLSurfaceView的Client,只需要將實(shí)現(xiàn)了渲染函數(shù)的Renderer的實(shí)現(xiàn)類設(shè)置給GLSurfaceView即可贮预。
這里看到GLSurfaceView有一個(gè)特點(diǎn)就是不是系統(tǒng)幫我們繪制預(yù)覽畫面了贝室,而是需要我們自己拿到數(shù)據(jù)之后自己渲染,同時(shí)這里會(huì)有一個(gè)單獨(dú)的GL線程來進(jìn)行刷新數(shù)據(jù)仿吞。
四滑频、流程總結(jié)
上面就介紹完了所有類的大致功能以及幾種視頻源采集的數(shù)據(jù)格式,下面來總結(jié)一下:
第一茫藏、兩種編碼器MediaCodec和MediaRecorder
MediaCodec可以通過createInputSurface方法設(shè)置輸入Surface類型以及configure方法設(shè)置輸出Surface類型
MediaRecorder可以通過setVideoSource方法設(shè)置視頻源误趴,兩種:一種是攝像頭,一種是錄制屏幕
這兩種編碼器的區(qū)別在于:MediaCodec可以處理詳細(xì)的視頻流信息务傲,但是MediaRecorder封裝太好了凉当,沒辦法處理。
第二售葡、兩種視頻源Camera和MediaProjection
攝像頭數(shù)據(jù)源提供了一個(gè)回調(diào)接口中的一個(gè)回調(diào)方法:onPreviewFrame(byte[] data...)可以獲取到視頻的每一幀數(shù)據(jù)
屏幕數(shù)據(jù)源類VirtualDiaplay提供了一個(gè)輸入Surface類型的設(shè)置入口類型
第三看杭、視頻源格式和視頻編碼數(shù)據(jù)格式
攝像頭采集的視頻數(shù)據(jù)格式是N21和YV12,但是編碼器MediaCodec處理的數(shù)據(jù)格式是Y420P和Y420SP的挟伙,所以這里需要做一次數(shù)據(jù)格式的轉(zhuǎn)化楼雹,同樣如果想采集攝像頭的每一幀圖片做處理的話,還需要把N21格式轉(zhuǎn)化成RGB格式尖阔。
第四贮缅、視頻預(yù)覽View
1》這里主要有SurfaceView類型,他主要和SurfaceHolder介却,Surface相關(guān)聯(lián)谴供,攝像頭提供了setPreviewDisplay方法設(shè)置SurfaceHolder類型。攝像頭可以通過SurfaceView進(jìn)行數(shù)據(jù)的預(yù)覽齿坷,錄制屏幕VirtualDisplay可以提供一個(gè)設(shè)置Surface入口桂肌,所以錄制屏幕也可以通過SurfaceView進(jìn)行數(shù)據(jù)的預(yù)覽数焊。
2》還有就是TextureView類型,他主要和SurfaceTexture類相對(duì)應(yīng)的崎场,而攝像頭提供了一個(gè)setPreviewTexture方法來設(shè)置SurfaceTexture類型佩耳,但是錄制屏幕的VirtualDisplay沒有,所以攝像頭可以通過TextureView進(jìn)行數(shù)據(jù)預(yù)覽谭跨,但是錄制屏幕不可以干厚。
3》最后就是GLSurfaceView類型了,他是繼承SurfaceView的饺蚊,在這基礎(chǔ)上添加了OpenGL技術(shù)萍诱,用來處理視頻數(shù)據(jù)和圖片數(shù)據(jù)的。但是GLSurfaceView和前面兩個(gè)預(yù)覽View不同的是污呼,他需要拿到數(shù)據(jù)自己進(jìn)行渲染預(yù)覽裕坊,大致流程如下:
GLSurfaceView->setRender->onSurfaceCreated回調(diào)方法中構(gòu)造一個(gè)SurfaceTexture對(duì)象,然后設(shè)置到Camera預(yù)覽中->SurfaceTexture中的回調(diào)方法onFrameAvailable來得知一幀的數(shù)據(jù)準(zhǔn)備好了->requestRender通知Render來繪制數(shù)據(jù)->在Render的回調(diào)方法onDrawFrame中調(diào)用SurfaceTexture的updateTexImage方法獲取一幀數(shù)據(jù)燕酷,然后開始使用GL來進(jìn)行繪制預(yù)覽籍凝。
五、使用場景
第一種場景:從攝像頭采集視頻數(shù)據(jù)保存到本地
第一種辦法:
Camera->setPreviewCallback->onPreviewFrame->獲取沒幀數(shù)據(jù)(N21)->轉(zhuǎn)化數(shù)據(jù)格式為Y420SP->給MediaCodec->編碼生成H264格式的視頻流保存到本地
第二種辦法:
設(shè)置MediaRecorder的視頻源為Camera即可苗缩,但是這個(gè)過程中獲取不到攝像頭的沒幀數(shù)據(jù)饵蒂,做不了處理了。
第二種場景:錄制屏幕視頻數(shù)據(jù)保存到本地
MediaProjection->VirtualDisplay->設(shè)置輸入Surface(MediaRecorder通過getSurface方法獲取酱讶,這里需要設(shè)置MediaRecorder視頻源為Surface格式的)
第三種場景:從攝像頭中采集數(shù)據(jù)做每一幀數(shù)據(jù)處理獲取圖片
第一種方法:
使用YUVImage類退盯,將N21/YV12格式變成一個(gè)Bitmap數(shù)據(jù)
第二種方法:
獲取每幀數(shù)據(jù),將N21/YV12數(shù)據(jù)格式轉(zhuǎn)化成ARGB8888格式數(shù)據(jù)泻肯,然后產(chǎn)生圖片
第三種方法:
調(diào)用Camera的回調(diào)接口PictureCallback中的回調(diào)方法 onPictureTaken(byte[] data, final Camera camera)直接獲取的data數(shù)據(jù)就是圖片格式數(shù)據(jù)
第四種場景:錄制屏幕時(shí)獲取一張圖片(屏幕截圖功能)
使用ImageReader->getSurface->設(shè)置到VirtualDisplay類中作為輸入的Surface->ImageReader獲取Image圖片即可
第五個(gè)場景:對(duì)采集的視頻和圖片做濾鏡效果
我們通過Camera的回調(diào)方法onPreviewFrame(byte[] data...)來獲取視頻流中每一幀數(shù)據(jù)渊迁,然后借助強(qiáng)大的OpenGL技術(shù)來進(jìn)行數(shù)據(jù)處理,做到類似于美顏相機(jī)功能
第六個(gè)場景:掃描二維碼技術(shù)解析
可以借助Camera灶挟,獲取每一幀數(shù)據(jù)琉朽,然后進(jìn)行二維碼的識(shí)別。
六稚铣、總結(jié)
到這里我們就介紹完了視頻直播中大致知識(shí)結(jié)構(gòu)箱叁,兩個(gè)數(shù)據(jù)源,兩種編碼器惕医,數(shù)據(jù)格式耕漱,三種預(yù)覽View,但是我們?cè)陬A(yù)覽View中看到涉及到了OpenGL技術(shù)了抬伺,沒錯(cuò)孤个,這個(gè)也是我們后續(xù)需要介紹的內(nèi)容,如何使用OpenGL做視頻的濾鏡效果沛简,讓每個(gè)直播都那么白齐鲤,后續(xù)會(huì)詳細(xì)的結(jié)合案例來介紹每個(gè)知識(shí)點(diǎn)。