| Index: media/cast/sender/frame_sender.cc
|
| diff --git a/media/cast/sender/frame_sender.cc b/media/cast/sender/frame_sender.cc
|
| index 61f8b1af1ac2b95cbb198205e6aaae3263a49c4d..f338dd9b2b065ce91a7ea98332711478916ff16f 100644
|
| --- a/media/cast/sender/frame_sender.cc
|
| +++ b/media/cast/sender/frame_sender.cc
|
| @@ -13,14 +13,13 @@ const int kMinSchedulingDelayMs = 1;
|
| FrameSender::FrameSender(scoped_refptr<CastEnvironment> cast_environment,
|
| CastTransportSender* const transport_sender,
|
| base::TimeDelta rtcp_interval,
|
| - int frequency,
|
| + int rtp_timebase,
|
| uint32 ssrc,
|
| double max_frame_rate,
|
| base::TimeDelta playout_delay)
|
| : cast_environment_(cast_environment),
|
| transport_sender_(transport_sender),
|
| ssrc_(ssrc),
|
| - rtp_timestamp_helper_(frequency),
|
| rtt_available_(false),
|
| rtcp_interval_(rtcp_interval),
|
| max_frame_rate_(max_frame_rate),
|
| @@ -28,10 +27,12 @@ FrameSender::FrameSender(scoped_refptr<CastEnvironment> cast_environment,
|
| last_sent_frame_id_(0),
|
| latest_acked_frame_id_(0),
|
| duplicate_ack_counter_(0),
|
| + rtp_timebase_(rtp_timebase),
|
| weak_factory_(this) {
|
| + DCHECK_GT(rtp_timebase_, 0);
|
| SetTargetPlayoutDelay(playout_delay);
|
| send_target_playout_delay_ = false;
|
| - memset(frame_id_to_rtp_timestamp_, 0, sizeof(frame_id_to_rtp_timestamp_));
|
| + memset(frame_rtp_timestamps_, 0, sizeof(frame_rtp_timestamps_));
|
| }
|
|
|
| FrameSender::~FrameSender() {
|
| @@ -54,15 +55,25 @@ void FrameSender::ScheduleNextRtcpReport() {
|
|
|
| void FrameSender::SendRtcpReport(bool schedule_future_reports) {
|
| DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| +
|
| + // Sanity-check: We should have sent at least the first frame by this point.
|
| + DCHECK(!last_send_time_.is_null());
|
| +
|
| + // Create lip-sync info for the sender report. The last sent frame's
|
| + // reference time and RTP timestamp are used to estimate an RTP timestamp in
|
| + // terms of "now." Note that |now| is never likely to be precise to an exact
|
| + // frame boundary; and so the computation here will result in a
|
| + // |now_as_rtp_timestamp| value that is rarely equal to any one emitted by the
|
| + // encoder.
|
| const base::TimeTicks now = cast_environment_->Clock()->NowTicks();
|
| - uint32 now_as_rtp_timestamp = 0;
|
| - if (rtp_timestamp_helper_.GetCurrentTimeAsRtpTimestamp(
|
| - now, &now_as_rtp_timestamp)) {
|
| - transport_sender_->SendSenderReport(ssrc_, now, now_as_rtp_timestamp);
|
| - } else {
|
| - // |rtp_timestamp_helper_| should have stored a mapping by this point.
|
| - NOTREACHED();
|
| - }
|
| + const base::TimeDelta time_delta =
|
| + now - GetRecordedReferenceTime(last_sent_frame_id_);
|
| + const int64 rtp_delta = TimeDeltaToRtpDelta(time_delta, rtp_timebase_);
|
| + const uint32 now_as_rtp_timestamp =
|
| + GetRecordedRtpTimestamp(last_sent_frame_id_) +
|
| + static_cast<uint32>(rtp_delta);
|
| + transport_sender_->SendSenderReport(ssrc_, now, now_as_rtp_timestamp);
|
| +
|
| if (schedule_future_reports)
|
| ScheduleNextRtcpReport();
|
| }
|
| @@ -129,5 +140,23 @@ void FrameSender::ResendForKickstart() {
|
| transport_sender_->ResendFrameForKickstart(ssrc_, last_sent_frame_id_);
|
| }
|
|
|
| +void FrameSender::RecordLatestFrameTimestamps(uint32 frame_id,
|
| + base::TimeTicks reference_time,
|
| + RtpTimestamp rtp_timestamp) {
|
| + DCHECK(!reference_time.is_null());
|
| + frame_reference_times_[frame_id % arraysize(frame_reference_times_)] =
|
| + reference_time;
|
| + frame_rtp_timestamps_[frame_id % arraysize(frame_rtp_timestamps_)] =
|
| + rtp_timestamp;
|
| +}
|
| +
|
| +base::TimeTicks FrameSender::GetRecordedReferenceTime(uint32 frame_id) const {
|
| + return frame_reference_times_[frame_id % arraysize(frame_reference_times_)];
|
| +}
|
| +
|
| +RtpTimestamp FrameSender::GetRecordedRtpTimestamp(uint32 frame_id) const {
|
| + return frame_rtp_timestamps_[frame_id % arraysize(frame_rtp_timestamps_)];
|
| +}
|
| +
|
| } // namespace cast
|
| } // namespace media
|
|
|