blob: 7aec5d024f0231828151039a7c2db0269487c9b0 [file] [log] [blame]
/*
* Copyright (c) 2013 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 <memory>
#include "webrtc/common_types.h"
#include "webrtc/modules/audio_coding/codecs/pcm16b/pcm16b.h"
#include "webrtc/modules/audio_coding/include/audio_coding_module.h"
#include "webrtc/modules/audio_coding/test/utility.h"
#include "webrtc/modules/include/module_common_types.h"
#include "webrtc/test/gtest.h"
#include "webrtc/test/testsupport/fileutils.h"
namespace webrtc {
class TargetDelayTest : public ::testing::Test {
protected:
TargetDelayTest() : acm_(AudioCodingModule::Create(0)) {}
~TargetDelayTest() {}
void SetUp() {
EXPECT_TRUE(acm_.get() != NULL);
ASSERT_EQ(0, acm_->InitializeReceiver());
constexpr int pltype = 108;
ASSERT_EQ(true,
acm_->RegisterReceiveCodec(pltype, {"L16", kSampleRateHz, 1}));
rtp_info_.header.payloadType = pltype;
rtp_info_.header.timestamp = 0;
rtp_info_.header.ssrc = 0x12345678;
rtp_info_.header.markerBit = false;
rtp_info_.header.sequenceNumber = 0;
rtp_info_.type.Audio.channel = 1;
rtp_info_.type.Audio.isCNG = false;
rtp_info_.frameType = kAudioFrameSpeech;
int16_t audio[kFrameSizeSamples];
const int kRange = 0x7FF; // 2047, easy for masking.
for (size_t n = 0; n < kFrameSizeSamples; ++n)
audio[n] = (rand() & kRange) - kRange / 2;
WebRtcPcm16b_Encode(audio, kFrameSizeSamples, payload_);
}
void OutOfRangeInput() {
EXPECT_EQ(-1, SetMinimumDelay(-1));
EXPECT_EQ(-1, SetMinimumDelay(10001));
}
void NoTargetDelayBufferSizeChanges() {
for (int n = 0; n < 30; ++n) // Run enough iterations.
Run(true);
int clean_optimal_delay = GetCurrentOptimalDelayMs();
Run(false); // Run with jitter.
int jittery_optimal_delay = GetCurrentOptimalDelayMs();
EXPECT_GT(jittery_optimal_delay, clean_optimal_delay);
int required_delay = RequiredDelay();
EXPECT_GT(required_delay, 0);
EXPECT_NEAR(required_delay, jittery_optimal_delay, 1);
}
void WithTargetDelayBufferNotChanging() {
// A target delay that is one packet larger than jitter.
const int kTargetDelayMs = (kInterarrivalJitterPacket + 1) *
kNum10msPerFrame * 10;
ASSERT_EQ(0, SetMinimumDelay(kTargetDelayMs));
for (int n = 0; n < 30; ++n) // Run enough iterations to fill the buffer.
Run(true);
int clean_optimal_delay = GetCurrentOptimalDelayMs();
EXPECT_EQ(kTargetDelayMs, clean_optimal_delay);
Run(false); // Run with jitter.
int jittery_optimal_delay = GetCurrentOptimalDelayMs();
EXPECT_EQ(jittery_optimal_delay, clean_optimal_delay);
}
void RequiredDelayAtCorrectRange() {
for (int n = 0; n < 30; ++n) // Run clean and store delay.
Run(true);
int clean_optimal_delay = GetCurrentOptimalDelayMs();
// A relatively large delay.
const int kTargetDelayMs = (kInterarrivalJitterPacket + 10) *
kNum10msPerFrame * 10;
ASSERT_EQ(0, SetMinimumDelay(kTargetDelayMs));
for (int n = 0; n < 300; ++n) // Run enough iterations to fill the buffer.
Run(true);
Run(false); // Run with jitter.
int jittery_optimal_delay = GetCurrentOptimalDelayMs();
EXPECT_EQ(kTargetDelayMs, jittery_optimal_delay);
int required_delay = RequiredDelay();
// Checking |required_delay| is in correct range.
EXPECT_GT(required_delay, 0);
EXPECT_GT(jittery_optimal_delay, required_delay);
EXPECT_GT(required_delay, clean_optimal_delay);
// A tighter check for the value of |required_delay|.
// The jitter forces a delay of
// |kInterarrivalJitterPacket * kNum10msPerFrame * 10| milliseconds. So we
// expect |required_delay| be close to that.
EXPECT_NEAR(kInterarrivalJitterPacket * kNum10msPerFrame * 10,
required_delay, 1);
}
void TargetDelayBufferMinMax() {
const int kTargetMinDelayMs = kNum10msPerFrame * 10;
ASSERT_EQ(0, SetMinimumDelay(kTargetMinDelayMs));
for (int m = 0; m < 30; ++m) // Run enough iterations to fill the buffer.
Run(true);
int clean_optimal_delay = GetCurrentOptimalDelayMs();
EXPECT_EQ(kTargetMinDelayMs, clean_optimal_delay);
const int kTargetMaxDelayMs = 2 * (kNum10msPerFrame * 10);
ASSERT_EQ(0, SetMaximumDelay(kTargetMaxDelayMs));
for (int n = 0; n < 30; ++n) // Run enough iterations to fill the buffer.
Run(false);
int capped_optimal_delay = GetCurrentOptimalDelayMs();
EXPECT_EQ(kTargetMaxDelayMs, capped_optimal_delay);
}
private:
static const int kSampleRateHz = 16000;
static const int kNum10msPerFrame = 2;
static const size_t kFrameSizeSamples = 320; // 20 ms @ 16 kHz.
// payload-len = frame-samples * 2 bytes/sample.
static const int kPayloadLenBytes = 320 * 2;
// Inter-arrival time in number of packets in a jittery channel. One is no
// jitter.
static const int kInterarrivalJitterPacket = 2;
void Push() {
rtp_info_.header.timestamp += kFrameSizeSamples;
rtp_info_.header.sequenceNumber++;
ASSERT_EQ(0, acm_->IncomingPacket(payload_, kFrameSizeSamples * 2,
rtp_info_));
}
// Pull audio equivalent to the amount of audio in one RTP packet.
void Pull() {
AudioFrame frame;
bool muted;
for (int k = 0; k < kNum10msPerFrame; ++k) { // Pull one frame.
ASSERT_EQ(0, acm_->PlayoutData10Ms(-1, &frame, &muted));
ASSERT_FALSE(muted);
// Had to use ASSERT_TRUE, ASSERT_EQ generated error.
ASSERT_TRUE(kSampleRateHz == frame.sample_rate_hz_);
ASSERT_EQ(1u, frame.num_channels_);
ASSERT_TRUE(kSampleRateHz / 100 == frame.samples_per_channel_);
}
}
void Run(bool clean) {
for (int n = 0; n < 10; ++n) {
for (int m = 0; m < 5; ++m) {
Push();
Pull();
}
if (!clean) {
for (int m = 0; m < 10; ++m) { // Long enough to trigger delay change.
Push();
for (int n = 0; n < kInterarrivalJitterPacket; ++n)
Pull();
}
}
}
}
int SetMinimumDelay(int delay_ms) {
return acm_->SetMinimumPlayoutDelay(delay_ms);
}
int SetMaximumDelay(int delay_ms) {
return acm_->SetMaximumPlayoutDelay(delay_ms);
}
int GetCurrentOptimalDelayMs() {
NetworkStatistics stats;
acm_->GetNetworkStatistics(&stats);
return stats.preferredBufferSize;
}
int RequiredDelay() {
return acm_->LeastRequiredDelayMs();
}
std::unique_ptr<AudioCodingModule> acm_;
WebRtcRTPHeader rtp_info_;
uint8_t payload_[kPayloadLenBytes];
};
// Flaky on iOS: webrtc:7057.
#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
#define MAYBE_OutOfRangeInput DISABLED_OutOfRangeInput
#else
#define MAYBE_OutOfRangeInput OutOfRangeInput
#endif
TEST_F(TargetDelayTest, MAYBE_OutOfRangeInput) {
OutOfRangeInput();
}
// Flaky on iOS: webrtc:7057.
#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
#define MAYBE_NoTargetDelayBufferSizeChanges \
DISABLED_NoTargetDelayBufferSizeChanges
#else
#define MAYBE_NoTargetDelayBufferSizeChanges NoTargetDelayBufferSizeChanges
#endif
TEST_F(TargetDelayTest, MAYBE_NoTargetDelayBufferSizeChanges) {
NoTargetDelayBufferSizeChanges();
}
// Flaky on iOS: webrtc:7057.
#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
#define MAYBE_WithTargetDelayBufferNotChanging \
DISABLED_WithTargetDelayBufferNotChanging
#else
#define MAYBE_WithTargetDelayBufferNotChanging WithTargetDelayBufferNotChanging
#endif
TEST_F(TargetDelayTest, MAYBE_WithTargetDelayBufferNotChanging) {
WithTargetDelayBufferNotChanging();
}
// Flaky on iOS: webrtc:7057.
#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
#define MAYBE_RequiredDelayAtCorrectRange DISABLED_RequiredDelayAtCorrectRange
#else
#define MAYBE_RequiredDelayAtCorrectRange RequiredDelayAtCorrectRange
#endif
TEST_F(TargetDelayTest, MAYBE_RequiredDelayAtCorrectRange) {
RequiredDelayAtCorrectRange();
}
// Flaky on iOS: webrtc:7057.
#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
#define MAYBE_TargetDelayBufferMinMax DISABLED_TargetDelayBufferMinMax
#else
#define MAYBE_TargetDelayBufferMinMax TargetDelayBufferMinMax
#endif
TEST_F(TargetDelayTest, MAYBE_TargetDelayBufferMinMax) {
TargetDelayBufferMinMax();
}
} // namespace webrtc