OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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/logging.h" | |
7 #include "base/thread_task_runner_handle.h" | |
8 #include "base/timer/timer.h" | |
9 #include "media/base/android/media_codec_audio_decoder.h" | |
10 #include "media/base/android/media_codec_bridge.h" | |
11 #include "media/base/android/media_codec_video_decoder.h" | |
12 #include "media/base/android/test_data_factory.h" | |
13 #include "testing/gtest/include/gtest/gtest.h" | |
14 #include "ui/gl/android/surface_texture.h" | |
15 | |
16 namespace media { | |
17 | |
18 // Helper macro to skip the test if MediaCodecBridge isn't available. | |
19 #define SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE() \ | |
20 do { \ | |
21 if (!MediaCodecBridge::IsAvailable()) { \ | |
22 VLOG(0) << "Could not run test - not supported on device."; \ | |
23 return; \ | |
24 } \ | |
25 } while (0) | |
26 | |
27 namespace { | |
28 | |
29 const base::TimeDelta kDefaultTimeout = base::TimeDelta::FromMilliseconds(200); | |
30 const base::TimeDelta kAudioFramePeriod = base::TimeDelta::FromMilliseconds(20); | |
31 const base::TimeDelta kVideoFramePeriod = base::TimeDelta::FromMilliseconds(20); | |
32 | |
33 class AudioFactory : public TestDataFactory { | |
34 public: | |
35 AudioFactory(const base::TimeDelta& duration); | |
36 DemuxerConfigs GetConfigs(); | |
37 | |
38 protected: | |
39 void ModifyAccessUnit(int index_in_chunk, AccessUnit* unit) override; | |
40 }; | |
41 | |
42 class VideoFactory : public TestDataFactory { | |
43 public: | |
44 VideoFactory(const base::TimeDelta& duration); | |
45 DemuxerConfigs GetConfigs(); | |
46 | |
47 protected: | |
48 void ModifyAccessUnit(int index_in_chunk, AccessUnit* unit) override; | |
49 }; | |
50 | |
51 AudioFactory::AudioFactory(const base::TimeDelta& duration) | |
52 : TestDataFactory("vorbis-packet-%d", duration, kAudioFramePeriod) { | |
53 } | |
54 | |
55 DemuxerConfigs AudioFactory::GetConfigs() { | |
56 return TestDataFactory::CreateAudioConfigs(kCodecVorbis, duration_); | |
57 } | |
58 | |
59 void AudioFactory::ModifyAccessUnit(int index_in_chunk, AccessUnit* unit) { | |
60 // Vorbis needs 4 extra bytes padding on Android to decode properly. Check | |
61 // NuMediaExtractor.cpp in Android source code. | |
62 uint8 padding[4] = {0xff, 0xff, 0xff, 0xff}; | |
63 unit->data.insert(unit->data.end(), padding, padding + 4); | |
64 } | |
65 | |
66 VideoFactory::VideoFactory(const base::TimeDelta& duration) | |
67 : TestDataFactory("h264-320x180-frame-%d", duration, kVideoFramePeriod) { | |
68 } | |
69 | |
70 DemuxerConfigs VideoFactory::GetConfigs() { | |
71 return TestDataFactory::CreateVideoConfigs(kCodecH264, duration_, | |
72 gfx::Size(320, 180)); | |
73 } | |
74 | |
75 void VideoFactory::ModifyAccessUnit(int index_in_chunk, AccessUnit* unit) { | |
76 // The frames are taken from High profile and some are B-frames. | |
77 // The first 4 frames appear in the file in the following order: | |
78 // | |
79 // Frames: I P B P | |
80 // Decoding order: 0 1 2 3 | |
81 // Presentation order: 0 2 1 4(3) | |
82 // | |
83 // I keep the last PTS to be 3 for simplicity. | |
84 | |
85 // Swap pts for second and third frames. | |
86 if (index_in_chunk == 1) // second frame | |
87 unit->timestamp += frame_period_; | |
88 if (index_in_chunk == 2) // third frame | |
89 unit->timestamp -= frame_period_; | |
90 | |
91 if (index_in_chunk == 0) | |
92 unit->is_key_frame = true; | |
93 } | |
94 | |
95 // Class that computes statistics: number of calls, minimum and maximum values. | |
96 // It is used for PTS statistics to verify that playback did actually happen. | |
97 | |
98 template <typename T> | |
99 class Minimax { | |
100 public: | |
101 Minimax() : num_values_(0) {} | |
102 ~Minimax() {} | |
103 | |
104 void AddValue(const T& value) { | |
105 ++num_values_; | |
106 if (value < min_) | |
107 min_ = value; | |
108 else if (max_ < value) | |
109 max_ = value; | |
110 } | |
111 | |
112 const T& min() const { return min_; } | |
113 const T& max() const { return max_; } | |
114 int num_values() const { return num_values_; } | |
115 | |
116 private: | |
117 T min_; | |
118 T max_; | |
119 int num_values_; | |
120 }; | |
121 | |
122 } // namespace (anonymous) | |
123 | |
124 // The test fixture for MediaCodecDecoder | |
125 | |
126 class MediaCodecDecoderTest : public testing::Test { | |
127 public: | |
128 MediaCodecDecoderTest(); | |
129 ~MediaCodecDecoderTest() override; | |
130 | |
131 // Conditions we wait for. | |
132 bool is_prefetched() const { return is_prefetched_; } | |
133 bool is_stopped() const { return is_stopped_; } | |
134 bool is_starved() const { return is_starved_; } | |
135 | |
136 // Prefetch callback has to be public. | |
137 void SetPrefetched() { is_prefetched_ = true; } | |
138 | |
139 protected: | |
140 typedef base::Callback<bool()> Predicate; | |
141 | |
142 typedef base::Callback<void(const DemuxerData&)> DataAvailableCallback; | |
143 | |
144 // Waits for condition to become true or for timeout to expire. | |
145 // Returns true if the condition becomes true. | |
146 bool WaitForCondition(const Predicate& condition, | |
147 const base::TimeDelta& timeout = kDefaultTimeout); | |
148 | |
149 void SetDataFactory(scoped_refptr<TestDataFactory> factory) { | |
150 data_factory_ = factory; | |
151 } | |
152 | |
153 void CreateAudioDecoder(); | |
154 void CreateVideoDecoder(); | |
155 void SetVideoSurface(); | |
156 | |
157 // Decoder callbacks. | |
158 void OnDataRequested(); | |
159 void OnStarvation() { is_starved_ = true; } | |
160 void OnStopDone() { is_stopped_ = true; } | |
161 void OnError() {} | |
162 void OnUpdateCurrentTime(base::TimeDelta now_playing, | |
163 base::TimeDelta last_buffered) { | |
164 pts_stat_.AddValue(now_playing); | |
165 } | |
166 void OnVideoSizeChanged(const gfx::Size& video_size) {} | |
167 void OnVideoCodecCreated() {} | |
168 | |
169 scoped_ptr<MediaCodecDecoder> decoder_; | |
170 scoped_refptr<TestDataFactory> data_factory_; | |
171 Minimax<base::TimeDelta> pts_stat_; | |
172 | |
173 private: | |
174 bool is_timeout_expired() const { return is_timeout_expired_; } | |
175 void SetTimeoutExpired(bool value) { is_timeout_expired_ = value; } | |
176 | |
177 base::MessageLoop message_loop_; | |
178 bool is_timeout_expired_; | |
179 | |
180 bool is_prefetched_; | |
181 bool is_stopped_; | |
182 bool is_starved_; | |
183 | |
184 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | |
185 DataAvailableCallback data_available_cb_; | |
186 scoped_refptr<gfx::SurfaceTexture> surface_texture_; | |
187 | |
188 DISALLOW_COPY_AND_ASSIGN(MediaCodecDecoderTest); | |
189 }; | |
190 | |
191 MediaCodecDecoderTest::MediaCodecDecoderTest() | |
192 : is_timeout_expired_(false), | |
193 is_prefetched_(false), | |
194 is_stopped_(false), | |
195 is_starved_(false), | |
196 task_runner_(base::ThreadTaskRunnerHandle::Get()) { | |
197 } | |
198 | |
199 MediaCodecDecoderTest::~MediaCodecDecoderTest() { | |
200 } | |
qinmin
2015/06/18 00:24:40
this can go to the previous line
Tima Vaisburd
2015/06/18 19:34:04
Done.
| |
201 | |
202 bool MediaCodecDecoderTest::WaitForCondition(const Predicate& condition, | |
203 const base::TimeDelta& timeout) { | |
204 // Let the message_loop_ process events. | |
205 // We start the timer and RunUntilIdle() until it signals. | |
206 | |
207 SetTimeoutExpired(false); | |
208 | |
209 base::Timer timer(false, false); | |
210 timer.Start(FROM_HERE, timeout, | |
211 base::Bind(&MediaCodecDecoderTest::SetTimeoutExpired, | |
212 base::Unretained(this), true)); | |
213 | |
214 do { | |
215 if (condition.Run()) { | |
216 timer.Stop(); | |
217 return true; | |
218 } | |
219 message_loop_.RunUntilIdle(); | |
220 } while (!is_timeout_expired()); | |
221 | |
222 DCHECK(!timer.IsRunning()); | |
223 return false; | |
224 } | |
225 | |
226 void MediaCodecDecoderTest::CreateAudioDecoder() { | |
227 decoder_ = scoped_ptr<MediaCodecDecoder>(new MediaCodecAudioDecoder( | |
228 task_runner_, base::Bind(&MediaCodecDecoderTest::OnDataRequested, | |
229 base::Unretained(this)), | |
230 base::Bind(&MediaCodecDecoderTest::OnStarvation, base::Unretained(this)), | |
231 base::Bind(&MediaCodecDecoderTest::OnStopDone, base::Unretained(this)), | |
232 base::Bind(&MediaCodecDecoderTest::OnError, base::Unretained(this)), | |
233 base::Bind(&MediaCodecDecoderTest::OnUpdateCurrentTime, | |
234 base::Unretained(this)))); | |
235 | |
236 data_available_cb_ = base::Bind(&MediaCodecDecoder::OnDemuxerDataAvailable, | |
237 base::Unretained(decoder_.get())); | |
238 } | |
239 | |
240 void MediaCodecDecoderTest::CreateVideoDecoder() { | |
241 decoder_ = scoped_ptr<MediaCodecDecoder>(new MediaCodecVideoDecoder( | |
242 task_runner_, base::Bind(&MediaCodecDecoderTest::OnDataRequested, | |
243 base::Unretained(this)), | |
244 base::Bind(&MediaCodecDecoderTest::OnStarvation, base::Unretained(this)), | |
245 base::Bind(&MediaCodecDecoderTest::OnStopDone, base::Unretained(this)), | |
246 base::Bind(&MediaCodecDecoderTest::OnError, base::Unretained(this)), | |
247 base::Bind(&MediaCodecDecoderTest::OnUpdateCurrentTime, | |
248 base::Unretained(this)), | |
249 base::Bind(&MediaCodecDecoderTest::OnVideoSizeChanged, | |
250 base::Unretained(this)), | |
251 base::Bind(&MediaCodecDecoderTest::OnVideoCodecCreated, | |
252 base::Unretained(this)))); | |
253 | |
254 data_available_cb_ = base::Bind(&MediaCodecDecoder::OnDemuxerDataAvailable, | |
255 base::Unretained(decoder_.get())); | |
256 } | |
257 | |
258 void MediaCodecDecoderTest::OnDataRequested() { | |
259 if (!data_factory_) | |
260 return; | |
261 | |
262 DemuxerData data; | |
263 base::TimeDelta delay; | |
264 data_factory_->CreateChunk(&data, &delay); | |
265 | |
266 task_runner_->PostDelayedTask(FROM_HERE, base::Bind(data_available_cb_, data), | |
267 delay); | |
268 } | |
269 | |
270 void MediaCodecDecoderTest::SetVideoSurface() { | |
271 surface_texture_ = gfx::SurfaceTexture::Create(0); | |
272 gfx::ScopedJavaSurface surface(surface_texture_.get()); | |
273 ASSERT_NE(nullptr, decoder_.get()); | |
274 MediaCodecVideoDecoder* video_decoder = | |
275 static_cast<MediaCodecVideoDecoder*>(decoder_.get()); | |
276 video_decoder->SetPendingSurface(surface.Pass()); | |
277 } | |
278 | |
279 TEST_F(MediaCodecDecoderTest, AudioPrefetch) { | |
280 CreateAudioDecoder(); | |
281 | |
282 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500); | |
283 SetDataFactory(scoped_refptr<TestDataFactory>(new AudioFactory(duration))); | |
284 | |
285 decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched, | |
286 base::Unretained(this))); | |
287 | |
288 EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched, | |
289 base::Unretained(this)))); | |
290 } | |
291 | |
292 TEST_F(MediaCodecDecoderTest, VideoPrefetch) { | |
293 CreateVideoDecoder(); | |
294 | |
295 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500); | |
296 SetDataFactory(scoped_refptr<TestDataFactory>(new VideoFactory(duration))); | |
297 | |
298 decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched, | |
299 base::Unretained(this))); | |
300 | |
301 EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched, | |
302 base::Unretained(this)))); | |
303 } | |
304 | |
305 TEST_F(MediaCodecDecoderTest, AudioConfigureNoParams) { | |
306 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); | |
307 | |
308 CreateAudioDecoder(); | |
309 | |
310 // Cannot configure without config parameters. | |
311 EXPECT_EQ(MediaCodecDecoder::CONFIG_FAILURE, decoder_->Configure()); | |
312 } | |
313 | |
314 TEST_F(MediaCodecDecoderTest, AudioConfigureValidParams) { | |
315 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); | |
316 | |
317 CreateAudioDecoder(); | |
318 | |
319 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500); | |
320 scoped_refptr<AudioFactory> factory(new AudioFactory(duration)); | |
321 decoder_->SetDemuxerConfigs(factory->GetConfigs()); | |
322 | |
323 EXPECT_EQ(MediaCodecDecoder::CONFIG_OK, decoder_->Configure()); | |
324 } | |
325 | |
326 TEST_F(MediaCodecDecoderTest, VideoConfigureNoParams) { | |
327 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); | |
328 | |
329 CreateVideoDecoder(); | |
330 | |
331 // Cannot configure without config parameters. | |
332 EXPECT_EQ(MediaCodecDecoder::CONFIG_FAILURE, decoder_->Configure()); | |
333 } | |
334 | |
335 TEST_F(MediaCodecDecoderTest, VideoConfigureNoSurface) { | |
336 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); | |
337 | |
338 CreateVideoDecoder(); | |
339 | |
340 // decoder_->Configure() searches back for the key frame. | |
341 // We have to prefetch decoder. | |
342 | |
343 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500); | |
344 scoped_refptr<VideoFactory> factory(new VideoFactory(duration)); | |
345 SetDataFactory(factory); | |
346 | |
347 decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched, | |
348 base::Unretained(this))); | |
349 | |
350 EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched, | |
351 base::Unretained(this)))); | |
352 | |
353 decoder_->SetDemuxerConfigs(factory->GetConfigs()); | |
354 | |
355 // Surface is not set, Configure() should fail. | |
356 | |
357 EXPECT_EQ(MediaCodecDecoder::CONFIG_FAILURE, decoder_->Configure()); | |
358 } | |
359 | |
360 TEST_F(MediaCodecDecoderTest, VideoConfigureInvalidSurface) { | |
361 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); | |
362 | |
363 CreateVideoDecoder(); | |
364 | |
365 // decoder_->Configure() searches back for the key frame. | |
366 // We have to prefetch decoder. | |
367 | |
368 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500); | |
369 scoped_refptr<VideoFactory> factory(new VideoFactory(duration)); | |
370 SetDataFactory(factory); | |
371 | |
372 decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched, | |
373 base::Unretained(this))); | |
374 | |
375 EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched, | |
376 base::Unretained(this)))); | |
377 | |
378 decoder_->SetDemuxerConfigs(factory->GetConfigs()); | |
379 | |
380 // Prepare the surface. | |
381 scoped_refptr<gfx::SurfaceTexture> surface_texture( | |
382 gfx::SurfaceTexture::Create(0)); | |
383 gfx::ScopedJavaSurface surface(surface_texture.get()); | |
384 | |
385 // Release the surface texture. | |
386 surface_texture = NULL; | |
387 | |
388 MediaCodecVideoDecoder* video_decoder = | |
389 static_cast<MediaCodecVideoDecoder*>(decoder_.get()); | |
390 video_decoder->SetPendingSurface(surface.Pass()); | |
391 | |
392 EXPECT_EQ(MediaCodecDecoder::CONFIG_FAILURE, decoder_->Configure()); | |
393 } | |
394 | |
395 TEST_F(MediaCodecDecoderTest, VideoConfigureValidParams) { | |
396 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); | |
397 | |
398 CreateVideoDecoder(); | |
399 | |
400 // decoder_->Configure() searches back for the key frame. | |
401 // We have to prefetch decoder. | |
402 | |
403 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500); | |
404 scoped_refptr<VideoFactory> factory(new VideoFactory(duration)); | |
405 SetDataFactory(factory); | |
406 | |
407 decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched, | |
408 base::Unretained(this))); | |
409 | |
410 EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched, | |
411 base::Unretained(this)))); | |
412 | |
413 decoder_->SetDemuxerConfigs(factory->GetConfigs()); | |
414 | |
415 SetVideoSurface(); | |
416 | |
417 // Now we can expect Configure() to succeed. | |
418 | |
419 EXPECT_EQ(MediaCodecDecoder::CONFIG_OK, decoder_->Configure()); | |
420 } | |
421 | |
422 TEST_F(MediaCodecDecoderTest, AudioStartWithoutConfigure) { | |
423 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); | |
424 | |
425 CreateAudioDecoder(); | |
426 | |
427 // Decoder has to be prefetched and configured before the start. | |
428 | |
429 // Wrong state: not prefetched | |
430 EXPECT_FALSE(decoder_->Start(base::TimeDelta::FromMilliseconds(0))); | |
431 | |
432 // Do the prefetch. | |
433 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500); | |
434 scoped_refptr<AudioFactory> factory(new AudioFactory(duration)); | |
435 SetDataFactory(factory); | |
436 | |
437 // Prefetch to avoid starvation at the beginning of playback. | |
438 decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched, | |
439 base::Unretained(this))); | |
440 | |
441 EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched, | |
442 base::Unretained(this)))); | |
443 | |
444 // Still, decoder is not configured. | |
445 EXPECT_FALSE(decoder_->Start(base::TimeDelta::FromMilliseconds(0))); | |
446 } | |
447 | |
448 TEST_F(MediaCodecDecoderTest, AudioPlayTillCompletion) { | |
449 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); | |
450 | |
451 CreateAudioDecoder(); | |
452 | |
453 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500); | |
454 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(600); | |
455 scoped_refptr<AudioFactory> factory(new AudioFactory(duration)); | |
456 SetDataFactory(factory); | |
457 | |
458 // Prefetch to avoid starvation at the beginning of playback. | |
459 decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched, | |
460 base::Unretained(this))); | |
461 | |
462 EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched, | |
463 base::Unretained(this)))); | |
464 | |
465 decoder_->SetDemuxerConfigs(factory->GetConfigs()); | |
466 | |
467 EXPECT_EQ(MediaCodecDecoder::CONFIG_OK, decoder_->Configure()); | |
468 | |
469 EXPECT_TRUE(decoder_->Start(base::TimeDelta::FromMilliseconds(0))); | |
470 | |
471 EXPECT_TRUE(WaitForCondition( | |
472 base::Bind(&MediaCodecDecoderTest::is_stopped, base::Unretained(this)), | |
473 timeout)); | |
474 | |
475 EXPECT_TRUE(decoder_->IsStopped()); | |
476 EXPECT_TRUE(decoder_->IsCompleted()); | |
477 | |
478 // It is hard to properly estimate minimum and maximum values because | |
479 // reported times are different from PTS. | |
480 EXPECT_EQ(25, pts_stat_.num_values()); | |
481 } | |
482 | |
483 TEST_F(MediaCodecDecoderTest, VideoPlayTillCompletion) { | |
484 SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); | |
485 | |
486 CreateVideoDecoder(); | |
487 | |
488 base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500); | |
489 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(600); | |
490 scoped_refptr<VideoFactory> factory(new VideoFactory(duration)); | |
491 SetDataFactory(factory); | |
492 | |
493 // Prefetch | |
494 decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched, | |
495 base::Unretained(this))); | |
496 | |
497 EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched, | |
498 base::Unretained(this)))); | |
499 | |
500 decoder_->SetDemuxerConfigs(factory->GetConfigs()); | |
501 | |
502 SetVideoSurface(); | |
503 | |
504 EXPECT_EQ(MediaCodecDecoder::CONFIG_OK, decoder_->Configure()); | |
505 | |
506 EXPECT_TRUE(decoder_->Start(base::TimeDelta::FromMilliseconds(0))); | |
507 | |
508 EXPECT_TRUE(WaitForCondition( | |
509 base::Bind(&MediaCodecDecoderTest::is_stopped, base::Unretained(this)), | |
510 timeout)); | |
511 | |
512 EXPECT_TRUE(decoder_->IsStopped()); | |
513 EXPECT_TRUE(decoder_->IsCompleted()); | |
514 | |
515 EXPECT_EQ(26, pts_stat_.num_values()); | |
516 EXPECT_EQ(data_factory_->last_pts(), pts_stat_.max()); | |
517 } | |
518 | |
519 } // namespace media | |
OLD | NEW |