Answers:
$ 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
...
将其包装到脚本中以循环执行将不会很困难。
请注意,如果您尝试根据ffprobe
调用输出的持续时间来计算迭代次数,则根据片段开始时的平均比特率和片段的文件大小来估算该调用的持续时间,除非您指定-count_frames
参数,否则会大大降低其操作速度。
要注意的另一件事是-ss
命令行中选项的位置很重要。我现在拥有的位置缓慢但准确。此答案的第一个版本提供了快速但不准确的替代方法。链接的文章还介绍了一种最快速但仍然准确的替代方法,您需要付出一些复杂性才能支付费用。
撇开所有这些,我不认为您真的想在每个剪辑上恰好在10分钟时进行剪辑。这样就可以在句子甚至单词的中间插入剪切。我认为您应该使用视频编辑器或播放器来查找相距不到10分钟的自然切入点。
假设您的文件采用YouTube可以直接接受的格式,则无需重新编码即可获取细分。只需将自然的切点偏移量传递到ffmpeg
,告诉它通过使用“复制”编解码器将编码的A / V保持原样通过:
$ 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
...
该-c copy
参数告诉它将所有输入流(音频,视频以及可能的其他流,例如字幕)原样复制到输出中。对于简单的A / V程序,它等效于更详细的标志-c:v copy -c:a copy
或旧式标志-vcodec copy -acodec copy
。当您只想复制其中一个流,而对另一个流重新编码时,可以使用更详细的样式。例如,很多年前,使用QuickTime文件的一种常见做法是使用H.264视频压缩视频,而将音频保留为未压缩的PCM;如果您今天遇到了这样的文件,则可以对其进行现代化-c:v copy -c:a aac
处理,以仅处理音频流,而使视频保持不变。
上面第一个命令之后的每个命令的起点是前一个命令的起点加上前一个命令的持续时间。
ffmpeg -i input.mp4 -c copy -map 0 -segment_time 00:20:00 -f segment output%03d.mp4
请注意,这不会给您准确的拆分,但应满足您的需求。相反,它将在指定的时间之后的第一帧开始剪切segment_time
,在上面的代码中将在20分钟标记之后。
如果发现只有第一个块是可播放的,请尝试添加-reset_timestamps 1
注释中提到的内容。
ffmpeg -i input.mp4 -c copy -map 0 -segment_time 00:20:00 -f segment -reset_timestamps 1 output%03d.mp4
-reset_timestamps 1
对我来说可以解决问题
较早面对相同的问题,并使用FFMpeg编写了一个简单的Python脚本来完成该任务。可在此处获取:https : //github.com/c0decracker/video-splitter,并粘贴在下面:
#!/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)
如果要创建真正相同的块,则必须强制ffmpeg在每个块的第一帧上创建i帧,以便可以使用此命令创建0.5秒的块。
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
一种替代更可读的办法是
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
*/
这是常用的FFmpeg命令的来源和列表。
#!/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