OLD | NEW |
---|---|
(Empty) | |
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 <deque> | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/md5.h" | |
9 #include "base/message_loop/message_loop.h" | |
10 #include "base/run_loop.h" | |
11 #include "build/build_config.h" | |
12 #include "media/base/audio_buffer.h" | |
13 #include "media/base/audio_bus.h" | |
14 #include "media/base/audio_hash.h" | |
15 #include "media/base/decoder_buffer.h" | |
16 #include "media/base/test_data_util.h" | |
17 #include "media/base/test_helpers.h" | |
18 #include "media/ffmpeg/ffmpeg_common.h" | |
19 #include "media/filters/audio_file_reader.h" | |
20 #include "media/filters/ffmpeg_audio_decoder.h" | |
21 #include "media/filters/in_memory_url_protocol.h" | |
22 #include "media/filters/opus_audio_decoder.h" | |
23 #include "testing/gtest/include/gtest/gtest.h" | |
24 | |
wolenetz
2014/06/06 21:00:03
nit: add using testing::ValuesIn; ?
DaleCurtis
2014/06/06 21:16:59
No, I dislike those.
| |
25 namespace media { | |
26 | |
27 // The number of packets to read and then decode from each file. | |
28 static const int kDecodeRuns = 3; | |
29 | |
30 enum AudioDecoderType { | |
31 FFMPEG_DECODER, | |
32 OPUS_DECODER, | |
33 }; | |
34 | |
35 struct AudioSample { | |
wolenetz
2014/06/06 21:00:03
nit: "Sample" seems strange term to me for what ap
DaleCurtis
2014/06/12 22:00:47
Done.
| |
36 const int64 timestamp; | |
37 const int64 duration; | |
38 const char* hash; | |
39 }; | |
40 | |
41 struct AudioDecoderTestData { | |
42 const AudioDecoderType decoder_type; | |
43 const char* filename; | |
44 const AudioSample* samples; | |
45 }; | |
46 | |
47 // Tells gtest how to print our AudioDecoderTestData structure. | |
48 std::ostream& operator<<(std::ostream& os, const AudioDecoderTestData& data) { | |
49 return os << data.filename; | |
wolenetz
2014/06/06 21:00:04
nit: include decoder type or any summary of the sa
DaleCurtis
2014/06/06 21:17:00
No, not relevant for figuring out what's wrong. Th
| |
50 } | |
51 | |
52 class AudioDecoderTest : public testing::TestWithParam<AudioDecoderTestData> { | |
53 public: | |
54 AudioDecoderTest() : pending_decode_(false), pending_reset_(false) { | |
55 switch (GetParam().decoder_type) { | |
56 case FFMPEG_DECODER: | |
57 decoder_.reset(new FFmpegAudioDecoder( | |
58 message_loop_.message_loop_proxy(), LogCB())); | |
59 break; | |
60 case OPUS_DECODER: | |
61 decoder_.reset( | |
62 new OpusAudioDecoder(message_loop_.message_loop_proxy())); | |
wolenetz
2014/06/06 21:00:04
nit: wrap previous line like Decoder(\n ?
DaleCurtis
2014/06/06 21:17:00
Are you arguing with our deity, clang-format?! :)
wolenetz
2014/06/06 21:52:31
I figured as much. No worries.
| |
63 break; | |
64 } | |
65 } | |
66 | |
67 virtual ~AudioDecoderTest() { | |
68 EXPECT_FALSE(pending_decode_); | |
69 EXPECT_FALSE(pending_reset_); | |
70 } | |
71 | |
72 protected: | |
73 void SatisfyPendingDecode() { base::RunLoop().RunUntilIdle(); } | |
74 | |
75 void SendEndOfStream() { | |
76 pending_decode_ = true; | |
wolenetz
2014/06/06 21:00:04
First, ASSERT/EXPECT_FALSE(pending_decode_); and A
DaleCurtis
2014/06/12 22:00:47
Done.
| |
77 decoder_->Decode( | |
78 DecoderBuffer::CreateEOSBuffer(), | |
79 base::Bind(&AudioDecoderTest::DecodeFinished, base::Unretained(this))); | |
80 base::RunLoop().RunUntilIdle(); | |
81 } | |
82 | |
83 void Initialize() { | |
84 // Load the test data file. | |
85 data_ = ReadTestDataFile(GetParam().filename); | |
86 protocol_.reset( | |
87 new InMemoryUrlProtocol(data_->data(), data_->data_size(), false)); | |
88 reader_.reset(new AudioFileReader(protocol_.get())); | |
89 reader_->Open(); | |
wolenetz
2014/06/06 21:00:04
nit: We only decode/verify the first enumerated au
DaleCurtis
2014/06/06 21:17:00
No, we don't support multiple streams of the same
| |
90 | |
91 // Load the first packet and save its timestamp. | |
92 AVPacket packet; | |
93 ASSERT_TRUE(reader_->ReadPacketForTesting(&packet)); | |
94 start_timestamp_ = ConvertFromTimeBase( | |
wolenetz
2014/06/06 21:00:04
nit: Verify expected start timestamp?
DaleCurtis
2014/06/06 21:16:59
Maybe. I'll see about adding it.
DaleCurtis
2014/06/12 22:00:48
Done.
| |
95 reader_->codec_context_for_testing()->time_base, packet.pts); | |
96 av_free_packet(&packet); | |
97 | |
98 // Seek back to the unconverted timestamp. | |
99 ASSERT_TRUE( | |
100 reader_->SeekForTesting(base::TimeDelta::FromMicroseconds(packet.pts))); | |
101 | |
102 AudioDecoderConfig config; | |
103 AVCodecContextToAudioDecoderConfig( | |
wolenetz
2014/06/06 21:00:04
nit: Verify expected config?
DaleCurtis
2014/06/06 21:17:00
Maybe. I could verify codec, channels, and sample
DaleCurtis
2014/06/12 22:00:47
Done.
| |
104 reader_->codec_context_for_testing(), false, &config, false); | |
wolenetz
2014/06/06 21:00:05
Can the config change during any test? If not, ver
DaleCurtis
2014/06/06 21:17:00
This is already checked within the decoder. Only
| |
105 InitializeDecoder(config); | |
106 } | |
107 | |
108 void InitializeDecoder(const AudioDecoderConfig& config) { | |
109 decoder_->Initialize(config, NewExpectedStatusCB(PIPELINE_OK)); | |
110 base::RunLoop().RunUntilIdle(); | |
111 } | |
112 | |
113 void Decode() { | |
114 pending_decode_ = true; | |
wolenetz
2014/06/06 21:00:04
ditto: (expect nothing pending, or add tests if th
DaleCurtis
2014/06/12 22:00:47
Done.
| |
115 | |
116 AVPacket packet; | |
117 ASSERT_TRUE(reader_->ReadPacketForTesting(&packet)); | |
118 | |
119 scoped_refptr<DecoderBuffer> buffer = | |
120 DecoderBuffer::CopyFrom(packet.data, packet.size); | |
121 buffer->set_timestamp(ConvertFromTimeBase( | |
122 reader_->codec_context_for_testing()->time_base, packet.pts)); | |
wolenetz
2014/06/06 21:00:03
Do decoders care at all about decode timestamp? Do
DaleCurtis
2014/06/06 21:17:00
No. DecodeTimestamp is a StreamParserBuffer only c
| |
123 buffer->set_duration(ConvertFromTimeBase( | |
124 reader_->codec_context_for_testing()->time_base, packet.duration)); | |
125 decoder_->Decode( | |
126 buffer, | |
127 base::Bind(&AudioDecoderTest::DecodeFinished, base::Unretained(this))); | |
128 base::RunLoop().RunUntilIdle(); | |
129 av_free_packet(&packet); | |
wolenetz
2014/06/06 21:00:03
nit: Why free after RunUntilIdle() and not before?
DaleCurtis
2014/06/06 21:17:00
To ensure all operations which might be using the
| |
130 } | |
131 | |
132 void Reset() { | |
133 pending_reset_ = true; | |
wolenetz
2014/06/06 21:00:04
ditto: (expect nothing pending, or add tests if th
DaleCurtis
2014/06/12 22:00:48
Done.
| |
134 decoder_->Reset( | |
135 base::Bind(&AudioDecoderTest::ResetFinished, base::Unretained(this))); | |
136 base::RunLoop().RunUntilIdle(); | |
137 } | |
138 | |
139 void Stop() { | |
140 decoder_->Stop(); | |
wolenetz
2014/06/06 21:00:05
nit: does setting some flag like |stopped_| true m
DaleCurtis
2014/06/12 22:00:48
Seems unnecessary, that'll blow up in all sorts of
| |
141 base::RunLoop().RunUntilIdle(); | |
142 } | |
143 | |
144 void Seek(base::TimeDelta seek_time) { | |
145 Reset(); | |
146 decoded_audio_.clear(); | |
147 const base::TimeDelta converted_time = | |
wolenetz
2014/06/06 21:00:04
ditto of audio_file_reader.cc comment: this looks
DaleCurtis
2014/06/06 21:16:59
Reading the docs, it looks like this is wrong. Th
wolenetz
2014/06/06 21:52:31
I'm not sure about the automatic conversion, unles
DaleCurtis
2014/06/12 22:00:47
Ah yeah, you're correct. I need to convert to the
| |
148 base::TimeDelta::FromMicroseconds(ConvertToTimeBase( | |
149 reader_->codec_context_for_testing()->time_base, seek_time)); | |
150 ASSERT_TRUE(reader_->SeekForTesting(converted_time)); | |
151 } | |
152 | |
153 void DecodeFinished(AudioDecoder::Status status, | |
154 const scoped_refptr<AudioBuffer>& buffer) { | |
155 EXPECT_TRUE(pending_decode_); | |
156 pending_decode_ = false; | |
157 | |
158 if (status == AudioDecoder::kNotEnoughData) { | |
wolenetz
2014/06/06 21:00:04
Verify versus expectation of not enough data?
DaleCurtis
2014/06/06 21:16:59
I don't understand what you're saying.
wolenetz
2014/06/06 21:52:31
Sorry. Can we deterministically expect and verify
DaleCurtis
2014/06/12 22:00:47
Not easily and I don't think it adds any value tha
| |
159 EXPECT_TRUE(buffer.get() == NULL); | |
160 Decode(); | |
161 return; | |
162 } | |
163 | |
164 decoded_audio_.push_back(buffer); | |
165 | |
166 // If we hit a NULL buffer or have a pending reset, we expect an abort. | |
167 if (buffer.get() == NULL || pending_reset_) { | |
168 EXPECT_TRUE(buffer.get() == NULL); | |
169 EXPECT_EQ(status, AudioDecoder::kAborted); | |
wolenetz
2014/06/06 21:00:04
nit: here and multiple other places in this file:
DaleCurtis
2014/06/12 22:00:47
Done.
| |
170 return; | |
171 } | |
172 | |
173 EXPECT_EQ(status, AudioDecoder::kOk); | |
174 } | |
175 | |
176 void ResetFinished() { | |
177 EXPECT_TRUE(pending_reset_); | |
178 // Reset should always finish after Decode. | |
179 EXPECT_FALSE(pending_decode_); | |
180 | |
181 pending_reset_ = false; | |
182 } | |
183 | |
184 // Generates an exact hash of the audio signal. Should not be used for checks | |
185 // across platforms as audio varies slightly across platforms. | |
wolenetz
2014/06/06 21:00:04
Is cross-platform audio variance a bug?
DaleCurtis
2014/06/06 21:17:00
No, it's expected due to differing implementations
wolenetz
2014/06/06 21:52:31
Much learning for me today. Many wow :)
| |
186 std::string GetDecodedAudioMD5(size_t i) { | |
187 CHECK_LT(i, decoded_audio_.size()); | |
188 const scoped_refptr<AudioBuffer>& buffer = decoded_audio_[i]; | |
189 | |
190 scoped_ptr<AudioBus> output = | |
191 AudioBus::Create(buffer->channel_count(), buffer->frame_count()); | |
192 buffer->ReadFrames(buffer->frame_count(), 0, 0, output.get()); | |
193 | |
194 // Generate an exact MD5 hash for comparison of multiple runs on the same | |
wolenetz
2014/06/06 21:00:05
nit: truncated comment
DaleCurtis
2014/06/12 22:00:47
Removed. Function comment says more.
| |
195 base::MD5Context context; | |
196 base::MD5Init(&context); | |
197 for (int ch = 0; ch < output->channels(); ++ch) { | |
198 base::MD5Update( | |
199 &context, | |
200 base::StringPiece(reinterpret_cast<char*>(output->channel(ch)), | |
201 output->frames() * sizeof(*output->channel(ch)))); | |
202 } | |
203 base::MD5Digest digest; | |
204 base::MD5Final(&digest, &context); | |
205 return base::MD5DigestToBase16(digest); | |
206 } | |
207 | |
208 void ExpectDecodedAudio(size_t i, const std::string& exact_hash) { | |
209 EXPECT_LT(i, decoded_audio_.size()); | |
wolenetz
2014/06/06 21:00:03
nit: s/EXPECT/ASSERT or CHECK/
DaleCurtis
2014/06/12 22:00:47
Done.
| |
210 const scoped_refptr<AudioBuffer>& buffer = decoded_audio_[i]; | |
211 | |
212 const AudioSample& sample_info = GetParam().samples[i]; | |
213 EXPECT_EQ(sample_info.timestamp, buffer->timestamp().InMicroseconds()); | |
214 EXPECT_EQ(sample_info.duration, buffer->duration().InMicroseconds()); | |
215 EXPECT_FALSE(buffer->end_of_stream()); | |
216 | |
217 scoped_ptr<AudioBus> output = | |
218 AudioBus::Create(buffer->channel_count(), buffer->frame_count()); | |
219 buffer->ReadFrames(buffer->frame_count(), 0, 0, output.get()); | |
220 | |
221 // Generate a lossy hash of the audio used for comparison across platforms. | |
222 AudioHash audio_hash; | |
223 audio_hash.Update(output.get(), output->frames()); | |
wolenetz
2014/06/06 21:00:05
nit: I'm not sure the claim of "Value chosen by di
DaleCurtis
2014/06/06 21:17:00
Again, I don't understand what you're asking. The
wolenetz
2014/06/06 21:52:31
I was commenting that the apparent 'randomness' of
DaleCurtis
2014/06/12 22:00:48
Still don't understand :) That value is used for t
wolenetz
2014/06/16 23:36:08
We're missing each other :)
I think AudioHash's cl
| |
224 EXPECT_EQ(sample_info.hash, audio_hash.ToString()); | |
225 | |
226 if (!exact_hash.empty()) | |
227 EXPECT_EQ(exact_hash, GetDecodedAudioMD5(i)); | |
wolenetz
2014/06/06 21:00:04
nit: have a test show that this portion of the met
DaleCurtis
2014/06/12 22:00:47
Done.
| |
228 } | |
229 | |
230 void ExpectEndOfStream(size_t i) { | |
231 EXPECT_LT(i, decoded_audio_.size()); | |
wolenetz
2014/06/06 21:00:03
nit: s/EXPECT/ASSERT or CHECK/
DaleCurtis
2014/06/12 22:00:47
Done.
| |
232 EXPECT_TRUE(decoded_audio_[i]->end_of_stream()); | |
233 } | |
234 | |
235 size_t decoded_audio_size() const { return decoded_audio_.size(); } | |
236 base::TimeDelta start_timestamp() const { return start_timestamp_; } | |
237 | |
238 private: | |
239 base::MessageLoop message_loop_; | |
240 scoped_refptr<DecoderBuffer> data_; | |
241 scoped_ptr<InMemoryUrlProtocol> protocol_; | |
242 scoped_ptr<AudioFileReader> reader_; | |
243 | |
244 scoped_ptr<AudioDecoder> decoder_; | |
245 bool pending_decode_; | |
246 bool pending_reset_; | |
247 | |
248 std::deque<scoped_refptr<AudioBuffer> > decoded_audio_; | |
249 base::TimeDelta start_timestamp_; | |
250 | |
251 DISALLOW_COPY_AND_ASSIGN(AudioDecoderTest); | |
252 }; | |
253 | |
254 TEST_P(AudioDecoderTest, Initialize) { | |
255 Initialize(); | |
256 Stop(); | |
257 } | |
258 | |
259 TEST_P(AudioDecoderTest, InitializeWithNoCodecDelay) { | |
260 if (GetParam().decoder_type != OPUS_DECODER) | |
wolenetz
2014/06/06 21:00:03
nit: Can this test can be done explicitly (and sim
DaleCurtis
2014/06/06 21:17:00
Possibly, but it's dirty since I'll need to make A
wolenetz
2014/06/06 21:52:31
That's icky. Please forget I asked :).
| |
261 return; | |
262 | |
263 const uint8_t kOpusExtraData[] = { | |
264 0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64, 0x01, 0x02, | |
265 // The next two bytes represent the codec delay. | |
266 0x00, 0x00, 0x80, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00}; | |
267 AudioDecoderConfig decoder_config; | |
268 decoder_config.Initialize(kCodecOpus, | |
269 kSampleFormatF32, | |
270 CHANNEL_LAYOUT_STEREO, | |
271 48000, | |
272 kOpusExtraData, | |
273 ARRAYSIZE_UNSAFE(kOpusExtraData), | |
274 false, | |
275 false, | |
276 base::TimeDelta::FromMilliseconds(80), | |
277 0); | |
278 InitializeDecoder(decoder_config); | |
279 Stop(); | |
280 } | |
281 | |
282 TEST_P(AudioDecoderTest, ProduceAudioSamples) { | |
283 Initialize(); | |
284 | |
285 // Run the test multiple times with a seek back to the beginning in between. | |
286 std::vector<std::string> decoded_audio_md5_hashes; | |
287 for (int i = 0; i < 2; ++i) { | |
288 for (int j = 0; j < kDecodeRuns; ++j) { | |
289 Decode(); | |
290 | |
291 // On the first pass record the exact MD5 hash for each decoded buffer. | |
292 if (i == 0) | |
293 decoded_audio_md5_hashes.push_back(GetDecodedAudioMD5(j)); | |
294 } | |
295 | |
296 ASSERT_EQ(static_cast<size_t>(kDecodeRuns), decoded_audio_size()); | |
297 | |
298 // On the first pass verify the basic audio hash and sample info. On the | |
299 // second, verify the exact MD5 sum for each packet. It shouldn't change. | |
300 for (int j = 0; j < kDecodeRuns; ++j) | |
301 ExpectDecodedAudio(j, i == 0 ? "" : decoded_audio_md5_hashes[j]); | |
302 | |
303 // Call one more time to trigger EOS. | |
304 SendEndOfStream(); | |
305 | |
306 ASSERT_EQ(static_cast<size_t>(kDecodeRuns + 1), decoded_audio_size()); | |
307 ExpectEndOfStream(kDecodeRuns); | |
308 | |
309 // Seek back to the beginning. | |
310 Seek(start_timestamp()); | |
311 } | |
312 | |
313 Stop(); | |
314 } | |
315 | |
316 TEST_P(AudioDecoderTest, DecodeAbort) { | |
317 Initialize(); | |
318 Decode(); | |
319 Stop(); | |
wolenetz
2014/06/06 21:00:04
We had some further verification in previous FFmpe
DaleCurtis
2014/06/12 22:00:48
Fixed. Exposed a bug in the opus decoder!
| |
320 } | |
321 | |
322 TEST_P(AudioDecoderTest, PendingDecode_Stop) { | |
323 Initialize(); | |
324 Decode(); | |
325 Stop(); | |
326 SatisfyPendingDecode(); | |
wolenetz
2014/06/06 21:00:04
Is there some deterministic [partial/empty] decode
DaleCurtis
2014/06/06 21:17:00
I don't understand what you're asking. ProduceAudi
wolenetz
2014/06/06 21:52:31
Sorry. I'm confused about the difference between t
DaleCurtis
2014/06/12 22:00:47
Reworked since this covers the Decode() -> Stop()
| |
327 } | |
328 | |
329 TEST_P(AudioDecoderTest, PendingDecode_Reset) { | |
330 Initialize(); | |
331 Decode(); | |
332 Reset(); | |
333 SatisfyPendingDecode(); | |
334 Stop(); | |
wolenetz
2014/06/06 21:00:03
ditto
DaleCurtis
2014/06/12 22:00:47
Nuked. ProduceAudioSamples covers this now. The
| |
335 } | |
336 | |
337 TEST_P(AudioDecoderTest, PendingDecode_ResetStop) { | |
338 Initialize(); | |
339 Decode(); | |
340 Reset(); | |
341 Stop(); | |
342 SatisfyPendingDecode(); | |
wolenetz
2014/06/06 21:00:05
ditto
DaleCurtis
2014/06/12 22:00:47
Nuked, ProduceAudioSamples covers the Reset() -> S
| |
343 } | |
344 | |
345 const AudioSample kBearOpusSamples[] = { | |
346 {0, 3500, "-0.26,0.87,1.36,0.84,-0.30,-1.22,"}, | |
347 {3500, 10000, "0.09,0.23,0.21,0.03,-0.17,-0.24,"}, | |
348 {13500, 10000, "0.10,0.24,0.23,0.04,-0.14,-0.23,"}, | |
349 }; | |
350 | |
351 const AudioDecoderTestData kOpusTests[] = { | |
352 {OPUS_DECODER, "bear-opus.ogg", kBearOpusSamples}, | |
353 }; | |
354 | |
355 INSTANTIATE_TEST_CASE_P(OpusAudioDecoderTest, | |
356 AudioDecoderTest, | |
357 testing::ValuesIn(kOpusTests)); | |
358 | |
359 #if defined(USE_PROPRIETARY_CODECS) | |
360 const AudioSample kSfxMp3Samples[] = { | |
361 {0, 1065, "2.81,3.99,4.53,4.10,3.08,2.46,"}, | |
362 {1065, 26122, "-3.81,-4.14,-3.90,-3.36,-3.03,-3.23,"}, | |
363 {27188, 26122, "4.24,3.95,4.22,4.78,5.13,4.93,"}, | |
364 }; | |
365 | |
366 const AudioSample kSfxAdtsSamples[] = { | |
367 {0, 23219, "-1.90,-1.53,-0.15,1.28,1.23,-0.33,"}, | |
368 {23219, 23219, "0.54,0.88,2.19,3.54,3.24,1.63,"}, | |
369 {46439, 23219, "1.42,1.69,2.95,4.23,4.02,2.36,"}, | |
370 }; | |
371 #endif | |
372 | |
373 #if defined(OS_CHROMEOS) | |
374 const AudioSample kBearFlacSamples[] = { | |
375 {0, 104489, "-2.24,-0.86,0.82,2.06,1.41,-0.16,"}, | |
376 {104489, 104489, "-1.58,-0.52,1.70,2.34,2.06,-0.05,"}, | |
377 {208979, 104489, "-2.17,-0.75,1.03,2.14,1.41,-0.25,"}, | |
378 }; | |
379 #endif | |
380 | |
381 const AudioSample kBearOgvSamples[] = { | |
382 {0, 13061, "-2.09,-0.21,1.34,2.09,0.76,-0.95,"}, | |
383 {13061, 23219, "-1.44,-1.27,0.18,1.37,1.95,0.13,"}, | |
384 {36281, 23219, "-1.80,-1.41,-0.13,1.30,1.65,0.01,"}, | |
385 }; | |
386 | |
387 const AudioSample kSfxWaveSamples[] = { | |
388 {0, 23219, "-1.23,-0.87,0.47,1.85,1.88,0.29,"}, | |
389 {23219, 23219, "0.75,1.10,2.43,3.78,3.53,1.93,"}, | |
390 {46439, 23219, "1.27,1.56,2.83,4.13,3.87,2.23,"}, | |
391 }; | |
392 | |
393 const AudioDecoderTestData kFFmpegTests[] = { | |
394 #if defined(USE_PROPRIETARY_CODECS) | |
395 {FFMPEG_DECODER, "sfx.mp3", kSfxMp3Samples}, | |
396 {FFMPEG_DECODER, "sfx.adts", kSfxAdtsSamples}, | |
397 #endif | |
398 #if defined(OS_CHROMEOS) | |
399 {FFMPEG_DECODER, "bear.flac", kBearFlacSamples}, | |
400 #endif | |
401 {FFMPEG_DECODER, "sfx_f32le.wav", kSfxWaveSamples}, | |
402 {FFMPEG_DECODER, "bear.ogv", kBearOgvSamples}, | |
403 }; | |
404 | |
405 INSTANTIATE_TEST_CASE_P(FFmpegAudioDecoderTest, | |
406 AudioDecoderTest, | |
407 testing::ValuesIn(kFFmpegTests)); | |
408 | |
409 } // namespace media | |
OLD | NEW |