Orange PI PCで動画のハードウェアエンコードはできるのか

Pocket

これはLinux Advent Calendar 2015 18日目の記事です。

ARMベースの小型PCとしてRaspberry PIシリーズが人気です。ハードウェアによる動画(H.264)エンコード支援がついていて、それが目的で使っている人もいるようです。

しかし私はRaspberry PI 2の代わりにOrange PI PCを購入しました。機能的には持っているようなので、できればなんとか使いたいところです。

参考にするため、そもそもRasPiではどうやっているのかをちょっと調べてみました。Googleで検索すると、gstreamerでomxh264encを使うという方法が紹介されています

これはgst-omxというコンポーネントのようで、OpenMaxというコーデックの取り扱いを抽象化したAPIのようです。さらに調べてみると、RasPiではlibopenmaxil.soという共有ライブラリが呼び出されているようです。

ではこいつのソースコードはどこだと探してみましたが、ファームウェアの一部という扱いでバイナリーのみが配布されているという残念な状況でした。RasPi側からの調査はここが限界です。

ではOrange PIではどうか。Orange PIとAllwinnerのチップの情報に関しては、linux-sunxi.orgが総本山のようです。ここにいろいろと情報がありました。

Orange PI PC用カーネルにはcedar-veというモジュールが追加されており、これがハードウェアコーデック用のデバイスドライバとなっています。/dev/cedar_devというスペシャルデバイスを介してioctlで直接ハードウェアを操作する機能を提供しています。残念ながらメインラインにはマージされていないようです。

そしてそれを使うよう改造されたffmpegが公開されています。これを動かすことを目標としていろいろ試してみました。リポジトリにはdeb化されたバイナリーもありますが、どうもwheezy向けのようです。しかもパッケージングの作法もよろしくなく、既存のdebianパッケージとファイル単位でいろいろ衝突を起こすので、これを利用するのはお勧めしません。

そこで手動でのビルドを行いましたが、ライブラリ周りで苦労しました。最終的には以下のような手順でビルドできると思います。

# apt-get build-dep ffmpeg
# apt-get install frei0r-plugins-dev libgnutls28-dev ladspa-sdk \
libiec61883-dev libavc1394-dev libass-dev libbluray-dev libbs2b-dev  \
libcaca-dev flite1-dev libgme-dev libgsm1-dev libmodplug-dev \
libmp3lame-dev libopencore-amrnb-dev libopencore-amrwb-dev \
libopenjpeg-dev libopus-dev librtmp-dev libshine-dev libssh-dev \
libspeex-dev libtheora-dev libtwolame-dev libvo-aacenc-dev libvo-amrwbenc-dev \
libvorbis-dev libvpx-dev libwavpack-dev libwebp-dev libx265-dev \
libxvidcore-dev libzvbi-dev libopenal-dev
# ./configure --prefix=/opt/ffmpeg \
        --build-suffix="-ffmpeg" \
        --toolchain=hardened \
        --enable-gpl \
        --enable-shared \
        --disable-stripping \
        --disable-decoder=libopenjpeg \
        --disable-decoder=libschroedinger \
        --enable-avresample \
        --enable-avisynth \
        --enable-gnutls \
        --enable-ladspa \
        --enable-libass \
        --enable-libbluray \
        --enable-libbs2b \
        --enable-libcaca \
        --disable-libcdio \
        --enable-libflite \
        --enable-libfontconfig \
        --enable-libfreetype \
        --enable-libfribidi \
        --enable-libgme \
        --enable-libgsm \
        --enable-libmodplug \
        --enable-libmp3lame \
        --enable-libopenjpeg \
        --enable-openal \
        --enable-libopus \
        --enable-libpulse \
        --enable-librtmp \
        --enable-libschroedinger \
        --enable-libshine \
        --enable-libspeex \
        --enable-libssh \
        --enable-libtheora \
        --enable-libtwolame \
        --enable-libvorbis \
        --enable-libvpx \
        --enable-libwavpack \
        --enable-libwebp \
        --enable-libx265 \
        --enable-libxvid \
        --enable-libzvbi \
        --disable-opengl \
        --enable-x11grab \
        --enable-version3 \
        --enable-libopencore_amrnb \
        --enable-libopencore_amrwb \
        --enable-libvo_aacenc \
        --enable-libiec61883 \
        --enable-libzmq \
        --enable-frei0r \
        --enable-libx264 \
        --enable-libopencv \
        --enable-libvo_amrwbenc
# make

ビルドしたバイナリを実行してみます。/dev/cedar_devへのrwアクセスができる状態でffmpegを実行します。

$ ffmpeg -i inputfile.ts -vcodec cedrus264 -pix_fmt nv12 output.mp4
ffmpeg version git-2015-01-22-f86a076 Copyright (c) 2000-2014 the FFmpeg developers
  built on Dec  7 2015 14:04:23 with gcc 4.9.2 (Debian 4.9.2-10)
(中略)
[VE SUNXI] VE version 0x0000 opened.
[cedrus264 @ 0x1f42f0] Cannot allocate frame.
Output #0, mp4, to 'output.mp4':
    Stream #0:0: Video: h264, q=2-31, 128 kb/s, SAR 1:1 DAR 0:0, 29.97 fps
    Metadata:
      encoder         : Lavc56.0.101 cedrus264
    Stream #0:1: Audio: aac, 0 channels, 128 kb/s
    Metadata:
      encoder         : Lavc56.0.101 libvo_aacenc
Stream mapping:
  Stream #0:0 -> #0:0 (mpeg2video (native) -> h264 (cedrus264))
  Stream #0:1 -> #0:1 (aac (native) -> aac (libvo_aacenc))
Error while opening encoder for output stream #0:0 - maybe incorrect parameters such as bit_rate, rate, width or height

残念ながらうまく動きませんでした。gdbで追いかけてみると、ve_open()で/dev/cedar_devをオープンしてioctl IOCTL_GET_ENV_INFOで構造体ve_infoの値を取得していますが、これ自体は失敗しないものの、各種メンバの値reserved_mem, reserved_mem_size, registersがすべて0になっています。これによりve_mallocが失敗して6行目のエラーメッセージが表示されます。ドライバ側のコードを読むと、「このインターフェースは使うな」というコメントがあり、実際に0を返していました。きっと古いcedar_devで動くコードだったのでしょう…

http://linux-sunxi.org/CedarX/Encoderの記述によると、現在あるコード以外にもsunxi_memドライバという物理メモリにアクセスするドライバ(/dev/sunxi_mem)と併用して動くCedarXというライブラリが別にあるようです。ハードウェアエンコーダのサンプルコードもあるのですが、やはりバイナリでしか存在しないライブラリ群に依存しています。

それでもいいからなんとか動かせないかと、sunxi_memドライバを追加した勝手カーネルをビルドしようとしたのですが、mach/includes.hという存在しないヘッダファイルを必要としてビルドできませんでした。ここはまだ調査不足です。

現状の結論として、Orange PI PCでH.264のハードウェアエンコードは「できそうな雰囲気」という煮え切れない結論となってしまいました。linux-sunxi MLの購読を始めたので、今後はこのあたりどうなっているかを尋ねてみたいと思っています。