ffmpeg-split.py (8914B)
1 #!/usr/bin/env python 2 3 import csv 4 import subprocess 5 import math 6 import json 7 import os 8 import shlex 9 from optparse import OptionParser 10 11 12 def split_by_manifest(filename, manifest, vcodec="copy", acodec="copy", 13 extra="", **kwargs): 14 """ Split video into segments based on the given manifest file. 15 16 Arguments: 17 filename (str) - Location of the video. 18 manifest (str) - Location of the manifest file. 19 vcodec (str) - Controls the video codec for the ffmpeg video 20 output. 21 acodec (str) - Controls the audio codec for the ffmpeg video 22 output. 23 extra (str) - Extra options for ffmpeg. 24 """ 25 if not os.path.exists(manifest): 26 print "File does not exist: %s" % manifest 27 raise SystemExit 28 29 with open(manifest) as manifest_file: 30 manifest_type = manifest.split(".")[-1] 31 if manifest_type == "json": 32 config = json.load(manifest_file) 33 elif manifest_type == "csv": 34 config = csv.DictReader(manifest_file) 35 else: 36 print "Format not supported. File must be a csv or json file" 37 raise SystemExit 38 39 split_cmd = ["ffmpeg", "-i", filename, "-vcodec", vcodec, 40 "-acodec", acodec, "-y"] + shlex.split(extra) 41 try: 42 fileext = filename.split(".")[-1] 43 except IndexError as e: 44 raise IndexError("No . in filename. Error: " + str(e)) 45 for video_config in config: 46 split_str = "" 47 split_args = [] 48 try: 49 split_start = video_config["start_time"] 50 split_length = video_config.get("end_time", None) 51 if not split_length: 52 split_length = video_config["length"] 53 filebase = video_config["rename_to"] 54 if fileext in filebase: 55 filebase = ".".join(filebase.split(".")[:-1]) 56 57 split_args += ["-ss", str(split_start), "-t", 58 str(split_length), filebase + "." + fileext] 59 print "########################################################" 60 print "About to run: "+" ".join(split_cmd+split_args) 61 print "########################################################" 62 subprocess.check_output(split_cmd+split_args) 63 except KeyError as e: 64 print "############# Incorrect format ##############" 65 if manifest_type == "json": 66 print "The format of each json array should be:" 67 print "{start_time: <int>, length: <int>, rename_to: <string>}" 68 elif manifest_type == "csv": 69 print "start_time,length,rename_to should be the first line " 70 print "in the csv file." 71 print "#############################################" 72 print e 73 raise SystemExit 74 75 def get_video_length(filename): 76 77 output = subprocess.check_output(("ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", filename)).strip() 78 video_length = int(float(output)) 79 print "Video length in seconds: "+str(video_length) 80 81 return video_length 82 83 def ceildiv(a, b): 84 return int(math.ceil(a / float(b))) 85 86 def split_by_seconds(filename, split_length, vcodec="copy", acodec="copy", 87 extra="", video_length=None, **kwargs): 88 if split_length and split_length <= 0: 89 print "Split length can't be 0" 90 raise SystemExit 91 92 if not video_length: 93 video_length = get_video_length(filename) 94 split_count = ceildiv(video_length, split_length) 95 if(split_count == 1): 96 print "Video length is less then the target split length." 97 raise SystemExit 98 99 split_cmd = ["ffmpeg", "-i", filename, "-vcodec", vcodec, "-acodec", acodec] + shlex.split(extra) 100 try: 101 filebase = ".".join(filename.split(".")[:-1]) 102 fileext = filename.split(".")[-1] 103 except IndexError as e: 104 raise IndexError("No . in filename. Error: " + str(e)) 105 for n in range(0, split_count): 106 split_args = [] 107 if n == 0: 108 split_start = 0 109 else: 110 split_start = split_length * n 111 112 split_args += ["-ss", str(split_start), "-t", str(split_length), 113 filebase + "-" + str(n+1) + "-of-" + \ 114 str(split_count) + "." + fileext] 115 print "About to run: "+" ".join(split_cmd+split_args) 116 subprocess.check_output(split_cmd+split_args) 117 118 119 def main(): 120 parser = OptionParser() 121 122 parser.add_option("-f", "--file", 123 dest = "filename", 124 help = "File to split, for example sample.avi", 125 type = "string", 126 action = "store" 127 ) 128 parser.add_option("-s", "--split-size", 129 dest = "split_length", 130 help = "Split or chunk size in seconds, for example 10", 131 type = "int", 132 action = "store" 133 ) 134 parser.add_option("-c", "--split-chunks", 135 dest = "split_chunks", 136 help = "Number of chunks to split to", 137 type = "int", 138 action = "store" 139 ) 140 parser.add_option("-S", "--split-filesize", 141 dest = "split_filesize", 142 help = "Split or chunk size in bytes (approximate)", 143 type = "int", 144 action = "store" 145 ) 146 parser.add_option("--filesize-factor", 147 dest = "filesize_factor", 148 help = "with --split-filesize, use this factor in time to" \ 149 " size heuristics [default: %default]", 150 type = "float", 151 action = "store", 152 default = 0.95 153 ) 154 parser.add_option("--chunk-strategy", 155 dest = "chunk_strategy", 156 help = "with --split-filesize, allocate chunks according to" \ 157 " given strategy (eager or even)", 158 type = "choice", 159 action = "store", 160 choices = ['eager', 'even'], 161 default = 'eager' 162 ) 163 parser.add_option("-m", "--manifest", 164 dest = "manifest", 165 help = "Split video based on a json manifest file. ", 166 type = "string", 167 action = "store" 168 ) 169 parser.add_option("-v", "--vcodec", 170 dest = "vcodec", 171 help = "Video codec to use. ", 172 type = "string", 173 default = "copy", 174 action = "store" 175 ) 176 parser.add_option("-a", "--acodec", 177 dest = "acodec", 178 help = "Audio codec to use. ", 179 type = "string", 180 default = "copy", 181 action = "store" 182 ) 183 parser.add_option("-e", "--extra", 184 dest = "extra", 185 help = "Extra options for ffmpeg, e.g. '-e -threads 8'. ", 186 type = "string", 187 default = "", 188 action = "store" 189 ) 190 (options, args) = parser.parse_args() 191 192 def bailout(): 193 parser.print_help() 194 raise SystemExit 195 196 if not options.filename: 197 bailout() 198 199 if options.manifest: 200 split_by_manifest(**(options.__dict__)) 201 else: 202 video_length = None 203 if not options.split_length: 204 video_length = get_video_length(options.filename) 205 file_size = os.stat(options.filename).st_size 206 split_filesize = None 207 if options.split_filesize: 208 split_filesize = int(options.split_filesize * options.filesize_factor) 209 if split_filesize and options.chunk_strategy == 'even': 210 options.split_chunks = ceildiv(file_size, split_filesize) 211 if options.split_chunks: 212 options.split_length = ceildiv(video_length, options.split_chunks) 213 if not options.split_length and split_filesize: 214 options.split_length = int(split_filesize / float(file_size) * video_length) 215 if not options.split_length: 216 bailout() 217 split_by_seconds(video_length=video_length, **(options.__dict__)) 218 219 if __name__ == '__main__': 220 main()