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