大自然搬运工,VLC官方手册学习笔记。链接
复杂的多层输入
输入模块背后的思想是处理数据包,而根本不知道其中包含什么。它只需要一个包,读取它的ID,并在包头中指定的正确时间(MPEG中的SCR和PCR字段)将其发送到解码器。所有的基本浏览操作都是在不查看基本流内容的情况下实现的。
因此,它仍然是非常通用的。这也意味着你不能像“现在播放3帧”或“向前移动10帧”或“尽可能快地播放所有帧”。它甚至不知道“frame”是什么。没有像视频那样的特权基本流(原因很简单,根据MPEG,一个流可能包含多个视频ES)。ES流:也叫基本码流(elementary stream),包含视频、音频或数据的连续码流。
读取文件
每次读取文件都会生成一个输入线程。实际上,输入结构和解码器需要重新初始化,因为流的特性可能不同。接口线程(playlist module)调用input_CreateThread。
一开始,vlc通过调用函数 module->pf_activate来寻找access或access_demux模块来打开流。如果成功,则启动模块线程,vlc可以开始寻找demux模块来解复用访问模块的输出。
流管理
已打开输入套接字的函数必须指定有关它的两个属性:
-
p_input->stream.b_pace_control:流是否可以按我们自己的速度读取(由流的频率和主机的系统时钟决定)。例如,可以按我们的速度读取文件或管道(包括TCP/IP连接),如果读取速度不够快,管道的另一端将只阻塞write()操作。相反,UDP流(如VideoLAN服务器使用的流)是按照服务器的速度进行的,如果我们读得不够快,当内核的缓冲区满了,数据包就会丢失。因此,服务器时钟引入的误差必须得到定期补偿。此属性控制时钟管理,以及是否可以执行快进和慢动作。
时钟管理中的微妙之处:使用UDP套接字和远程服务器,误差是不可忽略的,因为在整部电影中,如果其中一个时钟稍有问题,它可以占秒。这意味着输入线程给出的呈现日期在某种程度上可能与每个基本流中给出的频率不同步。输出线程(顺便说一句,译码器线程)必须处理它。
当从例如连接到视频编码板的设备(如video4linux的/dev/video)读取数据时,可能会发生同样的问题。我们不可能把它和一只简单的猫区别开来每加仑食物|vlc-并不意味着任何时钟问题。所以说对了™ 会询问用户关于b_速度控制的值,但是没有人会理解它的意思(你不是地球上最笨的人,显然你已经读了几遍这段话来理解它:-)。无论如何,漂移应该可以忽略不计,因为主板将与CPU共享同一个时钟,所以我们选择忽略它。 -
p_input->stream.b_seekable
偏移时间转换
管理时钟的函数位于src/input/input_clock.c中。我们只知道文件的开始偏移量和结束偏移量(p_input->stream.p_selected_area->i_size),目前以字节为单位,但它可能依赖于插件。那么我们怎么能在界面上以秒为单位显示时间呢?PS流有一个mux_rate属性,该属性指示一秒钟应该读取多少字节。这随时都可能发生变化,但实际上对于我们所知的所有流来说,它都是一个常数。所以我们用它来确定时间偏移。
导出到接口的结构
让我们关注一下输入模块和接口之间的通信API。最重要的文件是include/vlc_input.h,它定义输入线程结构,include/input_internal.h用于流描述符结构(在access和demux之间)stream_t,include/vlc_esout.h用于ES描述符(可以将其视为树)。
对于所有对p_input->stream结构的修改和访问,必须保持p_input->stream.stream_lock.
通过ID(适当的解复用器将寻找的ID)、stream_id(真正的MPEG流ID)、类型(在ISO/iec13818-1表2-29中定义)和文字描述来描述e。它还包含解复用器的上下文信息,以及我们将在下一章中讨论的p_decoder_fifo。如果要读取的流不是MPEG系统层(例如AVI或RTP),则必须编写特定的解复用器。在这种情况下,如果您需要携带其他信息,您可以在方便的时候使用voidp_demux_data。它将在关机时自动释放。*
接口使用的方法
input_SetStatus ( input_thread_t * p_input, int i_mode )
Changes the pace of reading. i_mode can be one of INPUT_STATUS_END, INPUT_STATUS_PLAY, INPUT_STATUS_PAUSE, INPUT_STATUS_FASTER, INPUT_STATUS_SLOWER.
input_Seek ( input_thread_t * p_input, off_t i_position )
Changes the offset of reading. Used to jump to another place in a file. You mustn't call this function if p_input->stream.b_seekable == 0. The position is a number (usually long long, depends on your libc) between p_input->p_selected_area->i_start and p_input->p_selected_area->i_size (current value is in p_input->p_selected_area->i_tell).
input_OffsetToTime ( input_thread_t * p_input, char * psz_buffer, off_t i_offset )
Converts an offset value to a time coordinate (used for interface display). [currently it is broken with MPEG-2 files]
input_ChangeES ( input_thread_t * p_input, es_descriptor_t * p_es, u8 i_cat )
Unselects all elementary streams of type i_cat and selects p_es. Used for instance to change language or subtitle track.
input_ToggleES ( input_thread_t * p_input, es_descriptor_t * p_es, boolean_t b_select )
This is the clean way to select or unselect a particular elementary stream from the interface.
缓冲区管理
Input plugins must implement a way to allocate and deallocate packets (whose structures will be described in the next chapter). We basically need four functions:
pf_new_packet ( void * p_private_data, size_t i_buffer_size )
Allocates a new data_packet_t and an associated buffer of i_buffer_size bytes.
pf_new_pes ( void * p_private_data )
Allocates a new pes_packet_t.
pf_delete_packet ( void * p_private_data, data_packet_t * p_data )
Deallocates p_data
pf_delete_pes ( void * p_private_data, pes_packet_t * p_pes )
Deallocates p_pes.
All functions are given p_input->p_method_data as first parameter, so that you can keep records of allocated and freed packets.
缓冲区管理三种策略
- 传统的libc分配:很长一段时间以来,我们在PS插件malloc()和free()中使用,每次我们需要分配或释放一个包。与普遍的看法相反,它没有那么慢。
- Netlist:在这个方法中,我们在问题开始时分配一个非常大的缓冲区,然后管理指向空闲包的指针列表(“Netlist”)。只有当所有包的大小都相同时,这才有效。用于TS输入的长时间。DVD插件也使用它,但是添加了refcount标志,因为缓冲区(2048字节)可以在多个数据包之间共享。它现在已被弃用,不会被记录。
- 缓冲缓存:我们目前正在开发一种新的方法。它已经在PS插件中使用。其思想是调用malloc()和free()来吸收流的不规则性,但通过缓存系统重新使用所有分配的缓冲区。我们正在扩展它,这样它就可以在任何插件中使用而不会影响性能,但它目前还没有文档化。
分流
在被pf_read读取后,你的插件必须给出一个指向解复用器函数的函数指针。解复用器负责解析数据包,收集pe,并为解码器提供数据
标准MPEG结构(PS和TS)的解复用器已经编写完成。您只需要为pf_demux指定input_DemuxPS和input_DemuxTS。你也可以写你自己的解复用器。
PS:
scr (system_clock_reference)系统参考时钟 存在于ts流和program流中,用于多节目流间的同步; \pcr (program-_clock_reference)节目参考时钟 存在于ts流里,用于确定同一节目的解码时序;