この文書はAVIファイルでのVBR音声の同期方法について,これまで用いられてきたNandoによる方法と,今回新たに報告する新方式の2つの方法を説明する. この文書を理解するためには,AVIファイルの基本構造とVBR音声形式の基礎を理解している必要がある. もしファイル構造には興味が無いが試してみたいのであれば,付録1を参照されたい.
AVI(Audio Video Interleave)ファイルにVBR(Variable Bitrate:可変ビットレート)音声を格納することは一筋縄にはいかない. 適切な格納をしなければ,ビデオとの同期が取れなくなってしまい,音ズレが起こる. この章ではAVIファイルの基本とVBR音声で音ズレする原理を説明する.
AVIファイルはMicrosoftがWindows向け動画フォーマットとして制定したファイルフォーマットである. Windowsの普及だけでなく,構造のわかりやすさから多くの編集ソフトが存在する. このため,AVIファイルは現在最も普及している動画ファイル形式のひとつとなっている.
AVIの内部構造はRIFF形式であり,データの種類を識別する4文字コードさえ定義されていれば,基本的にどのようなデータでも格納できる. 1996年にAVIファイルを拡張する形式としてOpen-DMLが制定され,ファイルサイズの制限もほぼ無くなった.
AVIファイルではVBRを利用できないというのは明らかな誤解である. 実際,多くの圧縮されたビデオストリームはVBRであり,AVIファイルでも問題なく扱える. また,VBRな音声ストリームも同様の方法で扱うことができる(第3章を参照). しかし,Microsoftの提供するVFW(Video for Windows)のAPIではVBR音声ストリームを適切に扱うのが困難である. このため,多くのソフトがVBR音声を適切に扱えず,同期の取れないファイルになってしまう. このようにAVIファイルでVBR音声を扱うには対応したソフトを利用しなければならない.
VBRとは,情報量の少ない部分に割くビットを節約し,その分を情報量の多い部分に割り当てて全体の音質を上げるという戦略に基づいている. 従って,同じサイズであればCBR(Constant Bitrate:固定ビットレート)の音声より音質は良い.
VBRでは,ビットレートの高い部分と低い部分がファイル中に混在している. このため,再生位置とファイル中の位置は必ずしも一致しない. これが後述する音ズレの原因となる.
MicrosoftのAVIスプリッタは,AVIヘッダのうち次の値を元に音声のタイムスタンプを決定する.
WAVEFORMATEX::nBlockAlign
AVIStreamHeader::dwRate / AVIStreamHeader::dwScale
VBR音声に対応していない多くのソフトは圧縮音声のヘッダ情報を,nBlockAlign=1
,dwScale=1
,dwRate=nAvgBytesPerSec
として扱っている.
すなわち,1バイト単位のブロックが1秒間にnAvgBytesPerSec
個含まれていることを意味する.
CBRの場合ファイル中の位置と再生位置は一致するのでこれで同期が取れるが,VBRの場合は前述の通りファイル中の位置と再生位置が一致しないので同期が取れなくなる.
ではどうすれば同期させることができるだろうか.
VBR音声の同期方法はこれまでNandoの方法しかなかった. この章ではその方法とともに,筆者の発見した新方式について,具体的に説明する.
この章で用いる単語の意味は以下で統一している.
この方法は,VirtualDub-MP3で最初に実装された方法である. その後VDub-MP3の開発停止を受け,NandoによりNandubに実装されて普及したためNandoの方法と呼ばれている. VirtualDubModやAVIMux-GUI等のVBR音声対応ソフトはこの方法を採用している. 当初はMP3のみの実装であったが,AACやDTS等でも有効であることが示されている.
多くの音声圧縮形式では,いくつかの音声サンプルをまとめて1パケットとして圧縮している. 例えばMPEG-1 Layer3の場合,1パケットは1152サンプルを含む. Nandoの方法ではこの1パケットに含まれるサンプル数が一定である必要がある. AVIファイルにデータを格納するときは,各wbチャンクには一定数のパケットを格納する. 例えばNandubやVirtualDubModでは1パケットを1つのwbチャンクに格納している. こうすることで,wbチャンクに含まれるサンプル数が一定,すなわち,wbチャンクの継続時間は一定となる.
ビデオストリームの場合,各フレームは1つのdb/dcチャンクに格納され,dwRate/dwScale
によってフレームレートが定義されている.
すなわち,フレームレートはdb/dcチャンクのレートを定義している.
VBR音声を上記の方法で格納していれば,1秒間に対応するwbチャンクの数は一定になっている.
よってビデオと同様に,wbチャンクをブロック単位としてdwRate/dwScale
でブロックのレートを指定すれば同期が取れる.
音声のブロックサイズはnBlockAlign
で定義されるが,各ブロックは複数のwbチャンクをまたぐことはできない(AVIIF_FIRSTPARTまたはAVIIF_LASTPARTが指定されている場合を除く).
したがって,最大のwbチャンクのデータサイズをnBlockAlign
に指定すればよい.
また,ブロックのレートは,サンプリング数 / チャンクに含まれるサンプル数である.
例えば,48000HzのMPEG-1 Layer3で,各wbチャンクに2パケットずつ格納した場合,ヘッダ情報は次のようになる.
AVIStreamHeader::dwRate = 48000
AVIStreamHeader::dwScale = 1152 * 2
AVIStreamHeader::dwLength = wbチャンクの総数
WAVEFORMATEX::nBlockAlign = 最大のwbチャンクのデータサイズ
Nandubに見られる初期の実装では,nBlockAlign = 1152
と固定してしまっている.
しかし,MP3のパケットサイズは1152バイトを超えることがあるので,この値は適切ではない.
この問題は,Nandubの実装を引き継いでいる様々なソフトに残されている.
とはいえ,1パケット1チャンクの場合,wbチャンクのデータが1152バイトを超えるのは32000Hzまたは8000Hzかつ高ビットレートの時のみであり,問題が表面化する場面は少ない.
この方法は,wbチャンクに含まれるサンプル数を一定にすることで,各ブロックのタイムスタンプと実際の時間を一致させている. 従って,全くズレの無い再生が可能になる.
一方で問題もある. この方法を用いるためには1パケットに含まれるサンプル数が一定である必要がある. そのため,パケットに含まれるサンプル数の変動するVorbisのような形式はどうやっても利用することができない. また,この方法でAVIファイルを作成するには,AVI作成ソフトは個々の圧縮形式について,パケットに含まれるサンプル数を知らなければならない. このため,未知の形式を扱うことはできない.
次に,筆者の発見した新方式について説明する. Nandoの方法がタイムスタンプと同期しているのに対し,この方法はビデオストリームとの同期に着目した方法である.
新方式でのデータの格納は,ビデオのフレームレートが基準となる. wbチャンクのレートをビデオフレームレート/N (N=自然数)とし,ビデオフレームに時間的に対応する音声データを格納していく. ビデオにNフレーム間隔で音声をインターリーブする場合を考えると,Nフレームごとに音声データが現れることになり,このレートに一致する. ただし,必ずしもインターリーブしている必要は無い. ここで例として次の条件を考える.
このとき,最初のwbチャンクには,ビデオの0~2フレームに時間的に対応する音声を格納する. これは次のように計算でき,MP3の0~4パケットを格納すればよいことがわかる.
インターリーブ間隔 / ビデオフレームレート * 音声サンプリングレート / パケット中のサンプル数
= 3 * 1001/30000 * 48000 / 1152 = 4.17
続くwbチャンクでも同様に,ビデオの3~5フレームに対応する音声を格納する. 下記のように計算すると,MP3の5~8パケットとなる.
2 * 3 * 1001/30000 * 48000 / 1152 = 8.34
前述の通り,wbチャンクのレートはビデオフレームレート/Nである. 従って,Nandoの方法と同様にwbチャンクを1ブロックとして扱わせれば,ブロックのレートはビデオフレームレート/Nとすることができる. 上の例の条件では,以下のようになる.
AVIStreamHeader::dwRate = 30000
AVIStreamHeader::dwScale = 1001 * 3
AVIStreamHeader::dwLength = wbチャンクの総数
WAVEFORMATEX::nBlockAlign = 最大のwbチャンクのデータサイズ
格納の例では,最初のwbチャンクには5パケット含まれるのに対し,次のwbチャンクには4パケットしか含まれていない. AVIスプリッタはブロック単位でタイムコードを決定するため,ブロックに含まれるサンプル数が一定でないこの方法では,タイムコードと実際の再生時間に最大で1パケット分の誤差が生じてしまう. しかし,ビデオ1フレーム単位の精度での同期が可能なので,ビデオとずれるということはありえない. 従って,巨視的な問題となることは無いと考えられる.
一方,この特性は利点にもなりうる. Nandoの方法と違い,wbチャンクに含まれるサンプル数が一定である必要が無いので,Vorbisのような形式も他の形式と同様に利用できる可能性が高い. FFMPEG等によるAVIファイルへのVorbisの格納は,同期のために多大なオーバーヘッドを必要としていた(参考文献2を参照). しかし今回の方法であれば,大きなオーバーヘッドも必要なく,何よりMP3など他の音声形式と同じ方法で利用が可能である. これはソフトの開発における大きな利点となる.
この方法でAVIファイルを作成する時に必要なのは,音声データがビデオと時間的に対応することが保証されることだけである. Nandoの方法のように,音声形式個別の知識を必要とはしない. 通常,動画や音声のコンテナには再生時間を同期させるための情報があり,時間的な対応を保証するのは容易である. 従って,他コンテナからの変換や多重化を行なう場合,コンテナの知識さえあれば内部の形式まで気にする必要は無く,汎用的なプログラムが作成可能である.
これまで述べてきた特性とは別に,この方法には重大な欠点がある. OpenDML形式のファイルにこの方法を適用した場合,MicrosoftのAVIスプリッタではRIFF-AVIXリストへのシークができなくなる(RIFF-AVIリスト内へのシークは可能). ただし,独自にAVIファイルを扱えるMediaPlayerClassic等ではこの問題は発生しないため,MicrosoftのAVIスプリッタ独自の問題と考えられる. また,AVI1.0あるいはRIFF-AVIXリストを持たないOpenDMLファイルではこの問題は起こらない.
ここまで説明したように,AVIファイルはVBRの音声を十分扱える能力を備えているが,Nandoの方法のようなハックが必要であった. これを実装するためには,音声形式についてある程度の知識を必要とし,実装はやや難しかった. 今回発表した新方式は,Nandoの方法より実装が単純かつ汎用的なため,VBR音声対応ソフトの作成は容易になった. これでAVIファイルに付き纏うVBR音声の問題も少しは軽減されるかもしれない.
AVIファイルのデメリットとして挙げられる事実に,音声にVorbisを(一般的な方法では)使えないというものがあった. しかし,今回発表した新方式ではVorbisを一般的な方法で扱える可能性が出てきた. Vorbisの利用が可能となれば,AVIファイルの明らかなデメリットはもうほとんど残されていない.
AVIファイルはマルチ音声だけでなく,マルチビデオを既にサポートしており,この点では他コンテナより進んでいる. (ただし,それを効果的に扱えるスプリッタやプレイヤはまだ無い.) また,RIFF形式の汎用性から,互換を保ったままでもチャプターやアイコン,画像なども格納可能となりうる. AVIファイルは確かに古い形式かもしれないが,まだまだ実用的と言えよう.
現在のところ,新方式の完全な実装は存在しないが,既存のソフトで検証するための方法を用意した. あくまで検証目的であり,処理を一部省略しているため,実用には適さないことを留意していただきたい. また既存ソフトの都合により,MP3のみでの検証となる.
文中の誤り等は下記アドレスまで連絡ください.
MakKi <makki_d210@yahoo.co.jp>