OLD | NEW |
| (Empty) |
1 // Copyright 2015 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/protocol/webrtc_video_capturer_adapter.h" | |
6 | |
7 #include <utility> | |
8 | |
9 #include "remoting/base/constants.h" | |
10 #include "third_party/libyuv/include/libyuv/convert.h" | |
11 #include "third_party/webrtc/media/engine/webrtcvideoframe.h" | |
12 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" | |
13 | |
14 namespace remoting { | |
15 namespace protocol { | |
16 | |
17 // Number of frames to be captured per second. | |
18 const int kFramesPerSec = 30; | |
19 | |
20 WebrtcVideoCapturerAdapter::WebrtcVideoCapturerAdapter( | |
21 std::unique_ptr<webrtc::DesktopCapturer> capturer) | |
22 : desktop_capturer_(std::move(capturer)), weak_factory_(this) { | |
23 DCHECK(desktop_capturer_); | |
24 | |
25 // Disable video adaptation since we don't intend to use it. | |
26 set_enable_video_adapter(false); | |
27 } | |
28 | |
29 WebrtcVideoCapturerAdapter::~WebrtcVideoCapturerAdapter() { | |
30 DCHECK(!capture_timer_); | |
31 } | |
32 | |
33 void WebrtcVideoCapturerAdapter::SetSizeCallback( | |
34 const VideoStream::SizeCallback& size_callback) { | |
35 DCHECK(thread_checker_.CalledOnValidThread()); | |
36 size_callback_ = size_callback; | |
37 } | |
38 | |
39 base::WeakPtr<WebrtcVideoCapturerAdapter> | |
40 WebrtcVideoCapturerAdapter::GetWeakPtr() { | |
41 return weak_factory_.GetWeakPtr(); | |
42 } | |
43 | |
44 bool WebrtcVideoCapturerAdapter::GetBestCaptureFormat( | |
45 const cricket::VideoFormat& desired, | |
46 cricket::VideoFormat* best_format) { | |
47 DCHECK(thread_checker_.CalledOnValidThread()); | |
48 | |
49 // The |capture_format| passed to Start() is always ignored, so copy | |
50 // |best_format| to |desired_format|. | |
51 *best_format = desired; | |
52 return true; | |
53 } | |
54 | |
55 cricket::CaptureState WebrtcVideoCapturerAdapter::Start( | |
56 const cricket::VideoFormat& capture_format) { | |
57 DCHECK(thread_checker_.CalledOnValidThread()); | |
58 DCHECK(!capture_timer_); | |
59 | |
60 if (!desktop_capturer_) { | |
61 VLOG(1) << "WebrtcVideoCapturerAdapter failed to start."; | |
62 return cricket::CS_FAILED; | |
63 } | |
64 | |
65 desktop_capturer_->Start(this); | |
66 | |
67 capture_timer_.reset(new base::RepeatingTimer()); | |
68 capture_timer_->Start(FROM_HERE, | |
69 base::TimeDelta::FromSeconds(1) / kFramesPerSec, this, | |
70 &WebrtcVideoCapturerAdapter::CaptureNextFrame); | |
71 | |
72 return cricket::CS_RUNNING; | |
73 } | |
74 | |
75 bool WebrtcVideoCapturerAdapter::PauseCapturer(bool pause) { | |
76 DCHECK(thread_checker_.CalledOnValidThread()); | |
77 | |
78 if (pause) { | |
79 if (paused_) | |
80 return true; | |
81 | |
82 bool running = capture_state() == cricket::CS_STARTING || | |
83 capture_state() == cricket::CS_RUNNING; | |
84 | |
85 DCHECK_EQ(running, IsRunning()); | |
86 | |
87 if (!running) { | |
88 LOG(ERROR) | |
89 << "Cannot pause WebrtcVideoCapturerAdapter."; | |
90 return false; | |
91 } | |
92 | |
93 capture_timer_->Stop(); | |
94 paused_ = true; | |
95 | |
96 VLOG(1) << "WebrtcVideoCapturerAdapter paused."; | |
97 | |
98 return true; | |
99 } else { // Unpausing. | |
100 if (!paused_ || !GetCaptureFormat() || !capture_timer_) { | |
101 LOG(ERROR) << "Cannot unpause WebrtcVideoCapturerAdapter."; | |
102 return false; | |
103 } | |
104 | |
105 capture_timer_->Start(FROM_HERE, | |
106 base::TimeDelta::FromMicroseconds( | |
107 GetCaptureFormat()->interval / | |
108 (base::Time::kNanosecondsPerMicrosecond)), | |
109 this, | |
110 &WebrtcVideoCapturerAdapter::CaptureNextFrame); | |
111 paused_ = false; | |
112 | |
113 VLOG(1) << "WebrtcVideoCapturerAdapter unpaused."; | |
114 } | |
115 return true; | |
116 } | |
117 | |
118 void WebrtcVideoCapturerAdapter::Stop() { | |
119 DCHECK(thread_checker_.CalledOnValidThread()); | |
120 DCHECK_NE(capture_state(), cricket::CS_STOPPED); | |
121 | |
122 capture_timer_.reset(); | |
123 desktop_capturer_.reset(); | |
124 | |
125 SetCaptureFormat(nullptr); | |
126 SetCaptureState(cricket::CS_STOPPED); | |
127 | |
128 VLOG(1) << "WebrtcVideoCapturerAdapter stopped."; | |
129 } | |
130 | |
131 bool WebrtcVideoCapturerAdapter::IsRunning() { | |
132 DCHECK(thread_checker_.CalledOnValidThread()); | |
133 return capture_timer_->IsRunning(); | |
134 } | |
135 | |
136 bool WebrtcVideoCapturerAdapter::IsScreencast() const { | |
137 return true; | |
138 } | |
139 | |
140 bool WebrtcVideoCapturerAdapter::GetPreferredFourccs( | |
141 std::vector<uint32_t>* fourccs) { | |
142 return false; | |
143 } | |
144 | |
145 webrtc::SharedMemory* WebrtcVideoCapturerAdapter::CreateSharedMemory( | |
146 size_t size) { | |
147 return nullptr; | |
148 } | |
149 | |
150 void WebrtcVideoCapturerAdapter::OnCaptureCompleted( | |
151 webrtc::DesktopFrame* frame) { | |
152 DCHECK(thread_checker_.CalledOnValidThread()); | |
153 | |
154 DCHECK(capture_pending_); | |
155 capture_pending_ = false; | |
156 | |
157 if (!frame) | |
158 return; | |
159 | |
160 std::unique_ptr<webrtc::DesktopFrame> owned_frame(frame); | |
161 | |
162 // TODO(sergeyu): Currently the adapter keeps generating frames even when | |
163 // nothing is changing on the screen. This is necessary because the video | |
164 // sender drops frames. Obviously this is a suboptimal. The sending code in | |
165 // WebRTC needs to have some mechanism to notify when the bandwidth is | |
166 // exceeded, so the capturer can adapt frame rate. | |
167 | |
168 webrtc::DesktopVector dpi = | |
169 frame->dpi().is_zero() ? webrtc::DesktopVector(kDefaultDpi, kDefaultDpi) | |
170 : frame->dpi(); | |
171 if (!frame_size_.equals(frame->size()) || !frame_dpi_.equals(dpi)) { | |
172 frame_size_ = frame->size(); | |
173 frame_dpi_ = dpi; | |
174 if (!size_callback_.is_null()) | |
175 size_callback_.Run(frame_size_, frame_dpi_); | |
176 } | |
177 | |
178 if (!yuv_frame_ || | |
179 !frame_size_.equals( | |
180 webrtc::DesktopSize(yuv_frame_->width(), yuv_frame_->height()))) { | |
181 yuv_frame_ = new rtc::RefCountedObject<webrtc::I420Buffer>( | |
182 frame_size_.width(), frame_size_.height()); | |
183 // Set updated_region so the whole frame is converted to YUV below. | |
184 frame->mutable_updated_region()->SetRect( | |
185 webrtc::DesktopRect::MakeSize(frame_size_)); | |
186 } | |
187 | |
188 if (!yuv_frame_->HasOneRef()) { | |
189 // Frame is still used, typically by the encoder. We have to make | |
190 // a copy before modifying it. | |
191 // TODO(sergeyu): This will copy the buffer if it's being used. | |
192 // Optimize it by keeping a queue of frames. | |
193 yuv_frame_ = webrtc::I420Buffer::Copy(yuv_frame_); | |
194 } | |
195 | |
196 for (webrtc::DesktopRegion::Iterator i(frame->updated_region()); !i.IsAtEnd(); | |
197 i.Advance()) { | |
198 int left = i.rect().left(); | |
199 int top = i.rect().top(); | |
200 int width = i.rect().width(); | |
201 int height = i.rect().height(); | |
202 | |
203 if (left % 2 == 1) { | |
204 --left; | |
205 ++width; | |
206 } | |
207 if (top % 2 == 1) { | |
208 --top; | |
209 ++height; | |
210 } | |
211 | |
212 int y_stride = yuv_frame_->stride(webrtc::kYPlane); | |
213 int u_stride = yuv_frame_->stride(webrtc::kUPlane); | |
214 int v_stride = yuv_frame_->stride(webrtc::kVPlane); | |
215 libyuv::ARGBToI420( | |
216 frame->data() + frame->stride() * top + | |
217 left * webrtc::DesktopFrame::kBytesPerPixel, | |
218 frame->stride(), | |
219 yuv_frame_->MutableData(webrtc::kYPlane) + y_stride * top + left, | |
220 y_stride, yuv_frame_->MutableData(webrtc::kUPlane) + | |
221 u_stride * top / 2 + left / 2, | |
222 u_stride, yuv_frame_->MutableData(webrtc::kVPlane) + | |
223 v_stride * top / 2 + left / 2, | |
224 v_stride, width, height); | |
225 } | |
226 | |
227 cricket::WebRtcVideoFrame video_frame( | |
228 yuv_frame_, (base::TimeTicks::Now() - base::TimeTicks()) / | |
229 base::TimeDelta::FromMicroseconds(1) * 1000, | |
230 webrtc::kVideoRotation_0); | |
231 | |
232 OnFrame(this, &video_frame); | |
233 } | |
234 | |
235 void WebrtcVideoCapturerAdapter::CaptureNextFrame() { | |
236 DCHECK(thread_checker_.CalledOnValidThread()); | |
237 | |
238 if (capture_pending_) | |
239 return; | |
240 capture_pending_ = true; | |
241 desktop_capturer_->Capture(webrtc::DesktopRegion()); | |
242 } | |
243 | |
244 } // namespace protocol | |
245 } // namespace remoting | |
OLD | NEW |