轉自: http://topic.csdn.net/u/20100104/16/0fd992e8-b0a6-4c2b-85a4-d9513d3b1491.html
相信有不少人和我一樣,希望實現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打包部分,我連工程名字都沒有改。他的源代碼在這里http://www.pudn.com/detail.asp?id=510807。3****,他提供的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版本就支持。兩個版本的VLC播放器大家去着哩下載這里我測試用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幀,難道只能換台好電腦了?
4.下一步我想用jrtplib來打包RTP,因為聽說用這個庫實現RTCP很方便,是不是這個庫會根據網絡狀況自動發送RTCP信息。如果哪位高手有這方面的代碼或者是實現了RTSP的代碼,希望能拿出來交流,哪怕是部分代碼或者是實現部分功能也好。
源碼下載地址:http://download.csdn.net/source/1961862 大家下載后有什么問題可以直接和我聯系,不留郵箱了,直接留QQ號吧:1002666420.另外我這里還有一個老外寫的用LIVE555庫實現H264 STREAMING的教程及代碼,有需要的可以聯系我。
部分回復:
1. vlc1.0.5播放sdp沒問題,0.9.7我測試不行。sdp里面的ip地址不用設置也行。
2. vlc播放一段時間后,1個小時或者2個小時就會出現大量丟幀,誰知道這是vlc的問題還是rtp封包的問題?
3. 關於時間戳的設置,只要和 *.sdp中的定義一致就好了.
a = rtpmap:96 H264/90000 (這個時間戳可以改,越大client的播放速度越快)
用SDP的方法可以直接match你的naldecoder的設置,如果用VLC自己的設置,可能有些參數不一樣,你可以試
着用 98 試一下
播放速度可能是資源問題,也可能是sdp中時鍾頻率的設置問題
如果要用Jrtp做底層的發送的話, client也需要用JRTP接受才行
4. 頂起樓主,但是重問33樓的問題,為什么用NALDecoder,解碼出的碼流,雖然播放流暢,但是卻非常緩慢
呢,我試圖修改了時鍾頻率,播放速度發生了變化,但是卻無法按照正常的時間長度播放,什么樣的時鍾能
夠讓VLC正常播放視頻呢?
5. 你這個跟你的幀的時間戳設置有關,幀與幀之間的單位間隔大了就會慢。90000那個是控制基本單位的長短
的,控制完基本單位后播放時的速度就由兩幀時間戳之間的基本單元數決定。
6. 看過代碼,關於播放速度的問題,我覺得可能因為時間戳寫的有錯誤,代碼中每個Nal單元都會造成時間戳增 加3600(也就是90K時鍾下,25幀/s時一幀的時間),而每個Nal單元中不一定就是一幀圖像。 |