FFmpeg rtmp推流直播
文章目录
- rtmp协议
- RTMP协议组成
- RTMP的握手过程
- RTMP流的创建
- RTMP消息格式
- Chunking(Message 分块)
- rtmp服务器搭建
- Nginx服务器
- 配置Nginx服务器
- librtmp库编译
- 推流
rtmp协议
RTMP(Real Time Messaging Protocol)是由Adobe公司基于Flash Player播放器对应的音视频flv封装格式提出的一种,基于TCP的数据传输协议。本身具有稳定、兼容性强、高穿透的特点。常被应用于流媒体直播、点播等场景。常用于推流方(主播)的稳定传输需求。
RTMP协议组成
RTMP协议主要由以下三个部分组成:
- 握手阶段: 在RTMP连接建立之初,客户端与服务器通过握手过程来确认双方的协议版本以及交换随机数等信息,握手成功后,双方将建立起稳定的连接。
- 消息传输: 在握手成功之后,RTMP协议将音视频数据、命令消息等封装成消息进行传输。RTMP协议支持多种消息类型,包括音频、视频、数据消息、命令消息等。为保证消息的有序传输,RTMP还引入了流ID、消息ID等概念来对消息进行管理。
- 块传输: RTMP协议采用分块传输机制来提高传输效率。将消息划分为一系列较小的块(chunks),每个块的大小可配置。这种分块传输机制可以降低延迟,提高实时性。
RTMP协议的工作原理可概括为以下几个步骤:
- 客户端与服务器建立TCP连接
- 双方通过握手过程确认协议版本及交换随机数等信息
- 客户端发送连接命令(connect)到服务器
- 服务器响应连接命令,返回连接结果
- 客户端与服务器建立流(stream)进行音视频数据传输
- 在传输过程中,双方可以发送控制命令,如播放、暂停等
- 当连接关闭时,双方结束消息传输并断开连接
RTMP的握手过程

RTMP流的创建

RTMP消息格式
RTMP数据单元(Message)是RTMP协议中用于封装音频、视频、命令和数据等信息的基本单位。其结构如图所示:RTMP的消息格式都是由消息头和消息体构成。

在RTMP Header中包含三个部分,基本头(Basic Header),消息头(Message Header)和扩展时间戳(Extended TimeStamp)其中消息头和扩展时间戳是可选的。
Basic Header包含了chunk stream ID(流通道id)和chunk type,chunk stream id一般被简写为CSID,用来唯一标识一个特定的流通道,chunk type决定了后面Message Header的格式。Basic Header的长度可能是1,2或4个字节,其中chunk type的长度是固定的(占2位,单位是bit),Basic Header是变长的,其长度取决于CSID的大小,在足够存储这两个字段的前提下,最好用尽量少的字节从而减少由于引入Header增加的数据量。
RTMP协议最多支持65597个用户定义chunk stream ID,范围为[3,65599],ID 0,1,2被协议规范直接使用,其中ID值0,1分别表示了Basic Header占用2个字节和4个字节:
ID值0:代表Basic Header占用2个字节,CSID在 [64,319]之间
ID值1:代表Basic Header占用4个字节,CSID在[64,65599]之间
ID值2:代表chunk是控制信息和一些命令信息

消息头(Message Header) 包含时间戳(TimeStamp),消息长度(MsgLength),消息类型(TypeID)和流ID(SteamID)
它们都是可选的。常用的消息类型如下表所示:

扩展时间戳 是可选的。当时间戳过大,TimeStamp无法表示时才会使用。即TimeStamp 的值为0xFFFFFF
Chunking(Message 分块)
RTMP在收发数据的时候并不是以Message为单位的,而是把Message拆分成Chunk发送,而且必须在一个Chunk发送完成之后才能开始发送下一个Chunk。每个Chunk中带有MessageID(Chunk Stream ID)代表属于哪个Message,接受端也会按照这个id来将chunk组装成Message。
为什么RTMP要将Message拆分成不同的Chunk呢?通过拆分,数据量较大的Message可以被拆分成较小的“Message”,这样就可以避免优先级低的消息持续发送阻塞优先级高的数据,比如在视频的传输过程中,会包括视频帧,音频帧和RTMP控制信息,如果持续发送音频数据或者控制数据的话可能就会造成视频帧的阻塞,然后就会造成看视频时最烦人的卡顿现象。同时对于数据量较大的Message,可以通过对Chunk Header的字段来压缩信息,从而减少信息的传输量。
Chunk的默认大小是128字节,在传输过程中,通过一个叫做Set Chunk Size的控制信息可以设置Chunk数据量的最大值,在发送端和接受端会各自维护一个Chunk Size(srs流媒体服务器默认是60000),可以分别设置这个值来改变这一方发送的Chunk的最大值。大一点的Chunk减少了计算每个chunk的时间从而减少了CPU的占用率,但是它会占用更多的时间在发送上,尤其是在低带宽的网络情况下,很可能会阻塞后面更重要信息的传输。小一点的Chunk可以减少这种阻塞问题,但小的Chunk会引起过多额外的信息(Chunk中的Header),少量多次的传输也可能会造成发送的间断导致不能充分利用高带宽的优势,因此并不适合在高比特率的流中传输。在实际发送时应对要发送的数据用不同的Chunk Size去尝试,通过抓包分析等手段得出合适的Chunk大小,并且在传输过程中可以根据当前的带宽信息和实际信息的大小动态调Chunk的大小,从而尽量提高CPU的利用率并减少信息的阻塞机率。
rtmp服务器搭建
Nginx服务器
Nginx(engine x)是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·塞索耶夫为俄罗斯访问量第二的Rambler.ru站点开发的,第一个公开版本0.1.0发布于2004年10月4日。其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、简单的配置文件和低系统资源的消耗而闻名。2011年6月1日,nignx 1.0.4发布。
其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。
Nginx是高性能的HTTP和反向代理的web服务器,处理高并发能力是十分强大的,能经受高负载的考验,有报告表明能支持高达50,000个并发连接数。
Nginx支持热部署,启动简单,可以做到7*24小时不间断运行,几个月都不需要重新启动。
Windows平台下要使用特殊的Nginx版本:
Nginx服务器下载地址:http://nginx-win.ecsds.eu/download/ 选择nginx 1.7.11.3 Gryphon.zip下载
想要推拉流还需要下载一个rtmp模块
Nginx的rtmp模块下载地址:https://github.com/arut/nginx-rtmp-module/
配置Nginx服务器
- 解压Nginx的压缩包并打开

- 将下载好的rtmp模块解压,放到该目录下

- 进入conf目录,打开Nginx配置文件nginx-win.conf

4.在该文件中添加如下内容
rtmp {server {listen 1935;#监听端口,若被占用,可以更改chunk_size 4000;#上传flv文件块儿的大小application live { #创建一个叫live的应用live on;#开启live的应用allow publish 127.0.0.1;#allow play all;}}
}

5. 启动Nginx服务器
进入Nginx.exe所在目录

6. 使用命令行打开

常用命令如下
nginx.exe -c conf\nginx-win.conf
nginx.exe -s stop //快速终止服务器,可能不保存相关信息
nginx.exe -s quit //完整有序停止服务器,保存相关信息
nginx.exe -s reload //重新载入Nginx,当配置信息修改,需要重新载入这些配置时使用此命令

执行后有个光标在那闪,nginx就启动成功了
7. 测试服务器是否是正常的
拉流
打开电脑上的vlc,没有的话去下载一个

点媒体>网络串流
输入网络填 rtmp://127.0.0.1/live/room

推流
8. 打开电脑上的obs,没有的话去下载一个

9. 点左下角+添加场景,然后点中间的+点显示器采集,点确定,选择主显示器。点确定

10. 点设置>直播>服务>自定义

11. 直播成功

librtmp库编译
librtmp库编译
推流
flv构成在框架简介那篇有介绍
推流代码用vs2022跑的,代替了obs的工作
#define _CRT_SECURE_NO_WARNINGS#include <iostream>
#include <WinSock2.h>
extern "C" {
#include <rtmp.h>
}
#pragma comment(lib, "ws2_32.lib")bool openFLV(CONST char* filename, FILE** file)//打开FLV文件
{*file = fopen(filename, "rb");//打开文件if (!*file){std::cout << "打开文件失败" << std::endl;return false;}fseek(*file, 9, SEEK_SET);//跳过FLV头fseek(*file, 4, SEEK_CUR);//跳过PreviousTagSize,定位到当前Tagreturn true;
}
int readFLV(FILE* file, RTMPPacket** packet)
{char tag[11] = "";if (fread(tag, 1, 11, file) != 11)//读取11个字节return 0;uint32_t dataSize = (tag[1] << 16 & 0xFF0000) | (tag[2] << 8 & 0xFF00) | (tag[3] & 0xFF);;//获取数据大小if (tag[0] != 0x08 && tag[0] != 0x09)//判断是否是音频或视频Tag{fseek(file, dataSize + 4, SEEK_CUR);//跳过当前Tag,和下一个PreviousTagSize,定位到下一个Tagreturn 2;}int ret = fread((*packet)->m_body, 1, dataSize, file);//读取数据if (ret != dataSize)//判断是否读取成功return 0;(*packet)->m_headerType = RTMP_PACKET_SIZE_LARGE;//设置包大小(*packet)->m_nBodySize = dataSize;//设置包大小uint32_t timestamp = (tag[4] << 16 & 0xFF0000) | (tag[5] << 8 & 0xFF00) | (tag[6] & 0xFF);//获取时间戳(*packet)->m_nTimeStamp = timestamp;//设置时间戳(*packet)->m_packetType = tag[0];//设置包类型std::cout << "read " << dataSize << " bytes, timestamp: " << timestamp << std::endl;fseek(file, 4, SEEK_CUR);//跳过PreviousTagSizereturn 1;
}int main()
{WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)return -1;RTMP* r = RTMP_Alloc();//分配内存RTMP_Init(r);//初始化RTMP_SetupURL(r, (char*)"rtmp://localhost/live/stream");//设置URL RTMP_EnableWrite(r);//启用写权限if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0))//连接return -1;RTMPPacket* packet = new RTMPPacket;//分配内存RTMPPacket_Alloc(packet, 1024 * 1024);//分配内存RTMPPacket_Reset(packet);//重置packet->m_hasAbsTimestamp = 0;//设置时间戳packet->m_nChannel = 0x04;//设置通道packet->m_nInfoField2 = r->m_stream_id;//设置流IDFILE* file;if (!openFLV("source/video-60fps.flv", &file))//打开FLV文件return -1;int ret = 0;uint32_t ts = 0;while (true){ret = readFLV(file, &packet);//读取FLV文件if (ret == 0)//读取失败break;if (ret == 2)//读取成功,但不是音频或视频Tagcontinue;if (!RTMP_IsConnected(r))//判断是否连接成功break;if (ts < packet->m_nTimeStamp)//判断是否需要等待Sleep(packet->m_nTimeStamp - ts);if (!RTMP_SendPacket(r, packet, true))//发送包break;ts = packet->m_nTimeStamp;//更新时间戳}std::cout << "推流结束" << std::endl;fclose(file);//关闭文件RTMPPacket_Free(packet);//释放内存RTMP_Close(r);//关闭连接RTMP_Free(r);//释放内存WSACleanup();//清理return 0;
}
}
测试
1.启动nignx

2.打开vlc,配置上面代码中设置的url地址

3. 运行上面写的代码

相关文章:
FFmpeg rtmp推流直播
文章目录 rtmp协议RTMP协议组成RTMP的握手过程RTMP流的创建RTMP消息格式Chunking(Message 分块) rtmp服务器搭建Nginx服务器配置Nginx服务器 librtmp库编译推流 rtmp协议 RTMP(Real Time Messaging Protocol)是由Adobe公司基于Flash Player播放器对应的…...
WordPress Icegram Express插件Sql注入漏洞复现(CVE-2024-2876)(附脚本)
免责申明: 本文所描述的漏洞及其复现步骤仅供网络安全研究与教育目的使用。任何人不得将本文提供的信息用于非法目的或未经授权的系统测试。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权,请及时与我们联系,我们将尽快处理并删除相关内容。 0x0…...
重构字符串(767)
767. 重构字符串 - 力扣(LeetCode) 解法: class Solution { public:string reorganizeString(string s){string res;//因为1 < s.length < 500 , uint64_t 类型足够uint16_t n s.size();if (n 0) {return res;}unordere…...
IO进程线程复习
IO进程线程复习...
深入理解Linux内核的虚拟地址到物理地址转换机制及缓存优化
在现代计算机系统中,虚拟地址到物理地址的转换是操作系统内存管理的重要组成部分。特别是在基于x86_64架构的Linux系统上,这一转换过程及其相关的缓存机制对系统性能和稳定性至关重要。本文将深入探讨Debian 10上运行Linux 4.19内核时,这些机制的实现细节,特别是页表管理、…...
2025年01月29日Github流行趋势
项目名称:Janus 项目地址url:https://github.com/deepseek-ai/Janus项目语言:Python历史star数:9350今日star数:5969项目维护者:learningpro, hills-code, TheOneTrueGuy, mowentian, soloice项目简介&…...
yolov11、yolov8部署的7种方法(yolov11、yolov8部署rknn的7种方法),一天一种部署方法,7天入门部署
由于涉及量化、部署两个领域,本博文难免有不对之处,欢迎指正。 本博客对 yolov11(yolov8)尝试了7种不同的部署方法,在最基础的模型上一步一步的去掉解码相关的操作(移到后处理种进行)࿰…...
【ArcGIS遇上Python】批量提取多波段影像至单个波段
本案例基于ArcGIS python,将landsat影像的7个波段影像数据,批量提取至单个波段。 相关阅读:【ArcGIS微课1000例】0141:提取多波段影像中的单个波段 文章目录 一、数据准备二、效果比对二、python批处理1. 编写python代码2. 运行代码一、数据准备 实验数据及完整的python位…...
Node.js MySQL:深度解析与最佳实践
Node.js MySQL:深度解析与最佳实践 引言 Node.js作为一种流行的JavaScript运行时环境,以其轻量级、高性能和事件驱动模型受到开发者的青睐。MySQL则是一款功能强大的关系型数据库管理系统,广泛应用于各种规模的应用程序中。本文将深入探讨Node.js与MySQL的集成,分析其优势…...
wordpress外贸独立站常用询盘软件
LiveChat LiveChat是一家提供实时聊天软件的公司,帮助企业通过其平台与客户进行即时通讯,提高客户满意度和忠诚度。他们的产品允许企业在网站、应用程序或电子邮件等多个渠道与客户互动,从而提升客户体验并促进销售增长。 LiveChat的软件特…...
Kotlin 委托详解
Kotlin 委托详解 引言 Kotlin 作为一种现代化的编程语言,在 Android 开发等领域得到了广泛的应用。在 Kotlin 中,委托(Delegation)是一种强大的特性,它可以让我们以更简洁的方式实现代码的复用和扩展。本文将详细解析…...
Cursor 简介:AI 如何改变编程体验
在软件开发领域,效率和质量始终是开发者们追求的目标。随着人工智能技术的飞速发展,编程工具也在不断进化,Cursor 便是这一趋势下的产物。它不仅仅是一个代码编辑器,更是一个集成了 AI 能力的智能编程助手,旨在通过 AI…...
Fiddler(一) - Fiddler简介_fiddler软件
文章目录 一、为什么选择Fiddler作为抓包工具? 二、什么是Fiddler?三、Fiddler使用界面简介四、延伸阅读 一、为什么选择Fiddler作为抓包工具? 抓包工具有很多,小到最常用的web调试工具firebug,大到通用性强大的抓包工具wireshark。为什么使用fid…...
实测数据处理(Wk算法处理)——SAR成像算法系列(十二)
系列文章目录 《SAR学习笔记-SAR成像算法系列(一)》 《wk算法-SAR成像算法系列(五)》 文章目录 前言 一、算法流程 1.1、回波信号生成 2.2 Stolt插值 2.3 距离脉冲压缩 2.4 方位脉冲压缩 2.5 SAR成像 二、仿真实验 2.1、仿真参数…...
P1775 石子合并(弱化版)
P1775 石子合并(弱化版) 题目描述 设有 N ( N ≤ 300 ) N(N \le 300) N(N≤300) 堆石子排成一排,其编号为 1 , 2 , 3 , ⋯ , N 1,2,3,\cdots,N 1,2,3,⋯,N。每堆石子有一定的质量 m i ( m i ≤ 1000 ) m_i\ (m_i \le 1000) mi (mi≤…...
一文回顾讲解Java中的集合框架
这篇文章以提问的方式总结回顾下Java中常见的集合框架 Java中的集合框架可以分为两条大的支线:Collection和Map Collection,主要由List、Set、Queue组成; List是有序,可重复的集合,典型代表有封装了动态数组的ArrayList和封装了链…...
多模态论文笔记——NaViT
大家好,这里是好评笔记,公主号:Goodnote,专栏文章私信限时Free。本文详细解读多模态论文NaViT(Native Resolution ViT),将来自不同图像的多个patches打包成一个单一序列——称为Patch n’ Pack—…...
智能小区物业管理系统推动数字化转型与提升用户居住体验
内容概要 在当今快速发展的社会中,智能小区物业管理系统的出现正在改变传统的物业管理方式。这种系统不仅仅是一种工具,更是一种推动数字化转型的重要力量。它通过高效的技术手段,将物业管理与用户居住体验紧密结合,无疑为社区带…...
I2C基础知识
引言 这里祝大家新年快乐!前面我们介绍了串口通讯协议,现在我们继续来介绍另一种常见的简单的串行通讯方式——I2C通讯协议。 一、什么是I2C I2C 通讯协议(Inter-Integrated Circuit)是由Phiilps公司在上个世纪80年代开发的&#…...
护眼好帮手:Windows显示器调节工具
在长时间使用电脑的过程中,显示器的亮度和色温对眼睛的舒适度有着重要影响。传统的显示器调节方式不仅操作繁琐,而且在低亮度下容易导致色彩失真。因此,今天我想为大家介绍一款适用于Windows系统的护眼工具,它可以帮助你轻松调节显…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
