Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
Z
ZLMediaKit
概览
Overview
Details
Activity
Cycle Analytics
版本库
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
问题
0
Issues
0
列表
Board
标记
里程碑
合并请求
0
Merge Requests
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
Snippets
成员
Collapse sidebar
Close sidebar
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
张翔宇
ZLMediaKit
Commits
375d36b4
Commit
375d36b4
authored
Jul 29, 2021
by
ziyue
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
确保rtp丢包时,丢弃gop后续所有帧,防止丢包花屏
parent
55fc598d
显示空白字符变更
内嵌
并排
正在显示
4 个修改的文件
包含
151 行增加
和
117 行删除
+151
-117
src/Extension/H264Rtp.cpp
+81
-77
src/Extension/H264Rtp.h
+7
-2
src/Extension/H265Rtp.cpp
+56
-34
src/Extension/H265Rtp.h
+7
-4
没有找到文件。
src/Extension/H264Rtp.cpp
查看文件 @
375d36b4
...
...
@@ -46,7 +46,14 @@ H264Frame::Ptr H264RtpDecoder::obtainFrame() {
}
bool
H264RtpDecoder
::
inputRtp
(
const
RtpPacket
::
Ptr
&
rtp
,
bool
key_pos
)
{
return
decodeRtp
(
rtp
);
auto
seq
=
rtp
->
getSeq
();
auto
ret
=
decodeRtp
(
rtp
);
if
(
!
_gop_dropped
&&
seq
!=
(
uint16_t
)
(
_last_seq
+
1
)
&&
_last_seq
)
{
_gop_dropped
=
true
;
WarnL
<<
"start drop h264 gop, last seq:"
<<
_last_seq
<<
", rtp:
\r\n
"
<<
rtp
->
dumpString
();
}
_last_seq
=
seq
;
return
ret
;
}
/*
...
...
@@ -66,118 +73,115 @@ Table 1. Summary of NAL unit types and their payload structures
30-31 undefined -
*/
bool
H264RtpDecoder
::
decodeRtp
(
const
RtpPacket
::
Ptr
&
rtp
)
{
auto
frame
=
rtp
->
getPayload
();
auto
length
=
rtp
->
getPayloadSize
();
auto
stamp
=
rtp
->
getStampMS
();
auto
seq
=
rtp
->
getSeq
();
auto
nal_type
=
*
frame
&
0x1F
;
auto
nal_suffix
=
*
frame
&
(
~
0x1F
);
if
(
nal_type
>=
0
&&
nal_type
<
24
)
{
//a full frame
bool
H264RtpDecoder
::
singleFrame
(
const
RtpPacket
::
Ptr
&
rtp
,
const
uint8_t
*
ptr
,
ssize_t
size
,
uint32_t
stamp
){
_frame
->
_buffer
.
assign
(
"
\x00\x00\x00\x01
"
,
4
);
_frame
->
_buffer
.
append
((
char
*
)
frame
,
length
);
_frame
->
_buffer
.
append
((
char
*
)
ptr
,
size
);
_frame
->
_pts
=
stamp
;
auto
key
=
_frame
->
keyFrame
();
onGetH264
(
_frame
);
return
(
key
);
//i frame
}
outputFrame
(
rtp
,
_frame
);
return
key
;
}
switch
(
nal_type
)
{
case
24
:
{
// 24 STAP-A 单一时间的组合包
bool
haveIDR
=
false
;
auto
ptr
=
frame
+
1
;
while
(
true
)
{
size_t
off
=
ptr
-
frame
;
if
(
off
>=
length
)
{
bool
H264RtpDecoder
::
unpackStapA
(
const
RtpPacket
::
Ptr
&
rtp
,
const
uint8_t
*
ptr
,
ssize_t
size
,
uint32_t
stamp
)
{
//STAP-A 单一时间的组合包
auto
have_key_frame
=
false
;
auto
end
=
ptr
+
size
;
while
(
ptr
+
2
<
end
)
{
uint16_t
len
=
(
ptr
[
0
]
<<
8
)
|
ptr
[
1
];
if
(
!
len
||
ptr
+
len
>
end
)
{
WarnL
<<
"invalid rtp data size:"
<<
len
<<
",rtp:
\r\n
"
<<
rtp
->
dumpString
();
_gop_dropped
=
true
;
break
;
}
//获取当前nalu的大小
uint16_t
len
=
*
ptr
++
;
len
<<=
8
;
len
|=
*
ptr
++
;
if
(
off
+
len
>
length
)
{
break
;
}
if
(
len
>
0
)
{
//有有效数据
_frame
->
_buffer
.
assign
(
"
\x00\x00\x00\x01
"
,
4
);
_frame
->
_buffer
.
append
((
char
*
)
ptr
,
len
);
_frame
->
_pts
=
stamp
;
if
((
ptr
[
0
]
&
0x1F
)
==
H264Frame
::
NAL_IDR
)
{
haveIDR
=
true
;
}
onGetH264
(
_frame
);
ptr
+=
2
;
if
(
singleFrame
(
rtp
,
ptr
,
len
,
stamp
))
{
have_key_frame
=
true
;
}
ptr
+=
len
;
}
return
haveIDR
;
}
return
have_key_frame
;
}
case
28
:
{
//FU-A
FuFlags
*
fu
=
(
FuFlags
*
)
(
frame
+
1
);
bool
H264RtpDecoder
::
mergeFu
(
const
RtpPacket
::
Ptr
&
rtp
,
const
uint8_t
*
ptr
,
ssize_t
size
,
uint32_t
stamp
,
uint16_t
seq
)
{
auto
nal_suffix
=
*
ptr
&
(
~
0x1F
);
FuFlags
*
fu
=
(
FuFlags
*
)
(
ptr
+
1
);
if
(
fu
->
start_bit
)
{
//该帧的第一个rtp包 FU-A start
//预留空间,防止频繁扩容拷贝
_frame
->
_buffer
.
reserve
(
_max_frame_size
);
//该帧的第一个rtp包
_frame
->
_buffer
.
assign
(
"
\x00\x00\x00\x01
"
,
4
);
_frame
->
_buffer
.
push_back
(
nal_suffix
|
fu
->
nal_type
);
_frame
->
_buffer
.
append
((
char
*
)
frame
+
2
,
length
-
2
);
_frame
->
_pts
=
stamp
;
//该函数return时,保存下当前sequence,以便下次对比seq是否连续
_last_seq
=
seq
;
return
_frame
->
keyFrame
();
_fu_dropped
=
false
;
}
if
(
seq
!=
(
uint16_t
)
(
_last_seq
+
1
))
{
//中间的或末尾的rtp包,其seq必须连续(如果回环了则判定为连续),否则说明rtp丢包,那么该帧不完整,必须得丢弃
_frame
->
_buffer
.
clear
();
WarnL
<<
"rtp丢包: "
<<
seq
<<
" != "
<<
_last_seq
<<
" + 1,该帧被废弃"
;
if
(
_fu_dropped
)
{
//该帧不完整
return
false
;
}
if
(
!
fu
->
end_bit
)
{
//该帧的中间rtp包 FU-A mid
_frame
->
_buffer
.
append
((
char
*
)
frame
+
2
,
length
-
2
);
//该函数return时,保存下当前sequence,以便下次对比seq是否连续
_last_seq
=
seq
;
if
(
!
fu
->
start_bit
&&
seq
!=
(
uint16_t
)
(
_last_seq
+
1
))
{
//中间的或末尾的rtp包,其seq必须连续,否则说明rtp丢包,那么该帧不完整,必须得丢弃
_fu_dropped
=
true
;
_frame
->
_buffer
.
clear
();
return
false
;
}
//该帧最后一个rtp包 FU-A end
_frame
->
_buffer
.
append
((
char
*
)
frame
+
2
,
length
-
2
);
_frame
->
_pts
=
stamp
;
//计算最大的帧
auto
frame_size
=
_frame
->
size
();
if
(
frame_size
>
_max_frame_size
)
{
_max_frame_size
=
frame_size
;
//后面追加数据
_frame
->
_buffer
.
append
((
char
*
)
ptr
+
2
,
size
-
2
);
if
(
!
fu
->
end_bit
)
{
//非末尾包
return
fu
->
start_bit
?
_frame
->
keyFrame
()
:
false
;
}
onGetH264
(
_frame
);
//确保下一次fu必须收到第一个包
_fu_dropped
=
true
;
//该帧最后一个rtp包,输出frame
outputFrame
(
rtp
,
_frame
);
return
false
;
}
}
bool
H264RtpDecoder
::
decodeRtp
(
const
RtpPacket
::
Ptr
&
rtp
)
{
auto
frame
=
rtp
->
getPayload
();
auto
length
=
rtp
->
getPayloadSize
();
auto
stamp
=
rtp
->
getStampMS
();
auto
seq
=
rtp
->
getSeq
();
int
nal
=
H264_TYPE
(
frame
[
0
]);
switch
(
nal
)
{
case
24
:
// 24 STAP-A Single-time aggregation packet 5.7.1
return
unpackStapA
(
rtp
,
frame
+
1
,
length
-
1
,
stamp
);
case
28
:
// 28 FU-A Fragmentation unit
return
mergeFu
(
rtp
,
frame
,
length
,
stamp
,
seq
);
default
:
{
// 29 FU-B 单NAL单元B模式
// 25 STAP-B 单一时间的组合包
// 26 MTAP16 多个时间的组合包
// 27 MTAP24 多个时间的组合包
WarnL
<<
"不支持的rtp类型:"
<<
(
int
)
nal_type
<<
" "
<<
seq
;
if
(
nal
<
24
)
{
//Single NAL Unit Packets
return
singleFrame
(
rtp
,
frame
,
length
,
stamp
);
}
_gop_dropped
=
true
;
WarnL
<<
"不支持该类型的264 RTP包, nal type:"
<<
nal
<<
", rtp:
\r\n
"
<<
rtp
->
dumpString
();
return
false
;
}
}
}
void
H264RtpDecoder
::
o
nGetH264
(
const
H264Frame
::
Ptr
&
frame
)
{
void
H264RtpDecoder
::
o
utputFrame
(
const
RtpPacket
::
Ptr
&
rtp
,
const
H264Frame
::
Ptr
&
frame
)
{
//rtsp没有dts,那么根据pts排序算法生成dts
_dts_generator
.
getDts
(
frame
->
_pts
,
frame
->
_dts
);
_dts_generator
.
getDts
(
frame
->
_pts
,
frame
->
_dts
);
if
(
frame
->
keyFrame
()
&&
_gop_dropped
)
{
_gop_dropped
=
false
;
InfoL
<<
"new gop received, rtp:
\r\n
"
<<
rtp
->
dumpString
();
}
if
(
!
_gop_dropped
)
{
RtpCodec
::
inputFrame
(
frame
);
}
_frame
=
obtainFrame
();
}
////////////////////////////////////////////////////////////////////////
H264RtpEncoder
::
H264RtpEncoder
(
uint32_t
ssrc
,
uint32_t
mtu
,
uint32_t
sample_rate
,
uint8_t
pt
,
uint8_t
interleaved
)
...
...
src/Extension/H264Rtp.h
查看文件 @
375d36b4
...
...
@@ -43,13 +43,18 @@ public:
}
private
:
bool
singleFrame
(
const
RtpPacket
::
Ptr
&
rtp
,
const
uint8_t
*
ptr
,
ssize_t
size
,
uint32_t
stamp
);
bool
unpackStapA
(
const
RtpPacket
::
Ptr
&
rtp
,
const
uint8_t
*
ptr
,
ssize_t
size
,
uint32_t
stamp
);
bool
mergeFu
(
const
RtpPacket
::
Ptr
&
rtp
,
const
uint8_t
*
ptr
,
ssize_t
size
,
uint32_t
stamp
,
uint16_t
seq
);
bool
decodeRtp
(
const
RtpPacket
::
Ptr
&
rtp
);
void
onGetH264
(
const
H264Frame
::
Ptr
&
frame
);
H264Frame
::
Ptr
obtainFrame
();
void
outputFrame
(
const
RtpPacket
::
Ptr
&
rtp
,
const
H264Frame
::
Ptr
&
frame
);
private
:
bool
_gop_dropped
=
false
;
bool
_fu_dropped
=
true
;
uint16_t
_last_seq
=
0
;
size_t
_max_frame_size
=
0
;
H264Frame
::
Ptr
_frame
;
DtsGenerator
_dts_generator
;
};
...
...
src/Extension/H265Rtp.cpp
查看文件 @
375d36b4
...
...
@@ -12,6 +12,7 @@
namespace
mediakit
{
//https://datatracker.ietf.org/doc/rfc7798/
//H265 nalu 头两个字节的定义
/*
0 1
...
...
@@ -41,7 +42,7 @@ H265Frame::Ptr H265RtpDecoder::obtainFrame() {
#define CHECK_SIZE(total, size, ret) \
if (total < size) { \
WarnL << "
数据不够:" << total << " " << size;
return ret; \
WarnL << "
invalid rtp data size:" << total << " < " << size << ",rtp:\r\n" << rtp->dumpString(); _gop_dropped = true;
return ret; \
}
// 4.4.2. Aggregation Packets (APs) (p25)
...
...
@@ -68,9 +69,8 @@ H265Frame::Ptr H265RtpDecoder::obtainFrame() {
| : ...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
bool
H265RtpDecoder
::
unpackAp
(
const
uint8_t
*
ptr
,
ssize_t
size
,
uint32_t
stamp
){
bool
H265RtpDecoder
::
unpackAp
(
const
RtpPacket
::
Ptr
&
rtp
,
const
uint8_t
*
ptr
,
ssize_t
size
,
uint32_t
stamp
){
bool
have_key_frame
=
false
;
//忽略PayloadHdr
CHECK_SIZE
(
size
,
2
,
have_key_frame
);
ptr
+=
2
;
...
...
@@ -88,7 +88,7 @@ bool H265RtpDecoder::unpackAp(const uint8_t *ptr, ssize_t size, uint32_t stamp){
size
-=
2
;
ptr
+=
2
;
CHECK_SIZE
(
size
,
nalu_size
,
have_key_frame
)
if
(
singleFrame
(
ptr
,
nalu_size
,
stamp
))
{
if
(
singleFrame
(
rtp
,
ptr
,
nalu_size
,
stamp
))
{
have_key_frame
=
true
;
}
size
-=
nalu_size
;
...
...
@@ -119,7 +119,7 @@ bool H265RtpDecoder::unpackAp(const uint8_t *ptr, ssize_t size, uint32_t stamp){
+---------------+
*/
bool
H265RtpDecoder
::
mergeFu
(
const
uint8_t
*
ptr
,
ssize_t
size
,
uint16_t
seq
,
uint32_t
stamp
){
bool
H265RtpDecoder
::
mergeFu
(
const
RtpPacket
::
Ptr
&
rtp
,
const
uint8_t
*
ptr
,
ssize_t
size
,
uint32_t
stamp
,
uint16_t
seq
){
CHECK_SIZE
(
size
,
4
,
false
);
auto
s_bit
=
ptr
[
2
]
>>
7
;
auto
e_bit
=
(
ptr
[
2
]
>>
6
)
&
0x01
;
...
...
@@ -127,15 +127,21 @@ bool H265RtpDecoder::mergeFu(const uint8_t *ptr, ssize_t size, uint16_t seq, uin
if
(
s_bit
)
{
//该帧的第一个rtp包
_frame
->
_buffer
.
assign
(
"
\x00\x00\x00\x01
"
,
4
);
//恢复nalu头两个字节
_frame
->
_buffer
.
push_back
((
type
<<
1
)
|
(
ptr
[
0
]
&
0x81
));
_frame
->
_buffer
.
push_back
(
ptr
[
1
]);
_frame
->
_pts
=
stamp
;
_fu_dropped
=
false
;
}
if
(
!
s_bit
&&
seq
!=
(
uint16_t
)
(
_last_seq
+
1
)
&&
seq
!=
0
)
{
if
(
_fu_dropped
)
{
//该帧不完整
return
false
;
}
if
(
!
s_bit
&&
seq
!=
(
uint16_t
)
(
_last_seq
+
1
))
{
//中间的或末尾的rtp包,其seq必须连续,否则说明rtp丢包,那么该帧不完整,必须得丢弃
_fu_dropped
=
true
;
_frame
->
_buffer
.
clear
();
WarnL
<<
"rtp丢包: "
<<
seq
<<
" != "
<<
_last_seq
<<
" + 1,该帧被废弃"
;
return
false
;
}
...
...
@@ -152,64 +158,80 @@ bool H265RtpDecoder::mergeFu(const uint8_t *ptr, ssize_t size, uint16_t seq, uin
CHECK_SIZE
(
size
,
1
,
false
);
//后面追加数据
_frame
->
_buffer
.
append
((
char
*
)
ptr
,
size
);
if
(
!
e_bit
)
{
//非末尾包
_last_seq
=
seq
;
_frame
->
_buffer
.
append
((
char
*
)
ptr
,
size
);
return
s_bit
?
_frame
->
keyFrame
()
:
false
;
}
//确保下一次fu必须收到第一个包
_fu_dropped
=
true
;
//该帧最后一个rtp包
_frame
->
_pts
=
stamp
;
_frame
->
_buffer
.
append
((
char
*
)
ptr
,
size
);
outputFrame
(
_frame
);
outputFrame
(
rtp
,
_frame
);
return
false
;
}
bool
H265RtpDecoder
::
inputRtp
(
const
RtpPacket
::
Ptr
&
rtp
,
bool
)
{
bool
H265RtpDecoder
::
inputRtp
(
const
RtpPacket
::
Ptr
&
rtp
,
bool
)
{
auto
seq
=
rtp
->
getSeq
();
auto
ret
=
decodeRtp
(
rtp
);
if
(
!
_gop_dropped
&&
seq
!=
(
uint16_t
)
(
_last_seq
+
1
)
&&
_last_seq
)
{
_gop_dropped
=
true
;
WarnL
<<
"start drop h265 gop, last seq:"
<<
_last_seq
<<
", rtp:
\r\n
"
<<
rtp
->
dumpString
();
}
_last_seq
=
seq
;
return
ret
;
}
bool
H265RtpDecoder
::
decodeRtp
(
const
RtpPacket
::
Ptr
&
rtp
)
{
auto
frame
=
rtp
->
getPayload
();
auto
length
=
rtp
->
getPayloadSize
();
auto
stamp
=
rtp
->
getStampMS
();
auto
seq
=
rtp
->
getSeq
();
int
nal
=
H265_TYPE
(
frame
[
0
]);
if
(
nal
>
50
){
// packet discard, Unsupported (HEVC) NAL type
WarnL
<<
"不支持该类型的265 RTP包"
<<
nal
;
return
false
;
}
switch
(
nal
)
{
case
50
:
//4.4.4. PACI Packets (p32)
WarnL
<<
"不支持该类型的265 RTP包"
<<
nal
;
return
false
;
case
48
:
// aggregated packet (AP) - with two or more NAL units
return
unpackAp
(
frame
,
length
,
stamp
);
return
unpackAp
(
rtp
,
frame
,
length
,
stamp
);
case
49
:
// fragmentation unit (FU)
return
mergeFu
(
frame
,
length
,
seq
,
stamp
);
default
:
// 4.4.1. Single NAL Unit Packets (p24)
return
singleFrame
(
frame
,
length
,
stamp
);
return
mergeFu
(
rtp
,
frame
,
length
,
stamp
,
seq
);
default
:
{
if
(
nal
<
48
)
{
// Single NAL Unit Packets (p24)
return
singleFrame
(
rtp
,
frame
,
length
,
stamp
);
}
_gop_dropped
=
true
;
WarnL
<<
"不支持该类型的265 RTP包, nal type"
<<
nal
<<
", rtp:
\r\n
"
<<
rtp
->
dumpString
();
return
false
;
}
}
}
bool
H265RtpDecoder
::
singleFrame
(
const
uint8_t
*
ptr
,
ssize_t
size
,
uint32_t
stamp
){
//a full frame
bool
H265RtpDecoder
::
singleFrame
(
const
RtpPacket
::
Ptr
&
rtp
,
const
uint8_t
*
ptr
,
ssize_t
size
,
uint32_t
stamp
){
_frame
->
_buffer
.
assign
(
"
\x00\x00\x00\x01
"
,
4
);
_frame
->
_buffer
.
append
((
char
*
)
ptr
,
size
);
_frame
->
_pts
=
stamp
;
auto
key
=
_frame
->
keyFrame
();
outputFrame
(
_frame
);
outputFrame
(
rtp
,
_frame
);
return
key
;
}
void
H265RtpDecoder
::
outputFrame
(
const
H265Frame
::
Ptr
&
frame
)
{
void
H265RtpDecoder
::
outputFrame
(
const
RtpPacket
::
Ptr
&
rtp
,
const
H265Frame
::
Ptr
&
frame
)
{
//rtsp没有dts,那么根据pts排序算法生成dts
_dts_generator
.
getDts
(
frame
->
_pts
,
frame
->
_dts
);
//输出frame
_dts_generator
.
getDts
(
frame
->
_pts
,
frame
->
_dts
);
if
(
frame
->
keyFrame
()
&&
_gop_dropped
)
{
_gop_dropped
=
false
;
InfoL
<<
"new gop received, rtp:
\r\n
"
<<
rtp
->
dumpString
();
}
if
(
!
_gop_dropped
)
{
RtpCodec
::
inputFrame
(
frame
);
}
_frame
=
obtainFrame
();
}
...
...
src/Extension/H265Rtp.h
查看文件 @
375d36b4
...
...
@@ -44,15 +44,18 @@ public:
}
private
:
bool
unpackAp
(
const
uint8_t
*
ptr
,
ssize_t
size
,
uint32_t
stamp
);
bool
mergeFu
(
const
uint8_t
*
ptr
,
ssize_t
size
,
uint16_t
seq
,
uint32_t
stamp
);
bool
singleFrame
(
const
uint8_t
*
ptr
,
ssize_t
size
,
uint32_t
stamp
);
bool
unpackAp
(
const
RtpPacket
::
Ptr
&
rtp
,
const
uint8_t
*
ptr
,
ssize_t
size
,
uint32_t
stamp
);
bool
mergeFu
(
const
RtpPacket
::
Ptr
&
rtp
,
const
uint8_t
*
ptr
,
ssize_t
size
,
uint32_t
stamp
,
uint16_t
seq
);
bool
singleFrame
(
const
RtpPacket
::
Ptr
&
rtp
,
const
uint8_t
*
ptr
,
ssize_t
size
,
uint32_t
stamp
);
bool
decodeRtp
(
const
RtpPacket
::
Ptr
&
rtp
);
H265Frame
::
Ptr
obtainFrame
();
void
outputFrame
(
const
H265Frame
::
Ptr
&
frame
);
void
outputFrame
(
const
RtpPacket
::
Ptr
&
rtp
,
const
H265Frame
::
Ptr
&
frame
);
private
:
bool
_using_donl_field
=
false
;
bool
_gop_dropped
=
false
;
bool
_fu_dropped
=
true
;
uint16_t
_last_seq
=
0
;
H265Frame
::
Ptr
_frame
;
DtsGenerator
_dts_generator
;
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论