Android/iOS同時対応動画をエンコードする心得


## AndroidとiOS両方に対応するならmp4を使う

動画を撮るとき、Androidは.mp4、iOSは.mov (QuickTime）形式で保存されます。
mp4はmovは、様々な形式（コーデック）の動画情報や音声情報を含むことができる「コンテナフォーマット」で、中身の動画や音声の情報の保存方法は別途決められています。AndroidやiOSで動画を撮影した場合、動画コーデックは **H.264（別名MPEG-4 AVC）** 、音声コーデックはAACで保存されています。

.movはそのままの形式ではAndroidでは再生できませんが、コンテナなので、動画や音声の情報をデコード・エンコードせずにデータをコピーするだけで.mp4に変換できます。iPhoneは.mp4も再生できるので、 **.mp4に変換するとAndroid/iOSの両方に対応** できます。
（.mp4は.movをベースに作られています）

## H.264のProfileとLevel

H.264（MPEG-4 AVC）では、エンコードおよび再生時に機器に求められる性能（すなわちCPUやメモリ量）、圧縮率、画質といったパラメータが **Profile** と **Level** によって決められていて、用途に応じて変更することができます。機器やデコーダによってサポートされているProfile, Levelが異なるため、再生するデバイスに応じて適切なものを選択すべきです。

- Profile: H.264でサポートされる動画の圧縮方法のうち、どれを使用できるかを定義します。
  Profileがより高いものになると圧縮率や画質が改善しますが、エンコーダとデコーダの実装や処理が複雑になります。
  主なものは、簡単な順から **Baseline Profile**, **Main Profile**, **High Profile**です。
- Level: 解像度やビットレートなど、パラメータの最大値を定義します。
  Levelがより高いものになると、画質が向上しますが、データ量と求められる機器性能はより高くなります。
  同じLevelでもProfileによってビットレートなどが異なったりします。

スマートフォンやPCにおいても、ハードウェア上のエンコーダ、デコーダを使用するようで、
ソフトウェアが該当するProfile, Levelをサポートしていたとしてもハードウェアで対応されていない場合は、うまく再生できなかったりします。

### Androidが対応するProfile/Level

Androidでは公式には（エンコーダ、デコーダのどちらなのか言及なしに）「Baseline Profileがサポートされている」としていますが、Levelについては特に言及がありません。

http://developer.android.com/guide/appendix/media-formats.html

少なくともNexus 5 (Android 4.4)ではMain Profile @ Level 4.0の（解像度を下げた）動画を再生することが出来ました。
また、Nexus 5で動画を撮影すると、Baseline Profile @ Level 4.0になりました。

Xperia acroについて調査した記事が見つかり、Baseline, Main, High @ 3.1は再生できるようでした。

http://d.hatena.ne.jp/shiku_otomiya/20110729/p1

前述のとおり、実際に対応可能なProfile/Levelは端末の性能に依存しているのでしょう。しかし少なくとも、Baseline Profileはサポートされるようです。

### iOSが対応するProfile/Level

iOSはiPhone、iPod Touch、iPad上でしか動作しないので、それぞれの端末のスペックを見ることで、対応するフォーマットを確認することができます。

https://www.apple.com/jp/iphone-5s/specs/

また間接的な資料ですが、"HTTP Live Streaming"のドキュメントに、
iPhone 3G、3GS ~~は~~ からBaseline Profile @ Level 3.1 ~~のみ~~ をサポートしていたとの記載があります。
それより古い端末の互換性を考えるならBaseline @ 3.0にすべき、また、iPhone 4以上のみ考えるならMain @ 3.1にしておいたほうがよいとの記述もあります。

https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/FrequentlyAskedQuestions/FrequentlyAskedQuestions.html

ちなみにiPhone 4S (iOS 7.1)ではBaseline @ 4.1になりました。

## オレが考えたAndroid/iOS両対応の最強Profile/Level

Androidは公式にはBaselineしかサポートしていないことを考えると、画質を優先する場合の選択肢は下記のとおりになると思います。

- iPhone 3GSみたいな古い端末をサポートする: Baseline Profile @ Level 3.0
  - ffmpegに`-profile:v baseline -level:v 3.0`を指定
- iPhone 4以降に絞ってしてしまう: Baseline Profile @ Level 3.1
  - ffmpegに`-profile:v baseline -level:v 3.1`を指定

paperclipの場合はgeometryと一緒に下記のような形で指定してください。

```ruby:
convert_options: { output: { :'profile:v' => 'baseline', :'level:v' => '3.1' } }
```

[paperclipでffmpegに直接オプションを渡す方法](https://github.com/owahab/paperclip-ffmpeg#ffmpeg-options)

streamingする場合など、データ量を減らしたい場合は、より低いLevelを指定することも可能です。

~~※streamingする場合は`streaming: true`もお忘れなく・・！~~ → コメント参照
※音声コーデックについてはガン無視しました、すみません・・。

### 追記：AndroidのドキュメントにMain Profileの記述が増えた

https://developer.android.com/guide/topics/media/media-formats.html
いつのまにかBaseline ProfileとMain Profileが別々に書かれていて、Main Profileのdecoderは必須とされています。
しかしながら、CDD （Compatibility Definition Document)に追加されたのがAndroid M (6.0)からだからアテにしないほうが良いとの記載もあります。
https://github.com/google/ExoPlayer/issues/1378#issuecomment-197447909