Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(483)

Side by Side Diff: media/cast/audio_receiver/audio_decoder_unittest.cc

Issue 308043006: [Cast] Clean-up: Merge RtpReceiver+AudioReceiver+VideoReceiver-->FrameReceiver. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed hclam's comments. Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « media/cast/audio_receiver/audio_decoder.cc ('k') | media/cast/audio_receiver/audio_receiver.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « media/cast/audio_receiver/audio_decoder.cc ('k') | media/cast/audio_receiver/audio_receiver.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698