版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/glw0223/article/details/89219545
一个rtp缓存实现
- 只是思路,不能直接使用
class RTPCache {
public:
//设置缓存rtp包的最大个数,目前代码里音频是1000个,视频是3000个
//最先调用,或者应该rename成init,便于理解
uint32_t set_max_size(uint32_t max_size);
//收到rtp数据
int32_t set_rtp_normal(const avformat::RTP_FIXED_HEADER *rtp, uint16_t len);
private:
//用于释放rtp数据
uint32_t _adjust();
//队列尾部插入rtp数据
void _push_back(const avformat::RTP_FIXED_HEADER *rtp, uint16_t len);
//队列特定位置插入rtp数据(根据sequence number来计算)
int _fill_in(const avformat::RTP_FIXED_HEADER *rtp, uint16_t len);
//清空队列所有数据
void _clean();
protected:
//存储rtp数据
std::deque<RTPPacket> _deque_rtp_packet;
private:
//视频:90k,音频:48k/44.1k等
uint32_t _sample_rate;
};
uint32_t RTPCache::set_max_size(uint32_t max_size)
{
_max_size = max_size;
_adjust();
return _max_size;
}
int32_t RTPCache::set_rtp_normal(const RTP_FIXED_HEADER *rtp, uint16_t len)
{
//没有初始化采样率,直接返回
if (_sample_rate == 0)
{
return 1;
}
//数据无效,返回错误
if (rtp == NULL || len < sizeof(RTP_FIXED_HEADER))
{
return -1;
}
//ssrc初始化;最新时间戳更新
if (_ssrc == 0)
{
_ssrc = rtp->get_ssrc();
_last_push_tick_timestamp = rtp->get_rtp_timestamp();
}
//后续的rtp包,ssrc必须相等
if (rtp->get_ssrc() != _ssrc)
{
return -1;
}
///////////////////////////////////////////////////
//如果队列为空,直接放到队列最后
if (_deque_rtp_packet.size() == 0)
{
_push_back(rtp, len);
return 0;
}
uint16_t latest_len = 0;
//队列尾部的是最新的rtp包
avformat::RTP_FIXED_HEADER *latest = _deque_rtp_packet.back().get_real_rtp(latest_len);
//当前rtp包的时间戳和最新的时间戳相差20s,则认为时间跳跃;清空队列,把当前包放到队列尾部
if (latest && abs(int(latest->get_rtp_timestamp() - rtp->get_rtp_timestamp())) >= int(20 * _sample_rate))
{
_clean();//清空队列
_push_back(rtp, len);
return 0;
}
// expect_next_max = back.seq + _max_size
// 3 1 2
// ||---------|-------------------------|-------------------|------------------||
// front.seq back.seq expect_next_max
// | cache | large seq | small seq
//
// 1. if back.seq < new_block.seq < expect_next_max, push back and fill empty block
// 2. if expect_next_max <= new_block.seq < front.seq, reset cache,
// 3. if front.seq <= new_block.seq <= back.seq, fill in.
//
uint16_t seq = rtp->get_seq();
uint16_t back_seq = _deque_rtp_packet.back().get_seq();
uint16_t front_seq = _deque_rtp_packet.front().get_seq();
//expect_next_max的范围是[0,65535]
uint16_t expect_next_max = back_seq + _max_size;
//
// 1. if back.seq < new_block.seq <= expect_next_max, push back and fill empty block
//(uint16_t)(seq - back_seq),A、计算机语言只有加法;B、在计算机里,正数是原码,负数是补码
//如果大码率,这里需要改造
if (seq != back_seq && (uint16_t)(seq - back_seq) < _max_size)
{
for (uint16_t idx = back_seq + 1; idx != seq; idx++)
{
RTPPacket block(NULL, 0);
_deque_rtp_packet.push_back(block);
}
_push_back(rtp, len);
//由于新包插入尾部,可能会超过_max_size,需要释放队列头部的包
_adjust();
return 0;
}
// 2. if expect_next_max < new_block.seq < front.seq, reset cache,
if ((uint16_t)(seq - expect_next_max) < (uint16_t)(front_seq - expect_next_max))
{
_clean();
_push_back(rtp, len);
return 0;
}
//如果当前包的sequence number在队列头和队列尾之间,则插入到指定索引
// 3. if front.seq <= new_block.seq <= back.seq, fill in.
if ((uint16_t)(seq - front_seq) <= (uint16_t)(back_seq - front_seq))
{
_fill_in(rtp, len);
//这里认为插入到指定位置的rtp包,是恢复的包(实际上,有可能是乱序的包)
_deque_recover_rtp_packet_seq.push_back(seq);
return 0;
}
return -1;
}
uint32_t RTPCache::_adjust(){
//如果大于设置的队列阈值,则释放
while(_deque_rtp_packet.size()>_max_size){
_deque_rtp_packet.front().finalize();
_deque_rtp_packet.pop_front();
}
//判断如果是无效的数据,则删除掉(无效数据是人为加入的,再分析)
while(_deque_rtp_packet.size()>0 && !_deque_rtp_packet.front().is_valid()){
_deque_rtp_packet.front().finalize();
_deque_rtp_packet.pop_front();
}
return _deque_rtp_packet.size();
}
void RTPCache::_push_back(const avformat::RTP_FIXED_HEADER *rtp, uint16_t len)
{
//构造rtp数据包,放到队列尾部
RTPPacket packet(rtp, len);
_deque_rtp_packet.push_back(packet);
//计算相邻两个rtp包的时间戳偏差(可能为0,如果是同一帧的两个包的话),
//大约13个小时后会循环,没有考虑,估计是用户发现异常,自己重新上传了!!!
uint32_t delta_tick = (uint32_t)(packet.get_timestamp() - _last_push_tick_timestamp);
//将偏差换算成毫秒,然后累加到成员变量上
_last_push_relative_timestamp_ms += (uint64_t)delta_tick * 1000 / _sample_rate;
//保存sequence number和时间戳到成员变量
_last_push_seq = packet.get_seq();
_last_push_tick_timestamp = packet.get_timestamp();
//有rtp包上来,就更新该流最后的活跃时间点
_push_active = time(NULL);
}
int RTPCache::_fill_in(const RTP_FIXED_HEADER *rtp, uint16_t len)
{
uint16_t seq = rtp->get_seq();
uint16_t front_seq = _deque_rtp_packet.front().get_seq();
uint32_t idx = (uint16_t)(seq - front_seq);
//rtp包放的位置是参考队列的第一个包
if (idx >= _deque_rtp_packet.size())
{
_clean();
return -1;
}
//如果索引idx位置的包是无效包,则更新该包
RTPPacket &packet = _deque_rtp_packet[idx];
if (!packet.is_valid())
{
packet.set_real_rtp(rtp, len);
}
return 0;
}
void RTPCache::_clean()
{
while (_deque_rtp_packet.size() > 0)
{
RTPPacket &packet = _deque_rtp_packet.back();
packet.finalize();
_deque_rtp_packet.pop_back();
}
}