• スポンサードリンク

音&動画の同時キャプチャがした〜い(その1)

Android Camera MediaCodec 非同期

本当は、MediaCodec+MediaMuxerを使って音&動画の同時キャプチャがした〜い。

前置き

内蔵カメラ・内蔵マイクからの録音録画であれば、あえてMediaCodec+MediaMuxerを使う必要はあまりありません。
MediaRecorderってのを使えば簡単に出来ます・・・たぶん。ステートマシンが複雑だったり、公式ドキュメントに載ってない設定をしないと駄目だったりと色々苦労しますけど。
MediaRecorderの自分にとっての一番の問題は、公式には内蔵カメラ以外のビデオソースに対応してない事かな。だってUSB接続のカメラから録画したいじゃん・・・AOSPと一緒にビルドするとか裏技は有るけど・・・

ちなみに、WebのAndroid DevelopersのAPIレファレンスでMediaRecorder.VideoSourceを表示させると、ビデオソースとしては、

  • CAMERA
  • DEFAULT

の2つしか載っていません。DEFAULTとCAMERAの違いって何ねんな。でもEclipseからSDKのAPIレファレンスのMediaRecorder.VideoSourceを表示させると、ビデオソースとして

  • CAMERA
  • DEFAULT
  • SURFACE

の3つがしらっと表示されます。・・・えっSURFACE?hideになってたんじゃないん?いっいつの間に・・・知ってた?
ちなみにSDKのAPIレファレンスの説明文はこれ。対応APIレベルの欄は空欄です。SDKは23っす。前から載ってたかどうかは記憶にございません。

public static final int SURFACE

  • Surface video source
    Using a Surface as video source.
    This flag must be used when recording from an android.hardware.camera2.CameraDevice source.
    When using this video source type, use getSurface() to retrieve the surface created by MediaRecorder.
    Constant Value: 2 (0x00000002)

ん?そもそもMediaRecorder#getSurfaceなんて有ったっけ?APIレファレンスにはWebのにもSDKのにも載ってない。
SDKのソースも覗いてみた。

やっぱりhideじゃん。しかも#getSurfaceなんて無いし。ぬか喜びでしたね。これはもしかして予告なんかな?でもきっと間違えて載せとるんやろな。
と言う事でやっぱりMediaCodec+MediaMuxerで行きましょう(^o^)/
閑話休題

めでたく?MediaCodec+MediaMuxerで行くとなったわけですが、実際に実装しようとすると情報が余りありません。
録画だけってのはGrafika(GitHub:Grafikaへのリンクはこれ)も含めて色々とあります。MediaCodec+MediaMuxerでの録音ってのは数は少ないけど少しは見つかります(録音だけなら普通はMediaRecorderが定番だからでしょう)。でも、MediaCodec+MediaMuxerで録音・録画ってどないすんねんってグルグル先生に聞いても、同じ様などないしたらええんや〜って質問が出てくるだけで、実際にこないしたらええねんってのはよく判りませんでした。

と言う事で、まずは挙動確認のために、USBのカメラはほっといて内蔵カメラ・内蔵マイクからMediaCodec+MediaMuxerを使ってMPEG4で出力するサンプルを作ってみました。音声はAAC、動画はAVC(H.264)でエンコードすることにします。
前置きが長っ(^_^;)いや、実際のコードはもっともっと長いけどね。

全体の構成

ソースは数も沢山あるしそれぞれそれなりに長いので全体の構成を先に載せてしまいましょう。もっともいきなり構成を考えてからプログラムをしたわけでは無いですけど。

スレッド(MediaCodec/MediaMuxer周りのみ)

まずはスレッドです。これが一番大事です。
MediaCodecMediaMuxer1MediaCodec/MediaMuxerに関係するスレッドは、右の図の用になります。sourceスレッドとdrainスレッドは兼用できますが、効率よく処理するには別々に分けた方が良いと思います。なお、それぞれのTrackスレッドとWriteスレッドはMediaMuxerが内部で生成してくれるので自分で生成する必要はありません。ただ非同期で書き出しているんだってことは覚えとかないと駄目なのであえて図に加えています。
なお、実際にはMediaCodec内部もスレッドで動いているでしょうし、内蔵カメラ・内蔵マイクから録音録画出来るアプリとして動くようにするには、これ以外にもカメラ操作用のスレッドとかプレビュー描画用のスレッドとかも必要になります。

それぞれの役割は読んで字の如しですが、

  • sourceスレッド
    データの生成しMediaCodecへ入力するスレッド
  • drainスレッド
    MediaCodecがエンコードしたデータを取り出してMediaMuxerへ入力するスレッド
  • track/writerスレッド(MediaMuxerが内部で生成)
    drainスレッドが入力したデータをMPEG4の形式でファイルとして書き出すスレッド
クラス構成(MediaCodec/MediaMuxer周りのみ)

エンコード部分(スレッドでいうところのsourceとdrainを含むクラス)とMediaCodecのインスタンスは音声と映像用にそれぞれ別々に生成しなければなりません。なので普通は2つクラスを作るところなのですが、音声と映像の処理で似ているところが沢山あります。なので、共通の抽象クラスと(MediaEncoder)、音声用の子クラス(MediaAudioEncoder)、映像用の子クラス(MediaVideoEncoder)の3クラスに分割しました。
一方書き出し側については、APIレファレンスを見てもよく判りませんが、MediaMuxerのインスタンスは1つしか生成せず、音声と映像の両方から書き込むことになります。
MediaMuxerのインスタンスを音声用・映像用のエンコードクラスに直接渡すことも出来なくはないのですが、少し厄介なのがMediaMuxerのスタート方法・MediaMuxer&エンコーダーの終了方法なのです。このためMediaMuxerのラッパークラス(MediaMuxerWrapper)を1つ作成しました。この部分は後ほどソースと一緒に説明することにします。
ちなみに、MediaMuxerは単独で生成して、エンコーダークラスのコンストラクタへ引き渡し、抽象クラスでMediaMuxerWrapper相当の処理を実装することも出来ますが、MediaMuxerWrapperとしてクラスを1つ増やして旗振り役を任せた方がわかりやすのではないかと思います。
と言う事で次からはいよいよコードの嵐です。

« »

  • スポンサードリンク

%d人のブロガーが「いいね」をつけました。