ffmpeg.c总体调用

本文主要以转码为例子,流程主要为解析命令,转码,转码又可以细分为:打开编解码器,开始转码,关闭编解码器。

FFmpeg的总体函数调用结构图如下图所示:

命令解析

ffmpeg_parse_options

命令解析主要在ret = ffmpeg_parse_options(argc, argv);

find_option

 po = find_option(options, opt);  //从全局const OptionDef options[]中查找opt,opt是字符串,对应OptionDef的第一列。

void opt_output_file(void *optctx, const char *filename)
{
    //略…
    err = avformat_alloc_output_context2(&oc, NULL, o->format, filename);
 
    if (!oc) {
        print_error(filename, err);
        exit(1);
    }
    //略…
   new_video_stream();
   …
   new_audio_stream();
   …
   new_subtitle_stream ();
    //略…
}

avformat_find_stream_info

        avformat_find_stream_info()函数可以读取一部分视音频数据并且获得一些相关的信息。函数正常执行后返回值大于等于0。该函数上下文部分是调节起播延迟效果最明显的地方。函数分析流的时间主要由传入的AVFormatContext的probesize 和 max_analyze_duration两个属性决定,probesize是探测读入大小,默认值为32K;max_analyze_duration默认值为5S。在网络状况比较好的情况下可分别设置4K和1S,也可根据具体情况在起播画面效果、起播延迟、分析成功率等因素间取舍。这两个参数的设置在avformat_open_input()函数对结构体AVFormatContext处理完成后进行设置。


在ffmpeg_parse_options中,会调用libavfornat/utils.c里的avformat_find_stream_info,其中会会调用try_decode_frame(avcodec_open2,avcodec_decode_xxx,),avcodec_close;重要步骤为:
1.查找解码器:find_decoder()
2.打开解码器:avcodec_open2()
3.读取完整的一帧压缩编码的数据:read_frame_internal()
4.解码一些压缩编码数据:try_decode_frame()

choose_output()

用于选择一个OutputStream。比如有一个audio,一个video,那要根据pts策略,比如谁的pts比较小,就挑哪个OutputStream先干活。
transcode_frome_filter()函数用于选个一个InputStream,用于下一步的process_input()。
process_input()函数主要是解码,并把解码的buffer送往filter处理,送入滤镜。

reap_filters()函数主要是,从filter的FIFO拿出buffer,并编码。

do_video_out(AVFormatContext*s...   //准备编码YUV数据。

static OutputStream *choose_output(void)
{
    int i;
    int64_t opts_min = INT64_MAX;
    OutputStream *ost_min = NULL;
    for (i = 0; i < nb_output_streams; i++) {
        OutputStream *ost = output_streams[i];
        int64_t opts = ost->st->cur_dts == AV_NOPTS_VALUE ? INT64_MIN :
                       av_rescale_q(ost->st->cur_dts, ost->st->time_base,
                                    AV_TIME_BASE_Q);
        if (ost->st->cur_dts == AV_NOPTS_VALUE)
            av_log(NULL, AV_LOG_DEBUG, "cur_dts is invalid (this is harmless if it occurs once at the start per stream)\n");

        if (!ost->finished && opts < opts_min) {
            opts_min = opts;
            ost_min  = ost->unavailable ? NULL : ost;
        }

    }

  //比如;输出是双码率,nb_output_streams就是4(一个视频,一个音频),从4个输出里找最小的,即为:opts_min。

    return ost_min;
}

update_stream_timings
start_time1 = av_rescale_q(st->start_time, st->time_base,AV_TIME_BASE_Q);
start_time = FFMIN(start_time, start_time1);
ic->start_time = start_time;  //90000基转成1000000基,修改了开始时间。如133200变成1480000。

try_decode_frame
从try_decode_frame()的定义可以看出,该函数首先判断视音频流的解码器是否已经打开,如果没有打开的话,先打开相应的解码器。接下来根据视音频流类型的不同,调用不同的解码函数进行解码:视频流调用avcodec_decode_video2(),音频流调用avcodec_decode_audio4(),字幕流调用avcodec_decode_subtitle2()。解码的循环会一直持续下去直到满足了while()的所有条件。while()语句的条件中有一个has_codec_parameters()函数,用于判断AVStream中的成员变量是否都已经设置完毕。该函数在avformat_find_stream_info()中的多个地方被使用过。

转码

转码的内容封装在transcode函数里,会重新打开解码器,编码器。

# 打开编解码器

如上图,打开编码器,到最后还是调用具体的编码器(X264编码器),X264初始化的函数为int X264_init(AVCodecContext *avctx),可以看出ffmpeg通过AVCodecContext结构体,将编码参数传递给x264的x264_param_t。

process_input

主要是解码,并把解码的buffer送往filter处理。

process_input_packet

该函数对帧数据进行解码并通过所有适用的过滤器进行处理。时间戳校准和字幕处理的工作也在这个函数中进行。最后,在函数返回之前,已解码的帧被复制到每个相关的输出流。

复制解码后的帧

static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eof)
{......

for (i = 0; pkt && i < nb_output_streams; i++) {

  ...  // check constraints
  do_streamcopy(ist, ost, pkt);   

}  ......

}

transcode_step()

transcode_step()调用了如下函数:

process_input():完成解码工作。

transcode_step()函数调用reap_filters()函数(第1424行)来循环遍历每个输出流。reap_filters()函数的for循环负责收集缓冲区中待处理的帧,并将这些帧进行编码,然后封装到一个输出文件中。

reap_filters()
reap_filters()主要完成了编码的工作。其函数调用结构如下图所示。

reap_filters()调用了如下函数
av_buffersink_get_buffer_ref():从AVFilterContext中取出一帧解码后的数据(结构为AVFilterBufferRef,可以转换为AVFrame)。
avfilter_copy_buf_props():AVFilterBufferRef转换为AVFrame。
do_audio_out():编码音频。
do_video_out():编码视频。
avfilter_unref_buffer():释放资源。
 
do_video_out()调用了如下函数
avcodec_encode_video2():编码一帧视频。
write_frame():写入编码后的视频压缩数据。
 
write_frame()调用了如下函数:
av_bitstream_filter_filter():使用AVBitStreamFilter的时候,会调用此函数进行处理。
av_interleaved_write_frame():写入压缩编码数据。
 
do_audio_out()调用的函数与do_video_out()基本上一样。唯一的不同在于视频编码函数avcodec_encode_video2()变成了音频编码函数avcodec_encode_audio2()。

copy怎么工作?
在main()->ffmpeg-parse_options()->open_files()->open_output_file()->new_video->stream()->new_output_stream()->choose_encoder()有:
if (!strcmp(codec_name, "copy"))
        ost->stream_copy = 1;

另一个地方会        ost->encoding_needed = !ost->stream_copy,表明是否需要解码。
在transcode_init中
 if (ost->stream_copy) {
    所有的参数都用解码后得到的参数。
}else{
   使用转码参数。
   调用init_simple_filtergraph();有这句ost->filter = fg->outputs[0];这样ost->filter就有了值,在reap_filters就会进行编码(会判断ost->filter)。
}


多线程问题

#要想使用多线程,需要编译的时候加上--enable-pthread。

#有些编解码不支持多线程,配置了线程数也不会起作用。

参考:http://blog.csdn.net/leixiaohua1020/article/details/39760711




  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山西茄子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值