Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / cc / resources / video_resource_updater.cc
blob01a6042ef3e1a95f563411e30763226cec27b179
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/resources/video_resource_updater.h"
7 #include "base/bind.h"
8 #include "base/debug/trace_event.h"
9 #include "cc/output/gl_renderer.h"
10 #include "cc/resources/resource_provider.h"
11 #include "gpu/GLES2/gl2extchromium.h"
12 #include "gpu/command_buffer/client/gles2_interface.h"
13 #include "media/base/video_frame.h"
14 #include "media/filters/skcanvas_video_renderer.h"
15 #include "third_party/khronos/GLES2/gl2.h"
16 #include "third_party/khronos/GLES2/gl2ext.h"
17 #include "ui/gfx/geometry/size_conversions.h"
19 namespace cc {
21 namespace {
23 const ResourceFormat kYUVResourceFormat = LUMINANCE_8;
24 const ResourceFormat kRGBResourceFormat = RGBA_8888;
26 class SyncPointClientImpl : public media::VideoFrame::SyncPointClient {
27 public:
28 explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {}
29 ~SyncPointClientImpl() override {}
30 uint32 InsertSyncPoint() override {
31 return GLC(gl_, gl_->InsertSyncPointCHROMIUM());
33 void WaitSyncPoint(uint32 sync_point) override {
34 GLC(gl_, gl_->WaitSyncPointCHROMIUM(sync_point));
37 private:
38 gpu::gles2::GLES2Interface* gl_;
41 } // namespace
43 VideoFrameExternalResources::VideoFrameExternalResources() : type(NONE) {}
45 VideoFrameExternalResources::~VideoFrameExternalResources() {}
47 VideoResourceUpdater::VideoResourceUpdater(ContextProvider* context_provider,
48 ResourceProvider* resource_provider)
49 : context_provider_(context_provider),
50 resource_provider_(resource_provider) {
53 VideoResourceUpdater::~VideoResourceUpdater() {
54 while (!all_resources_.empty()) {
55 resource_provider_->DeleteResource(all_resources_.back());
56 all_resources_.pop_back();
60 void VideoResourceUpdater::DeleteResource(unsigned resource_id) {
61 resource_provider_->DeleteResource(resource_id);
62 all_resources_.erase(std::remove(all_resources_.begin(),
63 all_resources_.end(),
64 resource_id));
67 VideoFrameExternalResources VideoResourceUpdater::
68 CreateExternalResourcesFromVideoFrame(
69 const scoped_refptr<media::VideoFrame>& video_frame) {
70 if (!VerifyFrame(video_frame))
71 return VideoFrameExternalResources();
73 if (video_frame->format() == media::VideoFrame::NATIVE_TEXTURE)
74 return CreateForHardwarePlanes(video_frame);
75 else
76 return CreateForSoftwarePlanes(video_frame);
79 bool VideoResourceUpdater::VerifyFrame(
80 const scoped_refptr<media::VideoFrame>& video_frame) {
81 switch (video_frame->format()) {
82 // Acceptable inputs.
83 case media::VideoFrame::YV12:
84 case media::VideoFrame::I420:
85 case media::VideoFrame::YV12A:
86 case media::VideoFrame::YV16:
87 case media::VideoFrame::YV12J:
88 case media::VideoFrame::YV24:
89 case media::VideoFrame::NATIVE_TEXTURE:
90 #if defined(VIDEO_HOLE)
91 case media::VideoFrame::HOLE:
92 #endif // defined(VIDEO_HOLE)
93 return true;
95 // Unacceptable inputs. ¯\(°_o)/¯
96 case media::VideoFrame::UNKNOWN:
97 case media::VideoFrame::NV12:
98 break;
100 return false;
103 // For frames that we receive in software format, determine the dimensions of
104 // each plane in the frame.
105 static gfx::Size SoftwarePlaneDimension(
106 const scoped_refptr<media::VideoFrame>& input_frame,
107 ResourceFormat output_resource_format,
108 size_t plane_index) {
109 if (output_resource_format == kYUVResourceFormat) {
110 return media::VideoFrame::PlaneSize(
111 input_frame->format(), plane_index, input_frame->coded_size());
114 DCHECK_EQ(output_resource_format, kRGBResourceFormat);
115 return input_frame->coded_size();
118 VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes(
119 const scoped_refptr<media::VideoFrame>& video_frame) {
120 TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForSoftwarePlanes");
121 media::VideoFrame::Format input_frame_format = video_frame->format();
123 #if defined(VIDEO_HOLE)
124 if (input_frame_format == media::VideoFrame::HOLE) {
125 VideoFrameExternalResources external_resources;
126 external_resources.type = VideoFrameExternalResources::HOLE;
127 return external_resources;
129 #endif // defined(VIDEO_HOLE)
131 // Only YUV software video frames are supported.
132 DCHECK(input_frame_format == media::VideoFrame::YV12 ||
133 input_frame_format == media::VideoFrame::I420 ||
134 input_frame_format == media::VideoFrame::YV12A ||
135 input_frame_format == media::VideoFrame::YV12J ||
136 input_frame_format == media::VideoFrame::YV16 ||
137 input_frame_format == media::VideoFrame::YV24);
138 if (input_frame_format != media::VideoFrame::YV12 &&
139 input_frame_format != media::VideoFrame::I420 &&
140 input_frame_format != media::VideoFrame::YV12A &&
141 input_frame_format != media::VideoFrame::YV12J &&
142 input_frame_format != media::VideoFrame::YV16 &&
143 input_frame_format != media::VideoFrame::YV24)
144 return VideoFrameExternalResources();
146 bool software_compositor = context_provider_ == NULL;
148 ResourceFormat output_resource_format = kYUVResourceFormat;
149 size_t output_plane_count = media::VideoFrame::NumPlanes(input_frame_format);
151 // TODO(skaslev): If we're in software compositing mode, we do the YUV -> RGB
152 // conversion here. That involves an extra copy of each frame to a bitmap.
153 // Obviously, this is suboptimal and should be addressed once ubercompositor
154 // starts shaping up.
155 if (software_compositor) {
156 output_resource_format = kRGBResourceFormat;
157 output_plane_count = 1;
160 int max_resource_size = resource_provider_->max_texture_size();
161 std::vector<PlaneResource> plane_resources;
162 bool allocation_success = true;
164 for (size_t i = 0; i < output_plane_count; ++i) {
165 gfx::Size output_plane_resource_size =
166 SoftwarePlaneDimension(video_frame, output_resource_format, i);
167 if (output_plane_resource_size.IsEmpty() ||
168 output_plane_resource_size.width() > max_resource_size ||
169 output_plane_resource_size.height() > max_resource_size) {
170 allocation_success = false;
171 break;
174 ResourceProvider::ResourceId resource_id = 0;
175 gpu::Mailbox mailbox;
177 // Try recycle a previously-allocated resource.
178 for (size_t i = 0; i < recycled_resources_.size(); ++i) {
179 bool resource_matches =
180 recycled_resources_[i].resource_format == output_resource_format &&
181 recycled_resources_[i].resource_size == output_plane_resource_size;
182 bool not_in_use =
183 !software_compositor || !resource_provider_->InUseByConsumer(
184 recycled_resources_[i].resource_id);
185 if (resource_matches && not_in_use) {
186 resource_id = recycled_resources_[i].resource_id;
187 mailbox = recycled_resources_[i].mailbox;
188 recycled_resources_.erase(recycled_resources_.begin() + i);
189 break;
193 if (resource_id == 0) {
194 // TODO(danakj): Abstract out hw/sw resource create/delete from
195 // ResourceProvider and stop using ResourceProvider in this class.
196 resource_id = resource_provider_->CreateResource(
197 output_plane_resource_size,
198 GL_CLAMP_TO_EDGE,
199 ResourceProvider::TextureHintImmutable,
200 output_resource_format);
202 DCHECK(mailbox.IsZero());
204 if (!software_compositor) {
205 DCHECK(context_provider_);
207 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
209 GLC(gl, gl->GenMailboxCHROMIUM(mailbox.name));
210 ResourceProvider::ScopedWriteLockGL lock(resource_provider_,
211 resource_id);
212 GLC(gl,
213 gl->ProduceTextureDirectCHROMIUM(
214 lock.texture_id(), GL_TEXTURE_2D, mailbox.name));
217 if (resource_id)
218 all_resources_.push_back(resource_id);
221 if (resource_id == 0) {
222 allocation_success = false;
223 break;
226 DCHECK(software_compositor || !mailbox.IsZero());
227 plane_resources.push_back(PlaneResource(resource_id,
228 output_plane_resource_size,
229 output_resource_format,
230 mailbox));
233 if (!allocation_success) {
234 for (size_t i = 0; i < plane_resources.size(); ++i)
235 DeleteResource(plane_resources[i].resource_id);
236 return VideoFrameExternalResources();
239 VideoFrameExternalResources external_resources;
241 if (software_compositor) {
242 DCHECK_EQ(plane_resources.size(), 1u);
243 DCHECK_EQ(plane_resources[0].resource_format, kRGBResourceFormat);
244 DCHECK(plane_resources[0].mailbox.IsZero());
246 if (!video_renderer_)
247 video_renderer_.reset(new media::SkCanvasVideoRenderer);
250 ResourceProvider::ScopedWriteLockSoftware lock(
251 resource_provider_, plane_resources[0].resource_id);
252 video_renderer_->Copy(video_frame, lock.sk_canvas());
255 RecycleResourceData recycle_data = {
256 plane_resources[0].resource_id,
257 plane_resources[0].resource_size,
258 plane_resources[0].resource_format,
259 gpu::Mailbox()
261 external_resources.software_resources.push_back(
262 plane_resources[0].resource_id);
263 external_resources.software_release_callback =
264 base::Bind(&RecycleResource, AsWeakPtr(), recycle_data);
265 external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE;
267 return external_resources;
270 for (size_t i = 0; i < plane_resources.size(); ++i) {
271 // Update each plane's resource id with its content.
272 DCHECK_EQ(plane_resources[i].resource_format, kYUVResourceFormat);
274 const uint8_t* input_plane_pixels = video_frame->data(i);
276 gfx::Rect image_rect(0,
278 video_frame->stride(i),
279 plane_resources[i].resource_size.height());
280 gfx::Rect source_rect(plane_resources[i].resource_size);
281 resource_provider_->SetPixels(plane_resources[i].resource_id,
282 input_plane_pixels,
283 image_rect,
284 source_rect,
285 gfx::Vector2d());
287 RecycleResourceData recycle_data = {
288 plane_resources[i].resource_id,
289 plane_resources[i].resource_size,
290 plane_resources[i].resource_format,
291 plane_resources[i].mailbox
294 external_resources.mailboxes.push_back(
295 TextureMailbox(plane_resources[i].mailbox, GL_TEXTURE_2D, 0));
296 external_resources.release_callbacks.push_back(
297 base::Bind(&RecycleResource, AsWeakPtr(), recycle_data));
300 external_resources.type = VideoFrameExternalResources::YUV_RESOURCE;
301 return external_resources;
304 // static
305 void VideoResourceUpdater::ReturnTexture(
306 base::WeakPtr<VideoResourceUpdater> updater,
307 const scoped_refptr<media::VideoFrame>& video_frame,
308 uint32 sync_point,
309 bool lost_resource,
310 BlockingTaskRunner* main_thread_task_runner) {
311 // TODO(dshwang) this case should be forwarded to the decoder as lost
312 // resource.
313 if (lost_resource || !updater.get())
314 return;
315 // VideoFrame::UpdateReleaseSyncPoint() creates new sync point using the same
316 // GL context which created the given |sync_point|, so discard the
317 // |sync_point|.
318 SyncPointClientImpl client(updater->context_provider_->ContextGL());
319 video_frame->UpdateReleaseSyncPoint(&client);
322 VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes(
323 const scoped_refptr<media::VideoFrame>& video_frame) {
324 TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForHardwarePlanes");
325 media::VideoFrame::Format frame_format = video_frame->format();
327 DCHECK_EQ(frame_format, media::VideoFrame::NATIVE_TEXTURE);
328 if (frame_format != media::VideoFrame::NATIVE_TEXTURE)
329 return VideoFrameExternalResources();
331 if (!context_provider_)
332 return VideoFrameExternalResources();
334 const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
335 VideoFrameExternalResources external_resources;
336 switch (mailbox_holder->texture_target) {
337 case GL_TEXTURE_2D:
338 external_resources.type = VideoFrameExternalResources::RGB_RESOURCE;
339 break;
340 case GL_TEXTURE_EXTERNAL_OES:
341 external_resources.type =
342 VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE;
343 break;
344 case GL_TEXTURE_RECTANGLE_ARB:
345 external_resources.type = VideoFrameExternalResources::IO_SURFACE;
346 break;
347 default:
348 NOTREACHED();
349 return VideoFrameExternalResources();
352 external_resources.mailboxes.push_back(
353 TextureMailbox(mailbox_holder->mailbox,
354 mailbox_holder->texture_target,
355 mailbox_holder->sync_point));
356 external_resources.release_callbacks.push_back(
357 base::Bind(&ReturnTexture, AsWeakPtr(), video_frame));
358 return external_resources;
361 // static
362 void VideoResourceUpdater::RecycleResource(
363 base::WeakPtr<VideoResourceUpdater> updater,
364 RecycleResourceData data,
365 uint32 sync_point,
366 bool lost_resource,
367 BlockingTaskRunner* main_thread_task_runner) {
368 if (!updater.get()) {
369 // Resource was already deleted.
370 return;
373 ContextProvider* context_provider = updater->context_provider_;
374 if (context_provider && sync_point) {
375 GLC(context_provider->ContextGL(),
376 context_provider->ContextGL()->WaitSyncPointCHROMIUM(sync_point));
379 if (lost_resource) {
380 updater->DeleteResource(data.resource_id);
381 return;
384 // Drop recycled resources that are the wrong format.
385 while (!updater->recycled_resources_.empty() &&
386 updater->recycled_resources_.back().resource_format !=
387 data.resource_format) {
388 updater->DeleteResource(updater->recycled_resources_.back().resource_id);
389 updater->recycled_resources_.pop_back();
392 PlaneResource recycled_resource(data.resource_id,
393 data.resource_size,
394 data.resource_format,
395 data.mailbox);
396 updater->recycled_resources_.push_back(recycled_resource);
399 } // namespace cc