Last modified 2023-11-28.
Time to read: 5 minutes.
When I set up a 4K camera on location and let it roll while I play a set, I get really large video files. I also get large files when using OBS Studio; my usual setup records video from Cam Link 4K, and audio using RME TotalMix. I need to be able to trim video files so the portions before and after the good stuff are discarded.
Lots of online conversations revolve around trimming video files. Dozens of PC and Mac programs exist to do that task, mostly low quality and / or bothersome to use. Other solutions are overkill, for example Adobe Premiere Pro and DaVinci Resolve.
In this post I present a command-line program that can dramatically reduce file size, while preserving quality, and properly trimming to specified time periods.
Although the program is written as a Ruby gem, it installs a command called
No programming knowledge is required in order to use it.
What Is FFmpeg?
ffmpeg is a powerful command-line media converter.
It is not particularly user-friendly, but it works very well if you put in the time to learn it.
The program described in this article provides a easy means of running
ffmpeg for those who want to trim media.
FFmpegis a universal media converter. It can read a wide variety of inputs – including live grabbing/recording devices – filter, and transcode them into a plethora of output formats.
FFmpegreads from an arbitrary number of input “files” (which can be regular files, pipes, network streams, grabbing devices, etc.), specified by the
‑ioption, and writes to an arbitrary number of output “files”, which are specified by a plain output url. Anything found on the command line which cannot be interpreted as an option is considered to be an output url.
Each input or output url can, in principle, contain any number of streams of different types (video, audio, subtitle, attachment, or data). The allowed number and/or types of streams may be limited by the container format. Selecting which streams from which inputs will go into which output is either done automatically or with the
‑mapoption (see the Stream selection chapter).
Need a way to figure out the start and stop times to trim a video?
Djv is an excellent video player for this purpose.
It is one of the very few video players than can step through a video frame-by-frame, forwards and backwards.
- Mac, Windows, Linux
- Allows frame-by-frame stepping
- Displays the current time reliabily
- High quality
Installing the Trim Command
You need a working Ruby environment to install this program. I describe how to set that up here.
trim command is provided by the
media_trim Ruby gem.
Install it like this:
$ gem install media_trim
The help message is:
$ trim -h media_trim - Trim an audio or video file using ffmpeg
- Works with all formats supported by ffmpeg. - Seeks to the nearest frame positions by re-encoding the media. - Reduces file size produced by OBS Studio by over 80 percent. - Can be used as a Ruby gem. - Installs the 'trim' command.
When run as a command, output files are named by adding a 'trim.' prefix to the media file name, e.g. 'dir/trim.file.ext'. By default, the trim command does not overwrite pre-existing output files. When trimming is complete, the trim command displays the trimmed file, unless the -q option is specified
Command-line Usage: trim [OPTIONS] dir/file.ext start [[to|for] end]
- The start and end timecodes have the format [HH:[MM:]]SS[.XXX] Note that decimal seconds may be specified, and frames may not; this is consistent with how ffmpeg parses timecodes. - end defaults to the end of audio/video file
OPTIONS are: -d Enable debug output -f Overwrite output file if present -v Verbose output -V Do not view the trimmed file when complete.
Examples: # Crop demo/demo.mp4 from 15.0 seconds to the end of the video, save to demo/trim.demo.mp4: trim demo/demo.mp4 15
# Crop dir/file.mkv from 3 minutes, 25 seconds to 9 minutes, 35 seconds, save to demo/trim.demo.mp4: trim demo/demo.mp4 3:25 9:35
# Same as the previous example, using optional 'to' syntax: trim demo/demo.mp4 3:25 to 9:35
# Save as the previous example, but specify the duration instead of the end time by using the for keyword: trim demo/demo.mp4 3:25 for 6:10
Need a way to figure out the start and stop times to trim a video? DJV is an excellent video viewer https://darbyjohnston.github.io/DJV/ - allows frame-by-frame stepping - displays the current time reliabily - F/OSS - Mac, Windows, Linux - High quality
Following is a sample usage of
trim, which extracts the portion of
and writes the extracted portion to a new file called
$ trim VideoFile.mkv 25 2:52 Trimming '/mnt/f/work/VideoFile.mp4' from 00:25 to 2:52 (duration 2:27) Trim took 0:03
Now we have these files:
$ ls -AlF total 1546136 -rw-r--r-- 1 mslinn mslinn 30579665 Jan 23 13:03 'trim.VideoFile.mkv' -rwxrwxrwx 1 mslinn mslinn 180988555 Jan 22 18:31 'VideoFile.mkv'
Notes to Programmers
For any programmers who might read this:
- Some of the
ffmpegoptions this script uses are not available in older versions.
The most important thing to know about options that might be passed to
ffmpegis that arbitrary start and end times can be specified accurately when trimming if the video stream is re-encoded. To force a video re-encoding, simply do not specify the
- We discard extra (non-essential) streams from video files. See Selecting streams with the -map option.
Sony A7iii Media Files
This section provides background for how the
media_trim program operates.
Most people do not need to read it.
I wanted to trim a video file created by my Sony A7iii camera, so only the portion from 0:51 through 2:45 was extracted.
First let’s use
ffprobe to examine the streams within the media file.
FFprobegathers information from multimedia streams and prints it in human- and machine-readable fashion.
For example, it can be used to check the format of the container used by a multimedia stream and the format and type of each media stream contained in it.
$ ffprobe myvideo.mp4 ffprobe version 5.1.2-3ubuntu1 Copyright (c) 2007-2022 the FFmpeg developers built with gcc 12 (Ubuntu 12.2.0-14ubuntu2) configuration: --prefix=/usr --extra-version=3ubuntu1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --arch=amd64 --enable-gpl --disable-stripping --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libdav1d --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libglslang --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librabbitmq --enable-librist --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libsvtav1 --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzimg --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --disable-sndio --enable-libjxl --enable-pocketsphinx --enable-librsvg --enable-libmfx --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-libplacebo --enable-librav1e --enable-shared libavutil 57. 28.100 / 57. 28.100 libavcodec 59. 37.100 / 59. 37.100 libavformat 59. 27.100 / 59. 27.100 libavdevice 59. 7.100 / 59. 7.100 libavfilter 8. 44.100 / 8. 44.100 libswscale 6. 7.100 / 6. 7.100 libswresample 4. 7.100 / 4. 7.100 libpostproc 56. 6.100 / 56. 6.100 [mov,mp4,m4a,3gp,3g2,mj2 @ 0x560a973df5c0] st: 0 edit list: 1 Missing key frame while searching for timestamp: 1001 [mov,mp4,m4a,3gp,3g2,mj2 @ 0x560a973df5c0] st: 0 edit list 1 Cannot find an index entry before timestamp: 1001. Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'myvideo.mp4': Metadata: major_brand : XAVC minor_version : 16785407 compatible_brands: XAVCmp42iso2 creation_time : 2023-01-05T00:52:24.000000Z Duration: 00:10:58.16, start: 0.000000, bitrate: 51445 kb/s Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709/bt709/iec61966-2-4, progressive), 1920x1080 [SAR 1:1 DAR 16:9], 49370 kb/s, 59.94 fps, 59.94 tbr, 60k tbn (default) Metadata: creation_time : 2023-01-05T00:52:24.000000Z handler_name : Video Media Handler vendor_id :  encoder : AVC Coding Stream #0:1[0x2](und): Audio: pcm_s16be (twos / 0x736F7774), 48000 Hz, 2 channels, s16, 1536 kb/s (default) Metadata: creation_time : 2023-01-05T00:52:24.000000Z handler_name : Sound Media Handler vendor_id :  Stream #0:2[0x3](und): Data: none (rtmd / 0x646D7472), 491 kb/s (default) Metadata: creation_time : 2023-01-05T00:52:24.000000Z handler_name : Timed Metadata Media Handler timecode : 03:52:30:26 Unsupported codec with id 0 for input stream 2
FFprobe shows us that
myvideo.mp4 has 3 streams, numbered from origin zero:
MPEG-4 video stream – encoded using the H.264
high profile, which is the most commonly used
with 1920x1080 resolution, recorded at 50 Mb/s, and 59.94 fps.
This stream was recoded to the same format using
FFmpeg’s default stream handling. The recoding process computed keyframes for the new start and points after trimming.
CD-quality audio stream –
WAVformat), at 48 kHz, in stereo, with a bit rate of 1536 kb/s. In contrast, 24 bits is commonly used in 2023 for streaming audio.
WAVencoding is uncompressed, and so it is disallowed by strict mp4 compliance.
mp4compliance by default,
FFmpegwill not copy audio encoded in
WAVformat. However, the audio stream can be transcoded to a compressed format, so it can be included in the output file. You could relax
mp4compliance by providing the
-strict experimentaloption to
FFmpeg. Instead, I decided to compress the audio using AAC because this results in a smaller file that sounds just as good.
Data stream – normally ignored by
ffmpegwhen creating output.
ffprobeshows an error for this stream (
Unsupported codec with id 0 for input stream 2), but we do not need this stream, so the
trimscript will also ignore it.
The hierarchy of audio encoder quality
has been reported as:
libopus > libvorbis >= libfdk_aac > libmp3lame >= eac3/ac3 > aac > libtwolame > vorbis > mp2 > wmav2/wmav1.
Ffmpeg’s AAC transcoder gives poor results for bit rates less than 128k.
Opus gives a better result, however some hardware devices may not have support.
My 3-year-old TCL TV does not support Opus, for example.
FFMpeg supports 5 AAC encoders, including the regular AAC encoder, and the Fraunhofer FDK AAC codec. The latter requires that you compile FFmpeg because of licensing issues. This article will not get into that.
Regular AAC codec
Following is the incantation used by the
It yields regular an
mp4 with a video stream and an AAC stereo stream.
$ ffmpeg -y -ss 51 -to 2:45 -i input.mp4 -acodec aac output.mp4
Both the video and audio streams are recoded. The video stream selected from the input file is the highest resolution video stream in that file. Non-essential streams for regular playback are ignored.😁
The resulting file is a balance of the highest quality audio and video streams, with the smallest overall file size that can usually be expected to play on most devices.
Fraunhofer FDK AAC codec
You could go one better and
FFmpeg with the Fraunhofer FDK AAC codec,
then invoke it with:
$ ffmpeg -y -ss 51 -to 2:45 -i input.mp4 -c:a libfdk_aac output.mp4