Index: content/common/gpu/media/android_video_decode_accelerator.cc |
diff --git a/content/common/gpu/media/android_video_decode_accelerator.cc b/content/common/gpu/media/android_video_decode_accelerator.cc |
index ccc3ef84639cecd268370fae7d3dc1ad9fdc03ef..bd79478d8097c11704072f3aef53f6415cb6e4c0 100644 |
--- a/content/common/gpu/media/android_video_decode_accelerator.cc |
+++ b/content/common/gpu/media/android_video_decode_accelerator.cc |
@@ -10,6 +10,7 @@ |
#include "base/auto_reset.h" |
#include "base/bind.h" |
#include "base/bind_helpers.h" |
+#include "base/callback_helpers.h" |
#include "base/command_line.h" |
#include "base/lazy_instance.h" |
#include "base/logging.h" |
@@ -292,6 +293,7 @@ AndroidVideoDecodeAccelerator::AndroidVideoDecodeAccelerator( |
is_encrypted_(false), |
state_(NO_ERROR), |
picturebuffers_requested_(false), |
+ drain_type_(DRAIN_TYPE_NONE), |
media_drm_bridge_cdm_context_(nullptr), |
cdm_registration_id_(0), |
pending_input_buf_index_(-1), |
@@ -528,8 +530,6 @@ bool AndroidVideoDecodeAccelerator::QueueInput() { |
TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", |
pending_bitstream_buffers_.size()); |
- DCHECK_NE(state_, ERROR); |
- state_ = WAITING_FOR_EOS; |
media_codec_->QueueEOS(input_buf_index); |
return true; |
} |
@@ -648,17 +648,29 @@ bool AndroidVideoDecodeAccelerator::DequeueOutput() { |
switch (status) { |
case media::MEDIA_CODEC_ERROR: |
- POST_ERROR(PLATFORM_FAILURE, "DequeueOutputBuffer failed."); |
+ // Do not post an error if we are draining for reset. Instead, try |
watk
2016/04/18 21:12:18
Remove "try" since we are unconditionally running
Tima Vaisburd
2016/04/20 01:54:19
Done.
|
+ // to run the drain completion callback. |
+ if (IsDrainingForReset()) { |
+ state_ = ERROR; |
+ DCHECK(!drain_completed_cb_.is_null()); |
+ base::ResetAndReturn(&drain_completed_cb_).Run(); |
+ } else { |
+ POST_ERROR(PLATFORM_FAILURE, "DequeueOutputBuffer failed."); |
+ } |
return false; |
case media::MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: |
return false; |
case media::MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: { |
+ if (IsDrainingForReset()) |
+ return true; |
+ |
if (media_codec_->GetOutputSize(&size_) != media::MEDIA_CODEC_OK) { |
POST_ERROR(PLATFORM_FAILURE, "GetOutputSize failed."); |
return false; |
} |
+ |
DVLOG(3) << __FUNCTION__ |
<< " OUTPUT_FORMAT_CHANGED, new size: " << size_.ToString(); |
@@ -707,19 +719,21 @@ bool AndroidVideoDecodeAccelerator::DequeueOutput() { |
// Some Android platforms seem to send an EOS buffer even when we're not |
// expecting it. In this case, destroy and reset the codec but don't notify |
// flush done since it violates the state machine. http://crbug.com/585959. |
- const bool was_waiting_for_eos = state_ == WAITING_FOR_EOS; |
- state_ = was_waiting_for_eos ? NO_ERROR : ERROR; |
- |
- ResetCodecState(); |
- // |media_codec_| might still be null. |
- if (was_waiting_for_eos) { |
- base::MessageLoop::current()->PostTask( |
- FROM_HERE, base::Bind(&AndroidVideoDecodeAccelerator::NotifyFlushDone, |
- weak_this_factory_.GetWeakPtr())); |
+ if (drain_completed_cb_.is_null()) { |
+ // Unexpected EOS. |
+ state_ = ERROR; |
+ ResetCodecState(base::Closure()); |
+ } else { |
+ base::ResetAndReturn(&drain_completed_cb_).Run(); |
} |
return false; |
} |
+ if (IsDrainingForReset()) { |
+ media_codec_->ReleaseOutputBuffer(buf_index, false); |
+ return true; |
+ } |
+ |
if (!picturebuffers_requested_) { |
// If, somehow, we get a decoded frame back before a FORMAT_CHANGED |
// message, then we might not have any picture buffers to use. This |
@@ -848,9 +862,10 @@ void AndroidVideoDecodeAccelerator::DecodeBuffer( |
} |
void AndroidVideoDecodeAccelerator::RequestPictureBuffers() { |
- client_->ProvidePictureBuffers(kNumPictureBuffers, 1, |
- strategy_->GetPictureBufferSize(), |
- strategy_->GetTextureTarget()); |
+ if (client_) |
+ client_->ProvidePictureBuffers(kNumPictureBuffers, 1, |
+ strategy_->GetPictureBufferSize(), |
+ strategy_->GetTextureTarget()); |
} |
void AndroidVideoDecodeAccelerator::AssignPictureBuffers( |
@@ -908,7 +923,13 @@ void AndroidVideoDecodeAccelerator::ReusePictureBuffer( |
void AndroidVideoDecodeAccelerator::Flush() { |
DCHECK(thread_checker_.CalledOnValidThread()); |
- DecodeBuffer(media::BitstreamBuffer(-1, base::SharedMemoryHandle(), 0)); |
+ base::Closure notification_cb = media::BindToCurrentLoop( |
+ base::Bind(&AndroidVideoDecodeAccelerator::NotifyFlushDone, |
+ weak_this_factory_.GetWeakPtr())); |
+ |
+ RequestDrain(DRAIN_FOR_FLUSH, |
+ base::Bind(&AndroidVideoDecodeAccelerator::ResetCodecState, |
+ weak_this_factory_.GetWeakPtr(), notification_cb)); |
} |
void AndroidVideoDecodeAccelerator::ConfigureMediaCodecAsynchronously() { |
@@ -996,13 +1017,36 @@ void AndroidVideoDecodeAccelerator::OnCodecConfigured( |
ManageTimer(true); |
} |
-void AndroidVideoDecodeAccelerator::ResetCodecState() { |
+void AndroidVideoDecodeAccelerator::RequestDrain( |
watk
2016/04/18 21:12:18
IMO RequestDrain makes it sound like there's a cha
Tima Vaisburd
2016/04/20 01:54:19
Renamed to StartCodecDrain(). I have to strong pre
|
+ DrainType drain_type, |
+ base::Closure drain_completed_cb) { |
watk
2016/04/18 21:12:18
I'm wondering if it would be clearer to structure
Tima Vaisburd
2016/04/20 01:54:19
Yes, thank you! I tried now, and I think this way
|
+ DVLOG(2) << __FUNCTION__ << " drain_type:" << drain_type; |
+ |
+ // We assume that DRAIN_FOR_FLUSH and DRAIN_FOR_RESET cannot come while |
+ // another drain request is present, but DRAIN_FOR_DESTROY can. |
+ DCHECK(drain_completed_cb_.is_null() || drain_type == DRAIN_FOR_DESTROY); |
+ |
+ drain_completed_cb_ = drain_completed_cb; |
+ drain_type_ = drain_type; |
+ DecodeBuffer(media::BitstreamBuffer(-1, base::SharedMemoryHandle(), 0)); |
watk
2016/04/18 21:12:18
If there's an EOS already queued for a DRAIN_FOR_F
Tima Vaisburd
2016/04/20 01:54:19
Done.
|
+} |
+ |
+bool AndroidVideoDecodeAccelerator::IsDrainingForReset() const { |
+ return !drain_completed_cb_.is_null() && |
+ (drain_type_ == DRAIN_FOR_RESET || drain_type_ == DRAIN_FOR_DESTROY); |
+} |
+ |
+void AndroidVideoDecodeAccelerator::ResetCodecState( |
+ base::Closure notification_cb) { |
watk
2016/04/18 21:12:18
I would be inclined to name this consistently with
Tima Vaisburd
2016/04/20 01:54:19
Done.
|
DCHECK(thread_checker_.CalledOnValidThread()); |
// If there is already a reset in flight, then that counts. This can really |
// only happen if somebody calls Reset. |
- if (state_ == WAITING_FOR_CODEC) |
+ if (state_ == WAITING_FOR_CODEC) { |
+ if (!notification_cb.is_null()) |
+ notification_cb.Run(); |
return; |
+ } |
bitstream_buffers_in_decoder_.clear(); |
@@ -1015,8 +1059,8 @@ void AndroidVideoDecodeAccelerator::ResetCodecState() { |
pending_input_buf_index_ = -1; |
} |
- if (state_ == WAITING_FOR_KEY) |
- state_ = NO_ERROR; |
+ const bool did_codec_error_happen = (state_ == ERROR); |
+ state_ = NO_ERROR; |
// We might increment error_sequence_token here to cancel any delayed errors, |
// but right now it's unclear that it's safe to do so. If we are in an error |
@@ -1031,7 +1075,7 @@ void AndroidVideoDecodeAccelerator::ResetCodecState() { |
// (b/8125974, b/8347958) so we must delete the MediaCodec and create a new |
// one. The full reconfigure is much slower and may cause visible freezing if |
// done mid-stream. |
- if (state_ == NO_ERROR && |
+ if (!did_codec_error_happen && |
base::android::BuildInfo::GetInstance()->sdk_int() >= 18) { |
DVLOG(3) << __FUNCTION__ << " Doing fast MediaCodec reset (flush)."; |
media_codec_->Reset(); |
@@ -1044,12 +1088,16 @@ void AndroidVideoDecodeAccelerator::ResetCodecState() { |
g_avda_timer.Pointer()->StopTimer(this); |
// Changing the codec will also notify the strategy to forget about any |
// output buffers it has currently. |
- state_ = NO_ERROR; |
ConfigureMediaCodecAsynchronously(); |
} |
+ |
+ if (!notification_cb.is_null()) |
+ notification_cb.Run(); |
} |
void AndroidVideoDecodeAccelerator::Reset() { |
+ DVLOG(1) << __FUNCTION__; |
+ |
DCHECK(thread_checker_.CalledOnValidThread()); |
TRACE_EVENT0("media", "AVDA::Reset"); |
@@ -1070,16 +1118,28 @@ void AndroidVideoDecodeAccelerator::Reset() { |
// Any error that is waiting to post can be ignored. |
error_sequence_token_++; |
- ResetCodecState(); |
+ DCHECK(strategy_); |
+ strategy_->ReleaseCodecBuffers(output_picture_buffers_); |
- // Note that |media_codec_| might not yet be ready, but we can still post |
- // this anyway. |
- base::MessageLoop::current()->PostTask( |
- FROM_HERE, base::Bind(&AndroidVideoDecodeAccelerator::NotifyResetDone, |
- weak_this_factory_.GetWeakPtr())); |
+ base::Closure notification_cb = media::BindToCurrentLoop( |
watk
2016/04/18 21:12:18
I don't think you need BindToCurrentLoop because t
Tima Vaisburd
2016/04/20 01:54:19
Removed BindToCurrentLoop. The original code alway
|
+ base::Bind(&AndroidVideoDecodeAccelerator::NotifyResetDone, |
+ weak_this_factory_.GetWeakPtr())); |
+ |
+ // Some VP8 files require complete MediaCodec drain before we can call |
+ // MediaCodec.flush() or MediaCodec.reset(). http://crbug.com/598963. |
+ if (codec_config_->codec_ == media::kCodecVP8) { |
+ // Postpone ResetCodecState() after the drain. |
+ RequestDrain(DRAIN_FOR_RESET, |
+ base::Bind(&AndroidVideoDecodeAccelerator::ResetCodecState, |
+ weak_this_factory_.GetWeakPtr(), notification_cb)); |
+ } else { |
+ ResetCodecState(notification_cb); |
+ } |
} |
void AndroidVideoDecodeAccelerator::Destroy() { |
+ DVLOG(1) << __FUNCTION__; |
+ |
watk
2016/04/18 21:12:18
nit: Remove empty line to keep the LOG and thread
Tima Vaisburd
2016/04/20 01:54:19
Done.
|
DCHECK(thread_checker_.CalledOnValidThread()); |
bool have_context = make_context_current_cb_.Run(); |
@@ -1095,6 +1155,27 @@ void AndroidVideoDecodeAccelerator::Destroy() { |
on_frame_available_handler_ = nullptr; |
} |
+ client_ = nullptr; |
+ |
+ // Some VP8 files require complete MediaCodec drain before we can call |
+ // MediaCodec.flush() or MediaCodec.reset(). http://crbug.com/598963. |
+ if (media_codec_ && codec_config_->codec_ == media::kCodecVP8) { |
+ // Clear pending_bitstream_buffers_. |
+ while (!pending_bitstream_buffers_.empty()) |
+ pending_bitstream_buffers_.pop(); |
+ |
+ // Postpone ActualDestroy after the drain. |
+ RequestDrain(DRAIN_FOR_DESTROY, |
+ media::BindToCurrentLoop( |
+ base::Bind(&AndroidVideoDecodeAccelerator::ActualDestroy, |
+ weak_this_factory_.GetWeakPtr()))); |
+ } else { |
+ ActualDestroy(); |
+ } |
+} |
+ |
+void AndroidVideoDecodeAccelerator::ActualDestroy() { |
+ DVLOG(1) << __FUNCTION__; |
// Note that async codec construction might still be in progress. In that |
// case, the codec will be deleted when it completes once we invalidate all |
// our weak refs. |
@@ -1201,25 +1282,30 @@ void AndroidVideoDecodeAccelerator::OnKeyAdded() { |
} |
void AndroidVideoDecodeAccelerator::NotifyInitializationComplete(bool success) { |
- client_->NotifyInitializationComplete(success); |
+ if (client_) |
+ client_->NotifyInitializationComplete(success); |
} |
void AndroidVideoDecodeAccelerator::NotifyPictureReady( |
const media::Picture& picture) { |
- client_->PictureReady(picture); |
+ if (client_) |
+ client_->PictureReady(picture); |
} |
void AndroidVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer( |
int input_buffer_id) { |
- client_->NotifyEndOfBitstreamBuffer(input_buffer_id); |
+ if (client_) |
+ client_->NotifyEndOfBitstreamBuffer(input_buffer_id); |
} |
void AndroidVideoDecodeAccelerator::NotifyFlushDone() { |
- client_->NotifyFlushDone(); |
+ if (client_) |
+ client_->NotifyFlushDone(); |
} |
void AndroidVideoDecodeAccelerator::NotifyResetDone() { |
- client_->NotifyResetDone(); |
+ if (client_) |
+ client_->NotifyResetDone(); |
} |
void AndroidVideoDecodeAccelerator::NotifyError( |
@@ -1230,7 +1316,8 @@ void AndroidVideoDecodeAccelerator::NotifyError( |
if (token != error_sequence_token_) |
return; |
- client_->NotifyError(error); |
+ if (client_) |
+ client_->NotifyError(error); |
} |
void AndroidVideoDecodeAccelerator::ManageTimer(bool did_work) { |