tremolo or tremor -モバイル環境のOggVorbisのデコードについて-
はじめに
の続き。
上記記事では、Windows上での
について記述している。
今回は、Android(Xamarin.Androidで使うことを前提としている)での
について見ていく。長くなってしまったので、他2項目については別の機会で記述する予定である。
上記記事では、当初よりクロスプラットフォームでの使用を念頭に置いて進めていたが、デスクトップにおいてはクロスプラットフォームではあるものの、時間の都合上ライブラリの検証等ができず、モバイルを含めたところにまで手を伸ばせなかった。
そこで、まず今回はAndroidのみではあるがモバイル環境でのOggVorbisライブラリについて調査してみた。
本格的に見ていく前に、
- クロスプラットフォーム
- デスクトップ
- モバイル
というワードを前提を書かずに挙げているが、私が想定しているクラスプラットフォームについて図示してみる。
共有できるものは共有して、楽に開発できるようになればいいという考えで進めている。 クロスプラットフォームのアプリケーションにおいて、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 is the name of Xiph.org's container format for audio, video, and metadata.
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.
ということで、
OggにVorbisで圧縮した音声を格納したものがOgg Vorbis。(なお、VorbisはOgg以外にも格納でき、Matroskaでも対応している。)
3つのトレモロ
調査していると、Androidで使われているOggVorbisデコーダは、tremoloと呼ばれているにもかかわらず、他ではtremorというものが使われていたりといろいろと情報があり、それらをまとめて取り扱った記事は全く見られなかったので、まとめておく。
大まかに言うと tremorから派生し、ARM向けのアセンブラで書かれたtremoloがあり、そのとあるバージョンから派生したのがAndroidで使われているtremoloである。
tremorとは
まず、本家。
Tremor is a fixed-point version of the Ogg Vorbis decoder for those platforms that can't do floating point math. Tremor - XiphWiki
とあるようにOggやVorbisを作っているxiph.orgがOgg Vorbis decoderの固定小数点版として作成、ARMの場合、計算の一部がアセンブラで実装されている。 FPUの無いもしくはあっても遅い機器向け、モバイル向き。低消費メモリ版もあるよう。
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.
ややこしいが、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においてARMv5対応がなくなり*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を作成した。 この記事を書いている最中に後述するものを見つけた。それに合わせて修正したのをおいておく。
C#からDLL関数の呼び出し
さて、ここからは、Xamarin.AndroidにおいてのOggVorbisデコーダの使用について見ていきたいと思う。
tremoloバインディングは
に詳しい。
をもとにしていたが、最近修正したのもがあるよう。
ライブラリは、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の)について見ていこうと思っている。
*1:http://takeshich.hatenablog.com/entry/2017/12/22/000000
*2:https://git.xiph.org/?p=tremor.git;a=summary
*3:https://android.googlesource.com/platform/external/tremolo/+/master
*4:Support for ARMv5 (armeabi), MIPS, and MIPS64 has been removed. Attempting to build any of these ABIs will result in an error.
https://developer.android.com/ndk/downloads/revision_history?hl=ja