VLC代码初探

来源:互联网 发布:java获取字符串编码 编辑:程序博客网 时间:2024/05/21 08:53

http://blog.csdn.net/chenee543216/article/details/4377660


VLC代码初探(部分内容鉴于作者智商,基本胡扯)


1 废话
2 代码阅读工具、方法
3 VLC部分框架,结合control的rc说明
4 下一步工作

一废话:
惭愧进度比预计的要慢,有老婆孩子就是麻烦阿,尤其是孩子还在老婆肚子里面的时候。想当年年轻的时候晚上工作到2、3点,第2天早上7:30接着搞,LFS的时候3天时间搞定,包括吃饭睡觉几乎没有离开电脑。(bahbahbah...一通废话和借口)

二代码阅读工具、方法:
回到正题,首先讨论一下如何去学习代码(这里是我的浅见,各位大大如果有好的方法,请一定要不吝赐教!拜一个!)。我的方法是使用CGDB( CGDB - curses frontend to gdb)!用CGDB来走流程,对于这些有源码的项目来说再好不过了,实时观看程序流程,把握各个变量和宏的定义,直观学习函数调用。尤其是那些被#define的函数和各种代码段。同时,CGDB还有一个十分好用的功能,断点和观察点!!用这2个再平常不过的功能,我们直接下到对应的位置,运行程序,然后用bt观看程序调用。多么惬意的事情阿。

有例有真相:(这里只简单列举一些例子,算是抛砖头希望各位大大能够丢点玉下来:)
1 找main函数;代码编译好以后,那个文件是入口?如果没有main.c,或者grep main 有N个结果,怎么办,一个一个找?
cgdb vlc;即可!自动停在main入口。当然找main的方法多的是,比如看map文件等等。

2 watch变量,判断在什么地方赋值:当我们知道结果,轻易的找到其调用过程。
(gdb) watch p_intf->pf_run
Hardware watchpoint 3: p_intf->pf_run
(gdb) c
Continuing.
Hardware watchpoint 3: p_intf->pf_run

Old value = (void (*)(struct intf_thread_t *)) 0
New value = (void (*)(struct intf_thread_t *)) 0xb7a21d49 <Run>
Activate (p_this=0x9c3c800) at rc.c:347
(gdb) bt
#0  Activate (p_this=0x9c3c800) at rc.c:347
#1  0xb7f59634 in __module_need (p_this=0x9c3c800, psz_capability=0xb7f8a14c "interface", psz_name=0x9c3c750 "", b_strict=true)
 at modules/modules.c:583
#2  0xb7ef13e0 in intf_Create (p_this=0x9bc7210, psz_module=0xb7f7fb3d "$intf") at interface/interface.c:124
#3  0xb7ed96e3 in libvlc_InternalAddIntf (p_libvlc=0x9bc7210, psz_module=0x0) at libvlc.c:1160
#4  0xb7fa9c73 in libvlc_add_intf (p_i=0x9bc7908, name=0x0, p_e=0xbfbdb38c) at control/core.c:190
#5  0x08048d4a in main (i_argc=4, ppsz_argv=0xbfbdb464) at vlc.c:157
(gdb)
这里是我在跟踪./vlc/modules/control/rc.c时候的输出,可以看出该模块的赋值是通过rc.c中的Activate()函数完成的。


3 自动解析繁琐的#define:
在每个module都有一个vlc_module_begin (),这是一个#define。如果你懒得看,或者用肉眼看起来比较困难的话。
#define vlc_module_begin( )                                                   /
    EXTERN_SYMBOL DLL_SYMBOL int CDECL_SYMBOL                                 /
    __VLC_SYMBOL(vlc_entry) ( module_t *p_module );                           /
                                                                              /
    EXTERN_SYMBOL DLL_SYMBOL int CDECL_SYMBOL                                 /
    __VLC_SYMBOL(vlc_entry) ( module_t *p_module )                            /
    {                                                                         /
        module_config_t *p_config = NULL;                                     /
        const char *domain = NULL;                                            /
        if (vlc_module_set (p_module, VLC_MODULE_NAME,                        /
                            (const char *)(MODULE_STRING)))                   /
            goto error;                                                       /
        {                                                                     /
            module_t *p_submodule = p_module;

对于我们这里rc.c文件的代码,直接在代码中下断点,然后让他跑
下断点:
 194| vlc_module_begin ()
 195+>    set_shortname( N_("RC"))
 196|     set_category( CAT_INTERFACE )
 197|     set_subcategory( SU
直接看结果:
(gdb) bt
#0  vlc_entry__1_0_0e (p_module=0x9069ca0) at rc.c:195
#1  0xb7f0cad1 in module_Call (obj=0x906e800, p_module=0x9069ca0) at modules/os.c:120
#2  0xb7f069d4 in AllocatePlugin (p_this=0x906e800, psz_file=0x9035c38 "/home/chenee/workstation/ODM/NetDVR/vlc/modules/control/.libs/librc_plugin.so") at modules/modules.c:1265
可以看出,这个其实是函数vlc_entry__1_0_0e()而已。当然,对于#define,我们完全可以用gcc -E搞定他。

三 VLC部分框架,结合control的rc说明(这里只针对我这几天研究rc模块谈谈,还有好多地方没有搞清楚)
0 在我们研究代码之前,我的习惯是研究Makefile或者是configure,可以通过./configure --help来搞清楚每个参数是什么意思,尽量精简,尤其是对于VLC这种大代码,尤其是公司的破机器,编译一遍很痛苦的。
1 我这里引用的代码是GIT:6d5cb64b3f15494acb43f48e5152d1a9b113f008,2009-07-21 12:19 Fabio Ritrovato。

2 我们的代码入口main是在./vlc/bin/vlc.c中,而这个文件其实只是一个示例而已,大部分功能是由libvlc来提供的。具体是通过Line 144这个函数来创建一个实例。
    /* Initialize libvlc */
    libvlc_instance_t *vlc = libvlc_new (argc, argv, &ex);
该函数进一步调用libvlc_InternalInit(),这是一个大的函数,好几百行。会初始化VLC用到的各种变量,读取~/目录下面的cache config等目录、文件,初始化moudle_bank等。在这个函数运行完毕,我们会得到一个完整的程序框架,包括已经读取的cache、config、cmdline,还有通过遍历加载的模块链表。cache/config就是文件操作,cmdline就是使用   getopt_long()而已,不表。其他功能目前没有用到,不胡扯。

3 VLC可以看成libvlc+modules的集合:对于libvlc来说,主要功能是实现模块加载框架并遍历安装目录下面/modules文件夹,读取所有*.so模块信息。而真正的功能是由各个module来实现。这样各个开源贡献者可以只专注自己的部分。下面我就简单说明一些模块加载过程。
3.1 即使精简后的modules目录下面也有几百个modules。
3.2 libvlc按需索取:就是根据当前的应用来判断需要加载那些modules。
3.3 对modules/control/rc.c进行说明,这个module是默认加载的interface类的module实现命令行上的控制,根据功能可以看出,它主要是读取解析cmdline然后调用相应函数。rc全称为remote control,因为我是在本地操作所以没有用到里面的net部分,忽略。(每个模块可能会有部分函数或者格式不一样),rc.c文件2k多行,大部分是处理命令,函数10几个,我们取关键部分,
      1 vlc_module_begin 和vlc_module_end部分,其实上面说过,就是vlc_entry__1_0_0e()函数,用于注册到系统的时候提供给VLC一些信息,比如模块的功能和优先级:set_capability( "interface", 20 );模块名称、描述、简称等。。。
      2 Activate()函数:激活,顾名思义,用于初始化和创建一部分成员,如果rc用到网络功能,会有网络server部分的初始化。这里有个关键赋值是p_intf->pf_run = Run;这个p_intf->pf_run就是提供给后边thread_create的线程主函数。其他略
      3 Run()函数,就是简单的读cmd,然后解析,调用。。。

3.4 模块加载过程简单分析:对于我们rc.c来说,调用堆栈如下。
#0  0xb7efc5cc in __module_need (p_this=0x890b800, psz_capability=0xb7f2d14c "interface", psz_name=0x890b750 "", b_strict=true)
 at modules/modules.c:573
#1  0xb7e943e0 in intf_Create (p_this=0x8896210, psz_module=0xb7f22b3d "$intf") at interface/interface.c:124
#2  0xb7e7c6e3 in libvlc_InternalAddIntf (p_libvlc=0x8896210, psz_module=0x0) at libvlc.c:1160
#3  0xb7f4cc73 in libvlc_add_intf (p_i=0x8896908, name=0x0, p_e=0xbfa8022c) at control/core.c:190
#4  0x08048d4a in main (i_argc=4, ppsz_argv=0xbfa80304) at vlc.c:157
到了libvlc_add_intf()这里以后,所有的VLC部分已经初始化完成,libvlc使用module_need函数来加载(dlopen,dlsym。。)并运行(thread_create)。

faint,说了这么多废话,其实就是“vlc通过dlopen打开某些module,然后用线程来运行,over!” 这就是libvlc模块话的全部思想,其它的,暂不关心。

4 下一步工作:
1 改写rc,按需实现我们的控制功能(其它功能,我暂时不负责,所以不考虑)
2 进一步了解vlc,尽量做到熟悉每个变量。结构
3 说到变量才发现我忘记讨论变量和结构了。时间原因,下次吧。
结构部分只要关注VLC_COMMON_MEMBERS部分,和各个结构相互cast。
4 优化,还是存在很多低效的module_list_get()需要想想办法改进。
..........
原创粉丝点击