#! /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]'
|