SRS4.0源代码分析之WebRTC推流端处理

1、目标:

上一节分析了SRS4.0中WebRTC模块的总体架构和软件处理流程。接下来分析SRS4.0 WebRTC模块针对客户端推流连接上各种协议报文的软件处理逻辑。

2、内容:

WebRTC模块在启动过程中: 1、创建SrsUdpMuxListener监听对象,监听指定的UDP端口(默认配置8000端口)。

srs_error_t SrsRtcServer::listen_udp() {  // 创建监听对象
    SrsUdpMuxListener* listener = new SrsUdpMuxListener(this, ip, port);
    listener->listen();  
}
​
srs_error_t SrsUdpMuxListener::listen() {  // bind监听端口,并启动监听协程
    srs_udp_listen(ip, port, &lfd);  
    trd = new SrsSTCoroutine("udp", this, cid);
    trd->start(); 
}

2、启动此对象内部的SrsUdpMuxListener::cycle()协程,从UDP监听端口接收数据。

srs_error_t SrsUdpMuxListener::cycle() { // 此协程从监听端口读取数据
    ......
    SrsUdpMuxSocket skt(lfd);
    while (true) {
        // 以阻塞方式从监听端口读取数据,
        // 在skt.recvfrom内部使用对端地址的port(16bit)+ipv4(32bit)构成一个fast_id_(64bit)
        // 同理,使用对端地址的ipv4+port构成一个字符串类型的peer_id_
        skt.recvfrom(SRS_UTIME_NO_TIMEOUT); 
​
        handler->on_udp_packet(&skt); // 这里实际是调用SrsRtcServer::on_udp_packet()
    }
}
​
int SrsUdpMuxSocket::recvfrom(srs_utime_t timeout){
    nread = srs_recvfrom(lfd, buf, nb_buf, (sockaddr*)&from, &fromlen, timeout);
}

根据上一节的介绍,推流客户端通过API接口完成SDP交换,再从服务器的SDP信息中,获取服务器的IP地址+端口号,并按照WebRTC协议的要求,向服务器端口依次发送各种协议报文,完成客户端与服务器的连接建立、安全认证和RTP报文加密传输。

所以,WebRTC客户端与服务器的连接建立过程中大概涉及四种主要的协议处理

  1. 客户端和服务端通过STUN协议和ICE机制建立连接

  2. 客户端和服务端通过DTLS协议报文完成安全认证并生成SRTP加解密所需的密钥

  3. 客户端和服务端之间通过SRTP算法实现RTP报文的加解密

  4. 客户端和服务端之间通过RTCP报文完成音视频数据的Qos处理

srs_error_t SrsRtcServer::on_udp_packet(SrsUdpMuxSocket* skt) {
    // 查找udp客户端对应的SrsRtcConnection
    session = (SrsRtcConnection*)_srs_rtc_manager->find_by_fast_id(fast_id);
    session = (SrsRtcConnection*)_srs_rtc_manager->find_by_id(peer_id);
    
    // STUN协议报文处理
    if (srs_is_stun((uint8_t*)data, size)) { 
        // ping.decode()内部检查接收到的必须是合法STUN报文,否则返回错误信息
        if ((err = ping.decode(data, size)) != srs_success) {
            return srs_error_wrap(err, "decode stun packet failed");
        }
        return session->on_stun(skt, &ping); 
    }
    
    // RTP协议报文处理
    if (is_rtp_or_rtcp && !is_rtcp) { return session->on_rtp(data, size); }
    
    // RTCP协议报文处理
    if (is_rtp_or_rtcp && is_rtcp) { return session->on_rtcp(data, size); }
    
    // DTLS协议报文处理
    if (srs_is_dtls((uint8_t*)data, size)) { return session->on_dtls(data, size); }
}

3.1 STUN报文格式与Lite-ICE协商

由于IPv4地址不足以及网络架构的原因,一般用户的电脑或手机总是在一个局域网中,通过连接NAT网关接入公网Internet网络。

一般情况下,同一个局域网的设备之间通过私网IP地址进行通信,局域网设备通过NAT网关获取一个公网IP+端口实现与公网服务器之间的通信。

不同局域网中的设备,因为互相之间不知道对端设备的公网IP,所以一般情况下,无法直接通信。

STUN协议简单说,就是让一种私网设备获取自身公网IP地址的方法,它的运行原理很简单,如下所示: 在这里插入图片描述

    1、处于局域网的私网设备向公网STUN服务器发送STUN Binding Request请求报文。

    2、请求报文经过NAT网关时,请求报文中的源IP和源端口号被NAT网关修改为网关出口的公网IP+端口号。

    3、STUN服务器接收到请求报文后,返回一个STUN Binding Response 响应报文,并将服务器所看到的设备公网IP地址+端口信息(这个地址也被称为服务器反射地址server reflex address),放到响应报文的净荷中一起返回给私网设备。

所以,如上过程所示,SRS4.0的WebRTC模块首先要实现一个简单的STUN服务,即接收客户端发送的STUN Binding Request请求报文,并返回一个STUN Binding Response 响应报文。代码如下:

1、SrsStunPacket::decode()函数用于校验客户端发送的Binding Request请求报文是否正确,并得到报文各字段信息

srs_error_t SrsStunPacket::decode(const char* buf, const int nb_buf)
{
    SrsBuffer* stream = new SrsBuffer(const_cast<char*>(buf), nb_buf);
    
    if (stream->left() < 20) { // 校验STUN报文长度一定不能少于20个字节 
        return srs_error_new(ERROR_RTC_STUN, "invalid stun packet, size=%d", stream->size());
    }
    // 按STUN报文格式依次读取各个字段,用于报文格式校验
    message_type = stream->read_2bytes();
    uint16_t message_len = stream->read_2bytes(); // STUN报文除去头部以后的净荷长度
    string magic_cookie = stream->read_string(4);
    transcation_id = stream->read_string(12);
    
    // 如果STUN报文的净荷长度+20字节的STUN报文头不等于UDP数据包长度,则数据包不是STUN报文,直接丢弃
    if (nb_buf != 20 + message_len) { 
        return srs_error_new(ERROR_RTC_STUN, "invalid stun packet, message_len=%d, nb_buf=%d", message_len, nb_buf);
    }
​
    // 按照TLV方式,依次解析STUN报文净荷部分的各个Attributes信息
    while (stream->left() >= 4) {
        uint16_t type = stream->read_2bytes();
        uint16_t len = stream->read_2bytes();
        ...
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值