Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,7 @@
hta@webrtc.org
mflodman@webrtc.org
tommi@webrtc.org
mbonadei@webrtc.org
per-file rate_statistics*=sprang@webrtc.org
per-file rate_statistics*=stefan@webrtc.org

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2015 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.
*/
#ifndef RTC_BASE_ARRAYSIZE_H_
#define RTC_BASE_ARRAYSIZE_H_
#include <stddef.h>
// This file defines the arraysize() macro and is derived from Chromium's
// base/macros.h.
// The arraysize(arr) macro returns the # of elements in an array arr.
// The expression is a compile-time constant, and therefore can be
// used in defining new arrays, for example. If you use arraysize on
// a pointer by mistake, you will get a compile-time error.
// This template function declaration is used in defining arraysize.
// Note that the function doesn't need an implementation, as we only
// use its type.
template <typename T, size_t N>
char (&ArraySizeHelper(T (&array)[N]))[N];
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
#endif // RTC_BASE_ARRAYSIZE_H_

View file

@ -0,0 +1,204 @@
/*
* Copyright 2023 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 "rtc_base/async_dns_resolver.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "api/make_ref_counted.h"
#include "rtc_base/logging.h"
#include "rtc_base/platform_thread.h"
#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
#include <dispatch/dispatch.h>
#endif
namespace webrtc {
namespace {
#ifdef __native_client__
int ResolveHostname(absl::string_view hostname,
int family,
std::vector<rtc::IPAddress>* addresses) {
RTC_DCHECK_NOTREACHED();
RTC_LOG(LS_WARNING) << "ResolveHostname() is not implemented for NaCl";
return -1;
}
#else // notdef(__native_client__)
int ResolveHostname(absl::string_view hostname,
int family,
std::vector<rtc::IPAddress>& addresses) {
addresses.clear();
struct addrinfo* result = nullptr;
struct addrinfo hints = {0};
hints.ai_family = family;
// `family` here will almost always be AF_UNSPEC, because `family` comes from
// AsyncResolver::addr_.family(), which comes from a SocketAddress constructed
// with a hostname. When a SocketAddress is constructed with a hostname, its
// family is AF_UNSPEC. However, if someday in the future we construct
// a SocketAddress with both a hostname and a family other than AF_UNSPEC,
// then it would be possible to get a specific family value here.
// The behavior of AF_UNSPEC is roughly "get both ipv4 and ipv6", as
// documented by the various operating systems:
// Linux: http://man7.org/linux/man-pages/man3/getaddrinfo.3.html
// Windows: https://msdn.microsoft.com/en-us/library/windows/desktop/
// ms738520(v=vs.85).aspx
// Mac: https://developer.apple.com/legacy/library/documentation/Darwin/
// Reference/ManPages/man3/getaddrinfo.3.html
// Android (source code, not documentation):
// https://android.googlesource.com/platform/bionic/+/
// 7e0bfb511e85834d7c6cb9631206b62f82701d60/libc/netbsd/net/getaddrinfo.c#1657
hints.ai_flags = AI_ADDRCONFIG;
int ret =
getaddrinfo(std::string(hostname).c_str(), nullptr, &hints, &result);
if (ret != 0) {
return ret;
}
struct addrinfo* cursor = result;
for (; cursor; cursor = cursor->ai_next) {
if (family == AF_UNSPEC || cursor->ai_family == family) {
rtc::IPAddress ip;
if (IPFromAddrInfo(cursor, &ip)) {
addresses.push_back(ip);
}
}
}
freeaddrinfo(result);
return 0;
}
#endif // !__native_client__
// Special task posting for Mac/iOS
#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
void GlobalGcdRunTask(void* context) {
std::unique_ptr<absl::AnyInvocable<void() &&>> task(
static_cast<absl::AnyInvocable<void() &&>*>(context));
std::move (*task)();
}
// Post a task into the system-defined global concurrent queue.
void PostTaskToGlobalQueue(
std::unique_ptr<absl::AnyInvocable<void() &&>> task) {
dispatch_async_f(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
task.release(), &GlobalGcdRunTask);
}
#endif // defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
} // namespace
class AsyncDnsResolver::State : public rtc::RefCountedBase {
public:
enum class Status {
kActive, // Running request, or able to be passed one
kFinished, // Request has finished processing
kDead // The owning AsyncDnsResolver has been deleted
};
static rtc::scoped_refptr<AsyncDnsResolver::State> Create() {
return rtc::make_ref_counted<AsyncDnsResolver::State>();
}
// Execute the passed function if the state is Active.
void Finish(absl::AnyInvocable<void()> function) {
webrtc::MutexLock lock(&mutex_);
if (status_ != Status::kActive) {
return;
}
status_ = Status::kFinished;
function();
}
void Kill() {
webrtc::MutexLock lock(&mutex_);
status_ = Status::kDead;
}
private:
webrtc::Mutex mutex_;
Status status_ RTC_GUARDED_BY(mutex_) = Status::kActive;
};
AsyncDnsResolver::AsyncDnsResolver() : state_(State::Create()) {}
AsyncDnsResolver::~AsyncDnsResolver() {
state_->Kill();
}
void AsyncDnsResolver::Start(const rtc::SocketAddress& addr,
absl::AnyInvocable<void()> callback) {
Start(addr, addr.family(), std::move(callback));
}
// Start address resolution of the hostname in `addr` matching `family`.
void AsyncDnsResolver::Start(const rtc::SocketAddress& addr,
int family,
absl::AnyInvocable<void()> callback) {
RTC_DCHECK_RUN_ON(&result_.sequence_checker_);
result_.addr_ = addr;
callback_ = std::move(callback);
auto thread_function = [this, addr, family, flag = safety_.flag(),
caller_task_queue = webrtc::TaskQueueBase::Current(),
state = state_] {
std::vector<rtc::IPAddress> addresses;
int error = ResolveHostname(addr.hostname(), family, addresses);
// We assume that the caller task queue is still around if the
// AsyncDnsResolver has not been destroyed.
state->Finish([this, error, flag, caller_task_queue,
addresses = std::move(addresses)]() {
caller_task_queue->PostTask(
SafeTask(flag, [this, error, addresses = std::move(addresses)] {
RTC_DCHECK_RUN_ON(&result_.sequence_checker_);
result_.addresses_ = addresses;
result_.error_ = error;
callback_();
}));
});
};
#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
PostTaskToGlobalQueue(
std::make_unique<absl::AnyInvocable<void() &&>>(thread_function));
#else
rtc::PlatformThread::SpawnDetached(std::move(thread_function),
"AsyncResolver");
#endif
}
const AsyncDnsResolverResult& AsyncDnsResolver::result() const {
return result_;
}
bool AsyncDnsResolverResultImpl::GetResolvedAddress(
int family,
rtc::SocketAddress* addr) const {
RTC_DCHECK_RUN_ON(&sequence_checker_);
RTC_DCHECK(addr);
if (error_ != 0 || addresses_.empty())
return false;
*addr = addr_;
for (const auto& address : addresses_) {
if (family == address.family()) {
addr->SetResolvedIP(address);
return true;
}
}
return false;
}
int AsyncDnsResolverResultImpl::GetError() const {
RTC_DCHECK_RUN_ON(&sequence_checker_);
return error_;
}
} // namespace webrtc

View file

@ -0,0 +1,63 @@
/*
* Copyright 2023 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.
*/
#ifndef RTC_BASE_ASYNC_DNS_RESOLVER_H_
#define RTC_BASE_ASYNC_DNS_RESOLVER_H_
#include <vector>
#include "api/async_dns_resolver.h"
#include "api/sequence_checker.h"
#include "api/task_queue/pending_task_safety_flag.h"
#include "rtc_base/ref_counted_object.h"
#include "rtc_base/system/rtc_export.h"
#include "rtc_base/thread_annotations.h"
namespace webrtc {
// This file contains a default implementation of
// webrtc::AsyncDnsResolverInterface, for use when there is no need for special
// treatment.
class AsyncDnsResolverResultImpl : public AsyncDnsResolverResult {
public:
bool GetResolvedAddress(int family, rtc::SocketAddress* addr) const override;
// Returns error from resolver.
int GetError() const override;
private:
friend class AsyncDnsResolver;
RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker sequence_checker_;
rtc::SocketAddress addr_ RTC_GUARDED_BY(sequence_checker_);
std::vector<rtc::IPAddress> addresses_ RTC_GUARDED_BY(sequence_checker_);
int error_ RTC_GUARDED_BY(sequence_checker_);
};
class RTC_EXPORT AsyncDnsResolver : public AsyncDnsResolverInterface {
public:
AsyncDnsResolver();
~AsyncDnsResolver();
// Start address resolution of the hostname in `addr`.
void Start(const rtc::SocketAddress& addr,
absl::AnyInvocable<void()> callback) override;
// Start address resolution of the hostname in `addr` matching `family`.
void Start(const rtc::SocketAddress& addr,
int family,
absl::AnyInvocable<void()> callback) override;
const AsyncDnsResolverResult& result() const override;
private:
class State;
ScopedTaskSafety safety_; // To check for client going away
rtc::scoped_refptr<State> state_; // To check for "this" going away
AsyncDnsResolverResultImpl result_;
absl::AnyInvocable<void()> callback_;
};
} // namespace webrtc
#endif // RTC_BASE_ASYNC_DNS_RESOLVER_H_

View file

@ -0,0 +1,73 @@
/*
* Copyright 2015 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 "rtc_base/async_packet_socket.h"
#include "rtc_base/checks.h"
namespace rtc {
PacketTimeUpdateParams::PacketTimeUpdateParams() = default;
PacketTimeUpdateParams::PacketTimeUpdateParams(
const PacketTimeUpdateParams& other) = default;
PacketTimeUpdateParams::~PacketTimeUpdateParams() = default;
PacketOptions::PacketOptions() = default;
PacketOptions::PacketOptions(DiffServCodePoint dscp) : dscp(dscp) {}
PacketOptions::PacketOptions(const PacketOptions& other) = default;
PacketOptions::~PacketOptions() = default;
AsyncPacketSocket::~AsyncPacketSocket() = default;
void AsyncPacketSocket::SubscribeCloseEvent(
const void* removal_tag,
std::function<void(AsyncPacketSocket*, int)> callback) {
RTC_DCHECK_RUN_ON(&network_checker_);
on_close_.AddReceiver(removal_tag, std::move(callback));
}
void AsyncPacketSocket::UnsubscribeCloseEvent(const void* removal_tag) {
RTC_DCHECK_RUN_ON(&network_checker_);
on_close_.RemoveReceivers(removal_tag);
}
void AsyncPacketSocket::RegisterReceivedPacketCallback(
absl::AnyInvocable<void(AsyncPacketSocket*, const rtc::ReceivedPacket&)>
received_packet_callback) {
RTC_DCHECK_RUN_ON(&network_checker_);
RTC_CHECK(!received_packet_callback_);
received_packet_callback_ = std::move(received_packet_callback);
}
void AsyncPacketSocket::DeregisterReceivedPacketCallback() {
RTC_DCHECK_RUN_ON(&network_checker_);
received_packet_callback_ = nullptr;
}
void AsyncPacketSocket::NotifyPacketReceived(
const rtc::ReceivedPacket& packet) {
RTC_DCHECK_RUN_ON(&network_checker_);
if (received_packet_callback_) {
received_packet_callback_(this, packet);
return;
}
}
void CopySocketInformationToPacketInfo(size_t packet_size_bytes,
const AsyncPacketSocket& socket_from,
bool is_connectionless,
rtc::PacketInfo* info) {
info->packet_size_bytes = packet_size_bytes;
info->ip_overhead_bytes = socket_from.GetLocalAddress().ipaddr().overhead();
}
} // namespace rtc

View file

@ -0,0 +1,201 @@
/*
* Copyright 2004 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.
*/
#ifndef RTC_BASE_ASYNC_PACKET_SOCKET_H_
#define RTC_BASE_ASYNC_PACKET_SOCKET_H_
#include <cstdint>
#include <vector>
#include "api/sequence_checker.h"
#include "rtc_base/callback_list.h"
#include "rtc_base/dscp.h"
#include "rtc_base/network/received_packet.h"
#include "rtc_base/network/sent_packet.h"
#include "rtc_base/socket.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/system/rtc_export.h"
#include "rtc_base/third_party/sigslot/sigslot.h"
#include "rtc_base/time_utils.h"
namespace rtc {
// This structure holds the info needed to update the packet send time header
// extension, including the information needed to update the authentication tag
// after changing the value.
struct PacketTimeUpdateParams {
PacketTimeUpdateParams();
PacketTimeUpdateParams(const PacketTimeUpdateParams& other);
~PacketTimeUpdateParams();
int rtp_sendtime_extension_id = -1; // extension header id present in packet.
std::vector<char> srtp_auth_key; // Authentication key.
int srtp_auth_tag_len = -1; // Authentication tag length.
int64_t srtp_packet_index = -1; // Required for Rtp Packet authentication.
};
// This structure holds meta information for the packet which is about to send
// over network.
struct RTC_EXPORT PacketOptions {
PacketOptions();
explicit PacketOptions(DiffServCodePoint dscp);
PacketOptions(const PacketOptions& other);
~PacketOptions();
DiffServCodePoint dscp = DSCP_NO_CHANGE;
// When used with RTP packets (for example, webrtc::PacketOptions), the value
// should be 16 bits. A value of -1 represents "not set".
int64_t packet_id = -1;
PacketTimeUpdateParams packet_time_params;
// PacketInfo is passed to SentPacket when signaling this packet is sent.
PacketInfo info_signaled_after_sent;
// True if this is a batchable packet. Batchable packets are collected at low
// levels and sent first when their AsyncPacketSocket receives a
// OnSendBatchComplete call.
bool batchable = false;
// True if this is the last packet of a batch.
bool last_packet_in_batch = false;
};
// Provides the ability to receive packets asynchronously. Sends are not
// buffered since it is acceptable to drop packets under high load.
class RTC_EXPORT AsyncPacketSocket : public sigslot::has_slots<> {
public:
enum State {
STATE_CLOSED,
STATE_BINDING,
STATE_BOUND,
STATE_CONNECTING,
STATE_CONNECTED
};
AsyncPacketSocket() = default;
~AsyncPacketSocket() override;
AsyncPacketSocket(const AsyncPacketSocket&) = delete;
AsyncPacketSocket& operator=(const AsyncPacketSocket&) = delete;
// Returns current local address. Address may be set to null if the
// socket is not bound yet (GetState() returns STATE_BINDING).
virtual SocketAddress GetLocalAddress() const = 0;
// Returns remote address. Returns zeroes if this is not a client TCP socket.
virtual SocketAddress GetRemoteAddress() const = 0;
// Send a packet.
virtual int Send(const void* pv, size_t cb, const PacketOptions& options) = 0;
virtual int SendTo(const void* pv,
size_t cb,
const SocketAddress& addr,
const PacketOptions& options) = 0;
// Close the socket.
virtual int Close() = 0;
// Returns current state of the socket.
virtual State GetState() const = 0;
// Get/set options.
virtual int GetOption(Socket::Option opt, int* value) = 0;
virtual int SetOption(Socket::Option opt, int value) = 0;
// Get/Set current error.
// TODO: Remove SetError().
virtual int GetError() const = 0;
virtual void SetError(int error) = 0;
// Register a callback to be called when the socket is closed.
void SubscribeCloseEvent(
const void* removal_tag,
std::function<void(AsyncPacketSocket*, int)> callback);
void UnsubscribeCloseEvent(const void* removal_tag);
void RegisterReceivedPacketCallback(
absl::AnyInvocable<void(AsyncPacketSocket*, const rtc::ReceivedPacket&)>
received_packet_callback);
void DeregisterReceivedPacketCallback();
// Emitted each time a packet is sent.
sigslot::signal2<AsyncPacketSocket*, const SentPacket&> SignalSentPacket;
// Emitted when the socket is currently able to send.
sigslot::signal1<AsyncPacketSocket*> SignalReadyToSend;
// Emitted after address for the socket is allocated, i.e. binding
// is finished. State of the socket is changed from BINDING to BOUND
// (for UDP sockets).
sigslot::signal2<AsyncPacketSocket*, const SocketAddress&> SignalAddressReady;
// Emitted for client TCP sockets when state is changed from
// CONNECTING to CONNECTED.
sigslot::signal1<AsyncPacketSocket*> SignalConnect;
void NotifyClosedForTest(int err) { NotifyClosed(err); }
protected:
// TODO(bugs.webrtc.org/11943): Remove after updating downstream code.
void SignalClose(AsyncPacketSocket* s, int err) {
RTC_DCHECK_EQ(s, this);
NotifyClosed(err);
}
void NotifyClosed(int err) {
RTC_DCHECK_RUN_ON(&network_checker_);
on_close_.Send(this, err);
}
// TODO(bugs.webrtc.org:15368): Deprecate and remove.
void NotifyPacketReceived(AsyncPacketSocket*,
const char* data,
size_t size,
const SocketAddress& address,
const int64_t& packet_time_us) {
NotifyPacketReceived(
ReceivedPacket::CreateFromLegacy(data, size, packet_time_us, address));
}
void NotifyPacketReceived(const rtc::ReceivedPacket& packet);
RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker network_checker_{
webrtc::SequenceChecker::kDetached};
private:
webrtc::CallbackList<AsyncPacketSocket*, int> on_close_
RTC_GUARDED_BY(&network_checker_);
absl::AnyInvocable<void(AsyncPacketSocket*, const rtc::ReceivedPacket&)>
received_packet_callback_ RTC_GUARDED_BY(&network_checker_);
};
// Listen socket, producing an AsyncPacketSocket when a peer connects.
class RTC_EXPORT AsyncListenSocket : public sigslot::has_slots<> {
public:
enum class State {
kClosed,
kBound,
};
// Returns current state of the socket.
virtual State GetState() const = 0;
// Returns current local address. Address may be set to null if the
// socket is not bound yet (GetState() returns kBinding).
virtual SocketAddress GetLocalAddress() const = 0;
sigslot::signal2<AsyncListenSocket*, AsyncPacketSocket*> SignalNewConnection;
};
void CopySocketInformationToPacketInfo(size_t packet_size_bytes,
const AsyncPacketSocket& socket_from,
bool is_connectionless,
rtc::PacketInfo* info);
} // namespace rtc
#endif // RTC_BASE_ASYNC_PACKET_SOCKET_H_

View file

@ -0,0 +1,113 @@
/*
* Copyright 2010 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 "rtc_base/async_socket.h"
#include "absl/memory/memory.h"
#include "rtc_base/checks.h"
namespace rtc {
AsyncSocketAdapter::AsyncSocketAdapter(Socket* socket)
: socket_(absl::WrapUnique(socket)) {
RTC_DCHECK(socket_);
socket_->SignalConnectEvent.connect(this,
&AsyncSocketAdapter::OnConnectEvent);
socket_->SignalReadEvent.connect(this, &AsyncSocketAdapter::OnReadEvent);
socket_->SignalWriteEvent.connect(this, &AsyncSocketAdapter::OnWriteEvent);
socket_->SignalCloseEvent.connect(this, &AsyncSocketAdapter::OnCloseEvent);
}
SocketAddress AsyncSocketAdapter::GetLocalAddress() const {
return socket_->GetLocalAddress();
}
SocketAddress AsyncSocketAdapter::GetRemoteAddress() const {
return socket_->GetRemoteAddress();
}
int AsyncSocketAdapter::Bind(const SocketAddress& addr) {
return socket_->Bind(addr);
}
int AsyncSocketAdapter::Connect(const SocketAddress& addr) {
return socket_->Connect(addr);
}
int AsyncSocketAdapter::Send(const void* pv, size_t cb) {
return socket_->Send(pv, cb);
}
int AsyncSocketAdapter::SendTo(const void* pv,
size_t cb,
const SocketAddress& addr) {
return socket_->SendTo(pv, cb, addr);
}
int AsyncSocketAdapter::Recv(void* pv, size_t cb, int64_t* timestamp) {
return socket_->Recv(pv, cb, timestamp);
}
int AsyncSocketAdapter::RecvFrom(void* pv,
size_t cb,
SocketAddress* paddr,
int64_t* timestamp) {
return socket_->RecvFrom(pv, cb, paddr, timestamp);
}
int AsyncSocketAdapter::Listen(int backlog) {
return socket_->Listen(backlog);
}
Socket* AsyncSocketAdapter::Accept(SocketAddress* paddr) {
return socket_->Accept(paddr);
}
int AsyncSocketAdapter::Close() {
return socket_->Close();
}
int AsyncSocketAdapter::GetError() const {
return socket_->GetError();
}
void AsyncSocketAdapter::SetError(int error) {
return socket_->SetError(error);
}
Socket::ConnState AsyncSocketAdapter::GetState() const {
return socket_->GetState();
}
int AsyncSocketAdapter::GetOption(Option opt, int* value) {
return socket_->GetOption(opt, value);
}
int AsyncSocketAdapter::SetOption(Option opt, int value) {
return socket_->SetOption(opt, value);
}
void AsyncSocketAdapter::OnConnectEvent(Socket* socket) {
SignalConnectEvent(this);
}
void AsyncSocketAdapter::OnReadEvent(Socket* socket) {
SignalReadEvent(this);
}
void AsyncSocketAdapter::OnWriteEvent(Socket* socket) {
SignalWriteEvent(this);
}
void AsyncSocketAdapter::OnCloseEvent(Socket* socket, int err) {
SignalCloseEvent(this, err);
}
} // namespace rtc

View file

@ -0,0 +1,65 @@
/*
* Copyright 2004 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.
*/
#ifndef RTC_BASE_ASYNC_SOCKET_H_
#define RTC_BASE_ASYNC_SOCKET_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include "rtc_base/socket.h"
#include "rtc_base/socket_address.h"
#include "rtc_base/third_party/sigslot/sigslot.h"
namespace rtc {
class AsyncSocketAdapter : public Socket, public sigslot::has_slots<> {
public:
// Takes ownership of the passed in socket.
// TODO(bugs.webrtc.org/6424): Change to unique_ptr here and in callers.
explicit AsyncSocketAdapter(Socket* socket);
SocketAddress GetLocalAddress() const override;
SocketAddress GetRemoteAddress() const override;
int Bind(const SocketAddress& addr) override;
int Connect(const SocketAddress& addr) override;
int Send(const void* pv, size_t cb) override;
int SendTo(const void* pv, size_t cb, const SocketAddress& addr) override;
int Recv(void* pv, size_t cb, int64_t* timestamp) override;
int RecvFrom(void* pv,
size_t cb,
SocketAddress* paddr,
int64_t* timestamp) override;
int Listen(int backlog) override;
Socket* Accept(SocketAddress* paddr) override;
int Close() override;
int GetError() const override;
void SetError(int error) override;
ConnState GetState() const override;
int GetOption(Option opt, int* value) override;
int SetOption(Option opt, int value) override;
protected:
virtual void OnConnectEvent(Socket* socket);
virtual void OnReadEvent(Socket* socket);
virtual void OnWriteEvent(Socket* socket);
virtual void OnCloseEvent(Socket* socket, int err);
Socket* GetSocket() const { return socket_.get(); }
private:
const std::unique_ptr<Socket> socket_;
};
} // namespace rtc
#endif // RTC_BASE_ASYNC_SOCKET_H_

View file

@ -0,0 +1,358 @@
/*
* Copyright 2004 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 "rtc_base/async_tcp_socket.h"
#include <stdint.h>
#include <string.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <memory>
#include "api/array_view.h"
#include "rtc_base/byte_order.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/network/sent_packet.h"
#include "rtc_base/time_utils.h" // for TimeMillis
#if defined(WEBRTC_POSIX)
#include <errno.h>
#endif // WEBRTC_POSIX
namespace rtc {
static const size_t kMaxPacketSize = 64 * 1024;
typedef uint16_t PacketLength;
static const size_t kPacketLenSize = sizeof(PacketLength);
static const size_t kBufSize = kMaxPacketSize + kPacketLenSize;
// The input buffer will be resized so that at least kMinimumRecvSize bytes can
// be received (but it will not grow above the maximum size passed to the
// constructor).
static const size_t kMinimumRecvSize = 128;
static const int kListenBacklog = 5;
// Binds and connects `socket`
Socket* AsyncTCPSocketBase::ConnectSocket(
rtc::Socket* socket,
const rtc::SocketAddress& bind_address,
const rtc::SocketAddress& remote_address) {
std::unique_ptr<rtc::Socket> owned_socket(socket);
if (socket->Bind(bind_address) < 0) {
RTC_LOG(LS_ERROR) << "Bind() failed with error " << socket->GetError();
return nullptr;
}
if (socket->Connect(remote_address) < 0) {
RTC_LOG(LS_ERROR) << "Connect() failed with error " << socket->GetError();
return nullptr;
}
return owned_socket.release();
}
AsyncTCPSocketBase::AsyncTCPSocketBase(Socket* socket, size_t max_packet_size)
: socket_(socket),
max_insize_(max_packet_size),
max_outsize_(max_packet_size) {
inbuf_.EnsureCapacity(kMinimumRecvSize);
socket_->SignalConnectEvent.connect(this,
&AsyncTCPSocketBase::OnConnectEvent);
socket_->SignalReadEvent.connect(this, &AsyncTCPSocketBase::OnReadEvent);
socket_->SignalWriteEvent.connect(this, &AsyncTCPSocketBase::OnWriteEvent);
socket_->SignalCloseEvent.connect(this, &AsyncTCPSocketBase::OnCloseEvent);
}
AsyncTCPSocketBase::~AsyncTCPSocketBase() {}
SocketAddress AsyncTCPSocketBase::GetLocalAddress() const {
return socket_->GetLocalAddress();
}
SocketAddress AsyncTCPSocketBase::GetRemoteAddress() const {
return socket_->GetRemoteAddress();
}
int AsyncTCPSocketBase::Close() {
return socket_->Close();
}
AsyncTCPSocket::State AsyncTCPSocketBase::GetState() const {
switch (socket_->GetState()) {
case Socket::CS_CLOSED:
return STATE_CLOSED;
case Socket::CS_CONNECTING:
return STATE_CONNECTING;
case Socket::CS_CONNECTED:
return STATE_CONNECTED;
default:
RTC_DCHECK_NOTREACHED();
return STATE_CLOSED;
}
}
int AsyncTCPSocketBase::GetOption(Socket::Option opt, int* value) {
return socket_->GetOption(opt, value);
}
int AsyncTCPSocketBase::SetOption(Socket::Option opt, int value) {
return socket_->SetOption(opt, value);
}
int AsyncTCPSocketBase::GetError() const {
return socket_->GetError();
}
void AsyncTCPSocketBase::SetError(int error) {
return socket_->SetError(error);
}
int AsyncTCPSocketBase::SendTo(const void* pv,
size_t cb,
const SocketAddress& addr,
const rtc::PacketOptions& options) {
const SocketAddress& remote_address = GetRemoteAddress();
if (addr == remote_address)
return Send(pv, cb, options);
// Remote address may be empty if there is a sudden network change.
RTC_DCHECK(remote_address.IsNil());
socket_->SetError(ENOTCONN);
return -1;
}
int AsyncTCPSocketBase::FlushOutBuffer() {
RTC_DCHECK_GT(outbuf_.size(), 0);
rtc::ArrayView<uint8_t> view = outbuf_;
int res;
while (view.size() > 0) {
res = socket_->Send(view.data(), view.size());
if (res <= 0) {
break;
}
if (static_cast<size_t>(res) > view.size()) {
RTC_DCHECK_NOTREACHED();
res = -1;
break;
}
view = view.subview(res);
}
if (res > 0) {
// The output buffer may have been written out over multiple partial Send(),
// so reconstruct the total written length.
RTC_DCHECK_EQ(view.size(), 0);
res = outbuf_.size();
outbuf_.Clear();
} else {
// There was an error when calling Send(), so there will still be data left
// to send at a later point.
RTC_DCHECK_GT(view.size(), 0);
// In the special case of EWOULDBLOCK, signal that we had a partial write.
if (socket_->GetError() == EWOULDBLOCK) {
res = outbuf_.size() - view.size();
}
if (view.size() < outbuf_.size()) {
memmove(outbuf_.data(), view.data(), view.size());
outbuf_.SetSize(view.size());
}
}
return res;
}
void AsyncTCPSocketBase::AppendToOutBuffer(const void* pv, size_t cb) {
RTC_DCHECK(outbuf_.size() + cb <= max_outsize_);
outbuf_.AppendData(static_cast<const uint8_t*>(pv), cb);
}
void AsyncTCPSocketBase::OnConnectEvent(Socket* socket) {
SignalConnect(this);
}
void AsyncTCPSocketBase::OnReadEvent(Socket* socket) {
RTC_DCHECK(socket_.get() == socket);
size_t total_recv = 0;
while (true) {
size_t free_size = inbuf_.capacity() - inbuf_.size();
if (free_size < kMinimumRecvSize && inbuf_.capacity() < max_insize_) {
inbuf_.EnsureCapacity(std::min(max_insize_, inbuf_.capacity() * 2));
free_size = inbuf_.capacity() - inbuf_.size();
}
int len = socket_->Recv(inbuf_.data() + inbuf_.size(), free_size, nullptr);
if (len < 0) {
// TODO(stefan): Do something better like forwarding the error to the
// user.
if (!socket_->IsBlocking()) {
RTC_LOG(LS_ERROR) << "Recv() returned error: " << socket_->GetError();
}
break;
}
total_recv += len;
inbuf_.SetSize(inbuf_.size() + len);
if (!len || static_cast<size_t>(len) < free_size) {
break;
}
}
if (!total_recv) {
return;
}
size_t processed = ProcessInput(inbuf_);
size_t bytes_remaining = inbuf_.size() - processed;
if (processed > inbuf_.size()) {
RTC_LOG(LS_ERROR) << "input buffer overflow";
RTC_DCHECK_NOTREACHED();
inbuf_.Clear();
} else {
if (bytes_remaining > 0) {
memmove(inbuf_.data(), inbuf_.data() + processed, bytes_remaining);
}
inbuf_.SetSize(bytes_remaining);
}
}
void AsyncTCPSocketBase::OnWriteEvent(Socket* socket) {
RTC_DCHECK(socket_.get() == socket);
if (outbuf_.size() > 0) {
FlushOutBuffer();
}
if (outbuf_.size() == 0) {
SignalReadyToSend(this);
}
}
void AsyncTCPSocketBase::OnCloseEvent(Socket* socket, int error) {
NotifyClosed(error);
}
// AsyncTCPSocket
// Binds and connects `socket` and creates AsyncTCPSocket for
// it. Takes ownership of `socket`. Returns null if bind() or
// connect() fail (`socket` is destroyed in that case).
AsyncTCPSocket* AsyncTCPSocket::Create(Socket* socket,
const SocketAddress& bind_address,
const SocketAddress& remote_address) {
return new AsyncTCPSocket(
AsyncTCPSocketBase::ConnectSocket(socket, bind_address, remote_address));
}
AsyncTCPSocket::AsyncTCPSocket(Socket* socket)
: AsyncTCPSocketBase(socket, kBufSize) {}
int AsyncTCPSocket::Send(const void* pv,
size_t cb,
const rtc::PacketOptions& options) {
if (cb > kBufSize) {
SetError(EMSGSIZE);
return -1;
}
// If we are blocking on send, then silently drop this packet
if (!IsOutBufferEmpty())
return static_cast<int>(cb);
PacketLength pkt_len = HostToNetwork16(static_cast<PacketLength>(cb));
AppendToOutBuffer(&pkt_len, kPacketLenSize);
AppendToOutBuffer(pv, cb);
int res = FlushOutBuffer();
if (res <= 0) {
// drop packet if we made no progress
ClearOutBuffer();
return res;
}
rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis(),
options.info_signaled_after_sent);
CopySocketInformationToPacketInfo(cb, *this, false, &sent_packet.info);
SignalSentPacket(this, sent_packet);
// We claim to have sent the whole thing, even if we only sent partial
return static_cast<int>(cb);
}
size_t AsyncTCPSocket::ProcessInput(rtc::ArrayView<const uint8_t> data) {
SocketAddress remote_addr(GetRemoteAddress());
size_t processed_bytes = 0;
while (true) {
size_t bytes_left = data.size() - processed_bytes;
if (bytes_left < kPacketLenSize)
return processed_bytes;
PacketLength pkt_len = rtc::GetBE16(data.data() + processed_bytes);
if (bytes_left < kPacketLenSize + pkt_len)
return processed_bytes;
rtc::ReceivedPacket received_packet(
data.subview(processed_bytes + kPacketLenSize, pkt_len), remote_addr,
webrtc::Timestamp::Micros(rtc::TimeMicros()));
NotifyPacketReceived(received_packet);
processed_bytes += kPacketLenSize + pkt_len;
}
}
AsyncTcpListenSocket::AsyncTcpListenSocket(std::unique_ptr<Socket> socket)
: socket_(std::move(socket)) {
RTC_DCHECK(socket_.get() != nullptr);
socket_->SignalReadEvent.connect(this, &AsyncTcpListenSocket::OnReadEvent);
if (socket_->Listen(kListenBacklog) < 0) {
RTC_LOG(LS_ERROR) << "Listen() failed with error " << socket_->GetError();
}
}
AsyncTcpListenSocket::State AsyncTcpListenSocket::GetState() const {
switch (socket_->GetState()) {
case Socket::CS_CLOSED:
return State::kClosed;
case Socket::CS_CONNECTING:
return State::kBound;
default:
RTC_DCHECK_NOTREACHED();
return State::kClosed;
}
}
SocketAddress AsyncTcpListenSocket::GetLocalAddress() const {
return socket_->GetLocalAddress();
}
void AsyncTcpListenSocket::OnReadEvent(Socket* socket) {
RTC_DCHECK(socket_.get() == socket);
rtc::SocketAddress address;
rtc::Socket* new_socket = socket->Accept(&address);
if (!new_socket) {
// TODO(stefan): Do something better like forwarding the error
// to the user.
RTC_LOG(LS_ERROR) << "TCP accept failed with error " << socket_->GetError();
return;
}
HandleIncomingConnection(new_socket);
// Prime a read event in case data is waiting.
new_socket->SignalReadEvent(new_socket);
}
void AsyncTcpListenSocket::HandleIncomingConnection(Socket* socket) {
SignalNewConnection(this, new AsyncTCPSocket(socket));
}
} // namespace rtc

View file

@ -0,0 +1,126 @@
/*
* Copyright 2004 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.
*/
#ifndef RTC_BASE_ASYNC_TCP_SOCKET_H_
#define RTC_BASE_ASYNC_TCP_SOCKET_H_
#include <stddef.h>
#include <cstdint>
#include <memory>
#include "api/array_view.h"
#include "rtc_base/async_packet_socket.h"
#include "rtc_base/buffer.h"
#include "rtc_base/socket.h"
#include "rtc_base/socket_address.h"
namespace rtc {
// Simulates UDP semantics over TCP. Send and Recv packet sizes
// are preserved, and drops packets silently on Send, rather than
// buffer them in user space.
class AsyncTCPSocketBase : public AsyncPacketSocket {
public:
AsyncTCPSocketBase(Socket* socket, size_t max_packet_size);
~AsyncTCPSocketBase() override;
AsyncTCPSocketBase(const AsyncTCPSocketBase&) = delete;
AsyncTCPSocketBase& operator=(const AsyncTCPSocketBase&) = delete;
// Pure virtual methods to send and recv data.
int Send(const void* pv,
size_t cb,
const rtc::PacketOptions& options) override = 0;
// Must return the number of bytes processed.
virtual size_t ProcessInput(rtc::ArrayView<const uint8_t> data) = 0;
SocketAddress GetLocalAddress() const override;
SocketAddress GetRemoteAddress() const override;
int SendTo(const void* pv,
size_t cb,
const SocketAddress& addr,
const rtc::PacketOptions& options) override;
int Close() override;
State GetState() const override;
int GetOption(Socket::Option opt, int* value) override;
int SetOption(Socket::Option opt, int value) override;
int GetError() const override;
void SetError(int error) override;
protected:
// Binds and connects `socket` and creates AsyncTCPSocket for
// it. Takes ownership of `socket`. Returns null if bind() or
// connect() fail (`socket` is destroyed in that case).
static Socket* ConnectSocket(Socket* socket,
const SocketAddress& bind_address,
const SocketAddress& remote_address);
int FlushOutBuffer();
// Add data to `outbuf_`.
void AppendToOutBuffer(const void* pv, size_t cb);
// Helper methods for `outpos_`.
bool IsOutBufferEmpty() const { return outbuf_.size() == 0; }
void ClearOutBuffer() { outbuf_.Clear(); }
private:
// Called by the underlying socket
void OnConnectEvent(Socket* socket);
void OnReadEvent(Socket* socket);
void OnWriteEvent(Socket* socket);
void OnCloseEvent(Socket* socket, int error);
std::unique_ptr<Socket> socket_;
Buffer inbuf_;
Buffer outbuf_;
size_t max_insize_;
size_t max_outsize_;
};
class AsyncTCPSocket : public AsyncTCPSocketBase {
public:
// Binds and connects `socket` and creates AsyncTCPSocket for
// it. Takes ownership of `socket`. Returns null if bind() or
// connect() fail (`socket` is destroyed in that case).
static AsyncTCPSocket* Create(Socket* socket,
const SocketAddress& bind_address,
const SocketAddress& remote_address);
explicit AsyncTCPSocket(Socket* socket);
~AsyncTCPSocket() override {}
AsyncTCPSocket(const AsyncTCPSocket&) = delete;
AsyncTCPSocket& operator=(const AsyncTCPSocket&) = delete;
int Send(const void* pv,
size_t cb,
const rtc::PacketOptions& options) override;
size_t ProcessInput(rtc::ArrayView<const uint8_t>) override;
};
class AsyncTcpListenSocket : public AsyncListenSocket {
public:
explicit AsyncTcpListenSocket(std::unique_ptr<Socket> socket);
State GetState() const override;
SocketAddress GetLocalAddress() const override;
virtual void HandleIncomingConnection(rtc::Socket* socket);
private:
// Called by the underlying socket
void OnReadEvent(Socket* socket);
std::unique_ptr<Socket> socket_;
};
} // namespace rtc
#endif // RTC_BASE_ASYNC_TCP_SOCKET_H_

View file

@ -0,0 +1,157 @@
/*
* Copyright 2004 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 "rtc_base/async_udp_socket.h"
#include "absl/types/optional.h"
#include "api/units/time_delta.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/network/received_packet.h"
#include "rtc_base/network/sent_packet.h"
#include "rtc_base/time_utils.h"
#include "system_wrappers/include/field_trial.h"
namespace rtc {
// Returns true if the experiement "WebRTC-SCM-Timestamp" is explicitly
// disabled.
static bool IsScmTimeStampExperimentDisabled() {
return webrtc::field_trial::IsDisabled("WebRTC-SCM-Timestamp");
}
AsyncUDPSocket* AsyncUDPSocket::Create(Socket* socket,
const SocketAddress& bind_address) {
std::unique_ptr<Socket> owned_socket(socket);
if (socket->Bind(bind_address) < 0) {
RTC_LOG(LS_ERROR) << "Bind() failed with error " << socket->GetError();
return nullptr;
}
return new AsyncUDPSocket(owned_socket.release());
}
AsyncUDPSocket* AsyncUDPSocket::Create(SocketFactory* factory,
const SocketAddress& bind_address) {
Socket* socket = factory->CreateSocket(bind_address.family(), SOCK_DGRAM);
if (!socket)
return nullptr;
return Create(socket, bind_address);
}
AsyncUDPSocket::AsyncUDPSocket(Socket* socket) : socket_(socket) {
sequence_checker_.Detach();
// The socket should start out readable but not writable.
socket_->SignalReadEvent.connect(this, &AsyncUDPSocket::OnReadEvent);
socket_->SignalWriteEvent.connect(this, &AsyncUDPSocket::OnWriteEvent);
}
SocketAddress AsyncUDPSocket::GetLocalAddress() const {
return socket_->GetLocalAddress();
}
SocketAddress AsyncUDPSocket::GetRemoteAddress() const {
return socket_->GetRemoteAddress();
}
int AsyncUDPSocket::Send(const void* pv,
size_t cb,
const rtc::PacketOptions& options) {
rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis(),
options.info_signaled_after_sent);
CopySocketInformationToPacketInfo(cb, *this, false, &sent_packet.info);
int ret = socket_->Send(pv, cb);
SignalSentPacket(this, sent_packet);
return ret;
}
int AsyncUDPSocket::SendTo(const void* pv,
size_t cb,
const SocketAddress& addr,
const rtc::PacketOptions& options) {
rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis(),
options.info_signaled_after_sent);
CopySocketInformationToPacketInfo(cb, *this, true, &sent_packet.info);
int ret = socket_->SendTo(pv, cb, addr);
SignalSentPacket(this, sent_packet);
return ret;
}
int AsyncUDPSocket::Close() {
return socket_->Close();
}
AsyncUDPSocket::State AsyncUDPSocket::GetState() const {
return STATE_BOUND;
}
int AsyncUDPSocket::GetOption(Socket::Option opt, int* value) {
return socket_->GetOption(opt, value);
}
int AsyncUDPSocket::SetOption(Socket::Option opt, int value) {
return socket_->SetOption(opt, value);
}
int AsyncUDPSocket::GetError() const {
return socket_->GetError();
}
void AsyncUDPSocket::SetError(int error) {
return socket_->SetError(error);
}
void AsyncUDPSocket::OnReadEvent(Socket* socket) {
RTC_DCHECK(socket_.get() == socket);
RTC_DCHECK_RUN_ON(&sequence_checker_);
Socket::ReceiveBuffer receive_buffer(buffer_);
int len = socket_->RecvFrom(receive_buffer);
if (len < 0) {
// An error here typically means we got an ICMP error in response to our
// send datagram, indicating the remote address was unreachable.
// When doing ICE, this kind of thing will often happen.
// TODO: Do something better like forwarding the error to the user.
SocketAddress local_addr = socket_->GetLocalAddress();
RTC_LOG(LS_INFO) << "AsyncUDPSocket[" << local_addr.ToSensitiveString()
<< "] receive failed with error " << socket_->GetError();
return;
}
if (len == 0) {
// Spurios wakeup.
return;
}
if (!receive_buffer.arrival_time) {
// Timestamp from socket is not available.
receive_buffer.arrival_time = webrtc::Timestamp::Micros(rtc::TimeMicros());
} else {
if (!socket_time_offset_) {
// Estimate timestamp offset from first packet arrival time unless
// disabled
bool estimate_time_offset = !IsScmTimeStampExperimentDisabled();
if (estimate_time_offset) {
socket_time_offset_ = webrtc::Timestamp::Micros(rtc::TimeMicros()) -
*receive_buffer.arrival_time;
} else {
socket_time_offset_ = webrtc::TimeDelta::Micros(0);
}
}
*receive_buffer.arrival_time += *socket_time_offset_;
}
NotifyPacketReceived(ReceivedPacket(receive_buffer.payload,
receive_buffer.source_address,
receive_buffer.arrival_time));
}
void AsyncUDPSocket::OnWriteEvent(Socket* socket) {
SignalReadyToSend(this);
}
} // namespace rtc

View file

@ -0,0 +1,79 @@
/*
* Copyright 2004 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.
*/
#ifndef RTC_BASE_ASYNC_UDP_SOCKET_H_
#define RTC_BASE_ASYNC_UDP_SOCKET_H_
#include <stddef.h>
#include <cstdint>
#include <memory>
#include "absl/types/optional.h"
#include "api/sequence_checker.h"
#include "api/units/time_delta.h"
#include "rtc_base/async_packet_socket.h"
#include "rtc_base/socket.h"
#include "rtc_base/socket_address.h"
#include "rtc_base/socket_factory.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/thread_annotations.h"
namespace rtc {
// Provides the ability to receive packets asynchronously. Sends are not
// buffered since it is acceptable to drop packets under high load.
class AsyncUDPSocket : public AsyncPacketSocket {
public:
// Binds `socket` and creates AsyncUDPSocket for it. Takes ownership
// of `socket`. Returns null if bind() fails (`socket` is destroyed
// in that case).
static AsyncUDPSocket* Create(Socket* socket,
const SocketAddress& bind_address);
// Creates a new socket for sending asynchronous UDP packets using an
// asynchronous socket from the given factory.
static AsyncUDPSocket* Create(SocketFactory* factory,
const SocketAddress& bind_address);
explicit AsyncUDPSocket(Socket* socket);
~AsyncUDPSocket() = default;
SocketAddress GetLocalAddress() const override;
SocketAddress GetRemoteAddress() const override;
int Send(const void* pv,
size_t cb,
const rtc::PacketOptions& options) override;
int SendTo(const void* pv,
size_t cb,
const SocketAddress& addr,
const rtc::PacketOptions& options) override;
int Close() override;
State GetState() const override;
int GetOption(Socket::Option opt, int* value) override;
int SetOption(Socket::Option opt, int value) override;
int GetError() const override;
void SetError(int error) override;
private:
// Called when the underlying socket is ready to be read from.
void OnReadEvent(Socket* socket);
// Called when the underlying socket is ready to send.
void OnWriteEvent(Socket* socket);
RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker sequence_checker_;
std::unique_ptr<Socket> socket_;
rtc::Buffer buffer_ RTC_GUARDED_BY(sequence_checker_);
absl::optional<webrtc::TimeDelta> socket_time_offset_
RTC_GUARDED_BY(sequence_checker_);
};
} // namespace rtc
#endif // RTC_BASE_ASYNC_UDP_SOCKET_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,230 @@
/*
* Copyright 2015 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 "rtc_base/bit_buffer.h"
#include <algorithm>
#include <limits>
#include "absl/numeric/bits.h"
#include "absl/strings/string_view.h"
#include "rtc_base/checks.h"
namespace {
// Returns the highest byte of `val` in a uint8_t.
uint8_t HighestByte(uint64_t val) {
return static_cast<uint8_t>(val >> 56);
}
// Returns the result of writing partial data from `source`, of
// `source_bit_count` size in the highest bits, to `target` at
// `target_bit_offset` from the highest bit.
uint8_t WritePartialByte(uint8_t source,
size_t source_bit_count,
uint8_t target,
size_t target_bit_offset) {
RTC_DCHECK(target_bit_offset < 8);
RTC_DCHECK(source_bit_count < 9);
RTC_DCHECK(source_bit_count <= (8 - target_bit_offset));
// Generate a mask for just the bits we're going to overwrite, so:
uint8_t mask =
// The number of bits we want, in the most significant bits...
static_cast<uint8_t>(0xFF << (8 - source_bit_count))
// ...shifted over to the target offset from the most signficant bit.
>> target_bit_offset;
// We want the target, with the bits we'll overwrite masked off, or'ed with
// the bits from the source we want.
return (target & ~mask) | (source >> target_bit_offset);
}
} // namespace
namespace rtc {
BitBufferWriter::BitBufferWriter(uint8_t* bytes, size_t byte_count)
: writable_bytes_(bytes),
byte_count_(byte_count),
byte_offset_(),
bit_offset_() {
RTC_DCHECK(static_cast<uint64_t>(byte_count_) <=
std::numeric_limits<uint32_t>::max());
}
uint64_t BitBufferWriter::RemainingBitCount() const {
return (static_cast<uint64_t>(byte_count_) - byte_offset_) * 8 - bit_offset_;
}
bool BitBufferWriter::ConsumeBytes(size_t byte_count) {
return ConsumeBits(byte_count * 8);
}
bool BitBufferWriter::ConsumeBits(size_t bit_count) {
if (bit_count > RemainingBitCount()) {
return false;
}
byte_offset_ += (bit_offset_ + bit_count) / 8;
bit_offset_ = (bit_offset_ + bit_count) % 8;
return true;
}
void BitBufferWriter::GetCurrentOffset(size_t* out_byte_offset,
size_t* out_bit_offset) {
RTC_CHECK(out_byte_offset != nullptr);
RTC_CHECK(out_bit_offset != nullptr);
*out_byte_offset = byte_offset_;
*out_bit_offset = bit_offset_;
}
bool BitBufferWriter::Seek(size_t byte_offset, size_t bit_offset) {
if (byte_offset > byte_count_ || bit_offset > 7 ||
(byte_offset == byte_count_ && bit_offset > 0)) {
return false;
}
byte_offset_ = byte_offset;
bit_offset_ = bit_offset;
return true;
}
bool BitBufferWriter::WriteUInt8(uint8_t val) {
return WriteBits(val, sizeof(uint8_t) * 8);
}
bool BitBufferWriter::WriteUInt16(uint16_t val) {
return WriteBits(val, sizeof(uint16_t) * 8);
}
bool BitBufferWriter::WriteUInt32(uint32_t val) {
return WriteBits(val, sizeof(uint32_t) * 8);
}
bool BitBufferWriter::WriteBits(uint64_t val, size_t bit_count) {
if (bit_count > RemainingBitCount()) {
return false;
}
size_t total_bits = bit_count;
// For simplicity, push the bits we want to read from val to the highest bits.
val <<= (sizeof(uint64_t) * 8 - bit_count);
uint8_t* bytes = writable_bytes_ + byte_offset_;
// The first byte is relatively special; the bit offset to write to may put us
// in the middle of the byte, and the total bit count to write may require we
// save the bits at the end of the byte.
size_t remaining_bits_in_current_byte = 8 - bit_offset_;
size_t bits_in_first_byte =
std::min(bit_count, remaining_bits_in_current_byte);
*bytes = WritePartialByte(HighestByte(val), bits_in_first_byte, *bytes,
bit_offset_);
if (bit_count <= remaining_bits_in_current_byte) {
// Nothing left to write, so quit early.
return ConsumeBits(total_bits);
}
// Subtract what we've written from the bit count, shift it off the value, and
// write the remaining full bytes.
val <<= bits_in_first_byte;
bytes++;
bit_count -= bits_in_first_byte;
while (bit_count >= 8) {
*bytes++ = HighestByte(val);
val <<= 8;
bit_count -= 8;
}
// Last byte may also be partial, so write the remaining bits from the top of
// val.
if (bit_count > 0) {
*bytes = WritePartialByte(HighestByte(val), bit_count, *bytes, 0);
}
// All done! Consume the bits we've written.
return ConsumeBits(total_bits);
}
bool BitBufferWriter::WriteNonSymmetric(uint32_t val, uint32_t num_values) {
RTC_DCHECK_LT(val, num_values);
RTC_DCHECK_LE(num_values, uint32_t{1} << 31);
if (num_values == 1) {
// When there is only one possible value, it requires zero bits to store it.
// But WriteBits doesn't support writing zero bits.
return true;
}
size_t count_bits = absl::bit_width(num_values);
uint32_t num_min_bits_values = (uint32_t{1} << count_bits) - num_values;
return val < num_min_bits_values
? WriteBits(val, count_bits - 1)
: WriteBits(val + num_min_bits_values, count_bits);
}
size_t BitBufferWriter::SizeNonSymmetricBits(uint32_t val,
uint32_t num_values) {
RTC_DCHECK_LT(val, num_values);
RTC_DCHECK_LE(num_values, uint32_t{1} << 31);
size_t count_bits = absl::bit_width(num_values);
uint32_t num_min_bits_values = (uint32_t{1} << count_bits) - num_values;
return val < num_min_bits_values ? (count_bits - 1) : count_bits;
}
bool BitBufferWriter::WriteExponentialGolomb(uint32_t val) {
// We don't support reading UINT32_MAX, because it doesn't fit in a uint32_t
// when encoded, so don't support writing it either.
if (val == std::numeric_limits<uint32_t>::max()) {
return false;
}
uint64_t val_to_encode = static_cast<uint64_t>(val) + 1;
// We need to write bit_width(val+1) 0s and then val+1. Since val (as a
// uint64_t) has leading zeros, we can just write the total golomb encoded
// size worth of bits, knowing the value will appear last.
return WriteBits(val_to_encode, absl::bit_width(val_to_encode) * 2 - 1);
}
bool BitBufferWriter::WriteSignedExponentialGolomb(int32_t val) {
if (val == 0) {
return WriteExponentialGolomb(0);
} else if (val > 0) {
uint32_t signed_val = val;
return WriteExponentialGolomb((signed_val * 2) - 1);
} else {
if (val == std::numeric_limits<int32_t>::min())
return false; // Not supported, would cause overflow.
uint32_t signed_val = -val;
return WriteExponentialGolomb(signed_val * 2);
}
}
bool BitBufferWriter::WriteLeb128(uint64_t val) {
bool success = true;
do {
uint8_t byte = static_cast<uint8_t>(val & 0x7f);
val >>= 7;
if (val > 0) {
byte |= 0x80;
}
success &= WriteUInt8(byte);
} while (val > 0);
return success;
}
bool BitBufferWriter::WriteString(absl::string_view data) {
bool success = true;
for (char c : data) {
success &= WriteUInt8(c);
}
return success;
}
} // namespace rtc

View file

@ -0,0 +1,100 @@
/*
* Copyright 2015 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.
*/
#ifndef RTC_BASE_BIT_BUFFER_H_
#define RTC_BASE_BIT_BUFFER_H_
#include <stddef.h> // For size_t.
#include <stdint.h> // For integer types.
#include "absl/strings/string_view.h"
#include "api/units/data_size.h"
namespace rtc {
// A BitBuffer API for write operations. Supports symmetric write APIs to the
// reading APIs of BitstreamReader.
// Sizes/counts specify bits/bytes, for clarity.
// Byte order is assumed big-endian/network.
class BitBufferWriter {
public:
static constexpr webrtc::DataSize kMaxLeb128Length =
webrtc::DataSize::Bytes(10);
// Constructs a bit buffer for the writable buffer of `bytes`.
BitBufferWriter(uint8_t* bytes, size_t byte_count);
BitBufferWriter(const BitBufferWriter&) = delete;
BitBufferWriter& operator=(const BitBufferWriter&) = delete;
// Gets the current offset, in bytes/bits, from the start of the buffer. The
// bit offset is the offset into the current byte, in the range [0,7].
void GetCurrentOffset(size_t* out_byte_offset, size_t* out_bit_offset);
// The remaining bits in the byte buffer.
uint64_t RemainingBitCount() const;
// Moves current position `byte_count` bytes forward. Returns false if
// there aren't enough bytes left in the buffer.
bool ConsumeBytes(size_t byte_count);
// Moves current position `bit_count` bits forward. Returns false if
// there aren't enough bits left in the buffer.
bool ConsumeBits(size_t bit_count);
// Sets the current offset to the provied byte/bit offsets. The bit
// offset is from the given byte, in the range [0,7].
bool Seek(size_t byte_offset, size_t bit_offset);
// Writes byte-sized values from the buffer. Returns false if there isn't
// enough data left for the specified type.
bool WriteUInt8(uint8_t val);
bool WriteUInt16(uint16_t val);
bool WriteUInt32(uint32_t val);
// Writes bit-sized values to the buffer. Returns false if there isn't enough
// room left for the specified number of bits.
bool WriteBits(uint64_t val, size_t bit_count);
// Writes value in range [0, num_values - 1]
// See ReadNonSymmetric documentation for the format,
// Call SizeNonSymmetricBits to get number of bits needed to store the value.
// Returns false if there isn't enough room left for the value.
bool WriteNonSymmetric(uint32_t val, uint32_t num_values);
// Returns number of bits required to store `val` with NonSymmetric encoding.
static size_t SizeNonSymmetricBits(uint32_t val, uint32_t num_values);
// Writes the exponential golomb encoded version of the supplied value.
// Returns false if there isn't enough room left for the value.
bool WriteExponentialGolomb(uint32_t val);
// Writes the signed exponential golomb version of the supplied value.
// Signed exponential golomb values are just the unsigned values mapped to the
// sequence 0, 1, -1, 2, -2, etc. in order.
bool WriteSignedExponentialGolomb(int32_t val);
// Writes the Leb128 encoded value.
bool WriteLeb128(uint64_t val);
// Writes the string as bytes of data.
bool WriteString(absl::string_view data);
private:
// The buffer, as a writable array.
uint8_t* const writable_bytes_;
// The total size of `bytes_`.
const size_t byte_count_;
// The current offset, in bytes, from the start of `bytes_`.
size_t byte_offset_;
// The current offset, in bits, into the current byte.
size_t bit_offset_;
};
} // namespace rtc
#endif // RTC_BASE_BIT_BUFFER_H_

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2023 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 "rtc_base/bitrate_tracker.h"
#include "absl/types/optional.h"
#include "api/units/data_rate.h"
#include "api/units/timestamp.h"
#include "rtc_base/rate_statistics.h"
namespace webrtc {
BitrateTracker::BitrateTracker(TimeDelta max_window_size)
: impl_(max_window_size.ms(), RateStatistics::kBpsScale) {}
absl::optional<DataRate> BitrateTracker::Rate(Timestamp now) const {
if (absl::optional<int64_t> rate = impl_.Rate(now.ms())) {
return DataRate::BitsPerSec(*rate);
}
return absl::nullopt;
}
bool BitrateTracker::SetWindowSize(TimeDelta window_size, Timestamp now) {
return impl_.SetWindowSize(window_size.ms(), now.ms());
}
void BitrateTracker::Update(int64_t bytes, Timestamp now) {
impl_.Update(bytes, now.ms());
}
} // namespace webrtc

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2023 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.
*/
#ifndef RTC_BASE_BITRATE_TRACKER_H_
#define RTC_BASE_BITRATE_TRACKER_H_
#include <stddef.h>
#include <stdint.h>
#include "absl/types/optional.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "rtc_base/rate_statistics.h"
#include "rtc_base/system/rtc_export.h"
namespace webrtc {
// Class to estimate bitrates over running window.
// Timestamps used in Update(), Rate() and SetWindowSize() must never
// decrease for two consecutive calls.
// This class is thread unsafe.
class RTC_EXPORT BitrateTracker {
public:
// max_window_sizes = Maximum window size for the rate estimation.
// Initial window size is set to this, but may be changed
// to something lower by calling SetWindowSize().
explicit BitrateTracker(TimeDelta max_window_size);
BitrateTracker(const BitrateTracker&) = default;
BitrateTracker(BitrateTracker&&) = default;
BitrateTracker& operator=(const BitrateTracker&) = delete;
BitrateTracker& operator=(BitrateTracker&&) = delete;
~BitrateTracker() = default;
// Resets instance to original state.
void Reset() { impl_.Reset(); }
// Updates bitrate with a new data point, moving averaging window as needed.
void Update(int64_t bytes, Timestamp now);
void Update(DataSize size, Timestamp now) { Update(size.bytes(), now); }
// Returns bitrate, moving averaging window as needed.
// Returns nullopt when bitrate can't be measured.
absl::optional<DataRate> Rate(Timestamp now) const;
// Update the size of the averaging window. The maximum allowed value for
// `window_size` is `max_window_size` as supplied in the constructor.
bool SetWindowSize(TimeDelta window_size, Timestamp now);
private:
RateStatistics impl_;
};
} // namespace webrtc
#endif // RTC_BASE_BITRATE_TRACKER_H_

View file

@ -0,0 +1,167 @@
/*
* Copyright 2021 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 "rtc_base/bitstream_reader.h"
#include <stdint.h>
#include <limits>
#include "absl/numeric/bits.h"
#include "rtc_base/checks.h"
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
uint64_t BitstreamReader::ReadBits(int bits) {
RTC_DCHECK_GE(bits, 0);
RTC_DCHECK_LE(bits, 64);
set_last_read_is_verified(false);
if (remaining_bits_ < bits) {
remaining_bits_ -= bits;
return 0;
}
int remaining_bits_in_first_byte = remaining_bits_ % 8;
remaining_bits_ -= bits;
if (bits < remaining_bits_in_first_byte) {
// Reading fewer bits than what's left in the current byte, just
// return the portion of this byte that is needed.
int offset = (remaining_bits_in_first_byte - bits);
return ((*bytes_) >> offset) & ((1 << bits) - 1);
}
uint64_t result = 0;
if (remaining_bits_in_first_byte > 0) {
// Read all bits that were left in the current byte and consume that byte.
bits -= remaining_bits_in_first_byte;
uint8_t mask = (1 << remaining_bits_in_first_byte) - 1;
result = static_cast<uint64_t>(*bytes_ & mask) << bits;
++bytes_;
}
// Read as many full bytes as we can.
while (bits >= 8) {
bits -= 8;
result |= uint64_t{*bytes_} << bits;
++bytes_;
}
// Whatever is left to read is smaller than a byte, so grab just the needed
// bits and shift them into the lowest bits.
if (bits > 0) {
result |= (*bytes_ >> (8 - bits));
}
return result;
}
int BitstreamReader::ReadBit() {
set_last_read_is_verified(false);
--remaining_bits_;
if (remaining_bits_ < 0) {
return 0;
}
int bit_position = remaining_bits_ % 8;
if (bit_position == 0) {
// Read the last bit from current byte and move to the next byte.
return (*bytes_++) & 0x01;
}
return (*bytes_ >> bit_position) & 0x01;
}
void BitstreamReader::ConsumeBits(int bits) {
RTC_DCHECK_GE(bits, 0);
set_last_read_is_verified(false);
if (remaining_bits_ < bits) {
Invalidate();
return;
}
int remaining_bytes = (remaining_bits_ + 7) / 8;
remaining_bits_ -= bits;
int new_remaining_bytes = (remaining_bits_ + 7) / 8;
bytes_ += (remaining_bytes - new_remaining_bytes);
}
uint32_t BitstreamReader::ReadNonSymmetric(uint32_t num_values) {
RTC_DCHECK_GT(num_values, 0);
RTC_DCHECK_LE(num_values, uint32_t{1} << 31);
int width = absl::bit_width(num_values);
uint32_t num_min_bits_values = (uint32_t{1} << width) - num_values;
uint64_t val = ReadBits(width - 1);
if (val < num_min_bits_values) {
return val;
}
return (val << 1) + ReadBit() - num_min_bits_values;
}
uint32_t BitstreamReader::ReadExponentialGolomb() {
// Count the number of leading 0.
int zero_bit_count = 0;
while (ReadBit() == 0) {
if (++zero_bit_count >= 32) {
// Golob value won't fit into 32 bits of the return value. Fail the parse.
Invalidate();
return 0;
}
}
// The bit count of the value is the number of zeros + 1.
// However the first '1' was already read above.
return (uint32_t{1} << zero_bit_count) +
rtc::dchecked_cast<uint32_t>(ReadBits(zero_bit_count)) - 1;
}
int BitstreamReader::ReadSignedExponentialGolomb() {
uint32_t unsigned_val = ReadExponentialGolomb();
if ((unsigned_val & 1) == 0) {
return -static_cast<int>(unsigned_val / 2);
} else {
return (unsigned_val + 1) / 2;
}
}
uint64_t BitstreamReader::ReadLeb128() {
uint64_t decoded = 0;
size_t i = 0;
uint8_t byte;
// A LEB128 value can in theory be arbitrarily large, but for convenience sake
// consider it invalid if it can't fit in an uint64_t.
do {
byte = Read<uint8_t>();
decoded +=
(static_cast<uint64_t>(byte & 0x7f) << static_cast<uint64_t>(7 * i));
++i;
} while (i < 10 && (byte & 0x80));
// The first 9 bytes represent the first 63 bits. The tenth byte can therefore
// not be larger than 1 as it would overflow an uint64_t.
if (i == 10 && byte > 1) {
Invalidate();
}
return Ok() ? decoded : 0;
}
std::string BitstreamReader::ReadString(int num_bytes) {
std::string res;
res.reserve(num_bytes);
for (int i = 0; i < num_bytes; ++i) {
res += Read<uint8_t>();
}
return Ok() ? res : std::string();
}
} // namespace webrtc

View file

@ -0,0 +1,152 @@
/*
* Copyright 2021 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.
*/
#ifndef RTC_BASE_BITSTREAM_READER_H_
#define RTC_BASE_BITSTREAM_READER_H_
#include <stdint.h>
#include "absl/base/attributes.h"
#include "absl/strings/string_view.h"
#include "api/array_view.h"
#include "rtc_base/checks.h"
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
// A class to parse sequence of bits. Byte order is assumed big-endian/network.
// This class is optimized for successful parsing and binary size.
// Individual calls to `Read` and `ConsumeBits` never fail. Instead they may
// change the class state into 'failure state'. User of this class should verify
// parsing by checking if class is in that 'failure state' by calling `Ok`.
// That verification can be done once after multiple reads.
class BitstreamReader {
public:
explicit BitstreamReader(
rtc::ArrayView<const uint8_t> bytes ABSL_ATTRIBUTE_LIFETIME_BOUND);
explicit BitstreamReader(
absl::string_view bytes ABSL_ATTRIBUTE_LIFETIME_BOUND);
BitstreamReader(const BitstreamReader&) = default;
BitstreamReader& operator=(const BitstreamReader&) = default;
~BitstreamReader();
// Return number of unread bits in the buffer, or negative number if there
// was a reading error.
int RemainingBitCount() const;
// Returns `true` iff all calls to `Read` and `ConsumeBits` were successful.
bool Ok() const { return RemainingBitCount() >= 0; }
// Sets `BitstreamReader` into the failure state.
void Invalidate() { remaining_bits_ = -1; }
// Moves current read position forward. `bits` must be non-negative.
void ConsumeBits(int bits);
// Reads single bit. Returns 0 or 1.
ABSL_MUST_USE_RESULT int ReadBit();
// Reads `bits` from the bitstream. `bits` must be in range [0, 64].
// Returns an unsigned integer in range [0, 2^bits - 1].
// On failure sets `BitstreamReader` into the failure state and returns 0.
ABSL_MUST_USE_RESULT uint64_t ReadBits(int bits);
// Reads unsigned integer of fixed width.
template <typename T,
typename std::enable_if<std::is_unsigned<T>::value &&
!std::is_same<T, bool>::value &&
sizeof(T) <= 8>::type* = nullptr>
ABSL_MUST_USE_RESULT T Read() {
return rtc::dchecked_cast<T>(ReadBits(sizeof(T) * 8));
}
// Reads single bit as boolean.
template <
typename T,
typename std::enable_if<std::is_same<T, bool>::value>::type* = nullptr>
ABSL_MUST_USE_RESULT bool Read() {
return ReadBit() != 0;
}
// Reads value in range [0, `num_values` - 1].
// This encoding is similar to ReadBits(val, Ceil(Log2(num_values)),
// but reduces wastage incurred when encoding non-power of two value ranges
// Non symmetric values are encoded as:
// 1) n = bit_width(num_values)
// 2) k = (1 << n) - num_values
// Value v in range [0, k - 1] is encoded in (n-1) bits.
// Value v in range [k, num_values - 1] is encoded as (v+k) in n bits.
// https://aomediacodec.github.io/av1-spec/#nsn
uint32_t ReadNonSymmetric(uint32_t num_values);
// Reads exponential golomb encoded value.
// On failure sets `BitstreamReader` into the failure state and returns
// unspecified value.
// Exponential golomb values are encoded as:
// 1) x = source val + 1
// 2) In binary, write [bit_width(x) - 1] 0s, then x
// To decode, we count the number of leading 0 bits, read that many + 1 bits,
// and increment the result by 1.
// Fails the parsing if the value wouldn't fit in a uint32_t.
uint32_t ReadExponentialGolomb();
// Reads signed exponential golomb values at the current offset. Signed
// exponential golomb values are just the unsigned values mapped to the
// sequence 0, 1, -1, 2, -2, etc. in order.
// On failure sets `BitstreamReader` into the failure state and returns
// unspecified value.
int ReadSignedExponentialGolomb();
// Reads a LEB128 encoded value. The value will be considered invalid if it
// can't fit into a uint64_t.
uint64_t ReadLeb128();
std::string ReadString(int num_bytes);
private:
void set_last_read_is_verified(bool value) const;
// Next byte with at least one unread bit.
const uint8_t* bytes_;
// Number of bits remained to read.
int remaining_bits_;
// Unused in release mode.
mutable bool last_read_is_verified_ = true;
};
inline BitstreamReader::BitstreamReader(rtc::ArrayView<const uint8_t> bytes)
: bytes_(bytes.data()),
remaining_bits_(rtc::checked_cast<int>(bytes.size() * 8)) {}
inline BitstreamReader::BitstreamReader(absl::string_view bytes)
: bytes_(reinterpret_cast<const uint8_t*>(bytes.data())),
remaining_bits_(rtc::checked_cast<int>(bytes.size() * 8)) {}
inline BitstreamReader::~BitstreamReader() {
RTC_DCHECK(last_read_is_verified_) << "Latest calls to Read or ConsumeBit "
"were not checked with Ok function.";
}
inline void BitstreamReader::set_last_read_is_verified(bool value) const {
#ifdef RTC_DCHECK_IS_ON
last_read_is_verified_ = value;
#endif
}
inline int BitstreamReader::RemainingBitCount() const {
set_last_read_is_verified(true);
return remaining_bits_;
}
} // namespace webrtc
#endif // RTC_BASE_BITSTREAM_READER_H_

View file

@ -0,0 +1,412 @@
/*
* Copyright 2020 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 "rtc_base/boringssl_certificate.h"
#include "absl/strings/string_view.h"
#if defined(WEBRTC_WIN)
// Must be included first before openssl headers.
#include "rtc_base/win32.h" // NOLINT
#endif // WEBRTC_WIN
#include <openssl/asn1.h>
#include <openssl/bytestring.h>
#include <openssl/digest.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/pool.h>
#include <openssl/rand.h>
#include <time.h>
#include <cstring>
#include <memory>
#include <utility>
#include <vector>
#include "rtc_base/checks.h"
#include "rtc_base/helpers.h"
#include "rtc_base/logging.h"
#include "rtc_base/message_digest.h"
#include "rtc_base/openssl_digest.h"
#include "rtc_base/openssl_key_pair.h"
#include "rtc_base/openssl_utility.h"
namespace rtc {
namespace {
// List of OIDs of signature algorithms accepted by WebRTC.
// Taken from openssl/nid.h.
static const uint8_t kMD5WithRSA[] = {0x2b, 0x0e, 0x03, 0x02, 0x03};
static const uint8_t kMD5WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x04};
static const uint8_t kECDSAWithSHA1[] = {0x2a, 0x86, 0x48, 0xce,
0x3d, 0x04, 0x01};
static const uint8_t kDSAWithSHA1[] = {0x2a, 0x86, 0x48, 0xce,
0x38, 0x04, 0x03};
static const uint8_t kDSAWithSHA1_2[] = {0x2b, 0x0e, 0x03, 0x02, 0x1b};
static const uint8_t kSHA1WithRSA[] = {0x2b, 0x0e, 0x03, 0x02, 0x1d};
static const uint8_t kSHA1WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x05};
static const uint8_t kECDSAWithSHA224[] = {0x2a, 0x86, 0x48, 0xce,
0x3d, 0x04, 0x03, 0x01};
static const uint8_t kSHA224WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x0e};
static const uint8_t kDSAWithSHA224[] = {0x60, 0x86, 0x48, 0x01, 0x65,
0x03, 0x04, 0x03, 0x01};
static const uint8_t kECDSAWithSHA256[] = {0x2a, 0x86, 0x48, 0xce,
0x3d, 0x04, 0x03, 0x02};
static const uint8_t kSHA256WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x0b};
static const uint8_t kDSAWithSHA256[] = {0x60, 0x86, 0x48, 0x01, 0x65,
0x03, 0x04, 0x03, 0x02};
static const uint8_t kECDSAWithSHA384[] = {0x2a, 0x86, 0x48, 0xce,
0x3d, 0x04, 0x03, 0x03};
static const uint8_t kSHA384WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x0c};
static const uint8_t kECDSAWithSHA512[] = {0x2a, 0x86, 0x48, 0xce,
0x3d, 0x04, 0x03, 0x04};
static const uint8_t kSHA512WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x0d};
#if !defined(NDEBUG)
// Print a certificate to the log, for debugging.
static void PrintCert(BoringSSLCertificate* cert) {
// Since we're using CRYPTO_BUFFER, we can't use X509_print_ex, so we'll just
// print the PEM string.
RTC_DLOG(LS_VERBOSE) << "PEM representation of certificate:\n"
<< cert->ToPEMString();
}
#endif
bool AddSHA256SignatureAlgorithm(CBB* cbb, KeyType key_type) {
// An AlgorithmIdentifier is described in RFC 5280, 4.1.1.2.
CBB sequence, oid, params;
if (!CBB_add_asn1(cbb, &sequence, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&sequence, &oid, CBS_ASN1_OBJECT)) {
return false;
}
switch (key_type) {
case KT_RSA:
if (!CBB_add_bytes(&oid, kSHA256WithRSAEncryption,
sizeof(kSHA256WithRSAEncryption)) ||
!CBB_add_asn1(&sequence, &params, CBS_ASN1_NULL)) {
return false;
}
break;
case KT_ECDSA:
if (!CBB_add_bytes(&oid, kECDSAWithSHA256, sizeof(kECDSAWithSHA256))) {
return false;
}
break;
default:
RTC_DCHECK_NOTREACHED();
return false;
}
if (!CBB_flush(cbb)) {
return false;
}
return true;
}
// Adds an X.509 Common Name to `cbb`.
bool AddCommonName(CBB* cbb, absl::string_view common_name) {
// See RFC 4519.
static const uint8_t kCommonName[] = {0x55, 0x04, 0x03};
if (common_name.empty()) {
RTC_LOG(LS_ERROR) << "Common name cannot be empty.";
return false;
}
// See RFC 5280, section 4.1.2.4.
CBB rdns;
if (!CBB_add_asn1(cbb, &rdns, CBS_ASN1_SEQUENCE)) {
return false;
}
CBB rdn, attr, type, value;
if (!CBB_add_asn1(&rdns, &rdn, CBS_ASN1_SET) ||
!CBB_add_asn1(&rdn, &attr, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&attr, &type, CBS_ASN1_OBJECT) ||
!CBB_add_bytes(&type, kCommonName, sizeof(kCommonName)) ||
!CBB_add_asn1(&attr, &value, CBS_ASN1_UTF8STRING) ||
!CBB_add_bytes(&value,
reinterpret_cast<const uint8_t*>(common_name.data()),
common_name.size()) ||
!CBB_flush(cbb)) {
return false;
}
return true;
}
bool AddTime(CBB* cbb, time_t time) {
bssl::UniquePtr<ASN1_TIME> asn1_time(ASN1_TIME_new());
if (!asn1_time) {
return false;
}
if (!ASN1_TIME_set(asn1_time.get(), time)) {
return false;
}
unsigned tag;
switch (asn1_time->type) {
case V_ASN1_UTCTIME:
tag = CBS_ASN1_UTCTIME;
break;
case V_ASN1_GENERALIZEDTIME:
tag = CBS_ASN1_GENERALIZEDTIME;
break;
default:
return false;
}
CBB child;
if (!CBB_add_asn1(cbb, &child, tag) ||
!CBB_add_bytes(&child, asn1_time->data, asn1_time->length) ||
!CBB_flush(cbb)) {
return false;
}
return true;
}
// Generate a self-signed certificate, with the public key from the
// given key pair. Caller is responsible for freeing the returned object.
static bssl::UniquePtr<CRYPTO_BUFFER> MakeCertificate(
EVP_PKEY* pkey,
const SSLIdentityParams& params) {
RTC_LOG(LS_INFO) << "Making certificate for " << params.common_name;
// See RFC 5280, section 4.1. First, construct the TBSCertificate.
bssl::ScopedCBB cbb;
CBB tbs_cert, version, validity;
uint8_t* tbs_cert_bytes;
size_t tbs_cert_len;
uint64_t serial_number;
if (!CBB_init(cbb.get(), 64) ||
!CBB_add_asn1(cbb.get(), &tbs_cert, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&tbs_cert, &version,
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
!CBB_add_asn1_uint64(&version, 2) ||
!RAND_bytes(reinterpret_cast<uint8_t*>(&serial_number),
sizeof(serial_number)) ||
!CBB_add_asn1_uint64(&tbs_cert, serial_number) ||
!AddSHA256SignatureAlgorithm(&tbs_cert, params.key_params.type()) ||
!AddCommonName(&tbs_cert, params.common_name) || // issuer
!CBB_add_asn1(&tbs_cert, &validity, CBS_ASN1_SEQUENCE) ||
!AddTime(&validity, params.not_before) ||
!AddTime(&validity, params.not_after) ||
!AddCommonName(&tbs_cert, params.common_name) || // subject
!EVP_marshal_public_key(&tbs_cert, pkey) || // subjectPublicKeyInfo
!CBB_finish(cbb.get(), &tbs_cert_bytes, &tbs_cert_len)) {
return nullptr;
}
bssl::UniquePtr<uint8_t> delete_tbs_cert_bytes(tbs_cert_bytes);
// Sign the TBSCertificate and write the entire certificate.
CBB cert, signature;
bssl::ScopedEVP_MD_CTX ctx;
uint8_t* sig_out;
size_t sig_len;
uint8_t* cert_bytes;
size_t cert_len;
if (!CBB_init(cbb.get(), tbs_cert_len) ||
!CBB_add_asn1(cbb.get(), &cert, CBS_ASN1_SEQUENCE) ||
!CBB_add_bytes(&cert, tbs_cert_bytes, tbs_cert_len) ||
!AddSHA256SignatureAlgorithm(&cert, params.key_params.type()) ||
!CBB_add_asn1(&cert, &signature, CBS_ASN1_BITSTRING) ||
!CBB_add_u8(&signature, 0 /* no unused bits */) ||
!EVP_DigestSignInit(ctx.get(), nullptr, EVP_sha256(), nullptr, pkey) ||
// Compute the maximum signature length.
!EVP_DigestSign(ctx.get(), nullptr, &sig_len, tbs_cert_bytes,
tbs_cert_len) ||
!CBB_reserve(&signature, &sig_out, sig_len) ||
// Actually sign the TBSCertificate.
!EVP_DigestSign(ctx.get(), sig_out, &sig_len, tbs_cert_bytes,
tbs_cert_len) ||
!CBB_did_write(&signature, sig_len) ||
!CBB_finish(cbb.get(), &cert_bytes, &cert_len)) {
return nullptr;
}
bssl::UniquePtr<uint8_t> delete_cert_bytes(cert_bytes);
RTC_LOG(LS_INFO) << "Returning certificate";
return bssl::UniquePtr<CRYPTO_BUFFER>(
CRYPTO_BUFFER_new(cert_bytes, cert_len, openssl::GetBufferPool()));
}
} // namespace
BoringSSLCertificate::BoringSSLCertificate(
bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer)
: cert_buffer_(std::move(cert_buffer)) {
RTC_DCHECK(cert_buffer_ != nullptr);
}
std::unique_ptr<BoringSSLCertificate> BoringSSLCertificate::Generate(
OpenSSLKeyPair* key_pair,
const SSLIdentityParams& params) {
SSLIdentityParams actual_params(params);
if (actual_params.common_name.empty()) {
// Use a random string, arbitrarily 8 chars long.
actual_params.common_name = CreateRandomString(8);
}
bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer =
MakeCertificate(key_pair->pkey(), actual_params);
if (!cert_buffer) {
openssl::LogSSLErrors("Generating certificate");
return nullptr;
}
auto ret = std::make_unique<BoringSSLCertificate>(std::move(cert_buffer));
#if !defined(NDEBUG)
PrintCert(ret.get());
#endif
return ret;
}
std::unique_ptr<BoringSSLCertificate> BoringSSLCertificate::FromPEMString(
absl::string_view pem_string) {
std::string der;
if (!SSLIdentity::PemToDer(kPemTypeCertificate, pem_string, &der)) {
return nullptr;
}
bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer(
CRYPTO_BUFFER_new(reinterpret_cast<const uint8_t*>(der.c_str()),
der.length(), openssl::GetBufferPool()));
if (!cert_buffer) {
return nullptr;
}
return std::make_unique<BoringSSLCertificate>(std::move(cert_buffer));
}
#define OID_MATCHES(oid, oid_other) \
(CBS_len(&oid) == sizeof(oid_other) && \
0 == memcmp(CBS_data(&oid), oid_other, sizeof(oid_other)))
bool BoringSSLCertificate::GetSignatureDigestAlgorithm(
std::string* algorithm) const {
CBS oid;
if (!openssl::ParseCertificate(cert_buffer_.get(), &oid, nullptr)) {
RTC_LOG(LS_ERROR) << "Failed to parse certificate.";
return false;
}
if (OID_MATCHES(oid, kMD5WithRSA) ||
OID_MATCHES(oid, kMD5WithRSAEncryption)) {
*algorithm = DIGEST_MD5;
return true;
}
if (OID_MATCHES(oid, kECDSAWithSHA1) || OID_MATCHES(oid, kDSAWithSHA1) ||
OID_MATCHES(oid, kDSAWithSHA1_2) || OID_MATCHES(oid, kSHA1WithRSA) ||
OID_MATCHES(oid, kSHA1WithRSAEncryption)) {
*algorithm = DIGEST_SHA_1;
return true;
}
if (OID_MATCHES(oid, kECDSAWithSHA224) ||
OID_MATCHES(oid, kSHA224WithRSAEncryption) ||
OID_MATCHES(oid, kDSAWithSHA224)) {
*algorithm = DIGEST_SHA_224;
return true;
}
if (OID_MATCHES(oid, kECDSAWithSHA256) ||
OID_MATCHES(oid, kSHA256WithRSAEncryption) ||
OID_MATCHES(oid, kDSAWithSHA256)) {
*algorithm = DIGEST_SHA_256;
return true;
}
if (OID_MATCHES(oid, kECDSAWithSHA384) ||
OID_MATCHES(oid, kSHA384WithRSAEncryption)) {
*algorithm = DIGEST_SHA_384;
return true;
}
if (OID_MATCHES(oid, kECDSAWithSHA512) ||
OID_MATCHES(oid, kSHA512WithRSAEncryption)) {
*algorithm = DIGEST_SHA_512;
return true;
}
// Unknown algorithm. There are several unhandled options that are less
// common and more complex.
RTC_LOG(LS_ERROR) << "Unknown signature algorithm.";
algorithm->clear();
return false;
}
bool BoringSSLCertificate::ComputeDigest(absl::string_view algorithm,
unsigned char* digest,
size_t size,
size_t* length) const {
return ComputeDigest(cert_buffer_.get(), algorithm, digest, size, length);
}
bool BoringSSLCertificate::ComputeDigest(const CRYPTO_BUFFER* cert_buffer,
absl::string_view algorithm,
unsigned char* digest,
size_t size,
size_t* length) {
const EVP_MD* md = nullptr;
unsigned int n = 0;
if (!OpenSSLDigest::GetDigestEVP(algorithm, &md)) {
return false;
}
if (size < static_cast<size_t>(EVP_MD_size(md))) {
return false;
}
if (!EVP_Digest(CRYPTO_BUFFER_data(cert_buffer),
CRYPTO_BUFFER_len(cert_buffer), digest, &n, md, nullptr)) {
return false;
}
*length = n;
return true;
}
BoringSSLCertificate::~BoringSSLCertificate() {}
std::unique_ptr<SSLCertificate> BoringSSLCertificate::Clone() const {
return std::make_unique<BoringSSLCertificate>(
bssl::UpRef(cert_buffer_.get()));
}
std::string BoringSSLCertificate::ToPEMString() const {
return SSLIdentity::DerToPem(kPemTypeCertificate,
CRYPTO_BUFFER_data(cert_buffer_.get()),
CRYPTO_BUFFER_len(cert_buffer_.get()));
}
void BoringSSLCertificate::ToDER(Buffer* der_buffer) const {
der_buffer->SetData(CRYPTO_BUFFER_data(cert_buffer_.get()),
CRYPTO_BUFFER_len(cert_buffer_.get()));
}
bool BoringSSLCertificate::operator==(const BoringSSLCertificate& other) const {
return CRYPTO_BUFFER_len(cert_buffer_.get()) ==
CRYPTO_BUFFER_len(other.cert_buffer_.get()) &&
0 == memcmp(CRYPTO_BUFFER_data(cert_buffer_.get()),
CRYPTO_BUFFER_data(other.cert_buffer_.get()),
CRYPTO_BUFFER_len(cert_buffer_.get()));
}
bool BoringSSLCertificate::operator!=(const BoringSSLCertificate& other) const {
return !(*this == other);
}
int64_t BoringSSLCertificate::CertificateExpirationTime() const {
int64_t ret;
if (!openssl::ParseCertificate(cert_buffer_.get(), nullptr, &ret)) {
RTC_LOG(LS_ERROR) << "Failed to parse certificate.";
return -1;
}
return ret;
}
} // namespace rtc

View file

@ -0,0 +1,82 @@
/*
* Copyright 2020 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.
*/
#ifndef RTC_BASE_BORINGSSL_CERTIFICATE_H_
#define RTC_BASE_BORINGSSL_CERTIFICATE_H_
#include <openssl/ossl_typ.h>
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <string>
#include "absl/strings/string_view.h"
#include "rtc_base/buffer.h"
#include "rtc_base/ssl_certificate.h"
#include "rtc_base/ssl_identity.h"
namespace rtc {
class OpenSSLKeyPair;
// BoringSSLCertificate encapsulates a BoringSSL CRYPTO_BUFFER object holding a
// certificate, which is also reference counted inside the BoringSSL library.
// This offers binary size and memory improvements over the OpenSSL X509
// object.
class BoringSSLCertificate final : public SSLCertificate {
public:
explicit BoringSSLCertificate(bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer);
static std::unique_ptr<BoringSSLCertificate> Generate(
OpenSSLKeyPair* key_pair,
const SSLIdentityParams& params);
static std::unique_ptr<BoringSSLCertificate> FromPEMString(
absl::string_view pem_string);
~BoringSSLCertificate() override;
BoringSSLCertificate(const BoringSSLCertificate&) = delete;
BoringSSLCertificate& operator=(const BoringSSLCertificate&) = delete;
std::unique_ptr<SSLCertificate> Clone() const override;
CRYPTO_BUFFER* cert_buffer() const { return cert_buffer_.get(); }
std::string ToPEMString() const override;
void ToDER(Buffer* der_buffer) const override;
bool operator==(const BoringSSLCertificate& other) const;
bool operator!=(const BoringSSLCertificate& other) const;
// Compute the digest of the certificate given `algorithm`.
bool ComputeDigest(absl::string_view algorithm,
unsigned char* digest,
size_t size,
size_t* length) const override;
// Compute the digest of a certificate as a CRYPTO_BUFFER.
static bool ComputeDigest(const CRYPTO_BUFFER* cert_buffer,
absl::string_view algorithm,
unsigned char* digest,
size_t size,
size_t* length);
bool GetSignatureDigestAlgorithm(std::string* algorithm) const override;
int64_t CertificateExpirationTime() const override;
private:
// A handle to the DER encoded certificate data.
bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer_;
};
} // namespace rtc
#endif // RTC_BASE_BORINGSSL_CERTIFICATE_H_

View file

@ -0,0 +1,216 @@
/*
* Copyright 2020 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 "rtc_base/boringssl_identity.h"
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/pool.h>
#include <stdint.h>
#include <string.h>
#include <memory>
#include <utility>
#include <vector>
#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/openssl.h"
#include "rtc_base/openssl_utility.h"
namespace rtc {
BoringSSLIdentity::BoringSSLIdentity(
std::unique_ptr<OpenSSLKeyPair> key_pair,
std::unique_ptr<BoringSSLCertificate> certificate)
: key_pair_(std::move(key_pair)) {
RTC_DCHECK(key_pair_ != nullptr);
RTC_DCHECK(certificate != nullptr);
std::vector<std::unique_ptr<SSLCertificate>> certs;
certs.push_back(std::move(certificate));
cert_chain_.reset(new SSLCertChain(std::move(certs)));
}
BoringSSLIdentity::BoringSSLIdentity(std::unique_ptr<OpenSSLKeyPair> key_pair,
std::unique_ptr<SSLCertChain> cert_chain)
: key_pair_(std::move(key_pair)), cert_chain_(std::move(cert_chain)) {
RTC_DCHECK(key_pair_ != nullptr);
RTC_DCHECK(cert_chain_ != nullptr);
}
BoringSSLIdentity::~BoringSSLIdentity() = default;
std::unique_ptr<BoringSSLIdentity> BoringSSLIdentity::CreateInternal(
const SSLIdentityParams& params) {
auto key_pair = OpenSSLKeyPair::Generate(params.key_params);
if (key_pair) {
std::unique_ptr<BoringSSLCertificate> certificate(
BoringSSLCertificate::Generate(key_pair.get(), params));
if (certificate != nullptr) {
return absl::WrapUnique(
new BoringSSLIdentity(std::move(key_pair), std::move(certificate)));
}
}
RTC_LOG(LS_ERROR) << "Identity generation failed.";
return nullptr;
}
// static
std::unique_ptr<BoringSSLIdentity> BoringSSLIdentity::CreateWithExpiration(
absl::string_view common_name,
const KeyParams& key_params,
time_t certificate_lifetime) {
SSLIdentityParams params;
params.key_params = key_params;
params.common_name = std::string(common_name);
time_t now = time(nullptr);
params.not_before = now + kCertificateWindowInSeconds;
params.not_after = now + certificate_lifetime;
if (params.not_before > params.not_after)
return nullptr;
return CreateInternal(params);
}
std::unique_ptr<BoringSSLIdentity> BoringSSLIdentity::CreateForTest(
const SSLIdentityParams& params) {
return CreateInternal(params);
}
std::unique_ptr<SSLIdentity> BoringSSLIdentity::CreateFromPEMStrings(
absl::string_view private_key,
absl::string_view certificate) {
std::unique_ptr<BoringSSLCertificate> cert(
BoringSSLCertificate::FromPEMString(certificate));
if (!cert) {
RTC_LOG(LS_ERROR)
<< "Failed to create BoringSSLCertificate from PEM string.";
return nullptr;
}
auto key_pair = OpenSSLKeyPair::FromPrivateKeyPEMString(private_key);
if (!key_pair) {
RTC_LOG(LS_ERROR) << "Failed to create key pair from PEM string.";
return nullptr;
}
return absl::WrapUnique(
new BoringSSLIdentity(std::move(key_pair), std::move(cert)));
}
std::unique_ptr<SSLIdentity> BoringSSLIdentity::CreateFromPEMChainStrings(
absl::string_view private_key,
absl::string_view certificate_chain) {
bssl::UniquePtr<BIO> bio(
BIO_new_mem_buf(certificate_chain.data(),
rtc::dchecked_cast<int>(certificate_chain.size())));
if (!bio) {
return nullptr;
}
BIO_set_mem_eof_return(bio.get(), 0);
std::vector<std::unique_ptr<SSLCertificate>> certs;
while (true) {
char* name;
char* header;
unsigned char* data;
long len; // NOLINT
int ret = PEM_read_bio(bio.get(), &name, &header, &data, &len);
if (ret == 0) {
uint32_t err = ERR_peek_error();
if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
ERR_GET_REASON(err) == PEM_R_NO_START_LINE) {
break;
}
RTC_LOG(LS_ERROR) << "Failed to parse certificate from PEM string.";
return nullptr;
}
bssl::UniquePtr<char> owned_name(name);
bssl::UniquePtr<char> owned_header(header);
bssl::UniquePtr<unsigned char> owned_data(data);
if (strcmp(owned_name.get(), PEM_STRING_X509) != 0) {
RTC_LOG(LS_ERROR)
<< "Non-certificate found while parsing certificate chain: "
<< owned_name.get();
return nullptr;
}
bssl::UniquePtr<CRYPTO_BUFFER> crypto_buffer(
CRYPTO_BUFFER_new(data, len, openssl::GetBufferPool()));
if (!crypto_buffer) {
return nullptr;
}
certs.emplace_back(new BoringSSLCertificate(std::move(crypto_buffer)));
}
if (certs.empty()) {
RTC_LOG(LS_ERROR) << "Found no certificates in PEM string.";
return nullptr;
}
auto key_pair = OpenSSLKeyPair::FromPrivateKeyPEMString(private_key);
if (!key_pair) {
RTC_LOG(LS_ERROR) << "Failed to create key pair from PEM string.";
return nullptr;
}
return absl::WrapUnique(new BoringSSLIdentity(
std::move(key_pair), std::make_unique<SSLCertChain>(std::move(certs))));
}
const BoringSSLCertificate& BoringSSLIdentity::certificate() const {
return *static_cast<const BoringSSLCertificate*>(&cert_chain_->Get(0));
}
const SSLCertChain& BoringSSLIdentity::cert_chain() const {
return *cert_chain_.get();
}
std::unique_ptr<SSLIdentity> BoringSSLIdentity::CloneInternal() const {
// We cannot use std::make_unique here because the referenced
// BoringSSLIdentity constructor is private.
return absl::WrapUnique(
new BoringSSLIdentity(key_pair_->Clone(), cert_chain_->Clone()));
}
bool BoringSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) {
std::vector<CRYPTO_BUFFER*> cert_buffers;
for (size_t i = 0; i < cert_chain_->GetSize(); ++i) {
cert_buffers.push_back(
static_cast<const BoringSSLCertificate*>(&cert_chain_->Get(i))
->cert_buffer());
}
// 1 is the documented success return code.
if (1 != SSL_CTX_set_chain_and_key(ctx, &cert_buffers[0], cert_buffers.size(),
key_pair_->pkey(), nullptr)) {
openssl::LogSSLErrors("Configuring key and certificate");
return false;
}
return true;
}
std::string BoringSSLIdentity::PrivateKeyToPEMString() const {
return key_pair_->PrivateKeyToPEMString();
}
std::string BoringSSLIdentity::PublicKeyToPEMString() const {
return key_pair_->PublicKeyToPEMString();
}
bool BoringSSLIdentity::operator==(const BoringSSLIdentity& other) const {
return *this->key_pair_ == *other.key_pair_ &&
this->certificate() == other.certificate();
}
bool BoringSSLIdentity::operator!=(const BoringSSLIdentity& other) const {
return !(*this == other);
}
} // namespace rtc

View file

@ -0,0 +1,77 @@
/*
* Copyright 2020 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.
*/
#ifndef RTC_BASE_BORINGSSL_IDENTITY_H_
#define RTC_BASE_BORINGSSL_IDENTITY_H_
#include <openssl/ossl_typ.h>
#include <ctime>
#include <memory>
#include <string>
#include "absl/strings/string_view.h"
#include "rtc_base/boringssl_certificate.h"
#include "rtc_base/openssl_key_pair.h"
#include "rtc_base/ssl_certificate.h"
#include "rtc_base/ssl_identity.h"
namespace rtc {
// Holds a keypair and certificate together, and a method to generate them
// consistently. Uses CRYPTO_BUFFER instead of X509, which offers binary size
// and memory improvements.
class BoringSSLIdentity final : public SSLIdentity {
public:
static std::unique_ptr<BoringSSLIdentity> CreateWithExpiration(
absl::string_view common_name,
const KeyParams& key_params,
time_t certificate_lifetime);
static std::unique_ptr<BoringSSLIdentity> CreateForTest(
const SSLIdentityParams& params);
static std::unique_ptr<SSLIdentity> CreateFromPEMStrings(
absl::string_view private_key,
absl::string_view certificate);
static std::unique_ptr<SSLIdentity> CreateFromPEMChainStrings(
absl::string_view private_key,
absl::string_view certificate_chain);
~BoringSSLIdentity() override;
BoringSSLIdentity(const BoringSSLIdentity&) = delete;
BoringSSLIdentity& operator=(const BoringSSLIdentity&) = delete;
const BoringSSLCertificate& certificate() const override;
const SSLCertChain& cert_chain() const override;
// Configure an SSL context object to use our key and certificate.
bool ConfigureIdentity(SSL_CTX* ctx);
std::string PrivateKeyToPEMString() const override;
std::string PublicKeyToPEMString() const override;
bool operator==(const BoringSSLIdentity& other) const;
bool operator!=(const BoringSSLIdentity& other) const;
private:
BoringSSLIdentity(std::unique_ptr<OpenSSLKeyPair> key_pair,
std::unique_ptr<BoringSSLCertificate> certificate);
BoringSSLIdentity(std::unique_ptr<OpenSSLKeyPair> key_pair,
std::unique_ptr<SSLCertChain> cert_chain);
std::unique_ptr<SSLIdentity> CloneInternal() const override;
static std::unique_ptr<BoringSSLIdentity> CreateInternal(
const SSLIdentityParams& params);
std::unique_ptr<OpenSSLKeyPair> key_pair_;
std::unique_ptr<SSLCertChain> cert_chain_;
};
} // namespace rtc
#endif // RTC_BASE_BORINGSSL_IDENTITY_H_

View file

@ -0,0 +1,155 @@
/*
* Copyright 2020 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.
*/
#ifndef RTC_BASE_BOUNDED_INLINE_VECTOR_H_
#define RTC_BASE_BOUNDED_INLINE_VECTOR_H_
#include <stdint.h>
#include <memory>
#include <type_traits>
#include <utility>
#include "rtc_base/bounded_inline_vector_impl.h"
#include "rtc_base/checks.h"
namespace webrtc {
// A small std::vector-like type whose capacity is a compile-time constant. It
// stores all data inline and never heap allocates (beyond what its element type
// requires). Trying to grow it beyond its constant capacity is an error.
//
// TODO(bugs.webrtc.org/11391): Comparison operators.
// TODO(bugs.webrtc.org/11391): Methods for adding and deleting elements.
template <typename T, int fixed_capacity>
class BoundedInlineVector {
static_assert(!std::is_const<T>::value, "T may not be const");
static_assert(fixed_capacity > 0, "Capacity must be strictly positive");
public:
using size_type = int;
using value_type = T;
using const_iterator = const T*;
BoundedInlineVector() = default;
BoundedInlineVector(const BoundedInlineVector&) = default;
BoundedInlineVector(BoundedInlineVector&&) = default;
BoundedInlineVector& operator=(const BoundedInlineVector&) = default;
BoundedInlineVector& operator=(BoundedInlineVector&&) = default;
~BoundedInlineVector() = default;
// This constructor is implicit, to make it possible to write e.g.
//
// BoundedInlineVector<double, 7> x = {2.72, 3.14};
//
// and
//
// BoundedInlineVector<double, 7> GetConstants() {
// return {2.72, 3.14};
// }
template <typename... Ts,
typename std::enable_if_t<
bounded_inline_vector_impl::AllConvertible<T, Ts...>::value>* =
nullptr>
BoundedInlineVector(Ts&&... elements) // NOLINT(runtime/explicit)
: storage_(std::forward<Ts>(elements)...) {
static_assert(sizeof...(Ts) <= fixed_capacity, "");
}
template <
int other_capacity,
typename std::enable_if_t<other_capacity != fixed_capacity>* = nullptr>
BoundedInlineVector(const BoundedInlineVector<T, other_capacity>& other) {
RTC_DCHECK_LE(other.size(), fixed_capacity);
bounded_inline_vector_impl::CopyElements(other.data(), other.size(),
storage_.data, &storage_.size);
}
template <
int other_capacity,
typename std::enable_if_t<other_capacity != fixed_capacity>* = nullptr>
BoundedInlineVector(BoundedInlineVector<T, other_capacity>&& other) {
RTC_DCHECK_LE(other.size(), fixed_capacity);
bounded_inline_vector_impl::MoveElements(other.data(), other.size(),
storage_.data, &storage_.size);
}
template <
int other_capacity,
typename std::enable_if_t<other_capacity != fixed_capacity>* = nullptr>
BoundedInlineVector& operator=(
const BoundedInlineVector<T, other_capacity>& other) {
bounded_inline_vector_impl::DestroyElements(storage_.data, storage_.size);
RTC_DCHECK_LE(other.size(), fixed_capacity);
bounded_inline_vector_impl::CopyElements(other.data(), other.size(),
storage_.data, &storage_.size);
return *this;
}
template <
int other_capacity,
typename std::enable_if_t<other_capacity != fixed_capacity>* = nullptr>
BoundedInlineVector& operator=(
BoundedInlineVector<T, other_capacity>&& other) {
bounded_inline_vector_impl::DestroyElements(storage_.data, storage_.size);
RTC_DCHECK_LE(other.size(), fixed_capacity);
bounded_inline_vector_impl::MoveElements(other.data(), other.size(),
storage_.data, &storage_.size);
return *this;
}
bool empty() const { return storage_.size == 0; }
int size() const { return storage_.size; }
constexpr int capacity() const { return fixed_capacity; }
// Resizes the BoundedInlineVector to the given size, which must not exceed
// its constant capacity. If the size is increased, the added elements are
// default constructed.
void resize(int new_size) {
RTC_DCHECK_GE(new_size, 0);
RTC_DCHECK_LE(new_size, fixed_capacity);
if (new_size > storage_.size) {
bounded_inline_vector_impl::DefaultInitializeElements(
storage_.data + storage_.size, new_size - storage_.size);
} else if (new_size < storage_.size) {
bounded_inline_vector_impl::DestroyElements(storage_.data + new_size,
storage_.size - new_size);
}
storage_.size = new_size;
}
const T* data() const { return storage_.data; }
T* data() { return storage_.data; }
const T& operator[](int index) const {
RTC_DCHECK_GE(index, 0);
RTC_DCHECK_LT(index, storage_.size);
return storage_.data[index];
}
T& operator[](int index) {
RTC_DCHECK_GE(index, 0);
RTC_DCHECK_LT(index, storage_.size);
return storage_.data[index];
}
T* begin() { return storage_.data; }
T* end() { return storage_.data + storage_.size; }
const T* begin() const { return storage_.data; }
const T* end() const { return storage_.data + storage_.size; }
const T* cbegin() const { return storage_.data; }
const T* cend() const { return storage_.data + storage_.size; }
private:
bounded_inline_vector_impl::Storage<T, fixed_capacity> storage_;
};
} // namespace webrtc
#endif // RTC_BASE_BOUNDED_INLINE_VECTOR_H_

View file

@ -0,0 +1,225 @@
/*
* Copyright 2020 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.
*/
#ifndef RTC_BASE_BOUNDED_INLINE_VECTOR_IMPL_H_
#define RTC_BASE_BOUNDED_INLINE_VECTOR_IMPL_H_
#include <stdint.h>
#include <cstring>
#include <memory>
#include <type_traits>
#include <utility>
namespace webrtc {
namespace bounded_inline_vector_impl {
template <bool...>
struct BoolPack;
// Tests if all its parameters (x0, x1, ..., xn) are true. The implementation
// checks whether (x0, x1, ..., xn, true) == (true, x0, x1, ..., xn), which is
// true iff true == x0 && x0 == x1 && x1 == x2 ... && xn-1 == xn && xn == true.
template <bool... Bs>
using AllTrue = std::is_same<BoolPack<Bs..., true>, BoolPack<true, Bs...>>;
template <typename To, typename... Froms>
using AllConvertible = AllTrue<std::is_convertible<Froms, To>::value...>;
// Initializes part of an uninitialized array. Unlike normal array
// initialization, does not zero the remaining array elements. Caller is
// responsible for ensuring that there is enough space in `data`.
template <typename T>
void InitializeElements(T* data) {}
template <typename T, typename U, typename... Us>
void InitializeElements(T* data, U&& element, Us&&... elements) {
// Placement new, because we construct a new object in uninitialized memory.
::new (data) T(std::forward<U>(element));
InitializeElements(data + 1, std::forward<Us>(elements)...);
}
// Default initializes uninitialized array elements.
// TODO(kwiberg): Replace with std::uninitialized_default_construct_n() (C++17).
template <typename T>
void DefaultInitializeElements(T* data, int size) {
for (int i = 0; i < size; ++i) {
// Placement new, because we construct a new object in uninitialized memory.
::new (&data[i]) T;
}
}
// Copies from source to uninitialized destination. Caller is responsible for
// ensuring that there is enough space in `dst_data`.
template <typename T>
void CopyElements(const T* src_data, int src_size, T* dst_data, int* dst_size) {
if /*constexpr*/ (std::is_trivially_copy_constructible<T>::value) {
std::memcpy(dst_data, src_data, src_size * sizeof(T));
} else {
std::uninitialized_copy_n(src_data, src_size, dst_data);
}
*dst_size = src_size;
}
// Moves from source to uninitialized destination. Caller is responsible for
// ensuring that there is enough space in `dst_data`.
template <typename T>
void MoveElements(T* src_data, int src_size, T* dst_data, int* dst_size) {
if /*constexpr*/ (std::is_trivially_move_constructible<T>::value) {
std::memcpy(dst_data, src_data, src_size * sizeof(T));
} else {
// TODO(kwiberg): Use std::uninitialized_move_n() instead (C++17).
for (int i = 0; i < src_size; ++i) {
// Placement new, because we create a new object in uninitialized
// memory.
::new (&dst_data[i]) T(std::move(src_data[i]));
}
}
*dst_size = src_size;
}
// Destroys elements, leaving them uninitialized.
template <typename T>
void DestroyElements(T* data, int size) {
if /*constexpr*/ (!std::is_trivially_destructible<T>::value) {
for (int i = 0; i < size; ++i) {
data[i].~T();
}
}
}
// If elements are trivial and the total capacity is at most this many bytes,
// copy everything instead of just the elements that are in use; this is more
// efficient, and makes BoundedInlineVector trivially copyable.
static constexpr int kSmallSize = 64;
// Storage implementations.
//
// There are diferent Storage structs for diferent kinds of element types. The
// common contract is the following:
//
// * They have public `size` variables and `data` array members.
//
// * Their owner is responsible for enforcing the invariant that the first
// `size` elements in `data` are initialized, and the remaining elements are
// not initialized.
//
// * They implement default construction, construction with one or more
// elements, copy/move construction, copy/move assignment, and destruction;
// the owner must ensure that the invariant holds whenever these operations
// occur.
// Storage implementation for nontrivial element types.
template <typename T,
int fixed_capacity,
bool is_trivial = std::is_trivial<T>::value,
bool is_small = (sizeof(T) * fixed_capacity <= kSmallSize)>
struct Storage {
static_assert(!std::is_trivial<T>::value, "");
template <
typename... Ts,
typename std::enable_if_t<AllConvertible<T, Ts...>::value>* = nullptr>
explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) {
InitializeElements(data, std::forward<Ts>(elements)...);
}
Storage(const Storage& other) {
CopyElements(other.data, other.size, data, &size);
}
Storage(Storage&& other) {
MoveElements(other.data, other.size, data, &size);
}
Storage& operator=(const Storage& other) {
if (this != &other) {
DestroyElements(data, size);
CopyElements(other.data, other.size, data, &size);
}
return *this;
}
Storage& operator=(Storage&& other) {
DestroyElements(data, size);
size = 0; // Needed in case of self assignment.
MoveElements(other.data, other.size, data, &size);
return *this;
}
~Storage() { DestroyElements(data, size); }
int size;
union {
// Since this array is in a union, we get to construct and destroy it
// manually.
T data[fixed_capacity]; // NOLINT(runtime/arrays)
};
};
// Storage implementation for trivial element types when the capacity is small
// enough that we can cheaply copy everything.
template <typename T, int fixed_capacity>
struct Storage<T, fixed_capacity, /*is_trivial=*/true, /*is_small=*/true> {
static_assert(std::is_trivial<T>::value, "");
static_assert(sizeof(T) * fixed_capacity <= kSmallSize, "");
template <
typename... Ts,
typename std::enable_if_t<AllConvertible<T, Ts...>::value>* = nullptr>
explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) {
InitializeElements(data, std::forward<Ts>(elements)...);
}
Storage(const Storage&) = default;
Storage& operator=(const Storage&) = default;
~Storage() = default;
int size;
T data[fixed_capacity]; // NOLINT(runtime/arrays)
};
// Storage implementation for trivial element types when the capacity is large
// enough that we want to avoid copying uninitialized elements.
template <typename T, int fixed_capacity>
struct Storage<T, fixed_capacity, /*is_trivial=*/true, /*is_small=*/false> {
static_assert(std::is_trivial<T>::value, "");
static_assert(sizeof(T) * fixed_capacity > kSmallSize, "");
template <
typename... Ts,
typename std::enable_if_t<AllConvertible<T, Ts...>::value>* = nullptr>
explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) {
InitializeElements(data, std::forward<Ts>(elements)...);
}
Storage(const Storage& other) : size(other.size) {
std::memcpy(data, other.data, other.size * sizeof(T));
}
Storage& operator=(const Storage& other) {
if (this != &other) {
size = other.size;
std::memcpy(data, other.data, other.size * sizeof(T));
}
return *this;
}
~Storage() = default;
int size;
union {
T data[fixed_capacity]; // NOLINT(runtime/arrays)
};
};
} // namespace bounded_inline_vector_impl
} // namespace webrtc
#endif // RTC_BASE_BOUNDED_INLINE_VECTOR_IMPL_H_

View file

@ -0,0 +1,452 @@
/*
* Copyright 2004 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.
*/
#ifndef RTC_BASE_BUFFER_H_
#define RTC_BASE_BUFFER_H_
#include <stdint.h>
#include <algorithm>
#include <cstring>
#include <memory>
#include <type_traits>
#include <utility>
#include "absl/strings/string_view.h"
#include "api/array_view.h"
#include "rtc_base/checks.h"
#include "rtc_base/type_traits.h"
#include "rtc_base/zero_memory.h"
namespace rtc {
namespace internal {
// (Internal; please don't use outside this file.) Determines if elements of
// type U are compatible with a BufferT<T>. For most types, we just ignore
// top-level const and forbid top-level volatile and require T and U to be
// otherwise equal, but all byte-sized integers (notably char, int8_t, and
// uint8_t) are compatible with each other. (Note: We aim to get rid of this
// behavior, and treat all types the same.)
template <typename T, typename U>
struct BufferCompat {
static constexpr bool value =
!std::is_volatile<U>::value &&
((std::is_integral<T>::value && sizeof(T) == 1)
? (std::is_integral<U>::value && sizeof(U) == 1)
: (std::is_same<T, typename std::remove_const<U>::type>::value));
};
} // namespace internal
// Basic buffer class, can be grown and shrunk dynamically.
// Unlike std::string/vector, does not initialize data when increasing size.
// If "ZeroOnFree" is true, any memory is explicitly cleared before releasing.
// The type alias "ZeroOnFreeBuffer" below should be used instead of setting
// "ZeroOnFree" in the template manually to "true".
template <typename T, bool ZeroOnFree = false>
class BufferT {
// We want T's destructor and default constructor to be trivial, i.e. perform
// no action, so that we don't have to touch the memory we allocate and
// deallocate. And we want T to be trivially copyable, so that we can copy T
// instances with std::memcpy. This is precisely the definition of a trivial
// type.
static_assert(std::is_trivial<T>::value, "T must be a trivial type.");
// This class relies heavily on being able to mutate its data.
static_assert(!std::is_const<T>::value, "T may not be const");
public:
using value_type = T;
using const_iterator = const T*;
// An empty BufferT.
BufferT() : size_(0), capacity_(0), data_(nullptr) {
RTC_DCHECK(IsConsistent());
}
// Disable copy construction and copy assignment, since copying a buffer is
// expensive enough that we want to force the user to be explicit about it.
BufferT(const BufferT&) = delete;
BufferT& operator=(const BufferT&) = delete;
BufferT(BufferT&& buf)
: size_(buf.size()),
capacity_(buf.capacity()),
data_(std::move(buf.data_)) {
RTC_DCHECK(IsConsistent());
buf.OnMovedFrom();
}
// Construct a buffer with the specified number of uninitialized elements.
explicit BufferT(size_t size) : BufferT(size, size) {}
BufferT(size_t size, size_t capacity)
: size_(size),
capacity_(std::max(size, capacity)),
data_(capacity_ > 0 ? new T[capacity_] : nullptr) {
RTC_DCHECK(IsConsistent());
}
// Construct a buffer and copy the specified number of elements into it.
template <typename U,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
BufferT(const U* data, size_t size) : BufferT(data, size, size) {}
template <typename U,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
BufferT(U* data, size_t size, size_t capacity) : BufferT(size, capacity) {
static_assert(sizeof(T) == sizeof(U), "");
if (size > 0) {
RTC_DCHECK(data);
std::memcpy(data_.get(), data, size * sizeof(U));
}
}
// Construct a buffer from the contents of an array.
template <typename U,
size_t N,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
BufferT(U (&array)[N]) : BufferT(array, N) {}
~BufferT() { MaybeZeroCompleteBuffer(); }
// Implicit conversion to absl::string_view if T is compatible with char.
template <typename U = T>
operator typename std::enable_if<internal::BufferCompat<U, char>::value,
absl::string_view>::type() const {
return absl::string_view(data<char>(), size());
}
// Get a pointer to the data. Just .data() will give you a (const) T*, but if
// T is a byte-sized integer, you may also use .data<U>() for any other
// byte-sized integer U.
template <typename U = T,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
const U* data() const {
RTC_DCHECK(IsConsistent());
return reinterpret_cast<U*>(data_.get());
}
template <typename U = T,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
U* data() {
RTC_DCHECK(IsConsistent());
return reinterpret_cast<U*>(data_.get());
}
bool empty() const {
RTC_DCHECK(IsConsistent());
return size_ == 0;
}
size_t size() const {
RTC_DCHECK(IsConsistent());
return size_;
}
size_t capacity() const {
RTC_DCHECK(IsConsistent());
return capacity_;
}
BufferT& operator=(BufferT&& buf) {
RTC_DCHECK(buf.IsConsistent());
MaybeZeroCompleteBuffer();
size_ = buf.size_;
capacity_ = buf.capacity_;
using std::swap;
swap(data_, buf.data_);
buf.data_.reset();
buf.OnMovedFrom();
return *this;
}
bool operator==(const BufferT& buf) const {
RTC_DCHECK(IsConsistent());
if (size_ != buf.size_) {
return false;
}
if (std::is_integral<T>::value) {
// Optimization.
return std::memcmp(data_.get(), buf.data_.get(), size_ * sizeof(T)) == 0;
}
for (size_t i = 0; i < size_; ++i) {
if (data_[i] != buf.data_[i]) {
return false;
}
}
return true;
}
bool operator!=(const BufferT& buf) const { return !(*this == buf); }
T& operator[](size_t index) {
RTC_DCHECK_LT(index, size_);
return data()[index];
}
T operator[](size_t index) const {
RTC_DCHECK_LT(index, size_);
return data()[index];
}
T* begin() { return data(); }
T* end() { return data() + size(); }
const T* begin() const { return data(); }
const T* end() const { return data() + size(); }
const T* cbegin() const { return data(); }
const T* cend() const { return data() + size(); }
// The SetData functions replace the contents of the buffer. They accept the
// same input types as the constructors.
template <typename U,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
void SetData(const U* data, size_t size) {
RTC_DCHECK(IsConsistent());
const size_t old_size = size_;
size_ = 0;
AppendData(data, size);
if (ZeroOnFree && size_ < old_size) {
ZeroTrailingData(old_size - size_);
}
}
template <typename U,
size_t N,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
void SetData(const U (&array)[N]) {
SetData(array, N);
}
template <typename W,
typename std::enable_if<
HasDataAndSize<const W, const T>::value>::type* = nullptr>
void SetData(const W& w) {
SetData(w.data(), w.size());
}
// Replaces the data in the buffer with at most `max_elements` of data, using
// the function `setter`, which should have the following signature:
//
// size_t setter(ArrayView<U> view)
//
// `setter` is given an appropriately typed ArrayView of length exactly
// `max_elements` that describes the area where it should write the data; it
// should return the number of elements actually written. (If it doesn't fill
// the whole ArrayView, it should leave the unused space at the end.)
template <typename U = T,
typename F,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
size_t SetData(size_t max_elements, F&& setter) {
RTC_DCHECK(IsConsistent());
const size_t old_size = size_;
size_ = 0;
const size_t written = AppendData<U>(max_elements, std::forward<F>(setter));
if (ZeroOnFree && size_ < old_size) {
ZeroTrailingData(old_size - size_);
}
return written;
}
// The AppendData functions add data to the end of the buffer. They accept
// the same input types as the constructors.
template <typename U,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
void AppendData(const U* data, size_t size) {
if (size == 0) {
return;
}
RTC_DCHECK(data);
RTC_DCHECK(IsConsistent());
const size_t new_size = size_ + size;
EnsureCapacityWithHeadroom(new_size, true);
static_assert(sizeof(T) == sizeof(U), "");
std::memcpy(data_.get() + size_, data, size * sizeof(U));
size_ = new_size;
RTC_DCHECK(IsConsistent());
}
template <typename U,
size_t N,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
void AppendData(const U (&array)[N]) {
AppendData(array, N);
}
template <typename W,
typename std::enable_if<
HasDataAndSize<const W, const T>::value>::type* = nullptr>
void AppendData(const W& w) {
AppendData(w.data(), w.size());
}
template <typename U,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
void AppendData(const U& item) {
AppendData(&item, 1);
}
// Appends at most `max_elements` to the end of the buffer, using the function
// `setter`, which should have the following signature:
//
// size_t setter(ArrayView<U> view)
//
// `setter` is given an appropriately typed ArrayView of length exactly
// `max_elements` that describes the area where it should write the data; it
// should return the number of elements actually written. (If it doesn't fill
// the whole ArrayView, it should leave the unused space at the end.)
template <typename U = T,
typename F,
typename std::enable_if<
internal::BufferCompat<T, U>::value>::type* = nullptr>
size_t AppendData(size_t max_elements, F&& setter) {
RTC_DCHECK(IsConsistent());
const size_t old_size = size_;
SetSize(old_size + max_elements);
U* base_ptr = data<U>() + old_size;
size_t written_elements = setter(rtc::ArrayView<U>(base_ptr, max_elements));
RTC_CHECK_LE(written_elements, max_elements);
size_ = old_size + written_elements;
RTC_DCHECK(IsConsistent());
return written_elements;
}
// Sets the size of the buffer. If the new size is smaller than the old, the
// buffer contents will be kept but truncated; if the new size is greater,
// the existing contents will be kept and the new space will be
// uninitialized.
void SetSize(size_t size) {
const size_t old_size = size_;
EnsureCapacityWithHeadroom(size, true);
size_ = size;
if (ZeroOnFree && size_ < old_size) {
ZeroTrailingData(old_size - size_);
}
}
// Ensure that the buffer size can be increased to at least capacity without
// further reallocation. (Of course, this operation might need to reallocate
// the buffer.)
void EnsureCapacity(size_t capacity) {
// Don't allocate extra headroom, since the user is asking for a specific
// capacity.
EnsureCapacityWithHeadroom(capacity, false);
}
// Resets the buffer to zero size without altering capacity. Works even if the
// buffer has been moved from.
void Clear() {
MaybeZeroCompleteBuffer();
size_ = 0;
RTC_DCHECK(IsConsistent());
}
// Swaps two buffers. Also works for buffers that have been moved from.
friend void swap(BufferT& a, BufferT& b) {
using std::swap;
swap(a.size_, b.size_);
swap(a.capacity_, b.capacity_);
swap(a.data_, b.data_);
}
private:
void EnsureCapacityWithHeadroom(size_t capacity, bool extra_headroom) {
RTC_DCHECK(IsConsistent());
if (capacity <= capacity_)
return;
// If the caller asks for extra headroom, ensure that the new capacity is
// >= 1.5 times the old capacity. Any constant > 1 is sufficient to prevent
// quadratic behavior; as to why we pick 1.5 in particular, see
// https://github.com/facebook/folly/blob/master/folly/docs/FBVector.md and
// http://www.gahcep.com/cpp-internals-stl-vector-part-1/.
const size_t new_capacity =
extra_headroom ? std::max(capacity, capacity_ + capacity_ / 2)
: capacity;
std::unique_ptr<T[]> new_data(new T[new_capacity]);
if (data_ != nullptr) {
std::memcpy(new_data.get(), data_.get(), size_ * sizeof(T));
}
MaybeZeroCompleteBuffer();
data_ = std::move(new_data);
capacity_ = new_capacity;
RTC_DCHECK(IsConsistent());
}
// Zero the complete buffer if template argument "ZeroOnFree" is true.
void MaybeZeroCompleteBuffer() {
if (ZeroOnFree && capacity_ > 0) {
// It would be sufficient to only zero "size_" elements, as all other
// methods already ensure that the unused capacity contains no sensitive
// data---but better safe than sorry.
ExplicitZeroMemory(data_.get(), capacity_ * sizeof(T));
}
}
// Zero the first "count" elements of unused capacity.
void ZeroTrailingData(size_t count) {
RTC_DCHECK(IsConsistent());
RTC_DCHECK_LE(count, capacity_ - size_);
ExplicitZeroMemory(data_.get() + size_, count * sizeof(T));
}
// Precondition for all methods except Clear, operator= and the destructor.
// Postcondition for all methods except move construction and move
// assignment, which leave the moved-from object in a possibly inconsistent
// state.
bool IsConsistent() const {
return (data_ || capacity_ == 0) && capacity_ >= size_;
}
// Called when *this has been moved from. Conceptually it's a no-op, but we
// can mutate the state slightly to help subsequent sanity checks catch bugs.
void OnMovedFrom() {
RTC_DCHECK(!data_); // Our heap block should have been stolen.
#if RTC_DCHECK_IS_ON
// Ensure that *this is always inconsistent, to provoke bugs.
size_ = 1;
capacity_ = 0;
#else
// Make *this consistent and empty. Shouldn't be necessary, but better safe
// than sorry.
size_ = 0;
capacity_ = 0;
#endif
}
size_t size_;
size_t capacity_;
std::unique_ptr<T[]> data_;
};
// By far the most common sort of buffer.
using Buffer = BufferT<uint8_t>;
// A buffer that zeros memory before releasing it.
template <typename T>
using ZeroOnFreeBuffer = BufferT<T, true>;
} // namespace rtc
#endif // RTC_BASE_BUFFER_H_

View file

@ -0,0 +1,85 @@
/*
* Copyright 2015 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 "rtc_base/buffer_queue.h"
#include <stdint.h>
#include <string.h>
#include <algorithm>
namespace rtc {
BufferQueue::BufferQueue(size_t capacity, size_t default_size)
: capacity_(capacity), default_size_(default_size) {}
BufferQueue::~BufferQueue() {
RTC_DCHECK_RUN_ON(&sequence_checker_);
for (Buffer* buffer : queue_)
delete buffer;
for (Buffer* buffer : free_list_)
delete buffer;
}
size_t BufferQueue::size() const {
RTC_DCHECK_RUN_ON(&sequence_checker_);
return queue_.size();
}
void BufferQueue::Clear() {
RTC_DCHECK_RUN_ON(&sequence_checker_);
while (!queue_.empty()) {
free_list_.push_back(queue_.front());
queue_.pop_front();
}
}
bool BufferQueue::ReadFront(void* buffer, size_t bytes, size_t* bytes_read) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
if (queue_.empty())
return false;
Buffer* packet = queue_.front();
queue_.pop_front();
bytes = std::min(bytes, packet->size());
memcpy(buffer, packet->data(), bytes);
if (bytes_read)
*bytes_read = bytes;
free_list_.push_back(packet);
return true;
}
bool BufferQueue::WriteBack(const void* buffer,
size_t bytes,
size_t* bytes_written) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
if (queue_.size() == capacity_)
return false;
Buffer* packet;
if (!free_list_.empty()) {
packet = free_list_.back();
free_list_.pop_back();
} else {
packet = new Buffer(bytes, default_size_);
}
packet->SetData(static_cast<const uint8_t*>(buffer), bytes);
if (bytes_written)
*bytes_written = bytes;
queue_.push_back(packet);
return true;
}
} // namespace rtc

View file

@ -0,0 +1,70 @@
/*
* Copyright 2015 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.
*/
#ifndef RTC_BASE_BUFFER_QUEUE_H_
#define RTC_BASE_BUFFER_QUEUE_H_
#include <stddef.h>
#include <deque>
#include <vector>
#include "api/sequence_checker.h"
#include "rtc_base/buffer.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/thread_annotations.h"
namespace rtc {
class BufferQueue final {
public:
// Creates a buffer queue with a given capacity and default buffer size.
BufferQueue(size_t capacity, size_t default_size);
~BufferQueue();
BufferQueue(const BufferQueue&) = delete;
BufferQueue& operator=(const BufferQueue&) = delete;
// Return number of queued buffers.
size_t size() const;
// Clear the BufferQueue by moving all Buffers from `queue_` to `free_list_`.
void Clear();
// ReadFront will only read one buffer at a time and will truncate buffers
// that don't fit in the passed memory.
// Returns true unless no data could be returned.
bool ReadFront(void* data, size_t bytes, size_t* bytes_read);
// WriteBack always writes either the complete memory or nothing.
// Returns true unless no data could be written.
bool WriteBack(const void* data, size_t bytes, size_t* bytes_written);
bool is_writable() const {
RTC_DCHECK_RUN_ON(&sequence_checker_);
return queue_.size() < capacity_;
}
bool is_readable() const {
RTC_DCHECK_RUN_ON(&sequence_checker_);
return !queue_.empty();
}
private:
RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker sequence_checker_;
const size_t capacity_;
const size_t default_size_;
std::deque<Buffer*> queue_ RTC_GUARDED_BY(sequence_checker_);
std::vector<Buffer*> free_list_ RTC_GUARDED_BY(sequence_checker_);
};
} // namespace rtc
#endif // RTC_BASE_BUFFER_QUEUE_H_

View file

@ -0,0 +1,170 @@
/*
* Copyright 2004 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 "rtc_base/byte_buffer.h"
#include <string.h>
namespace rtc {
ByteBufferWriter::ByteBufferWriter() : ByteBufferWriterT() {}
ByteBufferWriter::ByteBufferWriter(const uint8_t* bytes, size_t len)
: ByteBufferWriterT(bytes, len) {}
ByteBufferReader::ByteBufferReader(rtc::ArrayView<const uint8_t> bytes) {
Construct(bytes.data(), bytes.size());
}
ByteBufferReader::ByteBufferReader(const ByteBufferWriter& buf) {
Construct(reinterpret_cast<const uint8_t*>(buf.Data()), buf.Length());
}
void ByteBufferReader::Construct(const uint8_t* bytes, size_t len) {
bytes_ = bytes;
size_ = len;
start_ = 0;
end_ = len;
}
bool ByteBufferReader::ReadUInt8(uint8_t* val) {
if (!val)
return false;
return ReadBytes(val, 1);
}
bool ByteBufferReader::ReadUInt16(uint16_t* val) {
if (!val)
return false;
uint16_t v;
if (!ReadBytes(reinterpret_cast<uint8_t*>(&v), 2)) {
return false;
} else {
*val = NetworkToHost16(v);
return true;
}
}
bool ByteBufferReader::ReadUInt24(uint32_t* val) {
if (!val)
return false;
uint32_t v = 0;
uint8_t* read_into = reinterpret_cast<uint8_t*>(&v);
++read_into;
if (!ReadBytes(read_into, 3)) {
return false;
} else {
*val = NetworkToHost32(v);
return true;
}
}
bool ByteBufferReader::ReadUInt32(uint32_t* val) {
if (!val)
return false;
uint32_t v;
if (!ReadBytes(reinterpret_cast<uint8_t*>(&v), 4)) {
return false;
} else {
*val = NetworkToHost32(v);
return true;
}
}
bool ByteBufferReader::ReadUInt64(uint64_t* val) {
if (!val)
return false;
uint64_t v;
if (!ReadBytes(reinterpret_cast<uint8_t*>(&v), 8)) {
return false;
} else {
*val = NetworkToHost64(v);
return true;
}
}
bool ByteBufferReader::ReadUVarint(uint64_t* val) {
if (!val) {
return false;
}
// Integers are deserialized 7 bits at a time, with each byte having a
// continuation byte (msb=1) if there are more bytes to be read.
uint64_t v = 0;
for (int i = 0; i < 64; i += 7) {
uint8_t byte;
if (!ReadBytes(&byte, 1)) {
return false;
}
// Read the first 7 bits of the byte, then offset by bits read so far.
v |= (static_cast<uint64_t>(byte) & 0x7F) << i;
// Return if the msb is not a continuation byte.
if (byte < 0x80) {
*val = v;
return true;
}
}
return false;
}
bool ByteBufferReader::ReadString(std::string* val, size_t len) {
if (!val)
return false;
if (len > Length()) {
return false;
} else {
val->append(reinterpret_cast<const char*>(bytes_ + start_), len);
start_ += len;
return true;
}
}
bool ByteBufferReader::ReadStringView(absl::string_view* val, size_t len) {
if (!val || len > Length())
return false;
*val = absl::string_view(reinterpret_cast<const char*>(bytes_ + start_), len);
start_ += len;
return true;
}
bool ByteBufferReader::ReadBytes(rtc::ArrayView<uint8_t> val) {
if (val.size() == 0) {
return true;
}
return ReadBytes(val.data(), val.size());
}
// Private function supporting the other Read* functions.
bool ByteBufferReader::ReadBytes(uint8_t* val, size_t len) {
if (len > Length()) {
return false;
}
if (len == 0) {
return true;
}
memcpy(val, bytes_ + start_, len);
start_ += len;
return true;
}
bool ByteBufferReader::Consume(size_t size) {
if (size > Length())
return false;
start_ += size;
return true;
}
} // namespace rtc

View file

@ -0,0 +1,203 @@
/*
* Copyright 2004 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.
*/
#ifndef RTC_BASE_BYTE_BUFFER_H_
#define RTC_BASE_BYTE_BUFFER_H_
#include <stddef.h>
#include <stdint.h>
#include <string>
#include "absl/base/attributes.h"
#include "absl/strings/string_view.h"
#include "api/array_view.h"
#include "rtc_base/buffer.h"
#include "rtc_base/byte_order.h"
// Reads/Writes from/to buffer using network byte order (big endian)
namespace rtc {
template <class BufferClassT>
class ByteBufferWriterT {
using value_type = typename BufferClassT::value_type;
public:
ByteBufferWriterT() { Construct(nullptr, kDefaultCapacity); }
ByteBufferWriterT(const value_type* bytes, size_t len) {
Construct(bytes, len);
}
ByteBufferWriterT(const ByteBufferWriterT&) = delete;
ByteBufferWriterT& operator=(const ByteBufferWriterT&) = delete;
const value_type* Data() const { return buffer_.data(); }
size_t Length() const { return buffer_.size(); }
size_t Capacity() const { return buffer_.capacity(); }
rtc::ArrayView<const value_type> DataView() const {
return rtc::MakeArrayView(Data(), Length());
}
// Accessor that returns a string_view, independent of underlying type.
// Intended to provide access for existing users that expect char*
// when the underlying type changes to uint8_t.
// TODO(bugs.webrtc.org/15665): Delete when users are converted.
absl::string_view DataAsStringView() const {
return absl::string_view(reinterpret_cast<const char*>(Data()), Length());
}
const char* DataAsCharPointer() const {
return reinterpret_cast<const char*>(Data());
}
// Write value to the buffer. Resizes the buffer when it is
// neccessary.
void WriteUInt8(uint8_t val) {
WriteBytesInternal(reinterpret_cast<const value_type*>(&val), 1);
}
void WriteUInt16(uint16_t val) {
uint16_t v = HostToNetwork16(val);
WriteBytesInternal(reinterpret_cast<const value_type*>(&v), 2);
}
void WriteUInt24(uint32_t val) {
uint32_t v = HostToNetwork32(val);
value_type* start = reinterpret_cast<value_type*>(&v);
++start;
WriteBytesInternal(start, 3);
}
void WriteUInt32(uint32_t val) {
uint32_t v = HostToNetwork32(val);
WriteBytesInternal(reinterpret_cast<const value_type*>(&v), 4);
}
void WriteUInt64(uint64_t val) {
uint64_t v = HostToNetwork64(val);
WriteBytesInternal(reinterpret_cast<const value_type*>(&v), 8);
}
// Serializes an unsigned varint in the format described by
// https://developers.google.com/protocol-buffers/docs/encoding#varints
// with the caveat that integers are 64-bit, not 128-bit.
void WriteUVarint(uint64_t val) {
while (val >= 0x80) {
// Write 7 bits at a time, then set the msb to a continuation byte
// (msb=1).
value_type byte = static_cast<value_type>(val) | 0x80;
WriteBytesInternal(&byte, 1);
val >>= 7;
}
value_type last_byte = static_cast<value_type>(val);
WriteBytesInternal(&last_byte, 1);
}
void WriteString(absl::string_view val) {
WriteBytesInternal(reinterpret_cast<const value_type*>(val.data()),
val.size());
}
// Write an array of bytes (uint8_t)
void WriteBytes(const uint8_t* val, size_t len) {
WriteBytesInternal(reinterpret_cast<const value_type*>(val), len);
}
// Reserves the given number of bytes and returns a value_type* that can be
// written into. Useful for functions that require a value_type* buffer and
// not a ByteBufferWriter.
value_type* ReserveWriteBuffer(size_t len) {
buffer_.SetSize(buffer_.size() + len);
return buffer_.data();
}
// Resize the buffer to the specified `size`.
void Resize(size_t size) { buffer_.SetSize(size); }
// Clears the contents of the buffer. After this, Length() will be 0.
void Clear() { buffer_.Clear(); }
private:
static constexpr size_t kDefaultCapacity = 4096;
void Construct(const value_type* bytes, size_t size) {
if (bytes) {
buffer_.AppendData(bytes, size);
} else {
buffer_.EnsureCapacity(size);
}
}
void WriteBytesInternal(const value_type* val, size_t len) {
buffer_.AppendData(val, len);
}
BufferClassT buffer_;
// There are sensible ways to define these, but they aren't needed in our code
// base.
};
class ByteBufferWriter : public ByteBufferWriterT<BufferT<uint8_t>> {
public:
ByteBufferWriter();
ByteBufferWriter(const uint8_t* bytes, size_t len);
ByteBufferWriter(const ByteBufferWriter&) = delete;
ByteBufferWriter& operator=(const ByteBufferWriter&) = delete;
};
// The ByteBufferReader references the passed data, i.e. the pointer must be
// valid during the lifetime of the reader.
class ByteBufferReader {
public:
explicit ByteBufferReader(
rtc::ArrayView<const uint8_t> bytes ABSL_ATTRIBUTE_LIFETIME_BOUND);
explicit ByteBufferReader(const ByteBufferWriter& buf);
ByteBufferReader(const ByteBufferReader&) = delete;
ByteBufferReader& operator=(const ByteBufferReader&) = delete;
const uint8_t* Data() const { return bytes_ + start_; }
// Returns number of unprocessed bytes.
size_t Length() const { return end_ - start_; }
// Returns a view of the unprocessed data. Does not move current position.
rtc::ArrayView<const uint8_t> DataView() const {
return rtc::ArrayView<const uint8_t>(bytes_ + start_, end_ - start_);
}
// Read a next value from the buffer. Return false if there isn't
// enough data left for the specified type.
bool ReadUInt8(uint8_t* val);
bool ReadUInt16(uint16_t* val);
bool ReadUInt24(uint32_t* val);
bool ReadUInt32(uint32_t* val);
bool ReadUInt64(uint64_t* val);
bool ReadUVarint(uint64_t* val);
// Copies the val.size() next bytes into val.data().
bool ReadBytes(rtc::ArrayView<uint8_t> val);
// Appends next `len` bytes from the buffer to `val`. Returns false
// if there is less than `len` bytes left.
bool ReadString(std::string* val, size_t len);
// Same as `ReadString` except that the returned string_view will point into
// the internal buffer (no additional buffer allocation).
bool ReadStringView(absl::string_view* val, size_t len);
// Moves current position `size` bytes forward. Returns false if
// there is less than `size` bytes left in the buffer. Consume doesn't
// permanently remove data, so remembered read positions are still valid
// after this call.
bool Consume(size_t size);
private:
void Construct(const uint8_t* bytes, size_t size);
bool ReadBytes(uint8_t* val, size_t len);
const uint8_t* bytes_;
size_t size_;
size_t start_;
size_t end_;
};
} // namespace rtc
#endif // RTC_BASE_BYTE_BUFFER_H_

View file

@ -0,0 +1,212 @@
/*
* Copyright 2004 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.
*/
#ifndef RTC_BASE_BYTE_ORDER_H_
#define RTC_BASE_BYTE_ORDER_H_
#include <stdint.h>
#include <cstring>
#if defined(WEBRTC_POSIX) && !defined(__native_client__)
#include <arpa/inet.h>
#endif
#include "rtc_base/system/arch.h"
#if defined(WEBRTC_MAC)
#include <libkern/OSByteOrder.h>
#define htobe16(v) OSSwapHostToBigInt16(v)
#define htobe32(v) OSSwapHostToBigInt32(v)
#define htobe64(v) OSSwapHostToBigInt64(v)
#define be16toh(v) OSSwapBigToHostInt16(v)
#define be32toh(v) OSSwapBigToHostInt32(v)
#define be64toh(v) OSSwapBigToHostInt64(v)
#define htole16(v) OSSwapHostToLittleInt16(v)
#define htole32(v) OSSwapHostToLittleInt32(v)
#define htole64(v) OSSwapHostToLittleInt64(v)
#define le16toh(v) OSSwapLittleToHostInt16(v)
#define le32toh(v) OSSwapLittleToHostInt32(v)
#define le64toh(v) OSSwapLittleToHostInt64(v)
#elif defined(WEBRTC_WIN) || defined(__native_client__)
#if defined(WEBRTC_WIN)
#include <stdlib.h>
#include <winsock2.h>
#else
#include <netinet/in.h>
#endif // defined(WEBRTC_WIN)
#if defined(WEBRTC_ARCH_LITTLE_ENDIAN)
#define htobe16(v) htons(v)
#define htobe32(v) htonl(v)
#define be16toh(v) ntohs(v)
#define be32toh(v) ntohl(v)
#define htole16(v) (v)
#define htole32(v) (v)
#define htole64(v) (v)
#define le16toh(v) (v)
#define le32toh(v) (v)
#define le64toh(v) (v)
#if defined(WEBRTC_WIN)
#define htobe64(v) _byteswap_uint64(v)
#define be64toh(v) _byteswap_uint64(v)
#endif // defined(WEBRTC_WIN)
#if defined(__native_client__)
#define htobe64(v) __builtin_bswap64(v)
#define be64toh(v) __builtin_bswap64(v)
#endif // defined(__native_client__)
#elif defined(WEBRTC_ARCH_BIG_ENDIAN)
#define htobe16(v) (v)
#define htobe32(v) (v)
#define be16toh(v) (v)
#define be32toh(v) (v)
#define htole16(v) __builtin_bswap16(v)
#define htole32(v) __builtin_bswap32(v)
#define htole64(v) __builtin_bswap64(v)
#define le16toh(v) __builtin_bswap16(v)
#define le32toh(v) __builtin_bswap32(v)
#define le64toh(v) __builtin_bswap64(v)
#if defined(WEBRTC_WIN)
#define htobe64(v) (v)
#define be64toh(v) (v)
#endif // defined(WEBRTC_WIN)
#if defined(__native_client__)
#define htobe64(v) (v)
#define be64toh(v) (v)
#endif // defined(__native_client__)
#else
#error WEBRTC_ARCH_BIG_ENDIAN or WEBRTC_ARCH_LITTLE_ENDIAN must be defined.
#endif // defined(WEBRTC_ARCH_LITTLE_ENDIAN)
#elif defined(WEBRTC_POSIX)
#include <endian.h>
#else
#error "Missing byte order functions for this arch."
#endif // defined(WEBRTC_MAC)
namespace rtc {
// Reading and writing of little and big-endian numbers from memory
inline void Set8(void* memory, size_t offset, uint8_t v) {
static_cast<uint8_t*>(memory)[offset] = v;
}
inline uint8_t Get8(const void* memory, size_t offset) {
return static_cast<const uint8_t*>(memory)[offset];
}
inline void SetBE16(void* memory, uint16_t v) {
uint16_t val = htobe16(v);
memcpy(memory, &val, sizeof(val));
}
inline void SetBE32(void* memory, uint32_t v) {
uint32_t val = htobe32(v);
memcpy(memory, &val, sizeof(val));
}
inline void SetBE64(void* memory, uint64_t v) {
uint64_t val = htobe64(v);
memcpy(memory, &val, sizeof(val));
}
inline uint16_t GetBE16(const void* memory) {
uint16_t val;
memcpy(&val, memory, sizeof(val));
return be16toh(val);
}
inline uint32_t GetBE32(const void* memory) {
uint32_t val;
memcpy(&val, memory, sizeof(val));
return be32toh(val);
}
inline uint64_t GetBE64(const void* memory) {
uint64_t val;
memcpy(&val, memory, sizeof(val));
return be64toh(val);
}
inline void SetLE16(void* memory, uint16_t v) {
uint16_t val = htole16(v);
memcpy(memory, &val, sizeof(val));
}
inline void SetLE32(void* memory, uint32_t v) {
uint32_t val = htole32(v);
memcpy(memory, &val, sizeof(val));
}
inline void SetLE64(void* memory, uint64_t v) {
uint64_t val = htole64(v);
memcpy(memory, &val, sizeof(val));
}
inline uint16_t GetLE16(const void* memory) {
uint16_t val;
memcpy(&val, memory, sizeof(val));
return le16toh(val);
}
inline uint32_t GetLE32(const void* memory) {
uint32_t val;
memcpy(&val, memory, sizeof(val));
return le32toh(val);
}
inline uint64_t GetLE64(const void* memory) {
uint64_t val;
memcpy(&val, memory, sizeof(val));
return le64toh(val);
}
// Check if the current host is big endian.
inline bool IsHostBigEndian() {
#if defined(WEBRTC_ARCH_BIG_ENDIAN)
return true;
#else
return false;
#endif
}
inline uint16_t HostToNetwork16(uint16_t n) {
return htobe16(n);
}
inline uint32_t HostToNetwork32(uint32_t n) {
return htobe32(n);
}
inline uint64_t HostToNetwork64(uint64_t n) {
return htobe64(n);
}
inline uint16_t NetworkToHost16(uint16_t n) {
return be16toh(n);
}
inline uint32_t NetworkToHost32(uint32_t n) {
return be32toh(n);
}
inline uint64_t NetworkToHost64(uint64_t n) {
return be64toh(n);
}
} // namespace rtc
#endif // RTC_BASE_BYTE_ORDER_H_

View file

@ -0,0 +1,121 @@
/*
* Copyright 2020 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 "rtc_base/callback_list.h"
#include "rtc_base/checks.h"
namespace webrtc {
namespace callback_list_impl {
CallbackListReceivers::CallbackListReceivers() = default;
CallbackListReceivers::~CallbackListReceivers() {
RTC_CHECK(!send_in_progress_);
}
void CallbackListReceivers::RemoveReceivers(const void* removal_tag) {
RTC_DCHECK(removal_tag);
// We divide the receivers_ vector into three regions: from right to left, the
// "keep" region, the "todo" region, and the "remove" region. The "todo"
// region initially covers the whole vector.
size_t first_todo = 0; // First element of the "todo"
// region.
size_t first_remove = receivers_.size(); // First element of the "remove"
// region.
// Loop until the "todo" region is empty.
while (first_todo != first_remove) {
if (receivers_[first_todo].removal_tag != removal_tag) {
// The first element of the "todo" region should be kept. Move the
// "keep"/"todo" boundary.
++first_todo;
} else if (receivers_[first_remove - 1].removal_tag == removal_tag) {
// The last element of the "todo" region should be removed. Move the
// "todo"/"remove" boundary.
if (send_in_progress_) {
// Tag this receiver for removal, which will be done when `ForEach`
// has completed.
receivers_[first_remove - 1].removal_tag = pending_removal_tag();
}
--first_remove;
} else if (!send_in_progress_) {
// The first element of the "todo" region should be removed, and the last
// element of the "todo" region should be kept. Swap them, and then shrink
// the "todo" region from both ends.
RTC_DCHECK_NE(first_todo, first_remove - 1);
using std::swap;
swap(receivers_[first_todo], receivers_[first_remove - 1]);
RTC_DCHECK_NE(receivers_[first_todo].removal_tag, removal_tag);
++first_todo;
RTC_DCHECK_EQ(receivers_[first_remove - 1].removal_tag, removal_tag);
--first_remove;
}
}
if (!send_in_progress_) {
// Discard the remove region.
receivers_.resize(first_remove);
}
}
void CallbackListReceivers::Foreach(
rtc::FunctionView<void(UntypedFunction&)> fv) {
RTC_CHECK(!send_in_progress_);
bool removals_detected = false;
send_in_progress_ = true;
for (auto& r : receivers_) {
RTC_DCHECK_NE(r.removal_tag, pending_removal_tag());
fv(r.function);
if (r.removal_tag == pending_removal_tag()) {
removals_detected = true;
}
}
send_in_progress_ = false;
if (removals_detected) {
RemoveReceivers(pending_removal_tag());
}
}
template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::TrivialUntypedFunctionArgs<1>);
template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::TrivialUntypedFunctionArgs<2>);
template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::TrivialUntypedFunctionArgs<3>);
template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::TrivialUntypedFunctionArgs<4>);
template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::NontrivialUntypedFunctionArgs);
template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::FunctionPointerUntypedFunctionArgs);
template void CallbackListReceivers::AddReceiver(
UntypedFunction::TrivialUntypedFunctionArgs<1>);
template void CallbackListReceivers::AddReceiver(
UntypedFunction::TrivialUntypedFunctionArgs<2>);
template void CallbackListReceivers::AddReceiver(
UntypedFunction::TrivialUntypedFunctionArgs<3>);
template void CallbackListReceivers::AddReceiver(
UntypedFunction::TrivialUntypedFunctionArgs<4>);
template void CallbackListReceivers::AddReceiver(
UntypedFunction::NontrivialUntypedFunctionArgs);
template void CallbackListReceivers::AddReceiver(
UntypedFunction::FunctionPointerUntypedFunctionArgs);
} // namespace callback_list_impl
} // namespace webrtc

View file

@ -0,0 +1,223 @@
/*
* Copyright 2020 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.
*/
#ifndef RTC_BASE_CALLBACK_LIST_H_
#define RTC_BASE_CALLBACK_LIST_H_
#include <utility>
#include <vector>
#include "api/function_view.h"
#include "rtc_base/checks.h"
#include "rtc_base/system/assume.h"
#include "rtc_base/system/inline.h"
#include "rtc_base/system/rtc_export.h"
#include "rtc_base/untyped_function.h"
namespace webrtc {
namespace callback_list_impl {
class RTC_EXPORT CallbackListReceivers {
public:
CallbackListReceivers();
CallbackListReceivers(const CallbackListReceivers&) = delete;
CallbackListReceivers& operator=(const CallbackListReceivers&) = delete;
CallbackListReceivers(CallbackListReceivers&&) = delete;
CallbackListReceivers& operator=(CallbackListReceivers&&) = delete;
~CallbackListReceivers();
template <typename UntypedFunctionArgsT>
RTC_NO_INLINE void AddReceiver(const void* removal_tag,
UntypedFunctionArgsT args) {
RTC_CHECK(!send_in_progress_);
RTC_DCHECK(removal_tag != nullptr);
receivers_.push_back({removal_tag, UntypedFunction::Create(args)});
}
template <typename UntypedFunctionArgsT>
RTC_NO_INLINE void AddReceiver(UntypedFunctionArgsT args) {
RTC_CHECK(!send_in_progress_);
receivers_.push_back({nullptr, UntypedFunction::Create(args)});
}
void RemoveReceivers(const void* removal_tag);
void Foreach(rtc::FunctionView<void(UntypedFunction&)> fv);
private:
// Special protected pointer value that's used as a removal_tag for
// receivers that want to unsubscribe from within a callback.
// Note we could use `&receivers_` too, but since it's the first member
// variable of the class, its address will be the same as the instance
// CallbackList instance, so we take an extra step to avoid collision.
const void* pending_removal_tag() const { return &send_in_progress_; }
struct Callback {
const void* removal_tag;
UntypedFunction function;
};
std::vector<Callback> receivers_;
bool send_in_progress_ = false;
};
extern template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::TrivialUntypedFunctionArgs<1>);
extern template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::TrivialUntypedFunctionArgs<2>);
extern template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::TrivialUntypedFunctionArgs<3>);
extern template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::TrivialUntypedFunctionArgs<4>);
extern template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::NontrivialUntypedFunctionArgs);
extern template void CallbackListReceivers::AddReceiver(
const void*,
UntypedFunction::FunctionPointerUntypedFunctionArgs);
extern template void CallbackListReceivers::AddReceiver(
UntypedFunction::TrivialUntypedFunctionArgs<1>);
extern template void CallbackListReceivers::AddReceiver(
UntypedFunction::TrivialUntypedFunctionArgs<2>);
extern template void CallbackListReceivers::AddReceiver(
UntypedFunction::TrivialUntypedFunctionArgs<3>);
extern template void CallbackListReceivers::AddReceiver(
UntypedFunction::TrivialUntypedFunctionArgs<4>);
extern template void CallbackListReceivers::AddReceiver(
UntypedFunction::NontrivialUntypedFunctionArgs);
extern template void CallbackListReceivers::AddReceiver(
UntypedFunction::FunctionPointerUntypedFunctionArgs);
} // namespace callback_list_impl
// A collection of receivers (callable objects) that can be called all at once.
// Optimized for minimal binary size. The template arguments dictate what
// signature the callbacks must have; for example, a CallbackList<int, float>
// will require callbacks with signature void(int, float).
//
// CallbackList is neither copyable nor movable (could easily be made movable if
// necessary). Callbacks must be movable, but need not be copyable.
//
// Usage example:
//
// // Declaration (usually a member variable).
// CallbackList<int, float> foo_;
//
// // Register callbacks. This can be done zero or more times. The
// // callbacks must accept the arguments types listed in the CallbackList's
// // template argument list, and must return void.
// foo_.AddReceiver([...](int a, float b) {...}); // Lambda.
// foo_.AddReceiver(SomeFunction); // Function pointer.
//
// // Call the zero or more receivers, one after the other.
// foo_.Send(17, 3.14);
//
// Callback lifetime considerations
// --------------------------------
//
// CallbackList::AddReceiver() takes ownership of the given callback by moving
// it in place. The callback can be any callable object; in particular, it may
// have a nontrivial destructor, which will be run when the CallbackList is
// destroyed. The callback may thus access data via any type of smart pointer,
// expressing e.g. unique, shared, or weak ownership. Of course, if the data is
// guaranteed to outlive the callback, a plain raw pointer can be used.
//
// Take care when trying to have the callback own reference-counted data. The
// CallbackList will keep the callback alive, and the callback will keep its
// data alive, so as usual with reference-counted ownership, keep an eye out for
// cycles!
//
// Thread safety
// -------------
//
// Like most C++ types, CallbackList is thread compatible: it's not safe to
// access it concurrently from multiple threads, but it can be made safe if it
// is protected by a mutex, for example.
//
// Excercise some care when deciding what mutexes to hold when you call
// CallbackList::Send(). In particular, do not hold mutexes that callbacks may
// need to grab. If a larger object has a CallbackList member and a single mutex
// that protects all of its data members, this may e.g. make it necessary to
// protect its CallbackList with a separate mutex; otherwise, there will be a
// deadlock if the callbacks try to access the object.
//
// CallbackList as a class data member
// -----------------------------------
//
// CallbackList is a normal C++ data type, and should be private when it is a
// data member of a class. For thread safety reasons (see above), it is likely
// best to not have an accessor for the entire CallbackList, and instead only
// allow callers to add callbacks:
//
// template <typename F>
// void AddFooCallback(F&& callback) {
// // Maybe grab a mutex here?
// foo_callbacks_.AddReceiver(std::forward<F>(callback));
// }
//
template <typename... ArgT>
class CallbackList {
public:
CallbackList() = default;
CallbackList(const CallbackList&) = delete;
CallbackList& operator=(const CallbackList&) = delete;
CallbackList(CallbackList&&) = delete;
CallbackList& operator=(CallbackList&&) = delete;
// Adds a new receiver. The receiver (a callable object or a function pointer)
// must be movable, but need not be copyable. Its call signature should be
// `void(ArgT...)`. The removal tag is a pointer to an arbitrary object that
// you own, and that will stay alive until the CallbackList is gone, or until
// all receivers using it as a removal tag have been removed; you can use it
// to remove the receiver.
template <typename F>
void AddReceiver(const void* removal_tag, F&& f) {
receivers_.AddReceiver(
removal_tag,
UntypedFunction::PrepareArgs<void(ArgT...)>(std::forward<F>(f)));
}
// Adds a new receiver with no removal tag.
template <typename F>
void AddReceiver(F&& f) {
receivers_.AddReceiver(
UntypedFunction::PrepareArgs<void(ArgT...)>(std::forward<F>(f)));
}
// Removes all receivers that were added with the given removal tag.
void RemoveReceivers(const void* removal_tag) {
receivers_.RemoveReceivers(removal_tag);
}
// Calls all receivers with the given arguments. While the Send is in
// progress, no method calls are allowed; specifically, this means that the
// callbacks may not do anything with this CallbackList instance.
//
// Note: Receivers are called serially, but not necessarily in the same order
// they were added.
template <typename... ArgU>
void Send(ArgU&&... args) {
receivers_.Foreach([&](UntypedFunction& f) {
f.Call<void(ArgT...)>(std::forward<ArgU>(args)...);
});
}
private:
callback_list_impl::CallbackListReceivers receivers_;
};
} // namespace webrtc
#endif // RTC_BASE_CALLBACK_LIST_H_

View file

@ -0,0 +1,240 @@
/*
* Copyright 2006 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.
*/
// Most of this was borrowed (with minor modifications) from V8's and Chromium's
// src/base/logging.cc.
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include "absl/strings/string_view.h"
#if defined(WEBRTC_ANDROID)
#define RTC_LOG_TAG_ANDROID "rtc"
#include <android/log.h> // NOLINT
#endif
#if defined(WEBRTC_WIN)
#include <windows.h>
#endif
#if defined(WEBRTC_WIN)
#define LAST_SYSTEM_ERROR (::GetLastError())
#elif defined(__native_client__) && __native_client__
#define LAST_SYSTEM_ERROR (0)
#elif defined(WEBRTC_POSIX)
#include <errno.h>
#define LAST_SYSTEM_ERROR (errno)
#endif // WEBRTC_WIN
#include "rtc_base/checks.h"
namespace {
#if defined(__GNUC__)
__attribute__((__format__(__printf__, 2, 3)))
#endif
void AppendFormat(std::string* s, const char* fmt, ...) {
va_list args, copy;
va_start(args, fmt);
va_copy(copy, args);
const int predicted_length = std::vsnprintf(nullptr, 0, fmt, copy);
va_end(copy);
if (predicted_length > 0) {
const size_t size = s->size();
s->resize(size + predicted_length);
// Pass "+ 1" to vsnprintf to include space for the '\0'.
std::vsnprintf(&((*s)[size]), predicted_length + 1, fmt, args);
}
va_end(args);
}
} // namespace
namespace rtc {
namespace webrtc_checks_impl {
#if !defined(WEBRTC_CHROMIUM_BUILD)
RTC_NORETURN void WriteFatalLog(absl::string_view output) {
#if defined(WEBRTC_ANDROID)
std::string output_str(output);
__android_log_print(ANDROID_LOG_ERROR, RTC_LOG_TAG_ANDROID, "%s\n",
output_str.c_str());
#endif
fflush(stdout);
fwrite(output.data(), output.size(), 1, stderr);
fflush(stderr);
#if defined(WEBRTC_WIN)
DebugBreak();
#endif
abort();
}
RTC_NORETURN void WriteFatalLog(const char* file,
int line,
absl::string_view output) {
WriteFatalLog(output);
}
#endif // !defined(WEBRTC_CHROMIUM_BUILD)
#if RTC_CHECK_MSG_ENABLED
// Reads one argument from args, appends it to s and advances fmt.
// Returns true iff an argument was sucessfully parsed.
bool ParseArg(va_list* args, const CheckArgType** fmt, std::string* s) {
if (**fmt == CheckArgType::kEnd)
return false;
switch (**fmt) {
case CheckArgType::kInt:
AppendFormat(s, "%d", va_arg(*args, int));
break;
case CheckArgType::kLong:
AppendFormat(s, "%ld", va_arg(*args, long));
break;
case CheckArgType::kLongLong:
AppendFormat(s, "%lld", va_arg(*args, long long));
break;
case CheckArgType::kUInt:
AppendFormat(s, "%u", va_arg(*args, unsigned));
break;
case CheckArgType::kULong:
AppendFormat(s, "%lu", va_arg(*args, unsigned long));
break;
case CheckArgType::kULongLong:
AppendFormat(s, "%llu", va_arg(*args, unsigned long long));
break;
case CheckArgType::kDouble:
AppendFormat(s, "%g", va_arg(*args, double));
break;
case CheckArgType::kLongDouble:
AppendFormat(s, "%Lg", va_arg(*args, long double));
break;
case CheckArgType::kCharP:
s->append(va_arg(*args, const char*));
break;
case CheckArgType::kStdString:
s->append(*va_arg(*args, const std::string*));
break;
case CheckArgType::kStringView: {
const absl::string_view sv = *va_arg(*args, const absl::string_view*);
s->append(sv.data(), sv.size());
break;
}
case CheckArgType::kVoidP:
AppendFormat(s, "%p", va_arg(*args, const void*));
break;
default:
s->append("[Invalid CheckArgType]");
return false;
}
(*fmt)++;
return true;
}
RTC_NORETURN void FatalLog(const char* file,
int line,
const char* message,
const CheckArgType* fmt,
...) {
va_list args;
va_start(args, fmt);
std::string s;
AppendFormat(&s,
"\n\n"
"#\n"
"# Fatal error in: %s, line %d\n"
"# last system error: %u\n"
"# Check failed: %s",
file, line, LAST_SYSTEM_ERROR, message);
if (*fmt == CheckArgType::kCheckOp) {
// This log message was generated by RTC_CHECK_OP, so we have to complete
// the error message using the operands that have been passed as the first
// two arguments.
fmt++;
std::string s1, s2;
if (ParseArg(&args, &fmt, &s1) && ParseArg(&args, &fmt, &s2))
AppendFormat(&s, " (%s vs. %s)\n# ", s1.c_str(), s2.c_str());
} else {
s.append("\n# ");
}
// Append all the user-supplied arguments to the message.
while (ParseArg(&args, &fmt, &s))
;
va_end(args);
WriteFatalLog(file, line, s);
}
#else // RTC_CHECK_MSG_ENABLED
RTC_NORETURN void FatalLog(const char* file, int line) {
std::string s;
AppendFormat(&s,
"\n\n"
"#\n"
"# Fatal error in: %s, line %d\n"
"# last system error: %u\n"
"# Check failed.\n"
"# ",
file, line, LAST_SYSTEM_ERROR);
WriteFatalLog(file, line, s);
}
#endif // RTC_CHECK_MSG_ENABLED
#if RTC_DCHECK_IS_ON
RTC_NORETURN void UnreachableCodeReached(const char* file, int line) {
std::string s;
AppendFormat(&s,
"\n\n"
"#\n"
"# Unreachable code reached: %s, line %d\n"
"# last system error: %u\n"
"# ",
file, line, LAST_SYSTEM_ERROR);
WriteFatalLog(file, line, s);
}
#else // !RTC_DCHECK_IS_ON
RTC_NORETURN void UnreachableCodeReached() {
std::string s;
AppendFormat(&s,
"\n\n"
"#\n"
"# Unreachable code reached (file and line unknown)\n"
"# last system error: %u\n"
"# ",
LAST_SYSTEM_ERROR);
WriteFatalLog(s);
}
#endif // !RTC_DCHECK_IS_ON
} // namespace webrtc_checks_impl
} // namespace rtc
// Function to call from the C version of the RTC_CHECK and RTC_DCHECK macros.
RTC_NORETURN void rtc_FatalMessage(const char* file,
int line,
const char* msg) {
#if RTC_CHECK_MSG_ENABLED
static constexpr rtc::webrtc_checks_impl::CheckArgType t[] = {
rtc::webrtc_checks_impl::CheckArgType::kEnd};
rtc::webrtc_checks_impl::FatalLog(file, line, msg, t);
#else
rtc::webrtc_checks_impl::FatalLog(file, line);
#endif
}

View file

@ -0,0 +1,520 @@
/*
* Copyright 2006 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.
*/
#ifndef RTC_BASE_CHECKS_H_
#define RTC_BASE_CHECKS_H_
// If you for some reson need to know if DCHECKs are on, test the value of
// RTC_DCHECK_IS_ON. (Test its value, not if it's defined; it'll always be
// defined, to either a true or a false value.)
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
#define RTC_DCHECK_IS_ON 1
#else
#define RTC_DCHECK_IS_ON 0
#endif
// Annotate a function that will not return control flow to the caller.
#if defined(_MSC_VER)
#define RTC_NORETURN __declspec(noreturn)
#elif defined(__GNUC__)
#define RTC_NORETURN __attribute__((__noreturn__))
#else
#define RTC_NORETURN
#endif
#ifdef __cplusplus
extern "C" {
#endif
RTC_NORETURN void rtc_FatalMessage(const char* file, int line, const char* msg);
#ifdef __cplusplus
} // extern "C"
#endif
#ifdef RTC_DISABLE_CHECK_MSG
#define RTC_CHECK_MSG_ENABLED 0
#else
#define RTC_CHECK_MSG_ENABLED 1
#endif
#if RTC_CHECK_MSG_ENABLED
#define RTC_CHECK_EVAL_MESSAGE(message) message
#else
#define RTC_CHECK_EVAL_MESSAGE(message) ""
#endif
#ifdef __cplusplus
// C++ version.
#include <string>
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
#include "api/scoped_refptr.h"
#include "rtc_base/numerics/safe_compare.h"
#include "rtc_base/system/inline.h"
#include "rtc_base/system/rtc_export.h"
// The macros here print a message to stderr and abort under various
// conditions. All will accept additional stream messages. For example:
// RTC_DCHECK_EQ(foo, bar) << "I'm printed when foo != bar.";
//
// - RTC_CHECK(x) is an assertion that x is always true, and that if it isn't,
// it's better to terminate the process than to continue. During development,
// the reason that it's better to terminate might simply be that the error
// handling code isn't in place yet; in production, the reason might be that
// the author of the code truly believes that x will always be true, but that
// they recognizes that if they are wrong, abrupt and unpleasant process
// termination is still better than carrying on with the assumption violated.
//
// RTC_CHECK always evaluates its argument, so it's OK for x to have side
// effects.
//
// - RTC_DCHECK(x) is the same as RTC_CHECK(x)---an assertion that x is always
// true---except that x will only be evaluated in debug builds; in production
// builds, x is simply assumed to be true. This is useful if evaluating x is
// expensive and the expected cost of failing to detect the violated
// assumption is acceptable. You should not handle cases where a production
// build fails to spot a violated condition, even those that would result in
// crashes. If the code needs to cope with the error, make it cope, but don't
// call RTC_DCHECK; if the condition really can't occur, but you'd sleep
// better at night knowing that the process will suicide instead of carrying
// on in case you were wrong, use RTC_CHECK instead of RTC_DCHECK.
//
// RTC_DCHECK only evaluates its argument in debug builds, so if x has visible
// side effects, you need to write e.g.
// bool w = x; RTC_DCHECK(w);
//
// - RTC_CHECK_EQ, _NE, _GT, ..., and RTC_DCHECK_EQ, _NE, _GT, ... are
// specialized variants of RTC_CHECK and RTC_DCHECK that print prettier
// messages if the condition doesn't hold. Prefer them to raw RTC_CHECK and
// RTC_DCHECK.
//
// - RTC_FATAL() aborts unconditionally.
namespace rtc {
namespace webrtc_checks_impl {
enum class CheckArgType : int8_t {
kEnd = 0,
kInt,
kLong,
kLongLong,
kUInt,
kULong,
kULongLong,
kDouble,
kLongDouble,
kCharP,
kStdString,
kStringView,
kVoidP,
// kCheckOp doesn't represent an argument type. Instead, it is sent as the
// first argument from RTC_CHECK_OP to make FatalLog use the next two
// arguments to build the special CHECK_OP error message
// (the "a == b (1 vs. 2)" bit).
kCheckOp,
};
// These two functions are public so they can be overridden from
// webrtc_overrides in chromium.
RTC_NORETURN void WriteFatalLog(const char* file,
int line,
absl::string_view output);
RTC_NORETURN void WriteFatalLog(absl::string_view output);
#if RTC_CHECK_MSG_ENABLED
RTC_NORETURN RTC_EXPORT void FatalLog(const char* file,
int line,
const char* message,
const CheckArgType* fmt,
...);
#else
RTC_NORETURN RTC_EXPORT void FatalLog(const char* file, int line);
#endif
// Wrapper for log arguments. Only ever make values of this type with the
// MakeVal() functions.
template <CheckArgType N, typename T>
struct Val {
static constexpr CheckArgType Type() { return N; }
T GetVal() const { return val; }
T val;
};
// Case for when we need to construct a temp string and then print that.
// (We can't use Val<CheckArgType::kStdString, const std::string*>
// because we need somewhere to store the temp string.)
struct ToStringVal {
static constexpr CheckArgType Type() { return CheckArgType::kStdString; }
const std::string* GetVal() const { return &val; }
std::string val;
};
inline Val<CheckArgType::kInt, int> MakeVal(int x) {
return {x};
}
inline Val<CheckArgType::kLong, long> MakeVal(long x) {
return {x};
}
inline Val<CheckArgType::kLongLong, long long> MakeVal(long long x) {
return {x};
}
inline Val<CheckArgType::kUInt, unsigned int> MakeVal(unsigned int x) {
return {x};
}
inline Val<CheckArgType::kULong, unsigned long> MakeVal(unsigned long x) {
return {x};
}
inline Val<CheckArgType::kULongLong, unsigned long long> MakeVal(
unsigned long long x) {
return {x};
}
inline Val<CheckArgType::kDouble, double> MakeVal(double x) {
return {x};
}
inline Val<CheckArgType::kLongDouble, long double> MakeVal(long double x) {
return {x};
}
inline Val<CheckArgType::kCharP, const char*> MakeVal(const char* x) {
return {x};
}
inline Val<CheckArgType::kStdString, const std::string*> MakeVal(
const std::string& x) {
return {&x};
}
inline Val<CheckArgType::kStringView, const absl::string_view*> MakeVal(
const absl::string_view& x) {
return {&x};
}
inline Val<CheckArgType::kVoidP, const void*> MakeVal(const void* x) {
return {x};
}
template <typename T>
inline Val<CheckArgType::kVoidP, const void*> MakeVal(
const rtc::scoped_refptr<T>& p) {
return {p.get()};
}
// The enum class types are not implicitly convertible to arithmetic types.
template <typename T,
absl::enable_if_t<std::is_enum<T>::value &&
!std::is_arithmetic<T>::value>* = nullptr>
inline decltype(MakeVal(std::declval<absl::underlying_type_t<T>>())) MakeVal(
T x) {
return {static_cast<absl::underlying_type_t<T>>(x)};
}
template <typename T, decltype(ToLogString(std::declval<T>()))* = nullptr>
ToStringVal MakeVal(const T& x) {
return {ToLogString(x)};
}
// Ephemeral type that represents the result of the logging << operator.
template <typename... Ts>
class LogStreamer;
// Base case: Before the first << argument.
template <>
class LogStreamer<> final {
public:
template <typename U,
typename V = decltype(MakeVal(std::declval<U>())),
absl::enable_if_t<std::is_arithmetic<U>::value ||
std::is_enum<U>::value>* = nullptr>
RTC_FORCE_INLINE LogStreamer<V> operator<<(U arg) const {
return LogStreamer<V>(MakeVal(arg), this);
}
template <typename U,
typename V = decltype(MakeVal(std::declval<U>())),
absl::enable_if_t<!std::is_arithmetic<U>::value &&
!std::is_enum<U>::value>* = nullptr>
RTC_FORCE_INLINE LogStreamer<V> operator<<(const U& arg) const {
return LogStreamer<V>(MakeVal(arg), this);
}
#if RTC_CHECK_MSG_ENABLED
template <typename... Us>
RTC_NORETURN RTC_FORCE_INLINE static void Call(const char* file,
const int line,
const char* message,
const Us&... args) {
static constexpr CheckArgType t[] = {Us::Type()..., CheckArgType::kEnd};
FatalLog(file, line, message, t, args.GetVal()...);
}
template <typename... Us>
RTC_NORETURN RTC_FORCE_INLINE static void CallCheckOp(const char* file,
const int line,
const char* message,
const Us&... args) {
static constexpr CheckArgType t[] = {CheckArgType::kCheckOp, Us::Type()...,
CheckArgType::kEnd};
FatalLog(file, line, message, t, args.GetVal()...);
}
#else
template <typename... Us>
RTC_NORETURN RTC_FORCE_INLINE static void Call(const char* file,
const int line) {
FatalLog(file, line);
}
#endif
};
// Inductive case: We've already seen at least one << argument. The most recent
// one had type `T`, and the earlier ones had types `Ts`.
template <typename T, typename... Ts>
class LogStreamer<T, Ts...> final {
public:
RTC_FORCE_INLINE LogStreamer(T arg, const LogStreamer<Ts...>* prior)
: arg_(arg), prior_(prior) {}
template <typename U,
typename V = decltype(MakeVal(std::declval<U>())),
absl::enable_if_t<std::is_arithmetic<U>::value ||
std::is_enum<U>::value>* = nullptr>
RTC_FORCE_INLINE LogStreamer<V, T, Ts...> operator<<(U arg) const {
return LogStreamer<V, T, Ts...>(MakeVal(arg), this);
}
template <typename U,
typename V = decltype(MakeVal(std::declval<U>())),
absl::enable_if_t<!std::is_arithmetic<U>::value &&
!std::is_enum<U>::value>* = nullptr>
RTC_FORCE_INLINE LogStreamer<V, T, Ts...> operator<<(const U& arg) const {
return LogStreamer<V, T, Ts...>(MakeVal(arg), this);
}
#if RTC_CHECK_MSG_ENABLED
template <typename... Us>
RTC_NORETURN RTC_FORCE_INLINE void Call(const char* file,
const int line,
const char* message,
const Us&... args) const {
prior_->Call(file, line, message, arg_, args...);
}
template <typename... Us>
RTC_NORETURN RTC_FORCE_INLINE void CallCheckOp(const char* file,
const int line,
const char* message,
const Us&... args) const {
prior_->CallCheckOp(file, line, message, arg_, args...);
}
#else
template <typename... Us>
RTC_NORETURN RTC_FORCE_INLINE void Call(const char* file,
const int line) const {
prior_->Call(file, line);
}
#endif
private:
// The most recent argument.
T arg_;
// Earlier arguments.
const LogStreamer<Ts...>* prior_;
};
template <bool isCheckOp>
class FatalLogCall final {
public:
FatalLogCall(const char* file, int line, const char* message)
: file_(file), line_(line), message_(message) {}
// This can be any binary operator with precedence lower than <<.
template <typename... Ts>
RTC_NORETURN RTC_FORCE_INLINE void operator&(
const LogStreamer<Ts...>& streamer) {
#if RTC_CHECK_MSG_ENABLED
isCheckOp ? streamer.CallCheckOp(file_, line_, message_)
: streamer.Call(file_, line_, message_);
#else
streamer.Call(file_, line_);
#endif
}
private:
const char* file_;
int line_;
const char* message_;
};
#if RTC_DCHECK_IS_ON
// Be helpful, and include file and line in the RTC_CHECK_NOTREACHED error
// message.
#define RTC_UNREACHABLE_FILE_AND_LINE_CALL_ARGS __FILE__, __LINE__
RTC_NORETURN RTC_EXPORT void UnreachableCodeReached(const char* file, int line);
#else
// Be mindful of binary size, and don't include file and line in the
// RTC_CHECK_NOTREACHED error message.
#define RTC_UNREACHABLE_FILE_AND_LINE_CALL_ARGS
RTC_NORETURN RTC_EXPORT void UnreachableCodeReached();
#endif
} // namespace webrtc_checks_impl
// The actual stream used isn't important. We reference `ignored` in the code
// but don't evaluate it; this is to avoid "unused variable" warnings (we do so
// in a particularly convoluted way with an extra ?: because that appears to be
// the simplest construct that keeps Visual Studio from complaining about
// condition being unused).
#define RTC_EAT_STREAM_PARAMETERS(ignored) \
(true ? true : ((void)(ignored), true)) \
? static_cast<void>(0) \
: ::rtc::webrtc_checks_impl::FatalLogCall<false>("", 0, "") & \
::rtc::webrtc_checks_impl::LogStreamer<>()
// Call RTC_EAT_STREAM_PARAMETERS with an argument that fails to compile if
// values of the same types as `a` and `b` can't be compared with the given
// operation, and that would evaluate `a` and `b` if evaluated.
#define RTC_EAT_STREAM_PARAMETERS_OP(op, a, b) \
RTC_EAT_STREAM_PARAMETERS(((void)::rtc::Safe##op(a, b)))
// RTC_CHECK dies with a fatal error if condition is not true. It is *not*
// controlled by NDEBUG or anything else, so the check will be executed
// regardless of compilation mode.
//
// We make sure RTC_CHECK et al. always evaluates `condition`, as
// doing RTC_CHECK(FunctionWithSideEffect()) is a common idiom.
//
// RTC_CHECK_OP is a helper macro for binary operators.
// Don't use this macro directly in your code, use RTC_CHECK_EQ et al below.
#if RTC_CHECK_MSG_ENABLED
#define RTC_CHECK(condition) \
(condition) ? static_cast<void>(0) \
: ::rtc::webrtc_checks_impl::FatalLogCall<false>( \
__FILE__, __LINE__, #condition) & \
::rtc::webrtc_checks_impl::LogStreamer<>()
#define RTC_CHECK_OP(name, op, val1, val2) \
::rtc::Safe##name((val1), (val2)) \
? static_cast<void>(0) \
: ::rtc::webrtc_checks_impl::FatalLogCall<true>( \
__FILE__, __LINE__, #val1 " " #op " " #val2) & \
::rtc::webrtc_checks_impl::LogStreamer<>() << (val1) << (val2)
#else
#define RTC_CHECK(condition) \
(condition) ? static_cast<void>(0) \
: true ? ::rtc::webrtc_checks_impl::FatalLogCall<false>(__FILE__, __LINE__, \
"") & \
::rtc::webrtc_checks_impl::LogStreamer<>() \
: ::rtc::webrtc_checks_impl::FatalLogCall<false>("", 0, "") & \
::rtc::webrtc_checks_impl::LogStreamer<>()
#define RTC_CHECK_OP(name, op, val1, val2) \
::rtc::Safe##name((val1), (val2)) ? static_cast<void>(0) \
: true ? ::rtc::webrtc_checks_impl::FatalLogCall<true>(__FILE__, __LINE__, \
"") & \
::rtc::webrtc_checks_impl::LogStreamer<>() \
: ::rtc::webrtc_checks_impl::FatalLogCall<false>("", 0, "") & \
::rtc::webrtc_checks_impl::LogStreamer<>()
#endif
#define RTC_CHECK_EQ(val1, val2) RTC_CHECK_OP(Eq, ==, val1, val2)
#define RTC_CHECK_NE(val1, val2) RTC_CHECK_OP(Ne, !=, val1, val2)
#define RTC_CHECK_LE(val1, val2) RTC_CHECK_OP(Le, <=, val1, val2)
#define RTC_CHECK_LT(val1, val2) RTC_CHECK_OP(Lt, <, val1, val2)
#define RTC_CHECK_GE(val1, val2) RTC_CHECK_OP(Ge, >=, val1, val2)
#define RTC_CHECK_GT(val1, val2) RTC_CHECK_OP(Gt, >, val1, val2)
// The RTC_DCHECK macro is equivalent to RTC_CHECK except that it only generates
// code in debug builds. It does reference the condition parameter in all cases,
// though, so callers won't risk getting warnings about unused variables.
#if RTC_DCHECK_IS_ON
#define RTC_DCHECK(condition) RTC_CHECK(condition)
#define RTC_DCHECK_EQ(v1, v2) RTC_CHECK_EQ(v1, v2)
#define RTC_DCHECK_NE(v1, v2) RTC_CHECK_NE(v1, v2)
#define RTC_DCHECK_LE(v1, v2) RTC_CHECK_LE(v1, v2)
#define RTC_DCHECK_LT(v1, v2) RTC_CHECK_LT(v1, v2)
#define RTC_DCHECK_GE(v1, v2) RTC_CHECK_GE(v1, v2)
#define RTC_DCHECK_GT(v1, v2) RTC_CHECK_GT(v1, v2)
#else
#define RTC_DCHECK(condition) RTC_EAT_STREAM_PARAMETERS(condition)
#define RTC_DCHECK_EQ(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Eq, v1, v2)
#define RTC_DCHECK_NE(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Ne, v1, v2)
#define RTC_DCHECK_LE(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Le, v1, v2)
#define RTC_DCHECK_LT(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Lt, v1, v2)
#define RTC_DCHECK_GE(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Ge, v1, v2)
#define RTC_DCHECK_GT(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Gt, v1, v2)
#endif
#define RTC_UNREACHABLE_CODE_HIT false
#define RTC_DCHECK_NOTREACHED() RTC_DCHECK(RTC_UNREACHABLE_CODE_HIT)
// Kills the process with an error message. Never returns. Use when you wish to
// assert that a point in the code is never reached.
#define RTC_CHECK_NOTREACHED() \
do { \
::rtc::webrtc_checks_impl::UnreachableCodeReached( \
RTC_UNREACHABLE_FILE_AND_LINE_CALL_ARGS); \
} while (0)
#define RTC_FATAL() \
::rtc::webrtc_checks_impl::FatalLogCall<false>(__FILE__, __LINE__, \
"FATAL()") & \
::rtc::webrtc_checks_impl::LogStreamer<>()
// Performs the integer division a/b and returns the result. CHECKs that the
// remainder is zero.
template <typename T>
inline T CheckedDivExact(T a, T b) {
RTC_CHECK_EQ(a % b, 0) << a << " is not evenly divisible by " << b;
return a / b;
}
} // namespace rtc
#else // __cplusplus not defined
// C version. Lacks many features compared to the C++ version, but usage
// guidelines are the same.
#define RTC_CHECK(condition) \
do { \
if (!(condition)) { \
rtc_FatalMessage(__FILE__, __LINE__, \
RTC_CHECK_EVAL_MESSAGE("CHECK failed: " #condition)); \
} \
} while (0)
#define RTC_CHECK_EQ(a, b) RTC_CHECK((a) == (b))
#define RTC_CHECK_NE(a, b) RTC_CHECK((a) != (b))
#define RTC_CHECK_LE(a, b) RTC_CHECK((a) <= (b))
#define RTC_CHECK_LT(a, b) RTC_CHECK((a) < (b))
#define RTC_CHECK_GE(a, b) RTC_CHECK((a) >= (b))
#define RTC_CHECK_GT(a, b) RTC_CHECK((a) > (b))
#define RTC_DCHECK(condition) \
do { \
if (RTC_DCHECK_IS_ON && !(condition)) { \
rtc_FatalMessage(__FILE__, __LINE__, \
RTC_CHECK_EVAL_MESSAGE("DCHECK failed: " #condition)); \
} \
} while (0)
#define RTC_DCHECK_EQ(a, b) RTC_DCHECK((a) == (b))
#define RTC_DCHECK_NE(a, b) RTC_DCHECK((a) != (b))
#define RTC_DCHECK_LE(a, b) RTC_DCHECK((a) <= (b))
#define RTC_DCHECK_LT(a, b) RTC_DCHECK((a) < (b))
#define RTC_DCHECK_GE(a, b) RTC_DCHECK((a) >= (b))
#define RTC_DCHECK_GT(a, b) RTC_DCHECK((a) > (b))
#endif // __cplusplus
#endif // RTC_BASE_CHECKS_H_

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2012 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.
*/
#ifndef RTC_BASE_COMPILE_ASSERT_C_H_
#define RTC_BASE_COMPILE_ASSERT_C_H_
// Use this macro to verify at compile time that certain restrictions are met.
// The argument is the boolean expression to evaluate.
// Example:
// RTC_COMPILE_ASSERT(sizeof(foo) < 128);
// Note: In C++, use static_assert instead!
#define RTC_COMPILE_ASSERT(expression) \
switch (0) { \
case 0: \
case expression:; \
}
#endif // RTC_BASE_COMPILE_ASSERT_C_H_

View file

@ -0,0 +1,374 @@
/*
* Copyright (c) 2021 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.
*/
// This implementation is borrowed from Chromium.
#ifndef RTC_BASE_CONTAINERS_FLAT_MAP_H_
#define RTC_BASE_CONTAINERS_FLAT_MAP_H_
#include <functional>
#include <tuple>
#include <utility>
#include <vector>
#include "rtc_base/checks.h"
#include "rtc_base/containers/flat_tree.h" // IWYU pragma: export
namespace webrtc {
namespace flat_containers_internal {
// An implementation of the flat_tree GetKeyFromValue template parameter that
// extracts the key as the first element of a pair.
struct GetFirst {
template <class Key, class Mapped>
constexpr const Key& operator()(const std::pair<Key, Mapped>& p) const {
return p.first;
}
};
} // namespace flat_containers_internal
// flat_map is a container with a std::map-like interface that stores its
// contents in a sorted container, by default a vector.
//
// Its implementation mostly tracks the corresponding standardization proposal
// https://wg21.link/P0429, except that the storage of keys and values is not
// split.
//
// PROS
//
// - Good memory locality.
// - Low overhead, especially for smaller maps.
// - Performance is good for more workloads than you might expect (see
// //base/containers/README.md in Chromium repository)
// - Supports C++14 map interface.
//
// CONS
//
// - Inserts and removals are O(n).
//
// IMPORTANT NOTES
//
// - Iterators are invalidated across mutations. This means that the following
// line of code has undefined behavior since adding a new element could
// resize the container, invalidating all iterators:
// container["new element"] = it.second;
// - If possible, construct a flat_map in one operation by inserting into
// a container and moving that container into the flat_map constructor.
//
// QUICK REFERENCE
//
// Most of the core functionality is inherited from flat_tree. Please see
// flat_tree.h for more details for most of these functions. As a quick
// reference, the functions available are:
//
// Constructors (inputs need not be sorted):
// flat_map(const flat_map&);
// flat_map(flat_map&&);
// flat_map(InputIterator first, InputIterator last,
// const Compare& compare = Compare());
// flat_map(const container_type& items,
// const Compare& compare = Compare());
// flat_map(container_type&& items,
// const Compare& compare = Compare()); // Re-use storage.
// flat_map(std::initializer_list<value_type> ilist,
// const Compare& comp = Compare());
//
// Constructors (inputs need to be sorted):
// flat_map(sorted_unique_t,
// InputIterator first, InputIterator last,
// const Compare& compare = Compare());
// flat_map(sorted_unique_t,
// const container_type& items,
// const Compare& compare = Compare());
// flat_map(sorted_unique_t,
// container_type&& items,
// const Compare& compare = Compare()); // Re-use storage.
// flat_map(sorted_unique_t,
// std::initializer_list<value_type> ilist,
// const Compare& comp = Compare());
//
// Assignment functions:
// flat_map& operator=(const flat_map&);
// flat_map& operator=(flat_map&&);
// flat_map& operator=(initializer_list<value_type>);
//
// Memory management functions:
// void reserve(size_t);
// size_t capacity() const;
// void shrink_to_fit();
//
// Size management functions:
// void clear();
// size_t size() const;
// size_t max_size() const;
// bool empty() const;
//
// Iterator functions:
// iterator begin();
// const_iterator begin() const;
// const_iterator cbegin() const;
// iterator end();
// const_iterator end() const;
// const_iterator cend() const;
// reverse_iterator rbegin();
// const reverse_iterator rbegin() const;
// const_reverse_iterator crbegin() const;
// reverse_iterator rend();
// const_reverse_iterator rend() const;
// const_reverse_iterator crend() const;
//
// Insert and accessor functions:
// mapped_type& operator[](const key_type&);
// mapped_type& operator[](key_type&&);
// mapped_type& at(const K&);
// const mapped_type& at(const K&) const;
// pair<iterator, bool> insert(const value_type&);
// pair<iterator, bool> insert(value_type&&);
// iterator insert(const_iterator hint, const value_type&);
// iterator insert(const_iterator hint, value_type&&);
// void insert(InputIterator first, InputIterator last);
// pair<iterator, bool> insert_or_assign(K&&, M&&);
// iterator insert_or_assign(const_iterator hint, K&&, M&&);
// pair<iterator, bool> emplace(Args&&...);
// iterator emplace_hint(const_iterator, Args&&...);
// pair<iterator, bool> try_emplace(K&&, Args&&...);
// iterator try_emplace(const_iterator hint, K&&, Args&&...);
// Underlying type functions:
// container_type extract() &&;
// void replace(container_type&&);
//
// Erase functions:
// iterator erase(iterator);
// iterator erase(const_iterator);
// iterator erase(const_iterator first, const_iterator& last);
// template <class K> size_t erase(const K& key);
//
// Comparators (see std::map documentation).
// key_compare key_comp() const;
// value_compare value_comp() const;
//
// Search functions:
// template <typename K> size_t count(const K&) const;
// template <typename K> iterator find(const K&);
// template <typename K> const_iterator find(const K&) const;
// template <typename K> bool contains(const K&) const;
// template <typename K> pair<iterator, iterator> equal_range(const K&);
// template <typename K> iterator lower_bound(const K&);
// template <typename K> const_iterator lower_bound(const K&) const;
// template <typename K> iterator upper_bound(const K&);
// template <typename K> const_iterator upper_bound(const K&) const;
//
// General functions:
// void swap(flat_map&);
//
// Non-member operators:
// bool operator==(const flat_map&, const flat_map);
// bool operator!=(const flat_map&, const flat_map);
// bool operator<(const flat_map&, const flat_map);
// bool operator>(const flat_map&, const flat_map);
// bool operator>=(const flat_map&, const flat_map);
// bool operator<=(const flat_map&, const flat_map);
//
template <class Key,
class Mapped,
class Compare = std::less<>,
class Container = std::vector<std::pair<Key, Mapped>>>
class flat_map : public ::webrtc::flat_containers_internal::flat_tree<
Key,
flat_containers_internal::GetFirst,
Compare,
Container> {
private:
using tree = typename ::webrtc::flat_containers_internal::
flat_tree<Key, flat_containers_internal::GetFirst, Compare, Container>;
public:
using key_type = typename tree::key_type;
using mapped_type = Mapped;
using value_type = typename tree::value_type;
using reference = typename Container::reference;
using const_reference = typename Container::const_reference;
using size_type = typename Container::size_type;
using difference_type = typename Container::difference_type;
using iterator = typename tree::iterator;
using const_iterator = typename tree::const_iterator;
using reverse_iterator = typename tree::reverse_iterator;
using const_reverse_iterator = typename tree::const_reverse_iterator;
using container_type = typename tree::container_type;
// --------------------------------------------------------------------------
// Lifetime and assignments.
//
// Note: we explicitly bring operator= in because otherwise
// flat_map<...> x;
// x = {...};
// Would first create a flat_map and then move assign it. This most likely
// would be optimized away but still affects our debug builds.
using tree::tree;
using tree::operator=;
// Out-of-bound calls to at() will CHECK.
template <class K>
mapped_type& at(const K& key);
template <class K>
const mapped_type& at(const K& key) const;
// --------------------------------------------------------------------------
// Map-specific insert operations.
//
// Normal insert() functions are inherited from flat_tree.
//
// Assume that every operation invalidates iterators and references.
// Insertion of one element can take O(size).
mapped_type& operator[](const key_type& key);
mapped_type& operator[](key_type&& key);
template <class K, class M>
std::pair<iterator, bool> insert_or_assign(K&& key, M&& obj);
template <class K, class M>
iterator insert_or_assign(const_iterator hint, K&& key, M&& obj);
template <class K, class... Args>
std::enable_if_t<std::is_constructible<key_type, K&&>::value,
std::pair<iterator, bool>>
try_emplace(K&& key, Args&&... args);
template <class K, class... Args>
std::enable_if_t<std::is_constructible<key_type, K&&>::value, iterator>
try_emplace(const_iterator hint, K&& key, Args&&... args);
// --------------------------------------------------------------------------
// General operations.
//
// Assume that swap invalidates iterators and references.
void swap(flat_map& other) noexcept;
friend void swap(flat_map& lhs, flat_map& rhs) noexcept { lhs.swap(rhs); }
};
// ----------------------------------------------------------------------------
// Lookups.
template <class Key, class Mapped, class Compare, class Container>
template <class K>
auto flat_map<Key, Mapped, Compare, Container>::at(const K& key)
-> mapped_type& {
iterator found = tree::find(key);
RTC_CHECK(found != tree::end());
return found->second;
}
template <class Key, class Mapped, class Compare, class Container>
template <class K>
auto flat_map<Key, Mapped, Compare, Container>::at(const K& key) const
-> const mapped_type& {
const_iterator found = tree::find(key);
RTC_CHECK(found != tree::cend());
return found->second;
}
// ----------------------------------------------------------------------------
// Insert operations.
template <class Key, class Mapped, class Compare, class Container>
auto flat_map<Key, Mapped, Compare, Container>::operator[](const key_type& key)
-> mapped_type& {
iterator found = tree::lower_bound(key);
if (found == tree::end() || tree::key_comp()(key, found->first))
found = tree::unsafe_emplace(found, key, mapped_type());
return found->second;
}
template <class Key, class Mapped, class Compare, class Container>
auto flat_map<Key, Mapped, Compare, Container>::operator[](key_type&& key)
-> mapped_type& {
iterator found = tree::lower_bound(key);
if (found == tree::end() || tree::key_comp()(key, found->first))
found = tree::unsafe_emplace(found, std::move(key), mapped_type());
return found->second;
}
template <class Key, class Mapped, class Compare, class Container>
template <class K, class M>
auto flat_map<Key, Mapped, Compare, Container>::insert_or_assign(K&& key,
M&& obj)
-> std::pair<iterator, bool> {
auto result =
tree::emplace_key_args(key, std::forward<K>(key), std::forward<M>(obj));
if (!result.second)
result.first->second = std::forward<M>(obj);
return result;
}
template <class Key, class Mapped, class Compare, class Container>
template <class K, class M>
auto flat_map<Key, Mapped, Compare, Container>::insert_or_assign(
const_iterator hint,
K&& key,
M&& obj) -> iterator {
auto result = tree::emplace_hint_key_args(hint, key, std::forward<K>(key),
std::forward<M>(obj));
if (!result.second)
result.first->second = std::forward<M>(obj);
return result.first;
}
template <class Key, class Mapped, class Compare, class Container>
template <class K, class... Args>
auto flat_map<Key, Mapped, Compare, Container>::try_emplace(K&& key,
Args&&... args)
-> std::enable_if_t<std::is_constructible<key_type, K&&>::value,
std::pair<iterator, bool>> {
return tree::emplace_key_args(
key, std::piecewise_construct,
std::forward_as_tuple(std::forward<K>(key)),
std::forward_as_tuple(std::forward<Args>(args)...));
}
template <class Key, class Mapped, class Compare, class Container>
template <class K, class... Args>
auto flat_map<Key, Mapped, Compare, Container>::try_emplace(const_iterator hint,
K&& key,
Args&&... args)
-> std::enable_if_t<std::is_constructible<key_type, K&&>::value, iterator> {
return tree::emplace_hint_key_args(
hint, key, std::piecewise_construct,
std::forward_as_tuple(std::forward<K>(key)),
std::forward_as_tuple(std::forward<Args>(args)...))
.first;
}
// ----------------------------------------------------------------------------
// General operations.
template <class Key, class Mapped, class Compare, class Container>
void flat_map<Key, Mapped, Compare, Container>::swap(flat_map& other) noexcept {
tree::swap(other);
}
// Erases all elements that match predicate. It has O(size) complexity.
//
// flat_map<int, Timestamp> last_times;
// ...
// EraseIf(last_times,
// [&](const auto& element) { return now - element.second > kLimit; });
// NOLINTNEXTLINE(misc-unused-using-decls)
using ::webrtc::flat_containers_internal::EraseIf;
} // namespace webrtc
#endif // RTC_BASE_CONTAINERS_FLAT_MAP_H_

View file

@ -0,0 +1,178 @@
/*
* Copyright (c) 2021 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.
*/
// This implementation is borrowed from Chromium.
#ifndef RTC_BASE_CONTAINERS_FLAT_SET_H_
#define RTC_BASE_CONTAINERS_FLAT_SET_H_
#include <functional>
#include <vector>
#include "rtc_base/containers/flat_tree.h" // IWYU pragma: export
#include "rtc_base/containers/identity.h"
namespace webrtc {
// flat_set is a container with a std::set-like interface that stores its
// contents in a sorted container, by default a vector.
//
// Its implementation mostly tracks the corresponding standardization proposal
// https://wg21.link/P1222.
//
//
// PROS
//
// - Good memory locality.
// - Low overhead, especially for smaller sets.
// - Performance is good for more workloads than you might expect (see
// //base/containers/README.md in Chromium repository)
// - Supports C++14 set interface.
//
// CONS
//
// - Inserts and removals are O(n).
//
// IMPORTANT NOTES
//
// - Iterators are invalidated across mutations.
// - If possible, construct a flat_set in one operation by inserting into
// a container and moving that container into the flat_set constructor.
// - For multiple removals use base::EraseIf() which is O(n) rather than
// O(n * removed_items).
//
// QUICK REFERENCE
//
// Most of the core functionality is inherited from flat_tree. Please see
// flat_tree.h for more details for most of these functions. As a quick
// reference, the functions available are:
//
// Constructors (inputs need not be sorted):
// flat_set(const flat_set&);
// flat_set(flat_set&&);
// flat_set(InputIterator first, InputIterator last,
// const Compare& compare = Compare());
// flat_set(const container_type& items,
// const Compare& compare = Compare());
// flat_set(container_type&& items,
// const Compare& compare = Compare()); // Re-use storage.
// flat_set(std::initializer_list<value_type> ilist,
// const Compare& comp = Compare());
//
// Constructors (inputs need to be sorted):
// flat_set(sorted_unique_t,
// InputIterator first, InputIterator last,
// const Compare& compare = Compare());
// flat_set(sorted_unique_t,
// const container_type& items,
// const Compare& compare = Compare());
// flat_set(sorted_unique_t,
// container_type&& items,
// const Compare& compare = Compare()); // Re-use storage.
// flat_set(sorted_unique_t,
// std::initializer_list<value_type> ilist,
// const Compare& comp = Compare());
//
// Assignment functions:
// flat_set& operator=(const flat_set&);
// flat_set& operator=(flat_set&&);
// flat_set& operator=(initializer_list<Key>);
//
// Memory management functions:
// void reserve(size_t);
// size_t capacity() const;
// void shrink_to_fit();
//
// Size management functions:
// void clear();
// size_t size() const;
// size_t max_size() const;
// bool empty() const;
//
// Iterator functions:
// iterator begin();
// const_iterator begin() const;
// const_iterator cbegin() const;
// iterator end();
// const_iterator end() const;
// const_iterator cend() const;
// reverse_iterator rbegin();
// const reverse_iterator rbegin() const;
// const_reverse_iterator crbegin() const;
// reverse_iterator rend();
// const_reverse_iterator rend() const;
// const_reverse_iterator crend() const;
//
// Insert and accessor functions:
// pair<iterator, bool> insert(const key_type&);
// pair<iterator, bool> insert(key_type&&);
// void insert(InputIterator first, InputIterator last);
// iterator insert(const_iterator hint, const key_type&);
// iterator insert(const_iterator hint, key_type&&);
// pair<iterator, bool> emplace(Args&&...);
// iterator emplace_hint(const_iterator, Args&&...);
//
// Underlying type functions:
// container_type extract() &&;
// void replace(container_type&&);
//
// Erase functions:
// iterator erase(iterator);
// iterator erase(const_iterator);
// iterator erase(const_iterator first, const_iterator& last);
// template <typename K> size_t erase(const K& key);
//
// Comparators (see std::set documentation).
// key_compare key_comp() const;
// value_compare value_comp() const;
//
// Search functions:
// template <typename K> size_t count(const K&) const;
// template <typename K> iterator find(const K&);
// template <typename K> const_iterator find(const K&) const;
// template <typename K> bool contains(const K&) const;
// template <typename K> pair<iterator, iterator> equal_range(K&);
// template <typename K> iterator lower_bound(const K&);
// template <typename K> const_iterator lower_bound(const K&) const;
// template <typename K> iterator upper_bound(const K&);
// template <typename K> const_iterator upper_bound(const K&) const;
//
// General functions:
// void swap(flat_set&);
//
// Non-member operators:
// bool operator==(const flat_set&, const flat_set);
// bool operator!=(const flat_set&, const flat_set);
// bool operator<(const flat_set&, const flat_set);
// bool operator>(const flat_set&, const flat_set);
// bool operator>=(const flat_set&, const flat_set);
// bool operator<=(const flat_set&, const flat_set);
//
template <class Key,
class Compare = std::less<>,
class Container = std::vector<Key>>
using flat_set = typename ::webrtc::flat_containers_internal::
flat_tree<Key, webrtc::identity, Compare, Container>;
// ----------------------------------------------------------------------------
// General operations.
// Erases all elements that match predicate. It has O(size) complexity.
//
// flat_set<int> numbers;
// ...
// EraseIf(numbers, [](int number) { return number % 2 == 1; });
// NOLINTNEXTLINE(misc-unused-using-decls)
using ::webrtc::flat_containers_internal::EraseIf;
} // namespace webrtc
#endif // RTC_BASE_CONTAINERS_FLAT_SET_H_

View file

@ -0,0 +1,19 @@
/*
* Copyright (c) 2021 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.
*/
// This implementation is borrowed from Chromium.
#include "rtc_base/containers/flat_tree.h"
namespace webrtc {
sorted_unique_t sorted_unique;
} // namespace webrtc

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2021 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.
*/
// This implementation is borrowed from Chromium.
#ifndef RTC_BASE_CONTAINERS_IDENTITY_H_
#define RTC_BASE_CONTAINERS_IDENTITY_H_
#include <utility>
namespace webrtc {
// Implementation of C++20's std::identity.
//
// Reference:
// - https://en.cppreference.com/w/cpp/utility/functional/identity
// - https://wg21.link/func.identity
struct identity {
template <typename T>
constexpr T&& operator()(T&& t) const noexcept {
return std::forward<T>(t);
}
using is_transparent = void;
};
} // namespace webrtc
#endif // RTC_BASE_CONTAINERS_IDENTITY_H_

View file

@ -0,0 +1,162 @@
/*
* Copyright (c) 2021 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.
*/
// This implementation is borrowed from Chromium.
#ifndef RTC_BASE_CONTAINERS_INVOKE_H_
#define RTC_BASE_CONTAINERS_INVOKE_H_
#include <type_traits>
#include <utility>
namespace webrtc {
namespace invoke_internal {
// Helper struct and alias to deduce the class type from a member function
// pointer or member object pointer.
template <typename DecayedF>
struct member_pointer_class {};
template <typename ReturnT, typename ClassT>
struct member_pointer_class<ReturnT ClassT::*> {
using type = ClassT;
};
template <typename DecayedF>
using member_pointer_class_t = typename member_pointer_class<DecayedF>::type;
// Utility struct to detect specializations of std::reference_wrapper.
template <typename T>
struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
// Small helpers used below in invoke_internal::invoke to make the SFINAE more
// concise.
template <typename F>
const bool& IsMemFunPtr =
std::is_member_function_pointer<std::decay_t<F>>::value;
template <typename F>
const bool& IsMemObjPtr = std::is_member_object_pointer<std::decay_t<F>>::value;
template <typename F,
typename T,
typename MemPtrClass = member_pointer_class_t<std::decay_t<F>>>
const bool& IsMemPtrToBaseOf =
std::is_base_of<MemPtrClass, std::decay_t<T>>::value;
template <typename T>
const bool& IsRefWrapper = is_reference_wrapper<std::decay_t<T>>::value;
template <bool B>
using EnableIf = std::enable_if_t<B, bool>;
// Invokes a member function pointer on a reference to an object of a suitable
// type. Covers bullet 1 of the INVOKE definition.
//
// Reference: https://wg21.link/func.require#1.1
template <typename F,
typename T1,
typename... Args,
EnableIf<IsMemFunPtr<F> && IsMemPtrToBaseOf<F, T1>> = true>
constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) {
return (std::forward<T1>(t1).*f)(std::forward<Args>(args)...);
}
// Invokes a member function pointer on a std::reference_wrapper to an object of
// a suitable type. Covers bullet 2 of the INVOKE definition.
//
// Reference: https://wg21.link/func.require#1.2
template <typename F,
typename T1,
typename... Args,
EnableIf<IsMemFunPtr<F> && IsRefWrapper<T1>> = true>
constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) {
return (t1.get().*f)(std::forward<Args>(args)...);
}
// Invokes a member function pointer on a pointer-like type to an object of a
// suitable type. Covers bullet 3 of the INVOKE definition.
//
// Reference: https://wg21.link/func.require#1.3
template <typename F,
typename T1,
typename... Args,
EnableIf<IsMemFunPtr<F> && !IsMemPtrToBaseOf<F, T1> &&
!IsRefWrapper<T1>> = true>
constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) {
return ((*std::forward<T1>(t1)).*f)(std::forward<Args>(args)...);
}
// Invokes a member object pointer on a reference to an object of a suitable
// type. Covers bullet 4 of the INVOKE definition.
//
// Reference: https://wg21.link/func.require#1.4
template <typename F,
typename T1,
EnableIf<IsMemObjPtr<F> && IsMemPtrToBaseOf<F, T1>> = true>
constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) {
return std::forward<T1>(t1).*f;
}
// Invokes a member object pointer on a std::reference_wrapper to an object of
// a suitable type. Covers bullet 5 of the INVOKE definition.
//
// Reference: https://wg21.link/func.require#1.5
template <typename F,
typename T1,
EnableIf<IsMemObjPtr<F> && IsRefWrapper<T1>> = true>
constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) {
return t1.get().*f;
}
// Invokes a member object pointer on a pointer-like type to an object of a
// suitable type. Covers bullet 6 of the INVOKE definition.
//
// Reference: https://wg21.link/func.require#1.6
template <typename F,
typename T1,
EnableIf<IsMemObjPtr<F> && !IsMemPtrToBaseOf<F, T1> &&
!IsRefWrapper<T1>> = true>
constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) {
return (*std::forward<T1>(t1)).*f;
}
// Invokes a regular function or function object. Covers bullet 7 of the INVOKE
// definition.
//
// Reference: https://wg21.link/func.require#1.7
template <typename F, typename... Args>
constexpr decltype(auto) InvokeImpl(F&& f, Args&&... args) {
return std::forward<F>(f)(std::forward<Args>(args)...);
}
} // namespace invoke_internal
// Implementation of C++17's std::invoke. This is not based on implementation
// referenced in original std::invoke proposal, but rather a manual
// implementation, so that it can be constexpr.
//
// References:
// - https://wg21.link/n4169#implementability
// - https://en.cppreference.com/w/cpp/utility/functional/invoke
// - https://wg21.link/func.invoke
template <typename F, typename... Args>
constexpr decltype(auto) invoke(F&& f, Args&&... args) {
return invoke_internal::InvokeImpl(std::forward<F>(f),
std::forward<Args>(args)...);
}
} // namespace webrtc
#endif // RTC_BASE_CONTAINERS_INVOKE_H_

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2021 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.
*/
// This implementation is borrowed from Chromium.
#ifndef RTC_BASE_CONTAINERS_MOVE_ONLY_INT_H_
#define RTC_BASE_CONTAINERS_MOVE_ONLY_INT_H_
namespace webrtc {
// A move-only class that holds an integer. This is designed for testing
// containers. See also CopyOnlyInt.
class MoveOnlyInt {
public:
explicit MoveOnlyInt(int data = 1) : data_(data) {}
MoveOnlyInt(const MoveOnlyInt& other) = delete;
MoveOnlyInt& operator=(const MoveOnlyInt& other) = delete;
MoveOnlyInt(MoveOnlyInt&& other) : data_(other.data_) { other.data_ = 0; }
~MoveOnlyInt() { data_ = 0; }
MoveOnlyInt& operator=(MoveOnlyInt&& other) {
data_ = other.data_;
other.data_ = 0;
return *this;
}
friend bool operator==(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {
return lhs.data_ == rhs.data_;
}
friend bool operator!=(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {
return !operator==(lhs, rhs);
}
friend bool operator<(const MoveOnlyInt& lhs, int rhs) {
return lhs.data_ < rhs;
}
friend bool operator<(int lhs, const MoveOnlyInt& rhs) {
return lhs < rhs.data_;
}
friend bool operator<(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {
return lhs.data_ < rhs.data_;
}
friend bool operator>(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {
return rhs < lhs;
}
friend bool operator<=(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {
return !(rhs < lhs);
}
friend bool operator>=(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {
return !(lhs < rhs);
}
int data() const { return data_; }
private:
volatile int data_;
};
} // namespace webrtc
#endif // RTC_BASE_CONTAINERS_MOVE_ONLY_INT_H_

View file

@ -0,0 +1,127 @@
/*
* Copyright 2016 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 "rtc_base/copy_on_write_buffer.h"
#include <stddef.h>
#include "absl/strings/string_view.h"
namespace rtc {
CopyOnWriteBuffer::CopyOnWriteBuffer() : offset_(0), size_(0) {
RTC_DCHECK(IsConsistent());
}
CopyOnWriteBuffer::CopyOnWriteBuffer(const CopyOnWriteBuffer& buf)
: buffer_(buf.buffer_), offset_(buf.offset_), size_(buf.size_) {}
CopyOnWriteBuffer::CopyOnWriteBuffer(CopyOnWriteBuffer&& buf) noexcept
: buffer_(std::move(buf.buffer_)), offset_(buf.offset_), size_(buf.size_) {
buf.offset_ = 0;
buf.size_ = 0;
RTC_DCHECK(IsConsistent());
}
CopyOnWriteBuffer::CopyOnWriteBuffer(absl::string_view s)
: CopyOnWriteBuffer(s.data(), s.length()) {}
CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size)
: buffer_(size > 0 ? new RefCountedBuffer(size) : nullptr),
offset_(0),
size_(size) {
RTC_DCHECK(IsConsistent());
}
CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size, size_t capacity)
: buffer_(size > 0 || capacity > 0 ? new RefCountedBuffer(size, capacity)
: nullptr),
offset_(0),
size_(size) {
RTC_DCHECK(IsConsistent());
}
CopyOnWriteBuffer::~CopyOnWriteBuffer() = default;
bool CopyOnWriteBuffer::operator==(const CopyOnWriteBuffer& buf) const {
// Must either be the same view of the same buffer or have the same contents.
RTC_DCHECK(IsConsistent());
RTC_DCHECK(buf.IsConsistent());
return size_ == buf.size_ &&
(cdata() == buf.cdata() || memcmp(cdata(), buf.cdata(), size_) == 0);
}
void CopyOnWriteBuffer::SetSize(size_t size) {
RTC_DCHECK(IsConsistent());
if (!buffer_) {
if (size > 0) {
buffer_ = new RefCountedBuffer(size);
offset_ = 0;
size_ = size;
}
RTC_DCHECK(IsConsistent());
return;
}
if (size <= size_) {
size_ = size;
return;
}
UnshareAndEnsureCapacity(std::max(capacity(), size));
buffer_->SetSize(size + offset_);
size_ = size;
RTC_DCHECK(IsConsistent());
}
void CopyOnWriteBuffer::EnsureCapacity(size_t new_capacity) {
RTC_DCHECK(IsConsistent());
if (!buffer_) {
if (new_capacity > 0) {
buffer_ = new RefCountedBuffer(0, new_capacity);
offset_ = 0;
size_ = 0;
}
RTC_DCHECK(IsConsistent());
return;
} else if (new_capacity <= capacity()) {
return;
}
UnshareAndEnsureCapacity(new_capacity);
RTC_DCHECK(IsConsistent());
}
void CopyOnWriteBuffer::Clear() {
if (!buffer_)
return;
if (buffer_->HasOneRef()) {
buffer_->Clear();
} else {
buffer_ = new RefCountedBuffer(0, capacity());
}
offset_ = 0;
size_ = 0;
RTC_DCHECK(IsConsistent());
}
void CopyOnWriteBuffer::UnshareAndEnsureCapacity(size_t new_capacity) {
if (buffer_->HasOneRef() && new_capacity <= capacity()) {
return;
}
buffer_ =
new RefCountedBuffer(buffer_->data() + offset_, size_, new_capacity);
offset_ = 0;
RTC_DCHECK(IsConsistent());
}
} // namespace rtc

View file

@ -0,0 +1,318 @@
/*
* Copyright 2016 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.
*/
#ifndef RTC_BASE_COPY_ON_WRITE_BUFFER_H_
#define RTC_BASE_COPY_ON_WRITE_BUFFER_H_
#include <stdint.h>
#include <algorithm>
#include <cstring>
#include <string>
#include <type_traits>
#include <utility>
#include "absl/strings/string_view.h"
#include "api/scoped_refptr.h"
#include "rtc_base/buffer.h"
#include "rtc_base/checks.h"
#include "rtc_base/ref_counted_object.h"
#include "rtc_base/system/rtc_export.h"
#include "rtc_base/type_traits.h"
namespace rtc {
class RTC_EXPORT CopyOnWriteBuffer {
public:
// An empty buffer.
CopyOnWriteBuffer();
// Share the data with an existing buffer.
CopyOnWriteBuffer(const CopyOnWriteBuffer& buf);
// Move contents from an existing buffer.
CopyOnWriteBuffer(CopyOnWriteBuffer&& buf) noexcept;
// Construct a buffer from a string, convenient for unittests.
explicit CopyOnWriteBuffer(absl::string_view s);
// Construct a buffer with the specified number of uninitialized bytes.
explicit CopyOnWriteBuffer(size_t size);
CopyOnWriteBuffer(size_t size, size_t capacity);
// Construct a buffer and copy the specified number of bytes into it. The
// source array may be (const) uint8_t*, int8_t*, or char*.
template <typename T,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
CopyOnWriteBuffer(const T* data, size_t size)
: CopyOnWriteBuffer(data, size, size) {}
template <typename T,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
CopyOnWriteBuffer(const T* data, size_t size, size_t capacity)
: CopyOnWriteBuffer(size, capacity) {
if (buffer_) {
std::memcpy(buffer_->data(), data, size);
offset_ = 0;
size_ = size;
}
}
// Construct a buffer from the contents of an array.
template <typename T,
size_t N,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
CopyOnWriteBuffer(const T (&array)[N]) // NOLINT: runtime/explicit
: CopyOnWriteBuffer(array, N) {}
// Construct a buffer from a vector like type.
template <typename VecT,
typename ElemT = typename std::remove_pointer_t<
decltype(std::declval<VecT>().data())>,
typename std::enable_if_t<
!std::is_same<VecT, CopyOnWriteBuffer>::value &&
HasDataAndSize<VecT, ElemT>::value &&
internal::BufferCompat<uint8_t, ElemT>::value>* = nullptr>
explicit CopyOnWriteBuffer(const VecT& v)
: CopyOnWriteBuffer(v.data(), v.size()) {}
// Construct a buffer from a vector like type and a capacity argument
template <typename VecT,
typename ElemT = typename std::remove_pointer_t<
decltype(std::declval<VecT>().data())>,
typename std::enable_if_t<
!std::is_same<VecT, CopyOnWriteBuffer>::value &&
HasDataAndSize<VecT, ElemT>::value &&
internal::BufferCompat<uint8_t, ElemT>::value>* = nullptr>
explicit CopyOnWriteBuffer(const VecT& v, size_t capacity)
: CopyOnWriteBuffer(v.data(), v.size(), capacity) {}
~CopyOnWriteBuffer();
// Get a pointer to the data. Just .data() will give you a (const) uint8_t*,
// but you may also use .data<int8_t>() and .data<char>().
template <typename T = uint8_t,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
const T* data() const {
return cdata<T>();
}
// Get writable pointer to the data. This will create a copy of the underlying
// data if it is shared with other buffers.
template <typename T = uint8_t,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
T* MutableData() {
RTC_DCHECK(IsConsistent());
if (!buffer_) {
return nullptr;
}
UnshareAndEnsureCapacity(capacity());
return buffer_->data<T>() + offset_;
}
// Get const pointer to the data. This will not create a copy of the
// underlying data if it is shared with other buffers.
template <typename T = uint8_t,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
const T* cdata() const {
RTC_DCHECK(IsConsistent());
if (!buffer_) {
return nullptr;
}
return buffer_->data<T>() + offset_;
}
bool empty() const { return size_ == 0; }
size_t size() const {
RTC_DCHECK(IsConsistent());
return size_;
}
size_t capacity() const {
RTC_DCHECK(IsConsistent());
return buffer_ ? buffer_->capacity() - offset_ : 0;
}
CopyOnWriteBuffer& operator=(const CopyOnWriteBuffer& buf) {
RTC_DCHECK(IsConsistent());
RTC_DCHECK(buf.IsConsistent());
if (&buf != this) {
buffer_ = buf.buffer_;
offset_ = buf.offset_;
size_ = buf.size_;
}
return *this;
}
CopyOnWriteBuffer& operator=(CopyOnWriteBuffer&& buf) {
RTC_DCHECK(IsConsistent());
RTC_DCHECK(buf.IsConsistent());
buffer_ = std::move(buf.buffer_);
offset_ = buf.offset_;
size_ = buf.size_;
buf.offset_ = 0;
buf.size_ = 0;
return *this;
}
bool operator==(const CopyOnWriteBuffer& buf) const;
bool operator!=(const CopyOnWriteBuffer& buf) const {
return !(*this == buf);
}
uint8_t operator[](size_t index) const {
RTC_DCHECK_LT(index, size());
return cdata()[index];
}
// Replace the contents of the buffer. Accepts the same types as the
// constructors.
template <typename T,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
void SetData(const T* data, size_t size) {
RTC_DCHECK(IsConsistent());
if (!buffer_) {
buffer_ = size > 0 ? new RefCountedBuffer(data, size) : nullptr;
} else if (!buffer_->HasOneRef()) {
buffer_ = new RefCountedBuffer(data, size, capacity());
} else {
buffer_->SetData(data, size);
}
offset_ = 0;
size_ = size;
RTC_DCHECK(IsConsistent());
}
template <typename T,
size_t N,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
void SetData(const T (&array)[N]) {
SetData(array, N);
}
void SetData(const CopyOnWriteBuffer& buf) {
RTC_DCHECK(IsConsistent());
RTC_DCHECK(buf.IsConsistent());
if (&buf != this) {
buffer_ = buf.buffer_;
offset_ = buf.offset_;
size_ = buf.size_;
}
}
// Append data to the buffer. Accepts the same types as the constructors.
template <typename T,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
void AppendData(const T* data, size_t size) {
RTC_DCHECK(IsConsistent());
if (!buffer_) {
buffer_ = new RefCountedBuffer(data, size);
offset_ = 0;
size_ = size;
RTC_DCHECK(IsConsistent());
return;
}
UnshareAndEnsureCapacity(std::max(capacity(), size_ + size));
buffer_->SetSize(offset_ +
size_); // Remove data to the right of the slice.
buffer_->AppendData(data, size);
size_ += size;
RTC_DCHECK(IsConsistent());
}
template <typename T,
size_t N,
typename std::enable_if<
internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
void AppendData(const T (&array)[N]) {
AppendData(array, N);
}
template <typename VecT,
typename ElemT = typename std::remove_pointer_t<
decltype(std::declval<VecT>().data())>,
typename std::enable_if_t<
HasDataAndSize<VecT, ElemT>::value &&
internal::BufferCompat<uint8_t, ElemT>::value>* = nullptr>
void AppendData(const VecT& v) {
AppendData(v.data(), v.size());
}
// Sets the size of the buffer. If the new size is smaller than the old, the
// buffer contents will be kept but truncated; if the new size is greater,
// the existing contents will be kept and the new space will be
// uninitialized.
void SetSize(size_t size);
// Ensure that the buffer size can be increased to at least capacity without
// further reallocation. (Of course, this operation might need to reallocate
// the buffer.)
void EnsureCapacity(size_t capacity);
// Resets the buffer to zero size without altering capacity. Works even if the
// buffer has been moved from.
void Clear();
// Swaps two buffers.
friend void swap(CopyOnWriteBuffer& a, CopyOnWriteBuffer& b) {
a.buffer_.swap(b.buffer_);
std::swap(a.offset_, b.offset_);
std::swap(a.size_, b.size_);
}
CopyOnWriteBuffer Slice(size_t offset, size_t length) const {
CopyOnWriteBuffer slice(*this);
RTC_DCHECK_LE(offset, size_);
RTC_DCHECK_LE(length + offset, size_);
slice.offset_ += offset;
slice.size_ = length;
return slice;
}
private:
using RefCountedBuffer = FinalRefCountedObject<Buffer>;
// Create a copy of the underlying data if it is referenced from other Buffer
// objects or there is not enough capacity.
void UnshareAndEnsureCapacity(size_t new_capacity);
// Pre- and postcondition of all methods.
bool IsConsistent() const {
if (buffer_) {
return buffer_->capacity() > 0 && offset_ <= buffer_->size() &&
offset_ + size_ <= buffer_->size();
} else {
return size_ == 0 && offset_ == 0;
}
}
// buffer_ is either null, or points to an rtc::Buffer with capacity > 0.
scoped_refptr<RefCountedBuffer> buffer_;
// This buffer may represent a slice of a original data.
size_t offset_; // Offset of a current slice in the original data in buffer_.
// Should be 0 if the buffer_ is empty.
size_t size_; // Size of a current slice in the original data in buffer_.
// Should be 0 if the buffer_ is empty.
};
} // namespace rtc
#endif // RTC_BASE_COPY_ON_WRITE_BUFFER_H_

View file

@ -0,0 +1,145 @@
/*
* Copyright (c) 2017 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 "rtc_base/cpu_time.h"
#include "rtc_base/logging.h"
#include "rtc_base/time_utils.h"
#if defined(WEBRTC_LINUX)
#include <time.h>
#elif defined(WEBRTC_MAC)
#include <mach/mach_init.h>
#include <mach/mach_port.h>
#include <mach/thread_act.h>
#include <mach/thread_info.h>
#include <sys/resource.h>
#include <sys/times.h>
#include <sys/types.h>
#include <unistd.h>
#elif defined(WEBRTC_WIN)
#include <windows.h>
#elif defined(WEBRTC_FUCHSIA)
#include <lib/zx/process.h>
#include <lib/zx/thread.h>
#include <zircon/status.h>
#endif
#if defined(WEBRTC_WIN)
namespace {
// FILETIME resolution is 100 nanosecs.
const int64_t kNanosecsPerFiletime = 100;
} // namespace
#endif
namespace rtc {
int64_t GetProcessCpuTimeNanos() {
#if defined(WEBRTC_FUCHSIA)
zx_info_task_runtime_t runtime_info;
zx_status_t status =
zx::process::self()->get_info(ZX_INFO_TASK_RUNTIME, &runtime_info,
sizeof(runtime_info), nullptr, nullptr);
if (status == ZX_OK) {
return runtime_info.cpu_time;
} else {
RTC_LOG_ERR(LS_ERROR) << "get_info() failed: "
<< zx_status_get_string(status);
}
#elif defined(WEBRTC_LINUX)
struct timespec ts;
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0) {
return ts.tv_sec * kNumNanosecsPerSec + ts.tv_nsec;
} else {
RTC_LOG_ERR(LS_ERROR) << "clock_gettime() failed.";
}
#elif defined(WEBRTC_MAC)
struct rusage rusage;
if (getrusage(RUSAGE_SELF, &rusage) == 0) {
return rusage.ru_utime.tv_sec * kNumNanosecsPerSec +
rusage.ru_utime.tv_usec * kNumNanosecsPerMicrosec;
} else {
RTC_LOG_ERR(LS_ERROR) << "getrusage() failed.";
}
#elif defined(WEBRTC_WIN)
FILETIME createTime;
FILETIME exitTime;
FILETIME kernelTime;
FILETIME userTime;
if (GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime,
&userTime) != 0) {
return ((static_cast<uint64_t>(userTime.dwHighDateTime) << 32) +
userTime.dwLowDateTime) *
kNanosecsPerFiletime;
} else {
RTC_LOG_ERR(LS_ERROR) << "GetProcessTimes() failed.";
}
#else
// Not implemented yet.
static_assert(
false, "GetProcessCpuTimeNanos() platform support not yet implemented.");
#endif
return -1;
}
int64_t GetThreadCpuTimeNanos() {
#if defined(WEBRTC_FUCHSIA)
zx_info_task_runtime_t runtime_info;
zx_status_t status =
zx::thread::self()->get_info(ZX_INFO_TASK_RUNTIME, &runtime_info,
sizeof(runtime_info), nullptr, nullptr);
if (status == ZX_OK) {
return runtime_info.cpu_time;
} else {
RTC_LOG_ERR(LS_ERROR) << "get_info() failed: "
<< zx_status_get_string(status);
}
#elif defined(WEBRTC_LINUX)
struct timespec ts;
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0) {
return ts.tv_sec * kNumNanosecsPerSec + ts.tv_nsec;
} else {
RTC_LOG_ERR(LS_ERROR) << "clock_gettime() failed.";
}
#elif defined(WEBRTC_MAC)
mach_port_t thread_port = mach_thread_self();
thread_basic_info_data_t info;
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
kern_return_t kr =
thread_info(thread_port, THREAD_BASIC_INFO, (thread_info_t)&info, &count);
mach_port_deallocate(mach_task_self(), thread_port);
if (kr == KERN_SUCCESS) {
return info.user_time.seconds * kNumNanosecsPerSec +
info.user_time.microseconds * kNumNanosecsPerMicrosec;
} else {
RTC_LOG_ERR(LS_ERROR) << "thread_info() failed.";
}
#elif defined(WEBRTC_WIN)
FILETIME createTime;
FILETIME exitTime;
FILETIME kernelTime;
FILETIME userTime;
if (GetThreadTimes(GetCurrentThread(), &createTime, &exitTime, &kernelTime,
&userTime) != 0) {
return ((static_cast<uint64_t>(userTime.dwHighDateTime) << 32) +
userTime.dwLowDateTime) *
kNanosecsPerFiletime;
} else {
RTC_LOG_ERR(LS_ERROR) << "GetThreadTimes() failed.";
}
#else
// Not implemented yet.
static_assert(
false, "GetThreadCpuTimeNanos() platform support not yet implemented.");
#endif
return -1;
}
} // namespace rtc

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2017 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.
*/
#ifndef RTC_BASE_CPU_TIME_H_
#define RTC_BASE_CPU_TIME_H_
#include <stdint.h>
namespace rtc {
// Returns total CPU time of a current process in nanoseconds.
// Time base is unknown, therefore use only to calculate deltas.
int64_t GetProcessCpuTimeNanos();
// Returns total CPU time of a current thread in nanoseconds.
// Time base is unknown, therefore use only to calculate deltas.
int64_t GetThreadCpuTimeNanos();
} // namespace rtc
#endif // RTC_BASE_CPU_TIME_H_

View file

@ -0,0 +1,50 @@
/*
* Copyright 2012 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 "rtc_base/crc32.h"
#include "rtc_base/arraysize.h"
namespace rtc {
// This implementation is based on the sample implementation in RFC 1952.
// CRC32 polynomial, in reversed form.
// See RFC 1952, or http://en.wikipedia.org/wiki/Cyclic_redundancy_check
static const uint32_t kCrc32Polynomial = 0xEDB88320;
static uint32_t* LoadCrc32Table() {
static uint32_t kCrc32Table[256];
for (uint32_t i = 0; i < arraysize(kCrc32Table); ++i) {
uint32_t c = i;
for (size_t j = 0; j < 8; ++j) {
if (c & 1) {
c = kCrc32Polynomial ^ (c >> 1);
} else {
c >>= 1;
}
}
kCrc32Table[i] = c;
}
return kCrc32Table;
}
uint32_t UpdateCrc32(uint32_t start, const void* buf, size_t len) {
static uint32_t* kCrc32Table = LoadCrc32Table();
uint32_t c = start ^ 0xFFFFFFFF;
const uint8_t* u = static_cast<const uint8_t*>(buf);
for (size_t i = 0; i < len; ++i) {
c = kCrc32Table[(c ^ u[i]) & 0xFF] ^ (c >> 8);
}
return c ^ 0xFFFFFFFF;
}
} // namespace rtc

View file

@ -0,0 +1,37 @@
/*
* Copyright 2012 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.
*/
#ifndef RTC_BASE_CRC32_H_
#define RTC_BASE_CRC32_H_
#include <stddef.h>
#include <stdint.h>
#include <string>
#include "absl/strings/string_view.h"
namespace rtc {
// Updates a CRC32 checksum with `len` bytes from `buf`. `initial` holds the
// checksum result from the previous update; for the first call, it should be 0.
uint32_t UpdateCrc32(uint32_t initial, const void* buf, size_t len);
// Computes a CRC32 checksum using `len` bytes from `buf`.
inline uint32_t ComputeCrc32(const void* buf, size_t len) {
return UpdateCrc32(0, buf, len);
}
inline uint32_t ComputeCrc32(absl::string_view str) {
return ComputeCrc32(str.data(), str.size());
}
} // namespace rtc
#endif // RTC_BASE_CRC32_H_

View file

@ -0,0 +1,46 @@
/*
* Copyright 2015 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 "rtc_base/crypt_string.h"
namespace rtc {
size_t EmptyCryptStringImpl::GetLength() const {
return 0;
}
void EmptyCryptStringImpl::CopyTo(char* dest, bool nullterminate) const {
if (nullterminate) {
*dest = '\0';
}
}
std::string EmptyCryptStringImpl::UrlEncode() const {
return "";
}
CryptStringImpl* EmptyCryptStringImpl::Copy() const {
return new EmptyCryptStringImpl();
}
void EmptyCryptStringImpl::CopyRawTo(std::vector<unsigned char>* dest) const {
dest->clear();
}
CryptString::CryptString() : impl_(new EmptyCryptStringImpl()) {}
CryptString::CryptString(const CryptString& other)
: impl_(other.impl_->Copy()) {}
CryptString::CryptString(const CryptStringImpl& impl) : impl_(impl.Copy()) {}
CryptString::~CryptString() = default;
} // namespace rtc

View file

@ -0,0 +1,70 @@
/*
* Copyright 2004 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.
*/
#ifndef RTC_BASE_CRYPT_STRING_H_
#define RTC_BASE_CRYPT_STRING_H_
#include <string.h>
#include <memory>
#include <string>
#include <vector>
namespace rtc {
class CryptStringImpl {
public:
virtual ~CryptStringImpl() {}
virtual size_t GetLength() const = 0;
virtual void CopyTo(char* dest, bool nullterminate) const = 0;
virtual std::string UrlEncode() const = 0;
virtual CryptStringImpl* Copy() const = 0;
virtual void CopyRawTo(std::vector<unsigned char>* dest) const = 0;
};
class EmptyCryptStringImpl : public CryptStringImpl {
public:
~EmptyCryptStringImpl() override {}
size_t GetLength() const override;
void CopyTo(char* dest, bool nullterminate) const override;
std::string UrlEncode() const override;
CryptStringImpl* Copy() const override;
void CopyRawTo(std::vector<unsigned char>* dest) const override;
};
class CryptString {
public:
CryptString();
size_t GetLength() const { return impl_->GetLength(); }
void CopyTo(char* dest, bool nullterminate) const {
impl_->CopyTo(dest, nullterminate);
}
CryptString(const CryptString& other);
explicit CryptString(const CryptStringImpl& impl);
~CryptString();
CryptString& operator=(const CryptString& other) {
if (this != &other) {
impl_.reset(other.impl_->Copy());
}
return *this;
}
void Clear() { impl_.reset(new EmptyCryptStringImpl()); }
std::string UrlEncode() const { return impl_->UrlEncode(); }
void CopyRawTo(std::vector<unsigned char>* dest) const {
return impl_->CopyRawTo(dest);
}
private:
std::unique_ptr<const CryptStringImpl> impl_;
};
} // namespace rtc
#endif // RTC_BASE_CRYPT_STRING_H_

View file

@ -0,0 +1,29 @@
/*
* Copyright 2012 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 "rtc_base/data_rate_limiter.h"
namespace rtc {
bool DataRateLimiter::CanUse(size_t desired, double time) {
return ((time > period_end_ && desired <= max_per_period_) ||
(used_in_period_ + desired) <= max_per_period_);
}
void DataRateLimiter::Use(size_t used, double time) {
if (time > period_end_) {
period_start_ = time;
period_end_ = time + period_length_;
used_in_period_ = 0;
}
used_in_period_ += used;
}
} // namespace rtc

View file

@ -0,0 +1,58 @@
/*
* Copyright 2012 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.
*/
#ifndef RTC_BASE_DATA_RATE_LIMITER_H_
#define RTC_BASE_DATA_RATE_LIMITER_H_
#include <stddef.h>
#include "rtc_base/system/rtc_export.h"
namespace rtc {
// Limits the rate of use to a certain maximum quantity per period of
// time. Use, for example, for simple bandwidth throttling.
//
// It's implemented like a diet plan: You have so many calories per
// day. If you hit the limit, you can't eat any more until the next
// day.
class RTC_EXPORT DataRateLimiter {
public:
// For example, 100kb per second.
DataRateLimiter(size_t max, double period)
: max_per_period_(max),
period_length_(period),
used_in_period_(0),
period_start_(0.0),
period_end_(period) {}
virtual ~DataRateLimiter() {}
// Returns true if if the desired quantity is available in the
// current period (< (max - used)). Once the given time passes the
// end of the period, used is set to zero and more use is available.
bool CanUse(size_t desired, double time);
// Increment the quantity used this period. If past the end of a
// period, a new period is started.
void Use(size_t used, double time);
size_t used_in_period() const { return used_in_period_; }
size_t max_per_period() const { return max_per_period_; }
private:
size_t max_per_period_;
double period_length_;
size_t used_in_period_;
double period_start_;
double period_end_;
};
} // namespace rtc
#endif // RTC_BASE_DATA_RATE_LIMITER_H_

View file

@ -0,0 +1,218 @@
/*
* Copyright 2015 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 "rtc_base/deprecated/recursive_critical_section.h"
#include <time.h>
#include "rtc_base/checks.h"
#include "rtc_base/platform_thread_types.h"
#include "rtc_base/synchronization/yield.h"
#include "rtc_base/system/unused.h"
#if RTC_DCHECK_IS_ON
#define RTC_CS_DEBUG_CODE(x) x
#else // !RTC_DCHECK_IS_ON
#define RTC_CS_DEBUG_CODE(x)
#endif // !RTC_DCHECK_IS_ON
namespace rtc {
RecursiveCriticalSection::RecursiveCriticalSection() {
#if defined(WEBRTC_WIN)
InitializeCriticalSection(&crit_);
#elif defined(WEBRTC_POSIX)
#if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
lock_queue_ = 0;
owning_thread_ = 0;
recursion_ = 0;
semaphore_ = dispatch_semaphore_create(0);
#else
pthread_mutexattr_t mutex_attribute;
pthread_mutexattr_init(&mutex_attribute);
pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
#if defined(WEBRTC_MAC)
pthread_mutexattr_setpolicy_np(&mutex_attribute,
_PTHREAD_MUTEX_POLICY_FIRSTFIT);
#endif
pthread_mutex_init(&mutex_, &mutex_attribute);
pthread_mutexattr_destroy(&mutex_attribute);
#endif
RTC_CS_DEBUG_CODE(thread_ = 0);
RTC_CS_DEBUG_CODE(recursion_count_ = 0);
RTC_UNUSED(thread_);
RTC_UNUSED(recursion_count_);
#else
#error Unsupported platform.
#endif
}
RecursiveCriticalSection::~RecursiveCriticalSection() {
#if defined(WEBRTC_WIN)
DeleteCriticalSection(&crit_);
#elif defined(WEBRTC_POSIX)
#if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
dispatch_release(semaphore_);
#else
pthread_mutex_destroy(&mutex_);
#endif
#else
#error Unsupported platform.
#endif
}
void RecursiveCriticalSection::Enter() const RTC_EXCLUSIVE_LOCK_FUNCTION() {
#if defined(WEBRTC_WIN)
EnterCriticalSection(&crit_);
#elif defined(WEBRTC_POSIX)
#if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
int spin = 3000;
PlatformThreadRef self = CurrentThreadRef();
bool have_lock = false;
do {
// Instead of calling TryEnter() in this loop, we do two interlocked
// operations, first a read-only one in order to avoid affecting the lock
// cache-line while spinning, in case another thread is using the lock.
if (!IsThreadRefEqual(owning_thread_, self)) {
if (AtomicOps::AcquireLoad(&lock_queue_) == 0) {
if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) == 0) {
have_lock = true;
break;
}
}
} else {
AtomicOps::Increment(&lock_queue_);
have_lock = true;
break;
}
sched_yield();
} while (--spin);
if (!have_lock && AtomicOps::Increment(&lock_queue_) > 1) {
// Owning thread cannot be the current thread since TryEnter() would
// have succeeded.
RTC_DCHECK(!IsThreadRefEqual(owning_thread_, self));
// Wait for the lock to become available.
dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
RTC_DCHECK(owning_thread_ == 0);
RTC_DCHECK(!recursion_);
}
owning_thread_ = self;
++recursion_;
#else
pthread_mutex_lock(&mutex_);
#endif
#if RTC_DCHECK_IS_ON
if (!recursion_count_) {
RTC_DCHECK(!thread_);
thread_ = CurrentThreadRef();
} else {
RTC_DCHECK(CurrentThreadIsOwner());
}
++recursion_count_;
#endif
#else
#error Unsupported platform.
#endif
}
bool RecursiveCriticalSection::TryEnter() const
RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
#if defined(WEBRTC_WIN)
return TryEnterCriticalSection(&crit_) != FALSE;
#elif defined(WEBRTC_POSIX)
#if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
if (!IsThreadRefEqual(owning_thread_, CurrentThreadRef())) {
if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) != 0)
return false;
owning_thread_ = CurrentThreadRef();
RTC_DCHECK(!recursion_);
} else {
AtomicOps::Increment(&lock_queue_);
}
++recursion_;
#else
if (pthread_mutex_trylock(&mutex_) != 0)
return false;
#endif
#if RTC_DCHECK_IS_ON
if (!recursion_count_) {
RTC_DCHECK(!thread_);
thread_ = CurrentThreadRef();
} else {
RTC_DCHECK(CurrentThreadIsOwner());
}
++recursion_count_;
#endif
return true;
#else
#error Unsupported platform.
#endif
}
void RecursiveCriticalSection::Leave() const RTC_UNLOCK_FUNCTION() {
RTC_DCHECK(CurrentThreadIsOwner());
#if defined(WEBRTC_WIN)
LeaveCriticalSection(&crit_);
#elif defined(WEBRTC_POSIX)
#if RTC_DCHECK_IS_ON
--recursion_count_;
RTC_DCHECK(recursion_count_ >= 0);
if (!recursion_count_)
thread_ = 0;
#endif
#if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
RTC_DCHECK(IsThreadRefEqual(owning_thread_, CurrentThreadRef()));
RTC_DCHECK_GE(recursion_, 0);
--recursion_;
if (!recursion_)
owning_thread_ = 0;
if (AtomicOps::Decrement(&lock_queue_) > 0 && !recursion_)
dispatch_semaphore_signal(semaphore_);
#else
pthread_mutex_unlock(&mutex_);
#endif
#else
#error Unsupported platform.
#endif
}
bool RecursiveCriticalSection::CurrentThreadIsOwner() const {
#if defined(WEBRTC_WIN)
// OwningThread has type HANDLE but actually contains the Thread ID:
// http://stackoverflow.com/questions/12675301/why-is-the-owningthread-member-of-critical-section-of-type-handle-when-it-is-de
// Converting through size_t avoids the VS 2015 warning C4312: conversion from
// 'type1' to 'type2' of greater size
return crit_.OwningThread ==
reinterpret_cast<HANDLE>(static_cast<size_t>(GetCurrentThreadId()));
#elif defined(WEBRTC_POSIX)
#if RTC_DCHECK_IS_ON
return IsThreadRefEqual(thread_, CurrentThreadRef());
#else
return true;
#endif // RTC_DCHECK_IS_ON
#else
#error Unsupported platform.
#endif
}
CritScope::CritScope(const RecursiveCriticalSection* cs) : cs_(cs) {
cs_->Enter();
}
CritScope::~CritScope() {
cs_->Leave();
}
} // namespace rtc

View file

@ -0,0 +1,107 @@
/*
* Copyright 2004 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.
*/
#ifndef RTC_BASE_DEPRECATED_RECURSIVE_CRITICAL_SECTION_H_
#define RTC_BASE_DEPRECATED_RECURSIVE_CRITICAL_SECTION_H_
#include <atomic>
#include "rtc_base/platform_thread_types.h"
#include "rtc_base/thread_annotations.h"
#if defined(WEBRTC_WIN)
// clang-format off
// clang formating would change include order.
// Include winsock2.h before including <windows.h> to maintain consistency with
// win32.h. To include win32.h directly, it must be broken out into its own
// build target.
#include <winsock2.h>
#include <windows.h>
#include <sal.h> // must come after windows headers.
// clang-format on
#endif // defined(WEBRTC_WIN)
#if defined(WEBRTC_POSIX)
#include <pthread.h>
#endif
// See notes in the 'Performance' unit test for the effects of this flag.
#define RTC_USE_NATIVE_MUTEX_ON_MAC 1
#if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
#include <dispatch/dispatch.h>
#endif
namespace rtc {
// NOTE: This class is deprecated. Please use webrtc::Mutex instead!
// Search using https://www.google.com/?q=recursive+lock+considered+harmful
// to find the reasons.
//
// Locking methods (Enter, TryEnter, Leave)are const to permit protecting
// members inside a const context without requiring mutable
// RecursiveCriticalSections everywhere. RecursiveCriticalSection is
// reentrant lock.
class RTC_LOCKABLE RecursiveCriticalSection {
public:
RecursiveCriticalSection();
~RecursiveCriticalSection();
void Enter() const RTC_EXCLUSIVE_LOCK_FUNCTION();
bool TryEnter() const RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true);
void Leave() const RTC_UNLOCK_FUNCTION();
private:
// Use only for RTC_DCHECKing.
bool CurrentThreadIsOwner() const;
#if defined(WEBRTC_WIN)
mutable CRITICAL_SECTION crit_;
#elif defined(WEBRTC_POSIX)
#if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
// Number of times the lock has been locked + number of threads waiting.
// TODO(tommi): We could use this number and subtract the recursion count
// to find places where we have multiple threads contending on the same lock.
mutable std::atomic<int> lock_queue_;
// `recursion_` represents the recursion count + 1 for the thread that owns
// the lock. Only modified by the thread that owns the lock.
mutable int recursion_;
// Used to signal a single waiting thread when the lock becomes available.
mutable dispatch_semaphore_t semaphore_;
// The thread that currently holds the lock. Required to handle recursion.
mutable PlatformThreadRef owning_thread_;
#else
mutable pthread_mutex_t mutex_;
#endif
mutable PlatformThreadRef thread_; // Only used by RTC_DCHECKs.
mutable int recursion_count_; // Only used by RTC_DCHECKs.
#else // !defined(WEBRTC_WIN) && !defined(WEBRTC_POSIX)
#error Unsupported platform.
#endif
};
// CritScope, for serializing execution through a scope.
class RTC_SCOPED_LOCKABLE CritScope {
public:
explicit CritScope(const RecursiveCriticalSection* cs)
RTC_EXCLUSIVE_LOCK_FUNCTION(cs);
~CritScope() RTC_UNLOCK_FUNCTION();
CritScope(const CritScope&) = delete;
CritScope& operator=(const CritScope&) = delete;
private:
const RecursiveCriticalSection* const cs_;
};
} // namespace rtc
#endif // RTC_BASE_DEPRECATED_RECURSIVE_CRITICAL_SECTION_H_

View file

@ -0,0 +1,45 @@
/*
* Copyright 2013 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.
*/
#ifndef RTC_BASE_DSCP_H_
#define RTC_BASE_DSCP_H_
namespace rtc {
// Differentiated Services Code Point.
// See http://tools.ietf.org/html/rfc2474 for details.
enum DiffServCodePoint {
DSCP_NO_CHANGE = -1,
DSCP_DEFAULT = 0, // Same as DSCP_CS0
DSCP_CS0 = 0, // The default
DSCP_CS1 = 8, // Bulk/background traffic
DSCP_AF11 = 10,
DSCP_AF12 = 12,
DSCP_AF13 = 14,
DSCP_CS2 = 16,
DSCP_AF21 = 18,
DSCP_AF22 = 20,
DSCP_AF23 = 22,
DSCP_CS3 = 24,
DSCP_AF31 = 26,
DSCP_AF32 = 28,
DSCP_AF33 = 30,
DSCP_CS4 = 32,
DSCP_AF41 = 34, // Video
DSCP_AF42 = 36, // Video
DSCP_AF43 = 38, // Video
DSCP_CS5 = 40, // Video
DSCP_EF = 46, // Voice
DSCP_CS6 = 48, // Voice
DSCP_CS7 = 56, // Control messages
};
} // namespace rtc
#endif // RTC_BASE_DSCP_H_

View file

@ -0,0 +1,210 @@
/*
* Copyright 2004 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 "rtc_base/event.h"
#if defined(WEBRTC_WIN)
#include <windows.h>
#elif defined(WEBRTC_POSIX)
#include <errno.h>
#include <pthread.h>
#include <sys/time.h>
#include <time.h>
#else
#error "Must define either WEBRTC_WIN or WEBRTC_POSIX."
#endif
#include "absl/types/optional.h"
#include "rtc_base/checks.h"
#include "rtc_base/synchronization/yield_policy.h"
#include "rtc_base/system/warn_current_thread_is_deadlocked.h"
#include "rtc_base/time_utils.h"
namespace rtc {
using ::webrtc::TimeDelta;
Event::Event() : Event(false, false) {}
#if defined(WEBRTC_WIN)
Event::Event(bool manual_reset, bool initially_signaled) {
event_handle_ = ::CreateEvent(nullptr, // Security attributes.
manual_reset, initially_signaled,
nullptr); // Name.
RTC_CHECK(event_handle_);
}
Event::~Event() {
CloseHandle(event_handle_);
}
void Event::Set() {
SetEvent(event_handle_);
}
void Event::Reset() {
ResetEvent(event_handle_);
}
bool Event::Wait(TimeDelta give_up_after, TimeDelta /*warn_after*/) {
ScopedYieldPolicy::YieldExecution();
const DWORD ms =
give_up_after.IsPlusInfinity()
? INFINITE
: give_up_after.RoundUpTo(webrtc::TimeDelta::Millis(1)).ms();
return (WaitForSingleObject(event_handle_, ms) == WAIT_OBJECT_0);
}
#elif defined(WEBRTC_POSIX)
// On MacOS, clock_gettime is available from version 10.12, and on
// iOS, from version 10.0. So we can't use it yet.
#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
#define USE_CLOCK_GETTIME 0
#define USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP 0
// On Android, pthread_condattr_setclock is available from version 21. By
// default, we target a new enough version for 64-bit platforms but not for
// 32-bit platforms. For older versions, use
// pthread_cond_timedwait_monotonic_np.
#elif defined(WEBRTC_ANDROID) && (__ANDROID_API__ < 21)
#define USE_CLOCK_GETTIME 1
#define USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP 1
#else
#define USE_CLOCK_GETTIME 1
#define USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP 0
#endif
Event::Event(bool manual_reset, bool initially_signaled)
: is_manual_reset_(manual_reset), event_status_(initially_signaled) {
RTC_CHECK(pthread_mutex_init(&event_mutex_, nullptr) == 0);
pthread_condattr_t cond_attr;
RTC_CHECK(pthread_condattr_init(&cond_attr) == 0);
#if USE_CLOCK_GETTIME && !USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP
RTC_CHECK(pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC) == 0);
#endif
RTC_CHECK(pthread_cond_init(&event_cond_, &cond_attr) == 0);
pthread_condattr_destroy(&cond_attr);
}
Event::~Event() {
pthread_mutex_destroy(&event_mutex_);
pthread_cond_destroy(&event_cond_);
}
void Event::Set() {
pthread_mutex_lock(&event_mutex_);
event_status_ = true;
pthread_cond_broadcast(&event_cond_);
pthread_mutex_unlock(&event_mutex_);
}
void Event::Reset() {
pthread_mutex_lock(&event_mutex_);
event_status_ = false;
pthread_mutex_unlock(&event_mutex_);
}
namespace {
timespec GetTimespec(TimeDelta duration_from_now) {
timespec ts;
// Get the current time.
#if USE_CLOCK_GETTIME
clock_gettime(CLOCK_MONOTONIC, &ts);
#else
timeval tv;
gettimeofday(&tv, nullptr);
ts.tv_sec = tv.tv_sec;
ts.tv_nsec = tv.tv_usec * kNumNanosecsPerMicrosec;
#endif
// Add the specified number of milliseconds to it.
int64_t microsecs_from_now = duration_from_now.us();
ts.tv_sec += microsecs_from_now / kNumMicrosecsPerSec;
ts.tv_nsec +=
(microsecs_from_now % kNumMicrosecsPerSec) * kNumNanosecsPerMicrosec;
// Normalize.
if (ts.tv_nsec >= kNumNanosecsPerSec) {
ts.tv_sec++;
ts.tv_nsec -= kNumNanosecsPerSec;
}
return ts;
}
} // namespace
bool Event::Wait(TimeDelta give_up_after, TimeDelta warn_after) {
// Instant when we'll log a warning message (because we've been waiting so
// long it might be a bug), but not yet give up waiting. nullopt if we
// shouldn't log a warning.
const absl::optional<timespec> warn_ts =
warn_after >= give_up_after
? absl::nullopt
: absl::make_optional(GetTimespec(warn_after));
// Instant when we'll stop waiting and return an error. nullopt if we should
// never give up.
const absl::optional<timespec> give_up_ts =
give_up_after.IsPlusInfinity()
? absl::nullopt
: absl::make_optional(GetTimespec(give_up_after));
ScopedYieldPolicy::YieldExecution();
pthread_mutex_lock(&event_mutex_);
// Wait for `event_cond_` to trigger and `event_status_` to be set, with the
// given timeout (or without a timeout if none is given).
const auto wait = [&](const absl::optional<timespec> timeout_ts) {
int error = 0;
while (!event_status_ && error == 0) {
if (timeout_ts == absl::nullopt) {
error = pthread_cond_wait(&event_cond_, &event_mutex_);
} else {
#if USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP
error = pthread_cond_timedwait_monotonic_np(&event_cond_, &event_mutex_,
&*timeout_ts);
#else
error =
pthread_cond_timedwait(&event_cond_, &event_mutex_, &*timeout_ts);
#endif
}
}
return error;
};
int error;
if (warn_ts == absl::nullopt) {
error = wait(give_up_ts);
} else {
error = wait(warn_ts);
if (error == ETIMEDOUT) {
webrtc::WarnThatTheCurrentThreadIsProbablyDeadlocked();
error = wait(give_up_ts);
}
}
// NOTE(liulk): Exactly one thread will auto-reset this event. All
// the other threads will think it's unsignaled. This seems to be
// consistent with auto-reset events in WEBRTC_WIN
if (error == 0 && !is_manual_reset_)
event_status_ = false;
pthread_mutex_unlock(&event_mutex_);
return (error == 0);
}
#endif
} // namespace rtc

View file

@ -0,0 +1,137 @@
/*
* Copyright 2004 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.
*/
#ifndef RTC_BASE_EVENT_H_
#define RTC_BASE_EVENT_H_
#include "api/units/time_delta.h"
#if defined(WEBRTC_WIN)
#include <windows.h>
#elif defined(WEBRTC_POSIX)
#include <pthread.h>
#else
#error "Must define either WEBRTC_WIN or WEBRTC_POSIX."
#endif
#include "rtc_base/synchronization/yield_policy.h"
namespace rtc {
// RTC_DISALLOW_WAIT() utility
//
// Sets a stack-scoped flag that disallows use of `rtc::Event::Wait` by means
// of raising a DCHECK when a call to `rtc::Event::Wait()` is made..
// This is useful to guard synchronization-free scopes against regressions.
//
// Example of what this would catch (`ScopeToProtect` calls `Foo`):
//
// void Foo(TaskQueue* tq) {
// Event event;
// tq->PostTask([&event]() {
// event.Set();
// });
// event.Wait(Event::kForever); // <- Will trigger a DCHECK.
// }
//
// void ScopeToProtect() {
// TaskQueue* tq = GetSomeTaskQueue();
// RTC_DISALLOW_WAIT(); // Policy takes effect.
// Foo(tq);
// }
//
#if RTC_DCHECK_IS_ON
#define RTC_DISALLOW_WAIT() ScopedDisallowWait disallow_wait_##__LINE__
#else
#define RTC_DISALLOW_WAIT()
#endif
class Event {
public:
// TODO(bugs.webrtc.org/14366): Consider removing this redundant alias.
static constexpr webrtc::TimeDelta kForever =
webrtc::TimeDelta::PlusInfinity();
Event();
Event(bool manual_reset, bool initially_signaled);
Event(const Event&) = delete;
Event& operator=(const Event&) = delete;
~Event();
void Set();
void Reset();
// Waits for the event to become signaled, but logs a warning if it takes more
// than `warn_after`, and gives up completely if it takes more than
// `give_up_after`. (If `warn_after >= give_up_after`, no warning will be
// logged.) Either or both may be `kForever`, which means wait indefinitely.
//
// Care is taken so that the underlying OS wait call isn't requested to sleep
// shorter than `give_up_after`.
//
// Returns true if the event was signaled, false if there was a timeout or
// some other error.
bool Wait(webrtc::TimeDelta give_up_after, webrtc::TimeDelta warn_after);
// Waits with the given timeout and a reasonable default warning timeout.
bool Wait(webrtc::TimeDelta give_up_after) {
return Wait(give_up_after, give_up_after.IsPlusInfinity()
? webrtc::TimeDelta::Seconds(3)
: kForever);
}
private:
#if defined(WEBRTC_WIN)
HANDLE event_handle_;
#elif defined(WEBRTC_POSIX)
pthread_mutex_t event_mutex_;
pthread_cond_t event_cond_;
const bool is_manual_reset_;
bool event_status_;
#endif
};
// These classes are provided for compatibility with Chromium.
// The rtc::Event implementation is overriden inside of Chromium for the
// purposes of detecting when threads are blocked that shouldn't be as well as
// to use the more accurate event implementation that's there than is provided
// by default on some platforms (e.g. Windows).
// When building with standalone WebRTC, this class is a noop.
// For further information, please see the
// ScopedAllowBaseSyncPrimitives(ForTesting) classes in Chromium.
class ScopedAllowBaseSyncPrimitives {
public:
ScopedAllowBaseSyncPrimitives() {}
~ScopedAllowBaseSyncPrimitives() {}
};
class ScopedAllowBaseSyncPrimitivesForTesting {
public:
ScopedAllowBaseSyncPrimitivesForTesting() {}
~ScopedAllowBaseSyncPrimitivesForTesting() {}
};
#if RTC_DCHECK_IS_ON
class ScopedDisallowWait {
public:
ScopedDisallowWait() = default;
private:
class DisallowYieldHandler : public YieldInterface {
public:
void YieldExecution() override { RTC_DCHECK_NOTREACHED(); }
} handler_;
rtc::ScopedYieldPolicy policy{&handler_};
};
#endif
} // namespace rtc
#endif // RTC_BASE_EVENT_H_

View file

@ -0,0 +1,412 @@
/*
* Copyright (c) 2012 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 "rtc_base/event_tracer.h"
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <atomic>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "api/sequence_checker.h"
#include "rtc_base/checks.h"
#include "rtc_base/event.h"
#include "rtc_base/logging.h"
#include "rtc_base/platform_thread.h"
#include "rtc_base/platform_thread_types.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h"
#include "rtc_base/time_utils.h"
#include "rtc_base/trace_event.h"
// This is a guesstimate that should be enough in most cases.
static const size_t kEventLoggerArgsStrBufferInitialSize = 256;
static const size_t kTraceArgBufferLength = 32;
namespace webrtc {
namespace {
GetCategoryEnabledPtr g_get_category_enabled_ptr = nullptr;
AddTraceEventPtr g_add_trace_event_ptr = nullptr;
} // namespace
void SetupEventTracer(GetCategoryEnabledPtr get_category_enabled_ptr,
AddTraceEventPtr add_trace_event_ptr) {
g_get_category_enabled_ptr = get_category_enabled_ptr;
g_add_trace_event_ptr = add_trace_event_ptr;
}
const unsigned char* EventTracer::GetCategoryEnabled(const char* name) {
if (g_get_category_enabled_ptr)
return g_get_category_enabled_ptr(name);
// A string with null terminator means category is disabled.
return reinterpret_cast<const unsigned char*>("\0");
}
// Arguments to this function (phase, etc.) are as defined in
// webrtc/rtc_base/trace_event.h.
void EventTracer::AddTraceEvent(char phase,
const unsigned char* category_enabled,
const char* name,
unsigned long long id,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
unsigned char flags) {
if (g_add_trace_event_ptr) {
g_add_trace_event_ptr(phase, category_enabled, name, id, num_args,
arg_names, arg_types, arg_values, flags);
}
}
} // namespace webrtc
namespace rtc {
namespace tracing {
namespace {
// Atomic-int fast path for avoiding logging when disabled.
static std::atomic<int> g_event_logging_active(0);
// TODO(pbos): Log metadata for all threads, etc.
class EventLogger final {
public:
~EventLogger() { RTC_DCHECK(thread_checker_.IsCurrent()); }
void AddTraceEvent(const char* name,
const unsigned char* category_enabled,
char phase,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
uint64_t timestamp,
int pid,
rtc::PlatformThreadId thread_id) {
std::vector<TraceArg> args(num_args);
for (int i = 0; i < num_args; ++i) {
TraceArg& arg = args[i];
arg.name = arg_names[i];
arg.type = arg_types[i];
arg.value.as_uint = arg_values[i];
// Value is a pointer to a temporary string, so we have to make a copy.
if (arg.type == TRACE_VALUE_TYPE_COPY_STRING) {
// Space for the string and for the terminating null character.
size_t str_length = strlen(arg.value.as_string) + 1;
char* str_copy = new char[str_length];
memcpy(str_copy, arg.value.as_string, str_length);
arg.value.as_string = str_copy;
}
}
webrtc::MutexLock lock(&mutex_);
trace_events_.push_back(
{name, category_enabled, phase, args, timestamp, 1, thread_id});
}
// The TraceEvent format is documented here:
// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
void Log() {
RTC_DCHECK(output_file_);
static constexpr webrtc::TimeDelta kLoggingInterval =
webrtc::TimeDelta::Millis(100);
fprintf(output_file_, "{ \"traceEvents\": [\n");
bool has_logged_event = false;
while (true) {
bool shutting_down = shutdown_event_.Wait(kLoggingInterval);
std::vector<TraceEvent> events;
{
webrtc::MutexLock lock(&mutex_);
trace_events_.swap(events);
}
std::string args_str;
args_str.reserve(kEventLoggerArgsStrBufferInitialSize);
for (TraceEvent& e : events) {
args_str.clear();
if (!e.args.empty()) {
args_str += ", \"args\": {";
bool is_first_argument = true;
for (TraceArg& arg : e.args) {
if (!is_first_argument)
args_str += ",";
is_first_argument = false;
args_str += " \"";
args_str += arg.name;
args_str += "\": ";
args_str += TraceArgValueAsString(arg);
// Delete our copy of the string.
if (arg.type == TRACE_VALUE_TYPE_COPY_STRING) {
delete[] arg.value.as_string;
arg.value.as_string = nullptr;
}
}
args_str += " }";
}
fprintf(output_file_,
"%s{ \"name\": \"%s\""
", \"cat\": \"%s\""
", \"ph\": \"%c\""
", \"ts\": %" PRIu64
", \"pid\": %d"
#if defined(WEBRTC_WIN)
", \"tid\": %lu"
#else
", \"tid\": %d"
#endif // defined(WEBRTC_WIN)
"%s"
"}\n",
has_logged_event ? "," : " ", e.name, e.category_enabled,
e.phase, e.timestamp, e.pid, e.tid, args_str.c_str());
has_logged_event = true;
}
if (shutting_down)
break;
}
fprintf(output_file_, "]}\n");
if (output_file_owned_)
fclose(output_file_);
output_file_ = nullptr;
}
void Start(FILE* file, bool owned) {
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_DCHECK(file);
RTC_DCHECK(!output_file_);
output_file_ = file;
output_file_owned_ = owned;
{
webrtc::MutexLock lock(&mutex_);
// Since the atomic fast-path for adding events to the queue can be
// bypassed while the logging thread is shutting down there may be some
// stale events in the queue, hence the vector needs to be cleared to not
// log events from a previous logging session (which may be days old).
trace_events_.clear();
}
// Enable event logging (fast-path). This should be disabled since starting
// shouldn't be done twice.
int zero = 0;
RTC_CHECK(g_event_logging_active.compare_exchange_strong(zero, 1));
// Finally start, everything should be set up now.
logging_thread_ =
PlatformThread::SpawnJoinable([this] { Log(); }, "EventTracingThread");
TRACE_EVENT_INSTANT0("webrtc", "EventLogger::Start");
}
void Stop() {
RTC_DCHECK(thread_checker_.IsCurrent());
TRACE_EVENT_INSTANT0("webrtc", "EventLogger::Stop");
// Try to stop. Abort if we're not currently logging.
int one = 1;
if (g_event_logging_active.compare_exchange_strong(one, 0))
return;
// Wake up logging thread to finish writing.
shutdown_event_.Set();
// Join the logging thread.
logging_thread_.Finalize();
}
private:
struct TraceArg {
const char* name;
unsigned char type;
// Copied from webrtc/rtc_base/trace_event.h TraceValueUnion.
union TraceArgValue {
bool as_bool;
unsigned long long as_uint;
long long as_int;
double as_double;
const void* as_pointer;
const char* as_string;
} value;
// Assert that the size of the union is equal to the size of the as_uint
// field since we are assigning to arbitrary types using it.
static_assert(sizeof(TraceArgValue) == sizeof(unsigned long long),
"Size of TraceArg value union is not equal to the size of "
"the uint field of that union.");
};
struct TraceEvent {
const char* name;
const unsigned char* category_enabled;
char phase;
std::vector<TraceArg> args;
uint64_t timestamp;
int pid;
rtc::PlatformThreadId tid;
};
static std::string TraceArgValueAsString(TraceArg arg) {
std::string output;
if (arg.type == TRACE_VALUE_TYPE_STRING ||
arg.type == TRACE_VALUE_TYPE_COPY_STRING) {
// Space for every character to be an espaced character + two for
// quatation marks.
output.reserve(strlen(arg.value.as_string) * 2 + 2);
output += '\"';
const char* c = arg.value.as_string;
do {
if (*c == '"' || *c == '\\') {
output += '\\';
output += *c;
} else {
output += *c;
}
} while (*++c);
output += '\"';
} else {
output.resize(kTraceArgBufferLength);
size_t print_length = 0;
switch (arg.type) {
case TRACE_VALUE_TYPE_BOOL:
if (arg.value.as_bool) {
strcpy(&output[0], "true");
print_length = 4;
} else {
strcpy(&output[0], "false");
print_length = 5;
}
break;
case TRACE_VALUE_TYPE_UINT:
print_length = snprintf(&output[0], kTraceArgBufferLength, "%llu",
arg.value.as_uint);
break;
case TRACE_VALUE_TYPE_INT:
print_length = snprintf(&output[0], kTraceArgBufferLength, "%lld",
arg.value.as_int);
break;
case TRACE_VALUE_TYPE_DOUBLE:
print_length = snprintf(&output[0], kTraceArgBufferLength, "%f",
arg.value.as_double);
break;
case TRACE_VALUE_TYPE_POINTER:
print_length = snprintf(&output[0], kTraceArgBufferLength, "\"%p\"",
arg.value.as_pointer);
break;
}
size_t output_length = print_length < kTraceArgBufferLength
? print_length
: kTraceArgBufferLength - 1;
// This will hopefully be very close to nop. On most implementations, it
// just writes null byte and sets the length field of the string.
output.resize(output_length);
}
return output;
}
webrtc::Mutex mutex_;
std::vector<TraceEvent> trace_events_ RTC_GUARDED_BY(mutex_);
rtc::PlatformThread logging_thread_;
rtc::Event shutdown_event_;
webrtc::SequenceChecker thread_checker_;
FILE* output_file_ = nullptr;
bool output_file_owned_ = false;
};
static std::atomic<EventLogger*> g_event_logger(nullptr);
static const char* const kDisabledTracePrefix = TRACE_DISABLED_BY_DEFAULT("");
const unsigned char* InternalGetCategoryEnabled(const char* name) {
const char* prefix_ptr = &kDisabledTracePrefix[0];
const char* name_ptr = name;
// Check whether name contains the default-disabled prefix.
while (*prefix_ptr == *name_ptr && *prefix_ptr != '\0') {
++prefix_ptr;
++name_ptr;
}
return reinterpret_cast<const unsigned char*>(*prefix_ptr == '\0' ? ""
: name);
}
const unsigned char* InternalEnableAllCategories(const char* name) {
return reinterpret_cast<const unsigned char*>(name);
}
void InternalAddTraceEvent(char phase,
const unsigned char* category_enabled,
const char* name,
unsigned long long id,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
unsigned char flags) {
// Fast path for when event tracing is inactive.
if (g_event_logging_active.load() == 0)
return;
g_event_logger.load()->AddTraceEvent(
name, category_enabled, phase, num_args, arg_names, arg_types, arg_values,
rtc::TimeMicros(), 1, rtc::CurrentThreadId());
}
} // namespace
void SetupInternalTracer(bool enable_all_categories) {
EventLogger* null_logger = nullptr;
RTC_CHECK(
g_event_logger.compare_exchange_strong(null_logger, new EventLogger()));
webrtc::SetupEventTracer(enable_all_categories ? InternalEnableAllCategories
: InternalGetCategoryEnabled,
InternalAddTraceEvent);
}
void StartInternalCaptureToFile(FILE* file) {
EventLogger* event_logger = g_event_logger.load();
if (event_logger) {
event_logger->Start(file, false);
}
}
bool StartInternalCapture(absl::string_view filename) {
EventLogger* event_logger = g_event_logger.load();
if (!event_logger)
return false;
FILE* file = fopen(std::string(filename).c_str(), "w");
if (!file) {
RTC_LOG(LS_ERROR) << "Failed to open trace file '" << filename
<< "' for writing.";
return false;
}
event_logger->Start(file, true);
return true;
}
void StopInternalCapture() {
EventLogger* event_logger = g_event_logger.load();
if (event_logger) {
event_logger->Stop();
}
}
void ShutdownInternalTracer() {
StopInternalCapture();
EventLogger* old_logger = g_event_logger.load(std::memory_order_acquire);
RTC_DCHECK(old_logger);
RTC_CHECK(g_event_logger.compare_exchange_strong(old_logger, nullptr));
delete old_logger;
webrtc::SetupEventTracer(nullptr, nullptr);
}
} // namespace tracing
} // namespace rtc

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2012 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.
*/
// This file defines the interface for event tracing in WebRTC.
//
// Event log handlers are set through SetupEventTracer(). User of this API will
// provide two function pointers to handle event tracing calls.
//
// * GetCategoryEnabledPtr
// Event tracing system calls this function to determine if a particular
// event category is enabled.
//
// * AddTraceEventPtr
// Adds a tracing event. It is the user's responsibility to log the data
// provided.
//
// Parameters for the above two functions are described in trace_event.h.
#ifndef RTC_BASE_EVENT_TRACER_H_
#define RTC_BASE_EVENT_TRACER_H_
#include <stdio.h>
#include "absl/strings/string_view.h"
#include "rtc_base/system/rtc_export.h"
namespace webrtc {
typedef const unsigned char* (*GetCategoryEnabledPtr)(const char* name);
typedef void (*AddTraceEventPtr)(char phase,
const unsigned char* category_enabled,
const char* name,
unsigned long long id,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
unsigned char flags);
// User of WebRTC can call this method to setup event tracing.
//
// This method must be called before any WebRTC methods. Functions
// provided should be thread-safe.
void SetupEventTracer(GetCategoryEnabledPtr get_category_enabled_ptr,
AddTraceEventPtr add_trace_event_ptr);
// This class defines interface for the event tracing system to call
// internally. Do not call these methods directly.
class EventTracer {
public:
static const unsigned char* GetCategoryEnabled(const char* name);
static void AddTraceEvent(char phase,
const unsigned char* category_enabled,
const char* name,
unsigned long long id,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
unsigned char flags);
};
} // namespace webrtc
namespace rtc {
namespace tracing {
// Set up internal event tracer.
RTC_EXPORT void SetupInternalTracer(bool enable_all_categories = true);
RTC_EXPORT bool StartInternalCapture(absl::string_view filename);
RTC_EXPORT void StartInternalCaptureToFile(FILE* file);
RTC_EXPORT void StopInternalCapture();
// Make sure we run this, this will tear down the internal tracing.
RTC_EXPORT void ShutdownInternalTracer();
} // namespace tracing
} // namespace rtc
#endif // RTC_BASE_EVENT_TRACER_H_

View file

@ -0,0 +1,115 @@
/*
* Copyright 2004 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 "rtc_base/event.h"
#include "api/units/time_delta.h"
#include "rtc_base/platform_thread.h"
#include "system_wrappers/include/clock.h"
#include "test/gtest.h"
namespace rtc {
TEST(EventTest, InitiallySignaled) {
Event event(false, true);
ASSERT_TRUE(event.Wait(webrtc::TimeDelta::Zero()));
}
TEST(EventTest, ManualReset) {
Event event(true, false);
ASSERT_FALSE(event.Wait(webrtc::TimeDelta::Zero()));
event.Set();
ASSERT_TRUE(event.Wait(webrtc::TimeDelta::Zero()));
ASSERT_TRUE(event.Wait(webrtc::TimeDelta::Zero()));
event.Reset();
ASSERT_FALSE(event.Wait(webrtc::TimeDelta::Zero()));
}
TEST(EventTest, AutoReset) {
Event event;
ASSERT_FALSE(event.Wait(webrtc::TimeDelta::Zero()));
event.Set();
ASSERT_TRUE(event.Wait(webrtc::TimeDelta::Zero()));
ASSERT_FALSE(event.Wait(webrtc::TimeDelta::Zero()));
}
class SignalerThread {
public:
void Start(Event* writer, Event* reader) {
writer_ = writer;
reader_ = reader;
thread_ = PlatformThread::SpawnJoinable(
[this] {
while (!stop_event_.Wait(webrtc::TimeDelta::Zero())) {
writer_->Set();
reader_->Wait(Event::kForever);
}
},
"EventPerf");
}
void Stop() {
stop_event_.Set();
thread_.Finalize();
}
Event stop_event_;
Event* writer_;
Event* reader_;
PlatformThread thread_;
};
TEST(EventTest, UnsignaledWaitDoesNotReturnBeforeTimeout) {
constexpr webrtc::TimeDelta kDuration = webrtc::TimeDelta::Micros(10'499);
Event event;
auto begin = webrtc::Clock::GetRealTimeClock()->CurrentTime();
EXPECT_FALSE(event.Wait(kDuration));
EXPECT_GE(webrtc::Clock::GetRealTimeClock()->CurrentTime(),
begin + kDuration);
}
// These tests are disabled by default and only intended to be run manually.
TEST(EventTest, DISABLED_PerformanceSingleThread) {
static const int kNumIterations = 10000000;
Event event;
for (int i = 0; i < kNumIterations; ++i) {
event.Set();
event.Wait(webrtc::TimeDelta::Zero());
}
}
TEST(EventTest, DISABLED_PerformanceMultiThread) {
static const int kNumIterations = 10000;
Event read;
Event write;
SignalerThread thread;
thread.Start(&read, &write);
for (int i = 0; i < kNumIterations; ++i) {
write.Set();
read.Wait(Event::kForever);
}
write.Set();
thread.Stop();
}
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
// Tests that we crash if we attempt to call rtc::Event::Wait while we're
// not allowed to (as per `RTC_DISALLOW_WAIT()`).
TEST(EventTestDeathTest, DisallowEventWait) {
Event event;
RTC_DISALLOW_WAIT();
EXPECT_DEATH(event.Wait(Event::kForever), "");
}
#endif // RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
} // namespace rtc

View file

@ -0,0 +1,13 @@
asapersson@webrtc.org
sprang@webrtc.org
srte@webrtc.org
per-file audio_allocation_settings*=srte@webrtc.org
per-file congestion_controller_experiment*=srte@webrtc.org
per-file cpu_speed_experiment*=asapersson@webrtc.org
per-file field_trial*=srte@webrtc.org
per-file keyframe_interval_settings*=brandtr@webrtc.org
per-file normalize_simulcast_size_experiment*=asapersson@webrtc.org
per-file quality_scaling_experiment*=asapersson@webrtc.org
per-file rtt_mult_experiment*=mhoro@webrtc.org
per-file rate_control_settings*=srte@webrtc.org

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2018 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 "rtc_base/experiments/alr_experiment.h"
#include <inttypes.h>
#include <stdio.h>
#include <string>
#include "absl/strings/string_view.h"
#include "api/field_trials_view.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
constexpr absl::string_view kDefaultProbingScreenshareBweSettings =
"1.0,2875,80,40,-60,3";
} // namespace
bool AlrExperimentSettings::MaxOneFieldTrialEnabled(
const FieldTrialsView& key_value_config) {
return key_value_config.Lookup(kStrictPacingAndProbingExperimentName)
.empty() ||
key_value_config.Lookup(kScreenshareProbingBweExperimentName).empty();
}
absl::optional<AlrExperimentSettings>
AlrExperimentSettings::CreateFromFieldTrial(
const FieldTrialsView& key_value_config,
absl::string_view experiment_name) {
absl::optional<AlrExperimentSettings> ret;
std::string group_name = key_value_config.Lookup(experiment_name);
const std::string kIgnoredSuffix = "_Dogfood";
std::string::size_type suffix_pos = group_name.rfind(kIgnoredSuffix);
if (suffix_pos != std::string::npos &&
suffix_pos == group_name.length() - kIgnoredSuffix.length()) {
group_name.resize(group_name.length() - kIgnoredSuffix.length());
}
if (group_name.empty()) {
if (experiment_name == kScreenshareProbingBweExperimentName) {
// This experiment is now default-on with fixed settings.
// TODO(sprang): Remove this kill-switch and clean up experiment code.
group_name = kDefaultProbingScreenshareBweSettings;
} else {
return ret;
}
}
AlrExperimentSettings settings;
if (sscanf(group_name.c_str(), "%f,%" PRId64 ",%d,%d,%d,%d",
&settings.pacing_factor, &settings.max_paced_queue_time,
&settings.alr_bandwidth_usage_percent,
&settings.alr_start_budget_level_percent,
&settings.alr_stop_budget_level_percent,
&settings.group_id) == 6) {
ret.emplace(settings);
RTC_LOG(LS_INFO) << "Using ALR experiment settings: "
"pacing factor: "
<< settings.pacing_factor << ", max pacer queue length: "
<< settings.max_paced_queue_time
<< ", ALR bandwidth usage percent: "
<< settings.alr_bandwidth_usage_percent
<< ", ALR start budget level percent: "
<< settings.alr_start_budget_level_percent
<< ", ALR end budget level percent: "
<< settings.alr_stop_budget_level_percent
<< ", ALR experiment group ID: " << settings.group_id;
} else {
RTC_LOG(LS_INFO) << "Failed to parse ALR experiment: " << experiment_name;
}
return ret;
}
} // namespace webrtc

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_ALR_EXPERIMENT_H_
#define RTC_BASE_EXPERIMENTS_ALR_EXPERIMENT_H_
#include <stdint.h>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
namespace webrtc {
struct AlrExperimentSettings {
public:
float pacing_factor;
int64_t max_paced_queue_time;
int alr_bandwidth_usage_percent;
int alr_start_budget_level_percent;
int alr_stop_budget_level_percent;
// Will be sent to the receive side for stats slicing.
// Can be 0..6, because it's sent as a 3 bits value and there's also
// reserved value to indicate absence of experiment.
int group_id;
static constexpr absl::string_view kScreenshareProbingBweExperimentName =
"WebRTC-ProbingScreenshareBwe";
static constexpr absl::string_view kStrictPacingAndProbingExperimentName =
"WebRTC-StrictPacingAndProbing";
static absl::optional<AlrExperimentSettings> CreateFromFieldTrial(
const FieldTrialsView& key_value_config,
absl::string_view experiment_name);
static bool MaxOneFieldTrialEnabled(const FieldTrialsView& key_value_config);
private:
AlrExperimentSettings() = default;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_ALR_EXPERIMENT_H_

View file

@ -0,0 +1,487 @@
/*
* Copyright 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 "rtc_base/experiments/balanced_degradation_settings.h"
#include <limits>
#include "rtc_base/experiments/field_trial_list.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
constexpr char kFieldTrial[] = "WebRTC-Video-BalancedDegradationSettings";
constexpr int kMinFps = 1;
constexpr int kMaxFps = 100; // 100 means unlimited fps.
std::vector<BalancedDegradationSettings::Config> DefaultConfigs() {
return {{320 * 240,
7,
0,
0,
BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0}},
{480 * 360,
10,
0,
0,
1,
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0}},
{640 * 480,
15,
0,
0,
1,
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0}}};
}
bool IsValidConfig(
const BalancedDegradationSettings::CodecTypeSpecific& config) {
if (config.GetQpLow().has_value() != config.GetQpHigh().has_value()) {
RTC_LOG(LS_WARNING) << "Neither or both thresholds should be set.";
return false;
}
if (config.GetQpLow().has_value() && config.GetQpHigh().has_value() &&
config.GetQpLow().value() >= config.GetQpHigh().value()) {
RTC_LOG(LS_WARNING) << "Invalid threshold value, low >= high threshold.";
return false;
}
if (config.GetFps().has_value() && (config.GetFps().value() < kMinFps ||
config.GetFps().value() > kMaxFps)) {
RTC_LOG(LS_WARNING) << "Unsupported fps setting, value ignored.";
return false;
}
return true;
}
bool IsValid(const BalancedDegradationSettings::CodecTypeSpecific& config1,
const BalancedDegradationSettings::CodecTypeSpecific& config2) {
bool both_or_none_set = ((config1.qp_low > 0) == (config2.qp_low > 0) &&
(config1.qp_high > 0) == (config2.qp_high > 0) &&
(config1.fps > 0) == (config2.fps > 0));
if (!both_or_none_set) {
RTC_LOG(LS_WARNING) << "Invalid value, all/none should be set.";
return false;
}
if (config1.fps > 0 && config1.fps < config2.fps) {
RTC_LOG(LS_WARNING) << "Invalid fps/pixel value provided.";
return false;
}
return true;
}
bool IsValid(const std::vector<BalancedDegradationSettings::Config>& configs) {
if (configs.size() <= 1) {
if (configs.size() == 1)
RTC_LOG(LS_WARNING) << "Unsupported size, value ignored.";
return false;
}
for (const auto& config : configs) {
if (config.fps < kMinFps || config.fps > kMaxFps) {
RTC_LOG(LS_WARNING) << "Unsupported fps setting, value ignored.";
return false;
}
}
int last_kbps = configs[0].kbps;
for (size_t i = 1; i < configs.size(); ++i) {
if (configs[i].kbps > 0) {
if (configs[i].kbps < last_kbps) {
RTC_LOG(LS_WARNING) << "Invalid bitrate value provided.";
return false;
}
last_kbps = configs[i].kbps;
}
}
for (size_t i = 1; i < configs.size(); ++i) {
if (configs[i].pixels < configs[i - 1].pixels ||
configs[i].fps < configs[i - 1].fps) {
RTC_LOG(LS_WARNING) << "Invalid fps/pixel value provided.";
return false;
}
if (!IsValid(configs[i].vp8, configs[i - 1].vp8) ||
!IsValid(configs[i].vp9, configs[i - 1].vp9) ||
!IsValid(configs[i].h264, configs[i - 1].h264) ||
!IsValid(configs[i].av1, configs[i - 1].av1) ||
!IsValid(configs[i].generic, configs[i - 1].generic)) {
return false;
}
}
for (const auto& config : configs) {
if (!IsValidConfig(config.vp8) || !IsValidConfig(config.vp9) ||
!IsValidConfig(config.h264) || !IsValidConfig(config.av1) ||
!IsValidConfig(config.generic)) {
return false;
}
}
return true;
}
std::vector<BalancedDegradationSettings::Config> GetValidOrDefault(
const std::vector<BalancedDegradationSettings::Config>& configs) {
if (IsValid(configs)) {
return configs;
}
return DefaultConfigs();
}
absl::optional<VideoEncoder::QpThresholds> GetThresholds(
VideoCodecType type,
const BalancedDegradationSettings::Config& config) {
absl::optional<int> low;
absl::optional<int> high;
switch (type) {
case kVideoCodecVP8:
low = config.vp8.GetQpLow();
high = config.vp8.GetQpHigh();
break;
case kVideoCodecVP9:
low = config.vp9.GetQpLow();
high = config.vp9.GetQpHigh();
break;
case kVideoCodecH265:
// TODO(bugs.webrtc.org/13485): Use H264 QP thresholds for now.
case kVideoCodecH264:
low = config.h264.GetQpLow();
high = config.h264.GetQpHigh();
break;
case kVideoCodecAV1:
low = config.av1.GetQpLow();
high = config.av1.GetQpHigh();
break;
case kVideoCodecGeneric:
low = config.generic.GetQpLow();
high = config.generic.GetQpHigh();
break;
default:
break;
}
if (low && high) {
RTC_LOG(LS_INFO) << "QP thresholds: low: " << *low << ", high: " << *high;
return absl::optional<VideoEncoder::QpThresholds>(
VideoEncoder::QpThresholds(*low, *high));
}
return absl::nullopt;
}
int GetFps(VideoCodecType type,
const absl::optional<BalancedDegradationSettings::Config>& config) {
if (!config.has_value()) {
return std::numeric_limits<int>::max();
}
absl::optional<int> fps;
switch (type) {
case kVideoCodecVP8:
fps = config->vp8.GetFps();
break;
case kVideoCodecH265:
// TODO(bugs.webrtc.org/13485): Use VP9 bitrate limits for now.
case kVideoCodecVP9:
fps = config->vp9.GetFps();
break;
case kVideoCodecH264:
fps = config->h264.GetFps();
break;
case kVideoCodecAV1:
fps = config->av1.GetFps();
break;
case kVideoCodecGeneric:
fps = config->generic.GetFps();
break;
default:
break;
}
const int framerate = fps.value_or(config->fps);
return (framerate == kMaxFps) ? std::numeric_limits<int>::max() : framerate;
}
absl::optional<int> GetKbps(
VideoCodecType type,
const absl::optional<BalancedDegradationSettings::Config>& config) {
if (!config.has_value())
return absl::nullopt;
absl::optional<int> kbps;
switch (type) {
case kVideoCodecVP8:
kbps = config->vp8.GetKbps();
break;
case kVideoCodecH265:
// TODO(bugs.webrtc.org/13485): Use VP9 bitrate limits for now.
case kVideoCodecVP9:
kbps = config->vp9.GetKbps();
break;
case kVideoCodecH264:
kbps = config->h264.GetKbps();
break;
case kVideoCodecAV1:
kbps = config->av1.GetKbps();
break;
case kVideoCodecGeneric:
kbps = config->generic.GetKbps();
break;
default:
break;
}
if (kbps.has_value())
return kbps;
return config->kbps > 0 ? absl::optional<int>(config->kbps) : absl::nullopt;
}
absl::optional<int> GetKbpsRes(
VideoCodecType type,
const absl::optional<BalancedDegradationSettings::Config>& config) {
if (!config.has_value())
return absl::nullopt;
absl::optional<int> kbps_res;
switch (type) {
case kVideoCodecVP8:
kbps_res = config->vp8.GetKbpsRes();
break;
case kVideoCodecH265:
// TODO(bugs.webrtc.org/13485): Use VP9 bitrate limits for now.
case kVideoCodecVP9:
kbps_res = config->vp9.GetKbpsRes();
break;
case kVideoCodecH264:
kbps_res = config->h264.GetKbpsRes();
break;
case kVideoCodecAV1:
kbps_res = config->av1.GetKbpsRes();
break;
case kVideoCodecGeneric:
kbps_res = config->generic.GetKbpsRes();
break;
default:
break;
}
if (kbps_res.has_value())
return kbps_res;
return config->kbps_res > 0 ? absl::optional<int>(config->kbps_res)
: absl::nullopt;
}
} // namespace
absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetQpLow()
const {
return (qp_low > 0) ? absl::optional<int>(qp_low) : absl::nullopt;
}
absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetQpHigh()
const {
return (qp_high > 0) ? absl::optional<int>(qp_high) : absl::nullopt;
}
absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetFps()
const {
return (fps > 0) ? absl::optional<int>(fps) : absl::nullopt;
}
absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetKbps()
const {
return (kbps > 0) ? absl::optional<int>(kbps) : absl::nullopt;
}
absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetKbpsRes()
const {
return (kbps_res > 0) ? absl::optional<int>(kbps_res) : absl::nullopt;
}
BalancedDegradationSettings::Config::Config() = default;
BalancedDegradationSettings::Config::Config(int pixels,
int fps,
int kbps,
int kbps_res,
int fps_diff,
CodecTypeSpecific vp8,
CodecTypeSpecific vp9,
CodecTypeSpecific h264,
CodecTypeSpecific av1,
CodecTypeSpecific generic)
: pixels(pixels),
fps(fps),
kbps(kbps),
kbps_res(kbps_res),
fps_diff(fps_diff),
vp8(vp8),
vp9(vp9),
h264(h264),
av1(av1),
generic(generic) {}
BalancedDegradationSettings::BalancedDegradationSettings(
const FieldTrialsView& field_trials) {
FieldTrialStructList<Config> configs(
{FieldTrialStructMember("pixels", [](Config* c) { return &c->pixels; }),
FieldTrialStructMember("fps", [](Config* c) { return &c->fps; }),
FieldTrialStructMember("kbps", [](Config* c) { return &c->kbps; }),
FieldTrialStructMember("kbps_res",
[](Config* c) { return &c->kbps_res; }),
FieldTrialStructMember("fps_diff",
[](Config* c) { return &c->fps_diff; }),
FieldTrialStructMember("vp8_qp_low",
[](Config* c) { return &c->vp8.qp_low; }),
FieldTrialStructMember("vp8_qp_high",
[](Config* c) { return &c->vp8.qp_high; }),
FieldTrialStructMember("vp8_fps", [](Config* c) { return &c->vp8.fps; }),
FieldTrialStructMember("vp8_kbps",
[](Config* c) { return &c->vp8.kbps; }),
FieldTrialStructMember("vp8_kbps_res",
[](Config* c) { return &c->vp8.kbps_res; }),
FieldTrialStructMember("vp9_qp_low",
[](Config* c) { return &c->vp9.qp_low; }),
FieldTrialStructMember("vp9_qp_high",
[](Config* c) { return &c->vp9.qp_high; }),
FieldTrialStructMember("vp9_fps", [](Config* c) { return &c->vp9.fps; }),
FieldTrialStructMember("vp9_kbps",
[](Config* c) { return &c->vp9.kbps; }),
FieldTrialStructMember("vp9_kbps_res",
[](Config* c) { return &c->vp9.kbps_res; }),
FieldTrialStructMember("h264_qp_low",
[](Config* c) { return &c->h264.qp_low; }),
FieldTrialStructMember("h264_qp_high",
[](Config* c) { return &c->h264.qp_high; }),
FieldTrialStructMember("h264_fps",
[](Config* c) { return &c->h264.fps; }),
FieldTrialStructMember("h264_kbps",
[](Config* c) { return &c->h264.kbps; }),
FieldTrialStructMember("h264_kbps_res",
[](Config* c) { return &c->h264.kbps_res; }),
FieldTrialStructMember("av1_qp_low",
[](Config* c) { return &c->av1.qp_low; }),
FieldTrialStructMember("av1_qp_high",
[](Config* c) { return &c->av1.qp_high; }),
FieldTrialStructMember("av1_fps", [](Config* c) { return &c->av1.fps; }),
FieldTrialStructMember("av1_kbps",
[](Config* c) { return &c->av1.kbps; }),
FieldTrialStructMember("av1_kbps_res",
[](Config* c) { return &c->av1.kbps_res; }),
FieldTrialStructMember("generic_qp_low",
[](Config* c) { return &c->generic.qp_low; }),
FieldTrialStructMember("generic_qp_high",
[](Config* c) { return &c->generic.qp_high; }),
FieldTrialStructMember("generic_fps",
[](Config* c) { return &c->generic.fps; }),
FieldTrialStructMember("generic_kbps",
[](Config* c) { return &c->generic.kbps; }),
FieldTrialStructMember("generic_kbps_res",
[](Config* c) { return &c->generic.kbps_res; })},
{});
ParseFieldTrial({&configs}, field_trials.Lookup(kFieldTrial));
configs_ = GetValidOrDefault(configs.Get());
RTC_DCHECK_GT(configs_.size(), 1);
}
BalancedDegradationSettings::~BalancedDegradationSettings() {}
std::vector<BalancedDegradationSettings::Config>
BalancedDegradationSettings::GetConfigs() const {
return configs_;
}
int BalancedDegradationSettings::MinFps(VideoCodecType type, int pixels) const {
return GetFps(type, GetMinFpsConfig(pixels));
}
absl::optional<BalancedDegradationSettings::Config>
BalancedDegradationSettings::GetMinFpsConfig(int pixels) const {
for (const auto& config : configs_) {
if (pixels <= config.pixels)
return config;
}
return absl::nullopt;
}
int BalancedDegradationSettings::MaxFps(VideoCodecType type, int pixels) const {
return GetFps(type, GetMaxFpsConfig(pixels));
}
absl::optional<BalancedDegradationSettings::Config>
BalancedDegradationSettings::GetMaxFpsConfig(int pixels) const {
for (size_t i = 0; i < configs_.size() - 1; ++i) {
if (pixels <= configs_[i].pixels)
return configs_[i + 1];
}
return absl::nullopt;
}
bool BalancedDegradationSettings::CanAdaptUp(VideoCodecType type,
int pixels,
uint32_t bitrate_bps) const {
absl::optional<int> min_kbps = GetKbps(type, GetMaxFpsConfig(pixels));
if (!min_kbps.has_value() || bitrate_bps == 0) {
return true; // No limit configured or bitrate provided.
}
return bitrate_bps >= static_cast<uint32_t>(min_kbps.value() * 1000);
}
bool BalancedDegradationSettings::CanAdaptUpResolution(
VideoCodecType type,
int pixels,
uint32_t bitrate_bps) const {
absl::optional<int> min_kbps = GetKbpsRes(type, GetMaxFpsConfig(pixels));
if (!min_kbps.has_value() || bitrate_bps == 0) {
return true; // No limit configured or bitrate provided.
}
return bitrate_bps >= static_cast<uint32_t>(min_kbps.value() * 1000);
}
absl::optional<int> BalancedDegradationSettings::MinFpsDiff(int pixels) const {
for (const auto& config : configs_) {
if (pixels <= config.pixels) {
return (config.fps_diff > kNoFpsDiff)
? absl::optional<int>(config.fps_diff)
: absl::nullopt;
}
}
return absl::nullopt;
}
absl::optional<VideoEncoder::QpThresholds>
BalancedDegradationSettings::GetQpThresholds(VideoCodecType type,
int pixels) const {
return GetThresholds(type, GetConfig(pixels));
}
BalancedDegradationSettings::Config BalancedDegradationSettings::GetConfig(
int pixels) const {
for (const auto& config : configs_) {
if (pixels <= config.pixels)
return config;
}
return configs_.back(); // Use last above highest pixels.
}
} // namespace webrtc

View file

@ -0,0 +1,143 @@
/*
* Copyright 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_BALANCED_DEGRADATION_SETTINGS_H_
#define RTC_BASE_EXPERIMENTS_BALANCED_DEGRADATION_SETTINGS_H_
#include <vector>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/video_codecs/video_encoder.h"
namespace webrtc {
class BalancedDegradationSettings {
public:
static constexpr int kNoFpsDiff = -100;
BalancedDegradationSettings(const FieldTrialsView& field_trials);
~BalancedDegradationSettings();
struct CodecTypeSpecific {
CodecTypeSpecific() {}
CodecTypeSpecific(int qp_low, int qp_high, int fps, int kbps, int kbps_res)
: qp_low(qp_low),
qp_high(qp_high),
fps(fps),
kbps(kbps),
kbps_res(kbps_res) {}
bool operator==(const CodecTypeSpecific& o) const {
return qp_low == o.qp_low && qp_high == o.qp_high && fps == o.fps &&
kbps == o.kbps && kbps_res == o.kbps_res;
}
absl::optional<int> GetQpLow() const;
absl::optional<int> GetQpHigh() const;
absl::optional<int> GetFps() const;
absl::optional<int> GetKbps() const;
absl::optional<int> GetKbpsRes() const;
// Optional settings.
int qp_low = 0;
int qp_high = 0;
int fps = 0; // If unset, defaults to `fps` in Config.
int kbps = 0; // If unset, defaults to `kbps` in Config.
int kbps_res = 0; // If unset, defaults to `kbps_res` in Config.
};
struct Config {
Config();
Config(int pixels,
int fps,
int kbps,
int kbps_res,
int fps_diff,
CodecTypeSpecific vp8,
CodecTypeSpecific vp9,
CodecTypeSpecific h264,
CodecTypeSpecific av1,
CodecTypeSpecific generic);
bool operator==(const Config& o) const {
return pixels == o.pixels && fps == o.fps && kbps == o.kbps &&
kbps_res == o.kbps_res && fps_diff == o.fps_diff && vp8 == o.vp8 &&
vp9 == o.vp9 && h264 == o.h264 && av1 == o.av1 &&
generic == o.generic;
}
// Example:
// WebRTC-Video-BalancedDegradationSettings/pixels:100|200|300,fps:5|15|25/
// pixels <= 100 -> min framerate: 5 fps
// pixels <= 200 -> min framerate: 15 fps
// pixels <= 300 -> min framerate: 25 fps
//
// WebRTC-Video-BalancedDegradationSettings/pixels:100|200|300,
// fps:5|15|25, // Min framerate.
// kbps:0|60|70, // Min bitrate needed to adapt up.
// kbps_res:0|65|75/ // Min bitrate needed to adapt up in resolution.
//
// pixels: fps: kbps: kbps_res:
// 300 30 - -
// 300 25 70 kbps 75 kbps
// 200 25 70 kbps -
// 200 15 60 kbps 65 kbps
// 100 15 60 kbps -
// 100 5
// optional optional
int pixels = 0; // Video frame size.
// If the frame size is less than or equal to `pixels`:
int fps = 0; // Min framerate to be used.
int kbps = 0; // Min bitrate needed to adapt up (resolution/fps).
int kbps_res = 0; // Min bitrate needed to adapt up in resolution.
int fps_diff = kNoFpsDiff; // Min fps reduction needed (input fps - `fps`)
// w/o triggering a new subsequent downgrade
// check.
CodecTypeSpecific vp8;
CodecTypeSpecific vp9;
CodecTypeSpecific h264;
CodecTypeSpecific av1;
CodecTypeSpecific generic;
};
// Returns configurations from field trial on success (default on failure).
std::vector<Config> GetConfigs() const;
// Gets the min/max framerate from `configs_` based on `pixels`.
int MinFps(VideoCodecType type, int pixels) const;
int MaxFps(VideoCodecType type, int pixels) const;
// Checks if quality can be increased based on `pixels` and `bitrate_bps`.
bool CanAdaptUp(VideoCodecType type, int pixels, uint32_t bitrate_bps) const;
bool CanAdaptUpResolution(VideoCodecType type,
int pixels,
uint32_t bitrate_bps) const;
// Gets the min framerate diff from `configs_` based on `pixels`.
absl::optional<int> MinFpsDiff(int pixels) const;
// Gets QpThresholds for the codec `type` based on `pixels`.
absl::optional<VideoEncoder::QpThresholds> GetQpThresholds(
VideoCodecType type,
int pixels) const;
private:
absl::optional<Config> GetMinFpsConfig(int pixels) const;
absl::optional<Config> GetMaxFpsConfig(int pixels) const;
Config GetConfig(int pixels) const;
std::vector<Config> configs_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_BALANCED_DEGRADATION_SETTINGS_H_

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2021 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 "rtc_base/experiments/bandwidth_quality_scaler_settings.h"
#include "api/transport/field_trial_based_config.h"
#include "rtc_base/logging.h"
namespace webrtc {
BandwidthQualityScalerSettings::BandwidthQualityScalerSettings(
const FieldTrialsView* const key_value_config)
: bitrate_state_update_interval_s_("bitrate_state_update_interval_s_") {
ParseFieldTrial(
{&bitrate_state_update_interval_s_},
key_value_config->Lookup("WebRTC-Video-BandwidthQualityScalerSettings"));
}
BandwidthQualityScalerSettings
BandwidthQualityScalerSettings::ParseFromFieldTrials() {
FieldTrialBasedConfig field_trial_config;
return BandwidthQualityScalerSettings(&field_trial_config);
}
absl::optional<uint32_t>
BandwidthQualityScalerSettings::BitrateStateUpdateInterval() const {
if (bitrate_state_update_interval_s_ &&
bitrate_state_update_interval_s_.Value() <= 0) {
RTC_LOG(LS_WARNING)
<< "Unsupported bitrate_state_update_interval_s_ value, ignored.";
return absl::nullopt;
}
return bitrate_state_update_interval_s_.GetOptional();
}
} // namespace webrtc

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_BANDWIDTH_QUALITY_SCALER_SETTINGS_H_
#define RTC_BASE_EXPERIMENTS_BANDWIDTH_QUALITY_SCALER_SETTINGS_H_
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
class BandwidthQualityScalerSettings final {
public:
static BandwidthQualityScalerSettings ParseFromFieldTrials();
absl::optional<uint32_t> BitrateStateUpdateInterval() const;
private:
explicit BandwidthQualityScalerSettings(
const FieldTrialsView* const key_value_config);
FieldTrialOptional<uint32_t> bitrate_state_update_interval_s_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_BANDWIDTH_QUALITY_SCALER_SETTINGS_H_

View file

@ -0,0 +1,89 @@
/*
* Copyright 2018 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 "rtc_base/experiments/cpu_speed_experiment.h"
#include <stdio.h>
#include "rtc_base/experiments/field_trial_list.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
namespace {
constexpr char kFieldTrial[] = "WebRTC-VP8-CpuSpeed-Arm";
constexpr int kMinSetting = -16;
constexpr int kMaxSetting = -1;
std::vector<CpuSpeedExperiment::Config> GetValidOrEmpty(
const std::vector<CpuSpeedExperiment::Config>& configs) {
if (configs.empty()) {
return {};
}
for (const auto& config : configs) {
if (config.cpu_speed < kMinSetting || config.cpu_speed > kMaxSetting) {
RTC_LOG(LS_WARNING) << "Unsupported cpu speed setting, value ignored.";
return {};
}
}
for (size_t i = 1; i < configs.size(); ++i) {
if (configs[i].pixels < configs[i - 1].pixels ||
configs[i].cpu_speed > configs[i - 1].cpu_speed) {
RTC_LOG(LS_WARNING) << "Invalid parameter value provided.";
return {};
}
}
return configs;
}
bool HasLeCores(const std::vector<CpuSpeedExperiment::Config>& configs) {
for (const auto& config : configs) {
if (config.cpu_speed_le_cores == 0)
return false;
}
return true;
}
} // namespace
CpuSpeedExperiment::CpuSpeedExperiment() : cores_("cores") {
FieldTrialStructList<Config> configs(
{FieldTrialStructMember("pixels", [](Config* c) { return &c->pixels; }),
FieldTrialStructMember("cpu_speed",
[](Config* c) { return &c->cpu_speed; }),
FieldTrialStructMember(
"cpu_speed_le_cores",
[](Config* c) { return &c->cpu_speed_le_cores; })},
{});
ParseFieldTrial({&configs, &cores_}, field_trial::FindFullName(kFieldTrial));
configs_ = GetValidOrEmpty(configs.Get());
}
CpuSpeedExperiment::~CpuSpeedExperiment() {}
absl::optional<int> CpuSpeedExperiment::GetValue(int pixels,
int num_cores) const {
if (configs_.empty())
return absl::nullopt;
bool use_le = HasLeCores(configs_) && cores_ && num_cores <= cores_.Value();
for (const auto& config : configs_) {
if (pixels <= config.pixels)
return use_le ? absl::optional<int>(config.cpu_speed_le_cores)
: absl::optional<int>(config.cpu_speed);
}
return absl::optional<int>(kMinSetting);
}
} // namespace webrtc

View file

@ -0,0 +1,64 @@
/*
* Copyright 2018 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_CPU_SPEED_EXPERIMENT_H_
#define RTC_BASE_EXPERIMENTS_CPU_SPEED_EXPERIMENT_H_
#include <vector>
#include "absl/types/optional.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
class CpuSpeedExperiment {
public:
CpuSpeedExperiment();
~CpuSpeedExperiment();
// Example:
// WebRTC-VP8-CpuSpeed-Arm/pixels:100|200|300,cpu_speed:-1|-2|-3/
// pixels <= 100 -> cpu speed: -1
// pixels <= 200 -> cpu speed: -2
// pixels <= 300 -> cpu speed: -3
// WebRTC-VP8-CpuSpeed-Arm/pixels:100|200|300,cpu_speed:-1|-2|-3/,
// cpu_speed_le_cores:-4|-5|-6,cores:3/
// If `num_cores` > 3
// pixels <= 100 -> cpu speed: -1
// pixels <= 200 -> cpu speed: -2
// pixels <= 300 -> cpu speed: -3
// else
// pixels <= 100 -> cpu speed: -4
// pixels <= 200 -> cpu speed: -5
// pixels <= 300 -> cpu speed: -6
struct Config {
int pixels = 0; // The video frame size.
int cpu_speed = 0; // The `cpu_speed` to be used if the frame size is less
// than or equal to `pixels`.
// Optional.
int cpu_speed_le_cores = 0; // Same as `cpu_speed` above but only used if
// `num_cores` <= `cores_`.
};
// Gets the cpu speed based on `pixels` and `num_cores`.
absl::optional<int> GetValue(int pixels, int num_cores) const;
private:
std::vector<Config> configs_;
// Threshold for when to use `cpu_speed_le_cores`.
FieldTrialOptional<int> cores_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_CPU_SPEED_EXPERIMENT_H_

View file

@ -0,0 +1,236 @@
/*
* Copyright 2021 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 "rtc_base/experiments/encoder_info_settings.h"
#include <stdio.h>
#include "absl/strings/string_view.h"
#include "rtc_base/experiments/field_trial_list.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
namespace {
std::vector<VideoEncoder::ResolutionBitrateLimits> ToResolutionBitrateLimits(
const std::vector<EncoderInfoSettings::BitrateLimit>& limits) {
std::vector<VideoEncoder::ResolutionBitrateLimits> result;
for (const auto& limit : limits) {
result.push_back(VideoEncoder::ResolutionBitrateLimits(
limit.frame_size_pixels, limit.min_start_bitrate_bps,
limit.min_bitrate_bps, limit.max_bitrate_bps));
}
return result;
}
constexpr float kDefaultMinBitratebps = 30000;
} // namespace
// Default bitrate limits for simulcast with one active stream:
// {frame_size_pixels, min_start_bitrate_bps, min_bitrate_bps, max_bitrate_bps}.
std::vector<VideoEncoder::ResolutionBitrateLimits>
EncoderInfoSettings::GetDefaultSinglecastBitrateLimits(
VideoCodecType codec_type) {
if (codec_type == kVideoCodecAV1) {
// AV1 singlecast max bitrate limits are higher than AV1 SVC max limits.
// This is because in singlecast we normally have just one receiver, BWE is
// known end-to-end and the encode target bitrate guarantees delivery of
// video.
// The min bitrate limits are not used in singlecast (used in SVC/simulcast
// to de-/activate spatial layers) and are set to zero. Send resolution in
// singlecast is assumed to be regulated by QP-based quality scaler.
return {{320 * 180, 0, 0, 256000},
{480 * 270, 176000, 0, 384000},
{640 * 360, 256000, 0, 512000},
{960 * 540, 384000, 0, 1024000},
{1280 * 720, 576000, 0, 1536000}};
}
if (codec_type == kVideoCodecVP9) {
// VP9 singlecast bitrate limits are derived ~directly from VP9 SVC bitrate
// limits. The current max limits are unnecessarily too strict for
// singlecast, where BWE is known end-to-end, especially for low
// resolutions.
return {{320 * 180, 0, 30000, 150000},
{480 * 270, 120000, 30000, 300000},
{640 * 360, 190000, 30000, 420000},
{960 * 540, 350000, 30000, 1000000},
{1280 * 720, 480000, 30000, 1500000}};
}
// VP8 and other codecs.
return {{320 * 180, 0, 30000, 300000},
{480 * 270, 200000, 30000, 500000},
{640 * 360, 300000, 30000, 800000},
{960 * 540, 500000, 30000, 1500000},
{1280 * 720, 900000, 30000, 2500000}};
}
absl::optional<VideoEncoder::ResolutionBitrateLimits>
EncoderInfoSettings::GetDefaultSinglecastBitrateLimitsForResolution(
VideoCodecType codec_type,
int frame_size_pixels) {
VideoEncoder::EncoderInfo info;
info.resolution_bitrate_limits =
GetDefaultSinglecastBitrateLimits(codec_type);
return info.GetEncoderBitrateLimitsForResolution(frame_size_pixels);
}
// Return the suitable bitrate limits for specified resolution when qp is
// untrusted, they are experimental values.
// TODO(bugs.webrtc.org/12942): Maybe we need to add other codecs(VP8/VP9)
// experimental values.
std::vector<VideoEncoder::ResolutionBitrateLimits>
EncoderInfoSettings::GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted() {
// Specific limits for H264/AVC
return {{0 * 0, 0, 0, 0},
{320 * 180, 0, 30000, 300000},
{480 * 270, 300000, 30000, 500000},
{640 * 360, 500000, 30000, 800000},
{960 * 540, 800000, 30000, 1500000},
{1280 * 720, 1500000, 30000, 2500000},
{1920 * 1080, 2500000, 30000, 4000000}};
}
// Through linear interpolation, return the bitrate limit corresponding to the
// specified |frame_size_pixels|.
absl::optional<VideoEncoder::ResolutionBitrateLimits>
EncoderInfoSettings::GetSinglecastBitrateLimitForResolutionWhenQpIsUntrusted(
absl::optional<int> frame_size_pixels,
const std::vector<VideoEncoder::ResolutionBitrateLimits>&
resolution_bitrate_limits) {
if (!frame_size_pixels.has_value() || frame_size_pixels.value() <= 0) {
return absl::nullopt;
}
std::vector<VideoEncoder::ResolutionBitrateLimits> bitrate_limits =
resolution_bitrate_limits;
// Sort the list of bitrate limits by resolution.
sort(bitrate_limits.begin(), bitrate_limits.end(),
[](const VideoEncoder::ResolutionBitrateLimits& lhs,
const VideoEncoder::ResolutionBitrateLimits& rhs) {
return lhs.frame_size_pixels < rhs.frame_size_pixels;
});
if (bitrate_limits.empty()) {
return absl::nullopt;
}
int interpolation_index = -1;
for (size_t i = 0; i < bitrate_limits.size(); ++i) {
if (bitrate_limits[i].frame_size_pixels >= frame_size_pixels.value()) {
interpolation_index = i;
break;
}
}
// -1 means that the maximum resolution is exceeded, we will select the
// largest data as the return result.
if (interpolation_index == -1) {
return *bitrate_limits.rbegin();
}
// If we have a matching resolution, return directly without interpolation.
if (bitrate_limits[interpolation_index].frame_size_pixels ==
frame_size_pixels.value()) {
return bitrate_limits[interpolation_index];
}
// No matching resolution, do a linear interpolate.
int lower_pixel_count =
bitrate_limits[interpolation_index - 1].frame_size_pixels;
int upper_pixel_count = bitrate_limits[interpolation_index].frame_size_pixels;
float alpha = (frame_size_pixels.value() - lower_pixel_count) * 1.0 /
(upper_pixel_count - lower_pixel_count);
int min_start_bitrate_bps = static_cast<int>(
bitrate_limits[interpolation_index].min_start_bitrate_bps * alpha +
bitrate_limits[interpolation_index - 1].min_start_bitrate_bps *
(1.0 - alpha));
int max_bitrate_bps = static_cast<int>(
bitrate_limits[interpolation_index].max_bitrate_bps * alpha +
bitrate_limits[interpolation_index - 1].max_bitrate_bps * (1.0 - alpha));
if (max_bitrate_bps >= min_start_bitrate_bps) {
return VideoEncoder::ResolutionBitrateLimits(
frame_size_pixels.value(), min_start_bitrate_bps, kDefaultMinBitratebps,
max_bitrate_bps);
} else {
RTC_LOG(LS_WARNING)
<< "BitRate interpolation calculating result is abnormal. "
<< " lower_pixel_count = " << lower_pixel_count
<< " upper_pixel_count = " << upper_pixel_count
<< " frame_size_pixels = " << frame_size_pixels.value()
<< " min_start_bitrate_bps = " << min_start_bitrate_bps
<< " min_bitrate_bps = " << kDefaultMinBitratebps
<< " max_bitrate_bps = " << max_bitrate_bps;
return absl::nullopt;
}
}
EncoderInfoSettings::EncoderInfoSettings(absl::string_view name)
: requested_resolution_alignment_("requested_resolution_alignment"),
apply_alignment_to_all_simulcast_layers_(
"apply_alignment_to_all_simulcast_layers") {
FieldTrialStructList<BitrateLimit> bitrate_limits(
{FieldTrialStructMember(
"frame_size_pixels",
[](BitrateLimit* b) { return &b->frame_size_pixels; }),
FieldTrialStructMember(
"min_start_bitrate_bps",
[](BitrateLimit* b) { return &b->min_start_bitrate_bps; }),
FieldTrialStructMember(
"min_bitrate_bps",
[](BitrateLimit* b) { return &b->min_bitrate_bps; }),
FieldTrialStructMember(
"max_bitrate_bps",
[](BitrateLimit* b) { return &b->max_bitrate_bps; })},
{});
std::string name_str(name);
if (field_trial::FindFullName(name_str).empty()) {
// Encoder name not found, use common string applying to all encoders.
name_str = "WebRTC-GetEncoderInfoOverride";
}
ParseFieldTrial({&bitrate_limits, &requested_resolution_alignment_,
&apply_alignment_to_all_simulcast_layers_},
field_trial::FindFullName(name_str));
resolution_bitrate_limits_ = ToResolutionBitrateLimits(bitrate_limits.Get());
}
absl::optional<uint32_t> EncoderInfoSettings::requested_resolution_alignment()
const {
if (requested_resolution_alignment_ &&
requested_resolution_alignment_.Value() < 1) {
RTC_LOG(LS_WARNING) << "Unsupported alignment value, ignored.";
return absl::nullopt;
}
return requested_resolution_alignment_.GetOptional();
}
EncoderInfoSettings::~EncoderInfoSettings() {}
SimulcastEncoderAdapterEncoderInfoSettings::
SimulcastEncoderAdapterEncoderInfoSettings()
: EncoderInfoSettings(
"WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride") {}
LibvpxVp8EncoderInfoSettings::LibvpxVp8EncoderInfoSettings()
: EncoderInfoSettings("WebRTC-VP8-GetEncoderInfoOverride") {}
LibvpxVp9EncoderInfoSettings::LibvpxVp9EncoderInfoSettings()
: EncoderInfoSettings("WebRTC-VP9-GetEncoderInfoOverride") {}
LibaomAv1EncoderInfoSettings::LibaomAv1EncoderInfoSettings()
: EncoderInfoSettings("WebRTC-Av1-GetEncoderInfoOverride") {}
} // namespace webrtc

View file

@ -0,0 +1,100 @@
/*
* Copyright 2021 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_ENCODER_INFO_SETTINGS_H_
#define RTC_BASE_EXPERIMENTS_ENCODER_INFO_SETTINGS_H_
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/video_codecs/video_encoder.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
class EncoderInfoSettings {
public:
virtual ~EncoderInfoSettings();
// Bitrate limits per resolution.
struct BitrateLimit {
int frame_size_pixels = 0; // The video frame size.
int min_start_bitrate_bps = 0; // The minimum bitrate to start encoding.
int min_bitrate_bps = 0; // The minimum bitrate.
int max_bitrate_bps = 0; // The maximum bitrate.
};
absl::optional<uint32_t> requested_resolution_alignment() const;
bool apply_alignment_to_all_simulcast_layers() const {
return apply_alignment_to_all_simulcast_layers_.Get();
}
std::vector<VideoEncoder::ResolutionBitrateLimits> resolution_bitrate_limits()
const {
return resolution_bitrate_limits_;
}
static std::vector<VideoEncoder::ResolutionBitrateLimits>
GetDefaultSinglecastBitrateLimits(VideoCodecType codec_type);
static absl::optional<VideoEncoder::ResolutionBitrateLimits>
GetDefaultSinglecastBitrateLimitsForResolution(VideoCodecType codec_type,
int frame_size_pixels);
static std::vector<VideoEncoder::ResolutionBitrateLimits>
GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted();
static absl::optional<VideoEncoder::ResolutionBitrateLimits>
GetSinglecastBitrateLimitForResolutionWhenQpIsUntrusted(
absl::optional<int> frame_size_pixels,
const std::vector<VideoEncoder::ResolutionBitrateLimits>&
resolution_bitrate_limits);
protected:
explicit EncoderInfoSettings(absl::string_view name);
private:
FieldTrialOptional<uint32_t> requested_resolution_alignment_;
FieldTrialFlag apply_alignment_to_all_simulcast_layers_;
std::vector<VideoEncoder::ResolutionBitrateLimits> resolution_bitrate_limits_;
};
// EncoderInfo settings for SimulcastEncoderAdapter.
class SimulcastEncoderAdapterEncoderInfoSettings : public EncoderInfoSettings {
public:
SimulcastEncoderAdapterEncoderInfoSettings();
~SimulcastEncoderAdapterEncoderInfoSettings() override {}
};
// EncoderInfo settings for LibvpxVp8Encoder.
class LibvpxVp8EncoderInfoSettings : public EncoderInfoSettings {
public:
LibvpxVp8EncoderInfoSettings();
~LibvpxVp8EncoderInfoSettings() override {}
};
// EncoderInfo settings for LibvpxVp9Encoder.
class LibvpxVp9EncoderInfoSettings : public EncoderInfoSettings {
public:
LibvpxVp9EncoderInfoSettings();
~LibvpxVp9EncoderInfoSettings() override {}
};
// EncoderInfo settings for LibaomAv1Encoder.
class LibaomAv1EncoderInfoSettings : public EncoderInfoSettings {
public:
LibaomAv1EncoderInfoSettings();
~LibaomAv1EncoderInfoSettings() override {}
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_ENCODER_INFO_SETTINGS_H_

View file

@ -0,0 +1,59 @@
/*
* Copyright 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 "rtc_base/experiments/field_trial_list.h"
#include "absl/strings/string_view.h"
namespace webrtc {
FieldTrialListBase::FieldTrialListBase(absl::string_view key)
: FieldTrialParameterInterface(key),
failed_(false),
parse_got_called_(false) {}
bool FieldTrialListBase::Failed() const {
return failed_;
}
bool FieldTrialListBase::Used() const {
return parse_got_called_;
}
int FieldTrialListWrapper::Length() {
return GetList()->Size();
}
bool FieldTrialListWrapper::Failed() {
return GetList()->Failed();
}
bool FieldTrialListWrapper::Used() {
return GetList()->Used();
}
bool FieldTrialStructListBase::Parse(absl::optional<std::string> str_value) {
RTC_DCHECK_NOTREACHED();
return true;
}
int FieldTrialStructListBase::ValidateAndGetLength() {
int length = -1;
for (std::unique_ptr<FieldTrialListWrapper>& list : sub_lists_) {
if (list->Failed())
return -1;
else if (!list->Used())
continue;
else if (length == -1)
length = list->Length();
else if (length != list->Length())
return -1;
}
return length;
}
} // namespace webrtc

View file

@ -0,0 +1,226 @@
/*
* Copyright 2018 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_
#define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_
#include <initializer_list>
#include <memory>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/string_encode.h"
// List support for field trial strings. FieldTrialList and FieldTrialStructList
// are used similarly to the other FieldTrialParameters, but take a variable
// number of parameters. A FieldTrialList<T> parses a |-delimeted string into a
// list of T, using ParseTypedParameter to parse the individual tokens.
// Example string: "my_list:1|2|3,empty_list,other_list:aardvark".
// A FieldTrialStructList combines multiple lists into a list-of-structs. It
// ensures that all its sublists parse correctly and have the same length, then
// uses user-supplied accessor functions to write those elements into structs of
// a user-supplied type.
// See the unit test for usage and behavior.
namespace webrtc {
class FieldTrialListBase : public FieldTrialParameterInterface {
protected:
friend class FieldTrialListWrapper;
explicit FieldTrialListBase(absl::string_view key);
bool Failed() const;
bool Used() const;
virtual int Size() = 0;
bool failed_;
bool parse_got_called_;
};
// This class represents a vector of type T. The elements are separated by a |
// and parsed using ParseTypedParameter.
template <typename T>
class FieldTrialList : public FieldTrialListBase {
public:
explicit FieldTrialList(absl::string_view key) : FieldTrialList(key, {}) {}
FieldTrialList(absl::string_view key, std::initializer_list<T> default_values)
: FieldTrialListBase(key), values_(default_values) {}
std::vector<T> Get() const { return values_; }
operator std::vector<T>() const { return Get(); }
typename std::vector<T>::const_reference operator[](size_t index) const {
return values_[index];
}
const std::vector<T>* operator->() const { return &values_; }
protected:
bool Parse(absl::optional<std::string> str_value) override {
parse_got_called_ = true;
if (!str_value) {
values_.clear();
return true;
}
std::vector<T> new_values_;
for (const absl::string_view token : rtc::split(str_value.value(), '|')) {
absl::optional<T> value = ParseTypedParameter<T>(token);
if (value) {
new_values_.push_back(*value);
} else {
failed_ = true;
return false;
}
}
values_.swap(new_values_);
return true;
}
int Size() override { return values_.size(); }
private:
std::vector<T> values_;
};
class FieldTrialListWrapper {
public:
virtual ~FieldTrialListWrapper() = default;
// Takes the element at the given index in the wrapped list and writes it to
// the given struct.
virtual void WriteElement(void* struct_to_write, int index) = 0;
virtual FieldTrialListBase* GetList() = 0;
int Length();
// Returns true iff the wrapped list has failed to parse at least one token.
bool Failed();
bool Used();
protected:
FieldTrialListWrapper() = default;
};
namespace field_trial_list_impl {
// The LambdaTypeTraits struct provides type information about lambdas in the
// template expressions below.
template <typename T>
struct LambdaTypeTraits : public LambdaTypeTraits<decltype(&T::operator())> {};
template <typename ClassType, typename RetType, typename SourceType>
struct LambdaTypeTraits<RetType* (ClassType::*)(SourceType*) const> {
using ret = RetType;
using src = SourceType;
};
template <typename T>
struct TypedFieldTrialListWrapper : FieldTrialListWrapper {
public:
TypedFieldTrialListWrapper(absl::string_view key,
std::function<void(void*, T)> sink)
: list_(key), sink_(sink) {}
void WriteElement(void* struct_to_write, int index) override {
sink_(struct_to_write, list_[index]);
}
FieldTrialListBase* GetList() override { return &list_; }
private:
FieldTrialList<T> list_;
std::function<void(void*, T)> sink_;
};
} // namespace field_trial_list_impl
template <typename F,
typename Traits = typename field_trial_list_impl::LambdaTypeTraits<F>>
FieldTrialListWrapper* FieldTrialStructMember(absl::string_view key,
F accessor) {
return new field_trial_list_impl::TypedFieldTrialListWrapper<
typename Traits::ret>(key, [accessor](void* s, typename Traits::ret t) {
*accessor(static_cast<typename Traits::src*>(s)) = t;
});
}
// This base class is here to reduce the amount of code we have to generate for
// each type of FieldTrialStructList.
class FieldTrialStructListBase : public FieldTrialParameterInterface {
protected:
FieldTrialStructListBase(
std::initializer_list<FieldTrialListWrapper*> sub_lists)
: FieldTrialParameterInterface(""), sub_lists_() {
// Take ownership of the list wrappers generated by FieldTrialStructMember
// on the call site.
for (FieldTrialListWrapper* const* it = sub_lists.begin();
it != sub_lists.end(); it++) {
sub_parameters_.push_back((*it)->GetList());
sub_lists_.push_back(std::unique_ptr<FieldTrialListWrapper>(*it));
}
}
// Check that all of our sublists that were in the field trial string had the
// same number of elements. If they do, we return that length. If they had
// different lengths, any sublist had parse failures or no sublists had
// user-supplied values, we return -1.
int ValidateAndGetLength();
bool Parse(absl::optional<std::string> str_value) override;
std::vector<std::unique_ptr<FieldTrialListWrapper>> sub_lists_;
};
template <typename S>
class FieldTrialStructList : public FieldTrialStructListBase {
public:
FieldTrialStructList(std::initializer_list<FieldTrialListWrapper*> l,
std::initializer_list<S> default_list)
: FieldTrialStructListBase(l), values_(default_list) {}
std::vector<S> Get() const { return values_; }
operator std::vector<S>() const { return Get(); }
const S& operator[](size_t index) const { return values_[index]; }
const std::vector<S>* operator->() const { return &values_; }
protected:
void ParseDone() override {
int length = ValidateAndGetLength();
if (length == -1)
return;
std::vector<S> new_values(length, S());
for (std::unique_ptr<FieldTrialListWrapper>& li : sub_lists_) {
if (li->Used()) {
for (int i = 0; i < length; i++) {
li->WriteElement(&new_values[i], i);
}
}
}
values_.swap(new_values);
}
private:
std::vector<S> values_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_

View file

@ -0,0 +1,260 @@
/*
* Copyright 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 "rtc_base/experiments/field_trial_parser.h"
#include <inttypes.h>
#include <algorithm>
#include <map>
#include <type_traits>
#include <utility>
#include "absl/strings/string_view.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
FieldTrialParameterInterface::FieldTrialParameterInterface(
absl::string_view key)
: key_(key) {}
FieldTrialParameterInterface::~FieldTrialParameterInterface() {
RTC_DCHECK(used_) << "Field trial parameter with key: '" << key_
<< "' never used.";
}
void ParseFieldTrial(
std::initializer_list<FieldTrialParameterInterface*> fields,
absl::string_view trial_string) {
std::map<absl::string_view, FieldTrialParameterInterface*> field_map;
FieldTrialParameterInterface* keyless_field = nullptr;
for (FieldTrialParameterInterface* field : fields) {
field->MarkAsUsed();
if (!field->sub_parameters_.empty()) {
for (FieldTrialParameterInterface* sub_field : field->sub_parameters_) {
RTC_DCHECK(!sub_field->key_.empty());
sub_field->MarkAsUsed();
field_map[sub_field->key_] = sub_field;
}
continue;
}
if (field->key_.empty()) {
RTC_DCHECK(!keyless_field);
keyless_field = field;
} else {
field_map[field->key_] = field;
}
}
bool logged_unknown_key = false;
absl::string_view tail = trial_string;
while (!tail.empty()) {
size_t key_end = tail.find_first_of(",:");
absl::string_view key = tail.substr(0, key_end);
absl::optional<std::string> opt_value;
if (key_end == absl::string_view::npos) {
tail = "";
} else if (tail[key_end] == ':') {
tail = tail.substr(key_end + 1);
size_t value_end = tail.find(',');
opt_value.emplace(tail.substr(0, value_end));
if (value_end == absl::string_view::npos) {
tail = "";
} else {
tail = tail.substr(value_end + 1);
}
} else {
RTC_DCHECK_EQ(tail[key_end], ',');
tail = tail.substr(key_end + 1);
}
auto field = field_map.find(key);
if (field != field_map.end()) {
if (!field->second->Parse(std::move(opt_value))) {
RTC_LOG(LS_WARNING) << "Failed to read field with key: '" << key
<< "' in trial: \"" << trial_string << "\"";
}
} else if (!opt_value && keyless_field && !key.empty()) {
if (!keyless_field->Parse(std::string(key))) {
RTC_LOG(LS_WARNING) << "Failed to read empty key field with value '"
<< key << "' in trial: \"" << trial_string << "\"";
}
} else if (key.empty() || key[0] != '_') {
// "_" is be used to prefix keys that are part of the string for
// debugging purposes but not neccessarily used.
// e.g. WebRTC-Experiment/param: value, _DebuggingString
if (!logged_unknown_key) {
RTC_LOG(LS_INFO) << "No field with key: '" << key
<< "' (found in trial: \"" << trial_string << "\")";
std::string valid_keys;
for (const auto& f : field_map) {
valid_keys.append(f.first.data(), f.first.size());
valid_keys += ", ";
}
RTC_LOG(LS_INFO) << "Valid keys are: " << valid_keys;
logged_unknown_key = true;
}
}
}
for (FieldTrialParameterInterface* field : fields) {
field->ParseDone();
}
}
template <>
absl::optional<bool> ParseTypedParameter<bool>(absl::string_view str) {
if (str == "true" || str == "1") {
return true;
} else if (str == "false" || str == "0") {
return false;
}
return absl::nullopt;
}
template <>
absl::optional<double> ParseTypedParameter<double>(absl::string_view str) {
double value;
char unit[2]{0, 0};
if (sscanf(std::string(str).c_str(), "%lf%1s", &value, unit) >= 1) {
if (unit[0] == '%')
return value / 100;
return value;
} else {
return absl::nullopt;
}
}
template <>
absl::optional<int> ParseTypedParameter<int>(absl::string_view str) {
int64_t value;
if (sscanf(std::string(str).c_str(), "%" SCNd64, &value) == 1) {
if (rtc::IsValueInRangeForNumericType<int, int64_t>(value)) {
return static_cast<int>(value);
}
}
return absl::nullopt;
}
template <>
absl::optional<unsigned> ParseTypedParameter<unsigned>(absl::string_view str) {
int64_t value;
if (sscanf(std::string(str).c_str(), "%" SCNd64, &value) == 1) {
if (rtc::IsValueInRangeForNumericType<unsigned, int64_t>(value)) {
return static_cast<unsigned>(value);
}
}
return absl::nullopt;
}
template <>
absl::optional<std::string> ParseTypedParameter<std::string>(
absl::string_view str) {
return std::string(str);
}
template <>
absl::optional<absl::optional<bool>> ParseTypedParameter<absl::optional<bool>>(
absl::string_view str) {
return ParseOptionalParameter<bool>(str);
}
template <>
absl::optional<absl::optional<int>> ParseTypedParameter<absl::optional<int>>(
absl::string_view str) {
return ParseOptionalParameter<int>(str);
}
template <>
absl::optional<absl::optional<unsigned>>
ParseTypedParameter<absl::optional<unsigned>>(absl::string_view str) {
return ParseOptionalParameter<unsigned>(str);
}
template <>
absl::optional<absl::optional<double>>
ParseTypedParameter<absl::optional<double>>(absl::string_view str) {
return ParseOptionalParameter<double>(str);
}
FieldTrialFlag::FieldTrialFlag(absl::string_view key)
: FieldTrialFlag(key, false) {}
FieldTrialFlag::FieldTrialFlag(absl::string_view key, bool default_value)
: FieldTrialParameterInterface(key), value_(default_value) {}
bool FieldTrialFlag::Get() const {
return value_;
}
webrtc::FieldTrialFlag::operator bool() const {
return value_;
}
bool FieldTrialFlag::Parse(absl::optional<std::string> str_value) {
// Only set the flag if there is no argument provided.
if (str_value) {
absl::optional<bool> opt_value = ParseTypedParameter<bool>(*str_value);
if (!opt_value)
return false;
value_ = *opt_value;
} else {
value_ = true;
}
return true;
}
AbstractFieldTrialEnum::AbstractFieldTrialEnum(
absl::string_view key,
int default_value,
std::map<std::string, int> mapping)
: FieldTrialParameterInterface(key),
value_(default_value),
enum_mapping_(mapping) {
for (auto& key_val : enum_mapping_)
valid_values_.insert(key_val.second);
}
AbstractFieldTrialEnum::AbstractFieldTrialEnum(const AbstractFieldTrialEnum&) =
default;
AbstractFieldTrialEnum::~AbstractFieldTrialEnum() = default;
bool AbstractFieldTrialEnum::Parse(absl::optional<std::string> str_value) {
if (str_value) {
auto it = enum_mapping_.find(*str_value);
if (it != enum_mapping_.end()) {
value_ = it->second;
return true;
}
absl::optional<int> value = ParseTypedParameter<int>(*str_value);
if (value.has_value() &&
(valid_values_.find(*value) != valid_values_.end())) {
value_ = *value;
return true;
}
}
return false;
}
template class FieldTrialParameter<bool>;
template class FieldTrialParameter<double>;
template class FieldTrialParameter<int>;
template class FieldTrialParameter<unsigned>;
template class FieldTrialParameter<std::string>;
template class FieldTrialConstrained<double>;
template class FieldTrialConstrained<int>;
template class FieldTrialConstrained<unsigned>;
template class FieldTrialOptional<double>;
template class FieldTrialOptional<int>;
template class FieldTrialOptional<unsigned>;
template class FieldTrialOptional<bool>;
template class FieldTrialOptional<std::string>;
} // namespace webrtc

View file

@ -0,0 +1,291 @@
/*
* Copyright 2018 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_
#define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_
#include <stdint.h>
#include <initializer_list>
#include <map>
#include <set>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
// Field trial parser functionality. Provides funcitonality to parse field trial
// argument strings in key:value format. Each parameter is described using
// key:value, parameters are separated with a ,. Values can't include the comma
// character, since there's no quote facility. For most types, white space is
// ignored. Parameters are declared with a given type for which an
// implementation of ParseTypedParameter should be provided. The
// ParseTypedParameter implementation is given whatever is between the : and the
// ,. If the key is provided without : a FieldTrialOptional will use nullopt.
// Example string: "my_optional,my_int:3,my_string:hello"
// For further description of usage and behavior, see the examples in the unit
// tests.
namespace webrtc {
class FieldTrialParameterInterface {
public:
virtual ~FieldTrialParameterInterface();
std::string key() const { return key_; }
protected:
// Protected to allow implementations to provide assignment and copy.
FieldTrialParameterInterface(const FieldTrialParameterInterface&) = default;
FieldTrialParameterInterface& operator=(const FieldTrialParameterInterface&) =
default;
explicit FieldTrialParameterInterface(absl::string_view key);
friend void ParseFieldTrial(
std::initializer_list<FieldTrialParameterInterface*> fields,
absl::string_view trial_string);
void MarkAsUsed() { used_ = true; }
virtual bool Parse(absl::optional<std::string> str_value) = 0;
virtual void ParseDone() {}
std::vector<FieldTrialParameterInterface*> sub_parameters_;
private:
std::string key_;
bool used_ = false;
};
// ParseFieldTrial function parses the given string and fills the given fields
// with extracted values if available.
void ParseFieldTrial(
std::initializer_list<FieldTrialParameterInterface*> fields,
absl::string_view trial_string);
// Specialize this in code file for custom types. Should return absl::nullopt if
// the given string cannot be properly parsed.
template <typename T>
absl::optional<T> ParseTypedParameter(absl::string_view);
// This class uses the ParseTypedParameter function to implement a parameter
// implementation with an enforced default value.
template <typename T>
class FieldTrialParameter : public FieldTrialParameterInterface {
public:
FieldTrialParameter(absl::string_view key, T default_value)
: FieldTrialParameterInterface(key), value_(default_value) {}
T Get() const { return value_; }
operator T() const { return Get(); }
const T* operator->() const { return &value_; }
void SetForTest(T value) { value_ = value; }
protected:
bool Parse(absl::optional<std::string> str_value) override {
if (str_value) {
absl::optional<T> value = ParseTypedParameter<T>(*str_value);
if (value.has_value()) {
value_ = value.value();
return true;
}
}
return false;
}
private:
T value_;
};
// This class uses the ParseTypedParameter function to implement a parameter
// implementation with an enforced default value and a range constraint. Values
// outside the configured range will be ignored.
template <typename T>
class FieldTrialConstrained : public FieldTrialParameterInterface {
public:
FieldTrialConstrained(absl::string_view key,
T default_value,
absl::optional<T> lower_limit,
absl::optional<T> upper_limit)
: FieldTrialParameterInterface(key),
value_(default_value),
lower_limit_(lower_limit),
upper_limit_(upper_limit) {}
T Get() const { return value_; }
operator T() const { return Get(); }
const T* operator->() const { return &value_; }
protected:
bool Parse(absl::optional<std::string> str_value) override {
if (str_value) {
absl::optional<T> value = ParseTypedParameter<T>(*str_value);
if (value && (!lower_limit_ || *value >= *lower_limit_) &&
(!upper_limit_ || *value <= *upper_limit_)) {
value_ = *value;
return true;
}
}
return false;
}
private:
T value_;
absl::optional<T> lower_limit_;
absl::optional<T> upper_limit_;
};
class AbstractFieldTrialEnum : public FieldTrialParameterInterface {
public:
AbstractFieldTrialEnum(absl::string_view key,
int default_value,
std::map<std::string, int> mapping);
~AbstractFieldTrialEnum() override;
AbstractFieldTrialEnum(const AbstractFieldTrialEnum&);
protected:
bool Parse(absl::optional<std::string> str_value) override;
protected:
int value_;
std::map<std::string, int> enum_mapping_;
std::set<int> valid_values_;
};
// The FieldTrialEnum class can be used to quickly define a parser for a
// specific enum. It handles values provided as integers and as strings if a
// mapping is provided.
template <typename T>
class FieldTrialEnum : public AbstractFieldTrialEnum {
public:
FieldTrialEnum(absl::string_view key,
T default_value,
std::map<std::string, T> mapping)
: AbstractFieldTrialEnum(key,
static_cast<int>(default_value),
ToIntMap(mapping)) {}
T Get() const { return static_cast<T>(value_); }
operator T() const { return Get(); }
private:
static std::map<std::string, int> ToIntMap(std::map<std::string, T> mapping) {
std::map<std::string, int> res;
for (const auto& it : mapping)
res[it.first] = static_cast<int>(it.second);
return res;
}
};
// This class uses the ParseTypedParameter function to implement an optional
// parameter implementation that can default to absl::nullopt.
template <typename T>
class FieldTrialOptional : public FieldTrialParameterInterface {
public:
explicit FieldTrialOptional(absl::string_view key)
: FieldTrialParameterInterface(key) {}
FieldTrialOptional(absl::string_view key, absl::optional<T> default_value)
: FieldTrialParameterInterface(key), value_(default_value) {}
absl::optional<T> GetOptional() const { return value_; }
const T& Value() const { return value_.value(); }
const T& operator*() const { return value_.value(); }
const T* operator->() const { return &value_.value(); }
explicit operator bool() const { return value_.has_value(); }
protected:
bool Parse(absl::optional<std::string> str_value) override {
if (str_value) {
absl::optional<T> value = ParseTypedParameter<T>(*str_value);
if (!value.has_value())
return false;
value_ = value.value();
} else {
value_ = absl::nullopt;
}
return true;
}
private:
absl::optional<T> value_;
};
// Equivalent to a FieldTrialParameter<bool> in the case that both key and value
// are present. If key is missing, evaluates to false. If key is present, but no
// explicit value is provided, the flag evaluates to true.
class FieldTrialFlag : public FieldTrialParameterInterface {
public:
explicit FieldTrialFlag(absl::string_view key);
FieldTrialFlag(absl::string_view key, bool default_value);
bool Get() const;
explicit operator bool() const;
protected:
bool Parse(absl::optional<std::string> str_value) override;
private:
bool value_;
};
template <typename T>
absl::optional<absl::optional<T>> ParseOptionalParameter(
absl::string_view str) {
if (str.empty())
return absl::optional<T>();
auto parsed = ParseTypedParameter<T>(str);
if (parsed.has_value())
return parsed;
return absl::nullopt;
}
template <>
absl::optional<bool> ParseTypedParameter<bool>(absl::string_view str);
template <>
absl::optional<double> ParseTypedParameter<double>(absl::string_view str);
template <>
absl::optional<int> ParseTypedParameter<int>(absl::string_view str);
template <>
absl::optional<unsigned> ParseTypedParameter<unsigned>(absl::string_view str);
template <>
absl::optional<std::string> ParseTypedParameter<std::string>(
absl::string_view str);
template <>
absl::optional<absl::optional<bool>> ParseTypedParameter<absl::optional<bool>>(
absl::string_view str);
template <>
absl::optional<absl::optional<int>> ParseTypedParameter<absl::optional<int>>(
absl::string_view str);
template <>
absl::optional<absl::optional<unsigned>>
ParseTypedParameter<absl::optional<unsigned>>(absl::string_view str);
template <>
absl::optional<absl::optional<double>>
ParseTypedParameter<absl::optional<double>>(absl::string_view str);
// Accepts true, false, else parsed with sscanf %i, true if != 0.
extern template class FieldTrialParameter<bool>;
// Interpreted using sscanf %lf.
extern template class FieldTrialParameter<double>;
// Interpreted using sscanf %i.
extern template class FieldTrialParameter<int>;
// Interpreted using sscanf %u.
extern template class FieldTrialParameter<unsigned>;
// Using the given value as is.
extern template class FieldTrialParameter<std::string>;
extern template class FieldTrialConstrained<double>;
extern template class FieldTrialConstrained<int>;
extern template class FieldTrialConstrained<unsigned>;
extern template class FieldTrialOptional<double>;
extern template class FieldTrialOptional<int>;
extern template class FieldTrialOptional<unsigned>;
extern template class FieldTrialOptional<bool>;
extern template class FieldTrialOptional<std::string>;
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_

View file

@ -0,0 +1,116 @@
/*
* Copyright 2018 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 "rtc_base/experiments/field_trial_units.h"
#include <stdio.h>
#include <limits>
#include <string>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
// Large enough to fit "seconds", the longest supported unit name.
#define RTC_TRIAL_UNIT_LENGTH_STR "7"
#define RTC_TRIAL_UNIT_SIZE 8
namespace webrtc {
namespace {
struct ValueWithUnit {
double value;
std::string unit;
};
absl::optional<ValueWithUnit> ParseValueWithUnit(absl::string_view str) {
if (str == "inf") {
return ValueWithUnit{std::numeric_limits<double>::infinity(), ""};
} else if (str == "-inf") {
return ValueWithUnit{-std::numeric_limits<double>::infinity(), ""};
} else {
double double_val;
char unit_char[RTC_TRIAL_UNIT_SIZE];
unit_char[0] = 0;
if (sscanf(std::string(str).c_str(), "%lf%" RTC_TRIAL_UNIT_LENGTH_STR "s",
&double_val, unit_char) >= 1) {
return ValueWithUnit{double_val, unit_char};
}
}
return absl::nullopt;
}
} // namespace
template <>
absl::optional<DataRate> ParseTypedParameter<DataRate>(absl::string_view str) {
absl::optional<ValueWithUnit> result = ParseValueWithUnit(str);
if (result) {
if (result->unit.empty() || result->unit == "kbps") {
return DataRate::KilobitsPerSec(result->value);
} else if (result->unit == "bps") {
return DataRate::BitsPerSec(result->value);
}
}
return absl::nullopt;
}
template <>
absl::optional<DataSize> ParseTypedParameter<DataSize>(absl::string_view str) {
absl::optional<ValueWithUnit> result = ParseValueWithUnit(str);
if (result) {
if (result->unit.empty() || result->unit == "bytes")
return DataSize::Bytes(result->value);
}
return absl::nullopt;
}
template <>
absl::optional<TimeDelta> ParseTypedParameter<TimeDelta>(
absl::string_view str) {
absl::optional<ValueWithUnit> result = ParseValueWithUnit(str);
if (result) {
if (result->unit == "s" || result->unit == "seconds") {
return TimeDelta::Seconds(result->value);
} else if (result->unit == "us") {
return TimeDelta::Micros(result->value);
} else if (result->unit.empty() || result->unit == "ms") {
return TimeDelta::Millis(result->value);
}
}
return absl::nullopt;
}
template <>
absl::optional<absl::optional<DataRate>>
ParseTypedParameter<absl::optional<DataRate>>(absl::string_view str) {
return ParseOptionalParameter<DataRate>(str);
}
template <>
absl::optional<absl::optional<DataSize>>
ParseTypedParameter<absl::optional<DataSize>>(absl::string_view str) {
return ParseOptionalParameter<DataSize>(str);
}
template <>
absl::optional<absl::optional<TimeDelta>>
ParseTypedParameter<absl::optional<TimeDelta>>(absl::string_view str) {
return ParseOptionalParameter<TimeDelta>(str);
}
template class FieldTrialParameter<DataRate>;
template class FieldTrialParameter<DataSize>;
template class FieldTrialParameter<TimeDelta>;
template class FieldTrialConstrained<DataRate>;
template class FieldTrialConstrained<DataSize>;
template class FieldTrialConstrained<TimeDelta>;
template class FieldTrialOptional<DataRate>;
template class FieldTrialOptional<DataSize>;
template class FieldTrialOptional<TimeDelta>;
} // namespace webrtc

View file

@ -0,0 +1,41 @@
/*
* Copyright 2018 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_FIELD_TRIAL_UNITS_H_
#define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_UNITS_H_
#include "absl/strings/string_view.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
template <>
absl::optional<DataRate> ParseTypedParameter<DataRate>(absl::string_view str);
template <>
absl::optional<DataSize> ParseTypedParameter<DataSize>(absl::string_view str);
template <>
absl::optional<TimeDelta> ParseTypedParameter<TimeDelta>(absl::string_view str);
extern template class FieldTrialParameter<DataRate>;
extern template class FieldTrialParameter<DataSize>;
extern template class FieldTrialParameter<TimeDelta>;
extern template class FieldTrialConstrained<DataRate>;
extern template class FieldTrialConstrained<DataSize>;
extern template class FieldTrialConstrained<TimeDelta>;
extern template class FieldTrialOptional<DataRate>;
extern template class FieldTrialOptional<DataSize>;
extern template class FieldTrialOptional<TimeDelta>;
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_FIELD_TRIAL_UNITS_H_

View file

@ -0,0 +1,39 @@
/*
* 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 "rtc_base/experiments/keyframe_interval_settings.h"
#include "api/transport/field_trial_based_config.h"
namespace webrtc {
namespace {
constexpr char kFieldTrialName[] = "WebRTC-KeyframeInterval";
} // namespace
KeyframeIntervalSettings::KeyframeIntervalSettings(
const FieldTrialsView* const key_value_config)
: min_keyframe_send_interval_ms_("min_keyframe_send_interval_ms") {
ParseFieldTrial({&min_keyframe_send_interval_ms_},
key_value_config->Lookup(kFieldTrialName));
}
KeyframeIntervalSettings KeyframeIntervalSettings::ParseFromFieldTrials() {
FieldTrialBasedConfig field_trial_config;
return KeyframeIntervalSettings(&field_trial_config);
}
absl::optional<int> KeyframeIntervalSettings::MinKeyframeSendIntervalMs()
const {
return min_keyframe_send_interval_ms_.GetOptional();
}
} // namespace webrtc

View file

@ -0,0 +1,39 @@
/*
* 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_KEYFRAME_INTERVAL_SETTINGS_H_
#define RTC_BASE_EXPERIMENTS_KEYFRAME_INTERVAL_SETTINGS_H_
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
// TODO(bugs.webrtc.org/10427): Remove and replace with proper configuration
// parameter, or move to using FIR if intent is to avoid triggering multiple
// times to PLIs corresponding to the same request when RTT is large.
class KeyframeIntervalSettings final {
public:
static KeyframeIntervalSettings ParseFromFieldTrials();
// Sender side.
// The encoded keyframe send rate is <= 1/MinKeyframeSendIntervalMs().
absl::optional<int> MinKeyframeSendIntervalMs() const;
private:
explicit KeyframeIntervalSettings(const FieldTrialsView* key_value_config);
FieldTrialOptional<int> min_keyframe_send_interval_ms_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_KEYFRAME_INTERVAL_SETTINGS_H_

View file

@ -0,0 +1,116 @@
/*
* Copyright 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 "rtc_base/experiments/min_video_bitrate_experiment.h"
#include <string>
#include "rtc_base/checks.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
const int kDefaultMinVideoBitrateBps = 30000;
namespace {
const char kForcedFallbackFieldTrial[] =
"WebRTC-VP8-Forced-Fallback-Encoder-v2";
const char kMinVideoBitrateExperiment[] = "WebRTC-Video-MinVideoBitrate";
absl::optional<int> GetFallbackMinBpsFromFieldTrial(VideoCodecType type) {
if (type != kVideoCodecVP8) {
return absl::nullopt;
}
if (!webrtc::field_trial::IsEnabled(kForcedFallbackFieldTrial)) {
return absl::nullopt;
}
const std::string group =
webrtc::field_trial::FindFullName(kForcedFallbackFieldTrial);
if (group.empty()) {
return absl::nullopt;
}
int min_pixels; // Ignored.
int max_pixels; // Ignored.
int min_bps;
if (sscanf(group.c_str(), "Enabled-%d,%d,%d", &min_pixels, &max_pixels,
&min_bps) != 3) {
return absl::nullopt;
}
if (min_bps <= 0) {
return absl::nullopt;
}
return min_bps;
}
} // namespace
absl::optional<DataRate> GetExperimentalMinVideoBitrate(VideoCodecType type) {
const absl::optional<int> fallback_min_bitrate_bps =
GetFallbackMinBpsFromFieldTrial(type);
if (fallback_min_bitrate_bps) {
return DataRate::BitsPerSec(*fallback_min_bitrate_bps);
}
if (webrtc::field_trial::IsEnabled(kMinVideoBitrateExperiment)) {
webrtc::FieldTrialFlag enabled("Enabled");
// Backwards-compatibility with an old experiment - a generic minimum which,
// if set, applies to all codecs.
webrtc::FieldTrialOptional<webrtc::DataRate> min_video_bitrate("br");
// New experiment - per-codec minimum bitrate.
webrtc::FieldTrialOptional<webrtc::DataRate> min_bitrate_vp8("vp8_br");
webrtc::FieldTrialOptional<webrtc::DataRate> min_bitrate_vp9("vp9_br");
webrtc::FieldTrialOptional<webrtc::DataRate> min_bitrate_av1("av1_br");
webrtc::FieldTrialOptional<webrtc::DataRate> min_bitrate_h264("h264_br");
webrtc::ParseFieldTrial(
{&enabled, &min_video_bitrate, &min_bitrate_vp8, &min_bitrate_vp9,
&min_bitrate_av1, &min_bitrate_h264},
webrtc::field_trial::FindFullName(kMinVideoBitrateExperiment));
if (min_video_bitrate) {
if (min_bitrate_vp8 || min_bitrate_vp9 || min_bitrate_av1 ||
min_bitrate_h264) {
// "br" is mutually-exclusive with the other configuration possibilites.
RTC_LOG(LS_WARNING) << "Self-contradictory experiment config.";
}
return *min_video_bitrate;
}
switch (type) {
case kVideoCodecVP8:
return min_bitrate_vp8.GetOptional();
case kVideoCodecH265:
// TODO(bugs.webrtc.org/13485): Use VP9 bitrate limits for now.
case kVideoCodecVP9:
return min_bitrate_vp9.GetOptional();
case kVideoCodecAV1:
return min_bitrate_av1.GetOptional();
case kVideoCodecH264:
return min_bitrate_h264.GetOptional();
case kVideoCodecGeneric:
case kVideoCodecMultiplex:
return absl::nullopt;
}
RTC_DCHECK_NOTREACHED();
}
return absl::nullopt;
}
} // namespace webrtc

View file

@ -0,0 +1,28 @@
/*
* Copyright 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_MIN_VIDEO_BITRATE_EXPERIMENT_H_
#define RTC_BASE_EXPERIMENTS_MIN_VIDEO_BITRATE_EXPERIMENT_H_
#include "absl/types/optional.h"
#include "api/units/data_rate.h"
#include "api/video/video_codec_type.h"
namespace webrtc {
extern const int kDefaultMinVideoBitrateBps;
// Return the experiment-driven minimum video bitrate.
// If no experiment is effective, returns nullopt.
absl::optional<DataRate> GetExperimentalMinVideoBitrate(VideoCodecType type);
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_MIN_VIDEO_BITRATE_EXPERIMENT_H_

View file

@ -0,0 +1,49 @@
/*
* Copyright 2018 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 "rtc_base/experiments/normalize_simulcast_size_experiment.h"
#include <stdio.h>
#include <string>
#include "rtc_base/logging.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
namespace {
constexpr char kFieldTrial[] = "WebRTC-NormalizeSimulcastResolution";
constexpr int kMinSetting = 0;
constexpr int kMaxSetting = 5;
} // namespace
absl::optional<int> NormalizeSimulcastSizeExperiment::GetBase2Exponent() {
if (!webrtc::field_trial::IsEnabled(kFieldTrial))
return absl::nullopt;
const std::string group = webrtc::field_trial::FindFullName(kFieldTrial);
if (group.empty())
return absl::nullopt;
int exponent;
if (sscanf(group.c_str(), "Enabled-%d", &exponent) != 1) {
RTC_LOG(LS_WARNING) << "No parameter provided.";
return absl::nullopt;
}
if (exponent < kMinSetting || exponent > kMaxSetting) {
RTC_LOG(LS_WARNING) << "Unsupported exp value provided, value ignored.";
return absl::nullopt;
}
return absl::optional<int>(exponent);
}
} // namespace webrtc

View file

@ -0,0 +1,25 @@
/*
* Copyright 2018 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_NORMALIZE_SIMULCAST_SIZE_EXPERIMENT_H_
#define RTC_BASE_EXPERIMENTS_NORMALIZE_SIMULCAST_SIZE_EXPERIMENT_H_
#include "absl/types/optional.h"
namespace webrtc {
class NormalizeSimulcastSizeExperiment {
public:
// Returns the base two exponent from field trial.
static absl::optional<int> GetBase2Exponent();
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_NORMALIZE_SIMULCAST_SIZE_EXPERIMENT_H_

View file

@ -0,0 +1,82 @@
/*
* 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 "rtc_base/experiments/quality_rampup_experiment.h"
#include <algorithm>
#include "api/transport/field_trial_based_config.h"
#include "rtc_base/logging.h"
namespace webrtc {
QualityRampupExperiment::QualityRampupExperiment(
const FieldTrialsView* const key_value_config)
: min_pixels_("min_pixels"),
min_duration_ms_("min_duration_ms"),
max_bitrate_factor_("max_bitrate_factor") {
ParseFieldTrial(
{&min_pixels_, &min_duration_ms_, &max_bitrate_factor_},
key_value_config->Lookup("WebRTC-Video-QualityRampupSettings"));
}
QualityRampupExperiment QualityRampupExperiment::ParseSettings() {
FieldTrialBasedConfig field_trial_config;
return QualityRampupExperiment(&field_trial_config);
}
absl::optional<int> QualityRampupExperiment::MinPixels() const {
return min_pixels_.GetOptional();
}
absl::optional<int> QualityRampupExperiment::MinDurationMs() const {
return min_duration_ms_.GetOptional();
}
absl::optional<double> QualityRampupExperiment::MaxBitrateFactor() const {
return max_bitrate_factor_.GetOptional();
}
void QualityRampupExperiment::SetMaxBitrate(int pixels,
uint32_t max_bitrate_kbps) {
if (!min_pixels_ || pixels < min_pixels_.Value() || max_bitrate_kbps == 0) {
return;
}
max_bitrate_kbps_ = std::max(max_bitrate_kbps_.value_or(0), max_bitrate_kbps);
}
bool QualityRampupExperiment::BwHigh(int64_t now_ms,
uint32_t available_bw_kbps) {
if (!min_pixels_ || !min_duration_ms_ || !max_bitrate_kbps_) {
return false;
}
if (available_bw_kbps <
max_bitrate_kbps_.value() * MaxBitrateFactor().value_or(1)) {
start_ms_.reset();
return false;
}
if (!start_ms_)
start_ms_ = now_ms;
return (now_ms - *start_ms_) >= min_duration_ms_.Value();
}
void QualityRampupExperiment::Reset() {
start_ms_.reset();
max_bitrate_kbps_.reset();
}
bool QualityRampupExperiment::Enabled() const {
return min_pixels_ && min_duration_ms_;
}
} // namespace webrtc

View file

@ -0,0 +1,53 @@
/*
* 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_QUALITY_RAMPUP_EXPERIMENT_H_
#define RTC_BASE_EXPERIMENTS_QUALITY_RAMPUP_EXPERIMENT_H_
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
class QualityRampupExperiment final {
public:
static QualityRampupExperiment ParseSettings();
absl::optional<int> MinPixels() const;
absl::optional<int> MinDurationMs() const;
absl::optional<double> MaxBitrateFactor() const;
// Sets the max bitrate and the frame size.
// The call has no effect if the frame size is less than `min_pixels_`.
void SetMaxBitrate(int pixels, uint32_t max_bitrate_kbps);
// Returns true if the available bandwidth is a certain percentage
// (max_bitrate_factor_) above `max_bitrate_kbps_` for `min_duration_ms_`.
bool BwHigh(int64_t now_ms, uint32_t available_bw_kbps);
void Reset();
bool Enabled() const;
private:
explicit QualityRampupExperiment(
const FieldTrialsView* const key_value_config);
FieldTrialOptional<int> min_pixels_;
FieldTrialOptional<int> min_duration_ms_;
FieldTrialOptional<double> max_bitrate_factor_;
absl::optional<int64_t> start_ms_;
absl::optional<uint32_t> max_bitrate_kbps_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_QUALITY_RAMPUP_EXPERIMENT_H_

View file

@ -0,0 +1,96 @@
/*
* 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 "rtc_base/experiments/quality_scaler_settings.h"
#include "api/field_trials_view.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
const int kMinFrames = 10;
const double kMinScaleFactor = 0.01;
} // namespace
QualityScalerSettings::QualityScalerSettings(
const FieldTrialsView& field_trials)
: sampling_period_ms_("sampling_period_ms"),
average_qp_window_("average_qp_window"),
min_frames_("min_frames"),
initial_scale_factor_("initial_scale_factor"),
scale_factor_("scale_factor"),
initial_bitrate_interval_ms_("initial_bitrate_interval_ms"),
initial_bitrate_factor_("initial_bitrate_factor") {
ParseFieldTrial({&sampling_period_ms_, &average_qp_window_, &min_frames_,
&initial_scale_factor_, &scale_factor_,
&initial_bitrate_interval_ms_, &initial_bitrate_factor_},
field_trials.Lookup("WebRTC-Video-QualityScalerSettings"));
}
absl::optional<int> QualityScalerSettings::SamplingPeriodMs() const {
if (sampling_period_ms_ && sampling_period_ms_.Value() <= 0) {
RTC_LOG(LS_WARNING) << "Unsupported sampling_period_ms value, ignored.";
return absl::nullopt;
}
return sampling_period_ms_.GetOptional();
}
absl::optional<int> QualityScalerSettings::AverageQpWindow() const {
if (average_qp_window_ && average_qp_window_.Value() <= 0) {
RTC_LOG(LS_WARNING) << "Unsupported average_qp_window value, ignored.";
return absl::nullopt;
}
return average_qp_window_.GetOptional();
}
absl::optional<int> QualityScalerSettings::MinFrames() const {
if (min_frames_ && min_frames_.Value() < kMinFrames) {
RTC_LOG(LS_WARNING) << "Unsupported min_frames value, ignored.";
return absl::nullopt;
}
return min_frames_.GetOptional();
}
absl::optional<double> QualityScalerSettings::InitialScaleFactor() const {
if (initial_scale_factor_ &&
initial_scale_factor_.Value() < kMinScaleFactor) {
RTC_LOG(LS_WARNING) << "Unsupported initial_scale_factor value, ignored.";
return absl::nullopt;
}
return initial_scale_factor_.GetOptional();
}
absl::optional<double> QualityScalerSettings::ScaleFactor() const {
if (scale_factor_ && scale_factor_.Value() < kMinScaleFactor) {
RTC_LOG(LS_WARNING) << "Unsupported scale_factor value, ignored.";
return absl::nullopt;
}
return scale_factor_.GetOptional();
}
absl::optional<int> QualityScalerSettings::InitialBitrateIntervalMs() const {
if (initial_bitrate_interval_ms_ &&
initial_bitrate_interval_ms_.Value() < 0) {
RTC_LOG(LS_WARNING) << "Unsupported bitrate_interval value, ignored.";
return absl::nullopt;
}
return initial_bitrate_interval_ms_.GetOptional();
}
absl::optional<double> QualityScalerSettings::InitialBitrateFactor() const {
if (initial_bitrate_factor_ &&
initial_bitrate_factor_.Value() < kMinScaleFactor) {
RTC_LOG(LS_WARNING) << "Unsupported initial_bitrate_factor value, ignored.";
return absl::nullopt;
}
return initial_bitrate_factor_.GetOptional();
}
} // namespace webrtc

View file

@ -0,0 +1,44 @@
/*
* 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_QUALITY_SCALER_SETTINGS_H_
#define RTC_BASE_EXPERIMENTS_QUALITY_SCALER_SETTINGS_H_
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
class QualityScalerSettings final {
public:
explicit QualityScalerSettings(const FieldTrialsView& field_trials);
absl::optional<int> SamplingPeriodMs() const;
absl::optional<int> AverageQpWindow() const;
absl::optional<int> MinFrames() const;
absl::optional<double> InitialScaleFactor() const;
absl::optional<double> ScaleFactor() const;
absl::optional<int> InitialBitrateIntervalMs() const;
absl::optional<double> InitialBitrateFactor() const;
private:
FieldTrialOptional<int> sampling_period_ms_;
FieldTrialOptional<int> average_qp_window_;
FieldTrialOptional<int> min_frames_;
FieldTrialOptional<double> initial_scale_factor_;
FieldTrialOptional<double> scale_factor_;
FieldTrialOptional<int> initial_bitrate_interval_ms_;
FieldTrialOptional<double> initial_bitrate_factor_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_QUALITY_SCALER_SETTINGS_H_

View file

@ -0,0 +1,117 @@
/*
* Copyright 2018 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 "rtc_base/experiments/quality_scaling_experiment.h"
#include <stdio.h>
#include <string>
#include "absl/strings/match.h"
#include "api/field_trials_view.h"
#include "api/transport/field_trial_based_config.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
constexpr char kFieldTrial[] = "WebRTC-Video-QualityScaling";
constexpr int kMinQp = 1;
constexpr int kMaxVp8Qp = 127;
constexpr int kMaxVp9Qp = 255;
constexpr int kMaxH264Qp = 51;
constexpr int kMaxGenericQp = 255;
#if !defined(WEBRTC_IOS)
constexpr char kDefaultQualityScalingSetttings[] =
"Enabled-29,95,149,205,24,37,26,36,0.9995,0.9999,1";
#endif
absl::optional<VideoEncoder::QpThresholds> GetThresholds(int low,
int high,
int max) {
if (low < kMinQp || high > max || high < low)
return absl::nullopt;
RTC_LOG(LS_INFO) << "QP thresholds: low: " << low << ", high: " << high;
return absl::optional<VideoEncoder::QpThresholds>(
VideoEncoder::QpThresholds(low, high));
}
} // namespace
bool QualityScalingExperiment::Enabled(const FieldTrialsView& field_trials) {
#if defined(WEBRTC_IOS)
return absl::StartsWith(field_trials.Lookup(kFieldTrial), "Enabled");
#else
return !absl::StartsWith(field_trials.Lookup(kFieldTrial), "Disabled");
#endif
}
absl::optional<QualityScalingExperiment::Settings>
QualityScalingExperiment::ParseSettings(const FieldTrialsView& field_trials) {
std::string group = field_trials.Lookup(kFieldTrial);
// TODO(http://crbug.com/webrtc/12401): Completely remove the experiment code
// after few releases.
#if !defined(WEBRTC_IOS)
if (group.empty())
group = kDefaultQualityScalingSetttings;
#endif
Settings s;
if (sscanf(group.c_str(), "Enabled-%d,%d,%d,%d,%d,%d,%d,%d,%f,%f,%d",
&s.vp8_low, &s.vp8_high, &s.vp9_low, &s.vp9_high, &s.h264_low,
&s.h264_high, &s.generic_low, &s.generic_high, &s.alpha_high,
&s.alpha_low, &s.drop) != 11) {
RTC_LOG(LS_WARNING) << "Invalid number of parameters provided.";
return absl::nullopt;
}
return s;
}
absl::optional<VideoEncoder::QpThresholds>
QualityScalingExperiment::GetQpThresholds(VideoCodecType codec_type,
const FieldTrialsView& field_trials) {
const auto settings = ParseSettings(field_trials);
if (!settings)
return absl::nullopt;
switch (codec_type) {
case kVideoCodecVP8:
return GetThresholds(settings->vp8_low, settings->vp8_high, kMaxVp8Qp);
case kVideoCodecVP9:
return GetThresholds(settings->vp9_low, settings->vp9_high, kMaxVp9Qp);
case kVideoCodecH265:
// TODO(bugs.webrtc.org/13485): Use H264 QP thresholds for now.
case kVideoCodecH264:
return GetThresholds(settings->h264_low, settings->h264_high, kMaxH264Qp);
case kVideoCodecGeneric:
return GetThresholds(settings->generic_low, settings->generic_high,
kMaxGenericQp);
default:
return absl::nullopt;
}
}
QualityScalingExperiment::Config QualityScalingExperiment::GetConfig(
const FieldTrialsView& field_trials) {
const auto settings = ParseSettings(field_trials);
if (!settings)
return Config();
Config config;
config.use_all_drop_reasons = settings->drop > 0;
if (settings->alpha_high < 0 || settings->alpha_low < settings->alpha_high) {
RTC_LOG(LS_WARNING) << "Invalid alpha value provided, using default.";
return config;
}
config.alpha_high = settings->alpha_high;
config.alpha_low = settings->alpha_low;
return config;
}
} // namespace webrtc

View file

@ -0,0 +1,61 @@
/*
* Copyright 2018 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_QUALITY_SCALING_EXPERIMENT_H_
#define RTC_BASE_EXPERIMENTS_QUALITY_SCALING_EXPERIMENT_H_
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/video_codecs/video_encoder.h"
namespace webrtc {
class QualityScalingExperiment {
public:
struct Settings {
int vp8_low; // VP8: low QP threshold.
int vp8_high; // VP8: high QP threshold.
int vp9_low; // VP9: low QP threshold.
int vp9_high; // VP9: high QP threshold.
int h264_low; // H264: low QP threshold.
int h264_high; // H264: high QP threshold.
int generic_low; // Generic: low QP threshold.
int generic_high; // Generic: high QP threshold.
float alpha_high; // `alpha_` for ExpFilter used when checking high QP.
float alpha_low; // `alpha_` for ExpFilter used when checking low QP.
int drop; // >0 sets `use_all_drop_reasons` to true.
};
// Used by QualityScaler.
struct Config {
float alpha_high = 0.9995f;
float alpha_low = 0.9999f;
// If set, all type of dropped frames are used.
// Otherwise only dropped frames by MediaOptimization are used.
bool use_all_drop_reasons = false;
};
// Returns true if the experiment is enabled.
static bool Enabled(const FieldTrialsView& field_trials);
// Returns settings from field trial.
static absl::optional<Settings> ParseSettings(
const FieldTrialsView& field_trials);
// Returns QpThresholds for the `codec_type`.
static absl::optional<VideoEncoder::QpThresholds> GetQpThresholds(
VideoCodecType codec_type,
const FieldTrialsView& field_trials);
// Returns parsed values. If the parsing fails, default values are returned.
static Config GetConfig(const FieldTrialsView& field_trials);
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_QUALITY_SCALING_EXPERIMENT_H_

View file

@ -0,0 +1,183 @@
/*
* 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 "rtc_base/experiments/rate_control_settings.h"
#include <inttypes.h>
#include <stdio.h>
#include <string>
#include "absl/strings/match.h"
#include "api/transport/field_trial_based_config.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
namespace {
const int kDefaultAcceptedQueueMs = 350;
const int kDefaultMinPushbackTargetBitrateBps = 30000;
const char kCongestionWindowDefaultFieldTrialString[] =
"QueueSize:350,MinBitrate:30000,DropFrame:true";
const char kUseBaseHeavyVp8Tl3RateAllocationFieldTrialName[] =
"WebRTC-UseBaseHeavyVP8TL3RateAllocation";
bool IsEnabled(const FieldTrialsView* const key_value_config,
absl::string_view key) {
return absl::StartsWith(key_value_config->Lookup(key), "Enabled");
}
} // namespace
constexpr char CongestionWindowConfig::kKey[];
std::unique_ptr<StructParametersParser> CongestionWindowConfig::Parser() {
return StructParametersParser::Create("QueueSize", &queue_size_ms, //
"MinBitrate", &min_bitrate_bps,
"InitWin", &initial_data_window,
"DropFrame", &drop_frame_only);
}
// static
CongestionWindowConfig CongestionWindowConfig::Parse(absl::string_view config) {
CongestionWindowConfig res;
res.Parser()->Parse(config);
return res;
}
constexpr char VideoRateControlConfig::kKey[];
std::unique_ptr<StructParametersParser> VideoRateControlConfig::Parser() {
// The empty comments ensures that each pair is on a separate line.
return StructParametersParser::Create(
"pacing_factor", &pacing_factor, //
"alr_probing", &alr_probing, //
"vp8_qp_max", &vp8_qp_max, //
"vp8_min_pixels", &vp8_min_pixels, //
"trust_vp8", &trust_vp8, //
"trust_vp9", &trust_vp9, //
"bitrate_adjuster", &bitrate_adjuster, //
"adjuster_use_headroom", &adjuster_use_headroom, //
"vp8_s0_boost", &vp8_s0_boost, //
"vp8_base_heavy_tl3_alloc", &vp8_base_heavy_tl3_alloc);
}
RateControlSettings::RateControlSettings(
const FieldTrialsView* const key_value_config) {
std::string congestion_window_config =
key_value_config->Lookup(CongestionWindowConfig::kKey).empty()
? kCongestionWindowDefaultFieldTrialString
: key_value_config->Lookup(CongestionWindowConfig::kKey);
congestion_window_config_ =
CongestionWindowConfig::Parse(congestion_window_config);
video_config_.vp8_base_heavy_tl3_alloc = IsEnabled(
key_value_config, kUseBaseHeavyVp8Tl3RateAllocationFieldTrialName);
video_config_.Parser()->Parse(
key_value_config->Lookup(VideoRateControlConfig::kKey));
}
RateControlSettings::~RateControlSettings() = default;
RateControlSettings::RateControlSettings(RateControlSettings&&) = default;
RateControlSettings RateControlSettings::ParseFromFieldTrials() {
FieldTrialBasedConfig field_trial_config;
return RateControlSettings(&field_trial_config);
}
RateControlSettings RateControlSettings::ParseFromKeyValueConfig(
const FieldTrialsView* const key_value_config) {
FieldTrialBasedConfig field_trial_config;
return RateControlSettings(key_value_config ? key_value_config
: &field_trial_config);
}
bool RateControlSettings::UseCongestionWindow() const {
return static_cast<bool>(congestion_window_config_.queue_size_ms);
}
int64_t RateControlSettings::GetCongestionWindowAdditionalTimeMs() const {
return congestion_window_config_.queue_size_ms.value_or(
kDefaultAcceptedQueueMs);
}
bool RateControlSettings::UseCongestionWindowPushback() const {
return congestion_window_config_.queue_size_ms &&
congestion_window_config_.min_bitrate_bps;
}
bool RateControlSettings::UseCongestionWindowDropFrameOnly() const {
return congestion_window_config_.drop_frame_only;
}
uint32_t RateControlSettings::CongestionWindowMinPushbackTargetBitrateBps()
const {
return congestion_window_config_.min_bitrate_bps.value_or(
kDefaultMinPushbackTargetBitrateBps);
}
absl::optional<DataSize>
RateControlSettings::CongestionWindowInitialDataWindow() const {
return congestion_window_config_.initial_data_window;
}
absl::optional<double> RateControlSettings::GetPacingFactor() const {
return video_config_.pacing_factor;
}
bool RateControlSettings::UseAlrProbing() const {
return video_config_.alr_probing;
}
absl::optional<int> RateControlSettings::LibvpxVp8QpMax() const {
if (video_config_.vp8_qp_max &&
(*video_config_.vp8_qp_max < 0 || *video_config_.vp8_qp_max > 63)) {
RTC_LOG(LS_WARNING) << "Unsupported vp8_qp_max_ value, ignored.";
return absl::nullopt;
}
return video_config_.vp8_qp_max;
}
absl::optional<int> RateControlSettings::LibvpxVp8MinPixels() const {
if (video_config_.vp8_min_pixels && *video_config_.vp8_min_pixels < 1) {
return absl::nullopt;
}
return video_config_.vp8_min_pixels;
}
bool RateControlSettings::LibvpxVp8TrustedRateController() const {
return video_config_.trust_vp8;
}
bool RateControlSettings::Vp8BoostBaseLayerQuality() const {
return video_config_.vp8_s0_boost;
}
bool RateControlSettings::LibvpxVp9TrustedRateController() const {
return video_config_.trust_vp9;
}
bool RateControlSettings::Vp8BaseHeavyTl3RateAllocation() const {
return video_config_.vp8_base_heavy_tl3_alloc;
}
bool RateControlSettings::UseEncoderBitrateAdjuster() const {
return video_config_.bitrate_adjuster;
}
bool RateControlSettings::BitrateAdjusterCanUseNetworkHeadroom() const {
return video_config_.adjuster_use_headroom;
}
} // namespace webrtc

View file

@ -0,0 +1,93 @@
/*
* 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_RATE_CONTROL_SETTINGS_H_
#define RTC_BASE_EXPERIMENTS_RATE_CONTROL_SETTINGS_H_
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/units/data_size.h"
#include "api/video_codecs/video_codec.h"
#include "rtc_base/experiments/struct_parameters_parser.h"
#include "video/config/video_encoder_config.h"
namespace webrtc {
struct CongestionWindowConfig {
static constexpr char kKey[] = "WebRTC-CongestionWindow";
absl::optional<int> queue_size_ms;
absl::optional<int> min_bitrate_bps;
absl::optional<DataSize> initial_data_window;
bool drop_frame_only = false;
std::unique_ptr<StructParametersParser> Parser();
static CongestionWindowConfig Parse(absl::string_view config);
};
struct VideoRateControlConfig {
static constexpr char kKey[] = "WebRTC-VideoRateControl";
absl::optional<double> pacing_factor;
bool alr_probing = false;
absl::optional<int> vp8_qp_max;
absl::optional<int> vp8_min_pixels;
bool trust_vp8 = true;
bool trust_vp9 = true;
bool bitrate_adjuster = true;
bool adjuster_use_headroom = true;
bool vp8_s0_boost = false;
bool vp8_base_heavy_tl3_alloc = false;
std::unique_ptr<StructParametersParser> Parser();
};
class RateControlSettings final {
public:
~RateControlSettings();
RateControlSettings(RateControlSettings&&);
static RateControlSettings ParseFromFieldTrials();
static RateControlSettings ParseFromKeyValueConfig(
const FieldTrialsView* const key_value_config);
// When CongestionWindowPushback is enabled, the pacer is oblivious to
// the congestion window. The relation between outstanding data and
// the congestion window affects encoder allocations directly.
bool UseCongestionWindow() const;
int64_t GetCongestionWindowAdditionalTimeMs() const;
bool UseCongestionWindowPushback() const;
bool UseCongestionWindowDropFrameOnly() const;
uint32_t CongestionWindowMinPushbackTargetBitrateBps() const;
absl::optional<DataSize> CongestionWindowInitialDataWindow() const;
absl::optional<double> GetPacingFactor() const;
bool UseAlrProbing() const;
absl::optional<int> LibvpxVp8QpMax() const;
absl::optional<int> LibvpxVp8MinPixels() const;
bool LibvpxVp8TrustedRateController() const;
bool Vp8BoostBaseLayerQuality() const;
bool Vp8DynamicRateSettings() const;
bool LibvpxVp9TrustedRateController() const;
bool Vp9DynamicRateSettings() const;
bool Vp8BaseHeavyTl3RateAllocation() const;
bool UseEncoderBitrateAdjuster() const;
bool BitrateAdjusterCanUseNetworkHeadroom() const;
private:
explicit RateControlSettings(const FieldTrialsView* const key_value_config);
CongestionWindowConfig congestion_window_config_;
VideoRateControlConfig video_config_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_RATE_CONTROL_SETTINGS_H_

View file

@ -0,0 +1,39 @@
/*
* Copyright 2018 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 "rtc_base/experiments/rtt_mult_experiment.h"
#include <stdio.h>
#include <algorithm>
#include <string>
#include "rtc_base/logging.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
namespace {
const char kRttMultExperiment[] = "WebRTC-RttMult";
} // namespace
bool RttMultExperiment::RttMultEnabled() {
return !field_trial::IsDisabled(kRttMultExperiment);
}
absl::optional<RttMultExperiment::Settings>
RttMultExperiment::GetRttMultValue() {
if (!RttMultExperiment::RttMultEnabled()) {
return absl::nullopt;
}
return RttMultExperiment::Settings{.rtt_mult_setting = 0.9,
.rtt_mult_add_cap_ms = 200.0};
}
} // namespace webrtc

View file

@ -0,0 +1,35 @@
/*
* Copyright 2018 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_RTT_MULT_EXPERIMENT_H_
#define RTC_BASE_EXPERIMENTS_RTT_MULT_EXPERIMENT_H_
#include "absl/types/optional.h"
namespace webrtc {
class RttMultExperiment {
public:
struct Settings {
float rtt_mult_setting; // Jitter buffer size is increased by this factor
// times the estimated RTT.
float rtt_mult_add_cap_ms; // Jitter buffer size increase is capped by this
// value.
};
// Returns true if the experiment is enabled.
static bool RttMultEnabled();
// Returns rtt_mult value and rtt_mult addition cap value from field trial.
static absl::optional<RttMultExperiment::Settings> GetRttMultValue();
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_RTT_MULT_EXPERIMENT_H_

View file

@ -0,0 +1,63 @@
/*
* Copyright 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 "rtc_base/experiments/stable_target_rate_experiment.h"
#include "api/transport/field_trial_based_config.h"
namespace webrtc {
namespace {
constexpr char kFieldTrialName[] = "WebRTC-StableTargetRate";
} // namespace
StableTargetRateExperiment::StableTargetRateExperiment(
const FieldTrialsView* const key_value_config,
double default_video_hysteresis,
double default_screenshare_hysteresis)
: enabled_("enabled", false),
video_hysteresis_factor_("video_hysteresis_factor",
default_video_hysteresis),
screenshare_hysteresis_factor_("screenshare_hysteresis_factor",
default_screenshare_hysteresis) {
ParseFieldTrial(
{&enabled_, &video_hysteresis_factor_, &screenshare_hysteresis_factor_},
key_value_config->Lookup(kFieldTrialName));
}
StableTargetRateExperiment::StableTargetRateExperiment(
const StableTargetRateExperiment&) = default;
StableTargetRateExperiment::StableTargetRateExperiment(
StableTargetRateExperiment&&) = default;
StableTargetRateExperiment StableTargetRateExperiment::ParseFromFieldTrials() {
FieldTrialBasedConfig config;
return ParseFromKeyValueConfig(&config);
}
StableTargetRateExperiment StableTargetRateExperiment::ParseFromKeyValueConfig(
const FieldTrialsView* const key_value_config) {
return StableTargetRateExperiment(key_value_config,
/*default_video_hysteresis=*/1.2,
/*default_screenshare_hysteresis=*/1.35);
}
bool StableTargetRateExperiment::IsEnabled() const {
return enabled_.Get();
}
double StableTargetRateExperiment::GetVideoHysteresisFactor() const {
return video_hysteresis_factor_.Get();
}
double StableTargetRateExperiment::GetScreenshareHysteresisFactor() const {
return screenshare_hysteresis_factor_.Get();
}
} // namespace webrtc

View file

@ -0,0 +1,44 @@
/*
* Copyright 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_STABLE_TARGET_RATE_EXPERIMENT_H_
#define RTC_BASE_EXPERIMENTS_STABLE_TARGET_RATE_EXPERIMENT_H_
#include "api/field_trials_view.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
class StableTargetRateExperiment {
public:
StableTargetRateExperiment(const StableTargetRateExperiment&);
StableTargetRateExperiment(StableTargetRateExperiment&&);
static StableTargetRateExperiment ParseFromFieldTrials();
static StableTargetRateExperiment ParseFromKeyValueConfig(
const FieldTrialsView* const key_value_config);
bool IsEnabled() const;
double GetVideoHysteresisFactor() const;
double GetScreenshareHysteresisFactor() const;
private:
explicit StableTargetRateExperiment(
const FieldTrialsView* const key_value_config,
double default_video_hysteresis,
double default_screenshare_hysteresis);
FieldTrialParameter<bool> enabled_;
FieldTrialParameter<double> video_hysteresis_factor_;
FieldTrialParameter<double> screenshare_hysteresis_factor_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_STABLE_TARGET_RATE_EXPERIMENT_H_

View file

@ -0,0 +1,135 @@
/*
* 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 "rtc_base/experiments/struct_parameters_parser.h"
#include <algorithm>
#include "absl/strings/string_view.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
size_t FindOrEnd(absl::string_view str, size_t start, char delimiter) {
size_t pos = str.find(delimiter, start);
pos = (pos == absl::string_view::npos) ? str.length() : pos;
return pos;
}
} // namespace
namespace struct_parser_impl {
namespace {
inline void StringEncode(std::string* target, bool val) {
*target += rtc::ToString(val);
}
inline void StringEncode(std::string* target, double val) {
*target += rtc::ToString(val);
}
inline void StringEncode(std::string* target, int val) {
*target += rtc::ToString(val);
}
inline void StringEncode(std::string* target, unsigned val) {
*target += rtc::ToString(val);
}
inline void StringEncode(std::string* target, DataRate val) {
*target += webrtc::ToString(val);
}
inline void StringEncode(std::string* target, DataSize val) {
*target += webrtc::ToString(val);
}
inline void StringEncode(std::string* target, TimeDelta val) {
*target += webrtc::ToString(val);
}
template <typename T>
inline void StringEncode(std::string* sb, absl::optional<T> val) {
if (val)
StringEncode(sb, *val);
}
} // namespace
template <typename T>
bool TypedParser<T>::Parse(absl::string_view src, void* target) {
auto parsed = ParseTypedParameter<T>(std::string(src));
if (parsed.has_value())
*reinterpret_cast<T*>(target) = *parsed;
return parsed.has_value();
}
template <typename T>
void TypedParser<T>::Encode(const void* src, std::string* target) {
StringEncode(target, *reinterpret_cast<const T*>(src));
}
template class TypedParser<bool>;
template class TypedParser<double>;
template class TypedParser<int>;
template class TypedParser<unsigned>;
template class TypedParser<absl::optional<double>>;
template class TypedParser<absl::optional<int>>;
template class TypedParser<absl::optional<unsigned>>;
template class TypedParser<DataRate>;
template class TypedParser<DataSize>;
template class TypedParser<TimeDelta>;
template class TypedParser<absl::optional<DataRate>>;
template class TypedParser<absl::optional<DataSize>>;
template class TypedParser<absl::optional<TimeDelta>>;
} // namespace struct_parser_impl
StructParametersParser::StructParametersParser(
std::vector<struct_parser_impl::MemberParameter> members)
: members_(std::move(members)) {}
void StructParametersParser::Parse(absl::string_view src) {
size_t i = 0;
while (i < src.length()) {
size_t val_end = FindOrEnd(src, i, ',');
size_t colon_pos = FindOrEnd(src, i, ':');
size_t key_end = std::min(val_end, colon_pos);
size_t val_begin = key_end + 1u;
absl::string_view key(src.substr(i, key_end - i));
absl::string_view opt_value;
if (val_end >= val_begin)
opt_value = src.substr(val_begin, val_end - val_begin);
i = val_end + 1u;
bool found = false;
for (auto& member : members_) {
if (key == member.key) {
found = true;
if (!member.parser.parse(opt_value, member.member_ptr)) {
RTC_LOG(LS_WARNING) << "Failed to read field with key: '" << key
<< "' in trial: \"" << src << "\"";
}
break;
}
}
// "_" is be used to prefix keys that are part of the string for
// debugging purposes but not neccessarily used.
// e.g. WebRTC-Experiment/param: value, _DebuggingString
if (!found && (key.empty() || key[0] != '_')) {
RTC_LOG(LS_INFO) << "No field with key: '" << key
<< "' (found in trial: \"" << src << "\")";
}
}
}
std::string StructParametersParser::Encode() const {
std::string res;
bool first = true;
for (const auto& member : members_) {
if (!first)
res += ",";
res += member.key;
res += ":";
member.parser.encode(member.member_ptr, &res);
first = false;
}
return res;
}
} // namespace webrtc

View file

@ -0,0 +1,110 @@
/*
* 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_STRUCT_PARAMETERS_PARSER_H_
#define RTC_BASE_EXPERIMENTS_STRUCT_PARAMETERS_PARSER_H_
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/experiments/field_trial_units.h"
#include "rtc_base/string_encode.h"
namespace webrtc {
namespace struct_parser_impl {
struct TypedMemberParser {
public:
bool (*parse)(absl::string_view src, void* target);
void (*encode)(const void* src, std::string* target);
};
struct MemberParameter {
const char* key;
void* member_ptr;
TypedMemberParser parser;
};
template <typename T>
class TypedParser {
public:
static bool Parse(absl::string_view src, void* target);
static void Encode(const void* src, std::string* target);
};
// Instantiated in cc file to avoid duplication during compile. Add additional
// parsers as needed. Generally, try to use these suggested types even if the
// context where the value is used might require a different type. For instance,
// a size_t representing a packet size should use an int parameter as there's no
// need to support packet sizes larger than INT32_MAX.
extern template class TypedParser<bool>;
extern template class TypedParser<double>;
extern template class TypedParser<int>;
extern template class TypedParser<unsigned>;
extern template class TypedParser<absl::optional<double>>;
extern template class TypedParser<absl::optional<int>>;
extern template class TypedParser<absl::optional<unsigned>>;
extern template class TypedParser<DataRate>;
extern template class TypedParser<DataSize>;
extern template class TypedParser<TimeDelta>;
extern template class TypedParser<absl::optional<DataRate>>;
extern template class TypedParser<absl::optional<DataSize>>;
extern template class TypedParser<absl::optional<TimeDelta>>;
template <typename T>
void AddMembers(MemberParameter* out, const char* key, T* member) {
*out = MemberParameter{
key, member,
TypedMemberParser{&TypedParser<T>::Parse, &TypedParser<T>::Encode}};
}
template <typename T, typename... Args>
void AddMembers(MemberParameter* out,
const char* key,
T* member,
Args... args) {
AddMembers(out, key, member);
AddMembers(++out, args...);
}
} // namespace struct_parser_impl
class StructParametersParser {
public:
template <typename T, typename... Args>
static std::unique_ptr<StructParametersParser> Create(const char* first_key,
T* first_member,
Args... args) {
std::vector<struct_parser_impl::MemberParameter> members(
sizeof...(args) / 2 + 1);
struct_parser_impl::AddMembers(&members.front(), std::move(first_key),
first_member, args...);
return absl::WrapUnique(new StructParametersParser(std::move(members)));
}
void Parse(absl::string_view src);
std::string Encode() const;
private:
explicit StructParametersParser(
std::vector<struct_parser_impl::MemberParameter> members);
std::vector<struct_parser_impl::MemberParameter> members_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_STRUCT_PARAMETERS_PARSER_H_

View file

@ -0,0 +1,62 @@
/*
* Copyright 2016 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 "rtc_base/fake_clock.h"
#include "rtc_base/checks.h"
#include "rtc_base/thread.h"
namespace rtc {
int64_t FakeClock::TimeNanos() const {
webrtc::MutexLock lock(&lock_);
return time_ns_;
}
void FakeClock::SetTime(webrtc::Timestamp new_time) {
webrtc::MutexLock lock(&lock_);
RTC_DCHECK(new_time.us() * 1000 >= time_ns_);
time_ns_ = new_time.us() * 1000;
}
void FakeClock::AdvanceTime(webrtc::TimeDelta delta) {
webrtc::MutexLock lock(&lock_);
time_ns_ += delta.ns();
}
void ThreadProcessingFakeClock::SetTime(webrtc::Timestamp time) {
clock_.SetTime(time);
// If message queues are waiting in a socket select() with a timeout provided
// by the OS, they should wake up and dispatch all messages that are ready.
ThreadManager::ProcessAllMessageQueuesForTesting();
}
void ThreadProcessingFakeClock::AdvanceTime(webrtc::TimeDelta delta) {
clock_.AdvanceTime(delta);
ThreadManager::ProcessAllMessageQueuesForTesting();
}
ScopedBaseFakeClock::ScopedBaseFakeClock() {
prev_clock_ = SetClockForTesting(this);
}
ScopedBaseFakeClock::~ScopedBaseFakeClock() {
SetClockForTesting(prev_clock_);
}
ScopedFakeClock::ScopedFakeClock() {
prev_clock_ = SetClockForTesting(this);
}
ScopedFakeClock::~ScopedFakeClock() {
SetClockForTesting(prev_clock_);
}
} // namespace rtc

View file

@ -0,0 +1,83 @@
/*
* Copyright 2016 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.
*/
#ifndef RTC_BASE_FAKE_CLOCK_H_
#define RTC_BASE_FAKE_CLOCK_H_
#include <stdint.h>
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h"
#include "rtc_base/time_utils.h"
namespace rtc {
// Fake clock for use with unit tests, which does not tick on its own.
// Starts at time 0.
//
// TODO(deadbeef): Unify with webrtc::SimulatedClock.
class FakeClock : public ClockInterface {
public:
FakeClock() = default;
FakeClock(const FakeClock&) = delete;
FakeClock& operator=(const FakeClock&) = delete;
~FakeClock() override = default;
// ClockInterface implementation.
int64_t TimeNanos() const override;
// Methods that can be used by the test to control the time.
// Should only be used to set a time in the future.
void SetTime(webrtc::Timestamp new_time);
void AdvanceTime(webrtc::TimeDelta delta);
private:
mutable webrtc::Mutex lock_;
int64_t time_ns_ RTC_GUARDED_BY(lock_) = 0;
};
class ThreadProcessingFakeClock : public ClockInterface {
public:
int64_t TimeNanos() const override { return clock_.TimeNanos(); }
void SetTime(webrtc::Timestamp time);
void AdvanceTime(webrtc::TimeDelta delta);
private:
FakeClock clock_;
};
// Helper class that sets itself as the global clock in its constructor and
// unsets it in its destructor.
class ScopedBaseFakeClock : public FakeClock {
public:
ScopedBaseFakeClock();
~ScopedBaseFakeClock() override;
private:
ClockInterface* prev_clock_;
};
// TODO(srte): Rename this to reflect that it also does thread processing.
class ScopedFakeClock : public ThreadProcessingFakeClock {
public:
ScopedFakeClock();
~ScopedFakeClock() override;
private:
ClockInterface* prev_clock_;
};
} // namespace rtc
#endif // RTC_BASE_FAKE_CLOCK_H_

Some files were not shown because too many files have changed in this diff Show more