VLC源码解析

转载至: http://www.byywee.com/page/M0/S753/753358.html


1 VLC源码布局

vlc核心的是libvlc,它供给界面,应用处理惩罚功能,所有的libvlc的源代码都放在src目次及其子目次

1.1 ./config/
从号令行和设备文件中加载设备
1.2 ./control/
供给动作把握功能,如播放等操纵
1.3  ./extras/
大多是平台的特别代码
1.4  ./modules/
模块经管
1.5  ./network/
供给收集接口(socket经管,收集接口)
1.6  ./osd/
显示屏幕上的操纵
1.7  ./test/
libvlc测试模块
1.8  ./text/
字符集
1.9  ./interface/
供给代码中可以调用的接口,如按键后的硬件作出反响
1.10  ./playlist/
经管播放功能
1.11  ./input/
建树并读取一个输入流,并且分别此中的音频和视频,然后把分别好的音频和视频流发给解码器
1.12  ./audio_output/
初始化音频混淆器,即设置正确的同步频率,并对从解码器传来的音频流从头取样
1.13  ./video_output/
初始化视频播放器,把从解码器获得视频画面转化格局从yuv到rgb,然后播放
1.14  ./stream_output/ 
输出音频流和视频流到收集
1.15  ./misc/

libvlc应用的其他项目组功能,如线程体系,消息队列等.


2 configure详解

概述

VLC 
属于Video LAN开源项目组织中的一款全开源的流媒体办事器和多媒体播放器。作为流媒体办事器,VLC跨平台,支撑多操纵体系和策画机体系布局;作 
为多媒体播放器,VLC可以播放多种格局的媒体文件。首要包含有:WMV、ASF、MPG、MP、AVI、H.264等多种常见媒体格局。


VLC 
采取全模块化布局,在体系内部,经由过程动态的载入所需的模块,放入一个module_bank的布局体中同一经管,连VLC的Main模块也是经由过程插件的方 
式动态载入的(经由过程module_InitBank函数在初始化建树module_bank时)。对于不支撑动态载入插件的体系景象中,VLC也可以采取 
builtin的体式格式,在VLC启动的时辰静态载入所须要的插件,并放入module_bank同一经管。


VLC 
的模块分成很多类别首要有:access、access_filter、access_output、audio_filter、 
audio_mixer、audio_output、codec、control、demux、gui、misc、mux、packetizer、 
stream_output、video_filter、video_output、interface、input、playlist等(此中黑体为核 
心模块)。VLC无论是作为流媒体办事器还是多媒体播放器,它的本质思路就是一个“播放器”,之所以这么形象描述,是因为(The core gives a framework to do the media processing,  input (files, network streams) to output (audio or video, on ascreen or a network), going through various muxers, demuxers, decoders and filters. Even the interfaces are plugins for LibVLC. It is up to the developer to choose which module will be loaded. 摘
于官网申明) 
它本质处理惩罚的是ES、PES、PS、TS等流间的转换、传输与显示。对于流媒体办事器,若是从文件作为输入 
即:PS->DEMUX->ES->MUX->TS;对于多媒体播放器若是采取UDP体式格式传输 
即:TS->DEMUX->ES。


1. 插件经管框架


在VLC中每种类型的模块中都有一个抽象层/布局体,在抽象层或布局体中定义了若干操纵的函数指针,经由过程这些函数指针就能实现模块的动态载入,赋值相干的函数指针的函数地址,最后经由过程调用函数指针能调用实际模块的操纵。


对 
于VLC所有的模块中,有且仅有一个导出函数:vlc_entry__(MODULE_NAME)。(此中MODULE_NAME为宏定义,对于main 
模块,在\include\modules_inner.h中定义为main)动态载入模块的过程是:应用module_Need函数,在 
module_bank中按照各个插件的capability等相干属性,寻找第一个能满足请求并激活的模块。所谓激活是指,调用插件的初始化函数成功。 
对于各个插件的初始化函数和析构函数均在vlc_entry__(MODULE_NAME)函数中指定了相干函数地址。是以载入各个插件(动态库)的过 
程,就成为懂得析动态库文件,并找到此中vlc_entry__函数的地址,然后运行。如许各个模块的激活函数就会赋值各个操纵的函数地址,以待后面函数 
动态调用。


具体函数调用过程如下:


l Main模块的载入过程:


int main( int i_argc, char *ppsz_argv[] ) 
(src\vlc.c)->i_ret = VLC_Init( 0, i_argc, ppsz_argv )-> 
module_InitBank( p_vlc ) 
(src\libvlc.c void __module_InitBank( vlc_object_t *p_this ))-> module_LoadMain( p_this ) 
(src\misc 
\modules.c)->AllocateBuiltinModule( p_this, vlc_entry__main )->pf_entry( p_module ) 
(激活了main模块,以上为main模块的载入过程,对于main模块调用的实际函数为导出函数vlc_entry__main,其它模块导出的均为 
vlc_entry__0_8_6)


l Module_Need函数实现载入随便率性模块的过程:


module_t * __module_Need( vlc_object_t *p_this, const char *psz_capability,


                          const char *psz_name, vlc_bool_t b_strict ) 
(src\misc\modules.c)-> vlc_list_find(将所有已经载入的模块查询出来)->然后轮回,按照 
capability查找第一个最合适的module->AllocatePlugin(动态载入所须要的插件,该函数会在动态库地点目次,遍历所 
有动态库文件,)->p_module->pf_activate(调用激活函数)


l VLC_Init函数流程:


module_InitBank->module_LoadBuiltins(载入静态插件)->module_LoadPlugins(载入动态插件->VLC_AddIntf(添加interface插件,VLC会静态载入hotkeys模块)


在VLC中按照处理惩罚任务不合,会静态载入不合的模块,main、memcpy、hotkeys等;动态载入的模块按照处理惩罚任务不合,差别很大。


2. VLC流媒体办事器体系布局


以下首要评论辩论VLC作为流媒体办事器时的体系布局。针对一个节目单文件,调试其运行过程,并最后给出总结。


该实例的播放节目单为如下:


New br broadcast enabled


Setup br input /mnt/hgfs/movie/caiyan.mpg


Setup br output #standard{mux=ts,access=udp,url=234.0.1.4,sap,name=ch1}


在例子中,经由过程VLC供给API:libvlc_new,libvlc_vlm_new,libvlc_vlm_play_media,libvlc_vlm_load_file等(有些API是本身添加的)可以完成对广播节目br的播放。


下面让我们细心看看经由过程这几个接口,VLC内部到底是怎么工作完成了流媒体公布的。


1. 起首法度调用libvlc_new(\src\control\core.c)接口,实现创建一个VLC运行实例libvlc_instance_t,该实例在法度运行过程中独一。


2. 在libvlc_new接口中,调用了VLC_Init函数实现具体的初始化工作。


3. VLC_Init(\src 
\libvlc.c)函数中,起首经由过程system_Init函数完成传入参数对体系的相干初始化,接着经由过程module_InitBank(\src 
\misc\modules.c)函数初始化module_bank布局体,并创建了main模块,然后经由过程module_LoadBuiltins载入 
静态模块,经由过程module_LoadPlugins(\src\misc\modules.c)函数载入动态模块,经由过程 
module_Need(\src\misc\modules.c)函数载入并激活memcpy模块,经由过程playlist_Create(\src 
\playlist\playlist.c)函数,创建了一个playlist播放经管的线程,其线程处理惩罚函数为RunThread(\src 
\playlist\playlist.c),经由过程VLC_AddIntf(\src\libvlc.c)函数添加并激活hotkeys模块,最后按照系 
统设置定义了宏HAVE_X11_XLIB_H,是以还须要添加screensaver模块。


4. 总结:此时加载的模块有main,hotkeys,screensaver,memcpy;多创建了一个线程,用于经管playlist,该线程无穷轮回,直到p_playlist->b_die状况为止。


5. 其次法度中调用libvlc_vlm_new接口,创建VLM对象(该接口为本身添加的)。


6. 该接口调用的是vlm_New(\src\misc\vlm.c)函数,实现VLM对象的创建,函数返回值是指向vlm_t的指针。


7. Vlm_new 
函数中,创建了一个vlm经管线程,线程处理惩罚函数为Manage(\src\misc\vlm.c)。该函数轮回处理惩罚当前各类媒体(vod、 
broadcast、schedule)的播放实例,把握其每个播放细节(如:从一个input切换到下一个input;schedule周期轮回调剂 
等)。与playlist线程不合的是,Manage首要针对播放实例的操纵,而RunThread首要针对播放列表的经管,也就是说VLC经管是分级 
的,播放列表级和播放列表中媒体播放实例级。


8. 其次法度调用libvlc_vlm_load_file接口,载入播放节目单(该接口也为本身添加,播放节目单如上所述)。


9. 该接口调用的是vlm_Load(\src\misc\vlm.c)函数,在该函数中,依次调用如下函数:stream_UrlNew、stream_Seek、stream_Read、Load,以下具体介绍各个函数感化。


a) 首 
先是stream_UrlNew(\src\input\stream.c)函数。先调MRLSplit(\src\input\input.c)函数完 
成对access、demux和path的解析。具体对于本例解析的成果为:access= " ",demux=" ",path=" aa"。然后调 
用access2_New(\src\input\access.c)函数创建一个access_t布局体并初始化。具体运行时载入模块的相干参数 
是:capability="access2",name="access_file",psz_filename=access/libaccess_file_plugin.so。 
最后调用stream_AccessNew(\src\input\stream.c)函数,创建stream_t布局体对象,并初始化对象中所有函数指 
针;


b) 再调用stream_Seek(\include\vlc_stream.h)内联函数,设置肇端地位;


c) 再调用stream_Size(\include\vlc_stream.h)获得大小;


d) 再调用stream_Read(\include\vlc_stream.h),读取到缓冲区;


e) 最 
后调用Load(\src\misc\vlm.c),完成实际的载入节目单。对于节目单文件,是一行行解析,并调用 
uteCommand(\src\misc\vlm.c)完成解析的。Load函数的调用仅仅是设置了相干参数,如:设置input字符串值,设 
置output字符串值,设置mux的值及与播放相干的enabled、loop等参数。Load工作仅仅是为了下一步公布流做筹办的。


10. 法度中调用libvlc_vlm_play_media接口,将节目流公布出去。(本身添加接口)


11. 在 
libvlc_vlm_play_media接口中,本质是创建了号令“control br play”再调用 
vlm_uteCommand(\src\misc\vlm.c),完成对号令的履行,按照号令类型,由 
vlm_MediaControl(\src\misc\vlm.c)函数处理惩罚。


12. 在 
vlm_MediaControl函数中,会调用vlc_input_item_Init(\include\vlc_input.h)函数完成播放实例 
的初始化,并调用input_CreateThread2(\src\input\input.c)函数完成播放线程的创建。该线程的处理惩罚函数为 
Run(\src\input\input.c)。


13. Run线程是全部VLC作为流媒体办事器的核心。其首要分为如下几个步调:Init、MainLoop和End。此中MainLoop是一个无穷轮回,是完成流媒体的全部公布过程。


a) 起首调用Init(\src\input\input.c)函数,初始化相干统计参数;


b) 其次再调用input_EsOutNew(\src\input\es_out.c)函数,初始化es_out_t布局体对象和es_out_sys_t布局体对象,并设置相干函数指针;


c) 再调用InputSourceInit(\src\input\input.c)函数,初始化input_thread_t对象中的input_source_t对象,首要有access_t、stream_t、demux_t三个布局体对象;


d) 总结此时各个模块实际载入的景象:


1) (access_t)type="access",name="access_filter",capability="access2",psz_filename="access/libaccess_file_plugin.so";


2) (stream_t)type="stream",pf_read="AStreamReadStream",pf_seek="AStreamPeekStream",pf_control="AStreamControl",pf_destory="AStreamDestory";


3) (demux_t)type="demux",capability="demux2",shortcuts="ps";


4) (sout_instance_t)type="stream out",psz_capability="sout stream",shortcut="stream_out_standard",psz_filename="/stream_out 
/libstream_out_standard_plugin.so";


5) (es_out_t)pf_add="ESOutAdd",pf_send="ESOutSend",pf_del="ESOutDel",pf_control="ESOutControl";


e) 再调用MainLoop(\src\input\input.c)函数,完成读取、解复用、解码、复用和传输;


f) MainLoop函数为无穷轮回,直到input_thread_t对象存在b_die、b_error、b_eof时为止。在该函数中,存在如下行代码:


i_ret=p_input->input.p_demux->pf_demux(p_input->input.p_demux);


它就是流媒体办事器运行的出发点,所有的后续操纵都邑在该函数中持续衍生。


g) Pf_demux调用的是(\modules\demux\ps.c)中的Demux函数,在该函数中首要完成如下操纵:


1) 先调用ps_pkt_resynch(\modules\demux\ps.c)函数,完成PS流中数据包从头同步(这里应当涉及到多媒体相干常识,须要补补);


2) 再调用ps_pkt_read(\modules\demux\ps.c)函数,终极调用stream_Block函数,这个函数内部会按照实际景象,调用stream_t模块中的pf_read或pf_block函数,函数成果会返回一个读取的buffer;


3) 按照数据包的i_code的值,做不合的处理惩罚,对于音视频数据流,调用es_out_Send(\include\vlc_es_out.h)函数处理惩罚;


4) es_out_Send一个抽象层函数,其经由过程函数指针,实际调用的是EsOutSend(\src\input\es_out.c)函数;


5) EsOutSend函数终极会调用input_DecoderDecode(\src\input\decoder.c)函数;


6) input_DecoderDecode函数会调用DecoderDecode(\src\input\decoder.c)函数完成解码;


7) DecoderDecode函数会调用pf_packetize(\modules\packetizer\mpegvideo.c)函数实现PES的打包;


8) DecoderDecode函数会调用sout_InputSendBuffer(\src\stream_output\stream_output.c)函数,实现发送;


9) sout_InputSendBuffer函数中的pf_send指针,指向的是(\modules\stream_out\standard.c)Send函数;


10) Send 
函数调用的是流化输出(stream_output)的抽象层(\src\stream_output\stream_output.c)中的 
sout_MuxSendBuffer函数,起首将要发送的数据放入fifo队列中,然后调用pf_mux函数指针,完成多路复用;


11) Pf_mux函数指针指向的是(\modules\mux\mpeg\ts.c)的Mux函数,完成多路复用后,终极调用(\modules\mux\mpeg\ts.c)TSSchedule函数,筹办调剂发送了;


12) TSSchedule函数中调用了TSDate(\modules\mux\mpeg\ts.c)函数;


13) TSDate函数中调用了流化输出(stream_output)的抽象层(\src\stream_output\stream_output.c)中的sout_AccessOutWrite函数,终极调用pf_write函数完成数据输出;


14) pf_write函数指向的是(\modules\access_output\udp.c)中的Write函数,完成数据UDP发送,如许数据就转换称TS流输出了;




15) 总结:pf_demux函数为流媒体所有操纵的出发点,经由过程该处衍生了很多其他模块的处理惩罚,从上方的解析可以看出,体系本质就是PS、ES、PES和TS几种流间的转换,针对应用处合(首要指做办事器或客户端)的不合,转换的体式格式不合



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值