Chromium Code Reviews| Index: webrtc/api/android/jni/androidmediaencoder_jni.cc |
| diff --git a/webrtc/api/android/jni/androidmediaencoder_jni.cc b/webrtc/api/android/jni/androidmediaencoder_jni.cc |
| index 8d0d3b54bd512557401dcf652b94d62d930388f4..bdd308ce93a8e38ac7895dadbb6a62c69dc968bc 100644 |
| --- a/webrtc/api/android/jni/androidmediaencoder_jni.cc |
| +++ b/webrtc/api/android/jni/androidmediaencoder_jni.cc |
| @@ -126,11 +126,6 @@ class MediaCodecVideoEncoder : public webrtc::VideoEncoder, |
| const char* ImplementationName() const override; |
| private: |
| - // ResetCodecOnCodecThread() calls ReleaseOnCodecThread() and |
| - // InitEncodeOnCodecThread() in an attempt to restore the codec to an |
| - // operable state. Necessary after all manner of OMX-layer errors. |
| - bool ResetCodecOnCodecThread(); |
| - |
| // Implementation of webrtc::VideoEncoder methods above, all running on the |
| // codec thread exclusively. |
| // |
| @@ -282,6 +277,8 @@ class MediaCodecVideoEncoder : public webrtc::VideoEncoder, |
| int64_t last_frame_received_ms_; |
| int frames_received_since_last_key_; |
| webrtc::VideoCodecMode codec_mode_; |
| + |
| + bool sw_fallback_required_; |
| }; |
| MediaCodecVideoEncoder::~MediaCodecVideoEncoder() { |
| @@ -289,25 +286,27 @@ MediaCodecVideoEncoder::~MediaCodecVideoEncoder() { |
| Release(); |
| } |
| -MediaCodecVideoEncoder::MediaCodecVideoEncoder( |
| - JNIEnv* jni, VideoCodecType codecType, jobject egl_context) : |
| - codecType_(codecType), |
| - callback_(NULL), |
| - codec_thread_(new Thread()), |
| - j_media_codec_video_encoder_class_( |
| - jni, |
| - FindClass(jni, "org/webrtc/MediaCodecVideoEncoder")), |
| - j_media_codec_video_encoder_( |
| - jni, |
| - jni->NewObject(*j_media_codec_video_encoder_class_, |
| - GetMethodID(jni, |
| - *j_media_codec_video_encoder_class_, |
| - "<init>", |
| - "()V"))), |
| - inited_(false), |
| - use_surface_(false), |
| - picture_id_(0), |
| - egl_context_(egl_context) { |
| +MediaCodecVideoEncoder::MediaCodecVideoEncoder(JNIEnv* jni, |
| + VideoCodecType codecType, |
| + jobject egl_context) |
| + : codecType_(codecType), |
| + callback_(NULL), |
| + codec_thread_(new Thread()), |
| + j_media_codec_video_encoder_class_( |
| + jni, |
| + FindClass(jni, "org/webrtc/MediaCodecVideoEncoder")), |
| + j_media_codec_video_encoder_( |
| + jni, |
| + jni->NewObject(*j_media_codec_video_encoder_class_, |
| + GetMethodID(jni, |
| + *j_media_codec_video_encoder_class_, |
| + "<init>", |
| + "()V"))), |
| + inited_(false), |
| + use_surface_(false), |
| + picture_id_(0), |
| + egl_context_(egl_context), |
| + sw_fallback_required_(false) { |
| ScopedLocalRefFrame local_ref_frame(jni); |
| // It would be nice to avoid spinning up a new thread per MediaCodec, and |
| // instead re-use e.g. the PeerConnectionFactory's |worker_thread_|, but bug |
| @@ -361,7 +360,10 @@ MediaCodecVideoEncoder::MediaCodecVideoEncoder( |
| GetFieldID(jni, j_output_buffer_info_class, "isKeyFrame", "Z"); |
| j_info_presentation_timestamp_us_field_ = GetFieldID( |
| jni, j_output_buffer_info_class, "presentationTimestampUs", "J"); |
| - CHECK_EXCEPTION(jni) << "MediaCodecVideoEncoder ctor failed"; |
| + if (CheckException(jni)) { |
| + ALOGW << "MediaCodecVideoEncoder ctor failed. Falling back to SW encoder."; |
| + sw_fallback_required_ = true; |
| + } |
| srand(time(NULL)); |
| AllowBlockingCalls(); |
| } |
| @@ -491,23 +493,12 @@ void MediaCodecVideoEncoder::OnMessage(rtc::Message* msg) { |
| LogStatistics(false); |
| } |
| -bool MediaCodecVideoEncoder::ResetCodecOnCodecThread() { |
| - RTC_DCHECK(codec_thread_checker_.CalledOnValidThread()); |
| - ALOGE << "ResetOnCodecThread"; |
| - if (ReleaseOnCodecThread() != WEBRTC_VIDEO_CODEC_OK || |
| - InitEncodeOnCodecThread(width_, height_, 0, 0, false) != |
| - WEBRTC_VIDEO_CODEC_OK) { |
| - // TODO(fischman): wouldn't it be nice if there was a way to gracefully |
| - // degrade to a SW encoder at this point? There isn't one AFAICT :( |
| - // https://code.google.com/p/webrtc/issues/detail?id=2920 |
| - return false; |
| - } |
| - return true; |
| -} |
| - |
| int32_t MediaCodecVideoEncoder::InitEncodeOnCodecThread( |
| int width, int height, int kbps, int fps, bool use_surface) { |
| RTC_DCHECK(codec_thread_checker_.CalledOnValidThread()); |
| + if (sw_fallback_required_) { |
| + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
| + } |
| RTC_CHECK(!use_surface || egl_context_ != nullptr) << "EGL context not set."; |
| JNIEnv* jni = AttachCurrentThreadIfNeeded(); |
| ScopedLocalRefFrame local_ref_frame(jni); |
| @@ -562,13 +553,22 @@ int32_t MediaCodecVideoEncoder::InitEncodeOnCodecThread( |
| ALOGE << "Failed to configure encoder."; |
| return WEBRTC_VIDEO_CODEC_ERROR; |
|
AlexG
2016/08/31 00:25:57
fallback to sw encoder here. Still return error, b
sakal
2016/09/02 08:08:00
Done.
|
| } |
| - CHECK_EXCEPTION(jni); |
| + if (CheckException(jni)) { |
| + ALOGE << "Exception in init encode. Falling back to SW encoder."; |
| + sw_fallback_required_ = true; |
| + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
|
AlexG
2016/08/31 00:25:58
return WEBRTC_VIDEO_CODEC_ERROR? In decoder WEBRT
sakal
2016/09/02 08:08:00
Done.
|
| + } |
| if (!use_surface) { |
| jobjectArray input_buffers = reinterpret_cast<jobjectArray>( |
| jni->CallObjectMethod(*j_media_codec_video_encoder_, |
| j_get_input_buffers_method_)); |
| - CHECK_EXCEPTION(jni); |
| + if (CheckException(jni)) { |
| + ALOGE << "Exception in get input buffers. Falling back to SW encoder."; |
| + sw_fallback_required_ = true; |
| + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
| + } |
| + |
| if (IsNull(jni, input_buffers)) { |
| return WEBRTC_VIDEO_CODEC_ERROR; |
|
AlexG
2016/08/31 00:25:57
set sw_fallback_required_ = true
sakal
2016/09/02 08:08:01
Done.
|
| } |
| @@ -596,7 +596,12 @@ int32_t MediaCodecVideoEncoder::InitEncodeOnCodecThread( |
| jni->NewGlobalRef(jni->GetObjectArrayElement(input_buffers, i)); |
| int64_t yuv_buffer_capacity = |
| jni->GetDirectBufferCapacity(input_buffers_[i]); |
| - CHECK_EXCEPTION(jni); |
| + if (CheckException(jni)) { |
| + ALOGE << "Exception in get direct buffer capacity. Falling back to SW " |
| + "encoder."; |
| + sw_fallback_required_ = true; |
| + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
| + } |
| RTC_CHECK(yuv_buffer_capacity >= yuv_size_) << "Insufficient capacity"; |
| } |
| } |
| @@ -610,6 +615,9 @@ int32_t MediaCodecVideoEncoder::EncodeOnCodecThread( |
| const std::vector<webrtc::FrameType>* frame_types, |
| const int64_t frame_input_time_ms) { |
| RTC_DCHECK(codec_thread_checker_.CalledOnValidThread()); |
| + if (sw_fallback_required_) { |
| + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
| + } |
| JNIEnv* jni = AttachCurrentThreadIfNeeded(); |
| ScopedLocalRefFrame local_ref_frame(jni); |
| @@ -636,8 +644,8 @@ int32_t MediaCodecVideoEncoder::EncodeOnCodecThread( |
| frames_received_++; |
| if (!DeliverPendingOutputs(jni)) { |
| - if (!ResetCodecOnCodecThread()) |
| - return WEBRTC_VIDEO_CODEC_ERROR; |
| + sw_fallback_required_ = true; |
| + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
| } |
| if (frames_encoded_ < kMaxEncodedLogFrames) { |
| ALOGD << "Encoder frame in # " << (frames_received_ - 1) |
| @@ -670,8 +678,8 @@ int32_t MediaCodecVideoEncoder::EncodeOnCodecThread( |
| if (consecutive_full_queue_frame_drops_ >= |
| ENCODER_STALL_FRAMEDROP_THRESHOLD) { |
| ALOGE << "Encoder got stuck. Reset."; |
| - ResetCodecOnCodecThread(); |
| - return WEBRTC_VIDEO_CODEC_ERROR; |
| + sw_fallback_required_ = true; |
| + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
| } |
| frames_dropped_media_encoder_++; |
| OnDroppedFrameOnCodecThread(); |
| @@ -715,7 +723,11 @@ int32_t MediaCodecVideoEncoder::EncodeOnCodecThread( |
| if (!input_frame.video_frame_buffer()->native_handle()) { |
| int j_input_buffer_index = jni->CallIntMethod(*j_media_codec_video_encoder_, |
| j_dequeue_input_buffer_method_); |
| - CHECK_EXCEPTION(jni); |
| + if (CheckException(jni)) { |
| + ALOGE << "Exception in dequeu input buffer. Falling back to SW encoder."; |
| + sw_fallback_required_ = true; |
| + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
| + } |
| if (j_input_buffer_index == -1) { |
| // Video codec falls behind - no input buffer available. |
| ALOGW << "Encoder drop frame - no input buffers available"; |
| @@ -731,8 +743,8 @@ int32_t MediaCodecVideoEncoder::EncodeOnCodecThread( |
| } |
| return WEBRTC_VIDEO_CODEC_OK; // TODO(fischman): see webrtc bug 2887. |
| } else if (j_input_buffer_index == -2) { |
| - ResetCodecOnCodecThread(); |
| - return WEBRTC_VIDEO_CODEC_ERROR; |
| + sw_fallback_required_ = true; |
| + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
| } |
| encode_status = EncodeByteBufferOnCodecThread(jni, key_frame, input_frame, |
| j_input_buffer_index); |
| @@ -742,8 +754,8 @@ int32_t MediaCodecVideoEncoder::EncodeOnCodecThread( |
| if (!encode_status) { |
| ALOGE << "Failed encode frame with timestamp: " << input_frame.timestamp(); |
| - ResetCodecOnCodecThread(); |
| - return WEBRTC_VIDEO_CODEC_ERROR; |
| + sw_fallback_required_ = true; |
| + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
| } |
| // Save input image timestamps for later output. |
| @@ -761,8 +773,8 @@ int32_t MediaCodecVideoEncoder::EncodeOnCodecThread( |
| if (!DeliverPendingOutputs(jni)) { |
| ALOGE << "Failed deliver pending outputs."; |
| - ResetCodecOnCodecThread(); |
| - return WEBRTC_VIDEO_CODEC_ERROR; |
| + sw_fallback_required_ = true; |
| + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
| } |
| return WEBRTC_VIDEO_CODEC_OK; |
| } |
| @@ -810,7 +822,12 @@ bool MediaCodecVideoEncoder::EncodeByteBufferOnCodecThread(JNIEnv* jni, |
| jobject j_input_buffer = input_buffers_[input_buffer_index]; |
| uint8_t* yuv_buffer = |
| reinterpret_cast<uint8_t*>(jni->GetDirectBufferAddress(j_input_buffer)); |
| - CHECK_EXCEPTION(jni); |
| + if (CheckException(jni)) { |
| + ALOGE << "Exception in get direct buffer address. Falling back to SW " |
| + "encoder."; |
| + sw_fallback_required_ = true; |
|
AlexG
2016/08/31 00:25:57
We can not fallback to SW encoder for H.264 codec
sakal
2016/09/02 08:08:00
Done.
|
| + return false; |
| + } |
| RTC_CHECK(yuv_buffer) << "Indirect buffer??"; |
| RTC_CHECK(!libyuv::ConvertFromI420( |
| frame.video_frame_buffer()->DataY(), |
| @@ -828,7 +845,11 @@ bool MediaCodecVideoEncoder::EncodeByteBufferOnCodecThread(JNIEnv* jni, |
| input_buffer_index, |
| yuv_size_, |
| current_timestamp_us_); |
| - CHECK_EXCEPTION(jni); |
| + if (CheckException(jni)) { |
| + ALOGE << "Exception in encode buffer. Falling back to SW encoder."; |
| + sw_fallback_required_ = true; |
| + return false; |
| + } |
| return encode_status; |
| } |
| @@ -845,7 +866,11 @@ bool MediaCodecVideoEncoder::EncodeTextureOnCodecThread(JNIEnv* jni, |
| handle->oes_texture_id, |
| sampling_matrix, |
| current_timestamp_us_); |
| - CHECK_EXCEPTION(jni); |
| + if (CheckException(jni)) { |
| + ALOGE << "Exception in encode texture. Falling back to SW encoder."; |
| + sw_fallback_required_ = true; |
| + return false; |
| + } |
| return encode_status; |
| } |
| @@ -872,7 +897,11 @@ int32_t MediaCodecVideoEncoder::ReleaseOnCodecThread() { |
| jni->DeleteGlobalRef(input_buffers_[i]); |
| input_buffers_.clear(); |
| jni->CallVoidMethod(*j_media_codec_video_encoder_, j_release_method_); |
| - CHECK_EXCEPTION(jni); |
| + if (CheckException(jni)) { |
| + ALOGE << "Exception in release. Falling back to SW encoder."; |
| + sw_fallback_required_ = true; |
| + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
|
AlexG
2016/08/31 00:25:58
Return error here?
sakal
2016/09/02 08:08:01
Done.
|
| + } |
| rtc::MessageQueueManager::Clear(this); |
| inited_ = false; |
| use_surface_ = false; |
| @@ -883,6 +912,9 @@ int32_t MediaCodecVideoEncoder::ReleaseOnCodecThread() { |
| int32_t MediaCodecVideoEncoder::SetRatesOnCodecThread(uint32_t new_bit_rate, |
| uint32_t frame_rate) { |
| RTC_DCHECK(codec_thread_checker_.CalledOnValidThread()); |
| + if (sw_fallback_required_) { |
| + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
|
AlexG
2016/08/31 00:25:58
Return WEBRTC_VIDEO_CODEC_OK here? WEBRTC_VIDEO_CO
sakal
2016/09/02 08:08:00
Done.
|
| + } |
| frame_rate = (frame_rate < MAX_ALLOWED_VIDEO_FPS) ? |
| frame_rate : MAX_ALLOWED_VIDEO_FPS; |
| if (last_set_bitrate_kbps_ == new_bit_rate && |
| @@ -904,10 +936,9 @@ int32_t MediaCodecVideoEncoder::SetRatesOnCodecThread(uint32_t new_bit_rate, |
| j_set_rates_method_, |
| last_set_bitrate_kbps_, |
| last_set_fps_); |
| - CHECK_EXCEPTION(jni); |
| - if (!ret) { |
| - ResetCodecOnCodecThread(); |
| - return WEBRTC_VIDEO_CODEC_ERROR; |
| + if (CheckException(jni) || !ret) { |
| + sw_fallback_required_ = true; |
|
AlexG
2016/08/31 00:25:58
ditto: Return WEBRTC_VIDEO_CODEC_OK here, but keep
sakal
2016/09/02 08:08:00
Done.
|
| + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
| } |
| return WEBRTC_VIDEO_CODEC_OK; |
| } |
| @@ -943,7 +974,12 @@ bool MediaCodecVideoEncoder::DeliverPendingOutputs(JNIEnv* jni) { |
| while (true) { |
| jobject j_output_buffer_info = jni->CallObjectMethod( |
| *j_media_codec_video_encoder_, j_dequeue_output_buffer_method_); |
| - CHECK_EXCEPTION(jni); |
| + if (CheckException(jni)) { |
| + ALOGE << "Exception in set dequeue output buffer. Falling back to SW " |
| + "encoder."; |
| + sw_fallback_required_ = true; |
| + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
| + } |
| if (IsNull(jni, j_output_buffer_info)) { |
| break; |
| } |
| @@ -951,7 +987,7 @@ bool MediaCodecVideoEncoder::DeliverPendingOutputs(JNIEnv* jni) { |
| int output_buffer_index = |
| GetOutputBufferInfoIndex(jni, j_output_buffer_info); |
| if (output_buffer_index == -1) { |
| - ResetCodecOnCodecThread(); |
| + sw_fallback_required_ = true; |
| return false; |
| } |
| @@ -979,7 +1015,12 @@ bool MediaCodecVideoEncoder::DeliverPendingOutputs(JNIEnv* jni) { |
| size_t payload_size = jni->GetDirectBufferCapacity(j_output_buffer); |
| uint8_t* payload = reinterpret_cast<uint8_t*>( |
| jni->GetDirectBufferAddress(j_output_buffer)); |
| - CHECK_EXCEPTION(jni); |
| + if (CheckException(jni)) { |
| + ALOGE << "Exception in get direct buffer address. Falling back to SW " |
| + "encoder."; |
| + sw_fallback_required_ = true; |
| + return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
| + } |
| // Callback - return encoded frame. |
| int32_t callback_status = 0; |
| @@ -1079,7 +1120,7 @@ bool MediaCodecVideoEncoder::DeliverPendingOutputs(JNIEnv* jni) { |
| ALOGE << "Data:" << image->_buffer[0] << " " << image->_buffer[1] |
| << " " << image->_buffer[2] << " " << image->_buffer[3] |
| << " " << image->_buffer[4] << " " << image->_buffer[5]; |
| - ResetCodecOnCodecThread(); |
| + sw_fallback_required_ = true; |
|
AlexG
2016/08/31 00:25:58
You can not fallback to SW for H.264 encoder - we
sakal
2016/09/02 08:08:01
Done.
|
| return false; |
| } |
| scPositions[scPositionsLength] = payload_size; |
| @@ -1100,9 +1141,8 @@ bool MediaCodecVideoEncoder::DeliverPendingOutputs(JNIEnv* jni) { |
| bool success = jni->CallBooleanMethod(*j_media_codec_video_encoder_, |
| j_release_output_buffer_method_, |
| output_buffer_index); |
| - CHECK_EXCEPTION(jni); |
| - if (!success) { |
| - ResetCodecOnCodecThread(); |
| + if (CheckException(jni) || !success) { |
| + sw_fallback_required_ = true; |
| return false; |
| } |