ffmpeg利用libav库把yuv视频流转换为TS串流
? ? ? ? 今天到月末了,才發我這個月的第一篇文章,因為這個月前三周一直在看ffmpeg的libavcodec和libavformat兩個庫源碼。實驗室要做一個“小傳大”的軟件,就是android手機或平板電腦的屏幕包括操作等全映射到電腦或電視上去。這個首先想到的就是用TS串流來做,一來是符合標準規范,音視頻同步方便;二來是接收端非常簡單,普通能播放網絡串流的播放器都可以勝任,大大降低開發難度。于是我就開始看ffmpeg的libav庫,如下是我的小體會。
? ? ? ? ffmpeg庫的框架非常漂亮,接口函數在幾個主要的頭文件中,如avformat.h,avcodec.h等,內部靜態函數雖然調用層次非常紛繁復雜,但是命名規范,層次也非常清晰,看起來只要自己不亂,那是非常爽的,這個必須贊一下先哈。如果只是編解碼的話,只要調用avcodec.h里面的接口函數就足夠了,關于編碼有兩個結構體,AVCodecContext和AVCodec。AVCodecContext結構體里面的成員是編解碼器的一些參數,codec_type,codec_id,width,height,bit_rate,time_base,pix_fmt這些,其中前兩個在avcodec_alloc_context3(codec)函數初始化的時候自動賦值,其實也就是用的其參數AVCodec *codec里面設置的值;后面的幾個參數要用戶自己賦值。AVCodec結構體是由函數avcodec_find_encode(CODEC_ID)來初始化的,這一步通過用戶傳入的參數CODEC_ID,比如AV_CODEC_ID_MJPEG,不僅把type和id賦了值(這倆就是在AVCodecContext初始化時傳入的值),而且把encode等這些函數指針也都賦了值,指向了相應的編解碼函數。當做完兩個結構體的初始化后,就可以編碼了。但是yuv文件和編完碼后的碼流存在哪里?這就用到ffmpeg的AVFrame和AVPacket兩個結構體。有時候也可以用AVPicture來存yuv裸數據,畢竟AVPicture只是AVFrame的子集,我就一直用AVFrame了。AVFrame結構體里面除了width和height以及linesize后,就是一個8個元素的指針數組:data[8],我不知道為啥要給8個,即使是BGRA格式也只用4個哈,這就不管了,現在用YUV420P只占用3個,即data[0]->Y,data[1]->U,data[2]->V。好了,初始化工作完成后,調用avcodec_encode_video2函數就妥了,然后直接從AVPacket結構體里取編成你設置好的格式的碼流就行了。
? ? ? ? 關鍵要說的不是上面的這些,因為我3個星期才看出這點玩意就太山寨了。我要把yuv編成ts流,當初不知道,直接上面的步驟只是把CODEC_ID設為AV_CODEC_ID_MPEG2TS,結果各種報錯,當時我世界觀就踏了,到處發貼問,但都石沉大海,木有人回應,不知道是大家都不做TS流編碼還是怎么著。當我再回過頭仔細看源碼并且看TS的標準18030之后,才發現是怎么一回事。TS流就是個容器,可以包mpeg2或h264等ES流,所以如果要編碼TS流就要經過兩步,第一步是編碼成mpeg2或h264,第二步封裝成TS流,后者就是libavformat要干的事兒,這就引出了下面的結構體:AVFormatContext,AVOutputFormat和AVStream。AVFormatContext結構體是標準的始祖,太全了,包羅萬象,用avformat_alloc_context()初始化,捎帶把AVOutputFormat也初始化了,這個結構體里面的write_header,write_packet,interleave_packet這些函數太重要了,但也就是這里出現了大的問題,我后面說明。AVStream結構體就是保存各種流,如果有音頻和視頻那么就是兩個流。
? ? ? ? 方是時,正當我高興的以為做出來了的時候,才發現,av_interleaved_write_frame()函數里一個參數是AVIOContext,就是這個結構體太坑了,給TA初始化的時候要添加文件名,也就是說必須是硬盤上的文件才可以,而我要把yuv做成TS流的目的就是要實時打成RTP包發出去,TA讓我先存到硬盤上,再從硬盤上讀取到內存中打包發送是怎么個意思??還怎么實時性?!我第一個想到的就是直接從AVIOContext結構體里面把那個指向編玩碼的流指針找出來,這樣就可以解決,但是又當我欣喜的時候,我才發現,這個AVIOContext里面的buffer指針指向的根本就不是封裝好的TS流!!!我再一次進去扒代碼,才發現,經過了層層的函數調用后,TA的過程我描述如下:
? ? ? ? 編好的mpeg2或h264 ES流文件存在AVPacket *pkt中,pkt作為輸入傳入av_interleaved_write_frame()函數,在里面新實例了一個AVPacket *opkt的局部結構體,當調用mpegts_write_packet_internal()函數的時候,輸入參數已經是opkt了,寫上TS的頭,寫上opkt的部分長數據,寫入文件,free掉buf指針,再寫上TS body的數據,再寫入部分opkt數據,寫入文件,free掉buf指針,。。。不斷循環操作,直到寫完為止。這樣,這個局部TS流文件指針就壓根沒有!!如果改庫的話在AVIOContext結構體里面新建一個TS流指針,把上面的文件操作全部賦給這個指針,我最后也是可以得到的,但是改庫的代價和不可預期的錯誤太大,我稚嫩的肩膀不能hold住。。。。但臨近月末,啥都做不出來太沒有工作量了,于是我換了一種簡單的思路先湊和解決了,就是yuv編碼成jpg然后直接udp傳,接收端我用QT寫了個界面,實時把接到的圖片顯示出來。雖然做出來了,但是以后音視頻同步還是個問題,我還是認為應該用TS流來做,但具體怎么做我要歇幾天以后靠靈感了~也期待大家給點靈感~~:-)
? ? ? ? 轉載請注明: 轉自http://blog.csdn.net/littlethunder/article/details/9164929
?
轉載于:https://www.cnblogs.com/jiangu66/p/3155485.html
總結
以上是生活随笔為你收集整理的ffmpeg利用libav库把yuv视频流转换为TS串流的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++ STL map的使用
- 下一篇: 【Servlet3.0新特性】第03节_