http://blog.csdn.net/shuiniu1224/article/details/24932869
在ffmpeg中,可以分别采用帧内多线程解码和帧间多线程解码,帧内多线程解码的的依据主要是帧内各宏块的参考宏块可能相同,需要相同参考宏块进行解码的宏块可以同时进行解码。同理,帧间多线程解码的依据也是由于各帧图像需要的参考帧可能相同,需要相同参考帧的帧可以同时进行解码,最容易理解的就是部分B帧的并行解码了。由于帧内多线程解码的效率并不是很高,因此我主要针对的是帧间多线程解码方法。
关于帧间多线程解码的内容可以参考网站http://blog.csdn.NET/bsplover/article/details/7542980和硕士论文《视频编解码算法的并行研究》,以及以下三张图片内容:






=================================
http://blog.csdn.net/xietao_live_cn/article/details/6452030
FFMPEG多线程解码
FFMPEG多线程解码器分为Frame级和Slice级两种,Slice级多线程同时解码一帧中不同的部分。Frame级多线程同时接受多帧码流,实现并行解码,当前帧处于显示状态时,未来的几帧已经在其他线程中被解码。
- 1. Slice Threading
FFmpeg中,dvvideo_decoder, ffv1_decoder, h264_decoder, mpeg2_video_decoder和mpeg_video_decoder均支持了Slice Threading。
实现方法是:首先为codecContext注册注册多线程处理函数excute(),Codec解码过程中处理Slice时调用avctx->excute()。excute()启动Slice解码工作线程开始多线程解码,同时快速返回开始下一Slice的解析和解码。
Frame Threading主线程和解码线程的同步如图1所示。

- 2. Frame Threading
X264编码的片子,同样1080P的尺寸的,才不到10G,而H264编码的就要20多G
x264是一个基于h.264的免费开源的视频Codec,属于后起之秀
H不开源,是商业的
X开源,是自由的
x264是一种免费的、具有更优秀算法的H.264/MPEG-4 AVC视频压缩编码格式。它同xvid一样都是开源项目,但x264是采用H.264标准的,而xvid是采用MPEG-4早期标准的。由于H.264是2003年正式发布的最新的视频编码标准,因此,在通常情况下,x264压缩出的视频文件在相同质量下要比xvid压缩出的文件要小,或者也可以说,在相同体积下比xvid压缩出的文件质量要好。它符合GPL许可证。
H.264
H.264是由国际电信联盟(ITU-T)所制定的新一代的视频压缩格式。H.264最具价值的部分无疑是更高的数据压缩比。在同等的图像质量条件下,H.264的数据压缩比能比当前DVD系统中使用的 MPEG-2高2-3倍,比MPEG-4高1.5-2倍。正因为如此,经过H.264压缩的视频数据,在网络传输过程中所需要的带宽更少,也更加经济。在 MPEG-2需要6Mbps的传输速率匹配时,H.264只需要1Mbps-2Mbps的传输速率。
与MPEG-4一样,经过H.264压缩的视频文件一般也是采用.avi 作为其后缀名,同样不容易辨认,只能通过解码器来自己识别。
目前为止支持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_await_progress()调用之前不能读,同样,包括加边draw_edges()在内的处理,在ff_thread_report_progress()调用之后,Buffer内容不能写。
每个线程都有以下四个状态。如图2所示,为了保证线程安全,若Codec未实现update_thread_context()和线程安全的get_buffer(),则必须在解码完成后才能将状态转换为STATUS_SETUP_FINISHED,意味着下一个线程只能在当前线程解码完成后才能开始解码。
而如图3所示,如果Codec实现update_thread_context()和线程安全的get_buffer(),线程状态可以在解码开始之前转换为STATUS_SETUP_FINISHED,这样,下一个线程就可能与当前线程并行。

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

======================
http://jmvc.blog.sohu.com/145356341.html
3. 多线程代码分析
(1)文档解读
分析完X264的基本架构,来看看多线程发挥力量的地方。X264自带的多线程介绍文档是本课题的必读文档,它存放在X264的DOC文件夹下。本文描述的大意是:当前的X264多线程模式已经放弃基于slice的并行编码,转而采用帧级和宏块级的并行,原因是slice并行需要采用slice group,会引入而外冗余降低编码效率。摘抄一段原文如下:
New threading method: frame-based
application calls x264
x264 runs B-adapt and ratecontrol (serial to the application, but parallel to the other x264 threads)
spawn a thread for this frame
thread runs encode in 1 slice, deblock, hpel filter
meanwhile x264 waits for the oldest thread to finish
return to application, but the rest of the threads continue running in the background
No additional threads are needed to decode the input, unless decoding+B-adapt is slower than slice+deblock+hpel, in which case an additional input thread would allow decoding in parallel to B-adapt.【3】
以上的说明意味着,X264采用B帧在编码时不作为参考帧,所以适宜对其进行并行。
(2)运行状况分析
先来看看x264_pthread_create被调用的地方,只有这些地方才实实在在的创建了线程。
x264_pthread_create( &h->thread_handle, NULL, (void*)x264_slices_write, h )
x264_pthread_create( &look_h->thread_handle, NULL, (void *)x264_lookahead_thread, look_h )
x264_pthread_create( &h->tid, NULL, (void*)read_frame_thread_int, h->next_args )
x264_pthread_create( pool->thread_handle+i, NULL, (void*)x264_threadpool_thread, pool ) //新增的

由上图的运行可以看出,在开启了--threads 4后。x264_slices_write()可以开启4个线程同时编码,而同时存在一个主线程和一个x264_lookahead_thread()线程。x264_slices_write()的优先级为低,原因是调用了
if( h->param.i_sync_lookahead )
x264_lower_thread_priority( 10 );
调低本线程的优先级。read_frame_thread_int()是读磁盘上的流数据信息,因为I/O和内存的不同步,所以应该分开线程处理。
在x264_encoder_open()中可以找到一下代码,可以看到对于x264_slices_write()和x264_lookahead_thread()都有被分配了专有的上下文变量,供单一线程使用。
for( i = 1; i < h->param.i_threads + !!h->param.i_sync_lookahead; i++ )
CHECKED_MALLOC( h->thread[i], sizeof(x264_t) );
(3)如何确保按指定线程数来开启线程编码?
按打印实验可以看到,假设使用--threads 4的参数选项,代码会同时开启4个x264_slices_write()线程,然后每编完一个帧(前面的一个线程返回后),一个新的被产生出来,使得x264_slices_write()线程总数保持在4个,这一过程的相关代码如下:
int x264_encoder_encode( x264_t *h,x264_nal_t **pp_nal, int *pi_nal,x264_picture_t *pic_in,
x264_picture_t *pic_out )
{
...
if( h->param.i_threads > 1)
{
int i = ++h->i_thread_phase;
int t = h->param.i_threads;
thread_current = h->thread[ i%t ];
thread_prev = h->thread[ (i-1)%t ];
thread_oldest = h->thread[ (i+1)%t ];
x264_thread_sync_context( thread_current, thread_prev );
x264_thread_sync_ratecontrol( thread_current, thread_prev, thread_oldest );
h = thread_current;
}
...
/* Write frame */
if( h->param.i_threads > 1 )
{
printf("x264_pthread_create\n");
if( x264_pthread_create( &h->thread_handle, NULL, (void*)x264_slices_write, h ) )
return -1;
h->b_thread_active = 1;
}
else
if( (intptr_t)x264_slices_write( h ) )
return -1;
return x264_encoder_frame_end( thread_oldest, thread_current, pp_nal, pi_nal, pic_out );
...
}
static int x264_encoder_frame_end( x264_t *h, x264_t *thread_current,x264_nal_t **pp_nal, int *pi_nal, x264_picture_t *pic_out )
{
...
if( h->b_thread_active )
{
void *ret = NULL;
x264_pthread_join( h->thread_handle, &ret );
if( (intptr_t)ret )
return (intptr_t)ret;
h->b_thread_active = 0;
}
...
}
从以上两个函数的代码段可以看到,h上下文中保持的线程不会多于4个, x264_pthread_create()根据主线程的调用,创建出x264_slices_write线程,然后thread_oldest被指定并被率控函数判断重设,当前的线程数还不足4的时候,thread_oldest指向新线程,h->b_thread_active为0,不能进入x264_encoder_frame_end()的相关代码,主线程继续循环创建x264_slices_write线程,当线程总数为4,这时thread_oldest指向4个线程中被判断最快返回的那个,这时h->b_thread_active=1将进入x264_pthread_join(),那样,该线程就将主线至于阻塞状态,直至thread_oldest完成,才能重现创建新线程,以此机制,保持指定数码的编码线程数。
(4)x264_lookahead_thread()线程的作用
在分析这个线程之前,来看看两个重要的线程控制函数:
//唤醒等待该条件变量的所有线程。如果没有等待的线程,则什么也不做。
#define x264_pthread_cond_broadcast pthread_cond_broadcast
//自动解锁互斥量(如同执行了 pthread_unlock_mutex),并等待条件变量触发。这时线程挂起,不占用 CPU
时间,直到条件变量被触发。在调用 pthread_cond_wait 之前,应用程序必须加锁互斥量。pthread_cond_wait 函数返回前,自动重新对互斥量加锁(如同执行了 pthread_lock_mutex)。
#define x264_pthread_cond_wait pthread_cond_wait
以下的代码是X264中x264_lookahead_thread代码经常阻塞的地方,
**************************代码段A********************************************
if( h->lookahead->next.i_size <= h->lookahead->i_slicetype_length )
{
while( !h->lookahead->ifbuf.i_size && !h->lookahead->b_exit_thread )
x264_pthread_cond_wait( &h->lookahead->ifbuf.cv_fill, &h->lookahead->ifbuf.mutex );
x264_pthread_mutex_unlock( &h->lookahead->ifbuf.mutex );
}
else
{
x264_pthread_mutex_unlock( &h->lookahead->ifbuf.mutex );
x264_lookahead_slicetype_decide( h );
}
这里是等待满足!h->lookahead->ifbuf.i_size && !h->lookahead->b_exit_thread 的条件,后一条件在正常编码过程是TRUE,因为不会无故退出线程。那么这里等待的其实是ifbuf.i_size为非0.查找相关代码,
这里的 ifbuf.i_size条件是在x264_synch_frame_list_push()得到满足的,这里在得到一个输入的新编码帧后将发出信号。
slist->list[ slist->i_size++ ] = frame;
x264_pthread_cond_broadcast( &slist->cv_fill );
在代码段A中,if( h->lookahead->next.i_size <= h->lookahead->i_slicetype_length )条件中,i_slicetype_length表示为了进行slice type的判断而缓存的帧,它的值有取决于h->frames.i_delay,由代码的初始化设定值决定(默认为40)。也就是说预存40帧的数值,进行slice type决定用。暂时不详细分析slice type判断的具体实现,它的大概思想是根据码率,GOP和失真状况的权衡,来进行帧类型选择,在类似实时通信场合,不允许B帧的使用,也不可能预存那么多帧,这样的处理没有意义。
回头看这里的处理意义,是阻塞线程,等待后续的输入帧,然后利用处理规则来决定其slice type,为slice编码准备帧。
(5)宏块级别的并行
在数据结构x264_frame_t中,有变量x264_pthread_cond_t cv; 该变量分别在下面的两个函数里被封装了阻塞和唤醒:
void x264_frame_cond_broadcast( x264_frame_t *frame, int i_lines_completed );
void x264_frame_cond_wait( x264_frame_t *frame, int i_lines_completed );
考查它们被调用的地方,
************代码B****************from x264_macroblock_analyse( )->x264_mb_analyse_init()
int thresh = pix_y + h->param.analyse.i_mv_range_thread;
for( i = (h->sh.i_type == SLICE_TYPE_B); i >= 0; i-- )
{
x264_frame_t **fref = i ? h->fref1 : h->fref0;
int i_ref = i ? h->i_ref1 : h->i_ref0;
for( j=0; j<i_ref; j++ )
{
x264_frame_cond_wait( fref[j], thresh );
thread_mvy_range = X264_MIN( thread_mvy_range, fref[j]->i_lines_completed - pix_y );
}
}
**************************代码C************************************from x264_fdec_filter_row()
if( h->param.i_threads > 1 && h->fdec->b_kept_as_ref )
{
x264_frame_cond_broadcast( h->fdec, mb_y*16 + (b_end ? 10000 : -(X264_THREAD_HEIGHT <<h->sh.b_mbaff)) );
}
从上面的代码段可以看到没完成图像一行的编码,便会使用mb_y*16 -X264_THREAD_HEIGH的值来尝试唤醒x264_pthread_cond_wait( &frame->cv, &frame->mutex ),要判断的条件是
mb_y*16 -X264_THREAD_HEIGH < thresh = pix_y + h->param.analyse.i_mv_range_thread;
后者作为一个设想的阈值,用于确保依赖于本帧的后续帧在编码时,本帧已经编码出若干行宏块,以后续编码帧的基础,那样可以设想的情形如下图,不过X264是以编码完整行为单位的。

本文的分析道这里告一段落,对于帧间多线程分析和宏块的并行优化,或按自己的应用做代码裁剪,可以通过改正上面的(4)(5)代码段来实现,在当前(四核CPU)的X264测试中,已有代码确实能够很好的利用多核资源,并行编码的话题会随硬件的升级而不断探索下去。
