OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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/base/encoder_vp8.h" | 5 #include "remoting/base/encoder_vp8.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "media/base/callback.h" | 8 #include "media/base/callback.h" |
9 #include "media/base/yuv_convert.h" | 9 #include "media/base/yuv_convert.h" |
10 #include "remoting/base/capture_data.h" | 10 #include "remoting/base/capture_data.h" |
(...skipping 16 matching lines...) Expand all Loading... |
27 | 27 |
28 namespace remoting { | 28 namespace remoting { |
29 | 29 |
30 EncoderVp8::EncoderVp8() | 30 EncoderVp8::EncoderVp8() |
31 : initialized_(false), | 31 : initialized_(false), |
32 codec_(NULL), | 32 codec_(NULL), |
33 image_(NULL), | 33 image_(NULL), |
34 active_map_width_(0), | 34 active_map_width_(0), |
35 active_map_height_(0), | 35 active_map_height_(0), |
36 last_timestamp_(0), | 36 last_timestamp_(0), |
37 size_(0, 0) { | 37 size_(SkISize::Make(0, 0)) { |
38 } | 38 } |
39 | 39 |
40 EncoderVp8::~EncoderVp8() { | 40 EncoderVp8::~EncoderVp8() { |
41 Destroy(); | 41 Destroy(); |
42 } | 42 } |
43 | 43 |
44 void EncoderVp8::Destroy() { | 44 void EncoderVp8::Destroy() { |
45 if (initialized_) { | 45 if (initialized_) { |
46 vpx_codec_err_t ret = vpx_codec_destroy(codec_.get()); | 46 vpx_codec_err_t ret = vpx_codec_destroy(codec_.get()); |
47 DCHECK(ret == VPX_CODEC_OK) << "Failed to destroy codec"; | 47 DCHECK(ret == VPX_CODEC_OK) << "Failed to destroy codec"; |
48 initialized_ = false; | 48 initialized_ = false; |
49 } | 49 } |
50 } | 50 } |
51 | 51 |
52 bool EncoderVp8::Init(const gfx::Size& size) { | 52 bool EncoderVp8::Init(const SkISize& size) { |
53 Destroy(); | 53 Destroy(); |
54 size_ = size; | 54 size_ = size; |
55 codec_.reset(new vpx_codec_ctx_t()); | 55 codec_.reset(new vpx_codec_ctx_t()); |
56 image_.reset(new vpx_image_t()); | 56 image_.reset(new vpx_image_t()); |
57 memset(image_.get(), 0, sizeof(vpx_image_t)); | 57 memset(image_.get(), 0, sizeof(vpx_image_t)); |
58 | 58 |
59 image_->fmt = VPX_IMG_FMT_YV12; | 59 image_->fmt = VPX_IMG_FMT_YV12; |
60 | 60 |
61 // libvpx seems to require both to be assigned. | 61 // libvpx seems to require both to be assigned. |
62 image_->d_w = size.width(); | 62 image_->d_w = size.width(); |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
128 return false; | 128 return false; |
129 | 129 |
130 // Use the lowest level of noise sensitivity so as to spend less time | 130 // Use the lowest level of noise sensitivity so as to spend less time |
131 // on motion estimation and inter-prediction mode. | 131 // on motion estimation and inter-prediction mode. |
132 if (vpx_codec_control(codec_.get(), VP8E_SET_NOISE_SENSITIVITY, 0)) | 132 if (vpx_codec_control(codec_.get(), VP8E_SET_NOISE_SENSITIVITY, 0)) |
133 return false; | 133 return false; |
134 return true; | 134 return true; |
135 } | 135 } |
136 | 136 |
137 // static | 137 // static |
138 gfx::Rect EncoderVp8::AlignAndClipRect(const gfx::Rect& rect, | 138 SkIRect EncoderVp8::AlignAndClipRect(const SkIRect& rect, |
139 int width, int height) { | 139 int width, int height) { |
140 gfx::Rect screen(RoundToTwosMultiple(width), RoundToTwosMultiple(height)); | 140 SkIRect screen(SkIRect::MakeWH(RoundToTwosMultiple(width), |
141 return screen.Intersect(AlignRect(rect)); | 141 RoundToTwosMultiple(height))); |
| 142 if (!screen.intersect(AlignRect(rect))) { |
| 143 screen = SkIRect::MakeWH(0, 0); |
| 144 } |
| 145 return screen; |
142 } | 146 } |
143 | 147 |
144 bool EncoderVp8::PrepareImage(scoped_refptr<CaptureData> capture_data, | 148 bool EncoderVp8::PrepareImage(scoped_refptr<CaptureData> capture_data, |
145 std::vector<gfx::Rect>* updated_rects) { | 149 RectVector* updated_rects) { |
146 // Perform RGB->YUV conversion. | 150 // Perform RGB->YUV conversion. |
147 if (capture_data->pixel_format() != media::VideoFrame::RGB32) { | 151 if (capture_data->pixel_format() != media::VideoFrame::RGB32) { |
148 LOG(ERROR) << "Only RGB32 is supported"; | 152 LOG(ERROR) << "Only RGB32 is supported"; |
149 return false; | 153 return false; |
150 } | 154 } |
151 | 155 |
152 const SkRegion& region = capture_data->dirty_region(); | 156 const SkRegion& region = capture_data->dirty_region(); |
153 const uint8* in = capture_data->data_planes().data[0]; | 157 const uint8* in = capture_data->data_planes().data[0]; |
154 const int in_stride = capture_data->data_planes().strides[0]; | 158 const int in_stride = capture_data->data_planes().strides[0]; |
155 const int plane_size = | 159 const int plane_size = |
156 capture_data->size().width() * capture_data->size().height(); | 160 capture_data->size().width() * capture_data->size().height(); |
157 uint8* y_out = yuv_image_.get(); | 161 uint8* y_out = yuv_image_.get(); |
158 uint8* u_out = yuv_image_.get() + plane_size; | 162 uint8* u_out = yuv_image_.get() + plane_size; |
159 uint8* v_out = yuv_image_.get() + plane_size + plane_size / 4; | 163 uint8* v_out = yuv_image_.get() + plane_size + plane_size / 4; |
160 const int y_stride = image_->stride[0]; | 164 const int y_stride = image_->stride[0]; |
161 const int uv_stride = image_->stride[1]; | 165 const int uv_stride = image_->stride[1]; |
162 | 166 |
163 DCHECK(updated_rects->empty()); | 167 DCHECK(updated_rects->empty()); |
164 for (SkRegion::Iterator r(region); !r.done(); r.next()) { | 168 for (SkRegion::Iterator r(region); !r.done(); r.next()) { |
165 // Align the rectangle, report it as updated. | 169 // Align the rectangle, report it as updated. |
166 SkIRect skRect = r.rect(); | 170 SkIRect rect = r.rect(); |
167 gfx::Rect rect(skRect.fLeft, skRect.fTop, skRect.width(), skRect.height()); | |
168 rect = AlignAndClipRect(rect, image_->w, image_->h); | 171 rect = AlignAndClipRect(rect, image_->w, image_->h); |
169 if (!rect.IsEmpty()) | 172 if (!rect.isEmpty()) |
170 updated_rects->push_back(rect); | 173 updated_rects->push_back(rect); |
171 | 174 |
172 ConvertRGB32ToYUVWithRect(in, | 175 ConvertRGB32ToYUVWithRect(in, |
173 y_out, | 176 y_out, |
174 u_out, | 177 u_out, |
175 v_out, | 178 v_out, |
176 rect.x(), | 179 rect.fLeft, |
177 rect.y(), | 180 rect.fTop, |
178 rect.width(), | 181 rect.width(), |
179 rect.height(), | 182 rect.height(), |
180 in_stride, | 183 in_stride, |
181 y_stride, | 184 y_stride, |
182 uv_stride); | 185 uv_stride); |
183 } | 186 } |
184 return true; | 187 return true; |
185 } | 188 } |
186 | 189 |
187 void EncoderVp8::PrepareActiveMap( | 190 void EncoderVp8::PrepareActiveMap(const RectVector& updated_rects) { |
188 const std::vector<gfx::Rect>& updated_rects) { | |
189 // Clear active map first. | 191 // Clear active map first. |
190 memset(active_map_.get(), 0, active_map_width_ * active_map_height_); | 192 memset(active_map_.get(), 0, active_map_width_ * active_map_height_); |
191 | 193 |
192 // Mark blocks at active. | 194 // Mark blocks at active. |
193 for (size_t i = 0; i < updated_rects.size(); ++i) { | 195 for (size_t i = 0; i < updated_rects.size(); ++i) { |
194 const gfx::Rect& r = updated_rects[i]; | 196 const SkIRect& r = updated_rects[i]; |
195 CHECK(r.width() && r.height()); | 197 CHECK(r.width() && r.height()); |
196 | 198 |
197 int left = r.x() / kMacroBlockSize; | 199 int left = r.fLeft / kMacroBlockSize; |
198 int right = (r.right() - 1) / kMacroBlockSize; | 200 int right = (r.fRight - 1) / kMacroBlockSize; |
199 int top = r.y() / kMacroBlockSize; | 201 int top = r.fTop / kMacroBlockSize; |
200 int bottom = (r.bottom() - 1) / kMacroBlockSize; | 202 int bottom = (r.fBottom - 1) / kMacroBlockSize; |
201 CHECK(right < active_map_width_); | 203 CHECK(right < active_map_width_); |
202 CHECK(bottom < active_map_height_); | 204 CHECK(bottom < active_map_height_); |
203 | 205 |
204 uint8* map = active_map_.get() + top * active_map_width_; | 206 uint8* map = active_map_.get() + top * active_map_width_; |
205 for (int y = top; y <= bottom; ++y) { | 207 for (int y = top; y <= bottom; ++y) { |
206 for (int x = left; x <= right; ++x) | 208 for (int x = left; x <= right; ++x) |
207 map[x] = 1; | 209 map[x] = 1; |
208 map += active_map_width_; | 210 map += active_map_width_; |
209 } | 211 } |
210 } | 212 } |
211 } | 213 } |
212 | 214 |
213 void EncoderVp8::Encode(scoped_refptr<CaptureData> capture_data, | 215 void EncoderVp8::Encode(scoped_refptr<CaptureData> capture_data, |
214 bool key_frame, | 216 bool key_frame, |
215 DataAvailableCallback* data_available_callback) { | 217 DataAvailableCallback* data_available_callback) { |
216 if (!initialized_ || (capture_data->size() != size_)) { | 218 if (!initialized_ || (capture_data->size() != size_)) { |
217 bool ret = Init(capture_data->size()); | 219 bool ret = Init(capture_data->size()); |
218 // TODO(hclam): Handle error better. | 220 // TODO(hclam): Handle error better. |
219 DCHECK(ret) << "Initialization of encoder failed"; | 221 DCHECK(ret) << "Initialization of encoder failed"; |
220 initialized_ = ret; | 222 initialized_ = ret; |
221 } | 223 } |
222 | 224 |
223 std::vector<gfx::Rect> updated_rects; | 225 RectVector updated_rects; |
224 if (!PrepareImage(capture_data, &updated_rects)) { | 226 if (!PrepareImage(capture_data, &updated_rects)) { |
225 NOTREACHED() << "Can't image data for encoding"; | 227 NOTREACHED() << "Can't image data for encoding"; |
226 } | 228 } |
227 | 229 |
228 // Update active map based on updated rectangles. | 230 // Update active map based on updated rectangles. |
229 PrepareActiveMap(updated_rects); | 231 PrepareActiveMap(updated_rects); |
230 | 232 |
231 // Apply active map to the encoder. | 233 // Apply active map to the encoder. |
232 vpx_active_map_t act_map; | 234 vpx_active_map_t act_map; |
233 act_map.rows = active_map_height_; | 235 act_map.rows = active_map_height_; |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
276 | 278 |
277 message->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); | 279 message->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); |
278 message->set_flags(VideoPacket::FIRST_PACKET | VideoPacket::LAST_PACKET | | 280 message->set_flags(VideoPacket::FIRST_PACKET | VideoPacket::LAST_PACKET | |
279 VideoPacket::LAST_PARTITION); | 281 VideoPacket::LAST_PARTITION); |
280 message->mutable_format()->set_screen_width(capture_data->size().width()); | 282 message->mutable_format()->set_screen_width(capture_data->size().width()); |
281 message->mutable_format()->set_screen_height(capture_data->size().height()); | 283 message->mutable_format()->set_screen_height(capture_data->size().height()); |
282 message->set_capture_time_ms(capture_data->capture_time_ms()); | 284 message->set_capture_time_ms(capture_data->capture_time_ms()); |
283 message->set_client_sequence_number(capture_data->client_sequence_number()); | 285 message->set_client_sequence_number(capture_data->client_sequence_number()); |
284 for (size_t i = 0; i < updated_rects.size(); ++i) { | 286 for (size_t i = 0; i < updated_rects.size(); ++i) { |
285 Rect* rect = message->add_dirty_rects(); | 287 Rect* rect = message->add_dirty_rects(); |
286 rect->set_x(updated_rects[i].x()); | 288 rect->set_x(updated_rects[i].fLeft); |
287 rect->set_y(updated_rects[i].y()); | 289 rect->set_y(updated_rects[i].fTop); |
288 rect->set_width(updated_rects[i].width()); | 290 rect->set_width(updated_rects[i].width()); |
289 rect->set_height(updated_rects[i].height()); | 291 rect->set_height(updated_rects[i].height()); |
290 } | 292 } |
291 | 293 |
292 data_available_callback->Run(message); | 294 data_available_callback->Run(message); |
293 delete data_available_callback; | 295 delete data_available_callback; |
294 } | 296 } |
295 | 297 |
296 } // namespace remoting | 298 } // namespace remoting |
OLD | NEW |