Revert of Combine webrtc/api/java/android and webrtc/api/java/src. (patchset #1 id:1 of https://codereview.webrtc.org/2111823002/ )
Reason for revert:
Breaks downstream dependencies
Original issue's description:
> Combine webrtc/api/java/android and webrtc/api/java/src.
>
> It used to be that there was a Java api for devices not running Android
> but that is no longer the case. I combined the directories and made
> the folder structure chromium style.
>
> BUG=webrtc:6067
> R=magjed@webrtc.org, tommi@webrtc.org
>
> Committed: https://chromium.googlesource.com/external/webrtc/+/ceefe20dd65387d83059d9fc1ce84842650ed5e2
TBR=magjed@webrtc.org,tommi@webrtc.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=webrtc:6067
Review URL: https://codereview.webrtc.org/2106333005 .
Cr-Commit-Position: refs/heads/master@{#13357}
diff --git a/webrtc/api/java/jni/androidmediadecoder_jni.cc b/webrtc/api/java/jni/androidmediadecoder_jni.cc
new file mode 100644
index 0000000..2ec222f
--- /dev/null
+++ b/webrtc/api/java/jni/androidmediadecoder_jni.cc
@@ -0,0 +1,995 @@
+/*
+ * 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/api/java/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/api/java/jni/androidmediacodeccommon.h"
+#include "webrtc/api/java/jni/classreferenceholder.h"
+#include "webrtc/api/java/jni/native_handle_impl.h"
+#include "webrtc/api/java/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.
+ frame_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,
+ frame_buffer->MutableDataY(),
+ frame_buffer->StrideY(),
+ frame_buffer->MutableDataU(),
+ frame_buffer->StrideU(),
+ frame_buffer->MutableDataV(),
+ frame_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,
+ frame_buffer->MutableDataY(),
+ frame_buffer->StrideY(),
+ frame_buffer->MutableDataU(),
+ frame_buffer->StrideU(),
+ frame_buffer->MutableDataV(),
+ frame_buffer->StrideV(),
+ width, height);
+ }
+ // 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