Home | History | Annotate | Download | only in resources
      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 "gpu/command_buffer/client/gles2_interface.h"
     12 #include "media/base/video_frame.h"
     13 #include "media/filters/skcanvas_video_renderer.h"
     14 #include "third_party/khronos/GLES2/gl2.h"
     15 #include "third_party/khronos/GLES2/gl2ext.h"
     16 #include "ui/gfx/size_conversions.h"
     17 
     18 namespace cc {
     19 
     20 const ResourceFormat kYUVResourceFormat = LUMINANCE_8;
     21 const ResourceFormat kRGBResourceFormat = RGBA_8888;
     22 
     23 VideoFrameExternalResources::VideoFrameExternalResources() : type(NONE) {}
     24 
     25 VideoFrameExternalResources::~VideoFrameExternalResources() {}
     26 
     27 VideoResourceUpdater::VideoResourceUpdater(ContextProvider* context_provider,
     28                                            ResourceProvider* resource_provider)
     29     : context_provider_(context_provider),
     30       resource_provider_(resource_provider) {
     31 }
     32 
     33 VideoResourceUpdater::~VideoResourceUpdater() {
     34   while (!all_resources_.empty()) {
     35     resource_provider_->DeleteResource(all_resources_.back());
     36     all_resources_.pop_back();
     37   }
     38 }
     39 
     40 void VideoResourceUpdater::DeleteResource(unsigned resource_id) {
     41   resource_provider_->DeleteResource(resource_id);
     42   all_resources_.erase(std::remove(all_resources_.begin(),
     43                                    all_resources_.end(),
     44                                    resource_id));
     45 }
     46 
     47 VideoFrameExternalResources VideoResourceUpdater::
     48     CreateExternalResourcesFromVideoFrame(
     49         const scoped_refptr<media::VideoFrame>& video_frame) {
     50   if (!VerifyFrame(video_frame))
     51     return VideoFrameExternalResources();
     52 
     53   if (video_frame->format() == media::VideoFrame::NATIVE_TEXTURE)
     54     return CreateForHardwarePlanes(video_frame);
     55   else
     56     return CreateForSoftwarePlanes(video_frame);
     57 }
     58 
     59 bool VideoResourceUpdater::VerifyFrame(
     60     const scoped_refptr<media::VideoFrame>& video_frame) {
     61   // If these fail, we'll have to add logic that handles offset bitmap/texture
     62   // UVs. For now, just expect (0, 0) offset, since all our decoders so far
     63   // don't offset.
     64   DCHECK_EQ(video_frame->visible_rect().x(), 0);
     65   DCHECK_EQ(video_frame->visible_rect().y(), 0);
     66 
     67   switch (video_frame->format()) {
     68     // Acceptable inputs.
     69     case media::VideoFrame::YV12:
     70     case media::VideoFrame::YV12A:
     71     case media::VideoFrame::YV16:
     72     case media::VideoFrame::YV12J:
     73     case media::VideoFrame::NATIVE_TEXTURE:
     74 #if defined(VIDEO_HOLE)
     75     case media::VideoFrame::HOLE:
     76 #endif  // defined(VIDEO_HOLE)
     77       return true;
     78 
     79     // Unacceptable inputs. \(_o)/
     80     case media::VideoFrame::UNKNOWN:
     81     case media::VideoFrame::HISTOGRAM_MAX:
     82     case media::VideoFrame::I420:
     83       break;
     84   }
     85   return false;
     86 }
     87 
     88 // For frames that we receive in software format, determine the dimensions of
     89 // each plane in the frame.
     90 static gfx::Size SoftwarePlaneDimension(
     91     media::VideoFrame::Format input_frame_format,
     92     gfx::Size coded_size,
     93     ResourceFormat output_resource_format,
     94     int plane_index) {
     95   if (output_resource_format == kYUVResourceFormat) {
     96     if (plane_index == media::VideoFrame::kYPlane ||
     97         plane_index == media::VideoFrame::kAPlane)
     98       return coded_size;
     99 
    100     switch (input_frame_format) {
    101       case media::VideoFrame::YV12:
    102       case media::VideoFrame::YV12A:
    103       case media::VideoFrame::YV12J:
    104         return gfx::ToFlooredSize(gfx::ScaleSize(coded_size, 0.5f, 0.5f));
    105       case media::VideoFrame::YV16:
    106         return gfx::ToFlooredSize(gfx::ScaleSize(coded_size, 0.5f, 1.f));
    107 
    108       case media::VideoFrame::UNKNOWN:
    109       case media::VideoFrame::I420:
    110       case media::VideoFrame::NATIVE_TEXTURE:
    111       case media::VideoFrame::HISTOGRAM_MAX:
    112 #if defined(VIDEO_HOLE)
    113       case media::VideoFrame::HOLE:
    114 #endif  // defined(VIDEO_HOLE)
    115         NOTREACHED();
    116     }
    117   }
    118 
    119   DCHECK_EQ(output_resource_format, kRGBResourceFormat);
    120   return coded_size;
    121 }
    122 
    123 VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes(
    124     const scoped_refptr<media::VideoFrame>& video_frame) {
    125   media::VideoFrame::Format input_frame_format = video_frame->format();
    126 
    127 #if defined(VIDEO_HOLE)
    128   if (input_frame_format == media::VideoFrame::HOLE) {
    129     VideoFrameExternalResources external_resources;
    130     external_resources.type = VideoFrameExternalResources::HOLE;
    131     return external_resources;
    132   }
    133 #endif  // defined(VIDEO_HOLE)
    134 
    135   // Only YUV software video frames are supported.
    136   DCHECK(input_frame_format == media::VideoFrame::YV12 ||
    137          input_frame_format == media::VideoFrame::YV12A ||
    138          input_frame_format == media::VideoFrame::YV12J ||
    139          input_frame_format == media::VideoFrame::YV16);
    140   if (input_frame_format != media::VideoFrame::YV12 &&
    141       input_frame_format != media::VideoFrame::YV12A &&
    142       input_frame_format != media::VideoFrame::YV12J &&
    143       input_frame_format != media::VideoFrame::YV16)
    144     return VideoFrameExternalResources();
    145 
    146   bool software_compositor = context_provider_ == NULL;
    147 
    148   ResourceFormat output_resource_format = kYUVResourceFormat;
    149   size_t output_plane_count =
    150       (input_frame_format == media::VideoFrame::YV12A) ? 4 : 3;
    151 
    152   // TODO(skaslev): If we're in software compositing mode, we do the YUV -> RGB
    153   // conversion here. That involves an extra copy of each frame to a bitmap.
    154   // Obviously, this is suboptimal and should be addressed once ubercompositor
    155   // starts shaping up.
    156   if (software_compositor) {
    157     output_resource_format = kRGBResourceFormat;
    158     output_plane_count = 1;
    159   }
    160 
    161   int max_resource_size = resource_provider_->max_texture_size();
    162   gfx::Size coded_frame_size = video_frame->coded_size();
    163 
    164   std::vector<PlaneResource> plane_resources;
    165   bool allocation_success = true;
    166 
    167   for (size_t i = 0; i < output_plane_count; ++i) {
    168     gfx::Size output_plane_resource_size =
    169         SoftwarePlaneDimension(input_frame_format,
    170                                coded_frame_size,
    171                                output_resource_format,
    172                                i);
    173     if (output_plane_resource_size.IsEmpty() ||
    174         output_plane_resource_size.width() > max_resource_size ||
    175         output_plane_resource_size.height() > max_resource_size) {
    176       allocation_success = false;
    177       break;
    178     }
    179 
    180     ResourceProvider::ResourceId resource_id = 0;
    181     gpu::Mailbox mailbox;
    182 
    183     // Try recycle a previously-allocated resource.
    184     for (size_t i = 0; i < recycled_resources_.size(); ++i) {
    185       if (recycled_resources_[i].resource_format == output_resource_format &&
    186           recycled_resources_[i].resource_size == output_plane_resource_size) {
    187         resource_id = recycled_resources_[i].resource_id;
    188         mailbox = recycled_resources_[i].mailbox;
    189         recycled_resources_.erase(recycled_resources_.begin() + i);
    190         break;
    191       }
    192     }
    193 
    194     if (resource_id == 0) {
    195       // TODO(danakj): Abstract out hw/sw resource create/delete from
    196       // ResourceProvider and stop using ResourceProvider in this class.
    197       resource_id =
    198           resource_provider_->CreateResource(output_plane_resource_size,
    199                                              GL_CLAMP_TO_EDGE,
    200                                              ResourceProvider::TextureUsageAny,
    201                                              output_resource_format);
    202 
    203       DCHECK(mailbox.IsZero());
    204 
    205       if (!software_compositor) {
    206         DCHECK(context_provider_);
    207 
    208         gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
    209 
    210         GLC(gl, gl->GenMailboxCHROMIUM(mailbox.name));
    211         if (mailbox.IsZero()) {
    212           resource_provider_->DeleteResource(resource_id);
    213           resource_id = 0;
    214         } else {
    215           ResourceProvider::ScopedWriteLockGL lock(
    216               resource_provider_, resource_id);
    217           GLC(gl, gl->BindTexture(GL_TEXTURE_2D, lock.texture_id()));
    218           GLC(gl, gl->ProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name));
    219           GLC(gl, gl->BindTexture(GL_TEXTURE_2D, 0));
    220         }
    221       }
    222 
    223       if (resource_id)
    224         all_resources_.push_back(resource_id);
    225     }
    226 
    227     if (resource_id == 0) {
    228       allocation_success = false;
    229       break;
    230     }
    231 
    232     DCHECK(software_compositor || !mailbox.IsZero());
    233     plane_resources.push_back(PlaneResource(resource_id,
    234                                             output_plane_resource_size,
    235                                             output_resource_format,
    236                                             mailbox));
    237   }
    238 
    239   if (!allocation_success) {
    240     for (size_t i = 0; i < plane_resources.size(); ++i)
    241       DeleteResource(plane_resources[i].resource_id);
    242     return VideoFrameExternalResources();
    243   }
    244 
    245   VideoFrameExternalResources external_resources;
    246 
    247   if (software_compositor) {
    248     DCHECK_EQ(plane_resources.size(), 1u);
    249     DCHECK_EQ(plane_resources[0].resource_format, kRGBResourceFormat);
    250     DCHECK(plane_resources[0].mailbox.IsZero());
    251 
    252     if (!video_renderer_)
    253       video_renderer_.reset(new media::SkCanvasVideoRenderer);
    254 
    255     {
    256       ResourceProvider::ScopedWriteLockSoftware lock(
    257           resource_provider_, plane_resources[0].resource_id);
    258       video_renderer_->Paint(video_frame.get(),
    259                              lock.sk_canvas(),
    260                              video_frame->visible_rect(),
    261                              0xff);
    262     }
    263 
    264     RecycleResourceData recycle_data = {
    265       plane_resources[0].resource_id,
    266       plane_resources[0].resource_size,
    267       plane_resources[0].resource_format,
    268       gpu::Mailbox()
    269     };
    270     base::SharedMemory* shared_memory =
    271         resource_provider_->GetSharedMemory(plane_resources[0].resource_id);
    272     if (shared_memory) {
    273       external_resources.mailboxes.push_back(
    274           TextureMailbox(shared_memory, plane_resources[0].resource_size));
    275       external_resources.release_callbacks
    276           .push_back(base::Bind(&RecycleResource, AsWeakPtr(), recycle_data));
    277       external_resources.type = VideoFrameExternalResources::RGB_RESOURCE;
    278     } else {
    279       // TODO(jbauman): Remove this path once shared memory is available
    280       // everywhere.
    281       external_resources.software_resources
    282           .push_back(plane_resources[0].resource_id);
    283       external_resources.software_release_callback =
    284           base::Bind(&RecycleResource, AsWeakPtr(), recycle_data);
    285       external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE;
    286     }
    287 
    288     return external_resources;
    289   }
    290 
    291   for (size_t i = 0; i < plane_resources.size(); ++i) {
    292     // Update each plane's resource id with its content.
    293     DCHECK_EQ(plane_resources[i].resource_format, kYUVResourceFormat);
    294 
    295     const uint8_t* input_plane_pixels = video_frame->data(i);
    296 
    297     gfx::Rect image_rect(0,
    298                          0,
    299                          video_frame->stride(i),
    300                          plane_resources[i].resource_size.height());
    301     gfx::Rect source_rect(plane_resources[i].resource_size);
    302     resource_provider_->SetPixels(plane_resources[i].resource_id,
    303                                   input_plane_pixels,
    304                                   image_rect,
    305                                   source_rect,
    306                                   gfx::Vector2d());
    307 
    308     RecycleResourceData recycle_data = {
    309       plane_resources[i].resource_id,
    310       plane_resources[i].resource_size,
    311       plane_resources[i].resource_format,
    312       plane_resources[i].mailbox
    313     };
    314 
    315     external_resources.mailboxes.push_back(
    316         TextureMailbox(plane_resources[i].mailbox));
    317     external_resources.release_callbacks.push_back(
    318         base::Bind(&RecycleResource, AsWeakPtr(), recycle_data));
    319   }
    320 
    321   external_resources.type = VideoFrameExternalResources::YUV_RESOURCE;
    322   return external_resources;
    323 }
    324 
    325 static void ReturnTexture(const scoped_refptr<media::VideoFrame>& frame,
    326                           unsigned sync_point,
    327                           bool lost_resource) {
    328   frame->texture_mailbox()->Resync(sync_point);
    329 }
    330 
    331 VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes(
    332     const scoped_refptr<media::VideoFrame>& video_frame) {
    333   media::VideoFrame::Format frame_format = video_frame->format();
    334 
    335   DCHECK_EQ(frame_format, media::VideoFrame::NATIVE_TEXTURE);
    336   if (frame_format != media::VideoFrame::NATIVE_TEXTURE)
    337       return VideoFrameExternalResources();
    338 
    339   if (!context_provider_)
    340     return VideoFrameExternalResources();
    341 
    342   VideoFrameExternalResources external_resources;
    343   switch (video_frame->texture_target()) {
    344     case GL_TEXTURE_2D:
    345       external_resources.type = VideoFrameExternalResources::RGB_RESOURCE;
    346       break;
    347     case GL_TEXTURE_EXTERNAL_OES:
    348       external_resources.type =
    349           VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE;
    350       break;
    351     case GL_TEXTURE_RECTANGLE_ARB:
    352       external_resources.type = VideoFrameExternalResources::IO_SURFACE;
    353       break;
    354     default:
    355       NOTREACHED();
    356       return VideoFrameExternalResources();
    357   }
    358 
    359   media::VideoFrame::MailboxHolder* mailbox_holder =
    360       video_frame->texture_mailbox();
    361 
    362   external_resources.mailboxes.push_back(
    363       TextureMailbox(mailbox_holder->mailbox(),
    364                      video_frame->texture_target(),
    365                      mailbox_holder->sync_point()));
    366   external_resources.release_callbacks.push_back(
    367       base::Bind(&ReturnTexture, video_frame));
    368   return external_resources;
    369 }
    370 
    371 // static
    372 void VideoResourceUpdater::RecycleResource(
    373     base::WeakPtr<VideoResourceUpdater> updater,
    374     RecycleResourceData data,
    375     unsigned sync_point,
    376     bool lost_resource) {
    377   if (!updater.get()) {
    378     // Resource was already deleted.
    379     return;
    380   }
    381 
    382   ContextProvider* context_provider = updater->context_provider_;
    383   if (context_provider && sync_point) {
    384     GLC(context_provider->ContextGL(),
    385         context_provider->ContextGL()->WaitSyncPointCHROMIUM(sync_point));
    386   }
    387 
    388   if (lost_resource) {
    389     updater->DeleteResource(data.resource_id);
    390     return;
    391   }
    392 
    393   // Drop recycled resources that are the wrong format.
    394   while (!updater->recycled_resources_.empty() &&
    395          updater->recycled_resources_.back().resource_format !=
    396          data.resource_format) {
    397     updater->DeleteResource(updater->recycled_resources_.back().resource_id);
    398     updater->recycled_resources_.pop_back();
    399   }
    400 
    401   PlaneResource recycled_resource(data.resource_id,
    402                                   data.resource_size,
    403                                   data.resource_format,
    404                                   data.mailbox);
    405   updater->recycled_resources_.push_back(recycled_resource);
    406 }
    407 
    408 }  // namespace cc
    409