| #! /usr/bin/env python |
| |
| # Universal (non-interactive) CMIF video file copier. |
| |
| |
| # Possibilities: |
| # |
| # - Manipulate the time base: |
| # = resample at a fixed rate |
| # = divide the time codes by a speed factor (to make it go faster/slower) |
| # = drop frames that are less than n msec apart (to accomodate slow players) |
| # - Convert to a different format |
| # - Magnify (scale) the image |
| |
| |
| # Usage function (keep this up-to-date if you change the program!) |
| |
| def usage(): |
| print 'Usage: Vcopy [options] [infile [outfile]]' |
| print |
| print 'Options:' |
| print |
| print '-t type : new image type (default unchanged)' |
| print |
| print '-M magnify : image magnification factor (default unchanged)' |
| print '-w width : output image width (default height*4/3 if -h used)' |
| print '-h height : output image height (default width*3/4 if -w used)' |
| print |
| print '-p pf : new x and y packfactor (default unchanged)' |
| print '-x xpf : new x packfactor (default unchanged)' |
| print '-y ypf : new y packfactor (default unchanged)' |
| print |
| print '-m delta : drop frames closer than delta msec (default 0)' |
| print '-r delta : regenerate input time base delta msec apart' |
| print '-s speed : speed change factor (default unchanged)' |
| print |
| print 'infile : input file (default film.video)' |
| print 'outfile : output file (default out.video)' |
| |
| |
| import sys |
| sys.path.append('/ufs/guido/src/video') |
| |
| import VFile |
| import imgconv |
| import imageop |
| import getopt |
| import string |
| |
| |
| # Global options |
| |
| speed = 1.0 |
| mindelta = 0 |
| regen = None |
| newpf = None |
| newtype = None |
| magnify = None |
| newwidth = None |
| newheight = None |
| |
| |
| # Function to turn a string into a float |
| |
| atof_error = 'atof_error' # Exception if it fails |
| |
| def atof(s): |
| try: |
| return float(eval(s)) |
| except: |
| raise atof_error |
| |
| |
| # Main program -- mostly command line parsing |
| |
| def main(): |
| global speed, mindelta, regen, newpf, newtype, \ |
| magnify, newwidth, newheight |
| |
| # Parse command line |
| try: |
| opts, args = getopt.getopt(sys.argv[1:], \ |
| 'M:h:m:p:r:s:t:w:x:y:') |
| except getopt.error, msg: |
| sys.stdout = sys.stderr |
| print 'Error:', msg, '\n' |
| usage() |
| sys.exit(2) |
| |
| xpf = ypf = None |
| |
| # Interpret options |
| try: |
| for opt, arg in opts: |
| if opt == '-M': magnify = atof(arg) |
| if opt == '-h': height = string.atoi(arg) |
| if opt == '-m': mindelta = string.atoi(arg) |
| if opt == '-p': xpf = ypf = string.atoi(arg) |
| if opt == '-r': regen = string.atoi(arg) |
| if opt == '-s': speed = atof(arg) |
| if opt == '-t': newtype = arg |
| if opt == '-w': newwidth = string.atoi(arg) |
| if opt == '-x': xpf = string.atoi(arg) |
| if opt == '-y': ypf = string.atoi(arg) |
| except string.atoi_error: |
| sys.stdout = sys.stderr |
| print 'Option', opt, 'requires integer argument' |
| sys.exit(2) |
| except atof_error: |
| sys.stdout = sys.stderr |
| print 'Option', opt, 'requires float argument' |
| sys.exit(2) |
| |
| if xpf or ypf: |
| newpf = (xpf, ypf) |
| |
| if newwidth or newheight: |
| if magnify: |
| sys.stdout = sys.stderr |
| print 'Options -w or -h are incompatible with -M' |
| sys.exit(2) |
| if not newheight: |
| newheight = newwidth * 3 / 4 |
| elif not newwidth: |
| newwidth = newheight * 4 / 3 |
| |
| # Check filename arguments |
| if len(args) < 1: |
| args.append('film.video') |
| if len(args) < 2: |
| args.append('out.video') |
| if len(args) > 2: |
| usage() |
| sys.exit(2) |
| if args[0] == args[1]: |
| sys.stderr.write('Input file can\'t be output file\n') |
| sys.exit(2) |
| |
| # Do the right thing |
| sts = process(args[0], args[1]) |
| |
| # Exit |
| sys.exit(sts) |
| |
| |
| # Copy one file to another |
| |
| def process(infilename, outfilename): |
| global newwidth, newheight, newpf |
| |
| try: |
| vin = VFile.BasicVinFile(infilename) |
| except IOError, msg: |
| sys.stderr.write(infilename + ': I/O error: ' + `msg` + '\n') |
| return 1 |
| except VFile.Error, msg: |
| sys.stderr.write(msg + '\n') |
| return 1 |
| except EOFError: |
| sys.stderr.write(infilename + ': EOF in video file\n') |
| return 1 |
| |
| try: |
| vout = VFile.BasicVoutFile(outfilename) |
| except IOError, msg: |
| sys.stderr.write(outfilename + ': I/O error: ' + `msg` + '\n') |
| return 1 |
| |
| print '=== input file ===' |
| vin.printinfo() |
| |
| vout.setinfo(vin.getinfo()) |
| |
| scale = 0 |
| flip = 0 |
| decompress = 0 |
| |
| vinfmt = vin.format |
| if vinfmt == 'compress': |
| if not newtype or newtype == 'compress': |
| # compressed->compressed: copy compression header |
| vout.setcompressheader(vin.getcompressheader()) |
| else: |
| # compressed->something else: go via rgb-24 |
| decompress = 1 |
| vinfmt = 'rgb' |
| elif newtype == 'compress': |
| # something else->compressed: not implemented |
| sys.stderr.write('Sorry, conversion to compressed not yet implemented\n') |
| return 1 |
| if newtype: |
| vout.setformat(newtype) |
| try: |
| convert = imgconv.getconverter(vinfmt, vout.format) |
| except imgconv.error, msg: |
| sys.stderr.write(str(msg) + '\n') |
| return 1 |
| |
| if newpf: |
| xpf, ypf = newpf |
| if not xpf: xpf = vin.xpf |
| if not ypf: ypf = vout.ypf |
| newpf = (xpf, ypf) |
| vout.setpf(newpf) |
| |
| if newwidth and newheight: |
| scale = 1 |
| |
| if vin.upside_down <> vout.upside_down or \ |
| vin.mirror_image <> vout.mirror_image: |
| flip = 1 |
| |
| inwidth, inheight = vin.getsize() |
| inwidth = inwidth / vin.xpf |
| inheight = inheight / vin.ypf |
| |
| if magnify: |
| newwidth = int(vout.width * magnify) |
| newheight = int(vout.height * magnify) |
| scale = 1 |
| |
| if scale: |
| vout.setsize(newwidth, newheight) |
| else: |
| newwidth, newheight = vout.getsize() |
| |
| if vin.packfactor <> vout.packfactor: |
| scale = 1 |
| |
| if scale or flip: |
| if vout.bpp not in (8, 32): |
| sys.stderr.write('Can\'t scale or flip this type\n') |
| return 1 |
| |
| newwidth = newwidth / vout.xpf |
| newheight = newheight / vout.ypf |
| |
| print '=== output file ===' |
| vout.printinfo() |
| vout.writeheader() |
| |
| told = 0 |
| nin = 0 |
| nout = 0 |
| tin = 0 |
| tout = 0 |
| |
| while 1: |
| try: |
| tin, data, cdata = vin.getnextframe() |
| except EOFError: |
| break |
| if decompress: |
| data = vin.decompress(data) |
| nin = nin + 1 |
| if regen: |
| tout = nin * regen |
| else: |
| tout = tin |
| tout = int(tout / speed) |
| if tout - told < mindelta: |
| continue |
| told = tout |
| if newtype: |
| data = convert(data, inwidth, inheight) |
| if scale: |
| data = imageop.scale(data, vout.bpp/8, \ |
| inwidth, inheight, newwidth, newheight) |
| if flip: |
| x0, y0 = 0, 0 |
| x1, y1 = newwidth-1, newheight-1 |
| if vin.upside_down <> vout.upside_down: |
| y1, y0 = y0, y1 |
| if vin.mirror_image <> vout.mirror_image: |
| x1, x0 = x0, x1 |
| data = imageop.crop(data, vout.bpp/8, \ |
| newwidth, newheight, x0, y0, x1, y1) |
| print 'Writing frame', nout |
| vout.writeframe(tout, data, cdata) |
| nout = nout + 1 |
| |
| vout.close() |
| vin.close() |
| |
| |
| # Don't forget to call the main program |
| |
| try: |
| main() |
| except KeyboardInterrupt: |
| print '[Interrupt]' |