BR2_PACKAGE_BUSYBOX_CONFIG="package/busybox/lightMPD-busybox-config"
BR2_PACKAGE_BUSYBOX_CONFIG="packages/busybox/lightMPD-busybox-config" に修正
takobozu
lightMPDのrootイメージの作成方法の公開ありがとうございました。BBBベースで書かれているのも助かります。
以前、「lightMPDは何もいじれないから苦手だ」と思っていたのですが、とんでもない勘違いでした。これだけ公開度の高いディストリビューションはありませんね。
とりあえず公開している Boticized lightmpd のrootfs部分をこれに入れ換えるつもりです。
再度アップロード、お手数をお掛け致しました。
こちらの環境でも、romfsのビルドできることを確認いたしました。
mpd-0.20rt-dsdを作って、入れ替えたり、romfs丸ごと手順に従って
作成したものと入れ替えたりして遊んでおりますが、しっかり起動いたします。
ありがとうございました。
takobozu
以前からlightMPDに使われているbuildrootに興味があったところ、今回digififanさんのイメージの作成方法が公開されたので色々試してみました。なお私は、たかじんさんのB4-dacを使っています。
手順どおりに作業して一度環境が整えば、lightMPDに足したり引いたり、簡単にカスタマイズできますね。ちなみにstable版buildrootで試してから、その設定をgit版にも持って行って試しました。
たとえばmpdパッケージについては、package/mpdやpackage/mpd-native-dsdの中のmpd.mkの設定を書きかえるなりして自分用にコンフィグ・オプションを選んでmakeもできるわけですね。私はmpd-0.20.3に20用のrtパッチを当てて、自分の必要なオプションだけでmakeしてみました。
makeの後に output/target/usr/bin/mpd に生成されるのをSDカードに持って行くわけです。(output/build/mpd-0.20.3/src/mpd にも実行ファイルがあるんですが、なぜかファイルサイズが違ったりします。まあ output/target/usr/bin/mpd の方を使うので良いのでしょうか。)
ここからの話は趣味的になりますが、uInitrdの減量も試して5.8M程にしました。自分が使わないものひたすら除いて、たとえば ffmpeg を外したり、Gaucheが生成したsetup.shを直接使うことにしてGaucheも外したり、あと/usr/share/sounds/alsaにテスト用のwavがあったりしたのでそれも一応除きました。
なお、kernelについてもbuildrootで生成しましたが、私には下手に設定をいじれないのであまりサイズは小さくできませんでした(usb関係を外したくらい)。
ついでにrpi1B+を引っぱり出して、音楽再生とは違う目的で make raspberrypi_defconfig から作ってみようと思ったんですが、こっちは今のところブートすらできず…お手本があることの有難さが身にしみます。
mpd-native-dsd.mkのお話が出てましたので質問させてください。
CFLAGS=やCXXFLAGS=を明示されてますが、buildrootはここでこの指定をしてやらないと
いけないのでしょうか?それとも、この、mpd-native-dsd.mkに限ったことで、
通常のmpdディレクトリにあるmpd.mkに関してはこのような設定はする必要はないんでしょうか?
あと、ビルドされたmpdについてですが、私はtartgetディレクトリにある/usr/bin/mpdを
使っています。これはファイルサイズが小さく(stripで小さくするみたいのことを聞いた
ことがあります。)、これが本来romfsに含まれるものだと思っていましたので。
実際、この小さい方のファイルでmpdの着せ替えをしておりますが、ちゃんと起動して
おります。なのであまり疑問に思ったことがございませんでした。
takobozu
詳細なコメントをいただき助かります。
>setup.shを直接使うという事は、ipアドレス、nasの設定は固定になると思いますが、それならlightmpd-root はインストールしないで直接/etc/network/interfacesや/etc/fstabを編集してしまったほうがいいと思います。
たしかにそうするのが素直かと思いました。見直してみると、標準のbuildrootからの変更点も私の場合多くないので、overlay機能で付け加えるのでも良さそうに感じてきました。このへん検討してみます。
>lightMPDを発表するまでは私もこのようにして使っていました。しかし、5.8Mというのはすごいですね。可能ならdot.configをみせてもらえないでしょうか?
後ほどメールでお送りします。BBG+B4dacでの再生のため自分用に設定してるので汎用性はないですけれども…
>提案ですが、設定が固定でいい場合はboot deviceをマウントする必要がないのでkernelからusbのmmcやfat関連のドライバーも削除できます。
ブート後に必要なければ、kernelやuInitrdにはディスク関連の機能はなくて良いのですね。現状ではnfsだけ使ってるので、他を外す形で試そうかと思います。
ただ、SDのマウントができないとなると、アップデートにはSDを抜くなりして別のシステムでの編集が必要で、ちょっと手間です(今はSDをマウントして、nfsからuInitrd等をコピーしてます。)
その点ではnfsブートできるなら楽かな、と思うのですがuEnvあたりの設定で可能だったりするんでしょうか?
>upnpgwのクライアントとして使う場合は、これに加えて、nfs,cifs,notify-inodeなど相当のドライバーをkernelから削除できます。rootイメージに合わせてkernelのスリム化にも挑戦して見て下さい。
BBGをクライアントにして完全に再生専用にするのはおもしろそうです。
今のところupnpの環境がないので試せないのですが、2つのpolipo同士を接続するなどは興味があります(apuも持ってなんですが…少し安めのapu2c0というのもあるんですね)。
あるいは、raspiなどにmpdを入れてoutputをhttpdして、BBGのmpdで受け、さらにpolipoを挟むとupnpっぽくなるかな?とも考えているんですが、まだ実行してません。
>> ついでにrpi1B+を引っぱり出して、音楽再生とは違う目的で make raspberrypi_defconfig から作ってみようと思ったんですが、こっちは今のところブートすらできず…お手本があることの有難さが身にしみます。
>raspi等のarm系の場合はlightmpdのconfigを修正した方が早いです。
>(中略)
>実際にbuildしたわけではないのですが、これでbuildできると思います。
情報ありがとうございます。raspiについても、やはり動くことがわかってるlightmpdをベースにするのが簡単なのでしょうね。こちらもそのうち試してみたいと思います。
前回もあわせて様々にアドバイスいただき有難うございます。buildrootを最近私は使い始めたばかりということもあるので(その点では、configの件もそのつもりで見ていただければと…)、大変参考になります。
> すでにraspiやbbbを余分に持っている場合はこの方がいいと思います。
私もraspiについては、1B+、2B、3B と持ってます(しかし動いてるのは2Bだけ…)。普通のPCにくらべると安いと思って買ってしまうんですよね。
アドバイス頂いた件は、まずは一つづつ試して行きたいと思います。区切りがつけば、結果報告させていただくかもしれません。
手順どおりにやっているつもりですが、以下のところでエラーとなります。
# roofsを initrd.romfsに戻します。
sudo genromfs -f ../initrd.romfs
のところで、genromfs「コマンドが見つかりません」となります。単純なミスをしているのだ
ろうと思うのですが、見当がつきません。お手数をお掛けしますが何方か教えてやってください。
そう云えば、VMware環境にUbntuを導入しておりますが、インストール後の
updateなどを失念しており、初期状態のまま操作しておりました。
初歩的なミスでお手数をおかけいたしました。
ご回答いただきありがとうございます。勉強になります。
私は今回公開されているやり方でlightmpdbbbv1.03に
upmpdcli+polipoを組み込んでみました。
/lighmtMPD/mpd.confには
input {
plugin "curl"
proxy "127.0.0.1:8123"
}
を追加した状態です。
/lightMPD/lightmpd.confはnfsをマウントするように設定してあります。
まず、cantata(mpdclieant)から再生をしてみました。
topで/usr/bin/polipoのvszを観察します。
再生を開始するともちろん音はでます。
polipoのvszは全く変化はありません。もちろん、upmpdcliのvszも変化なしです。
今までどおりの再生をしているわけですから当然ですね。
次に、cantataを停止し、LUMINを起動します。
LUMINにはcantataで再生していた曲目が表示されています。当然ですが。
LUMINを起動した瞬間からupmpdcliのvszが跳ね上がり、一定の値で止まります。
LUMINで再生を開始します。
もちろん、音がでます。
しかし、polipoのvszには変化がありません。
今度は、LUMINで表示されているplaylistの曲目を削除し、あらためてLUMINで
選曲した曲を再生します。
今度は、polipoのvszも跳ね上がり出しました。音もちゃんと出ております。
以上のような状態ですが、これで通常のnfsの再生とupnpレンダラーの再生
両方ともできていると解釈してよろしいんでしょうか?
takobozu
(1) buildrootでの減量の件ですが、nfs利用のみにしぼって、uInitrd が 3.5M、 zImage が 2.4M になりました。いちおう動いてます。
これには/usr/lib/*.so*のうち、不要そうなものを削除したりもしました。たとえば telnet でログインして:
for i in /usr/lib/*.so; do test "`grep $i /proc/*/maps`" = "" && echo -n "$i "; done; echo
の様にすると、現在のプロセスで利用されてない*.soが分かります。ただ、libeventなど、たぶん利用されてるはずのものも混るので、名前から類推して必要そうなものは残してあります。
このへんのconfig等の設定については、システムの様子をしばらく見てから、みなさんの参考までにGoogle driveにでも上げてみようかと思っています(たぶん2月末?)。
(2) 話が脱線ぎみですが、前にシステムA(raspiなど)でhttpdでoutputして、システムB(BBGなど)でcurlで受けて、その間にpolipoを挟めるか、ということを書いたのですが、curlで受けるところまではOKでした。
ただし、たとえばシステムB側にpolipoを挟もうとすると、
CURL failed: The requested URL returned error: 502 Unknown server HTTP version
というメッセージが出て上手く行きませんでした。そういうわけでイマイチな結果ですが、参考までにA側のhttpdの設定を書いておきます:
audio_output {
type "httpd"
name "Stream A"
encoder "flac"
compresion "0" #0(least compresion)-8(most compresion)
port "8000"
}
encoderの部分はwaveやnullもマニュアルによるとありますが、なぜかB側で受けられませんでした。なお、再生に関してはB側のaudio_buffer_sizeの設定がだいぶ影響します。
結局polipoを使うなら、素直にupnpにした方がよさそうです。
3. mpd本体を改造すると、再生の前に自動でデータをRAM上(あるいはSD上)にコピーして再生する、という動作をさせられるようです(いわゆるramplay(volumioに関し、たかじんさんの記事で見ました)、メモリ再生)。
上の2を試してる内、mpdはinputもプラグインで行なっていて、ファイル読み込みに関しても同じと気づいたので、そこのコードを見て改造してみました。以下がそのパッチです:
################################################################################
diff -ruN a/src/fs/io/FileReader.cxx b/src/fs/io/FileReader.cxx
--- a/src/fs/io/FileReader.cxx 2017-01-27 16:46:51.000000000 +0900
+++ b/src/fs/io/FileReader.cxx 2017-02-11 14:09:00.735098365 +0900
@@ -24,6 +24,9 @@
#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
#ifdef WIN32
FileReader::FileReader(Path _path)
@@ -87,12 +90,21 @@
#else
-FileReader::FileReader(Path _path)
+FileReader::FileReader(Path _path, bool ramplay)
:path(_path)
{
fd.OpenReadOnly(path.c_str());
if (!fd.IsDefined())
throw FormatErrno("Failed to open %s", path.ToUTF8().c_str());
+ if (ramplay && fd.GetSize() < 200000000) {
+ char tdir[] = "/tmp/mpd-temp-file";
+ char cmd [256];
+ memset(cmd, 0, sizeof(cmd));
+ snprintf(cmd, sizeof(cmd), "cp \"%s\" %s", path.c_str(), tdir);
+ system(cmd);
+ fd.Close();
+ fd.OpenReadOnly(tdir);
+ }
}
FileInfo
diff -ruN a/src/fs/io/FileReader.hxx b/src/fs/io/FileReader.hxx
--- a/src/fs/io/FileReader.hxx 2017-01-27 16:46:51.000000000 +0900
+++ b/src/fs/io/FileReader.hxx 2017-02-11 04:51:03.744126028 +0900
@@ -46,7 +46,7 @@
#endif
public:
- explicit FileReader(Path _path);
+ explicit FileReader(Path _path, bool ramplay=false);
#ifdef WIN32
FileReader(FileReader &&other)
diff -ruN a/src/input/plugins/FileInputPlugin.cxx b/src/input/plugins/FileInputPlugin.cxx
--- a/src/input/plugins/FileInputPlugin.cxx 2017-01-27 16:46:51.000000000 +0900
+++ b/src/input/plugins/FileInputPlugin.cxx 2017-02-11 04:51:03.744126028 +0900
@@ -57,7 +57,7 @@
OpenFileInputStream(Path path,
Mutex &mutex, Cond &cond)
{
- FileReader reader(path);
+ FileReader reader(path, true);
const FileInfo info = reader.GetFileInfo();
################################################################################
cxxの中でshellを呼ぶという手抜きですが、お試しと言うことで…。
これを当ててmakeしたmpdを使うと、ファイル読み込みして普通に再生する場合(input に file pluginを使う場合)は、すべて自動でramplayになります。
lightMPDの様にtmpfsを使っているのが前提です。パッチ内の /tmp/mpd-temp-file をSDの場所に変えればSD再生になります。BBG はメモリが512MBなので、fd.GetSize() < 200000000 と制限をつけてます(もっとメモリの大きいシステムや、SD再生にすれば不要でしょう。ただ転送時間はどうか?)。
データをコピーしてから再生するので、再生中にネットワークへのアクセスがなくなり、その影響を受けなくなると思われます。なお、ramplay関係については、他にもaudio_buffer_sizeを大きくする話があったと思います。
現状、「playerのpolipoとupnpgwのpolipoをカスケードにする」と再生が上手くいかない問題があったと思うのですが、upnpgwではpolipoを使い、playerではaki2muraさんが見つけられたramplayの設定を使えば、polipoのカスケードと同じ効果が得られるのではないでしょうか?
Linuxやプログラミングの知識がないので、見当違いかもしれませんが。
私はupnpの理解が浅く、カスケード問題も良くわかってないんですが、たぶん上の(3)では直接の解決にならないと思います。もしかすると(2)と(3)をつづけて書いたので誤解が生まれたかもしれません。
上の(3)の話は、「wav・flac等のファイルをRAM(or SD)に普通にコピーして再生する手続き」を自動化する話です。
一方で、私の大雑把な理解ですが、upnpはファイルをストリームにして受け渡しするのだと思います。プラグインもcurl受けなので、(3)で改造したfile pluginと違ってきます。ストリームをcurl pluginの改造でRAMにキャッシュできるかは、ちょっと分かりません。
横から失礼をしました。
無知ながらもなんとなく違いがわかりました。
そう都合よくいかないものですね。
* 構成
A: raspi(いまのところ普通のdebian)、eth0をBに接続、eth1はホーム・ネットワークに接続。
B: BBG(buildroot)、eth0をAに接続。
mpd A (httpd output) --> polipo A --> eth0 A --> eth0 B --> polipo B --> mpd B (curl input) --> dac..
の構成で、Bにあらかじめ mpc add http://.. などでAのアドレスを渡す。曲の選択・操作などはAのmpdで行う。
* やったこと。
ヒントを元に、Aのマシンで curl --head 127.0.0.1:8000 とすると、以下の出力を得ました。
HTTP/1.1 200 OK
Content-Type: audio/flac
Connection: close
Pragma: no-cache
Cache-Control: no-cache, no-store
どの辺がpolipoにとって足りないのか、これだけだと私にはわからず、またhttpdの構成も把握できてないので、mpd側の改造はちょっと難しそうでした。しかし一方で、digififanさんのコメントからすると、ちょっとしたことの様にも思えます。
そこで思い立ち、polipoのエラー箇所をコードの中から探すと server.c というファイル中にみつかりました。そこでは、
if(version != HTTP_10 && version != HTTP_11) {
...
のような部分があって、ここで version が取れてないとエラーで止まる仕様。物は試しと、この中を書きかえて、エラー処理を消してかわりに「version = HTTP_11」と代入する形にしました。荒っぽい対処ですが、まあとりあえずということで…
この改造でmakeしたpolipoをA、Bにおいて、上手く繋がるようになりました。htop等で動作も確認したのでOKだと思います。
この構成だとB側はmpd+flac(+polipo)だけあれば良く、SD・nfsのマウントも必要ないので、相当サイズを小さくできそうです。
確かにpolipoの件は奇妙な様なので、http_parse.c等が今回の場合にどう動いてるか、時間のある時にもう少し調べたいと思っています。
A->Bの間のencoder->decoderの件も気になるところです。実は先週末に、nullやwaveについて多少テストしました。outputのencoderでnullを使った場合、curlで読んだとき、
Content-Type: application/octet-stream
と表示されるので、input側のpcm decoderのコードPcmDecoderPlugin.cxx中のpcm_mime_typesに、"application/octet-stream"を追加すると、44.1kHzの音楽ファイルであれば再生できました。ただし48kHzでは雑音になります。
これは、pcm_stream_decodeという関数中でaudio_formatに44100などのデフォルト設定を決めてるためと思われ、digififanさんの書いた事情の通りかと思います。
同じ関数のなかで、データを読み取ってaudio_formatを再設定をしているので、この辺にちゃんと値を渡せればあるいはnullも使えるのかとは思ったんですが、具体的なところは分かりませんでした。
なお、waveの場合は、たとえばaudiofileのaudiofile_mime_typesに、同じ様にcurlで出てきた"audio/wav"を安直に追加してみましたが、再生もダメでした。
なんとなく、flacはmpd中でもコード量が多く手厚い雰囲気で、httpdのことも考慮されてるのかもと感じます。ところで先程調べたところ、flacは無圧縮?にできるそうで、コマンドとしては、
flac --disable-constant-subframes --disable-fixed-subframes --no-seektable -l 0 -b 4608 -V a.wav
などが見つかります。実際wavをflacに変換しても、ファイルサイズが変わりません。これを使うので良ければ、FlacEncoderPlugin.cxx中のflac_encoder_setupで
FLAC__stream_encoder_set_compression_level (libFLACの関数)
を呼んでる部分をコメントアウトして、代わりに
FLAC__stream_encoder_disable_constant_subframes
など対応する関数を書けば良さそうに見えます。(出張中なもので、試すとしても週末になりますが…)
http渡しでA->Bで役割を分離できるのは、なかなか楽しいかもしれません。特に、B側をBBGなどにするとCPUの処理能力は低いので、代わりにA側に高性能なraspi 3Bなどのシステムを使って補う、という使い方もできそうです。たとえばAで整数倍アップサンプリングしてBに渡す、など試してみたいと思いました。
(1) mpdのhttp outをpolipoで受けるには改造が必要という話ですが、コードにprintfをくっくけ出力を見て、原因がわかった気がします。
polipoのhttp_parse.cのhttpParseServerFirstLineは、HTTP 1.1 OK..というヘッダを期待しています。一方、mpdはこの ServerFirstLine (curl --headでは出てこない?)で、mpdのIcyMetaDataServer.cxxのicy_server_metadata_headerによるヘッダICY 200 OK..を渡してる様で、ここで問題が起きる様でした。
(2) また、別の問題なのですが、http outで数曲再生していると、polipoが突然ダウンすることがあります。どうも、polipoが想定してない使い方のせいか、offset(object.cのobjectAddData内だとか、lockChunkのiなど)がC言語の整数型の最大値を越えてマイナスになりエラーが起きるようです。
対策として、曲と曲の合間に、B側に mpc で stop・play を自動で送って繋ぎ直す形にすると、offsetがリセットされる様子で一応大丈夫になりました。ただ、もっと良い方法があるかもしれません。
(3) 出力のencodeをどうするかという件は、結局、Null encoder (A側)で素のPCMデータを送ってPCM decoder(B側)で受ける形にしました。これだとaudioformatを別に渡す必要があるわけですが、mpdの中で完結させるにはちょっと知識が足りなかったので、もう一つ通信を開いてデータを渡すことにしました(busyboxのnetcatを利用)。
ちなみに、
ramplay->オーバーサンプリング->polipo(A)->polipo(B)->dac..
と全部盛ってみて使ってます。なお、A:raspi 3B で B:BBG にして、両方ともbuildroot 2017.2 にしましたが、すんなり移行できました。一度形が決まれば、新しい環境でもすぐ試せるのはbuildrootの良いところですね。
(4) raspi3Bで、2ベキ倍のオーバーサンプリングをしようと思ったんですが、lightMPDについてるパッチはmpd0.20系では使えないんですね。調べたところ、src/output/OutputThread.cxxの中でフィルター(soxr)を当てる前の部分を変更すればよさそうなので、次のパッチの様にしました:
--- a/src/output/OutputThread.cxx 2017-01-27 16:46:51.000000000 +0900
+++ e/src/output/OutputThread.cxx 2017-03-02 00:21:47.750285083 +0900
@@ -121,7 +121,16 @@
name, plugin.name));
}
- const auto cf = f.WithMask(config_audio_format);
+ int mult = 1;
+
+ while (2*mult*f.sample_rate <= config_audio_format.sample_rate)
+ mult = 2*mult;
+
+ AudioFormat cfx = config_audio_format;
+ cfx.sample_rate = mult*f.sample_rate;
+
+ const auto cf = f.WithMask(cfx);
+ // const auto cf = f.WithMask(config_audio_format);
if (open && cf != filter_audio_format) {
/* if the filter's output format changes, the output
mpd.confで指定したformatの値を越えない範囲の最大値で2ベキ倍されます。たとえば format 364000:32:2 の様に指定すると、44100系については8倍の352800になると思います。(このへん私の理解が足りず、何か変な部分があるかもしれませんが…)
また、buildrootは標準ではlibsoxr.mkでopenmpが無効になってるため、有効にしてビルドしました。
polipoの制限については除く予定があったのですね。そうなると確かに助かります(個人的には、どんな風に改修するのかも興味あります。コード全体に渡るものと思うので、だいぶ大変そうですね)。
自分の使い方では、先に述べたように繋ぎ直しで一応しのげるので、とりあえずはそれで回避しておきます。ちなみに、src/player/Thread.cxx に Player::SongBorder という関数があって、名前のとおり曲と曲の間に呼ばれるようなのでそこに処理を挟んでいます。
また、0.20系の拡張フォーマットについても公開予定だったのですね。先走ったようで済みません。
ApplyMask(WithMask)のことですが、気になっていたのは src/decoder/DecoderControl.cxx の中でWithMaskが呼ばれていることでした。このあたりのdecorder関係の動作をイマイチ把握できず、修正の影響が出て良いかわからなかったので、上に挙げたパッチでは OutputThread の filter の手前だけを修正する形にしてました。