本記事について
- OpenMAX IL についてざっくり説明します
- オープンソースである Bellagio OpenMAX IL を利用し、OpenMAX IL のコンポーネントの一覧取得まで(だけ)コードを書いてみます
- Ubuntu 22.04 x64 上で
docker run --rm -it ubuntu:bionic
した環境で動作確認をしています
OpenMAX
OpenMAX とは
- 動画・音声などのマルチメディアを操作するための API
- インターフェースのみで、実装は提供しない。C言語で言えば、ヘッダファイルのみが提供される。
- プラットフォーム非依存・アプリケーション非依存
- 動画再生は非常に大きなファイルが高速に処理される必要があり、ハードウェア・プラットフォームの支援が必要不可欠
- プラットフォームに強く依存したソフトウェアは移植が困難になる。OpenMAX は、処理速度も移植性も犠牲にしないインターフェースを提供する
- 動画再生は非常に大きなファイルが高速に処理される必要があり、ハードウェア・プラットフォームの支援が必要不可欠
- Khronos が策定
- Khronos が策定している API には OpenMAX の他、 OpenGL、Vulkan などがある
- ロイヤリティフリーである
- ロイヤリティフリーでないインターフェースには、例えば HDMI がある
- 3 つの API が定義されている
- DL (Develop Layer)
- メディア処理によく使われる機能の API
- 例えば高速フーリエ変換・ハフマン符号の展開など
- IL (Integration Layer)
- 主にメディアコーデックなどを制御する API
- 例えば圧縮データのデコードなど
- AL (Application Layer)
- 主にメディアプレーヤーなどを制御する API
- 例えば再生開始・終了の指示など
- DL (Develop Layer)
- 活用例として、Android の内部実装に OpenMAX IL のインターフェースが利用されている1
OpenMAX IL とは
OpenMAX IL 1.1.2 Specification clause 1.2
- Component という単位で機能が分割され、機能追加が容易
- ある Component はデコードを、ある Component はエンコードを、別の Component はカメラ画像を取得…など
- API を利用して Componenet を操作するコードは IL Client と呼ばれる
- Component はComponent 同士を直接つなぐことも可能
- IL Client がデータを操作する必要がなくなる
- Component の処理は GPU のメモリ空間で行われる場合が多いため、GPU のメモリ空間で別 Component に直接データを渡すことで、GPU のメモリ空間と CPU のメモリ空間を行き来するコストを削減できる
- ハードウェアと連携しやすい API 設計になっている
- その挙動が地中(=GPU空間)に潜っているように見えることから2、トンネルと表現されることがある(multimedia tunneling、communication tunnel)
- IL Client がデータを操作する必要がなくなる
OpenMAX IL の API について。API は core API と component API の 2 種類がある。
- core API は Component をロードしたり破棄したりする API
- component API は Component に対して操作する API
- Component の状態を操作する
- LOADED, IDLE, EXECUTING の 3 つの状態が主軸
- 状態を変えるには、次の状態を StateSet した後、次の状態の条件を満たす操作をすることで状態が変更される
- LOAD -> IDLE の場合、StateSet(IDLE) した後、バッファを必要数確保すると、IDLE に遷移する
- OpenMAX IL 1.1.2 Specification 3.4 Calling Sequences にシーケンス図と共に例が記載されている
- Component に対するデータの送信(EmptyThisBuffer関数)や受信(FillThisBuffer関数)
- Empty は、用意したバッファを Component が消費するというイメージ3
- その他オプション設定など
- Component の状態を操作する
Bellagio OpenMAX IL とは
- OpenMAX IL インターフェースを実装した OpenMAX IL Core
- Linux で動作する
- オープンソースで、Ubuntu や Debian であれば apt から入手できる
- 利用可能なコンポーネントは多くない。主にカメラ等の入出力デバイスと、vorbis等の数個のデコーダのみ
- ソースからビルド4すれば ffmpeg 実装のビデオデコーダが手に入る。ただし
With some rework to do (bad)
の扱いになっている
実装
- Bellagio OpenMAX IL を利用して、 OpenMAX IL のコードを書いてみます
- OpenMAX IL のインターフェースは C 言語ですが、C++17 から取り扱うことにします
- 以降記載するコードは、ソースコード量を短縮させるため、エラーを雑に扱っています
環境構築
-
libomxil-bellagio-dev
をインストールして Bellagio OpenMAX IL のライブラリと OpenMAX IL のヘッダを取得 - g++ ビルド環境に
build-essential
とpkg-config
をインストール
apt update
apt install build-essential pkg-config libomxil-bellagio-dev
core を初期化するだけ
コードは以下の通り。
#include <iostream>
#include <OMX_Core.h>
int main() {
OMX_ERRORTYPE res = OMX_Init();
if (res != OMX_ErrorNone) {
std::cerr << "OMX_Init res=" << std::endl;
abort();
}
std::cout << "OMX_Init: ok" << std::endl;
OMX_ERRORTYPE res = OMX_Deinit();
if (res != OMX_ErrorNone) {
std::cerr << "OMX_Deinit res=" << std::endl;
abort();
}
std::cout << "OMX_Deinit: ok" << std::endl;
return 0;
}
gcc を使って上記コードをビルドします。ライブラリの依存の引数は pkg-config
に任せておきます。
g++ -std=c++17 -o app1 $(pkg-config --cflags libomxil-bellagio) app1.cpp $(pkg-config --libs libomxil-bellagio)
OMX_Init: ok
OMX_Deinit: ok
Component の一覧取得
Component の一覧と、Component ごとの role を取得してみます。
1 つの Component が複数の機能を持っていることがあり、role で区別されます。
よくあるのは、DRM (Digital Rights Management) に対応したものとそうでないものが用意されているケースで、OMX.Xxx.AVC.Decoder
と OMX.Xxx.AVC.Decoder.secure
が取得されがちです。
以下にコードを示します。role 名を格納する型と Component 名を格納する型が異なる点を除いては、特に変わっている点はないです。
#include <array>
#include <iostream>
#include <vector>
#include <OMX_Component.h>
#include <OMX_Core.h>
#define CHECK_OMX(expr) \
{ \
OMX_ERRORTYPE __res; \
if ((__res = (expr)) != OMX_ErrorNone) { \
std::cerr << "LINE" << __LINE__ << ": failed `" #expr "` errortype=" \
<< static_cast<int>(__res) << std::endl; \
abort(); \
} \
}
std::string
convertRoleToString(const std::array<OMX_U8, OMX_MAX_STRINGNAME_SIZE> &role) {
return std::string(reinterpret_cast<const char *>(role.data()));
}
std::vector<std::array<OMX_U8, OMX_MAX_STRINGNAME_SIZE>>
getRolesOfComponent(const std::string &component_name) {
constexpr OMX_U32 kMaxNumRoles = 4;
// TODO: USE OMX_UUIDTYPE
std::vector<std::array<OMX_U8, OMX_MAX_STRINGNAME_SIZE>> role_names_buf(
kMaxNumRoles);
std::vector<OMX_U8 *> role_names_buf_ptr(kMaxNumRoles);
OMX_U32 num_roles = kMaxNumRoles;
for (size_t i = 0; i < (size_t)num_roles; ++i)
role_names_buf_ptr[i] = role_names_buf[i].data();
CHECK_OMX(OMX_GetRolesOfComponent(const_cast<char *>(component_name.data()),
&num_roles, role_names_buf_ptr.data()));
role_names_buf.resize(num_roles);
return role_names_buf;
}
std::vector<std::string> getAllComponentNames() {
std::vector<std::string> component_names;
for (OMX_U32 index = 0;; ++index) {
char component_name[OMX_MAX_STRINGNAME_SIZE] = {};
OMX_ERRORTYPE result = OMX_ComponentNameEnum(
component_name, OMX_MAX_STRINGNAME_SIZE - 1, index);
if (result == OMX_ErrorNoMore)
break;
CHECK_OMX(result);
component_names.emplace_back(component_name);
}
return component_names;
}
int main() {
CHECK_OMX(OMX_Init());
for (const auto &component_name : getAllComponentNames()) {
std::cout << "component name: " << component_name << "\n";
for (const auto &role_name : getRolesOfComponent(component_name)) {
std::cout << "- role: " << convertRoleToString(role_name) << "\n";
}
std::cout << std::endl;
}
CHECK_OMX(OMX_Deinit());
return 0;
}
実行してみると、Component が 1 つも取得できていないと思います。
libomxil-bellagio-dev
には Component は入っておらず、別のパッケージに分かれているので、別途インストールします。改めて実行すると、インストールしたものが表示されるはず。
$ g++ -std=c++17 -o app2 $(pkg-config --cflags libomxil-bellagio) app2.cpp $(pkg-config --libs libomxil-bellagio)
$ ./app2
$ apt search libomxil-bellagio0-components
Sorting... Done
Full Text Search... Done
libomxil-bellagio0-components-alsa/bionic 0.1-2 amd64
ALSA source/sink components for Bellagio OpenMAX IL
libomxil-bellagio0-components-base/bionic 0.9.3-4 amd64
components for Bellagio OpenMAX IL
...
$ apt install libomxil-bellagio0-components-fbdevsink
...
$ ./app2
component name: OMX.st.fbdev.fbdev_sink
- role: fbdev.fbdev_sink
component name: OMX.st.fbdev.fbdev_sink
- role: fbdev.fbdev_sink
Component の作成に進みたいところですが、記事が長くなってしまうのでここまで。
おわり
株式会社ACCESS Advent Calendar 2022 2日目の記事でした。
業務で OpenMAX IL を触れたので、最も手に取りやすい Bellagio を題材にして OpenMAX IL の記事を書きました。
OpenMAX IL に興味を持って触ってみようと思った方は、まずは先に Android の MediaCodec に触れてみることをオススメします。インターフェースはよく似ているし、インターネットにより多くの情報があります。
-
実際の実装はこの辺り https://android.googlesource.com/platform/frameworks/av/+/a8a1b31a1d53ce0e2e854ceb3a154c1991495dc4/media/libstagefright/ACodec.cpp ↩
-
たぶん ↩
-
だと思う ↩
-
2010 年より前のコードのようです。Ubuntu 10.04 より新しい環境では動作保証されていない。 ↩