This implementation greatly simplifies Android video capturing stack. The old
stack will be removed soon in a separate CL. Constraints will not be supported
in the new implementation. Apps can request a format directly and the closest
supported format will be selected.

Changes needed from the apps:
1. Use the new createVideoSource without constraints.
2. Call startCapture manually.
3. Don't call videoSource.stop/restart, use startCapture/stopCapture instead.

R=magjed@webrtc.org
TBR=kjellander@webrtc.org

Review URL: https://codereview.webrtc.org/2127893002 .

Cr-Commit-Position: refs/heads/master@{#13504}
diff --git a/webrtc/api/BUILD.gn b/webrtc/api/BUILD.gn
index 391af74..526e82c 100644
--- a/webrtc/api/BUILD.gn
+++ b/webrtc/api/BUILD.gn
@@ -156,6 +156,7 @@
       "android/jni/androidnetworkmonitor_jni.h",
       "android/jni/androidvideocapturer_jni.cc",
       "android/jni/androidvideocapturer_jni.h",
+      "android/jni/androidvideotracksource_jni.cc",
       "android/jni/classreferenceholder.cc",
       "android/jni/classreferenceholder.h",
       "android/jni/jni_helpers.cc",
@@ -167,6 +168,8 @@
       "android/jni/surfacetexturehelper_jni.h",
       "androidvideocapturer.cc",
       "androidvideocapturer.h",
+      "androidvideotracksource.cc",
+      "androidvideotracksource.h",
     ]
 
     configs += [
diff --git a/webrtc/api/OWNERS b/webrtc/api/OWNERS
index d6441d3..2160ffe 100644
--- a/webrtc/api/OWNERS
+++ b/webrtc/api/OWNERS
@@ -6,6 +6,9 @@
 tommi@webrtc.org
 deadbeef@webrtc.org
 
+per-file androidvideotracksource.*=sakal@webrtc.org
+per-file androidvideotracksource.*=magjed@webrtc.org
+
 # These are for the common case of adding or renaming files. If you're doing
 # structural changes, please get a review from a reviewer in this file.
 per-file *.gyp=*
diff --git a/webrtc/api/android/java/src/org/webrtc/Camera2Capturer.java b/webrtc/api/android/java/src/org/webrtc/Camera2Capturer.java
index 8110945..c3c912a 100644
--- a/webrtc/api/android/java/src/org/webrtc/Camera2Capturer.java
+++ b/webrtc/api/android/java/src/org/webrtc/Camera2Capturer.java
@@ -575,6 +575,7 @@
       if (eventsHandler != null) {
         eventsHandler.onCameraClosed();
       }
+      capturerObserver.onCapturerStopped();
     }
   }
 
diff --git a/webrtc/api/android/java/src/org/webrtc/PeerConnectionFactory.java b/webrtc/api/android/java/src/org/webrtc/PeerConnectionFactory.java
index 2edd56e..8e3e11d 100644
--- a/webrtc/api/android/java/src/org/webrtc/PeerConnectionFactory.java
+++ b/webrtc/api/android/java/src/org/webrtc/PeerConnectionFactory.java
@@ -111,8 +111,9 @@
         nativeCreateLocalMediaStream(nativeFactory, label));
   }
 
-  // The VideoSource takes ownership of |capturer|, so capturer.release() should not be called
-  // manually after this.
+  // The VideoSource takes ownership of |capturer|, so capturer.dispose() should not be called
+  // manually after this. Video capturer is automatically started so there is no need to call
+  // startCapture after this method.
   public VideoSource createVideoSource(
       VideoCapturer capturer, MediaConstraints constraints) {
     final EglBase.Context eglContext =
@@ -121,6 +122,17 @@
         eglContext, capturer, constraints));
   }
 
+  public VideoSource createVideoSource(VideoCapturer capturer) {
+    final EglBase.Context eglContext =
+        localEglbase == null ? null : localEglbase.getEglBaseContext();
+    long nativeAndroidVideoTrackSource = nativeCreateVideoSource2(nativeFactory, eglContext);
+    VideoCapturer.CapturerObserver capturerObserver
+        = new VideoCapturer.AndroidVideoTrackSourceObserver(nativeAndroidVideoTrackSource);
+    nativeInitializeVideoCapturer(nativeFactory, capturer, nativeAndroidVideoTrackSource,
+         capturerObserver);
+    return new VideoSource(nativeAndroidVideoTrackSource);
+  }
+
   public VideoTrack createVideoTrack(String id, VideoSource source) {
     return new VideoTrack(nativeCreateVideoTrack(
         nativeFactory, id, source.nativeSource));
@@ -239,6 +251,13 @@
       long nativeFactory, EglBase.Context eglContext, VideoCapturer videoCapturer,
       MediaConstraints constraints);
 
+  private static native long nativeCreateVideoSource2(
+      long nativeFactory, EglBase.Context eglContext);
+
+  private static native void nativeInitializeVideoCapturer(
+    long native_factory, VideoCapturer j_video_capturer, long native_source,
+    VideoCapturer.CapturerObserver j_frame_observer);
+
   private static native long nativeCreateVideoTrack(
       long nativeFactory, String id, long nativeVideoSource);
 
diff --git a/webrtc/api/android/java/src/org/webrtc/VideoCapturer.java b/webrtc/api/android/java/src/org/webrtc/VideoCapturer.java
index afa3a05..c92f82a 100644
--- a/webrtc/api/android/java/src/org/webrtc/VideoCapturer.java
+++ b/webrtc/api/android/java/src/org/webrtc/VideoCapturer.java
@@ -21,6 +21,7 @@
     // Notify if the camera have been started successfully or not.
     // Called on a Java thread owned by VideoCapturer.
     void onCapturerStarted(boolean success);
+    void onCapturerStopped();
 
     // Delivers a captured frame. Called on a Java thread owned by VideoCapturer.
     void onByteBufferFrameCaptured(byte[] data, int width, int height, int rotation,
@@ -53,6 +54,9 @@
     }
 
     @Override
+    public void onCapturerStopped() {}
+
+    @Override
     public void onByteBufferFrameCaptured(byte[] data, int width, int height,
         int rotation, long timeStamp) {
       nativeOnByteBufferFrameCaptured(nativeCapturer, data, data.length, width, height, rotation,
@@ -82,6 +86,57 @@
         int width, int height, int framerate);
   }
 
+  // An implementation of CapturerObserver that forwards all calls from
+  // Java to the C layer.
+  static class AndroidVideoTrackSourceObserver implements CapturerObserver {
+    // Pointer to VideoTrackSourceProxy proxying AndroidVideoTrackSource.
+    private final long nativeSource;
+
+    public AndroidVideoTrackSourceObserver(long nativeSource) {
+      this.nativeSource = nativeSource;
+    }
+
+    @Override
+    public void onCapturerStarted(boolean success) {
+      nativeCapturerStarted(nativeSource, success);
+    }
+
+    @Override
+    public void onCapturerStopped() {
+      nativeCapturerStopped(nativeSource);
+    }
+
+    @Override
+    public void onByteBufferFrameCaptured(byte[] data, int width, int height,
+        int rotation, long timeStamp) {
+      nativeOnByteBufferFrameCaptured(nativeSource, data, data.length, width, height, rotation,
+          timeStamp);
+    }
+
+    @Override
+    public void onTextureFrameCaptured(
+        int width, int height, int oesTextureId, float[] transformMatrix, int rotation,
+        long timestamp) {
+      nativeOnTextureFrameCaptured(nativeSource, width, height, oesTextureId, transformMatrix,
+          rotation, timestamp);
+    }
+
+    @Override
+    public void onOutputFormatRequest(int width, int height, int framerate) {
+      nativeOnOutputFormatRequest(nativeSource, width, height, framerate);
+    }
+
+    private native void nativeCapturerStarted(long nativeSource,
+        boolean success);
+    private native void nativeCapturerStopped(long nativeSource);
+    private native void nativeOnByteBufferFrameCaptured(long nativeSource,
+        byte[] data, int length, int width, int height, int rotation, long timeStamp);
+    private native void nativeOnTextureFrameCaptured(long nativeSource, int width, int height,
+        int oesTextureId, float[] transformMatrix, int rotation, long timestamp);
+    private native void nativeOnOutputFormatRequest(long nativeSource,
+        int width, int height, int framerate);
+  }
+
   /**
    * Returns a list with all the formats this VideoCapturer supports.
    */
diff --git a/webrtc/api/android/java/src/org/webrtc/VideoCapturerAndroid.java b/webrtc/api/android/java/src/org/webrtc/VideoCapturerAndroid.java
index 1e9c7ee..d3a9faf 100644
--- a/webrtc/api/android/java/src/org/webrtc/VideoCapturerAndroid.java
+++ b/webrtc/api/android/java/src/org/webrtc/VideoCapturerAndroid.java
@@ -23,9 +23,9 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 // Android specific implementation of VideoCapturer.
 // An instance of this class can be created by an application using
@@ -484,6 +484,7 @@
         eventsHandler.onCameraError("Camera stop timeout");
       }
     }
+    frameObserver.onCapturerStopped();
     Logging.d(TAG, "stopCapture done");
   }
 
diff --git a/webrtc/api/android/java/src/org/webrtc/VideoSource.java b/webrtc/api/android/java/src/org/webrtc/VideoSource.java
index 3864ccb..ba5d2ca 100644
--- a/webrtc/api/android/java/src/org/webrtc/VideoSource.java
+++ b/webrtc/api/android/java/src/org/webrtc/VideoSource.java
@@ -36,11 +36,6 @@
     restart(nativeSource);
   }
 
-  @Override
-  public void dispose() {
-    super.dispose();
-  }
-
   private static native void stop(long nativeSource);
   private static native void restart(long nativeSource);
 }
diff --git a/webrtc/api/android/jni/OWNERS b/webrtc/api/android/jni/OWNERS
index 4178fd6..14c6742 100644
--- a/webrtc/api/android/jni/OWNERS
+++ b/webrtc/api/android/jni/OWNERS
@@ -2,6 +2,8 @@
 per-file androidmediaencoder*=magjed@webrtc.org
 per-file androidmediadecoder*=magjed@webrtc.org
 per-file androidmediacodeccommon.h=magjed@webrtc.org
+per-file androidvideotracksource_jni.cc=magjed@webrtc.org
+per-file androidvideotracksource_jni.cc=sakal@webrtc.org
 per-file surfacetexturehelper*=magjed@webrtc.org
 per-file native_handle_impl*=magjed@webrtc.org
 # Video related parts of peerconnection only.
diff --git a/webrtc/api/android/jni/androidvideotracksource_jni.cc b/webrtc/api/android/jni/androidvideotracksource_jni.cc
new file mode 100644
index 0000000..5ec281a
--- /dev/null
+++ b/webrtc/api/android/jni/androidvideotracksource_jni.cc
@@ -0,0 +1,88 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/api/android/jni/classreferenceholder.h"
+#include "webrtc/api/androidvideotracksource.h"
+#include "webrtc/api/videosourceproxy.h"
+
+// Identifiers are over 80 characters long so this is needed to fit them on one
+// line.
+#define JOW_OBSERVER_METHOD(rettype, name) \
+  JOW(rettype, VideoCapturer_00024AndroidVideoTrackSourceObserver_##name)
+
+namespace webrtc_jni {
+
+static webrtc::AndroidVideoTrackSource* AndroidVideoTrackSourceFromJavaProxy(
+    jlong j_proxy) {
+  auto proxy_source = reinterpret_cast<webrtc::VideoTrackSourceProxy*>(j_proxy);
+  return reinterpret_cast<webrtc::AndroidVideoTrackSource*>(
+      proxy_source->internal());
+}
+
+JOW_OBSERVER_METHOD(void, nativeOnByteBufferFrameCaptured)
+(JNIEnv* jni,
+ jclass,
+ jlong j_source,
+ jbyteArray j_frame,
+ jint length,
+ jint width,
+ jint height,
+ jint rotation,
+ jlong timestamp) {
+  webrtc::AndroidVideoTrackSource* source =
+      AndroidVideoTrackSourceFromJavaProxy(j_source);
+  jbyte* bytes = jni->GetByteArrayElements(j_frame, nullptr);
+  source->OnByteBufferFrameCaptured(bytes, length, width, height, rotation,
+                                    timestamp);
+  jni->ReleaseByteArrayElements(j_frame, bytes, JNI_ABORT);
+}
+
+JOW_OBSERVER_METHOD(void, nativeOnTextureFrameCaptured)
+(JNIEnv* jni,
+ jclass,
+ jlong j_source,
+ jint j_width,
+ jint j_height,
+ jint j_oes_texture_id,
+ jfloatArray j_transform_matrix,
+ jint j_rotation,
+ jlong j_timestamp) {
+  webrtc::AndroidVideoTrackSource* source =
+      AndroidVideoTrackSourceFromJavaProxy(j_source);
+  source->OnTextureFrameCaptured(
+      j_width, j_height, j_rotation, j_timestamp,
+      NativeHandleImpl(jni, j_oes_texture_id, j_transform_matrix));
+}
+
+JOW_OBSERVER_METHOD(void, nativeCapturerStarted)
+(JNIEnv* jni, jclass, jlong j_source, jboolean j_success) {
+  LOG(LS_INFO) << "AndroidVideoTrackSourceObserve_nativeCapturerStarted";
+  webrtc::AndroidVideoTrackSource* source =
+      AndroidVideoTrackSourceFromJavaProxy(j_source);
+  source->SetState(webrtc::AndroidVideoTrackSource::SourceState::kLive);
+}
+
+JOW_OBSERVER_METHOD(void, nativeCapturerStopped)
+(JNIEnv* jni, jclass, jlong j_source) {
+  LOG(LS_INFO) << "AndroidVideoTrackSourceObserve_nativeCapturerStopped";
+  webrtc::AndroidVideoTrackSource* source =
+      AndroidVideoTrackSourceFromJavaProxy(j_source);
+  source->SetState(webrtc::AndroidVideoTrackSource::SourceState::kEnded);
+}
+
+JOW_OBSERVER_METHOD(void, nativeOnOutputFormatRequest)
+(JNIEnv* jni, jclass, jlong j_source, jint j_width, jint j_height, jint j_fps) {
+  LOG(LS_INFO) << "AndroidVideoTrackSourceObserve_nativeOnOutputFormatRequest";
+  webrtc::AndroidVideoTrackSource* source =
+      AndroidVideoTrackSourceFromJavaProxy(j_source);
+  source->OnOutputFormatRequest(j_width, j_height, j_fps);
+}
+
+}  // namespace webrtc_jni
diff --git a/webrtc/api/android/jni/peerconnection_jni.cc b/webrtc/api/android/jni/peerconnection_jni.cc
index fefb447..8b5a692 100644
--- a/webrtc/api/android/jni/peerconnection_jni.cc
+++ b/webrtc/api/android/jni/peerconnection_jni.cc
@@ -44,6 +44,7 @@
 #include <utility>
 
 #include "webrtc/api/androidvideocapturer.h"
+#include "webrtc/api/androidvideotracksource.h"
 #include "webrtc/api/android/jni/androidmediadecoder_jni.h"
 #include "webrtc/api/android/jni/androidmediaencoder_jni.h"
 #include "webrtc/api/android/jni/androidnetworkmonitor_jni.h"
@@ -55,6 +56,7 @@
 #include "webrtc/api/peerconnectioninterface.h"
 #include "webrtc/api/rtpreceiverinterface.h"
 #include "webrtc/api/rtpsenderinterface.h"
+#include "webrtc/api/videosourceproxy.h"
 #include "webrtc/api/webrtcsdp.h"
 #include "webrtc/base/bind.h"
 #include "webrtc/base/checks.h"
@@ -116,6 +118,7 @@
 // Set in PeerConnectionFactory_initializeAndroidGlobals().
 static bool factory_static_initialized = false;
 static bool video_hw_acceleration_enabled = true;
+static jobject j_application_context = nullptr;
 
 // Return the (singleton) Java Enum object corresponding to |index|;
 // |state_class_fragment| is something like "MediaSource$State".
@@ -931,7 +934,7 @@
 }
 
 JOW(void, MediaSource_free)(JNIEnv*, jclass, jlong j_p) {
-  CHECK_RELEASE(reinterpret_cast<MediaSourceInterface*>(j_p));
+  reinterpret_cast<rtc::RefCountInterface*>(j_p)->Release();
 }
 
 JOW(void, VideoRenderer_freeWrappedVideoRenderer)(JNIEnv*, jclass, jlong j_p) {
@@ -985,14 +988,20 @@
   return (jlong)new PCOJava(jni, j_observer);
 }
 
-JOW(jboolean, PeerConnectionFactory_initializeAndroidGlobals)(
-    JNIEnv* jni, jclass, jobject context,
-    jboolean initialize_audio, jboolean initialize_video,
-    jboolean video_hw_acceleration) {
+JOW(jboolean, PeerConnectionFactory_initializeAndroidGlobals)
+(JNIEnv* jni,
+ jclass,
+ jobject context,
+ jboolean initialize_audio,
+ jboolean initialize_video,
+ jboolean video_hw_acceleration) {
   bool failure = false;
   video_hw_acceleration_enabled = video_hw_acceleration;
   AndroidNetworkMonitor::SetAndroidContext(jni, context);
   if (!factory_static_initialized) {
+    RTC_DCHECK(j_application_context == nullptr);
+    j_application_context = NewGlobalRef(jni, context);
+
     if (initialize_video) {
       failure |= AndroidVideoCapturerJni::SetAndroidObjects(jni, context);
     }
@@ -1075,6 +1084,8 @@
   }
 
   PeerConnectionFactoryInterface* factory() { return factory_; }
+  Thread* signaling_thread() { return signaling_thread_.get(); }
+  Thread* worker_thread() { return worker_thread_.get(); }
   WebRtcVideoEncoderFactory* encoder_factory() { return encoder_factory_; }
   WebRtcVideoDecoderFactory* decoder_factory() { return decoder_factory_; }
   rtc::NetworkMonitorFactory* network_monitor_factory() {
@@ -1270,6 +1281,49 @@
   return (jlong)source.release();
 }
 
+JOW(jlong, PeerConnectionFactory_nativeCreateVideoSource2)
+(JNIEnv* jni, jclass, jlong native_factory, jobject j_egl_context) {
+  OwnedFactoryAndThreads* factory =
+      reinterpret_cast<OwnedFactoryAndThreads*>(native_factory);
+
+  rtc::scoped_refptr<webrtc::AndroidVideoTrackSource> source(
+      new rtc::RefCountedObject<webrtc::AndroidVideoTrackSource>(
+          factory->signaling_thread(), jni, j_egl_context));
+  rtc::scoped_refptr<webrtc::VideoTrackSourceProxy> proxy_source =
+      webrtc::VideoTrackSourceProxy::Create(factory->signaling_thread(),
+                                            factory->worker_thread(), source);
+
+  return (jlong)proxy_source.release();
+}
+
+JOW(void, PeerConnectionFactory_nativeInitializeVideoCapturer)
+(JNIEnv* jni,
+ jclass,
+ jlong native_factory,
+ jobject j_video_capturer,
+ jlong native_source,
+ jobject j_frame_observer) {
+  LOG(LS_INFO) << "PeerConnectionFactory_nativeInitializeVideoCapturer";
+  rtc::scoped_refptr<PeerConnectionFactoryInterface> factory(
+      factoryFromJava(native_factory));
+  auto proxy_source =
+      reinterpret_cast<webrtc::VideoTrackSourceProxy*>(native_source);
+  auto source = reinterpret_cast<webrtc::AndroidVideoTrackSource*>(
+      proxy_source->internal());
+  rtc::scoped_refptr<SurfaceTextureHelper> surface_texture_helper =
+      source->surface_texture_helper();
+  jni->CallVoidMethod(
+      j_video_capturer,
+      GetMethodID(jni, FindClass(jni, "org/webrtc/VideoCapturer"), "initialize",
+                  "(Lorg/webrtc/SurfaceTextureHelper;Landroid/content/"
+                  "Context;Lorg/webrtc/VideoCapturer$CapturerObserver;)V"),
+      surface_texture_helper
+          ? surface_texture_helper->GetJavaSurfaceTextureHelper()
+          : nullptr,
+      j_application_context, j_frame_observer);
+  CHECK_EXCEPTION(jni) << "error during VideoCapturer.initialize()";
+}
+
 JOW(jlong, PeerConnectionFactory_nativeCreateVideoTrack)(
     JNIEnv* jni, jclass, jlong native_factory, jstring id,
     jlong native_source) {
diff --git a/webrtc/api/androidtests/src/org/webrtc/CameraVideoCapturerTestFixtures.java b/webrtc/api/androidtests/src/org/webrtc/CameraVideoCapturerTestFixtures.java
index 729e99a..4c933db 100644
--- a/webrtc/api/androidtests/src/org/webrtc/CameraVideoCapturerTestFixtures.java
+++ b/webrtc/api/androidtests/src/org/webrtc/CameraVideoCapturerTestFixtures.java
@@ -105,6 +105,11 @@
     }
 
     @Override
+    public void onCapturerStopped() {
+      Logging.d(TAG, "onCapturerStopped");
+    }
+
+    @Override
     public void onByteBufferFrameCaptured(byte[] frame, int width, int height, int rotation,
         long timeStamp) {
       synchronized (frameLock) {
diff --git a/webrtc/api/androidvideotracksource.cc b/webrtc/api/androidvideotracksource.cc
new file mode 100644
index 0000000..c52e4f8
--- /dev/null
+++ b/webrtc/api/androidvideotracksource.cc
@@ -0,0 +1,260 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/api/androidvideotracksource.h"
+
+#include <utility>
+
+namespace webrtc {
+
+AndroidVideoTrackSource::AndroidVideoTrackSource(rtc::Thread* signaling_thread,
+                                                 JNIEnv* jni,
+                                                 jobject j_egl_context)
+    : signaling_thread_(signaling_thread),
+      surface_texture_helper_(webrtc_jni::SurfaceTextureHelper::create(
+          jni,
+          "Camera SurfaceTextureHelper",
+          j_egl_context)) {
+  LOG(LS_INFO) << "AndroidVideoTrackSource ctor";
+  worker_thread_checker_.DetachFromThread();
+  camera_thread_checker_.DetachFromThread();
+}
+
+bool AndroidVideoTrackSource::GetStats(AndroidVideoTrackSource::Stats* stats) {
+  rtc::CritScope lock(&stats_crit_);
+
+  if (!stats_) {
+    return false;
+  }
+
+  *stats = *stats_;
+  return true;
+}
+
+void AndroidVideoTrackSource::SetState(SourceState state) {
+  if (rtc::Thread::Current() != signaling_thread_) {
+    invoker_.AsyncInvoke<void>(
+        RTC_FROM_HERE, signaling_thread_,
+        rtc::Bind(&AndroidVideoTrackSource::SetState, this, state));
+    return;
+  }
+
+  if (state_ != state) {
+    state_ = state;
+    FireOnChanged();
+  }
+}
+
+void AndroidVideoTrackSource::AddOrUpdateSink(
+    rtc::VideoSinkInterface<cricket::VideoFrame>* sink,
+    const rtc::VideoSinkWants& wants) {
+  RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
+
+  broadcaster_.AddOrUpdateSink(sink, wants);
+  OnSinkWantsChanged(broadcaster_.wants());
+}
+
+void AndroidVideoTrackSource::RemoveSink(
+    rtc::VideoSinkInterface<cricket::VideoFrame>* sink) {
+  RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
+
+  broadcaster_.RemoveSink(sink);
+  OnSinkWantsChanged(broadcaster_.wants());
+}
+
+void AndroidVideoTrackSource::OnSinkWantsChanged(
+    const rtc::VideoSinkWants& wants) {
+  {
+    rtc::CritScope lock(&apply_rotation_crit_);
+    apply_rotation_ = wants.rotation_applied;
+  }
+
+  video_adapter_.OnResolutionRequest(wants.max_pixel_count,
+                                     wants.max_pixel_count_step_up);
+}
+
+void AndroidVideoTrackSource::OnByteBufferFrameCaptured(const void* frame_data,
+                                                        int length,
+                                                        int width,
+                                                        int height,
+                                                        int rotation,
+                                                        int64_t timestamp_ns) {
+  RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
+  RTC_DCHECK(rotation == 0 || rotation == 90 || rotation == 180 ||
+             rotation == 270);
+
+  int adapted_width;
+  int adapted_height;
+  int crop_width;
+  int crop_height;
+  int crop_x;
+  int crop_y;
+  int64_t translated_camera_time_us;
+
+  if (!AdaptFrame(width, height, timestamp_ns / rtc::kNumNanosecsPerMicrosec,
+                  &adapted_width, &adapted_height, &crop_width, &crop_height,
+                  &crop_x, &crop_y, &translated_camera_time_us)) {
+    return;
+  }
+
+  int rotated_width = crop_width;
+  int rotated_height = crop_height;
+
+  rtc::CritScope lock(&apply_rotation_crit_);
+  if (apply_rotation_ && (rotation == 90 || rotation == 270)) {
+    std::swap(adapted_width, adapted_height);
+    std::swap(rotated_width, rotated_height);
+  }
+
+  rtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer =
+      pre_scale_pool_.CreateBuffer(rotated_width, rotated_height);
+
+  const uint8_t* y_plane = static_cast<const uint8_t*>(frame_data);
+  const uint8_t* uv_plane = y_plane + width * height;
+  int uv_width = (width + 1) / 2;
+
+  RTC_CHECK_GE(length, width * height + 2 * uv_width * ((height + 1) / 2));
+
+  // Can only crop at even pixels.
+  crop_x &= ~1;
+  crop_y &= ~1;
+
+  libyuv::NV12ToI420Rotate(
+      y_plane + width * crop_y + crop_x, width,
+      uv_plane + uv_width * crop_y + crop_x, width, buffer->MutableDataY(),
+      buffer->StrideY(),
+      // Swap U and V, since we have NV21, not NV12.
+      buffer->MutableDataV(), buffer->StrideV(), buffer->MutableDataU(),
+      buffer->StrideU(), crop_width, crop_height,
+      static_cast<libyuv::RotationMode>(apply_rotation_ ? rotation : 0));
+
+  if (adapted_width != buffer->width() || adapted_height != buffer->height()) {
+    rtc::scoped_refptr<webrtc::I420Buffer> scaled_buffer(
+        post_scale_pool_.CreateBuffer(adapted_width, adapted_height));
+    scaled_buffer->ScaleFrom(buffer);
+    buffer = scaled_buffer;
+  }
+
+  OnFrame(cricket::WebRtcVideoFrame(
+              buffer,
+              apply_rotation_ ? webrtc::kVideoRotation_0
+                              : static_cast<webrtc::VideoRotation>(rotation),
+              translated_camera_time_us),
+          width, height);
+}
+
+void AndroidVideoTrackSource::OnTextureFrameCaptured(
+    int width,
+    int height,
+    int rotation,
+    int64_t timestamp_ns,
+    const webrtc_jni::NativeHandleImpl& handle) {
+  RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
+  RTC_DCHECK(rotation == 0 || rotation == 90 || rotation == 180 ||
+             rotation == 270);
+
+  int adapted_width;
+  int adapted_height;
+  int crop_width;
+  int crop_height;
+  int crop_x;
+  int crop_y;
+  int64_t translated_camera_time_us;
+
+  if (!AdaptFrame(width, height, timestamp_ns / rtc::kNumNanosecsPerMicrosec,
+                  &adapted_width, &adapted_height, &crop_width, &crop_height,
+                  &crop_x, &crop_y, &translated_camera_time_us)) {
+    surface_texture_helper_->ReturnTextureFrame();
+    return;
+  }
+
+  webrtc_jni::Matrix matrix = handle.sampling_matrix;
+
+  matrix.Crop(crop_width / static_cast<float>(width),
+              crop_height / static_cast<float>(height),
+              crop_x / static_cast<float>(width),
+              crop_y / static_cast<float>(height));
+
+  rtc::CritScope lock(&apply_rotation_crit_);
+  if (apply_rotation_) {
+    if (rotation == webrtc::kVideoRotation_90 ||
+        rotation == webrtc::kVideoRotation_270) {
+      std::swap(adapted_width, adapted_height);
+    }
+    matrix.Rotate(static_cast<webrtc::VideoRotation>(rotation));
+  }
+
+  OnFrame(cricket::WebRtcVideoFrame(
+              surface_texture_helper_->CreateTextureFrame(
+                  adapted_width, adapted_height,
+                  webrtc_jni::NativeHandleImpl(handle.oes_texture_id, matrix)),
+              apply_rotation_ ? webrtc::kVideoRotation_0
+                              : static_cast<webrtc::VideoRotation>(rotation),
+              translated_camera_time_us),
+          width, height);
+}
+
+void AndroidVideoTrackSource::OnFrame(const cricket::VideoFrame& frame,
+                                      int width,
+                                      int height) {
+  {
+    rtc::CritScope lock(&stats_crit_);
+    stats_ = rtc::Optional<AndroidVideoTrackSource::Stats>({width, height});
+  }
+
+  broadcaster_.OnFrame(frame);
+}
+
+void AndroidVideoTrackSource::OnOutputFormatRequest(int width,
+                                                    int height,
+                                                    int fps) {
+  RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
+
+  cricket::VideoFormat format(width, height,
+                              cricket::VideoFormat::FpsToInterval(fps), 0);
+  video_adapter_.OnOutputFormatRequest(format);
+}
+
+bool AndroidVideoTrackSource::AdaptFrame(int width,
+                                         int height,
+                                         int64_t camera_time_us,
+                                         int* out_width,
+                                         int* out_height,
+                                         int* crop_width,
+                                         int* crop_height,
+                                         int* crop_x,
+                                         int* crop_y,
+                                         int64_t* translated_camera_time_us) {
+  RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
+
+  int64_t system_time_us = rtc::TimeMicros();
+
+  int64_t offset_us =
+      timestamp_aligner_.UpdateOffset(camera_time_us, system_time_us);
+
+  if (!broadcaster_.frame_wanted()) {
+    return false;
+  }
+
+  if (!video_adapter_.AdaptFrameResolution(
+          width, height, camera_time_us * rtc::kNumNanosecsPerMicrosec,
+          crop_width, crop_height, out_width, out_height)) {
+    // VideoAdapter dropped the frame.
+    return false;
+  }
+  *crop_x = (width - *crop_width) / 2;
+  *crop_y = (height - *crop_height) / 2;
+
+  *translated_camera_time_us = timestamp_aligner_.ClipTimestamp(
+      camera_time_us + offset_us, system_time_us);
+  return true;
+}
+
+}  // namespace webrtc
diff --git a/webrtc/api/androidvideotracksource.h b/webrtc/api/androidvideotracksource.h
new file mode 100644
index 0000000..cc6ead4
--- /dev/null
+++ b/webrtc/api/androidvideotracksource.h
@@ -0,0 +1,124 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_API_ANDROIDVIDEOTRACKSOURCE_H_
+#define WEBRTC_API_ANDROIDVIDEOTRACKSOURCE_H_
+
+#include "webrtc/api/android/jni/native_handle_impl.h"
+#include "webrtc/api/android/jni/surfacetexturehelper_jni.h"
+#include "webrtc/api/mediastreaminterface.h"
+#include "webrtc/api/notifier.h"
+#include "webrtc/base/asyncinvoker.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/thread_checker.h"
+#include "webrtc/base/timestampaligner.h"
+#include "webrtc/common_video/include/i420_buffer_pool.h"
+#include "webrtc/media/base/videoadapter.h"
+#include "webrtc/media/base/videobroadcaster.h"
+#include "webrtc/media/base/videosinkinterface.h"
+#include "third_party/libyuv/include/libyuv/convert.h"
+
+namespace webrtc {
+
+class AndroidVideoTrackSource : public Notifier<VideoTrackSourceInterface> {
+ public:
+  AndroidVideoTrackSource(rtc::Thread* signaling_thread,
+                          JNIEnv* jni,
+                          jobject j_egl_context);
+
+  // Not used on Android.
+  // TODO(sakal/magjed): Try to remove this from the interface.
+  void Stop() override { RTC_NOTREACHED(); };
+  // Not used on Android.
+  // TODO(sakal/magjed): Try to remove this from the interface.
+  void Restart() override { RTC_NOTREACHED(); }
+
+  // Currently, none of the Android implementations are screencast.
+  bool is_screencast() const override { return false; }
+
+  // Indicates that the encoder should denoise video before encoding it.
+  // If it is not set, the default configuration is used which is different
+  // depending on video codec.
+  rtc::Optional<bool> needs_denoising() const override {
+    return rtc::Optional<bool>(false);
+  }
+
+  // Returns false if no stats are available, e.g, for a remote
+  // source, or a source which has not seen its first frame yet.
+  // Should avoid blocking.
+  bool GetStats(Stats* stats) override;
+
+  // Called by the native capture observer
+  void SetState(SourceState state);
+
+  SourceState state() const override { return state_; }
+
+  bool remote() const override { return false; }
+
+  void AddOrUpdateSink(rtc::VideoSinkInterface<cricket::VideoFrame>* sink,
+                       const rtc::VideoSinkWants& wants) override;
+  void RemoveSink(rtc::VideoSinkInterface<cricket::VideoFrame>* sink) override;
+
+  void OnByteBufferFrameCaptured(const void* frame_data,
+                                 int length,
+                                 int width,
+                                 int height,
+                                 int rotation,
+                                 int64_t timestamp_ns);
+
+  void OnTextureFrameCaptured(int width,
+                              int height,
+                              int rotation,
+                              int64_t timestamp_ns,
+                              const webrtc_jni::NativeHandleImpl& handle);
+
+  void OnOutputFormatRequest(int width, int height, int fps);
+
+  rtc::scoped_refptr<webrtc_jni::SurfaceTextureHelper>
+  surface_texture_helper() {
+    return surface_texture_helper_;
+  }
+
+ private:
+  rtc::Thread* signaling_thread_;
+  rtc::AsyncInvoker invoker_;
+  rtc::ThreadChecker worker_thread_checker_;
+  rtc::ThreadChecker camera_thread_checker_;
+  rtc::CriticalSection stats_crit_;
+  rtc::Optional<Stats> stats_ GUARDED_BY(stats_crit_);
+  SourceState state_;
+  rtc::VideoBroadcaster broadcaster_;
+  rtc::TimestampAligner timestamp_aligner_;
+  cricket::VideoAdapter video_adapter_;
+  rtc::CriticalSection apply_rotation_crit_;
+  bool apply_rotation_ GUARDED_BY(apply_rotation_crit_);
+  webrtc::I420BufferPool pre_scale_pool_;
+  webrtc::I420BufferPool post_scale_pool_;
+  rtc::scoped_refptr<webrtc_jni::SurfaceTextureHelper> surface_texture_helper_;
+
+  void OnFrame(const cricket::VideoFrame& frame, int width, int height);
+
+  void OnSinkWantsChanged(const rtc::VideoSinkWants& wants);
+
+  bool AdaptFrame(int width,
+                  int height,
+                  int64_t camera_time_us,
+                  int* out_width,
+                  int* out_height,
+                  int* crop_width,
+                  int* crop_height,
+                  int* crop_x,
+                  int* crop_y,
+                  int64_t* translated_camera_time_us);
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_API_ANDROIDVIDEOTRACKSOURCE_H_
diff --git a/webrtc/api/api.gyp b/webrtc/api/api.gyp
index 15c88dd..86a55fd 100644
--- a/webrtc/api/api.gyp
+++ b/webrtc/api/api.gyp
@@ -45,6 +45,7 @@
             'android/jni/androidnetworkmonitor_jni.h',
             'android/jni/androidvideocapturer_jni.cc',
             'android/jni/androidvideocapturer_jni.h',
+            'android/jni/androidvideotracksource_jni.cc',
             'android/jni/classreferenceholder.cc',
             'android/jni/classreferenceholder.h',
             'android/jni/jni_helpers.cc',
@@ -56,6 +57,8 @@
             'android/jni/surfacetexturehelper_jni.h',
             'androidvideocapturer.cc',
             'androidvideocapturer.h',
+            'androidvideotracksource.cc',
+            'androidvideotracksource.h',
           ],
           'include_dirs': [
             '<(libyuv_dir)/include',
diff --git a/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java b/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
index 0f7ff35..f12d5ce 100644
--- a/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
+++ b/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
@@ -107,14 +107,16 @@
   private boolean videoCallEnabled;
   private boolean preferIsac;
   private String preferredVideoCodec;
-  private boolean videoSourceStopped;
+  private boolean videoCapturerStopped;
   private boolean isError;
   private Timer statsTimer;
   private VideoRenderer.Callbacks localRender;
   private VideoRenderer.Callbacks remoteRender;
   private SignalingParameters signalingParameters;
   private MediaConstraints pcConstraints;
-  private MediaConstraints videoConstraints;
+  private int videoWidth;
+  private int videoHeight;
+  private int videoFps;
   private MediaConstraints audioConstraints;
   private ParcelFileDescriptor aecDumpFileDescriptor;
   private MediaConstraints sdpMediaConstraints;
@@ -260,7 +262,7 @@
     factory = null;
     peerConnection = null;
     preferIsac = false;
-    videoSourceStopped = false;
+    videoCapturerStopped = false;
     isError = false;
     queuedRemoteCandidates = null;
     localSdp = null; // either offer or answer SDP
@@ -399,42 +401,24 @@
     }
     // Create video constraints if video call is enabled.
     if (videoCallEnabled) {
-      videoConstraints = new MediaConstraints();
-      int videoWidth = peerConnectionParameters.videoWidth;
-      int videoHeight = peerConnectionParameters.videoHeight;
+      videoWidth = peerConnectionParameters.videoWidth;
+      videoHeight = peerConnectionParameters.videoHeight;
+      videoFps = peerConnectionParameters.videoFps;
 
-      // If VP8 HW video encoder is supported and video resolution is not
-      // specified force it to HD.
-      if ((videoWidth == 0 || videoHeight == 0)
-          && peerConnectionParameters.videoCodecHwAcceleration
-          && MediaCodecVideoEncoder.isVp8HwSupported()) {
+      // If video resolution is not specified, default to HD.
+      if (videoWidth == 0 || videoHeight == 0) {
         videoWidth = HD_VIDEO_WIDTH;
         videoHeight = HD_VIDEO_HEIGHT;
       }
 
-      // Add video resolution constraints.
-      if (videoWidth > 0 && videoHeight > 0) {
-        videoWidth = Math.min(videoWidth, MAX_VIDEO_WIDTH);
-        videoHeight = Math.min(videoHeight, MAX_VIDEO_HEIGHT);
-        videoConstraints.mandatory.add(new KeyValuePair(
-            MIN_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
-        videoConstraints.mandatory.add(new KeyValuePair(
-            MAX_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
-        videoConstraints.mandatory.add(new KeyValuePair(
-            MIN_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
-        videoConstraints.mandatory.add(new KeyValuePair(
-            MAX_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
+      // If fps is not specified, default to 30.
+      if (videoFps == 0) {
+        videoFps = 30;
       }
 
-      // Add fps constraints.
-      int videoFps = peerConnectionParameters.videoFps;
-      if (videoFps > 0) {
-        videoFps = Math.min(videoFps, MAX_VIDEO_FPS);
-        videoConstraints.mandatory.add(new KeyValuePair(
-            MIN_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
-        videoConstraints.mandatory.add(new KeyValuePair(
-            MAX_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
-      }
+      videoWidth = Math.min(videoWidth, MAX_VIDEO_WIDTH);
+      videoHeight = Math.min(videoHeight, MAX_VIDEO_HEIGHT);
+      videoFps = Math.min(videoFps, MAX_VIDEO_FPS);
     }
 
     // Create audio constraints.
@@ -502,9 +486,6 @@
     Log.d(TAG, "Create peer connection.");
 
     Log.d(TAG, "PCConstraints: " + pcConstraints.toString());
-    if (videoConstraints != null) {
-      Log.d(TAG, "VideoConstraints: " + videoConstraints.toString());
-    }
     queuedRemoteCandidates = new LinkedList<IceCandidate>();
 
     if (videoCallEnabled) {
@@ -592,6 +573,16 @@
       audioSource.dispose();
       audioSource = null;
     }
+    Log.d(TAG, "Stopping capture.");
+    if (videoCapturer != null) {
+      try {
+        videoCapturer.stopCapture();
+      } catch(InterruptedException e) {
+        throw new RuntimeException(e);
+      }
+      videoCapturer.dispose();
+      videoCapturer = null;
+    }
     Log.d(TAG, "Closing video source.");
     if (videoSource != null) {
       videoSource.dispose();
@@ -613,24 +604,8 @@
     if (!videoCallEnabled) {
       return false;
     }
-    int minWidth = 0;
-    int minHeight = 0;
-    for (KeyValuePair keyValuePair : videoConstraints.mandatory) {
-      if (keyValuePair.getKey().equals("minWidth")) {
-        try {
-          minWidth = Integer.parseInt(keyValuePair.getValue());
-        } catch (NumberFormatException e) {
-          Log.e(TAG, "Can not parse video width from video constraints");
-        }
-      } else if (keyValuePair.getKey().equals("minHeight")) {
-        try {
-          minHeight = Integer.parseInt(keyValuePair.getValue());
-        } catch (NumberFormatException e) {
-          Log.e(TAG, "Can not parse video height from video constraints");
-        }
-      }
-    }
-    return minWidth * minHeight >= 1280 * 720;
+
+    return videoWidth * videoHeight >= 1280 * 720;
   }
 
   private void getStats() {
@@ -791,10 +766,12 @@
     executor.execute(new Runnable() {
       @Override
       public void run() {
-        if (videoSource != null && !videoSourceStopped) {
+        if (videoCapturer != null && !videoCapturerStopped) {
           Log.d(TAG, "Stop video source.");
-          videoSource.stop();
-          videoSourceStopped = true;
+          try {
+            videoCapturer.stopCapture();
+          } catch (InterruptedException e) {}
+          videoCapturerStopped = true;
         }
       }
     });
@@ -804,10 +781,10 @@
     executor.execute(new Runnable() {
       @Override
       public void run() {
-        if (videoSource != null && videoSourceStopped) {
+        if (videoCapturer != null && videoCapturerStopped) {
           Log.d(TAG, "Restart video source.");
-          videoSource.restart();
-          videoSourceStopped = false;
+          videoCapturer.startCapture(videoWidth, videoHeight, videoFps);
+          videoCapturerStopped = false;
         }
       }
     });
@@ -834,7 +811,8 @@
   }
 
   private VideoTrack createVideoTrack(VideoCapturer capturer) {
-    videoSource = factory.createVideoSource(capturer, videoConstraints);
+    videoSource = factory.createVideoSource(capturer);
+    capturer.startCapture(videoWidth, videoHeight, videoFps);
 
     localVideoTrack = factory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
     localVideoTrack.setEnabled(renderVideo);