diff --git a/talk/app/webrtc/peerconnectioninterface_unittest.cc b/talk/app/webrtc/peerconnectioninterface_unittest.cc index b1f718b460..421836e74e 100644 --- a/talk/app/webrtc/peerconnectioninterface_unittest.cc +++ b/talk/app/webrtc/peerconnectioninterface_unittest.cc @@ -2084,8 +2084,12 @@ TEST_F(PeerConnectionInterfaceTest, SdpWithoutMsidCreatesDefaultStream) { ASSERT_EQ(1u, observer_.remote_streams()->count()); ASSERT_EQ(1u, remote_stream->GetAudioTracks().size()); EXPECT_EQ("defaulta0", remote_stream->GetAudioTracks()[0]->id()); + EXPECT_EQ(MediaStreamTrackInterface::kLive, + remote_stream->GetAudioTracks()[0]->state()); ASSERT_EQ(1u, remote_stream->GetVideoTracks().size()); EXPECT_EQ("defaultv0", remote_stream->GetVideoTracks()[0]->id()); + EXPECT_EQ(MediaStreamTrackInterface::kLive, + remote_stream->GetVideoTracks()[0]->state()); } // This tests that a default MediaStream is created if a remote session diff --git a/talk/media/webrtc/fakewebrtccall.h b/talk/media/webrtc/fakewebrtccall.h index 3528c7a7b1..ab1e3b6a52 100644 --- a/talk/media/webrtc/fakewebrtccall.h +++ b/talk/media/webrtc/fakewebrtccall.h @@ -89,6 +89,7 @@ class FakeAudioReceiveStream final : public webrtc::AudioReceiveStream { void SetStats(const webrtc::AudioReceiveStream::Stats& stats); int received_packets() const { return received_packets_; } void IncrementReceivedPackets(); + const webrtc::AudioSinkInterface* sink() const { return sink_.get(); } private: // webrtc::ReceiveStream implementation. diff --git a/talk/media/webrtc/webrtcvoiceengine.cc b/talk/media/webrtc/webrtcvoiceengine.cc index 6c07ff4154..c4f5f99c98 100644 --- a/talk/media/webrtc/webrtcvoiceengine.cc +++ b/talk/media/webrtc/webrtcvoiceengine.cc @@ -132,6 +132,16 @@ const int kMaxTelephoneEventCode = 255; const int kMinTelephoneEventDuration = 100; const int kMaxTelephoneEventDuration = 60000; // Actual limit is 2^16 +class ProxySink : public webrtc::AudioSinkInterface { + public: + ProxySink(AudioSinkInterface* sink) : sink_(sink) { RTC_DCHECK(sink); } + + void OnData(const Data& audio) override { sink_->OnData(audio); } + + private: + webrtc::AudioSinkInterface* sink_; +}; + bool ValidateStreamParams(const StreamParams& sp) { if (sp.ssrcs.empty()) { LOG(LS_ERROR) << "No SSRCs in stream parameters: " << sp.ToString(); @@ -2191,6 +2201,11 @@ void WebRtcVoiceMediaChannel::OnPacketReceived( } default_recv_ssrc_ = ssrc; SetOutputVolume(default_recv_ssrc_, default_recv_volume_); + if (default_sink_) { + rtc::scoped_ptr proxy_sink( + new ProxySink(default_sink_.get())); + SetRawAudioSink(default_recv_ssrc_, std::move(proxy_sink)); + } } // Forward packet to Call. If the SSRC is unknown we'll return after this. @@ -2419,7 +2434,17 @@ void WebRtcVoiceMediaChannel::SetRawAudioSink( uint32_t ssrc, rtc::scoped_ptr sink) { RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); - LOG(LS_VERBOSE) << "WebRtcVoiceMediaChannel::SetRawAudioSink"; + LOG(LS_VERBOSE) << "WebRtcVoiceMediaChannel::SetRawAudioSink: ssrc:" << ssrc + << " " << (sink ? "(ptr)" : "NULL"); + if (ssrc == 0) { + if (default_recv_ssrc_ != -1) { + rtc::scoped_ptr proxy_sink( + sink ? new ProxySink(sink.get()) : nullptr); + SetRawAudioSink(default_recv_ssrc_, std::move(proxy_sink)); + } + default_sink_ = std::move(sink); + return; + } const auto it = recv_streams_.find(ssrc); if (it == recv_streams_.end()) { LOG(LS_WARNING) << "SetRawAudioSink: no recv stream" << ssrc; diff --git a/talk/media/webrtc/webrtcvoiceengine.h b/talk/media/webrtc/webrtcvoiceengine.h index ce3bdf3ed7..556d8c080f 100644 --- a/talk/media/webrtc/webrtcvoiceengine.h +++ b/talk/media/webrtc/webrtcvoiceengine.h @@ -272,6 +272,8 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel, int64_t default_recv_ssrc_ = -1; // Volume for unsignalled stream, which may be set before the stream exists. double default_recv_volume_ = 1.0; + // Sink for unsignalled stream, which may be set before the stream exists. + rtc::scoped_ptr default_sink_; // Default SSRC to use for RTCP receiver reports in case of no signaled // send streams. See: https://code.google.com/p/webrtc/issues/detail?id=4740 // and https://code.google.com/p/chromium/issues/detail?id=547661 diff --git a/talk/media/webrtc/webrtcvoiceengine_unittest.cc b/talk/media/webrtc/webrtcvoiceengine_unittest.cc index a62bcb225f..2d272fc6d4 100644 --- a/talk/media/webrtc/webrtcvoiceengine_unittest.cc +++ b/talk/media/webrtc/webrtcvoiceengine_unittest.cc @@ -72,6 +72,11 @@ class FakeVoEWrapper : public cricket::VoEWrapper { }; } // namespace +class FakeAudioSink : public webrtc::AudioSinkInterface { + public: + void OnData(const Data& audio) override {} +}; + class WebRtcVoiceEngineTestFake : public testing::Test { public: WebRtcVoiceEngineTestFake() @@ -125,6 +130,12 @@ class WebRtcVoiceEngineTestFake : public testing::Test { return *send_stream; } + const cricket::FakeAudioReceiveStream& GetRecvStream(uint32_t ssrc) { + const auto* recv_stream = call_.GetAudioReceiveStream(ssrc); + EXPECT_TRUE(recv_stream); + return *recv_stream; + } + const webrtc::AudioSendStream::Config& GetSendStreamConfig(uint32_t ssrc) { const auto* send_stream = call_.GetAudioSendStream(ssrc); EXPECT_TRUE(send_stream); @@ -3105,6 +3116,52 @@ TEST_F(WebRtcVoiceEngineTestFake, AssociateChannelResetUponDeleteChannnel) { EXPECT_EQ(voe_.GetAssociateSendChannel(recv_ch), -1); } +TEST_F(WebRtcVoiceEngineTestFake, SetRawAudioSink) { + EXPECT_TRUE(SetupEngine()); + rtc::scoped_ptr fake_sink_1(new FakeAudioSink()); + rtc::scoped_ptr fake_sink_2(new FakeAudioSink()); + + // Setting the sink before a recv stream exists should do nothing. + channel_->SetRawAudioSink(kSsrc1, std::move(fake_sink_1)); + EXPECT_TRUE( + channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(kSsrc1))); + EXPECT_EQ(nullptr, GetRecvStream(kSsrc1).sink()); + + // Now try actually setting the sink. + channel_->SetRawAudioSink(kSsrc1, std::move(fake_sink_2)); + EXPECT_NE(nullptr, GetRecvStream(kSsrc1).sink()); + + // Now try resetting it. + channel_->SetRawAudioSink(kSsrc1, nullptr); + EXPECT_EQ(nullptr, GetRecvStream(kSsrc1).sink()); +} + +TEST_F(WebRtcVoiceEngineTestFake, SetRawAudioSinkDefaultRecvStream) { + EXPECT_TRUE(SetupEngine()); + rtc::scoped_ptr fake_sink_1(new FakeAudioSink()); + rtc::scoped_ptr fake_sink_2(new FakeAudioSink()); + + // Should be able to set a default sink even when no stream exists. + channel_->SetRawAudioSink(0, std::move(fake_sink_1)); + + // Create default channel and ensure it's assigned the default sink. + DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame)); + EXPECT_NE(nullptr, GetRecvStream(0x01).sink()); + + // Try resetting the default sink. + channel_->SetRawAudioSink(0, nullptr); + EXPECT_EQ(nullptr, GetRecvStream(0x01).sink()); + + // Try setting the default sink while the default stream exists. + channel_->SetRawAudioSink(0, std::move(fake_sink_2)); + EXPECT_NE(nullptr, GetRecvStream(0x01).sink()); + + // If we remove and add a default stream, it should get the same sink. + EXPECT_TRUE(channel_->RemoveRecvStream(0x01)); + DeliverPacket(kPcmuFrame, sizeof(kPcmuFrame)); + EXPECT_NE(nullptr, GetRecvStream(0x01).sink()); +} + // Tests that the library initializes and shuts down properly. TEST(WebRtcVoiceEngineTest, StartupShutdown) { cricket::WebRtcVoiceEngine engine; diff --git a/webrtc/audio_receive_stream.h b/webrtc/audio_receive_stream.h index 8cab094f4b..b14ffb452e 100644 --- a/webrtc/audio_receive_stream.h +++ b/webrtc/audio_receive_stream.h @@ -112,7 +112,7 @@ class AudioReceiveStream : public ReceiveStream { // Sets an audio sink that receives unmixed audio from the receive stream. // Ownership of the sink is passed to the stream and can be used by the // caller to do lifetime management (i.e. when the sink's dtor is called). - // Only one sink can be set and passing a null sink, clears an existing one. + // Only one sink can be set and passing a null sink clears an existing one. // NOTE: Audio must still somehow be pulled through AudioTransport for audio // to stream through this sink. In practice, this happens if mixed audio // is being pulled+rendered and/or if audio is being pulled for the purposes