锐英源软件
第一信赖

精通

英语

开源

擅长

开发

培训

胸怀四海 

第一信赖

当前位置:锐英源 / 开源技术 / C++开源 / VLC功能组件分解
服务方向
人工智能数据处理
人工智能培训
kaldi数据准备
小语种语音识别
语音识别标注
语音识别系统
语音识别转文字
kaldi开发技术服务
软件开发
运动控制卡上位机
机械加工软件
软件开发培训
Java 安卓移动开发
VC++
C#软件
汇编和破解
驱动开发
联系方式
固话:0371-63888850
手机:138-0381-0136
Q Q:396806883
微信:ryysoft

VLC功能组件分解

4.1 复合的多层输入技术

输入组件的中心思想就是处理包,但又不必知道包里的具体内容。它只是读包的ID,在包头(在MPEG中是SCR和PCR字段)的指示的正确时刻将包 投递到解码器。输入组件不需要具体细节。如,不需要知道怎样播放一帧或者几帧,也不需要知道什么是“帧”。像视频图像一样,基本流没有优先级。
经过分析发现输入组件对一个媒体文件做了以下这些事情:为每一个读取的文件派生输入线程。实际上,由于流是不一样的,因此输入文件的结构和解码器需 要重新被初始化。这些工作由接口线程(播放列表组件)调用input_CreateThread来完成:首先寻找一个能读取这个文件的输入插件(首先我们 要打开文件的socket,然后探测文件流的开始处以确定哪个插件可以读取这个文件),文件的socket是被 input_FileOpen,input_NetworkOpen或者input_DvdOpen函数所打开的,这些函数需要设置两个非常重要的参 数:b_pace_control和b_seekable。然后我们可以运行输入插件的pf_init函数和一个无限循环来实现pf_read和 pf_demux函数的功能,这个插件是用来初始化流结构(p—input->stream),管理文件包缓存,读取文件包并且使这些文件包分路。
但是这些最重要的任务是在输入的API高级函数的协助下完成的。

4.2 文件流的管理

这个己经打开输入socket的功能模块必须先规范两个属性如下:
(1)p_input->stream.b_pace_control:
不管文件流是否以客户端所要求的速率被读取(这个由流本身的频率和客户电脑系统的时钟所确定),比如说如果客户端不能读取文件足够快得话,这时一个 文件或者一个管道(包括TCP/IP连接)可以以客户端速率被读取,那么这个管道的另一端将会被write()阻塞掉;相反,如果客户端不能读取文件足够 快得话,UDP流(比如说被VLS所使用的流)以服务器端的速率被读取,当内核缓冲器满的时候文件包就会丢失,因此服务器时钟的漂移策略将会用来弥补这种 包的丢失。不管文件流是怎样的速率,这个属性是用来控制时钟的管理。
时钟管理的子菜单:当用到UDPsocket和远程服务器时,服务器时钟的漂流策略是必不可少的,这是由于如果在流文件传输过程中,两端的时钟有一 个哪怕是一点的混乱,那么利用漂流策略则可以让流文件说明这个时钟的偏差。这也就意味着在每一个基本流给出的频率中,由输入线程显示的日期将会有某种程度 的不同步,输出线程(通常说的是解码线程)将会处理这个问题。不同步的问题也可以出现在读取己连接上的设备文件上,比如说把这些文件读取到视频编码盘上。 由于cat foo.mpg|vlc没任何显示时钟问题的功能,仅仅从简单的cat foo.mpg|vlc上客户端是不可能觉察出时间的不同步的,因此此时应该知道用户的b_pace_control值。总之,当客户端和服务器都能同步 于同一个CPU时钟时,服务器时钟的漂流策略就可以被忽略掉。
(2)p_input->stream.b_seekable:
不管lseek()函数在文件中有没有被描述,我们都要调用它来管理流。基本上我们要么跳转到文件流的任何地方(在滚动栏上显示),要么就一字节一 字节的读取文件流。这个属性没有第一个属性对流的管理来的重要,但是它也不是多余的,这是因为catfoo.mpg1vlc中 b_pace_control和b_seekable有关联,比如当b_pace_control=1时b_seekable =0,相反就是当b_pace_control=0时b_seekable=1是不可能成立的。如果流是可以被搜寻的,p_input-> stream.p_selectede_area->i_size必须被设置(比如说在任意的字节单位 中,p_input->stream.p_selectede_area->i_size必须设置成和p_input->i_tell 一样,这是因为p_input->i_tell表示当前从流读取的字节)。
时间转换的偏移:时钟管理函数位于src\input\input_clock.c目录下。客户端所能知道的就是文件开头和结尾的偏移量 (p_input->stream.p_selectede_area->i_size),目前这个偏移量是用字节来表示的而且是依赖于插件 的。例如如何在界面上以秒来显示hell这个文件的时间信息呢?那就是客户端要获得PS流中的可以表明每秒读取多少字节的mux_rate属性,这个属性 可以随时更改但它在整个流传输过程中是不变的,这样可以用它来确定时间的偏移量。

4.3 输出到接口的结构分析

这里着重说明输入组件和界面之间的API通信。最重要的文件是include\input_ext-intf.h,它定义了input_thread_t structure,the stream_descriptor_t和ES描述符(可看成树的结构)。
注意到input_thread_t structure的特色是有两个void型的指针,这两个指针是p_method_data和p_plugin_data,创门分别用于缓冲管理数据和 插件数据。并且文件流的描述放在了一个树形结构的程序描述符里,这个程序描述符里包含了几种基本流的描述符。
下图表示出了这个树形结构,这里一个流启动了两个线程。而在多数情况下只能有一个线程,目前只有TS流下可以有启动多个线程,比如说一个电影和一场足球比赛可以同时播放,这对于卫星和光纤广播是足够用的了。

VLC输入组件和界面之间的API通信的树形结构图
VLC输入组件和界面之间的API通信的树形结构图
这里需要注意的是在对p_input->stream结构进行修改和存取时,必须先对其加锁。
Es被一个ID(这个ID适合于多路信号分离器来寻找),一个stream_id(the real MPEG stream ID),一个模式(在ISO/IEC 13818-1 table 2-29里被定义)和一些描述符来标示,它同样提供其他有用的信息给多路信号分离器。如果需要读取的流文件不是MPEG系统层的流(比如AVI或者 RTP),那么需要写一个规范的多路信号分离器,在这种情况下,如果要携带额外的信息时,就可能要用到一个void型的p_demux_data的指针, 这个指针在不传输流时会自动的被释放掉。
下面说明为什么要用ID而不是简单的用stream_id:当一个信息包(可以是TS包,PS包或者其他类型的包)被读取时,多路信号分离器将会从 这个包里寻找这个ID来找到相关的基本流,并且如果用户选择了基本流的话就分离这个流。对于TS包,我们能知道的唯一信息就是ES PID,因此我们保存的参考ID就是PID,PID在PS流里并不存在,因此需要写这个PID。当然所有在PS包里能找到的信息都是基于 stream_id的,但是既然每个私有流(AC3,SPU,LPCM的等等)都在使用同一个stream_id,因此仅仅基于stream_id是不够 的,在这种情况下,PES有效负载的第一个字节就是流的私有ID,把这个私有ID和stream_id经过某种形式的合并来获得想要的唯一ID。
流程序和ES的结构都写在了插件里的pf_init()函数里,这个函数可以随时被更改(在vic-0.8.0版本前都放在了src\input \input_programs.c中)。DVD插件解析.ifo文件后知道是哪个ES在信息流里,TS插件读取流里的PAT和PMT结构,PS插件也可 以解析PSM结构(目前很少见),或者通过预解析数据的第一个兆字节来编译树结构上不工作的部分。这里需要注意的是:因为几乎从来没有 PSM(program stream map)结构,因此在大多说境况下我们需要预解析(也就是说读取数据的第一个兆字节,然后又回到数据的开始处)PS流,虽然并不适合这样做,但是这是唯一 的选择。这样做会出现以下两个问题:首先不能解析不可被搜索到的流,因此ES树在不工作的时候被编译;再者,如果一个新的ES流在上一个数据包的第一个兆 字节后出现的话(比如说在节目字幕中不会出现对其的说明字幕),在没遇到第一个数据包前是不会出现在菜单里的。由于要花费很长的时间(即使包没被解码), 因此无法解析全部的流。
通常输入插件的任务是派生出必要的解码线程,它必须在其选择的ES流里调用 input_seleetES(input_thread_t*p_input,es_descriptor_t*p_es),流的描述符也包含一些区域 (比如说DVD里的章目和标题),这些区域在流里呈现出逻辑的不连续性。尽管当PSM(或者PAT/PMT)改变时要用到这些区域,但是在TS和PS流里 只有一个这样的区域。这个区域的目标是当寻找另一个区域时,输入插件要导入一个新的流描述符的树结构(这时选择流的ID可能是不正确的)。

4.4 接口用到的方法

Input_ext-intf.c提供了一些函数来控制读取流:
(l)input_SetStatus(input_thread_t*p_input,int i_mode):改变读取流的速度。i_mode可以是 INPUT_STATUS_END,INPUT_STATUS_PLAY,INPUT_STATUS_PAUSE,INPUT_STATUS_FASTER,INPUT_STATUS_SLOWER 的一种。读取流速度的主要是由变量p_input->stream.control.i_rate来决定。它的默认值为DEFAULT_RATE, 这个值越小,读取得速度就会越快,速度的改变要考虑到工input_ClockManageRef。暂停是通过简单的停止输入线程来获得的(此时被一个
pthread信号唤醒),在这种情况下,解码器也会停止。当统计解码时间(src/video_out/vout_synchro.c来统计)时需注意到这一点,如果p_input->b_pace_control==0时则不要调用这个函数。
(2)input_Seek(input_thread_t*p_input,off_t i_position):改变读取速度的偏移量。如果p_input->stream.b_seekable=0时不能调用这个函数。它的位置在 p_input->p_selected_area->i_start和 p_input->p_selected_area->i_size之间。(当前值是在 p_input->p_selected_area->i_tell)。由于多媒体文件可能会很大(尤其当我们读取像DVD这样的设备时), 因此偏移量必须要有64bit这么大。在很多系统下(比如FreeBSD),off_t的默认值就是64bit,但是在GNU libc 2.x下并不是这样,这也是为什么需要用-D_FILE_OFFSETBITS=64-D_USE_UNIX98来编译VLC的原因。随机的改变读取流的 位置会导致流的混乱,读取流的解码器也会出错,为了避免这种情况的发生,在改变读取流位置的之前,需要先发送一些空包(全零)。事实上,对于大多数的音视 频格式,足够长的零流就是流的溢出并且解码器也可以不出错。
(3)input_OffsetToTime(input_thread_t*p_input,char *psz_buffer,off_t i_offset):把偏移值转换成和时间一致(用于界面播放),通常在播放MPEG-2文件时这个一致性会受到破坏。
(4) input_ChangeEs(input_thread_t*p_input,es_descriptor_t*p_es,u8 i_cat):选择部分基本流的i_cat类型和p_es,用于更改语言和字幕路径。
(5)input_ToggleEs(input_thread_t*p_input,es_descriptor_t*p-es,boolean_t b_select):用来清除界面上的被选择的基本流。

4.5 缓冲器的管理

输入插件必须要执行分配和删除分配包的功能,这个功能的实现要用到以下四个基本函数:
(1)pf_new_packet(void*p_private_data,size_t i_buffer_size):关联到缓冲器的i_buffer_size的大小,分配一个新的data_packet_t。
(2) pf_new_es(void*p_private_data):分配一个新的pes_packet_t。
(3)pf_delete_packet(void*p_private_data,data_packet_t*pes_data):删除p_data。
(4) pf_delete_es(void*p_private_data,pes_packet_t*p_pes):删除p_pes。
以上四个函数都把p_input->p_method_data(即*p_private_data)作为第一个参数,因此可以保存这个分配纪录和释放包。缓冲器的管理可以有以下三种方法:
(l)传统的libc分配:在PS插件中每次经常要用到malloc()和free()函数来分配和删除分配包,这并不像想象中的那么慢。
(2)Netlist:在包有问题的开头处用这种方法来分配一个非常大的缓冲空间,然后通过管理指针列表来释放包(这就是netlist)。这种方 法只有当所有的包都是同样大小时效果才会很好,它长时间用在TS输入上。DVD的插件也会用到它,但是要把加入的标志符也考虑进去,这是因为缓冲器 (2048bytes)通常被若干个包所共享。现在这个方法己经不被使用而且也没有文档记录。
(3)缓冲器的cache:这种方法是目前在发展中的最新方法,它己经用在了PS的插件中。它的思想是调用malloc()和free()来获得流 的不规则规律,然后经过cache系统后重新使用所有已被分配好的缓冲空间。现在的工作就是要扩展它在性能没妨碍的前提下可以用到任何插件里,但是目前还 没有这方面的参考文档。

4.6 流的分路

流文件被pf_read读取后,插件必须把一个函数指针给分路函数,这个分路函数用来解析包,收集PES并把包送到解码器里。这个分路函数是专门为 标准的MPEG结构(PS和TS)来写的,必须对pf_demux表明input_DemuxPS和input_DemuxTS,当然也可以自己写这个函 数。

友情链接
版权所有 Copyright(c)2004-2021 锐英源软件
公司注册号:410105000449586 豫ICP备08007559号 最佳分辨率 1024*768
地址:郑州大学北校区院(文化路97号院)内