Android AppRTCMobile: Transition local render to new VideoSink interface

BUG=None

Review-Url: https://codereview.webrtc.org/3016443002
Cr-Original-Commit-Position: refs/heads/master@{#19820}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: 4e15e67da8072e8ce7b70c32df3c4be286363ab4
diff --git a/examples/androidapp/src/org/appspot/apprtc/CallActivity.java b/examples/androidapp/src/org/appspot/apprtc/CallActivity.java
index 9c3347b..45bc26b 100644
--- a/examples/androidapp/src/org/appspot/apprtc/CallActivity.java
+++ b/examples/androidapp/src/org/appspot/apprtc/CallActivity.java
@@ -56,7 +56,9 @@
 import org.webrtc.SurfaceViewRenderer;
 import org.webrtc.VideoCapturer;
 import org.webrtc.VideoFileRenderer;
+import org.webrtc.VideoFrame;
 import org.webrtc.VideoRenderer;
+import org.webrtc.VideoSink;
 
 /**
  * Activity for peer connection call setup, call waiting
@@ -137,9 +139,11 @@
   // Peer connection statistics callback period in ms.
   private static final int STAT_CALLBACK_PERIOD = 1000;
 
-  private class ProxyRenderer implements VideoRenderer.Callbacks {
-    private VideoRenderer.Callbacks target;
+  private class ProxyRenderer<T extends VideoRenderer.Callbacks & VideoSink>
+      implements VideoRenderer.Callbacks, VideoSink {
+    private T target;
 
+    @Override
     synchronized public void renderFrame(VideoRenderer.I420Frame frame) {
       if (target == null) {
         Logging.d(TAG, "Dropping frame in proxy because target is null.");
@@ -150,7 +154,17 @@
       target.renderFrame(frame);
     }
 
-    synchronized public void setTarget(VideoRenderer.Callbacks target) {
+    @Override
+    synchronized public void onFrame(VideoFrame frame) {
+      if (target == null) {
+        Logging.d(TAG, "Dropping frame in proxy because target is null.");
+        return;
+      }
+
+      target.onFrame(frame);
+    }
+
+    synchronized public void setTarget(T target) {
       this.target = target;
     }
   }
diff --git a/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java b/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
index c9c9a51..e40a17c 100644
--- a/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
+++ b/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
@@ -52,6 +52,7 @@
 import org.webrtc.StatsReport;
 import org.webrtc.VideoCapturer;
 import org.webrtc.VideoRenderer;
+import org.webrtc.VideoSink;
 import org.webrtc.VideoSource;
 import org.webrtc.VideoTrack;
 import org.webrtc.voiceengine.WebRtcAudioManager;
@@ -123,7 +124,7 @@
   private boolean videoCapturerStopped;
   private boolean isError;
   private Timer statsTimer;
-  private VideoRenderer.Callbacks localRender;
+  private VideoSink localRender;
   private List<VideoRenderer.Callbacks> remoteRenders;
   private SignalingParameters signalingParameters;
   private MediaConstraints pcConstraints;
@@ -332,13 +333,13 @@
     });
   }
 
-  public void createPeerConnection(final VideoRenderer.Callbacks localRender,
+  public void createPeerConnection(final VideoSink localRender,
       final VideoRenderer.Callbacks remoteRender, final VideoCapturer videoCapturer,
       final SignalingParameters signalingParameters) {
     createPeerConnection(
         localRender, Collections.singletonList(remoteRender), videoCapturer, signalingParameters);
   }
-  public void createPeerConnection(final VideoRenderer.Callbacks localRender,
+  public void createPeerConnection(final VideoSink localRender,
       final List<VideoRenderer.Callbacks> remoteRenders, final VideoCapturer videoCapturer,
       final SignalingParameters signalingParameters) {
     if (peerConnectionParameters == null) {
@@ -946,7 +947,7 @@
 
     localVideoTrack = factory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
     localVideoTrack.setEnabled(renderVideo);
-    localVideoTrack.addRenderer(new VideoRenderer(localRender));
+    localVideoTrack.addSink(localRender);
     return localVideoTrack;
   }
 
diff --git a/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java b/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java
index 08c0242..dda16ad 100644
--- a/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java
+++ b/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java
@@ -42,7 +42,9 @@
 import org.webrtc.SessionDescription;
 import org.webrtc.StatsReport;
 import org.webrtc.VideoCapturer;
+import org.webrtc.VideoFrame;
 import org.webrtc.VideoRenderer;
+import org.webrtc.VideoSink;
 
 @RunWith(BaseJUnit4ClassRunner.class)
 public class PeerConnectionClientTest implements PeerConnectionEvents {
@@ -126,6 +128,49 @@
     }
   }
 
+  // Mock VideoSink implementation.
+  private static class MockSink implements VideoSink {
+    // These are protected by 'this' since we gets called from worker threads.
+    private String rendererName;
+    private boolean renderFrameCalled = false;
+
+    // Thread-safe in itself.
+    private CountDownLatch doneRendering;
+
+    public MockSink(int expectedFrames, String rendererName) {
+      this.rendererName = rendererName;
+      reset(expectedFrames);
+    }
+
+    // Resets render to wait for new amount of video frames.
+    public synchronized void reset(int expectedFrames) {
+      renderFrameCalled = false;
+      doneRendering = new CountDownLatch(expectedFrames);
+    }
+
+    @Override
+    public synchronized void onFrame(VideoFrame frame) {
+      if (!renderFrameCalled) {
+        if (rendererName != null) {
+          Log.d(TAG,
+              rendererName + " render frame: " + frame.getRotatedWidth() + " x "
+                  + frame.getRotatedHeight());
+        } else {
+          Log.d(TAG, "Render frame: " + frame.getRotatedWidth() + " x " + frame.getRotatedHeight());
+        }
+      }
+      renderFrameCalled = true;
+      doneRendering.countDown();
+    }
+
+    // This method shouldn't hold any locks or touch member variables since it
+    // blocks.
+    public boolean waitForFramesRendered(int timeoutMs) throws InterruptedException {
+      doneRendering.await(timeoutMs, TimeUnit.MILLISECONDS);
+      return (doneRendering.getCount() <= 0);
+    }
+  }
+
   // Peer connection events implementation.
   @Override
   public void onLocalDescription(SessionDescription sdp) {
@@ -236,7 +281,7 @@
     }
   }
 
-  PeerConnectionClient createPeerConnectionClient(MockRenderer localRenderer,
+  PeerConnectionClient createPeerConnectionClient(MockSink localRenderer,
       MockRenderer remoteRenderer, PeerConnectionParameters peerConnectionParameters,
       VideoCapturer videoCapturer) {
     List<PeerConnection.IceServer> iceServers = new LinkedList<PeerConnection.IceServer>();
@@ -333,7 +378,7 @@
   @SmallTest
   public void testSetLocalOfferMakesVideoFlowLocally() throws InterruptedException {
     Log.d(TAG, "testSetLocalOfferMakesVideoFlowLocally");
-    MockRenderer localRenderer = new MockRenderer(EXPECTED_VIDEO_FRAMES, LOCAL_RENDERER_NAME);
+    MockSink localRenderer = new MockSink(EXPECTED_VIDEO_FRAMES, LOCAL_RENDERER_NAME);
     pcClient = createPeerConnectionClient(localRenderer, new MockRenderer(0, null),
         createParametersForVideoCall(VIDEO_CODEC_VP8),
         createCameraCapturer(false /* captureToTexture */));
@@ -355,11 +400,11 @@
   private void doLoopbackTest(PeerConnectionParameters parameters, VideoCapturer videoCapturer,
       boolean decodeToTexture) throws InterruptedException {
     loopback = true;
-    MockRenderer localRenderer = null;
+    MockSink localRenderer = null;
     MockRenderer remoteRenderer = null;
     if (parameters.videoCallEnabled) {
       Log.d(TAG, "testLoopback for video " + parameters.videoCodec);
-      localRenderer = new MockRenderer(EXPECTED_VIDEO_FRAMES, LOCAL_RENDERER_NAME);
+      localRenderer = new MockSink(EXPECTED_VIDEO_FRAMES, LOCAL_RENDERER_NAME);
       remoteRenderer = new MockRenderer(EXPECTED_VIDEO_FRAMES, REMOTE_RENDERER_NAME);
     } else {
       Log.d(TAG, "testLoopback for audio.");
@@ -494,7 +539,7 @@
     Log.d(TAG, "testCameraSwitch");
     loopback = true;
 
-    MockRenderer localRenderer = new MockRenderer(EXPECTED_VIDEO_FRAMES, LOCAL_RENDERER_NAME);
+    MockSink localRenderer = new MockSink(EXPECTED_VIDEO_FRAMES, LOCAL_RENDERER_NAME);
     MockRenderer remoteRenderer = new MockRenderer(EXPECTED_VIDEO_FRAMES, REMOTE_RENDERER_NAME);
 
     pcClient = createPeerConnectionClient(localRenderer, remoteRenderer,
@@ -542,7 +587,7 @@
     Log.d(TAG, "testVideoSourceRestart");
     loopback = true;
 
-    MockRenderer localRenderer = new MockRenderer(EXPECTED_VIDEO_FRAMES, LOCAL_RENDERER_NAME);
+    MockSink localRenderer = new MockSink(EXPECTED_VIDEO_FRAMES, LOCAL_RENDERER_NAME);
     MockRenderer remoteRenderer = new MockRenderer(EXPECTED_VIDEO_FRAMES, REMOTE_RENDERER_NAME);
 
     pcClient = createPeerConnectionClient(localRenderer, remoteRenderer,
@@ -591,7 +636,7 @@
     Log.d(TAG, "testCaptureFormatChange");
     loopback = true;
 
-    MockRenderer localRenderer = new MockRenderer(EXPECTED_VIDEO_FRAMES, LOCAL_RENDERER_NAME);
+    MockSink localRenderer = new MockSink(EXPECTED_VIDEO_FRAMES, LOCAL_RENDERER_NAME);
     MockRenderer remoteRenderer = new MockRenderer(EXPECTED_VIDEO_FRAMES, REMOTE_RENDERER_NAME);
 
     pcClient = createPeerConnectionClient(localRenderer, remoteRenderer,