我們所知道 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 協議流式傳輸視訊,您肯定會受益於它的優勢。 該協議保護您的視訊流,並確保所有資料在傳送時都經過加密。 它還消除了特殊網際網路連線的負擔,因為該協議可保證您交付的視訊內容的質量。