dotfiles

My personal shell configs and stuff
git clone git://git.alex.balgavy.eu/dotfiles.git
Log | Files | Refs | Submodules | README | LICENSE

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()