1 // Copyright 2011 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/layers/video_layer_impl.h"
8 #include "base/logging.h"
9 #include "cc/layers/video_frame_provider_client_impl.h"
10 #include "cc/quads/io_surface_draw_quad.h"
11 #include "cc/quads/stream_video_draw_quad.h"
12 #include "cc/quads/texture_draw_quad.h"
13 #include "cc/quads/yuv_video_draw_quad.h"
14 #include "cc/resources/resource_provider.h"
15 #include "cc/resources/single_release_callback_impl.h"
16 #include "cc/trees/layer_tree_impl.h"
17 #include "cc/trees/occlusion.h"
18 #include "cc/trees/proxy.h"
19 #include "media/base/video_frame.h"
21 #if defined(VIDEO_HOLE)
22 #include "cc/quads/solid_color_draw_quad.h"
23 #endif // defined(VIDEO_HOLE)
28 scoped_ptr
<VideoLayerImpl
> VideoLayerImpl::Create(
29 LayerTreeImpl
* tree_impl
,
31 VideoFrameProvider
* provider
,
32 media::VideoRotation video_rotation
) {
33 DCHECK(tree_impl
->proxy()->IsMainThreadBlocked());
34 DCHECK(tree_impl
->proxy()->IsImplThread());
36 scoped_refptr
<VideoFrameProviderClientImpl
> provider_client_impl
=
37 VideoFrameProviderClientImpl::Create(
38 provider
, tree_impl
->GetVideoFrameControllerClient());
40 return make_scoped_ptr(
41 new VideoLayerImpl(tree_impl
, id
, provider_client_impl
, video_rotation
));
44 VideoLayerImpl::VideoLayerImpl(
45 LayerTreeImpl
* tree_impl
,
47 const scoped_refptr
<VideoFrameProviderClientImpl
>& provider_client_impl
,
48 media::VideoRotation video_rotation
)
49 : LayerImpl(tree_impl
, id
),
50 provider_client_impl_(provider_client_impl
),
52 video_rotation_(video_rotation
) {
55 VideoLayerImpl::~VideoLayerImpl() {
56 if (!provider_client_impl_
->Stopped()) {
57 // In impl side painting, we may have a pending and active layer
58 // associated with the video provider at the same time. Both have a ref
59 // on the VideoFrameProviderClientImpl, but we stop when the first
60 // LayerImpl (the one on the pending tree) is destroyed since we know
61 // the main thread is blocked for this commit.
62 DCHECK(layer_tree_impl()->proxy()->IsImplThread());
63 DCHECK(layer_tree_impl()->proxy()->IsMainThreadBlocked());
64 provider_client_impl_
->Stop();
68 scoped_ptr
<LayerImpl
> VideoLayerImpl::CreateLayerImpl(
69 LayerTreeImpl
* tree_impl
) {
70 return make_scoped_ptr(new VideoLayerImpl(
71 tree_impl
, id(), provider_client_impl_
, video_rotation_
));
74 void VideoLayerImpl::DidBecomeActive() {
75 provider_client_impl_
->SetActiveVideoLayer(this);
78 bool VideoLayerImpl::WillDraw(DrawMode draw_mode
,
79 ResourceProvider
* resource_provider
) {
80 if (draw_mode
== DRAW_MODE_RESOURCELESS_SOFTWARE
)
83 // Explicitly acquire and release the provider mutex so it can be held from
84 // WillDraw to DidDraw. Since the compositor thread is in the middle of
85 // drawing, the layer will not be destroyed before DidDraw is called.
86 // Therefore, the only thing that will prevent this lock from being released
87 // is the GPU process locking it. As the GPU process can't cause the
88 // destruction of the provider (calling StopUsingProvider), holding this
89 // lock should not cause a deadlock.
90 frame_
= provider_client_impl_
->AcquireLockAndCurrentFrame();
93 // Drop any resources used by the updater if there is no frame to display.
96 provider_client_impl_
->ReleaseLock();
100 if (!LayerImpl::WillDraw(draw_mode
, resource_provider
))
105 new VideoResourceUpdater(layer_tree_impl()->context_provider(),
106 layer_tree_impl()->resource_provider()));
109 VideoFrameExternalResources external_resources
=
110 updater_
->CreateExternalResourcesFromVideoFrame(frame_
);
111 frame_resource_type_
= external_resources
.type
;
113 if (external_resources
.type
==
114 VideoFrameExternalResources::SOFTWARE_RESOURCE
) {
115 software_resources_
= external_resources
.software_resources
;
116 software_release_callback_
=
117 external_resources
.software_release_callback
;
121 DCHECK_EQ(external_resources
.mailboxes
.size(),
122 external_resources
.release_callbacks
.size());
123 for (size_t i
= 0; i
< external_resources
.mailboxes
.size(); ++i
) {
124 unsigned resource_id
= resource_provider
->CreateResourceFromTextureMailbox(
125 external_resources
.mailboxes
[i
],
126 SingleReleaseCallbackImpl::Create(
127 external_resources
.release_callbacks
[i
]),
128 external_resources
.read_lock_fences_enabled
);
129 frame_resources_
.push_back(FrameResource(
130 resource_id
, external_resources
.mailboxes
[i
].size_in_pixels(),
131 external_resources
.mailboxes
[i
].allow_overlay()));
137 void VideoLayerImpl::AppendQuads(RenderPass
* render_pass
,
138 AppendQuadsData
* append_quads_data
) {
139 DCHECK(frame_
.get());
141 gfx::Transform transform
= draw_transform();
142 gfx::Size rotated_size
= bounds();
144 switch (video_rotation_
) {
145 case media::VIDEO_ROTATION_90
:
146 rotated_size
= gfx::Size(rotated_size
.height(), rotated_size
.width());
147 transform
.Rotate(90.0);
148 transform
.Translate(0.0, -rotated_size
.height());
150 case media::VIDEO_ROTATION_180
:
151 transform
.Rotate(180.0);
152 transform
.Translate(-rotated_size
.width(), -rotated_size
.height());
154 case media::VIDEO_ROTATION_270
:
155 rotated_size
= gfx::Size(rotated_size
.height(), rotated_size
.width());
156 transform
.Rotate(270.0);
157 transform
.Translate(-rotated_size
.width(), 0);
158 case media::VIDEO_ROTATION_0
:
162 SharedQuadState
* shared_quad_state
=
163 render_pass
->CreateAndAppendSharedQuadState();
164 shared_quad_state
->SetAll(transform
, rotated_size
, visible_layer_rect(),
165 clip_rect(), is_clipped(), draw_opacity(),
166 draw_blend_mode(), sorting_context_id());
168 AppendDebugBorderQuad(
169 render_pass
, rotated_size
, shared_quad_state
, append_quads_data
);
171 gfx::Rect
quad_rect(rotated_size
);
172 gfx::Rect
opaque_rect(contents_opaque() ? quad_rect
: gfx::Rect());
173 gfx::Rect visible_rect
= frame_
->visible_rect();
174 gfx::Size coded_size
= frame_
->coded_size();
176 Occlusion occlusion_in_video_space
=
178 .occlusion_in_content_space
.GetOcclusionWithGivenDrawTransform(
180 gfx::Rect visible_quad_rect
=
181 occlusion_in_video_space
.GetUnoccludedContentRect(quad_rect
);
182 if (visible_quad_rect
.IsEmpty())
185 // Pixels for macroblocked formats.
186 const float tex_width_scale
=
187 static_cast<float>(visible_rect
.width()) / coded_size
.width();
188 const float tex_height_scale
=
189 static_cast<float>(visible_rect
.height()) / coded_size
.height();
191 switch (frame_resource_type_
) {
192 // TODO(danakj): Remove this, hide it in the hardware path.
193 case VideoFrameExternalResources::SOFTWARE_RESOURCE
: {
194 DCHECK_EQ(frame_resources_
.size(), 0u);
195 DCHECK_EQ(software_resources_
.size(), 1u);
196 if (software_resources_
.size() < 1u)
198 bool premultiplied_alpha
= true;
199 gfx::PointF
uv_top_left(0.f
, 0.f
);
200 gfx::PointF
uv_bottom_right(tex_width_scale
, tex_height_scale
);
201 float opacity
[] = {1.0f
, 1.0f
, 1.0f
, 1.0f
};
202 bool flipped
= false;
203 bool nearest_neighbor
= false;
204 TextureDrawQuad
* texture_quad
=
205 render_pass
->CreateAndAppendDrawQuad
<TextureDrawQuad
>();
206 texture_quad
->SetNew(shared_quad_state
,
210 software_resources_
[0],
218 ValidateQuadResources(texture_quad
);
221 case VideoFrameExternalResources::YUV_RESOURCE
: {
222 DCHECK_GE(frame_resources_
.size(), 3u);
224 YUVVideoDrawQuad::ColorSpace color_space
= YUVVideoDrawQuad::REC_601
;
225 int videoframe_color_space
;
226 if (frame_
->metadata()->GetInteger(media::VideoFrameMetadata::COLOR_SPACE
,
227 &videoframe_color_space
)) {
228 if (videoframe_color_space
== media::VideoFrame::COLOR_SPACE_JPEG
) {
229 color_space
= YUVVideoDrawQuad::JPEG
;
230 } else if (videoframe_color_space
==
231 media::VideoFrame::COLOR_SPACE_HD_REC709
) {
232 color_space
= YUVVideoDrawQuad::REC_709
;
236 const gfx::Size ya_tex_size
= coded_size
;
237 gfx::Size uv_tex_size
;
239 if (frame_
->HasTextures()) {
240 DCHECK_EQ(media::VideoFrame::I420
, frame_
->format());
241 DCHECK_EQ(3u, frame_resources_
.size()); // Alpha is not supported yet.
242 DCHECK(visible_rect
.origin().IsOrigin());
243 DCHECK(visible_rect
.size() == coded_size
);
244 uv_tex_size
.SetSize((ya_tex_size
.width() + 1) / 2,
245 (ya_tex_size
.height() + 1) / 2);
247 uv_tex_size
= media::VideoFrame::PlaneSize(
248 frame_
->format(), media::VideoFrame::kUPlane
, coded_size
);
249 DCHECK(uv_tex_size
==
250 media::VideoFrame::PlaneSize(
251 frame_
->format(), media::VideoFrame::kVPlane
, coded_size
));
253 frame_resources_
.size() > 3,
255 media::VideoFrame::PlaneSize(
256 frame_
->format(), media::VideoFrame::kAPlane
, coded_size
));
259 // Compute the UV sub-sampling factor based on the ratio between
260 // |ya_tex_size| and |uv_tex_size|.
261 float uv_subsampling_factor_x
=
262 static_cast<float>(ya_tex_size
.width()) / uv_tex_size
.width();
263 float uv_subsampling_factor_y
=
264 static_cast<float>(ya_tex_size
.height()) / uv_tex_size
.height();
265 gfx::RectF
ya_tex_coord_rect(visible_rect
);
266 gfx::RectF
uv_tex_coord_rect(
267 visible_rect
.x() / uv_subsampling_factor_x
,
268 visible_rect
.y() / uv_subsampling_factor_y
,
269 visible_rect
.width() / uv_subsampling_factor_x
,
270 visible_rect
.height() / uv_subsampling_factor_y
);
272 YUVVideoDrawQuad
* yuv_video_quad
=
273 render_pass
->CreateAndAppendDrawQuad
<YUVVideoDrawQuad
>();
274 yuv_video_quad
->SetNew(
275 shared_quad_state
, quad_rect
, opaque_rect
, visible_quad_rect
,
276 ya_tex_coord_rect
, uv_tex_coord_rect
, ya_tex_size
, uv_tex_size
,
277 frame_resources_
[0].id
, frame_resources_
[1].id
,
278 frame_resources_
[2].id
,
279 frame_resources_
.size() > 3 ? frame_resources_
[3].id
: 0,
281 ValidateQuadResources(yuv_video_quad
);
284 case VideoFrameExternalResources::RGBA_RESOURCE
:
285 case VideoFrameExternalResources::RGB_RESOURCE
: {
286 DCHECK_EQ(frame_resources_
.size(), 1u);
287 if (frame_resources_
.size() < 1u)
289 bool premultiplied_alpha
=
290 (frame_resource_type_
== VideoFrameExternalResources::RGBA_RESOURCE
);
291 gfx::PointF
uv_top_left(0.f
, 0.f
);
292 gfx::PointF
uv_bottom_right(tex_width_scale
, tex_height_scale
);
293 float opacity
[] = {1.0f
, 1.0f
, 1.0f
, 1.0f
};
294 bool flipped
= false;
295 bool nearest_neighbor
= false;
296 TextureDrawQuad
* texture_quad
=
297 render_pass
->CreateAndAppendDrawQuad
<TextureDrawQuad
>();
298 texture_quad
->SetNew(shared_quad_state
, quad_rect
, opaque_rect
,
299 visible_quad_rect
, frame_resources_
[0].id
,
300 premultiplied_alpha
, uv_top_left
, uv_bottom_right
,
301 SK_ColorTRANSPARENT
, opacity
, flipped
,
303 ValidateQuadResources(texture_quad
);
306 case VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE
: {
307 DCHECK_EQ(frame_resources_
.size(), 1u);
308 if (frame_resources_
.size() < 1u)
310 gfx::Transform scale
;
311 scale
.Scale(tex_width_scale
, tex_height_scale
);
312 StreamVideoDrawQuad
* stream_video_quad
=
313 render_pass
->CreateAndAppendDrawQuad
<StreamVideoDrawQuad
>();
314 stream_video_quad
->SetNew(
315 shared_quad_state
, quad_rect
, opaque_rect
, visible_quad_rect
,
316 frame_resources_
[0].id
, frame_resources_
[0].size_in_pixels
,
317 frame_resources_
[0].allow_overlay
,
318 scale
* provider_client_impl_
->StreamTextureMatrix());
319 ValidateQuadResources(stream_video_quad
);
322 case VideoFrameExternalResources::IO_SURFACE
: {
323 DCHECK_EQ(frame_resources_
.size(), 1u);
324 if (frame_resources_
.size() < 1u)
326 IOSurfaceDrawQuad
* io_surface_quad
=
327 render_pass
->CreateAndAppendDrawQuad
<IOSurfaceDrawQuad
>();
328 io_surface_quad
->SetNew(shared_quad_state
, quad_rect
, opaque_rect
,
329 visible_quad_rect
, visible_rect
.size(),
330 frame_resources_
[0].id
,
331 IOSurfaceDrawQuad::UNFLIPPED
);
332 ValidateQuadResources(io_surface_quad
);
335 #if defined(VIDEO_HOLE)
336 // This block and other blocks wrapped around #if defined(VIDEO_HOLE) is not
337 // maintained by the general compositor team. Please contact the following
340 // wonsik@chromium.org
342 case VideoFrameExternalResources::HOLE
: {
343 DCHECK_EQ(frame_resources_
.size(), 0u);
344 SolidColorDrawQuad
* solid_color_draw_quad
=
345 render_pass
->CreateAndAppendDrawQuad
<SolidColorDrawQuad
>();
347 // Create a solid color quad with transparent black and force no
348 // blending / no anti-aliasing.
349 gfx::Rect opaque_rect
= quad_rect
;
350 solid_color_draw_quad
->SetAll(shared_quad_state
,
359 #endif // defined(VIDEO_HOLE)
360 case VideoFrameExternalResources::NONE
:
366 void VideoLayerImpl::DidDraw(ResourceProvider
* resource_provider
) {
367 LayerImpl::DidDraw(resource_provider
);
369 DCHECK(frame_
.get());
371 if (frame_resource_type_
==
372 VideoFrameExternalResources::SOFTWARE_RESOURCE
) {
373 for (size_t i
= 0; i
< software_resources_
.size(); ++i
) {
374 software_release_callback_
.Run(
375 0, false, layer_tree_impl()->BlockingMainThreadTaskRunner());
378 software_resources_
.clear();
379 software_release_callback_
.Reset();
381 for (size_t i
= 0; i
< frame_resources_
.size(); ++i
)
382 resource_provider
->DeleteResource(frame_resources_
[i
].id
);
383 frame_resources_
.clear();
386 provider_client_impl_
->PutCurrentFrame();
389 provider_client_impl_
->ReleaseLock();
392 SimpleEnclosedRegion
VideoLayerImpl::VisibleOpaqueRegion() const {
393 // If we don't have a frame yet, then we don't have an opaque region.
394 if (!provider_client_impl_
->HasCurrentFrame())
395 return SimpleEnclosedRegion();
396 return LayerImpl::VisibleOpaqueRegion();
399 void VideoLayerImpl::ReleaseResources() {
403 void VideoLayerImpl::SetNeedsRedraw() {
404 SetUpdateRect(gfx::UnionRects(update_rect(), gfx::Rect(bounds())));
405 layer_tree_impl()->SetNeedsRedraw();
408 const char* VideoLayerImpl::LayerTypeAsString() const {
409 return "cc::VideoLayerImpl";