1// Copyright 2016-present 650 Industries. All rights reserved.
2
3#import <AVKit/AVKit.h>
4
5#include <OpenGLES/ES3/gl.h>
6#include <OpenGLES/ES3/glext.h>
7
8#import <ExpoModulesCore/EXCameraInterface.h>
9
10#import <ExpoGL/EXGLCameraObject.h>
11#import <ExpoGL/EXGLContext.h>
12
13@interface EXGLCameraObject () <AVCaptureVideoDataOutputSampleBufferDelegate>
14
15@property (nonatomic, strong) id<EXCameraInterface> camera;
16@property (nonatomic, strong) EAGLContext *eaglCtx;
17@property (nonatomic, strong) AVCaptureVideoDataOutput *cameraOutput;
18@property (nonatomic, assign) CVOpenGLESTextureCacheRef cameraTextureCache;
19
20@end
21
22@implementation EXGLCameraObject
23
24- (instancetype)initWithContext:(EXGLContext *)glContext andCamera:(id<EXCameraInterface>)camera
25{
26  EXGLContextId exglCtxId = [glContext contextId];
27
28  if (self = [super initWithConfig:@{ @"exglCtxId": @(exglCtxId) }]) {
29    _eaglCtx = [glContext createSharedEAGLContext];
30    _camera = camera;
31
32    dispatch_async(camera.sessionQueue, ^{
33      AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init];
34
35      if ([camera.session canAddOutput:videoOutput]) {
36        videoOutput.videoSettings = @{ (id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA) };
37        [videoOutput setSampleBufferDelegate:self queue:camera.sessionQueue];
38        [videoOutput setAlwaysDiscardsLateVideoFrames:YES];
39        [camera.session addOutput:videoOutput];
40        self->_cameraOutput = videoOutput;
41      }
42    });
43  }
44  return self;
45}
46
47- (void)dealloc
48{
49  if (_cameraOutput) {
50    [_camera.session removeOutput:_cameraOutput];
51    [_cameraOutput setSampleBufferDelegate:nil queue:nil];
52  }
53}
54
55- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
56{
57  [connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
58  [connection setVideoMirrored:YES];
59
60  CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
61  GLsizei bufferWidth = (GLsizei)CVPixelBufferGetWidth(pixelBuffer);
62  GLsizei bufferHeight = (GLsizei)CVPixelBufferGetHeight(pixelBuffer);
63
64  CVPixelBufferRetain(pixelBuffer);
65  CVPixelBufferLockBaseAddress(pixelBuffer, 0);
66
67  [EAGLContext setCurrentContext:_eaglCtx];
68
69  if (!_cameraTextureCache) {
70    CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, _eaglCtx, NULL, &_cameraTextureCache);
71  }
72
73  CVOpenGLESTextureRef textureRef = NULL;
74
75  CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
76                                               _cameraTextureCache,
77                                               pixelBuffer,
78                                               NULL,
79                                               GL_TEXTURE_2D,
80                                               GL_RGBA,
81                                               bufferWidth,
82                                               bufferHeight,
83                                               GL_BGRA,
84                                               GL_UNSIGNED_BYTE,
85                                               0,
86                                               &textureRef);
87
88  if (textureRef) {
89    GLuint textureName = CVOpenGLESTextureGetName(textureRef);
90    EXGLContextMapObject([self exglCtxId], [self exglObjId], textureName);
91  }
92
93  CVOpenGLESTextureCacheFlush(_cameraTextureCache, 0);
94  CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
95  CVPixelBufferRelease(pixelBuffer);
96  CFRelease(textureRef);
97
98  [EAGLContext setCurrentContext:nil];
99}
100
101@end
102