文件夾
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元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

