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 "RTCOpenGLVideoRenderer.h" 37#import "RTCVideoRenderer.h" 38 39@interface RTCNSGLVideoView () <RTCVideoRendererDelegate> 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 RTCVideoRenderer* _videoRenderer; 61} 62 63- (instancetype)initWithFrame:(NSRect)frame 64 pixelFormat:(NSOpenGLPixelFormat*)format { 65 if (self = [super initWithFrame:frame pixelFormat:format]) { 66 _videoRenderer = [[RTCVideoRenderer alloc] initWithDelegate:self]; 67 } 68 return self; 69} 70 71- (void)dealloc { 72 [self teardownDisplayLink]; 73} 74 75- (void)drawRect:(NSRect)rect { 76 [self drawFrame]; 77} 78 79- (void)reshape { 80 [super reshape]; 81 NSRect frame = [self frame]; 82 CGLLockContext([[self openGLContext] CGLContextObj]); 83 glViewport(0, 0, frame.size.width, frame.size.height); 84 CGLUnlockContext([[self openGLContext] CGLContextObj]); 85} 86 87- (void)lockFocus { 88 NSOpenGLContext* context = [self openGLContext]; 89 [super lockFocus]; 90 if ([context view] != self) { 91 [context setView:self]; 92 } 93 [context makeCurrentContext]; 94} 95 96- (void)prepareOpenGL { 97 [super prepareOpenGL]; 98 if (!self.glRenderer) { 99 self.glRenderer = 100 [[RTCOpenGLVideoRenderer alloc] initWithContext:[self openGLContext]]; 101 } 102 [self.glRenderer setupGL]; 103 [self setupDisplayLink]; 104} 105 106- (void)clearGLContext { 107 [self.glRenderer teardownGL]; 108 self.glRenderer = nil; 109 [super clearGLContext]; 110} 111 112- (void)setVideoTrack:(RTCVideoTrack*)videoTrack { 113 if (_videoTrack == videoTrack) { 114 return; 115 } 116 if (_videoTrack) { 117 [_videoTrack removeRenderer:_videoRenderer]; 118 CVDisplayLinkStop(_displayLink); 119 // Clear contents. 120 self.i420Frame = nil; 121 [self drawFrame]; 122 } 123 _videoTrack = videoTrack; 124 if (_videoTrack) { 125 [_videoTrack addRenderer:_videoRenderer]; 126 CVDisplayLinkStart(_displayLink); 127 } 128} 129 130#pragma mark - RTCVideoRendererDelegate 131 132// These methods are called when the video track has frame information to 133// provide. This occurs on non-main thread. 134- (void)renderer:(RTCVideoRenderer*)renderer 135 didSetSize:(CGSize)size { 136 dispatch_async(dispatch_get_main_queue(), ^{ 137 [self.delegate videoView:self didChangeVideoSize:size]; 138 }); 139} 140 141- (void)renderer:(RTCVideoRenderer*)renderer 142 didReceiveFrame:(RTCI420Frame*)frame { 143 self.i420Frame = frame; 144} 145 146#pragma mark - Private 147 148- (void)drawFrame { 149 RTCI420Frame* i420Frame = self.i420Frame; 150 if (self.glRenderer.lastDrawnFrame != i420Frame) { 151 // This method may be called from CVDisplayLink callback which isn't on the 152 // main thread so we have to lock the GL context before drawing. 153 CGLLockContext([[self openGLContext] CGLContextObj]); 154 [self.glRenderer drawFrame:i420Frame]; 155 CGLUnlockContext([[self openGLContext] CGLContextObj]); 156 } 157} 158 159- (void)setupDisplayLink { 160 if (_displayLink) { 161 return; 162 } 163 // Synchronize buffer swaps with vertical refresh rate. 164 GLint swapInt = 1; 165 [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; 166 167 // Create display link. 168 CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink); 169 CVDisplayLinkSetOutputCallback(_displayLink, 170 &OnDisplayLinkFired, 171 (__bridge void*)self); 172 // Set the display link for the current renderer. 173 CGLContextObj cglContext = [[self openGLContext] CGLContextObj]; 174 CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj]; 175 CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext( 176 _displayLink, cglContext, cglPixelFormat); 177 if (_videoTrack) { 178 CVDisplayLinkStart(_displayLink); 179 } 180} 181 182- (void)teardownDisplayLink { 183 if (!_displayLink) { 184 return; 185 } 186 CVDisplayLinkRelease(_displayLink); 187 _displayLink = NULL; 188} 189 190@end 191