Introduce class that handles native wrapping of AndroidVideoTrackSource
This CL attempts to do separation of concerns by introducing a simple
class that only handles JNI wrapping of a C++ AndroidVideoTrackSource.
This layer can be easiliy mocked out in Java unit tests.
Bug: webrtc:10247
Change-Id: Idbdbfde6d3e00b64f3f310f76505801fa496580d
Reviewed-on: https://webrtc-review.googlesource.com/c/121562
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26556}
diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn
index f666c3d..0e603e4 100644
--- a/sdk/android/BUILD.gn
+++ b/sdk/android/BUILD.gn
@@ -327,6 +327,7 @@
"api/org/webrtc/VideoTrack.java",
"src/java/org/webrtc/NativeLibrary.java",
"src/java/org/webrtc/NativeCapturerObserver.java",
+ "src/java/org/webrtc/NativeAndroidVideoTrackSource.java",
]
deps = [
@@ -662,7 +663,6 @@
"src/jni/video_encoder_wrapper.h",
"src/jni/video_sink.cc",
"src/jni/video_sink.h",
- "src/jni/video_source.cc",
"src/jni/video_track.cc",
"src/jni/yuv_helper.cc",
]
@@ -1256,12 +1256,12 @@
"api/org/webrtc/VideoEncoderFallback.java",
"api/org/webrtc/VideoFrame.java",
"api/org/webrtc/VideoSink.java",
- "api/org/webrtc/VideoSource.java",
"api/org/webrtc/VideoTrack.java",
"api/org/webrtc/YuvHelper.java",
"src/java/org/webrtc/H264Utils.java",
"src/java/org/webrtc/NV12Buffer.java",
"src/java/org/webrtc/NV21Buffer.java",
+ "src/java/org/webrtc/NativeAndroidVideoTrackSource.java",
"src/java/org/webrtc/NativeCapturerObserver.java",
"src/java/org/webrtc/VideoDecoderWrapper.java",
"src/java/org/webrtc/VideoEncoderWrapper.java",
diff --git a/sdk/android/api/org/webrtc/VideoSource.java b/sdk/android/api/org/webrtc/VideoSource.java
index 995304e..f783a57 100644
--- a/sdk/android/api/org/webrtc/VideoSource.java
+++ b/sdk/android/api/org/webrtc/VideoSource.java
@@ -16,11 +16,40 @@
* Java wrapper of native AndroidVideoTrackSource.
*/
public class VideoSource extends MediaSource {
- private final NativeCapturerObserver capturerObserver;
+ /** Simple aspect ratio clas for use in constraining output format. */
+ public static class AspectRatio {
+ public static final AspectRatio UNDEFINED = new AspectRatio(/* width= */ 0, /* height= */ 0);
+
+ public final int width;
+ public final int height;
+
+ public AspectRatio(int width, int height) {
+ this.width = width;
+ this.height = height;
+ }
+ }
+
+ private final NativeAndroidVideoTrackSource nativeAndroidVideoTrackSource;
+ private final CapturerObserver capturerObserver = new CapturerObserver() {
+ @Override
+ public void onCapturerStarted(boolean success) {
+ nativeAndroidVideoTrackSource.setState(success);
+ }
+
+ @Override
+ public void onCapturerStopped() {
+ nativeAndroidVideoTrackSource.setState(/* isLive= */ false);
+ }
+
+ @Override
+ public void onFrameCaptured(VideoFrame frame) {
+ nativeAndroidVideoTrackSource.onFrameCaptured(frame);
+ }
+ };
public VideoSource(long nativeSource) {
super(nativeSource);
- this.capturerObserver = new NativeCapturerObserver(nativeSource);
+ this.nativeAndroidVideoTrackSource = new NativeAndroidVideoTrackSource(nativeSource);
}
/**
@@ -42,8 +71,18 @@
*/
public void adaptOutputFormat(
int landscapeWidth, int landscapeHeight, int portraitWidth, int portraitHeight, int fps) {
- nativeAdaptOutputFormat(getNativeVideoTrackSource(), landscapeWidth, landscapeHeight,
- portraitWidth, portraitHeight, fps);
+ adaptOutputFormat(new AspectRatio(landscapeWidth, landscapeHeight),
+ /* maxLandscapePixelCount= */ landscapeWidth * landscapeHeight,
+ new AspectRatio(portraitWidth, portraitHeight),
+ /* maxPortraitPixelCount= */ portraitWidth * portraitHeight, fps);
+ }
+
+ /** Same as above, with even more control as each constraint is optional. */
+ public void adaptOutputFormat(AspectRatio targetLandscapeAspectRatio,
+ @Nullable Integer maxLandscapePixelCount, AspectRatio targetPortraitAspectRatio,
+ @Nullable Integer maxPortraitPixelCount, @Nullable Integer maxFps) {
+ nativeAndroidVideoTrackSource.adaptOutputFormat(targetLandscapeAspectRatio,
+ maxLandscapePixelCount, targetPortraitAspectRatio, maxPortraitPixelCount, maxFps);
}
public CapturerObserver getCapturerObserver() {
@@ -54,7 +93,4 @@
long getNativeVideoTrackSource() {
return getNativeMediaSource();
}
-
- private static native void nativeAdaptOutputFormat(long source, int landscapeWidth,
- int landscapeHeight, int portraitWidth, int portraitHeight, int fps);
}
diff --git a/sdk/android/src/java/org/webrtc/NativeAndroidVideoTrackSource.java b/sdk/android/src/java/org/webrtc/NativeAndroidVideoTrackSource.java
new file mode 100644
index 0000000..da963a8
--- /dev/null
+++ b/sdk/android/src/java/org/webrtc/NativeAndroidVideoTrackSource.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+package org.webrtc;
+
+import android.support.annotation.Nullable;
+import org.webrtc.VideoFrame;
+
+/**
+ * This class is meant to be a simple layer that only handles the JNI wrapping of a C++
+ * AndroidVideoTrackSource, that can easily be mocked out in Java unit tests. Refrain from adding
+ * any unnecessary logic to this class.
+ */
+class NativeAndroidVideoTrackSource {
+ // Pointer to webrtc::jni::AndroidVideoTrackSource.
+ private final long nativeAndroidVideoTrackSource;
+
+ public NativeAndroidVideoTrackSource(long nativeAndroidVideoTrackSource) {
+ this.nativeAndroidVideoTrackSource = nativeAndroidVideoTrackSource;
+ }
+
+ /**
+ * Set the state for the native MediaSourceInterface. Maps boolean to either
+ * SourceState::kLive or SourceState::kEnded.
+ */
+ public void setState(boolean isLive) {
+ nativeSetState(nativeAndroidVideoTrackSource, isLive);
+ }
+
+ /** Pass a frame to the native AndroidVideoTrackSource. */
+ public void onFrameCaptured(VideoFrame frame) {
+ nativeOnFrameCaptured(nativeAndroidVideoTrackSource, frame.getBuffer().getWidth(),
+ frame.getBuffer().getHeight(), frame.getRotation(), frame.getTimestampNs(),
+ frame.getBuffer());
+ }
+
+ /**
+ * Calling this function will cause frames to be scaled down to the requested resolution. Also,
+ * frames will be cropped to match the requested aspect ratio, and frames will be dropped to match
+ * the requested fps.
+ */
+ public void adaptOutputFormat(VideoSource.AspectRatio targetLandscapeAspectRatio,
+ @Nullable Integer maxLandscapePixelCount, VideoSource.AspectRatio targetPortraitAspectRatio,
+ @Nullable Integer maxPortraitPixelCount, @Nullable Integer maxFps) {
+ nativeAdaptOutputFormat(nativeAndroidVideoTrackSource, targetLandscapeAspectRatio.width,
+ targetLandscapeAspectRatio.height, maxLandscapePixelCount, targetPortraitAspectRatio.width,
+ targetPortraitAspectRatio.height, maxPortraitPixelCount, maxFps);
+ }
+
+ private static native void nativeSetState(long nativeAndroidVideoTrackSource, boolean isLive);
+ private static native void nativeAdaptOutputFormat(long nativeAndroidVideoTrackSource,
+ int landscapeWidth, int landscapeHeight, @Nullable Integer maxLandscapePixelCount,
+ int portraitWidth, int portraitHeight, @Nullable Integer maxPortraitPixelCount,
+ @Nullable Integer maxFps);
+ private static native void nativeOnFrameCaptured(long nativeAndroidVideoTrackSource, int width,
+ int height, int rotation, long timestampNs, VideoFrame.Buffer buffer);
+}
diff --git a/sdk/android/src/java/org/webrtc/NativeCapturerObserver.java b/sdk/android/src/java/org/webrtc/NativeCapturerObserver.java
index b5f8ae4..69a0d54 100644
--- a/sdk/android/src/java/org/webrtc/NativeCapturerObserver.java
+++ b/sdk/android/src/java/org/webrtc/NativeCapturerObserver.java
@@ -10,40 +10,32 @@
package org.webrtc;
-import android.support.annotation.Nullable;
import org.webrtc.VideoFrame;
/**
- * Implements VideoCapturer.CapturerObserver and feeds frames to
- * webrtc::jni::AndroidVideoTrackSource.
+ * Used from native api and implements a simple VideoCapturer.CapturerObserver that feeds frames to
+ * a webrtc::jni::AndroidVideoTrackSource.
*/
class NativeCapturerObserver implements CapturerObserver {
- // Pointer to webrtc::jni::AndroidVideoTrackSource.
- private final long nativeSource;
+ private final NativeAndroidVideoTrackSource nativeAndroidVideoTrackSource;
@CalledByNative
public NativeCapturerObserver(long nativeSource) {
- this.nativeSource = nativeSource;
+ this.nativeAndroidVideoTrackSource = new NativeAndroidVideoTrackSource(nativeSource);
}
@Override
public void onCapturerStarted(boolean success) {
- nativeCapturerStarted(nativeSource, success);
+ nativeAndroidVideoTrackSource.setState(success);
}
@Override
public void onCapturerStopped() {
- nativeCapturerStopped(nativeSource);
+ nativeAndroidVideoTrackSource.setState(/* isLive= */ false);
}
@Override
public void onFrameCaptured(VideoFrame frame) {
- nativeOnFrameCaptured(nativeSource, frame.getBuffer().getWidth(), frame.getBuffer().getHeight(),
- frame.getRotation(), frame.getTimestampNs(), frame.getBuffer());
+ nativeAndroidVideoTrackSource.onFrameCaptured(frame);
}
-
- private static native void nativeCapturerStarted(long source, boolean success);
- private static native void nativeCapturerStopped(long source);
- private static native void nativeOnFrameCaptured(
- long source, int width, int height, int rotation, long timestampNs, VideoFrame.Buffer frame);
}
diff --git a/sdk/android/src/jni/android_video_track_source.cc b/sdk/android/src/jni/android_video_track_source.cc
index 65b2bef..a9fb10d 100644
--- a/sdk/android/src/jni/android_video_track_source.cc
+++ b/sdk/android/src/jni/android_video_track_source.cc
@@ -10,6 +10,8 @@
#include "sdk/android/src/jni/android_video_track_source.h"
+#include "sdk/android/generated_video_jni/jni/NativeAndroidVideoTrackSource_jni.h"
+
#include <utility>
#include "rtc_base/logging.h"
@@ -20,6 +22,20 @@
namespace {
// MediaCodec wants resolution to be divisible by 2.
const int kRequiredResolutionAlignment = 2;
+
+VideoRotation jintToVideoRotation(jint rotation) {
+ RTC_DCHECK(rotation == 0 || rotation == 90 || rotation == 180 ||
+ rotation == 270);
+ return static_cast<VideoRotation>(rotation);
+}
+
+absl::optional<std::pair<int, int>> OptionalAspectRatio(jint j_width,
+ jint j_height) {
+ if (j_width > 0 && j_height > 0)
+ return std::pair<int, int>(j_width, j_height);
+ return absl::nullopt;
+}
+
} // namespace
AndroidVideoTrackSource::AndroidVideoTrackSource(rtc::Thread* signaling_thread,
@@ -42,11 +58,17 @@
return false;
}
-void AndroidVideoTrackSource::SetState(SourceState state) {
+void AndroidVideoTrackSource::SetState(JNIEnv* env,
+ const JavaRef<jobject>& j_caller,
+ jboolean j_is_live) {
+ InternalSetState(j_is_live ? kLive : kEnded);
+}
+
+void AndroidVideoTrackSource::InternalSetState(SourceState state) {
if (rtc::Thread::Current() != signaling_thread_) {
invoker_.AsyncInvoke<void>(
RTC_FROM_HERE, signaling_thread_,
- rtc::Bind(&AndroidVideoTrackSource::SetState, this, state));
+ rtc::Bind(&AndroidVideoTrackSource::InternalSetState, this, state));
return;
}
@@ -65,13 +87,16 @@
}
void AndroidVideoTrackSource::OnFrameCaptured(
- JNIEnv* jni,
- int width,
- int height,
- int64_t timestamp_ns,
- VideoRotation rotation,
+ JNIEnv* env,
+ const JavaRef<jobject>& j_caller,
+ jint j_width,
+ jint j_height,
+ jint j_rotation,
+ jlong j_timestamp_ns,
const JavaRef<jobject>& j_video_frame_buffer) {
- int64_t camera_time_us = timestamp_ns / rtc::kNumNanosecsPerMicrosec;
+ const VideoRotation rotation = jintToVideoRotation(j_rotation);
+
+ int64_t camera_time_us = j_timestamp_ns / rtc::kNumNanosecsPerMicrosec;
int64_t translated_camera_time_us =
align_timestamps_ ? timestamp_aligner_.TranslateTimestamp(
camera_time_us, rtc::TimeMicros())
@@ -85,14 +110,14 @@
int crop_y;
if (rotation % 180 == 0) {
- if (!AdaptFrame(width, height, camera_time_us, &adapted_width,
+ if (!AdaptFrame(j_width, j_height, camera_time_us, &adapted_width,
&adapted_height, &crop_width, &crop_height, &crop_x,
&crop_y)) {
return;
}
} else {
// Swap all width/height and x/y.
- if (!AdaptFrame(height, width, camera_time_us, &adapted_height,
+ if (!AdaptFrame(j_height, j_width, camera_time_us, &adapted_height,
&adapted_width, &crop_height, &crop_width, &crop_y,
&crop_x)) {
return;
@@ -100,8 +125,8 @@
}
rtc::scoped_refptr<VideoFrameBuffer> buffer =
- AndroidVideoBuffer::Create(jni, j_video_frame_buffer)
- ->CropAndScale(jni, crop_x, crop_y, crop_width, crop_height,
+ AndroidVideoBuffer::Create(env, j_video_frame_buffer)
+ ->CropAndScale(env, crop_x, crop_y, crop_width, crop_height,
adapted_width, adapted_height);
// AdaptedVideoTrackSource handles applying rotation for I420 frames.
@@ -116,16 +141,22 @@
.build());
}
-void AndroidVideoTrackSource::OnOutputFormatRequest(int landscape_width,
- int landscape_height,
- int portrait_width,
- int portrait_height,
- int fps) {
+void AndroidVideoTrackSource::AdaptOutputFormat(
+ JNIEnv* env,
+ const JavaRef<jobject>& j_caller,
+ jint j_landscape_width,
+ jint j_landscape_height,
+ const JavaRef<jobject>& j_max_landscape_pixel_count,
+ jint j_portrait_width,
+ jint j_portrait_height,
+ const JavaRef<jobject>& j_max_portrait_pixel_count,
+ const JavaRef<jobject>& j_max_fps) {
video_adapter()->OnOutputFormatRequest(
- std::make_pair(landscape_width, landscape_height),
- landscape_width * landscape_height,
- std::make_pair(portrait_width, portrait_height),
- portrait_width * portrait_height, fps);
+ OptionalAspectRatio(j_landscape_width, j_landscape_height),
+ JavaToNativeOptionalInt(env, j_max_landscape_pixel_count),
+ OptionalAspectRatio(j_portrait_width, j_portrait_height),
+ JavaToNativeOptionalInt(env, j_max_portrait_pixel_count),
+ JavaToNativeOptionalInt(env, j_max_fps));
}
} // namespace jni
diff --git a/sdk/android/src/jni/android_video_track_source.h b/sdk/android/src/jni/android_video_track_source.h
index 337a93d..5640c1d 100644
--- a/sdk/android/src/jni/android_video_track_source.h
+++ b/sdk/android/src/jni/android_video_track_source.h
@@ -39,27 +39,37 @@
// depending on video codec.
absl::optional<bool> needs_denoising() const override;
- // Called by the native capture observer
void SetState(SourceState state);
SourceState state() const override;
bool remote() const override;
- void OnFrameCaptured(JNIEnv* jni,
- int width,
- int height,
- int64_t timestamp_ns,
- VideoRotation rotation,
+ void OnFrameCaptured(JNIEnv* env,
+ const JavaRef<jobject>& j_caller,
+ jint j_width,
+ jint j_height,
+ jint j_rotation,
+ jlong j_timestamp_ns,
const JavaRef<jobject>& j_video_frame_buffer);
- void OnOutputFormatRequest(int landscape_width,
- int landscape_height,
- int portrait_width,
- int portrait_height,
- int fps);
+ void SetState(JNIEnv* env,
+ const JavaRef<jobject>& j_caller,
+ jboolean j_is_live);
+
+ void AdaptOutputFormat(JNIEnv* env,
+ const JavaRef<jobject>& j_caller,
+ jint j_landscape_width,
+ jint j_landscape_height,
+ const JavaRef<jobject>& j_max_landscape_pixel_count,
+ jint j_portrait_width,
+ jint j_portrait_height,
+ const JavaRef<jobject>& j_max_portrait_pixel_count,
+ const JavaRef<jobject>& j_max_fps);
private:
+ void InternalSetState(SourceState state);
+
rtc::Thread* signaling_thread_;
rtc::AsyncInvoker invoker_;
SourceState state_;
diff --git a/sdk/android/src/jni/native_capturer_observer.cc b/sdk/android/src/jni/native_capturer_observer.cc
index 961f5bf..1babf68 100644
--- a/sdk/android/src/jni/native_capturer_observer.cc
+++ b/sdk/android/src/jni/native_capturer_observer.cc
@@ -18,14 +18,6 @@
namespace webrtc {
namespace jni {
-namespace {
-VideoRotation jintToVideoRotation(jint rotation) {
- RTC_DCHECK(rotation == 0 || rotation == 90 || rotation == 180 ||
- rotation == 270);
- return static_cast<VideoRotation>(rotation);
-}
-} // namespace
-
ScopedJavaLocalRef<jobject> CreateJavaNativeCapturerObserver(
JNIEnv* env,
rtc::scoped_refptr<AndroidVideoTrackSource> native_source) {
@@ -33,40 +25,5 @@
env, NativeToJavaPointer(native_source.release()));
}
-static void JNI_NativeCapturerObserver_OnFrameCaptured(
- JNIEnv* jni,
- jlong j_source,
- jint j_width,
- jint j_height,
- jint j_rotation,
- jlong j_timestamp_ns,
- const JavaParamRef<jobject>& j_video_frame_buffer) {
- AndroidVideoTrackSource* source =
- reinterpret_cast<AndroidVideoTrackSource*>(j_source);
- source->OnFrameCaptured(jni, j_width, j_height, j_timestamp_ns,
- jintToVideoRotation(j_rotation),
- j_video_frame_buffer);
-}
-
-static void JNI_NativeCapturerObserver_CapturerStarted(
- JNIEnv* jni,
- jlong j_source,
- jboolean j_success) {
- RTC_LOG(LS_INFO) << "NativeCapturerObserver_nativeCapturerStarted";
- AndroidVideoTrackSource* source =
- reinterpret_cast<AndroidVideoTrackSource*>(j_source);
- source->SetState(j_success ? AndroidVideoTrackSource::SourceState::kLive
- : AndroidVideoTrackSource::SourceState::kEnded);
-}
-
-static void JNI_NativeCapturerObserver_CapturerStopped(
- JNIEnv* jni,
- jlong j_source) {
- RTC_LOG(LS_INFO) << "NativeCapturerObserver_nativeCapturerStopped";
- AndroidVideoTrackSource* source =
- reinterpret_cast<AndroidVideoTrackSource*>(j_source);
- source->SetState(AndroidVideoTrackSource::SourceState::kEnded);
-}
-
} // namespace jni
} // namespace webrtc
diff --git a/sdk/android/src/jni/video_source.cc b/sdk/android/src/jni/video_source.cc
deleted file mode 100644
index 0c41fd8..0000000
--- a/sdk/android/src/jni/video_source.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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 "rtc_base/logging.h"
-#include "sdk/android/generated_video_jni/jni/VideoSource_jni.h"
-#include "sdk/android/native_api/jni/java_types.h"
-#include "sdk/android/src/jni/android_video_track_source.h"
-
-namespace webrtc {
-namespace jni {
-
-static void JNI_VideoSource_AdaptOutputFormat(JNIEnv* jni,
- jlong j_source,
- jint j_landscape_width,
- jint j_landscape_height,
- jint j_portrait_width,
- jint j_portrait_height,
- jint j_fps) {
- RTC_LOG(LS_INFO) << "VideoSource_nativeAdaptOutputFormat";
- reinterpret_cast<AndroidVideoTrackSource*>(j_source)->OnOutputFormatRequest(
- j_landscape_width, j_landscape_height, j_portrait_width,
- j_portrait_height, j_fps);
-}
-
-} // namespace jni
-} // namespace webrtc