Accurately cut video files from command line
Cutting video with ffmpeg
You can accurately cut videos with FFmpeg. Since version 2.5 it's very easy.
This would for example cut 10 seconds, starting from 0 minutes, 3 seconds and 123 milliseconds.
ffmpeg -ss 00:00:03.123 -i input.mp4 -t 10 -c:v libx264 -c:a copy out.mp4
The position and the time may be either in seconds or in hh:mm:ss[.xxx]
form.
Note that in these examples, video will be re-encoded using the x264 encoder; audio is copied over.
You can also use -to
instead of -t
to specify the end point instead of the duration. In this case, however, -to
is equivalent to -t
, since by putting the -ss
in front of -i
, ffmpeg will first seek to that point and then start outputting.
If the output does not appear to be cut correctly, adding -fflags +genpts
to the command may help.
See also the Seeking wiki entry.
The only Linux command-line tool I've found so-far, that can cut at exact frame (or, with frame accuracy), is melt
(sudo apt-get install melt
).
Say you have an inputvid.mp4
- first check its encoding settings with say ffmpeg
(here, I just say I want to encode it again to -f mp4
, but as the file /dev/null
so the output is discarded; I redirect stderr so I can grep through it - note in the middle, the command prompts, and you should answer y
with ENTER, so the process proceeds and dumps the useful info; this is with ffmpeg 3.3.3 on Ubuntu 14):
ffmpeg -i inputvid.mp4 -f mp4 /dev/null 2>&1 | grep 'Stream\|encoder'
Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(tv, bt709), 640x360 [SAR 1:1 DAR 16:9], 389 kb/s, 23.98 fps, 23.98 tbr, 24k tbn, 47.95 tbc (default)
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 95 kb/s (default)
y
File '/dev/null' already exists. Overwrite ? [y/N] Stream mapping:
Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
Stream #0:1 -> #0:1 (aac (native) -> aac (native))
encoder : Lavf57.71.100
Stream #0:0(und): Video: h264 (libx264) ([33][0][0][0] / 0x0021), yuv420p(progressive), 640x360 [SAR 1:1 DAR 16:9], q=-1--1, 23.98 fps, 24k tbn, 23.98 tbc (default)
encoder : Lavc57.89.100 libx264
Stream #0:1(und): Audio: aac (LC) ([64][0][0][0] / 0x0040), 44100 Hz, stereo, fltp, 128 kb/s (default)
encoder : Lavc57.89.100 aac
Ok, so we can see ffmpeg
chooses libx264
and aac
encoders for this video; then we can enter this in for melt
:
melt inputvid.mp4 in=7235 out=7349 -consumer avformat:cut.mp4 acodec=aac vcodec=libx264
.... and melt
will cut with the piece between frames 7235 and 7349 into a new file, cut.mp4
. Then to check if cut.mp4
loops correctly, use melt
again to play it back twice - and play it to an SDL window:
melt cut.mp4 cut.mp4 -consumer sdl
... and here is what ffmpeg
sees for this file:
ffmpeg -i cut.mp4 -f mp4 /dev/null 2>&1 | grep 'Stream\|encoder' encoder : Lavf54.20.4
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 640x360 [SAR 1:1 DAR 16:9], 526 kb/s, 23.98 fps, 23.98 tbr, 24k tbn, 47.95 tbc (default)
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 182 kb/s (default)
y
File '/dev/null' already exists. Overwrite ? [y/N] Stream mapping:
Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
Stream #0:1 -> #0:1 (aac (native) -> aac (native))
encoder : Lavf57.71.100
Stream #0:0(und): Video: h264 (libx264) ([33][0][0][0] / 0x0021), yuv420p, 640x360 [SAR 1:1 DAR 16:9], q=-1--1, 23.98 fps, 24k tbn, 23.98 tbc (default)
encoder : Lavc57.89.100 libx264
Stream #0:1(und): Audio: aac (LC) ([64][0][0][0] / 0x0040), 48000 Hz, stereo, fltp, 128 kb/s (default)
encoder : Lavc57.89.100 aac
The video encoding settings for cut.mp4
seem to be identical to inputvid.mp4
except video bitrate changed from 389 kb/s to 526 kb/s, and also the audio encoding settings are nearly the same, except the sampling rate changed from 44100 to 48000 Hz; though that can be regulated with:
melt inputvid.mp4 in=7235 out=7349 -consumer avformat:cut.mp4 acodec=aac ar=44100 ab=95k vcodec=libx264 vb=389k
... however, even with this, the final video bitrate for me ends up 337 kb/s. Still, the cuts loop fine (and that includes audio) when played in a loop, so I guess this is indeed frame-accurate...