INASOFT 管理人のひとこと


フリーソフトダウンロードサイト「INASOFT」の管理人 矢吹拓也 が日々の「ひとこと」を語るページです。
2021年1月1日より、旧ブログ(blog.inasoft.org)からお引越ししました。
・INASOFT Webサイト: https://www.inasoft.org/
・管理人のふたこと(長文記事/寄稿文): https://www.inasoft.org/talk/
2022年7月下旬より再び本業多忙化してきているため、更新頻度は落ちます。 [2022/7/24 19:32]

目次 | ←前へ / 2017-12-12 00:00 / 次へ→ / 最新へ⇒

■ffmpegって名前付きパイプだとうまくいかないのかなぁ…と思いかけた落とし穴にハマる

2017年12月12日(火) 0:00:00 [さくらのブログから転記]



RSSRSS配信中
https://www.inasoft.org/



(1)ネット経由でストリーミング受信した音声ファイル(rtmpdumpでflvを生成)を、(2)ffmpegでm4aに変換し、(3)さらに1.45倍速にしたものもm4aにする自動処理を行っていました。これは、Raspberry PiのLinux上で実施しています。

(Raspberry Piでffmpegを使うには、通常以上の手間が必要になるのですが、それについてはまた今度)

今までは、(1)(2)(3)の処理を逐次行っていたため、処理時間は非常に長かったのですが、

(1)で必要とするCPU利用率は0.2%程度。必要な時間は2時間30分。

(2)で必要とするCPU利用率はほぼ100%。必要な時間は13分。

(3)で必要とするCPU利用率はほぼ100%。必要な時間は13分。

という感じでした。

もし、(1)と並行して、ゆっくりと(2)(3)を行うことができるなら、うしろの26分間をほぼゼロに近づけることができるんじゃないか?と思いまして。

試しに、(1)と(2)を並行で実施することにしてみました。

rtmpdumpは、出力ファイル名を指定しなければ(-o ファイル名 を付けない)、ファイルの内容は標準出力に出力されます。

ffmpeg は -i pipe 0 というオプションを付けると、標準入力から入力を受け付けます。

指定するコマンドはこんな感じ。

$ rtmpdump (様々なオプション) | ffmpeg -i pipe 0 -ab 64k 出力ファイル名.m4a

参考サイト:rtmpdumpとffmpegをつかったライブトランスコーディング - 別館 子子子子子子(ねこのここねこ)

さて、(1)(2)に加え、(3)も並行で実行するにはどうしたらよいか?

パイプを2分岐させれば良いことになります。

rtmpdump→標準出力→標準入力→tee→→→ ファイル 
    └→→標準出力→標準入力→ffmpeg

パイプの2分岐は、teeコマンドを用います。

teeコマンドは、標準入力を、「ファイル」と「標準出力」に2分岐させるためのコマンドです。

このうち「標準出力」は、別のコマンド(今回の場合はffmpeg)の入力にすることができますが、「ファイル」を別のコマンドへの入力にするにはどうしたらよいか?

色々調べたところ、名前付きパイプを使えば良いことが分かりました。

名前付きパイプは、ファイル名を媒介にして、2つのプロセス同士の標準出力と標準入力を繋ぐための方法です。

名前付きパイプはmkfifoコマンドで、あたかもファイルのように作成できます。

以後、そのファイル名へ書き込みをすれば、別のプロセスへの標準入力になるというわけです。

(なお、1つの標準出力を2つのプロセスの標準入力に割り当てることはできません。手元の環境で実験したところ、後から名前付きパイプをオープンしたプロセスに対してのみ、入力が行われました)

mkfifo temp_pipe

ffmpeg -i pipe:0 -ab 64k -af atempo=1.45 出力ファイル名_x145.m4a < temp_pipe &

rtmpdump (様々なオプション) | tee temp_pipe | ffmpeg -i pipe 0 -ab 64k 出力ファイル名.m4a

rm temp_pipe



2行目の最後に & を付けないと、パイプからの入力待ちでブロックされてしまいます。以後のコマンドと並列処理をさせるために & を付けています。

で、これで実行してみると、なんだか上手くいきません。

音楽再生プレイヤーで開くと、「出力ファイル名_x145.m4a」の内容が不正という扱いになります。

色々見直してみましたが、何がおかしいのかよく分からない…。

うーん、名前付きパイプからの入力を、ffmpeg が受け付けられないとか?そんなことはないよなぁ…。

ここで悩むこと5時間。

 :

 :

 :

 :


何かヒントが得られないかと、「出力ファイル名_x145.m4a」をバイナリエディタで開いてみました。

すると、「出力ファイル名.m4a」の方には付いているファイル末尾の書式が付いていないっぽいことがわかりました。

もしかして、名前付きパイプの方で処理しているffmpegでは、最後までファイルが出力できていないのでは?

最後まで処理させてもらえていないのでは?

処理が打ち切られているのでは!

そうなんです。

rtmpdumpによるストリーミング処理の終了後、もうちょっと処理時間に余裕がないと、ffmpegの処理が終了できないようなのです。

名前付きパイプ側のffmpegの処理終了を待つような仕掛けが必要です。

調べてみると、そんなときに活躍するコマンドがすでに準備されていました。

waitコマンドです。

waitコマンドは、引数で指定したプロセスが終了するのを待つコマンドですが、引数を指定しなければ、ここまでに起動した並列処理のプロセス終了をまとめて待ってくれます。

というわけで、正しくは次のようなシェルスクリプトになります。

mkfifo temp_pipe

ffmpeg -i pipe:0 -ab 64k -af atempo=1.45 出力ファイル名_x145.m4a < temp_pipe &

rtmpdump (様々なオプション) | tee temp_pipe | ffmpeg -i pipe 0 -ab 64k 出力ファイル名.m4a

wait

rm temp_pipe




C++で普通にプログラミングをしているときなら、マルチスレッドの同期処理について考えを及ばせることができるのですが、シェルスクリプトのマルチプロセス処理については、あまりにも楽観的に考えすぎていました。

手軽に作れるシェルスクリプトだろうと、きちんとマルチプロセスの同期処理についても、配慮しなきゃいけませんね。

ここまで来て、ふと思ったこととしては、名前なしパイプ側のffmpeg処理についても、後処理が動いているはずなので、もしかしたらたまたま、名前付きパイプ側の処理時間と同じか多くなって、たまたまうまく動いちゃった可能性もあるんですよね。そうなると、次回以降のどこかのタイミングで問題が勃発することになって、泣いていたかも知れません。

マルチスレッド/マルチプロセスが原因となるバグは、こういう風に起きたり起きなかったりが気まぐれで発生するので、やっかいなんですよね…。



目次 | ←前へ / 2017-12-12 00:00 / 次へ→ / 最新へ⇒


目次の表示:


ブログではないので、コメント機能とトラックバック機能は提供していません。ご質問・ご意見等はメールフィードバックまたはTwitter等からお願いします。いただいたご質問・ご意見などは、この「管理人のひとこと」の記事に追加、あるいは新規の記事にする形で一部または全文をそのまま、あるいは加工させていただいた上で、ご紹介させていただく場合があります。
当サイトでは掲載内容による不具合等に関する責任を持ちません。また、内容の正確性についての保証もありませんので、情報をご利用の際は、利用者の自己責任で確認をお願いします。本ページは公開から1年半後の任意のタイミングで削除される予定です。


- 最近の更新 -



3129917 (+0379)[+0470]

Copyright© 2010-2024 INASOFT