| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. | |
| 3 * | |
| 4 * Use of this source code is governed by a BSD-style license | |
| 5 * that can be found in the LICENSE file in the root of the source | |
| 6 * tree. An additional intellectual property rights grant can be found | |
| 7 * in the file PATENTS. All contributing project authors may | |
| 8 * be found in the AUTHORS file in the root of the source tree. | |
| 9 */ | |
| 10 | |
| 11 #include <algorithm> | |
| 12 #include <memory> | |
| 13 #include <vector> | |
| 14 | |
| 15 // NOTICE: androidmediadecoder_jni.h must be included before | |
| 16 // androidmediacodeccommon.h to avoid build errors. | |
| 17 #include "webrtc/api/android/jni/androidmediadecoder_jni.h" | |
| 18 | |
| 19 #include "third_party/libyuv/include/libyuv/convert.h" | |
| 20 #include "third_party/libyuv/include/libyuv/convert_from.h" | |
| 21 #include "third_party/libyuv/include/libyuv/video_common.h" | |
| 22 #include "webrtc/api/android/jni/androidmediacodeccommon.h" | |
| 23 #include "webrtc/api/android/jni/classreferenceholder.h" | |
| 24 #include "webrtc/api/android/jni/native_handle_impl.h" | |
| 25 #include "webrtc/api/android/jni/surfacetexturehelper_jni.h" | |
| 26 #include "webrtc/base/bind.h" | |
| 27 #include "webrtc/base/checks.h" | |
| 28 #include "webrtc/base/logging.h" | |
| 29 #include "webrtc/base/scoped_ref_ptr.h" | |
| 30 #include "webrtc/base/thread.h" | |
| 31 #include "webrtc/base/timeutils.h" | |
| 32 #include "webrtc/common_video/include/i420_buffer_pool.h" | |
| 33 #include "webrtc/modules/video_coding/include/video_codec_interface.h" | |
| 34 #include "webrtc/system_wrappers/include/logcat_trace_context.h" | |
| 35 | |
| 36 using rtc::Bind; | |
| 37 using rtc::Thread; | |
| 38 using rtc::ThreadManager; | |
| 39 | |
| 40 using webrtc::CodecSpecificInfo; | |
| 41 using webrtc::DecodedImageCallback; | |
| 42 using webrtc::EncodedImage; | |
| 43 using webrtc::VideoFrame; | |
| 44 using webrtc::RTPFragmentationHeader; | |
| 45 using webrtc::VideoCodec; | |
| 46 using webrtc::VideoCodecType; | |
| 47 using webrtc::kVideoCodecH264; | |
| 48 using webrtc::kVideoCodecVP8; | |
| 49 using webrtc::kVideoCodecVP9; | |
| 50 | |
| 51 namespace webrtc_jni { | |
| 52 | |
| 53 // Logging macros. | |
| 54 #define TAG_DECODER "MediaCodecVideoDecoder" | |
| 55 #ifdef TRACK_BUFFER_TIMING | |
| 56 #define ALOGV(...) | |
| 57 __android_log_print(ANDROID_LOG_VERBOSE, TAG_DECODER, __VA_ARGS__) | |
| 58 #else | |
| 59 #define ALOGV(...) | |
| 60 #endif | |
| 61 #define ALOGD LOG_TAG(rtc::LS_INFO, TAG_DECODER) | |
| 62 #define ALOGW LOG_TAG(rtc::LS_WARNING, TAG_DECODER) | |
| 63 #define ALOGE LOG_TAG(rtc::LS_ERROR, TAG_DECODER) | |
| 64 | |
| 65 enum { kMaxWarningLogFrames = 2 }; | |
| 66 | |
| 67 class MediaCodecVideoDecoder : public webrtc::VideoDecoder, | |
| 68 public rtc::MessageHandler { | |
| 69 public: | |
| 70 explicit MediaCodecVideoDecoder( | |
| 71 JNIEnv* jni, VideoCodecType codecType, jobject render_egl_context); | |
| 72 virtual ~MediaCodecVideoDecoder(); | |
| 73 | |
| 74 int32_t InitDecode(const VideoCodec* codecSettings, int32_t numberOfCores) | |
| 75 override; | |
| 76 | |
| 77 int32_t Decode( | |
| 78 const EncodedImage& inputImage, bool missingFrames, | |
| 79 const RTPFragmentationHeader* fragmentation, | |
| 80 const CodecSpecificInfo* codecSpecificInfo = NULL, | |
| 81 int64_t renderTimeMs = -1) override; | |
| 82 | |
| 83 int32_t RegisterDecodeCompleteCallback(DecodedImageCallback* callback) | |
| 84 override; | |
| 85 | |
| 86 int32_t Release() override; | |
| 87 | |
| 88 bool PrefersLateDecoding() const override { return true; } | |
| 89 | |
| 90 // rtc::MessageHandler implementation. | |
| 91 void OnMessage(rtc::Message* msg) override; | |
| 92 | |
| 93 const char* ImplementationName() const override; | |
| 94 | |
| 95 private: | |
| 96 // CHECK-fail if not running on |codec_thread_|. | |
| 97 void CheckOnCodecThread(); | |
| 98 | |
| 99 int32_t InitDecodeOnCodecThread(); | |
| 100 int32_t ResetDecodeOnCodecThread(); | |
| 101 int32_t ReleaseOnCodecThread(); | |
| 102 int32_t DecodeOnCodecThread(const EncodedImage& inputImage); | |
| 103 // Deliver any outputs pending in the MediaCodec to our |callback_| and return | |
| 104 // true on success. | |
| 105 bool DeliverPendingOutputs(JNIEnv* jni, int dequeue_timeout_us); | |
| 106 int32_t ProcessHWErrorOnCodecThread(); | |
| 107 void EnableFrameLogOnWarning(); | |
| 108 void ResetVariables(); | |
| 109 | |
| 110 // Type of video codec. | |
| 111 VideoCodecType codecType_; | |
| 112 | |
| 113 // Render EGL context - owned by factory, should not be allocated/destroyed | |
| 114 // by VideoDecoder. | |
| 115 jobject render_egl_context_; | |
| 116 | |
| 117 bool key_frame_required_; | |
| 118 bool inited_; | |
| 119 bool sw_fallback_required_; | |
| 120 bool use_surface_; | |
| 121 VideoCodec codec_; | |
| 122 webrtc::I420BufferPool decoded_frame_pool_; | |
| 123 rtc::scoped_refptr<SurfaceTextureHelper> surface_texture_helper_; | |
| 124 DecodedImageCallback* callback_; | |
| 125 int frames_received_; // Number of frames received by decoder. | |
| 126 int frames_decoded_; // Number of frames decoded by decoder. | |
| 127 // Number of decoded frames for which log information is displayed. | |
| 128 int frames_decoded_logged_; | |
| 129 int64_t start_time_ms_; // Start time for statistics. | |
| 130 int current_frames_; // Number of frames in the current statistics interval. | |
| 131 int current_bytes_; // Encoded bytes in the current statistics interval. | |
| 132 int current_decoding_time_ms_; // Overall decoding time in the current second | |
| 133 int current_delay_time_ms_; // Overall delay time in the current second. | |
| 134 uint32_t max_pending_frames_; // Maximum number of pending input frames. | |
| 135 | |
| 136 // State that is constant for the lifetime of this object once the ctor | |
| 137 // returns. | |
| 138 std::unique_ptr<Thread> | |
| 139 codec_thread_; // Thread on which to operate MediaCodec. | |
| 140 ScopedGlobalRef<jclass> j_media_codec_video_decoder_class_; | |
| 141 ScopedGlobalRef<jobject> j_media_codec_video_decoder_; | |
| 142 jmethodID j_init_decode_method_; | |
| 143 jmethodID j_reset_method_; | |
| 144 jmethodID j_release_method_; | |
| 145 jmethodID j_dequeue_input_buffer_method_; | |
| 146 jmethodID j_queue_input_buffer_method_; | |
| 147 jmethodID j_dequeue_byte_buffer_method_; | |
| 148 jmethodID j_dequeue_texture_buffer_method_; | |
| 149 jmethodID j_return_decoded_byte_buffer_method_; | |
| 150 // MediaCodecVideoDecoder fields. | |
| 151 jfieldID j_input_buffers_field_; | |
| 152 jfieldID j_output_buffers_field_; | |
| 153 jfieldID j_color_format_field_; | |
| 154 jfieldID j_width_field_; | |
| 155 jfieldID j_height_field_; | |
| 156 jfieldID j_stride_field_; | |
| 157 jfieldID j_slice_height_field_; | |
| 158 // MediaCodecVideoDecoder.DecodedTextureBuffer fields. | |
| 159 jfieldID j_texture_id_field_; | |
| 160 jfieldID j_transform_matrix_field_; | |
| 161 jfieldID j_texture_presentation_timestamp_ms_field_; | |
| 162 jfieldID j_texture_timestamp_ms_field_; | |
| 163 jfieldID j_texture_ntp_timestamp_ms_field_; | |
| 164 jfieldID j_texture_decode_time_ms_field_; | |
| 165 jfieldID j_texture_frame_delay_ms_field_; | |
| 166 // MediaCodecVideoDecoder.DecodedOutputBuffer fields. | |
| 167 jfieldID j_info_index_field_; | |
| 168 jfieldID j_info_offset_field_; | |
| 169 jfieldID j_info_size_field_; | |
| 170 jfieldID j_presentation_timestamp_ms_field_; | |
| 171 jfieldID j_timestamp_ms_field_; | |
| 172 jfieldID j_ntp_timestamp_ms_field_; | |
| 173 jfieldID j_byte_buffer_decode_time_ms_field_; | |
| 174 | |
| 175 // Global references; must be deleted in Release(). | |
| 176 std::vector<jobject> input_buffers_; | |
| 177 }; | |
| 178 | |
| 179 MediaCodecVideoDecoder::MediaCodecVideoDecoder( | |
| 180 JNIEnv* jni, VideoCodecType codecType, jobject render_egl_context) : | |
| 181 codecType_(codecType), | |
| 182 render_egl_context_(render_egl_context), | |
| 183 key_frame_required_(true), | |
| 184 inited_(false), | |
| 185 sw_fallback_required_(false), | |
| 186 codec_thread_(new Thread()), | |
| 187 j_media_codec_video_decoder_class_( | |
| 188 jni, | |
| 189 FindClass(jni, "org/webrtc/MediaCodecVideoDecoder")), | |
| 190 j_media_codec_video_decoder_( | |
| 191 jni, | |
| 192 jni->NewObject(*j_media_codec_video_decoder_class_, | |
| 193 GetMethodID(jni, | |
| 194 *j_media_codec_video_decoder_class_, | |
| 195 "<init>", | |
| 196 "()V"))) { | |
| 197 ScopedLocalRefFrame local_ref_frame(jni); | |
| 198 codec_thread_->SetName("MediaCodecVideoDecoder", NULL); | |
| 199 RTC_CHECK(codec_thread_->Start()) << "Failed to start MediaCodecVideoDecoder"; | |
| 200 | |
| 201 j_init_decode_method_ = GetMethodID( | |
| 202 jni, *j_media_codec_video_decoder_class_, "initDecode", | |
| 203 "(Lorg/webrtc/MediaCodecVideoDecoder$VideoCodecType;" | |
| 204 "IILorg/webrtc/SurfaceTextureHelper;)Z"); | |
| 205 j_reset_method_ = | |
| 206 GetMethodID(jni, *j_media_codec_video_decoder_class_, "reset", "(II)V"); | |
| 207 j_release_method_ = | |
| 208 GetMethodID(jni, *j_media_codec_video_decoder_class_, "release", "()V"); | |
| 209 j_dequeue_input_buffer_method_ = GetMethodID( | |
| 210 jni, *j_media_codec_video_decoder_class_, "dequeueInputBuffer", "()I"); | |
| 211 j_queue_input_buffer_method_ = GetMethodID( | |
| 212 jni, *j_media_codec_video_decoder_class_, "queueInputBuffer", "(IIJJJ)Z"); | |
| 213 j_dequeue_byte_buffer_method_ = GetMethodID( | |
| 214 jni, *j_media_codec_video_decoder_class_, "dequeueOutputBuffer", | |
| 215 "(I)Lorg/webrtc/MediaCodecVideoDecoder$DecodedOutputBuffer;"); | |
| 216 j_dequeue_texture_buffer_method_ = GetMethodID( | |
| 217 jni, *j_media_codec_video_decoder_class_, "dequeueTextureBuffer", | |
| 218 "(I)Lorg/webrtc/MediaCodecVideoDecoder$DecodedTextureBuffer;"); | |
| 219 j_return_decoded_byte_buffer_method_ = | |
| 220 GetMethodID(jni, *j_media_codec_video_decoder_class_, | |
| 221 "returnDecodedOutputBuffer", "(I)V"); | |
| 222 | |
| 223 j_input_buffers_field_ = GetFieldID( | |
| 224 jni, *j_media_codec_video_decoder_class_, | |
| 225 "inputBuffers", "[Ljava/nio/ByteBuffer;"); | |
| 226 j_output_buffers_field_ = GetFieldID( | |
| 227 jni, *j_media_codec_video_decoder_class_, | |
| 228 "outputBuffers", "[Ljava/nio/ByteBuffer;"); | |
| 229 j_color_format_field_ = GetFieldID( | |
| 230 jni, *j_media_codec_video_decoder_class_, "colorFormat", "I"); | |
| 231 j_width_field_ = GetFieldID( | |
| 232 jni, *j_media_codec_video_decoder_class_, "width", "I"); | |
| 233 j_height_field_ = GetFieldID( | |
| 234 jni, *j_media_codec_video_decoder_class_, "height", "I"); | |
| 235 j_stride_field_ = GetFieldID( | |
| 236 jni, *j_media_codec_video_decoder_class_, "stride", "I"); | |
| 237 j_slice_height_field_ = GetFieldID( | |
| 238 jni, *j_media_codec_video_decoder_class_, "sliceHeight", "I"); | |
| 239 | |
| 240 jclass j_decoded_texture_buffer_class = FindClass(jni, | |
| 241 "org/webrtc/MediaCodecVideoDecoder$DecodedTextureBuffer"); | |
| 242 j_texture_id_field_ = GetFieldID( | |
| 243 jni, j_decoded_texture_buffer_class, "textureID", "I"); | |
| 244 j_transform_matrix_field_ = GetFieldID( | |
| 245 jni, j_decoded_texture_buffer_class, "transformMatrix", "[F"); | |
| 246 j_texture_presentation_timestamp_ms_field_ = GetFieldID( | |
| 247 jni, j_decoded_texture_buffer_class, "presentationTimeStampMs", "J"); | |
| 248 j_texture_timestamp_ms_field_ = GetFieldID( | |
| 249 jni, j_decoded_texture_buffer_class, "timeStampMs", "J"); | |
| 250 j_texture_ntp_timestamp_ms_field_ = GetFieldID( | |
| 251 jni, j_decoded_texture_buffer_class, "ntpTimeStampMs", "J"); | |
| 252 j_texture_decode_time_ms_field_ = GetFieldID( | |
| 253 jni, j_decoded_texture_buffer_class, "decodeTimeMs", "J"); | |
| 254 j_texture_frame_delay_ms_field_ = GetFieldID( | |
| 255 jni, j_decoded_texture_buffer_class, "frameDelayMs", "J"); | |
| 256 | |
| 257 jclass j_decoded_output_buffer_class = FindClass(jni, | |
| 258 "org/webrtc/MediaCodecVideoDecoder$DecodedOutputBuffer"); | |
| 259 j_info_index_field_ = GetFieldID( | |
| 260 jni, j_decoded_output_buffer_class, "index", "I"); | |
| 261 j_info_offset_field_ = GetFieldID( | |
| 262 jni, j_decoded_output_buffer_class, "offset", "I"); | |
| 263 j_info_size_field_ = GetFieldID( | |
| 264 jni, j_decoded_output_buffer_class, "size", "I"); | |
| 265 j_presentation_timestamp_ms_field_ = GetFieldID( | |
| 266 jni, j_decoded_output_buffer_class, "presentationTimeStampMs", "J"); | |
| 267 j_timestamp_ms_field_ = GetFieldID( | |
| 268 jni, j_decoded_output_buffer_class, "timeStampMs", "J"); | |
| 269 j_ntp_timestamp_ms_field_ = GetFieldID( | |
| 270 jni, j_decoded_output_buffer_class, "ntpTimeStampMs", "J"); | |
| 271 j_byte_buffer_decode_time_ms_field_ = GetFieldID( | |
| 272 jni, j_decoded_output_buffer_class, "decodeTimeMs", "J"); | |
| 273 | |
| 274 CHECK_EXCEPTION(jni) << "MediaCodecVideoDecoder ctor failed"; | |
| 275 use_surface_ = (render_egl_context_ != NULL); | |
| 276 ALOGD << "MediaCodecVideoDecoder ctor. Use surface: " << use_surface_; | |
| 277 memset(&codec_, 0, sizeof(codec_)); | |
| 278 AllowBlockingCalls(); | |
| 279 } | |
| 280 | |
| 281 MediaCodecVideoDecoder::~MediaCodecVideoDecoder() { | |
| 282 // Call Release() to ensure no more callbacks to us after we are deleted. | |
| 283 Release(); | |
| 284 } | |
| 285 | |
| 286 int32_t MediaCodecVideoDecoder::InitDecode(const VideoCodec* inst, | |
| 287 int32_t numberOfCores) { | |
| 288 ALOGD << "InitDecode."; | |
| 289 if (inst == NULL) { | |
| 290 ALOGE << "NULL VideoCodec instance"; | |
| 291 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | |
| 292 } | |
| 293 // Factory should guard against other codecs being used with us. | |
| 294 RTC_CHECK(inst->codecType == codecType_) | |
| 295 << "Unsupported codec " << inst->codecType << " for " << codecType_; | |
| 296 | |
| 297 if (sw_fallback_required_) { | |
| 298 ALOGE << "InitDecode() - fallback to SW decoder"; | |
| 299 return WEBRTC_VIDEO_CODEC_OK; | |
| 300 } | |
| 301 // Save VideoCodec instance for later. | |
| 302 if (&codec_ != inst) { | |
| 303 codec_ = *inst; | |
| 304 } | |
| 305 // If maxFramerate is not set then assume 30 fps. | |
| 306 codec_.maxFramerate = (codec_.maxFramerate >= 1) ? codec_.maxFramerate : 30; | |
| 307 | |
| 308 // Call Java init. | |
| 309 return codec_thread_->Invoke<int32_t>( | |
| 310 RTC_FROM_HERE, | |
| 311 Bind(&MediaCodecVideoDecoder::InitDecodeOnCodecThread, this)); | |
| 312 } | |
| 313 | |
| 314 void MediaCodecVideoDecoder::ResetVariables() { | |
| 315 CheckOnCodecThread(); | |
| 316 | |
| 317 key_frame_required_ = true; | |
| 318 frames_received_ = 0; | |
| 319 frames_decoded_ = 0; | |
| 320 frames_decoded_logged_ = kMaxDecodedLogFrames; | |
| 321 start_time_ms_ = rtc::TimeMillis(); | |
| 322 current_frames_ = 0; | |
| 323 current_bytes_ = 0; | |
| 324 current_decoding_time_ms_ = 0; | |
| 325 current_delay_time_ms_ = 0; | |
| 326 } | |
| 327 | |
| 328 int32_t MediaCodecVideoDecoder::InitDecodeOnCodecThread() { | |
| 329 CheckOnCodecThread(); | |
| 330 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | |
| 331 ScopedLocalRefFrame local_ref_frame(jni); | |
| 332 ALOGD << "InitDecodeOnCodecThread Type: " << (int)codecType_ << ". " | |
| 333 << codec_.width << " x " << codec_.height << ". Fps: " << | |
| 334 (int)codec_.maxFramerate; | |
| 335 | |
| 336 // Release previous codec first if it was allocated before. | |
| 337 int ret_val = ReleaseOnCodecThread(); | |
| 338 if (ret_val < 0) { | |
| 339 ALOGE << "Release failure: " << ret_val << " - fallback to SW codec"; | |
| 340 sw_fallback_required_ = true; | |
| 341 return WEBRTC_VIDEO_CODEC_ERROR; | |
| 342 } | |
| 343 | |
| 344 ResetVariables(); | |
| 345 | |
| 346 if (use_surface_) { | |
| 347 surface_texture_helper_ = SurfaceTextureHelper::create( | |
| 348 jni, "Decoder SurfaceTextureHelper", render_egl_context_); | |
| 349 if (!surface_texture_helper_) { | |
| 350 ALOGE << "Couldn't create SurfaceTextureHelper - fallback to SW codec"; | |
| 351 sw_fallback_required_ = true; | |
| 352 return WEBRTC_VIDEO_CODEC_ERROR; | |
| 353 } | |
| 354 } | |
| 355 | |
| 356 jobject j_video_codec_enum = JavaEnumFromIndexAndClassName( | |
| 357 jni, "MediaCodecVideoDecoder$VideoCodecType", codecType_); | |
| 358 bool success = jni->CallBooleanMethod( | |
| 359 *j_media_codec_video_decoder_, | |
| 360 j_init_decode_method_, | |
| 361 j_video_codec_enum, | |
| 362 codec_.width, | |
| 363 codec_.height, | |
| 364 use_surface_ ? surface_texture_helper_->GetJavaSurfaceTextureHelper() | |
| 365 : nullptr); | |
| 366 | |
| 367 if (CheckException(jni) || !success) { | |
| 368 ALOGE << "Codec initialization error - fallback to SW codec."; | |
| 369 sw_fallback_required_ = true; | |
| 370 return WEBRTC_VIDEO_CODEC_ERROR; | |
| 371 } | |
| 372 inited_ = true; | |
| 373 | |
| 374 switch (codecType_) { | |
| 375 case kVideoCodecVP8: | |
| 376 max_pending_frames_ = kMaxPendingFramesVp8; | |
| 377 break; | |
| 378 case kVideoCodecVP9: | |
| 379 max_pending_frames_ = kMaxPendingFramesVp9; | |
| 380 break; | |
| 381 case kVideoCodecH264: | |
| 382 max_pending_frames_ = kMaxPendingFramesH264; | |
| 383 break; | |
| 384 default: | |
| 385 max_pending_frames_ = 0; | |
| 386 } | |
| 387 ALOGD << "Maximum amount of pending frames: " << max_pending_frames_; | |
| 388 | |
| 389 jobjectArray input_buffers = (jobjectArray)GetObjectField( | |
| 390 jni, *j_media_codec_video_decoder_, j_input_buffers_field_); | |
| 391 size_t num_input_buffers = jni->GetArrayLength(input_buffers); | |
| 392 input_buffers_.resize(num_input_buffers); | |
| 393 for (size_t i = 0; i < num_input_buffers; ++i) { | |
| 394 input_buffers_[i] = | |
| 395 jni->NewGlobalRef(jni->GetObjectArrayElement(input_buffers, i)); | |
| 396 if (CheckException(jni)) { | |
| 397 ALOGE << "NewGlobalRef error - fallback to SW codec."; | |
| 398 sw_fallback_required_ = true; | |
| 399 return WEBRTC_VIDEO_CODEC_ERROR; | |
| 400 } | |
| 401 } | |
| 402 | |
| 403 codec_thread_->PostDelayed(RTC_FROM_HERE, kMediaCodecPollMs, this); | |
| 404 | |
| 405 return WEBRTC_VIDEO_CODEC_OK; | |
| 406 } | |
| 407 | |
| 408 int32_t MediaCodecVideoDecoder::ResetDecodeOnCodecThread() { | |
| 409 CheckOnCodecThread(); | |
| 410 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | |
| 411 ScopedLocalRefFrame local_ref_frame(jni); | |
| 412 ALOGD << "ResetDecodeOnCodecThread Type: " << (int)codecType_ << ". " | |
| 413 << codec_.width << " x " << codec_.height; | |
| 414 ALOGD << " Frames received: " << frames_received_ << | |
| 415 ". Frames decoded: " << frames_decoded_; | |
| 416 | |
| 417 inited_ = false; | |
| 418 rtc::MessageQueueManager::Clear(this); | |
| 419 ResetVariables(); | |
| 420 | |
| 421 jni->CallVoidMethod( | |
| 422 *j_media_codec_video_decoder_, | |
| 423 j_reset_method_, | |
| 424 codec_.width, | |
| 425 codec_.height); | |
| 426 | |
| 427 if (CheckException(jni)) { | |
| 428 ALOGE << "Soft reset error - fallback to SW codec."; | |
| 429 sw_fallback_required_ = true; | |
| 430 return WEBRTC_VIDEO_CODEC_ERROR; | |
| 431 } | |
| 432 inited_ = true; | |
| 433 | |
| 434 codec_thread_->PostDelayed(RTC_FROM_HERE, kMediaCodecPollMs, this); | |
| 435 | |
| 436 return WEBRTC_VIDEO_CODEC_OK; | |
| 437 } | |
| 438 | |
| 439 int32_t MediaCodecVideoDecoder::Release() { | |
| 440 ALOGD << "DecoderRelease request"; | |
| 441 return codec_thread_->Invoke<int32_t>( | |
| 442 RTC_FROM_HERE, Bind(&MediaCodecVideoDecoder::ReleaseOnCodecThread, this)); | |
| 443 } | |
| 444 | |
| 445 int32_t MediaCodecVideoDecoder::ReleaseOnCodecThread() { | |
| 446 if (!inited_) { | |
| 447 return WEBRTC_VIDEO_CODEC_OK; | |
| 448 } | |
| 449 CheckOnCodecThread(); | |
| 450 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | |
| 451 ALOGD << "DecoderReleaseOnCodecThread: Frames received: " << | |
| 452 frames_received_ << ". Frames decoded: " << frames_decoded_; | |
| 453 ScopedLocalRefFrame local_ref_frame(jni); | |
| 454 for (size_t i = 0; i < input_buffers_.size(); i++) { | |
| 455 jni->DeleteGlobalRef(input_buffers_[i]); | |
| 456 } | |
| 457 input_buffers_.clear(); | |
| 458 jni->CallVoidMethod(*j_media_codec_video_decoder_, j_release_method_); | |
| 459 surface_texture_helper_ = nullptr; | |
| 460 inited_ = false; | |
| 461 rtc::MessageQueueManager::Clear(this); | |
| 462 if (CheckException(jni)) { | |
| 463 ALOGE << "Decoder release exception"; | |
| 464 return WEBRTC_VIDEO_CODEC_ERROR; | |
| 465 } | |
| 466 ALOGD << "DecoderReleaseOnCodecThread done"; | |
| 467 return WEBRTC_VIDEO_CODEC_OK; | |
| 468 } | |
| 469 | |
| 470 void MediaCodecVideoDecoder::CheckOnCodecThread() { | |
| 471 RTC_CHECK(codec_thread_.get() == ThreadManager::Instance()->CurrentThread()) | |
| 472 << "Running on wrong thread!"; | |
| 473 } | |
| 474 | |
| 475 void MediaCodecVideoDecoder::EnableFrameLogOnWarning() { | |
| 476 // Log next 2 output frames. | |
| 477 frames_decoded_logged_ = std::max( | |
| 478 frames_decoded_logged_, frames_decoded_ + kMaxWarningLogFrames); | |
| 479 } | |
| 480 | |
| 481 int32_t MediaCodecVideoDecoder::ProcessHWErrorOnCodecThread() { | |
| 482 CheckOnCodecThread(); | |
| 483 int ret_val = ReleaseOnCodecThread(); | |
| 484 if (ret_val < 0) { | |
| 485 ALOGE << "ProcessHWError: Release failure"; | |
| 486 } | |
| 487 if (codecType_ == kVideoCodecH264) { | |
| 488 // For now there is no SW H.264 which can be used as fallback codec. | |
| 489 // So try to restart hw codec for now. | |
| 490 ret_val = InitDecodeOnCodecThread(); | |
| 491 ALOGE << "Reset H.264 codec done. Status: " << ret_val; | |
| 492 if (ret_val == WEBRTC_VIDEO_CODEC_OK) { | |
| 493 // H.264 codec was succesfully reset - return regular error code. | |
| 494 return WEBRTC_VIDEO_CODEC_ERROR; | |
| 495 } else { | |
| 496 // Fail to restart H.264 codec - return error code which should stop the | |
| 497 // call. | |
| 498 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; | |
| 499 } | |
| 500 } else { | |
| 501 sw_fallback_required_ = true; | |
| 502 ALOGE << "Return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE"; | |
| 503 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; | |
| 504 } | |
| 505 } | |
| 506 | |
| 507 int32_t MediaCodecVideoDecoder::Decode( | |
| 508 const EncodedImage& inputImage, | |
| 509 bool missingFrames, | |
| 510 const RTPFragmentationHeader* fragmentation, | |
| 511 const CodecSpecificInfo* codecSpecificInfo, | |
| 512 int64_t renderTimeMs) { | |
| 513 if (sw_fallback_required_) { | |
| 514 ALOGE << "Decode() - fallback to SW codec"; | |
| 515 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; | |
| 516 } | |
| 517 if (callback_ == NULL) { | |
| 518 ALOGE << "Decode() - callback_ is NULL"; | |
| 519 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | |
| 520 } | |
| 521 if (inputImage._buffer == NULL && inputImage._length > 0) { | |
| 522 ALOGE << "Decode() - inputImage is incorrect"; | |
| 523 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | |
| 524 } | |
| 525 if (!inited_) { | |
| 526 ALOGE << "Decode() - decoder is not initialized"; | |
| 527 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | |
| 528 } | |
| 529 | |
| 530 // Check if encoded frame dimension has changed. | |
| 531 if ((inputImage._encodedWidth * inputImage._encodedHeight > 0) && | |
| 532 (inputImage._encodedWidth != codec_.width || | |
| 533 inputImage._encodedHeight != codec_.height)) { | |
| 534 ALOGW << "Input resolution changed from " << | |
| 535 codec_.width << " x " << codec_.height << " to " << | |
| 536 inputImage._encodedWidth << " x " << inputImage._encodedHeight; | |
| 537 codec_.width = inputImage._encodedWidth; | |
| 538 codec_.height = inputImage._encodedHeight; | |
| 539 int32_t ret; | |
| 540 if (use_surface_ && | |
| 541 (codecType_ == kVideoCodecVP8 || codecType_ == kVideoCodecH264)) { | |
| 542 // Soft codec reset - only for surface decoding. | |
| 543 ret = codec_thread_->Invoke<int32_t>( | |
| 544 RTC_FROM_HERE, | |
| 545 Bind(&MediaCodecVideoDecoder::ResetDecodeOnCodecThread, this)); | |
| 546 } else { | |
| 547 // Hard codec reset. | |
| 548 ret = InitDecode(&codec_, 1); | |
| 549 } | |
| 550 if (ret < 0) { | |
| 551 ALOGE << "InitDecode failure: " << ret << " - fallback to SW codec"; | |
| 552 sw_fallback_required_ = true; | |
| 553 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; | |
| 554 } | |
| 555 } | |
| 556 | |
| 557 // Always start with a complete key frame. | |
| 558 if (key_frame_required_) { | |
| 559 if (inputImage._frameType != webrtc::kVideoFrameKey) { | |
| 560 ALOGE << "Decode() - key frame is required"; | |
| 561 return WEBRTC_VIDEO_CODEC_ERROR; | |
| 562 } | |
| 563 if (!inputImage._completeFrame) { | |
| 564 ALOGE << "Decode() - complete frame is required"; | |
| 565 return WEBRTC_VIDEO_CODEC_ERROR; | |
| 566 } | |
| 567 key_frame_required_ = false; | |
| 568 } | |
| 569 if (inputImage._length == 0) { | |
| 570 return WEBRTC_VIDEO_CODEC_ERROR; | |
| 571 } | |
| 572 | |
| 573 return codec_thread_->Invoke<int32_t>( | |
| 574 RTC_FROM_HERE, | |
| 575 Bind(&MediaCodecVideoDecoder::DecodeOnCodecThread, this, inputImage)); | |
| 576 } | |
| 577 | |
| 578 int32_t MediaCodecVideoDecoder::DecodeOnCodecThread( | |
| 579 const EncodedImage& inputImage) { | |
| 580 CheckOnCodecThread(); | |
| 581 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | |
| 582 ScopedLocalRefFrame local_ref_frame(jni); | |
| 583 | |
| 584 // Try to drain the decoder and wait until output is not too | |
| 585 // much behind the input. | |
| 586 if (codecType_ == kVideoCodecH264 && | |
| 587 frames_received_ > frames_decoded_ + max_pending_frames_) { | |
| 588 // Print warning for H.264 only - for VP8/VP9 one frame delay is ok. | |
| 589 ALOGW << "Decoder is too far behind. Try to drain. Received: " << | |
| 590 frames_received_ << ". Decoded: " << frames_decoded_; | |
| 591 EnableFrameLogOnWarning(); | |
| 592 } | |
| 593 const int64 drain_start = rtc::TimeMillis(); | |
| 594 while ((frames_received_ > frames_decoded_ + max_pending_frames_) && | |
| 595 (rtc::TimeMillis() - drain_start) < kMediaCodecTimeoutMs) { | |
| 596 if (!DeliverPendingOutputs(jni, kMediaCodecPollMs)) { | |
| 597 ALOGE << "DeliverPendingOutputs error. Frames received: " << | |
| 598 frames_received_ << ". Frames decoded: " << frames_decoded_; | |
| 599 return ProcessHWErrorOnCodecThread(); | |
| 600 } | |
| 601 } | |
| 602 if (frames_received_ > frames_decoded_ + max_pending_frames_) { | |
| 603 ALOGE << "Output buffer dequeue timeout. Frames received: " << | |
| 604 frames_received_ << ". Frames decoded: " << frames_decoded_; | |
| 605 return ProcessHWErrorOnCodecThread(); | |
| 606 } | |
| 607 | |
| 608 // Get input buffer. | |
| 609 int j_input_buffer_index = jni->CallIntMethod( | |
| 610 *j_media_codec_video_decoder_, j_dequeue_input_buffer_method_); | |
| 611 if (CheckException(jni) || j_input_buffer_index < 0) { | |
| 612 ALOGE << "dequeueInputBuffer error: " << j_input_buffer_index << | |
| 613 ". Retry DeliverPendingOutputs."; | |
| 614 EnableFrameLogOnWarning(); | |
| 615 // Try to drain the decoder. | |
| 616 if (!DeliverPendingOutputs(jni, kMediaCodecPollMs)) { | |
| 617 ALOGE << "DeliverPendingOutputs error. Frames received: " << | |
| 618 frames_received_ << ". Frames decoded: " << frames_decoded_; | |
| 619 return ProcessHWErrorOnCodecThread(); | |
| 620 } | |
| 621 // Try dequeue input buffer one last time. | |
| 622 j_input_buffer_index = jni->CallIntMethod( | |
| 623 *j_media_codec_video_decoder_, j_dequeue_input_buffer_method_); | |
| 624 if (CheckException(jni) || j_input_buffer_index < 0) { | |
| 625 ALOGE << "dequeueInputBuffer critical error: " << j_input_buffer_index; | |
| 626 return ProcessHWErrorOnCodecThread(); | |
| 627 } | |
| 628 } | |
| 629 | |
| 630 // Copy encoded data to Java ByteBuffer. | |
| 631 jobject j_input_buffer = input_buffers_[j_input_buffer_index]; | |
| 632 uint8_t* buffer = | |
| 633 reinterpret_cast<uint8_t*>(jni->GetDirectBufferAddress(j_input_buffer)); | |
| 634 RTC_CHECK(buffer) << "Indirect buffer??"; | |
| 635 int64_t buffer_capacity = jni->GetDirectBufferCapacity(j_input_buffer); | |
| 636 if (CheckException(jni) || buffer_capacity < inputImage._length) { | |
| 637 ALOGE << "Input frame size "<< inputImage._length << | |
| 638 " is bigger than buffer size " << buffer_capacity; | |
| 639 return ProcessHWErrorOnCodecThread(); | |
| 640 } | |
| 641 jlong presentation_timestamp_us = static_cast<jlong>( | |
| 642 static_cast<int64_t>(frames_received_) * 1000000 / codec_.maxFramerate); | |
| 643 memcpy(buffer, inputImage._buffer, inputImage._length); | |
| 644 | |
| 645 if (frames_decoded_ < frames_decoded_logged_) { | |
| 646 ALOGD << "Decoder frame in # " << frames_received_ << | |
| 647 ". Type: " << inputImage._frameType << | |
| 648 ". Buffer # " << j_input_buffer_index << | |
| 649 ". TS: " << presentation_timestamp_us / 1000 << | |
| 650 ". Size: " << inputImage._length; | |
| 651 } | |
| 652 | |
| 653 // Save input image timestamps for later output. | |
| 654 frames_received_++; | |
| 655 current_bytes_ += inputImage._length; | |
| 656 | |
| 657 // Feed input to decoder. | |
| 658 bool success = jni->CallBooleanMethod( | |
| 659 *j_media_codec_video_decoder_, | |
| 660 j_queue_input_buffer_method_, | |
| 661 j_input_buffer_index, | |
| 662 inputImage._length, | |
| 663 presentation_timestamp_us, | |
| 664 static_cast<int64_t> (inputImage._timeStamp), | |
| 665 inputImage.ntp_time_ms_); | |
| 666 if (CheckException(jni) || !success) { | |
| 667 ALOGE << "queueInputBuffer error"; | |
| 668 return ProcessHWErrorOnCodecThread(); | |
| 669 } | |
| 670 | |
| 671 // Try to drain the decoder | |
| 672 if (!DeliverPendingOutputs(jni, 0)) { | |
| 673 ALOGE << "DeliverPendingOutputs error"; | |
| 674 return ProcessHWErrorOnCodecThread(); | |
| 675 } | |
| 676 | |
| 677 return WEBRTC_VIDEO_CODEC_OK; | |
| 678 } | |
| 679 | |
| 680 bool MediaCodecVideoDecoder::DeliverPendingOutputs( | |
| 681 JNIEnv* jni, int dequeue_timeout_ms) { | |
| 682 if (frames_received_ <= frames_decoded_) { | |
| 683 // No need to query for output buffers - decoder is drained. | |
| 684 return true; | |
| 685 } | |
| 686 // Get decoder output. | |
| 687 jobject j_decoder_output_buffer = | |
| 688 jni->CallObjectMethod(*j_media_codec_video_decoder_, | |
| 689 use_surface_ ? j_dequeue_texture_buffer_method_ | |
| 690 : j_dequeue_byte_buffer_method_, | |
| 691 dequeue_timeout_ms); | |
| 692 | |
| 693 if (CheckException(jni)) { | |
| 694 ALOGE << "dequeueOutputBuffer() error"; | |
| 695 return false; | |
| 696 } | |
| 697 if (IsNull(jni, j_decoder_output_buffer)) { | |
| 698 // No decoded frame ready. | |
| 699 return true; | |
| 700 } | |
| 701 | |
| 702 // Get decoded video frame properties. | |
| 703 int color_format = GetIntField(jni, *j_media_codec_video_decoder_, | |
| 704 j_color_format_field_); | |
| 705 int width = GetIntField(jni, *j_media_codec_video_decoder_, j_width_field_); | |
| 706 int height = GetIntField(jni, *j_media_codec_video_decoder_, j_height_field_); | |
| 707 int stride = GetIntField(jni, *j_media_codec_video_decoder_, j_stride_field_); | |
| 708 int slice_height = GetIntField(jni, *j_media_codec_video_decoder_, | |
| 709 j_slice_height_field_); | |
| 710 | |
| 711 rtc::scoped_refptr<webrtc::VideoFrameBuffer> frame_buffer; | |
| 712 int64_t presentation_timestamps_ms = 0; | |
| 713 int64_t output_timestamps_ms = 0; | |
| 714 int64_t output_ntp_timestamps_ms = 0; | |
| 715 int decode_time_ms = 0; | |
| 716 int64_t frame_delayed_ms = 0; | |
| 717 if (use_surface_) { | |
| 718 // Extract data from Java DecodedTextureBuffer. | |
| 719 presentation_timestamps_ms = GetLongField( | |
| 720 jni, j_decoder_output_buffer, | |
| 721 j_texture_presentation_timestamp_ms_field_); | |
| 722 output_timestamps_ms = GetLongField( | |
| 723 jni, j_decoder_output_buffer, j_texture_timestamp_ms_field_); | |
| 724 output_ntp_timestamps_ms = GetLongField( | |
| 725 jni, j_decoder_output_buffer, j_texture_ntp_timestamp_ms_field_); | |
| 726 decode_time_ms = GetLongField( | |
| 727 jni, j_decoder_output_buffer, j_texture_decode_time_ms_field_); | |
| 728 | |
| 729 const int texture_id = | |
| 730 GetIntField(jni, j_decoder_output_buffer, j_texture_id_field_); | |
| 731 if (texture_id != 0) { // |texture_id| == 0 represents a dropped frame. | |
| 732 const jfloatArray j_transform_matrix = | |
| 733 reinterpret_cast<jfloatArray>(GetObjectField( | |
| 734 jni, j_decoder_output_buffer, j_transform_matrix_field_)); | |
| 735 frame_delayed_ms = GetLongField( | |
| 736 jni, j_decoder_output_buffer, j_texture_frame_delay_ms_field_); | |
| 737 | |
| 738 // Create webrtc::VideoFrameBuffer with native texture handle. | |
| 739 frame_buffer = surface_texture_helper_->CreateTextureFrame( | |
| 740 width, height, NativeHandleImpl(jni, texture_id, j_transform_matrix)); | |
| 741 } else { | |
| 742 EnableFrameLogOnWarning(); | |
| 743 } | |
| 744 } else { | |
| 745 // Extract data from Java ByteBuffer and create output yuv420 frame - | |
| 746 // for non surface decoding only. | |
| 747 const int output_buffer_index = GetIntField( | |
| 748 jni, j_decoder_output_buffer, j_info_index_field_); | |
| 749 const int output_buffer_offset = GetIntField( | |
| 750 jni, j_decoder_output_buffer, j_info_offset_field_); | |
| 751 const int output_buffer_size = GetIntField( | |
| 752 jni, j_decoder_output_buffer, j_info_size_field_); | |
| 753 presentation_timestamps_ms = GetLongField( | |
| 754 jni, j_decoder_output_buffer, j_presentation_timestamp_ms_field_); | |
| 755 output_timestamps_ms = GetLongField( | |
| 756 jni, j_decoder_output_buffer, j_timestamp_ms_field_); | |
| 757 output_ntp_timestamps_ms = GetLongField( | |
| 758 jni, j_decoder_output_buffer, j_ntp_timestamp_ms_field_); | |
| 759 | |
| 760 decode_time_ms = GetLongField(jni, j_decoder_output_buffer, | |
| 761 j_byte_buffer_decode_time_ms_field_); | |
| 762 | |
| 763 if (output_buffer_size < width * height * 3 / 2) { | |
| 764 ALOGE << "Insufficient output buffer size: " << output_buffer_size; | |
| 765 return false; | |
| 766 } | |
| 767 if (output_buffer_size < stride * height * 3 / 2 && | |
| 768 slice_height == height && stride > width) { | |
| 769 // Some codecs (Exynos) incorrectly report stride information for | |
| 770 // output byte buffer, so actual stride value need to be corrected. | |
| 771 stride = output_buffer_size * 2 / (height * 3); | |
| 772 } | |
| 773 jobjectArray output_buffers = reinterpret_cast<jobjectArray>(GetObjectField( | |
| 774 jni, *j_media_codec_video_decoder_, j_output_buffers_field_)); | |
| 775 jobject output_buffer = | |
| 776 jni->GetObjectArrayElement(output_buffers, output_buffer_index); | |
| 777 uint8_t* payload = reinterpret_cast<uint8_t*>(jni->GetDirectBufferAddress( | |
| 778 output_buffer)); | |
| 779 if (CheckException(jni)) { | |
| 780 return false; | |
| 781 } | |
| 782 payload += output_buffer_offset; | |
| 783 | |
| 784 // Create yuv420 frame. | |
| 785 rtc::scoped_refptr<webrtc::I420Buffer> i420_buffer; | |
| 786 | |
| 787 i420_buffer = decoded_frame_pool_.CreateBuffer(width, height); | |
| 788 if (color_format == COLOR_FormatYUV420Planar) { | |
| 789 RTC_CHECK_EQ(0, stride % 2); | |
| 790 RTC_CHECK_EQ(0, slice_height % 2); | |
| 791 const int uv_stride = stride / 2; | |
| 792 const int u_slice_height = slice_height / 2; | |
| 793 const uint8_t* y_ptr = payload; | |
| 794 const uint8_t* u_ptr = y_ptr + stride * slice_height; | |
| 795 const uint8_t* v_ptr = u_ptr + uv_stride * u_slice_height; | |
| 796 libyuv::I420Copy(y_ptr, stride, u_ptr, uv_stride, v_ptr, uv_stride, | |
| 797 i420_buffer->MutableDataY(), i420_buffer->StrideY(), | |
| 798 i420_buffer->MutableDataU(), i420_buffer->StrideU(), | |
| 799 i420_buffer->MutableDataV(), i420_buffer->StrideV(), | |
| 800 width, height); | |
| 801 } else { | |
| 802 // All other supported formats are nv12. | |
| 803 const uint8_t* y_ptr = payload; | |
| 804 const uint8_t* uv_ptr = y_ptr + stride * slice_height; | |
| 805 libyuv::NV12ToI420(y_ptr, stride, uv_ptr, stride, | |
| 806 i420_buffer->MutableDataY(), i420_buffer->StrideY(), | |
| 807 i420_buffer->MutableDataU(), i420_buffer->StrideU(), | |
| 808 i420_buffer->MutableDataV(), i420_buffer->StrideV(), | |
| 809 width, height); | |
| 810 } | |
| 811 frame_buffer = i420_buffer; | |
| 812 | |
| 813 // Return output byte buffer back to codec. | |
| 814 jni->CallVoidMethod( | |
| 815 *j_media_codec_video_decoder_, | |
| 816 j_return_decoded_byte_buffer_method_, | |
| 817 output_buffer_index); | |
| 818 if (CheckException(jni)) { | |
| 819 ALOGE << "returnDecodedOutputBuffer error"; | |
| 820 return false; | |
| 821 } | |
| 822 } | |
| 823 if (frames_decoded_ < frames_decoded_logged_) { | |
| 824 ALOGD << "Decoder frame out # " << frames_decoded_ << | |
| 825 ". " << width << " x " << height << | |
| 826 ". " << stride << " x " << slice_height << | |
| 827 ". Color: " << color_format << | |
| 828 ". TS: " << presentation_timestamps_ms << | |
| 829 ". DecTime: " << (int)decode_time_ms << | |
| 830 ". DelayTime: " << (int)frame_delayed_ms; | |
| 831 } | |
| 832 | |
| 833 // Calculate and print decoding statistics - every 3 seconds. | |
| 834 frames_decoded_++; | |
| 835 current_frames_++; | |
| 836 current_decoding_time_ms_ += decode_time_ms; | |
| 837 current_delay_time_ms_ += frame_delayed_ms; | |
| 838 int statistic_time_ms = rtc::TimeMillis() - start_time_ms_; | |
| 839 if (statistic_time_ms >= kMediaCodecStatisticsIntervalMs && | |
| 840 current_frames_ > 0) { | |
| 841 int current_bitrate = current_bytes_ * 8 / statistic_time_ms; | |
| 842 int current_fps = | |
| 843 (current_frames_ * 1000 + statistic_time_ms / 2) / statistic_time_ms; | |
| 844 ALOGD << "Frames decoded: " << frames_decoded_ << | |
| 845 ". Received: " << frames_received_ << | |
| 846 ". Bitrate: " << current_bitrate << " kbps" << | |
| 847 ". Fps: " << current_fps << | |
| 848 ". DecTime: " << (current_decoding_time_ms_ / current_frames_) << | |
| 849 ". DelayTime: " << (current_delay_time_ms_ / current_frames_) << | |
| 850 " for last " << statistic_time_ms << " ms."; | |
| 851 start_time_ms_ = rtc::TimeMillis(); | |
| 852 current_frames_ = 0; | |
| 853 current_bytes_ = 0; | |
| 854 current_decoding_time_ms_ = 0; | |
| 855 current_delay_time_ms_ = 0; | |
| 856 } | |
| 857 | |
| 858 // If the frame was dropped, frame_buffer is left as nullptr. | |
| 859 if (frame_buffer) { | |
| 860 VideoFrame decoded_frame(frame_buffer, 0, 0, webrtc::kVideoRotation_0); | |
| 861 decoded_frame.set_timestamp(output_timestamps_ms); | |
| 862 decoded_frame.set_ntp_time_ms(output_ntp_timestamps_ms); | |
| 863 | |
| 864 const int32_t callback_status = | |
| 865 callback_->Decoded(decoded_frame, decode_time_ms); | |
| 866 if (callback_status > 0) { | |
| 867 ALOGE << "callback error"; | |
| 868 } | |
| 869 } | |
| 870 return true; | |
| 871 } | |
| 872 | |
| 873 int32_t MediaCodecVideoDecoder::RegisterDecodeCompleteCallback( | |
| 874 DecodedImageCallback* callback) { | |
| 875 callback_ = callback; | |
| 876 return WEBRTC_VIDEO_CODEC_OK; | |
| 877 } | |
| 878 | |
| 879 void MediaCodecVideoDecoder::OnMessage(rtc::Message* msg) { | |
| 880 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | |
| 881 ScopedLocalRefFrame local_ref_frame(jni); | |
| 882 if (!inited_) { | |
| 883 return; | |
| 884 } | |
| 885 // We only ever send one message to |this| directly (not through a Bind()'d | |
| 886 // functor), so expect no ID/data. | |
| 887 RTC_CHECK(!msg->message_id) << "Unexpected message!"; | |
| 888 RTC_CHECK(!msg->pdata) << "Unexpected message!"; | |
| 889 CheckOnCodecThread(); | |
| 890 | |
| 891 if (!DeliverPendingOutputs(jni, 0)) { | |
| 892 ALOGE << "OnMessage: DeliverPendingOutputs error"; | |
| 893 ProcessHWErrorOnCodecThread(); | |
| 894 return; | |
| 895 } | |
| 896 codec_thread_->PostDelayed(RTC_FROM_HERE, kMediaCodecPollMs, this); | |
| 897 } | |
| 898 | |
| 899 MediaCodecVideoDecoderFactory::MediaCodecVideoDecoderFactory() | |
| 900 : egl_context_(nullptr) { | |
| 901 ALOGD << "MediaCodecVideoDecoderFactory ctor"; | |
| 902 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | |
| 903 ScopedLocalRefFrame local_ref_frame(jni); | |
| 904 jclass j_decoder_class = FindClass(jni, "org/webrtc/MediaCodecVideoDecoder"); | |
| 905 supported_codec_types_.clear(); | |
| 906 | |
| 907 bool is_vp8_hw_supported = jni->CallStaticBooleanMethod( | |
| 908 j_decoder_class, | |
| 909 GetStaticMethodID(jni, j_decoder_class, "isVp8HwSupported", "()Z")); | |
| 910 if (CheckException(jni)) { | |
| 911 is_vp8_hw_supported = false; | |
| 912 } | |
| 913 if (is_vp8_hw_supported) { | |
| 914 ALOGD << "VP8 HW Decoder supported."; | |
| 915 supported_codec_types_.push_back(kVideoCodecVP8); | |
| 916 } | |
| 917 | |
| 918 bool is_vp9_hw_supported = jni->CallStaticBooleanMethod( | |
| 919 j_decoder_class, | |
| 920 GetStaticMethodID(jni, j_decoder_class, "isVp9HwSupported", "()Z")); | |
| 921 if (CheckException(jni)) { | |
| 922 is_vp9_hw_supported = false; | |
| 923 } | |
| 924 if (is_vp9_hw_supported) { | |
| 925 ALOGD << "VP9 HW Decoder supported."; | |
| 926 supported_codec_types_.push_back(kVideoCodecVP9); | |
| 927 } | |
| 928 | |
| 929 bool is_h264_hw_supported = jni->CallStaticBooleanMethod( | |
| 930 j_decoder_class, | |
| 931 GetStaticMethodID(jni, j_decoder_class, "isH264HwSupported", "()Z")); | |
| 932 if (CheckException(jni)) { | |
| 933 is_h264_hw_supported = false; | |
| 934 } | |
| 935 if (is_h264_hw_supported) { | |
| 936 ALOGD << "H264 HW Decoder supported."; | |
| 937 supported_codec_types_.push_back(kVideoCodecH264); | |
| 938 } | |
| 939 } | |
| 940 | |
| 941 MediaCodecVideoDecoderFactory::~MediaCodecVideoDecoderFactory() { | |
| 942 ALOGD << "MediaCodecVideoDecoderFactory dtor"; | |
| 943 if (egl_context_) { | |
| 944 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | |
| 945 jni->DeleteGlobalRef(egl_context_); | |
| 946 } | |
| 947 } | |
| 948 | |
| 949 void MediaCodecVideoDecoderFactory::SetEGLContext( | |
| 950 JNIEnv* jni, jobject egl_context) { | |
| 951 ALOGD << "MediaCodecVideoDecoderFactory::SetEGLContext"; | |
| 952 if (egl_context_) { | |
| 953 jni->DeleteGlobalRef(egl_context_); | |
| 954 egl_context_ = nullptr; | |
| 955 } | |
| 956 egl_context_ = jni->NewGlobalRef(egl_context); | |
| 957 if (CheckException(jni)) { | |
| 958 ALOGE << "error calling NewGlobalRef for EGL Context."; | |
| 959 } | |
| 960 } | |
| 961 | |
| 962 webrtc::VideoDecoder* MediaCodecVideoDecoderFactory::CreateVideoDecoder( | |
| 963 VideoCodecType type) { | |
| 964 if (supported_codec_types_.empty()) { | |
| 965 ALOGW << "No HW video decoder for type " << (int)type; | |
| 966 return nullptr; | |
| 967 } | |
| 968 for (VideoCodecType codec_type : supported_codec_types_) { | |
| 969 if (codec_type == type) { | |
| 970 ALOGD << "Create HW video decoder for type " << (int)type; | |
| 971 return new MediaCodecVideoDecoder(AttachCurrentThreadIfNeeded(), type, | |
| 972 egl_context_); | |
| 973 } | |
| 974 } | |
| 975 ALOGW << "Can not find HW video decoder for type " << (int)type; | |
| 976 return nullptr; | |
| 977 } | |
| 978 | |
| 979 void MediaCodecVideoDecoderFactory::DestroyVideoDecoder( | |
| 980 webrtc::VideoDecoder* decoder) { | |
| 981 ALOGD << "Destroy video decoder."; | |
| 982 delete decoder; | |
| 983 } | |
| 984 | |
| 985 const char* MediaCodecVideoDecoder::ImplementationName() const { | |
| 986 return "MediaCodec"; | |
| 987 } | |
| 988 | |
| 989 } // namespace webrtc_jni | |
| OLD | NEW |