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