| 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..e19095725d917d79dacf857314bd38ce873150c2 100644
|
| --- a/webrtc/api/android/jni/androidmediaencoder_jni.cc
|
| +++ b/webrtc/api/android/jni/androidmediaencoder_jni.cc
|
| @@ -129,8 +129,20 @@ class MediaCodecVideoEncoder : public webrtc::VideoEncoder,
|
| // ResetCodecOnCodecThread() calls ReleaseOnCodecThread() and
|
| // InitEncodeOnCodecThread() in an attempt to restore the codec to an
|
| // operable state. Necessary after all manner of OMX-layer errors.
|
| + // Returns true if the codec was reset successfully.
|
| bool ResetCodecOnCodecThread();
|
|
|
| + // Fallback to a software encoder if one is supported else try to reset the
|
| + // encoder. Called with |reset_if_fallback_unavailable| equal to false from
|
| + // init/release encoder so that we don't go into infinite recursion.
|
| + // Returns true if the codec was reset successfully.
|
| + bool ProcessHWErrorOnCodecThread(bool reset_if_fallback_unavailable);
|
| +
|
| + // Calls ProcessHWErrorOnCodecThread(true). Returns
|
| + // WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE if sw_fallback_required_ was set or
|
| + // WEBRTC_VIDEO_CODEC_ERROR otherwise.
|
| + int32_t ProcessHWErrorOnEncodeOnCodecThread();
|
| +
|
| // Implementation of webrtc::VideoEncoder methods above, all running on the
|
| // codec thread exclusively.
|
| //
|
| @@ -282,6 +294,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 +303,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 +377,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.";
|
| + ProcessHWErrorOnCodecThread(true /* reset_if_fallback_unavailable */);
|
| + }
|
| srand(time(NULL));
|
| AllowBlockingCalls();
|
| }
|
| @@ -378,7 +397,9 @@ int32_t MediaCodecVideoEncoder::InitEncode(
|
| RTC_CHECK(codec_settings->codecType == codecType_)
|
| << "Unsupported codec " << codec_settings->codecType << " for "
|
| << codecType_;
|
| -
|
| + if (sw_fallback_required_) {
|
| + return WEBRTC_VIDEO_CODEC_OK;
|
| + }
|
| codec_mode_ = codec_settings->mode;
|
| int init_width = codec_settings->width;
|
| int init_height = codec_settings->height;
|
| @@ -494,20 +515,45 @@ void MediaCodecVideoEncoder::OnMessage(rtc::Message* msg) {
|
| 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
|
| + if (ReleaseOnCodecThread() != WEBRTC_VIDEO_CODEC_OK) {
|
| + ALOGE << "Releasing codec failed during reset.";
|
| + return false;
|
| + }
|
| + if (InitEncodeOnCodecThread(width_, height_, 0, 0, false) !=
|
| + WEBRTC_VIDEO_CODEC_OK) {
|
| + ALOGE << "Initializing encoder failed during reset.";
|
| return false;
|
| }
|
| return true;
|
| }
|
|
|
| +bool MediaCodecVideoEncoder::ProcessHWErrorOnCodecThread(
|
| + bool reset_if_fallback_unavailable) {
|
| + ALOGE << "ProcessHWErrorOnCodecThread";
|
| + if (VideoEncoder::IsSupportedSoftware(
|
| + VideoEncoder::CodecToEncoderType(codecType_))) {
|
| + ALOGE << "Fallback to SW encoder.";
|
| + sw_fallback_required_ = true;
|
| + return false;
|
| + } else if (reset_if_fallback_unavailable) {
|
| + ALOGE << "Reset encoder.";
|
| + return ResetCodecOnCodecThread();
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +int32_t MediaCodecVideoEncoder::ProcessHWErrorOnEncodeOnCodecThread() {
|
| + ProcessHWErrorOnCodecThread(true /* reset_if_fallback_unavailable */);
|
| + return sw_fallback_required_ ? WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE
|
| + : WEBRTC_VIDEO_CODEC_ERROR;
|
| +}
|
| +
|
| 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_OK;
|
| + }
|
| RTC_CHECK(!use_surface || egl_context_ != nullptr) << "EGL context not set.";
|
| JNIEnv* jni = AttachCurrentThreadIfNeeded();
|
| ScopedLocalRefFrame local_ref_frame(jni);
|
| @@ -560,16 +606,27 @@ int32_t MediaCodecVideoEncoder::InitEncodeOnCodecThread(
|
| (use_surface ? egl_context_ : nullptr));
|
| if (!encode_status) {
|
| ALOGE << "Failed to configure encoder.";
|
| + ProcessHWErrorOnCodecThread(false /* reset_if_fallback_unavailable */);
|
| + return WEBRTC_VIDEO_CODEC_ERROR;
|
| + }
|
| + if (CheckException(jni)) {
|
| + ALOGE << "Exception in init encode.";
|
| + ProcessHWErrorOnCodecThread(false /* reset_if_fallback_unavailable */);
|
| return WEBRTC_VIDEO_CODEC_ERROR;
|
| }
|
| - CHECK_EXCEPTION(jni);
|
|
|
| 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.";
|
| + ProcessHWErrorOnCodecThread(false /* reset_if_fallback_unavailable */);
|
| + return WEBRTC_VIDEO_CODEC_ERROR;
|
| + }
|
| +
|
| if (IsNull(jni, input_buffers)) {
|
| + ProcessHWErrorOnCodecThread(false /* reset_if_fallback_unavailable */);
|
| return WEBRTC_VIDEO_CODEC_ERROR;
|
| }
|
|
|
| @@ -585,6 +642,7 @@ int32_t MediaCodecVideoEncoder::InitEncodeOnCodecThread(
|
| break;
|
| default:
|
| LOG(LS_ERROR) << "Wrong color format.";
|
| + ProcessHWErrorOnCodecThread(false /* reset_if_fallback_unavailable */);
|
| return WEBRTC_VIDEO_CODEC_ERROR;
|
| }
|
| size_t num_input_buffers = jni->GetArrayLength(input_buffers);
|
| @@ -596,7 +654,11 @@ 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.";
|
| + ProcessHWErrorOnCodecThread(false /* reset_if_fallback_unavailable */);
|
| + return WEBRTC_VIDEO_CODEC_ERROR;
|
| + }
|
| RTC_CHECK(yuv_buffer_capacity >= yuv_size_) << "Insufficient capacity";
|
| }
|
| }
|
| @@ -610,6 +672,8 @@ 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 +700,11 @@ int32_t MediaCodecVideoEncoder::EncodeOnCodecThread(
|
|
|
| frames_received_++;
|
| if (!DeliverPendingOutputs(jni)) {
|
| - if (!ResetCodecOnCodecThread())
|
| - return WEBRTC_VIDEO_CODEC_ERROR;
|
| + if (!ProcessHWErrorOnCodecThread(
|
| + true /* reset_if_fallback_unavailable */)) {
|
| + return sw_fallback_required_ ? WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE
|
| + : WEBRTC_VIDEO_CODEC_ERROR;
|
| + }
|
| }
|
| if (frames_encoded_ < kMaxEncodedLogFrames) {
|
| ALOGD << "Encoder frame in # " << (frames_received_ - 1)
|
| @@ -669,9 +736,8 @@ int32_t MediaCodecVideoEncoder::EncodeOnCodecThread(
|
| consecutive_full_queue_frame_drops_++;
|
| if (consecutive_full_queue_frame_drops_ >=
|
| ENCODER_STALL_FRAMEDROP_THRESHOLD) {
|
| - ALOGE << "Encoder got stuck. Reset.";
|
| - ResetCodecOnCodecThread();
|
| - return WEBRTC_VIDEO_CODEC_ERROR;
|
| + ALOGE << "Encoder got stuck.";
|
| + return ProcessHWErrorOnEncodeOnCodecThread();
|
| }
|
| frames_dropped_media_encoder_++;
|
| OnDroppedFrameOnCodecThread();
|
| @@ -715,7 +781,10 @@ 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.";
|
| + return ProcessHWErrorOnEncodeOnCodecThread();
|
| + }
|
| if (j_input_buffer_index == -1) {
|
| // Video codec falls behind - no input buffer available.
|
| ALOGW << "Encoder drop frame - no input buffers available";
|
| @@ -731,8 +800,7 @@ 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;
|
| + return ProcessHWErrorOnEncodeOnCodecThread();
|
| }
|
| encode_status = EncodeByteBufferOnCodecThread(jni, key_frame, input_frame,
|
| j_input_buffer_index);
|
| @@ -742,8 +810,7 @@ int32_t MediaCodecVideoEncoder::EncodeOnCodecThread(
|
|
|
| if (!encode_status) {
|
| ALOGE << "Failed encode frame with timestamp: " << input_frame.timestamp();
|
| - ResetCodecOnCodecThread();
|
| - return WEBRTC_VIDEO_CODEC_ERROR;
|
| + return ProcessHWErrorOnEncodeOnCodecThread();
|
| }
|
|
|
| // Save input image timestamps for later output.
|
| @@ -760,9 +827,7 @@ int32_t MediaCodecVideoEncoder::EncodeOnCodecThread(
|
| codec_thread_->PostDelayed(RTC_FROM_HERE, kMediaCodecPollMs, this);
|
|
|
| if (!DeliverPendingOutputs(jni)) {
|
| - ALOGE << "Failed deliver pending outputs.";
|
| - ResetCodecOnCodecThread();
|
| - return WEBRTC_VIDEO_CODEC_ERROR;
|
| + return ProcessHWErrorOnEncodeOnCodecThread();
|
| }
|
| return WEBRTC_VIDEO_CODEC_OK;
|
| }
|
| @@ -810,7 +875,11 @@ 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.";
|
| + ProcessHWErrorOnCodecThread(true /* reset_if_fallback_unavailable */);
|
| + return false;
|
| + }
|
| RTC_CHECK(yuv_buffer) << "Indirect buffer??";
|
| RTC_CHECK(!libyuv::ConvertFromI420(
|
| frame.video_frame_buffer()->DataY(),
|
| @@ -828,7 +897,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.";
|
| + ProcessHWErrorOnCodecThread(true /* reset_if_fallback_unavailable */);
|
| + return false;
|
| + }
|
| return encode_status;
|
| }
|
|
|
| @@ -845,7 +918,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.";
|
| + ProcessHWErrorOnCodecThread(true /* reset_if_fallback_unavailable */);
|
| + return false;
|
| + }
|
| return encode_status;
|
| }
|
|
|
| @@ -872,7 +949,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.";
|
| + ProcessHWErrorOnCodecThread(false /* reset_if_fallback_unavailable */);
|
| + return WEBRTC_VIDEO_CODEC_ERROR;
|
| + }
|
| rtc::MessageQueueManager::Clear(this);
|
| inited_ = false;
|
| use_surface_ = false;
|
| @@ -883,6 +964,8 @@ 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_OK;
|
| frame_rate = (frame_rate < MAX_ALLOWED_VIDEO_FPS) ?
|
| frame_rate : MAX_ALLOWED_VIDEO_FPS;
|
| if (last_set_bitrate_kbps_ == new_bit_rate &&
|
| @@ -904,10 +987,10 @@ 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) {
|
| + ProcessHWErrorOnCodecThread(true /* reset_if_fallback_unavailable */);
|
| + return sw_fallback_required_ ? WEBRTC_VIDEO_CODEC_OK
|
| + : WEBRTC_VIDEO_CODEC_ERROR;
|
| }
|
| return WEBRTC_VIDEO_CODEC_OK;
|
| }
|
| @@ -943,7 +1026,11 @@ 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.";
|
| + ProcessHWErrorOnCodecThread(true /* reset_if_fallback_unavailable */);
|
| + return WEBRTC_VIDEO_CODEC_ERROR;
|
| + }
|
| if (IsNull(jni, j_output_buffer_info)) {
|
| break;
|
| }
|
| @@ -951,7 +1038,7 @@ bool MediaCodecVideoEncoder::DeliverPendingOutputs(JNIEnv* jni) {
|
| int output_buffer_index =
|
| GetOutputBufferInfoIndex(jni, j_output_buffer_info);
|
| if (output_buffer_index == -1) {
|
| - ResetCodecOnCodecThread();
|
| + ProcessHWErrorOnCodecThread(true /* reset_if_fallback_unavailable */);
|
| return false;
|
| }
|
|
|
| @@ -979,7 +1066,11 @@ 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.";
|
| + ProcessHWErrorOnCodecThread(true /* reset_if_fallback_unavailable */);
|
| + return WEBRTC_VIDEO_CODEC_ERROR;
|
| + }
|
|
|
| // Callback - return encoded frame.
|
| int32_t callback_status = 0;
|
| @@ -1079,7 +1170,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();
|
| + ProcessHWErrorOnCodecThread(true /* reset_if_fallback_unavailable */);
|
| return false;
|
| }
|
| scPositions[scPositionsLength] = payload_size;
|
| @@ -1100,9 +1191,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) {
|
| + ProcessHWErrorOnCodecThread(true /* reset_if_fallback_unavailable */);
|
| return false;
|
| }
|
|
|
|
|