1#!/usr/local/bin/python3.8
2
3
4import locale
5locale.setlocale(locale.LC_NUMERIC,"C")
6
7"""
8lives_avi_encoder3
9
10Front-end to various programs needed to create AVI
11movies with some image-enhancing capabilities through the use of
12ImageMagick. Meant as a companion to LiVES (to possibly call
13from within a plugin, see http://www.xs4all.nl/~salsaman/lives/ )
14but can also be used as a stand-alone program.
15
16Requires MPlayer, ImageMagick (GraphicsMagick might also
17work), sox, lame, and Python 2.3.0 or greater. Note that
18this program can encode a SNOW video stream (if mencoder
19supports it), but that it is still highly experimental.
20This is also true for h.264. Use at your own risk.
21
22Copyright (C) 2004-2005 Marco De la Cruz (marco@reimeika.ca)
23It's a trivial program, but might as well GPL it, so see:
24http://www.gnu.org/copyleft/gpl.html for license.
25
26See my vids at http://amv.reimeika.ca !
27
28Copyright (C) 2008 - 2018 Salsaman (salsaman+lives@gmail.com)
29"""
30import os
31version = '0.0.19'
32if os.name == 'nt':
33    convert = 'mgkvert'
34else:
35    convert = 'convert'
36mencoder = 'mencoder'
37lameenc = 'lame'
38sox = 'sox'
39
40usage = \
41      """
42lives_avi_encoder3 -h
43lives_avi_encoder3 -V
44lives_avi_encoder3 -C
45lives_avi_encoder3 [-o out] [-p pre] [-d dir] [-a aspect] [-D delay]
46               [-q|-v] [-t type] [-k] [-e [[-w dir] [-c geom] [-r geom]]]
47               [-s sndfile] [-b sndrate] [-f fpscode] [-L lv1file]
48               [firstframe lastframe]
49      """
50
51help = \
52     """
53SUMMARY (ver. %s):
54
55Encodes a series of PNG or JPG images into an XviD/Snow/h.264 + MP3
56(AVI) stream and is also capable of performing some simple image
57enhacements using ImageMagick. The images and audio are assumed to
58be in an uncompressed LiVES format, hence making this encoder
59more suitable to be used within a LiVES plugin (but can also
60be used directly on a LiVES temporary directory).
61
62OPTIONS:
63
64-h          for help.
65
66-V          shows the version number.
67
68-C          check external program dependencies and exit.
69
70-o out      will save the video in file "out".
71            Default: "'type'_movie.avi". See below for 'type'.
72
73-p pre      the encoder assumes that sound and images are named using
74            LiVES' conventions i.e. "audio" and "00000001.ext",
75            "00000002.ext"... where "ext" is either "jpg" or "png".
76            However, lives_avi_encoder3 will create temporary files
77            which can be saved for later use (for example, after
78            enhancing the images with "-e" , which may take a long
79            time). These temporary files will be named
80            "pre_00000001.ext", etc... where "pre" is either "eimg"
81            or "rimg" and will be kept if the "-k" option is used.
82            lives_avi_encoder3 can then be run over the enhanced images
83            only by selecting the appropriate prefix ("eimg" or
84            "rimg") here. See "-e" to see what each prefix means.
85            Default: "" (an empty string).
86
87-d dir      "dir" is the directory containing the source image
88            files. These must be of the form "00000001.ext" or
89            "pre_00000001.ext" as explained above.
90            Default: "."
91
92-a aspect   sets the aspect ratio of the resulting MPEG-4. This can be:
93
94               1    1:1 display
95               2    4:3 display
96               3    16:9 display
97               4    2.21:1 display
98
99            Default: "2"
100
101-D delay    delay the audio stream by "delay" ms. Must be positive.
102            Default: "0"
103
104-q          quiet operation, only complains on error.
105
106-v          be verbose.
107
108-t type     type of video created. The options here are:
109
110               "hi":   a very high quality XviD file, suitable
111                       for archiving images of 720x480.
112
113               "mh":   medium-high quality XviD, which still allows
114                       to watch small videos (352x240 and below)
115                       fullscreen.
116
117               "ml":   medium-low quality XviD, suitable for
118                       most videos but in particular small videos
119                       (352x240 and below) meant for online
120                       distribution.
121
122               "lo":   a low quality XviD format. Enlarging
123                       small videos will result in noticeable
124                       artifacts. Good for distributing over slow
125                       connections.
126
127               "hi_x", "mh_x", "ml_x", "lo_x": same as above.
128
129               "hi_h", "mh_h", "ml_h", "lo_h": same as above, but
130                                               using the h.264 codec.
131
132               "hi_s", "mh_s", "ml_s", "lo_s": same as above, but
133                                               the experimental
134                                               SNOW codec.
135
136            Default: "ml"
137
138-e          perform some simple filtering/enhancement to improve image
139            quality. The images created by using this option will be
140            stored in the directory specified by the "-w" option (or
141            "-d" if not present, see above) and each filename will be
142            prefixed with "eimg" (see "-p" above). Using this option will
143            take enormous amounts of disk space, and in reality does
144            not do anything that cannot be done within LiVES itself.
145            Using this option enables the following four:
146
147            -w dir      "dir" is the working directory where the enhanced
148                        images  will be saved. Although these can all
149                        co-exist with the original LiVES files it is
150                        recommended you use a different directory. Note
151                        that temporary files may take a huge amount of
152                        space, perhaps twice as much as the LiVES files
153                        themselves. If this directory does not exist is
154                        will be created.
155                        Default: same as "-d".
156
157            -k         keep the temporary files, useful to experiment
158                       with different encoding types without having
159                       to repeatedly enhance the images (which can be
160                       very time-consuming). See "-p" above.
161
162            -c geom    in addition to enhancing the images, crop them
163                       to the specified geometry e.g. "688x448+17+11".
164                       Filename prefix remains "eimg". Note that the
165                       dimensions of the images should both be multiples
166                       of "16", otherwise some players may show artifacts
167                       (such as garbage or green bars at the borders).
168
169            -r geom    this will create a second set of images resulting
170                       from resizing the images that have already been
171                       enhanced and possibly cropped (if "-c" has been
172                       specified). The geometry here is simple a new
173                       image size e.g. "352x240!". This second set will have
174                       filenames prefixed with the string "rimg" (see "-p"
175                       above). Note that the dimensions of the images
176                       should both be multiples of "16", otherwise some
177                       players may show artifacts (such as garbage or
178                       green bars at the borders).
179
180-s sndfile  name of the audio file. Can be either raw "audio" or
181            "[/path/to/]soundfile.wav".
182            Default: "audio" inside "dir" as specified by "-d".
183
184-b sndrate  sample rate of the sound file in Hertz.
185            Default: "44100".
186
187-f fpscode  frame-rate code. Acceptable values are:
188
189              1 - 24000.0/1001.0 (NTSC 3:2 pulldown converted FILM)
190              2 - 24.0 (NATIVE FILM)
191              3 - 25.0 (PAL/SECAM VIDEO / converted FILM)
192              4 - 30000.0/1001.0 (NTSC VIDEO)
193              5 - 30.0
194              6 - 50.0 (PAL FIELD RATE)
195              7 - 60000.0/1001.0 (NTSC FIELD RATE)
196              8 - 60.0
197
198            If the input value is a float (e.g. 5.0, 14.555, etc.)
199            the encoder will use that as frame rate.
200
201            Default: "4".
202
203-L lv1file  use the images stored in the LiVES file "lv1file" (e.g.
204            "movie.lv1"). Files will be extracted inside "dir" as
205            specified by "-d". Using this option allows to encode
206            a movie without having to restore it first with LiVES.
207            Note that the encoder will not delete the extracted
208            files after it has finished, this must be done manually
209            afterwards. If frame code/rate (-f) and/or sound rate (-b)
210            are not specified the defaults will be used (30000/1001 fps
211            and 44100 Hz). These, however, may not be correct, in which
212            case they should be specified explicitly. Furthermore, the
213            .lv1 audio (if present) is always assumed to have a sample
214            data size in 16-bit words with two channels, and to be signed
215            linear (this is usually the case, however). If any of these
216            latter parameters are incorrect the resulting file may be
217            corrupted and encoding will have to be done using LiVES.
218
219firstframe  first frame number. If less than eight digits long it will
220            be padded with zeroes e.g. 234 -> 00000234. A prefix can
221            be added using the "-p" option.
222            Default: "00000001"
223
224lastframe   last frame number, padded with zeroes if necessary, prefix
225            set by "-p".
226            Default: the last frame of its type in the "-d" directory,
227                     where "type" is set by the prefix.
228
229Note that either no frames or both first and last frames must be specified.
230
231EXAMPLES:
232
233Suppose you have restored a LiVES' .lv1 file (in either JPG or PNG format),
234and that the images are stored in the directory "/tmp/livestmp/991319584/".
235In this example the movie is assumed to be 16:9. Then, in order to create
236an AVI file you can simply do the following:
237
238   lives_avi_encoder3 -d /tmp/livestmp/991319584 -o /movies/default.avi -a 3
239
240and the clip "default.avi" will be created in "/movies/" with the correct
241aspect ratio.
242
243Suppose we want to make a downloadable version of the clip, small in size
244but of good quality. The following command activates the generic enhancement
245filter and resizes the images to "352x240". Note that these operations
246are performed after cropping the originals a bit (using "704x464+5+6").
247This is done because we are assuming that there is a black border around
248the original pictures which we want to get rid of (this might distort the
249images somewhat, so be careful about cropping/resizing, and remember to
250use dimensions which are multiples of 16):
251
252   lives_avi_encoder3 -v -d /tmp/livestmp/991319584 -w /tmp/tmpavi \\
253   -o /movies/download.avi -a 3 -k -e -c "704x464+5+6" -r "352x240"
254
255Since we use the "-k" flag the enhanced images are kept (both full-size
256crops and the resized ones) in "/tmp/tmpavi". Beware that this may consume
257a lot of disk space (about 10x as much as the originals). The reason we
258keep them is because the above may take quite a long time and we may want
259to re-use the enhanced images. So, for example, creating a high-quality
260clip at full size can be accomplished now as follows:
261
262   lives_avi_encoder3 -d /tmp/tmpavi -t hi -o /movies/archive.avi \\
263   -s /tmp/livestmp/991319584/audio -a 3 -k -p eimg
264
265If, for example, we only want to encode frames 100 to 150 we can run
266the following:
267
268   lives_avi_encoder3 -v -d /tmp/tmpavi -o /movies/selection.avi \\
269   -a 3 -k -p eimg 100 150
270
271Note that no audio has been selected ("-s"). This is because
272lives_avi_encoder3 cannot trim the audio to the appropriate length.
273You would need to use LiVES to do this.
274
275To delete all the enhanced images you can just remove "/tmp/tmpavi".
276
277If you notice that the video and audio become out of sync so that
278near the end of the movie the video trails the audio by, say, 0.2s,
279you can use the "-D" option as follows:
280
281   lives_avi_encoder3 -d /tmp/livestmp/991319584 -o test.avi -D 200
282
283Suppose that you have "movie1.lv1", "movie2.lv1" and "movie3.lv1".
284Batch-encoding can be done as follows (zsh-syntax):
285
286   for i in movie?.lv1
287   do
288     mkdir /tmp/$i:r
289     lives_avi_encoder3 -d /tmp/$i:r -o /tmp/$i:r.avi -L $i
290     rm -rf /tmp/$i:r
291   done
292
293This will generate the files "movie1.avi", "movie2.avi" and
294"movie3.avi" in "/tmp". Note that is is not necessary to
295specify whether the files are stored in JPG or PNG format,
296and that potentially time-consuming options (e.g. "-e") may
297be enabled. It is not necessary to have a working LiVES
298installation to encode .lv1 files.
299     """ % version
300
301def run(command):
302    """
303    Run a shell command
304    """
305
306    if verbose:
307        print('Running: \n' + command + '\n=== ... ===')
308        std = ''
309    else:
310        std = ' > /dev/null 2>&1'
311
312    os.system(command + std)
313
314
315def do_enhance():
316    """
317    Image cleanup/crop/resize routine. Generic, but seems to work
318    well with anime :)
319    """
320
321    if not quiet:
322        print('Enhancing images... please wait, this might take long...')
323
324    enh_opts = "-enhance -sharpen '0.0x0.5' -gamma 1.2 -contrast -depth 8"
325
326    if cgeom:
327        docrop = ' '.join(['-crop', "'%s!'" % cgeom])
328    else:
329        docrop = ''
330
331    iframe = first_num
332    while True:
333        # File names are padded with zeroes to 8 characters
334        # (not counting the .ext).
335        frame = str(iframe).zfill(8)
336        fname = os.path.join(img_dir, frame + ext)
337        if not os.path.isfile(fname) or (iframe == last_num + 1):
338            break
339        eimg_fname = os.path.join(work_dir, 'eimg' + frame + '.png')
340        rimg_fname = os.path.join(work_dir, 'rimg' + frame + '.png')
341        command = ' '.join([convert, docrop, enh_opts, fname, eimg_fname])
342        run(command)
343        if rgeom:
344            if os.path.exists(rimg_fname): os.remove(rimg_fname)
345            shrink = ' '.join([convert, '-resize', "'%s!'" % rgeom, '-depth 8'])
346            command = ' '.join([shrink, eimg_fname, rimg_fname])
347            run(command)
348        else:
349            if os.path.exists(rimg_fname): os.remove(rimg_fname)
350            try:
351                os.symlink(eimg_fname, rimg_fname)
352            except (IOError, OSError):
353                shutil.copy(eimg_fname, rimg_fname)
354        iframe+=1
355
356
357def do_encode():
358    """
359    Encode a series of images into an XviD/Snow or h.264 stream, multiplexing
360    audio if necessary
361    """
362
363    if not quiet:
364        print('Creating "%s"-quality video' % vtype)
365
366    if verbose:
367        std = ''
368        lameinfo = '--verbose'
369        meinfo = '-v'
370        hinfo = ':log=-1'
371    else:
372        std = ' > /dev/null 2>&1'
373        lameinfo = '--quiet'
374        meinfo = '-quiet'
375        hinfo = ''
376
377    common_vopts = meinfo + ' -mf type=%s:fps=%s -mc 0 -noskip ' % (ext[1:], fps) + \
378                  '-vf hqdn3d=2:1:3,pp=va:128:8/ha:128:8/dr,dsize=%s' % aspect
379
380    if vtype[-1] == 's':
381        codec_vopts = '-ovc lavc -lavcopts ' + \
382                      'vcodec=snow:vstrict=-2:mbcmp=1:subcmp=1:cmp=1:mbd=2:v4mv' + \
383                      ':qpel:pred=1:autoaspect:vqscale='
384        cpass = ''
385    elif vtype[-1] == 'h':
386        codec_vopts = '-ovc x264 -x264encopts ' + \
387                      'frameref=15:subq=5' + hinfo + \
388                      ':bitrate='
389        cpass = 'pass='
390    else:
391        codec_vopts = '-ovc xvid -xvidencopts qpel:chroma_me:chroma_opt' + \
392                      ':max_bframes=1:autoaspect:hq_ac:vhq=4:bitrate='
393        cpass = 'pass='
394
395    vqual = vtype[0:-2]
396    if vqual == 'hi':
397        if vtype[-1] == 'x': rate = '2000:'
398        if vtype[-1] == 's': rate = '2'
399        if vtype[-1] == 'h': rate = '2000:'
400        mencoder_opts = ' '.join([common_vopts, codec_vopts + rate + cpass])
401        lameenc_opts = '--vbr-new -h -V 0'
402    elif vqual == 'mh':
403        if vtype[-1] == 'x': rate = '1000:'
404        if vtype[-1] == 's': rate = '3'
405        if vtype[-1] == 'h': rate = '1000:'
406        mencoder_opts = ' '.join([common_vopts, codec_vopts + rate + cpass])
407        lameenc_opts = '--vbr-new -h -V 2'
408    elif vqual == 'ml':
409        if vtype[-1] == 'x': rate = '500:'
410        if vtype[-1] == 's': rate = '4'
411        if vtype[-1] == 'h': rate = '500:'
412        mencoder_opts = ' '.join([common_vopts, codec_vopts + rate + cpass])
413        lameenc_opts = '--vbr-new -h -V 4'
414    elif vqual == 'lo':
415        if vtype[-1] == 'x': rate = '200:'
416        if vtype[-1] == 's': rate = '6'
417        if vtype[-1] == 'h': rate = '200:'
418        mencoder_opts = ' '.join([common_vopts, codec_vopts + rate + cpass])
419        lameenc_opts = '--vbr-new -h -V 6'
420
421
422    if img_dir != work_dir and not enhance:
423        source_dir = img_dir
424    else:
425        source_dir = work_dir
426
427    if frame_range:
428        frame_list = [img_pre + str(f).zfill(8) + ext \
429                      for f in range(first_num, last_num + 1)]
430        syml = 'temporary_symlink_'
431        for iframe in frame_list:
432            frfile = os.path.join(source_dir, iframe)
433            frlink = os.path.join(source_dir, syml + iframe)
434            if os.path.islink(frlink): os.remove(frlink)
435            try:
436                os.symlink(frfile, frlink)
437            except (IOError, OSError):
438                shutil.copy(frfile, frlink)
439    else:
440        syml = ''
441
442    aviopts = '-of avi -force-avi-aspect %s -ofps %s' % (aspect, fps)
443
444    all_vars = {}
445    all_vars.update(globals())
446    all_vars.update(locals())
447
448    if audio:
449        if not quiet:
450            print('Creating "%s"-quality mp3 file' % vtype)
451
452        mp3 = tempfile.mkstemp('.mp3', '', work_dir)[1]
453        if rawsndf:
454            wav = tempfile.mkstemp('.wav', '', work_dir)[1]
455            sox_opts = '-t raw -r %s -w -c 2 -s' % sndr
456            command = ' '.join([sox, sox_opts, sndf, wav])
457            run(command)
458        else:
459            wav = sndf
460        command = ' '.join([lameenc, lameinfo, lameenc_opts, wav, mp3])
461        run(command)
462
463        avimp3 = '-audiofile %s -oac copy -audio-delay %s' % (mp3, delay)
464    else:
465        wav = 'dummy'
466        mp3 = 'dummy'
467        avimp3 = '-nosound'
468
469    all_vars.update(locals())
470
471    if not quiet: print('Encoding and multiplexing...')
472    if vtype[-1] == 's':
473        command = """cd %(source_dir)s ; \\
474%(mencoder)s %(aviopts)s %(avimp3)s "mf://%(syml)s%(img_pre)s*%(ext)s" %(mencoder_opts)s -o "%(vidname)s" %(std)s""" % all_vars
475        run(command)
476    else:
477        command = """cd %(source_dir)s ; \\
478%(mencoder)s %(aviopts)s -nosound "mf://%(syml)s%(img_pre)s*%(ext)s" %(mencoder_opts)s1 -o /dev/null %(std)s ; \\
479%(mencoder)s %(aviopts)s %(avimp3)s "mf://%(syml)s%(img_pre)s*%(ext)s" %(mencoder_opts)s2 -o "%(vidname)s" %(std)s
480""" % all_vars
481        run(command)
482
483    if os.path.exists(os.path.join(source_dir, 'divx2pass.log')):
484        os.remove(os.path.join(source_dir, 'divx2pass.log'))
485    if os.path.exists(os.path.join(source_dir, 'xvid-twopass.stats')):
486        os.remove(os.path.join(source_dir, 'xvid-twopass.stats'))
487    if rawsndf and os.path.exists(wav): os.remove(wav)
488    if os.path.exists(mp3): os.remove(mp3)
489
490    if frame_range:
491        lframes = os.path.join(source_dir, syml)
492        for frame in glob.glob(lframes + '*'):
493            os.remove(frame)
494
495
496def do_clean():
497    """
498    Delete enhanced files
499    """
500
501    if not quiet:
502        print('Deleting all enhanced images (if any)')
503
504    eframes = os.path.join(work_dir, 'eimg')
505    rframes = os.path.join(work_dir, 'rimg')
506    for frame in glob.glob(eframes + '*'):
507         os.remove(frame)
508    for frame in glob.glob(rframes + '*'):
509         os.remove(frame)
510
511
512def which(command):
513    """
514    Finds (or not) a command a la "which"
515    """
516
517    command_found = False
518
519    if command[0] == '/':
520        if os.path.isfile(command) and \
521               os.access(command, os.X_OK):
522            command_found = True
523            abs_command = command
524    else:
525        path = os.environ.get('PATH', '').split(os.pathsep)
526        for dir in path:
527            abs_command = os.path.join(dir, command)
528            if os.path.isfile(abs_command) and \
529               os.access(abs_command, os.X_OK):
530                command_found = True
531                break
532
533    if not command_found:
534        abs_command = ''
535
536    return abs_command
537
538
539def is_installed(prog):
540    """
541    See whether "prog" is installed
542    """
543
544    wprog = which(prog)
545
546    if wprog == '':
547        print(prog + ': command not found')
548        raise SystemExit(1)
549    else:
550        if verbose:
551            print(wprog + ': found')
552
553
554if __name__ == '__main__':
555
556    import os
557    import sys
558    import getopt
559    import shutil
560    import tempfile
561    import glob
562    import tarfile
563
564    try:
565        if sys.version_info[0:3] < (3, 0, 0):
566            raise SystemExit(1)
567    except:
568        print('You need Python 3.0.0 or greater to run me!')
569        raise SystemExit(1)
570
571    try:
572        (opts, args) = getopt.getopt(sys.argv[1:], \
573                                         'ho:p:d:w:a:qvt:ekc:r:s:b:f:VCD:L:')
574    except:
575        print("Something's wrong. Try the '-h' flag.")
576        raise SystemExit(1)
577
578    opts = dict(opts)
579
580    if not opts and not args:
581        print(usage)
582        raise SystemExit(1)
583
584    if '-h' in opts:
585        print(usage + help)
586        raise SystemExit
587
588    if '-V' in opts:
589        print('lives_avi_encoder3 version ' + version)
590        raise SystemExit
591
592    if ('-v' in opts) or ('-C' in opts):
593        verbose = True
594    else:
595        verbose = False
596
597    if '-q' in opts:
598        quiet = True
599        verbose = False
600    else:
601        quiet = False
602
603    for i in [convert, mencoder, lameenc, sox]:
604        is_installed(i)
605    if '-C' in opts: raise SystemExit
606
607    img_pre = opts.get('-p', '')
608
609    if img_pre not in ['', 'eimg', 'rimg']:
610         print('Improper image name prefix.')
611         raise SystemExit(1)
612
613    temp_dir = ''
614    img_dir = opts.get('-d', '.')
615    img_dir = os.path.abspath(img_dir)
616    if ' ' in img_dir:
617        temp_dir = tempfile.mkdtemp('', '.lives-', '/tmp/')
618        try:
619            os.symlink(img_dir, temp_dir + '/img_dir')
620        except (IOError, OSError):
621            shutil.copy(img_dir, temp_dir + '/img_dir')
622        img_dir = temp_dir + '/img_dir'
623
624    if not os.path.isdir(img_dir):
625        print('The image source directory: '  + img_dir + \
626              ' does not exist!')
627        raise SystemExit(1)
628
629    if len(args) not in [0, 2]:
630        print('If frames are specified both first and last ' + \
631              'image numbers must be chosen.')
632        raise SystemExit(1)
633    elif len(args) == 0:
634        args = [None, None]
635
636    frame_range = False
637    if not args[0]:
638        first_frame_num = '1'
639    else:
640        frame_range = True
641        first_frame_num = args[0]
642    first_frame_num = first_frame_num.zfill(8)
643    first_num = int(first_frame_num)
644
645    if not args[1]:
646        last_frame_num = '99999999'
647    else:
648        last_frame_num = args[1]
649    last_frame_num = last_frame_num.zfill(8)
650    last_num = int(last_frame_num)
651
652    aspectc = opts.get('-a', '2')
653
654    if aspectc not in [str(i) for i in range(1,5)]:
655        print('Invalid aspect ratio.')
656        raise SystemExit(1)
657    else:
658        if aspectc == '1': aspect = 1.0/1.0
659        elif aspectc == '2': aspect = 4.0/3.0
660        elif aspectc == '3': aspect = 16.0/9.0
661        elif aspectc == '4': aspect = 2.21/1.0
662
663    vtype = opts.get('-t', 'ml')
664
665    out_avi = opts.get('-o', vtype + '_movie.avi')
666
667    if '_' not in vtype:
668        vtype = vtype + '_x'
669
670    if vtype not in ['hi_s', 'mh_s', 'ml_s', 'lo_s', \
671                     'hi_h', 'mh_h', 'ml_h', 'lo_h', \
672                     'hi_x', 'mh_x', 'ml_x', 'lo_x']:
673        print('Invalid video type.')
674        raise SystemExit(1)
675
676    fpsc = opts.get('-f', '4')
677
678    if fpsc not in [str(i) for i in range(1,9)]:
679        if not quiet: print('Invalid fps code, attempting float fps.')
680        foundfps = False
681    else:
682        if fpsc == '1': fps = 24000.0/1001.0
683        elif fpsc == '2': fps = 24.0
684        elif fpsc == '3': fps = 25.0
685        elif fpsc == '4': fps = 30000.0/1001.0
686        elif fpsc == '5': fps = 30.0
687        elif fpsc == '6': fps = 50.0
688        elif fpsc == '7': fps = 60000.0/1001.0
689        elif fpsc == '8': fps = 60.0
690        foundfps = True
691
692    if not foundfps:
693        try:
694            fps = locale.atof(fpsc)
695            if not quiet: print('Using fps = %s' % fps)
696            if fps > 0: foundfps = True
697        except:
698            pass
699
700    if not foundfps:
701        print('Invalid fps code or rate.')
702        raise SystemExit(1)
703
704    if '-e' not in opts:
705        enhance = False
706    else:
707        enhance = True
708
709    if enhance and img_pre:
710        print('Sorry, you cannot enhance already-enhanced images')
711        raise SystemExit(1)
712
713    if '-k' not in opts:
714        keep = False
715    else:
716        keep = True
717
718    cgeom = opts.get('-c', '')
719    rgeom = opts.get('-r', '')
720
721    if (cgeom or rgeom) and not enhance:
722        print('Missing "-e" option.')
723        raise SystemExit(1)
724
725    lv1file = opts.get('-L', None)
726    if lv1file:
727        if not quiet: print('Opening lv1 file...')
728        try:
729            lv1 = tarfile.open(os.path.abspath(lv1file))
730        except:
731            print('This does not appear to be a valid LiVES file!')
732            raise SystemExit(1)
733        if 'header.tar' not in lv1.getnames():
734            print('This does not appear to be a valid LiVES file!')
735            raise SystemExit(1)
736        for tfile in lv1.getmembers():
737            lv1.extract(tfile, img_dir)
738        for tfile in glob.glob(os.path.join(img_dir, '*.tar')):
739            imgtar = tarfile.open(tfile)
740            for img in imgtar.getmembers():
741                imgtar.extract(img, img_dir)
742            os.remove(tfile)
743
744    test_file = os.path.join(img_dir, img_pre + first_frame_num)
745    if os.path.isfile(test_file + '.jpg'):
746        ext = '.jpg'
747    elif os.path.isfile(test_file + '.png'):
748        ext = '.png'
749    else:
750        print('Cannot find any appropriate %s or %s files!' % ('.jpg','.png'))
751        raise SystemExit(1)
752    first_frame = test_file + ext
753    last_frame = os.path.join(img_dir, img_pre + last_frame_num + ext)
754
755    if not quiet: print('Found: ' + first_frame)
756
757    work_dir = opts.get('-w', img_dir)
758    work_dir = os.path.abspath(work_dir)
759    if not os.path.isdir(work_dir):
760        if not quiet: print('Creating ' + work_dir)
761        try:
762            os.makedirs(work_dir)
763            os.chmod(work_dir, 755)
764        except:
765            print('Could not create the work directory ' + \
766                  work_dir)
767            raise SystemExit(1)
768    if ' ' in work_dir:
769        if temp_dir == '':
770            temp_dir = tempfile.mkdtemp('', '.lives-', '/tmp/')
771        try:
772            os.symlink(work_dir, temp_dir + '/work_dir')
773        except (IOError, OSError):
774            shutil.copy(work_dir, temp_dir + '/work_dir')
775        work_dir = temp_dir + '/work_dir'
776
777    sndf = opts.get('-s', os.path.join(img_dir, 'audio'))
778#    sndf = os.path.abspath(sndf)
779    rawsndf = True
780    if not os.path.isfile(sndf):
781        audio = False
782        rawsndf = False
783    else:
784        audio = True
785        if sndf[-4:] == '.wav':
786            rawsndf = False
787        if not quiet: print('Found audio file: ' + sndf)
788
789    sndr = opts.get('-b', '44100')
790
791    # We use here mencoder's -audio-delay which is not really
792    # the true linear delay of the other plugins. See the mencoder
793    # man page. Negative values do not work. Must be in seconds.
794    delay = opts.get('-D', '0')
795    try:
796        delay = '%s' % (locale.atof(delay)/1000.0,)
797    except:
798        print('Invalid audio delay: ' + delay)
799    if verbose and audio: print('Audio delay (ms): ' + delay)
800
801    if enhance:
802        do_enhance()
803        # Note that do_enhance() always creates images prefixed
804        # with 'rimg'. What's important to note if that if the
805        # images are resized the 'rimg' are indeed resized, but
806        # if not the 'rimg' are simply symlinks to the 'eimg'
807        # (enhanced) images.
808        img_pre = 'rimg'
809        ext = '.png'
810    vidname = os.path.join(work_dir, out_avi)
811    # do_encode() acts on images prefixed by img_pre.
812    do_encode()
813    if not keep:
814        do_clean()
815    if temp_dir != '':
816        shutil.rmtree(temp_dir)
817    if not quiet: print("Done!")
818
819
820"""
821CHANGELOG:
822
82303 Jan 2005 : 0.0.1 : first release, based on mkv_encoder.
82404 Jan 2005 : 0.0.2 : added support for h.264.
82510 Mar 2005 : 0.0.3 : fixed snow quality settings.
82626 Mar 2005 : 0.0.4 : use env python (hopefully >= 2.3).
827                      replace denoise3d with hqdn3d.
82824 Aug 2005 : 0.0.5 : fixed 2.21:1 aspect ratio.
829                      fine-tune mplayer filters.
830                      expanded h.264 bit rates.
83109 Mar 2006 : 0.0.6 : added '-depth 8' to resize, as ImageMagick
832                      keeps changing its damn behaviour.
833                      Fixed snow encoding (still highly experimental).
834                      Fixed bitrate levels.
83513 Mar 2006 : 0.0.7 : tweaked bitrate levels.
83628 Jun 2007 : 0.0.8 : handles paths with spaces appropriately
837                      (thanks Gabriel).
83808 Dec 2007 : 0.0.9 : added "-mc 0" and "-noskip" options.
839"""
840