Reland "Adding a test framework for conference mode application in VoE."
"Adding a test framework for conference mode application in VoE." was wrongly committed and therefore was temporarily reverted. This is to reland. The CL is indifferent from its original version https://review.webrtc.org/46249004/ TBR=phoglund@webrtc.org BUG= Review URL: https://webrtc-codereview.appspot.com/50109005 Cr-Commit-Position: refs/heads/master@{#9290}
This commit is contained in:
parent
a4b7e5e35a
commit
afef4bfd1c
275
webrtc/voice_engine/test/auto_test/fakes/conference_transport.cc
Normal file
275
webrtc/voice_engine/test/auto_test/fakes/conference_transport.cc
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
/*
|
||||||
|
* 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/conference_transport.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "webrtc/base/byteorder.h"
|
||||||
|
#include "webrtc/base/timeutils.h"
|
||||||
|
#include "webrtc/test/testsupport/fileutils.h"
|
||||||
|
#include "webrtc/system_wrappers/interface/sleep.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
static const unsigned int kReflectorSsrc = 0x0000;
|
||||||
|
static const unsigned int kLocalSsrc = 0x0001;
|
||||||
|
static const unsigned int kFirstRemoteSsrc = 0x0002;
|
||||||
|
static const webrtc::CodecInst kCodecInst =
|
||||||
|
{120, "opus", 48000, 960, 2, 64000};
|
||||||
|
|
||||||
|
static unsigned int ParseSsrc(const void* data, size_t len, bool rtcp) {
|
||||||
|
const size_t ssrc_pos = (!rtcp) ? 8 : 4;
|
||||||
|
unsigned int ssrc = 0;
|
||||||
|
if (len >= (ssrc_pos + sizeof(ssrc))) {
|
||||||
|
ssrc = rtc::GetBE32(static_cast<const char*>(data) + ssrc_pos);
|
||||||
|
}
|
||||||
|
return ssrc;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace voetest {
|
||||||
|
|
||||||
|
ConferenceTransport::ConferenceTransport()
|
||||||
|
: pq_crit_(webrtc::CriticalSectionWrapper::CreateCriticalSection()),
|
||||||
|
stream_crit_(webrtc::CriticalSectionWrapper::CreateCriticalSection()),
|
||||||
|
packet_event_(webrtc::EventWrapper::Create()),
|
||||||
|
thread_(webrtc::ThreadWrapper::CreateThread(Run,
|
||||||
|
this,
|
||||||
|
"ConferenceTransport")),
|
||||||
|
rtt_ms_(0),
|
||||||
|
stream_count_(0) {
|
||||||
|
local_voe_ = webrtc::VoiceEngine::Create();
|
||||||
|
local_base_ = webrtc::VoEBase::GetInterface(local_voe_);
|
||||||
|
local_network_ = webrtc::VoENetwork::GetInterface(local_voe_);
|
||||||
|
local_rtp_rtcp_ = webrtc::VoERTP_RTCP::GetInterface(local_voe_);
|
||||||
|
|
||||||
|
// In principle, we can use one VoiceEngine to achieve the same goal. Well, in
|
||||||
|
// here, we use two engines to make it more like reality.
|
||||||
|
remote_voe_ = webrtc::VoiceEngine::Create();
|
||||||
|
remote_base_ = webrtc::VoEBase::GetInterface(remote_voe_);
|
||||||
|
remote_codec_ = webrtc::VoECodec::GetInterface(remote_voe_);
|
||||||
|
remote_network_ = webrtc::VoENetwork::GetInterface(remote_voe_);
|
||||||
|
remote_rtp_rtcp_ = webrtc::VoERTP_RTCP::GetInterface(remote_voe_);
|
||||||
|
remote_file_ = webrtc::VoEFile::GetInterface(remote_voe_);
|
||||||
|
|
||||||
|
EXPECT_EQ(0, local_base_->Init());
|
||||||
|
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_base_->StartSend(local_sender_));
|
||||||
|
|
||||||
|
EXPECT_EQ(0, remote_base_->Init());
|
||||||
|
reflector_ = remote_base_->CreateChannel();
|
||||||
|
EXPECT_EQ(0, remote_network_->RegisterExternalTransport(reflector_, *this));
|
||||||
|
EXPECT_EQ(0, remote_rtp_rtcp_->SetLocalSSRC(reflector_, kReflectorSsrc));
|
||||||
|
|
||||||
|
thread_->Start();
|
||||||
|
thread_->SetPriority(webrtc::kHighPriority);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConferenceTransport::~ConferenceTransport() {
|
||||||
|
// Must stop sending, otherwise DispatchPackets() cannot quit.
|
||||||
|
EXPECT_EQ(0, remote_network_->DeRegisterExternalTransport(reflector_));
|
||||||
|
EXPECT_EQ(0, local_network_->DeRegisterExternalTransport(local_sender_));
|
||||||
|
|
||||||
|
while (!streams_.empty()) {
|
||||||
|
auto stream = streams_.begin();
|
||||||
|
RemoveStream(stream->first);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_TRUE(thread_->Stop());
|
||||||
|
|
||||||
|
remote_file_->Release();
|
||||||
|
remote_rtp_rtcp_->Release();
|
||||||
|
remote_network_->Release();
|
||||||
|
remote_base_->Release();
|
||||||
|
|
||||||
|
local_rtp_rtcp_->Release();
|
||||||
|
local_network_->Release();
|
||||||
|
local_base_->Release();
|
||||||
|
|
||||||
|
EXPECT_TRUE(webrtc::VoiceEngine::Delete(remote_voe_));
|
||||||
|
EXPECT_TRUE(webrtc::VoiceEngine::Delete(local_voe_));
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConferenceTransport::SendPacket(int channel, const void* data, size_t len) {
|
||||||
|
StorePacket(Packet::Rtp, channel, data, len);
|
||||||
|
return static_cast<int>(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConferenceTransport::SendRTCPPacket(int channel, const void* data,
|
||||||
|
size_t len) {
|
||||||
|
StorePacket(Packet::Rtcp, channel, data, len);
|
||||||
|
return static_cast<int>(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConferenceTransport::GetReceiverChannelForSsrc(unsigned int sender_ssrc)
|
||||||
|
const {
|
||||||
|
webrtc::CriticalSectionScoped lock(stream_crit_.get());
|
||||||
|
auto it = streams_.find(sender_ssrc);
|
||||||
|
if (it != streams_.end()) {
|
||||||
|
return it->second.second;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConferenceTransport::StorePacket(Packet::Type type, int channel,
|
||||||
|
const void* data, size_t len) {
|
||||||
|
{
|
||||||
|
webrtc::CriticalSectionScoped lock(pq_crit_.get());
|
||||||
|
packet_queue_.push_back(Packet(type, channel, data, len, rtc::Time()));
|
||||||
|
}
|
||||||
|
packet_event_->Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This simulates the flow of RTP and RTCP packets. Complications like that
|
||||||
|
// 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;
|
||||||
|
int destination = -1;
|
||||||
|
switch (packet.type_) {
|
||||||
|
case Packet::Rtp:
|
||||||
|
sender_ssrc = ParseSsrc(packet.data_, packet.len_, false);
|
||||||
|
if (sender_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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Packet::Rtcp:
|
||||||
|
sender_ssrc = ParseSsrc(packet.data_, packet.len_, true);
|
||||||
|
if (sender_ssrc == kLocalSsrc) {
|
||||||
|
remote_network_->ReceivedRTCPPacket(reflector_, packet.data_,
|
||||||
|
packet.len_);
|
||||||
|
} else if (sender_ssrc == kReflectorSsrc) {
|
||||||
|
local_network_->ReceivedRTCPPacket(local_sender_, packet.data_,
|
||||||
|
packet.len_);
|
||||||
|
} else {
|
||||||
|
destination = GetReceiverChannelForSsrc(sender_ssrc);
|
||||||
|
if (destination != -1) {
|
||||||
|
local_network_->ReceivedRTCPPacket(destination, packet.data_,
|
||||||
|
packet.len_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConferenceTransport::DispatchPackets() {
|
||||||
|
switch (packet_event_->Wait(1000)) {
|
||||||
|
case webrtc::kEventSignaled:
|
||||||
|
break;
|
||||||
|
case webrtc::kEventTimeout:
|
||||||
|
return true;
|
||||||
|
case webrtc::kEventError:
|
||||||
|
ADD_FAILURE() << "kEventError encountered.";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
Packet packet;
|
||||||
|
{
|
||||||
|
webrtc::CriticalSectionScoped lock(pq_crit_.get());
|
||||||
|
if (packet_queue_.empty())
|
||||||
|
break;
|
||||||
|
packet = packet_queue_.front();
|
||||||
|
packet_queue_.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 elapsed_time_ms = rtc::TimeSince(packet.send_time_ms_);
|
||||||
|
int32 sleep_ms = rtt_ms_ / 2 - elapsed_time_ms;
|
||||||
|
if (sleep_ms > 0) {
|
||||||
|
// Every packet should be delayed by half of RTT.
|
||||||
|
webrtc::SleepMs(sleep_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
SendPacket(packet);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
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_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));
|
||||||
|
|
||||||
|
const int new_receiver = local_base_->CreateChannel();
|
||||||
|
EXPECT_EQ(0, local_base_->AssociateSendChannel(new_receiver, local_sender_));
|
||||||
|
|
||||||
|
EXPECT_EQ(0, local_network_->RegisterExternalTransport(new_receiver, *this));
|
||||||
|
// Receive channels have to have the same SSRC in order to send receiver
|
||||||
|
// reports with this SSRC.
|
||||||
|
EXPECT_EQ(0, local_rtp_rtcp_->SetLocalSSRC(new_receiver, kLocalSsrc));
|
||||||
|
|
||||||
|
{
|
||||||
|
webrtc::CriticalSectionScoped lock(stream_crit_.get());
|
||||||
|
streams_[remote_ssrc] = std::make_pair(new_sender, new_receiver);
|
||||||
|
}
|
||||||
|
return remote_ssrc; // remote ssrc used as stream id.
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConferenceTransport::RemoveStream(unsigned int id) {
|
||||||
|
webrtc::CriticalSectionScoped lock(stream_crit_.get());
|
||||||
|
auto it = streams_.find(id);
|
||||||
|
if (it == streams_.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(0, remote_network_->
|
||||||
|
DeRegisterExternalTransport(it->second.second));
|
||||||
|
EXPECT_EQ(0, local_network_->
|
||||||
|
DeRegisterExternalTransport(it->second.first));
|
||||||
|
EXPECT_EQ(0, remote_base_->DeleteChannel(it->second.second));
|
||||||
|
EXPECT_EQ(0, local_base_->DeleteChannel(it->second.first));
|
||||||
|
streams_.erase(it);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConferenceTransport::StartPlayout(unsigned int id) {
|
||||||
|
int dst = GetReceiverChannelForSsrc(id);
|
||||||
|
if (dst == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(0, local_base_->StartPlayout(dst));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConferenceTransport::GetReceiverStatistics(unsigned int id,
|
||||||
|
webrtc::CallStatistics* stats) {
|
||||||
|
int dst = GetReceiverChannelForSsrc(id);
|
||||||
|
if (dst == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(0, local_rtp_rtcp_->GetRTCPStatistics(dst, *stats));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace voetest
|
158
webrtc/voice_engine/test/auto_test/fakes/conference_transport.h
Normal file
158
webrtc/voice_engine/test/auto_test/fakes/conference_transport.h
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* 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_CONFERENCE_TRANSPORT_H_
|
||||||
|
#define WEBRTC_VOICE_ENGINE_TEST_AUTO_TEST_FAKES_CONFERENCE_TRANSPORT_H_
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
#include <map>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
#include "webrtc/base/basictypes.h"
|
||||||
|
#include "webrtc/base/scoped_ptr.h"
|
||||||
|
#include "webrtc/common_types.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"
|
||||||
|
#include "webrtc/voice_engine/include/voe_base.h"
|
||||||
|
#include "webrtc/voice_engine/include/voe_codec.h"
|
||||||
|
#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"
|
||||||
|
|
||||||
|
|
||||||
|
static const size_t kMaxPacketSizeByte = 1500;
|
||||||
|
|
||||||
|
namespace voetest {
|
||||||
|
|
||||||
|
// This class is to simulate a conference call. There are two Voice Engines, one
|
||||||
|
// for local channels and the other for remote channels. There is a simulated
|
||||||
|
// reflector, which exchanges RTCP with local channels. For simplicity, it
|
||||||
|
// also uses the Voice Engine for remote channels. One can add streams by
|
||||||
|
// calling AddStream(), which creates a remote sender channel and a local
|
||||||
|
// receive channel. The remote sender channel plays a file as microphone in a
|
||||||
|
// looped fashion. Received streams are mixed and played.
|
||||||
|
|
||||||
|
class ConferenceTransport: public webrtc::Transport {
|
||||||
|
public:
|
||||||
|
ConferenceTransport();
|
||||||
|
virtual ~ConferenceTransport();
|
||||||
|
|
||||||
|
/* SetRtt()
|
||||||
|
* Set RTT between local channels and reflector.
|
||||||
|
*
|
||||||
|
* Input:
|
||||||
|
* rtt_ms : RTT in milliseconds.
|
||||||
|
*/
|
||||||
|
void SetRtt(unsigned int rtt_ms);
|
||||||
|
|
||||||
|
/* AddStream()
|
||||||
|
* Adds a stream in the conference.
|
||||||
|
*
|
||||||
|
* Returns stream id.
|
||||||
|
*/
|
||||||
|
unsigned int AddStream();
|
||||||
|
|
||||||
|
/* RemoveStream()
|
||||||
|
* Removes a stream with specified ID from the conference.
|
||||||
|
*
|
||||||
|
* Input:
|
||||||
|
* id : stream id.
|
||||||
|
*
|
||||||
|
* Returns false if the specified stream does not exist, true if succeeds.
|
||||||
|
*/
|
||||||
|
bool RemoveStream(unsigned int id);
|
||||||
|
|
||||||
|
/* StartPlayout()
|
||||||
|
* Starts playing out the stream with specified ID, using the default device.
|
||||||
|
*
|
||||||
|
* Input:
|
||||||
|
* id : stream id.
|
||||||
|
*
|
||||||
|
* Returns false if the specified stream does not exist, true if succeeds.
|
||||||
|
*/
|
||||||
|
bool StartPlayout(unsigned int id);
|
||||||
|
|
||||||
|
/* GetReceiverStatistics()
|
||||||
|
* Gets RTCP statistics of the stream with specified ID.
|
||||||
|
*
|
||||||
|
* Input:
|
||||||
|
* id : stream id;
|
||||||
|
* stats : pointer to a CallStatistics to store the result.
|
||||||
|
*
|
||||||
|
* Returns false if the specified stream does not exist, true if succeeds.
|
||||||
|
*/
|
||||||
|
bool GetReceiverStatistics(unsigned int id, webrtc::CallStatistics* stats);
|
||||||
|
|
||||||
|
// Inherit from class webrtc::Transport.
|
||||||
|
int SendPacket(int channel, const void *data, size_t len) override;
|
||||||
|
int SendRTCPPacket(int channel, const void *data, size_t len) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Packet {
|
||||||
|
enum Type { Rtp, Rtcp, } type_;
|
||||||
|
|
||||||
|
Packet() : len_(0) {}
|
||||||
|
Packet(Type type, int channel, const void* data, size_t len, uint32 time_ms)
|
||||||
|
: type_(type),
|
||||||
|
channel_(channel),
|
||||||
|
len_(len),
|
||||||
|
send_time_ms_(time_ms) {
|
||||||
|
EXPECT_LE(len_, kMaxPacketSizeByte);
|
||||||
|
memcpy(data_, data, len_);
|
||||||
|
}
|
||||||
|
|
||||||
|
int channel_;
|
||||||
|
uint8_t data_[kMaxPacketSizeByte];
|
||||||
|
size_t len_;
|
||||||
|
uint32 send_time_ms_;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool Run(void* transport) {
|
||||||
|
return static_cast<ConferenceTransport*>(transport)->DispatchPackets();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
bool DispatchPackets();
|
||||||
|
|
||||||
|
const rtc::scoped_ptr<webrtc::CriticalSectionWrapper> pq_crit_;
|
||||||
|
const rtc::scoped_ptr<webrtc::CriticalSectionWrapper> stream_crit_;
|
||||||
|
const rtc::scoped_ptr<webrtc::EventWrapper> packet_event_;
|
||||||
|
const rtc::scoped_ptr<webrtc::ThreadWrapper> thread_;
|
||||||
|
|
||||||
|
unsigned int rtt_ms_;
|
||||||
|
unsigned int stream_count_;
|
||||||
|
|
||||||
|
std::map<unsigned int, std::pair<int, int>> streams_
|
||||||
|
GUARDED_BY(stream_crit_.get());
|
||||||
|
std::deque<Packet> packet_queue_ GUARDED_BY(pq_crit_.get());
|
||||||
|
|
||||||
|
int local_sender_; // Channel Id of local sender
|
||||||
|
int reflector_;
|
||||||
|
|
||||||
|
webrtc::VoiceEngine* local_voe_;
|
||||||
|
webrtc::VoEBase* local_base_;
|
||||||
|
webrtc::VoERTP_RTCP* local_rtp_rtcp_;
|
||||||
|
webrtc::VoENetwork* local_network_;
|
||||||
|
|
||||||
|
webrtc::VoiceEngine* remote_voe_;
|
||||||
|
webrtc::VoEBase* remote_base_;
|
||||||
|
webrtc::VoECodec* remote_codec_;
|
||||||
|
webrtc::VoERTP_RTCP* remote_rtp_rtcp_;
|
||||||
|
webrtc::VoENetwork* remote_network_;
|
||||||
|
webrtc::VoEFile* remote_file_;
|
||||||
|
};
|
||||||
|
} // namespace voetest
|
||||||
|
|
||||||
|
#endif // WEBRTC_VOICE_ENGINE_TEST_AUTO_TEST_FAKES_CONFERENCE_TRANSPORT_H_
|
108
webrtc/voice_engine/test/auto_test/voe_conference_test.cc
Normal file
108
webrtc/voice_engine/test/auto_test/voe_conference_test.cc
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* 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 <queue>
|
||||||
|
|
||||||
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
#include "webrtc/base/format_macros.h"
|
||||||
|
#include "webrtc/base/timeutils.h"
|
||||||
|
#include "webrtc/system_wrappers/interface/sleep.h"
|
||||||
|
#include "webrtc/voice_engine/test/auto_test/fakes/conference_transport.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
static const int kRttMs = 25;
|
||||||
|
|
||||||
|
static bool IsNear(int ref, int comp, int error) {
|
||||||
|
return (ref - comp <= error) && (comp - ref >= -error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace voetest {
|
||||||
|
|
||||||
|
TEST(VoeConferenceTest, RttAndStartNtpTime) {
|
||||||
|
struct Stats {
|
||||||
|
Stats(int64_t rtt_receiver_1, int64_t rtt_receiver_2, int64_t ntp_delay)
|
||||||
|
: rtt_receiver_1_(rtt_receiver_1),
|
||||||
|
rtt_receiver_2_(rtt_receiver_2),
|
||||||
|
ntp_delay_(ntp_delay) {
|
||||||
|
}
|
||||||
|
int64_t rtt_receiver_1_;
|
||||||
|
int64_t rtt_receiver_2_;
|
||||||
|
int64_t ntp_delay_;
|
||||||
|
};
|
||||||
|
|
||||||
|
const int kDelayMs = 987;
|
||||||
|
ConferenceTransport trans;
|
||||||
|
trans.SetRtt(kRttMs);
|
||||||
|
|
||||||
|
unsigned int id_1 = trans.AddStream();
|
||||||
|
unsigned int id_2 = trans.AddStream();
|
||||||
|
|
||||||
|
EXPECT_TRUE(trans.StartPlayout(id_1));
|
||||||
|
// Start NTP time is the time when a stream is played out, rather than
|
||||||
|
// when it is added.
|
||||||
|
webrtc::SleepMs(kDelayMs);
|
||||||
|
EXPECT_TRUE(trans.StartPlayout(id_2));
|
||||||
|
|
||||||
|
const int kMaxRunTimeMs = 25000;
|
||||||
|
const int kNeedSuccessivePass = 3;
|
||||||
|
const int kStatsRequestIntervalMs = 1000;
|
||||||
|
const int kStatsBufferSize = 3;
|
||||||
|
|
||||||
|
uint32 deadline = rtc::TimeAfter(kMaxRunTimeMs);
|
||||||
|
// Run the following up to |kMaxRunTimeMs| milliseconds.
|
||||||
|
int successive_pass = 0;
|
||||||
|
webrtc::CallStatistics stats_1;
|
||||||
|
webrtc::CallStatistics stats_2;
|
||||||
|
std::queue<Stats> stats_buffer;
|
||||||
|
|
||||||
|
while (rtc::TimeIsLater(rtc::Time(), deadline) &&
|
||||||
|
successive_pass < kNeedSuccessivePass) {
|
||||||
|
webrtc::SleepMs(kStatsRequestIntervalMs);
|
||||||
|
|
||||||
|
EXPECT_TRUE(trans.GetReceiverStatistics(id_1, &stats_1));
|
||||||
|
EXPECT_TRUE(trans.GetReceiverStatistics(id_2, &stats_2));
|
||||||
|
|
||||||
|
// It is not easy to verify the NTP time directly. We verify it by testing
|
||||||
|
// the difference of two start NTP times.
|
||||||
|
int64_t captured_start_ntp_delay = stats_2.capture_start_ntp_time_ms_ -
|
||||||
|
stats_1.capture_start_ntp_time_ms_;
|
||||||
|
|
||||||
|
// For the checks of RTT and start NTP time, We allow 10% accuracy.
|
||||||
|
if (IsNear(kRttMs, stats_1.rttMs, kRttMs / 10 + 1) &&
|
||||||
|
IsNear(kRttMs, stats_2.rttMs, kRttMs / 10 + 1) &&
|
||||||
|
IsNear(kDelayMs, captured_start_ntp_delay, kDelayMs / 10 + 1)) {
|
||||||
|
successive_pass++;
|
||||||
|
} else {
|
||||||
|
successive_pass = 0;
|
||||||
|
}
|
||||||
|
if (stats_buffer.size() >= kStatsBufferSize) {
|
||||||
|
stats_buffer.pop();
|
||||||
|
}
|
||||||
|
stats_buffer.push(Stats(stats_1.rttMs, stats_2.rttMs,
|
||||||
|
captured_start_ntp_delay));
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_GE(successive_pass, kNeedSuccessivePass) << "Expected to get RTT and"
|
||||||
|
" start NTP time estimate within 10% of the correct value over "
|
||||||
|
<< kStatsRequestIntervalMs * kNeedSuccessivePass / 1000
|
||||||
|
<< " seconds.";
|
||||||
|
if (successive_pass < kNeedSuccessivePass) {
|
||||||
|
printf("The most recent values (RTT for receiver 1, RTT for receiver 2, "
|
||||||
|
"NTP delay between receiver 1 and 2) are (from oldest):\n");
|
||||||
|
while (!stats_buffer.empty()) {
|
||||||
|
Stats stats = stats_buffer.front();
|
||||||
|
printf("(%" PRId64 ", %" PRId64 ", %" PRId64 ")\n", stats.rtt_receiver_1_,
|
||||||
|
stats.rtt_receiver_2_, stats.ntp_delay_);
|
||||||
|
stats_buffer.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace voetest
|
@ -158,6 +158,8 @@
|
|||||||
'test/auto_test/automated_mode.cc',
|
'test/auto_test/automated_mode.cc',
|
||||||
'test/auto_test/extended/agc_config_test.cc',
|
'test/auto_test/extended/agc_config_test.cc',
|
||||||
'test/auto_test/extended/ec_metrics_test.cc',
|
'test/auto_test/extended/ec_metrics_test.cc',
|
||||||
|
'test/auto_test/fakes/conference_transport.cc',
|
||||||
|
'test/auto_test/fakes/conference_transport.h',
|
||||||
'test/auto_test/fakes/fake_external_transport.cc',
|
'test/auto_test/fakes/fake_external_transport.cc',
|
||||||
'test/auto_test/fakes/fake_external_transport.h',
|
'test/auto_test/fakes/fake_external_transport.h',
|
||||||
'test/auto_test/fixtures/after_initialization_fixture.cc',
|
'test/auto_test/fixtures/after_initialization_fixture.cc',
|
||||||
@ -187,6 +189,7 @@
|
|||||||
'test/auto_test/standard/video_sync_test.cc',
|
'test/auto_test/standard/video_sync_test.cc',
|
||||||
'test/auto_test/standard/volume_test.cc',
|
'test/auto_test/standard/volume_test.cc',
|
||||||
'test/auto_test/resource_manager.cc',
|
'test/auto_test/resource_manager.cc',
|
||||||
|
'test/auto_test/voe_conference_test.cc',
|
||||||
'test/auto_test/voe_cpu_test.cc',
|
'test/auto_test/voe_cpu_test.cc',
|
||||||
'test/auto_test/voe_cpu_test.h',
|
'test/auto_test/voe_cpu_test.h',
|
||||||
'test/auto_test/voe_standard_test.cc',
|
'test/auto_test/voe_standard_test.cc',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user