1. 基本流程
复制代码
#include <librtmp/rtmp.h>
#include <librtmp/log.h>
// 基本推流流程
RTMP* rtmp = NULL;
// 初始化RTMP连接
rtmp = RTMP_Alloc();
RTMP_Init(rtmp);
// 设置服务器地址和流密钥
RTMP_SetupURL(rtmp, (char*)"rtmp://server:1935/live/stream");
RTMP_EnableWrite(rtmp);
// 连接服务器
if (!RTMP_Connect(rtmp, NULL)) {
// 处理错误
}
// 连接流
if (!RTMP_ConnectStream(rtmp, 0)) {
// 处理错误
}
// 发送音视频数据
// ...
// 清理
RTMP_Close(rtmp);
RTMP_Free(rtmp);
2. H.264视频流推送
复制代码
// H.264相关参数
typedef struct {
uint8_t* sps; // SPS数据
int sps_len; // SPS长度
uint8_t* pps; // PPS数据
int pps_len; // PPS长度
uint32_t timestamp; // 时间戳
} H264Context;
// 发送H.264帧
int send_h264_frame(RTMP* rtmp, uint8_t* data, int size,
uint32_t timestamp, int is_keyframe) {
RTMPPacket packet;
RTMPPacket_Reset(&packet);
RTMPPacket_Alloc(&packet, size + 16);
packet.m_packetType = RTMP_PACKET_TYPE_VIDEO;
packet.m_nChannel = 0x04; // 视频通道
packet.m_nTimeStamp = timestamp;
packet.m_nInfoField2 = rtmp->m_stream_id;
uint8_t* body = packet.m_body;
int i = 0;
// 视频标签头
if (is_keyframe) {
body[i++] = 0x17; // FrameType: KeyFrame, CodecID: AVC
} else {
body[i++] = 0x27; // FrameType: InterFrame, CodecID: AVC
}
// AVC包类型: NALU
body[i++] = 0x01;
// Composition time
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x00;
// NALU长度
body[i++] = (size >> 24) & 0xff;
body[i++] = (size >> 16) & 0xff;
body[i++] = (size >> 8) & 0xff;
body[i++] = size & 0xff;
// 复制NALU数据
memcpy(&body[i], data, size);
packet.m_nBodySize = size + i;
// 发送包
int ret = RTMP_SendPacket(rtmp, &packet, 0);
RTMPPacket_Free(&packet);
return ret;
}
// 发送SPS/PPS信息
int send_sps_pps(RTMP* rtmp, H264Context* ctx) {
RTMPPacket packet;
int sps_pps_size = ctx->sps_len + ctx->pps_len + 16;
RTMPPacket_Reset(&packet);
RTMPPacket_Alloc(&packet, sps_pps_size);
packet.m_packetType = RTMP_PACKET_TYPE_VIDEO;
packet.m_nChannel = 0x04;
packet.m_nTimeStamp = 0;
packet.m_nInfoField2 = rtmp->m_stream_id;
uint8_t* body = packet.m_body;
int i = 0;
// 关键帧 + AVC序列头
body[i++] = 0x17; // FrameType: KeyFrame, CodecID: AVC
// AVC包类型: Sequence Header
body[i++] = 0x00;
// Composition time
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x00;
// AVC序列头
body[i++] = 0x01; // version
body[i++] = ctx->sps[1]; // profile
body[i++] = ctx->sps[2]; // profile_compat
body[i++] = ctx->sps[3]; // level
body[i++] = 0xff; // 6 bits reserved + 2 bits nal size length - 1
// sps数量
body[i++] = 0xe1; // 3 bits reserved + 5 bits num of sps
// sps长度
body[i++] = (ctx->sps_len >> 8) & 0xff;
body[i++] = ctx->sps_len & 0xff;
// sps数据
memcpy(&body[i], ctx->sps, ctx->sps_len);
i += ctx->sps_len;
// pps数量
body[i++] = 0x01; // num of pps
// pps长度
body[i++] = (ctx->pps_len >> 8) & 0xff;
body[i++] = ctx->pps_len & 0xff;
// pps数据
memcpy(&body[i], ctx->pps, ctx->pps_len);
i += ctx->pps_len;
packet.m_nBodySize = i;
int ret = RTMP_SendPacket(rtmp, &packet, 0);
RTMPPacket_Free(&packet);
return ret;
}
3. AAC音频流推送
复制代码
// AAC相关参数
typedef struct {
uint8_t* asc; // AudioSpecificConfig
int asc_len; // ASC长度
uint32_t timestamp; // 时间戳
} AACContext;
// 发送AAC序列头
int send_aac_sequence_header(RTMP* rtmp, AACContext* ctx) {
RTMPPacket packet;
int size = ctx->asc_len + 2;
RTMPPacket_Reset(&packet);
RTMPPacket_Alloc(&packet, size);
packet.m_packetType = RTMP_PACKET_TYPE_AUDIO;
packet.m_nChannel = 0x05; // 音频通道
packet.m_nTimeStamp = 0;
packet.m_nInfoField2 = rtmp->m_stream_id;
uint8_t* body = packet.m_body;
// 音频标签头
body[0] = 0xAF; // SoundFormat: AAC (10), SoundRate: 44kHz (3)
// SoundSize: 16-bit samples (1), SoundType: Stereo (1)
// AAC包类型: Sequence Header
body[1] = 0x00;
// AudioSpecificConfig
memcpy(&body[2], ctx->asc, ctx->asc_len);
packet.m_nBodySize = size;
int ret = RTMP_SendPacket(rtmp, &packet, 0);
RTMPPacket_Free(&packet);
return ret;
}
// 发送AAC音频帧
int send_aac_frame(RTMP* rtmp, uint8_t* data, int size,
uint32_t timestamp) {
RTMPPacket packet;
RTMPPacket_Reset(&packet);
RTMPPacket_Alloc(&packet, size + 2);
packet.m_packetType = RTMP_PACKET_TYPE_AUDIO;
packet.m_nChannel = 0x05;
packet.m_nTimeStamp = timestamp;
packet.m_nInfoField2 = rtmp->m_stream_id;
uint8_t* body = packet.m_body;
// 音频标签头
body[0] = 0xAF; // SoundFormat: AAC, 44kHz, 16-bit, Stereo
// AAC包类型: Raw data
body[1] = 0x01;
// 音频数据
memcpy(&body[2], data, size);
packet.m_nBodySize = size + 2;
int ret = RTMP_SendPacket(rtmp, &packet, 0);
RTMPPacket_Free(&packet);
return ret;
}
4. 完整的主程序示例
复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <librtmp/rtmp.h>
#include <librtmp/log.h>
// 解析H.264 SPS/PPS (简化示例)
int parse_h264_sps_pps(uint8_t* data, int size, H264Context* ctx) {
// 实际实现需要解析NALU找到SPS和PPS
// 这里假设data包含SPS和PPS
// ...
return 0;
}
int main() {
RTMP* rtmp = NULL;
H264Context h264_ctx;
AACContext aac_ctx;
// 初始化RTMP
rtmp = RTMP_Alloc();
RTMP_Init(rtmp);
// 设置URL
char url[] = "rtmp://your-server:1935/live/stream";
RTMP_SetupURL(rtmp, url);
RTMP_EnableWrite(rtmp);
// 连接
if (!RTMP_Connect(rtmp, NULL)) {
printf("RTMP连接失败\n");
goto cleanup;
}
if (!RTMP_ConnectStream(rtmp, 0)) {
printf("RTMP连接流失败\n");
goto cleanup;
}
printf("RTMP连接成功\n");
// 初始化音视频参数
// 这里需要从你的音视频流中获取SPS/PPS和ASC
// h264_ctx.sps = ...; h264_ctx.sps_len = ...;
// h264_ctx.pps = ...; h264_ctx.pps_len = ...;
// aac_ctx.asc = ...; aac_ctx.asc_len = ...;
// 发送序列头
send_sps_pps(rtmp, &h264_ctx);
send_aac_sequence_header(rtmp, &aac_ctx);
// 模拟音视频推送循环
uint32_t video_timestamp = 0;
uint32_t audio_timestamp = 0;
int video_interval = 40; // 假设25fps
int audio_interval = 23; // 假设44.1kHz, 1024 samples/frame
for (int i = 0; i < 1000; i++) { // 推送1000帧
// 发送视频帧
uint8_t* video_data = NULL;
int video_size = 0;
int is_keyframe = (i % 100 == 0); // 每100帧一个关键帧
// 这里获取实际的H.264 NALU数据
// get_h264_frame(&video_data, &video_size, &is_keyframe);
if (video_size > 0) {
send_h264_frame(rtmp, video_data, video_size,
video_timestamp, is_keyframe);
video_timestamp += video_interval;
}
// 发送音频帧
uint8_t* audio_data = NULL;
int audio_size = 0;
// 这里获取实际的AAC帧数据
// get_aac_frame(&audio_data, &audio_size);
if (audio_size > 0) {
send_aac_frame(rtmp, audio_data, audio_size, audio_timestamp);
audio_timestamp += audio_interval;
}
// 简单同步,实际应用中需要更精确的时钟控制
usleep(10000); // 10ms
}
cleanup:
// 清理
if (rtmp) {
RTMP_Close(rtmp);
RTMP_Free(rtmp);
}
return 0;
}
5. 编译命令
复制代码
gcc -o rtmp_pusher rtmp_pusher.c -lrtmp -lz -lpthread
6. 注意事项
- 时间戳同步:音视频时间戳需要同步,通常使用同一个时钟基准
- NALU处理:H.264流需要按NALU单位发送,注意起始码(0x00000001)的处理
- 关键帧:需要定期发送关键帧,并在开头发送SPS/PPS
- 错误处理:需要处理网络断开、服务器错误等情况
- 线程安全:如果使用多线程推送音视频,需要做好同步