##' Create video from sequence of still images
##'
##' Stitch a sequence of images into a video animation using FFmpeg
##' software.
##'
##' @param dir directory containing images, default is working
##' directory.
##' @param pattern character, pattern for matching image file names.
##' See details.
##' @param output character, output file name. See details.
##' @param output_dir output directory, default is working directory, but will
##' be created if it does not exist.
##' @param fps_in integer, intended framerate of input image sequence
##' in frames per second.
##' @param start_frame integer, start frame. Defaults to
##' \code{start=1}.
##' @param end_frame integer, end frame. Defaults to \code{end_frame
##' = NULL}.
##' @param size character, the dimensions of the video
##' output. Defaults to \code{"source"}, which is equal to the
##' dimensions of the input files. Otherwise scaling is performed on
##' the output. See details.
##' @param preset character, encoding presets available in
##' FFmpeg. Defaults to \code{medium}. See details.
##' @param codec character, video codec used. See details.
##' @param format character, pixel format used. See details.
##' @param lossless logical, use lossless H.264 encoding if
##' applicable. Defaults to \code{FALSE}. See details.
##' @param fps_out integer, framerate of animation in frames per
##' second.
##' @param overwrite logical, overwrite existing output file?
##'
##' @param ffmpeg A file path (characer) to FFmpeg executable. This
##' argument is only needed if ffmpeg is not added to your system
##' path. For Windows machines, path must point to 'ffmpeg.exe',
##' located in the bin subfolder within the ffmpeg folder. For
##' example on Windows machines,
##' "C:/Users/Username/Documents/ffmpeg-3.4.1-win64-static/bin/ffmpeg.exe").
##' On Mac, path must point to 'ffmpeg' within the 'bin' subfolder
##' "/home/directory/Documents/bin/ffmpeg".
##'
##' @param diagnostic_mode Logical (default = FALSE). If true, return value
##' is a character vector with FFMPEG output.
##'
##' @details \code{make_video} is based on \code{mapmate::ffmpeg}.
##' More information about \code{mapmate} package is found at
##' \code{https://github.com/leonawicz/mapmate}. This function
##' converts R syntax into command line call that is submitted to
##' \code{ffmpeg}. \code{ffmpeg} must be installed and able to be
##' accessed by the system command line prior to running
##' \code{make_video}. See \code{https://www.ffmpeg.org} for
##' information on installing ffmpeg.
##'
##' @details The FFmpeg multimedia framework provides extensive
##' flexibility and options for converting and manipulating video
##' and audio content. \code{make_video} provides a small subset
##' of options useful for creating simple animated videos. If
##' additional flexibility or options are needed, the user may need
##' to develop or modify \code{make_video} or submit calls
##' directly to FFmpeg using the system command line.
##'
##' @details Sequenced image files are input into FFmpeg using a file name
##' convention which requires specifying the entire, non-changing file name
##' with a consecutive integer numbering component. The integer number
##' component indicates the maximum number of digits of all the images. For
##' example, \code{\%04d.png} represents the file numbering \code{0000.png,
##' 0001.png, 0002.png, ..., 9999.png} and \code{\%02d.png} represents images
##' numbered from \code{00.png, 01.png, 02.png, ..., 10.png}. File names of
##' image sequences input to FFmpeg may have a prefix provided that all image
##' files have the sequence. For example an image sequence of
##' \code{myfile001.png, myfile002.png, myfile003.png, ..., myfile999.png} is
##' represented as \code{myfile\%03d.png}. \code{pattern} only accepts this
##' pattern of image file names.
##'
##' @details Function can create .mp4, .mov, .mkv, .gif, .wmv, or
##' .mpeg animations. Format of created animation is determined by
##' file extension of \code{output}.
##' @details \code{make_video} allows
##' user to specify input framerate \code{fps_in} of image sequence
##' and output \code{fps_out} framerate of animation.
##'
##' @details \code{start_frame} specifies starting frame in animation
##' and \code{end_frame} specifies end frame in animation. If
##' \code{start_frame} and \code{end_frame} are specified, only
##' images within range are used in animation. Specifying
##' \code{start_frame} and \code{end_frame} allows user to skip
##' images.
##'
##' @details If \code{size} is not set to \code{"source"}, the output video is
##' scaled. \code{size} can be a character string of dimensions in length by
##' height format such as \code{"720x480"} or an abbreviated standard such as
##' \code{"ntsc"}. See
##' \href{http://ffmpeg.org/ffmpeg-utils.html#Video-size}{FFmpegstandard video
##' sizes} for common dimensions and available abbreviations.
##'
##' @details Presets provide a certain encoding speed to compression
##' ratio. Available presets include \code{ultrafast},
##' \code{superfast}, \code{veryfast}, \code{faster}, \code{fast},
##' \code{medium}, \code{slow}, \code{slower}, \code{veryslow}.
##' Faster preset i.e.(\code{ultrafast}) corresponds to greater file
##' size, lower animation quality, and faster computation
##' times. Slower speeds \code{veryslow} corresponds to smaller
##' file, higher quality animation and slower computation times.
##' See \url{http://trac.ffmpeg.org/wiki/Encode/H.264}
##'
##' @details \code{codec} is ignored if the file name in
##' \code{pattern} ends with \code{.gif}. For other video output
##' file types a default codec is used depending on the file
##' extension of output animation.These
##' can be overridden with options like \code{codec="h264"},
##' \code{"libx264"}, \code{"libvpx"}, \code{"prores"},
##' \code{"qtrle"}, etc., but the user needs to be knowledgeable
##' regarding which codecs can be used for which output types or
##' errors will be thrown.
##'
##' @details \code{format} is ignored if the file name in
##' \code{pattern} ends with \code{.gif}. The default is
##' \code{"yuv420p"}, which performs 4:2:0 chroma subsampling. This
##' pixel format can reduce video quality, but it is the default
##' because it ensures compatibility with most media players. For
##' valid alternatives, run \code{system("ffmpeg -pix_fmts")}.
##'
##' @details \code{lossless} is ignored except for relevant
##' \code{codec} settings, e.g., \code{h264} or \code{libx264}. If
##' \code{TRUE}, recommended \code{preset} values are
##' \code{ultrafast} or \code{veryslow}. See
##' \url{https://trac.ffmpeg.org/wiki/Encode/H.264} for more
##' information.
##'
##' @return One video animation will be written to \code{output_dir}
##'
##' @author Todd Hayden, Tom Binder, Chris Holbrook
##'
##' @examples
##'
##' \dontrun{
##'
##' # load frames
##' frames <- system.file("extdata", "frames", package = "glatos")
##'
##' # make .mp4 video
##' # make sure ffmpeg is on system path (see \code{make_frames} and details)
##' make_video(dir = frames, pattern = "%02d.png", output = "animation.mp4" )
##'
##' # make .wmv video
##' make_video(dir=frames, pattern = "%02d.png", output = "animation.wmv" )
##'
##' # start animation on frame 10, end on frame 20
##' make_video(dir=frames, pattern = "%02d.png", start_frame = 10,
##' end_frame = 20, output = "animation_2.mp4")
##'
##' # resize output video to 720x480
##' make_video(dir=frames, pattern = "%02d.png", size = "720x480",
##' output = "animation_3.mp4" )
##'
##' # change ffmpeg preset
##' make_video(dir=frames, pattern = "%02d.png", preset = "ultrafast",
##' output = "animation_4.mp4")
##'
##' # change input framerate
##' make_video(dir=frames, fps_in = 1, pattern = "%02d.png",
##' preset = "ultrafast", output = "animation_5.mp4")
##'
##'
##' # add path to ffmpeg (windows)
##' make_video(dir = frames, pattern = "%02d.png", output = "animation.mp4",
##' ffmpeg = "c://path//to//windows//ffmpeg.exe")
##'
##' # add path to ffmpeg (mac)
##' make_video(dir = frames, pattern = "%02d.png", output = "animation.mp4",
##' ffmpeg = "/path/to/ffmpeg")
##' }
##'
##' @export
make_video <- function(dir = getwd(),
pattern,
output = "animation.mp4",
output_dir = getwd(),
fps_in = 30,
start_frame = 1,
end_frame = NULL,
size = "source",
preset = "medium",
codec = "default",
format = "yuv420p",
lossless = FALSE,
fps_out = 30,
overwrite = FALSE,
ffmpeg = NA,
diagnostic_mode = FALSE){
# test ffmpeg and get path
ffmpeg <- glatos:::get_ffmpeg_path(ffmpeg)
#check if dir exists
if(!dir.exists(dir)) stop(paste0("Input dir '", dir , "' not found."),
.call = FALSE)
#make output directory if it does not already exist
if(!dir.exists(output_dir)) dir.create(output_dir)
cmd <- ifelse(is.na(ffmpeg), 'ffmpeg', ffmpeg)
input <- file.path(dir, pattern)
input <- paste0("-i ", "\"", input, "\"" )
inrate <- paste("-framerate", fps_in)
start <- paste("-start_number", start_frame)
input <- paste0(paste(inrate, start, input), collapse = " ")
ext <- strsplit(output, "\\.")[[1]]
ext_stop <-
"'output' must end in '.mp4', '.mov', '.mkv', '.gif', 'wmv', 'mpeg'"
if (length(ext) == 1) {
stop(ext_stop)
} else { ext <- utils::tail(ext, 1) }
if (!ext %in% c("mp4", "mov", "mkv", "gif", "wmv", "mpeg")) stop(ext_stop)
output_file <- file.path(output_dir, output)
output <- paste0("\"", output_file, "\"")
if (!is.null(end_frame)){
nframes <- end_frame - start_frame
output <- paste("-vframes", nframes, output)
}
format <- paste0("format=", format)
if (size == "source") {
size <- ""
} else if (ext != "gif") {
size <- paste0(",scale=", size, ",setsar=1:1")
} else { size <- paste("-s", size) }
if (ext == "gif") {
vf <- size
} else { vf <- paste0("-vf ", "\"", format, size, "\"")}
output <- paste(vf, output)
outrate <- paste("-r", fps_out)
output <- paste(outrate, output, ifelse(overwrite, "-y", "-n"))
if (ext == "gif") { vc <- " "
} else {
if (codec == "default")
codec <- switch(ext, mp4 = "libx264", mov = "libx264",
mkv = "libx264", wmv = "libx264", mpeg = "libx264" )
vc <- paste0(" -c:v ", codec, " -preset ", preset, " ")
if (lossless & codec %in% c("h264", "libx264")) vc <- paste0(vc, "-qp 0 ")
}
x <- gsub(" ", " ", paste0(input, vc, output))
#check if output file exists
if(file.exists(output_file) & overwrite == FALSE) {
warning("No video file written because output file already exists and ",
"overwrite = FALSE.")
return()
}
msg_i <- system2(cmd, x, stdout = TRUE)
if(diagnostic_mode) {
message("[diagnostic mode]: See return object for ffmpeg output.")
return(msg_i)
}
message("Video file written to ", output_file, ".")
return(output_file)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.