From ef4342f21ba9448138fc7d22482f3210cb20fd7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20Kalliom=C3=A4ki?= Date: Mon, 26 Jun 2017 13:40:49 +0200 Subject: [PATCH] C++ wrapper for VideoDecoder and VideoDecoderFactory interfaces. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: webrtc:7760 Change-Id: I136aff9bcfb9244bb45ec552b5443f4a06b87c27 Reviewed-on: https://chromium-review.googlesource.com/535475 Commit-Queue: Sami Kalliomäki Reviewed-by: Magnus Jedvert Cr-Commit-Position: refs/heads/master@{#18773} --- webrtc/sdk/android/BUILD.gn | 5 + .../java/org/webrtc/HardwareVideoDecoder.java | 4 +- .../webrtc/VideoDecoderWrapperCallback.java | 30 ++ webrtc/sdk/android/src/jni/DEPS | 1 + .../android/src/jni/classreferenceholder.cc | 5 + .../src/jni/videodecoderfactorywrapper.cc | 47 ++++ .../src/jni/videodecoderfactorywrapper.h | 40 +++ .../android/src/jni/videodecoderwrapper.cc | 256 ++++++++++++++++++ .../sdk/android/src/jni/videodecoderwrapper.h | 115 ++++++++ 9 files changed, 502 insertions(+), 1 deletion(-) create mode 100644 webrtc/sdk/android/src/java/org/webrtc/VideoDecoderWrapperCallback.java create mode 100644 webrtc/sdk/android/src/jni/videodecoderfactorywrapper.cc create mode 100644 webrtc/sdk/android/src/jni/videodecoderfactorywrapper.h create mode 100644 webrtc/sdk/android/src/jni/videodecoderwrapper.cc create mode 100644 webrtc/sdk/android/src/jni/videodecoderwrapper.h diff --git a/webrtc/sdk/android/BUILD.gn b/webrtc/sdk/android/BUILD.gn index adac2a8214..97b56044f9 100644 --- a/webrtc/sdk/android/BUILD.gn +++ b/webrtc/sdk/android/BUILD.gn @@ -92,6 +92,10 @@ rtc_static_library("video_jni") { "src/jni/surfacetexturehelper_jni.h", "src/jni/video_jni.cc", "src/jni/video_renderer_jni.cc", + "src/jni/videodecoderfactorywrapper.cc", + "src/jni/videodecoderfactorywrapper.h", + "src/jni/videodecoderwrapper.cc", + "src/jni/videodecoderwrapper.h", "src/jni/videofilerenderer_jni.cc", "src/jni/videotrack_jni.cc", "src/jni/wrapped_native_i420_buffer.cc", @@ -395,6 +399,7 @@ android_library("libjingle_peerconnection_java") { "src/java/org/webrtc/HardwareVideoEncoder.java", "src/java/org/webrtc/Histogram.java", "src/java/org/webrtc/I420BufferImpl.java", + "src/java/org/webrtc/VideoDecoderWrapperCallback.java", "src/java/org/webrtc/MediaCodecUtils.java", "src/java/org/webrtc/VideoCodecType.java", "src/java/org/webrtc/WrappedNativeI420Buffer.java", diff --git a/webrtc/sdk/android/src/java/org/webrtc/HardwareVideoDecoder.java b/webrtc/sdk/android/src/java/org/webrtc/HardwareVideoDecoder.java index 20547e1f5b..a71155fb93 100644 --- a/webrtc/sdk/android/src/java/org/webrtc/HardwareVideoDecoder.java +++ b/webrtc/sdk/android/src/java/org/webrtc/HardwareVideoDecoder.java @@ -269,7 +269,9 @@ class HardwareVideoDecoder implements VideoDecoder { @Override public VideoCodecStatus release() { - decoderThreadChecker.checkIsOnValidThread(); + // TODO(sakal): This is not called on the correct thread but is still called synchronously. + // Re-enable the check once this is called on the correct thread. + // decoderThreadChecker.checkIsOnValidThread(); try { // The outputThread actually stops and releases the codec once running is false. running = false; diff --git a/webrtc/sdk/android/src/java/org/webrtc/VideoDecoderWrapperCallback.java b/webrtc/sdk/android/src/java/org/webrtc/VideoDecoderWrapperCallback.java new file mode 100644 index 0000000000..232466d7cb --- /dev/null +++ b/webrtc/sdk/android/src/java/org/webrtc/VideoDecoderWrapperCallback.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc; + +/** + * VideoDecoder callback that calls VideoDecoderWrapper.OnDecodedFrame for the decoded frames. + */ +class VideoDecoderWrapperCallback implements VideoDecoder.Callback { + private final long nativeDecoder; + + public VideoDecoderWrapperCallback(long nativeDecoder) { + this.nativeDecoder = nativeDecoder; + } + + @Override + public void onDecodedFrame(VideoFrame frame, Integer decodeTimeMs, Integer qp) { + nativeOnDecodedFrame(nativeDecoder, frame, decodeTimeMs, qp); + } + + private native static void nativeOnDecodedFrame( + long nativeDecoder, VideoFrame frame, Integer decodeTimeMs, Integer qp); +} diff --git a/webrtc/sdk/android/src/jni/DEPS b/webrtc/sdk/android/src/jni/DEPS index 9f982707b1..803232c0b6 100644 --- a/webrtc/sdk/android/src/jni/DEPS +++ b/webrtc/sdk/android/src/jni/DEPS @@ -8,6 +8,7 @@ include_rules = [ "+webrtc/media/base", "+webrtc/media/engine", "+webrtc/modules/utility/include/jvm_android.h", + "+webrtc/modules/video_coding/include/video_codec_interface.h", "+webrtc/modules/video_coding/utility/vp8_header_parser.h", "+webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h", "+webrtc/pc", diff --git a/webrtc/sdk/android/src/jni/classreferenceholder.cc b/webrtc/sdk/android/src/jni/classreferenceholder.cc index 01e1521b54..1f453881b5 100644 --- a/webrtc/sdk/android/src/jni/classreferenceholder.cc +++ b/webrtc/sdk/android/src/jni/classreferenceholder.cc @@ -65,6 +65,8 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { LoadClass(jni, "org/webrtc/EglBase"); LoadClass(jni, "org/webrtc/EglBase$Context"); LoadClass(jni, "org/webrtc/EglBase14$Context"); + LoadClass(jni, "org/webrtc/EncodedImage"); + LoadClass(jni, "org/webrtc/EncodedImage$FrameType"); LoadClass(jni, "org/webrtc/IceCandidate"); LoadClass(jni, "org/webrtc/MediaCodecVideoDecoder"); LoadClass(jni, "org/webrtc/MediaCodecVideoDecoder$DecodedOutputBuffer"); @@ -103,6 +105,9 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { LoadClass(jni, "org/webrtc/StatsReport$Value"); LoadClass(jni, "org/webrtc/SurfaceTextureHelper"); LoadClass(jni, "org/webrtc/VideoCapturer"); + LoadClass(jni, "org/webrtc/VideoCodecStatus"); + LoadClass(jni, "org/webrtc/VideoDecoder$Settings"); + LoadClass(jni, "org/webrtc/VideoDecoderWrapperCallback"); LoadClass(jni, "org/webrtc/VideoFrame"); LoadClass(jni, "org/webrtc/VideoFrame$Buffer"); LoadClass(jni, "org/webrtc/VideoFrame$I420Buffer"); diff --git a/webrtc/sdk/android/src/jni/videodecoderfactorywrapper.cc b/webrtc/sdk/android/src/jni/videodecoderfactorywrapper.cc new file mode 100644 index 0000000000..1584fd71d0 --- /dev/null +++ b/webrtc/sdk/android/src/jni/videodecoderfactorywrapper.cc @@ -0,0 +1,47 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/sdk/android/src/jni/videodecoderfactorywrapper.h" + +#include "webrtc/api/video_codecs/video_decoder.h" +#include "webrtc/base/logging.h" +#include "webrtc/common_types.h" +#include "webrtc/sdk/android/src/jni/videodecoderwrapper.h" + +namespace webrtc_jni { + +VideoDecoderFactoryWrapper::VideoDecoderFactoryWrapper(JNIEnv* jni, + jobject decoder_factory) + : decoder_factory_(jni, decoder_factory) { + jclass decoder_factory_class = jni->GetObjectClass(*decoder_factory_); + create_decoder_method_ = + jni->GetMethodID(decoder_factory_class, "createDecoder", + "(Ljava/lang/String;)Lorg/webrtc/VideoDecoder;"); +} + +webrtc::VideoDecoder* VideoDecoderFactoryWrapper::CreateVideoDecoder( + webrtc::VideoCodecType type) { + JNIEnv* jni = AttachCurrentThreadIfNeeded(); + ScopedLocalRefFrame local_ref_frame(jni); + rtc::Optional type_payload = + webrtc::CodecTypeToPayloadName(type); + RTC_DCHECK(type_payload); + jstring name = jni->NewStringUTF(*type_payload); + jobject decoder = + jni->CallObjectMethod(*decoder_factory_, create_decoder_method_, name); + return decoder != nullptr ? new VideoDecoderWrapper(jni, decoder) : nullptr; +} + +void VideoDecoderFactoryWrapper::DestroyVideoDecoder( + webrtc::VideoDecoder* decoder) { + delete decoder; +} + +} // namespace webrtc_jni diff --git a/webrtc/sdk/android/src/jni/videodecoderfactorywrapper.h b/webrtc/sdk/android/src/jni/videodecoderfactorywrapper.h new file mode 100644 index 0000000000..0e98a9550d --- /dev/null +++ b/webrtc/sdk/android/src/jni/videodecoderfactorywrapper.h @@ -0,0 +1,40 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_SDK_ANDROID_SRC_JNI_VIDEODECODERFACTORYWRAPPER_H_ +#define WEBRTC_SDK_ANDROID_SRC_JNI_VIDEODECODERFACTORYWRAPPER_H_ + +#include + +#include "webrtc/media/engine/webrtcvideodecoderfactory.h" +#include "webrtc/sdk/android/src/jni/jni_helpers.h" + +namespace webrtc_jni { + +// Wrapper for Java VideoDecoderFactory class. Delegates method calls through +// JNI and wraps the decoder inside VideoDecoderWrapper. +class VideoDecoderFactoryWrapper : public cricket::WebRtcVideoDecoderFactory { + public: + VideoDecoderFactoryWrapper(JNIEnv* jni, jobject decoder_factory); + + // Caller takes the ownership of the returned object and it should be released + // by calling DestroyVideoDecoder(). + webrtc::VideoDecoder* CreateVideoDecoder( + webrtc::VideoCodecType type) override; + void DestroyVideoDecoder(webrtc::VideoDecoder* decoder) override; + + private: + const ScopedGlobalRef decoder_factory_; + jmethodID create_decoder_method_; +}; + +} // namespace webrtc_jni + +#endif // WEBRTC_SDK_ANDROID_SRC_JNI_VIDEODECODERFACTORYWRAPPER_H_ diff --git a/webrtc/sdk/android/src/jni/videodecoderwrapper.cc b/webrtc/sdk/android/src/jni/videodecoderwrapper.cc new file mode 100644 index 0000000000..465cd7fb72 --- /dev/null +++ b/webrtc/sdk/android/src/jni/videodecoderwrapper.cc @@ -0,0 +1,256 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/sdk/android/src/jni/videodecoderwrapper.h" + +#include "webrtc/api/video/video_frame.h" +#include "webrtc/base/logging.h" +#include "webrtc/modules/video_coding/include/video_codec_interface.h" +#include "webrtc/sdk/android/src/jni/classreferenceholder.h" + +namespace webrtc_jni { + +VideoDecoderWrapper::VideoDecoderWrapper(JNIEnv* jni, jobject decoder) + : android_video_buffer_factory_(jni), + decoder_(jni, decoder), + encoded_image_class_(jni, FindClass(jni, "org/webrtc/EncodedImage")), + frame_type_class_(jni, + FindClass(jni, "org/webrtc/EncodedImage$FrameType")), + settings_class_(jni, FindClass(jni, "org/webrtc/VideoDecoder$Settings")), + video_frame_class_(jni, FindClass(jni, "org/webrtc/VideoFrame")), + video_codec_status_class_(jni, + FindClass(jni, "org/webrtc/VideoCodecStatus")), + integer_class_(jni, jni->FindClass("java/lang/Integer")) { + encoded_image_constructor_ = + jni->GetMethodID(*encoded_image_class_, "", + "(Ljava/nio/ByteBuffer;IIJLorg/webrtc/" + "EncodedImage$FrameType;IZLjava/lang/Integer;)V"); + settings_constructor_ = + jni->GetMethodID(*settings_class_, "", "(III)V"); + + empty_frame_field_ = jni->GetStaticFieldID( + *frame_type_class_, "EmptyFrame", "Lorg/webrtc/EncodedImage$FrameType;"); + video_frame_key_field_ = + jni->GetStaticFieldID(*frame_type_class_, "VideoFrameKey", + "Lorg/webrtc/EncodedImage$FrameType;"); + video_frame_delta_field_ = + jni->GetStaticFieldID(*frame_type_class_, "VideoFrameDelta", + "Lorg/webrtc/EncodedImage$FrameType;"); + + video_frame_get_timestamp_ns_method_ = + jni->GetMethodID(*video_frame_class_, "getTimestampNs", "()J"); + + jclass decoder_class = jni->GetObjectClass(decoder); + init_decode_method_ = + jni->GetMethodID(decoder_class, "initDecode", + "(Lorg/webrtc/VideoDecoder$Settings;Lorg/webrtc/" + "VideoDecoder$Callback;)Lorg/webrtc/VideoCodecStatus;"); + release_method_ = jni->GetMethodID(decoder_class, "release", + "()Lorg/webrtc/VideoCodecStatus;"); + decode_method_ = jni->GetMethodID(decoder_class, "decode", + "(Lorg/webrtc/EncodedImage;Lorg/webrtc/" + "VideoDecoder$DecodeInfo;)Lorg/webrtc/" + "VideoCodecStatus;"); + get_prefers_late_decoding_method_ = + jni->GetMethodID(decoder_class, "getPrefersLateDecoding", "()Z"); + get_implementation_name_method_ = jni->GetMethodID( + decoder_class, "getImplementationName", "()Ljava/lang/String;"); + + get_number_method_ = + jni->GetMethodID(*video_codec_status_class_, "getNumber", "()I"); + + integer_constructor_ = jni->GetMethodID(*integer_class_, "", "(I)V"); + int_value_method_ = jni->GetMethodID(*integer_class_, "intValue", "()I"); + + initialized_ = false; +} + +int32_t VideoDecoderWrapper::InitDecode( + const webrtc::VideoCodec* codec_settings, + int32_t number_of_cores) { + JNIEnv* jni = AttachCurrentThreadIfNeeded(); + ScopedLocalRefFrame local_ref_frame(jni); + + codec_settings_ = *codec_settings; + number_of_cores_ = number_of_cores; + return InitDecodeInternal(jni); +} + +int32_t VideoDecoderWrapper::InitDecodeInternal(JNIEnv* jni) { + jobject settings = + jni->NewObject(*settings_class_, settings_constructor_, number_of_cores_, + codec_settings_.width, codec_settings_.height); + + jclass callback_class = + FindClass(jni, "org/webrtc/VideoDecoderWrapperCallback"); + jmethodID callback_constructor = + jni->GetMethodID(callback_class, "", "(J)V"); + jobject callback = jni->NewObject(callback_class, callback_constructor, + jlongFromPointer(this)); + + jobject ret = + jni->CallObjectMethod(*decoder_, init_decode_method_, settings, callback); + if (jni->CallIntMethod(ret, get_number_method_) == WEBRTC_VIDEO_CODEC_OK) { + initialized_ = true; + } + return HandleReturnCode(jni, ret); +} + +int32_t VideoDecoderWrapper::Decode( + const webrtc::EncodedImage& input_image, + bool missing_frames, + const webrtc::RTPFragmentationHeader* fragmentation, + const webrtc::CodecSpecificInfo* codec_specific_info, + int64_t render_time_ms) { + if (!initialized_) { + // Most likely initializing the codec failed. + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; + } + + JNIEnv* jni = AttachCurrentThreadIfNeeded(); + ScopedLocalRefFrame local_ref_frame(jni); + + FrameExtraInfo frame_extra_info; + frame_extra_info.capture_time_ms = input_image.capture_time_ms_; + frame_extra_info.timestamp_rtp = input_image._timeStamp; + frame_extra_infos_.push_back(frame_extra_info); + + jobject jinput_image = + ConvertEncodedImageToJavaEncodedImage(jni, input_image); + jobject ret = + jni->CallObjectMethod(*decoder_, decode_method_, jinput_image, nullptr); + return HandleReturnCode(jni, ret); +} + +int32_t VideoDecoderWrapper::RegisterDecodeCompleteCallback( + webrtc::DecodedImageCallback* callback) { + callback_ = callback; + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t VideoDecoderWrapper::Release() { + JNIEnv* jni = AttachCurrentThreadIfNeeded(); + ScopedLocalRefFrame local_ref_frame(jni); + jobject ret = jni->CallObjectMethod(*decoder_, release_method_); + frame_extra_infos_.clear(); + initialized_ = false; + return HandleReturnCode(jni, ret); +} + +bool VideoDecoderWrapper::PrefersLateDecoding() const { + JNIEnv* jni = AttachCurrentThreadIfNeeded(); + return jni->CallBooleanMethod(*decoder_, get_prefers_late_decoding_method_); +} + +const char* VideoDecoderWrapper::ImplementationName() const { + JNIEnv* jni = AttachCurrentThreadIfNeeded(); + ScopedLocalRefFrame local_ref_frame(jni); + jstring jname = reinterpret_cast( + jni->CallObjectMethod(*decoder_, get_implementation_name_method_)); + return JavaToStdString(jni, jname).c_str(); +} + +void VideoDecoderWrapper::OnDecodedFrame(JNIEnv* jni, + jobject jframe, + jobject jdecode_time_ms, + jobject jqp) { + const jlong capture_time_ns = + jni->CallLongMethod(jframe, video_frame_get_timestamp_ns_method_); + const uint32_t capture_time_ms = capture_time_ns / 1000 / 1000; + FrameExtraInfo frame_extra_info; + do { + if (frame_extra_infos_.empty()) { + LOG(LS_WARNING) << "Java decoder produced an unexpected frame."; + return; + } + + frame_extra_info = frame_extra_infos_.front(); + frame_extra_infos_.pop_front(); + // If the decoder might drop frames so iterate through the queue until we + // find a matching timestamp. + } while (frame_extra_info.capture_time_ms != capture_time_ms); + + webrtc::VideoFrame frame = android_video_buffer_factory_.CreateFrame( + jni, jframe, frame_extra_info.timestamp_rtp); + + rtc::Optional decoding_time_ms; + if (jdecode_time_ms != nullptr) { + decoding_time_ms = rtc::Optional( + jni->CallIntMethod(jdecode_time_ms, int_value_method_)); + } + + rtc::Optional qp; + if (jqp != nullptr) { + qp = rtc::Optional(jni->CallIntMethod(jqp, int_value_method_)); + } + + callback_->Decoded(frame, decoding_time_ms, rtc::Optional()); +} + +jobject VideoDecoderWrapper::ConvertEncodedImageToJavaEncodedImage( + JNIEnv* jni, + const webrtc::EncodedImage& image) { + jobject buffer = jni->NewDirectByteBuffer(image._buffer, image._length); + jfieldID frame_type_field; + switch (image._frameType) { + case webrtc::kEmptyFrame: + frame_type_field = empty_frame_field_; + break; + case webrtc::kVideoFrameKey: + frame_type_field = video_frame_key_field_; + break; + case webrtc::kVideoFrameDelta: + frame_type_field = video_frame_delta_field_; + break; + default: + RTC_NOTREACHED(); + } + jobject frame_type = + jni->GetStaticObjectField(*frame_type_class_, frame_type_field); + jobject qp = nullptr; + if (image.qp_ != -1) { + qp = jni->NewObject(*integer_class_, integer_constructor_, image.qp_); + } + return jni->NewObject(*encoded_image_class_, encoded_image_constructor_, + buffer, static_cast(image._encodedWidth), + static_cast(image._encodedHeight), + static_cast(image.capture_time_ms_), frame_type, + static_cast(image.rotation_), + image._completeFrame, qp); +} + +int32_t VideoDecoderWrapper::HandleReturnCode(JNIEnv* jni, jobject code) { + int32_t value = jni->CallIntMethod(code, get_number_method_); + if (value < 0) { // Any errors are represented by negative values. + // Reset the codec. + if (Release() == WEBRTC_VIDEO_CODEC_OK) { + InitDecodeInternal(jni); + } + + LOG(LS_WARNING) << "Falling back to software decoder."; + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; + } else { + return value; + } +} + +JOW(void, VideoDecoderWrapperCallback_nativeOnDecodedFrame) +(JNIEnv* jni, + jclass, + jlong jnative_decoder, + jobject jframe, + jobject jdecode_time_ms, + jobject jqp) { + VideoDecoderWrapper* native_decoder = + reinterpret_cast(jnative_decoder); + native_decoder->OnDecodedFrame(jni, jframe, jdecode_time_ms, jqp); +} + +} // namespace webrtc_jni diff --git a/webrtc/sdk/android/src/jni/videodecoderwrapper.h b/webrtc/sdk/android/src/jni/videodecoderwrapper.h new file mode 100644 index 0000000000..eb76d3bf91 --- /dev/null +++ b/webrtc/sdk/android/src/jni/videodecoderwrapper.h @@ -0,0 +1,115 @@ +/* + * Copyright 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_SDK_ANDROID_SRC_JNI_VIDEODECODERWRAPPER_H_ +#define WEBRTC_SDK_ANDROID_SRC_JNI_VIDEODECODERWRAPPER_H_ + +#include + +#include + +#include "webrtc/api/video_codecs/video_decoder.h" +#include "webrtc/sdk/android/src/jni/jni_helpers.h" +#include "webrtc/sdk/android/src/jni/native_handle_impl.h" + +namespace webrtc_jni { + +// Wraps a Java decoder and delegates all calls to it. Passes +// VideoDecoderWrapperCallback to the decoder on InitDecode. Wraps the received +// frames to AndroidVideoBuffer. +class VideoDecoderWrapper : public webrtc::VideoDecoder { + public: + VideoDecoderWrapper(JNIEnv* jni, jobject decoder); + + int32_t InitDecode(const webrtc::VideoCodec* codec_settings, + int32_t number_of_cores) override; + + int32_t Decode(const webrtc::EncodedImage& input_image, + bool missing_frames, + const webrtc::RTPFragmentationHeader* fragmentation, + const webrtc::CodecSpecificInfo* codec_specific_info, + int64_t render_time_ms) override; + + int32_t RegisterDecodeCompleteCallback( + webrtc::DecodedImageCallback* callback) override; + + int32_t Release() override; + + // Returns true if the decoder prefer to decode frames late. + // That is, it can not decode infinite number of frames before the decoded + // frame is consumed. + bool PrefersLateDecoding() const override; + + const char* ImplementationName() const override; + + // Wraps the frame to a AndroidVideoBuffer and passes it to the callback. + void OnDecodedFrame(JNIEnv* jni, + jobject jframe, + jobject jdecode_time_ms, + jobject jqp); + + private: + struct FrameExtraInfo { + uint32_t capture_time_ms; // Used as an identifier of the frame. + + uint32_t timestamp_rtp; + }; + + int32_t InitDecodeInternal(JNIEnv* jni); + + // Takes Java VideoCodecStatus, handles it and returns WEBRTC_VIDEO_CODEC_* + // status code. + int32_t HandleReturnCode(JNIEnv* jni, jobject code); + + webrtc::VideoCodec codec_settings_; + int32_t number_of_cores_; + + bool initialized_; + AndroidVideoBufferFactory android_video_buffer_factory_; + std::deque frame_extra_infos_; + + webrtc::DecodedImageCallback* callback_; + + const ScopedGlobalRef decoder_; + const ScopedGlobalRef encoded_image_class_; + const ScopedGlobalRef frame_type_class_; + const ScopedGlobalRef settings_class_; + const ScopedGlobalRef video_frame_class_; + const ScopedGlobalRef video_codec_status_class_; + const ScopedGlobalRef integer_class_; + + jmethodID encoded_image_constructor_; + jmethodID settings_constructor_; + + jfieldID empty_frame_field_; + jfieldID video_frame_key_field_; + jfieldID video_frame_delta_field_; + + jmethodID video_frame_get_timestamp_ns_method_; + + jmethodID init_decode_method_; + jmethodID release_method_; + jmethodID decode_method_; + jmethodID get_prefers_late_decoding_method_; + jmethodID get_implementation_name_method_; + + jmethodID get_number_method_; + + jmethodID integer_constructor_; + jmethodID int_value_method_; + + jobject ConvertEncodedImageToJavaEncodedImage( + JNIEnv* jni, + const webrtc::EncodedImage& image); +}; + +} // namespace webrtc_jni + +#endif // WEBRTC_SDK_ANDROID_SRC_JNI_VIDEODECODERWRAPPER_H_