Commit 375d36b4 by ziyue

确保rtp丢包时,丢弃gop后续所有帧,防止丢包花屏

parent 55fc598d
......@@ -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::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);
_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);
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();
}
......
......@@ -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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论