Vlc部分功能流程图
block_FifoGet 获取编码的block |
DecoderProcess |
Decoder thread |
DecoderProcessVideo |
packetizer_Packetize将接受到的block进行packet组装为一个完整的NAL |
DecoderDecodeVideo 使用ffmpeg解码 |
DecodeVideo |
判断当前packet是否过期需要丢弃,无需解码 |
填充AVPacket,解码avcodec_decode_video2 |
Compute the PTS,Stream pts转换为system pts,并加上缓冲延时时间。判断当前帧是否已经滞后,并记统计滞后数目。解码下一帧时根据滞后标记,决定解码还是直接丢弃。更新p_pic->date =i_pts |
DecoderPlayVideo stream pts到system pts的转换,加上缓冲时间延迟。display thread会用于判断显示 |
Display thread |
ThreadDisplayPicture |
ThreadDisplayPreparePicture |
ThreadDisplayRenderPicture |
picture_fifo_Pop获取解码过的picture |
根据decoded->date与当前系统时间相减,若超过20ms直接丢弃,若不到则显示并打印提示滞后时间。 |
将decoded的picture赋值vout->p->displayed.decoded = picture_Hold(decoded); |
若current为空则直接赋值currentvout->p->displayed.current =picture;否则赋给next。vout->p->displayed.next=picture; |
date_refresh = vout->p->displayed.date +VOUT_REDISPLAY_DELAY - render_delay计算刷新时间,与当前比较决定是否refresh =date_refresh <= date; |
vout->p->displayed.current获取current显示的picture |
vout_display_Prepare进行subpic和todisplay的blend |
Direct3DImportPicture将picture的surface copy到系统texture的surface |
Direct3DImportSubpicture将subpicture blend入到texture |
vout_display_Display完成显示 |
1. 对于ts流和一些封装过的流在live555接收到数据未作处理直接调用block_FifoPut放入p_sys->block_fifo_t之中。 2. 需要Demux线程调用block_FifoGet(p_sys->p_fifo )分析数据去掉封装最后调用block_FifoPut(p_owner->p_fifo, p_block );放入到decoder_owner_sys_t的p_fifo中。对于直接是h264的数据的block是直接放入decoder_owner_sys_t的p_fifo中的。 |
解码线程
i_used = avcodec_decode_video2( p_context, p_sys->p_ff_pic,
&b_gotpicture, &pkt );
利用ffmpeg decode解码得到AVFrame即p_sys->p_ff_pic,里面包含有解码的frame的pts,vlc将对这个pts进行修正,确保时间戳的合法性。
渲染线程
ThreadDisplayPicture在渲染线程中执行。
vout_display_Prepare主要讲subpic与todisplay进行blend
解码线程与渲染线程的同步:
Decode thread和renderthrad通过picture_fifo_t对解码过的picture_t进行同步使用
sys->pool:Direct3DCreatePool调用picture_pool_NewExtended,picture_reserved为false,会创建count为1的pool
sys->decoder_pool:vout_InitWrapper会创建count为33的pool,前四个的picture_reserved设置为true。XXX3 for filter, 1 for SPU. 1 for last displayed picture,28 for decoder picture
sys->private_pool:picture_pool_Reserve(sys->decoder_pool,private_picture);同时将sys->decoder_pool的前四个的picture_reserved设置为true,并会创建count为4的pool。
以下使用ts stream作为source进行debug调试:
call stack
decode thread:
Decode thread get display date
input_clock_ConvertTS
根据ffmpeg解码给出的pts,pts转换为系统时间加上delay时间与当前系统时间比较判断当前frame是否过期。
判断当前帧是否滞后,并统计滞后的数目
更新picture的pts,直接等于ffmpeg解码给出的时间戳。
丢弃过期的block:
主要用于判断当前frame的时间戳是否已经过期,并统计过期的frame数目,当只有一帧数据时需要更新第一个过期时间,此时间可用于后面的frame是解码还是丢弃的时间参考。
一般由于网络延迟或者cpu解码性能不够导致。
Display thread:
prepare
Prepare render 时会将pts与当前的系统的时间进行比较,若有延迟则会log显示滞后时间。可以通过vlc的消息窗口显示当前的log信息。
消息窗口在vlc 的工具菜单选项下面,或者按快捷键Ctrl+M显示出来。图如下。
Buffering的过程:
i_buffering_duration的时常就是的预先设置好的缓冲时间,如1000ms,则i_buffering_duration = 1000*1000;
call stack:
typedef struct
{
/* Program ID */
int i_id;
/* Number of es for this pgrm */
int i_es;
bool b_selected;
bool b_scrambled;
/* Clock for this program */
input_clock_t *p_clock; //此处使用了这个clock
char *psz_name;
char *psz_now_playing;
char *psz_publisher;
}es_out_pgrm_t;
PCR: program clock reference
Ts stream block 更新pcr
根据streamclock和system clock创建clock_point_t。
Demux使用input_clock_Update将frame的stream时间和系统时间更新到clock->last中,而clock->ref参考时钟只有在cl->b_has_reference没有的情况下才会进行更新。
i_ck_stream的时间间隔一般为33.3ms。
ClockStreamToSystem,将当前的stream clock减去stream ref,再加上system ref,得到期望的时间;滞后时间即为当前时间减去cache的缓冲时间,再减去期望时间。通过滞后时间可以判断当前frame是否已经滞后。
具体stream和system的duration通过cl的ref和last来计算。
int input_clock_GetState( input_clock_t *cl,
mtime_t *pi_stream_start,mtime_t *pi_system_start,
mtime_t *pi_stream_duration,mtime_t *pi_system_duration )
{
vlc_mutex_lock( &cl->lock );
if( !cl->b_has_reference )
{
vlc_mutex_unlock( &cl->lock );
return VLC_EGENERIC;
}
*pi_stream_start = cl->ref.i_stream;
*pi_system_start = cl->ref.i_system;
*pi_stream_duration = cl->last.i_stream -cl->ref.i_stream;
*pi_system_duration = cl->last.i_system -cl->ref.i_system;
vlc_mutex_unlock( &cl->lock );
return VLC_SUCCESS;
}
render
network-caching参数主要用于修改pts。
/*PTS delay: request from demux first. This is required for
* access_demux and some special caseslike SDP demux. Otherwise,
* fallback to access */
if( demux_Control( in->p_demux,DEMUX_GET_PTS_DELAY,
&in->i_pts_delay ) )
Ts流的pts一般是以90khz为单位,计算时间戳时使用i_pcr *100 / 9,得到单位是us。
cl->last = clock_point_Create( i_ck_stream, i_ck_system );
调用clock_point_Create用于更新参考时钟的cl->last.i_stream和cl->last.i_system。
Clock referenceaverage counter
i_cr_average = var_GetInteger(p_input, "cr-average" );一般等于40
#define CLOCK_FREQINT64_C(1000000)
#define DEFAULT_PTS_DELAY (3*CLOCK_FREQ/10)
当设置network-caching=1000时,i_cr_average = 133;
const inti_cr_average = var_GetInteger( p_input, "cr-average" ) * i_pts_delay / DEFAULT_PTS_DELAY;
es_out_SetJitter( p_input->p->p_es_out,i_pts_delay, 0, i_cr_average );
streamclock convert to system clock
ClockStreamToSystem ( cl, *pi_ts0 + AvgGet( &cl->drift ) );
然后加上delay时间得到最终的时间戳。
返回的时间单位为us
About packetizer_Packetize