RTMP服務器搭建可參考:
Nginx與Nginx-rtmp-module搭建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;
-
-
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;
-
}
-
-
-
-
-
-
RTMP *rtmp=RTMP_Alloc();
-
RTMP_Init(rtmp);
-
-
rtmp->Link.timeout=10;
-
-
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;
-
}
-
-
-
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: