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.
4
5 #include "cc/layers/video_layer_impl.h"
6
7 #include "base/bind.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_tracker.h"
18 #include "cc/trees/proxy.h"
19 #include "media/base/video_frame.h"
20
21 #if defined(VIDEO_HOLE)
22 #include "cc/quads/solid_color_draw_quad.h"
23 #endif // defined(VIDEO_HOLE)
24
25 namespace cc {
26
27 // static
Create(LayerTreeImpl * tree_impl,int id,VideoFrameProvider * provider,media::VideoRotation video_rotation)28 scoped_ptr<VideoLayerImpl> VideoLayerImpl::Create(
29 LayerTreeImpl* tree_impl,
30 int id,
31 VideoFrameProvider* provider,
32 media::VideoRotation video_rotation) {
33 scoped_ptr<VideoLayerImpl> layer(
34 new VideoLayerImpl(tree_impl, id, video_rotation));
35 layer->SetProviderClientImpl(VideoFrameProviderClientImpl::Create(provider));
36 DCHECK(tree_impl->proxy()->IsImplThread());
37 DCHECK(tree_impl->proxy()->IsMainThreadBlocked());
38 return layer.Pass();
39 }
40
VideoLayerImpl(LayerTreeImpl * tree_impl,int id,media::VideoRotation video_rotation)41 VideoLayerImpl::VideoLayerImpl(LayerTreeImpl* tree_impl,
42 int id,
43 media::VideoRotation video_rotation)
44 : LayerImpl(tree_impl, id), frame_(NULL), video_rotation_(video_rotation) {
45 }
46
~VideoLayerImpl()47 VideoLayerImpl::~VideoLayerImpl() {
48 if (!provider_client_impl_->Stopped()) {
49 // In impl side painting, we may have a pending and active layer
50 // associated with the video provider at the same time. Both have a ref
51 // on the VideoFrameProviderClientImpl, but we stop when the first
52 // LayerImpl (the one on the pending tree) is destroyed since we know
53 // the main thread is blocked for this commit.
54 DCHECK(layer_tree_impl()->proxy()->IsImplThread());
55 DCHECK(layer_tree_impl()->proxy()->IsMainThreadBlocked());
56 provider_client_impl_->Stop();
57 }
58 }
59
CreateLayerImpl(LayerTreeImpl * tree_impl)60 scoped_ptr<LayerImpl> VideoLayerImpl::CreateLayerImpl(
61 LayerTreeImpl* tree_impl) {
62 VideoLayerImpl* impl = new VideoLayerImpl(tree_impl, id(), video_rotation_);
63 return scoped_ptr<LayerImpl>(impl);
64 }
65
PushPropertiesTo(LayerImpl * layer)66 void VideoLayerImpl::PushPropertiesTo(LayerImpl* layer) {
67 LayerImpl::PushPropertiesTo(layer);
68
69 VideoLayerImpl* other = static_cast<VideoLayerImpl*>(layer);
70 other->SetProviderClientImpl(provider_client_impl_);
71 }
72
DidBecomeActive()73 void VideoLayerImpl::DidBecomeActive() {
74 provider_client_impl_->set_active_video_layer(this);
75 }
76
WillDraw(DrawMode draw_mode,ResourceProvider * resource_provider)77 bool VideoLayerImpl::WillDraw(DrawMode draw_mode,
78 ResourceProvider* resource_provider) {
79 if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE)
80 return false;
81
82 // Explicitly acquire and release the provider mutex so it can be held from
83 // WillDraw to DidDraw. Since the compositor thread is in the middle of
84 // drawing, the layer will not be destroyed before DidDraw is called.
85 // Therefore, the only thing that will prevent this lock from being released
86 // is the GPU process locking it. As the GPU process can't cause the
87 // destruction of the provider (calling StopUsingProvider), holding this
88 // lock should not cause a deadlock.
89 frame_ = provider_client_impl_->AcquireLockAndCurrentFrame();
90
91 if (!frame_.get()) {
92 // Drop any resources used by the updater if there is no frame to display.
93 updater_.reset();
94
95 provider_client_impl_->ReleaseLock();
96 return false;
97 }
98
99 if (!LayerImpl::WillDraw(draw_mode, resource_provider))
100 return false;
101
102 if (!updater_) {
103 updater_.reset(
104 new VideoResourceUpdater(layer_tree_impl()->context_provider(),
105 layer_tree_impl()->resource_provider()));
106 }
107
108 VideoFrameExternalResources external_resources =
109 updater_->CreateExternalResourcesFromVideoFrame(frame_);
110 frame_resource_type_ = external_resources.type;
111
112 if (external_resources.type ==
113 VideoFrameExternalResources::SOFTWARE_RESOURCE) {
114 software_resources_ = external_resources.software_resources;
115 software_release_callback_ =
116 external_resources.software_release_callback;
117 return true;
118 }
119
120 DCHECK_EQ(external_resources.mailboxes.size(),
121 external_resources.release_callbacks.size());
122 for (size_t i = 0; i < external_resources.mailboxes.size(); ++i) {
123 unsigned resource_id = resource_provider->CreateResourceFromTextureMailbox(
124 external_resources.mailboxes[i],
125 SingleReleaseCallbackImpl::Create(
126 external_resources.release_callbacks[i]));
127 frame_resources_.push_back(resource_id);
128 }
129
130 return true;
131 }
132
AppendQuads(RenderPass * render_pass,const OcclusionTracker<LayerImpl> & occlusion_tracker,AppendQuadsData * append_quads_data)133 void VideoLayerImpl::AppendQuads(
134 RenderPass* render_pass,
135 const OcclusionTracker<LayerImpl>& occlusion_tracker,
136 AppendQuadsData* append_quads_data) {
137 DCHECK(frame_.get());
138
139 gfx::Transform transform = draw_transform();
140 gfx::Size rotated_size = content_bounds();
141
142 switch (video_rotation_) {
143 case media::VIDEO_ROTATION_90:
144 rotated_size = gfx::Size(rotated_size.height(), rotated_size.width());
145 transform.Rotate(90.0);
146 transform.Translate(0.0, -rotated_size.height());
147 break;
148 case media::VIDEO_ROTATION_180:
149 transform.Rotate(180.0);
150 transform.Translate(-rotated_size.width(), -rotated_size.height());
151 break;
152 case media::VIDEO_ROTATION_270:
153 rotated_size = gfx::Size(rotated_size.height(), rotated_size.width());
154 transform.Rotate(270.0);
155 transform.Translate(-rotated_size.width(), 0);
156 case media::VIDEO_ROTATION_0:
157 break;
158 }
159
160 SharedQuadState* shared_quad_state =
161 render_pass->CreateAndAppendSharedQuadState();
162 shared_quad_state->SetAll(transform,
163 rotated_size,
164 visible_content_rect(),
165 clip_rect(),
166 is_clipped(),
167 draw_opacity(),
168 blend_mode(),
169 sorting_context_id());
170
171 AppendDebugBorderQuad(
172 render_pass, rotated_size, shared_quad_state, append_quads_data);
173
174 gfx::Rect quad_rect(rotated_size);
175 gfx::Rect opaque_rect(contents_opaque() ? quad_rect : gfx::Rect());
176 gfx::Rect visible_rect = frame_->visible_rect();
177 gfx::Size coded_size = frame_->coded_size();
178
179 gfx::Rect visible_quad_rect =
180 occlusion_tracker.GetCurrentOcclusionForLayer(transform)
181 .GetUnoccludedContentRect(quad_rect);
182 if (visible_quad_rect.IsEmpty())
183 return;
184
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();
190 const float tex_x_offset =
191 static_cast<float>(visible_rect.x()) / coded_size.width();
192 const float tex_y_offset =
193 static_cast<float>(visible_rect.y()) / coded_size.height();
194
195 switch (frame_resource_type_) {
196 // TODO(danakj): Remove this, hide it in the hardware path.
197 case VideoFrameExternalResources::SOFTWARE_RESOURCE: {
198 DCHECK_EQ(frame_resources_.size(), 0u);
199 DCHECK_EQ(software_resources_.size(), 1u);
200 if (software_resources_.size() < 1u)
201 break;
202 bool premultiplied_alpha = true;
203 gfx::PointF uv_top_left(0.f, 0.f);
204 gfx::PointF uv_bottom_right(tex_width_scale, tex_height_scale);
205 float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
206 bool flipped = false;
207 TextureDrawQuad* texture_quad =
208 render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
209 texture_quad->SetNew(shared_quad_state,
210 quad_rect,
211 opaque_rect,
212 visible_quad_rect,
213 software_resources_[0],
214 premultiplied_alpha,
215 uv_top_left,
216 uv_bottom_right,
217 SK_ColorTRANSPARENT,
218 opacity,
219 flipped);
220 break;
221 }
222 case VideoFrameExternalResources::YUV_RESOURCE: {
223 DCHECK_GE(frame_resources_.size(), 3u);
224 if (frame_resources_.size() < 3u)
225 break;
226 YUVVideoDrawQuad::ColorSpace color_space =
227 frame_->format() == media::VideoFrame::YV12J
228 ? YUVVideoDrawQuad::REC_601_JPEG
229 : YUVVideoDrawQuad::REC_601;
230 gfx::RectF tex_coord_rect(
231 tex_x_offset, tex_y_offset, tex_width_scale, tex_height_scale);
232 YUVVideoDrawQuad* yuv_video_quad =
233 render_pass->CreateAndAppendDrawQuad<YUVVideoDrawQuad>();
234 yuv_video_quad->SetNew(
235 shared_quad_state,
236 quad_rect,
237 opaque_rect,
238 visible_quad_rect,
239 tex_coord_rect,
240 frame_resources_[0],
241 frame_resources_[1],
242 frame_resources_[2],
243 frame_resources_.size() > 3 ? frame_resources_[3] : 0,
244 color_space);
245 break;
246 }
247 case VideoFrameExternalResources::RGB_RESOURCE: {
248 DCHECK_EQ(frame_resources_.size(), 1u);
249 if (frame_resources_.size() < 1u)
250 break;
251 bool premultiplied_alpha = true;
252 gfx::PointF uv_top_left(0.f, 0.f);
253 gfx::PointF uv_bottom_right(tex_width_scale, tex_height_scale);
254 float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
255 bool flipped = false;
256 TextureDrawQuad* texture_quad =
257 render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
258 texture_quad->SetNew(shared_quad_state,
259 quad_rect,
260 opaque_rect,
261 visible_quad_rect,
262 frame_resources_[0],
263 premultiplied_alpha,
264 uv_top_left,
265 uv_bottom_right,
266 SK_ColorTRANSPARENT,
267 opacity,
268 flipped);
269 break;
270 }
271 case VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE: {
272 DCHECK_EQ(frame_resources_.size(), 1u);
273 if (frame_resources_.size() < 1u)
274 break;
275 gfx::Transform scale;
276 scale.Scale(tex_width_scale, tex_height_scale);
277 StreamVideoDrawQuad* stream_video_quad =
278 render_pass->CreateAndAppendDrawQuad<StreamVideoDrawQuad>();
279 stream_video_quad->SetNew(
280 shared_quad_state,
281 quad_rect,
282 opaque_rect,
283 visible_quad_rect,
284 frame_resources_[0],
285 scale * provider_client_impl_->stream_texture_matrix());
286 break;
287 }
288 case VideoFrameExternalResources::IO_SURFACE: {
289 DCHECK_EQ(frame_resources_.size(), 1u);
290 if (frame_resources_.size() < 1u)
291 break;
292 IOSurfaceDrawQuad* io_surface_quad =
293 render_pass->CreateAndAppendDrawQuad<IOSurfaceDrawQuad>();
294 io_surface_quad->SetNew(shared_quad_state,
295 quad_rect,
296 opaque_rect,
297 visible_quad_rect,
298 visible_rect.size(),
299 frame_resources_[0],
300 IOSurfaceDrawQuad::UNFLIPPED);
301 break;
302 }
303 #if defined(VIDEO_HOLE)
304 // This block and other blocks wrapped around #if defined(GOOGLE_TV) is not
305 // maintained by the general compositor team. Please contact the following
306 // people instead:
307 //
308 // wonsik@chromium.org
309 // ycheo@chromium.org
310 case VideoFrameExternalResources::HOLE: {
311 DCHECK_EQ(frame_resources_.size(), 0u);
312 SolidColorDrawQuad* solid_color_draw_quad =
313 render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
314
315 // Create a solid color quad with transparent black and force no
316 // blending / no anti-aliasing.
317 gfx::Rect opaque_rect = quad_rect;
318 solid_color_draw_quad->SetAll(shared_quad_state,
319 quad_rect,
320 opaque_rect,
321 visible_quad_rect,
322 false,
323 SK_ColorTRANSPARENT,
324 true);
325 break;
326 }
327 #endif // defined(VIDEO_HOLE)
328 case VideoFrameExternalResources::NONE:
329 NOTIMPLEMENTED();
330 break;
331 }
332 }
333
DidDraw(ResourceProvider * resource_provider)334 void VideoLayerImpl::DidDraw(ResourceProvider* resource_provider) {
335 LayerImpl::DidDraw(resource_provider);
336
337 DCHECK(frame_.get());
338
339 if (frame_resource_type_ ==
340 VideoFrameExternalResources::SOFTWARE_RESOURCE) {
341 for (size_t i = 0; i < software_resources_.size(); ++i) {
342 software_release_callback_.Run(
343 0, false, layer_tree_impl()->BlockingMainThreadTaskRunner());
344 }
345
346 software_resources_.clear();
347 software_release_callback_.Reset();
348 } else {
349 for (size_t i = 0; i < frame_resources_.size(); ++i)
350 resource_provider->DeleteResource(frame_resources_[i]);
351 frame_resources_.clear();
352 }
353
354 provider_client_impl_->PutCurrentFrame(frame_);
355 frame_ = NULL;
356
357 provider_client_impl_->ReleaseLock();
358 }
359
ReleaseResources()360 void VideoLayerImpl::ReleaseResources() {
361 updater_.reset();
362 }
363
SetNeedsRedraw()364 void VideoLayerImpl::SetNeedsRedraw() {
365 SetUpdateRect(gfx::UnionRects(update_rect(), gfx::RectF(bounds())));
366 layer_tree_impl()->SetNeedsRedraw();
367 }
368
SetProviderClientImpl(scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl)369 void VideoLayerImpl::SetProviderClientImpl(
370 scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl) {
371 provider_client_impl_ = provider_client_impl;
372 }
373
LayerTypeAsString() const374 const char* VideoLayerImpl::LayerTypeAsString() const {
375 return "cc::VideoLayerImpl";
376 }
377
378 } // namespace cc
379