Move /webrtc/api/android files to /webrtc/sdk/android
I decided to make one webrtc/sdk/android/BUILD.gn for both tests and Java/jni src. External dependencies needs to be updated after this CL. Future work is required to clean up the Android api and move implementation details to /webrtc/sdk/android/src. BUG=webrtc:5882,webrtc:6804 NOPRESUBMIT=True Review-Url: https://codereview.webrtc.org/2547483003 Cr-Commit-Position: refs/heads/master@{#15443}
This commit is contained in:
5
webrtc/sdk/android/src/jni/DEPS
Normal file
5
webrtc/sdk/android/src/jni/DEPS
Normal file
@ -0,0 +1,5 @@
|
||||
include_rules = [
|
||||
"+third_party/libyuv",
|
||||
"+webrtc/common_video/libyuv/include/webrtc_libyuv.h",
|
||||
"+webrtc/voice_engine/include/voe_base.h",
|
||||
]
|
||||
4
webrtc/sdk/android/src/jni/OWNERS
Normal file
4
webrtc/sdk/android/src/jni/OWNERS
Normal file
@ -0,0 +1,4 @@
|
||||
per-file androidhistogram_jni.cc=sakal@webrtc.org
|
||||
per-file androidmetrics_jni.cc=sakal@webrtc.org
|
||||
per-file androidvideotracksource.*=sakal@webrtc.org
|
||||
per-file androidvideotracksource_jni.cc=sakal@webrtc.org
|
||||
44
webrtc/sdk/android/src/jni/androidhistogram_jni.cc
Normal file
44
webrtc/sdk/android/src/jni/androidhistogram_jni.cc
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 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 <map>
|
||||
#include <memory>
|
||||
|
||||
#include "webrtc/sdk/android/src/jni/classreferenceholder.h"
|
||||
#include "webrtc/sdk/android/src/jni/jni_helpers.h"
|
||||
#include "webrtc/sdk/android/src/jni/native_handle_impl.h"
|
||||
#include "webrtc/system_wrappers/include/metrics.h"
|
||||
|
||||
// Enables collection of native histograms and creating them.
|
||||
namespace webrtc_jni {
|
||||
|
||||
JOW(jlong, Histogram_nativeCreateCounts)
|
||||
(JNIEnv* jni, jclass, jstring j_name, jint min, jint max, jint buckets) {
|
||||
std::string name = JavaToStdString(jni, j_name);
|
||||
return jlongFromPointer(
|
||||
webrtc::metrics::HistogramFactoryGetCounts(name, min, max, buckets));
|
||||
}
|
||||
|
||||
JOW(jlong, Histogram_nativeCreateEnumeration)
|
||||
(JNIEnv* jni, jclass, jstring j_name, jint max) {
|
||||
std::string name = JavaToStdString(jni, j_name);
|
||||
return jlongFromPointer(
|
||||
webrtc::metrics::HistogramFactoryGetEnumeration(name, max));
|
||||
}
|
||||
|
||||
JOW(void, Histogram_nativeAddSample)
|
||||
(JNIEnv* jni, jclass, jlong histogram, jint sample) {
|
||||
if (histogram) {
|
||||
HistogramAdd(reinterpret_cast<webrtc::metrics::Histogram*>(histogram),
|
||||
sample);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc_jni
|
||||
97
webrtc/sdk/android/src/jni/androidmediacodeccommon.h
Normal file
97
webrtc/sdk/android/src/jni/androidmediacodeccommon.h
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2015 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_ANDROIDMEDIACODECCOMMON_H_
|
||||
#define WEBRTC_SDK_ANDROID_SRC_JNI_ANDROIDMEDIACODECCOMMON_H_
|
||||
|
||||
#include <android/log.h>
|
||||
#include <string>
|
||||
|
||||
#include "webrtc/base/thread.h"
|
||||
#include "webrtc/sdk/android/src/jni/classreferenceholder.h"
|
||||
#include "webrtc/sdk/android/src/jni/jni_helpers.h"
|
||||
#include "webrtc/base/logging.h"
|
||||
#include "webrtc/base/thread.h"
|
||||
|
||||
namespace webrtc_jni {
|
||||
|
||||
// Uncomment this define to enable verbose logging for every encoded/decoded
|
||||
// video frame.
|
||||
//#define TRACK_BUFFER_TIMING
|
||||
|
||||
#define TAG_COMMON "MediaCodecVideo"
|
||||
|
||||
// Color formats supported by encoder or decoder - should include all
|
||||
// colors from supportedColorList in MediaCodecVideoEncoder.java and
|
||||
// MediaCodecVideoDecoder.java. Supported color format set in encoder
|
||||
// and decoder could be different.
|
||||
enum COLOR_FORMATTYPE {
|
||||
COLOR_FormatYUV420Planar = 0x13,
|
||||
COLOR_FormatYUV420SemiPlanar = 0x15,
|
||||
COLOR_QCOM_FormatYUV420SemiPlanar = 0x7FA30C00,
|
||||
// NV12 color format supported by QCOM codec, but not declared in MediaCodec -
|
||||
// see /hardware/qcom/media/mm-core/inc/OMX_QCOMExtns.h
|
||||
// This format is presumably similar to COLOR_FormatYUV420SemiPlanar,
|
||||
// but requires some (16, 32?) byte alignment.
|
||||
COLOR_QCOM_FORMATYVU420PackedSemiPlanar32m4ka = 0x7FA30C01,
|
||||
COLOR_QCOM_FORMATYVU420PackedSemiPlanar16m4ka = 0x7FA30C02,
|
||||
COLOR_QCOM_FORMATYVU420PackedSemiPlanar64x32Tile2m8ka = 0x7FA30C03,
|
||||
COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m = 0x7FA30C04
|
||||
};
|
||||
|
||||
// Arbitrary interval to poll the codec for new outputs.
|
||||
enum { kMediaCodecPollMs = 10 };
|
||||
// Arbitrary interval to poll at when there should be no more frames.
|
||||
enum { kMediaCodecPollNoFramesMs = 100 };
|
||||
// Media codec maximum output buffer ready timeout.
|
||||
enum { kMediaCodecTimeoutMs = 1000 };
|
||||
// Interval to print codec statistics (bitrate, fps, encoding/decoding time).
|
||||
enum { kMediaCodecStatisticsIntervalMs = 3000 };
|
||||
// Maximum amount of pending frames for VP8 decoder.
|
||||
enum { kMaxPendingFramesVp8 = 1 };
|
||||
// Maximum amount of pending frames for VP9 decoder.
|
||||
enum { kMaxPendingFramesVp9 = 1 };
|
||||
// Maximum amount of pending frames for H.264 decoder.
|
||||
enum { kMaxPendingFramesH264 = 4 };
|
||||
// Maximum amount of decoded frames for which per-frame logging is enabled.
|
||||
enum { kMaxDecodedLogFrames = 10 };
|
||||
// Maximum amount of encoded frames for which per-frame logging is enabled.
|
||||
enum { kMaxEncodedLogFrames = 10 };
|
||||
|
||||
static inline void AllowBlockingCalls() {
|
||||
rtc::Thread* current_thread = rtc::Thread::Current();
|
||||
if (current_thread != NULL)
|
||||
current_thread->SetAllowBlockingCalls(true);
|
||||
}
|
||||
|
||||
// Return the (singleton) Java Enum object corresponding to |index|;
|
||||
// |state_class_fragment| is something like "MediaSource$State".
|
||||
static inline jobject JavaEnumFromIndexAndClassName(
|
||||
JNIEnv* jni, const std::string& state_class_fragment, int index) {
|
||||
const std::string state_class = "org/webrtc/" + state_class_fragment;
|
||||
return JavaEnumFromIndex(jni, FindClass(jni, state_class.c_str()),
|
||||
state_class, index);
|
||||
}
|
||||
|
||||
// Checks for any Java exception, prints stack backtrace and clears
|
||||
// currently thrown exception.
|
||||
static inline bool CheckException(JNIEnv* jni) {
|
||||
if (jni->ExceptionCheck()) {
|
||||
LOG_TAG(rtc::LS_ERROR, TAG_COMMON) << "Java JNI exception.";
|
||||
jni->ExceptionDescribe();
|
||||
jni->ExceptionClear();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace webrtc_jni
|
||||
|
||||
#endif // WEBRTC_SDK_ANDROID_SRC_JNI_ANDROIDMEDIACODECCOMMON_H_
|
||||
989
webrtc/sdk/android/src/jni/androidmediadecoder_jni.cc
Normal file
989
webrtc/sdk/android/src/jni/androidmediadecoder_jni.cc
Normal file
@ -0,0 +1,989 @@
|
||||
/*
|
||||
* Copyright 2015 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 <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
// NOTICE: androidmediadecoder_jni.h must be included before
|
||||
// androidmediacodeccommon.h to avoid build errors.
|
||||
#include "webrtc/sdk/android/src/jni/androidmediadecoder_jni.h"
|
||||
|
||||
#include "third_party/libyuv/include/libyuv/convert.h"
|
||||
#include "third_party/libyuv/include/libyuv/convert_from.h"
|
||||
#include "third_party/libyuv/include/libyuv/video_common.h"
|
||||
#include "webrtc/sdk/android/src/jni/androidmediacodeccommon.h"
|
||||
#include "webrtc/sdk/android/src/jni/classreferenceholder.h"
|
||||
#include "webrtc/sdk/android/src/jni/native_handle_impl.h"
|
||||
#include "webrtc/sdk/android/src/jni/surfacetexturehelper_jni.h"
|
||||
#include "webrtc/base/bind.h"
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/logging.h"
|
||||
#include "webrtc/base/scoped_ref_ptr.h"
|
||||
#include "webrtc/base/thread.h"
|
||||
#include "webrtc/base/timeutils.h"
|
||||
#include "webrtc/common_video/include/i420_buffer_pool.h"
|
||||
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
||||
#include "webrtc/system_wrappers/include/logcat_trace_context.h"
|
||||
|
||||
using rtc::Bind;
|
||||
using rtc::Thread;
|
||||
using rtc::ThreadManager;
|
||||
|
||||
using webrtc::CodecSpecificInfo;
|
||||
using webrtc::DecodedImageCallback;
|
||||
using webrtc::EncodedImage;
|
||||
using webrtc::VideoFrame;
|
||||
using webrtc::RTPFragmentationHeader;
|
||||
using webrtc::VideoCodec;
|
||||
using webrtc::VideoCodecType;
|
||||
using webrtc::kVideoCodecH264;
|
||||
using webrtc::kVideoCodecVP8;
|
||||
using webrtc::kVideoCodecVP9;
|
||||
|
||||
namespace webrtc_jni {
|
||||
|
||||
// Logging macros.
|
||||
#define TAG_DECODER "MediaCodecVideoDecoder"
|
||||
#ifdef TRACK_BUFFER_TIMING
|
||||
#define ALOGV(...)
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, TAG_DECODER, __VA_ARGS__)
|
||||
#else
|
||||
#define ALOGV(...)
|
||||
#endif
|
||||
#define ALOGD LOG_TAG(rtc::LS_INFO, TAG_DECODER)
|
||||
#define ALOGW LOG_TAG(rtc::LS_WARNING, TAG_DECODER)
|
||||
#define ALOGE LOG_TAG(rtc::LS_ERROR, TAG_DECODER)
|
||||
|
||||
enum { kMaxWarningLogFrames = 2 };
|
||||
|
||||
class MediaCodecVideoDecoder : public webrtc::VideoDecoder,
|
||||
public rtc::MessageHandler {
|
||||
public:
|
||||
explicit MediaCodecVideoDecoder(
|
||||
JNIEnv* jni, VideoCodecType codecType, jobject render_egl_context);
|
||||
virtual ~MediaCodecVideoDecoder();
|
||||
|
||||
int32_t InitDecode(const VideoCodec* codecSettings, int32_t numberOfCores)
|
||||
override;
|
||||
|
||||
int32_t Decode(
|
||||
const EncodedImage& inputImage, bool missingFrames,
|
||||
const RTPFragmentationHeader* fragmentation,
|
||||
const CodecSpecificInfo* codecSpecificInfo = NULL,
|
||||
int64_t renderTimeMs = -1) override;
|
||||
|
||||
int32_t RegisterDecodeCompleteCallback(DecodedImageCallback* callback)
|
||||
override;
|
||||
|
||||
int32_t Release() override;
|
||||
|
||||
bool PrefersLateDecoding() const override { return true; }
|
||||
|
||||
// rtc::MessageHandler implementation.
|
||||
void OnMessage(rtc::Message* msg) override;
|
||||
|
||||
const char* ImplementationName() const override;
|
||||
|
||||
private:
|
||||
// CHECK-fail if not running on |codec_thread_|.
|
||||
void CheckOnCodecThread();
|
||||
|
||||
int32_t InitDecodeOnCodecThread();
|
||||
int32_t ResetDecodeOnCodecThread();
|
||||
int32_t ReleaseOnCodecThread();
|
||||
int32_t DecodeOnCodecThread(const EncodedImage& inputImage);
|
||||
// Deliver any outputs pending in the MediaCodec to our |callback_| and return
|
||||
// true on success.
|
||||
bool DeliverPendingOutputs(JNIEnv* jni, int dequeue_timeout_us);
|
||||
int32_t ProcessHWErrorOnCodecThread();
|
||||
void EnableFrameLogOnWarning();
|
||||
void ResetVariables();
|
||||
|
||||
// Type of video codec.
|
||||
VideoCodecType codecType_;
|
||||
|
||||
// Render EGL context - owned by factory, should not be allocated/destroyed
|
||||
// by VideoDecoder.
|
||||
jobject render_egl_context_;
|
||||
|
||||
bool key_frame_required_;
|
||||
bool inited_;
|
||||
bool sw_fallback_required_;
|
||||
bool use_surface_;
|
||||
VideoCodec codec_;
|
||||
webrtc::I420BufferPool decoded_frame_pool_;
|
||||
rtc::scoped_refptr<SurfaceTextureHelper> surface_texture_helper_;
|
||||
DecodedImageCallback* callback_;
|
||||
int frames_received_; // Number of frames received by decoder.
|
||||
int frames_decoded_; // Number of frames decoded by decoder.
|
||||
// Number of decoded frames for which log information is displayed.
|
||||
int frames_decoded_logged_;
|
||||
int64_t start_time_ms_; // Start time for statistics.
|
||||
int current_frames_; // Number of frames in the current statistics interval.
|
||||
int current_bytes_; // Encoded bytes in the current statistics interval.
|
||||
int current_decoding_time_ms_; // Overall decoding time in the current second
|
||||
int current_delay_time_ms_; // Overall delay time in the current second.
|
||||
uint32_t max_pending_frames_; // Maximum number of pending input frames.
|
||||
|
||||
// State that is constant for the lifetime of this object once the ctor
|
||||
// returns.
|
||||
std::unique_ptr<Thread>
|
||||
codec_thread_; // Thread on which to operate MediaCodec.
|
||||
ScopedGlobalRef<jclass> j_media_codec_video_decoder_class_;
|
||||
ScopedGlobalRef<jobject> j_media_codec_video_decoder_;
|
||||
jmethodID j_init_decode_method_;
|
||||
jmethodID j_reset_method_;
|
||||
jmethodID j_release_method_;
|
||||
jmethodID j_dequeue_input_buffer_method_;
|
||||
jmethodID j_queue_input_buffer_method_;
|
||||
jmethodID j_dequeue_byte_buffer_method_;
|
||||
jmethodID j_dequeue_texture_buffer_method_;
|
||||
jmethodID j_return_decoded_byte_buffer_method_;
|
||||
// MediaCodecVideoDecoder fields.
|
||||
jfieldID j_input_buffers_field_;
|
||||
jfieldID j_output_buffers_field_;
|
||||
jfieldID j_color_format_field_;
|
||||
jfieldID j_width_field_;
|
||||
jfieldID j_height_field_;
|
||||
jfieldID j_stride_field_;
|
||||
jfieldID j_slice_height_field_;
|
||||
// MediaCodecVideoDecoder.DecodedTextureBuffer fields.
|
||||
jfieldID j_texture_id_field_;
|
||||
jfieldID j_transform_matrix_field_;
|
||||
jfieldID j_texture_presentation_timestamp_ms_field_;
|
||||
jfieldID j_texture_timestamp_ms_field_;
|
||||
jfieldID j_texture_ntp_timestamp_ms_field_;
|
||||
jfieldID j_texture_decode_time_ms_field_;
|
||||
jfieldID j_texture_frame_delay_ms_field_;
|
||||
// MediaCodecVideoDecoder.DecodedOutputBuffer fields.
|
||||
jfieldID j_info_index_field_;
|
||||
jfieldID j_info_offset_field_;
|
||||
jfieldID j_info_size_field_;
|
||||
jfieldID j_presentation_timestamp_ms_field_;
|
||||
jfieldID j_timestamp_ms_field_;
|
||||
jfieldID j_ntp_timestamp_ms_field_;
|
||||
jfieldID j_byte_buffer_decode_time_ms_field_;
|
||||
|
||||
// Global references; must be deleted in Release().
|
||||
std::vector<jobject> input_buffers_;
|
||||
};
|
||||
|
||||
MediaCodecVideoDecoder::MediaCodecVideoDecoder(
|
||||
JNIEnv* jni, VideoCodecType codecType, jobject render_egl_context) :
|
||||
codecType_(codecType),
|
||||
render_egl_context_(render_egl_context),
|
||||
key_frame_required_(true),
|
||||
inited_(false),
|
||||
sw_fallback_required_(false),
|
||||
codec_thread_(new Thread()),
|
||||
j_media_codec_video_decoder_class_(
|
||||
jni,
|
||||
FindClass(jni, "org/webrtc/MediaCodecVideoDecoder")),
|
||||
j_media_codec_video_decoder_(
|
||||
jni,
|
||||
jni->NewObject(*j_media_codec_video_decoder_class_,
|
||||
GetMethodID(jni,
|
||||
*j_media_codec_video_decoder_class_,
|
||||
"<init>",
|
||||
"()V"))) {
|
||||
ScopedLocalRefFrame local_ref_frame(jni);
|
||||
codec_thread_->SetName("MediaCodecVideoDecoder", NULL);
|
||||
RTC_CHECK(codec_thread_->Start()) << "Failed to start MediaCodecVideoDecoder";
|
||||
|
||||
j_init_decode_method_ = GetMethodID(
|
||||
jni, *j_media_codec_video_decoder_class_, "initDecode",
|
||||
"(Lorg/webrtc/MediaCodecVideoDecoder$VideoCodecType;"
|
||||
"IILorg/webrtc/SurfaceTextureHelper;)Z");
|
||||
j_reset_method_ =
|
||||
GetMethodID(jni, *j_media_codec_video_decoder_class_, "reset", "(II)V");
|
||||
j_release_method_ =
|
||||
GetMethodID(jni, *j_media_codec_video_decoder_class_, "release", "()V");
|
||||
j_dequeue_input_buffer_method_ = GetMethodID(
|
||||
jni, *j_media_codec_video_decoder_class_, "dequeueInputBuffer", "()I");
|
||||
j_queue_input_buffer_method_ = GetMethodID(
|
||||
jni, *j_media_codec_video_decoder_class_, "queueInputBuffer", "(IIJJJ)Z");
|
||||
j_dequeue_byte_buffer_method_ = GetMethodID(
|
||||
jni, *j_media_codec_video_decoder_class_, "dequeueOutputBuffer",
|
||||
"(I)Lorg/webrtc/MediaCodecVideoDecoder$DecodedOutputBuffer;");
|
||||
j_dequeue_texture_buffer_method_ = GetMethodID(
|
||||
jni, *j_media_codec_video_decoder_class_, "dequeueTextureBuffer",
|
||||
"(I)Lorg/webrtc/MediaCodecVideoDecoder$DecodedTextureBuffer;");
|
||||
j_return_decoded_byte_buffer_method_ =
|
||||
GetMethodID(jni, *j_media_codec_video_decoder_class_,
|
||||
"returnDecodedOutputBuffer", "(I)V");
|
||||
|
||||
j_input_buffers_field_ = GetFieldID(
|
||||
jni, *j_media_codec_video_decoder_class_,
|
||||
"inputBuffers", "[Ljava/nio/ByteBuffer;");
|
||||
j_output_buffers_field_ = GetFieldID(
|
||||
jni, *j_media_codec_video_decoder_class_,
|
||||
"outputBuffers", "[Ljava/nio/ByteBuffer;");
|
||||
j_color_format_field_ = GetFieldID(
|
||||
jni, *j_media_codec_video_decoder_class_, "colorFormat", "I");
|
||||
j_width_field_ = GetFieldID(
|
||||
jni, *j_media_codec_video_decoder_class_, "width", "I");
|
||||
j_height_field_ = GetFieldID(
|
||||
jni, *j_media_codec_video_decoder_class_, "height", "I");
|
||||
j_stride_field_ = GetFieldID(
|
||||
jni, *j_media_codec_video_decoder_class_, "stride", "I");
|
||||
j_slice_height_field_ = GetFieldID(
|
||||
jni, *j_media_codec_video_decoder_class_, "sliceHeight", "I");
|
||||
|
||||
jclass j_decoded_texture_buffer_class = FindClass(jni,
|
||||
"org/webrtc/MediaCodecVideoDecoder$DecodedTextureBuffer");
|
||||
j_texture_id_field_ = GetFieldID(
|
||||
jni, j_decoded_texture_buffer_class, "textureID", "I");
|
||||
j_transform_matrix_field_ = GetFieldID(
|
||||
jni, j_decoded_texture_buffer_class, "transformMatrix", "[F");
|
||||
j_texture_presentation_timestamp_ms_field_ = GetFieldID(
|
||||
jni, j_decoded_texture_buffer_class, "presentationTimeStampMs", "J");
|
||||
j_texture_timestamp_ms_field_ = GetFieldID(
|
||||
jni, j_decoded_texture_buffer_class, "timeStampMs", "J");
|
||||
j_texture_ntp_timestamp_ms_field_ = GetFieldID(
|
||||
jni, j_decoded_texture_buffer_class, "ntpTimeStampMs", "J");
|
||||
j_texture_decode_time_ms_field_ = GetFieldID(
|
||||
jni, j_decoded_texture_buffer_class, "decodeTimeMs", "J");
|
||||
j_texture_frame_delay_ms_field_ = GetFieldID(
|
||||
jni, j_decoded_texture_buffer_class, "frameDelayMs", "J");
|
||||
|
||||
jclass j_decoded_output_buffer_class = FindClass(jni,
|
||||
"org/webrtc/MediaCodecVideoDecoder$DecodedOutputBuffer");
|
||||
j_info_index_field_ = GetFieldID(
|
||||
jni, j_decoded_output_buffer_class, "index", "I");
|
||||
j_info_offset_field_ = GetFieldID(
|
||||
jni, j_decoded_output_buffer_class, "offset", "I");
|
||||
j_info_size_field_ = GetFieldID(
|
||||
jni, j_decoded_output_buffer_class, "size", "I");
|
||||
j_presentation_timestamp_ms_field_ = GetFieldID(
|
||||
jni, j_decoded_output_buffer_class, "presentationTimeStampMs", "J");
|
||||
j_timestamp_ms_field_ = GetFieldID(
|
||||
jni, j_decoded_output_buffer_class, "timeStampMs", "J");
|
||||
j_ntp_timestamp_ms_field_ = GetFieldID(
|
||||
jni, j_decoded_output_buffer_class, "ntpTimeStampMs", "J");
|
||||
j_byte_buffer_decode_time_ms_field_ = GetFieldID(
|
||||
jni, j_decoded_output_buffer_class, "decodeTimeMs", "J");
|
||||
|
||||
CHECK_EXCEPTION(jni) << "MediaCodecVideoDecoder ctor failed";
|
||||
use_surface_ = (render_egl_context_ != NULL);
|
||||
ALOGD << "MediaCodecVideoDecoder ctor. Use surface: " << use_surface_;
|
||||
memset(&codec_, 0, sizeof(codec_));
|
||||
AllowBlockingCalls();
|
||||
}
|
||||
|
||||
MediaCodecVideoDecoder::~MediaCodecVideoDecoder() {
|
||||
// Call Release() to ensure no more callbacks to us after we are deleted.
|
||||
Release();
|
||||
}
|
||||
|
||||
int32_t MediaCodecVideoDecoder::InitDecode(const VideoCodec* inst,
|
||||
int32_t numberOfCores) {
|
||||
ALOGD << "InitDecode.";
|
||||
if (inst == NULL) {
|
||||
ALOGE << "NULL VideoCodec instance";
|
||||
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
||||
}
|
||||
// Factory should guard against other codecs being used with us.
|
||||
RTC_CHECK(inst->codecType == codecType_)
|
||||
<< "Unsupported codec " << inst->codecType << " for " << codecType_;
|
||||
|
||||
if (sw_fallback_required_) {
|
||||
ALOGE << "InitDecode() - fallback to SW decoder";
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
// Save VideoCodec instance for later.
|
||||
if (&codec_ != inst) {
|
||||
codec_ = *inst;
|
||||
}
|
||||
// If maxFramerate is not set then assume 30 fps.
|
||||
codec_.maxFramerate = (codec_.maxFramerate >= 1) ? codec_.maxFramerate : 30;
|
||||
|
||||
// Call Java init.
|
||||
return codec_thread_->Invoke<int32_t>(
|
||||
RTC_FROM_HERE,
|
||||
Bind(&MediaCodecVideoDecoder::InitDecodeOnCodecThread, this));
|
||||
}
|
||||
|
||||
void MediaCodecVideoDecoder::ResetVariables() {
|
||||
CheckOnCodecThread();
|
||||
|
||||
key_frame_required_ = true;
|
||||
frames_received_ = 0;
|
||||
frames_decoded_ = 0;
|
||||
frames_decoded_logged_ = kMaxDecodedLogFrames;
|
||||
start_time_ms_ = rtc::TimeMillis();
|
||||
current_frames_ = 0;
|
||||
current_bytes_ = 0;
|
||||
current_decoding_time_ms_ = 0;
|
||||
current_delay_time_ms_ = 0;
|
||||
}
|
||||
|
||||
int32_t MediaCodecVideoDecoder::InitDecodeOnCodecThread() {
|
||||
CheckOnCodecThread();
|
||||
JNIEnv* jni = AttachCurrentThreadIfNeeded();
|
||||
ScopedLocalRefFrame local_ref_frame(jni);
|
||||
ALOGD << "InitDecodeOnCodecThread Type: " << (int)codecType_ << ". "
|
||||
<< codec_.width << " x " << codec_.height << ". Fps: " <<
|
||||
(int)codec_.maxFramerate;
|
||||
|
||||
// Release previous codec first if it was allocated before.
|
||||
int ret_val = ReleaseOnCodecThread();
|
||||
if (ret_val < 0) {
|
||||
ALOGE << "Release failure: " << ret_val << " - fallback to SW codec";
|
||||
sw_fallback_required_ = true;
|
||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||
}
|
||||
|
||||
ResetVariables();
|
||||
|
||||
if (use_surface_) {
|
||||
surface_texture_helper_ = SurfaceTextureHelper::create(
|
||||
jni, "Decoder SurfaceTextureHelper", render_egl_context_);
|
||||
if (!surface_texture_helper_) {
|
||||
ALOGE << "Couldn't create SurfaceTextureHelper - fallback to SW codec";
|
||||
sw_fallback_required_ = true;
|
||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
jobject j_video_codec_enum = JavaEnumFromIndexAndClassName(
|
||||
jni, "MediaCodecVideoDecoder$VideoCodecType", codecType_);
|
||||
bool success = jni->CallBooleanMethod(
|
||||
*j_media_codec_video_decoder_,
|
||||
j_init_decode_method_,
|
||||
j_video_codec_enum,
|
||||
codec_.width,
|
||||
codec_.height,
|
||||
use_surface_ ? surface_texture_helper_->GetJavaSurfaceTextureHelper()
|
||||
: nullptr);
|
||||
|
||||
if (CheckException(jni) || !success) {
|
||||
ALOGE << "Codec initialization error - fallback to SW codec.";
|
||||
sw_fallback_required_ = true;
|
||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||
}
|
||||
inited_ = true;
|
||||
|
||||
switch (codecType_) {
|
||||
case kVideoCodecVP8:
|
||||
max_pending_frames_ = kMaxPendingFramesVp8;
|
||||
break;
|
||||
case kVideoCodecVP9:
|
||||
max_pending_frames_ = kMaxPendingFramesVp9;
|
||||
break;
|
||||
case kVideoCodecH264:
|
||||
max_pending_frames_ = kMaxPendingFramesH264;
|
||||
break;
|
||||
default:
|
||||
max_pending_frames_ = 0;
|
||||
}
|
||||
ALOGD << "Maximum amount of pending frames: " << max_pending_frames_;
|
||||
|
||||
jobjectArray input_buffers = (jobjectArray)GetObjectField(
|
||||
jni, *j_media_codec_video_decoder_, j_input_buffers_field_);
|
||||
size_t num_input_buffers = jni->GetArrayLength(input_buffers);
|
||||
input_buffers_.resize(num_input_buffers);
|
||||
for (size_t i = 0; i < num_input_buffers; ++i) {
|
||||
input_buffers_[i] =
|
||||
jni->NewGlobalRef(jni->GetObjectArrayElement(input_buffers, i));
|
||||
if (CheckException(jni)) {
|
||||
ALOGE << "NewGlobalRef error - fallback to SW codec.";
|
||||
sw_fallback_required_ = true;
|
||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
codec_thread_->PostDelayed(RTC_FROM_HERE, kMediaCodecPollMs, this);
|
||||
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
|
||||
int32_t MediaCodecVideoDecoder::ResetDecodeOnCodecThread() {
|
||||
CheckOnCodecThread();
|
||||
JNIEnv* jni = AttachCurrentThreadIfNeeded();
|
||||
ScopedLocalRefFrame local_ref_frame(jni);
|
||||
ALOGD << "ResetDecodeOnCodecThread Type: " << (int)codecType_ << ". "
|
||||
<< codec_.width << " x " << codec_.height;
|
||||
ALOGD << " Frames received: " << frames_received_ <<
|
||||
". Frames decoded: " << frames_decoded_;
|
||||
|
||||
inited_ = false;
|
||||
rtc::MessageQueueManager::Clear(this);
|
||||
ResetVariables();
|
||||
|
||||
jni->CallVoidMethod(
|
||||
*j_media_codec_video_decoder_,
|
||||
j_reset_method_,
|
||||
codec_.width,
|
||||
codec_.height);
|
||||
|
||||
if (CheckException(jni)) {
|
||||
ALOGE << "Soft reset error - fallback to SW codec.";
|
||||
sw_fallback_required_ = true;
|
||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||
}
|
||||
inited_ = true;
|
||||
|
||||
codec_thread_->PostDelayed(RTC_FROM_HERE, kMediaCodecPollMs, this);
|
||||
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
|
||||
int32_t MediaCodecVideoDecoder::Release() {
|
||||
ALOGD << "DecoderRelease request";
|
||||
return codec_thread_->Invoke<int32_t>(
|
||||
RTC_FROM_HERE, Bind(&MediaCodecVideoDecoder::ReleaseOnCodecThread, this));
|
||||
}
|
||||
|
||||
int32_t MediaCodecVideoDecoder::ReleaseOnCodecThread() {
|
||||
if (!inited_) {
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
CheckOnCodecThread();
|
||||
JNIEnv* jni = AttachCurrentThreadIfNeeded();
|
||||
ALOGD << "DecoderReleaseOnCodecThread: Frames received: " <<
|
||||
frames_received_ << ". Frames decoded: " << frames_decoded_;
|
||||
ScopedLocalRefFrame local_ref_frame(jni);
|
||||
for (size_t i = 0; i < input_buffers_.size(); i++) {
|
||||
jni->DeleteGlobalRef(input_buffers_[i]);
|
||||
}
|
||||
input_buffers_.clear();
|
||||
jni->CallVoidMethod(*j_media_codec_video_decoder_, j_release_method_);
|
||||
surface_texture_helper_ = nullptr;
|
||||
inited_ = false;
|
||||
rtc::MessageQueueManager::Clear(this);
|
||||
if (CheckException(jni)) {
|
||||
ALOGE << "Decoder release exception";
|
||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||
}
|
||||
ALOGD << "DecoderReleaseOnCodecThread done";
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
|
||||
void MediaCodecVideoDecoder::CheckOnCodecThread() {
|
||||
RTC_CHECK(codec_thread_.get() == ThreadManager::Instance()->CurrentThread())
|
||||
<< "Running on wrong thread!";
|
||||
}
|
||||
|
||||
void MediaCodecVideoDecoder::EnableFrameLogOnWarning() {
|
||||
// Log next 2 output frames.
|
||||
frames_decoded_logged_ = std::max(
|
||||
frames_decoded_logged_, frames_decoded_ + kMaxWarningLogFrames);
|
||||
}
|
||||
|
||||
int32_t MediaCodecVideoDecoder::ProcessHWErrorOnCodecThread() {
|
||||
CheckOnCodecThread();
|
||||
int ret_val = ReleaseOnCodecThread();
|
||||
if (ret_val < 0) {
|
||||
ALOGE << "ProcessHWError: Release failure";
|
||||
}
|
||||
if (codecType_ == kVideoCodecH264) {
|
||||
// For now there is no SW H.264 which can be used as fallback codec.
|
||||
// So try to restart hw codec for now.
|
||||
ret_val = InitDecodeOnCodecThread();
|
||||
ALOGE << "Reset H.264 codec done. Status: " << ret_val;
|
||||
if (ret_val == WEBRTC_VIDEO_CODEC_OK) {
|
||||
// H.264 codec was succesfully reset - return regular error code.
|
||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||
} else {
|
||||
// Fail to restart H.264 codec - return error code which should stop the
|
||||
// call.
|
||||
return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
|
||||
}
|
||||
} else {
|
||||
sw_fallback_required_ = true;
|
||||
ALOGE << "Return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE";
|
||||
return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t MediaCodecVideoDecoder::Decode(
|
||||
const EncodedImage& inputImage,
|
||||
bool missingFrames,
|
||||
const RTPFragmentationHeader* fragmentation,
|
||||
const CodecSpecificInfo* codecSpecificInfo,
|
||||
int64_t renderTimeMs) {
|
||||
if (sw_fallback_required_) {
|
||||
ALOGE << "Decode() - fallback to SW codec";
|
||||
return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
|
||||
}
|
||||
if (callback_ == NULL) {
|
||||
ALOGE << "Decode() - callback_ is NULL";
|
||||
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
||||
}
|
||||
if (inputImage._buffer == NULL && inputImage._length > 0) {
|
||||
ALOGE << "Decode() - inputImage is incorrect";
|
||||
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
||||
}
|
||||
if (!inited_) {
|
||||
ALOGE << "Decode() - decoder is not initialized";
|
||||
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
||||
}
|
||||
|
||||
// Check if encoded frame dimension has changed.
|
||||
if ((inputImage._encodedWidth * inputImage._encodedHeight > 0) &&
|
||||
(inputImage._encodedWidth != codec_.width ||
|
||||
inputImage._encodedHeight != codec_.height)) {
|
||||
ALOGW << "Input resolution changed from " <<
|
||||
codec_.width << " x " << codec_.height << " to " <<
|
||||
inputImage._encodedWidth << " x " << inputImage._encodedHeight;
|
||||
codec_.width = inputImage._encodedWidth;
|
||||
codec_.height = inputImage._encodedHeight;
|
||||
int32_t ret;
|
||||
if (use_surface_ &&
|
||||
(codecType_ == kVideoCodecVP8 || codecType_ == kVideoCodecH264)) {
|
||||
// Soft codec reset - only for surface decoding.
|
||||
ret = codec_thread_->Invoke<int32_t>(
|
||||
RTC_FROM_HERE,
|
||||
Bind(&MediaCodecVideoDecoder::ResetDecodeOnCodecThread, this));
|
||||
} else {
|
||||
// Hard codec reset.
|
||||
ret = InitDecode(&codec_, 1);
|
||||
}
|
||||
if (ret < 0) {
|
||||
ALOGE << "InitDecode failure: " << ret << " - fallback to SW codec";
|
||||
sw_fallback_required_ = true;
|
||||
return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
|
||||
}
|
||||
}
|
||||
|
||||
// Always start with a complete key frame.
|
||||
if (key_frame_required_) {
|
||||
if (inputImage._frameType != webrtc::kVideoFrameKey) {
|
||||
ALOGE << "Decode() - key frame is required";
|
||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||
}
|
||||
if (!inputImage._completeFrame) {
|
||||
ALOGE << "Decode() - complete frame is required";
|
||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||
}
|
||||
key_frame_required_ = false;
|
||||
}
|
||||
if (inputImage._length == 0) {
|
||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||
}
|
||||
|
||||
return codec_thread_->Invoke<int32_t>(
|
||||
RTC_FROM_HERE,
|
||||
Bind(&MediaCodecVideoDecoder::DecodeOnCodecThread, this, inputImage));
|
||||
}
|
||||
|
||||
int32_t MediaCodecVideoDecoder::DecodeOnCodecThread(
|
||||
const EncodedImage& inputImage) {
|
||||
CheckOnCodecThread();
|
||||
JNIEnv* jni = AttachCurrentThreadIfNeeded();
|
||||
ScopedLocalRefFrame local_ref_frame(jni);
|
||||
|
||||
// Try to drain the decoder and wait until output is not too
|
||||
// much behind the input.
|
||||
if (codecType_ == kVideoCodecH264 &&
|
||||
frames_received_ > frames_decoded_ + max_pending_frames_) {
|
||||
// Print warning for H.264 only - for VP8/VP9 one frame delay is ok.
|
||||
ALOGW << "Decoder is too far behind. Try to drain. Received: " <<
|
||||
frames_received_ << ". Decoded: " << frames_decoded_;
|
||||
EnableFrameLogOnWarning();
|
||||
}
|
||||
const int64 drain_start = rtc::TimeMillis();
|
||||
while ((frames_received_ > frames_decoded_ + max_pending_frames_) &&
|
||||
(rtc::TimeMillis() - drain_start) < kMediaCodecTimeoutMs) {
|
||||
if (!DeliverPendingOutputs(jni, kMediaCodecPollMs)) {
|
||||
ALOGE << "DeliverPendingOutputs error. Frames received: " <<
|
||||
frames_received_ << ". Frames decoded: " << frames_decoded_;
|
||||
return ProcessHWErrorOnCodecThread();
|
||||
}
|
||||
}
|
||||
if (frames_received_ > frames_decoded_ + max_pending_frames_) {
|
||||
ALOGE << "Output buffer dequeue timeout. Frames received: " <<
|
||||
frames_received_ << ". Frames decoded: " << frames_decoded_;
|
||||
return ProcessHWErrorOnCodecThread();
|
||||
}
|
||||
|
||||
// Get input buffer.
|
||||
int j_input_buffer_index = jni->CallIntMethod(
|
||||
*j_media_codec_video_decoder_, j_dequeue_input_buffer_method_);
|
||||
if (CheckException(jni) || j_input_buffer_index < 0) {
|
||||
ALOGE << "dequeueInputBuffer error: " << j_input_buffer_index <<
|
||||
". Retry DeliverPendingOutputs.";
|
||||
EnableFrameLogOnWarning();
|
||||
// Try to drain the decoder.
|
||||
if (!DeliverPendingOutputs(jni, kMediaCodecPollMs)) {
|
||||
ALOGE << "DeliverPendingOutputs error. Frames received: " <<
|
||||
frames_received_ << ". Frames decoded: " << frames_decoded_;
|
||||
return ProcessHWErrorOnCodecThread();
|
||||
}
|
||||
// Try dequeue input buffer one last time.
|
||||
j_input_buffer_index = jni->CallIntMethod(
|
||||
*j_media_codec_video_decoder_, j_dequeue_input_buffer_method_);
|
||||
if (CheckException(jni) || j_input_buffer_index < 0) {
|
||||
ALOGE << "dequeueInputBuffer critical error: " << j_input_buffer_index;
|
||||
return ProcessHWErrorOnCodecThread();
|
||||
}
|
||||
}
|
||||
|
||||
// Copy encoded data to Java ByteBuffer.
|
||||
jobject j_input_buffer = input_buffers_[j_input_buffer_index];
|
||||
uint8_t* buffer =
|
||||
reinterpret_cast<uint8_t*>(jni->GetDirectBufferAddress(j_input_buffer));
|
||||
RTC_CHECK(buffer) << "Indirect buffer??";
|
||||
int64_t buffer_capacity = jni->GetDirectBufferCapacity(j_input_buffer);
|
||||
if (CheckException(jni) || buffer_capacity < inputImage._length) {
|
||||
ALOGE << "Input frame size "<< inputImage._length <<
|
||||
" is bigger than buffer size " << buffer_capacity;
|
||||
return ProcessHWErrorOnCodecThread();
|
||||
}
|
||||
jlong presentation_timestamp_us = static_cast<jlong>(
|
||||
static_cast<int64_t>(frames_received_) * 1000000 / codec_.maxFramerate);
|
||||
memcpy(buffer, inputImage._buffer, inputImage._length);
|
||||
|
||||
if (frames_decoded_ < frames_decoded_logged_) {
|
||||
ALOGD << "Decoder frame in # " << frames_received_ <<
|
||||
". Type: " << inputImage._frameType <<
|
||||
". Buffer # " << j_input_buffer_index <<
|
||||
". TS: " << presentation_timestamp_us / 1000 <<
|
||||
". Size: " << inputImage._length;
|
||||
}
|
||||
|
||||
// Save input image timestamps for later output.
|
||||
frames_received_++;
|
||||
current_bytes_ += inputImage._length;
|
||||
|
||||
// Feed input to decoder.
|
||||
bool success = jni->CallBooleanMethod(
|
||||
*j_media_codec_video_decoder_,
|
||||
j_queue_input_buffer_method_,
|
||||
j_input_buffer_index,
|
||||
inputImage._length,
|
||||
presentation_timestamp_us,
|
||||
static_cast<int64_t> (inputImage._timeStamp),
|
||||
inputImage.ntp_time_ms_);
|
||||
if (CheckException(jni) || !success) {
|
||||
ALOGE << "queueInputBuffer error";
|
||||
return ProcessHWErrorOnCodecThread();
|
||||
}
|
||||
|
||||
// Try to drain the decoder
|
||||
if (!DeliverPendingOutputs(jni, 0)) {
|
||||
ALOGE << "DeliverPendingOutputs error";
|
||||
return ProcessHWErrorOnCodecThread();
|
||||
}
|
||||
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
|
||||
bool MediaCodecVideoDecoder::DeliverPendingOutputs(
|
||||
JNIEnv* jni, int dequeue_timeout_ms) {
|
||||
if (frames_received_ <= frames_decoded_) {
|
||||
// No need to query for output buffers - decoder is drained.
|
||||
return true;
|
||||
}
|
||||
// Get decoder output.
|
||||
jobject j_decoder_output_buffer =
|
||||
jni->CallObjectMethod(*j_media_codec_video_decoder_,
|
||||
use_surface_ ? j_dequeue_texture_buffer_method_
|
||||
: j_dequeue_byte_buffer_method_,
|
||||
dequeue_timeout_ms);
|
||||
|
||||
if (CheckException(jni)) {
|
||||
ALOGE << "dequeueOutputBuffer() error";
|
||||
return false;
|
||||
}
|
||||
if (IsNull(jni, j_decoder_output_buffer)) {
|
||||
// No decoded frame ready.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get decoded video frame properties.
|
||||
int color_format = GetIntField(jni, *j_media_codec_video_decoder_,
|
||||
j_color_format_field_);
|
||||
int width = GetIntField(jni, *j_media_codec_video_decoder_, j_width_field_);
|
||||
int height = GetIntField(jni, *j_media_codec_video_decoder_, j_height_field_);
|
||||
int stride = GetIntField(jni, *j_media_codec_video_decoder_, j_stride_field_);
|
||||
int slice_height = GetIntField(jni, *j_media_codec_video_decoder_,
|
||||
j_slice_height_field_);
|
||||
|
||||
rtc::scoped_refptr<webrtc::VideoFrameBuffer> frame_buffer;
|
||||
int64_t presentation_timestamps_ms = 0;
|
||||
int64_t output_timestamps_ms = 0;
|
||||
int64_t output_ntp_timestamps_ms = 0;
|
||||
int decode_time_ms = 0;
|
||||
int64_t frame_delayed_ms = 0;
|
||||
if (use_surface_) {
|
||||
// Extract data from Java DecodedTextureBuffer.
|
||||
presentation_timestamps_ms = GetLongField(
|
||||
jni, j_decoder_output_buffer,
|
||||
j_texture_presentation_timestamp_ms_field_);
|
||||
output_timestamps_ms = GetLongField(
|
||||
jni, j_decoder_output_buffer, j_texture_timestamp_ms_field_);
|
||||
output_ntp_timestamps_ms = GetLongField(
|
||||
jni, j_decoder_output_buffer, j_texture_ntp_timestamp_ms_field_);
|
||||
decode_time_ms = GetLongField(
|
||||
jni, j_decoder_output_buffer, j_texture_decode_time_ms_field_);
|
||||
|
||||
const int texture_id =
|
||||
GetIntField(jni, j_decoder_output_buffer, j_texture_id_field_);
|
||||
if (texture_id != 0) { // |texture_id| == 0 represents a dropped frame.
|
||||
const jfloatArray j_transform_matrix =
|
||||
reinterpret_cast<jfloatArray>(GetObjectField(
|
||||
jni, j_decoder_output_buffer, j_transform_matrix_field_));
|
||||
frame_delayed_ms = GetLongField(
|
||||
jni, j_decoder_output_buffer, j_texture_frame_delay_ms_field_);
|
||||
|
||||
// Create webrtc::VideoFrameBuffer with native texture handle.
|
||||
frame_buffer = surface_texture_helper_->CreateTextureFrame(
|
||||
width, height, NativeHandleImpl(jni, texture_id, j_transform_matrix));
|
||||
} else {
|
||||
EnableFrameLogOnWarning();
|
||||
}
|
||||
} else {
|
||||
// Extract data from Java ByteBuffer and create output yuv420 frame -
|
||||
// for non surface decoding only.
|
||||
const int output_buffer_index = GetIntField(
|
||||
jni, j_decoder_output_buffer, j_info_index_field_);
|
||||
const int output_buffer_offset = GetIntField(
|
||||
jni, j_decoder_output_buffer, j_info_offset_field_);
|
||||
const int output_buffer_size = GetIntField(
|
||||
jni, j_decoder_output_buffer, j_info_size_field_);
|
||||
presentation_timestamps_ms = GetLongField(
|
||||
jni, j_decoder_output_buffer, j_presentation_timestamp_ms_field_);
|
||||
output_timestamps_ms = GetLongField(
|
||||
jni, j_decoder_output_buffer, j_timestamp_ms_field_);
|
||||
output_ntp_timestamps_ms = GetLongField(
|
||||
jni, j_decoder_output_buffer, j_ntp_timestamp_ms_field_);
|
||||
|
||||
decode_time_ms = GetLongField(jni, j_decoder_output_buffer,
|
||||
j_byte_buffer_decode_time_ms_field_);
|
||||
|
||||
if (output_buffer_size < width * height * 3 / 2) {
|
||||
ALOGE << "Insufficient output buffer size: " << output_buffer_size;
|
||||
return false;
|
||||
}
|
||||
if (output_buffer_size < stride * height * 3 / 2 &&
|
||||
slice_height == height && stride > width) {
|
||||
// Some codecs (Exynos) incorrectly report stride information for
|
||||
// output byte buffer, so actual stride value need to be corrected.
|
||||
stride = output_buffer_size * 2 / (height * 3);
|
||||
}
|
||||
jobjectArray output_buffers = reinterpret_cast<jobjectArray>(GetObjectField(
|
||||
jni, *j_media_codec_video_decoder_, j_output_buffers_field_));
|
||||
jobject output_buffer =
|
||||
jni->GetObjectArrayElement(output_buffers, output_buffer_index);
|
||||
uint8_t* payload = reinterpret_cast<uint8_t*>(jni->GetDirectBufferAddress(
|
||||
output_buffer));
|
||||
if (CheckException(jni)) {
|
||||
return false;
|
||||
}
|
||||
payload += output_buffer_offset;
|
||||
|
||||
// Create yuv420 frame.
|
||||
rtc::scoped_refptr<webrtc::I420Buffer> i420_buffer;
|
||||
|
||||
i420_buffer = decoded_frame_pool_.CreateBuffer(width, height);
|
||||
if (color_format == COLOR_FormatYUV420Planar) {
|
||||
RTC_CHECK_EQ(0, stride % 2);
|
||||
RTC_CHECK_EQ(0, slice_height % 2);
|
||||
const int uv_stride = stride / 2;
|
||||
const int u_slice_height = slice_height / 2;
|
||||
const uint8_t* y_ptr = payload;
|
||||
const uint8_t* u_ptr = y_ptr + stride * slice_height;
|
||||
const uint8_t* v_ptr = u_ptr + uv_stride * u_slice_height;
|
||||
libyuv::I420Copy(y_ptr, stride, u_ptr, uv_stride, v_ptr, uv_stride,
|
||||
i420_buffer->MutableDataY(), i420_buffer->StrideY(),
|
||||
i420_buffer->MutableDataU(), i420_buffer->StrideU(),
|
||||
i420_buffer->MutableDataV(), i420_buffer->StrideV(),
|
||||
width, height);
|
||||
} else {
|
||||
// All other supported formats are nv12.
|
||||
const uint8_t* y_ptr = payload;
|
||||
const uint8_t* uv_ptr = y_ptr + stride * slice_height;
|
||||
libyuv::NV12ToI420(y_ptr, stride, uv_ptr, stride,
|
||||
i420_buffer->MutableDataY(), i420_buffer->StrideY(),
|
||||
i420_buffer->MutableDataU(), i420_buffer->StrideU(),
|
||||
i420_buffer->MutableDataV(), i420_buffer->StrideV(),
|
||||
width, height);
|
||||
}
|
||||
frame_buffer = i420_buffer;
|
||||
|
||||
// Return output byte buffer back to codec.
|
||||
jni->CallVoidMethod(
|
||||
*j_media_codec_video_decoder_,
|
||||
j_return_decoded_byte_buffer_method_,
|
||||
output_buffer_index);
|
||||
if (CheckException(jni)) {
|
||||
ALOGE << "returnDecodedOutputBuffer error";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (frames_decoded_ < frames_decoded_logged_) {
|
||||
ALOGD << "Decoder frame out # " << frames_decoded_ <<
|
||||
". " << width << " x " << height <<
|
||||
". " << stride << " x " << slice_height <<
|
||||
". Color: " << color_format <<
|
||||
". TS: " << presentation_timestamps_ms <<
|
||||
". DecTime: " << (int)decode_time_ms <<
|
||||
". DelayTime: " << (int)frame_delayed_ms;
|
||||
}
|
||||
|
||||
// Calculate and print decoding statistics - every 3 seconds.
|
||||
frames_decoded_++;
|
||||
current_frames_++;
|
||||
current_decoding_time_ms_ += decode_time_ms;
|
||||
current_delay_time_ms_ += frame_delayed_ms;
|
||||
int statistic_time_ms = rtc::TimeMillis() - start_time_ms_;
|
||||
if (statistic_time_ms >= kMediaCodecStatisticsIntervalMs &&
|
||||
current_frames_ > 0) {
|
||||
int current_bitrate = current_bytes_ * 8 / statistic_time_ms;
|
||||
int current_fps =
|
||||
(current_frames_ * 1000 + statistic_time_ms / 2) / statistic_time_ms;
|
||||
ALOGD << "Frames decoded: " << frames_decoded_ <<
|
||||
". Received: " << frames_received_ <<
|
||||
". Bitrate: " << current_bitrate << " kbps" <<
|
||||
". Fps: " << current_fps <<
|
||||
". DecTime: " << (current_decoding_time_ms_ / current_frames_) <<
|
||||
". DelayTime: " << (current_delay_time_ms_ / current_frames_) <<
|
||||
" for last " << statistic_time_ms << " ms.";
|
||||
start_time_ms_ = rtc::TimeMillis();
|
||||
current_frames_ = 0;
|
||||
current_bytes_ = 0;
|
||||
current_decoding_time_ms_ = 0;
|
||||
current_delay_time_ms_ = 0;
|
||||
}
|
||||
|
||||
// If the frame was dropped, frame_buffer is left as nullptr.
|
||||
if (frame_buffer) {
|
||||
VideoFrame decoded_frame(frame_buffer, 0, 0, webrtc::kVideoRotation_0);
|
||||
decoded_frame.set_timestamp(output_timestamps_ms);
|
||||
decoded_frame.set_ntp_time_ms(output_ntp_timestamps_ms);
|
||||
|
||||
const int32_t callback_status =
|
||||
callback_->Decoded(decoded_frame, decode_time_ms);
|
||||
if (callback_status > 0) {
|
||||
ALOGE << "callback error";
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t MediaCodecVideoDecoder::RegisterDecodeCompleteCallback(
|
||||
DecodedImageCallback* callback) {
|
||||
callback_ = callback;
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
|
||||
void MediaCodecVideoDecoder::OnMessage(rtc::Message* msg) {
|
||||
JNIEnv* jni = AttachCurrentThreadIfNeeded();
|
||||
ScopedLocalRefFrame local_ref_frame(jni);
|
||||
if (!inited_) {
|
||||
return;
|
||||
}
|
||||
// We only ever send one message to |this| directly (not through a Bind()'d
|
||||
// functor), so expect no ID/data.
|
||||
RTC_CHECK(!msg->message_id) << "Unexpected message!";
|
||||
RTC_CHECK(!msg->pdata) << "Unexpected message!";
|
||||
CheckOnCodecThread();
|
||||
|
||||
if (!DeliverPendingOutputs(jni, 0)) {
|
||||
ALOGE << "OnMessage: DeliverPendingOutputs error";
|
||||
ProcessHWErrorOnCodecThread();
|
||||
return;
|
||||
}
|
||||
codec_thread_->PostDelayed(RTC_FROM_HERE, kMediaCodecPollMs, this);
|
||||
}
|
||||
|
||||
MediaCodecVideoDecoderFactory::MediaCodecVideoDecoderFactory()
|
||||
: egl_context_(nullptr) {
|
||||
ALOGD << "MediaCodecVideoDecoderFactory ctor";
|
||||
JNIEnv* jni = AttachCurrentThreadIfNeeded();
|
||||
ScopedLocalRefFrame local_ref_frame(jni);
|
||||
jclass j_decoder_class = FindClass(jni, "org/webrtc/MediaCodecVideoDecoder");
|
||||
supported_codec_types_.clear();
|
||||
|
||||
bool is_vp8_hw_supported = jni->CallStaticBooleanMethod(
|
||||
j_decoder_class,
|
||||
GetStaticMethodID(jni, j_decoder_class, "isVp8HwSupported", "()Z"));
|
||||
if (CheckException(jni)) {
|
||||
is_vp8_hw_supported = false;
|
||||
}
|
||||
if (is_vp8_hw_supported) {
|
||||
ALOGD << "VP8 HW Decoder supported.";
|
||||
supported_codec_types_.push_back(kVideoCodecVP8);
|
||||
}
|
||||
|
||||
bool is_vp9_hw_supported = jni->CallStaticBooleanMethod(
|
||||
j_decoder_class,
|
||||
GetStaticMethodID(jni, j_decoder_class, "isVp9HwSupported", "()Z"));
|
||||
if (CheckException(jni)) {
|
||||
is_vp9_hw_supported = false;
|
||||
}
|
||||
if (is_vp9_hw_supported) {
|
||||
ALOGD << "VP9 HW Decoder supported.";
|
||||
supported_codec_types_.push_back(kVideoCodecVP9);
|
||||
}
|
||||
|
||||
bool is_h264_hw_supported = jni->CallStaticBooleanMethod(
|
||||
j_decoder_class,
|
||||
GetStaticMethodID(jni, j_decoder_class, "isH264HwSupported", "()Z"));
|
||||
if (CheckException(jni)) {
|
||||
is_h264_hw_supported = false;
|
||||
}
|
||||
if (is_h264_hw_supported) {
|
||||
ALOGD << "H264 HW Decoder supported.";
|
||||
supported_codec_types_.push_back(kVideoCodecH264);
|
||||
}
|
||||
}
|
||||
|
||||
MediaCodecVideoDecoderFactory::~MediaCodecVideoDecoderFactory() {
|
||||
ALOGD << "MediaCodecVideoDecoderFactory dtor";
|
||||
if (egl_context_) {
|
||||
JNIEnv* jni = AttachCurrentThreadIfNeeded();
|
||||
jni->DeleteGlobalRef(egl_context_);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaCodecVideoDecoderFactory::SetEGLContext(
|
||||
JNIEnv* jni, jobject egl_context) {
|
||||
ALOGD << "MediaCodecVideoDecoderFactory::SetEGLContext";
|
||||
if (egl_context_) {
|
||||
jni->DeleteGlobalRef(egl_context_);
|
||||
egl_context_ = nullptr;
|
||||
}
|
||||
egl_context_ = jni->NewGlobalRef(egl_context);
|
||||
if (CheckException(jni)) {
|
||||
ALOGE << "error calling NewGlobalRef for EGL Context.";
|
||||
}
|
||||
}
|
||||
|
||||
webrtc::VideoDecoder* MediaCodecVideoDecoderFactory::CreateVideoDecoder(
|
||||
VideoCodecType type) {
|
||||
if (supported_codec_types_.empty()) {
|
||||
ALOGW << "No HW video decoder for type " << (int)type;
|
||||
return nullptr;
|
||||
}
|
||||
for (VideoCodecType codec_type : supported_codec_types_) {
|
||||
if (codec_type == type) {
|
||||
ALOGD << "Create HW video decoder for type " << (int)type;
|
||||
return new MediaCodecVideoDecoder(AttachCurrentThreadIfNeeded(), type,
|
||||
egl_context_);
|
||||
}
|
||||
}
|
||||
ALOGW << "Can not find HW video decoder for type " << (int)type;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MediaCodecVideoDecoderFactory::DestroyVideoDecoder(
|
||||
webrtc::VideoDecoder* decoder) {
|
||||
ALOGD << "Destroy video decoder.";
|
||||
delete decoder;
|
||||
}
|
||||
|
||||
const char* MediaCodecVideoDecoder::ImplementationName() const {
|
||||
return "MediaCodec";
|
||||
}
|
||||
|
||||
} // namespace webrtc_jni
|
||||
41
webrtc/sdk/android/src/jni/androidmediadecoder_jni.h
Normal file
41
webrtc/sdk/android/src/jni/androidmediadecoder_jni.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2015 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_ANDROIDMEDIADECODER_JNI_H_
|
||||
#define WEBRTC_SDK_ANDROID_SRC_JNI_ANDROIDMEDIADECODER_JNI_H_
|
||||
|
||||
#include "webrtc/sdk/android/src/jni/jni_helpers.h"
|
||||
#include "webrtc/media/engine/webrtcvideodecoderfactory.h"
|
||||
|
||||
namespace webrtc_jni {
|
||||
|
||||
// Implementation of Android MediaCodec based decoder factory.
|
||||
class MediaCodecVideoDecoderFactory
|
||||
: public cricket::WebRtcVideoDecoderFactory {
|
||||
public:
|
||||
MediaCodecVideoDecoderFactory();
|
||||
virtual ~MediaCodecVideoDecoderFactory();
|
||||
|
||||
void SetEGLContext(JNIEnv* jni, jobject render_egl_context);
|
||||
|
||||
// WebRtcVideoDecoderFactory implementation.
|
||||
webrtc::VideoDecoder* CreateVideoDecoder(webrtc::VideoCodecType type)
|
||||
override;
|
||||
|
||||
void DestroyVideoDecoder(webrtc::VideoDecoder* decoder) override;
|
||||
|
||||
private:
|
||||
jobject egl_context_;
|
||||
std::vector<webrtc::VideoCodecType> supported_codec_types_;
|
||||
};
|
||||
|
||||
} // namespace webrtc_jni
|
||||
|
||||
#endif // WEBRTC_SDK_ANDROID_SRC_JNI_ANDROIDMEDIADECODER_JNI_H_
|
||||
1288
webrtc/sdk/android/src/jni/androidmediaencoder_jni.cc
Normal file
1288
webrtc/sdk/android/src/jni/androidmediaencoder_jni.cc
Normal file
File diff suppressed because it is too large
Load Diff
49
webrtc/sdk/android/src/jni/androidmediaencoder_jni.h
Normal file
49
webrtc/sdk/android/src/jni/androidmediaencoder_jni.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2015 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_ANDROIDMEDIAENCODER_JNI_H_
|
||||
#define WEBRTC_SDK_ANDROID_SRC_JNI_ANDROIDMEDIAENCODER_JNI_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/sdk/android/src/jni/jni_helpers.h"
|
||||
#include "webrtc/media/engine/webrtcvideoencoderfactory.h"
|
||||
|
||||
namespace webrtc_jni {
|
||||
|
||||
// Implementation of Android MediaCodec based encoder factory.
|
||||
class MediaCodecVideoEncoderFactory
|
||||
: public cricket::WebRtcVideoEncoderFactory {
|
||||
public:
|
||||
MediaCodecVideoEncoderFactory();
|
||||
virtual ~MediaCodecVideoEncoderFactory();
|
||||
|
||||
void SetEGLContext(JNIEnv* jni, jobject egl_context);
|
||||
|
||||
// WebRtcVideoEncoderFactory implementation.
|
||||
webrtc::VideoEncoder* CreateVideoEncoder(
|
||||
const cricket::VideoCodec& codec) override;
|
||||
const std::vector<cricket::VideoCodec>& supported_codecs() const override;
|
||||
void DestroyVideoEncoder(webrtc::VideoEncoder* encoder) override;
|
||||
|
||||
private:
|
||||
// Disable overloaded virtual function warning. TODO(magjed): Remove once
|
||||
// http://crbug/webrtc/6402 is fixed.
|
||||
using cricket::WebRtcVideoEncoderFactory::CreateVideoEncoder;
|
||||
|
||||
jobject egl_context_;
|
||||
|
||||
// Empty if platform support is lacking, const after ctor returns.
|
||||
std::vector<cricket::VideoCodec> supported_codecs_;
|
||||
};
|
||||
|
||||
} // namespace webrtc_jni
|
||||
|
||||
#endif // WEBRTC_SDK_ANDROID_SRC_JNI_ANDROIDMEDIAENCODER_JNI_H_
|
||||
61
webrtc/sdk/android/src/jni/androidmetrics_jni.cc
Normal file
61
webrtc/sdk/android/src/jni/androidmetrics_jni.cc
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 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 <map>
|
||||
#include <memory>
|
||||
|
||||
#include "webrtc/sdk/android/src/jni/classreferenceholder.h"
|
||||
#include "webrtc/sdk/android/src/jni/jni_helpers.h"
|
||||
#include "webrtc/system_wrappers/include/metrics.h"
|
||||
#include "webrtc/system_wrappers/include/metrics_default.h"
|
||||
|
||||
// Enables collection of native histograms and creating them.
|
||||
namespace webrtc_jni {
|
||||
|
||||
JOW(void, Metrics_nativeEnable)(JNIEnv* jni, jclass) {
|
||||
webrtc::metrics::Enable();
|
||||
}
|
||||
|
||||
// Gets and clears native histograms.
|
||||
JOW(jobject, Metrics_nativeGetAndReset)(JNIEnv* jni, jclass) {
|
||||
jclass j_metrics_class = jni->FindClass("org/webrtc/Metrics");
|
||||
jmethodID j_add =
|
||||
GetMethodID(jni, j_metrics_class, "add",
|
||||
"(Ljava/lang/String;Lorg/webrtc/Metrics$HistogramInfo;)V");
|
||||
jclass j_info_class = jni->FindClass("org/webrtc/Metrics$HistogramInfo");
|
||||
jmethodID j_add_sample = GetMethodID(jni, j_info_class, "addSample", "(II)V");
|
||||
|
||||
// Create |Metrics|.
|
||||
jobject j_metrics = jni->NewObject(
|
||||
j_metrics_class, GetMethodID(jni, j_metrics_class, "<init>", "()V"));
|
||||
|
||||
std::map<std::string, std::unique_ptr<webrtc::metrics::SampleInfo>>
|
||||
histograms;
|
||||
webrtc::metrics::GetAndReset(&histograms);
|
||||
for (const auto& kv : histograms) {
|
||||
// Create and add samples to |HistogramInfo|.
|
||||
jobject j_info = jni->NewObject(
|
||||
j_info_class, GetMethodID(jni, j_info_class, "<init>", "(III)V"),
|
||||
kv.second->min, kv.second->max,
|
||||
static_cast<int>(kv.second->bucket_count));
|
||||
for (const auto& sample : kv.second->samples) {
|
||||
jni->CallVoidMethod(j_info, j_add_sample, sample.first, sample.second);
|
||||
}
|
||||
// Add |HistogramInfo| to |Metrics|.
|
||||
jstring j_name = jni->NewStringUTF(kv.first.c_str());
|
||||
jni->CallVoidMethod(j_metrics, j_add, j_name, j_info);
|
||||
jni->DeleteLocalRef(j_name);
|
||||
jni->DeleteLocalRef(j_info);
|
||||
}
|
||||
CHECK_EXCEPTION(jni);
|
||||
return j_metrics;
|
||||
}
|
||||
|
||||
} // namespace webrtc_jni
|
||||
435
webrtc/sdk/android/src/jni/androidnetworkmonitor_jni.cc
Normal file
435
webrtc/sdk/android/src/jni/androidnetworkmonitor_jni.cc
Normal file
@ -0,0 +1,435 @@
|
||||
/*
|
||||
* Copyright 2015 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/androidnetworkmonitor_jni.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
// This was added in Lollipop to dlfcn.h
|
||||
#define RTLD_NOLOAD 4
|
||||
|
||||
#include "webrtc/sdk/android/src/jni/classreferenceholder.h"
|
||||
#include "webrtc/sdk/android/src/jni/jni_helpers.h"
|
||||
#include "webrtc/base/bind.h"
|
||||
#include "webrtc/base/common.h"
|
||||
#include "webrtc/base/ipaddress.h"
|
||||
|
||||
namespace webrtc_jni {
|
||||
|
||||
enum AndroidSdkVersion {
|
||||
SDK_VERSION_LOLLIPOP = 21,
|
||||
SDK_VERSION_MARSHMALLOW = 23
|
||||
};
|
||||
|
||||
jobject AndroidNetworkMonitor::application_context_ = nullptr;
|
||||
int AndroidNetworkMonitor::android_sdk_int_ = 0;
|
||||
|
||||
static NetworkType GetNetworkTypeFromJava(JNIEnv* jni, jobject j_network_type) {
|
||||
std::string enum_name =
|
||||
GetJavaEnumName(jni, "org/webrtc/NetworkMonitorAutoDetect$ConnectionType",
|
||||
j_network_type);
|
||||
if (enum_name == "CONNECTION_UNKNOWN") {
|
||||
return NetworkType::NETWORK_UNKNOWN;
|
||||
}
|
||||
if (enum_name == "CONNECTION_ETHERNET") {
|
||||
return NetworkType::NETWORK_ETHERNET;
|
||||
}
|
||||
if (enum_name == "CONNECTION_WIFI") {
|
||||
return NetworkType::NETWORK_WIFI;
|
||||
}
|
||||
if (enum_name == "CONNECTION_4G") {
|
||||
return NetworkType::NETWORK_4G;
|
||||
}
|
||||
if (enum_name == "CONNECTION_3G") {
|
||||
return NetworkType::NETWORK_3G;
|
||||
}
|
||||
if (enum_name == "CONNECTION_2G") {
|
||||
return NetworkType::NETWORK_2G;
|
||||
}
|
||||
if (enum_name == "CONNECTION_UNKNOWN_CELLULAR") {
|
||||
return NetworkType::NETWORK_UNKNOWN_CELLULAR;
|
||||
}
|
||||
if (enum_name == "CONNECTION_BLUETOOTH") {
|
||||
return NetworkType::NETWORK_BLUETOOTH;
|
||||
}
|
||||
if (enum_name == "CONNECTION_NONE") {
|
||||
return NetworkType::NETWORK_NONE;
|
||||
}
|
||||
ASSERT(false);
|
||||
return NetworkType::NETWORK_UNKNOWN;
|
||||
}
|
||||
|
||||
static rtc::AdapterType AdapterTypeFromNetworkType(NetworkType network_type) {
|
||||
switch (network_type) {
|
||||
case NETWORK_UNKNOWN:
|
||||
return rtc::ADAPTER_TYPE_UNKNOWN;
|
||||
case NETWORK_ETHERNET:
|
||||
return rtc::ADAPTER_TYPE_ETHERNET;
|
||||
case NETWORK_WIFI:
|
||||
return rtc::ADAPTER_TYPE_WIFI;
|
||||
case NETWORK_4G:
|
||||
case NETWORK_3G:
|
||||
case NETWORK_2G:
|
||||
case NETWORK_UNKNOWN_CELLULAR:
|
||||
return rtc::ADAPTER_TYPE_CELLULAR;
|
||||
case NETWORK_BLUETOOTH:
|
||||
// There is no corresponding mapping for bluetooth networks.
|
||||
// Map it to VPN for now.
|
||||
return rtc::ADAPTER_TYPE_VPN;
|
||||
default:
|
||||
RTC_DCHECK(false) << "Invalid network type " << network_type;
|
||||
return rtc::ADAPTER_TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static rtc::IPAddress GetIPAddressFromJava(JNIEnv* jni, jobject j_ip_address) {
|
||||
jclass j_ip_address_class = GetObjectClass(jni, j_ip_address);
|
||||
jfieldID j_address_id = GetFieldID(jni, j_ip_address_class, "address", "[B");
|
||||
jbyteArray j_addresses =
|
||||
static_cast<jbyteArray>(GetObjectField(jni, j_ip_address, j_address_id));
|
||||
size_t address_length = jni->GetArrayLength(j_addresses);
|
||||
jbyte* addr_array = jni->GetByteArrayElements(j_addresses, nullptr);
|
||||
CHECK_EXCEPTION(jni) << "Error during GetIPAddressFromJava";
|
||||
if (address_length == 4) {
|
||||
// IP4
|
||||
struct in_addr ip4_addr;
|
||||
memcpy(&ip4_addr.s_addr, addr_array, 4);
|
||||
jni->ReleaseByteArrayElements(j_addresses, addr_array, JNI_ABORT);
|
||||
return rtc::IPAddress(ip4_addr);
|
||||
}
|
||||
// IP6
|
||||
RTC_CHECK(address_length == 16);
|
||||
struct in6_addr ip6_addr;
|
||||
memcpy(ip6_addr.s6_addr, addr_array, address_length);
|
||||
jni->ReleaseByteArrayElements(j_addresses, addr_array, JNI_ABORT);
|
||||
return rtc::IPAddress(ip6_addr);
|
||||
}
|
||||
|
||||
static void GetIPAddressesFromJava(JNIEnv* jni,
|
||||
jobjectArray j_ip_addresses,
|
||||
std::vector<rtc::IPAddress>* ip_addresses) {
|
||||
ip_addresses->clear();
|
||||
size_t num_addresses = jni->GetArrayLength(j_ip_addresses);
|
||||
CHECK_EXCEPTION(jni) << "Error during GetArrayLength";
|
||||
for (size_t i = 0; i < num_addresses; ++i) {
|
||||
jobject j_ip_address = jni->GetObjectArrayElement(j_ip_addresses, i);
|
||||
CHECK_EXCEPTION(jni) << "Error during GetObjectArrayElement";
|
||||
rtc::IPAddress ip = GetIPAddressFromJava(jni, j_ip_address);
|
||||
ip_addresses->push_back(ip);
|
||||
}
|
||||
}
|
||||
|
||||
static NetworkInformation GetNetworkInformationFromJava(
|
||||
JNIEnv* jni,
|
||||
jobject j_network_info) {
|
||||
jclass j_network_info_class = GetObjectClass(jni, j_network_info);
|
||||
jfieldID j_interface_name_id =
|
||||
GetFieldID(jni, j_network_info_class, "name", "Ljava/lang/String;");
|
||||
jfieldID j_handle_id = GetFieldID(jni, j_network_info_class, "handle", "J");
|
||||
jfieldID j_type_id =
|
||||
GetFieldID(jni, j_network_info_class, "type",
|
||||
"Lorg/webrtc/NetworkMonitorAutoDetect$ConnectionType;");
|
||||
jfieldID j_ip_addresses_id =
|
||||
GetFieldID(jni, j_network_info_class, "ipAddresses",
|
||||
"[Lorg/webrtc/NetworkMonitorAutoDetect$IPAddress;");
|
||||
|
||||
NetworkInformation network_info;
|
||||
network_info.interface_name = JavaToStdString(
|
||||
jni, GetStringField(jni, j_network_info, j_interface_name_id));
|
||||
network_info.handle = static_cast<NetworkHandle>(
|
||||
GetLongField(jni, j_network_info, j_handle_id));
|
||||
network_info.type = GetNetworkTypeFromJava(
|
||||
jni, GetObjectField(jni, j_network_info, j_type_id));
|
||||
jobjectArray j_ip_addresses = static_cast<jobjectArray>(
|
||||
GetObjectField(jni, j_network_info, j_ip_addresses_id));
|
||||
GetIPAddressesFromJava(jni, j_ip_addresses, &network_info.ip_addresses);
|
||||
return network_info;
|
||||
}
|
||||
|
||||
std::string NetworkInformation::ToString() const {
|
||||
std::stringstream ss;
|
||||
ss << "NetInfo[name " << interface_name << "; handle " << handle << "; type "
|
||||
<< type << "; address";
|
||||
for (const rtc::IPAddress address : ip_addresses) {
|
||||
ss << " " << address.ToString();
|
||||
}
|
||||
ss << "]";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// static
|
||||
void AndroidNetworkMonitor::SetAndroidContext(JNIEnv* jni, jobject context) {
|
||||
if (application_context_) {
|
||||
jni->DeleteGlobalRef(application_context_);
|
||||
}
|
||||
application_context_ = NewGlobalRef(jni, context);
|
||||
}
|
||||
|
||||
AndroidNetworkMonitor::AndroidNetworkMonitor()
|
||||
: j_network_monitor_class_(jni(),
|
||||
FindClass(jni(), "org/webrtc/NetworkMonitor")),
|
||||
j_network_monitor_(
|
||||
jni(),
|
||||
jni()->CallStaticObjectMethod(
|
||||
*j_network_monitor_class_,
|
||||
GetStaticMethodID(
|
||||
jni(),
|
||||
*j_network_monitor_class_,
|
||||
"init",
|
||||
"(Landroid/content/Context;)Lorg/webrtc/NetworkMonitor;"),
|
||||
application_context_)) {
|
||||
ASSERT(application_context_ != nullptr);
|
||||
CHECK_EXCEPTION(jni()) << "Error during NetworkMonitor.init";
|
||||
if (android_sdk_int_ <= 0) {
|
||||
jmethodID m = GetStaticMethodID(jni(), *j_network_monitor_class_,
|
||||
"androidSdkInt", "()I");
|
||||
android_sdk_int_ = jni()->CallStaticIntMethod(*j_network_monitor_class_, m);
|
||||
CHECK_EXCEPTION(jni()) << "Error during NetworkMonitor.androidSdkInt";
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidNetworkMonitor::Start() {
|
||||
RTC_CHECK(thread_checker_.CalledOnValidThread());
|
||||
if (started_) {
|
||||
return;
|
||||
}
|
||||
started_ = true;
|
||||
|
||||
// This is kind of magic behavior, but doing this allows the SocketServer to
|
||||
// use this as a NetworkBinder to bind sockets on a particular network when
|
||||
// it creates sockets.
|
||||
worker_thread()->socketserver()->set_network_binder(this);
|
||||
|
||||
jmethodID m =
|
||||
GetMethodID(jni(), *j_network_monitor_class_, "startMonitoring", "(J)V");
|
||||
jni()->CallVoidMethod(*j_network_monitor_, m, jlongFromPointer(this));
|
||||
CHECK_EXCEPTION(jni()) << "Error during CallVoidMethod";
|
||||
}
|
||||
|
||||
void AndroidNetworkMonitor::Stop() {
|
||||
RTC_CHECK(thread_checker_.CalledOnValidThread());
|
||||
if (!started_) {
|
||||
return;
|
||||
}
|
||||
started_ = false;
|
||||
|
||||
// Once the network monitor stops, it will clear all network information and
|
||||
// it won't find the network handle to bind anyway.
|
||||
if (worker_thread()->socketserver()->network_binder() == this) {
|
||||
worker_thread()->socketserver()->set_network_binder(nullptr);
|
||||
}
|
||||
|
||||
jmethodID m =
|
||||
GetMethodID(jni(), *j_network_monitor_class_, "stopMonitoring", "(J)V");
|
||||
jni()->CallVoidMethod(*j_network_monitor_, m, jlongFromPointer(this));
|
||||
CHECK_EXCEPTION(jni()) << "Error during NetworkMonitor.stopMonitoring";
|
||||
|
||||
network_handle_by_address_.clear();
|
||||
network_info_by_handle_.clear();
|
||||
}
|
||||
|
||||
// The implementation is largely taken from UDPSocketPosix::BindToNetwork in
|
||||
// https://cs.chromium.org/chromium/src/net/udp/udp_socket_posix.cc
|
||||
int AndroidNetworkMonitor::BindSocketToNetwork(int socket_fd,
|
||||
const rtc::IPAddress& address) {
|
||||
RTC_CHECK(thread_checker_.CalledOnValidThread());
|
||||
// Android prior to Lollipop didn't have support for binding sockets to
|
||||
// networks. In that case it should not have reached here because
|
||||
// |network_handle_by_address_| is only populated in Android Lollipop
|
||||
// and above.
|
||||
if (android_sdk_int_ < SDK_VERSION_LOLLIPOP) {
|
||||
LOG(LS_ERROR) << "BindSocketToNetwork is not supported in Android SDK "
|
||||
<< android_sdk_int_;
|
||||
return rtc::NETWORK_BIND_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
auto iter = network_handle_by_address_.find(address);
|
||||
if (iter == network_handle_by_address_.end()) {
|
||||
return rtc::NETWORK_BIND_ADDRESS_NOT_FOUND;
|
||||
}
|
||||
NetworkHandle network_handle = iter->second;
|
||||
|
||||
int rv = 0;
|
||||
if (android_sdk_int_ >= SDK_VERSION_MARSHMALLOW) {
|
||||
// See declaration of android_setsocknetwork() here:
|
||||
// http://androidxref.com/6.0.0_r1/xref/development/ndk/platforms/android-M/include/android/multinetwork.h#65
|
||||
// Function cannot be called directly as it will cause app to fail to load
|
||||
// on pre-marshmallow devices.
|
||||
typedef int (*MarshmallowSetNetworkForSocket)(NetworkHandle net,
|
||||
int socket);
|
||||
static MarshmallowSetNetworkForSocket marshmallowSetNetworkForSocket;
|
||||
// This is not thread-safe, but we are running this only on the worker
|
||||
// thread.
|
||||
if (!marshmallowSetNetworkForSocket) {
|
||||
const std::string android_native_lib_path = "libandroid.so";
|
||||
void* lib = dlopen(android_native_lib_path.c_str(), RTLD_NOW);
|
||||
if (lib == nullptr) {
|
||||
LOG(LS_ERROR) << "Library " << android_native_lib_path << " not found!";
|
||||
return rtc::NETWORK_BIND_NOT_IMPLEMENTED;
|
||||
}
|
||||
marshmallowSetNetworkForSocket =
|
||||
reinterpret_cast<MarshmallowSetNetworkForSocket>(
|
||||
dlsym(lib, "android_setsocknetwork"));
|
||||
}
|
||||
if (!marshmallowSetNetworkForSocket) {
|
||||
LOG(LS_ERROR) << "Symbol marshmallowSetNetworkForSocket is not found";
|
||||
return rtc::NETWORK_BIND_NOT_IMPLEMENTED;
|
||||
}
|
||||
rv = marshmallowSetNetworkForSocket(network_handle, socket_fd);
|
||||
} else {
|
||||
// NOTE: This relies on Android implementation details, but it won't change
|
||||
// because Lollipop is already released.
|
||||
typedef int (*LollipopSetNetworkForSocket)(unsigned net, int socket);
|
||||
static LollipopSetNetworkForSocket lollipopSetNetworkForSocket;
|
||||
// This is not threadsafe, but we are running this only on the worker
|
||||
// thread.
|
||||
if (!lollipopSetNetworkForSocket) {
|
||||
// Android's netd client library should always be loaded in our address
|
||||
// space as it shims libc functions like connect().
|
||||
const std::string net_library_path = "libnetd_client.so";
|
||||
// Use RTLD_NOW to match Android's prior loading of the library:
|
||||
// http://androidxref.com/6.0.0_r5/xref/bionic/libc/bionic/NetdClient.cpp#37
|
||||
// Use RTLD_NOLOAD to assert that the library is already loaded and
|
||||
// avoid doing any disk IO.
|
||||
void* lib = dlopen(net_library_path.c_str(), RTLD_NOW | RTLD_NOLOAD);
|
||||
if (lib == nullptr) {
|
||||
LOG(LS_ERROR) << "Library " << net_library_path << " not found!";
|
||||
return rtc::NETWORK_BIND_NOT_IMPLEMENTED;
|
||||
}
|
||||
lollipopSetNetworkForSocket =
|
||||
reinterpret_cast<LollipopSetNetworkForSocket>(
|
||||
dlsym(lib, "setNetworkForSocket"));
|
||||
}
|
||||
if (!lollipopSetNetworkForSocket) {
|
||||
LOG(LS_ERROR) << "Symbol lollipopSetNetworkForSocket is not found ";
|
||||
return rtc::NETWORK_BIND_NOT_IMPLEMENTED;
|
||||
}
|
||||
rv = lollipopSetNetworkForSocket(network_handle, socket_fd);
|
||||
}
|
||||
|
||||
// If |network| has since disconnected, |rv| will be ENONET. Surface this as
|
||||
// ERR_NETWORK_CHANGED, rather than MapSystemError(ENONET) which gives back
|
||||
// the less descriptive ERR_FAILED.
|
||||
if (rv == 0) {
|
||||
return rtc::NETWORK_BIND_SUCCESS;
|
||||
}
|
||||
if (rv == ENONET) {
|
||||
return rtc::NETWORK_BIND_NETWORK_CHANGED;
|
||||
}
|
||||
return rtc::NETWORK_BIND_FAILURE;
|
||||
}
|
||||
|
||||
void AndroidNetworkMonitor::OnNetworkConnected(
|
||||
const NetworkInformation& network_info) {
|
||||
worker_thread()->Invoke<void>(
|
||||
RTC_FROM_HERE, rtc::Bind(&AndroidNetworkMonitor::OnNetworkConnected_w,
|
||||
this, network_info));
|
||||
// Fire SignalNetworksChanged to update the list of networks.
|
||||
OnNetworksChanged();
|
||||
}
|
||||
|
||||
void AndroidNetworkMonitor::OnNetworkConnected_w(
|
||||
const NetworkInformation& network_info) {
|
||||
LOG(LS_INFO) << "Network connected: " << network_info.ToString();
|
||||
adapter_type_by_name_[network_info.interface_name] =
|
||||
AdapterTypeFromNetworkType(network_info.type);
|
||||
network_info_by_handle_[network_info.handle] = network_info;
|
||||
for (const rtc::IPAddress& address : network_info.ip_addresses) {
|
||||
network_handle_by_address_[address] = network_info.handle;
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidNetworkMonitor::OnNetworkDisconnected(NetworkHandle handle) {
|
||||
LOG(LS_INFO) << "Network disconnected for handle " << handle;
|
||||
worker_thread()->Invoke<void>(
|
||||
RTC_FROM_HERE,
|
||||
rtc::Bind(&AndroidNetworkMonitor::OnNetworkDisconnected_w, this, handle));
|
||||
}
|
||||
|
||||
void AndroidNetworkMonitor::OnNetworkDisconnected_w(NetworkHandle handle) {
|
||||
auto iter = network_info_by_handle_.find(handle);
|
||||
if (iter != network_info_by_handle_.end()) {
|
||||
for (const rtc::IPAddress& address : iter->second.ip_addresses) {
|
||||
network_handle_by_address_.erase(address);
|
||||
}
|
||||
network_info_by_handle_.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidNetworkMonitor::SetNetworkInfos(
|
||||
const std::vector<NetworkInformation>& network_infos) {
|
||||
RTC_CHECK(thread_checker_.CalledOnValidThread());
|
||||
network_handle_by_address_.clear();
|
||||
network_info_by_handle_.clear();
|
||||
LOG(LS_INFO) << "Android network monitor found " << network_infos.size()
|
||||
<< " networks";
|
||||
for (NetworkInformation network : network_infos) {
|
||||
OnNetworkConnected_w(network);
|
||||
}
|
||||
}
|
||||
|
||||
rtc::AdapterType AndroidNetworkMonitor::GetAdapterType(
|
||||
const std::string& if_name) {
|
||||
auto iter = adapter_type_by_name_.find(if_name);
|
||||
rtc::AdapterType type = (iter == adapter_type_by_name_.end())
|
||||
? rtc::ADAPTER_TYPE_UNKNOWN
|
||||
: iter->second;
|
||||
if (type == rtc::ADAPTER_TYPE_UNKNOWN) {
|
||||
LOG(LS_WARNING) << "Get an unknown type for the interface " << if_name;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
rtc::NetworkMonitorInterface*
|
||||
AndroidNetworkMonitorFactory::CreateNetworkMonitor() {
|
||||
return new AndroidNetworkMonitor();
|
||||
}
|
||||
|
||||
JOW(void, NetworkMonitor_nativeNotifyConnectionTypeChanged)(
|
||||
JNIEnv* jni, jobject j_monitor, jlong j_native_monitor) {
|
||||
rtc::NetworkMonitorInterface* network_monitor =
|
||||
reinterpret_cast<rtc::NetworkMonitorInterface*>(j_native_monitor);
|
||||
network_monitor->OnNetworksChanged();
|
||||
}
|
||||
|
||||
JOW(void, NetworkMonitor_nativeNotifyOfActiveNetworkList)(
|
||||
JNIEnv* jni, jobject j_monitor, jlong j_native_monitor,
|
||||
jobjectArray j_network_infos) {
|
||||
AndroidNetworkMonitor* network_monitor =
|
||||
reinterpret_cast<AndroidNetworkMonitor*>(j_native_monitor);
|
||||
std::vector<NetworkInformation> network_infos;
|
||||
size_t num_networks = jni->GetArrayLength(j_network_infos);
|
||||
for (size_t i = 0; i < num_networks; ++i) {
|
||||
jobject j_network_info = jni->GetObjectArrayElement(j_network_infos, i);
|
||||
CHECK_EXCEPTION(jni) << "Error during GetObjectArrayElement";
|
||||
network_infos.push_back(GetNetworkInformationFromJava(jni, j_network_info));
|
||||
}
|
||||
network_monitor->SetNetworkInfos(network_infos);
|
||||
}
|
||||
|
||||
JOW(void, NetworkMonitor_nativeNotifyOfNetworkConnect)(
|
||||
JNIEnv* jni, jobject j_monitor, jlong j_native_monitor,
|
||||
jobject j_network_info) {
|
||||
AndroidNetworkMonitor* network_monitor =
|
||||
reinterpret_cast<AndroidNetworkMonitor*>(j_native_monitor);
|
||||
NetworkInformation network_info =
|
||||
GetNetworkInformationFromJava(jni, j_network_info);
|
||||
network_monitor->OnNetworkConnected(network_info);
|
||||
}
|
||||
|
||||
JOW(void, NetworkMonitor_nativeNotifyOfNetworkDisconnect)(
|
||||
JNIEnv* jni, jobject j_monitor, jlong j_native_monitor,
|
||||
jlong network_handle) {
|
||||
AndroidNetworkMonitor* network_monitor =
|
||||
reinterpret_cast<AndroidNetworkMonitor*>(j_native_monitor);
|
||||
network_monitor->OnNetworkDisconnected(
|
||||
static_cast<NetworkHandle>(network_handle));
|
||||
}
|
||||
|
||||
} // namespace webrtc_jni
|
||||
93
webrtc/sdk/android/src/jni/androidnetworkmonitor_jni.h
Normal file
93
webrtc/sdk/android/src/jni/androidnetworkmonitor_jni.h
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2015 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_ANDROIDNETWORKMONITOR_JNI_H_
|
||||
#define WEBRTC_SDK_ANDROID_SRC_JNI_ANDROIDNETWORKMONITOR_JNI_H_
|
||||
|
||||
#include "webrtc/base/networkmonitor.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "webrtc/sdk/android/src/jni/jni_helpers.h"
|
||||
#include "webrtc/base/basictypes.h"
|
||||
#include "webrtc/base/thread_checker.h"
|
||||
|
||||
namespace webrtc_jni {
|
||||
|
||||
typedef int64_t NetworkHandle;
|
||||
|
||||
// c++ equivalent of java NetworkMonitorAutoDetect.ConnectionType.
|
||||
enum NetworkType {
|
||||
NETWORK_UNKNOWN,
|
||||
NETWORK_ETHERNET,
|
||||
NETWORK_WIFI,
|
||||
NETWORK_4G,
|
||||
NETWORK_3G,
|
||||
NETWORK_2G,
|
||||
NETWORK_UNKNOWN_CELLULAR,
|
||||
NETWORK_BLUETOOTH,
|
||||
NETWORK_NONE
|
||||
};
|
||||
|
||||
// The information is collected from Android OS so that the native code can get
|
||||
// the network type and handle (Android network ID) for each interface.
|
||||
struct NetworkInformation {
|
||||
std::string interface_name;
|
||||
NetworkHandle handle;
|
||||
NetworkType type;
|
||||
std::vector<rtc::IPAddress> ip_addresses;
|
||||
|
||||
std::string ToString() const;
|
||||
};
|
||||
|
||||
class AndroidNetworkMonitor : public rtc::NetworkMonitorBase,
|
||||
public rtc::NetworkBinderInterface {
|
||||
public:
|
||||
AndroidNetworkMonitor();
|
||||
|
||||
static void SetAndroidContext(JNIEnv* jni, jobject context);
|
||||
|
||||
void Start() override;
|
||||
void Stop() override;
|
||||
|
||||
int BindSocketToNetwork(int socket_fd,
|
||||
const rtc::IPAddress& address) override;
|
||||
rtc::AdapterType GetAdapterType(const std::string& if_name) override;
|
||||
void OnNetworkConnected(const NetworkInformation& network_info);
|
||||
void OnNetworkDisconnected(NetworkHandle network_handle);
|
||||
void SetNetworkInfos(const std::vector<NetworkInformation>& network_infos);
|
||||
|
||||
private:
|
||||
static jobject application_context_;
|
||||
static int android_sdk_int_;
|
||||
JNIEnv* jni() { return AttachCurrentThreadIfNeeded(); }
|
||||
|
||||
void OnNetworkConnected_w(const NetworkInformation& network_info);
|
||||
void OnNetworkDisconnected_w(NetworkHandle network_handle);
|
||||
|
||||
ScopedGlobalRef<jclass> j_network_monitor_class_;
|
||||
ScopedGlobalRef<jobject> j_network_monitor_;
|
||||
rtc::ThreadChecker thread_checker_;
|
||||
bool started_ = false;
|
||||
std::map<std::string, rtc::AdapterType> adapter_type_by_name_;
|
||||
std::map<rtc::IPAddress, NetworkHandle> network_handle_by_address_;
|
||||
std::map<NetworkHandle, NetworkInformation> network_info_by_handle_;
|
||||
};
|
||||
|
||||
class AndroidNetworkMonitorFactory : public rtc::NetworkMonitorFactory {
|
||||
public:
|
||||
AndroidNetworkMonitorFactory() {}
|
||||
|
||||
rtc::NetworkMonitorInterface* CreateNetworkMonitor() override;
|
||||
};
|
||||
|
||||
} // namespace webrtc_jni
|
||||
|
||||
#endif // WEBRTC_SDK_ANDROID_SRC_JNI_ANDROIDNETWORKMONITOR_JNI_H_
|
||||
163
webrtc/sdk/android/src/jni/androidvideotracksource.cc
Normal file
163
webrtc/sdk/android/src/jni/androidvideotracksource.cc
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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/sdk/android/src/jni/androidvideotracksource.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
AndroidVideoTrackSource::AndroidVideoTrackSource(rtc::Thread* signaling_thread,
|
||||
JNIEnv* jni,
|
||||
jobject j_egl_context,
|
||||
bool is_screencast)
|
||||
: signaling_thread_(signaling_thread),
|
||||
surface_texture_helper_(webrtc_jni::SurfaceTextureHelper::create(
|
||||
jni,
|
||||
"Camera SurfaceTextureHelper",
|
||||
j_egl_context)),
|
||||
is_screencast_(is_screencast) {
|
||||
LOG(LS_INFO) << "AndroidVideoTrackSource ctor";
|
||||
camera_thread_checker_.DetachFromThread();
|
||||
}
|
||||
|
||||
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::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);
|
||||
|
||||
int64_t camera_time_us = timestamp_ns / rtc::kNumNanosecsPerMicrosec;
|
||||
int64_t translated_camera_time_us =
|
||||
timestamp_aligner_.TranslateTimestamp(camera_time_us, rtc::TimeMicros());
|
||||
|
||||
int adapted_width;
|
||||
int adapted_height;
|
||||
int crop_width;
|
||||
int crop_height;
|
||||
int crop_x;
|
||||
int crop_y;
|
||||
|
||||
if (!AdaptFrame(width, height, camera_time_us, &adapted_width,
|
||||
&adapted_height, &crop_width, &crop_height, &crop_x,
|
||||
&crop_y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t* y_plane = static_cast<const uint8_t*>(frame_data);
|
||||
const uint8_t* uv_plane = y_plane + width * height;
|
||||
const 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;
|
||||
// Crop just by modifying pointers.
|
||||
y_plane += width * crop_y + crop_x;
|
||||
uv_plane += uv_width * crop_y + crop_x;
|
||||
|
||||
rtc::scoped_refptr<webrtc::I420Buffer> buffer =
|
||||
buffer_pool_.CreateBuffer(adapted_width, adapted_height);
|
||||
|
||||
nv12toi420_scaler_.NV12ToI420Scale(
|
||||
y_plane, width, uv_plane, uv_width * 2, crop_width, crop_height,
|
||||
buffer->MutableDataY(), buffer->StrideY(),
|
||||
// Swap U and V, since we have NV21, not NV12.
|
||||
buffer->MutableDataV(), buffer->StrideV(), buffer->MutableDataU(),
|
||||
buffer->StrideU(), buffer->width(), buffer->height());
|
||||
|
||||
OnFrame(VideoFrame(buffer, static_cast<webrtc::VideoRotation>(rotation),
|
||||
translated_camera_time_us));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
int64_t camera_time_us = timestamp_ns / rtc::kNumNanosecsPerMicrosec;
|
||||
int64_t translated_camera_time_us =
|
||||
timestamp_aligner_.TranslateTimestamp(camera_time_us, rtc::TimeMicros());
|
||||
|
||||
int adapted_width;
|
||||
int adapted_height;
|
||||
int crop_width;
|
||||
int crop_height;
|
||||
int crop_x;
|
||||
int crop_y;
|
||||
|
||||
if (!AdaptFrame(width, height, camera_time_us, &adapted_width,
|
||||
&adapted_height, &crop_width, &crop_height, &crop_x,
|
||||
&crop_y)) {
|
||||
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));
|
||||
|
||||
// Make a local copy, since value of apply_rotation() may change
|
||||
// under our feet.
|
||||
bool do_rotate = apply_rotation();
|
||||
|
||||
if (do_rotate) {
|
||||
if (rotation == webrtc::kVideoRotation_90 ||
|
||||
rotation == webrtc::kVideoRotation_270) {
|
||||
std::swap(adapted_width, adapted_height);
|
||||
}
|
||||
matrix.Rotate(static_cast<webrtc::VideoRotation>(rotation));
|
||||
}
|
||||
|
||||
OnFrame(VideoFrame(
|
||||
surface_texture_helper_->CreateTextureFrame(
|
||||
adapted_width, adapted_height,
|
||||
webrtc_jni::NativeHandleImpl(handle.oes_texture_id, matrix)),
|
||||
do_rotate ? webrtc::kVideoRotation_0
|
||||
: static_cast<webrtc::VideoRotation>(rotation),
|
||||
translated_camera_time_us));
|
||||
}
|
||||
|
||||
void AndroidVideoTrackSource::OnOutputFormatRequest(int width,
|
||||
int height,
|
||||
int fps) {
|
||||
cricket::VideoFormat format(width, height,
|
||||
cricket::VideoFormat::FpsToInterval(fps), 0);
|
||||
video_adapter()->OnOutputFormatRequest(format);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
84
webrtc/sdk/android/src/jni/androidvideotracksource.h
Normal file
84
webrtc/sdk/android/src/jni/androidvideotracksource.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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_ANDROID_JNI_ANDROIDVIDEOTRACKSOURCE_H_
|
||||
#define WEBRTC_API_ANDROID_JNI_ANDROIDVIDEOTRACKSOURCE_H_
|
||||
|
||||
#include "webrtc/sdk/android/src/jni/native_handle_impl.h"
|
||||
#include "webrtc/sdk/android/src/jni/surfacetexturehelper_jni.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/common_video/libyuv/include/webrtc_libyuv.h"
|
||||
#include "webrtc/media/base/adaptedvideotracksource.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class AndroidVideoTrackSource : public rtc::AdaptedVideoTrackSource {
|
||||
public:
|
||||
AndroidVideoTrackSource(rtc::Thread* signaling_thread,
|
||||
JNIEnv* jni,
|
||||
jobject j_egl_context,
|
||||
bool is_screencast = false);
|
||||
|
||||
bool is_screencast() const override { return is_screencast_; }
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Called by the native capture observer
|
||||
void SetState(SourceState state);
|
||||
|
||||
SourceState state() const override { return state_; }
|
||||
|
||||
bool remote() const override { return false; }
|
||||
|
||||
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 camera_thread_checker_;
|
||||
SourceState state_;
|
||||
rtc::VideoBroadcaster broadcaster_;
|
||||
rtc::TimestampAligner timestamp_aligner_;
|
||||
webrtc::NV12ToI420Scaler nv12toi420_scaler_;
|
||||
webrtc::I420BufferPool buffer_pool_;
|
||||
rtc::scoped_refptr<webrtc_jni::SurfaceTextureHelper> surface_texture_helper_;
|
||||
const bool is_screencast_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_API_ANDROID_JNI_ANDROIDVIDEOTRACKSOURCE_H_
|
||||
90
webrtc/sdk/android/src/jni/androidvideotracksource_jni.cc
Normal file
90
webrtc/sdk/android/src/jni/androidvideotracksource_jni.cc
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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/sdk/android/src/jni/androidvideotracksource.h"
|
||||
#include "webrtc/sdk/android/src/jni/classreferenceholder.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(j_success
|
||||
? webrtc::AndroidVideoTrackSource::SourceState::kLive
|
||||
: webrtc::AndroidVideoTrackSource::SourceState::kEnded);
|
||||
}
|
||||
|
||||
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(void, VideoSource_nativeAdaptOutputFormat)
|
||||
(JNIEnv* jni, jclass, jlong j_source, jint j_width, jint j_height, jint j_fps) {
|
||||
LOG(LS_INFO) << "VideoSource_nativeAdaptOutputFormat";
|
||||
webrtc::AndroidVideoTrackSource* source =
|
||||
AndroidVideoTrackSourceFromJavaProxy(j_source);
|
||||
source->OnOutputFormatRequest(j_width, j_height, j_fps);
|
||||
}
|
||||
|
||||
} // namespace webrtc_jni
|
||||
134
webrtc/sdk/android/src/jni/classreferenceholder.cc
Normal file
134
webrtc/sdk/android/src/jni/classreferenceholder.cc
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2015 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/classreferenceholder.h"
|
||||
|
||||
#include "webrtc/sdk/android/src/jni/jni_helpers.h"
|
||||
|
||||
namespace webrtc_jni {
|
||||
|
||||
// ClassReferenceHolder holds global reference to Java classes in app/webrtc.
|
||||
class ClassReferenceHolder {
|
||||
public:
|
||||
explicit ClassReferenceHolder(JNIEnv* jni);
|
||||
~ClassReferenceHolder();
|
||||
|
||||
void FreeReferences(JNIEnv* jni);
|
||||
jclass GetClass(const std::string& name);
|
||||
|
||||
private:
|
||||
void LoadClass(JNIEnv* jni, const std::string& name);
|
||||
|
||||
std::map<std::string, jclass> classes_;
|
||||
};
|
||||
|
||||
// Allocated in LoadGlobalClassReferenceHolder(),
|
||||
// freed in FreeGlobalClassReferenceHolder().
|
||||
static ClassReferenceHolder* g_class_reference_holder = nullptr;
|
||||
|
||||
void LoadGlobalClassReferenceHolder() {
|
||||
RTC_CHECK(g_class_reference_holder == nullptr);
|
||||
g_class_reference_holder = new ClassReferenceHolder(GetEnv());
|
||||
}
|
||||
|
||||
void FreeGlobalClassReferenceHolder() {
|
||||
g_class_reference_holder->FreeReferences(AttachCurrentThreadIfNeeded());
|
||||
delete g_class_reference_holder;
|
||||
g_class_reference_holder = nullptr;
|
||||
}
|
||||
|
||||
ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) {
|
||||
LoadClass(jni, "android/graphics/SurfaceTexture");
|
||||
LoadClass(jni, "java/nio/ByteBuffer");
|
||||
LoadClass(jni, "java/util/ArrayList");
|
||||
LoadClass(jni, "org/webrtc/AudioTrack");
|
||||
LoadClass(jni, "org/webrtc/Camera1Enumerator");
|
||||
LoadClass(jni, "org/webrtc/Camera2Enumerator");
|
||||
LoadClass(jni, "org/webrtc/CameraEnumerationAndroid");
|
||||
LoadClass(jni, "org/webrtc/DataChannel");
|
||||
LoadClass(jni, "org/webrtc/DataChannel$Buffer");
|
||||
LoadClass(jni, "org/webrtc/DataChannel$Init");
|
||||
LoadClass(jni, "org/webrtc/DataChannel$State");
|
||||
LoadClass(jni, "org/webrtc/EglBase");
|
||||
LoadClass(jni, "org/webrtc/EglBase$Context");
|
||||
LoadClass(jni, "org/webrtc/EglBase14$Context");
|
||||
LoadClass(jni, "org/webrtc/IceCandidate");
|
||||
LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder");
|
||||
LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder$OutputBufferInfo");
|
||||
LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder$VideoCodecType");
|
||||
LoadClass(jni, "org/webrtc/MediaCodecVideoDecoder");
|
||||
LoadClass(jni, "org/webrtc/MediaCodecVideoDecoder$DecodedTextureBuffer");
|
||||
LoadClass(jni, "org/webrtc/MediaCodecVideoDecoder$DecodedOutputBuffer");
|
||||
LoadClass(jni, "org/webrtc/MediaCodecVideoDecoder$VideoCodecType");
|
||||
LoadClass(jni, "org/webrtc/MediaSource$State");
|
||||
LoadClass(jni, "org/webrtc/MediaStream");
|
||||
LoadClass(jni, "org/webrtc/MediaStreamTrack$State");
|
||||
LoadClass(jni, "org/webrtc/NetworkMonitor");
|
||||
LoadClass(jni, "org/webrtc/NetworkMonitorAutoDetect$ConnectionType");
|
||||
LoadClass(jni, "org/webrtc/NetworkMonitorAutoDetect$IPAddress");
|
||||
LoadClass(jni, "org/webrtc/NetworkMonitorAutoDetect$NetworkInformation");
|
||||
LoadClass(jni, "org/webrtc/PeerConnectionFactory");
|
||||
LoadClass(jni, "org/webrtc/PeerConnection$BundlePolicy");
|
||||
LoadClass(jni, "org/webrtc/PeerConnection$ContinualGatheringPolicy");
|
||||
LoadClass(jni, "org/webrtc/PeerConnection$RtcpMuxPolicy");
|
||||
LoadClass(jni, "org/webrtc/PeerConnection$IceConnectionState");
|
||||
LoadClass(jni, "org/webrtc/PeerConnection$IceGatheringState");
|
||||
LoadClass(jni, "org/webrtc/PeerConnection$IceTransportsType");
|
||||
LoadClass(jni, "org/webrtc/PeerConnection$TcpCandidatePolicy");
|
||||
LoadClass(jni, "org/webrtc/PeerConnection$CandidateNetworkPolicy");
|
||||
LoadClass(jni, "org/webrtc/PeerConnection$KeyType");
|
||||
LoadClass(jni, "org/webrtc/PeerConnection$SignalingState");
|
||||
LoadClass(jni, "org/webrtc/RtpReceiver");
|
||||
LoadClass(jni, "org/webrtc/RtpSender");
|
||||
LoadClass(jni, "org/webrtc/SessionDescription");
|
||||
LoadClass(jni, "org/webrtc/SessionDescription$Type");
|
||||
LoadClass(jni, "org/webrtc/StatsReport");
|
||||
LoadClass(jni, "org/webrtc/StatsReport$Value");
|
||||
LoadClass(jni, "org/webrtc/SurfaceTextureHelper");
|
||||
LoadClass(jni, "org/webrtc/VideoCapturer");
|
||||
LoadClass(jni, "org/webrtc/VideoRenderer$I420Frame");
|
||||
LoadClass(jni, "org/webrtc/VideoTrack");
|
||||
}
|
||||
|
||||
ClassReferenceHolder::~ClassReferenceHolder() {
|
||||
RTC_CHECK(classes_.empty()) << "Must call FreeReferences() before dtor!";
|
||||
}
|
||||
|
||||
void ClassReferenceHolder::FreeReferences(JNIEnv* jni) {
|
||||
for (std::map<std::string, jclass>::const_iterator it = classes_.begin();
|
||||
it != classes_.end(); ++it) {
|
||||
jni->DeleteGlobalRef(it->second);
|
||||
}
|
||||
classes_.clear();
|
||||
}
|
||||
|
||||
jclass ClassReferenceHolder::GetClass(const std::string& name) {
|
||||
std::map<std::string, jclass>::iterator it = classes_.find(name);
|
||||
RTC_CHECK(it != classes_.end()) << "Unexpected GetClass() call for: " << name;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void ClassReferenceHolder::LoadClass(JNIEnv* jni, const std::string& name) {
|
||||
jclass localRef = jni->FindClass(name.c_str());
|
||||
CHECK_EXCEPTION(jni) << "error during FindClass: " << name;
|
||||
RTC_CHECK(localRef) << name;
|
||||
jclass globalRef = reinterpret_cast<jclass>(jni->NewGlobalRef(localRef));
|
||||
CHECK_EXCEPTION(jni) << "error during NewGlobalRef: " << name;
|
||||
RTC_CHECK(globalRef) << name;
|
||||
bool inserted = classes_.insert(std::make_pair(name, globalRef)).second;
|
||||
RTC_CHECK(inserted) << "Duplicate class name: " << name;
|
||||
}
|
||||
|
||||
// Returns a global reference guaranteed to be valid for the lifetime of the
|
||||
// process.
|
||||
jclass FindClass(JNIEnv* jni, const char* name) {
|
||||
return g_class_reference_holder->GetClass(name);
|
||||
}
|
||||
|
||||
} // namespace webrtc_jni
|
||||
41
webrtc/sdk/android/src/jni/classreferenceholder.h
Normal file
41
webrtc/sdk/android/src/jni/classreferenceholder.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
*/
|
||||
|
||||
// Android's FindClass() is trickier than usual because the app-specific
|
||||
// ClassLoader is not consulted when there is no app-specific frame on the
|
||||
// stack. Consequently, we only look up all classes once in app/webrtc.
|
||||
// http://developer.android.com/training/articles/perf-jni.html#faq_FindClass
|
||||
|
||||
#ifndef WEBRTC_SDK_ANDROID_SRC_JNI_CLASSREFERENCEHOLDER_H_
|
||||
#define WEBRTC_SDK_ANDROID_SRC_JNI_CLASSREFERENCEHOLDER_H_
|
||||
|
||||
#include <jni.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace webrtc_jni {
|
||||
|
||||
// LoadGlobalClassReferenceHolder must be called in JNI_OnLoad.
|
||||
void LoadGlobalClassReferenceHolder();
|
||||
// FreeGlobalClassReferenceHolder must be called in JNI_UnLoad.
|
||||
void FreeGlobalClassReferenceHolder();
|
||||
|
||||
// Returns a global reference guaranteed to be valid for the lifetime of the
|
||||
// process.
|
||||
jclass FindClass(JNIEnv* jni, const char* name);
|
||||
|
||||
// Convenience macro defining JNI-accessible methods in the org.webrtc package.
|
||||
// Eliminates unnecessary boilerplate and line-wraps, reducing visual clutter.
|
||||
#define JOW(rettype, name) extern "C" rettype JNIEXPORT JNICALL \
|
||||
Java_org_webrtc_##name
|
||||
|
||||
} // namespace webrtc_jni
|
||||
|
||||
#endif // WEBRTC_SDK_ANDROID_SRC_JNI_CLASSREFERENCEHOLDER_H_
|
||||
350
webrtc/sdk/android/src/jni/jni_helpers.cc
Normal file
350
webrtc/sdk/android/src/jni/jni_helpers.cc
Normal file
@ -0,0 +1,350 @@
|
||||
/*
|
||||
* Copyright 2015 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/jni_helpers.h"
|
||||
|
||||
#include "webrtc/sdk/android/src/jni/classreferenceholder.h"
|
||||
|
||||
#include <asm/unistd.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace webrtc_jni {
|
||||
|
||||
static JavaVM* g_jvm = nullptr;
|
||||
|
||||
static pthread_once_t g_jni_ptr_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
// Key for per-thread JNIEnv* data. Non-NULL in threads attached to |g_jvm| by
|
||||
// AttachCurrentThreadIfNeeded(), NULL in unattached threads and threads that
|
||||
// were attached by the JVM because of a Java->native call.
|
||||
static pthread_key_t g_jni_ptr;
|
||||
|
||||
JavaVM *GetJVM() {
|
||||
RTC_CHECK(g_jvm) << "JNI_OnLoad failed to run?";
|
||||
return g_jvm;
|
||||
}
|
||||
|
||||
// Return a |JNIEnv*| usable on this thread or NULL if this thread is detached.
|
||||
JNIEnv* GetEnv() {
|
||||
void* env = nullptr;
|
||||
jint status = g_jvm->GetEnv(&env, JNI_VERSION_1_6);
|
||||
RTC_CHECK(((env != nullptr) && (status == JNI_OK)) ||
|
||||
((env == nullptr) && (status == JNI_EDETACHED)))
|
||||
<< "Unexpected GetEnv return: " << status << ":" << env;
|
||||
return reinterpret_cast<JNIEnv*>(env);
|
||||
}
|
||||
|
||||
static void ThreadDestructor(void* prev_jni_ptr) {
|
||||
// This function only runs on threads where |g_jni_ptr| is non-NULL, meaning
|
||||
// we were responsible for originally attaching the thread, so are responsible
|
||||
// for detaching it now. However, because some JVM implementations (notably
|
||||
// Oracle's http://goo.gl/eHApYT) also use the pthread_key_create mechanism,
|
||||
// the JVMs accounting info for this thread may already be wiped out by the
|
||||
// time this is called. Thus it may appear we are already detached even though
|
||||
// it was our responsibility to detach! Oh well.
|
||||
if (!GetEnv())
|
||||
return;
|
||||
|
||||
RTC_CHECK(GetEnv() == prev_jni_ptr)
|
||||
<< "Detaching from another thread: " << prev_jni_ptr << ":" << GetEnv();
|
||||
jint status = g_jvm->DetachCurrentThread();
|
||||
RTC_CHECK(status == JNI_OK) << "Failed to detach thread: " << status;
|
||||
RTC_CHECK(!GetEnv()) << "Detaching was a successful no-op???";
|
||||
}
|
||||
|
||||
static void CreateJNIPtrKey() {
|
||||
RTC_CHECK(!pthread_key_create(&g_jni_ptr, &ThreadDestructor))
|
||||
<< "pthread_key_create";
|
||||
}
|
||||
|
||||
jint InitGlobalJniVariables(JavaVM *jvm) {
|
||||
RTC_CHECK(!g_jvm) << "InitGlobalJniVariables!";
|
||||
g_jvm = jvm;
|
||||
RTC_CHECK(g_jvm) << "InitGlobalJniVariables handed NULL?";
|
||||
|
||||
RTC_CHECK(!pthread_once(&g_jni_ptr_once, &CreateJNIPtrKey)) << "pthread_once";
|
||||
|
||||
JNIEnv* jni = nullptr;
|
||||
if (jvm->GetEnv(reinterpret_cast<void**>(&jni), JNI_VERSION_1_6) != JNI_OK)
|
||||
return -1;
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
// Return thread ID as a string.
|
||||
static std::string GetThreadId() {
|
||||
char buf[21]; // Big enough to hold a kuint64max plus terminating NULL.
|
||||
RTC_CHECK_LT(snprintf(buf, sizeof(buf), "%ld",
|
||||
static_cast<long>(syscall(__NR_gettid))),
|
||||
sizeof(buf))
|
||||
<< "Thread id is bigger than uint64??";
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
// Return the current thread's name.
|
||||
static std::string GetThreadName() {
|
||||
char name[17] = {0};
|
||||
if (prctl(PR_GET_NAME, name) != 0)
|
||||
return std::string("<noname>");
|
||||
return std::string(name);
|
||||
}
|
||||
|
||||
// Return a |JNIEnv*| usable on this thread. Attaches to |g_jvm| if necessary.
|
||||
JNIEnv* AttachCurrentThreadIfNeeded() {
|
||||
JNIEnv* jni = GetEnv();
|
||||
if (jni)
|
||||
return jni;
|
||||
RTC_CHECK(!pthread_getspecific(g_jni_ptr))
|
||||
<< "TLS has a JNIEnv* but not attached?";
|
||||
|
||||
std::string name(GetThreadName() + " - " + GetThreadId());
|
||||
JavaVMAttachArgs args;
|
||||
args.version = JNI_VERSION_1_6;
|
||||
args.name = &name[0];
|
||||
args.group = nullptr;
|
||||
// Deal with difference in signatures between Oracle's jni.h and Android's.
|
||||
#ifdef _JAVASOFT_JNI_H_ // Oracle's jni.h violates the JNI spec!
|
||||
void* env = nullptr;
|
||||
#else
|
||||
JNIEnv* env = nullptr;
|
||||
#endif
|
||||
RTC_CHECK(!g_jvm->AttachCurrentThread(&env, &args))
|
||||
<< "Failed to attach thread";
|
||||
RTC_CHECK(env) << "AttachCurrentThread handed back NULL!";
|
||||
jni = reinterpret_cast<JNIEnv*>(env);
|
||||
RTC_CHECK(!pthread_setspecific(g_jni_ptr, jni)) << "pthread_setspecific";
|
||||
return jni;
|
||||
}
|
||||
|
||||
// Return a |jlong| that will correctly convert back to |ptr|. This is needed
|
||||
// because the alternative (of silently passing a 32-bit pointer to a vararg
|
||||
// function expecting a 64-bit param) picks up garbage in the high 32 bits.
|
||||
jlong jlongFromPointer(void* ptr) {
|
||||
static_assert(sizeof(intptr_t) <= sizeof(jlong),
|
||||
"Time to rethink the use of jlongs");
|
||||
// Going through intptr_t to be obvious about the definedness of the
|
||||
// conversion from pointer to integral type. intptr_t to jlong is a standard
|
||||
// widening by the static_assert above.
|
||||
jlong ret = reinterpret_cast<intptr_t>(ptr);
|
||||
RTC_DCHECK(reinterpret_cast<void*>(ret) == ptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// JNIEnv-helper methods that RTC_CHECK success: no Java exception thrown and
|
||||
// found object/class/method/field is non-null.
|
||||
jmethodID GetMethodID(
|
||||
JNIEnv* jni, jclass c, const std::string& name, const char* signature) {
|
||||
jmethodID m = jni->GetMethodID(c, name.c_str(), signature);
|
||||
CHECK_EXCEPTION(jni) << "error during GetMethodID: " << name << ", "
|
||||
<< signature;
|
||||
RTC_CHECK(m) << name << ", " << signature;
|
||||
return m;
|
||||
}
|
||||
|
||||
jmethodID GetStaticMethodID(
|
||||
JNIEnv* jni, jclass c, const char* name, const char* signature) {
|
||||
jmethodID m = jni->GetStaticMethodID(c, name, signature);
|
||||
CHECK_EXCEPTION(jni) << "error during GetStaticMethodID: " << name << ", "
|
||||
<< signature;
|
||||
RTC_CHECK(m) << name << ", " << signature;
|
||||
return m;
|
||||
}
|
||||
|
||||
jfieldID GetFieldID(
|
||||
JNIEnv* jni, jclass c, const char* name, const char* signature) {
|
||||
jfieldID f = jni->GetFieldID(c, name, signature);
|
||||
CHECK_EXCEPTION(jni) << "error during GetFieldID";
|
||||
RTC_CHECK(f) << name << ", " << signature;
|
||||
return f;
|
||||
}
|
||||
|
||||
jclass GetObjectClass(JNIEnv* jni, jobject object) {
|
||||
jclass c = jni->GetObjectClass(object);
|
||||
CHECK_EXCEPTION(jni) << "error during GetObjectClass";
|
||||
RTC_CHECK(c) << "GetObjectClass returned NULL";
|
||||
return c;
|
||||
}
|
||||
|
||||
jobject GetObjectField(JNIEnv* jni, jobject object, jfieldID id) {
|
||||
jobject o = jni->GetObjectField(object, id);
|
||||
CHECK_EXCEPTION(jni) << "error during GetObjectField";
|
||||
RTC_CHECK(!IsNull(jni, o)) << "GetObjectField returned NULL";
|
||||
return o;
|
||||
}
|
||||
|
||||
jobject GetNullableObjectField(JNIEnv* jni, jobject object, jfieldID id) {
|
||||
jobject o = jni->GetObjectField(object, id);
|
||||
CHECK_EXCEPTION(jni) << "error during GetObjectField";
|
||||
return o;
|
||||
}
|
||||
|
||||
jstring GetStringField(JNIEnv* jni, jobject object, jfieldID id) {
|
||||
return static_cast<jstring>(GetObjectField(jni, object, id));
|
||||
}
|
||||
|
||||
jlong GetLongField(JNIEnv* jni, jobject object, jfieldID id) {
|
||||
jlong l = jni->GetLongField(object, id);
|
||||
CHECK_EXCEPTION(jni) << "error during GetLongField";
|
||||
return l;
|
||||
}
|
||||
|
||||
jint GetIntField(JNIEnv* jni, jobject object, jfieldID id) {
|
||||
jint i = jni->GetIntField(object, id);
|
||||
CHECK_EXCEPTION(jni) << "error during GetIntField";
|
||||
return i;
|
||||
}
|
||||
|
||||
bool GetBooleanField(JNIEnv* jni, jobject object, jfieldID id) {
|
||||
jboolean b = jni->GetBooleanField(object, id);
|
||||
CHECK_EXCEPTION(jni) << "error during GetBooleanField";
|
||||
return b;
|
||||
}
|
||||
|
||||
bool IsNull(JNIEnv* jni, jobject obj) {
|
||||
return jni->IsSameObject(obj, nullptr);
|
||||
}
|
||||
|
||||
// Given a UTF-8 encoded |native| string return a new (UTF-16) jstring.
|
||||
jstring JavaStringFromStdString(JNIEnv* jni, const std::string& native) {
|
||||
jstring jstr = jni->NewStringUTF(native.c_str());
|
||||
CHECK_EXCEPTION(jni) << "error during NewStringUTF";
|
||||
return jstr;
|
||||
}
|
||||
|
||||
// Given a (UTF-16) jstring return a new UTF-8 native string.
|
||||
std::string JavaToStdString(JNIEnv* jni, const jstring& j_string) {
|
||||
const char* chars = jni->GetStringUTFChars(j_string, nullptr);
|
||||
CHECK_EXCEPTION(jni) << "Error during GetStringUTFChars";
|
||||
std::string str(chars, jni->GetStringUTFLength(j_string));
|
||||
CHECK_EXCEPTION(jni) << "Error during GetStringUTFLength";
|
||||
jni->ReleaseStringUTFChars(j_string, chars);
|
||||
CHECK_EXCEPTION(jni) << "Error during ReleaseStringUTFChars";
|
||||
return str;
|
||||
}
|
||||
|
||||
// Return the (singleton) Java Enum object corresponding to |index|;
|
||||
jobject JavaEnumFromIndex(JNIEnv* jni, jclass state_class,
|
||||
const std::string& state_class_name, int index) {
|
||||
jmethodID state_values_id = GetStaticMethodID(
|
||||
jni, state_class, "values", ("()[L" + state_class_name + ";").c_str());
|
||||
jobjectArray state_values = static_cast<jobjectArray>(
|
||||
jni->CallStaticObjectMethod(state_class, state_values_id));
|
||||
CHECK_EXCEPTION(jni) << "error during CallStaticObjectMethod";
|
||||
jobject ret = jni->GetObjectArrayElement(state_values, index);
|
||||
CHECK_EXCEPTION(jni) << "error during GetObjectArrayElement";
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string GetJavaEnumName(JNIEnv* jni,
|
||||
const std::string& className,
|
||||
jobject j_enum) {
|
||||
jclass enumClass = FindClass(jni, className.c_str());
|
||||
jmethodID nameMethod =
|
||||
GetMethodID(jni, enumClass, "name", "()Ljava/lang/String;");
|
||||
jstring name =
|
||||
reinterpret_cast<jstring>(jni->CallObjectMethod(j_enum, nameMethod));
|
||||
CHECK_EXCEPTION(jni) << "error during CallObjectMethod for " << className
|
||||
<< ".name";
|
||||
return JavaToStdString(jni, name);
|
||||
}
|
||||
|
||||
jobject NewGlobalRef(JNIEnv* jni, jobject o) {
|
||||
jobject ret = jni->NewGlobalRef(o);
|
||||
CHECK_EXCEPTION(jni) << "error during NewGlobalRef";
|
||||
RTC_CHECK(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DeleteGlobalRef(JNIEnv* jni, jobject o) {
|
||||
jni->DeleteGlobalRef(o);
|
||||
CHECK_EXCEPTION(jni) << "error during DeleteGlobalRef";
|
||||
}
|
||||
|
||||
// Scope Java local references to the lifetime of this object. Use in all C++
|
||||
// callbacks (i.e. entry points that don't originate in a Java callstack
|
||||
// through a "native" method call).
|
||||
ScopedLocalRefFrame::ScopedLocalRefFrame(JNIEnv* jni) : jni_(jni) {
|
||||
RTC_CHECK(!jni_->PushLocalFrame(0)) << "Failed to PushLocalFrame";
|
||||
}
|
||||
ScopedLocalRefFrame::~ScopedLocalRefFrame() {
|
||||
jni_->PopLocalFrame(nullptr);
|
||||
}
|
||||
|
||||
// Creates an iterator representing the end of any collection.
|
||||
Iterable::Iterator::Iterator() : iterator_(nullptr) {}
|
||||
|
||||
// Creates an iterator pointing to the beginning of the specified collection.
|
||||
Iterable::Iterator::Iterator(JNIEnv* jni, jobject iterable) : jni_(jni) {
|
||||
jclass j_class = GetObjectClass(jni, iterable);
|
||||
jmethodID iterator_id =
|
||||
GetMethodID(jni, j_class, "iterator", "()Ljava/util/Iterator;");
|
||||
iterator_ = jni->CallObjectMethod(iterable, iterator_id);
|
||||
CHECK_EXCEPTION(jni) << "error during CallObjectMethod";
|
||||
RTC_CHECK(iterator_ != nullptr);
|
||||
|
||||
jclass iterator_class = GetObjectClass(jni, iterator_);
|
||||
has_next_id_ = GetMethodID(jni, iterator_class, "hasNext", "()Z");
|
||||
next_id_ = GetMethodID(jni, iterator_class, "next", "()Ljava/lang/Object;");
|
||||
|
||||
// Start at the first element in the collection.
|
||||
++(*this);
|
||||
}
|
||||
|
||||
// Move constructor - necessary to be able to return iterator types from
|
||||
// functions.
|
||||
Iterable::Iterator::Iterator(Iterator&& other)
|
||||
: jni_(std::move(other.jni_)),
|
||||
iterator_(std::move(other.iterator_)),
|
||||
value_(std::move(other.value_)),
|
||||
has_next_id_(std::move(other.has_next_id_)),
|
||||
next_id_(std::move(other.next_id_)),
|
||||
thread_checker_(std::move(other.thread_checker_)){};
|
||||
|
||||
// Advances the iterator one step.
|
||||
Iterable::Iterator& Iterable::Iterator::operator++() {
|
||||
RTC_CHECK(thread_checker_.CalledOnValidThread());
|
||||
if (AtEnd()) {
|
||||
// Can't move past the end.
|
||||
return *this;
|
||||
}
|
||||
bool has_next = jni_->CallBooleanMethod(iterator_, has_next_id_);
|
||||
CHECK_EXCEPTION(jni_) << "error during CallBooleanMethod";
|
||||
if (!has_next) {
|
||||
iterator_ = nullptr;
|
||||
value_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
value_ = jni_->CallObjectMethod(iterator_, next_id_);
|
||||
CHECK_EXCEPTION(jni_) << "error during CallObjectMethod";
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Provides a way to compare the iterator with itself and with the end iterator.
|
||||
// Note: all other comparison results are undefined, just like for C++ input
|
||||
// iterators.
|
||||
bool Iterable::Iterator::operator==(const Iterable::Iterator& other) {
|
||||
// Two different active iterators should never be compared.
|
||||
RTC_DCHECK(this == &other || AtEnd() || other.AtEnd());
|
||||
return AtEnd() == other.AtEnd();
|
||||
}
|
||||
|
||||
jobject Iterable::Iterator::operator*() {
|
||||
RTC_CHECK(!AtEnd());
|
||||
return value_;
|
||||
}
|
||||
|
||||
bool Iterable::Iterator::AtEnd() const {
|
||||
RTC_CHECK(thread_checker_.CalledOnValidThread());
|
||||
return jni_ == nullptr || IsNull(jni_, iterator_);
|
||||
}
|
||||
|
||||
} // namespace webrtc_jni
|
||||
191
webrtc/sdk/android/src/jni/jni_helpers.h
Normal file
191
webrtc/sdk/android/src/jni/jni_helpers.h
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
*/
|
||||
|
||||
// This file contain convenience functions and classes for JNI.
|
||||
// Before using any of the methods, InitGlobalJniVariables must be called.
|
||||
|
||||
#ifndef WEBRTC_SDK_ANDROID_SRC_JNI_JNI_HELPERS_H_
|
||||
#define WEBRTC_SDK_ANDROID_SRC_JNI_JNI_HELPERS_H_
|
||||
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/thread_checker.h"
|
||||
|
||||
// Abort the process if |jni| has a Java exception pending.
|
||||
// This macros uses the comma operator to execute ExceptionDescribe
|
||||
// and ExceptionClear ignoring their return values and sending ""
|
||||
// to the error stream.
|
||||
#define CHECK_EXCEPTION(jni) \
|
||||
RTC_CHECK(!jni->ExceptionCheck()) \
|
||||
<< (jni->ExceptionDescribe(), jni->ExceptionClear(), "")
|
||||
|
||||
// Helper that calls ptr->Release() and aborts the process with a useful
|
||||
// message if that didn't actually delete *ptr because of extra refcounts.
|
||||
#define CHECK_RELEASE(ptr) \
|
||||
RTC_CHECK_EQ(0, (ptr)->Release()) << "Unexpected refcount."
|
||||
|
||||
namespace webrtc_jni {
|
||||
|
||||
jint InitGlobalJniVariables(JavaVM *jvm);
|
||||
|
||||
// Return a |JNIEnv*| usable on this thread or NULL if this thread is detached.
|
||||
JNIEnv* GetEnv();
|
||||
|
||||
JavaVM *GetJVM();
|
||||
|
||||
// Return a |JNIEnv*| usable on this thread. Attaches to |g_jvm| if necessary.
|
||||
JNIEnv* AttachCurrentThreadIfNeeded();
|
||||
|
||||
// Return a |jlong| that will correctly convert back to |ptr|. This is needed
|
||||
// because the alternative (of silently passing a 32-bit pointer to a vararg
|
||||
// function expecting a 64-bit param) picks up garbage in the high 32 bits.
|
||||
jlong jlongFromPointer(void* ptr);
|
||||
|
||||
// JNIEnv-helper methods that RTC_CHECK success: no Java exception thrown and
|
||||
// found object/class/method/field is non-null.
|
||||
jmethodID GetMethodID(
|
||||
JNIEnv* jni, jclass c, const std::string& name, const char* signature);
|
||||
|
||||
jmethodID GetStaticMethodID(
|
||||
JNIEnv* jni, jclass c, const char* name, const char* signature);
|
||||
|
||||
jfieldID GetFieldID(JNIEnv* jni, jclass c, const char* name,
|
||||
const char* signature);
|
||||
|
||||
jclass GetObjectClass(JNIEnv* jni, jobject object);
|
||||
|
||||
// Throws an exception if the object field is null.
|
||||
jobject GetObjectField(JNIEnv* jni, jobject object, jfieldID id);
|
||||
|
||||
jobject GetNullableObjectField(JNIEnv* jni, jobject object, jfieldID id);
|
||||
|
||||
jstring GetStringField(JNIEnv* jni, jobject object, jfieldID id);
|
||||
|
||||
jlong GetLongField(JNIEnv* jni, jobject object, jfieldID id);
|
||||
|
||||
jint GetIntField(JNIEnv* jni, jobject object, jfieldID id);
|
||||
|
||||
bool GetBooleanField(JNIEnv* jni, jobject object, jfieldID id);
|
||||
|
||||
// Returns true if |obj| == null in Java.
|
||||
bool IsNull(JNIEnv* jni, jobject obj);
|
||||
|
||||
// Given a UTF-8 encoded |native| string return a new (UTF-16) jstring.
|
||||
jstring JavaStringFromStdString(JNIEnv* jni, const std::string& native);
|
||||
|
||||
// Given a (UTF-16) jstring return a new UTF-8 native string.
|
||||
std::string JavaToStdString(JNIEnv* jni, const jstring& j_string);
|
||||
|
||||
// Return the (singleton) Java Enum object corresponding to |index|;
|
||||
jobject JavaEnumFromIndex(JNIEnv* jni, jclass state_class,
|
||||
const std::string& state_class_name, int index);
|
||||
|
||||
// Returns the name of a Java enum.
|
||||
std::string GetJavaEnumName(JNIEnv* jni,
|
||||
const std::string& className,
|
||||
jobject j_enum);
|
||||
|
||||
jobject NewGlobalRef(JNIEnv* jni, jobject o);
|
||||
|
||||
void DeleteGlobalRef(JNIEnv* jni, jobject o);
|
||||
|
||||
// Scope Java local references to the lifetime of this object. Use in all C++
|
||||
// callbacks (i.e. entry points that don't originate in a Java callstack
|
||||
// through a "native" method call).
|
||||
class ScopedLocalRefFrame {
|
||||
public:
|
||||
explicit ScopedLocalRefFrame(JNIEnv* jni);
|
||||
~ScopedLocalRefFrame();
|
||||
|
||||
private:
|
||||
JNIEnv* jni_;
|
||||
};
|
||||
|
||||
// Scoped holder for global Java refs.
|
||||
template<class T> // T is jclass, jobject, jintArray, etc.
|
||||
class ScopedGlobalRef {
|
||||
public:
|
||||
ScopedGlobalRef(JNIEnv* jni, T obj)
|
||||
: obj_(static_cast<T>(jni->NewGlobalRef(obj))) {}
|
||||
~ScopedGlobalRef() {
|
||||
DeleteGlobalRef(AttachCurrentThreadIfNeeded(), obj_);
|
||||
}
|
||||
T operator*() const {
|
||||
return obj_;
|
||||
}
|
||||
private:
|
||||
T obj_;
|
||||
};
|
||||
|
||||
// Provides a convenient way to iterate over a Java Iterable using the
|
||||
// C++ range-for loop.
|
||||
// E.g. for (jobject value : Iterable(jni, j_iterable)) { ... }
|
||||
// Note: Since Java iterators cannot be duplicated, the iterator class is not
|
||||
// copyable to prevent creating multiple C++ iterators that refer to the same
|
||||
// Java iterator.
|
||||
class Iterable {
|
||||
public:
|
||||
Iterable(JNIEnv* jni, jobject iterable) : jni_(jni), iterable_(iterable) {}
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
// Creates an iterator representing the end of any collection.
|
||||
Iterator();
|
||||
// Creates an iterator pointing to the beginning of the specified
|
||||
// collection.
|
||||
Iterator(JNIEnv* jni, jobject iterable);
|
||||
|
||||
// Move constructor - necessary to be able to return iterator types from
|
||||
// functions.
|
||||
Iterator(Iterator&& other);
|
||||
|
||||
// Move assignment should not be used.
|
||||
Iterator& operator=(Iterator&&) = delete;
|
||||
|
||||
// Advances the iterator one step.
|
||||
Iterator& operator++();
|
||||
|
||||
// Provides a way to compare the iterator with itself and with the end
|
||||
// iterator.
|
||||
// Note: all other comparison results are undefined, just like for C++ input
|
||||
// iterators.
|
||||
bool operator==(const Iterator& other);
|
||||
bool operator!=(const Iterator& other) { return !(*this == other); }
|
||||
jobject operator*();
|
||||
|
||||
private:
|
||||
bool AtEnd() const;
|
||||
|
||||
JNIEnv* jni_ = nullptr;
|
||||
jobject iterator_ = nullptr;
|
||||
jobject value_ = nullptr;
|
||||
jmethodID has_next_id_ = nullptr;
|
||||
jmethodID next_id_ = nullptr;
|
||||
rtc::ThreadChecker thread_checker_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(Iterator);
|
||||
};
|
||||
|
||||
Iterable::Iterator begin() { return Iterable::Iterator(jni_, iterable_); }
|
||||
Iterable::Iterator end() { return Iterable::Iterator(); }
|
||||
|
||||
private:
|
||||
JNIEnv* jni_;
|
||||
jobject iterable_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(Iterable);
|
||||
};
|
||||
|
||||
} // namespace webrtc_jni
|
||||
|
||||
#endif // WEBRTC_SDK_ANDROID_SRC_JNI_JNI_HELPERS_H_
|
||||
38
webrtc/sdk/android/src/jni/jni_onload.cc
Normal file
38
webrtc/sdk/android/src/jni/jni_onload.cc
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2015 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 <jni.h>
|
||||
#undef JNIEXPORT
|
||||
#define JNIEXPORT __attribute__((visibility("default")))
|
||||
|
||||
#include "webrtc/sdk/android/src/jni/classreferenceholder.h"
|
||||
#include "webrtc/sdk/android/src/jni/jni_helpers.h"
|
||||
#include "webrtc/base/ssladapter.h"
|
||||
|
||||
namespace webrtc_jni {
|
||||
|
||||
extern "C" jint JNIEXPORT JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
||||
jint ret = InitGlobalJniVariables(jvm);
|
||||
RTC_DCHECK_GE(ret, 0);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
RTC_CHECK(rtc::InitializeSSL()) << "Failed to InitializeSSL()";
|
||||
LoadGlobalClassReferenceHolder();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern "C" void JNIEXPORT JNICALL JNI_OnUnLoad(JavaVM *jvm, void *reserved) {
|
||||
FreeGlobalClassReferenceHolder();
|
||||
RTC_CHECK(rtc::CleanupSSL()) << "Failed to CleanupSSL()";
|
||||
}
|
||||
|
||||
} // namespace webrtc_jni
|
||||
215
webrtc/sdk/android/src/jni/native_handle_impl.cc
Normal file
215
webrtc/sdk/android/src/jni/native_handle_impl.cc
Normal file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright 2015 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/native_handle_impl.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "webrtc/sdk/android/src/jni/jni_helpers.h"
|
||||
#include "webrtc/base/bind.h"
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/keep_ref_until_done.h"
|
||||
#include "webrtc/base/logging.h"
|
||||
#include "webrtc/base/scoped_ref_ptr.h"
|
||||
|
||||
using webrtc::NativeHandleBuffer;
|
||||
|
||||
namespace webrtc_jni {
|
||||
|
||||
Matrix::Matrix(JNIEnv* jni, jfloatArray a) {
|
||||
RTC_CHECK_EQ(16, jni->GetArrayLength(a));
|
||||
jfloat* ptr = jni->GetFloatArrayElements(a, nullptr);
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
elem_[i] = ptr[i];
|
||||
}
|
||||
jni->ReleaseFloatArrayElements(a, ptr, 0);
|
||||
}
|
||||
|
||||
jfloatArray Matrix::ToJava(JNIEnv* jni) {
|
||||
jfloatArray matrix = jni->NewFloatArray(16);
|
||||
jni->SetFloatArrayRegion(matrix, 0, 16, elem_);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
void Matrix::Rotate(webrtc::VideoRotation rotation) {
|
||||
// Texture coordinates are in the range 0 to 1. The transformation of the last
|
||||
// row in each rotation matrix is needed for proper translation, e.g, to
|
||||
// mirror x, we don't replace x by -x, but by 1-x.
|
||||
switch (rotation) {
|
||||
case webrtc::kVideoRotation_0:
|
||||
break;
|
||||
case webrtc::kVideoRotation_90: {
|
||||
const float ROTATE_90[16] =
|
||||
{ elem_[4], elem_[5], elem_[6], elem_[7],
|
||||
-elem_[0], -elem_[1], -elem_[2], -elem_[3],
|
||||
elem_[8], elem_[9], elem_[10], elem_[11],
|
||||
elem_[0] + elem_[12], elem_[1] + elem_[13],
|
||||
elem_[2] + elem_[14], elem_[3] + elem_[15]};
|
||||
memcpy(elem_, ROTATE_90, sizeof(elem_));
|
||||
} break;
|
||||
case webrtc::kVideoRotation_180: {
|
||||
const float ROTATE_180[16] =
|
||||
{ -elem_[0], -elem_[1], -elem_[2], -elem_[3],
|
||||
-elem_[4], -elem_[5], -elem_[6], -elem_[7],
|
||||
elem_[8], elem_[9], elem_[10], elem_[11],
|
||||
elem_[0] + elem_[4] + elem_[12], elem_[1] + elem_[5] + elem_[13],
|
||||
elem_[2] + elem_[6] + elem_[14], elem_[3] + elem_[11]+ elem_[15]};
|
||||
memcpy(elem_, ROTATE_180, sizeof(elem_));
|
||||
} break;
|
||||
case webrtc::kVideoRotation_270: {
|
||||
const float ROTATE_270[16] =
|
||||
{ -elem_[4], -elem_[5], -elem_[6], -elem_[7],
|
||||
elem_[0], elem_[1], elem_[2], elem_[3],
|
||||
elem_[8], elem_[9], elem_[10], elem_[11],
|
||||
elem_[4] + elem_[12], elem_[5] + elem_[13],
|
||||
elem_[6] + elem_[14], elem_[7] + elem_[15]};
|
||||
memcpy(elem_, ROTATE_270, sizeof(elem_));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculates result = a * b, in column-major order.
|
||||
void Matrix::Multiply(const float a[16], const float b[16], float result[16]) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
float sum = 0;
|
||||
for (int k = 0; k < 4; ++k) {
|
||||
sum += a[k * 4 + j] * b[i * 4 + k];
|
||||
}
|
||||
result[i * 4 + j] = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Center crop by keeping xFraction of the width and yFraction of the height,
|
||||
// so e.g. cropping from 640x480 to 640x360 would use
|
||||
// xFraction=1, yFraction=360/480.
|
||||
void Matrix::Crop(float xFraction,
|
||||
float yFraction,
|
||||
float xOffset,
|
||||
float yOffset) {
|
||||
const float crop_matrix[16] =
|
||||
{xFraction, 0, 0, 0,
|
||||
0, yFraction, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
xOffset, yOffset, 0, 1};
|
||||
const Matrix old = *this;
|
||||
Multiply(crop_matrix, old.elem_, this->elem_);
|
||||
}
|
||||
|
||||
// Aligning pointer to 64 bytes for improved performance, e.g. use SIMD.
|
||||
static const int kBufferAlignment = 64;
|
||||
|
||||
NativeHandleImpl::NativeHandleImpl(int id, const Matrix& matrix)
|
||||
: oes_texture_id(id), sampling_matrix(matrix) {}
|
||||
|
||||
NativeHandleImpl::NativeHandleImpl(JNIEnv* jni,
|
||||
jint j_oes_texture_id,
|
||||
jfloatArray j_transform_matrix)
|
||||
: oes_texture_id(j_oes_texture_id),
|
||||
sampling_matrix(jni, j_transform_matrix) {}
|
||||
|
||||
AndroidTextureBuffer::AndroidTextureBuffer(
|
||||
int width,
|
||||
int height,
|
||||
const NativeHandleImpl& native_handle,
|
||||
jobject surface_texture_helper,
|
||||
const rtc::Callback0<void>& no_longer_used)
|
||||
: webrtc::NativeHandleBuffer(&native_handle_, width, height),
|
||||
native_handle_(native_handle),
|
||||
surface_texture_helper_(surface_texture_helper),
|
||||
no_longer_used_cb_(no_longer_used) {}
|
||||
|
||||
AndroidTextureBuffer::~AndroidTextureBuffer() {
|
||||
no_longer_used_cb_();
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<webrtc::VideoFrameBuffer>
|
||||
AndroidTextureBuffer::NativeToI420Buffer() {
|
||||
int uv_width = (width()+7) / 8;
|
||||
int stride = 8 * uv_width;
|
||||
int uv_height = (height()+1)/2;
|
||||
size_t size = stride * (height() + uv_height);
|
||||
// The data is owned by the frame, and the normal case is that the
|
||||
// data is deleted by the frame's destructor callback.
|
||||
//
|
||||
// TODO(nisse): Use an I420BufferPool. We then need to extend that
|
||||
// class, and I420Buffer, to support our memory layout.
|
||||
std::unique_ptr<uint8_t, webrtc::AlignedFreeDeleter> yuv_data(
|
||||
static_cast<uint8_t*>(webrtc::AlignedMalloc(size, kBufferAlignment)));
|
||||
// See YuvConverter.java for the required layout.
|
||||
uint8_t* y_data = yuv_data.get();
|
||||
uint8_t* u_data = y_data + height() * stride;
|
||||
uint8_t* v_data = u_data + stride/2;
|
||||
|
||||
rtc::scoped_refptr<webrtc::VideoFrameBuffer> copy =
|
||||
new rtc::RefCountedObject<webrtc::WrappedI420Buffer>(
|
||||
width(), height(),
|
||||
y_data, stride,
|
||||
u_data, stride,
|
||||
v_data, stride,
|
||||
rtc::Bind(&webrtc::AlignedFree, yuv_data.release()));
|
||||
|
||||
JNIEnv* jni = AttachCurrentThreadIfNeeded();
|
||||
ScopedLocalRefFrame local_ref_frame(jni);
|
||||
|
||||
jmethodID transform_mid = GetMethodID(
|
||||
jni,
|
||||
GetObjectClass(jni, surface_texture_helper_),
|
||||
"textureToYUV",
|
||||
"(Ljava/nio/ByteBuffer;IIII[F)V");
|
||||
|
||||
jobject byte_buffer = jni->NewDirectByteBuffer(y_data, size);
|
||||
|
||||
jfloatArray sampling_matrix = native_handle_.sampling_matrix.ToJava(jni);
|
||||
jni->CallVoidMethod(surface_texture_helper_,
|
||||
transform_mid,
|
||||
byte_buffer, width(), height(), stride,
|
||||
native_handle_.oes_texture_id, sampling_matrix);
|
||||
CHECK_EXCEPTION(jni) << "textureToYUV throwed an exception";
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<AndroidTextureBuffer>
|
||||
AndroidTextureBuffer::CropScaleAndRotate(int cropped_width,
|
||||
int cropped_height,
|
||||
int crop_x,
|
||||
int crop_y,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
webrtc::VideoRotation rotation) {
|
||||
if (cropped_width == dst_width && cropped_height == dst_height &&
|
||||
width() == dst_width && height() == dst_height &&
|
||||
rotation == webrtc::kVideoRotation_0) {
|
||||
return this;
|
||||
}
|
||||
int rotated_width = (rotation % 180 == 0) ? dst_width : dst_height;
|
||||
int rotated_height = (rotation % 180 == 0) ? dst_height : dst_width;
|
||||
|
||||
// Here we use Bind magic to add a reference count to |this| until the newly
|
||||
// created AndroidTextureBuffer is destructed
|
||||
rtc::scoped_refptr<AndroidTextureBuffer> buffer(
|
||||
new rtc::RefCountedObject<AndroidTextureBuffer>(
|
||||
rotated_width, rotated_height, native_handle_,
|
||||
surface_texture_helper_, rtc::KeepRefUntilDone(this)));
|
||||
|
||||
if (cropped_width != width() || cropped_height != height()) {
|
||||
buffer->native_handle_.sampling_matrix.Crop(
|
||||
cropped_width / static_cast<float>(width()),
|
||||
cropped_height / static_cast<float>(height()),
|
||||
crop_x / static_cast<float>(width()),
|
||||
crop_y / static_cast<float>(height()));
|
||||
}
|
||||
buffer->native_handle_.sampling_matrix.Rotate(rotation);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
} // namespace webrtc_jni
|
||||
87
webrtc/sdk/android/src/jni/native_handle_impl.h
Normal file
87
webrtc/sdk/android/src/jni/native_handle_impl.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2015 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_NATIVE_HANDLE_IMPL_H_
|
||||
#define WEBRTC_SDK_ANDROID_SRC_JNI_NATIVE_HANDLE_IMPL_H_
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "webrtc/common_video/include/video_frame_buffer.h"
|
||||
#include "webrtc/common_video/rotation.h"
|
||||
|
||||
namespace webrtc_jni {
|
||||
|
||||
// Open gl texture matrix, in column-major order. Operations are
|
||||
// in-place.
|
||||
class Matrix {
|
||||
public:
|
||||
Matrix(JNIEnv* jni, jfloatArray a);
|
||||
|
||||
jfloatArray ToJava(JNIEnv* jni);
|
||||
|
||||
// Crop arguments are relative to original size.
|
||||
void Crop(float cropped_width,
|
||||
float cropped_height,
|
||||
float crop_x,
|
||||
float crop_y);
|
||||
|
||||
void Rotate(webrtc::VideoRotation rotation);
|
||||
|
||||
private:
|
||||
static void Multiply(const float a[16], const float b[16], float result[16]);
|
||||
float elem_[16];
|
||||
};
|
||||
|
||||
// Wrapper for texture object.
|
||||
struct NativeHandleImpl {
|
||||
NativeHandleImpl(JNIEnv* jni,
|
||||
jint j_oes_texture_id,
|
||||
jfloatArray j_transform_matrix);
|
||||
|
||||
NativeHandleImpl(int id, const Matrix& matrix);
|
||||
|
||||
const int oes_texture_id;
|
||||
Matrix sampling_matrix;
|
||||
};
|
||||
|
||||
class AndroidTextureBuffer : public webrtc::NativeHandleBuffer {
|
||||
public:
|
||||
AndroidTextureBuffer(int width,
|
||||
int height,
|
||||
const NativeHandleImpl& native_handle,
|
||||
jobject surface_texture_helper,
|
||||
const rtc::Callback0<void>& no_longer_used);
|
||||
~AndroidTextureBuffer();
|
||||
rtc::scoped_refptr<VideoFrameBuffer> NativeToI420Buffer() override;
|
||||
|
||||
// First crop, then scale to dst resolution, and then rotate.
|
||||
rtc::scoped_refptr<AndroidTextureBuffer> CropScaleAndRotate(
|
||||
int cropped_width,
|
||||
int cropped_height,
|
||||
int crop_x,
|
||||
int crop_y,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
webrtc::VideoRotation rotation);
|
||||
|
||||
private:
|
||||
NativeHandleImpl native_handle_;
|
||||
// Raw object pointer, relying on the caller, i.e.,
|
||||
// AndroidVideoCapturerJni or the C++ SurfaceTextureHelper, to keep
|
||||
// a global reference. TODO(nisse): Make this a reference to the C++
|
||||
// SurfaceTextureHelper instead, but that requires some refactoring
|
||||
// of AndroidVideoCapturerJni.
|
||||
jobject surface_texture_helper_;
|
||||
rtc::Callback0<void> no_longer_used_cb_;
|
||||
};
|
||||
|
||||
} // namespace webrtc_jni
|
||||
|
||||
#endif // WEBRTC_SDK_ANDROID_SRC_JNI_NATIVE_HANDLE_IMPL_H_
|
||||
2414
webrtc/sdk/android/src/jni/peerconnection_jni.cc
Normal file
2414
webrtc/sdk/android/src/jni/peerconnection_jni.cc
Normal file
File diff suppressed because it is too large
Load Diff
82
webrtc/sdk/android/src/jni/surfacetexturehelper_jni.cc
Normal file
82
webrtc/sdk/android/src/jni/surfacetexturehelper_jni.cc
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2015 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/surfacetexturehelper_jni.h"
|
||||
|
||||
#include "webrtc/sdk/android/src/jni/classreferenceholder.h"
|
||||
#include "webrtc/base/bind.h"
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/logging.h"
|
||||
|
||||
namespace webrtc_jni {
|
||||
|
||||
rtc::scoped_refptr<SurfaceTextureHelper> SurfaceTextureHelper::create(
|
||||
JNIEnv* jni,
|
||||
const char* thread_name,
|
||||
jobject j_egl_context) {
|
||||
jobject j_surface_texture_helper = jni->CallStaticObjectMethod(
|
||||
FindClass(jni, "org/webrtc/SurfaceTextureHelper"),
|
||||
GetStaticMethodID(jni, FindClass(jni, "org/webrtc/SurfaceTextureHelper"),
|
||||
"create",
|
||||
"(Ljava/lang/String;Lorg/webrtc/EglBase$Context;)"
|
||||
"Lorg/webrtc/SurfaceTextureHelper;"),
|
||||
jni->NewStringUTF(thread_name), j_egl_context);
|
||||
CHECK_EXCEPTION(jni)
|
||||
<< "error during initialization of Java SurfaceTextureHelper";
|
||||
if (IsNull(jni, j_surface_texture_helper))
|
||||
return nullptr;
|
||||
return new rtc::RefCountedObject<SurfaceTextureHelper>(
|
||||
jni, j_surface_texture_helper);
|
||||
}
|
||||
|
||||
SurfaceTextureHelper::SurfaceTextureHelper(JNIEnv* jni,
|
||||
jobject j_surface_texture_helper)
|
||||
: j_surface_texture_helper_(jni, j_surface_texture_helper),
|
||||
j_return_texture_method_(
|
||||
GetMethodID(jni,
|
||||
FindClass(jni, "org/webrtc/SurfaceTextureHelper"),
|
||||
"returnTextureFrame",
|
||||
"()V")) {
|
||||
CHECK_EXCEPTION(jni) << "error during initialization of SurfaceTextureHelper";
|
||||
}
|
||||
|
||||
SurfaceTextureHelper::~SurfaceTextureHelper() {
|
||||
LOG(LS_INFO) << "SurfaceTextureHelper dtor";
|
||||
JNIEnv* jni = AttachCurrentThreadIfNeeded();
|
||||
jni->CallVoidMethod(
|
||||
*j_surface_texture_helper_,
|
||||
GetMethodID(jni, FindClass(jni, "org/webrtc/SurfaceTextureHelper"),
|
||||
"dispose", "()V"));
|
||||
|
||||
CHECK_EXCEPTION(jni) << "error during SurfaceTextureHelper.dispose()";
|
||||
}
|
||||
|
||||
jobject SurfaceTextureHelper::GetJavaSurfaceTextureHelper() const {
|
||||
return *j_surface_texture_helper_;
|
||||
}
|
||||
|
||||
void SurfaceTextureHelper::ReturnTextureFrame() const {
|
||||
JNIEnv* jni = AttachCurrentThreadIfNeeded();
|
||||
jni->CallVoidMethod(*j_surface_texture_helper_, j_return_texture_method_);
|
||||
|
||||
CHECK_EXCEPTION(
|
||||
jni) << "error during SurfaceTextureHelper.returnTextureFrame";
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<webrtc::VideoFrameBuffer>
|
||||
SurfaceTextureHelper::CreateTextureFrame(int width, int height,
|
||||
const NativeHandleImpl& native_handle) {
|
||||
return new rtc::RefCountedObject<AndroidTextureBuffer>(
|
||||
width, height, native_handle, *j_surface_texture_helper_,
|
||||
rtc::Bind(&SurfaceTextureHelper::ReturnTextureFrame, this));
|
||||
}
|
||||
|
||||
} // namespace webrtc_jni
|
||||
68
webrtc/sdk/android/src/jni/surfacetexturehelper_jni.h
Normal file
68
webrtc/sdk/android/src/jni/surfacetexturehelper_jni.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2015 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_SURFACETEXTUREHELPER_JNI_H_
|
||||
#define WEBRTC_SDK_ANDROID_SRC_JNI_SURFACETEXTUREHELPER_JNI_H_
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "webrtc/sdk/android/src/jni/jni_helpers.h"
|
||||
#include "webrtc/sdk/android/src/jni/native_handle_impl.h"
|
||||
#include "webrtc/base/refcount.h"
|
||||
#include "webrtc/base/scoped_ref_ptr.h"
|
||||
#include "webrtc/common_video/include/video_frame_buffer.h"
|
||||
|
||||
namespace webrtc_jni {
|
||||
|
||||
// Helper class to create and synchronize access to an Android SurfaceTexture.
|
||||
// It is used for creating webrtc::VideoFrameBuffers from a SurfaceTexture when
|
||||
// the SurfaceTexture has been updated.
|
||||
// When the VideoFrameBuffer is released, this class returns the buffer to the
|
||||
// java SurfaceTextureHelper so it can be updated safely. The VideoFrameBuffer
|
||||
// can be released on an arbitrary thread.
|
||||
// SurfaceTextureHelper is reference counted to make sure that it is not
|
||||
// destroyed while a VideoFrameBuffer is in use.
|
||||
// This class is the C++ counterpart of the java class SurfaceTextureHelper.
|
||||
// It owns the corresponding java object, and calls the java dispose
|
||||
// method when destroyed.
|
||||
// Usage:
|
||||
// 1. Create an instance of this class.
|
||||
// 2. Get the Java SurfaceTextureHelper with GetJavaSurfaceTextureHelper().
|
||||
// 3. Register a listener to the Java SurfaceListener and start producing
|
||||
// new buffers.
|
||||
// 4. Call CreateTextureFrame to wrap the Java texture in a VideoFrameBuffer.
|
||||
class SurfaceTextureHelper : public rtc::RefCountInterface {
|
||||
public:
|
||||
// Might return null if creating the Java SurfaceTextureHelper fails.
|
||||
static rtc::scoped_refptr<SurfaceTextureHelper> create(
|
||||
JNIEnv* jni, const char* thread_name, jobject j_egl_context);
|
||||
|
||||
jobject GetJavaSurfaceTextureHelper() const;
|
||||
|
||||
rtc::scoped_refptr<webrtc::VideoFrameBuffer> CreateTextureFrame(
|
||||
int width,
|
||||
int height,
|
||||
const NativeHandleImpl& native_handle);
|
||||
|
||||
// May be called on arbitrary thread.
|
||||
void ReturnTextureFrame() const;
|
||||
|
||||
protected:
|
||||
~SurfaceTextureHelper();
|
||||
SurfaceTextureHelper(JNIEnv* jni, jobject j_surface_texture_helper);
|
||||
|
||||
private:
|
||||
const ScopedGlobalRef<jobject> j_surface_texture_helper_;
|
||||
const jmethodID j_return_texture_method_;
|
||||
};
|
||||
|
||||
} // namespace webrtc_jni
|
||||
|
||||
#endif // WEBRTC_SDK_ANDROID_SRC_JNI_SURFACETEXTUREHELPER_JNI_H_
|
||||
Reference in New Issue
Block a user