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