• スポンサードリンク

MediaCodecInfo#getCapabilitiesForTypeが激遅になる件

Android MediaCodec

事の起こりは…

USB Webカメラアプリでコマ落ちや画面の乱れを防ごうと色々最適化していたら、ふと気が付くとGalaxy S3(SC-06D, Android4.1.2)だけ動画を保存できなくなってしまった。画面表示は問題ないし、動画もNexus7(2012, Android4.4.4)とNexus5(Android4.4.4)は特に問題なし。

色々調べてみた

でも、Galaxy S3はroot取ってないのでネットワーク越しではデバッグ出来ないのです。USBでPCと繋ぐとUSBカメラを繋げなくなって意味無いし。Nexus7/5はroot無くてもネットワーク越しでデバッグできて快適\(^o^)/
しょうが無いので、最終的には処理毎にLogCatへ出力して後でPCに繋いでログを眺めるなんて事をしていたらようやく気が付きました。
MediaCodecInfo#getCapabilitiesForTypeが返ってこうへん(●`ε´●)

えらいこっちゃ

もう少し正確には、「プレビューの描画をしているスレッドが終了するまで返ってこない」でした。
プレビュー描画の処理も動画のエンコードも保存も処理が重いのでマルチスレッドで処理を走らせています。なので、なかなか気が付きませんでした。
まぁ、以前からGalaxy S3だけ録画開始時のタイムラグが数秒あって気にはなってたんだけど、とうとう録画できなくなってる・゚・(つД`)・゚・
とは言うものの、MediaCodecInfo#getCapabilitiesForTypeを呼んでカラーフォーマットを確認しないことにはMediaCodecを正しく初期化出来ません。

想像してみた

でも、AndroidのAPIが返ってこない原因なんて・・・しかも特定の機種だけだめな上に、以前はなんとか動いてたなんて状況はグルグル先生も知りませんでした。しょうが無いので想像です。

  1. Galaxy S3だけ、プレビュー描画スレッドが終了するまではMediaCodecInfo#getCapabilitiesForTypeが返ってこない
  2. 以前は数秒掛かるけど返って来ていた
  3. プレビュー描画はGPU(フラグメントシェーダ)を多用してる
    (GitHubで公開しているライブラリ/サンプルはCPUでピクセルフォーマット変換/描画をしているけど、フレームレートが上がらないのと電池の消耗が速いのでGoogle Playで公開しているアプリではGPUを使ってピクセルフォーマット変換/描画をしています)
  4. プログラムの最適化のお陰でプレビュー描画のフレームレートが上がってる
  5. MediaCodecではハードウエアデコード/エンコードもサポートしている

なんて状況から想像すると、GPUのドライバ次第でしょうが、どうやらプレビュー描画でGPUを一杯働かせているとMediaCodecInfo#getCapabilitiesForTypeを呼んでも返事をしてもらえないのではないかと思いました。
う〜ん。

どないすんねん

GPUを一杯働かせているとはいえ、それでもせいぜい30fps程度、GPUへ描画している時間が1フレーム当たり数ミリ秒なのに対し、nativeコードを走っている時間は数十ミリ秒で、GPUが限界まで働いている気はしません。
もしかすると、Android4.1.2とそれ以降とでOSのスケジューリングアルゴリズムが変わったのか、nativeで生成した時とJavaで生成した時のスレッドの優先度のデフォルトがOSで違うのかもしれません(調べてないけど)。
と言う事で、とりあえずMediaCodecInfo#getCapabilitiesForTypeを呼び出すときだけスレッドの優先度を一時的に高くしてみました。

Java(と言うよりAndroid)でスレッドの優先度を変えるには2通りの方法が有るみたいです。

  1. Thread#setPriorityを使う方法。
    設定できるのは、Thread.NORM_PRIORITY, Thread.MAX_PRIORITY, Thread.MIN_PRIORITYの3種類。名前からしてThread.NORM_PRIORITYがデフォルト値ですよね
  2. android.os.Process#setThreadPriorityを使う方法。
    設定できるのは-19〜19までの整数で、Process.THREAD_PRIORITY_XXXとして定数が幾つか定義されています。

どっちを使うのがいいのかはわかりませんが、APIのレファレンスに、Thread.MAX_PRIORITYはProcess.THREAD_PRIORITY_URGENT_DISPLAYに相当すると書いてあったので、Thread#setPriorityを使うことにしました。わざわざThread.MAX_PRIORITYとして定義してあるってことは、原則としてこれを最大値として使えっていうOS/API設計者の意思表示ですよね?

結果

やった〜動きました\(^o^)/。まだ、Galaxy S3だけMediaCodecInfo#getCapabilitiesForTypeの呼び出しに時間がかかって、録画開始時に秒単位で遅延するのが気になりますが、全然動かないよりましです。

おまけ

MediaCodecで使用するコーデックとカラーフォマットを選択するメソッドを2つ載せます。
1つ目は、MediaCodecInfoのAPIレファレンスに書いてあるコードにカラーフォーマットの制限を付け加えたものです。

1つ目のは、MediaCodecInfo#getSupportedTypesを使ってMIMEが一致するものを選んだ後、更にカラーフォーマットを選択するために、MIMEを引数にしてMediaCodecInfo#getCapabilitiesForTypeを呼び出しています。
それならば最初からMediaCodecInfo#getCapabilitiesForTypeを呼び出してしまえばええんやないのってことで考えたのが2つ目。

自分としては同じ結果が返ってくると期待していたんですが…
1つ目の方法:

  • Nexus7(2012)では
    OMX.Nvidia.h264.encoder
    profile:AVCProfileBaseline,level=AVCLevel41
  • Nexus5では、
    OMX.qcom.video.encoder.avc
    profile:AVCProfileBaseline,level=AVCLevel51
    profile:AVCProfileMain,level=AVCLevel51
    profile:AVCProfileHigh,level=AVCLevel51
  • Galaxy S3では、
    OMX.qcom.video.encoder.avc
    profile:AVCProfileBaseline,level=AVCLevel4
    profile:AVCProfileMain,level=AVCLevel4
    profile:AVCProfileHigh,level=AVCLevel4

が選択されました。

一方で2つ目の方法だと、Logに「Failed to set standard component role ‘video_encoder.avc’.」とか色々エラーが表示されていいて、

  • Nexus7(2012)では、
    OMX.Nvidia.mp4.encoder
    profile:AVCProfileBaseline,level=AVCLevel22
  • Nexus5では、
    OMX.qcom.video.encoder.mpeg4
    profile:AVCProfileBaseline,level=AVCLevel22
    profile:32768,level=AVCLevel22
  • Galaxy S3では、
    OMX.qcom.mp4.encoder
    profile:AVCProfileBaseline,level=AVCLevel4
    profile:AVCProfileMain,level=AVCLevel4
    profile:AVCProfileHigh,level=AVCLevel4

という結果になりました。
いや、だからどうだということは無いんですけど。2つ目の方法だと互換エンコーダーみたいなのが選択されちゃっている状態なのかな?
どっちでも動くし、出来た動画も自分では区別付かないのでいいんですけど、2つ目の方法だとNexus5/7でAVCのレベルが低く返って来ているのが気になります。

まとめ

売上を高くしようとして色々特徴をもたせようってのはわかるのですが、役に立たない訳わからないところで特徴をもたせようというのはやめて欲しいです。世の中には数千種類ぐらいの機種バリエーションがあるみたいだし、全部確認するなんて不可能。今回のはレアケースかもしれないけど、でも実機を持って無ければ確認しようもないし治しようも無かったんだよ〜(T_T)
Galaxy Note2でプレビューが数秒で止まってしまうって話も聞いたし…だれか下さい(笑)m(_ _)m

と言う事で、お疲れ様でした〜

« »

  • スポンサードリンク