Chromium Code Reviews| 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 |