Better handling of error condition in MediaCodecVideoEncoder.
BUG=b/36082499 Review-Url: https://codereview.webrtc.org/2748123002 Cr-Commit-Position: refs/heads/master@{#17246}
This commit is contained in:
@ -13,7 +13,6 @@ package org.webrtc;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -211,7 +210,7 @@ public class ThreadUtils {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static StackTraceElement[] concatStackTraces(
|
static StackTraceElement[] concatStackTraces(
|
||||||
StackTraceElement[] inner, StackTraceElement[] outer) {
|
StackTraceElement[] inner, StackTraceElement[] outer) {
|
||||||
final StackTraceElement[] combined = new StackTraceElement[inner.length + outer.length];
|
final StackTraceElement[] combined = new StackTraceElement[inner.length + outer.length];
|
||||||
System.arraycopy(inner, 0, combined, 0, inner.length);
|
System.arraycopy(inner, 0, combined, 0, inner.length);
|
||||||
|
|||||||
@ -454,6 +454,7 @@ public class MediaCodecVideoEncoder {
|
|||||||
this.type = type;
|
this.type = type;
|
||||||
if (mediaCodec == null) {
|
if (mediaCodec == null) {
|
||||||
Logging.e(TAG, "Can not create media encoder");
|
Logging.e(TAG, "Can not create media encoder");
|
||||||
|
release();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
|
mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
|
||||||
@ -471,6 +472,7 @@ public class MediaCodecVideoEncoder {
|
|||||||
|
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
Logging.e(TAG, "initEncode failed", e);
|
Logging.e(TAG, "initEncode failed", e);
|
||||||
|
release();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -544,6 +546,13 @@ public class MediaCodecVideoEncoder {
|
|||||||
Logging.d(TAG, "Java releaseEncoder");
|
Logging.d(TAG, "Java releaseEncoder");
|
||||||
checkOnMediaCodecThread();
|
checkOnMediaCodecThread();
|
||||||
|
|
||||||
|
class CaughtException {
|
||||||
|
Exception e;
|
||||||
|
}
|
||||||
|
final CaughtException caughtException = new CaughtException();
|
||||||
|
boolean stopHung = false;
|
||||||
|
|
||||||
|
if (mediaCodec != null) {
|
||||||
// Run Mediacodec stop() and release() on separate thread since sometime
|
// Run Mediacodec stop() and release() on separate thread since sometime
|
||||||
// Mediacodec.stop() may hang.
|
// Mediacodec.stop() may hang.
|
||||||
final CountDownLatch releaseDone = new CountDownLatch(1);
|
final CountDownLatch releaseDone = new CountDownLatch(1);
|
||||||
@ -551,14 +560,20 @@ public class MediaCodecVideoEncoder {
|
|||||||
Runnable runMediaCodecRelease = new Runnable() {
|
Runnable runMediaCodecRelease = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
|
||||||
Logging.d(TAG, "Java releaseEncoder on release thread");
|
Logging.d(TAG, "Java releaseEncoder on release thread");
|
||||||
|
try {
|
||||||
mediaCodec.stop();
|
mediaCodec.stop();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logging.e(TAG, "Media encoder stop failed", e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
mediaCodec.release();
|
mediaCodec.release();
|
||||||
Logging.d(TAG, "Java releaseEncoder on release thread done");
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logging.e(TAG, "Media encoder release failed", e);
|
Logging.e(TAG, "Media encoder release failed", e);
|
||||||
|
caughtException.e = e;
|
||||||
}
|
}
|
||||||
|
Logging.d(TAG, "Java releaseEncoder on release thread done");
|
||||||
|
|
||||||
releaseDone.countDown();
|
releaseDone.countDown();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -566,14 +581,12 @@ public class MediaCodecVideoEncoder {
|
|||||||
|
|
||||||
if (!ThreadUtils.awaitUninterruptibly(releaseDone, MEDIA_CODEC_RELEASE_TIMEOUT_MS)) {
|
if (!ThreadUtils.awaitUninterruptibly(releaseDone, MEDIA_CODEC_RELEASE_TIMEOUT_MS)) {
|
||||||
Logging.e(TAG, "Media encoder release timeout");
|
Logging.e(TAG, "Media encoder release timeout");
|
||||||
codecErrors++;
|
stopHung = true;
|
||||||
if (errorCallback != null) {
|
|
||||||
Logging.e(TAG, "Invoke codec error callback. Errors: " + codecErrors);
|
|
||||||
errorCallback.onMediaCodecVideoEncoderCriticalError(codecErrors);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaCodec = null;
|
mediaCodec = null;
|
||||||
|
}
|
||||||
|
|
||||||
mediaCodecThread = null;
|
mediaCodecThread = null;
|
||||||
if (drawer != null) {
|
if (drawer != null) {
|
||||||
drawer.release();
|
drawer.release();
|
||||||
@ -588,6 +601,25 @@ public class MediaCodecVideoEncoder {
|
|||||||
inputSurface = null;
|
inputSurface = null;
|
||||||
}
|
}
|
||||||
runningInstance = null;
|
runningInstance = null;
|
||||||
|
|
||||||
|
if (stopHung) {
|
||||||
|
codecErrors++;
|
||||||
|
if (errorCallback != null) {
|
||||||
|
Logging.e(TAG, "Invoke codec error callback. Errors: " + codecErrors);
|
||||||
|
errorCallback.onMediaCodecVideoEncoderCriticalError(codecErrors);
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Media encoder release timeout.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-throw any runtime exception caught inside the other thread. Since this is an invoke, add
|
||||||
|
// stack trace for the waiting thread as well.
|
||||||
|
if (caughtException.e != null) {
|
||||||
|
final RuntimeException runtimeException = new RuntimeException(caughtException.e);
|
||||||
|
runtimeException.setStackTrace(ThreadUtils.concatStackTraces(
|
||||||
|
caughtException.e.getStackTrace(), runtimeException.getStackTrace()));
|
||||||
|
throw runtimeException;
|
||||||
|
}
|
||||||
|
|
||||||
Logging.d(TAG, "Java releaseEncoder done");
|
Logging.d(TAG, "Java releaseEncoder done");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -456,6 +456,11 @@ bool MediaCodecVideoEncoder::EncodeTask::Run() {
|
|||||||
// about it and let the next app-called API method reveal the borkedness.
|
// about it and let the next app-called API method reveal the borkedness.
|
||||||
encoder_->DeliverPendingOutputs(jni);
|
encoder_->DeliverPendingOutputs(jni);
|
||||||
|
|
||||||
|
if (!encoder_) {
|
||||||
|
// Encoder can be destroyed in DeliverPendingOutputs.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Call log statistics here so it's called even if no frames are being
|
// Call log statistics here so it's called even if no frames are being
|
||||||
// delivered.
|
// delivered.
|
||||||
encoder_->LogStatistics(false);
|
encoder_->LogStatistics(false);
|
||||||
@ -545,8 +550,6 @@ int32_t MediaCodecVideoEncoder::InitEncodeInternal(int width,
|
|||||||
gof_idx_ = 0;
|
gof_idx_ = 0;
|
||||||
last_frame_received_ms_ = -1;
|
last_frame_received_ms_ = -1;
|
||||||
frames_received_since_last_key_ = kMinKeyFrameInterval;
|
frames_received_since_last_key_ = kMinKeyFrameInterval;
|
||||||
weak_factory_.reset(new rtc::WeakPtrFactory<MediaCodecVideoEncoder>(this));
|
|
||||||
encode_task_.reset(new EncodeTask(weak_factory_->GetWeakPtr()));
|
|
||||||
|
|
||||||
// We enforce no extra stride/padding in the format creation step.
|
// We enforce no extra stride/padding in the format creation step.
|
||||||
jobject j_video_codec_enum = JavaEnumFromIndexAndClassName(
|
jobject j_video_codec_enum = JavaEnumFromIndexAndClassName(
|
||||||
@ -620,6 +623,9 @@ int32_t MediaCodecVideoEncoder::InitEncodeInternal(int width,
|
|||||||
#endif
|
#endif
|
||||||
inited_ = true;
|
inited_ = true;
|
||||||
}
|
}
|
||||||
|
weak_factory_.reset(new rtc::WeakPtrFactory<MediaCodecVideoEncoder>(this));
|
||||||
|
encode_task_.reset(new EncodeTask(weak_factory_->GetWeakPtr()));
|
||||||
|
|
||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -884,6 +890,8 @@ int32_t MediaCodecVideoEncoder::Release() {
|
|||||||
ALOGD << "EncoderRelease: Frames received: " << frames_received_
|
ALOGD << "EncoderRelease: Frames received: " << frames_received_
|
||||||
<< ". Encoded: " << frames_encoded_
|
<< ". Encoded: " << frames_encoded_
|
||||||
<< ". Dropped: " << frames_dropped_media_encoder_;
|
<< ". Dropped: " << frames_dropped_media_encoder_;
|
||||||
|
encode_task_.reset(nullptr);
|
||||||
|
weak_factory_.reset(nullptr);
|
||||||
ScopedLocalRefFrame local_ref_frame(jni);
|
ScopedLocalRefFrame local_ref_frame(jni);
|
||||||
for (size_t i = 0; i < input_buffers_.size(); ++i)
|
for (size_t i = 0; i < input_buffers_.size(); ++i)
|
||||||
jni->DeleteGlobalRef(input_buffers_[i]);
|
jni->DeleteGlobalRef(input_buffers_[i]);
|
||||||
@ -901,8 +909,6 @@ int32_t MediaCodecVideoEncoder::Release() {
|
|||||||
inited_ = false;
|
inited_ = false;
|
||||||
}
|
}
|
||||||
use_surface_ = false;
|
use_surface_ = false;
|
||||||
encode_task_.reset(nullptr);
|
|
||||||
weak_factory_.reset(nullptr);
|
|
||||||
ALOGD << "EncoderRelease done.";
|
ALOGD << "EncoderRelease done.";
|
||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user