knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) # Work in a temporary directory: the chunks below write real files, and none of # them belongs beside the vignette sources (verification.Rmd:33 set this). knitr::opts_knit$set(root.dir = tempdir())
library(tidymedia)
tidymedia runs FFmpeg and MediaInfo from R. It helps you prepare media files for research in a way you can repeat. It trims, crops and converts files, often many at once. It also reads media metadata into tibbles.
tidymedia does not try to cover everything FFmpeg can do. The words that FFmpeg uses, such as codec and stream, are defined in the glossary at the end of this page.
This page uses a short sample clip that comes with the package:
video <- system.file("extdata", "sample.mp4", package = "tidymedia")
Most jobs need one call to a task function. For example, you may need the audio
of a recording for a transcription tool. extract_audio() writes the audio to
its own file:
extract_audio(video, "audio.m4a")
A task function runs FFmpeg at once. It returns the FFmpeg command it ran, but
invisibly, so R prints nothing. A function that writes two files, such as
separate_audio_video(), returns both commands.
To see the command without running it, add run = FALSE. The function then
returns the command as a string that you can read, log or save:
extract_audio(video, "audio.m4a", run = FALSE)
This is the main idea of the package. You can read each command before you run it. Cropping works the same way:
crop_video(video, "cropped.mp4", width = 160, height = 120, run = FALSE)
Some recordings have more than one audio track, for example a room microphone and a lapel microphone. If you do not choose a track, FFmpeg chooses one for you.
Each task function that reads one input file and picks an audio track has an
audio_stream argument. It counts from 0, and it counts only the audio tracks.
So audio_stream = 1 is the second audio track, wherever it sits in the file:
extract_audio(video, "lapel.m4a", audio_stream = 1, run = FALSE)
If you leave audio_stream out, the default depends on the function. A
function that writes exactly one audio track, such as extract_audio(), takes
the first track. A function that passes the audio through, such as
crop_video(), keeps every track. Compare the -map parts of the two commands
above and below:
crop_video(video, "cropped.mp4", width = 160, height = 120, run = FALSE)
The help page ?audio_stream lists which functions use each default. It also
explains audio_input, which the functions for several input files use. That
argument counts input files from 0, not audio tracks.
Each task function has a batch version for a folder of files, such as
extract_audio_batch() and crop_video_batch(). See vignette("batch"). For
a full research example that uses many task functions, see
vignette("workflow").
tidymedia has three kinds of function:
extract_audio() and crop_video(), do one
common job in one call. Start here.ffm_, build an FFmpeg
command one step at a time. Each task function uses them. Use them when no
task function does the job you need.ffmpeg(), ffprobe() and mediainfo(), pass your own
arguments to the program. Use them for anything that tidymedia does not
cover.The rest of this page shows the pipeline functions.
A pipeline starts with ffm_files(), which names the input and output files.
You add steps with |>. Each step adds an instruction, and nothing runs yet.
ffm_compile() turns the pipeline into the FFmpeg command:
ffm_files(video, "output.mp4") |> ffm_trim(start = 1, end = 5) |> ffm_crop(width = 160, height = 120) |> ffm_codec(video = "libx264") |> ffm_compile()
When you print a pipeline, R shows the same command. So you can look at a pipeline at any point:
ffm_files(video, "output.mp4") |> ffm_scale(width = 320, height = 240) |> ffm_pixel_format("yuv420p")
To run the command and write the output file, use ffm_run() in place of
ffm_compile().
ffm_fps() changes the frame rate. ffm_drawbox() draws a box
on the picture, which can hide a name on screen:
ffm_files(video, "boxed.mp4") |> ffm_fps(15) |> ffm_drawbox(x = 10, y = 10, width = 60, height = 40, color = "black") |> ffm_compile()
ffm_loudnorm() makes audio a set loudness, in LUFS, with a limit
on its true peak. Here ffm_drop() also leaves the video out of
the output:
ffm_files(video, "speech.m4a") |> ffm_drop("video") |> ffm_loudnorm(target_loudness = -23, true_peak = -1) |> ffm_compile()
ffm_output_options() adds FFmpeg output options that have no pipeline
function of their own. tidymedia still puts them in the right place in the
command:
ffm_files(video, "web.mp4") |> ffm_output_options("-movflags +faststart") |> ffm_compile()
You can cut a clip in two ways. An exact cut re-encodes the video, which is slower. A fast cut uses a stream copy, which keeps the quality but starts at the nearest keyframe.
ffm_seek() does both. Set reencode = FALSE and add ffm_copy() for a fast
cut:
# Fast cut with no loss of quality ffm_files(video, "output.mp4") |> ffm_seek(start = 1, end = 5, reencode = FALSE) |> ffm_copy() |> ffm_compile()
ffm_seek() uses FFmpeg's -ss and -to options. ffm_trim() uses FFmpeg's
trim filter. Only ffm_seek() can make a fast cut.
Some pipeline functions take more than one input. Give ffm_files() a vector
of files. Then use ffm_hstack() to put the videos side by side, or
ffm_vstack() to put one above the other. ffm_overlay() puts one video on
top of another, and ffm_concat() joins them end to end:
ffm_files(c(video, video), "side_by_side.mp4") |> ffm_hstack() |> ffm_compile()
ffm_hstack(), ffm_vstack() and ffm_overlay() leave the audio out. To
keep it, add ffm_map("0:a"). ffm_concat() keeps all the streams, audio
included.
One-input task functions that pass audio through, such as crop_video(), do
the opposite and keep every audio track.
Two task functions cover the common cases. compare_videos() puts videos side
by side or one above the other. picture_in_picture() puts a smaller copy of
one video on top of another. Both take audio_input, which names the input
whose audio to keep. They copy that audio unchanged unless you choose an audio
encoder with audio_codec.
compare_videos(c(video, video), "compare.mp4", audio_input = 0, run = FALSE)
A pipeline has one input chain, a list of filters in order, and one output. It
cannot build an FFmpeg filter graph with branches. For that, use the direct
command ffmpeg():
# Your own arguments, passed to FFmpeg as they are ffmpeg("-version")[1]
"h264" or "aac".libx264 and h264_videotoolbox for
H.264."yuv420p". Many players need "yuv420p" to play H.264 video.vignette("workflow") shows a full research example.vignette("batch") shows how to run a task function over many files.vignette("metadata") shows how to read metadata into tibbles.vignette("verification") shows how to check outputs and limit run time.Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.