FFMPEG-0.11.1分析之ffmpeg结构(简单涉及)与代码流程(数据流、多线程)【很乱,建议直接看其中包含的原文连接】

1.      【数据流】【这里暂省略,内存的管理,只记录简单流程,不说每个数据包在哪里出生,怎么流向,在哪里销毁了。】

可参考链接:http://www.rosoo.net/a/201207/16135.html

 

比特流:流:

这里说的是URL、IO端,仅仅是个人给他取个名字说是,比特流、流,没有特殊意义。

 

包:

 

前端是demux,文件以流的形式在IO打开,opt_input_file—avformat_open_input中调用s->iformat->read_header(s)进行文件头信息的解析,然后再transcode中,从input_files[file_index]->ctx获取格式上下文,av_read_frame(is,&pkt)从文件中将数据以包的形式读出来,单个包中是整帧数据还是片,看具体容器格式(rvmb中多包一帧见得较多)。接着在output_packet(InputStream *ist, const AVPacket *pkt)中,拷贝一下当前包,然后转入解码端。

 

后端是mux,比如转码时音视频编解码信息,有些手动设置,有些默认,在transcode_init—avformat_write_header,调用AVOutputFormat. write_header,文件的头信息(比如,flv、avi、3gp在文件起始,存储了很多信息,有些标记音视频编解码参数,有些指示媒体数据或其他数据在接下来文件中的排布,它们遵从各自的文件协议。在后端编码没有完全结束时,有些类型的文件头部信息也是没有完结的,其中flv不是,它头信息与媒体负载是无关的)。

output_packet—do_streamcopy—write_frame—av_interleaved_write_frame—s->oformat->write_packet中写入包数据,而IO紧接着就在mux的下层,顺理成章即可。


帧:

 

解码器的打开avcodec_open2在init_input_stream invoked by transcode_init,同时其中AVCodecContext.get_buffer/release_buffer得到static intcodec_get_buffer(AVCodecContext *s, AVFrame *frame)/ static voidcodec_release_buffer(AVCodecContext *s, AVFrame *frame),get_buffer中从InputStream. buffer_pool中取帧,InputStream是管理解码后的数据的结构,buffer_pool是一个FrameBuffer链表。

decode_video(InputStream *ist, AVPacket *pkt, int*got_output)中,在InputStream中取AVFrame,avcodec_decode_video2中解码,单线程和多线程片解码走vctx->codec->decode,多线程帧解码走ff_thread_decode_frame。 

 FFMPEG-0.11.1分析之ffmpeg结构(简单涉及)与代码流程(数据流、多线程)【很乱,建议直接看其中包含的原文连接】_第1张图片

箭头是指针引用关系,直线是平行关系。呵呵,大家可以看下代码,这里画一下,就是说明他们有关联。

 

另外,同步问题,大家也要注意下。

 

这有一张图插在这里,有用,来自:http://www.rosoo.net/a/201207/16135.html

FFMPEG-0.11.1分析之ffmpeg结构(简单涉及)与代码流程(数据流、多线程)【很乱,建议直接看其中包含的原文连接】_第2张图片

 

解码出来的数据,经过pre_process_video_frame(去边?在解码中会有加边处理),OutputStream是用来管理将要编码之前的数据,在new_output_stream中ost->sync_ist = input_streams[source_index]将两者联系在一起。在transcode—init_simple_filtergraph中,ist->filters[ist->nb_filters- 1] = fg->inputs[0]符合了。下面还是看一下filters,不然很难理清头绪,这是ffmpeg中新的机制。

 

ffmpeg新版本中的filter可参考:http://blog.csdn.net/nkmnkm/article/details/7219641

AVFilterGraph:几乎完全等同与directShow中的fitlerGraph,代表一串连接起来的filter.【对照起来看,容易理解,很快可以上手】
AVFilter:
代表一个filter.
AVFilterPad:
代表一个filter的输入或输出口,等同于DShow中的Pin.只有输出padfiltersource,只有输入padfiltersink.
AVFilterLink:
代表两个连接的fitler之间的粘合物.

1 产生graph: AVFilterGraph*graph = avfilter_graph_alloc();
2 创建source
AVFilterContext *filt_src;
avfilter_graph_create_filter(&filt_src,&input_filter, "src",NULL, is, graph);
3 创建sink
AVFilterContext *filt_out;

ret = avfilter_graph_create_filter(&filt_out,avfilter_get_by_name("buffersink"), "out", NULL, pix_fmts,graph);
4 连接sourcesink:avfilter_link(filt_src, 0, filt_out, 0);
5 graph做最后的检查: avfilter_graph_config(graph, NULL);
我们是从sink中取出处理完成的帧,所以最好把sink的引用保存下来,比如:
AVFilterContext *out_video_filter=filt_out;

6实现input_filter

 

在transcode_init中configure_simple_filtergraph—configure_video_filters做了配置。在poll_filters中调用av_buffersink_read或av_buffersink_get_buffer_ref,这里缩放就是用这个机制实现的,取代了先前的swscale,不过这个版本的代码有些乱。

 

编码器的打开是在transcode_init中,avcodec_open2(ost->st->codec, codec, &ost->opts),avctx->codec->init初始化。在output_packet中,但有if(!check_output_constraints(ist, ost) || ost->encoding_needed)判断,poll_filters—do_video_out—avcodec_encode_video2中进行编码,接着write_frame打包入容器。

 

2.      【多线程】(-threads n 、解码多线程)(如果要真正支持多线程,需要编译的时候,加入线程库pthread

建议参考:http://www.360doc.com/content/12/0416/11/474846_204064235.shtml

 

解码多线程:

Avcodec_open2中,ff_lockmgr_cb互斥锁,相关函数av_lockmgr_register、avpriv_lock_avformat、avpriv_unlock_avformat,entangled_thread_counter变量在

avcodec_open2做简单标记。ff_thread_init中validate_thread_parameters注意下,如h264解码的AVCodec .capabilities=/*CODEC_CAP_DRAW_HORIZ_BAND |*/ CODEC_CAP_DR1 | CODEC_CAP_DELAY |CODEC_CAP_SLICE_THREADS |CODEC_CAP_FRAME_THREADS,这里说明其能支持的类型,并且在ifelse选择是FF_THREAD_FRAME先于FF_THREAD_SLICE。

 

(linux)线程相关的函数在pthread.c中,可以参阅以下,ff_thread_init下面会调用到frame_thread_init,里面有frame_worker_thread工作线程。

 

要注意AVCodecContext.thread_opaque以及execute、execute2的赋值。

 

SLICE_THREADING:

Ffmpeg中,dvvideo_decoder,ffv1_decoder,h264_decoder,mpeg2_video_decoder和mpeg_video_decoder均支持FF_THREAD_SLICE(slice可以是整帧,可也以帧的分割)。如果线程类型是片,则在ff_thread_init是调用thread_init(AVCodecContext *avctx)做初始化,(顺便说一下,这个函数里可以看到,线程的auto选择是先获取cpu数目,FFMIN(nb_cpus + 1, MAX_AUTO_THREADS)),接着pthread_create(&c->workers[i],NULL,worker, avctx)创建线程,avcodec_thread_park_workers,再然后execute =avcodec_thread_execute、execute2 = avcodec_thread_execute2。

这里注册完AVCodecContext的多线程处理函数excute,Codec解码过程中处理slice时调用avctx->excute(),excute启动slice解码工作线程开始多线程解码,同时快速返回开始下一slice的解析和解码。FrameThreading主线程和解码线程的同步如下图:

 FFMPEG-0.11.1分析之ffmpeg结构(简单涉及)与代码流程(数据流、多线程)【很乱,建议直接看其中包含的原文连接】_第3张图片

FRAME_THREADING:

目前支持Frame Threading的解码器有h264_decoder,huffyuv_decoder, ffvhuff_decoder, mdec_decoder, mimic_decoder, mpeg4_decoder,theora_decoder, vp3_decoder和vp8_decoder。

Frame Threading有如下限制:用户函数draw_horiz_band()必须是线程安全的;为了提升性能,用户应该为codec提供线程安全的get_buffer()回调函数,用户必须能处理多线程带来的延时。另外,支持frame Threading的codec要求每个包包含一个完整帧。Buffer内容在ff_thread_report_progress()调用之后,buffer内容不能写。

每个线程都有以下四个状态,如图2所示,为了保证线程安全,若codec未实现update_thread_context()和线程安全的get_buffer(),则必须解码完成后才能将状态转换为STATUS_SETUP_FINISHED,意味着下一个线程只能在当前线程解码完成后才能开始解码。

FFMPEG-0.11.1分析之ffmpeg结构(简单涉及)与代码流程(数据流、多线程)【很乱,建议直接看其中包含的原文连接】_第4张图片

而入兔三所示,如果codec实现update_thread_context()和线程安全的get_buffer(),线程状态可以再解码开始之前转换为STATUS_SETUP_FINISHED,这样,下一个线程就可能与当前线程并行。

FFMPEG-0.11.1分析之ffmpeg结构(简单涉及)与代码流程(数据流、多线程)【很乱,建议直接看其中包含的原文连接】_第5张图片

解码主线程通过调用submit_packet将码流交给对应的解码线程。主线程和解码线程的同步如图4所示。


图中可以看到,主线程按序递交packet,由work线程处理。

【文中已标出参考文章,建议大家看原文,本文只是鄙人理思路记录的碎片】

你可能感兴趣的:(FFMPEG-0.11.1分析之ffmpeg结构(简单涉及)与代码流程(数据流、多线程)【很乱,建议直接看其中包含的原文连接】)