Home | History | Annotate | Download | only in codec
      1 // Copyright (c) 2012 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 "remoting/codec/video_decoder_vp8.h"
      6 
      7 #include <math.h>
      8 
      9 #include <algorithm>
     10 
     11 #include "base/logging.h"
     12 #include "media/base/media.h"
     13 #include "media/base/yuv_convert.h"
     14 #include "remoting/base/util.h"
     15 
     16 extern "C" {
     17 #define VPX_CODEC_DISABLE_COMPAT 1
     18 #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h"
     19 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
     20 }
     21 
     22 namespace remoting {
     23 
     24 enum { kBytesPerPixelRGB32 = 4 };
     25 
     26 const uint32 kTransparent = 0;
     27 
     28 VideoDecoderVp8::VideoDecoderVp8()
     29     : state_(kUninitialized),
     30       codec_(NULL),
     31       last_image_(NULL),
     32       screen_size_(SkISize::Make(0, 0)) {
     33 }
     34 
     35 VideoDecoderVp8::~VideoDecoderVp8() {
     36   if (codec_) {
     37     vpx_codec_err_t ret = vpx_codec_destroy(codec_);
     38     CHECK(ret == VPX_CODEC_OK) << "Failed to destroy codec";
     39   }
     40   delete codec_;
     41 }
     42 
     43 void VideoDecoderVp8::Initialize(const SkISize& screen_size) {
     44   DCHECK(!screen_size.isEmpty());
     45 
     46   screen_size_ = screen_size;
     47   state_ = kReady;
     48 
     49   transparent_region_.setRect(SkIRect::MakeSize(screen_size_));
     50 }
     51 
     52 VideoDecoder::DecodeResult VideoDecoderVp8::DecodePacket(
     53     const VideoPacket* packet) {
     54   DCHECK_EQ(kReady, state_);
     55 
     56   // Initialize the codec as needed.
     57   if (!codec_) {
     58     codec_ = new vpx_codec_ctx_t();
     59 
     60     // TODO(hclam): Scale the number of threads with number of cores of the
     61     // machine.
     62     vpx_codec_dec_cfg config;
     63     config.w = 0;
     64     config.h = 0;
     65     config.threads = 2;
     66     vpx_codec_err_t ret =
     67         vpx_codec_dec_init(
     68             codec_, vpx_codec_vp8_dx(), &config, 0);
     69     if (ret != VPX_CODEC_OK) {
     70       LOG(INFO) << "Cannot initialize codec.";
     71       delete codec_;
     72       codec_ = NULL;
     73       state_ = kError;
     74       return DECODE_ERROR;
     75     }
     76   }
     77 
     78   // Do the actual decoding.
     79   vpx_codec_err_t ret = vpx_codec_decode(
     80       codec_, reinterpret_cast<const uint8*>(packet->data().data()),
     81       packet->data().size(), NULL, 0);
     82   if (ret != VPX_CODEC_OK) {
     83     LOG(INFO) << "Decoding failed:" << vpx_codec_err_to_string(ret) << "\n"
     84               << "Details: " << vpx_codec_error(codec_) << "\n"
     85               << vpx_codec_error_detail(codec_);
     86     return DECODE_ERROR;
     87   }
     88 
     89   // Gets the decoded data.
     90   vpx_codec_iter_t iter = NULL;
     91   vpx_image_t* image = vpx_codec_get_frame(codec_, &iter);
     92   if (!image) {
     93     LOG(INFO) << "No video frame decoded";
     94     return DECODE_ERROR;
     95   }
     96   last_image_ = image;
     97 
     98   SkRegion region;
     99   for (int i = 0; i < packet->dirty_rects_size(); ++i) {
    100     Rect remoting_rect = packet->dirty_rects(i);
    101     SkIRect rect = SkIRect::MakeXYWH(remoting_rect.x(),
    102                                      remoting_rect.y(),
    103                                      remoting_rect.width(),
    104                                      remoting_rect.height());
    105     region.op(rect, SkRegion::kUnion_Op);
    106   }
    107 
    108   updated_region_.op(region, SkRegion::kUnion_Op);
    109 
    110   // Update the desktop shape region.
    111   SkRegion desktop_shape_region;
    112   if (packet->has_use_desktop_shape()) {
    113     for (int i = 0; i < packet->desktop_shape_rects_size(); ++i) {
    114       Rect remoting_rect = packet->desktop_shape_rects(i);
    115       SkIRect rect = SkIRect::MakeXYWH(remoting_rect.x(),
    116                                        remoting_rect.y(),
    117                                        remoting_rect.width(),
    118                                        remoting_rect.height());
    119       desktop_shape_region.op(rect, SkRegion::kUnion_Op);
    120     }
    121   } else {
    122     // Fallback for the case when the host didn't include the desktop shape
    123     // region.
    124     desktop_shape_region = SkRegion(SkIRect::MakeSize(screen_size_));
    125   }
    126 
    127   UpdateImageShapeRegion(&desktop_shape_region);
    128 
    129   return DECODE_DONE;
    130 }
    131 
    132 bool VideoDecoderVp8::IsReadyForData() {
    133   return state_ == kReady;
    134 }
    135 
    136 VideoPacketFormat::Encoding VideoDecoderVp8::Encoding() {
    137   return VideoPacketFormat::ENCODING_VP8;
    138 }
    139 
    140 void VideoDecoderVp8::Invalidate(const SkISize& view_size,
    141                                  const SkRegion& region) {
    142   DCHECK_EQ(kReady, state_);
    143   DCHECK(!view_size.isEmpty());
    144 
    145   for (SkRegion::Iterator i(region); !i.done(); i.next()) {
    146     SkIRect rect = i.rect();
    147     rect = ScaleRect(rect, view_size, screen_size_);
    148     updated_region_.op(rect, SkRegion::kUnion_Op);
    149   }
    150 
    151   // Updated areas outside of the new desktop shape region should be made
    152   // transparent, not repainted.
    153   SkRegion difference = updated_region_;
    154   difference.op(desktop_shape_, SkRegion::kDifference_Op);
    155   updated_region_.op(difference, SkRegion::kDifference_Op);
    156   transparent_region_.op(difference, SkRegion::kUnion_Op);
    157 }
    158 
    159 void VideoDecoderVp8::RenderFrame(const SkISize& view_size,
    160                                   const SkIRect& clip_area,
    161                                   uint8* image_buffer,
    162                                   int image_stride,
    163                                   SkRegion* output_region) {
    164   DCHECK_EQ(kReady, state_);
    165   DCHECK(!view_size.isEmpty());
    166 
    167   // Early-return and do nothing if we haven't yet decoded any frames.
    168   if (!last_image_)
    169     return;
    170 
    171   SkIRect source_clip = SkIRect::MakeWH(last_image_->d_w, last_image_->d_h);
    172 
    173   // ScaleYUVToRGB32WithRect does not currently support up-scaling.  We won't
    174   // be asked to up-scale except during resizes or if page zoom is >100%, so
    175   // we work-around the limitation by using the slower ScaleYUVToRGB32.
    176   // TODO(wez): Remove this hack if/when ScaleYUVToRGB32WithRect can up-scale.
    177   if (!updated_region_.isEmpty() &&
    178       (source_clip.width() < view_size.width() ||
    179        source_clip.height() < view_size.height())) {
    180     // We're scaling only |clip_area| into the |image_buffer|, so we need to
    181     // work out which source rectangle that corresponds to.
    182     SkIRect source_rect = ScaleRect(clip_area, view_size, screen_size_);
    183     source_rect = SkIRect::MakeLTRB(RoundToTwosMultiple(source_rect.left()),
    184                                     RoundToTwosMultiple(source_rect.top()),
    185                                     source_rect.right(),
    186                                     source_rect.bottom());
    187 
    188     // If there were no changes within the clip source area then don't render.
    189     if (!updated_region_.intersects(source_rect))
    190       return;
    191 
    192     // Scale & convert the entire clip area.
    193     int y_offset = CalculateYOffset(source_rect.x(),
    194                                     source_rect.y(),
    195                                     last_image_->stride[0]);
    196     int uv_offset = CalculateUVOffset(source_rect.x(),
    197                                       source_rect.y(),
    198                                       last_image_->stride[1]);
    199     ScaleYUVToRGB32(last_image_->planes[0] + y_offset,
    200                     last_image_->planes[1] + uv_offset,
    201                     last_image_->planes[2] + uv_offset,
    202                     image_buffer,
    203                     source_rect.width(),
    204                     source_rect.height(),
    205                     clip_area.width(),
    206                     clip_area.height(),
    207                     last_image_->stride[0],
    208                     last_image_->stride[1],
    209                     image_stride,
    210                     media::YV12,
    211                     media::ROTATE_0,
    212                     media::FILTER_BILINEAR);
    213 
    214     output_region->op(clip_area, SkRegion::kUnion_Op);
    215     updated_region_.op(source_rect, SkRegion::kDifference_Op);
    216     return;
    217   }
    218 
    219   for (SkRegion::Iterator i(updated_region_); !i.done(); i.next()) {
    220     // Determine the scaled area affected by this rectangle changing.
    221     SkIRect rect = i.rect();
    222     if (!rect.intersect(source_clip))
    223       continue;
    224     rect = ScaleRect(rect, screen_size_, view_size);
    225     if (!rect.intersect(clip_area))
    226       continue;
    227 
    228     ConvertAndScaleYUVToRGB32Rect(last_image_->planes[0],
    229                                   last_image_->planes[1],
    230                                   last_image_->planes[2],
    231                                   last_image_->stride[0],
    232                                   last_image_->stride[1],
    233                                   screen_size_,
    234                                   source_clip,
    235                                   image_buffer,
    236                                   image_stride,
    237                                   view_size,
    238                                   clip_area,
    239                                   rect);
    240 
    241     output_region->op(rect, SkRegion::kUnion_Op);
    242   }
    243 
    244   updated_region_.op(ScaleRect(clip_area, view_size, screen_size_),
    245                      SkRegion::kDifference_Op);
    246 
    247   for (SkRegion::Iterator i(transparent_region_); !i.done(); i.next()) {
    248     // Determine the scaled area affected by this rectangle changing.
    249     SkIRect rect = i.rect();
    250     if (!rect.intersect(source_clip))
    251       continue;
    252     rect = ScaleRect(rect, screen_size_, view_size);
    253     if (!rect.intersect(clip_area))
    254       continue;
    255 
    256     // Fill the rectange with transparent pixels.
    257     FillRect(image_buffer, image_stride, rect, kTransparent);
    258     output_region->op(rect, SkRegion::kUnion_Op);
    259   }
    260 
    261   SkIRect scaled_clip_area = ScaleRect(clip_area, view_size, screen_size_);
    262   updated_region_.op(scaled_clip_area, SkRegion::kDifference_Op);
    263   transparent_region_.op(scaled_clip_area, SkRegion::kDifference_Op);
    264 }
    265 
    266 const SkRegion* VideoDecoderVp8::GetImageShape() {
    267   return &desktop_shape_;
    268 }
    269 
    270 void VideoDecoderVp8::FillRect(uint8* buffer,
    271                                int stride,
    272                                const SkIRect& rect,
    273                                uint32 color) {
    274   uint32* ptr = reinterpret_cast<uint32*>(buffer + (rect.top() * stride) +
    275       (rect.left() * kBytesPerPixelRGB32));
    276   int width = rect.width();
    277   for (int height = rect.height(); height > 0; --height) {
    278     std::fill(ptr, ptr + width, color);
    279     ptr += stride / kBytesPerPixelRGB32;
    280   }
    281 }
    282 
    283 void VideoDecoderVp8::UpdateImageShapeRegion(SkRegion* new_desktop_shape) {
    284   // Add all areas that have been updated or become transparent to the
    285   // transparent region. Exclude anything within the new desktop shape.
    286   transparent_region_.op(desktop_shape_, SkRegion::kUnion_Op);
    287   transparent_region_.op(updated_region_, SkRegion::kUnion_Op);
    288   transparent_region_.op(*new_desktop_shape, SkRegion::kDifference_Op);
    289 
    290   // Add newly exposed areas to the update region and limit updates to the new
    291   // desktop shape.
    292   SkRegion difference = *new_desktop_shape;
    293   difference.op(desktop_shape_, SkRegion::kDifference_Op);
    294   updated_region_.op(difference, SkRegion::kUnion_Op);
    295   updated_region_.op(*new_desktop_shape, SkRegion::kIntersect_Op);
    296 
    297   // Set the new desktop shape region.
    298   desktop_shape_.swap(*new_desktop_shape);
    299 }
    300 
    301 }  // namespace remoting
    302