Home | History | Annotate | Download | only in ios
      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 
     22 using namespace webrtc;
     23 
     24 const char OpenGles20::indices_[] = {0, 3, 2, 0, 2, 1};
     25 
     26 const 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.
     37 const 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 
     59 OpenGles20::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 
     76 OpenGles20::~OpenGles20() {
     77   if (program_) {
     78     glDeleteTextures(3, texture_ids_);
     79     glDeleteProgram(program_);
     80   }
     81 }
     82 
     83 bool 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 
    121 bool 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 
    154 bool 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 
    166 GLuint 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 
    196 GLuint 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 
    245 static 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 
    264 void 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.
    281 static 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 
    313 void 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