OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "media/tools/player_x11/gles_video_renderer.h" |
| 6 |
| 7 #include <dlfcn.h> |
| 8 #include <X11/Xutil.h> |
| 9 #include <X11/extensions/Xrender.h> |
| 10 #include <X11/extensions/Xcomposite.h> |
| 11 |
| 12 #include "media/base/buffers.h" |
| 13 #include "media/base/yuv_convert.h" |
| 14 |
| 15 GlesVideoRenderer* GlesVideoRenderer::instance_ = NULL; |
| 16 |
| 17 GlesVideoRenderer::GlesVideoRenderer(Display* display, Window window) |
| 18 : display_(display), |
| 19 window_(window), |
| 20 new_frame_(false), |
| 21 egl_display_(NULL), |
| 22 egl_surface_(NULL), |
| 23 egl_context_(NULL) { |
| 24 } |
| 25 |
| 26 GlesVideoRenderer::~GlesVideoRenderer() { |
| 27 } |
| 28 |
| 29 // static |
| 30 bool GlesVideoRenderer::IsMediaFormatSupported( |
| 31 const media::MediaFormat& media_format) { |
| 32 int width = 0; |
| 33 int height = 0; |
| 34 return ParseMediaFormat(media_format, &width, &height); |
| 35 } |
| 36 |
| 37 void GlesVideoRenderer::OnStop() { |
| 38 eglMakeCurrent(egl_display_, EGL_NO_SURFACE, |
| 39 EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| 40 eglDestroyContext(egl_display_, egl_context_); |
| 41 eglDestroySurface(egl_display_, egl_surface_); |
| 42 } |
| 43 |
| 44 // Matrix used for the YUV to RGB conversion. |
| 45 static const float kYUV2RGB[9] = { |
| 46 1.f, 0.f, 1.403f, |
| 47 1.f, -.344f, -.714f, |
| 48 1.f, 1.772f, 0.f, |
| 49 }; |
| 50 |
| 51 // Vertices for a full screen quad. |
| 52 static const float kVertices[8] = { |
| 53 -1.f, 1.f, |
| 54 -1.f, -1.f, |
| 55 1.f, 1.f, |
| 56 1.f, -1.f, |
| 57 }; |
| 58 |
| 59 // Texture Coordinates mapping the entire texture. |
| 60 static const float kTextureCoords[8] = { |
| 61 0, 0, |
| 62 0, 1, |
| 63 1, 0, |
| 64 1, 1, |
| 65 }; |
| 66 |
| 67 // Pass-through vertex shader. |
| 68 static const char kVertexShader[] = |
| 69 "precision highp float; precision highp int;\n" |
| 70 "varying vec2 interp_tc;\n" |
| 71 "\n" |
| 72 "attribute vec4 in_pos;\n" |
| 73 "attribute vec2 in_tc;\n" |
| 74 "\n" |
| 75 "void main() {\n" |
| 76 " interp_tc = in_tc;\n" |
| 77 " gl_Position = in_pos;\n" |
| 78 "}\n"; |
| 79 |
| 80 // YUV to RGB pixel shader. Loads a pixel from each plane and pass through the |
| 81 // matrix. |
| 82 static const char kFragmentShader[] = |
| 83 "precision mediump float; precision mediump int;\n" |
| 84 "varying vec2 interp_tc;\n" |
| 85 "\n" |
| 86 "uniform sampler2D y_tex;\n" |
| 87 "uniform sampler2D u_tex;\n" |
| 88 "uniform sampler2D v_tex;\n" |
| 89 "uniform mat3 yuv2rgb;\n" |
| 90 "\n" |
| 91 "void main() {\n" |
| 92 " float y = texture2D(y_tex, interp_tc).x;\n" |
| 93 " float u = texture2D(u_tex, interp_tc).r - .5;\n" |
| 94 " float v = texture2D(v_tex, interp_tc).r - .5;\n" |
| 95 " vec3 rgb = yuv2rgb * vec3(y, u, v);\n" |
| 96 " gl_FragColor = vec4(rgb, 1);\n" |
| 97 "}\n"; |
| 98 |
| 99 // Buffer size for compile errors. |
| 100 static const unsigned int kErrorSize = 4096; |
| 101 |
| 102 bool GlesVideoRenderer::OnInitialize(media::VideoDecoder* decoder) { |
| 103 if (!ParseMediaFormat(decoder->media_format(), &width_, &height_)) |
| 104 return false; |
| 105 |
| 106 LOG(INFO) << "Initializing GLES Renderer..."; |
| 107 |
| 108 // Resize the window to fit that of the video. |
| 109 XResizeWindow(display_, window_, width_, height_); |
| 110 |
| 111 egl_display_ = eglGetDisplay(display_); |
| 112 if (eglGetError() != EGL_SUCCESS) { |
| 113 DLOG(ERROR) << "eglGetDisplay failed."; |
| 114 return false; |
| 115 } |
| 116 |
| 117 EGLint major; |
| 118 EGLint minor; |
| 119 if (!eglInitialize(egl_display_, &major, &minor)) { |
| 120 DLOG(ERROR) << "eglInitialize failed."; |
| 121 return false; |
| 122 } |
| 123 DLOG(INFO) << "EGL vendor:" << eglQueryString(egl_display_, EGL_VENDOR); |
| 124 DLOG(INFO) << "EGL version:" << eglQueryString(egl_display_, EGL_VERSION); |
| 125 DLOG(INFO) << "EGL extensions:" |
| 126 << eglQueryString(egl_display_, EGL_EXTENSIONS); |
| 127 DLOG(INFO) << "EGL client apis:" |
| 128 << eglQueryString(egl_display_, EGL_CLIENT_APIS); |
| 129 |
| 130 EGLint attribs[] = { |
| 131 EGL_RED_SIZE, 5, |
| 132 EGL_GREEN_SIZE, 6, |
| 133 EGL_BLUE_SIZE, 5, |
| 134 EGL_DEPTH_SIZE, 16, |
| 135 EGL_STENCIL_SIZE, 0, |
| 136 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, |
| 137 EGL_NONE |
| 138 }; |
| 139 |
| 140 EGLint num_configs = -1; |
| 141 if (!eglGetConfigs(egl_display_, NULL, 0, &num_configs)) { |
| 142 DLOG(ERROR) << "eglGetConfigs failed."; |
| 143 return false; |
| 144 } |
| 145 |
| 146 EGLConfig config; |
| 147 if (!eglChooseConfig(egl_display_, attribs, &config, 1, &num_configs)) { |
| 148 DLOG(ERROR) << "eglChooseConfig failed."; |
| 149 return false; |
| 150 } |
| 151 |
| 152 EGLint red_size, green_size, blue_size, alpha_size, depth_size, stencil_size; |
| 153 eglGetConfigAttrib(egl_display_, config, EGL_RED_SIZE, &red_size); |
| 154 eglGetConfigAttrib(egl_display_, config, EGL_GREEN_SIZE, &green_size); |
| 155 eglGetConfigAttrib(egl_display_, config, EGL_BLUE_SIZE, &blue_size); |
| 156 eglGetConfigAttrib(egl_display_, config, EGL_ALPHA_SIZE, &alpha_size); |
| 157 eglGetConfigAttrib(egl_display_, config, EGL_DEPTH_SIZE, &depth_size); |
| 158 eglGetConfigAttrib(egl_display_, config, EGL_STENCIL_SIZE, &stencil_size); |
| 159 DLOG(INFO) << "R,G,B,A: " << red_size << "," << green_size |
| 160 << "," << blue_size << "," << alpha_size << " bits"; |
| 161 DLOG(INFO) << "Depth: " << depth_size << " bits, Stencil:" << stencil_size |
| 162 << "bits"; |
| 163 |
| 164 egl_surface_ = eglCreateWindowSurface(egl_display_, config, window_, NULL); |
| 165 if (!egl_surface_) { |
| 166 DLOG(ERROR) << "eglCreateWindowSurface failed."; |
| 167 return false; |
| 168 } |
| 169 |
| 170 egl_context_ = eglCreateContext(egl_display_, config, NULL, NULL); |
| 171 if (!egl_context_) { |
| 172 DLOG(ERROR) << "eglCreateContext failed."; |
| 173 eglDestroySurface(egl_display_, egl_surface_); |
| 174 return false; |
| 175 } |
| 176 |
| 177 if (eglMakeCurrent(egl_display_, egl_surface_, |
| 178 egl_surface_, egl_context_) == EGL_FALSE) { |
| 179 eglDestroyContext(egl_display_, egl_context_); |
| 180 eglDestroySurface(egl_display_, egl_surface_); |
| 181 egl_display_ = NULL; |
| 182 egl_surface_ = NULL; |
| 183 egl_context_ = NULL; |
| 184 return false; |
| 185 } |
| 186 |
| 187 EGLint width; |
| 188 EGLint height; |
| 189 eglQuerySurface(egl_display_, egl_surface_, EGL_WIDTH, &width); |
| 190 eglQuerySurface(egl_display_, egl_surface_, EGL_HEIGHT, &height); |
| 191 glViewport(0, 0, width, height); |
| 192 |
| 193 glViewport(0, 0, width_, height_); |
| 194 |
| 195 // Create 3 textures, one for each plane, and bind them to different |
| 196 // texture units. |
| 197 glGenTextures(media::VideoSurface::kNumYUVPlanes, textures_); |
| 198 glActiveTexture(GL_TEXTURE0); |
| 199 glBindTexture(GL_TEXTURE_2D, textures_[0]); |
| 200 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| 201 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| 202 glEnable(GL_TEXTURE_2D); |
| 203 |
| 204 glActiveTexture(GL_TEXTURE1); |
| 205 glBindTexture(GL_TEXTURE_2D, textures_[1]); |
| 206 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| 207 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| 208 glEnable(GL_TEXTURE_2D); |
| 209 |
| 210 glActiveTexture(GL_TEXTURE2); |
| 211 glBindTexture(GL_TEXTURE_2D, textures_[2]); |
| 212 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| 213 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| 214 glEnable(GL_TEXTURE_2D); |
| 215 |
| 216 GLuint program_ = glCreateProgram(); |
| 217 |
| 218 // Create our YUV->RGB shader. |
| 219 GLuint vertex_shader_ = glCreateShader(GL_VERTEX_SHADER); |
| 220 const char* vs_source = kVertexShader; |
| 221 int vs_size = sizeof(kVertexShader); |
| 222 glShaderSource(vertex_shader_, 1, &vs_source, &vs_size); |
| 223 glCompileShader(vertex_shader_); |
| 224 int result = GL_FALSE; |
| 225 glGetShaderiv(vertex_shader_, GL_COMPILE_STATUS, &result); |
| 226 if (!result) { |
| 227 char log[kErrorSize]; |
| 228 int len; |
| 229 glGetShaderInfoLog(vertex_shader_, kErrorSize - 1, &len, log); |
| 230 log[kErrorSize - 1] = 0; |
| 231 LOG(FATAL) << log; |
| 232 } |
| 233 glAttachShader(program_, vertex_shader_); |
| 234 |
| 235 GLuint fragment_shader_ = glCreateShader(GL_FRAGMENT_SHADER); |
| 236 const char* ps_source = kFragmentShader; |
| 237 int ps_size = sizeof(kFragmentShader); |
| 238 glShaderSource(fragment_shader_, 1, &ps_source, &ps_size); |
| 239 glCompileShader(fragment_shader_); |
| 240 result = GL_FALSE; |
| 241 glGetShaderiv(fragment_shader_, GL_COMPILE_STATUS, &result); |
| 242 if (!result) { |
| 243 char log[kErrorSize]; |
| 244 int len; |
| 245 glGetShaderInfoLog(fragment_shader_, kErrorSize - 1, &len, log); |
| 246 log[kErrorSize - 1] = 0; |
| 247 LOG(FATAL) << log; |
| 248 } |
| 249 glAttachShader(program_, fragment_shader_); |
| 250 |
| 251 glLinkProgram(program_); |
| 252 result = GL_FALSE; |
| 253 glGetProgramiv(program_, GL_LINK_STATUS, &result); |
| 254 if (!result) { |
| 255 char log[kErrorSize]; |
| 256 int len; |
| 257 glGetProgramInfoLog(program_, kErrorSize - 1, &len, log); |
| 258 log[kErrorSize - 1] = 0; |
| 259 LOG(FATAL) << log; |
| 260 } |
| 261 glUseProgram(program_); |
| 262 |
| 263 // Bind parameters. |
| 264 glUniform1i(glGetUniformLocation(program_, "y_tex"), 0); |
| 265 glUniform1i(glGetUniformLocation(program_, "u_tex"), 1); |
| 266 glUniform1i(glGetUniformLocation(program_, "v_tex"), 2); |
| 267 int yuv2rgb_location = glGetUniformLocation(program_, "yuv2rgb"); |
| 268 glUniformMatrix3fv(yuv2rgb_location, 1, GL_TRUE, kYUV2RGB); |
| 269 |
| 270 int pos_location = glGetAttribLocation(program_, "in_pos"); |
| 271 glEnableVertexAttribArray(pos_location); |
| 272 glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, 0, kVertices); |
| 273 |
| 274 int tc_location = glGetAttribLocation(program_, "in_tc"); |
| 275 glEnableVertexAttribArray(tc_location); |
| 276 glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0, |
| 277 kTextureCoords); |
| 278 |
| 279 // We are getting called on a thread. Release the context so that it can be |
| 280 // made current on the main thread. |
| 281 eglMakeCurrent(egl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| 282 |
| 283 // Save this instance. |
| 284 DCHECK(!instance_); |
| 285 instance_ = this; |
| 286 return true; |
| 287 } |
| 288 |
| 289 void GlesVideoRenderer::OnFrameAvailable() { |
| 290 AutoLock auto_lock(lock_); |
| 291 new_frame_ = true; |
| 292 } |
| 293 |
| 294 void GlesVideoRenderer::Paint() { |
| 295 // Use |new_frame_| to prevent overdraw since Paint() is called more |
| 296 // often than needed. It is OK to lock only this flag and we don't |
| 297 // want to lock the whole function because this method takes a long |
| 298 // time to complete. |
| 299 { |
| 300 AutoLock auto_lock(lock_); |
| 301 if (!new_frame_) |
| 302 return; |
| 303 new_frame_ = false; |
| 304 } |
| 305 |
| 306 scoped_refptr<media::VideoFrame> video_frame; |
| 307 GetCurrentFrame(&video_frame); |
| 308 |
| 309 if (!video_frame) |
| 310 return; |
| 311 |
| 312 // Convert YUV frame to RGB. |
| 313 media::VideoSurface frame_in; |
| 314 if (video_frame->Lock(&frame_in)) { |
| 315 DCHECK(frame_in.format == media::VideoSurface::YV12 || |
| 316 frame_in.format == media::VideoSurface::YV16); |
| 317 DCHECK(frame_in.strides[media::VideoSurface::kUPlane] == |
| 318 frame_in.strides[media::VideoSurface::kVPlane]); |
| 319 DCHECK(frame_in.planes == media::VideoSurface::kNumYUVPlanes); |
| 320 |
| 321 if (eglGetCurrentContext() != egl_context_) { |
| 322 eglMakeCurrent(egl_display_, egl_surface_, |
| 323 egl_surface_, egl_context_); |
| 324 } |
| 325 for (unsigned int i = 0; i < media::VideoSurface::kNumYUVPlanes; ++i) { |
| 326 unsigned int width = (i == media::VideoSurface::kYPlane) ? |
| 327 frame_in.width : frame_in.width / 2; |
| 328 unsigned int height = (i == media::VideoSurface::kYPlane || |
| 329 frame_in.format == media::VideoSurface::YV16) ? |
| 330 frame_in.height : frame_in.height / 2; |
| 331 glActiveTexture(GL_TEXTURE0 + i); |
| 332 // No GL_UNPACK_ROW_LENGTH in GLES2. |
| 333 // glPixelStorei(GL_UNPACK_ROW_LENGTH, frame_in.strides[i]); |
| 334 glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, |
| 335 GL_LUMINANCE, GL_UNSIGNED_BYTE, frame_in.data[i]); |
| 336 } |
| 337 video_frame->Unlock(); |
| 338 } else { |
| 339 NOTREACHED(); |
| 340 } |
| 341 |
| 342 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| 343 eglSwapBuffers(egl_display_, egl_surface_); |
| 344 } |
OLD | NEW |