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:
magjed
2016-12-06 04:29:37 -08:00
committed by Commit bot
parent 45bb5130b0
commit 768c64877e
98 changed files with 721 additions and 684 deletions

View 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",
]

View 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

View 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

View 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_

View 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

View 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_

File diff suppressed because it is too large Load Diff

View 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_

View 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

View 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

View 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_

View 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

View 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_

View 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

View 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

View 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_

View 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

View 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_

View 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

View 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

View 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_

File diff suppressed because it is too large Load Diff

View 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

View 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_