RTMP協議播放流程的實現及抓包分析
實時流協議(Real-TimeMessaging Protocol,RTMP)是用於網際網路上傳輸視音訊資料的網路協議。本API提供了支援RTMP, RTMPT,RTMPE, RTMP RTMPS以及以上幾種協議的變種(RTMPTE, RTMPTS)協議所需的大部分客戶端功能以及少量的伺服器功能。RTMP是目前各種網路直播應用最核心的傳輸協議,也是互動直播採用最廣泛的協議。
RTMP協議規定,播放一個流媒體有兩個前提步驟:第一步,建立一個網路連線(NetConnection);第二步,建立一個網路流(NetStream)。其中,網路連線代表伺服器端應用程式和客戶端之間基礎的連通關係。網路流代表了傳送多媒體資料的通道。伺服器和客戶端之間只能建立一個網路連線,但是基於該連線可以建立很多網路流。播放一個RTMP協議的流媒體需要經過以下幾個步驟:握手,建立連線,建立流,播放。RTMP連線都是以握手作為開始的。建立連線階段用於建立客戶端與伺服器之間的“網路連線”;建立流階段用於建立客戶端與伺服器之間的“網路流”;播放階段用於傳輸視音訊資料。
一 RTMP儲存為FLV
使用librtmp接收RTMP流的函式執行流程圖如下圖所示。
InitSockets():初始化Socket
RTMP_Alloc():為結構體“RTMP”分配記憶體。
RTMP_Init():初始化結構體“RTMP”中的成員變數。
RTMP_SetupURL():設定輸入的RTMP連線的URL。
RTMP_Connect():建立RTMP連線,建立一個RTMP協議規範中的NetConnection。
RTMP_ConnectStream():建立一個RTMP協議規範中的NetStream。
RTMP_Read():從伺服器讀取資料。
RTMP_Close():關閉RTMP連線。
RTMP_Free():釋放結構體“RTMP”。
CleanupSockets():關閉Socket。
原始碼:
#include <stdio.h>
#include "librtmp/rtmp_sys.h"
#include "librtmp/log.h"
int InitSockets()
{
WORD version;
WSADATA wsaData;
version = MAKEWORD(1, 1);
return (WSAStartup(version, &wsaData) == 0);
}
void CleanupSockets()
{
WSACleanup();
}
int main(int argc, char* argv[])
{
InitSockets();
double duration=-1;
int nRead;
//is live stream ?
bool bLiveStream=true;
int bufsize=1024*1024*10;
char *buf=(char*)malloc(bufsize);
memset(buf,0,bufsize);
long countbufsize=0;
FILE *fp=fopen("receive.flv","wb");
if (!fp){
RTMP_LogPrintf("Open File Error.\n");
CleanupSockets();
return -1;
}
/* set log level */
//RTMP_LogLevel loglvl=RTMP_LOGDEBUG;
//RTMP_LogSetLevel(loglvl);
RTMP *rtmp=RTMP_Alloc();
RTMP_Init(rtmp);
//set connection timeout,default 30s
rtmp->Link.timeout=10;
// HKS's live URL
if(!RTMP_SetupURL(rtmp,"rtmp://live.hkstv.hk.lxdns.com/live/hks"))
{
RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n");
RTMP_Free(rtmp);
CleanupSockets();
return -1;
}
if (bLiveStream){
rtmp->Link.lFlags|=RTMP_LF_LIVE;
}
//1hour
RTMP_SetBufferMS(rtmp, 3600*1000);
if(!RTMP_Connect(rtmp,NULL)){
RTMP_Log(RTMP_LOGERROR,"Connect Err\n");
RTMP_Free(rtmp);
CleanupSockets();
return -1;
}
if(!RTMP_ConnectStream(rtmp,0)){
RTMP_Log(RTMP_LOGERROR,"ConnectStream Err\n");
RTMP_Close(rtmp);
RTMP_Free(rtmp);
CleanupSockets();
return -1;
}
while(nRead=RTMP_Read(rtmp,buf,bufsize)){
fwrite(buf,1,nRead,fp);
countbufsize+=nRead;
RTMP_LogPrintf("Receive: %5dByte, Total: %5.2fkB\n",nRead,countbufsize*1.0/1024);
}
if(fp)
fclose(fp);
if(buf){
free(buf);
}
if(rtmp){
RTMP_Close(rtmp);
RTMP_Free(rtmp);
CleanupSockets();
rtmp=NULL;
}
return 0;
}
二 RTMP協議播放流程抓包分析
1 Wireshark抓RTMP包
用wireshark抓取RTMP包,開啟如下:
2 握手(Handshake)
一個RTMP連線以握手開始,我們先看下圖:
首先我們要明確的是客戶端IP是192.168.1.50(我的電腦),192.168.1.123是RTMP伺服器。
劇本應該是這樣子的:
1.RTMP協議是TCP協議的上層協議,所以必須要先建立TCP連線,所以就看到了1-4這幾個TCP三次握手的包。
2.客戶端向伺服器傳送C0塊(chunks),表示要和伺服器握手,C0中包含版本號。
3.伺服器收到C0後,檢查C0中的版本是否支援,如果支援傳送S0作為響應,否則應該終止連線。
4.客戶端和伺服器都分別等待C1和S1,等待版本確認。
5.客戶端收到S1後傳送C2,伺服器收到C1後傳送S2(確認傳送,測試握手完成。)
然而,協議的實際執行卻不是按照劇本來的(如果按劇本來,延遲就要大大增大了),實際執行是這樣的:
1.RTMP協議是TCP協議的上層協議,所以必須要先建立TCP連線,所以就看到了1-4這幾個TCP三次握手的包。
2.客戶端傳送的是C0+C1塊,直接告訴伺服器我發的版本我自己確認了。
3.伺服器更狠,一個大嘴巴子就抽回來了(傳送S0+S1+S2)。
4.客戶端收到後,傳送C2,握手完成!
附上RTMP協議中的流程圖:
3 建立一個網路連線(NetConnection)
提示:網路連線代表伺服器應用程式和客戶端之間基礎的連通關係
我們接著看抓到的包:
RTMP握手完成後,要建立網路連線。大家都知道一個普通的標準的rtmp流是什麼樣子的?rtmp://IP:PORT/APP/Stream 是不是這樣?
實際劇本是這樣子滴:
1.客戶端在傳送C2的時候,順帶還發了一個請求連線的命令,要求與伺服器應用建立網路連線,這就是RTMP URL中的的Application。soga,是不是恍然大悟?
2.伺服器在收到客戶端傳送的連線請求後傳送如下資訊:
主要是告訴客戶端確認視窗大小,設定節點頻寬,然後伺服器把“連線”連線到指定的應用並返回結果,“網路連線成功”。並且返回流開始的的訊息(Stream Begin 0)。
3.客戶端在收到伺服器發來的訊息後,返回確認視窗大小,此時網路連線建立完成。
協議流程圖:
4 建立一個網路流(NetStream)
提示:網路流代表了傳送多媒體資料的通道。伺服器和客戶端之間只能建立一個網路連線,且多個網路流可以複用這一個網路連線。
接著看抓包:
現在地洞挖好了,就差鋪鐵軌了!
1.客戶端向伺服器傳送請求建立流(createStream)。
2.伺服器收到請求後向客戶端傳送_result(),對建立流的訊息進行響應。此時NetStream建立完成。
協議流程圖:
5 播放
提示:主要功能:傳輸音視訊資料
看抓包:
萬事具備,只欠東風了。
1.客戶端向伺服器傳送播放命令,請求播放stream,並設定Buffer Length 1,3000ms。
2.伺服器收到請求後,向客戶端傳送設定塊大小的協議訊息,並且還附加了一堆其他的訊息一起傳送:
包括 Stream Begin(告知客戶端流ID為0)、NetStream.Play.Start( 告知客戶端播放成功)等。
3. 伺服器向客戶端傳送推流通知,並附帶元資料資訊(解析度、幀率、音訊取樣率、音訊位元速率等等)和視訊、音訊資料。此時客戶端就可以開始正常播放rtmp流了。 協議流程圖:
Reference:
http://blog.csdn.net/leixiaohua1020/article/details/42104893
http://blog.csdn.net/wishfly/article/details/52965787
http://blog.csdn.net/shangmingyang/article/details/50837852
http://blog.chinaunix.net/uid-17102734-id-3986995.html
http://befo.io/306.html
---------------------
作者:DaveBobo
來源:CSDN
原文:https://blog.csdn.net/davebobo/article/details/76557596
版權宣告:本文為博主原創文章,轉載請附上博文連結!