Commit 791fb7fd authored by Chris Watkins's avatar Chris Watkins Committed by Commit Bot

media: MediaCodecVideoDecoder now requests OverlayInfo updates

If overlays are supported by the device, MCVD now requests OverlayInfo
updates during lazy init and continues initialization when it gets the
first one. If overlays are not supported it skips making any calls to
AndroidVideoSurfaceChooser and uses a surface texture.

Bug: 660942
Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
Change-Id: I5fb4a008f7bf2932cec96570f6b5782a1d327474
Reviewed-on: https://chromium-review.googlesource.com/689416
Commit-Queue: Chris Watkins <watk@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Cr-Commit-Position: refs/heads/master@{#505892}
parent 1a21364c
......@@ -62,9 +62,8 @@ class MEDIA_GPU_EXPORT AndroidVideoSurfaceChooser {
// Sets the client callbacks to be called when a new surface choice is made.
// Must be called before UpdateState();
virtual void SetClientCallbacks(
UseOverlayCB use_overlay_cb,
UseSurfaceTextureCB use_surface_texture_cb) = 0;
virtual void SetClientCallbacks(UseOverlayCB use_overlay_cb,
UseSurfaceTextureCB use_surface_texture_cb);
// Updates the current state and makes a new surface choice with the new
// state. If |new_factory| is empty, the factory is left as-is. Otherwise,
......
......@@ -163,27 +163,16 @@ void MediaCodecVideoDecoder::Initialize(const VideoDecoderConfig& config,
bound_init_cb.Run(false);
return;
}
decoder_config_ = config;
if (first_init) {
if (!codec_allocator_->StartThread(this)) {
LOG(ERROR) << "Unable to start thread";
bound_init_cb.Run(false);
return;
}
}
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
if (config.codec() == kCodecH264)
ExtractSpsAndPps(config.extra_data(), &csd0_, &csd1_);
#endif
// We defer initialization of the Surface and MediaCodec until we
// receive a Decode() call to avoid consuming those resources in cases where
// we'll be destructed before getting a Decode(). Failure to initialize those
// resources will be reported as a decode error on the first decode.
// TODO(watk): Initialize the CDM before calling init_cb.
// Do the rest of the initialization lazily on the first decode.
// TODO(watk): Add CDM Support.
DCHECK(!cdm_context);
init_cb.Run(true);
}
......@@ -195,6 +184,11 @@ void MediaCodecVideoDecoder::OnKeyAdded() {
void MediaCodecVideoDecoder::StartLazyInit() {
DVLOG(2) << __func__;
lazy_init_pending_ = false;
if (!codec_allocator_->StartThread(this)) {
EnterTerminalState(State::kError);
return;
}
video_frame_factory_->Initialize(
gpu_task_runner_, get_stub_cb_,
base::Bind(&MediaCodecVideoDecoder::OnVideoFrameFactoryInitialized,
......@@ -209,25 +203,42 @@ void MediaCodecVideoDecoder::OnVideoFrameFactoryInitialized(
return;
}
surface_texture_bundle_ = new AVDASurfaceBundle(std::move(surface_texture));
InitializeSurfaceChooser();
// TODO(watk): This isn't sufficient. Overlays should also be disabled if
// we're using threaded texture mailboxes (http://crbug.com/582170).
// See gpu::GpuPreferences::enable_threaded_texture_mailboxes.
if (!device_info_->SupportsOverlaySurfaces()) {
OnSurfaceChosen(nullptr);
return;
}
// Request OverlayInfo updates. Initialization continues on the first one.
bool restart_for_transitions = device_info_->IsSetOutputSurfaceSupported();
std::move(request_overlay_info_cb_)
.Run(restart_for_transitions,
base::Bind(&MediaCodecVideoDecoder::OnOverlayInfoChanged,
weak_factory_.GetWeakPtr()));
}
void MediaCodecVideoDecoder::OnOverlayInfoChanged(
const OverlayInfo& overlay_info) {
DVLOG(2) << __func__;
DCHECK(device_info_->SupportsOverlaySurfaces());
if (InTerminalState())
return;
// TODO(watk): Handle frame_hidden like AVDA. Maybe even if in a terminal
// state.
// TODO(watk): Incorporate the other chooser_state_ signals.
bool overlay_changed = !overlay_info_.RefersToSameOverlayAs(overlay_info);
overlay_info_ = overlay_info;
// Only update surface chooser if it's initialized and the overlay changed.
if (state_ != State::kInitializing && overlay_changed)
surface_chooser_->UpdateState(CreateOverlayFactoryCb(), chooser_state_);
}
void MediaCodecVideoDecoder::InitializeSurfaceChooser() {
DVLOG(2) << __func__;
DCHECK_EQ(state_, State::kInitializing);
// Initialize |surface_chooser_| and wait for its decision. Note: the
// callback may be reentrant.
surface_chooser_->UpdateState(CreateOverlayFactoryCb(), chooser_state_);
chooser_state_.is_fullscreen = overlay_info_.is_fullscreen;
chooser_state_.is_frame_hidden = overlay_info_.is_frame_hidden;
surface_chooser_->UpdateState(
overlay_changed ? base::make_optional(CreateOverlayFactoryCb())
: base::nullopt,
chooser_state_);
}
void MediaCodecVideoDecoder::OnSurfaceChosen(
......@@ -338,12 +349,11 @@ void MediaCodecVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer,
}
pending_decodes_.emplace_back(buffer, std::move(decode_cb));
if (lazy_init_pending_) {
lazy_init_pending_ = false;
StartLazyInit();
if (state_ == State::kInitializing) {
if (lazy_init_pending_)
StartLazyInit();
return;
}
PumpCodec(true);
}
......@@ -603,8 +613,9 @@ void MediaCodecVideoDecoder::OnCodecDrained() {
void MediaCodecVideoDecoder::EnterTerminalState(State state) {
DVLOG(2) << __func__ << " " << static_cast<int>(state);
DCHECK(state == State::kError || state == State::kSurfaceDestroyed);
state_ = state;
DCHECK(InTerminalState());
// Cancel pending codec creation.
codec_allocator_weak_factory_.InvalidateWeakPtrs();
......@@ -618,6 +629,10 @@ void MediaCodecVideoDecoder::EnterTerminalState(State state) {
OnCodecDrained();
}
bool MediaCodecVideoDecoder::InTerminalState() {
return state_ == State::kSurfaceDestroyed || state_ == State::kError;
}
void MediaCodecVideoDecoder::ClearPendingDecodes(DecodeStatus status) {
for (auto& pending_decode : pending_decodes_)
pending_decode.decode_cb.Run(status);
......
......@@ -79,9 +79,6 @@ class MEDIA_GPU_EXPORT MediaCodecVideoDecoder
bool CanReadWithoutStalling() const override;
int GetMaxDecodeRequests() const override;
// Updates the current overlay info.
void OnOverlayInfoChanged(const OverlayInfo& overlay_info);
protected:
// Protected for testing.
~MediaCodecVideoDecoder() override;
......@@ -120,8 +117,8 @@ class MEDIA_GPU_EXPORT MediaCodecVideoDecoder
// accept buffers.
void OnKeyAdded();
// Initializes |surface_chooser_|.
void InitializeSurfaceChooser();
// Updates |surface_chooser_| with the new overlay info.
void OnOverlayInfoChanged(const OverlayInfo& overlay_info);
void OnSurfaceChosen(std::unique_ptr<AndroidOverlay> overlay);
void OnSurfaceDestroyed(AndroidOverlay* overlay);
......@@ -170,6 +167,7 @@ class MEDIA_GPU_EXPORT MediaCodecVideoDecoder
// Sets |state_| and does common teardown for the terminal states. |state_|
// must be either kSurfaceDestroyed or kError.
void EnterTerminalState(State state);
bool InTerminalState();
// Releases |codec_| if it's not null.
void ReleaseCodec();
......@@ -242,8 +240,7 @@ class MEDIA_GPU_EXPORT MediaCodecVideoDecoder
// The factory for creating VideoFrames from CodecOutputBuffers.
std::unique_ptr<VideoFrameFactory> video_frame_factory_;
// An optional factory callback for creating mojo AndroidOverlays. This must
// only be called on the GPU thread.
// An optional factory callback for creating mojo AndroidOverlays.
AndroidOverlayMojoFactoryCB overlay_factory_cb_;
DeviceInfo* device_info_;
......
......@@ -35,8 +35,6 @@ void OutputCb(const scoped_refptr<VideoFrame>&) {}
void OutputWithReleaseMailboxCb(VideoFrameFactory::ReleaseMailboxCB,
const scoped_refptr<VideoFrame>&) {}
void RequestOverlayInfoCb(bool, const ProvideOverlayInfoCB&) {}
std::unique_ptr<AndroidOverlay> CreateAndroidOverlayCb(
std::unique_ptr<service_manager::ServiceContextRef>,
const base::UnguessableToken&,
......@@ -116,7 +114,9 @@ class MediaCodecVideoDecoderTest : public testing::Test {
base::ThreadTaskRunnerHandle::Get(), base::Bind(&GetStubCb),
base::Bind(&OutputWithReleaseMailboxCb), device_info_.get(),
codec_allocator_.get(), std::move(surface_chooser),
base::Bind(&CreateAndroidOverlayCb), base::Bind(&RequestOverlayInfoCb),
base::Bind(&CreateAndroidOverlayCb),
base::Bind(&MediaCodecVideoDecoderTest::RequestOverlayInfoCb,
base::Unretained(this)),
std::move(video_frame_factory),
base::MakeUnique<MockServiceContextRef>());
mcvd_.reset(observable_mcvd);
......@@ -150,6 +150,9 @@ class MediaCodecVideoDecoderTest : public testing::Test {
VideoDecoderConfig config = TestVideoConfig::Large(kCodecH264)) {
Initialize(config);
mcvd_->Decode(fake_decoder_buffer_, decode_cb_.Get());
OverlayInfo info;
info.routing_token = base::UnguessableToken::Deserialize(1, 2);
provide_overlay_info_cb_.Run(info);
auto overlay_ptr = base::MakeUnique<MockAndroidOverlay>();
auto* overlay = overlay_ptr.get();
surface_chooser_->ProvideOverlay(std::move(overlay_ptr));
......@@ -162,6 +165,7 @@ class MediaCodecVideoDecoderTest : public testing::Test {
VideoDecoderConfig config = TestVideoConfig::Large(kCodecH264)) {
Initialize(config);
mcvd_->Decode(fake_decoder_buffer_, decode_cb_.Get());
provide_overlay_info_cb_.Run(OverlayInfo());
surface_chooser_->ProvideSurfaceTexture();
}
......@@ -178,6 +182,12 @@ class MediaCodecVideoDecoderTest : public testing::Test {
// it can be called after |mcvd_| is reset.
void PumpCodec() { mcvd_raw_->PumpCodec(false); }
void RequestOverlayInfoCb(
bool restart_for_transitions,
const ProvideOverlayInfoCB& provide_overlay_info_cb) {
provide_overlay_info_cb_ = provide_overlay_info_cb;
}
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
scoped_refptr<DecoderBuffer> fake_decoder_buffer_;
......@@ -188,6 +198,7 @@ class MediaCodecVideoDecoderTest : public testing::Test {
MockVideoFrameFactory* video_frame_factory_;
NiceMock<base::MockCallback<VideoDecoder::DecodeCB>> decode_cb_;
std::unique_ptr<DestructionObserver> destruction_observer_;
ProvideOverlayInfoCB provide_overlay_info_cb_;
// |mcvd_raw_| lets us call PumpCodec() even after |mcvd_| is dropped, for
// testing the teardown path.
......@@ -221,15 +232,34 @@ TEST_F(MediaCodecVideoDecoderTest, FirstDecodeTriggersFrameFactoryInit) {
mcvd_->Decode(fake_decoder_buffer_, decode_cb_.Get());
}
TEST_F(MediaCodecVideoDecoderTest, FirstDecodeTriggersSurfaceChooserInit) {
TEST_F(MediaCodecVideoDecoderTest,
FirstDecodeTriggersOverlayInfoRequestIfSupported) {
Initialize();
EXPECT_CALL(*surface_chooser_, MockUpdateState());
// Requesting overlay info sets this cb.
ASSERT_FALSE(provide_overlay_info_cb_);
mcvd_->Decode(fake_decoder_buffer_, decode_cb_.Get());
ASSERT_TRUE(provide_overlay_info_cb_);
}
TEST_F(MediaCodecVideoDecoderTest,
OverlayInfoIsNotRequestedIfOverlaysNotSupported) {
Initialize();
ON_CALL(*device_info_, SupportsOverlaySurfaces())
.WillByDefault(Return(false));
mcvd_->Decode(fake_decoder_buffer_, decode_cb_.Get());
ASSERT_FALSE(provide_overlay_info_cb_);
}
TEST_F(MediaCodecVideoDecoderTest, OverlayInfoDuringInitUpdatesSurfaceChooser) {
InitializeWithSurfaceTexture_OneDecodePending();
EXPECT_CALL(*surface_chooser_, MockUpdateState());
provide_overlay_info_cb_.Run(OverlayInfo());
}
TEST_F(MediaCodecVideoDecoderTest, CodecIsCreatedAfterSurfaceChosen) {
Initialize();
mcvd_->Decode(fake_decoder_buffer_, decode_cb_.Get());
provide_overlay_info_cb_.Run(OverlayInfo());
EXPECT_CALL(*codec_allocator_, MockCreateMediaCodecAsync(_, NotNull()));
surface_chooser_->ProvideSurfaceTexture();
}
......@@ -251,6 +281,14 @@ TEST_F(MediaCodecVideoDecoderTest, CodecCreationFailureIsAnError) {
codec_allocator_->ProvideNullCodecAsync();
}
TEST_F(MediaCodecVideoDecoderTest, CodecFailuresAreAnError) {
auto* codec = InitializeFully_OneDecodePending();
EXPECT_CALL(*codec, DequeueInputBuffer(_, _))
.WillOnce(Return(MEDIA_CODEC_ERROR));
EXPECT_CALL(decode_cb_, Run(DecodeStatus::DECODE_ERROR));
PumpCodec();
}
TEST_F(MediaCodecVideoDecoderTest, AfterInitCompletesTheCodecIsPolled) {
auto* codec = InitializeFully_OneDecodePending();
// Run a RunLoop until the first time the codec is polled for an available
......@@ -269,57 +307,46 @@ TEST_F(MediaCodecVideoDecoderTest, CodecIsReleasedOnDestruction) {
EXPECT_CALL(*codec_allocator_, MockReleaseMediaCodec(codec, _, _));
}
TEST_F(MediaCodecVideoDecoderTest,
SurfaceChooserNotInitializedWithOverlayFactory) {
InitializeWithSurfaceTexture_OneDecodePending();
// The surface chooser should not have an overlay factory because
// OnOverlayInfoChanged() was not called before it was initialized.
ASSERT_FALSE(surface_chooser_->factory_);
}
TEST_F(MediaCodecVideoDecoderTest,
SurfaceChooserInitializedWithOverlayFactory) {
Initialize();
TEST_F(MediaCodecVideoDecoderTest, SurfaceChooserIsUpdatedWithTheOverlay) {
InitializeFully_OneDecodePending();
OverlayInfo info;
info.routing_token = base::UnguessableToken::Deserialize(1, 2);
mcvd_->OnOverlayInfoChanged(info);
mcvd_->Decode(fake_decoder_buffer_, decode_cb_.Get());
// The surface chooser should have an overlay factory because
// OnOverlayInfoChanged() was called before it was initialized.
provide_overlay_info_cb_.Run(info);
// The surface chooser should have an overlay factory.
ASSERT_TRUE(surface_chooser_->factory_);
}
TEST_F(MediaCodecVideoDecoderTest,
OnOverlayInfoChangedIsValidBeforeInitialize) {
TEST_F(MediaCodecVideoDecoderTest, SurfaceChooserIsUpdatedOnOverlayChanges) {
InitializeWithSurfaceTexture_OneDecodePending();
EXPECT_CALL(*surface_chooser_, MockReplaceOverlayFactory(_)).Times(2);
OverlayInfo info;
info.routing_token = base::UnguessableToken::Deserialize(1, 2);
mcvd_->OnOverlayInfoChanged(info);
Initialize();
mcvd_->Decode(fake_decoder_buffer_, decode_cb_.Get());
ASSERT_TRUE(surface_chooser_->factory_);
provide_overlay_info_cb_.Run(info);
info.routing_token = base::UnguessableToken::Deserialize(3, 4);
provide_overlay_info_cb_.Run(info);
}
TEST_F(MediaCodecVideoDecoderTest,
OnOverlayInfoChangedReplacesTheOverlayFactory) {
InitializeWithOverlay_OneDecodePending();
TEST_F(MediaCodecVideoDecoderTest, OverlayInfoUpdatesAreIgnoredInStateError) {
InitializeWithSurfaceTexture_OneDecodePending();
// Enter the error state.
codec_allocator_->ProvideNullCodecAsync();
EXPECT_CALL(*surface_chooser_, MockReplaceOverlayFactory(_)).Times(2);
EXPECT_CALL(*surface_chooser_, MockUpdateState()).Times(0);
OverlayInfo info;
info.routing_token = base::UnguessableToken::Deserialize(1, 2);
mcvd_->OnOverlayInfoChanged(info);
info.routing_token = base::UnguessableToken::Deserialize(3, 4);
mcvd_->OnOverlayInfoChanged(info);
provide_overlay_info_cb_.Run(info);
}
TEST_F(MediaCodecVideoDecoderTest, DuplicateOnOverlayInfoChangedAreIgnored) {
InitializeWithOverlay_OneDecodePending();
TEST_F(MediaCodecVideoDecoderTest, DuplicateOverlayInfoUpdatesAreIgnored) {
InitializeWithSurfaceTexture_OneDecodePending();
// The second OnOverlayInfoChanged() should be ignored.
// The second overlay info update should be ignored.
EXPECT_CALL(*surface_chooser_, MockReplaceOverlayFactory(_)).Times(1);
OverlayInfo info;
info.routing_token = base::UnguessableToken::Deserialize(1, 2);
mcvd_->OnOverlayInfoChanged(info);
mcvd_->OnOverlayInfoChanged(info);
provide_overlay_info_cb_.Run(info);
provide_overlay_info_cb_.Run(info);
}
TEST_F(MediaCodecVideoDecoderTest, CodecIsCreatedWithChosenOverlay) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment