本文介绍如何用ffmpeg开源组件按时长进行切片,举一个例子,一个视频网站,拿到一个时长1.5小时的电影,用户点击播放时,常用的技术方案就是把一个完整的大文件,转码后切成按固定时长的小文件,分发到cdn上去,这样用户就可以实现就近下载,包括拖放等操作,这里面有比较多的技术细节,本文只讨论第一步,按时长切片。
一 获取视频时长
ffmpeg安装后,直接执行ffmpeg -i 文件就可以从内容中获取到时长,如下图所示,其中的Duration就是时长
wangyachangdeMacBook-Pro:ffmpeg wangyachang$ /usr/local/ffmpeg/bin/ffmpeg -i movie.mp4
ffmpeg version 3.0.2 Copyright (c) 2000-2016 the FFmpeg developers
built with Apple LLVM version 7.0.2 (clang-700.1.81)
configuration: --prefix=/usr/local/ffmpeg/ --disable-yasm
libavutil 55. 17.103 / 55. 17.103
libavcodec 57. 24.102 / 57. 24.102
libavformat 57. 25.100 / 57. 25.100
libavdevice 57. 0.101 / 57. 0.101
libavfilter 6. 31.100 / 6. 31.100
libswscale 4. 0.100 / 4. 0.100
libswresample 2. 0.101 / 2. 0.101
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'movie.mp4':
Metadata:
major_brand : isom
minor_version : 1
compatible_brands: isom
creation_time : 2015-11-04 06:54:54
encoder : FormatFactory : www.pcfreetime.com
Duration: 00:03:25.01, start: 0.000000, bitrate: 1101 kb/s
Stream #0:0(und): Video: mpeg4 (Simple Profile) (mp4v / 0x7634706D), yuv420p, 720x480 [SAR 32:27 DAR 16:9], 977 kb/s, 24 fps, 24 tbr, 24k tbn, 24 tbc (default)
Metadata:
creation_time : 2015-11-04 06:54:54
handler_name : video
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 122 kb/s (default)
Metadata:
creation_time : 2015-11-04 06:54:54
handler_name : sound
不过上面的信息是打印到标准出错的,用下面的命令可以取到对应信息
wangyachangdeMacBook-Pro:ffmpeg wangyachang$ /usr/local/ffmpeg/bin/ffmpeg -i movie.mp4 2>&1 | grep Duration
Duration: 00:03:25.01, start: 0.000000, bitrate: 1101 kb/s
通过awk可以方便的取出时\分\秒。
二 切片
通过下面的命令可以实现切片。
/usr/local/ffmpeg/bin/ffmpeg -ss START -t LENGTH -i INPUTFILE -vcodec copy -acodec copy OUTFILE
举一个例子,如果要把一个3分钟的文件,按每1分钟切一个片,可以用下面的方式切成三个文件
/usr/local/ffmpeg/bin/ffmpeg -ss 0 -t 00:01:00 -i movie.mp4 -vcodec copy -acodec copy movie-1.mp4
/usr/local/ffmpeg/bin/ffmpeg -ss 00:01:00 -t 00:02:00 -i movie.mp4 -vcodec copy -acodec copy movie-2.mp4
/usr/local/ffmpeg/bin/ffmpeg -ss 00:02:00 -t 00:03:00 -i movie.mp4 -vcodec copy -acodec copy movie-3.mp4
执行后,结果如下所示:
ffmpeg version 3.0.2 Copyright (c) 2000-2016 the FFmpeg developers
built with Apple LLVM version 7.0.2 (clang-700.1.81)
configuration: --prefix=/usr/local/ffmpeg/ --disable-yasm
libavutil 55. 17.103 / 55. 17.103
libavcodec 57. 24.102 / 57. 24.102
libavformat 57. 25.100 / 57. 25.100
libavdevice 57. 0.101 / 57. 0.101
libavfilter 6. 31.100 / 6. 31.100
libswscale 4. 0.100 / 4. 0.100
libswresample 2. 0.101 / 2. 0.101
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'movie.mp4':
Metadata:
major_brand : isom
minor_version : 1
compatible_brands: isom
creation_time : 2015-11-04 06:54:54
encoder : FormatFactory : www.pcfreetime.com
Duration: 00:03:25.01, start: 0.000000, bitrate: 1101 kb/s
Stream #0:0(und): Video: mpeg4 (Simple Profile) (mp4v / 0x7634706D), yuv420p, 720x480 [SAR 32:27 DAR 16:9], 977 kb/s, 24 fps, 24 tbr, 24k tbn, 24 tbc (default)
Metadata:
creation_time : 2015-11-04 06:54:54
handler_name : video
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 122 kb/s (default)
Metadata:
creation_time : 2015-11-04 06:54:54
handler_name : sound
File 'movie-1.mp4' already exists. Overwrite ? [y/N] y
Output #0, mp4, to 'movie-1.mp4':
Metadata:
major_brand : isom
minor_version : 1
compatible_brands: isom
encoder : Lavf57.25.100
Stream #0:0(und): Video: mpeg4 ( [0][0][0] / 0x0020), yuv420p, 720x480 [SAR 32:27 DAR 16:9], q=2-31, 977 kb/s, 24 fps, 24 tbr, 24k tbn, 24k tbc (default)
Metadata:
creation_time : 2015-11-04 06:54:54
handler_name : video
Stream #0:1(und): Audio: aac (LC) ([64][0][0][0] / 0x0040), 44100 Hz, stereo, 122 kb/s (default)
Metadata:
creation_time : 2015-11-04 06:54:54
handler_name : sound
Stream mapping:
Stream #0:0 -> #0:0 (copy)
Stream #0:1 -> #0:1 (copy)
Press [q] to stop, [?] for help
frame= 1440 fps=0.0 q=-1.0 Lsize= 8435kB time=00:01:00.00 bitrate=1151.7kbits/s speed=1.59e+03x
video:7480kB audio:920kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.419454%
最后,分享一个开源的脚本,做了一些优化,十分感谢原作者
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#本脚本使用ffmpeg分割音视频文件,分割过程不进行转码或压缩
import subprocess
import re
import os
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, file_list) = parse_options()
if split_length <= 0:
print "Split length can't be 0"
raise SystemExit
output = subprocess.Popen("/usr/local/ffmpeg/bin/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
print split_count, video_length, split_length
if os.path.exists(file_list):
os.remove(file_list)
fp = open(file_list, "wb")
split_cmd = "/usr/local/ffmpeg/bin/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
output_filename = filename[:-4] + "-" + str(n) + "." + filename[-3:]
split_str += " -ss "+str(split_start)+" -t "+str(split_length) + \
" '"+output_filename+ \
"'"
print "About to run: "+split_cmd+split_str
fp.write(output_filename+"\n")
output = subprocess.Popen(split_cmd+split_str, shell = True, stdout =
subprocess.PIPE).stdout.read()
fp.close()
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"
)
parser.add_option("-o", "--file-list",
dest = "file_list",
help = "output file_list",
type = "string",
action = "store"
)
(options, args) = parser.parse_args()
if options.filename and options.split_size and options.file_list:
return (options.filename, options.split_size, options.file_list)
else:
parser.print_help()
raise SystemExit
if __name__ == '__main__':
try:
main()
except Exception, e:
print "Exception occured running main():"
print str(e)
三 压缩
/usr/local/ffmpeg/bin/ffmpeg -i "2.4_mission.mp4" -vcodec libx264 -s 1920X1080 -vf "movie=stark.png [watermark]; [in][watermark] overlay=main_w-overlay_w-10:10 [out]" -acodec copy "2.4_1080P.mp4"
压缩效果还不错,99m的原文件,压缩后画质未变的情况下,体积变成14m。
参考文献:
http://icephoenix.us/notes-for-myself/auto-splitting-video-file-in-equal-chunks-with-ffmpeg-and-python/
http://blog.itpub.net/29754888/viewspace-1383562/ http://blog.csdn.net/dotphoenix/article/details/9714773