blob: b401ead8d9d18d978611a02598a07ee72a96f5e6 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <utility>
#include "base/check.h"
#include "base/time/tick_clock.h"
#include "media/cast/logging/receiver_time_offset_estimator_impl.h"
namespace media {
namespace cast {
namespace {
// Bitwise merging of values to produce an ordered key for entries in the
// BoundCalculator::events_ map.
uint64_t MakeEventKey(RtpTimeTicks rtp, uint16_t packet_id, bool audio) {
return (static_cast<uint64_t>(rtp.lower_32_bits()) << 32) |
(static_cast<uint64_t>(packet_id) << 1) |
(audio ? UINT64_C(1) : UINT64_C(0));
}
} // namespace
ReceiverTimeOffsetEstimatorImpl::BoundCalculator::BoundCalculator()
: has_bound_(false) {}
ReceiverTimeOffsetEstimatorImpl::BoundCalculator::~BoundCalculator() = default;
void ReceiverTimeOffsetEstimatorImpl::BoundCalculator::SetSent(
RtpTimeTicks rtp,
uint16_t packet_id,
bool audio,
base::TimeTicks t) {
const uint64_t key = MakeEventKey(rtp, packet_id, audio);
events_[key].first = t;
CheckUpdate(key);
}
void ReceiverTimeOffsetEstimatorImpl::BoundCalculator::SetReceived(
RtpTimeTicks rtp,
uint16_t packet_id,
bool audio,
base::TimeTicks t) {
const uint64_t key = MakeEventKey(rtp, packet_id, audio);
events_[key].second = t;
CheckUpdate(key);
}
void ReceiverTimeOffsetEstimatorImpl::BoundCalculator::UpdateBound(
base::TimeTicks sent, base::TimeTicks received) {
base::TimeDelta delta = received - sent;
if (has_bound_) {
if (delta < bound_) {
bound_ = delta;
} else {
bound_ += (delta - bound_) / kClockDriftSpeed;
}
} else {
bound_ = delta;
}
has_bound_ = true;
}
void ReceiverTimeOffsetEstimatorImpl::BoundCalculator::CheckUpdate(
uint64_t key) {
const TimeTickPair& ticks = events_[key];
if (!ticks.first.is_null() && !ticks.second.is_null()) {
UpdateBound(ticks.first, ticks.second);
events_.erase(key);
return;
}
if (events_.size() > kMaxEventTimesMapSize) {
auto i = ModMapOldest(&events_);
if (i != events_.end()) {
events_.erase(i);
}
}
}
ReceiverTimeOffsetEstimatorImpl::ReceiverTimeOffsetEstimatorImpl() = default;
ReceiverTimeOffsetEstimatorImpl::~ReceiverTimeOffsetEstimatorImpl() {
DCHECK(thread_checker_.CalledOnValidThread());
}
void ReceiverTimeOffsetEstimatorImpl::OnReceiveFrameEvent(
const FrameEvent& frame_event) {
DCHECK(thread_checker_.CalledOnValidThread());
switch (frame_event.type) {
case FRAME_ACK_SENT:
lower_bound_.SetSent(frame_event.rtp_timestamp,
0,
frame_event.media_type == AUDIO_EVENT,
frame_event.timestamp);
break;
case FRAME_ACK_RECEIVED:
lower_bound_.SetReceived(frame_event.rtp_timestamp,
0,
frame_event.media_type == AUDIO_EVENT,
frame_event.timestamp);
break;
default:
// Ignored
break;
}
}
bool ReceiverTimeOffsetEstimatorImpl::GetReceiverOffsetBounds(
base::TimeDelta* lower_bound,
base::TimeDelta* upper_bound) {
if (!lower_bound_.has_bound() || !upper_bound_.has_bound())
return false;
*lower_bound = -lower_bound_.bound();
*upper_bound = upper_bound_.bound();
// Sanitize the output, we don't want the upper bound to be
// lower than the lower bound, make them the same.
if (upper_bound < lower_bound) {
lower_bound += (lower_bound - upper_bound) / 2;
upper_bound = lower_bound;
}
return true;
}
void ReceiverTimeOffsetEstimatorImpl::OnReceivePacketEvent(
const PacketEvent& packet_event) {
DCHECK(thread_checker_.CalledOnValidThread());
switch (packet_event.type) {
case PACKET_SENT_TO_NETWORK:
upper_bound_.SetSent(packet_event.rtp_timestamp,
packet_event.packet_id,
packet_event.media_type == AUDIO_EVENT,
packet_event.timestamp);
break;
case PACKET_RECEIVED:
upper_bound_.SetReceived(packet_event.rtp_timestamp,
packet_event.packet_id,
packet_event.media_type == AUDIO_EVENT,
packet_event.timestamp);
break;
default:
// Ignored
break;
}
}
} // namespace cast
} // namespace media