VLC学习-3

    这章主要想写写数据初始化的问题

     --工作中主要是PS流,裸码流,音频的编码也是G711, 所以自己的说法有很大的片面性,请谅解

     小吐怡情: 本来是由较充裕的时间完成这些工作的,但是因为小万同学的突然辞职,前面段时间就一直在接手他手上关于流媒体服务器的工作。不过这次却让我对整个live555的架构有了更深的认识,非常感谢小万同学的耐心指导,也感谢我老大赵大哥也指导了我非常多的东西。这次学习我进步很多了,在这里祝万同学在新的环境里,一切顺利、步步高升。

        下面继续说说vlc中数据初始化的问题。数据初始化分为几个部分(可能说的也不是很对,仅供参考):

         1. libvlc_instance_t :     vlc实例 

         2. libvlc_media_player:             播放器  

         3. libvlc_media_t:                      媒体路径

         4. 视频播放相关变量如何初始化

         5. 音频播放相关变量如何初始化 

       可能自己更关心的是这3个东西怎么组合起来,完成这个播放的功能。另外播放器的事件是如何驱动的。

--------------------------------------------------------------------------------------------------------------------------

1. libvlc_instance_t :  顾名思义,是整个vlc的实例。我们跟着代码:

   vlc_argv[vlc_argc++] = "--ignore-config";  //其他的怎么配置我也不太清楚,在我所接触到的的Demo中就是这么调用的
   p_instance = libvlc_new(vlc_argc, vlc_argv);

   这里面到底初始化些什么乱七八糟的东西?

        a. 初始化TLS (线程局部存储): 初始看来是没什么用的

        b. libvlc_InternalInit().  这又进入到了VLC学习-2中的内容

2. libvlc_media_t: 媒体路径,这个东东是用来标记媒体的么?

       下面看两个接口:

   libvlc_media_t *libvlc_media_new_location(libvlc_instance_t *p_instance, const char * psz_mrl );//MRL
   libvlc_media_t *libvlc_media_new_path( libvlc_instance_t *p_instance,const char *path );//本地文件
实际上在 libvlc_media_new_path() 最终也是调用libvlc_media_new_location() 那我们下来还是仔细的看看这个接口就可以了。

input_item_t * p_input_item;  //输入项目
libvlc_media_t * p_md;	      //media
p_input_item = input_item_New( p_instance->p_libvlc_int, psz_mrl, NULL ); //初始化input_item ,设置psz_uri
//最主要的地方在于初始化eventManager,及注册相关事件
p_md = libvlc_media_new_from_input_item( p_instance, p_input_item );

 3.libvlc_media_player_t 真正的播放器 

   直接上代码:libvlc_media_player_new_from_media(libvlc_media_t* p_md)

    libvlc_media_player_t * p_mi;
    //a.创建属性:例如var_Create (mp, "fullscreen", VLC_VAR_BOOL); 为播放器添加"fullscreen"的属性,属性类型为BOOL
    //b.添加事件 register_event(mp, Stopped); 为播放器添加Stopped事件
    //c.另外用户自己可以在libvlc_media_player_t的struct中添加自己的属性,在这里将其初始化
    p_mi = libvlc_media_player_new( p_md->p_libvlc_instance );
    libvlc_media_retain( p_md );  //增加引用计数,在后面释放时候只有引用计数最终减为0的时候才会真正释放资源
    p_mi->p_md = p_md;

-------------------------------------------------------------------------------------------------------------------

 4.对于音视频主要有几个步骤:

     视频  Input >Demux >(h264:packetizer) > Decoder >Show

      音频 Input >Demux >Decoder >resampler >mix >play

     首先需要了解VLC的是基于插件实现功能的,这样方便VLC的功能扩展,在VLC-学习1中提到的plugin中就是这些插件存放的地方。

  那么这些Input, Demux, Decoder ,Show模块无一例外的都是插件形式提供的。

     那么我们分别来看其中的几个大模块的大致作用

      a>  Input 实际上就是位于module/access模块下具体有几种 tcp, udp, file, rtsp等等,其实这个模块无外乎就是获取数据的地方。

            无论是何种输入的模式,都需要根据选择实现:pf_read,pf_control,pf_seek, pf_block几个模块。

            插件的调用方式:查看VLC学习-2中的内容

      b> Demux(解复用功能):对不同格式的数据分离音视频,因为公司用到的就暂时只有PS,es, 所以自己也只是对这两种比较熟悉。

           PS相对于ES(h264)实际上就是多了PS相关的头。在解复用的时候需要将流中的PTS,DTS,SCR抽取出来,做音视频播放,同步等。

        另外因为有PS的头信息,可以根据这些信息判定PS包是否完整。PS流的音视频格式等。

           ES(h264)在Demux的时候,是通过packize_h264的功能将h264流中的帧率获取到I-Slice,P-Slice,B-Slice,SPS,PPS信息,从中获取到帧率,具体的计算帧率的方式

           可以参照live555中的h264Framer中的实现。 获取过程太长,截取一小段代码:
        

	unsigned num_units_in_tick = bs_read( &s, 32 );
	unsigned time_scale = bs_read( &s, 32 );
	unsigned fixed_frame_rate_flag = bs_read( &s, 1 );
	fps = time_scale/(2.0*num_units_in_tick);
           那么在执行es_out_send前将pts加入到block_t的实例中就OK。直接上代码:

        iNow = time(NULL);
        int64_t m_gap =  (int64_t)((double)1000000.0 / fps);
	if (iNow > p_sys->m_last + m_gap)
	{
	    p_block_out->i_dts = p_block_out->i_pts = p_sys->m_last = i_now;
	}
	else
	{
	      p_sys->m_last +=m_gap;
	      p_block_out->i_dts = p_block_out->i_pts = p_sys->m_last;
	}		

	es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block_out->i_dts );
        es_out_Send( p_demux->out, p_sys->tk->p_es, p_block_out );

       c > Decoder 解码: 好像要绕好多弯弯,留待后面详解

             视频: video.c 主要是调用ffmpeg库,将传入的数据解码为图片YUV420格式 输出,那么渲染的时候可以有2种方式,直接渲染YV12,或者是转为RGB

             音频:  araw.c 主要是重采样,量化,混音

        d >show  音频播放,视频渲染。这两个自己也不是太清楚,看dx的demo吧

              视频渲染: DirectX 或者D3D 这个就不说了吧

              音频: DirectSound

下面接着看上面所提到的 都是在从哪儿进入去调用 module_need()接口而完成插件的加载呢。VLC采取的是动态的加载插件的方式,实际上在项目使用中,可以将

这些动态库在程序初始化时一次性全部载入,然后在程序运行中,只需要找到入口即可。我们这边就是这样去使用的。有兴趣的可以去试试。

>> vlc 是怎么事件驱动的呢: MainLoop()在core模块的Input分支下? 

      在第一部分提到的instance, media, player的初始化中,好像都没有看到哪里创建了线程......

      初始化完成后,我们需要做的一个动作是调用libvlc_media_player_play(libvlc_media_player_t* player);

       那我们进入到接口中:

 在这个接口中有这么一句:这个input_Create调用了libvlccore库中,这个库就是整个vlc的逻辑控制
  的核心库了,所有的控制都是在这里完成的。
p_input_thread = input_Create( p_mi, p_mi->p_md->p_input_item, NULL,
                                   p_mi->input.p_resource );

     这个接口真的是做了好多事情啊:几乎所有的东西在这个里面初始化的,可以说自己完全没看懂么,自己能够看懂的就只有

创建了input_thread对象,并且通过 input_ConfigVarInit( p_input )及 input_ControlVarInit( p_input ) 两个接口初始化了很多东西

另外里面的一个函数调用: p_input->p->p_es_out_display = input_EsOutNew( p_input, p_input->p->i_rate ); 这个也是自己没能理解到底是什么,

    

    匆匆进入到input_start()接口: 里面很简单,只是创建了线程:线程的调用为Run() 在core库的input.c中

首先:创建es out, 在前面提到 的流程:ps >(demux) > es 
p_input->p->p_es_out = input_EsOutTimeshiftNew( p_input, p_input->p->p_es_out_display, p_input->p->i_rate );
其次:初始化动作 InputSourceInit( p_input, &p_input->p->input,p_input->p->p_item->psz_uri, NULL )
       这里就主要Open了3个东西Demux, access 及stream ,可以理解为stream 就是在access上面封装的一层的。

 哎:囫囵吞枣... 找到了2个模块的初始化的地方。

该死的初始化总算是完了吧,进入了MainLoop()中,这可是VLC的发动机啊!

-------------------------------------------------------------------------------








       

  




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值