ffmpeg + Mathematicaで動画ファイルを作成する

MathematicaのExportでAVIなどの動画ファイルを作成することは簡単に可能だが、 いかんせんすべてのフレームの画像を作成してからファイルに書きだすので、フレーム数に比例してメモリの使用量が多くなってしまう。 そのため長いムービーの作成は困難であった。

そこで、ffmpegと併用することでメモリ使用量を抑えつつ、長編の動画ファイルを制作する。 具体的には、

  1. ffmpegで標準入力から読み込ませるOutputStreamオブジェクトを作成する
  2. BinaryWriteでそのストリームに各フレームの画像を書き込む
  3. 終わったらCloseでストリームを閉じる

とする。ここで、各フレーム画像の形式をPPMにしないとうまく動作しなかった(BMP、PNGなどではなぜか動作せず)。

例えば、以下のようにするとtest001.aviができあがる。

s = OpenWrite[
   "!ffmpeg -y -vcodec ppm -f image2pipe -i - -vcodec rawvideo test001.avi", BinaryFormat -> True];
Do[
  BinaryWrite[s, ExportString[
    Plot[Sin[x + i], {x, 0, 2 Pi}
     , ImageSize -> {640, 480}
     , PlotRange -> {{0, 2 Pi}, {-1, 1}}
     , Frame -> True]
    , "PPM"]]
  , {i, 0, 2 Pi, Pi/50}
  ];
Close[s]

Exportと同じような使い勝手の関数ExportMovieを作るとこんな感じになる。

Options[ExportMovie] = {
   "FrameRate" -> 25,
   "VideoCodecOption" -> "-vcodec rawvideo",
   "ExportOptions" -> {}
   };
ExportMovie[outputfilepath_String, 
   expr_, {i_Symbol, imin_: 1, imax_, di_: 1}, OptionsPattern[]] := 
  Module[{stream},
   stream = OpenWrite[
     ToString[
      StringForm[
       "!ffmpeg -y -vcodec ppm -f image2pipe -i - -r `` `` ``"
       , OptionValue["FrameRate"]
       , OptionValue["VideoCodecOption"]
       , outputfilepath
       ]
      ]
     , BinaryFormat -> True];
   Do[
    BinaryWrite[stream, 
     ExportString[expr, "PPM", OptionValue["ExportOptions"]]]
    , {i, imin, imax, di}
    ];
   Close[stream]
   ];
Attributes[ExportMovie] = {HoldAll};
SyntaxInformation[ExportMovie] = {
   "ArgumentsPattern" -> {_, _, {_, _, _., _.}, OptionsPattern[]}
   , "LocalVariables" -> {"Table", {3, 3}}
   };

使い方の例としては以下。反復子を使ってTableなどと同様の表記で動画が作成できる。 また、ffmpegでサポートされていれば、Mathematicaでサポートされていない形式でも動画が作成可能。

ExportMovie["test002.avi"
 , Plot[Sin[x + i], {x, 0, 2 Pi}
  , ImageSize -> {640, 480}
  , PlotRange -> {{0, 2 Pi}, {-1, 1}}
  , Frame -> True]
 , {i, 0, 2 Pi, 2 Pi/100}
 , "FrameRate" -> 29.97
 , "VideoCodecOption" -> "-vcodec huffyuv"]