1/* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#if !defined(__has_feature) || !__has_feature(objc_arc) 12#error "This file requires ARC support." 13#endif 14 15// This files is mostly copied from 16// webrtc/modules/video_render/android/video_render_opengles20.h 17 18// TODO(sjlee): unify this copy with the android one. 19#include "webrtc/modules/video_render/ios/open_gles20.h" 20#include "webrtc/system_wrappers/include/trace.h" 21 22using namespace webrtc; 23 24const char OpenGles20::indices_[] = {0, 3, 2, 0, 2, 1}; 25 26const char OpenGles20::vertext_shader_[] = { 27 "attribute vec4 aPosition;\n" 28 "attribute vec2 aTextureCoord;\n" 29 "varying vec2 vTextureCoord;\n" 30 "void main() {\n" 31 " gl_Position = aPosition;\n" 32 " vTextureCoord = aTextureCoord;\n" 33 "}\n"}; 34 35// The fragment shader. 36// Do YUV to RGB565 conversion. 37const char OpenGles20::fragment_shader_[] = { 38 "precision mediump float;\n" 39 "uniform sampler2D Ytex;\n" 40 "uniform sampler2D Utex,Vtex;\n" 41 "varying vec2 vTextureCoord;\n" 42 "void main(void) {\n" 43 " float nx,ny,r,g,b,y,u,v;\n" 44 " mediump vec4 txl,ux,vx;" 45 " nx=vTextureCoord[0];\n" 46 " ny=vTextureCoord[1];\n" 47 " y=texture2D(Ytex,vec2(nx,ny)).r;\n" 48 " u=texture2D(Utex,vec2(nx,ny)).r;\n" 49 " v=texture2D(Vtex,vec2(nx,ny)).r;\n" 50 " y=1.1643*(y-0.0625);\n" 51 " u=u-0.5;\n" 52 " v=v-0.5;\n" 53 " r=y+1.5958*v;\n" 54 " g=y-0.39173*u-0.81290*v;\n" 55 " b=y+2.017*u;\n" 56 " gl_FragColor=vec4(r,g,b,1.0);\n" 57 "}\n"}; 58 59OpenGles20::OpenGles20() : texture_width_(-1), texture_height_(-1) { 60 texture_ids_[0] = 0; 61 texture_ids_[1] = 0; 62 texture_ids_[2] = 0; 63 64 program_ = 0; 65 66 const GLfloat vertices[20] = { 67 // X, Y, Z, U, V 68 -1, -1, 0, 0, 1, // Bottom Left 69 1, -1, 0, 1, 1, // Bottom Right 70 1, 1, 0, 1, 0, // Top Right 71 -1, 1, 0, 0, 0}; // Top Left 72 73 memcpy(vertices_, vertices, sizeof(vertices_)); 74} 75 76OpenGles20::~OpenGles20() { 77 if (program_) { 78 glDeleteTextures(3, texture_ids_); 79 glDeleteProgram(program_); 80 } 81} 82 83bool OpenGles20::Setup(int32_t width, int32_t height) { 84 program_ = CreateProgram(vertext_shader_, fragment_shader_); 85 if (!program_) { 86 return false; 87 } 88 89 int position_handle = glGetAttribLocation(program_, "aPosition"); 90 int texture_handle = glGetAttribLocation(program_, "aTextureCoord"); 91 92 // set the vertices array in the shader 93 // vertices_ contains 4 vertices with 5 coordinates. 94 // 3 for (xyz) for the vertices and 2 for the texture 95 glVertexAttribPointer( 96 position_handle, 3, GL_FLOAT, false, 5 * sizeof(GLfloat), vertices_); 97 98 glEnableVertexAttribArray(position_handle); 99 100 // set the texture coordinate array in the shader 101 // vertices_ contains 4 vertices with 5 coordinates. 102 // 3 for (xyz) for the vertices and 2 for the texture 103 glVertexAttribPointer( 104 texture_handle, 2, GL_FLOAT, false, 5 * sizeof(GLfloat), &vertices_[3]); 105 glEnableVertexAttribArray(texture_handle); 106 107 glUseProgram(program_); 108 int i = glGetUniformLocation(program_, "Ytex"); 109 glUniform1i(i, 0); /* Bind Ytex to texture unit 0 */ 110 111 i = glGetUniformLocation(program_, "Utex"); 112 glUniform1i(i, 1); /* Bind Utex to texture unit 1 */ 113 114 i = glGetUniformLocation(program_, "Vtex"); 115 glUniform1i(i, 2); /* Bind Vtex to texture unit 2 */ 116 117 glViewport(0, 0, width, height); 118 return true; 119} 120 121bool OpenGles20::SetCoordinates(const float z_order, 122 const float left, 123 const float top, 124 const float right, 125 const float bottom) { 126 if (top > 1 || top < 0 || right > 1 || right < 0 || bottom > 1 || 127 bottom < 0 || left > 1 || left < 0) { 128 return false; 129 } 130 131 // Bottom Left 132 vertices_[0] = (left * 2) - 1; 133 vertices_[1] = -1 * (2 * bottom) + 1; 134 vertices_[2] = z_order; 135 136 // Bottom Right 137 vertices_[5] = (right * 2) - 1; 138 vertices_[6] = -1 * (2 * bottom) + 1; 139 vertices_[7] = z_order; 140 141 // Top Right 142 vertices_[10] = (right * 2) - 1; 143 vertices_[11] = -1 * (2 * top) + 1; 144 vertices_[12] = z_order; 145 146 // Top Left 147 vertices_[15] = (left * 2) - 1; 148 vertices_[16] = -1 * (2 * top) + 1; 149 vertices_[17] = z_order; 150 151 return true; 152} 153 154bool OpenGles20::Render(const VideoFrame& frame) { 155 if (texture_width_ != (GLsizei)frame.width() || 156 texture_height_ != (GLsizei)frame.height()) { 157 SetupTextures(frame); 158 } 159 UpdateTextures(frame); 160 161 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indices_); 162 163 return true; 164} 165 166GLuint OpenGles20::LoadShader(GLenum shader_type, const char* shader_source) { 167 GLuint shader = glCreateShader(shader_type); 168 if (shader) { 169 glShaderSource(shader, 1, &shader_source, NULL); 170 glCompileShader(shader); 171 172 GLint compiled = 0; 173 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); 174 if (!compiled) { 175 GLint info_len = 0; 176 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_len); 177 if (info_len) { 178 char* buf = (char*)malloc(info_len); 179 glGetShaderInfoLog(shader, info_len, NULL, buf); 180 WEBRTC_TRACE(kTraceError, 181 kTraceVideoRenderer, 182 0, 183 "%s: Could not compile shader %d: %s", 184 __FUNCTION__, 185 shader_type, 186 buf); 187 free(buf); 188 } 189 glDeleteShader(shader); 190 shader = 0; 191 } 192 } 193 return shader; 194} 195 196GLuint OpenGles20::CreateProgram(const char* vertex_source, 197 const char* fragment_source) { 198 GLuint vertex_shader = LoadShader(GL_VERTEX_SHADER, vertex_source); 199 if (!vertex_shader) { 200 return -1; 201 } 202 203 GLuint fragment_shader = LoadShader(GL_FRAGMENT_SHADER, fragment_source); 204 if (!fragment_shader) { 205 return -1; 206 } 207 208 GLuint program = glCreateProgram(); 209 if (program) { 210 glAttachShader(program, vertex_shader); 211 glAttachShader(program, fragment_shader); 212 glLinkProgram(program); 213 GLint link_status = GL_FALSE; 214 glGetProgramiv(program, GL_LINK_STATUS, &link_status); 215 if (link_status != GL_TRUE) { 216 GLint info_len = 0; 217 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_len); 218 if (info_len) { 219 char* buf = (char*)malloc(info_len); 220 glGetProgramInfoLog(program, info_len, NULL, buf); 221 WEBRTC_TRACE(kTraceError, 222 kTraceVideoRenderer, 223 0, 224 "%s: Could not link program: %s", 225 __FUNCTION__, 226 buf); 227 free(buf); 228 } 229 glDeleteProgram(program); 230 program = 0; 231 } 232 } 233 234 if (vertex_shader) { 235 glDeleteShader(vertex_shader); 236 } 237 238 if (fragment_shader) { 239 glDeleteShader(fragment_shader); 240 } 241 242 return program; 243} 244 245static void InitializeTexture(int name, int id, int width, int height) { 246 glActiveTexture(name); 247 glBindTexture(GL_TEXTURE_2D, id); 248 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 249 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 250 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 251 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 252 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 253 glTexImage2D(GL_TEXTURE_2D, 254 0, 255 GL_LUMINANCE, 256 width, 257 height, 258 0, 259 GL_LUMINANCE, 260 GL_UNSIGNED_BYTE, 261 NULL); 262} 263 264void OpenGles20::SetupTextures(const VideoFrame& frame) { 265 const GLsizei width = frame.width(); 266 const GLsizei height = frame.height(); 267 268 if (!texture_ids_[0]) { 269 glGenTextures(3, texture_ids_); // Generate the Y, U and V texture 270 } 271 272 InitializeTexture(GL_TEXTURE0, texture_ids_[0], width, height); 273 InitializeTexture(GL_TEXTURE1, texture_ids_[1], width / 2, height / 2); 274 InitializeTexture(GL_TEXTURE2, texture_ids_[2], width / 2, height / 2); 275 276 texture_width_ = width; 277 texture_height_ = height; 278} 279 280// Uploads a plane of pixel data, accounting for stride != width*bpp. 281static void GlTexSubImage2D(GLsizei width, 282 GLsizei height, 283 int stride, 284 const uint8_t* plane) { 285 if (stride == width) { 286 // Yay! We can upload the entire plane in a single GL call. 287 glTexSubImage2D(GL_TEXTURE_2D, 288 0, 289 0, 290 0, 291 width, 292 height, 293 GL_LUMINANCE, 294 GL_UNSIGNED_BYTE, 295 static_cast<const GLvoid*>(plane)); 296 } else { 297 // Boo! Since GLES2 doesn't have GL_UNPACK_ROW_LENGTH and iOS doesn't 298 // have GL_EXT_unpack_subimage we have to upload a row at a time. Ick. 299 for (int row = 0; row < height; ++row) { 300 glTexSubImage2D(GL_TEXTURE_2D, 301 0, 302 0, 303 row, 304 width, 305 1, 306 GL_LUMINANCE, 307 GL_UNSIGNED_BYTE, 308 static_cast<const GLvoid*>(plane + (row * stride))); 309 } 310 } 311} 312 313void OpenGles20::UpdateTextures(const VideoFrame& frame) { 314 const GLsizei width = frame.width(); 315 const GLsizei height = frame.height(); 316 317 glActiveTexture(GL_TEXTURE0); 318 glBindTexture(GL_TEXTURE_2D, texture_ids_[0]); 319 GlTexSubImage2D(width, height, frame.stride(kYPlane), frame.buffer(kYPlane)); 320 321 glActiveTexture(GL_TEXTURE1); 322 glBindTexture(GL_TEXTURE_2D, texture_ids_[1]); 323 GlTexSubImage2D( 324 width / 2, height / 2, frame.stride(kUPlane), frame.buffer(kUPlane)); 325 326 glActiveTexture(GL_TEXTURE2); 327 glBindTexture(GL_TEXTURE_2D, texture_ids_[2]); 328 GlTexSubImage2D( 329 width / 2, height / 2, frame.stride(kVPlane), frame.buffer(kVPlane)); 330} 331