RTMP协议命令的流程详解
一, RMTP协议的命令的
1, handshake
客户端与服务器的RTMP握手的的流程
①, 客户端发送 C0+C1 规则是
消息头一共是9个字节分别是:
cid 标志是什么命令(一个字节) | 时间戳timestamp(四个字节) | 扩展字节(四个字节) |
---|---|---|
一个字节标志是什么游戏 0X03 表示发送C0+C1 | 四个字节时间戳 ::time(NULL) | 四个字节的 0X00 |
后面在加1537个字节的随机数
代码
// [1+1536]char* c0c1;// [1+1536+1536]char* s0s1s2;// [1536]char* c2;c0c1 = new char[1537];srs_random_generate(c0c1, 1537);// plain text required.SrsStream stream;if ((ret = stream.initialize(c0c1, 9)) != ERROR_SUCCESS) {return ret;}stream.write_1bytes(0x03);stream.write_4bytes((int32_t)::time(NULL));stream.write_4bytes(0x00);
举例
03 61 50 b0 a8 00 00 00 00 75 da 5b 1b 46 d4 74 .aP......u.[.F.t
......
1e 55 4e 8c c2 7b 12 b8 ce 33 4e 75 be 96 36 88 .UN..{...3Nu..6.
eb .
②,服务器端先回复 S0+S1+S2一个字节cid =0X03字段
一共是3073个字节
1 + 1536+1536 = 3073
其中1536 是随机数
③, 客户端发送C2标志
客户端发送C2完成就完成RTMP的握手流程
C2规则发送
cid标志位(1byte) | 时间戳(4byte) | S0+S1+S2+时间戳(4byte) |
---|---|---|
标志什么消息 | 客户端的时间戳 |
代码
int create_c2(){int ret = ERROR_SUCCESS;if (c2) {return ret;}c2 = new char[1536];srs_random_generate(c2, 1536);// timeSrsStream stream;if ((ret = stream.initialize(c2, 8)) != ERROR_SUCCESS) {return ret;}stream.write_4bytes((int32_t)::time(NULL));// c2 time2 copy from s1 这边就是把S0+S1+S2的时间戳赋值过来的的4byteif (s0s1s2) {stream.write_bytes(s0s1s2 + 1, 4);}return ret;}
例子
00 00 00 00 00 00 00 00 36 72 72 32 6f 77 70 2d ........6rr2owp-
....
6e 73 2d 73 73 6c 32 72 36 65 65 69 6c 65 65 73 ns-ssl2r6eeilees
下载RTMP握手流程完成了 下面就要创建流推流
2,RTMP推流命令
typedef struct RTMPChunk {int c_headerSize;int c_chunkSize;char *c_chunk;char c_header[RTMP_MAX_HEADER_SIZE];
} RTMPChunk;typedef struct RTMPPacket {uint8_t m_headerType;uint8_t m_packetType;uint8_t m_hasAbsTimestamp; /* timestamp absolute or relative? */int m_nChannel;uint32_t m_nTimeStamp; /* timestamp */int32_t m_nInfoField2; /* last 4 bytes in a long header */uint32_t m_nBodySize;uint32_t m_nBytesRead;RTMPChunk *m_chunk;char *m_body;
} RTMPPacket;
RTMP header 计算
int
RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue) {const RTMPPacket *prevPacket;uint32_t last = 0;int nSize;int hSize, cSize;char *header, *hptr, *hend, hbuf[RTMP_MAX_HEADER_SIZE], c;uint32_t t;char *buffer, *tbuf = NULL, *toff = NULL;int nChunkSize;int tlen;if (packet->m_nChannel >= r->m_channelsAllocatedOut) {int n = packet->m_nChannel + 10;RTMPPacket **packets = realloc(r->m_vecChannelsOut, sizeof(RTMPPacket *) * n);if (!packets) {free(r->m_vecChannelsOut);r->m_vecChannelsOut = NULL;r->m_channelsAllocatedOut = 0;return FALSE;}r->m_vecChannelsOut = packets;memset(r->m_vecChannelsOut + r->m_channelsAllocatedOut, 0,sizeof(RTMPPacket *) * (n - r->m_channelsAllocatedOut));r->m_channelsAllocatedOut = n;}prevPacket = r->m_vecChannelsOut[packet->m_nChannel];if (prevPacket && packet->m_headerType != RTMP_PACKET_SIZE_LARGE){/* compress a bit by using the prev packet's attributes */if (prevPacket->m_nBodySize == packet->m_nBodySize && prevPacket->m_packetType == packet->m_packetType && packet->m_headerType == RTMP_PACKET_SIZE_MEDIUM){packet->m_headerType = RTMP_PACKET_SIZE_SMALL;}if (prevPacket->m_nTimeStamp == packet->m_nTimeStamp && packet->m_headerType == RTMP_PACKET_SIZE_SMALL){packet->m_headerType = RTMP_PACKET_SIZE_MINIMUM;}last = prevPacket->m_nTimeStamp;}// headerType --> 1 4 6if (packet->m_headerType > 3) /* sanity */{RTMP_Log(RTMP_LOGERROR, "sanity failed!! trying to send header of type: 0x%02x.",(unsigned char) packet->m_headerType);return FALSE;}nSize = packetSize[packet->m_headerType];hSize = nSize;cSize = 0;t = packet->m_nTimeStamp - last;if (packet->m_body) {header = packet->m_body - nSize;hend = packet->m_body;} else {header = hbuf + 6;hend = hbuf + sizeof(hbuf);}if (packet->m_nChannel > 319){cSize = 2;}else if (packet->m_nChannel > 63){cSize = 1;}if (cSize){header -= cSize;hSize += cSize;}if (t >= 0xffffff) {header -= 4;hSize += 4;RTMP_Log(RTMP_LOGWARNING, "Larger timestamp than 24-bit: 0x%x", t);}hptr = header;c = packet->m_headerType << 6;switch (cSize) {case 0:c |= packet->m_nChannel;break;case 1:break;case 2:c |= 1;break;}*hptr++ = c;if (cSize) {int tmp = packet->m_nChannel - 64;*hptr++ = tmp & 0xff;if (cSize == 2)*hptr++ = tmp >> 8;}//时间戳if (nSize > 1){hptr = AMF_EncodeInt24(hptr, hend, t > 0xffffff ? 0xffffff : t);}if (nSize > 4){hptr = AMF_EncodeInt24(hptr, hend, packet->m_nBodySize);*hptr++ = packet->m_packetType;}if (nSize > 8)hptr += EncodeInt32LE(hptr, packet->m_nInfoField2);if (t >= 0xffffff)hptr = AMF_EncodeInt32(hptr, hend, t);nSize = packet->m_nBodySize;buffer = packet->m_body;nChunkSize = r->m_outChunkSize;RTMP_Log(RTMP_LOGDEBUG2, "%s: fd=%d, size=%d", __FUNCTION__, r->m_sb.sb_socket,nSize);/* send all chunks in one HTTP request */if (r->Link.protocol & RTMP_FEATURE_HTTP) {int chunks = (nSize + nChunkSize - 1) / nChunkSize;if (chunks > 1) {tlen = chunks * (cSize + 1) + nSize + hSize;tbuf = malloc(tlen);if (!tbuf)return FALSE;toff = tbuf;}}while (nSize + hSize) {int wrote;if (nSize < nChunkSize)nChunkSize = nSize;RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *) header, hSize);RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *) buffer, nChunkSize);if (tbuf) {memcpy(toff, header, nChunkSize + hSize);toff += nChunkSize + hSize;} else {wrote = WriteN(r, header, nChunkSize + hSize);if (!wrote)return FALSE;}nSize -= nChunkSize;buffer += nChunkSize;hSize = 0;if (nSize > 0) {header = buffer - 1;hSize = 1;if (cSize) {header -= cSize;hSize += cSize;}if (t >= 0xffffff) {header -= 4;hSize += 4;}*header = (0xc0 | c);if (cSize) {int tmp = packet->m_nChannel - 64;header[1] = tmp & 0xff;if (cSize == 2)header[2] = tmp >> 8;}if (t >= 0xffffff) {char *extendedTimestamp = header + 1 + cSize;AMF_EncodeInt32(extendedTimestamp, extendedTimestamp + 4, t);}}}if (tbuf) {int wrote = WriteN(r, tbuf, toff - tbuf);free(tbuf);tbuf = NULL;if (!wrote)return FALSE;}/* we invoked a remote method */if (packet->m_packetType == RTMP_PACKET_TYPE_INVOKE) {AVal method;char *ptr;ptr = packet->m_body + 1;AMF_DecodeString(ptr, &method);RTMP_Log(RTMP_LOGDEBUG, "Invoking %s", method.av_val);/* keep it in call queue till result arrives */if (queue){int txn;ptr += 3 + method.av_len;txn = (int) AMF_DecodeNumber(ptr);AV_queue(&r->m_methodCalls, &r->m_numCalls, &method, txn);}}if (!r->m_vecChannelsOut[packet->m_nChannel])r->m_vecChannelsOut[packet->m_nChannel] = malloc(sizeof(RTMPPacket));memcpy(r->m_vecChannelsOut[packet->m_nChannel], packet, sizeof(RTMPPacket));return TRUE;
}
messgeheader
m_headerType:代表头节点数据有多大了 {1btye}
channel :有可能1byte和2byte
时间戳:3byte也有可能是6byte
①, connect(‘流的名称’)
协议 CID 0X03
|cid(1byte)|timestamp一般4byte有可能8字节|||
参数
app | flashVer | swfUrl | tcUrl | fpad | capabilities | audioCodecs | videoCodecs | videoFunction | pageUrl | objectEncoding |
---|---|---|---|---|---|---|---|---|---|---|
string | string | string | string | bool | int | int | int | int | string | int |
流的名称 | 版本 | rtmp:/ip/live/app |
②, Window Acknowledgemnt Size (滑动窗口的大小)
③,Set Chunk Size (设置RTMP的Chunk最大值)
④,releaseStream(‘流的名称’)
⑤,FCPublish(‘流的名称’)
⑥, createStream()
⑦,publish(‘test’)
⑧,Video Data
⑨,Audio Data
3, RTMP哪流命令
①, connect(‘live’) C->S
②, onBWDone() S->C
③,_checkbw() C->S
④, _result() S->C
⑤, getStreamLength() C->S
⑥, play(‘test’) C->S
⑦,Set buffer Length 1, 3000ms C->S
⑧,Stream Begin 1 S->C
⑨,|RtmpSampleAccess() S->C
⑩, onStatus(‘NetStream.Data.Start’)
⑪, Video Data
RTMP协议命令的流程详解相关推荐
- 域名注册、域名实名认证、域名解析流程详解
1.域名注册流程详解 首先登陆阿里云网站 www.aliyun.com 点击产品,选择域名注册(左下角蓝色字体) 然后来到此页面 在输入框中填入你想要注册的域名产看是否已经被注册 如:shiyansh ...
- MySQL系列---架构与SQL执行流程详解
文章目录 1. 背景 2. 架构体系 2.1 架构图 2.2 模块详解 2.3 架构分层 3. 查询SQL语句执行流程 3.1 连接 3.1.1 MySQL支持的通信协议 3.1.2 通信方式 3.2 ...
- IP协议相关技术终极详解
IP协议相关技术终极详解 DNS域名解析协议 概述 域名的层级关系 域名的解析流程 ARP地址解析协议 概述 为什么需要ARP协议? ARP协议是属于哪一层呢? ARP协议和DNS协议的区别 ARP协 ...
- jenkins插件调用job_Jenkins迁移job插件Job Import Plugin流程详解
Jenkins迁移job插件Job Import Plugin流程详解 由于又开了新机器所以又要重新布置Jenkins从老项目拷贝过来,发现Job Import Plugin 这个插件更新了,和以前的 ...
- ARP协议及ARP欺骗详解
ARP协议及ARP欺骗详解 地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议.主机发送信息时将包含目标IP地址的ARP ...
- U-Boot启动流程详解
参考:U-Boot顶层目录链接脚本文件(u-boot.lds)介绍 作者:一只青木呀 发布时间: 2020-10-23 13:52:23 网址:https://blog.csdn.net/weixin ...
- 推荐系统整体架构及算法流程详解
省时查报告-专业.及时.全面的行研报告库 省时查方案-专业.及时.全面的营销策划方案库 知识图谱在美团推荐场景中的应用实践 搜索场景下的智能实体推荐 机器学习在B站推荐系统中的应用实践 小红书推荐系统 ...
- Linux 下 TC 命令原理及详解<一>
文章目录 1 前言 2 相关概念 3 使用TC 4 创建HTB队列 5 为根队列创建相应的类别 6 为各个类别设置过滤器 7 复杂的实例 Linux 下 TC 命令原理及详解<一> Lin ...
- Springboot启动流程详解
SpringMVC请求流程详解 SpringMVC框架是一个基于请求驱动的Web框架,并且使用了'前端控制器'模型来进行设计,再根据'请求映射规则'分发给相应的页面控制器进行处理. (一)整体流程 每 ...
最新文章
- RunLoop 浅析
- Servlet其实是单例多线程
- Gym 102798A(思维) acm寒假集训日记21/12/31or22/1/1
- 拓端tecdat|R语言用于线性回归的稳健方差估计
- python采集数据搭建小说网站_Python制作爬虫采集小说
- python持久层框架_python网络爬虫——scrapy框架持久化存储
- C++ Primer 5th Edition(英文版)kindle.mobi
- qt界面之comboBox控件的三种基本用法
- Rust语言教程(2) - 从熟悉的部分开始
- swiper设置autoplay不起作用
- 模电笔记 差分放大器1
- 微软VS硬伤 - 未能起用约束集
- DTOJ 4745. 进制转换
- 湖仓一体(Lakehouse)是什么?
- windows+MAC系统使用-快捷键
- 读书有多重要--即可以朝九晚五,又可以浪迹天涯
- java/php/net/pythont员工管理系统设计与实现设计
- VS2003 R6025 RUNTIME ERROR 问题
- vue element-ui导出表格的功能,csv文件格式
- executeBatch报语法错误,分析是inert into的语句后面加了;
热门文章
- OpenCV学习笔记~VS code1.25.0.0环境中配置opencv
- linux kylin使用手册,优麒麟Ubuntu Kylin 18.10中的生物识别管理工具,附使用方法
- 爱尔兰博士后招聘|利默里克大学-广告学
- 话说当下朱仝对众人 水浒
- 通过jar文件运行软件
- 信用卡PK信用贷款,那个更划算?
- arduino yun 京东_收购域名、注册商标,京东的“云布局”终于铺开了!
- 如何从 Power BI 示例中获取数据以供练习
- 交通交警行业相关名词解释说明,主要包含人、车、路三大块的内容
- Python奇妙之旅-出发吧