OLD | NEW |
| (Empty) |
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/base/decoder_vp8.h" | |
6 | |
7 #include <math.h> | |
8 | |
9 #include "base/logging.h" | |
10 #include "media/base/media.h" | |
11 #include "media/base/yuv_convert.h" | |
12 #include "remoting/base/util.h" | |
13 | |
14 extern "C" { | |
15 #define VPX_CODEC_DISABLE_COMPAT 1 | |
16 #include "third_party/libvpx/libvpx.h" | |
17 } | |
18 | |
19 namespace remoting { | |
20 | |
21 DecoderVp8::DecoderVp8() | |
22 : state_(kUninitialized), | |
23 codec_(NULL), | |
24 last_image_(NULL), | |
25 screen_size_(SkISize::Make(0, 0)) { | |
26 } | |
27 | |
28 DecoderVp8::~DecoderVp8() { | |
29 if (codec_) { | |
30 vpx_codec_err_t ret = vpx_codec_destroy(codec_); | |
31 CHECK(ret == VPX_CODEC_OK) << "Failed to destroy codec"; | |
32 } | |
33 delete codec_; | |
34 } | |
35 | |
36 void DecoderVp8::Initialize(const SkISize& screen_size) { | |
37 DCHECK(!screen_size.isEmpty()); | |
38 | |
39 screen_size_ = screen_size; | |
40 state_ = kReady; | |
41 } | |
42 | |
43 Decoder::DecodeResult DecoderVp8::DecodePacket(const VideoPacket* packet) { | |
44 DCHECK_EQ(kReady, state_); | |
45 | |
46 // Initialize the codec as needed. | |
47 if (!codec_) { | |
48 codec_ = new vpx_codec_ctx_t(); | |
49 | |
50 // TODO(hclam): Scale the number of threads with number of cores of the | |
51 // machine. | |
52 vpx_codec_dec_cfg config; | |
53 config.w = 0; | |
54 config.h = 0; | |
55 config.threads = 2; | |
56 vpx_codec_err_t ret = | |
57 vpx_codec_dec_init( | |
58 codec_, vpx_codec_vp8_dx(), &config, 0); | |
59 if (ret != VPX_CODEC_OK) { | |
60 LOG(INFO) << "Cannot initialize codec."; | |
61 delete codec_; | |
62 codec_ = NULL; | |
63 state_ = kError; | |
64 return DECODE_ERROR; | |
65 } | |
66 } | |
67 | |
68 // Do the actual decoding. | |
69 vpx_codec_err_t ret = vpx_codec_decode( | |
70 codec_, reinterpret_cast<const uint8*>(packet->data().data()), | |
71 packet->data().size(), NULL, 0); | |
72 if (ret != VPX_CODEC_OK) { | |
73 LOG(INFO) << "Decoding failed:" << vpx_codec_err_to_string(ret) << "\n" | |
74 << "Details: " << vpx_codec_error(codec_) << "\n" | |
75 << vpx_codec_error_detail(codec_); | |
76 return DECODE_ERROR; | |
77 } | |
78 | |
79 // Gets the decoded data. | |
80 vpx_codec_iter_t iter = NULL; | |
81 vpx_image_t* image = vpx_codec_get_frame(codec_, &iter); | |
82 if (!image) { | |
83 LOG(INFO) << "No video frame decoded"; | |
84 return DECODE_ERROR; | |
85 } | |
86 last_image_ = image; | |
87 | |
88 SkRegion region; | |
89 for (int i = 0; i < packet->dirty_rects_size(); ++i) { | |
90 Rect remoting_rect = packet->dirty_rects(i); | |
91 SkIRect rect = SkIRect::MakeXYWH(remoting_rect.x(), | |
92 remoting_rect.y(), | |
93 remoting_rect.width(), | |
94 remoting_rect.height()); | |
95 region.op(rect, SkRegion::kUnion_Op); | |
96 } | |
97 | |
98 updated_region_.op(region, SkRegion::kUnion_Op); | |
99 return DECODE_DONE; | |
100 } | |
101 | |
102 bool DecoderVp8::IsReadyForData() { | |
103 return state_ == kReady; | |
104 } | |
105 | |
106 VideoPacketFormat::Encoding DecoderVp8::Encoding() { | |
107 return VideoPacketFormat::ENCODING_VP8; | |
108 } | |
109 | |
110 void DecoderVp8::Invalidate(const SkISize& view_size, | |
111 const SkRegion& region) { | |
112 DCHECK_EQ(kReady, state_); | |
113 DCHECK(!view_size.isEmpty()); | |
114 | |
115 for (SkRegion::Iterator i(region); !i.done(); i.next()) { | |
116 SkIRect rect = i.rect(); | |
117 rect = ScaleRect(rect, view_size, screen_size_); | |
118 updated_region_.op(rect, SkRegion::kUnion_Op); | |
119 } | |
120 } | |
121 | |
122 void DecoderVp8::RenderFrame(const SkISize& view_size, | |
123 const SkIRect& clip_area, | |
124 uint8* image_buffer, | |
125 int image_stride, | |
126 SkRegion* output_region) { | |
127 DCHECK_EQ(kReady, state_); | |
128 DCHECK(!view_size.isEmpty()); | |
129 | |
130 // Early-return and do nothing if we haven't yet decoded any frames. | |
131 if (!last_image_) | |
132 return; | |
133 | |
134 SkIRect source_clip = SkIRect::MakeWH(last_image_->d_w, last_image_->d_h); | |
135 | |
136 // ScaleYUVToRGB32WithRect does not currently support up-scaling. We won't | |
137 // be asked to up-scale except during resizes or if page zoom is >100%, so | |
138 // we work-around the limitation by using the slower ScaleYUVToRGB32. | |
139 // TODO(wez): Remove this hack if/when ScaleYUVToRGB32WithRect can up-scale. | |
140 if (!updated_region_.isEmpty() && | |
141 (source_clip.width() < view_size.width() || | |
142 source_clip.height() < view_size.height())) { | |
143 // We're scaling only |clip_area| into the |image_buffer|, so we need to | |
144 // work out which source rectangle that corresponds to. | |
145 SkIRect source_rect = ScaleRect(clip_area, view_size, screen_size_); | |
146 source_rect = SkIRect::MakeLTRB(RoundToTwosMultiple(source_rect.left()), | |
147 RoundToTwosMultiple(source_rect.top()), | |
148 source_rect.right(), | |
149 source_rect.bottom()); | |
150 | |
151 // If there were no changes within the clip source area then don't render. | |
152 if (!updated_region_.intersects(source_rect)) | |
153 return; | |
154 | |
155 // Scale & convert the entire clip area. | |
156 int y_offset = CalculateYOffset(source_rect.x(), | |
157 source_rect.y(), | |
158 last_image_->stride[0]); | |
159 int uv_offset = CalculateUVOffset(source_rect.x(), | |
160 source_rect.y(), | |
161 last_image_->stride[1]); | |
162 ScaleYUVToRGB32(last_image_->planes[0] + y_offset, | |
163 last_image_->planes[1] + uv_offset, | |
164 last_image_->planes[2] + uv_offset, | |
165 image_buffer, | |
166 source_rect.width(), | |
167 source_rect.height(), | |
168 clip_area.width(), | |
169 clip_area.height(), | |
170 last_image_->stride[0], | |
171 last_image_->stride[1], | |
172 image_stride, | |
173 media::YV12, | |
174 media::ROTATE_0, | |
175 media::FILTER_BILINEAR); | |
176 | |
177 output_region->op(clip_area, SkRegion::kUnion_Op); | |
178 updated_region_.op(source_rect, SkRegion::kDifference_Op); | |
179 return; | |
180 } | |
181 | |
182 for (SkRegion::Iterator i(updated_region_); !i.done(); i.next()) { | |
183 // Determine the scaled area affected by this rectangle changing. | |
184 SkIRect rect = i.rect(); | |
185 if (!rect.intersect(source_clip)) | |
186 continue; | |
187 rect = ScaleRect(rect, screen_size_, view_size); | |
188 if (!rect.intersect(clip_area)) | |
189 continue; | |
190 | |
191 ConvertAndScaleYUVToRGB32Rect(last_image_->planes[0], | |
192 last_image_->planes[1], | |
193 last_image_->planes[2], | |
194 last_image_->stride[0], | |
195 last_image_->stride[1], | |
196 screen_size_, | |
197 source_clip, | |
198 image_buffer, | |
199 image_stride, | |
200 view_size, | |
201 clip_area, | |
202 rect); | |
203 | |
204 output_region->op(rect, SkRegion::kUnion_Op); | |
205 } | |
206 | |
207 updated_region_.op(ScaleRect(clip_area, view_size, screen_size_), | |
208 SkRegion::kDifference_Op); | |
209 } | |
210 | |
211 } // namespace remoting | |
OLD | NEW |