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