要求
-
必须以 chain模式 为基础。 大多数简单的播放管道会将音频从解码器推入音频接收器。
-
必须基于 get_range 进行操作大多数专业音频应用程序将在音频接收器从管道中抽取样本的模式下运行。 通常,这是从音频接收器请求N个样本的回调中完成的。 回调是从线程安排的,也可以是从音频硬件设备的中断安排的。
-
精确的样本,精确时钟。 即使在采样丢失或流中发现不连续的情况下,音频接收器也必须能够提供精确采样时钟。
-
准确的播放时间。 音频接收器必须能够在其准确时间播放样本。
-
尽可能使用DMA访问。 当硬件可以执行DMA时,我们应该使用它。 这也应该在缓冲池上起作用,以避免将数据复制到内核空间或从内核空间复制数据。
设计
该设计基于一组基类和样本环形缓冲区的概念。
+-----------+ - provide preroll, rendering, timing
+ basesink + - caps nego
+-----+-----+
|
+-----V----------+ - manages ringbuffer
+ audiobasesink + - manages scheduling (push/pull)
+-----+----------+ - manages clock/query/seek
| - manages scheduling of samples in the ringbuffer
| - manages caps parsing
|
+-----V------+ - default ringbuffer implementation with a GThread
+ audiosink + - subclasses provide open/read/close methods
+------------+
环形缓冲区是一块连续的内存,分为几段。每个段都有segsize字节。
play position
v
+---+---+---+-------------------------------------+----------+
+ 0 | 1 | 2 | .... | segtotal |
+---+---+---+-------------------------------------+----------+
<--->
segsize bytes = N samples * bytes_per_sample.
环形缓冲区具有播放位置,该播放位置以段表示。播放位置是设备当前正在从缓冲区读取样本的位置。
可以将环形缓冲区置于PLAYING or STOPPED状态。
在这种STOPPED状态下,没有样本播放到设备,并且播放指针不前进。
在PLAYING状态下,样本被写入设备,并且在将每个段写入设备后,环形缓冲区应调用可配置的回调。在这种状态下,在写入每个段之后,将播放指针前进。
对环形缓冲区的写操作会将新样本放入环形缓冲区。如果环形缓冲区中没有足够的空间,则写操作将阻塞。即使缓冲区为空,缓冲区的播放也不会停止。当缓冲区为空时,设备将静音。
环形缓冲区是通过无锁原子操作实现的,尤其是在读取侧,因此可以实现低延迟操作。
每当将新样本放入环形缓冲区时,都会读取读指针的位置。获取所需的写入位置,并在所需位置和实际位置之间进行区分。如果差异为< 0,则样本为时已晚。如果差异大于隔离长度,则书写部分必须等待播放指针前进。
模式
1、基于chain的模式
在基于链的模式下,字节被写入环形缓冲区(ringbuffer)。填充环形缓冲区后,此操作最终将阻塞。
当没有采样及时到达时,环形缓冲区将静音。每个到达的缓冲区将在正确的时间放入环形缓冲区。这意味着放下采样或插入静音是自动完成的,非常准确,并且与播放指针无关。
在这种模式下,环形缓冲区通常保持尽可能满。当使用较小的缓冲区(较小的段大小和段大小)时,可以将音频从接收器开始播放到播放时的等待时间保持较低,但是必须在读取和写入之间至少进行一次上下文切换。
2、基于get_range的模式
在基于get_range的模式下,audiobasesink将使用环形缓冲区的回调函数从对等元素获取segsize样本。然后将这些样本放在下一个播放位置的环形缓冲区中。假定get_range函数在播放指针到达写入指针之前返回足够快的速度以填充环形缓冲区。
在这种模式下,环形缓冲区通常保持尽可能为空。在创建样本的元素与将样本实际写入设备之间不需要上下文切换。
3、DMA模式
可以进行基于DMA的音频设备访问的元素必须从GstAudioBaseSink该类的子类中并将DMA环形缓冲区包装在的子类中GstRingBuffer。
在将每个样本写入或播放到设备后,ringbuffer子类应触发回调。可以从线程或音频设备的信号触发此回调。
时钟
该GstAudioBaseSink课程将使用ringbuffer充当时钟供应商。它可以通过使用播放指针和延迟来计算时钟时间来实现。