1 /* 2 * Copyright 2015 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 "RTCNSGLVideoView.h" 12 13 #import <CoreVideo/CVDisplayLink.h> 14 #import <OpenGL/gl3.h> 15 #import "RTCVideoFrame.h" 16 #import "RTCOpenGLVideoRenderer.h" 17 18 @interface RTCNSGLVideoView () 19 // |videoFrame| is set when we receive a frame from a worker thread and is read 20 // from the display link callback so atomicity is required. 21 @property(atomic, strong) RTCVideoFrame *videoFrame; 22 @property(atomic, strong) RTCOpenGLVideoRenderer *glRenderer; 23 - (void)drawFrame; 24 @end 25 26 static CVReturn OnDisplayLinkFired(CVDisplayLinkRef displayLink, 27 const CVTimeStamp *now, 28 const CVTimeStamp *outputTime, 29 CVOptionFlags flagsIn, 30 CVOptionFlags *flagsOut, 31 void *displayLinkContext) { 32 RTCNSGLVideoView *view = (__bridge RTCNSGLVideoView *)displayLinkContext; 33 [view drawFrame]; 34 return kCVReturnSuccess; 35 } 36 37 @implementation RTCNSGLVideoView { 38 CVDisplayLinkRef _displayLink; 39 } 40 41 @synthesize delegate = _delegate; 42 @synthesize videoFrame = _videoFrame; 43 @synthesize glRenderer = _glRenderer; 44 45 - (void)dealloc { 46 [self teardownDisplayLink]; 47 } 48 49 - (void)drawRect:(NSRect)rect { 50 [self drawFrame]; 51 } 52 53 - (void)reshape { 54 [super reshape]; 55 NSRect frame = [self frame]; 56 CGLLockContext([[self openGLContext] CGLContextObj]); 57 glViewport(0, 0, frame.size.width, frame.size.height); 58 CGLUnlockContext([[self openGLContext] CGLContextObj]); 59 } 60 61 - (void)lockFocus { 62 NSOpenGLContext *context = [self openGLContext]; 63 [super lockFocus]; 64 if ([context view] != self) { 65 [context setView:self]; 66 } 67 [context makeCurrentContext]; 68 } 69 70 - (void)prepareOpenGL { 71 [super prepareOpenGL]; 72 if (!self.glRenderer) { 73 self.glRenderer = 74 [[RTCOpenGLVideoRenderer alloc] initWithContext:[self openGLContext]]; 75 } 76 [self.glRenderer setupGL]; 77 [self setupDisplayLink]; 78 } 79 80 - (void)clearGLContext { 81 [self.glRenderer teardownGL]; 82 self.glRenderer = nil; 83 [super clearGLContext]; 84 } 85 86 #pragma mark - RTCVideoRenderer 87 88 // These methods may be called on non-main thread. 89 - (void)setSize:(CGSize)size { 90 dispatch_async(dispatch_get_main_queue(), ^{ 91 [self.delegate videoView:self didChangeVideoSize:size]; 92 }); 93 } 94 95 - (void)renderFrame:(RTCVideoFrame *)frame { 96 self.videoFrame = frame; 97 } 98 99 #pragma mark - Private 100 101 - (void)drawFrame { 102 RTCVideoFrame *videoFrame = self.videoFrame; 103 if (self.glRenderer.lastDrawnFrame != videoFrame) { 104 // This method may be called from CVDisplayLink callback which isn't on the 105 // main thread so we have to lock the GL context before drawing. 106 CGLLockContext([[self openGLContext] CGLContextObj]); 107 [self.glRenderer drawFrame:videoFrame]; 108 CGLUnlockContext([[self openGLContext] CGLContextObj]); 109 } 110 } 111 112 - (void)setupDisplayLink { 113 if (_displayLink) { 114 return; 115 } 116 // Synchronize buffer swaps with vertical refresh rate. 117 GLint swapInt = 1; 118 [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; 119 120 // Create display link. 121 CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink); 122 CVDisplayLinkSetOutputCallback(_displayLink, 123 &OnDisplayLinkFired, 124 (__bridge void *)self); 125 // Set the display link for the current renderer. 126 CGLContextObj cglContext = [[self openGLContext] CGLContextObj]; 127 CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj]; 128 CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext( 129 _displayLink, cglContext, cglPixelFormat); 130 CVDisplayLinkStart(_displayLink); 131 } 132 133 - (void)teardownDisplayLink { 134 if (!_displayLink) { 135 return; 136 } 137 CVDisplayLinkRelease(_displayLink); 138 _displayLink = NULL; 139 } 140 141 @end 142