blob: ca59791248f46d82f03cfc7a8926dd8e9081992a [file] [log] [blame]
/*
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h"
#include <algorithm>
#include <cmath>
#include <type_traits>
#include <utility>
#include <vector>
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
namespace {
static constexpr int kFieldValueSize = 3;
static constexpr int kFieldSize = 1 + kFieldValueSize;
static constexpr DataRate kDataRateResolution = DataRate::KilobitsPerSec(1);
constexpr int64_t kMaxEncoded = (1 << (kFieldValueSize * 8)) - 1;
class DataRateSerializer {
public:
DataRateSerializer(
uint8_t id,
std::function<DataRate*(NetworkStateEstimate*)> field_getter)
: id_(id), field_getter_(field_getter) {}
uint8_t id() const { return id_; }
void Read(const uint8_t* src, NetworkStateEstimate* target) const {
int64_t scaled = ByteReader<uint32_t, kFieldValueSize>::ReadBigEndian(src);
if (scaled == kMaxEncoded) {
*field_getter_(target) = DataRate::PlusInfinity();
} else {
*field_getter_(target) = kDataRateResolution * scaled;
}
}
bool Write(const NetworkStateEstimate& src, uint8_t* target) const {
auto value = *field_getter_(const_cast<NetworkStateEstimate*>(&src));
if (value.IsMinusInfinity()) {
RTC_LOG(LS_WARNING) << "Trying to serialize MinusInfinity";
return false;
}
ByteWriter<uint8_t>::WriteBigEndian(target++, id_);
int64_t scaled;
if (value.IsPlusInfinity()) {
scaled = kMaxEncoded;
} else {
scaled = value / kDataRateResolution;
if (scaled >= kMaxEncoded) {
scaled = kMaxEncoded;
RTC_LOG(LS_WARNING) << ToString(value) << " is larger than max ("
<< ToString(kMaxEncoded * kDataRateResolution)
<< "), encoded as PlusInfinity.";
}
}
ByteWriter<uint32_t, kFieldValueSize>::WriteBigEndian(target, scaled);
return true;
}
private:
const uint8_t id_;
const std::function<DataRate*(NetworkStateEstimate*)> field_getter_;
};
class RemoteEstimateSerializerImpl : public RemoteEstimateSerializer {
public:
explicit RemoteEstimateSerializerImpl(std::vector<DataRateSerializer> fields)
: fields_(fields) {}
rtc::Buffer Serialize(const NetworkStateEstimate& src) const override {
size_t max_size = fields_.size() * kFieldSize;
size_t size = 0;
rtc::Buffer buf(max_size);
for (const auto& field : fields_) {
if (field.Write(src, buf.data() + size)) {
size += kFieldSize;
}
}
buf.SetSize(size);
return buf;
}
bool Parse(rtc::ArrayView<const uint8_t> src,
NetworkStateEstimate* target) const override {
if (src.size() % kFieldSize != 0)
return false;
RTC_DCHECK_EQ(src.size() % kFieldSize, 0);
for (const uint8_t* data_ptr = src.data(); data_ptr < src.end();
data_ptr += kFieldSize) {
uint8_t field_id = ByteReader<uint8_t>::ReadBigEndian(data_ptr);
for (const auto& field : fields_) {
if (field.id() == field_id) {
field.Read(data_ptr + 1, target);
break;
}
}
}
return true;
}
private:
const std::vector<DataRateSerializer> fields_;
};
} // namespace
const RemoteEstimateSerializer* GetRemoteEstimateSerializer() {
using E = NetworkStateEstimate;
static auto* serializer = new RemoteEstimateSerializerImpl({
{1, [](E* e) { return &e->link_capacity_lower; }},
{2, [](E* e) { return &e->link_capacity_upper; }},
});
return serializer;
}
RemoteEstimate::RemoteEstimate() : serializer_(GetRemoteEstimateSerializer()) {
SetSubType(kSubType);
SetName(kName);
SetSenderSsrc(0);
}
RemoteEstimate::RemoteEstimate(App&& app)
: App(std::move(app)), serializer_(GetRemoteEstimateSerializer()) {}
bool RemoteEstimate::ParseData() {
return serializer_->Parse({data(), data_size()}, &estimate_);
}
void RemoteEstimate::SetEstimate(NetworkStateEstimate estimate) {
estimate_ = estimate;
auto buf = serializer_->Serialize(estimate);
SetData(buf.data(), buf.size());
}
} // namespace rtcp
} // namespace webrtc