ffmpeg推流 —— RTMP推流例程

参考:

雷霄骅 - 最简单的基于FFmpeg的推流器(以推送RTMP为例)

nginx+rtmp配置,网页播放rtmp流(实测可用)


RTMP推流例程:

#include <stdio.h>
#include "libavformat/avformat.h"
#include "libavutil/time.h"
#include "libavutil/mathematics.h"

#define RTMP_ADDR "rtmp://127.0.0.1:1935/live/1234"

void fix_packet_pts(AVPacket *packet, AVRational timebase, int frame_index) {
    // 两帧之间的持续时间
    // r_frame_rate = (AVRational) {den , num}, 与time_base结构相反
    AVRational in_frame_rate = {
        timebase.num,
        timebase.den,
    };
    int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(in_frame_rate); //in_stream->r_frame_rate

    packet->pts = (double)(frame_index * calc_duration) / (double)(av_q2d(timebase)*AV_TIME_BASE);
    packet->dts = packet->pts;
    packet->duration = (double)calc_duration / (double)(av_q2d(timebase)*AV_TIME_BASE);
}

void send_rtmp(const char *file) {
    // 输入
    AVFormatContext *ifmt_ctx = NULL;
    if (avformat_open_input(&ifmt_ctx, file, 0, 0) < 0) {
        printf("failed to open input file\n");
        goto _Error;
    }

    if (avformat_find_stream_info(ifmt_ctx, 0) < 0) {
        printf("failed to find stream info\n");
        goto _Error;
    }

    int video_idx = -1; 
    video_idx = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if (video_idx < 0) {
        printf("failed to find stream_index\n");
        goto _Error;
    }
    av_dump_format(ifmt_ctx, 0, file, 0);
        
    
    // 输出
    AVFormatContext *ofmt_ctx = NULL;
    if (avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", RTMP_ADDR) < 0) {
        printf("failed to alloc output context\n");
        goto _Error;
    }
    int out_stm_idx = -1;
    for (int i = 0; i < ifmt_ctx->nb_streams; i++) { 
        AVStream *in_stream = ifmt_ctx->streams[i];
        AVStream *out_stream = avformat_new_stream(ofmt_ctx, NULL);
        avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);

        out_stream->codecpar->codec_tag = 0;
        //if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
        //    out_stream->codecpar->flags |= CODEC_FLAG_GLOBAL_HEADER;
        //}
    }
    av_dump_format(ofmt_ctx, 0, RTMP_ADDR, 1);

    // open output url
    if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
        if (avio_open(&ofmt_ctx->pb, RTMP_ADDR, AVIO_FLAG_WRITE) < 0) {
            printf("failed to open output url:%s\n", RTMP_ADDR);
            goto _Error;
        }
    }
    if (avformat_write_header(ofmt_ctx, NULL) < 0) {
        printf("failed to write header\n");
        goto _Error;
    }


    int frame_index = 0;
    int64_t start_time = av_gettime();
    AVPacket *packet = av_packet_alloc();
    while (1) {
        if (av_read_frame(ifmt_ctx, packet) < 0) break;
        
        // raw h264, doesn't contain pts
        // 如果实际推流的是flv文件,不会执行里面的fix_packet_pts
        if (packet->pts == AV_NOPTS_VALUE) {
            fix_packet_pts(packet, ifmt_ctx->streams[video_idx]->time_base, frame_index); 
        }
        // important delay
        if (packet->stream_index == video_idx) {
            AVRational timebase = ifmt_ctx->streams[video_idx]->time_base;
            AVRational timebase_q = {1, AV_TIME_BASE};
            int64_t pts_time = av_rescale_q(packet->pts, timebase, timebase_q);
            int64_t now_time = av_gettime() - start_time;
            if (pts_time > now_time) 
                av_usleep(pts_time - now_time);
        }
        
        // rescale time base
        av_packet_rescale_ts(packet, ifmt_ctx->streams[packet->stream_index]->time_base, \
                ofmt_ctx->streams[packet->stream_index]->time_base);
        packet->pos = -1;
        if (packet->stream_index == video_idx) {
            printf("send %6d video frames to output url\n", frame_index);
            frame_index++;
        }

        if (av_interleaved_write_frame(ofmt_ctx, packet) < 0) {
            printf("failed to write frame to url\n");
            break;
        }

        av_packet_unref(packet);
    }
    
    av_write_trailer(ofmt_ctx);

_Error:
    if (ifmt_ctx) {
        avformat_close_input(&ifmt_ctx);
    }
    if (ofmt_ctx) {
        if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) 
            avio_close(ofmt_ctx->pb);
        avformat_free_context(ofmt_ctx);
    }
    if (packet) av_packet_free(&packet);
}

int main(int argc, char const* argv[])
{
    send_rtmp(argv[1]); 
    return 0;
}

测试运行:
image-20210527223038034
image-20210527225751575


出现了一个警告,如下图。大致原因就是找不到视频的时长和文件大小信息,我们不理会也是没有问题的。

image-20210527223005126
实际用ffmpeg命令推流时也会遇到这个问题,ffmpeg -re -i ../../testvideo/chinese_partner.flv -vcodec libx264 -f flv rtmp://127.0.0.1:1935/live/1234
image-20210527223652295
可以添加-flvflags no_duration_filesize 参数,让ffmpeg不要抛出duration_filesize警告。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值