Improve Java video codec error handling.

FALLBACK_SOFTWARE is now treated as a critical error and results in
immediate fallback to software coding if available. If ERROR is
returned, codec reset is attempted. If that fails, software fallback
is used.

Bug: b/73498933
Change-Id: I7fe163efd09e6f27c72491e9595954ddc59b1448
Reviewed-on: https://webrtc-review.googlesource.com/54901
Reviewed-by: Alex Glaznev <glaznev@webrtc.org>
Commit-Queue: Sami Kalliomäki <sakal@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22169}
This commit is contained in:
Sami Kalliomäki 2018-02-20 11:35:53 +01:00 committed by Commit Bot
parent 99f52f83e4
commit d60d5c4479
6 changed files with 78 additions and 52 deletions

View File

@ -163,7 +163,7 @@ class HardwareVideoDecoder
Logging.d(TAG, "initDecodeInternal");
if (outputThread != null) {
Logging.e(TAG, "initDecodeInternal called while the codec is already running");
return VideoCodecStatus.ERROR;
return VideoCodecStatus.FALLBACK_SOFTWARE;
}
// Note: it is not necessary to initialize dimensions under the lock, since the output thread
@ -180,7 +180,7 @@ class HardwareVideoDecoder
codec = MediaCodec.createByCodecName(codecName);
} catch (IOException | IllegalArgumentException e) {
Logging.e(TAG, "Cannot create media decoder " + codecName);
return VideoCodecStatus.ERROR;
return VideoCodecStatus.FALLBACK_SOFTWARE;
}
try {
MediaFormat format = MediaFormat.createVideoFormat(codecType.mimeType(), width, height);
@ -192,7 +192,7 @@ class HardwareVideoDecoder
} catch (IllegalStateException e) {
Logging.e(TAG, "initDecode failed", e);
release();
return VideoCodecStatus.ERROR;
return VideoCodecStatus.FALLBACK_SOFTWARE;
}
running = true;
outputThread = createOutputThread();
@ -241,11 +241,11 @@ class HardwareVideoDecoder
// Need to process a key frame first.
if (frame.frameType != EncodedImage.FrameType.VideoFrameKey) {
Logging.e(TAG, "decode() - key frame required first");
return VideoCodecStatus.ERROR;
return VideoCodecStatus.NO_OUTPUT;
}
if (!frame.completeFrame) {
Logging.e(TAG, "decode() - complete frame required first");
return VideoCodecStatus.ERROR;
return VideoCodecStatus.NO_OUTPUT;
}
}

View File

@ -176,7 +176,7 @@ class HardwareVideoEncoder implements VideoEncoder {
codec = MediaCodec.createByCodecName(codecName);
} catch (IOException | IllegalArgumentException e) {
Logging.e(TAG, "Cannot create media encoder " + codecName);
return VideoCodecStatus.ERROR;
return VideoCodecStatus.FALLBACK_SOFTWARE;
}
final int colorFormat = useSurfaceMode ? surfaceColorFormat : yuvColorFormat;
@ -218,7 +218,7 @@ class HardwareVideoEncoder implements VideoEncoder {
} catch (IllegalStateException e) {
Logging.e(TAG, "initEncodeInternal failed", e);
release();
return VideoCodecStatus.ERROR;
return VideoCodecStatus.FALLBACK_SOFTWARE;
}
running = true;

View File

@ -67,9 +67,10 @@ int32_t VideoDecoderWrapper::InitDecodeInternal(JNIEnv* jni) {
Java_VideoDecoderWrapper_createDecoderCallback(jni,
jlongFromPointer(this));
ScopedJavaLocalRef<jobject> ret =
Java_VideoDecoder_initDecode(jni, decoder_, settings, callback);
if (JavaToNativeVideoCodecStatus(jni, ret) == WEBRTC_VIDEO_CODEC_OK) {
int32_t status = JavaToNativeVideoCodecStatus(
jni, Java_VideoDecoder_initDecode(jni, decoder_, settings, callback));
RTC_LOG(LS_INFO) << "initDecode: " << status;
if (status == WEBRTC_VIDEO_CODEC_OK) {
initialized_ = true;
}
@ -77,7 +78,7 @@ int32_t VideoDecoderWrapper::InitDecodeInternal(JNIEnv* jni) {
// providing QP values.
qp_parsing_enabled_ = true;
return HandleReturnCode(jni, ret);
return status;
}
int32_t VideoDecoderWrapper::Decode(
@ -116,7 +117,7 @@ int32_t VideoDecoderWrapper::Decode(
ScopedJavaLocalRef<jobject> decode_info;
ScopedJavaLocalRef<jobject> ret =
Java_VideoDecoder_decode(env, decoder_, jinput_image, decode_info);
return HandleReturnCode(env, ret);
return HandleReturnCode(env, ret, "decode");
}
int32_t VideoDecoderWrapper::RegisterDecodeCompleteCallback(
@ -128,13 +129,14 @@ int32_t VideoDecoderWrapper::RegisterDecodeCompleteCallback(
int32_t VideoDecoderWrapper::Release() {
JNIEnv* jni = AttachCurrentThreadIfNeeded();
ScopedJavaLocalRef<jobject> ret = Java_VideoDecoder_release(jni, decoder_);
int32_t status = JavaToNativeVideoCodecStatus(
jni, Java_VideoDecoder_release(jni, decoder_));
RTC_LOG(LS_INFO) << "release: " << status;
{
rtc::CritScope cs(&frame_extra_infos_lock_);
frame_extra_infos_.clear();
}
initialized_ = false;
int32_t status = HandleReturnCode(jni, ret);
// It is allowed to reinitialize the codec on a different thread.
decoder_thread_checker_.DetachFromThread();
return status;
@ -193,19 +195,29 @@ void VideoDecoderWrapper::OnDecodedFrame(
}
int32_t VideoDecoderWrapper::HandleReturnCode(JNIEnv* jni,
const JavaRef<jobject>& code) {
int32_t value = JavaToNativeVideoCodecStatus(jni, code);
if (value < 0) { // Any errors are represented by negative values.
// Reset the codec.
if (Release() == WEBRTC_VIDEO_CODEC_OK) {
InitDecodeInternal(jni);
}
RTC_LOG(LS_WARNING) << "Falling back to software decoder.";
return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
} else {
const JavaRef<jobject>& j_value,
const char* method_name) {
int32_t value = JavaToNativeVideoCodecStatus(jni, j_value);
if (value >= 0) { // OK or NO_OUTPUT
return value;
}
RTC_LOG(LS_WARNING) << method_name << ": " << value;
if (value == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE ||
value == WEBRTC_VIDEO_CODEC_UNINITIALIZED) { // Critical error.
RTC_LOG(LS_WARNING) << "Java decoder requested software fallback.";
return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
}
// Try resetting the codec.
if (Release() == WEBRTC_VIDEO_CODEC_OK &&
InitDecodeInternal(jni) == WEBRTC_VIDEO_CODEC_OK) {
RTC_LOG(LS_WARNING) << "Reset Java decoder.";
return WEBRTC_VIDEO_CODEC_ERROR;
}
RTC_LOG(LS_WARNING) << "Unable to reset Java decoder.";
return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
}
rtc::Optional<uint8_t> VideoDecoderWrapper::ParseQP(

View File

@ -73,7 +73,9 @@ class VideoDecoderWrapper : public VideoDecoder {
// Takes Java VideoCodecStatus, handles it and returns WEBRTC_VIDEO_CODEC_*
// status code.
int32_t HandleReturnCode(JNIEnv* jni, const JavaRef<jobject>& code)
int32_t HandleReturnCode(JNIEnv* jni,
const JavaRef<jobject>& j_value,
const char* method_name)
RTC_RUN_ON(decoder_thread_checker_);
rtc::Optional<uint8_t> ParseQP(const EncodedImage& input_image)

View File

@ -31,8 +31,6 @@
namespace webrtc {
namespace jni {
static const int kMaxJavaEncoderResets = 3;
VideoEncoderWrapper::VideoEncoderWrapper(JNIEnv* jni,
const JavaRef<jobject>& j_encoder)
: encoder_(jni, j_encoder), int_array_class_(GetClass(jni, "[I")) {
@ -81,14 +79,14 @@ int32_t VideoEncoderWrapper::InitEncodeInternal(JNIEnv* jni) {
Java_VideoEncoderWrapper_createEncoderCallback(jni,
jlongFromPointer(this));
ScopedJavaLocalRef<jobject> ret =
Java_VideoEncoder_initEncode(jni, encoder_, settings, callback);
int32_t status = JavaToNativeVideoCodecStatus(
jni, Java_VideoEncoder_initEncode(jni, encoder_, settings, callback));
RTC_LOG(LS_INFO) << "initEncode: " << status;
if (JavaToNativeVideoCodecStatus(jni, ret) == WEBRTC_VIDEO_CODEC_OK) {
if (status == WEBRTC_VIDEO_CODEC_OK) {
initialized_ = true;
}
return HandleReturnCode(jni, ret);
return status;
}
int32_t VideoEncoderWrapper::RegisterEncodeCompleteCallback(
@ -99,11 +97,15 @@ int32_t VideoEncoderWrapper::RegisterEncodeCompleteCallback(
int32_t VideoEncoderWrapper::Release() {
JNIEnv* jni = AttachCurrentThreadIfNeeded();
ScopedJavaLocalRef<jobject> ret = Java_VideoEncoder_release(jni, encoder_);
int32_t status = JavaToNativeVideoCodecStatus(
jni, Java_VideoEncoder_release(jni, encoder_));
RTC_LOG(LS_INFO) << "release: " << status;
frame_extra_infos_.clear();
initialized_ = false;
encoder_queue_ = nullptr;
return HandleReturnCode(jni, ret);
return status;
}
int32_t VideoEncoderWrapper::Encode(
@ -132,7 +134,7 @@ int32_t VideoEncoderWrapper::Encode(
ScopedJavaLocalRef<jobject> ret =
Java_VideoEncoder_encode(jni, encoder_, j_frame, encode_info);
ReleaseJavaVideoFrame(jni, j_frame);
return HandleReturnCode(jni, ret);
return HandleReturnCode(jni, ret, "encode");
}
int32_t VideoEncoderWrapper::SetChannelParameters(uint32_t packet_loss,
@ -140,7 +142,7 @@ int32_t VideoEncoderWrapper::SetChannelParameters(uint32_t packet_loss,
JNIEnv* jni = AttachCurrentThreadIfNeeded();
ScopedJavaLocalRef<jobject> ret = Java_VideoEncoder_setChannelParameters(
jni, encoder_, (jshort)packet_loss, (jlong)rtt);
return HandleReturnCode(jni, ret);
return HandleReturnCode(jni, ret, "setChannelParameters");
}
int32_t VideoEncoderWrapper::SetRateAllocation(
@ -152,7 +154,7 @@ int32_t VideoEncoderWrapper::SetRateAllocation(
ToJavaBitrateAllocation(jni, allocation);
ScopedJavaLocalRef<jobject> ret = Java_VideoEncoder_setRateAllocation(
jni, encoder_, j_bitrate_allocation, (jint)framerate);
return HandleReturnCode(jni, ret);
return HandleReturnCode(jni, ret, "setRateAllocation");
}
VideoEncoderWrapper::ScalingSettings VideoEncoderWrapper::GetScalingSettings()
@ -263,21 +265,29 @@ void VideoEncoderWrapper::OnEncodedFrame(JNIEnv* jni,
}
int32_t VideoEncoderWrapper::HandleReturnCode(JNIEnv* jni,
const JavaRef<jobject>& code) {
int32_t value = JavaToNativeVideoCodecStatus(jni, code);
if (value < 0) { // Any errors are represented by negative values.
// Try resetting the codec.
if (++num_resets_ <= kMaxJavaEncoderResets &&
Release() == WEBRTC_VIDEO_CODEC_OK) {
RTC_LOG(LS_WARNING) << "Reset Java encoder: " << num_resets_;
return InitEncodeInternal(jni);
}
RTC_LOG(LS_WARNING) << "Falling back to software decoder.";
return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
} else {
const JavaRef<jobject>& j_value,
const char* method_name) {
int32_t value = JavaToNativeVideoCodecStatus(jni, j_value);
if (value >= 0) { // OK or NO_OUTPUT
return value;
}
RTC_LOG(LS_WARNING) << method_name << ": " << value;
if (value == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE ||
value == WEBRTC_VIDEO_CODEC_UNINITIALIZED) { // Critical error.
RTC_LOG(LS_WARNING) << "Java encoder requested software fallback.";
return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
}
// Try resetting the codec.
if (Release() == WEBRTC_VIDEO_CODEC_OK &&
InitEncodeInternal(jni) == WEBRTC_VIDEO_CODEC_OK) {
RTC_LOG(LS_WARNING) << "Reset Java encoder.";
return WEBRTC_VIDEO_CODEC_ERROR;
}
RTC_LOG(LS_WARNING) << "Unable to reset Java encoder.";
return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
}
RTPFragmentationHeader VideoEncoderWrapper::ParseFragmentationHeader(

View File

@ -78,7 +78,9 @@ class VideoEncoderWrapper : public VideoEncoder {
// Takes Java VideoCodecStatus, handles it and returns WEBRTC_VIDEO_CODEC_*
// status code.
int32_t HandleReturnCode(JNIEnv* jni, const JavaRef<jobject>& code);
int32_t HandleReturnCode(JNIEnv* jni,
const JavaRef<jobject>& j_value,
const char* method_name);
RTPFragmentationHeader ParseFragmentationHeader(
const std::vector<uint8_t>& buffer);