Replace VoEBase::[Start/Stop]Playout().

The functionality is moved into AudioState.

TBR: henrika@webrtc.org
Bug: webrtc:4690
Change-Id: I015482ad18a39609634f6ead9e991d5210107f0f
Reviewed-on: https://webrtc-review.googlesource.com/34502
Reviewed-by: Fredrik Solenberg <solenberg@webrtc.org>
Commit-Queue: Fredrik Solenberg <solenberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21338}
diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc
index a1233f2..74f1773 100644
--- a/audio/audio_receive_stream.cc
+++ b/audio/audio_receive_stream.cc
@@ -118,9 +118,7 @@
 AudioReceiveStream::~AudioReceiveStream() {
   RTC_DCHECK_RUN_ON(&worker_thread_checker_);
   RTC_LOG(LS_INFO) << "~AudioReceiveStream: " << config_.ToString();
-  if (playing_) {
-    Stop();
-  }
+  Stop();
   channel_proxy_->DisassociateSendChannel();
   channel_proxy_->RegisterTransport(nullptr);
   channel_proxy_->ResetReceiverCongestionControlObjects();
@@ -132,21 +130,9 @@
   if (playing_) {
     return;
   }
-
-  int error = SetVoiceEnginePlayout(true);
-  if (error != 0) {
-    RTC_LOG(LS_ERROR) << "AudioReceiveStream::Start failed with error: "
-                      << error;
-    return;
-  }
-
-  if (!audio_state()->mixer()->AddSource(this)) {
-    RTC_LOG(LS_ERROR) << "Failed to add source to mixer.";
-    SetVoiceEnginePlayout(false);
-    return;
-  }
-
+  channel_proxy_->StartPlayout();
   playing_ = true;
+  audio_state()->AddReceivingStream(this);
 }
 
 void AudioReceiveStream::Stop() {
@@ -154,10 +140,9 @@
   if (!playing_) {
     return;
   }
+  channel_proxy_->StopPlayout();
   playing_ = false;
-
-  audio_state()->mixer()->RemoveSource(this);
-  SetVoiceEnginePlayout(false);
+  audio_state()->RemoveReceivingStream(this);
 }
 
 webrtc::AudioReceiveStream::Stats AudioReceiveStream::GetStats() const {
@@ -344,14 +329,5 @@
   RTC_DCHECK(audio_state);
   return audio_state;
 }
-
-int AudioReceiveStream::SetVoiceEnginePlayout(bool playout) {
-  ScopedVoEInterface<VoEBase> base(voice_engine());
-  if (playout) {
-    return base->StartPlayout(config_.voe_channel_id);
-  } else {
-    return base->StopPlayout(config_.voe_channel_id);
-  }
-}
 }  // namespace internal
 }  // namespace webrtc
diff --git a/audio/audio_receive_stream.h b/audio/audio_receive_stream.h
index a61c896..55e58d7 100644
--- a/audio/audio_receive_stream.h
+++ b/audio/audio_receive_stream.h
@@ -82,7 +82,6 @@
  private:
   VoiceEngine* voice_engine() const;
   AudioState* audio_state() const;
-  int SetVoiceEnginePlayout(bool playout);
 
   rtc::ThreadChecker worker_thread_checker_;
   rtc::ThreadChecker module_process_thread_checker_;
diff --git a/audio/audio_receive_stream_unittest.cc b/audio/audio_receive_stream_unittest.cc
index d24bed5..4d4af2e 100644
--- a/audio/audio_receive_stream_unittest.cc
+++ b/audio/audio_receive_stream_unittest.cc
@@ -48,7 +48,8 @@
   return audio_decode_stats;
 }
 
-const int kChannelId = 2;
+const int kChannelId1 = 2;
+const int kChannelId2 = 29;
 const uint32_t kRemoteSsrc = 1234;
 const uint32_t kLocalSsrc = 5678;
 const size_t kOneByteExtensionHeaderLength = 4;
@@ -84,7 +85,7 @@
         new rtc::RefCountedObject<MockAudioDeviceModule>();
     audio_state_ = AudioState::Create(config);
 
-    EXPECT_CALL(voice_engine_, ChannelProxyFactory(kChannelId))
+    EXPECT_CALL(voice_engine_, ChannelProxyFactory(kChannelId1))
         .WillOnce(Invoke([this](int channel_id) {
           EXPECT_FALSE(channel_proxy_);
           channel_proxy_ = new testing::StrictMock<MockVoEChannelProxy>();
@@ -118,7 +119,15 @@
                   }));
           return channel_proxy_;
         }));
-    stream_config_.voe_channel_id = kChannelId;
+    EXPECT_CALL(voice_engine_, ChannelProxyFactory(kChannelId2))
+        .WillRepeatedly(Invoke([this](int channel_id) {
+          testing::NiceMock<MockVoEChannelProxy>* proxy =
+              new testing::NiceMock<MockVoEChannelProxy>();
+          EXPECT_CALL(*proxy, GetAudioDecoderFactory())
+              .WillOnce(ReturnRef(decoder_factory_));
+          return proxy;
+        }));
+    stream_config_.voe_channel_id = kChannelId1;
     stream_config_.rtp.local_ssrc = kLocalSsrc;
     stream_config_.rtp.remote_ssrc = kRemoteSsrc;
     stream_config_.rtp.nack.rtp_history_ms = 300;
@@ -231,7 +240,7 @@
   AudioReceiveStream::Config config;
   config.rtp.remote_ssrc = kRemoteSsrc;
   config.rtp.local_ssrc = kLocalSsrc;
-  config.voe_channel_id = kChannelId;
+  config.voe_channel_id = kChannelId1;
   config.rtp.extensions.push_back(
       RtpExtension(RtpExtension::kAudioLevelUri, kAudioLevelId));
   EXPECT_EQ(
@@ -354,32 +363,36 @@
   recv_stream.SetGain(0.765f);
 }
 
-TEST(AudioReceiveStreamTest, StreamShouldNotBeAddedToMixerWhenVoEReturnsError) {
+TEST(AudioReceiveStreamTest, StreamsShouldBeAddedToMixerOnceOnStart) {
   ConfigHelper helper;
-  internal::AudioReceiveStream recv_stream(
+  internal::AudioReceiveStream recv_stream1(
       helper.rtp_stream_receiver_controller(),
       helper.packet_router(),
       helper.config(), helper.audio_state(), helper.event_log());
-
-  EXPECT_CALL(helper.voice_engine(), StartPlayout(_)).WillOnce(Return(-1));
-  EXPECT_CALL(*helper.audio_mixer(), AddSource(_)).Times(0);
-
-  recv_stream.Start();
-}
-
-TEST(AudioReceiveStreamTest, StreamShouldBeAddedToMixerOnStart) {
-  ConfigHelper helper;
-  internal::AudioReceiveStream recv_stream(
+  AudioReceiveStream::Config config2 = helper.config();
+  config2.voe_channel_id = kChannelId2;
+  internal::AudioReceiveStream recv_stream2(
       helper.rtp_stream_receiver_controller(),
       helper.packet_router(),
-      helper.config(), helper.audio_state(), helper.event_log());
+      config2, helper.audio_state(), helper.event_log());
 
-  EXPECT_CALL(helper.voice_engine(), StartPlayout(_)).WillOnce(Return(0));
-  EXPECT_CALL(helper.voice_engine(), StopPlayout(_));
-  EXPECT_CALL(*helper.audio_mixer(), AddSource(&recv_stream))
+  EXPECT_CALL(*helper.channel_proxy(), StartPlayout()).Times(1);
+  EXPECT_CALL(*helper.channel_proxy(), StopPlayout()).Times(1);
+  EXPECT_CALL(*helper.audio_mixer(), AddSource(&recv_stream1))
       .WillOnce(Return(true));
+  EXPECT_CALL(*helper.audio_mixer(), AddSource(&recv_stream2))
+      .WillOnce(Return(true));
+  EXPECT_CALL(*helper.audio_mixer(), RemoveSource(&recv_stream1)).Times(1);
+  EXPECT_CALL(*helper.audio_mixer(), RemoveSource(&recv_stream2)).Times(1);
 
-  recv_stream.Start();
+  recv_stream1.Start();
+  recv_stream2.Start();
+
+  // One more should not result in any more mixer sources added.
+  recv_stream1.Start();
+
+  // Stop stream before it is being destructed.
+  recv_stream2.Stop();
 }
 }  // namespace test
 }  // namespace webrtc
diff --git a/audio/audio_state.cc b/audio/audio_state.cc
index ac7eaf7..9d980e8 100644
--- a/audio/audio_state.cc
+++ b/audio/audio_state.cc
@@ -14,6 +14,7 @@
 #include <utility>
 #include <vector>
 
+#include "audio/audio_receive_stream.h"
 #include "modules/audio_device/include/audio_device.h"
 #include "rtc_base/atomicops.h"
 #include "rtc_base/checks.h"
@@ -37,6 +38,7 @@
 
 AudioState::~AudioState() {
   RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  RTC_DCHECK(receiving_streams_.empty());
   RTC_DCHECK(sending_streams_.empty());
 }
 
@@ -45,16 +47,44 @@
   return config_.voice_engine;
 }
 
-rtc::scoped_refptr<AudioMixer> AudioState::mixer() {
-  RTC_DCHECK(thread_checker_.CalledOnValidThread());
-  return config_.audio_mixer;
-}
-
 bool AudioState::typing_noise_detected() const {
   RTC_DCHECK(thread_checker_.CalledOnValidThread());
   return audio_transport_.typing_noise_detected();
 }
 
+void AudioState::AddReceivingStream(webrtc::AudioReceiveStream* stream) {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  RTC_DCHECK_EQ(0, receiving_streams_.count(stream));
+  receiving_streams_.insert(stream);
+  if (!config_.audio_mixer->AddSource(
+      static_cast<internal::AudioReceiveStream*>(stream))) {
+    RTC_LOG(LS_ERROR) << "Failed to add source to mixer.";
+  }
+
+  // Make sure playback is initialized; start playing if enabled.
+  auto* adm = config_.audio_device_module.get();
+  if (!adm->Playing()) {
+    if (adm->InitPlayout() == 0) {
+      if (playout_enabled_) {
+        adm->StartPlayout();
+      }
+    } else {
+      RTC_DLOG_F(LS_ERROR) << "Failed to initialize playout.";
+    }
+  }
+}
+
+void AudioState::RemoveReceivingStream(webrtc::AudioReceiveStream* stream) {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  auto count = receiving_streams_.erase(stream);
+  RTC_DCHECK_EQ(1, count);
+  config_.audio_mixer->RemoveSource(
+      static_cast<internal::AudioReceiveStream*>(stream));
+  if (receiving_streams_.empty()) {
+    config_.audio_device_module->StopPlayout();
+  }
+}
+
 void AudioState::AddSendingStream(webrtc::AudioSendStream* stream,
                                   int sample_rate_hz, size_t num_channels) {
   RTC_DCHECK(thread_checker_.CalledOnValidThread());
@@ -89,20 +119,18 @@
 void AudioState::SetPlayout(bool enabled) {
   RTC_LOG(INFO) << "SetPlayout(" << enabled << ")";
   RTC_DCHECK(thread_checker_.CalledOnValidThread());
-  const bool currently_enabled = (null_audio_poller_ == nullptr);
-  if (enabled == currently_enabled) {
-    return;
-  }
-  if (enabled) {
-    null_audio_poller_.reset();
-  }
-  // Will stop/start playout of the underlying device, if necessary, and
-  // remember the setting for when it receives subsequent calls of
-  // StartPlayout.
-  voe_base_->SetPlayout(enabled);
-  if (!enabled) {
-    null_audio_poller_ =
-        rtc::MakeUnique<NullAudioPoller>(&audio_transport_);
+  if (playout_enabled_ != enabled) {
+    playout_enabled_ = enabled;
+    if (enabled) {
+      null_audio_poller_.reset();
+      if (!receiving_streams_.empty()) {
+        config_.audio_device_module->StartPlayout();
+      }
+    } else {
+      config_.audio_device_module->StopPlayout();
+      null_audio_poller_ =
+          rtc::MakeUnique<NullAudioPoller>(&audio_transport_);
+    }
   }
 }
 
@@ -157,7 +185,7 @@
 
 void AudioState::UpdateAudioTransportWithSendingStreams() {
   RTC_DCHECK(thread_checker_.CalledOnValidThread());
-  std::vector<AudioSendStream*> sending_streams;
+  std::vector<webrtc::AudioSendStream*> sending_streams;
   int max_sample_rate_hz = 8000;
   size_t max_num_channels = 1;
   for (const auto& kv : sending_streams_) {
diff --git a/audio/audio_state.h b/audio/audio_state.h
index 540a249..3a92d42 100644
--- a/audio/audio_state.h
+++ b/audio/audio_state.h
@@ -13,6 +13,7 @@
 
 #include <map>
 #include <memory>
+#include <unordered_set>
 
 #include "audio/audio_transport_impl.h"
 #include "audio/null_audio_poller.h"
@@ -27,6 +28,7 @@
 namespace webrtc {
 
 class AudioSendStream;
+class AudioReceiveStream;
 
 namespace internal {
 
@@ -50,9 +52,11 @@
   void SetStereoChannelSwapping(bool enable) override;
 
   VoiceEngine* voice_engine();
-  rtc::scoped_refptr<AudioMixer> mixer();
   bool typing_noise_detected() const;
 
+  void AddReceivingStream(webrtc::AudioReceiveStream* stream);
+  void RemoveReceivingStream(webrtc::AudioReceiveStream* stream);
+
   void AddSendingStream(webrtc::AudioSendStream* stream,
                         int sample_rate_hz, size_t num_channels);
   void RemoveSendingStream(webrtc::AudioSendStream* stream);
@@ -68,6 +72,7 @@
   rtc::ThreadChecker process_thread_checker_;
   const webrtc::AudioState::Config config_;
   bool recording_enabled_ = true;
+  bool playout_enabled_ = true;
 
   // We hold one interface pointer to the VoE to make sure it is kept alive.
   ScopedVoEInterface<VoEBase> voe_base_;
@@ -85,6 +90,7 @@
   // stats are still updated.
   std::unique_ptr<NullAudioPoller> null_audio_poller_;
 
+  std::unordered_set<webrtc::AudioReceiveStream*> receiving_streams_;
   struct StreamProperties {
     int sample_rate_hz = 0;
     size_t num_channels = 0;