一、啰嗦幾句
好幾年不寫博客了,一是作業計算機都加密了沒法編輯提交;二是各種語言混用,什么都會就是什么都不會,delphi、c#、vb、python、c++要說我精通啥,啥也不精,所以不敢亂寫,
最近做一個關于視頻處理的專案,用到ffmpeg,實在是憋不住,在此記錄一下摸索的程序,可以毫不夸張的說,網上關于ffmpeg的使用,大部分用命令列方式,呼叫api方式的很少,而且盲目抄襲甚盛,斗膽妄言,罪過罪過,
二、感謝
我是通過學習雷神的博客逐漸掌握了ffmpeg的一些東西,好歹把專案做完了,效果很好,在此向雷神由衷的表示感謝,雷神由淺入深的介紹了ffmpeg的使用方法,有理論有實踐,可以說網上的很多文章很難與雷神媲美,而且國內這方面的文章太少了,這么多做視頻方面的,怎么就沒有這方面的優質文章,在此是個疑點,可惜的是雷神花費了大量時間開放自己的學習探索的心得,當逐步的到達核心地帶時,戛然而止,雷神去世了,天妒英才吶,在此沉痛緬懷并致以崇高的敬意!
在本文中,沒有直接可運行的代碼,一是加密,無法拷貝;二是提倡動手實踐,先把雷神的實體代碼挨個學習除錯,自會有極大的提高;
三、專案背景
核心一句話:接收高清視頻流(H264+mp3 TS流),每30分鐘存盤一個mp4檔案,相鄰兩個檔案的播放要順暢不能丟幀,為啥說是高清呢,30分鐘檔案就有5個G,
編程語言c++
四、踩過的坑
4.1行程方式
網上很多文章都是用命令列的,這種方式只能說測驗還行,真正專案應用差點意思了,因為你要管理這個行程,他是個什么狀態,你不知,但你又不能不管,關鍵做不到前后兩個視頻無縫銜接,咋整,雞肋啊,做個測驗、驗證等可以,做專案不行,用api吧,資料太少,專案組意見不一,最后舉個例子達成一致了,前面有個碉堡,我們明知道用手榴彈不行,還堅持讓大家扔手榴彈,這是瞎耽誤工夫;拿zha yao肯定行,但是有人得犧牲(扔手榴彈站在遠處扔就行,zha yao包得有人扛到跟前),要想徹底解決就得用徹底的辦法,所以很多時候我們缺少的就是沉下心的耐力和扛zha yao包的勇氣,潰癰雖痛勝于養毒,把雷神用api的例子全部從頭除錯一遍,總結出流程,都需要哪些要素,時間基、采樣率、音視頻流是啥用來做啥,搞明白就完事了,
4.2 ffmpeg rtp
ffmpeg可以直接接收RTP,也有提供轉換MP4的方法,要注意的是接收和處理放在一個執行緒中有問題,容易丟幀,因為UDP通信必須設定快取大小,但是一旦處理堵住了,資料絕對會丟失,程式在現場長時間不間斷運行,很難保證不出現丟幀的情況,經簡單測驗,直接拋棄該方式,
五、我的實作方法
1、使用UDP方式接收組播視頻流,并寫入檔案中,檔案按時間命名,
2、當檢測到夠30分鐘時,停止寫入當前檔案,開始寫入另一個檔案,
3、通知視頻轉換執行緒,處理當前寫完的檔案,
4、視頻轉換執行緒,讀取檔案,
打開輸入檔案流(avformat_open_input),
創建輸出背景關系(avformat_alloc_output_context2),我們要根據檔案轉成mp4,
查找視頻資訊(avformat_find_stream_info),查找輸入的碼流:視頻流、音頻流、字幕流,
根據輸入碼流創建輸出碼流,流的引數拷貝就行(avcodec_parameters_copy),特別要注意的是輸入輸出流的時間基(time_base),
打開輸出流,寫入檔案頭,設定一個檔案結尾的閾值,當輸入流剩余位元組數小于該值時并且找到最后一個關鍵幀,則寫入到輸出流后,將剩余輸入檔案的結尾置換到下一個檔案的開頭中,這樣前后兩個檔案無縫銜接,第一個檔案最后一個關鍵幀是第二個檔案開頭的第一幀,所以無縫銜接了,
回圈讀取輸入流(av_read_frame)根據流索引確定是音頻還是視頻流,如果是視頻流寫入檔案的第一幀必須是關鍵幀,寫入時特別注意音頻和視頻的pkt的時間(pts、dts、duration)需要根據自己的時間基重新換算(av_rescale_q_rnd),并記錄第一幀的時間戳pts,換算后的pts和dts要減去第一幀的pts,這樣每個檔案播放就是從頭開始了,寫入輸出流用av_interleaved_write_frame,
讀取檔案轉換成mp4,在現場機器(高速快取設備)上總共需要不到15秒鐘,
六、最終效果
專案部署5個多月,記憶體(103M左右,峰值180M)、cpu(3%--8%),非常穩定,無例外崩潰退出,視頻無馬賽克、前后視頻銜接很棒,
七、總結
堅持實踐就是硬道理,無論什么職位、角色都不能眼高手低,
抄別人代碼一千遍不如自己動手調一遍,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/30662.html
標籤:C++
