Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 // | 4 // |
| 5 // The bulk of this file is support code; sorry about that. Here's an overview | 5 // The bulk of this file is support code; sorry about that. Here's an overview |
| 6 // to hopefully help readers of this code: | 6 // to hopefully help readers of this code: |
| 7 // - RenderingHelper is charged with interacting with X11, EGL, and GLES2. | 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. | 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 | 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 | 10 // to be written sequentially and wait for the decode client to see certain |
| 11 // state transitions. | 11 // state transitions. |
| 12 // - EglRenderingVDAClient is a VideoDecodeAccelerator::Client implementation | 12 // - EglRenderingVDAClient is a VideoDecodeAccelerator::Client implementation |
| 13 // - Finally actual TEST cases are at the bottom of this file, using the above | 13 // - Finally actual TEST cases are at the bottom of this file, using the above |
| 14 // infrastructure. | 14 // infrastructure. |
| 15 | 15 |
| 16 #include <fcntl.h> | |
| 17 #include <math.h> | |
| 18 #include <sys/stat.h> | |
| 16 #include <sys/types.h> | 19 #include <sys/types.h> |
| 17 #include <sys/stat.h> | |
| 18 #include <fcntl.h> | |
| 19 | 20 |
| 20 // Include gtest.h out of order because <X11/X.h> #define's Bool & None, which | 21 // 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 // 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'ing gtest after anything that pulls in X.h fails to compile. |
| 23 #include "testing/gtest/include/gtest/gtest.h" | 24 #include "testing/gtest/include/gtest/gtest.h" |
| 24 | 25 |
| 25 #include "base/at_exit.h" | 26 #include "base/at_exit.h" |
| 26 #include "base/file_util.h" | 27 #include "base/file_util.h" |
| 27 #include "base/stl_util-inl.h" | 28 #include "base/stl_util-inl.h" |
| 28 #include "base/stringize_macros.h" | 29 #include "base/stringize_macros.h" |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 41 using media::VideoDecodeAccelerator; | 42 using media::VideoDecodeAccelerator; |
| 42 | 43 |
| 43 namespace { | 44 namespace { |
| 44 | 45 |
| 45 // General-purpose constants for this test. | 46 // General-purpose constants for this test. |
| 46 enum { | 47 enum { |
| 47 kFrameWidth = 320, | 48 kFrameWidth = 320, |
| 48 kFrameHeight = 240, | 49 kFrameHeight = 240, |
| 49 }; | 50 }; |
| 50 | 51 |
| 51 // Helper for managing X11, EGL, and GLES2 resources. Because GL state is | 52 // Helper for managing X11, EGL, and GLES2 resources. Xlib is not thread-safe, |
| 52 // thread-specific, all the methods of this class (except for ctor/dtor) CHECK | 53 // and GL state is thread-specific, so all the methods of this class (except for |
| 53 // for being run on a single thread. | 54 // ctor/dtor) ensure they're being run on a single thread. |
| 54 // | 55 // |
| 55 // TODO(fischman): consider moving this into media/ if we can de-dup some of the | 56 // TODO(fischman): consider moving this into media/ if we can de-dup some of the |
| 56 // code that ends up getting copy/pasted all over the place (esp. the GL setup | 57 // code that ends up getting copy/pasted all over the place (esp. the GL setup |
| 57 // code). | 58 // code). |
| 58 class RenderingHelper { | 59 class RenderingHelper { |
| 59 public: | 60 public: |
| 60 explicit RenderingHelper(); | 61 explicit RenderingHelper(); |
| 61 ~RenderingHelper(); | 62 ~RenderingHelper(); |
| 62 | 63 |
| 63 // Initialize all structures to prepare to render to a window of the specified | 64 // Initialize all structures to prepare to render to one or more windows of |
| 64 // dimensions. CHECK-fails if any initialization step fails. After this | 65 // the specified dimensions. CHECK-fails if any initialization step fails. |
| 65 // returns, texture creation and rendering (swaps) can be requested. | 66 // After this returns, texture creation and rendering can be requested. This |
| 66 // This method can be called multiple times, in which case all | 67 // method can be called multiple times, in which case all previously-acquired |
| 67 // previously-acquired resources and initializations are discarded. | 68 // resources and initializations are discarded. If |suppress_swap_to_display| |
| 68 void Initialize(int width, int height, base::WaitableEvent* done); | 69 // then all the usual work is done, except for the final swap of the EGL |
| 70 // surface to the display. This cuts test times over 50% so is worth doing | |
| 71 // when testing non-rendering-related aspects. | |
| 72 void Initialize(bool suppress_swap_to_display, int num_windows, | |
| 73 int width, int height, base::WaitableEvent* done); | |
| 69 | 74 |
| 70 // Undo the effects of Initialize() and signal |*done|. | 75 // Undo the effects of Initialize() and signal |*done|. |
| 71 void UnInitialize(base::WaitableEvent* done); | 76 void UnInitialize(base::WaitableEvent* done); |
| 72 | 77 |
| 73 // Return a newly-created GLES2 texture id. | 78 // Return a newly-created GLES2 texture id rendering to a specific window, and |
| 74 GLuint CreateTexture(); | 79 // signal |*done|. |
| 80 void CreateTexture(int window_id, GLuint* texture_id, | |
| 81 base::WaitableEvent* done); | |
| 75 | 82 |
| 76 // Render |texture_id| to the screen. | 83 // Render |texture_id| to the screen (unless |suppress_swap_to_display_|). |
| 77 void RenderTexture(GLuint texture_id); | 84 void RenderTexture(GLuint texture_id); |
| 78 | 85 |
| 79 EGLDisplay egl_display() { return egl_display_; } | 86 EGLDisplay egl_display() { return egl_display_; } |
| 80 EGLContext egl_context() { return egl_context_; } | 87 EGLContext egl_context() { return egl_context_; } |
| 81 | 88 |
| 82 private: | 89 private: |
| 90 // Zero-out internal state. Helper for ctor & UnInitialize(). | |
| 91 void Clear(); | |
| 92 | |
| 93 bool suppress_swap_to_display_; | |
| 83 int width_; | 94 int width_; |
| 84 int height_; | 95 int height_; |
| 85 Display* x_display_; | 96 Display* x_display_; |
| 86 Window x_window_; | 97 std::vector<Window> x_windows_; |
| 87 EGLDisplay egl_display_; | 98 EGLDisplay egl_display_; |
| 88 EGLContext egl_context_; | 99 EGLContext egl_context_; |
| 89 EGLSurface egl_surface_; | 100 std::vector<EGLSurface> egl_surfaces_; |
| 90 // Since GL carries per-thread state, we ensure all operations are carried out | 101 std::map<GLuint, int> texture_id_to_surface_index_; |
| 91 // on the same thread by remembering where we were Initialized. | 102 // We ensure all operations are carried out on the same thread by remembering |
| 103 // where we were Initialized. | |
| 92 MessageLoop* message_loop_; | 104 MessageLoop* message_loop_; |
| 93 }; | 105 }; |
| 94 | 106 |
| 95 RenderingHelper::RenderingHelper() { | 107 RenderingHelper::RenderingHelper() { |
| 96 memset(this, 0, sizeof(this)); | 108 Clear(); |
| 97 } | 109 } |
| 98 | 110 |
| 99 RenderingHelper::~RenderingHelper() { | 111 RenderingHelper::~RenderingHelper() { |
| 100 CHECK_EQ(width_, 0) << "Must call UnInitialize before dtor."; | 112 CHECK_EQ(width_, 0) << "Must call UnInitialize before dtor."; |
| 101 } | 113 } |
| 102 | 114 |
| 115 void RenderingHelper::Clear() { | |
| 116 suppress_swap_to_display_ = false; | |
| 117 width_ = 0; | |
| 118 height_ = 0; | |
| 119 x_display_ = NULL; | |
| 120 x_windows_.clear(); | |
| 121 egl_display_ = EGL_NO_DISPLAY; | |
| 122 egl_context_ = EGL_NO_CONTEXT; | |
| 123 egl_surfaces_.clear(); | |
| 124 texture_id_to_surface_index_.clear(); | |
| 125 message_loop_ = NULL; | |
| 126 } | |
| 127 | |
| 103 // Helper for Shader creation. | 128 // Helper for Shader creation. |
| 104 static void CreateShader( | 129 static void CreateShader( |
| 105 GLuint program, GLenum type, const char* source, int size) { | 130 GLuint program, GLenum type, const char* source, int size) { |
| 106 GLuint shader = glCreateShader(type); | 131 GLuint shader = glCreateShader(type); |
| 107 glShaderSource(shader, 1, &source, &size); | 132 glShaderSource(shader, 1, &source, &size); |
| 108 glCompileShader(shader); | 133 glCompileShader(shader); |
| 109 int result = GL_FALSE; | 134 int result = GL_FALSE; |
| 110 glGetShaderiv(shader, GL_COMPILE_STATUS, &result); | 135 glGetShaderiv(shader, GL_COMPILE_STATUS, &result); |
| 111 if (!result) { | 136 if (!result) { |
| 112 char log[4096]; | 137 char log[4096]; |
| 113 glGetShaderInfoLog(shader, arraysize(log), NULL, log); | 138 glGetShaderInfoLog(shader, arraysize(log), NULL, log); |
| 114 LOG(FATAL) << log; | 139 LOG(FATAL) << log; |
| 115 } | 140 } |
| 116 glAttachShader(program, shader); | 141 glAttachShader(program, shader); |
| 117 glDeleteShader(shader); | 142 glDeleteShader(shader); |
| 118 CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); | 143 CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); |
| 119 } | 144 } |
| 120 | 145 |
| 121 void RenderingHelper::Initialize( | 146 void RenderingHelper::Initialize( |
| 122 int width, int height, base::WaitableEvent* done) { | 147 bool suppress_swap_to_display, |
| 148 int num_windows, | |
| 149 int width, int height, | |
| 150 base::WaitableEvent* done) { | |
| 123 // Use width_ != 0 as a proxy for the class having already been | 151 // Use width_ != 0 as a proxy for the class having already been |
| 124 // Initialize()'d, and UnInitialize() before continuing. | 152 // Initialize()'d, and UnInitialize() before continuing. |
| 125 if (width_) { | 153 if (width_) { |
| 126 base::WaitableEvent done(false, false); | 154 base::WaitableEvent done(false, false); |
| 127 UnInitialize(&done); | 155 UnInitialize(&done); |
| 128 done.Wait(); | 156 done.Wait(); |
| 129 } | 157 } |
| 130 | 158 |
| 159 suppress_swap_to_display_ = suppress_swap_to_display; | |
| 131 CHECK_GT(width, 0); | 160 CHECK_GT(width, 0); |
| 132 CHECK_GT(height, 0); | 161 CHECK_GT(height, 0); |
| 133 width_ = width; | 162 width_ = width; |
| 134 height_ = height; | 163 height_ = height; |
| 135 message_loop_ = MessageLoop::current(); | 164 message_loop_ = MessageLoop::current(); |
| 136 | 165 |
| 137 // X11 initialization. | 166 // Per-display X11 & EGL initialization. |
| 138 CHECK(x_display_ = XOpenDisplay(NULL)); | 167 CHECK(x_display_ = XOpenDisplay(NULL)); |
| 139 int depth = DefaultDepth(x_display_, DefaultScreen(x_display_)); | 168 int depth = DefaultDepth(x_display_, DefaultScreen(x_display_)); |
| 140 XSetWindowAttributes window_attributes; | 169 XSetWindowAttributes window_attributes; |
| 141 window_attributes.background_pixel = | 170 window_attributes.background_pixel = |
| 142 BlackPixel(x_display_, DefaultScreen(x_display_)); | 171 BlackPixel(x_display_, DefaultScreen(x_display_)); |
| 143 window_attributes.override_redirect = true; | 172 window_attributes.override_redirect = true; |
| 144 x_window_ = XCreateWindow( | |
| 145 x_display_, DefaultRootWindow(x_display_), | |
| 146 100, 100, /* x/y of top-left corner */ | |
| 147 width_, height_, | |
| 148 0 /* border width */, | |
| 149 depth, CopyFromParent /* class */, CopyFromParent /* visual */, | |
| 150 (CWBackPixel | CWOverrideRedirect), &window_attributes); | |
| 151 XStoreName(x_display_, x_window_, "OmxVideoDecodeAcceleratorTest"); | |
| 152 XSelectInput(x_display_, x_window_, ExposureMask); | |
| 153 XMapWindow(x_display_, x_window_); | |
| 154 | 173 |
| 155 // EGL initialization. | |
| 156 egl_display_ = eglGetDisplay(x_display_); | 174 egl_display_ = eglGetDisplay(x_display_); |
| 157 EGLint major; | 175 EGLint major; |
| 158 EGLint minor; | 176 EGLint minor; |
| 159 CHECK(eglInitialize(egl_display_, &major, &minor)) << eglGetError(); | 177 CHECK(eglInitialize(egl_display_, &major, &minor)) << eglGetError(); |
| 160 EGLint rgba8888[] = { | 178 static EGLint rgba8888[] = { |
| 161 EGL_RED_SIZE, 8, | 179 EGL_RED_SIZE, 8, |
| 162 EGL_GREEN_SIZE, 8, | 180 EGL_GREEN_SIZE, 8, |
| 163 EGL_BLUE_SIZE, 8, | 181 EGL_BLUE_SIZE, 8, |
| 164 EGL_ALPHA_SIZE, 8, | 182 EGL_ALPHA_SIZE, 8, |
| 165 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, | 183 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, |
| 166 EGL_NONE, | 184 EGL_NONE, |
| 167 }; | 185 }; |
| 168 | |
| 169 EGLConfig egl_config; | 186 EGLConfig egl_config; |
| 170 int num_configs; | 187 int num_configs; |
| 171 CHECK(eglChooseConfig(egl_display_, rgba8888, &egl_config, 1, &num_configs)) | 188 CHECK(eglChooseConfig(egl_display_, rgba8888, &egl_config, 1, &num_configs)) |
| 172 << eglGetError(); | 189 << eglGetError(); |
| 173 CHECK_GE(num_configs, 1); | 190 CHECK_GE(num_configs, 1); |
| 174 egl_surface_ = | 191 static EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; |
| 175 eglCreateWindowSurface(egl_display_, egl_config, x_window_, NULL); | |
| 176 CHECK_NE(egl_surface_, EGL_NO_SURFACE); | |
| 177 EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; | |
| 178 egl_context_ = eglCreateContext( | 192 egl_context_ = eglCreateContext( |
| 179 egl_display_, egl_config, EGL_NO_CONTEXT, context_attribs); | 193 egl_display_, egl_config, EGL_NO_CONTEXT, context_attribs); |
| 180 CHECK(eglMakeCurrent(egl_display_, egl_surface_, egl_surface_, egl_context_)) | 194 CHECK_NE(egl_context_, EGL_NO_CONTEXT) << eglGetError(); |
| 181 << eglGetError(); | 195 |
| 196 // Per-window/surface X11 & EGL initialization. | |
| 197 for (int i = 0; i < num_windows; ++i) { | |
| 198 // Arrange X windows side by side whimsically. | |
|
vrk (LEFT CHROMIUM)
2011/06/09 20:36:11
:)
Ami GONE FROM CHROMIUM
2011/06/09 23:22:26
Done.
| |
| 199 int top_left_x = (width + 50) * i; | |
| 200 int top_left_y = 100 + (i % 2) * 250; | |
| 201 Window x_window = XCreateWindow( | |
| 202 x_display_, DefaultRootWindow(x_display_), | |
| 203 top_left_x, top_left_y, width_, height_, | |
| 204 0 /* border width */, | |
| 205 depth, CopyFromParent /* class */, CopyFromParent /* visual */, | |
| 206 (CWBackPixel | CWOverrideRedirect), &window_attributes); | |
| 207 x_windows_.push_back(x_window); | |
| 208 XStoreName(x_display_, x_window, "OmxVideoDecodeAcceleratorTest"); | |
| 209 XSelectInput(x_display_, x_window, ExposureMask); | |
| 210 XMapWindow(x_display_, x_window); | |
| 211 | |
| 212 EGLSurface egl_surface = | |
| 213 eglCreateWindowSurface(egl_display_, egl_config, x_window, NULL); | |
| 214 egl_surfaces_.push_back(egl_surface); | |
| 215 CHECK_NE(egl_surface, EGL_NO_SURFACE); | |
| 216 CHECK(eglMakeCurrent(egl_display_, egl_surface, egl_surface, egl_context_)) | |
|
vrk (LEFT CHROMIUM)
2011/06/09 20:36:11
What's the reason for this eglMakeCurrent call her
Ami GONE FROM CHROMIUM
2011/06/09 23:22:26
Without it the shaders fail to compile, presumably
| |
| 217 << eglGetError(); | |
| 218 } | |
| 182 | 219 |
| 183 // GLES2 initialization. Note: This is pretty much copy/pasted from | 220 // GLES2 initialization. Note: This is pretty much copy/pasted from |
| 184 // media/tools/player_x11/gles_video_renderer.cc, with some simplification | 221 // media/tools/player_x11/gles_video_renderer.cc, with some simplification |
| 185 // applied. | 222 // applied. |
| 186 static const float kVertices[] = | 223 static const float kVertices[] = |
| 187 { -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f, -1.f, }; | 224 { -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f, -1.f, }; |
| 188 static const float kTextureCoordsEgl[] = { 0, 1, 0, 0, 1, 1, 1, 0, }; | 225 static const float kTextureCoordsEgl[] = { 0, 1, 0, 0, 1, 1, 1, 0, }; |
| 189 static const char kVertexShader[] = STRINGIZE( | 226 static const char kVertexShader[] = STRINGIZE( |
| 190 varying vec2 interp_tc; | 227 varying vec2 interp_tc; |
| 191 attribute vec4 in_pos; | 228 attribute vec4 in_pos; |
| 192 attribute vec2 in_tc; | 229 attribute vec2 in_tc; |
| 193 void main() { | 230 void main() { |
| 194 interp_tc = in_tc; | 231 interp_tc = in_tc; |
| 195 gl_Position = in_pos; | 232 gl_Position = in_pos; |
| 196 } | 233 } |
| 197 ); | 234 ); |
| 198 static const char kFragmentShaderEgl[] = STRINGIZE( | 235 static const char kFragmentShaderEgl[] = STRINGIZE( |
| 199 precision mediump float; | 236 precision mediump float; |
| 200 varying vec2 interp_tc; | 237 varying vec2 interp_tc; |
| 201 uniform sampler2D tex; | 238 uniform sampler2D tex; |
| 202 void main() { | 239 void main() { |
| 203 gl_FragColor = texture2D(tex, interp_tc); | 240 gl_FragColor = texture2D(tex, interp_tc); |
| 204 } | 241 } |
| 205 ); | 242 ); |
| 206 GLuint program = glCreateProgram(); | 243 GLuint program = glCreateProgram(); |
| 207 CreateShader(program, GL_VERTEX_SHADER, kVertexShader, sizeof(kVertexShader)); | 244 CreateShader(program, GL_VERTEX_SHADER, |
| 245 kVertexShader, arraysize(kVertexShader)); | |
| 208 CreateShader(program, GL_FRAGMENT_SHADER, | 246 CreateShader(program, GL_FRAGMENT_SHADER, |
| 209 kFragmentShaderEgl, sizeof(kFragmentShaderEgl)); | 247 kFragmentShaderEgl, arraysize(kFragmentShaderEgl)); |
| 210 glLinkProgram(program); | 248 glLinkProgram(program); |
| 211 int result = GL_FALSE; | 249 int result = GL_FALSE; |
| 212 glGetProgramiv(program, GL_LINK_STATUS, &result); | 250 glGetProgramiv(program, GL_LINK_STATUS, &result); |
| 213 if (!result) { | 251 if (!result) { |
| 214 char log[4096]; | 252 char log[4096]; |
| 215 glGetShaderInfoLog(program, arraysize(log), NULL, log); | 253 glGetShaderInfoLog(program, arraysize(log), NULL, log); |
| 216 LOG(FATAL) << log; | 254 LOG(FATAL) << log; |
| 217 } | 255 } |
| 218 glUseProgram(program); | 256 glUseProgram(program); |
| 219 glDeleteProgram(program); | 257 glDeleteProgram(program); |
| 220 | 258 |
| 221 glUniform1i(glGetUniformLocation(program, "tex"), 0); | 259 glUniform1i(glGetUniformLocation(program, "tex"), 0); |
| 222 int pos_location = glGetAttribLocation(program, "in_pos"); | 260 int pos_location = glGetAttribLocation(program, "in_pos"); |
| 223 glEnableVertexAttribArray(pos_location); | 261 glEnableVertexAttribArray(pos_location); |
| 224 glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, 0, kVertices); | 262 glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, 0, kVertices); |
| 225 int tc_location = glGetAttribLocation(program, "in_tc"); | 263 int tc_location = glGetAttribLocation(program, "in_tc"); |
| 226 glEnableVertexAttribArray(tc_location); | 264 glEnableVertexAttribArray(tc_location); |
| 227 glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0, | 265 glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0, |
| 228 kTextureCoordsEgl); | 266 kTextureCoordsEgl); |
| 267 | |
| 229 done->Signal(); | 268 done->Signal(); |
| 230 } | 269 } |
| 231 | 270 |
| 232 void RenderingHelper::UnInitialize(base::WaitableEvent* done) { | 271 void RenderingHelper::UnInitialize(base::WaitableEvent* done) { |
| 233 CHECK_EQ(MessageLoop::current(), message_loop_); | 272 CHECK_EQ(MessageLoop::current(), message_loop_); |
| 234 // Destroy resources acquired in Initialize, in reverse-acquisition order. | 273 // Destroy resources acquired in Initialize, in reverse-acquisition order. |
| 235 CHECK(eglMakeCurrent( | 274 CHECK(eglMakeCurrent(egl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE, |
| 236 egl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); | 275 EGL_NO_CONTEXT)) << eglGetError(); |
| 237 CHECK(eglDestroyContext(egl_display_, egl_context_)); | 276 CHECK(eglDestroyContext(egl_display_, egl_context_)); |
| 238 CHECK(eglDestroySurface(egl_display_, egl_surface_)); | 277 for (size_t i = 0; i < egl_surfaces_.size(); ++i) |
| 278 CHECK(eglDestroySurface(egl_display_, egl_surfaces_[i])); | |
| 239 CHECK(eglTerminate(egl_display_)); | 279 CHECK(eglTerminate(egl_display_)); |
| 240 CHECK(XUnmapWindow(x_display_, x_window_)); | 280 for (size_t i = 0; i < x_windows_.size(); ++i) { |
| 241 CHECK(XDestroyWindow(x_display_, x_window_)); | 281 CHECK(XUnmapWindow(x_display_, x_windows_[i])); |
| 282 CHECK(XDestroyWindow(x_display_, x_windows_[i])); | |
| 283 } | |
| 242 // Mimic newly-created object. | 284 // Mimic newly-created object. |
| 243 memset(this, 0, sizeof(this)); | 285 Clear(); |
| 244 done->Signal(); | 286 done->Signal(); |
| 245 } | 287 } |
| 246 | 288 |
| 247 GLuint RenderingHelper::CreateTexture() { | 289 void RenderingHelper::CreateTexture(int window_id, GLuint* texture_id, |
| 248 CHECK_EQ(MessageLoop::current(), message_loop_); | 290 base::WaitableEvent* done) { |
| 249 GLuint texture_id; | 291 if (MessageLoop::current() != message_loop_) { |
| 250 glGenTextures(1, &texture_id); | 292 message_loop_->PostTask( |
| 251 glBindTexture(GL_TEXTURE_2D, texture_id); | 293 FROM_HERE, |
| 294 base::Bind(&RenderingHelper::CreateTexture, base::Unretained(this), | |
| 295 window_id, texture_id, done)); | |
| 296 return; | |
| 297 } | |
| 298 CHECK(eglMakeCurrent(egl_display_, egl_surfaces_[window_id], | |
| 299 egl_surfaces_[window_id], egl_context_)) | |
| 300 << eglGetError(); | |
| 301 glGenTextures(1, texture_id); | |
| 302 glBindTexture(GL_TEXTURE_2D, *texture_id); | |
| 252 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_, 0, GL_RGBA, | 303 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_, 0, GL_RGBA, |
| 253 GL_UNSIGNED_BYTE, NULL); | 304 GL_UNSIGNED_BYTE, NULL); |
| 254 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | 305 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 255 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | 306 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 256 CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); | 307 CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); |
| 257 return texture_id; | 308 CHECK(texture_id_to_surface_index_.insert( |
| 309 std::make_pair(*texture_id, window_id)).second); | |
| 310 done->Signal(); | |
| 258 } | 311 } |
| 259 | 312 |
| 260 void RenderingHelper::RenderTexture(GLuint texture_id) { | 313 void RenderingHelper::RenderTexture(GLuint texture_id) { |
| 261 CHECK_EQ(MessageLoop::current(), message_loop_); | 314 CHECK_EQ(MessageLoop::current(), message_loop_); |
| 262 glActiveTexture(GL_TEXTURE0); | 315 glActiveTexture(GL_TEXTURE0); |
| 263 glBindTexture(GL_TEXTURE_2D, texture_id); | 316 glBindTexture(GL_TEXTURE_2D, texture_id); |
| 264 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | 317 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| 265 DCHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); | 318 DCHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); |
| 266 eglSwapBuffers(egl_display_, egl_surface_); | 319 DCHECK_EQ(static_cast<int>(eglGetError()), EGL_SUCCESS); |
| 320 if (!suppress_swap_to_display_) { | |
| 321 int window_id = texture_id_to_surface_index_[texture_id]; | |
| 322 CHECK(eglMakeCurrent(egl_display_, egl_surfaces_[window_id], | |
| 323 egl_surfaces_[window_id], egl_context_)) | |
| 324 << eglGetError(); | |
| 325 eglSwapBuffers(egl_display_, egl_surfaces_[window_id]); | |
| 326 } | |
| 267 DCHECK_EQ(static_cast<int>(eglGetError()), EGL_SUCCESS); | 327 DCHECK_EQ(static_cast<int>(eglGetError()), EGL_SUCCESS); |
| 268 } | 328 } |
| 269 | 329 |
| 270 // State of the EglRenderingVDAClient below. | 330 // State of the EglRenderingVDAClient below. |
| 271 enum ClientState { | 331 enum ClientState { |
| 272 CS_CREATED, | 332 CS_CREATED, |
| 273 CS_DECODER_SET, | 333 CS_DECODER_SET, |
| 274 CS_INITIALIZED, | 334 CS_INITIALIZED, |
| 275 CS_FLUSHED, | 335 CS_FLUSHED, |
| 276 CS_DONE, | 336 CS_DONE, |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 315 pending_states_for_notification_.pop(); | 375 pending_states_for_notification_.pop(); |
| 316 return ret; | 376 return ret; |
| 317 } | 377 } |
| 318 | 378 |
| 319 // Client that can accept callbacks from a VideoDecodeAccelerator and is used by | 379 // Client that can accept callbacks from a VideoDecodeAccelerator and is used by |
| 320 // the TESTs below. | 380 // the TESTs below. |
| 321 class EglRenderingVDAClient : public VideoDecodeAccelerator::Client { | 381 class EglRenderingVDAClient : public VideoDecodeAccelerator::Client { |
| 322 public: | 382 public: |
| 323 // Doesn't take ownership of |note| or |encoded_data|, which must outlive | 383 // Doesn't take ownership of |note| or |encoded_data|, which must outlive |
| 324 // |*this|. | 384 // |*this|. |
| 325 EglRenderingVDAClient(ClientStateNotification* note, | 385 EglRenderingVDAClient(RenderingHelper* rendering_helper, |
| 326 std::string* encoded_data); | 386 int rendering_window_id, |
| 387 ClientStateNotification* note, | |
| 388 std::string* encoded_data, | |
| 389 int num_NALs_per_decode, | |
| 390 bool suppress_swap_to_display); | |
| 327 virtual ~EglRenderingVDAClient(); | 391 virtual ~EglRenderingVDAClient(); |
| 328 | 392 |
| 329 // VideoDecodeAccelerator::Client implementation. | 393 // VideoDecodeAccelerator::Client implementation. |
| 330 // The heart of the Client. | 394 // The heart of the Client. |
| 331 virtual void ProvidePictureBuffers( | 395 virtual void ProvidePictureBuffers( |
| 332 uint32 requested_num_of_buffers, | 396 uint32 requested_num_of_buffers, |
| 333 const gfx::Size& dimensions, | 397 const gfx::Size& dimensions, |
| 334 VideoDecodeAccelerator::MemoryType type); | 398 VideoDecodeAccelerator::MemoryType type); |
| 335 virtual void DismissPictureBuffer(int32 picture_buffer_id); | 399 virtual void DismissPictureBuffer(int32 picture_buffer_id); |
| 336 virtual void PictureReady(const media::Picture& picture); | 400 virtual void PictureReady(const media::Picture& picture); |
| 337 // Simple state changes. | 401 // Simple state changes. |
| 338 virtual void NotifyInitializeDone(); | 402 virtual void NotifyInitializeDone(); |
| 339 virtual void NotifyEndOfStream(); | 403 virtual void NotifyEndOfStream(); |
| 340 virtual void NotifyEndOfBitstreamBuffer(int32 bitstream_buffer_id); | 404 virtual void NotifyEndOfBitstreamBuffer(int32 bitstream_buffer_id); |
| 341 virtual void NotifyFlushDone(); | 405 virtual void NotifyFlushDone(); |
| 342 virtual void NotifyAbortDone(); | 406 virtual void NotifyAbortDone(); |
| 343 virtual void NotifyError(VideoDecodeAccelerator::Error error); | 407 virtual void NotifyError(VideoDecodeAccelerator::Error error); |
| 344 | 408 |
| 345 // Doesn't take ownership of |decoder|, which must outlive |*this|. | 409 // Doesn't take ownership of |decoder|, which must outlive |*this|. |
| 346 void SetDecoder(VideoDecodeAccelerator* decoder); | 410 void SetDecoder(VideoDecodeAccelerator* decoder); |
| 347 | 411 |
| 348 // Simple getters for inspecting the state of the Client. | 412 // Simple getters for inspecting the state of the Client. |
| 349 ClientState state() { return state_; } | 413 ClientState state() { return state_; } |
| 350 VideoDecodeAccelerator::Error error() { return error_; } | 414 VideoDecodeAccelerator::Error error() { return error_; } |
| 351 int num_done_bitstream_buffers() { return num_done_bitstream_buffers_; } | 415 int num_done_bitstream_buffers() { return num_done_bitstream_buffers_; } |
| 352 int num_decoded_frames() { return num_decoded_frames_; } | 416 int num_decoded_frames() { return num_decoded_frames_; } |
| 353 MessageLoop* message_loop() { return thread_.message_loop(); } | 417 EGLDisplay egl_display() { return rendering_helper_->egl_display(); } |
| 354 EGLDisplay egl_display() { return rendering_helper_.egl_display(); } | 418 EGLContext egl_context() { return rendering_helper_->egl_context(); } |
| 355 EGLContext egl_context() { return rendering_helper_.egl_context(); } | 419 double frames_per_second(); |
| 356 | 420 |
| 357 private: | 421 private: |
| 358 void SetState(ClientState new_state) { | 422 void SetState(ClientState new_state) { |
| 359 note_->Notify(new_state); | 423 note_->Notify(new_state); |
| 360 state_ = new_state; | 424 state_ = new_state; |
| 361 } | 425 } |
| 362 | 426 |
| 363 // Request decode of the next NALU in the encoded data. | 427 // Compute & return in |*end_pos| the end position for the next batch of NALs |
|
vrk (LEFT CHROMIUM)
2011/06/09 20:36:11
teeny tiny nit: should all these NALs be NALUs?
Ami GONE FROM CHROMIUM
2011/06/09 23:22:26
Done.
| |
| 364 void DecodeNextNALU(); | 428 // to ship to the decoder (based on |start_pos| & |num_NALs_per_decode_|). |
| 429 void GetRangeForNextNALs(size_t start_pos, size_t* end_pos); | |
| 365 | 430 |
| 431 // Request decode of the next batch of NALUs in the encoded data. | |
| 432 void DecodeNextNALUs(); | |
| 433 | |
| 434 RenderingHelper* rendering_helper_; | |
| 435 int rendering_window_id_; | |
| 366 const std::string* encoded_data_; | 436 const std::string* encoded_data_; |
| 437 const int num_NALs_per_decode_; | |
| 367 size_t encoded_data_next_pos_to_decode_; | 438 size_t encoded_data_next_pos_to_decode_; |
| 368 int next_bitstream_buffer_id_; | 439 int next_bitstream_buffer_id_; |
| 369 ClientStateNotification* note_; | 440 ClientStateNotification* note_; |
| 370 VideoDecodeAccelerator* decoder_; | 441 VideoDecodeAccelerator* decoder_; |
| 371 ClientState state_; | 442 ClientState state_; |
| 372 VideoDecodeAccelerator::Error error_; | 443 VideoDecodeAccelerator::Error error_; |
| 373 int num_decoded_frames_; | 444 int num_decoded_frames_; |
| 374 int num_done_bitstream_buffers_; | 445 int num_done_bitstream_buffers_; |
| 375 std::map<int, media::GLESBuffer*> picture_buffers_by_id_; | 446 std::map<int, media::GLESBuffer*> picture_buffers_by_id_; |
| 376 // Required for Thread to work. Not used otherwise. | 447 base::TimeTicks initialize_done_ticks_; |
| 377 base::ShadowingAtExitManager at_exit_manager_; | 448 base::TimeTicks last_frame_delivered_ticks_; |
| 378 base::Thread thread_; | |
| 379 RenderingHelper rendering_helper_; | |
| 380 }; | 449 }; |
| 381 | 450 |
| 382 EglRenderingVDAClient::EglRenderingVDAClient(ClientStateNotification* note, | 451 EglRenderingVDAClient::EglRenderingVDAClient(RenderingHelper* rendering_helper, |
| 383 std::string* encoded_data) | 452 int rendering_window_id, |
| 384 : encoded_data_(encoded_data), encoded_data_next_pos_to_decode_(0), | 453 ClientStateNotification* note, |
| 385 next_bitstream_buffer_id_(0), note_(note), decoder_(NULL), | 454 std::string* encoded_data, |
| 386 state_(CS_CREATED), | 455 int num_NALs_per_decode, |
| 456 bool suppress_swap_to_display) | |
| 457 : rendering_helper_(rendering_helper), | |
| 458 rendering_window_id_(rendering_window_id), | |
| 459 encoded_data_(encoded_data), num_NALs_per_decode_(num_NALs_per_decode), | |
| 460 encoded_data_next_pos_to_decode_(0), next_bitstream_buffer_id_(0), | |
| 461 note_(note), decoder_(NULL), state_(CS_CREATED), | |
| 387 error_(VideoDecodeAccelerator::VIDEODECODERERROR_NONE), | 462 error_(VideoDecodeAccelerator::VIDEODECODERERROR_NONE), |
| 388 num_decoded_frames_(0), num_done_bitstream_buffers_(0), | 463 num_decoded_frames_(0), num_done_bitstream_buffers_(0) { |
| 389 thread_("EglRenderingVDAClientThread") { | 464 CHECK_GT(num_NALs_per_decode, 0); |
| 390 CHECK(thread_.Start()); | |
| 391 base::WaitableEvent done(false, false); | |
| 392 message_loop()->PostTask( | |
| 393 FROM_HERE, | |
| 394 base::Bind(&RenderingHelper::Initialize, | |
| 395 base::Unretained(&rendering_helper_), | |
| 396 static_cast<int>(kFrameWidth), static_cast<int>(kFrameHeight), | |
| 397 &done)); | |
| 398 done.Wait(); | |
| 399 } | 465 } |
| 400 | 466 |
| 401 EglRenderingVDAClient::~EglRenderingVDAClient() { | 467 EglRenderingVDAClient::~EglRenderingVDAClient() { |
| 402 base::WaitableEvent done(false, false); | |
| 403 message_loop()->PostTask( | |
| 404 FROM_HERE, | |
| 405 base::Bind(&RenderingHelper::UnInitialize, | |
| 406 base::Unretained(&rendering_helper_), | |
| 407 &done)); | |
| 408 done.Wait(); | |
| 409 thread_.Stop(); | |
| 410 STLDeleteValues(&picture_buffers_by_id_); | 468 STLDeleteValues(&picture_buffers_by_id_); |
| 411 SetState(CS_DESTROYED); | 469 SetState(CS_DESTROYED); |
| 412 } | 470 } |
| 413 | 471 |
| 414 void EglRenderingVDAClient::ProvidePictureBuffers( | 472 void EglRenderingVDAClient::ProvidePictureBuffers( |
| 415 uint32 requested_num_of_buffers, | 473 uint32 requested_num_of_buffers, |
| 416 const gfx::Size& dimensions, | 474 const gfx::Size& dimensions, |
| 417 VideoDecodeAccelerator::MemoryType type) { | 475 VideoDecodeAccelerator::MemoryType type) { |
| 418 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 419 CHECK_EQ(type, VideoDecodeAccelerator::PICTUREBUFFER_MEMORYTYPE_GL_TEXTURE); | 476 CHECK_EQ(type, VideoDecodeAccelerator::PICTUREBUFFER_MEMORYTYPE_GL_TEXTURE); |
| 420 std::vector<media::GLESBuffer> buffers; | 477 std::vector<media::GLESBuffer> buffers; |
| 421 CHECK_EQ(dimensions.width(), kFrameWidth); | 478 CHECK_EQ(dimensions.width(), kFrameWidth); |
| 422 CHECK_EQ(dimensions.height(), kFrameHeight); | 479 CHECK_EQ(dimensions.height(), kFrameHeight); |
| 423 | 480 |
| 424 for (uint32 i = 0; i < requested_num_of_buffers; ++i) { | 481 for (uint32 i = 0; i < requested_num_of_buffers; ++i) { |
| 425 uint32 id = picture_buffers_by_id_.size(); | 482 uint32 id = picture_buffers_by_id_.size(); |
| 426 GLuint texture_id = rendering_helper_.CreateTexture(); | 483 GLuint texture_id; |
| 484 base::WaitableEvent done(false, false); | |
| 485 rendering_helper_->CreateTexture(rendering_window_id_, &texture_id, &done); | |
| 486 done.Wait(); | |
| 427 // TODO(fischman): context_id is always 0. Can it be removed from the API? | 487 // TODO(fischman): context_id is always 0. Can it be removed from the API? |
| 428 // (since it's always inferrable from context). | 488 // (since it's always inferrable from context). |
| 429 media::GLESBuffer* buffer = | 489 media::GLESBuffer* buffer = |
| 430 new media::GLESBuffer(id, dimensions, texture_id, 0 /* context_id */); | 490 new media::GLESBuffer(id, dimensions, texture_id, 0 /* context_id */); |
| 431 CHECK(picture_buffers_by_id_.insert(std::make_pair(id, buffer)).second); | 491 CHECK(picture_buffers_by_id_.insert(std::make_pair(id, buffer)).second); |
| 432 buffers.push_back(*buffer); | 492 buffers.push_back(*buffer); |
| 433 } | 493 } |
| 434 decoder_->AssignGLESBuffers(buffers); | 494 decoder_->AssignGLESBuffers(buffers); |
| 435 CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); | 495 CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); |
| 436 CHECK_EQ(static_cast<int>(eglGetError()), EGL_SUCCESS); | 496 CHECK_EQ(static_cast<int>(eglGetError()), EGL_SUCCESS); |
| 437 } | 497 } |
| 438 | 498 |
| 439 void EglRenderingVDAClient::DismissPictureBuffer(int32 picture_buffer_id) { | 499 void EglRenderingVDAClient::DismissPictureBuffer(int32 picture_buffer_id) { |
|
vrk (LEFT CHROMIUM)
2011/06/09 20:36:11
Didn't catch this before, but I think you also hav
Ami GONE FROM CHROMIUM
2011/06/09 23:22:26
Done.
| |
| 440 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 441 delete picture_buffers_by_id_[picture_buffer_id]; | 500 delete picture_buffers_by_id_[picture_buffer_id]; |
| 442 CHECK_EQ(1U, picture_buffers_by_id_.erase(picture_buffer_id)); | 501 CHECK_EQ(1U, picture_buffers_by_id_.erase(picture_buffer_id)); |
| 443 } | 502 } |
| 444 | 503 |
| 445 void EglRenderingVDAClient::PictureReady(const media::Picture& picture) { | 504 void EglRenderingVDAClient::PictureReady(const media::Picture& picture) { |
| 446 CHECK_EQ(message_loop(), MessageLoop::current()); | 505 last_frame_delivered_ticks_ = base::TimeTicks::Now(); |
| 447 | 506 |
| 448 // Because we feed the decoder one NALU at a time, we can be sure each frame | 507 // Because we feed the decoder a limited number of NALUs at a time, we can be |
| 449 // comes from a bitstream buffer numbered at least as high as our current | 508 // sure each the bitstream buffer from which a frame comes has a limited |
| 450 // decoded frame's index, and less than the id of the next bitstream buffer | 509 // range. Assert that. |
| 451 // we'll send for decoding. Assert that. | 510 CHECK_GE((picture.bitstream_buffer_id() + 1) * num_NALs_per_decode_, |
| 452 CHECK_GE(picture.bitstream_buffer_id(), num_decoded_frames_); | 511 num_decoded_frames_); |
| 453 CHECK_LE(picture.bitstream_buffer_id(), next_bitstream_buffer_id_); | 512 CHECK_LE(picture.bitstream_buffer_id(), next_bitstream_buffer_id_); |
| 454 ++num_decoded_frames_; | 513 ++num_decoded_frames_; |
| 455 | 514 |
| 456 media::GLESBuffer* gles_buffer = | 515 media::GLESBuffer* gles_buffer = |
| 457 picture_buffers_by_id_[picture.picture_buffer_id()]; | 516 picture_buffers_by_id_[picture.picture_buffer_id()]; |
| 458 CHECK(gles_buffer); | 517 CHECK(gles_buffer); |
| 459 rendering_helper_.RenderTexture(gles_buffer->texture_id()); | 518 rendering_helper_->RenderTexture(gles_buffer->texture_id()); |
| 460 | 519 |
| 461 decoder_->ReusePictureBuffer(picture.picture_buffer_id()); | 520 decoder_->ReusePictureBuffer(picture.picture_buffer_id()); |
| 462 } | 521 } |
| 463 | 522 |
| 464 void EglRenderingVDAClient::NotifyInitializeDone() { | 523 void EglRenderingVDAClient::NotifyInitializeDone() { |
| 465 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 466 SetState(CS_INITIALIZED); | 524 SetState(CS_INITIALIZED); |
| 467 DecodeNextNALU(); | 525 initialize_done_ticks_ = base::TimeTicks::Now(); |
| 526 DecodeNextNALUs(); | |
| 468 } | 527 } |
| 469 | 528 |
| 470 void EglRenderingVDAClient::NotifyEndOfStream() { | 529 void EglRenderingVDAClient::NotifyEndOfStream() { |
| 471 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 472 SetState(CS_DONE); | 530 SetState(CS_DONE); |
| 473 } | 531 } |
| 474 | 532 |
| 475 void EglRenderingVDAClient::NotifyEndOfBitstreamBuffer( | 533 void EglRenderingVDAClient::NotifyEndOfBitstreamBuffer( |
| 476 int32 bitstream_buffer_id) { | 534 int32 bitstream_buffer_id) { |
| 477 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 478 ++num_done_bitstream_buffers_; | 535 ++num_done_bitstream_buffers_; |
| 479 DecodeNextNALU(); | 536 DecodeNextNALUs(); |
| 480 } | 537 } |
| 481 | 538 |
| 482 void EglRenderingVDAClient::NotifyFlushDone() { | 539 void EglRenderingVDAClient::NotifyFlushDone() { |
| 483 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 484 SetState(CS_FLUSHED); | 540 SetState(CS_FLUSHED); |
| 485 } | 541 } |
| 486 | 542 |
| 487 void EglRenderingVDAClient::NotifyAbortDone() { | 543 void EglRenderingVDAClient::NotifyAbortDone() { |
| 488 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 489 SetState(CS_ABORTED); | 544 SetState(CS_ABORTED); |
| 490 } | 545 } |
| 491 | 546 |
| 492 void EglRenderingVDAClient::NotifyError(VideoDecodeAccelerator::Error error) { | 547 void EglRenderingVDAClient::NotifyError(VideoDecodeAccelerator::Error error) { |
| 493 CHECK_EQ(message_loop(), MessageLoop::current()); | |
| 494 SetState(CS_ERROR); | 548 SetState(CS_ERROR); |
| 495 error_ = error; | 549 error_ = error; |
| 496 } | 550 } |
| 497 | 551 |
| 498 void EglRenderingVDAClient::SetDecoder(VideoDecodeAccelerator* decoder) { | 552 void EglRenderingVDAClient::SetDecoder(VideoDecodeAccelerator* decoder) { |
| 499 decoder_ = decoder; | 553 decoder_ = decoder; |
| 500 SetState(CS_DECODER_SET); | 554 SetState(CS_DECODER_SET); |
| 501 } | 555 } |
| 502 | 556 |
| 503 static bool LookingAtNAL(const std::string& encoded, size_t pos) { | 557 static bool LookingAtNAL(const std::string& encoded, size_t pos) { |
| 504 return pos + 3 < encoded.size() && | 558 return pos + 3 < encoded.size() && |
| 505 encoded[pos] == 0 && encoded[pos + 1] == 0 && | 559 encoded[pos] == 0 && encoded[pos + 1] == 0 && |
| 506 encoded[pos + 2] == 0 && encoded[pos + 3] == 1; | 560 encoded[pos + 2] == 0 && encoded[pos + 3] == 1; |
| 507 } | 561 } |
| 508 | 562 |
| 509 void EglRenderingVDAClient::DecodeNextNALU() { | 563 void EglRenderingVDAClient::GetRangeForNextNALs( |
| 564 size_t start_pos, size_t* end_pos) { | |
| 565 *end_pos = start_pos; | |
| 566 CHECK(LookingAtNAL(*encoded_data_, start_pos)); | |
| 567 for (int i = 0; i < num_NALs_per_decode_; ++i) { | |
| 568 *end_pos += 4; | |
| 569 while (*end_pos + 3 < encoded_data_->size() && | |
| 570 !LookingAtNAL(*encoded_data_, *end_pos)) { | |
| 571 ++*end_pos; | |
| 572 } | |
| 573 if (*end_pos + 3 >= encoded_data_->size()) { | |
| 574 *end_pos = encoded_data_->size(); | |
| 575 return; | |
| 576 } | |
| 577 } | |
| 578 } | |
| 579 | |
| 580 void EglRenderingVDAClient::DecodeNextNALUs() { | |
| 510 if (encoded_data_next_pos_to_decode_ == encoded_data_->size()) { | 581 if (encoded_data_next_pos_to_decode_ == encoded_data_->size()) { |
| 511 decoder_->Flush(); | 582 decoder_->Flush(); |
| 512 return; | 583 return; |
| 513 } | 584 } |
| 514 size_t start_pos = encoded_data_next_pos_to_decode_; | 585 size_t start_pos = encoded_data_next_pos_to_decode_; |
| 515 CHECK(LookingAtNAL(*encoded_data_, start_pos)); | 586 size_t end_pos; |
| 516 size_t end_pos = encoded_data_next_pos_to_decode_ + 4; | 587 GetRangeForNextNALs(start_pos, &end_pos); |
| 517 while (end_pos + 3 < encoded_data_->size() && | |
| 518 !LookingAtNAL(*encoded_data_, end_pos)) { | |
| 519 ++end_pos; | |
| 520 } | |
| 521 if (end_pos + 3 >= encoded_data_->size()) | |
| 522 end_pos = encoded_data_->size(); | |
| 523 | 588 |
| 524 // Populate the shared memory buffer w/ the NALU, duplicate its handle, and | 589 // Populate the shared memory buffer w/ the NALU, duplicate its handle, and |
| 525 // hand it off to the decoder. | 590 // hand it off to the decoder. |
| 526 base::SharedMemory shm; | 591 base::SharedMemory shm; |
| 527 CHECK(shm.CreateAndMapAnonymous(end_pos - start_pos)) | 592 CHECK(shm.CreateAndMapAnonymous(end_pos - start_pos)) |
| 528 << start_pos << ", " << end_pos; | 593 << start_pos << ", " << end_pos; |
| 529 memcpy(shm.memory(), encoded_data_->data() + start_pos, end_pos - start_pos); | 594 memcpy(shm.memory(), encoded_data_->data() + start_pos, end_pos - start_pos); |
| 530 base::SharedMemoryHandle dup_handle; | 595 base::SharedMemoryHandle dup_handle; |
| 531 CHECK(shm.ShareToProcess(base::Process::Current().handle(), &dup_handle)); | 596 CHECK(shm.ShareToProcess(base::Process::Current().handle(), &dup_handle)); |
| 532 media::BitstreamBuffer bitstream_buffer( | 597 media::BitstreamBuffer bitstream_buffer( |
| 533 next_bitstream_buffer_id_++, dup_handle, end_pos - start_pos); | 598 next_bitstream_buffer_id_++, dup_handle, end_pos - start_pos); |
| 534 decoder_->Decode(bitstream_buffer); | 599 decoder_->Decode(bitstream_buffer); |
| 535 encoded_data_next_pos_to_decode_ = end_pos; | 600 encoded_data_next_pos_to_decode_ = end_pos; |
| 536 } | 601 } |
| 537 | 602 |
| 603 double EglRenderingVDAClient::frames_per_second() { | |
| 604 base::TimeDelta delta = last_frame_delivered_ticks_ - initialize_done_ticks_; | |
| 605 CHECK_GT(delta.InSecondsF(), 0); | |
| 606 return num_decoded_frames_ / delta.InSecondsF(); | |
| 607 } | |
| 608 | |
| 609 // Test parameters: | |
| 610 // - Number of NALs per Decode() call. | |
| 611 // - Number of concurrent decoders. | |
| 612 class OmxVideoDecodeAcceleratorTest | |
| 613 : public ::testing::TestWithParam<std::pair<int, int> > { | |
| 614 }; | |
| 615 | |
| 538 // Test the most straightforward case possible: data is decoded from a single | 616 // Test the most straightforward case possible: data is decoded from a single |
| 539 // chunk and rendered to the screen. | 617 // chunk and rendered to the screen. |
| 540 TEST(OmxVideoDecodeAcceleratorTest, TestSimpleDecode) { | 618 TEST_P(OmxVideoDecodeAcceleratorTest, TestSimpleDecode) { |
| 541 logging::SetMinLogLevel(-1); | 619 // Can be useful for debugging VLOGs from OVDA. |
| 542 ClientStateNotification note; | 620 // logging::SetMinLogLevel(-1); |
| 543 ClientState state; | 621 |
| 544 // Read in the video data and hand it off to the client for later decoding. | 622 // Required for Thread to work. Not used otherwise. |
| 623 base::ShadowingAtExitManager at_exit_manager_; | |
|
vrk (LEFT CHROMIUM)
2011/06/09 20:36:11
nit: no ending _
Ami GONE FROM CHROMIUM
2011/06/09 23:22:26
Done.
| |
| 624 | |
| 625 const int num_NALs_per_decode = GetParam().first; | |
| 626 const size_t num_concurrent_decoders = GetParam().second; | |
| 627 | |
| 628 // Suppress EGL surface swapping in all but one test, to cut down overall test | |
|
vrk (LEFT CHROMIUM)
2011/06/09 20:36:11
Looks like 2 tests have num_NALs_per_decode == 1?
Ami GONE FROM CHROMIUM
2011/06/09 23:22:26
Done.
| |
| 629 // runtime. | |
| 630 const bool suppress_swap_to_display = num_NALs_per_decode > 1; | |
| 631 | |
| 632 std::vector<ClientStateNotification*> notes(num_concurrent_decoders, NULL); | |
| 633 std::vector<EglRenderingVDAClient*> clients(num_concurrent_decoders, NULL); | |
| 634 std::vector<OmxVideoDecodeAccelerator*> decoders( | |
| 635 num_concurrent_decoders, NULL); | |
| 636 // Read in the video data. | |
| 545 std::string data_str; | 637 std::string data_str; |
| 546 CHECK(file_util::ReadFileToString(FilePath(std::string("test-25fps.h264")), | 638 CHECK(file_util::ReadFileToString(FilePath(std::string("test-25fps.h264")), |
| 547 &data_str)); | 639 &data_str)); |
| 548 EglRenderingVDAClient client(¬e, &data_str); | |
| 549 OmxVideoDecodeAccelerator decoder(&client, client.message_loop()); | |
| 550 client.SetDecoder(&decoder); | |
| 551 decoder.SetEglState(client.egl_display(), client.egl_context()); | |
| 552 ASSERT_EQ((state = note.Wait()), CS_DECODER_SET); | |
| 553 | 640 |
| 641 // Initialize the rendering helper. | |
| 642 base::Thread rendering_thread("EglRenderingVDAClientThread"); | |
| 643 rendering_thread.Start(); | |
| 644 RenderingHelper rendering_helper; | |
| 554 | 645 |
| 555 // Configure the decoder. | 646 base::WaitableEvent done(false, false); |
| 556 int32 config_array[] = { | 647 rendering_thread.message_loop()->PostTask( |
| 557 media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_FOURCC, | 648 FROM_HERE, |
| 558 media::VIDEOCODECFOURCC_H264, | 649 base::Bind(&RenderingHelper::Initialize, |
| 559 media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_WIDTH, kFrameWidth, | 650 base::Unretained(&rendering_helper), |
| 560 media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_HEIGHT, kFrameHeight, | 651 suppress_swap_to_display, num_concurrent_decoders, |
| 561 media::VIDEOATTRIBUTEKEY_VIDEOCOLORFORMAT, media::VIDEOCOLORFORMAT_RGBA, | 652 static_cast<int>(kFrameWidth), static_cast<int>(kFrameHeight), |
| 562 media::VIDEOATTRIBUTEKEY_TERMINATOR | 653 &done)); |
| 563 }; | 654 done.Wait(); |
| 564 std::vector<uint32> config( | |
| 565 config_array, config_array + arraysize(config_array)); | |
| 566 CHECK(decoder.Initialize(config)); | |
| 567 ASSERT_EQ((state = note.Wait()), CS_INITIALIZED); | |
| 568 // InitializeDone kicks off decoding inside the client, so we just need to | |
| 569 // wait for Flush. | |
| 570 ASSERT_EQ((state = note.Wait()), CS_FLUSHED); | |
| 571 | 655 |
| 572 EXPECT_EQ(client.num_decoded_frames(), 25 /* fps */ * 10 /* seconds */); | 656 // First kick off all the decoders. |
| 573 // Hard-coded the number of NALUs in the stream. Icky. | 657 for (size_t index = 0; index < num_concurrent_decoders; ++index) { |
| 574 EXPECT_EQ(client.num_done_bitstream_buffers(), 258); | 658 ClientStateNotification* note = new ClientStateNotification(); |
| 575 } | 659 notes[index] = note; |
| 660 EglRenderingVDAClient* client = new EglRenderingVDAClient( | |
| 661 &rendering_helper, index, | |
| 662 note, &data_str, num_NALs_per_decode, | |
| 663 suppress_swap_to_display); | |
| 664 clients[index] = client; | |
| 665 OmxVideoDecodeAccelerator* decoder = | |
| 666 new OmxVideoDecodeAccelerator(client, rendering_thread.message_loop()); | |
| 667 decoders[index] = decoder; | |
| 668 client->SetDecoder(decoder); | |
| 669 decoder->SetEglState(client->egl_display(), client->egl_context()); | |
| 670 ASSERT_EQ(note->Wait(), CS_DECODER_SET); | |
| 671 | |
| 672 // Configure the decoder. | |
| 673 int32 config_array[] = { | |
| 674 media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_FOURCC, | |
| 675 media::VIDEOCODECFOURCC_H264, | |
| 676 media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_WIDTH, kFrameWidth, | |
| 677 media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_HEIGHT, kFrameHeight, | |
| 678 media::VIDEOATTRIBUTEKEY_VIDEOCOLORFORMAT, media::VIDEOCOLORFORMAT_RGBA, | |
| 679 media::VIDEOATTRIBUTEKEY_TERMINATOR | |
| 680 }; | |
| 681 std::vector<uint32> config( | |
| 682 config_array, config_array + arraysize(config_array)); | |
| 683 CHECK(decoder->Initialize(config)); | |
| 684 } | |
| 685 // Then wait for all the decodes to finish. | |
| 686 for (size_t i = 0; i < num_concurrent_decoders; ++i) { | |
| 687 ClientStateNotification* note = notes[i]; | |
| 688 ASSERT_EQ(note->Wait(), CS_INITIALIZED); | |
| 689 // InitializeDone kicks off decoding inside the client, so we just need to | |
| 690 // wait for Flush. | |
| 691 ASSERT_EQ(note->Wait(), CS_FLUSHED); | |
| 692 } | |
| 693 // Finally assert that decoding went as expected. | |
| 694 for (size_t i = 0; i < num_concurrent_decoders; ++i) { | |
| 695 EglRenderingVDAClient* client = clients[i]; | |
| 696 EXPECT_EQ(client->num_decoded_frames(), 25 /* fps */ * 10 /* seconds */); | |
| 697 // Hard-coded the number of NALUs in the stream. Icky. | |
| 698 EXPECT_EQ(client->num_done_bitstream_buffers(), | |
| 699 ceil(258.0 / num_NALs_per_decode)); | |
| 700 // These numbers are pulled out of a hat, but seem to be safe with current | |
| 701 // hardware. | |
| 702 double min_expected_fps = suppress_swap_to_display ? 175 : 50; | |
| 703 min_expected_fps /= num_concurrent_decoders; | |
| 704 LOG(INFO) << "Decoder " << i << " fps: " << client->frames_per_second(); | |
| 705 EXPECT_GT(client->frames_per_second(), min_expected_fps); | |
| 706 } | |
| 707 STLDeleteElements(&decoders); | |
| 708 STLDeleteElements(&clients); | |
| 709 STLDeleteElements(¬es); | |
| 710 | |
| 711 rendering_thread.message_loop()->PostTask( | |
| 712 FROM_HERE, | |
| 713 base::Bind(&RenderingHelper::UnInitialize, | |
| 714 base::Unretained(&rendering_helper), | |
| 715 &done)); | |
| 716 done.Wait(); | |
| 717 rendering_thread.Stop(); | |
| 718 }; | |
| 719 | |
| 720 // TODO(fischman): using 16 and higher below breaks decode - visual artifacts | |
| 721 // are introduced as well as spurious frames are delivered (more pictures are | |
| 722 // returned than NALUs are fed to the decoder). Increase the "15" below when | |
| 723 // http://code.google.com/p/chrome-os-partner/issues/detail?id=4378 is fixed. | |
| 724 INSTANTIATE_TEST_CASE_P( | |
| 725 DecodeVariations, OmxVideoDecodeAcceleratorTest, | |
| 726 ::testing::Values( | |
| 727 std::make_pair(1, 1), std::make_pair(1, 3), std::make_pair(2, 1), | |
| 728 std::make_pair(3, 1), std::make_pair(5, 1), std::make_pair(8, 1), | |
| 729 std::make_pair(15, 1))); | |
| 576 | 730 |
| 577 // TODO(fischman, vrk): add more tests! In particular: | 731 // TODO(fischman, vrk): add more tests! In particular: |
| 578 // - Test that chunking Decode() calls differently works. | 732 // - Test life-cycle: Seek/Stop/Pause/Play/RePlay for a single decoder. |
| 579 // - Test for memory leaks (valgrind) | 733 // - Test for memory leaks (valgrind) |
| 580 // - Test decode speed. Ideally we can beat 60fps esp on simple test.mp4. | |
| 581 // - Test alternate configurations | 734 // - Test alternate configurations |
| 582 // - Test failure conditions. | 735 // - Test failure conditions. |
| 583 // - Test multiple decodes; sequentially & concurrently. | |
|
vrk (LEFT CHROMIUM)
2011/06/09 20:36:11
In what way did you want to test decodes sequentia
Ami GONE FROM CHROMIUM
2011/06/09 23:22:26
Play one stream, then play another, with the same
| |
| 584 // - Test frame size changes mid-stream | 736 // - Test frame size changes mid-stream |
| 585 | 737 |
| 586 } // namespace | 738 } // namespace |
| OLD | NEW |