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"
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"
23 const ResourceFormat kYUVResourceFormat
= LUMINANCE_8
;
24 const ResourceFormat kRGBResourceFormat
= RGBA_8888
;
26 class SyncPointClientImpl
: public media::VideoFrame::SyncPointClient
{
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
));
38 gpu::gles2::GLES2Interface
* gl_
;
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(),
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
);
76 return CreateForSoftwarePlanes(video_frame
);
79 bool VideoResourceUpdater::VerifyFrame(
80 const scoped_refptr
<media::VideoFrame
>& video_frame
) {
81 switch (video_frame
->format()) {
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)
95 // Unacceptable inputs. ¯\(°_o)/¯
96 case media::VideoFrame::UNKNOWN
:
97 case media::VideoFrame::NV12
:
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;
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
;
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
);
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
,
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_
,
213 gl
->ProduceTextureDirectCHROMIUM(
214 lock
.texture_id(), GL_TEXTURE_2D
, mailbox
.name
));
218 all_resources_
.push_back(resource_id
);
221 if (resource_id
== 0) {
222 allocation_success
= false;
226 DCHECK(software_compositor
|| !mailbox
.IsZero());
227 plane_resources
.push_back(PlaneResource(resource_id
,
228 output_plane_resource_size
,
229 output_resource_format
,
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
,
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
,
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
;
305 void VideoResourceUpdater::ReturnTexture(
306 base::WeakPtr
<VideoResourceUpdater
> updater
,
307 const scoped_refptr
<media::VideoFrame
>& video_frame
,
310 BlockingTaskRunner
* main_thread_task_runner
) {
311 // TODO(dshwang) this case should be forwarded to the decoder as lost
313 if (lost_resource
|| !updater
.get())
315 // VideoFrame::UpdateReleaseSyncPoint() creates new sync point using the same
316 // GL context which created the given |sync_point|, so discard the
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
) {
338 external_resources
.type
= VideoFrameExternalResources::RGB_RESOURCE
;
340 case GL_TEXTURE_EXTERNAL_OES
:
341 external_resources
.type
=
342 VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE
;
344 case GL_TEXTURE_RECTANGLE_ARB
:
345 external_resources
.type
= VideoFrameExternalResources::IO_SURFACE
;
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
;
362 void VideoResourceUpdater::RecycleResource(
363 base::WeakPtr
<VideoResourceUpdater
> updater
,
364 RecycleResourceData data
,
367 BlockingTaskRunner
* main_thread_task_runner
) {
368 if (!updater
.get()) {
369 // Resource was already deleted.
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
));
380 updater
->DeleteResource(data
.resource_id
);
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
,
394 data
.resource_format
,
396 updater
->recycled_resources_
.push_back(recycled_resource
);