The way I typically handle this type of situation is to export the individual images, then have software like FFmpeg handle the conversion to video. FFmpeg in particular is an extremely powerful tool (and is available for Windows, OS X, and most Linux distros).
First download the appropriate binary for your system. I placed the ffmpeg
binary in the folder se-temp
. Let's find it:
tempDir = FileNameJoin[{$HomeDirectory, "se-temp"}];
SetDirectory[tempDir];
FileNames[]
(* {"ffmpeg.exe"} *)
Yeah, I'm doing this from Windows. Don't worry though, the only change you should have to make if you're on Linux or OS X is to remove the .exe
from the command name. In fact, let me do that for you:
command = First@FileNames["ffmpeg*"];
We can use the (new in v10) function RunProcess
to check that we can call FFmpeg from Mathematica.
RunProcess[{command, "-version"}, "StandardOutput"]
(*
ffmpeg version N-72086-g51f6455 Copyright (c) 2000-2015 the FFmpeg developers
built with gcc 4.9.2 (GCC)
configuration: --enable-gpl --enable-version3 --disable-w32threads ...
*)
If you're on an earlier version, you can use Run
instead:
Run[command, "-version"]
(* 0 *)
0
tells us that the command was executed successfully (errors typically result in nonzero exit codes). If you're on Windows, a terminal window will flash open while the command is running.
Now let's generate some images:
r = 24;
l = 12;
imgs = Join @@
Table[Graphics[{If[
10 >= n > 2 &&
f > 0, {{EdgeForm[
Directive[Black, Thickness[1/200], JoinForm["Round"]]],
GrayLevel[0.5],
Disk[{0, 0}, 19, Pi/2 - {0, 2 Pi f/r}]}, {Black,
Thickness[1/500],
Line[{{{-16, 0}, {+16, 0}}, {{0, -9}, {0, 9}}}]}, {White,
Thickness[1/120], Circle[{0, 0}, 7],
Circle[{0, 0}, 8]}}], {Black,
Text[Style[n, 400], {0, 0}, {0, 0}]}},
PlotRange -> {{-16, 16}, {-9, 9}},
Background ->
If[10 >= n > 2 || (f == 0 && n >= 2), GrayLevel[0.8], Black],
ImageSize -> 1280], {n, Reverse@Range[l]}, {f, Range[r] - 1}];
r
sets the framerate, and l
sets the number of seconds. Just for fun, we can also generate some sound:
fs = 48000;
sound = Flatten[
N@Table[Boole[n == 2 && f == 0] Sin[2 Pi*1000 s/fs], {n,
Reverse@Range[l]}, {f, Range[r] - 1}, {s, fs/r}]];
(Ok, it's not an aural masterpiece: less Tupac and more 2-pop.) The parameters are the same as before, except now we have the sampling rate fs
.
Let's export:
imgName[i_Integer] := imgName @ IntegerString[i, 10, 4]
imgName[s_String] := "img" <> s <> ".png"
soundName = "audio.wav";
MapIndexed[Export[imgName[First@#2], #1] &, imgs];
Export[soundName, ListPlay[sound, SampleRate -> fs]];
Finally we can call ffmpeg
to join our images into a video:
RunProcess[{command, "-y", "-r", ToString@r, "-f", "image2", "-i", imgName["%04d"], "-i", soundName, vidName}];
Or using Run
:
Run[command, "-y", "-r", r, "-f", "image2", "-i", imgName["%04d"], "-i", soundName, vidName];
This runs the following command:
ffmpeg
-y # overwrite output if already exists
-r 24 # declares input frame rate
-f image2 # declares input format
-i "img%04d.png" # input image name
-i audio.wav # input audio name
video.mp4 # output name
Finally, we should clean up after ourselves by deleting the image files:
DeleteFile @* imgName /@ Range[Length[imgs]];
If you have a bunch of already-existing video files that you want to concatenate, FFmpeg will do that too.
First we need to create a temporary file with the list of videos to concatenate:
listName = "list.txt";
videos = {"video.mp4", "video.mp4"};
Export[listName,
StringJoin[
Riffle["file \"" <> # <> "\"" & /@
StringReplace[videos, {"\"" -> "\\\"", "\\" -> "\\\\"}], "\n"]]];
Then we run the command:
vidName = "videoDoubled.mp4";
RunProcess[{command, "-y", "-f", "concat", "-i", listName, "-c", "copy", vidName}];
Since our list had "video.mp4"
twice, the new video is just the old video repeated twice; but you get the idea.
Finally we clean up:
DeleteFile /@ videos;
DeleteFile @ listName;
Import
ed. $\endgroup$