blob: de54e36711a1945e05f979de76d750a7da84a5a9 [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
Mirko Bonadeia81e9c82020-05-04 16:14:32 +020026@interface RTC_OBJC_TYPE (RTCNSGLVideoView)
27()
28 // |videoFrame| is set when we receive a frame from a worker thread and is read
29 // from the display link callback so atomicity is required.
30 @property(atomic, strong) RTC_OBJC_TYPE(RTCVideoFrame) *
31 videoFrame;
magjed13941912017-05-30 06:11:58 -070032@property(atomic, strong) RTCI420TextureCache *i420TextureCache;
magjed2f7f9b82017-04-13 04:15:53 -070033
Jon Hjellee799bad2016-01-11 13:47:11 -080034- (void)drawFrame;
35@end
36
37static CVReturn OnDisplayLinkFired(CVDisplayLinkRef displayLink,
38 const CVTimeStamp *now,
39 const CVTimeStamp *outputTime,
40 CVOptionFlags flagsIn,
41 CVOptionFlags *flagsOut,
42 void *displayLinkContext) {
Mirko Bonadeia81e9c82020-05-04 16:14:32 +020043 RTC_OBJC_TYPE(RTCNSGLVideoView) *view =
44 (__bridge RTC_OBJC_TYPE(RTCNSGLVideoView) *)displayLinkContext;
Jon Hjellee799bad2016-01-11 13:47:11 -080045 [view drawFrame];
46 return kCVReturnSuccess;
47}
48
Mirko Bonadeia81e9c82020-05-04 16:14:32 +020049@implementation RTC_OBJC_TYPE (RTCNSGLVideoView) {
Jon Hjellee799bad2016-01-11 13:47:11 -080050 CVDisplayLinkRef _displayLink;
Mirko Bonadeia81e9c82020-05-04 16:14:32 +020051 RTC_OBJC_TYPE(RTCVideoFrame) * _lastDrawnFrame;
52 id<RTC_OBJC_TYPE(RTCVideoViewShading)> _shader;
Jon Hjellee799bad2016-01-11 13:47:11 -080053}
54
55@synthesize delegate = _delegate;
56@synthesize videoFrame = _videoFrame;
magjed13941912017-05-30 06:11:58 -070057@synthesize i420TextureCache = _i420TextureCache;
58
59- (instancetype)initWithFrame:(NSRect)frame pixelFormat:(NSOpenGLPixelFormat *)format {
60 return [self initWithFrame:frame pixelFormat:format shader:[[RTCDefaultShader alloc] init]];
61}
62
63- (instancetype)initWithFrame:(NSRect)frame
64 pixelFormat:(NSOpenGLPixelFormat *)format
Mirko Bonadeia81e9c82020-05-04 16:14:32 +020065 shader:(id<RTC_OBJC_TYPE(RTCVideoViewShading)>)shader {
magjed13941912017-05-30 06:11:58 -070066 if (self = [super initWithFrame:frame pixelFormat:format]) {
67 _shader = shader;
68 }
69 return self;
70}
Jon Hjellee799bad2016-01-11 13:47:11 -080071
72- (void)dealloc {
73 [self teardownDisplayLink];
74}
75
76- (void)drawRect:(NSRect)rect {
77 [self drawFrame];
78}
79
80- (void)reshape {
81 [super reshape];
82 NSRect frame = [self frame];
adam.fedorfee994c2017-06-09 05:16:10 -070083 [self ensureGLContext];
Jon Hjellee799bad2016-01-11 13:47:11 -080084 CGLLockContext([[self openGLContext] CGLContextObj]);
85 glViewport(0, 0, frame.size.width, frame.size.height);
86 CGLUnlockContext([[self openGLContext] CGLContextObj]);
87}
88
89- (void)lockFocus {
90 NSOpenGLContext *context = [self openGLContext];
91 [super lockFocus];
92 if ([context view] != self) {
93 [context setView:self];
94 }
95 [context makeCurrentContext];
96}
97
98- (void)prepareOpenGL {
99 [super prepareOpenGL];
magjed2f7f9b82017-04-13 04:15:53 -0700100 [self ensureGLContext];
101 glDisable(GL_DITHER);
Jon Hjellee799bad2016-01-11 13:47:11 -0800102 [self setupDisplayLink];
103}
104
105- (void)clearGLContext {
magjed2f7f9b82017-04-13 04:15:53 -0700106 [self ensureGLContext];
magjed13941912017-05-30 06:11:58 -0700107 self.i420TextureCache = nil;
Jon Hjellee799bad2016-01-11 13:47:11 -0800108 [super clearGLContext];
109}
110
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200111#pragma mark - RTC_OBJC_TYPE(RTCVideoRenderer)
Jon Hjellee799bad2016-01-11 13:47:11 -0800112
113// These methods may be called on non-main thread.
114- (void)setSize:(CGSize)size {
115 dispatch_async(dispatch_get_main_queue(), ^{
116 [self.delegate videoView:self didChangeVideoSize:size];
117 });
118}
119
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200120- (void)renderFrame:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame {
Jon Hjellee799bad2016-01-11 13:47:11 -0800121 self.videoFrame = frame;
122}
123
124#pragma mark - Private
125
126- (void)drawFrame {
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200127 RTC_OBJC_TYPE(RTCVideoFrame) *frame = self.videoFrame;
magjed2f7f9b82017-04-13 04:15:53 -0700128 if (!frame || frame == _lastDrawnFrame) {
129 return;
Jon Hjellee799bad2016-01-11 13:47:11 -0800130 }
magjed2f7f9b82017-04-13 04:15:53 -0700131 // This method may be called from CVDisplayLink callback which isn't on the
132 // main thread so we have to lock the GL context before drawing.
133 NSOpenGLContext *context = [self openGLContext];
134 CGLLockContext([context CGLContextObj]);
135
136 [self ensureGLContext];
137 glClear(GL_COLOR_BUFFER_BIT);
138
139 // Rendering native CVPixelBuffer is not supported on OS X.
magjed13941912017-05-30 06:11:58 -0700140 // TODO(magjed): Add support for NV12 texture cache on OS X.
magjed2f7f9b82017-04-13 04:15:53 -0700141 frame = [frame newI420VideoFrame];
magjed13941912017-05-30 06:11:58 -0700142 if (!self.i420TextureCache) {
143 self.i420TextureCache = [[RTCI420TextureCache alloc] initWithContext:context];
magjed2f7f9b82017-04-13 04:15:53 -0700144 }
magjed13941912017-05-30 06:11:58 -0700145 RTCI420TextureCache *i420TextureCache = self.i420TextureCache;
146 if (i420TextureCache) {
147 [i420TextureCache uploadFrameToTextures:frame];
Magnus Jedvert6b9653e2017-06-05 17:58:34 +0200148 [_shader applyShadingForFrameWithWidth:frame.width
149 height:frame.height
150 rotation:frame.rotation
151 yPlane:i420TextureCache.yTexture
152 uPlane:i420TextureCache.uTexture
153 vPlane:i420TextureCache.vTexture];
magjed2f7f9b82017-04-13 04:15:53 -0700154 [context flushBuffer];
155 _lastDrawnFrame = frame;
magjed2f7f9b82017-04-13 04:15:53 -0700156 }
157 CGLUnlockContext([context CGLContextObj]);
Jon Hjellee799bad2016-01-11 13:47:11 -0800158}
159
160- (void)setupDisplayLink {
161 if (_displayLink) {
162 return;
163 }
164 // Synchronize buffer swaps with vertical refresh rate.
165 GLint swapInt = 1;
166 [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
167
168 // Create display link.
169 CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
170 CVDisplayLinkSetOutputCallback(_displayLink,
171 &OnDisplayLinkFired,
172 (__bridge void *)self);
173 // Set the display link for the current renderer.
174 CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
175 CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
176 CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(
177 _displayLink, cglContext, cglPixelFormat);
178 CVDisplayLinkStart(_displayLink);
179}
180
181- (void)teardownDisplayLink {
182 if (!_displayLink) {
183 return;
184 }
185 CVDisplayLinkRelease(_displayLink);
186 _displayLink = NULL;
187}
188
magjed2f7f9b82017-04-13 04:15:53 -0700189- (void)ensureGLContext {
190 NSOpenGLContext* context = [self openGLContext];
191 NSAssert(context, @"context shouldn't be nil");
192 if ([NSOpenGLContext currentContext] != context) {
193 [context makeCurrentContext];
194 }
195}
196
Jon Hjellee799bad2016-01-11 13:47:11 -0800197@end
tkchin9eeb6242016-04-27 01:54:20 -0700198
199#endif // !TARGET_OS_IPHONE