視頻網絡傳輸一定要用rtp嗎?
RTP/RTCP有一套很好的反饋機制,通過其可以估算網絡狀況,然后在編碼端進行調整,如網絡繁忙時適當的降低畫面質量,減小碼率等,主要是想通過RTP/RTCP協議實現QoS!同時也可以用http或udp啊,都可以的,自己再加一些控制信息。利用rtp/rtcp的好處是這個協議本身幫你加入了 一些控制信息。
相信有不少人和我一樣,希望實現H264格式視頻的流媒體播放。但是對于一個新手來說,往往不知道從何入手。利用百度,GOOGLE等搜索資料真是沙里淘金。在琢磨了N周之后,才弄出來了點成果,其中費了很多無用的功夫,光看英文協議就費了一周,后來才知道有中文版,并且我所達到的目的很簡單,只要讓VLC實時播放就行,不需要了解整個協議。我也很希望能直接搜出來一套代碼,都一直沒找到,還是得自己動手。現在我把代碼貼出來,希望對做類似工程的朋友有所幫助。
????????? 一、本示例代碼在我的電腦上實現了對標準H264碼流的RTP打包發送到本機的1234端口,用VLC播放器從1234端口能接收到該碼流并實時播放。代碼附有詳細的注釋,應該很容易理解(前提是大家稍微對RFC3550 RFC3984協議有了解)。
????????? 二、本示例代碼是按照RFC3984協議僅完成了RTP打包,并沒有完成發送RTCP。原因就引用這位達人的話:“1.RTCP里頭有很多關于RTCP發送簡隔的時間計算,RTP信息的統計,這種操作不是難,而是煩,我不想去寫。2.RTCP和RTP一開始出來的時候并不是因為視頻的點播等應用的,而是視頻會議。RTCP有管理與會者的層面含義,這一功能在很多場合并不會用到。3.我想簡單,沒有寫多個流間的同步,如一個影片的視頻和音頻流。這些其實是RTCP來完成的。我懶得去寫,因為這些功作RTP的各個庫類(例如JRTPLIB庫)都做得很好。我覺得用庫的最大優點就在這吧”。
????????? 三、和代碼相關的原理性的東西,大家應該去看看RFC3550,RFC3984.這兩份協議都有熱心網友翻譯好的中文版。我把他們放在壓縮包里,大家就不用再累個半死去搜索注冊下載了。如果為了更省事,我覺得看看這位網友總結的RFC3984的內容就夠了。網址是http://www.cppblog.com/czanyou/archive/2009/12/25/67940.html。如果打不開網頁,就到壓縮包里資料文件夾下找吧。我已經把網頁保存下來了。
????????? 四、代碼并非是我完全原創的,而是我在搜索到得網友的代碼的基礎上修改的。這里要特別感謝以下幾位網友:
1.貓頭上的鷹(他的博客地址http://blog.csdn.net/Tinnal/archive/2008/09/03/2871734.aspx)在他的博客里我第一次找到了有價值的東西,并且他無償提供的MPEG的RTP打包源碼只要拷貝下來建個工程就能實現MPEG的流媒體,對我啟發很大。
2.liming,他提供的代碼已經實現了H264的碼流分析,將其中的每個NALU單元分離開來,并分析出了NALU的類型,長度等信息。為我實現RTP打包提供了很大的方便,事實上,這份示例代碼就是在他的代碼上添加了RTP打包部分,我連工程名字都沒有改。他的源代碼在這里
3.luny,他提供的SDP文件在關鍵時候幫了我大忙,我發送的RTP數據包通過Wireshark抓包工具分析一直沒錯,可VLC播放器就是沒任何反應。直到下載了他的SDP文件文件后終于出畫面了。某位網友說VLC對H264只能通過TS封包或SDP文件打開RTP碼流,在此我這么懷疑。
4.jessiepan和他的帖子,http://topic.csdn.net/u/20090725/11/5FBC75B0-1091-4DD4-9154-3E3D59F9B6D1.html,這里提供了很多有用的信息。
??????????? 使用方法:直接在VC6上打開工程,編譯。(需要注意的是大家要把IP地址改為自己的。在h264.h的#define DEST_IP ?? ??? "192.168.0.30"和#define DEST_PORT 1234這兩行修改就行了。同時w.sdp文件里也要改成一致的IP和端口號,不然VLC是接受不到數據的。在c=IN IP4 192.168.0.30 和m=video 1234 RTP/AVP 96這兩行。中間的1234是我設置的端口號。)在執行程序之前,先用VLC打開w.sdp文件,然后執行程序,就可以看到畫面了:)。同樣需要注意的是VLC1.0以后的版本不支持直接打開h.264視頻文件,但是0.97版本就支持。這里我測試用1.03和0.97兩份版本]的VLC都可以接受并播放h.264RTP碼流。
目前還有幾個問題我沒有弄明白,希望有高手在看完這個帖子后能幫我解答:
1.關于時間戳的設置。RFC3984里沒有提到時間戳具體如何計算,我也是按照各方面的小道消息這樣設置。unsigned int timestamp_increse=0;timestamp_increse=(unsigned int)(90000.0 / framerate); 即初始值設為0,時間戳增量設為90000.0 / framerate,framerate我設為25,即每秒25幀。每發送一個NALU單元,時間戳增加。若是該NALU大于1400字節,需要分片時,則多個分片擁有相同的時間戳。這樣設置是否正確。請牛人給個權威解答。
2.按照我的理解,SDP文件僅實現了告訴VLC在哪個IP和端口接受264RTP包,同樣的信息我也通過在VLC的媒體-》打開網絡串流,協議選RTP,然后填寫IP和端口號中設置好了,為什么用打開SDP文件的方法能接收,但用后者VLC卻沒有一點反應。
3.當我將幀率設為25時(即代碼里的float framerate=25;)vlc能接受碼流,但會比較卡,常緩沖,提示錯誤為main error: ES_OUT_SET_(GROUP_)PCR?? is called too late, increasing pts_delay to 339 ms。我懷疑是我的電腦發送UDP包速度不夠每秒播放25幀的所需要的UDP包數量,因此在SDP文件我添加了a=framerate:15來限制播放器每秒播放15幀,同時在代碼里的相應行float framerate=15;也將幀率改為15這樣雖然解決了卡的問題,但是視頻播放很慢。請問要是我想達到每秒播放25幀,難道只能換臺好電腦了?
5.下一步我想用jrtplib來打包RTP,因為聽說用這個庫實現RTCP很方便,是不是這個庫會根據網絡狀況自動發送RTCP信息。如果哪位高手有這方面的代碼或者是實現了RTSP的代碼,希望能拿出來交流,哪怕是部分代碼或者是實現部分功能也好。
1、第一個問題,如果你看過 RFC3984 就應該知道答案;
2、第三個問題,我認為不是發包速度的問題,發包其實是非常快的;
3、第四個問題,可以參考 Darwin 的源代碼(下載地址:http://dss.macosforge.org/),不過這套代碼非常龐大。VLC 的服務端源代碼也應該會發 RTCP 吧?
A:RFC里說時間戳是RTP數據包里第一個字節的采樣時間,就是說我在給一個NALU數據打上報頭的時候調用系統函數time()來獲取當前的系統時間(從1970年1月一日到現在所經過的秒數),這個秒數就是應該打在RTP包頭上的時間戳嗎? 還有RFC3984里提到必須使用90K的時鐘,這個90K的時鐘是干什么用的了?麻煩解答一下
我認為其實時間戳從 0 開始也可以。90K 是一個參考時鐘,例如你的系統是 30fps,那么幀與幀之間的時間戳增量就該是 90000 / 30 = 3000。
A:謝謝版主,那我在第一個問題中敘述的時間戳設置方法看來是蒙對了啊。
不過關于第三個問題,當我把幀率設為25fps時,VLC經常緩沖,然后就會有馬賽克了。版主知道這可能是什么原因嗎??? 經過我測試,只要幀率設為18fps以下,就沒有這個問題。
這個得做實驗具體分析了。有誤碼,說明碼流出錯了。先截取一下 VLC 收到的碼流,看是否是正確的。
A:我用LZ的程序進行傳輸并播放,LZ程序中自帶的兩個.264文件都能正常播放,但是我用JM編碼生成的.264文件就不能播放,這是為什么呢?請高手解答~~~
因為 JM 編碼生成的 RTP 格式的 264 碼流不是標準 RTP 格式。
A:我想請教您一個問題?? 如果不打成RTP包 直接打成TCP包可以么 我這個問題問的很無知 但是還是請您回答我下
如果打成TCP包,那就只有你自己的播放器能播了,因為通用的播放器,或者說是標準的播放器,都是播放RTP打包的數據。
A:如何判斷nalu是否屬于同一幀圖像呢,難道根據unit_type嗎?貌似這樣也不對啊,nalu里有沒有什么信息可以用來判斷的嗎,等待牛人指導。
編碼器自己本來就知道哪些 NALU 屬于一幀。