1/* 2 * libjingle 3 * Copyright 2014 Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#if !defined(__has_feature) || !__has_feature(objc_arc) 29#error "This file requires ARC support." 30#endif 31 32#import "RTCNSGLVideoView.h" 33 34#import <CoreVideo/CVDisplayLink.h> 35#import <OpenGL/gl3.h> 36#import "RTCI420Frame.h" 37#import "RTCOpenGLVideoRenderer.h" 38 39@interface RTCNSGLVideoView () 40// |i420Frame| is set when we receive a frame from a worker thread and is read 41// from the display link callback so atomicity is required. 42@property(atomic, strong) RTCI420Frame* i420Frame; 43@property(atomic, strong) RTCOpenGLVideoRenderer* glRenderer; 44- (void)drawFrame; 45@end 46 47static CVReturn OnDisplayLinkFired(CVDisplayLinkRef displayLink, 48 const CVTimeStamp* now, 49 const CVTimeStamp* outputTime, 50 CVOptionFlags flagsIn, 51 CVOptionFlags* flagsOut, 52 void* displayLinkContext) { 53 RTCNSGLVideoView* view = (__bridge RTCNSGLVideoView*)displayLinkContext; 54 [view drawFrame]; 55 return kCVReturnSuccess; 56} 57 58@implementation RTCNSGLVideoView { 59 CVDisplayLinkRef _displayLink; 60} 61 62- (void)dealloc { 63 [self teardownDisplayLink]; 64} 65 66- (void)drawRect:(NSRect)rect { 67 [self drawFrame]; 68} 69 70- (void)reshape { 71 [super reshape]; 72 NSRect frame = [self frame]; 73 CGLLockContext([[self openGLContext] CGLContextObj]); 74 glViewport(0, 0, frame.size.width, frame.size.height); 75 CGLUnlockContext([[self openGLContext] CGLContextObj]); 76} 77 78- (void)lockFocus { 79 NSOpenGLContext* context = [self openGLContext]; 80 [super lockFocus]; 81 if ([context view] != self) { 82 [context setView:self]; 83 } 84 [context makeCurrentContext]; 85} 86 87- (void)prepareOpenGL { 88 [super prepareOpenGL]; 89 if (!self.glRenderer) { 90 self.glRenderer = 91 [[RTCOpenGLVideoRenderer alloc] initWithContext:[self openGLContext]]; 92 } 93 [self.glRenderer setupGL]; 94 [self setupDisplayLink]; 95} 96 97- (void)clearGLContext { 98 [self.glRenderer teardownGL]; 99 self.glRenderer = nil; 100 [super clearGLContext]; 101} 102 103#pragma mark - RTCVideoRenderer 104 105// These methods may be called on non-main thread. 106- (void)setSize:(CGSize)size { 107 dispatch_async(dispatch_get_main_queue(), ^{ 108 [self.delegate videoView:self didChangeVideoSize:size]; 109 }); 110} 111 112- (void)renderFrame:(RTCI420Frame*)frame { 113 self.i420Frame = frame; 114} 115 116#pragma mark - Private 117 118- (void)drawFrame { 119 RTCI420Frame* i420Frame = self.i420Frame; 120 if (self.glRenderer.lastDrawnFrame != i420Frame) { 121 // This method may be called from CVDisplayLink callback which isn't on the 122 // main thread so we have to lock the GL context before drawing. 123 CGLLockContext([[self openGLContext] CGLContextObj]); 124 [self.glRenderer drawFrame:i420Frame]; 125 CGLUnlockContext([[self openGLContext] CGLContextObj]); 126 } 127} 128 129- (void)setupDisplayLink { 130 if (_displayLink) { 131 return; 132 } 133 // Synchronize buffer swaps with vertical refresh rate. 134 GLint swapInt = 1; 135 [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; 136 137 // Create display link. 138 CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink); 139 CVDisplayLinkSetOutputCallback(_displayLink, 140 &OnDisplayLinkFired, 141 (__bridge void*)self); 142 // Set the display link for the current renderer. 143 CGLContextObj cglContext = [[self openGLContext] CGLContextObj]; 144 CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj]; 145 CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext( 146 _displayLink, cglContext, cglPixelFormat); 147 CVDisplayLinkStart(_displayLink); 148} 149 150- (void)teardownDisplayLink { 151 if (!_displayLink) { 152 return; 153 } 154 CVDisplayLinkRelease(_displayLink); 155 _displayLink = NULL; 156} 157 158@end 159