OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h" | 5 #include "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/debug/trace_event.h" | 8 #include "base/debug/trace_event.h" |
9 #include "base/memory/aligned_memory.h" | 9 #include "base/memory/aligned_memory.h" |
10 #include "media/base/video_frame.h" | 10 #include "media/base/video_frame.h" |
| 11 #include "media/base/video_frame_pool.h" |
| 12 #include "third_party/libjingle/source/talk/media/base/videoframe.h" |
| 13 #include "third_party/libjingle/source/talk/media/base/videoframefactory.h" |
| 14 #include "third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe.h" |
| 15 #include "third_party/libyuv/include/libyuv/convert_from.h" |
11 #include "third_party/libyuv/include/libyuv/scale.h" | 16 #include "third_party/libyuv/include/libyuv/scale.h" |
12 | 17 |
13 namespace content { | 18 namespace content { |
| 19 namespace { |
| 20 |
| 21 // Empty method used for keeping a reference to the original media::VideoFrame. |
| 22 // The reference to |frame| is kept in the closure that calls this method. |
| 23 void ReleaseOriginalFrame(const scoped_refptr<media::VideoFrame>& frame) { |
| 24 } |
| 25 |
| 26 // Thin map between an existing media::VideoFrame and cricket::VideoFrame to |
| 27 // avoid premature deep copies. |
| 28 // This implementation is only safe to use in a const context and should never |
| 29 // be written to. |
| 30 class VideoFrameWrapper : public cricket::VideoFrame { |
| 31 public: |
| 32 VideoFrameWrapper(const scoped_refptr<media::VideoFrame>& frame, |
| 33 int64 elapsed_time) |
| 34 : frame_(media::VideoFrame::WrapVideoFrame( |
| 35 frame, |
| 36 frame->visible_rect(), |
| 37 frame->natural_size(), |
| 38 base::Bind(&ReleaseOriginalFrame, frame))), |
| 39 elapsed_time_(elapsed_time) {} |
| 40 |
| 41 virtual VideoFrame* Copy() const override { |
| 42 DCHECK(thread_checker_.CalledOnValidThread()); |
| 43 return new VideoFrameWrapper(frame_, elapsed_time_); |
| 44 } |
| 45 |
| 46 virtual size_t GetWidth() const override { |
| 47 DCHECK(thread_checker_.CalledOnValidThread()); |
| 48 return static_cast<size_t>(frame_->visible_rect().width()); |
| 49 } |
| 50 |
| 51 virtual size_t GetHeight() const override { |
| 52 DCHECK(thread_checker_.CalledOnValidThread()); |
| 53 return static_cast<size_t>(frame_->visible_rect().height()); |
| 54 } |
| 55 |
| 56 virtual const uint8* GetYPlane() const override { |
| 57 DCHECK(thread_checker_.CalledOnValidThread()); |
| 58 return frame_->visible_data(media::VideoFrame::kYPlane); |
| 59 } |
| 60 |
| 61 virtual const uint8* GetUPlane() const override { |
| 62 DCHECK(thread_checker_.CalledOnValidThread()); |
| 63 return frame_->visible_data(media::VideoFrame::kUPlane); |
| 64 } |
| 65 |
| 66 virtual const uint8* GetVPlane() const override { |
| 67 DCHECK(thread_checker_.CalledOnValidThread()); |
| 68 return frame_->visible_data(media::VideoFrame::kVPlane); |
| 69 } |
| 70 |
| 71 virtual uint8* GetYPlane() override { |
| 72 DCHECK(thread_checker_.CalledOnValidThread()); |
| 73 return frame_->visible_data(media::VideoFrame::kYPlane); |
| 74 } |
| 75 |
| 76 virtual uint8* GetUPlane() override { |
| 77 DCHECK(thread_checker_.CalledOnValidThread()); |
| 78 return frame_->visible_data(media::VideoFrame::kUPlane); |
| 79 } |
| 80 |
| 81 virtual uint8* GetVPlane() override { |
| 82 DCHECK(thread_checker_.CalledOnValidThread()); |
| 83 return frame_->visible_data(media::VideoFrame::kVPlane); |
| 84 } |
| 85 |
| 86 virtual int32 GetYPitch() const override { |
| 87 DCHECK(thread_checker_.CalledOnValidThread()); |
| 88 return frame_->stride(media::VideoFrame::kYPlane); |
| 89 } |
| 90 |
| 91 virtual int32 GetUPitch() const override { |
| 92 DCHECK(thread_checker_.CalledOnValidThread()); |
| 93 return frame_->stride(media::VideoFrame::kUPlane); |
| 94 } |
| 95 |
| 96 virtual int32 GetVPitch() const override { |
| 97 DCHECK(thread_checker_.CalledOnValidThread()); |
| 98 return frame_->stride(media::VideoFrame::kVPlane); |
| 99 } |
| 100 |
| 101 virtual void* GetNativeHandle() const override { |
| 102 DCHECK(thread_checker_.CalledOnValidThread()); |
| 103 return NULL; |
| 104 } |
| 105 |
| 106 virtual size_t GetPixelWidth() const override { |
| 107 DCHECK(thread_checker_.CalledOnValidThread()); |
| 108 return 1; |
| 109 } |
| 110 virtual size_t GetPixelHeight() const override { |
| 111 DCHECK(thread_checker_.CalledOnValidThread()); |
| 112 return 1; |
| 113 } |
| 114 |
| 115 virtual int64 GetElapsedTime() const override { |
| 116 DCHECK(thread_checker_.CalledOnValidThread()); |
| 117 return elapsed_time_; |
| 118 } |
| 119 |
| 120 virtual int64 GetTimeStamp() const override { |
| 121 DCHECK(thread_checker_.CalledOnValidThread()); |
| 122 return frame_->timestamp().InMicroseconds() * |
| 123 base::Time::kNanosecondsPerMicrosecond; |
| 124 } |
| 125 |
| 126 virtual void SetElapsedTime(int64 elapsed_time) override { |
| 127 DCHECK(thread_checker_.CalledOnValidThread()); |
| 128 elapsed_time_ = elapsed_time; |
| 129 } |
| 130 |
| 131 virtual void SetTimeStamp(int64 time_stamp) override { |
| 132 DCHECK(thread_checker_.CalledOnValidThread()); |
| 133 // Round to closest microsecond. |
| 134 frame_->set_timestamp(base::TimeDelta::FromMicroseconds( |
| 135 (time_stamp + base::Time::kNanosecondsPerMicrosecond / 2) / |
| 136 base::Time::kNanosecondsPerMicrosecond)); |
| 137 } |
| 138 |
| 139 virtual int GetRotation() const override { |
| 140 DCHECK(thread_checker_.CalledOnValidThread()); |
| 141 return 0; |
| 142 } |
| 143 |
| 144 // TODO(magjed): Refactor into base class. |
| 145 virtual size_t ConvertToRgbBuffer(uint32 to_fourcc, |
| 146 uint8* buffer, |
| 147 size_t size, |
| 148 int stride_rgb) const override { |
| 149 DCHECK(thread_checker_.CalledOnValidThread()); |
| 150 const size_t needed = std::abs(stride_rgb) * GetHeight(); |
| 151 if (size < needed) { |
| 152 DLOG(WARNING) << "RGB buffer is not large enough"; |
| 153 return needed; |
| 154 } |
| 155 |
| 156 if (libyuv::ConvertFromI420(GetYPlane(), |
| 157 GetYPitch(), |
| 158 GetUPlane(), |
| 159 GetUPitch(), |
| 160 GetVPlane(), |
| 161 GetVPitch(), |
| 162 buffer, |
| 163 stride_rgb, |
| 164 static_cast<int>(GetWidth()), |
| 165 static_cast<int>(GetHeight()), |
| 166 to_fourcc)) { |
| 167 DLOG(ERROR) << "RGB type not supported: " << to_fourcc; |
| 168 return 0; // 0 indicates error |
| 169 } |
| 170 return needed; |
| 171 } |
| 172 |
| 173 // The rest of the public methods are NOTIMPLEMENTED. |
| 174 virtual bool InitToBlack(int w, |
| 175 int h, |
| 176 size_t pixel_width, |
| 177 size_t pixel_height, |
| 178 int64 elapsed_time, |
| 179 int64 time_stamp) override { |
| 180 NOTIMPLEMENTED(); |
| 181 return false; |
| 182 } |
| 183 |
| 184 virtual bool Reset(uint32 fourcc, |
| 185 int w, |
| 186 int h, |
| 187 int dw, |
| 188 int dh, |
| 189 uint8* sample, |
| 190 size_t sample_size, |
| 191 size_t pixel_width, |
| 192 size_t pixel_height, |
| 193 int64 elapsed_time, |
| 194 int64 time_stamp, |
| 195 int rotation) override { |
| 196 NOTIMPLEMENTED(); |
| 197 return false; |
| 198 } |
| 199 |
| 200 virtual bool MakeExclusive() override { |
| 201 NOTIMPLEMENTED(); |
| 202 return false; |
| 203 } |
| 204 |
| 205 virtual size_t CopyToBuffer(uint8* buffer, size_t size) const override { |
| 206 NOTIMPLEMENTED(); |
| 207 return 0; |
| 208 } |
| 209 |
| 210 protected: |
| 211 // TODO(magjed): Refactor as a static method in WebRtcVideoFrame. |
| 212 virtual VideoFrame* CreateEmptyFrame(int w, |
| 213 int h, |
| 214 size_t pixel_width, |
| 215 size_t pixel_height, |
| 216 int64 elapsed_time, |
| 217 int64 time_stamp) const override { |
| 218 DCHECK(thread_checker_.CalledOnValidThread()); |
| 219 VideoFrame* frame = new cricket::WebRtcVideoFrame(); |
| 220 frame->InitToBlack( |
| 221 w, h, pixel_width, pixel_height, elapsed_time, time_stamp); |
| 222 return frame; |
| 223 } |
| 224 |
| 225 private: |
| 226 scoped_refptr<media::VideoFrame> frame_; |
| 227 int64 elapsed_time_; |
| 228 base::ThreadChecker thread_checker_; |
| 229 }; |
| 230 |
| 231 } // anonymous namespace |
| 232 |
| 233 // A cricket::VideoFrameFactory for media::VideoFrame. The purpose of this |
| 234 // class is to avoid a premature frame copy. A media::VideoFrame is injected |
| 235 // with SetFrame, and converted into a cricket::VideoFrame with |
| 236 // CreateAliasedFrame. SetFrame should be called before CreateAliasedFrame |
| 237 // for every frame. |
| 238 class WebRtcVideoCapturerAdapter::MediaVideoFrameFactory |
| 239 : public cricket::VideoFrameFactory { |
| 240 public: |
| 241 void SetFrame(const scoped_refptr<media::VideoFrame>& frame, |
| 242 int64_t elapsed_time) { |
| 243 DCHECK(frame.get()); |
| 244 // Create a CapturedFrame that only contains header information, not the |
| 245 // actual pixel data. |
| 246 captured_frame_.width = frame->natural_size().width(); |
| 247 captured_frame_.height = frame->natural_size().height(); |
| 248 captured_frame_.elapsed_time = elapsed_time; |
| 249 captured_frame_.time_stamp = frame->timestamp().InMicroseconds() * |
| 250 base::Time::kNanosecondsPerMicrosecond; |
| 251 captured_frame_.pixel_height = 1; |
| 252 captured_frame_.pixel_width = 1; |
| 253 captured_frame_.rotation = 0; |
| 254 captured_frame_.data = NULL; |
| 255 captured_frame_.data_size = cricket::CapturedFrame::kUnknownDataSize; |
| 256 captured_frame_.fourcc = static_cast<uint32>(cricket::FOURCC_ANY); |
| 257 |
| 258 frame_ = frame; |
| 259 } |
| 260 |
| 261 void ReleaseFrame() { frame_ = NULL; } |
| 262 |
| 263 const cricket::CapturedFrame* GetCapturedFrame() const { |
| 264 return &captured_frame_; |
| 265 } |
| 266 |
| 267 virtual cricket::VideoFrame* CreateAliasedFrame( |
| 268 const cricket::CapturedFrame* captured_frame, |
| 269 int dst_width, |
| 270 int dst_height) const override { |
| 271 // Check that captured_frame is actually our frame. |
| 272 DCHECK(captured_frame == &captured_frame_); |
| 273 DCHECK(frame_.get()); |
| 274 |
| 275 scoped_refptr<media::VideoFrame> video_frame = frame_; |
| 276 // Check if scaling is needed. |
| 277 if (dst_width != frame_->visible_rect().width() || |
| 278 dst_height != frame_->visible_rect().height()) { |
| 279 video_frame = |
| 280 scaled_frame_pool_.CreateFrame(media::VideoFrame::I420, |
| 281 gfx::Size(dst_width, dst_height), |
| 282 gfx::Rect(0, 0, dst_width, dst_height), |
| 283 gfx::Size(dst_width, dst_height), |
| 284 frame_->timestamp()); |
| 285 libyuv::I420Scale(frame_->visible_data(media::VideoFrame::kYPlane), |
| 286 frame_->stride(media::VideoFrame::kYPlane), |
| 287 frame_->visible_data(media::VideoFrame::kUPlane), |
| 288 frame_->stride(media::VideoFrame::kUPlane), |
| 289 frame_->visible_data(media::VideoFrame::kVPlane), |
| 290 frame_->stride(media::VideoFrame::kVPlane), |
| 291 frame_->visible_rect().width(), |
| 292 frame_->visible_rect().height(), |
| 293 video_frame->data(media::VideoFrame::kYPlane), |
| 294 video_frame->stride(media::VideoFrame::kYPlane), |
| 295 video_frame->data(media::VideoFrame::kUPlane), |
| 296 video_frame->stride(media::VideoFrame::kUPlane), |
| 297 video_frame->data(media::VideoFrame::kVPlane), |
| 298 video_frame->stride(media::VideoFrame::kVPlane), |
| 299 dst_width, |
| 300 dst_height, |
| 301 libyuv::kFilterBilinear); |
| 302 } |
| 303 |
| 304 // Create a shallow cricket::VideoFrame wrapper around the |
| 305 // media::VideoFrame. The caller has ownership of the returned frame. |
| 306 return new VideoFrameWrapper(video_frame, captured_frame_.elapsed_time); |
| 307 } |
| 308 |
| 309 private: |
| 310 scoped_refptr<media::VideoFrame> frame_; |
| 311 cricket::CapturedFrame captured_frame_; |
| 312 // This is used only if scaling is needed. |
| 313 mutable media::VideoFramePool scaled_frame_pool_; |
| 314 }; |
14 | 315 |
15 WebRtcVideoCapturerAdapter::WebRtcVideoCapturerAdapter(bool is_screencast) | 316 WebRtcVideoCapturerAdapter::WebRtcVideoCapturerAdapter(bool is_screencast) |
16 : is_screencast_(is_screencast), | 317 : is_screencast_(is_screencast), |
17 running_(false), | 318 running_(false), |
18 buffer_(NULL), | 319 first_frame_timestamp_(media::kNoTimestamp()), |
19 buffer_size_(0) { | 320 frame_factory_(new MediaVideoFrameFactory) { |
20 thread_checker_.DetachFromThread(); | 321 thread_checker_.DetachFromThread(); |
| 322 // The base class takes ownership of the frame factory. |
| 323 set_frame_factory(frame_factory_); |
21 } | 324 } |
22 | 325 |
23 WebRtcVideoCapturerAdapter::~WebRtcVideoCapturerAdapter() { | 326 WebRtcVideoCapturerAdapter::~WebRtcVideoCapturerAdapter() { |
24 DVLOG(3) << " WebRtcVideoCapturerAdapter::dtor"; | 327 DVLOG(3) << " WebRtcVideoCapturerAdapter::dtor"; |
25 base::AlignedFree(buffer_); | |
26 } | 328 } |
27 | 329 |
28 cricket::CaptureState WebRtcVideoCapturerAdapter::Start( | 330 cricket::CaptureState WebRtcVideoCapturerAdapter::Start( |
29 const cricket::VideoFormat& capture_format) { | 331 const cricket::VideoFormat& capture_format) { |
30 DCHECK(thread_checker_.CalledOnValidThread()); | 332 DCHECK(thread_checker_.CalledOnValidThread()); |
31 DCHECK(!running_); | 333 DCHECK(!running_); |
32 DVLOG(3) << " WebRtcVideoCapturerAdapter::Start w = " << capture_format.width | 334 DVLOG(3) << " WebRtcVideoCapturerAdapter::Start w = " << capture_format.width |
33 << " h = " << capture_format.height; | 335 << " h = " << capture_format.height; |
34 | 336 |
35 running_ = true; | 337 running_ = true; |
(...skipping 10 matching lines...) Expand all Loading... |
46 } | 348 } |
47 | 349 |
48 bool WebRtcVideoCapturerAdapter::IsRunning() { | 350 bool WebRtcVideoCapturerAdapter::IsRunning() { |
49 DCHECK(thread_checker_.CalledOnValidThread()); | 351 DCHECK(thread_checker_.CalledOnValidThread()); |
50 return running_; | 352 return running_; |
51 } | 353 } |
52 | 354 |
53 bool WebRtcVideoCapturerAdapter::GetPreferredFourccs( | 355 bool WebRtcVideoCapturerAdapter::GetPreferredFourccs( |
54 std::vector<uint32>* fourccs) { | 356 std::vector<uint32>* fourccs) { |
55 DCHECK(thread_checker_.CalledOnValidThread()); | 357 DCHECK(thread_checker_.CalledOnValidThread()); |
56 if (!fourccs) | 358 DCHECK(!fourccs || fourccs->empty()); |
57 return false; | 359 if (fourccs) |
58 fourccs->push_back(cricket::FOURCC_I420); | 360 fourccs->push_back(cricket::FOURCC_I420); |
59 return true; | 361 return fourccs != NULL; |
60 } | 362 } |
61 | 363 |
62 bool WebRtcVideoCapturerAdapter::IsScreencast() const { | 364 bool WebRtcVideoCapturerAdapter::IsScreencast() const { |
63 return is_screencast_; | 365 return is_screencast_; |
64 } | 366 } |
65 | 367 |
66 bool WebRtcVideoCapturerAdapter::GetBestCaptureFormat( | 368 bool WebRtcVideoCapturerAdapter::GetBestCaptureFormat( |
67 const cricket::VideoFormat& desired, | 369 const cricket::VideoFormat& desired, |
68 cricket::VideoFormat* best_format) { | 370 cricket::VideoFormat* best_format) { |
69 DCHECK(thread_checker_.CalledOnValidThread()); | 371 DCHECK(thread_checker_.CalledOnValidThread()); |
(...skipping 20 matching lines...) Expand all Loading... |
90 // Some types of sources support textures as output. Since connecting | 392 // Some types of sources support textures as output. Since connecting |
91 // sources and sinks do not check the format, we need to just ignore | 393 // sources and sinks do not check the format, we need to just ignore |
92 // formats that we can not handle. | 394 // formats that we can not handle. |
93 NOTREACHED(); | 395 NOTREACHED(); |
94 return; | 396 return; |
95 } | 397 } |
96 | 398 |
97 if (first_frame_timestamp_ == media::kNoTimestamp()) | 399 if (first_frame_timestamp_ == media::kNoTimestamp()) |
98 first_frame_timestamp_ = frame->timestamp(); | 400 first_frame_timestamp_ = frame->timestamp(); |
99 | 401 |
100 cricket::CapturedFrame captured_frame; | 402 const int64 elapsed_time = |
101 captured_frame.width = frame->natural_size().width(); | |
102 captured_frame.height = frame->natural_size().height(); | |
103 // cricket::CapturedFrame time is in nanoseconds. | |
104 captured_frame.elapsed_time = | |
105 (frame->timestamp() - first_frame_timestamp_).InMicroseconds() * | 403 (frame->timestamp() - first_frame_timestamp_).InMicroseconds() * |
106 base::Time::kNanosecondsPerMicrosecond; | 404 base::Time::kNanosecondsPerMicrosecond; |
107 captured_frame.time_stamp = frame->timestamp().InMicroseconds() * | |
108 base::Time::kNanosecondsPerMicrosecond; | |
109 captured_frame.pixel_height = 1; | |
110 captured_frame.pixel_width = 1; | |
111 | 405 |
112 // TODO(perkj): | 406 // Inject the frame via the VideoFrameFractory. |
113 // Libjingle expects contiguous layout of image planes as input. | 407 DCHECK(frame_factory_ == frame_factory()); |
114 // The only format where that is true in Chrome is I420 where the | 408 frame_factory_->SetFrame(frame, elapsed_time); |
115 // coded_size == natural_size(). | |
116 if (frame->format() != media::VideoFrame::I420 || | |
117 frame->coded_size() != frame->natural_size()) { | |
118 // Cropping / Scaling and or switching UV planes is needed. | |
119 UpdateI420Buffer(frame); | |
120 captured_frame.data = buffer_; | |
121 captured_frame.data_size = buffer_size_; | |
122 captured_frame.fourcc = cricket::FOURCC_I420; | |
123 } else { | |
124 captured_frame.fourcc = media::VideoFrame::I420 == frame->format() ? | |
125 cricket::FOURCC_I420 : cricket::FOURCC_YV12; | |
126 captured_frame.data = frame->data(0); | |
127 captured_frame.data_size = | |
128 media::VideoFrame::AllocationSize(frame->format(), frame->coded_size()); | |
129 } | |
130 | 409 |
131 // This signals to libJingle that a new VideoFrame is available. | 410 // This signals to libJingle that a new VideoFrame is available. |
132 // libJingle have no assumptions on what thread this signal come from. | 411 SignalFrameCaptured(this, frame_factory_->GetCapturedFrame()); |
133 SignalFrameCaptured(this, &captured_frame); | |
134 } | |
135 | 412 |
136 void WebRtcVideoCapturerAdapter::UpdateI420Buffer( | 413 frame_factory_->ReleaseFrame(); // Release the frame ASAP. |
137 const scoped_refptr<media::VideoFrame>& src) { | |
138 DCHECK(thread_checker_.CalledOnValidThread()); | |
139 const int dst_width = src->natural_size().width(); | |
140 const int dst_height = src->natural_size().height(); | |
141 DCHECK(src->visible_rect().width() >= dst_width && | |
142 src->visible_rect().height() >= dst_height); | |
143 | |
144 const gfx::Rect& visible_rect = src->visible_rect(); | |
145 | |
146 const uint8* src_y = src->data(media::VideoFrame::kYPlane) + | |
147 visible_rect.y() * src->stride(media::VideoFrame::kYPlane) + | |
148 visible_rect.x(); | |
149 const uint8* src_u = src->data(media::VideoFrame::kUPlane) + | |
150 visible_rect.y() / 2 * src->stride(media::VideoFrame::kUPlane) + | |
151 visible_rect.x() / 2; | |
152 const uint8* src_v = src->data(media::VideoFrame::kVPlane) + | |
153 visible_rect.y() / 2 * src->stride(media::VideoFrame::kVPlane) + | |
154 visible_rect.x() / 2; | |
155 | |
156 const size_t dst_size = | |
157 media::VideoFrame::AllocationSize(src->format(), src->natural_size()); | |
158 | |
159 if (dst_size != buffer_size_) { | |
160 base::AlignedFree(buffer_); | |
161 buffer_ = reinterpret_cast<uint8*>( | |
162 base::AlignedAlloc(dst_size + media::VideoFrame::kFrameSizePadding, | |
163 media::VideoFrame::kFrameAddressAlignment)); | |
164 buffer_size_ = dst_size; | |
165 } | |
166 | |
167 uint8* dst_y = buffer_; | |
168 const int dst_stride_y = dst_width; | |
169 uint8* dst_u = dst_y + dst_width * dst_height; | |
170 const int dst_halfwidth = (dst_width + 1) / 2; | |
171 const int dst_halfheight = (dst_height + 1) / 2; | |
172 uint8* dst_v = dst_u + dst_halfwidth * dst_halfheight; | |
173 | |
174 libyuv::I420Scale(src_y, | |
175 src->stride(media::VideoFrame::kYPlane), | |
176 src_u, | |
177 src->stride(media::VideoFrame::kUPlane), | |
178 src_v, | |
179 src->stride(media::VideoFrame::kVPlane), | |
180 visible_rect.width(), | |
181 visible_rect.height(), | |
182 dst_y, | |
183 dst_stride_y, | |
184 dst_u, | |
185 dst_halfwidth, | |
186 dst_v, | |
187 dst_halfwidth, | |
188 dst_width, | |
189 dst_height, | |
190 libyuv::kFilterBilinear); | |
191 } | 414 } |
192 | 415 |
193 } // namespace content | 416 } // namespace content |
OLD | NEW |