Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 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. |
| 4 | // |
| 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 |
| 8 | // Win/EGL. |
| 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 |
| 12 | // state transitions. |
| 13 | // - GLRenderingVDAClient is a VideoDecodeAccelerator::Client implementation |
| 14 | // - Finally actual TEST cases are at the bottom of this file, using the above |
| 15 | // infrastructure. |
| 16 | |
| 17 | #include <fcntl.h> |
| 18 | #include <math.h> |
| 19 | #include <sys/stat.h> |
| 20 | #include <sys/types.h> |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 21 | #include <deque> |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 22 | |
| 23 | // Include gtest.h out of order because <X11/X.h> #define's Bool & None, which |
| 24 | // gtest uses as struct names (inside a namespace). This means that |
| 25 | // #include'ing gtest after anything that pulls in X.h fails to compile. |
| 26 | // This is http://code.google.com/p/googletest/issues/detail?id=371 |
| 27 | #include "testing/gtest/include/gtest/gtest.h" |
| 28 | |
| 29 | #include "base/at_exit.h" |
| 30 | #include "base/bind.h" |
| 31 | #include "base/command_line.h" |
| 32 | #include "base/file_util.h" |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 33 | #include "base/format_macros.h" |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 34 | #include "base/md5.h" |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 35 | #include "base/platform_file.h" |
Ben Murdoch | 58e6fbe | 2013-07-26 10:20:38 +0100 | [diff] [blame] | 36 | #include "base/process/process.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 37 | #include "base/stl_util.h" |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 38 | #include "base/strings/string_number_conversions.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 39 | #include "base/strings/string_split.h" |
| 40 | #include "base/strings/stringize_macros.h" |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 41 | #include "base/strings/stringprintf.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 42 | #include "base/strings/utf_string_conversions.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 43 | #include "base/synchronization/condition_variable.h" |
| 44 | #include "base/synchronization/lock.h" |
| 45 | #include "base/synchronization/waitable_event.h" |
| 46 | #include "base/threading/thread.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 47 | #include "content/common/gpu/media/rendering_helper.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 48 | #include "content/public/common/content_switches.h" |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 49 | #include "ui/gfx/codec/png_codec.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 50 | |
| 51 | #if defined(OS_WIN) |
| 52 | #include "content/common/gpu/media/dxva_video_decode_accelerator.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 53 | #elif defined(OS_CHROMEOS) |
| 54 | #if defined(ARCH_CPU_ARMEL) |
| 55 | #include "content/common/gpu/media/exynos_video_decode_accelerator.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 56 | #elif defined(ARCH_CPU_X86_FAMILY) |
| 57 | #include "content/common/gpu/media/vaapi_video_decode_accelerator.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 58 | #include "content/common/gpu/media/vaapi_wrapper.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 59 | #endif // ARCH_CPU_ARMEL |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 60 | #else |
| 61 | #error The VideoAccelerator tests are not supported on this platform. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 62 | #endif // OS_WIN |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 63 | |
| 64 | using media::VideoDecodeAccelerator; |
| 65 | |
| 66 | namespace content { |
| 67 | namespace { |
| 68 | |
| 69 | // Values optionally filled in from flags; see main() below. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 70 | // The syntax of multiple test videos is: |
| 71 | // test-video1;test-video2;test-video3 |
| 72 | // where only the first video is required and other optional videos would be |
| 73 | // decoded by concurrent decoders. |
| 74 | // The syntax of each test-video is: |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 75 | // filename:width:height:numframes:numfragments:minFPSwithRender:minFPSnoRender |
| 76 | // where only the first field is required. Value details: |
| 77 | // - |filename| must be an h264 Annex B (NAL) stream or an IVF VP8 stream. |
| 78 | // - |width| and |height| are in pixels. |
| 79 | // - |numframes| is the number of picture frames in the file. |
| 80 | // - |numfragments| NALU (h264) or frame (VP8) count in the stream. |
| 81 | // - |minFPSwithRender| and |minFPSnoRender| are minimum frames/second speeds |
| 82 | // expected to be achieved with and without rendering to the screen, resp. |
| 83 | // (the latter tests just decode speed). |
| 84 | // - |profile| is the media::VideoCodecProfile set during Initialization. |
| 85 | // An empty value for a numeric field means "ignore". |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 86 | const base::FilePath::CharType* g_test_video_data = |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 87 | // FILE_PATH_LITERAL("test-25fps.vp8:320:240:250:250:50:175:11"); |
| 88 | FILE_PATH_LITERAL("test-25fps.h264:320:240:250:258:50:175:1"); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 89 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 90 | // The path of the frame delivery time log. We can enable the log and specify |
| 91 | // the filename by the "--frame_delivery_log" switch. |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 92 | const base::FilePath::CharType* g_frame_delivery_log = NULL; |
| 93 | |
| 94 | // The value is set by the switch "--rendering_fps". |
| 95 | double g_rendering_fps = 0; |
| 96 | |
| 97 | // Disable rendering, the value is set by the switch "--disable_rendering". |
| 98 | bool g_disable_rendering = false; |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 99 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 100 | // Magic constants for differentiating the reasons for NotifyResetDone being |
| 101 | // called. |
| 102 | enum ResetPoint { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 103 | START_OF_STREAM_RESET = -3, |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 104 | MID_STREAM_RESET = -2, |
| 105 | END_OF_STREAM_RESET = -1 |
| 106 | }; |
| 107 | |
| 108 | const int kMaxResetAfterFrameNum = 100; |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 109 | const int kMaxFramesToDelayReuse = 64; |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 110 | const base::TimeDelta kReuseDelay = base::TimeDelta::FromSeconds(1); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 111 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 112 | struct TestVideoFile { |
| 113 | explicit TestVideoFile(base::FilePath::StringType file_name) |
| 114 | : file_name(file_name), |
| 115 | width(-1), |
| 116 | height(-1), |
| 117 | num_frames(-1), |
| 118 | num_fragments(-1), |
| 119 | min_fps_render(-1), |
| 120 | min_fps_no_render(-1), |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 121 | profile(-1), |
| 122 | reset_after_frame_num(END_OF_STREAM_RESET) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 123 | } |
| 124 | |
| 125 | base::FilePath::StringType file_name; |
| 126 | int width; |
| 127 | int height; |
| 128 | int num_frames; |
| 129 | int num_fragments; |
| 130 | int min_fps_render; |
| 131 | int min_fps_no_render; |
| 132 | int profile; |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 133 | int reset_after_frame_num; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 134 | std::string data_str; |
| 135 | }; |
| 136 | |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 137 | // Presumed minimal display size. |
| 138 | const gfx::Size kThumbnailsDisplaySize(1366, 768); |
| 139 | const gfx::Size kThumbnailsPageSize(1600, 1200); |
| 140 | const gfx::Size kThumbnailSize(160, 120); |
| 141 | const int kMD5StringLength = 32; |
| 142 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 143 | // Parse |data| into its constituent parts, set the various output fields |
| 144 | // accordingly, and read in video stream. CHECK-fails on unexpected or |
| 145 | // missing required data. Unspecified optional fields are set to -1. |
| 146 | void ParseAndReadTestVideoData(base::FilePath::StringType data, |
| 147 | size_t num_concurrent_decoders, |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 148 | int reset_point, |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 149 | std::vector<TestVideoFile*>* test_video_files) { |
| 150 | std::vector<base::FilePath::StringType> entries; |
| 151 | base::SplitString(data, ';', &entries); |
| 152 | CHECK_GE(entries.size(), 1U) << data; |
| 153 | for (size_t index = 0; index < entries.size(); ++index) { |
| 154 | std::vector<base::FilePath::StringType> fields; |
| 155 | base::SplitString(entries[index], ':', &fields); |
| 156 | CHECK_GE(fields.size(), 1U) << entries[index]; |
| 157 | CHECK_LE(fields.size(), 8U) << entries[index]; |
| 158 | TestVideoFile* video_file = new TestVideoFile(fields[0]); |
| 159 | if (!fields[1].empty()) |
| 160 | CHECK(base::StringToInt(fields[1], &video_file->width)); |
| 161 | if (!fields[2].empty()) |
| 162 | CHECK(base::StringToInt(fields[2], &video_file->height)); |
| 163 | if (!fields[3].empty()) { |
| 164 | CHECK(base::StringToInt(fields[3], &video_file->num_frames)); |
| 165 | // If we reset mid-stream and start playback over, account for frames |
| 166 | // that are decoded twice in our expectations. |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 167 | if (video_file->num_frames > 0 && reset_point == MID_STREAM_RESET) { |
| 168 | // Reset should not go beyond the last frame; reset after the first |
| 169 | // frame for short videos. |
| 170 | video_file->reset_after_frame_num = kMaxResetAfterFrameNum; |
| 171 | if (video_file->num_frames <= kMaxResetAfterFrameNum) |
| 172 | video_file->reset_after_frame_num = 1; |
| 173 | video_file->num_frames += video_file->reset_after_frame_num; |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 174 | } else { |
| 175 | video_file->reset_after_frame_num = reset_point; |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 176 | } |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 177 | } |
| 178 | if (!fields[4].empty()) |
| 179 | CHECK(base::StringToInt(fields[4], &video_file->num_fragments)); |
| 180 | if (!fields[5].empty()) { |
| 181 | CHECK(base::StringToInt(fields[5], &video_file->min_fps_render)); |
| 182 | video_file->min_fps_render /= num_concurrent_decoders; |
| 183 | } |
| 184 | if (!fields[6].empty()) { |
| 185 | CHECK(base::StringToInt(fields[6], &video_file->min_fps_no_render)); |
| 186 | video_file->min_fps_no_render /= num_concurrent_decoders; |
| 187 | } |
| 188 | if (!fields[7].empty()) |
| 189 | CHECK(base::StringToInt(fields[7], &video_file->profile)); |
| 190 | |
| 191 | // Read in the video data. |
| 192 | base::FilePath filepath(video_file->file_name); |
| 193 | CHECK(file_util::ReadFileToString(filepath, &video_file->data_str)) |
| 194 | << "test_video_file: " << filepath.MaybeAsASCII(); |
| 195 | |
| 196 | test_video_files->push_back(video_file); |
| 197 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 198 | } |
| 199 | |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 200 | // Read in golden MD5s for the thumbnailed rendering of this video |
| 201 | void ReadGoldenThumbnailMD5s(const TestVideoFile* video_file, |
| 202 | std::vector<std::string>* md5_strings) { |
| 203 | base::FilePath filepath(video_file->file_name); |
| 204 | filepath = filepath.AddExtension(FILE_PATH_LITERAL(".md5")); |
| 205 | std::string all_md5s; |
| 206 | file_util::ReadFileToString(filepath, &all_md5s); |
| 207 | base::SplitString(all_md5s, '\n', md5_strings); |
| 208 | // Check these are legitimate MD5s. |
| 209 | for (std::vector<std::string>::iterator md5_string = md5_strings->begin(); |
| 210 | md5_string != md5_strings->end(); ++md5_string) { |
| 211 | // Ignore the empty string added by SplitString |
| 212 | if (!md5_string->length()) |
| 213 | continue; |
| 214 | |
| 215 | CHECK_EQ(static_cast<int>(md5_string->length()), |
| 216 | kMD5StringLength) << *md5_string; |
| 217 | bool hex_only = std::count_if(md5_string->begin(), |
| 218 | md5_string->end(), isxdigit) == |
| 219 | kMD5StringLength; |
| 220 | CHECK(hex_only) << *md5_string; |
| 221 | } |
| 222 | CHECK_GE(md5_strings->size(), 1U) << all_md5s; |
| 223 | } |
| 224 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 225 | // State of the GLRenderingVDAClient below. Order matters here as the test |
| 226 | // makes assumptions about it. |
| 227 | enum ClientState { |
| 228 | CS_CREATED = 0, |
| 229 | CS_DECODER_SET = 1, |
| 230 | CS_INITIALIZED = 2, |
| 231 | CS_FLUSHING = 3, |
| 232 | CS_FLUSHED = 4, |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 233 | CS_RESETTING = 5, |
| 234 | CS_RESET = 6, |
| 235 | CS_ERROR = 7, |
| 236 | CS_DESTROYED = 8, |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 237 | CS_MAX, // Must be last entry. |
| 238 | }; |
| 239 | |
| 240 | // Helper class allowing one thread to wait on a notification from another. |
| 241 | // If notifications come in faster than they are Wait()'d for, they are |
| 242 | // accumulated (so exactly as many Wait() calls will unblock as Notify() calls |
| 243 | // were made, regardless of order). |
| 244 | class ClientStateNotification { |
| 245 | public: |
| 246 | ClientStateNotification(); |
| 247 | ~ClientStateNotification(); |
| 248 | |
| 249 | // Used to notify a single waiter of a ClientState. |
| 250 | void Notify(ClientState state); |
| 251 | // Used by waiters to wait for the next ClientState Notification. |
| 252 | ClientState Wait(); |
| 253 | private: |
| 254 | base::Lock lock_; |
| 255 | base::ConditionVariable cv_; |
| 256 | std::queue<ClientState> pending_states_for_notification_; |
| 257 | }; |
| 258 | |
| 259 | ClientStateNotification::ClientStateNotification() : cv_(&lock_) {} |
| 260 | |
| 261 | ClientStateNotification::~ClientStateNotification() {} |
| 262 | |
| 263 | void ClientStateNotification::Notify(ClientState state) { |
| 264 | base::AutoLock auto_lock(lock_); |
| 265 | pending_states_for_notification_.push(state); |
| 266 | cv_.Signal(); |
| 267 | } |
| 268 | |
| 269 | ClientState ClientStateNotification::Wait() { |
| 270 | base::AutoLock auto_lock(lock_); |
| 271 | while (pending_states_for_notification_.empty()) |
| 272 | cv_.Wait(); |
| 273 | ClientState ret = pending_states_for_notification_.front(); |
| 274 | pending_states_for_notification_.pop(); |
| 275 | return ret; |
| 276 | } |
| 277 | |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 278 | // A wrapper client that throttles the PictureReady callbacks to a given rate. |
| 279 | // It may drops or queues frame to deliver them on time. |
| 280 | class ThrottlingVDAClient : public VideoDecodeAccelerator::Client, |
| 281 | public base::SupportsWeakPtr<ThrottlingVDAClient> { |
| 282 | public: |
| 283 | // Callback invoked whan the picture is dropped and should be reused for |
| 284 | // the decoder again. |
| 285 | typedef base::Callback<void(int32 picture_buffer_id)> ReusePictureCB; |
| 286 | |
| 287 | ThrottlingVDAClient(VideoDecodeAccelerator::Client* client, |
| 288 | double fps, |
| 289 | ReusePictureCB reuse_picture_cb); |
| 290 | virtual ~ThrottlingVDAClient(); |
| 291 | |
| 292 | // VideoDecodeAccelerator::Client implementation |
| 293 | virtual void ProvidePictureBuffers(uint32 requested_num_of_buffers, |
| 294 | const gfx::Size& dimensions, |
| 295 | uint32 texture_target) OVERRIDE; |
| 296 | virtual void DismissPictureBuffer(int32 picture_buffer_id) OVERRIDE; |
| 297 | virtual void PictureReady(const media::Picture& picture) OVERRIDE; |
| 298 | virtual void NotifyInitializeDone() OVERRIDE; |
| 299 | virtual void NotifyEndOfBitstreamBuffer(int32 bitstream_buffer_id) OVERRIDE; |
| 300 | virtual void NotifyFlushDone() OVERRIDE; |
| 301 | virtual void NotifyResetDone() OVERRIDE; |
| 302 | virtual void NotifyError(VideoDecodeAccelerator::Error error) OVERRIDE; |
| 303 | |
| 304 | int num_decoded_frames() { return num_decoded_frames_; } |
| 305 | |
| 306 | private: |
| 307 | |
| 308 | void CallClientPictureReady(int version); |
| 309 | |
| 310 | VideoDecodeAccelerator::Client* client_; |
| 311 | ReusePictureCB reuse_picture_cb_; |
| 312 | base::TimeTicks next_frame_delivered_time_; |
| 313 | base::TimeDelta frame_duration_; |
| 314 | |
| 315 | int num_decoded_frames_; |
| 316 | int stream_version_; |
| 317 | std::deque<media::Picture> pending_pictures_; |
| 318 | |
| 319 | DISALLOW_IMPLICIT_CONSTRUCTORS(ThrottlingVDAClient); |
| 320 | }; |
| 321 | |
| 322 | ThrottlingVDAClient::ThrottlingVDAClient(VideoDecodeAccelerator::Client* client, |
| 323 | double fps, |
| 324 | ReusePictureCB reuse_picture_cb) |
| 325 | : client_(client), |
| 326 | reuse_picture_cb_(reuse_picture_cb), |
| 327 | num_decoded_frames_(0), |
| 328 | stream_version_(0) { |
| 329 | CHECK(client_); |
| 330 | CHECK_GT(fps, 0); |
| 331 | frame_duration_ = base::TimeDelta::FromSeconds(1) / fps; |
| 332 | } |
| 333 | |
| 334 | ThrottlingVDAClient::~ThrottlingVDAClient() {} |
| 335 | |
| 336 | void ThrottlingVDAClient::ProvidePictureBuffers(uint32 requested_num_of_buffers, |
| 337 | const gfx::Size& dimensions, |
| 338 | uint32 texture_target) { |
| 339 | client_->ProvidePictureBuffers( |
| 340 | requested_num_of_buffers, dimensions, texture_target); |
| 341 | } |
| 342 | |
| 343 | void ThrottlingVDAClient::DismissPictureBuffer(int32 picture_buffer_id) { |
| 344 | client_->DismissPictureBuffer(picture_buffer_id); |
| 345 | } |
| 346 | |
| 347 | void ThrottlingVDAClient::PictureReady(const media::Picture& picture) { |
| 348 | ++num_decoded_frames_; |
| 349 | |
| 350 | if (pending_pictures_.empty()) { |
| 351 | base::TimeDelta delay = |
| 352 | next_frame_delivered_time_.is_null() |
| 353 | ? base::TimeDelta() |
| 354 | : next_frame_delivered_time_ - base::TimeTicks::Now(); |
| 355 | base::MessageLoop::current()->PostDelayedTask( |
| 356 | FROM_HERE, |
| 357 | base::Bind(&ThrottlingVDAClient::CallClientPictureReady, |
| 358 | AsWeakPtr(), |
| 359 | stream_version_), |
| 360 | delay); |
| 361 | } |
| 362 | pending_pictures_.push_back(picture); |
| 363 | } |
| 364 | |
| 365 | void ThrottlingVDAClient::CallClientPictureReady(int version) { |
| 366 | // Just return if we have reset the decoder |
| 367 | if (version != stream_version_) |
| 368 | return; |
| 369 | |
| 370 | base::TimeTicks now = base::TimeTicks::Now(); |
| 371 | |
| 372 | if (next_frame_delivered_time_.is_null()) |
| 373 | next_frame_delivered_time_ = now; |
| 374 | |
| 375 | if (next_frame_delivered_time_ + frame_duration_ < now) { |
| 376 | // Too late, drop the frame |
| 377 | reuse_picture_cb_.Run(pending_pictures_.front().picture_buffer_id()); |
| 378 | } else { |
| 379 | client_->PictureReady(pending_pictures_.front()); |
| 380 | } |
| 381 | |
| 382 | pending_pictures_.pop_front(); |
| 383 | next_frame_delivered_time_ += frame_duration_; |
| 384 | if (!pending_pictures_.empty()) { |
| 385 | base::MessageLoop::current()->PostDelayedTask( |
| 386 | FROM_HERE, |
| 387 | base::Bind(&ThrottlingVDAClient::CallClientPictureReady, |
| 388 | AsWeakPtr(), |
| 389 | stream_version_), |
| 390 | next_frame_delivered_time_ - base::TimeTicks::Now()); |
| 391 | } |
| 392 | } |
| 393 | |
| 394 | void ThrottlingVDAClient::NotifyInitializeDone() { |
| 395 | client_->NotifyInitializeDone(); |
| 396 | } |
| 397 | |
| 398 | void ThrottlingVDAClient::NotifyEndOfBitstreamBuffer( |
| 399 | int32 bitstream_buffer_id) { |
| 400 | client_->NotifyEndOfBitstreamBuffer(bitstream_buffer_id); |
| 401 | } |
| 402 | |
| 403 | void ThrottlingVDAClient::NotifyFlushDone() { |
| 404 | if (!pending_pictures_.empty()) { |
| 405 | base::MessageLoop::current()->PostDelayedTask( |
| 406 | FROM_HERE, |
| 407 | base::Bind(&ThrottlingVDAClient::NotifyFlushDone, |
| 408 | base::Unretained(this)), |
| 409 | next_frame_delivered_time_ - base::TimeTicks::Now()); |
| 410 | return; |
| 411 | } |
| 412 | client_->NotifyFlushDone(); |
| 413 | } |
| 414 | |
| 415 | void ThrottlingVDAClient::NotifyResetDone() { |
| 416 | ++stream_version_; |
| 417 | while (!pending_pictures_.empty()) { |
| 418 | reuse_picture_cb_.Run(pending_pictures_.front().picture_buffer_id()); |
| 419 | pending_pictures_.pop_front(); |
| 420 | } |
| 421 | next_frame_delivered_time_ = base::TimeTicks(); |
| 422 | client_->NotifyResetDone(); |
| 423 | } |
| 424 | |
| 425 | void ThrottlingVDAClient::NotifyError(VideoDecodeAccelerator::Error error) { |
| 426 | client_->NotifyError(error); |
| 427 | } |
| 428 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 429 | // Client that can accept callbacks from a VideoDecodeAccelerator and is used by |
| 430 | // the TESTs below. |
| 431 | class GLRenderingVDAClient : public VideoDecodeAccelerator::Client { |
| 432 | public: |
| 433 | // Doesn't take ownership of |rendering_helper| or |note|, which must outlive |
| 434 | // |*this|. |
| 435 | // |num_fragments_per_decode| counts NALUs for h264 and frames for VP8. |
| 436 | // |num_play_throughs| indicates how many times to play through the video. |
| 437 | // |reset_after_frame_num| can be a frame number >=0 indicating a mid-stream |
| 438 | // Reset() should be done after that frame number is delivered, or |
| 439 | // END_OF_STREAM_RESET to indicate no mid-stream Reset(). |
| 440 | // |delete_decoder_state| indicates when the underlying decoder should be |
| 441 | // Destroy()'d and deleted and can take values: N<0: delete after -N Decode() |
| 442 | // calls have been made, N>=0 means interpret as ClientState. |
| 443 | // Both |reset_after_frame_num| & |delete_decoder_state| apply only to the |
| 444 | // last play-through (governed by |num_play_throughs|). |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 445 | // |rendering_fps| indicates the target rendering fps. 0 means no target fps |
| 446 | // and it would render as fast as possible. |
| 447 | // |suppress_rendering| indicates GL rendering is suppressed or not. |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 448 | // After |delay_reuse_after_frame_num| frame has been delivered, the client |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 449 | // will start delaying the call to ReusePictureBuffer() for kReuseDelay. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 450 | GLRenderingVDAClient(RenderingHelper* rendering_helper, |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 451 | int rendering_window_id, |
| 452 | ClientStateNotification* note, |
| 453 | const std::string& encoded_data, |
| 454 | int num_fragments_per_decode, |
| 455 | int num_in_flight_decodes, |
| 456 | int num_play_throughs, |
| 457 | int reset_after_frame_num, |
| 458 | int delete_decoder_state, |
| 459 | int frame_width, |
| 460 | int frame_height, |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 461 | int profile, |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 462 | double rendering_fps, |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 463 | bool suppress_rendering, |
| 464 | int delay_reuse_after_frame_num); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 465 | virtual ~GLRenderingVDAClient(); |
| 466 | void CreateDecoder(); |
| 467 | |
| 468 | // VideoDecodeAccelerator::Client implementation. |
| 469 | // The heart of the Client. |
| 470 | virtual void ProvidePictureBuffers(uint32 requested_num_of_buffers, |
| 471 | const gfx::Size& dimensions, |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 472 | uint32 texture_target) OVERRIDE; |
| 473 | virtual void DismissPictureBuffer(int32 picture_buffer_id) OVERRIDE; |
| 474 | virtual void PictureReady(const media::Picture& picture) OVERRIDE; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 475 | // Simple state changes. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 476 | virtual void NotifyInitializeDone() OVERRIDE; |
| 477 | virtual void NotifyEndOfBitstreamBuffer(int32 bitstream_buffer_id) OVERRIDE; |
| 478 | virtual void NotifyFlushDone() OVERRIDE; |
| 479 | virtual void NotifyResetDone() OVERRIDE; |
| 480 | virtual void NotifyError(VideoDecodeAccelerator::Error error) OVERRIDE; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 481 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 482 | void OutputFrameDeliveryTimes(base::PlatformFile output); |
| 483 | |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 484 | void NotifyFrameDropped(int32 picture_buffer_id); |
| 485 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 486 | // Simple getters for inspecting the state of the Client. |
| 487 | int num_done_bitstream_buffers() { return num_done_bitstream_buffers_; } |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 488 | int num_skipped_fragments() { return num_skipped_fragments_; } |
| 489 | int num_queued_fragments() { return num_queued_fragments_; } |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 490 | int num_decoded_frames(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 491 | double frames_per_second(); |
| 492 | bool decoder_deleted() { return !decoder_.get(); } |
| 493 | |
| 494 | private: |
| 495 | typedef std::map<int, media::PictureBuffer*> PictureBufferById; |
| 496 | |
| 497 | void SetState(ClientState new_state); |
| 498 | |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 499 | // Delete the associated decoder helper. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 500 | void DeleteDecoder(); |
| 501 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 502 | // Compute & return the first encoded bytes (including a start frame) to send |
| 503 | // to the decoder, starting at |start_pos| and returning |
| 504 | // |num_fragments_per_decode| units. Skips to the first decodable position. |
| 505 | std::string GetBytesForFirstFragments(size_t start_pos, size_t* end_pos); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 506 | // Compute & return the next encoded bytes to send to the decoder (based on |
| 507 | // |start_pos| & |num_fragments_per_decode_|). |
| 508 | std::string GetBytesForNextFragments(size_t start_pos, size_t* end_pos); |
| 509 | // Helpers for GetRangeForNextFragments above. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 510 | void GetBytesForNextNALU(size_t start_pos, size_t* end_pos); // For h.264. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 511 | std::string GetBytesForNextFrames( |
| 512 | size_t start_pos, size_t* end_pos); // For VP8. |
| 513 | |
| 514 | // Request decode of the next batch of fragments in the encoded data. |
| 515 | void DecodeNextFragments(); |
| 516 | |
| 517 | RenderingHelper* rendering_helper_; |
| 518 | int rendering_window_id_; |
| 519 | std::string encoded_data_; |
| 520 | const int num_fragments_per_decode_; |
| 521 | const int num_in_flight_decodes_; |
| 522 | int outstanding_decodes_; |
| 523 | size_t encoded_data_next_pos_to_decode_; |
| 524 | int next_bitstream_buffer_id_; |
| 525 | ClientStateNotification* note_; |
| 526 | scoped_ptr<VideoDecodeAccelerator> decoder_; |
| 527 | std::set<int> outstanding_texture_ids_; |
| 528 | int remaining_play_throughs_; |
| 529 | int reset_after_frame_num_; |
| 530 | int delete_decoder_state_; |
| 531 | ClientState state_; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 532 | int num_skipped_fragments_; |
| 533 | int num_queued_fragments_; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 534 | int num_decoded_frames_; |
| 535 | int num_done_bitstream_buffers_; |
| 536 | PictureBufferById picture_buffers_by_id_; |
| 537 | base::TimeTicks initialize_done_ticks_; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 538 | int profile_; |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 539 | bool suppress_rendering_; |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 540 | std::vector<base::TimeTicks> frame_delivery_times_; |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 541 | int delay_reuse_after_frame_num_; |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 542 | scoped_ptr<ThrottlingVDAClient> throttling_client_; |
| 543 | |
| 544 | DISALLOW_IMPLICIT_CONSTRUCTORS(GLRenderingVDAClient); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 545 | }; |
| 546 | |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 547 | GLRenderingVDAClient::GLRenderingVDAClient(RenderingHelper* rendering_helper, |
| 548 | int rendering_window_id, |
| 549 | ClientStateNotification* note, |
| 550 | const std::string& encoded_data, |
| 551 | int num_fragments_per_decode, |
| 552 | int num_in_flight_decodes, |
| 553 | int num_play_throughs, |
| 554 | int reset_after_frame_num, |
| 555 | int delete_decoder_state, |
| 556 | int frame_width, |
| 557 | int frame_height, |
| 558 | int profile, |
| 559 | double rendering_fps, |
| 560 | bool suppress_rendering, |
| 561 | int delay_reuse_after_frame_num) |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 562 | : rendering_helper_(rendering_helper), |
| 563 | rendering_window_id_(rendering_window_id), |
| 564 | encoded_data_(encoded_data), |
| 565 | num_fragments_per_decode_(num_fragments_per_decode), |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 566 | num_in_flight_decodes_(num_in_flight_decodes), |
| 567 | outstanding_decodes_(0), |
| 568 | encoded_data_next_pos_to_decode_(0), |
| 569 | next_bitstream_buffer_id_(0), |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 570 | note_(note), |
| 571 | remaining_play_throughs_(num_play_throughs), |
| 572 | reset_after_frame_num_(reset_after_frame_num), |
| 573 | delete_decoder_state_(delete_decoder_state), |
| 574 | state_(CS_CREATED), |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 575 | num_skipped_fragments_(0), |
| 576 | num_queued_fragments_(0), |
| 577 | num_decoded_frames_(0), |
| 578 | num_done_bitstream_buffers_(0), |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 579 | profile_(profile), |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 580 | suppress_rendering_(suppress_rendering), |
| 581 | delay_reuse_after_frame_num_(delay_reuse_after_frame_num) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 582 | CHECK_GT(num_fragments_per_decode, 0); |
| 583 | CHECK_GT(num_in_flight_decodes, 0); |
| 584 | CHECK_GT(num_play_throughs, 0); |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 585 | CHECK_GE(rendering_fps, 0); |
| 586 | if (rendering_fps > 0) |
| 587 | throttling_client_.reset(new ThrottlingVDAClient( |
| 588 | this, |
| 589 | rendering_fps, |
| 590 | base::Bind(&GLRenderingVDAClient::NotifyFrameDropped, |
| 591 | base::Unretained(this)))); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 592 | } |
| 593 | |
| 594 | GLRenderingVDAClient::~GLRenderingVDAClient() { |
| 595 | DeleteDecoder(); // Clean up in case of expected error. |
| 596 | CHECK(decoder_deleted()); |
| 597 | STLDeleteValues(&picture_buffers_by_id_); |
| 598 | SetState(CS_DESTROYED); |
| 599 | } |
| 600 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 601 | static bool DoNothingReturnTrue() { return true; } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 602 | |
| 603 | void GLRenderingVDAClient::CreateDecoder() { |
| 604 | CHECK(decoder_deleted()); |
| 605 | CHECK(!decoder_.get()); |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 606 | |
| 607 | VideoDecodeAccelerator::Client* client = this; |
| 608 | if (throttling_client_) |
| 609 | client = throttling_client_.get(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 610 | #if defined(OS_WIN) |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 611 | decoder_.reset( |
| 612 | new DXVAVideoDecodeAccelerator(client, base::Bind(&DoNothingReturnTrue))); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 613 | #elif defined(OS_CHROMEOS) |
| 614 | #if defined(ARCH_CPU_ARMEL) |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 615 | decoder_.reset(new ExynosVideoDecodeAccelerator( |
| 616 | static_cast<EGLDisplay>(rendering_helper_->GetGLDisplay()), |
| 617 | static_cast<EGLContext>(rendering_helper_->GetGLContext()), |
| 618 | client, |
| 619 | base::Bind(&DoNothingReturnTrue))); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 620 | #elif defined(ARCH_CPU_X86_FAMILY) |
| 621 | decoder_.reset(new VaapiVideoDecodeAccelerator( |
| 622 | static_cast<Display*>(rendering_helper_->GetGLDisplay()), |
| 623 | static_cast<GLXContext>(rendering_helper_->GetGLContext()), |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 624 | client, |
| 625 | base::Bind(&DoNothingReturnTrue))); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 626 | #endif // ARCH_CPU_ARMEL |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 627 | #endif // OS_WIN |
| 628 | CHECK(decoder_.get()); |
| 629 | SetState(CS_DECODER_SET); |
| 630 | if (decoder_deleted()) |
| 631 | return; |
| 632 | |
| 633 | // Configure the decoder. |
| 634 | media::VideoCodecProfile profile = media::H264PROFILE_BASELINE; |
| 635 | if (profile_ != -1) |
| 636 | profile = static_cast<media::VideoCodecProfile>(profile_); |
| 637 | CHECK(decoder_->Initialize(profile)); |
| 638 | } |
| 639 | |
| 640 | void GLRenderingVDAClient::ProvidePictureBuffers( |
| 641 | uint32 requested_num_of_buffers, |
| 642 | const gfx::Size& dimensions, |
| 643 | uint32 texture_target) { |
| 644 | if (decoder_deleted()) |
| 645 | return; |
| 646 | std::vector<media::PictureBuffer> buffers; |
| 647 | |
| 648 | for (uint32 i = 0; i < requested_num_of_buffers; ++i) { |
| 649 | uint32 id = picture_buffers_by_id_.size(); |
| 650 | uint32 texture_id; |
| 651 | base::WaitableEvent done(false, false); |
| 652 | rendering_helper_->CreateTexture( |
| 653 | rendering_window_id_, texture_target, &texture_id, &done); |
| 654 | done.Wait(); |
| 655 | CHECK(outstanding_texture_ids_.insert(texture_id).second); |
| 656 | media::PictureBuffer* buffer = |
| 657 | new media::PictureBuffer(id, dimensions, texture_id); |
| 658 | CHECK(picture_buffers_by_id_.insert(std::make_pair(id, buffer)).second); |
| 659 | buffers.push_back(*buffer); |
| 660 | } |
| 661 | decoder_->AssignPictureBuffers(buffers); |
| 662 | } |
| 663 | |
| 664 | void GLRenderingVDAClient::DismissPictureBuffer(int32 picture_buffer_id) { |
| 665 | PictureBufferById::iterator it = |
| 666 | picture_buffers_by_id_.find(picture_buffer_id); |
| 667 | CHECK(it != picture_buffers_by_id_.end()); |
| 668 | CHECK_EQ(outstanding_texture_ids_.erase(it->second->texture_id()), 1U); |
| 669 | rendering_helper_->DeleteTexture(it->second->texture_id()); |
| 670 | delete it->second; |
| 671 | picture_buffers_by_id_.erase(it); |
| 672 | } |
| 673 | |
| 674 | void GLRenderingVDAClient::PictureReady(const media::Picture& picture) { |
| 675 | // We shouldn't be getting pictures delivered after Reset has completed. |
| 676 | CHECK_LT(state_, CS_RESET); |
| 677 | |
| 678 | if (decoder_deleted()) |
| 679 | return; |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 680 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 681 | frame_delivery_times_.push_back(base::TimeTicks::Now()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 682 | |
| 683 | CHECK_LE(picture.bitstream_buffer_id(), next_bitstream_buffer_id_); |
| 684 | ++num_decoded_frames_; |
| 685 | |
| 686 | // Mid-stream reset applies only to the last play-through per constructor |
| 687 | // comment. |
| 688 | if (remaining_play_throughs_ == 1 && |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 689 | reset_after_frame_num_ == num_decoded_frames()) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 690 | reset_after_frame_num_ = MID_STREAM_RESET; |
| 691 | decoder_->Reset(); |
| 692 | // Re-start decoding from the beginning of the stream to avoid needing to |
| 693 | // know how to find I-frames and so on in this test. |
| 694 | encoded_data_next_pos_to_decode_ = 0; |
| 695 | } |
| 696 | |
| 697 | media::PictureBuffer* picture_buffer = |
| 698 | picture_buffers_by_id_[picture.picture_buffer_id()]; |
| 699 | CHECK(picture_buffer); |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 700 | if (!suppress_rendering_) { |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 701 | rendering_helper_->RenderTexture(picture_buffer->texture_id()); |
| 702 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 703 | |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 704 | if (num_decoded_frames() > delay_reuse_after_frame_num_) { |
| 705 | base::MessageLoop::current()->PostDelayedTask( |
| 706 | FROM_HERE, |
| 707 | base::Bind(&VideoDecodeAccelerator::ReusePictureBuffer, |
| 708 | decoder_->AsWeakPtr(), |
| 709 | picture.picture_buffer_id()), |
| 710 | kReuseDelay); |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 711 | } else { |
| 712 | decoder_->ReusePictureBuffer(picture.picture_buffer_id()); |
| 713 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 714 | } |
| 715 | |
| 716 | void GLRenderingVDAClient::NotifyInitializeDone() { |
| 717 | SetState(CS_INITIALIZED); |
| 718 | initialize_done_ticks_ = base::TimeTicks::Now(); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 719 | |
| 720 | if (reset_after_frame_num_ == START_OF_STREAM_RESET) { |
| 721 | decoder_->Reset(); |
| 722 | return; |
| 723 | } |
| 724 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 725 | for (int i = 0; i < num_in_flight_decodes_; ++i) |
| 726 | DecodeNextFragments(); |
| 727 | DCHECK_EQ(outstanding_decodes_, num_in_flight_decodes_); |
| 728 | } |
| 729 | |
| 730 | void GLRenderingVDAClient::NotifyEndOfBitstreamBuffer( |
| 731 | int32 bitstream_buffer_id) { |
| 732 | // TODO(fischman): this test currently relies on this notification to make |
| 733 | // forward progress during a Reset(). But the VDA::Reset() API doesn't |
| 734 | // guarantee this, so stop relying on it (and remove the notifications from |
| 735 | // VaapiVideoDecodeAccelerator::FinishReset()). |
| 736 | ++num_done_bitstream_buffers_; |
| 737 | --outstanding_decodes_; |
| 738 | DecodeNextFragments(); |
| 739 | } |
| 740 | |
| 741 | void GLRenderingVDAClient::NotifyFlushDone() { |
| 742 | if (decoder_deleted()) |
| 743 | return; |
| 744 | SetState(CS_FLUSHED); |
| 745 | --remaining_play_throughs_; |
| 746 | DCHECK_GE(remaining_play_throughs_, 0); |
| 747 | if (decoder_deleted()) |
| 748 | return; |
| 749 | decoder_->Reset(); |
| 750 | SetState(CS_RESETTING); |
| 751 | } |
| 752 | |
| 753 | void GLRenderingVDAClient::NotifyResetDone() { |
| 754 | if (decoder_deleted()) |
| 755 | return; |
| 756 | |
| 757 | if (reset_after_frame_num_ == MID_STREAM_RESET) { |
| 758 | reset_after_frame_num_ = END_OF_STREAM_RESET; |
| 759 | DecodeNextFragments(); |
| 760 | return; |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 761 | } else if (reset_after_frame_num_ == START_OF_STREAM_RESET) { |
| 762 | reset_after_frame_num_ = END_OF_STREAM_RESET; |
| 763 | for (int i = 0; i < num_in_flight_decodes_; ++i) |
| 764 | DecodeNextFragments(); |
| 765 | return; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 766 | } |
| 767 | |
| 768 | if (remaining_play_throughs_) { |
| 769 | encoded_data_next_pos_to_decode_ = 0; |
| 770 | NotifyInitializeDone(); |
| 771 | return; |
| 772 | } |
| 773 | |
| 774 | SetState(CS_RESET); |
| 775 | if (!decoder_deleted()) |
| 776 | DeleteDecoder(); |
| 777 | } |
| 778 | |
| 779 | void GLRenderingVDAClient::NotifyError(VideoDecodeAccelerator::Error error) { |
| 780 | SetState(CS_ERROR); |
| 781 | } |
| 782 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 783 | void GLRenderingVDAClient::OutputFrameDeliveryTimes(base::PlatformFile output) { |
| 784 | std::string s = base::StringPrintf("frame count: %" PRIuS "\n", |
| 785 | frame_delivery_times_.size()); |
| 786 | base::WritePlatformFileAtCurrentPos(output, s.data(), s.length()); |
| 787 | base::TimeTicks t0 = initialize_done_ticks_; |
| 788 | for (size_t i = 0; i < frame_delivery_times_.size(); ++i) { |
| 789 | s = base::StringPrintf("frame %04" PRIuS ": %" PRId64 " us\n", |
| 790 | i, |
| 791 | (frame_delivery_times_[i] - t0).InMicroseconds()); |
| 792 | t0 = frame_delivery_times_[i]; |
| 793 | base::WritePlatformFileAtCurrentPos(output, s.data(), s.length()); |
| 794 | } |
| 795 | } |
| 796 | |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 797 | void GLRenderingVDAClient::NotifyFrameDropped(int32 picture_buffer_id) { |
| 798 | decoder_->ReusePictureBuffer(picture_buffer_id); |
| 799 | } |
| 800 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 801 | static bool LookingAtNAL(const std::string& encoded, size_t pos) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 802 | return encoded[pos] == 0 && encoded[pos + 1] == 0 && |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 803 | encoded[pos + 2] == 0 && encoded[pos + 3] == 1; |
| 804 | } |
| 805 | |
| 806 | void GLRenderingVDAClient::SetState(ClientState new_state) { |
| 807 | note_->Notify(new_state); |
| 808 | state_ = new_state; |
| 809 | if (!remaining_play_throughs_ && new_state == delete_decoder_state_) { |
| 810 | CHECK(!decoder_deleted()); |
| 811 | DeleteDecoder(); |
| 812 | } |
| 813 | } |
| 814 | |
| 815 | void GLRenderingVDAClient::DeleteDecoder() { |
| 816 | if (decoder_deleted()) |
| 817 | return; |
| 818 | decoder_.release()->Destroy(); |
| 819 | STLClearObject(&encoded_data_); |
| 820 | for (std::set<int>::iterator it = outstanding_texture_ids_.begin(); |
| 821 | it != outstanding_texture_ids_.end(); ++it) { |
| 822 | rendering_helper_->DeleteTexture(*it); |
| 823 | } |
| 824 | outstanding_texture_ids_.clear(); |
| 825 | // Cascade through the rest of the states to simplify test code below. |
| 826 | for (int i = state_ + 1; i < CS_MAX; ++i) |
| 827 | SetState(static_cast<ClientState>(i)); |
| 828 | } |
| 829 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 830 | std::string GLRenderingVDAClient::GetBytesForFirstFragments( |
| 831 | size_t start_pos, size_t* end_pos) { |
| 832 | if (profile_ < media::H264PROFILE_MAX) { |
| 833 | *end_pos = start_pos; |
| 834 | while (*end_pos + 4 < encoded_data_.size()) { |
| 835 | if ((encoded_data_[*end_pos + 4] & 0x1f) == 0x7) // SPS start frame |
| 836 | return GetBytesForNextFragments(*end_pos, end_pos); |
| 837 | GetBytesForNextNALU(*end_pos, end_pos); |
| 838 | num_skipped_fragments_++; |
| 839 | } |
| 840 | *end_pos = start_pos; |
| 841 | return std::string(); |
| 842 | } |
| 843 | DCHECK_LE(profile_, media::VP8PROFILE_MAX); |
| 844 | return GetBytesForNextFragments(start_pos, end_pos); |
| 845 | } |
| 846 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 847 | std::string GLRenderingVDAClient::GetBytesForNextFragments( |
| 848 | size_t start_pos, size_t* end_pos) { |
| 849 | if (profile_ < media::H264PROFILE_MAX) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 850 | size_t new_end_pos = start_pos; |
| 851 | *end_pos = start_pos; |
| 852 | for (int i = 0; i < num_fragments_per_decode_; ++i) { |
| 853 | GetBytesForNextNALU(*end_pos, &new_end_pos); |
| 854 | if (*end_pos == new_end_pos) |
| 855 | break; |
| 856 | *end_pos = new_end_pos; |
| 857 | num_queued_fragments_++; |
| 858 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 859 | return encoded_data_.substr(start_pos, *end_pos - start_pos); |
| 860 | } |
| 861 | DCHECK_LE(profile_, media::VP8PROFILE_MAX); |
| 862 | return GetBytesForNextFrames(start_pos, end_pos); |
| 863 | } |
| 864 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 865 | void GLRenderingVDAClient::GetBytesForNextNALU( |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 866 | size_t start_pos, size_t* end_pos) { |
| 867 | *end_pos = start_pos; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 868 | if (*end_pos + 4 > encoded_data_.size()) |
| 869 | return; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 870 | CHECK(LookingAtNAL(encoded_data_, start_pos)); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 871 | *end_pos += 4; |
| 872 | while (*end_pos + 4 <= encoded_data_.size() && |
| 873 | !LookingAtNAL(encoded_data_, *end_pos)) { |
| 874 | ++*end_pos; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 875 | } |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 876 | if (*end_pos + 3 >= encoded_data_.size()) |
| 877 | *end_pos = encoded_data_.size(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 878 | } |
| 879 | |
| 880 | std::string GLRenderingVDAClient::GetBytesForNextFrames( |
| 881 | size_t start_pos, size_t* end_pos) { |
| 882 | // Helpful description: http://wiki.multimedia.cx/index.php?title=IVF |
| 883 | std::string bytes; |
| 884 | if (start_pos == 0) |
| 885 | start_pos = 32; // Skip IVF header. |
| 886 | *end_pos = start_pos; |
| 887 | for (int i = 0; i < num_fragments_per_decode_; ++i) { |
| 888 | uint32 frame_size = *reinterpret_cast<uint32*>(&encoded_data_[*end_pos]); |
| 889 | *end_pos += 12; // Skip frame header. |
| 890 | bytes.append(encoded_data_.substr(*end_pos, frame_size)); |
| 891 | *end_pos += frame_size; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 892 | num_queued_fragments_++; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 893 | if (*end_pos + 12 >= encoded_data_.size()) |
| 894 | return bytes; |
| 895 | } |
| 896 | return bytes; |
| 897 | } |
| 898 | |
| 899 | void GLRenderingVDAClient::DecodeNextFragments() { |
| 900 | if (decoder_deleted()) |
| 901 | return; |
| 902 | if (encoded_data_next_pos_to_decode_ == encoded_data_.size()) { |
| 903 | if (outstanding_decodes_ == 0) { |
| 904 | decoder_->Flush(); |
| 905 | SetState(CS_FLUSHING); |
| 906 | } |
| 907 | return; |
| 908 | } |
| 909 | size_t end_pos; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 910 | std::string next_fragment_bytes; |
| 911 | if (encoded_data_next_pos_to_decode_ == 0) { |
| 912 | next_fragment_bytes = GetBytesForFirstFragments(0, &end_pos); |
| 913 | } else { |
| 914 | next_fragment_bytes = |
| 915 | GetBytesForNextFragments(encoded_data_next_pos_to_decode_, &end_pos); |
| 916 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 917 | size_t next_fragment_size = next_fragment_bytes.size(); |
| 918 | |
| 919 | // Populate the shared memory buffer w/ the fragments, duplicate its handle, |
| 920 | // and hand it off to the decoder. |
| 921 | base::SharedMemory shm; |
| 922 | CHECK(shm.CreateAndMapAnonymous(next_fragment_size)); |
| 923 | memcpy(shm.memory(), next_fragment_bytes.data(), next_fragment_size); |
| 924 | base::SharedMemoryHandle dup_handle; |
| 925 | CHECK(shm.ShareToProcess(base::Process::Current().handle(), &dup_handle)); |
| 926 | media::BitstreamBuffer bitstream_buffer( |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 927 | next_bitstream_buffer_id_, dup_handle, next_fragment_size); |
| 928 | // Mask against 30 bits, to avoid (undefined) wraparound on signed integer. |
| 929 | next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & 0x3FFFFFFF; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 930 | decoder_->Decode(bitstream_buffer); |
| 931 | ++outstanding_decodes_; |
| 932 | encoded_data_next_pos_to_decode_ = end_pos; |
| 933 | |
| 934 | if (!remaining_play_throughs_ && |
| 935 | -delete_decoder_state_ == next_bitstream_buffer_id_) { |
| 936 | DeleteDecoder(); |
| 937 | } |
| 938 | } |
| 939 | |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 940 | int GLRenderingVDAClient::num_decoded_frames() { |
| 941 | return throttling_client_ ? throttling_client_->num_decoded_frames() |
| 942 | : num_decoded_frames_; |
| 943 | } |
| 944 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 945 | double GLRenderingVDAClient::frames_per_second() { |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 946 | base::TimeDelta delta = frame_delivery_times_.back() - initialize_done_ticks_; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 947 | if (delta.InSecondsF() == 0) |
| 948 | return 0; |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 949 | return num_decoded_frames() / delta.InSecondsF(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 950 | } |
| 951 | |
| 952 | // Test parameters: |
| 953 | // - Number of fragments per Decode() call. |
| 954 | // - Number of concurrent decoders. |
| 955 | // - Number of concurrent in-flight Decode() calls per decoder. |
| 956 | // - Number of play-throughs. |
| 957 | // - reset_after_frame_num: see GLRenderingVDAClient ctor. |
| 958 | // - delete_decoder_phase: see GLRenderingVDAClient ctor. |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 959 | // - whether to test slow rendering by delaying ReusePictureBuffer(). |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 960 | // - whether the video frames are rendered as thumbnails. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 961 | class VideoDecodeAcceleratorTest |
| 962 | : public ::testing::TestWithParam< |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 963 | Tuple8<int, int, int, int, ResetPoint, ClientState, bool, bool> > { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 964 | }; |
| 965 | |
| 966 | // Helper so that gtest failures emit a more readable version of the tuple than |
| 967 | // its byte representation. |
| 968 | ::std::ostream& operator<<( |
| 969 | ::std::ostream& os, |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 970 | const Tuple8<int, int, int, int, ResetPoint, ClientState, bool, bool>& t) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 971 | return os << t.a << ", " << t.b << ", " << t.c << ", " << t.d << ", " << t.e |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 972 | << ", " << t.f << ", " << t.g << ", " << t.h; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 973 | } |
| 974 | |
| 975 | // Wait for |note| to report a state and if it's not |expected_state| then |
| 976 | // assert |client| has deleted its decoder. |
| 977 | static void AssertWaitForStateOrDeleted(ClientStateNotification* note, |
| 978 | GLRenderingVDAClient* client, |
| 979 | ClientState expected_state) { |
| 980 | ClientState state = note->Wait(); |
| 981 | if (state == expected_state) return; |
| 982 | ASSERT_TRUE(client->decoder_deleted()) |
| 983 | << "Decoder not deleted but Wait() returned " << state |
| 984 | << ", instead of " << expected_state; |
| 985 | } |
| 986 | |
| 987 | // We assert a minimal number of concurrent decoders we expect to succeed. |
| 988 | // Different platforms can support more concurrent decoders, so we don't assert |
| 989 | // failure above this. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 990 | enum { kMinSupportedNumConcurrentDecoders = 3 }; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 991 | |
| 992 | // Test the most straightforward case possible: data is decoded from a single |
| 993 | // chunk and rendered to the screen. |
| 994 | TEST_P(VideoDecodeAcceleratorTest, TestSimpleDecode) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 995 | // Required for Thread to work. Not used otherwise. |
| 996 | base::ShadowingAtExitManager at_exit_manager; |
| 997 | |
| 998 | const int num_fragments_per_decode = GetParam().a; |
| 999 | const size_t num_concurrent_decoders = GetParam().b; |
| 1000 | const size_t num_in_flight_decodes = GetParam().c; |
| 1001 | const int num_play_throughs = GetParam().d; |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 1002 | const int reset_point = GetParam().e; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1003 | const int delete_decoder_state = GetParam().f; |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 1004 | bool test_reuse_delay = GetParam().g; |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1005 | const bool render_as_thumbnails = GetParam().h; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1006 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1007 | std::vector<TestVideoFile*> test_video_files; |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1008 | ParseAndReadTestVideoData(g_test_video_data, |
| 1009 | num_concurrent_decoders, |
| 1010 | reset_point, |
| 1011 | &test_video_files); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1012 | |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1013 | // Suppress GL rendering for all tests when the "--disable_rendering" is set. |
| 1014 | // Otherwise, suppress rendering in all but a few tests, to cut down overall |
| 1015 | // test runtime. |
| 1016 | const bool suppress_rendering = |
| 1017 | num_fragments_per_decode > 1 || g_disable_rendering; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1018 | |
| 1019 | std::vector<ClientStateNotification*> notes(num_concurrent_decoders, NULL); |
| 1020 | std::vector<GLRenderingVDAClient*> clients(num_concurrent_decoders, NULL); |
| 1021 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1022 | // Initialize the rendering helper. |
| 1023 | base::Thread rendering_thread("GLRenderingVDAClientThread"); |
| 1024 | base::Thread::Options options; |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 1025 | options.message_loop_type = base::MessageLoop::TYPE_DEFAULT; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1026 | #if defined(OS_WIN) |
| 1027 | // For windows the decoding thread initializes the media foundation decoder |
| 1028 | // which uses COM. We need the thread to be a UI thread. |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 1029 | options.message_loop_type = base::MessageLoop::TYPE_UI; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1030 | #endif // OS_WIN |
| 1031 | |
| 1032 | rendering_thread.StartWithOptions(options); |
| 1033 | scoped_ptr<RenderingHelper> rendering_helper(RenderingHelper::Create()); |
| 1034 | |
| 1035 | base::WaitableEvent done(false, false); |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1036 | RenderingHelperParams helper_params; |
| 1037 | helper_params.num_windows = num_concurrent_decoders; |
| 1038 | helper_params.render_as_thumbnails = render_as_thumbnails; |
| 1039 | if (render_as_thumbnails) { |
| 1040 | // Only one decoder is supported with thumbnail rendering |
| 1041 | CHECK_EQ(num_concurrent_decoders, 1U); |
| 1042 | gfx::Size frame_size(test_video_files[0]->width, |
| 1043 | test_video_files[0]->height); |
| 1044 | helper_params.frame_dimensions.push_back(frame_size); |
| 1045 | helper_params.window_dimensions.push_back(kThumbnailsDisplaySize); |
| 1046 | helper_params.thumbnails_page_size = kThumbnailsPageSize; |
| 1047 | helper_params.thumbnail_size = kThumbnailSize; |
| 1048 | } else { |
| 1049 | for (size_t index = 0; index < test_video_files.size(); ++index) { |
| 1050 | gfx::Size frame_size(test_video_files[index]->width, |
| 1051 | test_video_files[index]->height); |
| 1052 | helper_params.frame_dimensions.push_back(frame_size); |
| 1053 | helper_params.window_dimensions.push_back(frame_size); |
| 1054 | } |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1055 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1056 | rendering_thread.message_loop()->PostTask( |
| 1057 | FROM_HERE, |
| 1058 | base::Bind(&RenderingHelper::Initialize, |
| 1059 | base::Unretained(rendering_helper.get()), |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1060 | helper_params, |
| 1061 | &done)); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1062 | done.Wait(); |
| 1063 | |
| 1064 | // First kick off all the decoders. |
| 1065 | for (size_t index = 0; index < num_concurrent_decoders; ++index) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1066 | TestVideoFile* video_file = |
| 1067 | test_video_files[index % test_video_files.size()]; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1068 | ClientStateNotification* note = new ClientStateNotification(); |
| 1069 | notes[index] = note; |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 1070 | |
| 1071 | int delay_after_frame_num = std::numeric_limits<int>::max(); |
| 1072 | if (test_reuse_delay && |
| 1073 | kMaxFramesToDelayReuse * 2 < video_file->num_frames) { |
| 1074 | delay_after_frame_num = video_file->num_frames - kMaxFramesToDelayReuse; |
| 1075 | } |
| 1076 | |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1077 | GLRenderingVDAClient* client = |
| 1078 | new GLRenderingVDAClient(rendering_helper.get(), |
| 1079 | index, |
| 1080 | note, |
| 1081 | video_file->data_str, |
| 1082 | num_fragments_per_decode, |
| 1083 | num_in_flight_decodes, |
| 1084 | num_play_throughs, |
| 1085 | video_file->reset_after_frame_num, |
| 1086 | delete_decoder_state, |
| 1087 | video_file->width, |
| 1088 | video_file->height, |
| 1089 | video_file->profile, |
| 1090 | g_rendering_fps, |
| 1091 | suppress_rendering, |
| 1092 | delay_after_frame_num); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1093 | clients[index] = client; |
| 1094 | |
| 1095 | rendering_thread.message_loop()->PostTask( |
| 1096 | FROM_HERE, |
| 1097 | base::Bind(&GLRenderingVDAClient::CreateDecoder, |
| 1098 | base::Unretained(client))); |
| 1099 | |
| 1100 | ASSERT_EQ(note->Wait(), CS_DECODER_SET); |
| 1101 | } |
| 1102 | // Then wait for all the decodes to finish. |
| 1103 | // Only check performance & correctness later if we play through only once. |
| 1104 | bool skip_performance_and_correctness_checks = num_play_throughs > 1; |
| 1105 | for (size_t i = 0; i < num_concurrent_decoders; ++i) { |
| 1106 | ClientStateNotification* note = notes[i]; |
| 1107 | ClientState state = note->Wait(); |
| 1108 | if (state != CS_INITIALIZED) { |
| 1109 | skip_performance_and_correctness_checks = true; |
| 1110 | // We expect initialization to fail only when more than the supported |
| 1111 | // number of decoders is instantiated. Assert here that something else |
| 1112 | // didn't trigger failure. |
| 1113 | ASSERT_GT(num_concurrent_decoders, |
| 1114 | static_cast<size_t>(kMinSupportedNumConcurrentDecoders)); |
| 1115 | continue; |
| 1116 | } |
| 1117 | ASSERT_EQ(state, CS_INITIALIZED); |
| 1118 | for (int n = 0; n < num_play_throughs; ++n) { |
| 1119 | // For play-throughs other than the first, we expect initialization to |
| 1120 | // succeed unconditionally. |
| 1121 | if (n > 0) { |
| 1122 | ASSERT_NO_FATAL_FAILURE( |
| 1123 | AssertWaitForStateOrDeleted(note, clients[i], CS_INITIALIZED)); |
| 1124 | } |
| 1125 | // InitializeDone kicks off decoding inside the client, so we just need to |
| 1126 | // wait for Flush. |
| 1127 | ASSERT_NO_FATAL_FAILURE( |
| 1128 | AssertWaitForStateOrDeleted(note, clients[i], CS_FLUSHING)); |
| 1129 | ASSERT_NO_FATAL_FAILURE( |
| 1130 | AssertWaitForStateOrDeleted(note, clients[i], CS_FLUSHED)); |
| 1131 | // FlushDone requests Reset(). |
| 1132 | ASSERT_NO_FATAL_FAILURE( |
| 1133 | AssertWaitForStateOrDeleted(note, clients[i], CS_RESETTING)); |
| 1134 | } |
| 1135 | ASSERT_NO_FATAL_FAILURE( |
| 1136 | AssertWaitForStateOrDeleted(note, clients[i], CS_RESET)); |
| 1137 | // ResetDone requests Destroy(). |
| 1138 | ASSERT_NO_FATAL_FAILURE( |
| 1139 | AssertWaitForStateOrDeleted(note, clients[i], CS_DESTROYED)); |
| 1140 | } |
| 1141 | // Finally assert that decoding went as expected. |
| 1142 | for (size_t i = 0; i < num_concurrent_decoders && |
| 1143 | !skip_performance_and_correctness_checks; ++i) { |
| 1144 | // We can only make performance/correctness assertions if the decoder was |
| 1145 | // allowed to finish. |
| 1146 | if (delete_decoder_state < CS_FLUSHED) |
| 1147 | continue; |
| 1148 | GLRenderingVDAClient* client = clients[i]; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1149 | TestVideoFile* video_file = test_video_files[i % test_video_files.size()]; |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 1150 | if (video_file->num_frames > 0) { |
| 1151 | // Expect the decoded frames may be more than the video frames as frames |
| 1152 | // could still be returned until resetting done. |
| 1153 | if (video_file->reset_after_frame_num > 0) |
| 1154 | EXPECT_GE(client->num_decoded_frames(), video_file->num_frames); |
| 1155 | else |
| 1156 | EXPECT_EQ(client->num_decoded_frames(), video_file->num_frames); |
| 1157 | } |
| 1158 | if (reset_point == END_OF_STREAM_RESET) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1159 | EXPECT_EQ(video_file->num_fragments, client->num_skipped_fragments() + |
| 1160 | client->num_queued_fragments()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1161 | EXPECT_EQ(client->num_done_bitstream_buffers(), |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1162 | ceil(static_cast<double>(client->num_queued_fragments()) / |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1163 | num_fragments_per_decode)); |
| 1164 | } |
| 1165 | LOG(INFO) << "Decoder " << i << " fps: " << client->frames_per_second(); |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1166 | if (!render_as_thumbnails) { |
| 1167 | int min_fps = suppress_rendering ? |
| 1168 | video_file->min_fps_no_render : video_file->min_fps_render; |
| 1169 | if (min_fps > 0 && !test_reuse_delay) |
| 1170 | EXPECT_GT(client->frames_per_second(), min_fps); |
| 1171 | } |
| 1172 | } |
| 1173 | |
| 1174 | if (render_as_thumbnails) { |
| 1175 | std::vector<unsigned char> rgb; |
| 1176 | bool alpha_solid; |
| 1177 | rendering_thread.message_loop()->PostTask( |
| 1178 | FROM_HERE, |
| 1179 | base::Bind(&RenderingHelper::GetThumbnailsAsRGB, |
| 1180 | base::Unretained(rendering_helper.get()), |
| 1181 | &rgb, &alpha_solid, &done)); |
| 1182 | done.Wait(); |
| 1183 | |
| 1184 | std::vector<std::string> golden_md5s; |
| 1185 | std::string md5_string = base::MD5String( |
| 1186 | base::StringPiece(reinterpret_cast<char*>(&rgb[0]), rgb.size())); |
| 1187 | ReadGoldenThumbnailMD5s(test_video_files[0], &golden_md5s); |
| 1188 | std::vector<std::string>::iterator match = |
| 1189 | find(golden_md5s.begin(), golden_md5s.end(), md5_string); |
| 1190 | if (match == golden_md5s.end()) { |
| 1191 | // Convert raw RGB into PNG for export. |
| 1192 | std::vector<unsigned char> png; |
| 1193 | gfx::PNGCodec::Encode(&rgb[0], |
| 1194 | gfx::PNGCodec::FORMAT_RGB, |
| 1195 | kThumbnailsPageSize, |
| 1196 | kThumbnailsPageSize.width() * 3, |
| 1197 | true, |
| 1198 | std::vector<gfx::PNGCodec::Comment>(), |
| 1199 | &png); |
| 1200 | |
| 1201 | LOG(ERROR) << "Unknown thumbnails MD5: " << md5_string; |
| 1202 | |
| 1203 | base::FilePath filepath(test_video_files[0]->file_name); |
| 1204 | filepath = filepath.AddExtension(FILE_PATH_LITERAL(".bad_thumbnails")); |
| 1205 | filepath = filepath.AddExtension(FILE_PATH_LITERAL(".png")); |
| 1206 | int num_bytes = file_util::WriteFile(filepath, |
| 1207 | reinterpret_cast<char*>(&png[0]), |
| 1208 | png.size()); |
| 1209 | ASSERT_EQ(num_bytes, static_cast<int>(png.size())); |
| 1210 | } |
| 1211 | ASSERT_NE(match, golden_md5s.end()); |
| 1212 | EXPECT_EQ(alpha_solid, true) << "RGBA frame had incorrect alpha"; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1213 | } |
| 1214 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 1215 | // Output the frame delivery time to file |
| 1216 | // We can only make performance/correctness assertions if the decoder was |
| 1217 | // allowed to finish. |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1218 | if (g_frame_delivery_log != NULL && delete_decoder_state >= CS_FLUSHED) { |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 1219 | base::PlatformFile output_file = base::CreatePlatformFile( |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1220 | base::FilePath(g_frame_delivery_log), |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 1221 | base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE, |
| 1222 | NULL, |
| 1223 | NULL); |
| 1224 | for (size_t i = 0; i < num_concurrent_decoders; ++i) { |
| 1225 | clients[i]->OutputFrameDeliveryTimes(output_file); |
| 1226 | } |
| 1227 | base::ClosePlatformFile(output_file); |
| 1228 | } |
| 1229 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1230 | rendering_thread.message_loop()->PostTask( |
| 1231 | FROM_HERE, |
| 1232 | base::Bind(&STLDeleteElements<std::vector<GLRenderingVDAClient*> >, |
| 1233 | &clients)); |
| 1234 | rendering_thread.message_loop()->PostTask( |
| 1235 | FROM_HERE, |
| 1236 | base::Bind(&STLDeleteElements<std::vector<ClientStateNotification*> >, |
| 1237 | ¬es)); |
| 1238 | rendering_thread.message_loop()->PostTask( |
| 1239 | FROM_HERE, |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1240 | base::Bind(&STLDeleteElements<std::vector<TestVideoFile*> >, |
| 1241 | &test_video_files)); |
| 1242 | rendering_thread.message_loop()->PostTask( |
| 1243 | FROM_HERE, |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1244 | base::Bind(&RenderingHelper::UnInitialize, |
| 1245 | base::Unretained(rendering_helper.get()), |
| 1246 | &done)); |
| 1247 | done.Wait(); |
| 1248 | rendering_thread.Stop(); |
| 1249 | }; |
| 1250 | |
| 1251 | // Test that replay after EOS works fine. |
| 1252 | INSTANTIATE_TEST_CASE_P( |
| 1253 | ReplayAfterEOS, VideoDecodeAcceleratorTest, |
| 1254 | ::testing::Values( |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1255 | MakeTuple(1, 1, 1, 4, END_OF_STREAM_RESET, CS_RESET, false, false))); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1256 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1257 | // This hangs on Exynos, preventing further testing and wasting test machine |
| 1258 | // time. |
| 1259 | // TODO(ihf): Enable again once http://crbug.com/269754 is fixed. |
| 1260 | #if defined(ARCH_CPU_X86_FAMILY) |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1261 | // Test that Reset() before the first Decode() works fine. |
| 1262 | INSTANTIATE_TEST_CASE_P( |
| 1263 | ResetBeforeDecode, VideoDecodeAcceleratorTest, |
| 1264 | ::testing::Values( |
| 1265 | MakeTuple(1, 1, 1, 1, START_OF_STREAM_RESET, CS_RESET, false, false))); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1266 | #endif // ARCH_CPU_X86_FAMILY |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1267 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1268 | // Test that Reset() mid-stream works fine and doesn't affect decoding even when |
| 1269 | // Decode() calls are made during the reset. |
| 1270 | INSTANTIATE_TEST_CASE_P( |
| 1271 | MidStreamReset, VideoDecodeAcceleratorTest, |
| 1272 | ::testing::Values( |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1273 | MakeTuple(1, 1, 1, 1, MID_STREAM_RESET, CS_RESET, false, false))); |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 1274 | |
| 1275 | INSTANTIATE_TEST_CASE_P( |
| 1276 | SlowRendering, VideoDecodeAcceleratorTest, |
| 1277 | ::testing::Values( |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1278 | MakeTuple(1, 1, 1, 1, END_OF_STREAM_RESET, CS_RESET, true, false))); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1279 | |
| 1280 | // Test that Destroy() mid-stream works fine (primarily this is testing that no |
| 1281 | // crashes occur). |
| 1282 | INSTANTIATE_TEST_CASE_P( |
| 1283 | TearDownTiming, VideoDecodeAcceleratorTest, |
| 1284 | ::testing::Values( |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1285 | MakeTuple(1, 1, 1, 1, END_OF_STREAM_RESET, CS_DECODER_SET, false, |
| 1286 | false), |
| 1287 | MakeTuple(1, 1, 1, 1, END_OF_STREAM_RESET, CS_INITIALIZED, false, |
| 1288 | false), |
| 1289 | MakeTuple(1, 1, 1, 1, END_OF_STREAM_RESET, CS_FLUSHING, false, false), |
| 1290 | MakeTuple(1, 1, 1, 1, END_OF_STREAM_RESET, CS_FLUSHED, false, false), |
| 1291 | MakeTuple(1, 1, 1, 1, END_OF_STREAM_RESET, CS_RESETTING, false, false), |
| 1292 | MakeTuple(1, 1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, false), |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1293 | MakeTuple(1, 1, 1, 1, END_OF_STREAM_RESET, |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1294 | static_cast<ClientState>(-1), false, false), |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1295 | MakeTuple(1, 1, 1, 1, END_OF_STREAM_RESET, |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1296 | static_cast<ClientState>(-10), false, false), |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1297 | MakeTuple(1, 1, 1, 1, END_OF_STREAM_RESET, |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1298 | static_cast<ClientState>(-100), false, false))); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1299 | |
Torne (Richard Coles) | b2df76e | 2013-05-13 16:52:09 +0100 | [diff] [blame] | 1300 | // Test that decoding various variation works: multiple fragments per Decode() |
| 1301 | // call and multiple in-flight decodes. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1302 | INSTANTIATE_TEST_CASE_P( |
| 1303 | DecodeVariations, VideoDecodeAcceleratorTest, |
| 1304 | ::testing::Values( |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1305 | MakeTuple(1, 1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, false), |
| 1306 | MakeTuple(1, 1, 10, 1, END_OF_STREAM_RESET, CS_RESET, false, false), |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1307 | // Tests queuing. |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1308 | MakeTuple(1, 1, 15, 1, END_OF_STREAM_RESET, CS_RESET, false, false), |
| 1309 | MakeTuple(2, 1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, false), |
| 1310 | MakeTuple(3, 1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, false), |
| 1311 | MakeTuple(5, 1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, false), |
| 1312 | MakeTuple(8, 1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, false), |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1313 | // TODO(fischman): decoding more than 15 NALUs at once breaks decode - |
| 1314 | // visual artifacts are introduced as well as spurious frames are |
| 1315 | // delivered (more pictures are returned than NALUs are fed to the |
| 1316 | // decoder). Increase the "15" below when |
| 1317 | // http://code.google.com/p/chrome-os-partner/issues/detail?id=4378 is |
| 1318 | // fixed. |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1319 | MakeTuple(15, 1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, false))); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1320 | |
| 1321 | // Find out how many concurrent decoders can go before we exhaust system |
| 1322 | // resources. |
| 1323 | INSTANTIATE_TEST_CASE_P( |
| 1324 | ResourceExhaustion, VideoDecodeAcceleratorTest, |
| 1325 | ::testing::Values( |
| 1326 | // +0 hack below to promote enum to int. |
| 1327 | MakeTuple(1, kMinSupportedNumConcurrentDecoders + 0, 1, 1, |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1328 | END_OF_STREAM_RESET, CS_RESET, false, false), |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1329 | MakeTuple(1, kMinSupportedNumConcurrentDecoders + 1, 1, 1, |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1330 | END_OF_STREAM_RESET, CS_RESET, false, false))); |
| 1331 | |
| 1332 | // Thumbnailing test |
| 1333 | INSTANTIATE_TEST_CASE_P( |
| 1334 | Thumbnail, VideoDecodeAcceleratorTest, |
| 1335 | ::testing::Values( |
| 1336 | MakeTuple(1, 1, 1, 1, END_OF_STREAM_RESET, CS_RESET, false, true))); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1337 | |
| 1338 | // TODO(fischman, vrk): add more tests! In particular: |
| 1339 | // - Test life-cycle: Seek/Stop/Pause/Play for a single decoder. |
| 1340 | // - Test alternate configurations |
| 1341 | // - Test failure conditions. |
| 1342 | // - Test frame size changes mid-stream |
| 1343 | |
| 1344 | } // namespace |
| 1345 | } // namespace content |
| 1346 | |
| 1347 | int main(int argc, char **argv) { |
| 1348 | testing::InitGoogleTest(&argc, argv); // Removes gtest-specific args. |
| 1349 | CommandLine::Init(argc, argv); |
| 1350 | |
| 1351 | // Needed to enable DVLOG through --vmodule. |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 1352 | logging::LoggingSettings settings; |
| 1353 | settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; |
| 1354 | settings.dcheck_state = |
| 1355 | logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS; |
| 1356 | CHECK(logging::InitLogging(settings)); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1357 | |
| 1358 | CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
| 1359 | DCHECK(cmd_line); |
| 1360 | |
| 1361 | CommandLine::SwitchMap switches = cmd_line->GetSwitches(); |
| 1362 | for (CommandLine::SwitchMap::const_iterator it = switches.begin(); |
| 1363 | it != switches.end(); ++it) { |
| 1364 | if (it->first == "test_video_data") { |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1365 | content::g_test_video_data = it->second.c_str(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1366 | continue; |
| 1367 | } |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 1368 | if (it->first == "frame_delivery_log") { |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1369 | content::g_frame_delivery_log = it->second.c_str(); |
| 1370 | continue; |
| 1371 | } |
| 1372 | if (it->first == "rendering_fps") { |
| 1373 | // On Windows, CommandLine::StringType is wstring. We need to convert |
| 1374 | // it to std::string first |
| 1375 | std::string input(it->second.begin(), it->second.end()); |
| 1376 | CHECK(base::StringToDouble(input, &content::g_rendering_fps)); |
| 1377 | continue; |
| 1378 | } |
| 1379 | if (it->first == "disable_rendering") { |
| 1380 | content::g_disable_rendering = true; |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 1381 | continue; |
| 1382 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1383 | if (it->first == "v" || it->first == "vmodule") |
| 1384 | continue; |
| 1385 | LOG(FATAL) << "Unexpected switch: " << it->first << ":" << it->second; |
| 1386 | } |
| 1387 | |
| 1388 | base::ShadowingAtExitManager at_exit_manager; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1389 | |
| 1390 | #if defined(OS_WIN) |
| 1391 | content::DXVAVideoDecodeAccelerator::PreSandboxInitialization(); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1392 | #elif defined(OS_CHROMEOS) |
| 1393 | #if defined(ARCH_CPU_ARMEL) |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 1394 | content::ExynosVideoDecodeAccelerator::PreSandboxInitialization(); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1395 | #elif defined(ARCH_CPU_X86_FAMILY) |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 1396 | content::VaapiWrapper::PreSandboxInitialization(); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1397 | #endif // ARCH_CPU_ARMEL |
| 1398 | #endif // OS_CHROMEOS |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1399 | |
| 1400 | return RUN_ALL_TESTS(); |
| 1401 | } |