Index: remoting/protocol/webrtc_video_stream.cc |
diff --git a/remoting/protocol/webrtc_video_stream.cc b/remoting/protocol/webrtc_video_stream.cc |
index 7d0b5db2cbe3d95ed34f53d4d757de75bdd750d6..84edbfb267ac4577814ab041592c9176bf06ce92 100644 |
--- a/remoting/protocol/webrtc_video_stream.cc |
+++ b/remoting/protocol/webrtc_video_stream.cc |
@@ -5,7 +5,13 @@ |
#include "remoting/protocol/webrtc_video_stream.h" |
#include "base/logging.h" |
+#include "base/single_thread_task_runner.h" |
+#include "base/task_runner_util.h" |
+#include "base/threading/thread_task_runner_handle.h" |
+#include "remoting/base/constants.h" |
+#include "remoting/proto/video.pb.h" |
#include "remoting/protocol/webrtc_dummy_video_capturer.h" |
+#include "remoting/protocol/webrtc_transport.h" |
#include "third_party/webrtc/api/mediastreaminterface.h" |
#include "third_party/webrtc/api/peerconnectioninterface.h" |
#include "third_party/webrtc/api/test/fakeconstraints.h" |
@@ -14,10 +20,57 @@ |
namespace remoting { |
namespace protocol { |
+namespace { |
+ |
+// Task running on the encoder thread to encode the |frame|. |
+std::unique_ptr<VideoPacket> EncodeFrame( |
+ VideoEncoder* encoder, |
+ std::unique_ptr<webrtc::DesktopFrame> frame, |
+ uint32_t target_bitrate_kbps, |
+ bool key_frame_request, |
+ int64_t capture_time_ms) { |
+ uint32_t flags = 0; |
+ if (key_frame_request) |
+ flags |= VideoEncoder::REQUEST_KEY_FRAME; |
+ |
+ base::TimeTicks current = base::TimeTicks::Now(); |
+ encoder->UpdateTargetBitrate(target_bitrate_kbps); |
+ std::unique_ptr<VideoPacket> packet = encoder->Encode(*frame, flags); |
+ if (!packet) |
+ return nullptr; |
+ // TODO(isheriff): Note that while VideoPacket capture time is supposed |
+ // to be capture duration, we (ab)use it for capture timestamp here. This |
+ // will go away when we move away from VideoPacket. |
+ packet->set_capture_time_ms(capture_time_ms); |
+ |
+ VLOG(1) << "Encode duration " |
+ << (base::TimeTicks::Now() - current).InMilliseconds() |
+ << " payload size " << packet->data().size(); |
+ return packet; |
+} |
+ |
+void PostTaskOnTaskRunner( |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
+ const base::Closure& task) { |
+ task_runner->PostTask(FROM_HERE, task); |
+} |
+ |
+template <typename ParamType> |
+void PostTaskOnTaskRunnerWithParam( |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
+ const base::Callback<void(ParamType param)>& task, |
+ ParamType param) { |
+ task_runner->PostTask(FROM_HERE, base::Bind(task, param)); |
+} |
+ |
+} // namespace |
+ |
const char kStreamLabel[] = "screen_stream"; |
const char kVideoLabel[] = "screen_video"; |
-WebrtcVideoStream::WebrtcVideoStream() {} |
+WebrtcVideoStream::WebrtcVideoStream() |
+ : main_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
+ weak_factory_(this) {} |
WebrtcVideoStream::~WebrtcVideoStream() { |
if (stream_) { |
@@ -25,31 +78,35 @@ WebrtcVideoStream::~WebrtcVideoStream() { |
track->GetSource()->Stop(); |
stream_->RemoveTrack(track.get()); |
} |
- connection_->RemoveStream(stream_.get()); |
+ peer_connection_->RemoveStream(stream_.get()); |
} |
+ encode_task_runner_->DeleteSoon(FROM_HERE, encoder_.release()); |
} |
bool WebrtcVideoStream::Start( |
std::unique_ptr<webrtc::DesktopCapturer> desktop_capturer, |
WebrtcTransport* webrtc_transport, |
- scoped_refptr<base::SingleThreadTaskRunner> video_encode_task_runner, |
+ scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner, |
std::unique_ptr<VideoEncoder> video_encoder) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
DCHECK(webrtc_transport); |
DCHECK(desktop_capturer); |
- DCHECK(video_encode_task_runner); |
+ DCHECK(encode_task_runner); |
DCHECK(video_encoder); |
scoped_refptr<webrtc::PeerConnectionFactoryInterface> peer_connection_factory( |
webrtc_transport->peer_connection_factory()); |
- connection_ = webrtc_transport->peer_connection(); |
+ peer_connection_ = webrtc_transport->peer_connection(); |
DCHECK(peer_connection_factory); |
- DCHECK(connection_); |
+ DCHECK(peer_connection_); |
+ |
+ encode_task_runner_ = encode_task_runner; |
+ capturer_ = std::move(desktop_capturer); |
+ webrtc_transport_ = webrtc_transport; |
+ encoder_ = std::move(video_encoder); |
+ capture_timer_.reset(new base::RepeatingTimer()); |
- std::unique_ptr<WebrtcFrameScheduler> frame_scheduler( |
- new WebrtcFrameScheduler(video_encode_task_runner, |
- std::move(desktop_capturer), webrtc_transport, |
- std::move(video_encoder))); |
- webrtc_frame_scheduler_ = frame_scheduler.get(); |
+ capturer_->Start(this); |
// Set video stream constraints. |
webrtc::FakeConstraints video_constraints; |
@@ -57,26 +114,44 @@ bool WebrtcVideoStream::Start( |
webrtc::MediaConstraintsInterface::kMinFrameRate, 5); |
rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> src = |
- peer_connection_factory->CreateVideoSource( |
- new WebrtcDummyVideoCapturer(std::move(frame_scheduler)), |
- &video_constraints); |
+ peer_connection_factory->CreateVideoSource(new WebrtcDummyVideoCapturer(), |
+ &video_constraints); |
rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track = |
peer_connection_factory->CreateVideoTrack(kVideoLabel, src); |
stream_ = peer_connection_factory->CreateLocalMediaStream(kStreamLabel); |
if (!stream_->AddTrack(video_track.get()) || |
- !connection_->AddStream(stream_.get())) { |
+ !peer_connection_->AddStream(stream_.get())) { |
stream_ = nullptr; |
- connection_ = nullptr; |
+ peer_connection_ = nullptr; |
return false; |
} |
+ |
+ // Register for PLI requests. |
+ webrtc_transport_->video_encoder_factory()->SetKeyFrameRequestCallback( |
+ base::Bind(&PostTaskOnTaskRunner, main_task_runner_, |
+ base::Bind(&WebrtcVideoStream::SetKeyFrameRequest, |
+ weak_factory_.GetWeakPtr()))); |
+ |
+ // Register for target bitrate notifications. |
+ webrtc_transport_->video_encoder_factory()->SetTargetBitrateCallback( |
+ base::Bind(&PostTaskOnTaskRunnerWithParam<int>, main_task_runner_, |
+ base::Bind(&WebrtcVideoStream::SetTargetBitrate, |
+ weak_factory_.GetWeakPtr()))); |
+ |
return true; |
} |
void WebrtcVideoStream::Pause(bool pause) { |
- if (webrtc_frame_scheduler_) |
- webrtc_frame_scheduler_->Pause(pause); |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ if (pause) { |
+ capture_timer_->Stop(); |
+ } else { |
+ if (received_first_frame_request_) { |
+ StartCaptureTimer(); |
+ } |
+ } |
} |
void WebrtcVideoStream::OnInputEventReceived(int64_t event_timestamp) { |
@@ -92,8 +167,121 @@ void WebrtcVideoStream::SetLosslessColor(bool want_lossless) { |
} |
void WebrtcVideoStream::SetSizeCallback(const SizeCallback& size_callback) { |
- if (webrtc_frame_scheduler_) |
- webrtc_frame_scheduler_->SetSizeCallback(size_callback); |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ size_callback_ = size_callback; |
+} |
+ |
+void WebrtcVideoStream::SetKeyFrameRequest() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
+ key_frame_request_ = true; |
+ if (!received_first_frame_request_) { |
+ received_first_frame_request_ = true; |
+ StartCaptureTimer(); |
+ main_task_runner_->PostTask( |
+ FROM_HERE, base::Bind(&WebrtcVideoStream::StartCaptureTimer, |
+ weak_factory_.GetWeakPtr())); |
+ } |
+} |
+ |
+void WebrtcVideoStream::StartCaptureTimer() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ capture_timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(1) / 30, this, |
+ &WebrtcVideoStream::CaptureNextFrame); |
+} |
+ |
+void WebrtcVideoStream::SetTargetBitrate(int target_bitrate_kbps) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
+ VLOG(1) << "Set Target bitrate " << target_bitrate_kbps; |
+ target_bitrate_kbps_ = target_bitrate_kbps; |
+} |
+ |
+bool WebrtcVideoStream::ClearAndGetKeyFrameRequest() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
+ bool key_frame_request = key_frame_request_; |
+ key_frame_request_ = false; |
+ return key_frame_request; |
+} |
+ |
+void WebrtcVideoStream::OnCaptureResult( |
+ webrtc::DesktopCapturer::Result result, |
+ std::unique_ptr<webrtc::DesktopFrame> frame) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
+ base::TimeTicks captured_ticks = base::TimeTicks::Now(); |
+ int64_t capture_timestamp_ms = |
+ (captured_ticks - base::TimeTicks()).InMilliseconds(); |
+ capture_pending_ = false; |
+ |
+ if (encode_pending_) { |
+ // TODO(isheriff): consider queuing here |
+ VLOG(1) << "Dropping captured frame since encoder is still busy"; |
+ return; |
+ } |
+ |
+ // TODO(sergeyu): Handle ERROR_PERMANENT result here. |
+ |
+ webrtc::DesktopVector dpi = |
+ frame->dpi().is_zero() ? webrtc::DesktopVector(kDefaultDpi, kDefaultDpi) |
+ : frame->dpi(); |
+ |
+ if (!frame_size_.equals(frame->size()) || !frame_dpi_.equals(dpi)) { |
+ frame_size_ = frame->size(); |
+ frame_dpi_ = dpi; |
+ if (!size_callback_.is_null()) |
+ size_callback_.Run(frame_size_, frame_dpi_); |
+ } |
+ encode_pending_ = true; |
+ base::PostTaskAndReplyWithResult( |
+ encode_task_runner_.get(), FROM_HERE, |
+ base::Bind(&EncodeFrame, encoder_.get(), base::Passed(std::move(frame)), |
+ target_bitrate_kbps_, ClearAndGetKeyFrameRequest(), |
+ capture_timestamp_ms), |
+ base::Bind(&WebrtcVideoStream::OnFrameEncoded, |
+ weak_factory_.GetWeakPtr())); |
+} |
+ |
+void WebrtcVideoStream::CaptureNextFrame() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
+ if (capture_pending_ || encode_pending_) { |
+ VLOG(1) << "Capture/encode still pending.."; |
+ return; |
+ } |
+ |
+ capture_pending_ = true; |
+ VLOG(1) << "Capture next frame after " |
+ << (base::TimeTicks::Now() - last_capture_started_ticks_) |
+ .InMilliseconds(); |
+ last_capture_started_ticks_ = base::TimeTicks::Now(); |
+ capturer_->Capture(webrtc::DesktopRegion()); |
+} |
+ |
+void WebrtcVideoStream::OnFrameEncoded(std::unique_ptr<VideoPacket> packet) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
+ encode_pending_ = false; |
+ if (!packet) |
+ return; |
+ base::TimeTicks current = base::TimeTicks::Now(); |
+ float encoded_bits = packet->data().size() * 8.0; |
+ |
+ // Simplistic adaptation of frame polling in the range 5 FPS to 30 FPS. |
+ uint32_t next_sched_ms = std::max( |
+ 33, std::min(static_cast<int>(encoded_bits / target_bitrate_kbps_), 200)); |
+ if (webrtc_transport_->video_encoder_factory()->SendEncodedFrame( |
+ std::move(packet)) >= 0) { |
+ VLOG(1) << "Send duration " |
+ << (base::TimeTicks::Now() - current).InMilliseconds() |
+ << ", next sched " << next_sched_ms; |
+ } else { |
+ LOG(ERROR) << "SendEncodedFrame() failed"; |
+ } |
+ capture_timer_->Start(FROM_HERE, |
+ base::TimeDelta::FromMilliseconds(next_sched_ms), this, |
+ &WebrtcVideoStream::CaptureNextFrame); |
} |
} // namespace protocol |