| OLD | NEW |
| 1 // Copyright 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "cc/layers/video_layer_impl.h" | 5 #include "cc/layers/video_layer_impl.h" |
| 6 | 6 |
| 7 #include "base/bind.h" |
| 7 #include "base/logging.h" | 8 #include "base/logging.h" |
| 8 #include "cc/base/math_util.h" | |
| 9 #include "cc/layers/quad_sink.h" | 9 #include "cc/layers/quad_sink.h" |
| 10 #include "cc/layers/video_frame_provider_client_impl.h" | 10 #include "cc/layers/video_frame_provider_client_impl.h" |
| 11 #include "cc/output/renderer.h" | |
| 12 #include "cc/quads/io_surface_draw_quad.h" | 11 #include "cc/quads/io_surface_draw_quad.h" |
| 13 #include "cc/quads/stream_video_draw_quad.h" | 12 #include "cc/quads/stream_video_draw_quad.h" |
| 14 #include "cc/quads/texture_draw_quad.h" | 13 #include "cc/quads/texture_draw_quad.h" |
| 15 #include "cc/quads/yuv_video_draw_quad.h" | 14 #include "cc/quads/yuv_video_draw_quad.h" |
| 16 #include "cc/resources/resource_provider.h" | 15 #include "cc/resources/resource_provider.h" |
| 17 #include "cc/trees/layer_tree_impl.h" | 16 #include "cc/trees/layer_tree_impl.h" |
| 18 #include "gpu/GLES2/gl2extchromium.h" | 17 #include "cc/trees/proxy.h" |
| 19 #include "media/filters/skcanvas_video_renderer.h" | 18 #include "media/base/video_frame.h" |
| 20 #include "third_party/khronos/GLES2/gl2.h" | |
| 21 #include "third_party/khronos/GLES2/gl2ext.h" | |
| 22 | 19 |
| 23 #if defined(GOOGLE_TV) | 20 #if defined(GOOGLE_TV) |
| 24 #include "cc/quads/solid_color_draw_quad.h" | 21 #include "cc/quads/solid_color_draw_quad.h" |
| 25 #endif | 22 #endif |
| 26 | 23 |
| 27 namespace cc { | 24 namespace cc { |
| 28 | 25 |
| 29 // static | 26 // static |
| 30 scoped_ptr<VideoLayerImpl> VideoLayerImpl::Create( | 27 scoped_ptr<VideoLayerImpl> VideoLayerImpl::Create( |
| 31 LayerTreeImpl* tree_impl, | 28 LayerTreeImpl* tree_impl, |
| 32 int id, | 29 int id, |
| 33 VideoFrameProvider* provider) { | 30 VideoFrameProvider* provider) { |
| 34 scoped_ptr<VideoLayerImpl> layer(new VideoLayerImpl(tree_impl, id)); | 31 scoped_ptr<VideoLayerImpl> layer(new VideoLayerImpl(tree_impl, id)); |
| 35 layer->SetProviderClientImpl(VideoFrameProviderClientImpl::Create(provider)); | 32 layer->SetProviderClientImpl(VideoFrameProviderClientImpl::Create(provider)); |
| 36 DCHECK(tree_impl->proxy()->IsImplThread()); | 33 DCHECK(tree_impl->proxy()->IsImplThread()); |
| 37 DCHECK(tree_impl->proxy()->IsMainThreadBlocked()); | 34 DCHECK(tree_impl->proxy()->IsMainThreadBlocked()); |
| 38 return layer.Pass(); | 35 return layer.Pass(); |
| 39 } | 36 } |
| 40 | 37 |
| 41 VideoLayerImpl::VideoLayerImpl(LayerTreeImpl* tree_impl, int id) | 38 VideoLayerImpl::VideoLayerImpl(LayerTreeImpl* tree_impl, int id) |
| 42 : LayerImpl(tree_impl, id), | 39 : LayerImpl(tree_impl, id), |
| 43 frame_(NULL), | 40 frame_(NULL) {} |
| 44 format_(media::VideoFrame::INVALID), | |
| 45 convert_yuv_(false), | |
| 46 external_texture_resource_(0) {} | |
| 47 | 41 |
| 48 VideoLayerImpl::~VideoLayerImpl() { | 42 VideoLayerImpl::~VideoLayerImpl() { |
| 49 if (!provider_client_impl_->Stopped()) { | 43 if (!provider_client_impl_->Stopped()) { |
| 50 // In impl side painting, we may have a pending and active layer | 44 // In impl side painting, we may have a pending and active layer |
| 51 // associated with the video provider at the same time. Both have a ref | 45 // associated with the video provider at the same time. Both have a ref |
| 52 // on the VideoFrameProviderClientImpl, but we stop when the first | 46 // on the VideoFrameProviderClientImpl, but we stop when the first |
| 53 // LayerImpl (the one on the pending tree) is destroyed since we know | 47 // LayerImpl (the one on the pending tree) is destroyed since we know |
| 54 // the main thread is blocked for this commit. | 48 // the main thread is blocked for this commit. |
| 55 DCHECK(layer_tree_impl()->proxy()->IsImplThread()); | 49 DCHECK(layer_tree_impl()->proxy()->IsImplThread()); |
| 56 DCHECK(layer_tree_impl()->proxy()->IsMainThreadBlocked()); | 50 DCHECK(layer_tree_impl()->proxy()->IsMainThreadBlocked()); |
| 57 provider_client_impl_->Stop(); | 51 provider_client_impl_->Stop(); |
| 58 } | 52 } |
| 59 FreeFramePlanes(layer_tree_impl()->resource_provider()); | |
| 60 | |
| 61 #ifndef NDEBUG | |
| 62 for (size_t i = 0; i < media::VideoFrame::kMaxPlanes; ++i) | |
| 63 DCHECK(!frame_planes_[i].resource_id); | |
| 64 DCHECK(!external_texture_resource_); | |
| 65 #endif | |
| 66 } | 53 } |
| 67 | 54 |
| 68 scoped_ptr<LayerImpl> VideoLayerImpl::CreateLayerImpl( | 55 scoped_ptr<LayerImpl> VideoLayerImpl::CreateLayerImpl( |
| 69 LayerTreeImpl* tree_impl) { | 56 LayerTreeImpl* tree_impl) { |
| 70 return scoped_ptr<LayerImpl>(new VideoLayerImpl(tree_impl, id())); | 57 return scoped_ptr<LayerImpl>(new VideoLayerImpl(tree_impl, id())); |
| 71 } | 58 } |
| 72 | 59 |
| 73 void VideoLayerImpl::PushPropertiesTo(LayerImpl* layer) { | 60 void VideoLayerImpl::PushPropertiesTo(LayerImpl* layer) { |
| 74 LayerImpl::PushPropertiesTo(layer); | 61 LayerImpl::PushPropertiesTo(layer); |
| 75 | 62 |
| 76 VideoLayerImpl* other = static_cast<VideoLayerImpl*>(layer); | 63 VideoLayerImpl* other = static_cast<VideoLayerImpl*>(layer); |
| 77 other->SetProviderClientImpl(provider_client_impl_); | 64 other->SetProviderClientImpl(provider_client_impl_); |
| 78 } | 65 } |
| 79 | 66 |
| 80 void VideoLayerImpl::DidBecomeActive() { | 67 void VideoLayerImpl::DidBecomeActive() { |
| 81 provider_client_impl_->set_active_video_layer(this); | 68 provider_client_impl_->set_active_video_layer(this); |
| 82 } | 69 } |
| 83 | 70 |
| 71 static void EmptyCallback(unsigned sync_point) {} |
| 72 |
| 84 void VideoLayerImpl::WillDraw(ResourceProvider* resource_provider) { | 73 void VideoLayerImpl::WillDraw(ResourceProvider* resource_provider) { |
| 85 LayerImpl::WillDraw(resource_provider); | 74 LayerImpl::WillDraw(resource_provider); |
| 86 | 75 |
| 87 | |
| 88 // Explicitly acquire and release the provider mutex so it can be held from | 76 // Explicitly acquire and release the provider mutex so it can be held from |
| 89 // WillDraw to DidDraw. Since the compositor thread is in the middle of | 77 // WillDraw to DidDraw. Since the compositor thread is in the middle of |
| 90 // drawing, the layer will not be destroyed before DidDraw is called. | 78 // drawing, the layer will not be destroyed before DidDraw is called. |
| 91 // Therefore, the only thing that will prevent this lock from being released | 79 // Therefore, the only thing that will prevent this lock from being released |
| 92 // is the GPU process locking it. As the GPU process can't cause the | 80 // is the GPU process locking it. As the GPU process can't cause the |
| 93 // destruction of the provider (calling StopUsingProvider), holding this | 81 // destruction of the provider (calling StopUsingProvider), holding this |
| 94 // lock should not cause a deadlock. | 82 // lock should not cause a deadlock. |
| 95 frame_ = provider_client_impl_->AcquireLockAndCurrentFrame(); | 83 frame_ = provider_client_impl_->AcquireLockAndCurrentFrame(); |
| 96 | 84 |
| 97 WillDrawInternal(resource_provider); | 85 if (!frame_) { |
| 98 FreeUnusedFramePlanes(resource_provider); | |
| 99 | |
| 100 if (!frame_) | |
| 101 provider_client_impl_->ReleaseLock(); | 86 provider_client_impl_->ReleaseLock(); |
| 102 } | |
| 103 | |
| 104 void VideoLayerImpl::WillDrawInternal(ResourceProvider* resource_provider) { | |
| 105 DCHECK(!external_texture_resource_); | |
| 106 | |
| 107 if (!frame_) | |
| 108 return; | |
| 109 | |
| 110 format_ = frame_->format(); | |
| 111 | |
| 112 #if defined(GOOGLE_TV) | |
| 113 if (format_ == media::VideoFrame::HOLE) | |
| 114 return; | |
| 115 #endif | |
| 116 | |
| 117 // If these fail, we'll have to add draw logic that handles offset bitmap/ | |
| 118 // texture UVs. For now, just expect (0, 0) offset, since all our decoders | |
| 119 // so far don't offset. | |
| 120 DCHECK_EQ(frame_->visible_rect().x(), 0); | |
| 121 DCHECK_EQ(frame_->visible_rect().y(), 0); | |
| 122 | |
| 123 if (format_ == media::VideoFrame::INVALID) { | |
| 124 provider_client_impl_->PutCurrentFrame(frame_); | |
| 125 frame_ = NULL; | |
| 126 return; | 87 return; |
| 127 } | 88 } |
| 128 | 89 |
| 129 // TODO(skaslev): If we're in software compositing mode, we do the YUV -> RGB | 90 if (!updater_) |
| 130 // conversion here. That involves an extra copy of each frame to a bitmap. | 91 updater_.reset(new VideoResourceUpdater(resource_provider)); |
| 131 // Obviously, this is suboptimal and should be addressed once ubercompositor | |
| 132 // starts shaping up. | |
| 133 convert_yuv_ = | |
| 134 resource_provider->default_resource_type() == ResourceProvider::Bitmap && | |
| 135 (format_ == media::VideoFrame::YV12 || | |
| 136 format_ == media::VideoFrame::YV16); | |
| 137 | 92 |
| 138 if (convert_yuv_) | 93 VideoFrameExternalResources external_resources; |
| 139 format_ = media::VideoFrame::RGB32; | 94 if (frame_->format() == media::VideoFrame::NATIVE_TEXTURE) { |
| 95 // TODO(danakj): To make this work for ubercomp, push this code out to |
| 96 // WebMediaPlayer and have it set a callback so it knows it can reuse the |
| 97 // texture. |
| 98 TextureMailbox::ReleaseCallback empty_callback = base::Bind(&EmptyCallback); |
| 99 external_resources = updater_->CreateForHardwarePlanes( |
| 100 frame_, empty_callback); |
| 101 } else { |
| 102 external_resources = updater_->CreateForSoftwarePlanes(frame_); |
| 103 } |
| 140 | 104 |
| 141 if (!SetupFramePlanes(resource_provider)) { | 105 frame_resource_type_ = external_resources.type; |
| 142 provider_client_impl_->PutCurrentFrame(frame_); | 106 |
| 143 frame_ = NULL; | 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; |
| 144 return; | 112 return; |
| 145 } | 113 } |
| 146 | 114 |
| 147 if (format_ == media::VideoFrame::NATIVE_TEXTURE && | 115 for (size_t i = 0; i < external_resources.mailboxes.size(); ++i) { |
| 148 frame_->texture_target() == GL_TEXTURE_2D) { | 116 frame_resources_.push_back( |
| 149 external_texture_resource_ = | 117 resource_provider->CreateResourceFromTextureMailbox( |
| 150 resource_provider->CreateResourceFromExternalTexture( | 118 external_resources.mailboxes[i])); |
| 151 frame_->texture_id()); | |
| 152 } | 119 } |
| 153 } | 120 } |
| 154 | 121 |
| 155 void VideoLayerImpl::AppendQuads(QuadSink* quad_sink, | 122 void VideoLayerImpl::AppendQuads(QuadSink* quad_sink, |
| 156 AppendQuadsData* append_quads_data) { | 123 AppendQuadsData* append_quads_data) { |
| 157 if (!frame_) | 124 if (!frame_) |
| 158 return; | 125 return; |
| 159 | 126 |
| 160 SharedQuadState* shared_quad_state = | 127 SharedQuadState* shared_quad_state = |
| 161 quad_sink->UseSharedQuadState(CreateSharedQuadState()); | 128 quad_sink->UseSharedQuadState(CreateSharedQuadState()); |
| 162 AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data); | 129 AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data); |
| 163 | 130 |
| 164 // TODO(danakj): When we pass quads out of process, we need to double-buffer, | |
| 165 // or otherwise synchonize use of all textures in the quad. | |
| 166 | |
| 167 gfx::Rect quad_rect(content_bounds()); | 131 gfx::Rect quad_rect(content_bounds()); |
| 168 gfx::Rect opaque_rect(contents_opaque() ? quad_rect : gfx::Rect()); | 132 gfx::Rect opaque_rect(contents_opaque() ? quad_rect : gfx::Rect()); |
| 169 gfx::Rect visible_rect = frame_->visible_rect(); | 133 gfx::Rect visible_rect = frame_->visible_rect(); |
| 170 gfx::Size coded_size = frame_->coded_size(); | 134 gfx::Size coded_size = frame_->coded_size(); |
| 171 | 135 |
| 172 // pixels for macroblocked formats. | 136 // Pixels for macroblocked formats. |
| 173 float tex_width_scale = | 137 float tex_width_scale = |
| 174 static_cast<float>(visible_rect.width()) / coded_size.width(); | 138 static_cast<float>(visible_rect.width()) / coded_size.width(); |
| 175 float tex_height_scale = | 139 float tex_height_scale = |
| 176 static_cast<float>(visible_rect.height()) / coded_size.height(); | 140 static_cast<float>(visible_rect.height()) / coded_size.height(); |
| 177 | 141 |
| 178 #if defined(GOOGLE_TV) | 142 switch (frame_resource_type_) { |
| 179 // This block and other blocks wrapped around #if defined(GOOGLE_TV) is not | 143 // TODO(danakj): Remove this, hide it in the hardware path. |
| 180 // maintained by the general compositor team. Please contact the following | 144 case VideoFrameExternalResources::SOFTWARE_RESOURCE: { |
| 181 // people instead: | 145 DCHECK_EQ(frame_resources_.size(), 0u); |
| 182 // | 146 DCHECK_EQ(software_resources_.size(), 1u); |
| 183 // wonsik@chromium.org | 147 if (software_resources_.size() < 1u) |
| 184 // ycheo@chromium.org | 148 break; |
| 185 | 149 bool premultiplied_alpha = true; |
| 186 if (frame_->format() == media::VideoFrame::HOLE) { | 150 gfx::PointF uv_top_left(0.f, 0.f); |
| 187 scoped_ptr<SolidColorDrawQuad> solid_color_draw_quad = | 151 gfx::PointF uv_bottom_right(tex_width_scale, tex_height_scale); |
| 188 SolidColorDrawQuad::Create(); | 152 float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; |
| 189 // Create a solid color quad with transparent black and force no | 153 bool flipped = false; |
| 190 // blending. | 154 scoped_ptr<TextureDrawQuad> texture_quad = TextureDrawQuad::Create(); |
| 191 solid_color_draw_quad->SetAll( | 155 texture_quad->SetNew(shared_quad_state, |
| 192 shared_quad_state, quad_rect, quad_rect, quad_rect, false, | 156 quad_rect, |
| 193 SK_ColorTRANSPARENT); | 157 opaque_rect, |
| 194 quad_sink->Append(solid_color_draw_quad.PassAs<DrawQuad>(), | 158 software_resources_[0], |
| 195 append_quads_data); | 159 premultiplied_alpha, |
| 196 return; | 160 uv_top_left, |
| 197 } | 161 uv_bottom_right, |
| 198 #endif | 162 opacity, |
| 199 | 163 flipped); |
| 200 switch (format_) { | 164 quad_sink->Append(texture_quad.PassAs<DrawQuad>(), append_quads_data); |
| 201 case media::VideoFrame::YV12: | 165 break; |
| 202 case media::VideoFrame::YV16: { | 166 } |
| 203 // YUV software decoder. | 167 case VideoFrameExternalResources::YUV_RESOURCE: { |
| 204 const FramePlane& y_plane = frame_planes_[media::VideoFrame::kYPlane]; | 168 DCHECK_EQ(frame_resources_.size(), 3u); |
| 205 const FramePlane& u_plane = frame_planes_[media::VideoFrame::kUPlane]; | 169 if (frame_resources_.size() < 3u) |
| 206 const FramePlane& v_plane = frame_planes_[media::VideoFrame::kVPlane]; | 170 break; |
| 207 gfx::SizeF tex_scale(tex_width_scale, tex_height_scale); | 171 gfx::SizeF tex_scale(tex_width_scale, tex_height_scale); |
| 208 scoped_ptr<YUVVideoDrawQuad> yuv_video_quad = YUVVideoDrawQuad::Create(); | 172 scoped_ptr<YUVVideoDrawQuad> yuv_video_quad = YUVVideoDrawQuad::Create(); |
| 209 yuv_video_quad->SetNew(shared_quad_state, | 173 yuv_video_quad->SetNew(shared_quad_state, |
| 210 quad_rect, | 174 quad_rect, |
| 211 opaque_rect, | 175 opaque_rect, |
| 212 tex_scale, | 176 tex_scale, |
| 213 y_plane, | 177 frame_resources_[0], |
| 214 u_plane, | 178 frame_resources_[1], |
| 215 v_plane); | 179 frame_resources_[2]); |
| 216 quad_sink->Append(yuv_video_quad.PassAs<DrawQuad>(), append_quads_data); | 180 quad_sink->Append(yuv_video_quad.PassAs<DrawQuad>(), append_quads_data); |
| 217 break; | 181 break; |
| 218 } | 182 } |
| 219 case media::VideoFrame::RGB32: { | 183 case VideoFrameExternalResources::RGB_RESOURCE: { |
| 220 // RGBA software decoder: a converted YUV frame (see: convert_yuv_). | 184 DCHECK_EQ(frame_resources_.size(), 1u); |
| 221 const FramePlane& plane = frame_planes_[media::VideoFrame::kRGBPlane]; | 185 if (frame_resources_.size() < 1u) |
| 186 break; |
| 222 bool premultiplied_alpha = true; | 187 bool premultiplied_alpha = true; |
| 223 gfx::PointF uv_top_left(0.f, 0.f); | 188 gfx::PointF uv_top_left(0.f, 0.f); |
| 224 gfx::PointF uv_bottom_right(tex_width_scale, tex_height_scale); | 189 gfx::PointF uv_bottom_right(tex_width_scale, tex_height_scale); |
| 225 float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; | 190 float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; |
| 226 bool flipped = false; | 191 bool flipped = false; |
| 227 scoped_ptr<TextureDrawQuad> texture_quad = TextureDrawQuad::Create(); | 192 scoped_ptr<TextureDrawQuad> texture_quad = TextureDrawQuad::Create(); |
| 228 texture_quad->SetNew(shared_quad_state, | 193 texture_quad->SetNew(shared_quad_state, |
| 229 quad_rect, | 194 quad_rect, |
| 230 opaque_rect, | 195 opaque_rect, |
| 231 plane.resource_id, | 196 frame_resources_[0], |
| 232 premultiplied_alpha, | 197 premultiplied_alpha, |
| 233 uv_top_left, | 198 uv_top_left, |
| 234 uv_bottom_right, | 199 uv_bottom_right, |
| 235 opacity, | 200 opacity, |
| 236 flipped); | 201 flipped); |
| 237 quad_sink->Append(texture_quad.PassAs<DrawQuad>(), append_quads_data); | 202 quad_sink->Append(texture_quad.PassAs<DrawQuad>(), append_quads_data); |
| 238 break; | 203 break; |
| 239 } | 204 } |
| 240 case media::VideoFrame::NATIVE_TEXTURE: | 205 case VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE: { |
| 241 switch (frame_->texture_target()) { | 206 DCHECK_EQ(frame_resources_.size(), 1u); |
| 242 case GL_TEXTURE_2D: { | 207 if (frame_resources_.size() < 1u) |
| 243 // NativeTexture hardware decoder. | 208 break; |
| 244 bool premultiplied_alpha = true; | 209 gfx::Transform transform( |
| 245 gfx::PointF uv_top_left(0.f, 0.f); | 210 provider_client_impl_->stream_texture_matrix()); |
| 246 gfx::PointF uv_bottom_right(tex_width_scale, tex_height_scale); | 211 transform.Scale(tex_width_scale, tex_height_scale); |
| 247 float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; | 212 scoped_ptr<StreamVideoDrawQuad> stream_video_quad = |
| 248 bool flipped = false; | 213 StreamVideoDrawQuad::Create(); |
| 249 scoped_ptr<TextureDrawQuad> texture_quad = TextureDrawQuad::Create(); | 214 stream_video_quad->SetNew(shared_quad_state, |
| 250 texture_quad->SetNew(shared_quad_state, | 215 quad_rect, |
| 251 quad_rect, | 216 opaque_rect, |
| 252 opaque_rect, | 217 frame_resources_[0], |
| 253 external_texture_resource_, | 218 transform); |
| 254 premultiplied_alpha, | 219 quad_sink->Append(stream_video_quad.PassAs<DrawQuad>(), |
| 255 uv_top_left, | 220 append_quads_data); |
| 256 uv_bottom_right, | |
| 257 opacity, | |
| 258 flipped); | |
| 259 quad_sink->Append(texture_quad.PassAs<DrawQuad>(), append_quads_data); | |
| 260 break; | |
| 261 } | |
| 262 case GL_TEXTURE_RECTANGLE_ARB: { | |
| 263 gfx::Size visible_size(visible_rect.width(), visible_rect.height()); | |
| 264 scoped_ptr<IOSurfaceDrawQuad> io_surface_quad = | |
| 265 IOSurfaceDrawQuad::Create(); | |
| 266 io_surface_quad->SetNew(shared_quad_state, | |
| 267 quad_rect, | |
| 268 opaque_rect, | |
| 269 visible_size, | |
| 270 frame_->texture_id(), | |
| 271 IOSurfaceDrawQuad::UNFLIPPED); | |
| 272 quad_sink->Append(io_surface_quad.PassAs<DrawQuad>(), | |
| 273 append_quads_data); | |
| 274 break; | |
| 275 } | |
| 276 case GL_TEXTURE_EXTERNAL_OES: { | |
| 277 // StreamTexture hardware decoder. | |
| 278 gfx::Transform transform( | |
| 279 provider_client_impl_->stream_texture_matrix()); | |
| 280 transform.Scale(tex_width_scale, tex_height_scale); | |
| 281 scoped_ptr<StreamVideoDrawQuad> stream_video_quad = | |
| 282 StreamVideoDrawQuad::Create(); | |
| 283 stream_video_quad->SetNew(shared_quad_state, | |
| 284 quad_rect, | |
| 285 opaque_rect, | |
| 286 frame_->texture_id(), | |
| 287 transform); | |
| 288 quad_sink->Append(stream_video_quad.PassAs<DrawQuad>(), | |
| 289 append_quads_data); | |
| 290 break; | |
| 291 } | |
| 292 default: | |
| 293 NOTREACHED(); | |
| 294 break; | |
| 295 } | |
| 296 break; | 221 break; |
| 297 case media::VideoFrame::INVALID: | 222 } |
| 298 case media::VideoFrame::EMPTY: | 223 case VideoFrameExternalResources::IO_SURFACE: { |
| 299 case media::VideoFrame::I420: | 224 DCHECK_EQ(frame_resources_.size(), 1u); |
| 225 if (frame_resources_.size() < 1u) |
| 226 break; |
| 227 gfx::Size visible_size(visible_rect.width(), visible_rect.height()); |
| 228 scoped_ptr<IOSurfaceDrawQuad> io_surface_quad = |
| 229 IOSurfaceDrawQuad::Create(); |
| 230 io_surface_quad->SetNew(shared_quad_state, |
| 231 quad_rect, |
| 232 opaque_rect, |
| 233 visible_size, |
| 234 frame_resources_[0], |
| 235 IOSurfaceDrawQuad::UNFLIPPED); |
| 236 quad_sink->Append(io_surface_quad.PassAs<DrawQuad>(), |
| 237 append_quads_data); |
| 238 break; |
| 239 } |
| 300 #if defined(GOOGLE_TV) | 240 #if defined(GOOGLE_TV) |
| 301 case media::VideoFrame::HOLE: | 241 // This block and other blocks wrapped around #if defined(GOOGLE_TV) is not |
| 242 // maintained by the general compositor team. Please contact the following |
| 243 // people instead: |
| 244 // |
| 245 // wonsik@chromium.org |
| 246 // ycheo@chromium.org |
| 247 case VideoFrameExternalResources::HOLE: { |
| 248 DCHECK_EQ(frame_resources_.size(), 0u); |
| 249 scoped_ptr<SolidColorDrawQuad> solid_color_draw_quad = |
| 250 SolidColorDrawQuad::Create(); |
| 251 // Create a solid color quad with transparent black and force no |
| 252 // blending. |
| 253 solid_color_draw_quad->SetAll( |
| 254 shared_quad_state, quad_rect, quad_rect, quad_rect, false, |
| 255 SK_ColorTRANSPARENT); |
| 256 quad_sink->Append(solid_color_draw_quad.PassAs<DrawQuad>(), |
| 257 append_quads_data); |
| 258 break; |
| 259 } |
| 302 #endif | 260 #endif |
| 303 NOTREACHED(); | 261 case VideoFrameExternalResources::NONE: |
| 262 NOTIMPLEMENTED(); |
| 304 break; | 263 break; |
| 305 } | 264 } |
| 306 } | 265 } |
| 307 | 266 |
| 308 void VideoLayerImpl::DidDraw(ResourceProvider* resource_provider) { | 267 void VideoLayerImpl::DidDraw(ResourceProvider* resource_provider) { |
| 309 LayerImpl::DidDraw(resource_provider); | 268 LayerImpl::DidDraw(resource_provider); |
| 310 | 269 |
| 311 if (!frame_) | 270 if (!frame_) |
| 312 return; | 271 return; |
| 313 | 272 |
| 314 if (format_ == media::VideoFrame::NATIVE_TEXTURE && | 273 if (frame_resource_type_ == |
| 315 frame_->texture_target() == GL_TEXTURE_2D) { | 274 VideoFrameExternalResources::SOFTWARE_RESOURCE) { |
| 316 DCHECK(external_texture_resource_); | 275 for (size_t i = 0; i < software_resources_.size(); ++i) |
| 317 // TODO(danakj): the following assert will not be true when sending | 276 software_release_callback_.Run(0); |
| 318 // resources to a parent compositor. We will probably need to hold on to | 277 |
| 319 // frame_ for longer, and have several "current frames" in the pipeline. | 278 software_resources_.clear(); |
| 320 DCHECK(!resource_provider->InUseByConsumer(external_texture_resource_)); | 279 software_release_callback_.Reset(); |
| 321 resource_provider->DeleteResource(external_texture_resource_); | 280 } else { |
| 322 external_texture_resource_ = 0; | 281 for (size_t i = 0; i < frame_resources_.size(); ++i) |
| 282 resource_provider->DeleteResource(frame_resources_[i]); |
| 283 frame_resources_.clear(); |
| 323 } | 284 } |
| 324 | 285 |
| 325 provider_client_impl_->PutCurrentFrame(frame_); | 286 provider_client_impl_->PutCurrentFrame(frame_); |
| 326 frame_ = NULL; | 287 frame_ = NULL; |
| 327 | 288 |
| 328 provider_client_impl_->ReleaseLock(); | 289 provider_client_impl_->ReleaseLock(); |
| 329 } | 290 } |
| 330 | 291 |
| 331 static gfx::Size VideoFrameDimension(media::VideoFrame* frame, int plane) { | |
| 332 gfx::Size dimensions = frame->coded_size(); | |
| 333 switch (frame->format()) { | |
| 334 case media::VideoFrame::YV12: | |
| 335 if (plane != media::VideoFrame::kYPlane) { | |
| 336 dimensions.set_width(dimensions.width() / 2); | |
| 337 dimensions.set_height(dimensions.height() / 2); | |
| 338 } | |
| 339 break; | |
| 340 case media::VideoFrame::YV16: | |
| 341 if (plane != media::VideoFrame::kYPlane) | |
| 342 dimensions.set_width(dimensions.width() / 2); | |
| 343 break; | |
| 344 default: | |
| 345 break; | |
| 346 } | |
| 347 return dimensions; | |
| 348 } | |
| 349 | |
| 350 bool VideoLayerImpl::FramePlane::AllocateData( | |
| 351 ResourceProvider* resource_provider) { | |
| 352 if (resource_id) | |
| 353 return true; | |
| 354 | |
| 355 resource_id = resource_provider->CreateResource( | |
| 356 size, format, ResourceProvider::TextureUsageAny); | |
| 357 return resource_id != 0; | |
| 358 } | |
| 359 | |
| 360 void VideoLayerImpl::FramePlane::FreeData(ResourceProvider* resource_provider) { | |
| 361 if (!resource_id) | |
| 362 return; | |
| 363 | |
| 364 resource_provider->DeleteResource(resource_id); | |
| 365 resource_id = 0; | |
| 366 } | |
| 367 | |
| 368 // Convert media::VideoFrame::Format to OpenGL enum values. | |
| 369 static GLenum ConvertVFCFormatToGLenum(const media::VideoFrame::Format format) { | |
| 370 switch (format) { | |
| 371 case media::VideoFrame::YV12: | |
| 372 case media::VideoFrame::YV16: | |
| 373 return GL_LUMINANCE; | |
| 374 case media::VideoFrame::RGB32: | |
| 375 return GL_RGBA; | |
| 376 case media::VideoFrame::NATIVE_TEXTURE: | |
| 377 #if defined(GOOGLE_TV) | |
| 378 case media::VideoFrame::HOLE: | |
| 379 #endif | |
| 380 case media::VideoFrame::INVALID: | |
| 381 case media::VideoFrame::EMPTY: | |
| 382 case media::VideoFrame::I420: | |
| 383 NOTREACHED(); | |
| 384 break; | |
| 385 } | |
| 386 return GL_INVALID_VALUE; | |
| 387 } | |
| 388 | |
| 389 bool VideoLayerImpl::SetupFramePlanes(ResourceProvider* resource_provider) { | |
| 390 const size_t plane_count = media::VideoFrame::NumPlanes(format_); | |
| 391 if (!plane_count) | |
| 392 return true; | |
| 393 | |
| 394 const int max_texture_size = resource_provider->max_texture_size(); | |
| 395 const GLenum pixel_format = ConvertVFCFormatToGLenum(format_); | |
| 396 for (size_t plane_index = 0; plane_index < plane_count; ++plane_index) { | |
| 397 VideoLayerImpl::FramePlane* plane = &frame_planes_[plane_index]; | |
| 398 | |
| 399 gfx::Size required_texture_size = VideoFrameDimension(frame_, plane_index); | |
| 400 // TODO(danakj): Remove the test against max_texture_size when tiled layers | |
| 401 // are implemented. | |
| 402 if (required_texture_size.IsEmpty() || | |
| 403 required_texture_size.width() > max_texture_size || | |
| 404 required_texture_size.height() > max_texture_size) | |
| 405 return false; | |
| 406 | |
| 407 if (plane->size != required_texture_size || plane->format != pixel_format) { | |
| 408 plane->FreeData(resource_provider); | |
| 409 plane->size = required_texture_size; | |
| 410 plane->format = pixel_format; | |
| 411 } | |
| 412 | |
| 413 if (!plane->AllocateData(resource_provider)) | |
| 414 return false; | |
| 415 } | |
| 416 | |
| 417 if (convert_yuv_) { | |
| 418 if (!video_renderer_) | |
| 419 video_renderer_.reset(new media::SkCanvasVideoRenderer); | |
| 420 const VideoLayerImpl::FramePlane& plane = | |
| 421 frame_planes_[media::VideoFrame::kRGBPlane]; | |
| 422 ResourceProvider::ScopedWriteLockSoftware lock(resource_provider, | |
| 423 plane.resource_id); | |
| 424 video_renderer_->Paint(frame_, | |
| 425 lock.sk_canvas(), | |
| 426 frame_->visible_rect(), | |
| 427 0xff); | |
| 428 return true; | |
| 429 } | |
| 430 | |
| 431 for (size_t plane_index = 0; plane_index < plane_count; ++plane_index) { | |
| 432 const VideoLayerImpl::FramePlane& plane = frame_planes_[plane_index]; | |
| 433 // Only planar formats planes should need upload. | |
| 434 DCHECK_EQ(plane.format, static_cast<unsigned>(GL_LUMINANCE)); | |
| 435 const uint8_t* software_plane_pixels = frame_->data(plane_index); | |
| 436 gfx::Rect image_rect(0, | |
| 437 0, | |
| 438 frame_->stride(plane_index), | |
| 439 plane.size.height()); | |
| 440 gfx::Rect source_rect(plane.size); | |
| 441 resource_provider->SetPixels(plane.resource_id, | |
| 442 software_plane_pixels, | |
| 443 image_rect, | |
| 444 source_rect, | |
| 445 gfx::Vector2d()); | |
| 446 } | |
| 447 return true; | |
| 448 } | |
| 449 | |
| 450 void VideoLayerImpl::FreeFramePlanes(ResourceProvider* resource_provider) { | |
| 451 for (size_t i = 0; i < media::VideoFrame::kMaxPlanes; ++i) | |
| 452 frame_planes_[i].FreeData(resource_provider); | |
| 453 } | |
| 454 | |
| 455 void VideoLayerImpl::FreeUnusedFramePlanes( | |
| 456 ResourceProvider* resource_provider) { | |
| 457 size_t first_unused_plane = (frame_ ? media::VideoFrame::NumPlanes(format_) | |
| 458 : 0); | |
| 459 for (size_t i = first_unused_plane; i < media::VideoFrame::kMaxPlanes; ++i) | |
| 460 frame_planes_[i].FreeData(resource_provider); | |
| 461 } | |
| 462 | |
| 463 void VideoLayerImpl::DidLoseOutputSurface() { | 292 void VideoLayerImpl::DidLoseOutputSurface() { |
| 464 FreeFramePlanes(layer_tree_impl()->resource_provider()); | 293 updater_.reset(); |
| 465 } | 294 } |
| 466 | 295 |
| 467 void VideoLayerImpl::SetNeedsRedraw() { | 296 void VideoLayerImpl::SetNeedsRedraw() { |
| 468 set_update_rect(gfx::UnionRects(update_rect(), gfx::RectF(bounds()))); | 297 set_update_rect(gfx::UnionRects(update_rect(), gfx::RectF(bounds()))); |
| 469 layer_tree_impl()->SetNeedsRedraw(); | 298 layer_tree_impl()->SetNeedsRedraw(); |
| 470 } | 299 } |
| 471 | 300 |
| 472 void VideoLayerImpl::SetProviderClientImpl( | 301 void VideoLayerImpl::SetProviderClientImpl( |
| 473 scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl) { | 302 scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl) { |
| 474 provider_client_impl_ = provider_client_impl; | 303 provider_client_impl_ = provider_client_impl; |
| 475 } | 304 } |
| 476 | 305 |
| 477 const char* VideoLayerImpl::LayerTypeAsString() const { | 306 const char* VideoLayerImpl::LayerTypeAsString() const { |
| 478 return "VideoLayer"; | 307 return "VideoLayer"; |
| 479 } | 308 } |
| 480 | 309 |
| 481 } // namespace cc | 310 } // namespace cc |
| OLD | NEW |