第一部分 变量及宏定义
1.消息映射宏
vlc_module_begin();
…………………..
vlc_module_end();
2.结构中包含函数
struct input_thread_t
{
VLC_COMMON_MEMBERS
vlc_bool_t b_eof;
vlc_bool_t b_out_pace_control;
module_t * p_access;
ssize_t (* pf_read ) ( input_thread_t *, byte_t *, size_t);
int (* pf_set_program )( input_thread_t *, pgrm_descriptor_t *);
int (* pf_set_area )( input_thread_t *, input_area_t * );
void (* pf_seek ) ( input_thread_t *, off_t );
}
3.宏与换行符妙用
#define VLC_COMMON_MEMBERS int i_object_id; int
i_object_type; char *psz_object_type; char *psz_object_name;int
be_sure_to_add_VLC_COMMON_MEMBERS_to_struct;
#define VLC_OBJECT( x ) \
((vlc_object_t *)(x))+
0*(x)- be_sure_to_add_VLC_COMMON_MEMBERS_to_struct
struct vlc_object_t
{
VLC_COMMON_MEMBERS
};//定义一个结构来使用宏定义的公共成员
4.定义导出函数
#ifndef __PLUGIN__
# define VLC_EXPORT( type, name, args ) type name args
#else
# define VLC_EXPORT( type, name, args ) struct_u_n_u_s_e_d_
extern module_symbols_t* p_symbols;
#endif
5.定义回调函数
typedef int ( * vlc_callback_t ) ( vlc_object_t *,
char const *,
vlc_value_t,
vlc_value_t,
void * );
6.函数作为参数的定义方式
Int Fun(int n,int (*pf)(int ,int),char *pstr)
{ int j =10;
pf(n,j);
}
7.回调函数的声明
必须声明为global,或者static
Int vlc_callback_t (int ,int)
{。。。。。。。。。。。}
8.回调函数的使用
Fun(0, vlc_callback_t,”test”);
9.函数表达式
#define input_BuffersInit(a)__input_BuffersInit(VLC_OBJECT(a))
void * __input_BuffersInit( vlc_object_t * );
#define module_Need(a,b,c,d)__module_Need(VLC_OBJECT(a),b,c,d)
VLC_EXPORT( module_t *, __module_Need, ( vlc_object_t *, const char*, const char *, vlc_bool_t ) );
10.定义函数
#define INSERT_ELEM( p_ar, i_oldsize, i_pos, elem ) do { if(i_oldsize ) { (p_ar) = realloc( p_ar, ((i_oldsize) + 1) * sizeof(*(p_ar) )
); } else { (p_ar) = malloc( ((i_oldsize) + 1) * sizeof( *(p_ar) )); } if( (i_oldsize) - (i_pos) ) { memmove( (p_ar) + (i_pos) + 1,(p_ar) +
(i_pos), ((i_oldsize) - (i_pos)) * sizeof( *(p_ar) ) ); }(p_ar)[i_pos] = elem; (i_oldsize)++; } while( 0 )
应用为:
INSERT_ELEM( p_new- p_libvlc- pp_objects,
p_new- p_libvlc- i_objects,
p_new- p_libvlc- i_objects,
p_new );
11.改变地址的方式传递其值
stream_t *input_StreamNew( input_thread_t *p_input )
{ stream_t *s = vlc_object_create( p_input, sizeof( stream_t ));
input_stream_sys_t *p_sys;
if( s )
{
s- p_sys = malloc( sizeof( input_stream_sys_t ) );
p_sys = (input_stream_sys_t*)s- p_sys;
p_sys- p_input = p_input;
}
return s;//注解:s- p_sys改变了
}
第二部分
1.播放列表文件src/playlist/playlist.c的线程
playlist_t * __playlist_Create ( vlc_object_t *p_parent)函数中创建的线程,线程函数为
static void RunThread ( playlist_t *p_playlist )
线程思路分析:
在RunThread里面执行循环,如果没有任务执行,则适当的延迟,如果接到p_playlist- i_status !=PLAYLIST_STOPPED的条件,则调用PlayItem(
p_playlist )函数,在PlayItem( p_playlist )函数中从新创建输入线程。
通过void playlist_Command( playlist_t * p_playlist,playlist_command_t i_command,int i_arg)接收来自GUI界面的各种命令,然后设置p_playlist-
i_status的状态,由该状态改变该播放列表文件主循环线程的执行。
2.输入文件SRC/INPUT/INPUT.C的输入线程
input_thread_t *__input_CreateThread( vlc_object_t*p_parent,
input_item_t *p_item )函数中创建的线程,线程函数为
static int RunThread( input_thread_t *p_input )
线程思路分析:
由 input_thread_t结构的成员分析是接收文件流还是网络流,如果是文件流,则调用filemodule 的读函数(pf_read)和打开函数(--).如果是network 则打
开network module 的打开函数和读函数(pf_read)。
在 RunThread线程函数中接收数据和调用demux或者decode etc处理。
一旦产生新的输入,则在播放列表线程中会首先结束该输入线程,然后从新创建新的输入线程。
3.视频输出文件src/video_output/video_output.c的线程
vout_thread_t * __vout_Create( vlc_object_t *p_parent,
unsigned int i_width, unsigned int i_height,
vlc_fourcc_t i_chroma, unsigned int i_aspect)函数中创建的线程,线程函数为
static void RunThread( vout_thread_t *p_vout)
线程思路分析:
在RunThread里面执行循环,任务是显示视频。
4.在modules\gui\wxwindows\wxwindows.cpp中的GUI线程
static void Run( intf_thread_t *p_intf ) 函数中创建的线程,线程函数为
static void Init( intf_thread_t *p_intf )
线程思路分析:
在Init( intf_thread_t *p_intf)里面执行循环,创建新的GUI实例。Instance-》OnInit()(CreateDialogsProvider)-》DialogsProvider为运行的对话
框。
接收网络文件的步骤
OnOpenNet( wxCommandEvent& event)打开网络文件的步骤。打开OpenDialog对话框,点击Ok后调用OpenDialog::OnOk(wxCommandEvent& WXUNUSED(event)
)函数,调用playlist_Command函数改变播放列表线程的状态。
激活线程分析:
在wxwindow.cpp中的消息映射中 set_callbacks( OpenDialogs, Close );则设置了module_t- pf_activate= OpenDialogs函数,
在module.c 的__module_Need( vlc_object_t *p_this, const char*psz_capability,
const char *psz_name, vlc_bool_t b_strict )
函数中用到了pf_activate激活GUI对话框;
在video_output.c 的static void RunThread( vout_thread_t*p_vout)线程中,也用到了pf_activate激活GUI对话框;
5.开始所有module 的精髓
消息映射宏
vlc_module_begin();
set_callbacks( NetOpen, NULL );
vlc_module_end();
然后设置模块结构的成员函数为:
#define set_callbacks( activate, deactivate ) p_submodule-pf_activate = activate; p_submodule- pf_deactivate =deactivate
在__module_Need函数中启动pf_activate 激活相应的module。
网络数据流接收处理分析
1、在input.c(src\input)文件中的主线程循环
Thread in charge of processing the network packets anddemultiplexing
RunThread( input_thread_t *p_input )
{
InitThread( p_input ) ;
…………………………………………………….
input_SelectES( p_input, p_input->stream.p_newly_selected_es);
…………………………………………………….
i_count = p_input->pf_demux( p_input );
}
2、在下列函数中:
分离出access , demux , name字符串 ;
根据分离出的access 字符串通过module_Need函数找到acess 指针模块;
根据分离出的demux 字符串通过module_Need函数找到demux 指针模块;
static int InitThread( input_thread_t * p_input )
{
msg_Dbg( p_input, "access `%s', demux `%s', name `%s'",
p_input->psz_access, p_input->psz_demux, p_input->psz_name);
p_input->p_access = module_Need( p_input, "access",
p_input->psz_access, VLC_TRUE );
…………………………………………………….
while( !input_FillBuffer( p_input ) )
…………………………………………………….
p_input->p_demux =
module_Need( p_input, "demux",
(p_input->psz_demux && *p_input->psz_demux)?
p_input->psz_demux : "$demux",
(p_input->psz_demux && *p_input->psz_demux)?
VLC_TRUE : VLC_FALSE );
…………………………………………………….
}
3、在ps.c (module\demux\mpeg)文件中
a.通过消息映射宏赋值启动函数Activate;
b.通过函数Activate赋值p_input->pf_demux = Demux;
c. 通过函数module_Need( p_input, "mpeg-system", NULL, 0 )激活p_input->p_demux_data->mpeg.pf_read_ps( p_input,&p_data )函数(pf_read_ps)
;
d.在InitThread函数中激活;
static int Activate( vlc_object_t * p_this )
{
p_input->pf_demux = Demux;
p_input->p_private = (void*)&p_demux->mpeg;
p_demux->p_module = module_Need( p_input, "mpeg-system", NULL, 0);
}
4、在system.c (module\demux\mpeg)文件中
赋值解码模块mpeg_demux_t的成员函数;
static int Activate ( vlc_object_t *p_this )
{
static mpeg_demux_t mpeg_demux =
{ NULL, ReadPS, ParsePS, DemuxPS, ReadTS, DemuxTS };
mpeg_demux.cur_scr_time = -1;
memcpy( p_this->p_private, &mpeg_demux, sizeof( mpeg_demux ));
return VLC_SUCCESS;
}
并且申明函数static ssize_t ReadPS( input_thread_t * p_input,data_packet_t ** pp_data );
5、在ps.c (module\demux\mpeg)文件中
Demux( input_thread_t * p_input )
{
i_result = p_input->p_demux_data->mpeg.pf_read_ps( p_input,&p_data );
p_input->p_demux_data->mpeg.pf_demux_ps( p_input, p_data);
}
进行读取数据和分离工作;
6、在system.c (module\demux\mpeg)文件中
数据走向图如下
ReadPS-> PEEK->input_Peek(src\input\input_ext-plugins.c)-> input_FillBuffert 通过i_ret = p_input->pf_read( p_input,
(byte_t *)p_buf + sizeof(data_buffer_t)
+ i_remains,
p_input->i_bufsize );
input_thread_t结构的pf_read函数成员如果是为udp.c(modules\access)的RTPChoose函数
则在开启access(UDP 模块)时通过module_need 激活;
激活网络读数据模块 RTPChoose(modules\access\udp.c)->Read->net_Read(src\misc\net.c);
7、在input_programs.c(src\input)文件中
运行解码器对ES流解码
int input_SelectES( input_thread_t * p_input, es_descriptor_t *p_es )
{
p_es->p_dec = input_RunDecoder( p_input, p_es );
}
input_SelectES(src\input\input_programs.c)->input_RunDecoder(src\input\input_dec.c)->DecoderThread->DecoderDecode -
>vout_DisplayPicture
从接收到数据流到播放视频的过程分析
Demux(modules\demux\mpeg\ps.c)->DemuxPs(modules\demux\mpeg\system.c)->ParsePS->input_SelectES(src\input\input_programs.c)->input_RunDecoder
(src\input\input_dec.c)->CreateDecoder->
vout_new_buffer->vout_Request(src\video_output\video_output.c)->vout_Create->RunThread->vout_RenderPicture(src\video_output\vout_pictures.c)-
>pf_display
注意:p_dec->pf_vout_buffer_new =vout_new_buffer的pf_vout_buffer_new在ffmpeg_NewPictBuf(modules\codec\ffmpeg\video.c)函数中激活
Demux(modules\demux\mpeg\ps.c)->DemuxPs(modules\demux\mpeg\system.c)->ParsePS->input_SelectES(src\input\input_programs.c)->input_RunDecoder
(src\input\input_dec.c)->CreateDecoder->
DecoderThread
详细资料http://developers.videolan.org/vlc/
Chapter 5.
Data structures and main loop
Important data structures are defined in include/video.h andinclude/video_output.h. The main data structure is picture_t, whichdescribes
everything a video decoder thread needs. Please refer to this filefor more information. Typically, p_data will be a pointer to YUVplanar
picture.
Note also the subpicture_t structure. In fact the VLC SPU decoderonly parses the SPU header, and converts the SPU graphical data toan
internal format which can be rendered much faster. So a part of the"real" SPU decoder lies in src/video_output/video_spu.c.
The vout_thread_t structure is much more complex, but you needn'tunderstand everything. Basically the video output thread manages aheap of
pictures and subpictures (5 by default). Every picture has a status(displayed, destroyed, empty...) and eventually a presentationtime. The
main job of the video output is an infinite loop to : [this issubject to change in the near future]
2.
3.
also do the scaling, add subtitles and an optional pictureinformation field.
4.
5.
>p_buffer is an array of two buffers where the YUV transformtakes place, and p_vout->i_buffer_index indicates the currentlydisplayed buffer.
6.
Methods used by video decoders
The video output exports a bunch of functions so that decoders cansend their decoded data. The most important function isvout_CreatePicture
which allocates the picture buffer to the size indicated by thevideo decoder. It then just needs to feed (void *)p_picture->p_data with the
decoded data, and call vout_DisplayPicture and vout_DatePictureupon necessary.
i_type will be for instance YUV_420_PICTURE, and i_width andi_height are in pixels.
Warning
*
freed while the decoder still needs it. For instance, an I or Ppicture can still be needed after displaying to decode interleavedB pictures.
*
link previously made.
*
before knowing precisely at what time it will be displayed. Forinstance to date an I or P picture, you must wait until you havedecoded all
previous B pictures (which are indeed placed after - decoding order!= presentation order).
*
is ready to be rendered. It can be called before or aftervout_DatePicture.
*
*
i_channel is the ID of the subpicture channel, i_type isDVD_SUBPICTURE or TEXT_SUBPICTURE, i_size is the length in bytes ofthe packet.
*
decoded. It obsoletes the previous subpicture.
*
VLC(五) 视频播放的基本原理
DirectShow,在播放视频的原理和架构上都是非常相似的,理解这个对理解VLC的源码会有事半功倍的效果。
播协议、端口等信息;如果检测出来是RTP协议(RTP协议在UDP头部简单得加上了固定12个字节的信息),还要分析RTP头部信息。这部分可以参看VLC源码
/modules/access/udp.c。在同目录下还可以看到大量的access模块,如file、http、dvd、ftp、smb、tcp、dshow、mms、v4l…等等
,一个program又可以包含多个视频、音频、和文字信息的ES流;每个ES流会有不同的PID标示。而又为了可以分析这些ES流,TS有一些固定的PID用来间隔发送
program和es流信息的表格:PAT和PMT表。关于TS格式的详细信息可以去google一下。
装格式也就有了demux。
行某种压缩。这就是我们熟知的音视频编码格式,包括MPEG1(VCD)、MPEG2(DVD)、MPEG4、H.264、rmvb等等。音视频解码器的作用就是把这些压缩了的数据还
原成原始的音视频数据。VLC解码MPEG2使用了一个独立的库libmpeg2,调用它的源文件是/modules/codec/libmpeg2.c。VLC关于编解码的模块都放
在/modules/codec目录下,其中包括著名的庞大的 ffmpeg。
那样直接把图像画到窗口DC上——VLC的一个输出模块WinGDI就是这么干的,但是通常这太慢了,而且消耗大量的CPU。在Windows下比较好的办法是用DirectX的接
口,会自动调用显卡的加速功能。
传输、封装格式和编辑码格式如MPEG系列,有些封装格式是独立的容器,但是很多人会误解它是编解码格式,如mkv、avi这些。
这些都是很复杂而又很重要的事情。
video_filter类型的模块来做这一类事情。
http、UDP、文件等等。
频的输出。
DirectShow中的SourceFIlter,这一类Filter只有输出pin没有输入pin。demux模块对应于splitterfilter,这种filter有一个输入pin,多个输出pin。解码模
块是一类transform filter,有一个输入pin、一个输出pin,输出模块对应于readeringfilter,有一个输入pin,没有输出pin。当然transform filter不一定是
解码器,也可能是某种其他的处理。