Androidで動画編集アプリを作るならmedia-for-mobile

#media-for-mobile
https://github.com/INDExOS/media-for-mobile

INDExOSがOSSとして公開している動画編集系のフレームワーク
Apache License 2.0で利用できる
例えばOpenGLESのprogramをProgramクラスが担っていたり基本的に生のOpenGLESを利用せずにラップしているクラスを介して処理している。
まだ６コミットで今年の頭に登場した。
java製

----

#プラットフォーム
Supported Android versions: Jelly Bean 4.3 or higher

----

#導入
Gradleで入れる事は出来ないのでモジュールとして入れる。
機種依存系の修正は絶対何処かでする事になるのでコンパイルせずに入れるのが良かった。

----

#サンプル
/samples以下がそれにあたる

----

#出来る事
動画の連結
動画エフェクト追加
音声エフェクトの追加

----

#基本

```kotlin
mediaComposer = MediaComposer(factory, progressListener)
mediaComposer?.start()
```
MediaComposerを生成し、そこにメディアを追加して処理する。

引数のprogressListenerには
```
public void onMediaStart();
public void onMediaProgress(float progress);
public void onMediaDone();
public void onMediaPause();
public void onMediaStop();
public void onError(Exception exception);
```
が生えている。内部ではわりと浅い位置にtry/catchのブロックでエラーハンドリングが行われているので機種依存などで処理が終わらない時等はさっさとカスタムExceptionを返してonErrorで処理を分けると良い。

----

#機能

----

##ビデオフォーマット指定
```
mediaComposer.targetVideoFormat = videoFormat
```
で指定する。

```
val videoFormat = VideoFormatAndroid(transcodeConfig.videoMimeType, width, height)
videoFormat.videoBitRateInKBytes = transcodeConfig.videoBitRateInKBytes
videoFormat.videoFrameRate = transcodeConfig.videoFrameRate
videoFormat.videoIFrameInterval = transcodeConfig.videoIFrameInterval
```
フレームレートは指定しても特に変化はなかった、内部的にはこのパラメータをMediaCodecのsetIntegerしているだけなのでMediaCodecのフレームレート指定と同じ挙動をしているということだと思う。

----

##オーディオフォーマット指定
```
val aFormat = AudioFormatAndroid(transcodeConfig.audioMimeType, audioFormat?.audioSampleRateInHz ?: 44100, audioFormat?.audioChannelCount ?: 1)
aFormat.audioBitrateInBytes = transcodeConfig.audioBitRate
aFormat.audioProfile = MediaCodecInfo.CodecProfileLevel.AACObjectLC
mediaComposer.targetAudioFormat = aFormat
```
こちらもMediaCodecへの設定渡しをラップしてくれているクラスを利用する。

----

##連結

```
mediaComposer = MediaComposer(factory, progressListener)
mediaComposer?.addSourceFile(video1)
mediaComposer?.addSourceFile(video2)
mediaComposer?.start()
```

addSourceFileした順に動画が連結される。
指定位置に差し込む`insertSourceFile`もある

----

##トリミング

```
val trimRange = Pair(10000,200000)
val mediaFile = mediaComposer.sourceFiles[0]
mediaFile.addSegment(trimRange)
```

メディアは一度addSourceFileしたあと、sourceFilesから`MediaFile`クラスとして取り出せる。
そこに対して`addSegment`と、範囲情報を与えるとトリミング範囲を指定出来る。

----

##エフェクト
VideoEffectクラスを継承して実装する。
`setFragmentShader`が生えているので、そこに自分で書いたフラグメントシェーダを指定するとOpenGLES2のエフェクトがかけられる。
いちいちビルドするのは現実的じゃないので
http://www.kickjs.org/example/shader_editor/shader_editor.html#
等でシェーダを作ってからコピペするのが良い。
フラグメントシェーダはKotlinで書くとヒアドキュメントが使えて良い。
またエフェクトを重ねがけする場合は一度エンコードしてから再エンコードする。同時刻に２種類以上のエフェクトが存在する場合は後からかけられたエフェクトが優先される。

----

##プレビュー
プレビュー機能は提供されていない。
プレビューを使いたい場合はSurfaceTextureなどで自作するのが良い、エフェクトのプレビューはVideoEffectクラスから取れるシェーダーを適用させて毎フレームaddEffectSpecificを呼ぶとレンダリング結果と同じプレビューをレンダリング出来る。

----

##キャプチャ
ゲームキャプチャとカメラキャプチャ機能もあるが、サンプル止まりなので自分で作ったほうが良さそうだった。

----

#上手く動かなかったところ

----

##SamsungGalaxyS6ではレンダリング後の動画がノイズだらけになることがある。
トリミングをした時のみ発生する問題で、トリミング範囲を100ms程度ずらしてノイズの乗らない位置を探すと良い。
この問題が発生する時はMuxRenderクラスのwriteSampleDataメソッドでのsampleTimeが大きくずれたり張り付いたりするのでそれを検知すると上手くいく。
音声エフェクトをかける際もこの問題がおきやすかった。

----

##Xperiaの一部機種で処理が終わらない事がある
何らかが原因で処理が抜けてしまいパイプラインから迷子になるっぽい、タイムアウトを設けて過ぎてしまったら強制的に処理を終了させるなどの対策をすると良い。