blob: 530d9a7e6a195e1d00afc10428f9e9a88b093d47 [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
magjedbf084522017-01-13 07:10:16 -080011#import <Foundation/Foundation.h>
12
tkchin9eeb6242016-04-27 01:54:20 -070013#if !TARGET_OS_IPHONE
14
15#import "WebRTC/RTCNSGLVideoView.h"
Jon Hjellee799bad2016-01-11 13:47:11 -080016
magjed2f7f9b82017-04-13 04:15:53 -070017#import <AppKit/NSOpenGL.h>
Jon Hjellee799bad2016-01-11 13:47:11 -080018#import <CoreVideo/CVDisplayLink.h>
19#import <OpenGL/gl3.h>
tkchin9eeb6242016-04-27 01:54:20 -070020
magjed2f7f9b82017-04-13 04:15:53 -070021#import "RTCShader+Private.h"
22#import "WebRTC/RTCLogging.h"
tkchin9eeb6242016-04-27 01:54:20 -070023#import "WebRTC/RTCVideoFrame.h"
Jon Hjellee799bad2016-01-11 13:47:11 -080024
25@interface RTCNSGLVideoView ()
26// |videoFrame| is set when we receive a frame from a worker thread and is read
27// from the display link callback so atomicity is required.
28@property(atomic, strong) RTCVideoFrame *videoFrame;
magjed2f7f9b82017-04-13 04:15:53 -070029@property(atomic, strong) id<RTCShader> i420Shader;
30
Jon Hjellee799bad2016-01-11 13:47:11 -080031- (void)drawFrame;
32@end
33
34static CVReturn OnDisplayLinkFired(CVDisplayLinkRef displayLink,
35 const CVTimeStamp *now,
36 const CVTimeStamp *outputTime,
37 CVOptionFlags flagsIn,
38 CVOptionFlags *flagsOut,
39 void *displayLinkContext) {
40 RTCNSGLVideoView *view = (__bridge RTCNSGLVideoView *)displayLinkContext;
41 [view drawFrame];
42 return kCVReturnSuccess;
43}
44
45@implementation RTCNSGLVideoView {
46 CVDisplayLinkRef _displayLink;
magjed2f7f9b82017-04-13 04:15:53 -070047 RTCVideoFrame *_lastDrawnFrame;
Jon Hjellee799bad2016-01-11 13:47:11 -080048}
49
50@synthesize delegate = _delegate;
51@synthesize videoFrame = _videoFrame;
magjed2f7f9b82017-04-13 04:15:53 -070052@synthesize i420Shader = _i420Shader;
Jon Hjellee799bad2016-01-11 13:47:11 -080053
54- (void)dealloc {
55 [self teardownDisplayLink];
56}
57
58- (void)drawRect:(NSRect)rect {
59 [self drawFrame];
60}
61
62- (void)reshape {
63 [super reshape];
64 NSRect frame = [self frame];
65 CGLLockContext([[self openGLContext] CGLContextObj]);
66 glViewport(0, 0, frame.size.width, frame.size.height);
67 CGLUnlockContext([[self openGLContext] CGLContextObj]);
68}
69
70- (void)lockFocus {
71 NSOpenGLContext *context = [self openGLContext];
72 [super lockFocus];
73 if ([context view] != self) {
74 [context setView:self];
75 }
76 [context makeCurrentContext];
77}
78
79- (void)prepareOpenGL {
80 [super prepareOpenGL];
magjed2f7f9b82017-04-13 04:15:53 -070081 [self ensureGLContext];
82 glDisable(GL_DITHER);
Jon Hjellee799bad2016-01-11 13:47:11 -080083 [self setupDisplayLink];
84}
85
86- (void)clearGLContext {
magjed2f7f9b82017-04-13 04:15:53 -070087 [self ensureGLContext];
88 self.i420Shader = nil;
Jon Hjellee799bad2016-01-11 13:47:11 -080089 [super clearGLContext];
90}
91
92#pragma mark - RTCVideoRenderer
93
94// These methods may be called on non-main thread.
95- (void)setSize:(CGSize)size {
96 dispatch_async(dispatch_get_main_queue(), ^{
97 [self.delegate videoView:self didChangeVideoSize:size];
98 });
99}
100
101- (void)renderFrame:(RTCVideoFrame *)frame {
102 self.videoFrame = frame;
103}
104
105#pragma mark - Private
106
107- (void)drawFrame {
magjed2f7f9b82017-04-13 04:15:53 -0700108 RTCVideoFrame *frame = self.videoFrame;
109 if (!frame || frame == _lastDrawnFrame) {
110 return;
Jon Hjellee799bad2016-01-11 13:47:11 -0800111 }
magjed2f7f9b82017-04-13 04:15:53 -0700112 // This method may be called from CVDisplayLink callback which isn't on the
113 // main thread so we have to lock the GL context before drawing.
114 NSOpenGLContext *context = [self openGLContext];
115 CGLLockContext([context CGLContextObj]);
116
117 [self ensureGLContext];
118 glClear(GL_COLOR_BUFFER_BIT);
119
120 // Rendering native CVPixelBuffer is not supported on OS X.
121 frame = [frame newI420VideoFrame];
122 if (!self.i420Shader) {
123 self.i420Shader = [[RTCI420Shader alloc] initWithContext:context];
124 }
125 if (self.i420Shader && [self.i420Shader drawFrame:frame]) {
126 [context flushBuffer];
127 _lastDrawnFrame = frame;
128 } else {
129 RTCLog(@"Failed to draw frame.");
130 }
131 CGLUnlockContext([context CGLContextObj]);
Jon Hjellee799bad2016-01-11 13:47:11 -0800132}
133
134- (void)setupDisplayLink {
135 if (_displayLink) {
136 return;
137 }
138 // Synchronize buffer swaps with vertical refresh rate.
139 GLint swapInt = 1;
140 [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
141
142 // Create display link.
143 CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
144 CVDisplayLinkSetOutputCallback(_displayLink,
145 &OnDisplayLinkFired,
146 (__bridge void *)self);
147 // Set the display link for the current renderer.
148 CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
149 CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
150 CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(
151 _displayLink, cglContext, cglPixelFormat);
152 CVDisplayLinkStart(_displayLink);
153}
154
155- (void)teardownDisplayLink {
156 if (!_displayLink) {
157 return;
158 }
159 CVDisplayLinkRelease(_displayLink);
160 _displayLink = NULL;
161}
162
magjed2f7f9b82017-04-13 04:15:53 -0700163- (void)ensureGLContext {
164 NSOpenGLContext* context = [self openGLContext];
165 NSAssert(context, @"context shouldn't be nil");
166 if ([NSOpenGLContext currentContext] != context) {
167 [context makeCurrentContext];
168 }
169}
170
Jon Hjellee799bad2016-01-11 13:47:11 -0800171@end
tkchin9eeb6242016-04-27 01:54:20 -0700172
173#endif // !TARGET_OS_IPHONE