javaCV入門指南:序章

  • 2019 年 11 月 1 日
  • 筆記

版權聲明:本文為部落客原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。

本文鏈接:https://blog.csdn.net/eguid_1/article/details/82875343

前言

從2016年6月開始寫《javacv開發詳解》系列,到而今的《javacv入門指南》,雖然僅隔了兩年多時間,卻也改變了很多東西。

比如我們的流媒體技術群從剛開始的兩三個人發展到現在的三個500人群。又比如部落客剛開始也想放棄,期間自行腦洞內心掙扎的場面也就不詳說了,結果是現在還在堅持更新部落格。當然這期間離不開群里小夥伴們一直以來的陪伴和支援,感謝大家一起默默為java流媒體技術踩坑,踩的多了也就真的成了路(也可能踩成深坑 )。另外感謝雷霄驊博士的ffmpeg部落格,給予部落客很大幫助,2016年剛開始接觸ffmpeg就忽聞博士去世,甚為感慨,大家且行且珍惜吧。

以前從來不覺得java可以做流媒體、音影片編解碼這些,直到現在,頂多說java做流媒體是非主流。業界廣泛應用的librtmp、live555、ffmpeg也都是c/c++的庫,剛開始也確實嘗試過使用jni方式調ffmpeg,發現做起來吃力不討好,後來在github發現了新大陸:javaCV。

有,總比沒有強。雖然連個API文檔都沒有,通過github項目描述的那可憐的幾個字勉勉強強知道它對ffmpeg、opencv等等等十幾個庫做了封裝,用javacpp方式為fmpeg、opencv等庫編譯了各個系統環境的包方便跨平台調用。

一些題外話 踩坑到今天,可能還會有許多人躊躇疑惑javacv除了可以在音影片和影像處理這塊稍微可以施展手腳外,還可以做什麼?除了這些,在應對各種紛繁複雜的流媒體協議(rtp/rtsp/rtmp/flv/hls等等)也不在話下,當然一些小眾和國產協議(比如sip/gb28181/jtt178等)可能需要依賴netty/mina等網路庫來實現,編解碼上結合javaCV,性能上也已經沒有什麼顧慮。另外在深度學習領域,deeplearning4j藉助javaCV的東風令java在深度學習領域也同樣引領風騷。

本系列將結合《javacv開發詳解》系列作為實戰教程,結合實例,力求簡單易懂,快速上手。

一、老生常談

javaCV能做什麼,既然是"CV"大法,那自然是電腦視覺領域的庫,諸如音影片、流媒體、影像處理、深度學習、機器學習、人工智慧等等等(現在流行後面這三個,寫上去應該能唬住不少人,deeplearning曉得不,裡面一堆的javaCV庫沒發現嗎)。

二、入門基礎

以上全是些空話,我們無非就是要用javaCV採集影片和音頻,給這些音影片編解碼,然後是用什麼封裝格式封裝這些音影片數據,以及用什麼協議傳輸,可能還要對影片里的影像進一步進行處理(這個屬於影像處理範疇),流程大致如此(音頻方面了解不多,大家見諒):

拉流(採集)—>影像像素數據/音頻數據<—->編/解碼 <—->音/影片幀<—->解封裝/封裝—->推流

舉例:編解碼過程(以hevc編碼的rtsp轉rtmp/flv為例,無音頻數據): rtsp流—拉流解復用—>h265(hevc)—解碼—>yuv像素數據—編碼—>h264—封裝推流-–>rtmp/flv

1、影像像素格式與圖片封裝格式

影像像素格式(簡稱像素格式),一般指的是沒有經過編碼的按照原始像素排列的數據。

舉個栗子,一個完整影像的像素排列一般是這樣的(以4*4像素的rgb像素格式為例):

rgbrgbrgbrgb rgbrgbrgbrgb rgbrgbrgbrgb rgbrgbrgbrgb

當然我們存儲的時候一般使用一維數組來存這些數據,所以排列順序就變成這樣:rgbrgbrgbrgb…….以此類推。

圖片封裝格式指的我們日常見到的png,jpg,bmp,gif等等圖片格式,其中bmp是無損格式,裡面的數據格式就是圖片頭資訊加上rgb排列的像素數據,png/jpg這些都是有損壓縮格式,但是壓縮比還是很高的,為什麼要壓縮下面會講到。

2、影像?影片幀?傻傻分不清楚

影像像素數據指的是yuv、rgb,rbga,bgr,gbra等影像像素格式,經過編碼後才是影片幀。比如我們常見的h264編碼,編碼其實就是對影像像素數據的壓縮,(以rgb為例,假如當前影像像素尺寸為1920*1080,,每種顏色用一個位元組表示,也就是說每個像素點有紅綠藍三色共3位元組,影像有1920*1080個像素點,也就是說這張影像大小為1920*1080*3位元組,顯然數據太大了),可以這樣理解,h264編碼本質上就是一種影像數據壓縮演算法。

補充:影片幀中常常提到的I幀,B幀和P幀指的是什麼?i幀也叫關鍵幀,實際上就是一張完整的靜態影像,而B幀和P幀只是用來記錄畫面的運動矢量等非影像數據,B/P幀都需要依賴i幀才能夠正確解碼出完整影像(有損的影像畫面)。在實際應用中各種影片源中很少使用B幀,原因是雖然使用大量B幀可以提高壓縮率,但也會消耗更多的硬體性能,所以大多數情況下的影片源都以i幀(關鍵幀)和大量P幀為主。 另外在直播應用中i幀間隔會很低,這樣能夠更快的顯示首幀畫面(B/P幀需要i幀才能夠解碼),但是這樣也增加了傳輸的數據量,因為一個i幀通常會很大。

3、編碼?封裝?傻傻分不清楚

編碼上面已經講了,是一種壓縮演算法;那麼封裝格式又是什麼呢,封裝格式就是我們日常見到的影片文件了,比如mp4,avi,mkv,flv等等等,按照每種封裝格式的規範把影片幀和音頻按照一定順序存起來就成我們日常看到的影片文件了,這些封裝格式一般都會包含一些頭/尾標識和一些影片描述資訊,這樣播放器讀取影片文件的時候就知道該怎麼播放這些影片文件了(可以把封裝格式理解成收納箱,上面貼著小紙條說明裡面放了哪些東西)。

壓縮圖片格式也可以參考影片編碼格式,原理都一樣,都是對影像數據做有損/無損壓縮。

什麼是轉封裝?為什麼轉封裝比轉碼消耗更少?為什麼轉封裝無法改動影片尺寸? 先舉個栗子:假設影片格式(mp4,flv,avi等)是盒子,裡面的影片編碼數據(h264,hevc)是蘋果,我們把這個蘋果從盒子里取出來放到另一個盒子里,盒子是變了,蘋果是沒有變動的,因此影片相關的尺寸數據是沒有改動的,這個就是轉封裝的概念。 有了上面這個例子,我們可以把「轉碼」理解為:把這個盒子里的蘋果(hevc)拿出來削皮切塊後再加工成櫻桃(h264)後再裝到另一個盒子里,多了一步對蘋果(hevc)轉換為櫻桃(h264)的操作,自然比直接把蘋果拿到另一個盒子(轉封裝)要消耗更多機器性能。

4、音/影片源

音/影片源可以是影片文件、音頻文件,流媒體源,設備等等。

比如我們要看電腦或手機攝影機影片,就得採集設備的影像數據(從源設備採集到的是像素數據,一般是bgr或者rgb像素數據)如果是某些廠商的商用攝像機,可能會支援rtsp/rtmp協議,要採集聲音呢,就得採集錄音/話筒設備裡面的數據(一般是pcm取樣數據)。

5、流媒體協議

rtsp協議棧,rtmp協議棧,hls,http-flv(理論上講這個flv不能算是流媒體協議,它只是個無限大的flv文件)等等。

例如rtmp,對編碼後的音影片幀,要對其進行封裝成flv進行傳輸。

補充:說到底這些協議原理上依然是建立在tcp/udp基礎上的應用層傳輸協議。

6、流媒體服務

支援音影片存儲分發的服務都可以叫流媒體服務。

比如常見的srs(開源的rtmp流媒體服務,當然它支援rtmp/hls/http-flv的分發)和nginx(通過安裝模組可以支援rtmp,hls,http-flv分發),除此之外的收費的和一些不太友好的開源流媒體服務就不一一介紹了。

下一章:javaCV入門指南:調用FFmpeg原生API和JavaCV是如何封裝了FFmpeg的音影片操作