目的

PeerCastStationはmp3配信に対応していません(Peercast-YT版はおそらく対応しています)。 ですから通常の音声のみ配信(SHOUTcastみたいなやつ)をやりたい場合は PeerCastStationが対応しているコンテナの中から音声だけ乗せられるコンテナを選ぶ必要があります。

通常であればASF(かFLV)を選べば大丈夫だと思いますがここではあえてMKV(Matroska Video)/MKA(Matroska Audio)で配信を行います。

そして今回はPowerShell上から全ての操作を行います。

なお今回の手順はWindows 10上で行っています。

手順

前提条件

PowerShellを使うので出来るだけ新しいバージョンのPowerShellをインストールしてください。 執筆した時に使用したのはPowerShell 7.3.0です。

もしバージョンを確認したい場合は次のコマンドで確認可能です。

Get-Host | ForEach-Object {$_.Version}

音声をキャプチャ/エンコードしてPeerCastStationへ送出するのにffmpegが必要です。 https://ffmpeg.org/download.html からつながっているリンク先で Windows用の実行ファイルをダウンロードして配置してください。

今回の手順ではffmpegの実行ファイルを PATHが通っている場所に配置していることが前提になっています。

ffmpegは現状だとループバックキャプチャ、 つまり音声出力をそのままキャプチャできません(出来ることもある)。 疑似的にキャプチャするためにVB-CABLEをインストールして出力デバイスをVB-Cable Inputと書いてあるものにしてください。

PowerShellでJSON RPCと戦う

PeerCastStationはJSON RPCのAPIを公開しています(https://github.com/kumaryu/peercaststation/wiki/JSON-RPC-API-%E3%83%A1%E3%83%A2)。 通常であればこの文書に書いてある通りcurlを使うのが一番賢い方法です。 ですが今回はPowerShellで戦います。

まず配信設定用のhash tableを作ります。以下は実際に使用した例です。

$ch_jsonrpc_data = @{
  "networkType"="IPv6";
  "yellowPageId"=2044241712;
  "sourceUri"='http://0.0.0.0:8080/stream.mka';
  "contentReader"="Matroska (MKV or WebM)";
  "info"=@{
    "name"="ふぃるちゃんねる(テスト)";
    "url"='http://bbs.jpnkn.com/philmist/';
    "genre"="Test";
    "desc"="テスト中:突然終わります";
    "comment"="音だけです。何も反応できません。";
    "contentType"="MKV"
  };
  "track"=@{}
}

yellowPageIdは事前にJSON RPCを使用してPeerCastStationから取得してください("method": "getYellowPages")。 contentReaderも同様です("method": "getContentReaders"で得られたnameのほう)。

sourceUriはPeerCastStationが待ち受けるURIとポートを指定します。 同一PCで動かしているのであればhttp://localhost:8080/stream.mkaとかで良いと思いますが、 別PCで動かしているのであれば0.0.0.0を指定して全てのインタフェースで待ち受けするのが楽です。

あとはこれをInvoke-WebRequestにJSON RPCの形式にしてPOSTすれば良いのですが以下は 動かなかった例です

$invoke_result = @{
  "jsonrpc"="2.0";
  "method"="broadcastChannel";
  "id"=7144;"params"=$ch_jsonrpc_data
} |
ConvertTo-Json |
Invoke-WebRequest -Method Post -Headers @{"X-Requested-With"="XMLHttpRequest"} -ContentType "application/json" http://192.168.0.100:7144/api/1

これを実行するとPeerCastStationのほうでは日本語が文字化けした状態で登録されます。

なぜこういうことが起こるのかというと、 あくまで推測になりますがPowershellはBOM付きのUTF-8を送信しようとするからです。 同様に受信する時もBOMつきのUTF-8でないと文字化けします。 この挙動を理解するためには受信の例を見るのが良いです。

受信する時は次のような感じにすれば良いです($resultを後で参照してください)。

$result = @{
  "method"="getChannels";
  "id"=19021;
  "jsonrpc"="2.0";
  "params"=@{}
} |
ConvertTo-Json |
Invoke-WebRequest -Method Post -Headers @{
  "Content-type"="application/json";
  "X-Requested-With"="XMLHttpRequest"
} http://192.168.0.100:7144/api/1 |
%{ [Text.Encoding]::UTF8.GetString([Text.Encoding]::UTF8.GetBytes($_.Content)) } |
ConvertFrom-Json

ここの肝となる部分は [Text.Encoding]::UTF8.GetString([Text.Encoding]::UTF8.GetBytes($_.Content)) です(%ForEach-Objectのalias)。

.NETの関数を使用した作業です。Text.Encoding.UTF8.GetBytesは対象を(なぜか)BOMつきであろうがなかろうがUTF-8のバイト列に変換します。 Text.Encoding.UTF8.GetStringはバイト列をBOMつきの文字列に変換します。

なぜこういうことをして上手くいくのかというとどうやらPowershell内部の文字列処理がBOM付きを前提にしていることによります。 そしてこの問題がInvoke-WebRequestで送信する時に問題を引きおこします。

これを踏まえた次の送信例は 動かなかった例 です。

$encoding = New-Object System.Text.UTF8Encoding $false
$invoke_result = @{
  "jsonrpc"="2.0";
  "method"="broadcastChannel";
  "id"=7144;
  "params"=$ch_jsonrpc_data
} |
ConvertTo-Json |
%{ $encoding.GetString([Text.Encoding]::UTF8.GetBytes($_)) } |
Invoke-WebRequest -Method Post -Headers @{"Content-type"="application/json;coding=utf-8";"X-Requested-With"="XMLHttpRequest"} http://192.168.0.100:7144/api/1

これはなんとかがんばってBOM付きに変換しようとしていますが、 結局パイプで渡される過程で死んでいます。 これを回避するにはいったんファイルに書きだす必要があります。

@{
  "jsonrpc"="2.0";
  "method"="broadcastChannel";
  "id"=7144;
  "params"=$ch_jsonrpc_data
} |
ConvertTo-Json |
Out-File -Encoding UTF8 request.txt

そしてこれをInvoke-WebRequestで読み込んで送信します。

$invoke_result = Invoke-WebRequest -Method Post -Headers @{"X-Requested-With"="XMLHttpRequest"} -ContentType "application/json" -InFile .\request.txt http://192.168.0.100:7144/api/1

正直ダメな仕様だと思いますが 現状ではこれが回避策だと思います。

参考文献を挙げておきます。

  • https://stackoverflow.com/questions/53033242/powershell-convertfrom-json-encoding-special-characters-issue/53033574#53033574
  • https://qiita.com/zaki-lknr/items/1ae3258d7b77c5e2a2ba
  • https://qiita.com/todkm/items/fbb139b8c9f3e481054b

ffmpegと戦う

前節の作業でPeerCastStationは待ち受けを開始しています。 今度はffmpegでデータを送信します。

まず最初にffmpegで認識されているデバイスを確認します。

ffmpeg -list_devices true -f dshow -i dummy

この中にVB-CableのOutput(CABLE Output (VB-Audio Virtual Cable)みたいなやつ)があることを確認します。 これを入力デバイスとしてPeerCastStationに出力します。

ここでは192.168.0.100をPeerCastStationが起動されているPCとしています。 別途ファイアウォール等の設定はしてあるものとします。

ffmpeg -f dshow -i 'audio=CABLE Output (VB-Audio Virtual Cable)' -map 0:a:0 -c:a libvorbis -metadata 'language=ja-jp' http://192.168.0.100:8080/stream.mka

今回は関係ありませんが ffmpegは-iオプションの順番で入力ソースを管理するのに注意が必要です。

-map 0:a:0は1番目の入力でオーディオソースのうちの1番目を出力先の1番目のストリームとして指定しています(いわゆる0-indexed)。 -mapは指定した順番に出力先へ格納されます。

-c:aでオーディオで使用するコーデック(というよりライブラリ)を指定しています。 今回はlibvorbisなのでVorbisを音声コーデックに指定しています(良く使われるOgg VorbisのVorbis)。

-b:aでABRのビットレート、-q:aでVBRの品質を指定できると思いますが、 今回は特に指定していません。指定していない場合は-q:a 3.0相当になるはずです。 詳細は https://ffmpeg.org/ffmpeg-codecs.html#libvorbis を見てください。

結論

光の戦士時代にオーディオだけ送信をやるのはかなり無理があるよ……