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