AndroidのMediaPlayerだとフレーム単位で制御するAPIがなく早送りや任意にフレームスキップさせることは困難である。
しかし、MediaCodecやMediaExtractorを使えばフレーム単位での制御が可能である。
ここでは自分で調べた結果をまとめておく。
動画再生に関しては素人なので間違ったことを書いてるかも。
実装サンプル
mafshin/MediaCodecDemo: Android MediaCodec API Demo with Seek and MediaController
実装としては上記のコードがわかりやすい。
MediaExtractorで動画ファイルから動画データを読み(音声データは無視)、MediaCodecで動画データをデコードしてSurfaceに対して描画している。
ちなみに、MediaCodecのgetInputBuffers()あたりはdeprecatedなので以下のように置き換える。
// 変更前
ByteBuffer[] inputBuffers = decoder.getInputBuffers();
...
ByteBuffer buffer = inputBuffers[inIndex];
// 変更後
ByteBuffer buffer = decoder.getInputBuffer(inIndex);
フレームスキップ
- java - Skip frames in video playback with MediaExtractor and MediaCodec - Stack Overflow
- android - Problems with MediaExtractor - Stack Overflow
動画のキーフレーム以外のフレームではキーフレームからの差分のデータしか持っていないため、MediaCodecにはキーフレームからのデータを与え続ける必要がある。
上記リンク先の質問のようにMediaExtractorでキーフレームを無視してキーフレーム以外の動画データをMediaCodecに渡してもデコードは上手くいかない。
MediaExtractorのseekTo()ではキーフレームにしか移動できないがここらへんが関係していそう。
基本的にはMediaCodecにはキーフレームからのデータを与え続けてエンコードさせreleaseOutputBuffer()でrenderフラグをfalseにして描画をしないことでフレームスキップする。
早送り/スローモーション再生
上記のサンプルに対して以下のようにすることで早送りやスローモーション再生ができたりする。
が、上記のフレームスキップやサンプルのようにsleepで待つ方が良い?
- 2倍速早送り再生
decoder.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime() / 2, 0);
- 2倍速スローモーション再生
decoder.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime() * 2, 0);
動画と音声の同期
提示されているコードはよくわかってないけど、音声再生ベースにして動画再生をフレームスキップや待ったりして同期かければ良いのかなという感じ。