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;