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 "cc/output/gl_renderer.h"
9 #include "cc/resources/resource_provider.h"
10 #include "gpu/GLES2/gl2extchromium.h"
11 #include "media/base/video_frame.h"
12 #include "media/filters/skcanvas_video_renderer.h"
13 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
14 #include "third_party/khronos/GLES2/gl2.h"
15 #include "third_party/khronos/GLES2/gl2ext.h"
16 #include "ui/gfx/size_conversions.h"
20 const ResourceFormat kYUVResourceFormat
= LUMINANCE_8
;
21 const ResourceFormat kRGBResourceFormat
= RGBA_8888
;
23 VideoFrameExternalResources::VideoFrameExternalResources() : type(NONE
) {}
25 VideoFrameExternalResources::~VideoFrameExternalResources() {}
27 VideoResourceUpdater::VideoResourceUpdater(ContextProvider
* context_provider
,
28 ResourceProvider
* resource_provider
)
29 : context_provider_(context_provider
),
30 resource_provider_(resource_provider
) {
33 VideoResourceUpdater::~VideoResourceUpdater() {
34 while (!all_resources_
.empty()) {
35 resource_provider_
->DeleteResource(all_resources_
.back());
36 all_resources_
.pop_back();
40 void VideoResourceUpdater::DeleteResource(unsigned resource_id
) {
41 resource_provider_
->DeleteResource(resource_id
);
42 all_resources_
.erase(std::remove(all_resources_
.begin(),
47 VideoFrameExternalResources
VideoResourceUpdater::
48 CreateExternalResourcesFromVideoFrame(
49 const scoped_refptr
<media::VideoFrame
>& video_frame
) {
50 if (!VerifyFrame(video_frame
))
51 return VideoFrameExternalResources();
53 if (video_frame
->format() == media::VideoFrame::NATIVE_TEXTURE
)
54 return CreateForHardwarePlanes(video_frame
);
56 return CreateForSoftwarePlanes(video_frame
);
59 bool VideoResourceUpdater::VerifyFrame(
60 const scoped_refptr
<media::VideoFrame
>& video_frame
) {
61 // If these fail, we'll have to add logic that handles offset bitmap/texture
62 // UVs. For now, just expect (0, 0) offset, since all our decoders so far
64 DCHECK_EQ(video_frame
->visible_rect().x(), 0);
65 DCHECK_EQ(video_frame
->visible_rect().y(), 0);
67 switch (video_frame
->format()) {
69 case media::VideoFrame::YV12
:
70 case media::VideoFrame::YV12A
:
71 case media::VideoFrame::YV16
:
72 case media::VideoFrame::NATIVE_TEXTURE
:
73 #if defined(GOOGLE_TV)
74 case media::VideoFrame::HOLE
:
78 // Unacceptable inputs. ¯\(°_o)/¯
79 case media::VideoFrame::INVALID
:
80 case media::VideoFrame::RGB32
:
81 case media::VideoFrame::EMPTY
:
82 case media::VideoFrame::I420
:
88 // For frames that we receive in software format, determine the dimensions of
89 // each plane in the frame.
90 static gfx::Size
SoftwarePlaneDimension(
91 media::VideoFrame::Format input_frame_format
,
93 ResourceFormat output_resource_format
,
95 if (output_resource_format
== kYUVResourceFormat
) {
96 if (plane_index
== media::VideoFrame::kYPlane
||
97 plane_index
== media::VideoFrame::kAPlane
)
100 switch (input_frame_format
) {
101 case media::VideoFrame::YV12
:
102 case media::VideoFrame::YV12A
:
103 return gfx::ToFlooredSize(gfx::ScaleSize(coded_size
, 0.5f
, 0.5f
));
104 case media::VideoFrame::YV16
:
105 return gfx::ToFlooredSize(gfx::ScaleSize(coded_size
, 0.5f
, 1.f
));
107 case media::VideoFrame::INVALID
:
108 case media::VideoFrame::RGB32
:
109 case media::VideoFrame::EMPTY
:
110 case media::VideoFrame::I420
:
111 case media::VideoFrame::NATIVE_TEXTURE
:
112 #if defined(GOOGLE_TV)
113 case media::VideoFrame::HOLE
:
119 DCHECK_EQ(output_resource_format
, kRGBResourceFormat
);
123 VideoFrameExternalResources
VideoResourceUpdater::CreateForSoftwarePlanes(
124 const scoped_refptr
<media::VideoFrame
>& video_frame
) {
125 media::VideoFrame::Format input_frame_format
= video_frame
->format();
127 #if defined(GOOGLE_TV)
128 if (input_frame_format
== media::VideoFrame::HOLE
) {
129 VideoFrameExternalResources external_resources
;
130 external_resources
.type
= VideoFrameExternalResources::HOLE
;
131 return external_resources
;
135 // Only YUV software video frames are supported.
136 DCHECK(input_frame_format
== media::VideoFrame::YV12
||
137 input_frame_format
== media::VideoFrame::YV12A
||
138 input_frame_format
== media::VideoFrame::YV16
);
139 if (input_frame_format
!= media::VideoFrame::YV12
&&
140 input_frame_format
!= media::VideoFrame::YV12A
&&
141 input_frame_format
!= media::VideoFrame::YV16
)
142 return VideoFrameExternalResources();
144 bool software_compositor
= context_provider_
== NULL
;
146 ResourceFormat output_resource_format
= kYUVResourceFormat
;
147 size_t output_plane_count
=
148 (input_frame_format
== media::VideoFrame::YV12A
) ? 4 : 3;
150 // TODO(skaslev): If we're in software compositing mode, we do the YUV -> RGB
151 // conversion here. That involves an extra copy of each frame to a bitmap.
152 // Obviously, this is suboptimal and should be addressed once ubercompositor
153 // starts shaping up.
154 if (software_compositor
) {
155 output_resource_format
= kRGBResourceFormat
;
156 output_plane_count
= 1;
159 int max_resource_size
= resource_provider_
->max_texture_size();
160 gfx::Size coded_frame_size
= video_frame
->coded_size();
162 std::vector
<PlaneResource
> plane_resources
;
163 bool allocation_success
= true;
165 for (size_t i
= 0; i
< output_plane_count
; ++i
) {
166 gfx::Size output_plane_resource_size
=
167 SoftwarePlaneDimension(input_frame_format
,
169 output_resource_format
,
171 if (output_plane_resource_size
.IsEmpty() ||
172 output_plane_resource_size
.width() > max_resource_size
||
173 output_plane_resource_size
.height() > max_resource_size
) {
174 allocation_success
= false;
178 ResourceProvider::ResourceId resource_id
= 0;
179 gpu::Mailbox mailbox
;
181 // Try recycle a previously-allocated resource.
182 for (size_t i
= 0; i
< recycled_resources_
.size(); ++i
) {
183 if (recycled_resources_
[i
].resource_format
== output_resource_format
&&
184 recycled_resources_
[i
].resource_size
== output_plane_resource_size
) {
185 resource_id
= recycled_resources_
[i
].resource_id
;
186 mailbox
= recycled_resources_
[i
].mailbox
;
187 recycled_resources_
.erase(recycled_resources_
.begin() + i
);
192 if (resource_id
== 0) {
193 // TODO(danakj): Abstract out hw/sw resource create/delete from
194 // ResourceProvider and stop using ResourceProvider in this class.
196 resource_provider_
->CreateResource(output_plane_resource_size
,
198 ResourceProvider::TextureUsageAny
,
199 output_resource_format
);
201 DCHECK(mailbox
.IsZero());
203 if (!software_compositor
) {
204 DCHECK(context_provider_
);
206 WebKit::WebGraphicsContext3D
* context
=
207 context_provider_
->Context3d();
209 GLC(context
, context
->genMailboxCHROMIUM(mailbox
.name
));
210 if (mailbox
.IsZero()) {
211 resource_provider_
->DeleteResource(resource_id
);
214 ResourceProvider::ScopedWriteLockGL
lock(
215 resource_provider_
, resource_id
);
216 GLC(context
, context
->bindTexture(GL_TEXTURE_2D
, lock
.texture_id()));
217 GLC(context
, context
->produceTextureCHROMIUM(GL_TEXTURE_2D
,
219 GLC(context
, context
->bindTexture(GL_TEXTURE_2D
, 0));
224 all_resources_
.push_back(resource_id
);
227 if (resource_id
== 0) {
228 allocation_success
= false;
232 DCHECK(software_compositor
|| !mailbox
.IsZero());
233 plane_resources
.push_back(PlaneResource(resource_id
,
234 output_plane_resource_size
,
235 output_resource_format
,
239 if (!allocation_success
) {
240 for (size_t i
= 0; i
< plane_resources
.size(); ++i
)
241 DeleteResource(plane_resources
[i
].resource_id
);
242 return VideoFrameExternalResources();
245 VideoFrameExternalResources external_resources
;
247 if (software_compositor
) {
248 DCHECK_EQ(plane_resources
.size(), 1u);
249 DCHECK_EQ(plane_resources
[0].resource_format
, kRGBResourceFormat
);
250 DCHECK(plane_resources
[0].mailbox
.IsZero());
252 if (!video_renderer_
)
253 video_renderer_
.reset(new media::SkCanvasVideoRenderer
);
256 ResourceProvider::ScopedWriteLockSoftware
lock(
257 resource_provider_
, plane_resources
[0].resource_id
);
258 video_renderer_
->Paint(video_frame
.get(),
260 video_frame
->visible_rect(),
264 // In software mode, the resource provider won't be lost. Soon this callback
265 // will be called directly from the resource provider, same as 3d
266 // compositing mode, so this raw unretained resource_provider will always
267 // be valid when the callback is fired.
268 RecycleResourceData recycle_data
= {
269 plane_resources
[0].resource_id
,
270 plane_resources
[0].resource_size
,
271 plane_resources
[0].resource_format
,
275 external_resources
.software_resources
.push_back(
276 plane_resources
[0].resource_id
);
277 external_resources
.software_release_callback
=
278 base::Bind(&RecycleResource
, AsWeakPtr(), recycle_data
);
281 external_resources
.type
= VideoFrameExternalResources::SOFTWARE_RESOURCE
;
282 return external_resources
;
285 for (size_t i
= 0; i
< plane_resources
.size(); ++i
) {
286 // Update each plane's resource id with its content.
287 DCHECK_EQ(plane_resources
[i
].resource_format
, kYUVResourceFormat
);
289 const uint8_t* input_plane_pixels
= video_frame
->data(i
);
291 gfx::Rect
image_rect(0,
293 video_frame
->stride(i
),
294 plane_resources
[i
].resource_size
.height());
295 gfx::Rect
source_rect(plane_resources
[i
].resource_size
);
296 resource_provider_
->SetPixels(plane_resources
[i
].resource_id
,
302 RecycleResourceData recycle_data
= {
303 plane_resources
[i
].resource_id
,
304 plane_resources
[i
].resource_size
,
305 plane_resources
[i
].resource_format
,
306 plane_resources
[i
].mailbox
309 external_resources
.mailboxes
.push_back(
310 TextureMailbox(plane_resources
[i
].mailbox
));
311 external_resources
.release_callbacks
.push_back(
312 base::Bind(&RecycleResource
, AsWeakPtr(), recycle_data
));
315 external_resources
.type
= VideoFrameExternalResources::YUV_RESOURCE
;
316 return external_resources
;
319 static void ReturnTexture(
320 scoped_refptr
<media::VideoFrame::MailboxHolder
> mailbox_holder
,
322 bool lost_resource
) {
323 mailbox_holder
->Return(sync_point
);
326 VideoFrameExternalResources
VideoResourceUpdater::CreateForHardwarePlanes(
327 const scoped_refptr
<media::VideoFrame
>& video_frame
) {
328 media::VideoFrame::Format frame_format
= video_frame
->format();
330 DCHECK_EQ(frame_format
, media::VideoFrame::NATIVE_TEXTURE
);
331 if (frame_format
!= media::VideoFrame::NATIVE_TEXTURE
)
332 return VideoFrameExternalResources();
334 if (!context_provider_
)
335 return VideoFrameExternalResources();
337 VideoFrameExternalResources external_resources
;
338 switch (video_frame
->texture_target()) {
340 external_resources
.type
= VideoFrameExternalResources::RGB_RESOURCE
;
342 case GL_TEXTURE_EXTERNAL_OES
:
343 external_resources
.type
=
344 VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE
;
346 case GL_TEXTURE_RECTANGLE_ARB
:
347 external_resources
.type
= VideoFrameExternalResources::IO_SURFACE
;
351 return VideoFrameExternalResources();
354 scoped_refptr
<media::VideoFrame::MailboxHolder
> mailbox_holder
=
355 video_frame
->texture_mailbox();
357 external_resources
.mailboxes
.push_back(
358 TextureMailbox(mailbox_holder
->mailbox(),
359 video_frame
->texture_target(),
360 mailbox_holder
->sync_point()));
361 external_resources
.release_callbacks
.push_back(
362 base::Bind(&ReturnTexture
, mailbox_holder
));
363 return external_resources
;
367 void VideoResourceUpdater::RecycleResource(
368 base::WeakPtr
<VideoResourceUpdater
> updater
,
369 RecycleResourceData data
,
371 bool lost_resource
) {
372 if (!updater
.get()) {
373 // Resource was already deleted.
377 ContextProvider
* context_provider
= updater
->context_provider_
;
378 if (context_provider
&& sync_point
) {
379 GLC(context_provider
->Context3d(),
380 context_provider
->Context3d()->waitSyncPoint(sync_point
));
384 updater
->DeleteResource(data
.resource_id
);
388 // Drop recycled resources that are the wrong format.
389 while (!updater
->recycled_resources_
.empty() &&
390 updater
->recycled_resources_
.back().resource_format
!=
391 data
.resource_format
) {
392 updater
->DeleteResource(updater
->recycled_resources_
.back().resource_id
);
393 updater
->recycled_resources_
.pop_back();
396 PlaneResource
recycled_resource(data
.resource_id
,
398 data
.resource_format
,
400 updater
->recycled_resources_
.push_back(recycled_resource
);