OLD | NEW |
1 /* | 1 /* |
2 * libjingle | 2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. |
3 * Copyright 2014 Google Inc. | |
4 * | 3 * |
5 * Redistribution and use in source and binary forms, with or without | 4 * Use of this source code is governed by a BSD-style license |
6 * modification, are permitted provided that the following conditions are met: | 5 * that can be found in the LICENSE file in the root of the source |
7 * | 6 * tree. An additional intellectual property rights grant can be found |
8 * 1. Redistributions of source code must retain the above copyright notice, | 7 * in the file PATENTS. All contributing project authors may |
9 * this list of conditions and the following disclaimer. | 8 * be found in the AUTHORS file in the root of the source tree. |
10 * 2. Redistributions in binary form must reproduce the above copyright notice, | |
11 * this list of conditions and the following disclaimer in the documentation | |
12 * and/or other materials provided with the distribution. | |
13 * 3. The name of the author may not be used to endorse or promote products | |
14 * derived from this software without specific prior written permission. | |
15 * | |
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
26 */ | 9 */ |
27 | 10 |
28 #if !defined(__has_feature) || !__has_feature(objc_arc) | |
29 #error "This file requires ARC support." | |
30 #endif | |
31 | |
32 #import "RTCEAGLVideoView.h" | 11 #import "RTCEAGLVideoView.h" |
33 | 12 |
34 #import <GLKit/GLKit.h> | 13 #import <GLKit/GLKit.h> |
35 | 14 |
36 #import "RTCI420Frame.h" | 15 #import "RTCI420Frame.h" |
37 #import "RTCOpenGLVideoRenderer.h" | 16 #import "RTCOpenGLVideoRenderer.h" |
38 | 17 |
39 // RTCDisplayLinkTimer wraps a CADisplayLink and is set to fire every two screen | 18 // RTCDisplayLinkTimer wraps a CADisplayLink and is set to fire every two screen |
40 // refreshes, which should be 30fps. We wrap the display link in order to avoid | 19 // refreshes, which should be 30fps. We wrap the display link in order to avoid |
41 // a retain cycle since CADisplayLink takes a strong reference onto its target. | 20 // a retain cycle since CADisplayLink takes a strong reference onto its target. |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
100 // when the drawable size would be empty which would result in the | 79 // when the drawable size would be empty which would result in the |
101 // error GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT. -[GLKView display] is | 80 // error GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT. -[GLKView display] is |
102 // the method that will trigger the binding of the render | 81 // the method that will trigger the binding of the render |
103 // buffer. Because the standard behaviour of -[UIView setNeedsDisplay] | 82 // buffer. Because the standard behaviour of -[UIView setNeedsDisplay] |
104 // is disabled for the reasons above, the RTCEAGLVideoView maintains | 83 // is disabled for the reasons above, the RTCEAGLVideoView maintains |
105 // its own |isDirty| flag. | 84 // its own |isDirty| flag. |
106 | 85 |
107 @interface RTCEAGLVideoView () <GLKViewDelegate> | 86 @interface RTCEAGLVideoView () <GLKViewDelegate> |
108 // |i420Frame| is set when we receive a frame from a worker thread and is read | 87 // |i420Frame| is set when we receive a frame from a worker thread and is read |
109 // from the display link callback so atomicity is required. | 88 // from the display link callback so atomicity is required. |
110 @property(atomic, strong) RTCI420Frame* i420Frame; | 89 @property(atomic, strong) RTCI420Frame *i420Frame; |
111 @property(nonatomic, readonly) GLKView* glkView; | 90 @property(nonatomic, readonly) GLKView *glkView; |
112 @property(nonatomic, readonly) RTCOpenGLVideoRenderer* glRenderer; | 91 @property(nonatomic, readonly) RTCOpenGLVideoRenderer *glRenderer; |
113 @end | 92 @end |
114 | 93 |
115 @implementation RTCEAGLVideoView { | 94 @implementation RTCEAGLVideoView { |
116 RTCDisplayLinkTimer* _timer; | 95 RTCDisplayLinkTimer *_timer; |
117 GLKView* _glkView; | 96 GLKView *_glkView; |
118 RTCOpenGLVideoRenderer* _glRenderer; | 97 RTCOpenGLVideoRenderer *_glRenderer; |
119 // This flag should only be set and read on the main thread (e.g. by | 98 // This flag should only be set and read on the main thread (e.g. by |
120 // setNeedsDisplay) | 99 // setNeedsDisplay) |
121 BOOL _isDirty; | 100 BOOL _isDirty; |
122 } | 101 } |
123 | 102 |
124 - (instancetype)initWithFrame:(CGRect)frame { | 103 - (instancetype)initWithFrame:(CGRect)frame { |
125 if (self = [super initWithFrame:frame]) { | 104 if (self = [super initWithFrame:frame]) { |
126 [self configure]; | 105 [self configure]; |
127 } | 106 } |
128 return self; | 107 return self; |
129 } | 108 } |
130 | 109 |
131 - (instancetype)initWithCoder:(NSCoder *)aDecoder { | 110 - (instancetype)initWithCoder:(NSCoder *)aDecoder { |
132 if (self = [super initWithCoder:aDecoder]) { | 111 if (self = [super initWithCoder:aDecoder]) { |
133 [self configure]; | 112 [self configure]; |
134 } | 113 } |
135 return self; | 114 return self; |
136 } | 115 } |
137 | 116 |
138 - (void)configure { | 117 - (void)configure { |
139 EAGLContext* glContext = | 118 EAGLContext *glContext = |
140 [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; | 119 [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; |
141 if (!glContext) { | 120 if (!glContext) { |
142 glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; | 121 glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; |
143 } | 122 } |
144 _glRenderer = [[RTCOpenGLVideoRenderer alloc] initWithContext:glContext]; | 123 _glRenderer = [[RTCOpenGLVideoRenderer alloc] initWithContext:glContext]; |
145 | 124 |
146 // GLKView manages a framebuffer for us. | 125 // GLKView manages a framebuffer for us. |
147 _glkView = [[GLKView alloc] initWithFrame:CGRectZero | 126 _glkView = [[GLKView alloc] initWithFrame:CGRectZero |
148 context:glContext]; | 127 context:glContext]; |
149 _glkView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; | 128 _glkView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; |
(...skipping 14 matching lines...) Expand all Loading... |
164 name:UIApplicationWillResignActiveNotification | 143 name:UIApplicationWillResignActiveNotification |
165 object:nil]; | 144 object:nil]; |
166 [notificationCenter addObserver:self | 145 [notificationCenter addObserver:self |
167 selector:@selector(didBecomeActive) | 146 selector:@selector(didBecomeActive) |
168 name:UIApplicationDidBecomeActiveNotification | 147 name:UIApplicationDidBecomeActiveNotification |
169 object:nil]; | 148 object:nil]; |
170 | 149 |
171 // Frames are received on a separate thread, so we poll for current frame | 150 // Frames are received on a separate thread, so we poll for current frame |
172 // using a refresh rate proportional to screen refresh frequency. This | 151 // using a refresh rate proportional to screen refresh frequency. This |
173 // occurs on the main thread. | 152 // occurs on the main thread. |
174 __weak RTCEAGLVideoView* weakSelf = self; | 153 __weak RTCEAGLVideoView *weakSelf = self; |
175 _timer = [[RTCDisplayLinkTimer alloc] initWithTimerHandler:^{ | 154 _timer = [[RTCDisplayLinkTimer alloc] initWithTimerHandler:^{ |
176 RTCEAGLVideoView* strongSelf = weakSelf; | 155 RTCEAGLVideoView *strongSelf = weakSelf; |
177 [strongSelf displayLinkTimerDidFire]; | 156 [strongSelf displayLinkTimerDidFire]; |
178 }]; | 157 }]; |
179 [self setupGL]; | 158 [self setupGL]; |
180 } | 159 } |
181 | 160 |
182 - (void)dealloc { | 161 - (void)dealloc { |
183 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 162 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
184 UIApplicationState appState = | 163 UIApplicationState appState = |
185 [UIApplication sharedApplication].applicationState; | 164 [UIApplication sharedApplication].applicationState; |
186 if (appState == UIApplicationStateActive) { | 165 if (appState == UIApplicationStateActive) { |
(...skipping 16 matching lines...) Expand all Loading... |
203 | 182 |
204 - (void)layoutSubviews { | 183 - (void)layoutSubviews { |
205 [super layoutSubviews]; | 184 [super layoutSubviews]; |
206 _glkView.frame = self.bounds; | 185 _glkView.frame = self.bounds; |
207 } | 186 } |
208 | 187 |
209 #pragma mark - GLKViewDelegate | 188 #pragma mark - GLKViewDelegate |
210 | 189 |
211 // This method is called when the GLKView's content is dirty and needs to be | 190 // This method is called when the GLKView's content is dirty and needs to be |
212 // redrawn. This occurs on main thread. | 191 // redrawn. This occurs on main thread. |
213 - (void)glkView:(GLKView*)view drawInRect:(CGRect)rect { | 192 - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { |
214 // The renderer will draw the frame to the framebuffer corresponding to the | 193 // The renderer will draw the frame to the framebuffer corresponding to the |
215 // one used by |view|. | 194 // one used by |view|. |
216 [_glRenderer drawFrame:self.i420Frame]; | 195 [_glRenderer drawFrame:self.i420Frame]; |
217 } | 196 } |
218 | 197 |
219 #pragma mark - RTCVideoRenderer | 198 #pragma mark - RTCVideoRenderer |
220 | 199 |
221 // These methods may be called on non-main thread. | 200 // These methods may be called on non-main thread. |
222 - (void)setSize:(CGSize)size { | 201 - (void)setSize:(CGSize)size { |
223 __weak RTCEAGLVideoView* weakSelf = self; | 202 __weak RTCEAGLVideoView *weakSelf = self; |
224 dispatch_async(dispatch_get_main_queue(), ^{ | 203 dispatch_async(dispatch_get_main_queue(), ^{ |
225 RTCEAGLVideoView* strongSelf = weakSelf; | 204 RTCEAGLVideoView *strongSelf = weakSelf; |
226 [strongSelf.delegate videoView:strongSelf didChangeVideoSize:size]; | 205 [strongSelf.delegate videoView:strongSelf didChangeVideoSize:size]; |
227 }); | 206 }); |
228 } | 207 } |
229 | 208 |
230 - (void)renderFrame:(RTCI420Frame*)frame { | 209 - (void)renderFrame:(RTCI420Frame *)frame { |
231 self.i420Frame = frame; | 210 self.i420Frame = frame; |
232 } | 211 } |
233 | 212 |
234 #pragma mark - Private | 213 #pragma mark - Private |
235 | 214 |
236 - (void)displayLinkTimerDidFire { | 215 - (void)displayLinkTimerDidFire { |
237 // Don't render unless video frame have changed or the view content | 216 // Don't render unless video frame have changed or the view content |
238 // has explicitly been marked dirty. | 217 // has explicitly been marked dirty. |
239 if (!_isDirty && _glRenderer.lastDrawnFrame == self.i420Frame) { | 218 if (!_isDirty && _glRenderer.lastDrawnFrame == self.i420Frame) { |
240 return; | 219 return; |
(...skipping 27 matching lines...) Expand all Loading... |
268 | 247 |
269 - (void)didBecomeActive { | 248 - (void)didBecomeActive { |
270 [self setupGL]; | 249 [self setupGL]; |
271 } | 250 } |
272 | 251 |
273 - (void)willResignActive { | 252 - (void)willResignActive { |
274 [self teardownGL]; | 253 [self teardownGL]; |
275 } | 254 } |
276 | 255 |
277 @end | 256 @end |
OLD | NEW |