Android 用MediaRecorder實時視訊採集
阿新 • • 發佈:2019-02-14
都是摸著石頭過河,花了整整一個星期,終於把技術難點給突破了,貌似網上對這個討論的較少。
主要需要實現的功能是在android手機上實時採集視訊,並在遠端比如PC機上實時顯示出來,也就是以android手機作為監控攝像頭。
一開始查到的是smartcam的一個開源專案,看了下原始碼,發現其實現原理是利用android 的camera影象的預採集,通過實現PreviewCallback類的回撥函式onPreviewFrame,獲得camera採集的原始影象資料之後,壓成jpeg格式傳到pc端。pc端對接收到的jpeg影象序列進行實時解壓和顯示,就達到了預想的效果。
雖然這種方式稍微顯得比較笨拙,這個方式還可以接受。但是不可接受的是jpeg只是幀內壓縮,320x280的圖片序列,FPS大概是10上下,網路流量就到達了100kb/s以上。這個幾乎是無法實際應用的。
於是必須直接傳視訊流,MPEG4或者H.264格式。貌似我的開發機上(HTC G8)只支援到MPEG4,所以還是選取MPEG4。但是如何實時採集視訊流是一個大問題,畢竟在video方面,android並沒有提供一個類似於OnPreviewFrame的回撥函式。
想到用opencore或者更為新一點的stagefright,大概看看了其sdk的框架後,馬上洩氣了,這個太龐大了。在http://blog.csdn.net/zblue78/archive/2010/12/18/6083374.aspx的帖子中提到一個很好的解決方案,就是利用MediaRecorder:MediaRecorder的輸出路徑(其實叫file descriptor)除了是本地檔案路徑之外,還可以繫結socket埠。也就是說,通過一個socket埠,就可以實時獲得MediaRecorder的視訊流資料。
(其實上面部落格的內容可以在開源專案sipdroid 的 videocamera檔案中找到,但是非常感謝部落格主人zhangzhenj對網友提問的回答,贊一個。)
通過socket接收的視訊流與直接寫在本地檔案的視訊流資料有點不一樣,因為是通過socket傳輸,就無法對視訊檔案的回寫,通常MediaRecorder結束錄影的時候都會對視訊檔案進行回寫處理,這樣才可以被播放器播放。所以通過socket接受到的資料,儲存下來是無法播放的。16進位制方式查看了一下其輸出檔案,發現其前32byte都是00,緊接著就是mdat。問題就出現在這了:缺少了一個ftyp box 的描述(28 bytes)以及mdat的長度描述(4 bytes).網上已經有人順利解決這樣的問題,在資料中查詢moov的起始位置,發現前面會有ftyp的描述,長度剛剛好28bytes。你可以copy這28bytes到檔案開始的28byte中。這ftyp的描述是從moov的起始位置 的前32byte開始一直到前4byte(後面4byte是moov的長度描述)。然後mdat的長度就是 moov的起始位置 減去 0x20,道理就不解釋了。然後把這個值寫到mdat的前面4byte。剛剛好填滿32byte,之後就能順利播放了。
儲存好的檔案能播放之後,最後一個問題,如何在實時顯示這個視訊流呢?檢視一下mpeg4的檔案格式,很快就會知道答案,答案就在mdat中。mdat之後緊跟的就是視訊媒體資料,每一幀以 00 00 01 b6 為開始標誌,貌似沒有結束標誌,分幀的話估計要用這個。開始標誌後緊接著的兩bit就是I、P、B幀的標誌了,分別對應值為00,01,10,記住是兩bit不是兩byte
好了,把mdat的一幀資料取出來,可以用ffmpeg解碼,然後顯示,這樣的路子是可行的,不過細節還是有點麻煩,關鍵是ffmpeg在解碼mpeg4的時候一定要先指定width和height,否則解碼失敗。
大概思路就是這樣了,完整的程式碼還沒出來。以後再說。
主要需要實現的功能是在android手機上實時採集視訊,並在遠端比如PC機上實時顯示出來,也就是以android手機作為監控攝像頭。
一開始查到的是smartcam的一個開源專案,看了下原始碼,發現其實現原理是利用android 的camera影象的預採集,通過實現PreviewCallback類的回撥函式onPreviewFrame,獲得camera採集的原始影象資料之後,壓成jpeg格式傳到pc端。pc端對接收到的jpeg影象序列進行實時解壓和顯示,就達到了預想的效果。
雖然這種方式稍微顯得比較笨拙,這個方式還可以接受。但是不可接受的是jpeg只是幀內壓縮,320x280的圖片序列,FPS大概是10上下,網路流量就到達了100kb/s以上。這個幾乎是無法實際應用的。
於是必須直接傳視訊流,MPEG4或者H.264格式。貌似我的開發機上(HTC G8)只支援到MPEG4,所以還是選取MPEG4。但是如何實時採集視訊流是一個大問題,畢竟在video方面,android並沒有提供一個類似於OnPreviewFrame的回撥函式。
想到用opencore或者更為新一點的stagefright,大概看看了其sdk的框架後,馬上洩氣了,這個太龐大了。在http://blog.csdn.net/zblue78/archive/2010/12/18/6083374.aspx的帖子中提到一個很好的解決方案,就是利用MediaRecorder:MediaRecorder的輸出路徑(其實叫file descriptor)除了是本地檔案路徑之外,還可以繫結socket埠。也就是說,通過一個socket埠,就可以實時獲得MediaRecorder的視訊流資料。
(其實上面部落格的內容可以在開源專案sipdroid 的 videocamera檔案中找到,但是非常感謝部落格主人zhangzhenj對網友提問的回答,贊一個。)
通過socket接收的視訊流與直接寫在本地檔案的視訊流資料有點不一樣,因為是通過socket傳輸,就無法對視訊檔案的回寫,通常MediaRecorder結束錄影的時候都會對視訊檔案進行回寫處理,這樣才可以被播放器播放。所以通過socket接受到的資料,儲存下來是無法播放的。16進位制方式查看了一下其輸出檔案,發現其前32byte都是00,緊接著就是mdat。問題就出現在這了:缺少了一個ftyp box 的描述(28 bytes)以及mdat的長度描述(4 bytes).網上已經有人順利解決這樣的問題,在資料中查詢moov的起始位置,發現前面會有ftyp的描述,長度剛剛好28bytes。你可以copy這28bytes到檔案開始的28byte中。這ftyp的描述是從moov的起始位置 的前32byte開始一直到前4byte(後面4byte是moov的長度描述)。然後mdat的長度就是 moov的起始位置 減去 0x20,道理就不解釋了。然後把這個值寫到mdat的前面4byte。剛剛好填滿32byte,之後就能順利播放了。
儲存好的檔案能播放之後,最後一個問題,如何在實時顯示這個視訊流呢?檢視一下mpeg4的檔案格式,很快就會知道答案,答案就在mdat中。mdat之後緊跟的就是視訊媒體資料,每一幀以 00 00 01 b6 為開始標誌,貌似沒有結束標誌,分幀的話估計要用這個。開始標誌後緊接著的兩bit就是I、P、B幀的標誌了,分別對應值為00,01,10,記住是兩bit不是兩byte
好了,把mdat的一幀資料取出來,可以用ffmpeg解碼,然後顯示,這樣的路子是可行的,不過細節還是有點麻煩,關鍵是ffmpeg在解碼mpeg4的時候一定要先指定width和height,否則解碼失敗。
大概思路就是這樣了,完整的程式碼還沒出來。以後再說。