news 2026/5/14 13:50:59

使用c/c++实现一个rtmp客户端程序

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用c/c++实现一个rtmp客户端程序

一 概述 

        该文章主要实现了rtmp拉流的功能。rtmp协议中的负载视频为h264格式,音频为aac格式.将接收到的流提取出h264裸码流和aac裸码流可以进行解码播放,存储和传输。该客户端程序只实现了将h264视频数据和aac音频数据存入文件.

二 程序的依赖库

     1.ssl(加密认证库)

     2.zip(压缩库)

     3.librtmp(开源库)

     4.rtmp_pull(基于librtmp库封装的拉流库)

        rtmp_pull库,参考下面链接:

https://blog.csdn.net/kk821521286/article/details/157728972?spm=1001.2014.3001.5502https://blog.csdn.net/kk821521286/article/details/157728972?spm=1001.2014.3001.5502

三 关键代码实现

    1.rtmpparse.h  

#ifndef RTMPPARSE_H #define RTMPPARSE_H #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include "rtmp_pull.h" // 宏定义:RTMP包类型(协议规定) #define RTMP_PACKET_TYPE_AUDIO 0x08 // 音频包 #define RTMP_PACKET_TYPE_VIDEO 0x09 // 视频包 #define RTMP_PACKET_TYPE_AMF0_DATA 0x12 // AMF0数据(包含onMetaData元数据) // 视频编码ID(RTMP协议) #define RTMP_VIDEO_CODEC_H264 0x07 // H.264/AVC // 音频编码ID(RTMP协议) #define RTMP_AUDIO_CODEC_AAC 0x0A // AAC // 帧类型(视频包高4位) #define RTMP_VIDEO_FRAME_KEY 0x10 // 关键帧(I帧) #define RTMP_VIDEO_FRAME_SEQ_HEADER 0x00 // AVC序列头(SPS/PPS) // AAC包类型(音频包扩展位) #define RTMP_AAC_PACKET_CONFIG 0x00 // AAC配置帧(ADTS头参数) #define RTMP_AAC_PACKET_RAW 0x01 // AAC原始数据帧 /************************** 解析结果结构化定义 **************************/ // 视频包解析结果 typedef struct { int is_h264; // 是否为H.264编码 int is_seq_header; // 是否为AVC序列头(SPS/PPS,必须先解析这个才能解码) int is_key_frame; // 是否为关键帧 uint8_t* raw_data; // 原始H.264数据(去掉RTMP头部后的纯数据) int raw_data_len; // 原始H.264数据长度 } RtmpVideoFrame; // 音频包解析结果 typedef struct { int is_aac; // 是否为AAC编码 int is_config; // 是否为AAC配置帧(必须先解析这个才能解码) uint8_t* raw_data; // 原始AAC数据(去掉RTMP头部后的纯数据) int raw_data_len; // 原始AAC数据长度 int sample_rate; // 采样率(8k/16k/32k/44.1k/48k) int channel; // 声道(1:单声道,2:立体声) int bit_depth; // 位深(8/16位) } RtmpAudioFrame; // 元数据解析结果(onMetaData,包含流的基础信息) typedef struct { double video_width; // 视频宽度 double video_height; // 视频高度 double frame_rate; // 视频帧率 double audio_samplerate; // 音频采样率 double audio_channels; // 音频声道 double duration; // 流总时长(秒,直播流为0) double video_bitrate; // 视频码率(kbps) double audio_bitrate; // 音频码率(kbps) } RtmpMetaData; // 通用RTMP包解析结果 typedef struct { int packet_type; // 包类型:音频/视频/元数据/其他 uint32_t timestamp; // 时间戳(毫秒) uint32_t body_len; // 数据包体长度 RtmpVideoFrame video; // 视频包数据(仅packet_type=视频时有效) RtmpAudioFrame audio; // 音频包数据(仅packet_type=音频时有效) RtmpMetaData meta; // 元数据(仅packet_type=元数据时有效) int is_valid; // 解析是否有效(0:无效,1:有效) } RtmpPacketParseResult; #ifdef __cplusplus extern "C"{ #endif void RtmpParseResult_Init(RtmpPacketParseResult* result); void RtmpParseResult_Free(RtmpPacketParseResult* result); int RTMPPullPacket_Parse(RTMPPullPacket* packet, RtmpPacketParseResult* result); #ifdef __cplusplus } #endif #endif // RTMPPARSE_H

2.rtmpparse.cpp

#include "rtmpparse.h" #include <iostream> /************************** 工具函数:初始化/释放解析结果 **************************/ // 初始化解析结果结构体 void RtmpParseResult_Init(RtmpPacketParseResult* result) { if (result == NULL) return; memset(result, 0, sizeof(RtmpPacketParseResult)); // 初始化默认值 result->is_valid = 0; result->meta.video_width = 0; result->meta.video_height = 0; result->audio.sample_rate = 0; result->audio.channel = 0; } // 释放解析结果的内存(防止内存泄漏) void RtmpParseResult_Free(RtmpPacketParseResult* result) { if (result == NULL) return; // 释放视频原始数据 if (result->video.raw_data != NULL) { free(result->video.raw_data); result->video.raw_data = NULL; } // 释放音频原始数据 if (result->audio.raw_data != NULL) { free(result->audio.raw_data); result->audio.raw_data = NULL; } // 重置结构体 memset(result, 0, sizeof(RtmpPacketParseResult)); } /************************** 核心解析接口:解析RTMPPacket **************************/ /** * @brief 解析RTMP元数据(onMetaData,AMF0格式) * @param body RTMP包体指针 * @param body_len 包体长度 * @param meta 解析后的元数据结果 * @return 0:失败,1:成功 */ static int parse_amf0_metadata(uint8_t* body, int body_len, RtmpMetaData* meta) { #if 0 /************************ 第一步:入参合法性校验 ************************/ if (body == NULL || body_len < 10 || meta == NULL) { printf("【元数据解析】失败:入参无效或包体过短(长度:%d)\n", body_len); return 0; } memset(meta, 0, sizeof(RtmpMetaData)); const char* amf_buf = (const char*)body; int parse_offset = 0; // 定义需要的AVal键名(原生amf.h的AVC宏) const AVal av_onMetaData = AVC("onMetaData"); const AV
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/10 6:55:54

7个问题诊断串流工具性能瓶颈:终极优化指南实现零延迟体验

7个问题诊断串流工具性能瓶颈&#xff1a;终极优化指南实现零延迟体验 【免费下载链接】Sunshine Sunshine: Sunshine是一个自托管的游戏流媒体服务器&#xff0c;支持通过Moonlight在各种设备上进行低延迟的游戏串流。 项目地址: https://gitcode.com/GitHub_Trending/su/Su…

作者头像 李华
网站建设 2026/5/11 16:55:42

Zotero Style:重塑科研文献管理效率的全方位解决方案

Zotero Style&#xff1a;重塑科研文献管理效率的全方位解决方案 【免费下载链接】zotero-style zotero-style - 一个 Zotero 插件&#xff0c;提供了一系列功能来增强 Zotero 的用户体验&#xff0c;如阅读进度可视化和标签管理&#xff0c;适合研究人员和学者。 项目地址: …

作者头像 李华
网站建设 2026/5/13 11:24:21

Python:函数对象

在 Python 中&#xff0c;函数对象&#xff08;function object&#xff09;并不是语法层面的子程序&#xff0c;而是在运行时创建的一种对象。与其他对象一样&#xff0c;它可以被绑定、传递和存储&#xff1b;不同之处在于&#xff0c;函数对象用于承载一次函数调用所需的全部…

作者头像 李华
网站建设 2026/5/13 7:57:12

SeqGPT-560M GPU适配实战:RTX 4090双卡环境CUDA/cuDNN版本匹配指南

SeqGPT-560M GPU适配实战&#xff1a;RTX 4090双卡环境CUDA/cuDNN版本匹配指南 1. 为什么双卡RTX 4090部署SeqGPT-560M必须谨慎选版本&#xff1f; 你手头刚到两块崭新的RTX 4090&#xff0c;显存合计48GB&#xff0c;算力拉满&#xff0c;信心十足地准备部署SeqGPT-560M——…

作者头像 李华
网站建设 2026/5/10 3:12:51

Qwen3-VL-4B Pro参数详解:活跃度滑块如何影响图文推理多样性与准确性

Qwen3-VL-4B Pro参数详解&#xff1a;活跃度滑块如何影响图文推理多样性与准确性 1. 什么是Qwen3-VL-4B Pro&#xff1f; Qwen3-VL-4B Pro不是简单升级的“大一号”模型&#xff0c;而是一次面向真实图文交互场景的深度能力重构。它基于阿里通义实验室发布的Qwen/Qwen3-VL-4B…

作者头像 李华