文件夾
1 初衷
2 ts demux的功能介紹
1 初衷
????之前打算給dtplayer加入一些亮點功能,最初的想法是:bt下載播放 + hls支持
????bt下載因為以來libtorrent庫,盡管搞懂了怎樣加入,但須要改動libtorrent庫來集成,
????若將libtorrent集成到代碼中,會將代碼變得龐大,框架清晰度會變差,隨機臨時取消了bt功能的開發/
? ?
????后面開始加入hls支持,hls支持打算加入例如以下模塊: stream - hls ? demuxer - ts ?decoder - h264 這樣加上之前的faad
????對于特定網絡流就能夠不依賴ffmpeg來播放了。
????ts demuxer便是第一步功能。
????憑借之前對ts的理解,感覺應該比較快完畢的,當時從github中找到了一個開源的ts解析庫: https://github.com/nevali/tsdemux
????但不幸的是這個庫僅僅提供了 section的解析功能,并沒有提供讀取es包的功能。遂打算自行將這部分功能補全。
代碼:? https://github.com/peterfuture/dtplayer/blob/dev-ts/dtdemux/demuxer/demuxer_ts.c
2 ts demux的功能
????ts demux符合dtplayer demuxer的標準接口,
????先看下定義:
demuxer_wrapper_t demuxer_ts = {
???? . name = "ts demuxer" ,
???? . id = DEMUXER_TS ,
???? . probe = ts_probe ,
???? . open = ts_open ,
???? . read_frame = ts_read_frame ,
???? . setup_info = ts_setup_info ,
???? . seek_frame = ts_seek_frame ,
???? . close = ts_close
};
以下依次介紹各個功能。
2.1 probe
static int ts_probe ( demuxer_wrapper_t * wrapper , dt_buffer_t * probe_buf )
{
???? const uint8_t * buf = probe_buf -> data ;
???? const uint8_t * end = buf + probe_buf -> level - 7 ;
????
???? if ( probe_buf -> level < 10 )
???????? return 0 ;
???? int retry_times = 100 ;
???? for (; buf < end ; buf ++ )
???? {
???????? uint32_t header = DT_RB8 ( buf );
???????? if (( header & 0xFF ) != 0x47 )
???????? {
???????????? if ( retry_times -- == 0 )
???????????????? return 0 ;
???????????? continue ;
???????? }
???????? //found 0x47
???????? if ( buf + 188 > end )
???????????? return 0 ;
???????? header = DT_RB8 ( buf + 188 );
???????? if (( header & 0xFF ) == 0x47 )
???????? {
???????????? dt_info ( TAG , "ts detect \n " );
???????????? return 1 ;
???????? }
???????? else
???????????? return 0 ;
???? }
???? return 0 ;
}
這里實現了一個比較簡單的probe功能,先找到同步字0x47,若后面188字節后面也是0x47,則覺得是ts流,臨時probe有些流還不能解析
主要問題有: a 有些流的包大小并非188, 這樣的流會出錯 b 單純的僅僅推斷一次會有偶然性的問題
但這些修正起來比較簡單,因為功能還未實現,這里僅僅介紹思想,還有面會完好
2 ts_open
這里是解析ts頭信息,主要功能包含:
pat解析
pmt解析
stream 信息解析: duration - bitrate
es信息解析:audio: channel-samplerate-bps ?video: width-height-fmt
這里代碼比較多就僅僅說下思想,感興趣的自己去讀代碼就能夠了
首先pat pmt的解析是通過之前說的開源庫完畢的,通過解析pat pmt能夠得到文件里有幾個流,各自是什么格式
得到后一般若有video stream,則以video為參考來計算流信息(正確的做法是找到流的pcr_pid,也就是參考流來計算,但普通情況下參考流都是video)
計算duration: 首先找到第一幀的pts , 然后找到最后一幀的pts, 通過pts差距得到duration
計算bitrate: 有了stream size和 duration, 直接計算得到bitrate
當中計算pts的時候,這里介紹下,詳細代碼在ts/stream.c中,是后面我自己加的
if ( packet -> payloadlen > 0 && packet -> unitstart )
???? {
???????? uint8_t * pcrbuf = packet -> payload ;
???????? int len = pcrbuf [ 0 ];
????????if ( len <= 0 || len > 183 ) //broken from the stream layer or invalid
???????????? goto QUIT ;
???????? pcrbuf ++ ;
int flags = pcrbuf [ 0 ];
int has_pcr ;
has_pcr = flags & 0x10 ;
???????? pcrbuf ++ ;
???????? if ( ! has_pcr )
???????????? goto QUIT ;
???????? int64_t pcr = - 1 ;
???????? int64_t pcr_ext = - 1 ;
???????? unsigned int v = 0 ;
? ? //v = (uint32_t)pcrbuf[3]<<24 | pcrbuf[2]<<16 | pcrbuf[1]<<8 |pcrbuf[0];
v = ( uint32_t ) pcrbuf [ 0 ] << 24 | pcrbuf [ 1 ] << 16 | pcrbuf [ 2 ] << 8 | pcrbuf [ 3 ];
???????? pcr = (( int64_t ) v << 1 ) | ( pcrbuf [ 4 ] >> 7 );
pcr_ext = ( pcrbuf [ 4 ] & 0x01 ) << 8 ;
pcr_ext |= pcrbuf [ 5 ];
????????pcr = pcr * 300 + pcr_ext
???????? packet -> pts = pcr / 300 ;
???????? //printf("get pts:%lld \n",pcr);
???? }
這里是去掉了ts包開頭的四個字節,首先看是否是一幀的開頭,若是的話推斷是否有pcr flag
若有則直接解析,解析方法比較簡單,依照標準就可以
這里介紹下ts的編碼參考時鐘為:27M HZ,而pts的單位與時間s的換算為:90000
因此獲取參考時間(也就是上面的pcr后),換算為pts的計算方式為: pts = pcr * 9000 / 27000000 = pcr / 300
這里得到的就直接是pts了。
另一個問題是:這里直接解析es流貌似不能獲取 視頻: width height ?音頻:channel samplerate等信息,
對于ffmpeg來講沒有問題,因此在av_find_stream_info中能夠通過decode one frame來獲取, 但dtplayer框架上是不方便直接啟動解碼器的
對于mplayer 的ts 也是沒有decode的, 知道的同學能夠指導下最好,不勝感激 ,否則得自己扒mplayer的代碼了。
3 ts_setup_info
這里根據ts_open獲取的信息,來setup dtplayer的media info
然后選擇av視頻流就能夠了,比較簡單
4 ts_read_frame
這里比較重要,也是花了比較多時間的地方,一開始的時候,以為拿到av的pid后,后面直接組裝數據就能夠了
流程為: 解析ts包獲取pid -> 若是選擇的pid,直接將payload保存下來,通過簡單的推斷是否是unit_start來推斷是否讀取到了完整幀 --> 返回完整幀
但實際情況并非這樣,保存在ts包中的是實際的pes包,而不是es流,在讀包的時候須要將pes包頭去掉
這里參考ffmpeg進行了模擬,詳細代碼在: handle_ts_pkt 中,詳細邏輯不說了,僅僅是更正自己的一個認知誤區
只是這里還有個問題,讀取的數據包會摻雜錯誤信息: 經過跟代碼,大體定位了問題,在解析pes包頭的時候,是能夠知道這個包的大小的
但讀取ts包的時候都是依照188,即payload通常是184字節,這樣非常有可能就超過了pes包的大小
此時應該怎樣處理: 若僅僅讀取固定大小湊足包大小返回,則聲音是錯的
若將全部payload都打包進去,則播放過程會有雜音。
這里還須要研究下。 有熟悉的同學也請指教 。
5 ts_seek
這里seek就比較簡單了,盡管還未完畢,但思想能夠說下,參考ffmpeg,一句bitrate seek到某個位置,讀取ts包,計算pts
若不匹配,則依照二分查找算法,繼續seek
來達到seek的目的。(后面會實現)
這里ts demux僅僅是為了實現一個簡單的功能,給后面基于dtplayer開發簡單的應用的開發人員提供便利: 若服務端也自己做的話,能夠非常方便的改造dtplayer中的ts demux達到字節的要求,
同一時候去掉了ffmpeg的負擔,豈不非常方便,這也是dtplayer會一直遵循的目標。
github: https://github.com/avplayer/dtplayer ? ?# C++
github : https://github.com/peterfuture/dtplayer ?# C
bug report:? peter_future@outlook.com
blog:? http://blog.csdn.net/dtplayer
bbs:? http://avboost.com/
wiki:? http://wiki.avplayer.org/Dtplayer
因為后面隨著開發的進行文章會進行細節的更新,因此為了保證讀者隨時讀到最新的內容,文章禁止轉載,多謝大家支持!
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
