Ruby に手を出したの巻

スポンサーリンク

 インタプリタな処理体系としては Perl をよく使っていたけど、ここ最近は Ruby に興味が湧いてきた。
 Windows 上でのバッチファイルを作成するとき、時間計算を入れたいと思っても正直面倒臭すぎて「開始と終了の時刻」を表示させるだけで終わらせてみたりって始末。
 もっと本格的に何かをやろうと VBS なんかも考えてみたけど Windows でしか役に立たなそう。それじゃあ新たに Ruby にも手を出してみようってなった。Windows 上でも上手いこと宜しくやってくれるバイナリパッケージがあるのも幸いした。


 使用した Ruby は RubyInstaller の 2.2.1 とした。これはインストールしたら Ruby のバイナリまでパスを通してくれたので、コマンドプロンプトからも “ruby -v” と打ち込むだけでも動作を確認できた。拡張子 “.rb” に関しても関連づけされているから “.\hogehoge.rb” とするだけで実行する事も出来た。
 特別なことをせずとも望む形で使えるようになった。

>ruby -v
ruby 2.2.1p85 (2015-02-26 revision 49769) [i386-mingw32]

 さて、ここからは実際にアレコレと試行錯誤しながら買うだけ買って置いた書籍を手に取りつつ「何か」を書いていく事とした。今やりたいことは Windows 上に於けるバッチファイルのような事。変数やパスを定義しつつ定形化した作業を行いたい。
 具体的には x265.exe を用いた HEVC エンコを行うバッチファイルがあるんだけども、これを Ruby にやらせる。先ずはファイルのパスや引数として与える文字列の定義、外部の実行プログラムをスクリプト内から実行する事にある。折角だからエンコードにかかった時間も「~分~秒」かかったって情報も欲しい所。
 いきなりそこまで全てやりきる時間も無かったから取り敢えずやって見たことがこれ。

#! ruby -Ks
# -*- mode:ruby; coding:shift_jis -*-

require "open3"

x265_path = "/path/to/x265.exe"
x265_arg = "--version"

# 外部ファイルの実行
o, e, s = Open3.capture3(x265_path, x265_arg)

# x265 のバージョン情報は stderr に出力される。
e =~ /(\d+)\ bit.*\ (\d+)bpp/
bit = Regexp.last_match[1].to_i
bpp = Regexp.last_match[2].to_i

printf("%dbit, %dbpp build\n", bit, bpp)

 x265.exe のビルド情報の一部を抜き出して表示させるだけの物。
 変数の定義、外部ファイルの実行、stdin/stderr の参照、正規表現、テキスト整形なんていうやろうとしている事の基本を試した程度の物である。
 たったこれだけでも得られる物は多く、また悩みも出たりした。悩み的な物としては正規表現にマッチした物を “$1” やらで参照するか Regexp.last_match[1] を用いるかの違い。得られる結果は同じだけど、コードの可読性や趣味嗜好の問題となる。
 得られた物としては「手短で便利な事が出来る」と分かった事。上記ソースの “Regexp.last_match[1].to_i” なんて所がそう。やっていることを日本語にすると「正規表現で 1 番目にマッチした物を整数に変換する」となる。このメソッドなる物はまだ「なんですかそれ」状態だから、良く理解しながら勉強を進めないと行けないな…… ちなみに Perl の方ではメソッドとか使ったことが無い。

 基本的に何かをやりたいと言う目的があると言語って覚えやすいから、これを機に少しでも何か出来るようになれば良いなと思う。

2015/04/14 18:03 追記
 やろうとしていたことが出来たのでペタっとこれまた貼ってみる。
 Ruby の勉強にはなったかなーとは思うから今後もなにか Ruby で書くようにしてみよう。
 なお、このスクリプトの動作に必要な実行ファイルは exe ファイルを探せば見つかる。あとは avs ファイルを自分で書いてからこのスクリプトを動かすとエンコードから Mux までやってくれる。気になる方はコメントなりでご連絡頂ければと。

#! ruby -Ks
# -*- mode:ruby; coding:shift_jis -*-
#
# x265 frontend v0.1
# usage : hogehoge.rb [CRF] [PRESET]
#         CRF : default 20
#         PRESET : default medium
#

require "open3"

# User Settings ===============================================================
## ツール群の存在するパスを指定。
bin_path = "D:/TS/Utils"
## 作業及び成果物を出力するパスを指定。
temp_path = "D:/TS/temporary"

## Avisynth のスクリプトファイルを指定。
avsFile = "#{temp_path}/demux.avs"
## チャプターファイルの定義。チャプター不要ならファイルを作成しないでおく。
chapterFile = "#{temp_path}/!chap.txt"

# 各実行ファイルへのパスを定義
## HEVC エンコードを行うプログラム。64bit 版を推奨。
x265_path = "#{bin_path}/x265.exe"
## AVS ファイルをパイプ経由で x265 に渡すツール。
avs4x26x = "#{bin_path}/avs4x26x.exe"
## avs ファイルを元にトリミングした wave ファイルを出力するツール。
avs2wav = "#{bin_path}/avs2wav.exe"
## AAC ファイルを疑似 wave に相互変換するツール
fawcl = "#{bin_path}/fawcl.exe"
## 動画/音声を MP4 のコンテナに突っ込むツール。
muxer = "#{bin_path}/muxer.exe"
## 動画と音声やチャプターを一つの MP4 に結合するツール。
remuxer = "#{bin_path}/remuxer.exe"

# デフォルトのオプションを変更する場合……
#  CRF は x265_defaultCrf の行末尾の数字を変更する。
#  preset は x265_preset の行末尾 medium を変更する。
#  
# default 20                               __
x265_defaultCrf = ARGV[0] ? ARGV[0].to_i : 20
# default medium                        ______
x265_preset = ARGV[1] ? ARGV[1].to_s : "medium"
#          ____ nil にするとエンコードログ出力をしない。
x265_log = true
# crf と preset 以外を記述。
x265_extOpt = "--tune ssim --ssim --aq-mode 2 --aq-strength 0.6"
# ログ出力のオプション
x265_logOpt = x265_log ? "--csv #{temp_path}/x265_encode_log.csv" : ""

# 最終的なファイル名の定義。簡単な形式を予め書いておくとリネームし易くなるかも。
# "\" ~ \"" 内に記述する。ファイル名をユニークな物とする為、時刻を数字で入れ込んでいる。
# !!数値~~ とする事で、ファイル名をソートした場合に先頭へ行きやすいようにしている。
#            ____________________________________________________________________________________
mp4File = "\"!!#{Time.now.strftime("%Y%m%d%H%M%S")} crf#{x265_defaultCrf} x265 AAC 720p 10bit.mp4\""

# 動画のフレームレートを指定 (24000/1001 = 23.976fps, 30000/1001 = 29.97fps)
fps="24000/1001"

# ==============================================================================
# x265.exe の引数定義。(変更不要)
x265_arg = "--crf #{x265_defaultCrf} --preset #{x265_preset} #{x265_extOpt} #{x265_logOpt}"
# avs4x26x に引き渡すエンコードプログラムを指定する。(変更不要)
avs4x26x_opt = "--x26x-binary #{x265_path}"
# チャプターファイルが存在しなければチャプターの付加を行わない。
remuxer_chapter = File.exist?(chapterFile) ?  "--chapter #{chapterFile}" : ""

# ==============================================================================
# 呼び出したプログラムにエラーがあったかのチェックを行う関数
def errCheck(state)
	if state.to_s !~ /(exit 0)/ then
		puts "Error detected."
		puts "Press ENTER KEY to exit. ******************************************"
		gets
		exit(1)
	end
end
# ==============================================================================

start_time = Time.now.to_f
# Thread of Video Part
encodeThread = Thread.new do
	Thread.pass
	puts "HEVC Encode Start. **********************************************"
	Open3.popen3("start #{avs4x26x} #{avs4x26x_opt} #{x265_arg} -o #{temp_path}/encoded.265 #{avsFile}") do |i, o, e, w|
		e.each do |line|
			puts line
		end
	errCheck(w.value)
	puts "HEVC Encode - Success.."
	end
end

# Thread of Audio Part.
audioThread = Thread.new do
	Thread.pass
	puts "Audio Trimming start ********************************************"
	o, e, s = Open3.capture3("#{avs2wav} #{avsFile} #{temp_path}/tmp_audio_trimmed.wav")
	errCheck(s.to_s)
	puts "done."
	
	puts "Convert from wav to aac. ****************************************"
	o, e, s = Open3.capture3("#{fawcl} #{temp_path}/tmp_audio_trimmed.wav #{temp_path}/tmp_audio_final.aac")
	errCheck(s.to_s)
	puts "done."
	
	puts "Audio Mux Start. ************************************************"
	o, e, s = Open3.capture3("#{muxer} -i #{temp_path}/tmp_audio_final.aac?fps=#{fps} -o #{temp_path}/265.m4a")
	errCheck(s.to_s)
	puts "done."
end

# HEVC エンコードの終了を待つ
encodeThread.join
audioThread.join

puts "Video Mux Start. ************************************************"
Open3.popen3("start #{muxer} -i #{temp_path}/encoded.265?fps=#{fps} -o #{temp_path}/265.mp4") do |i, o, e, w|
	e.each do |line|
		print line
	end
	errCheck(w.value)
	puts "Video Mux - Success.."
end

puts "Remux Start. ****************************************************"
Open3.popen3("start #{remuxer} #{remuxer_chapter} -i #{temp_path}/265.mp4 -i #{temp_path}/265.m4a -o #{temp_path}/#{mp4File}") do |i, o, e, w|
	e.each do |line|
		print line.match(/^[^ ].*\n/)
	end
	errCheck(w.value)
	puts "Remux to MP4 - Success."
end

# 処理時間の算出と表示
t = Time.now.to_i - start_time
h = t / 3600
m = t / 60
s = t % 60

printf("Total Elapsed time : %02d:%02d:%02d\n", h, m, s)

puts "Press ENTER KEY to exit. ******************************************"
gets

スポンサーリンク