Chienomi

ffmpegを使うスクリプトでハマった話

Live With Linux::tips

ripcd evo はダメになってしまったripcdの進化版であり、CDのリッピングを行うスクリプトだが、それぞれ分散して置かれていて行方不明になったWalkman向けのスクリプトを集約したものでもある。

converters にWalkman用のファイルが配置され、Walkmanに合わせたファイルツリーを生成するようになった。

これで特に複雑化している要素はFATファイルシステムに合わせたファイル名のリネームが必要であることである。

当初ほとんどをZshで行っていたが、非常に時間がかかったことと、Zshではファイル名の補正がしきれていなかったことから、パイプを使って補正をRubyで行うように変更した。

この際にffmpegで思わぬ問題に遭遇し、ハマってしまったので記録する。

問題

入力ファイルの文字数が減る

ffmpegの入力ファイルパスが先頭から何文字か(むしろ結構な文字数)削られてしまい、ソースファイルがみつからないとしてエラーになる。

対象となるファイル名を1ファイル1行で受け取っているのだが、各行をinspectしても問題が全く見つからず、随分と苦労した。

結論としては、既にファイルが存在する場合に [y/N] で聞かれているところに標準入力を消費されてしまい、ファイルパスが先頭から削られている、というものだった。 対象ファイルは存在しないことを保証する設計だったのだが、生成するファイルのパスに誤りがあり、思わぬ形で既存ファイルに書く形になっていたのも原因のひとつだった。

Parse error, at least 3 arguments were expected, only 1 given in string

ところが、この問題を解消しても生成を開始するなり、いきなり表題のようなエラーが割り込む、という問題が発生し、うまく生成されないファイルがあった。

これもinspectしても問題が見つからず、ffmpegでだけ問題が発生した。

結論としては、そもそもffmpegが標準入力を消費するようになっているということで、-nostdinオプションを使えば良いということらしい。 ここにたどり着くまでが随分かかった。

コードに苦労の跡が忍ばれる。

done | ruby -e "STDIN.each { |elm| system('ffmpeg', '-nostdin', '-i', elm.chomp, '-c:a', 'libfdk_aac', '-b:a', ENV['AAC_BITRATE'], '-cutoff', '18000', %Q:#{ENV['DEST_DIR']}/: + (elm.sub(%r:/[^/]+$:) { $&.tr('!?\"\\<>*|:', '_').sub(/.[a-zA-Z0-9]+$/, '.m4a').chomp })) }"
    #"entries = STDIN.each.to_a; STDIN.close; entries.each { |elm| p(['ffmpeg', '-i', elm.chomp, '-c:a', 'libfdk_aac', '-b:a', ENV['AAC_BITRATE'], '-cutoff', '18000', %Q:#{ENV['DEST_DIR']}/: + (elm.sub(%r:/[^/]+$:) { $&.tr('!?\"\\<>*|:', '_').sub(/.[a-zA-Z0-9]+$/, '.m4a').chomp })]) }"
    #"STDIN.each { |elm| system('ffmpeg', '-i', elm.chomp, '-c:a', 'libfdk_aac', '-b:a', ENV['AAC_BITRATE'], '-cutoff', '18000', %Q:#{ENV['DEST_DIR']}/: + (elm.sub(%r:/[^/]+$:) { $&.tr('!?\"\\<>*|:', '_').sub(/.[a-zA-Z0-9]+$/, '.m4a').chomp })) }"
    #"STDIN.each { |elm| p(['ffmpeg', '-i', elm.chomp, '-c:a', 'libfdk_aac', '-b:a', ENV['AAC_BITRATE'], '-cutoff', '18000', (elm.sub(%r:/[^/]+$:) { $&.tr('!?\"\\<>*|:', '_').sub(/.[a-zA-Z0-9]+$/, '.m4a').chomp })]) }"
    #"STDIN.each { |elm| system('ffmpeg', '-i', elm.chomp, '-c:a', 'libfdk_aac', '-b:a', ENV['AAC_BITRATE'], '-cutoff', '18000', (elm.sub(%r:/[^/]+$:) { $&.tr('!?\"\\<>*|:', '_').sub(/.[a-zA-Z0-9]+$/, '.m4a').chomp })) }"

その後

Zshでは難しい要素を全部飲みこんでしまうため、ひとつの関数が丸々Rubyという形に変わった。

これでも完全ではないため、Rubyバージョンを開発中だ。

なお、GitHubだとなぜか100% Shellと判定される。