PT2の設定〜全自動CMカット、720i MP4を作成するまでの道のりをまとめたいと思った。

これは今現在のシステムを構築するまでにかかった道のりを、せっかくなのでまとめようと思ったので記録しておきます。
参考にされる方がおりましたら幸いです。

ただし、この構成ではインタレースを解除しないため、PCでインタレース解除支援が得られない場合にはシマシマになってしまいます。
推奨環境としてはBraviaなどのインタレースmp4を再生できるクライアントがあること前提です。

PT2導入〜ドライバインストール

現在のハードウェア構成
  1. Windows7 64bit Professional
  2. AMD Phenom II x6 1100T
  3. メモリ 12GB
  4. MB GA-970A-D3
  5. PT2
  6. SCR3310-NTTCom
この構成の問題点

64bit + GigabyteのMBという点で、0mbps問題が発生する。
そのため、ドライバはpt2wdmドライバを使用する。

ハードウェアの準備

PT2をPCIスロットに指す。
分配器・分波器の違いに注意して、アンテナケーブルを正しくS,Tに指す。

ICカードリーダのSCR3310-NTTComは普段アクセスするわけではないのでPCケース内のUSBに接続して入れておく。

テストモードの設定

pt2wdmをインストールするにあたって問題となるのが、テストモードにしないといけない点。
まあ、エンコ専用PCなもんでテストモードにすることによるデメリットの「ブルーレイが見れない」というのは無視する。

テストモードについては以下のページを参照

http://freesoft.tvbok.com/tips/win7rc64/x64_driver_bcdedit.html

デジタル署名取得を無効化する方法(テストモードON)
管理者権限でコマンドプロンプトを実行し
bcdedit /set TESTSIGNING ON
を実行。

デジタル署名取得を有効する(元に戻す)方法(テストモードOFF)
管理者権限でコマンドプロンプトを実行し
bcdedit /set TESTSIGNING OFF
を実行。

これによって、テストモードになる。

再起動することで、右下に「テストモード」と書かれたWater Markが出現する。

ドライバのインストール

pt2wdmの導入
http://2sen.dip.jp/cgi-bin/pt1up/upload.cgi?page=1&lm=25
ここから人柱α4版をダウンロード

Readmeの手順に従って、次のように設定する。

64bit版組込方法)- - - Windows7
1.全てのアプリケーションを終了して下さい。
2.手動にてデバイスマネージャから本ドライバーを組込んでください。
 (サウンド、ビデオ、およびゲームコントローラの中のPT1/2を右クリックしてドライバの更新選択)
  コンピュータを参照してドライバーソフトウェアを検索します(R)
コンピューター上のデバイスドライバーの一覧から選択します(L)
  ディスク使用(H)
  参照->デバイスドライバ一式ホルダ選択
  EarthPtWdm.Inf指定
  OK
  次へ(N)
  警告がダイログが表示されますが"続行"をクリック
  組込完了

となっている。

sample.exeによるテスト

http://earthsoft.jp/PT1_PT2/download.html
こちらからPT-Windows-Sample-200.zipをダウンロードして、x64の方を実行。
0: (なんか数字)
1: 終了
とかと書かれていたら成功。

適当に0, 1, 0, 0 とかと入力してチャンネルが受信できていたら成功。

BonCasServerの導入

PT2サーバーにリモートデスクトップでログインしたいため、IC-Cardリンクが切れないように導入する。
http://2sen.dip.jp/dtv/ から、BonCasProxyをダウンロード。
適当なフォルダにおいて、BonCasService.exeのフォルダのインストールを右クリックして管理者として実行。
これでServerがインストールされる。あとはBonCasProxyをログオン時に実行するようにする。

Spinel.exeの導入。

サーバーとしてPCを使用し、同一LAN内にあるPCすべてでTVTestによる視聴を可能にしたいので、Spinelを導入する。
おそらくbonDriver_pt2wdmは32bit版なので、Spinelなどはとりあえず32bitで構成する。
http://lapislabs.blog24.fc2.com/ ここからSpinelの最新版をダウンロード。
http://2sen.dip.jp/cgi-bin/pt1up/upload.cgi ここからbonDriver_pt2wdmをダウンロード。
SpinelのReadMeに従って設定する。

bonDriver_pt2wdm.dllを、
bonDriver_pt2wdm-S0.dll
bonDriver_pt2wdm-S1.dll
bonDriver_pt2wdm-T0.dll
bonDriver_pt2wdm-T1.dll

とコピー・リネームして、Bondriverフォルダに入れる。

spinel.exeをスタートアップに入れる。

TVTestで視聴テスト

http://tvtest.zzl.org/
TVTestの最新版をここからダウンロード。

http://lapislabs.blog24.fc2.com/
ここから、BonDriver_Spinel.dllをダウンロード。

BonDriver_Spinel.dllを
BonDriver_Spinel_PT-S0.dll
BonDriver_Spinel_PT-S1.dll
BonDriver_Spinel_PT-T0.dll
BonDriver_Spinel_PT-T1.dll
とリネームし、

BonDriver_Spinel.dll.iniを、
BonDriver_Spinel_PT-S0.dll.ini
BonDriver_Spinel_PT-S1.dll.ini
BonDriver_Spinel_PT-T0.dll.ini
BonDriver_Spinel_PT-T1.dll.ini
とリネームする。

iniの内容を、
192.168.[サーバーのIPアドレス]:48083
と編集する。

TunerPathをそれぞれのiniで
TunerPath = "PT/0/Satellite/0"
TunerPath = "PT/0/Satellite/1"
TunerPath = "PT/0/Terra/0"
TunerPath = "PT/0/Terra/1"
とする。

Windows 7では、Windowsのmpeg2デコーダが入っているので、そのままTVTestを起動、チャンネルスキャンをして視聴を確認する。

EpgDataCap_Bonの設定

10系を使用することにした。
http://2sen.dip.jp/cgi-bin/friioup/upload.cgi
ここから10.66をダウンロード。x64のOSであるが、うまくいかなかったのでx86版を使う。

適当な場所に展開して、ReadMeのやり方に従って設定を行う。

具体的には、BonDriver_Spinelをすべてのフォルダに入れる。

以下Read Meより。

 1.64bitOSで64bitネイティブで動作させるには「x64」フォルダを、
   それ以外の場合は「x86」フォルダを使用してください。

 2.使用デバイス用のBonDriverを用意し、BonDriver フォルダに入れる。
   BonDriverによってはiniファイルなどで設定できる内容があるので、あ
   らかじめ設定をしておく

 3.EpgDataCap_Bon.exeを起動し、「設定」→「基本設定」タブで「設定関
   係保存フォルダ」を設定する。

 4.チューナーから使用チューナーを選んで「チャンネルスキャン」を行う。
   地デジで5分程はかかると思います。
   使用するチューナーの種類が複数の場合は同じ事を行う。
   同時に複数起動して、チャンネルスキャンを行うことができます。

 5.全てのチューナーのチャンネルスキャンが完了するまで待ちます。

 6.EpgDataCap_Bon.exeを終了する。
   (複数起動している場合は、チャンネルスキャンが終了したものから終
   わらせてください)

 8.EpgTimer.exeを起動する。

 9.「設定」→「基本設定」→「チューナー」タブで各BonDriverで使用する
   チューナー数とEPGデータの取得に使用するかを設定します。
   ◆チューナー数が正しく設定されないと、正常に動作しません◆

10.「EPG取得」タブでEPG取得対象サービスを設定します。

11.EpgTimer.exeを終了する。
   ◆チューナー数の設定は起動時にのみ反映するので、終了することが重要です◆

12.EpgTimer.exeを起動する。

13.基本的な使用準備は終わり。
   EPG取得を行い、必要に応じて各種設定や、予約登録など行う。

即時録画で適当なチャンネルが録画できていることを確認する。
EpgTimerの設定で、
録画保存フォルダを設定、チューナーですべてのチューナーでEPG取得をチェック、チューナー数を1とする。
EPG取得で、BSや地上波の2つ目以降のいらないチャンネルを外す。

動作設定タブの録画動作では、常時起動サーバーなので、録画後「何もしない」にする。

好きなようにタイマーを入れて、録画できることを確認。

出力用aviutlの設定

http://www.katch.ne.jp/~kakonacl/douga/aviutl_tmpgenc/x264.html
このページを参考にして、aviutlでmp4の出力ができるよう設定します。試しに短いtsをmp4でエンコできることを確認して下さい。

aviutlには、AviUtl Control ver1.4
http://www.geocities.co.jp/aji_0/
をインストールしておきます。

また、mp4の圧縮設定は個人の趣味もあるかもしれませんが、自分は

    • crf 21 --qcomp 0.7 --rc-lookahead 50 --scenecut 70 --bframes 6 --b-adapt 2 --b-pyramid none --deblock -2:-2 --tff --me umh --subme 8 --ref 6 --weightp 0 --no-fast-pskip --no-dct-decimate --trellis 2

としています。圧縮設定のコマンドの直接入力のところでGUIに反映させれば入ると思います。
aviutlのプロファイルを新しく作り、デフォルトで入っているものを消して、
リサイズ・インタレ解除・フィルタなどの適用をすべて外し、圧縮設定だけ上記にしたものを用意しておきます。

logoデータの作成

ロゴデータは著作権があるかもしれないので載せないこととします。
http://www.losttechnology.jp/Movie/delogo.html
ここのやり方に従って、ロゴデータを作成します。
結構面倒ですが、ロゴデータを1回作れば結構慣れます。
また、tsファイルをaviutlで読み込むと地デジのtsの場合、1440x1080なので、4:3の比率になってとまどうかもしれませんが、これはSAR(ピクセルアスペクト比)が4:3なのでかけ算して16:9になり視聴時に変換されているのが、aviutlでの表示ではそのようにならないためです。

ロゴデータ作成はaviutl内ではリサイズせず、そのままの状態で行えばOKです(すべてのフィルタ・リサイズの設定を無効にする)

無理にリサイズすると失敗します。ここではまりました。
aviutlでロゴを作るときは無理にCMをすべてカットしなくても、アニメなら前半と後半の間のCMをカットして、適当に真ん中から最後のへんまでロゴ解析すれば充分です。

キッズステーション.lgd
テレビ愛知1.lgd
テレビ東京1.lgd
メ〜テレ.lgd
中京テレビ1.lgd
東海テレビ011.lgd
BS−TBS.lgd
BS11.lgd
CBC.lgd
TBS1.lgd

のようにチャンネルのロゴデータを作成します。そのとき、ロゴが表示されているフレームと表示されていないフレームの境目をテキストファイルにメモするようにします。

94
44748
46549
46846

これで、94-44748フレーム、46549-46846フレームにロゴが表示されているという意味です。

あとはLogoGuillo
http://loggialogic.blogspot.jp/2011/12/logoguillov200.html
のReadMeに従って、autotune.paramファイルを作成します。
logoguilloのインストールのときに、avisynthもインストールしますが、無理せず32bit版avisynthをインストールしましょう。
これによって、aviutlでavsファイルも読めるようになります。

http://loggialogic.blogspot.jp/2012/02/mpeg-2-video-vfapi-plug-in.html
ここに従って、必ずこのようにvfapiの設定を行っておきます。そうしないとaviutlでの読み込みにもよくないですし、LogoGuilloも正常動作しません。


ここで注意するところはないと思います。ファイル名にスペースなんかがあるとうまくいかないことがあるので、適当にファイル名をアルファベットのみにしておくのがいいと思います。これはSVMみたいなのの学習をしてるってことでしょうかね。


CMカットのテストを行います。そのとき、avsファイルを出力するようにしておけば、aviutlで確認ができるので視覚的にわかります。

できたAVSファイルをaviutlで読み出してみましょう。きれいにカットできていれば成功です。
個人的な感想ですが、BS11や輝度の低いCBCなんかは前後のCMが多めに入る印象があります。
テレビ愛知ではほぼ完璧なカットができているので、テレ東系列で成功しているのがうれしいです。



手動テスト

  1. tsファイルをBonTsDemuxでm2v+wavに分離
  2. LogoGuilloでavsファイルが出力されることを確認。
  3. avsファイルの内容を置き換え

#REPKEY_1#を次に置換。(次の内容はprefilter.txtなどとして保存しておく)

global AviUtl_plugin_directory = "O:\PT2\Encoder\avisynth\"
global AviUtl_plugin_copy = false
global AviUtl_plugin_debug = false
global AviUtl_plugin_thread = 6
LoadPlugin("O:\PT2\Encoder\avisynth\warpsharp.dll")
LoadPlugin("O:\PT2\Encoder\avisynth\delogo.dll")

####################################
# エッジレベル調整 MT ver 0.7
####################################
# i0 : 特性 default(10) range(-31,31)
# i1 : 閾値 default(16) range(0,255)
# i2 : 黒補正 default(0) range(0,31)
# i3 : 白補正 default(0) range(0,31)
function AU_edgelevelMT(clip clip,
\ int "i0", int "i1", int "i2", int "i3")
{
LoadAviUtlFilterPlugin(AviUtl_plugin_directory+"edgelevelMT.auf", "_AU_edgelevelMT", copy=AviUtl_plugin_copy, debug=AviUtl_plugin_debug, thread=AviUtl_plugin_thread)
#LoadAviUtlFilterPlugin2(AviUtl_plugin_directory+"edgelevelMT.auf", "_AU_edgelevelMT", copy=AviUtl_plugin_copy, debug=AviUtl_plugin_debug, thread=AviUtl_plugin_thread)
return clip._AU_edgelevelMT(
\ default(i0,10), default(i1,16), default(i2,0), default(i3,0))
}
# example:
# ConvertYUY2ToAviUtlYC()
# AU_edgelevelMT(10,16,0,0)
# ConvertAviUtlYCToYUY2()

# フィールド別にフィルタを適用するための関数
function FPNR(clip clip, string "filter")
{
clip
SeparateFields() #フィールド分割
Top = SelectEven #トップフィールド
Bottom = SelectOdd #ボトムフィールド
Top = Eval("Top." + filter)
Bottom = Eval("Bottom." + filter)
Interleave(Top, Bottom).Weave() #インタレースに戻す
return last
}

# プログレッシブでフィルタを適用するための関数
function FPProg(clip clip, bool parity, string "filter")
{
clip
Bob(b=0,c=0.5) #プログレッシブにする
Eval(filter)
parity ? AssumeTFF() : AssumeBFF()
SeparateFields()
SelectEvery(4, 0, 3) #フィールドの選択
Weave() #インタレースに戻す
return last
}

#REPKEY_30#を置換(ロゴファイルがあるときのみ, 次の内容をpostfilterDelogo.txtなどとして保存する。)

#ロゴ除去
EraseLOGO(logofile="%l", pos_x=0, pos_y=0, depth=128, yc_y=0, yc_u=0, yc_v=0, start=0, fadein=0, fadeout=0, end=-1, interlaced=true)

#REPKEY_31#を置換(次の内容をpostfilterResize.txtなどとして保存する。)

AssumeTFF()
parity = GetParity()
FPProg(parity, "Lanczos4Resize(%x,%y)")

#REPKEY_32#を置換(アニメフラグがオンのとき, 次の内容をpostfilterAnime.txtなどとして保存する。)

# WarpSharp
FPNR("WarpSharp(95,3,85,0)")

# エッジレベル調整
ConvertYUY2ToAviUtlYC()
FPNR("AU_edgelevelMT(9,20,5,2)")
ConvertAviUtlYCToYUY2()

  1. aviutlに読み込み
  2. mp4保存テスト

という流れ。

あとはこの動作をバッチファイルに書けば完成です。

もう一度まとめると

  1. BonTSDemuxで分離
  2. Logoがある局なら、LogoGuilloでCMカット
  3. avsファイルの内容をロゴ除去・インターレース保持リサイズ・アニメ向けフィルタを適用するために書き換え
  4. aviutlでAVSファイルを読み込ませる
  5. aviutlでmp4を出力

の流れとなります。
僕はbatファイルに疎いので、C#で書くことにしました。
その方がより柔軟に書けるので便利とおもったのです。
BonTSDemuxで分離するときに、サービス番号があると便利なので、EpgDataCap_BonのSettingにあるchset4.txtを利用しています。
0ベースで7番目のカラムにサービス番号が入っています。
これをBonTsDemuxの-srv引数に入れることで、目的とするサービスのみを抽出できます。

                    string[] S0ServiceName = ReadFile(S0ServicePath).Split('\n');
                    string[] T0ServiceName = ReadFile(T0ServicePath).Split('\n');

                    /// -------------------------------------------------------------------
                    /// 1. TSをm2vとwavにdemux
                    /// 
                    /// サービス番号を検索して、そのサービスだけ取り出す。
                    /// 
                    /// EPGTimer_BonのChSet2.txtをSとT両方で読み込み、
                    /// サービス番号は左から4番目(0 order, 3)にあるので、サービス名と一致するサービス番号を取り出す。
                    /// -------------------------------------------------------------------
                    Console.WriteLine("<録画tsモード>");
                    Console.WriteLine();
                    Console.WriteLine();
                    Console.WriteLine("*****************************");
                    Console.WriteLine("    BonTSDemuxによる分離");
                    Console.WriteLine("*****************************");

                    /// サービス番号を検索する
                    /// 1番はじめにみつかったサービス名のIDを用いる。
                    int ServiceNumber = 0;
                    foreach (string s in S0ServiceName)
                    {
                        if (s.Contains(ChannelName))
                        {
                            ServiceNumber = int.Parse(s.Split('\t')[7]);
                            break;
                        }
                    }
                    foreach (string s in T0ServiceName)
                    {
                        if (s.Contains(ChannelName))
                        {
                            ServiceNumber = int.Parse(s.Split('\t')[7]);
                            break;
                        }
                    }

                    /// サービス名に基づいてBonTsDemux行う。
                    string servicearg = "";
                    if (ServiceNumber != 0) servicearg = " -srv " + ServiceNumber.ToString();
                    Console.WriteLine("Service ID = " + ServiceNumber);

                    /// m2vがあればなにもしない。
                    if (!File.Exists(M2VFile))
                    {
                        Console.WriteLine("Exec : " + BonTsDemuxPath + " -nogui -i " + "\"" + InputFileName + "\"" + servicearg);
                        Exec(BonTsDemuxPath, "-nogui -i " + "\"" + InputFileName + "\"" + servicearg);
                    }
                    else
                    {
                        Console.WriteLine("m2vが存在するのでスキップ");
                    }


アニメ名でフォルダ分けしたいため、DoClassify関数でファイル名からアニメ名を出し、その名前でフォルダを作り、そこに出力するようにしています。

        /// <summary>
        /// 分類を行う。
        /// </summary>
        /// <param name="InputFileName"></param>
        /// <param name="OutputDir"></param>
        /// <param name="ChannelName"></param>
        /// <returns></returns>
        string DoClassify(string InputFileName, string OutputDir, out string ChannelName)
        {
            string ClassifyName = "";   
            string outname = "";
            bool DefaultEncode = false;

            /// 新しいフォーマット通りなら
            Match m = Regex.Match(Path.GetFileName(InputFileName), "(^\\d+-)\\[(.*?)\\](.*)\\.ts");

            ChannelName = "";
            
            if (m.Success)
            {
                ClassifyName = m.Groups[3].Value;
                ChannelName = m.Groups[2].Value;
                outname = m.Groups[1].Value + m.Groups[3].Value;
            }
            else
            {
                DefaultEncode = true;
            }

            /// 出力先があれば分類情報を作る
            if (OutputDir != "" && DefaultEncode == false)
            {

                /// 分類名
                ClassifyName = Regex.Replace(ClassifyName, "\\[.+?\\]", "");
                ClassifyName = Regex.Replace(ClassifyName, "<.*?>", "");
                ClassifyName = Regex.Replace(ClassifyName, "アニメ[  ]+", "");
                ClassifyName = Regex.Replace(ClassifyName, ".mp4", "");
                ClassifyName = Regex.Replace(ClassifyName, ".ts", "");
                ClassifyName = ClassifyName.Trim();

                string b = Regex.Match(ClassifyName, "(^.*?)[  第「(「\\[[『‘“【##〜]").Groups[1].Value;
                if (b != "") ClassifyName = b;

                /// 分類先フォルダを作り、出力先を決める
                /// 一応、フォルダのアクセスタイムを決める。
                string finaldir = Path.Combine(OutputDir, ClassifyName);
                try
                {
                    Directory.CreateDirectory(finaldir);
                    Directory.SetLastAccessTime(finaldir, DateTime.Now);
                    Directory.SetLastWriteTime(finaldir, DateTime.Now);
                    Directory.SetCreationTime(finaldir, DateTime.Now);
                }
                catch { }


                return Path.Combine(OutputDir, ClassifyName, Path.GetFileNameWithoutExtension(outname) + ".mp4");
            }
            else
            {
                ChannelName = "";
                return Path.ChangeExtension(InputFileName, ".mp4");
            }

        }

SARがすべて4:3とは限らないため、ffmpegでSARの情報を取得し、SAR 1:1となるように変更しておきます。
853:480は、720:400にダウンスケールするべきかもしれませんが、せっかくの水平解像度を維持したかったので、864:480とかいう変な解像度になっています。

                    /// -------------------------------------------------------------------
                    /// 2. ffmpegで出力解像度の決定
                    /// 
                    /// 結構、SAR4:3だったりSAR1:1だったり1440x1080だったり1920x1080だったり480x480だったり変な形式も多いので、
                    /// Width = w * sarx / sarh
                    /// Height = h
                    /// ということでSAR1:1になるように計算し直す。
                    /// また、計算結果1920x1080になったら、1280x720にダウンスケールするようにした。
                    /// -------------------------------------------------------------------
                    string ffmpegret = ExecRedirect(ffmpegPath, "-i \"" + M2VFile + "\"");

                    Match mm = Regex.Match(ffmpegret, @"(\d+)x(\d+) \[SAR (\d+)\:(\d+)");

                    /// デフォルトの大きさ
                    int x = 1280, y = 720;

                    /// ffmpegがうまく出力できていなかったら、とりあえず1280x720を使う。
                    if (mm.Success)
                    {
                        int mx = int.Parse(mm.Groups[1].Value);
                        int my = int.Parse(mm.Groups[2].Value);
                        int sarx = int.Parse(mm.Groups[3].Value);
                        int sary = int.Parse(mm.Groups[4].Value);
                        x = (int)((float)sarx / (float)sary * mx);
                        y = my;

                        /// ハイビジョンは720iに落とす
                        if (x == 1920 && y == 1080) { x = 1280; y = 720; }
                        if (x == 853 && y == 480) { x = 864; y = 480; }
                    }

                    


program.txtからアニメかどうかを取得します。アニメというフラグが立っていても、ドラマとなっていたりすることもあるので(仮面ライダーなど)その場合はアニメフラグをオフにするようにします。

                    /// -------------------------------------------------------------------
                    /// 4. .program.txtからジャンル情報を取得
                    /// 
                    /// アニメでないなら、アニメフラグをオフにする。
                    /// -------------------------------------------------------------------
                    Console.WriteLine();
                    Console.WriteLine();
                    Console.WriteLine("*****************************");
                    Console.WriteLine("    ジャンル情報取得");
                    Console.WriteLine("*****************************");

                    IsAnime = true;
                    try
                    {
                        Match m = Regex.Match(ProgramTxt, "ジャンル : .*? : ", RegexOptions.Singleline);
                        if (m.Success)
                        {
                            Console.WriteLine(m.Value);
                            if (!m.Value.Contains("アニメ") || m.Value.Contains("ドラマ") || m.Value.Contains("バラエティ"))
                            {
                                IsAnime = false;
                            }
                        }
                    }
                    catch { }

AUCで出力します。これはAUCのサンプルをC#に移植しただけですが、.glファイルの読み込みのウィンドウ中は動作を抑制するためちょっと改良しました。
aviutlはプロセスごとに新しく起動しています。1つめのプロファイルにデフォルト出力となっていれば動作するはずです。

                    /// -------------------------------------------------------------------
                    /// 6. aviutlで出力
                    /// 
                    /// 本来なら、x264とNeroAACEncとmp4boxなんかでやるべき何だけど
                    /// avs2wavがうまくいかなかったのでaviutlに頼ることに。
                    /// -------------------------------------------------------------------

                    Console.WriteLine();
                    Console.WriteLine();
                    Console.WriteLine("*****************************");
                    Console.WriteLine("    Aviutlで出力");
                    Console.WriteLine("*****************************");
                    

                    /// とにかく新しく実行する
                    object   ret = Auc("exec", "\"" + AviutlPath + "\"");
                    int   hwnd = (int)ret;
                    if (hwnd == 0) return;


                    /// 
                    if (File.Exists(Path.ChangeExtension(InputFileName, ".gl")))
                    {
                        Auc("open", hwnd.ToString() + " \"" + AVSFile + "\"");
                        Thread.Sleep(1000);
                    }
                    else
                    {
                        Auc("open", hwnd.ToString() + " \"" + AVSFile + "\"");
                        Thread.Sleep(5000);
                        while ((int)FindWindowEx(IntPtr.Zero, IntPtr.Zero, "#32770", "MPEG-2 VIDEO VFAPI Plug-In : 情報") != 0)
                        {
                            Thread.Sleep(1000);
                        }
                    }

                    int prof = 0;
                    Auc("setprof", hwnd.ToString() + " " + prof);
                    Thread.Sleep(1000);

                    Console.WriteLine("出力開始");
                    Auc("plugout", hwnd.ToString() + " 1 \"" + OutputFileName + "\"");
                    Thread.Sleep(1000);
  
                    Auc("wait", hwnd.ToString());

                    Auc("exit", hwnd.ToString());
                    Thread.Sleep(1000);
C#のプログラムで完全自動化

batファイルの方がうまくかけるという方がいたら、そっちを使われるのがいいです。
Visual C# 2010なんかでコンパイルしてみてください。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.Diagnostics;
using PostRec.Properties;
using System.Threading;
using System.Reflection;
using System.Runtime.InteropServices;

namespace PostRec
{
    class Program
    {
        /// <summary>
        /// とにかくArgumentを入れて、実行する
        /// </summary>
        /// <param name="ExeName"></param>
        /// <param name="Args"></param>
        void Exec(string ExeName, string Args)
        {
            Process p = new Process();
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.FileName = ExeName;
            p.StartInfo.Arguments = Args;
            p.Start();
            p.Refresh();
            p.PriorityClass = ProcessPriorityClass.Idle;
            while (p.HasExited == false)
            {
                Thread.Sleep(100);
            }
            p.WaitForExit();
        }

        /// <summary>
        /// StandardErrorを出力する。
        /// </summary>
        /// <param name="ExeName"></param>
        /// <param name="Args"></param>
        /// <returns></returns>
        string ExecRedirect(string ExeName, string Args)
        {
            Process p = new Process();
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.FileName = ExeName;
            p.StartInfo.Arguments = Args;
            p.StartInfo.RedirectStandardError = true;
            p.StartInfo.RedirectStandardInput = true;
            p.StartInfo.RedirectStandardOutput = true;
            p.Start();
            p.Refresh();
            p.PriorityClass = ProcessPriorityClass.Idle;
            return p.StandardError.ReadToEnd();
        }

        /// <summary>
        /// 分類を行う。
        /// </summary>
        /// <param name="InputFileName"></param>
        /// <param name="OutputDir"></param>
        /// <param name="ChannelName"></param>
        /// <returns></returns>
        string DoClassify(string InputFileName, string OutputDir, out string ChannelName)
        {
            string ClassifyName = "";   
            string outname = "";
            bool DefaultEncode = false;

            /// 新しいフォーマット通りなら
            Match m = Regex.Match(Path.GetFileName(InputFileName), "(^\\d+-)\\[(.*?)\\](.*)\\.ts");

            ChannelName = "";
            
            if (m.Success)
            {
                ClassifyName = m.Groups[3].Value;
                ChannelName = m.Groups[2].Value;
                outname = m.Groups[1].Value + m.Groups[3].Value;
            }
            else
            {
                DefaultEncode = true;
            }

            /// 出力先があれば分類情報を作る
            if (OutputDir != "" && DefaultEncode == false)
            {

                /// 分類名
                ClassifyName = Regex.Replace(ClassifyName, "\\[.+?\\]", "");
                ClassifyName = Regex.Replace(ClassifyName, "<.*?>", "");
                ClassifyName = Regex.Replace(ClassifyName, "アニメ[  ]+", "");
                ClassifyName = Regex.Replace(ClassifyName, ".mp4", "");
                ClassifyName = Regex.Replace(ClassifyName, ".ts", "");
                ClassifyName = ClassifyName.Trim();

                string b = Regex.Match(ClassifyName, "(^.*?)[  第「(「\\[[『‘“【##〜]").Groups[1].Value;
                if (b != "") ClassifyName = b;

                /// 分類先フォルダを作り、出力先を決める
                /// 一応、フォルダのアクセスタイムを決める。
                string finaldir = Path.Combine(OutputDir, ClassifyName);
                try
                {
                    Directory.CreateDirectory(finaldir);
                    Directory.SetLastAccessTime(finaldir, DateTime.Now);
                    Directory.SetLastWriteTime(finaldir, DateTime.Now);
                    Directory.SetCreationTime(finaldir, DateTime.Now);
                }
                catch { }


                return Path.Combine(OutputDir, ClassifyName, Path.GetFileNameWithoutExtension(outname) + ".mp4");
            }
            else
            {
                ChannelName = "";
                return Path.ChangeExtension(InputFileName, ".mp4");
            }

        }


        /// <summary>
        /// AUCを実行
        /// </summary>
        object Auc(string command, string arg)
        {
            Type WshShell;  
            WshShell = Type.GetTypeFromProgID("Wscript.Shell");  
            Object obj = Activator.CreateInstance(WshShell);  
  
            Object type = 2;  
            Object wait = true;  
  
            return WshShell.InvokeMember(  
                "Run",   
                BindingFlags.InvokeMethod,   
                null,   
                obj,
                new Object[] { Path.Combine(Settings.Default.AUCDir, "auc_" + command + ".exe ") + arg, type, wait }  
            );  
        }

        /// <summary>
        /// テキストファイルを全部読み込み
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        string ReadFile(string filename)
        {
            try
            {
                using (StreamReader sr = new StreamReader(filename, Encoding.GetEncoding("shift-jis")))
                {
                    return sr.ReadToEnd();
                }
            }
            catch { return ""; }
        }

        /// <summary>
        /// ファイルをテキストで保存
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="filestring"></param>
        void WriteFile(string filename, string filestring)
        {
            try
            {
                using (StreamWriter sw = new StreamWriter(filename, false, Encoding.GetEncoding("shift-jis")))
                {
                    sw.Write(filestring);
                }
            }
            catch { }
        }


        /// <summary>
        /// GOPリスト出力ウィンドウ検索用
        /// </summary>
        /// <param name="parentHandle"></param>
        /// <param name="childAfter"></param>
        /// <param name="className"></param>
        /// <param name="windowTitle"></param>
        /// <returns></returns>
        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);

        /// <summary>
        /// logo.lgdのファイル名・番組名
        /// </summary>
        /// string[] ChannelArray = { "東海テレビ011", "中京テレビ1", "CBC", "メ〜テレ", "テレビ愛知1", "BS−TBS", "BS11", };


        public void MakeDefaultAVS(string AVSFile, string FilterTemplatePath, string M2VFile, string WavFile)
        {
            /// -------------------------------------------------------------------
            /// 3-2. DefaultのAVS
            /// 
            /// -------------------------------------------------------------------
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("*****************************");
            Console.WriteLine("   デフォルトのAVS出力");
            Console.WriteLine("*****************************");

            string template = ReadFile(FilterTemplatePath);
            template = template.Replace("%m2v", M2VFile);
            template = template.Replace("%wav", WavFile);
            WriteFile(AVSFile, template);
            Console.WriteLine("出力: " + AVSFile);

        }


        /// <summary>
        /// メインプログラム
        /// </summary>
        /// <param name="args"></param>
        public void Run(string[] args)
        {
            try
            {
                string OrgName = args[0];
                string OutputDir = Settings.Default.OutputDir;
                string OutputFileName = "";
                string InputFileName = OrgName;
                string ChannelName = "";


                string M2VFile = Path.ChangeExtension(InputFileName, ".m2v");
                string WavFile = Path.ChangeExtension(InputFileName, ".wav");
                string AVSFile = Path.ChangeExtension(InputFileName, ".avs");

                string BonTsDemuxPath = Settings.Default.BonTsDemuxPath; // @"O:\PT2\Encoder\BonTsDemux\BonTsDemux.exe";
                string S0ServicePath = Settings.Default.S0ServicePath; // @"O:\PT2\EpgDataCap_Bon\EpgTimerBon\BonDriver_Spinel_PT-S0(Spinel:PT_0/S0).ChSet2.txt";
                string T0ServicePath = Settings.Default.T0ServicePath;//  @"O:\PT2\EpgDataCap_Bon\EpgTimerBon\BonDriver_Spinel_PT-T0(Spinel:PT_0/T0).ChSet2.txt";
                string ffmpegPath = Settings.Default.ffmpegPath; // @"O:\PT2\Encoder\ffmpeg.exe";
                string LogoDir = Settings.Default.LogoDir; // @"O:\PT2\Encoder\LogoData";

                string AVS2YUVPath = Settings.Default.AVS2YUVPath;// @"O:\PT2\Encoder\aviutl\avs2yuv.exe";
                string M2VVFPPath = Settings.Default.M2VVFPPath; // @"O:\PT2\Encoder\aviutl\vfp\m2v.vfp";
                string LogoGuilloPath = Settings.Default.LogoGuilloPath; // @"O:\PT2\Encoder\logoGuillo\logoGuillo.exe";
                string AviutlPath = Settings.Default.AviutlPath; // @"O:\PT2\Encoder\aviutl\aviutl.exe";

                string FilterTemplatePath = Settings.Default.FilterTemplatePath; // @"o:\pt2\encoder\avisynth\filtertemplate.txt";
                string PreFilterPath = Settings.Default.PreFilterPath;// @"o:\pt2\encoder\avisynth\prefilter.txt";
                string PostFilterDelogoPath = Settings.Default.PostFilterDelogoPath; // @"o:\pt2\encoder\avisynth\postfilterDelogo.txt";
                string PostFilterSizePath = Settings.Default.PostFilterSizePath; // @"o:\pt2\encoder\avisynth\postfilterSize.txt";
                string PostFilterAnimePath = Settings.Default.PostFilterAnimePath; // @"o:\pt2\encoder\avisynth\postfilterAnime.txt";

                string LogoPath = "";

                if (Path.GetExtension(InputFileName).ToLower() != ".ts")
                {
                    Console.WriteLine("tsファイルでない");
                    return;
                }

                string ProgramTxt = ReadFile(InputFileName + ".program.txt");
                OutputFileName = DoClassify(InputFileName, OutputDir, out ChannelName);


                /// 録画したTSなら
                if (ChannelName != "")
                {
                    string[] S0ServiceName = ReadFile(S0ServicePath).Split('\n');
                    string[] T0ServiceName = ReadFile(T0ServicePath).Split('\n');

                    /// -------------------------------------------------------------------
                    /// 1. TSをm2vとwavにdemux
                    /// 
                    /// サービス番号を検索して、そのサービスだけ取り出す。
                    /// 
                    /// EPGTimer_BonのChSet2.txtをSとT両方で読み込み、
                    /// サービス番号は左から4番目(0 order, 3)にあるので、サービス名と一致するサービス番号を取り出す。
                    /// -------------------------------------------------------------------
                    Console.WriteLine("<録画tsモード>");
                    Console.WriteLine();
                    Console.WriteLine();
                    Console.WriteLine("*****************************");
                    Console.WriteLine("    BonTSDemuxによる分離");
                    Console.WriteLine("*****************************");

                    /// サービス番号を検索する
                    /// 1番はじめにみつかったサービス名のIDを用いる。
                    int ServiceNumber = 0;
                    foreach (string s in S0ServiceName)
                    {
                        if (s.Contains(ChannelName))
                        {
                            ServiceNumber = int.Parse(s.Split('\t')[7]);
                            break;
                        }
                    }
                    foreach (string s in T0ServiceName)
                    {
                        if (s.Contains(ChannelName))
                        {
                            ServiceNumber = int.Parse(s.Split('\t')[7]);
                            break;
                        }
                    }

                    /// サービス名に基づいてBonTsDemux行う。
                    string servicearg = "";
                    if (ServiceNumber != 0) servicearg = " -srv " + ServiceNumber.ToString();
                    Console.WriteLine("Service ID = " + ServiceNumber);

                    /// m2vがあればなにもしない。
                    if (!File.Exists(M2VFile))
                    {
                        Console.WriteLine("Exec : " + BonTsDemuxPath + " -nogui -i " + "\"" + InputFileName + "\"" + servicearg);
                        Exec(BonTsDemuxPath, "-nogui -i " + "\"" + InputFileName + "\"" + servicearg);
                    }
                    else
                    {
                        Console.WriteLine("m2vが存在するのでスキップ");
                    }

                    Console.WriteLine("完了");

                    /// -------------------------------------------------------------------
                    /// 2. ffmpegで出力解像度の決定
                    /// 
                    /// 結構、SAR4:3だったりSAR1:1だったり1440x1080だったり1920x1080だったり480x480だったり変な形式も多いので、
                    /// Width = w * sarx / sarh
                    /// Height = h
                    /// ということでSAR1:1になるように計算し直す。
                    /// また、計算結果1920x1080になったら、1280x720にダウンスケールするようにした。
                    /// -------------------------------------------------------------------
                    string ffmpegret = ExecRedirect(ffmpegPath, "-i \"" + M2VFile + "\"");

                    Match mm = Regex.Match(ffmpegret, @"(\d+)x(\d+) \[SAR (\d+)\:(\d+)");

                    /// デフォルトの大きさ
                    int x = 1280, y = 720;

                    /// ffmpegがうまく出力できていなかったら、とりあえず1280x720を使う。
                    if (mm.Success)
                    {
                        int mx = int.Parse(mm.Groups[1].Value);
                        int my = int.Parse(mm.Groups[2].Value);
                        int sarx = int.Parse(mm.Groups[3].Value);
                        int sary = int.Parse(mm.Groups[4].Value);
                        x = (int)((float)sarx / (float)sary * mx);
                        y = my;

                        /// ハイビジョンは720iに落とす
                        if (x == 1920 && y == 1080) { x = 1280; y = 720; }
                        if (x == 853 && y == 480) { x = 864; y = 480; }
                    }

                    
                    bool IsAnime = false;
                    bool IsDelogo = false;

                    string[] logos = Directory.GetFiles(LogoDir, "*.lgd");
                    for (int i = 0; i < logos.Length; i++)
                    {
                        logos[i] = Path.GetFileNameWithoutExtension(logos[i]);
                    }

                    /// -------------------------------------------------------------------
                    /// 3. 暫定的AVS出力
                    /// 
                    /// CMカット情報、ファイル情報だけが書き込まれたAVSを出力する。
                    /// -------------------------------------------------------------------
                    if (logos.Contains(ChannelName))
                    {

                        /// -------------------------------------------------------------------
                        /// 3-1. trim情報をlogoguilloで取得
                        /// 
                        /// -------------------------------------------------------------------
                        Console.WriteLine();
                        Console.WriteLine();
                        Console.WriteLine("*****************************");
                        Console.WriteLine("   logoGuilloによるCMカット");
                        Console.WriteLine("*****************************");

                        LogoPath = Path.Combine(LogoDir, ChannelName + ".lgd");

                        string logoguilloarg = "-video " + "\"" + M2VFile + "\"" +
                            " -lgd " + "\"" + LogoPath + "\"" +
                            " -avs2x " + "\"" + AVS2YUVPath + "\"" +
                            " -avsPlg " + "\"" + M2VVFPPath + "\"" +
                            " -prm " + "\"" + LogoPath + ".autoTune.param" + "\"" +
                            " -out " + "\"" + AVSFile + "\"" +
                            " -outFmt avs " +
                            " -delFstBlk 20.0 -delLstBlk 5.0";

                        if (!File.Exists(AVSFile))
                        {
                            Console.WriteLine("Exec : " + LogoGuilloPath + logoguilloarg);
                            Exec(LogoGuilloPath, logoguilloarg);
                        }
                        else
                        {
                            Console.WriteLine("avsが存在するのでスキップ");
                        }

                        Console.WriteLine("完了");

                        try
                        {
                            FileInfo fi = new FileInfo(AVSFile);
                            if (fi.Length == 0)
                            {
                                Console.WriteLine("***********AVSが出力されなかったのでデフォルト処理*************");
                                MakeDefaultAVS(AVSFile, FilterTemplatePath, M2VFile, WavFile);
                            }
                        }
                        catch { }

                        IsDelogo = true;

                    }
                    else
                    {
                        MakeDefaultAVS(AVSFile, FilterTemplatePath, M2VFile, WavFile);
                        IsDelogo = false;
                    }


                    /// -------------------------------------------------------------------
                    /// 4. .program.txtからジャンル情報を取得
                    /// 
                    /// アニメでないなら、アニメフラグをオフにする。
                    /// -------------------------------------------------------------------
                    Console.WriteLine();
                    Console.WriteLine();
                    Console.WriteLine("*****************************");
                    Console.WriteLine("    ジャンル情報取得");
                    Console.WriteLine("*****************************");

                    IsAnime = true;
                    try
                    {
                        Match m = Regex.Match(ProgramTxt, "ジャンル : .*? : ", RegexOptions.Singleline);
                        if (m.Success)
                        {
                            Console.WriteLine(m.Value);
                            if (!m.Value.Contains("アニメ") || m.Value.Contains("ドラマ") || m.Value.Contains("バラエティ"))
                            {
                                IsAnime = false;
                            }
                        }
                    }
                    catch { }




                    /// -------------------------------------------------------------------
                    /// 5. .avsの微調整
                    /// 
                    /// Delogoできるならロゴ除去を。
                    /// サイズ変更はすべてに。
                    /// アニメならアニメ用フィルタをかける。
                    /// -------------------------------------------------------------------
                    Console.WriteLine();
                    Console.WriteLine();
                    Console.WriteLine("*****************************");
                    Console.WriteLine("   AVSファイルの書き換え");
                    Console.WriteLine("*****************************");
                    string AVSText = ReadFile(AVSFile);

                    string PreFilter = ReadFile(PreFilterPath);
                    string PostFilterDelogo = ReadFile(PostFilterDelogoPath);
                    string PostFilterSize = ReadFile(PostFilterSizePath);
                    string PostFilterAnime = ReadFile(PostFilterAnimePath);


                    AVSText = AVSText.Replace("#REPKEY_1#", PreFilter);

                    /// %lはロゴのパスに変換される。
                    PostFilterDelogo = PostFilterDelogo.Replace("%l", LogoPath);

                    /// %x, %yはリサイズ後のファイルサイズに変換される。
                    PostFilterSize = PostFilterSize.Replace("%x", x.ToString()).Replace("%y", y.ToString());

                    /// ロゴ削除のフィルタ
                    if (IsDelogo)
                    {
                        Console.WriteLine("+ ロゴ除去フィルタ : " + LogoPath);
                        AVSText = AVSText.Replace("#REPKEY_30#", PostFilterDelogo);
                    }

                    /// リサイズフィルタ1
                    Console.WriteLine("+ リサイズフィルタ : " + x.ToString() + "x" + y.ToString());
                    AVSText = AVSText.Replace("#REPKEY_31#", PostFilterSize);

                    /// アニメフィルタ
                    if (IsAnime)
                    {
                        Console.WriteLine("+ アニメ向けフィルタ: Warpsharp + Edge強調");
                        AVSText = AVSText.Replace("#REPKEY_32#", PostFilterAnime);
                    }

                    /// 出力
                    WriteFile(AVSFile, AVSText);
                    Console.WriteLine("出力 " + AVSFile);
                    Console.WriteLine(AVSText);


                    /// -------------------------------------------------------------------
                    /// 6. aviutlで出力
                    /// 
                    /// 本来なら、x264とNeroAACEncとmp4boxなんかでやるべき何だけど
                    /// avs2wavがうまくいかなかったのでaviutlに頼ることに。
                    /// -------------------------------------------------------------------

                    Console.WriteLine();
                    Console.WriteLine();
                    Console.WriteLine("*****************************");
                    Console.WriteLine("    Aviutlで出力");
                    Console.WriteLine("*****************************");
                    

                    /// とにかく新しく実行する
                    object   ret = Auc("exec", "\"" + AviutlPath + "\"");
                    int   hwnd = (int)ret;
                    if (hwnd == 0) return;


                    /// 
                    if (File.Exists(Path.ChangeExtension(InputFileName, ".gl")))
                    {
                        Auc("open", hwnd.ToString() + " \"" + AVSFile + "\"");
                        Thread.Sleep(1000);
                    }
                    else
                    {
                        Auc("open", hwnd.ToString() + " \"" + AVSFile + "\"");
                        Thread.Sleep(5000);
                        while ((int)FindWindowEx(IntPtr.Zero, IntPtr.Zero, "#32770", "MPEG-2 VIDEO VFAPI Plug-In : 情報") != 0)
                        {
                            Thread.Sleep(1000);
                        }
                    }

                    int prof = 0;
                    Auc("setprof", hwnd.ToString() + " " + prof);
                    Thread.Sleep(1000);

                    Console.WriteLine("出力開始");
                    Auc("plugout", hwnd.ToString() + " 1 \"" + OutputFileName + "\"");
                    Thread.Sleep(1000);
  
                    Auc("wait", hwnd.ToString());

                    Auc("exit", hwnd.ToString());
                    Thread.Sleep(1000);


                    if (File.Exists(OutputFileName))
                    {
                        FileInfo fi = new FileInfo(OutputFileName);
                        /// 10MB以上なら、たぶん成功してる
                        if (fi.Length > 1024 * 1024 * 10)
                        {
                            /// 中間ファイルを削除し、ファイルを移動させる。
                            string[] files = Directory.GetFiles(Path.GetDirectoryName(InputFileName), Path.GetFileNameWithoutExtension(InputFileName) + "*");
                            foreach (string s in files)
                            {
                                try
                                {
                                    /// とりあえずtsと.program.txtだけ移動先に動かして、残りは削除
                                    if (s == InputFileName || s == InputFileName + ".program.txt")
                                    {
                                        File.Move(s, Path.Combine(Settings.Default.ConvertedFilePath, Path.GetFileName(s)));
                                    }
                                    else
                                        File.Delete(s);
                                }
                                catch { }
                            }
                        }
                    }
                }
                else
                {
                    Console.WriteLine("<通常エンコードモード>");
                    Console.WriteLine("HandBrakeでエンコードを開始します。");
                    /// CMカットができないなら
                    /// デフォルトのエンコード
                    string EncoderArgs = Settings.Default.DefaultEncoderArgs.Replace("%1", InputFileName);
                    EncoderArgs = EncoderArgs.Replace("%2", OutputFileName);
                    Exec(Settings.Default.DefaultEncoderPath, EncoderArgs);
                /// 変換後のファイルを移動
                if (Settings.Default.ConvertedFilePath != "")
                    File.Move(InputFileName, Path.Combine(Settings.Default.ConvertedFilePath, Path.GetFileName(InputFileName)));
                }
            }
            catch (Exception e)
            {
                using (StreamWriter sw = new StreamWriter(Settings.Default.LogFileName, true, Encoding.GetEncoding("shift-jis")))
                {
                    sw.WriteLine(DateTime.Now.ToString());
                    sw.WriteLine(e.Message);
                    sw.WriteLine(e.InnerException);
                    sw.WriteLine(e.StackTrace);
                    sw.WriteLine("------------");
                }

            }
        }



        /// <summary>
        /// Args 
        /// [0] input file
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            Program p = new Program();
            p.Run(args);
        }
    }
}

適当にSettingsの内容を書き換えれば動くと思います。
でもそれくらいできる人なら自分でくむと思いますが・・・


リクエストがあればバイナリをおこうかとも思いますが、とりあえず面倒なのでそのままで。

Beer Media Serverをインストール

インストールし、iniを書き換えてBraviaで楽しむ。

PT2の地デジ1440x1080tsファイルを自動CMカットして 1280x720にインターレースを保持したままリサイズしエッジ強調フィルタとWarpsharpフィルタを適用する。

  • TSファイルはmp4にしたい。
  • どうせするなら、サイズを小さく、500MB前後に納めたい
  • 1280x720のソースと同じサイズにリサイズしたい。
  • どうせエンコするなら、フィルタを適用したい
  • VFRと自動フィールドシフトを試したけど、Braviaでの再生でつっかかる部分がかなりある。自動でインタレ解除+FPSVFRするなんてムリと判断
    • インターレース保持派になりました。
    • Braviaのインタレ除去+再生時フィルタは超優秀。640x480のPCで見るとつぶれてるような動画が1.5mの場所でかなりくっきり見える。
      • 近づくとやっぱり補完してるなーっていうのはよくわかるんだけどね。
  • CMカットもしたい。
  • ロゴも除去したい。
  • 毎日これらの操作をやるなんてまっぴらごめん。完全自動化したい。

これらのことを全部やりたい。PT2で深夜アニメを録画できるものをすべて録画するのが目標。
朝起きると、CMカットされて圧縮されたMP4が生成されている、という状態が理想。




ようやく目的とする自動エンコシステムが完成したのでメモっておく。
誰か参照してくれるとうれしいな。

tsから目的とするStreamをDemuxする。

衛星放送でも特にスカパーe2なんかは複数の放送が一つのtsで送られてきて、とんでもないサイズのtsができることが多々ある。
それを自動エンコするためには、自動で目的とするStreamのみをDemuxしないといけない。

EPGTimer_Bonを使って、program.txtを出力するようにし、録画ファイル名マクロを次のようにする。
$SDYYYY$$SDMM$$SDDD$$STHH$$STMM$$STSS$01-[$ServiceName$]$Title$.ts
チューナーIDのマクロが無かったので、暫定的に01とし、番組名がファイルの先端につくようにした。

EPGTimer_BonのChSet2.txtをSとT両方で読み込む。サービス番号は左から4番目にあるので、サービス名と一致するサービス番号を取り出す。

BonTsDemuxでその取り出したサービス番号を -srv nnnn として送る。


これで、衛星放送多重tsStream問題が解決した。

自動CMカットは話題のLogoGuilloを使う。

http://loggialogic.blogspot.jp/2011/11/cm-logoguillo.html

とにかく、こちらのページ書いてあるように、Aviutlにm2v.vfsを導入しAviutlでロゴ解析をがんばって行い、ロゴの表示されたフレーム番号を記録し、LogoGuilloのパラメータ調節モードで開いて調節、テストという流れ。


出力形式は.avsとした。

ffmpegを使って、出力されたm2vファイルの形式を確認する。

結構、SAR4:3だったりSAR1:1だったり1440x1080だったり1920x1080だったり480x480だったり変な形式も多いので、
Width = w * sarx / sary
Height = h
ということでSAR1:1になるように計算し直す。
また、計算結果1920x1080になったら、1280x720にダウンスケールするようにした。

.program.txtから番組のジャンルを取得する

EPGTimer_Bon形式にすると、「ジャンル:」という項目が出力される。
その行に「アニメ」の文字列があった場合、アニメ用のWarpSharpフィルタ、エッジ強調フィルタをオンにする。

AVSを編集し、リサイズ・フィルタをかける

次に、出力された.avsを書き換える。
ちょうど#REPKEY_1#のように、「置換してください!」っていう感じのキーが一杯並んでたので、それらを置換するように。
http://www.h6.dion.ne.jp/~nytheta/dtv/encode-video.html
ここを参考に、

#REPKEY_1#を次に置換。

global AviUtl_plugin_directory = "O:\PT2\Encoder\avisynth\"
global AviUtl_plugin_copy = false
global AviUtl_plugin_debug = false
global AviUtl_plugin_thread = 6
LoadPlugin("O:\PT2\Encoder\avisynth\warpsharp.dll")
LoadPlugin("O:\PT2\Encoder\avisynth\delogo.dll")

####################################
# エッジレベル調整 MT ver 0.7
####################################
# i0 : 特性 default(10) range(-31,31)
# i1 : 閾値 default(16) range(0,255)
# i2 : 黒補正 default(0) range(0,31)
# i3 : 白補正 default(0) range(0,31)
function AU_edgelevelMT(clip clip,
\ int "i0", int "i1", int "i2", int "i3")
{
LoadAviUtlFilterPlugin(AviUtl_plugin_directory+"edgelevelMT.auf", "_AU_edgelevelMT", copy=AviUtl_plugin_copy, debug=AviUtl_plugin_debug, thread=AviUtl_plugin_thread)
#LoadAviUtlFilterPlugin2(AviUtl_plugin_directory+"edgelevelMT.auf", "_AU_edgelevelMT", copy=AviUtl_plugin_copy, debug=AviUtl_plugin_debug, thread=AviUtl_plugin_thread)
return clip._AU_edgelevelMT(
\ default(i0,10), default(i1,16), default(i2,0), default(i3,0))
}
# example:
# ConvertYUY2ToAviUtlYC()
# AU_edgelevelMT(10,16,0,0)
# ConvertAviUtlYCToYUY2()

# フィールド別にフィルタを適用するための関数
function FPNR(clip clip, string "filter")
{
clip
SeparateFields() #フィールド分割
Top = SelectEven #トップフィールド
Bottom = SelectOdd #ボトムフィールド
Top = Eval("Top." + filter)
Bottom = Eval("Bottom." + filter)
Interleave(Top, Bottom).Weave() #インタレースに戻す
return last
}

# プログレッシブでフィルタを適用するための関数
function FPProg(clip clip, bool parity, string "filter")
{
clip
Bob(b=0,c=0.5) #プログレッシブにする
Eval(filter)
parity ? AssumeTFF() : AssumeBFF()
SeparateFields()
SelectEvery(4, 0, 3) #フィールドの選択
Weave() #インタレースに戻す
return last
}

#REPKEY_30#を置換(ロゴファイルがあるときのみ)

#ロゴ除去
EraseLOGO(logofile="%l", pos_x=0, pos_y=0, depth=128, yc_y=0, yc_u=0, yc_v=0, start=0, fadein=0, fadeout=0, end=-1, interlaced=true)

#REPKEY_31#を置換

AssumeTFF()
parity = GetParity()
FPProg(parity, "Lanczos4Resize(%x,%y)")

#REPKEY_32#を置換(アニメフラグがオンのとき)

# WarpSharp
FPNR("WarpSharp(95,3,85,0)")

# エッジレベル調整
ConvertYUY2ToAviUtlYC()
FPNR("AU_edgelevelMT(9,20,5,2)")
ConvertAviUtlYCToYUY2()

aviutlのヘルパーをつかって操作。

AviUtl Control ver1.4を使う。
http://www.geocities.co.jp/aji_0/
まあ、書こうと思えばC#で書けたんだけど、めんどくさいんで再利用した。


C#だとこんな感じ。
.glが存在しないときに、GOPリストを作るからそのときにちゃんと止まるように設定。

                    object   ret = Auc("exec", "\"" + @"c:\aviutl\aviutl.exe" + "\"");
                    int   hwnd = (int)ret;
                    if (hwnd == 0) return;


                    if (File.Exists(Path.ChangeExtension(InputFileName, ".gl")))
                    {
                        Auc("open", hwnd.ToString() + " \"" + AVSFile + "\"");
                        Thread.Sleep(1000);
                    }
                    else
                    {
                        Auc("open", hwnd.ToString() + " \"" + AVSFile + "\"");
                        Thread.Sleep(5000);
                        while ((int)FindWindowEx(IntPtr.Zero, IntPtr.Zero, "#32770", "MPEG-2 VIDEO VFAPI Plug-In : 情報") != 0)
                        {
                            Thread.Sleep(1000);
                        }
                    }

                    int prof = 0;
                    Auc("setprof", hwnd.ToString() + " " + prof);
                    Thread.Sleep(1000);

                    Console.WriteLine("出力開始");
                    Auc("plugout", hwnd.ToString() + " 1 \"" + OutputFileName + "\"");
                    Thread.Sleep(1000);
  
                    Auc("wait", hwnd.ToString());

                    Auc("exit", hwnd.ToString());
                    Thread.Sleep(1000);

とりあえずやっつけでこんな感じ。ちょっとしたらC#スクリプトをきれいに書き直して貼るか。

PT2で録画したtsファイルをbatファイルで自動的にmp4とするアニメエンコについて、最も良いと思われる方法をいっぱい考えてみた

とりあえず、テレシネ・インタレ解除を高品質で、自動でやってくれるHandbrakeが最強。
HandbrakeCLIに次のコマンドを流すことで対応とした。

%1に入力ファイル名を、%2に出力ファイル名を。

  • i %1 -t 1 -o %2 -f mp4 -4 --detelecine --decomb -w 1280 -l 720 -e x264 -q 21 -r 29.97 --pfr -a 1,1 -E faac,ffac3 -B 160,160 -6 dpl2,auto -R Auto,Auto -D 0,0 --gain=0,0 --audio-copy-mask none --audio-fallback ffac3 -x merange=32:deblock=-2,-2:b-adapt=2:rc-lookahead=50:ref=6:bframes=6:me=umh:analyse=all:trellis=2:no-fast-pskip=1:scenecut=70:b-pyramid=strict --verbose=1

まず、CRFのqualityは21で。20でも良いんだけど容量削減+地デジソースのもともとの汚さを考慮すると、21でも充分耐えられる範囲かと。
第一、20と21でファイルサイズに25%の差が出るのは大きい。

動画サイズは1280x720で固定とする。x1080にしてもいいんだろうけれど、地デジのソース自体がx720なので、劣化が無いと考えて1280x720で。

    • pfrでフレームレートを可変に、最大を29.97とする。こうすることでレートのダイエットができる。

アニメのOPだけ24FPSでCMは30FPS、本編が24FPSで予告が30FPSっていうのもよくあることだからこれで可変レート再生ができ、mp4のフレームがすべてプログレッシブになる。

  • x以下は一番がんばりました。

Phenom II 1100TのPCなので、エンコに負荷を思いっきりかけても、だいたい16FPSくらいでエンコできる。
meはumhで使える中で最もよい方法に。
analyse=allでブロック解析をすべてに。
ref=6, bframe=6でなるだけ参照範囲を多く。

これらのオプションで5%ほどファイルサイズが縮みました。



エンコした結果は満足のいく内容になりました。再生にはBeer-Media-Serverを利用。
http://code.google.com/p/beer-media-server/

糞なWindowsMediaPlayer11のDLNAから離脱し、ライブラリの更新を行うことなくファイルをエクスプローラー感覚で見ることができる。
WMP11のライブラリはなぜか更新されなくなったりと使い勝手があまりにも悪い。Microsoftむかつく


視聴にはBraviaを使用。ちゃんと4倍速フィルタがオンになって、スクロール画面ではヌルヌルの高画質を見ることができる。
Braviaのモーション予測ルーチンは超優秀だね。


アニオタならテレビを買い換えるなら4倍速のBravia一択だと思います。



PT2を使っているなら、Bat処理にHandbrakeCLIのパスと上記のコマンドで、%1と%2を出力先に変えておけば完璧かと。



幸せエンコ生活が始まったので記念に書いておく。

1日のタスク

筋トレ

腕立て伏せ 10回x3セット
腹筋 10回x3セット(しっかり腹部に負荷をかける)
スクワット 20kg負荷で30回
ひざに肘をつけて上腕二頭筋を鍛えるやつ 7.5kg 10回x3セット
後ろで上腕三頭筋を鍛えるやつ 7.5kg 10回x3セット
屈筋群を鍛えるやつ 7.5kg 20回x3セット
広背筋を鍛える膝と片手をついてやるやつ 12.5kg 10回x3セット

これだけはとりあえず毎日やろう!

ジムで
シーテッドチェストプレス
レッグエクステンション

など