デジカメで撮影したMotion JPEGな動画ファイルをPhotos.appに登録してもiOS端末に同期されない

はてなblogでのお初記事です。よろしくお願いします。

状況説明

別記事にも載せていますが、デジカメは RICOH CX5 を使っています。このデジカメで動画を撮影すると Motion JPEG 形式(拡張子は .avi)になります。macOS の写真.app(以下 Photos.app )に Motion JPEG を登録すれば再生可能なのですが、残念ながら iOS では Motion JPEG を再生出来ないようで、Photos.app に Motion JPEG を登録しても iOS に同期されません。さらに残念なことに、Photos.app は iOS 向け変換をしてくれないのです(してくれればいいのに)。ということで、このような動画ファイルを iOS 端末へ同期するようにするには、動画をPhotos.app に登録する前に iOS で再生可能な形式に変換しておく必要があります。つまり H.264 形式(拡張子は .mp4)にしておけば良いわけです。

変換にはやはりFFmpegでやるのですが

ただし、ごく単純に FFmpeg

ffmpeg -i RMOV0001.AVI RMOV0001.mp4

とやっても、生成されたファイル RMOV0001.mp4 は iOS 端末や MacQuickTime)で再生出来ません。YUV420 という形式にしておかないと再生出来ないのです。Motion JPEG は YUV422 なので、FFmpeg は何もオプション指定していないとそのまま YUV422 の H.264 形式の動画ファイルを作ってしまうんですね*1

ということで動画変換には

適切なオプションを付けておく必要があります。今回は YUV420 にするために -pix_fmt yuv420p を付けておきます。そして iOS 端末で撮影した動画ファイルの拡張子は .mov なので今回の変換後ファイルの拡張子も揃えておきましょう。

ffmpeg -i RMOV0002.AVI -pix_fmt yuv420p RMOV0002.mov

この変換後の動画ファイル RMOV0002.mov を Photos.app に登録すれば、問題無く iOS 端末へ同期できます。

時刻情報はどうする

さて。上で FFmpeg で動画形式を変換しましたが、撮影時刻情報が全く消えています。このままでは FFmpeg で変換した時刻が撮影時刻であると Photos.app が判断してしまいます。困りますよね。

① 変換後の動画ファイルの作成時刻を変換前の動画ファイルと同じにしちゃえ

touch コマンドで変換後の動画ファイル RMOV0003.mov を変換前の動画ファイル RMOV0003.AVI の作成時刻と同じにしてやってもOKなようです。

touch -r RMOV0003.AVI RMOV0003.mov

もちろんこの作業を行った後に Photos.app へ登録します。登録後に行っても意味ありません。

② 変換後の動画ファイルに時刻のメタ情報を書き込んじゃえ

私が最近お気に入りの exiftool を使うと、撮影時刻情報を変換後の動画ファイルに書き込むことができます。どうやら QuickTime:CreateDate というメタ情報が撮影時刻情報になるようです。変換前の動画ファイル RMOV0003.AVI の作成時刻、もしくは RMOV0003.AVI の撮影時刻のメタ情報を使って、QuickTime:CreateDate に書き込んでやれば OK です。変換前の動画ファイルの作成時刻のメタ情報は、私の使っている RICOH CX5 の場合だと RIFF:DateTimeOriginal に入っていました。

$ exiftool -s -G -RIFF:DateTimeOriginal RMOV0003.AVI
[RIFF]          DateTimeOriginal                : 2019:01:01 07:13:24:

この RIFF:DateTimeOriginal には時差情報が入っていません。一方、 Photos.app は QuickTime:CreateDate を基本的にUTCとして取り扱うようです。ですので、日本で撮影した場合には時差を考慮してやる必要があります。いちいち手作業でこんなことをやっていては間違うでしょう。ということでシェルスクリプトなどをつかってやります。私は bash をほとんど分かっていないので、慣れている Ruby で大半の変換作業を行いました。

$ exiftool -P -overwrite_original -QuickTime:CreateDate="$(ruby -rtime -e 'print Time.strptime(`exiftool -DateTimeOriginal RMOV0003.AVI`[/\d{4}:\d\d:\d\d \d\d:\d\d:\d\d/], "%Y:%m:%d %T").utc.strftime("%Y:%m:%d %T")')" RMOV0003.mov
    1 image files updated

-QuickTime:CreateDate= 以降にある $()bash のコマンド展開です。ので、丸括弧の中に Ruby ワンライナーを入れておけばワンライナーの実行結果が反映されるってわけです。今回は文字列としての日付が欲しいのでさらに外側をダブルクオートで括っておきます。

Ruby での処理内容は以下の通りです。

  1. `exiftool -DateTimeOriginal RMOV0003.AVI` で作成時刻のメタ情報の文字列を取得。
  2. メタ情報の文字列から [/\d{4}:\d\d:\d\d \d\d:\d\d:\d\d/] で時刻情報のみを抜き出す。
  3. Time.strptime メソッドを使って時刻情報文字列を Time オブジェクトへ変換。
  4. Time#utc メソッドでUTCに変換。
  5. Time#strftime メソッドでフォーマットを指定して時刻表示文字列に変換(目的としていた UTC 表記時刻)。
  6. Kernel.#print メソッドで文字列を出力

この後に Photos.app へ登録します。(しつこいですが)登録後に行っても意味ありません。

*1:JPEG ファイルも YUV422 なので、FFmpeg で複数枚の JPEG ファイルから動画ファイルを造るときにも同様の現象が生じます。そんなことをメモ書きしたのが連番静止画からタイムラプス動画を作る - Qiitaです。