1 // Copyright (c) 2012 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.
5 // The bulk of this file is support code; sorry about that. Here's an overview
6 // to hopefully help readers of this code:
7 // - RenderingHelper is charged with interacting with X11/{EGL/GLES2,GLX/GL} or
9 // - ClientState is an enum for the state of the decode client used by the test.
10 // - ClientStateNotification is a barrier abstraction that allows the test code
11 // to be written sequentially and wait for the decode client to see certain
13 // - GLRenderingVDAClient is a VideoDecodeAccelerator::Client implementation
14 // - Finally actual TEST cases are at the bottom of this file, using the above
19 #include <sys/types.h>
24 // Include gtest.h out of order because <X11/X.h> #define's Bool & None, which
25 // gtest uses as struct names (inside a namespace). This means that
26 // #include'ing gtest after anything that pulls in X.h fails to compile.
27 // This is http://code.google.com/p/googletest/issues/detail?id=371
28 #include "testing/gtest/include/gtest/gtest.h"
30 #include "base/at_exit.h"
31 #include "base/bind.h"
32 #include "base/command_line.h"
33 #include "base/file_util.h"
34 #include "base/format_macros.h"
36 #include "base/message_loop/message_loop_proxy.h"
37 #include "base/platform_file.h"
38 #include "base/process/process.h"
39 #include "base/stl_util.h"
40 #include "base/strings/string_number_conversions.h"
41 #include "base/strings/string_split.h"
42 #include "base/strings/stringize_macros.h"
43 #include "base/strings/stringprintf.h"
44 #include "base/strings/utf_string_conversions.h"
45 #include "base/synchronization/condition_variable.h"
46 #include "base/synchronization/lock.h"
47 #include "base/synchronization/waitable_event.h"
48 #include "base/threading/thread.h"
49 #include "content/common/gpu/media/rendering_helper.h"
50 #include "content/common/gpu/media/video_accelerator_unittest_helpers.h"
51 #include "content/public/common/content_switches.h"
52 #include "media/filters/h264_parser.h"
53 #include "ui/gfx/codec/png_codec.h"
56 #include "content/common/gpu/media/dxva_video_decode_accelerator.h"
57 #elif defined(OS_CHROMEOS) || defined(OS_LINUX)
58 #if defined(ARCH_CPU_ARMEL)
59 #include "content/common/gpu/media/v4l2_video_decode_accelerator.h"
60 #include "content/common/gpu/media/v4l2_video_device.h"
61 #elif defined(ARCH_CPU_X86_FAMILY)
62 #include "content/common/gpu/media/vaapi_video_decode_accelerator.h"
63 #include "content/common/gpu/media/vaapi_wrapper.h"
65 #include "ui/gl/gl_implementation.h"
67 #endif // ARCH_CPU_ARMEL
69 #error The VideoAccelerator tests are not supported on this platform.
72 using media::VideoDecodeAccelerator;
77 // Values optionally filled in from flags; see main() below.
78 // The syntax of multiple test videos is:
79 // test-video1;test-video2;test-video3
80 // where only the first video is required and other optional videos would be
81 // decoded by concurrent decoders.
82 // The syntax of each test-video is:
83 // filename:width:height:numframes:numfragments:minFPSwithRender:minFPSnoRender
84 // where only the first field is required. Value details:
85 // - |filename| must be an h264 Annex B (NAL) stream or an IVF VP8 stream.
86 // - |width| and |height| are in pixels.
87 // - |numframes| is the number of picture frames in the file.
88 // - |numfragments| NALU (h264) or frame (VP8) count in the stream.
89 // - |minFPSwithRender| and |minFPSnoRender| are minimum frames/second speeds
90 // expected to be achieved with and without rendering to the screen, resp.
91 // (the latter tests just decode speed).
92 // - |profile| is the media::VideoCodecProfile set during Initialization.
93 // An empty value for a numeric field means "ignore".
94 const base::FilePath::CharType* g_test_video_data =
95 // FILE_PATH_LITERAL("test-25fps.vp8:320:240:250:250:50:175:11");
96 FILE_PATH_LITERAL("test-25fps.h264:320:240:250:258:50:175:1");
98 // The file path of the test output log. This is used to communicate the test
99 // results to CrOS autotests. We can enable the log and specify the filename by
100 // the "--output_log" switch.
101 const base::FilePath::CharType* g_output_log = NULL;
103 // The value is set by the switch "--rendering_fps".
104 double g_rendering_fps = 0;
106 // Disable rendering, the value is set by the switch "--disable_rendering".
107 bool g_disable_rendering = false;
109 // Magic constants for differentiating the reasons for NotifyResetDone being
112 // Reset() just after calling Decode() with a fragment containing config info.
113 RESET_AFTER_FIRST_CONFIG_INFO = -4,
114 START_OF_STREAM_RESET = -3,
115 MID_STREAM_RESET = -2,
116 END_OF_STREAM_RESET = -1
119 const int kMaxResetAfterFrameNum = 100;
120 const int kMaxFramesToDelayReuse = 64;
121 const base::TimeDelta kReuseDelay = base::TimeDelta::FromSeconds(1);
122 // Simulate WebRTC and call VDA::Decode 30 times per second.
123 const int kWebRtcDecodeCallsPerSecond = 30;
125 struct TestVideoFile {
126 explicit TestVideoFile(base::FilePath::StringType file_name)
127 : file_name(file_name),
133 min_fps_no_render(-1),
134 profile(media::VIDEO_CODEC_PROFILE_UNKNOWN),
135 reset_after_frame_num(END_OF_STREAM_RESET) {
138 base::FilePath::StringType file_name;
144 int min_fps_no_render;
145 media::VideoCodecProfile profile;
146 int reset_after_frame_num;
147 std::string data_str;
150 // Presumed minimal display size.
151 // We subtract one pixel from the width because some ARM chromebooks do not
152 // support two fullscreen app running at the same time. See crbug.com/270064.
153 const gfx::Size kThumbnailsDisplaySize(1366 - 1, 768);
154 const gfx::Size kThumbnailsPageSize(1600, 1200);
155 const gfx::Size kThumbnailSize(160, 120);
156 const int kMD5StringLength = 32;
158 // Read in golden MD5s for the thumbnailed rendering of this video
159 void ReadGoldenThumbnailMD5s(const TestVideoFile* video_file,
160 std::vector<std::string>* md5_strings) {
161 base::FilePath filepath(video_file->file_name);
162 filepath = filepath.AddExtension(FILE_PATH_LITERAL(".md5"));
163 std::string all_md5s;
164 base::ReadFileToString(filepath, &all_md5s);
165 base::SplitString(all_md5s, '\n', md5_strings);
166 // Check these are legitimate MD5s.
167 for (std::vector<std::string>::iterator md5_string = md5_strings->begin();
168 md5_string != md5_strings->end(); ++md5_string) {
169 // Ignore the empty string added by SplitString
170 if (!md5_string->length())
173 if (md5_string->at(0) == '#')
176 CHECK_EQ(static_cast<int>(md5_string->length()),
177 kMD5StringLength) << *md5_string;
178 bool hex_only = std::count_if(md5_string->begin(),
179 md5_string->end(), isxdigit) ==
181 CHECK(hex_only) << *md5_string;
183 CHECK_GE(md5_strings->size(), 1U) << all_md5s;
186 // State of the GLRenderingVDAClient below. Order matters here as the test
187 // makes assumptions about it.
198 CS_MAX, // Must be last entry.
201 // A wrapper client that throttles the PictureReady callbacks to a given rate.
202 // It may drops or queues frame to deliver them on time.
203 class ThrottlingVDAClient : public VideoDecodeAccelerator::Client,
204 public base::SupportsWeakPtr<ThrottlingVDAClient> {
206 // Callback invoked whan the picture is dropped and should be reused for
207 // the decoder again.
208 typedef base::Callback<void(int32 picture_buffer_id)> ReusePictureCB;
210 ThrottlingVDAClient(VideoDecodeAccelerator::Client* client,
212 ReusePictureCB reuse_picture_cb);
213 virtual ~ThrottlingVDAClient();
215 // VideoDecodeAccelerator::Client implementation
216 virtual void ProvidePictureBuffers(uint32 requested_num_of_buffers,
217 const gfx::Size& dimensions,
218 uint32 texture_target) OVERRIDE;
219 virtual void DismissPictureBuffer(int32 picture_buffer_id) OVERRIDE;
220 virtual void PictureReady(const media::Picture& picture) OVERRIDE;
221 virtual void NotifyInitializeDone() OVERRIDE;
222 virtual void NotifyEndOfBitstreamBuffer(int32 bitstream_buffer_id) OVERRIDE;
223 virtual void NotifyFlushDone() OVERRIDE;
224 virtual void NotifyResetDone() OVERRIDE;
225 virtual void NotifyError(VideoDecodeAccelerator::Error error) OVERRIDE;
227 int num_decoded_frames() { return num_decoded_frames_; }
231 void CallClientPictureReady(int version);
233 VideoDecodeAccelerator::Client* client_;
234 ReusePictureCB reuse_picture_cb_;
235 base::TimeTicks next_frame_delivered_time_;
236 base::TimeDelta frame_duration_;
238 int num_decoded_frames_;
240 std::deque<media::Picture> pending_pictures_;
242 DISALLOW_IMPLICIT_CONSTRUCTORS(ThrottlingVDAClient);
245 ThrottlingVDAClient::ThrottlingVDAClient(VideoDecodeAccelerator::Client* client,
247 ReusePictureCB reuse_picture_cb)
249 reuse_picture_cb_(reuse_picture_cb),
250 num_decoded_frames_(0),
254 frame_duration_ = base::TimeDelta::FromSeconds(1) / fps;
257 ThrottlingVDAClient::~ThrottlingVDAClient() {}
259 void ThrottlingVDAClient::ProvidePictureBuffers(uint32 requested_num_of_buffers,
260 const gfx::Size& dimensions,
261 uint32 texture_target) {
262 client_->ProvidePictureBuffers(
263 requested_num_of_buffers, dimensions, texture_target);
266 void ThrottlingVDAClient::DismissPictureBuffer(int32 picture_buffer_id) {
267 client_->DismissPictureBuffer(picture_buffer_id);
270 void ThrottlingVDAClient::PictureReady(const media::Picture& picture) {
271 ++num_decoded_frames_;
273 if (pending_pictures_.empty()) {
274 base::TimeDelta delay =
275 next_frame_delivered_time_.is_null()
277 : next_frame_delivered_time_ - base::TimeTicks::Now();
278 base::MessageLoop::current()->PostDelayedTask(
280 base::Bind(&ThrottlingVDAClient::CallClientPictureReady,
285 pending_pictures_.push_back(picture);
288 void ThrottlingVDAClient::CallClientPictureReady(int version) {
289 // Just return if we have reset the decoder
290 if (version != stream_version_)
293 base::TimeTicks now = base::TimeTicks::Now();
295 if (next_frame_delivered_time_.is_null())
296 next_frame_delivered_time_ = now;
298 if (next_frame_delivered_time_ + frame_duration_ < now) {
299 // Too late, drop the frame
300 reuse_picture_cb_.Run(pending_pictures_.front().picture_buffer_id());
302 client_->PictureReady(pending_pictures_.front());
305 pending_pictures_.pop_front();
306 next_frame_delivered_time_ += frame_duration_;
307 if (!pending_pictures_.empty()) {
308 base::MessageLoop::current()->PostDelayedTask(
310 base::Bind(&ThrottlingVDAClient::CallClientPictureReady,
313 next_frame_delivered_time_ - base::TimeTicks::Now());
317 void ThrottlingVDAClient::NotifyInitializeDone() {
318 client_->NotifyInitializeDone();
321 void ThrottlingVDAClient::NotifyEndOfBitstreamBuffer(
322 int32 bitstream_buffer_id) {
323 client_->NotifyEndOfBitstreamBuffer(bitstream_buffer_id);
326 void ThrottlingVDAClient::NotifyFlushDone() {
327 if (!pending_pictures_.empty()) {
328 base::MessageLoop::current()->PostDelayedTask(
330 base::Bind(&ThrottlingVDAClient::NotifyFlushDone,
331 base::Unretained(this)),
332 next_frame_delivered_time_ - base::TimeTicks::Now());
335 client_->NotifyFlushDone();
338 void ThrottlingVDAClient::NotifyResetDone() {
340 while (!pending_pictures_.empty()) {
341 reuse_picture_cb_.Run(pending_pictures_.front().picture_buffer_id());
342 pending_pictures_.pop_front();
344 next_frame_delivered_time_ = base::TimeTicks();
345 client_->NotifyResetDone();
348 void ThrottlingVDAClient::NotifyError(VideoDecodeAccelerator::Error error) {
349 client_->NotifyError(error);
352 // Client that can accept callbacks from a VideoDecodeAccelerator and is used by
354 class GLRenderingVDAClient
355 : public VideoDecodeAccelerator::Client,
356 public base::SupportsWeakPtr<GLRenderingVDAClient> {
358 // Doesn't take ownership of |rendering_helper| or |note|, which must outlive
360 // |num_play_throughs| indicates how many times to play through the video.
361 // |reset_after_frame_num| can be a frame number >=0 indicating a mid-stream
362 // Reset() should be done after that frame number is delivered, or
363 // END_OF_STREAM_RESET to indicate no mid-stream Reset().
364 // |delete_decoder_state| indicates when the underlying decoder should be
365 // Destroy()'d and deleted and can take values: N<0: delete after -N Decode()
366 // calls have been made, N>=0 means interpret as ClientState.
367 // Both |reset_after_frame_num| & |delete_decoder_state| apply only to the
368 // last play-through (governed by |num_play_throughs|).
369 // |rendering_fps| indicates the target rendering fps. 0 means no target fps
370 // and it would render as fast as possible.
371 // |suppress_rendering| indicates GL rendering is suppressed or not.
372 // After |delay_reuse_after_frame_num| frame has been delivered, the client
373 // will start delaying the call to ReusePictureBuffer() for kReuseDelay.
374 // |decode_calls_per_second| is the number of VDA::Decode calls per second.
375 // If |decode_calls_per_second| > 0, |num_in_flight_decodes| must be 1.
376 GLRenderingVDAClient(RenderingHelper* rendering_helper,
377 int rendering_window_id,
378 ClientStateNotification<ClientState>* note,
379 const std::string& encoded_data,
380 int num_in_flight_decodes,
381 int num_play_throughs,
382 int reset_after_frame_num,
383 int delete_decoder_state,
386 media::VideoCodecProfile profile,
387 double rendering_fps,
388 bool suppress_rendering,
389 int delay_reuse_after_frame_num,
390 int decode_calls_per_second);
391 virtual ~GLRenderingVDAClient();
392 void CreateAndStartDecoder();
394 // VideoDecodeAccelerator::Client implementation.
395 // The heart of the Client.
396 virtual void ProvidePictureBuffers(uint32 requested_num_of_buffers,
397 const gfx::Size& dimensions,
398 uint32 texture_target) OVERRIDE;
399 virtual void DismissPictureBuffer(int32 picture_buffer_id) OVERRIDE;
400 virtual void PictureReady(const media::Picture& picture) OVERRIDE;
401 // Simple state changes.
402 virtual void NotifyInitializeDone() OVERRIDE;
403 virtual void NotifyEndOfBitstreamBuffer(int32 bitstream_buffer_id) OVERRIDE;
404 virtual void NotifyFlushDone() OVERRIDE;
405 virtual void NotifyResetDone() OVERRIDE;
406 virtual void NotifyError(VideoDecodeAccelerator::Error error) OVERRIDE;
408 void OutputFrameDeliveryTimes(base::PlatformFile output);
410 void NotifyFrameDropped(int32 picture_buffer_id);
412 // Simple getters for inspecting the state of the Client.
413 int num_done_bitstream_buffers() { return num_done_bitstream_buffers_; }
414 int num_skipped_fragments() { return num_skipped_fragments_; }
415 int num_queued_fragments() { return num_queued_fragments_; }
416 int num_decoded_frames();
417 double frames_per_second();
418 // Return the median of the decode time in milliseconds.
419 int decode_time_median();
420 bool decoder_deleted() { return !decoder_.get(); }
423 typedef std::map<int, media::PictureBuffer*> PictureBufferById;
425 void SetState(ClientState new_state);
427 // Delete the associated decoder helper.
428 void DeleteDecoder();
430 // Compute & return the first encoded bytes (including a start frame) to send
431 // to the decoder, starting at |start_pos| and returning one fragment. Skips
432 // to the first decodable position.
433 std::string GetBytesForFirstFragment(size_t start_pos, size_t* end_pos);
434 // Compute & return the encoded bytes of next fragment to send to the decoder
435 // (based on |start_pos|).
436 std::string GetBytesForNextFragment(size_t start_pos, size_t* end_pos);
437 // Helpers for GetBytesForNextFragment above.
438 void GetBytesForNextNALU(size_t start_pos, size_t* end_pos); // For h.264.
439 std::string GetBytesForNextFrame(
440 size_t start_pos, size_t* end_pos); // For VP8.
442 // Request decode of the next fragment in the encoded data.
443 void DecodeNextFragment();
445 RenderingHelper* rendering_helper_;
446 int rendering_window_id_;
447 std::string encoded_data_;
448 const int num_in_flight_decodes_;
449 int outstanding_decodes_;
450 size_t encoded_data_next_pos_to_decode_;
451 int next_bitstream_buffer_id_;
452 ClientStateNotification<ClientState>* note_;
453 scoped_ptr<VideoDecodeAccelerator> decoder_;
454 std::set<int> outstanding_texture_ids_;
455 int remaining_play_throughs_;
456 int reset_after_frame_num_;
457 int delete_decoder_state_;
459 int num_skipped_fragments_;
460 int num_queued_fragments_;
461 int num_decoded_frames_;
462 int num_done_bitstream_buffers_;
463 PictureBufferById picture_buffers_by_id_;
464 base::TimeTicks initialize_done_ticks_;
465 media::VideoCodecProfile profile_;
466 GLenum texture_target_;
467 bool suppress_rendering_;
468 std::vector<base::TimeTicks> frame_delivery_times_;
469 int delay_reuse_after_frame_num_;
470 scoped_ptr<ThrottlingVDAClient> throttling_client_;
471 // A map from bitstream buffer id to the decode start time of the buffer.
472 std::map<int, base::TimeTicks> decode_start_time_;
473 // The decode time of all decoded frames.
474 std::vector<base::TimeDelta> decode_time_;
475 // The number of VDA::Decode calls per second. This is to simulate webrtc.
476 int decode_calls_per_second_;
478 DISALLOW_IMPLICIT_CONSTRUCTORS(GLRenderingVDAClient);
481 GLRenderingVDAClient::GLRenderingVDAClient(
482 RenderingHelper* rendering_helper,
483 int rendering_window_id,
484 ClientStateNotification<ClientState>* note,
485 const std::string& encoded_data,
486 int num_in_flight_decodes,
487 int num_play_throughs,
488 int reset_after_frame_num,
489 int delete_decoder_state,
492 media::VideoCodecProfile profile,
493 double rendering_fps,
494 bool suppress_rendering,
495 int delay_reuse_after_frame_num,
496 int decode_calls_per_second)
497 : rendering_helper_(rendering_helper),
498 rendering_window_id_(rendering_window_id),
499 encoded_data_(encoded_data),
500 num_in_flight_decodes_(num_in_flight_decodes),
501 outstanding_decodes_(0),
502 encoded_data_next_pos_to_decode_(0),
503 next_bitstream_buffer_id_(0),
505 remaining_play_throughs_(num_play_throughs),
506 reset_after_frame_num_(reset_after_frame_num),
507 delete_decoder_state_(delete_decoder_state),
509 num_skipped_fragments_(0),
510 num_queued_fragments_(0),
511 num_decoded_frames_(0),
512 num_done_bitstream_buffers_(0),
514 suppress_rendering_(suppress_rendering),
515 delay_reuse_after_frame_num_(delay_reuse_after_frame_num),
516 decode_calls_per_second_(decode_calls_per_second) {
517 CHECK_GT(num_in_flight_decodes, 0);
518 CHECK_GT(num_play_throughs, 0);
519 CHECK_GE(rendering_fps, 0);
520 // |num_in_flight_decodes_| is unsupported if |decode_calls_per_second_| > 0.
521 if (decode_calls_per_second_ > 0)
522 CHECK_EQ(1, num_in_flight_decodes_);
524 // Default to H264 baseline if no profile provided.
525 profile_ = (profile != media::VIDEO_CODEC_PROFILE_UNKNOWN
527 : media::H264PROFILE_BASELINE);
529 if (rendering_fps > 0)
530 throttling_client_.reset(new ThrottlingVDAClient(
533 base::Bind(&GLRenderingVDAClient::NotifyFrameDropped,
534 base::Unretained(this))));
537 GLRenderingVDAClient::~GLRenderingVDAClient() {
538 DeleteDecoder(); // Clean up in case of expected error.
539 CHECK(decoder_deleted());
540 STLDeleteValues(&picture_buffers_by_id_);
541 SetState(CS_DESTROYED);
544 static bool DoNothingReturnTrue() { return true; }
546 void GLRenderingVDAClient::CreateAndStartDecoder() {
547 CHECK(decoder_deleted());
548 CHECK(!decoder_.get());
550 VideoDecodeAccelerator::Client* client = this;
551 base::WeakPtr<VideoDecodeAccelerator::Client> weak_client = AsWeakPtr();
552 if (throttling_client_) {
553 client = throttling_client_.get();
554 weak_client = throttling_client_->AsWeakPtr();
558 new DXVAVideoDecodeAccelerator(client, base::Bind(&DoNothingReturnTrue)));
559 #elif defined(OS_CHROMEOS) || defined(OS_LINUX)
560 #if defined(ARCH_CPU_ARMEL)
562 scoped_ptr<V4L2Device> device = V4L2Device::Create();
564 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE);
567 decoder_.reset(new V4L2VideoDecodeAccelerator(
568 static_cast<EGLDisplay>(rendering_helper_->GetGLDisplay()),
571 base::Bind(&DoNothingReturnTrue),
573 base::MessageLoopProxy::current()));
574 #elif defined(ARCH_CPU_X86_FAMILY)
575 CHECK_EQ(gfx::kGLImplementationDesktopGL, gfx::GetGLImplementation())
576 << "Hardware video decode does not work with OSMesa";
577 decoder_.reset(new VaapiVideoDecodeAccelerator(
578 static_cast<Display*>(rendering_helper_->GetGLDisplay()),
580 base::Bind(&DoNothingReturnTrue)));
581 #endif // ARCH_CPU_ARMEL
583 CHECK(decoder_.get());
584 SetState(CS_DECODER_SET);
585 if (decoder_deleted())
588 CHECK(decoder_->Initialize(profile_));
591 void GLRenderingVDAClient::ProvidePictureBuffers(
592 uint32 requested_num_of_buffers,
593 const gfx::Size& dimensions,
594 uint32 texture_target) {
595 if (decoder_deleted())
597 std::vector<media::PictureBuffer> buffers;
599 texture_target_ = texture_target;
600 for (uint32 i = 0; i < requested_num_of_buffers; ++i) {
601 uint32 id = picture_buffers_by_id_.size();
603 base::WaitableEvent done(false, false);
604 rendering_helper_->CreateTexture(
605 rendering_window_id_, texture_target_, &texture_id, &done);
607 CHECK(outstanding_texture_ids_.insert(texture_id).second);
608 media::PictureBuffer* buffer =
609 new media::PictureBuffer(id, dimensions, texture_id);
610 CHECK(picture_buffers_by_id_.insert(std::make_pair(id, buffer)).second);
611 buffers.push_back(*buffer);
613 decoder_->AssignPictureBuffers(buffers);
616 void GLRenderingVDAClient::DismissPictureBuffer(int32 picture_buffer_id) {
617 PictureBufferById::iterator it =
618 picture_buffers_by_id_.find(picture_buffer_id);
619 CHECK(it != picture_buffers_by_id_.end());
620 CHECK_EQ(outstanding_texture_ids_.erase(it->second->texture_id()), 1U);
621 rendering_helper_->DeleteTexture(it->second->texture_id());
623 picture_buffers_by_id_.erase(it);
626 void GLRenderingVDAClient::PictureReady(const media::Picture& picture) {
627 // We shouldn't be getting pictures delivered after Reset has completed.
628 CHECK_LT(state_, CS_RESET);
630 if (decoder_deleted())
633 base::TimeTicks now = base::TimeTicks::Now();
634 frame_delivery_times_.push_back(now);
635 // Save the decode time of this picture.
636 std::map<int, base::TimeTicks>::iterator it =
637 decode_start_time_.find(picture.bitstream_buffer_id());
638 ASSERT_NE(decode_start_time_.end(), it);
639 decode_time_.push_back(now - it->second);
640 decode_start_time_.erase(it);
642 CHECK_LE(picture.bitstream_buffer_id(), next_bitstream_buffer_id_);
643 ++num_decoded_frames_;
645 // Mid-stream reset applies only to the last play-through per constructor
647 if (remaining_play_throughs_ == 1 &&
648 reset_after_frame_num_ == num_decoded_frames()) {
649 reset_after_frame_num_ = MID_STREAM_RESET;
651 // Re-start decoding from the beginning of the stream to avoid needing to
652 // know how to find I-frames and so on in this test.
653 encoded_data_next_pos_to_decode_ = 0;
656 media::PictureBuffer* picture_buffer =
657 picture_buffers_by_id_[picture.picture_buffer_id()];
658 CHECK(picture_buffer);
659 if (!suppress_rendering_) {
660 rendering_helper_->RenderTexture(texture_target_,
661 picture_buffer->texture_id());
664 if (num_decoded_frames() > delay_reuse_after_frame_num_) {
665 base::MessageLoop::current()->PostDelayedTask(
667 base::Bind(&VideoDecodeAccelerator::ReusePictureBuffer,
668 decoder_->AsWeakPtr(),
669 picture.picture_buffer_id()),
672 decoder_->ReusePictureBuffer(picture.picture_buffer_id());
676 void GLRenderingVDAClient::NotifyInitializeDone() {
677 SetState(CS_INITIALIZED);
678 initialize_done_ticks_ = base::TimeTicks::Now();
680 if (reset_after_frame_num_ == START_OF_STREAM_RESET) {
681 reset_after_frame_num_ = MID_STREAM_RESET;
686 for (int i = 0; i < num_in_flight_decodes_; ++i)
687 DecodeNextFragment();
688 DCHECK_EQ(outstanding_decodes_, num_in_flight_decodes_);
691 void GLRenderingVDAClient::NotifyEndOfBitstreamBuffer(
692 int32 bitstream_buffer_id) {
693 // TODO(fischman): this test currently relies on this notification to make
694 // forward progress during a Reset(). But the VDA::Reset() API doesn't
695 // guarantee this, so stop relying on it (and remove the notifications from
696 // VaapiVideoDecodeAccelerator::FinishReset()).
697 ++num_done_bitstream_buffers_;
698 --outstanding_decodes_;
699 if (decode_calls_per_second_ == 0)
700 DecodeNextFragment();
703 void GLRenderingVDAClient::NotifyFlushDone() {
704 if (decoder_deleted())
706 SetState(CS_FLUSHED);
707 --remaining_play_throughs_;
708 DCHECK_GE(remaining_play_throughs_, 0);
709 if (decoder_deleted())
712 SetState(CS_RESETTING);
715 void GLRenderingVDAClient::NotifyResetDone() {
716 if (decoder_deleted())
719 if (reset_after_frame_num_ == MID_STREAM_RESET) {
720 reset_after_frame_num_ = END_OF_STREAM_RESET;
721 DecodeNextFragment();
723 } else if (reset_after_frame_num_ == START_OF_STREAM_RESET) {
724 reset_after_frame_num_ = END_OF_STREAM_RESET;
725 for (int i = 0; i < num_in_flight_decodes_; ++i)
726 DecodeNextFragment();
730 if (remaining_play_throughs_) {
731 encoded_data_next_pos_to_decode_ = 0;
732 NotifyInitializeDone();
737 if (!decoder_deleted())
741 void GLRenderingVDAClient::NotifyError(VideoDecodeAccelerator::Error error) {
745 void GLRenderingVDAClient::OutputFrameDeliveryTimes(base::PlatformFile output) {
746 std::string s = base::StringPrintf("frame count: %" PRIuS "\n",
747 frame_delivery_times_.size());
748 base::WritePlatformFileAtCurrentPos(output, s.data(), s.length());
749 base::TimeTicks t0 = initialize_done_ticks_;
750 for (size_t i = 0; i < frame_delivery_times_.size(); ++i) {
751 s = base::StringPrintf("frame %04" PRIuS ": %" PRId64 " us\n",
753 (frame_delivery_times_[i] - t0).InMicroseconds());
754 t0 = frame_delivery_times_[i];
755 base::WritePlatformFileAtCurrentPos(output, s.data(), s.length());
759 void GLRenderingVDAClient::NotifyFrameDropped(int32 picture_buffer_id) {
760 decoder_->ReusePictureBuffer(picture_buffer_id);
763 static bool LookingAtNAL(const std::string& encoded, size_t pos) {
764 return encoded[pos] == 0 && encoded[pos + 1] == 0 &&
765 encoded[pos + 2] == 0 && encoded[pos + 3] == 1;
768 void GLRenderingVDAClient::SetState(ClientState new_state) {
769 note_->Notify(new_state);
771 if (!remaining_play_throughs_ && new_state == delete_decoder_state_) {
772 CHECK(!decoder_deleted());
777 void GLRenderingVDAClient::DeleteDecoder() {
778 if (decoder_deleted())
780 decoder_.release()->Destroy();
781 STLClearObject(&encoded_data_);
782 for (std::set<int>::iterator it = outstanding_texture_ids_.begin();
783 it != outstanding_texture_ids_.end(); ++it) {
784 rendering_helper_->DeleteTexture(*it);
786 outstanding_texture_ids_.clear();
787 // Cascade through the rest of the states to simplify test code below.
788 for (int i = state_ + 1; i < CS_MAX; ++i)
789 SetState(static_cast<ClientState>(i));
792 std::string GLRenderingVDAClient::GetBytesForFirstFragment(
793 size_t start_pos, size_t* end_pos) {
794 if (profile_ < media::H264PROFILE_MAX) {
795 *end_pos = start_pos;
796 while (*end_pos + 4 < encoded_data_.size()) {
797 if ((encoded_data_[*end_pos + 4] & 0x1f) == 0x7) // SPS start frame
798 return GetBytesForNextFragment(*end_pos, end_pos);
799 GetBytesForNextNALU(*end_pos, end_pos);
800 num_skipped_fragments_++;
802 *end_pos = start_pos;
803 return std::string();
805 DCHECK_LE(profile_, media::VP8PROFILE_MAX);
806 return GetBytesForNextFragment(start_pos, end_pos);
809 std::string GLRenderingVDAClient::GetBytesForNextFragment(
810 size_t start_pos, size_t* end_pos) {
811 if (profile_ < media::H264PROFILE_MAX) {
812 *end_pos = start_pos;
813 GetBytesForNextNALU(*end_pos, end_pos);
814 if (start_pos != *end_pos) {
815 num_queued_fragments_++;
817 return encoded_data_.substr(start_pos, *end_pos - start_pos);
819 DCHECK_LE(profile_, media::VP8PROFILE_MAX);
820 return GetBytesForNextFrame(start_pos, end_pos);
823 void GLRenderingVDAClient::GetBytesForNextNALU(
824 size_t start_pos, size_t* end_pos) {
825 *end_pos = start_pos;
826 if (*end_pos + 4 > encoded_data_.size())
828 CHECK(LookingAtNAL(encoded_data_, start_pos));
830 while (*end_pos + 4 <= encoded_data_.size() &&
831 !LookingAtNAL(encoded_data_, *end_pos)) {
834 if (*end_pos + 3 >= encoded_data_.size())
835 *end_pos = encoded_data_.size();
838 std::string GLRenderingVDAClient::GetBytesForNextFrame(
839 size_t start_pos, size_t* end_pos) {
840 // Helpful description: http://wiki.multimedia.cx/index.php?title=IVF
843 start_pos = 32; // Skip IVF header.
844 *end_pos = start_pos;
845 uint32 frame_size = *reinterpret_cast<uint32*>(&encoded_data_[*end_pos]);
846 *end_pos += 12; // Skip frame header.
847 bytes.append(encoded_data_.substr(*end_pos, frame_size));
848 *end_pos += frame_size;
849 num_queued_fragments_++;
853 static bool FragmentHasConfigInfo(const uint8* data, size_t size,
854 media::VideoCodecProfile profile) {
855 if (profile >= media::H264PROFILE_MIN &&
856 profile <= media::H264PROFILE_MAX) {
857 media::H264Parser parser;
858 parser.SetStream(data, size);
859 media::H264NALU nalu;
860 media::H264Parser::Result result = parser.AdvanceToNextNALU(&nalu);
861 if (result != media::H264Parser::kOk) {
862 // Let the VDA figure out there's something wrong with the stream.
866 return nalu.nal_unit_type == media::H264NALU::kSPS;
867 } else if (profile >= media::VP8PROFILE_MIN &&
868 profile <= media::VP8PROFILE_MAX) {
869 return (size > 0 && !(data[0] & 0x01));
871 // Shouldn't happen at this point.
872 LOG(FATAL) << "Invalid profile: " << profile;
876 void GLRenderingVDAClient::DecodeNextFragment() {
877 if (decoder_deleted())
879 if (encoded_data_next_pos_to_decode_ == encoded_data_.size()) {
880 if (outstanding_decodes_ == 0) {
882 SetState(CS_FLUSHING);
887 std::string next_fragment_bytes;
888 if (encoded_data_next_pos_to_decode_ == 0) {
889 next_fragment_bytes = GetBytesForFirstFragment(0, &end_pos);
891 next_fragment_bytes =
892 GetBytesForNextFragment(encoded_data_next_pos_to_decode_, &end_pos);
894 size_t next_fragment_size = next_fragment_bytes.size();
896 // Call Reset() just after Decode() if the fragment contains config info.
897 // This tests how the VDA behaves when it gets a reset request before it has
898 // a chance to ProvidePictureBuffers().
899 bool reset_here = false;
900 if (reset_after_frame_num_ == RESET_AFTER_FIRST_CONFIG_INFO) {
901 reset_here = FragmentHasConfigInfo(
902 reinterpret_cast<const uint8*>(next_fragment_bytes.data()),
906 reset_after_frame_num_ = END_OF_STREAM_RESET;
909 // Populate the shared memory buffer w/ the fragment, duplicate its handle,
910 // and hand it off to the decoder.
911 base::SharedMemory shm;
912 CHECK(shm.CreateAndMapAnonymous(next_fragment_size));
913 memcpy(shm.memory(), next_fragment_bytes.data(), next_fragment_size);
914 base::SharedMemoryHandle dup_handle;
915 CHECK(shm.ShareToProcess(base::Process::Current().handle(), &dup_handle));
916 media::BitstreamBuffer bitstream_buffer(
917 next_bitstream_buffer_id_, dup_handle, next_fragment_size);
918 decode_start_time_[next_bitstream_buffer_id_] = base::TimeTicks::Now();
919 // Mask against 30 bits, to avoid (undefined) wraparound on signed integer.
920 next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & 0x3FFFFFFF;
921 decoder_->Decode(bitstream_buffer);
922 ++outstanding_decodes_;
923 if (!remaining_play_throughs_ &&
924 -delete_decoder_state_ == next_bitstream_buffer_id_) {
929 reset_after_frame_num_ = MID_STREAM_RESET;
931 // Restart from the beginning to re-Decode() the SPS we just sent.
932 encoded_data_next_pos_to_decode_ = 0;
934 encoded_data_next_pos_to_decode_ = end_pos;
937 if (decode_calls_per_second_ > 0) {
938 base::MessageLoop::current()->PostDelayedTask(
940 base::Bind(&GLRenderingVDAClient::DecodeNextFragment, AsWeakPtr()),
941 base::TimeDelta::FromSeconds(1) / decode_calls_per_second_);
945 int GLRenderingVDAClient::num_decoded_frames() {
946 return throttling_client_ ? throttling_client_->num_decoded_frames()
947 : num_decoded_frames_;
950 double GLRenderingVDAClient::frames_per_second() {
951 base::TimeDelta delta = frame_delivery_times_.back() - initialize_done_ticks_;
952 if (delta.InSecondsF() == 0)
954 return num_decoded_frames() / delta.InSecondsF();
957 int GLRenderingVDAClient::decode_time_median() {
958 if (decode_time_.size() == 0)
960 std::sort(decode_time_.begin(), decode_time_.end());
961 int index = decode_time_.size() / 2;
962 if (decode_time_.size() % 2 != 0)
963 return decode_time_[index].InMilliseconds();
965 return (decode_time_[index] + decode_time_[index - 1]).InMilliseconds() / 2;
968 class VideoDecodeAcceleratorTest : public ::testing::Test {
970 VideoDecodeAcceleratorTest();
971 virtual void SetUp();
972 virtual void TearDown();
974 // Parse |data| into its constituent parts, set the various output fields
975 // accordingly, and read in video stream. CHECK-fails on unexpected or
976 // missing required data. Unspecified optional fields are set to -1.
977 void ParseAndReadTestVideoData(base::FilePath::StringType data,
978 std::vector<TestVideoFile*>* test_video_files);
980 // Update the parameters of |test_video_files| according to
981 // |num_concurrent_decoders| and |reset_point|. Ex: the expected number of
982 // frames should be adjusted if decoder is reset in the middle of the stream.
983 void UpdateTestVideoFileParams(
984 size_t num_concurrent_decoders,
986 std::vector<TestVideoFile*>* test_video_files);
988 void InitializeRenderingHelper(const RenderingHelperParams& helper_params);
989 void CreateAndStartDecoder(GLRenderingVDAClient* client,
990 ClientStateNotification<ClientState>* note);
991 void WaitUntilDecodeFinish(ClientStateNotification<ClientState>* note);
992 void WaitUntilIdle();
993 void OutputLogFile(const base::FilePath::CharType* log_path,
994 const std::string& content);
996 std::vector<TestVideoFile*> test_video_files_;
997 RenderingHelper rendering_helper_;
998 scoped_refptr<base::MessageLoopProxy> rendering_loop_proxy_;
1001 base::Thread rendering_thread_;
1002 // Required for Thread to work. Not used otherwise.
1003 base::ShadowingAtExitManager at_exit_manager_;
1005 DISALLOW_COPY_AND_ASSIGN(VideoDecodeAcceleratorTest);
1008 VideoDecodeAcceleratorTest::VideoDecodeAcceleratorTest()
1009 : rendering_thread_("GLRenderingVDAClientThread") {}
1011 void VideoDecodeAcceleratorTest::SetUp() {
1012 ParseAndReadTestVideoData(g_test_video_data, &test_video_files_);
1014 // Initialize the rendering thread.
1015 base::Thread::Options options;
1016 options.message_loop_type = base::MessageLoop::TYPE_DEFAULT;
1018 // For windows the decoding thread initializes the media foundation decoder
1019 // which uses COM. We need the thread to be a UI thread.
1020 options.message_loop_type = base::MessageLoop::TYPE_UI;
1023 rendering_thread_.StartWithOptions(options);
1024 rendering_loop_proxy_ = rendering_thread_.message_loop_proxy();
1027 void VideoDecodeAcceleratorTest::TearDown() {
1028 rendering_loop_proxy_->PostTask(
1030 base::Bind(&STLDeleteElements<std::vector<TestVideoFile*> >,
1031 &test_video_files_));
1033 base::WaitableEvent done(false, false);
1034 rendering_loop_proxy_->PostTask(
1036 base::Bind(&RenderingHelper::UnInitialize,
1037 base::Unretained(&rendering_helper_),
1041 rendering_thread_.Stop();
1044 void VideoDecodeAcceleratorTest::ParseAndReadTestVideoData(
1045 base::FilePath::StringType data,
1046 std::vector<TestVideoFile*>* test_video_files) {
1047 std::vector<base::FilePath::StringType> entries;
1048 base::SplitString(data, ';', &entries);
1049 CHECK_GE(entries.size(), 1U) << data;
1050 for (size_t index = 0; index < entries.size(); ++index) {
1051 std::vector<base::FilePath::StringType> fields;
1052 base::SplitString(entries[index], ':', &fields);
1053 CHECK_GE(fields.size(), 1U) << entries[index];
1054 CHECK_LE(fields.size(), 8U) << entries[index];
1055 TestVideoFile* video_file = new TestVideoFile(fields[0]);
1056 if (!fields[1].empty())
1057 CHECK(base::StringToInt(fields[1], &video_file->width));
1058 if (!fields[2].empty())
1059 CHECK(base::StringToInt(fields[2], &video_file->height));
1060 if (!fields[3].empty())
1061 CHECK(base::StringToInt(fields[3], &video_file->num_frames));
1062 if (!fields[4].empty())
1063 CHECK(base::StringToInt(fields[4], &video_file->num_fragments));
1064 if (!fields[5].empty())
1065 CHECK(base::StringToInt(fields[5], &video_file->min_fps_render));
1066 if (!fields[6].empty())
1067 CHECK(base::StringToInt(fields[6], &video_file->min_fps_no_render));
1069 if (!fields[7].empty())
1070 CHECK(base::StringToInt(fields[7], &profile));
1071 video_file->profile = static_cast<media::VideoCodecProfile>(profile);
1073 // Read in the video data.
1074 base::FilePath filepath(video_file->file_name);
1075 CHECK(base::ReadFileToString(filepath, &video_file->data_str))
1076 << "test_video_file: " << filepath.MaybeAsASCII();
1078 test_video_files->push_back(video_file);
1082 void VideoDecodeAcceleratorTest::UpdateTestVideoFileParams(
1083 size_t num_concurrent_decoders,
1085 std::vector<TestVideoFile*>* test_video_files) {
1086 for (size_t i = 0; i < test_video_files->size(); i++) {
1087 TestVideoFile* video_file = (*test_video_files)[i];
1088 if (reset_point == MID_STREAM_RESET) {
1089 // Reset should not go beyond the last frame;
1090 // reset in the middle of the stream for short videos.
1091 video_file->reset_after_frame_num = kMaxResetAfterFrameNum;
1092 if (video_file->num_frames <= video_file->reset_after_frame_num)
1093 video_file->reset_after_frame_num = video_file->num_frames / 2;
1095 video_file->num_frames += video_file->reset_after_frame_num;
1097 video_file->reset_after_frame_num = reset_point;
1100 if (video_file->min_fps_render != -1)
1101 video_file->min_fps_render /= num_concurrent_decoders;
1102 if (video_file->min_fps_no_render != -1)
1103 video_file->min_fps_no_render /= num_concurrent_decoders;
1107 void VideoDecodeAcceleratorTest::InitializeRenderingHelper(
1108 const RenderingHelperParams& helper_params) {
1109 base::WaitableEvent done(false, false);
1110 rendering_loop_proxy_->PostTask(
1112 base::Bind(&RenderingHelper::Initialize,
1113 base::Unretained(&rendering_helper_),
1119 void VideoDecodeAcceleratorTest::CreateAndStartDecoder(
1120 GLRenderingVDAClient* client,
1121 ClientStateNotification<ClientState>* note) {
1122 rendering_loop_proxy_->PostTask(
1124 base::Bind(&GLRenderingVDAClient::CreateAndStartDecoder,
1125 base::Unretained(client)));
1126 ASSERT_EQ(note->Wait(), CS_DECODER_SET);
1129 void VideoDecodeAcceleratorTest::WaitUntilDecodeFinish(
1130 ClientStateNotification<ClientState>* note) {
1131 for (int i = 0; i < CS_MAX; i++) {
1132 if (note->Wait() == CS_DESTROYED)
1137 void VideoDecodeAcceleratorTest::WaitUntilIdle() {
1138 base::WaitableEvent done(false, false);
1139 rendering_loop_proxy_->PostTask(
1141 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done)));
1145 void VideoDecodeAcceleratorTest::OutputLogFile(
1146 const base::FilePath::CharType* log_path,
1147 const std::string& content) {
1148 base::PlatformFile file = base::CreatePlatformFile(
1149 base::FilePath(log_path),
1150 base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE,
1153 base::WritePlatformFileAtCurrentPos(file, content.data(), content.length());
1154 base::ClosePlatformFile(file);
1158 // - Number of concurrent decoders.
1159 // - Number of concurrent in-flight Decode() calls per decoder.
1160 // - Number of play-throughs.
1161 // - reset_after_frame_num: see GLRenderingVDAClient ctor.
1162 // - delete_decoder_phase: see GLRenderingVDAClient ctor.
1163 // - whether to test slow rendering by delaying ReusePictureBuffer().
1164 // - whether the video frames are rendered as thumbnails.
1165 class VideoDecodeAcceleratorParamTest
1166 : public VideoDecodeAcceleratorTest,
1167 public ::testing::WithParamInterface<
1168 Tuple7<int, int, int, ResetPoint, ClientState, bool, bool> > {
1171 // Helper so that gtest failures emit a more readable version of the tuple than
1172 // its byte representation.
1173 ::std::ostream& operator<<(
1175 const Tuple7<int, int, int, ResetPoint, ClientState, bool, bool>& t) {
1176 return os << t.a << ", " << t.b << ", " << t.c << ", " << t.d << ", " << t.e
1177 << ", " << t.f << ", " << t.g;
1180 // Wait for |note| to report a state and if it's not |expected_state| then
1181 // assert |client| has deleted its decoder.
1182 static void AssertWaitForStateOrDeleted(
1183 ClientStateNotification<ClientState>* note,
1184 GLRenderingVDAClient* client,
1185 ClientState expected_state) {
1186 ClientState state = note->Wait();
1187 if (state == expected_state) return;
1188 ASSERT_TRUE(client->decoder_deleted())
1189 << "Decoder not deleted but Wait() returned " << state
1190 << ", instead of " << expected_state;
1193 // We assert a minimal number of concurrent decoders we expect to succeed.
1194 // Different platforms can support more concurrent decoders, so we don't assert
1195 // failure above this.
1196 enum { kMinSupportedNumConcurrentDecoders = 3 };
1198 // Test the most straightforward case possible: data is decoded from a single
1199 // chunk and rendered to the screen.
1200 TEST_P(VideoDecodeAcceleratorParamTest, TestSimpleDecode) {
1201 const size_t num_concurrent_decoders = GetParam().a;
1202 const size_t num_in_flight_decodes = GetParam().b;
1203 const int num_play_throughs = GetParam().c;
1204 const int reset_point = GetParam().d;
1205 const int delete_decoder_state = GetParam().e;
1206 bool test_reuse_delay = GetParam().f;
1207 const bool render_as_thumbnails = GetParam().g;
1209 UpdateTestVideoFileParams(
1210 num_concurrent_decoders, reset_point, &test_video_files_);
1212 // Suppress GL rendering for all tests when the "--disable_rendering" is set.
1213 const bool suppress_rendering = g_disable_rendering;
1215 std::vector<ClientStateNotification<ClientState>*>
1216 notes(num_concurrent_decoders, NULL);
1217 std::vector<GLRenderingVDAClient*> clients(num_concurrent_decoders, NULL);
1219 RenderingHelperParams helper_params;
1220 helper_params.num_windows = num_concurrent_decoders;
1221 helper_params.render_as_thumbnails = render_as_thumbnails;
1222 if (render_as_thumbnails) {
1223 // Only one decoder is supported with thumbnail rendering
1224 CHECK_EQ(num_concurrent_decoders, 1U);
1225 gfx::Size frame_size(test_video_files_[0]->width,
1226 test_video_files_[0]->height);
1227 helper_params.frame_dimensions.push_back(frame_size);
1228 helper_params.window_dimensions.push_back(kThumbnailsDisplaySize);
1229 helper_params.thumbnails_page_size = kThumbnailsPageSize;
1230 helper_params.thumbnail_size = kThumbnailSize;
1232 for (size_t index = 0; index < test_video_files_.size(); ++index) {
1233 gfx::Size frame_size(test_video_files_[index]->width,
1234 test_video_files_[index]->height);
1235 helper_params.frame_dimensions.push_back(frame_size);
1236 helper_params.window_dimensions.push_back(frame_size);
1239 InitializeRenderingHelper(helper_params);
1241 // First kick off all the decoders.
1242 for (size_t index = 0; index < num_concurrent_decoders; ++index) {
1243 TestVideoFile* video_file =
1244 test_video_files_[index % test_video_files_.size()];
1245 ClientStateNotification<ClientState>* note =
1246 new ClientStateNotification<ClientState>();
1247 notes[index] = note;
1249 int delay_after_frame_num = std::numeric_limits<int>::max();
1250 if (test_reuse_delay &&
1251 kMaxFramesToDelayReuse * 2 < video_file->num_frames) {
1252 delay_after_frame_num = video_file->num_frames - kMaxFramesToDelayReuse;
1255 GLRenderingVDAClient* client =
1256 new GLRenderingVDAClient(&rendering_helper_,
1259 video_file->data_str,
1260 num_in_flight_decodes,
1262 video_file->reset_after_frame_num,
1263 delete_decoder_state,
1266 video_file->profile,
1269 delay_after_frame_num,
1271 clients[index] = client;
1273 CreateAndStartDecoder(client, note);
1275 // Then wait for all the decodes to finish.
1276 // Only check performance & correctness later if we play through only once.
1277 bool skip_performance_and_correctness_checks = num_play_throughs > 1;
1278 for (size_t i = 0; i < num_concurrent_decoders; ++i) {
1279 ClientStateNotification<ClientState>* note = notes[i];
1280 ClientState state = note->Wait();
1281 if (state != CS_INITIALIZED) {
1282 skip_performance_and_correctness_checks = true;
1283 // We expect initialization to fail only when more than the supported
1284 // number of decoders is instantiated. Assert here that something else
1285 // didn't trigger failure.
1286 ASSERT_GT(num_concurrent_decoders,
1287 static_cast<size_t>(kMinSupportedNumConcurrentDecoders));
1290 ASSERT_EQ(state, CS_INITIALIZED);
1291 for (int n = 0; n < num_play_throughs; ++n) {
1292 // For play-throughs other than the first, we expect initialization to
1293 // succeed unconditionally.
1295 ASSERT_NO_FATAL_FAILURE(
1296 AssertWaitForStateOrDeleted(note, clients[i], CS_INITIALIZED));
1298 // InitializeDone kicks off decoding inside the client, so we just need to
1300 ASSERT_NO_FATAL_FAILURE(
1301 AssertWaitForStateOrDeleted(note, clients[i], CS_FLUSHING));
1302 ASSERT_NO_FATAL_FAILURE(
1303 AssertWaitForStateOrDeleted(note, clients[i], CS_FLUSHED));
1304 // FlushDone requests Reset().
1305 ASSERT_NO_FATAL_FAILURE(
1306 AssertWaitForStateOrDeleted(note, clients[i], CS_RESETTING));
1308 ASSERT_NO_FATAL_FAILURE(
1309 AssertWaitForStateOrDeleted(note, clients[i], CS_RESET));
1310 // ResetDone requests Destroy().
1311 ASSERT_NO_FATAL_FAILURE(
1312 AssertWaitForStateOrDeleted(note, clients[i], CS_DESTROYED));
1314 // Finally assert that decoding went as expected.
1315 for (size_t i = 0; i < num_concurrent_decoders &&
1316 !skip_performance_and_correctness_checks; ++i) {
1317 // We can only make performance/correctness assertions if the decoder was
1318 // allowed to finish.
1319 if (delete_decoder_state < CS_FLUSHED)
1321 GLRenderingVDAClient* client = clients[i];
1322 TestVideoFile* video_file = test_video_files_[i % test_video_files_.size()];
1323 if (video_file->num_frames > 0) {
1324 // Expect the decoded frames may be more than the video frames as frames
1325 // could still be returned until resetting done.
1326 if (video_file->reset_after_frame_num > 0)
1327 EXPECT_GE(client->num_decoded_frames(), video_file->num_frames);
1329 EXPECT_EQ(client->num_decoded_frames(), video_file->num_frames);
1331 if (reset_point == END_OF_STREAM_RESET) {
1332 EXPECT_EQ(video_file->num_fragments, client->num_skipped_fragments() +
1333 client->num_queued_fragments());
1334 EXPECT_EQ(client->num_done_bitstream_buffers(),
1335 client->num_queued_fragments());
1337 VLOG(0) << "Decoder " << i << " fps: " << client->frames_per_second();
1338 if (!render_as_thumbnails) {
1339 int min_fps = suppress_rendering ?
1340 video_file->min_fps_no_render : video_file->min_fps_render;
1341 if (min_fps > 0 && !test_reuse_delay)
1342 EXPECT_GT(client->frames_per_second(), min_fps);
1346 if (render_as_thumbnails) {
1347 std::vector<unsigned char> rgb;
1349 base::WaitableEvent done(false, false);
1350 rendering_loop_proxy_->PostTask(
1352 base::Bind(&RenderingHelper::GetThumbnailsAsRGB,
1353 base::Unretained(&rendering_helper_),
1354 &rgb, &alpha_solid, &done));
1357 std::vector<std::string> golden_md5s;
1358 std::string md5_string = base::MD5String(
1359 base::StringPiece(reinterpret_cast<char*>(&rgb[0]), rgb.size()));
1360 ReadGoldenThumbnailMD5s(test_video_files_[0], &golden_md5s);
1361 std::vector<std::string>::iterator match =
1362 find(golden_md5s.begin(), golden_md5s.end(), md5_string);
1363 if (match == golden_md5s.end()) {
1364 // Convert raw RGB into PNG for export.
1365 std::vector<unsigned char> png;
1366 gfx::PNGCodec::Encode(&rgb[0],
1367 gfx::PNGCodec::FORMAT_RGB,
1368 kThumbnailsPageSize,
1369 kThumbnailsPageSize.width() * 3,
1371 std::vector<gfx::PNGCodec::Comment>(),
1374 LOG(ERROR) << "Unknown thumbnails MD5: " << md5_string;
1376 base::FilePath filepath(test_video_files_[0]->file_name);
1377 filepath = filepath.AddExtension(FILE_PATH_LITERAL(".bad_thumbnails"));
1378 filepath = filepath.AddExtension(FILE_PATH_LITERAL(".png"));
1379 int num_bytes = file_util::WriteFile(filepath,
1380 reinterpret_cast<char*>(&png[0]),
1382 ASSERT_EQ(num_bytes, static_cast<int>(png.size()));
1384 ASSERT_NE(match, golden_md5s.end());
1385 EXPECT_EQ(alpha_solid, true) << "RGBA frame had incorrect alpha";
1388 // Output the frame delivery time to file
1389 // We can only make performance/correctness assertions if the decoder was
1390 // allowed to finish.
1391 if (g_output_log != NULL && delete_decoder_state >= CS_FLUSHED) {
1392 base::PlatformFile output_file = base::CreatePlatformFile(
1393 base::FilePath(g_output_log),
1394 base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE,
1397 for (size_t i = 0; i < num_concurrent_decoders; ++i) {
1398 clients[i]->OutputFrameDeliveryTimes(output_file);
1400 base::ClosePlatformFile(output_file);
1403 rendering_loop_proxy_->PostTask(
1405 base::Bind(&STLDeleteElements<std::vector<GLRenderingVDAClient*> >,
1407 rendering_loop_proxy_->PostTask(
1409 base::Bind(&STLDeleteElements<
1410 std::vector<ClientStateNotification<ClientState>*> >,
1415 // Test that replay after EOS works fine.
1416 INSTANTIATE_TEST_CASE_P(
1417 ReplayAfterEOS, VideoDecodeAcceleratorParamTest,
1419 MakeTuple(1, 1, 4, END_OF_STREAM_RESET, CS_RESET, false, false)));
1421 // Test that Reset() before the first Decode() works fine.
1422 INSTANTIATE_TEST_CASE_P(
1423 ResetBeforeDecode, VideoDecodeAcceleratorParamTest,
1425 MakeTuple(1, 1, 1, START_OF_STREAM_RESET, CS_RESET, false, false)));
1427 // Test Reset() immediately after Decode() containing config info.
1428 INSTANTIATE_TEST_CASE_P(
1429 ResetAfterFirstConfigInfo, VideoDecodeAcceleratorParamTest,
1432 1, 1, 1, RESET_AFTER_FIRST_CONFIG_INFO, CS_RESET, false, false)));
1434 // Test that Reset() mid-stream works fine and doesn't affect decoding even when
1435 // Decode() calls are made during the reset.
1436 INSTANTIATE_TEST_CASE_P(
1437 MidStreamReset, VideoDecodeAcceleratorParamTest,
1439 MakeTuple(1, 1, 1, MID_STREAM_RESET, CS_RESET, false, false)));
1441 INSTANTIATE_TEST_CASE_P(
1442 SlowRendering, VideoDecodeAcceleratorParamTest,
1444 MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_RESET, true, false)));
1446 // Test that Destroy() mid-stream works fine (primarily this is testing that no
1448 INSTANTIATE_TEST_CASE_P(
1449 TearDownTiming, VideoDecodeAcceleratorParamTest,
1451 MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_DECODER_SET, false, false),
1452 MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_INITIALIZED, false, false),
1453 MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_FLUSHING, false, false),
1454 MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_FLUSHED, false, false),
1455 MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_RESETTING, false, false),
1456 MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, false),
1457 MakeTuple(1, 1, 1, END_OF_STREAM_RESET,
1458 static_cast<ClientState>(-1), false, false),
1459 MakeTuple(1, 1, 1, END_OF_STREAM_RESET,
1460 static_cast<ClientState>(-10), false, false),
1461 MakeTuple(1, 1, 1, END_OF_STREAM_RESET,
1462 static_cast<ClientState>(-100), false, false)));
1464 // Test that decoding various variation works with multiple in-flight decodes.
1465 INSTANTIATE_TEST_CASE_P(
1466 DecodeVariations, VideoDecodeAcceleratorParamTest,
1468 MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, false),
1469 MakeTuple(1, 10, 1, END_OF_STREAM_RESET, CS_RESET, false, false),
1471 MakeTuple(1, 15, 1, END_OF_STREAM_RESET, CS_RESET, false, false)));
1473 // Find out how many concurrent decoders can go before we exhaust system
1475 INSTANTIATE_TEST_CASE_P(
1476 ResourceExhaustion, VideoDecodeAcceleratorParamTest,
1478 // +0 hack below to promote enum to int.
1479 MakeTuple(kMinSupportedNumConcurrentDecoders + 0, 1, 1,
1480 END_OF_STREAM_RESET, CS_RESET, false, false),
1481 MakeTuple(kMinSupportedNumConcurrentDecoders + 1, 1, 1,
1482 END_OF_STREAM_RESET, CS_RESET, false, false)));
1484 // Thumbnailing test
1485 INSTANTIATE_TEST_CASE_P(
1486 Thumbnail, VideoDecodeAcceleratorParamTest,
1488 MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, true)));
1490 // Measure the median of the decode time when VDA::Decode is called 30 times per
1492 TEST_F(VideoDecodeAcceleratorTest, TestDecodeTimeMedian) {
1493 RenderingHelperParams helper_params;
1494 helper_params.num_windows = 1;
1495 helper_params.render_as_thumbnails = false;
1496 gfx::Size frame_size(test_video_files_[0]->width,
1497 test_video_files_[0]->height);
1498 helper_params.frame_dimensions.push_back(frame_size);
1499 helper_params.window_dimensions.push_back(frame_size);
1500 InitializeRenderingHelper(helper_params);
1502 ClientStateNotification<ClientState>* note =
1503 new ClientStateNotification<ClientState>();
1504 GLRenderingVDAClient* client =
1505 new GLRenderingVDAClient(&rendering_helper_,
1508 test_video_files_[0]->data_str,
1511 test_video_files_[0]->reset_after_frame_num,
1513 test_video_files_[0]->width,
1514 test_video_files_[0]->height,
1515 test_video_files_[0]->profile,
1518 std::numeric_limits<int>::max(),
1519 kWebRtcDecodeCallsPerSecond);
1520 CreateAndStartDecoder(client, note);
1521 WaitUntilDecodeFinish(note);
1523 int decode_time_median = client->decode_time_median();
1524 std::string output_string =
1525 base::StringPrintf("Decode time median: %d ms", decode_time_median);
1526 VLOG(0) << output_string;
1527 ASSERT_GT(decode_time_median, 0);
1529 if (g_output_log != NULL)
1530 OutputLogFile(g_output_log, output_string);
1532 rendering_loop_proxy_->DeleteSoon(FROM_HERE, client);
1533 rendering_loop_proxy_->DeleteSoon(FROM_HERE, note);
1537 // TODO(fischman, vrk): add more tests! In particular:
1538 // - Test life-cycle: Seek/Stop/Pause/Play for a single decoder.
1539 // - Test alternate configurations
1540 // - Test failure conditions.
1541 // - Test frame size changes mid-stream
1544 } // namespace content
1546 int main(int argc, char **argv) {
1547 testing::InitGoogleTest(&argc, argv); // Removes gtest-specific args.
1548 CommandLine::Init(argc, argv);
1550 // Needed to enable DVLOG through --vmodule.
1551 logging::LoggingSettings settings;
1552 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
1553 settings.dcheck_state =
1554 logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS;
1555 CHECK(logging::InitLogging(settings));
1557 CommandLine* cmd_line = CommandLine::ForCurrentProcess();
1560 CommandLine::SwitchMap switches = cmd_line->GetSwitches();
1561 for (CommandLine::SwitchMap::const_iterator it = switches.begin();
1562 it != switches.end(); ++it) {
1563 if (it->first == "test_video_data") {
1564 content::g_test_video_data = it->second.c_str();
1567 // TODO(wuchengli): remove frame_deliver_log after CrOS test get updated.
1568 // See http://crosreview.com/175426.
1569 if (it->first == "frame_delivery_log" || it->first == "output_log") {
1570 content::g_output_log = it->second.c_str();
1573 if (it->first == "rendering_fps") {
1574 // On Windows, CommandLine::StringType is wstring. We need to convert
1575 // it to std::string first
1576 std::string input(it->second.begin(), it->second.end());
1577 CHECK(base::StringToDouble(input, &content::g_rendering_fps));
1580 if (it->first == "disable_rendering") {
1581 content::g_disable_rendering = true;
1584 if (it->first == "v" || it->first == "vmodule")
1586 LOG(FATAL) << "Unexpected switch: " << it->first << ":" << it->second;
1589 base::ShadowingAtExitManager at_exit_manager;
1591 return RUN_ALL_TESTS();