| OLD | NEW | 
|    1 /* |    1 /* | 
|    2  *  Copyright 2016 The WebRTC project authors. All Rights Reserved. |    2  *  Copyright 2016 The WebRTC project authors. All Rights Reserved. | 
|    3  * |    3  * | 
|    4  *  Use of this source code is governed by a BSD-style license |    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 |    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 |    6  *  tree. An additional intellectual property rights grant can be found | 
|    7  *  in the file PATENTS.  All contributing project authors may |    7  *  in the file PATENTS.  All contributing project authors may | 
|    8  *  be found in the AUTHORS file in the root of the source tree. |    8  *  be found in the AUTHORS file in the root of the source tree. | 
|    9  */ |    9  */ | 
|   10  |   10  | 
|   11 #import "RTCShader.h" |   11 #import "RTCShader.h" | 
|   12  |   12  | 
|   13 #include <vector> |   13 #import "RTCI420TextureCache.h" | 
|   14  |  | 
|   15 #import "RTCShader+Private.h" |   14 #import "RTCShader+Private.h" | 
|   16 #import "WebRTC/RTCLogging.h" |   15 #import "WebRTC/RTCLogging.h" | 
|   17 #import "WebRTC/RTCVideoFrame.h" |   16 #import "WebRTC/RTCVideoFrame.h" | 
|   18  |   17  | 
|   19 #include "webrtc/base/optional.h" |   18 #include "webrtc/base/optional.h" | 
|   20  |   19  | 
|   21 // |kNumTextures| must not exceed 8, which is the limit in OpenGLES2. Two sets |  | 
|   22 // of 3 textures are used here, one for each of the Y, U and V planes. Having |  | 
|   23 // two sets alleviates CPU blockage in the event that the GPU is asked to render |  | 
|   24 // to a texture that is already in use. |  | 
|   25 static const GLsizei kNumTextureSets = 2; |  | 
|   26 static const GLsizei kNumTexturesPerSet = 3; |  | 
|   27 static const GLsizei kNumTextures = kNumTexturesPerSet * kNumTextureSets; |  | 
|   28  |  | 
|   29 // Fragment shader converts YUV values from input textures into a final RGB |   20 // Fragment shader converts YUV values from input textures into a final RGB | 
|   30 // pixel. The conversion formula is from http://www.fourcc.org/fccyvrgb.php. |   21 // pixel. The conversion formula is from http://www.fourcc.org/fccyvrgb.php. | 
|   31 static const char kI420FragmentShaderSource[] = |   22 static const char kI420FragmentShaderSource[] = | 
|   32   SHADER_VERSION |   23   SHADER_VERSION | 
|   33   "precision highp float;" |   24   "precision highp float;" | 
|   34   FRAGMENT_SHADER_IN " vec2 v_texcoord;\n" |   25   FRAGMENT_SHADER_IN " vec2 v_texcoord;\n" | 
|   35   "uniform lowp sampler2D s_textureY;\n" |   26   "uniform lowp sampler2D s_textureY;\n" | 
|   36   "uniform lowp sampler2D s_textureU;\n" |   27   "uniform lowp sampler2D s_textureU;\n" | 
|   37   "uniform lowp sampler2D s_textureV;\n" |   28   "uniform lowp sampler2D s_textureV;\n" | 
|   38   FRAGMENT_SHADER_OUT |   29   FRAGMENT_SHADER_OUT | 
|   39   "void main() {\n" |   30   "void main() {\n" | 
|   40   "    float y, u, v, r, g, b;\n" |   31   "    float y, u, v, r, g, b;\n" | 
|   41   "    y = " FRAGMENT_SHADER_TEXTURE "(s_textureY, v_texcoord).r;\n" |   32   "    y = " FRAGMENT_SHADER_TEXTURE "(s_textureY, v_texcoord).r;\n" | 
|   42   "    u = " FRAGMENT_SHADER_TEXTURE "(s_textureU, v_texcoord).r;\n" |   33   "    u = " FRAGMENT_SHADER_TEXTURE "(s_textureU, v_texcoord).r;\n" | 
|   43   "    v = " FRAGMENT_SHADER_TEXTURE "(s_textureV, v_texcoord).r;\n" |   34   "    v = " FRAGMENT_SHADER_TEXTURE "(s_textureV, v_texcoord).r;\n" | 
|   44   "    u = u - 0.5;\n" |   35   "    u = u - 0.5;\n" | 
|   45   "    v = v - 0.5;\n" |   36   "    v = v - 0.5;\n" | 
|   46   "    r = y + 1.403 * v;\n" |   37   "    r = y + 1.403 * v;\n" | 
|   47   "    g = y - 0.344 * u - 0.714 * v;\n" |   38   "    g = y - 0.344 * u - 0.714 * v;\n" | 
|   48   "    b = y + 1.770 * u;\n" |   39   "    b = y + 1.770 * u;\n" | 
|   49   "    " FRAGMENT_SHADER_COLOR " = vec4(r, g, b, 1.0);\n" |   40   "    " FRAGMENT_SHADER_COLOR " = vec4(r, g, b, 1.0);\n" | 
|   50   "  }\n"; |   41   "  }\n"; | 
|   51  |   42  | 
|   52 @implementation RTCI420Shader { |   43 @implementation RTCI420Shader { | 
|   53   BOOL _hasUnpackRowLength; |   44   RTCI420TextureCache* textureCache; | 
|   54   GLint _currentTextureSet; |  | 
|   55   // Handles for OpenGL constructs. |   45   // Handles for OpenGL constructs. | 
|   56   GLuint _textures[kNumTextures]; |  | 
|   57   GLuint _i420Program; |   46   GLuint _i420Program; | 
|   58   GLuint _vertexArray; |   47   GLuint _vertexArray; | 
|   59   GLuint _vertexBuffer; |   48   GLuint _vertexBuffer; | 
|   60   GLint _ySampler; |   49   GLint _ySampler; | 
|   61   GLint _uSampler; |   50   GLint _uSampler; | 
|   62   GLint _vSampler; |   51   GLint _vSampler; | 
|   63   // Store current rotation and only upload new vertex data when rotation |   52   // Store current rotation and only upload new vertex data when rotation | 
|   64   // changes. |   53   // changes. | 
|   65   rtc::Optional<RTCVideoRotation> _currentRotation; |   54   rtc::Optional<RTCVideoRotation> _currentRotation; | 
|   66   // Used to create a non-padded plane for GPU upload when we receive padded |  | 
|   67   // frames. |  | 
|   68   std::vector<uint8_t> _planeBuffer; |  | 
|   69 } |   55 } | 
|   70  |   56  | 
|   71 - (instancetype)initWithContext:(GlContextType *)context { |   57 - (instancetype)initWithContext:(GlContextType *)context { | 
|   72   if (self = [super init]) { |   58   if (self = [super init]) { | 
|   73 #if TARGET_OS_IPHONE |   59     textureCache = [[RTCI420TextureCache alloc] initWithContext:context]; | 
|   74     _hasUnpackRowLength = (context.API == kEAGLRenderingAPIOpenGLES3); |  | 
|   75 #else |  | 
|   76     _hasUnpackRowLength = YES; |  | 
|   77 #endif |  | 
|   78     glPixelStorei(GL_UNPACK_ALIGNMENT, 1); |   60     glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | 
|   79     if (![self setupI420Program] || ![self setupTextures] || |   61     if (![self setupI420Program] || | 
|   80         !RTCSetupVerticesForProgram(_i420Program, &_vertexBuffer, &_vertexArray)
     ) { |   62         !RTCSetupVerticesForProgram(_i420Program, &_vertexBuffer, &_vertexArray)
     ) { | 
|   81       RTCLog(@"Failed to initialize RTCI420Shader."); |   63       RTCLog(@"Failed to initialize RTCI420Shader."); | 
|   82       self = nil; |   64       self = nil; | 
|   83     } |   65     } | 
|   84   } |   66   } | 
|   85   return self; |   67   return self; | 
|   86 } |   68 } | 
|   87  |   69  | 
|   88 - (void)dealloc { |   70 - (void)dealloc { | 
|   89   glDeleteProgram(_i420Program); |   71   glDeleteProgram(_i420Program); | 
|   90   glDeleteTextures(kNumTextures, _textures); |  | 
|   91   glDeleteBuffers(1, &_vertexBuffer); |   72   glDeleteBuffers(1, &_vertexBuffer); | 
|   92   glDeleteVertexArrays(1, &_vertexArray); |   73   glDeleteVertexArrays(1, &_vertexArray); | 
|   93 } |   74 } | 
|   94  |   75  | 
|   95 - (BOOL)setupI420Program { |   76 - (BOOL)setupI420Program { | 
|   96   _i420Program = RTCCreateProgramFromFragmentSource(kI420FragmentShaderSource); |   77   _i420Program = RTCCreateProgramFromFragmentSource(kI420FragmentShaderSource); | 
|   97   if (!_i420Program) { |   78   if (!_i420Program) { | 
|   98     return NO; |   79     return NO; | 
|   99   } |   80   } | 
|  100   _ySampler = glGetUniformLocation(_i420Program, "s_textureY"); |   81   _ySampler = glGetUniformLocation(_i420Program, "s_textureY"); | 
|  101   _uSampler = glGetUniformLocation(_i420Program, "s_textureU"); |   82   _uSampler = glGetUniformLocation(_i420Program, "s_textureU"); | 
|  102   _vSampler = glGetUniformLocation(_i420Program, "s_textureV"); |   83   _vSampler = glGetUniformLocation(_i420Program, "s_textureV"); | 
|  103  |   84  | 
|  104   return (_ySampler >= 0 && _uSampler >= 0 && _vSampler >= 0); |   85   return (_ySampler >= 0 && _uSampler >= 0 && _vSampler >= 0); | 
|  105 } |   86 } | 
|  106  |   87  | 
|  107 - (BOOL)setupTextures { |  | 
|  108   glGenTextures(kNumTextures, _textures); |  | 
|  109   // Set parameters for each of the textures we created. |  | 
|  110   for (GLsizei i = 0; i < kNumTextures; i++) { |  | 
|  111     glBindTexture(GL_TEXTURE_2D, _textures[i]); |  | 
|  112     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |  | 
|  113     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |  | 
|  114     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |  | 
|  115     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |  | 
|  116   } |  | 
|  117   return YES; |  | 
|  118 } |  | 
|  119  |  | 
|  120 - (BOOL)drawFrame:(RTCVideoFrame*)frame { |   88 - (BOOL)drawFrame:(RTCVideoFrame*)frame { | 
|  121   glUseProgram(_i420Program); |   89   glUseProgram(_i420Program); | 
|  122   if (![self updateTextureDataForFrame:frame]) { |   90  | 
|  123     return NO; |   91   [textureCache uploadFrameToTextures:frame]; | 
|  124   } |   92  | 
|  125 #if !TARGET_OS_IPHONE |   93 #if !TARGET_OS_IPHONE | 
|  126   glBindVertexArray(_vertexArray); |   94   glBindVertexArray(_vertexArray); | 
|  127 #endif |   95 #endif | 
 |   96  | 
 |   97   glActiveTexture(GL_TEXTURE0); | 
 |   98   glBindTexture(GL_TEXTURE_2D, textureCache.yTexture); | 
 |   99   glUniform1i(_ySampler, 0); | 
 |  100  | 
 |  101   glActiveTexture(GL_TEXTURE1); | 
 |  102   glBindTexture(GL_TEXTURE_2D, textureCache.uTexture); | 
 |  103   glUniform1i(_uSampler, 1); | 
 |  104  | 
 |  105   glActiveTexture(GL_TEXTURE2); | 
 |  106   glBindTexture(GL_TEXTURE_2D, textureCache.vTexture); | 
 |  107   glUniform1i(_vSampler, 2); | 
 |  108  | 
|  128   glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); |  109   glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); | 
|  129   if (!_currentRotation || frame.rotation != *_currentRotation) { |  110   if (!_currentRotation || frame.rotation != *_currentRotation) { | 
|  130     _currentRotation = rtc::Optional<RTCVideoRotation>(frame.rotation); |  111     _currentRotation = rtc::Optional<RTCVideoRotation>(frame.rotation); | 
|  131     RTCSetVertexData(*_currentRotation); |  112     RTCSetVertexData(*_currentRotation); | 
|  132   } |  113   } | 
|  133   glDrawArrays(GL_TRIANGLE_FAN, 0, 4); |  114   glDrawArrays(GL_TRIANGLE_FAN, 0, 4); | 
|  134  |  115  | 
|  135   return YES; |  116   return YES; | 
|  136 } |  117 } | 
|  137  |  118  | 
|  138 - (void)uploadPlane:(const uint8_t *)plane |  | 
|  139             sampler:(GLint)sampler |  | 
|  140              offset:(GLint)offset |  | 
|  141               width:(size_t)width |  | 
|  142              height:(size_t)height |  | 
|  143              stride:(int32_t)stride { |  | 
|  144   glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + offset)); |  | 
|  145   glBindTexture(GL_TEXTURE_2D, _textures[offset]); |  | 
|  146  |  | 
|  147   // When setting texture sampler uniforms, the texture index is used not |  | 
|  148   // the texture handle. |  | 
|  149   glUniform1i(sampler, offset); |  | 
|  150   const uint8_t *uploadPlane = plane; |  | 
|  151   if ((size_t)stride != width) { |  | 
|  152    if (_hasUnpackRowLength) { |  | 
|  153       // GLES3 allows us to specify stride. |  | 
|  154       glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); |  | 
|  155       glTexImage2D(GL_TEXTURE_2D, |  | 
|  156                    0, |  | 
|  157                    RTC_PIXEL_FORMAT, |  | 
|  158                    static_cast<GLsizei>(width), |  | 
|  159                    static_cast<GLsizei>(height), |  | 
|  160                    0, |  | 
|  161                    RTC_PIXEL_FORMAT, |  | 
|  162                    GL_UNSIGNED_BYTE, |  | 
|  163                    uploadPlane); |  | 
|  164       glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |  | 
|  165       return; |  | 
|  166     } else { |  | 
|  167       // Make an unpadded copy and upload that instead. Quick profiling showed |  | 
|  168       // that this is faster than uploading row by row using glTexSubImage2D. |  | 
|  169       uint8_t *unpaddedPlane = _planeBuffer.data(); |  | 
|  170       for (size_t y = 0; y < height; ++y) { |  | 
|  171         memcpy(unpaddedPlane + y * width, plane + y * stride, width); |  | 
|  172       } |  | 
|  173       uploadPlane = unpaddedPlane; |  | 
|  174     } |  | 
|  175   } |  | 
|  176   glTexImage2D(GL_TEXTURE_2D, |  | 
|  177                0, |  | 
|  178                RTC_PIXEL_FORMAT, |  | 
|  179                static_cast<GLsizei>(width), |  | 
|  180                static_cast<GLsizei>(height), |  | 
|  181                0, |  | 
|  182                RTC_PIXEL_FORMAT, |  | 
|  183                GL_UNSIGNED_BYTE, |  | 
|  184                uploadPlane); |  | 
|  185 } |  | 
|  186  |  | 
|  187 - (BOOL)updateTextureDataForFrame:(RTCVideoFrame *)frame { |  | 
|  188   GLint textureOffset = _currentTextureSet * 3; |  | 
|  189   NSAssert(textureOffset + 3 <= kNumTextures, @"invalid offset"); |  | 
|  190  |  | 
|  191   const int chromaWidth = (frame.width + 1) / 2; |  | 
|  192   const int chromaHeight = (frame.height + 1) / 2; |  | 
|  193   if (frame.strideY != frame.width || |  | 
|  194       frame.strideU != chromaWidth || |  | 
|  195       frame.strideV != chromaWidth) { |  | 
|  196     _planeBuffer.resize(frame.width * frame.height); |  | 
|  197   } |  | 
|  198  |  | 
|  199   [self uploadPlane:frame.dataY |  | 
|  200             sampler:_ySampler |  | 
|  201              offset:textureOffset |  | 
|  202               width:frame.width |  | 
|  203              height:frame.height |  | 
|  204              stride:frame.strideY]; |  | 
|  205  |  | 
|  206   [self uploadPlane:frame.dataU |  | 
|  207             sampler:_uSampler |  | 
|  208              offset:textureOffset + 1 |  | 
|  209               width:chromaWidth |  | 
|  210              height:chromaHeight |  | 
|  211              stride:frame.strideU]; |  | 
|  212  |  | 
|  213   [self uploadPlane:frame.dataV |  | 
|  214             sampler:_vSampler |  | 
|  215              offset:textureOffset + 2 |  | 
|  216               width:chromaWidth |  | 
|  217              height:chromaHeight |  | 
|  218              stride:frame.strideV]; |  | 
|  219  |  | 
|  220   _currentTextureSet = (_currentTextureSet + 1) % kNumTextureSets; |  | 
|  221   return YES; |  | 
|  222 } |  | 
|  223  |  | 
|  224 @end |  119 @end | 
| OLD | NEW |