1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/bind.h" 6 #include "base/bind_helpers.h" 7 #include "base/synchronization/condition_variable.h" 8 #include "base/synchronization/lock.h" 9 #include "base/sys_byteorder.h" 10 #include "base/time/time.h" 11 #include "media/cast/cast_config.h" 12 #include "media/cast/receiver/audio_decoder.h" 13 #include "media/cast/test/utility/audio_utility.h" 14 #include "media/cast/test/utility/standalone_cast_environment.h" 15 #include "testing/gtest/include/gtest/gtest.h" 16 #include "third_party/opus/src/include/opus.h" 17 18 namespace media { 19 namespace cast { 20 21 namespace { 22 struct TestScenario { 23 transport::AudioCodec codec; 24 int num_channels; 25 int sampling_rate; 26 27 TestScenario(transport::AudioCodec c, int n, int s) 28 : codec(c), num_channels(n), sampling_rate(s) {} 29 }; 30 } // namespace 31 32 class AudioDecoderTest : public ::testing::TestWithParam<TestScenario> { 33 public: 34 AudioDecoderTest() 35 : cast_environment_(new StandaloneCastEnvironment()), 36 cond_(&lock_) {} 37 38 protected: 39 virtual void SetUp() OVERRIDE { 40 audio_decoder_.reset(new AudioDecoder(cast_environment_, 41 GetParam().num_channels, 42 GetParam().sampling_rate, 43 GetParam().codec)); 44 CHECK_EQ(STATUS_AUDIO_INITIALIZED, audio_decoder_->InitializationResult()); 45 46 audio_bus_factory_.reset( 47 new TestAudioBusFactory(GetParam().num_channels, 48 GetParam().sampling_rate, 49 TestAudioBusFactory::kMiddleANoteFreq, 50 0.5f)); 51 last_frame_id_ = 0; 52 seen_a_decoded_frame_ = false; 53 54 if (GetParam().codec == transport::kOpus) { 55 opus_encoder_memory_.reset( 56 new uint8[opus_encoder_get_size(GetParam().num_channels)]); 57 OpusEncoder* const opus_encoder = 58 reinterpret_cast<OpusEncoder*>(opus_encoder_memory_.get()); 59 CHECK_EQ(OPUS_OK, opus_encoder_init(opus_encoder, 60 GetParam().sampling_rate, 61 GetParam().num_channels, 62 OPUS_APPLICATION_AUDIO)); 63 CHECK_EQ(OPUS_OK, 64 opus_encoder_ctl(opus_encoder, OPUS_SET_BITRATE(OPUS_AUTO))); 65 } 66 67 total_audio_feed_in_ = base::TimeDelta(); 68 total_audio_decoded_ = base::TimeDelta(); 69 } 70 71 // Called from the unit test thread to create another EncodedFrame and push it 72 // into the decoding pipeline. 73 void FeedMoreAudio(const base::TimeDelta& duration, 74 int num_dropped_frames) { 75 // Prepare a simulated EncodedFrame to feed into the AudioDecoder. 76 scoped_ptr<transport::EncodedFrame> encoded_frame( 77 new transport::EncodedFrame()); 78 encoded_frame->dependency = transport::EncodedFrame::KEY; 79 encoded_frame->frame_id = last_frame_id_ + 1 + num_dropped_frames; 80 encoded_frame->referenced_frame_id = encoded_frame->frame_id; 81 last_frame_id_ = encoded_frame->frame_id; 82 83 const scoped_ptr<AudioBus> audio_bus( 84 audio_bus_factory_->NextAudioBus(duration).Pass()); 85 86 // Encode |audio_bus| into |encoded_frame->data|. 87 const int num_elements = audio_bus->channels() * audio_bus->frames(); 88 std::vector<int16> interleaved(num_elements); 89 audio_bus->ToInterleaved( 90 audio_bus->frames(), sizeof(int16), &interleaved.front()); 91 if (GetParam().codec == transport::kPcm16) { 92 encoded_frame->data.resize(num_elements * sizeof(int16)); 93 int16* const pcm_data = 94 reinterpret_cast<int16*>(encoded_frame->mutable_bytes()); 95 for (size_t i = 0; i < interleaved.size(); ++i) 96 pcm_data[i] = static_cast<int16>(base::HostToNet16(interleaved[i])); 97 } else if (GetParam().codec == transport::kOpus) { 98 OpusEncoder* const opus_encoder = 99 reinterpret_cast<OpusEncoder*>(opus_encoder_memory_.get()); 100 const int kOpusEncodeBufferSize = 4000; 101 encoded_frame->data.resize(kOpusEncodeBufferSize); 102 const int payload_size = 103 opus_encode(opus_encoder, 104 &interleaved.front(), 105 audio_bus->frames(), 106 encoded_frame->mutable_bytes(), 107 encoded_frame->data.size()); 108 CHECK_GT(payload_size, 1); 109 encoded_frame->data.resize(payload_size); 110 } else { 111 ASSERT_TRUE(false); // Not reached. 112 } 113 114 { 115 base::AutoLock auto_lock(lock_); 116 total_audio_feed_in_ += duration; 117 } 118 119 cast_environment_->PostTask( 120 CastEnvironment::MAIN, 121 FROM_HERE, 122 base::Bind(&AudioDecoder::DecodeFrame, 123 base::Unretained(audio_decoder_.get()), 124 base::Passed(&encoded_frame), 125 base::Bind(&AudioDecoderTest::OnDecodedFrame, 126 base::Unretained(this), 127 num_dropped_frames == 0))); 128 } 129 130 // Blocks the caller until all audio that has been feed in has been decoded. 131 void WaitForAllAudioToBeDecoded() { 132 DCHECK(!cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); 133 base::AutoLock auto_lock(lock_); 134 while (total_audio_decoded_ < total_audio_feed_in_) 135 cond_.Wait(); 136 EXPECT_EQ(total_audio_feed_in_.InMicroseconds(), 137 total_audio_decoded_.InMicroseconds()); 138 } 139 140 private: 141 // Called by |audio_decoder_| to deliver each frame of decoded audio. 142 void OnDecodedFrame(bool should_be_continuous, 143 scoped_ptr<AudioBus> audio_bus, 144 bool is_continuous) { 145 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); 146 147 // A NULL |audio_bus| indicates a decode error, which we don't expect. 148 ASSERT_FALSE(!audio_bus); 149 150 // Did the decoder detect whether frames were dropped? 151 EXPECT_EQ(should_be_continuous, is_continuous); 152 153 // Does the audio data seem to be intact? For Opus, we have to ignore the 154 // first frame seen at the start (and immediately after dropped packet 155 // recovery) because it introduces a tiny, significant delay. 156 bool examine_signal = true; 157 if (GetParam().codec == transport::kOpus) { 158 examine_signal = seen_a_decoded_frame_ && should_be_continuous; 159 seen_a_decoded_frame_ = true; 160 } 161 if (examine_signal) { 162 for (int ch = 0; ch < audio_bus->channels(); ++ch) { 163 EXPECT_NEAR( 164 TestAudioBusFactory::kMiddleANoteFreq * 2 * audio_bus->frames() / 165 GetParam().sampling_rate, 166 CountZeroCrossings(audio_bus->channel(ch), audio_bus->frames()), 167 1); 168 } 169 } 170 171 // Signal the main test thread that more audio was decoded. 172 base::AutoLock auto_lock(lock_); 173 total_audio_decoded_ += base::TimeDelta::FromSeconds(1) * 174 audio_bus->frames() / GetParam().sampling_rate; 175 cond_.Signal(); 176 } 177 178 const scoped_refptr<StandaloneCastEnvironment> cast_environment_; 179 scoped_ptr<AudioDecoder> audio_decoder_; 180 scoped_ptr<TestAudioBusFactory> audio_bus_factory_; 181 uint32 last_frame_id_; 182 bool seen_a_decoded_frame_; 183 scoped_ptr<uint8[]> opus_encoder_memory_; 184 185 base::Lock lock_; 186 base::ConditionVariable cond_; 187 base::TimeDelta total_audio_feed_in_; 188 base::TimeDelta total_audio_decoded_; 189 190 DISALLOW_COPY_AND_ASSIGN(AudioDecoderTest); 191 }; 192 193 TEST_P(AudioDecoderTest, DecodesFramesWithSameDuration) { 194 const base::TimeDelta kTenMilliseconds = 195 base::TimeDelta::FromMilliseconds(10); 196 const int kNumFrames = 10; 197 for (int i = 0; i < kNumFrames; ++i) 198 FeedMoreAudio(kTenMilliseconds, 0); 199 WaitForAllAudioToBeDecoded(); 200 } 201 202 TEST_P(AudioDecoderTest, DecodesFramesWithVaryingDuration) { 203 // These are the set of frame durations supported by the Opus encoder. 204 const int kFrameDurationMs[] = { 5, 10, 20, 40, 60 }; 205 206 const int kNumFrames = 10; 207 for (size_t i = 0; i < arraysize(kFrameDurationMs); ++i) 208 for (int j = 0; j < kNumFrames; ++j) 209 FeedMoreAudio(base::TimeDelta::FromMilliseconds(kFrameDurationMs[i]), 0); 210 WaitForAllAudioToBeDecoded(); 211 } 212 213 TEST_P(AudioDecoderTest, RecoversFromDroppedFrames) { 214 const base::TimeDelta kTenMilliseconds = 215 base::TimeDelta::FromMilliseconds(10); 216 const int kNumFrames = 100; 217 int next_drop_at = 3; 218 int next_num_dropped = 1; 219 for (int i = 0; i < kNumFrames; ++i) { 220 if (i == next_drop_at) { 221 const int num_dropped = next_num_dropped++; 222 next_drop_at *= 2; 223 i += num_dropped; 224 FeedMoreAudio(kTenMilliseconds, num_dropped); 225 } else { 226 FeedMoreAudio(kTenMilliseconds, 0); 227 } 228 } 229 WaitForAllAudioToBeDecoded(); 230 } 231 232 INSTANTIATE_TEST_CASE_P(AudioDecoderTestScenarios, 233 AudioDecoderTest, 234 ::testing::Values( 235 TestScenario(transport::kPcm16, 1, 8000), 236 TestScenario(transport::kPcm16, 2, 48000), 237 TestScenario(transport::kOpus, 1, 8000), 238 TestScenario(transport::kOpus, 2, 48000))); 239 240 } // namespace cast 241 } // namespace media 242