blob: f6d08d1e3f98e07c0c64c7931af0248fa436c417 [file] [log] [blame]
James Robinson646469d2014-10-03 15:33:28 -07001// 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.
4
5#include "cc/resources/video_resource_updater.h"
6
Etienne Membrivesb1556b32014-12-16 13:56:09 +01007#include <algorithm>
8
James Robinson646469d2014-10-03 15:33:28 -07009#include "base/bind.h"
10#include "base/debug/trace_event.h"
11#include "cc/output/gl_renderer.h"
12#include "cc/resources/resource_provider.h"
13#include "gpu/GLES2/gl2extchromium.h"
14#include "gpu/command_buffer/client/gles2_interface.h"
15#include "media/base/video_frame.h"
16#include "media/filters/skcanvas_video_renderer.h"
17#include "third_party/khronos/GLES2/gl2.h"
18#include "third_party/khronos/GLES2/gl2ext.h"
James Robinson30d547e2014-10-23 18:20:06 -070019#include "ui/gfx/geometry/size_conversions.h"
James Robinson646469d2014-10-03 15:33:28 -070020
21namespace cc {
22
23namespace {
24
James Robinson646469d2014-10-03 15:33:28 -070025const ResourceFormat kRGBResourceFormat = RGBA_8888;
26
27class SyncPointClientImpl : public media::VideoFrame::SyncPointClient {
28 public:
29 explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {}
James Robinsone1b30cf2014-10-21 12:25:40 -070030 ~SyncPointClientImpl() override {}
31 uint32 InsertSyncPoint() override {
James Robinson646469d2014-10-03 15:33:28 -070032 return GLC(gl_, gl_->InsertSyncPointCHROMIUM());
33 }
James Robinsone1b30cf2014-10-21 12:25:40 -070034 void WaitSyncPoint(uint32 sync_point) override {
James Robinson646469d2014-10-03 15:33:28 -070035 GLC(gl_, gl_->WaitSyncPointCHROMIUM(sync_point));
36 }
37
38 private:
39 gpu::gles2::GLES2Interface* gl_;
40};
41
42} // namespace
43
James Robinson6a64b812014-12-03 13:38:42 -080044VideoResourceUpdater::PlaneResource::PlaneResource(
45 unsigned int resource_id,
46 const gfx::Size& resource_size,
47 ResourceFormat resource_format,
48 gpu::Mailbox mailbox)
49 : resource_id(resource_id),
50 resource_size(resource_size),
51 resource_format(resource_format),
52 mailbox(mailbox),
Etienne Membrivesb1556b32014-12-16 13:56:09 +010053 ref_count(0),
James Robinson6a64b812014-12-03 13:38:42 -080054 frame_ptr(nullptr),
55 plane_index(0) {
56}
57
58bool VideoResourceUpdater::PlaneResourceMatchesUniqueID(
59 const PlaneResource& plane_resource,
60 const media::VideoFrame* video_frame,
61 int plane_index) {
62 return plane_resource.frame_ptr == video_frame &&
63 plane_resource.plane_index == plane_index &&
64 plane_resource.timestamp == video_frame->timestamp();
65}
66
67void VideoResourceUpdater::SetPlaneResourceUniqueId(
68 const media::VideoFrame* video_frame,
69 int plane_index,
70 PlaneResource* plane_resource) {
71 plane_resource->frame_ptr = video_frame;
72 plane_resource->plane_index = plane_index;
73 plane_resource->timestamp = video_frame->timestamp();
74}
75
James Robinson646469d2014-10-03 15:33:28 -070076VideoFrameExternalResources::VideoFrameExternalResources() : type(NONE) {}
77
78VideoFrameExternalResources::~VideoFrameExternalResources() {}
79
80VideoResourceUpdater::VideoResourceUpdater(ContextProvider* context_provider,
81 ResourceProvider* resource_provider)
82 : context_provider_(context_provider),
83 resource_provider_(resource_provider) {
84}
85
86VideoResourceUpdater::~VideoResourceUpdater() {
Etienne Membrivesb1556b32014-12-16 13:56:09 +010087 for (const PlaneResource& plane_resource : all_resources_)
88 resource_provider_->DeleteResource(plane_resource.resource_id);
James Robinson646469d2014-10-03 15:33:28 -070089}
90
Etienne Membrivesb1556b32014-12-16 13:56:09 +010091VideoResourceUpdater::ResourceList::iterator
92VideoResourceUpdater::AllocateResource(const gfx::Size& plane_size,
93 ResourceFormat format,
94 bool has_mailbox) {
95 // TODO(danakj): Abstract out hw/sw resource create/delete from
96 // ResourceProvider and stop using ResourceProvider in this class.
97 const ResourceProvider::ResourceId resource_id =
98 resource_provider_->CreateResource(plane_size, GL_CLAMP_TO_EDGE,
99 ResourceProvider::TextureHintImmutable,
100 format);
101 if (resource_id == 0)
102 return all_resources_.end();
103
104 gpu::Mailbox mailbox;
105 if (has_mailbox) {
106 DCHECK(context_provider_);
107
108 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
109
110 GLC(gl, gl->GenMailboxCHROMIUM(mailbox.name));
111 ResourceProvider::ScopedWriteLockGL lock(resource_provider_, resource_id);
112 GLC(gl, gl->ProduceTextureDirectCHROMIUM(lock.texture_id(), GL_TEXTURE_2D,
113 mailbox.name));
114 }
115 all_resources_.push_front(
116 PlaneResource(resource_id, plane_size, format, mailbox));
117 return all_resources_.begin();
118}
119
120void VideoResourceUpdater::DeleteResource(ResourceList::iterator resource_it) {
121 DCHECK_EQ(resource_it->ref_count, 0);
122 resource_provider_->DeleteResource(resource_it->resource_id);
123 all_resources_.erase(resource_it);
James Robinson646469d2014-10-03 15:33:28 -0700124}
125
126VideoFrameExternalResources VideoResourceUpdater::
127 CreateExternalResourcesFromVideoFrame(
128 const scoped_refptr<media::VideoFrame>& video_frame) {
129 if (!VerifyFrame(video_frame))
130 return VideoFrameExternalResources();
131
132 if (video_frame->format() == media::VideoFrame::NATIVE_TEXTURE)
133 return CreateForHardwarePlanes(video_frame);
134 else
135 return CreateForSoftwarePlanes(video_frame);
136}
137
138bool VideoResourceUpdater::VerifyFrame(
139 const scoped_refptr<media::VideoFrame>& video_frame) {
140 switch (video_frame->format()) {
141 // Acceptable inputs.
142 case media::VideoFrame::YV12:
143 case media::VideoFrame::I420:
144 case media::VideoFrame::YV12A:
145 case media::VideoFrame::YV16:
146 case media::VideoFrame::YV12J:
147 case media::VideoFrame::YV24:
148 case media::VideoFrame::NATIVE_TEXTURE:
149#if defined(VIDEO_HOLE)
150 case media::VideoFrame::HOLE:
151#endif // defined(VIDEO_HOLE)
James Robinsond753aca2015-01-12 13:07:20 -0800152 case media::VideoFrame::ARGB:
James Robinson646469d2014-10-03 15:33:28 -0700153 return true;
154
155 // Unacceptable inputs. ¯\(°_o)/¯
156 case media::VideoFrame::UNKNOWN:
157 case media::VideoFrame::NV12:
158 break;
159 }
160 return false;
161}
162
163// For frames that we receive in software format, determine the dimensions of
164// each plane in the frame.
165static gfx::Size SoftwarePlaneDimension(
166 const scoped_refptr<media::VideoFrame>& input_frame,
James Robinson6a64b812014-12-03 13:38:42 -0800167 bool software_compositor,
James Robinson646469d2014-10-03 15:33:28 -0700168 size_t plane_index) {
James Robinson6a64b812014-12-03 13:38:42 -0800169 if (!software_compositor) {
James Robinson646469d2014-10-03 15:33:28 -0700170 return media::VideoFrame::PlaneSize(
171 input_frame->format(), plane_index, input_frame->coded_size());
172 }
James Robinson646469d2014-10-03 15:33:28 -0700173 return input_frame->coded_size();
174}
175
176VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes(
177 const scoped_refptr<media::VideoFrame>& video_frame) {
178 TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForSoftwarePlanes");
179 media::VideoFrame::Format input_frame_format = video_frame->format();
180
181#if defined(VIDEO_HOLE)
182 if (input_frame_format == media::VideoFrame::HOLE) {
183 VideoFrameExternalResources external_resources;
184 external_resources.type = VideoFrameExternalResources::HOLE;
185 return external_resources;
186 }
187#endif // defined(VIDEO_HOLE)
188
189 // Only YUV software video frames are supported.
James Robinson646469d2014-10-03 15:33:28 -0700190 if (input_frame_format != media::VideoFrame::YV12 &&
191 input_frame_format != media::VideoFrame::I420 &&
192 input_frame_format != media::VideoFrame::YV12A &&
193 input_frame_format != media::VideoFrame::YV12J &&
194 input_frame_format != media::VideoFrame::YV16 &&
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100195 input_frame_format != media::VideoFrame::YV24) {
196 NOTREACHED() << input_frame_format;
James Robinson646469d2014-10-03 15:33:28 -0700197 return VideoFrameExternalResources();
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100198 }
James Robinson646469d2014-10-03 15:33:28 -0700199
200 bool software_compositor = context_provider_ == NULL;
201
James Robinson6a64b812014-12-03 13:38:42 -0800202 ResourceFormat output_resource_format =
203 resource_provider_->yuv_resource_format();
James Robinson646469d2014-10-03 15:33:28 -0700204 size_t output_plane_count = media::VideoFrame::NumPlanes(input_frame_format);
205
206 // TODO(skaslev): If we're in software compositing mode, we do the YUV -> RGB
207 // conversion here. That involves an extra copy of each frame to a bitmap.
208 // Obviously, this is suboptimal and should be addressed once ubercompositor
209 // starts shaping up.
210 if (software_compositor) {
211 output_resource_format = kRGBResourceFormat;
212 output_plane_count = 1;
213 }
214
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100215 // Drop recycled resources that are the wrong format.
216 for (auto it = all_resources_.begin(); it != all_resources_.end();) {
217 if (it->ref_count == 0 && it->resource_format != output_resource_format)
218 DeleteResource(it++);
219 else
220 ++it;
221 }
James Robinson646469d2014-10-03 15:33:28 -0700222
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100223 const int max_resource_size = resource_provider_->max_texture_size();
224 std::vector<ResourceList::iterator> plane_resources;
James Robinson646469d2014-10-03 15:33:28 -0700225 for (size_t i = 0; i < output_plane_count; ++i) {
226 gfx::Size output_plane_resource_size =
James Robinson6a64b812014-12-03 13:38:42 -0800227 SoftwarePlaneDimension(video_frame, software_compositor, i);
James Robinson646469d2014-10-03 15:33:28 -0700228 if (output_plane_resource_size.IsEmpty() ||
229 output_plane_resource_size.width() > max_resource_size ||
230 output_plane_resource_size.height() > max_resource_size) {
James Robinson646469d2014-10-03 15:33:28 -0700231 break;
232 }
233
James Robinson646469d2014-10-03 15:33:28 -0700234 // Try recycle a previously-allocated resource.
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100235 ResourceList::iterator resource_it = all_resources_.end();
236 for (auto it = all_resources_.begin(); it != all_resources_.end(); ++it) {
237 if (it->resource_size == output_plane_resource_size &&
238 it->resource_format == output_resource_format) {
239 if (PlaneResourceMatchesUniqueID(*it, video_frame.get(), i)) {
240 // Bingo, we found a resource that already contains the data we are
241 // planning to put in it. It's safe to reuse it even if
242 // resource_provider_ holds some references to it, because those
243 // references are read-only.
244 resource_it = it;
James Robinson6a64b812014-12-03 13:38:42 -0800245 break;
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100246 }
247
248 // This extra check is needed because resources backed by SharedMemory
249 // are not ref-counted, unlike mailboxes. Full discussion in
250 // codereview.chromium.org/145273021.
251 const bool in_use =
252 software_compositor &&
253 resource_provider_->InUseByConsumer(it->resource_id);
254 if (it->ref_count == 0 && !in_use) {
255 // We found a resource with the correct size that we can overwrite.
256 resource_it = it;
257 }
James Robinson646469d2014-10-03 15:33:28 -0700258 }
259 }
260
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100261 // Check if we need to allocate a new resource.
262 if (resource_it == all_resources_.end()) {
263 resource_it =
264 AllocateResource(output_plane_resource_size, output_resource_format,
265 !software_compositor);
James Robinson646469d2014-10-03 15:33:28 -0700266 }
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100267 if (resource_it == all_resources_.end())
James Robinson646469d2014-10-03 15:33:28 -0700268 break;
James Robinson6a64b812014-12-03 13:38:42 -0800269
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100270 ++resource_it->ref_count;
271 plane_resources.push_back(resource_it);
James Robinson646469d2014-10-03 15:33:28 -0700272 }
273
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100274 if (plane_resources.size() != output_plane_count) {
275 // Allocation failed, nothing will be returned so restore reference counts.
276 for (ResourceList::iterator resource_it : plane_resources)
277 --resource_it->ref_count;
James Robinson646469d2014-10-03 15:33:28 -0700278 return VideoFrameExternalResources();
279 }
280
281 VideoFrameExternalResources external_resources;
282
283 if (software_compositor) {
284 DCHECK_EQ(plane_resources.size(), 1u);
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100285 PlaneResource& plane_resource = *plane_resources[0];
286 DCHECK_EQ(plane_resource.resource_format, kRGBResourceFormat);
287 DCHECK(plane_resource.mailbox.IsZero());
James Robinson646469d2014-10-03 15:33:28 -0700288
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100289 if (!PlaneResourceMatchesUniqueID(plane_resource, video_frame.get(), 0)) {
James Robinson6a64b812014-12-03 13:38:42 -0800290 // We need to transfer data from |video_frame| to the plane resource.
291 if (!video_renderer_)
292 video_renderer_.reset(new media::SkCanvasVideoRenderer);
James Robinson646469d2014-10-03 15:33:28 -0700293
James Robinson646469d2014-10-03 15:33:28 -0700294 ResourceProvider::ScopedWriteLockSoftware lock(
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100295 resource_provider_, plane_resource.resource_id);
James Robinsonc4c1c592014-11-21 18:27:04 -0800296 SkCanvas canvas(lock.sk_bitmap());
297 video_renderer_->Copy(video_frame, &canvas);
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100298 SetPlaneResourceUniqueId(video_frame.get(), 0, &plane_resource);
James Robinson646469d2014-10-03 15:33:28 -0700299 }
300
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100301 external_resources.software_resources.push_back(plane_resource.resource_id);
James Robinson646469d2014-10-03 15:33:28 -0700302 external_resources.software_release_callback =
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100303 base::Bind(&RecycleResource, AsWeakPtr(), plane_resource.resource_id);
James Robinson646469d2014-10-03 15:33:28 -0700304 external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE;
James Robinson646469d2014-10-03 15:33:28 -0700305 return external_resources;
306 }
307
308 for (size_t i = 0; i < plane_resources.size(); ++i) {
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100309 PlaneResource& plane_resource = *plane_resources[i];
James Robinson646469d2014-10-03 15:33:28 -0700310 // Update each plane's resource id with its content.
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100311 DCHECK_EQ(plane_resource.resource_format,
James Robinson6a64b812014-12-03 13:38:42 -0800312 resource_provider_->yuv_resource_format());
James Robinson646469d2014-10-03 15:33:28 -0700313
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100314 if (!PlaneResourceMatchesUniqueID(plane_resource, video_frame.get(), i)) {
James Robinson6a64b812014-12-03 13:38:42 -0800315 // We need to transfer data from |video_frame| to the plane resource.
316 const uint8_t* input_plane_pixels = video_frame->data(i);
James Robinson646469d2014-10-03 15:33:28 -0700317
James Robinson6a64b812014-12-03 13:38:42 -0800318 gfx::Rect image_rect(0, 0, video_frame->stride(i),
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100319 plane_resource.resource_size.height());
320 gfx::Rect source_rect(plane_resource.resource_size);
321 resource_provider_->SetPixels(plane_resource.resource_id,
James Robinson6a64b812014-12-03 13:38:42 -0800322 input_plane_pixels, image_rect, source_rect,
323 gfx::Vector2d());
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100324 SetPlaneResourceUniqueId(video_frame.get(), i, &plane_resource);
James Robinson6a64b812014-12-03 13:38:42 -0800325 }
James Robinson646469d2014-10-03 15:33:28 -0700326
327 external_resources.mailboxes.push_back(
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100328 TextureMailbox(plane_resource.mailbox, GL_TEXTURE_2D, 0));
James Robinson646469d2014-10-03 15:33:28 -0700329 external_resources.release_callbacks.push_back(
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100330 base::Bind(&RecycleResource, AsWeakPtr(), plane_resource.resource_id));
James Robinson646469d2014-10-03 15:33:28 -0700331 }
332
333 external_resources.type = VideoFrameExternalResources::YUV_RESOURCE;
334 return external_resources;
335}
336
337// static
338void VideoResourceUpdater::ReturnTexture(
339 base::WeakPtr<VideoResourceUpdater> updater,
340 const scoped_refptr<media::VideoFrame>& video_frame,
341 uint32 sync_point,
342 bool lost_resource,
343 BlockingTaskRunner* main_thread_task_runner) {
344 // TODO(dshwang) this case should be forwarded to the decoder as lost
345 // resource.
346 if (lost_resource || !updater.get())
347 return;
348 // VideoFrame::UpdateReleaseSyncPoint() creates new sync point using the same
349 // GL context which created the given |sync_point|, so discard the
350 // |sync_point|.
351 SyncPointClientImpl client(updater->context_provider_->ContextGL());
352 video_frame->UpdateReleaseSyncPoint(&client);
353}
354
355VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes(
356 const scoped_refptr<media::VideoFrame>& video_frame) {
357 TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForHardwarePlanes");
358 media::VideoFrame::Format frame_format = video_frame->format();
359
360 DCHECK_EQ(frame_format, media::VideoFrame::NATIVE_TEXTURE);
361 if (frame_format != media::VideoFrame::NATIVE_TEXTURE)
362 return VideoFrameExternalResources();
363
364 if (!context_provider_)
365 return VideoFrameExternalResources();
366
367 const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
368 VideoFrameExternalResources external_resources;
369 switch (mailbox_holder->texture_target) {
370 case GL_TEXTURE_2D:
371 external_resources.type = VideoFrameExternalResources::RGB_RESOURCE;
372 break;
373 case GL_TEXTURE_EXTERNAL_OES:
374 external_resources.type =
375 VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE;
376 break;
377 case GL_TEXTURE_RECTANGLE_ARB:
378 external_resources.type = VideoFrameExternalResources::IO_SURFACE;
379 break;
380 default:
381 NOTREACHED();
382 return VideoFrameExternalResources();
383 }
384
385 external_resources.mailboxes.push_back(
386 TextureMailbox(mailbox_holder->mailbox,
387 mailbox_holder->texture_target,
388 mailbox_holder->sync_point));
389 external_resources.release_callbacks.push_back(
390 base::Bind(&ReturnTexture, AsWeakPtr(), video_frame));
391 return external_resources;
392}
393
394// static
395void VideoResourceUpdater::RecycleResource(
396 base::WeakPtr<VideoResourceUpdater> updater,
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100397 ResourceProvider::ResourceId resource_id,
James Robinson646469d2014-10-03 15:33:28 -0700398 uint32 sync_point,
399 bool lost_resource,
400 BlockingTaskRunner* main_thread_task_runner) {
401 if (!updater.get()) {
402 // Resource was already deleted.
403 return;
404 }
405
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100406 const ResourceList::iterator resource_it = std::find_if(
407 updater->all_resources_.begin(), updater->all_resources_.end(),
408 [resource_id](const PlaneResource& plane_resource) {
409 return plane_resource.resource_id == resource_id;
410 });
411 if (resource_it == updater->all_resources_.end())
412 return;
413
James Robinson646469d2014-10-03 15:33:28 -0700414 ContextProvider* context_provider = updater->context_provider_;
415 if (context_provider && sync_point) {
416 GLC(context_provider->ContextGL(),
417 context_provider->ContextGL()->WaitSyncPointCHROMIUM(sync_point));
418 }
419
420 if (lost_resource) {
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100421 resource_it->ref_count = 0;
422 updater->DeleteResource(resource_it);
James Robinson646469d2014-10-03 15:33:28 -0700423 return;
424 }
425
Etienne Membrivesb1556b32014-12-16 13:56:09 +0100426 --resource_it->ref_count;
427 DCHECK_GE(resource_it->ref_count, 0);
James Robinson646469d2014-10-03 15:33:28 -0700428}
429
430} // namespace cc