blob: e9fb161007636b9087e375e86ea8064dc0d05612 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/content_video_view_overlay_allocator.h"
#include "media/gpu/avda_codec_allocator.h"
namespace media {
// static
ContentVideoViewOverlayAllocator*
ContentVideoViewOverlayAllocator::GetInstance() {
static ContentVideoViewOverlayAllocator* allocator =
new ContentVideoViewOverlayAllocator();
return allocator;
}
ContentVideoViewOverlayAllocator::ContentVideoViewOverlayAllocator()
: allocator_(AVDACodecAllocator::GetInstance()) {}
ContentVideoViewOverlayAllocator::~ContentVideoViewOverlayAllocator() {}
bool ContentVideoViewOverlayAllocator::AllocateSurface(Client* client) {
DCHECK(thread_checker_.CalledOnValidThread());
const int32_t surface_id = client->GetSurfaceId();
DVLOG(1) << __func__ << ": " << surface_id;
DCHECK_NE(surface_id, SurfaceManager::kNoSurfaceID);
// If it's not owned or being released, |client| now owns it.
// Note: it's owned until it's released, since AVDACodecAllocator does that.
// It keeps the bundle around (and also the overlay that's the current owner)
// until the codec is done with it. That's required to use AndroidOverlay.
// So, we don't need to check for 'being released'; the owner is good enough.
auto it = surface_owners_.find(surface_id);
if (it == surface_owners_.end()) {
OwnerRecord record;
record.owner = client;
surface_owners_.insert(OwnerMap::value_type(surface_id, record));
return true;
}
// Otherwise |client| replaces the previous waiter (if any).
OwnerRecord& record = it->second;
if (record.waiter)
record.waiter->OnSurfaceAvailable(false);
record.waiter = client;
return false;
}
void ContentVideoViewOverlayAllocator::DeallocateSurface(Client* client) {
DCHECK(thread_checker_.CalledOnValidThread());
const int32_t surface_id = client->GetSurfaceId();
DCHECK_NE(surface_id, SurfaceManager::kNoSurfaceID);
// If we time out waiting for the surface to be destroyed, then we might have
// already removed |surface_id|. If it's now trying to deallocate, then
// maybe we just weren't patient enough, or mediaserver restarted.
auto it = surface_owners_.find(surface_id);
if (it == surface_owners_.end())
return;
OwnerRecord& record = it->second;
if (record.owner == client)
record.owner = nullptr;
else if (record.waiter == client)
record.waiter = nullptr;
// Promote the waiter if possible.
if (record.waiter && !record.owner) {
record.owner = record.waiter;
record.waiter = nullptr;
record.owner->OnSurfaceAvailable(true);
return;
}
// Remove the record if it's now unused.
if (!record.owner && !record.waiter)
surface_owners_.erase(it);
}
// During surface teardown we have to handle the following cases.
// 1) No AVDA has acquired the surface, or the surface has already been
// completely released.
// This case is easy -- there's no owner or waiter, and we can return.
//
// 2) A MediaCodec is currently being configured with the surface on another
// thread. Whether an AVDA owns the surface or has already deallocated it,
// the MediaCodec should be dropped when configuration completes.
// In this case, we'll find an owner. We'll notify it about the destruction.
// Note that AVDA doesn't handle this case correctly right now, since it
// doesn't know the state of codec creation on the codec thread. This is
// only a problem because CVV has the 'wait on main thread' semantics.
//
// 3) An AVDA owns the surface and it responds to OnSurfaceDestroyed() by:
// a) Replacing the destroyed surface by calling MediaCodec#setSurface().
// b) Releasing the MediaCodec it's attached to.
// In case a, the surface will be destroyed during OnSurfaceDestroyed.
// In case b, we'll have to wait for the release to complete.
//
// 4) No AVDA owns the surface, but the MediaCodec it's attached to is currently
// being destroyed on another thread.
// This is the same as 3b.
void ContentVideoViewOverlayAllocator::OnSurfaceDestroyed(int32_t surface_id) {
DCHECK(thread_checker_.CalledOnValidThread());
DVLOG(1) << __func__ << ": " << surface_id;
// If it isn't currently owned, then we're done. Rememeber that the overlay
// must outlive any user of it (MediaCodec!), and currently AVDACodecAllocator
// is responsible for making sure that happens for AVDA.
auto it = surface_owners_.find(surface_id);
if (it == surface_owners_.end())
return;
// Notify the owner and waiter (if any).
OwnerRecord& record = it->second;
if (record.waiter) {
record.waiter->OnSurfaceAvailable(false);
record.waiter = nullptr;
}
DCHECK(record.owner);
// |record| could be removed by the callback, if it deallocates the surface.
record.owner->OnSurfaceDestroyed();
// If owner deallocated the surface, then we don't need to wait. Note that
// the owner might have been deleted in that case. Since CVVOverlay only
// deallocates the surface during destruction, it's a safe bet.
it = surface_owners_.find(surface_id);
if (it == surface_owners_.end())
return;
// The surface is still in use, but should have been posted to another thread
// for destruction. Note that this isn't technically required for overlays
// in general, but CVV requires it. All of the pending release stuff should
// be moved here, or to custom deleters of CVVOverlay. However, in the
// interest of not changing too much at once, we let AVDACodecAllocator
// handle it. Since we're deprecating CVVOverlay anyway, all of this can be
// removed eventually.
// If the wait fails, then clean up |surface_owners_| anyway, since the codec
// release is probably hung up.
if (!allocator_->WaitForPendingRelease(record.owner))
surface_owners_.erase(it);
}
} // namespace media