当前位置: 首页 > java >正文

[直播推流] rtmpdump 库学习

常用 api 学习

参考文档
直播推流需要用到

#初始化:
RTMP_Alloc() :用于创建一个RTMP会话的句柄。
RTMP_Init():初始化句柄。
RTMP_SetupURL():设置会话的参数。
RTMP_EnableWrite(): 启用发布模式
RTMP_SetBufferMS():设置缓存区时间
#连接服务器
RTMP_Connect():建立RTMP链接中的网络连接(NetConnection)。
RTMP_ConnectStream():建立RTMP链接中的网络流(NetStream)。
#发送数据
RTMP_Write():发送数据
#销毁
RTMP_Close():关闭连接
RTMP_Free():用于清理会话。
#include "RtmpStream.h"
// 平台特定头文件
// 如果不是Windows平台
#ifndef _WIN32
// 定义毫秒级睡眠函数
#define SLEEP_MS(ms) usleep((ms)*1000)
// 如果是Windows平台
#else
// 定义毫秒级睡眠函数
#define SLEEP_MS(ms) Sleep(ms)
#endif
// 定义连接超时时间,单位为秒
const int CONNECT_TIMEOUT = 5;          // seconds
// 定义缓冲区持续时间,单位为秒(1小时)
const int BUFFER_DURATION = 3600;       // seconds (1 hour)
// 定义目标帧率,单位为帧每秒
const int TARGET_FRAME_RATE = 30;       // fps
// 计算每帧之间的延迟时间,单位为毫秒
const int FRAME_DELAY_MS = 1000 / TARGET_FRAME_RATE;// 构造函数,初始化RTMP URL
RtmpStream::RtmpStream(const char* rtmpUrl): m_rtmpUrl(rtmpUrl), m_rtmp(nullptr)
{
}// 析构函数,清理资源
RtmpStream::~RtmpStream()
{cleanup();
}// 初始化RTMP连接
bool RtmpStream::initialize()
{// 分配RTMP对象内存m_rtmp = RTMP_Alloc();if (!m_rtmp) {// 记录错误日志RTMP_Log(RTMP_LOGERROR, "RTMP_Alloc failed");return false;}// 初始化RTMP对象RTMP_Init(m_rtmp);// 设置连接超时时间m_rtmp->Link.timeout = CONNECT_TIMEOUT;// 设置RTMP URLif (!RTMP_SetupURL(m_rtmp, const_cast<char*>(m_rtmpUrl))) {// 记录错误日志RTMP_Log(RTMP_LOGERROR, "RTMP_SetupURL failed");return false;}// 启用发布模式RTMP_EnableWrite(m_rtmp);// 设置缓冲区持续时间RTMP_SetBufferMS(m_rtmp, BUFFER_DURATION * 1000);return true;
}// 连接到RTMP服务器
bool RtmpStream::connect()
{// 建立RTMP连接if (!RTMP_Connect(m_rtmp, nullptr)) {// 记录错误日志RTMP_Log(RTMP_LOGERROR, "RTMP_Connect failed");return false;}// 连接到RTMP流if (!RTMP_ConnectStream(m_rtmp, 0)) {// 记录错误日志RTMP_Log(RTMP_LOGERROR, "RTMP_ConnectStream failed");return false;}return true;
}// 发布FLV文件到RTMP服务器
bool RtmpStream::publish(char* packet, uint32_t packetSize, uint32_t timestamp)
{// 记录开始发布日志RTMP_LogPrintf("Starting to publish stream...");// 记录开始时间uint32_t startTime = RTMP_GetTime();// 记录上一帧时间戳uint32_t lastTimestamp = 0;// 记录已发送帧数uint32_t framesSent = 0;// 检查RTMP连接是否仍然有效if (!RTMP_IsConnected(m_rtmp)) {// 若连接丢失,记录错误日志RTMP_Log(RTMP_LOGERROR, "RTMP connection lost");return false;}// 尝试将数据包写入RTMP连接if (!RTMP_Write(m_rtmp, (char*)packet, packetSize)) {// 若写入失败,记录错误日志RTMP_Log(RTMP_LOGERROR, "RTMP_Write failed");// 释放已分配的内存return false;}// 增加已发送的帧数计数framesSent++;// 帧率控制逻辑// 计算从开始发布到现在经过的时间uint32_t elapsed = RTMP_GetTime() - startTime;// 如果当前标签的时间戳大于已过去的时间,进行适当延迟if (timestamp > elapsed) {// 调用平台特定的睡眠函数进行延迟SLEEP_MS(FRAME_DELAY_MS);}// 定期记录发布进度if (framesSent % 100 == 0) {// 每发送100帧,记录已发送帧数和当前时间戳RTMP_LogPrintf("Sent %u frames, timestamp: %ums",framesSent, timestamp);}RTMP_LogPrintf("Publishing completed. Total frames sent: %u", framesSent);return true;
}void RtmpStream::cleanup()
{if (m_rtmp) {if (RTMP_IsConnected(m_rtmp)) {RTMP_Close(m_rtmp);}RTMP_Free(m_rtmp);m_rtmp = nullptr;}
}

rtmp url 格式学习

url 格式

rtmp[t][e|s]://hostname[:port][/app[/playpath]]

格式解析

参考文档
rtmp://localhost/vod/mp4:sample1_1500kbps.f4v
“: //”之前的是使用的协议类型,可以是rtmp,rtmpt,rtmps等
之后是服务器地址;再之后是端口号(可以没有,默认1935);在之后是application的名字,在这里是“vod”;最后是流媒体文件路径。

RTMP_Write 需要的数据

flv 格式

flv header + flv body
flv body = PreviousTagSize + n* Tag
Tag = Type+DataSize+Timestamp+TimestampExtended+StreamID+Data+PreviousTagSize
只需要将 Tag 传入RTMP_Write,他就会解析了,底层使用的是 RTMP_Publish。

解析参考代码:

  1. 一开始就跳过 13个字节,分别是FLV文件头(9字节)和第一个PreviousTagSize(4字节)
  2. 之后每次先读取 tag 的 header,获取后面数据的长度,
    然后读取数据。
bool FlvParse::open()
{// 打开FLV文件m_file = fopen(m_flvPath, "rb");if (!m_file) {// 记录错误日志cerr << "Failed to open FLV file: " << m_flvPath << endl;return false;}// 跳过FLV文件头(9字节)和第一个PreviousTagSize(4字节)if (fseek(m_file, 13, SEEK_SET) != 0) {// 记录错误日志cerr << "Failed to seek FLV file" << endl;return false;}return true;
}bool FlvParse::readTag(char** packet, uint32_t* packetSize, uint32_t* timestamp)
{bool bRes = false;do{// 读取标签头(11字节)uint8_t header[11];if (fread(header, 1, 11, m_file) != 11) break;// 解析标签信息// 标签类型uint8_t tagType = header[0];// 数据大小uint32_t dataSize = (header[1] << 16) | (header[2] << 8) | header[3];// 时间戳m_timestamp = (header[4] << 16) | (header[5] << 8) | header[6];// 扩展时间戳(如果需要)m_timestamp |= (header[7] << 24);// 跳过非音频/视频标签if (tagType != 0x08 && tagType != 0x09) {// 移动文件指针跳过数据和PreviousTagSizefseek(m_file, dataSize + 4, SEEK_CUR);continue;}// 分配缓冲区用于完整标签(头 + 数据 + 前一个标签大小)uint32_t packetSize = 11 + dataSize + 4;cout << "packetSize: " << packetSize << endl;// 动态分配内存以存储完整的数据包m_packet.resize(packetSize);// 复制标签头到数据包缓冲区memcpy(m_packet.data(), header, 11);// 从文件中读取数据部分到数据包缓冲区if (fread(m_packet.data() + 11, 1, dataSize + 4, m_file) != dataSize + 4) {// 若读取失败,释放已分配的内存m_packet.clear();break;}bRes = true;break;} while (1);if (packet && packetSize && timestamp){*packet = (char*)m_packet.data();*packetSize = m_packet.size();*timestamp = m_timestamp;}return bRes;
}
http://www.xdnf.cn/news/14305.html

相关文章:

  • Jmeter录制APP脚本
  • 【FreeRTOS-队列集】
  • Java的接口
  • SKUA-GOCAD入门教程-第八节 线的创建与编辑4
  • Milvus/ES 插入方案对比
  • K8s 容器化安全产品性能问题排查指南
  • web3方法详解
  • 【Java】网络编程基础与聊天室架构分析
  • HTML 从入门到起飞 · 系列合集:一站式学习不掉线
  • 构建多智能体(AI Agent)的高效协作平台——CrewAI探索
  • 基于CNN深度学习的小程序识别2-视频介绍下自取
  • 超子说物联网-MQTT_笔记1---通过EMQX搭建MQTT服务器
  • springboot项目启动报错:spring boot application in default package
  • React条件渲染之逻辑与和逻辑或详解
  • 第19篇:数据库中间件中的 SQL 分析与审计机制设计
  • 嵌入式硬件篇---常见电平标准
  • 【MPC】模型预测控制笔记 (3):无约束输出反馈MPC
  • flutter 项目配置Gradle下载代理
  • 以太网交换机交换表的建立
  • 使用VSCode开发FastAPI指南(二)
  • Kubernetes (K8S) 系统学习规划
  • 分布式数据库中间件-Sharding-JDBC
  • 性能优化 - 高级进阶: Spring Boot服务性能优化
  • C#设计模式之AbstractFactory_抽象工厂_对象创建新模式-学习
  • leetcode23-合并K个升序链表
  • Docker + PyFlink1.17 数据写入 MySQL
  • 技术选型指南:如何选择更适合项目的开源语言及其生态系统
  • ESP32 005 MicroPython I2S 实现音频传输与播放
  • 【数据可视化】Pyecharts-家乡地图
  • 从0开始学习语言模型--Day02-如何最大化利用硬件