news 2026/10/1 6:34:07

使用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/10/1 3:36:07

RMBG-2.0与CNN结合:提升图像分割精度的创新方法

RMBG-2.0与CNN结合&#xff1a;提升图像分割精度的创新方法 1. 这不是普通的背景去除&#xff0c;而是发丝级精度的视觉革命 你有没有试过给一张带复杂发丝的人物照片去背景&#xff1f;那种边缘毛躁、半透明区域处理失真、细节丢失的感觉&#xff0c;是不是让人特别抓狂&…

作者头像 李华
网站建设 2026/9/30 0:02:17

3步搞定视频PPT智能提取:告别手动截图的高效解决方案

3步搞定视频PPT智能提取&#xff1a;告别手动截图的高效解决方案 【免费下载链接】extract-video-ppt extract the ppt in the video 项目地址: https://gitcode.com/gh_mirrors/ex/extract-video-ppt 你是否经历过这些场景&#xff1a;在线课程结束后&#xff0c;花费数…

作者头像 李华
网站建设 2026/9/30 11:31:14

GLM-4.7-Flash快速上手指南:30B MoE中文大模型零基础调用

GLM-4.7-Flash快速上手指南&#xff1a;30B MoE中文大模型零基础调用 你是不是也遇到过这些情况&#xff1a;想试试最新大模型&#xff0c;却被复杂的环境配置卡住&#xff1b;下载完模型发现显存不够跑不动&#xff1b;好不容易部署成功&#xff0c;API又不兼容现有代码&…

作者头像 李华
网站建设 2026/9/28 13:32:36

YOLO12 WebUI体验:上传图片自动识别物体的完整流程

YOLO12 WebUI体验&#xff1a;上传图片自动识别物体的完整流程 1. 为什么这次目标检测体验让人眼前一亮&#xff1f; 你有没有试过把一张随手拍的照片拖进网页&#xff0c;几秒钟后&#xff0c;图中的人、车、猫、手机全被框出来&#xff0c;还标好了名字和可信度&#xff1f…

作者头像 李华