Index: media/gpu/avda_picture_buffer_manager.cc |
diff --git a/media/gpu/avda_picture_buffer_manager.cc b/media/gpu/avda_picture_buffer_manager.cc |
index 135aa3a23c5fd9ef983e854a4d2248fbbb971b68..118a28dc59a57f1e43318756b9cb6d14d1ad7661 100644 |
--- a/media/gpu/avda_picture_buffer_manager.cc |
+++ b/media/gpu/avda_picture_buffer_manager.cc |
@@ -77,79 +77,29 @@ scoped_refptr<gl::SurfaceTexture> CreateAttachedSurfaceTexture( |
} // namespace |
-// Handle OnFrameAvailable callbacks safely. Since they occur asynchronously, |
-// we take care that the object that wants them still exists. WeakPtrs cannot |
-// be used because OnFrameAvailable callbacks can occur on any thread. We also |
-// can't guarantee when the SurfaceTexture will quit sending callbacks to |
-// coordinate with the destruction of the AVDA and PictureBufferManager, so we |
-// have a separate object that the callback can own. |
-class AVDAPictureBufferManager::OnFrameAvailableHandler |
- : public base::RefCountedThreadSafe<OnFrameAvailableHandler> { |
- public: |
- // We do not retain ownership of |listener|. It must remain valid until after |
- // ClearListener() is called. This will register with |surface_texture| to |
- // receive OnFrameAvailable callbacks. |
- OnFrameAvailableHandler(AVDASharedState* listener, |
- gl::SurfaceTexture* surface_texture) |
- : listener_(listener) { |
- surface_texture->SetFrameAvailableCallbackOnAnyThread( |
- base::Bind(&OnFrameAvailableHandler::OnFrameAvailable, |
- scoped_refptr<OnFrameAvailableHandler>(this))); |
- } |
- |
- // Forget about |listener_|, which is required before one deletes it. |
- // No further callbacks will happen once this completes. |
- void ClearListener() { |
- base::AutoLock lock(lock_); |
- listener_ = nullptr; |
- } |
- |
- // Notify the listener if there is one. |
- void OnFrameAvailable() { |
- base::AutoLock auto_lock(lock_); |
- if (listener_) |
- listener_->SignalFrameAvailable(); |
- } |
- |
- private: |
- friend class base::RefCountedThreadSafe<OnFrameAvailableHandler>; |
- |
- ~OnFrameAvailableHandler() { DCHECK(!listener_); } |
- |
- // Protects changes to listener_. |
- base::Lock lock_; |
- |
- // The AVDASharedState that wants the OnFrameAvailable callback. |
- AVDASharedState* listener_; |
- |
- DISALLOW_COPY_AND_ASSIGN(OnFrameAvailableHandler); |
-}; |
- |
-AVDAPictureBufferManager::AVDAPictureBufferManager() |
- : state_provider_(nullptr), media_codec_(nullptr) {} |
+AVDAPictureBufferManager::AVDAPictureBufferManager( |
+ AVDAStateProvider* state_provider) |
+ : state_provider_(state_provider), media_codec_(nullptr) {} |
AVDAPictureBufferManager::~AVDAPictureBufferManager() {} |
-gl::ScopedJavaSurface AVDAPictureBufferManager::Initialize( |
- AVDAStateProvider* state_provider, |
- int surface_view_id) { |
- state_provider_ = state_provider; |
+gl::ScopedJavaSurface AVDAPictureBufferManager::Initialize(int surface_id) { |
shared_state_ = new AVDASharedState(); |
// Acquire the SurfaceView surface if given a valid id. |
- if (surface_view_id != VideoDecodeAccelerator::Config::kNoSurfaceID) { |
- return gpu::GpuSurfaceLookup::GetInstance()->AcquireJavaSurface( |
- surface_view_id); |
+ if (surface_id != SurfaceManager::kNoSurfaceID) { |
+ if (surface_texture_) { |
+ surface_texture_->ReleaseSurfaceTexture(); |
+ surface_texture_ = nullptr; |
+ } |
+ return gpu::GpuSurfaceLookup::GetInstance()->AcquireJavaSurface(surface_id); |
} |
- // Otherwise create a SurfaceTexture. |
- GLuint service_id = 0; |
+ // Always create a SurfaceTexture, we'll use it for entering and exiting from |
+ // fullscreen mode. |
+ GLuint service_id; |
surface_texture_ = CreateAttachedSurfaceTexture( |
state_provider_->GetGlDecoder(), &service_id); |
- if (surface_texture_) { |
- on_frame_available_handler_ = new OnFrameAvailableHandler( |
- shared_state_.get(), surface_texture_.get()); |
- } |
shared_state_->SetSurfaceTexture(surface_texture_, service_id); |
return gl::ScopedJavaSurface(surface_texture_.get()); |
} |
@@ -159,11 +109,6 @@ void AVDAPictureBufferManager::Destroy(const PictureBufferMap& buffers) { |
if (!shared_state_) |
return; |
- // If we have an OnFrameAvailable handler, tell it that we no longer want |
- // callbacks. |
- if (on_frame_available_handler_) |
- on_frame_available_handler_->ClearListener(); |
- |
ReleaseCodecBuffers(buffers); |
CodecChanged(nullptr); |
@@ -174,22 +119,17 @@ void AVDAPictureBufferManager::Destroy(const PictureBufferMap& buffers) { |
} |
uint32_t AVDAPictureBufferManager::GetTextureTarget() const { |
- // If we're using a surface texture, then we need an external texture target |
- // to sample from it. If not, then we'll use 2D transparent textures to draw |
- // a transparent hole through which to see the SurfaceView. This is normally |
- // needed only for the devtools inspector, since the overlay mechanism handles |
- // it otherwise. |
- return surface_texture_ ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; |
+ // Always use OES textures even though this will cause flickering in dev tools |
+ // when inspecting a fullscreen video. See http://crbug.com/592798 |
+ return GL_TEXTURE_EXTERNAL_OES; |
} |
gfx::Size AVDAPictureBufferManager::GetPictureBufferSize() const { |
- // For SurfaceView, request a 1x1 2D texture to reduce memory during |
- // initialization. For SurfaceTexture, 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. However, it's still important to |
- // get the coded size right, so that VideoLayerImpl doesn't try to scale the |
- // texture when building the quad for it. |
- return surface_texture_ ? state_provider_->GetSize() : gfx::Size(1, 1); |
+ // 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. |
+ return state_provider_->GetSize(); |
} |
gpu::gles2::TextureRef* AVDAPictureBufferManager::GetTextureForPicture( |
@@ -212,7 +152,7 @@ gpu::gles2::TextureRef* AVDAPictureBufferManager::GetTextureForPicture( |
void AVDAPictureBufferManager::SetImageForPicture( |
const PictureBuffer& picture_buffer, |
- const scoped_refptr<gpu::gles2::GLStreamTextureImage>& image) { |
+ gpu::gles2::GLStreamTextureImage* image) { |
gpu::gles2::TextureRef* texture_ref = GetTextureForPicture(picture_buffer); |
RETURN_IF_NULL(texture_ref); |
@@ -236,8 +176,7 @@ void AVDAPictureBufferManager::SetImageForPicture( |
size.width(), size.height(), 1, 0, GL_RGBA, |
GL_UNSIGNED_BYTE, gfx::Rect()); |
- static_cast<AVDACodecImage*>(image.get()) |
- ->set_texture(texture_ref->texture()); |
+ static_cast<AVDACodecImage*>(image)->set_texture(texture_ref->texture()); |
} |
// If we're clearing the image, or setting a SurfaceTexture backed image, we |
@@ -249,13 +188,19 @@ void AVDAPictureBufferManager::SetImageForPicture( |
// requires it. If something tries to sample from this texture it won't work, |
// but there's no way to sample from a SurfaceView anyway, so it doesn't |
// matter. |
- if (image && !surface_texture_) |
+ if (image && !shared_state_->surface_texture_service_id()) |
image_state = gpu::gles2::Texture::BOUND; |
texture_manager->SetLevelStreamTextureImage(texture_ref, GetTextureTarget(), |
- 0, image.get(), image_state, |
+ 0, image, image_state, |
stream_texture_service_id); |
} |
+AVDACodecImage* AVDAPictureBufferManager::GetImageForPicture( |
+ int picture_buffer_id) const { |
+ auto it = codec_images_.find(picture_buffer_id); |
+ return it == codec_images_.end() ? nullptr : it->second; |
+} |
+ |
void AVDAPictureBufferManager::UseCodecBufferForPictureBuffer( |
int32_t codec_buf_index, |
const PictureBuffer& picture_buffer) { |
@@ -264,15 +209,26 @@ void AVDAPictureBufferManager::UseCodecBufferForPictureBuffer( |
// Notify the AVDACodecImage for picture_buffer that it should use the |
// decoded buffer codec_buf_index to render this frame. |
- AVDACodecImage* avda_image = |
- shared_state_->GetImageForPicture(picture_buffer.id()); |
+ AVDACodecImage* avda_image = GetImageForPicture(picture_buffer.id()); |
RETURN_IF_NULL(avda_image); |
// Note that this is not a race, since we do not re-use a PictureBuffer |
// until after the CC is done drawing it. |
pictures_out_for_display_.push_back(picture_buffer.id()); |
- avda_image->set_media_codec_buffer_index(codec_buf_index); |
- avda_image->set_size(state_provider_->GetSize()); |
+ avda_image->SetBufferMetadata(codec_buf_index, |
+ !!shared_state_->surface_texture_service_id(), |
+ state_provider_->GetSize()); |
+ |
+ // If the shared state has changed for this image, retarget its texture. |
+ if (avda_image->SetSharedState(shared_state_)) { |
+ gpu::gles2::TextureRef* texture_ref = GetTextureForPicture(picture_buffer); |
+ RETURN_IF_NULL(texture_ref); |
+ texture_ref->texture()->SetStreamTextureServiceId( |
+ shared_state_->surface_texture_service_id()); |
+ |
+ // TODO(dalecurtis): What about ImageState? For ST we set UNBOUND while we |
+ // always set BOUND for SV. |
liberato (no reviews please)
2016/11/03 20:51:04
we should do that here too, i think.
actually, i
DaleCurtis
2016/11/03 20:59:35
Originally tried this, but there are some restrict
DaleCurtis
2016/11/03 23:25:05
Ah, this was because the last ref to the GLImage w
|
+ } |
MaybeRenderEarly(); |
} |
@@ -282,32 +238,32 @@ void AVDAPictureBufferManager::AssignOnePictureBuffer( |
bool have_context) { |
// Attach a GLImage to each texture that will use the surface texture. |
// We use a refptr here in case SetImageForPicture fails. |
- scoped_refptr<gpu::gles2::GLStreamTextureImage> gl_image = |
- new AVDACodecImage(picture_buffer.id(), shared_state_, media_codec_, |
- state_provider_->GetGlDecoder()); |
- SetImageForPicture(picture_buffer, gl_image); |
- |
- if (!surface_texture_ && have_context) { |
- // To make devtools work, we're using a 2D texture. Make it transparent, |
- // so that it draws a hole for the SV to show through. This is only |
- // because devtools draws and reads back, which skips overlay processing. |
- // It's unclear why devtools renders twice -- once normally, and once |
- // including a readback layer. The result is that the device screen |
- // flashes as we alternately draw the overlay hole and this texture, |
- // unless we make the texture transparent. |
- static const uint8_t rgba[] = {0, 0, 0, 0}; |
- const gfx::Size size(1, 1); |
- DCHECK_LE(1u, picture_buffer.service_texture_ids().size()); |
- glBindTexture(GL_TEXTURE_2D, picture_buffer.service_texture_ids()[0]); |
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, |
- GL_RGBA, GL_UNSIGNED_BYTE, rgba); |
- } |
+ scoped_refptr<gpu::gles2::GLStreamTextureImage> gl_image = new AVDACodecImage( |
+ shared_state_, media_codec_, state_provider_->GetGlDecoder()); |
+ SetImageForPicture(picture_buffer, gl_image.get()); |
+ codec_images_[picture_buffer.id()] = |
+ static_cast<AVDACodecImage*>(gl_image.get()); |
+ |
+ // if (!shared_state_->surface_texture_service_id() && have_context) { |
+ // // To make devtools work, we're using a 2D texture. Make it transparent, |
+ // // so that it draws a hole for the SV to show through. This is only |
+ // // because devtools draws and reads back, which skips overlay processing. |
+ // // It's unclear why devtools renders twice -- once normally, and once |
+ // // including a readback layer. The result is that the device screen |
+ // // flashes as we alternately draw the overlay hole and this texture, |
+ // // unless we make the texture transparent. |
+ // static const uint8_t rgba[] = {0, 0, 0, 0}; |
+ // const gfx::Size size(1, 1); |
+ // DCHECK_LE(1u, picture_buffer.service_texture_ids().size()); |
+ // glBindTexture(GL_TEXTURE_2D, picture_buffer.service_texture_ids()[0]); |
+ // glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, |
+ // GL_RGBA, GL_UNSIGNED_BYTE, rgba); |
+ // } |
} |
void AVDAPictureBufferManager::ReleaseCodecBufferForPicture( |
const PictureBuffer& picture_buffer) { |
- AVDACodecImage* avda_image = |
- shared_state_->GetImageForPicture(picture_buffer.id()); |
+ AVDACodecImage* avda_image = GetImageForPicture(picture_buffer.id()); |
RETURN_IF_NULL(avda_image); |
avda_image->UpdateSurface(AVDACodecImage::UpdateMode::DISCARD_CODEC_BUFFER); |
} |
@@ -344,7 +300,7 @@ void AVDAPictureBufferManager::MaybeRenderEarly() { |
AVDACodecImage* first_renderable_image = nullptr; |
for (int i = front_index; i >= 0; --i) { |
const int id = pictures_out_for_display_[i]; |
- AVDACodecImage* avda_image = shared_state_->GetImageForPicture(id); |
+ AVDACodecImage* avda_image = GetImageForPicture(id); |
if (!avda_image) |
continue; |
@@ -369,14 +325,16 @@ void AVDAPictureBufferManager::MaybeRenderEarly() { |
// Back buffer rendering is only available for surface textures. We'll always |
// have at least one front buffer, so the next buffer must be the backbuffer. |
size_t backbuffer_index = front_index + 1; |
- if (!surface_texture_ || backbuffer_index >= pictures_out_for_display_.size()) |
+ if (!shared_state_->surface_texture_service_id() || |
+ backbuffer_index >= pictures_out_for_display_.size()) { |
return; |
+ } |
// See if the back buffer is free. If so, then render the frame adjacent to |
// the front buffer. The listing is in render order, so we can just use the |
// first unrendered frame if there is back buffer space. |
- first_renderable_image = shared_state_->GetImageForPicture( |
- pictures_out_for_display_[backbuffer_index]); |
+ first_renderable_image = |
+ GetImageForPicture(pictures_out_for_display_[backbuffer_index]); |
if (!first_renderable_image || |
first_renderable_image->was_rendered_to_back_buffer()) { |
return; |
@@ -390,13 +348,24 @@ void AVDAPictureBufferManager::MaybeRenderEarly() { |
void AVDAPictureBufferManager::CodecChanged(VideoCodecBridge* codec) { |
media_codec_ = codec; |
- shared_state_->CodecChanged(codec); |
+ for (auto& image_kv : codec_images_) |
+ image_kv.second->CodecChanged(codec); |
+ shared_state_->clear_release_time(); |
} |
bool AVDAPictureBufferManager::ArePicturesOverlayable() { |
// SurfaceView frames are always overlayable because that's the only way to |
// display them. |
- return !surface_texture_; |
+ return !shared_state_->surface_texture_service_id(); |
+} |
+ |
+bool AVDAPictureBufferManager::ArePicturesOutstanding() const { |
+ for (int id : pictures_out_for_display_) { |
+ AVDACodecImage* avda_image = GetImageForPicture(id); |
+ if (avda_image && avda_image->has_unrendered_frame()) |
+ return true; |
+ } |
+ return false; |
} |
} // namespace media |