RTMP是Real Time Messaging Protocol(实时消息传输协议)的首字母缩写。该协议基于TCP,是一个协议族,常用在视频直播领域。RTMP协议的默认端口是1935。

学习一个协议最好的方法就是调试其通信过程,期间还可以使用wireshark抓包分析。本人在libRTMP的基础上添加了推流部分,并且使得整个过程变得可调试,学习其协议就变得简单多了。配置好的VS2010可调试的libRTMP工程:https://github.com/jiayayao/librtmp。该工程可以使用VS调试RTMP协议内部的代码,并且对RTMP协议部分做了详细的注释。推流部分参考leixiaohua的blog,RTMP Server可以采用Nginx-RTMP module的方式,搭建RTMP Server过程可以参考:使用nginx+nginx-rtmp-module+ffmpeg搭建流媒体服务器笔记(一)

testRTMP工程是推流客户端,推送一个FLV文件需要经过以下几个步骤:握手,建立连接,建立流,推流。RTMP连接都是以握手作为开始的。建立连接阶段用于建立客户端与服务器之间的“网络连接”;建立流阶段用于建立客户端与服务器之间的“网络流”;推流即按照FLV格式将数据传送至RTMP Server。

一、握手

RTMP握手过程如下:

1. 客户端向服务器发送C0、C1块,服务器收到后发送S0和S1块;

2. 客户端收到S0和S1后,向服务器发送C2块;服务器收到C2块后发送S2块;

3. 客户端和服务器分别收到S2和C2后,握手建立完成。

与HandShake相对应,还有SHandShake函数是服务器部分的握手部分,有兴趣的可以看一下。

二、建立网络连接

三、建立网络流

RTMP_ConnectStream()时接收到的packet的type依次是:

0x05: Set server bindwidth(BW = )
0x06: Set client bindwidth(BW = )
0x01: Set in chunk size() 0x20:Invoke <_result>
(object begin)
(object begin)
Property: <Name: fmsVer, STRING: FMS/,,,>
Property: <Name: capabilities, NUMBER: 31.00>
(object end)
(object begin)
Property: <Name: level, STRING: status>
Property: <Name: code, STRING: NetConnection.Connect.Success>
Property: <Name: description, STRING: Connection succeeded.>
Property: <Name: objectEncoding, NUMBER: 0.00>
(object end)
(object end) 0x20:Invoke <_result>
(object begin)
Property: NULL
(object end) 0x20:Invoke <onStatus>
(object begin)
Property: NULL
(object begin)
Property: <Name: level, STRING: status>
Property: <Name: code, STRING: NetStream.Publish.Start>
Property: <Name: description, STRING: Start publishing>
(object end)
(object end)

服务器接收到“connect”消息后,会返回_result给客户端,客户端接收到是connect的response后,会发送“createStream”命令到服务器。

服务器接收到“createStream”消息后,会返回_result给客户端,客户端接收到是“createStream”命令返回的response后,会发送“publish”命令到服务器。网络流建立完成,开始传送数据。

四、推流

推流部分的关键代码如下:

int publish_using_packet(){
RTMP *rtmp=NULL;
RTMPPacket *packet=NULL;
uint32_t start_time=;
uint32_t now_time=;
//the timestamp of the previous frame
long pre_frame_time=;
long lasttime=;
int bNextIsKey=;
uint32_t preTagsize=; //packet attributes
uint32_t type=;
uint32_t datalength=;
uint32_t timestamp=;
uint32_t streamid=; FILE*fp=NULL;
fp=fopen("cuc_ieschool.flv","rb");
if (!fp){
RTMP_LogPrintf("Open File Error.\n");
CleanupSockets();
return -;
} if (!InitSockets()){
RTMP_LogPrintf("Init Socket Err\n");
return -;
} // 创建一个RTMP会话的句柄
rtmp=RTMP_Alloc();
// 初始化RTMP句柄
RTMP_Init(rtmp);
//set connection timeout,default 30s
rtmp->Link.timeout=;
// 设置URL
if(!RTMP_SetupURL(rtmp,"rtmp://192.168.37.130:1935/myapp/test1"))
{
RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n");
RTMP_Free(rtmp);
CleanupSockets();
return -;
} //if unable,the AMF command would be 'play' instead of 'publish'
RTMP_EnableWrite(rtmp); // RTMP_Connect分为2步:RTMP_Connect0和RTMP_Connect1
// 0负责建立TCP底层连接
// 1负责RTMP握手操作
if (!RTMP_Connect(rtmp,NULL)){
RTMP_Log(RTMP_LOGERROR,"Connect Err\n");
RTMP_Free(rtmp);
CleanupSockets();
return -;
} if (!RTMP_ConnectStream(rtmp,)){
RTMP_Log(RTMP_LOGERROR,"ConnectStream Err\n");
RTMP_Close(rtmp);
RTMP_Free(rtmp);
CleanupSockets();
return -;
} packet=(RTMPPacket*)malloc(sizeof(RTMPPacket));
RTMPPacket_Alloc(packet,*);
RTMPPacket_Reset(packet); packet->m_hasAbsTimestamp = ;
packet->m_nChannel = 0x04;
packet->m_nInfoField2 = rtmp->m_stream_id; RTMP_LogPrintf("Start to send data ...\n"); //jump over FLV Header
// FLV格式的header为9个字节
fseek(fp,,SEEK_SET);
//jump over previousTagSizen
// 跳过表征前一段Tag大小的4个字节
fseek(fp,,SEEK_CUR);
start_time=RTMP_GetTime();
while()
{
if((((now_time=RTMP_GetTime())-start_time)
<(pre_frame_time)) && bNextIsKey){
//wait for 1 sec if the send process is too fast
//this mechanism is not very good,need some improvement
if(pre_frame_time>lasttime){
RTMP_LogPrintf("TimeStamp:%8lu ms\n",pre_frame_time);
lasttime=pre_frame_time;
}
Sleep();
continue;
} //not quite the same as FLV spec
// 读取当前Tag的类型(1个字节)
if(!ReadU8(&type,fp))
break;
// 读取当前Tag data部分的大小(3个字节)
if(!ReadU24(&datalength,fp))
break;
// 读取时间戳(4个字节)
if(!ReadTime(&timestamp,fp))
break;
// 读取stream id(3个字节),一般为0
if(!ReadU24(&streamid,fp))
break; // 跳过既非视频也非音频的Tag
if (type!=0x08&&type!=0x09){
//jump over non_audio and non_video frame,
//jump over next previousTagSizen at the same time
fseek(fp,datalength+,SEEK_CUR);
continue;
} // 读取当前音视频Tag的数据到packet
if(fread(packet->m_body,,datalength,fp)!=datalength)
break; packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
packet->m_nTimeStamp = timestamp;
packet->m_packetType = type;
packet->m_nBodySize = datalength;
pre_frame_time=timestamp; if (!RTMP_IsConnected(rtmp)){
RTMP_Log(RTMP_LOGERROR,"rtmp is not connect\n");
break;
}
// 这样看下来是一个FLV的Tag发送一个RTMPPacket
if (!RTMP_SendPacket(rtmp,packet,)){
RTMP_Log(RTMP_LOGERROR,"Send Error\n");
break;
} // 读取前一个Tag的size
if(!ReadU32(&preTagsize,fp))
break; // 读取当前Tag的type
if(!PeekU8(&type,fp))
break;
if(type==0x09){
if(fseek(fp,,SEEK_CUR)!=)
break;
if(!PeekU8(&type,fp)){
break;
}
if(type==0x17)
bNextIsKey=;
else
bNextIsKey=; fseek(fp,-,SEEK_CUR);
}
} RTMP_LogPrintf("\nSend Data Over\n"); if(fp)
fclose(fp); if (rtmp!=NULL){
RTMP_Close(rtmp);
RTMP_Free(rtmp);
rtmp=NULL;
}
if (packet!=NULL){
RTMPPacket_Free(packet);
free(packet);
packet=NULL;
} CleanupSockets();
return ;
}

在推送过程中,打开VLC播放器,输入网络流地址为:"rtmp://192.168.37.130:1935/myapp/test1",即可看到推流客户端推送的视频。

参考资料:

1. RTMP流媒体播放过程

2. RTMP规范简单分析

调试libRTMP代码来分析RTMP协议的更多相关文章

  1. 从wireshark数据中分析rtmp协议,并提取出H264视频流

    我写的小工具 rtmp_parse.exe 使用用法如先介绍下: -sps  [文件路径] 解析 sps 数据 文件当中的内容就是纯方本的hexstring: 如 42 E0 33 8D 68 05 ...

  2. RTMP协议分析及推流过程

    1.RTMP(实时消息传输协议)是Adobe 公司开发的一个基于TCP的应用层协议. 2.RTMP协议中基本的数据单元称为消息(Message). 3.当RTMP协议在互联网中传输数据的时候,消息会被 ...

  3. rtmp协议分析

    最近需要做一个rtmp服务器,着手分析一下rtmp协议,开干. rtmp握手 这个推荐一篇文章讲解得比较透彻http://blog.sina.com.cn/s/blog_676e11660102v8b ...

  4. C++实现RTMP协议发送H.264编码及AAC编码的音视频

    http://www.cnblogs.com/haibindev/archive/2011/12/29/2305712.html C++实现RTMP协议发送H.264编码及AAC编码的音视频 RTMP ...

  5. C++实现RTMP协议发送H.264编码及AAC编码的音视频(转)

    C++实现RTMP协议发送H.264编码及AAC编码的音视频(转) RTMP(Real Time Messaging Protocol)是专门用来传输音视频数据的流媒体协议,最初由Macromedia ...

  6. 【转】C++实现RTMP协议发送H.264编码及AAC编码的音视频

    RTMP(Real Time Messaging Protocol)是专门用来传输音视频数据的流媒体协议,最初由Macromedia 公司创建,后来归Adobe公司所有,是一种私有协议,主要用来联系F ...

  7. RTMPdump(libRTMP)源代码分析 4: 连接第一步——握手(Hand Shake)

    ===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...

  8. RTMPdump(libRTMP) 源代码分析 10: 处理各种消息(Message)

    ===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...

  9. RTMP协议发送H.264编码及AAC编码的音视频,实现摄像头直播

    RTMP(Real Time Messaging Protocol)是专门用来传输音视频数据的流媒体协议,最初由Macromedia 公司创建,后来归Adobe公司所有,是一种私有协议,主要用来联系F ...

随机推荐

  1. K:汉诺塔问题

    相关介绍:  汉诺塔问题是一个通过隐式使用递归栈来进行实现的一个经典问题,该问题最早的发明人是法国数学家爱德华·卢卡斯.传说印度某间寺院有三根柱子,上串64个金盘.寺院里的僧侣依照一个古老的预言,以上 ...

  2. JQuery图片加载显示loading和加载失败默认图片

    在很多时候我们需要用到图片加载功能,在网上搜资料后就想为了以后使用方便重新整理了下,结果如图: 最后一张是加载失败显示的图片,没找到合适的图片,先用他顶替. 页面引用 <div class=&q ...

  3. NodeList、HTMLCollection和NamedNodeMap

    上篇文章以arguments为例讲到了类数组对象,这篇我们讨论更多的类数组对象NodeList.HTMLCollection和NamedNodeMap.既然是类数组对象,这3种对象也都能应用上篇文章中 ...

  4. 前端面试题1(html)

    HTML     * Doctype作用?严格模式与混杂模式如何区分?它们有何意义? 1.<!DOCTYPE> 声明位于文档中的最前面的位置,处于 <html> 标签之前.此标 ...

  5. Mongodb安装详解及mongochef视图工具安装。

    按照国际惯例我们先来介绍一下MongoDB. MongoDB是一个基于分布式文件存储的数据库,由c++语言编写,为WEB应用提供可扩展的高性能数据存储解决方案.MongoDB属于非关系数据库,也不能说 ...

  6. 文件路径太长无法删除 robocopy

    方法一:新建空文件夹 D:\temp robocopy D:\temp D:\target\source\test  /purge 或者在同一目录下,建一个空文件夹, test 在cmd下切换进入到当 ...

  7. [CentOS]安装软件问题:/lib/ld-linux.so.2: bad ELF interpreter解决

    环境: [orangle@localhost Downloads]$ uname -m&&uname -r x86_64 2.6.32-220.el6.x86_64 [orangle@ ...

  8. 利用Vagrant完成开发环境配置

    作者:astaxie链接:https://github.com/astaxie/go-best-practice/blob/master/ebook/zh/01.0.md著作权归作者所有.商业转载请联 ...

  9. select server

    server with select #include<stdio.h> #include<sys/types.h> #include<sys/socket.h> ...

  10. 装饰器( decorate )

    装饰器分步解释-形成过程: #-*- coding: UTF-8 -*- #示例1: def deco(p_args): def pack(): print('haha,i am deco fun') ...