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
25namespace cc {
26
27// static
28scoped_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
41VideoLayerImpl::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
47VideoLayerImpl::~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
60scoped_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
66void VideoLayerImpl::PushPropertiesTo(LayerImpl* layer) {
67  LayerImpl::PushPropertiesTo(layer);
68
69  VideoLayerImpl* other = static_cast<VideoLayerImpl*>(layer);
70  other->SetProviderClientImpl(provider_client_impl_);
71}
72
73void VideoLayerImpl::DidBecomeActive() {
74  provider_client_impl_->set_active_video_layer(this);
75}
76
77bool 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
133void 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
334void 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
360void VideoLayerImpl::ReleaseResources() {
361  updater_.reset();
362}
363
364void VideoLayerImpl::SetNeedsRedraw() {
365  SetUpdateRect(gfx::UnionRects(update_rect(), gfx::RectF(bounds())));
366  layer_tree_impl()->SetNeedsRedraw();
367}
368
369void VideoLayerImpl::SetProviderClientImpl(
370    scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl) {
371  provider_client_impl_ = provider_client_impl;
372}
373
374const char* VideoLayerImpl::LayerTypeAsString() const {
375  return "cc::VideoLayerImpl";
376}
377
378}  // namespace cc
379