How to Cut a Clip from Video Without Quality Loss

TL;DR: ready to use scripts are at the end of the post.

Sometimes when watching a movie or an episode of a series there comes a scene which makes a great impression on you, so great that you decide to share it with your friends. Sharing a file is not a problem in this day and age, yet surprisingly the problem is to cut a clip in the first place without losing quality from double compression. It's hard to find an easy to use tool able to do this relatively simple task so I've written a script just for this purpose.

Solutions

The first ideas that come to mind:

  1. Record it. Whether by using a camera or a screen capturing tool, the process itself is annoying, you waste a lot of time by watching the scene again, the cut is far from perfect and the end result is usually horrible. Bad solution.
  2. Use a converter or video editor.

The second solution sounds good if you already have a video editor or converter lying around which actually can cut a clip without losing quality and adding watermark on top of it. Unfortunately, it's hard to find a free tool which can do that. You may be more lucky with commercial ones but you shouldn't need them in the first place for such a simple job.

ffmpeg comes to the rescue, a neat command line program for processing audio and video. "What? No GUI? How is that supposed to be simple?!", you ask. Well if you expected some fancy looking application from this post then I'm sorry but you're not gonna get that. I find it simple enough to use for my purposes.

The obvious way

After reading the docs I managed to write the following command which indeed works as intended:

ffmpeg -ss 0:10 -i "not_cut.mp4" -codec copy -t 0:05 "cut.mp4"
  • -ss [time] means "start processing no later than this point in time". Notice that ffmpeg will use earlier time point if needed, since video files are compressed in such a way that the next frame depends on some previous ones, so it sometimes has to significantly go back in time but the clip cannot get more perfect than this.
  • -i [input file] is obvious.
  • -codec copy the magic switch that we are looking for! Don't compress, just copy this fragment.
  • -t [time] now here's where things get a little annoying. This switch tells how long the final clip should last not when to end the clip.

Note. Order of parameters is important for ffmpeg. Everything before the -i switch is used only for the input file, everything after regards only the output file.

Here's a test result:


Now, normal people would probably be happy enough and move on. However, I'm not fully happy with this solution since it requires me to calculate how long the clip needs to be (and I hate subtracting time stamps). I would like to just type "from here to here please, no fries with that, thanks".

What is more surprising, the above command actually can create some weird graphical artifacts in the final clip and I'm not sure why. The final version doesn't have such problems for some reason at least in my experience in using it.

Replacing the -t switch with -to switch

I thought it would be weird if ffmpeg didn't have a -to switch! Let's use it.

ffmpeg -ss 0:10 -i "not_cut.mp4" -codec copy -to 0:15 "cut.mp4"

Of course, it can never be that simple, can it. The result lasts 15 seconds and it doesn't require high level math to figure out that it should only last 5. It does what it should:

  1. Processes input from time 0:10.
  2. Processes output to time 0:15.

What's the problem here? Well, the black box which gives input for step 2 is resetting the time stamps in the video. So the point 0:10 is now somewhere around 0:00. Therefore, the switch in this case works the same as -t.

Second try

We can tell ffmpeg to copy the timestamps by using -copyts before -to.

ffmpeg -ss 0:10 -i "not_cut.mp4" -codec copy -copyts -to 0:15 "cut.mp4"

The above clip has clearly problems with measuring time. It indeed lasts 5 seconds as we intended but it thinks it starts from 0:10 and ends at 0:15 instead of 0:00 to 0:05. Most video players are not used to this.

Last try

The fix should be simple: just shift the timestamps so that the first frame is at 0:00 but do that after cutting the input to -to. Turns out there is also a switch for that: -avoid_negative_ts make_zero.

ffmpeg -ss 0:10 -i "not_cut.mp4" -codec copy -copyts -to 0:15 -avoid_negative_ts make_zero "cut.mp4" 

Perfection.

General command

ffmpeg -hide_banner -ss $begin -i "$input" -codec copy -copyts -to $end -avoid_negative_ts make_zero "$output"

Names starting with $ of course are meant to be replaced with proper values.

-hide_banner switch hides some useless info ffmpeg prints in the terminal.

Make it even easier

Let's be honest, I'm going to forget what this command looks like tomorrow. You probably will to. Also, I'm lazy and would like the script to upload the video to the cloud immediately, preferably putting the sharing url in my clibboard already.

Perfect time to review Bash and Batch knowledge and write a proper script. Unfortunately, without the sharing url feature due to some limitations.

Obviously, the scripts below require ffmpeg to be in a folder in the system path variable.

For Linux

For Windows

Usage

ffmpeg-cut {input} {from} {to} [{output}]
  • Time must be in format accepted by ffmpeg.
  • The output path is optional and defaults to a folder in a Dropbox directory. The script is easy to modify if you want the change the default.

Example (Linux):

./ffmpeg-cut.sh "movie.mp4" 0:10 0:15 "cut.mp4"

Example (Windows):

ffmpeg-cut.bat "movie.mp4" 0:10 0:15 "cut.mp4"

 

Clips embeded in the post come from a short movie Big Buck Bunny (c) copyright 2008, Blender Foundation / www.bigbuckbunny.org

Comments