mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-26 04:31:37 +08:00
确保rtp丢包时,丢弃gop后续所有帧,防止丢包花屏
This commit is contained in:
parent
55fc598d4c
commit
375d36b4c8
@ -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::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(rtp, _frame);
|
||||
return key;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
ptr += 2;
|
||||
if (singleFrame(rtp, ptr, len, stamp)) {
|
||||
have_key_frame = true;
|
||||
}
|
||||
ptr += len;
|
||||
}
|
||||
return have_key_frame;
|
||||
}
|
||||
|
||||
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包
|
||||
_frame->_buffer.assign("\x00\x00\x00\x01", 4);
|
||||
_frame->_buffer.push_back(nal_suffix | fu->nal_type);
|
||||
_frame->_pts = stamp;
|
||||
_fu_dropped = false;
|
||||
}
|
||||
|
||||
if (_fu_dropped) {
|
||||
//该帧不完整
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fu->start_bit && seq != (uint16_t) (_last_seq + 1)) {
|
||||
//中间的或末尾的rtp包,其seq必须连续,否则说明rtp丢包,那么该帧不完整,必须得丢弃
|
||||
_fu_dropped = true;
|
||||
_frame->_buffer.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
//后面追加数据
|
||||
_frame->_buffer.append((char *) ptr + 2, size - 2);
|
||||
|
||||
if (!fu->end_bit) {
|
||||
//非末尾包
|
||||
return fu->start_bit ? _frame->keyFrame() : false;
|
||||
}
|
||||
|
||||
//确保下一次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();
|
||||
auto nal_type = *frame & 0x1F;
|
||||
auto nal_suffix = *frame & (~0x1F);
|
||||
int nal = H264_TYPE(frame[0]);
|
||||
|
||||
if (nal_type >= 0 && nal_type < 24) {
|
||||
//a full frame
|
||||
_frame->_buffer.assign("\x00\x00\x00\x01", 4);
|
||||
_frame->_buffer.append((char *) frame, length);
|
||||
_frame->_pts = stamp;
|
||||
auto key = _frame->keyFrame();
|
||||
onGetH264(_frame);
|
||||
return (key); //i frame
|
||||
}
|
||||
switch (nal) {
|
||||
case 24:
|
||||
// 24 STAP-A Single-time aggregation packet 5.7.1
|
||||
return unpackStapA(rtp, frame + 1, length - 1, stamp);
|
||||
|
||||
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) {
|
||||
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 += len;
|
||||
}
|
||||
return haveIDR;
|
||||
}
|
||||
|
||||
case 28: {
|
||||
//FU-A
|
||||
FuFlags *fu = (FuFlags *) (frame + 1);
|
||||
if (fu->start_bit) {
|
||||
//该帧的第一个rtp包 FU-A start
|
||||
//预留空间,防止频繁扩容拷贝
|
||||
_frame->_buffer.reserve(_max_frame_size);
|
||||
_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();
|
||||
}
|
||||
|
||||
if (seq != (uint16_t) (_last_seq + 1)) {
|
||||
//中间的或末尾的rtp包,其seq必须连续(如果回环了则判定为连续),否则说明rtp丢包,那么该帧不完整,必须得丢弃
|
||||
_frame->_buffer.clear();
|
||||
WarnL << "rtp丢包: " << seq << " != " << _last_seq << " + 1,该帧被废弃";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fu->end_bit) {
|
||||
//该帧的中间rtp包 FU-A mid
|
||||
_frame->_buffer.append((char *) frame + 2, length - 2);
|
||||
//该函数return时,保存下当前sequence,以便下次对比seq是否连续
|
||||
_last_seq = seq;
|
||||
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;
|
||||
}
|
||||
onGetH264(_frame);
|
||||
return false;
|
||||
}
|
||||
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::onGetH264(const H264Frame::Ptr &frame) {
|
||||
void H264RtpDecoder::outputFrame(const RtpPacket::Ptr &rtp, const H264Frame::Ptr &frame) {
|
||||
//rtsp没有dts,那么根据pts排序算法生成dts
|
||||
_dts_generator.getDts(frame->_pts,frame->_dts);
|
||||
RtpCodec::inputFrame(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();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
H264RtpEncoder::H264RtpEncoder(uint32_t ssrc, uint32_t mtu, uint32_t sample_rate, uint8_t pt, uint8_t interleaved)
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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);
|
||||
case 49:
|
||||
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
|
||||
RtpCodec::inputFrame(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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user