Chromium Code Reviews| 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 "third_party/libyuv/include/libyuv/scale.h" | 11 #include "third_party/libjingle/source/talk/media/base/videoframe.h" |
| 12 #include "third_party/libjingle/source/talk/media/webrtc/webrtcvideoframe.h" | |
| 13 #include "third_party/libyuv/include/libyuv/convert_from.h" | |
| 14 | |
| 15 // Empty method used for keeping a reference to the original media::VideoFrame. | |
| 16 // The reference to |frame| is kept in the closure that calls this method. | |
| 17 void ReleaseOriginalFrame(const scoped_refptr<media::VideoFrame>& frame) { | |
| 18 } | |
| 19 | |
| 20 // Thin map between an existing media::VideoFrame and cricket::VideoFrame to | |
| 21 // avoid premature deep copies. | |
| 22 // This implementation is only safe to use in a const context and should never | |
| 23 // be written to. | |
| 24 class VideoFrameWrapper : public cricket::VideoFrame { | |
|
tommi (sloooow) - chröme
2014/09/25 20:06:48
Assuming this is a single threaded class, can we a
magjed_chromium
2014/09/25 20:52:08
Yes, will fix.
| |
| 25 public: | |
| 26 VideoFrameWrapper(const scoped_refptr<media::VideoFrame>& frame, | |
| 27 int64 elapsed_time) | |
| 28 : frame_(media::VideoFrame::WrapVideoFrame( | |
| 29 frame, | |
| 30 frame->visible_rect(), | |
| 31 frame->natural_size(), | |
| 32 base::Bind(&ReleaseOriginalFrame, frame))), | |
| 33 elapsed_time_(elapsed_time) {} | |
| 34 | |
| 35 virtual VideoFrame* Copy() const OVERRIDE { | |
| 36 return new VideoFrameWrapper(frame_, elapsed_time_); | |
| 37 } | |
| 38 | |
| 39 virtual size_t GetWidth() const OVERRIDE { | |
| 40 return static_cast<size_t>(frame_->visible_rect().width()); | |
| 41 } | |
| 42 | |
| 43 virtual size_t GetHeight() const OVERRIDE { | |
| 44 return static_cast<size_t>(frame_->visible_rect().height()); | |
| 45 } | |
| 46 | |
| 47 virtual const uint8* GetYPlane() const OVERRIDE { | |
| 48 return frame_->visible_data(media::VideoFrame::kYPlane); | |
| 49 } | |
| 50 | |
| 51 virtual const uint8* GetUPlane() const OVERRIDE { | |
| 52 return frame_->visible_data(media::VideoFrame::kUPlane); | |
| 53 } | |
| 54 | |
| 55 virtual const uint8* GetVPlane() const OVERRIDE { | |
| 56 return frame_->visible_data(media::VideoFrame::kVPlane); | |
| 57 } | |
| 58 | |
| 59 virtual uint8* GetYPlane() OVERRIDE { | |
| 60 return frame_->visible_data(media::VideoFrame::kYPlane); | |
| 61 } | |
| 62 | |
| 63 virtual uint8* GetUPlane() OVERRIDE { | |
| 64 return frame_->visible_data(media::VideoFrame::kUPlane); | |
| 65 } | |
| 66 | |
| 67 virtual uint8* GetVPlane() OVERRIDE { | |
| 68 return frame_->visible_data(media::VideoFrame::kVPlane); | |
| 69 } | |
| 70 | |
| 71 virtual int32 GetYPitch() const OVERRIDE { | |
| 72 return frame_->stride(media::VideoFrame::kYPlane); | |
| 73 } | |
| 74 | |
| 75 virtual int32 GetUPitch() const OVERRIDE { | |
| 76 return frame_->stride(media::VideoFrame::kUPlane); | |
| 77 } | |
| 78 | |
| 79 virtual int32 GetVPitch() const OVERRIDE { | |
| 80 return frame_->stride(media::VideoFrame::kVPlane); | |
| 81 } | |
| 82 | |
| 83 virtual void* GetNativeHandle() const OVERRIDE { return NULL; } | |
| 84 virtual size_t GetPixelWidth() const OVERRIDE { return 1; } | |
| 85 virtual size_t GetPixelHeight() const OVERRIDE { return 1; } | |
| 86 | |
| 87 virtual int64 GetElapsedTime() const OVERRIDE { return elapsed_time_; } | |
| 88 | |
| 89 virtual int64 GetTimeStamp() const OVERRIDE { | |
| 90 return frame_->timestamp().InMicroseconds() * | |
| 91 base::Time::kNanosecondsPerMicrosecond; | |
| 92 } | |
| 93 | |
| 94 virtual void SetElapsedTime(int64 elapsed_time) OVERRIDE { | |
| 95 elapsed_time_ = elapsed_time; | |
| 96 } | |
| 97 | |
| 98 virtual void SetTimeStamp(int64 time_stamp) OVERRIDE { | |
| 99 // Round to closest microsecond. | |
| 100 frame_->set_timestamp(base::TimeDelta::FromMicroseconds( | |
| 101 (time_stamp + base::Time::kNanosecondsPerMicrosecond / 2) / | |
| 102 base::Time::kNanosecondsPerMicrosecond)); | |
| 103 } | |
| 104 | |
| 105 virtual int GetRotation() const OVERRIDE { return 0; } | |
| 106 | |
| 107 // TODO(magjed): Refactor into base class | |
| 108 virtual size_t ConvertToRgbBuffer(uint32 to_fourcc, | |
| 109 uint8* buffer, | |
| 110 size_t size, | |
| 111 int stride_rgb) const OVERRIDE { | |
| 112 const size_t needed = std::abs(stride_rgb) * GetHeight(); | |
| 113 if (size < needed) { | |
| 114 DLOG(WARNING) << "RGB buffer is not large enough"; | |
| 115 return needed; | |
| 116 } | |
| 117 | |
| 118 if (libyuv::ConvertFromI420(GetYPlane(), | |
| 119 GetYPitch(), | |
| 120 GetUPlane(), | |
| 121 GetUPitch(), | |
| 122 GetVPlane(), | |
| 123 GetVPitch(), | |
| 124 buffer, | |
| 125 stride_rgb, | |
| 126 static_cast<int>(GetWidth()), | |
| 127 static_cast<int>(GetHeight()), | |
| 128 to_fourcc)) { | |
| 129 DLOG(ERROR) << "RGB type not supported: " << to_fourcc; | |
| 130 return 0; // 0 indicates error | |
| 131 } | |
| 132 return needed; | |
| 133 } | |
| 134 | |
| 135 // The rest of the public methods are NOTIMPLEMENTED. | |
| 136 virtual bool InitToBlack(int w, | |
| 137 int h, | |
| 138 size_t pixel_width, | |
| 139 size_t pixel_height, | |
| 140 int64 elapsed_time, | |
| 141 int64 time_stamp) OVERRIDE { | |
| 142 NOTIMPLEMENTED(); | |
| 143 return false; | |
| 144 } | |
| 145 | |
| 146 virtual bool Reset(uint32 fourcc, | |
| 147 int w, | |
| 148 int h, | |
| 149 int dw, | |
| 150 int dh, | |
| 151 uint8* sample, | |
| 152 size_t sample_size, | |
| 153 size_t pixel_width, | |
| 154 size_t pixel_height, | |
| 155 int64 elapsed_time, | |
| 156 int64 time_stamp, | |
| 157 int rotation) OVERRIDE { | |
| 158 NOTIMPLEMENTED(); | |
| 159 return false; | |
| 160 } | |
| 161 | |
| 162 virtual bool MakeExclusive() OVERRIDE { | |
| 163 NOTIMPLEMENTED(); | |
| 164 return false; | |
| 165 } | |
| 166 | |
| 167 virtual size_t CopyToBuffer(uint8* buffer, size_t size) const OVERRIDE { | |
| 168 NOTIMPLEMENTED(); | |
| 169 return 0; | |
| 170 } | |
| 171 | |
| 172 protected: | |
| 173 // TODO(magjed): Refactor as a static method in WebRtcVideoFrame | |
| 174 virtual VideoFrame* CreateEmptyFrame(int w, | |
| 175 int h, | |
| 176 size_t pixel_width, | |
| 177 size_t pixel_height, | |
| 178 int64 elapsed_time, | |
| 179 int64 time_stamp) const OVERRIDE { | |
| 180 VideoFrame* frame = new cricket::WebRtcVideoFrame(); | |
| 181 frame->InitToBlack( | |
| 182 w, h, pixel_width, pixel_height, elapsed_time, time_stamp); | |
| 183 return frame; | |
| 184 } | |
| 185 | |
| 186 private: | |
| 187 scoped_refptr<media::VideoFrame> frame_; | |
| 188 int64 elapsed_time_; | |
| 189 }; | |
| 190 | |
| 191 class DummyFactory : public cricket::VideoFrameFactory { | |
|
tommi (sloooow) - chröme
2014/09/25 20:06:48
can you add documentation for what context this cl
magjed_chromium
2014/09/25 20:52:08
The context for VideoFrameWrapper and DummyFactory
| |
| 192 public: | |
| 193 DummyFactory(const scoped_refptr<media::VideoFrame>& frame, | |
| 194 int64_t elapsed_time) | |
| 195 : frame_(frame), elapsed_time_(elapsed_time) {} | |
| 196 | |
| 197 virtual cricket::VideoFrame* CreateAliasedFrame(const cricket::CapturedFrame*, | |
| 198 int, | |
| 199 int) const OVERRIDE { | |
| 200 // Create a shallow cricket::VideoFrame wrapper around the | |
| 201 // media::VideoFrame. The caller has ownership over the returned frame. | |
| 202 return new VideoFrameWrapper(frame_, elapsed_time_); | |
| 203 } | |
| 204 | |
| 205 private: | |
| 206 const scoped_refptr<media::VideoFrame>& frame_; | |
|
tommi (sloooow) - chröme
2014/09/25 20:06:48
is the reference intentional?
I'd think it'd be s
magjed_chromium
2014/09/25 20:52:08
Ops, that is not intentional. Will fix.
| |
| 207 const int64_t elapsed_time_; | |
| 208 }; | |
| 12 | 209 |
| 13 namespace content { | 210 namespace content { |
| 14 | 211 |
| 15 WebRtcVideoCapturerAdapter::WebRtcVideoCapturerAdapter(bool is_screencast) | 212 WebRtcVideoCapturerAdapter::WebRtcVideoCapturerAdapter(bool is_screencast) |
| 16 : is_screencast_(is_screencast), | 213 : is_screencast_(is_screencast), running_(false) { |
| 17 running_(false), | |
| 18 buffer_(NULL), | |
| 19 buffer_size_(0) { | |
| 20 thread_checker_.DetachFromThread(); | 214 thread_checker_.DetachFromThread(); |
| 21 } | 215 } |
| 22 | 216 |
| 23 WebRtcVideoCapturerAdapter::~WebRtcVideoCapturerAdapter() { | 217 WebRtcVideoCapturerAdapter::~WebRtcVideoCapturerAdapter() { |
| 24 DVLOG(3) << " WebRtcVideoCapturerAdapter::dtor"; | 218 DVLOG(3) << " WebRtcVideoCapturerAdapter::dtor"; |
| 25 base::AlignedFree(buffer_); | |
| 26 } | 219 } |
| 27 | 220 |
| 28 cricket::CaptureState WebRtcVideoCapturerAdapter::Start( | 221 cricket::CaptureState WebRtcVideoCapturerAdapter::Start( |
| 29 const cricket::VideoFormat& capture_format) { | 222 const cricket::VideoFormat& capture_format) { |
| 30 DCHECK(thread_checker_.CalledOnValidThread()); | 223 DCHECK(thread_checker_.CalledOnValidThread()); |
| 31 DCHECK(!running_); | 224 DCHECK(!running_); |
| 32 DVLOG(3) << " WebRtcVideoCapturerAdapter::Start w = " << capture_format.width | 225 DVLOG(3) << " WebRtcVideoCapturerAdapter::Start w = " << capture_format.width |
| 33 << " h = " << capture_format.height; | 226 << " h = " << capture_format.height; |
| 34 | 227 |
| 35 running_ = true; | 228 running_ = true; |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 46 } | 239 } |
| 47 | 240 |
| 48 bool WebRtcVideoCapturerAdapter::IsRunning() { | 241 bool WebRtcVideoCapturerAdapter::IsRunning() { |
| 49 DCHECK(thread_checker_.CalledOnValidThread()); | 242 DCHECK(thread_checker_.CalledOnValidThread()); |
| 50 return running_; | 243 return running_; |
| 51 } | 244 } |
| 52 | 245 |
| 53 bool WebRtcVideoCapturerAdapter::GetPreferredFourccs( | 246 bool WebRtcVideoCapturerAdapter::GetPreferredFourccs( |
| 54 std::vector<uint32>* fourccs) { | 247 std::vector<uint32>* fourccs) { |
| 55 DCHECK(thread_checker_.CalledOnValidThread()); | 248 DCHECK(thread_checker_.CalledOnValidThread()); |
| 56 if (!fourccs) | 249 if (fourccs) |
|
tommi (sloooow) - chröme
2014/09/25 20:06:48
Does it make sense to call this function with a NU
magjed_chromium
2014/09/25 20:52:08
We only have one real call to this function in Chr
| |
| 57 return false; | 250 fourccs->push_back(cricket::FOURCC_I420); |
| 58 fourccs->push_back(cricket::FOURCC_I420); | 251 return fourccs; |
|
tommi (sloooow) - chröme
2014/09/25 20:06:48
hm... does this compile? :)
I'm guessing you'd wa
magjed_chromium
2014/09/25 20:52:08
Yes, sorry about that. It compiled locally.
| |
| 59 return true; | |
| 60 } | 252 } |
| 61 | 253 |
| 62 bool WebRtcVideoCapturerAdapter::IsScreencast() const { | 254 bool WebRtcVideoCapturerAdapter::IsScreencast() const { |
| 63 return is_screencast_; | 255 return is_screencast_; |
| 64 } | 256 } |
| 65 | 257 |
| 66 bool WebRtcVideoCapturerAdapter::GetBestCaptureFormat( | 258 bool WebRtcVideoCapturerAdapter::GetBestCaptureFormat( |
| 67 const cricket::VideoFormat& desired, | 259 const cricket::VideoFormat& desired, |
| 68 cricket::VideoFormat* best_format) { | 260 cricket::VideoFormat* best_format) { |
| 69 DCHECK(thread_checker_.CalledOnValidThread()); | 261 DCHECK(thread_checker_.CalledOnValidThread()); |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 90 // Some types of sources support textures as output. Since connecting | 282 // Some types of sources support textures as output. Since connecting |
| 91 // sources and sinks do not check the format, we need to just ignore | 283 // sources and sinks do not check the format, we need to just ignore |
| 92 // formats that we can not handle. | 284 // formats that we can not handle. |
| 93 NOTREACHED(); | 285 NOTREACHED(); |
| 94 return; | 286 return; |
| 95 } | 287 } |
| 96 | 288 |
| 97 if (first_frame_timestamp_ == media::kNoTimestamp()) | 289 if (first_frame_timestamp_ == media::kNoTimestamp()) |
| 98 first_frame_timestamp_ = frame->timestamp(); | 290 first_frame_timestamp_ = frame->timestamp(); |
| 99 | 291 |
| 100 cricket::CapturedFrame captured_frame; | 292 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() * | 293 (frame->timestamp() - first_frame_timestamp_).InMicroseconds() * |
| 106 base::Time::kNanosecondsPerMicrosecond; | 294 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 | 295 |
| 112 // TODO(perkj): | 296 // Create a CapturedFrame that only contains header information, not the |
| 113 // Libjingle expects contiguous layout of image planes as input. | 297 // actual pixel data. The purpose of this charade is to satisfy the interface |
| 114 // The only format where that is true in Chrome is I420 where the | 298 // of cricket::VideoCapturer. We will inject the real cricket::VideoFrame with |
| 115 // coded_size == natural_size(). | 299 // a custom VideoFrameFactory instead. |
| 116 if (frame->format() != media::VideoFrame::I420 || | 300 cricket::CapturedFrame dummy_captured_frame; |
| 117 frame->coded_size() != frame->natural_size()) { | 301 dummy_captured_frame.width = frame->visible_rect().width(); |
| 118 // Cropping / Scaling and or switching UV planes is needed. | 302 dummy_captured_frame.height = frame->visible_rect().height(); |
| 119 UpdateI420Buffer(frame); | 303 dummy_captured_frame.elapsed_time = elapsed_time; |
| 120 captured_frame.data = buffer_; | 304 dummy_captured_frame.time_stamp = frame->timestamp().InMicroseconds() * |
| 121 captured_frame.data_size = buffer_size_; | 305 base::Time::kNanosecondsPerMicrosecond; |
| 122 captured_frame.fourcc = cricket::FOURCC_I420; | 306 dummy_captured_frame.pixel_height = 1; |
| 123 } else { | 307 dummy_captured_frame.pixel_width = 1; |
| 124 captured_frame.fourcc = media::VideoFrame::I420 == frame->format() ? | 308 dummy_captured_frame.rotation = 0; |
| 125 cricket::FOURCC_I420 : cricket::FOURCC_YV12; | 309 dummy_captured_frame.data = NULL; |
| 126 captured_frame.data = frame->data(0); | 310 dummy_captured_frame.data_size = cricket::CapturedFrame::kUnknownDataSize; |
| 127 captured_frame.data_size = | 311 dummy_captured_frame.fourcc = cricket::FOURCC_ANY; |
| 128 media::VideoFrame::AllocationSize(frame->format(), frame->coded_size()); | 312 |
| 129 } | 313 // Inject the frame via the VideoFrameFractory. |
| 314 set_frame_factory(new DummyFactory(frame, elapsed_time)); | |
|
tommi (sloooow) - chröme
2014/09/25 20:06:48
does set_frame_factory accept a scoped_ptr? if not
magjed_chromium
2014/09/25 20:52:08
No, it does not accept a scoped_ptr. The definitio
| |
| 130 | 315 |
| 131 // This signals to libJingle that a new VideoFrame is available. | 316 // This signals to libJingle that a new VideoFrame is available. |
| 132 // libJingle have no assumptions on what thread this signal come from. | 317 // libJingle have no assumptions on what thread this signal come from. |
| 133 SignalFrameCaptured(this, &captured_frame); | 318 SignalFrameCaptured(this, &dummy_captured_frame); |
| 134 } | |
| 135 | |
| 136 void WebRtcVideoCapturerAdapter::UpdateI420Buffer( | |
| 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, | |
|
magjed_chromium
2014/09/25 19:21:23
Let the CoordinatedVideoAdapter take care of this
| |
| 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 } | 319 } |
| 192 | 320 |
| 193 } // namespace content | 321 } // namespace content |
| OLD | NEW |