Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 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, and GLES2. | |
| 8 // - ClientState is an enum for the state of the decode client used by the test. | |
| 9 // - ClientStateNotification is a barrier abstraction that allows the test code | |
| 10 // to be written sequentially and wait for the decode client to see certain | |
| 11 // state transitions. | |
| 12 // - EglRenderingVDAClient is a VideoDecodeAccelerator::Client implementation | |
| 13 // - Finally actual TEST cases are at the bottom of this file, using the above | |
| 14 // infrastructure. | |
| 15 | |
| 16 #include <sys/types.h> | |
| 17 #include <sys/stat.h> | |
| 18 #include <fcntl.h> | |
| 19 | |
| 20 // Include gtest.h out of order because <X11/X.h> #define's Bool & None, which | |
| 21 // gtest uses as struct names (inside a namespace). This means that | |
| 22 // #include'ing gtest after anything that pulls in X.h fails to compile. | |
| 23 #include "testing/gtest/include/gtest/gtest.h" | |
| 24 | |
| 25 #include "base/at_exit.h" | |
| 26 #include "base/file_util.h" | |
| 27 #include "base/stl_util-inl.h" | |
| 28 #include "base/synchronization/condition_variable.h" | |
| 29 #include "base/synchronization/lock.h" | |
| 30 #include "base/synchronization/waitable_event.h" | |
| 31 #include "base/threading/thread.h" | |
| 32 #include "content/common/gpu/omx_video_decode_accelerator.h" | |
| 33 #include "third_party/angle/include/EGL/egl.h" | |
| 34 #include "third_party/angle/include/GLES2/gl2.h" | |
| 35 | |
| 36 #if !defined(OS_CHROMEOS) || !defined(ARCH_CPU_ARMEL) | |
| 37 #error This test (and OmxVideoDecodeAccelerator) are only supported on cros/ARM! | |
| 38 #endif | |
| 39 | |
| 40 using media::VideoDecodeAccelerator; | |
| 41 | |
| 42 namespace { | |
| 43 | |
| 44 // General-purpose constants for this test. | |
| 45 enum { | |
| 46 kFrameWidth = 320, | |
| 47 kFrameHeight = 240, | |
| 48 }; | |
| 49 | |
| 50 // Helper for managing X11, EGL, and GLES2 resources. Because GL state is | |
| 51 // thread-specific, all the methods of this class (except for ctor/dtor) CHECK | |
| 52 // for being run on a single thread. | |
| 53 // | |
| 54 // TODO(fischman): consider moving this into media/ if we can de-dup some of the | |
| 55 // code that ends up getting copy/pasted all over the place (esp. the GL setup | |
| 56 // code). | |
| 57 class RenderingHelper { | |
| 58 public: | |
| 59 explicit RenderingHelper(); | |
| 60 ~RenderingHelper(); | |
| 61 | |
| 62 // Initialize all structures to prepare to render to a window of the specified | |
| 63 // dimensions. CHECK-fails if any initialization step fails. After this | |
| 64 // returns, texture creation and rendering (swaps) can be requested. | |
| 65 // This method can be called multiple times, in which case all | |
| 66 // previously-acquired resources and initializations are discarded. | |
| 67 void Initialize(int width, int height, base::WaitableEvent* done); | |
| 68 | |
| 69 // Undo the effects of Initialize() and signal |*done|. | |
| 70 void UnInitialize(base::WaitableEvent* done); | |
| 71 | |
| 72 // Return a newly-created GLES2 texture id. | |
| 73 GLuint CreateTexture(); | |
| 74 | |
| 75 // Render |texture_id| to the screen. | |
| 76 void RenderTexture(GLuint texture_id); | |
| 77 | |
| 78 EGLDisplay egl_display() { return egl_display_; } | |
| 79 EGLContext egl_context() { return egl_context_; } | |
| 80 | |
| 81 private: | |
| 82 int width_; | |
| 83 int height_; | |
| 84 Display* x_display_; | |
| 85 Window x_window_; | |
| 86 EGLDisplay egl_display_; | |
| 87 EGLContext egl_context_; | |
| 88 EGLSurface egl_surface_; | |
| 89 // Since GL carries per-thread state, we ensure all operations are carried out | |
| 90 // on the same thread by remembering where we were Initialized. | |
| 91 MessageLoop* message_loop_; | |
| 92 }; | |
| 93 | |
| 94 RenderingHelper::RenderingHelper() { | |
| 95 memset(this, 0, sizeof(this)); | |
| 96 } | |
| 97 | |
| 98 RenderingHelper::~RenderingHelper() { | |
| 99 CHECK_EQ(width_, 0) << "Must call UnInitialize before dtor."; | |
| 100 } | |
| 101 | |
| 102 // Helper for Shader creation. | |
| 103 static void CreateShader( | |
| 104 GLuint program, GLenum type, const char* source, int size) { | |
| 105 GLuint shader = glCreateShader(type); | |
| 106 glShaderSource(shader, 1, &source, &size); | |
| 107 glCompileShader(shader); | |
| 108 int result = GL_FALSE; | |
| 109 glGetShaderiv(shader, GL_COMPILE_STATUS, &result); | |
| 110 CHECK(result); | |
| 111 glAttachShader(program, shader); | |
| 112 glDeleteShader(shader); | |
| 113 CHECK_EQ((int)glGetError(), GL_NO_ERROR); | |
| 114 } | |
| 115 | |
| 116 void RenderingHelper::Initialize( | |
| 117 int width, int height, base::WaitableEvent* done) { | |
| 118 if (width_) { | |
|
vrk (LEFT CHROMIUM)
2011/06/03 07:55:24
I get what you're doing, but at first glance looks
Ami GONE FROM CHROMIUM
2011/06/03 17:43:59
Done.
| |
| 119 base::WaitableEvent done(false, false); | |
| 120 UnInitialize(&done); | |
| 121 done.Wait(); | |
| 122 } | |
| 123 | |
| 124 CHECK_GT(width, 0); | |
| 125 CHECK_GT(height, 0); | |
| 126 width_ = width; | |
| 127 height_ = height; | |
| 128 message_loop_ = MessageLoop::current(); | |
| 129 | |
| 130 // X11 initialization. | |
| 131 CHECK(x_display_ = XOpenDisplay(NULL)); | |
| 132 int depth = DefaultDepth(x_display_, DefaultScreen(x_display_)); | |
| 133 XSetWindowAttributes window_attributes; | |
| 134 window_attributes.background_pixel = | |
| 135 BlackPixel(x_display_, DefaultScreen(x_display_)); | |
| 136 window_attributes.override_redirect = true; | |
| 137 x_window_ = XCreateWindow( | |
| 138 x_display_, DefaultRootWindow(x_display_), | |
| 139 0, 0, /* x/y of top-left corner */ | |
| 140 width_, height_, | |
| 141 0 /* border width */, | |
| 142 depth, CopyFromParent /* class */, CopyFromParent /* visual */, | |
| 143 (CWBackPixel | CWOverrideRedirect), &window_attributes); | |
| 144 XStoreName(x_display_, x_window_, "OmxVideoDecodeAcceleratorTest"); | |
| 145 XSelectInput(x_display_, x_window_, ExposureMask); | |
| 146 XMapWindow(x_display_, x_window_); | |
| 147 | |
| 148 // EGL initialization. | |
| 149 egl_display_ = eglGetDisplay(x_display_); | |
| 150 EGLint major; | |
| 151 EGLint minor; | |
| 152 CHECK(eglInitialize(egl_display_, &major, &minor)) << eglGetError(); | |
| 153 EGLint rgba8888[] = { | |
| 154 EGL_RED_SIZE, 8, | |
| 155 EGL_GREEN_SIZE, 8, | |
| 156 EGL_BLUE_SIZE, 8, | |
| 157 EGL_ALPHA_SIZE, 8, | |
| 158 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, | |
| 159 EGL_NONE, | |
| 160 }; | |
| 161 | |
| 162 EGLConfig egl_config; | |
| 163 int num_configs; | |
| 164 CHECK(eglChooseConfig(egl_display_, rgba8888, &egl_config, 1, &num_configs)) | |
| 165 << eglGetError(); | |
| 166 CHECK_GE(num_configs, 1); | |
| 167 egl_surface_ = | |
| 168 eglCreateWindowSurface(egl_display_, egl_config, x_window_, NULL); | |
| 169 CHECK_NE(egl_surface_, EGL_NO_SURFACE); | |
| 170 EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; | |
| 171 egl_context_ = eglCreateContext( | |
| 172 egl_display_, egl_config, EGL_NO_CONTEXT, context_attribs); | |
| 173 CHECK(eglMakeCurrent(egl_display_, egl_surface_, egl_surface_, egl_context_)) | |
| 174 << eglGetError(); | |
| 175 | |
| 176 // GLES2 initialization. Note: This is pretty much copy/pasted from | |
| 177 // media/tools/player_x11/gles_video_renderer.cc, with some simplification | |
| 178 // applied. | |
| 179 static const float kVertices[] = | |
| 180 { -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f, -1.f, }; | |
| 181 static const float kTextureCoordsEgl[] = { 0, 1, 0, 0, 1, 1, 1, 0, }; | |
| 182 static const char kVertexShader[] = | |
| 183 "precision highp float; precision highp int;\n" | |
|
vrk (LEFT CHROMIUM)
2011/06/03 07:55:24
These shouldn't be necessary. (no ints/floats used
Ami GONE FROM CHROMIUM
2011/06/03 17:43:59
Whoah! Do you actually know GL? I don't! Yay!
| |
| 184 "varying vec2 interp_tc;\n" | |
| 185 "\n" | |
| 186 "attribute vec4 in_pos;\n" | |
| 187 "attribute vec2 in_tc;\n" | |
| 188 "\n" | |
| 189 "void main() {\n" | |
| 190 " interp_tc = in_tc;\n" | |
| 191 " gl_Position = in_pos;\n" | |
| 192 "}\n"; | |
| 193 static const char kFragmentShaderEgl[] = | |
| 194 "precision mediump float;\n" | |
| 195 "precision mediump int;\n" | |
|
vrk (LEFT CHROMIUM)
2011/06/03 07:55:24
These shouldn't be necessary. (no ints/floats used
Ami GONE FROM CHROMIUM
2011/06/03 17:43:59
int line can be removed, but removing the float li
| |
| 196 "varying vec2 interp_tc;\n" | |
| 197 "\n" | |
| 198 "uniform sampler2D tex;\n" | |
| 199 "\n" | |
| 200 "void main() {\n" | |
| 201 " gl_FragColor = texture2D(tex, interp_tc);\n" | |
| 202 "}\n"; | |
| 203 | |
| 204 GLuint program = glCreateProgram(); | |
| 205 CreateShader(program, GL_VERTEX_SHADER, kVertexShader, sizeof(kVertexShader)); | |
| 206 CreateShader(program, GL_FRAGMENT_SHADER, | |
| 207 kFragmentShaderEgl, sizeof(kFragmentShaderEgl)); | |
| 208 glLinkProgram(program); | |
| 209 int result = GL_FALSE; | |
| 210 glGetProgramiv(program, GL_LINK_STATUS, &result); | |
| 211 CHECK(result); | |
| 212 glUseProgram(program); | |
| 213 glDeleteProgram(program); | |
| 214 | |
| 215 glUniform1i(glGetUniformLocation(program, "tex"), 0); | |
| 216 int pos_location = glGetAttribLocation(program, "in_pos"); | |
| 217 glEnableVertexAttribArray(pos_location); | |
| 218 glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, 0, kVertices); | |
| 219 int tc_location = glGetAttribLocation(program, "in_tc"); | |
| 220 glEnableVertexAttribArray(tc_location); | |
| 221 glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0, | |
| 222 kTextureCoordsEgl); | |
| 223 done->Signal(); | |
| 224 } | |
| 225 | |
| 226 void RenderingHelper::UnInitialize(base::WaitableEvent* done) { | |
| 227 CHECK_EQ(MessageLoop::current(), message_loop_); | |
| 228 // Destroy resources acquired in Initialize, in reverse-acquisition order. | |
| 229 CHECK(eglMakeCurrent( | |
| 230 egl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); | |
| 231 CHECK(eglDestroyContext(egl_display_, egl_context_)); | |
| 232 CHECK(eglDestroySurface(egl_display_, egl_surface_)); | |
| 233 CHECK(eglTerminate(egl_display_)); | |
| 234 CHECK(XUnmapWindow(x_display_, x_window_)); | |
| 235 CHECK(XDestroyWindow(x_display_, x_window_)); | |
| 236 // Mimic newly-created object. | |
| 237 memset(this, 0, sizeof(this)); | |
| 238 done->Signal(); | |
| 239 } | |
| 240 | |
| 241 GLuint RenderingHelper::CreateTexture() { | |
| 242 CHECK_EQ(MessageLoop::current(), message_loop_); | |
| 243 GLuint texture_id; | |
| 244 glGenTextures(1, &texture_id); | |
| 245 glBindTexture(GL_TEXTURE_2D, texture_id); | |
| 246 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_, 0, GL_RGBA, | |
| 247 GL_UNSIGNED_BYTE, NULL); | |
| 248 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
| 249 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
| 250 CHECK_EQ((int)glGetError(), GL_NO_ERROR); | |
| 251 return texture_id; | |
| 252 } | |
| 253 | |
| 254 void RenderingHelper::RenderTexture(GLuint texture_id) { | |
| 255 CHECK_EQ(MessageLoop::current(), message_loop_); | |
| 256 glActiveTexture(GL_TEXTURE0); | |
| 257 glBindTexture(GL_TEXTURE_2D, texture_id); | |
| 258 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | |
| 259 DCHECK_EQ((int)glGetError(), GL_NO_ERROR); | |
| 260 eglSwapBuffers(egl_display_, egl_surface_); | |
| 261 DCHECK_EQ((int)eglGetError(), EGL_SUCCESS); | |
| 262 } | |
| 263 | |
| 264 // State of the EglRenderingVDAClient below. | |
| 265 enum ClientState { | |
| 266 CS_CREATED, | |
| 267 CS_DECODER_SET, | |
| 268 CS_INITIALIZED, | |
| 269 CS_FLUSHED, | |
| 270 CS_DONE, | |
| 271 CS_ABORTED, | |
| 272 CS_ERROR, | |
| 273 CS_DESTROYED, | |
| 274 }; | |
| 275 | |
| 276 // Helper class allowing one thread to wait on a notification from another. | |
| 277 // If notifications come in faster than they are Wait()'d for, they are | |
| 278 // accumulated (so exactly as many Wait() calls will unblock as Notify() calls | |
| 279 // were made, regardless of order). | |
| 280 class ClientStateNotification { | |
|
vrk (LEFT CHROMIUM)
2011/06/03 07:55:24
SO ELEGANT
| |
| 281 public: | |
| 282 ClientStateNotification(); | |
| 283 ~ClientStateNotification(); | |
| 284 | |
| 285 // Used to notify a single waiter of a ClientState. | |
| 286 void Notify(ClientState state); | |
| 287 // Used by waiters to wait for the next ClientState Notification. | |
| 288 ClientState Wait(); | |
| 289 private: | |
| 290 base::Lock lock_; | |
| 291 base::ConditionVariable cv_; | |
| 292 std::queue<ClientState> pending_states_for_notification_; | |
| 293 }; | |
| 294 | |
| 295 ClientStateNotification::ClientStateNotification() : cv_(&lock_) {} | |
| 296 ClientStateNotification::~ClientStateNotification() {} | |
| 297 | |
| 298 void ClientStateNotification::Notify(ClientState state) { | |
| 299 base::AutoLock auto_lock(lock_); | |
| 300 pending_states_for_notification_.push(state); | |
| 301 cv_.Signal(); | |
| 302 } | |
| 303 | |
| 304 ClientState ClientStateNotification::Wait() { | |
| 305 base::AutoLock auto_lock(lock_); | |
| 306 while (pending_states_for_notification_.empty()) | |
| 307 cv_.Wait(); | |
| 308 ClientState ret = pending_states_for_notification_.front(); | |
| 309 pending_states_for_notification_.pop(); | |
| 310 return ret; | |
| 311 } | |
| 312 | |
| 313 // Client that can accept callbacks from a VideoDecodeAccelerator and is used by | |
| 314 // the TESTs below. | |
| 315 class EglRenderingVDAClient : public VideoDecodeAccelerator::Client { | |
| 316 public: | |
| 317 // Doesn't take ownership of |note|, which must outlive |*this|. | |
| 318 EglRenderingVDAClient(ClientStateNotification* note); | |
| 319 virtual ~EglRenderingVDAClient(); | |
| 320 | |
| 321 // VideoDecodeAccelerator::Client implementation. | |
| 322 // The heart of the Client. | |
| 323 virtual void ProvidePictureBuffers( | |
| 324 uint32 requested_num_of_buffers, | |
| 325 const gfx::Size& dimensions, | |
| 326 VideoDecodeAccelerator::MemoryType type); | |
| 327 virtual void DismissPictureBuffer(int32 picture_buffer_id); | |
| 328 virtual void PictureReady(const media::Picture& picture); | |
| 329 // Simple state changes. | |
| 330 virtual void NotifyInitializeDone(); | |
| 331 virtual void NotifyEndOfStream(); | |
| 332 virtual void NotifyEndOfBitstreamBuffer(int32 bitstream_buffer_id); | |
| 333 virtual void NotifyFlushDone(); | |
| 334 virtual void NotifyAbortDone(); | |
| 335 virtual void NotifyError(VideoDecodeAccelerator::Error error); | |
| 336 | |
| 337 // Doesn't take ownership of |decoder|, which must outlive |*this|. | |
| 338 void SetDecoder(VideoDecodeAccelerator* decoder); | |
| 339 | |
| 340 // Simple getters for inspecting the state of the Client. | |
| 341 ClientState state() { return state_; } | |
| 342 VideoDecodeAccelerator::Error error() { return error_; } | |
| 343 int num_done_bitstream_buffers() { return num_done_bitstream_buffers_; } | |
| 344 int num_decoded_frames() { return num_decoded_frames_; } | |
| 345 MessageLoop* message_loop() { return thread_.message_loop(); } | |
| 346 EGLDisplay egl_display() { return rendering_helper_.egl_display(); } | |
| 347 EGLContext egl_context() { return rendering_helper_.egl_context(); } | |
| 348 | |
| 349 private: | |
| 350 void SetState(ClientState new_state) { | |
| 351 note_->Notify(new_state); | |
| 352 state_ = new_state; | |
| 353 } | |
| 354 | |
| 355 VideoDecodeAccelerator* decoder_; | |
| 356 ClientStateNotification* note_; | |
| 357 ClientState state_; | |
| 358 VideoDecodeAccelerator::Error error_; | |
| 359 int num_decoded_frames_; | |
| 360 int num_done_bitstream_buffers_; | |
| 361 std::map<int, media::GLESBuffer*> picture_buffers_by_id_; | |
| 362 // Required for Thread to work. Not used otherwise. | |
| 363 base::ShadowingAtExitManager at_exit_manager_; | |
| 364 base::Thread thread_; | |
| 365 RenderingHelper rendering_helper_; | |
| 366 }; | |
| 367 | |
| 368 EglRenderingVDAClient::EglRenderingVDAClient(ClientStateNotification* note) | |
| 369 : decoder_(NULL), note_(note), state_(CS_CREATED), | |
| 370 error_(VideoDecodeAccelerator::VIDEODECODERERROR_NONE), | |
| 371 num_decoded_frames_(0), num_done_bitstream_buffers_(0), | |
| 372 thread_("EglRenderingVDAClientThread") { | |
| 373 CHECK(thread_.Start()); | |
| 374 base::WaitableEvent done(false, false); | |
| 375 message_loop()->PostTask( | |
| 376 FROM_HERE, | |
| 377 base::Bind(&RenderingHelper::Initialize, | |
| 378 base::Unretained(&rendering_helper_), | |
| 379 static_cast<int>(kFrameWidth), static_cast<int>(kFrameHeight), | |
| 380 &done)); | |
| 381 done.Wait(); | |
| 382 } | |
| 383 | |
| 384 EglRenderingVDAClient::~EglRenderingVDAClient() { | |
| 385 base::WaitableEvent done(false, false); | |
| 386 message_loop()->PostTask( | |
| 387 FROM_HERE, | |
| 388 base::Bind(&RenderingHelper::UnInitialize, | |
| 389 base::Unretained(&rendering_helper_), | |
| 390 &done)); | |
| 391 done.Wait(); | |
| 392 thread_.Stop(); | |
| 393 STLDeleteValues(&picture_buffers_by_id_); | |
| 394 SetState(CS_DESTROYED); | |
| 395 } | |
| 396 | |
| 397 void EglRenderingVDAClient::ProvidePictureBuffers( | |
| 398 uint32 requested_num_of_buffers, | |
| 399 const gfx::Size& dimensions, | |
| 400 VideoDecodeAccelerator::MemoryType type) { | |
| 401 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 402 CHECK_EQ(type, VideoDecodeAccelerator::PICTUREBUFFER_MEMORYTYPE_GL_TEXTURE); | |
| 403 std::vector<media::GLESBuffer> buffers; | |
| 404 CHECK_EQ(dimensions.width(), kFrameWidth); | |
| 405 CHECK_EQ(dimensions.height(), kFrameHeight); | |
| 406 | |
| 407 for (uint32 i = 0; i < requested_num_of_buffers; ++i) { | |
| 408 uint32 id = picture_buffers_by_id_.size(); | |
| 409 GLuint texture_id = rendering_helper_.CreateTexture(); | |
| 410 // TODO(fischman): context_id is always 0. Can it be removed from the API? | |
| 411 // (since it's always inferrable from context). | |
|
vrk (LEFT CHROMIUM)
2011/06/03 07:55:24
Yeah I think for iteration 1 it'd be good to assum
Ami GONE FROM CHROMIUM
2011/06/03 17:43:59
Follow-up CL.
| |
| 412 media::GLESBuffer* buffer = | |
| 413 new media::GLESBuffer(id, dimensions, texture_id, 0 /* context_id */); | |
| 414 CHECK(picture_buffers_by_id_.insert(std::make_pair(id, buffer)).second); | |
| 415 buffers.push_back(*buffer); | |
| 416 } | |
| 417 decoder_->AssignGLESBuffers(buffers); | |
| 418 CHECK_EQ((int)glGetError(), GL_NO_ERROR); | |
| 419 CHECK_EQ((int)eglGetError(), EGL_SUCCESS); | |
| 420 } | |
| 421 | |
| 422 void EglRenderingVDAClient::DismissPictureBuffer(int32 picture_buffer_id) { | |
| 423 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 424 delete picture_buffers_by_id_[picture_buffer_id]; | |
| 425 CHECK_EQ(1U, picture_buffers_by_id_.erase(picture_buffer_id)); | |
| 426 } | |
| 427 | |
| 428 void EglRenderingVDAClient::PictureReady(const media::Picture& picture) { | |
| 429 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 430 | |
| 431 ++num_decoded_frames_; | |
| 432 | |
| 433 media::GLESBuffer* gles_buffer = | |
| 434 picture_buffers_by_id_[picture.picture_buffer_id()]; | |
| 435 CHECK(gles_buffer); | |
| 436 rendering_helper_.RenderTexture(gles_buffer->texture_id()); | |
| 437 | |
| 438 decoder_->ReusePictureBuffer(picture.picture_buffer_id()); | |
| 439 } | |
| 440 | |
| 441 void EglRenderingVDAClient::NotifyInitializeDone() { | |
| 442 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 443 SetState(CS_INITIALIZED); | |
| 444 } | |
| 445 | |
| 446 void EglRenderingVDAClient::NotifyEndOfStream() { | |
| 447 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 448 SetState(CS_DONE); | |
| 449 } | |
| 450 | |
| 451 void EglRenderingVDAClient::NotifyEndOfBitstreamBuffer( | |
| 452 int32 bitstream_buffer_id) { | |
| 453 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 454 ++num_done_bitstream_buffers_; | |
| 455 | |
| 456 // TODO(fischman): this is hokey! It should be possible to call Flush() from | |
| 457 // outside a callback. | |
| 458 if (num_done_bitstream_buffers_ == 1) | |
| 459 decoder_->Flush(); | |
| 460 } | |
| 461 | |
| 462 void EglRenderingVDAClient::NotifyFlushDone() { | |
| 463 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 464 SetState(CS_FLUSHED); | |
| 465 } | |
| 466 | |
| 467 void EglRenderingVDAClient::NotifyAbortDone() { | |
| 468 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 469 SetState(CS_ABORTED); | |
| 470 } | |
| 471 | |
| 472 void EglRenderingVDAClient::NotifyError(VideoDecodeAccelerator::Error error) { | |
| 473 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 474 SetState(CS_ERROR); | |
| 475 error_ = error; | |
| 476 } | |
| 477 | |
| 478 void EglRenderingVDAClient::SetDecoder(VideoDecodeAccelerator* decoder) { | |
| 479 decoder_ = decoder; | |
| 480 SetState(CS_DECODER_SET); | |
| 481 } | |
| 482 | |
| 483 // Test the most straightforward case possible: data is decoded from a single | |
| 484 // chunk and rendered to the screen. | |
| 485 TEST(OmxVideoDecodeAcceleratorTest, TestSimpleDecode) { | |
| 486 logging::SetMinLogLevel(-1); | |
| 487 ClientStateNotification note; | |
|
vrk (LEFT CHROMIUM)
2011/06/03 07:55:24
nit: indentation
Ami GONE FROM CHROMIUM
2011/06/03 17:43:59
Done.
| |
| 488 ClientState state; | |
| 489 EglRenderingVDAClient client(¬e); | |
| 490 OmxVideoDecodeAccelerator decoder(&client, client.message_loop()); | |
| 491 client.SetDecoder(&decoder); | |
| 492 decoder.SetEglState(client.egl_display(), client.egl_context()); | |
| 493 ASSERT_EQ((state = note.Wait()), CS_DECODER_SET); | |
| 494 | |
| 495 int32 config_array[] = { | |
| 496 media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_FOURCC, | |
| 497 media::VIDEOCODECFOURCC_H264, | |
| 498 media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_WIDTH, kFrameWidth, | |
| 499 media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_HEIGHT, kFrameHeight, | |
| 500 media::VIDEOATTRIBUTEKEY_COLORFORMAT_RGBA, | |
| 501 media::VIDEOATTRIBUTEKEY_TERMINATOR | |
| 502 }; | |
| 503 std::vector<uint32> config( | |
| 504 config_array, config_array + arraysize(config_array)); | |
| 505 CHECK(decoder.Initialize(config)); | |
| 506 ASSERT_EQ((state = note.Wait()), CS_INITIALIZED); | |
| 507 | |
| 508 // Set up buffers to pass to Decode. | |
| 509 char const* data_filename = "test-25fps.h264"; | |
| 510 int data_fd = open(data_filename, O_RDONLY); | |
| 511 CHECK_GE(data_fd, 0); | |
| 512 int64 file_size = -1; | |
| 513 CHECK(file_util::GetFileSize(FilePath(std::string(data_filename)), | |
| 514 &file_size)); | |
| 515 base::SharedMemory data_shm(base::FileDescriptor(data_fd, false), true); | |
| 516 CHECK(data_shm.Map(file_size)); | |
| 517 media::BitstreamBuffer bitstream_buffer(0, data_shm.handle(), file_size); | |
| 518 | |
| 519 decoder.Decode(bitstream_buffer); | |
| 520 ASSERT_EQ((state = note.Wait()), CS_FLUSHED); | |
| 521 | |
| 522 EXPECT_EQ(client.num_decoded_frames(), 25 /* fps */ * 10 /* seconds */); | |
| 523 EXPECT_EQ(client.num_done_bitstream_buffers(), 1); | |
| 524 } | |
| 525 | |
| 526 // TODO(fischman, vrk): add more tests! In particular: | |
| 527 // - Test that breaking up the data buffers into many Decode() calls works. | |
| 528 // - Test decode speed. Ideally we can beat 60fps esp on simple test.mp4. | |
| 529 // - Test alternate configurations | |
| 530 // - Test failure conditions. | |
| 531 // - Test multiple concurrent decoders going at once. | |
| 532 // - Test frame size changes mid-stream | |
| 533 | |
| 534 } // anonymous namespace | |
|
brettw
2011/06/02 23:55:04
This should just be "} // namespace"
Ami GONE FROM CHROMIUM
2011/06/03 02:13:06
Done.
| |
| OLD | NEW |