OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 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 "remoting/protocol/webrtc_video_stream.h" | 5 #include "remoting/protocol/webrtc_video_stream.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/single_thread_task_runner.h" |
| 9 #include "base/task_runner_util.h" |
| 10 #include "base/threading/thread_task_runner_handle.h" |
| 11 #include "remoting/base/constants.h" |
| 12 #include "remoting/proto/video.pb.h" |
8 #include "remoting/protocol/webrtc_dummy_video_capturer.h" | 13 #include "remoting/protocol/webrtc_dummy_video_capturer.h" |
| 14 #include "remoting/protocol/webrtc_transport.h" |
9 #include "third_party/webrtc/api/mediastreaminterface.h" | 15 #include "third_party/webrtc/api/mediastreaminterface.h" |
10 #include "third_party/webrtc/api/peerconnectioninterface.h" | 16 #include "third_party/webrtc/api/peerconnectioninterface.h" |
11 #include "third_party/webrtc/api/test/fakeconstraints.h" | 17 #include "third_party/webrtc/api/test/fakeconstraints.h" |
12 #include "third_party/webrtc/media/base/videocapturer.h" | 18 #include "third_party/webrtc/media/base/videocapturer.h" |
13 | 19 |
14 namespace remoting { | 20 namespace remoting { |
15 namespace protocol { | 21 namespace protocol { |
16 | 22 |
| 23 namespace { |
| 24 |
| 25 // Task running on the encoder thread to encode the |frame|. |
| 26 std::unique_ptr<VideoPacket> EncodeFrame( |
| 27 VideoEncoder* encoder, |
| 28 std::unique_ptr<webrtc::DesktopFrame> frame, |
| 29 uint32_t target_bitrate_kbps, |
| 30 bool key_frame_request, |
| 31 int64_t capture_time_ms) { |
| 32 uint32_t flags = 0; |
| 33 if (key_frame_request) |
| 34 flags |= VideoEncoder::REQUEST_KEY_FRAME; |
| 35 |
| 36 base::TimeTicks current = base::TimeTicks::Now(); |
| 37 encoder->UpdateTargetBitrate(target_bitrate_kbps); |
| 38 std::unique_ptr<VideoPacket> packet = encoder->Encode(*frame, flags); |
| 39 if (!packet) |
| 40 return nullptr; |
| 41 // TODO(isheriff): Note that while VideoPacket capture time is supposed |
| 42 // to be capture duration, we (ab)use it for capture timestamp here. This |
| 43 // will go away when we move away from VideoPacket. |
| 44 packet->set_capture_time_ms(capture_time_ms); |
| 45 |
| 46 VLOG(1) << "Encode duration " |
| 47 << (base::TimeTicks::Now() - current).InMilliseconds() |
| 48 << " payload size " << packet->data().size(); |
| 49 return packet; |
| 50 } |
| 51 |
| 52 void PostTaskOnTaskRunner( |
| 53 scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
| 54 const base::Closure& task) { |
| 55 task_runner->PostTask(FROM_HERE, task); |
| 56 } |
| 57 |
| 58 template <typename ParamType> |
| 59 void PostTaskOnTaskRunnerWithParam( |
| 60 scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
| 61 const base::Callback<void(ParamType param)>& task, |
| 62 ParamType param) { |
| 63 task_runner->PostTask(FROM_HERE, base::Bind(task, param)); |
| 64 } |
| 65 |
| 66 } // namespace |
| 67 |
17 const char kStreamLabel[] = "screen_stream"; | 68 const char kStreamLabel[] = "screen_stream"; |
18 const char kVideoLabel[] = "screen_video"; | 69 const char kVideoLabel[] = "screen_video"; |
19 | 70 |
20 WebrtcVideoStream::WebrtcVideoStream() {} | 71 WebrtcVideoStream::WebrtcVideoStream() |
| 72 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| 73 weak_factory_(this) {} |
21 | 74 |
22 WebrtcVideoStream::~WebrtcVideoStream() { | 75 WebrtcVideoStream::~WebrtcVideoStream() { |
23 if (stream_) { | 76 if (stream_) { |
24 for (const auto& track : stream_->GetVideoTracks()) { | 77 for (const auto& track : stream_->GetVideoTracks()) { |
25 track->GetSource()->Stop(); | 78 track->GetSource()->Stop(); |
26 stream_->RemoveTrack(track.get()); | 79 stream_->RemoveTrack(track.get()); |
27 } | 80 } |
28 connection_->RemoveStream(stream_.get()); | 81 peer_connection_->RemoveStream(stream_.get()); |
29 } | 82 } |
| 83 encode_task_runner_->DeleteSoon(FROM_HERE, encoder_.release()); |
30 } | 84 } |
31 | 85 |
32 bool WebrtcVideoStream::Start( | 86 bool WebrtcVideoStream::Start( |
33 std::unique_ptr<webrtc::DesktopCapturer> desktop_capturer, | 87 std::unique_ptr<webrtc::DesktopCapturer> desktop_capturer, |
34 WebrtcTransport* webrtc_transport, | 88 WebrtcTransport* webrtc_transport, |
35 scoped_refptr<base::SingleThreadTaskRunner> video_encode_task_runner, | 89 scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner, |
36 std::unique_ptr<VideoEncoder> video_encoder) { | 90 std::unique_ptr<VideoEncoder> video_encoder) { |
| 91 DCHECK(thread_checker_.CalledOnValidThread()); |
37 DCHECK(webrtc_transport); | 92 DCHECK(webrtc_transport); |
38 DCHECK(desktop_capturer); | 93 DCHECK(desktop_capturer); |
39 DCHECK(video_encode_task_runner); | 94 DCHECK(encode_task_runner); |
40 DCHECK(video_encoder); | 95 DCHECK(video_encoder); |
41 | 96 |
42 scoped_refptr<webrtc::PeerConnectionFactoryInterface> peer_connection_factory( | 97 scoped_refptr<webrtc::PeerConnectionFactoryInterface> peer_connection_factory( |
43 webrtc_transport->peer_connection_factory()); | 98 webrtc_transport->peer_connection_factory()); |
44 connection_ = webrtc_transport->peer_connection(); | 99 peer_connection_ = webrtc_transport->peer_connection(); |
45 DCHECK(peer_connection_factory); | 100 DCHECK(peer_connection_factory); |
46 DCHECK(connection_); | 101 DCHECK(peer_connection_); |
47 | 102 |
48 std::unique_ptr<WebrtcFrameScheduler> frame_scheduler( | 103 encode_task_runner_ = encode_task_runner; |
49 new WebrtcFrameScheduler(video_encode_task_runner, | 104 capturer_ = std::move(desktop_capturer); |
50 std::move(desktop_capturer), webrtc_transport, | 105 webrtc_transport_ = webrtc_transport; |
51 std::move(video_encoder))); | 106 encoder_ = std::move(video_encoder); |
52 webrtc_frame_scheduler_ = frame_scheduler.get(); | 107 capture_timer_.reset(new base::RepeatingTimer()); |
| 108 |
| 109 capturer_->Start(this); |
53 | 110 |
54 // Set video stream constraints. | 111 // Set video stream constraints. |
55 webrtc::FakeConstraints video_constraints; | 112 webrtc::FakeConstraints video_constraints; |
56 video_constraints.AddMandatory( | 113 video_constraints.AddMandatory( |
57 webrtc::MediaConstraintsInterface::kMinFrameRate, 5); | 114 webrtc::MediaConstraintsInterface::kMinFrameRate, 5); |
58 | 115 |
59 rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> src = | 116 rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> src = |
60 peer_connection_factory->CreateVideoSource( | 117 peer_connection_factory->CreateVideoSource(new WebrtcDummyVideoCapturer(), |
61 new WebrtcDummyVideoCapturer(std::move(frame_scheduler)), | 118 &video_constraints); |
62 &video_constraints); | |
63 rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track = | 119 rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track = |
64 peer_connection_factory->CreateVideoTrack(kVideoLabel, src); | 120 peer_connection_factory->CreateVideoTrack(kVideoLabel, src); |
65 | 121 |
66 stream_ = peer_connection_factory->CreateLocalMediaStream(kStreamLabel); | 122 stream_ = peer_connection_factory->CreateLocalMediaStream(kStreamLabel); |
67 | 123 |
68 if (!stream_->AddTrack(video_track.get()) || | 124 if (!stream_->AddTrack(video_track.get()) || |
69 !connection_->AddStream(stream_.get())) { | 125 !peer_connection_->AddStream(stream_.get())) { |
70 stream_ = nullptr; | 126 stream_ = nullptr; |
71 connection_ = nullptr; | 127 peer_connection_ = nullptr; |
72 return false; | 128 return false; |
73 } | 129 } |
| 130 |
| 131 // Register for PLI requests. |
| 132 webrtc_transport_->video_encoder_factory()->SetKeyFrameRequestCallback( |
| 133 base::Bind(&PostTaskOnTaskRunner, main_task_runner_, |
| 134 base::Bind(&WebrtcVideoStream::SetKeyFrameRequest, |
| 135 weak_factory_.GetWeakPtr()))); |
| 136 |
| 137 // Register for target bitrate notifications. |
| 138 webrtc_transport_->video_encoder_factory()->SetTargetBitrateCallback( |
| 139 base::Bind(&PostTaskOnTaskRunnerWithParam<int>, main_task_runner_, |
| 140 base::Bind(&WebrtcVideoStream::SetTargetBitrate, |
| 141 weak_factory_.GetWeakPtr()))); |
| 142 |
74 return true; | 143 return true; |
75 } | 144 } |
76 | 145 |
77 void WebrtcVideoStream::Pause(bool pause) { | 146 void WebrtcVideoStream::Pause(bool pause) { |
78 if (webrtc_frame_scheduler_) | 147 DCHECK(thread_checker_.CalledOnValidThread()); |
79 webrtc_frame_scheduler_->Pause(pause); | 148 if (pause) { |
| 149 capture_timer_->Stop(); |
| 150 } else { |
| 151 if (received_first_frame_request_) { |
| 152 StartCaptureTimer(); |
| 153 } |
| 154 } |
80 } | 155 } |
81 | 156 |
82 void WebrtcVideoStream::OnInputEventReceived(int64_t event_timestamp) { | 157 void WebrtcVideoStream::OnInputEventReceived(int64_t event_timestamp) { |
83 NOTIMPLEMENTED(); | 158 NOTIMPLEMENTED(); |
84 } | 159 } |
85 | 160 |
86 void WebrtcVideoStream::SetLosslessEncode(bool want_lossless) { | 161 void WebrtcVideoStream::SetLosslessEncode(bool want_lossless) { |
87 NOTIMPLEMENTED(); | 162 NOTIMPLEMENTED(); |
88 } | 163 } |
89 | 164 |
90 void WebrtcVideoStream::SetLosslessColor(bool want_lossless) { | 165 void WebrtcVideoStream::SetLosslessColor(bool want_lossless) { |
91 NOTIMPLEMENTED(); | 166 NOTIMPLEMENTED(); |
92 } | 167 } |
93 | 168 |
94 void WebrtcVideoStream::SetSizeCallback(const SizeCallback& size_callback) { | 169 void WebrtcVideoStream::SetSizeCallback(const SizeCallback& size_callback) { |
95 if (webrtc_frame_scheduler_) | 170 DCHECK(thread_checker_.CalledOnValidThread()); |
96 webrtc_frame_scheduler_->SetSizeCallback(size_callback); | 171 size_callback_ = size_callback; |
| 172 } |
| 173 |
| 174 void WebrtcVideoStream::SetKeyFrameRequest() { |
| 175 DCHECK(thread_checker_.CalledOnValidThread()); |
| 176 |
| 177 key_frame_request_ = true; |
| 178 if (!received_first_frame_request_) { |
| 179 received_first_frame_request_ = true; |
| 180 StartCaptureTimer(); |
| 181 main_task_runner_->PostTask( |
| 182 FROM_HERE, base::Bind(&WebrtcVideoStream::StartCaptureTimer, |
| 183 weak_factory_.GetWeakPtr())); |
| 184 } |
| 185 } |
| 186 |
| 187 void WebrtcVideoStream::StartCaptureTimer() { |
| 188 DCHECK(thread_checker_.CalledOnValidThread()); |
| 189 capture_timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(1) / 30, this, |
| 190 &WebrtcVideoStream::CaptureNextFrame); |
| 191 } |
| 192 |
| 193 void WebrtcVideoStream::SetTargetBitrate(int target_bitrate_kbps) { |
| 194 DCHECK(thread_checker_.CalledOnValidThread()); |
| 195 |
| 196 VLOG(1) << "Set Target bitrate " << target_bitrate_kbps; |
| 197 target_bitrate_kbps_ = target_bitrate_kbps; |
| 198 } |
| 199 |
| 200 bool WebrtcVideoStream::ClearAndGetKeyFrameRequest() { |
| 201 DCHECK(thread_checker_.CalledOnValidThread()); |
| 202 |
| 203 bool key_frame_request = key_frame_request_; |
| 204 key_frame_request_ = false; |
| 205 return key_frame_request; |
| 206 } |
| 207 |
| 208 void WebrtcVideoStream::OnCaptureResult( |
| 209 webrtc::DesktopCapturer::Result result, |
| 210 std::unique_ptr<webrtc::DesktopFrame> frame) { |
| 211 DCHECK(thread_checker_.CalledOnValidThread()); |
| 212 |
| 213 base::TimeTicks captured_ticks = base::TimeTicks::Now(); |
| 214 int64_t capture_timestamp_ms = |
| 215 (captured_ticks - base::TimeTicks()).InMilliseconds(); |
| 216 capture_pending_ = false; |
| 217 |
| 218 if (encode_pending_) { |
| 219 // TODO(isheriff): consider queuing here |
| 220 VLOG(1) << "Dropping captured frame since encoder is still busy"; |
| 221 return; |
| 222 } |
| 223 |
| 224 // TODO(sergeyu): Handle ERROR_PERMANENT result here. |
| 225 |
| 226 webrtc::DesktopVector dpi = |
| 227 frame->dpi().is_zero() ? webrtc::DesktopVector(kDefaultDpi, kDefaultDpi) |
| 228 : frame->dpi(); |
| 229 |
| 230 if (!frame_size_.equals(frame->size()) || !frame_dpi_.equals(dpi)) { |
| 231 frame_size_ = frame->size(); |
| 232 frame_dpi_ = dpi; |
| 233 if (!size_callback_.is_null()) |
| 234 size_callback_.Run(frame_size_, frame_dpi_); |
| 235 } |
| 236 encode_pending_ = true; |
| 237 base::PostTaskAndReplyWithResult( |
| 238 encode_task_runner_.get(), FROM_HERE, |
| 239 base::Bind(&EncodeFrame, encoder_.get(), base::Passed(std::move(frame)), |
| 240 target_bitrate_kbps_, ClearAndGetKeyFrameRequest(), |
| 241 capture_timestamp_ms), |
| 242 base::Bind(&WebrtcVideoStream::OnFrameEncoded, |
| 243 weak_factory_.GetWeakPtr())); |
| 244 } |
| 245 |
| 246 void WebrtcVideoStream::CaptureNextFrame() { |
| 247 DCHECK(thread_checker_.CalledOnValidThread()); |
| 248 |
| 249 if (capture_pending_ || encode_pending_) { |
| 250 VLOG(1) << "Capture/encode still pending.."; |
| 251 return; |
| 252 } |
| 253 |
| 254 capture_pending_ = true; |
| 255 VLOG(1) << "Capture next frame after " |
| 256 << (base::TimeTicks::Now() - last_capture_started_ticks_) |
| 257 .InMilliseconds(); |
| 258 last_capture_started_ticks_ = base::TimeTicks::Now(); |
| 259 capturer_->Capture(webrtc::DesktopRegion()); |
| 260 } |
| 261 |
| 262 void WebrtcVideoStream::OnFrameEncoded(std::unique_ptr<VideoPacket> packet) { |
| 263 DCHECK(thread_checker_.CalledOnValidThread()); |
| 264 |
| 265 encode_pending_ = false; |
| 266 if (!packet) |
| 267 return; |
| 268 base::TimeTicks current = base::TimeTicks::Now(); |
| 269 float encoded_bits = packet->data().size() * 8.0; |
| 270 |
| 271 // Simplistic adaptation of frame polling in the range 5 FPS to 30 FPS. |
| 272 uint32_t next_sched_ms = std::max( |
| 273 33, std::min(static_cast<int>(encoded_bits / target_bitrate_kbps_), 200)); |
| 274 if (webrtc_transport_->video_encoder_factory()->SendEncodedFrame( |
| 275 std::move(packet)) >= 0) { |
| 276 VLOG(1) << "Send duration " |
| 277 << (base::TimeTicks::Now() - current).InMilliseconds() |
| 278 << ", next sched " << next_sched_ms; |
| 279 } else { |
| 280 LOG(ERROR) << "SendEncodedFrame() failed"; |
| 281 } |
| 282 capture_timer_->Start(FROM_HERE, |
| 283 base::TimeDelta::FromMilliseconds(next_sched_ms), this, |
| 284 &WebrtcVideoStream::CaptureNextFrame); |
97 } | 285 } |
98 | 286 |
99 } // namespace protocol | 287 } // namespace protocol |
100 } // namespace remoting | 288 } // namespace remoting |
OLD | NEW |