ffmpeg推流到rtmp服务器---简洁代码输出

前言:

原先通过windows下搭建RTMP视频服务器_windows 下rtmp搭建-CSDN博客 已经可以通过命令方式推流到rtmp服务器,如下是通过ffmpeg源码编译的简洁方式

1.代码

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

int main()
{
    int iRet = 0, video_idx = -1, frame_idx = 0;
    av_log_set_level(AV_LOG_INFO);
    const char *input_file_name = "/home/sulier/work/AudioVedio/VedioFile/59733_720p.mp4";
    const char *output_file_name = "rtmp://192.168.36.1:9004:/live/play59733";

    AVFormatContext *input_ctx = NULL, *output_ctx = NULL;
    //1.输入
    iRet = avformat_open_input(&input_ctx, input_file_name, NULL, NULL);
    if (iRet < 0)
    {
        av_log(NULL, AV_LOG_INFO, "avformat_open_input fail\n");
        return iRet;
    }
    for (int i = 0; i < input_ctx->nb_streams; i++) {
        if (input_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_idx = i;
            break;
        }
    }
    //2.分析流
    iRet = avformat_find_stream_info(input_ctx, NULL);
    if (iRet < 0)
    {
        av_log(NULL, AV_LOG_INFO, "cannot find stream information\n");
        return iRet;
    }
    //3.转储输入格式
    av_dump_format(input_ctx, 0, input_file_name, 0);
    //4.输出
    iRet = avformat_alloc_output_context2(&output_ctx, NULL, "flv", output_file_name);
    if (iRet < 0)
    {
        av_log(NULL, AV_LOG_INFO, "output fail\n");
        return iRet;
    }
    //5.输入输出流配置
    for (int i = 0; i < input_ctx->nb_streams; i++)
    {
        const AVCodec *pICodec;
        pICodec = avcodec_find_decoder(input_ctx->streams[i]->codecpar->codec_id);
        AVStream *pOStream = avformat_new_stream(output_ctx, pICodec);
        if (pOStream == NULL)
        {
            av_log(NULL, AV_LOG_INFO, "avformat_new_stream fail at %d\n", i);
            return -1;
        }
        avcodec_parameters_copy(pOStream->codecpar, input_ctx->streams[i]->codecpar);
        //裸码流的时候其实可以不用,但是如果是mp4之类的就需要
        pOStream->codecpar->codec_tag = 0;
    }
    //6.转储输出格式
    av_dump_format(output_ctx, 0, output_file_name, 1);

    iRet = avio_open(&output_ctx->pb, output_file_name, AVIO_FLAG_WRITE);
    if (iRet < 0)
    {
        av_log(NULL, AV_LOG_INFO, "avio_open fail\n");
        return iRet;
    }
    //7.写文件头
    iRet = avformat_write_header(output_ctx, NULL);
    if (iRet < 0)
    {
        av_log(NULL, AV_LOG_INFO, "avformat_write_header fail\n");
        return iRet;
    }
    //8.打包申请
    AVPacket *av_pkt = av_packet_alloc();
    //9.读写帧
    int64_t startTime = av_gettime();
    while(1)
    {
        AVStream* in_stream;
        AVStream* out_stream;

        iRet = av_read_frame(input_ctx, av_pkt);
        if (iRet < 0) {
            break;
        }

        // write pts
        if (av_pkt->pts == AV_NOPTS_VALUE) {
            AVRational time_base1 = input_ctx->streams[video_idx]->time_base;
            // 这里需要使用平均帧率而不是使用实时帧率,不然最后播放时长有问题
            int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(input_ctx->streams[video_idx]->avg_frame_rate);
            // parameters
            // pts是播放时间戳,告诉播放器什么时候播放这一帧视频,PTS通常是按照递增顺序排列的,以保证正确的时间顺序和播放同步
            // dts是解码时间戳,告诉播放器什么时候解码这一帧视频
            av_pkt->pts = (double)(frame_idx * calc_duration) / (double)(av_q2d(time_base1) * AV_TIME_BASE);
            av_pkt->dts = av_pkt->pts;
            av_pkt->duration = (double)calc_duration / (double)(av_q2d(time_base1) * AV_TIME_BASE);
        }

        //计算当前帧应该运行的时间
        int64_t pts_time = 0;
        if (av_pkt->stream_index == video_idx) {
            AVRational time_base = input_ctx->streams[video_idx]->time_base;
            AVRational time_base_q = { 1, AV_TIME_BASE };
            pts_time = av_rescale_q(av_pkt->dts, time_base, time_base_q);
        }

        in_stream = input_ctx->streams[av_pkt->stream_index];
        out_stream = output_ctx->streams[av_pkt->stream_index];

        // copy packet
        // convert PTS/DTS
        av_pkt->pts = av_rescale_q_rnd(av_pkt->pts, in_stream->time_base, out_stream->time_base, (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        av_pkt->dts = av_rescale_q_rnd(av_pkt->dts, in_stream->time_base, out_stream->time_base, (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        av_pkt->duration = av_rescale_q(av_pkt->duration, in_stream->time_base, out_stream->time_base);
        av_pkt->pos = -1;

        // print to screen
        if (av_pkt->stream_index == video_idx) {
            frame_idx++;
        }

        iRet = av_interleaved_write_frame(output_ctx, av_pkt);
        if (iRet < 0) {
            av_log(NULL, AV_LOG_INFO, "av_interleaved_write_frame fail\n");
            break;
        }
        if (av_pkt->stream_index == video_idx) {
            int64_t now_time = av_gettime() - startTime;
            if (pts_time > now_time) {
                //av_log(NULL, AV_LOG_INFO, "pts_time: %" PRId64 " now_time%" PRId64 " usleep: %" PRId64 "\n", pts_time, now_time, pts_time-now_time);
                av_usleep(pts_time - now_time);
            }
        }
        av_packet_unref(av_pkt);
    }

    //10.写文件尾
    av_write_trailer(output_ctx);
    //11.关闭释放输入上下文
    avformat_free_context(input_ctx);
    //12.关闭释放输出上下文
    if (output_ctx->pb)
    {
        avio_closep(&output_ctx->pb);
    }
    avformat_free_context(output_ctx);
    av_packet_free(&av_pkt);
    return 0;
}

注意:codec_tag 在非裸码流时进入输出为flv格式的RTMP接收服务器时,会在

flv_write_header-》flv_write_codec_header-》avio_w8  (这是给我做笔记的,你们可以忽略)

2.编译

 编译的前提是安装了ffmpeg,下面用到的依赖库以及头文件换成自己的就行

参考linux下ffmpeg安装-CSDN博客

gcc -o main main.c -lavutil -lavformat -lavcodec -L/home/sulier/work/AudioVedio/ffmpeg_third/lib/ -I/home/sulier/work/AudioVedio/ffmpeg_third/include/ -g

3.运行

windows下搭建RTMP视频服务器_windows 下rtmp搭建-CSDN博客

其中ffmpeg推流操作换成我们的编译代码执行就行

4.效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值