blob: 063e6f1330ed246e829378b33150201988831bf2 [file] [log] [blame]
Jon Hjellee799bad2016-01-11 13:47:11 -08001/*
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