2010年10月23日土曜日

Console2

XhmikosR氏のファイル置き場を眺めていたら、/toolsにconsole_2.00_Beta_b146というのがあった。
なんだろうと思ってググって見たら、sourceforgeの本家のページが見つかったので、最新のzipをDLして、ヘルプを読んでみた。
Console is a Windows console window enhancement.
(consoleはWindowsのコンソールウィンドウの拡張用ツールです)
Console is simply a nice-looking front end for a shell of your choice (cmd.exe, 4NT, bash, etc.) Other command-line utilities can also be used as 'shells' by Console.
(単純に言ってしまえば、consoleはあなたの選んだshell(cmd.exeとかbashとか)のためのかっこいいフロントエンドであり、他のCLIユーティリティでもshell同様に使えます)
どうやら複数のCLI端末のウィンドウをタブで管理できたり、コピペ操作がテキストエディタぽくマウスで出来たり、フォントを変えたり、背景を個別に設定できたりするらしい。
端末がタブで扱えるのは大変うれしいので、早速使ってみることにした。

1. とりあえずzipを解凍し、適当なところに置く。
2. 次にヘルプ(.chm)を開き、軽く目を通す。
3. console.exeをダブルクリック。
4. ヘルプのSetting -> Language settingを読みながら、フォントの設定を行う。
5. その他設定を行う。

ためしに「console2 日本語」で検索したら、4のあたりの情報がいくつか出てきた。ヘルプを読むのは大事よね。

とりあえずタブの設定は3種類作ってみた。
ひとつはcmd.exe用
Shellとして、C:\Windows\System32\cmd.exeを登録し、Startup dirはC:\にした。
2つ目はmsysのbash用。
これは
@echo off
G:\msys\bin\sh.exe --login -i
だけ書いたbatを用意して、それをShellとして登録する。
3つ目はCygwinのbash用。
これもmsysと同様にbatを登録するだけ。

これでやることはとりあえず終了。
いざ使ってみると…これはイイ!
新しい端末をショートカットキー一発でタブで開けて、切り替えもラクラク。
特にノートPCとか狭い画面で複数の端末を操作する必要がある人にはお奨めですね。
あとは背景とかも設定できるのでいろいろいじってみるのもいいかも。

2010年10月22日金曜日

GPACが

mingw / mingw-w64共に再びビルドできるようになったみたい。(SVN rev2183時点)

でも
./configure --cross-prefix=x86_64-w64-mingw32-
ってやったら
C   compiler: x86_64-w64-mingw32-gcc
C++ compiler: g++
と表示されるあたり、次に壊れるのも早そう…つーか、まだ直ってはいないといったほうがいいのかも。

2010年10月19日火曜日

AvsPのブックマーク位置をずらすマクロ

AvsP(mod)でTrimSelectionを使ってCMカットすると、各パートの終端フレームにブックマークが打たれた状態になる。
これが結構曲者で、例えば動画にチャプターを打つ場合、チャプターポイントは各パートの先頭フレームに来るように打つものであるが、ブックマークが打たれている場所が各パートの終端だと、そのまま"Bookmarks to Chapter.py"を使うわけにいかない。
各パートの終端フレームの次のフレームは当然、次のパートの先頭フレームになるわけだが、この1フレームのずれを修正するために、いちいちブックマークを消して、1フレーム移動して、もう一度ブックマークを打ち直すのはめんどくさい、なんとかならんもんかいな?

と思ったので、これもマクロで何とかしてみようというのが今回のお話。
-----------------------------------------------------------------------------
既存のブックマークすべてを1フレーム右(未来方向)にずらすだけなら簡単である。
#Shift Bookmarks right+1.py
before=avsp.GetBookmarkList()
if len(before):
    after = [i + 1 for i in before]
    after.sort()
    avsp.SetBookmark(before) #ブックマークは二重に打つと消える。
    avsp.SetBookmark(after)
しかし、これをそのまま使うと、もし最終フレームにブックマークが打たれていた場合、そのブックマークは範囲外に飛び出して消えてしまう。
それに、すでにブックマークを1フレームずらし終えたのに、何らかの操作ミスでもう一回ずらしてしまったらどうしよう? マクロを使った場合、undoは効かないよ?
はたまた、例えばブックマークを打ち終え、チャプターを出力する段になったところでトリムのし残しを発見してしまった場合は? その部分が全部で10個打ったブックマークの2番目と3番目の間に30フレームあったりしたら、1番目と2番目は打ち直すとしても、3番目以降のためにこれを30回実行するの?
ずらすフレーム数を任意に指定できるようにしたとして、全部で10000フレームしかないクリップに対して、桁を間違えて100000なんて指定してしまったら?
などと不測の事態についていろいろ考えていたら、最後にはこうなっていた。
#Shift Bookmarks.py
def newBM(present, shift, last):
    if 0 <= present + shift <= last:
        return present + shift
    elif present + shift > last:
        return newBM(present, shift - last - 1, last)
    else:
        return newBM(present, shift + last + 1, last)

input = avsp.GetTextEntry('Input an integer value',default='1')
shift = int(input)
before = avsp.GetBookmarkList()
if shift and len(before):
    framecount = avsp.GetVideoFramecount() 
    after = [newBM(old, shift, framecount - 1) for old in before]
    after.sort()
    avsp.SetBookmark(before)
    avsp.SetBookmark(after)
うーむ、再帰なんて初めて使ったよ。
-------------------------------------------------------------------------
使い方:
ブックマークが打たれた状態でマクロを実行するとダイアログが現れるので、数値を入力する。デフォルトは1。
正ならば右(未来方向)、負ならば左(過去方向)にすべてのブックマークが指定した数値分だけ移動する。
最終フレーム + 1 = 0番フレーム(0番フレーム - 1 = 最終フレーム)として循環処理される。
数値以外の文字列を入力した場合はエラーとなり何もしない。小数点以下は切り捨て。
操作を取り消したい場合はもう一度マクロを実行し、事前の指定値の反数を入力すること。

2010年10月18日月曜日

CM検出マクロ for AvsP

この前のSCx264 to Bookmarksは、少々時間がかかり過ぎるので、CMカットにはいまいち不向きなようだった。
もっと短時間で可能な方法はないものだろうか?
しばし悩んだ末、そういえばAviUtlにもなにやらCMカット用のシーンチェンジ検出プラグインがあると小耳に挟んだことを思い出したので、見てみることにした。
「チャプター編集 for AviUtl by ぽむ + 無音&シーンチェンジ検索機能 by ru」
読み込んでいる動画の音声トラックをスキャンし、無音連続部分を検出して、その「開始部分」にチャプターを打ちます。
その後チャプターへのシーク時に無音部分内のシーンチェンジを検出し、その位置へ移動できます。
うーむ、なるほど…。
シーンチェンジ検出を無音部分に限定すれば高速化が可能かも!?
-------------------------------------------------------------------------
では早速やってみましょう。

まずサンプルクリップを用意します。
今回はMPEG2-TS、音声AACで再生時間31分、総フレーム数55749フレームの地デジソース(1440x1080)です。
#sample.avs
MPEG2Source("sample.d2v")
AudioDub(last, WAVSource("sample.wav"))

#とりあえず普通に再生できる音声(FAWとかの偽装WAVはこの場合は駄目)でディレイ補正処理を済ませておく。
#今回はソースが地デジTSなので、とりあえず
#[DGIndexでdemuxしたaac -> FAWを2回通してディレイ処理済のaac -> FAADでPCM.wavに変換]
#したものを使った。
#処理終了後に差し替えれば、偽装WAVでも大丈夫。
#もしaacを直読みしたい場合は、BassAudioでも使ってDelayAudio()で補正することになる。
このクリップの無音部分を検出しなければならんわけですが、使えそうな道具は…MinMaxAudioかな?
AudioMax(0)が-100dbよりも低ければ、そのフレームは無音と判定することにしましょう(テキトー)。
#sample.avs
MPEG2Source("sample.d2v")
AudioDub(last, WAVSource("sample.wav"))

LoadPlugin("MinMaxAudio.dll")
AudioDub(BlankClip(last, width=160, height=80), last)
#スピードアップのため、クリップを映像160x80で真っ黒、音声はそのままに変換する
WriteFileIf("silence.log", "AudioMax(0) < -100", "current_frame")
#該当フレームのフレーム番号をテキストに書き出す
このavsをVirtuaDubの"Run Video Analysis pass"にかけてやると、約1250fpsくらいのスピードでsilence.logが生成されます。
結果を見てみると、どうやら無音のフレームは、各CM及び本編の切れ目あたりにだいたい10~30フレーム入るみたいです。
具体的には全55749フレームのうち、41-53、475-504、926-954、1376-1402、1825-1837、2289-2302、2725-2738、9077-9092、9990-10003、10874-10905、26915-26923、27373-27385、27808-27821、28707-28738、46494-46507、49179-49206、49628-49656、50078-50103、50525-50553、50975-50987、51439-51451、51874-51902、52783-52788、53043-53101、53523-53535、54422-54434、54886-54898、55320-55350、55735-55748の全29箇所561フレームが無音(-100db未満)です。

そこでsample.avsをこうしてやり
#sample.avs
MPEG2Source("sample.d2v")
AudioDub(last, WAVSource("sample.wav"))

PointResize(last.width / 4, last.height / 4)
#シーンチェンジ検出には320x270程度で十分なのでPointResizeでリサイズ
FreezeFrame(0,41,41)
FreezeFrame(53,475,475)
FreezeFrame(504,926,926)
#途中24行省略
FreezeFrame(54898,55320,55320)
FreezeFrame(55350,55735,55735)
FreezeFrame(55748,0, 0)

#AviSynthの仕様ではFreezeFrameでコピーされるフレームはデコードされないので、
#MPEG2Sourceのボトルネックが解消されるはず
このavsをこの前のSCx264 to Bookmarks.pyにかけてやると、x264はなんと約2200fpsという想像を絶するスピードでシーンチェンジ検出を終え、ブックマークを打ち終わりました。
あとはFreezeFrameの行を消してやれば…YATTA!!
-----------------------------------------------------------------------------
てなことがありまして、これらの操作を自動で行うべく書いたAvsPマクロが以下の通りです。
使い方は、とりあえずAudioDubまで書いたavsを用意して、マクロを実行するだけです。
FAW使用者は、実行後に音声を偽装WAVに差し替えればいいです。ちょっと書き換えれば、そこらへんも自動化できるでしょう
前回約10分かかってたのが、約1分まで縮まりました(on Q9450定格)。
著作権は…発生するのかどうか知りませんが、もし発生するのであればGPLv3です(なんとなく)。
つーか、Pythonスクリプトですから、バイナリと違って秘密にはしようがないと思うけど…。

EDIT:
よく考えたらavs2aviのnull出力とx264の--preset ultrafast -o nul ではスピードは大して変わらないので(っていうか、今度はMinMaxAudioがボトルネックになっているので)、avs2aviをx264に変更。
avs2aviは要らなくしました。
#Silent_Scenechanges to Boolmarks.py for AvsP
#Author : Chikuzen (http://csbarn.blogspot.com/)
#Requiament: MinMaxAudio.dll, x264.exe, (avs2avi.exe)

#--------------preparation----------------------------------
import os

avsname = avsp.SaveScript()
script_org = avsp.GetText()

#あらかじめ各ツールのパスを設定しておくこと
#ファイルパスを変数に入れる際、前にrをつけるのはPythonのお約束
mmaudio = r'C:\AviSynth 2.5\Plugins_x86\MinMaxAudio.dll'
x264cli = r'G:\Enctools\x264_x86.exe'
#avs2avi = r'G:\Enctools\avs2avi.exe'
if mmaudio and x264cli and avsname and script_org:
#if mmaudio and x264cli and avs2avi and avsname and script_org:
#--------------------------------------------------------------
    #Search Silence
    logfile_si = avsname + '_silence.log'
    text1 = '\nLoadPlugin("%s")' % mmaudio
    text1 += '\nAudioDub(BlankClip(last, width = 160, height = 120), last)'
    text1 += '\nWriteFileIf("%s", "AudioMax(0) < -100", "current_frame", append = false)' % logfile_si
    avsp.InsertText(text1, pos = -1, index = None)
    avsp.SaveScript()
    os.system('%s %s --preset ultrafast --crf 51 -I infinite -o nul' % (x264cli, avsname))
    #os.system('%s %s -c null -o n' % (avs2avi, avsname))

    #silence_log to freezeframelines
    avsp.SetText(script_org)
    sifile=open(logfile_si)
    lines_si=sifile.readlines()
    sifile.close()
    text2='\nPointResize(last.width / 4, last.height / 4)'
    if len(lines_si):
        x = 0
        for line_si in lines_si:
            log_si = int(line_si.strip())
            if x != log_si - 1:
                text2 += '\nFreezeFrame(%i, %i, %i)' % (x, log_si, log_si)
                x = log_si
            else:
                x += 1
        text2 += '\nFreezeFrame(%i, %i, %i)\n' % (int(lines_si[-1].strip()), 0, 0)
    avsp.InsertText(text2, pos = -1, index = None)
    avsp.SaveScript()
    
    #SCx264
    logfile_sc = avsname + '_scenechange.log'
    opts = '--preset ultrafast --crf 30 -I infinite -i 1 --scenecut 50 -o nul'
    os.system('%s %s -p 1 --stats %s %s' % (x264cli, avsname, logfile_sc, opts))
    avsp.SetText(script_org)
    avsp.SaveScript()

    #scenechange_log to bookmarks
    scfile = open(logfile_sc)
    lines_sc = scfile.readlines()
    scfile.close()
    bookmarks = []
    for line_sc in lines_sc:
        log_sc = line_sc.split(' ')
        if log_sc[2] == 'type:I':
            bmpoint = int(log_sc[0].lstrip('in:'))
            bookmarks.append(bmpoint)
    avsp.SetBookmark(bookmarks)

2010年10月16日土曜日

Syntax Highlighting

Henry氏の薦めもあって、このブログにSyntax Highlightingを入れてみた。

初めはこちらのwidgetを入れてみたのだが、いざ適用してみるとFirefox3.6やIE8だとちゃんとHighlightingしてくれない。
してくれないだけならまだいいが、ついでに"can't find brush for:python"なんていうのまで表示される。
それも適用したコード全てに対して表示されるので、例えばコードが10個あれば10回、100個あれば100回表示されるという始末である。
Chromeなら大丈夫みたいだけど、みんながみんなChromeを使っているわけではないだろうから(そもそも自分自身、メインで使っているのはFirefoxである)すぐにやめた。

で、結局こちらのほうを見ながら、テンプレートの編集なんてことをやる羽目になったわけだが、いざ導入してみると、これはこれで対応している言語が少ない、少ない、少なすぎる。

AviSynthがないのは仕方ない(つーかavsをHighlightingできるところなんて、世界広しと言えどもCCCPastebinくらいのものだろう。流石はMeWikiのメンテナーcheckers氏がやってるだけのことはある)けど、shellに対応してないってのは痛いなぁ…。

まあ、ないものは仕方がないので他で代用することにした。
とりあえずAviSynthはPythonでいいだろう。なんか似てるし。
Shellはどうしようとなやんだが、これも結局Pythonにした。特に問題はないみたいだし。

やっぱりCSSとかも勉強したほうがいいのかな。

SCx264 to Bookmarks for AvsP

またまたAvsPマクロネタ。

さて、AviSynthのプラグインにはいろいろと毛色の変わったものがありますが、そんな中にSClavcSCXvidというものがあります。
オリジナルのSClavcはx264のペンギン様ことLoren Merritt氏作、SCXvidの作者は#darkholdのいかつい人、MyrsloikことFredrik Mellbin氏であります。(以前一度、JEEB氏に写真を見せてもらったことがあるのですが、Myrsloik氏はやたら人相の怖い太っちょでした。一方、一緒に写ってたTheFluff氏は小柄で柔和そうな感じで、普段の言動から受ける印象とあまりにも違いすぎるので、思わず大笑いしたものです)

で、これらが何のためにあるかというと、要するにffmpegやXvidの2pass用のログを利用して、クリップ内のシーンチェンジの場所とかを割り出して色々利用しようというものらしい。
なんで「らしい」かといえば、使い方がよくわからないから。
とりあえずavs書いてVirtualDubに食わせて"Run Video Analysis pass"を走らせるなりすれば、2pass用のlogファイルが作られるわけですが、そこから先がどうしたものやら…。
AviSynth.infoによれば、「元々は、Yattaにおいて、libavcodecのシーンチェンジ・メトリクスの利用を可能するために書かれた。」とありますが、Yattaってねぇ…あれの使い方がわかる日本人なんているのかしら?
7月ごろにJEEB氏に「#yattaでmenter氏が初心者講習会を開くので来ないか?」と誘われたのでROMってみましたが、何をしゃべってるのかさっぱりわかりませんでした。(そもそも知りたかったのは理論やテクニックではなく、操作方法そのものなのですが、そこらへんはやってくれなかったし…orz)

まあ、Yattaの使い方は今もさっぱりわかりませんが、2pass用logからシーンチェンジ検出というのは理解できます。
そこでふと思ったことが「それならx264のlogファイル使ったほうがよくね? lavcのmpeg4やXvidよりもlog出力速いじゃん」。
で、こんなのを書いてみた。
#SCx264 to bookmarks.py
frames = avsp.GetVideoFramecount()
logfilename = avsp.GetFilename(title='Select x264_logfile')

if logfilename:
    logfile = open(logfilename)
    logs = logfile.readlines()
    logfile.close()
    if len(logs) == frames + 1:
        bookmarks = []
        for logline in logs:
            log = logline.split(' ')
            if log[2] == 'type:I':
                bmpoint = int(log[0].lstrip('in:'))
                bookmarks.append(bmpoint)
        avsp.SetBookmark(bookmarks)
    else:
        avsp.MsgBox('This log is not corresponding to the number of frames of current clip.',title='Warning')

あらかじめx264.exeで --pass 1 をつけてエンコしたlogから、IDRフレームと判定されたフレームにブックマークを自動で打つマクロである。(またブックマークか…)
とりあえずシーンチェンジを検出して、ちゃんとIDRと判定されるようなオプションにしていれば、ほぼ100%検出できるはずである。

さて、ただlog読んでブックマーク打つだけでは面白くないので、さらに発展させてみる。
#SCx264 to bookmarks AUTO.py
import os
avsname = avsp.SaveScript()
#使用するx264.exeを毎回指定したい場合はこちら
#exe = avsp.GetFilename(title = 'Specify x264.exe used.')
#x264.exeのパスを固定したい場合は、こちらにフルパスで書いておく
exe = r'G:\Enctools\x264_x86.exe'

if avsname and exe:
    x264opt = '--preset ultrafast --crf 30 -p 1 -I infinite -i 1'
    thresh = '--scenecut 50'
    logfilename = avsname + '.sc_log'
    os.system('%s %s %s %s --stats %s -o nul' % (exe, avsname, x264opt, thresh, logfilename))

    logfile = open(logfilename)
    logs = logfile.readlines()
    logfile.close()
    bookmarks = []
    for logline in logs:
        log = logline.split(' ')
        if log[2] == 'type:I':
            bmpoint = int(log[0].lstrip('in:'))
            bookmarks.append(bmpoint)
    avsp.SetBookmark(bookmarks)

log出力も一緒にやるようにしてみた。

ううむ、これをどう使えばいいのだろうか?

例えばMPEG2Source("hogehoge.d2v")だけのavsの状態でこれを実行するとCMカットは楽になる。
なぜって、ただブックマーク移動していくだけで本編とCMの切れ目を確実にシークできるから。
AvsP初期装備のTrimEditorと併用すれば、ほとんどストレスなくCMカットできるはず(ただし、TrimEditorでTrimを実行する際は、ブックマークをすべてクリアしてからにすること。これを忘れるとプレビューの更新で随分時間を食うことになります)。
x264は--preset ultrafastならものすごく高速です。自分のそろそろ時代遅れなQ9450でも、1440x1080
のMPEG2-TSを90fps~100fpsで処理します。つーかMPEG2Sourceがボトルネックになっているので、本当はもっと速いです。2本同時にlog出力したりするといいかもしれません。

なお、「30分のTSだとlog出力だけで10分かかるじゃねえか、それだけあればCMカットなんか終わってるよ」とかいう意見は無視します…orz

2010年10月15日金曜日

分割結合マクロ for AvsP

前回のTrimEditorは、いざ使ってみるとメリットはそれほどなかったような気もするけど、まあ、マクロの練習にはなった。
さて、今回もBookmarkを使ったマクロを書いてみる。
#Divide and Concatenate.py
bookmarks=avsp.GetBookmarkList()
if bookmarks:
    bookmarks.sort()
    start, trim, cat = 0, "", "\n"
    for i in xrange(len(bookmarks)):
        trim += "\nV%02i = last.Trim(%i, %i)" % (i, start, bookmarks[i] - 1)
        cat += "V%02i ++ " % i
        start = bookmarks[i]
    i += 1
    trim += "\nV%02i = last.Trim(%i, 0)" % (i, start) 
    cat += "V%02i\n" % i
    avsp.InsertText(trim + cat, pos=None, index=None)
例えば
AVISource("hoge.avi")
AudioDub(Last, WAVSource("hoge.wav"))
なんていうavsがあったとして、1234,5678,9012フレームにブックマークを打ってからこのマクロを実行すると
AVISource("hoge.avi")
V01 = Last.Trim(0, 1233)
V02 = Last.Trim(1234, 5677)
V03 = Last.Trim(5678, 9011)
V04 = Last.Trim(9012, 0)
V01 + V02 + V03 + V04
AudioDub(Last, WAVSource("hoge.wav"))
というように、いったんブックマークの位置で分割して、最後に結合するスクリプトが挿入される(挿入位置はカーソルの位置)。
なににこんなものを使うかといえば、
AVISource("hoge.avi")
V01 = Last.Trim(0, 999).Sharpen(1.0)
V02 = Last.Trim(1000, 1999).Blur(1.0)
V03 = Last.Trim(2000, 2999).FlipVertical()
V04 = Last.Trim(3000, 0).FlipHorizontal()
V01 + V02 + V03 + V04
AudioDub(Last, WAVSource("hoge.wav"))
てな感じでFilterRangeのかわりにするとか、はたまたEasyVFRとか使う人には便利かもしれない。

2010年10月13日水曜日

俺様TrimEditor for AvsP

スクリプトなんて簡単なBash scriptとAviSynth scriptしか書けない私ですが、最近はほんの少しだけPythonをいじるようになりました。
なんでPythonかといえば、AvsP(AviSynth script editor)がPythonで書かれていて、マクロもPythonで書かないといけないから。

さて、本日のお題はTrimEditor。
AvsPには始めからTrimEditorが付いていますが、これが自分にはどうも使いにくい。
まずHomeを押すと出現するダイアログがウザい。
ある範囲を選択(削除)するごとにTrim(a,b)を1行挿入するので、Trimし終わったころには、それだけで7,8行のスクリプトになってしまうのもウザい。
というわけで、自分用のTrimEditorをでっち上げます。
#Bookmarks to TrimLine.py
bookmarks = avsp.GetBookmarkList()
num = len(bookmarks)
if num % 2 == 0 and num:
    bookmarks.sort()
    text = ''
    for i in range(0, num, 2):
        trimrange = bookmarks[i:i+2]
        cattext = '++Trim( %i, %i)' % (trimrange[0], trimrange[1])
        text += cattext
    avsp.InsertText(text.lstrip('++') + '\n', pos=None, index=None)
else:
    avsp.MsgBox('The number of bookmarks is odd (or zero).', title='Warning')
使い方:
ブックマーク(Ctrl+B)を適当にガンガン打って、マクロ実行。
2つのブックマークにはさまれた部位を残すスクリプトが、カーソル位置に1行で挿入されます。
例)ブックマークを1000,2000,3000,4000,5000,6000フレームに打って実行したら Trim(1000,2000)++Trim(3000,4000)++Trim(5000,6000) となる
なお、10行目のpos=Noneをpos=-1に書き換えると、スクリプトの最後に挿入位置が変更されます。

2010年10月3日日曜日

regression test

#ffmpeg-develでDark Shikari氏のregression test用スクリプトを見つけた。
#!/bin/sh
vid=videos/foreman_cif.y4m
for tune in film zerolatency
do for m in "--intra-refresh" ""
  do for j in "" "--no-cabac"
    do for i in "--keyint 250 --slice-max-size 1000" "--no-weightb --interlaced"
      do for p in ultrafast superfast veryfast faster fast medium slow slower
        do ./x264 $vid --quiet --preset $p --tune $tune $i $j $m -o test.h264 --dump-yuv test.yuv
           ~/x264/JM/bin/ldecod.exe -i test.h264 > /dev/null
           cmp test.yuv test_dec.yuv
        done
      done
    done
  done
done
えーと、2x2x2x2x8=128パターンか。