| Index: talk/app/webrtc/objc/RTCEAGLVideoView.m
|
| diff --git a/talk/app/webrtc/objc/RTCEAGLVideoView.m b/talk/app/webrtc/objc/RTCEAGLVideoView.m
|
| index 61040ba1025c2f23eed07f916df93d36b2955d76..d19462c9d97576b841df86cd6f1872fc738de2ba 100644
|
| --- a/talk/app/webrtc/objc/RTCEAGLVideoView.m
|
| +++ b/talk/app/webrtc/objc/RTCEAGLVideoView.m
|
| @@ -92,6 +92,18 @@
|
|
|
| @end
|
|
|
| +// RTCEAGLVideoView wraps a GLKView which is setup with
|
| +// enableSetNeedsDisplay = NO for the purpose of gaining control of
|
| +// exactly when to call -[GLKView display]. This need for extra
|
| +// control is required to avoid triggering method calls on GLKView
|
| +// that results in attempting to bind the underlying render buffer
|
| +// when the drawable size would be empty which would result in the
|
| +// error GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT. -[GLKView display] is
|
| +// the method that will trigger the binding of the render
|
| +// buffer. Because the standard behaviour of -[UIView setNeedsDisplay]
|
| +// is disabled for the reasons above, the RTCEAGLVideoView maintains
|
| +// its own |isDirty| flag.
|
| +
|
| @interface RTCEAGLVideoView () <GLKViewDelegate>
|
| // |i420Frame| is set when we receive a frame from a worker thread and is read
|
| // from the display link callback so atomicity is required.
|
| @@ -104,6 +116,9 @@
|
| RTCDisplayLinkTimer* _timer;
|
| GLKView* _glkView;
|
| RTCOpenGLVideoRenderer* _glRenderer;
|
| + // This flag should only be set and read on the main thread (e.g. by
|
| + // setNeedsDisplay)
|
| + BOOL _isDirty;
|
| }
|
|
|
| - (instancetype)initWithFrame:(CGRect)frame {
|
| @@ -137,6 +152,7 @@
|
| _glkView.drawableMultisample = GLKViewDrawableMultisampleNone;
|
| _glkView.delegate = self;
|
| _glkView.layer.masksToBounds = YES;
|
| + _glkView.enableSetNeedsDisplay = NO;
|
| [self addSubview:_glkView];
|
|
|
| // Listen to application state in order to clean up OpenGL before app goes
|
| @@ -158,13 +174,7 @@
|
| __weak RTCEAGLVideoView* weakSelf = self;
|
| _timer = [[RTCDisplayLinkTimer alloc] initWithTimerHandler:^{
|
| RTCEAGLVideoView* strongSelf = weakSelf;
|
| - // Don't render if frame hasn't changed.
|
| - if (strongSelf.glRenderer.lastDrawnFrame == strongSelf.i420Frame) {
|
| - return;
|
| - }
|
| - // This tells the GLKView that it's dirty, which will then call the
|
| - // GLKViewDelegate method implemented below.
|
| - [strongSelf.glkView setNeedsDisplay];
|
| + [strongSelf displayLinkTimerDidFire];
|
| }];
|
| [self setupGL];
|
| }
|
| @@ -181,6 +191,16 @@
|
|
|
| #pragma mark - UIView
|
|
|
| +- (void)setNeedsDisplay {
|
| + [super setNeedsDisplay];
|
| + _isDirty = YES;
|
| +}
|
| +
|
| +- (void)setNeedsDisplayInRect:(CGRect)rect {
|
| + [super setNeedsDisplayInRect:rect];
|
| + _isDirty = YES;
|
| +}
|
| +
|
| - (void)layoutSubviews {
|
| [super layoutSubviews];
|
| _glkView.frame = self.bounds;
|
| @@ -213,6 +233,26 @@
|
|
|
| #pragma mark - Private
|
|
|
| +- (void)displayLinkTimerDidFire {
|
| + // Don't render unless video frame have changed or the view content
|
| + // has explicitly been marked dirty.
|
| + if (!_isDirty && _glRenderer.lastDrawnFrame == self.i420Frame) {
|
| + return;
|
| + }
|
| +
|
| + // Always reset isDirty at this point, even if -[GLKView display]
|
| + // won't be called in the case the drawable size is empty.
|
| + _isDirty = NO;
|
| +
|
| + // Only call -[GLKView display] if the drawable size is
|
| + // non-empty. Calling display will make the GLKView setup its
|
| + // render buffer if necessary, but that will fail with error
|
| + // GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT if size is empty.
|
| + if (self.bounds.size.width > 0 && self.bounds.size.height > 0) {
|
| + [_glkView display];
|
| + }
|
| +}
|
| +
|
| - (void)setupGL {
|
| self.i420Frame = nil;
|
| [_glRenderer setupGL];
|
|
|