Guido van Rossum | f06ee5f | 1996-11-27 19:52:01 +0000 | [diff] [blame] | 1 | #! /usr/bin/env python |
Guido van Rossum | 349f2b5 | 1992-12-24 11:41:14 +0000 | [diff] [blame] | 2 | |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 3 | # Universal (non-interactive) CMIF video file copier. |
Guido van Rossum | 349f2b5 | 1992-12-24 11:41:14 +0000 | [diff] [blame] | 4 | |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 5 | |
| 6 | # Possibilities: |
| 7 | # |
| 8 | # - Manipulate the time base: |
| 9 | # = resample at a fixed rate |
| 10 | # = divide the time codes by a speed factor (to make it go faster/slower) |
Jeremy Hylton | a05e293 | 2000-06-28 14:48:01 +0000 | [diff] [blame] | 11 | # = drop frames that are less than n msec apart (to accommodate slow players) |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 12 | # - Convert to a different format |
| 13 | # - Magnify (scale) the image |
| 14 | |
| 15 | |
| 16 | # Usage function (keep this up-to-date if you change the program!) |
Guido van Rossum | 349f2b5 | 1992-12-24 11:41:14 +0000 | [diff] [blame] | 17 | |
| 18 | def usage(): |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 19 | print 'Usage: Vcopy [options] [infile [outfile]]' |
| 20 | print |
| 21 | print 'Options:' |
| 22 | print |
| 23 | print '-t type : new image type (default unchanged)' |
| 24 | print |
| 25 | print '-M magnify : image magnification factor (default unchanged)' |
| 26 | print '-w width : output image width (default height*4/3 if -h used)' |
| 27 | print '-h height : output image height (default width*3/4 if -w used)' |
| 28 | print |
| 29 | print '-p pf : new x and y packfactor (default unchanged)' |
Guido van Rossum | c97d2ed | 1993-02-25 14:50:27 +0000 | [diff] [blame] | 30 | print '-x xpf : new x packfactor (default unchanged)' |
| 31 | print '-y ypf : new y packfactor (default unchanged)' |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 32 | print |
| 33 | print '-m delta : drop frames closer than delta msec (default 0)' |
| 34 | print '-r delta : regenerate input time base delta msec apart' |
| 35 | print '-s speed : speed change factor (default unchanged)' |
| 36 | print |
| 37 | print 'infile : input file (default film.video)' |
| 38 | print 'outfile : output file (default out.video)' |
Guido van Rossum | 349f2b5 | 1992-12-24 11:41:14 +0000 | [diff] [blame] | 39 | |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 40 | |
| 41 | import sys |
| 42 | sys.path.append('/ufs/guido/src/video') |
| 43 | |
| 44 | import VFile |
| 45 | import imgconv |
| 46 | import imageop |
| 47 | import getopt |
| 48 | import string |
| 49 | |
| 50 | |
| 51 | # Global options |
| 52 | |
| 53 | speed = 1.0 |
| 54 | mindelta = 0 |
| 55 | regen = None |
| 56 | newpf = None |
| 57 | newtype = None |
| 58 | magnify = None |
| 59 | newwidth = None |
| 60 | newheight = None |
| 61 | |
| 62 | |
| 63 | # Function to turn a string into a float |
| 64 | |
| 65 | atof_error = 'atof_error' # Exception if it fails |
| 66 | |
| 67 | def atof(s): |
| 68 | try: |
| 69 | return float(eval(s)) |
| 70 | except: |
| 71 | raise atof_error |
| 72 | |
| 73 | |
| 74 | # Main program -- mostly command line parsing |
Guido van Rossum | 349f2b5 | 1992-12-24 11:41:14 +0000 | [diff] [blame] | 75 | |
| 76 | def main(): |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 77 | global speed, mindelta, regen, newpf, newtype, \ |
| 78 | magnify, newwidth, newheight |
| 79 | |
| 80 | # Parse command line |
| 81 | try: |
| 82 | opts, args = getopt.getopt(sys.argv[1:], \ |
| 83 | 'M:h:m:p:r:s:t:w:x:y:') |
| 84 | except getopt.error, msg: |
| 85 | sys.stdout = sys.stderr |
| 86 | print 'Error:', msg, '\n' |
Guido van Rossum | 349f2b5 | 1992-12-24 11:41:14 +0000 | [diff] [blame] | 87 | usage() |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 88 | sys.exit(2) |
| 89 | |
| 90 | xpf = ypf = None |
Guido van Rossum | 349f2b5 | 1992-12-24 11:41:14 +0000 | [diff] [blame] | 91 | |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 92 | # Interpret options |
| 93 | try: |
| 94 | for opt, arg in opts: |
| 95 | if opt == '-M': magnify = atof(arg) |
| 96 | if opt == '-h': height = string.atoi(arg) |
| 97 | if opt == '-m': mindelta = string.atoi(arg) |
| 98 | if opt == '-p': xpf = ypf = string.atoi(arg) |
| 99 | if opt == '-r': regen = string.atoi(arg) |
| 100 | if opt == '-s': speed = atof(arg) |
| 101 | if opt == '-t': newtype = arg |
| 102 | if opt == '-w': newwidth = string.atoi(arg) |
| 103 | if opt == '-x': xpf = string.atoi(arg) |
| 104 | if opt == '-y': ypf = string.atoi(arg) |
| 105 | except string.atoi_error: |
| 106 | sys.stdout = sys.stderr |
| 107 | print 'Option', opt, 'requires integer argument' |
| 108 | sys.exit(2) |
| 109 | except atof_error: |
| 110 | sys.stdout = sys.stderr |
| 111 | print 'Option', opt, 'requires float argument' |
| 112 | sys.exit(2) |
Guido van Rossum | 349f2b5 | 1992-12-24 11:41:14 +0000 | [diff] [blame] | 113 | |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 114 | if xpf or ypf: |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 115 | newpf = (xpf, ypf) |
| 116 | |
| 117 | if newwidth or newheight: |
| 118 | if magnify: |
| 119 | sys.stdout = sys.stderr |
| 120 | print 'Options -w or -h are incompatible with -M' |
| 121 | sys.exit(2) |
| 122 | if not newheight: |
| 123 | newheight = newwidth * 3 / 4 |
| 124 | elif not newwidth: |
| 125 | newwidth = newheight * 4 / 3 |
| 126 | |
| 127 | # Check filename arguments |
| 128 | if len(args) < 1: |
| 129 | args.append('film.video') |
| 130 | if len(args) < 2: |
| 131 | args.append('out.video') |
| 132 | if len(args) > 2: |
| 133 | usage() |
| 134 | sys.exit(2) |
| 135 | if args[0] == args[1]: |
| 136 | sys.stderr.write('Input file can\'t be output file\n') |
| 137 | sys.exit(2) |
| 138 | |
| 139 | # Do the right thing |
| 140 | sts = process(args[0], args[1]) |
| 141 | |
| 142 | # Exit |
| 143 | sys.exit(sts) |
| 144 | |
| 145 | |
| 146 | # Copy one file to another |
| 147 | |
| 148 | def process(infilename, outfilename): |
Guido van Rossum | c97d2ed | 1993-02-25 14:50:27 +0000 | [diff] [blame] | 149 | global newwidth, newheight, newpf |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 150 | |
| 151 | try: |
Guido van Rossum | 21a3ff9 | 1993-12-17 15:11:41 +0000 | [diff] [blame] | 152 | vin = VFile.BasicVinFile(infilename) |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 153 | except IOError, msg: |
| 154 | sys.stderr.write(infilename + ': I/O error: ' + `msg` + '\n') |
| 155 | return 1 |
| 156 | except VFile.Error, msg: |
| 157 | sys.stderr.write(msg + '\n') |
| 158 | return 1 |
| 159 | except EOFError: |
| 160 | sys.stderr.write(infilename + ': EOF in video file\n') |
| 161 | return 1 |
| 162 | |
| 163 | try: |
Guido van Rossum | 21a3ff9 | 1993-12-17 15:11:41 +0000 | [diff] [blame] | 164 | vout = VFile.BasicVoutFile(outfilename) |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 165 | except IOError, msg: |
| 166 | sys.stderr.write(outfilename + ': I/O error: ' + `msg` + '\n') |
| 167 | return 1 |
| 168 | |
Guido van Rossum | 85f7bd5 | 1993-02-25 16:10:16 +0000 | [diff] [blame] | 169 | print '=== input file ===' |
Guido van Rossum | c97d2ed | 1993-02-25 14:50:27 +0000 | [diff] [blame] | 170 | vin.printinfo() |
| 171 | |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 172 | vout.setinfo(vin.getinfo()) |
| 173 | |
| 174 | scale = 0 |
| 175 | flip = 0 |
Jack Jansen | 72d7364 | 1993-09-28 16:46:15 +0000 | [diff] [blame] | 176 | decompress = 0 |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 177 | |
Jack Jansen | 72d7364 | 1993-09-28 16:46:15 +0000 | [diff] [blame] | 178 | vinfmt = vin.format |
| 179 | if vinfmt == 'compress': |
| 180 | if not newtype or newtype == 'compress': |
| 181 | # compressed->compressed: copy compression header |
| 182 | vout.setcompressheader(vin.getcompressheader()) |
| 183 | else: |
| 184 | # compressed->something else: go via rgb-24 |
| 185 | decompress = 1 |
| 186 | vinfmt = 'rgb' |
| 187 | elif newtype == 'compress': |
| 188 | # something else->compressed: not implemented |
| 189 | sys.stderr.write('Sorry, conversion to compressed not yet implemented\n') |
| 190 | return 1 |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 191 | if newtype: |
| 192 | vout.setformat(newtype) |
| 193 | try: |
Jack Jansen | 72d7364 | 1993-09-28 16:46:15 +0000 | [diff] [blame] | 194 | convert = imgconv.getconverter(vinfmt, vout.format) |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 195 | except imgconv.error, msg: |
| 196 | sys.stderr.write(str(msg) + '\n') |
| 197 | return 1 |
| 198 | |
| 199 | if newpf: |
Guido van Rossum | c97d2ed | 1993-02-25 14:50:27 +0000 | [diff] [blame] | 200 | xpf, ypf = newpf |
| 201 | if not xpf: xpf = vin.xpf |
| 202 | if not ypf: ypf = vout.ypf |
| 203 | newpf = (xpf, ypf) |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 204 | vout.setpf(newpf) |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 205 | |
| 206 | if newwidth and newheight: |
| 207 | scale = 1 |
| 208 | |
| 209 | if vin.upside_down <> vout.upside_down or \ |
| 210 | vin.mirror_image <> vout.mirror_image: |
| 211 | flip = 1 |
| 212 | |
| 213 | inwidth, inheight = vin.getsize() |
| 214 | inwidth = inwidth / vin.xpf |
| 215 | inheight = inheight / vin.ypf |
| 216 | |
| 217 | if magnify: |
| 218 | newwidth = int(vout.width * magnify) |
| 219 | newheight = int(vout.height * magnify) |
| 220 | scale = 1 |
| 221 | |
| 222 | if scale: |
| 223 | vout.setsize(newwidth, newheight) |
| 224 | else: |
| 225 | newwidth, newheight = vout.getsize() |
| 226 | |
| 227 | if vin.packfactor <> vout.packfactor: |
| 228 | scale = 1 |
| 229 | |
| 230 | if scale or flip: |
| 231 | if vout.bpp not in (8, 32): |
| 232 | sys.stderr.write('Can\'t scale or flip this type\n') |
| 233 | return 1 |
| 234 | |
| 235 | newwidth = newwidth / vout.xpf |
| 236 | newheight = newheight / vout.ypf |
| 237 | |
Guido van Rossum | 85f7bd5 | 1993-02-25 16:10:16 +0000 | [diff] [blame] | 238 | print '=== output file ===' |
Guido van Rossum | c97d2ed | 1993-02-25 14:50:27 +0000 | [diff] [blame] | 239 | vout.printinfo() |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 240 | vout.writeheader() |
| 241 | |
| 242 | told = 0 |
| 243 | nin = 0 |
| 244 | nout = 0 |
| 245 | tin = 0 |
| 246 | tout = 0 |
| 247 | |
Guido van Rossum | 349f2b5 | 1992-12-24 11:41:14 +0000 | [diff] [blame] | 248 | while 1: |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 249 | try: |
| 250 | tin, data, cdata = vin.getnextframe() |
| 251 | except EOFError: |
Guido van Rossum | 349f2b5 | 1992-12-24 11:41:14 +0000 | [diff] [blame] | 252 | break |
Jack Jansen | 72d7364 | 1993-09-28 16:46:15 +0000 | [diff] [blame] | 253 | if decompress: |
| 254 | data = vin.decompress(data) |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 255 | nin = nin + 1 |
| 256 | if regen: |
| 257 | tout = nin * regen |
Guido van Rossum | 349f2b5 | 1992-12-24 11:41:14 +0000 | [diff] [blame] | 258 | else: |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 259 | tout = tin |
| 260 | tout = int(tout / speed) |
| 261 | if tout - told < mindelta: |
| 262 | continue |
| 263 | told = tout |
| 264 | if newtype: |
| 265 | data = convert(data, inwidth, inheight) |
Guido van Rossum | c97d2ed | 1993-02-25 14:50:27 +0000 | [diff] [blame] | 266 | if scale: |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 267 | data = imageop.scale(data, vout.bpp/8, \ |
| 268 | inwidth, inheight, newwidth, newheight) |
Guido van Rossum | c97d2ed | 1993-02-25 14:50:27 +0000 | [diff] [blame] | 269 | if flip: |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 270 | x0, y0 = 0, 0 |
Guido van Rossum | 85f7bd5 | 1993-02-25 16:10:16 +0000 | [diff] [blame] | 271 | x1, y1 = newwidth-1, newheight-1 |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 272 | if vin.upside_down <> vout.upside_down: |
| 273 | y1, y0 = y0, y1 |
| 274 | if vin.mirror_image <> vout.mirror_image: |
| 275 | x1, x0 = x0, x1 |
| 276 | data = imageop.crop(data, vout.bpp/8, \ |
| 277 | newwidth, newheight, x0, y0, x1, y1) |
Guido van Rossum | 85f7bd5 | 1993-02-25 16:10:16 +0000 | [diff] [blame] | 278 | print 'Writing frame', nout |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 279 | vout.writeframe(tout, data, cdata) |
| 280 | nout = nout + 1 |
Guido van Rossum | 349f2b5 | 1992-12-24 11:41:14 +0000 | [diff] [blame] | 281 | |
Guido van Rossum | 5e044b7 | 1993-02-25 14:20:13 +0000 | [diff] [blame] | 282 | vout.close() |
| 283 | vin.close() |
| 284 | |
| 285 | |
| 286 | # Don't forget to call the main program |
| 287 | |
| 288 | try: |
| 289 | main() |
| 290 | except KeyboardInterrupt: |
| 291 | print '[Interrupt]' |