blob: 59f119a85d53c898d2b3d35c96413e97674213a4 [file] [log] [blame]
pbos@webrtc.org1d096902013-12-13 12:48:05 +00001/*
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10#include <assert.h>
11
12#include <algorithm>
13#include <sstream>
14#include <string>
15
16#include "testing/gtest/include/gtest/gtest.h"
17
18#include "webrtc/call.h"
henrik.lundin@webrtc.orged865b52014-03-06 10:28:07 +000019#include "webrtc/common.h"
20#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h"
pbos@webrtc.org1d096902013-12-13 12:48:05 +000021#include "webrtc/modules/remote_bitrate_estimator/include/rtp_to_ntp.h"
22#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
23#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
24#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
25#include "webrtc/system_wrappers/interface/scoped_ptr.h"
26#include "webrtc/test/direct_transport.h"
27#include "webrtc/test/fake_audio_device.h"
28#include "webrtc/test/fake_decoder.h"
29#include "webrtc/test/fake_encoder.h"
30#include "webrtc/test/frame_generator.h"
31#include "webrtc/test/frame_generator_capturer.h"
32#include "webrtc/test/rtp_rtcp_observer.h"
33#include "webrtc/test/testsupport/fileutils.h"
34#include "webrtc/test/testsupport/perf_test.h"
35#include "webrtc/video/transport_adapter.h"
36#include "webrtc/voice_engine/include/voe_base.h"
37#include "webrtc/voice_engine/include/voe_codec.h"
38#include "webrtc/voice_engine/include/voe_network.h"
39#include "webrtc/voice_engine/include/voe_rtp_rtcp.h"
40#include "webrtc/voice_engine/include/voe_video_sync.h"
41
42namespace webrtc {
43
44static unsigned int kLongTimeoutMs = 120 * 1000;
45static const uint32_t kSendSsrc = 0x654321;
46static const uint32_t kReceiverLocalSsrc = 0x123456;
47static const uint8_t kSendPayloadType = 125;
48
49class CallPerfTest : public ::testing::Test {
asapersson@webrtc.orgbdc5ed22014-01-31 10:05:07 +000050 public:
51 CallPerfTest()
52 : send_stream_(NULL), fake_encoder_(Clock::GetRealTimeClock()) {}
53 protected:
54 VideoSendStream::Config GetSendTestConfig(Call* call) {
55 VideoSendStream::Config config = call->GetDefaultSendConfig();
56 config.encoder = &fake_encoder_;
57 config.internal_source = false;
58 config.rtp.ssrcs.push_back(kSendSsrc);
59 test::FakeEncoder::SetCodecSettings(&config.codec, 1);
60 config.codec.plType = kSendPayloadType;
61 return config;
62 }
63 void RunVideoSendTest(Call* call,
64 const VideoSendStream::Config& config,
65 test::RtpRtcpObserver* observer) {
66 send_stream_ = call->CreateVideoSendStream(config);
67 scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
68 test::FrameGeneratorCapturer::Create(
69 send_stream_->Input(), 320, 240, 30, Clock::GetRealTimeClock()));
70 send_stream_->StartSending();
71 frame_generator_capturer->Start();
72
73 EXPECT_EQ(kEventSignaled, observer->Wait());
74
75 observer->StopSending();
76 frame_generator_capturer->Stop();
77 send_stream_->StopSending();
78 call->DestroyVideoSendStream(send_stream_);
79 }
80
81 VideoSendStream* send_stream_;
82 test::FakeEncoder fake_encoder_;
pbos@webrtc.org1d096902013-12-13 12:48:05 +000083};
84
85class SyncRtcpObserver : public test::RtpRtcpObserver {
86 public:
stefan@webrtc.orgfaada6e2013-12-18 20:28:25 +000087 explicit SyncRtcpObserver(const FakeNetworkPipe::Config& config)
88 : test::RtpRtcpObserver(kLongTimeoutMs, config),
pbos@webrtc.org1d096902013-12-13 12:48:05 +000089 critical_section_(CriticalSectionWrapper::CreateCriticalSection()) {}
90
91 virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
92 RTCPUtility::RTCPParserV2 parser(packet, length, true);
93 EXPECT_TRUE(parser.IsValid());
94
95 for (RTCPUtility::RTCPPacketTypes packet_type = parser.Begin();
96 packet_type != RTCPUtility::kRtcpNotValidCode;
97 packet_type = parser.Iterate()) {
98 if (packet_type == RTCPUtility::kRtcpSrCode) {
99 const RTCPUtility::RTCPPacket& packet = parser.Packet();
100 synchronization::RtcpMeasurement ntp_rtp_pair(
101 packet.SR.NTPMostSignificant,
102 packet.SR.NTPLeastSignificant,
103 packet.SR.RTPTimestamp);
104 StoreNtpRtpPair(ntp_rtp_pair);
105 }
106 }
107 return SEND_PACKET;
108 }
109
110 int64_t RtpTimestampToNtp(uint32_t timestamp) const {
111 CriticalSectionScoped cs(critical_section_.get());
112 int64_t timestamp_in_ms = -1;
113 if (ntp_rtp_pairs_.size() == 2) {
114 // TODO(stefan): We can't EXPECT_TRUE on this call due to a bug in the
115 // RTCP sender where it sends RTCP SR before any RTP packets, which leads
116 // to a bogus NTP/RTP mapping.
117 synchronization::RtpToNtpMs(timestamp, ntp_rtp_pairs_, &timestamp_in_ms);
118 return timestamp_in_ms;
119 }
120 return -1;
121 }
122
123 private:
124 void StoreNtpRtpPair(synchronization::RtcpMeasurement ntp_rtp_pair) {
125 CriticalSectionScoped cs(critical_section_.get());
126 for (synchronization::RtcpList::iterator it = ntp_rtp_pairs_.begin();
127 it != ntp_rtp_pairs_.end();
128 ++it) {
129 if (ntp_rtp_pair.ntp_secs == it->ntp_secs &&
130 ntp_rtp_pair.ntp_frac == it->ntp_frac) {
131 // This RTCP has already been added to the list.
132 return;
133 }
134 }
135 // We need two RTCP SR reports to map between RTP and NTP. More than two
136 // will not improve the mapping.
137 if (ntp_rtp_pairs_.size() == 2) {
138 ntp_rtp_pairs_.pop_back();
139 }
140 ntp_rtp_pairs_.push_front(ntp_rtp_pair);
141 }
142
143 scoped_ptr<CriticalSectionWrapper> critical_section_;
144 synchronization::RtcpList ntp_rtp_pairs_;
145};
146
147class VideoRtcpAndSyncObserver : public SyncRtcpObserver, public VideoRenderer {
148 static const int kInSyncThresholdMs = 50;
149 static const int kStartupTimeMs = 2000;
150 static const int kMinRunTimeMs = 30000;
151
152 public:
153 VideoRtcpAndSyncObserver(Clock* clock,
154 int voe_channel,
155 VoEVideoSync* voe_sync,
henrik.lundin@webrtc.orged865b52014-03-06 10:28:07 +0000156 SyncRtcpObserver* audio_observer,
157 bool using_new_acm)
stefan@webrtc.orgfaada6e2013-12-18 20:28:25 +0000158 : SyncRtcpObserver(FakeNetworkPipe::Config()),
pbos@webrtc.org1d096902013-12-13 12:48:05 +0000159 clock_(clock),
160 voe_channel_(voe_channel),
161 voe_sync_(voe_sync),
162 audio_observer_(audio_observer),
163 creation_time_ms_(clock_->TimeInMilliseconds()),
henrik.lundin@webrtc.orged865b52014-03-06 10:28:07 +0000164 first_time_in_sync_(-1),
165 using_new_acm_(using_new_acm) {}
pbos@webrtc.org1d096902013-12-13 12:48:05 +0000166
167 virtual void RenderFrame(const I420VideoFrame& video_frame,
168 int time_to_render_ms) OVERRIDE {
169 int64_t now_ms = clock_->TimeInMilliseconds();
170 uint32_t playout_timestamp = 0;
171 if (voe_sync_->GetPlayoutTimestamp(voe_channel_, playout_timestamp) != 0)
172 return;
173 int64_t latest_audio_ntp =
174 audio_observer_->RtpTimestampToNtp(playout_timestamp);
175 int64_t latest_video_ntp = RtpTimestampToNtp(video_frame.timestamp());
176 if (latest_audio_ntp < 0 || latest_video_ntp < 0)
177 return;
178 int time_until_render_ms =
179 std::max(0, static_cast<int>(video_frame.render_time_ms() - now_ms));
180 latest_video_ntp += time_until_render_ms;
181 int64_t stream_offset = latest_audio_ntp - latest_video_ntp;
182 std::stringstream ss;
183 ss << stream_offset;
henrik.lundin@webrtc.orged865b52014-03-06 10:28:07 +0000184 std::stringstream acm_type;
185 if (using_new_acm_) {
186 acm_type << "_acm2";
187 }
188 webrtc::test::PrintResult("stream_offset",
189 acm_type.str(),
190 "synchronization",
191 ss.str(),
192 "ms",
193 false);
pbos@webrtc.org1d096902013-12-13 12:48:05 +0000194 int64_t time_since_creation = now_ms - creation_time_ms_;
195 // During the first couple of seconds audio and video can falsely be
196 // estimated as being synchronized. We don't want to trigger on those.
197 if (time_since_creation < kStartupTimeMs)
198 return;
pbos@webrtc.org0117d1c2014-03-03 16:47:03 +0000199 if (labs(latest_audio_ntp - latest_video_ntp) < kInSyncThresholdMs) {
pbos@webrtc.org1d096902013-12-13 12:48:05 +0000200 if (first_time_in_sync_ == -1) {
201 first_time_in_sync_ = now_ms;
202 webrtc::test::PrintResult("sync_convergence_time",
henrik.lundin@webrtc.orged865b52014-03-06 10:28:07 +0000203 acm_type.str(),
pbos@webrtc.org1d096902013-12-13 12:48:05 +0000204 "synchronization",
205 time_since_creation,
206 "ms",
207 false);
208 }
209 if (time_since_creation > kMinRunTimeMs)
210 observation_complete_->Set();
211 }
212 }
213
214 private:
215 Clock* clock_;
216 int voe_channel_;
217 VoEVideoSync* voe_sync_;
218 SyncRtcpObserver* audio_observer_;
219 int64_t creation_time_ms_;
220 int64_t first_time_in_sync_;
henrik.lundin@webrtc.orged865b52014-03-06 10:28:07 +0000221 bool using_new_acm_;
pbos@webrtc.org1d096902013-12-13 12:48:05 +0000222};
223
henrik.lundin@webrtc.orged865b52014-03-06 10:28:07 +0000224class ParamCallPerfTest : public CallPerfTest,
225 public ::testing::WithParamInterface<bool> {
226 public:
227 ParamCallPerfTest() : CallPerfTest(), use_new_acm_(GetParam()) {}
228
229 protected:
230 bool use_new_acm_;
231};
232
233TEST_P(ParamCallPerfTest, PlaysOutAudioAndVideoInSync) {
pbos@webrtc.org1d096902013-12-13 12:48:05 +0000234 VoiceEngine* voice_engine = VoiceEngine::Create();
235 VoEBase* voe_base = VoEBase::GetInterface(voice_engine);
236 VoECodec* voe_codec = VoECodec::GetInterface(voice_engine);
237 VoENetwork* voe_network = VoENetwork::GetInterface(voice_engine);
238 VoEVideoSync* voe_sync = VoEVideoSync::GetInterface(voice_engine);
239 const std::string audio_filename =
240 test::ResourcePath("voice_engine/audio_long16", "pcm");
241 ASSERT_STRNE("", audio_filename.c_str());
242 test::FakeAudioDevice fake_audio_device(Clock::GetRealTimeClock(),
243 audio_filename);
244 EXPECT_EQ(0, voe_base->Init(&fake_audio_device, NULL));
henrik.lundin@webrtc.orged865b52014-03-06 10:28:07 +0000245 Config config;
246 if (use_new_acm_) {
247 config.Set<webrtc::AudioCodingModuleFactory>(
248 new webrtc::NewAudioCodingModuleFactory());
249 } else {
250 config.Set<webrtc::AudioCodingModuleFactory>(
251 new webrtc::AudioCodingModuleFactory());
252 }
253 int channel = voe_base->CreateChannel(config);
pbos@webrtc.org1d096902013-12-13 12:48:05 +0000254
stefan@webrtc.orgfaada6e2013-12-18 20:28:25 +0000255 FakeNetworkPipe::Config net_config;
256 net_config.queue_delay_ms = 500;
257 SyncRtcpObserver audio_observer(net_config);
henrik.lundin@webrtc.orged865b52014-03-06 10:28:07 +0000258 VideoRtcpAndSyncObserver observer(Clock::GetRealTimeClock(),
259 channel,
260 voe_sync,
261 &audio_observer,
262 use_new_acm_);
pbos@webrtc.org1d096902013-12-13 12:48:05 +0000263
264 Call::Config receiver_config(observer.ReceiveTransport());
265 receiver_config.voice_engine = voice_engine;
266 scoped_ptr<Call> sender_call(
267 Call::Create(Call::Config(observer.SendTransport())));
268 scoped_ptr<Call> receiver_call(Call::Create(receiver_config));
269 CodecInst isac = {103, "ISAC", 16000, 480, 1, 32000};
270 EXPECT_EQ(0, voe_codec->SetSendCodec(channel, isac));
271
272 class VoicePacketReceiver : public PacketReceiver {
273 public:
274 VoicePacketReceiver(int channel, VoENetwork* voe_network)
275 : channel_(channel),
276 voe_network_(voe_network),
277 parser_(RtpHeaderParser::Create()) {}
278 virtual bool DeliverPacket(const uint8_t* packet, size_t length) {
279 int ret;
280 if (parser_->IsRtcp(packet, static_cast<int>(length))) {
281 ret = voe_network_->ReceivedRTCPPacket(
282 channel_, packet, static_cast<unsigned int>(length));
283 } else {
284 ret = voe_network_->ReceivedRTPPacket(
285 channel_, packet, static_cast<unsigned int>(length));
286 }
287 return ret == 0;
288 }
289
290 private:
291 int channel_;
292 VoENetwork* voe_network_;
293 scoped_ptr<RtpHeaderParser> parser_;
294 } voe_packet_receiver(channel, voe_network);
295
296 audio_observer.SetReceivers(&voe_packet_receiver, &voe_packet_receiver);
297
298 internal::TransportAdapter transport_adapter(audio_observer.SendTransport());
sprang@webrtc.orgd9b95602014-01-27 13:03:02 +0000299 transport_adapter.Enable();
pbos@webrtc.org1d096902013-12-13 12:48:05 +0000300 EXPECT_EQ(0,
301 voe_network->RegisterExternalTransport(channel, transport_adapter));
302
303 observer.SetReceivers(receiver_call->Receiver(), sender_call->Receiver());
304
pbos@webrtc.org1d096902013-12-13 12:48:05 +0000305 test::FakeDecoder fake_decoder;
306
asapersson@webrtc.orgbdc5ed22014-01-31 10:05:07 +0000307 VideoSendStream::Config send_config = GetSendTestConfig(sender_call.get());
pbos@webrtc.org1d096902013-12-13 12:48:05 +0000308
309 VideoReceiveStream::Config receive_config =
310 receiver_call->GetDefaultReceiveConfig();
311 receive_config.codecs.clear();
312 receive_config.codecs.push_back(send_config.codec);
313 ExternalVideoDecoder decoder;
314 decoder.decoder = &fake_decoder;
315 decoder.payload_type = send_config.codec.plType;
316 receive_config.external_decoders.push_back(decoder);
317 receive_config.rtp.remote_ssrc = send_config.rtp.ssrcs[0];
318 receive_config.rtp.local_ssrc = kReceiverLocalSsrc;
319 receive_config.renderer = &observer;
320 receive_config.audio_channel_id = channel;
321
322 VideoSendStream* send_stream =
323 sender_call->CreateVideoSendStream(send_config);
324 VideoReceiveStream* receive_stream =
325 receiver_call->CreateVideoReceiveStream(receive_config);
326 scoped_ptr<test::FrameGeneratorCapturer> capturer(
327 test::FrameGeneratorCapturer::Create(send_stream->Input(),
328 send_config.codec.width,
329 send_config.codec.height,
330 30,
331 Clock::GetRealTimeClock()));
332 receive_stream->StartReceiving();
333 send_stream->StartSending();
334 capturer->Start();
335
336 fake_audio_device.Start();
337 EXPECT_EQ(0, voe_base->StartPlayout(channel));
338 EXPECT_EQ(0, voe_base->StartReceive(channel));
339 EXPECT_EQ(0, voe_base->StartSend(channel));
340
341 EXPECT_EQ(kEventSignaled, observer.Wait())
342 << "Timed out while waiting for audio and video to be synchronized.";
343
344 EXPECT_EQ(0, voe_base->StopSend(channel));
345 EXPECT_EQ(0, voe_base->StopReceive(channel));
346 EXPECT_EQ(0, voe_base->StopPlayout(channel));
347 fake_audio_device.Stop();
348
349 capturer->Stop();
350 send_stream->StopSending();
351 receive_stream->StopReceiving();
352 observer.StopSending();
353 audio_observer.StopSending();
354
355 voe_base->DeleteChannel(channel);
356 voe_base->Release();
357 voe_codec->Release();
358 voe_network->Release();
359 voe_sync->Release();
360 sender_call->DestroyVideoSendStream(send_stream);
361 receiver_call->DestroyVideoReceiveStream(receive_stream);
362 VoiceEngine::Delete(voice_engine);
363}
asapersson@webrtc.orgbdc5ed22014-01-31 10:05:07 +0000364
henrik.lundin@webrtc.orged865b52014-03-06 10:28:07 +0000365// Test with both ACM1 and ACM2.
366INSTANTIATE_TEST_CASE_P(SwitchAcm, ParamCallPerfTest, ::testing::Bool());
367
asapersson@webrtc.orgbdc5ed22014-01-31 10:05:07 +0000368TEST_F(CallPerfTest, RegisterCpuOveruseObserver) {
369 // Verifies that either a normal or overuse callback is triggered.
370 class OveruseCallbackObserver : public test::RtpRtcpObserver,
371 public webrtc::OveruseCallback {
372 public:
373 OveruseCallbackObserver() : RtpRtcpObserver(kLongTimeoutMs) {}
374
375 virtual void OnOveruse() OVERRIDE {
376 observation_complete_->Set();
377 }
378 virtual void OnNormalUse() OVERRIDE {
379 observation_complete_->Set();
380 }
381 };
382
383 OveruseCallbackObserver observer;
384 Call::Config call_config(observer.SendTransport());
385 call_config.overuse_callback = &observer;
386 scoped_ptr<Call> call(Call::Create(call_config));
387
388 VideoSendStream::Config send_config = GetSendTestConfig(call.get());
389 RunVideoSendTest(call.get(), send_config, &observer);
390}
pbos@webrtc.org1d096902013-12-13 12:48:05 +0000391} // namespace webrtc