2011年2月1日火曜日

ふわふわさん part2

(Chikuzen) TheFluff氏が叫んでますな
(Chikuzen) 「空の境界」ってもともとSD制作だったの?
(JEEB) いや
(JEEB) ハイビジョン制作っす
(JEEB) 画像を見れば一部だけがなぜかワープシャープかかってる
(Chikuzen) なぜにwarpsharp...
(JEEB) その答えを見つけるためとりあえずマスタリングした会社の連絡先を見つけないとなぁ・・・
(Chikuzen) そこだけmasterなくしちゃったから、DVDソース使ったとか

カワイソス

2011年1月31日月曜日

中間出力のマルチプロセス化

先日、JEEB氏がAviSynth2.6.0αの非公式ビルドを始めた。

(JEEB) そういや、うちのAvs2.6ビルドってChikuzen氏がテストした奴と性能がどんな感じ?
(Chikuzen) 若干スピードが上がってるみたいよ
(Chikuzen) ちょっとベンチマーク回してみるか
      official 2.6.0α2       JEEB's 2.6.0(20110125)
1st   00:01:11.003(14.08fps)   00:01:07.254(14.87fps)
2nd   00:01:11.010(14.08fps)   00:01:07.003(14.92fps)
3rd   00:01:10.753(14.13fps)   00:01:07.003(14.92fps) (スクリプト等は前回と同じ)
(JEEB) interesting

たしかに興味深い。
どうやら最適化はα2配布後にさらに進んでいたようである。

さて、前回、自分はマルチスレッドによる高速化ではなく、マルチプロセスによる高速化を選んだことを書いた。
ここで問題となるのが、同時にエンコードするための複数のソースがない場合である。
そもそも自分は放送ソースの保存用エンコなんてまったくしない。そんなことには興味がない。
いろいろなフィルタやエンコーダーを試して遊ぶのが好きなのである。
当然、エンコードするべきソースは大抵1本しかなく、3本同時にエンコードなんていままでに数えるほどしかやったことがない。

では、どうすればいいのか?
エンコード対象がひとつしかないのであれば、ひとつを複数に分割してやればいいだけだ。
3本同時にエンコードできる余裕があるなら、1本を3分割してそれぞれ中間出力し、ふたたびくっつけてやればよい。
それが簡単にできるのがAviSynthである。
中間出力用のファイルサイズを気にするような時代でもないしね。

てことで、自動で分割->中間出力->結合->エンコードを行うAvsPマクロを書いた。
#multiprocess intermediate file generator.py
#dividing source script into X -> generate intermediate files at same time -> final encode
#requiaments : VirtualDub.exe, vcf file, ffmpeg or x264 (optional)
import sys
import os.path
import subprocess

vdub = r'C:\VirtualDub-1.9.11\VirtualDub.exe'
vcf  = r'C:\VirtualDub-1.9.11\fastrecomp_uly0_noaudio.vcf'

entries = avsp.GetTextEntry(['Number of dividing', 'SetMemoryMax(?)'], ['3', '1024'])
dnum, mmax = [int(i) for i in entries]

imf_outdir = avsp.GetDirectory('Select a directory to output intermediate files.')

encbin  = avsp.GetFilename('Set Path to Encoder(ffmpeg or x264)')
#encbin = r'G:\Enctools\ffmpeg.exe'
#encbin = r'G:\Enctools\x264_x64.exe'
encout  = avsp.GetSaveFilename('Set final output file name')
encopts = avsp.GetTextEntry('Input commandline options').split(' ')

source = avsp.SaveScript()
frames = avsp.GetVideoFramecount()

if dnum > 1 and mmax > 0 and imf_outdir and frames and os.path.isfile(vdub) and os.path.isfile(vcf):
    start = 0
    vdubprocess = []
    catfilename = os.path.splitext(source)[0] + '_imf.avs'
    catfile = open(catfilename, 'w')

    for i in range(dnum):
        end = frames / dnum * (i + 1)
        if i < dnum - 1:
            trimline = 'trim(%i, %i)' % (start, end)
        else:
            trimline = 'trim(%i, 0)' % start
        lines = 'SetMemoryMax(%i)\nImport("%s")\n%s\n' % (mmax, source, trimline)
        start = end + 1

        imf_avsname = os.path.join(imf_outdir, os.path.basename(source)).rstrip('avs') + 'imf_%02i.avs' % i
        file = open(imf_avsname, 'w')
        file.write(lines)
        file.close()

        imf_aviname = os.path.splitext(imf_avsname)[0] + '.avi'
        vdubargs = [vdub, '/s', vcf, '/p', imf_avsname, imf_aviname, '/r', '/c', '/x']
        p = subprocess.Popen(vdubargs)
        vdubprocess.append(p)

        if i < dnum - 1:
            catfile.write('AVISource("%s") + \\\n' % imf_aviname)
        else:
            catfile.write('AVISource("%s")\n' % imf_aviname)

    catfile.close()

    if encbin and encout:
        for p in vdubprocess:
            p.wait()

        if os.path.basename(encbin).count('ffmpeg'):
            encargs = [encbin, '-i', catfilename] + encopts + ['-y', encout]
        elif os.path.basename(encbin).count('x264'):
            encargs = [encbin, catfilename, '-o', encout] + encopts
        else:
            sys.exit()

        subprocess.Popen(encargs)

2011年1月18日火曜日

AviSynth2.6.0α

さまざまな試行と思索(ってほどのものでもないが)の結果、現在の自分はAviSynth2.6.0α(32bit)を使っている。

Q.なぜ64bitAviSynthじゃないの?
A.いろいろ不具合が多い上に開発が止まっているから。
 開発が順調なら人柱覚悟で使ってバグレポートもするが、現状停止中でforkする人もいないでは使う甲斐がない。
 まあ、気長に待つしかないかなぁ。

Q.なんでMT使わないの?
A.SetMTMode()はとにかく不安定すぎる。
 しかも同じスクリプトでもクラッシュしたりしなかったりと運用で回避を図ろうにも傾向がつかめない。
 たとえ速くても、完走出来ないマラソンランナーは正選手にはなれないのである。
 MT()のほうはSetMTMode()ほどひどくはないが…。

Q.なんでstableな2.5.8ではなく、2.6.0αなの?
A.安定していて速いから。

そう、2.6.0αはまだアルファなのに安定している。
どれくらい安定しているかといえば、既知の問題がα2公開後1年以上経つのにこの程度しかないくらい堅い。
しかも2.5.8に存在するバグもいくつか取れている。(これではどちらがstableなのかと小一時間…)

そして速い。 2.5.8->2.6.0の最適化の進み具合はすさまじい。 例えば次のようなベンチマークをしてみるとよくわかる。
#benchmark.avs
ImageReader("test_1280x720.bmp", 0, 999)
BlackmanResize(720, 480)
Spline64Resize(1920, 1080)
GaussResize(512, 384)

$ for i in {1..3}; do avs2avi benchmark.avs -c null -o n; done
これを2.5.8と2.6.0αでやってみると結果は
              2.5.8                   2.6.0α
1st   00:02:26.256(6.84fps)    00:01:10.753(14.13fps)
2nd   00:02:26.256(6.84fps)    00:01:10.764(14.13fps)
3rd   00:02:26.257(6.84fps)    00:01:10.753(14.13fps)  (CPU使用率はすべて30%弱)
特に重めなresizerをRGB32でかけた結果がこれである。
たまに高速化のためと称してMT()使って縦横別々にリサイズとかしてる人を見かけるけど、そんなもの手間をかけて無駄にCPU使用率を上げているだけですな。

2.6.0αはMT対応はしていないが、それ自体は高速である。
問題はプラグインのほうで、これがボトルネックになるわけだが…でもその場合はCPU自体はそれほど使ってないよね?
CPUに余裕があるなら同時に複数本走らせればいいだけだ。
この場合、1本あたりのスピードは低下してしまうが、3本同時にやれば終了するまでにかかる時間は1本ずつ3本エンコするのと比べてだいたい半分程度にはなるだろう。
1本あたり最大2GBのメモリを使うことになるが(pipeを使えばもう少し増える)、自分の環境は64bitOSで8GBつんでいるので、まだ余裕はある。
・スピードが結果的に倍程度まであがり
・CPUを無駄なく使い
・安定した出力を得られ
・互換性も特に気にする必要がない

現状ではやはりこれが一番いいんじゃないかなぁ。

2011年1月11日火曜日

RawSource.dll

AviSynthのRawSourceがffmpegやy4m.auoで出力したY4Mファイルを読めないので何とかしたいというお話。

YUV4MPEG2(Y4M)は非圧縮なYUVデータに動画の解像度やフレームレートといったものをヘッダーとして追加したシンプルなファイル形式である。
もともとはMJPEGToolsなるもので使われていたものらしいが、なかなか取り扱いが便利なので、今日ではffmpeg,MEncoder,x264といろいろなエンコーダーがY4M入力に対応している。
たとえばx264はただのYUVもY4Mも入力に使用出来るが、ただのYUVだと
$ x264 input.yuv --input-csp i420 --input-res 720x480 --fps 24000/1001 --sar 40:33 \
[other options] -o output.h264
てな具合にオプションを指定しなければならない。
一方、Y4Mファイルであれば、解像度等はヘッダーに書かれているので
$ x264 input.y4m [other options] -o output.h264
と、手間が省ける。
うん、便利だね。

さて、RawSource.dllは非圧縮ファイルを扱うためのAviSynthプラグインである。
このRawSource、Y4Mも読めることになってはいるのだが、いかんせん書かれたのが2006年と古いせいか、想定されているヘッダーが旧式である。ffmpegやAviUtlのYUV4MPEG2出力プラグインで出力される新しい形式(Cタグで色空間指定付)のものだと"YUV4MPEG2 header error"となって手が出ない。
「だれか何とかしてくれないかなー」と最初にこのことに気づいてから、かれこれ一年半ほど待ってみたが、そのような奇特な人はついに現れなかった(まあ黙って待っててもそうそう都合のいいことが起こるわけもないわな)。
そこで一念発起して自分でやってみることにしたのである。

まずC言語入門講座とかその手のサイトを2つほど回って、Hello Worldとかfizzbuzzとかを書くこと5日間。
そろそろ出来るかなと、ためしにやってみたら本当に出来た。ちなみに一番難しかったのはVisualStudioの操作方法だった。なんでAviSynthプラグインはmingwでビルドできないんだよチクショウめ。

とりあえず出来上がったものはDoom9に投稿したので、あとはツッコミ待ちである。

2010年12月19日日曜日

TAEC.py

またまた某所にて
(silverfilain) どうせならMaxMBPSとMaxFSも考慮して幅・高さ・fpsから
最適なレベルを表示するのも作って欲しいぉ
(Chikuzen) じゃあ、ちょっと調べてみる
てな感じで、また書くことになった。
#!/bin/env python
# coding: utf-8
#****************************************************************************
#  TAEC(Tiny Avc Encode Consultant).py 
#                                                     written by Chikuzen
#  Reference literature:
#    Rec. ITU-T H.264 (03/2010) – Prepublished version
#    インプレス標準教科書シリーズ改訂版 H.264/AVC教科書
#           著者:(監修)大久保 榮/(編者)角野 眞也、菊池 義浩、鈴木 輝彦
#    http://en.wikipedia.org/wiki/H.264/MPEG-4_AVC
#    猫科研究所( http://www.up-cat.net/ )
#         x264(vbv-maxrate,vbv-bufsize,profile,level),H.264(Profile/Level)
#
#****************************************************************************

__version__ = '0.3.3'
import sys
import getopt
import math

def set_default():
    width   = int(1280) 
    height  = int(720)
    fpsnum  = int(30000)
    fpsden  = int(1001)
    profile = 'high'
    mode    = 'progressive'
    return [[width, height], [fpsnum, fpsden], profile, mode]

def usage():
    param = set_default()
    print "\nUsage: taec.py [options]\n"
    print "  -r, --resolution <string> :set 'width x height' ('%ix%i')" % tuple(param[0])
    print "  -f, --fps <string>        :set 'fpsnum / fpsden' ('%i/%i')" % tuple(param[1])
    print "  -p, --profile <string>    :set 'profile' ('%s')" % param[2]
    print "  -i, --interlaced          :specify interlaced mode (not specified)"
    print "  -v, --version             :display version"
    print "  -h, --help                :display this help and exit\n"

def check_res_and_fps(arg, r_or_f):
    try:
        param = [abs(int(i)) for i in arg.split('x' * r_or_f or '/')]
        if len(param) != 2:
            raise SyntaxError
    except:
        print "\nERROR : invalid %s setting." % ('resolution' * r_or_f or 'fps')
        usage()
        sys.exit()
    else:
        return param

def check_profile(profile, ipflag):
    if profile in ('baseline', 'main', 'high'):
        if profile != 'baseline' or ipflag != 'interlaced':
            return 1
        else:
            print "\nERROR : baseline cannot accept interlaced."
    print "\nERROR : invalid profile setting."
    usage()
    sys.exit()

def calc_bs(resolution, fps, ipflag):
    fstmp = [int(math.ceil(i / 16.0)) for i in resolution]
    fs = dbp = fstmp[0] * fstmp[1]
    mbps  = fs * fps[0] // fps[1]
    if ipflag == 'interlaced':
        dbp += fstmp[0] * (fstmp[1] % 2)
    return [mbps, fs, dbp]

def calc_lv(bs, ipflag, spec):
    for i in spec:
        if bs[0] <= i[1] and bs[1] <= i[2] and bs[2] <= i[3]:
            return i[0]
    if ipflag == 'interlaced':
        print "ERROR : interlaced encoding cannot be done to this video."
    print "ERROR : there is no suitable setting."
    usage()
    sys.exit()

def calc_result(profile, bitstream, line):
    vbv = [int(i * ((profile == 'high') * 1.25 or 1)) for i in line[4]]
    ref = [16 * (i > 16) or i for i in [line[3] // bitstream[2]]]
    return tuple([line[0]] + vbv + ref)

def display_result(level, profile, bitstream, spec):
    index = [i[0] for i in spec]
    try:
        for i in xrange(len(index)):
            if index[i] == level:
                line = spec[i]
                print "%5s%12i%13i%8i" % calc_result(profile, bitstream, line)
                level = index[i + 1]
    except:
        return 0

def get_spec():
#H.264/AVC spec [(level, MaxMBPs, MaxFS, MaxDbpMBs, [MaxBR, MaxCPB], ipflag]}
    return [('1.0',   1485,    99,    396, [    64,    175], 'p'),
            ('1b ',   1485,    99,    396, [   128,    350], 'p'),
            ('1.1',   3000,   396,    900, [   192,    500], 'p'),
            ('1.2',   6000,   396,   2376, [   384,   1000], 'p'),
            ('1.3',  11880,   396,   2376, [   768,   2000], 'p'),
            ('2.0',  11880,   396,   2376, [  2000,   2000], 'p'),
            ('2.1',  19800,   792,   4752, [  4000,   4000], 'i'),
            ('2.2',  20250,  1620,   8100, [  4000,   4000], 'i'),
            ('3.0',  40500,  1620,   8100, [ 10000,  10000], 'i'),
            ('3.1', 108000,  3600,  18000, [ 14000,  14000], 'i'),
            ('3.2', 216000,  5120,  20480, [ 20000,  20000], 'i'),
            ('4.0', 245760,  8192,  32768, [ 20000,  25000], 'i'),
            ('4.1', 245760,  8192,  32768, [ 50000,  62500], 'i'),
            ('4.2', 491520,  8192,  34816, [ 50000,  62500], 'p'),
            ('5.0', 589824, 22080, 110400, [135000, 135000], 'p'),
            ('5.1', 983040, 36864, 184320, [240000, 240000], 'p')]

def set_param(opts, param):
    for opt, arg in opts:
        if opt in ("-r", "--resolution"):
            param[0] = check_res_and_fps(arg, 1)
        elif opt in ("-f", "--fps"):
            param[1] = check_res_and_fps(arg, 0)
        elif opt in ("-p", "--profile"):
            param[2] = arg
        elif opt in ("-i", "--interlaced"):
            param[3] = 'interlaced'
        elif opt in ("-h", "--help"):
            usage()
            sys.exit()
        elif opt in ("-v", "--version"):
            print "tiny avc encode consultant %s" % __version__
            sys.exit()
    return param

if __name__ == '__main__':
    try:
        opts, args = getopt.getopt(sys.argv[1:], "r:f:p:ihv",
            ["resolution=","fps=","profile=","interlaced","help","version"])
    except:
        usage()
        sys.exit()

    param = set_default()

    if len(opts) > 0:
        param = set_param(opts, param)
    else:
        usage()

    check_profile(param[2], param[3])

    print
    print " resolution       : %i x %i" % tuple(param[0])
    print " fps              : %i / %i" % tuple(param[1])
    print " profile          : %s"      % param[2]
    print " encoding mode    : %s\n"    % param[3]

    bitstream = calc_bs(param[0], param[1], param[3])
    print " MBPS ... %6iMB/s" % bitstream[0]
    print " FS   ... %6iMBs"  % bitstream[1]
    print " DPB  ... %6iMBs\n"  % bitstream[2]

    if param[3] == 'interlaced':
        avcspec = [i for i in get_spec() if i[5] == 'i']
    else:
        avcspec = get_spec()

    minlv = calc_lv(bitstream, param[3], avcspec)

    print " suitable settings are ...\n"
    print " level  vbv-maxrate  vbv-bufsize  max-ref"
    print " ----------------------------------------"
    display_result(minlv, param[2], bitstream, avcspec)

#changelog
# 2010/12/19     0.1.0 公開
# 2010/12/19     0.1.1 いろいろ計算がおかしかったのを修正
# 2010/12/20     0.2.0 lambda式面白い
# 2010/12/21     0.3.0 リスト内包とgetoptの存在を知る
#    〃          0.3.1  getoptの長文形式における要引数要素に=を付けていなかったのを修正
# 2010/01/02     0.3.2  処理が重複する関数(check_resolution, check_fps)をひとつにまとめた
# 2011/03/17     0.3.3  cosmetics
# 2011/10/12     0.3.4  Fix 10L
なにせ「コンサルタント」ですから、まず役にたたないと思われます。

追記:
06_taro氏がメンテナンスを引き継いでくれたようです。
https://gist.github.com/2325004

2010年12月18日土曜日

avc_refcalc.py

某所にて
(boiled_sugar) level計算機とかないのかなー
(Chikuzen) どんな計算したいの?
(boiled_sugar) 解像度とlevelで最大ref計算
(Chikuzen) この前D_Sがそんなの書いてx264に組み込んでなかったっけ?
(boiled_sugar) 独立したプログラムが欲しい
というわけで、書いてみた。
#!/bin/env python
# coding: utf-8

#****************************************************************************
#  avc_refcalc.py 0.20
#                                   written by Chikezun
#  Reference literature:
#    インプレス標準教科書シリーズ改訂版 H.264/AVC教科書
#           著者:(監修)大久保 榮/(編者)角野 眞也、菊池 義浩、鈴木 輝彦
#    http://en.wikipedia.org/wiki/H.264/MPEG-4_AVC
#    猫科研究所(http://www.up-cat.net/FrontPage.html)
#         x264(vbv-maxrate,vbv-bufsize,profile,level),H.264(Profile/Level)
#
#****************************************************************************

import sys
import math

def usage():
    print "Usage: avc_refcalc.py [options]\n"
    print "  -r, --resolution <string> :set 'width x height' ('1280x720')"
    print "  -l, --level <string>      :set 'level' ('4.1')"
    print "  -p, --profile <string>    :set 'profile' ('high')"
    print "  -i, --interlaced          :specify interlaced mode (not specified)"
    print "  -h, --help                :display this help and exit\n"

def check_prof(pr, ip):
    for i in ['baseline', 'main', 'high']:
        if i == pr:
            if i != 'baseline' or ip != 'interlaced':
                return i
            else:
                print "ERROR : baseline cannot accept interlaced."
    print "ERROR : invalid profile setting.\n"
    usage()
    sys.exit()

def check_level(lv, ip, dic):
    lvl = lv.replace('0','').replace('.','')
    if dic.has_key(lvl):
        if ip[0] != 'i' or dic.get(lvl)[0] == 'i':
            return lvl
        else:
            print "ERROR : specified level cannot accept interlaced."
    print "ERROR : invalid level value.\n"
    usage()
    sys.exit()

def calc_mbs(w, h, ip):
    mbh = int(math.ceil(float(w) / 16))
    mbv = int(math.ceil(float(h) / 16))
    if mbv % 2 == 1 and ip == 'interlaced':
        mbv += 1
    mbs = mbh * mbv
    if mbs > 0:
        return mbs
    else:
        print "ERROR : invalid resolution setting.\n"
        usage()
        sys.exit()

def calc_vbv(lv, pr, dic):
    vbvmax = dic.get(lv)[1]
    vbvbuf = dic.get(lv)[2]
    if pr == 'high':
        return [int(vbvmax * 1.25), int(vbvbuf * 1.25)]
    else:
        return [vbvmax, vbvbuf]

def calc_maxref(lv, mbs, dic):
    ref = int(dic.get(lv)[3] / mbs)
    if ref > 16:
        ref = 16
    if ref > 0:
        return ref
    else:
        print "ERROR : resolution is too large to level.\n"
        usage()
        sys.exit()

options = sys.argv
len_opt = len(options)

#set default values
width  = 1280
height = 720
level  = '4.1'
prof   = 'high'
mode   = 'progressive'
help   = 0

#H.264/AVC level dictionary {level: [interlaced flag, MaxBR, MaxCPB, MaxDbpMbs]}
avcdic = {'1' :['p',     64,    175,    396], '1b':['p',    128,    350,    396],
          '11':['p',    192,    500,    900], '12':['p',    384,   1000,   2376],
          '13':['p',    768,   2000,   2376], '2' :['p',   2000,   2000,   2376],
          '21':['i',   4000,   4000,   4752], '22':['i',   4000,   4000,   8100],
          '3' :['i',  10000,  10000,   8100], '31':['i',  14000,  14000,  18000],
          '32':['i',  20000,  20000,  20480], '4' :['i',  20000,  25000,  32768],
          '41':['i',  50000,  62500,  32768], '42':['p',  50000,  62500,  34816],
          '5' :['p', 135000, 135000, 110400], '51':['p', 240000, 240000, 184320]}

if len_opt > 1:
    for i in range(len_opt):
        try:
            if options[i] == '-r' or options[i] == '--resolution':
                res    = options[i + 1].split('x')
                width  = int(res[0])
                height = int(res[1])
            if options[i] == '-l' or options[i] == '--level':
                level = options[i + 1]
            if options[i] == '-p' or options[i] == '--profile':
                prof = options[i + 1]
            if options[i] == '-i' or options[i] == '--interlaced':
                mode = 'interlaced'
            if options[i] == '-h' or options[i] == '--help':
                help = 1

        except:
            print "ERROR : invalid arguments\n"
            help = 1

        else:
            pass

    if help == 1:
        usage()
        sys.exit()

else:
    usage()

profile = check_prof(prof, mode)
lv_tmp  = check_level(level, mode, avcdic)
mbs     = calc_mbs(width, height, mode)
vbv     = calc_vbv(lv_tmp, profile, avcdic)
maxref  = calc_maxref(lv_tmp, mbs, avcdic)

print " resolution       : %i x %i" % (width, height)
print " level            : %s" % level
print " profile          : %s" % profile
print " mode             : %s" % mode
print " vbv-maxrate(vlc) : %i" % vbv[0]
print " vbv-bufsize(vlc) : %i" % vbv[1]
print " max ref number   : %i" % maxref

2010年11月26日金曜日

mod16

およそ動画系の話題を扱うforumであればたまに出てくる話題に"mod16"なるものがある。
そう、「動画の解像度は縦横ともに16の倍数にすべきである」ってやつですな。
これは正しいといえば正しいが、正しくないといえば正しくない。
とりあえず事実だけを言うのであれば「(mpeg系のエンコーダーは)解像度が16の倍数でないと圧縮出来ない」になるだろう。

mpeg系エンコーダー(そしてこれらの影響を受けているH.263とかVPxとか)は、まず映像を左上を原点にとって16x16のMB(マクロブロック)の集まりに分割してから動き検索やらDCT(離散コサイン変換)やらを行う。
規格によっては16x16のMBをさらに4x4に分割したり8x8とか8x16とかにも分割したりするが(SMB:サブマクロブロック)、とりあえず一回16x16で分割しないことには何も始まらない。
さあ、16x16の集まりに分割するのだ。

でも、たとえば642x484なんていう解像度を圧縮する場合はどうすればいいのよ?
右端の2x16とか下端の16x4とか一番右下の2x4は処理できないじゃん。
困るよ、おい。

これを何とかするため、16の倍数になっていないものの場合はエンコーダーの内部でpadding(パディング)なるものが行われるようになっている。
paddingを英和辞典で引けば「詰め物」とかそういった感じの意味で載っているだろう。
つまり、本来存在しない映像をを付け足して、16の倍数になるようにするわけだ。642x484であれば、映像の右端に幅14ピクセル、下端に幅12ピクセルのダミーをくっつけて656x496にしてしまう。
よーし、これで圧縮にとりかかれますな。

ところでこのpaddingされる映像ってどんなものなのか、知ってます?
黒ベタ? 灰色ベタ?
正解は右端および下端の縁の色です。
つまり、こんな感じ。
なんでこんな絵の具が垂れたみたいな感じに?
それは、このようにpaddingを行うのが一番、本来の映像に影響を与えずに圧縮できるから。
単色ベタ塗りは色々とまずいことがあるのですよ。色々ね。
もし自分でこのようなpaddingをしてみたければ、AviUtlなら「"縁塗りつぶし"を"縁の色で塗る"にチェックを入れて値をマイナスに設定」、AviSynthなら「"AddBorders"と"BorderControl"または"FillMargins"を併用」で可能です。
#padding.avs
AVISource("642x484.avi")
AddBorders(0,0,14,12)

#BorderControlの場合
LoadPlugin("BorderControl.dll")
BorderControl(XRS=14,YBS=12)

#FillMarginsの場合
LoadPlugin("FillMargins.dll")
FillMargins(0,0,14,12)

さて、これで圧縮は出来たけど、いざ動画を再生するときに、こんな変な部分まで表示されたら、それはそれで嫌ではないかな?
本来存在しないはずのものまで見えるのってうざいと思うよっつーかうざいんだよボケ。心霊写真じゃあるまいし。
そういう文句が出ることは分かりきっているので、paddingされたデータはまともなデコーダーならデコード時にはcropしてしまうようになっています。つまりさっきくっつけた右端14ピクセルと下端12ピクセルは表示されず、もとの642x484として表示される。
やれやれ、これにて一件落着。

でもね。
影響の少ないダミーによる水増しとはいえ、映像は642x484ではなく656x496の状態で圧縮されてるわけですよ。
解像度が大きければ、その分ファイルサイズも当然大きくなります。
もし640x480だったならばpaddingも必要なく、ファイルサイズも小さくなるだろうに…。
表示されない屑データのために膨れるなんて、そんな贅肉見苦しいんだよ、メタボだよ、ああむかつくこんちくしょう、うーんと、えーっと、そもそも水平2ピクセル、垂直4ピクセルくらいなら、削っちゃっても問題なくね?フフフそうだよそれくらい削っても気にならないだろ普通は、っつーかむしろ気にするやつのほうがおかしいだろ頭悪いだろキ○○イだろフヒヒヒヒええい削ってしまえバカヤローウギャー…orz

というわけで、やたら圧縮率にこだわる人であれば16の倍数になるようにするでしょう。
これが「動画の解像度は縦横ともに16の倍数にするべきである」の正体です。
まあ、もともと16の倍数になるように絵が作られていれば、こんな葛藤も起こらないわけで、それにこしたことはないんですが…はぁ(ため息)。

ちなみにたまに「mod16に出来ないならば、次善の策としてmod8(8の倍数)にするべきである」という人がいますが、これは迷信の類です。
mod16でなければpaddingは起こります。
そしてpaddingされるデータ量は、解像度を16で割った余りが16に近ければ近いほど小さくなります。
644x484、648x488、652x492という3種類の映像があったとすれば、ファイルサイズに対してpadding分のデータ量の占める割合は、644x484 > 648x488 > 652x492です。
つまりpaddingは少ないほどよいってことですね。
「652x492か。縦横12ずつ削るってのはさすがに嫌だよなあ…でも縦横4ずつくらいならそれほど嫌じゃないぞ。よし、648x488でmod8にしよう」
「それはつまり、大事な映像をわざわざ削った挙句に無駄になるデータを増やそうってことですかそーですか…手前の馬鹿さ加減にゃあ父ちゃん情けなくって涙出てくらぁ!!」(CV:東野英心)

あと、「mod16じゃないと、映像の端がゆがむ」という人もいますが、これもどうなんでしょうかね。
よっぽどひどいエンコーダーでもない限り、前述したような適切なpaddingを行っていれば、まず人間の目ではソースと並べて見比べてもなかなか視認出来るものではないはずなんですが。