Skip to content

Instantly share code, notes, and snippets.

@moyashi
Created May 4, 2011 20:50
Show Gist options
  • Save moyashi/956007 to your computer and use it in GitHub Desktop.
Save moyashi/956007 to your computer and use it in GitHub Desktop.
TSSplitter + (BonTsDemuxもしくはHandBrakeCLI)という組み合わせでiPhone向けエンコードをするためのVBScript
'/* 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