blob: 714cae79c6dc13902722ab548c300393d8c287b1 [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
Anders Carlsson7bca8ca2018-08-30 09:30:29 +020015#import "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
magjed13941912017-05-30 06:11:58 -070021#import "RTCDefaultShader.h"
22#import "RTCI420TextureCache.h"
Anders Carlsson7bca8ca2018-08-30 09:30:29 +020023#import "base/RTCLogging.h"
24#import "base/RTCVideoFrame.h"
Jon Hjellee799bad2016-01-11 13:47:11 -080025
26@interface RTCNSGLVideoView ()
27// |videoFrame| is set when we receive a frame from a worker thread and is read
28// from the display link callback so atomicity is required.
29@property(atomic, strong) RTCVideoFrame *videoFrame;
magjed13941912017-05-30 06:11:58 -070030@property(atomic, strong) RTCI420TextureCache *i420TextureCache;
magjed2f7f9b82017-04-13 04:15:53 -070031
Jon Hjellee799bad2016-01-11 13:47:11 -080032- (void)drawFrame;
33@end
34
35static CVReturn OnDisplayLinkFired(CVDisplayLinkRef displayLink,
36 const CVTimeStamp *now,
37 const CVTimeStamp *outputTime,
38 CVOptionFlags flagsIn,
39 CVOptionFlags *flagsOut,
40 void *displayLinkContext) {
41 RTCNSGLVideoView *view = (__bridge RTCNSGLVideoView *)displayLinkContext;
42 [view drawFrame];
43 return kCVReturnSuccess;
44}
45
46@implementation RTCNSGLVideoView {
47 CVDisplayLinkRef _displayLink;
magjed2f7f9b82017-04-13 04:15:53 -070048 RTCVideoFrame *_lastDrawnFrame;
magjed13941912017-05-30 06:11:58 -070049 id<RTCVideoViewShading> _shader;
Jon Hjellee799bad2016-01-11 13:47:11 -080050}
51
52@synthesize delegate = _delegate;
53@synthesize videoFrame = _videoFrame;
magjed13941912017-05-30 06:11:58 -070054@synthesize i420TextureCache = _i420TextureCache;
55
56- (instancetype)initWithFrame:(NSRect)frame pixelFormat:(NSOpenGLPixelFormat *)format {
57 return [self initWithFrame:frame pixelFormat:format shader:[[RTCDefaultShader alloc] init]];
58}
59
60- (instancetype)initWithFrame:(NSRect)frame
61 pixelFormat:(NSOpenGLPixelFormat *)format
62 shader:(id<RTCVideoViewShading>)shader {
63 if (self = [super initWithFrame:frame pixelFormat:format]) {
64 _shader = shader;
65 }
66 return self;
67}
Jon Hjellee799bad2016-01-11 13:47:11 -080068
69- (void)dealloc {
70 [self teardownDisplayLink];
71}
72
73- (void)drawRect:(NSRect)rect {
74 [self drawFrame];
75}
76
77- (void)reshape {
78 [super reshape];
79 NSRect frame = [self frame];
adam.fedorfee994c2017-06-09 05:16:10 -070080 [self ensureGLContext];
Jon Hjellee799bad2016-01-11 13:47:11 -080081 CGLLockContext([[self openGLContext] CGLContextObj]);
82 glViewport(0, 0, frame.size.width, frame.size.height);
83 CGLUnlockContext([[self openGLContext] CGLContextObj]);
84}
85
86- (void)lockFocus {
87 NSOpenGLContext *context = [self openGLContext];
88 [super lockFocus];
89 if ([context view] != self) {
90 [context setView:self];
91 }
92 [context makeCurrentContext];
93}
94
95- (void)prepareOpenGL {
96 [super prepareOpenGL];
magjed2f7f9b82017-04-13 04:15:53 -070097 [self ensureGLContext];
98 glDisable(GL_DITHER);
Jon Hjellee799bad2016-01-11 13:47:11 -080099 [self setupDisplayLink];
100}
101
102- (void)clearGLContext {
magjed2f7f9b82017-04-13 04:15:53 -0700103 [self ensureGLContext];
magjed13941912017-05-30 06:11:58 -0700104 self.i420TextureCache = nil;
Jon Hjellee799bad2016-01-11 13:47:11 -0800105 [super clearGLContext];
106}
107
108#pragma mark - RTCVideoRenderer
109
110// These methods may be called on non-main thread.
111- (void)setSize:(CGSize)size {
112 dispatch_async(dispatch_get_main_queue(), ^{
113 [self.delegate videoView:self didChangeVideoSize:size];
114 });
115}
116
117- (void)renderFrame:(RTCVideoFrame *)frame {
118 self.videoFrame = frame;
119}
120
121#pragma mark - Private
122
123- (void)drawFrame {
magjed2f7f9b82017-04-13 04:15:53 -0700124 RTCVideoFrame *frame = self.videoFrame;
125 if (!frame || frame == _lastDrawnFrame) {
126 return;
Jon Hjellee799bad2016-01-11 13:47:11 -0800127 }
magjed2f7f9b82017-04-13 04:15:53 -0700128 // This method may be called from CVDisplayLink callback which isn't on the
129 // main thread so we have to lock the GL context before drawing.
130 NSOpenGLContext *context = [self openGLContext];
131 CGLLockContext([context CGLContextObj]);
132
133 [self ensureGLContext];
134 glClear(GL_COLOR_BUFFER_BIT);
135
136 // Rendering native CVPixelBuffer is not supported on OS X.
magjed13941912017-05-30 06:11:58 -0700137 // TODO(magjed): Add support for NV12 texture cache on OS X.
magjed2f7f9b82017-04-13 04:15:53 -0700138 frame = [frame newI420VideoFrame];
magjed13941912017-05-30 06:11:58 -0700139 if (!self.i420TextureCache) {
140 self.i420TextureCache = [[RTCI420TextureCache alloc] initWithContext:context];
magjed2f7f9b82017-04-13 04:15:53 -0700141 }
magjed13941912017-05-30 06:11:58 -0700142 RTCI420TextureCache *i420TextureCache = self.i420TextureCache;
143 if (i420TextureCache) {
144 [i420TextureCache uploadFrameToTextures:frame];
Magnus Jedvert6b9653e2017-06-05 17:58:34 +0200145 [_shader applyShadingForFrameWithWidth:frame.width
146 height:frame.height
147 rotation:frame.rotation
148 yPlane:i420TextureCache.yTexture
149 uPlane:i420TextureCache.uTexture
150 vPlane:i420TextureCache.vTexture];
magjed2f7f9b82017-04-13 04:15:53 -0700151 [context flushBuffer];
152 _lastDrawnFrame = frame;
magjed2f7f9b82017-04-13 04:15:53 -0700153 }
154 CGLUnlockContext([context CGLContextObj]);
Jon Hjellee799bad2016-01-11 13:47:11 -0800155}
156
157- (void)setupDisplayLink {
158 if (_displayLink) {
159 return;
160 }
161 // Synchronize buffer swaps with vertical refresh rate.
162 GLint swapInt = 1;
163 [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
164
165 // Create display link.
166 CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
167 CVDisplayLinkSetOutputCallback(_displayLink,
168 &OnDisplayLinkFired,
169 (__bridge void *)self);
170 // Set the display link for the current renderer.
171 CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
172 CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
173 CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(
174 _displayLink, cglContext, cglPixelFormat);
175 CVDisplayLinkStart(_displayLink);
176}
177
178- (void)teardownDisplayLink {
179 if (!_displayLink) {
180 return;
181 }
182 CVDisplayLinkRelease(_displayLink);
183 _displayLink = NULL;
184}
185
magjed2f7f9b82017-04-13 04:15:53 -0700186- (void)ensureGLContext {
187 NSOpenGLContext* context = [self openGLContext];
188 NSAssert(context, @"context shouldn't be nil");
189 if ([NSOpenGLContext currentContext] != context) {
190 [context makeCurrentContext];
191 }
192}
193
Jon Hjellee799bad2016-01-11 13:47:11 -0800194@end
tkchin9eeb6242016-04-27 01:54:20 -0700195
196#endif // !TARGET_OS_IPHONE