关闭

ffmpeg.c

577人阅读 评论(0) 收藏 举报
分类:

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

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()

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。

transcode_step()

transcode_step()调用了如下函数:
process_input():完成解码工作。

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


dup!

打印位置:

static void do_video_out(AVFormatContext *s,
                         OutputStream *ost,
                         AVFrame *next_picture,
                         double sync_ipts)
{

    if (nb_frames > (nb0_frames && ost->last_dropped) + (nb_frames > nb0_frames)) {
        if (nb_frames > dts_error_threshold * 30) {
            av_log(NULL, AV_LOG_ERROR, "%d frame duplication too large, skipping\n", nb_frames - 1);
            nb_frames_drop++;
            return;
        }
        nb_frames_dup += nb_frames - (nb0_frames && ost->last_dropped) - (nb_frames > nb0_frames);
        av_log(NULL, AV_LOG_VERBOSE, "*** %d dup!\n", nb_frames - 1);
    }

}

"Drop" means it has dropped a frame during encoding.
"Dup" means it has duplicated a frame during encoding.


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:565702次
    • 积分:8138
    • 等级:
    • 排名:第3087名
    • 原创:202篇
    • 转载:375篇
    • 译文:0篇
    • 评论:37条
    最新评论