zlMediaKit 11 rtsp相关--分包/sdp信令交互/排序

在这里插入图片描述

RtspSpliter

在这里插入图片描述

结构

    bool _enableRecvRtp = false; //是否允许收到rtp
    bool _isRtpPacket = false;   //包是否是rtp包
    Parser _parser;              //jie

重写了HttpRequestSpliter

ssize_t onRecvHeader(const char *data,size_t len) override;

void onRecvContent(const char *data,size_t len) override;

const char *onSearchPacketTail(const char *data,size_t len) override ;

子类需要实现

    /**
     * 收到完整的rtsp包回调,包括sdp等content数据
     * @param parser rtsp包
     */
    virtual void onWholeRtspPacket(Parser &parser) = 0;

    /**
     * 收到rtp包回调
     * @param data
     * @param len
     */
    virtual void onRtpPacket(const char *data,size_t len) = 0;

override

onSearchPacketTail

const char *RtspSplitter::onSearchPacketTail(const char *data, size_t len) {
    auto ret = onSearchPacketTail_l(data, len);
    if(ret){
        return ret;
    }

    if (len > 256 * 1024) {
        //rtp大于256KB
        ret = (char *) memchr(data, '$', len);
        if (!ret) {
            WarnL << "rtp缓存溢出:" << hexdump(data, 1024);
            reset();
        }
    }
    return ret;
}

为什么**$开头就是rtp包**呢

RTP/AVP & RTP/AVP/TCP

rtp载荷H264解包过程分析,ffmpeg解码qt展示_土豆西瓜大芝麻的博客-CSDN博客_ffmpeg qt rtp

const char *RtspSplitter::onSearchPacketTail_l(const char *data, size_t len) {
    if(!_enableRecvRtp || data[0] != '$'){
        //这是rtsp包
        _isRtpPacket = false;
        return HttpRequestSplitter::onSearchPacketTail(data, len);
    }
    //这是rtp包
    if(len < 4){
        //数据不够
        return nullptr;
    }
    uint16_t length = (((uint8_t *)data)[2] << 8) | ((uint8_t *)data)[3];
    if(len < (size_t)(length + 4)){
        //数据不够
        return nullptr;
    }
    //返回rtp包末尾
    _isRtpPacket = true;
    return data + 4 + length;
}

onRecvHeader

如果是裹着rtp的rtsp(以$开头)就onRtpPacket

如果是没有Content的rtsp,比如OPTIONS SETUP RECORD 等等,onWholeRtspPacket

如果是有Content的rtsp,比如ANNOUNCE,就回到HttpRequestSplitter::input中,之后调用onRecvContent

ssize_t RtspSplitter::onRecvHeader(const char *data, size_t len) {
    PrintD("onRecvHeader%s", data);
    if(_isRtpPacket){
        onRtpPacket(data,len);
        return 0;
    }
    _parser.Parse(data);
    auto ret = getContentLength(_parser); 
    if(ret == 0){
        onWholeRtspPacket(_parser);
        _parser.Clear();
    }
    return ret;
}

onRecvContent

void RtspSplitter::onRecvContent(const char *data, size_t len) {
    PrintD("onRecvContent%s", data);
    _parser.setContent(string(data,len));
    onWholeRtspPacket(_parser);
    _parser.Clear();
}

RtspSession

UML

在这里插入图片描述

数据结构

has a RtspMediaSourceImp

    //是否已经触发on_play事件
    bool _emit_on_play = false;
    bool _send_sr_rtcp[2] = {true, true};
    //断连续推延时
    uint32_t _continue_push_ms = 0;
    //推流或拉流客户端采用的rtp传输方式
    Rtsp::eRtpType _rtp_type = Rtsp::RTP_Invalid;
    //收到的seq,回复时一致
    int _cseq = 0;
    //消耗的总流量
    uint64_t _bytes_usage = 0;
    //ContentBase
    std::string _content_base;
    //Session号
    std::string _sessionid;
    //记录是否需要rtsp专属鉴权,防止重复触发事件
    std::string _rtsp_realm;
    //登录认证
    std::string _auth_nonce;
    //用于判断客户端是否超时
    toolkit::Ticker _alive_ticker;

    //url解析后保存的相关信息
    MediaInfo _media_info;
    //rtsp推流相关绑定的源
    RtspMediaSourceImp::Ptr _push_src;
    //推流器所有权
    std::shared_ptr<void> _push_src_ownership;
    //rtsp播放器绑定的直播源
    std::weak_ptr<RtspMediaSource> _play_src;
    //直播源读取器
    RtspMediaSource::RingType::RingReader::Ptr _play_reader;
    //sdp里面有效的track,包含音频或视频
    std::vector<SdpTrack::Ptr> _sdp_track;

    RTP over udp
    //RTP端口,trackid idx 为数组下标
    toolkit::Socket::Ptr _rtp_socks[2];
    //RTCP端口,trackid idx 为数组下标
    toolkit::Socket::Ptr _rtcp_socks[2];
    //标记是否收到播放的udp打洞包,收到播放的udp打洞包后才能知道其外网udp端口号
    std::unordered_set<int> _udp_connected_flags;
    RTP over udp_multicast
    //共享的rtp组播对象
    RtpMultiCaster::Ptr _multicaster;
    RTSP over HTTP  
    //quicktime 请求rtsp会产生两次tcp连接,
    //一次发送 get 一次发送post,需要通过x-sessioncookie关联起来
    std::string _http_x_sessioncookie;
    std::function<void(const toolkit::Buffer::Ptr &)> _on_recv;
    // rtcp 
    //rtcp发送时间,trackid idx 为数组下标
    toolkit::Ticker _rtcp_send_tickers[2];
    //统计rtp并发送rtcp
    std::vector<RtcpContext::Ptr> _rtcp_context;

RtspSplitter override

onRtpPacket

根据RTSP Interleaved Frame的第二个字节channel identifier判断是RTCP还是RTP协议

0x00 Video RTP 0x01Video RTCP 0x02 Audio Rtp 0x03 Audio Rtcp

void RtspSession::onRtpPacket(const char *data, size_t len) {
    uint8_t interleaved = data[1];
    if (interleaved % 2 == 0) {
        auto track_idx = getTrackIndexByInterleaved(interleaved);
        handleOneRtp(track_idx, _sdp_track[track_idx]->_type, _sdp_track[track_idx]->_samplerate, (uint8_t *) data + RtpPacket::kRtpTcpHeaderSize, len - RtpPacket::kRtpTcpHeaderSize); //RtpMultiReceiver::handleOneRtp
    } else {
        auto track_idx = getTrackIndexByInterleaved(interleaved - 1);
        onRtcpPacket(track_idx, _sdp_track[track_idx], data + RtpPacket::kRtpTcpHeaderSize, len - RtpPacket::kRtpTcpHeaderSize);
    }
}

onWholeRtspPacket

handle对应的信令

void RtspSession::onWholeRtspPacket(Parser &parser) {
    string method = parser.Method(); //提取出请求命令字
    _cseq = atoi(parser["CSeq"].data());
    if (_content_base.empty() && method != "GET") {
        _content_base = parser.Url();
        _media_info.parse(parser.FullUrl());
        _media_info._schema = RTSP_SCHEMA;
    }

    using rtsp_request_handler = void (RtspSession::*)(const Parser &parser);
    static unordered_map<string, rtsp_request_handler> s_cmd_functions;
    static onceToken token([]() {
        s_cmd_functions.emplace("OPTIONS", &RtspSession::handleReq_Options);
        s_cmd_functions.emplace("DESCRIBE", &RtspSession::handleReq_Describe);
        s_cmd_functions.emplace("ANNOUNCE", &RtspSession::handleReq_ANNOUNCE);
        s_cmd_functions.emplace("RECORD", &RtspSession::handleReq_RECORD);
        s_cmd_functions.emplace("SETUP", &RtspSession::handleReq_Setup);
        s_cmd_functions.emplace("PLAY", &RtspSession::handleReq_Play);
        s_cmd_functions.emplace("PAUSE", &RtspSession::handleReq_Pause);
        s_cmd_functions.emplace("TEARDOWN", &RtspSession::handleReq_Teardown);
        s_cmd_functions.emplace("GET", &RtspSession::handleReq_Get);
        s_cmd_functions.emplace("POST", &RtspSession::handleReq_Post);
        s_cmd_functions.emplace("SET_PARAMETER", &RtspSession::handleReq_SET_PARAMETER);
        s_cmd_functions.emplace("GET_PARAMETER", &RtspSession::handleReq_SET_PARAMETER);
    });

    auto it = s_cmd_functions.find(method);
    if (it == s_cmd_functions.end()) {
        sendRtspResponse("403 Forbidden");
        throw SockException(Err_shutdown, StrPrinter << "403 Forbidden:" << method);
    }

    (this->*(it->second))(parser);
    parser.Clear();
}

RtpReceiver override

在RtpTrack给包排序的时候会调用

onRtpSorted

if (_push_src) {
    _push_src->onWrite(std::move(rtp), false);//RtspMediaSourceImp::onWrite
} else {
    WarnL << "Not a rtsp push!";
}//rtsp推流相关绑定的源中写

onBeforeRtpSorted

updateRtcpContext

void RtspSession::updateRtcpContext(const RtpPacket::Ptr &rtp)
{
    //send rtcp every 5 second
}

OPTION信令 handleReq_OPTION

服务器下发自己的能力

ANNOUNCE信令 handleReq_ANNOUNCE

ANNOUNCE会回复session_id

发布 BroadcastMediaPublish 广播媒体源注册事件

auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPublish, MediaOriginType::rtsp_push, _media_info, invoker, static_cast<SockInfo &>(*this));

config.h中找到对应的事件,目前WebHook.cpp中installWebHook订阅了该事件

// 收到rtsp/rtmp推流事件广播,通过该事件控制推流鉴权
extern const std::string kBroadcastMediaPublish;
#define BroadcastMediaPublishArgs        //宏定义偷懒不写参数                                                                              \
    const MediaOriginType &type, const MediaInfo &args, const Broadcast::PublishAuthInvoker &invoker, SockInfo &sender

之前的webhook订阅

NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastMediaPublish, [](BroadcastMediaPublishArgs) {
    ...
            invoker("", ProtocolOption());//调用invoker
            return;
        }

onRes

//创建_push_src, ringSize (RtspMediaSourceImp:RtspMediaSource)环形缓存大小默认RTP_GOP_SIZE=512

		//断连后继续推流
		
		SdpParser sdpParser(parser.Content());//解析sdp,同时初始化av_track
        _sessionid = makeRandStr(12);//产生新的session_id
        _sdp_track = sdpParser.getAvailableTrack();

        _rtcp_context.clear();
        for (auto &track : _sdp_track) {
            _rtcp_context.emplace_back(std::make_shared<RtcpContextForRecv>());//对应的每个轨道都初始化rtcp控制
        }

		

		if (!_push_src) {
            _push_src = std::make_shared<RtspMediaSourceImp>(_media_info._vhost, _media_info._app, _media_info._streamid);
            //获取所有权
            _push_src_ownership = _push_src->getOwnership();
            _push_src->setProtocolOption(option);
            _push_src->setSdp(parser.Content());
        }

SETUP信令 handle_setup

一般av会发两次

V

SETUP rtsp://127.0.0.1:554/live/test1/streamid=0 RTSP/1.0
Transport: RTP/AVP/TCP;unicast;interleaved=0-1;mode=record  //recode模式
CSeq: 3
User-Agent: Lavf58.29.100
Session: YEtFIGQP5dCI
//根据客户端要求的协议初始化  RTP/AVP/TCP
//推流或拉流客户端采用的rtp传输方式
Rtsp::eRtpType _rtp_type = Rtsp::RTP_Invalid;

if(2 == sscanf(key_values["interleaved"].data(),"%d-%d",&interleaved_rtp,&interleaved_rtcp)){
                trackRef->_interleaved = interleaved_rtp; //V轨绑定0

A

SETUP rtsp://127.0.0.1:554/live/test1/streamid=1 RTSP/1.0
Transport: RTP/AVP/TCP;unicast;interleaved=2-3;mode=record
CSeq: 4
User-Agent: Lavf58.29.100
Session: YEtFIGQP5dCI

v已经根据客户端协议初始化了,A就确定和v一致了

A轨绑定2

因为客户端是TCP的,

如果是**UDP **unicast,还需要对应轨道分配对应的rtp/rtsp(udp)端口(AV总共4个端口…),用于传输Udp数据包()

如果是MULTICAST组播,和UDP unicast不同,所有的连接公用组播的端口,即一对多(省端口了)

RECORD信令 handleReq_RECORD

录制/开始推流

RTSP/1.0 200 OK
CSeq: 5
Date: Fri, Jul 22 2022 09:12:43 GMT
RTP-Info: url=rtsp://127.0.0.1:554/live/test1/streamid=0,url=rtsp://127.0.0.1:554/live/test1/streamid=1
Server: ZLMediaKit(git hash:305722e,branch:master,build time:Jul 22 2022 02:07:24)
Session: YEtFIGQP5dCI

合并写选项,服务器攒多了包再回ACK给pusher,再发REQ给player。提高服务器性能,加大推拉流测的延迟

    GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS);
    if(mergeWriteMS > 0) {
        //推流模式下,关闭TCP_NODELAY会增加推流端的延时,但是服务器性能将提高
        SockUtil::setNoDelay(getSock()->rawFD(), false);
        //播放模式下,开启MSG_MORE会增加延时,但是能提高发送性能
        setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE);
    }

toPlayer

DESCRIBE信令 handleReq_Describe

客户端请求服务器rtsp sdp信息,在此处做鉴权,分配session

emitOnPlay()
onAuthSuccess()

SETUP信令 handle_setup

pusher的setup

多了Session和Transport且声明自己是推流测(record)

Transport: RTP/AVP/TCP;unicast;interleaved=0-1;mode=record

Session: YEtFIGQP5dCI

回复时对应的ssrc=0

player的setup,声明自己已什么方式拉流

Transport: RTP/AVP/TCP;unicast;interleaved=0-1

?:第一次没有附带describe返回的session,第二次才附带对应的session

回复时带上ssrc=A93E689E。是对应pusher流的引用,拉相同流客户端获得的ssrc是相同的

PLAY信令 handleReq_Play

void RtspSession::handleReq_Play(const Parser &parser)
{
    //判断对应的流和源是否都还在
    //根据Range:npt=0.000-判断对应useGop
    //填充rsp中RTSP对应的RTP-Info:字段
    
    rsp
        
    _play_reader = play_src->getRing()->attach(getPoller(), useGOP);//绑定对应的读取器
    //给play_src源中,对应本线程poller的,read_dispatch添加reader
    _play_reader->setDetachCB()//detach时 
    _play_reader->setReadCB()  //reader   应该怎样读包
}

RtpReceiver/RtpMultiReceiver<2>

    /**
     * 输入数据指针生成并排序rtp包
     * @param index track下标索引
     * @param type track类型
     * @param samplerate rtp时间戳基准时钟,视频为90000,音频为采样率
     * @param ptr rtp数据指针
     * @param len rtp数据指针长度
     * @return 解析成功返回true
     *///RtpMultiReceiver::handleOneRtp
    bool handleOneRtp(int index, TrackType type, int sample_rate, uint8_t *ptr, size_t len) {
        assert(index < kCount && index >= 0);
        //RtpTrack::inputRtp
        return _track[index].inputRtp(type, sample_rate, ptr, len).operator bool();
    }

RtpTrack

UML

在这里插入图片描述

PacketSortor

结构

    //下次应该输出的SEQ
    SEQ _next_seq_out = 0;
    //seq回环次数计数
    size_t _seq_cycle_count = 0;
    //排序缓存长度
    size_t _max_sort_size = kMin;
    //pkt排序缓存,根据seq排序
    std::map<SEQ, T> _pkt_sort_cache_map;
    //回调
    std::function<void(SEQ seq, T &packet)> _cb;

sortPacket

给包排序过程中 会触发 RtspSession::onRtpSorted

    /**
     * 输入并排序
     * @param seq 序列号
     * @param packet 包负载
     */  //PacketSortor::sortPacket
    void sortPacket(SEQ seq, T packet) {
        if (seq < _next_seq_out) {
            if (_next_seq_out < seq + kMax) {
                //过滤seq回退包(回环包除外)
                return;
            }
        } else if (_next_seq_out && seq - _next_seq_out > ((std::numeric_limits<SEQ>::max)() >> 1)) {
            //过滤seq跳变非常大的包(防止回环时乱序时收到非常大的seq)
            return;
        }

        //放入排序缓存
        _pkt_sort_cache_map.emplace(seq, std::move(packet));
        //尝试输出排序后的包
        tryPopPacket();
    }

flush/popIterator

    void flush(){
        //清空缓存
        while (!_pkt_sort_cache_map.empty()) {
            popIterator(_pkt_sort_cache_map.begin());
        }
    }
	//弹出一个包
    void popIterator(typename std::map<SEQ, T>::iterator it) {
        auto seq = it->first;
        auto data = std::move(it->second);
        _pkt_sort_cache_map.erase(it);
        _next_seq_out = seq + 1;//下一个被弹出的包是,这个包的序号+1
        _cb(seq, data);
    }

popIterator !

从_pkt_sort_cache_map中popcache时,就是pusher写的时候,监听的客户端就会收到对应的序号包

    void popIterator(typename std::map<SEQ, T>::iterator it) {
        auto seq = it->first;
        auto data = std::move(it->second);
        _pkt_sort_cache_map.erase(it);
        _next_seq_out = seq + 1;
        _cb(seq, data);
        //RtpTrack::RtpTrack  onRtpSorted(std::move(packet));
        //RtpTrackImp::onRtpSorted  _on_sorted(std::move(rtp));
        //RtpMultiReceiver::RtpMultiReceiver() --> onRtpSorted(std::move(rtp), index);
        //RtspSession::onRtpSorted  --> _push_src->onWrite(std::move(rtp), false);
        //RtspMediaSourceImp::onWrite
    }

inputRtp

RtpPacket::Ptr RtpTrack::inputRtp(TrackType type, int sample_rate, uint8_t *ptr, size_t len)
{
    ...
    //收到一个包后看5s内需不需要发送rtcp
    onBeforeRtpSorted(rtp);//RtpMultiReceiver()->track.setBeforeSorted  RtspSession::onBeforeRtpSorted --> updateRtcpContext(rtp); 
    sortPacket(rtp->getSeq(), rtp); //PacketSortor::sortPacket
}

RtspMediaSourceImp

在这里插入图片描述

重写RtspMediaSource(RingDelegate) onWrite

先解复用rtp,直接代理模式下在写入ringbuffer

onWrite

void onWrite(RtpPacket::Ptr rtp, bool key_pos) override {
    if (_all_track_ready && !_muxer->isEnabled()) {
        //获取到所有Track后,并且未开启转协议,那么不需要解复用rtp
        //在关闭rtp解复用后,无法知道是否为关键帧,这样会导致无法秒开,或者开播花屏
        key_pos = rtp->type == TrackVideo;
    } else {
        //需要解复用rtp
        key_pos = _demuxer->inputRtp(rtp);
        //RtspDemuxer::inputRtp //解复用之后才能判断key_pos
        //H264RtpDecoder::inputRtp
    }
    GET_CONFIG(bool, directProxy, Rtsp::kDirectProxy);
    if (directProxy) {
        //直接代理模式才直接使用原始rtp
        RtspMediaSource::onWrite(std::move(rtp), key_pos);
        //RtspMediaSource::onWrite
    }
}

RtspMediaSource

 * rtsp媒体源的数据抽象
 * rtsp有关键的两要素,分别是sdp、rtp包
 * 只要生成了这两要素,那么要实现rtsp推流、rtsp服务器就很简单了
 * rtsp推拉流协议中,先传递sdp,然后再协商传输方式(tcp/udp/组播),最后一直传递rtp

onWrite

void onWrite(RtpPacket::Ptr rtp, bool keyPos) override {
        _speed[rtp->type] += rtp->size();
        assert(rtp->type >= 0 && rtp->type < TrackMax);
        auto &track = _tracks[rtp->type];
        auto stamp = rtp->getStampMS();
        if (track) {
            track->_seq = rtp->getSeq();
            track->_time_stamp = rtp->getStamp() * uint64_t(1000) / rtp->sample_rate;
            track->_ssrc = rtp->getSSRC();
        }
        if (!_ring) {
            std::weak_ptr<RtspMediaSource> weakSelf = std::dynamic_pointer_cast<RtspMediaSource>(shared_from_this());
            auto lam = [weakSelf](int size) {
                auto strongSelf = weakSelf.lock();
                if (!strongSelf) {
                    return;
                }
                strongSelf->onReaderChanged(size);
            };
            //GOP默认缓冲512组RTP包,每组RTP包时间戳相同(如果开启合并写了,那么每组为合并写时间内的RTP包),
            //每次遇到关键帧第一个RTP包,则会清空GOP缓存(因为有新的关键帧了,同样可以实现秒开)
            _ring = std::make_shared<RingType>(_ring_size, std::move(lam));
            onReaderChanged(0);
            if (!_sdp.empty()) {
                regist();
            }
        }
        bool is_video = rtp->type == TrackVideo;
        PacketCache<RtpPacket>::inputPacket(stamp, is_video, std::move(rtp), keyPos);
    	//PacketCache::inputPacket
    }

onFlush

    /**
     * 批量flush rtp包时触发该函数
     * @param rtp_list rtp包列表
     * @param key_pos 是否包含关键帧
     */
    void onFlush(std::shared_ptr<toolkit::List<RtpPacket::Ptr> > rtp_list, bool key_pos) override {
        //如果不存在视频,那么就没有存在GOP缓存的意义,所以is_key一直为true确保一直清空GOP缓存
        _ring->write(std::move(rtp_list), _have_video ? key_pos : true);
        //RingBuffer::write
    }

RTSP

SdpParser

sdp协议

load

v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
c=IN IP4 127.0.0.1
t=0 0
a=tool:libavformat 58.29.100
m=video 0 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAKKzZQHgCJ+XARAAAAwAEAAADAPA8YMZY,aOvjyyLA; profile-level-id=640028
a=control:streamid=0
m=audio 0 RTP/AVP 97
b=AS:128
a=rtpmap:97 MPEG4-GENERIC/48000/2
a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=119056E500
a=control:streamid=1


void SdpParser::load(const string &sdp)
{
    std::vector<std::string> lines = split(sdp, "\n"); //"\n"分隔
    trim(line); //去掉"\r"
    
    for (auto &track_ptr : _track_vec) //3个 m对应着新的SdpTrack
    {
        for (it = track._attr.find("rtpmap"); it != track._attr.end() && it->first == "rtpmap";)
        {
            else if (3 == sscanf(rtpmap.data(), "%d %15[^/]/%d", &pt, codec, &samplerate))//"96 H264/90000"
            //获取 payload_type,codec,samplerate //负载类型,编码格式,采样率
        }
        
        for (it = track._attr.find("fmtp"); it != track._attr.end() && it->first == "fmtp"; )
        {
            track._fmtp = //packetization-mode=1; sprop-parameter-sets=Z2QAKKzZQHgCJ+XARAAAAwAEAAADAPA8YMZY,aOvjyyLA; profile-level-id=640028
        }
        
        
    }
    
}

推流时服务器调用栈

推流rtp/rtsp调用栈

EventPoller::runLoop
	(*cb)(toPoller(ev.events));
Socket::attachEvent
	strong_self->onRead(strong_sock, is_udp);
Socket::onRead
	_on_read(_read_buffer, (struct sockaddr *)&addr, len);
TcpServer::onAcceptConnection
	strong_session->onRecv(buf);
RtspSession::onRecv
	input(buf->data(), buf->size());
HttpRequestSplitter::input
	_content_len = onRecvHeader(header_ptr, header_size);
RtspSplitter::onRecvHeader
	onRtpPacket(data,len);
RtspSession::onRtpPacket
	rtsp/rtp 
    handleOneRtp
    onRtcpPacket

处理rtp调用栈

RtspSession::onRtpPacket
    handleOneRtp
RtpMultiReceiver::handleOneRtp
    return _track[index].inputRtp(type, sample_rate, ptr, len).operator bool();
RtpTrack::inputRtp
    onBeforeRtpSorted
RtpTrackImp::onBeforeRtpSorted
    _on_before_sorted(rtp);
RtpMultiReceiver()
    onBeforeRtpSorted(rtp, index);
RtpTrack::inputRtp
	onBeforeRtpSorted(rtp);  //执行完毕后sortPacket(rtp->getSeq(), rtp);
RtspSession::onBeforeRtpSorted
    updateRtcpContext(rtp);
RtspSession::updateRtcpContext
	rtcp_ctx->onRtp(...);
RtcpContextForRecv::onRtp
    更新rtcp 对应的_packets/_bytes/_last_rtp_stamp/_last_ntp_stamp_ms
RtpTrack::inputRtp
	sortPacket(rtp->getSeq(), rtp);
PacketSortor
	sortPacket
    //放入排序缓存
    _pkt_sort_cache_map.emplace(seq, std::move(packet));//3398:pkg 3399:pkg
    ...popIterator
    
	_cb(seq, data);
    //RtpTrack::RtpTrack  onRtpSorted(std::move(packet));
    //RtpTrackImp::onRtpSorted  _on_sorted(std::move(rtp));
    //RtpMultiReceiver::RtpMultiReceiver() --> onRtpSorted(std::move(rtp), index);
    //RtspSession::onRtpSorted  --> _push_src->onWrite(std::move(rtp), false);
    //RtspMediaSourceImp::onWrite

解复用

RtspMediaSourceImp::onWrite
	key_pos = _demuxer->inputRtp(rtp);
RtspDemuxer::inputRtp
    return _video_rtp_decoder->inputRtp(rtp, true);
H264RtpDecoder::inputRtp
    auto ret = decodeRtp(rtp);
	// 24 STAP-A 28 FU-A  <24 singleFrame
H264RtpDecoder::decodeRtp
    outputFrame(rtp, _frame);
H264RtpDecoder::outputFrame
    RtpCodec::inputFrame(frame); //写入帧并派发
FrameDispatcher::inputFrame  //和下面的FrameDispatcher 不是一个
    H264Track::inputFrame
H264Track::inputFrame_l
    insertConfigFrame(frame);//插入sps pps
	VideoTrack::inputFrame(frame);
---------------------------------------------------------
FrameDispatcher::inputFrame  //和下面的FrameDispatcher 不是一个
    MediaSink::inputFrame
MediaSink::inputFrame
    auto ret = it->second.first->inputFrame(frame);//MediaSink::addTrack ANOUACE时loadsdp时添加
	 	H264Track::inputFrame
FrameDispatcher::inputFrame 
    MediaSink::inputFrame
FrameWriterInterfaceHelper::inputFrame //和下面的FrameDispatcher 不是一个
    return _writeCallback(frame);      //经历3层 FrameDispatcher::inputFrame
MediaSink::addTrack
    track->addDelegate ---> return onTrackFrame(frame);
MultiMediaSourceMuxer::onTrackFrame
    //MultiMediaSourceMuxer::MultiMediaSourceMuxer初始化时添加
    RtmpMediaSourceMuxer::Ptr _rtmp;
    RtspMediaSourceMuxer::Ptr _rtsp;
    TSMediaSourceMuxer::Ptr _ts;
    MediaSinkInterface::Ptr _mp4;
    HlsRecorder::Ptr _hls;
	--------------------------------------------------------
    if (_rtmp) {
        ret = _rtmp->inputFrame(frame) ? true : ret;
    }
    if (_rtsp) {
        ret = _rtsp->inputFrame(frame) ? true : ret;
    }
    if (_ts) {
        ret = _ts->inputFrame(frame) ? true : ret;
    }
	--------------------------rtmp----------------------------
    RtmpMediaSourceMuxer::inputFrame
    	RtmpMuxer::inputFrame
        	H264RtmpEncoder::inputFrame
    --------------------------rtsp----------------------------
    RtspMediaSourceMuxer::inputFrame
        RtspMuxer::inputFrame
        	H264RtmpEncoder::inputFrame
    --------------------------ts----------------------------
    TSMediaSourceMuxer::inputFrame
        MpegMuxer::inputFrame
    --------------------------hls----------------------------  	
    HlsRecorder::inputFrame
        MpegMuxer::inputFrame
    --------------------------_fmp4----------------------------
    FMP4MediaSourceMuxer::inputFrame
        MP4MuxerMemory::inputFrame
    

在这里插入图片描述

拉流时player调用栈

test_player.cpp

TcpClient::onSockConnect
	strong_self->onRecv(pBuf);
RtspPlayer::onRecv
    input(buf->data(), buf->size());
HttpRequestSplitter::input
    _content_len = onRecvHeader(header_ptr, header_size);
RtspSplitter::onRecvHeader
    onRtpPacket(data,len);
RtspPlayer::onRtpPacket
    handleOneRtp(trackIdx, _sdp_track[trackIdx]->_type,
bool RtpMultiReceiver::handleOneRtp
    return _track[index].inputRtp(type, sample_rate, ptr, len).operator bool();  
RtpTrack::inputRtp
    sortPacket(rtp->getSeq(), rtp);
PacketSortor::sortPacket
    tryPopPacket
    popPacket
    popIterator : _cb(seq, data)
RtpTrack::RtpTrack()
    onRtpSorted(std::move(packet));
RtpTrackImp::onRtpSorted
    _on_sorted(std::move(rtp));
RtpMultiReceiver()
    onRtpSorted(std::move(rtp), index);
RtspPlayer::onRtpSorted
    onRecvRTP(std::move(rtppt), _sdp_track[trackidx]);
RtspPlayerImp::onRecvRTP
    _demuxer->inputRtp(rtp);
RtspDemuxer::inputRtp
    return _video_rtp_decoder->inputRtp(rtp, true);
H264RtpDecoder::inputRtp
    decodeRtp(rtp);
    	singleFrame(rtp, frame, payload_size, stamp);
        	outputFrame(rtp, _frame);
                 RtpCodec::inputFrame(frame);
FrameDispatcher::inputFrame  // RtpCodec::inputFrame的_delegates == 1
    H264Track::inputFrame
    splitH264(frame->data(), ...
    H264Track::inputFrame_l
    VideoTrack::inputFrame
FrameDispatcher::inputFrame // VideoTrack::inputFrame的_delegates == 1 
    FrameWriterInterfaceHelper   ---> return _writeCallback(frame);
    
auto delegate = std::make_shared<FrameWriterInterfaceHelper>//test_player.cpp  自己实现的FFmpegDecoder
     return decoder->inputFrame(frame, false, true);
	addDecodeTask(frame->keyFrame(), [this, live, frame_cache, enable_merge]() //把帧放入队列里去异步解析
              	{
                  	inputFrame_l(frame_cache, live, enable_merge);
              	});
//异步线程
------------------------------------onDecode----------------------------------------
   	//老套路inputFrame_l中 调ffmpeg avcodec_send_packet/avcodec_receive_frame 解出帧了
    onDecode(out_frame);
    decoder->setOnDecode([displayer](const FFmpegFrame::Ptr &yuv)//test_player.cpp
    //把解出的帧展示成画面

总结

  • sdp协议的作用:parser解析sdp字段,sscanf用法

  • RTSP Interleaved Frame 区分 rtp/rtcp

  • 怎么完成对包的排序的PacketSortor

  • rtp udp/tcp/组播 怎么处理的

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值