1103

I tried to cut the video using the start and end time of the video by using the following command:

ffmpeg -ss 00:00:03 -t 00:00:08 -i movie.mp4 -acodec copy -vcodec copy -async 1 cut.mp4 

By using the above command I want to cut the video from 00:00:03 to 00:00:08. But it is not cutting the video between those times instead of that it is cutting the video with first 11 seconds.

2
  • 4
    Your full, uncut ffmpeg console output is missing. Please always include this when asking. Thanks. Commented Aug 26, 2013 at 12:55
  • 13
    You should either use -t 00:00:05 or -to 00:00:08 in order to cut the video from 00:00:03 to 00:00:08. Check out the documentation. Commented Dec 29, 2018 at 4:08

14 Answers 14

1612
+150

The fastest and best ffmpeg-based method I have figured out is:

ffmpeg -ss 00:01:00 -to 00:02:00 -i input.mp4 -c copy output.mp4 

This command trims your video in seconds!


Explanation of the command:

Command Explanation
-i This specifies the input file. In that case, it is (input.mp4).
-ss Used with -i, this seeks in the input file (input.mp4) to position.
00:01:00 This is the time your trimmed video will start with.
-to The next argument after -to specifies the position (counted from the start of the entire, original video) at which FFmpeg should stop reading the data (00:02:00).
00:02:00 This is the time your trimmed video will end with.
-c copy This is an option to trim via stream copy. (NB: Very fast)

The timing format is hh:mm:ss.


Please note that the previous highly upvoted answer forces a reencode, and so the trim would be extremely slow (but that is necessary if you want to cut the video at anything other than keyframes, e.g. with sub-second accuracy). For more information, look at "Seeking" on the FFmpeg wiki.

Sign up to request clarification or add additional context in comments.

24 Comments

You may still suffer from a lack of keyframes with this though. I took your approach to cutting, then re-encoded as per the accepted answer to make sure I minimised the amount of time I needed to spend re-encoding.
As @pelson says, this will cut off key frames and leave the first few seconds blank (if the cutting time is between key frames). Interestingly, changing the parameter order solves the problem: ffmpeg -ss 00:01:00 -to 00:02:00 -i input.mp4 -ss 00:01:00 -to 00:02:00 -c copy output.mp4. Now, however, skipping is broken and the end time is not correct (in my case).
According to FFmpeg, input seeking is now both fast and accurate, is there any specific reason you use output seeking instead of input seeking?
You should use this answer if you don't need to trim at an exact milisecond, thus it will select the closest keyframe available. That's why it's so fast, it selects the closest keyframes and takes the snippet out without the need of reencoding. However, if you do need a precise time, taking the nearest keyframe isn't enough, because it certainly won't match exactly. The only solution available is to reencode the video, and that's why it's so slow. That's the only solution, though, if you do need this. That's why the other answer should be the accepted one, as that's what was OP's issue about.
You probably want -ss and -to before -i, example: ffmpeg -ss aa:bb:cc -to xx:yy:zz -i input.mp4 -c copy output.mp4. Otherwise the -to value ends up being a duration instead of the end time from the original video. Omitting the -c copy will make it slower and more accurate by re-encoding, but still faster than if the -ss and -to are specified after -i, since that case means to trim after having to process the whole input file.
|
865

You probably do not have a keyframe at the 3 second mark. Because non-keyframes encode differences from other frames, they require all of the data starting with the previous keyframe.

With the mp4 container it is possible to cut at a non-keyframe without re-encoding using an edit list. In other words, if the closest keyframe before 3s is at 0s then it will copy the video starting at 0s and use an edit list to tell the player to start playing 3 seconds in.

If you are using the latest ffmpeg from git master it will do this using an edit list when invoked using the command that you provided. If this is not working for you then you are probably either using an older version of ffmpeg, or your player does not support edit lists. Some players will ignore the edit list and always play all of the media in the file from beginning to end.

If you want to cut precisely starting at a non-keyframe and want it to play starting at the desired point on a player that does not support edit lists, or want to ensure that the cut portion is not actually in the output file (for example if it contains confidential information), then you can do that by re-encoding so that there will be a keyframe precisely at the desired start time. Re-encoding is the default if you do not specify copy. For example:

ffmpeg -i movie.mp4 -ss 00:00:03 -t 00:00:08 -async 1 cut.mp4 

When re-encoding you may also wish to include additional quality-related options or a particular AAC encoder. For details, see ffmpeg's x264 Encoding Guide for video and AAC Encoding Guide for audio.

Also, the -t option specifies a duration, not an end time. The above command will encode 8s of video starting at 3s. To start at 3s and end at 8s use -t 5. If you are using a current version of ffmpeg you can also replace -t with -to in the above command to end at the specified time.

15 Comments

Is there a shortcut to specify till the end of the video?
@JikkuJose: Omit the -t/-to and its argument to continue to the end.
Note: it is faster to supply -ss BEFORE the input file (before -i), because that way ffmpeg will skip directly to the location. However, that will also set the time "zero" to that location, meaning that -to will point to the wrong time. Source: trac.ffmpeg.org/wiki/Seeking
I get audio and video sync issue after using this ffmpeg -i movie.mp4 -ss 00:00:03 -t 00:00:08 -async 1 cut.mp4 , though the original file has no such issue.
@user2002522: Try without the -async 1.
|
122
ffmpeg -i movie.mp4 -ss 00:00:03 -t 00:00:08 -async 1 -c copy cut.mp4 

Use -c copy for make in instantly. In that case ffmpeg will not re-encode video, just will cut to according size.

10 Comments

of -c copy works great, but only if I remove -async 1
This ends up failing on write due to invalid headers in the case of different input and output containers e.g. .mkv input and .mp4 output.
Alright, to convert .mkv to .mp4 you have to use another command: ffmpeg -i movie.mkv -vcodec copy -acodec copy converted_movie.mp4 and vise versa
What are the advantages of re-encoding? Is it of any use if we just want smaller video in the same format?
I have same question as @Harsha. What does -c copy in layman's terms?
|
59

Here's what I use and will only take a few seconds to run:

ffmpeg -i input.mp4 -ss 01:19:27 -to 02:18:51 -c:v copy -c:a copy output.mp4 

Reference: Trim video files using FFmpeg by Alexander Refsum Jensenius.


Generated mp4 files could also be used in iMovie. More info related to get the full duration using get_duration(input_video) model.

If you want to concatenate multiple cut scenes you can use following Python script:

#!/usr/bin/env python3 import subprocess def get_duration(input_video): cmd = ["ffprobe", "-i", input_video, "-show_entries", "format=duration", "-v", "quiet", "-sexagesimal", "-of", "csv=p=0"] return subprocess.check_output(cmd).decode("utf-8").strip() def main(): name = "input.mkv" times = [] times.append(["00:00:00", "00:00:10"]) times.append(["00:06:00", "00:07:00"]) # times = [["00:00:00", get_duration(name)]] if len(times) == 1: time = times[0] cmd = ["ffmpeg", "-i", name, "-ss", time[0], "-to", time[1], "-c:v", "copy", "-c:a", "copy", "output.mp4"] subprocess.check_output(cmd) else: open('concatenate.txt', 'w').close() for idx, time in enumerate(times): output_filename = f"output{idx}.mp4" cmd = ["ffmpeg", "-i", name, "-ss", time[0], "-to", time[1], "-c:v", "copy", "-c:a", "copy", output_filename] subprocess.check_output(cmd) with open("concatenate.txt", "a") as myfile: myfile.write(f"file {output_filename}\n") cmd = ["ffmpeg", "-f", "concat", "-i", "concatenate.txt", "-c", "copy", "output.mp4"] output = subprocess.check_output(cmd).decode("utf-8").strip() print(output) if __name__ == "__main__": main() 

Example script will cut and merge scenes in between 00:00:00 - 00:00:10 and 00:06:00 - 00:07:00.


If you want to cut the complete video (in case if you want to convert mkv format into mp4) just uncomment the following line:

# times = [["00:00:00", get_duration(name)]] 

6 Comments

Couldn't this just use "-c" with "copy" instead of splitting out -c:v and -c:a?
I don't know why but I'm getting error with this code
this is slow. the input file isn't being seeked, it's being processed and discarded. -ss as an output option = slow. see my updated answer.
I'm getting the video cutted but for first seconds the frames just freeze
@PabloDíaz The video you are using might be faulty. Can you give it a try with a different video uncommenting # times = [["00:00:00", get_duration(name)]] and see if the generated output.mp4 file is fine
|
39
 ffmpeg -i movie.mp4 -vf trim=3:8 cut.mp4 

Drop everything except from second 3 to second 8 (also does a re-encode of everything)

4 Comments

I get the first 8 seconds with this, with the first 3 seconds frozen on the first frame. Fixed by chaining setpts=PTS-STARTPTS filter after the trim filter.
This says "The encoder 'aac' is experimental but experimental codecs are not enabled, add '-strict -2' if you want to use it." Adding -strict -2 does not help. The result is a zero length file.
@Ivan, did you try adding -strict -2 before cut.mp4 (as opposed to the end of the command line)?
THis is super slow
31

new answer (fast)

You can make bash do the math for you, and it works with milliseconds.

toSeconds() { awk -F: 'NF==3 { print ($1 * 3600) + ($2 * 60) + $3 } NF==2 { print ($1 * 60) + $2 } NF==1 { print 0 + $1 }' <<< $1 } StartSeconds=$(toSeconds "45.5") EndSeconds=$(toSeconds "1:00.5") Duration=$(bc <<< "(${EndSeconds} + 0.01) - ${StartSeconds}" | awk '{ printf "%.4f", $0 }') ffmpeg -ss $StartSeconds -i input.mpg -t $Duration output.mpg 

This, like the old answer, will produce a 15 second clip. This method is ideal even when clipping from deep within a large file because seeking isn't disabled, unlike the old answer. And yes, I've verified it's frame perfect.

NOTE: The start-time is INCLUSIVE and the end-time is normally EXCLUSIVE, hence the +0.01, to make it inclusive.

If you use mpv you can enable millisecond timecodes in the OSD with --osd-fractions


old answer with explanation (slow)

To cut based on start and end time from the source video and avoid having to do math, specify the end time as the input option and the start time as the output option.

ffmpeg -t 1:00 -i input.mpg -ss 45 output.mpg 

This will produce a 15 second cut from 0:45 to 1:00.

This is because when -ss is given as an output option, the discarded time is still included in the total time read from the input, which -t uses to know when to stop. Whereas if -ss is given as an input option, the start time is seeked and not counted, which is where the confusion comes from.

It's slower than seeking since the omitted segment is still processed before being discarded, but this is the only way to do it as far as I know. If you're clipping from deep within a large file, it's more prudent to just do the math and use -ss for the input.

4 Comments

slow, but the only thing worked for me (without loosing frames from other answers)
Excelent. The only solution to not calculate duration but provide start and end to the ffmpeg
As of Aug 3, 2022, the old (slow) answer is the only one that works for me. The other answers produce a video where the timestamps are messed up and when I try to concatenate the parts, I get a totally messed up video.
These days you can now use timestamps of like 00:00:00 instead :)
7

Use -to instead of -t: -to specifies the end time, -t specifies the duration

1 Comment

4

My setup:

$ ffmpeg ffmpeg version 4.4 Copyright (c) 2000-2021 the FFmpeg developers built with Apple clang version 12.0.5 (clang-1205.0.22.9) 

I found that the order of the switches matters. If -i is not between -ss and -t then I got about 3-seconds leading blank in the result.

So whatever additional switches you use, make sure you got those three switches in the correct order, like this

ffmpeg -ss 00:00:03 -i movie.mp4 -t 00:00:08 cut.mp4 

Comments

3

I was interested in cut times based on milliseconds, and noticed that seconds (and fractions of them) can be specified for the cut as follows:

ffmpeg -i input.mp4 -ss 17.5 -to 20.5 output.mp4 

For a duration rather than an end time, use -t instead of -to:

ffmpeg -i input.mp4 -ss 17.5 -t 3 output.mp4 

Comments

1

First, a video should be coded using ffvhuff encoder so that the video could be cut to exactly at the start and end time. Normally, with other commands given above, it may be possible that the video is not cut to the specific duration as not every frame is an intra coded frame.

ffmpeg -ss 00:03:00 -i input_file.mkv -t 00:01:19 -c:v ffvhuff -pix_fmt yuv420p -y output_file.mkv 

2 Comments

For cases like not landing on an intra coded frame, would the idea be to seek backward to an iframe or something?
Yes, you could do it. But you need to do additional analysis like finding out the intra-period. So, it is better to encode a video using FFvhuff and then cut it.
0

feel free to use this tool https://github.com/rooty0/ffmpeg_video_cutter I wrote awhile ago Pretty much that's cli front-end for ffmpeg... you just need to create a yaml what you want to cut out... something like this

cut_method: delete # we're going to delete following video fragments from a video timeframe: - from: start # waiting for people to join the conference to: 4m - from: 10m11s # awkward silence to: 15m50s - from: 30m5s # Off-Topic Discussion to: end 

and then just run a tool to get result

1 Comment

Going by the GitHub account profile, it looks like this might be a tool that you've created yourself. If this is the case, you must disclose this in your answer, otherwise this can be considered spam.
0

Using ffmpeg-python library i did it this way.. but you must uninstall ffmpeg before installing ffmpeg-library.. see my github for more details. https://github.com/johntsunami/ffmpeg-python-video-trimmer/blob/main/trimmer.py

sys.path.append(r"C:\Users\johnc\Desktop\ffmpeg\bin") stream = ffmpeg.input(r"C:/Users/johnc/Desktop/krystal.mp4",ss=57, t=55) # ss is start time, t IS DURATION ## SEPARATES AUDIO AND VIDEO INDEPENDENTLY BECAUSE SOMETIMES IT DOESNT HAVE AUDIO UNLESS YOU DO THIS> audio = stream.audio video = stream.video #.trim(start=60,end=115).filter('setpts','PTS-STARTPTS') ## CURRENTLY IS GOOD BUT DOESNT CUT DURATION AFTER 55 seconds #overwrite_output overwrites it automatically if i dont want it just use output stream = ffmpeg.output(audio, video, r'C:\Users\johnc\Desktop\output.mp4') ffmpeg.run(stream) 

Comments

0

I created a pair of shell scripts to handle my transcoding needs, including clipping out advertisements. I wanted to do millisecond precise clipping, and don't mind paying the price of reencoding.

  • transcode.sh Handles transcoding, including extracting clips.
  • ff-segmenter.sh A front-end where you can specify where the advertisements (or segments) start and stop.

Put both scripts on your path... and...

# Cut out everything from (3min 12.345s to 4min 1s) and (1hour 48min 0.2s to 1hour 52min 43.0s) ff-segmenter.sh -i "input.mp4" -o "output.mp4" -x -s 3:12.345-4:01 -s 1:48:00.2-1:52:43 

Without the -x switch, the selections are inverted. So everything outside the cut list is dropped. The -h switch explains.

Comments

-7

Even though I'm 6 years late, but I think all the answers above didn't properly address the question @kalai is asking. The bash script below will process a text file in the following format:

URL | start_time | end_time | filename 

for example

https://www.youtube.com/watch?v=iUDURCrvrMI|00:02:02|00:03:41|1 

and loop through the file, downloads the file that youtube-dl supports, calculating duration between start_time and end_time and passing it to ffmpeg, since -t is actually the duration, not the real end_time

Hit me up if you have any question.

 for i in $(<video.txt); do URL=`echo $i | cut -d "|" -f 1`; START=`echo $i | cut -d "|" -f 2`; END=`echo $i | cut -d "|" -f 3`; FILE=`echo $i | cut -d "|" -f 4`; SEC1=`echo $START | sed 's/^/((/; s/:/)*60+/g' | bc` SEC2=`echo $END | sed 's/^/((/; s/:/)*60+/g' | bc` DIFFSEC=`expr ${SEC2} - ${SEC1}` ffmpeg $(youtube-dl -g $URL | sed "s/.*/-ss $START -i &/") -t $DIFFSEC -c copy $FILE".mkv"; ffmpeg -i $FILE".mkv" -f mp3 -ab 192000 -vn $FILE".mp3"; rm $FILE".mkv"; done; 

2 Comments

This outputs only an mp3 audio file and NOT a video.
I suppose you can update the script to output video format instead of audio. If I remember correctly, about 3 years ago I did it just for audio.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.