我们所知道 SRT 是由 Haivision 和 Wowza 开发的开源视频流协议。很多人会认为在不久的将来,它被是 RTMP 的替代品。因为 RTMP 协议安全性稍低,延迟相对较高 ,而相对于 SRT 协议支持高质量、稳定性、亚秒级延迟、强大的编解码器支持。SRT 被许多行业专家认为是视频流的新协议。SRT 究竟是什么?

什么是 SRT?

安全可靠传输 (SRT) 是一种开源数据传输协议。SRT 使用用户数据报协议 (UDP),旨在通过公共互联网发送高质量视频,因此该协议是音频和视频流的最佳选择。

在许多主要的开源技术 Wireshare、FFMpeg 中,应用了 SRT 安全可靠传输协议。

SRT 的应用在哪些领域?

SRT 协议主要的应用在直播、多流、视频编码、网关等领域。在技术方面,它提供类似于传输控制协议 (TCP) 的可靠传输。 然而,使用 UDP 协议作为底层传输层。

SRT 还支持低延迟(默认为 120 毫秒)的数据包恢复和使用高级加密标准 (AES) 的加密。

简而言之,通过 SRT,端到端流安全、视频弹性和基于网络条件的实时动态端点调整成为可能。

高质量视频传输

SRT 可以更轻松地通过互联网协议 (IP) 以低端到端延迟进行流式传输。截至目前,低延迟流媒体的协议偏好很少。

这是因为通过公共互联网流式传输可能会造成数据包丢失和抖动等障碍。 SRT 提供解决此问题的方法。

此外,该协议还包括防止数据包丢失、抖动和带宽波动的保护。这意味着如果网络状况不稳定,您的流可能会停止。但它几乎可以立即从这种丢包中恢复,您的观众在观看时几乎不会注意到任何问题。

其他有益于直播的功能包括:

1、 基于时间戳的数据包传输,通过源时间传输实现更好的延迟控制

2、 控制发送者的速度

3、 防止丢包未及时恢复造成丢包

4、数据包重传的定期 NAK 报告

SRT 如何更好的保护你的视频流

如果您使用 SRT 协议流式传输视频,您肯定会受益于它的优势。 该协议保护您的视频流,并确保所有数据在发送时都经过加密。 它还消除了特殊互联网连接的负担,因为该协议可保证您交付的视频内容的质量。

SRT 通过提供可确保安全传输即使是最高级别的产品的加密技术而闻名。 SRT 可以启用端到端 AES 128/256 位加密算法,这是任何需要保护的内容的理想选择。 即使在不可靠的 WiFi 或蜂窝连接导致带宽波动期间,SRT 也能防止视频抖动和数据包丢失,可保护您的视频内容免遭分发。

SRT 数据包

下面我们要对 SRT 协议要做进一步分析。

根据上图的红色框起来的方格 F:

F=0 ;Data Packet

Data (content to transmit)

Filtering packet (FEC)

F=1;Control Packet

HANDSHAKE

KEEPALIVE

ACK

NAK (Loss Report)

SHUTDOWN

ACKACK

SRT 流媒体传输协议握手过程

caller 作为连接的发起者,知道对应设置 Listener 模式设备的公网 IP 地址及其监听的 UDP 端口。而 Listener 监听发起的 SRT 请求,需要知道使用哪个 UDP 端口,并在这个端口一直监听。

Caller-Listener Handshake

caller 发起建立一个点对点传输的 SRT 连接,Listener 监听发起 SRT 会话的请求。

Rendezvous Handshake

Rendezvous 两端共同协商建立连接,基本不使用此种连接。

SRT 在快速连接方面有明显优势,两次握手成功即可建连;简单了明白了握手过程,接来就是 SRT 协议解析了。

SRT 协议解析及报文识别

下面我们对 SRT 协议进行解析。

/* 实际解析数据包的代码 
 *
 */
 static void dissect_srt_control_packet(u_char *data_info,int PayloadLen)
 {
     int offset = 0;      offset += 4;      if (data_info[0] == 0x80 && data_info[1] == 0x02)/*UMSG_ACK*/
     {         int ack_number = ntohl(*(uint32_t*)(data_info + offset));
        printf("ACK Number: %d\n",ack_number);         offset += 4; /*Time Stamp*/         int time_stamp = ntohl(*(uint32_t*)(data_info + offset));
        printf("Time Stamp: %d\n",time_stamp);         offset += 4; /*Destination Socket ID*/         int dst_sock_id = ntohl(*(uint32_t*)(data_info + offset));
        printf("Destination Socket ID: %d\n",dst_sock_id);         offset += 4; /*ACKD_RCVLASTACK*/         int ack_rcv = ntohl(*(uint32_t*)(data_info + offset));
        printf("ACKD_RCVLASTACK: %d \n",ack_rcv);         offset += 4; /*ACKD_RTT*/         int ack_rtt = ntohl(*(uint32_t*)(data_info + offset));
        printf("ACKD_RTT: %d us \n",ack_rtt);         offset += 4; /*ACKD_RTTVAR*/         int ack_rttvar = ntohl(*(uint32_t*)(data_info + offset));
        printf("ACKD_RTTVAR: %d us \n",ack_rttvar);         offset += 4; /*ACKD_BUFFERLEFT*/         int ack_buffer= ntohl(*(uint32_t*)(data_info + offset));
        printf("ACKD_BUFFERLEFT: %d pkts \n",ack_buffer);         offset += 4; /*ACKD_RCVSPEED*/         int ack_rcvspeed= ntohl(*(uint32_t*)(data_info + offset));
        printf("ACKD_RCVSPEED: %d pkts/s \n",ack_rcvspeed);         offset += 4; /*ACKD_BANDWIDTH*/         int ack_banwidth= ntohl(*(uint32_t*)(data_info + offset));
        printf("ACKD_BANDWIDTH: %d pkts/s \n",ack_banwidth);         offset += 4; /*ACKD_RCVRATE*/         int ack_rcvate= ntohl(*(uint32_t*)(data_info + offset));
        printf("ACKD_RCVRATE: %d pkts/s \n",ack_rcvate);      }
     else if (data_info[0] == 0x80 && data_info[1] == 0x00)/*UMSG_HANDSHAKE*/
     {
        char ipbuf[IP_BUFFER_SIZE];
        const int final_length = PayloadLen;
        int baselen = 64;
        offset += 12;
        const int version = ntohl(*(uint32_t*)(data_info + offset));
        /*包含握手版本(当前为4或5) */
        printf("Handshake version:%d\n",version);         offset += 2; /*Encryption Field*/         offset += 2; /*Extended Field*/         offset += 4; /*Initial Sequence Number*/         int srt_handshake_isn= ntohl(*(uint32_t*)(data_info + offset));
        printf("Initial Sequence Number: %d\n",srt_handshake_isn);         offset += 4; /*MTU*/         int srt_handshake_mtu= ntohl(*(uint32_t*)(data_info + offset));
        printf("MTU: %d \n",srt_handshake_mtu);         offset += 4; /*Flow Window*/         int srt_handshake_flow_window= ntohl(*(uint32_t*)(data_info + offset));
        printf("Flow Window: %d\n",srt_handshake_flow_window);         offset += 4; /*Hanshake Type*/         int srt_handshake_reqtype= ntohl(*(uint32_t*)(data_info + offset));
        printf("Hanshake Type: %d\n",srt_handshake_reqtype);         offset += 4; /*Socket ID*/         int srt_handshake_id= ntohl(*(uint32_t*)(data_info + offset));
        printf("Socket ID: %d\n",srt_handshake_id);         offset += 4; /*SYN Cookie*/         int srt_handshake_cookie= ntohl(*(uint32_t*)(data_info + offset));
        printf("SYN Cookie: %d\n",srt_handshake_cookie);         offset += 4; /*Peer IP Address*/         srt_format_ip_address(ipbuf, sizeof ipbuf,strdup((const char*)(data_info+offset)));         printf("Peer IP Address: %s\n",ipbuf);         if (final_length > baselen)
        {             /* 提取SRT握手扩展块
             并相应地增加baselen。 
            */
            int begin = baselen;             for (;;)
            {
                const uint16_t blockid  = ntohs(*(uint16_t*)(data_info + begin));                 begin += 2;
                const uint16_t blocklen = ntohs(*(uint16_t*)(data_info + begin));                 // Shift to the payload
                begin += 2;                 switch (blockid)
                {
                    case SRT_CMD_HSREQ:
                    case SRT_CMD_HSRSP:
                        if (blocklen == 3)
                        {
                            //uint32_t version = 0;
                            const int vmajor = (data_info[begin+1]) & 0xff;
                            const int vminor = (data_info[begin+2]) & 0xff;
                            const int vpatch = data_info[begin+3] & 0xff;
                            printf("SRT HS Extension type:%d \n",blockid);
                            printf("SRT HS Extension size:%d \n",blocklen);
                            printf("SRT Version(%d.%d.%d)\n", vmajor, vminor, vpatch);                         }
                        else
                        {                         }
                        break;                     case SRT_CMD_KMREQ:
                    case SRT_CMD_KMRSP:
                        // Rely on the extracted blocklen
                        //srt_format_kmx(tree, tvb, begin, blocklen*4);
                        break;                     case SRT_CMD_SID:
                        break;                     case SRT_CMD_CONJESTCTRL:
                        break;                     default:
                        printf( "Ext Type value is %u\n",blockid);
                        break;
                }                 /* Move the index pointer past the block and repeat. */
                begin += blocklen * 4;                 /* OK, once one block is done, interrupt the loop. */
                if (begin >= final_length)
                    break;
            }
            baselen = begin;
        }      }
     else
     {      }  } static void dissect_srt(u_char *data_info,int PayloadLen)
{
    /* Other misc. local variables. */
    bool is_control = 0;     /*必须至少有24个捕获的字节才能进行检查 */
    if (PayloadLen < 24)
        return ;      
    printf("SrtHdr 0x%.2X,0x%.2X,0x%.2X,0x%.2X\n",data_info[0],data_info[1],data_info[2],data_info[3]);      if ((data_info[0] == 0x80 && data_info[1] == 0x00 && data_info[2] == 0x00 && data_info[3] == 0x00)
    || (data_info[0] == 0x80 && data_info[1] == 0x02 && data_info[2] == 0x00 && data_info[3] == 0x00)/*UMSG_ACK*/
    || (data_info[0] == 0x80 && data_info[1] == 0x06 && data_info[2] == 0x00 && data_info[3] == 0x00)/*UMSG_ACKACK*/
    )
    {
        is_control = true;
    }     if (is_control)
    {         dissect_srt_control_packet(data_info,PayloadLen);
    }
    else
    {
        /*srt data type*/
    }
}

编译运行:

这里把 srt 协议识别出来,并且解析各个字段。

对比 RTMP 和 SRT 协议特点

RTMP 是实时消息协议,它保持持久、稳定的连接并允许低延迟通信。RTMP 协议的另一个缺点是可能由于带宽低而中断,直到您的流可能根本无法启动。添加到缺点列表中,由于交付视频的安全性低,一些严密的防火墙可能不允许 RTMP 连接。虽然,我们不得不说这种情况很少发生。

RTMP 协议目前使用 H.264 视频编解码器和 AAC 音频编解码器,它们相当陈旧,不能提供最佳质量。

最后总结一下 RTMP 优点及缺点:

优点:多播支持、低缓冲、宽平台支持。

缺点:旧的编解码器,安全性稍低,延迟相对较高。

SRT 是安全可靠传输协议,SRT 是由 Haivision 和 Wowza 开发的开源视频流协议。在不久的将来,它被广泛认为是 RTMP 的替代品。共享相同的优势,SRT 正在迈出下一步,使亚秒级延迟的稳定直播的梦想成为现实。它允许您通过次优网络直播您的内容。然而,一个很大的缺点是播放选项不可用。

SRT 可以保护您的实时视频免受抖动、带宽波动和数据包丢失的影响。此外,在亚秒级延迟方面,SRT 与 FTL 和 WebRTC 类似,可以实现近乎实时的通信。

此外,还声明该协议与编解码器无关,这意味着它支持任何现代视频和音频编解码器。

说了这么多,SRT 优点及缺点分别是:

优点:高质量、稳定性、亚秒级延迟、强大的编解码器支持。

缺点:平台支持弱,无法播放。

总结

如果您使用 SRT 协议流式传输视频,您肯定会受益于它的优势。 该协议保护您的视频流,并确保所有数据在发送时都经过加密。 它还消除了特殊互联网连接的负担,因为该协议可保证您交付的视频内容的质量。

音频和视频流最佳选择?SRT 协议解析及报文识别的更多相关文章

  1. 视音频数据处理入门:UDP-RTP协议解析

    ===================================================== 视音频数据处理入门系列文章: 视音频数据处理入门:RGB.YUV像素数据处理 视音频数据处理 ...

  2. AOSP中的HLS协议解析

    [时间:2018-04] [状态:Open] [关键词:流媒体,stream,HLS, AOSP, 源码分析,HttpLiveSource, LiveSession,PlaylistFetcher] ...

  3. SOCKS5 协议解析

    代理 根据 HTTP 1.1 的定义,proxy 是: An intermediary program which acts as both a server and a client for the ...

  4. 修改wireshark协议解析规则

    不同的协议有不同的解码器,wireshark尝试为每个包尝试找到正确的解码器,特定的情况有可能会选择错误的解码器. 1.使用了其它协议的标准端口,被错误解码,使用udp的80端口发送数据被当作QUIC ...

  5. 视音频数据处理入门:FLV封装格式解析

    ===================================================== 视音频数据处理入门系列文章: 视音频数据处理入门:RGB.YUV像素数据处理 视音频数据处理 ...

  6. nodejs 实践:express 最佳实践(四) express-session 解析

    nodejs 实践:express 最佳实践(四) express-session 解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs ...

  7. 协议解析Bug分析

    协议解析Bug分析 源自邮件协议RPC(远程过程调用)处理的Request请求数据包的bug.        一.Bug描写叙述 腾讯收购的Foxmailclient能够作为outlookclient ...

  8. ts 协议解析

    pes : http://wenku.baidu.com/link?url=KjcA0qXqZ1bWVQTa8i1YOmygofldSQL7Pjj-zGRw1e_6_LFmVLo5DIWF0SNwVn ...

  9. [转]netty对http协议解析原理

    本文主要介绍netty对http协议解析原理,着重讲解keep-alive,gzip,truncked等机制,详细描述了netty如何实现对http解析的高性能. 1 http协议 1.1 描述 标示 ...

  10. twemproxyRedis协议解析探索——剖析twemproxy代码正编

    这篇文章会对twemproxyRedis协议解析代码部分进行一番简单的分析,同时给出twemproxy目前支持的所有Redis命令.在这篇文章开始前,我想大家去简单地理解一下有限状态机,当然不理解也是 ...

随机推荐

  1. secureCRT设置配色方案

    https://blog.csdn.net/qq_42672770/article/details/81301494

  2. 2021版idea关于translation插件翻译报错

    cn.yiiguxing.plugin.translate.trans.TranslateException: Google 翻译 :: 翻译失败: 未知错误 这是idea拉下来的报错信息,trans ...

  3. labuladong数据结构

    缓存淘汰算法:LRU①.LFU② BST③ 完全二叉树④ 序列化和反序列化二叉树⑤ 最近公共祖先⑥ 单调栈⑦ 单调队列⑧ 递归反转链表⑨ k个一组反转链表

  4. golang 生成Sqlserver数据表实体

    最近开始学习golang,公司原来 很多项目都 Sqlserver数据库的,世面上很多文章,都是关于Mysql的,自己参考了一个博主的文章,整了一个生成Sqlserver实体的小工具分享一下,能给个星 ...

  5. 【Linux】Ubuntu随笔

    Ubuntu声明环境变量时使用 export JAVA_HOME=/xx/xx/xx,当需要引用时要写成 $JAVA_HOME 所以配置环境变量并声明方法如下: vim ~/.bashrc expor ...

  6. vue3 ts vite

    npm init vite@latest npm install -D sassnpm i vantnpm i vite-plugin-style-import@1.4.1 -Dvite.config ...

  7. C++ PTA 小于m的最大的10个素数

    7-5 小于m的最大的10个素数 (15分) 给定一个整数m(50<m<20000),找出小于m的最大的10个素数. 输入格式: 输入在一行中给出一个正整数m(50<m<200 ...

  8. List的初始化方式

    在LinkedIn考试考到了,很遗憾没考过,特意记录一下,下次再战!文章不是我写的,看到别人的总结,发在我这里自己看看 在 Java 中几种初始化 List 的方法: 1. List<Strin ...

  9. Python - XSS-Attribute

    参考资料:https://owasp-skf.gitbook.io/asvs-write-ups/cross-site-scripting-attribute-xss-attribute/kbid-3 ...

  10. C语言程序设计基础 实验3 函数

    C语言程序设计基础 实验3 函数   一.实验目的 1. 理解函数的本质:模块化,实现代码复用 2. 掌握函数定义.声明.调用的语法 3. 理解并掌握函数的形参.实参,以及函数调用和返回的过程 4. ...