Add LoudestFilter in ConferenceTransport
BUG= Review URL: https://codereview.webrtc.org/1236793003 Cr-Commit-Position: refs/heads/master@{#9712}
This commit is contained in:
@ -14,7 +14,6 @@
|
||||
|
||||
#include "webrtc/base/byteorder.h"
|
||||
#include "webrtc/base/timeutils.h"
|
||||
#include "webrtc/test/testsupport/fileutils.h"
|
||||
#include "webrtc/system_wrappers/interface/sleep.h"
|
||||
|
||||
namespace {
|
||||
@ -23,9 +22,10 @@ namespace {
|
||||
static const unsigned int kFirstRemoteSsrc = 0x0002;
|
||||
static const webrtc::CodecInst kCodecInst =
|
||||
{120, "opus", 48000, 960, 2, 64000};
|
||||
static const int kAudioLevelHeaderId = 1;
|
||||
|
||||
static unsigned int ParseSsrc(const void* data, size_t len, bool rtcp) {
|
||||
const size_t ssrc_pos = (!rtcp) ? 8 : 4;
|
||||
static unsigned int ParseRtcpSsrc(const void* data, size_t len) {
|
||||
const size_t ssrc_pos = 4;
|
||||
unsigned int ssrc = 0;
|
||||
if (len >= (ssrc_pos + sizeof(ssrc))) {
|
||||
ssrc = rtc::GetBE32(static_cast<const char*>(data) + ssrc_pos);
|
||||
@ -44,7 +44,12 @@ ConferenceTransport::ConferenceTransport()
|
||||
this,
|
||||
"ConferenceTransport")),
|
||||
rtt_ms_(0),
|
||||
stream_count_(0) {
|
||||
stream_count_(0),
|
||||
rtp_header_parser_(webrtc::RtpHeaderParser::Create()) {
|
||||
rtp_header_parser_->
|
||||
RegisterRtpHeaderExtension(webrtc::kRtpExtensionAudioLevel,
|
||||
kAudioLevelHeaderId);
|
||||
|
||||
local_voe_ = webrtc::VoiceEngine::Create();
|
||||
local_base_ = webrtc::VoEBase::GetInterface(local_voe_);
|
||||
local_network_ = webrtc::VoENetwork::GetInterface(local_voe_);
|
||||
@ -63,6 +68,10 @@ ConferenceTransport::ConferenceTransport()
|
||||
local_sender_ = local_base_->CreateChannel();
|
||||
EXPECT_EQ(0, local_network_->RegisterExternalTransport(local_sender_, *this));
|
||||
EXPECT_EQ(0, local_rtp_rtcp_->SetLocalSSRC(local_sender_, kLocalSsrc));
|
||||
EXPECT_EQ(0, local_rtp_rtcp_->
|
||||
SetSendAudioLevelIndicationStatus(local_sender_, true,
|
||||
kAudioLevelHeaderId));
|
||||
|
||||
EXPECT_EQ(0, local_base_->StartSend(local_sender_));
|
||||
|
||||
EXPECT_EQ(0, remote_base_->Init());
|
||||
@ -133,26 +142,30 @@ void ConferenceTransport::StorePacket(Packet::Type type, int channel,
|
||||
// a packet is first sent to the reflector, and then forwarded to the receiver
|
||||
// are simplified, in this particular case, to a direct link between the sender
|
||||
// and the receiver.
|
||||
void ConferenceTransport::SendPacket(const Packet& packet) const {
|
||||
unsigned int sender_ssrc;
|
||||
void ConferenceTransport::SendPacket(const Packet& packet) {
|
||||
int destination = -1;
|
||||
|
||||
switch (packet.type_) {
|
||||
case Packet::Rtp:
|
||||
sender_ssrc = ParseSsrc(packet.data_, packet.len_, false);
|
||||
if (sender_ssrc == kLocalSsrc) {
|
||||
case Packet::Rtp: {
|
||||
webrtc::RTPHeader rtp_header;
|
||||
rtp_header_parser_->Parse(packet.data_, packet.len_, &rtp_header);
|
||||
if (rtp_header.ssrc == kLocalSsrc) {
|
||||
remote_network_->ReceivedRTPPacket(reflector_, packet.data_,
|
||||
packet.len_, webrtc::PacketTime());
|
||||
} else {
|
||||
destination = GetReceiverChannelForSsrc(sender_ssrc);
|
||||
if (destination != -1) {
|
||||
local_network_->ReceivedRTPPacket(destination, packet.data_,
|
||||
packet.len_,
|
||||
webrtc::PacketTime());
|
||||
if (loudest_filter_.ForwardThisPacket(rtp_header)) {
|
||||
destination = GetReceiverChannelForSsrc(rtp_header.ssrc);
|
||||
if (destination != -1) {
|
||||
local_network_->ReceivedRTPPacket(destination, packet.data_,
|
||||
packet.len_,
|
||||
webrtc::PacketTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Packet::Rtcp:
|
||||
sender_ssrc = ParseSsrc(packet.data_, packet.len_, true);
|
||||
}
|
||||
case Packet::Rtcp: {
|
||||
unsigned int sender_ssrc = ParseRtcpSsrc(packet.data_, packet.len_);
|
||||
if (sender_ssrc == kLocalSsrc) {
|
||||
remote_network_->ReceivedRTCPPacket(reflector_, packet.data_,
|
||||
packet.len_);
|
||||
@ -167,6 +180,7 @@ void ConferenceTransport::SendPacket(const Packet& packet) const {
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,21 +221,20 @@ void ConferenceTransport::SetRtt(unsigned int rtt_ms) {
|
||||
rtt_ms_ = rtt_ms;
|
||||
}
|
||||
|
||||
unsigned int ConferenceTransport::AddStream() {
|
||||
const std::string kInputFileName =
|
||||
webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
|
||||
|
||||
unsigned int ConferenceTransport::AddStream(std::string file_name,
|
||||
webrtc::FileFormats format) {
|
||||
const int new_sender = remote_base_->CreateChannel();
|
||||
EXPECT_EQ(0, remote_network_->RegisterExternalTransport(new_sender, *this));
|
||||
|
||||
const unsigned int remote_ssrc = kFirstRemoteSsrc + stream_count_++;
|
||||
EXPECT_EQ(0, remote_rtp_rtcp_->SetLocalSSRC(new_sender, remote_ssrc));
|
||||
EXPECT_EQ(0, remote_rtp_rtcp_->
|
||||
SetSendAudioLevelIndicationStatus(new_sender, true, kAudioLevelHeaderId));
|
||||
|
||||
EXPECT_EQ(0, remote_codec_->SetSendCodec(new_sender, kCodecInst));
|
||||
EXPECT_EQ(0, remote_base_->StartSend(new_sender));
|
||||
EXPECT_EQ(0, remote_file_->StartPlayingFileAsMicrophone(
|
||||
new_sender, kInputFileName.c_str(), true, false,
|
||||
webrtc::kFileFormatPcm32kHzFile, 1.0));
|
||||
new_sender, file_name.c_str(), true, false, format, 1.0));
|
||||
|
||||
const int new_receiver = local_base_->CreateChannel();
|
||||
EXPECT_EQ(0, local_base_->AssociateSendChannel(new_receiver, local_sender_));
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
#include "webrtc/base/basictypes.h"
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/common_types.h"
|
||||
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
|
||||
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
||||
#include "webrtc/system_wrappers/interface/event_wrapper.h"
|
||||
#include "webrtc/system_wrappers/interface/thread_wrapper.h"
|
||||
@ -27,7 +28,7 @@
|
||||
#include "webrtc/voice_engine/include/voe_file.h"
|
||||
#include "webrtc/voice_engine/include/voe_network.h"
|
||||
#include "webrtc/voice_engine/include/voe_rtp_rtcp.h"
|
||||
|
||||
#include "webrtc/voice_engine/test/auto_test/fakes/loudest_filter.h"
|
||||
|
||||
static const size_t kMaxPacketSizeByte = 1500;
|
||||
|
||||
@ -57,9 +58,13 @@ class ConferenceTransport: public webrtc::Transport {
|
||||
/* AddStream()
|
||||
* Adds a stream in the conference.
|
||||
*
|
||||
* Input:
|
||||
* file_name : name of the file to be added as microphone input.
|
||||
* format : format of the input file.
|
||||
*
|
||||
* Returns stream id.
|
||||
*/
|
||||
unsigned int AddStream();
|
||||
unsigned int AddStream(std::string file_name, webrtc::FileFormats format);
|
||||
|
||||
/* RemoveStream()
|
||||
* Removes a stream with specified ID from the conference.
|
||||
@ -123,7 +128,7 @@ class ConferenceTransport: public webrtc::Transport {
|
||||
int GetReceiverChannelForSsrc(unsigned int sender_ssrc) const;
|
||||
void StorePacket(Packet::Type type, int channel, const void* data,
|
||||
size_t len);
|
||||
void SendPacket(const Packet& packet) const;
|
||||
void SendPacket(const Packet& packet);
|
||||
bool DispatchPackets();
|
||||
|
||||
const rtc::scoped_ptr<webrtc::CriticalSectionWrapper> pq_crit_;
|
||||
@ -152,6 +157,10 @@ class ConferenceTransport: public webrtc::Transport {
|
||||
webrtc::VoERTP_RTCP* remote_rtp_rtcp_;
|
||||
webrtc::VoENetwork* remote_network_;
|
||||
webrtc::VoEFile* remote_file_;
|
||||
|
||||
LoudestFilter loudest_filter_;
|
||||
|
||||
const rtc::scoped_ptr<webrtc::RtpHeaderParser> rtp_header_parser_;
|
||||
};
|
||||
} // namespace voetest
|
||||
|
||||
|
||||
82
webrtc/voice_engine/test/auto_test/fakes/loudest_filter.cc
Normal file
82
webrtc/voice_engine/test/auto_test/fakes/loudest_filter.cc
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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/voice_engine/test/auto_test/fakes/loudest_filter.h"
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
|
||||
namespace voetest {
|
||||
|
||||
void LoudestFilter::RemoveTimeoutStreams(uint32 time_ms) {
|
||||
auto it = stream_levels_.begin();
|
||||
while (it != stream_levels_.end()) {
|
||||
if (rtc::TimeDiff(time_ms, it->second.last_time_ms) >
|
||||
kStreamTimeOutMs) {
|
||||
stream_levels_.erase(it++);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int LoudestFilter::FindQuietestStream() {
|
||||
int quietest_level = kInvalidAudioLevel;
|
||||
unsigned int quietest_ssrc = 0;
|
||||
for (auto stream : stream_levels_) {
|
||||
// A smaller value if audio level corresponds to a louder sound.
|
||||
if (quietest_level == kInvalidAudioLevel ||
|
||||
stream.second.audio_level > quietest_level) {
|
||||
quietest_level = stream.second.audio_level;
|
||||
quietest_ssrc = stream.first;
|
||||
}
|
||||
}
|
||||
return quietest_ssrc;
|
||||
}
|
||||
|
||||
bool LoudestFilter::ForwardThisPacket(const webrtc::RTPHeader& rtp_header) {
|
||||
uint32 time_now_ms = rtc::Time();
|
||||
RemoveTimeoutStreams(time_now_ms);
|
||||
|
||||
int source_ssrc = rtp_header.ssrc;
|
||||
int audio_level = rtp_header.extension.hasAudioLevel ?
|
||||
rtp_header.extension.audioLevel : kInvalidAudioLevel;
|
||||
|
||||
if (audio_level == kInvalidAudioLevel) {
|
||||
// Always forward streams with unknown audio level, and don't keep their
|
||||
// states.
|
||||
return true;
|
||||
}
|
||||
|
||||
auto it = stream_levels_.find(source_ssrc);
|
||||
if (it != stream_levels_.end()) {
|
||||
// Stream has been forwarded. Update and continue to forward.
|
||||
it->second.audio_level = audio_level;
|
||||
it->second.last_time_ms = time_now_ms;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (stream_levels_.size() < kMaxMixSize) {
|
||||
stream_levels_[source_ssrc].Set(audio_level, time_now_ms);
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int quietest_ssrc = FindQuietestStream();
|
||||
CHECK_NE(0u, quietest_ssrc);
|
||||
// A smaller value if audio level corresponds to a louder sound.
|
||||
if (audio_level < stream_levels_[quietest_ssrc].audio_level) {
|
||||
stream_levels_.erase(quietest_ssrc);
|
||||
stream_levels_[source_ssrc].Set(audio_level, time_now_ms);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace voetest
|
||||
|
||||
54
webrtc/voice_engine/test/auto_test/fakes/loudest_filter.h
Normal file
54
webrtc/voice_engine/test/auto_test/fakes/loudest_filter.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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_VOICE_ENGINE_TEST_AUTO_TEST_FAKES_LOUDEST_FILTER_H_
|
||||
#define WEBRTC_VOICE_ENGINE_TEST_AUTO_TEST_FAKES_LOUDEST_FILTER_H_
|
||||
|
||||
#include <map>
|
||||
#include "webrtc/base/timeutils.h"
|
||||
#include "webrtc/common_types.h"
|
||||
|
||||
namespace voetest {
|
||||
|
||||
class LoudestFilter {
|
||||
public:
|
||||
/* ForwardThisPacket()
|
||||
* Decide whether to forward a RTP packet, given its header.
|
||||
*
|
||||
* Input:
|
||||
* rtp_header : Header of the RTP packet of interest.
|
||||
*/
|
||||
bool ForwardThisPacket(const webrtc::RTPHeader& rtp_header);
|
||||
|
||||
private:
|
||||
struct Status {
|
||||
void Set(int audio_level, uint32 last_time_ms) {
|
||||
this->audio_level = audio_level;
|
||||
this->last_time_ms = last_time_ms;
|
||||
}
|
||||
int audio_level;
|
||||
uint32 last_time_ms;
|
||||
};
|
||||
|
||||
void RemoveTimeoutStreams(uint32 time_ms);
|
||||
unsigned int FindQuietestStream();
|
||||
|
||||
// Keeps the streams being forwarded in pair<SSRC, Status>.
|
||||
std::map<unsigned int, Status> stream_levels_;
|
||||
|
||||
const int32 kStreamTimeOutMs = 5000;
|
||||
const size_t kMaxMixSize = 3;
|
||||
const int kInvalidAudioLevel = 128;
|
||||
};
|
||||
|
||||
|
||||
} // namespace voetest
|
||||
|
||||
#endif // WEBRTC_VOICE_ENGINE_TEST_AUTO_TEST_FAKES_LOUDEST_FILTER_H_
|
||||
@ -14,16 +14,28 @@
|
||||
#include "webrtc/base/format_macros.h"
|
||||
#include "webrtc/base/timeutils.h"
|
||||
#include "webrtc/system_wrappers/interface/sleep.h"
|
||||
#include "webrtc/test/testsupport/fileutils.h"
|
||||
#include "webrtc/voice_engine/test/auto_test/fakes/conference_transport.h"
|
||||
|
||||
namespace {
|
||||
static const int kRttMs = 25;
|
||||
const int kRttMs = 25;
|
||||
|
||||
static bool IsNear(int ref, int comp, int error) {
|
||||
return (ref - comp <= error) && (comp - ref >= -error);
|
||||
}
|
||||
bool IsNear(int ref, int comp, int error) {
|
||||
return (ref - comp <= error) && (comp - ref >= -error);
|
||||
}
|
||||
|
||||
void CreateSilenceFile(const std::string& silence_file, int sample_rate_hz) {
|
||||
FILE* fid = fopen(silence_file.c_str(), "wb");
|
||||
int16_t zero = 0;
|
||||
for (int i = 0; i < sample_rate_hz; ++i) {
|
||||
// Write 1 second, but it does not matter since the file will be looped.
|
||||
fwrite(&zero, sizeof(int16_t), 1, fid);
|
||||
}
|
||||
fclose(fid);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace voetest {
|
||||
|
||||
TEST(VoeConferenceTest, RttAndStartNtpTime) {
|
||||
@ -38,12 +50,16 @@ TEST(VoeConferenceTest, RttAndStartNtpTime) {
|
||||
int64_t ntp_delay_;
|
||||
};
|
||||
|
||||
const std::string input_file =
|
||||
webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
|
||||
const webrtc::FileFormats kInputFormat = webrtc::kFileFormatPcm32kHzFile;
|
||||
|
||||
const int kDelayMs = 987;
|
||||
ConferenceTransport trans;
|
||||
trans.SetRtt(kRttMs);
|
||||
|
||||
unsigned int id_1 = trans.AddStream();
|
||||
unsigned int id_2 = trans.AddStream();
|
||||
unsigned int id_1 = trans.AddStream(input_file, kInputFormat);
|
||||
unsigned int id_2 = trans.AddStream(input_file, kInputFormat);
|
||||
|
||||
EXPECT_TRUE(trans.StartPlayout(id_1));
|
||||
// Start NTP time is the time when a stream is played out, rather than
|
||||
@ -105,4 +121,56 @@ TEST(VoeConferenceTest, RttAndStartNtpTime) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(VoeConferenceTest, ReceivedPackets) {
|
||||
const int kPackets = 50;
|
||||
const int kPacketDurationMs = 20; // Correspond to Opus.
|
||||
|
||||
const std::string input_file =
|
||||
webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
|
||||
const webrtc::FileFormats kInputFormat = webrtc::kFileFormatPcm32kHzFile;
|
||||
|
||||
const std::string silence_file =
|
||||
webrtc::test::TempFilename(webrtc::test::OutputPath(), "silence");
|
||||
CreateSilenceFile(silence_file, 32000);
|
||||
|
||||
{
|
||||
ConferenceTransport trans;
|
||||
// Add silence to stream 0, so that it will be filtered out.
|
||||
unsigned int id_0 = trans.AddStream(silence_file, kInputFormat);
|
||||
unsigned int id_1 = trans.AddStream(input_file, kInputFormat);
|
||||
unsigned int id_2 = trans.AddStream(input_file, kInputFormat);
|
||||
unsigned int id_3 = trans.AddStream(input_file, kInputFormat);
|
||||
|
||||
EXPECT_TRUE(trans.StartPlayout(id_0));
|
||||
EXPECT_TRUE(trans.StartPlayout(id_1));
|
||||
EXPECT_TRUE(trans.StartPlayout(id_2));
|
||||
EXPECT_TRUE(trans.StartPlayout(id_3));
|
||||
|
||||
webrtc::SleepMs(kPacketDurationMs * kPackets);
|
||||
|
||||
webrtc::CallStatistics stats_0;
|
||||
webrtc::CallStatistics stats_1;
|
||||
webrtc::CallStatistics stats_2;
|
||||
webrtc::CallStatistics stats_3;
|
||||
EXPECT_TRUE(trans.GetReceiverStatistics(id_0, &stats_0));
|
||||
EXPECT_TRUE(trans.GetReceiverStatistics(id_1, &stats_1));
|
||||
EXPECT_TRUE(trans.GetReceiverStatistics(id_2, &stats_2));
|
||||
EXPECT_TRUE(trans.GetReceiverStatistics(id_3, &stats_3));
|
||||
|
||||
// We expect stream 0 to be filtered out totally, but since it may join the
|
||||
// call earlier than other streams and the beginning packets might have got
|
||||
// through. So we only expect |packetsReceived| to be close to zero.
|
||||
EXPECT_NEAR(stats_0.packetsReceived, 0, 2);
|
||||
// We expect |packetsReceived| to match |kPackets|, but the actual value
|
||||
// depends on the sleep timer. So we allow a small off from |kPackets|.
|
||||
EXPECT_NEAR(stats_1.packetsReceived, kPackets, 2);
|
||||
EXPECT_NEAR(stats_2.packetsReceived, kPackets, 2);
|
||||
EXPECT_NEAR(stats_3.packetsReceived, kPackets, 2);
|
||||
}
|
||||
|
||||
remove(silence_file.c_str());
|
||||
}
|
||||
|
||||
} // namespace voetest
|
||||
|
||||
@ -162,6 +162,8 @@
|
||||
'test/auto_test/fakes/conference_transport.h',
|
||||
'test/auto_test/fakes/fake_external_transport.cc',
|
||||
'test/auto_test/fakes/fake_external_transport.h',
|
||||
'test/auto_test/fakes/loudest_filter.cc',
|
||||
'test/auto_test/fakes/loudest_filter.h',
|
||||
'test/auto_test/fixtures/after_initialization_fixture.cc',
|
||||
'test/auto_test/fixtures/after_initialization_fixture.h',
|
||||
'test/auto_test/fixtures/after_streaming_fixture.cc',
|
||||
|
||||
Reference in New Issue
Block a user