1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/tools/player_x11/gl_video_renderer.h"
10 #include "base/message_loop/message_loop.h"
11 #include "media/base/buffers.h"
12 #include "media/base/video_frame.h"
13 #include "media/base/yuv_convert.h"
14 #include "ui/gl/gl_surface.h"
16 enum { kNumYUVPlanes
= 3 };
18 static GLXContext
InitGLContext(Display
* display
, Window window
) {
19 // Some versions of NVIDIA's GL libGL.so include a broken version of
20 // dlopen/dlsym, and so linking it into chrome breaks it. So we dynamically
21 // load it, and use glew to dynamically resolve symbols.
22 // See http://code.google.com/p/chromium/issues/detail?id=16800
23 if (!gfx::GLSurface::InitializeOneOff()) {
24 LOG(ERROR
) << "GLSurface::InitializeOneOff failed";
28 XWindowAttributes attributes
;
29 XGetWindowAttributes(display
, window
, &attributes
);
30 XVisualInfo visual_info_template
;
31 visual_info_template
.visualid
= XVisualIDFromVisual(attributes
.visual
);
32 int visual_info_count
= 0;
33 XVisualInfo
* visual_info_list
= XGetVisualInfo(display
, VisualIDMask
,
34 &visual_info_template
,
36 GLXContext context
= NULL
;
37 for (int i
= 0; i
< visual_info_count
&& !context
; ++i
) {
38 context
= glXCreateContext(display
, visual_info_list
+ i
, 0,
39 True
/* Direct rendering */);
42 XFree(visual_info_list
);
47 if (!glXMakeCurrent(display
, window
, context
)) {
48 glXDestroyContext(display
, context
);
55 // Matrix used for the YUV to RGB conversion.
56 static const float kYUV2RGB
[9] = {
62 // Vertices for a full screen quad.
63 static const float kVertices
[8] = {
70 // Pass-through vertex shader.
71 static const char kVertexShader
[] =
72 "varying vec2 interp_tc;\n"
74 "attribute vec4 in_pos;\n"
75 "attribute vec2 in_tc;\n"
78 " interp_tc = in_tc;\n"
79 " gl_Position = in_pos;\n"
82 // YUV to RGB pixel shader. Loads a pixel from each plane and pass through the
84 static const char kFragmentShader
[] =
85 "varying vec2 interp_tc;\n"
87 "uniform sampler2D y_tex;\n"
88 "uniform sampler2D u_tex;\n"
89 "uniform sampler2D v_tex;\n"
90 "uniform mat3 yuv2rgb;\n"
93 " float y = texture2D(y_tex, interp_tc).x;\n"
94 " float u = texture2D(u_tex, interp_tc).r - .5;\n"
95 " float v = texture2D(v_tex, interp_tc).r - .5;\n"
96 " vec3 rgb = yuv2rgb * vec3(y, u, v);\n"
97 " gl_FragColor = vec4(rgb, 1);\n"
100 // Buffer size for compile errors.
101 static const unsigned int kErrorSize
= 4096;
103 GlVideoRenderer::GlVideoRenderer(Display
* display
, Window window
)
109 GlVideoRenderer::~GlVideoRenderer() {
110 glXMakeCurrent(display_
, 0, NULL
);
111 glXDestroyContext(display_
, gl_context_
);
114 void GlVideoRenderer::Paint(
115 const scoped_refptr
<media::VideoFrame
>& video_frame
) {
117 Initialize(video_frame
->coded_size(), video_frame
->visible_rect());
119 // Convert YUV frame to RGB.
120 DCHECK(video_frame
->format() == media::VideoFrame::YV12
||
121 video_frame
->format() == media::VideoFrame::I420
||
122 video_frame
->format() == media::VideoFrame::YV16
);
123 DCHECK(video_frame
->stride(media::VideoFrame::kUPlane
) ==
124 video_frame
->stride(media::VideoFrame::kVPlane
));
126 if (glXGetCurrentContext() != gl_context_
||
127 glXGetCurrentDrawable() != window_
) {
128 glXMakeCurrent(display_
, window_
, gl_context_
);
130 for (unsigned int i
= 0; i
< kNumYUVPlanes
; ++i
) {
131 unsigned int width
= video_frame
->stride(i
);
132 unsigned int height
= video_frame
->rows(i
);
133 glActiveTexture(GL_TEXTURE0
+ i
);
134 glPixelStorei(GL_UNPACK_ROW_LENGTH
, video_frame
->stride(i
));
135 glTexImage2D(GL_TEXTURE_2D
, 0, GL_LUMINANCE
, width
, height
, 0,
136 GL_LUMINANCE
, GL_UNSIGNED_BYTE
, video_frame
->data(i
));
139 glDrawArrays(GL_TRIANGLE_STRIP
, 0, 4);
140 glXSwapBuffers(display_
, window_
);
143 void GlVideoRenderer::Initialize(gfx::Size coded_size
, gfx::Rect visible_rect
) {
145 VLOG(0) << "Initializing GL Renderer...";
147 // Resize the window to fit that of the video.
148 XResizeWindow(display_
, window_
, visible_rect
.width(), visible_rect
.height());
150 gl_context_
= InitGLContext(display_
, window_
);
151 CHECK(gl_context_
) << "Failed to initialize GL context";
153 // Create 3 textures, one for each plane, and bind them to different
155 glGenTextures(3, textures_
);
156 glActiveTexture(GL_TEXTURE0
);
157 glBindTexture(GL_TEXTURE_2D
, textures_
[0]);
158 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
159 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
160 glEnable(GL_TEXTURE_2D
);
162 glActiveTexture(GL_TEXTURE1
);
163 glBindTexture(GL_TEXTURE_2D
, textures_
[1]);
164 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
165 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
166 glEnable(GL_TEXTURE_2D
);
168 glActiveTexture(GL_TEXTURE2
);
169 glBindTexture(GL_TEXTURE_2D
, textures_
[2]);
170 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
171 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
172 glEnable(GL_TEXTURE_2D
);
174 GLuint program
= glCreateProgram();
176 // Create our YUV->RGB shader.
177 GLuint vertex_shader
= glCreateShader(GL_VERTEX_SHADER
);
178 const char* vs_source
= kVertexShader
;
179 int vs_size
= sizeof(kVertexShader
);
180 glShaderSource(vertex_shader
, 1, &vs_source
, &vs_size
);
181 glCompileShader(vertex_shader
);
182 int result
= GL_FALSE
;
183 glGetShaderiv(vertex_shader
, GL_COMPILE_STATUS
, &result
);
185 char log
[kErrorSize
];
187 glGetShaderInfoLog(vertex_shader
, kErrorSize
- 1, &len
, log
);
188 log
[kErrorSize
- 1] = 0;
191 glAttachShader(program
, vertex_shader
);
192 glDeleteShader(vertex_shader
);
194 GLuint fragment_shader
= glCreateShader(GL_FRAGMENT_SHADER
);
195 const char* ps_source
= kFragmentShader
;
196 int ps_size
= sizeof(kFragmentShader
);
197 glShaderSource(fragment_shader
, 1, &ps_source
, &ps_size
);
198 glCompileShader(fragment_shader
);
200 glGetShaderiv(fragment_shader
, GL_COMPILE_STATUS
, &result
);
202 char log
[kErrorSize
];
204 glGetShaderInfoLog(fragment_shader
, kErrorSize
- 1, &len
, log
);
205 log
[kErrorSize
- 1] = 0;
208 glAttachShader(program
, fragment_shader
);
209 glDeleteShader(fragment_shader
);
211 glLinkProgram(program
);
213 glGetProgramiv(program
, GL_LINK_STATUS
, &result
);
215 char log
[kErrorSize
];
217 glGetProgramInfoLog(program
, kErrorSize
- 1, &len
, log
);
218 log
[kErrorSize
- 1] = 0;
221 glUseProgram(program
);
222 glDeleteProgram(program
);
225 glUniform1i(glGetUniformLocation(program
, "y_tex"), 0);
226 glUniform1i(glGetUniformLocation(program
, "u_tex"), 1);
227 glUniform1i(glGetUniformLocation(program
, "v_tex"), 2);
228 int yuv2rgb_location
= glGetUniformLocation(program
, "yuv2rgb");
229 glUniformMatrix3fv(yuv2rgb_location
, 1, GL_TRUE
, kYUV2RGB
);
231 int pos_location
= glGetAttribLocation(program
, "in_pos");
232 glEnableVertexAttribArray(pos_location
);
233 glVertexAttribPointer(pos_location
, 2, GL_FLOAT
, GL_FALSE
, 0, kVertices
);
235 int tc_location
= glGetAttribLocation(program
, "in_tc");
236 glEnableVertexAttribArray(tc_location
);
238 float x0
= static_cast<float>(visible_rect
.x()) / coded_size
.width();
239 float y0
= static_cast<float>(visible_rect
.y()) / coded_size
.height();
240 float x1
= static_cast<float>(visible_rect
.right()) / coded_size
.width();
241 float y1
= static_cast<float>(visible_rect
.bottom()) / coded_size
.height();
242 verts
[0] = x0
; verts
[1] = y0
;
243 verts
[2] = x0
; verts
[3] = y1
;
244 verts
[4] = x1
; verts
[5] = y0
;
245 verts
[6] = x1
; verts
[7] = y1
;
246 glVertexAttribPointer(tc_location
, 2, GL_FLOAT
, GL_FALSE
, 0, verts
);
248 // We are getting called on a thread. Release the context so that it can be
249 // made current on the main thread.
250 glXMakeCurrent(display_
, 0, NULL
);