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
3 // found in the LICENSE file.
4
5 #include "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h"
6
7 #include "base/bind.h"
8 #include "base/debug/trace_event.h"
9 #include "base/memory/aligned_memory.h"
10 #include "media/base/video_frame.h"
11 #include "third_party/libyuv/include/libyuv/scale.h"
12
13 namespace content {
14
WebRtcVideoCapturerAdapter(bool is_screencast)15 WebRtcVideoCapturerAdapter::WebRtcVideoCapturerAdapter(bool is_screencast)
16 : is_screencast_(is_screencast),
17 running_(false),
18 buffer_(NULL),
19 buffer_size_(0) {
20 thread_checker_.DetachFromThread();
21 }
22
~WebRtcVideoCapturerAdapter()23 WebRtcVideoCapturerAdapter::~WebRtcVideoCapturerAdapter() {
24 DVLOG(3) << " WebRtcVideoCapturerAdapter::dtor";
25 base::AlignedFree(buffer_);
26 }
27
Start(const cricket::VideoFormat & capture_format)28 cricket::CaptureState WebRtcVideoCapturerAdapter::Start(
29 const cricket::VideoFormat& capture_format) {
30 DCHECK(thread_checker_.CalledOnValidThread());
31 DCHECK(!running_);
32 DVLOG(3) << " WebRtcVideoCapturerAdapter::Start w = " << capture_format.width
33 << " h = " << capture_format.height;
34
35 running_ = true;
36 return cricket::CS_RUNNING;
37 }
38
Stop()39 void WebRtcVideoCapturerAdapter::Stop() {
40 DCHECK(thread_checker_.CalledOnValidThread());
41 DVLOG(3) << " WebRtcVideoCapturerAdapter::Stop ";
42 DCHECK(running_);
43 running_ = false;
44 SetCaptureFormat(NULL);
45 SignalStateChange(this, cricket::CS_STOPPED);
46 }
47
IsRunning()48 bool WebRtcVideoCapturerAdapter::IsRunning() {
49 DCHECK(thread_checker_.CalledOnValidThread());
50 return running_;
51 }
52
GetPreferredFourccs(std::vector<uint32> * fourccs)53 bool WebRtcVideoCapturerAdapter::GetPreferredFourccs(
54 std::vector<uint32>* fourccs) {
55 DCHECK(thread_checker_.CalledOnValidThread());
56 if (!fourccs)
57 return false;
58 fourccs->push_back(cricket::FOURCC_I420);
59 return true;
60 }
61
IsScreencast() const62 bool WebRtcVideoCapturerAdapter::IsScreencast() const {
63 return is_screencast_;
64 }
65
GetBestCaptureFormat(const cricket::VideoFormat & desired,cricket::VideoFormat * best_format)66 bool WebRtcVideoCapturerAdapter::GetBestCaptureFormat(
67 const cricket::VideoFormat& desired,
68 cricket::VideoFormat* best_format) {
69 DCHECK(thread_checker_.CalledOnValidThread());
70 DVLOG(3) << " GetBestCaptureFormat:: "
71 << " w = " << desired.width
72 << " h = " << desired.height;
73
74 // Capability enumeration is done in MediaStreamVideoSource. The adapter can
75 // just use what is provided.
76 // Use the desired format as the best format.
77 best_format->width = desired.width;
78 best_format->height = desired.height;
79 best_format->fourcc = cricket::FOURCC_I420;
80 best_format->interval = desired.interval;
81 return true;
82 }
83
OnFrameCaptured(const scoped_refptr<media::VideoFrame> & frame)84 void WebRtcVideoCapturerAdapter::OnFrameCaptured(
85 const scoped_refptr<media::VideoFrame>& frame) {
86 DCHECK(thread_checker_.CalledOnValidThread());
87 TRACE_EVENT0("video", "WebRtcVideoCapturerAdapter::OnFrameCaptured");
88 if (!(media::VideoFrame::I420 == frame->format() ||
89 media::VideoFrame::YV12 == frame->format())) {
90 // Some types of sources support textures as output. Since connecting
91 // sources and sinks do not check the format, we need to just ignore
92 // formats that we can not handle.
93 NOTREACHED();
94 return;
95 }
96
97 if (first_frame_timestamp_ == media::kNoTimestamp())
98 first_frame_timestamp_ = frame->timestamp();
99
100 cricket::CapturedFrame captured_frame;
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() *
106 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
112 // TODO(perkj):
113 // Libjingle expects contiguous layout of image planes as input.
114 // The only format where that is true in Chrome is I420 where the
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
131 // This signals to libJingle that a new VideoFrame is available.
132 // libJingle have no assumptions on what thread this signal come from.
133 SignalFrameCaptured(this, &captured_frame);
134 }
135
UpdateI420Buffer(const scoped_refptr<media::VideoFrame> & src)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,
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 }
192
193 } // namespace content
194