blob: 2d402eef14fc8fc91df6cc893e8808d97cb630ae [file] [log] [blame]
tkchin04dbb342016-08-08 03:10:07 -07001/*
2 * Copyright 2016 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#import "RTCShader.h"
12
13// Native CVPixelBufferRef rendering is only supported on iPhone because it
14// depends on CVOpenGLESTextureCacheCreate.
15#if TARGET_OS_IPHONE
16
17#import <CoreVideo/CVOpenGLESTextureCache.h>
18
19#import "RTCShader+Private.h"
20#import "WebRTC/RTCVideoFrame.h"
21
22#include "webrtc/base/checks.h"
magjedfb372f02016-08-10 07:58:29 -070023#include "webrtc/base/optional.h"
tkchin04dbb342016-08-08 03:10:07 -070024
25static const char kNV12FragmentShaderSource[] =
26 SHADER_VERSION
27 "precision mediump float;"
28 FRAGMENT_SHADER_IN " vec2 v_texcoord;\n"
29 "uniform lowp sampler2D s_textureY;\n"
30 "uniform lowp sampler2D s_textureUV;\n"
31 FRAGMENT_SHADER_OUT
32 "void main() {\n"
33 " mediump float y;\n"
34 " mediump vec2 uv;\n"
35 " y = " FRAGMENT_SHADER_TEXTURE "(s_textureY, v_texcoord).r;\n"
36 " uv = " FRAGMENT_SHADER_TEXTURE "(s_textureUV, v_texcoord).ra -\n"
37 " vec2(0.5, 0.5);\n"
38 " " FRAGMENT_SHADER_COLOR " = vec4(y + 1.403 * uv.y,\n"
39 " y - 0.344 * uv.x - 0.714 * uv.y,\n"
40 " y + 1.770 * uv.x,\n"
41 " 1.0);\n"
42 " }\n";
43
44@implementation RTCNativeNV12Shader {
45 GLuint _vertexBuffer;
46 GLuint _nv12Program;
47 GLint _ySampler;
48 GLint _uvSampler;
49 CVOpenGLESTextureCacheRef _textureCache;
magjedfb372f02016-08-10 07:58:29 -070050 // Store current rotation and only upload new vertex data when rotation
51 // changes.
magjed7ee51252017-02-21 04:19:46 -080052 rtc::Optional<RTCVideoRotation> _currentRotation;
tkchin04dbb342016-08-08 03:10:07 -070053}
54
55- (instancetype)initWithContext:(GlContextType *)context {
56 if (self = [super init]) {
57 if (![self setupNV12Program] || ![self setupTextureCacheWithContext:context] ||
58 !RTCSetupVerticesForProgram(_nv12Program, &_vertexBuffer, nullptr)) {
59 self = nil;
60 }
61 }
62 return self;
63}
64
65- (void)dealloc {
66 glDeleteProgram(_nv12Program);
67 glDeleteBuffers(1, &_vertexBuffer);
68 if (_textureCache) {
69 CFRelease(_textureCache);
70 _textureCache = nullptr;
71 }
72}
73
74- (BOOL)setupNV12Program {
75 _nv12Program = RTCCreateProgramFromFragmentSource(kNV12FragmentShaderSource);
76 if (!_nv12Program) {
77 return NO;
78 }
79 _ySampler = glGetUniformLocation(_nv12Program, "s_textureY");
80 _uvSampler = glGetUniformLocation(_nv12Program, "s_textureUV");
81
82 return (_ySampler >= 0 && _uvSampler >= 0);
83}
84
85- (BOOL)setupTextureCacheWithContext:(GlContextType *)context {
86 CVReturn ret = CVOpenGLESTextureCacheCreate(
87 kCFAllocatorDefault, NULL,
88#if COREVIDEO_USE_EAGLCONTEXT_CLASS_IN_API
89 context,
90#else
91 (__bridge void *)context,
92#endif
93 NULL, &_textureCache);
94 return ret == kCVReturnSuccess;
95}
96
97- (BOOL)drawFrame:(RTCVideoFrame *)frame {
98 CVPixelBufferRef pixelBuffer = frame.nativeHandle;
99 RTC_CHECK(pixelBuffer);
100 glUseProgram(_nv12Program);
101 const OSType pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer);
102 RTC_CHECK(pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange ||
103 pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)
104 << "Unsupported native pixel format: " << pixelFormat;
105
106 // Y-plane.
107 const int lumaWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
108 const int lumaHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
109
110 CVOpenGLESTextureRef lumaTexture = nullptr;
111 glActiveTexture(GL_TEXTURE0);
112 glUniform1i(_ySampler, 0);
113 CVReturn ret = CVOpenGLESTextureCacheCreateTextureFromImage(
114 kCFAllocatorDefault, _textureCache, pixelBuffer, NULL, GL_TEXTURE_2D,
115 RTC_PIXEL_FORMAT, lumaWidth, lumaHeight, RTC_PIXEL_FORMAT,
116 GL_UNSIGNED_BYTE, 0, &lumaTexture);
117 if (ret != kCVReturnSuccess) {
118 CFRelease(lumaTexture);
119 return NO;
120 }
121
122 RTC_CHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
123 CVOpenGLESTextureGetTarget(lumaTexture));
124 glBindTexture(GL_TEXTURE_2D, CVOpenGLESTextureGetName(lumaTexture));
125 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
126 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
127 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
128 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
129
130 // UV-plane.
131 const int chromaWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
132 const int chromeHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
133
134 CVOpenGLESTextureRef chromaTexture = nullptr;
135 glActiveTexture(GL_TEXTURE1);
136 glUniform1i(_uvSampler, 1);
137 ret = CVOpenGLESTextureCacheCreateTextureFromImage(
138 kCFAllocatorDefault, _textureCache, pixelBuffer, NULL, GL_TEXTURE_2D,
139 GL_LUMINANCE_ALPHA, chromaWidth, chromeHeight, GL_LUMINANCE_ALPHA,
140 GL_UNSIGNED_BYTE, 1, &chromaTexture);
141 if (ret != kCVReturnSuccess) {
142 CFRelease(chromaTexture);
143 CFRelease(lumaTexture);
144 return NO;
145 }
146
147 RTC_CHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
148 CVOpenGLESTextureGetTarget(chromaTexture));
149 glBindTexture(GL_TEXTURE_2D, CVOpenGLESTextureGetName(chromaTexture));
150 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
151 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
152 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
153 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
154
155 glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
magjedfb372f02016-08-10 07:58:29 -0700156 if (!_currentRotation || frame.rotation != *_currentRotation) {
magjed7ee51252017-02-21 04:19:46 -0800157 _currentRotation = rtc::Optional<RTCVideoRotation>(frame.rotation);
magjedfb372f02016-08-10 07:58:29 -0700158 RTCSetVertexData(*_currentRotation);
159 }
tkchin04dbb342016-08-08 03:10:07 -0700160 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
161
162 CFRelease(chromaTexture);
163 CFRelease(lumaTexture);
164
165 return YES;
166}
167
168@end
169#endif // TARGET_OS_IPHONE