Cleanup RTCVideoRenderer interface.

RTCVideoRenderer should be a protocol not a class. This change includes
an adapter for use with the C++ apis. The video views have been refactored
to implement that protocol.

BUG=3795
R=glaznev@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/23279004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7626 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/app/webrtc/objc/RTCEAGLVideoView+Internal.h b/talk/app/webrtc/objc/RTCEAGLVideoView+Internal.h
deleted file mode 100644
index 10df2e3..0000000
--- a/talk/app/webrtc/objc/RTCEAGLVideoView+Internal.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * libjingle
- * Copyright 2014, Google Inc.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *  3. The name of the author may not be used to endorse or promote products
- *     derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#import <Foundation/Foundation.h>
-
-#import "RTCEAGLVideoView.h"
-#import "RTCVideoRenderer.h"
-
-// TODO(tkchin): Move declaration to implementation file. Exposed here in order
-// to support deprecated methods in RTCVideoRenderer.
-@interface RTCEAGLVideoView (Internal) <RTCVideoRendererDelegate>
-@end
diff --git a/talk/app/webrtc/objc/RTCEAGLVideoView.m b/talk/app/webrtc/objc/RTCEAGLVideoView.m
index faacef6..c55c87e 100644
--- a/talk/app/webrtc/objc/RTCEAGLVideoView.m
+++ b/talk/app/webrtc/objc/RTCEAGLVideoView.m
@@ -29,13 +29,12 @@
 #error "This file requires ARC support."
 #endif
 
-#import "RTCEAGLVideoView+Internal.h"
+#import "RTCEAGLVideoView.h"
 
 #import <GLKit/GLKit.h>
 
+#import "RTCI420Frame.h"
 #import "RTCOpenGLVideoRenderer.h"
-#import "RTCVideoRenderer.h"
-#import "RTCVideoTrack.h"
 
 // RTCDisplayLinkTimer wraps a CADisplayLink and is set to fire every two screen
 // refreshes, which should be 30fps. We wrap the display link in order to avoid
@@ -105,7 +104,6 @@
   RTCDisplayLinkTimer* _timer;
   GLKView* _glkView;
   RTCOpenGLVideoRenderer* _glRenderer;
-  RTCVideoRenderer* _videoRenderer;
 }
 
 - (instancetype)initWithFrame:(CGRect)frame {
@@ -152,7 +150,6 @@
       // GLKViewDelegate method implemented below.
       [strongSelf.glkView setNeedsDisplay];
     }];
-    _videoRenderer = [[RTCVideoRenderer alloc] initWithDelegate:self];
     [self setupGL];
   }
   return self;
@@ -168,18 +165,6 @@
   [_timer invalidate];
 }
 
-- (void)setVideoTrack:(RTCVideoTrack*)videoTrack {
-  if (_videoTrack == videoTrack) {
-    return;
-  }
-  [_videoTrack removeRenderer:_videoRenderer];
-  self.i420Frame = nil;
-  _videoTrack = videoTrack;
-  [_videoTrack addRenderer:_videoRenderer];
-  // TODO(tkchin): potentially handle changes in track state - e.g. render
-  // black if track fails.
-}
-
 #pragma mark - UIView
 
 - (void)layoutSubviews {
@@ -197,14 +182,31 @@
   [_glRenderer drawFrame:self.i420Frame];
 }
 
+#pragma mark - RTCVideoRenderer
+
+// These methods may be called on non-main thread.
+- (void)setSize:(CGSize)size {
+  __weak RTCEAGLVideoView* weakSelf = self;
+  dispatch_async(dispatch_get_main_queue(), ^{
+    RTCEAGLVideoView* strongSelf = weakSelf;
+    [strongSelf.delegate videoView:strongSelf didChangeVideoSize:size];
+  });
+}
+
+- (void)renderFrame:(RTCI420Frame*)frame {
+  self.i420Frame = frame;
+}
+
 #pragma mark - Private
 
 - (void)setupGL {
+  self.i420Frame = nil;
   [_glRenderer setupGL];
   _timer.isPaused = NO;
 }
 
 - (void)teardownGL {
+  self.i420Frame = nil;
   _timer.isPaused = YES;
   [_glkView deleteDrawable];
   [_glRenderer teardownGL];
@@ -219,25 +221,3 @@
 }
 
 @end
-
-@implementation RTCEAGLVideoView (Internal)
-
-#pragma mark - RTCVideoRendererDelegate
-
-// These methods are called when the video track has frame information to
-// provide. This occurs on non-main thread.
-- (void)renderer:(RTCVideoRenderer*)renderer
-      didSetSize:(CGSize)size {
-  __weak RTCEAGLVideoView* weakSelf = self;
-  dispatch_async(dispatch_get_main_queue(), ^{
-    RTCEAGLVideoView* strongSelf = weakSelf;
-    [strongSelf.delegate videoView:strongSelf didChangeVideoSize:size];
-  });
-}
-
-- (void)renderer:(RTCVideoRenderer*)renderer
-    didReceiveFrame:(RTCI420Frame*)frame {
-  self.i420Frame = frame;
-}
-
-@end
diff --git a/talk/app/webrtc/objc/RTCI420Frame.mm b/talk/app/webrtc/objc/RTCI420Frame.mm
index 0b50691..9c394e5 100644
--- a/talk/app/webrtc/objc/RTCI420Frame.mm
+++ b/talk/app/webrtc/objc/RTCI420Frame.mm
@@ -78,6 +78,10 @@
   return _videoFrame->GetVPitch();
 }
 
+- (BOOL)makeExclusive {
+  return _videoFrame->MakeExclusive();
+}
+
 @end
 
 @implementation RTCI420Frame (Internal)
diff --git a/talk/app/webrtc/objc/RTCMediaStream.mm b/talk/app/webrtc/objc/RTCMediaStream.mm
index 27d20b8..a72508a 100644
--- a/talk/app/webrtc/objc/RTCMediaStream.mm
+++ b/talk/app/webrtc/objc/RTCMediaStream.mm
@@ -71,7 +71,7 @@
 }
 
 - (BOOL)addVideoTrack:(RTCVideoTrack*)track {
-  if (self.mediaStream->AddTrack(track.videoTrack)) {
+  if (self.mediaStream->AddTrack(track.nativeVideoTrack)) {
     [_videoTracks addObject:track];
     return YES;
   }
@@ -93,7 +93,8 @@
   NSUInteger index = [_videoTracks indexOfObjectIdenticalTo:track];
   NSAssert(index != NSNotFound,
            @"|removeAudioTrack| called on unexpected RTCVideoTrack");
-  if (index != NSNotFound && self.mediaStream->RemoveTrack(track.videoTrack)) {
+  if (index != NSNotFound &&
+      self.mediaStream->RemoveTrack(track.nativeVideoTrack)) {
     [_videoTracks removeObjectAtIndex:index];
     return YES;
   }
diff --git a/talk/app/webrtc/objc/RTCNSGLVideoView.m b/talk/app/webrtc/objc/RTCNSGLVideoView.m
index 292e792..7aa4a11 100644
--- a/talk/app/webrtc/objc/RTCNSGLVideoView.m
+++ b/talk/app/webrtc/objc/RTCNSGLVideoView.m
@@ -33,10 +33,10 @@
 
 #import <CoreVideo/CVDisplayLink.h>
 #import <OpenGL/gl3.h>
+#import "RTCI420Frame.h"
 #import "RTCOpenGLVideoRenderer.h"
-#import "RTCVideoRenderer.h"
 
-@interface RTCNSGLVideoView () <RTCVideoRendererDelegate>
+@interface RTCNSGLVideoView ()
 // |i420Frame| is set when we receive a frame from a worker thread and is read
 // from the display link callback so atomicity is required.
 @property(atomic, strong) RTCI420Frame* i420Frame;
@@ -57,15 +57,6 @@
 
 @implementation RTCNSGLVideoView {
   CVDisplayLinkRef _displayLink;
-  RTCVideoRenderer* _videoRenderer;
-}
-
-- (instancetype)initWithFrame:(NSRect)frame
-                  pixelFormat:(NSOpenGLPixelFormat*)format {
-  if (self = [super initWithFrame:frame pixelFormat:format]) {
-    _videoRenderer = [[RTCVideoRenderer alloc] initWithDelegate:self];
-  }
-  return self;
 }
 
 - (void)dealloc {
@@ -109,37 +100,16 @@
   [super clearGLContext];
 }
 
-- (void)setVideoTrack:(RTCVideoTrack*)videoTrack {
-  if (_videoTrack == videoTrack) {
-    return;
-  }
-  if (_videoTrack) {
-    [_videoTrack removeRenderer:_videoRenderer];
-    CVDisplayLinkStop(_displayLink);
-    // Clear contents.
-    self.i420Frame = nil;
-    [self drawFrame];
-  }
-  _videoTrack = videoTrack;
-  if (_videoTrack) {
-    [_videoTrack addRenderer:_videoRenderer];
-    CVDisplayLinkStart(_displayLink);
-  }
-}
+#pragma mark - RTCVideoRenderer
 
-#pragma mark - RTCVideoRendererDelegate
-
-// These methods are called when the video track has frame information to
-// provide. This occurs on non-main thread.
-- (void)renderer:(RTCVideoRenderer*)renderer
-      didSetSize:(CGSize)size {
+// These methods may be called on non-main thread.
+- (void)setSize:(CGSize)size {
   dispatch_async(dispatch_get_main_queue(), ^{
     [self.delegate videoView:self didChangeVideoSize:size];
   });
 }
 
-- (void)renderer:(RTCVideoRenderer*)renderer
-    didReceiveFrame:(RTCI420Frame*)frame {
+- (void)renderFrame:(RTCI420Frame*)frame {
   self.i420Frame = frame;
 }
 
@@ -174,9 +144,7 @@
   CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
   CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(
       _displayLink, cglContext, cglPixelFormat);
-  if (_videoTrack) {
-    CVDisplayLinkStart(_displayLink);
-  }
+  CVDisplayLinkStart(_displayLink);
 }
 
 - (void)teardownDisplayLink {
diff --git a/talk/app/webrtc/objc/RTCVideoRenderer+Internal.h b/talk/app/webrtc/objc/RTCVideoRendererAdapter.h
similarity index 84%
rename from talk/app/webrtc/objc/RTCVideoRenderer+Internal.h
rename to talk/app/webrtc/objc/RTCVideoRendererAdapter.h
index 22e445c..faf0906 100644
--- a/talk/app/webrtc/objc/RTCVideoRenderer+Internal.h
+++ b/talk/app/webrtc/objc/RTCVideoRendererAdapter.h
@@ -29,8 +29,12 @@
 
 #include "talk/app/webrtc/mediastreaminterface.h"
 
-@interface RTCVideoRenderer (Internal)
+@interface RTCVideoRendererAdapter : NSObject
 
-@property(nonatomic, readonly) webrtc::VideoRendererInterface* videoRenderer;
+@property(nonatomic, readonly) id<RTCVideoRenderer> videoRenderer;
+@property(nonatomic, readonly)
+    webrtc::VideoRendererInterface* nativeVideoRenderer;
+
+- (instancetype)initWithVideoRenderer:(id<RTCVideoRenderer>)videoRenderer;
 
 @end
diff --git a/talk/app/webrtc/objc/RTCVideoRenderer.mm b/talk/app/webrtc/objc/RTCVideoRendererAdapter.mm
similarity index 70%
rename from talk/app/webrtc/objc/RTCVideoRenderer.mm
rename to talk/app/webrtc/objc/RTCVideoRendererAdapter.mm
index 4cfe43a..e29faad 100644
--- a/talk/app/webrtc/objc/RTCVideoRenderer.mm
+++ b/talk/app/webrtc/objc/RTCVideoRendererAdapter.mm
@@ -29,50 +29,46 @@
 #error "This file requires ARC support."
 #endif
 
-#import "RTCVideoRenderer+Internal.h"
+#import "RTCVideoRendererAdapter.h"
 #import "RTCI420Frame+Internal.h"
 
 namespace webrtc {
 
-class RTCVideoRendererAdapter : public VideoRendererInterface {
+class RTCVideoRendererNativeAdapter : public VideoRendererInterface {
  public:
-  RTCVideoRendererAdapter(RTCVideoRenderer* renderer) { _renderer = renderer; }
+  RTCVideoRendererNativeAdapter(RTCVideoRendererAdapter* adapter) {
+    _adapter = adapter;
+  }
 
   virtual void SetSize(int width, int height) OVERRIDE {
-    [_renderer.delegate renderer:_renderer
-                      didSetSize:CGSizeMake(width, height)];
+    [_adapter.videoRenderer setSize:CGSizeMake(width, height)];
   }
 
   virtual void RenderFrame(const cricket::VideoFrame* frame) OVERRIDE {
-    if (!_renderer.delegate) {
-      return;
-    }
     RTCI420Frame* i420Frame = [[RTCI420Frame alloc] initWithVideoFrame:frame];
-    [_renderer.delegate renderer:_renderer didReceiveFrame:i420Frame];
+    [_adapter.videoRenderer renderFrame:i420Frame];
   }
 
  private:
-  __weak RTCVideoRenderer* _renderer;
+  __weak RTCVideoRendererAdapter* _adapter;
 };
 }
 
-@implementation RTCVideoRenderer {
-  rtc::scoped_ptr<webrtc::RTCVideoRendererAdapter> _adapter;
+@implementation RTCVideoRendererAdapter {
+  id<RTCVideoRenderer> _videoRenderer;
+  rtc::scoped_ptr<webrtc::RTCVideoRendererNativeAdapter> _adapter;
 }
 
-- (instancetype)initWithDelegate:(id<RTCVideoRendererDelegate>)delegate {
+- (instancetype)initWithVideoRenderer:(id<RTCVideoRenderer>)videoRenderer {
+  NSParameterAssert(videoRenderer);
   if (self = [super init]) {
-    _delegate = delegate;
-    _adapter.reset(new webrtc::RTCVideoRendererAdapter(self));
+    _videoRenderer = videoRenderer;
+    _adapter.reset(new webrtc::RTCVideoRendererNativeAdapter(self));
   }
   return self;
 }
 
-@end
-
-@implementation RTCVideoRenderer (Internal)
-
-- (webrtc::VideoRendererInterface*)videoRenderer {
+- (webrtc::VideoRendererInterface*)nativeVideoRenderer {
   return _adapter.get();
 }
 
diff --git a/talk/app/webrtc/objc/RTCVideoTrack+Internal.h b/talk/app/webrtc/objc/RTCVideoTrack+Internal.h
index 03c8f95..5f267ac 100644
--- a/talk/app/webrtc/objc/RTCVideoTrack+Internal.h
+++ b/talk/app/webrtc/objc/RTCVideoTrack+Internal.h
@@ -34,7 +34,7 @@
 
 @interface RTCVideoTrack (Internal)
 
-@property(nonatomic, assign, readonly)
-    rtc::scoped_refptr<webrtc::VideoTrackInterface> videoTrack;
+@property(nonatomic, readonly)
+    rtc::scoped_refptr<webrtc::VideoTrackInterface> nativeVideoTrack;
 
 @end
diff --git a/talk/app/webrtc/objc/RTCVideoTrack.mm b/talk/app/webrtc/objc/RTCVideoTrack.mm
index beebde0..959bc6d 100644
--- a/talk/app/webrtc/objc/RTCVideoTrack.mm
+++ b/talk/app/webrtc/objc/RTCVideoTrack.mm
@@ -32,46 +32,55 @@
 #import "RTCVideoTrack+Internal.h"
 
 #import "RTCMediaStreamTrack+Internal.h"
-#import "RTCVideoRenderer+Internal.h"
+#import "RTCVideoRendererAdapter.h"
 
 @implementation RTCVideoTrack {
-  NSMutableArray* _rendererArray;
+  NSMutableArray* _adapters;
 }
 
 - (id)initWithMediaTrack:
           (rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>)
       mediaTrack {
   if (self = [super initWithMediaTrack:mediaTrack]) {
-    _rendererArray = [NSMutableArray array];
+    _adapters = [NSMutableArray array];
   }
   return self;
 }
 
-- (void)addRenderer:(RTCVideoRenderer*)renderer {
-  NSAssert1(![self.renderers containsObject:renderer],
-            @"renderers already contains object [%@]",
-            [renderer description]);
-  [_rendererArray addObject:renderer];
-  self.videoTrack->AddRenderer(renderer.videoRenderer);
-}
-
-- (void)removeRenderer:(RTCVideoRenderer*)renderer {
-  NSUInteger index = [self.renderers indexOfObjectIdenticalTo:renderer];
-  if (index != NSNotFound) {
-    [_rendererArray removeObjectAtIndex:index];
-    self.videoTrack->RemoveRenderer(renderer.videoRenderer);
+- (void)addRenderer:(id<RTCVideoRenderer>)renderer {
+  // Make sure we don't have this renderer yet.
+  for (RTCVideoRendererAdapter* adapter in _adapters) {
+    NSParameterAssert(adapter.videoRenderer != renderer);
   }
+  // Create a wrapper that provides a native pointer for us.
+  RTCVideoRendererAdapter* adapter =
+      [[RTCVideoRendererAdapter alloc] initWithVideoRenderer:renderer];
+  [_adapters addObject:adapter];
+  self.nativeVideoTrack->AddRenderer(adapter.nativeVideoRenderer);
 }
 
-- (NSArray*)renderers {
-  return [_rendererArray copy];
+- (void)removeRenderer:(id<RTCVideoRenderer>)renderer {
+  RTCVideoRendererAdapter* adapter = nil;
+  NSUInteger indexToRemove = NSNotFound;
+  for (NSUInteger i = 0; i < _adapters.count; i++) {
+    adapter = _adapters[i];
+    if (adapter.videoRenderer == renderer) {
+      indexToRemove = i;
+      break;
+    }
+  }
+  if (indexToRemove == NSNotFound) {
+    return;
+  }
+  self.nativeVideoTrack->RemoveRenderer(adapter.nativeVideoRenderer);
+  [_adapters removeObjectAtIndex:indexToRemove];
 }
 
 @end
 
 @implementation RTCVideoTrack (Internal)
 
-- (rtc::scoped_refptr<webrtc::VideoTrackInterface>)videoTrack {
+- (rtc::scoped_refptr<webrtc::VideoTrackInterface>)nativeVideoTrack {
   return static_cast<webrtc::VideoTrackInterface*>(self.mediaTrack.get());
 }
 
diff --git a/talk/app/webrtc/objc/public/RTCEAGLVideoView.h b/talk/app/webrtc/objc/public/RTCEAGLVideoView.h
index c38799e..526175f 100644
--- a/talk/app/webrtc/objc/public/RTCEAGLVideoView.h
+++ b/talk/app/webrtc/objc/public/RTCEAGLVideoView.h
@@ -37,11 +37,10 @@
 
 @end
 
-@class RTCVideoTrack;
-// RTCEAGLVideoView renders |videoTrack| onto itself using OpenGLES.
-@interface RTCEAGLVideoView : UIView
+// RTCEAGLVideoView is an RTCVideoRenderer which renders i420 frames in its
+// bounds using OpenGLES 2.0.
+@interface RTCEAGLVideoView : UIView <RTCVideoRenderer>
 
-@property(nonatomic, strong) RTCVideoTrack* videoTrack;
 @property(nonatomic, weak) id<RTCEAGLVideoViewDelegate> delegate;
 
 @end
diff --git a/talk/app/webrtc/objc/public/RTCI420Frame.h b/talk/app/webrtc/objc/public/RTCI420Frame.h
index 737968c..7a8c4d4 100644
--- a/talk/app/webrtc/objc/public/RTCI420Frame.h
+++ b/talk/app/webrtc/objc/public/RTCI420Frame.h
@@ -43,6 +43,8 @@
 @property(nonatomic, readonly) NSInteger uPitch;
 @property(nonatomic, readonly) NSInteger vPitch;
 
+- (BOOL)makeExclusive;
+
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
 // Disallow init and don't add to documentation
 - (id)init __attribute__((
diff --git a/talk/app/webrtc/objc/public/RTCNSGLVideoView.h b/talk/app/webrtc/objc/public/RTCNSGLVideoView.h
index fd757cb..0af2dc5 100644
--- a/talk/app/webrtc/objc/public/RTCNSGLVideoView.h
+++ b/talk/app/webrtc/objc/public/RTCNSGLVideoView.h
@@ -31,7 +31,7 @@
 
 #import <AppKit/NSOpenGLView.h>
 
-#import "RTCVideoTrack.h"
+#import "RTCVideoRenderer.h"
 
 @class RTCNSGLVideoView;
 @protocol RTCNSGLVideoViewDelegate
@@ -40,9 +40,8 @@
 
 @end
 
-@interface RTCNSGLVideoView : NSOpenGLView
+@interface RTCNSGLVideoView : NSOpenGLView <RTCVideoRenderer>
 
-@property(nonatomic, strong) RTCVideoTrack* videoTrack;
 @property(nonatomic, weak) id<RTCNSGLVideoViewDelegate> delegate;
 
 @end
diff --git a/talk/app/webrtc/objc/public/RTCVideoRenderer.h b/talk/app/webrtc/objc/public/RTCVideoRenderer.h
index 37977ce..3c2baba 100644
--- a/talk/app/webrtc/objc/public/RTCVideoRenderer.h
+++ b/talk/app/webrtc/objc/public/RTCVideoRenderer.h
@@ -31,34 +31,13 @@
 #endif
 
 @class RTCI420Frame;
-@class RTCVideoRenderer;
 
-// RTCVideoRendererDelegate is a protocol for an object that must be
-// implemented to get messages when rendering.
-@protocol RTCVideoRendererDelegate<NSObject>
+@protocol RTCVideoRenderer<NSObject>
 
 // The size of the frame.
-- (void)renderer:(RTCVideoRenderer*)renderer didSetSize:(CGSize)size;
+- (void)setSize:(CGSize)size;
 
 // The frame to be displayed.
-- (void)renderer:(RTCVideoRenderer*)renderer
-    didReceiveFrame:(RTCI420Frame*)frame;
-
-@end
-
-// Interface for rendering VideoFrames from a VideoTrack
-@interface RTCVideoRenderer : NSObject
-
-@property(nonatomic, weak) id<RTCVideoRendererDelegate> delegate;
-
-// Initialize the renderer.  Requires a delegate which does the actual drawing
-// of frames.
-- (instancetype)initWithDelegate:(id<RTCVideoRendererDelegate>)delegate;
-
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
-// Disallow init and don't add to documentation
-- (id)init __attribute__((
-    unavailable("init is not a supported initializer for this class.")));
-#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+- (void)renderFrame:(RTCI420Frame*)frame;
 
 @end
diff --git a/talk/app/webrtc/objc/public/RTCVideoTrack.h b/talk/app/webrtc/objc/public/RTCVideoTrack.h
index 291c923..8385b71 100644
--- a/talk/app/webrtc/objc/public/RTCVideoTrack.h
+++ b/talk/app/webrtc/objc/public/RTCVideoTrack.h
@@ -27,19 +27,16 @@
 
 #import "RTCMediaStreamTrack.h"
 
-@class RTCVideoRenderer;
+@protocol RTCVideoRenderer;
 
 // RTCVideoTrack is an ObjectiveC wrapper for VideoTrackInterface.
 @interface RTCVideoTrack : RTCMediaStreamTrack
 
-// The currently registered renderers.
-@property(nonatomic, strong, readonly) NSArray *renderers;
-
 // Register a renderer that will render all frames received on this track.
-- (void)addRenderer:(RTCVideoRenderer *)renderer;
+- (void)addRenderer:(id<RTCVideoRenderer>)renderer;
 
 // Deregister a renderer.
-- (void)removeRenderer:(RTCVideoRenderer *)renderer;
+- (void)removeRenderer:(id<RTCVideoRenderer>)renderer;
 
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
 // Disallow init and don't add to documentation
diff --git a/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm b/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm
index 6c5950b..92d3c49 100644
--- a/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm
+++ b/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm
@@ -46,6 +46,16 @@
 #error "This file requires ARC support."
 #endif
 
+@interface RTCFakeRenderer : NSObject <RTCVideoRenderer>
+@end
+
+@implementation RTCFakeRenderer
+
+- (void)setSize:(CGSize)size {}
+- (void)renderFrame:(RTCI420Frame*)frame {}
+
+@end
+
 @interface RTCPeerConnectionTest : NSObject
 
 // Returns whether the two sessions are of the same type.
@@ -80,8 +90,7 @@
   RTCMediaStream* localMediaStream = [factory mediaStreamWithLabel:streamLabel];
   RTCVideoTrack* videoTrack =
       [factory videoTrackWithID:videoTrackID source:videoSource];
-  RTCVideoRenderer* videoRenderer =
-      [[RTCVideoRenderer alloc] initWithDelegate:nil];
+  RTCFakeRenderer* videoRenderer = [[RTCFakeRenderer alloc] init];
   [videoTrack addRenderer:videoRenderer];
   [localMediaStream addVideoTrack:videoTrack];
   // Test that removal/re-add works.
diff --git a/talk/examples/objc/AppRTCDemo/ios/APPRTCViewController.m b/talk/examples/objc/AppRTCDemo/ios/APPRTCViewController.m
index 8042762..d8d9714 100644
--- a/talk/examples/objc/AppRTCDemo/ios/APPRTCViewController.m
+++ b/talk/examples/objc/AppRTCDemo/ios/APPRTCViewController.m
@@ -34,6 +34,7 @@
 #import <AVFoundation/AVFoundation.h>
 #import "APPRTCConnectionManager.h"
 #import "RTCEAGLVideoView.h"
+#import "RTCVideoTrack.h"
 
 // Padding space for local video view with its parent.
 static CGFloat const kLocalViewPadding = 20;
@@ -47,6 +48,8 @@
 
 @implementation APPRTCViewController {
   APPRTCConnectionManager* _connectionManager;
+  RTCVideoTrack* _localVideoTrack;
+  RTCVideoTrack* _remoteVideoTrack;
   CGSize _localVideoSize;
   CGSize _remoteVideoSize;
 }
@@ -101,13 +104,15 @@
 
 - (void)connectionManager:(APPRTCConnectionManager*)manager
     didReceiveLocalVideoTrack:(RTCVideoTrack*)localVideoTrack {
+  _localVideoTrack = localVideoTrack;
+  [_localVideoTrack addRenderer:self.localVideoView];
   self.localVideoView.hidden = NO;
-  self.localVideoView.videoTrack = localVideoTrack;
 }
 
 - (void)connectionManager:(APPRTCConnectionManager*)manager
     didReceiveRemoteVideoTrack:(RTCVideoTrack*)remoteVideoTrack {
-  self.remoteVideoView.videoTrack = remoteVideoTrack;
+  _remoteVideoTrack = remoteVideoTrack;
+  [_remoteVideoTrack addRenderer:self.remoteVideoView];
 }
 
 - (void)connectionManagerDidReceiveHangup:(APPRTCConnectionManager*)manager {
@@ -193,8 +198,16 @@
   self.instructionsView.hidden = NO;
   self.logView.hidden = YES;
   self.logView.text = nil;
-  self.localVideoView.videoTrack = nil;
-  self.remoteVideoView.videoTrack = nil;
+  if (_localVideoTrack) {
+    [_localVideoTrack removeRenderer:self.localVideoView];
+    _localVideoTrack = nil;
+    [self.localVideoView renderFrame:nil];
+  }
+  if (_remoteVideoTrack) {
+    [_remoteVideoTrack removeRenderer:self.remoteVideoView];
+    _remoteVideoTrack = nil;
+    [self.remoteVideoView renderFrame:nil];
+  }
   self.blackView.hidden = YES;
 }
 
diff --git a/talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.m b/talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.m
index cf5b836..08acac9 100644
--- a/talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.m
+++ b/talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.m
@@ -30,6 +30,7 @@
 #import <AVFoundation/AVFoundation.h>
 #import "APPRTCConnectionManager.h"
 #import "RTCNSGLVideoView.h"
+#import "RTCVideoTrack.h"
 
 static NSUInteger const kContentWidth = 1280;
 static NSUInteger const kContentHeight = 720;
@@ -227,6 +228,8 @@
 
 @implementation APPRTCViewController {
   APPRTCConnectionManager* _connectionManager;
+  RTCVideoTrack* _localVideoTrack;
+  RTCVideoTrack* _remoteVideoTrack;
 }
 
 - (instancetype)initWithNibName:(NSString*)nibName
@@ -258,12 +261,13 @@
 
 - (void)connectionManager:(APPRTCConnectionManager*)manager
     didReceiveLocalVideoTrack:(RTCVideoTrack*)localVideoTrack {
-  self.mainView.localVideoView.videoTrack = localVideoTrack;
+  _localVideoTrack = localVideoTrack;
 }
 
 - (void)connectionManager:(APPRTCConnectionManager*)manager
     didReceiveRemoteVideoTrack:(RTCVideoTrack*)remoteVideoTrack {
-  self.mainView.remoteVideoView.videoTrack = remoteVideoTrack;
+  _remoteVideoTrack = remoteVideoTrack;
+  [_remoteVideoTrack addRenderer:self.mainView.remoteVideoView];
 }
 
 - (void)connectionManagerDidReceiveHangup:(APPRTCConnectionManager*)manager {
@@ -305,7 +309,9 @@
 }
 
 - (void)disconnect {
-  self.mainView.remoteVideoView.videoTrack = nil;
+  [_remoteVideoTrack removeRenderer:self.mainView.remoteVideoView];
+  _remoteVideoTrack = nil;
+  [self.mainView.remoteVideoView renderFrame:nil];
   [_connectionManager disconnect];
 }
 
diff --git a/talk/libjingle.gyp b/talk/libjingle.gyp
index 803eaa3..90d1f41 100755
--- a/talk/libjingle.gyp
+++ b/talk/libjingle.gyp
@@ -204,8 +204,8 @@
             'app/webrtc/objc/RTCStatsReport.mm',
             'app/webrtc/objc/RTCVideoCapturer+Internal.h',
             'app/webrtc/objc/RTCVideoCapturer.mm',
-            'app/webrtc/objc/RTCVideoRenderer+Internal.h',
-            'app/webrtc/objc/RTCVideoRenderer.mm',
+            'app/webrtc/objc/RTCVideoRendererAdapter.h',
+            'app/webrtc/objc/RTCVideoRendererAdapter.mm',
             'app/webrtc/objc/RTCVideoSource+Internal.h',
             'app/webrtc/objc/RTCVideoSource.mm',
             'app/webrtc/objc/RTCVideoTrack+Internal.h',
@@ -259,7 +259,6 @@
           'conditions': [
             ['OS=="ios"', {
               'sources': [
-                'app/webrtc/objc/RTCEAGLVideoView+Internal.h',
                 'app/webrtc/objc/RTCEAGLVideoView.m',
                 'app/webrtc/objc/public/RTCEAGLVideoView.h',
               ],