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