ラズパイのmomoでステレオ音声でWebRTCで配信する

[前回](https://qiita.com/tetsu_koba/items/b887c1a0be9f26b795f2)の続き。

## libwebrtcでのaudio processing
[前回](https://qiita.com/tetsu_koba/items/b887c1a0be9f26b795f2)の記事で、momoで音楽を流すことに成功した。
しかし、libwebrtcではマイクから拾った人の話し声に特化した音声の加工がデフォルトで行われるため、音楽を流すと妙に痩せ細ったような不自然な感じになる。なので音楽などマイク以外の音声を流す場合にはこれらの音声加工をオフにしたい。

音声加工に関してどのようなものがあるのかはwebrtcのソースコードを調べた。詳細は後述。

## momoで各種音声加工処理を無効にする

とりあえず以下のようにmomoのソースコードを修正してビルドして試した。

```diff
diff --git a/src/rtc/manager.cpp b/src/rtc/manager.cpp
index 6cd5d7f..80fb657 100644
--- a/src/rtc/manager.cpp
+++ b/src/rtc/manager.cpp
@@ -147,9 +147,17 @@ std::shared_ptr<RTCConnection> RTCManager::createConnection(
 
   if (!_conn_settings.no_audio)
   {
+    cricket::AudioOptions ao;
+    ao.echo_cancellation = false;
+    ao.auto_gain_control = false;
+    ao.noise_suppression = false;
+    ao.highpass_filter = false;
+    ao.typing_detection = false;
+    ao.residual_echo_detector = false;
+    RTC_LOG(LS_INFO) << __FUNCTION__ << ao.ToString();
     rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
         _factory->CreateAudioTrack(Util::generateRandomChars(),
-                                   _factory->CreateAudioSource(cricket::AudioOptions())));
+                                   _factory->CreateAudioSource(ao)));
     if (audio_track)
     {
```

次のmomoのリリースではこのあたりをコマンドラインオプションか設定ファイルで指定できるようになると嬉しいなあ。

2019.9.10 追記
momo 19.09.0 のリリースで音声フィルターをdisableにするための各種コマンドラインオプションが追加されました！
追記ここまで。

ステレオの音声の左チャネルの音しか聞こえないという件は、`echo_cancellation` が影響していた。この項目をオフにすることでステレオの両方のチャネルの音が聞こえるようになった。


## 視聴側のブラウザによる違い

これでmomoからはステレオで送信できるようになったが、受信するブラウザによって聞こえ方が違うということがわかった。


Firefox ではステレオで視聴できた。
68.0.2 (64 ビット) Mac版

Chromeではモノラルになってしまった。左チャネルと右チャネルがミックスされた音が両方のチェンネルから聞こえる。
バージョン: 76.0.3809.100（Official Build） （64 ビット） Mac版
Chromeでは明示的に何かをオフにする必要があるらしい。

SafariでもChromeと同じようにモノラルになってしまった。
バージョン12.1.2 (14607.3.9) Mac版

** 2022/05/12 情報更新 **
Chome, SafariでWebRTCでステレオ音声がモノラルになってしまう件ですが、まだ状況は変わらないようですね。Firefoxだけがステレオで受信できます。
私かが確認したバージョンは以下の通り。

いずれもMacで
Chrome: バージョン: 101.0.4951.64（Official Build） （x86_64）
Safari: バージョン15.4 (17613.1.17.1.13)
Firefox: 100.0 (64 ビット)
** 2022/05/12 情報更新 ここまで **

ステレオのテスト音声ファイルとしては以下のサイトのものを使用した。
http://www.aoakley.com/articles/2018-08-24-stereo-test.php

## 詳細情報
(2019年8月の情報)

libwebrtcのソースコードを調べた。
AudioOptions で設定できる項目は以下。

```c++:api/audio_options.h
// Options that can be applied to a VoiceMediaChannel or a VoiceMediaEngine.
// Used to be flags, but that makes it hard to selectively apply options.
// We are moving all of the setting of options to structs like this,
// but some things currently still use flags.
struct AudioOptions {
  AudioOptions();
  ~AudioOptions();
  void SetAll(const AudioOptions& change);

  bool operator==(const AudioOptions& o) const;
  bool operator!=(const AudioOptions& o) const { return !(*this == o); }

  std::string ToString() const;

  // Audio processing that attempts to filter away the output signal from
  // later inbound pickup.
  absl::optional<bool> echo_cancellation;
#if defined(WEBRTC_IOS)
  // Forces software echo cancellation on iOS. This is a temporary workaround
  // (until Apple fixes the bug) for a device with non-functioning AEC. May
  // improve performance on that particular device, but will cause unpredictable
  // behavior in all other cases. See http://bugs.webrtc.org/8682.
  absl::optional<bool> ios_force_software_aec_HACK;
#endif
  // Audio processing to adjust the sensitivity of the local mic dynamically.
  absl::optional<bool> auto_gain_control;
  // Audio processing to filter out background noise.
  absl::optional<bool> noise_suppression;
  // Audio processing to remove background noise of lower frequencies.
  absl::optional<bool> highpass_filter;
  // Audio processing to swap the left and right channels.
  absl::optional<bool> stereo_swapping;
  // Audio receiver jitter buffer (NetEq) max capacity in number of packets.
  absl::optional<int> audio_jitter_buffer_max_packets;
  // Audio receiver jitter buffer (NetEq) fast accelerate mode.
  absl::optional<bool> audio_jitter_buffer_fast_accelerate;
  // Audio receiver jitter buffer (NetEq) minimum target delay in milliseconds.
  absl::optional<int> audio_jitter_buffer_min_delay_ms;
  // Audio receiver jitter buffer (NetEq) should handle retransmitted packets.
  absl::optional<bool> audio_jitter_buffer_enable_rtx_handling;
  // Audio processing to detect typing.
  absl::optional<bool> typing_detection;
  absl::optional<bool> experimental_agc;
  absl::optional<bool> extended_filter_aec;
  absl::optional<bool> delay_agnostic_aec;
  absl::optional<bool> experimental_ns;
  // Note that tx_agc_* only applies to non-experimental AGC.
  absl::optional<bool> residual_echo_detector;
  absl::optional<uint16_t> tx_agc_target_dbov;
  absl::optional<uint16_t> tx_agc_digital_compression_gain;
  absl::optional<bool> tx_agc_limiter;
  // Enable combined audio+bandwidth BWE.
  // TODO(pthatcher): This flag is set from the
  // "googCombinedAudioVideoBwe", but not used anywhere. So delete it,
  // and check if any other AudioOptions members are unused.
  absl::optional<bool> combined_audio_video_bwe;
  // Enable audio network adaptor.
  absl::optional<bool> audio_network_adaptor;
  // Config string for audio network adaptor.
  absl::optional<std::string> audio_network_adaptor_config;
};
```

初期化時にセットされるAudioOptionsのデフォルト値は以下の通り。

```media/engine/webrtc_voice_engine.cc
void WebRtcVoiceEngine::Init() {

  ...

  // Set default engine options.
  {
    AudioOptions options;
    options.echo_cancellation = true;
    options.auto_gain_control = true;
    options.noise_suppression = true;
    options.highpass_filter = true;
    options.stereo_swapping = false;
    options.audio_jitter_buffer_max_packets = 50;
    options.audio_jitter_buffer_fast_accelerate = false;
    options.audio_jitter_buffer_min_delay_ms = 0;
    options.audio_jitter_buffer_enable_rtx_handling = false;
    options.typing_detection = true;
    options.experimental_agc = false;
    options.extended_filter_aec = false;
    options.delay_agnostic_aec = false;
    options.experimental_ns = false;
    options.residual_echo_detector = true;
    bool error = ApplyOptions(options);
    RTC_DCHECK(error);
  }
```