Audiosink design

Audiosink的設計,須要知足下列一些需求:ios

  •  良好的chain_based 支持。絕大多數簡單playback pipelines都是將音頻數據從decoder直接push給audiosink;express

  •  良好 getrange_based支持。大部分專業的應用都是audio sink從pipeline拉取數據。典型的做法是在一個回調函數中pull N Samples,回調函數通常在一個單獨的線程或者是硬件設備來調度。ide

  • 提供準確的時鐘。必須可以提供即便samples被丟棄或在流中發現不連續時也能提供採樣準確的時鐘。函數

  • 可能的話,提供DMA支持oop

Design

The design is based on a set of base classes and the concept of a ringbuffer of samples.ui

+-----------+ - 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 +------------+

The ringbuffer is a contiguous piece of memory divided into segtotal pieces of segments. Each segment has segsize bytes.this

 play position  v  +---+---+---+-------------------------------------+----------+ + 0 | 1 | 2 | .... | segtotal | +---+---+---+-------------------------------------+----------+ <---> segsize bytes = N samples * bytes_per_sample. 
如上,環形緩衝器有一個play position,以segment表示。play position是設備正在從ringbuffer讀取samples的位置。
ringbuffer能夠進入PLAYING或STOPPED狀態:
在STOPPED狀態下,沒有samples被put到設備,play pointer不前進。
在PLAYING狀態下,samples被寫入設備,而且在每一個seg被寫入設備以後,ringbuffer應該調用可配置的回調,
play pointer在每一個段被寫入以後前進。

對ringbuffer的寫入操做將會把新的samples放入ringbuffer中。若是ringbuffer中沒有足夠的空間,
寫操做將被阻塞。即便buffer爲空,ringbuffer的播放也不會中止。當buffer爲空時,設備會播放靜音。
ringbuffer經過無鎖原子操做實現,特別是在讀取端,以便儘量地下降低延遲。
每當一個new samples要put進ringbuffer,先獲取read pointer,請求的write position和actural position做差值,
      /* get the currently processed segment */ segdone = g_atomic_int_get (&buf->segdone) - buf->segbase; /* see how far away it is from the write segment */ diff = writeseg - segdone;
   
.....

      /* segment too far ahead, writer too slow, we need to drop, hopefully UNLIKELY */
      if (G_UNLIKELY (diff < 0)) {
        /* we need to drop one segment at a time, pretend we wrote a segment. */
        skip = TRUE;
        break;
      }

      /* write segment is within writable range, we can break the loop and
       * start writing the data. */
      if (diff <= segtotal) {
        skip = FALSE;
        break;
      }

      /* else we need to wait for the segment to become writable. */
      if (!wait_segment (buf))
        goto not_started;
    }
 



若是差值diff<0,則samples來太慢了,丟掉來太慢的數據。若是diff > segtotal,寫入部分必須等待播放指針前進。atom

也就是須要設備先消耗掉ringbuffer中的數據,才能繼續往ringbuffer中寫入新的數據。spa

相關文章
相關標籤/搜索