なんとなく

誰得感満載な記事が多いかも。Mono関係とLinuxのサーバ関係、レビューとか。

tremolo or tremor -モバイル環境のOggVorbisのデコードについて-

はじめに

takeshich.hatenablog.com

の続き。

上記記事では、Windows上での

  • OggVorbis(Vorbis->PCM)のデコード
  • OpenAL
  • RadegastにおいてFMODで実装されているものをOpenALでの実装に変更

について記述している。

今回は、Android(Xamarin.Androidで使うことを前提としている)での

  • OggVorbis(Vorbis->PCM)のデコード
    • ライブラリの調査
    • Androidでの検証

について見ていく。長くなってしまったので、他2項目については別の機会で記述する予定である。

上記記事では、当初よりクロスプラットフォームでの使用を念頭に置いて進めていたが、デスクトップにおいてはクロスプラットフォームではあるものの、時間の都合上ライブラリの検証等ができず、モバイルを含めたところにまで手を伸ばせなかった。

そこで、まず今回はAndroidのみではあるがモバイル環境でのOggVorbisライブラリについて調査してみた。

本格的に見ていく前に、

というワードを前提を書かずに挙げているが、私が想定しているクラスプラットフォームについて図示してみる。

f:id:takeshich:20180807202512p:plain

共有できるものは共有して、楽に開発できるようになればいいという考えで進めている。 クロスプラットフォームのアプリケーションにおいて、UIの部分をのぞいてある程度、共通のコードで済ますことができるようにしたいと考えており、C#でそれを実現しようと思っている。

まとめ

記述していたら長くなったので、まとめを先に書いておく。

AndroidでOggVorbisのデコードについて調べてみるとシステム側にlibvorbisidec.soというものが存在し、それがOggVorbisをデコードするライブラリのようだ。 Android Nからは、システムライブラリの一部がアクセスできなくなっており、そのため、N未満のバージョンであれば、libvorbisidec.soをアプリケーションから叩くことが可能であったが、N以降は叩くことができなくなっている。

そこで、Android向けにビルドできるOggVorbisのデコーダーについて調査し、検証してみた。 調査した結果、AOSP(Android OpenSource Project)にあるlibvorbisidec.soのソースを使い、ビルドし、それをバインディングすることでC#で使用することができることがわかった。また、脆弱性やバグといった点からも管理し続けられており、継続性は今の所担保されているということがわかった。Androidのみで採用するのであれば、AOSPにあるソースである。

ただ、iOSも含めたモバイルのことにまで考慮を巡らせるのであれば、サポートCPUの変化によってAOSPにあるソースは過去にはメリットがあったが、現在時点で公式のモバイル向けのOggVorbisデコーダーと比較して特段メリットがあるわけではない状況になっている。C#ではないところのライブラリも同じソースでビルドできたほうが管理面では楽なはずなので、AndroidのみならずiOSも含めるモバイル環境の場合は、そちらも考慮しておいたほうが良さそうだ。C#からDLL関数の呼び出しを考えるとAPIはモバイルでは同一なので、必要があった場合に考えても遅くはない。

ライブラリをC#バインディングするものについては、一部修正及びさらなる調査が必要であったが、今回の調査の範囲では問題なく動いている。

OggVorbisデコーダの調査

Ogg Vorbis

前回*1も触れたが、再度触れておく。

Ogg Vorbisは、オーディオの圧縮形式のことで、詳細は、以下引用を確認してほしい。

Ogg

Ogg is the name of Xiph.org's container format for audio, video, and metadata.

Vorbis

Vorbis is the name of a specific audio compression scheme that's designed to be contained in Ogg. Note that other formats are capable of being embedded in Ogg such as FLAC and Speex.

http://vorbis.com/faq/#names

ということで、

  • Oggはメディアを格納するためのコンテナフォーマット。
  • Vorbisは実際の音声圧縮のフォーマット。

OggVorbisで圧縮した音声を格納したものがOgg Vorbis。(なお、VorbisOgg以外にも格納でき、Matroskaでも対応している。)

3つのトレモロ

調査していると、Androidで使われているOggVorbisデコーダは、tremoloと呼ばれているにもかかわらず、他ではtremorというものが使われていたりといろいろと情報があり、それらをまとめて取り扱った記事は全く見られなかったので、まとめておく。

大まかに言うと tremorから派生し、ARM向けのアセンブラで書かれたtremoloがあり、そのとあるバージョンから派生したのがAndroidで使われているtremoloである。

f:id:takeshich:20180807202618p:plain

tremorとは

まず、本家。

Tremor is a fixed-point version of the Ogg Vorbis decoder for those platforms that can't do floating point math. Tremor - XiphWiki

とあるようにOggVorbisを作っているxiph.orgがOgg Vorbis decoderの固定小数点版として作成、ARMの場合、計算の一部がアセンブラで実装されている。 FPUの無いもしくはあっても遅い機器向け、モバイル向き。低消費メモリ版もあるよう。

リポジトリ*2を見ると、脆弱性の対応はされているよう。

tremoloとは

The Tremor lib as supplied is (fairly) portable C, but with the option to use gcc's inline assembler in the speed critical sections for ARM based machines.

Tremolo has taken this further; these speed critical sections (and many others) have been completely written in hand optimised ARM code. Tremolo makes no attempt to target anything other than ARM based devices with the long multiply instruction. The bit reading sections of the code assume a little endian memory system, but this can probably be changed if required.

Tremolo

ややこしいが、tremorとtremoloは違って、tremoloはtremorのあるバージョンから派生し、tremorもARMアセンブラで書かれた部分はあるもののtremoloはそこからさらにARM最適化をほどこしたもののようだ。

The API to the library is identical - Tremolo and Tremor should be interchangable (except Tremolo should be faster)!

ともあり、ライブラリとしてもtremoloではなく、tremorを採用しても問題ないことを示唆してくれている。 なお、現状のiOSがサポートする環境のCPUは64bitのarmだから、tremoloを採用する積極的な理由を見出すことはできないと考える。

最新版は、v0.08

Androidで採用されているtremoloとは

tremoloのv0.07を元にしており、さらにバグフィックスがされているようだ*3

また、上述したようにtremoloの最新版はv0.08であるが、Androidで採用されているtremoloとはファイル単位で違った部分がある。

現状、AndroidのNDKにおいてarm対応がなくなり*4、tremorとtremoloの差異はほとんどなくてなっているように思える。

脆弱性対応

tremorからの視点であるが、

Information on source package libvorbisidec

を見ると時々脆弱性が発見されている。

tremorにある脆弱性はtremoloにあるのかよくわからないが、相互のコミットログを確認するとtremorで対応されたものについては、Androidで採用されているtremoloにおいて対応しているよう見える。

調査して判断したこと

Androidにおいては、

という点で、AOSPにあるtremoloを採用するのが妥当と判断した。

今後、iOSでも調査したいと考えるが、

  • 現状のiOSが採用されている環境のCPUは64bitのarm
  • tremorとtremoloの差異はほとんどなくてなっているように思える

ということを考えると、

という点を踏まえ、AndroidのみならずiOSも含めたモバイル環境では、オリジナルのtremorを採用したほうがよいように思える。

NDKによるビルド

AOSPにあるソースを利用したAndroid.mk Application.mkを作成した。 この記事を書いている最中に後述するものを見つけた。それに合わせて修正したのをおいておく。

github.com

C#からDLL関数の呼び出し

さて、ここからは、Xamarin.AndroidにおいてのOggVorbisデコーダの使用について見ていきたいと思う。

tremoloバインディング

d.hatena.ne.jp

に詳しい。

github.com

をもとにしていたが、最近修正したのもがあるよう。

github.com

ライブラリは、CMakeでbuildできるよう(未確認)

当方は、前述したように、AOSPにあるソースを利用したAndroid.mk Application.mkを作成して、NDKでビルドしたライブラリを使用している。

vorbis-sharp.csを使用したところ、一部問題があるが、稼働確認という点では、問題ないと思われるので、とりあえず対象箇所のコメントアウトで済ませてしまっている。

  • Disposeにおいて、ov_clearが AccessViolationExceptionで落ちる点
using (var osb = new OggStreamBuffer(stream))
{
~略~
 }

としているところで、落ちる。

ちょっとGCの動きとかが、よくわからず、どういう方法を取ればいいのか検討がつかなかったが、 ソースを見るとDisposeメソッド内で、配置されたポインタがあることをみて、freeしていたので、とりあえずov_clearをコメントアウトしておいた。 ov_clearの処理を見たところ、使用してないメソッドもあり、使用しているポインタについてはfreeされるはずなので、ov_clearが動かなくても問題ないのかなぁと勝手に判断している。 ただ、同じファイルを連続でデコードするとAccessViolationExceptionが出るので、これも原因なのかもしれないと思っているが、とりあえず、今回はコメントアウトで放置している。要はよくわかっていない。今後詳しく見ていく予定。

また、今回のには直接は関係ないが、前回*5、こちらを利用しようと思ったのがうまくいかず、時間もなかったので、別のライブラリで済ませてしまっていた。 今回、まずPCにおいて、軽くデバッグしてみたところov_readで-131が返ってきてしまい、デコードできていなかった。OV_EINVALであるから、引数をチェックしてみたところ、

vorbis/vorbisfile.h at 679433ebb8287744a9801f847b7a105dbc2a0404 · xiph/vorbis · GitHub

  • tremolo

Tremolo/ivorbisfile.h - platform/external/tremolo - Git at Google

デスクトップ等で使うvorbisとtremoloとではov_readの引数の数が違っていた。。。

そのため、vorbisで使うためには、ov_read周りの修正が必要だった。そこを修正するとデコードすることは可能だった。 issueでも上げてようかと思っているのだけど、英語が書けないのよ。でも頑張ろう。

PCMにデコードしたものは、OpenALに渡して、再生できるのは確認できている。次はOpenTKでバインディングするOpenAL(Androidの)について見ていこうと思っている。