There is often a need in the open source or active developer community to publish large video segments online. (Meet-up videos, campouts, tech talks...) Being that I am a developer and not a videographer I have no desire to fork out the extra scratch on a premium Vimeo account. How then do I take a 12.5 GB (1:20:00) MPEG tech talk video and slice it into 00:10:00 segments for easy uploading to video sharing sites?
11 Answers
Here is the one line solution:
ffmpeg -i input.mp4 -c copy -map 0 -segment_time 00:20:00 -f segment output%03d.mp4
Please note that this does not give you accurate splits, but should fit your needs. It will instead cut at the first frame after the time specified after segment_time, in the code above it would be after the 20 minute mark.
If you find that only the first chunk is playable, try adding -reset_timestamps 1 as mentioned in the comments.
ffmpeg -i input.mp4 -c copy -map 0 -segment_time 00:20:00 -f segment -reset_timestamps 1 output%03d.mp4
-
15It actually gives you very accurate splits, if you value video quality. Rather than splitting based on a particular time, it splits on the nearest keyframe following the requested time, so each new segment always starts with a keyframe.Malvineous– Malvineous2017-02-25 06:51:25 +00:00Commented Feb 25, 2017 at 6:51
-
4what are the units? 8s? 8min? 8h?user1133275– user11332752017-03-20 20:58:59 +00:00Commented Mar 20, 2017 at 20:58
-
1
-
7On Mac, I found that this resulted in N output video chunks but only the 1st of them was a valid, viewable MP4. The other N-1 chunks were blank video (all black) with no audio. To make it work, I needed to add the reset_timestamps flag like so: ffmpeg -i input.mp4 -c copy -map 0 -segment_time 8 -f segment -reset_timestamps 1 output%03d.mp4.jarmod– jarmod2017-06-05 15:32:48 +00:00Commented Jun 5, 2017 at 15:32
-
30found that adding
-reset_timestamps 1fixes the issue for mejlarsch– jlarsch2017-10-30 08:27:51 +00:00Commented Oct 30, 2017 at 8:27
$ ffmpeg -i source-file.foo -ss 0 -t 600 first-10-min.m4v
$ ffmpeg -i source-file.foo -ss 600 -t 600 second-10-min.m4v
$ ffmpeg -i source-file.foo -ss 1200 -t 600 third-10-min.m4v
...
Wrapping this up into a script to do it in a loop wouldn't be hard.
Beware that if you try to calculate the number of iterations based on the duration output from an ffprobe call that this is estimated from the average bit rate at the start of the clip and the clip's file size unless you give the -count_frames argument, which slows its operation considerably.
Another thing to be aware of is that the position of the -ss option on the command line matters. Where I have it now is slow but accurate. The linked article describes fast-but-inaccurate and slower-but-still-accurate alternative formulations. You pay for the latter with a certain complexity.
All that aside, I don't think you really want to be cutting at exactly 10 minutes for each clip. That will put cuts right in the middle of sentences, even words. I think you should be using a video editor or player to find natural cut points just shy of 10 minutes apart.
Assuming your file is in a format that YouTube can accept directly, you don't have to reencode to get segments. Just pass the natural cut point offsets to ffmpeg, telling it to pass the encoded A/V through untouched by using the "copy" codec:
$ ffmpeg -i source.m4v -ss 0 -t 593.3 -c copy part1.m4v
$ ffmpeg -i source.m4v -ss 593.3 -t 551.64 -c copy part2.m4v
$ ffmpeg -i source.m4v -ss 1144.94 -t 581.25 -c copy part3.m4v
...
The -c copy argument tells it to copy all input streams (audio, video, and potentially others, such as subtitles) into the output as-is. For simple A/V programs, it is equivalent to the more verbose flags -c:v copy -c:a copy or the old-style flags -vcodec copy -acodec copy. You would use the more verbose style when you want to copy only one of the streams, but re-encode the other. For example, many years ago there was a common practice with QuickTime files to compress the video with H.264 video but leave the audio as uncompressed PCM; if you ran across such a file today, you could modernize it with -c:v copy -c:a aac to reprocess just the audio stream, leaving the video untouched.
The start point for every command above after the first is the previous command's start point plus the previous command's duration.
-
2"cutting at exactly 10 minutes for each clip" is a good point.Chris– Chris2010-09-07 11:42:11 +00:00Commented Sep 7, 2010 at 11:42
-
maybe by using the -show_packets param you can make it more accurate.rogerdpack– rogerdpack2011-06-13 21:36:31 +00:00Commented Jun 13, 2011 at 21:36
-
how to put above in a loop?kRazzy R– kRazzy R2017-12-06 19:32:20 +00:00Commented Dec 6, 2017 at 19:32
-
i Use this cmd ya it split into right but Now the video and audio are not on SYNC any helpSunil Chaudhary– Sunil Chaudhary2018-03-16 06:08:20 +00:00Commented Mar 16, 2018 at 6:08
-
@SunilChaudhary: A/V file formats are complex and not all software implements them the same way, so that the information
ffmpegneeds to maintain synchronization between the audio and video streams might not be present in the source file. If this is happening to you, there are more complicated methods of splitting that avoid the problem.Warren Young– Warren Young2018-03-16 21:32:28 +00:00Commented Mar 16, 2018 at 21:32
An Alternate more readable way would be
ffmpeg -i input.mp4 -ss 00:00:00 -to 00:10:00 -c copy output1.mp4
ffmpeg -i input.mp4 -ss 00:10:00 -to 00:20:00 -c copy output2.mp4
/**
* -i input file
* -ss start time in seconds or in hh:mm:ss
* -to end time in seconds or in hh:mm:ss
* -c codec to use
*/
Here's the source and list of Commonly used FFmpeg commands.
Just use what is built into ffmpeg to do exactly this.
ffmpeg -i invid.mp4 -threads 3 \
-vcodec copy -f segment -segment_time 10:00 \
-reset_timestamps 1 \
cam_out_h264_%02d.mp4
This will split it into roughly 10-minute chunks, split at the relevant keyframes, and will output to the files
cam_out_h264_01.mp4, cam_out_h264_02.mp4, etc.
-
This is the best solution! The only issue is that the timestamps are the same as the old timestamps, so at file 0004 it starts at 4 * segment_time seconds.Jasper– Jasper2020-05-24 15:58:29 +00:00Commented May 24, 2020 at 15:58
-
6@Jasper you can use the
--reset_timestampsflag to undo thatJohn Allard– John Allard2021-07-06 01:03:45 +00:00Commented Jul 6, 2021 at 1:03
If you want to create really same Chunks must force ffmpeg to create i-frame on the every chunks' first frame so you can use this command for create 0.5 second chunk.
ffmpeg -hide_banner -err_detect ignore_err -i input.mp4 -r 24 -codec:v libx264 -vsync 1 -codec:a aac -ac 2 -ar 48k -f segment -preset fast -segment_format mpegts -segment_time 0.5 -force_key_frames "expr: gte(t, n_forced * 0.5)" out%d.mkv
-
1@StephanAhlf: Usually you want to avoid re-encoding, both for quality and CPU usage. It's only worth doing this if you really need exact split points and there aren't already I-frames there in the original. This also has a bunch of arbitrary choices for options, like forcing a frame-rate of exactly 24 FPS, not 24000/1001, and libx264's defaults of CRF23, plus a lower-quality preset
fast, and FFmpeg's low-qualityaacbuilt-in encoder with 48k bitrate.Peter Cordes– Peter Cordes2022-01-08 15:12:37 +00:00Commented Jan 8, 2022 at 15:12 -
2there's to much in this command without explanation for being a good answerDaniel Alder– Daniel Alder2022-06-11 23:20:37 +00:00Commented Jun 11, 2022 at 23:20
Faced the same problem earlier and put together a simple Python script to do just that (using FFMpeg). Available here: https://github.com/c0decracker/video-splitter, and pasted below:
#!/usr/bin/env python
import subprocess
import re
import math
from optparse import OptionParser
length_regexp = 'Duration: (\d{2}):(\d{2}):(\d{2})\.\d+,'
re_length = re.compile(length_regexp)
def main():
(filename, split_length) = parse_options()
if split_length <= 0:
print "Split length can't be 0"
raise SystemExit
output = subprocess.Popen("ffmpeg -i '"+filename+"' 2>&1 | grep 'Duration'",
shell = True,
stdout = subprocess.PIPE
).stdout.read()
print output
matches = re_length.search(output)
if matches:
video_length = int(matches.group(1)) * 3600 + \
int(matches.group(2)) * 60 + \
int(matches.group(3))
print "Video length in seconds: "+str(video_length)
else:
print "Can't determine video length."
raise SystemExit
split_count = int(math.ceil(video_length/float(split_length)))
if(split_count == 1):
print "Video length is less then the target split length."
raise SystemExit
split_cmd = "ffmpeg -i '"+filename+"' -vcodec copy "
for n in range(0, split_count):
split_str = ""
if n == 0:
split_start = 0
else:
split_start = split_length * n
split_str += " -ss "+str(split_start)+" -t "+str(split_length) + \
" '"+filename[:-4] + "-" + str(n) + "." + filename[-3:] + \
"'"
print "About to run: "+split_cmd+split_str
output = subprocess.Popen(split_cmd+split_str, shell = True, stdout =
subprocess.PIPE).stdout.read()
def parse_options():
parser = OptionParser()
parser.add_option("-f", "--file",
dest = "filename",
help = "file to split, for example sample.avi",
type = "string",
action = "store"
)
parser.add_option("-s", "--split-size",
dest = "split_size",
help = "split or chunk size in seconds, for example 10",
type = "int",
action = "store"
)
(options, args) = parser.parse_args()
if options.filename and options.split_size:
return (options.filename, options.split_size)
else:
parser.print_help()
raise SystemExit
if __name__ == '__main__':
try:
main()
except Exception, e:
print "Exception occured running main():"
print str(e)
Note the exact punctuation of the alternative format is -ss mm:ss.xxx. I struggled for hours trying to use the intuitive-but-wrong mm:ss:xx to no avail.
$ man ffmpeg | grep -C1 position
-ss position
Seek to given time position in seconds. "hh:mm:ss[.xxx]" syntax is also supported.
-
1For the record,
01:00:00is 1 hour. You can have fractional sections as part of the h:m:s.Peter Cordes– Peter Cordes2022-01-08 15:15:51 +00:00Commented Jan 8, 2022 at 15:15
ffmpeg -i input.mp4 -force_key_frames expr:gte(t,n_forced*600) -f segment -segment_time 600 -reset_timestamps 1 -map 0 -segment_format_options movflags=+faststart output_%03d.mp4
To split the video more accurately, you can use the -force_key_frames option to specify keyframes at the desired split points.
-f segment -segment_time 600sets the segment lengthgte(t, n_forced*600): This is the expression itself, where:trepresents the current time of the video frame being processed.n_forcedrepresents the number of keyframes that have been forced so far.*600multiplies n_forced by 600, which is the time interval in seconds.
#!/bin/bash
if [ "X$1" == "X" ]; then
echo "No file name for split, exiting ..."
exit 1
fi
if [ ! -f "$1" ]; then
echo "The file '$1' doesn't exist. exiting ..."
exit 1
fi
duration=$(ffmpeg -i "$1" 2>&1 | grep Duration | sed 's/^.*Duration: \(.*\)\..., start.*$/\1/' | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }') #'
split_time=${split_time:-55}
time=0
part=1
filename=${file%%.*}
postfix=${file##*.}
while [ ${time} -le ${duration} ]; do
echo ffmpeg -i "$1" -vcodec copy -ss ${time} -t ${split_time} "${filename}-${part}.${postfix}"
(( part++ ))
(( time = time + split_time ))
done
One line solution
for i in {3..100}; do ffmpeg -i input.mp4 -ss $(($((i))*600)) -t $(($((i+1))*600)) $i.mp4 ; done;
-
2Welcome to the site, and thank your for your contribution. Please note that it is not necessary to have nested
$((...))constructs,$(( i*600 ))or$(( (i+1)*600 ))are totally fine. Also, it is good practice to double-quote parameter expansions, as in"$i.mp4"(although in your example there is little danger of unwanted word-splitting).AdminBee– AdminBee2020-09-24 07:13:35 +00:00Commented Sep 24, 2020 at 7:13
Please have a look into below command
fun trim(): Array<String?>{
val cmdList = ArrayList<String>()
var cmds: Array<String?>? = null
try{
cmdList.add("-y")
cmdList.add("-ss")
cmdList.add(startduration.toString())
cmdList.add("-t")
cmdList.add(endduration.toString())
cmdList.add("-i")
cmdList.add(sourcevideopath)
cmdList.add("-vcodec")
cmdList.add("copy")
cmdList.add("-movflags")
cmdList.add("faststart")
cmdList.add("-preset")
cmdList.add("ultrafast")
cmdList.add(outputvideopath)
cmds = arrayOfNulls<String>(cmdList.size)
cmdList.toArray(cmds)
}catch (e:Exception){
e.printStackTrace()
}
return cmds!!
}
-
3Welcome to the site, and thank you for your contribution. You may want to add some explanation on what this command does and how to use it to solve the OPs problem.AdminBee– AdminBee2021-06-23 11:25:41 +00:00Commented Jun 23, 2021 at 11:25
-
3What language is that, and why did you choose it instead of an ordinary shell for running commands?Toby Speight– Toby Speight2021-06-24 10:29:32 +00:00Commented Jun 24, 2021 at 10:29
-
@TobySpeight looks like Kotlin, and it's unusual because all it does is create an argv for launching ffmpeg and doesn't launch it itself in a subprocess or other such mechanismmoshbear– moshbear2021-07-12 06:28:15 +00:00Commented Jul 12, 2021 at 6:28