blob: 415efe82282b42df8cf9a4a75ded00ad13df99a7 [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
tkchin9eeb6242016-04-27 01:54:20 -070011#if !TARGET_OS_IPHONE
12
13#import "WebRTC/RTCNSGLVideoView.h"
Jon Hjellee799bad2016-01-11 13:47:11 -080014
15#import <CoreVideo/CVDisplayLink.h>
16#import <OpenGL/gl3.h>
tkchin9eeb6242016-04-27 01:54:20 -070017
Jon Hjellee799bad2016-01-11 13:47:11 -080018#import "RTCOpenGLVideoRenderer.h"
tkchin9eeb6242016-04-27 01:54:20 -070019#import "WebRTC/RTCVideoFrame.h"
Jon Hjellee799bad2016-01-11 13:47:11 -080020
21@interface RTCNSGLVideoView ()
22// |videoFrame| is set when we receive a frame from a worker thread and is read
23// from the display link callback so atomicity is required.
24@property(atomic, strong) RTCVideoFrame *videoFrame;
25@property(atomic, strong) RTCOpenGLVideoRenderer *glRenderer;
26- (void)drawFrame;
27@end
28
29static CVReturn OnDisplayLinkFired(CVDisplayLinkRef displayLink,
30 const CVTimeStamp *now,
31 const CVTimeStamp *outputTime,
32 CVOptionFlags flagsIn,
33 CVOptionFlags *flagsOut,
34 void *displayLinkContext) {
35 RTCNSGLVideoView *view = (__bridge RTCNSGLVideoView *)displayLinkContext;
36 [view drawFrame];
37 return kCVReturnSuccess;
38}
39
40@implementation RTCNSGLVideoView {
41 CVDisplayLinkRef _displayLink;
42}
43
44@synthesize delegate = _delegate;
45@synthesize videoFrame = _videoFrame;
46@synthesize glRenderer = _glRenderer;
47
48- (void)dealloc {
49 [self teardownDisplayLink];
50}
51
52- (void)drawRect:(NSRect)rect {
53 [self drawFrame];
54}
55
56- (void)reshape {
57 [super reshape];
58 NSRect frame = [self frame];
59 CGLLockContext([[self openGLContext] CGLContextObj]);
60 glViewport(0, 0, frame.size.width, frame.size.height);
61 CGLUnlockContext([[self openGLContext] CGLContextObj]);
62}
63
64- (void)lockFocus {
65 NSOpenGLContext *context = [self openGLContext];
66 [super lockFocus];
67 if ([context view] != self) {
68 [context setView:self];
69 }
70 [context makeCurrentContext];
71}
72
73- (void)prepareOpenGL {
74 [super prepareOpenGL];
75 if (!self.glRenderer) {
76 self.glRenderer =
77 [[RTCOpenGLVideoRenderer alloc] initWithContext:[self openGLContext]];
78 }
79 [self.glRenderer setupGL];
80 [self setupDisplayLink];
81}
82
83- (void)clearGLContext {
84 [self.glRenderer teardownGL];
85 self.glRenderer = nil;
86 [super clearGLContext];
87}
88
89#pragma mark - RTCVideoRenderer
90
91// These methods may be called on non-main thread.
92- (void)setSize:(CGSize)size {
93 dispatch_async(dispatch_get_main_queue(), ^{
94 [self.delegate videoView:self didChangeVideoSize:size];
95 });
96}
97
98- (void)renderFrame:(RTCVideoFrame *)frame {
99 self.videoFrame = frame;
100}
101
102#pragma mark - Private
103
104- (void)drawFrame {
105 RTCVideoFrame *videoFrame = self.videoFrame;
106 if (self.glRenderer.lastDrawnFrame != videoFrame) {
107 // This method may be called from CVDisplayLink callback which isn't on the
108 // main thread so we have to lock the GL context before drawing.
109 CGLLockContext([[self openGLContext] CGLContextObj]);
110 [self.glRenderer drawFrame:videoFrame];
111 CGLUnlockContext([[self openGLContext] CGLContextObj]);
112 }
113}
114
115- (void)setupDisplayLink {
116 if (_displayLink) {
117 return;
118 }
119 // Synchronize buffer swaps with vertical refresh rate.
120 GLint swapInt = 1;
121 [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
122
123 // Create display link.
124 CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
125 CVDisplayLinkSetOutputCallback(_displayLink,
126 &OnDisplayLinkFired,
127 (__bridge void *)self);
128 // Set the display link for the current renderer.
129 CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
130 CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
131 CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(
132 _displayLink, cglContext, cglPixelFormat);
133 CVDisplayLinkStart(_displayLink);
134}
135
136- (void)teardownDisplayLink {
137 if (!_displayLink) {
138 return;
139 }
140 CVDisplayLinkRelease(_displayLink);
141 _displayLink = NULL;
142}
143
144@end
tkchin9eeb6242016-04-27 01:54:20 -0700145
146#endif // !TARGET_OS_IPHONE