Skip to content

Commit d60d5c4

Browse files
Sami KalliomäkiCommit Bot
Sami Kalliomäki
authored and
Commit Bot
committed
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 <[email protected]> Commit-Queue: Sami Kalliomäki <[email protected]> Cr-Commit-Position: refs/heads/master@{#22169}
1 parent 99f52f8 commit d60d5c4

6 files changed

+76
-50
lines changed

sdk/android/src/java/org/webrtc/HardwareVideoDecoder.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ private VideoCodecStatus initDecodeInternal(int width, int height) {
163163
Logging.d(TAG, "initDecodeInternal");
164164
if (outputThread != null) {
165165
Logging.e(TAG, "initDecodeInternal called while the codec is already running");
166-
return VideoCodecStatus.ERROR;
166+
return VideoCodecStatus.FALLBACK_SOFTWARE;
167167
}
168168

169169
// Note: it is not necessary to initialize dimensions under the lock, since the output thread
@@ -180,7 +180,7 @@ private VideoCodecStatus initDecodeInternal(int width, int height) {
180180
codec = MediaCodec.createByCodecName(codecName);
181181
} catch (IOException | IllegalArgumentException e) {
182182
Logging.e(TAG, "Cannot create media decoder " + codecName);
183-
return VideoCodecStatus.ERROR;
183+
return VideoCodecStatus.FALLBACK_SOFTWARE;
184184
}
185185
try {
186186
MediaFormat format = MediaFormat.createVideoFormat(codecType.mimeType(), width, height);
@@ -192,7 +192,7 @@ private VideoCodecStatus initDecodeInternal(int width, int height) {
192192
} catch (IllegalStateException e) {
193193
Logging.e(TAG, "initDecode failed", e);
194194
release();
195-
return VideoCodecStatus.ERROR;
195+
return VideoCodecStatus.FALLBACK_SOFTWARE;
196196
}
197197
running = true;
198198
outputThread = createOutputThread();
@@ -241,11 +241,11 @@ public VideoCodecStatus decode(EncodedImage frame, DecodeInfo info) {
241241
// Need to process a key frame first.
242242
if (frame.frameType != EncodedImage.FrameType.VideoFrameKey) {
243243
Logging.e(TAG, "decode() - key frame required first");
244-
return VideoCodecStatus.ERROR;
244+
return VideoCodecStatus.NO_OUTPUT;
245245
}
246246
if (!frame.completeFrame) {
247247
Logging.e(TAG, "decode() - complete frame required first");
248-
return VideoCodecStatus.ERROR;
248+
return VideoCodecStatus.NO_OUTPUT;
249249
}
250250
}
251251

sdk/android/src/java/org/webrtc/HardwareVideoEncoder.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ private VideoCodecStatus initEncodeInternal() {
176176
codec = MediaCodec.createByCodecName(codecName);
177177
} catch (IOException | IllegalArgumentException e) {
178178
Logging.e(TAG, "Cannot create media encoder " + codecName);
179-
return VideoCodecStatus.ERROR;
179+
return VideoCodecStatus.FALLBACK_SOFTWARE;
180180
}
181181

182182
final int colorFormat = useSurfaceMode ? surfaceColorFormat : yuvColorFormat;
@@ -218,7 +218,7 @@ private VideoCodecStatus initEncodeInternal() {
218218
} catch (IllegalStateException e) {
219219
Logging.e(TAG, "initEncodeInternal failed", e);
220220
release();
221-
return VideoCodecStatus.ERROR;
221+
return VideoCodecStatus.FALLBACK_SOFTWARE;
222222
}
223223

224224
running = true;

sdk/android/src/jni/videodecoderwrapper.cc

+29-17
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,18 @@ int32_t VideoDecoderWrapper::InitDecodeInternal(JNIEnv* jni) {
6767
Java_VideoDecoderWrapper_createDecoderCallback(jni,
6868
jlongFromPointer(this));
6969

70-
ScopedJavaLocalRef<jobject> ret =
71-
Java_VideoDecoder_initDecode(jni, decoder_, settings, callback);
72-
if (JavaToNativeVideoCodecStatus(jni, ret) == WEBRTC_VIDEO_CODEC_OK) {
70+
int32_t status = JavaToNativeVideoCodecStatus(
71+
jni, Java_VideoDecoder_initDecode(jni, decoder_, settings, callback));
72+
RTC_LOG(LS_INFO) << "initDecode: " << status;
73+
if (status == WEBRTC_VIDEO_CODEC_OK) {
7374
initialized_ = true;
7475
}
7576

7677
// The decoder was reinitialized so re-enable the QP parsing in case it stops
7778
// providing QP values.
7879
qp_parsing_enabled_ = true;
7980

80-
return HandleReturnCode(jni, ret);
81+
return status;
8182
}
8283

8384
int32_t VideoDecoderWrapper::Decode(
@@ -116,7 +117,7 @@ int32_t VideoDecoderWrapper::Decode(
116117
ScopedJavaLocalRef<jobject> decode_info;
117118
ScopedJavaLocalRef<jobject> ret =
118119
Java_VideoDecoder_decode(env, decoder_, jinput_image, decode_info);
119-
return HandleReturnCode(env, ret);
120+
return HandleReturnCode(env, ret, "decode");
120121
}
121122

122123
int32_t VideoDecoderWrapper::RegisterDecodeCompleteCallback(
@@ -128,13 +129,14 @@ int32_t VideoDecoderWrapper::RegisterDecodeCompleteCallback(
128129

129130
int32_t VideoDecoderWrapper::Release() {
130131
JNIEnv* jni = AttachCurrentThreadIfNeeded();
131-
ScopedJavaLocalRef<jobject> ret = Java_VideoDecoder_release(jni, decoder_);
132+
int32_t status = JavaToNativeVideoCodecStatus(
133+
jni, Java_VideoDecoder_release(jni, decoder_));
134+
RTC_LOG(LS_INFO) << "release: " << status;
132135
{
133136
rtc::CritScope cs(&frame_extra_infos_lock_);
134137
frame_extra_infos_.clear();
135138
}
136139
initialized_ = false;
137-
int32_t status = HandleReturnCode(jni, ret);
138140
// It is allowed to reinitialize the codec on a different thread.
139141
decoder_thread_checker_.DetachFromThread();
140142
return status;
@@ -193,19 +195,29 @@ void VideoDecoderWrapper::OnDecodedFrame(
193195
}
194196

195197
int32_t VideoDecoderWrapper::HandleReturnCode(JNIEnv* jni,
196-
const JavaRef<jobject>& code) {
197-
int32_t value = JavaToNativeVideoCodecStatus(jni, code);
198-
if (value < 0) { // Any errors are represented by negative values.
199-
// Reset the codec.
200-
if (Release() == WEBRTC_VIDEO_CODEC_OK) {
201-
InitDecodeInternal(jni);
202-
}
198+
const JavaRef<jobject>& j_value,
199+
const char* method_name) {
200+
int32_t value = JavaToNativeVideoCodecStatus(jni, j_value);
201+
if (value >= 0) { // OK or NO_OUTPUT
202+
return value;
203+
}
203204

204-
RTC_LOG(LS_WARNING) << "Falling back to software decoder.";
205+
RTC_LOG(LS_WARNING) << method_name << ": " << value;
206+
if (value == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE ||
207+
value == WEBRTC_VIDEO_CODEC_UNINITIALIZED) { // Critical error.
208+
RTC_LOG(LS_WARNING) << "Java decoder requested software fallback.";
205209
return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
206-
} else {
207-
return value;
208210
}
211+
212+
// Try resetting the codec.
213+
if (Release() == WEBRTC_VIDEO_CODEC_OK &&
214+
InitDecodeInternal(jni) == WEBRTC_VIDEO_CODEC_OK) {
215+
RTC_LOG(LS_WARNING) << "Reset Java decoder.";
216+
return WEBRTC_VIDEO_CODEC_ERROR;
217+
}
218+
219+
RTC_LOG(LS_WARNING) << "Unable to reset Java decoder.";
220+
return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
209221
}
210222

211223
rtc::Optional<uint8_t> VideoDecoderWrapper::ParseQP(

sdk/android/src/jni/videodecoderwrapper.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ class VideoDecoderWrapper : public VideoDecoder {
7373

7474
// Takes Java VideoCodecStatus, handles it and returns WEBRTC_VIDEO_CODEC_*
7575
// status code.
76-
int32_t HandleReturnCode(JNIEnv* jni, const JavaRef<jobject>& code)
76+
int32_t HandleReturnCode(JNIEnv* jni,
77+
const JavaRef<jobject>& j_value,
78+
const char* method_name)
7779
RTC_RUN_ON(decoder_thread_checker_);
7880

7981
rtc::Optional<uint8_t> ParseQP(const EncodedImage& input_image)

sdk/android/src/jni/videoencoderwrapper.cc

+34-24
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@
3131
namespace webrtc {
3232
namespace jni {
3333

34-
static const int kMaxJavaEncoderResets = 3;
35-
3634
VideoEncoderWrapper::VideoEncoderWrapper(JNIEnv* jni,
3735
const JavaRef<jobject>& j_encoder)
3836
: encoder_(jni, j_encoder), int_array_class_(GetClass(jni, "[I")) {
@@ -81,14 +79,14 @@ int32_t VideoEncoderWrapper::InitEncodeInternal(JNIEnv* jni) {
8179
Java_VideoEncoderWrapper_createEncoderCallback(jni,
8280
jlongFromPointer(this));
8381

84-
ScopedJavaLocalRef<jobject> ret =
85-
Java_VideoEncoder_initEncode(jni, encoder_, settings, callback);
82+
int32_t status = JavaToNativeVideoCodecStatus(
83+
jni, Java_VideoEncoder_initEncode(jni, encoder_, settings, callback));
84+
RTC_LOG(LS_INFO) << "initEncode: " << status;
8685

87-
if (JavaToNativeVideoCodecStatus(jni, ret) == WEBRTC_VIDEO_CODEC_OK) {
86+
if (status == WEBRTC_VIDEO_CODEC_OK) {
8887
initialized_ = true;
8988
}
90-
91-
return HandleReturnCode(jni, ret);
89+
return status;
9290
}
9391

9492
int32_t VideoEncoderWrapper::RegisterEncodeCompleteCallback(
@@ -99,11 +97,15 @@ int32_t VideoEncoderWrapper::RegisterEncodeCompleteCallback(
9997

10098
int32_t VideoEncoderWrapper::Release() {
10199
JNIEnv* jni = AttachCurrentThreadIfNeeded();
102-
ScopedJavaLocalRef<jobject> ret = Java_VideoEncoder_release(jni, encoder_);
100+
101+
int32_t status = JavaToNativeVideoCodecStatus(
102+
jni, Java_VideoEncoder_release(jni, encoder_));
103+
RTC_LOG(LS_INFO) << "release: " << status;
103104
frame_extra_infos_.clear();
104105
initialized_ = false;
105106
encoder_queue_ = nullptr;
106-
return HandleReturnCode(jni, ret);
107+
108+
return status;
107109
}
108110

109111
int32_t VideoEncoderWrapper::Encode(
@@ -132,15 +134,15 @@ int32_t VideoEncoderWrapper::Encode(
132134
ScopedJavaLocalRef<jobject> ret =
133135
Java_VideoEncoder_encode(jni, encoder_, j_frame, encode_info);
134136
ReleaseJavaVideoFrame(jni, j_frame);
135-
return HandleReturnCode(jni, ret);
137+
return HandleReturnCode(jni, ret, "encode");
136138
}
137139

138140
int32_t VideoEncoderWrapper::SetChannelParameters(uint32_t packet_loss,
139141
int64_t rtt) {
140142
JNIEnv* jni = AttachCurrentThreadIfNeeded();
141143
ScopedJavaLocalRef<jobject> ret = Java_VideoEncoder_setChannelParameters(
142144
jni, encoder_, (jshort)packet_loss, (jlong)rtt);
143-
return HandleReturnCode(jni, ret);
145+
return HandleReturnCode(jni, ret, "setChannelParameters");
144146
}
145147

146148
int32_t VideoEncoderWrapper::SetRateAllocation(
@@ -152,7 +154,7 @@ int32_t VideoEncoderWrapper::SetRateAllocation(
152154
ToJavaBitrateAllocation(jni, allocation);
153155
ScopedJavaLocalRef<jobject> ret = Java_VideoEncoder_setRateAllocation(
154156
jni, encoder_, j_bitrate_allocation, (jint)framerate);
155-
return HandleReturnCode(jni, ret);
157+
return HandleReturnCode(jni, ret, "setRateAllocation");
156158
}
157159

158160
VideoEncoderWrapper::ScalingSettings VideoEncoderWrapper::GetScalingSettings()
@@ -263,21 +265,29 @@ void VideoEncoderWrapper::OnEncodedFrame(JNIEnv* jni,
263265
}
264266

265267
int32_t VideoEncoderWrapper::HandleReturnCode(JNIEnv* jni,
266-
const JavaRef<jobject>& code) {
267-
int32_t value = JavaToNativeVideoCodecStatus(jni, code);
268-
if (value < 0) { // Any errors are represented by negative values.
269-
// Try resetting the codec.
270-
if (++num_resets_ <= kMaxJavaEncoderResets &&
271-
Release() == WEBRTC_VIDEO_CODEC_OK) {
272-
RTC_LOG(LS_WARNING) << "Reset Java encoder: " << num_resets_;
273-
return InitEncodeInternal(jni);
274-
}
268+
const JavaRef<jobject>& j_value,
269+
const char* method_name) {
270+
int32_t value = JavaToNativeVideoCodecStatus(jni, j_value);
271+
if (value >= 0) { // OK or NO_OUTPUT
272+
return value;
273+
}
275274

276-
RTC_LOG(LS_WARNING) << "Falling back to software decoder.";
275+
RTC_LOG(LS_WARNING) << method_name << ": " << value;
276+
if (value == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE ||
277+
value == WEBRTC_VIDEO_CODEC_UNINITIALIZED) { // Critical error.
278+
RTC_LOG(LS_WARNING) << "Java encoder requested software fallback.";
277279
return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
278-
} else {
279-
return value;
280280
}
281+
282+
// Try resetting the codec.
283+
if (Release() == WEBRTC_VIDEO_CODEC_OK &&
284+
InitEncodeInternal(jni) == WEBRTC_VIDEO_CODEC_OK) {
285+
RTC_LOG(LS_WARNING) << "Reset Java encoder.";
286+
return WEBRTC_VIDEO_CODEC_ERROR;
287+
}
288+
289+
RTC_LOG(LS_WARNING) << "Unable to reset Java encoder.";
290+
return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
281291
}
282292

283293
RTPFragmentationHeader VideoEncoderWrapper::ParseFragmentationHeader(

sdk/android/src/jni/videoencoderwrapper.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ class VideoEncoderWrapper : public VideoEncoder {
7878

7979
// Takes Java VideoCodecStatus, handles it and returns WEBRTC_VIDEO_CODEC_*
8080
// status code.
81-
int32_t HandleReturnCode(JNIEnv* jni, const JavaRef<jobject>& code);
81+
int32_t HandleReturnCode(JNIEnv* jni,
82+
const JavaRef<jobject>& j_value,
83+
const char* method_name);
8284

8385
RTPFragmentationHeader ParseFragmentationHeader(
8486
const std::vector<uint8_t>& buffer);

0 commit comments

Comments
 (0)