RTMP协议播放流程的实现及抓包分析

转载:https://blog.csdn.net/DaveBobo/article/details/76557596   多谢版主
协议过滤的时候使用的是 rtmpt,而不是rtmp

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。
源代码:
[cpp]  view plain  copy
  1. #include <stdio.h>    
  2. #include "librtmp/rtmp_sys.h"    
  3. #include "librtmp/log.h"    
  4.     
  5. int InitSockets()    
  6. {    
  7.     WORD version;    
  8.     WSADATA wsaData;    
  9.     version = MAKEWORD(1, 1);    
  10.     return (WSAStartup(version, &wsaData) == 0);    
  11. }    
  12.     
  13. void CleanupSockets()    
  14. {    
  15.     WSACleanup();    
  16. }    
  17.     
  18. int main(int argc, char* argv[])    
  19. {    
  20.     InitSockets();    
  21.         
  22.     double duration=-1;    
  23.     int nRead;    
  24.     //is live stream ?    
  25.     bool bLiveStream=true;                  
  26.         
  27.         
  28.     int bufsize=1024*1024*10;               
  29.     char *buf=(char*)malloc(bufsize);    
  30.     memset(buf,0,bufsize);    
  31.     long countbufsize=0;    
  32.         
  33.     FILE *fp=fopen("receive.flv","wb");    
  34.     if (!fp){    
  35.         RTMP_LogPrintf("Open File Error.\n");    
  36.         CleanupSockets();    
  37.         return -1;    
  38.     }    
  39.         
  40.     /* set log level */    
  41.     //RTMP_LogLevel loglvl=RTMP_LOGDEBUG;    
  42.     //RTMP_LogSetLevel(loglvl);    
  43.     
  44.     RTMP *rtmp=RTMP_Alloc();    
  45.     RTMP_Init(rtmp);    
  46.     //set connection timeout,default 30s    
  47.     rtmp->Link.timeout=10;       
  48.     // HKS's live URL    
  49.     if(!RTMP_SetupURL(rtmp,"rtmp://live.hkstv.hk.lxdns.com/live/hks"))    
  50.     {    
  51.         RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n");    
  52.         RTMP_Free(rtmp);    
  53.         CleanupSockets();    
  54.         return -1;    
  55.     }    
  56.     if (bLiveStream){    
  57.         rtmp->Link.lFlags|=RTMP_LF_LIVE;    
  58.     }    
  59.         
  60.     //1hour    
  61.     RTMP_SetBufferMS(rtmp, 3600*1000);          
  62.         
  63.     if(!RTMP_Connect(rtmp,NULL)){    
  64.         RTMP_Log(RTMP_LOGERROR,"Connect Err\n");    
  65.         RTMP_Free(rtmp);    
  66.         CleanupSockets();    
  67.         return -1;    
  68.     }    
  69.     
  70.     if(!RTMP_ConnectStream(rtmp,0)){    
  71.         RTMP_Log(RTMP_LOGERROR,"ConnectStream Err\n");    
  72.         RTMP_Close(rtmp);    
  73.         RTMP_Free(rtmp);    
  74.         CleanupSockets();    
  75.         return -1;    
  76.     }    
  77.     
  78.     while(nRead=RTMP_Read(rtmp,buf,bufsize)){    
  79.         fwrite(buf,1,nRead,fp);    
  80.     
  81.         countbufsize+=nRead;    
  82.         RTMP_LogPrintf("Receive: %5dByte, Total: %5.2fkB\n",nRead,countbufsize*1.0/1024);    
  83.     }    
  84.     
  85.     if(fp)    
  86.         fclose(fp);    
  87.     
  88.     if(buf){    
  89.         free(buf);    
  90.     }    
  91.     
  92.     if(rtmp){    
  93.         RTMP_Close(rtmp);    
  94.         RTMP_Free(rtmp);    
  95.         CleanupSockets();    
  96.         rtmp=NULL;    
  97.     }       
  98.     return 0;    
  99. }    
二 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:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值