Index: remoting/host/video_frame_pump.cc |
diff --git a/remoting/host/video_frame_pump.cc b/remoting/host/video_frame_pump.cc |
index 37d9da7c25e1ff47bc6b8bda8bf9488eac436be7..58b72db1ab03bdaaf38dabb44c35217ef6f06027 100644 |
--- a/remoting/host/video_frame_pump.cc |
+++ b/remoting/host/video_frame_pump.cc |
@@ -20,28 +20,6 @@ |
namespace remoting { |
-namespace { |
- |
-scoped_ptr<VideoPacket> EncodeFrame(VideoEncoder* encoder, |
- scoped_ptr<webrtc::DesktopFrame> frame) { |
- scoped_ptr<VideoPacket> packet; |
- |
- // If |frame| is non-NULL then let the encoder process it. |
- if (frame) { |
- packet = encoder->Encode(*frame); |
- } |
- |
- // If |frame| is NULL, or the encoder returned nothing, return an empty |
- // packet. |
- if (!packet) { |
- packet.reset(new VideoPacket()); |
- } |
- |
- return packet.Pass(); |
-} |
- |
-} // namespace |
- |
// Interval between empty keep-alive frames. These frames are sent only when the |
// stream is paused or inactive for some other reason (e.g. when blocked on |
// capturer). To prevent PseudoTCP from resetting congestion window this value |
@@ -50,6 +28,16 @@ static const int kKeepAlivePacketIntervalMs = 200; |
static bool g_enable_timestamps = false; |
+VideoFramePump::FrameTimestamps::FrameTimestamps() {} |
+VideoFramePump::FrameTimestamps::~FrameTimestamps() {} |
+ |
+VideoFramePump::PacketWithTimestamps::PacketWithTimestamps( |
+ scoped_ptr<VideoPacket> packet, |
+ scoped_ptr<FrameTimestamps> timestamps) |
+ : packet(packet.Pass()), timestamps(timestamps.Pass()) {} |
+ |
+VideoFramePump::PacketWithTimestamps::~PacketWithTimestamps() {} |
+ |
// static |
void VideoFramePump::EnableTimestampsForTests() { |
g_enable_timestamps = true; |
@@ -72,7 +60,6 @@ VideoFramePump::VideoFramePump( |
false), |
capture_scheduler_(base::Bind(&VideoFramePump::CaptureNextFrame, |
base::Unretained(this))), |
- latest_event_timestamp_(0), |
weak_factory_(this) { |
DCHECK(encoder_); |
DCHECK(video_stub_); |
@@ -91,10 +78,13 @@ void VideoFramePump::Pause(bool pause) { |
capture_scheduler_.Pause(pause); |
} |
-void VideoFramePump::SetLatestEventTimestamp(int64 latest_event_timestamp) { |
+void VideoFramePump::OnInputEventReceived(int64_t event_timestamp) { |
DCHECK(thread_checker_.CalledOnValidThread()); |
- latest_event_timestamp_ = latest_event_timestamp; |
+ if (!next_frame_timestamps_) |
+ next_frame_timestamps_.reset(new FrameTimestamps()); |
+ next_frame_timestamps_->input_event_client_timestamp = event_timestamp; |
+ next_frame_timestamps_->input_event_received_time = base::TimeTicks::Now(); |
} |
void VideoFramePump::SetLosslessEncode(bool want_lossless) { |
@@ -123,45 +113,126 @@ void VideoFramePump::OnCaptureCompleted(webrtc::DesktopFrame* frame) { |
capture_scheduler_.OnCaptureCompleted(); |
+ captured_frame_timestamps_->capture_ended_time = base::TimeTicks::Now(); |
+ |
// Even when |frame| is nullptr we still need to post it to the encode thread |
// to make sure frames are freed in the same order they are received and |
// that we don't start capturing frame n+2 before frame n is freed. |
base::PostTaskAndReplyWithResult( |
encode_task_runner_.get(), FROM_HERE, |
- base::Bind(&EncodeFrame, encoder_.get(), |
- base::Passed(make_scoped_ptr(frame))), |
- base::Bind(&VideoFramePump::SendEncodedFrame, weak_factory_.GetWeakPtr(), |
- latest_event_timestamp_, base::TimeTicks::Now())); |
+ base::Bind(&VideoFramePump::EncodeFrame, encoder_.get(), |
+ base::Passed(make_scoped_ptr(frame)), |
+ base::Passed(&captured_frame_timestamps_)), |
+ base::Bind(&VideoFramePump::OnFrameEncoded, weak_factory_.GetWeakPtr())); |
} |
void VideoFramePump::CaptureNextFrame() { |
DCHECK(thread_checker_.CalledOnValidThread()); |
+ // |next_frame_timestamps_| is not set if no input events were received since |
+ // the previous frame. In that case create FrameTimestamps instance without |
+ // setting |input_event_client_timestamp| and |input_event_received_time|. |
+ if (!next_frame_timestamps_) |
+ next_frame_timestamps_.reset(new FrameTimestamps()); |
+ |
+ captured_frame_timestamps_ = next_frame_timestamps_.Pass(); |
+ captured_frame_timestamps_->capture_started_time = base::TimeTicks::Now(); |
+ |
capturer_->Capture(webrtc::DesktopRegion()); |
} |
-void VideoFramePump::SendEncodedFrame(int64 latest_event_timestamp, |
- base::TimeTicks timestamp, |
- scoped_ptr<VideoPacket> packet) { |
+// static |
+scoped_ptr<VideoFramePump::PacketWithTimestamps> VideoFramePump::EncodeFrame( |
+ VideoEncoder* encoder, |
+ scoped_ptr<webrtc::DesktopFrame> frame, |
+ scoped_ptr<FrameTimestamps> timestamps) { |
+ timestamps->encode_started_time = base::TimeTicks::Now(); |
+ |
+ scoped_ptr<VideoPacket> packet; |
+ // If |frame| is non-NULL then let the encoder process it. |
+ if (frame) |
+ packet = encoder->Encode(*frame); |
+ |
+ // If |frame| is NULL, or the encoder returned nothing, return an empty |
+ // packet. |
+ if (!packet) |
+ packet.reset(new VideoPacket()); |
+ |
+ if (frame) |
+ packet->set_capture_time_ms(frame->capture_time_ms()); |
+ |
+ timestamps->encode_ended_time = base::TimeTicks::Now(); |
+ |
+ return make_scoped_ptr( |
+ new PacketWithTimestamps(packet.Pass(), timestamps.Pass())); |
+} |
+ |
+void VideoFramePump::OnFrameEncoded(scoped_ptr<PacketWithTimestamps> packet) { |
DCHECK(thread_checker_.CalledOnValidThread()); |
- if (g_enable_timestamps) |
- packet->set_timestamp(timestamp.ToInternalValue()); |
+ capture_scheduler_.OnFrameEncoded(packet->packet.get()); |
- packet->set_latest_event_timestamp(latest_event_timestamp); |
+ if (send_pending_) { |
+ pending_packets_.push_back(packet.Pass()); |
+ } else { |
+ SendPacket(packet.Pass()); |
+ } |
+} |
- capture_scheduler_.OnFrameEncoded(packet.get()); |
+void VideoFramePump::SendPacket(scoped_ptr<PacketWithTimestamps> packet) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ DCHECK(!send_pending_); |
- video_stub_->ProcessVideoPacket(packet.Pass(), |
+ packet->timestamps->can_send_time = base::TimeTicks::Now(); |
+ UpdateFrameTimers(packet->packet.get(), packet->timestamps.get()); |
+ |
+ send_pending_ = true; |
+ video_stub_->ProcessVideoPacket(packet->packet.Pass(), |
base::Bind(&VideoFramePump::OnVideoPacketSent, |
weak_factory_.GetWeakPtr())); |
} |
+void VideoFramePump::UpdateFrameTimers(VideoPacket* packet, |
+ FrameTimestamps* timestamps) { |
+ if (g_enable_timestamps) |
+ packet->set_timestamp(timestamps->capture_ended_time.ToInternalValue()); |
+ |
+ |
+ if (!timestamps->input_event_received_time.is_null()) { |
+ packet->set_capture_pending_time_ms((timestamps->capture_started_time - |
+ timestamps->input_event_received_time) |
+ .InMilliseconds()); |
+ packet->set_latest_event_timestamp( |
+ timestamps->input_event_client_timestamp); |
+ } |
+ |
+ packet->set_capture_overhead_time_ms( |
+ (timestamps->capture_ended_time - timestamps->capture_started_time) |
+ .InMilliseconds() - |
+ packet->capture_time_ms()); |
+ |
+ packet->set_encode_pending_time_ms( |
+ (timestamps->encode_started_time - timestamps->capture_ended_time) |
+ .InMilliseconds()); |
+ |
+ packet->set_send_pending_time_ms( |
+ (timestamps->can_send_time - timestamps->encode_ended_time) |
+ .InMilliseconds()); |
+} |
+ |
void VideoFramePump::OnVideoPacketSent() { |
DCHECK(thread_checker_.CalledOnValidThread()); |
+ send_pending_ = false; |
capture_scheduler_.OnFrameSent(); |
keep_alive_timer_.Reset(); |
+ |
+ // Send next packet if any. |
+ if (!pending_packets_.empty()) { |
+ scoped_ptr<PacketWithTimestamps> next(pending_packets_.front()); |
+ pending_packets_.weak_erase(pending_packets_.begin()); |
+ SendPacket(next.Pass()); |
+ } |
} |
void VideoFramePump::SendKeepAlivePacket() { |