• スポンサードリンク

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

Android Camera MediaCodec 非同期

と言う事でコード

まずはエンコード処理の共通の抽象クラスMediaEncoderから行きましょう(^o^)/

MediaEncoder

どっか〜ん\(^o^)/いきなり長い・・・その代わりaudio用・video用のsubclassは少し短い・・・はずです。コメントを取れば1/3ぐらいのはずだから内容的にはそう長くもないですけど。
この中で、sourceスレッドの核となるのが#encode、drainスレッドの核となるのが#drain、drainスレッドを操作するためのHandlerがEncoderHandlerになります。#encodeはMediaCodecへbyte配列でデータを入力するためのメソッドですが、今回のVideo入力に関してはSurface経由で行うのでaudioでしか使いません。
MediaCodecの初期化とMediaCodecへの入力についてはaudioとvideoで処理が結構違うので抽象クラスには含めずサブクラスで定義を行います。逆に言うとここで定義してしまっているdrainスレッド・drain処理についてはサブクラスでは殆ど気にする必要がありません。初期化メソッドは後々の事を考えてabstractとして宣言だけはしておきます。

この中で一番キーとなるのは、#darin内のMediaCodec.INFO_OUTPUT_FORMAT_CHANGEDが来た時の処理です。
MediaCodecについてはcreate > configure > start > [データ書き込み] > 終了指示(EOSセット) > stop > releaseと順に実行すればいいだけですが、MediaMuxerについてはそう簡単ではありません。
MediaMuxerは、 create > addTrack(MediaCodecからoutputFormat取得してセット&トラック番号を取得) > start > [write] > stop > releaseの順に実行しなければなりません。
まず初めに問題となるのは、「MediaCodecからoutputFormat取得」です。MediaCodec#getOutputFormatを呼び出すだけやろって思ったあなた、甘いです。

  1. #getOutputFormatはMediaCodec.INFO_OUTPUT_FORMAT_CHANGEDが来るまでは呼び出すことが出来ない
    MediaCodec.INFO_OUTPUT_FORMAT_CHANGEDは一番最初のエンコードが完了するまでは来ないので、MediaMuxer#startを呼び出す前にMediaCodecへデータの入力行ってそのデータのエンコードが完了して初めて#getOutputFormatを呼び出すことが出来るのです。ややこしい(´・ω・`)。
    MediaCodec.INFO_OUTPUT_FORMAT_CHANGEDが来る前に#getOutputFormatを呼び出すとクラッシュします。
    ちなみに、コメントに少し書いてありますが、Andoird4.3未満ではMediaCodec.INFO_OUTPUT_FORMAT_CHANGEDは決して来ません。つまり、#getOutputFormatのAPIレファレンスにはAPI16以上と書いてありますが、Andoird4.3(API18)未満では定義は有るものの決して呼び出してはいけないメソッドなのです(クラッシュします)。じゃぁどうすんねんと言うと、今回のサンプルはAPI>=18なので省略していますが、BufferInfo#flagにMediaCodec.BUFFER_FLAG_CODEC_CONFIGがセットされた時に、自前でOutputFormatを準備してMediaMuxerへ引き渡す必要があります。
  2. 一旦MediaMuxer#startを呼び出してしまうとMediaMuxer#addTrackを呼べなくなる
    audioまたはvideoのいずれか一方だけであれば、自分さえ準備が終わって#addTrackを呼び出せば直ぐに#startを呼び出すことが出来ます。でも、複数のトラック(audioとvideo)を書き込むには、両方で#addTrackを呼び出して初めて#startを呼び出すことが出来るのです。audioとvideoのどちらが先に準備完了しても大丈夫なように同期・同期待ちが必要ってことです。
    ちなみに、#startを呼び出した後に(別スレッドから)#addTrackを呼び出すと例外生成します。

他にも、presentationTimeUsの調整とか、audioだとMediaCodec#signalEndOfInputStreamが使えね〜とか、何でわざわざHandlerにしてるかなど、色々ハマりどころが有りますけど、コメントを一生懸命一杯書いたので頑張って解読して下さい。三〜四ヶ月程前には色々苦労したなぁ〜( ´Д`)=3

と言う事で今回はおしまいです。
お疲れ様でした。

このサンプルプロジェクトはGitHubで公開しています。Apache2 licenseなので自由に使って下さい。
リンクはこちら:GitHub:AudioVideoRecordingSample

« »

  • スポンサードリンク

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