Bindings for injectable Java video encoders.
BUG=webrtc:7760
Review-Url: https://codereview.webrtc.org/3003873002
Cr-Original-Commit-Position: refs/heads/master@{#19651}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: 07a3bd7c4b54399e2abd4713619427f9638ab6d9
diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn
index 16064cc..339e47c 100644
--- a/sdk/android/BUILD.gn
+++ b/sdk/android/BUILD.gn
@@ -108,6 +108,10 @@
"src/jni/videodecoderfactorywrapper.h",
"src/jni/videodecoderwrapper.cc",
"src/jni/videodecoderwrapper.h",
+ "src/jni/videoencoderfactorywrapper.cc",
+ "src/jni/videoencoderfactorywrapper.h",
+ "src/jni/videoencoderwrapper.cc",
+ "src/jni/videoencoderwrapper.h",
"src/jni/videofilerenderer_jni.cc",
"src/jni/videoframe_jni.cc",
"src/jni/videotrack_jni.cc",
@@ -145,6 +149,7 @@
"../../common_video:common_video",
"../../media:rtc_audio_video",
"../../media:rtc_media_base",
+ "../../modules:module_api",
"../../modules/utility:utility",
"../../modules/video_coding:video_coding_utility",
"../../rtc_base:rtc_base",
@@ -445,6 +450,7 @@
"src/java/org/webrtc/TextureBufferImpl.java",
"src/java/org/webrtc/VideoCodecType.java",
"src/java/org/webrtc/VideoDecoderWrapperCallback.java",
+ "src/java/org/webrtc/VideoEncoderWrapperCallback.java",
"src/java/org/webrtc/WrappedNativeI420Buffer.java",
"src/java/org/webrtc/YuvConverter.java",
]
diff --git a/sdk/android/api/org/webrtc/EncodedImage.java b/sdk/android/api/org/webrtc/EncodedImage.java
index 7aef023..6720cd7 100644
--- a/sdk/android/api/org/webrtc/EncodedImage.java
+++ b/sdk/android/api/org/webrtc/EncodedImage.java
@@ -18,10 +18,30 @@
* encoders.
*/
public class EncodedImage {
+ // Must be kept in sync with common_types.h FrameType.
public enum FrameType {
- EmptyFrame,
- VideoFrameKey,
- VideoFrameDelta,
+ EmptyFrame(0),
+ VideoFrameKey(3),
+ VideoFrameDelta(4);
+
+ private final int nativeIndex;
+
+ private FrameType(int nativeIndex) {
+ this.nativeIndex = nativeIndex;
+ }
+
+ public int getNative() {
+ return nativeIndex;
+ }
+
+ public static FrameType fromNative(int nativeIndex) {
+ for (FrameType type : FrameType.values()) {
+ if (type.nativeIndex == nativeIndex) {
+ return type;
+ }
+ }
+ throw new IllegalArgumentException("Unknown native frame type: " + nativeIndex);
+ }
}
public final ByteBuffer buffer;
diff --git a/sdk/android/api/org/webrtc/VideoEncoder.java b/sdk/android/api/org/webrtc/VideoEncoder.java
index 0867181..1fb9601 100644
--- a/sdk/android/api/org/webrtc/VideoEncoder.java
+++ b/sdk/android/api/org/webrtc/VideoEncoder.java
@@ -22,13 +22,16 @@
public final int height;
public final int startBitrate; // Kilobits per second.
public final int maxFramerate;
+ public final boolean automaticResizeOn;
- public Settings(int numberOfCores, int width, int height, int startBitrate, int maxFramerate) {
+ public Settings(int numberOfCores, int width, int height, int startBitrate, int maxFramerate,
+ boolean automaticResizeOn) {
this.numberOfCores = numberOfCores;
this.width = width;
this.height = height;
this.startBitrate = startBitrate;
this.maxFramerate = maxFramerate;
+ this.automaticResizeOn = automaticResizeOn;
}
}
@@ -84,11 +87,22 @@
/** Settings for WebRTC quality based scaling. */
public class ScalingSettings {
public final boolean on;
- public final int low;
- public final int high;
+ public final Integer low;
+ public final Integer high;
/**
- * Creates quality based scaling settings.
+ * Creates quality based scaling setting.
+ *
+ * @param on True if quality scaling is turned on.
+ */
+ public ScalingSettings(boolean on) {
+ this.on = on;
+ this.low = null;
+ this.high = null;
+ }
+
+ /**
+ * Creates quality based scaling settings with custom thresholds.
*
* @param on True if quality scaling is turned on.
* @param low Average QP at which to scale up the resolution.
@@ -129,6 +143,6 @@
VideoCodecStatus setRateAllocation(BitrateAllocation allocation, int framerate);
/** Any encoder that wants to use WebRTC provided quality scaler must implement this method. */
ScalingSettings getScalingSettings();
- /** Should return a descriptive name for the implementation. */
+ /** Should return a descriptive name for the implementation. Gets called once and cached. */
String getImplementationName();
}
diff --git a/sdk/android/api/org/webrtc/VideoEncoderFactory.java b/sdk/android/api/org/webrtc/VideoEncoderFactory.java
index 0ea7df6..4445524 100644
--- a/sdk/android/api/org/webrtc/VideoEncoderFactory.java
+++ b/sdk/android/api/org/webrtc/VideoEncoderFactory.java
@@ -15,6 +15,9 @@
/** Creates an encoder for the given video codec. */
public VideoEncoder createEncoder(VideoCodecInfo info);
- /** Enumerates the list of supported video codecs. */
+ /**
+ * Enumerates the list of supported video codecs. This method will only be called once and the
+ * result will be cached.
+ */
public VideoCodecInfo[] getSupportedCodecs();
}
diff --git a/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoDecoderTest.java b/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoDecoderTest.java
index 59dbc87..38d10e1 100644
--- a/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoDecoderTest.java
+++ b/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoDecoderTest.java
@@ -116,9 +116,9 @@
encodeDone.countDown();
}
};
- assertEquals(
- encoder.initEncode(
- new VideoEncoder.Settings(1, SETTINGS.width, SETTINGS.height, 300, 30), encodeCallback),
+ assertEquals(encoder.initEncode(new VideoEncoder.Settings(1, SETTINGS.width, SETTINGS.height,
+ 300, 30, true /* automaticResizeOn */),
+ encodeCallback),
VideoCodecStatus.OK);
// First, encode a frame.
@@ -191,9 +191,9 @@
encodeDone.countDown();
}
};
- assertEquals(
- encoder.initEncode(
- new VideoEncoder.Settings(1, SETTINGS.width, SETTINGS.height, 300, 30), encodeCallback),
+ assertEquals(encoder.initEncode(new VideoEncoder.Settings(1, SETTINGS.width, SETTINGS.height,
+ 300, 30, true /* automaticResizeOn */),
+ encodeCallback),
VideoCodecStatus.OK);
// First, encode a frame.
diff --git a/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoEncoderTest.java b/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoEncoderTest.java
index b9aead5..92e9fa3 100644
--- a/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoEncoderTest.java
+++ b/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoEncoderTest.java
@@ -32,8 +32,9 @@
private static final boolean ENABLE_INTEL_VP8_ENCODER = true;
private static final boolean ENABLE_H264_HIGH_PROFILE = true;
- private static final VideoEncoder.Settings SETTINGS = new VideoEncoder.Settings(
- 1 /* core */, 640 /* width */, 480 /* height */, 300 /* kbps */, 30 /* fps */);
+ private static final VideoEncoder.Settings SETTINGS =
+ new VideoEncoder.Settings(1 /* core */, 640 /* width */, 480 /* height */, 300 /* kbps */,
+ 30 /* fps */, true /* automaticResizeOn */);
@Test
@SmallTest
diff --git a/sdk/android/src/java/org/webrtc/HardwareVideoEncoder.java b/sdk/android/src/java/org/webrtc/HardwareVideoEncoder.java
index 7bb3857..08c09c7 100644
--- a/sdk/android/src/java/org/webrtc/HardwareVideoEncoder.java
+++ b/sdk/android/src/java/org/webrtc/HardwareVideoEncoder.java
@@ -93,6 +93,7 @@
private MediaCodec codec;
private Callback callback;
+ private boolean automaticResizeOn;
private int width;
private int height;
@@ -134,6 +135,8 @@
@Override
public VideoCodecStatus initEncode(Settings settings, Callback callback) {
+ automaticResizeOn = settings.automaticResizeOn;
+
return initEncodeInternal(
settings.width, settings.height, settings.startBitrate, settings.maxFramerate, callback);
}
@@ -380,8 +383,7 @@
@Override
public ScalingSettings getScalingSettings() {
- // TODO(mellem): Implement scaling settings.
- return null;
+ return new ScalingSettings(automaticResizeOn);
}
@Override
diff --git a/sdk/android/src/java/org/webrtc/VideoEncoderWrapperCallback.java b/sdk/android/src/java/org/webrtc/VideoEncoderWrapperCallback.java
new file mode 100644
index 0000000..0a8a8a6
--- /dev/null
+++ b/sdk/android/src/java/org/webrtc/VideoEncoderWrapperCallback.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017 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 java.nio.ByteBuffer;
+
+/**
+ * VideoEncoder callback that calls VideoEncoderWrapper.OnEncodedFrame for the Encoded frames.
+ */
+class VideoEncoderWrapperCallback implements VideoEncoder.Callback {
+ private final long nativeEncoder;
+
+ public VideoEncoderWrapperCallback(long nativeEncoder) {
+ this.nativeEncoder = nativeEncoder;
+ }
+
+ @Override
+ public void onEncodedFrame(EncodedImage frame, VideoEncoder.CodecSpecificInfo info) {
+ nativeOnEncodedFrame(nativeEncoder, frame.buffer, frame.encodedWidth, frame.encodedHeight,
+ frame.captureTimeNs, frame.frameType.getNative(), frame.rotation, frame.completeFrame,
+ frame.qp);
+ }
+
+ private native static void nativeOnEncodedFrame(long nativeEncoder, ByteBuffer buffer,
+ int encodedWidth, int encodedHeight, long captureTimeNs, int frameType, int rotation,
+ boolean completeFrame, Integer qp);
+}
diff --git a/sdk/android/src/jni/DEPS b/sdk/android/src/jni/DEPS
index ed8a8f4..382b404 100644
--- a/sdk/android/src/jni/DEPS
+++ b/sdk/android/src/jni/DEPS
@@ -1,17 +1,14 @@
include_rules = [
"+third_party/libyuv",
"+webrtc/call/callfactoryinterface.h",
- "+webrtc/common_video/h264/h264_bitstream_parser.h",
- "+webrtc/common_video/include",
- "+webrtc/common_video/libyuv/include/webrtc_libyuv.h",
+ "+webrtc/common_video",
"+webrtc/logging/rtc_event_log/rtc_event_log_factory_interface.h",
"+webrtc/media/base",
"+webrtc/media/engine",
"+webrtc/modules/audio_processing/include/audio_processing.h",
+ "+webrtc/modules/include",
"+webrtc/modules/utility/include/jvm_android.h",
- "+webrtc/modules/video_coding/include/video_codec_interface.h",
- "+webrtc/modules/video_coding/utility/vp8_header_parser.h",
- "+webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h",
+ "+webrtc/modules/video_coding",
"+webrtc/pc",
"+webrtc/system_wrappers/include",
"+webrtc/voice_engine/include/voe_base.h",
diff --git a/sdk/android/src/jni/classreferenceholder.cc b/sdk/android/src/jni/classreferenceholder.cc
index c79f936..7648798 100644
--- a/sdk/android/src/jni/classreferenceholder.cc
+++ b/sdk/android/src/jni/classreferenceholder.cc
@@ -106,9 +106,16 @@
LoadClass(jni, "org/webrtc/StatsReport$Value");
LoadClass(jni, "org/webrtc/SurfaceTextureHelper");
LoadClass(jni, "org/webrtc/VideoCapturer");
+ LoadClass(jni, "org/webrtc/VideoCodecInfo");
LoadClass(jni, "org/webrtc/VideoCodecStatus");
LoadClass(jni, "org/webrtc/VideoDecoder$Settings");
LoadClass(jni, "org/webrtc/VideoDecoderWrapperCallback");
+ LoadClass(jni, "org/webrtc/VideoEncoder");
+ LoadClass(jni, "org/webrtc/VideoEncoder$BitrateAllocation");
+ LoadClass(jni, "org/webrtc/VideoEncoder$EncodeInfo");
+ LoadClass(jni, "org/webrtc/VideoEncoder$ScalingSettings");
+ LoadClass(jni, "org/webrtc/VideoEncoder$Settings");
+ LoadClass(jni, "org/webrtc/VideoEncoderWrapperCallback");
LoadClass(jni, "org/webrtc/VideoFrame");
LoadClass(jni, "org/webrtc/VideoFrame$Buffer");
LoadClass(jni, "org/webrtc/VideoFrame$I420Buffer");
diff --git a/sdk/android/src/jni/jni_helpers.cc b/sdk/android/src/jni/jni_helpers.cc
index f6b4b6f..0384766 100644
--- a/sdk/android/src/jni/jni_helpers.cc
+++ b/sdk/android/src/jni/jni_helpers.cc
@@ -307,6 +307,41 @@
return JavaToStdString(jni, name);
}
+std::map<std::string, std::string> JavaToStdMapStrings(JNIEnv* jni,
+ jobject j_map) {
+ jclass map_class = jni->FindClass("java/util/Map");
+ jclass set_class = jni->FindClass("java/util/Set");
+ jclass iterator_class = jni->FindClass("java/util/Iterator");
+ jclass entry_class = jni->FindClass("java/util/Map$Entry");
+ jmethodID entry_set_method =
+ jni->GetMethodID(map_class, "entrySet", "()Ljava/util/Set;");
+ jmethodID iterator_method =
+ jni->GetMethodID(set_class, "iterator", "()Ljava/util/Iterator;");
+ jmethodID has_next_method =
+ jni->GetMethodID(iterator_class, "hasNext", "()Z");
+ jmethodID next_method =
+ jni->GetMethodID(iterator_class, "next", "()Ljava/lang/Object;");
+ jmethodID get_key_method =
+ jni->GetMethodID(entry_class, "getKey", "()Ljava/lang/Object;");
+ jmethodID get_value_method =
+ jni->GetMethodID(entry_class, "getValue", "()Ljava/lang/Object;");
+
+ jobject j_entry_set = jni->CallObjectMethod(j_map, entry_set_method);
+ jobject j_iterator = jni->CallObjectMethod(j_entry_set, iterator_method);
+
+ std::map<std::string, std::string> result;
+ while (jni->CallBooleanMethod(j_iterator, has_next_method)) {
+ jobject j_entry = jni->CallObjectMethod(j_iterator, next_method);
+ jstring j_key =
+ static_cast<jstring>(jni->CallObjectMethod(j_entry, get_key_method));
+ jstring j_value =
+ static_cast<jstring>(jni->CallObjectMethod(j_entry, get_value_method));
+ result[JavaToStdString(jni, j_key)] = JavaToStdString(jni, j_value);
+ }
+
+ return result;
+}
+
jobject NewGlobalRef(JNIEnv* jni, jobject o) {
jobject ret = jni->NewGlobalRef(o);
CHECK_EXCEPTION(jni) << "error during NewGlobalRef";
diff --git a/sdk/android/src/jni/jni_helpers.h b/sdk/android/src/jni/jni_helpers.h
index cc04f8b..83f2155 100644
--- a/sdk/android/src/jni/jni_helpers.h
+++ b/sdk/android/src/jni/jni_helpers.h
@@ -15,6 +15,7 @@
#define WEBRTC_SDK_ANDROID_SRC_JNI_JNI_HELPERS_H_
#include <jni.h>
+#include <map>
#include <string>
#include <vector>
@@ -114,6 +115,10 @@
const std::string& state_class_fragment,
int index);
+// Parses Map<String, String> to std::map<std::string, std::string>.
+std::map<std::string, std::string> JavaToStdMapStrings(JNIEnv* jni,
+ jobject j_map);
+
// Returns the name of a Java enum.
std::string GetJavaEnumName(JNIEnv* jni,
const std::string& className,
diff --git a/sdk/android/src/jni/pc/video_jni.cc b/sdk/android/src/jni/pc/video_jni.cc
index 4712d4f..3172b13 100644
--- a/sdk/android/src/jni/pc/video_jni.cc
+++ b/sdk/android/src/jni/pc/video_jni.cc
@@ -21,6 +21,7 @@
#include "webrtc/sdk/android/src/jni/pc/ownedfactoryandthreads.h"
#include "webrtc/sdk/android/src/jni/surfacetexturehelper_jni.h"
#include "webrtc/sdk/android/src/jni/videodecoderfactorywrapper.h"
+#include "webrtc/sdk/android/src/jni/videoencoderfactorywrapper.h"
namespace webrtc {
namespace jni {
@@ -29,14 +30,19 @@
// used and all applications inject their own codecs.
// This is semi broken if someone wants to create multiple peerconnection
// factories.
+static bool use_media_codec_encoder_factory;
static bool use_media_codec_decoder_factory;
cricket::WebRtcVideoEncoderFactory* CreateVideoEncoderFactory(
JNIEnv* jni,
jobject j_encoder_factory) {
- RTC_DCHECK(j_encoder_factory == nullptr)
- << "Injectable video encoders are not supported yet.";
- return new MediaCodecVideoEncoderFactory();
+ use_media_codec_encoder_factory = j_encoder_factory == nullptr;
+
+ if (use_media_codec_encoder_factory) {
+ return new MediaCodecVideoEncoderFactory();
+ } else {
+ return new VideoEncoderFactoryWrapper(jni, j_encoder_factory);
+ }
}
cricket::WebRtcVideoDecoderFactory* CreateVideoDecoderFactory(
@@ -111,7 +117,7 @@
MediaCodecVideoEncoderFactory* encoder_factory =
static_cast<MediaCodecVideoEncoderFactory*>(
owned_factory->encoder_factory());
- if (encoder_factory &&
+ if (use_media_codec_encoder_factory && encoder_factory &&
jni->IsInstanceOf(local_egl_context, j_eglbase14_context_class)) {
LOG(LS_INFO) << "Set EGL context for HW encoding.";
encoder_factory->SetEGLContext(jni, local_egl_context);
diff --git a/sdk/android/src/jni/videoencoderfactorywrapper.cc b/sdk/android/src/jni/videoencoderfactorywrapper.cc
new file mode 100644
index 0000000..138d863
--- /dev/null
+++ b/sdk/android/src/jni/videoencoderfactorywrapper.cc
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2017 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/sdk/android/src/jni/videoencoderfactorywrapper.h"
+
+#include "webrtc/api/video_codecs/video_encoder.h"
+#include "webrtc/common_types.h"
+#include "webrtc/rtc_base/logging.h"
+#include "webrtc/sdk/android/src/jni/classreferenceholder.h"
+#include "webrtc/sdk/android/src/jni/videoencoderwrapper.h"
+
+namespace webrtc {
+namespace jni {
+
+VideoEncoderFactoryWrapper::VideoEncoderFactoryWrapper(JNIEnv* jni,
+ jobject encoder_factory)
+ : video_codec_info_class_(jni, FindClass(jni, "org/webrtc/VideoCodecInfo")),
+ hash_map_class_(jni, jni->FindClass("java/util/HashMap")),
+ encoder_factory_(jni, encoder_factory) {
+ jclass encoder_factory_class = jni->GetObjectClass(*encoder_factory_);
+ create_encoder_method_ = jni->GetMethodID(
+ encoder_factory_class, "createEncoder",
+ "(Lorg/webrtc/VideoCodecInfo;)Lorg/webrtc/VideoEncoder;");
+ get_supported_codecs_method_ =
+ jni->GetMethodID(encoder_factory_class, "getSupportedCodecs",
+ "()[Lorg/webrtc/VideoCodecInfo;");
+
+ video_codec_info_constructor_ =
+ jni->GetMethodID(*video_codec_info_class_, "<init>",
+ "(ILjava/lang/String;Ljava/util/Map;)V");
+ payload_field_ = jni->GetFieldID(*video_codec_info_class_, "payload", "I");
+ name_field_ =
+ jni->GetFieldID(*video_codec_info_class_, "name", "Ljava/lang/String;");
+ params_field_ =
+ jni->GetFieldID(*video_codec_info_class_, "params", "Ljava/util/Map;");
+
+ hash_map_constructor_ = jni->GetMethodID(*hash_map_class_, "<init>", "()V");
+ put_method_ = jni->GetMethodID(
+ *hash_map_class_, "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+
+ supported_codecs_ = GetSupportedCodecs(jni);
+}
+
+VideoEncoder* VideoEncoderFactoryWrapper::CreateVideoEncoder(
+ const cricket::VideoCodec& codec) {
+ JNIEnv* jni = AttachCurrentThreadIfNeeded();
+ ScopedLocalRefFrame local_ref_frame(jni);
+ jobject j_codec_info = ToJavaCodecInfo(jni, codec);
+ jobject encoder = jni->CallObjectMethod(*encoder_factory_,
+ create_encoder_method_, j_codec_info);
+ return encoder != nullptr ? new VideoEncoderWrapper(jni, encoder) : nullptr;
+}
+
+jobject VideoEncoderFactoryWrapper::ToJavaCodecInfo(
+ JNIEnv* jni,
+ const cricket::VideoCodec& codec) {
+ jobject j_params = jni->NewObject(*hash_map_class_, hash_map_constructor_);
+ for (auto const& param : codec.params) {
+ jni->CallObjectMethod(j_params, put_method_,
+ JavaStringFromStdString(jni, param.first),
+ JavaStringFromStdString(jni, param.second));
+ }
+ return jni->NewObject(*video_codec_info_class_, video_codec_info_constructor_,
+ codec.id, JavaStringFromStdString(jni, codec.name),
+ j_params);
+}
+
+std::vector<cricket::VideoCodec> VideoEncoderFactoryWrapper::GetSupportedCodecs(
+ JNIEnv* jni) const {
+ const jobjectArray j_supported_codecs = static_cast<jobjectArray>(
+ jni->CallObjectMethod(*encoder_factory_, get_supported_codecs_method_));
+ const jsize supported_codecs_count = jni->GetArrayLength(j_supported_codecs);
+
+ std::vector<cricket::VideoCodec> supported_codecs;
+ supported_codecs.resize(supported_codecs_count);
+ for (jsize i = 0; i < supported_codecs_count; i++) {
+ jobject j_supported_codec =
+ jni->GetObjectArrayElement(j_supported_codecs, i);
+ int payload = jni->GetIntField(j_supported_codec, payload_field_);
+ jobject j_params = jni->GetObjectField(j_supported_codec, params_field_);
+ jstring j_name = static_cast<jstring>(
+ jni->GetObjectField(j_supported_codec, name_field_));
+ supported_codecs[i] =
+ cricket::VideoCodec(payload, JavaToStdString(jni, j_name));
+ supported_codecs[i].params = JavaToStdMapStrings(jni, j_params);
+ }
+ return supported_codecs;
+}
+
+void VideoEncoderFactoryWrapper::DestroyVideoEncoder(VideoEncoder* encoder) {
+ delete encoder;
+}
+
+} // namespace jni
+} // namespace webrtc
diff --git a/sdk/android/src/jni/videoencoderfactorywrapper.h b/sdk/android/src/jni/videoencoderfactorywrapper.h
new file mode 100644
index 0000000..03d054d
--- /dev/null
+++ b/sdk/android/src/jni/videoencoderfactorywrapper.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017 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_SDK_ANDROID_SRC_JNI_VIDEOENCODERFACTORYWRAPPER_H_
+#define WEBRTC_SDK_ANDROID_SRC_JNI_VIDEOENCODERFACTORYWRAPPER_H_
+
+#include <jni.h>
+#include <vector>
+
+#include "webrtc/media/engine/webrtcvideoencoderfactory.h"
+#include "webrtc/sdk/android/src/jni/jni_helpers.h"
+
+namespace webrtc {
+namespace jni {
+
+// Wrapper for Java VideoEncoderFactory class. Delegates method calls through
+// JNI and wraps the encoder inside VideoEncoderWrapper.
+class VideoEncoderFactoryWrapper : public cricket::WebRtcVideoEncoderFactory {
+ public:
+ VideoEncoderFactoryWrapper(JNIEnv* jni, jobject encoder_factory);
+
+ // Caller takes the ownership of the returned object and it should be released
+ // by calling DestroyVideoEncoder().
+ VideoEncoder* CreateVideoEncoder(const cricket::VideoCodec& codec) override;
+
+ // Returns a list of supported codecs in order of preference.
+ const std::vector<cricket::VideoCodec>& supported_codecs() const override {
+ return supported_codecs_;
+ }
+
+ void DestroyVideoEncoder(VideoEncoder* encoder) override;
+
+ private:
+ std::vector<cricket::VideoCodec> GetSupportedCodecs(JNIEnv* jni) const;
+ jobject ToJavaCodecInfo(JNIEnv* jni, const cricket::VideoCodec& codec);
+
+ const ScopedGlobalRef<jclass> video_codec_info_class_;
+ const ScopedGlobalRef<jclass> hash_map_class_;
+ const ScopedGlobalRef<jobject> encoder_factory_;
+
+ jmethodID create_encoder_method_;
+ jmethodID get_supported_codecs_method_;
+
+ jmethodID video_codec_info_constructor_;
+ jfieldID payload_field_;
+ jfieldID name_field_;
+ jfieldID params_field_;
+
+ jmethodID hash_map_constructor_;
+ jmethodID put_method_;
+
+ std::vector<cricket::VideoCodec> supported_codecs_;
+};
+
+} // namespace jni
+} // namespace webrtc
+
+#endif // WEBRTC_SDK_ANDROID_SRC_JNI_VIDEOENCODERFACTORYWRAPPER_H_
diff --git a/sdk/android/src/jni/videoencoderwrapper.cc b/sdk/android/src/jni/videoencoderwrapper.cc
new file mode 100644
index 0000000..2b87d67
--- /dev/null
+++ b/sdk/android/src/jni/videoencoderwrapper.cc
@@ -0,0 +1,490 @@
+/*
+ * Copyright 2017 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/sdk/android/src/jni/videoencoderwrapper.h"
+
+#include <utility>
+
+#include "webrtc/common_video/h264/h264_common.h"
+#include "webrtc/modules/include/module_common_types.h"
+#include "webrtc/modules/video_coding/include/video_codec_interface.h"
+#include "webrtc/modules/video_coding/include/video_error_codes.h"
+#include "webrtc/modules/video_coding/utility/vp8_header_parser.h"
+#include "webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h"
+#include "webrtc/rtc_base/logging.h"
+#include "webrtc/rtc_base/random.h"
+#include "webrtc/rtc_base/timeutils.h"
+#include "webrtc/sdk/android/src/jni/classreferenceholder.h"
+
+namespace webrtc {
+namespace jni {
+
+static const int kMaxJavaEncoderResets = 3;
+
+VideoEncoderWrapper::VideoEncoderWrapper(JNIEnv* jni, jobject j_encoder)
+ : encoder_(jni, j_encoder),
+ settings_class_(jni, FindClass(jni, "org/webrtc/VideoEncoder$Settings")),
+ encode_info_class_(jni,
+ FindClass(jni, "org/webrtc/VideoEncoder$EncodeInfo")),
+ frame_type_class_(jni,
+ FindClass(jni, "org/webrtc/EncodedImage$FrameType")),
+ bitrate_allocation_class_(
+ jni,
+ FindClass(jni, "org/webrtc/VideoEncoder$BitrateAllocation")),
+ int_array_class_(jni, jni->FindClass("[I")),
+ video_frame_factory_(jni) {
+ jclass encoder_class = FindClass(jni, "org/webrtc/VideoEncoder");
+
+ init_encode_method_ =
+ jni->GetMethodID(encoder_class, "initEncode",
+ "(Lorg/webrtc/VideoEncoder$Settings;Lorg/webrtc/"
+ "VideoEncoder$Callback;)Lorg/webrtc/VideoCodecStatus;");
+ release_method_ = jni->GetMethodID(encoder_class, "release",
+ "()Lorg/webrtc/VideoCodecStatus;");
+ encode_method_ = jni->GetMethodID(
+ encoder_class, "encode",
+ "(Lorg/webrtc/VideoFrame;Lorg/webrtc/"
+ "VideoEncoder$EncodeInfo;)Lorg/webrtc/VideoCodecStatus;");
+ set_channel_parameters_method_ =
+ jni->GetMethodID(encoder_class, "setChannelParameters",
+ "(SJ)Lorg/webrtc/VideoCodecStatus;");
+ set_rate_allocation_method_ =
+ jni->GetMethodID(encoder_class, "setRateAllocation",
+ "(Lorg/webrtc/VideoEncoder$BitrateAllocation;I)Lorg/"
+ "webrtc/VideoCodecStatus;");
+ get_scaling_settings_method_ =
+ jni->GetMethodID(encoder_class, "getScalingSettings",
+ "()Lorg/webrtc/VideoEncoder$ScalingSettings;");
+ get_implementation_name_method_ = jni->GetMethodID(
+ encoder_class, "getImplementationName", "()Ljava/lang/String;");
+
+ settings_constructor_ =
+ jni->GetMethodID(*settings_class_, "<init>", "(IIIIIZ)V");
+
+ encode_info_constructor_ = jni->GetMethodID(
+ *encode_info_class_, "<init>", "([Lorg/webrtc/EncodedImage$FrameType;)V");
+
+ frame_type_from_native_method_ =
+ jni->GetStaticMethodID(*frame_type_class_, "fromNative",
+ "(I)Lorg/webrtc/EncodedImage$FrameType;");
+
+ bitrate_allocation_constructor_ =
+ jni->GetMethodID(*bitrate_allocation_class_, "<init>", "([[I)V");
+
+ jclass video_codec_status_class =
+ FindClass(jni, "org/webrtc/VideoCodecStatus");
+ get_number_method_ =
+ jni->GetMethodID(video_codec_status_class, "getNumber", "()I");
+
+ jclass integer_class = jni->FindClass("java/lang/Integer");
+ int_value_method_ = jni->GetMethodID(integer_class, "intValue", "()I");
+
+ jclass scaling_settings_class =
+ FindClass(jni, "org/webrtc/VideoEncoder$ScalingSettings");
+ scaling_settings_on_field_ =
+ jni->GetFieldID(scaling_settings_class, "on", "Z");
+ scaling_settings_low_field_ =
+ jni->GetFieldID(scaling_settings_class, "low", "Ljava/lang/Integer;");
+ scaling_settings_high_field_ =
+ jni->GetFieldID(scaling_settings_class, "high", "Ljava/lang/Integer;");
+
+ implementation_name_ = GetImplementationName(jni);
+
+ encoder_queue_ = rtc::TaskQueue::Current();
+
+ initialized_ = false;
+ num_resets_ = 0;
+
+ Random random(rtc::TimeMicros());
+ picture_id_ = random.Rand<uint16_t>() & 0x7FFF;
+ tl0_pic_idx_ = random.Rand<uint8_t>();
+}
+
+int32_t VideoEncoderWrapper::InitEncode(const VideoCodec* codec_settings,
+ int32_t number_of_cores,
+ size_t max_payload_size) {
+ JNIEnv* jni = AttachCurrentThreadIfNeeded();
+ ScopedLocalRefFrame local_ref_frame(jni);
+
+ number_of_cores_ = number_of_cores;
+ codec_settings_ = *codec_settings;
+ num_resets_ = 0;
+
+ return InitEncodeInternal(jni);
+}
+
+int32_t VideoEncoderWrapper::InitEncodeInternal(JNIEnv* jni) {
+ bool automatic_resize_on;
+ switch (codec_settings_.codecType) {
+ case kVideoCodecVP8:
+ automatic_resize_on = codec_settings_.VP8()->automaticResizeOn;
+ break;
+ case kVideoCodecVP9:
+ automatic_resize_on = codec_settings_.VP9()->automaticResizeOn;
+ break;
+ default:
+ automatic_resize_on = true;
+ }
+
+ jobject settings =
+ jni->NewObject(*settings_class_, settings_constructor_, number_of_cores_,
+ codec_settings_.width, codec_settings_.height,
+ codec_settings_.startBitrate, codec_settings_.maxFramerate,
+ automatic_resize_on);
+
+ jclass callback_class =
+ FindClass(jni, "org/webrtc/VideoEncoderWrapperCallback");
+ jmethodID callback_constructor =
+ jni->GetMethodID(callback_class, "<init>", "(J)V");
+ jobject callback = jni->NewObject(callback_class, callback_constructor,
+ jlongFromPointer(this));
+
+ jobject ret =
+ jni->CallObjectMethod(*encoder_, init_encode_method_, settings, callback);
+ if (jni->CallIntMethod(ret, get_number_method_) == WEBRTC_VIDEO_CODEC_OK) {
+ initialized_ = true;
+ }
+
+ return HandleReturnCode(jni, ret);
+}
+
+int32_t VideoEncoderWrapper::RegisterEncodeCompleteCallback(
+ EncodedImageCallback* callback) {
+ callback_ = callback;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t VideoEncoderWrapper::Release() {
+ JNIEnv* jni = AttachCurrentThreadIfNeeded();
+ ScopedLocalRefFrame local_ref_frame(jni);
+ jobject ret = jni->CallObjectMethod(*encoder_, release_method_);
+ frame_extra_infos_.clear();
+ initialized_ = false;
+ return HandleReturnCode(jni, ret);
+}
+
+int32_t VideoEncoderWrapper::Encode(
+ const VideoFrame& frame,
+ const CodecSpecificInfo* /* codec_specific_info */,
+ const std::vector<FrameType>* frame_types) {
+ if (!initialized_) {
+ // Most likely initializing the codec failed.
+ return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
+ }
+
+ JNIEnv* jni = AttachCurrentThreadIfNeeded();
+ ScopedLocalRefFrame local_ref_frame(jni);
+
+ // Construct encode info.
+ jobjectArray j_frame_types =
+ jni->NewObjectArray(frame_types->size(), *frame_type_class_, nullptr);
+ for (size_t i = 0; i < frame_types->size(); ++i) {
+ jobject j_frame_type = jni->CallStaticObjectMethod(
+ *frame_type_class_, frame_type_from_native_method_,
+ static_cast<jint>((*frame_types)[i]));
+ jni->SetObjectArrayElement(j_frame_types, i, j_frame_type);
+ }
+ jobject encode_info = jni->NewObject(*encode_info_class_,
+ encode_info_constructor_, j_frame_types);
+
+ FrameExtraInfo info;
+ info.capture_time_ns = frame.timestamp_us() * rtc::kNumNanosecsPerMicrosec;
+ info.timestamp_rtp = frame.timestamp();
+ frame_extra_infos_.push_back(info);
+
+ jobject ret = jni->CallObjectMethod(
+ *encoder_, encode_method_, video_frame_factory_.ToJavaFrame(jni, frame),
+ encode_info);
+ return HandleReturnCode(jni, ret);
+}
+
+int32_t VideoEncoderWrapper::SetChannelParameters(uint32_t packet_loss,
+ int64_t rtt) {
+ JNIEnv* jni = AttachCurrentThreadIfNeeded();
+ ScopedLocalRefFrame local_ref_frame(jni);
+ jobject ret = jni->CallObjectMethod(*encoder_, set_channel_parameters_method_,
+ (jshort)packet_loss, (jlong)rtt);
+ return HandleReturnCode(jni, ret);
+}
+
+int32_t VideoEncoderWrapper::SetRateAllocation(
+ const BitrateAllocation& allocation,
+ uint32_t framerate) {
+ JNIEnv* jni = AttachCurrentThreadIfNeeded();
+ ScopedLocalRefFrame local_ref_frame(jni);
+
+ jobject j_bitrate_allocation = ToJavaBitrateAllocation(jni, allocation);
+ jobject ret = jni->CallObjectMethod(*encoder_, set_rate_allocation_method_,
+ j_bitrate_allocation, (jint)framerate);
+ return HandleReturnCode(jni, ret);
+}
+
+VideoEncoderWrapper::ScalingSettings VideoEncoderWrapper::GetScalingSettings()
+ const {
+ JNIEnv* jni = AttachCurrentThreadIfNeeded();
+ ScopedLocalRefFrame local_ref_frame(jni);
+ jobject j_scaling_settings =
+ jni->CallObjectMethod(*encoder_, get_scaling_settings_method_);
+ bool on =
+ jni->GetBooleanField(j_scaling_settings, scaling_settings_on_field_);
+ jobject j_low =
+ jni->GetObjectField(j_scaling_settings, scaling_settings_low_field_);
+ jobject j_high =
+ jni->GetObjectField(j_scaling_settings, scaling_settings_high_field_);
+
+ if (j_low != nullptr || j_high != nullptr) {
+ RTC_DCHECK(j_low != nullptr);
+ RTC_DCHECK(j_high != nullptr);
+ int low = jni->CallIntMethod(j_low, int_value_method_);
+ int high = jni->CallIntMethod(j_high, int_value_method_);
+ return ScalingSettings(on, low, high);
+ } else {
+ return ScalingSettings(on);
+ }
+}
+
+const char* VideoEncoderWrapper::ImplementationName() const {
+ return implementation_name_.c_str();
+}
+
+void VideoEncoderWrapper::OnEncodedFrame(JNIEnv* jni,
+ jobject j_buffer,
+ jint encoded_width,
+ jint encoded_height,
+ jlong capture_time_ns,
+ jint frame_type,
+ jint rotation,
+ jboolean complete_frame,
+ jobject j_qp) {
+ const uint8_t* buffer =
+ static_cast<uint8_t*>(jni->GetDirectBufferAddress(j_buffer));
+ const size_t buffer_size = jni->GetDirectBufferCapacity(j_buffer);
+
+ std::vector<uint8_t> buffer_copy(buffer_size);
+ memcpy(buffer_copy.data(), buffer, buffer_size);
+ int qp = -1;
+ if (j_qp != nullptr) {
+ qp = jni->CallIntMethod(j_qp, int_value_method_);
+ }
+
+ encoder_queue_->PostTask(
+ [
+ this, task_buffer = std::move(buffer_copy), qp, encoded_width,
+ encoded_height, capture_time_ns, frame_type, rotation, complete_frame
+ ]() {
+ FrameExtraInfo frame_extra_info;
+ do {
+ if (frame_extra_infos_.empty()) {
+ LOG(LS_WARNING)
+ << "Java encoder produced an unexpected frame with timestamp: "
+ << capture_time_ns;
+ return;
+ }
+
+ frame_extra_info = frame_extra_infos_.front();
+ frame_extra_infos_.pop_front();
+ // The encoder might drop frames so iterate through the queue until
+ // we find a matching timestamp.
+ } while (frame_extra_info.capture_time_ns != capture_time_ns);
+
+ RTPFragmentationHeader header = ParseFragmentationHeader(task_buffer);
+ EncodedImage frame(const_cast<uint8_t*>(task_buffer.data()),
+ task_buffer.size(), task_buffer.size());
+ frame._encodedWidth = encoded_width;
+ frame._encodedHeight = encoded_height;
+ frame._timeStamp = frame_extra_info.timestamp_rtp;
+ frame.capture_time_ms_ = capture_time_ns / rtc::kNumNanosecsPerMillisec;
+ frame._frameType = (FrameType)frame_type;
+ frame.rotation_ = (VideoRotation)rotation;
+ frame._completeFrame = complete_frame;
+ if (qp == -1) {
+ frame.qp_ = ParseQp(task_buffer);
+ } else {
+ frame.qp_ = qp;
+ }
+
+ CodecSpecificInfo info(ParseCodecSpecificInfo(frame));
+ callback_->OnEncodedImage(frame, &info, &header);
+ });
+}
+
+int32_t VideoEncoderWrapper::HandleReturnCode(JNIEnv* jni, jobject code) {
+ int32_t value = jni->CallIntMethod(code, get_number_method_);
+ if (value < 0) { // Any errors are represented by negative values.
+ // Try resetting the codec.
+ if (++num_resets_ <= kMaxJavaEncoderResets &&
+ Release() == WEBRTC_VIDEO_CODEC_OK) {
+ LOG(LS_WARNING) << "Reset Java encoder: " << num_resets_;
+ return InitEncodeInternal(jni);
+ }
+
+ LOG(LS_WARNING) << "Falling back to software decoder.";
+ return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
+ } else {
+ return value;
+ }
+}
+
+RTPFragmentationHeader VideoEncoderWrapper::ParseFragmentationHeader(
+ const std::vector<uint8_t>& buffer) {
+ RTPFragmentationHeader header;
+ if (codec_settings_.codecType == kVideoCodecH264) {
+ h264_bitstream_parser_.ParseBitstream(buffer.data(), buffer.size());
+
+ // For H.264 search for start codes.
+ const std::vector<H264::NaluIndex> nalu_idxs =
+ H264::FindNaluIndices(buffer.data(), buffer.size());
+ if (nalu_idxs.empty()) {
+ LOG(LS_ERROR) << "Start code is not found!";
+ LOG(LS_ERROR) << "Data:" << buffer[0] << " " << buffer[1] << " "
+ << buffer[2] << " " << buffer[3] << " " << buffer[4] << " "
+ << buffer[5];
+ }
+ header.VerifyAndAllocateFragmentationHeader(nalu_idxs.size());
+ for (size_t i = 0; i < nalu_idxs.size(); i++) {
+ header.fragmentationOffset[i] = nalu_idxs[i].payload_start_offset;
+ header.fragmentationLength[i] = nalu_idxs[i].payload_size;
+ header.fragmentationPlType[i] = 0;
+ header.fragmentationTimeDiff[i] = 0;
+ }
+ } else {
+ // Generate a header describing a single fragment.
+ header.VerifyAndAllocateFragmentationHeader(1);
+ header.fragmentationOffset[0] = 0;
+ header.fragmentationLength[0] = buffer.size();
+ header.fragmentationPlType[0] = 0;
+ header.fragmentationTimeDiff[0] = 0;
+ }
+ return header;
+}
+
+int VideoEncoderWrapper::ParseQp(const std::vector<uint8_t>& buffer) {
+ int qp;
+ bool success;
+ switch (codec_settings_.codecType) {
+ case kVideoCodecVP8:
+ success = vp8::GetQp(buffer.data(), buffer.size(), &qp);
+ break;
+ case kVideoCodecVP9:
+ success = vp9::GetQp(buffer.data(), buffer.size(), &qp);
+ break;
+ case kVideoCodecH264:
+ success = h264_bitstream_parser_.GetLastSliceQp(&qp);
+ break;
+ default: // Default is to not provide QP.
+ success = false;
+ break;
+ }
+ return success ? qp : -1; // -1 means unknown QP.
+}
+
+CodecSpecificInfo VideoEncoderWrapper::ParseCodecSpecificInfo(
+ const EncodedImage& frame) {
+ const bool key_frame = frame._frameType == kVideoFrameKey;
+
+ CodecSpecificInfo info;
+ memset(&info, 0, sizeof(info));
+ info.codecType = codec_settings_.codecType;
+ info.codec_name = implementation_name_.c_str();
+
+ switch (codec_settings_.codecType) {
+ case kVideoCodecVP8:
+ info.codecSpecific.VP8.pictureId = picture_id_;
+ info.codecSpecific.VP8.nonReference = false;
+ info.codecSpecific.VP8.simulcastIdx = 0;
+ info.codecSpecific.VP8.temporalIdx = kNoTemporalIdx;
+ info.codecSpecific.VP8.layerSync = false;
+ info.codecSpecific.VP8.tl0PicIdx = kNoTl0PicIdx;
+ info.codecSpecific.VP8.keyIdx = kNoKeyIdx;
+ break;
+ case kVideoCodecVP9:
+ if (key_frame) {
+ gof_idx_ = 0;
+ }
+ info.codecSpecific.VP9.picture_id = picture_id_;
+ info.codecSpecific.VP9.inter_pic_predicted = key_frame ? false : true;
+ info.codecSpecific.VP9.flexible_mode = false;
+ info.codecSpecific.VP9.ss_data_available = key_frame ? true : false;
+ info.codecSpecific.VP9.tl0_pic_idx = tl0_pic_idx_++;
+ info.codecSpecific.VP9.temporal_idx = kNoTemporalIdx;
+ info.codecSpecific.VP9.spatial_idx = kNoSpatialIdx;
+ info.codecSpecific.VP9.temporal_up_switch = true;
+ info.codecSpecific.VP9.inter_layer_predicted = false;
+ info.codecSpecific.VP9.gof_idx =
+ static_cast<uint8_t>(gof_idx_++ % gof_.num_frames_in_gof);
+ info.codecSpecific.VP9.num_spatial_layers = 1;
+ info.codecSpecific.VP9.spatial_layer_resolution_present = false;
+ if (info.codecSpecific.VP9.ss_data_available) {
+ info.codecSpecific.VP9.spatial_layer_resolution_present = true;
+ info.codecSpecific.VP9.width[0] = frame._encodedWidth;
+ info.codecSpecific.VP9.height[0] = frame._encodedHeight;
+ info.codecSpecific.VP9.gof.CopyGofInfoVP9(gof_);
+ }
+ break;
+ default:
+ break;
+ }
+
+ picture_id_ = (picture_id_ + 1) & 0x7FFF;
+
+ return info;
+}
+
+jobject VideoEncoderWrapper::ToJavaBitrateAllocation(
+ JNIEnv* jni,
+ const BitrateAllocation& allocation) {
+ jobjectArray j_allocation_array = jni->NewObjectArray(
+ kMaxSpatialLayers, *int_array_class_, nullptr /* initial */);
+ for (int spatial_i = 0; spatial_i < kMaxSpatialLayers; ++spatial_i) {
+ jintArray j_array_spatial_layer = jni->NewIntArray(kMaxTemporalStreams);
+ jint* array_spatial_layer =
+ jni->GetIntArrayElements(j_array_spatial_layer, nullptr /* isCopy */);
+ for (int temporal_i = 0; temporal_i < kMaxTemporalStreams; ++temporal_i) {
+ array_spatial_layer[temporal_i] =
+ allocation.GetBitrate(spatial_i, temporal_i);
+ }
+ jni->ReleaseIntArrayElements(j_array_spatial_layer, array_spatial_layer,
+ JNI_COMMIT);
+
+ jni->SetObjectArrayElement(j_allocation_array, spatial_i,
+ j_array_spatial_layer);
+ }
+ return jni->NewObject(*bitrate_allocation_class_,
+ bitrate_allocation_constructor_, j_allocation_array);
+}
+
+std::string VideoEncoderWrapper::GetImplementationName(JNIEnv* jni) const {
+ jstring jname = reinterpret_cast<jstring>(
+ jni->CallObjectMethod(*encoder_, get_implementation_name_method_));
+ return JavaToStdString(jni, jname);
+}
+
+JNI_FUNCTION_DECLARATION(void,
+ VideoEncoderWrapperCallback_nativeOnEncodedFrame,
+ JNIEnv* jni,
+ jclass,
+ jlong j_native_encoder,
+ jobject buffer,
+ jint encoded_width,
+ jint encoded_height,
+ jlong capture_time_ns,
+ jint frame_type,
+ jint rotation,
+ jboolean complete_frame,
+ jobject qp) {
+ VideoEncoderWrapper* native_encoder =
+ reinterpret_cast<VideoEncoderWrapper*>(j_native_encoder);
+ native_encoder->OnEncodedFrame(jni, buffer, encoded_width, encoded_height,
+ capture_time_ns, frame_type, rotation,
+ complete_frame, qp);
+}
+
+} // namespace jni
+} // namespace webrtc
diff --git a/sdk/android/src/jni/videoencoderwrapper.h b/sdk/android/src/jni/videoencoderwrapper.h
new file mode 100644
index 0000000..afda8d2
--- /dev/null
+++ b/sdk/android/src/jni/videoencoderwrapper.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2017 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_SDK_ANDROID_SRC_JNI_VIDEOENCODERWRAPPER_H_
+#define WEBRTC_SDK_ANDROID_SRC_JNI_VIDEOENCODERWRAPPER_H_
+
+#include <jni.h>
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "webrtc/api/video_codecs/video_encoder.h"
+#include "webrtc/common_video/h264/h264_bitstream_parser.h"
+#include "webrtc/modules/video_coding/codecs/vp9/include/vp9_globals.h"
+#include "webrtc/rtc_base/task_queue.h"
+#include "webrtc/sdk/android/src/jni/jni_helpers.h"
+#include "webrtc/sdk/android/src/jni/native_handle_impl.h"
+
+namespace webrtc {
+namespace jni {
+
+// Wraps a Java decoder and delegates all calls to it. Passes
+// VideoEncoderWrapperCallback to the decoder on InitDecode. Wraps the received
+// frames to AndroidVideoBuffer.
+class VideoEncoderWrapper : public VideoEncoder {
+ public:
+ VideoEncoderWrapper(JNIEnv* jni, jobject j_encoder);
+
+ int32_t InitEncode(const VideoCodec* codec_settings,
+ int32_t number_of_cores,
+ size_t max_payload_size) override;
+
+ int32_t RegisterEncodeCompleteCallback(
+ EncodedImageCallback* callback) override;
+
+ int32_t Release() override;
+
+ int32_t Encode(const VideoFrame& frame,
+ const CodecSpecificInfo* codec_specific_info,
+ const std::vector<FrameType>* frame_types) override;
+
+ int32_t SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
+
+ int32_t SetRateAllocation(const BitrateAllocation& allocation,
+ uint32_t framerate) override;
+
+ ScalingSettings GetScalingSettings() const override;
+
+ bool SupportsNativeHandle() const override { return true; }
+
+ // Should only be called by JNI.
+ void OnEncodedFrame(JNIEnv* jni,
+ jobject j_buffer,
+ jint encoded_width,
+ jint encoded_height,
+ jlong capture_time_ms,
+ jint frame_type,
+ jint rotation,
+ jboolean complete_frame,
+ jobject j_qp);
+
+ const char* ImplementationName() const override;
+
+ private:
+ struct FrameExtraInfo {
+ uint64_t capture_time_ns; // Used as an identifier of the frame.
+
+ uint32_t timestamp_rtp;
+ };
+
+ int32_t InitEncodeInternal(JNIEnv* jni);
+
+ // Takes Java VideoCodecStatus, handles it and returns WEBRTC_VIDEO_CODEC_*
+ // status code.
+ int32_t HandleReturnCode(JNIEnv* jni, jobject code);
+
+ RTPFragmentationHeader ParseFragmentationHeader(
+ const std::vector<uint8_t>& buffer);
+ int ParseQp(const std::vector<uint8_t>& buffer);
+ CodecSpecificInfo ParseCodecSpecificInfo(const EncodedImage& frame);
+ jobject ToJavaBitrateAllocation(JNIEnv* jni,
+ const BitrateAllocation& allocation);
+ std::string GetImplementationName(JNIEnv* jni) const;
+
+ const ScopedGlobalRef<jobject> encoder_;
+ const ScopedGlobalRef<jclass> settings_class_;
+ const ScopedGlobalRef<jclass> encode_info_class_;
+ const ScopedGlobalRef<jclass> frame_type_class_;
+ const ScopedGlobalRef<jclass> bitrate_allocation_class_;
+ const ScopedGlobalRef<jclass> int_array_class_;
+
+ jmethodID init_encode_method_;
+ jmethodID release_method_;
+ jmethodID encode_method_;
+ jmethodID set_channel_parameters_method_;
+ jmethodID set_rate_allocation_method_;
+ jmethodID get_scaling_settings_method_;
+ jmethodID get_implementation_name_method_;
+
+ jmethodID settings_constructor_;
+
+ jmethodID encode_info_constructor_;
+
+ jmethodID frame_type_from_native_method_;
+
+ jmethodID bitrate_allocation_constructor_;
+
+ jfieldID scaling_settings_on_field_;
+ jfieldID scaling_settings_low_field_;
+ jfieldID scaling_settings_high_field_;
+
+ jmethodID get_number_method_;
+
+ jmethodID int_value_method_;
+
+ std::string implementation_name_;
+
+ rtc::TaskQueue* encoder_queue_;
+ JavaVideoFrameFactory video_frame_factory_;
+ std::deque<FrameExtraInfo> frame_extra_infos_;
+ EncodedImageCallback* callback_;
+ bool initialized_;
+ int num_resets_;
+ int number_of_cores_;
+ VideoCodec codec_settings_;
+ H264BitstreamParser h264_bitstream_parser_;
+
+ // RTP state.
+ uint16_t picture_id_;
+ uint8_t tl0_pic_idx_;
+
+ // VP9 variables to populate codec specific structure.
+ GofInfoVP9 gof_; // Contains each frame's temporal information for
+ // non-flexible VP9 mode.
+ size_t gof_idx_;
+};
+
+} // namespace jni
+} // namespace webrtc
+
+#endif // WEBRTC_SDK_ANDROID_SRC_JNI_VIDEOENCODERWRAPPER_H_