Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * libjingle | 2 * libjingle |
| 3 * Copyright 2014 Google Inc. | 3 * Copyright 2014 Google Inc. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions are met: | 6 * modification, are permitted provided that the following conditions are met: |
| 7 * | 7 * |
| 8 * 1. Redistributions of source code must retain the above copyright notice, | 8 * 1. Redistributions of source code must retain the above copyright notice, |
| 9 * this list of conditions and the following disclaimer. | 9 * this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright notice, | 10 * 2. Redistributions in binary form must reproduce the above copyright notice, |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 85 - (void)invalidate { | 85 - (void)invalidate { |
| 86 [_displayLink invalidate]; | 86 [_displayLink invalidate]; |
| 87 } | 87 } |
| 88 | 88 |
| 89 - (void)displayLinkDidFire:(CADisplayLink*)displayLink { | 89 - (void)displayLinkDidFire:(CADisplayLink*)displayLink { |
| 90 _timerHandler(); | 90 _timerHandler(); |
| 91 } | 91 } |
| 92 | 92 |
| 93 @end | 93 @end |
| 94 | 94 |
| 95 // RTCEAGLVideoView wraps a GLKView which is setup with | |
| 96 // enableSetNeedsDisplay = NO for the purpose of gaining control of | |
| 97 // exactly when to call -[GLKView display]. This need for extra | |
| 98 // control is required to avoid triggering method calls on GLKView | |
| 99 // that results in attempting to bind the underlying render buffer | |
| 100 // when the drawable size would be empty which would result in the | |
| 101 // error GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT. -[GLKView display] is | |
| 102 // the method that will trigger the binding of the render | |
| 103 // buffer. Because the standard behaviour of -[UIView setNeedsDisplay] | |
| 104 // is disabled for the reasons above, the RTCEAGLVideoView maintains | |
| 105 // its own |isDirty| flag. | |
| 106 | |
| 95 @interface RTCEAGLVideoView () <GLKViewDelegate> | 107 @interface RTCEAGLVideoView () <GLKViewDelegate> |
| 96 // |i420Frame| is set when we receive a frame from a worker thread and is read | 108 // |i420Frame| is set when we receive a frame from a worker thread and is read |
| 97 // from the display link callback so atomicity is required. | 109 // from the display link callback so atomicity is required. |
| 98 @property(atomic, strong) RTCI420Frame* i420Frame; | 110 @property(atomic, strong) RTCI420Frame* i420Frame; |
| 99 @property(nonatomic, readonly) GLKView* glkView; | 111 @property(nonatomic, readonly) GLKView* glkView; |
| 100 @property(nonatomic, readonly) RTCOpenGLVideoRenderer* glRenderer; | 112 @property(nonatomic, readonly) RTCOpenGLVideoRenderer* glRenderer; |
| 101 @end | 113 @end |
| 102 | 114 |
| 103 @implementation RTCEAGLVideoView { | 115 @implementation RTCEAGLVideoView { |
| 104 RTCDisplayLinkTimer* _timer; | 116 RTCDisplayLinkTimer* _timer; |
| 105 GLKView* _glkView; | 117 GLKView* _glkView; |
| 106 RTCOpenGLVideoRenderer* _glRenderer; | 118 RTCOpenGLVideoRenderer* _glRenderer; |
| 119 BOOL _isDirty; // this flag should only be set and read on the main thread (e. g. by | |
|
magjed_webrtc
2015/09/21 11:44:10
Put this comment above the variable instead. Also
| |
| 120 // setNeedsDisplay) | |
| 107 } | 121 } |
| 108 | 122 |
| 109 - (instancetype)initWithFrame:(CGRect)frame { | 123 - (instancetype)initWithFrame:(CGRect)frame { |
| 110 if (self = [super initWithFrame:frame]) { | 124 if (self = [super initWithFrame:frame]) { |
| 111 [self configure]; | 125 [self configure]; |
| 112 } | 126 } |
| 113 return self; | 127 return self; |
| 114 } | 128 } |
| 115 | 129 |
| 116 - (instancetype)initWithCoder:(NSCoder *)aDecoder { | 130 - (instancetype)initWithCoder:(NSCoder *)aDecoder { |
| 117 if (self = [super initWithCoder:aDecoder]) { | 131 if (self = [super initWithCoder:aDecoder]) { |
| 118 [self configure]; | 132 [self configure]; |
| 119 } | 133 } |
| 120 return self; | 134 return self; |
| 121 } | 135 } |
| 122 | 136 |
| 123 - (void)configure { | 137 - (void)configure { |
| 124 EAGLContext* glContext = | 138 EAGLContext* glContext = |
| 125 [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; | 139 [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; |
| 126 if (!glContext) { | 140 if (!glContext) { |
| 127 glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; | 141 glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; |
| 128 } | 142 } |
| 129 _glRenderer = [[RTCOpenGLVideoRenderer alloc] initWithContext:glContext]; | 143 _glRenderer = [[RTCOpenGLVideoRenderer alloc] initWithContext:glContext]; |
| 130 | 144 |
| 131 // GLKView manages a framebuffer for us. | 145 // GLKView manages a framebuffer for us. |
| 132 _glkView = [[GLKView alloc] initWithFrame:CGRectZero | 146 _glkView = [[GLKView alloc] initWithFrame:CGRectZero |
|
magjed_webrtc
2015/09/21 11:44:10
Would it solve all problems to simply replace CGRe
| |
| 133 context:glContext]; | 147 context:glContext]; |
| 134 _glkView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; | 148 _glkView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; |
| 135 _glkView.drawableDepthFormat = GLKViewDrawableDepthFormatNone; | 149 _glkView.drawableDepthFormat = GLKViewDrawableDepthFormatNone; |
| 136 _glkView.drawableStencilFormat = GLKViewDrawableStencilFormatNone; | 150 _glkView.drawableStencilFormat = GLKViewDrawableStencilFormatNone; |
| 137 _glkView.drawableMultisample = GLKViewDrawableMultisampleNone; | 151 _glkView.drawableMultisample = GLKViewDrawableMultisampleNone; |
| 138 _glkView.delegate = self; | 152 _glkView.delegate = self; |
| 139 _glkView.layer.masksToBounds = YES; | 153 _glkView.layer.masksToBounds = YES; |
| 154 _glkView.enableSetNeedsDisplay = NO; | |
| 140 [self addSubview:_glkView]; | 155 [self addSubview:_glkView]; |
| 141 | 156 |
| 142 // Listen to application state in order to clean up OpenGL before app goes | 157 // Listen to application state in order to clean up OpenGL before app goes |
| 143 // away. | 158 // away. |
| 144 NSNotificationCenter* notificationCenter = | 159 NSNotificationCenter* notificationCenter = |
| 145 [NSNotificationCenter defaultCenter]; | 160 [NSNotificationCenter defaultCenter]; |
| 146 [notificationCenter addObserver:self | 161 [notificationCenter addObserver:self |
| 147 selector:@selector(willResignActive) | 162 selector:@selector(willResignActive) |
| 148 name:UIApplicationWillResignActiveNotification | 163 name:UIApplicationWillResignActiveNotification |
| 149 object:nil]; | 164 object:nil]; |
| 150 [notificationCenter addObserver:self | 165 [notificationCenter addObserver:self |
| 151 selector:@selector(didBecomeActive) | 166 selector:@selector(didBecomeActive) |
| 152 name:UIApplicationDidBecomeActiveNotification | 167 name:UIApplicationDidBecomeActiveNotification |
| 153 object:nil]; | 168 object:nil]; |
| 154 | 169 |
| 155 // Frames are received on a separate thread, so we poll for current frame | 170 // Frames are received on a separate thread, so we poll for current frame |
| 156 // using a refresh rate proportional to screen refresh frequency. This | 171 // using a refresh rate proportional to screen refresh frequency. This |
| 157 // occurs on the main thread. | 172 // occurs on the main thread. |
| 158 __weak RTCEAGLVideoView* weakSelf = self; | 173 __weak RTCEAGLVideoView* weakSelf = self; |
| 159 _timer = [[RTCDisplayLinkTimer alloc] initWithTimerHandler:^{ | 174 _timer = [[RTCDisplayLinkTimer alloc] initWithTimerHandler:^{ |
| 160 RTCEAGLVideoView* strongSelf = weakSelf; | 175 RTCEAGLVideoView* strongSelf = weakSelf; |
| 161 // Don't render if frame hasn't changed. | 176 [strongSelf displayLinkTimerDidFire]; |
|
magjed_webrtc
2015/09/21 11:44:10
What if you keep this code unchanged, remove the v
| |
| 162 if (strongSelf.glRenderer.lastDrawnFrame == strongSelf.i420Frame) { | |
| 163 return; | |
| 164 } | |
| 165 // This tells the GLKView that it's dirty, which will then call the | |
| 166 // GLKViewDelegate method implemented below. | |
| 167 [strongSelf.glkView setNeedsDisplay]; | |
| 168 }]; | 177 }]; |
| 169 [self setupGL]; | 178 [self setupGL]; |
| 170 } | 179 } |
| 171 | 180 |
| 172 - (void)dealloc { | 181 - (void)dealloc { |
| 173 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 182 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 174 UIApplicationState appState = | 183 UIApplicationState appState = |
| 175 [UIApplication sharedApplication].applicationState; | 184 [UIApplication sharedApplication].applicationState; |
| 176 if (appState == UIApplicationStateActive) { | 185 if (appState == UIApplicationStateActive) { |
| 177 [self teardownGL]; | 186 [self teardownGL]; |
| 178 } | 187 } |
| 179 [_timer invalidate]; | 188 [_timer invalidate]; |
| 180 } | 189 } |
| 181 | 190 |
| 182 #pragma mark - UIView | 191 #pragma mark - UIView |
| 183 | 192 |
| 193 - (void)setNeedsDisplay { | |
| 194 [super setNeedsDisplay]; | |
| 195 _isDirty = YES; | |
|
magjed_webrtc
2015/09/21 11:44:10
You still need to execute the code in displayLinkT
| |
| 196 } | |
| 197 | |
| 198 - (void)setNeedsDisplayInRect:(CGRect)rect { | |
| 199 [super setNeedsDisplayInRect:rect]; | |
| 200 _isDirty = YES; | |
| 201 } | |
| 202 | |
| 184 - (void)layoutSubviews { | 203 - (void)layoutSubviews { |
| 185 [super layoutSubviews]; | 204 [super layoutSubviews]; |
| 186 _glkView.frame = self.bounds; | 205 _glkView.frame = self.bounds; |
| 187 } | 206 } |
| 188 | 207 |
| 189 #pragma mark - GLKViewDelegate | 208 #pragma mark - GLKViewDelegate |
| 190 | 209 |
| 191 // This method is called when the GLKView's content is dirty and needs to be | 210 // This method is called when the GLKView's content is dirty and needs to be |
| 192 // redrawn. This occurs on main thread. | 211 // redrawn. This occurs on main thread. |
| 193 - (void)glkView:(GLKView*)view drawInRect:(CGRect)rect { | 212 - (void)glkView:(GLKView*)view drawInRect:(CGRect)rect { |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 206 [strongSelf.delegate videoView:strongSelf didChangeVideoSize:size]; | 225 [strongSelf.delegate videoView:strongSelf didChangeVideoSize:size]; |
| 207 }); | 226 }); |
| 208 } | 227 } |
| 209 | 228 |
| 210 - (void)renderFrame:(RTCI420Frame*)frame { | 229 - (void)renderFrame:(RTCI420Frame*)frame { |
| 211 self.i420Frame = frame; | 230 self.i420Frame = frame; |
| 212 } | 231 } |
| 213 | 232 |
| 214 #pragma mark - Private | 233 #pragma mark - Private |
| 215 | 234 |
| 235 - (void)displayLinkTimerDidFire { | |
| 236 // Don't render unless video frame have changed or the view content | |
| 237 // has explicitly been marked dirty. | |
| 238 if (!_isDirty && _glRenderer.lastDrawnFrame == self.i420Frame) { | |
| 239 return; | |
| 240 } | |
| 241 | |
| 242 // Always reset isDirty at this point, even if -[GLKView display] | |
| 243 // won't be called in the case the drawable size is empty. | |
| 244 _isDirty = NO; | |
| 245 | |
| 246 // Only call -[GLKView display] if the drawable size is | |
| 247 // non-empty. Calling display will make the GLKView setup its | |
| 248 // render buffer if necessary, but that will fail with error | |
| 249 // GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT if size is empty. | |
| 250 if (self.bounds.size.width > 0 && self.bounds.size.height > 0) { | |
| 251 [_glkView display]; | |
|
magjed_webrtc
2015/09/21 11:44:10
Is there any way to override the display call and
| |
| 252 } | |
| 253 } | |
| 254 | |
| 216 - (void)setupGL { | 255 - (void)setupGL { |
| 217 self.i420Frame = nil; | 256 self.i420Frame = nil; |
| 218 [_glRenderer setupGL]; | 257 [_glRenderer setupGL]; |
| 219 _timer.isPaused = NO; | 258 _timer.isPaused = NO; |
| 220 } | 259 } |
| 221 | 260 |
| 222 - (void)teardownGL { | 261 - (void)teardownGL { |
| 223 self.i420Frame = nil; | 262 self.i420Frame = nil; |
| 224 _timer.isPaused = YES; | 263 _timer.isPaused = YES; |
| 225 [_glkView deleteDrawable]; | 264 [_glkView deleteDrawable]; |
| 226 [_glRenderer teardownGL]; | 265 [_glRenderer teardownGL]; |
| 227 } | 266 } |
| 228 | 267 |
| 229 - (void)didBecomeActive { | 268 - (void)didBecomeActive { |
| 230 [self setupGL]; | 269 [self setupGL]; |
| 231 } | 270 } |
| 232 | 271 |
| 233 - (void)willResignActive { | 272 - (void)willResignActive { |
| 234 [self teardownGL]; | 273 [self teardownGL]; |
| 235 } | 274 } |
| 236 | 275 |
| 237 @end | 276 @end |
| OLD | NEW |