Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1362)

Unified Diff: content/common/gpu/media/android_video_decode_accelerator.cc

Issue 1963773003: Merge to M51: Various fixes to prevent playback hang on MotoX. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@2704
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 e4f45ca73b36cf7d2a11087c44e23699b25a2b3e..5316dcc9367668578e4802a3715281dd1b7e214a 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"
@@ -309,6 +310,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),
@@ -495,13 +497,17 @@ void AndroidVideoDecodeAccelerator::SetCdm(int cdm_id) {
void AndroidVideoDecodeAccelerator::DoIOTask(bool start_timer) {
DCHECK(thread_checker_.CalledOnValidThread());
TRACE_EVENT0("media", "AVDA::DoIOTask");
- if (state_ == ERROR || state_ == WAITING_FOR_CODEC) {
+ if (state_ == ERROR || state_ == WAITING_FOR_CODEC)
return;
- }
- bool did_work = QueueInput();
- while (DequeueOutput())
- did_work = true;
+ strategy_->MaybeRenderEarly();
+ bool did_work = false, did_input = false, did_output = false;
+ do {
+ did_input = QueueInput();
+ did_output = DequeueOutput();
+ if (did_input || did_output)
+ did_work = true;
+ } while (did_input || did_output);
ManageTimer(did_work || start_timer);
}
@@ -548,8 +554,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;
}
@@ -668,17 +672,32 @@ 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 and destroy.
+ // Instead, run the drain completion task.
+ if (IsDrainingForResetOrDestroy()) {
+ DVLOG(1) << __FUNCTION__ << ": error while codec draining";
+ state_ = ERROR;
+ OnDrainCompleted();
+ } 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: {
+ // An OUTPUT_FORMAT_CHANGED is not reported after flush() if the frame
+ // size does not change. Therefore we have to keep track on the format
+ // even if draining, unless we are draining for destroy.
+ if (drain_type_ == DRAIN_FOR_DESTROY)
+ return true; // ignore
+
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();
@@ -718,28 +737,15 @@ bool AndroidVideoDecodeAccelerator::DequeueOutput() {
} while (buf_index < 0);
if (eos) {
- DVLOG(3) << __FUNCTION__ << ": Resetting codec state after EOS";
-
- // If we were waiting for an EOS, clear the state and reset the MediaCodec
- // as normal. Otherwise, enter the ERROR state which will force destruction
- // of MediaCodec during ResetCodecState().
- //
- // 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()));
- }
+ OnDrainCompleted();
return false;
}
+ if (IsDrainingForResetOrDestroy()) {
+ 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
@@ -820,19 +826,20 @@ void AndroidVideoDecodeAccelerator::SendDecodedFrameToClient(
size_changed = true;
}
- // Connect the PictureBuffer to the decoded frame, via whatever
- // mechanism the strategy likes.
- strategy_->UseCodecBufferForPictureBuffer(codec_buffer_index, i->second);
-
const bool allow_overlay = strategy_->ArePicturesOverlayable();
-
media::Picture picture(picture_buffer_id, bitstream_id, gfx::Rect(size_),
allow_overlay);
picture.set_size_changed(size_changed);
- base::MessageLoop::current()->PostTask(
- FROM_HERE, base::Bind(&AndroidVideoDecodeAccelerator::NotifyPictureReady,
- weak_this_factory_.GetWeakPtr(), picture));
+ // Notify picture ready before calling UseCodecBufferForPictureBuffer() since
+ // that process may be slow and shouldn't delay delivery of the frame to the
+ // renderer. The picture is only used on the same thread as this method is
+ // called, so it is safe to do this.
+ NotifyPictureReady(picture);
+
+ // Connect the PictureBuffer to the decoded frame, via whatever mechanism the
+ // strategy likes.
+ strategy_->UseCodecBufferForPictureBuffer(codec_buffer_index, i->second);
}
void AndroidVideoDecodeAccelerator::Decode(
@@ -868,9 +875,11 @@ 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(
@@ -926,9 +935,10 @@ void AndroidVideoDecodeAccelerator::ReusePictureBuffer(
}
void AndroidVideoDecodeAccelerator::Flush() {
+ DVLOG(1) << __FUNCTION__;
DCHECK(thread_checker_.CalledOnValidThread());
- DecodeBuffer(media::BitstreamBuffer(-1, base::SharedMemoryHandle(), 0));
+ StartCodecDrain(DRAIN_FOR_FLUSH);
}
void AndroidVideoDecodeAccelerator::ConfigureMediaCodecAsynchronously() {
@@ -949,7 +959,7 @@ void AndroidVideoDecodeAccelerator::ConfigureMediaCodecAsynchronously() {
// strategy to forget the codec avoids this.
if (media_codec_) {
media_codec_.reset();
- strategy_->CodecChanged(nullptr, output_picture_buffers_);
+ strategy_->CodecChanged(nullptr);
}
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
@@ -1001,7 +1011,7 @@ void AndroidVideoDecodeAccelerator::OnCodecConfigured(
// Record one instance of the codec being initialized.
RecordFormatChangedMetric(FormatChangedValue::CodecInitialized);
- strategy_->CodecChanged(media_codec_.get(), output_picture_buffers_);
+ strategy_->CodecChanged(media_codec_.get());
// If we are supposed to notify that initialization is complete, then do so
// now. Otherwise, this is a reconfiguration.
@@ -1020,13 +1030,76 @@ void AndroidVideoDecodeAccelerator::OnCodecConfigured(
ManageTimer(true);
}
-void AndroidVideoDecodeAccelerator::ResetCodecState() {
+void AndroidVideoDecodeAccelerator::StartCodecDrain(DrainType drain_type) {
+ DVLOG(2) << __FUNCTION__ << " drain_type:" << drain_type;
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // We assume that DRAIN_FOR_FLUSH and DRAIN_FOR_RESET cannot come while
+ // another drain request is present, but DRAIN_FOR_DESTROY can.
+ DCHECK_NE(drain_type, DRAIN_TYPE_NONE);
+ DCHECK(drain_type_ == DRAIN_TYPE_NONE || drain_type == DRAIN_FOR_DESTROY)
+ << "Unexpected StartCodecDrain() with drain type " << drain_type
+ << " while already draining with drain type " << drain_type_;
+
+ const bool enqueue_eos = drain_type_ == DRAIN_TYPE_NONE;
+ drain_type_ = drain_type;
+
+ if (enqueue_eos)
+ DecodeBuffer(media::BitstreamBuffer(-1, base::SharedMemoryHandle(), 0));
+}
+
+bool AndroidVideoDecodeAccelerator::IsDrainingForResetOrDestroy() const {
+ return drain_type_ == DRAIN_FOR_RESET || drain_type_ == DRAIN_FOR_DESTROY;
+}
+
+void AndroidVideoDecodeAccelerator::OnDrainCompleted() {
+ DVLOG(2) << __FUNCTION__;
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // If we were waiting for an EOS, clear the state and reset the MediaCodec
+ // as normal. Otherwise, enter the ERROR state which will force destruction
+ // of MediaCodec during ResetCodecState().
+ //
+ // 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.
+
+ switch (drain_type_) {
+ case DRAIN_TYPE_NONE:
+ // Unexpected EOS.
+ state_ = ERROR;
+ ResetCodecState(base::Closure());
+ break;
+ case DRAIN_FOR_FLUSH:
+ ResetCodecState(media::BindToCurrentLoop(
+ base::Bind(&AndroidVideoDecodeAccelerator::NotifyFlushDone,
+ weak_this_factory_.GetWeakPtr())));
+ break;
+ case DRAIN_FOR_RESET:
+ ResetCodecState(media::BindToCurrentLoop(
+ base::Bind(&AndroidVideoDecodeAccelerator::NotifyResetDone,
+ weak_this_factory_.GetWeakPtr())));
+ break;
+ case DRAIN_FOR_DESTROY:
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&AndroidVideoDecodeAccelerator::ActualDestroy,
+ weak_this_factory_.GetWeakPtr()));
+ break;
+ }
+ drain_type_ = DRAIN_TYPE_NONE;
+}
+
+void AndroidVideoDecodeAccelerator::ResetCodecState(
+ const base::Closure& done_cb) {
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 (!done_cb.is_null())
+ done_cb.Run();
return;
+ }
bitstream_buffers_in_decoder_.clear();
@@ -1039,8 +1112,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
@@ -1055,25 +1128,28 @@ 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();
// Since we just flushed all the output buffers, make sure that nothing is
// using them.
- strategy_->CodecChanged(media_codec_.get(), output_picture_buffers_);
+ strategy_->CodecChanged(media_codec_.get());
} else {
DVLOG(3) << __FUNCTION__
<< " Deleting the MediaCodec and creating a new one.";
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 (!done_cb.is_null())
+ done_cb.Run();
}
void AndroidVideoDecodeAccelerator::Reset() {
+ DVLOG(1) << __FUNCTION__;
DCHECK(thread_checker_.CalledOnValidThread());
TRACE_EVENT0("media", "AVDA::Reset");
@@ -1094,16 +1170,23 @@ 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()));
+ // 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) {
+ // Postpone ResetCodecState() after the drain.
+ StartCodecDrain(DRAIN_FOR_RESET);
+ } else {
+ ResetCodecState(media::BindToCurrentLoop(
+ base::Bind(&AndroidVideoDecodeAccelerator::NotifyResetDone,
+ weak_this_factory_.GetWeakPtr())));
+ }
}
void AndroidVideoDecodeAccelerator::Destroy() {
+ DVLOG(1) << __FUNCTION__;
DCHECK(thread_checker_.CalledOnValidThread());
bool have_context = make_context_current_cb_.Run();
@@ -1119,6 +1202,26 @@ 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.
+ StartCodecDrain(DRAIN_FOR_DESTROY);
+ } else {
+ ActualDestroy();
+ }
+}
+
+void AndroidVideoDecodeAccelerator::ActualDestroy() {
+ DVLOG(1) << __FUNCTION__;
+ DCHECK(thread_checker_.CalledOnValidThread());
+
// 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.
@@ -1225,25 +1328,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(
@@ -1254,7 +1362,8 @@ void AndroidVideoDecodeAccelerator::NotifyError(
if (token != error_sequence_token_)
return;
- client_->NotifyError(error);
+ if (client_)
+ client_->NotifyError(error);
}
void AndroidVideoDecodeAccelerator::ManageTimer(bool did_work) {
« no previous file with comments | « content/common/gpu/media/android_video_decode_accelerator.h ('k') | content/common/gpu/media/avda_codec_image.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698