Home | History | Annotate | Download | only in objc
      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