summaryrefslogtreecommitdiff
path: root/media/cast
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2014-06-25 10:30:53 +0100
committerTorne (Richard Coles) <torne@google.com>2014-06-25 10:30:53 +0100
commit6d86b77056ed63eb6871182f42a9fd5f07550f90 (patch)
tree4bd56255660f52e406fbd45083c006cd6ddb2877 /media/cast
parente9f930807da3850e29ecc641d2becc0403b5709c (diff)
downloadchromium_org-6d86b77056ed63eb6871182f42a9fd5f07550f90.tar.gz
Merge from Chromium at DEPS revision 278856
This commit was generated by merge_to_master.py. Change-Id: If3807744d3e5d3ee84b897bd2d099a2b7ed2e7a3
Diffstat (limited to 'media/cast')
-rw-r--r--media/cast/audio_sender/audio_encoder.cc29
-rw-r--r--media/cast/audio_sender/audio_sender.cc216
-rw-r--r--media/cast/audio_sender/audio_sender.h101
-rw-r--r--media/cast/logging/log_deserializer.cc5
-rw-r--r--media/cast/logging/logging_defines.cc1
-rw-r--r--media/cast/logging/logging_defines.h1
-rw-r--r--media/cast/logging/proto/proto_utils.cc1
-rw-r--r--media/cast/logging/proto/raw_events.proto1
-rw-r--r--media/cast/rtcp/rtcp_sender_unittest.cc3
-rw-r--r--media/cast/rtcp/rtcp_unittest.cc3
-rw-r--r--media/cast/test/cast_benchmarks.cc5
-rw-r--r--media/cast/transport/cast_transport_sender.h7
-rw-r--r--media/cast/transport/cast_transport_sender_impl.cc15
-rw-r--r--media/cast/transport/cast_transport_sender_impl.h3
-rw-r--r--media/cast/transport/pacing/mock_paced_packet_sender.h3
-rw-r--r--media/cast/transport/pacing/paced_sender.cc40
-rw-r--r--media/cast/transport/pacing/paced_sender.h13
-rw-r--r--media/cast/transport/pacing/paced_sender_unittest.cc6
-rw-r--r--media/cast/transport/rtp_sender/rtp_sender.cc17
-rw-r--r--media/cast/transport/rtp_sender/rtp_sender.h3
-rw-r--r--media/cast/transport/transport/udp_transport.cc16
-rw-r--r--media/cast/transport/transport/udp_transport.h6
-rw-r--r--media/cast/video_sender/video_sender.cc15
-rw-r--r--media/cast/video_sender/video_sender.h13
24 files changed, 408 insertions, 115 deletions
diff --git a/media/cast/audio_sender/audio_encoder.cc b/media/cast/audio_sender/audio_encoder.cc
index f81ad26377..8860c7dd2d 100644
--- a/media/cast/audio_sender/audio_encoder.cc
+++ b/media/cast/audio_sender/audio_encoder.cc
@@ -9,14 +9,12 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/location.h"
-#include "base/logging.h"
#include "base/stl_util.h"
#include "base/sys_byteorder.h"
#include "base/time/time.h"
#include "media/base/audio_bus.h"
#include "media/cast/cast_defines.h"
#include "media/cast/cast_environment.h"
-#include "media/cast/logging/logging_defines.h"
#include "third_party/opus/src/include/opus.h"
namespace media {
@@ -33,28 +31,6 @@ const int kFrameDurationMillis = 1000 / kFramesPerSecond; // No remainder!
// coming in too slow with respect to the capture timestamps.
const int kUnderrunThresholdMillis = 3 * kFrameDurationMillis;
-void LogAudioFrameEncodedEvent(
- const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
- base::TimeTicks event_time,
- media::cast::RtpTimestamp rtp_timestamp,
- uint32 frame_id,
- size_t frame_size) {
- if (!cast_environment->CurrentlyOn(CastEnvironment::MAIN)) {
- cast_environment->PostTask(
- CastEnvironment::MAIN,
- FROM_HERE,
- base::Bind(&LogAudioFrameEncodedEvent,
- cast_environment, event_time,
- rtp_timestamp, frame_id, frame_size));
- return;
- }
- cast_environment->Logging()->InsertEncodedFrameEvent(
- event_time, media::cast::FRAME_ENCODED, media::cast::AUDIO_EVENT,
- rtp_timestamp, frame_id,
- static_cast<int>(frame_size), /* key_frame - unused */ false,
- /*target_bitrate - unused*/ 0);
-}
-
} // namespace
@@ -150,11 +126,6 @@ class AudioEncoder::ImplBase
audio_frame->reference_time = frame_capture_time_;
if (EncodeFromFilledBuffer(&audio_frame->data)) {
- LogAudioFrameEncodedEvent(cast_environment_,
- cast_environment_->Clock()->NowTicks(),
- audio_frame->rtp_timestamp,
- audio_frame->frame_id,
- audio_frame->data.size());
cast_environment_->PostTask(
CastEnvironment::MAIN,
FROM_HERE,
diff --git a/media/cast/audio_sender/audio_sender.cc b/media/cast/audio_sender/audio_sender.cc
index 27b42d0dc8..878f3456c8 100644
--- a/media/cast/audio_sender/audio_sender.cc
+++ b/media/cast/audio_sender/audio_sender.cc
@@ -8,7 +8,9 @@
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "media/cast/audio_sender/audio_encoder.h"
-#include "media/cast/transport/cast_transport_defines.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/rtcp/rtcp_defines.h"
+#include "media/cast/transport/cast_transport_config.h"
namespace media {
namespace cast {
@@ -16,13 +18,24 @@ namespace cast {
const int kNumAggressiveReportsSentAtStart = 100;
const int kMinSchedulingDelayMs = 1;
-// TODO(mikhal): Reduce heap allocation when not needed.
+// TODO(miu): This should be specified in AudioSenderConfig, but currently it is
+// fixed to 100 FPS (i.e., 10 ms per frame), and AudioEncoder assumes this as
+// well.
+const int kAudioFrameRate = 100;
+
AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment,
const AudioSenderConfig& audio_config,
transport::CastTransportSender* const transport_sender)
: cast_environment_(cast_environment),
+ target_playout_delay_(base::TimeDelta::FromMilliseconds(
+ audio_config.rtp_config.max_delay_ms)),
transport_sender_(transport_sender),
- rtp_timestamp_helper_(audio_config.frequency),
+ max_unacked_frames_(
+ std::min(kMaxUnackedFrames,
+ 1 + static_cast<int>(target_playout_delay_ *
+ kAudioFrameRate /
+ base::TimeDelta::FromSeconds(1)))),
+ configured_encoder_bitrate_(audio_config.bitrate),
rtcp_(cast_environment,
this,
transport_sender_,
@@ -34,10 +47,16 @@ AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment,
audio_config.incoming_feedback_ssrc,
audio_config.rtcp_c_name,
AUDIO_EVENT),
+ rtp_timestamp_helper_(audio_config.frequency),
num_aggressive_rtcp_reports_sent_(0),
+ last_sent_frame_id_(0),
+ latest_acked_frame_id_(0),
+ duplicate_ack_counter_(0),
cast_initialization_status_(STATUS_AUDIO_UNINITIALIZED),
weak_factory_(this) {
- rtcp_.SetCastReceiverEventHistorySize(kReceiverRtcpEventHistorySize);
+ VLOG(1) << "max_unacked_frames " << max_unacked_frames_;
+ DCHECK_GT(max_unacked_frames_, 0);
+
if (!audio_config.use_external_encoder) {
audio_encoder_.reset(
new AudioEncoder(cast_environment,
@@ -47,7 +66,7 @@ AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment,
cast_initialization_status_ = audio_encoder_->InitializationResult();
} else {
NOTREACHED(); // No support for external audio encoding.
- cast_initialization_status_ = STATUS_AUDIO_INITIALIZED;
+ cast_initialization_status_ = STATUS_AUDIO_UNINITIALIZED;
}
media::cast::transport::CastTransportAudioConfig transport_config;
@@ -55,10 +74,11 @@ AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment,
transport_config.rtp.config = audio_config.rtp_config;
transport_config.frequency = audio_config.frequency;
transport_config.channels = audio_config.channels;
- transport_config.rtp.max_outstanding_frames =
- audio_config.rtp_config.max_delay_ms / 100 + 1;
+ transport_config.rtp.max_outstanding_frames = max_unacked_frames_;
transport_sender_->InitializeAudio(transport_config);
+ rtcp_.SetCastReceiverEventHistorySize(kReceiverRtcpEventHistorySize);
+
memset(frame_id_to_rtp_timestamp_, 0, sizeof(frame_id_to_rtp_timestamp_));
}
@@ -72,16 +92,43 @@ void AudioSender::InsertAudio(scoped_ptr<AudioBus> audio_bus,
return;
}
DCHECK(audio_encoder_.get()) << "Invalid internal state";
+
+ if (AreTooManyFramesInFlight()) {
+ VLOG(1) << "Dropping frame due to too many frames currently in-flight.";
+ return;
+ }
+
audio_encoder_->InsertAudio(audio_bus.Pass(), recorded_time);
}
void AudioSender::SendEncodedAudioFrame(
- scoped_ptr<transport::EncodedFrame> audio_frame) {
+ scoped_ptr<transport::EncodedFrame> encoded_frame) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- DCHECK(!audio_frame->reference_time.is_null());
- rtp_timestamp_helper_.StoreLatestTime(audio_frame->reference_time,
- audio_frame->rtp_timestamp);
+ const uint32 frame_id = encoded_frame->frame_id;
+
+ const bool is_first_frame_to_be_sent = last_send_time_.is_null();
+ last_send_time_ = cast_environment_->Clock()->NowTicks();
+ last_sent_frame_id_ = frame_id;
+ // If this is the first frame about to be sent, fake the value of
+ // |latest_acked_frame_id_| to indicate the receiver starts out all caught up.
+ // Also, schedule the periodic frame re-send checks.
+ if (is_first_frame_to_be_sent) {
+ latest_acked_frame_id_ = frame_id - 1;
+ ScheduleNextResendCheck();
+ }
+
+ cast_environment_->Logging()->InsertEncodedFrameEvent(
+ last_send_time_, FRAME_ENCODED, AUDIO_EVENT, encoded_frame->rtp_timestamp,
+ frame_id, static_cast<int>(encoded_frame->data.size()),
+ encoded_frame->dependency == transport::EncodedFrame::KEY,
+ configured_encoder_bitrate_);
+ // Only use lowest 8 bits as key.
+ frame_id_to_rtp_timestamp_[frame_id & 0xff] = encoded_frame->rtp_timestamp;
+
+ DCHECK(!encoded_frame->reference_time.is_null());
+ rtp_timestamp_helper_.StoreLatestTime(encoded_frame->reference_time,
+ encoded_frame->rtp_timestamp);
// At the start of the session, it's important to send reports before each
// frame so that the receiver can properly compute playout times. The reason
@@ -98,15 +145,7 @@ void AudioSender::SendEncodedAudioFrame(
SendRtcpReport(is_last_aggressive_report);
}
- frame_id_to_rtp_timestamp_[audio_frame->frame_id & 0xff] =
- audio_frame->rtp_timestamp;
- transport_sender_->InsertCodedAudioFrame(*audio_frame);
-}
-
-void AudioSender::ResendPackets(
- const MissingFramesAndPacketsMap& missing_frames_and_packets) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- transport_sender_->ResendPackets(true, missing_frames_and_packets, false);
+ transport_sender_->InsertCodedAudioFrame(*encoded_frame);
}
void AudioSender::IncomingRtcpPacket(scoped_ptr<Packet> packet) {
@@ -146,6 +185,37 @@ void AudioSender::SendRtcpReport(bool schedule_future_reports) {
ScheduleNextRtcpReport();
}
+void AudioSender::ScheduleNextResendCheck() {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ DCHECK(!last_send_time_.is_null());
+ base::TimeDelta time_to_next =
+ last_send_time_ - cast_environment_->Clock()->NowTicks() +
+ target_playout_delay_;
+ time_to_next = std::max(
+ time_to_next, base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs));
+ cast_environment_->PostDelayedTask(
+ CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(&AudioSender::ResendCheck, weak_factory_.GetWeakPtr()),
+ time_to_next);
+}
+
+void AudioSender::ResendCheck() {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ DCHECK(!last_send_time_.is_null());
+ const base::TimeDelta time_since_last_send =
+ cast_environment_->Clock()->NowTicks() - last_send_time_;
+ if (time_since_last_send > target_playout_delay_) {
+ if (latest_acked_frame_id_ == last_sent_frame_id_) {
+ // Last frame acked, no point in doing anything
+ } else {
+ VLOG(1) << "ACK timeout; last acked frame: " << latest_acked_frame_id_;
+ ResendForKickstart();
+ }
+ }
+ ScheduleNextResendCheck();
+}
+
void AudioSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
@@ -161,15 +231,105 @@ void AudioSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
}
}
- if (!cast_feedback.missing_frames_and_packets_.empty()) {
- ResendPackets(cast_feedback.missing_frames_and_packets_);
+ if (last_send_time_.is_null())
+ return; // Cannot get an ACK without having first sent a frame.
+
+ if (cast_feedback.missing_frames_and_packets_.empty()) {
+ // We only count duplicate ACKs when we have sent newer frames.
+ if (latest_acked_frame_id_ == cast_feedback.ack_frame_id_ &&
+ latest_acked_frame_id_ != last_sent_frame_id_) {
+ duplicate_ack_counter_++;
+ } else {
+ duplicate_ack_counter_ = 0;
+ }
+ // TODO(miu): The values "2" and "3" should be derived from configuration.
+ if (duplicate_ack_counter_ >= 2 && duplicate_ack_counter_ % 3 == 2) {
+ VLOG(1) << "Received duplicate ACK for frame " << latest_acked_frame_id_;
+ ResendForKickstart();
+ }
+ } else {
+ // Only count duplicated ACKs if there is no NACK request in between.
+ // This is to avoid aggresive resend.
+ duplicate_ack_counter_ = 0;
+
+ base::TimeDelta rtt;
+ base::TimeDelta avg_rtt;
+ base::TimeDelta min_rtt;
+ base::TimeDelta max_rtt;
+ rtcp_.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt);
+
+ // A NACK is also used to cancel pending re-transmissions.
+ transport_sender_->ResendPackets(
+ true, cast_feedback.missing_frames_and_packets_, false, min_rtt);
}
- uint32 acked_frame_id = static_cast<uint32>(cast_feedback.ack_frame_id_);
- VLOG(2) << "Received audio ACK: " << acked_frame_id;
- cast_environment_->Logging()->InsertFrameEvent(
- cast_environment_->Clock()->NowTicks(),
- FRAME_ACK_RECEIVED, AUDIO_EVENT,
- frame_id_to_rtp_timestamp_[acked_frame_id & 0xff], acked_frame_id);
+
+ const base::TimeTicks now = cast_environment_->Clock()->NowTicks();
+
+ const RtpTimestamp rtp_timestamp =
+ frame_id_to_rtp_timestamp_[cast_feedback.ack_frame_id_ & 0xff];
+ cast_environment_->Logging()->InsertFrameEvent(now,
+ FRAME_ACK_RECEIVED,
+ AUDIO_EVENT,
+ rtp_timestamp,
+ cast_feedback.ack_frame_id_);
+
+ const bool is_acked_out_of_order =
+ static_cast<int32>(cast_feedback.ack_frame_id_ -
+ latest_acked_frame_id_) < 0;
+ VLOG(2) << "Received ACK" << (is_acked_out_of_order ? " out-of-order" : "")
+ << " for frame " << cast_feedback.ack_frame_id_;
+ if (!is_acked_out_of_order) {
+ // Cancel resends of acked frames.
+ MissingFramesAndPacketsMap missing_frames_and_packets;
+ PacketIdSet missing;
+ while (latest_acked_frame_id_ != cast_feedback.ack_frame_id_) {
+ latest_acked_frame_id_++;
+ missing_frames_and_packets[latest_acked_frame_id_] = missing;
+ }
+ transport_sender_->ResendPackets(
+ true, missing_frames_and_packets, true, base::TimeDelta());
+ latest_acked_frame_id_ = cast_feedback.ack_frame_id_;
+ }
+}
+
+bool AudioSender::AreTooManyFramesInFlight() const {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ int frames_in_flight = 0;
+ if (!last_send_time_.is_null()) {
+ frames_in_flight +=
+ static_cast<int32>(last_sent_frame_id_ - latest_acked_frame_id_);
+ }
+ VLOG(2) << frames_in_flight
+ << " frames in flight; last sent: " << last_sent_frame_id_
+ << " latest acked: " << latest_acked_frame_id_;
+ return frames_in_flight >= max_unacked_frames_;
+}
+
+void AudioSender::ResendForKickstart() {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ DCHECK(!last_send_time_.is_null());
+ VLOG(1) << "Resending last packet of frame " << last_sent_frame_id_
+ << " to kick-start.";
+ // Send the first packet of the last encoded frame to kick start
+ // retransmission. This gives enough information to the receiver what
+ // packets and frames are missing.
+ MissingFramesAndPacketsMap missing_frames_and_packets;
+ PacketIdSet missing;
+ missing.insert(kRtcpCastLastPacket);
+ missing_frames_and_packets.insert(
+ std::make_pair(last_sent_frame_id_, missing));
+ last_send_time_ = cast_environment_->Clock()->NowTicks();
+
+ base::TimeDelta rtt;
+ base::TimeDelta avg_rtt;
+ base::TimeDelta min_rtt;
+ base::TimeDelta max_rtt;
+ rtcp_.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt);
+
+ // Sending this extra packet is to kick-start the session. There is
+ // no need to optimize re-transmission for this case.
+ transport_sender_->ResendPackets(
+ true, missing_frames_and_packets, false, min_rtt);
}
} // namespace cast
diff --git a/media/cast/audio_sender/audio_sender.h b/media/cast/audio_sender/audio_sender.h
index 03de633bef..80cf8a4e9e 100644
--- a/media/cast/audio_sender/audio_sender.h
+++ b/media/cast/audio_sender/audio_sender.h
@@ -14,6 +14,8 @@
#include "base/time/time.h"
#include "media/base/audio_bus.h"
#include "media/cast/cast_config.h"
+#include "media/cast/cast_environment.h"
+#include "media/cast/logging/logging_defines.h"
#include "media/cast/rtcp/rtcp.h"
#include "media/cast/rtp_timestamp_helper.h"
@@ -22,8 +24,12 @@ namespace cast {
class AudioEncoder;
-// This class is not thread safe.
-// It's only called from the main cast thread.
+// Not thread safe. Only called from the main cast thread.
+// This class owns all objects related to sending audio, objects that create RTP
+// packets, congestion control, audio encoder, parsing and sending of
+// RTCP packets.
+// Additionally it posts a bunch of delayed tasks to the main thread for various
+// timeouts.
class AudioSender : public RtcpSenderFeedback,
public base::NonThreadSafe,
public base::SupportsWeakPtr<AudioSender> {
@@ -38,6 +44,10 @@ class AudioSender : public RtcpSenderFeedback,
return cast_initialization_status_;
}
+ // Note: It is not guaranteed that |audio_frame| will actually be encoded and
+ // sent, if AudioSender detects too many frames in flight. Therefore, clients
+ // should be careful about the rate at which this method is called.
+ //
// Note: It is invalid to call this method if InitializationResult() returns
// anything but STATUS_AUDIO_INITIALIZED.
void InsertAudio(scoped_ptr<AudioBus> audio_bus,
@@ -46,31 +56,98 @@ class AudioSender : public RtcpSenderFeedback,
// Only called from the main cast thread.
void IncomingRtcpPacket(scoped_ptr<Packet> packet);
- private:
- void ResendPackets(
- const MissingFramesAndPacketsMap& missing_frames_and_packets);
+ protected:
+ // Protected for testability.
+ virtual void OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback)
+ OVERRIDE;
+ private:
+ // Schedule and execute periodic sending of RTCP report.
void ScheduleNextRtcpReport();
void SendRtcpReport(bool schedule_future_reports);
+ // Schedule and execute periodic checks for re-sending packets. If no
+ // acknowledgements have been received for "too long," AudioSender will
+ // speculatively re-send certain packets of an unacked frame to kick-start
+ // re-transmission. This is a last resort tactic to prevent the session from
+ // getting stuck after a long outage.
+ void ScheduleNextResendCheck();
+ void ResendCheck();
+ void ResendForKickstart();
+
+ // Returns true if there are too many frames in flight, as defined by the
+ // configured target playout delay plus simple logic. When this is true,
+ // InsertAudio() will silenty drop frames instead of sending them to the audio
+ // encoder.
+ bool AreTooManyFramesInFlight() const;
+
// Called by the |audio_encoder_| with the next EncodedFrame to send.
void SendEncodedAudioFrame(scoped_ptr<transport::EncodedFrame> audio_frame);
- virtual void OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback)
- OVERRIDE;
-
- scoped_refptr<CastEnvironment> cast_environment_;
+ const scoped_refptr<CastEnvironment> cast_environment_;
+
+ // The total amount of time between a frame's capture/recording on the sender
+ // and its playback on the receiver (i.e., shown to a user). This is fixed as
+ // a value large enough to give the system sufficient time to encode,
+ // transmit/retransmit, receive, decode, and render; given its run-time
+ // environment (sender/receiver hardware performance, network conditions,
+ // etc.).
+ const base::TimeDelta target_playout_delay_;
+
+ // Sends encoded frames over the configured transport (e.g., UDP). In
+ // Chromium, this could be a proxy that first sends the frames from a renderer
+ // process to the browser process over IPC, with the browser process being
+ // responsible for "packetizing" the frames and pushing packets into the
+ // network layer.
transport::CastTransportSender* const transport_sender_;
+
+ // Maximum number of outstanding frames before the encoding and sending of
+ // new frames shall halt.
+ const int max_unacked_frames_;
+
+ // Encodes AudioBuses into EncodedFrames.
scoped_ptr<AudioEncoder> audio_encoder_;
- RtpTimestampHelper rtp_timestamp_helper_;
+ const int configured_encoder_bitrate_;
+
+ // Manages sending/receiving of RTCP packets, including sender/receiver
+ // reports.
Rtcp rtcp_;
+
+ // Records lip-sync (i.e., mapping of RTP <--> NTP timestamps), and
+ // extrapolates this mapping to any other point in time.
+ RtpTimestampHelper rtp_timestamp_helper_;
+
+ // Counts how many RTCP reports are being "aggressively" sent (i.e., one per
+ // frame) at the start of the session. Once a threshold is reached, RTCP
+ // reports are instead sent at the configured interval + random drift.
int num_aggressive_rtcp_reports_sent_;
+ // This is "null" until the first frame is sent. Thereafter, this tracks the
+ // last time any frame was sent or re-sent.
+ base::TimeTicks last_send_time_;
+
+ // The ID of the last frame sent. Logic throughout AudioSender assumes this
+ // can safely wrap-around. This member is invalid until
+ // |!last_send_time_.is_null()|.
+ uint32 last_sent_frame_id_;
+
+ // The ID of the latest (not necessarily the last) frame that has been
+ // acknowledged. Logic throughout AudioSender assumes this can safely
+ // wrap-around. This member is invalid until |!last_send_time_.is_null()|.
+ uint32 latest_acked_frame_id_;
+
+ // Counts the number of duplicate ACK that are being received. When this
+ // number reaches a threshold, the sender will take this as a sign that the
+ // receiver hasn't yet received the first packet of the next frame. In this
+ // case, AudioSender will trigger a re-send of the next frame.
+ int duplicate_ack_counter_;
+
// If this sender is ready for use, this is STATUS_AUDIO_INITIALIZED.
CastInitializationStatus cast_initialization_status_;
- // Used to map the lower 8 bits of the frame id to a RTP timestamp. This is
- // good enough as we only use it for logging.
+ // This is a "good enough" mapping for finding the RTP timestamp associated
+ // with a video frame. The key is the lowest 8 bits of frame id (which is
+ // what is sent via RTCP). This map is used for logging purposes.
RtpTimestamp frame_id_to_rtp_timestamp_[256];
// NOTE: Weak pointers must be invalidated before all other member variables.
diff --git a/media/cast/logging/log_deserializer.cc b/media/cast/logging/log_deserializer.cc
index a4c79b3de9..1c6dd57224 100644
--- a/media/cast/logging/log_deserializer.cc
+++ b/media/cast/logging/log_deserializer.cc
@@ -35,7 +35,12 @@ void MergePacketEvent(const AggregatedPacketEvent& from,
for (int j = 0; j < to->base_packet_event_size(); j++) {
BasePacketEvent* to_base_event = to->mutable_base_packet_event(j);
if (from_base_event.packet_id() == to_base_event->packet_id()) {
+ int packet_size = std::max(
+ from_base_event.size(), to_base_event->size());
+ // Need special merge logic here because we need to prevent a valid
+ // packet size (> 0) from being overwritten with an invalid one (= 0).
to_base_event->MergeFrom(from_base_event);
+ to_base_event->set_size(packet_size);
merged = true;
break;
}
diff --git a/media/cast/logging/logging_defines.cc b/media/cast/logging/logging_defines.cc
index d0dd5c8e57..05ceeb9521 100644
--- a/media/cast/logging/logging_defines.cc
+++ b/media/cast/logging/logging_defines.cc
@@ -25,6 +25,7 @@ const char* CastLoggingToString(CastLoggingEvent event) {
ENUM_TO_STRING(FRAME_PLAYOUT);
ENUM_TO_STRING(PACKET_SENT_TO_NETWORK);
ENUM_TO_STRING(PACKET_RETRANSMITTED);
+ ENUM_TO_STRING(PACKET_RTX_REJECTED);
ENUM_TO_STRING(PACKET_RECEIVED);
}
NOTREACHED();
diff --git a/media/cast/logging/logging_defines.h b/media/cast/logging/logging_defines.h
index b3f3841ffb..021a3c99a7 100644
--- a/media/cast/logging/logging_defines.h
+++ b/media/cast/logging/logging_defines.h
@@ -32,6 +32,7 @@ enum CastLoggingEvent {
// Sender side packet events.
PACKET_SENT_TO_NETWORK,
PACKET_RETRANSMITTED,
+ PACKET_RTX_REJECTED,
// Receiver side packet events.
PACKET_RECEIVED,
kNumOfLoggingEvents = PACKET_RECEIVED
diff --git a/media/cast/logging/proto/proto_utils.cc b/media/cast/logging/proto/proto_utils.cc
index 1f05616d83..03251e64c0 100644
--- a/media/cast/logging/proto/proto_utils.cc
+++ b/media/cast/logging/proto/proto_utils.cc
@@ -25,6 +25,7 @@ proto::EventType ToProtoEventType(CastLoggingEvent event) {
TO_PROTO_ENUM(FRAME_PLAYOUT);
TO_PROTO_ENUM(PACKET_SENT_TO_NETWORK);
TO_PROTO_ENUM(PACKET_RETRANSMITTED);
+ TO_PROTO_ENUM(PACKET_RTX_REJECTED);
TO_PROTO_ENUM(PACKET_RECEIVED);
}
NOTREACHED();
diff --git a/media/cast/logging/proto/raw_events.proto b/media/cast/logging/proto/raw_events.proto
index 08bf53d19a..1d2c537db8 100644
--- a/media/cast/logging/proto/raw_events.proto
+++ b/media/cast/logging/proto/raw_events.proto
@@ -66,6 +66,7 @@ enum EventType {
PACKET_SENT_TO_NETWORK = 36;
PACKET_RETRANSMITTED = 37;
PACKET_RECEIVED = 38;
+ PACKET_RTX_REJECTED = 39;
}
// Contains information independent of the stream that describes the system
diff --git a/media/cast/rtcp/rtcp_sender_unittest.cc b/media/cast/rtcp/rtcp_sender_unittest.cc
index 2bd807f380..0b0c7d3ab8 100644
--- a/media/cast/rtcp/rtcp_sender_unittest.cc
+++ b/media/cast/rtcp/rtcp_sender_unittest.cc
@@ -59,7 +59,8 @@ class TestRtcpTransport : public transport::PacedPacketSender {
return false;
}
virtual bool ResendPackets(
- const transport::SendPacketVector& packets) OVERRIDE {
+ const transport::SendPacketVector& packets,
+ base::TimeDelta dedupe_window) OVERRIDE {
return false;
}
diff --git a/media/cast/rtcp/rtcp_unittest.cc b/media/cast/rtcp/rtcp_unittest.cc
index d5bf312c3e..095e6d24df 100644
--- a/media/cast/rtcp/rtcp_unittest.cc
+++ b/media/cast/rtcp/rtcp_unittest.cc
@@ -104,7 +104,8 @@ class LocalRtcpTransport : public transport::PacedPacketSender {
}
virtual bool ResendPackets(
- const transport::SendPacketVector& packets) OVERRIDE {
+ const transport::SendPacketVector& packets,
+ base::TimeDelta dedupe_window) OVERRIDE {
return false;
}
diff --git a/media/cast/test/cast_benchmarks.cc b/media/cast/test/cast_benchmarks.cc
index c3468a2cdb..66257626bd 100644
--- a/media/cast/test/cast_benchmarks.cc
+++ b/media/cast/test/cast_benchmarks.cc
@@ -212,9 +212,10 @@ class CastTransportSenderWrapper : public transport::CastTransportSender {
virtual void ResendPackets(
bool is_audio,
const MissingFramesAndPacketsMap& missing_packets,
- bool cancel_rtx_if_not_in_list) OVERRIDE {
+ bool cancel_rtx_if_not_in_list,
+ base::TimeDelta dedupe_window) OVERRIDE {
transport_->ResendPackets(
- is_audio, missing_packets, cancel_rtx_if_not_in_list);
+ is_audio, missing_packets, cancel_rtx_if_not_in_list, dedupe_window);
}
private:
diff --git a/media/cast/transport/cast_transport_sender.h b/media/cast/transport/cast_transport_sender.h
index 2556a8bd3d..e88f2f4f09 100644
--- a/media/cast/transport/cast_transport_sender.h
+++ b/media/cast/transport/cast_transport_sender.h
@@ -96,11 +96,14 @@ class CastTransportSender : public base::NonThreadSafe {
// frame to be re-transmitted.
// If |cancel_rtx_if_not_in_list| is used as an optimization to cancel
// pending re-transmission requests of packets not listed in
- // |missing_packets|.
+ // |missing_packets|. If the requested packet(s) were sent recently
+ // (how long is specified by |dedupe_window|) then this re-transmit
+ // will be ignored.
virtual void ResendPackets(
bool is_audio,
const MissingFramesAndPacketsMap& missing_packets,
- bool cancel_rtx_if_not_in_list) = 0;
+ bool cancel_rtx_if_not_in_list,
+ base::TimeDelta dedupe_window) = 0;
};
} // namespace transport
diff --git a/media/cast/transport/cast_transport_sender_impl.cc b/media/cast/transport/cast_transport_sender_impl.cc
index 2f51a934e7..6fd848f27b 100644
--- a/media/cast/transport/cast_transport_sender_impl.cc
+++ b/media/cast/transport/cast_transport_sender_impl.cc
@@ -7,6 +7,7 @@
#include "base/single_thread_task_runner.h"
#include "media/cast/transport/cast_transport_config.h"
#include "media/cast/transport/cast_transport_defines.h"
+#include "net/base/net_util.h"
namespace media {
namespace cast {
@@ -66,6 +67,11 @@ CastTransportSenderImpl::CastTransportSenderImpl(
this,
&CastTransportSenderImpl::SendRawEvents);
}
+ if (transport_) {
+ // The default DSCP value for cast is AF41. Which gives it a higher
+ // priority over other traffic.
+ transport_->SetDscp(net::DSCP_AF41);
+ }
}
CastTransportSenderImpl::~CastTransportSenderImpl() {
@@ -178,15 +184,18 @@ void CastTransportSenderImpl::SendRtcpFromRtpSender(
void CastTransportSenderImpl::ResendPackets(
bool is_audio,
const MissingFramesAndPacketsMap& missing_packets,
- bool cancel_rtx_if_not_in_list) {
+ bool cancel_rtx_if_not_in_list,
+ base::TimeDelta dedupe_window) {
if (is_audio) {
DCHECK(audio_sender_) << "Audio sender uninitialized";
audio_sender_->ResendPackets(missing_packets,
- cancel_rtx_if_not_in_list);
+ cancel_rtx_if_not_in_list,
+ dedupe_window);
} else {
DCHECK(video_sender_) << "Video sender uninitialized";
video_sender_->ResendPackets(missing_packets,
- cancel_rtx_if_not_in_list);
+ cancel_rtx_if_not_in_list,
+ dedupe_window);
}
}
diff --git a/media/cast/transport/cast_transport_sender_impl.h b/media/cast/transport/cast_transport_sender_impl.h
index 4fc074c0b9..035ef844b6 100644
--- a/media/cast/transport/cast_transport_sender_impl.h
+++ b/media/cast/transport/cast_transport_sender_impl.h
@@ -67,7 +67,8 @@ class CastTransportSenderImpl : public CastTransportSender {
virtual void ResendPackets(bool is_audio,
const MissingFramesAndPacketsMap& missing_packets,
- bool cancel_rtx_if_not_in_list)
+ bool cancel_rtx_if_not_in_list,
+ base::TimeDelta dedupe_window)
OVERRIDE;
private:
diff --git a/media/cast/transport/pacing/mock_paced_packet_sender.h b/media/cast/transport/pacing/mock_paced_packet_sender.h
index 9f6d204f7f..20b7647035 100644
--- a/media/cast/transport/pacing/mock_paced_packet_sender.h
+++ b/media/cast/transport/pacing/mock_paced_packet_sender.h
@@ -18,7 +18,8 @@ class MockPacedPacketSender : public PacedPacketSender {
virtual ~MockPacedPacketSender();
MOCK_METHOD1(SendPackets, bool(const SendPacketVector& packets));
- MOCK_METHOD1(ResendPackets, bool(const SendPacketVector& packets));
+ MOCK_METHOD2(ResendPackets, bool(const SendPacketVector& packets,
+ base::TimeDelta dedupe_window));
MOCK_METHOD2(SendRtcpPacket, bool(unsigned int ssrc, PacketRef packet));
MOCK_METHOD1(CancelSendingPacket, void(const PacketKey& packet_key));
};
diff --git a/media/cast/transport/pacing/paced_sender.cc b/media/cast/transport/pacing/paced_sender.cc
index 10b4224075..20cbde85be 100644
--- a/media/cast/transport/pacing/paced_sender.cc
+++ b/media/cast/transport/pacing/paced_sender.cc
@@ -20,6 +20,7 @@ static const int64 kPacingIntervalMs = 10;
static const size_t kPacingMaxBurstsPerFrame = 3;
static const size_t kTargetBurstSize = 10;
static const size_t kMaxBurstSize = 20;
+static const size_t kMaxDedupeWindowMs = 500;
} // namespace
@@ -73,11 +74,21 @@ bool PacedSender::SendPackets(const SendPacketVector& packets) {
return true;
}
-bool PacedSender::ResendPackets(const SendPacketVector& packets) {
+bool PacedSender::ResendPackets(const SendPacketVector& packets,
+ base::TimeDelta dedupe_window) {
if (packets.empty()) {
return true;
}
+ base::TimeTicks now = clock_->NowTicks();
for (size_t i = 0; i < packets.size(); i++) {
+ std::map<PacketKey, base::TimeTicks>::const_iterator j =
+ sent_time_.find(packets[i].first);
+
+ if (j != sent_time_.end() && now - j->second < dedupe_window) {
+ LogPacketEvent(packets[i].second->data, PACKET_RTX_REJECTED);
+ continue;
+ }
+
packet_list_[packets[i].first] =
make_pair(PacketType_Resend, packets[i].second);
}
@@ -108,11 +119,13 @@ void PacedSender::CancelSendingPacket(const PacketKey& packet_key) {
packet_list_.erase(packet_key);
}
-PacketRef PacedSender::GetNextPacket(PacketType* packet_type) {
+PacketRef PacedSender::GetNextPacket(PacketType* packet_type,
+ PacketKey* packet_key) {
std::map<PacketKey, std::pair<PacketType, PacketRef> >::iterator i;
i = packet_list_.begin();
DCHECK(i != packet_list_.end());
*packet_type = i->second.first;
+ *packet_key = i->first;
PacketRef ret = i->second.second;
packet_list_.erase(i);
return ret;
@@ -185,14 +198,17 @@ void PacedSender::SendStoredPackets() {
return;
}
PacketType packet_type;
- PacketRef packet = GetNextPacket(&packet_type);
+ PacketKey packet_key;
+ PacketRef packet = GetNextPacket(&packet_type, &packet_key);
+ sent_time_[packet_key] = now;
+ sent_time_buffer_[packet_key] = now;
switch (packet_type) {
case PacketType_Resend:
- LogPacketEvent(packet->data, true);
+ LogPacketEvent(packet->data, PACKET_RETRANSMITTED);
break;
case PacketType_Normal:
- LogPacketEvent(packet->data, false);
+ LogPacketEvent(packet->data, PACKET_SENT_TO_NETWORK);
break;
case PacketType_RTCP:
break;
@@ -203,10 +219,20 @@ void PacedSender::SendStoredPackets() {
}
current_burst_size_++;
}
+ // Keep ~0.5 seconds of data (1000 packets)
+ if (sent_time_buffer_.size() >=
+ kMaxBurstSize * kMaxDedupeWindowMs / kPacingIntervalMs) {
+ sent_time_.swap(sent_time_buffer_);
+ sent_time_buffer_.clear();
+ }
+ DCHECK_LE(sent_time_buffer_.size(),
+ kMaxBurstSize * kMaxDedupeWindowMs / kPacingIntervalMs);
+ DCHECK_LE(sent_time_.size(),
+ 2 * kMaxBurstSize * kMaxDedupeWindowMs / kPacingIntervalMs);
state_ = State_Unblocked;
}
-void PacedSender::LogPacketEvent(const Packet& packet, bool retransmit) {
+void PacedSender::LogPacketEvent(const Packet& packet, CastLoggingEvent event) {
// Get SSRC from packet and compare with the audio_ssrc / video_ssrc to see
// if the packet is audio or video.
DCHECK_GE(packet.size(), 12u);
@@ -224,8 +250,6 @@ void PacedSender::LogPacketEvent(const Packet& packet, bool retransmit) {
return;
}
- CastLoggingEvent event = retransmit ?
- PACKET_RETRANSMITTED : PACKET_SENT_TO_NETWORK;
EventMediaType media_type = is_audio ? AUDIO_EVENT : VIDEO_EVENT;
logging_->InsertSinglePacketEvent(clock_->NowTicks(), event, media_type,
packet);
diff --git a/media/cast/transport/pacing/paced_sender.h b/media/cast/transport/pacing/paced_sender.h
index 2373fb5966..9fc0c8b8b8 100644
--- a/media/cast/transport/pacing/paced_sender.h
+++ b/media/cast/transport/pacing/paced_sender.h
@@ -41,7 +41,8 @@ typedef std::vector<std::pair<PacketKey, PacketRef> > SendPacketVector;
class PacedPacketSender {
public:
virtual bool SendPackets(const SendPacketVector& packets) = 0;
- virtual bool ResendPackets(const SendPacketVector& packets) = 0;
+ virtual bool ResendPackets(const SendPacketVector& packets,
+ base::TimeDelta dedupe_window) = 0;
virtual bool SendRtcpPacket(uint32 ssrc, PacketRef packet) = 0;
virtual void CancelSendingPacket(const PacketKey& packet_key) = 0;
@@ -72,14 +73,15 @@ class PacedSender : public PacedPacketSender,
// PacedPacketSender implementation.
virtual bool SendPackets(const SendPacketVector& packets) OVERRIDE;
- virtual bool ResendPackets(const SendPacketVector& packets) OVERRIDE;
+ virtual bool ResendPackets(const SendPacketVector& packets,
+ base::TimeDelta dedupe_window) OVERRIDE;
virtual bool SendRtcpPacket(uint32 ssrc, PacketRef packet) OVERRIDE;
virtual void CancelSendingPacket(const PacketKey& packet_key) OVERRIDE;
private:
// Actually sends the packets to the transport.
void SendStoredPackets();
- void LogPacketEvent(const Packet& packet, bool retransmit);
+ void LogPacketEvent(const Packet& packet, CastLoggingEvent event);
enum PacketType {
PacketType_RTCP,
@@ -108,7 +110,8 @@ class PacedSender : public PacedPacketSender,
// Returns the next packet to send. RTCP packets have highest priority,
// resend packets have second highest priority and then comes everything
// else.
- PacketRef GetNextPacket(PacketType* packet_type);
+ PacketRef GetNextPacket(PacketType* packet_type,
+ PacketKey* packet_key);
base::TickClock* const clock_; // Not owned by this class.
LoggingImpl* const logging_; // Not owned by this class.
@@ -117,6 +120,8 @@ class PacedSender : public PacedPacketSender,
uint32 audio_ssrc_;
uint32 video_ssrc_;
std::map<PacketKey, std::pair<PacketType, PacketRef> > packet_list_;
+ std::map<PacketKey, base::TimeTicks> sent_time_;
+ std::map<PacketKey, base::TimeTicks> sent_time_buffer_;
// Maximum burst size for the next three bursts.
size_t max_burst_size_;
diff --git a/media/cast/transport/pacing/paced_sender_unittest.cc b/media/cast/transport/pacing/paced_sender_unittest.cc
index ef9d89b5f0..5e24fca4b5 100644
--- a/media/cast/transport/pacing/paced_sender_unittest.cc
+++ b/media/cast/transport/pacing/paced_sender_unittest.cc
@@ -129,7 +129,7 @@ TEST_F(PacedSenderTest, PassThroughRtcp) {
SendPacketVector packets = CreateSendPacketVector(kSize1, 1, true);
EXPECT_TRUE(paced_sender_->SendPackets(packets));
- EXPECT_TRUE(paced_sender_->ResendPackets(packets));
+ EXPECT_TRUE(paced_sender_->ResendPackets(packets, base::TimeDelta()));
mock_transport_.AddExpectedSize(kSize2, 1);
Packet tmp(kSize2, kValue);
@@ -202,7 +202,7 @@ TEST_F(PacedSenderTest, PaceWithNack) {
EXPECT_TRUE(paced_sender_->SendPackets(first_frame_packets));
// Add first NACK request.
- EXPECT_TRUE(paced_sender_->ResendPackets(nack_packets));
+ EXPECT_TRUE(paced_sender_->ResendPackets(nack_packets, base::TimeDelta()));
// Check that we get the first NACK burst.
mock_transport_.AddExpectedSize(kNackSize, 10);
@@ -211,7 +211,7 @@ TEST_F(PacedSenderTest, PaceWithNack) {
task_runner_->RunTasks();
// Add second NACK request.
- EXPECT_TRUE(paced_sender_->ResendPackets(nack_packets));
+ EXPECT_TRUE(paced_sender_->ResendPackets(nack_packets, base::TimeDelta()));
// Check that we get the next NACK burst.
mock_transport_.AddExpectedSize(kNackSize, 10);
diff --git a/media/cast/transport/rtp_sender/rtp_sender.cc b/media/cast/transport/rtp_sender/rtp_sender.cc
index 2604d253de..b807b34757 100644
--- a/media/cast/transport/rtp_sender/rtp_sender.cc
+++ b/media/cast/transport/rtp_sender/rtp_sender.cc
@@ -4,6 +4,7 @@
#include "media/cast/transport/rtp_sender/rtp_sender.h"
+#include "base/big_endian.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "media/cast/transport/cast_transport_defines.h"
@@ -75,7 +76,8 @@ void RtpSender::SendFrame(const EncodedFrame& frame) {
void RtpSender::ResendPackets(
const MissingFramesAndPacketsMap& missing_frames_and_packets,
- bool cancel_rtx_if_not_in_list) {
+ bool cancel_rtx_if_not_in_list,
+ base::TimeDelta dedupe_window) {
DCHECK(storage_);
// Iterate over all frames in the list.
for (MissingFramesAndPacketsMap::const_iterator it =
@@ -129,15 +131,18 @@ void RtpSender::ResendPackets(
transport_->CancelSendingPacket(it->first);
}
}
- transport_->ResendPackets(packets_to_resend);
+ transport_->ResendPackets(packets_to_resend, dedupe_window);
}
}
void RtpSender::UpdateSequenceNumber(Packet* packet) {
- uint16 new_sequence_number = packetizer_->NextSequenceNumber();
- int index = 2;
- (*packet)[index] = (static_cast<uint8>(new_sequence_number));
- (*packet)[index + 1] = (static_cast<uint8>(new_sequence_number >> 8));
+ // TODO(miu): This is an abstraction violation. This needs to be a part of
+ // the overall packet (de)serialization consolidation.
+ static const int kByteOffsetToSequenceNumber = 2;
+ base::BigEndianWriter big_endian_writer(
+ reinterpret_cast<char*>((&packet->front()) + kByteOffsetToSequenceNumber),
+ sizeof(uint16));
+ big_endian_writer.WriteU16(packetizer_->NextSequenceNumber());
}
} // namespace transport
diff --git a/media/cast/transport/rtp_sender/rtp_sender.h b/media/cast/transport/rtp_sender/rtp_sender.h
index bfb46cb09e..e65326abf1 100644
--- a/media/cast/transport/rtp_sender/rtp_sender.h
+++ b/media/cast/transport/rtp_sender/rtp_sender.h
@@ -51,7 +51,8 @@ class RtpSender {
void SendFrame(const EncodedFrame& frame);
void ResendPackets(const MissingFramesAndPacketsMap& missing_packets,
- bool cancel_rtx_if_not_in_list);
+ bool cancel_rtx_if_not_in_list,
+ base::TimeDelta dedupe_window);
size_t send_packet_count() const {
return packetizer_ ? packetizer_->send_packet_count() : 0;
diff --git a/media/cast/transport/transport/udp_transport.cc b/media/cast/transport/transport/udp_transport.cc
index bcce4f72f3..9669b17d43 100644
--- a/media/cast/transport/transport/udp_transport.cc
+++ b/media/cast/transport/transport/udp_transport.cc
@@ -54,6 +54,7 @@ UdpTransport::UdpTransport(
send_pending_(false),
receive_pending_(false),
client_connected_(false),
+ next_dscp_value_(net::DSCP_NO_CHANGE),
status_callback_(status_callback),
weak_factory_(this) {
DCHECK(!IsEmpty(local_end_point) || !IsEmpty(remote_end_point));
@@ -88,6 +89,11 @@ void UdpTransport::StartReceiving(
ScheduleReceiveNextPacket();
}
+void UdpTransport::SetDscp(net::DiffServCodePoint dscp) {
+ DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread());
+ next_dscp_value_ = dscp;
+}
+
void UdpTransport::ScheduleReceiveNextPacket() {
DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread());
if (!packet_receiver_.is_null() && !receive_pending_) {
@@ -162,6 +168,16 @@ bool UdpTransport::SendPacket(PacketRef packet, const base::Closure& cb) {
return true;
}
+ if (next_dscp_value_ != net::DSCP_NO_CHANGE) {
+ int result = udp_socket_->SetDiffServCodePoint(next_dscp_value_);
+ if (result != net::OK) {
+ LOG(ERROR) << "Unable to set DSCP: " << next_dscp_value_
+ << " to socket; Error: " << result;
+ }
+ // Don't change DSCP in next send.
+ next_dscp_value_ = net::DSCP_NO_CHANGE;
+ }
+
scoped_refptr<net::IOBuffer> buf =
new net::WrappedIOBuffer(reinterpret_cast<char*>(&packet->data.front()));
diff --git a/media/cast/transport/transport/udp_transport.h b/media/cast/transport/transport/udp_transport.h
index 17b0d77599..1a568501d5 100644
--- a/media/cast/transport/transport/udp_transport.h
+++ b/media/cast/transport/transport/udp_transport.h
@@ -12,6 +12,7 @@
#include "media/cast/transport/cast_transport_config.h"
#include "media/cast/transport/cast_transport_sender.h"
#include "net/base/ip_endpoint.h"
+#include "net/base/net_util.h"
#include "net/udp/udp_socket.h"
namespace net {
@@ -46,6 +47,10 @@ class UdpTransport : public PacketSender {
// Start receiving packets. Packets are submitted to |packet_receiver|.
void StartReceiving(const PacketReceiverCallback& packet_receiver);
+ // Set a new DSCP value to the socket. The value will be set right before
+ // the next send.
+ void SetDscp(net::DiffServCodePoint dscp);
+
// PacketSender implementations.
virtual bool SendPacket(PacketRef packet,
const base::Closure& cb) OVERRIDE;
@@ -72,6 +77,7 @@ class UdpTransport : public PacketSender {
bool send_pending_;
bool receive_pending_;
bool client_connected_;
+ net::DiffServCodePoint next_dscp_value_;
scoped_ptr<Packet> next_packet_;
scoped_refptr<net::WrappedIOBuffer> recv_buf_;
net::IPEndPoint recv_addr_;
diff --git a/media/cast/video_sender/video_sender.cc b/media/cast/video_sender/video_sender.cc
index cc8b158950..cf050b7f10 100644
--- a/media/cast/video_sender/video_sender.cc
+++ b/media/cast/video_sender/video_sender.cc
@@ -78,7 +78,7 @@ VideoSender::VideoSender(
media::cast::transport::CastTransportVideoConfig transport_config;
transport_config.codec = video_config.codec;
transport_config.rtp.config = video_config.rtp_config;
- transport_config.rtp.max_outstanding_frames = max_unacked_frames_ + 1;
+ transport_config.rtp.max_outstanding_frames = max_unacked_frames_;
transport_sender_->InitializeVideo(transport_config);
rtcp_.SetCastReceiverEventHistorySize(kReceiverRtcpEventHistorySize);
@@ -321,7 +321,7 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
// A NACK is also used to cancel pending re-transmissions.
transport_sender_->ResendPackets(
- false, cast_feedback.missing_frames_and_packets_, true);
+ false, cast_feedback.missing_frames_and_packets_, true, rtt);
}
base::TimeTicks now = cast_environment_->Clock()->NowTicks();
@@ -348,7 +348,8 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
latest_acked_frame_id_++;
missing_frames_and_packets[latest_acked_frame_id_] = missing;
}
- transport_sender_->ResendPackets(false, missing_frames_and_packets, true);
+ transport_sender_->ResendPackets(
+ false, missing_frames_and_packets, true, rtt);
latest_acked_frame_id_ = cast_feedback.ack_frame_id_;
}
}
@@ -382,10 +383,16 @@ void VideoSender::ResendForKickstart() {
std::make_pair(last_sent_frame_id_, missing));
last_send_time_ = cast_environment_->Clock()->NowTicks();
+ base::TimeDelta rtt;
+ base::TimeDelta avg_rtt;
+ base::TimeDelta min_rtt;
+ base::TimeDelta max_rtt;
+ rtcp_.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt);
+
// Sending this extra packet is to kick-start the session. There is
// no need to optimize re-transmission for this case.
transport_sender_->ResendPackets(false, missing_frames_and_packets,
- false);
+ false, rtt);
}
} // namespace cast
diff --git a/media/cast/video_sender/video_sender.h b/media/cast/video_sender/video_sender.h
index 30066cbbf8..cf8d27511c 100644
--- a/media/cast/video_sender/video_sender.h
+++ b/media/cast/video_sender/video_sender.h
@@ -76,15 +76,13 @@ class VideoSender : public RtcpSenderFeedback,
void ScheduleNextRtcpReport();
void SendRtcpReport(bool schedule_future_reports);
- // Schedule and execute periodic checks for re-sending frames. If no
+ // Schedule and execute periodic checks for re-sending packets. If no
// acknowledgements have been received for "too long," VideoSender will
- // speculatively re-send the frame just after |latest_acked_frame_id_| (the
- // whole frame). This is a last resort tactic to prevent the session from
+ // speculatively re-send certain packets of an unacked frame to kick-start
+ // re-transmission. This is a last resort tactic to prevent the session from
// getting stuck after a long outage.
void ScheduleNextResendCheck();
void ResendCheck();
-
- // Resend certain packets of an unacked frame to kick start re-transmission.
void ResendForKickstart();
// Returns true if there are too many frames in flight, as defined by the
@@ -169,10 +167,7 @@ class VideoSender : public RtcpSenderFeedback,
// This is a "good enough" mapping for finding the RTP timestamp associated
// with a video frame. The key is the lowest 8 bits of frame id (which is
- // what is sent via RTCP). This map is used for logging purposes. The only
- // time when this mapping will be incorrect is when it receives an ACK for a
- // old enough frame such that 8-bit wrap around has already occurred, which
- // should be pretty rare.
+ // what is sent via RTCP). This map is used for logging purposes.
RtpTimestamp frame_id_to_rtp_timestamp_[256];
// NOTE: Weak pointers must be invalidated before all other member variables.