A few fixes to avoid crash in HW codec on device orientation change.

- Fix video encoder Reset() function to avoid setting codec
resolution to zero.
- Follow SW codec implementation and do not crash when frame
with the resolution different from the encoder resolution arrives.
Instead wait for at least 3 frames with new resolution and
re-initialize the codec. HW codec reset may take much longer
than SW codec, so these 3 frames threshold avoids resetting
codec when outstanding camera frame captured from previous device
orientation arrives.
- Plus some minor changes to make encoder reset/release
implementation closer to decoder implementation.

BUG=
R=tkchin@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7230 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
glaznev@webrtc.org 2014-09-18 23:01:03 +00:00
parent 143ffa4bd5
commit 0b435ba597

View File

@ -1291,9 +1291,6 @@ class MediaCodecVideoEncoder : public webrtc::VideoEncoder,
int32_t ReleaseOnCodecThread(); int32_t ReleaseOnCodecThread();
int32_t SetRatesOnCodecThread(uint32_t new_bit_rate, uint32_t frame_rate); int32_t SetRatesOnCodecThread(uint32_t new_bit_rate, uint32_t frame_rate);
// Reset parameters valid between InitEncode() & Release() (see below).
void ResetParameters(JNIEnv* jni);
// Helper accessors for MediaCodecVideoEncoder$OutputBufferInfo members. // Helper accessors for MediaCodecVideoEncoder$OutputBufferInfo members.
int GetOutputBufferInfoIndex(JNIEnv* jni, jobject j_output_buffer_info); int GetOutputBufferInfoIndex(JNIEnv* jni, jobject j_output_buffer_info);
jobject GetOutputBufferInfoBuffer(JNIEnv* jni, jobject j_output_buffer_info); jobject GetOutputBufferInfoBuffer(JNIEnv* jni, jobject j_output_buffer_info);
@ -1339,6 +1336,7 @@ class MediaCodecVideoEncoder : public webrtc::VideoEncoder,
int64_t current_timestamp_us_; // Current frame timestamps in us. int64_t current_timestamp_us_; // Current frame timestamps in us.
int frames_received_; // Number of frames received by encoder. int frames_received_; // Number of frames received by encoder.
int frames_dropped_; // Number of frames dropped by encoder. int frames_dropped_; // Number of frames dropped by encoder.
int frames_resolution_update_; // Number of frames with new codec resolution.
int frames_in_queue_; // Number of frames in encoder queue. int frames_in_queue_; // Number of frames in encoder queue.
int64_t start_time_ms_; // Start time for statistics. int64_t start_time_ms_; // Start time for statistics.
int current_frames_; // Number of frames in the current statistics interval. int current_frames_; // Number of frames in the current statistics interval.
@ -1360,24 +1358,24 @@ class MediaCodecVideoEncoder : public webrtc::VideoEncoder,
}; };
MediaCodecVideoEncoder::~MediaCodecVideoEncoder() { MediaCodecVideoEncoder::~MediaCodecVideoEncoder() {
// We depend on ResetParameters() to ensure no more callbacks to us after we // Call Release() to ensure no more callbacks to us after we are deleted.
// are deleted, so assert it here. Release();
CHECK(width_ == 0) << "Release() should have been called";
} }
MediaCodecVideoEncoder::MediaCodecVideoEncoder(JNIEnv* jni) MediaCodecVideoEncoder::MediaCodecVideoEncoder(JNIEnv* jni)
: callback_(NULL), : callback_(NULL),
codec_thread_(new Thread()), inited_(false),
j_media_codec_video_encoder_class_( codec_thread_(new Thread()),
jni, j_media_codec_video_encoder_class_(
FindClass(jni, "org/webrtc/MediaCodecVideoEncoder")), jni,
j_media_codec_video_encoder_( FindClass(jni, "org/webrtc/MediaCodecVideoEncoder")),
jni, j_media_codec_video_encoder_(
jni->NewObject(*j_media_codec_video_encoder_class_, jni,
GetMethodID(jni, jni->NewObject(*j_media_codec_video_encoder_class_,
*j_media_codec_video_encoder_class_, GetMethodID(jni,
"<init>", *j_media_codec_video_encoder_class_,
"()V"))) { "<init>",
"()V"))) {
ScopedLocalRefFrame local_ref_frame(jni); ScopedLocalRefFrame local_ref_frame(jni);
// It would be nice to avoid spinning up a new thread per MediaCodec, and // It would be nice to avoid spinning up a new thread per MediaCodec, and
// instead re-use e.g. the PeerConnectionFactory's |worker_thread_|, but bug // instead re-use e.g. the PeerConnectionFactory's |worker_thread_|, but bug
@ -1389,8 +1387,6 @@ MediaCodecVideoEncoder::MediaCodecVideoEncoder(JNIEnv* jni)
codec_thread_->SetName("MediaCodecVideoEncoder", NULL); codec_thread_->SetName("MediaCodecVideoEncoder", NULL);
CHECK(codec_thread_->Start()) << "Failed to start MediaCodecVideoEncoder"; CHECK(codec_thread_->Start()) << "Failed to start MediaCodecVideoEncoder";
ResetParameters(jni);
jclass j_output_buffer_info_class = jclass j_output_buffer_info_class =
FindClass(jni, "org/webrtc/MediaCodecVideoEncoder$OutputBufferInfo"); FindClass(jni, "org/webrtc/MediaCodecVideoEncoder$OutputBufferInfo");
j_init_encode_method_ = GetMethodID(jni, j_init_encode_method_ = GetMethodID(jni,
@ -1486,6 +1482,9 @@ void MediaCodecVideoEncoder::OnMessage(rtc::Message* msg) {
CHECK(!msg->message_id) << "Unexpected message!"; CHECK(!msg->message_id) << "Unexpected message!";
CHECK(!msg->pdata) << "Unexpected message!"; CHECK(!msg->pdata) << "Unexpected message!";
CheckOnCodecThread(); CheckOnCodecThread();
if (!inited_) {
return;
}
// It would be nice to recover from a failure here if one happened, but it's // It would be nice to recover from a failure here if one happened, but it's
// unclear how to signal such a failure to the app, so instead we stay silent // unclear how to signal such a failure to the app, so instead we stay silent
@ -1503,8 +1502,8 @@ void MediaCodecVideoEncoder::ResetCodec() {
ALOGE("ResetCodec"); ALOGE("ResetCodec");
if (Release() != WEBRTC_VIDEO_CODEC_OK || if (Release() != WEBRTC_VIDEO_CODEC_OK ||
codec_thread_->Invoke<int32_t>(Bind( codec_thread_->Invoke<int32_t>(Bind(
&MediaCodecVideoEncoder::InitEncodeOnCodecThread, this, 0, 0, 0, 0)) &MediaCodecVideoEncoder::InitEncodeOnCodecThread, this,
!= WEBRTC_VIDEO_CODEC_OK) { width_, height_, 0, 0)) != WEBRTC_VIDEO_CODEC_OK) {
// TODO(fischman): wouldn't it be nice if there was a way to gracefully // TODO(fischman): wouldn't it be nice if there was a way to gracefully
// degrade to a SW encoder at this point? There isn't one AFAICT :( // degrade to a SW encoder at this point? There isn't one AFAICT :(
// https://code.google.com/p/webrtc/issues/detail?id=2920 // https://code.google.com/p/webrtc/issues/detail?id=2920
@ -1516,12 +1515,13 @@ int32_t MediaCodecVideoEncoder::InitEncodeOnCodecThread(
CheckOnCodecThread(); CheckOnCodecThread();
JNIEnv* jni = AttachCurrentThreadIfNeeded(); JNIEnv* jni = AttachCurrentThreadIfNeeded();
ScopedLocalRefFrame local_ref_frame(jni); ScopedLocalRefFrame local_ref_frame(jni);
ALOGD("InitEncodeOnCodecThread %d x %d. Fps: %d", width, height, fps);
if (width == 0) { ALOGD("InitEncodeOnCodecThread %d x %d. Bitrate: %d kbps. Fps: %d",
width = width_; width, height, kbps, fps);
height = height_; if (kbps == 0) {
kbps = last_set_bitrate_kbps_; kbps = last_set_bitrate_kbps_;
}
if (fps == 0) {
fps = last_set_fps_; fps = last_set_fps_;
} }
@ -1532,6 +1532,7 @@ int32_t MediaCodecVideoEncoder::InitEncodeOnCodecThread(
yuv_size_ = width_ * height_ * 3 / 2; yuv_size_ = width_ * height_ * 3 / 2;
frames_received_ = 0; frames_received_ = 0;
frames_dropped_ = 0; frames_dropped_ = 0;
frames_resolution_update_ = 0;
frames_in_queue_ = 0; frames_in_queue_ = 0;
current_timestamp_us_ = 0; current_timestamp_us_ = 0;
start_time_ms_ = GetCurrentTimeMs(); start_time_ms_ = GetCurrentTimeMs();
@ -1543,6 +1544,7 @@ int32_t MediaCodecVideoEncoder::InitEncodeOnCodecThread(
timestamps_.clear(); timestamps_.clear();
render_times_ms_.clear(); render_times_ms_.clear();
frame_rtc_times_ms_.clear(); frame_rtc_times_ms_.clear();
drop_next_input_frame_ = false;
// We enforce no extra stride/padding in the format creation step. // We enforce no extra stride/padding in the format creation step.
jobjectArray input_buffers = reinterpret_cast<jobjectArray>( jobjectArray input_buffers = reinterpret_cast<jobjectArray>(
jni->CallObjectMethod(*j_media_codec_video_encoder_, jni->CallObjectMethod(*j_media_codec_video_encoder_,
@ -1595,6 +1597,9 @@ int32_t MediaCodecVideoEncoder::EncodeOnCodecThread(
JNIEnv* jni = AttachCurrentThreadIfNeeded(); JNIEnv* jni = AttachCurrentThreadIfNeeded();
ScopedLocalRefFrame local_ref_frame(jni); ScopedLocalRefFrame local_ref_frame(jni);
if (!inited_) {
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
}
frames_received_++; frames_received_++;
if (!DeliverPendingOutputs(jni)) { if (!DeliverPendingOutputs(jni)) {
ResetCodec(); ResetCodec();
@ -1608,8 +1613,20 @@ int32_t MediaCodecVideoEncoder::EncodeOnCodecThread(
} }
CHECK(frame_types->size() == 1) << "Unexpected stream count"; CHECK(frame_types->size() == 1) << "Unexpected stream count";
CHECK(frame.width() == width_) << "Unexpected resolution change"; if (frame.width() != width_ || frame.height() != height_) {
CHECK(frame.height() == height_) << "Unexpected resolution change"; frames_resolution_update_++;
ALOGD("Unexpected frame resolution change from %d x %d to %d x %d",
width_, height_, frame.width(), frame.height());
if (frames_resolution_update_ > 3) {
// Reset codec if we received more than 3 frames with new resolution.
width_ = frame.width();
height_ = frame.height();
frames_resolution_update_ = 0;
ResetCodec();
}
return WEBRTC_VIDEO_CODEC_OK;
}
frames_resolution_update_ = 0;
bool key_frame = frame_types->front() != webrtc::kDeltaFrame; bool key_frame = frame_types->front() != webrtc::kDeltaFrame;
@ -1691,8 +1708,9 @@ int32_t MediaCodecVideoEncoder::RegisterEncodeCompleteCallbackOnCodecThread(
} }
int32_t MediaCodecVideoEncoder::ReleaseOnCodecThread() { int32_t MediaCodecVideoEncoder::ReleaseOnCodecThread() {
if (!inited_) if (!inited_) {
return WEBRTC_VIDEO_CODEC_OK; return WEBRTC_VIDEO_CODEC_OK;
}
CheckOnCodecThread(); CheckOnCodecThread();
JNIEnv* jni = AttachCurrentThreadIfNeeded(); JNIEnv* jni = AttachCurrentThreadIfNeeded();
ALOGD("EncoderRelease: Frames received: %d. Frames dropped: %d.", ALOGD("EncoderRelease: Frames received: %d. Frames dropped: %d.",
@ -1702,8 +1720,9 @@ int32_t MediaCodecVideoEncoder::ReleaseOnCodecThread() {
jni->DeleteGlobalRef(input_buffers_[i]); jni->DeleteGlobalRef(input_buffers_[i]);
input_buffers_.clear(); input_buffers_.clear();
jni->CallVoidMethod(*j_media_codec_video_encoder_, j_release_method_); jni->CallVoidMethod(*j_media_codec_video_encoder_, j_release_method_);
ResetParameters(jni);
CHECK_EXCEPTION(jni); CHECK_EXCEPTION(jni);
rtc::MessageQueueManager::Clear(this);
inited_ = false;
return WEBRTC_VIDEO_CODEC_OK; return WEBRTC_VIDEO_CODEC_OK;
} }
@ -1734,17 +1753,6 @@ int32_t MediaCodecVideoEncoder::SetRatesOnCodecThread(uint32_t new_bit_rate,
return WEBRTC_VIDEO_CODEC_OK; return WEBRTC_VIDEO_CODEC_OK;
} }
void MediaCodecVideoEncoder::ResetParameters(JNIEnv* jni) {
rtc::MessageQueueManager::Clear(this);
width_ = 0;
height_ = 0;
yuv_size_ = 0;
drop_next_input_frame_ = false;
inited_ = false;
CHECK(input_buffers_.empty())
<< "ResetParameters called while holding input_buffers_!";
}
int MediaCodecVideoEncoder::GetOutputBufferInfoIndex( int MediaCodecVideoEncoder::GetOutputBufferInfoIndex(
JNIEnv* jni, JNIEnv* jni,
jobject j_output_buffer_info) { jobject j_output_buffer_info) {
@ -2051,21 +2059,21 @@ int MediaCodecVideoDecoder::SetAndroidObjects(JNIEnv* jni,
return 0; return 0;
} }
MediaCodecVideoDecoder::MediaCodecVideoDecoder(JNIEnv* jni) : MediaCodecVideoDecoder::MediaCodecVideoDecoder(JNIEnv* jni)
key_frame_required_(true), : key_frame_required_(true),
inited_(false), inited_(false),
use_surface_(HW_DECODER_USE_SURFACE), use_surface_(HW_DECODER_USE_SURFACE),
codec_thread_(new Thread()), codec_thread_(new Thread()),
j_media_codec_video_decoder_class_( j_media_codec_video_decoder_class_(
jni, jni,
FindClass(jni, "org/webrtc/MediaCodecVideoDecoder")), FindClass(jni, "org/webrtc/MediaCodecVideoDecoder")),
j_media_codec_video_decoder_( j_media_codec_video_decoder_(
jni, jni,
jni->NewObject(*j_media_codec_video_decoder_class_, jni->NewObject(*j_media_codec_video_decoder_class_,
GetMethodID(jni, GetMethodID(jni,
*j_media_codec_video_decoder_class_, *j_media_codec_video_decoder_class_,
"<init>", "<init>",
"()V"))) { "()V"))) {
ScopedLocalRefFrame local_ref_frame(jni); ScopedLocalRefFrame local_ref_frame(jni);
codec_thread_->SetName("MediaCodecVideoDecoder", NULL); codec_thread_->SetName("MediaCodecVideoDecoder", NULL);
CHECK(codec_thread_->Start()) << "Failed to start MediaCodecVideoDecoder"; CHECK(codec_thread_->Start()) << "Failed to start MediaCodecVideoDecoder";
@ -2123,6 +2131,7 @@ MediaCodecVideoDecoder::MediaCodecVideoDecoder(JNIEnv* jni) :
} }
MediaCodecVideoDecoder::~MediaCodecVideoDecoder() { MediaCodecVideoDecoder::~MediaCodecVideoDecoder() {
// Call Release() to ensure no more callbacks to us after we are deleted.
Release(); Release();
} }
@ -2233,6 +2242,7 @@ int32_t MediaCodecVideoDecoder::ReleaseOnCodecThread() {
} }
jni->CallVoidMethod(*j_media_codec_video_decoder_, j_release_method_); jni->CallVoidMethod(*j_media_codec_video_decoder_, j_release_method_);
CHECK_EXCEPTION(jni); CHECK_EXCEPTION(jni);
rtc::MessageQueueManager::Clear(this);
inited_ = false; inited_ = false;
return WEBRTC_VIDEO_CODEC_OK; return WEBRTC_VIDEO_CODEC_OK;
} }