Index: media/gpu/android_video_decode_accelerator.cc |
diff --git a/media/gpu/android_video_decode_accelerator.cc b/media/gpu/android_video_decode_accelerator.cc |
index 11adbd8189c59bc4f157815a5828ce9f55d11f23..3bec30326f5dc36493da0e8e4ba7865c61b7bc9c 100644 |
--- a/media/gpu/android_video_decode_accelerator.cc |
+++ b/media/gpu/android_video_decode_accelerator.cc |
@@ -169,7 +169,7 @@ class AVDAManager { |
// is assumed to be on the way out, so we fail its allocation request. |
bool AllocateSurface(int surface_id, AndroidVideoDecodeAccelerator* avda) { |
// Nobody has to wait for no surface. |
- if (surface_id == AndroidVideoDecodeAccelerator::Config::kNoSurfaceID) |
+ if (surface_id == SurfaceManager::kNoSurfaceID) |
return true; |
auto iter = surface_waiter_map_.find(surface_id); |
@@ -225,8 +225,7 @@ class AVDAManager { |
// defer the surface creation until the codec is actually used if we know no |
// software fallback exists. |
bool ShouldDeferSurfaceCreation(int surface_id, VideoCodec codec) { |
- return surface_id == AndroidVideoDecodeAccelerator::Config::kNoSurfaceID && |
- codec == kCodecH264 && |
+ return surface_id == SurfaceManager::kNoSurfaceID && codec == kCodecH264 && |
g_avda_codec_allocator.Get().IsAnyRegisteredAVDA() && |
(base::android::BuildInfo::GetInstance()->sdk_int() <= 18 || |
base::SysInfo::IsLowEndDevice()); |
@@ -319,6 +318,7 @@ AndroidVideoDecodeAccelerator::AndroidVideoDecodeAccelerator( |
deferred_initialization_pending_(false), |
codec_needs_reset_(false), |
defer_surface_creation_(false), |
+ surface_id_(SurfaceManager::kNoSurfaceID), |
weak_this_factory_(this) {} |
AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() { |
@@ -397,15 +397,21 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, |
return false; |
} |
+ // If SetSurface() was called before initialize, pick up the surface. |
+ if (pending_surface_id_) { |
+ surface_id_ = pending_surface_id_.value(); |
+ pending_surface_id_.reset(); |
+ } |
+ |
// If we're low on resources, we may decide to defer creation of the surface |
// until the codec is actually used. |
- if (g_avda_manager.Get().ShouldDeferSurfaceCreation(config_.surface_id, |
+ if (g_avda_manager.Get().ShouldDeferSurfaceCreation(surface_id_, |
codec_config_->codec_)) { |
DCHECK(!deferred_initialization_pending_); |
// We should never be here if a SurfaceView is required. |
- DCHECK_EQ(config_.surface_id, Config::kNoSurfaceID); |
- DCHECK(g_avda_manager.Get().AllocateSurface(config_.surface_id, this)); |
+ DCHECK_EQ(surface_id_, SurfaceManager::kNoSurfaceID); |
+ DCHECK(g_avda_manager.Get().AllocateSurface(surface_id_, this)); |
defer_surface_creation_ = true; |
NotifyInitializationComplete(true); |
@@ -420,7 +426,7 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, |
return false; |
} |
- if (g_avda_manager.Get().AllocateSurface(config_.surface_id, this)) { |
+ if (g_avda_manager.Get().AllocateSurface(surface_id_, this)) { |
// We have successfully owned the surface, so finish initialization now. |
return InitializePictureBufferManager(); |
} |
@@ -446,8 +452,7 @@ bool AndroidVideoDecodeAccelerator::InitializePictureBufferManager() { |
return false; |
} |
- codec_config_->surface_ = |
- picture_buffer_manager_.Initialize(config_.surface_id); |
+ codec_config_->surface_ = picture_buffer_manager_.Initialize(surface_id_); |
if (codec_config_->surface_.IsEmpty()) |
return false; |
@@ -654,6 +659,16 @@ bool AndroidVideoDecodeAccelerator::DequeueOutput() { |
return false; |
} |
+ // If we're waiting to switch surfaces pause output release until we have all |
+ // picture buffers returned. This is so we can ensure the right flags are set |
+ // on the picture buffers returned to the client. |
+ if (pending_surface_id_) { |
+ if (picture_buffer_manager_.HasUnrenderedPictures()) |
+ return false; |
+ if (!UpdateSurface()) |
+ return false; |
+ } |
+ |
bool eos = false; |
base::TimeDelta presentation_timestamp; |
int32_t buf_index = 0; |
@@ -884,10 +899,14 @@ void AndroidVideoDecodeAccelerator::DecodeBuffer( |
void AndroidVideoDecodeAccelerator::RequestPictureBuffers() { |
if (client_) { |
- client_->ProvidePictureBuffers( |
- kNumPictureBuffers, PIXEL_FORMAT_UNKNOWN, 1, |
- picture_buffer_manager_.GetPictureBufferSize(), |
- picture_buffer_manager_.GetTextureTarget()); |
+ // Allocate a picture buffer that is the actual frame size. Note that it |
+ // will be an external texture anyway, so it doesn't allocate an image of |
+ // that size. It's important to get the coded size right, so that |
+ // VideoLayerImpl doesn't try to scale the texture when building the quad |
+ // for it. |
+ client_->ProvidePictureBuffers(kNumPictureBuffers, PIXEL_FORMAT_UNKNOWN, 1, |
+ size_, |
+ AVDAPictureBufferManager::kTextureTarget); |
} |
} |
@@ -907,13 +926,7 @@ void AndroidVideoDecodeAccelerator::AssignPictureBuffers( |
<< "Failed to make GL context current for Assign, continuing."; |
for (size_t i = 0; i < buffers.size(); ++i) { |
- if (buffers[i].size() != picture_buffer_manager_.GetPictureBufferSize()) { |
- POST_ERROR(INVALID_ARGUMENT, |
- "Invalid picture buffer size assigned. Wanted " |
- << size_.ToString() << ", but got " |
- << buffers[i].size().ToString()); |
- return; |
- } |
+ DCHECK(buffers[i].size() == size_); |
int32_t id = buffers[i].id(); |
output_picture_buffers_.insert(std::make_pair(id, buffers[i])); |
free_picture_ids_.push(id); |
@@ -1241,6 +1254,18 @@ void AndroidVideoDecodeAccelerator::Reset() { |
} |
} |
+void AndroidVideoDecodeAccelerator::SetSurface(int32_t surface_id) { |
+ if (surface_id == surface_id_) { |
+ pending_surface_id_.reset(); |
+ return; |
+ } |
+ |
+ // Surface changes never take effect immediately, they will be handled during |
+ // DequeOutput() once we get to a good switch point or immediately during an |
+ // OnDestroyingSurface() call. |
+ pending_surface_id_ = surface_id; |
+} |
+ |
void AndroidVideoDecodeAccelerator::Destroy() { |
DVLOG(1) << __FUNCTION__; |
DCHECK(thread_checker_.CalledOnValidThread()); |
@@ -1274,7 +1299,7 @@ void AndroidVideoDecodeAccelerator::ActualDestroy() { |
// We no longer care about |surface_id|, in case we did before. It's okay |
// if we have no surface and/or weren't the owner or a waiter. |
- g_avda_manager.Get().DeallocateSurface(config_.surface_id, this); |
+ g_avda_manager.Get().DeallocateSurface(surface_id_, this); |
// 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 |
@@ -1308,8 +1333,28 @@ void AndroidVideoDecodeAccelerator::OnDestroyingSurface(int surface_id) { |
TRACE_EVENT0("media", "AVDA::OnDestroyingSurface"); |
DVLOG(1) << __FUNCTION__ << " surface_id: " << surface_id; |
- if (surface_id != config_.surface_id) |
+ if (surface_id != surface_id_) |
+ return; |
+ |
+ // If the API is available avoid having to restart the decoder in order to |
+ // leave fullscreen. If we don't clear the surface immediately during this |
+ // callback, the MediaCodec will throw an error as the surface is destroyed. |
+ if (base::android::BuildInfo::GetInstance()->sdk_int() >= 23) { |
+ // Since we can't wait for a transition, we must invalidate all outstanding |
+ // picture buffers to avoid putting the GL system in a broken state. |
+ picture_buffer_manager_.ReleaseCodecBuffers(output_picture_buffers_); |
+ |
+ // Switch away from the surface being destroyed to a surface texture. |
+ DCHECK_NE(surface_id_, SurfaceManager::kNoSurfaceID); |
+ |
+ // The leaving fullscreen notification may come in before this point. |
+ if (pending_surface_id_) |
+ DCHECK_EQ(pending_surface_id_.value(), SurfaceManager::kNoSurfaceID); |
+ |
+ pending_surface_id_ = SurfaceManager::kNoSurfaceID; |
+ UpdateSurface(); |
return; |
+ } |
// If we're currently asynchronously configuring a codec, it will be destroyed |
// when configuration completes and it notices that |state_| has changed to |
@@ -1584,4 +1629,35 @@ bool AndroidVideoDecodeAccelerator::IsMediaCodecSoftwareDecodingForbidden() |
codec_config_->codec_ == kCodecVP9); |
} |
+bool AndroidVideoDecodeAccelerator::UpdateSurface() { |
+ DCHECK(pending_surface_id_); |
+ DCHECK_NE(surface_id_, pending_surface_id_.value()); |
+ |
+ // Ensure the current context is active when switching surfaces; we may need |
+ // to create a new texture. |
+ if (!make_context_current_cb_.Run()) { |
+ POST_ERROR(PLATFORM_FAILURE, |
+ "Failed to make this decoder's GL context current when " |
+ "switching surfaces."); |
+ return false; |
+ } |
+ |
+ surface_id_ = pending_surface_id_.value(); |
+ codec_config_->surface_ = |
+ picture_buffer_manager_.Initialize(pending_surface_id_.value()); |
+ if (codec_config_->surface_.IsEmpty()) { |
+ POST_ERROR(PLATFORM_FAILURE, "An error occurred while switching surfaces."); |
+ return false; |
+ } |
+ |
+ if (media_codec_ && |
+ !media_codec_->SetSurface(codec_config_->surface_.j_surface().obj())) { |
+ POST_ERROR(PLATFORM_FAILURE, "MediaCodec failed to switch surfaces."); |
+ return false; |
+ } |
+ |
+ pending_surface_id_.reset(); |
+ return true; |
+} |
+ |
} // namespace media |