Clone of chromium aad1ce808763f59c7a3753e08f1500a104ecc6fd refs/remotes/origin/HEAD
diff --git a/cc/resources/video_resource_updater.cc b/cc/resources/video_resource_updater.cc
new file mode 100644
index 0000000..75edccb
--- /dev/null
+++ b/cc/resources/video_resource_updater.cc
@@ -0,0 +1,399 @@
+// Copyright 2013 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 "cc/resources/video_resource_updater.h"
+
+#include "base/bind.h"
+#include "base/debug/trace_event.h"
+#include "cc/output/gl_renderer.h"
+#include "cc/resources/resource_provider.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "media/base/video_frame.h"
+#include "media/filters/skcanvas_video_renderer.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+#include "ui/gfx/size_conversions.h"
+
+namespace cc {
+
+namespace {
+
+const ResourceFormat kYUVResourceFormat = LUMINANCE_8;
+const ResourceFormat kRGBResourceFormat = RGBA_8888;
+
+class SyncPointClientImpl : public media::VideoFrame::SyncPointClient {
+ public:
+ explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {}
+ virtual ~SyncPointClientImpl() {}
+ virtual uint32 InsertSyncPoint() OVERRIDE {
+ return GLC(gl_, gl_->InsertSyncPointCHROMIUM());
+ }
+ virtual void WaitSyncPoint(uint32 sync_point) OVERRIDE {
+ GLC(gl_, gl_->WaitSyncPointCHROMIUM(sync_point));
+ }
+
+ private:
+ gpu::gles2::GLES2Interface* gl_;
+};
+
+} // namespace
+
+VideoFrameExternalResources::VideoFrameExternalResources() : type(NONE) {}
+
+VideoFrameExternalResources::~VideoFrameExternalResources() {}
+
+VideoResourceUpdater::VideoResourceUpdater(ContextProvider* context_provider,
+ ResourceProvider* resource_provider)
+ : context_provider_(context_provider),
+ resource_provider_(resource_provider) {
+}
+
+VideoResourceUpdater::~VideoResourceUpdater() {
+ while (!all_resources_.empty()) {
+ resource_provider_->DeleteResource(all_resources_.back());
+ all_resources_.pop_back();
+ }
+}
+
+void VideoResourceUpdater::DeleteResource(unsigned resource_id) {
+ resource_provider_->DeleteResource(resource_id);
+ all_resources_.erase(std::remove(all_resources_.begin(),
+ all_resources_.end(),
+ resource_id));
+}
+
+VideoFrameExternalResources VideoResourceUpdater::
+ CreateExternalResourcesFromVideoFrame(
+ const scoped_refptr<media::VideoFrame>& video_frame) {
+ if (!VerifyFrame(video_frame))
+ return VideoFrameExternalResources();
+
+ if (video_frame->format() == media::VideoFrame::NATIVE_TEXTURE)
+ return CreateForHardwarePlanes(video_frame);
+ else
+ return CreateForSoftwarePlanes(video_frame);
+}
+
+bool VideoResourceUpdater::VerifyFrame(
+ const scoped_refptr<media::VideoFrame>& video_frame) {
+ switch (video_frame->format()) {
+ // Acceptable inputs.
+ case media::VideoFrame::YV12:
+ case media::VideoFrame::I420:
+ case media::VideoFrame::YV12A:
+ case media::VideoFrame::YV16:
+ case media::VideoFrame::YV12J:
+ case media::VideoFrame::YV24:
+ case media::VideoFrame::NATIVE_TEXTURE:
+#if defined(VIDEO_HOLE)
+ case media::VideoFrame::HOLE:
+#endif // defined(VIDEO_HOLE)
+ return true;
+
+ // Unacceptable inputs. ¯\(°_o)/¯
+ case media::VideoFrame::UNKNOWN:
+ case media::VideoFrame::NV12:
+ break;
+ }
+ return false;
+}
+
+// For frames that we receive in software format, determine the dimensions of
+// each plane in the frame.
+static gfx::Size SoftwarePlaneDimension(
+ const scoped_refptr<media::VideoFrame>& input_frame,
+ ResourceFormat output_resource_format,
+ size_t plane_index) {
+ if (output_resource_format == kYUVResourceFormat) {
+ return media::VideoFrame::PlaneSize(
+ input_frame->format(), plane_index, input_frame->coded_size());
+ }
+
+ DCHECK_EQ(output_resource_format, kRGBResourceFormat);
+ return input_frame->coded_size();
+}
+
+VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes(
+ const scoped_refptr<media::VideoFrame>& video_frame) {
+ TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForSoftwarePlanes");
+ media::VideoFrame::Format input_frame_format = video_frame->format();
+
+#if defined(VIDEO_HOLE)
+ if (input_frame_format == media::VideoFrame::HOLE) {
+ VideoFrameExternalResources external_resources;
+ external_resources.type = VideoFrameExternalResources::HOLE;
+ return external_resources;
+ }
+#endif // defined(VIDEO_HOLE)
+
+ // Only YUV software video frames are supported.
+ DCHECK(input_frame_format == media::VideoFrame::YV12 ||
+ input_frame_format == media::VideoFrame::I420 ||
+ input_frame_format == media::VideoFrame::YV12A ||
+ input_frame_format == media::VideoFrame::YV12J ||
+ input_frame_format == media::VideoFrame::YV16 ||
+ input_frame_format == media::VideoFrame::YV24);
+ if (input_frame_format != media::VideoFrame::YV12 &&
+ input_frame_format != media::VideoFrame::I420 &&
+ input_frame_format != media::VideoFrame::YV12A &&
+ input_frame_format != media::VideoFrame::YV12J &&
+ input_frame_format != media::VideoFrame::YV16 &&
+ input_frame_format != media::VideoFrame::YV24)
+ return VideoFrameExternalResources();
+
+ bool software_compositor = context_provider_ == NULL;
+
+ ResourceFormat output_resource_format = kYUVResourceFormat;
+ size_t output_plane_count = media::VideoFrame::NumPlanes(input_frame_format);
+
+ // TODO(skaslev): If we're in software compositing mode, we do the YUV -> RGB
+ // conversion here. That involves an extra copy of each frame to a bitmap.
+ // Obviously, this is suboptimal and should be addressed once ubercompositor
+ // starts shaping up.
+ if (software_compositor) {
+ output_resource_format = kRGBResourceFormat;
+ output_plane_count = 1;
+ }
+
+ int max_resource_size = resource_provider_->max_texture_size();
+ std::vector<PlaneResource> plane_resources;
+ bool allocation_success = true;
+
+ for (size_t i = 0; i < output_plane_count; ++i) {
+ gfx::Size output_plane_resource_size =
+ SoftwarePlaneDimension(video_frame, output_resource_format, i);
+ if (output_plane_resource_size.IsEmpty() ||
+ output_plane_resource_size.width() > max_resource_size ||
+ output_plane_resource_size.height() > max_resource_size) {
+ allocation_success = false;
+ break;
+ }
+
+ ResourceProvider::ResourceId resource_id = 0;
+ gpu::Mailbox mailbox;
+
+ // Try recycle a previously-allocated resource.
+ for (size_t i = 0; i < recycled_resources_.size(); ++i) {
+ bool resource_matches =
+ recycled_resources_[i].resource_format == output_resource_format &&
+ recycled_resources_[i].resource_size == output_plane_resource_size;
+ bool not_in_use =
+ !software_compositor || !resource_provider_->InUseByConsumer(
+ recycled_resources_[i].resource_id);
+ if (resource_matches && not_in_use) {
+ resource_id = recycled_resources_[i].resource_id;
+ mailbox = recycled_resources_[i].mailbox;
+ recycled_resources_.erase(recycled_resources_.begin() + i);
+ break;
+ }
+ }
+
+ if (resource_id == 0) {
+ // TODO(danakj): Abstract out hw/sw resource create/delete from
+ // ResourceProvider and stop using ResourceProvider in this class.
+ resource_id = resource_provider_->CreateResource(
+ output_plane_resource_size,
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureHintImmutable,
+ output_resource_format);
+
+ DCHECK(mailbox.IsZero());
+
+ if (!software_compositor) {
+ DCHECK(context_provider_);
+
+ gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
+
+ GLC(gl, gl->GenMailboxCHROMIUM(mailbox.name));
+ ResourceProvider::ScopedWriteLockGL lock(resource_provider_,
+ resource_id);
+ GLC(gl,
+ gl->ProduceTextureDirectCHROMIUM(
+ lock.texture_id(), GL_TEXTURE_2D, mailbox.name));
+ }
+
+ if (resource_id)
+ all_resources_.push_back(resource_id);
+ }
+
+ if (resource_id == 0) {
+ allocation_success = false;
+ break;
+ }
+
+ DCHECK(software_compositor || !mailbox.IsZero());
+ plane_resources.push_back(PlaneResource(resource_id,
+ output_plane_resource_size,
+ output_resource_format,
+ mailbox));
+ }
+
+ if (!allocation_success) {
+ for (size_t i = 0; i < plane_resources.size(); ++i)
+ DeleteResource(plane_resources[i].resource_id);
+ return VideoFrameExternalResources();
+ }
+
+ VideoFrameExternalResources external_resources;
+
+ if (software_compositor) {
+ DCHECK_EQ(plane_resources.size(), 1u);
+ DCHECK_EQ(plane_resources[0].resource_format, kRGBResourceFormat);
+ DCHECK(plane_resources[0].mailbox.IsZero());
+
+ if (!video_renderer_)
+ video_renderer_.reset(new media::SkCanvasVideoRenderer);
+
+ {
+ ResourceProvider::ScopedWriteLockSoftware lock(
+ resource_provider_, plane_resources[0].resource_id);
+ video_renderer_->Copy(video_frame, lock.sk_canvas());
+ }
+
+ RecycleResourceData recycle_data = {
+ plane_resources[0].resource_id,
+ plane_resources[0].resource_size,
+ plane_resources[0].resource_format,
+ gpu::Mailbox()
+ };
+ external_resources.software_resources.push_back(
+ plane_resources[0].resource_id);
+ external_resources.software_release_callback =
+ base::Bind(&RecycleResource, AsWeakPtr(), recycle_data);
+ external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE;
+
+ return external_resources;
+ }
+
+ for (size_t i = 0; i < plane_resources.size(); ++i) {
+ // Update each plane's resource id with its content.
+ DCHECK_EQ(plane_resources[i].resource_format, kYUVResourceFormat);
+
+ const uint8_t* input_plane_pixels = video_frame->data(i);
+
+ gfx::Rect image_rect(0,
+ 0,
+ video_frame->stride(i),
+ plane_resources[i].resource_size.height());
+ gfx::Rect source_rect(plane_resources[i].resource_size);
+ resource_provider_->SetPixels(plane_resources[i].resource_id,
+ input_plane_pixels,
+ image_rect,
+ source_rect,
+ gfx::Vector2d());
+
+ RecycleResourceData recycle_data = {
+ plane_resources[i].resource_id,
+ plane_resources[i].resource_size,
+ plane_resources[i].resource_format,
+ plane_resources[i].mailbox
+ };
+
+ external_resources.mailboxes.push_back(
+ TextureMailbox(plane_resources[i].mailbox, GL_TEXTURE_2D, 0));
+ external_resources.release_callbacks.push_back(
+ base::Bind(&RecycleResource, AsWeakPtr(), recycle_data));
+ }
+
+ external_resources.type = VideoFrameExternalResources::YUV_RESOURCE;
+ return external_resources;
+}
+
+// static
+void VideoResourceUpdater::ReturnTexture(
+ base::WeakPtr<VideoResourceUpdater> updater,
+ const scoped_refptr<media::VideoFrame>& video_frame,
+ uint32 sync_point,
+ bool lost_resource,
+ BlockingTaskRunner* main_thread_task_runner) {
+ // TODO(dshwang) this case should be forwarded to the decoder as lost
+ // resource.
+ if (lost_resource || !updater.get())
+ return;
+ // VideoFrame::UpdateReleaseSyncPoint() creates new sync point using the same
+ // GL context which created the given |sync_point|, so discard the
+ // |sync_point|.
+ SyncPointClientImpl client(updater->context_provider_->ContextGL());
+ video_frame->UpdateReleaseSyncPoint(&client);
+}
+
+VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes(
+ const scoped_refptr<media::VideoFrame>& video_frame) {
+ TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForHardwarePlanes");
+ media::VideoFrame::Format frame_format = video_frame->format();
+
+ DCHECK_EQ(frame_format, media::VideoFrame::NATIVE_TEXTURE);
+ if (frame_format != media::VideoFrame::NATIVE_TEXTURE)
+ return VideoFrameExternalResources();
+
+ if (!context_provider_)
+ return VideoFrameExternalResources();
+
+ const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
+ VideoFrameExternalResources external_resources;
+ switch (mailbox_holder->texture_target) {
+ case GL_TEXTURE_2D:
+ external_resources.type = VideoFrameExternalResources::RGB_RESOURCE;
+ break;
+ case GL_TEXTURE_EXTERNAL_OES:
+ external_resources.type =
+ VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE;
+ break;
+ case GL_TEXTURE_RECTANGLE_ARB:
+ external_resources.type = VideoFrameExternalResources::IO_SURFACE;
+ break;
+ default:
+ NOTREACHED();
+ return VideoFrameExternalResources();
+ }
+
+ external_resources.mailboxes.push_back(
+ TextureMailbox(mailbox_holder->mailbox,
+ mailbox_holder->texture_target,
+ mailbox_holder->sync_point));
+ external_resources.release_callbacks.push_back(
+ base::Bind(&ReturnTexture, AsWeakPtr(), video_frame));
+ return external_resources;
+}
+
+// static
+void VideoResourceUpdater::RecycleResource(
+ base::WeakPtr<VideoResourceUpdater> updater,
+ RecycleResourceData data,
+ uint32 sync_point,
+ bool lost_resource,
+ BlockingTaskRunner* main_thread_task_runner) {
+ if (!updater.get()) {
+ // Resource was already deleted.
+ return;
+ }
+
+ ContextProvider* context_provider = updater->context_provider_;
+ if (context_provider && sync_point) {
+ GLC(context_provider->ContextGL(),
+ context_provider->ContextGL()->WaitSyncPointCHROMIUM(sync_point));
+ }
+
+ if (lost_resource) {
+ updater->DeleteResource(data.resource_id);
+ return;
+ }
+
+ // Drop recycled resources that are the wrong format.
+ while (!updater->recycled_resources_.empty() &&
+ updater->recycled_resources_.back().resource_format !=
+ data.resource_format) {
+ updater->DeleteResource(updater->recycled_resources_.back().resource_id);
+ updater->recycled_resources_.pop_back();
+ }
+
+ PlaneResource recycled_resource(data.resource_id,
+ data.resource_size,
+ data.resource_format,
+ data.mailbox);
+ updater->recycled_resources_.push_back(recycled_resource);
+}
+
+} // namespace cc