OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 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/resources/video_resource_updater.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "cc/output/gl_renderer.h" |
| 9 #include "cc/resources/resource_provider.h" |
| 10 #include "gpu/GLES2/gl2extchromium.h" |
| 11 #include "media/base/video_frame.h" |
| 12 #include "media/filters/skcanvas_video_renderer.h" |
| 13 #include "third_party/khronos/GLES2/gl2.h" |
| 14 #include "third_party/khronos/GLES2/gl2ext.h" |
| 15 #include "ui/gfx/size_conversions.h" |
| 16 |
| 17 const unsigned kYUVResourceFormat = GL_LUMINANCE; |
| 18 const unsigned kRGBResourceFormat = GL_RGBA; |
| 19 |
| 20 namespace cc { |
| 21 |
| 22 VideoFrameExternalResources::VideoFrameExternalResources() : type(NONE) {} |
| 23 |
| 24 VideoFrameExternalResources::~VideoFrameExternalResources() {} |
| 25 |
| 26 VideoResourceUpdater::VideoResourceUpdater(ResourceProvider* resource_provider) |
| 27 : resource_provider_(resource_provider) { |
| 28 } |
| 29 |
| 30 VideoResourceUpdater::~VideoResourceUpdater() {} |
| 31 |
| 32 bool VideoResourceUpdater::VerifyFrame( |
| 33 const scoped_refptr<media::VideoFrame>& video_frame) { |
| 34 // If these fail, we'll have to add logic that handles offset bitmap/texture |
| 35 // UVs. For now, just expect (0, 0) offset, since all our decoders so far |
| 36 // don't offset. |
| 37 DCHECK_EQ(video_frame->visible_rect().x(), 0); |
| 38 DCHECK_EQ(video_frame->visible_rect().y(), 0); |
| 39 |
| 40 switch (video_frame->format()) { |
| 41 // Acceptable inputs. |
| 42 case media::VideoFrame::YV12: |
| 43 case media::VideoFrame::YV16: |
| 44 case media::VideoFrame::NATIVE_TEXTURE: |
| 45 #if defined(GOOGLE_TV) |
| 46 case media::VideoFrame::HOLE: |
| 47 #endif |
| 48 return true; |
| 49 |
| 50 // Unacceptable inputs. ¯\(°_o)/¯ |
| 51 case media::VideoFrame::INVALID: |
| 52 case media::VideoFrame::RGB32: |
| 53 case media::VideoFrame::EMPTY: |
| 54 case media::VideoFrame::I420: |
| 55 break; |
| 56 } |
| 57 return false; |
| 58 } |
| 59 |
| 60 // For frames that we receive in software format, determine the dimensions of |
| 61 // each plane in the frame. |
| 62 static gfx::Size SoftwarePlaneDimension( |
| 63 media::VideoFrame::Format input_frame_format, |
| 64 gfx::Size coded_size, |
| 65 GLenum output_resource_format, |
| 66 int plane_index) { |
| 67 if (output_resource_format == kYUVResourceFormat) { |
| 68 if (plane_index == media::VideoFrame::kYPlane) |
| 69 return coded_size; |
| 70 |
| 71 switch (input_frame_format) { |
| 72 case media::VideoFrame::YV12: |
| 73 return gfx::ToFlooredSize(gfx::ScaleSize(coded_size, 0.5f, 0.5f)); |
| 74 case media::VideoFrame::YV16: |
| 75 return gfx::ToFlooredSize(gfx::ScaleSize(coded_size, 0.5f, 1.f)); |
| 76 |
| 77 case media::VideoFrame::INVALID: |
| 78 case media::VideoFrame::RGB32: |
| 79 case media::VideoFrame::EMPTY: |
| 80 case media::VideoFrame::I420: |
| 81 case media::VideoFrame::NATIVE_TEXTURE: |
| 82 #if defined(GOOGLE_TV) |
| 83 case media::VideoFrame::HOLE: |
| 84 #endif |
| 85 NOTREACHED(); |
| 86 } |
| 87 } |
| 88 |
| 89 DCHECK_EQ(output_resource_format, static_cast<unsigned>(kRGBResourceFormat)); |
| 90 return coded_size; |
| 91 } |
| 92 |
| 93 static void ReleaseResource(ResourceProvider* resource_provider, |
| 94 ResourceProvider::ResourceId resource_id, |
| 95 unsigned sync_point) { |
| 96 resource_provider->DeleteResource(resource_id); |
| 97 } |
| 98 |
| 99 VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( |
| 100 const scoped_refptr<media::VideoFrame>& video_frame) { |
| 101 if (!VerifyFrame(video_frame)) |
| 102 return VideoFrameExternalResources(); |
| 103 |
| 104 media::VideoFrame::Format input_frame_format = video_frame->format(); |
| 105 |
| 106 #if defined(GOOGLE_TV) |
| 107 if (input_frame_format == media::VideoFrame::HOLE) { |
| 108 VideoFrameExternalResources external_resources; |
| 109 external_resources.type = VideoFrameExternalResources::HOLE; |
| 110 return external_resources; |
| 111 } |
| 112 #endif |
| 113 |
| 114 // Only YUV software video frames are supported. |
| 115 DCHECK(input_frame_format == media::VideoFrame::YV12 || |
| 116 input_frame_format == media::VideoFrame::YV16); |
| 117 if (input_frame_format != media::VideoFrame::YV12 && |
| 118 input_frame_format != media::VideoFrame::YV16) |
| 119 return VideoFrameExternalResources(); |
| 120 |
| 121 bool software_compositor = !resource_provider_->GraphicsContext3D(); |
| 122 |
| 123 GLenum output_resource_format = kYUVResourceFormat; |
| 124 size_t output_plane_count = 3; |
| 125 |
| 126 // TODO(skaslev): If we're in software compositing mode, we do the YUV -> RGB |
| 127 // conversion here. That involves an extra copy of each frame to a bitmap. |
| 128 // Obviously, this is suboptimal and should be addressed once ubercompositor |
| 129 // starts shaping up. |
| 130 if (software_compositor) { |
| 131 output_resource_format = kRGBResourceFormat; |
| 132 output_plane_count = 1; |
| 133 } |
| 134 |
| 135 int max_resource_size = resource_provider_->max_texture_size(); |
| 136 gfx::Size coded_frame_size = video_frame->coded_size(); |
| 137 |
| 138 ResourceProvider::ResourceIdArray plane_resources; |
| 139 bool allocation_success = true; |
| 140 |
| 141 for (size_t i = 0; i < output_plane_count; ++i) { |
| 142 gfx::Size plane_size = |
| 143 SoftwarePlaneDimension(input_frame_format, |
| 144 coded_frame_size, |
| 145 output_resource_format, |
| 146 i); |
| 147 if (plane_size.IsEmpty() || |
| 148 plane_size.width() > max_resource_size || |
| 149 plane_size.height() > max_resource_size) { |
| 150 allocation_success = false; |
| 151 break; |
| 152 } |
| 153 |
| 154 // TODO(danakj): Could recycle resources that we previously allocated and |
| 155 // were returned to us. |
| 156 ResourceProvider::ResourceId resource_id = |
| 157 resource_provider_->CreateResource(plane_size, |
| 158 output_resource_format, |
| 159 ResourceProvider::TextureUsageAny); |
| 160 if (resource_id == 0) { |
| 161 allocation_success = false; |
| 162 break; |
| 163 } |
| 164 |
| 165 plane_resources.push_back(resource_id); |
| 166 } |
| 167 |
| 168 if (!allocation_success) { |
| 169 for (size_t i = 0; i < plane_resources.size(); ++i) |
| 170 resource_provider_->DeleteResource(plane_resources[i]); |
| 171 return VideoFrameExternalResources(); |
| 172 } |
| 173 |
| 174 VideoFrameExternalResources external_resources; |
| 175 |
| 176 if (software_compositor) { |
| 177 DCHECK_EQ(output_resource_format, kRGBResourceFormat); |
| 178 DCHECK_EQ(plane_resources.size(), 1u); |
| 179 |
| 180 if (!video_renderer_) |
| 181 video_renderer_.reset(new media::SkCanvasVideoRenderer); |
| 182 |
| 183 { |
| 184 ResourceProvider::ScopedWriteLockSoftware lock( |
| 185 resource_provider_, plane_resources[0]); |
| 186 video_renderer_->Paint(video_frame, |
| 187 lock.sk_canvas(), |
| 188 video_frame->visible_rect(), |
| 189 0xff); |
| 190 } |
| 191 |
| 192 // In software mode, the resource provider won't be lost. Soon this callback |
| 193 // will be called directly from the resource provider, same as 3d |
| 194 // compositing mode, so this raw unretained resource_provider will always |
| 195 // be valid when the callback is fired. |
| 196 TextureMailbox::ReleaseCallback callback_to_free_resource = |
| 197 base::Bind(&ReleaseResource, |
| 198 base::Unretained(resource_provider_), |
| 199 plane_resources[0]); |
| 200 external_resources.software_resources.push_back(plane_resources[0]); |
| 201 external_resources.software_release_callback = callback_to_free_resource; |
| 202 |
| 203 external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE; |
| 204 return external_resources; |
| 205 } |
| 206 |
| 207 DCHECK_EQ(output_resource_format, |
| 208 static_cast<unsigned>(kYUVResourceFormat)); |
| 209 |
| 210 WebKit::WebGraphicsContext3D* context = |
| 211 resource_provider_->GraphicsContext3D(); |
| 212 DCHECK(context); |
| 213 |
| 214 for (size_t plane = 0; plane < plane_resources.size(); ++plane) { |
| 215 // Update each plane's resource id with its content. |
| 216 ResourceProvider::ResourceId output_plane_resource_id = |
| 217 plane_resources[plane]; |
| 218 gfx::Size plane_size = |
| 219 SoftwarePlaneDimension(input_frame_format, |
| 220 coded_frame_size, |
| 221 output_resource_format, |
| 222 plane); |
| 223 const uint8_t* input_plane_pixels = video_frame->data(plane); |
| 224 |
| 225 gfx::Rect image_rect( |
| 226 0, 0, video_frame->stride(plane), plane_size.height()); |
| 227 gfx::Rect source_rect(plane_size); |
| 228 resource_provider_->SetPixels(output_plane_resource_id, |
| 229 input_plane_pixels, |
| 230 image_rect, |
| 231 source_rect, |
| 232 gfx::Vector2d()); |
| 233 |
| 234 gpu::Mailbox mailbox; |
| 235 { |
| 236 ResourceProvider::ScopedWriteLockGL lock( |
| 237 resource_provider_, output_plane_resource_id); |
| 238 |
| 239 GLC(context, context->genMailboxCHROMIUM(mailbox.name)); |
| 240 GLC(context, context->bindTexture(GL_TEXTURE_2D, lock.texture_id())); |
| 241 GLC(context, context->produceTextureCHROMIUM(GL_TEXTURE_2D, |
| 242 mailbox.name)); |
| 243 GLC(context, context->bindTexture(GL_TEXTURE_2D, 0)); |
| 244 } |
| 245 |
| 246 // This callback is called by the resource provider itself, so it's okay to |
| 247 // use an unretained raw pointer here. |
| 248 TextureMailbox::ReleaseCallback callback_to_free_resource = |
| 249 base::Bind(&ReleaseResource, |
| 250 base::Unretained(resource_provider_), |
| 251 output_plane_resource_id); |
| 252 external_resources.mailboxes.push_back( |
| 253 TextureMailbox(mailbox, callback_to_free_resource)); |
| 254 } |
| 255 |
| 256 external_resources.type = VideoFrameExternalResources::YUV_RESOURCE; |
| 257 return external_resources; |
| 258 } |
| 259 |
| 260 VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes( |
| 261 const scoped_refptr<media::VideoFrame>& video_frame, |
| 262 const TextureMailbox::ReleaseCallback& release_callback) { |
| 263 if (!VerifyFrame(video_frame)) |
| 264 return VideoFrameExternalResources(); |
| 265 |
| 266 media::VideoFrame::Format frame_format = video_frame->format(); |
| 267 |
| 268 DCHECK_EQ(frame_format, media::VideoFrame::NATIVE_TEXTURE); |
| 269 if (frame_format != media::VideoFrame::NATIVE_TEXTURE) |
| 270 return VideoFrameExternalResources(); |
| 271 |
| 272 WebKit::WebGraphicsContext3D* context = |
| 273 resource_provider_->GraphicsContext3D(); |
| 274 if (!context) |
| 275 return VideoFrameExternalResources(); |
| 276 |
| 277 VideoFrameExternalResources external_resources; |
| 278 switch (video_frame->texture_target()) { |
| 279 case GL_TEXTURE_2D: |
| 280 external_resources.type = VideoFrameExternalResources::RGB_RESOURCE; |
| 281 break; |
| 282 case GL_TEXTURE_EXTERNAL_OES: |
| 283 external_resources.type = |
| 284 VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE; |
| 285 break; |
| 286 case GL_TEXTURE_RECTANGLE_ARB: |
| 287 external_resources.type = VideoFrameExternalResources::IO_SURFACE; |
| 288 break; |
| 289 default: |
| 290 NOTREACHED(); |
| 291 return VideoFrameExternalResources(); |
| 292 } |
| 293 |
| 294 gpu::Mailbox mailbox; |
| 295 GLC(context, context->genMailboxCHROMIUM(mailbox.name)); |
| 296 GLC(context, context->bindTexture(GL_TEXTURE_2D, video_frame->texture_id())); |
| 297 GLC(context, context->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name)); |
| 298 GLC(context, context->bindTexture(GL_TEXTURE_2D, 0)); |
| 299 |
| 300 TextureMailbox::ReleaseCallback callback_to_return_resource = |
| 301 base::Bind(&ReturnTexture, |
| 302 base::Unretained(resource_provider_), |
| 303 release_callback, |
| 304 video_frame->texture_id(), |
| 305 mailbox); |
| 306 external_resources.mailboxes.push_back( |
| 307 TextureMailbox(mailbox, callback_to_return_resource)); |
| 308 return external_resources; |
| 309 } |
| 310 |
| 311 // static |
| 312 void VideoResourceUpdater::ReturnTexture( |
| 313 ResourceProvider* resource_provider, |
| 314 TextureMailbox::ReleaseCallback callback, |
| 315 unsigned texture_id, |
| 316 gpu::Mailbox mailbox, |
| 317 unsigned sync_point) { |
| 318 WebKit::WebGraphicsContext3D* context = |
| 319 resource_provider->GraphicsContext3D(); |
| 320 GLC(context, context->bindTexture(GL_TEXTURE_2D, texture_id)); |
| 321 GLC(context, context->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name)); |
| 322 GLC(context, context->bindTexture(GL_TEXTURE_2D, 0)); |
| 323 callback.Run(sync_point); |
| 324 } |
| 325 |
| 326 } // namespace cc |
OLD | NEW |