Created
May 4, 2011 20:50
-
-
Save moyashi/956007 to your computer and use it in GitHub Desktop.
TSSplitter + (BonTsDemuxもしくはHandBrakeCLI)という組み合わせでiPhone向けエンコードをするためのVBScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'/* taskenc_m.vbs */ | |
' Original version: http://www13.atwiki.jp/mediacoder/pages/68.html | |
' Forked by moyashi (http://hitoriblog.com/?p=2818) | |
'使い方: cscript taskenc_m.vbs "f:\movies\hogehoge.ts" | |
'TVRockからの起動: TN:start "Encodeing(%4)" /min /LOW cscript "e:\bin\taskenc_m.vbs" "%1" | |
' Ver.0.3: 2012.04.28 HandBrakeCLIを使う設定の「-o」にフルパスを渡していなかったので修正 | |
' @yoheisnet さんに教えてもらったHandBrakeCLIのオプションを採用してみた | |
' Ver.0.2: 2011.05.08 HandBrakeCLIを使う設定を追加 | |
' Ver.0.1: 2011.05.05 | |
' TSSplitter + (BonTsDemuxもしくはHandBrakeCLI)という組み合わせで | |
' iPhone向けエンコードをする目的で改変したtaskenc.vbs | |
' | |
' オリジナル版と異なり、排他制御にロックファイルでなく | |
' ffmpegのプロセスの有無、CPU使用率の高低の情報を利用しています | |
' ffmpegが既に動いていないか、もしくは動いていてもCPU使用率が低ければ | |
' (これは悪いものを食ったffmpegがストールしているケースを想定) | |
' 処理を開始します | |
' よって何かの拍子にロックファイルが作られたままになり、後から実行された | |
' スクリプトが立ち往生することがありません | |
' 動作確認環境: Windows XP SP3 | |
'/* directive */ | |
option explicit | |
'/* variable definitions */ | |
dim tssplitter | |
dim tssplitter_opt | |
dim bontsdemux | |
dim bontsdemux_opt | |
dim handbrakecli | |
dim handbrakecli_opt | |
dim use_handbrakecli | |
dim source_postfix_regexp | |
dim log_file | |
dim allowed_hours | |
'/* ---------------------------------------------------- */ | |
' ここから上はユーザーは変更不要です | |
'/* ---------------------------------------------------- */ | |
'/* 必須設定項目 */ | |
'TSSplitterのある場所をフルパス指定 | |
tssplitter = "e:\bin\tssplitter\tssplitter.exe" | |
'TSSplitterに渡すオプションを指定 | |
tssplitter_opt = "-EIT -SD -1SEG -SEP2 -SEPA -WAIT2 -OVL5,7,0" | |
'エンコーダにBonTsDemuxを使うか、HandBrakeCLIを使うか決める | |
'trueならHandBrakeCLI、falseならBonTsDemux | |
use_handbrakecli = true | |
'BonTsDemuxのある場所をフルパス指定 | |
bontsdemux = "e:\bin\bontsdemux\bontsdemux.exe" | |
'BonTsDemuxに渡すオプションを指定 | |
'エンコードのプリセットをクオートしたい場合はダブルクオートを利用 | |
'bontsdemux_opt = "-encode ""X264_mp4"" -vf -delay -100 -nd -start -quit" | |
'エンコードのプリセット名を以下のようにシングルクオートでくくると | |
'プリセットの適用に失敗してデフォルトのdemux動作になってしまうので注意 | |
'bontsdemux_opt = "-encode 'X264_mp4' -vf -delay -100 -nd -start -quit" | |
bontsdemux_opt = "-encode X264_mp4 -vf -nd -start -quit" | |
'HandBrakeCLIのある場所をフルパス指定 | |
handbrakecli = "e:\bin\handbrake_cli\handbrakecli.exe" | |
'HandBrakeCLIに渡すオプションを指定 | |
'こちらからパクらせていただきました http://blog.browncat.org/2011/02/handbrake095mpeg2ts.html | |
'重いオプション | |
'handbrakecli_opt = "-e x264 -b 2000 -r 29.97 --pfr -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4 -4 -X 1280 --crop 0:0:0:0 --loose-anamorphic -m --decomb --main-feature --start-at duration:3 -x cabac=0:ref=2:me=umh:bframes=0:weightp=0:subme=6:8x8dct=0:trellis=0" | |
'@yoheisnet さんからパクったオプション | |
handbrakecli_opt = "-f mp4 -e x264 -b 3000 -r 29.97 --cfr -E faac -B 160 -X 1440 -Y 810 --deinterlace='slow' -x bitrate=3000:frameref=6:level=41:direct_pred=auto:partitions=p8x8:subq=6:chroma_me:mixed_refs:trellis=1:weight_b:nr=0:cabac=0:analyze=all:me=umh:no-fast-pskip=1:mixed-refs=1:deblock=-2:-2:no-dct-decimate=1:cqm=flat:aq-strength=0:psy-rd=0,0:threads=4" | |
'軽いオプション | |
'handbrakecli_opt = "-e ffmpeg -b 2000 -r 29.97 --pfr -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4 -4 -X 1280 --crop 0:0:0:0 --loose-anamorphic -m --decomb --main-feature --start-at duration:3" | |
'@yoheisnet さんからパクったオプション | |
'handbrakecli_opt = "-f mp4 -e x264 -b 600 -r 29.97 --cfr -E faac -B 160 -X 640 -Y 360 --deinterlace='slow' -x bitrate=600:frameref=6:level=41:direct_pred=auto:partitions=p8x8:subq=6:chroma_me:mixed_refs:trellis=1:weight_b:nr=0:cabac=0:analyze=all:me=umh:no-fast-pskip=1:mixed-refs=1:deblock=-2:-2:no-dct-decimate=1:cqm=flat:aq-strength=0:psy-rd=0,0:threads=4" | |
'/* オプション設定項目 */ | |
'TsSplitterの出力するファイルでBonTsDemuxのエンコード対象とするファイルの | |
'末尾を表現する正規表現を設定する | |
'TsSplitterに-SEPオプションを付けて起動すると | |
'aa.ts → "aa_HD-1.ts","aa_HD-2.ts","aa_HD-3.ts" | |
'のように複数のファイルを出力する | |
'このスクリプトはエンコード元のファイル名(ex."番組名.ts")から拡張子を取り(ex."番組名") | |
'ここで指定した正規表現を後ろにを付け足し(ex."番組名_HD.*\.ts")、この正規表現に | |
'マッチするファイルの中から最も*サイズが大きいもの*をエンコードします | |
'通常変更する必要はない | |
source_postfix_regexp = "_HD.*\.ts" | |
'ログファイル名を指定 | |
'ログファイルはスクリプトのあるディレクトリに作られます | |
log_file = "taskenc_m.log" | |
'エンコードを許可する時間帯。禁止する時間帯にスクリプトが起動された場合は | |
'許可された時間帯まで待機する | |
allowed_hours = Array(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23) | |
'/* ---------------------------------------------------- */ | |
' ここから下はユーザーは変更不要です | |
'/* ---------------------------------------------------- */ | |
'/* main */ | |
log_file = container(wscript.scriptfullname) & log_file | |
'CygwinのApacheでログファイルを公開する場合 | |
'log_file = "e:\cygwin\var\www\htdocs\" & log_file | |
p("スクリプト起動") | |
'入力MPEG2 TSを引数から取得 | |
'取得できなければ終了 | |
dim str_source_ts | |
str_source_ts = parse_args() | |
p("処理を開始します") | |
p("入力MPEG2 TS: " & str_source_ts) | |
'別のffmpegがエンコードしてたり、 | |
'エンコード禁止時間だったら待つ | |
adjust_encode_timing() | |
'主処理 | |
main_procedure(str_source_ts) | |
'/* function definitions */ | |
'エンコードの処理を実行します | |
'str_source_ts: 指定したtsファイルのフルパス (ex."d:\tv\番組名.ts") | |
function main_procedure(str_source_ts) | |
dim str_tmp, fso_, obj_file, wssh_, str_encode_file | |
dim i, int_len, int_error_code | |
set fso_ = fso() | |
set wssh_ = wssh() | |
'TsSplitterの実行 | |
str_tmp = """" & tssplitter & """ " & tssplitter_opt & " """ & str_source_ts & """" | |
run(str_tmp) | |
'TsSplitterの出力ファイルから最もサイズが大きいものを探す | |
dim arr_files, str_tmp_file_name, str_out_file_name | |
arr_files = array(0) | |
str_encode_file = "" | |
str_encode_file = largest_file(str_source_ts, source_postfix_regexp, arr_files) | |
if ("" = str_encode_file) then | |
errp("エンコード可能なファイルが見つかりませんでした") | |
'exit function | |
quit() | |
else | |
p("エンコード対象決定: " & str_encode_file) | |
end if | |
p("エンコード対象ファイルのあるディレクトリ: " & container(str_encode_file)) | |
p("""" & str_encode_file & """ をエンコードします") | |
'エンコード対象を英数字のテンポラリファイルにリネーム | |
str_tmp_file_name = container(str_source_ts) & left(fso_.gettempname(), 8) & ".ts" | |
str_tmp = "cmd /c move """ & str_encode_file & """ """ & str_tmp_file_name & """" | |
run(str_tmp) | |
'HandBrakeCLIかBonTsDemuxのどちらか実行 | |
if (use_handbrakecli) then | |
'HandBrakeCLIの実行 | |
str_tmp = """" & handbrakecli & """ " & "-i """ & str_tmp_file_name & """ " & _ | |
handbrakecli_opt & " -o """ & container(str_tmp_file_name) & fso().GetBaseName(str_tmp_file_name) & ".mp4""" | |
else | |
'BonTsDemuxの実行 | |
str_tmp = """" & bontsdemux & """ " & "-i """ & str_tmp_file_name & """ " & bontsdemux_opt | |
end if | |
run(str_tmp) | |
'エンコード対象のTSファイルをテンポラリファイルから元のファイル名にリネーム | |
str_tmp = "cmd /c move """ & str_tmp_file_name & """ """ & str_encode_file &"""" | |
run(str_tmp) | |
'エンコードされたファイルのファイル名は"TMPファイル名.mp4"みたいになっているので | |
'拡張子以外を元のファイル名に戻す | |
dim str_src, str_dest | |
if (not output_file(str_encode_file, str_tmp_file_name, str_src, str_dest)) then | |
errp("BonTsDemuxからの出力ファイルが見つかりません") | |
'exit function | |
quit() | |
end if | |
str_tmp = "cmd /c move """ & str_src & """ """ & str_dest & """" | |
run(str_tmp) | |
p("-----------------------------------------") | |
end function | |
function fso() | |
set fso = createObject("Scripting.FileSystemObject") | |
end function | |
function exists(str_file) | |
exists = fso().fileExists(str_file) | |
end function | |
function wssh() | |
set wssh = WScript.CreateObject("WScript.Shell") | |
end function | |
sub sleep_sec(int_sec) | |
wscript.sleep(int_sec * 1000) | |
end sub | |
sub sleep_min(int_min) | |
wscript.sleep(int_min * 60 * 1000) | |
end sub | |
function cpu_info() | |
set cpu_info =_ | |
getobject("winmgmts:\\.\root\cimv2")._ | |
execquery("Select * From Win32_Processor") | |
end function | |
function cpu_load() | |
dim obj_w32p, i, int_sum | |
i = 0 | |
int_sum = 0 | |
for each obj_w32p in cpu_info() | |
int_sum = int_sum + obj_w32p.LoadPercentage | |
i = i + 1 | |
next | |
cpu_load = int_sum / i | |
end function | |
function procs() | |
set procs =_ | |
getobject("winmgmts:")._ | |
InstancesOf("win32_process") | |
end function | |
function is_running(str_proc) | |
dim str_query | |
str_query = "select * from Win32_Process where Name='" & str_proc & "'" | |
dim obj_wmi, obj_proc | |
set obj_wmi = getobject("winmgmts:") | |
'プロセス情報を取得 | |
set obj_proc = obj_wmi.execquery(str_query) | |
if (obj_proc.count > 0) then | |
is_running = true | |
else | |
is_running = false | |
end if | |
end function | |
'引数に指定したアプリケーション(ex."ffmpeg.exe")をすべて終了 | |
function kill_procs(str_proc) | |
dim obj_p | |
for each obj_p in procs() | |
'p(obj_p.name) | |
'p(typename(obj_p)) | |
if (lcase(obj_p.name) = str_proc) then | |
obj_p.terminate | |
if (err.number <> 0) then | |
errp(Err.description) | |
end if | |
end if | |
next | |
end function | |
sub p(str_str) | |
l(str_str) | |
call log(str_str, log_file) | |
end sub | |
sub errp(str_str) | |
dim msg | |
msg = "ERROR: " & str_str | |
l(msg) | |
call log(msg, log_file) | |
end sub | |
sub l(str_str) | |
'startコマンドからcscriptを起動するなど、 | |
'GUIモードで起動すると、 | |
'以下は「ハンドルが無効です」エラーを起こす | |
'かといってwscript.echo()を使うとメッセージの | |
'表示のためにダイアログが出て処理が中断される | |
'wscript.stdout.writeline(str_str) | |
end sub | |
sub quit() | |
wscript.quit | |
end sub | |
sub m(str_str) | |
msgbox(str_str) | |
end sub | |
function container(str_str) | |
dim fso_, str_tmp | |
set fso_ = fso() | |
str_tmp = fso_.getparentfoldername(str_str) | |
if (0 = len(str_tmp)) then | |
stmp = ".\" | |
elseif ("\" <> right(str_tmp, 1)) then | |
str_tmp = str_tmp & "\" | |
end if | |
container = str_tmp | |
end function | |
'引数のコマンドをシェルで実行 | |
function run(str_cmd) | |
p("コマンド実行:" & str_cmd) | |
call wssh().run(str_cmd, 7, true) | |
end function | |
'引数を解析 | |
function parse_args() | |
dim str_source_file | |
dim obj_named | |
set obj_named =_ | |
wscript.arguments.named | |
dim obj_unnamed | |
set obj_unnamed =_ | |
wscript.arguments.unnamed | |
if (1 > obj_unnamed.count) then | |
errp("エンコードするTSファイル名を指定してください") | |
quit() | |
'exit function | |
else | |
str_source_file = obj_unnamed.item(0) | |
end if | |
'if (obj_named.exists("i")) then | |
' source_file = obj_named.item("i") | |
'end if | |
parse_args = str_source_file | |
end function | |
'引数となるMPEG2 TSのファイル名を返す | |
function source_file() | |
dim str_tmp | |
str_tmp = parse_args() | |
if (str_tmp = false) then | |
errp("処理対象のMPEG2 TSを引数として指定してください") | |
quit() | |
else | |
if (exists(str_tmp)) then | |
source_file = str_tmp | |
else | |
errp("指定されたMPEG2 TSファイルが存在しません") | |
quit() | |
end if | |
end if | |
end function | |
'arr_hours: エンコードを許可する時間帯を持つ配列 | |
'その配列内の数字が現在の時刻と一致しているものがあれば | |
'trueを返す | |
function is_allowed_now(arr_hours) | |
dim int_hour, i, int_len | |
int_hour = hour(now) | |
int_len = ubound(arr_hours) | |
for i = 0 to int_len | |
if (int_hour = arr_hours(i)) then | |
is_allowed_now = true | |
exit function | |
end if | |
next | |
is_allowed_now = false | |
end function | |
'引数の文字列をログに書き込む | |
sub log(str_str, str_log_file) | |
dim obj_file, str_tmp | |
set obj_file = fso().opentextfile(str_log_file, 8, true) | |
if (err.number = 0) then | |
str_tmp = formatdatetime(now, vbshortdate) & " " _ | |
& formatdatetime(now, vblongtime) & " " _ | |
& source_file() & " | " & str_str | |
obj_file.writeline(str_tmp) | |
obj_file.close | |
else | |
l("ERROR: 指定したログファイル " & str_log_file & " が開けませんでした") | |
end if | |
end sub | |
'引数で与えられた分数を最大とする乱数秒待機する | |
function wait_min_tops(int_min) | |
dim i | |
randomize | |
i = int(int_min * 60 * Rnd) | |
sleep_sec(i) | |
end function | |
'エンコードを開始していいかどうかを判断する | |
'返り値はbool | |
function should_start_encode() | |
should_start_encode = false | |
' 起動許可時間帯かどうか | |
if (is_allowed_now(allowed_hours)) then | |
'tssplitterが起動しているかどうか | |
if (is_running("tssplitter.exe")) then | |
p("別のtssplitterが起動中なので待機します") | |
should_start_encode = false | |
exit function | |
end if | |
'ffmpeg / HandBrakeCLIのどちらか起動しているか | |
if (is_running("ffmpeg.exe") or is_running("handbrakecli.exe")) then | |
if (cpu_load() > 30) then | |
'ffmpeg / HandBrakeCLIが起動していて、CPU使用率が高かったら | |
p("別のffmpegもしくはHandBrakeCLIがエンコード中でCPU使用率が高いので待機します") | |
should_start_encode = false | |
else | |
'ffmpeg / HandBrakeCLIが起動しているのにCPU使用率が低かったら | |
'ffmpeg / HandBrakeCLIがスタックしてる? | |
'ffmpeg / HandBrakeCLIがスタックしてることあるなら、先にヌッ殺しておく | |
if (false) then | |
p("別のffmpeg / HandBrakeCLIがストールしているようなのでヌッ殺してからエンコードを開始します") | |
kill_procs("handbrakecli.exe") | |
kill_procs("ffmpeg.exe") | |
kill_procs("bontsdemux.exe") | |
else | |
p("別のffmpeg / HandBrakeCLIが起動中ですが、CPU使用率が低いのでエンコードを開始します") | |
end if | |
should_start_encode = true | |
end if | |
else | |
'ffmpeg / HandBrakeCLIが起動していなかったら | |
p("他にffmpeg / HandBrakeCLIは起動していないようなのでエンコードを開始します") | |
should_start_encode = true | |
end if | |
else | |
'起動許可時間帯じゃなければ | |
p("現在はエンコード禁止時間帯です") | |
should_start_encode = false | |
end if | |
end function | |
'冒頭で乱数秒待ち、さらにエンコードしていいかどうか | |
'判断して、エンコードを見合わせた方がよければ1分待機 | |
'エンコード可能と判断できるまで | |
'「エンコード可否判断→1分待機」を繰り返す | |
function adjust_encode_timing() | |
p("エンコード開始まで1分を最大にランダム秒待機します") | |
wait_min_tops(1) | |
do | |
if (should_start_encode()) then | |
exit do | |
else | |
sleep_min(1) | |
end if | |
loop while (true) | |
end function | |
'文字列から正規表現のメタキャラクタをエスケープする | |
function escape_metachar(str_str) | |
dim obj_re, str_tmp | |
set obj_re = new regexp | |
obj_re.global = true | |
str_tmp = str_str | |
obj_re.pattern = "\\" | |
str_tmp = obj_re.replace(str_tmp,"\\") | |
obj_re.pattern = "\." | |
str_tmp = obj_re.replace(str_tmp,"\.") | |
obj_re.pattern = "\^" | |
str_tmp = obj_re.replace(str_tmp,"\^") | |
obj_re.pattern = "\+" | |
str_tmp = obj_re.replace(str_tmp,"\+") | |
obj_re.pattern = "\$" | |
str_tmp = obj_re.replace(str_tmp,"\$") | |
obj_re.pattern = "\{" | |
str_tmp = obj_re.replace(str_tmp,"\{") | |
obj_re.pattern = "\}" | |
str_tmp = obj_re.replace(str_tmp,"\}") | |
obj_re.pattern = "\(" | |
str_tmp = obj_re.replace(str_tmp,"\(") | |
obj_re.pattern = "\)" | |
str_tmp = obj_re.replace(str_tmp,"\)") | |
obj_re.pattern = "\[" | |
str_tmp = obj_re.replace(str_tmp,"\[") | |
obj_re.pattern = "\]" | |
str_tmp = obj_re.replace(str_tmp,"\]") | |
escape_metachar = str_tmp | |
end function | |
'str_target: 指定したTSファイル(ex."d:\tv\aaa.ts") | |
'str_target_ext: TsSplitterが出力するファイル名の末尾の正規表現(ex."_HD.*\.ts" | |
'arr_files: str_targetの".ts"の部分をstr_target_extに変更して作成した正規表現に | |
' マッチするファイル郡を返す。 | |
' (ex."d:\tv\aaa_HD.*\.ts"にマッチするので | |
' "d:\tv\aaa_HD-1.ts","d:\tv\aaa_HD-2.ts"等のファイル名が入る | |
'戻り値-arr_filesのなかで最もファイルサイズが大きいファイルのファイル名を返す。 | |
function largest_file(str_target, str_target_ext, byref arr_files) | |
dim fso_, obj_re, obj_dir, obj_files, file | |
dim str_path, str_pattern, int_max_size, str_max_file,i | |
largest_file = "" | |
i = -1 | |
int_max_size = 0 | |
str_max_file = "" | |
set fso_ = fso() | |
set obj_re = new regexp | |
obj_re.ignorecase = true | |
' l("str_target=" & str_target) | |
' l("str_target_ext=" & str_target_ext) | |
'ファイル名のみを取り出す | |
str_pattern = fso_.getfilename(str_target) | |
' l("Before_str_pattern=" & str_pattern) | |
'ファイル名からメタキャラクタをエスケープする | |
str_pattern = escape_metachar(str_pattern) | |
' l("after_str_pattern=" & str_pattern) | |
'ファイルの末尾を、ユーザが指定した正規表現に変える | |
obj_re.pattern = "\\.ts$" | |
obj_re.pattern = obj_re.replace(str_pattern, str_target_ext) | |
p("エンコード対象リスト正規表現: " & obj_re.pattern) | |
str_path = container(str_target) | |
if ("" = str_path) then | |
errp("エンコードする対象のファイルはフルパスで指定してください") | |
errp(" 例)×→""aa.ts"" OK!→""c:\tv\aa.ts"" ") | |
quit() | |
'exit function | |
end if | |
set obj_dir = fso_.getfolder(str_path) | |
set obj_files = obj_dir.files | |
for each file in obj_files | |
if (obj_re.test(file.name)) then | |
p("エンコード候補発見: " & file.name & ",size = " & file.size & "[byte]") | |
i= i+1 | |
redim preserve arr_files(i) | |
arr_files(i) = str_path & file.name | |
if (int_max_size < file.size) then | |
int_max_size = file.size | |
str_max_file = arr_files(i) | |
end if | |
end if | |
Next | |
largest_file = str_max_file | |
end function | |
'エンコード後のファイル名を推定し | |
'TMPファイルと同じ名前になっているエンコード後のファイル名と | |
'元のファイル名を返す | |
function output_file(str_org_name, str_tmp_name, str_move_src, str_move_dest) | |
dim fso_, obj_re | |
dim str_filename, str_ext, str_basename, str_pattern | |
dim str_path, str_org_basename, str_tmp_basename | |
set fso_ = fso() | |
set obj_re = new regexp | |
str_path = container(str_tmp_name) | |
str_org_basename = fso_.getbasename(str_org_name) | |
str_tmp_basename = fso_.getbasename(str_tmp_name) | |
obj_re.pattern = escape_metachar(str_tmp_basename) & "\." & ".+$" | |
' l("patteern=" & obj_re.pattern) | |
'ファイル名が同じで拡張子が違うファイルを探す | |
dim obj_dir, obj_files, file | |
set obj_dir = fso_.getfolder(str_path) | |
set obj_files = obj_dir.files | |
for each file in obj_files | |
' l("findname=" & file.name) | |
if (obj_re.test(file.name)) then | |
' l("match=" & file.name) | |
Dim str_tmp | |
str_move_src = str_path & file.name | |
str_move_dest = str_path & str_org_basename & "." & fso_.getextensionname(file.name) | |
output_file = true | |
exit function | |
end if | |
next | |
output_file = false | |
end function |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment