news 2026/4/3 3:09:21

使用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/4/2 15:42:56

KLayout版图设计工具全攻略:从入门到精通

KLayout版图设计工具全攻略&#xff1a;从入门到精通 【免费下载链接】klayout KLayout Main Sources 项目地址: https://gitcode.com/gh_mirrors/kl/klayout 基础认知&#xff1a;理解KLayout的核心价值 版图设计是芯片开发流程中的关键环节&#xff0c;工程师常面临三…

作者头像 李华
网站建设 2026/3/26 8:55:46

FLUX.1-dev-fp8-dit文生图保姆级教程:ComfyUI中SDXL Prompt Styler使用全步骤

FLUX.1-dev-fp8-dit文生图保姆级教程&#xff1a;ComfyUI中SDXL Prompt Styler使用全步骤 1. 为什么选FLUX.1-dev-fp8-dit SDXL Prompt Styler组合 你可能已经试过不少文生图模型&#xff0c;但总感觉生成的图片要么细节糊、要么风格跑偏、要么提示词写得再细也出不来想要的…

作者头像 李华
网站建设 2026/3/25 23:32:38

NCM格式转换技术探索:从加密破解到跨设备播放的完整实践指南

NCM格式转换技术探索&#xff1a;从加密破解到跨设备播放的完整实践指南 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换&#xff0c;Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 诊断数字音乐枷锁&#xff1a;NCM格式…

作者头像 李华
网站建设 2026/3/26 3:22:30

DASD-4B-Thinking部署教程:vLLM + Chainlit 镜像免配置快速启动

DASD-4B-Thinking部署教程&#xff1a;vLLM Chainlit 镜像免配置快速启动 1. 为什么这个模型值得你花5分钟试试&#xff1f; 你有没有遇到过这样的情况&#xff1a;想跑一个能真正“思考”的小模型&#xff0c;但不是卡在环境配置上&#xff0c;就是被复杂的API调用绕晕&…

作者头像 李华
网站建设 2026/3/13 1:23:23

B站直播P2P上传优化指南:解决带宽占用问题的实用方法

B站直播P2P上传优化指南&#xff1a;解决带宽占用问题的实用方法 【免费下载链接】BiliRoamingX-integrations BiliRoamingX integrations powered by revanced. 项目地址: https://gitcode.com/gh_mirrors/bi/BiliRoamingX-integrations 在观看B站直播时&#xff0c;你…

作者头像 李华