目录
概述:
rtmp播放流程
代码示例:
RTMP协议是Real Time Message Protocol(实时信息传输协议)的缩写,它是由Adobe公司提出的一种应用层的协议,用来解决多媒体数据传输流的多路复用(Multiplexing)和分包(packetizing)的问题。
RTMP协议是应用层协议,是要靠底层可靠的传输层协议(通常是TCP)来保证信息传输的可靠性的。在基于传输层协议的链接建立完成后,RTMP协议也要客户端和服务器通过“握手”来建立基于传输层链接之上的RTMP Connection链接。
RTMP一般在一个TCP通道上传输flv,f4v格式流和命令。
RTSP传输一般需要 2-3 个通道,命令和数据通道分离;TS和MP4。
概念
有效负载:包含在每一个包中的数据,就像音视频样本或压缩后的视频数据。
包: 一个数据包是由固定的包头和有效的负载数据来组成的。
端口:rtmp协议默认使用的是1935端口。
消息流:一个通信的逻辑通道,让消息流通
消息流id:每个消息拥有一个分配的id,标识消息流。
消息块:消息的一个片段,一个完整的消息会被分割成小的片段,每个片段都是一个消息块。
消息块流:一个通信的逻辑通道,允许消息块在一个特定方向流通,例如:从客户端到服务器。
消息块流id:每个消息块有一个分配的id用于识别跟随消息块流。
复合技术:把分开的音视频数据组合成一条音视频流的过程。
逆复合技术:复合的反向过程,交叉存取组装的音视频数据,是他们成为最初的音视频数据。
时间戳:在rtmp消息块中的时间戳使用整数来表示,但是为毫秒。时间戳必须是线性增加的,允许引用程序处理异步传输,带宽度量,检测,流控制。
图1 RTMP协议流程
bilibili pc端直播(libobs26.1.1推流,rtmp)wireshark截取摘要如下:(192.168.1.5本机IP)
122775 2233.876959 192.168.1.5 111.62.90.116 RTMP 1591 Handshake C0+C1
122783 2233.890926 192.168.1.5 111.62.90.116 RTMP 1590 Handshake C2
122784 2233.902499 111.62.90.116 192.168.1.5 RTMP 138 Handshake C2
122787 2233.903620 192.168.1.5 111.62.90.116 RTMP 283 Set Chunk Size 4096|connect('live-bvc')
122790 2233.916266 111.62.90.116 192.168.1.5 RTMP 70 Window Acknowledgement Size 5000000
122794 2233.975749 111.62.90.116 192.168.1.5 RTMP 333 Set Peer Bandwidth 5000000,Dynamic|Set Chunk Size 4096|_result('NetConnection.Connect.Success')
122795 2233.976037 192.168.1.5 111.62.90.116 RTMP 186 releaseStream('?streamname=live_2039896039_16392939&key=519686c99f3b1a4200eab44d83621b9d&schedule=rtmp&pflag=2')
122798 2233.988083 192.168.1.5 111.62.90.116 RTMP 215 FCPublish('?streamname=live_2039896039_16392939&key=519686c99f3b1a4200eab44d83621b9d&schedule=rtmp&pflag=2')|createStream()
122800 2234.000070 111.62.90.116 192.168.1.5 RTMP 95 _result()
122801 2234.000192 192.168.1.5 111.62.90.116 RTMP 191 publish('?streamname=live_2039896039_16392939&key=519686c99f3b1a4200eab44d83621b9d&schedule=rtmp&pflag=2')
122804 2234.067911 111.62.90.116 192.168.1.5 RTMP 171 onStatus('NetStream.Publish.Start')
122805 2234.068238 192.168.1.5 111.62.90.116 RTMP 467 @setDataFrame()音视频编码信息
122819 2234.386047 192.168.1.5 111.62.90.116 RTMP 73 Audio Data
122820 2234.386204 192.168.1.5 111.62.90.116 RTMP 4232 Video Data
122821 2234.386240 192.168.1.5 111.62.90.116 RTMP 4151 Video Data
122822 2234.386264 192.168.1.5 111.62.90.116 RTMP 4151 Video Data
122823 2234.386290 192.168.1.5 111.62.90.116 RTMP 4151 Unknown (0x0)
122824 2234.386322 192.168.1.5 111.62.90.116 RTMP 1506 Unknown (0x0)
122825 2234.398021 111.62.90.116 192.168.1.5 TCP 60 1935 → 59645 [ACK] Seq=3527 Ack=4165 Win=42496 Len=0
122835 2234.398316 192.168.1.5 111.62.90.116 RTMP 24738 Unknown (0x0)
122854 2234.418696 192.168.1.5 111.62.90.116 RTMP 2335 Video Data
122876 2234.452212 192.168.1.5 111.62.90.116 RTMP 4102 Video Data
122880 2234.485544 192.168.1.5 111.62.90.116 RTMP 623 Audio Data
122882 2234.497523 192.168.1.5 111.62.90.116 RTMP 638 Audio Data
523792 2681.306871 192.168.1.5 111.62.90.116 RTMP 4151 Abort Message -684195369|Unknown (0x0)关闭直播
RTMP协议规定,播放一个流媒体有两个前提步骤:第一步,建立一个网络连接(NetConnection);第二步,建立一个网络流(NetStream)。其中,网络连接代表服务器端应用程序和客户端之间基础的连通关系。网络流代表了发送多媒体数据的通道。服务器和客户端之间只能建立一个网络连接,但是基于该连接可以创建很多网络流。播放一个RTMP协议的流媒体需要经过以下几个步骤:握手,建立连接,建立流,播放。RTMP连接都是以握手作为开始的。建立连接阶段用于建立客户端与服务器之间的“网络连接”;建立流阶段用于建立客户端与服务器之间的“网络流”;播放阶段用于传输视音频数据。
图2 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。
图3 建立网络连接
图4 建立网络流播放
#include
#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://live.hkstv.hk.lxdns.com/live/hks
https://www.w3school.com.cn/i/movie.mp4