利用librtmp实现h264和AAC音频的推流

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. 注意事项

  1. 时间戳同步:音视频时间戳需要同步,通常使用同一个时钟基准
  2. NALU处理:H.264流需要按NALU单位发送,注意起始码(0x00000001)的处理
  3. 关键帧:需要定期发送关键帧,并在开头发送SPS/PPS
  4. 错误处理:需要处理网络断开、服务器错误等情况
  5. 线程安全:如果使用多线程推送音视频,需要做好同步
相关推荐
hflmwl814 小时前
Wise Data Recovery找回误删的文档照片音乐和视频
音视频·datarecovery·找回误删的文档照片音乐和视频·找回误删的文档照片音乐视频
技灵AI14 小时前
AI 视频转场提示词怎么写?让前后画面衔接更自然
人工智能·prompt·aigc·音视频
youandyouyou16 小时前
跨区培训的屏幕共享怎么做才稳?同屏同步与弱网优化实践
音视频·网络通信
开开心心就好16 小时前
课堂随机点名工具,绿色版免安装双击即用
java·服务器·前端·ffmpeg·音视频·perl·symfony
美狐美颜SDK开放平台16 小时前
直播APP集成美颜SDK,如何梳理第三方组件的数据权限?
开发语言·人工智能·深度学习·音视频·第三方美颜sdk
l143723326718 小时前
批量视频硬字幕擦除多少钱?影响报价的关键因素全解析
音视频
重生六道18 小时前
B站视频时长统计:一段JS脚本搞定分P/合集总时长(附倍速计算)
开发语言·javascript·音视频
音视频牛哥18 小时前
不换摄像机,也能接入 WebRTC?SmartMediaKit RTSP/RTMP 转 WHIP 技术实践
音视频·webrtc·smartmediakit·rtsp转whip·rtmp转whip·whip推流·rtsp转webrtc播放
lll235269177919 小时前
Android APP短视频音频提取,主流工具怎么选
ffmpeg·音视频
乐橙开放平台20 小时前
门店监控想集中看?乐橙含光支持多品牌摄像头跨店管理
网络·笔记·物联网·自动化·音视频·智能家居