#!/usr/bin/env perl

#---------------------------------------------------------------------
#                          file information
#---------------------------------------------------------------------

# Filename: ocvideoweb
# Purpose:  Video to HTML5 Video conversion tool
# Type:     Linux Perl script
# License:  CC BY-NC-SA 4.0. Attribution: OldCoder (Robert Kiraly).
# Revision: See program parameters section

#---------------------------------------------------------------------
#                           important note
#---------------------------------------------------------------------

# This software is provided on an  AS IS basis with ABSOLUTELY NO WAR-
# RANTY.  The  entire risk as to the  quality and  performance of  the
# software is with you.  Should the software prove defective,  you as-
# sume the cost of all necessary  servicing, repair or correction.  In
# no event will any of the developers,  or any other party, be  liable
# to anyone for damages arising out of use of the software, or inabil-
# ity to use the software.

#---------------------------------------------------------------------
#                              overview
#---------------------------------------------------------------------

                                # Label must be single-quoted here
my $USAGE_TEXT = << 'END_OF_USAGE_TEXT';
Usage: $PROGNAME file1.mp4 file2.mkv ...

1. As of 2020, web sites need to provide both MP4 and WebM versions of
videos. An HTML file is also required.

This is a Linux CLI tool that generates all three files.

Input files are video files.  Four output files  are produced for each
input files:  one MP4-AAC, one WebM-Opus, one HTML, and a shell script
that shows how the first two output files were generated.

The first three output files  should work,  when  used together,  with
most modern web browsers.

Presently, the output files are written to the current directory.  The
directories holding  the input files aren't modified.  Temporary files
are kept in "/tmp".

Videos may  be cropped, rotated, and scaled up or down.  Chapters  may
be defined. Video quality is  set to high by default.  These  features
are  supported  without  the need to figure  out complicated  "ffmpeg"
switches.

----------------------------------------------------------------------

2. Sample commands:

2.1. Produce an MP4 version of a BluRay M2TS rip as follows:

* copy the original audio track exactly
* tune for film as opposed to e.g. animation
* put chapter markers every 5 minutes
* set quality to CRF 28
* scale width down to 1280

ocvideotoweb --acopy --crf 30 --film --mkv \
    --chaptersec 300 --width 1280 everything.m2ts

----------------------------------------------------------------------

3. As noted above, the  program  generates  one shell script per input
file.  Each script handles  transcoding operations  for the associated
input file. The scripts are executed as they're generated.

If  you'd  prefer to  execute  the  scripts yourself,  you can specify
"--script" or "--scripts"  to  generate the scripts  without executing
them.

----------------------------------------------------------------------

4. You can modify the types of files produced as follows:

For MP4-AAC   output without WebM-Opus - specify: --mp4
For WebM-Opus output without MP4_AAC   - specify: --webm
To  disable HTML output                - specify: --nohtml

If  --mp4 and --webm are used together,  both MP4 and WEBM are genera-
ted.

----------------------------------------------------------------------

5. To run much faster at the cost of producing much lower quality out-
put, use this switch:

    --fast

This mode is only recommended if you need to debug code and/or parame-
ters.  The  output files  produced in this mode  shouldn't be  used on
production sites.

----------------------------------------------------------------------

6. To crop, use switches like these:

    # Crop 100 pixels on the left
    --cropleft=100      or --cropleft   100 or --cropl 100

    # Crop 125 pixels on the right
    --cropright=125     or --cropright  125 or --cropr 125

    # Crop 150 pixels at the top
    --croptop=150       or --croptop    150 or --cropt 150

    # Crop 175 pixels at the bottom
    --cropbottom=175    or --cropbottom 175 or --cropb 175

Important:  Cropping is  done before  rotation  and scaling.   So, the
pixel counts should be based on the original input-file dimensions and
orientation.

The words "left", right", "top", and "bottom"  may  be  abbreviated to
the first letter. "bottom" may be abbreviated to "bot" as well.  Addi-
tionally, --cropWORD and --crop-WORD are equivalent.

So, for example, the following two switches have the same effect:

    --cropB 175
    --crop-bot=175

Additional points:

    * Warning: These switches apply to all video input files
    * These switches may be used together in arbitrary combinations
    * Crops that would produce small widths or heights are ignored

----------------------------------------------------------------------

7. To rotate, use switches like these: 

    --rotate=90   or --rotate   90  # Rotate   90 degrees clockwise
    --rotate=180  or --rotate  180  # Rotate  180 degrees clockwise
    --rotate=270  or --rotate  270  # Rotate  270 degrees clockwise

    --rotate=-90  or --rotate  -90  #  90 degrees counter-clockwise
    --rotate=-180 or --rotate -180  # 180 degrees counter-clockwise
    --rotate=-270 or --rotate -270  # 270 degrees counter-clockwise

Warning: These changes are applied to  all input files  in the current
run.

----------------------------------------------------------------------

8. By default, videos are scaled up or down to 640 pixels wide. To se-
lect a different width, use a switch similar to the following:

    --width=720 or --width 720

To disable scaling, use: --noscale

Additional points:

    * Sharpening is done on scale-up
    * Aspect ratio is preserved
    * --width=0 has the same effect as --noscale
    * --width is ignored for values below 50 except for zero

----------------------------------------------------------------------

9. You can optionally add one of the following switches to specify the
nature of the source content:

    # Cartoon
    --animation or --animated or --anime or --cartoon

    # High-quality film
    --film

    # Still image
    --still or --stillimage or --still-image

    # Grainy film
    --grain or --grainy

This may improve quality of MP4-AAC output.

----------------------------------------------------------------------

10. The base  name  for the  four  output files is created  by  adding
"-webver" to the base name of the associated input file.

So,  for  example,  an input file named "foo.mp4" will result in  four
output files named  "foo-webver.mp4",  "foo-webver.webm", "foo-webver.
html", and "foo-webver.sh".

Files  that  contain  "-webver" in their names  are't allowed as input
files.

The output files  are  written to the current directory as  opposed to
the input-file directories.

----------------------------------------------------------------------

11. To  set chapters  of a  fixed length,  use a switch similar to the
following:

    --chapter-length  90 or --chapterlen 90 or
    --chapter-seconds 90 or --chaptersec 90

A switch of this time  will cause chapters of the specified length, in
seconds, to be set.  Note:  The  minimum  chapter length allowed is 30
seconds.

----------------------------------------------------------------------

12. To aim for a particular  number of chapters,  use a switch similar
to the following:

    --chapter-count 5

A switch of this type will cause approx. the specified number of chap-
ters to be generated.

Note:  If the resulting chapter length would be too short,  the switch
will be ignored.

If  both  "--chapter-length" and "--chapter-count" (or equivalent) are
specified, "--chapter-length" takes precedence.

----------------------------------------------------------------------

13. The program has just two non-trivial dependencies: "mediainfo" 19.
09 or above and "ffmpeg" 4.2.1 or above.

The  only complicated part is installing a sufficiently  powerful ver-
sion of "ffmpeg".

"ffmpeg"  needs to be built,  at a minimum,  with  support for  "aac",
"libopus", "libvpx-vp9", and "libx264".  For the program to work well,
"ffmpeg" should be built with support for as many additional libraries
as possible.

The  names of the  "ffmpeg" packages that should be installed are dis-
tro-specific.
END_OF_USAGE_TEXT

#---------------------------------------------------------------------
#                            module setup
#---------------------------------------------------------------------

require 5.16.1  ;
use strict      ;
use Carp        ;
use warnings    ;
                                # Trap warnings
$SIG{__WARN__} = sub { die @_; };

#---------------------------------------------------------------------
#                           basic constants
#---------------------------------------------------------------------

use constant ZERO  => 0;        # Zero
use constant ONE   => 1;        # One
use constant TWO   => 2;        # Two

use constant FALSE => 0;        # Boolean FALSE
use constant TRUE  => 1;        # Boolean TRUE

#---------------------------------------------------------------------
#                         program parameters
#---------------------------------------------------------------------

my $PURPOSE  = 'OldCoder HTML5 video tool';
my $REVISION = '240916';
my $USE_LESS = TRUE;            # Flag: Use "less" for usage text

#---------------------------------------------------------------------
#                          global variables
#---------------------------------------------------------------------

my $PROGNAME;                   # Program name (without path)
   $PROGNAME =  $0;
   $PROGNAME =~ s@.*/@@;

my $OptFast    = FALSE  ;

my $OptACopy   = FALSE  ;
my $OptCPTime  = FALSE  ;
my $OptScripts = FALSE  ;
my $OptHTML    = TRUE   ;
my $OptMKV     = FALSE  ;
my $OptMP4     = FALSE  ;
my $OptNoScale = FALSE  ;
my $OptRotate  = ZERO   ;
my $OptTune    = ""     ;
my $OptWebM    = FALSE  ;
my $OptWidth   = 640    ;
my $OptCRF     = 28     ;
my $OptVCopy   = FALSE  ;
my $OptVol     = ZERO   ;

my $ChapterCnt = ZERO   ;       # Chapter count             (or zero)
my $ChapterSec = ZERO   ;       # Chapter length in seconds (or zero)

my %CropMap    = ();
my %ifname     = ();

#---------------------------------------------------------------------
#                          support routines
#---------------------------------------------------------------------

# "UsageError" prints  usage text for the current program,  then term-
# inates the program with exit status one.

#---------------------------------------------------------------------

sub UsageError
{
    $USAGE_TEXT =~ s@^\s+@@s;
    $USAGE_TEXT =~ s@\$PROGNAME@$PROGNAME@g;

    $USAGE_TEXT = << "END";     # "END" must be double-quoted here
$PROGNAME $REVISION - $PURPOSE

$USAGE_TEXT
END
    $USAGE_TEXT =~ s@\s*\z@\n@s;

    if ($USE_LESS && (-t STDOUT) && open (OFD, "|/usr/bin/less"))
    {
                                # "END" must be double-quoted here
        $USAGE_TEXT = << "END";
To exit this "help" text, press "q" or "Q".  To scroll up or down, use
PGUP, PGDN, or the arrow keys.

$USAGE_TEXT
END
        print OFD $USAGE_TEXT;
        close OFD;
    }
    else
    {
        print "\n", $USAGE_TEXT, "\n";
    }

    exit ONE;
}

#---------------------------------------------------------------------
#                          support routines
#---------------------------------------------------------------------

# HandleSIGINT is a signal handler. It's triggered by Control-C and/or
# SIGTERM interrupts.

sub HandleSIGINT
{
    print STDERR "Error: Received SIGINT or SIGTERM - Aborting\n";
    exit ONE;
}

#---------------------------------------------------------------------

sub abs_frac_part
{
    my ($x) = @_;
    $x = sprintf ("%.3f", ($x - int ($x)));
    $x = -$x if $x < 0;
    $x =~ s@^-+@@;
    $x;
}

#---------------------------------------------------------------------

sub MakeMP4
{
    my ($ifname, $vfcmd, $metacmd) = @_;
    my $cmd;
    die if $ifname =~ m@-webver\.\w+\z@;

    my $ofname =  $ifname;
       $ofname =~ s@^.*/@@;

    my $ext = $OptMKV ? 'mkv' : 'mp4';
    die unless $ofname =~ s@(\.\w{2,4})\z@-webver.$ext@;
    while ($ofname =~ s@-(input|original)\b@@gi) {}

    my $optim =  "-preset slow -crf $OptCRF";
       $optim .= " -tune $OptTune" if length $OptTune; 
       $optim =  "" if $OptFast;

    my $PL = "/tmp/ffmpeg$>-$$";
    print << "END";
rm -fr "$ofname" $PL*
END
    my $vcodec = '-vcodec libx264';
       $vcodec = '-vcodec copy' if $OptVCopy;

    my $acodec = '-acodec aac -b:a 192k';
       $acodec = '-acodec copy' if $OptACopy;
       $acodec = "-acodec aac -b:a 192k -filter:a volume=$OptVol" if $OptVol > ZERO;

    $cmd = << "END";
ffmpeg -y -i $ifname $metacmd $optim
$vfcmd $vcodec $acodec
-passlogfile $PL
$ofname
END
    $cmd =~ s@\s+\z@@s;
    $cmd =~ s@\s+@ @g;

    print << "END";
$cmd
rm -fr $PL*
END
    print << "END" if $OptCPTime;
cptime "$ifname" "$ofname"
END
    undef;
}

#---------------------------------------------------------------------
# WebM-Opus side.

sub MakeWebM
{
    my ($ifname, $vfcmd, $metacmd) = @_;
    my $cmd;
    die if $ifname =~ m@-webver\.\w+\z@;

    my $ofname =  $ifname;
       $ofname =~ s@^.*/@@;

    die unless $ofname =~ s@(\.\w{2,4})\z@-webver.webm@;
    while ($ofname =~ s@-(input|original)\b@@gi) {}

    my $PL = "/tmp/ffmpeg$>-$$";

    print << "END";
rm -fr "$ofname" $PL*
END
    $cmd = << "END";
ffmpeg -y -i $ifname $metacmd
-vcodec libvpx-vp9 -b:v 0 -crf 30 -row-mt 1
$vfcmd
-passlogfile $PL
-pass 1 -an -f webm /dev/null
END
    $cmd =~ s@\s+\z@@s;
    $cmd =~ s@\s+@ @g;
    print "$cmd\n" unless $OptFast && !$OptVCopy;

    my $vcodec = '-vcodec libvpx-vp9';
       $vcodec = '-vcodec copy' if $OptVCopy;

    my $acodec = '-acodec libopus -b:a 192k';
       $acodec = '-acodec copy' if $OptACopy;
       $acodec = "-acodec aac -b:a 192k -filter:a volume=$OptVol" if $OptVol > ZERO;

    $cmd = << "END";
ffmpeg -y -i $ifname $metacmd
-vcodec $vcodec -b:v 0 -crf 30 -row-mt 1
$vfcmd
-passlogfile $PL
-pass 2 $acodec $ofname
END
    if ($OptFast || $OptVCopy)
    {
        $cmd =~ s@-pass 2 @ @;
        $cmd =~ s@-crf \d+ @ @;
        $cmd =~ s@-passlogfile \S+ @ @;
    }

    $cmd =~ s@\s+\z@@s;
    $cmd =~ s@\s+@ @g;

    print << "END";
$cmd
rm -fr $PL*
END
    print << "END" if $OptCPTime;
cptime "$ifname" "$ofname"
END
    undef;
}

#---------------------------------------------------------------------

sub ProcFile
{
    my ($ifname) = @_;
    my $nn;
    my $str;

#---------------------------------------------------------------------

    my $dim = $ifname {$ifname};
    die unless $dim =~ m@^(\d+),(\d+),(\d+)\z@;
    my ($w, $h, $duration) = ($1, $2, $3);

#---------------------------------------------------------------------

    my $nw;
    my $nh;

    if ($OptNoScale)
    {
        $nw = $w;
        $nh = $h;
    }
    else
    {
        my $r = $h / $w;
        $nw = $OptWidth;
        while ((&abs_frac_part ($nh = $r * $nw) > 0.125) ||
               (($nh & ONE) != ZERO)) { $nw--; }
        $nh = int ($nh);
    }

#---------------------------------------------------------------------

    my $vfcmd = "";

#---------------------------------------------------------------------

    my $cropw = $nw  ;
    my $croph = $nh  ;
    my $cropx = ZERO ;
    my $cropy = ZERO ;

    if (defined ($nn = $CropMap {'T' }) && ($nn > ZERO))
        { $croph -= $nn; $cropy =  $nn;  }

    if (defined ($nn = $CropMap {'B' }) && ($nn > ZERO))
        { $croph -= $nn; }

    if (defined ($nn = $CropMap {'L' }) && ($nn > ZERO))
        { $cropw -= $nn; $cropx =  $nn;  }

    if (defined ($nn = $CropMap {'R' }) && ($nn > ZERO))
        { $cropw -= $nn; }

    if ($cropw < 50) { $cropx = ZERO; $cropw = $nw; }
    if ($croph < 50) { $cropy = ZERO; $croph = $nh; }

    if (($cropx != ZERO) || ($cropy != ZERO) ||
        ($cropw != $nw ) || ($croph != $nh ))
    {
        $vfcmd .= "," if length ($vfcmd);
        $vfcmd .= "crop=$cropw:$croph:$cropx:$cropy";
    }

#---------------------------------------------------------------------

    if ($OptRotate == 90)
    {
        $vfcmd .= "," if length ($vfcmd);
        $vfcmd .= "transpose=1";
    }
    elsif ($OptRotate == 180)
    {
        $vfcmd .= "," if length ($vfcmd);
        $vfcmd .= "transpose=2,transpose=2";
    }
    elsif ($OptRotate == 270)
    {
        $vfcmd .= "," if length ($vfcmd);
        $vfcmd .= "transpose=2";
    }

#---------------------------------------------------------------------
# This must be the last video-filter setup section.

    my $scale = "";

    if ($OptNoScale)
    {                           # Don't scale
    }
    elsif ($nh > $h)
    {
        $scale = << "END";
scale=$nw:$nh,unsharp=3:3:1 -sws_flags lanczos
END
    }
    elsif ($nh < $h)
    {
        $scale = << "END";
scale=$nw:$nh -sws_flags lanczos
END
    }

    $scale =~ s@\s+\z@@s;

    if (length ($scale))
    {
        $vfcmd .= "," if length ($vfcmd);
        $vfcmd .= $scale;
    }

#---------------------------------------------------------------------

    my $metafile = "";
    my $metacmd  = "";

    if (($ChapterCnt > ZERO) && ($ChapterSec == ZERO))
    {
        $ChapterSec = int (($duration / $ChapterCnt) + 0.5);
        $ChapterSec = ZERO if $ChapterSec < 10;
    }

    if ($ChapterSec > ZERO)
    {
        $metafile = "/tmp/meta$>-$$.dat";
        open (OFD, ">$metafile") || die;
        select OFD;

        print << "END";
;FFMETADATA1
title=Havana
artist=Unspecified
END
        my $CN      = ONE;
        my $TimePos = ZERO;

        while (($TimePos + $ChapterSec) < $duration)
        {
            my $msstart = $TimePos * 1000;
            my $msend   = ($TimePos + $ChapterSec) * 1000 - ONE;
            print << "END";

[CHAPTER]
TIMEBASE=1/1000
START=$msstart
END=$msend
title=Chapter $CN
END
            $CN++;
            $TimePos += $ChapterSec;
        }    

        print << "END";

[STREAM]
title=Finish
END
        select STDOUT;
        close OFD;
        $metacmd ="-i $metafile -map_metadata 1";
    }

#---------------------------------------------------------------------

    $vfcmd = "-vf $vfcmd" if length $vfcmd;
    $str   =  $ifname;
    $str   =~ s@\.\w+\z@@;

    die if $str eq $ifname;
    $str =~ s@.*/@@;

    open (OFD, ">$str-webver.sh") || die;
    select OFD;

    &MakeMP4  ($ifname, $vfcmd, $metacmd) if $OptMP4  ;
    &MakeWebM ($ifname, $vfcmd, $metacmd) if $OptWebM ;
    print "rm -f $metafile\n" if length $metafile;

    select STDOUT;
    close (OFD) || die;
    system "bash ./$str-webver.sh" unless $OptScripts;

#---------------------------------------------------------------------

    my $ext = $OptMKV ? 'mkv' : 'mp4';
    $OptHTML = FALSE if $OptMKV;

    $str = $ifname;
    die unless $str =~ s@\.\w+\z@-webver.$ext@;
    my $mp4file = $str;

    $str = $ifname;
    die unless $str =~ s@\.\w+\z@-webver.webm@;
    my $webmfile = $str;

    my $file_lines = "";
       $file_lines .= << "END" if $OptWebM  ;
    <source src="$webmfile" type="video/webm" >
END
       $file_lines .= << "END" if $OptMP4   ;
    <source src="$mp4file"  type="video/mp4"  >
END
    $file_lines =~ s@\s+\z@@s;

    if ($OptHTML)
    {
        $str = $ifname;
        die unless $str =~ s@\.\w+\z@.html@;
        $str =~ s@.*/@@;

        my     $html_file = $str;
        unlink $html_file;

        open (OFD, ">$html_file") || die;
        select (OFD);

        print << "END";
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Video clip: ADD TITLE HERE</title>
    <meta charset="utf-8">
    <meta name="viewport"
        content="width=device-width, initial-scale=0.50" />
    <meta name="author" content="Twitter"  />
    <meta name="robots" content="noarchive"     />
    <link rel="canonical"
          href="https://vector.boldcoder.com/v/CatMirrorTest.html" />
    <link href="../css/oldcoder.css" rel="stylesheet">

    <style type="text/css">
    </style>
</head>

<!-- ------------------------------------------------------------- -->
<!--                      start of body block                      -->
<!-- ------------------------------------------------------------- -->

<body class="bodybg bodyfont">

<!-- ------------------------------------------------------------- -->
<!--                         general post                          -->
<!-- ------------------------------------------------------------- -->

<div class="ocbox">
<p>
<span class="trlabel">If it doesn't start:</span>
scroll down and press triangle at bottom of the video.
<span class="trlabel">To exit:</span>
Press browser or phone Back button. For more notes, look
below the video.</span>
</p><p>
<span class="trlabel">Title:</span>
ADD TITLE HERE<br />

<span class="trlabel">Categories:</span>
ADD CATEGORIES HERE
</p>

<video controls autoplay width="$nw">
$file_lines
Sorry; your browser doesn't support HTML5 video
</video>
<br clear="all" /><br />

<p>
</p>
</div>

<!-- statstart -->
<script type="text/javascript">
var sc_project=8950519;
var sc_invisible=1;
var sc_security="8ba87875";
var scJsHost = (("https:" == document.location.protocol) ?
"https://secure." : "http://www.");
document.write("<sc"+"ript type='text/javascript' src='" +
scJsHost+
"statcounter.com/counter/counter.js'></"+"script>");
</script>
<noscript><div class="statcounter"><a title="free hit
counter" href="http://statcounter.com/" target="_blank"><img
class="statcounter"
src="http://c.statcounter.com/8950519/0/8ba87875/1/"
alt="free hit counter"></a></div></noscript>
<!-- statend -->

</body>
</html>
END
        select (STDOUT);
        close (OFD) || die;
    }

    undef;
}

#---------------------------------------------------------------------
#                            main routine
#---------------------------------------------------------------------

sub Main
{
    my $OptRun;                 # Flag: "--run" option
    my $str;                    # Scratch (string)

#---------------------------------------------------------------------
# Initial setup.

    select STDERR; $| = ONE;    # Force STDERR flush on write
    select STDOUT; $| = ONE;    # Force STDOUT flush on write
                                # Install a signal handler
    $SIG {INT} = $SIG {TERM} = "HandleSIGINT";

#---------------------------------------------------------------------
# Check for external programs.

    for my $prog (qw (which ffmpeg mediainfo))
    {
        $str = `which $prog 2>/dev/null`;
        $str = "" unless defined $str;
        $str =~ s@\s+\z@@s;
        $str =  "" unless -f $str;

        die "Error: This program requires \"$prog\"\n"
            unless length $str;
    }

#---------------------------------------------------------------------
# Process command-line arguments.

    my $CropWords = '[lrtb]|left|right|top|bot|bottom';

    for my $ii (ZERO .. $#ARGV)
    {
        my $arg     = $ARGV [$ii];
        next unless length $arg;
        my $nextarg = "";
           $nextarg = $ARGV [$ii+ONE] if $ii < $#ARGV;

        if ($arg =~ m@[a-z0-9/._-]+\.[a-z0-9]{2,4}\z@i)
        {
            die "Error: File is missing or inaccessible: $arg\n"
                unless -f $arg && -r $arg;
            die "Error: Zero-length file: $arg\n" if -z $arg;
            print "Warning: Skipping -webver file: $arg\n"
                if $arg =~ m@-webver\.\w+\z@;

            $str = `mediainfo $arg 2>/dev/null`;
            $str =~ s@\t+@ @g;
            $str =  "\n" . $str;

            my ($w) = $str =~    m@\nWidth +: *([0-9, ]+?) +pixel@i;
            my ($h) = $str =~   m@\nHeight +: *([0-9, ]+?) +pixel@i;
            my ($t) = $str =~ m@\nDuration +: *(\S.+)@i;

            if (!defined ($w) || !defined ($h) || !defined ($t))
            {
                die "Error: Unsupported file type: $arg $str\n";
            }

            $w =~ s@\D+@@g;
            $h =~ s@\D+@@g;
            $t =~ s@\d+ ms\b@@;

            my $duration = ZERO;
               $duration += 3600 * $1 if $t =~ m@(\d+) h@;
               $duration +=   60 * $1 if $t =~ m@(\d+) m@;
               $duration +=        $1 if $t =~ m@(\d+) s@;

            $ifname {$arg} = "$w,$h,$duration";
        }

        elsif ($arg =~ m@^-+acopy\z@i       ) { $OptACopy   = TRUE ; }
        elsif ($arg =~ m@^-+cptime\z@i      ) { $OptCPTime  = TRUE ; }
        elsif ($arg =~ m@^-+fast\z@i        ) { $OptFast    = TRUE ; }
        elsif ($arg =~ m@^-+mkv\z@i         ) { $OptMKV     = TRUE ; }
        elsif ($arg =~ m@^-+mp4\z@i         ) { $OptMP4     = TRUE ; }
        elsif ($arg =~ m@^-+no\W*scale\z@i  ) { $OptNoScale = TRUE ; }
        elsif ($arg =~ m@^-+webm\z@i        ) { $OptWebM    = TRUE ; }
        elsif ($arg =~ m@^-+scripts?\z@i    ) { $OptScripts = TRUE ; }
        elsif ($arg =~ m@^-+vcopy\z@i       ) { $OptVCopy   = TRUE ; }

        elsif ($arg =~ m@^-+no\W*html5?\z@i ) { $OptHTML    = FALSE; }

        elsif ($arg =~ m@^-+(anim|cartoon)\w*\z@i   )
            { $OptTune = "animation"    ;           }
        elsif ($arg =~ m@^-+film\w*\z@i             )
            { $OptTune = "film"         ;           }
        elsif ($arg =~ m@^-+grain\w*\z@i            )
            { $OptTune = "grain"        ;           }
        elsif ($arg =~ m@^-+still-?\w*\z@i          )
            { $OptTune = "stillimage"   ;           }

        elsif ($arg =~ m@^-+chapter\S*(len|sec)\w*=(\d+)@)
        {
            $ChapterSec = $2;
        }
        elsif ($arg =~ m@^-+chapter\S*(len|sec)\w*\z@ &&
               $nextarg =~ m@^\d+\z@)
        {
            $ChapterSec = $nextarg;
            $ARGV [$ii+ONE] = "";
        }

        elsif ($arg =~ m@^-+crf=(\d+)@)
        {
            $OptCRF = $2;
        }
        elsif ($arg =~ m@^-+crf\z@ &&
               $nextarg =~ m@^\d+\z@)
        {
            $OptCRF = $nextarg;
            $ARGV [$ii+ONE] = "";
        }

        elsif ($arg =~ m@^-+vol=(\d+\.?\d*)\z@)
        {
            $OptVol = $1;
        }
        elsif ($arg =~ m@^-+vol\z@ &&
               $nextarg =~ m@^\d+\.?\d*\z@)
        {
            $OptVol = $nextarg;
            $ARGV [$ii+ONE] = "";
        }

        elsif ($arg =~ m@^-+chapter\S*(count)\w*=(\d+)@)
        {
            $ChapterCnt = $2;
        }
        elsif ($arg =~ m@^-+chapter\S*(count)\w*\z@ &&
               $nextarg =~ m@^\d+\z@)
        {
            $ChapterCnt = $nextarg;
            $ARGV [$ii+ONE] = "";
        }

        elsif ($arg =~ m@^-+crop\W*($CropWords)=(\d+)@i)
        {
            my ($a, $b) = ($1, $2);
            $a   =  uc ($1);
            $a   =~ s@^(.).+@$1@;
            $CropMap {$a} = $b;
        }
        elsif ($arg =~ m@^-+crop\W*($CropWords)\z@i &&
               length ($str = $1) &&
               $nextarg =~ m@^\d+\z@)
        {
            $str =  uc ($str);
            $str =~ s@^(.).+@$1@;
            $CropMap {$str} = $nextarg;
            $ARGV [$ii+ONE] = "";
        }

        elsif ($arg =~ m@^-+width=(\d+)@)
        {
            $OptNoScale = TRUE if $1 <  ONE ;
            $OptWidth   = $1   if $1 >= 50  ;
        }
        elsif ($arg =~ m@^-+width\z@ &&
               $nextarg =~ m@^\d+\z@)
        {
            $OptNoScale = TRUE     if $nextarg < ONE ;
            $OptWidth   = $nextarg if $nextarg >= 50 ;
            $ARGV [$ii+ONE] = "";
        }

        elsif ($arg =~ m@^-+rotate=(-?\d+)@)
        {
            $OptRotate  = $1;
        }
        elsif ($arg =~ m@^-+rotate\z@ && $nextarg =~ m@^-?\d+\z@)
        {
            $OptRotate  = $nextarg;
            $ARGV [$ii+ONE] = "";
        }

        else
        {
            die "Invalid argument: $arg\n";
        }
    }

#---------------------------------------------------------------------
# Post-argument checks and adjustments.

    &UsageError() unless scalar keys %ifname;

    if (!$OptMP4 && !$OptWebM)
    {
        $OptWebM = FALSE ;
        $OptMP4  = TRUE  ;
    }

    $OptRotate  = 360 + $OptRotate if $OptRotate < ZERO;

    $OptCRF     = 18 if $OptCRF < 18;
    $OptCRF     = 60 if $OptCRF > 60;

    die "Error: Invalid --chapter-length setting\n"
        if ($ChapterSec < 30) && ($ChapterSec != ZERO);

    die "Error: Invalid --rotate setting\n"
        unless $OptRotate =~ m@^(0|90|180|270)\z@;

#---------------------------------------------------------------------
# Perform operations.

    for my $ifname (sort keys %ifname)
    {
        if ($ifname =~ m@-webver[^/]*\z@i)
        {
            print << "END";
Warning: Skipping file due to "-webver" in base filename:
$ifname
"-webver" files are output files and can't be used as input files.
END
        }

        &ProcFile ($ifname);
    }

    undef;
}

#---------------------------------------------------------------------
#                            main program
#---------------------------------------------------------------------

&Main();                        # Call the main routine
exit ZERO;                      # Normal exit
