|  | /* | 
|  | *  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 |