Skip to contents

Re-encode a video to a consistent, reproducible format for analysis. The format is one video codec and pixel format, and optionally a resolution and frame rate, with +faststart for smooth playback. format_for_web uses a fixed recipe for web delivery. Here, every part of the standard is an argument. So a lab can set its own house format once and apply it across a dataset. The glossary in vignette("tidymedia") explains media terms such as codec, pixel format and frame rate.

Usage

standardize_video(
  infile,
  outfile,
  width = NULL,
  height = NULL,
  fps = NULL,
  video_codec = "libx264",
  audio_codec = "copy",
  pixel_format = "yuv420p",
  hardware = c("none", "nvenc", "videotoolbox"),
  fallback = FALSE,
  quality = NULL,
  audio_stream = NULL,
  run = TRUE
)

Arguments

infile

A string containing the path to a video file.

outfile

A string containing the path of the video file to write.

width

The output width in pixels (a positive number), or NULL (default) to leave the width unconstrained.

height

The output height in pixels (a positive number), or NULL (default) to leave the height unconstrained.

fps

The output frame rate (a positive number or FFmpeg framerate expression such as "30000/1001"), or NULL (default) to keep the input frame rate.

video_codec

A string naming the output video codec (default "libx264"). NULL emits no -codec:v and lets the output container's default encoder decide. NULL is how you opt out of the H.264 default for a container that does not hold it. For a .webm output, pass video_codec = NULL and audio_codec = NULL, because the default audio_codec = "copy" would otherwise carry a codec WebM cannot hold.

audio_codec

A string naming the output audio codec. The default "copy" stream-copies the source audio unchanged. Name a real encoder, such as "aac", when the source audio codec cannot be copied into the output container. NULL emits no -codec:a and lets the container's default encoder decide.

pixel_format

A string naming the output pixel format (default "yuv420p").

hardware

The encoder backend. "none" (default) uses the software video_codec. "nvenc" uses NVIDIA GPU encoding (H.264, HEVC and AV1), and "videotoolbox" uses Apple GPU encoding (H.264 and HEVC). A backend uses its own encoder for the family of video_codec. For example, "libx264" becomes "h264_nvenc" or "h264_videotoolbox". See has_hardware_encoder for availability and its caveats. This applies to video only. audio_codec is never hardware-accelerated. Resolving a hardware backend asks this FFmpeg build which encoders it has. So the first such call that re-encodes the video runs FFmpeg while the command is built, even under run = FALSE. The answer is remembered for the rest of the R session. See refresh_ffmpeg_capabilities to discard it.

fallback

A logical. When a hardware other than "none" is requested but its encoder is unavailable, TRUE re-encodes with the software video_codec and a message. FALSE (default) aborts instead. This keeps output reproducible by never changing the codec silently. A video_codec in a family that the backend has no encoder for is a wrong argument, not an absent encoder. So it aborts whatever fallback says.

quality

A number, or NULL (default) to leave the encoder's own default in place. It is the encoder's own rate-control value, passed through unchanged. libx264 and libx265 read it as -crf (0 to 51). The nvenc encoders read it as -cq (0 to 51), and the videotoolbox encoders as -q:v (1 to 100). Each scale is its own: the same number means something different on each encoder. A value outside the encoder's range is refused. An encoder outside those seven, such as libvpx-vp9, is refused with quality set. So is a video_codec of "copy", or of NULL under hardware = "none". When fallback = TRUE falls back to software, the value is dropped and the message says so, because it belonged to the hardware encoder's scale.

audio_stream

The audio track to carry into the output, as a number that counts from 0 among the audio tracks of the input. 0 is the first audio track and 1 is the second. Other streams in the file, such as video, do not count. NULL (default) carries every audio track. The every-track family reads NULL this way: separate_audio_video, standardize_video, anonymize_video, crop_video, segment_video and format_for_web, and their _batch forms. The first-track family reads it as the first audio track only: extract_audio, convert_audio and normalize_audio, and their _batch forms. The function does not carry subtitle or data streams in either case. A track the input does not have gives an FFmpeg error, not an R one. See audio_stream for how this differs from audio_input, the input index on compare_videos and picture_in_picture. (default = NULL)

run

A logical: run the command through FFmpeg (TRUE, default) or return the compiled command without running it (FALSE).

Value

The compiled FFmpeg command (invisibly when run = TRUE).

Details

The default standard, standardize_video(infile, outfile), re-encodes to H.264 video (video_codec = "libx264") with pixel_format = "yuv420p" and -movflags +faststart. It keeps the source resolution and frame rate. Audio is stream-copied unchanged (-c:a copy) unless audio_codec names an encoder. The same input therefore always compiles to a byte-identical command. Loudness standardization is out of scope. For that, see normalize_audio.

Resolution follows width and height:

  • With both, the output has exactly those dimensions.

  • With only one, the aspect ratio is kept, and the other dimension is rounded to the nearest even number (FFmpeg's -2).

  • With neither, the source resolution is kept, but odd dimensions are rounded down to the nearest even value. yuv420p and libx264 require this, and it changes nothing for input that is already even. So the output always encodes.

Examples

video <- system.file("extdata", "sample.mp4", package = "tidymedia")
# The documented default standard (H.264 / yuv420p / +faststart)
standardize_video(video, "std.mp4", run = FALSE)
#> [1] "-y -i \"/home/runner/work/_temp/Library/tidymedia/extdata/sample.mp4\" -vf \"crop=w=floor(in_w/2)*2:h=floor(in_h/2)*2:x=(in_w-out_w)/2:y=(in_h-out_h)/2\" -codec:v libx264 -codec:a copy -pix_fmt yuv420p -movflags +faststart -map \"0:v?\" -map \"0:a?\" \"std.mp4\""
# Pin resolution and frame rate too
standardize_video(video, "std.mp4", width = 1280, height = 720, fps = 30,
                  run = FALSE)
#> [1] "-y -i \"/home/runner/work/_temp/Library/tidymedia/extdata/sample.mp4\" -vf \"scale=w=1280:h=720,fps=30\" -codec:v libx264 -codec:a copy -pix_fmt yuv420p -movflags +faststart -map \"0:v?\" -map \"0:a?\" \"std.mp4\""
# Carry only the second audio track instead of all of them
standardize_video(video, "std.mp4", audio_stream = 1, run = FALSE)
#> [1] "-y -i \"/home/runner/work/_temp/Library/tidymedia/extdata/sample.mp4\" -vf \"crop=w=floor(in_w/2)*2:h=floor(in_h/2)*2:x=(in_w-out_w)/2:y=(in_h-out_h)/2\" -codec:v libx264 -codec:a copy -pix_fmt yuv420p -movflags +faststart -map \"0:v?\" -map \"0:a:1\" \"std.mp4\""