mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
233 lines
6.1 KiB
Objective-C
233 lines
6.1 KiB
Objective-C
#import "TGVideoCameraGLView.h"
|
|
#import <OpenGLES/EAGL.h>
|
|
#import <OpenGLES/ES2/glext.h>
|
|
#import <QuartzCore/CAEAGLLayer.h>
|
|
|
|
#import <LegacyComponents/TGPaintShader.h>
|
|
|
|
#import "LegacyComponentsInternal.h"
|
|
#import "TGVideoCameraPipeline.h"
|
|
|
|
@interface TGVideoCameraGLView ()
|
|
{
|
|
EAGLContext *_context;
|
|
CVOpenGLESTextureCacheRef _textureCache;
|
|
GLint _width;
|
|
GLint _height;
|
|
GLuint _framebuffer;
|
|
GLuint _colorbuffer;
|
|
|
|
TGPaintShader *_shader;
|
|
GLint _frame;
|
|
}
|
|
@end
|
|
|
|
@implementation TGVideoCameraGLView
|
|
|
|
+ (Class)layerClass
|
|
{
|
|
return [CAEAGLLayer class];
|
|
}
|
|
|
|
- (instancetype)initWithFrame:(CGRect)frame
|
|
{
|
|
self = [super initWithFrame:frame];
|
|
if (self != nil)
|
|
{
|
|
if (iosMajorVersion() >= 8)
|
|
self.contentScaleFactor = [UIScreen mainScreen].nativeScale;
|
|
else
|
|
self.contentScaleFactor = [UIScreen mainScreen].scale;
|
|
|
|
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
|
|
eaglLayer.opaque = true;
|
|
eaglLayer.drawableProperties = @{ kEAGLDrawablePropertyRetainedBacking : @false, kEAGLDrawablePropertyColorFormat : kEAGLColorFormatRGBA8 };
|
|
|
|
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
|
if (!_context)
|
|
return nil;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (bool)initializeBuffers
|
|
{
|
|
bool success = YES;
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
glGenFramebuffers(1, &_framebuffer);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
|
|
|
|
glGenRenderbuffers(1, &_colorbuffer );
|
|
glBindRenderbuffer(GL_RENDERBUFFER, _colorbuffer);
|
|
|
|
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
|
|
|
|
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_width);
|
|
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_height);
|
|
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorbuffer);
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
{
|
|
success = false;
|
|
goto bail;
|
|
}
|
|
|
|
|
|
CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, _context, NULL, &_textureCache);
|
|
if (err)
|
|
{
|
|
success = false;
|
|
goto bail;
|
|
}
|
|
|
|
_shader = [[TGPaintShader alloc] initWithVertexShader:@"Passthrough" fragmentShader:@"Passthrough" attributes:@[ @"inPosition", @"inTexcoord" ] uniforms:@[ @"texture" ]];
|
|
|
|
_frame = [_shader uniformForKey:@"texture"];
|
|
|
|
bail:
|
|
if ( ! success ) {
|
|
[self reset];
|
|
}
|
|
return success;
|
|
}
|
|
|
|
- (void)reset
|
|
{
|
|
EAGLContext *oldContext = [EAGLContext currentContext];
|
|
if (oldContext != _context)
|
|
{
|
|
if (![EAGLContext setCurrentContext:_context])
|
|
return;
|
|
}
|
|
|
|
if (_framebuffer)
|
|
{
|
|
glDeleteFramebuffers(1, &_framebuffer);
|
|
_framebuffer = 0;
|
|
}
|
|
|
|
if (_colorbuffer)
|
|
{
|
|
glDeleteRenderbuffers(1, &_colorbuffer);
|
|
_colorbuffer = 0;
|
|
}
|
|
|
|
if (_shader != nil)
|
|
{
|
|
[_shader cleanResources];
|
|
_shader = nil;
|
|
}
|
|
|
|
if (_textureCache)
|
|
{
|
|
CFRelease(_textureCache);
|
|
_textureCache = 0;
|
|
}
|
|
|
|
if (oldContext != _context)
|
|
[EAGLContext setCurrentContext:oldContext];
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[self reset];
|
|
}
|
|
|
|
- (void)displayPixelBuffer:(TGVideoCameraRendererBuffer *)pixelBuffer
|
|
{
|
|
static const GLfloat squareVertices[] =
|
|
{
|
|
-1.0f, -1.0f, // bottom left
|
|
1.0f, -1.0f, // bottom right
|
|
-1.0f, 1.0f, // top left
|
|
1.0f, 1.0f, // top right
|
|
};
|
|
|
|
if (pixelBuffer == NULL)
|
|
return;
|
|
|
|
EAGLContext *oldContext = [EAGLContext currentContext];
|
|
if (oldContext != _context)
|
|
{
|
|
if (![EAGLContext setCurrentContext:_context])
|
|
return;
|
|
}
|
|
|
|
if (_framebuffer == 0)
|
|
{
|
|
bool success = [self initializeBuffers];
|
|
if (!success)
|
|
return;
|
|
}
|
|
|
|
size_t frameWidth = CVPixelBufferGetWidth(pixelBuffer.buffer);
|
|
size_t frameHeight = CVPixelBufferGetHeight(pixelBuffer.buffer);
|
|
CVOpenGLESTextureRef texture = NULL;
|
|
CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _textureCache, pixelBuffer.buffer, NULL, GL_TEXTURE_2D, GL_RGBA, (GLsizei)frameWidth, (GLsizei)frameHeight, GL_BGRA, GL_UNSIGNED_BYTE, 0, &texture);
|
|
|
|
if (!texture || err)
|
|
return;
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
|
|
glViewport(0, 0, _width, _height);
|
|
|
|
glUseProgram(_shader.program);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(CVOpenGLESTextureGetTarget(texture), CVOpenGLESTextureGetName(texture));
|
|
glUniform1i(_frame, 0);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
glVertexAttribPointer(0, 2, GL_FLOAT, 0, 0, squareVertices);
|
|
glEnableVertexAttribArray(0);
|
|
|
|
CGSize textureSamplingSize;
|
|
CGSize cropScaleAmount = CGSizeMake(self.bounds.size.width / (CGFloat)frameWidth, self.bounds.size.height / (CGFloat)frameHeight);
|
|
if (cropScaleAmount.height > cropScaleAmount.width)
|
|
{
|
|
textureSamplingSize.width = self.bounds.size.width / ( frameWidth * cropScaleAmount.height );
|
|
textureSamplingSize.height = 1.0;
|
|
}
|
|
else
|
|
{
|
|
textureSamplingSize.width = 1.0;
|
|
textureSamplingSize.height = self.bounds.size.height / ( frameHeight * cropScaleAmount.width );
|
|
}
|
|
|
|
GLfloat passThroughTextureVertices[] =
|
|
{
|
|
(GLfloat)((1.0 - textureSamplingSize.width) / 2.0), (GLfloat)((1.0 + textureSamplingSize.height) / 2.0), // top left
|
|
(GLfloat)((1.0 + textureSamplingSize.width) / 2.0), (GLfloat)((1.0 + textureSamplingSize.height) / 2.0), // top right
|
|
(GLfloat)((1.0 - textureSamplingSize.width) / 2.0), (GLfloat)((1.0 - textureSamplingSize.height) / 2.0), // bottom left
|
|
(GLfloat)((1.0 + textureSamplingSize.width) / 2.0), (GLfloat)((1.0 - textureSamplingSize.height) / 2.0), // bottom right
|
|
};
|
|
|
|
glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, passThroughTextureVertices );
|
|
glEnableVertexAttribArray(1);
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
glBindRenderbuffer(GL_RENDERBUFFER, _colorbuffer);
|
|
[_context presentRenderbuffer:GL_RENDERBUFFER];
|
|
|
|
glBindTexture(CVOpenGLESTextureGetTarget(texture), 0);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
CFRelease(texture);
|
|
|
|
if (oldContext != _context)
|
|
[EAGLContext setCurrentContext:oldContext];
|
|
}
|
|
|
|
- (void)flushPixelBufferCache
|
|
{
|
|
if (_textureCache)
|
|
CVOpenGLESTextureCacheFlush(_textureCache, 0);
|
|
}
|
|
|
|
@end
|