From c7a8b08a7cd8d8f37d7f5fb9930d0cdc74baba35 Mon Sep 17 00:00:00 2001 From: solenberg Date: Fri, 16 Oct 2015 14:35:07 -0700 Subject: [PATCH] Add webrtc::AudioSendStream and methods on webrtc::Call to create and delete AudioSendStreams. AudioSendStream will be replacing the send side of VoiceEngine channels and associated APIs. Hence, they will be used transform recorded audio into RTP/RTCP packets that can be transmitted to another party, according to given parameters. BUG=webrtc:4690 Review URL: https://codereview.webrtc.org/1397123003 Cr-Commit-Position: refs/heads/master@{#10307} --- webrtc/audio/BUILD.gn | 2 + webrtc/audio/audio_send_stream.cc | 72 ++++++++++++++ webrtc/audio/audio_send_stream.h | 43 +++++++++ webrtc/audio/audio_send_stream_unittest.cc | 34 +++++++ webrtc/audio/webrtc_audio.gypi | 2 + webrtc/audio_send_stream.h | 8 +- webrtc/call/call.cc | 55 ++++++++--- webrtc/call/call_unittest.cc | 104 +++++++++++++++++++++ webrtc/webrtc_tests.gypi | 4 +- 9 files changed, 309 insertions(+), 15 deletions(-) create mode 100644 webrtc/audio/audio_send_stream.cc create mode 100644 webrtc/audio/audio_send_stream.h create mode 100644 webrtc/audio/audio_send_stream_unittest.cc create mode 100644 webrtc/call/call_unittest.cc diff --git a/webrtc/audio/BUILD.gn b/webrtc/audio/BUILD.gn index db0791fc97..c6f4b6bde0 100644 --- a/webrtc/audio/BUILD.gn +++ b/webrtc/audio/BUILD.gn @@ -12,6 +12,8 @@ source_set("audio") { sources = [ "audio_receive_stream.cc", "audio_receive_stream.h", + "audio_send_stream.cc", + "audio_send_stream.h", ] configs += [ "..:common_config" ] diff --git a/webrtc/audio/audio_send_stream.cc b/webrtc/audio/audio_send_stream.cc new file mode 100644 index 0000000000..0d0c072bf4 --- /dev/null +++ b/webrtc/audio/audio_send_stream.cc @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/audio/audio_send_stream.h" + +#include + +#include "webrtc/base/checks.h" +#include "webrtc/base/logging.h" + +namespace webrtc { +std::string AudioSendStream::Config::Rtp::ToString() const { + std::stringstream ss; + ss << "{ssrc: " << ssrc; + ss << ", extensions: ["; + for (size_t i = 0; i < extensions.size(); ++i) { + ss << extensions[i].ToString(); + if (i != extensions.size() - 1) + ss << ", "; + } + ss << ']'; + ss << '}'; + return ss.str(); +} + +std::string AudioSendStream::Config::ToString() const { + std::stringstream ss; + ss << "{rtp: " << rtp.ToString(); + ss << ", voe_channel_id: " << voe_channel_id; + // TODO(solenberg): Encoder config. + ss << ", cng_payload_type: " << cng_payload_type; + ss << ", red_payload_type: " << red_payload_type; + ss << '}'; + return ss.str(); +} + +namespace internal { +AudioSendStream::AudioSendStream(const webrtc::AudioSendStream::Config& config) + : config_(config) { + LOG(LS_INFO) << "AudioSendStream: " << config_.ToString(); + RTC_DCHECK(config.voe_channel_id != -1); +} + +AudioSendStream::~AudioSendStream() { + LOG(LS_INFO) << "~AudioSendStream: " << config_.ToString(); +} + +webrtc::AudioSendStream::Stats AudioSendStream::GetStats() const { + return webrtc::AudioSendStream::Stats(); +} + +void AudioSendStream::Start() { +} + +void AudioSendStream::Stop() { +} + +void AudioSendStream::SignalNetworkState(NetworkState state) { +} + +bool AudioSendStream::DeliverRtcp(const uint8_t* packet, size_t length) { + return false; +} +} // namespace internal +} // namespace webrtc diff --git a/webrtc/audio/audio_send_stream.h b/webrtc/audio/audio_send_stream.h new file mode 100644 index 0000000000..54046fcea9 --- /dev/null +++ b/webrtc/audio/audio_send_stream.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_AUDIO_AUDIO_SEND_STREAM_H_ +#define WEBRTC_AUDIO_AUDIO_SEND_STREAM_H_ + +#include "webrtc/audio_send_stream.h" + +namespace webrtc { +namespace internal { + +class AudioSendStream : public webrtc::AudioSendStream { + public: + explicit AudioSendStream(const webrtc::AudioSendStream::Config& config); + ~AudioSendStream() override; + + // webrtc::SendStream implementation. + void Start() override; + void Stop() override; + void SignalNetworkState(NetworkState state) override; + bool DeliverRtcp(const uint8_t* packet, size_t length) override; + + // webrtc::AudioSendStream implementation. + webrtc::AudioSendStream::Stats GetStats() const override; + + const webrtc::AudioSendStream::Config& config() const { + return config_; + } + + private: + const webrtc::AudioSendStream::Config config_; +}; +} // namespace internal +} // namespace webrtc + +#endif // WEBRTC_AUDIO_AUDIO_SEND_STREAM_H_ diff --git a/webrtc/audio/audio_send_stream_unittest.cc b/webrtc/audio/audio_send_stream_unittest.cc new file mode 100644 index 0000000000..e5d73ff0f2 --- /dev/null +++ b/webrtc/audio/audio_send_stream_unittest.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "testing/gtest/include/gtest/gtest.h" + +#include "webrtc/audio/audio_send_stream.h" + +namespace webrtc { + +TEST(AudioSendStreamTest, ConfigToString) { + const int kAbsSendTimeId = 3; + AudioSendStream::Config config(nullptr); + config.rtp.ssrc = 1234; + config.rtp.extensions.push_back( + RtpExtension(RtpExtension::kAbsSendTime, kAbsSendTimeId)); + config.voe_channel_id = 1; + config.cng_payload_type = 42; + config.red_payload_type = 17; + EXPECT_GT(config.ToString().size(), 0u); +} + +TEST(AudioSendStreamTest, ConstructDestruct) { + AudioSendStream::Config config(nullptr); + config.voe_channel_id = 1; + internal::AudioSendStream send_stream(config); +} +} // namespace webrtc diff --git a/webrtc/audio/webrtc_audio.gypi b/webrtc/audio/webrtc_audio.gypi index 42f91aca16..40ccff6b2a 100644 --- a/webrtc/audio/webrtc_audio.gypi +++ b/webrtc/audio/webrtc_audio.gypi @@ -16,6 +16,8 @@ 'webrtc_audio_sources': [ 'audio/audio_receive_stream.cc', 'audio/audio_receive_stream.h', + 'audio/audio_send_stream.cc', + 'audio/audio_send_stream.h', ], }, } diff --git a/webrtc/audio_send_stream.h b/webrtc/audio_send_stream.h index 2fb288f7ab..b96a8ef988 100644 --- a/webrtc/audio_send_stream.h +++ b/webrtc/audio_send_stream.h @@ -45,7 +45,8 @@ class AudioSendStream : public SendStream { std::vector extensions; } rtp; - // Transport for outgoing packets. + // Transport for outgoing packets. The transport is expected to exist for + // the entire life of the AudioSendStream and is owned by the API client. Transport* send_transport = nullptr; // Underlying VoiceEngine handle, used to map AudioSendStream to lower-level @@ -54,7 +55,10 @@ class AudioSendStream : public SendStream { // of Call. int voe_channel_id = -1; - rtc::scoped_ptr encoder; + // Ownership of the encoder object is transferred to Call when the config is + // passed to Call::CreateAudioSendStream(). + // TODO(solenberg): Implement, once we configure codecs through the new API. + // rtc::scoped_ptr encoder; int cng_payload_type = -1; // pt, or -1 to disable Comfort Noise Generator. int red_payload_type = -1; // pt, or -1 to disable REDundant coding. }; diff --git a/webrtc/call/call.cc b/webrtc/call/call.cc index a32a823943..6d9bf40ae8 100644 --- a/webrtc/call/call.cc +++ b/webrtc/call/call.cc @@ -14,6 +14,7 @@ #include #include "webrtc/audio/audio_receive_stream.h" +#include "webrtc/audio/audio_send_stream.h" #include "webrtc/base/checks.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/base/thread_annotations.h" @@ -103,6 +104,7 @@ class Call : public webrtc::Call, public PacketReceiver { bool network_enabled_ GUARDED_BY(network_enabled_crit_); rtc::scoped_ptr receive_crit_; + // Audio and Video receive streams are owned by the client that creates them. std::map audio_receive_ssrcs_ GUARDED_BY(receive_crit_); std::map video_receive_ssrcs_ @@ -113,6 +115,8 @@ class Call : public webrtc::Call, public PacketReceiver { GUARDED_BY(receive_crit_); rtc::scoped_ptr send_crit_; + // Audio and Video send streams are owned by the client that creates them. + std::map audio_send_ssrcs_ GUARDED_BY(send_crit_); std::map video_send_ssrcs_ GUARDED_BY(send_crit_); std::set video_send_streams_ GUARDED_BY(send_crit_); @@ -164,11 +168,12 @@ Call::Call(const Call::Config& config) } Call::~Call() { - RTC_CHECK_EQ(0u, video_send_ssrcs_.size()); - RTC_CHECK_EQ(0u, video_send_streams_.size()); - RTC_CHECK_EQ(0u, audio_receive_ssrcs_.size()); - RTC_CHECK_EQ(0u, video_receive_ssrcs_.size()); - RTC_CHECK_EQ(0u, video_receive_streams_.size()); + RTC_CHECK(audio_send_ssrcs_.empty()); + RTC_CHECK(video_send_ssrcs_.empty()); + RTC_CHECK(video_send_streams_.empty()); + RTC_CHECK(audio_receive_ssrcs_.empty()); + RTC_CHECK(video_receive_ssrcs_.empty()); + RTC_CHECK(video_receive_streams_.empty()); module_process_thread_->Stop(); Trace::ReturnTrace(); @@ -178,14 +183,36 @@ PacketReceiver* Call::Receiver() { return this; } webrtc::AudioSendStream* Call::CreateAudioSendStream( const webrtc::AudioSendStream::Config& config) { - // TODO(pbos): When adding AudioSendStream, add both TRACE_EVENT0 and config - // logging to AudioSendStream constructor. - return nullptr; + TRACE_EVENT0("webrtc", "Call::CreateAudioSendStream"); + AudioSendStream* send_stream = new AudioSendStream(config); + { + rtc::CritScope lock(&network_enabled_crit_); + WriteLockScoped write_lock(*send_crit_); + RTC_DCHECK(audio_send_ssrcs_.find(config.rtp.ssrc) == + audio_send_ssrcs_.end()); + audio_send_ssrcs_[config.rtp.ssrc] = send_stream; + + if (!network_enabled_) + send_stream->SignalNetworkState(kNetworkDown); + } + return send_stream; } void Call::DestroyAudioSendStream(webrtc::AudioSendStream* send_stream) { - // TODO(pbos): When adding AudioSendStream, add both TRACE_EVENT0 and config - // logging to AudioSendStream destructor. + TRACE_EVENT0("webrtc", "Call::DestroyAudioSendStream"); + RTC_DCHECK(send_stream != nullptr); + + send_stream->Stop(); + + webrtc::internal::AudioSendStream* audio_send_stream = + static_cast(send_stream); + { + WriteLockScoped write_lock(*send_crit_); + size_t num_deleted = audio_send_ssrcs_.erase( + audio_send_stream->config().rtp.ssrc); + RTC_DCHECK(num_deleted == 1); + } + delete audio_send_stream; } webrtc::AudioReceiveStream* Call::CreateAudioReceiveStream( @@ -207,8 +234,8 @@ void Call::DestroyAudioReceiveStream( webrtc::AudioReceiveStream* receive_stream) { TRACE_EVENT0("webrtc", "Call::DestroyAudioReceiveStream"); RTC_DCHECK(receive_stream != nullptr); - AudioReceiveStream* audio_receive_stream = - static_cast(receive_stream); + webrtc::internal::AudioReceiveStream* audio_receive_stream = + static_cast(receive_stream); { WriteLockScoped write_lock(*receive_crit_); size_t num_deleted = audio_receive_ssrcs_.erase( @@ -362,6 +389,7 @@ Call::Stats Call::GetStats() const { stats.pacer_delay_ms = channel_group_->GetPacerQueuingDelayMs(); { ReadLockScoped read_lock(*send_crit_); + // TODO(solenberg): Add audio send streams. for (const auto& kv : video_send_ssrcs_) { int rtt_ms = kv.second->GetRtt(); if (rtt_ms > 0) @@ -401,6 +429,9 @@ void Call::SignalNetworkState(NetworkState state) { channel_group_->SignalNetworkState(state); { ReadLockScoped write_lock(*send_crit_); + for (auto& kv : audio_send_ssrcs_) { + kv.second->SignalNetworkState(state); + } for (auto& kv : video_send_ssrcs_) { kv.second->SignalNetworkState(state); } diff --git a/webrtc/call/call_unittest.cc b/webrtc/call/call_unittest.cc new file mode 100644 index 0000000000..9adecc349b --- /dev/null +++ b/webrtc/call/call_unittest.cc @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "testing/gtest/include/gtest/gtest.h" + +#include "webrtc/call.h" + +namespace { + +struct CallHelper { + CallHelper() { + webrtc::Call::Config config; + // TODO(solenberg): Fill in with VoiceEngine* etc. + call_.reset(webrtc::Call::Create(config)); + } + + webrtc::Call* operator->() { return call_.get(); } + + private: + rtc::scoped_ptr call_; +}; +} // namespace + +namespace webrtc { + +TEST(CallTest, ConstructDestruct) { + CallHelper call; +} + +TEST(CallTest, CreateDestroy_AudioSendStream) { + CallHelper call; + AudioSendStream::Config config(nullptr); + config.rtp.ssrc = 42; + config.voe_channel_id = 123; + AudioSendStream* stream = call->CreateAudioSendStream(config); + EXPECT_NE(stream, nullptr); + call->DestroyAudioSendStream(stream); +} + +TEST(CallTest, CreateDestroy_AudioReceiveStream) { + CallHelper call; + AudioReceiveStream::Config config; + config.rtp.remote_ssrc = 42; + config.voe_channel_id = 123; + AudioReceiveStream* stream = call->CreateAudioReceiveStream(config); + EXPECT_NE(stream, nullptr); + call->DestroyAudioReceiveStream(stream); +} + +TEST(CallTest, CreateDestroy_AudioSendStreams) { + CallHelper call; + AudioSendStream::Config config(nullptr); + config.voe_channel_id = 123; + std::list streams; + for (int i = 0; i < 2; ++i) { + for (uint32_t ssrc = 0; ssrc < 1234567; ssrc += 34567) { + config.rtp.ssrc = ssrc; + AudioSendStream* stream = call->CreateAudioSendStream(config); + EXPECT_NE(stream, nullptr); + if (ssrc & 1) { + streams.push_back(stream); + } else { + streams.push_front(stream); + } + } + for (auto s : streams) { + call->DestroyAudioSendStream(s); + } + streams.clear(); + } +} + +TEST(CallTest, CreateDestroy_AudioReceiveStreams) { + CallHelper call; + AudioReceiveStream::Config config; + config.voe_channel_id = 123; + std::list streams; + for (int i = 0; i < 2; ++i) { + for (uint32_t ssrc = 0; ssrc < 1234567; ssrc += 34567) { + config.rtp.remote_ssrc = ssrc; + AudioReceiveStream* stream = call->CreateAudioReceiveStream(config); + EXPECT_NE(stream, nullptr); + if (ssrc & 1) { + streams.push_back(stream); + } else { + streams.push_front(stream); + } + } + for (auto s : streams) { + call->DestroyAudioReceiveStream(s); + } + streams.clear(); + } +} +} // namespace webrtc diff --git a/webrtc/webrtc_tests.gypi b/webrtc/webrtc_tests.gypi index 08235005dd..0e245c5426 100644 --- a/webrtc/webrtc_tests.gypi +++ b/webrtc/webrtc_tests.gypi @@ -146,12 +146,14 @@ ], }, { - # TODO(pbos): Add separate target webrtc_audio_tests and move files there. + # TODO(solenberg): Rename to webrtc_call_tests. 'target_name': 'video_engine_tests', 'type': '<(gtest_target_type)', 'sources': [ 'audio/audio_receive_stream_unittest.cc', + 'audio/audio_send_stream_unittest.cc', 'call/bitrate_estimator_tests.cc', + 'call/call_unittest.cc', 'call/packet_injection_tests.cc', 'test/common_unittest.cc', 'test/testsupport/metrics/video_metrics_unittest.cc',