OLD | NEW |
| (Empty) |
1 /* | |
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 <Foundation/Foundation.h> | |
12 | |
13 #if !TARGET_OS_IPHONE | |
14 | |
15 #import "WebRTC/RTCNSGLVideoView.h" | |
16 | |
17 #import <AppKit/NSOpenGL.h> | |
18 #import <CoreVideo/CVDisplayLink.h> | |
19 #import <OpenGL/gl3.h> | |
20 | |
21 #import "RTCShader+Private.h" | |
22 #import "WebRTC/RTCLogging.h" | |
23 #import "WebRTC/RTCVideoFrame.h" | |
24 | |
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; | |
29 @property(atomic, strong) id<RTCShader> i420Shader; | |
30 | |
31 - (void)drawFrame; | |
32 @end | |
33 | |
34 static 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; | |
47 RTCVideoFrame *_lastDrawnFrame; | |
48 } | |
49 | |
50 @synthesize delegate = _delegate; | |
51 @synthesize videoFrame = _videoFrame; | |
52 @synthesize i420Shader = _i420Shader; | |
53 | |
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]; | |
81 [self ensureGLContext]; | |
82 glDisable(GL_DITHER); | |
83 [self setupDisplayLink]; | |
84 } | |
85 | |
86 - (void)clearGLContext { | |
87 [self ensureGLContext]; | |
88 self.i420Shader = nil; | |
89 [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 { | |
108 RTCVideoFrame *frame = self.videoFrame; | |
109 if (!frame || frame == _lastDrawnFrame) { | |
110 return; | |
111 } | |
112 // 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]); | |
132 } | |
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 | |
163 - (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 | |
171 @end | |
172 | |
173 #endif // !TARGET_OS_IPHONE | |
OLD | NEW |