Switch to SW video decoder on Android after getting 2 or more

critical errors from HW decoder.

BUG=410730
R=tkchin@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/23819004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7368 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
glaznev@webrtc.org 2014-10-02 16:58:05 +00:00
parent 4b133da5fd
commit 25cc745d6b
2 changed files with 40 additions and 19 deletions

View File

@ -1993,6 +1993,7 @@ class MediaCodecVideoDecoder : public webrtc::VideoDecoder,
bool key_frame_required_;
bool inited_;
bool use_surface_;
int error_count_;
VideoCodec codec_;
I420VideoFrame decoded_image_;
NativeHandleImpl native_handle_;
@ -2072,6 +2073,7 @@ int MediaCodecVideoDecoder::SetAndroidObjects(JNIEnv* jni,
MediaCodecVideoDecoder::MediaCodecVideoDecoder(JNIEnv* jni)
: key_frame_required_(true),
inited_(false),
error_count_(0),
codec_thread_(new Thread()),
j_media_codec_video_decoder_class_(
jni,
@ -2089,7 +2091,7 @@ MediaCodecVideoDecoder::MediaCodecVideoDecoder(JNIEnv* jni)
j_init_decode_method_ = GetMethodID(
jni, *j_media_codec_video_decoder_class_, "initDecode",
"(IIZLandroid/opengl/EGLContext;)Z");
"(IIZZLandroid/opengl/EGLContext;)Z");
j_release_method_ =
GetMethodID(jni, *j_media_codec_video_decoder_class_, "release", "()V");
j_dequeue_input_buffer_method_ = GetMethodID(
@ -2176,13 +2178,19 @@ int32_t MediaCodecVideoDecoder::InitDecodeOnCodecThread() {
CheckOnCodecThread();
JNIEnv* jni = AttachCurrentThreadIfNeeded();
ScopedLocalRefFrame local_ref_frame(jni);
ALOGD("InitDecodeOnCodecThread: %d x %d. fps: %d",
codec_.width, codec_.height, codec_.maxFramerate);
ALOGD("InitDecodeOnCodecThread: %d x %d. Fps: %d. Errors: %d",
codec_.width, codec_.height, codec_.maxFramerate, error_count_);
bool use_sw_codec = false;
if (error_count_ > 1) {
// If more than one critical errors happen for HW codec, switch to SW codec.
use_sw_codec = true;
}
bool success = jni->CallBooleanMethod(*j_media_codec_video_decoder_,
j_init_decode_method_,
codec_.width,
codec_.height,
use_sw_codec,
use_surface_,
render_egl_context_);
CHECK_EXCEPTION(jni);
@ -2320,11 +2328,13 @@ int32_t MediaCodecVideoDecoder::DecodeOnCodecThread(
if (frames_received_ > frames_decoded_ + max_pending_frames_) {
ALOGV("Wait for output...");
if (!DeliverPendingOutputs(jni, kMediaCodecTimeoutMs * 1000)) {
error_count_++;
Reset();
return WEBRTC_VIDEO_CODEC_ERROR;
}
if (frames_received_ > frames_decoded_ + max_pending_frames_) {
ALOGE("Output buffer dequeue timeout");
error_count_++;
Reset();
return WEBRTC_VIDEO_CODEC_ERROR;
}
@ -2336,6 +2346,7 @@ int32_t MediaCodecVideoDecoder::DecodeOnCodecThread(
CHECK_EXCEPTION(jni);
if (j_input_buffer_index < 0) {
ALOGE("dequeueInputBuffer error");
error_count_++;
Reset();
return WEBRTC_VIDEO_CODEC_ERROR;
}
@ -2350,6 +2361,7 @@ int32_t MediaCodecVideoDecoder::DecodeOnCodecThread(
if (buffer_capacity < inputImage._length) {
ALOGE("Input frame size %d is bigger than buffer size %d.",
inputImage._length, buffer_capacity);
error_count_++;
Reset();
return WEBRTC_VIDEO_CODEC_ERROR;
}
@ -2374,6 +2386,7 @@ int32_t MediaCodecVideoDecoder::DecodeOnCodecThread(
CHECK_EXCEPTION(jni);
if (!success) {
ALOGE("queueInputBuffer error");
error_count_++;
Reset();
return WEBRTC_VIDEO_CODEC_ERROR;
}
@ -2381,6 +2394,7 @@ int32_t MediaCodecVideoDecoder::DecodeOnCodecThread(
// Try to drain the decoder
if (!DeliverPendingOutputs(jni, 0)) {
ALOGE("DeliverPendingOutputs error");
error_count_++;
Reset();
return WEBRTC_VIDEO_CODEC_ERROR;
}
@ -2410,7 +2424,6 @@ bool MediaCodecVideoDecoder::DeliverPendingOutputs(
GetIntField(jni, j_decoder_output_buffer_info, j_info_index_field_);
if (output_buffer_index < 0) {
ALOGE("dequeueOutputBuffer error : %d", output_buffer_index);
Reset();
return false;
}
int output_buffer_offset =
@ -2435,7 +2448,6 @@ bool MediaCodecVideoDecoder::DeliverPendingOutputs(
if (!use_surface_) {
if (output_buffer_size < width * height * 3 / 2) {
ALOGE("Insufficient output buffer size: %d", output_buffer_size);
Reset();
return false;
}
jobjectArray output_buffers = reinterpret_cast<jobjectArray>(GetObjectField(
@ -2494,7 +2506,6 @@ bool MediaCodecVideoDecoder::DeliverPendingOutputs(
CHECK_EXCEPTION(jni);
if (!success) {
ALOGE("releaseOutputBuffer error");
Reset();
return false;
}
@ -2561,7 +2572,10 @@ void MediaCodecVideoDecoder::OnMessage(rtc::Message* msg) {
CHECK(!msg->pdata) << "Unexpected message!";
CheckOnCodecThread();
DeliverPendingOutputs(jni, 0);
if (!DeliverPendingOutputs(jni, 0)) {
error_count_++;
Reset();
}
codec_thread_->PostDelayed(kMediaCodecPollMs, this);
}

View File

@ -68,6 +68,9 @@ class MediaCodecVideoDecoder {
// List of supported HW VP8 decoders.
private static final String[] supportedHwCodecPrefixes =
{"OMX.qcom.", "OMX.Nvidia." };
// List of supported SW VP8 decoders.
private static final String[] supportedSwCodecPrefixes =
{"OMX.google."};
// NV12 color format supported by QCOM codec, but not declared in MediaCodec -
// see /hardware/qcom/media/mm-core/inc/OMX_QCOMExtns.h
private static final int
@ -96,7 +99,7 @@ class MediaCodecVideoDecoder {
private MediaCodecVideoDecoder() { }
// Helper struct for findVp8HwDecoder() below.
// Helper struct for findVp8Decoder() below.
private static class DecoderProperties {
public DecoderProperties(String codecName, int colorFormat) {
this.codecName = codecName;
@ -106,10 +109,14 @@ class MediaCodecVideoDecoder {
public final int colorFormat; // Color format supported by codec.
}
private static DecoderProperties findVp8HwDecoder() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
private static DecoderProperties findVp8Decoder(boolean useSwCodec) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return null; // MediaCodec.setParameters is missing.
}
String[] supportedCodecPrefixes = supportedHwCodecPrefixes;
if (useSwCodec) {
supportedCodecPrefixes = supportedSwCodecPrefixes;
}
for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {
MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
if (info.isEncoder()) {
@ -127,10 +134,10 @@ class MediaCodecVideoDecoder {
}
Log.d(TAG, "Found candidate decoder " + name);
// Check if this is supported HW decoder.
// Check if this is supported decoder.
boolean supportedCodec = false;
for (String hwCodecPrefix : supportedHwCodecPrefixes) {
if (name.startsWith(hwCodecPrefix)) {
for (String codecPrefix : supportedCodecPrefixes) {
if (name.startsWith(codecPrefix)) {
supportedCodec = true;
break;
}
@ -160,7 +167,7 @@ class MediaCodecVideoDecoder {
}
private static boolean isPlatformSupported() {
return findVp8HwDecoder() != null;
return findVp8Decoder(false) != null;
}
private void checkOnMediaCodecThread() {
@ -265,21 +272,21 @@ class MediaCodecVideoDecoder {
}
}
private boolean initDecode(int width, int height, boolean useSurface,
EGLContext sharedContext) {
private boolean initDecode(int width, int height, boolean useSwCodec,
boolean useSurface, EGLContext sharedContext) {
if (mediaCodecThread != null) {
throw new RuntimeException("Forgot to release()?");
}
if (useSurface && sharedContext == null) {
throw new RuntimeException("No shared EGL context.");
}
DecoderProperties properties = findVp8HwDecoder();
DecoderProperties properties = findVp8Decoder(useSwCodec);
if (properties == null) {
throw new RuntimeException("Cannot find HW VP8 decoder");
}
Log.d(TAG, "Java initDecode: " + width + " x " + height +
". Color: 0x" + Integer.toHexString(properties.colorFormat) +
". Use Surface: " + useSurface );
". Use Surface: " + useSurface + ". Use SW codec: " + useSwCodec);
if (sharedContext != null) {
Log.d(TAG, "Decoder shared EGL Context: " + sharedContext);
}