Add LoudestFilter in ConferenceTransport

BUG=

Review URL: https://codereview.webrtc.org/1236793003

Cr-Commit-Position: refs/heads/master@{#9712}
This commit is contained in:
minyue
2015-08-14 07:33:56 -07:00
committed by Commit bot
parent 4c530dccb3
commit 03bb7c7bfa
6 changed files with 259 additions and 31 deletions

View File

@ -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_));

View File

@ -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

View 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

View 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_

View File

@ -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

View File

@ -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',