VideoLAN---VLC源代码分析

第一部分             变量及宏定义

1.消息映射宏

                       vlc_module_begin();

                           …………………..

vlc_module_end();

2.结构中包含函数

       struct input_thread_t

{

    VLC_COMMON_MEMBERS

    /* Thread properties */

    vlc_bool_t              b_eof;

    vlc_bool_t              b_out_pace_control;

    /* Access module */

    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                                                  \

/** \name VLC_COMMON_MEMBERS                                                \

* these members are common for all vlc objects                             \

*/                                                                         \

/**@{*/                                                                     \

    int   i_object_id;                                                      \

    int   i_object_type;                                                    \

    char *psz_object_type;                                                  \

    char *psz_object_name;                                                  \

                                                                                               \

/** Just a reminder so that people don't cast garbage */                \

    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 *,      /* variable's object */

                                   char const *,            /* variable name */

                                   vlc_value_t,                 /* old value */

                                   vlc_value_t,                 /* new value */

                                   void * );                /* callback data */                                                

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.定义函数

   /* Dynamic array handling: realloc array, move data, increment position */

#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结构的成员分析是接收文件流还是网络流,如果是文件流,则调用file module 的读函数(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 and demultiplexing

RunThread( input_thread_t *p_input )

{

          InitThread( p_input ) ;

…………………………………………………….

     input_SelectES( p_input, p_input->stream.p_newly_selected_es );

              …………………………………………………….

      /* Read and demultiplex some data. */

     i_count = p_input->pf_demux( p_input );

}

2、在下列函数中:

   1. 分离出access  , demux  , name字符串 ;

   2. 根据分离出的access  字符串通过module_Need函数找到acess 指针模块;

   3. 根据分离出的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 );

    /* Find and open appropriate access module */

    p_input->p_access = module_Need( p_input, "access",

                                     p_input->psz_access, VLC_TRUE );

…………………………………………………….

  while( !input_FillBuffer( p_input ) )

  …………………………………………………….

    /* Find and open appropriate demux module */

    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 )

{

      /* Set the demux function */

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

从接收到数据流到播放视频的过程分析

   从网络接收到流->对数据流进行视频和音频分离->对视频用解码器解码->显示解码后的视频

    视频显示部分走势线:分离->解码->新的VOUT缓冲区->VOUT线程

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

  注意:在解码线程中对数据流(AUDIO 或者VIDEO)进行解码

详细资料 http://developers.videolan.org/vlc/    VLC API documentation  或者VLC developer documentation

Chapter 5.  The video output layer

Data structures and main loop

Important data structures are defined in include/video.h and include/video_output.h. The main data structure is picture_t, which describes everything a video decoder thread needs. Please refer to this file for more information. Typically, p_data will be a pointer to YUV planar picture.

Note also the subpicture_t structure. In fact the VLC SPU decoder only parses the SPU header, and converts the SPU graphical data to an 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't understand everything. Basically the video output thread manages a heap of pictures and subpictures (5 by default). Every picture has a status (displayed, destroyed, empty...) and eventually a presentation time. The main job of the video output is an infinite loop to : [this is subject to change in the near future]

   1.      Find the next picture to display in the heap.

   2.      Find the current subpicture to display.

   3.      Render the picture (if the video output plug-in doesn't support YUV overlay). Rendering will call an optimized YUV plug-in, which will also do the scaling, add subtitles and an optional picture information field.

   4.      Sleep until the specified date.

   5.      Display the picture (plug-in function). For outputs which display RGB data, it is often accomplished with a buffer switching. p_vout->p_buffer is an array of two buffers where the YUV transform takes place, and p_vout->i_buffer_index indicates the currently displayed buffer.

   6.      Manage events.

Methods used by video decoders

The video output exports a bunch of functions so that decoders can send their decoded data. The most important function is vout_CreatePicture which allocates the picture buffer to the size indicated by the video decoder. It then just needs to feed (void *) p_picture->p_data with the decoded data, and call vout_DisplayPicture and vout_DatePicture upon necessary.

    * picture_t * vout_CreatePicture ( vout_thread_t *p_vout, int i_type, int i_width, int i_height ) : Returns an allocated picture buffer. i_type will be for instance YUV_420_PICTURE, and i_width and i_height are in pixels.

      Warning

If no picture is available in the heap, vout_CreatePicture will return NULL.

    *      vout_LinkPicture ( vout_thread_t *p_vout, picture_t *p_pic ) : Increases the refcount of the picture, so that it doesn't get accidently freed while the decoder still needs it. For instance, an I or P picture can still be needed after displaying to decode interleaved B pictures.

    *      vout_UnlinkPicture ( vout_thread_t *p_vout, picture_t *p_pic ) : Decreases the refcount of the picture. An unlink must be done for every link previously made.

    *      vout_DatePicture ( vout_thread_t *p_vout, picture_t *p_pic ) : Gives the picture a presentation date. You can start working on a picture before knowing precisely at what time it will be displayed. For instance to date an I or P picture, you must wait until you have decoded all previous B pictures (which are indeed placed after - decoding order != presentation order).

    *      vout_DisplayPicture ( vout_thread_t *p_vout, picture_t *p_pic ) : Tells the video output that a picture has been completely decoded and is ready to be rendered. It can be called before or after vout_DatePicture.

    *      vout_DestroyPicture ( vout_thread_t *p_vout, picture_t *p_pic ) : Marks the picture as empty (useful in case of a stream parsing error).

    *      subpicture_t * vout_CreateSubPicture ( vout_thread_t *p_vout, int i_channel, int i_type ) : Returns an allocated subpicture buffer. i_channel is the ID of the subpicture channel, i_type is DVD_SUBPICTURE or TEXT_SUBPICTURE, i_size is the length in bytes of the packet.

    *      vout_DisplaySubPicture ( vout_thread_t *p_vout, subpicture_t *p_subpic ) : Tells the video output that a subpicture has been completely decoded. It obsoletes the previous subpicture.

    *      vout_DestroySubPicture ( vout_thread_t *p_vout, subpicture_t *p_subpic ) : Marks the subpicture as empty.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值