wxRubyのGUIからffmpegを呼び出してmp4の動画からmp3を抽出変換するサンプル。
わからないなりに頑張って書いたのでまずい所があれば教えて下さい。
困ったこと
1. まず単純にffmpegを呼び出すと処理が終わるまでGUIが固まる
→解決策:Threadを作る。
別スレッドからはメインスレッドのGUIに手を出せないのでステータスバーに進捗が表示できない。
→解決策:Queueをつかって受け渡しする。
(説明してるサイトには何故か別のThreadから直接GUIの部品を変更してたので、古いバージョンではそこらへん管理がされてないのかもしれない。)wxRubyはGUIがメインスレッドを強烈に縛って他のスレッドに処理をよこさないらしい。
→解決策:Timerで定期的にsleepして強制的にCPUを明け渡す。
require 'wx'
require 'thread'
class MyApp < Wx::App
def on_init
MyFrame.new.show
end
end
class MyFrame < Wx::Frame
def initialize
super(nil, -1, "Thread Test")
@status_bar = create_status_bar
@q = Queue.new
toolbar = Wx::ToolBar.new(self)
toolbar.add_tool(Wx::ID_NEW, 'NEW', Wx::ArtProvider.bitmap(Wx::ART_NEW), 'Toolbar')
toolbar.realize
evt_tool(Wx::ID_NEW){|event| on_button_click(event)}
self.set_tool_bar(toolbar)
Wx::Timer.every(100) do
if not @q.empty?
@status_bar.set_status_text(@q.pop)
end
sleep 0.05
end
end
def execute_ffmpeg(command)
duration = 0
IO.popen(command){|pipe|
pipe.each{|line|
if line =~ /Duration: (\d{2}):(\d{2}):(\d{2}).(\d+)/
duration = (($1.to_i * 60 + $2.to_i) * 60 + $3.to_i) * 100 + $4.to_i
end
if line =~ /time=(\d{2}):(\d{2}):(\d{2}).(\d+)/
time = ($1.to_i * 60*60 + $2.to_i * 60 + $3.to_i) * 100 + $4.to_i
if duration != 0
@q.push "Encoding : ...#{time*100/duration}%"
end
end
}
}
raise "Could not encode by ffmpeg!" if $?.exitstatus != 0
end
def on_button_click(event)
command_ffmpeg = "ffmpeg -y -i input.mp4 -vn -ab 96k output.mp3 2>&1"
t = Thread.new do
execute_ffmpeg(command_ffmpeg)
end
end
end
MyApp.new.main_loop
あとffmpegは何故か出力結果を標準出力ではなく標準エラー出力に吐き出すので 2>&1 で標準出力に吐き戻させてる点に注意。
参考
wxRubyでThread使うときの注意事項 - 狼ニコ生うらやまけしからん日記 http://d.hatena.ne.jp/kesikaran/20100410/1270919896
Ruby for Scientific Research: Keeping wxRuby GUI Working with Threads http://rubyforscientificresearch.blogspot.jp/2009/11/keeping-wxruby-gui-working-with-threads.html