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