Skip to contents

tidymedia helps you work with video, audio and image files in R. It runs FFmpeg and MediaInfo for you, so you can prepare media 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.

Installation

You can install the development version from GitHub with:

# install.packages("devtools")
devtools::install_github("jmgirard/tidymedia")

Dependencies

tidymedia uses two free command-line programs. FFmpeg converts media files and comes with FFprobe, which reads them. MediaInfo also reads media files. Install the ones you need.

Debian and Ubuntu. In a terminal, run:
sudo apt-get install ffmpeg mediainfo

macOS. Install Homebrew. Then, in a terminal, run:
brew install ffmpeg media-info

Windows. For FFmpeg, run tidymedia::install_on_win() in R. It asks you to confirm before it downloads anything. For MediaInfo, download the CLI version from the MediaInfo download page and unzip it to a folder such as C:/Program Files/MediaInfo. Then tell tidymedia where the program is:
tidymedia::set_mediainfo("C:/Program Files/MediaInfo/mediainfo.exe")

Check the install. In R, run:
tidymedia::program_status()

Each program that tidymedia found shows a location and a version. If a location is NA, give tidymedia the path with set_ffmpeg(), set_ffprobe() or set_mediainfo(). On macOS, run brew --prefix ffmpeg in a terminal. FFmpeg and FFprobe are in the bin folder of the path that it prints. The help pages ?set_program and ?install_on_win give the details.

Examples

The examples below use a short sample clip that comes with the package. They copy it to the working folder, so the paths they print stay short:

invisible(file.copy(system.file("extdata", "sample.mp4", package = "tidymedia"), "."))
video <- "sample.mp4"

Build reproducible FFmpeg commands

The pipeline functions, whose names start with ffm_, build an FFmpeg command one step at a time. Nothing runs until you ask. ffm_compile() returns the command as a string, and ffm_run() runs it. See vignette("tidymedia") for a full tour.

ffm_files(video, "output.mp4") |>
  ffm_trim(start = 1, end = 5) |>
  ffm_crop(width = 160, height = 120) |>
  ffm_codec(video = "libx264") |>
  ffm_drop(streams = "audio") |>
  ffm_compile()
#> [1] "-y -i \"sample.mp4\" -vf \"trim=start=1:end=5,setpts=PTS-STARTPTS,crop=w=160:h=120:x=(in_w-out_w)/2:y=(in_h-out_h)/2\" -codec:v libx264 -an \"output.mp4\""

Common jobs have their own task functions. Add run = FALSE to see the command without running it:

extract_audio(video, "audio.aac", run = FALSE)
#> [1] "-y -i \"sample.mp4\" -codec:a copy -vn -map \"0:a:0\" \"audio.aac\""

Process a folder in batch

ffm_jobs() turns a folder into a jobs table, with one row for each media file. Some *_batch() functions, such as crop_video_batch(), take that table as it is. Others need a column added first, such as output.

If the folder has no files of the type you ask for, ffm_jobs() stops with an error. See vignette("batch") for more.

jobs <- ffm_jobs(".", type = "video")
# ffm_jobs() returns full paths; keep only the file name so this page stays
# short. The clip is in the working folder, so the commands still find it.
jobs$input <- basename(jobs$input)
crop_video_batch(jobs, width = 160, height = 120, run = FALSE)
#> # A tibble: 1 × 3
#>   input      output             command                                         
#>   <chr>      <chr>              <chr>                                           
#> 1 sample.mp4 sample_cropped.mp4 "-y -i \"sample.mp4\" -vf \"crop=w=160:h=120:x=…

Read metadata as tibbles

probe_all() uses FFprobe to read facts about the container and each stream. It returns them as tibbles. Each tibble starts with a file column, so the results for many files stack into one table:

probe_all(video)$streams
#> # A tibble: 2 × 69
#>   file      index codec_name codec_long_name profile codec_type codec_tag_string
#>   <chr>     <int> <chr>      <chr>           <chr>   <chr>      <chr>           
#> 1 sample.m…     0 h264       H.264 / AVC / … High    video      avc1            
#> 2 sample.m…     1 aac        AAC (Advanced … LC      audio      mp4a            
#> # ℹ 62 more variables: codec_tag <chr>, mime_codec_string <chr>, width <int>,
#> #   height <int>, coded_width <int>, coded_height <int>, has_b_frames <int>,
#> #   sample_aspect_ratio <chr>, display_aspect_ratio <chr>, pix_fmt <chr>,
#> #   level <int>, color_range <chr>, color_space <chr>, color_transfer <chr>,
#> #   color_primaries <chr>, chroma_location <chr>, field_order <chr>,
#> #   is_avc <chr>, nal_length_size <int>, id <chr>, r_frame_rate <chr>,
#> #   avg_frame_rate <chr>, time_base <chr>, start_pts <int>, start_time <dbl>, …

MediaInfo works too, through mediainfo_query(), mediainfo_template() and the get_*() functions. See vignette("metadata").

get_duration(video, unit = "sec")
#> [1] 1
get_width(video)
#> [1] 320

Query FFmpeg’s capabilities

ffmpeg_codecs() lists the codecs that your FFmpeg build knows:

head(ffmpeg_codecs())
#> # A tibble: 6 × 8
#>   name       details           type  decoding encoding intraframe lossy lossless
#>   <chr>      <chr>             <fct> <lgl>    <lgl>    <lgl>      <lgl> <lgl>   
#> 1 012v       Uncompressed 4:2… Video TRUE     FALSE    TRUE       FALSE TRUE    
#> 2 4xm        4X Movie          Video TRUE     FALSE    FALSE      TRUE  FALSE   
#> 3 8bps       QuickTime 8BPS v… Video TRUE     FALSE    TRUE       FALSE TRUE    
#> 4 a64_multi  Multicolor chars… Video FALSE    TRUE     TRUE       TRUE  FALSE   
#> 5 a64_multi5 Multicolor chars… Video FALSE    TRUE     TRUE       TRUE  FALSE   
#> 6 aasc       Autodesk RLE      Video TRUE     FALSE    FALSE      FALSE TRUE

Code of Conduct

Please note that the tidymedia project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.