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.
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"
27 TestScenario(Codec c
, int n
, int s
)
28 : codec(c
), num_channels(n
), sampling_rate(s
) {}
32 class AudioDecoderTest
: public ::testing::TestWithParam
<TestScenario
> {
35 : cast_environment_(new StandaloneCastEnvironment()),
38 virtual ~AudioDecoderTest() {
39 // Make sure all threads have stopped before the environment goes away.
40 cast_environment_
->Shutdown();
44 void SetUp() override
{
45 audio_decoder_
.reset(new AudioDecoder(cast_environment_
,
46 GetParam().num_channels
,
47 GetParam().sampling_rate
,
49 CHECK_EQ(STATUS_AUDIO_INITIALIZED
, audio_decoder_
->InitializationResult());
51 audio_bus_factory_
.reset(
52 new TestAudioBusFactory(GetParam().num_channels
,
53 GetParam().sampling_rate
,
54 TestAudioBusFactory::kMiddleANoteFreq
,
57 seen_a_decoded_frame_
= false;
59 if (GetParam().codec
== CODEC_AUDIO_OPUS
) {
60 opus_encoder_memory_
.reset(
61 new uint8
[opus_encoder_get_size(GetParam().num_channels
)]);
62 OpusEncoder
* const opus_encoder
=
63 reinterpret_cast<OpusEncoder
*>(opus_encoder_memory_
.get());
64 CHECK_EQ(OPUS_OK
, opus_encoder_init(opus_encoder
,
65 GetParam().sampling_rate
,
66 GetParam().num_channels
,
67 OPUS_APPLICATION_AUDIO
));
69 opus_encoder_ctl(opus_encoder
, OPUS_SET_BITRATE(OPUS_AUTO
)));
72 total_audio_feed_in_
= base::TimeDelta();
73 total_audio_decoded_
= base::TimeDelta();
76 // Called from the unit test thread to create another EncodedFrame and push it
77 // into the decoding pipeline.
78 void FeedMoreAudio(const base::TimeDelta
& duration
,
79 int num_dropped_frames
) {
80 // Prepare a simulated EncodedFrame to feed into the AudioDecoder.
81 scoped_ptr
<EncodedFrame
> encoded_frame(
83 encoded_frame
->dependency
= EncodedFrame::KEY
;
84 encoded_frame
->frame_id
= last_frame_id_
+ 1 + num_dropped_frames
;
85 encoded_frame
->referenced_frame_id
= encoded_frame
->frame_id
;
86 last_frame_id_
= encoded_frame
->frame_id
;
88 const scoped_ptr
<AudioBus
> audio_bus(
89 audio_bus_factory_
->NextAudioBus(duration
).Pass());
91 // Encode |audio_bus| into |encoded_frame->data|.
92 const int num_elements
= audio_bus
->channels() * audio_bus
->frames();
93 std::vector
<int16
> interleaved(num_elements
);
94 audio_bus
->ToInterleaved(
95 audio_bus
->frames(), sizeof(int16
), &interleaved
.front());
96 if (GetParam().codec
== CODEC_AUDIO_PCM16
) {
97 encoded_frame
->data
.resize(num_elements
* sizeof(int16
));
98 int16
* const pcm_data
=
99 reinterpret_cast<int16
*>(encoded_frame
->mutable_bytes());
100 for (size_t i
= 0; i
< interleaved
.size(); ++i
)
101 pcm_data
[i
] = static_cast<int16
>(base::HostToNet16(interleaved
[i
]));
102 } else if (GetParam().codec
== CODEC_AUDIO_OPUS
) {
103 OpusEncoder
* const opus_encoder
=
104 reinterpret_cast<OpusEncoder
*>(opus_encoder_memory_
.get());
105 const int kOpusEncodeBufferSize
= 4000;
106 encoded_frame
->data
.resize(kOpusEncodeBufferSize
);
107 const int payload_size
=
108 opus_encode(opus_encoder
,
109 &interleaved
.front(),
111 encoded_frame
->mutable_bytes(),
112 encoded_frame
->data
.size());
113 CHECK_GT(payload_size
, 1);
114 encoded_frame
->data
.resize(payload_size
);
116 ASSERT_TRUE(false); // Not reached.
120 base::AutoLock
auto_lock(lock_
);
121 total_audio_feed_in_
+= duration
;
124 cast_environment_
->PostTask(
125 CastEnvironment::MAIN
,
127 base::Bind(&AudioDecoder::DecodeFrame
,
128 base::Unretained(audio_decoder_
.get()),
129 base::Passed(&encoded_frame
),
130 base::Bind(&AudioDecoderTest::OnDecodedFrame
,
131 base::Unretained(this),
132 num_dropped_frames
== 0)));
135 // Blocks the caller until all audio that has been feed in has been decoded.
136 void WaitForAllAudioToBeDecoded() {
137 DCHECK(!cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
138 base::AutoLock
auto_lock(lock_
);
139 while (total_audio_decoded_
< total_audio_feed_in_
)
141 EXPECT_EQ(total_audio_feed_in_
.InMicroseconds(),
142 total_audio_decoded_
.InMicroseconds());
146 // Called by |audio_decoder_| to deliver each frame of decoded audio.
147 void OnDecodedFrame(bool should_be_continuous
,
148 scoped_ptr
<AudioBus
> audio_bus
,
149 bool is_continuous
) {
150 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
152 // A NULL |audio_bus| indicates a decode error, which we don't expect.
153 ASSERT_FALSE(!audio_bus
);
155 // Did the decoder detect whether frames were dropped?
156 EXPECT_EQ(should_be_continuous
, is_continuous
);
158 // Does the audio data seem to be intact? For Opus, we have to ignore the
159 // first frame seen at the start (and immediately after dropped packet
160 // recovery) because it introduces a tiny, significant delay.
161 bool examine_signal
= true;
162 if (GetParam().codec
== CODEC_AUDIO_OPUS
) {
163 examine_signal
= seen_a_decoded_frame_
&& should_be_continuous
;
164 seen_a_decoded_frame_
= true;
166 if (examine_signal
) {
167 for (int ch
= 0; ch
< audio_bus
->channels(); ++ch
) {
169 TestAudioBusFactory::kMiddleANoteFreq
* 2 * audio_bus
->frames() /
170 GetParam().sampling_rate
,
171 CountZeroCrossings(audio_bus
->channel(ch
), audio_bus
->frames()),
176 // Signal the main test thread that more audio was decoded.
177 base::AutoLock
auto_lock(lock_
);
178 total_audio_decoded_
+= base::TimeDelta::FromSeconds(1) *
179 audio_bus
->frames() / GetParam().sampling_rate
;
183 const scoped_refptr
<StandaloneCastEnvironment
> cast_environment_
;
184 scoped_ptr
<AudioDecoder
> audio_decoder_
;
185 scoped_ptr
<TestAudioBusFactory
> audio_bus_factory_
;
186 uint32 last_frame_id_
;
187 bool seen_a_decoded_frame_
;
188 scoped_ptr
<uint8
[]> opus_encoder_memory_
;
191 base::ConditionVariable cond_
;
192 base::TimeDelta total_audio_feed_in_
;
193 base::TimeDelta total_audio_decoded_
;
195 DISALLOW_COPY_AND_ASSIGN(AudioDecoderTest
);
198 TEST_P(AudioDecoderTest
, DecodesFramesWithSameDuration
) {
199 const base::TimeDelta kTenMilliseconds
=
200 base::TimeDelta::FromMilliseconds(10);
201 const int kNumFrames
= 10;
202 for (int i
= 0; i
< kNumFrames
; ++i
)
203 FeedMoreAudio(kTenMilliseconds
, 0);
204 WaitForAllAudioToBeDecoded();
207 TEST_P(AudioDecoderTest
, DecodesFramesWithVaryingDuration
) {
208 // These are the set of frame durations supported by the Opus encoder.
209 const int kFrameDurationMs
[] = { 5, 10, 20, 40, 60 };
211 const int kNumFrames
= 10;
212 for (size_t i
= 0; i
< arraysize(kFrameDurationMs
); ++i
)
213 for (int j
= 0; j
< kNumFrames
; ++j
)
214 FeedMoreAudio(base::TimeDelta::FromMilliseconds(kFrameDurationMs
[i
]), 0);
215 WaitForAllAudioToBeDecoded();
218 TEST_P(AudioDecoderTest
, RecoversFromDroppedFrames
) {
219 const base::TimeDelta kTenMilliseconds
=
220 base::TimeDelta::FromMilliseconds(10);
221 const int kNumFrames
= 100;
222 int next_drop_at
= 3;
223 int next_num_dropped
= 1;
224 for (int i
= 0; i
< kNumFrames
; ++i
) {
225 if (i
== next_drop_at
) {
226 const int num_dropped
= next_num_dropped
++;
229 FeedMoreAudio(kTenMilliseconds
, num_dropped
);
231 FeedMoreAudio(kTenMilliseconds
, 0);
234 WaitForAllAudioToBeDecoded();
237 INSTANTIATE_TEST_CASE_P(
238 AudioDecoderTestScenarios
,
241 TestScenario(CODEC_AUDIO_PCM16
, 1, 8000),
242 TestScenario(CODEC_AUDIO_PCM16
, 2, 48000),
243 TestScenario(CODEC_AUDIO_OPUS
, 1, 8000),
244 TestScenario(CODEC_AUDIO_OPUS
, 2, 48000)));