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
26static 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