Skip to content

Instantly share code, notes, and snippets.

@bmatherly
Created November 5, 2015 05:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bmatherly/e39fb68c876161d5b17b to your computer and use it in GitHub Desktop.
Save bmatherly/e39fb68c876161d5b17b to your computer and use it in GitHub Desktop.
Normalize media files using Python and FFMpeg
#! /usr/bin/python
import getopt, sys, os, time, subprocess, shutil, argparse
WIDTH = 1280
HEIGHT = 720
RATE = "60000/1001"
EXT = ".mov"
FFMPEG = "ffmpeg.exe"
description = (
"Normalize files\n"
" Input and output may be specified as either a file or directory. \n"
" * If input is a file, it will be normalized to the output file. \n"
" * If input is a directory, all files inside the directory will be \n"
" normalized and stored in the output directory. \n"
" In all cases, an appropriate file extension is added. \n"
)
parser = argparse.ArgumentParser(description=description)
parser.add_argument("-t", help="Rename output files with modification [t]ime", action='store_true')
parser.add_argument("src")
parser.add_argument("dst")
args = parser.parse_args()
def get_destination_filename(srcname):
dstname = ""
if args.t:
dstname = time.strftime("%Y-%m-%d-%H%M%S", time.gmtime(os.path.getmtime(srcname)) )
else:
# Strip the path and extension
dstname = os.path.basename( os.path.splitext(srcname) )
# Clean the name of spaces and strange characters
dstname.replace(" ", "")
''.join(e for e in dstname if e not in "{}()![],")
return dstname + EXT
def normalize_file(src_file, dst_file):
print("Normalize", srcfile, "to", dstfile)
# Get information about the video
infocmd = [ FFMPEG,
'-i',
src_file,
'-vframes', '1',
"-vf", 'showinfo',
'-an',
'-f', 'rawvideo',
'-y',
os.devnull
]
procinfo = subprocess.run(infocmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
vidinfo = ""
for line in procinfo.stdout.split("\n"):
if "showinfo" in line:
vidinfo = line
# Structure is printed as i:T or i:B or i:P
structure = vidinfo.split("i:")[1].split()[0]
# Size is printed as s:1920x1080
iw = vidinfo.split(" s:")[1].split()[0].split("x")[0]
ih = vidinfo.split(" s:")[1].split()[0].split("x")[1]
filters = ""
# Deinterlace if necessary
if structure != "P":
print("Source is Interlaced")
# Duplicate frames
filters = filters + "yadif=1:-1:0"
else:
print("Source is Progressive")
# Scale if necessary
if iw != WIDTH or ih != HEIGHT:
print( "Scale", iw, "x", ih, "->", WIDTH, "x", HEIGHT)
if filters != "":
filters += ","
filters += "scale=(iw*sar)*min({WIDTH}/(iw*sar)\,{HEIGHT}/ih):ih*min({WIDTH}/(iw*sar)\,{HEIGHT}/ih)".format(WIDTH=WIDTH, HEIGHT=HEIGHT)
filters += ","
# Add bars if AR does not match. Has no effect when they do match.
filters += "pad={WIDTH}:{HEIGHT}:({WIDTH}-iw*min({WIDTH}/iw\,{HEIGHT}/ih))/2:({HEIGHT}-ih*min({WIDTH}/iw\,{HEIGHT}/ih))/2".format(WIDTH=WIDTH, HEIGHT=HEIGHT)
cmd = [ FFMPEG,
'-i',
src_file,
'-sn',
]
if filters:
cmd += ["-vf", filters]
cmd += [
'-r', RATE,
'-vcodec', 'dnxhd',
'-vb', '220000k',
'-pix_fmt', 'yuv422p',
'-threads', '3',
'-acodec', 'pcm_s16le',
'-ac', '2',
'-ar', '48000',
dst_file
]
print( "About to run command:" )
print(cmd)
subprocess.run(cmd)
# Make sure the normalize succeeded.
if os.path.isfile(dst_file):
shutil.copystat( src_file, dst_file )
else:
print("Normalize Failed")
exit( 7 )
# Sanity checking, to make sure everything is in order.
if os.path.isfile(args.src):
# Normalize a single file
srcfile = args.src
dstfile = get_destination_filename(srcfile)
normalize_file( srcfile, dstfile )
elif os.path.isdir(args.src):
if not os.path.isdir(args.dst):
print(args.src, "is a directory but", args.dst, "is not a directory")
exit(1)
for file in os.listdir(args.src):
srcfile = os.path.join(args.src, file)
dstfile = os.path.join(args.dst, get_destination_filename(srcfile))
normalize_file( srcfile, dstfile )
else:
print( args.src, "is not a file or directory" )
exit(2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment