2012年10月31日水曜日

ビデオやオーディオのDMAバッファの管理手法 (動かして遊べるソースコード付き!)

DMAバッファを管理する

ビデオやオーディオのように絶え間なくやってくるデータの入出力を処理する場合、一般にDMAを使います。
システムの中において、バッファの状態は主に「DMA中」、「DMA完了」、「未使用」の3種類に分類する事ができます。

DMAは、リクエストを出してからバッファにデータが完全にやってくるまでに時間がかかります。
DMAのリクエストを出してから即座にそのバッファに対して処理を開始する事は通常しません。

ビデオやオーディオのDMAバッファの管理は、DMAを出してからDMA完了イベントを待ち、到着したデータに対して処理を始める事になります。

配列による手法

DMAバッファを管理する機構を実現する際によく見かけるのは以下のような配列を用いた手法です。


バッファや管理構造体を配列として保持し、何番目が「DMA中」、何番目が「DMA完了」といった具合にインデックスで管理する方法です。非常にシンプルですし、何の問題もないのですが、実装してみると意外とごちゃごちゃした感じになる傾向があります。これは、「システムの全体挙動を見る」という視点を持って見た場合、「配列にアクセスする」という実装詳細が目に入ってきてしまうためなのかもしれません。

モデルベースによる手法

少し考え直して、以下のような簡単なモデルを作ってみました。
このモデルには登場人物が4人います。


queueは、任意の長さを構成可能なFIFOで、「未使用」状態のバッファを格納しておく場所です。
delayは、任意の遅延数を構成可能なFIFOで、設定した遅延数でデータが出てくるディレイ・ラインです。

viprocは、ビデオやオーディオを入力するモジュールで、データを入力すべきタイミングでのみ実行されるものです。viprocは、自身が呼ばれたらqueueからバッファを取りだし、バッファにデータを入力するためにDMAをリクエストします。加えて、バッファをdelayに押し込んで終了します。

voprocは、ビデオやオーディオを出力するモジュールで、データを出力すべきタイミングでのみ実行されるものです。voprocは、自身が呼ばれたらdelayからバッファを取りだし、バッファからデータを出力するためにDMAをリクエストします。加えてqueueにバッファを押しこんで終了します。

上記設計の場合、queueの深さは出力DMAが完了するために必要な深さが必要です。
その点さえ注意すれば、上記のモデルは以下のようなメリットがあります。
  • 実装の抽象度が高く、メインテナンスが容易。
  • データ入力が確定するまでの遅延数をdelay(ディレイ・ライン)の長さで任意に調整可能。
  • 入出力が完全同期系でなくても、delayとqueueの範囲で吸収可能。
  • 使用可能なバッファ数はqueueの長さで決められ、先読みなどの処理も容易。
  • その他。
先の配列による手法では、バッファ管理のためのインデックス変数などが上位に出てきて、ごちゃごちゃした感が否めませんでした。
モデルベースによる手法を用いる事で実装の抽象度が上がってシンプルな実装を実現できます。

上記に示したメリットは配列による手法でも当然のように実現可能です。
が、実際に実現した実装を比較してみるとモデルベース設計の効果がわかると思います。

動かして遊べるソースコード

実際に上記のモデルを動かして遊べるソースコードを用意しました。
viomodel.tar.gzからダウンロードして下さい。

2013/03/31追記

DMAバッファ管理手法の続編」に続編を書きました。

0 件のコメント:

コメントを投稿