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:
parent
4b133da5fd
commit
25cc745d6b
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user