ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

ffmpeg 为视频添加背景音乐---单音频轨道stream

2021-05-11 20:57:27  阅读:189  来源: 互联网

标签:背景音乐 ffmpeg stream fmt ctx ret av out


我们知道多媒体文件是由多个stream组成,每个stream对应不同的内容。比如视频,音频,字幕。

如果要用A视频的图像,B视频的音乐,生成C视频。原理就是提取出A视频的视频流数据,B视频的的音频流数据,写入C视频的视频流和音频流中。

流程图如下

#include <stdlib.h>
#include <libavutil/log.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libavcodec/codec_par.h>
int make_out_file(double start_time, double end_time, char *audio_file_name, char *video_file_name, char *out_file_name);

int main(int argc, char *argv[])
{
    //输入4个参数  argv1 开始时间  argv2 结束时间  argv3 输入文件  argv3 输出文件
    double start_time, end_time;
    char *audio_file_name, *video_file_name, *out_file_name;
    av_log_set_level(AV_LOG_INFO);
    if (argc < 6)
    {
        av_log(NULL, AV_LOG_ERROR, "输入参数错误");
        return -1;
    }

    start_time = atoi(argv[1]);
    end_time = atoi(argv[2]);
    audio_file_name = argv[3];
    video_file_name = argv[4];
    out_file_name = argv[5];
    int ret = make_out_file(start_time, end_time, audio_file_name, video_file_name, out_file_name);
    return ret;
}

AVFormatContext *open_input_file(char *file_name)
{
    int ret = 0;
    AVFormatContext *fmt_ctx = NULL;
    ret = avformat_open_input(&fmt_ctx, file_name, NULL, NULL);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "打开输入文件失败 %s", file_name);
        return fmt_ctx;
    }
    ret = avformat_find_stream_info(fmt_ctx, NULL);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "获取输入文件流信息失败 %s", file_name);
        return fmt_ctx;
    }
    av_dump_format(fmt_ctx, 0, file_name, 0);
    return fmt_ctx;
}

int createStream(AVFormatContext *ofmt_ctx, AVStream *in_stream)
{
    AVStream *out_stream = avformat_new_stream(ofmt_ctx, NULL);
    int ret = 0;
    if (!out_stream)
    {
        av_log(NULL, AV_LOG_ERROR, "创建输出流失败");
        return -1;
    }
    ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "copy 流参数错误");
        return ret;
    }
    out_stream->codecpar->codec_tag = 0;
    return ret;
}

int write_data(AVFormatContext* in_fmt_ctx, int in_index, 
                AVFormatContext* out_fmt_ctx, int out_index,
                double start_time, double end_time){
    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.size = 0;
    pkt.data = NULL;

    int base_pts = 0;
    int base_dts = 0;
    int ret = 0;

    ret = av_seek_frame(in_fmt_ctx, -1, start_time * AV_TIME_BASE, AVSEEK_FLAG_ANY);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "移动的上下文到指定时间失败");
        return ret;
    }

    //写音频
    while (1)
    {
        AVStream *in_stream, *out_stream;
        ret = av_read_frame(in_fmt_ctx, &pkt);
        if (ret < 0)
            break;
        if (pkt.stream_index != in_index)
        {
            av_packet_unref(&pkt);
            continue;
        }
        in_stream = in_fmt_ctx->streams[in_index];
        out_stream = out_fmt_ctx->streams[out_index];

        //如果当前时间超过了要截取的时间,直接跳出循环
        if (av_q2d(in_stream->time_base) * pkt.pts > end_time)
        {
            av_packet_unref(&pkt);
            break;
        }

        //设置输出文件的dts和pts
        // av_log(NULL, AV_LOG_INFO, "writefreame pkt.pts = %lld\n", pkt.pts);
        if (base_pts == 0 || base_dts == 0)
        {

            base_dts = base_pts = pkt.pts;
            av_log(NULL, AV_LOG_INFO, "writefreame  base_pts = %d\n", base_pts);
        }
        av_log(NULL, AV_LOG_INFO, "writefreame  pkt.pts = %lld, dts = %lld\n", pkt.pts, pkt.dts);
        pkt.pts = av_rescale_q_rnd(pkt.pts - base_pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
        pkt.dts = av_rescale_q_rnd(pkt.dts - base_dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
        pkt.duration = (int)av_rescale_q((int64_t)pkt.duration, in_stream->time_base, out_stream->time_base);
        pkt.pos = -1;
        pkt.stream_index = out_index;
        av_log(NULL, AV_LOG_INFO, "writefreame 22222 pkt.pts = %lld, dts = %lld\n", pkt.pts, pkt.dts);

        ret = av_write_frame(out_fmt_ctx, &pkt);
        av_packet_unref(&pkt);
        if (ret < 0)
            break;
    }
    return ret;
}


int make_out_file(double start_time,
                  double end_time,
                  char *audio_file_name,
                  char *video_file_name,
                  char *out_file_name)
{
    int ret = 0;
    AVFormatContext *i_audio_fmt_ctx = NULL, *i_video_fmt_ctx = NULL, *ofmt_ctx = NULL;

    //第一步获取输入上下文

    i_audio_fmt_ctx = open_input_file(audio_file_name);
    if (!i_audio_fmt_ctx)
    {
        ret = -1;
        goto __ERROR;
    }

    i_video_fmt_ctx = open_input_file(video_file_name);
    if (!i_audio_fmt_ctx)
    {
        ret = -1;
        goto __ERROR;
    }

    //第二步获取输出上下文
    ret = avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_file_name);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "获取输出文件上下文失败");
        goto __ERROR;
    }

    int video_stream_index = av_find_best_stream(i_video_fmt_ctx,
                                                 AVMEDIA_TYPE_VIDEO,
                                                 -1,
                                                 -1,
                                                 NULL,
                                                 -1);
    av_log(NULL, AV_LOG_ERROR, "输入文件的视频频流索引是 videoStreamIndex=%d", video_stream_index);

    int audio_stream_index = av_find_best_stream(i_audio_fmt_ctx,
                                                 AVMEDIA_TYPE_AUDIO,
                                                 -1,
                                                 -1,
                                                 NULL,
                                                 -1);
    av_log(NULL, AV_LOG_ERROR, "输入文件的视频频流索引是 videoStreamIndex=%d", audio_stream_index);



    //第三步为输出文件创建输出流
    int out_video_stream_index = -1;
    int out_audio_stream_index = -1;
    ret = createStream(ofmt_ctx, i_video_fmt_ctx->streams[video_stream_index]);
    if(ret < 0){
        goto __ERROR;
    }
    out_video_stream_index = 0;

    ret = createStream(ofmt_ctx, i_audio_fmt_ctx->streams[audio_stream_index]);
    if(ret < 0){
        goto __ERROR;
    }
    out_audio_stream_index = 1;
    av_dump_format(ofmt_ctx, 0, out_file_name, 1);


    //第四步为输出文件创建AvIOContext
    if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
    {
        ret = avio_open(&(ofmt_ctx->pb), out_file_name, AVIO_FLAG_WRITE);
        if (ret < 0)
        {
            av_log(NULL, AV_LOG_ERROR, "创建输出文件IO上下文失败");
            goto __ERROR;
        }
    }

    //第五步写输出文件头
    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "写多媒体文件头失败");
        goto __ERROR;
    }



    //第六步读取指定时间段的数据,然后进行时间戳转化,再写入输出文件
    ret = write_data(i_video_fmt_ctx, video_stream_index, 
                ofmt_ctx, out_video_stream_index,
                start_time, end_time);
    if (ret < 0)
    {
        goto __ERROR;
    }          
   
    ret =  write_data(i_audio_fmt_ctx, audio_stream_index, 
                ofmt_ctx, out_audio_stream_index,
                start_time, end_time);
    if (ret < 0)
    {
        goto __ERROR;
    }   
   

    //第七步写多媒体文件尾巴
    av_write_trailer(ofmt_ctx);

__ERROR:
    //第八步释放资源
    if (i_audio_fmt_ctx)
    {
        avformat_close_input(&i_audio_fmt_ctx);
        avformat_free_context(i_audio_fmt_ctx);
    }

    if (i_video_fmt_ctx)
    {
        avformat_close_input(&i_video_fmt_ctx);
        avformat_free_context(i_video_fmt_ctx);
    }

    if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
    {
        avio_closep(&(ofmt_ctx->pb));
    }

    if (ofmt_ctx)
    {
        avformat_free_context(ofmt_ctx);
    }
    return ret;
}

坚持输出,才能不停的输入。

学习基础,才能学得更深。

学习要慢点再慢点。

学习原理,对抗碎片。

标签:背景音乐,ffmpeg,stream,fmt,ctx,ret,av,out
来源: https://blog.csdn.net/qq_15255121/article/details/116668946

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有