一个rtp缓存实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 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();
    }
}


猜你喜欢

转载自blog.csdn.net/glw0223/article/details/89219545