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,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.
*/
#include "modules/rtp_rtcp/source/absolute_capture_time_interpolator.h"
#include <limits>
#include "rtc_base/checks.h"
namespace webrtc {
AbsoluteCaptureTimeInterpolator::AbsoluteCaptureTimeInterpolator(Clock* clock)
: clock_(clock) {}
uint32_t AbsoluteCaptureTimeInterpolator::GetSource(
uint32_t ssrc,
rtc::ArrayView<const uint32_t> csrcs) {
if (csrcs.empty()) {
return ssrc;
}
return csrcs[0];
}
absl::optional<AbsoluteCaptureTime>
AbsoluteCaptureTimeInterpolator::OnReceivePacket(
uint32_t source,
uint32_t rtp_timestamp,
int rtp_clock_frequency_hz,
const absl::optional<AbsoluteCaptureTime>& received_extension) {
const Timestamp receive_time = clock_->CurrentTime();
MutexLock lock(&mutex_);
if (received_extension == absl::nullopt) {
if (!ShouldInterpolateExtension(receive_time, source, rtp_timestamp,
rtp_clock_frequency_hz)) {
last_receive_time_ = Timestamp::MinusInfinity();
return absl::nullopt;
}
return AbsoluteCaptureTime{
.absolute_capture_timestamp = InterpolateAbsoluteCaptureTimestamp(
rtp_timestamp, rtp_clock_frequency_hz, last_rtp_timestamp_,
last_received_extension_.absolute_capture_timestamp),
.estimated_capture_clock_offset =
last_received_extension_.estimated_capture_clock_offset,
};
} else {
last_source_ = source;
last_rtp_timestamp_ = rtp_timestamp;
last_rtp_clock_frequency_hz_ = rtp_clock_frequency_hz;
last_received_extension_ = *received_extension;
last_receive_time_ = receive_time;
return received_extension;
}
}
uint64_t AbsoluteCaptureTimeInterpolator::InterpolateAbsoluteCaptureTimestamp(
uint32_t rtp_timestamp,
int rtp_clock_frequency_hz,
uint32_t last_rtp_timestamp,
uint64_t last_absolute_capture_timestamp) {
RTC_DCHECK_GT(rtp_clock_frequency_hz, 0);
return last_absolute_capture_timestamp +
static_cast<int64_t>(uint64_t{rtp_timestamp - last_rtp_timestamp}
<< 32) /
rtp_clock_frequency_hz;
}
bool AbsoluteCaptureTimeInterpolator::ShouldInterpolateExtension(
Timestamp receive_time,
uint32_t source,
uint32_t rtp_timestamp,
int rtp_clock_frequency_hz) const {
// Shouldn't if the last received extension is not eligible for interpolation,
// in particular if we don't have a previously received extension stored.
if (receive_time - last_receive_time_ > kInterpolationMaxInterval) {
return false;
}
// Shouldn't if the source has changed.
if (last_source_ != source) {
return false;
}
// Shouldn't if the RTP clock frequency has changed.
if (last_rtp_clock_frequency_hz_ != rtp_clock_frequency_hz) {
return false;
}
// Shouldn't if the RTP clock frequency is invalid.
if (rtp_clock_frequency_hz <= 0) {
return false;
}
return true;
}
} // namespace webrtc

View file

@ -0,0 +1,87 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_ABSOLUTE_CAPTURE_TIME_INTERPOLATOR_H_
#define MODULES_RTP_RTCP_SOURCE_ABSOLUTE_CAPTURE_TIME_INTERPOLATOR_H_
#include "api/array_view.h"
#include "api/rtp_headers.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 "system_wrappers/include/clock.h"
namespace webrtc {
//
// Helper class for interpolating the `AbsoluteCaptureTime` header extension.
//
// Supports the "timestamp interpolation" optimization:
// A receiver SHOULD memorize the capture system (i.e. CSRC/SSRC), capture
// timestamp, and RTP timestamp of the most recently received abs-capture-time
// packet on each received stream. It can then use that information, in
// combination with RTP timestamps of packets without abs-capture-time, to
// extrapolate missing capture timestamps.
//
// See: https://webrtc.org/experiments/rtp-hdrext/abs-capture-time/
//
class AbsoluteCaptureTimeInterpolator {
public:
static constexpr TimeDelta kInterpolationMaxInterval = TimeDelta::Seconds(5);
explicit AbsoluteCaptureTimeInterpolator(Clock* clock);
// Returns the source (i.e. SSRC or CSRC) of the capture system.
static uint32_t GetSource(uint32_t ssrc,
rtc::ArrayView<const uint32_t> csrcs);
// Returns a received header extension, an interpolated header extension, or
// `absl::nullopt` if it's not possible to interpolate a header extension.
absl::optional<AbsoluteCaptureTime> OnReceivePacket(
uint32_t source,
uint32_t rtp_timestamp,
int rtp_clock_frequency_hz,
const absl::optional<AbsoluteCaptureTime>& received_extension);
private:
friend class AbsoluteCaptureTimeSender;
static uint64_t InterpolateAbsoluteCaptureTimestamp(
uint32_t rtp_timestamp,
int rtp_clock_frequency_hz,
uint32_t last_rtp_timestamp,
uint64_t last_absolute_capture_timestamp);
bool ShouldInterpolateExtension(Timestamp receive_time,
uint32_t source,
uint32_t rtp_timestamp,
int rtp_clock_frequency_hz) const
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
Clock* const clock_;
Mutex mutex_;
// Time of the last received header extension eligible for interpolation,
// MinusInfinity() if no extension was received, or last received one is
// not eligible for interpolation.
Timestamp last_receive_time_ RTC_GUARDED_BY(mutex_) =
Timestamp::MinusInfinity();
uint32_t last_source_ RTC_GUARDED_BY(mutex_);
uint32_t last_rtp_timestamp_ RTC_GUARDED_BY(mutex_);
int last_rtp_clock_frequency_hz_ RTC_GUARDED_BY(mutex_);
AbsoluteCaptureTime last_received_extension_ RTC_GUARDED_BY(mutex_);
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_ABSOLUTE_CAPTURE_TIME_INTERPOLATOR_H_

View file

@ -0,0 +1,121 @@
/*
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/rtp_rtcp/source/absolute_capture_time_sender.h"
#include <limits>
#include "modules/rtp_rtcp/source/absolute_capture_time_interpolator.h"
#include "system_wrappers/include/ntp_time.h"
namespace webrtc {
static_assert(
AbsoluteCaptureTimeInterpolator::kInterpolationMaxInterval >=
AbsoluteCaptureTimeSender::kInterpolationMaxInterval,
"Receivers should be as willing to interpolate timestamps as senders.");
AbsoluteCaptureTimeSender::AbsoluteCaptureTimeSender(Clock* clock)
: clock_(clock) {}
uint32_t AbsoluteCaptureTimeSender::GetSource(
uint32_t ssrc,
rtc::ArrayView<const uint32_t> csrcs) {
return AbsoluteCaptureTimeInterpolator::GetSource(ssrc, csrcs);
}
absl::optional<AbsoluteCaptureTime> AbsoluteCaptureTimeSender::OnSendPacket(
uint32_t source,
uint32_t rtp_timestamp,
uint32_t rtp_clock_frequency,
uint64_t absolute_capture_timestamp,
absl::optional<int64_t> estimated_capture_clock_offset) {
return OnSendPacket(source, rtp_timestamp, rtp_clock_frequency,
NtpTime(absolute_capture_timestamp),
estimated_capture_clock_offset, /*force=*/false);
}
absl::optional<AbsoluteCaptureTime> AbsoluteCaptureTimeSender::OnSendPacket(
uint32_t source,
uint32_t rtp_timestamp,
int rtp_clock_frequency_hz,
NtpTime absolute_capture_time,
absl::optional<int64_t> estimated_capture_clock_offset,
bool force) {
Timestamp send_time = clock_->CurrentTime();
if (!(force || ShouldSendExtension(
send_time, source, rtp_timestamp, rtp_clock_frequency_hz,
absolute_capture_time, estimated_capture_clock_offset))) {
return absl::nullopt;
}
last_source_ = source;
last_rtp_timestamp_ = rtp_timestamp;
last_rtp_clock_frequency_hz_ = rtp_clock_frequency_hz;
last_absolute_capture_time_ = absolute_capture_time;
last_estimated_capture_clock_offset_ = estimated_capture_clock_offset;
last_send_time_ = send_time;
return AbsoluteCaptureTime{
.absolute_capture_timestamp = uint64_t{absolute_capture_time},
.estimated_capture_clock_offset = estimated_capture_clock_offset,
};
}
bool AbsoluteCaptureTimeSender::ShouldSendExtension(
Timestamp send_time,
uint32_t source,
uint32_t rtp_timestamp,
int rtp_clock_frequency_hz,
NtpTime absolute_capture_time,
absl::optional<int64_t> estimated_capture_clock_offset) const {
// Should if the last sent extension is too old, in particular if we've never
// sent anything before.
if (send_time - last_send_time_ > kInterpolationMaxInterval) {
return true;
}
// Should if the source has changed.
if (last_source_ != source) {
return true;
}
// Should if the RTP clock frequency has changed.
if (last_rtp_clock_frequency_hz_ != rtp_clock_frequency_hz) {
return true;
}
// Should if the RTP clock frequency is invalid.
if (rtp_clock_frequency_hz <= 0) {
return true;
}
// Should if the estimated capture clock offset has changed.
if (last_estimated_capture_clock_offset_ != estimated_capture_clock_offset) {
return true;
}
// Should if interpolation would introduce too much error.
const uint64_t interpolated_absolute_capture_timestamp =
AbsoluteCaptureTimeInterpolator::InterpolateAbsoluteCaptureTimestamp(
rtp_timestamp, rtp_clock_frequency_hz, last_rtp_timestamp_,
uint64_t{last_absolute_capture_time_});
const uint64_t absolute_capture_timestamp = uint64_t{absolute_capture_time};
const int64_t interpolation_error_ms = UQ32x32ToInt64Ms(std::min(
interpolated_absolute_capture_timestamp - absolute_capture_timestamp,
absolute_capture_timestamp - interpolated_absolute_capture_timestamp));
if (interpolation_error_ms > kInterpolationMaxError.ms()) {
return true;
}
return false;
}
} // namespace webrtc

View file

@ -0,0 +1,107 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_ABSOLUTE_CAPTURE_TIME_SENDER_H_
#define MODULES_RTP_RTCP_SOURCE_ABSOLUTE_CAPTURE_TIME_SENDER_H_
#include "api/array_view.h"
#include "api/rtp_headers.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "system_wrappers/include/clock.h"
#include "system_wrappers/include/ntp_time.h"
namespace webrtc {
//
// Helper class for sending the `AbsoluteCaptureTime` header extension.
//
// Supports the "timestamp interpolation" optimization:
// A sender SHOULD save bandwidth by not sending abs-capture-time with every
// RTP packet. It SHOULD still send them at regular intervals (e.g. every
// second) to help mitigate the impact of clock drift and packet loss. Mixers
// SHOULD always send abs-capture-time with the first RTP packet after
// changing capture system.
//
// Timestamp interpolation works fine as long as theres reasonably low
// NTP/RTP clock drift. This is not always true. Senders that detect “jumps”
// between its NTP and RTP clock mappings SHOULD send abs-capture-time with
// the first RTP packet after such a thing happening.
//
// See: https://webrtc.org/experiments/rtp-hdrext/abs-capture-time/
//
class AbsoluteCaptureTimeSender {
public:
static constexpr TimeDelta kInterpolationMaxInterval = TimeDelta::Seconds(1);
static constexpr TimeDelta kInterpolationMaxError = TimeDelta::Millis(1);
explicit AbsoluteCaptureTimeSender(Clock* clock);
// Returns the source (i.e. SSRC or CSRC) of the capture system.
static uint32_t GetSource(uint32_t ssrc,
rtc::ArrayView<const uint32_t> csrcs);
// Returns value to write into AbsoluteCaptureTime RTP header extension to be
// sent, or `absl::nullopt` if the header extension shouldn't be attached to
// the outgoing packet.
//
// - `source` - id of the capture system.
// - `rtp_timestamp` - capture time represented as rtp timestamp in the
// outgoing packet
// - `rtp_clock_frequency_hz` - description of the `rtp_timestamp` units -
// `rtp_timetamp` delta of `rtp_clock_freqnecy_hz` represents 1 second.
// - `absolute_capture_time` - time when a frame was captured by the capture
// system.
// - `estimated_capture_clock_offset` - estimated offset between capture
// system clock and local `clock` passed as the AbsoluteCaptureTimeSender
// construction paramter. Uses the same units as `absolute_capture_time`,
// i.e. delta of 2^32 represents 1 second. See AbsoluteCaptureTime type
// comments for more details.
// - `force` - when set to true, OnSendPacket is forced to return non-nullopt.
absl::optional<AbsoluteCaptureTime> OnSendPacket(
uint32_t source,
uint32_t rtp_timestamp,
int rtp_clock_frequency_hz,
NtpTime absolute_capture_time,
absl::optional<int64_t> estimated_capture_clock_offset,
bool force = false);
// Returns a header extension to be sent, or `absl::nullopt` if the header
// extension shouldn't be sent.
[[deprecated]] absl::optional<AbsoluteCaptureTime> OnSendPacket(
uint32_t source,
uint32_t rtp_timestamp,
uint32_t rtp_clock_frequency,
uint64_t absolute_capture_timestamp,
absl::optional<int64_t> estimated_capture_clock_offset);
private:
bool ShouldSendExtension(
Timestamp send_time,
uint32_t source,
uint32_t rtp_timestamp,
int rtp_clock_frequency_hz,
NtpTime absolute_capture_time,
absl::optional<int64_t> estimated_capture_clock_offset) const;
Clock* const clock_;
Timestamp last_send_time_ = Timestamp::MinusInfinity();
uint32_t last_source_;
uint32_t last_rtp_timestamp_;
int last_rtp_clock_frequency_hz_;
NtpTime last_absolute_capture_time_;
absl::optional<int64_t> last_estimated_capture_clock_offset_;
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_ABSOLUTE_CAPTURE_TIME_SENDER_H_

View file

@ -0,0 +1,124 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/source/active_decode_targets_helper.h"
#include <stdint.h>
#include "api/array_view.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
// Returns mask of ids of chains previous frame is part of.
// Assumes for each chain frames are seen in order and no frame on any chain is
// missing. That assumptions allows a simple detection when previous frame is
// part of a chain.
std::bitset<32> LastSendOnChain(int frame_diff,
rtc::ArrayView<const int> chain_diffs) {
std::bitset<32> bitmask = 0;
for (size_t i = 0; i < chain_diffs.size(); ++i) {
if (frame_diff == chain_diffs[i]) {
bitmask.set(i);
}
}
return bitmask;
}
// Returns bitmask with first `num` bits set to 1.
std::bitset<32> AllActive(size_t num) {
RTC_DCHECK_LE(num, 32);
return (~uint32_t{0}) >> (32 - num);
}
// Returns bitmask of chains that protect at least one active decode target.
std::bitset<32> ActiveChains(
rtc::ArrayView<const int> decode_target_protected_by_chain,
int num_chains,
std::bitset<32> active_decode_targets) {
std::bitset<32> active_chains = 0;
for (size_t dt = 0; dt < decode_target_protected_by_chain.size(); ++dt) {
if (dt < active_decode_targets.size() && !active_decode_targets[dt]) {
continue;
}
int chain_idx = decode_target_protected_by_chain[dt];
RTC_DCHECK_LT(chain_idx, num_chains);
active_chains.set(chain_idx);
}
return active_chains;
}
} // namespace
void ActiveDecodeTargetsHelper::OnFrame(
rtc::ArrayView<const int> decode_target_protected_by_chain,
std::bitset<32> active_decode_targets,
bool is_keyframe,
int64_t frame_id,
rtc::ArrayView<const int> chain_diffs) {
const int num_chains = chain_diffs.size();
if (num_chains == 0) {
// Avoid printing the warning
// when already printed the warning for the same active decode targets, or
// when active_decode_targets are not changed from it's default value of
// all are active, including non-existent decode targets.
if (last_active_decode_targets_ != active_decode_targets &&
!active_decode_targets.all()) {
RTC_LOG(LS_WARNING) << "No chains are configured, but some decode "
"targets might be inactive. Unsupported.";
}
last_active_decode_targets_ = active_decode_targets;
return;
}
const size_t num_decode_targets = decode_target_protected_by_chain.size();
RTC_DCHECK_GT(num_decode_targets, 0);
std::bitset<32> all_decode_targets = AllActive(num_decode_targets);
// Default value for active_decode_targets is 'all are active', i.e. all bits
// are set. Default value is set before number of decode targets is known.
// It is up to this helper to make the value cleaner and unset unused bits.
active_decode_targets &= all_decode_targets;
if (is_keyframe) {
// Key frame resets the state.
last_active_decode_targets_ = all_decode_targets;
last_active_chains_ = AllActive(num_chains);
unsent_on_chain_.reset();
} else {
// Update state assuming previous frame was sent.
unsent_on_chain_ &=
~LastSendOnChain(frame_id - last_frame_id_, chain_diffs);
}
// Save for the next call to OnFrame.
// Though usually `frame_id == last_frame_id_ + 1`, it might not be so when
// frame id space is shared by several simulcast rtp streams.
last_frame_id_ = frame_id;
if (active_decode_targets == last_active_decode_targets_) {
return;
}
last_active_decode_targets_ = active_decode_targets;
if (active_decode_targets.none()) {
RTC_LOG(LS_ERROR) << "It is invalid to produce a frame (" << frame_id
<< ") while there are no active decode targets";
return;
}
last_active_chains_ = ActiveChains(decode_target_protected_by_chain,
num_chains, active_decode_targets);
// Frames that are part of inactive chains might not be produced by the
// encoder. Thus stop sending `active_decode_target` bitmask when it is sent
// on all active chains rather than on all chains.
unsent_on_chain_ = last_active_chains_;
RTC_DCHECK(!unsent_on_chain_.none());
}
} // namespace webrtc

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_ACTIVE_DECODE_TARGETS_HELPER_H_
#define MODULES_RTP_RTCP_SOURCE_ACTIVE_DECODE_TARGETS_HELPER_H_
#include <stdint.h>
#include <bitset>
#include "absl/types/optional.h"
#include "api/array_view.h"
namespace webrtc {
// Helper class that decides when active_decode_target_bitmask should be written
// into the dependency descriptor rtp header extension.
// See: https://aomediacodec.github.io/av1-rtp-spec/#a44-switching
// This class is thread-compatible
class ActiveDecodeTargetsHelper {
public:
ActiveDecodeTargetsHelper() = default;
ActiveDecodeTargetsHelper(const ActiveDecodeTargetsHelper&) = delete;
ActiveDecodeTargetsHelper& operator=(const ActiveDecodeTargetsHelper&) =
delete;
~ActiveDecodeTargetsHelper() = default;
// Decides if active decode target bitmask should be attached to the frame
// that is about to be sent.
void OnFrame(rtc::ArrayView<const int> decode_target_protected_by_chain,
std::bitset<32> active_decode_targets,
bool is_keyframe,
int64_t frame_id,
rtc::ArrayView<const int> chain_diffs);
// Returns active decode target to attach to the dependency descriptor.
absl::optional<uint32_t> ActiveDecodeTargetsBitmask() const {
if (unsent_on_chain_.none())
return absl::nullopt;
return last_active_decode_targets_.to_ulong();
}
std::bitset<32> ActiveChainsBitmask() const { return last_active_chains_; }
private:
// `unsent_on_chain_[i]` indicates last active decode
// target bitmask wasn't attached to a packet on the chain with id `i`.
std::bitset<32> unsent_on_chain_ = 0;
std::bitset<32> last_active_decode_targets_ = 0;
std::bitset<32> last_active_chains_ = 0;
int64_t last_frame_id_ = 0;
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_ACTIVE_DECODE_TARGETS_HELPER_H_

View file

@ -0,0 +1,402 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_BYTE_IO_H_
#define MODULES_RTP_RTCP_SOURCE_BYTE_IO_H_
// This file contains classes for reading and writing integer types from/to
// byte array representations. Signed/unsigned, partial (whole byte) sizes,
// and big/little endian byte order is all supported.
//
// Usage examples:
//
// uint8_t* buffer = ...;
//
// // Read an unsigned 4 byte integer in big endian format
// uint32_t val = ByteReader<uint32_t>::ReadBigEndian(buffer);
//
// // Read a signed 24-bit (3 byte) integer in little endian format
// int32_t val = ByteReader<int32_t, 3>::ReadLittle(buffer);
//
// // Write an unsigned 8 byte integer in little endian format
// ByteWriter<uint64_t>::WriteLittleEndian(buffer, val);
//
// Write an unsigned 40-bit (5 byte) integer in big endian format
// ByteWriter<uint64_t, 5>::WriteBigEndian(buffer, val);
//
// These classes are implemented as recursive templetizations, intended to make
// it easy for the compiler to completely inline the reading/writing.
#include <stdint.h>
#include <limits>
namespace webrtc {
// According to ISO C standard ISO/IEC 9899, section 6.2.6.2 (2), the three
// representations of signed integers allowed are two's complement, one's
// complement and sign/magnitude. We can detect which is used by looking at
// the two last bits of -1, which will be 11 in two's complement, 10 in one's
// complement and 01 in sign/magnitude.
// TODO(sprang): In the unlikely event that we actually need to support a
// platform that doesn't use two's complement, implement conversion to/from
// wire format.
// Assume the if any one signed integer type is two's complement, then all
// other will be too.
static_assert(
(-1 & 0x03) == 0x03,
"Only two's complement representation of signed integers supported.");
// Plain const char* won't work for static_assert, use #define instead.
#define kSizeErrorMsg "Byte size must be less than or equal to data type size."
// Utility class for getting the unsigned equivalent of a signed type.
template <typename T>
struct UnsignedOf;
// Class for reading integers from a sequence of bytes.
// T = type of integer, B = bytes to read, is_signed = true if signed integer.
// If is_signed is true and B < sizeof(T), sign extension might be needed.
template <typename T,
unsigned int B = sizeof(T),
bool is_signed = std::numeric_limits<T>::is_signed>
class ByteReader;
// Specialization of ByteReader for unsigned types.
template <typename T, unsigned int B>
class ByteReader<T, B, false> {
public:
static T ReadBigEndian(const uint8_t* data) {
static_assert(B <= sizeof(T), kSizeErrorMsg);
return InternalReadBigEndian(data);
}
static T ReadLittleEndian(const uint8_t* data) {
static_assert(B <= sizeof(T), kSizeErrorMsg);
return InternalReadLittleEndian(data);
}
private:
static T InternalReadBigEndian(const uint8_t* data) {
T val(0);
for (unsigned int i = 0; i < B; ++i)
val |= static_cast<T>(data[i]) << ((B - 1 - i) * 8);
return val;
}
static T InternalReadLittleEndian(const uint8_t* data) {
T val(0);
for (unsigned int i = 0; i < B; ++i)
val |= static_cast<T>(data[i]) << (i * 8);
return val;
}
};
// Specialization of ByteReader for signed types.
template <typename T, unsigned int B>
class ByteReader<T, B, true> {
public:
typedef typename UnsignedOf<T>::Type U;
static T ReadBigEndian(const uint8_t* data) {
U unsigned_val = ByteReader<T, B, false>::ReadBigEndian(data);
if (B < sizeof(T))
unsigned_val = SignExtend(unsigned_val);
return ReinterpretAsSigned(unsigned_val);
}
static T ReadLittleEndian(const uint8_t* data) {
U unsigned_val = ByteReader<T, B, false>::ReadLittleEndian(data);
if (B < sizeof(T))
unsigned_val = SignExtend(unsigned_val);
return ReinterpretAsSigned(unsigned_val);
}
private:
// As a hack to avoid implementation-specific or undefined behavior when
// bit-shifting or casting signed integers, read as a signed equivalent
// instead and convert to signed. This is safe since we have asserted that
// two's complement for is used.
static T ReinterpretAsSigned(U unsigned_val) {
// An unsigned value with only the highest order bit set (ex 0x80).
const U kUnsignedHighestBitMask = static_cast<U>(1)
<< ((sizeof(U) * 8) - 1);
// A signed value with only the highest bit set. Since this is two's
// complement form, we can use the min value from std::numeric_limits.
const T kSignedHighestBitMask = std::numeric_limits<T>::min();
T val;
if ((unsigned_val & kUnsignedHighestBitMask) != 0) {
// Casting is only safe when unsigned value can be represented in the
// signed target type, so mask out highest bit and mask it back manually.
val = static_cast<T>(unsigned_val & ~kUnsignedHighestBitMask);
val |= kSignedHighestBitMask;
} else {
val = static_cast<T>(unsigned_val);
}
return val;
}
// If number of bytes is less than native data type (eg 24 bit, in int32_t),
// and the most significant bit of the actual data is set, we must sign
// extend the remaining byte(s) with ones so that the correct negative
// number is retained.
// Ex: 0x810A0B -> 0xFF810A0B, but 0x710A0B -> 0x00710A0B
static U SignExtend(const U val) {
const uint8_t kMsb = static_cast<uint8_t>(val >> ((B - 1) * 8));
if ((kMsb & 0x80) != 0) {
// Create a mask where all bits used by the B bytes are set to one,
// for instance 0x00FFFFFF for B = 3. Bit-wise invert that mask (to
// (0xFF000000 in the example above) and add it to the input value.
// The "B % sizeof(T)" is a workaround to undefined values warnings for
// B == sizeof(T), in which case this code won't be called anyway.
const U kUsedBitsMask = (1 << ((B % sizeof(T)) * 8)) - 1;
return ~kUsedBitsMask | val;
}
return val;
}
};
// Class for writing integers to a sequence of bytes
// T = type of integer, B = bytes to write
template <typename T,
unsigned int B = sizeof(T),
bool is_signed = std::numeric_limits<T>::is_signed>
class ByteWriter;
// Specialization of ByteWriter for unsigned types.
template <typename T, unsigned int B>
class ByteWriter<T, B, false> {
public:
static void WriteBigEndian(uint8_t* data, T val) {
static_assert(B <= sizeof(T), kSizeErrorMsg);
for (unsigned int i = 0; i < B; ++i) {
data[i] = val >> ((B - 1 - i) * 8);
}
}
static void WriteLittleEndian(uint8_t* data, T val) {
static_assert(B <= sizeof(T), kSizeErrorMsg);
for (unsigned int i = 0; i < B; ++i) {
data[i] = val >> (i * 8);
}
}
};
// Specialization of ByteWriter for signed types.
template <typename T, unsigned int B>
class ByteWriter<T, B, true> {
public:
typedef typename UnsignedOf<T>::Type U;
static void WriteBigEndian(uint8_t* data, T val) {
ByteWriter<U, B, false>::WriteBigEndian(data, ReinterpretAsUnsigned(val));
}
static void WriteLittleEndian(uint8_t* data, T val) {
ByteWriter<U, B, false>::WriteLittleEndian(data,
ReinterpretAsUnsigned(val));
}
private:
static U ReinterpretAsUnsigned(T val) {
// According to ISO C standard ISO/IEC 9899, section 6.3.1.3 (1, 2) a
// conversion from signed to unsigned keeps the value if the new type can
// represent it, and otherwise adds one more than the max value of T until
// the value is in range. For two's complement, this fortunately means
// that the bit-wise value will be intact. Thus, since we have asserted that
// two's complement form is actually used, a simple cast is sufficient.
return static_cast<U>(val);
}
};
// ----- Below follows specializations of UnsignedOf utility class -----
template <>
struct UnsignedOf<int8_t> {
typedef uint8_t Type;
};
template <>
struct UnsignedOf<int16_t> {
typedef uint16_t Type;
};
template <>
struct UnsignedOf<int32_t> {
typedef uint32_t Type;
};
template <>
struct UnsignedOf<int64_t> {
typedef uint64_t Type;
};
// ----- Below follows specializations for unsigned, B in { 1, 2, 4, 8 } -----
// TODO(sprang): Check if these actually help or if generic cases will be
// unrolled to and optimized to similar performance.
// Specializations for single bytes
template <typename T>
class ByteReader<T, 1, false> {
public:
static T ReadBigEndian(const uint8_t* data) {
static_assert(sizeof(T) == 1, kSizeErrorMsg);
return data[0];
}
static T ReadLittleEndian(const uint8_t* data) {
static_assert(sizeof(T) == 1, kSizeErrorMsg);
return data[0];
}
};
template <typename T>
class ByteWriter<T, 1, false> {
public:
static void WriteBigEndian(uint8_t* data, T val) {
static_assert(sizeof(T) == 1, kSizeErrorMsg);
data[0] = val;
}
static void WriteLittleEndian(uint8_t* data, T val) {
static_assert(sizeof(T) == 1, kSizeErrorMsg);
data[0] = val;
}
};
// Specializations for two byte words
template <typename T>
class ByteReader<T, 2, false> {
public:
static T ReadBigEndian(const uint8_t* data) {
static_assert(sizeof(T) >= 2, kSizeErrorMsg);
return (data[0] << 8) | data[1];
}
static T ReadLittleEndian(const uint8_t* data) {
static_assert(sizeof(T) >= 2, kSizeErrorMsg);
return data[0] | (data[1] << 8);
}
};
template <typename T>
class ByteWriter<T, 2, false> {
public:
static void WriteBigEndian(uint8_t* data, T val) {
static_assert(sizeof(T) >= 2, kSizeErrorMsg);
data[0] = val >> 8;
data[1] = val;
}
static void WriteLittleEndian(uint8_t* data, T val) {
static_assert(sizeof(T) >= 2, kSizeErrorMsg);
data[0] = val;
data[1] = val >> 8;
}
};
// Specializations for four byte words.
template <typename T>
class ByteReader<T, 4, false> {
public:
static T ReadBigEndian(const uint8_t* data) {
static_assert(sizeof(T) >= 4, kSizeErrorMsg);
return (Get(data, 0) << 24) | (Get(data, 1) << 16) | (Get(data, 2) << 8) |
Get(data, 3);
}
static T ReadLittleEndian(const uint8_t* data) {
static_assert(sizeof(T) >= 4, kSizeErrorMsg);
return Get(data, 0) | (Get(data, 1) << 8) | (Get(data, 2) << 16) |
(Get(data, 3) << 24);
}
private:
inline static T Get(const uint8_t* data, unsigned int index) {
return static_cast<T>(data[index]);
}
};
// Specializations for four byte words.
template <typename T>
class ByteWriter<T, 4, false> {
public:
static void WriteBigEndian(uint8_t* data, T val) {
static_assert(sizeof(T) >= 4, kSizeErrorMsg);
data[0] = val >> 24;
data[1] = val >> 16;
data[2] = val >> 8;
data[3] = val;
}
static void WriteLittleEndian(uint8_t* data, T val) {
static_assert(sizeof(T) >= 4, kSizeErrorMsg);
data[0] = val;
data[1] = val >> 8;
data[2] = val >> 16;
data[3] = val >> 24;
}
};
// Specializations for eight byte words.
template <typename T>
class ByteReader<T, 8, false> {
public:
static T ReadBigEndian(const uint8_t* data) {
static_assert(sizeof(T) >= 8, kSizeErrorMsg);
return (Get(data, 0) << 56) | (Get(data, 1) << 48) | (Get(data, 2) << 40) |
(Get(data, 3) << 32) | (Get(data, 4) << 24) | (Get(data, 5) << 16) |
(Get(data, 6) << 8) | Get(data, 7);
}
static T ReadLittleEndian(const uint8_t* data) {
static_assert(sizeof(T) >= 8, kSizeErrorMsg);
return Get(data, 0) | (Get(data, 1) << 8) | (Get(data, 2) << 16) |
(Get(data, 3) << 24) | (Get(data, 4) << 32) | (Get(data, 5) << 40) |
(Get(data, 6) << 48) | (Get(data, 7) << 56);
}
private:
inline static T Get(const uint8_t* data, unsigned int index) {
return static_cast<T>(data[index]);
}
};
template <typename T>
class ByteWriter<T, 8, false> {
public:
static void WriteBigEndian(uint8_t* data, T val) {
static_assert(sizeof(T) >= 8, kSizeErrorMsg);
data[0] = val >> 56;
data[1] = val >> 48;
data[2] = val >> 40;
data[3] = val >> 32;
data[4] = val >> 24;
data[5] = val >> 16;
data[6] = val >> 8;
data[7] = val;
}
static void WriteLittleEndian(uint8_t* data, T val) {
static_assert(sizeof(T) >= 8, kSizeErrorMsg);
data[0] = val;
data[1] = val >> 8;
data[2] = val >> 16;
data[3] = val >> 24;
data[4] = val >> 32;
data[5] = val >> 40;
data[6] = val >> 48;
data[7] = val >> 56;
}
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_BYTE_IO_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 "modules/rtp_rtcp/source/capture_clock_offset_updater.h"
#include "system_wrappers/include/ntp_time.h"
namespace webrtc {
absl::optional<int64_t>
CaptureClockOffsetUpdater::AdjustEstimatedCaptureClockOffset(
absl::optional<int64_t> remote_capture_clock_offset) const {
if (remote_capture_clock_offset == absl::nullopt ||
remote_to_local_clock_offset_ == absl::nullopt) {
return absl::nullopt;
}
// Do calculations as "unsigned" to make overflows deterministic.
return static_cast<uint64_t>(*remote_capture_clock_offset) +
static_cast<uint64_t>(*remote_to_local_clock_offset_);
}
absl::optional<TimeDelta> CaptureClockOffsetUpdater::ConvertsToTimeDela(
absl::optional<int64_t> q32x32) {
if (q32x32 == absl::nullopt) {
return absl::nullopt;
}
return TimeDelta::Millis(Q32x32ToInt64Ms(*q32x32));
}
void CaptureClockOffsetUpdater::SetRemoteToLocalClockOffset(
absl::optional<int64_t> offset_q32x32) {
remote_to_local_clock_offset_ = offset_q32x32;
}
} // namespace webrtc

View file

@ -0,0 +1,56 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_CAPTURE_CLOCK_OFFSET_UPDATER_H_
#define MODULES_RTP_RTCP_SOURCE_CAPTURE_CLOCK_OFFSET_UPDATER_H_
#include <stdint.h>
#include "absl/types/optional.h"
#include "api/units/time_delta.h"
namespace webrtc {
//
// Helper class for calculating the clock offset against the capturer's clock.
//
// This is achieved by adjusting the estimated capture clock offset in received
// Absolute Capture Time RTP header extension (see
// https://webrtc.org/experiments/rtp-hdrext/abs-capture-time/), which
// represents the clock offset between a remote sender and the capturer, by
// adding local-to-remote clock offset.
class CaptureClockOffsetUpdater {
public:
// Adjusts remote_capture_clock_offset, which originates from Absolute Capture
// Time RTP header extension, to get the local clock offset against the
// capturer's clock.
absl::optional<int64_t> AdjustEstimatedCaptureClockOffset(
absl::optional<int64_t> remote_capture_clock_offset) const;
// Sets the NTP clock offset between the sender system (which may be different
// from the capture system) and the local system. This information is normally
// provided by passing half the value of the Round-Trip Time estimation given
// by RTCP sender reports (see DLSR/DLRR).
//
// Note that the value must be in Q32.32-formatted fixed-point seconds.
void SetRemoteToLocalClockOffset(absl::optional<int64_t> offset_q32x32);
// Converts a signed Q32.32-formatted fixed-point to a TimeDelta.
static absl::optional<TimeDelta> ConvertsToTimeDela(
absl::optional<int64_t> q32x32);
private:
absl::optional<int64_t> remote_to_local_clock_offset_;
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_CAPTURE_CLOCK_OFFSET_UPDATER_H_

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/rtp_rtcp/source/create_video_rtp_depacketizer.h"
#include <memory>
#include "api/video/video_codec_type.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_av1.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_generic.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_h264.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.h"
#ifdef RTC_ENABLE_H265
#include "modules/rtp_rtcp/source/video_rtp_depacketizer_h265.h"
#endif
namespace webrtc {
std::unique_ptr<VideoRtpDepacketizer> CreateVideoRtpDepacketizer(
VideoCodecType codec) {
switch (codec) {
case kVideoCodecH264:
return std::make_unique<VideoRtpDepacketizerH264>();
case kVideoCodecVP8:
return std::make_unique<VideoRtpDepacketizerVp8>();
case kVideoCodecVP9:
return std::make_unique<VideoRtpDepacketizerVp9>();
case kVideoCodecAV1:
return std::make_unique<VideoRtpDepacketizerAv1>();
case kVideoCodecH265:
#ifdef RTC_ENABLE_H265
return std::make_unique<VideoRtpDepacketizerH265>();
#else
return nullptr;
#endif
case kVideoCodecGeneric:
case kVideoCodecMultiplex:
return std::make_unique<VideoRtpDepacketizerGeneric>();
}
RTC_CHECK_NOTREACHED();
}
} // namespace webrtc

View file

@ -0,0 +1,26 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_CREATE_VIDEO_RTP_DEPACKETIZER_H_
#define MODULES_RTP_RTCP_SOURCE_CREATE_VIDEO_RTP_DEPACKETIZER_H_
#include <memory>
#include "api/video/video_codec_type.h"
#include "modules/rtp_rtcp/source/video_rtp_depacketizer.h"
namespace webrtc {
std::unique_ptr<VideoRtpDepacketizer> CreateVideoRtpDepacketizer(
VideoCodecType codec);
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_CREATE_VIDEO_RTP_DEPACKETIZER_H_

View file

@ -0,0 +1,374 @@
/*
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/rtp_rtcp/source/deprecated/deprecated_rtp_sender_egress.h"
#include <limits>
#include <memory>
#include <utility>
#include "absl/strings/match.h"
#include "api/units/timestamp.h"
#include "logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h"
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
constexpr uint32_t kTimestampTicksPerMs = 90;
constexpr TimeDelta kBitrateStatisticsWindow = TimeDelta::Seconds(1);
constexpr size_t kRtpSequenceNumberMapMaxEntries = 1 << 13;
} // namespace
DEPRECATED_RtpSenderEgress::NonPacedPacketSender::NonPacedPacketSender(
DEPRECATED_RtpSenderEgress* sender,
PacketSequencer* sequence_number_assigner)
: transport_sequence_number_(0),
sender_(sender),
sequence_number_assigner_(sequence_number_assigner) {
RTC_DCHECK(sequence_number_assigner_);
}
DEPRECATED_RtpSenderEgress::NonPacedPacketSender::~NonPacedPacketSender() =
default;
void DEPRECATED_RtpSenderEgress::NonPacedPacketSender::EnqueuePackets(
std::vector<std::unique_ptr<RtpPacketToSend>> packets) {
for (auto& packet : packets) {
// Assign sequence numbers, but not for flexfec which is already running on
// an internally maintained sequence number series.
if (packet->Ssrc() != sender_->FlexFecSsrc()) {
sequence_number_assigner_->Sequence(*packet);
}
if (!packet->SetExtension<TransportSequenceNumber>(
++transport_sequence_number_)) {
--transport_sequence_number_;
}
packet->ReserveExtension<TransmissionOffset>();
packet->ReserveExtension<AbsoluteSendTime>();
sender_->SendPacket(packet.get(), PacedPacketInfo());
}
}
DEPRECATED_RtpSenderEgress::DEPRECATED_RtpSenderEgress(
const RtpRtcpInterface::Configuration& config,
RtpPacketHistory* packet_history)
: ssrc_(config.local_media_ssrc),
rtx_ssrc_(config.rtx_send_ssrc),
flexfec_ssrc_(config.fec_generator ? config.fec_generator->FecSsrc()
: absl::nullopt),
populate_network2_timestamp_(config.populate_network2_timestamp),
clock_(config.clock),
packet_history_(packet_history),
transport_(config.outgoing_transport),
event_log_(config.event_log),
is_audio_(config.audio),
need_rtp_packet_infos_(config.need_rtp_packet_infos),
transport_feedback_observer_(config.transport_feedback_callback),
send_packet_observer_(config.send_packet_observer),
rtp_stats_callback_(config.rtp_stats_callback),
bitrate_callback_(config.send_bitrate_observer),
media_has_been_sent_(false),
force_part_of_allocation_(false),
timestamp_offset_(0),
send_rates_(kNumMediaTypes, BitrateTracker(kBitrateStatisticsWindow)),
rtp_sequence_number_map_(need_rtp_packet_infos_
? std::make_unique<RtpSequenceNumberMap>(
kRtpSequenceNumberMapMaxEntries)
: nullptr) {}
void DEPRECATED_RtpSenderEgress::SendPacket(
RtpPacketToSend* packet,
const PacedPacketInfo& pacing_info) {
RTC_DCHECK(packet);
const uint32_t packet_ssrc = packet->Ssrc();
RTC_DCHECK(packet->packet_type().has_value());
RTC_DCHECK(HasCorrectSsrc(*packet));
Timestamp now = clock_->CurrentTime();
int64_t now_ms = now.ms();
if (is_audio_) {
#if BWE_TEST_LOGGING_COMPILE_TIME_ENABLE
BWE_TEST_LOGGING_PLOT_WITH_SSRC(1, "AudioTotBitrate_kbps", now_ms,
GetSendRates().Sum().kbps(), packet_ssrc);
BWE_TEST_LOGGING_PLOT_WITH_SSRC(
1, "AudioNackBitrate_kbps", now_ms,
GetSendRates()[RtpPacketMediaType::kRetransmission].kbps(),
packet_ssrc);
#endif
} else {
#if BWE_TEST_LOGGING_COMPILE_TIME_ENABLE
BWE_TEST_LOGGING_PLOT_WITH_SSRC(1, "VideoTotBitrate_kbps", now_ms,
GetSendRates().Sum().kbps(), packet_ssrc);
BWE_TEST_LOGGING_PLOT_WITH_SSRC(
1, "VideoNackBitrate_kbps", now_ms,
GetSendRates()[RtpPacketMediaType::kRetransmission].kbps(),
packet_ssrc);
#endif
}
PacketOptions options;
{
MutexLock lock(&lock_);
options.included_in_allocation = force_part_of_allocation_;
if (need_rtp_packet_infos_ &&
packet->packet_type() == RtpPacketToSend::Type::kVideo) {
RTC_DCHECK(rtp_sequence_number_map_);
// Last packet of a frame, add it to sequence number info map.
const uint32_t timestamp = packet->Timestamp() - timestamp_offset_;
bool is_first_packet_of_frame = packet->is_first_packet_of_frame();
bool is_last_packet_of_frame = packet->Marker();
rtp_sequence_number_map_->InsertPacket(
packet->SequenceNumber(),
RtpSequenceNumberMap::Info(timestamp, is_first_packet_of_frame,
is_last_packet_of_frame));
}
}
// Bug webrtc:7859. While FEC is invoked from rtp_sender_video, and not after
// the pacer, these modifications of the header below are happening after the
// FEC protection packets are calculated. This will corrupt recovered packets
// at the same place. It's not an issue for extensions, which are present in
// all the packets (their content just may be incorrect on recovered packets).
// In case of VideoTimingExtension, since it's present not in every packet,
// data after rtp header may be corrupted if these packets are protected by
// the FEC.
int64_t diff_ms = now_ms - packet->capture_time().ms();
if (packet->HasExtension<TransmissionOffset>()) {
packet->SetExtension<TransmissionOffset>(kTimestampTicksPerMs * diff_ms);
}
if (packet->HasExtension<AbsoluteSendTime>()) {
packet->SetExtension<AbsoluteSendTime>(AbsoluteSendTime::To24Bits(now));
}
if (packet->HasExtension<VideoTimingExtension>()) {
if (populate_network2_timestamp_) {
packet->set_network2_time(now);
} else {
packet->set_pacer_exit_time(now);
}
}
const bool is_media = packet->packet_type() == RtpPacketMediaType::kAudio ||
packet->packet_type() == RtpPacketMediaType::kVideo;
// Downstream code actually uses this flag to distinguish between media and
// everything else.
options.is_retransmit = !is_media;
if (auto packet_id = packet->GetExtension<TransportSequenceNumber>()) {
options.packet_id = *packet_id;
options.included_in_feedback = true;
options.included_in_allocation = true;
AddPacketToTransportFeedback(*packet_id, *packet, pacing_info);
}
options.additional_data = packet->additional_data();
if (packet->packet_type() != RtpPacketMediaType::kPadding &&
packet->packet_type() != RtpPacketMediaType::kRetransmission) {
UpdateOnSendPacket(options.packet_id, packet->capture_time().ms(),
packet_ssrc);
}
const bool send_success = SendPacketToNetwork(*packet, options, pacing_info);
// Put packet in retransmission history or update pending status even if
// actual sending fails.
if (is_media && packet->allow_retransmission()) {
packet_history_->PutRtpPacket(std::make_unique<RtpPacketToSend>(*packet),
now);
} else if (packet->retransmitted_sequence_number()) {
packet_history_->MarkPacketAsSent(*packet->retransmitted_sequence_number());
}
if (send_success) {
MutexLock lock(&lock_);
UpdateRtpStats(*packet);
media_has_been_sent_ = true;
}
}
void DEPRECATED_RtpSenderEgress::ProcessBitrateAndNotifyObservers() {
if (!bitrate_callback_)
return;
MutexLock lock(&lock_);
RtpSendRates send_rates = GetSendRatesLocked();
bitrate_callback_->Notify(
send_rates.Sum().bps(),
send_rates[RtpPacketMediaType::kRetransmission].bps(), ssrc_);
}
RtpSendRates DEPRECATED_RtpSenderEgress::GetSendRates() const {
MutexLock lock(&lock_);
return GetSendRatesLocked();
}
RtpSendRates DEPRECATED_RtpSenderEgress::GetSendRatesLocked() const {
const Timestamp now = clock_->CurrentTime();
RtpSendRates current_rates;
for (size_t i = 0; i < kNumMediaTypes; ++i) {
RtpPacketMediaType type = static_cast<RtpPacketMediaType>(i);
current_rates[type] = send_rates_[i].Rate(now).value_or(DataRate::Zero());
}
return current_rates;
}
void DEPRECATED_RtpSenderEgress::GetDataCounters(
StreamDataCounters* rtp_stats,
StreamDataCounters* rtx_stats) const {
MutexLock lock(&lock_);
*rtp_stats = rtp_stats_;
*rtx_stats = rtx_rtp_stats_;
}
void DEPRECATED_RtpSenderEgress::ForceIncludeSendPacketsInAllocation(
bool part_of_allocation) {
MutexLock lock(&lock_);
force_part_of_allocation_ = part_of_allocation;
}
bool DEPRECATED_RtpSenderEgress::MediaHasBeenSent() const {
MutexLock lock(&lock_);
return media_has_been_sent_;
}
void DEPRECATED_RtpSenderEgress::SetMediaHasBeenSent(bool media_sent) {
MutexLock lock(&lock_);
media_has_been_sent_ = media_sent;
}
void DEPRECATED_RtpSenderEgress::SetTimestampOffset(uint32_t timestamp) {
MutexLock lock(&lock_);
timestamp_offset_ = timestamp;
}
std::vector<RtpSequenceNumberMap::Info>
DEPRECATED_RtpSenderEgress::GetSentRtpPacketInfos(
rtc::ArrayView<const uint16_t> sequence_numbers) const {
RTC_DCHECK(!sequence_numbers.empty());
if (!need_rtp_packet_infos_) {
return std::vector<RtpSequenceNumberMap::Info>();
}
std::vector<RtpSequenceNumberMap::Info> results;
results.reserve(sequence_numbers.size());
MutexLock lock(&lock_);
for (uint16_t sequence_number : sequence_numbers) {
const auto& info = rtp_sequence_number_map_->Get(sequence_number);
if (!info) {
// The empty vector will be returned. We can delay the clearing
// of the vector until after we exit the critical section.
return std::vector<RtpSequenceNumberMap::Info>();
}
results.push_back(*info);
}
return results;
}
bool DEPRECATED_RtpSenderEgress::HasCorrectSsrc(
const RtpPacketToSend& packet) const {
switch (*packet.packet_type()) {
case RtpPacketMediaType::kAudio:
case RtpPacketMediaType::kVideo:
return packet.Ssrc() == ssrc_;
case RtpPacketMediaType::kRetransmission:
case RtpPacketMediaType::kPadding:
// Both padding and retransmission must be on either the media or the
// RTX stream.
return packet.Ssrc() == rtx_ssrc_ || packet.Ssrc() == ssrc_;
case RtpPacketMediaType::kForwardErrorCorrection:
// FlexFEC is on separate SSRC, ULPFEC uses media SSRC.
return packet.Ssrc() == ssrc_ || packet.Ssrc() == flexfec_ssrc_;
}
return false;
}
void DEPRECATED_RtpSenderEgress::AddPacketToTransportFeedback(
uint16_t packet_id,
const RtpPacketToSend& packet,
const PacedPacketInfo& pacing_info) {
if (transport_feedback_observer_) {
RtpPacketSendInfo packet_info;
packet_info.media_ssrc = ssrc_;
packet_info.transport_sequence_number = packet_id;
packet_info.rtp_sequence_number = packet.SequenceNumber();
packet_info.length = packet.size();
packet_info.pacing_info = pacing_info;
packet_info.packet_type = packet.packet_type();
transport_feedback_observer_->OnAddPacket(packet_info);
}
}
void DEPRECATED_RtpSenderEgress::UpdateOnSendPacket(int packet_id,
int64_t capture_time_ms,
uint32_t ssrc) {
if (!send_packet_observer_ || capture_time_ms <= 0 || packet_id == -1) {
return;
}
send_packet_observer_->OnSendPacket(packet_id,
Timestamp::Millis(capture_time_ms), ssrc);
}
bool DEPRECATED_RtpSenderEgress::SendPacketToNetwork(
const RtpPacketToSend& packet,
const PacketOptions& options,
const PacedPacketInfo& pacing_info) {
int bytes_sent = -1;
if (transport_) {
bytes_sent = transport_->SendRtp(packet, options)
? static_cast<int>(packet.size())
: -1;
if (event_log_ && bytes_sent > 0) {
event_log_->Log(std::make_unique<RtcEventRtpPacketOutgoing>(
packet, pacing_info.probe_cluster_id));
}
}
if (bytes_sent <= 0) {
RTC_LOG(LS_WARNING) << "Transport failed to send packet.";
return false;
}
return true;
}
void DEPRECATED_RtpSenderEgress::UpdateRtpStats(const RtpPacketToSend& packet) {
Timestamp now = clock_->CurrentTime();
StreamDataCounters* counters =
packet.Ssrc() == rtx_ssrc_ ? &rtx_rtp_stats_ : &rtp_stats_;
counters->MaybeSetFirstPacketTime(now);
if (packet.packet_type() == RtpPacketMediaType::kForwardErrorCorrection) {
counters->fec.AddPacket(packet);
}
if (packet.packet_type() == RtpPacketMediaType::kRetransmission) {
counters->retransmitted.AddPacket(packet);
}
counters->transmitted.AddPacket(packet);
RTC_DCHECK(packet.packet_type().has_value());
send_rates_[static_cast<size_t>(*packet.packet_type())].Update(packet.size(),
now);
if (rtp_stats_callback_) {
rtp_stats_callback_->DataCountersUpdated(*counters, packet.Ssrc());
}
}
} // namespace webrtc

View file

@ -0,0 +1,137 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_DEPRECATED_DEPRECATED_RTP_SENDER_EGRESS_H_
#define MODULES_RTP_RTCP_SOURCE_DEPRECATED_DEPRECATED_RTP_SENDER_EGRESS_H_
#include <map>
#include <memory>
#include <vector>
#include "absl/types/optional.h"
#include "api/call/transport.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/units/data_rate.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/packet_sequencer.h"
#include "modules/rtp_rtcp/source/rtp_packet_history.h"
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
#include "modules/rtp_rtcp/source/rtp_sequence_number_map.h"
#include "rtc_base/bitrate_tracker.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h"
namespace webrtc {
class DEPRECATED_RtpSenderEgress {
public:
// Helper class that redirects packets directly to the send part of this class
// without passing through an actual paced sender.
class NonPacedPacketSender : public RtpPacketSender {
public:
NonPacedPacketSender(DEPRECATED_RtpSenderEgress* sender,
PacketSequencer* sequence_number_assigner);
virtual ~NonPacedPacketSender();
void EnqueuePackets(
std::vector<std::unique_ptr<RtpPacketToSend>> packets) override;
void RemovePacketsForSsrc(uint32_t ssrc) override {}
private:
uint16_t transport_sequence_number_;
DEPRECATED_RtpSenderEgress* const sender_;
PacketSequencer* sequence_number_assigner_;
};
DEPRECATED_RtpSenderEgress(const RtpRtcpInterface::Configuration& config,
RtpPacketHistory* packet_history);
~DEPRECATED_RtpSenderEgress() = default;
void SendPacket(RtpPacketToSend* packet, const PacedPacketInfo& pacing_info)
RTC_LOCKS_EXCLUDED(lock_);
uint32_t Ssrc() const { return ssrc_; }
absl::optional<uint32_t> RtxSsrc() const { return rtx_ssrc_; }
absl::optional<uint32_t> FlexFecSsrc() const { return flexfec_ssrc_; }
void ProcessBitrateAndNotifyObservers() RTC_LOCKS_EXCLUDED(lock_);
RtpSendRates GetSendRates() const RTC_LOCKS_EXCLUDED(lock_);
void GetDataCounters(StreamDataCounters* rtp_stats,
StreamDataCounters* rtx_stats) const
RTC_LOCKS_EXCLUDED(lock_);
void ForceIncludeSendPacketsInAllocation(bool part_of_allocation)
RTC_LOCKS_EXCLUDED(lock_);
bool MediaHasBeenSent() const RTC_LOCKS_EXCLUDED(lock_);
void SetMediaHasBeenSent(bool media_sent) RTC_LOCKS_EXCLUDED(lock_);
void SetTimestampOffset(uint32_t timestamp) RTC_LOCKS_EXCLUDED(lock_);
// For each sequence number in `sequence_number`, recall the last RTP packet
// which bore it - its timestamp and whether it was the first and/or last
// packet in that frame. If all of the given sequence numbers could be
// recalled, return a vector with all of them (in corresponding order).
// If any could not be recalled, return an empty vector.
std::vector<RtpSequenceNumberMap::Info> GetSentRtpPacketInfos(
rtc::ArrayView<const uint16_t> sequence_numbers) const
RTC_LOCKS_EXCLUDED(lock_);
private:
RtpSendRates GetSendRatesLocked() const RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_);
bool HasCorrectSsrc(const RtpPacketToSend& packet) const;
void AddPacketToTransportFeedback(uint16_t packet_id,
const RtpPacketToSend& packet,
const PacedPacketInfo& pacing_info);
void UpdateOnSendPacket(int packet_id,
int64_t capture_time_ms,
uint32_t ssrc);
// Sends packet on to `transport_`, leaving the RTP module.
bool SendPacketToNetwork(const RtpPacketToSend& packet,
const PacketOptions& options,
const PacedPacketInfo& pacing_info);
void UpdateRtpStats(const RtpPacketToSend& packet)
RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_);
const uint32_t ssrc_;
const absl::optional<uint32_t> rtx_ssrc_;
const absl::optional<uint32_t> flexfec_ssrc_;
const bool populate_network2_timestamp_;
Clock* const clock_;
RtpPacketHistory* const packet_history_;
Transport* const transport_;
RtcEventLog* const event_log_;
const bool is_audio_;
const bool need_rtp_packet_infos_;
TransportFeedbackObserver* const transport_feedback_observer_;
SendPacketObserver* const send_packet_observer_;
StreamDataCountersCallback* const rtp_stats_callback_;
BitrateStatisticsObserver* const bitrate_callback_;
mutable Mutex lock_;
bool media_has_been_sent_ RTC_GUARDED_BY(lock_);
bool force_part_of_allocation_ RTC_GUARDED_BY(lock_);
uint32_t timestamp_offset_ RTC_GUARDED_BY(lock_);
StreamDataCounters rtp_stats_ RTC_GUARDED_BY(lock_);
StreamDataCounters rtx_rtp_stats_ RTC_GUARDED_BY(lock_);
// One element per value in RtpPacketMediaType, with index matching value.
std::vector<BitrateTracker> send_rates_ RTC_GUARDED_BY(lock_);
// Maps sent packets' sequence numbers to a tuple consisting of:
// 1. The timestamp, without the randomizing offset mandated by the RFC.
// 2. Whether the packet was the first in its frame.
// 3. Whether the packet was the last in its frame.
const std::unique_ptr<RtpSequenceNumberMap> rtp_sequence_number_map_
RTC_GUARDED_BY(lock_);
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_DEPRECATED_DEPRECATED_RTP_SENDER_EGRESS_H_

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/rtp_rtcp/source/dtmf_queue.h"
#include <stddef.h>
#include "rtc_base/checks.h"
namespace {
constexpr size_t kDtmfOutbandMax = 20;
}
namespace webrtc {
DtmfQueue::DtmfQueue() {}
DtmfQueue::~DtmfQueue() {}
bool DtmfQueue::AddDtmf(const Event& event) {
MutexLock lock(&dtmf_mutex_);
if (queue_.size() >= kDtmfOutbandMax) {
return false;
}
queue_.push_back(event);
return true;
}
bool DtmfQueue::NextDtmf(Event* event) {
RTC_DCHECK(event);
MutexLock lock(&dtmf_mutex_);
if (queue_.empty()) {
return false;
}
*event = queue_.front();
queue_.pop_front();
return true;
}
bool DtmfQueue::PendingDtmf() const {
MutexLock lock(&dtmf_mutex_);
return !queue_.empty();
}
} // namespace webrtc

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2011 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 MODULES_RTP_RTCP_SOURCE_DTMF_QUEUE_H_
#define MODULES_RTP_RTCP_SOURCE_DTMF_QUEUE_H_
#include <stdint.h>
#include <list>
#include "rtc_base/synchronization/mutex.h"
namespace webrtc {
class DtmfQueue {
public:
struct Event {
uint16_t duration_ms = 0;
uint8_t payload_type = 0;
uint8_t key = 0;
uint8_t level = 0;
};
DtmfQueue();
~DtmfQueue();
bool AddDtmf(const Event& event);
bool NextDtmf(Event* event);
bool PendingDtmf() const;
private:
mutable Mutex dtmf_mutex_;
std::list<Event> queue_;
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_DTMF_QUEUE_H_

View file

@ -0,0 +1,660 @@
/*
* 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 "modules/rtp_rtcp/source/fec_private_tables_bursty.h"
namespace {
// clang-format off
#define kMaskBursty1_1 \
0x80, 0x00
#define kMaskBursty2_1 \
0xc0, 0x00
#define kMaskBursty2_2 \
0x80, 0x00, \
0xc0, 0x00
#define kMaskBursty3_1 \
0xe0, 0x00
#define kMaskBursty3_2 \
0xc0, 0x00, \
0xa0, 0x00
#define kMaskBursty3_3 \
0x80, 0x00, \
0xc0, 0x00, \
0x60, 0x00
#define kMaskBursty4_1 \
0xf0, 0x00
#define kMaskBursty4_2 \
0xa0, 0x00, \
0xd0, 0x00
#define kMaskBursty4_3 \
0xc0, 0x00, \
0x60, 0x00, \
0x90, 0x00
#define kMaskBursty4_4 \
0x80, 0x00, \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00
#define kMaskBursty5_1 \
0xf8, 0x00
#define kMaskBursty5_2 \
0xd0, 0x00, \
0xa8, 0x00
#define kMaskBursty5_3 \
0x70, 0x00, \
0x90, 0x00, \
0xc8, 0x00
#define kMaskBursty5_4 \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00, \
0x88, 0x00
#define kMaskBursty5_5 \
0x80, 0x00, \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00
#define kMaskBursty6_1 \
0xfc, 0x00
#define kMaskBursty6_2 \
0xa8, 0x00, \
0xd4, 0x00
#define kMaskBursty6_3 \
0x94, 0x00, \
0xc8, 0x00, \
0x64, 0x00
#define kMaskBursty6_4 \
0x60, 0x00, \
0x38, 0x00, \
0x88, 0x00, \
0xc4, 0x00
#define kMaskBursty6_5 \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x84, 0x00
#define kMaskBursty6_6 \
0x80, 0x00, \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00
#define kMaskBursty7_1 \
0xfe, 0x00
#define kMaskBursty7_2 \
0xd4, 0x00, \
0xaa, 0x00
#define kMaskBursty7_3 \
0xc8, 0x00, \
0x74, 0x00, \
0x92, 0x00
#define kMaskBursty7_4 \
0x38, 0x00, \
0x8a, 0x00, \
0xc4, 0x00, \
0x62, 0x00
#define kMaskBursty7_5 \
0x60, 0x00, \
0x30, 0x00, \
0x1c, 0x00, \
0x84, 0x00, \
0xc2, 0x00
#define kMaskBursty7_6 \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x82, 0x00
#define kMaskBursty7_7 \
0x80, 0x00, \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00
#define kMaskBursty8_1 \
0xff, 0x00
#define kMaskBursty8_2 \
0xaa, 0x00, \
0xd5, 0x00
#define kMaskBursty8_3 \
0x74, 0x00, \
0x92, 0x00, \
0xc9, 0x00
#define kMaskBursty8_4 \
0x8a, 0x00, \
0xc5, 0x00, \
0x62, 0x00, \
0x31, 0x00
#define kMaskBursty8_5 \
0x30, 0x00, \
0x1c, 0x00, \
0x85, 0x00, \
0xc2, 0x00, \
0x61, 0x00
#define kMaskBursty8_6 \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0e, 0x00, \
0x82, 0x00, \
0xc1, 0x00
#define kMaskBursty8_7 \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00, \
0x81, 0x00
#define kMaskBursty8_8 \
0x80, 0x00, \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00, \
0x03, 0x00
#define kMaskBursty9_1 \
0xff, 0x80
#define kMaskBursty9_2 \
0xd5, 0x00, \
0xaa, 0x80
#define kMaskBursty9_3 \
0x92, 0x00, \
0xc9, 0x00, \
0x74, 0x80
#define kMaskBursty9_4 \
0xc5, 0x00, \
0x62, 0x00, \
0x39, 0x00, \
0x8a, 0x80
#define kMaskBursty9_5 \
0x1c, 0x00, \
0x85, 0x00, \
0xc2, 0x80, \
0x61, 0x00, \
0x30, 0x80
#define kMaskBursty9_6 \
0x30, 0x00, \
0x18, 0x00, \
0x0e, 0x00, \
0x82, 0x80, \
0xc1, 0x00, \
0x60, 0x80
#define kMaskBursty9_7 \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x07, 0x00, \
0x81, 0x00, \
0xc0, 0x80
#define kMaskBursty9_8 \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00, \
0x03, 0x00, \
0x80, 0x80
#define kMaskBursty9_9 \
0x80, 0x00, \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00, \
0x03, 0x00, \
0x01, 0x80
#define kMaskBursty10_1 \
0xff, 0xc0
#define kMaskBursty10_2 \
0xaa, 0x80, \
0xd5, 0x40
#define kMaskBursty10_3 \
0xc9, 0x00, \
0x74, 0x80, \
0x92, 0x40
#define kMaskBursty10_4 \
0x62, 0x00, \
0x39, 0x00, \
0x8a, 0x80, \
0xc5, 0x40
#define kMaskBursty10_5 \
0x85, 0x00, \
0xc2, 0x80, \
0x61, 0x40, \
0x30, 0x80, \
0x18, 0x40
#define kMaskBursty10_6 \
0x18, 0x00, \
0x0e, 0x00, \
0x82, 0x80, \
0xc1, 0x40, \
0x60, 0x80, \
0x30, 0x40
#define kMaskBursty10_7 \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x07, 0x00, \
0x81, 0x40, \
0xc0, 0x80, \
0x60, 0x40
#define kMaskBursty10_8 \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00, \
0x03, 0x00, \
0x80, 0x80, \
0xc0, 0x40
#define kMaskBursty10_9 \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00, \
0x03, 0x00, \
0x01, 0x80, \
0x80, 0x40
#define kMaskBursty10_10 \
0x80, 0x00, \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00, \
0x03, 0x00, \
0x01, 0x80, \
0x00, 0xc0
#define kMaskBursty11_1 \
0xff, 0xe0
#define kMaskBursty11_2 \
0xd5, 0x40, \
0xaa, 0xa0
#define kMaskBursty11_3 \
0x74, 0x80, \
0x92, 0x40, \
0xc9, 0x20
#define kMaskBursty11_4 \
0x39, 0x00, \
0x8a, 0x80, \
0xc5, 0x40, \
0x62, 0x20
#define kMaskBursty11_5 \
0xc2, 0xc0, \
0x61, 0x00, \
0x30, 0xa0, \
0x1c, 0x40, \
0x85, 0x20
#define kMaskBursty11_6 \
0x0e, 0x00, \
0x82, 0x80, \
0xc1, 0x40, \
0x60, 0xa0, \
0x30, 0x40, \
0x18, 0x20
#define kMaskBursty11_7 \
0x18, 0x00, \
0x0c, 0x00, \
0x07, 0x00, \
0x81, 0x40, \
0xc0, 0xa0, \
0x60, 0x40, \
0x30, 0x20
#define kMaskBursty11_8 \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00, \
0x03, 0x40, \
0x80, 0xa0, \
0xc0, 0x40, \
0x60, 0x20
#define kMaskBursty11_9 \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00, \
0x03, 0x00, \
0x01, 0x80, \
0x80, 0x40, \
0xc0, 0x20
#define kMaskBursty11_10 \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00, \
0x03, 0x00, \
0x01, 0x80, \
0x00, 0xc0, \
0x80, 0x20
#define kMaskBursty11_11 \
0x80, 0x00, \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00, \
0x03, 0x00, \
0x01, 0x80, \
0x00, 0xc0, \
0x00, 0x60
#define kMaskBursty12_1 \
0xff, 0xf0
#define kMaskBursty12_2 \
0xaa, 0xa0, \
0xd5, 0x50
#define kMaskBursty12_3 \
0x92, 0x40, \
0xc9, 0x20, \
0x74, 0x90
#define kMaskBursty12_4 \
0x8a, 0x80, \
0xc5, 0x40, \
0x62, 0x20, \
0x39, 0x10
#define kMaskBursty12_5 \
0x61, 0x00, \
0x30, 0xa0, \
0x1c, 0x50, \
0x85, 0x20, \
0xc2, 0x90
#define kMaskBursty12_6 \
0x82, 0x90, \
0xc1, 0x40, \
0x60, 0xa0, \
0x30, 0x50, \
0x18, 0x20, \
0x0c, 0x10
#define kMaskBursty12_7 \
0x0c, 0x00, \
0x07, 0x00, \
0x81, 0x40, \
0xc0, 0xa0, \
0x60, 0x50, \
0x30, 0x20, \
0x18, 0x10
#define kMaskBursty12_8 \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00, \
0x03, 0x00, \
0x80, 0xa0, \
0xc0, 0x50, \
0x60, 0x20, \
0x30, 0x10
#define kMaskBursty12_9 \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00, \
0x03, 0x00, \
0x01, 0x80, \
0x80, 0x50, \
0xc0, 0x20, \
0x60, 0x10
#define kMaskBursty12_10 \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00, \
0x03, 0x00, \
0x01, 0x80, \
0x00, 0xc0, \
0x80, 0x20, \
0xc0, 0x10
#define kMaskBursty12_11 \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00, \
0x03, 0x00, \
0x01, 0x80, \
0x00, 0xc0, \
0x00, 0x60, \
0x80, 0x10
#define kMaskBursty12_12 \
0x80, 0x00, \
0xc0, 0x00, \
0x60, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0x0c, 0x00, \
0x06, 0x00, \
0x03, 0x00, \
0x01, 0x80, \
0x00, 0xc0, \
0x00, 0x60, \
0x00, 0x30
#define kPacketMaskBursty1 1, \
kMaskBursty1_1
#define kPacketMaskBursty2 2, \
kMaskBursty2_1, \
kMaskBursty2_2
#define kPacketMaskBursty3 3, \
kMaskBursty3_1, \
kMaskBursty3_2, \
kMaskBursty3_3
#define kPacketMaskBursty4 4, \
kMaskBursty4_1, \
kMaskBursty4_2, \
kMaskBursty4_3, \
kMaskBursty4_4
#define kPacketMaskBursty5 5, \
kMaskBursty5_1, \
kMaskBursty5_2, \
kMaskBursty5_3, \
kMaskBursty5_4, \
kMaskBursty5_5
#define kPacketMaskBursty6 6, \
kMaskBursty6_1, \
kMaskBursty6_2, \
kMaskBursty6_3, \
kMaskBursty6_4, \
kMaskBursty6_5, \
kMaskBursty6_6
#define kPacketMaskBursty7 7, \
kMaskBursty7_1, \
kMaskBursty7_2, \
kMaskBursty7_3, \
kMaskBursty7_4, \
kMaskBursty7_5, \
kMaskBursty7_6, \
kMaskBursty7_7
#define kPacketMaskBursty8 8, \
kMaskBursty8_1, \
kMaskBursty8_2, \
kMaskBursty8_3, \
kMaskBursty8_4, \
kMaskBursty8_5, \
kMaskBursty8_6, \
kMaskBursty8_7, \
kMaskBursty8_8
#define kPacketMaskBursty9 9, \
kMaskBursty9_1, \
kMaskBursty9_2, \
kMaskBursty9_3, \
kMaskBursty9_4, \
kMaskBursty9_5, \
kMaskBursty9_6, \
kMaskBursty9_7, \
kMaskBursty9_8, \
kMaskBursty9_9
#define kPacketMaskBursty10 10, \
kMaskBursty10_1, \
kMaskBursty10_2, \
kMaskBursty10_3, \
kMaskBursty10_4, \
kMaskBursty10_5, \
kMaskBursty10_6, \
kMaskBursty10_7, \
kMaskBursty10_8, \
kMaskBursty10_9, \
kMaskBursty10_10
#define kPacketMaskBursty11 11, \
kMaskBursty11_1, \
kMaskBursty11_2, \
kMaskBursty11_3, \
kMaskBursty11_4, \
kMaskBursty11_5, \
kMaskBursty11_6, \
kMaskBursty11_7, \
kMaskBursty11_8, \
kMaskBursty11_9, \
kMaskBursty11_10, \
kMaskBursty11_11
#define kPacketMaskBursty12 12, \
kMaskBursty12_1, \
kMaskBursty12_2, \
kMaskBursty12_3, \
kMaskBursty12_4, \
kMaskBursty12_5, \
kMaskBursty12_6, \
kMaskBursty12_7, \
kMaskBursty12_8, \
kMaskBursty12_9, \
kMaskBursty12_10, \
kMaskBursty12_11, \
kMaskBursty12_12
// clang-format on
} // namespace
namespace webrtc {
namespace fec_private_tables {
const uint8_t kPacketMaskBurstyTbl[] = {
12,
kPacketMaskBursty1,
kPacketMaskBursty2,
kPacketMaskBursty3,
kPacketMaskBursty4,
kPacketMaskBursty5,
kPacketMaskBursty6,
kPacketMaskBursty7,
kPacketMaskBursty8,
kPacketMaskBursty9,
kPacketMaskBursty10,
kPacketMaskBursty11,
kPacketMaskBursty12,
};
} // namespace fec_private_tables
} // namespace webrtc

View file

@ -0,0 +1,37 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_FEC_PRIVATE_TABLES_BURSTY_H_
#define MODULES_RTP_RTCP_SOURCE_FEC_PRIVATE_TABLES_BURSTY_H_
// This file contains a set of packets masks for the FEC code. The masks in
// this table are specifically designed to favor recovery of bursty/consecutive
// loss network conditions. The tradeoff is worse recovery for random losses.
// These packet masks are currently defined to protect up to 12 media packets.
// They have the following property: for any packet mask defined by the
// parameters (k,m), where k = number of media packets, m = number of FEC
// packets, all "consecutive" losses of size <= m are completely recoverable.
// By consecutive losses we mean consecutive with respect to the sequence
// number ordering of the list (media and FEC) of packets. The difference
// between these masks (`kFecMaskBursty`) and `kFecMaskRandom` type, defined
// in fec_private_tables.h, is more significant for longer codes
// (i.e., more packets/symbols in the code, so larger (k,m), i.e., k > 4,
// m > 3).
#include <stdint.h>
namespace webrtc {
namespace fec_private_tables {
extern const uint8_t kPacketMaskBurstyTbl[];
} // namespace fec_private_tables
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_FEC_PRIVATE_TABLES_BURSTY_H_

View file

@ -0,0 +1,660 @@
/*
* 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 "modules/rtp_rtcp/source/fec_private_tables_random.h"
namespace {
// clang-format off
#define kMaskRandom1_1 \
0x80, 0x00
#define kMaskRandom2_1 \
0xc0, 0x00
#define kMaskRandom2_2 \
0xc0, 0x00, \
0x80, 0x00
#define kMaskRandom3_1 \
0xe0, 0x00
#define kMaskRandom3_2 \
0xc0, 0x00, \
0xa0, 0x00
#define kMaskRandom3_3 \
0xc0, 0x00, \
0xa0, 0x00, \
0x60, 0x00
#define kMaskRandom4_1 \
0xf0, 0x00
#define kMaskRandom4_2 \
0xc0, 0x00, \
0xb0, 0x00
#define kMaskRandom4_3 \
0xc0, 0x00, \
0xb0, 0x00, \
0x60, 0x00
#define kMaskRandom4_4 \
0xc0, 0x00, \
0xa0, 0x00, \
0x30, 0x00, \
0x50, 0x00
#define kMaskRandom5_1 \
0xf8, 0x00
#define kMaskRandom5_2 \
0xa8, 0x00, \
0xd0, 0x00
#define kMaskRandom5_3 \
0xb0, 0x00, \
0xc8, 0x00, \
0x50, 0x00
#define kMaskRandom5_4 \
0xc8, 0x00, \
0xb0, 0x00, \
0x50, 0x00, \
0x28, 0x00
#define kMaskRandom5_5 \
0xc0, 0x00, \
0x30, 0x00, \
0x18, 0x00, \
0xa0, 0x00, \
0x48, 0x00
#define kMaskRandom6_1 \
0xfc, 0x00
#define kMaskRandom6_2 \
0xa8, 0x00, \
0xd4, 0x00
#define kMaskRandom6_3 \
0xd0, 0x00, \
0x68, 0x00, \
0xa4, 0x00
#define kMaskRandom6_4 \
0xa8, 0x00, \
0x58, 0x00, \
0x64, 0x00, \
0x94, 0x00
#define kMaskRandom6_5 \
0xa8, 0x00, \
0x84, 0x00, \
0x64, 0x00, \
0x90, 0x00, \
0x58, 0x00
#define kMaskRandom6_6 \
0x98, 0x00, \
0x64, 0x00, \
0x50, 0x00, \
0x14, 0x00, \
0xa8, 0x00, \
0xe0, 0x00
#define kMaskRandom7_1 \
0xfe, 0x00
#define kMaskRandom7_2 \
0xd4, 0x00, \
0xaa, 0x00
#define kMaskRandom7_3 \
0xd0, 0x00, \
0xaa, 0x00, \
0x64, 0x00
#define kMaskRandom7_4 \
0xd0, 0x00, \
0xaa, 0x00, \
0x64, 0x00, \
0x1c, 0x00
#define kMaskRandom7_5 \
0x0c, 0x00, \
0xb0, 0x00, \
0x1a, 0x00, \
0xc4, 0x00, \
0x62, 0x00
#define kMaskRandom7_6 \
0x8c, 0x00, \
0x4a, 0x00, \
0x64, 0x00, \
0xd0, 0x00, \
0xa0, 0x00, \
0x32, 0x00
#define kMaskRandom7_7 \
0x4a, 0x00, \
0x94, 0x00, \
0x1a, 0x00, \
0xc4, 0x00, \
0x28, 0x00, \
0xc2, 0x00, \
0x34, 0x00
#define kMaskRandom8_1 \
0xff, 0x00
#define kMaskRandom8_2 \
0xaa, 0x00, \
0xd5, 0x00
#define kMaskRandom8_3 \
0xc5, 0x00, \
0x92, 0x00, \
0x6a, 0x00
#define kMaskRandom8_4 \
0x45, 0x00, \
0xb4, 0x00, \
0x6a, 0x00, \
0x89, 0x00
#define kMaskRandom8_5 \
0x8c, 0x00, \
0x92, 0x00, \
0x2b, 0x00, \
0x51, 0x00, \
0x64, 0x00
#define kMaskRandom8_6 \
0xa1, 0x00, \
0x52, 0x00, \
0x91, 0x00, \
0x2a, 0x00, \
0xc4, 0x00, \
0x4c, 0x00
#define kMaskRandom8_7 \
0x15, 0x00, \
0xc2, 0x00, \
0x25, 0x00, \
0x62, 0x00, \
0x58, 0x00, \
0x8c, 0x00, \
0xa3, 0x00
#define kMaskRandom8_8 \
0x25, 0x00, \
0x8a, 0x00, \
0x91, 0x00, \
0x68, 0x00, \
0x32, 0x00, \
0x43, 0x00, \
0xc4, 0x00, \
0x1c, 0x00
#define kMaskRandom9_1 \
0xff, 0x80
#define kMaskRandom9_2 \
0xaa, 0x80, \
0xd5, 0x00
#define kMaskRandom9_3 \
0xa5, 0x00, \
0xc8, 0x00, \
0x52, 0x80
#define kMaskRandom9_4 \
0xa2, 0x00, \
0xc9, 0x00, \
0x52, 0x80, \
0x24, 0x80
#define kMaskRandom9_5 \
0x8c, 0x00, \
0x25, 0x00, \
0x92, 0x80, \
0x41, 0x80, \
0x58, 0x00
#define kMaskRandom9_6 \
0x84, 0x80, \
0x27, 0x00, \
0x51, 0x80, \
0x1a, 0x00, \
0x68, 0x00, \
0x89, 0x00
#define kMaskRandom9_7 \
0x8c, 0x00, \
0x47, 0x00, \
0x81, 0x80, \
0x12, 0x80, \
0x58, 0x00, \
0x28, 0x80, \
0xb4, 0x00
#define kMaskRandom9_8 \
0x2c, 0x00, \
0x91, 0x00, \
0x40, 0x80, \
0x06, 0x80, \
0xc8, 0x00, \
0x45, 0x00, \
0x30, 0x80, \
0xa2, 0x00
#define kMaskRandom9_9 \
0x4c, 0x00, \
0x62, 0x00, \
0x91, 0x00, \
0x42, 0x80, \
0xa4, 0x00, \
0x13, 0x00, \
0x30, 0x80, \
0x88, 0x80, \
0x09, 0x00
#define kMaskRandom10_1 \
0xff, 0xc0
#define kMaskRandom10_10 \
0x4c, 0x00, \
0x51, 0x00, \
0xa0, 0x40, \
0x04, 0xc0, \
0x03, 0x80, \
0x86, 0x00, \
0x29, 0x00, \
0x42, 0x40, \
0x98, 0x00, \
0x30, 0x80
#define kMaskRandom10_2 \
0xaa, 0x80, \
0xd5, 0x40
#define kMaskRandom10_3 \
0xa4, 0x40, \
0xc9, 0x00, \
0x52, 0x80
#define kMaskRandom10_4 \
0xca, 0x00, \
0x32, 0x80, \
0xa1, 0x40, \
0x55, 0x00
#define kMaskRandom10_5 \
0xca, 0x00, \
0x32, 0x80, \
0xa1, 0x40, \
0x55, 0x00, \
0x08, 0xc0
#define kMaskRandom10_6 \
0x0e, 0x00, \
0x33, 0x00, \
0x10, 0xc0, \
0x45, 0x40, \
0x88, 0x80, \
0xe0, 0x00
#define kMaskRandom10_7 \
0x46, 0x00, \
0x33, 0x00, \
0x80, 0xc0, \
0x0c, 0x40, \
0x28, 0x80, \
0x94, 0x00, \
0xc1, 0x00
#define kMaskRandom10_8 \
0x2c, 0x00, \
0x81, 0x80, \
0xa0, 0x40, \
0x05, 0x40, \
0x18, 0x80, \
0xc2, 0x00, \
0x22, 0x80, \
0x50, 0x40
#define kMaskRandom10_9 \
0x4c, 0x00, \
0x23, 0x00, \
0x88, 0xc0, \
0x21, 0x40, \
0x52, 0x80, \
0x94, 0x00, \
0x26, 0x00, \
0x48, 0x40, \
0x91, 0x80
#define kMaskRandom11_1 \
0xff, 0xe0
#define kMaskRandom11_10 \
0x64, 0x40, \
0x51, 0x40, \
0xa9, 0x00, \
0x04, 0xc0, \
0xd0, 0x00, \
0x82, 0x40, \
0x21, 0x20, \
0x0c, 0x20, \
0x4a, 0x00, \
0x12, 0xa0
#define kMaskRandom11_11 \
0x46, 0x40, \
0x33, 0x20, \
0x99, 0x00, \
0x05, 0x80, \
0x80, 0xa0, \
0x84, 0x40, \
0x40, 0x60, \
0x0a, 0x80, \
0x68, 0x00, \
0x10, 0x20, \
0x30, 0x40
#define kMaskRandom11_2 \
0xec, 0xc0, \
0x9b, 0xa0
#define kMaskRandom11_3 \
0xca, 0xc0, \
0xf1, 0x40, \
0xb6, 0x20
#define kMaskRandom11_4 \
0xc4, 0xc0, \
0x31, 0x60, \
0x4b, 0x20, \
0x2c, 0xa0
#define kMaskRandom11_5 \
0x86, 0x80, \
0x23, 0x20, \
0x16, 0x20, \
0x4c, 0x20, \
0x41, 0xc0
#define kMaskRandom11_6 \
0x64, 0x40, \
0x51, 0x40, \
0x0c, 0xa0, \
0xa1, 0x20, \
0x12, 0xa0, \
0x8a, 0x40
#define kMaskRandom11_7 \
0x46, 0x40, \
0x33, 0x20, \
0x91, 0x80, \
0xa4, 0x20, \
0x50, 0xa0, \
0x84, 0xc0, \
0x09, 0x60
#define kMaskRandom11_8 \
0x0c, 0x80, \
0x80, 0x60, \
0xa0, 0x80, \
0x05, 0x40, \
0x43, 0x00, \
0x1a, 0x00, \
0x60, 0x20, \
0x14, 0x20
#define kMaskRandom11_9 \
0x46, 0x40, \
0x62, 0x60, \
0x8c, 0x00, \
0x01, 0x60, \
0x07, 0x80, \
0xa0, 0x80, \
0x18, 0xa0, \
0x91, 0x00, \
0x78, 0x00
#define kMaskRandom12_1 \
0xff, 0xf0
#define kMaskRandom12_10 \
0x51, 0x40, \
0x45, 0x10, \
0x80, 0xd0, \
0x24, 0x20, \
0x0a, 0x20, \
0x00, 0xe0, \
0xb8, 0x00, \
0x09, 0x10, \
0x56, 0x00, \
0xa2, 0x80
#define kMaskRandom12_11 \
0x53, 0x60, \
0x21, 0x30, \
0x10, 0x90, \
0x00, 0x70, \
0x0c, 0x10, \
0x40, 0xc0, \
0x6a, 0x00, \
0x86, 0x00, \
0x24, 0x80, \
0x89, 0x00, \
0xc0, 0x20
#define kMaskRandom12_12 \
0x10, 0x60, \
0x02, 0x30, \
0x40, 0x50, \
0x21, 0x80, \
0x81, 0x10, \
0x14, 0x80, \
0x98, 0x00, \
0x08, 0x90, \
0x62, 0x00, \
0x24, 0x20, \
0x8a, 0x00, \
0x84, 0x40
#define kMaskRandom12_2 \
0xec, 0xc0, \
0x93, 0xb0
#define kMaskRandom12_3 \
0x9b, 0x80, \
0x4f, 0x10, \
0x3c, 0x60
#define kMaskRandom12_4 \
0x8b, 0x20, \
0x14, 0xb0, \
0x22, 0xd0, \
0x45, 0x50
#define kMaskRandom12_5 \
0x53, 0x60, \
0x64, 0x20, \
0x0c, 0xc0, \
0x82, 0xa0, \
0x09, 0x30
#define kMaskRandom12_6 \
0x51, 0x40, \
0xc5, 0x10, \
0x21, 0x80, \
0x12, 0x30, \
0x08, 0xe0, \
0x2e, 0x00
#define kMaskRandom12_7 \
0x53, 0x60, \
0x21, 0x30, \
0x90, 0x90, \
0x02, 0x50, \
0x06, 0xa0, \
0x2c, 0x00, \
0x88, 0x60
#define kMaskRandom12_8 \
0x20, 0x60, \
0x80, 0x30, \
0x42, 0x40, \
0x01, 0x90, \
0x14, 0x10, \
0x0a, 0x80, \
0x38, 0x00, \
0xc5, 0x00
#define kMaskRandom12_9 \
0x53, 0x60, \
0xe4, 0x20, \
0x24, 0x40, \
0xa1, 0x10, \
0x18, 0x30, \
0x03, 0x90, \
0x8a, 0x10, \
0x04, 0x90, \
0x00, 0xe0
#define kPacketMaskRandom1 1, \
kMaskRandom1_1
#define kPacketMaskRandom2 2, \
kMaskRandom2_1, \
kMaskRandom2_2
#define kPacketMaskRandom3 3, \
kMaskRandom3_1, \
kMaskRandom3_2, \
kMaskRandom3_3
#define kPacketMaskRandom4 4, \
kMaskRandom4_1, \
kMaskRandom4_2, \
kMaskRandom4_3, \
kMaskRandom4_4
#define kPacketMaskRandom5 5, \
kMaskRandom5_1, \
kMaskRandom5_2, \
kMaskRandom5_3, \
kMaskRandom5_4, \
kMaskRandom5_5
#define kPacketMaskRandom6 6, \
kMaskRandom6_1, \
kMaskRandom6_2, \
kMaskRandom6_3, \
kMaskRandom6_4, \
kMaskRandom6_5, \
kMaskRandom6_6
#define kPacketMaskRandom7 7, \
kMaskRandom7_1, \
kMaskRandom7_2, \
kMaskRandom7_3, \
kMaskRandom7_4, \
kMaskRandom7_5, \
kMaskRandom7_6, \
kMaskRandom7_7
#define kPacketMaskRandom8 8, \
kMaskRandom8_1, \
kMaskRandom8_2, \
kMaskRandom8_3, \
kMaskRandom8_4, \
kMaskRandom8_5, \
kMaskRandom8_6, \
kMaskRandom8_7, \
kMaskRandom8_8
#define kPacketMaskRandom9 9, \
kMaskRandom9_1, \
kMaskRandom9_2, \
kMaskRandom9_3, \
kMaskRandom9_4, \
kMaskRandom9_5, \
kMaskRandom9_6, \
kMaskRandom9_7, \
kMaskRandom9_8, \
kMaskRandom9_9
#define kPacketMaskRandom10 10, \
kMaskRandom10_1, \
kMaskRandom10_2, \
kMaskRandom10_3, \
kMaskRandom10_4, \
kMaskRandom10_5, \
kMaskRandom10_6, \
kMaskRandom10_7, \
kMaskRandom10_8, \
kMaskRandom10_9, \
kMaskRandom10_10
#define kPacketMaskRandom11 11, \
kMaskRandom11_1, \
kMaskRandom11_2, \
kMaskRandom11_3, \
kMaskRandom11_4, \
kMaskRandom11_5, \
kMaskRandom11_6, \
kMaskRandom11_7, \
kMaskRandom11_8, \
kMaskRandom11_9, \
kMaskRandom11_10, \
kMaskRandom11_11
#define kPacketMaskRandom12 12, \
kMaskRandom12_1, \
kMaskRandom12_2, \
kMaskRandom12_3, \
kMaskRandom12_4, \
kMaskRandom12_5, \
kMaskRandom12_6, \
kMaskRandom12_7, \
kMaskRandom12_8, \
kMaskRandom12_9, \
kMaskRandom12_10, \
kMaskRandom12_11, \
kMaskRandom12_12
// clang-format on
} // namespace
namespace webrtc {
namespace fec_private_tables {
const uint8_t kPacketMaskRandomTbl[] = {
12,
kPacketMaskRandom1, // 2 byte entries.
kPacketMaskRandom2,
kPacketMaskRandom3,
kPacketMaskRandom4,
kPacketMaskRandom5,
kPacketMaskRandom6,
kPacketMaskRandom7,
kPacketMaskRandom8,
kPacketMaskRandom9,
kPacketMaskRandom10,
kPacketMaskRandom11,
kPacketMaskRandom12,
};
} // namespace fec_private_tables
} // namespace webrtc

View file

@ -0,0 +1,27 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_FEC_PRIVATE_TABLES_RANDOM_H_
#define MODULES_RTP_RTCP_SOURCE_FEC_PRIVATE_TABLES_RANDOM_H_
// This file contains a set of packets masks for the FEC code. The masks in
// this table are specifically designed to favor recovery to random loss.
// These packet masks are defined to protect up to maximum of 48 media packets.
#include <stdint.h>
namespace webrtc {
namespace fec_private_tables {
extern const uint8_t kPacketMaskRandomTbl[];
} // namespace fec_private_tables
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_FEC_PRIVATE_TABLES_RANDOM_H_

View file

@ -0,0 +1,230 @@
/*
* 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 "modules/rtp_rtcp/source/fec_test_helper.h"
#include <memory>
#include <utility>
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtp_packet.h"
#include "rtc_base/checks.h"
namespace webrtc {
namespace test {
namespace fec {
namespace {
constexpr uint8_t kRtpMarkerBitMask = 0x80;
constexpr uint8_t kFecPayloadType = 96;
constexpr uint8_t kRedPayloadType = 97;
constexpr uint8_t kVp8PayloadType = 120;
constexpr int kPacketTimestampIncrement = 3000;
} // namespace
MediaPacketGenerator::MediaPacketGenerator(uint32_t min_packet_size,
uint32_t max_packet_size,
uint32_t ssrc,
Random* random)
: min_packet_size_(min_packet_size),
max_packet_size_(max_packet_size),
ssrc_(ssrc),
random_(random) {}
MediaPacketGenerator::~MediaPacketGenerator() = default;
ForwardErrorCorrection::PacketList MediaPacketGenerator::ConstructMediaPackets(
int num_media_packets,
uint16_t start_seq_num) {
RTC_DCHECK_GT(num_media_packets, 0);
uint16_t seq_num = start_seq_num;
int time_stamp = random_->Rand<int>();
ForwardErrorCorrection::PacketList media_packets;
for (int i = 0; i < num_media_packets; ++i) {
std::unique_ptr<ForwardErrorCorrection::Packet> media_packet(
new ForwardErrorCorrection::Packet());
media_packet->data.SetSize(
random_->Rand(min_packet_size_, max_packet_size_));
uint8_t* data = media_packet->data.MutableData();
// Generate random values for the first 2 bytes
data[0] = random_->Rand<uint8_t>();
data[1] = random_->Rand<uint8_t>();
// The first two bits are assumed to be 10 by the FEC encoder.
// In fact the FEC decoder will set the two first bits to 10 regardless of
// what they actually were. Set the first two bits to 10 so that a memcmp
// can be performed for the whole restored packet.
data[0] |= 0x80;
data[0] &= 0xbf;
// FEC is applied to a whole frame.
// A frame is signaled by multiple packets without the marker bit set
// followed by the last packet of the frame for which the marker bit is set.
// Only push one (fake) frame to the FEC.
data[1] &= 0x7f;
webrtc::ByteWriter<uint16_t>::WriteBigEndian(&data[2], seq_num);
webrtc::ByteWriter<uint32_t>::WriteBigEndian(&data[4], time_stamp);
webrtc::ByteWriter<uint32_t>::WriteBigEndian(&data[8], ssrc_);
// Generate random values for payload.
for (size_t j = 12; j < media_packet->data.size(); ++j)
data[j] = random_->Rand<uint8_t>();
seq_num++;
media_packets.push_back(std::move(media_packet));
}
// Last packet, set marker bit.
ForwardErrorCorrection::Packet* media_packet = media_packets.back().get();
RTC_DCHECK(media_packet);
media_packet->data.MutableData()[1] |= 0x80;
next_seq_num_ = seq_num;
return media_packets;
}
ForwardErrorCorrection::PacketList MediaPacketGenerator::ConstructMediaPackets(
int num_media_packets) {
return ConstructMediaPackets(num_media_packets, random_->Rand<uint16_t>());
}
uint16_t MediaPacketGenerator::GetNextSeqNum() {
return next_seq_num_;
}
AugmentedPacketGenerator::AugmentedPacketGenerator(uint32_t ssrc)
: num_packets_(0), ssrc_(ssrc), seq_num_(0), timestamp_(0) {}
void AugmentedPacketGenerator::NewFrame(size_t num_packets) {
num_packets_ = num_packets;
timestamp_ += kPacketTimestampIncrement;
}
uint16_t AugmentedPacketGenerator::NextPacketSeqNum() {
return ++seq_num_;
}
std::unique_ptr<AugmentedPacket> AugmentedPacketGenerator::NextPacket(
size_t offset,
size_t length) {
std::unique_ptr<AugmentedPacket> packet(new AugmentedPacket());
packet->data.SetSize(length + kRtpHeaderSize);
uint8_t* data = packet->data.MutableData();
for (size_t i = 0; i < length; ++i)
data[i + kRtpHeaderSize] = offset + i;
packet->data.SetSize(length + kRtpHeaderSize);
packet->header.headerLength = kRtpHeaderSize;
packet->header.markerBit = (num_packets_ == 1);
packet->header.payloadType = kVp8PayloadType;
packet->header.sequenceNumber = seq_num_;
packet->header.timestamp = timestamp_;
packet->header.ssrc = ssrc_;
WriteRtpHeader(packet->header, data);
++seq_num_;
--num_packets_;
return packet;
}
void AugmentedPacketGenerator::WriteRtpHeader(const RTPHeader& header,
uint8_t* data) {
data[0] = 0x80; // Version 2.
data[1] = header.payloadType;
data[1] |= (header.markerBit ? kRtpMarkerBitMask : 0);
ByteWriter<uint16_t>::WriteBigEndian(data + 2, header.sequenceNumber);
ByteWriter<uint32_t>::WriteBigEndian(data + 4, header.timestamp);
ByteWriter<uint32_t>::WriteBigEndian(data + 8, header.ssrc);
}
FlexfecPacketGenerator::FlexfecPacketGenerator(uint32_t media_ssrc,
uint32_t flexfec_ssrc)
: AugmentedPacketGenerator(media_ssrc),
flexfec_ssrc_(flexfec_ssrc),
flexfec_seq_num_(0),
flexfec_timestamp_(0) {}
std::unique_ptr<AugmentedPacket> FlexfecPacketGenerator::BuildFlexfecPacket(
const ForwardErrorCorrection::Packet& packet) {
RTC_DCHECK_LE(packet.data.size(),
static_cast<size_t>(IP_PACKET_SIZE - kRtpHeaderSize));
RTPHeader header;
header.sequenceNumber = flexfec_seq_num_;
++flexfec_seq_num_;
header.timestamp = flexfec_timestamp_;
flexfec_timestamp_ += kPacketTimestampIncrement;
header.ssrc = flexfec_ssrc_;
std::unique_ptr<AugmentedPacket> packet_with_rtp_header(
new AugmentedPacket());
packet_with_rtp_header->data.SetSize(kRtpHeaderSize + packet.data.size());
WriteRtpHeader(header, packet_with_rtp_header->data.MutableData());
memcpy(packet_with_rtp_header->data.MutableData() + kRtpHeaderSize,
packet.data.cdata(), packet.data.size());
return packet_with_rtp_header;
}
UlpfecPacketGenerator::UlpfecPacketGenerator(uint32_t ssrc)
: AugmentedPacketGenerator(ssrc) {}
RtpPacketReceived UlpfecPacketGenerator::BuildMediaRedPacket(
const AugmentedPacket& packet,
bool is_recovered) {
// Create a temporary buffer used to wrap the media packet in RED.
rtc::CopyOnWriteBuffer red_buffer;
const size_t kHeaderLength = packet.header.headerLength;
// Append header.
red_buffer.SetData(packet.data.data(), kHeaderLength);
// Find payload type and add it as RED header.
uint8_t media_payload_type = red_buffer[1] & 0x7F;
red_buffer.AppendData({media_payload_type});
// Append rest of payload/padding.
red_buffer.AppendData(
packet.data.Slice(kHeaderLength, packet.data.size() - kHeaderLength));
RtpPacketReceived red_packet;
RTC_CHECK(red_packet.Parse(std::move(red_buffer)));
red_packet.SetPayloadType(kRedPayloadType);
red_packet.set_recovered(is_recovered);
return red_packet;
}
RtpPacketReceived UlpfecPacketGenerator::BuildUlpfecRedPacket(
const ForwardErrorCorrection::Packet& packet) {
// Create a fake media packet to get a correct header. 1 byte RED header.
++num_packets_;
std::unique_ptr<AugmentedPacket> fake_packet =
NextPacket(0, packet.data.size() + 1);
RtpPacketReceived red_packet;
red_packet.Parse(fake_packet->data);
red_packet.SetMarker(false);
uint8_t* rtp_payload = red_packet.AllocatePayload(packet.data.size() + 1);
rtp_payload[0] = kFecPayloadType;
red_packet.SetPayloadType(kRedPayloadType);
memcpy(rtp_payload + 1, packet.data.cdata(), packet.data.size());
red_packet.set_recovered(false);
return red_packet;
}
} // namespace fec
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,125 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_FEC_TEST_HELPER_H_
#define MODULES_RTP_RTCP_SOURCE_FEC_TEST_HELPER_H_
#include <memory>
#include "modules/rtp_rtcp/source/forward_error_correction.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "rtc_base/random.h"
namespace webrtc {
namespace test {
namespace fec {
struct AugmentedPacket : public ForwardErrorCorrection::Packet {
RTPHeader header;
};
// TODO(brandtr): Consider merging MediaPacketGenerator and
// AugmentedPacketGenerator into a single class, since their functionality is
// similar.
// This class generates media packets corresponding to a single frame.
class MediaPacketGenerator {
public:
MediaPacketGenerator(uint32_t min_packet_size,
uint32_t max_packet_size,
uint32_t ssrc,
Random* random);
~MediaPacketGenerator();
// Construct the media packets, up to `num_media_packets` packets.
ForwardErrorCorrection::PacketList ConstructMediaPackets(
int num_media_packets,
uint16_t start_seq_num);
ForwardErrorCorrection::PacketList ConstructMediaPackets(
int num_media_packets);
uint16_t GetNextSeqNum();
private:
uint32_t min_packet_size_;
uint32_t max_packet_size_;
uint32_t ssrc_;
Random* random_;
ForwardErrorCorrection::PacketList media_packets_;
uint16_t next_seq_num_;
};
// This class generates media packets with a certain structure of the payload.
class AugmentedPacketGenerator {
public:
explicit AugmentedPacketGenerator(uint32_t ssrc);
// Prepare for generating a new set of packets, corresponding to a frame.
void NewFrame(size_t num_packets);
// Increment and return the newly incremented sequence number.
uint16_t NextPacketSeqNum();
// Return the next packet in the current frame.
std::unique_ptr<AugmentedPacket> NextPacket(size_t offset, size_t length);
protected:
// Given `header`, writes the appropriate RTP header fields in `data`.
static void WriteRtpHeader(const RTPHeader& header, uint8_t* data);
// Number of packets left to generate, in the current frame.
size_t num_packets_;
private:
uint32_t ssrc_;
uint16_t seq_num_;
uint32_t timestamp_;
};
// This class generates media and FlexFEC packets for a single frame.
class FlexfecPacketGenerator : public AugmentedPacketGenerator {
public:
FlexfecPacketGenerator(uint32_t media_ssrc, uint32_t flexfec_ssrc);
// Creates a new AugmentedPacket (with RTP headers) from a
// FlexFEC packet (without RTP headers).
std::unique_ptr<AugmentedPacket> BuildFlexfecPacket(
const ForwardErrorCorrection::Packet& packet);
private:
uint32_t flexfec_ssrc_;
uint16_t flexfec_seq_num_;
uint32_t flexfec_timestamp_;
};
// This class generates media and ULPFEC packets (both encapsulated in RED)
// for a single frame.
class UlpfecPacketGenerator : public AugmentedPacketGenerator {
public:
explicit UlpfecPacketGenerator(uint32_t ssrc);
// Creates a new RtpPacket with the RED header added to the packet.
static RtpPacketReceived BuildMediaRedPacket(const AugmentedPacket& packet,
bool is_recovered);
// Creates a new RtpPacket with FEC payload and RED header. Does this by
// creating a new fake media AugmentedPacket, clears the marker bit and adds a
// RED header. Finally replaces the payload with the content of
// `packet->data`.
RtpPacketReceived BuildUlpfecRedPacket(
const ForwardErrorCorrection::Packet& packet);
};
} // namespace fec
} // namespace test
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_FEC_TEST_HELPER_H_

View file

@ -0,0 +1,319 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/source/flexfec_03_header_reader_writer.h"
#include <string.h>
#include "api/scoped_refptr.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/forward_error_correction_internal.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
// Maximum number of media packets that can be protected in one batch.
constexpr size_t kMaxMediaPackets = 48; // Since we are reusing ULPFEC masks.
// Maximum number of media packets tracked by FEC decoder.
// Maintain a sufficiently larger tracking window than `kMaxMediaPackets`
// to account for packet reordering in pacer/ network.
constexpr size_t kMaxTrackedMediaPackets = 4 * kMaxMediaPackets;
// Maximum number of FEC packets stored inside ForwardErrorCorrection.
constexpr size_t kMaxFecPackets = kMaxMediaPackets;
// Size (in bytes) of packet masks, given number of K bits set.
constexpr size_t kFlexfecPacketMaskSizes[] = {2, 6, 14};
// Size (in bytes) of part of header which is not packet mask specific.
constexpr size_t kBaseHeaderSize = 12;
// Size (in bytes) of part of header which is stream specific.
constexpr size_t kStreamSpecificHeaderSize = 6;
// Size (in bytes) of header, given the single stream packet mask size, i.e.
// the number of K-bits set.
constexpr size_t kHeaderSizes[] = {
kBaseHeaderSize + kStreamSpecificHeaderSize + kFlexfecPacketMaskSizes[0],
kBaseHeaderSize + kStreamSpecificHeaderSize + kFlexfecPacketMaskSizes[1],
kBaseHeaderSize + kStreamSpecificHeaderSize + kFlexfecPacketMaskSizes[2]};
// Flexfec03 implementation only support single-stream protection.
// For multi-stream protection use implementation of the FlexFEC final standard.
constexpr uint8_t kSsrcCount = 1;
// There are three reserved bytes that MUST be set to zero in the header.
constexpr uint32_t kReservedBits = 0;
constexpr size_t kPacketMaskOffset =
kBaseHeaderSize + kStreamSpecificHeaderSize;
// Here we count the K-bits as belonging to the packet mask.
// This can be used in conjunction with
// Flexfec03HeaderWriter::MinPacketMaskSize, which calculates a bound on the
// needed packet mask size including K-bits, given a packet mask without K-bits.
size_t FlexfecHeaderSize(size_t packet_mask_size) {
RTC_DCHECK_LE(packet_mask_size, kFlexfecPacketMaskSizes[2]);
if (packet_mask_size <= kFlexfecPacketMaskSizes[0]) {
return kHeaderSizes[0];
} else if (packet_mask_size <= kFlexfecPacketMaskSizes[1]) {
return kHeaderSizes[1];
}
return kHeaderSizes[2];
}
} // namespace
Flexfec03HeaderReader::Flexfec03HeaderReader()
: FecHeaderReader(kMaxTrackedMediaPackets, kMaxFecPackets) {}
Flexfec03HeaderReader::~Flexfec03HeaderReader() = default;
// Flexfec03 implementation doesn't support retransmission and flexible masks.
// When that features are desired, they should be implemented in the class that
// follows final version of the FlexFEC standard.
bool Flexfec03HeaderReader::ReadFecHeader(
ForwardErrorCorrection::ReceivedFecPacket* fec_packet) const {
if (fec_packet->pkt->data.size() <=
kBaseHeaderSize + kStreamSpecificHeaderSize) {
RTC_LOG(LS_WARNING) << "Discarding truncated FlexFEC packet.";
return false;
}
uint8_t* const data = fec_packet->pkt->data.MutableData();
bool r_bit = (data[0] & 0x80) != 0;
if (r_bit) {
RTC_LOG(LS_INFO)
<< "FlexFEC03 packet with retransmission bit set. We do not "
"support this, thus discarding the packet.";
return false;
}
bool f_bit = (data[0] & 0x40) != 0;
if (f_bit) {
RTC_LOG(LS_INFO)
<< "FlexFEC03 packet with inflexible generator matrix. We do "
"not support this, thus discarding packet.";
return false;
}
uint8_t ssrc_count = ByteReader<uint8_t>::ReadBigEndian(&data[8]);
if (ssrc_count != 1) {
RTC_LOG(LS_INFO)
<< "FlexFEC03 packet protecting multiple media SSRCs. We do not "
"support this, thus discarding packet.";
return false;
}
uint32_t protected_ssrc = ByteReader<uint32_t>::ReadBigEndian(&data[12]);
uint16_t seq_num_base = ByteReader<uint16_t>::ReadBigEndian(&data[16]);
// Parse the FlexFEC packet mask and remove the interleaved K-bits.
// (See FEC header schematic in flexfec_header_reader_writer.h.)
// We store the packed packet mask in-band, which "destroys" the standards
// compliance of the header. That is fine though, since the code that
// reads from the header (from this point and onwards) is aware of this.
// TODO(brandtr): When the FEC packet classes have been refactored, store
// the packed packet masks out-of-band, thus leaving the FlexFEC header as is.
//
// We treat the mask parts as unsigned integers with host order endianness
// in order to simplify the bit shifting between bytes.
if (fec_packet->pkt->data.size() < kHeaderSizes[0]) {
RTC_LOG(LS_WARNING) << "Discarding truncated FlexFEC03 packet.";
return false;
}
uint8_t* const packet_mask = data + kPacketMaskOffset;
bool k_bit0 = (packet_mask[0] & 0x80) != 0;
uint16_t mask_part0 = ByteReader<uint16_t>::ReadBigEndian(&packet_mask[0]);
// Shift away K-bit 0, implicitly clearing the last bit.
mask_part0 <<= 1;
ByteWriter<uint16_t>::WriteBigEndian(&packet_mask[0], mask_part0);
size_t packet_mask_size;
if (k_bit0) {
// The first K-bit is set, and the packet mask is thus only 2 bytes long.
// We have now read the entire FEC header, and the rest of the packet
// is payload.
packet_mask_size = kFlexfecPacketMaskSizes[0];
} else {
if (fec_packet->pkt->data.size() < kHeaderSizes[1]) {
return false;
}
bool k_bit1 = (packet_mask[2] & 0x80) != 0;
// We have already shifted the first two bytes of the packet mask one step
// to the left, thus removing K-bit 0. We will now shift the next four bytes
// of the packet mask two steps to the left. (One step for the removed
// K-bit 0, and one step for the to be removed K-bit 1).
uint8_t bit15 = (packet_mask[2] >> 6) & 0x01;
packet_mask[1] |= bit15;
uint32_t mask_part1 = ByteReader<uint32_t>::ReadBigEndian(&packet_mask[2]);
// Shift away K-bit 1 and bit 15, implicitly clearing the last two bits.
mask_part1 <<= 2;
ByteWriter<uint32_t>::WriteBigEndian(&packet_mask[2], mask_part1);
if (k_bit1) {
// The first K-bit is clear, but the second K-bit is set. The packet
// mask is thus 6 bytes long. We have now read the entire FEC header,
// and the rest of the packet is payload.
packet_mask_size = kFlexfecPacketMaskSizes[1];
} else {
if (fec_packet->pkt->data.size() < kHeaderSizes[2]) {
RTC_LOG(LS_WARNING) << "Discarding truncated FlexFEC03 packet.";
return false;
}
bool k_bit2 = (packet_mask[6] & 0x80) != 0;
if (k_bit2) {
// The first and second K-bits are clear, but the third K-bit is set.
// The packet mask is thus 14 bytes long. We have now read the entire
// FEC header, and the rest of the packet is payload.
packet_mask_size = kFlexfecPacketMaskSizes[2];
} else {
RTC_LOG(LS_WARNING)
<< "Discarding FlexFEC03 packet with malformed header.";
return false;
}
// At this point, K-bits 0 and 1 have been removed, and the front-most
// part of the FlexFEC packet mask has been packed accordingly. We will
// now shift the remaining part of the packet mask three steps to
// the left. This corresponds to the (in total) three K-bits, which
// have been removed.
uint8_t tail_bits = (packet_mask[6] >> 5) & 0x03;
packet_mask[5] |= tail_bits;
uint64_t mask_part2 =
ByteReader<uint64_t>::ReadBigEndian(&packet_mask[6]);
// Shift away K-bit 2, bit 46, and bit 47, implicitly clearing the last
// three bits.
mask_part2 <<= 3;
ByteWriter<uint64_t>::WriteBigEndian(&packet_mask[6], mask_part2);
}
}
// Store "ULPFECized" packet mask info.
fec_packet->fec_header_size = FlexfecHeaderSize(packet_mask_size);
fec_packet->protected_streams = {{.ssrc = protected_ssrc,
.seq_num_base = seq_num_base,
.packet_mask_offset = kPacketMaskOffset,
.packet_mask_size = packet_mask_size}};
// In FlexFEC, all media packets are protected in their entirety.
fec_packet->protection_length =
fec_packet->pkt->data.size() - fec_packet->fec_header_size;
return true;
}
Flexfec03HeaderWriter::Flexfec03HeaderWriter()
: FecHeaderWriter(kMaxMediaPackets, kMaxFecPackets, kHeaderSizes[2]) {}
Flexfec03HeaderWriter::~Flexfec03HeaderWriter() = default;
size_t Flexfec03HeaderWriter::MinPacketMaskSize(const uint8_t* packet_mask,
size_t packet_mask_size) const {
if (packet_mask_size == kUlpfecPacketMaskSizeLBitClear &&
(packet_mask[1] & 0x01) == 0) {
// Packet mask is 16 bits long, with bit 15 clear.
// It can be used as is.
return kFlexfecPacketMaskSizes[0];
} else if (packet_mask_size == kUlpfecPacketMaskSizeLBitClear) {
// Packet mask is 16 bits long, with bit 15 set.
// We must expand the packet mask with zeros in the FlexFEC header.
return kFlexfecPacketMaskSizes[1];
} else if (packet_mask_size == kUlpfecPacketMaskSizeLBitSet &&
(packet_mask[5] & 0x03) == 0) {
// Packet mask is 48 bits long, with bits 46 and 47 clear.
// It can be used as is.
return kFlexfecPacketMaskSizes[1];
} else if (packet_mask_size == kUlpfecPacketMaskSizeLBitSet) {
// Packet mask is 48 bits long, with at least one of bits 46 and 47 set.
// We must expand it with zeros.
return kFlexfecPacketMaskSizes[2];
}
RTC_DCHECK_NOTREACHED() << "Incorrect packet mask size: " << packet_mask_size
<< ".";
return kFlexfecPacketMaskSizes[2];
}
size_t Flexfec03HeaderWriter::FecHeaderSize(size_t packet_mask_size) const {
return FlexfecHeaderSize(packet_mask_size);
}
// This function adapts the precomputed ULPFEC packet masks to the
// FlexFEC header standard. Note that the header size is computed by
// FecHeaderSize(), so in this function we can be sure that we are
// writing in space that is intended for the header.
void Flexfec03HeaderWriter::FinalizeFecHeader(
rtc::ArrayView<const ProtectedStream> protected_streams,
ForwardErrorCorrection::Packet& fec_packet) const {
RTC_CHECK_EQ(protected_streams.size(), 1);
uint32_t media_ssrc = protected_streams[0].ssrc;
uint16_t seq_num_base = protected_streams[0].seq_num_base;
const uint8_t* packet_mask = protected_streams[0].packet_mask.data();
size_t packet_mask_size = protected_streams[0].packet_mask.size();
uint8_t* data = fec_packet.data.MutableData();
data[0] &= 0x7f; // Clear R bit.
data[0] &= 0xbf; // Clear F bit.
ByteWriter<uint8_t>::WriteBigEndian(&data[8], kSsrcCount);
ByteWriter<uint32_t, 3>::WriteBigEndian(&data[9], kReservedBits);
ByteWriter<uint32_t>::WriteBigEndian(&data[12], media_ssrc);
ByteWriter<uint16_t>::WriteBigEndian(&data[16], seq_num_base);
// Adapt ULPFEC packet mask to FlexFEC header.
//
// We treat the mask parts as unsigned integers with host order endianness
// in order to simplify the bit shifting between bytes.
uint8_t* const written_packet_mask = data + kPacketMaskOffset;
if (packet_mask_size == kUlpfecPacketMaskSizeLBitSet) {
// The packet mask is 48 bits long.
uint16_t tmp_mask_part0 =
ByteReader<uint16_t>::ReadBigEndian(&packet_mask[0]);
uint32_t tmp_mask_part1 =
ByteReader<uint32_t>::ReadBigEndian(&packet_mask[2]);
tmp_mask_part0 >>= 1; // Shift, thus clearing K-bit 0.
ByteWriter<uint16_t>::WriteBigEndian(&written_packet_mask[0],
tmp_mask_part0);
tmp_mask_part1 >>= 2; // Shift, thus clearing K-bit 1 and bit 15.
ByteWriter<uint32_t>::WriteBigEndian(&written_packet_mask[2],
tmp_mask_part1);
bool bit15 = (packet_mask[1] & 0x01) != 0;
if (bit15)
written_packet_mask[2] |= 0x40; // Set bit 15.
bool bit46 = (packet_mask[5] & 0x02) != 0;
bool bit47 = (packet_mask[5] & 0x01) != 0;
if (!bit46 && !bit47) {
written_packet_mask[2] |= 0x80; // Set K-bit 1.
} else {
memset(&written_packet_mask[6], 0, 8); // Clear all trailing bits.
written_packet_mask[6] |= 0x80; // Set K-bit 2.
if (bit46)
written_packet_mask[6] |= 0x40; // Set bit 46.
if (bit47)
written_packet_mask[6] |= 0x20; // Set bit 47.
}
} else if (packet_mask_size == kUlpfecPacketMaskSizeLBitClear) {
// The packet mask is 16 bits long.
uint16_t tmp_mask_part0 =
ByteReader<uint16_t>::ReadBigEndian(&packet_mask[0]);
tmp_mask_part0 >>= 1; // Shift, thus clearing K-bit 0.
ByteWriter<uint16_t>::WriteBigEndian(&written_packet_mask[0],
tmp_mask_part0);
bool bit15 = (packet_mask[1] & 0x01) != 0;
if (!bit15) {
written_packet_mask[0] |= 0x80; // Set K-bit 0.
} else {
memset(&written_packet_mask[2], 0U, 4); // Clear all trailing bits.
written_packet_mask[2] |= 0x80; // Set K-bit 1.
written_packet_mask[2] |= 0x40; // Set bit 15.
}
} else {
RTC_DCHECK_NOTREACHED()
<< "Incorrect packet mask size: " << packet_mask_size << ".";
}
}
} // namespace webrtc

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_FLEXFEC_03_HEADER_READER_WRITER_H_
#define MODULES_RTP_RTCP_SOURCE_FLEXFEC_03_HEADER_READER_WRITER_H_
#include <stddef.h>
#include <stdint.h>
#include "modules/rtp_rtcp/source/forward_error_correction.h"
namespace webrtc {
// FlexFEC header, minimum 20 bytes.
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 0 |R|F|P|X| CC |M| PT recovery | length recovery |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 4 | TS recovery |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 8 | SSRCCount | reserved |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// 12 | SSRC_i |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 16 | SN base_i |k| Mask [0-14] |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 20 |k| Mask [15-45] (optional) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 24 |k| |
// +-+ Mask [46-108] (optional) |
// 28 | |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// : ... next in SSRC_i ... :
//
//
// FlexFEC header in 'inflexible' mode (F = 1), 20 bytes.
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 0 |0|1|P|X| CC |M| PT recovery | length recovery |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 4 | TS recovery |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 8 | SSRCCount | reserved |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 12 | SSRC_i |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 16 | SN base_i | M (columns) | N (rows) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
class Flexfec03HeaderReader : public FecHeaderReader {
public:
Flexfec03HeaderReader();
~Flexfec03HeaderReader() override;
bool ReadFecHeader(
ForwardErrorCorrection::ReceivedFecPacket* fec_packet) const override;
};
class Flexfec03HeaderWriter : public FecHeaderWriter {
public:
Flexfec03HeaderWriter();
~Flexfec03HeaderWriter() override;
size_t MinPacketMaskSize(const uint8_t* packet_mask,
size_t packet_mask_size) const override;
size_t FecHeaderSize(size_t packet_mask_row_size) const override;
void FinalizeFecHeader(
rtc::ArrayView<const ProtectedStream> protected_streams,
ForwardErrorCorrection::Packet& fec_packet) const override;
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_FLEXFEC_03_HEADER_READER_WRITER_H_

View file

@ -0,0 +1,328 @@
/*
* 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 "modules/rtp_rtcp/source/flexfec_header_reader_writer.h"
#include <string.h>
#include "api/scoped_refptr.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/forward_error_correction_internal.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
// Maximum number of media packets that can be protected in one batch.
constexpr size_t kMaxMediaPackets = 48; // Since we are reusing ULPFEC masks.
// Maximum number of media packets tracked by FEC decoder.
// Maintain a sufficiently larger tracking window than `kMaxMediaPackets`
// to account for packet reordering in pacer/ network.
constexpr size_t kMaxTrackedMediaPackets = 4 * kMaxMediaPackets;
// Maximum number of FEC packets stored inside ForwardErrorCorrection.
constexpr size_t kMaxFecPackets = kMaxMediaPackets;
// Size (in bytes) of packet masks, given number of K bits set.
constexpr size_t kFlexfecPacketMaskSizes[] = {2, 6, 14};
// Size (in bytes) of part of header which is not packet mask specific.
constexpr size_t kBaseHeaderSize = 8;
// Size (in bytes) of part of header which is stream specific.
constexpr size_t kStreamSpecificHeaderSize = 2;
// Size (in bytes) of header, given the single stream packet mask size, i.e.
// the number of K-bits set.
constexpr size_t kHeaderSizes[] = {
kBaseHeaderSize + kStreamSpecificHeaderSize + kFlexfecPacketMaskSizes[0],
kBaseHeaderSize + kStreamSpecificHeaderSize + kFlexfecPacketMaskSizes[1],
kBaseHeaderSize + kStreamSpecificHeaderSize + kFlexfecPacketMaskSizes[2]};
// Here we count the K-bits as belonging to the packet mask.
// This can be used in conjunction with FlexfecHeaderWriter::MinPacketMaskSize,
// which calculates a bound on the needed packet mask size including K-bits,
// given a packet mask without K-bits.
size_t FlexfecHeaderSize(size_t packet_mask_size) {
RTC_DCHECK_LE(packet_mask_size, kFlexfecPacketMaskSizes[2]);
if (packet_mask_size <= kFlexfecPacketMaskSizes[0]) {
return kHeaderSizes[0];
} else if (packet_mask_size <= kFlexfecPacketMaskSizes[1]) {
return kHeaderSizes[1];
}
return kHeaderSizes[2];
}
} // namespace
FlexfecHeaderReader::FlexfecHeaderReader()
: FecHeaderReader(kMaxTrackedMediaPackets, kMaxFecPackets) {}
FlexfecHeaderReader::~FlexfecHeaderReader() = default;
// TODO(brandtr): Update this function when we support flexible masks,
// and retransmissions.
bool FlexfecHeaderReader::ReadFecHeader(
ForwardErrorCorrection::ReceivedFecPacket* fec_packet) const {
// Protected ssrcs should already be populated from RTP header.
if (fec_packet->protected_streams.empty()) {
RTC_LOG(LS_WARNING)
<< "Discarding FlexFEC packet with no protected sources.";
return false;
}
if (fec_packet->pkt->data.size() <=
kBaseHeaderSize + kStreamSpecificHeaderSize) {
RTC_LOG(LS_WARNING) << "Discarding truncated FlexFEC packet.";
return false;
}
uint8_t* const data = fec_packet->pkt->data.MutableData();
bool r_bit = (data[0] & 0x80) != 0;
if (r_bit) {
RTC_LOG(LS_INFO)
<< "FlexFEC packet with retransmission bit set. We do not yet "
"support this, thus discarding the packet.";
return false;
}
bool f_bit = (data[0] & 0x40) != 0;
if (f_bit) {
RTC_LOG(LS_INFO)
<< "FlexFEC packet with inflexible generator matrix. We do "
"not yet support this, thus discarding packet.";
return false;
}
// First seq_num will be in byte index 8
// (See FEC header schematic in flexfec_header_reader_writer.h.)
size_t byte_index = 8;
for (size_t i = 0; i < fec_packet->protected_streams.size(); ++i) {
if (fec_packet->pkt->data.size() < byte_index + kStreamSpecificHeaderSize) {
RTC_LOG(LS_WARNING) << "Discarding truncated FlexFEC packet.";
return false;
}
fec_packet->protected_streams[i].seq_num_base =
ByteReader<uint16_t>::ReadBigEndian(&data[byte_index]);
byte_index += kStreamSpecificHeaderSize;
// Parse the FlexFEC packet mask and remove the interleaved K-bits.
// (See FEC header schematic in flexfec_header_reader_writer.h.)
// We store the packed packet mask in-band, which "destroys" the standards
// compliance of the header. That is fine though, since the code that
// reads from the header (from this point and onwards) is aware of this.
// TODO(brandtr): When the FEC packet classes have been refactored, store
// the packed packet masks out-of-band, thus leaving the FlexFEC header as
// is.
//
// We treat the mask parts as unsigned integers with host order endianness
// in order to simplify the bit shifting between bytes.
if (fec_packet->pkt->data.size() <
(byte_index + kFlexfecPacketMaskSizes[0])) {
RTC_LOG(LS_WARNING) << "Discarding truncated FlexFEC packet.";
return false;
}
fec_packet->protected_streams[i].packet_mask_offset = byte_index;
bool k_bit0 = (data[byte_index] & 0x80) != 0;
uint16_t mask_part0 =
ByteReader<uint16_t>::ReadBigEndian(&data[byte_index]);
// Shift away K-bit 0, implicitly clearing the last bit.
mask_part0 <<= 1;
ByteWriter<uint16_t>::WriteBigEndian(&data[byte_index], mask_part0);
byte_index += kFlexfecPacketMaskSizes[0];
if (!k_bit0) {
// The first K-bit is clear, and the packet mask is thus only 2 bytes
// long. We have finished reading the properties for current ssrc.
fec_packet->protected_streams[i].packet_mask_size =
kFlexfecPacketMaskSizes[0];
} else {
if (fec_packet->pkt->data.size() <
(byte_index + kFlexfecPacketMaskSizes[1] -
kFlexfecPacketMaskSizes[0])) {
return false;
}
bool k_bit1 = (data[byte_index] & 0x80) != 0;
// We have already shifted the first two bytes of the packet mask one step
// to the left, thus removing K-bit 0. We will now shift the next four
// bytes of the packet mask two steps to the left. (One step for the
// removed K-bit 0, and one step for the to be removed K-bit 1).
uint8_t bit15 = (data[byte_index] >> 6) & 0x01;
data[byte_index - 1] |= bit15;
uint32_t mask_part1 =
ByteReader<uint32_t>::ReadBigEndian(&data[byte_index]);
// Shift away K-bit 1 and bit 15, implicitly clearing the last two bits.
mask_part1 <<= 2;
ByteWriter<uint32_t>::WriteBigEndian(&data[byte_index], mask_part1);
byte_index += kFlexfecPacketMaskSizes[1] - kFlexfecPacketMaskSizes[0];
if (!k_bit1) {
// The first K-bit is set, but the second K-bit is clear. The packet
// mask is thus 6 bytes long. We have finished reading the properties
// for current ssrc.
fec_packet->protected_streams[i].packet_mask_size =
kFlexfecPacketMaskSizes[1];
} else {
if (fec_packet->pkt->data.size() <
(byte_index + kFlexfecPacketMaskSizes[2] -
kFlexfecPacketMaskSizes[1])) {
RTC_LOG(LS_WARNING) << "Discarding truncated FlexFEC packet.";
return false;
}
fec_packet->protected_streams[i].packet_mask_size =
kFlexfecPacketMaskSizes[2];
// At this point, K-bits 0 and 1 have been removed, and the front-most
// part of the FlexFEC packet mask has been packed accordingly. We will
// now shift the remaining part of the packet mask two steps to
// the left. This corresponds to the (in total) two K-bits, which
// have been removed.
uint8_t tail_bits = (data[byte_index] >> 6) & 0x03;
data[byte_index - 1] |= tail_bits;
uint64_t mask_part2 =
ByteReader<uint64_t>::ReadBigEndian(&data[byte_index]);
// Shift away bit 46, and bit 47, which were copied to the previous
// part of the mask, implicitly clearing the last two bits.
mask_part2 <<= 2;
ByteWriter<uint64_t>::WriteBigEndian(&data[byte_index], mask_part2);
byte_index += kFlexfecPacketMaskSizes[2] - kFlexfecPacketMaskSizes[1];
}
}
}
fec_packet->fec_header_size = byte_index;
// In FlexFEC, all media packets are protected in their entirety.
fec_packet->protection_length =
fec_packet->pkt->data.size() - fec_packet->fec_header_size;
return true;
}
FlexfecHeaderWriter::FlexfecHeaderWriter()
: FecHeaderWriter(kMaxMediaPackets, kMaxFecPackets, kHeaderSizes[2]) {}
FlexfecHeaderWriter::~FlexfecHeaderWriter() = default;
size_t FlexfecHeaderWriter::MinPacketMaskSize(const uint8_t* packet_mask,
size_t packet_mask_size) const {
if (packet_mask_size == kUlpfecPacketMaskSizeLBitClear &&
(packet_mask[1] & 0x01) == 0) {
// Packet mask is 16 bits long, with bit 15 clear.
// It can be used as is.
return kFlexfecPacketMaskSizes[0];
} else if (packet_mask_size == kUlpfecPacketMaskSizeLBitClear) {
// Packet mask is 16 bits long, with bit 15 set.
// We must expand the packet mask with zeros in the FlexFEC header.
return kFlexfecPacketMaskSizes[1];
} else if (packet_mask_size == kUlpfecPacketMaskSizeLBitSet &&
(packet_mask[5] & 0x03) == 0) {
// Packet mask is 48 bits long, with bits 46 and 47 clear.
// It can be used as is.
return kFlexfecPacketMaskSizes[1];
} else if (packet_mask_size == kUlpfecPacketMaskSizeLBitSet) {
// Packet mask is 48 bits long, with at least one of bits 46 and 47 set.
// We must expand it with zeros.
return kFlexfecPacketMaskSizes[2];
}
RTC_DCHECK_NOTREACHED() << "Incorrect packet mask size: " << packet_mask_size
<< ".";
return kFlexfecPacketMaskSizes[2];
}
size_t FlexfecHeaderWriter::FecHeaderSize(size_t packet_mask_size) const {
return FlexfecHeaderSize(packet_mask_size);
}
// This function adapts the precomputed ULPFEC packet masks to the
// FlexFEC header standard. Note that the header size is computed by
// FecHeaderSize(), so in this function we can be sure that we are
// writing in space that is intended for the header.
//
// TODO(brandtr): Update this function when we support offset-based masks
// and retransmissions.
void FlexfecHeaderWriter::FinalizeFecHeader(
rtc::ArrayView<const ProtectedStream> protected_streams,
ForwardErrorCorrection::Packet& fec_packet) const {
uint8_t* data = fec_packet.data.MutableData();
*data &= 0x7f; // Clear R bit.
*data &= 0xbf; // Clear F bit.
// First seq_num will be in byte index 8
// (See FEC header schematic in flexfec_header_reader_writer.h.)
uint8_t* write_at = data + 8;
for (const ProtectedStream& protected_stream : protected_streams) {
ByteWriter<uint16_t>::WriteBigEndian(write_at,
protected_stream.seq_num_base);
write_at += kStreamSpecificHeaderSize;
// Adapt ULPFEC packet mask to FlexFEC header.
//
// We treat the mask parts as unsigned integers with host order endianness
// in order to simplify the bit shifting between bytes.
if (protected_stream.packet_mask.size() == kUlpfecPacketMaskSizeLBitSet) {
// The packet mask is 48 bits long.
uint16_t tmp_mask_part0 =
ByteReader<uint16_t>::ReadBigEndian(&protected_stream.packet_mask[0]);
uint32_t tmp_mask_part1 =
ByteReader<uint32_t>::ReadBigEndian(&protected_stream.packet_mask[2]);
tmp_mask_part0 >>= 1; // Shift, thus clearing K-bit 0.
ByteWriter<uint16_t>::WriteBigEndian(write_at, tmp_mask_part0);
*write_at |= 0x80; // Set K-bit 0.
write_at += kFlexfecPacketMaskSizes[0];
tmp_mask_part1 >>= 2; // Shift twice, thus clearing K-bit 1 and bit 15.
ByteWriter<uint32_t>::WriteBigEndian(write_at, tmp_mask_part1);
bool bit15 = (protected_stream.packet_mask[1] & 0x01) != 0;
if (bit15)
*write_at |= 0x40; // Set bit 15.
bool bit46 = (protected_stream.packet_mask[5] & 0x02) != 0;
bool bit47 = (protected_stream.packet_mask[5] & 0x01) != 0;
if (!bit46 && !bit47) {
write_at += kFlexfecPacketMaskSizes[1] - kFlexfecPacketMaskSizes[0];
} else {
*write_at |= 0x80; // Set K-bit 1.
write_at += kFlexfecPacketMaskSizes[1] - kFlexfecPacketMaskSizes[0];
// Clear all trailing bits.
memset(write_at, 0,
kFlexfecPacketMaskSizes[2] - kFlexfecPacketMaskSizes[1]);
if (bit46)
*write_at |= 0x80; // Set bit 46.
if (bit47)
*write_at |= 0x40; // Set bit 47.
write_at += kFlexfecPacketMaskSizes[2] - kFlexfecPacketMaskSizes[1];
}
} else if (protected_stream.packet_mask.size() ==
kUlpfecPacketMaskSizeLBitClear) {
// The packet mask is 16 bits long.
uint16_t tmp_mask_part0 =
ByteReader<uint16_t>::ReadBigEndian(&protected_stream.packet_mask[0]);
tmp_mask_part0 >>= 1; // Shift, thus clearing K-bit 0.
ByteWriter<uint16_t>::WriteBigEndian(write_at, tmp_mask_part0);
bool bit15 = (protected_stream.packet_mask[1] & 0x01) != 0;
if (!bit15) {
write_at += kFlexfecPacketMaskSizes[0];
} else {
*write_at |= 0x80; // Set K-bit 0.
write_at += kFlexfecPacketMaskSizes[0];
// Clear all trailing bits.
memset(write_at, 0U,
kFlexfecPacketMaskSizes[1] - kFlexfecPacketMaskSizes[0]);
*write_at |= 0x40; // Set bit 15.
write_at += kFlexfecPacketMaskSizes[1] - kFlexfecPacketMaskSizes[0];
}
} else {
RTC_DCHECK_NOTREACHED() << "Incorrect packet mask size: "
<< protected_stream.packet_mask.size() << ".";
}
}
}
} // namespace webrtc

View file

@ -0,0 +1,67 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_FLEXFEC_HEADER_READER_WRITER_H_
#define MODULES_RTP_RTCP_SOURCE_FLEXFEC_HEADER_READER_WRITER_H_
#include <stddef.h>
#include <stdint.h>
#include "modules/rtp_rtcp/source/forward_error_correction.h"
namespace webrtc {
// FlexFEC header in flexible mode (R=0, F=0), minimum 12 bytes.
// https://datatracker.ietf.org/doc/html/rfc8627#section-4.2.2.1
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 0 |0|0|P|X| CC |M| PT recovery | length recovery |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 4 | TS recovery |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 8 | SN base_i |k| Mask [0-14] |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 12 |k| Mask [15-45] (optional) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 16 | Mask [46-109] (optional) |
// 20 | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ... next SN base and Mask for CSRC_i in CSRC list ... |
//
class FlexfecHeaderReader : public FecHeaderReader {
public:
FlexfecHeaderReader();
~FlexfecHeaderReader() override;
bool ReadFecHeader(
ForwardErrorCorrection::ReceivedFecPacket* fec_packet) const override;
};
class FlexfecHeaderWriter : public FecHeaderWriter {
public:
FlexfecHeaderWriter();
~FlexfecHeaderWriter() override;
size_t MinPacketMaskSize(const uint8_t* packet_mask,
size_t packet_mask_size) const override;
size_t FecHeaderSize(size_t packet_mask_row_size) const override;
void FinalizeFecHeader(
rtc::ArrayView<const ProtectedStream> protected_streams,
ForwardErrorCorrection::Packet& fec_packet) const override;
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_FLEXFEC_HEADER_READER_WRITER_H_

View file

@ -0,0 +1,202 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/include/flexfec_receiver.h"
#include <string.h>
#include "api/array_view.h"
#include "api/scoped_refptr.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
// Minimum header size (in bytes) of a well-formed non-singular FlexFEC packet.
constexpr size_t kMinFlexfecHeaderSize = 20;
// How often to log the recovered packets to the text log.
constexpr TimeDelta kPacketLogInterval = TimeDelta::Seconds(10);
} // namespace
FlexfecReceiver::FlexfecReceiver(
uint32_t ssrc,
uint32_t protected_media_ssrc,
RecoveredPacketReceiver* recovered_packet_receiver)
: FlexfecReceiver(Clock::GetRealTimeClock(),
ssrc,
protected_media_ssrc,
recovered_packet_receiver) {}
FlexfecReceiver::FlexfecReceiver(
Clock* clock,
uint32_t ssrc,
uint32_t protected_media_ssrc,
RecoveredPacketReceiver* recovered_packet_receiver)
: ssrc_(ssrc),
protected_media_ssrc_(protected_media_ssrc),
erasure_code_(
ForwardErrorCorrection::CreateFlexfec(ssrc, protected_media_ssrc)),
recovered_packet_receiver_(recovered_packet_receiver),
clock_(clock) {
// It's OK to create this object on a different thread/task queue than
// the one used during main operation.
sequence_checker_.Detach();
}
FlexfecReceiver::~FlexfecReceiver() = default;
void FlexfecReceiver::OnRtpPacket(const RtpPacketReceived& packet) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
// If this packet was recovered, it might be originating from
// ProcessReceivedPacket in this object. To avoid lifetime issues with
// `recovered_packets_`, we therefore break the cycle here.
// This might reduce decoding efficiency a bit, since we can't disambiguate
// recovered packets by RTX from recovered packets by FlexFEC.
if (packet.recovered())
return;
std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> received_packet =
AddReceivedPacket(packet);
if (!received_packet)
return;
ProcessReceivedPacket(*received_packet);
}
FecPacketCounter FlexfecReceiver::GetPacketCounter() const {
RTC_DCHECK_RUN_ON(&sequence_checker_);
return packet_counter_;
}
// TODO(eladalon): Consider using packet.recovered() to avoid processing
// recovered packets here.
std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>
FlexfecReceiver::AddReceivedPacket(const RtpPacketReceived& packet) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
// RTP packets with a full base header (12 bytes), but without payload,
// could conceivably be useful in the decoding. Therefore we check
// with a non-strict inequality here.
RTC_DCHECK_GE(packet.size(), kRtpHeaderSize);
// Demultiplex based on SSRC, and insert into erasure code decoder.
std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> received_packet(
new ForwardErrorCorrection::ReceivedPacket());
received_packet->seq_num = packet.SequenceNumber();
received_packet->ssrc = packet.Ssrc();
received_packet->extensions = packet.extension_manager();
if (received_packet->ssrc == ssrc_) {
// This is a FlexFEC packet.
if (packet.payload_size() < kMinFlexfecHeaderSize) {
RTC_LOG(LS_WARNING) << "Truncated FlexFEC packet, discarding.";
return nullptr;
}
received_packet->is_fec = true;
++packet_counter_.num_fec_packets;
// Insert packet payload into erasure code.
received_packet->pkt = rtc::scoped_refptr<ForwardErrorCorrection::Packet>(
new ForwardErrorCorrection::Packet());
received_packet->pkt->data =
packet.Buffer().Slice(packet.headers_size(), packet.payload_size());
} else {
// This is a media packet, or a FlexFEC packet belonging to some
// other FlexFEC stream.
if (received_packet->ssrc != protected_media_ssrc_) {
return nullptr;
}
received_packet->is_fec = false;
// Insert entire packet into erasure code.
// Create a copy and fill with zeros all mutable extensions.
received_packet->pkt = rtc::scoped_refptr<ForwardErrorCorrection::Packet>(
new ForwardErrorCorrection::Packet());
RtpPacketReceived packet_copy(packet);
packet_copy.ZeroMutableExtensions();
received_packet->pkt->data = packet_copy.Buffer();
}
++packet_counter_.num_packets;
return received_packet;
}
// Note that the implementation of this member function and the implementation
// in UlpfecReceiver::ProcessReceivedFec() are slightly different.
// This implementation only returns _recovered_ media packets through the
// callback, whereas the implementation in UlpfecReceiver returns _all inserted_
// media packets through the callback. The latter behaviour makes sense
// for ULPFEC, since the ULPFEC receiver is owned by the RtpVideoStreamReceiver.
// Here, however, the received media pipeline is more decoupled from the
// FlexFEC decoder, and we therefore do not interfere with the reception
// of non-recovered media packets.
void FlexfecReceiver::ProcessReceivedPacket(
const ForwardErrorCorrection::ReceivedPacket& received_packet) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
// Decode.
ForwardErrorCorrection::DecodeFecResult decode_result =
erasure_code_->DecodeFec(received_packet, &recovered_packets_);
if (decode_result.num_recovered_packets == 0) {
return;
}
// Return recovered packets through callback.
for (const auto& recovered_packet : recovered_packets_) {
RTC_CHECK(recovered_packet);
if (recovered_packet->returned) {
continue;
}
++packet_counter_.num_recovered_packets;
// Set this flag first, since OnRecoveredPacket may end up here
// again, with the same packet.
recovered_packet->returned = true;
RTC_CHECK_GE(recovered_packet->pkt->data.size(), kRtpHeaderSize);
RtpPacketReceived parsed_packet(&received_packet.extensions);
if (!parsed_packet.Parse(recovered_packet->pkt->data)) {
continue;
}
parsed_packet.set_recovered(true);
// TODO(brandtr): Update here when we support protecting audio packets too.
parsed_packet.set_payload_type_frequency(kVideoPayloadTypeFrequency);
recovered_packet_receiver_->OnRecoveredPacket(parsed_packet);
// Periodically log the incoming packets at LS_INFO.
Timestamp now = clock_->CurrentTime();
bool should_log_periodically =
now - last_recovered_packet_ > kPacketLogInterval;
if (RTC_LOG_CHECK_LEVEL(LS_VERBOSE) || should_log_periodically) {
rtc::LoggingSeverity level =
should_log_periodically ? rtc::LS_INFO : rtc::LS_VERBOSE;
RTC_LOG_V(level) << "Recovered media packet with SSRC: "
<< parsed_packet.Ssrc() << " seq "
<< parsed_packet.SequenceNumber() << " recovered length "
<< recovered_packet->pkt->data.size()
<< " received length "
<< received_packet.pkt->data.size()
<< " from FlexFEC stream with SSRC: " << ssrc_;
if (should_log_periodically) {
last_recovered_packet_ = now;
}
}
}
}
} // namespace webrtc

View file

@ -0,0 +1,204 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/include/flexfec_sender.h"
#include <string.h>
#include <list>
#include <utility>
#include "absl/strings/string_view.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/forward_error_correction.h"
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
// Let first sequence number be in the first half of the interval.
constexpr uint16_t kMaxInitRtpSeqNumber = 0x7fff;
// See breakdown in flexfec_header_reader_writer.cc.
constexpr size_t kFlexfecMaxHeaderSize = 32;
// Since we will mainly use FlexFEC to protect video streams, we use a 90 kHz
// clock for the RTP timestamps. (This is according to the RFC, which states
// that it is RECOMMENDED to use the same clock frequency for FlexFEC as for
// the protected media stream.)
// The constant converts from clock millisecond timestamps to the 90 kHz
// RTP timestamp.
const int kMsToRtpTimestamp = kVideoPayloadTypeFrequency / 1000;
// How often to log the generated FEC packets to the text log.
constexpr TimeDelta kPacketLogInterval = TimeDelta::Seconds(10);
RtpHeaderExtensionMap RegisterSupportedExtensions(
const std::vector<RtpExtension>& rtp_header_extensions) {
RtpHeaderExtensionMap map;
for (const auto& extension : rtp_header_extensions) {
if (extension.uri == TransportSequenceNumber::Uri()) {
map.Register<TransportSequenceNumber>(extension.id);
} else if (extension.uri == AbsoluteSendTime::Uri()) {
map.Register<AbsoluteSendTime>(extension.id);
} else if (extension.uri == TransmissionOffset::Uri()) {
map.Register<TransmissionOffset>(extension.id);
} else if (extension.uri == RtpMid::Uri()) {
map.Register<RtpMid>(extension.id);
} else {
RTC_LOG(LS_INFO)
<< "FlexfecSender only supports RTP header extensions for "
"BWE and MID, so the extension "
<< extension.ToString() << " will not be used.";
}
}
return map;
}
} // namespace
FlexfecSender::FlexfecSender(
int payload_type,
uint32_t ssrc,
uint32_t protected_media_ssrc,
absl::string_view mid,
const std::vector<RtpExtension>& rtp_header_extensions,
rtc::ArrayView<const RtpExtensionSize> extension_sizes,
const RtpState* rtp_state,
Clock* clock)
: clock_(clock),
random_(clock_->TimeInMicroseconds()),
payload_type_(payload_type),
// Reset RTP state if this is not the first time we are operating.
// Otherwise, randomize the initial timestamp offset and RTP sequence
// numbers. (This is not intended to be cryptographically strong.)
timestamp_offset_(rtp_state ? rtp_state->start_timestamp
: random_.Rand<uint32_t>()),
ssrc_(ssrc),
protected_media_ssrc_(protected_media_ssrc),
mid_(mid),
seq_num_(rtp_state ? rtp_state->sequence_number
: random_.Rand(1, kMaxInitRtpSeqNumber)),
ulpfec_generator_(
ForwardErrorCorrection::CreateFlexfec(ssrc, protected_media_ssrc),
clock_),
rtp_header_extension_map_(
RegisterSupportedExtensions(rtp_header_extensions)),
header_extensions_size_(
RtpHeaderExtensionSize(extension_sizes, rtp_header_extension_map_)),
fec_bitrate_(/*max_window_size=*/TimeDelta::Seconds(1)) {
// This object should not have been instantiated if FlexFEC is disabled.
RTC_DCHECK_GE(payload_type, 0);
RTC_DCHECK_LE(payload_type, 127);
}
FlexfecSender::~FlexfecSender() = default;
// We are reusing the implementation from UlpfecGenerator for SetFecParameters,
// AddRtpPacketAndGenerateFec, and FecAvailable.
void FlexfecSender::SetProtectionParameters(
const FecProtectionParams& delta_params,
const FecProtectionParams& key_params) {
ulpfec_generator_.SetProtectionParameters(delta_params, key_params);
}
void FlexfecSender::AddPacketAndGenerateFec(const RtpPacketToSend& packet) {
// TODO(brandtr): Generalize this SSRC check when we support multistream
// protection.
RTC_DCHECK_EQ(packet.Ssrc(), protected_media_ssrc_);
ulpfec_generator_.AddPacketAndGenerateFec(packet);
}
std::vector<std::unique_ptr<RtpPacketToSend>> FlexfecSender::GetFecPackets() {
RTC_CHECK_RUNS_SERIALIZED(&ulpfec_generator_.race_checker_);
std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets_to_send;
fec_packets_to_send.reserve(ulpfec_generator_.generated_fec_packets_.size());
size_t total_fec_data_bytes = 0;
for (const auto* fec_packet : ulpfec_generator_.generated_fec_packets_) {
std::unique_ptr<RtpPacketToSend> fec_packet_to_send(
new RtpPacketToSend(&rtp_header_extension_map_));
fec_packet_to_send->set_packet_type(
RtpPacketMediaType::kForwardErrorCorrection);
fec_packet_to_send->set_allow_retransmission(false);
// RTP header.
fec_packet_to_send->SetMarker(false);
fec_packet_to_send->SetPayloadType(payload_type_);
fec_packet_to_send->SetSequenceNumber(seq_num_++);
fec_packet_to_send->SetTimestamp(
timestamp_offset_ +
static_cast<uint32_t>(kMsToRtpTimestamp *
clock_->TimeInMilliseconds()));
// Set "capture time" so that the TransmissionOffset header extension
// can be set by the RTPSender.
fec_packet_to_send->set_capture_time(clock_->CurrentTime());
fec_packet_to_send->SetSsrc(ssrc_);
// Reserve extensions, if registered. These will be set by the RTPSender.
fec_packet_to_send->ReserveExtension<AbsoluteSendTime>();
fec_packet_to_send->ReserveExtension<TransmissionOffset>();
fec_packet_to_send->ReserveExtension<TransportSequenceNumber>();
// Possibly include the MID header extension.
if (!mid_.empty()) {
// This is a no-op if the MID header extension is not registered.
fec_packet_to_send->SetExtension<RtpMid>(mid_);
}
// RTP payload.
uint8_t* payload =
fec_packet_to_send->AllocatePayload(fec_packet->data.size());
memcpy(payload, fec_packet->data.cdata(), fec_packet->data.size());
total_fec_data_bytes += fec_packet_to_send->size();
fec_packets_to_send.push_back(std::move(fec_packet_to_send));
}
if (!fec_packets_to_send.empty()) {
ulpfec_generator_.ResetState();
}
Timestamp now = clock_->CurrentTime();
if (!fec_packets_to_send.empty() &&
now - last_generated_packet_ > kPacketLogInterval) {
RTC_LOG(LS_VERBOSE) << "Generated " << fec_packets_to_send.size()
<< " FlexFEC packets with payload type: "
<< payload_type_ << " and SSRC: " << ssrc_ << ".";
last_generated_packet_ = now;
}
MutexLock lock(&mutex_);
fec_bitrate_.Update(total_fec_data_bytes, now);
return fec_packets_to_send;
}
// The overhead is BWE RTP header extensions and FlexFEC header.
size_t FlexfecSender::MaxPacketOverhead() const {
return header_extensions_size_ + kFlexfecMaxHeaderSize;
}
DataRate FlexfecSender::CurrentFecRate() const {
MutexLock lock(&mutex_);
return fec_bitrate_.Rate(clock_->CurrentTime()).value_or(DataRate::Zero());
}
absl::optional<RtpState> FlexfecSender::GetRtpState() {
RtpState rtp_state;
rtp_state.sequence_number = seq_num_;
rtp_state.start_timestamp = timestamp_offset_;
return rtp_state;
}
} // namespace webrtc

View file

@ -0,0 +1,838 @@
/*
* 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 "modules/rtp_rtcp/source/forward_error_correction.h"
#include <string.h>
#include <algorithm>
#include <utility>
#include "absl/algorithm/container.h"
#include "modules/include/module_common_types_public.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/flexfec_03_header_reader_writer.h"
#include "modules/rtp_rtcp/source/forward_error_correction_internal.h"
#include "modules/rtp_rtcp/source/ulpfec_header_reader_writer.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/mod_ops.h"
namespace webrtc {
namespace {
// Transport header size in bytes. Assume UDP/IPv4 as a reasonable minimum.
constexpr size_t kTransportOverhead = 28;
constexpr uint16_t kOldSequenceThreshold = 0x3fff;
} // namespace
ForwardErrorCorrection::Packet::Packet() : data(0), ref_count_(0) {}
ForwardErrorCorrection::Packet::~Packet() = default;
int32_t ForwardErrorCorrection::Packet::AddRef() {
return ++ref_count_;
}
int32_t ForwardErrorCorrection::Packet::Release() {
int32_t ref_count;
ref_count = --ref_count_;
if (ref_count == 0)
delete this;
return ref_count;
}
// This comparator is used to compare std::unique_ptr's pointing to
// subclasses of SortablePackets. It needs to be parametric since
// the std::unique_ptr's are not covariant w.r.t. the types that
// they are pointing to.
template <typename S, typename T>
bool ForwardErrorCorrection::SortablePacket::LessThan::operator()(
const S& first,
const T& second) {
RTC_DCHECK_EQ(first->ssrc, second->ssrc);
return IsNewerSequenceNumber(second->seq_num, first->seq_num);
}
ForwardErrorCorrection::ReceivedPacket::ReceivedPacket() = default;
ForwardErrorCorrection::ReceivedPacket::~ReceivedPacket() = default;
ForwardErrorCorrection::RecoveredPacket::RecoveredPacket() = default;
ForwardErrorCorrection::RecoveredPacket::~RecoveredPacket() = default;
ForwardErrorCorrection::ProtectedPacket::ProtectedPacket() = default;
ForwardErrorCorrection::ProtectedPacket::~ProtectedPacket() = default;
ForwardErrorCorrection::ReceivedFecPacket::ReceivedFecPacket() = default;
ForwardErrorCorrection::ReceivedFecPacket::~ReceivedFecPacket() = default;
ForwardErrorCorrection::ForwardErrorCorrection(
std::unique_ptr<FecHeaderReader> fec_header_reader,
std::unique_ptr<FecHeaderWriter> fec_header_writer,
uint32_t ssrc,
uint32_t protected_media_ssrc)
: ssrc_(ssrc),
protected_media_ssrc_(protected_media_ssrc),
fec_header_reader_(std::move(fec_header_reader)),
fec_header_writer_(std::move(fec_header_writer)),
generated_fec_packets_(fec_header_writer_->MaxFecPackets()),
packet_mask_size_(0) {}
ForwardErrorCorrection::~ForwardErrorCorrection() = default;
std::unique_ptr<ForwardErrorCorrection> ForwardErrorCorrection::CreateUlpfec(
uint32_t ssrc) {
std::unique_ptr<FecHeaderReader> fec_header_reader(new UlpfecHeaderReader());
std::unique_ptr<FecHeaderWriter> fec_header_writer(new UlpfecHeaderWriter());
return std::unique_ptr<ForwardErrorCorrection>(new ForwardErrorCorrection(
std::move(fec_header_reader), std::move(fec_header_writer), ssrc, ssrc));
}
std::unique_ptr<ForwardErrorCorrection> ForwardErrorCorrection::CreateFlexfec(
uint32_t ssrc,
uint32_t protected_media_ssrc) {
std::unique_ptr<FecHeaderReader> fec_header_reader(
new Flexfec03HeaderReader());
std::unique_ptr<FecHeaderWriter> fec_header_writer(
new Flexfec03HeaderWriter());
return std::unique_ptr<ForwardErrorCorrection>(new ForwardErrorCorrection(
std::move(fec_header_reader), std::move(fec_header_writer), ssrc,
protected_media_ssrc));
}
int ForwardErrorCorrection::EncodeFec(const PacketList& media_packets,
uint8_t protection_factor,
int num_important_packets,
bool use_unequal_protection,
FecMaskType fec_mask_type,
std::list<Packet*>* fec_packets) {
const size_t num_media_packets = media_packets.size();
// Sanity check arguments.
RTC_DCHECK_GT(num_media_packets, 0);
RTC_DCHECK_GE(num_important_packets, 0);
RTC_DCHECK_LE(num_important_packets, num_media_packets);
RTC_DCHECK(fec_packets->empty());
const size_t max_media_packets = fec_header_writer_->MaxMediaPackets();
if (num_media_packets > max_media_packets) {
RTC_LOG(LS_WARNING) << "Can't protect " << num_media_packets
<< " media packets per frame. Max is "
<< max_media_packets << ".";
return -1;
}
// Error check the media packets.
for (const auto& media_packet : media_packets) {
RTC_DCHECK(media_packet);
if (media_packet->data.size() < kRtpHeaderSize) {
RTC_LOG(LS_WARNING) << "Media packet " << media_packet->data.size()
<< " bytes "
"is smaller than RTP header.";
return -1;
}
// Ensure the FEC packets will fit in a typical MTU.
if (media_packet->data.size() + MaxPacketOverhead() + kTransportOverhead >
IP_PACKET_SIZE) {
RTC_LOG(LS_WARNING) << "Media packet " << media_packet->data.size()
<< " bytes "
"with overhead is larger than "
<< IP_PACKET_SIZE << " bytes.";
}
}
// Prepare generated FEC packets.
int num_fec_packets = NumFecPackets(num_media_packets, protection_factor);
if (num_fec_packets == 0) {
return 0;
}
for (int i = 0; i < num_fec_packets; ++i) {
generated_fec_packets_[i].data.EnsureCapacity(IP_PACKET_SIZE);
memset(generated_fec_packets_[i].data.MutableData(), 0, IP_PACKET_SIZE);
// Use this as a marker for untouched packets.
generated_fec_packets_[i].data.SetSize(0);
fec_packets->push_back(&generated_fec_packets_[i]);
}
internal::PacketMaskTable mask_table(fec_mask_type, num_media_packets);
packet_mask_size_ = internal::PacketMaskSize(num_media_packets);
memset(packet_masks_, 0, num_fec_packets * packet_mask_size_);
internal::GeneratePacketMasks(num_media_packets, num_fec_packets,
num_important_packets, use_unequal_protection,
&mask_table, packet_masks_);
// Adapt packet masks to missing media packets.
int num_mask_bits = InsertZerosInPacketMasks(media_packets, num_fec_packets);
if (num_mask_bits < 0) {
RTC_LOG(LS_INFO) << "Due to sequence number gaps, cannot protect media "
"packets with a single block of FEC packets.";
fec_packets->clear();
return -1;
}
packet_mask_size_ = internal::PacketMaskSize(num_mask_bits);
// Write FEC packets to `generated_fec_packets_`.
GenerateFecPayloads(media_packets, num_fec_packets);
// TODO(brandtr): Generalize this when multistream protection support is
// added.
const uint32_t media_ssrc = ParseSsrc(media_packets.front()->data.data());
const uint16_t seq_num_base =
ParseSequenceNumber(media_packets.front()->data.data());
FinalizeFecHeaders(num_fec_packets, media_ssrc, seq_num_base);
return 0;
}
int ForwardErrorCorrection::NumFecPackets(int num_media_packets,
int protection_factor) {
// Result in Q0 with an unsigned round.
int num_fec_packets = (num_media_packets * protection_factor + (1 << 7)) >> 8;
// Generate at least one FEC packet if we need protection.
if (protection_factor > 0 && num_fec_packets == 0) {
num_fec_packets = 1;
}
RTC_DCHECK_LE(num_fec_packets, num_media_packets);
return num_fec_packets;
}
void ForwardErrorCorrection::GenerateFecPayloads(
const PacketList& media_packets,
size_t num_fec_packets) {
RTC_DCHECK(!media_packets.empty());
for (size_t i = 0; i < num_fec_packets; ++i) {
Packet* const fec_packet = &generated_fec_packets_[i];
size_t pkt_mask_idx = i * packet_mask_size_;
const size_t min_packet_mask_size = fec_header_writer_->MinPacketMaskSize(
&packet_masks_[pkt_mask_idx], packet_mask_size_);
const size_t fec_header_size =
fec_header_writer_->FecHeaderSize(min_packet_mask_size);
size_t media_pkt_idx = 0;
auto media_packets_it = media_packets.cbegin();
uint16_t prev_seq_num =
ParseSequenceNumber((*media_packets_it)->data.data());
while (media_packets_it != media_packets.end()) {
Packet* const media_packet = media_packets_it->get();
// Should `media_packet` be protected by `fec_packet`?
if (packet_masks_[pkt_mask_idx] & (1 << (7 - media_pkt_idx))) {
size_t media_payload_length =
media_packet->data.size() - kRtpHeaderSize;
size_t fec_packet_length = fec_header_size + media_payload_length;
if (fec_packet_length > fec_packet->data.size()) {
size_t old_size = fec_packet->data.size();
fec_packet->data.SetSize(fec_packet_length);
memset(fec_packet->data.MutableData() + old_size, 0,
fec_packet_length - old_size);
}
XorHeaders(*media_packet, fec_packet);
XorPayloads(*media_packet, media_payload_length, fec_header_size,
fec_packet);
}
media_packets_it++;
if (media_packets_it != media_packets.end()) {
uint16_t seq_num =
ParseSequenceNumber((*media_packets_it)->data.data());
media_pkt_idx += static_cast<uint16_t>(seq_num - prev_seq_num);
prev_seq_num = seq_num;
}
pkt_mask_idx += media_pkt_idx / 8;
media_pkt_idx %= 8;
}
RTC_DCHECK_GT(fec_packet->data.size(), 0)
<< "Packet mask is wrong or poorly designed.";
}
}
int ForwardErrorCorrection::InsertZerosInPacketMasks(
const PacketList& media_packets,
size_t num_fec_packets) {
size_t num_media_packets = media_packets.size();
if (num_media_packets <= 1) {
return num_media_packets;
}
uint16_t last_seq_num =
ParseSequenceNumber(media_packets.back()->data.data());
uint16_t first_seq_num =
ParseSequenceNumber(media_packets.front()->data.data());
size_t total_missing_seq_nums =
static_cast<uint16_t>(last_seq_num - first_seq_num) - num_media_packets +
1;
if (total_missing_seq_nums == 0) {
// All sequence numbers are covered by the packet mask.
// No zero insertion required.
return num_media_packets;
}
const size_t max_media_packets = fec_header_writer_->MaxMediaPackets();
if (total_missing_seq_nums + num_media_packets > max_media_packets) {
return -1;
}
// Allocate the new mask.
size_t tmp_packet_mask_size =
internal::PacketMaskSize(total_missing_seq_nums + num_media_packets);
memset(tmp_packet_masks_, 0, num_fec_packets * tmp_packet_mask_size);
auto media_packets_it = media_packets.cbegin();
uint16_t prev_seq_num = first_seq_num;
++media_packets_it;
// Insert the first column.
internal::CopyColumn(tmp_packet_masks_, tmp_packet_mask_size, packet_masks_,
packet_mask_size_, num_fec_packets, 0, 0);
size_t new_bit_index = 1;
size_t old_bit_index = 1;
// Insert zeros in the bit mask for every hole in the sequence.
while (media_packets_it != media_packets.end()) {
if (new_bit_index == max_media_packets) {
// We can only cover up to 48 packets.
break;
}
uint16_t seq_num = ParseSequenceNumber((*media_packets_it)->data.data());
const int num_zeros_to_insert =
static_cast<uint16_t>(seq_num - prev_seq_num - 1);
if (num_zeros_to_insert > 0) {
internal::InsertZeroColumns(num_zeros_to_insert, tmp_packet_masks_,
tmp_packet_mask_size, num_fec_packets,
new_bit_index);
}
new_bit_index += num_zeros_to_insert;
internal::CopyColumn(tmp_packet_masks_, tmp_packet_mask_size, packet_masks_,
packet_mask_size_, num_fec_packets, new_bit_index,
old_bit_index);
++new_bit_index;
++old_bit_index;
prev_seq_num = seq_num;
++media_packets_it;
}
if (new_bit_index % 8 != 0) {
// We didn't fill the last byte. Shift bits to correct position.
for (uint16_t row = 0; row < num_fec_packets; ++row) {
int new_byte_index = row * tmp_packet_mask_size + new_bit_index / 8;
tmp_packet_masks_[new_byte_index] <<= (7 - (new_bit_index % 8));
}
}
// Replace the old mask with the new.
memcpy(packet_masks_, tmp_packet_masks_,
num_fec_packets * tmp_packet_mask_size);
return new_bit_index;
}
void ForwardErrorCorrection::FinalizeFecHeaders(size_t num_fec_packets,
uint32_t media_ssrc,
uint16_t seq_num_base) {
for (size_t i = 0; i < num_fec_packets; ++i) {
const FecHeaderWriter::ProtectedStream protected_streams[] = {
{.ssrc = media_ssrc,
.seq_num_base = seq_num_base,
.packet_mask = {&packet_masks_[i * packet_mask_size_],
packet_mask_size_}}};
fec_header_writer_->FinalizeFecHeader(protected_streams,
generated_fec_packets_[i]);
}
}
void ForwardErrorCorrection::ResetState(
RecoveredPacketList* recovered_packets) {
// Free the memory for any existing recovered packets, if the caller hasn't.
recovered_packets->clear();
received_fec_packets_.clear();
}
void ForwardErrorCorrection::InsertMediaPacket(
RecoveredPacketList* recovered_packets,
const ReceivedPacket& received_packet) {
RTC_DCHECK_EQ(received_packet.ssrc, protected_media_ssrc_);
// Search for duplicate packets.
for (const auto& recovered_packet : *recovered_packets) {
RTC_DCHECK_EQ(recovered_packet->ssrc, received_packet.ssrc);
if (recovered_packet->seq_num == received_packet.seq_num) {
// Duplicate packet, no need to add to list.
return;
}
}
std::unique_ptr<RecoveredPacket> recovered_packet(new RecoveredPacket());
// This "recovered packet" was not recovered using parity packets.
recovered_packet->was_recovered = false;
// This media packet has already been passed on.
recovered_packet->returned = true;
recovered_packet->ssrc = received_packet.ssrc;
recovered_packet->seq_num = received_packet.seq_num;
recovered_packet->pkt = received_packet.pkt;
// TODO(holmer): Consider replacing this with a binary search for the right
// position, and then just insert the new packet. Would get rid of the sort.
RecoveredPacket* recovered_packet_ptr = recovered_packet.get();
recovered_packets->push_back(std::move(recovered_packet));
recovered_packets->sort(SortablePacket::LessThan());
UpdateCoveringFecPackets(*recovered_packet_ptr);
}
void ForwardErrorCorrection::UpdateCoveringFecPackets(
const RecoveredPacket& packet) {
for (auto& fec_packet : received_fec_packets_) {
// Is this FEC packet protecting the media packet `packet`?
auto protected_it = absl::c_lower_bound(
fec_packet->protected_packets, &packet, SortablePacket::LessThan());
if (protected_it != fec_packet->protected_packets.end() &&
(*protected_it)->seq_num == packet.seq_num) {
// Found an FEC packet which is protecting `packet`.
(*protected_it)->pkt = packet.pkt;
}
}
}
void ForwardErrorCorrection::InsertFecPacket(
const RecoveredPacketList& recovered_packets,
const ReceivedPacket& received_packet) {
RTC_DCHECK_EQ(received_packet.ssrc, ssrc_);
// Check for duplicate.
for (const auto& existing_fec_packet : received_fec_packets_) {
RTC_DCHECK_EQ(existing_fec_packet->ssrc, received_packet.ssrc);
if (existing_fec_packet->seq_num == received_packet.seq_num) {
// Drop duplicate FEC packet data.
return;
}
}
std::unique_ptr<ReceivedFecPacket> fec_packet(new ReceivedFecPacket());
fec_packet->pkt = received_packet.pkt;
fec_packet->ssrc = received_packet.ssrc;
fec_packet->seq_num = received_packet.seq_num;
// Parse ULPFEC/FlexFEC header specific info.
bool ret = fec_header_reader_->ReadFecHeader(fec_packet.get());
if (!ret) {
return;
}
RTC_CHECK_EQ(fec_packet->protected_streams.size(), 1);
if (fec_packet->protected_streams[0].ssrc != protected_media_ssrc_) {
RTC_LOG(LS_INFO)
<< "Received FEC packet is protecting an unknown media SSRC; dropping.";
return;
}
if (fec_packet->protected_streams[0].packet_mask_offset +
fec_packet->protected_streams[0].packet_mask_size >
fec_packet->pkt->data.size()) {
RTC_LOG(LS_INFO) << "Received corrupted FEC packet; dropping.";
return;
}
// Parse packet mask from header and represent as protected packets.
for (uint16_t byte_idx = 0;
byte_idx < fec_packet->protected_streams[0].packet_mask_size;
++byte_idx) {
uint8_t packet_mask =
fec_packet->pkt
->data[fec_packet->protected_streams[0].packet_mask_offset +
byte_idx];
for (uint16_t bit_idx = 0; bit_idx < 8; ++bit_idx) {
if (packet_mask & (1 << (7 - bit_idx))) {
std::unique_ptr<ProtectedPacket> protected_packet(
new ProtectedPacket());
// This wraps naturally with the sequence number.
protected_packet->ssrc = protected_media_ssrc_;
protected_packet->seq_num = static_cast<uint16_t>(
fec_packet->protected_streams[0].seq_num_base + (byte_idx << 3) +
bit_idx);
protected_packet->pkt = nullptr;
fec_packet->protected_packets.push_back(std::move(protected_packet));
}
}
}
if (fec_packet->protected_packets.empty()) {
// All-zero packet mask; we can discard this FEC packet.
RTC_LOG(LS_WARNING) << "Received FEC packet has an all-zero packet mask.";
} else {
AssignRecoveredPackets(recovered_packets, fec_packet.get());
// TODO(holmer): Consider replacing this with a binary search for the right
// position, and then just insert the new packet. Would get rid of the sort.
received_fec_packets_.push_back(std::move(fec_packet));
received_fec_packets_.sort(SortablePacket::LessThan());
const size_t max_fec_packets = fec_header_reader_->MaxFecPackets();
if (received_fec_packets_.size() > max_fec_packets) {
received_fec_packets_.pop_front();
}
RTC_DCHECK_LE(received_fec_packets_.size(), max_fec_packets);
}
}
void ForwardErrorCorrection::AssignRecoveredPackets(
const RecoveredPacketList& recovered_packets,
ReceivedFecPacket* fec_packet) {
ProtectedPacketList* protected_packets = &fec_packet->protected_packets;
std::vector<RecoveredPacket*> recovered_protected_packets;
// Find intersection between the (sorted) containers `protected_packets`
// and `recovered_packets`, i.e. all protected packets that have already
// been recovered. Update the corresponding protected packets to point to
// the recovered packets.
auto it_p = protected_packets->cbegin();
auto it_r = recovered_packets.cbegin();
SortablePacket::LessThan less_than;
while (it_p != protected_packets->end() && it_r != recovered_packets.end()) {
if (less_than(*it_p, *it_r)) {
++it_p;
} else if (less_than(*it_r, *it_p)) {
++it_r;
} else { // *it_p == *it_r.
// This protected packet has already been recovered.
(*it_p)->pkt = (*it_r)->pkt;
++it_p;
++it_r;
}
}
}
void ForwardErrorCorrection::InsertPacket(
const ReceivedPacket& received_packet,
RecoveredPacketList* recovered_packets) {
// Discard old FEC packets such that the sequence numbers in
// `received_fec_packets_` span at most 1/2 of the sequence number space.
// This is important for keeping `received_fec_packets_` sorted, and may
// also reduce the possibility of incorrect decoding due to sequence number
// wrap-around.
if (!received_fec_packets_.empty() &&
received_packet.ssrc == received_fec_packets_.front()->ssrc) {
// It only makes sense to detect wrap-around when `received_packet`
// and `front_received_fec_packet` belong to the same sequence number
// space, i.e., the same SSRC. This happens when `received_packet`
// is a FEC packet, or if `received_packet` is a media packet and
// RED+ULPFEC is used.
auto it = received_fec_packets_.begin();
while (it != received_fec_packets_.end()) {
uint16_t seq_num_diff = MinDiff(received_packet.seq_num, (*it)->seq_num);
if (seq_num_diff > kOldSequenceThreshold) {
it = received_fec_packets_.erase(it);
} else {
// No need to keep iterating, since `received_fec_packets_` is sorted.
break;
}
}
}
if (received_packet.is_fec) {
InsertFecPacket(*recovered_packets, received_packet);
} else {
InsertMediaPacket(recovered_packets, received_packet);
}
DiscardOldRecoveredPackets(recovered_packets);
}
bool ForwardErrorCorrection::StartPacketRecovery(
const ReceivedFecPacket& fec_packet,
RecoveredPacket* recovered_packet) {
// Ensure pkt is initialized.
recovered_packet->pkt = new Packet();
// Sanity check packet length.
if (fec_packet.pkt->data.size() <
fec_packet.fec_header_size + fec_packet.protection_length) {
RTC_LOG(LS_WARNING)
<< "The FEC packet is truncated: it does not contain enough room "
"for its own header.";
return false;
}
if (fec_packet.protection_length >
std::min(size_t{IP_PACKET_SIZE - kRtpHeaderSize},
IP_PACKET_SIZE - fec_packet.fec_header_size)) {
RTC_LOG(LS_WARNING) << "Incorrect protection length, dropping FEC packet.";
return false;
}
// Initialize recovered packet data.
recovered_packet->pkt->data.EnsureCapacity(IP_PACKET_SIZE);
recovered_packet->pkt->data.SetSize(fec_packet.protection_length +
kRtpHeaderSize);
recovered_packet->returned = false;
recovered_packet->was_recovered = true;
// Copy bytes corresponding to minimum RTP header size.
// Note that the sequence number and SSRC fields will be overwritten
// at the end of packet recovery.
memcpy(recovered_packet->pkt->data.MutableData(),
fec_packet.pkt->data.cdata(), kRtpHeaderSize);
// Copy remaining FEC payload.
if (fec_packet.protection_length > 0) {
memcpy(recovered_packet->pkt->data.MutableData() + kRtpHeaderSize,
fec_packet.pkt->data.cdata() + fec_packet.fec_header_size,
fec_packet.protection_length);
}
return true;
}
bool ForwardErrorCorrection::FinishPacketRecovery(
const ReceivedFecPacket& fec_packet,
RecoveredPacket* recovered_packet) {
uint8_t* data = recovered_packet->pkt->data.MutableData();
// Set the RTP version to 2.
data[0] |= 0x80; // Set the 1st bit.
data[0] &= 0xbf; // Clear the 2nd bit.
// Recover the packet length, from temporary location.
const size_t new_size =
ByteReader<uint16_t>::ReadBigEndian(&data[2]) + kRtpHeaderSize;
if (new_size > size_t{IP_PACKET_SIZE - kRtpHeaderSize}) {
RTC_LOG(LS_WARNING) << "The recovered packet had a length larger than a "
"typical IP packet, and is thus dropped.";
return false;
}
size_t old_size = recovered_packet->pkt->data.size();
recovered_packet->pkt->data.SetSize(new_size);
data = recovered_packet->pkt->data.MutableData();
if (new_size > old_size) {
memset(data + old_size, 0, new_size - old_size);
}
// Set the SN field.
ByteWriter<uint16_t>::WriteBigEndian(&data[2], recovered_packet->seq_num);
// Set the SSRC field.
ByteWriter<uint32_t>::WriteBigEndian(&data[8], recovered_packet->ssrc);
return true;
}
void ForwardErrorCorrection::XorHeaders(const Packet& src, Packet* dst) {
uint8_t* dst_data = dst->data.MutableData();
const uint8_t* src_data = src.data.cdata();
// XOR the first 2 bytes of the header: V, P, X, CC, M, PT fields.
dst_data[0] ^= src_data[0];
dst_data[1] ^= src_data[1];
// XOR the length recovery field.
uint8_t src_payload_length_network_order[2];
ByteWriter<uint16_t>::WriteBigEndian(src_payload_length_network_order,
src.data.size() - kRtpHeaderSize);
dst_data[2] ^= src_payload_length_network_order[0];
dst_data[3] ^= src_payload_length_network_order[1];
// XOR the 5th to 8th bytes of the header: the timestamp field.
dst_data[4] ^= src_data[4];
dst_data[5] ^= src_data[5];
dst_data[6] ^= src_data[6];
dst_data[7] ^= src_data[7];
// Skip the 9th to 12th bytes of the header.
}
void ForwardErrorCorrection::XorPayloads(const Packet& src,
size_t payload_length,
size_t dst_offset,
Packet* dst) {
// XOR the payload.
RTC_DCHECK_LE(kRtpHeaderSize + payload_length, src.data.size());
RTC_DCHECK_LE(dst_offset + payload_length, dst->data.capacity());
if (dst_offset + payload_length > dst->data.size()) {
size_t old_size = dst->data.size();
size_t new_size = dst_offset + payload_length;
dst->data.SetSize(new_size);
memset(dst->data.MutableData() + old_size, 0, new_size - old_size);
}
uint8_t* dst_data = dst->data.MutableData();
const uint8_t* src_data = src.data.cdata();
for (size_t i = 0; i < payload_length; ++i) {
dst_data[dst_offset + i] ^= src_data[kRtpHeaderSize + i];
}
}
bool ForwardErrorCorrection::RecoverPacket(const ReceivedFecPacket& fec_packet,
RecoveredPacket* recovered_packet) {
if (!StartPacketRecovery(fec_packet, recovered_packet)) {
return false;
}
for (const auto& protected_packet : fec_packet.protected_packets) {
if (protected_packet->pkt == nullptr) {
// This is the packet we're recovering.
recovered_packet->seq_num = protected_packet->seq_num;
recovered_packet->ssrc = protected_packet->ssrc;
} else {
XorHeaders(*protected_packet->pkt, recovered_packet->pkt.get());
XorPayloads(*protected_packet->pkt,
protected_packet->pkt->data.size() - kRtpHeaderSize,
kRtpHeaderSize, recovered_packet->pkt.get());
}
}
if (!FinishPacketRecovery(fec_packet, recovered_packet)) {
return false;
}
return true;
}
size_t ForwardErrorCorrection::AttemptRecovery(
RecoveredPacketList* recovered_packets) {
size_t num_recovered_packets = 0;
auto fec_packet_it = received_fec_packets_.begin();
while (fec_packet_it != received_fec_packets_.end()) {
// Search for each FEC packet's protected media packets.
int packets_missing = NumCoveredPacketsMissing(**fec_packet_it);
// We can only recover one packet with an FEC packet.
if (packets_missing == 1) {
// Recovery possible.
std::unique_ptr<RecoveredPacket> recovered_packet(new RecoveredPacket());
recovered_packet->pkt = nullptr;
if (!RecoverPacket(**fec_packet_it, recovered_packet.get())) {
// Can't recover using this packet, drop it.
fec_packet_it = received_fec_packets_.erase(fec_packet_it);
continue;
}
++num_recovered_packets;
auto* recovered_packet_ptr = recovered_packet.get();
// Add recovered packet to the list of recovered packets and update any
// FEC packets covering this packet with a pointer to the data.
// TODO(holmer): Consider replacing this with a binary search for the
// right position, and then just insert the new packet. Would get rid of
// the sort.
recovered_packets->push_back(std::move(recovered_packet));
recovered_packets->sort(SortablePacket::LessThan());
UpdateCoveringFecPackets(*recovered_packet_ptr);
DiscardOldRecoveredPackets(recovered_packets);
fec_packet_it = received_fec_packets_.erase(fec_packet_it);
// A packet has been recovered. We need to check the FEC list again, as
// this may allow additional packets to be recovered.
// Restart for first FEC packet.
fec_packet_it = received_fec_packets_.begin();
} else if (packets_missing == 0 ||
IsOldFecPacket(**fec_packet_it, recovered_packets)) {
// Either all protected packets arrived or have been recovered, or the FEC
// packet is old. We can discard this FEC packet.
fec_packet_it = received_fec_packets_.erase(fec_packet_it);
} else {
fec_packet_it++;
}
}
return num_recovered_packets;
}
int ForwardErrorCorrection::NumCoveredPacketsMissing(
const ReceivedFecPacket& fec_packet) {
int packets_missing = 0;
for (const auto& protected_packet : fec_packet.protected_packets) {
if (protected_packet->pkt == nullptr) {
++packets_missing;
if (packets_missing > 1) {
break; // We can't recover more than one packet.
}
}
}
return packets_missing;
}
void ForwardErrorCorrection::DiscardOldRecoveredPackets(
RecoveredPacketList* recovered_packets) {
const size_t max_media_packets = fec_header_reader_->MaxMediaPackets();
while (recovered_packets->size() > max_media_packets) {
recovered_packets->pop_front();
}
RTC_DCHECK_LE(recovered_packets->size(), max_media_packets);
}
bool ForwardErrorCorrection::IsOldFecPacket(
const ReceivedFecPacket& fec_packet,
const RecoveredPacketList* recovered_packets) {
if (recovered_packets->empty()) {
return false;
}
const uint16_t back_recovered_seq_num = recovered_packets->back()->seq_num;
const uint16_t last_protected_seq_num =
fec_packet.protected_packets.back()->seq_num;
// FEC packet is old if its last protected sequence number is much
// older than the latest protected sequence number received.
return (MinDiff(back_recovered_seq_num, last_protected_seq_num) >
kOldSequenceThreshold);
}
uint16_t ForwardErrorCorrection::ParseSequenceNumber(const uint8_t* packet) {
return (packet[2] << 8) + packet[3];
}
uint32_t ForwardErrorCorrection::ParseSsrc(const uint8_t* packet) {
return (packet[8] << 24) + (packet[9] << 16) + (packet[10] << 8) + packet[11];
}
ForwardErrorCorrection::DecodeFecResult ForwardErrorCorrection::DecodeFec(
const ReceivedPacket& received_packet,
RecoveredPacketList* recovered_packets) {
RTC_DCHECK(recovered_packets);
const size_t max_media_packets = fec_header_reader_->MaxMediaPackets();
if (recovered_packets->size() == max_media_packets) {
const RecoveredPacket* back_recovered_packet =
recovered_packets->back().get();
if (received_packet.ssrc == back_recovered_packet->ssrc) {
const unsigned int seq_num_diff =
MinDiff(received_packet.seq_num, back_recovered_packet->seq_num);
if (seq_num_diff > max_media_packets) {
// A big gap in sequence numbers. The old recovered packets
// are now useless, so it's safe to do a reset.
RTC_LOG(LS_INFO) << "Big gap in media/ULPFEC sequence numbers. No need "
"to keep the old packets in the FEC buffers, thus "
"resetting them.";
ResetState(recovered_packets);
}
}
}
InsertPacket(received_packet, recovered_packets);
DecodeFecResult decode_result;
decode_result.num_recovered_packets = AttemptRecovery(recovered_packets);
return decode_result;
}
size_t ForwardErrorCorrection::MaxPacketOverhead() const {
return fec_header_writer_->MaxPacketOverhead();
}
FecHeaderReader::FecHeaderReader(size_t max_media_packets,
size_t max_fec_packets)
: max_media_packets_(max_media_packets),
max_fec_packets_(max_fec_packets) {}
FecHeaderReader::~FecHeaderReader() = default;
size_t FecHeaderReader::MaxMediaPackets() const {
return max_media_packets_;
}
size_t FecHeaderReader::MaxFecPackets() const {
return max_fec_packets_;
}
FecHeaderWriter::FecHeaderWriter(size_t max_media_packets,
size_t max_fec_packets,
size_t max_packet_overhead)
: max_media_packets_(max_media_packets),
max_fec_packets_(max_fec_packets),
max_packet_overhead_(max_packet_overhead) {}
FecHeaderWriter::~FecHeaderWriter() = default;
size_t FecHeaderWriter::MaxMediaPackets() const {
return max_media_packets_;
}
size_t FecHeaderWriter::MaxFecPackets() const {
return max_fec_packets_;
}
size_t FecHeaderWriter::MaxPacketOverhead() const {
return max_packet_overhead_;
}
} // namespace webrtc

View file

@ -0,0 +1,443 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_H_
#define MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_H_
#include <stddef.h>
#include <stdint.h>
#include <list>
#include <memory>
#include <vector>
#include "absl/container/inlined_vector.h"
#include "api/scoped_refptr.h"
#include "api/units/timestamp.h"
#include "modules/include/module_fec_types.h"
#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/forward_error_correction_internal.h"
#include "rtc_base/copy_on_write_buffer.h"
namespace webrtc {
class FecHeaderReader;
class FecHeaderWriter;
// Performs codec-independent forward error correction (FEC), based on RFC 5109.
// Option exists to enable unequal protection (UEP) across packets.
// This is not to be confused with protection within packets
// (referred to as uneven level protection (ULP) in RFC 5109).
// TODO(brandtr): Split this class into a separate encoder
// and a separate decoder.
class ForwardErrorCorrection {
public:
// TODO(holmer): As a next step all these struct-like packet classes should be
// refactored into proper classes, and their members should be made private.
// This will require parts of the functionality in forward_error_correction.cc
// and receiver_fec.cc to be refactored into the packet classes.
class Packet {
public:
Packet();
virtual ~Packet();
// Add a reference.
virtual int32_t AddRef();
// Release a reference. Will delete the object if the reference count
// reaches zero.
virtual int32_t Release();
rtc::CopyOnWriteBuffer data; // Packet data.
private:
int32_t ref_count_; // Counts the number of references to a packet.
};
// TODO(holmer): Refactor into a proper class.
class SortablePacket {
public:
// Functor which returns true if the sequence number of `first`
// is < the sequence number of `second`. Should only ever be called for
// packets belonging to the same SSRC.
struct LessThan {
template <typename S, typename T>
bool operator()(const S& first, const T& second);
};
uint32_t ssrc;
uint16_t seq_num;
};
// Used for the input to DecodeFec().
class ReceivedPacket : public SortablePacket {
public:
ReceivedPacket();
~ReceivedPacket();
bool is_fec; // Set to true if this is an FEC packet and false
// otherwise.
bool is_recovered;
RtpHeaderExtensionMap extensions;
rtc::scoped_refptr<Packet> pkt; // Pointer to the packet storage.
};
// The recovered list parameter of DecodeFec() references structs of
// this type.
// TODO(holmer): Refactor into a proper class.
class RecoveredPacket : public SortablePacket {
public:
RecoveredPacket();
~RecoveredPacket();
bool was_recovered; // Will be true if this packet was recovered by
// the FEC. Otherwise it was a media packet passed in
// through the received packet list.
bool returned; // True when the packet already has been returned to the
// caller through the callback.
rtc::scoped_refptr<Packet> pkt; // Pointer to the packet storage.
};
// Used to link media packets to their protecting FEC packets.
//
// TODO(holmer): Refactor into a proper class.
class ProtectedPacket : public SortablePacket {
public:
ProtectedPacket();
~ProtectedPacket();
rtc::scoped_refptr<ForwardErrorCorrection::Packet> pkt;
};
using ProtectedPacketList = std::list<std::unique_ptr<ProtectedPacket>>;
struct ProtectedStream {
uint32_t ssrc = 0;
uint16_t seq_num_base = 0;
size_t packet_mask_offset = 0; // Relative start of FEC header.
size_t packet_mask_size = 0;
};
// Used for internal storage of received FEC packets in a list.
//
// TODO(holmer): Refactor into a proper class.
class ReceivedFecPacket : public SortablePacket {
public:
// SSRC count is limited by 4 bits of CSRC count in RTP header (max 15).
// Since most of the time number of SSRCs will be low (probably 1 most of
// the time) setting this value to 4 for optimization.
static constexpr size_t kInlinedSsrcsVectorSize = 4;
ReceivedFecPacket();
~ReceivedFecPacket();
// List of media packets that this FEC packet protects.
ProtectedPacketList protected_packets;
// RTP header fields.
uint32_t ssrc;
// FEC header fields.
size_t fec_header_size;
absl::InlinedVector<ProtectedStream, kInlinedSsrcsVectorSize>
protected_streams;
size_t protection_length;
// Raw data.
rtc::scoped_refptr<ForwardErrorCorrection::Packet> pkt;
};
using PacketList = std::list<std::unique_ptr<Packet>>;
using RecoveredPacketList = std::list<std::unique_ptr<RecoveredPacket>>;
using ReceivedFecPacketList = std::list<std::unique_ptr<ReceivedFecPacket>>;
~ForwardErrorCorrection();
// Creates a ForwardErrorCorrection tailored for a specific FEC scheme.
static std::unique_ptr<ForwardErrorCorrection> CreateUlpfec(uint32_t ssrc);
static std::unique_ptr<ForwardErrorCorrection> CreateFlexfec(
uint32_t ssrc,
uint32_t protected_media_ssrc);
// Generates a list of FEC packets from supplied media packets.
//
// Input: media_packets List of media packets to protect, of type
// Packet. All packets must belong to the
// same frame and the list must not be empty.
// Input: protection_factor FEC protection overhead in the [0, 255]
// domain. To obtain 100% overhead, or an
// equal number of FEC packets as
// media packets, use 255.
// Input: num_important_packets The number of "important" packets in the
// frame. These packets may receive greater
// protection than the remaining packets.
// The important packets must be located at the
// start of the media packet list. For codecs
// with data partitioning, the important
// packets may correspond to first partition
// packets.
// Input: use_unequal_protection Parameter to enable/disable unequal
// protection (UEP) across packets. Enabling
// UEP will allocate more protection to the
// num_important_packets from the start of the
// media_packets.
// Input: fec_mask_type The type of packet mask used in the FEC.
// Random or bursty type may be selected. The
// bursty type is only defined up to 12 media
// packets. If the number of media packets is
// above 12, the packet masks from the random
// table will be selected.
// Output: fec_packets List of pointers to generated FEC packets,
// of type Packet. Must be empty on entry.
// The memory available through the list will
// be valid until the next call to
// EncodeFec().
//
// Returns 0 on success, -1 on failure.
//
int EncodeFec(const PacketList& media_packets,
uint8_t protection_factor,
int num_important_packets,
bool use_unequal_protection,
FecMaskType fec_mask_type,
std::list<Packet*>* fec_packets);
// Decodes a list of received media and FEC packets. It will parse the
// `received_packets`, storing FEC packets internally, and move
// media packets to `recovered_packets`. The recovered list will be
// sorted by ascending sequence number and have duplicates removed.
// The function should be called as new packets arrive, and
// `recovered_packets` will be progressively assembled with each call.
// When the function returns, `received_packets` will be empty.
//
// The caller will allocate packets submitted through `received_packets`.
// The function will handle allocation of recovered packets.
//
// Input: received_packets List of new received packets, of type
// ReceivedPacket, belonging to a single
// frame. At output the list will be empty,
// with packets either stored internally,
// or accessible through the recovered list.
// Output: recovered_packets List of recovered media packets, of type
// RecoveredPacket, belonging to a single
// frame. The memory available through the
// list will be valid until the next call to
// DecodeFec().
//
struct DecodeFecResult {
// Number of recovered media packets using FEC.
size_t num_recovered_packets = 0;
};
DecodeFecResult DecodeFec(const ReceivedPacket& received_packet,
RecoveredPacketList* recovered_packets);
// Get the number of generated FEC packets, given the number of media packets
// and the protection factor.
static int NumFecPackets(int num_media_packets, int protection_factor);
// Gets the maximum size of the FEC headers in bytes, which must be
// accounted for as packet overhead.
size_t MaxPacketOverhead() const;
// Reset internal states from last frame and clear `recovered_packets`.
// Frees all memory allocated by this class.
void ResetState(RecoveredPacketList* recovered_packets);
// TODO(brandtr): Remove these functions when the Packet classes
// have been refactored.
static uint16_t ParseSequenceNumber(const uint8_t* packet);
static uint32_t ParseSsrc(const uint8_t* packet);
protected:
ForwardErrorCorrection(std::unique_ptr<FecHeaderReader> fec_header_reader,
std::unique_ptr<FecHeaderWriter> fec_header_writer,
uint32_t ssrc,
uint32_t protected_media_ssrc);
private:
// Analyzes `media_packets` for holes in the sequence and inserts zero columns
// into the `packet_mask` where those holes are found. Zero columns means that
// those packets will have no protection.
// Returns the number of bits used for one row of the new packet mask.
// Requires that `packet_mask` has at least 6 * `num_fec_packets` bytes
// allocated.
int InsertZerosInPacketMasks(const PacketList& media_packets,
size_t num_fec_packets);
// Writes FEC payloads and some recovery fields in the FEC headers.
void GenerateFecPayloads(const PacketList& media_packets,
size_t num_fec_packets);
// Writes the FEC header fields that are not written by GenerateFecPayloads.
// This includes writing the packet masks.
void FinalizeFecHeaders(size_t num_fec_packets,
uint32_t media_ssrc,
uint16_t seq_num_base);
// Inserts the `received_packet` into the internal received FEC packet list
// or into `recovered_packets`.
void InsertPacket(const ReceivedPacket& received_packet,
RecoveredPacketList* recovered_packets);
// Inserts the `received_packet` into `recovered_packets`. Deletes duplicates.
void InsertMediaPacket(RecoveredPacketList* recovered_packets,
const ReceivedPacket& received_packet);
// Assigns pointers to the recovered packet from all FEC packets which cover
// it.
// Note: This reduces the complexity when we want to try to recover a packet
// since we don't have to find the intersection between recovered packets and
// packets covered by the FEC packet.
void UpdateCoveringFecPackets(const RecoveredPacket& packet);
// Insert `received_packet` into internal FEC list. Deletes duplicates.
void InsertFecPacket(const RecoveredPacketList& recovered_packets,
const ReceivedPacket& received_packet);
// Assigns pointers to already recovered packets covered by `fec_packet`.
static void AssignRecoveredPackets(
const RecoveredPacketList& recovered_packets,
ReceivedFecPacket* fec_packet);
// Attempt to recover missing packets, using the internally stored
// received FEC packets.
size_t AttemptRecovery(RecoveredPacketList* recovered_packets);
// Initializes headers and payload before the XOR operation
// that recovers a packet.
static bool StartPacketRecovery(const ReceivedFecPacket& fec_packet,
RecoveredPacket* recovered_packet);
// Performs XOR between the first 8 bytes of `src` and `dst` and stores
// the result in `dst`. The 3rd and 4th bytes are used for storing
// the length recovery field.
static void XorHeaders(const Packet& src, Packet* dst);
// Performs XOR between the payloads of `src` and `dst` and stores the result
// in `dst`. The parameter `dst_offset` determines at what byte the
// XOR operation starts in `dst`. In total, `payload_length` bytes are XORed.
static void XorPayloads(const Packet& src,
size_t payload_length,
size_t dst_offset,
Packet* dst);
// Finalizes recovery of packet by setting RTP header fields.
// This is not specific to the FEC scheme used.
static bool FinishPacketRecovery(const ReceivedFecPacket& fec_packet,
RecoveredPacket* recovered_packet);
// Recover a missing packet.
static bool RecoverPacket(const ReceivedFecPacket& fec_packet,
RecoveredPacket* recovered_packet);
// Get the number of missing media packets which are covered by `fec_packet`.
// An FEC packet can recover at most one packet, and if zero packets are
// missing the FEC packet can be discarded. This function returns 2 when two
// or more packets are missing.
static int NumCoveredPacketsMissing(const ReceivedFecPacket& fec_packet);
// Discards old packets in `recovered_packets`, which are no longer relevant
// for recovering lost packets.
void DiscardOldRecoveredPackets(RecoveredPacketList* recovered_packets);
// Checks if the FEC packet is old enough and no longer relevant for
// recovering lost media packets.
bool IsOldFecPacket(const ReceivedFecPacket& fec_packet,
const RecoveredPacketList* recovered_packets);
// These SSRCs are only used by the decoder.
const uint32_t ssrc_;
const uint32_t protected_media_ssrc_;
std::unique_ptr<FecHeaderReader> fec_header_reader_;
std::unique_ptr<FecHeaderWriter> fec_header_writer_;
std::vector<Packet> generated_fec_packets_;
ReceivedFecPacketList received_fec_packets_;
// Arrays used to avoid dynamically allocating memory when generating
// the packet masks.
// (There are never more than `kUlpfecMaxMediaPackets` FEC packets generated.)
uint8_t packet_masks_[kUlpfecMaxMediaPackets * kUlpfecMaxPacketMaskSize];
uint8_t tmp_packet_masks_[kUlpfecMaxMediaPackets * kUlpfecMaxPacketMaskSize];
size_t packet_mask_size_;
};
// Classes derived from FecHeader{Reader,Writer} encapsulate the
// specifics of reading and writing FEC header for, e.g., ULPFEC
// and FlexFEC.
class FecHeaderReader {
public:
virtual ~FecHeaderReader();
// The maximum number of media packets that can be covered by one FEC packet.
size_t MaxMediaPackets() const;
// The maximum number of FEC packets that is supported, per call
// to ForwardErrorCorrection::EncodeFec().
size_t MaxFecPackets() const;
// Parses FEC header and stores information in ReceivedFecPacket members.
virtual bool ReadFecHeader(
ForwardErrorCorrection::ReceivedFecPacket* fec_packet) const = 0;
protected:
FecHeaderReader(size_t max_media_packets, size_t max_fec_packets);
const size_t max_media_packets_;
const size_t max_fec_packets_;
};
class FecHeaderWriter {
public:
struct ProtectedStream {
uint32_t ssrc = 0;
uint16_t seq_num_base = 0;
rtc::ArrayView<const uint8_t> packet_mask;
};
virtual ~FecHeaderWriter();
// The maximum number of media packets that can be covered by one FEC packet.
size_t MaxMediaPackets() const;
// The maximum number of FEC packets that is supported, per call
// to ForwardErrorCorrection::EncodeFec().
size_t MaxFecPackets() const;
// The maximum overhead (in bytes) per packet, due to FEC headers.
size_t MaxPacketOverhead() const;
// Calculates the minimum packet mask size needed (in bytes),
// given the discrete options of the ULPFEC masks and the bits
// set in the current packet mask.
virtual size_t MinPacketMaskSize(const uint8_t* packet_mask,
size_t packet_mask_size) const = 0;
// The header size (in bytes), given the packet mask size.
virtual size_t FecHeaderSize(size_t packet_mask_size) const = 0;
// Writes FEC header.
virtual void FinalizeFecHeader(
rtc::ArrayView<const ProtectedStream> protected_streams,
ForwardErrorCorrection::Packet& fec_packet) const = 0;
protected:
FecHeaderWriter(size_t max_media_packets,
size_t max_fec_packets,
size_t max_packet_overhead);
const size_t max_media_packets_;
const size_t max_fec_packets_;
const size_t max_packet_overhead_;
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_H_

View file

@ -0,0 +1,519 @@
/*
* 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 "modules/rtp_rtcp/source/forward_error_correction_internal.h"
#include <string.h>
#include <algorithm>
#include "modules/rtp_rtcp/source/fec_private_tables_bursty.h"
#include "modules/rtp_rtcp/source/fec_private_tables_random.h"
#include "rtc_base/checks.h"
namespace {
// Allow for different modes of protection for packets in UEP case.
enum ProtectionMode {
kModeNoOverlap,
kModeOverlap,
kModeBiasFirstPacket,
};
// Fits an input mask (sub_mask) to an output mask.
// The mask is a matrix where the rows are the FEC packets,
// and the columns are the source packets the FEC is applied to.
// Each row of the mask is represented by a number of mask bytes.
//
// \param[in] num_mask_bytes The number of mask bytes of output mask.
// \param[in] num_sub_mask_bytes The number of mask bytes of input mask.
// \param[in] num_rows The number of rows of the input mask.
// \param[in] sub_mask A pointer to hold the input mask, of size
// [0, num_rows * num_sub_mask_bytes]
// \param[out] packet_mask A pointer to hold the output mask, of size
// [0, x * num_mask_bytes], where x >= num_rows.
void FitSubMask(int num_mask_bytes,
int num_sub_mask_bytes,
int num_rows,
const uint8_t* sub_mask,
uint8_t* packet_mask) {
if (num_mask_bytes == num_sub_mask_bytes) {
memcpy(packet_mask, sub_mask, num_rows * num_sub_mask_bytes);
} else {
for (int i = 0; i < num_rows; ++i) {
int pkt_mask_idx = i * num_mask_bytes;
int pkt_mask_idx2 = i * num_sub_mask_bytes;
for (int j = 0; j < num_sub_mask_bytes; ++j) {
packet_mask[pkt_mask_idx] = sub_mask[pkt_mask_idx2];
pkt_mask_idx++;
pkt_mask_idx2++;
}
}
}
}
// Shifts a mask by number of columns (bits), and fits it to an output mask.
// The mask is a matrix where the rows are the FEC packets,
// and the columns are the source packets the FEC is applied to.
// Each row of the mask is represented by a number of mask bytes.
//
// \param[in] num_mask_bytes The number of mask bytes of output mask.
// \param[in] num_sub_mask_bytes The number of mask bytes of input mask.
// \param[in] num_column_shift The number columns to be shifted, and
// the starting row for the output mask.
// \param[in] end_row The ending row for the output mask.
// \param[in] sub_mask A pointer to hold the input mask, of size
// [0, (end_row_fec - start_row_fec) *
// num_sub_mask_bytes]
// \param[out] packet_mask A pointer to hold the output mask, of size
// [0, x * num_mask_bytes],
// where x >= end_row_fec.
// TODO(marpan): This function is doing three things at the same time:
// shift within a byte, byte shift and resizing.
// Split up into subroutines.
void ShiftFitSubMask(int num_mask_bytes,
int res_mask_bytes,
int num_column_shift,
int end_row,
const uint8_t* sub_mask,
uint8_t* packet_mask) {
// Number of bit shifts within a byte
const int num_bit_shifts = (num_column_shift % 8);
const int num_byte_shifts = num_column_shift >> 3;
// Modify new mask with sub-mask21.
// Loop over the remaining FEC packets.
for (int i = num_column_shift; i < end_row; ++i) {
// Byte index of new mask, for row i and column res_mask_bytes,
// offset by the number of bytes shifts
int pkt_mask_idx =
i * num_mask_bytes + res_mask_bytes - 1 + num_byte_shifts;
// Byte index of sub_mask, for row i and column res_mask_bytes
int pkt_mask_idx2 =
(i - num_column_shift) * res_mask_bytes + res_mask_bytes - 1;
uint8_t shift_right_curr_byte = 0;
uint8_t shift_left_prev_byte = 0;
uint8_t comb_new_byte = 0;
// Handle case of num_mask_bytes > res_mask_bytes:
// For a given row, copy the rightmost "numBitShifts" bits
// of the last byte of sub_mask into output mask.
if (num_mask_bytes > res_mask_bytes) {
shift_left_prev_byte = (sub_mask[pkt_mask_idx2] << (8 - num_bit_shifts));
packet_mask[pkt_mask_idx + 1] = shift_left_prev_byte;
}
// For each row i (FEC packet), shift the bit-mask of the sub_mask.
// Each row of the mask contains "resMaskBytes" of bytes.
// We start from the last byte of the sub_mask and move to first one.
for (int j = res_mask_bytes - 1; j > 0; j--) {
// Shift current byte of sub21 to the right by "numBitShifts".
shift_right_curr_byte = sub_mask[pkt_mask_idx2] >> num_bit_shifts;
// Fill in shifted bits with bits from the previous (left) byte:
// First shift the previous byte to the left by "8-numBitShifts".
shift_left_prev_byte =
(sub_mask[pkt_mask_idx2 - 1] << (8 - num_bit_shifts));
// Then combine both shifted bytes into new mask byte.
comb_new_byte = shift_right_curr_byte | shift_left_prev_byte;
// Assign to new mask.
packet_mask[pkt_mask_idx] = comb_new_byte;
pkt_mask_idx--;
pkt_mask_idx2--;
}
// For the first byte in the row (j=0 case).
shift_right_curr_byte = sub_mask[pkt_mask_idx2] >> num_bit_shifts;
packet_mask[pkt_mask_idx] = shift_right_curr_byte;
}
}
} // namespace
namespace webrtc {
namespace internal {
PacketMaskTable::PacketMaskTable(FecMaskType fec_mask_type,
int num_media_packets)
: table_(PickTable(fec_mask_type, num_media_packets)) {}
PacketMaskTable::~PacketMaskTable() = default;
rtc::ArrayView<const uint8_t> PacketMaskTable::LookUp(int num_media_packets,
int num_fec_packets) {
RTC_DCHECK_GT(num_media_packets, 0);
RTC_DCHECK_GT(num_fec_packets, 0);
RTC_DCHECK_LE(num_media_packets, kUlpfecMaxMediaPackets);
RTC_DCHECK_LE(num_fec_packets, num_media_packets);
if (num_media_packets <= 12) {
return LookUpInFecTable(table_, num_media_packets - 1, num_fec_packets - 1);
}
int mask_length =
static_cast<int>(PacketMaskSize(static_cast<size_t>(num_media_packets)));
// Generate FEC code mask for {num_media_packets(M), num_fec_packets(N)} (use
// N FEC packets to protect M media packets) In the mask, each FEC packet
// occupies one row, each bit / column represent one media packet. E.g. Row
// A, Col/Bit B is set to 1, means FEC packet A will have protection for media
// packet B.
// Loop through each fec packet.
for (int row = 0; row < num_fec_packets; row++) {
// Loop through each fec code in a row, one code has 8 bits.
// Bit X will be set to 1 if media packet X shall be protected by current
// FEC packet. In this implementation, the protection is interleaved, thus
// media packet X will be protected by FEC packet (X % N)
for (int col = 0; col < mask_length; col++) {
fec_packet_mask_[row * mask_length + col] =
((col * 8) % num_fec_packets == row && (col * 8) < num_media_packets
? 0x80
: 0x00) |
((col * 8 + 1) % num_fec_packets == row &&
(col * 8 + 1) < num_media_packets
? 0x40
: 0x00) |
((col * 8 + 2) % num_fec_packets == row &&
(col * 8 + 2) < num_media_packets
? 0x20
: 0x00) |
((col * 8 + 3) % num_fec_packets == row &&
(col * 8 + 3) < num_media_packets
? 0x10
: 0x00) |
((col * 8 + 4) % num_fec_packets == row &&
(col * 8 + 4) < num_media_packets
? 0x08
: 0x00) |
((col * 8 + 5) % num_fec_packets == row &&
(col * 8 + 5) < num_media_packets
? 0x04
: 0x00) |
((col * 8 + 6) % num_fec_packets == row &&
(col * 8 + 6) < num_media_packets
? 0x02
: 0x00) |
((col * 8 + 7) % num_fec_packets == row &&
(col * 8 + 7) < num_media_packets
? 0x01
: 0x00);
}
}
return {&fec_packet_mask_[0],
static_cast<size_t>(num_fec_packets * mask_length)};
}
// If `num_media_packets` is larger than the maximum allowed by `fec_mask_type`
// for the bursty type, or the random table is explicitly asked for, then the
// random type is selected. Otherwise the bursty table callback is returned.
const uint8_t* PacketMaskTable::PickTable(FecMaskType fec_mask_type,
int num_media_packets) {
RTC_DCHECK_GE(num_media_packets, 0);
RTC_DCHECK_LE(static_cast<size_t>(num_media_packets), kUlpfecMaxMediaPackets);
if (fec_mask_type != kFecMaskRandom &&
num_media_packets <=
static_cast<int>(fec_private_tables::kPacketMaskBurstyTbl[0])) {
return &fec_private_tables::kPacketMaskBurstyTbl[0];
}
return &fec_private_tables::kPacketMaskRandomTbl[0];
}
// Remaining protection after important (first partition) packet protection
void RemainingPacketProtection(int num_media_packets,
int num_fec_remaining,
int num_fec_for_imp_packets,
int num_mask_bytes,
ProtectionMode mode,
uint8_t* packet_mask,
PacketMaskTable* mask_table) {
if (mode == kModeNoOverlap) {
// sub_mask21
const int res_mask_bytes =
PacketMaskSize(num_media_packets - num_fec_for_imp_packets);
auto end_row = (num_fec_for_imp_packets + num_fec_remaining);
rtc::ArrayView<const uint8_t> packet_mask_sub_21 = mask_table->LookUp(
num_media_packets - num_fec_for_imp_packets, num_fec_remaining);
ShiftFitSubMask(num_mask_bytes, res_mask_bytes, num_fec_for_imp_packets,
end_row, &packet_mask_sub_21[0], packet_mask);
} else if (mode == kModeOverlap || mode == kModeBiasFirstPacket) {
// sub_mask22
rtc::ArrayView<const uint8_t> packet_mask_sub_22 =
mask_table->LookUp(num_media_packets, num_fec_remaining);
FitSubMask(num_mask_bytes, num_mask_bytes, num_fec_remaining,
&packet_mask_sub_22[0],
&packet_mask[num_fec_for_imp_packets * num_mask_bytes]);
if (mode == kModeBiasFirstPacket) {
for (int i = 0; i < num_fec_remaining; ++i) {
int pkt_mask_idx = i * num_mask_bytes;
packet_mask[pkt_mask_idx] = packet_mask[pkt_mask_idx] | (1 << 7);
}
}
} else {
RTC_DCHECK_NOTREACHED();
}
}
// Protection for important (first partition) packets
void ImportantPacketProtection(int num_fec_for_imp_packets,
int num_imp_packets,
int num_mask_bytes,
uint8_t* packet_mask,
PacketMaskTable* mask_table) {
const int num_imp_mask_bytes = PacketMaskSize(num_imp_packets);
// Get sub_mask1 from table
rtc::ArrayView<const uint8_t> packet_mask_sub_1 =
mask_table->LookUp(num_imp_packets, num_fec_for_imp_packets);
FitSubMask(num_mask_bytes, num_imp_mask_bytes, num_fec_for_imp_packets,
&packet_mask_sub_1[0], packet_mask);
}
// This function sets the protection allocation: i.e., how many FEC packets
// to use for num_imp (1st partition) packets, given the: number of media
// packets, number of FEC packets, and number of 1st partition packets.
int SetProtectionAllocation(int num_media_packets,
int num_fec_packets,
int num_imp_packets) {
// TODO(marpan): test different cases for protection allocation:
// Use at most (alloc_par * num_fec_packets) for important packets.
float alloc_par = 0.5;
int max_num_fec_for_imp = alloc_par * num_fec_packets;
int num_fec_for_imp_packets = (num_imp_packets < max_num_fec_for_imp)
? num_imp_packets
: max_num_fec_for_imp;
// Fall back to equal protection in this case
if (num_fec_packets == 1 && (num_media_packets > 2 * num_imp_packets)) {
num_fec_for_imp_packets = 0;
}
return num_fec_for_imp_packets;
}
// Modification for UEP: reuse the off-line tables for the packet masks.
// Note: these masks were designed for equal packet protection case,
// assuming random packet loss.
// Current version has 3 modes (options) to build UEP mask from existing ones.
// Various other combinations may be added in future versions.
// Longer-term, we may add another set of tables specifically for UEP cases.
// TODO(marpan): also consider modification of masks for bursty loss cases.
// Mask is characterized as (#packets_to_protect, #fec_for_protection).
// Protection factor defined as: (#fec_for_protection / #packets_to_protect).
// Let k=num_media_packets, n=total#packets, (n-k)=num_fec_packets,
// m=num_imp_packets.
// For ProtectionMode 0 and 1:
// one mask (sub_mask1) is used for 1st partition packets,
// the other mask (sub_mask21/22, for 0/1) is for the remaining FEC packets.
// In both mode 0 and 1, the packets of 1st partition (num_imp_packets) are
// treated equally important, and are afforded more protection than the
// residual partition packets.
// For num_imp_packets:
// sub_mask1 = (m, t): protection = t/(m), where t=F(k,n-k,m).
// t=F(k,n-k,m) is the number of packets used to protect first partition in
// sub_mask1. This is determined from the function SetProtectionAllocation().
// For the left-over protection:
// Mode 0: sub_mask21 = (k-m,n-k-t): protection = (n-k-t)/(k-m)
// mode 0 has no protection overlap between the two partitions.
// For mode 0, we would typically set t = min(m, n-k).
// Mode 1: sub_mask22 = (k, n-k-t), with protection (n-k-t)/(k)
// mode 1 has protection overlap between the two partitions (preferred).
// For ProtectionMode 2:
// This gives 1st packet of list (which is 1st packet of 1st partition) more
// protection. In mode 2, the equal protection mask (which is obtained from
// mode 1 for t=0) is modified (more "1s" added in 1st column of packet mask)
// to bias higher protection for the 1st source packet.
// Protection Mode 2 may be extended for a sort of sliding protection
// (i.e., vary the number/density of "1s" across columns) across packets.
void UnequalProtectionMask(int num_media_packets,
int num_fec_packets,
int num_imp_packets,
int num_mask_bytes,
uint8_t* packet_mask,
PacketMaskTable* mask_table) {
// Set Protection type and allocation
// TODO(marpan): test/update for best mode and some combinations thereof.
ProtectionMode mode = kModeOverlap;
int num_fec_for_imp_packets = 0;
if (mode != kModeBiasFirstPacket) {
num_fec_for_imp_packets = SetProtectionAllocation(
num_media_packets, num_fec_packets, num_imp_packets);
}
int num_fec_remaining = num_fec_packets - num_fec_for_imp_packets;
// Done with setting protection type and allocation
//
// Generate sub_mask1
//
if (num_fec_for_imp_packets > 0) {
ImportantPacketProtection(num_fec_for_imp_packets, num_imp_packets,
num_mask_bytes, packet_mask, mask_table);
}
//
// Generate sub_mask2
//
if (num_fec_remaining > 0) {
RemainingPacketProtection(num_media_packets, num_fec_remaining,
num_fec_for_imp_packets, num_mask_bytes, mode,
packet_mask, mask_table);
}
}
// This algorithm is tailored to look up data in the `kPacketMaskRandomTbl` and
// `kPacketMaskBurstyTbl` tables. These tables only cover fec code for up to 12
// media packets. Starting from 13 media packets, the fec code will be generated
// at runtime. The format of those arrays is that they're essentially a 3
// dimensional array with the following dimensions: * media packet
// * Size for kPacketMaskRandomTbl: 12
// * Size for kPacketMaskBurstyTbl: 12
// * fec index
// * Size for both random and bursty table increases from 1 to number of rows.
// (i.e. 1-48, or 1-12 respectively).
// * Fec data (what actually gets returned)
// * Size for kPacketMaskRandomTbl: 2 bytes.
// * For all entries: 2 * fec index (1 based)
// * Size for kPacketMaskBurstyTbl: 2 bytes.
// * For all entries: 2 * fec index (1 based)
rtc::ArrayView<const uint8_t> LookUpInFecTable(const uint8_t* table,
int media_packet_index,
int fec_index) {
RTC_DCHECK_LT(media_packet_index, table[0]);
// Skip over the table size.
const uint8_t* entry = &table[1];
uint8_t entry_size_increment = 2; // 0-16 are 2 byte wide, then changes to 6.
// Hop over un-interesting array entries.
for (int i = 0; i < media_packet_index; ++i) {
if (i == 16)
entry_size_increment = 6;
uint8_t count = entry[0];
++entry; // skip over the count.
for (int j = 0; j < count; ++j) {
entry += entry_size_increment * (j + 1); // skip over the data.
}
}
if (media_packet_index == 16)
entry_size_increment = 6;
RTC_DCHECK_LT(fec_index, entry[0]);
++entry; // Skip over the size.
// Find the appropriate data in the second dimension.
// Find the specific data we're looking for.
for (int i = 0; i < fec_index; ++i)
entry += entry_size_increment * (i + 1); // skip over the data.
size_t size = entry_size_increment * (fec_index + 1);
return {&entry[0], size};
}
void GeneratePacketMasks(int num_media_packets,
int num_fec_packets,
int num_imp_packets,
bool use_unequal_protection,
PacketMaskTable* mask_table,
uint8_t* packet_mask) {
RTC_DCHECK_GT(num_media_packets, 0);
RTC_DCHECK_GT(num_fec_packets, 0);
RTC_DCHECK_LE(num_fec_packets, num_media_packets);
RTC_DCHECK_LE(num_imp_packets, num_media_packets);
RTC_DCHECK_GE(num_imp_packets, 0);
const int num_mask_bytes = PacketMaskSize(num_media_packets);
// Equal-protection for these cases.
if (!use_unequal_protection || num_imp_packets == 0) {
// Retrieve corresponding mask table directly:for equal-protection case.
// Mask = (k,n-k), with protection factor = (n-k)/k,
// where k = num_media_packets, n=total#packets, (n-k)=num_fec_packets.
rtc::ArrayView<const uint8_t> mask =
mask_table->LookUp(num_media_packets, num_fec_packets);
memcpy(packet_mask, &mask[0], mask.size());
} else { // UEP case
UnequalProtectionMask(num_media_packets, num_fec_packets, num_imp_packets,
num_mask_bytes, packet_mask, mask_table);
} // End of UEP modification
} // End of GetPacketMasks
size_t PacketMaskSize(size_t num_sequence_numbers) {
RTC_DCHECK_LE(num_sequence_numbers, 8 * kUlpfecPacketMaskSizeLBitSet);
if (num_sequence_numbers > 8 * kUlpfecPacketMaskSizeLBitClear) {
return kUlpfecPacketMaskSizeLBitSet;
}
return kUlpfecPacketMaskSizeLBitClear;
}
void InsertZeroColumns(int num_zeros,
uint8_t* new_mask,
int new_mask_bytes,
int num_fec_packets,
int new_bit_index) {
for (uint16_t row = 0; row < num_fec_packets; ++row) {
const int new_byte_index = row * new_mask_bytes + new_bit_index / 8;
const int max_shifts = (7 - (new_bit_index % 8));
new_mask[new_byte_index] <<= std::min(num_zeros, max_shifts);
}
}
void CopyColumn(uint8_t* new_mask,
int new_mask_bytes,
uint8_t* old_mask,
int old_mask_bytes,
int num_fec_packets,
int new_bit_index,
int old_bit_index) {
RTC_CHECK_LT(new_bit_index, 8 * new_mask_bytes);
// Copy column from the old mask to the beginning of the new mask and shift it
// out from the old mask.
for (uint16_t row = 0; row < num_fec_packets; ++row) {
int new_byte_index = row * new_mask_bytes + new_bit_index / 8;
int old_byte_index = row * old_mask_bytes + old_bit_index / 8;
new_mask[new_byte_index] |= ((old_mask[old_byte_index] & 0x80) >> 7);
if (new_bit_index % 8 != 7) {
new_mask[new_byte_index] <<= 1;
}
old_mask[old_byte_index] <<= 1;
}
}
} // namespace internal
} // namespace webrtc

View file

@ -0,0 +1,121 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_INTERNAL_H_
#define MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_INTERNAL_H_
#include <stddef.h>
#include <stdint.h>
#include "api/array_view.h"
#include "modules/include/module_fec_types.h"
namespace webrtc {
// Maximum number of media packets that can be protected
// by these packet masks.
constexpr size_t kUlpfecMaxMediaPackets = 48;
// Packet mask size in bytes (given L bit).
constexpr size_t kUlpfecPacketMaskSizeLBitClear = 2;
constexpr size_t kUlpfecPacketMaskSizeLBitSet = 6;
// Packet code mask maximum length. kFECPacketMaskMaxSize = MaxNumFECPackets *
// (kUlpfecMaxMediaPackets / 8), and MaxNumFECPackets is equal to maximum number
// of media packets (kUlpfecMaxMediaPackets)
constexpr size_t kFECPacketMaskMaxSize = 288;
// Convenience constants.
constexpr size_t kUlpfecMinPacketMaskSize = kUlpfecPacketMaskSizeLBitClear;
constexpr size_t kUlpfecMaxPacketMaskSize = kUlpfecPacketMaskSizeLBitSet;
namespace internal {
class PacketMaskTable {
public:
PacketMaskTable(FecMaskType fec_mask_type, int num_media_packets);
~PacketMaskTable();
rtc::ArrayView<const uint8_t> LookUp(int num_media_packets,
int num_fec_packets);
private:
static const uint8_t* PickTable(FecMaskType fec_mask_type,
int num_media_packets);
const uint8_t* table_;
uint8_t fec_packet_mask_[kFECPacketMaskMaxSize];
};
rtc::ArrayView<const uint8_t> LookUpInFecTable(const uint8_t* table,
int media_packet_index,
int fec_index);
// Returns an array of packet masks. The mask of a single FEC packet
// corresponds to a number of mask bytes. The mask indicates which
// media packets should be protected by the FEC packet.
// \param[in] num_media_packets The number of media packets to protect.
// [1, max_media_packets].
// \param[in] num_fec_packets The number of FEC packets which will
// be generated. [1, num_media_packets].
// \param[in] num_imp_packets The number of important packets.
// [0, num_media_packets].
// num_imp_packets = 0 is the equal
// protection scenario.
// \param[in] use_unequal_protection Enables unequal protection: allocates
// more protection to the num_imp_packets.
// \param[in] mask_table An instance of the `PacketMaskTable`
// class, which contains the type of FEC
// packet mask used, and a pointer to the
// corresponding packet masks.
// \param[out] packet_mask A pointer to hold the packet mask array,
// of size: num_fec_packets *
// "number of mask bytes".
void GeneratePacketMasks(int num_media_packets,
int num_fec_packets,
int num_imp_packets,
bool use_unequal_protection,
PacketMaskTable* mask_table,
uint8_t* packet_mask);
// Returns the required packet mask size, given the number of sequence numbers
// that will be covered.
size_t PacketMaskSize(size_t num_sequence_numbers);
// Inserts `num_zeros` zero columns into `new_mask` at position
// `new_bit_index`. If the current byte of `new_mask` can't fit all zeros, the
// byte will be filled with zeros from `new_bit_index`, but the next byte will
// be untouched.
void InsertZeroColumns(int num_zeros,
uint8_t* new_mask,
int new_mask_bytes,
int num_fec_packets,
int new_bit_index);
// Copies the left most bit column from the byte pointed to by
// `old_bit_index` in `old_mask` to the right most column of the byte pointed
// to by `new_bit_index` in `new_mask`. `old_mask_bytes` and `new_mask_bytes`
// represent the number of bytes used per row for each mask. `num_fec_packets`
// represent the number of rows of the masks.
// The copied bit is shifted out from `old_mask` and is shifted one step to
// the left in `new_mask`. `new_mask` will contain "xxxx xxn0" after this
// operation, where x are previously inserted bits and n is the new bit.
void CopyColumn(uint8_t* new_mask,
int new_mask_bytes,
uint8_t* old_mask,
int old_mask_bytes,
int num_fec_packets,
int new_bit_index,
int old_bit_index);
} // namespace internal
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_INTERNAL_H_

View file

@ -0,0 +1,137 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/source/frame_object.h"
#include <string.h>
#include <utility>
#include "api/video/encoded_image.h"
#include "api/video/video_timing.h"
#include "rtc_base/checks.h"
namespace webrtc {
RtpFrameObject::RtpFrameObject(
uint16_t first_seq_num,
uint16_t last_seq_num,
bool markerBit,
int times_nacked,
int64_t first_packet_received_time,
int64_t last_packet_received_time,
uint32_t rtp_timestamp,
int64_t ntp_time_ms,
const VideoSendTiming& timing,
uint8_t payload_type,
VideoCodecType codec,
VideoRotation rotation,
VideoContentType content_type,
const RTPVideoHeader& video_header,
const absl::optional<webrtc::ColorSpace>& color_space,
RtpPacketInfos packet_infos,
rtc::scoped_refptr<EncodedImageBuffer> image_buffer)
: image_buffer_(image_buffer),
first_seq_num_(first_seq_num),
last_seq_num_(last_seq_num),
last_packet_received_time_(last_packet_received_time),
times_nacked_(times_nacked) {
rtp_video_header_ = video_header;
// EncodedFrame members
codec_type_ = codec;
// TODO(philipel): Remove when encoded image is replaced by EncodedFrame.
// VCMEncodedFrame members
CopyCodecSpecific(&rtp_video_header_);
_payloadType = payload_type;
SetRtpTimestamp(rtp_timestamp);
ntp_time_ms_ = ntp_time_ms;
_frameType = rtp_video_header_.frame_type;
// Setting frame's playout delays to the same values
// as of the first packet's.
SetPlayoutDelay(rtp_video_header_.playout_delay);
SetEncodedData(image_buffer_);
_encodedWidth = rtp_video_header_.width;
_encodedHeight = rtp_video_header_.height;
if (packet_infos.begin() != packet_infos.end()) {
csrcs_ = packet_infos.begin()->csrcs();
}
// EncodedFrame members
SetPacketInfos(std::move(packet_infos));
rotation_ = rotation;
SetColorSpace(color_space);
SetVideoFrameTrackingId(rtp_video_header_.video_frame_tracking_id);
content_type_ = content_type;
if (timing.flags != VideoSendTiming::kInvalid) {
// ntp_time_ms_ may be -1 if not estimated yet. This is not a problem,
// as this will be dealt with at the time of reporting.
timing_.encode_start_ms = ntp_time_ms_ + timing.encode_start_delta_ms;
timing_.encode_finish_ms = ntp_time_ms_ + timing.encode_finish_delta_ms;
timing_.packetization_finish_ms =
ntp_time_ms_ + timing.packetization_finish_delta_ms;
timing_.pacer_exit_ms = ntp_time_ms_ + timing.pacer_exit_delta_ms;
timing_.network_timestamp_ms =
ntp_time_ms_ + timing.network_timestamp_delta_ms;
timing_.network2_timestamp_ms =
ntp_time_ms_ + timing.network2_timestamp_delta_ms;
}
timing_.receive_start_ms = first_packet_received_time;
timing_.receive_finish_ms = last_packet_received_time;
timing_.flags = timing.flags;
is_last_spatial_layer = markerBit;
}
RtpFrameObject::~RtpFrameObject() {}
uint16_t RtpFrameObject::first_seq_num() const {
return first_seq_num_;
}
uint16_t RtpFrameObject::last_seq_num() const {
return last_seq_num_;
}
int RtpFrameObject::times_nacked() const {
return times_nacked_;
}
VideoFrameType RtpFrameObject::frame_type() const {
return rtp_video_header_.frame_type;
}
VideoCodecType RtpFrameObject::codec_type() const {
return codec_type_;
}
int64_t RtpFrameObject::ReceivedTime() const {
return last_packet_received_time_;
}
int64_t RtpFrameObject::RenderTime() const {
return _renderTimeMs;
}
bool RtpFrameObject::delayed_by_retransmission() const {
return times_nacked() > 0;
}
const RTPVideoHeader& RtpFrameObject::GetRtpVideoHeader() const {
return rtp_video_header_;
}
void RtpFrameObject::SetHeaderFromMetadata(const VideoFrameMetadata& metadata) {
rtp_video_header_.SetFromMetadata(metadata);
}
} // namespace webrtc

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_FRAME_OBJECT_H_
#define MODULES_RTP_RTCP_SOURCE_FRAME_OBJECT_H_
#include <vector>
#include "absl/types/optional.h"
#include "api/video/encoded_frame.h"
#include "api/video/video_frame_metadata.h"
namespace webrtc {
class RtpFrameObject : public EncodedFrame {
public:
RtpFrameObject(uint16_t first_seq_num,
uint16_t last_seq_num,
bool markerBit,
int times_nacked,
int64_t first_packet_received_time,
int64_t last_packet_received_time,
uint32_t rtp_timestamp,
int64_t ntp_time_ms,
const VideoSendTiming& timing,
uint8_t payload_type,
VideoCodecType codec,
VideoRotation rotation,
VideoContentType content_type,
const RTPVideoHeader& video_header,
const absl::optional<webrtc::ColorSpace>& color_space,
RtpPacketInfos packet_infos,
rtc::scoped_refptr<EncodedImageBuffer> image_buffer);
~RtpFrameObject() override;
uint16_t first_seq_num() const;
uint16_t last_seq_num() const;
int times_nacked() const;
VideoFrameType frame_type() const;
VideoCodecType codec_type() const;
int64_t ReceivedTime() const override;
int64_t RenderTime() const override;
bool delayed_by_retransmission() const override;
const RTPVideoHeader& GetRtpVideoHeader() const;
uint8_t* mutable_data() { return image_buffer_->data(); }
const std::vector<uint32_t>& Csrcs() const { return csrcs_; }
void SetFirstSeqNum(uint16_t first_seq_num) {
first_seq_num_ = first_seq_num;
}
void SetLastSeqNum(uint16_t last_seq_num) { last_seq_num_ = last_seq_num; }
void SetHeaderFromMetadata(const VideoFrameMetadata& metadata);
private:
// Reference for mutable access.
rtc::scoped_refptr<EncodedImageBuffer> image_buffer_;
RTPVideoHeader rtp_video_header_;
VideoCodecType codec_type_;
uint16_t first_seq_num_;
uint16_t last_seq_num_;
int64_t last_packet_received_time_;
std::vector<uint32_t> csrcs_;
// Equal to times nacked of the packet with the highet times nacked
// belonging to this frame.
int times_nacked_;
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_FRAME_OBJECT_H_

View file

@ -0,0 +1,63 @@
/*
* 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 "modules/rtp_rtcp/source/leb128.h"
#include <cstdint>
namespace webrtc {
int Leb128Size(uint64_t value) {
int size = 0;
while (value >= 0x80) {
++size;
value >>= 7;
}
return size + 1;
}
uint64_t ReadLeb128(const uint8_t*& read_at, const uint8_t* end) {
uint64_t value = 0;
int fill_bits = 0;
while (read_at != end && fill_bits < 64 - 7) {
uint8_t leb128_byte = *read_at;
value |= uint64_t{leb128_byte & 0x7Fu} << fill_bits;
++read_at;
fill_bits += 7;
if ((leb128_byte & 0x80) == 0) {
return value;
}
}
// Read 9 bytes and didn't find the terminator byte. Check if 10th byte
// is that terminator, however to fit result into uint64_t it may carry only
// single bit.
if (read_at != end && *read_at <= 1) {
value |= uint64_t{*read_at} << fill_bits;
++read_at;
return value;
}
// Failed to find terminator leb128 byte.
read_at = nullptr;
return 0;
}
int WriteLeb128(uint64_t value, uint8_t* buffer) {
int size = 0;
while (value >= 0x80) {
buffer[size] = 0x80 | (value & 0x7F);
++size;
value >>= 7;
}
buffer[size] = value;
++size;
return size;
}
} // namespace webrtc

View file

@ -0,0 +1,31 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_LEB128_H_
#define MODULES_RTP_RTCP_SOURCE_LEB128_H_
#include <cstdint>
namespace webrtc {
// Returns number of bytes needed to store `value` in leb128 format.
int Leb128Size(uint64_t value);
// Reads leb128 encoded value and advance read_at by number of bytes consumed.
// Sets read_at to nullptr on error.
uint64_t ReadLeb128(const uint8_t*& read_at, const uint8_t* end);
// Encodes `value` in leb128 format. Assumes buffer has size of at least
// Leb128Size(value). Returns number of bytes consumed.
int WriteLeb128(uint64_t value, uint8_t* buffer);
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_LEB128_H_

View file

@ -0,0 +1,141 @@
/*
* 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.
*/
#include "modules/rtp_rtcp/source/packet_loss_stats.h"
#include <cstdint>
#include <iterator>
#include <vector>
#include "rtc_base/checks.h"
// After this many packets are added, adding additional packets will cause the
// oldest packets to be pruned from the buffer.
static const int kBufferSize = 100;
namespace webrtc {
PacketLossStats::PacketLossStats()
: single_loss_historic_count_(0),
multiple_loss_historic_event_count_(0),
multiple_loss_historic_packet_count_(0) {}
PacketLossStats::~PacketLossStats() = default;
void PacketLossStats::AddLostPacket(uint16_t sequence_number) {
// Detect sequence number wrap around.
if (!lost_packets_buffer_.empty() &&
static_cast<int>(*(lost_packets_buffer_.rbegin())) - sequence_number >
0x8000) {
// The buffer contains large numbers and this is a small number.
lost_packets_wrapped_buffer_.insert(sequence_number);
} else {
lost_packets_buffer_.insert(sequence_number);
}
if (lost_packets_wrapped_buffer_.size() + lost_packets_buffer_.size() >
kBufferSize ||
(!lost_packets_wrapped_buffer_.empty() &&
*(lost_packets_wrapped_buffer_.rbegin()) > 0x4000)) {
PruneBuffer();
}
}
int PacketLossStats::GetSingleLossCount() const {
int single_loss_count, unused1, unused2;
ComputeLossCounts(&single_loss_count, &unused1, &unused2);
return single_loss_count;
}
int PacketLossStats::GetMultipleLossEventCount() const {
int event_count, unused1, unused2;
ComputeLossCounts(&unused1, &event_count, &unused2);
return event_count;
}
int PacketLossStats::GetMultipleLossPacketCount() const {
int packet_count, unused1, unused2;
ComputeLossCounts(&unused1, &unused2, &packet_count);
return packet_count;
}
void PacketLossStats::ComputeLossCounts(
int* out_single_loss_count,
int* out_multiple_loss_event_count,
int* out_multiple_loss_packet_count) const {
*out_single_loss_count = single_loss_historic_count_;
*out_multiple_loss_event_count = multiple_loss_historic_event_count_;
*out_multiple_loss_packet_count = multiple_loss_historic_packet_count_;
if (lost_packets_buffer_.empty()) {
RTC_DCHECK(lost_packets_wrapped_buffer_.empty());
return;
}
uint16_t last_num = 0;
int sequential_count = 0;
std::vector<const std::set<uint16_t>*> buffers;
buffers.push_back(&lost_packets_buffer_);
buffers.push_back(&lost_packets_wrapped_buffer_);
for (const auto* buffer : buffers) {
for (auto it = buffer->begin(); it != buffer->end(); ++it) {
uint16_t current_num = *it;
if (sequential_count > 0 && current_num != ((last_num + 1) & 0xFFFF)) {
if (sequential_count == 1) {
(*out_single_loss_count)++;
} else {
(*out_multiple_loss_event_count)++;
*out_multiple_loss_packet_count += sequential_count;
}
sequential_count = 0;
}
sequential_count++;
last_num = current_num;
}
}
if (sequential_count == 1) {
(*out_single_loss_count)++;
} else if (sequential_count > 1) {
(*out_multiple_loss_event_count)++;
*out_multiple_loss_packet_count += sequential_count;
}
}
void PacketLossStats::PruneBuffer() {
// Remove the oldest lost packet and any contiguous packets and move them
// into the historic counts.
auto it = lost_packets_buffer_.begin();
uint16_t last_removed = 0;
int remove_count = 0;
// Count adjacent packets and continue counting if it is wrap around by
// swapping in the wrapped buffer and letting our value wrap as well.
while (remove_count == 0 || (!lost_packets_buffer_.empty() &&
*it == ((last_removed + 1) & 0xFFFF))) {
last_removed = *it;
remove_count++;
auto to_erase = it++;
lost_packets_buffer_.erase(to_erase);
if (lost_packets_buffer_.empty()) {
lost_packets_buffer_.swap(lost_packets_wrapped_buffer_);
it = lost_packets_buffer_.begin();
}
}
if (remove_count > 1) {
multiple_loss_historic_event_count_++;
multiple_loss_historic_packet_count_ += remove_count;
} else {
single_loss_historic_count_++;
}
// Continue pruning if the wrapped buffer is beyond a threshold and there are
// things left in the pre-wrapped buffer.
if (!lost_packets_wrapped_buffer_.empty() &&
*(lost_packets_wrapped_buffer_.rbegin()) > 0x4000) {
PruneBuffer();
}
}
} // namespace webrtc

View file

@ -0,0 +1,58 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_PACKET_LOSS_STATS_H_
#define MODULES_RTP_RTCP_SOURCE_PACKET_LOSS_STATS_H_
#include <stdint.h>
#include <set>
namespace webrtc {
// Keeps track of statistics of packet loss including whether losses are a
// single packet or multiple packets in a row.
class PacketLossStats {
public:
PacketLossStats();
~PacketLossStats();
// Adds a lost packet to the stats by sequence number.
void AddLostPacket(uint16_t sequence_number);
// Queries the number of packets that were lost by themselves, no neighboring
// packets were lost.
int GetSingleLossCount() const;
// Queries the number of times that multiple packets with sequential numbers
// were lost. This is the number of events with more than one packet lost,
// regardless of the size of the event;
int GetMultipleLossEventCount() const;
// Queries the number of packets lost in multiple packet loss events. Combined
// with the event count, this can be used to determine the average event size.
int GetMultipleLossPacketCount() const;
private:
std::set<uint16_t> lost_packets_buffer_;
std::set<uint16_t> lost_packets_wrapped_buffer_;
int single_loss_historic_count_;
int multiple_loss_historic_event_count_;
int multiple_loss_historic_packet_count_;
void ComputeLossCounts(int* out_single_loss_count,
int* out_multiple_loss_event_count,
int* out_multiple_loss_packet_count) const;
void PruneBuffer();
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_PACKET_LOSS_STATS_H_

View file

@ -0,0 +1,151 @@
/*
* 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 "modules/rtp_rtcp/source/packet_sequencer.h"
#include "rtc_base/checks.h"
#include "rtc_base/random.h"
namespace webrtc {
namespace {
// RED header is first byte of payload, if present.
constexpr size_t kRedForFecHeaderLength = 1;
// Timestamps use a 90kHz clock.
constexpr uint32_t kTimestampTicksPerMs = 90;
} // namespace
PacketSequencer::PacketSequencer(uint32_t media_ssrc,
absl::optional<uint32_t> rtx_ssrc,
bool require_marker_before_media_padding,
Clock* clock)
: media_ssrc_(media_ssrc),
rtx_ssrc_(rtx_ssrc),
require_marker_before_media_padding_(require_marker_before_media_padding),
clock_(clock),
media_sequence_number_(0),
rtx_sequence_number_(0),
last_payload_type_(-1),
last_rtp_timestamp_(0),
last_packet_marker_bit_(false) {
Random random(clock_->TimeInMicroseconds());
// Random start, 16 bits. Upper half of range is avoided in order to prevent
// wraparound issues during startup. Sequence number 0 is avoided for
// historical reasons, presumably to avoid debugability or test usage
// conflicts.
constexpr uint16_t kMaxInitRtpSeqNumber = 0x7fff; // 2^15 - 1.
media_sequence_number_ = random.Rand(1, kMaxInitRtpSeqNumber);
rtx_sequence_number_ = random.Rand(1, kMaxInitRtpSeqNumber);
}
void PacketSequencer::Sequence(RtpPacketToSend& packet) {
if (packet.Ssrc() == media_ssrc_) {
if (packet.packet_type() == RtpPacketMediaType::kRetransmission) {
// Retransmission of an already sequenced packet, ignore.
return;
} else if (packet.packet_type() == RtpPacketMediaType::kPadding) {
PopulatePaddingFields(packet);
}
packet.SetSequenceNumber(media_sequence_number_++);
if (packet.packet_type() != RtpPacketMediaType::kPadding) {
UpdateLastPacketState(packet);
}
} else if (packet.Ssrc() == rtx_ssrc_) {
if (packet.packet_type() == RtpPacketMediaType::kPadding) {
PopulatePaddingFields(packet);
}
packet.SetSequenceNumber(rtx_sequence_number_++);
} else {
RTC_DCHECK_NOTREACHED() << "Unexpected ssrc " << packet.Ssrc();
}
}
void PacketSequencer::SetRtpState(const RtpState& state) {
media_sequence_number_ = state.sequence_number;
last_rtp_timestamp_ = state.timestamp;
last_capture_time_ = state.capture_time;
last_timestamp_time_ = state.last_timestamp_time;
}
void PacketSequencer::PopulateRtpState(RtpState& state) const {
state.sequence_number = media_sequence_number_;
state.timestamp = last_rtp_timestamp_;
state.capture_time = last_capture_time_;
state.last_timestamp_time = last_timestamp_time_;
}
void PacketSequencer::UpdateLastPacketState(const RtpPacketToSend& packet) {
// Remember marker bit to determine if padding can be inserted with
// sequence number following `packet`.
last_packet_marker_bit_ = packet.Marker();
// Remember media payload type to use in the padding packet if rtx is
// disabled.
if (packet.is_red()) {
RTC_DCHECK_GE(packet.payload_size(), kRedForFecHeaderLength);
last_payload_type_ = packet.PayloadBuffer()[0];
} else {
last_payload_type_ = packet.PayloadType();
}
// Save timestamps to generate timestamp field and extensions for the padding.
last_rtp_timestamp_ = packet.Timestamp();
last_timestamp_time_ = clock_->CurrentTime();
last_capture_time_ = packet.capture_time();
}
void PacketSequencer::PopulatePaddingFields(RtpPacketToSend& packet) {
if (packet.Ssrc() == media_ssrc_) {
RTC_DCHECK(CanSendPaddingOnMediaSsrc());
packet.SetTimestamp(last_rtp_timestamp_);
packet.set_capture_time(last_capture_time_);
packet.SetPayloadType(last_payload_type_);
return;
}
RTC_DCHECK(packet.Ssrc() == rtx_ssrc_);
if (packet.payload_size() > 0) {
// This is payload padding packet, don't update timestamp fields.
return;
}
packet.SetTimestamp(last_rtp_timestamp_);
packet.set_capture_time(last_capture_time_);
// Only change the timestamp of padding packets sent over RTX.
// Padding only packets over RTP has to be sent as part of a media
// frame (and therefore the same timestamp).
if (last_timestamp_time_ > Timestamp::Zero()) {
TimeDelta since_last_media = clock_->CurrentTime() - last_timestamp_time_;
packet.SetTimestamp(packet.Timestamp() +
since_last_media.ms() * kTimestampTicksPerMs);
if (packet.capture_time() > Timestamp::Zero()) {
packet.set_capture_time(packet.capture_time() + since_last_media);
}
}
}
bool PacketSequencer::CanSendPaddingOnMediaSsrc() const {
if (last_payload_type_ == -1) {
return false;
}
// Without RTX we can't send padding in the middle of frames.
// For audio marker bits doesn't mark the end of a frame and frames
// are usually a single packet, so for now we don't apply this rule
// for audio.
if (require_marker_before_media_padding_ && !last_packet_marker_bit_) {
return false;
}
return true;
}
} // namespace webrtc

View file

@ -0,0 +1,77 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_PACKET_SEQUENCER_H_
#define MODULES_RTP_RTCP_SOURCE_PACKET_SEQUENCER_H_
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
#include "system_wrappers/include/clock.h"
namespace webrtc {
// Helper class used to assign RTP sequence numbers and populate some fields for
// padding packets based on the last sequenced packets.
// This class is not thread safe, the caller must provide that.
class PacketSequencer {
public:
// If `require_marker_before_media_padding_` is true, padding packets on the
// media ssrc is not allowed unless the last sequenced media packet had the
// marker bit set (i.e. don't insert padding packets between the first and
// last packets of a video frame).
// Packets with unknown SSRCs will be ignored.
PacketSequencer(uint32_t media_ssrc,
absl::optional<uint32_t> rtx_ssrc,
bool require_marker_before_media_padding,
Clock* clock);
// Assigns sequence number, and in the case of non-RTX padding also timestamps
// and payload type.
void Sequence(RtpPacketToSend& packet);
void set_media_sequence_number(uint16_t sequence_number) {
media_sequence_number_ = sequence_number;
}
void set_rtx_sequence_number(uint16_t sequence_number) {
rtx_sequence_number_ = sequence_number;
}
void SetRtpState(const RtpState& state);
void PopulateRtpState(RtpState& state) const;
uint16_t media_sequence_number() const { return media_sequence_number_; }
uint16_t rtx_sequence_number() const { return rtx_sequence_number_; }
// Checks whether it is allowed to send padding on the media SSRC at this
// time, e.g. that we don't send padding in the middle of a video frame.
bool CanSendPaddingOnMediaSsrc() const;
private:
void UpdateLastPacketState(const RtpPacketToSend& packet);
void PopulatePaddingFields(RtpPacketToSend& packet);
const uint32_t media_ssrc_;
const absl::optional<uint32_t> rtx_ssrc_;
const bool require_marker_before_media_padding_;
Clock* const clock_;
uint16_t media_sequence_number_;
uint16_t rtx_sequence_number_;
int8_t last_payload_type_;
uint32_t last_rtp_timestamp_;
Timestamp last_capture_time_ = Timestamp::MinusInfinity();
Timestamp last_timestamp_time_ = Timestamp::MinusInfinity();
bool last_packet_marker_bit_;
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_PACKET_SEQUENCER_H_

View file

@ -0,0 +1,433 @@
/*
* Copyright (c) 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.
*/
#include "modules/rtp_rtcp/source/receive_statistics_impl.h"
#include <cmath>
#include <cstdlib>
#include <memory>
#include <utility>
#include <vector>
#include "api/units/time_delta.h"
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_config.h"
#include "rtc_base/logging.h"
#include "rtc_base/time_utils.h"
#include "system_wrappers/include/clock.h"
namespace webrtc {
namespace {
constexpr TimeDelta kStatisticsTimeout = TimeDelta::Seconds(8);
constexpr TimeDelta kStatisticsProcessInterval = TimeDelta::Seconds(1);
TimeDelta UnixEpochDelta(Clock& clock) {
Timestamp now = clock.CurrentTime();
NtpTime ntp_now = clock.ConvertTimestampToNtpTime(now);
return TimeDelta::Millis(ntp_now.ToMs() - now.ms() -
rtc::kNtpJan1970Millisecs);
}
} // namespace
StreamStatistician::~StreamStatistician() {}
StreamStatisticianImpl::StreamStatisticianImpl(uint32_t ssrc,
Clock* clock,
int max_reordering_threshold)
: ssrc_(ssrc),
clock_(clock),
delta_internal_unix_epoch_(UnixEpochDelta(*clock_)),
incoming_bitrate_(/*max_window_size=*/kStatisticsProcessInterval),
max_reordering_threshold_(max_reordering_threshold),
enable_retransmit_detection_(false),
cumulative_loss_is_capped_(false),
jitter_q4_(0),
cumulative_loss_(0),
cumulative_loss_rtcp_offset_(0),
last_received_timestamp_(0),
received_seq_first_(-1),
received_seq_max_(-1),
last_report_cumulative_loss_(0),
last_report_seq_max_(-1),
last_payload_type_frequency_(0) {}
StreamStatisticianImpl::~StreamStatisticianImpl() = default;
bool StreamStatisticianImpl::UpdateOutOfOrder(const RtpPacketReceived& packet,
int64_t sequence_number,
Timestamp now) {
// Check if `packet` is second packet of a stream restart.
if (received_seq_out_of_order_) {
// Count the previous packet as a received; it was postponed below.
--cumulative_loss_;
uint16_t expected_sequence_number = *received_seq_out_of_order_ + 1;
received_seq_out_of_order_ = absl::nullopt;
if (packet.SequenceNumber() == expected_sequence_number) {
// Ignore sequence number gap caused by stream restart for packet loss
// calculation, by setting received_seq_max_ to the sequence number just
// before the out-of-order seqno. This gives a net zero change of
// `cumulative_loss_`, for the two packets interpreted as a stream reset.
//
// Fraction loss for the next report may get a bit off, since we don't
// update last_report_seq_max_ and last_report_cumulative_loss_ in a
// consistent way.
last_report_seq_max_ = sequence_number - 2;
received_seq_max_ = sequence_number - 2;
return false;
}
}
if (std::abs(sequence_number - received_seq_max_) >
max_reordering_threshold_) {
// Sequence number gap looks too large, wait until next packet to check
// for a stream restart.
received_seq_out_of_order_ = packet.SequenceNumber();
// Postpone counting this as a received packet until we know how to update
// `received_seq_max_`, otherwise we temporarily decrement
// `cumulative_loss_`. The
// ReceiveStatisticsTest.StreamRestartDoesntCountAsLoss test expects
// `cumulative_loss_` to be unchanged by the reception of the first packet
// after stream reset.
++cumulative_loss_;
return true;
}
if (sequence_number > received_seq_max_)
return false;
// Old out of order packet, may be retransmit.
if (enable_retransmit_detection_ && IsRetransmitOfOldPacket(packet, now))
receive_counters_.retransmitted.AddPacket(packet);
return true;
}
void StreamStatisticianImpl::UpdateCounters(const RtpPacketReceived& packet) {
RTC_DCHECK_EQ(ssrc_, packet.Ssrc());
Timestamp now = clock_->CurrentTime();
incoming_bitrate_.Update(packet.size(), now);
receive_counters_.transmitted.AddPacket(packet);
--cumulative_loss_;
// Use PeekUnwrap and later update the state to avoid updating the state for
// out of order packets.
int64_t sequence_number = seq_unwrapper_.PeekUnwrap(packet.SequenceNumber());
if (!ReceivedRtpPacket()) {
received_seq_first_ = sequence_number;
last_report_seq_max_ = sequence_number - 1;
received_seq_max_ = sequence_number - 1;
receive_counters_.first_packet_time = now;
} else if (UpdateOutOfOrder(packet, sequence_number, now)) {
return;
}
// In order packet.
cumulative_loss_ += sequence_number - received_seq_max_;
received_seq_max_ = sequence_number;
// Update the internal state of `seq_unwrapper_`.
seq_unwrapper_.Unwrap(packet.SequenceNumber());
// If new time stamp and more than one in-order packet received, calculate
// new jitter statistics.
if (packet.Timestamp() != last_received_timestamp_ &&
(receive_counters_.transmitted.packets -
receive_counters_.retransmitted.packets) > 1) {
UpdateJitter(packet, now);
}
last_received_timestamp_ = packet.Timestamp();
last_receive_time_ = now;
}
void StreamStatisticianImpl::UpdateJitter(const RtpPacketReceived& packet,
Timestamp receive_time) {
RTC_DCHECK(last_receive_time_.has_value());
TimeDelta receive_diff = receive_time - *last_receive_time_;
RTC_DCHECK_GE(receive_diff, TimeDelta::Zero());
uint32_t receive_diff_rtp =
(receive_diff * packet.payload_type_frequency()).seconds<uint32_t>();
int32_t time_diff_samples =
receive_diff_rtp - (packet.Timestamp() - last_received_timestamp_);
ReviseFrequencyAndJitter(packet.payload_type_frequency());
// lib_jingle sometimes deliver crazy jumps in TS for the same stream.
// If this happens, don't update jitter value. Use 5 secs video frequency
// as the threshold.
if (time_diff_samples < 5 * kVideoPayloadTypeFrequency &&
time_diff_samples > -5 * kVideoPayloadTypeFrequency) {
// Note we calculate in Q4 to avoid using float.
int32_t jitter_diff_q4 = (std::abs(time_diff_samples) << 4) - jitter_q4_;
jitter_q4_ += ((jitter_diff_q4 + 8) >> 4);
}
}
void StreamStatisticianImpl::ReviseFrequencyAndJitter(
int payload_type_frequency) {
if (payload_type_frequency == last_payload_type_frequency_) {
return;
}
if (payload_type_frequency != 0) {
if (last_payload_type_frequency_ != 0) {
// Value in "jitter_q4_" variable is a number of samples.
// I.e. jitter = timestamp (s) * frequency (Hz).
// Since the frequency has changed we have to update the number of samples
// accordingly. The new value should rely on a new frequency.
// If we don't do such procedure we end up with the number of samples that
// cannot be converted into TimeDelta correctly
// (i.e. jitter = jitter_q4_ >> 4 / payload_type_frequency).
// In such case, the number of samples has a "mix".
// Doing so we pretend that everything prior and including the current
// packet were computed on packet's frequency.
jitter_q4_ = static_cast<int>(static_cast<uint64_t>(jitter_q4_) *
payload_type_frequency /
last_payload_type_frequency_);
}
// If last_payload_type_frequency_ is not present, the jitter_q4_
// variable has its initial value.
// Keep last_payload_type_frequency_ up to date and non-zero (set).
last_payload_type_frequency_ = payload_type_frequency;
}
}
void StreamStatisticianImpl::SetMaxReorderingThreshold(
int max_reordering_threshold) {
max_reordering_threshold_ = max_reordering_threshold;
}
void StreamStatisticianImpl::EnableRetransmitDetection(bool enable) {
enable_retransmit_detection_ = enable;
}
RtpReceiveStats StreamStatisticianImpl::GetStats() const {
RtpReceiveStats stats;
stats.packets_lost = cumulative_loss_;
// Note: internal jitter value is in Q4 and needs to be scaled by 1/16.
stats.jitter = jitter_q4_ >> 4;
if (last_payload_type_frequency_ > 0) {
// Divide value in fractional seconds by frequency to get jitter in
// fractional seconds.
stats.interarrival_jitter =
TimeDelta::Seconds(stats.jitter) / last_payload_type_frequency_;
}
if (last_receive_time_.has_value()) {
stats.last_packet_received =
*last_receive_time_ + delta_internal_unix_epoch_;
}
stats.packet_counter = receive_counters_.transmitted;
return stats;
}
void StreamStatisticianImpl::MaybeAppendReportBlockAndReset(
std::vector<rtcp::ReportBlock>& report_blocks) {
if (!ReceivedRtpPacket()) {
return;
}
Timestamp now = clock_->CurrentTime();
if (now - *last_receive_time_ >= kStatisticsTimeout) {
// Not active.
return;
}
report_blocks.emplace_back();
rtcp::ReportBlock& stats = report_blocks.back();
stats.SetMediaSsrc(ssrc_);
// Calculate fraction lost.
int64_t exp_since_last = received_seq_max_ - last_report_seq_max_;
RTC_DCHECK_GE(exp_since_last, 0);
int32_t lost_since_last = cumulative_loss_ - last_report_cumulative_loss_;
if (exp_since_last > 0 && lost_since_last > 0) {
// Scale 0 to 255, where 255 is 100% loss.
stats.SetFractionLost(255 * lost_since_last / exp_since_last);
}
int packets_lost = cumulative_loss_ + cumulative_loss_rtcp_offset_;
if (packets_lost < 0) {
// Clamp to zero. Work around to accommodate for senders that misbehave with
// negative cumulative loss.
packets_lost = 0;
cumulative_loss_rtcp_offset_ = -cumulative_loss_;
}
if (packets_lost > 0x7fffff) {
// Packets lost is a 24 bit signed field, and thus should be clamped, as
// described in https://datatracker.ietf.org/doc/html/rfc3550#appendix-A.3
if (!cumulative_loss_is_capped_) {
cumulative_loss_is_capped_ = true;
RTC_LOG(LS_WARNING) << "Cumulative loss reached maximum value for ssrc "
<< ssrc_;
}
packets_lost = 0x7fffff;
}
stats.SetCumulativeLost(packets_lost);
stats.SetExtHighestSeqNum(received_seq_max_);
// Note: internal jitter value is in Q4 and needs to be scaled by 1/16.
stats.SetJitter(jitter_q4_ >> 4);
// Only for report blocks in RTCP SR and RR.
last_report_cumulative_loss_ = cumulative_loss_;
last_report_seq_max_ = received_seq_max_;
BWE_TEST_LOGGING_PLOT_WITH_SSRC(1, "cumulative_loss_pkts", now.ms(),
cumulative_loss_, ssrc_);
BWE_TEST_LOGGING_PLOT_WITH_SSRC(1, "received_seq_max_pkts", now.ms(),
(received_seq_max_ - received_seq_first_),
ssrc_);
}
absl::optional<int> StreamStatisticianImpl::GetFractionLostInPercent() const {
if (!ReceivedRtpPacket()) {
return absl::nullopt;
}
int64_t expected_packets = 1 + received_seq_max_ - received_seq_first_;
if (expected_packets <= 0) {
return absl::nullopt;
}
if (cumulative_loss_ <= 0) {
return 0;
}
return 100 * static_cast<int64_t>(cumulative_loss_) / expected_packets;
}
StreamDataCounters StreamStatisticianImpl::GetReceiveStreamDataCounters()
const {
return receive_counters_;
}
uint32_t StreamStatisticianImpl::BitrateReceived() const {
return incoming_bitrate_.Rate(clock_->CurrentTime())
.value_or(DataRate::Zero())
.bps<uint32_t>();
}
bool StreamStatisticianImpl::IsRetransmitOfOldPacket(
const RtpPacketReceived& packet,
Timestamp now) const {
int frequency_hz = packet.payload_type_frequency();
RTC_DCHECK(last_receive_time_.has_value());
RTC_CHECK_GT(frequency_hz, 0);
TimeDelta time_diff = now - *last_receive_time_;
// Diff in time stamp since last received in order.
uint32_t timestamp_diff = packet.Timestamp() - last_received_timestamp_;
TimeDelta rtp_time_stamp_diff =
TimeDelta::Seconds(timestamp_diff) / frequency_hz;
// Jitter standard deviation in samples.
float jitter_std = std::sqrt(static_cast<float>(jitter_q4_ >> 4));
// 2 times the standard deviation => 95% confidence.
// Min max_delay is 1ms.
TimeDelta max_delay = std::max(
TimeDelta::Seconds(2 * jitter_std / frequency_hz), TimeDelta::Millis(1));
return time_diff > rtp_time_stamp_diff + max_delay;
}
std::unique_ptr<ReceiveStatistics> ReceiveStatistics::Create(Clock* clock) {
return std::make_unique<ReceiveStatisticsLocked>(
clock, [](uint32_t ssrc, Clock* clock, int max_reordering_threshold) {
return std::make_unique<StreamStatisticianLocked>(
ssrc, clock, max_reordering_threshold);
});
}
std::unique_ptr<ReceiveStatistics> ReceiveStatistics::CreateThreadCompatible(
Clock* clock) {
return std::make_unique<ReceiveStatisticsImpl>(
clock, [](uint32_t ssrc, Clock* clock, int max_reordering_threshold) {
return std::make_unique<StreamStatisticianImpl>(
ssrc, clock, max_reordering_threshold);
});
}
ReceiveStatisticsImpl::ReceiveStatisticsImpl(
Clock* clock,
std::function<std::unique_ptr<StreamStatisticianImplInterface>(
uint32_t ssrc,
Clock* clock,
int max_reordering_threshold)> stream_statistician_factory)
: clock_(clock),
stream_statistician_factory_(std::move(stream_statistician_factory)),
last_returned_ssrc_idx_(0),
max_reordering_threshold_(kDefaultMaxReorderingThreshold) {}
void ReceiveStatisticsImpl::OnRtpPacket(const RtpPacketReceived& packet) {
// StreamStatisticianImpl instance is created once and only destroyed when
// this whole ReceiveStatisticsImpl is destroyed. StreamStatisticianImpl has
// it's own locking so don't hold receive_statistics_lock_ (potential
// deadlock).
GetOrCreateStatistician(packet.Ssrc())->UpdateCounters(packet);
}
StreamStatistician* ReceiveStatisticsImpl::GetStatistician(
uint32_t ssrc) const {
const auto& it = statisticians_.find(ssrc);
if (it == statisticians_.end())
return nullptr;
return it->second.get();
}
StreamStatisticianImplInterface* ReceiveStatisticsImpl::GetOrCreateStatistician(
uint32_t ssrc) {
std::unique_ptr<StreamStatisticianImplInterface>& impl = statisticians_[ssrc];
if (impl == nullptr) { // new element
impl =
stream_statistician_factory_(ssrc, clock_, max_reordering_threshold_);
all_ssrcs_.push_back(ssrc);
}
return impl.get();
}
void ReceiveStatisticsImpl::SetMaxReorderingThreshold(
int max_reordering_threshold) {
max_reordering_threshold_ = max_reordering_threshold;
for (auto& statistician : statisticians_) {
statistician.second->SetMaxReorderingThreshold(max_reordering_threshold);
}
}
void ReceiveStatisticsImpl::SetMaxReorderingThreshold(
uint32_t ssrc,
int max_reordering_threshold) {
GetOrCreateStatistician(ssrc)->SetMaxReorderingThreshold(
max_reordering_threshold);
}
void ReceiveStatisticsImpl::EnableRetransmitDetection(uint32_t ssrc,
bool enable) {
GetOrCreateStatistician(ssrc)->EnableRetransmitDetection(enable);
}
std::vector<rtcp::ReportBlock> ReceiveStatisticsImpl::RtcpReportBlocks(
size_t max_blocks) {
std::vector<rtcp::ReportBlock> result;
result.reserve(std::min(max_blocks, all_ssrcs_.size()));
size_t ssrc_idx = 0;
for (size_t i = 0; i < all_ssrcs_.size() && result.size() < max_blocks; ++i) {
ssrc_idx = (last_returned_ssrc_idx_ + i + 1) % all_ssrcs_.size();
const uint32_t media_ssrc = all_ssrcs_[ssrc_idx];
auto statistician_it = statisticians_.find(media_ssrc);
RTC_DCHECK(statistician_it != statisticians_.end());
statistician_it->second->MaybeAppendReportBlockAndReset(result);
}
last_returned_ssrc_idx_ = ssrc_idx;
return result;
}
} // namespace webrtc

View file

@ -0,0 +1,252 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_RECEIVE_STATISTICS_IMPL_H_
#define MODULES_RTP_RTCP_SOURCE_RECEIVE_STATISTICS_IMPL_H_
#include <algorithm>
#include <functional>
#include <memory>
#include <utility>
#include <vector>
#include "absl/types/optional.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/rtp_rtcp/include/receive_statistics.h"
#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
#include "rtc_base/bitrate_tracker.h"
#include "rtc_base/containers/flat_map.h"
#include "rtc_base/numerics/sequence_number_unwrapper.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h"
namespace webrtc {
// Extends StreamStatistician with methods needed by the implementation.
class StreamStatisticianImplInterface : public StreamStatistician {
public:
virtual ~StreamStatisticianImplInterface() = default;
virtual void MaybeAppendReportBlockAndReset(
std::vector<rtcp::ReportBlock>& report_blocks) = 0;
virtual void SetMaxReorderingThreshold(int max_reordering_threshold) = 0;
virtual void EnableRetransmitDetection(bool enable) = 0;
virtual void UpdateCounters(const RtpPacketReceived& packet) = 0;
};
// Thread-compatible implementation of StreamStatisticianImplInterface.
class StreamStatisticianImpl : public StreamStatisticianImplInterface {
public:
StreamStatisticianImpl(uint32_t ssrc,
Clock* clock,
int max_reordering_threshold);
~StreamStatisticianImpl() override;
// Implements StreamStatistician
RtpReceiveStats GetStats() const override;
absl::optional<int> GetFractionLostInPercent() const override;
StreamDataCounters GetReceiveStreamDataCounters() const override;
uint32_t BitrateReceived() const override;
// Implements StreamStatisticianImplInterface
void MaybeAppendReportBlockAndReset(
std::vector<rtcp::ReportBlock>& report_blocks) override;
void SetMaxReorderingThreshold(int max_reordering_threshold) override;
void EnableRetransmitDetection(bool enable) override;
// Updates StreamStatistician for incoming packets.
void UpdateCounters(const RtpPacketReceived& packet) override;
private:
bool IsRetransmitOfOldPacket(const RtpPacketReceived& packet,
Timestamp now) const;
void UpdateJitter(const RtpPacketReceived& packet, Timestamp receive_time);
void ReviseFrequencyAndJitter(int payload_type_frequency);
// Updates StreamStatistician for out of order packets.
// Returns true if packet considered to be out of order.
bool UpdateOutOfOrder(const RtpPacketReceived& packet,
int64_t sequence_number,
Timestamp now);
// Checks if this StreamStatistician received any rtp packets.
bool ReceivedRtpPacket() const { return last_receive_time_.has_value(); }
const uint32_t ssrc_;
Clock* const clock_;
// Delta used to map internal timestamps to Unix epoch ones.
const TimeDelta delta_internal_unix_epoch_;
BitrateTracker incoming_bitrate_;
// In number of packets or sequence numbers.
int max_reordering_threshold_;
bool enable_retransmit_detection_;
bool cumulative_loss_is_capped_;
// Stats on received RTP packets.
uint32_t jitter_q4_;
// Cumulative loss according to RFC 3550, which may be negative (and often is,
// if packets are reordered and there are non-RTX retransmissions).
int32_t cumulative_loss_;
// Offset added to outgoing rtcp reports, to make ensure that the reported
// cumulative loss is non-negative. Reports with negative values confuse some
// senders, in particular, our own loss-based bandwidth estimator.
int32_t cumulative_loss_rtcp_offset_;
absl::optional<Timestamp> last_receive_time_;
uint32_t last_received_timestamp_;
RtpSequenceNumberUnwrapper seq_unwrapper_;
int64_t received_seq_first_;
int64_t received_seq_max_;
// Assume that the other side restarted when there are two sequential packets
// with large jump from received_seq_max_.
absl::optional<uint16_t> received_seq_out_of_order_;
// Current counter values.
StreamDataCounters receive_counters_;
// Counter values when we sent the last report.
int32_t last_report_cumulative_loss_;
int64_t last_report_seq_max_;
// The sample frequency of the last received packet.
int last_payload_type_frequency_;
};
// Thread-safe implementation of StreamStatisticianImplInterface.
class StreamStatisticianLocked : public StreamStatisticianImplInterface {
public:
StreamStatisticianLocked(uint32_t ssrc,
Clock* clock,
int max_reordering_threshold)
: impl_(ssrc, clock, max_reordering_threshold) {}
~StreamStatisticianLocked() override = default;
RtpReceiveStats GetStats() const override {
MutexLock lock(&stream_lock_);
return impl_.GetStats();
}
absl::optional<int> GetFractionLostInPercent() const override {
MutexLock lock(&stream_lock_);
return impl_.GetFractionLostInPercent();
}
StreamDataCounters GetReceiveStreamDataCounters() const override {
MutexLock lock(&stream_lock_);
return impl_.GetReceiveStreamDataCounters();
}
uint32_t BitrateReceived() const override {
MutexLock lock(&stream_lock_);
return impl_.BitrateReceived();
}
void MaybeAppendReportBlockAndReset(
std::vector<rtcp::ReportBlock>& report_blocks) override {
MutexLock lock(&stream_lock_);
impl_.MaybeAppendReportBlockAndReset(report_blocks);
}
void SetMaxReorderingThreshold(int max_reordering_threshold) override {
MutexLock lock(&stream_lock_);
return impl_.SetMaxReorderingThreshold(max_reordering_threshold);
}
void EnableRetransmitDetection(bool enable) override {
MutexLock lock(&stream_lock_);
return impl_.EnableRetransmitDetection(enable);
}
void UpdateCounters(const RtpPacketReceived& packet) override {
MutexLock lock(&stream_lock_);
return impl_.UpdateCounters(packet);
}
private:
mutable Mutex stream_lock_;
StreamStatisticianImpl impl_ RTC_GUARDED_BY(&stream_lock_);
};
// Thread-compatible implementation.
class ReceiveStatisticsImpl : public ReceiveStatistics {
public:
ReceiveStatisticsImpl(
Clock* clock,
std::function<std::unique_ptr<StreamStatisticianImplInterface>(
uint32_t ssrc,
Clock* clock,
int max_reordering_threshold)> stream_statistician_factory);
~ReceiveStatisticsImpl() override = default;
// Implements ReceiveStatisticsProvider.
std::vector<rtcp::ReportBlock> RtcpReportBlocks(size_t max_blocks) override;
// Implements RtpPacketSinkInterface
void OnRtpPacket(const RtpPacketReceived& packet) override;
// Implements ReceiveStatistics.
StreamStatistician* GetStatistician(uint32_t ssrc) const override;
void SetMaxReorderingThreshold(int max_reordering_threshold) override;
void SetMaxReorderingThreshold(uint32_t ssrc,
int max_reordering_threshold) override;
void EnableRetransmitDetection(uint32_t ssrc, bool enable) override;
private:
StreamStatisticianImplInterface* GetOrCreateStatistician(uint32_t ssrc);
Clock* const clock_;
std::function<std::unique_ptr<StreamStatisticianImplInterface>(
uint32_t ssrc,
Clock* clock,
int max_reordering_threshold)>
stream_statistician_factory_;
// The index within `all_ssrcs_` that was last returned.
size_t last_returned_ssrc_idx_;
std::vector<uint32_t> all_ssrcs_;
int max_reordering_threshold_;
flat_map<uint32_t /*ssrc*/, std::unique_ptr<StreamStatisticianImplInterface>>
statisticians_;
};
// Thread-safe implementation wrapping access to ReceiveStatisticsImpl with a
// mutex.
class ReceiveStatisticsLocked : public ReceiveStatistics {
public:
explicit ReceiveStatisticsLocked(
Clock* clock,
std::function<std::unique_ptr<StreamStatisticianImplInterface>(
uint32_t ssrc,
Clock* clock,
int max_reordering_threshold)> stream_statitician_factory)
: impl_(clock, std::move(stream_statitician_factory)) {}
~ReceiveStatisticsLocked() override = default;
std::vector<rtcp::ReportBlock> RtcpReportBlocks(size_t max_blocks) override {
MutexLock lock(&receive_statistics_lock_);
return impl_.RtcpReportBlocks(max_blocks);
}
void OnRtpPacket(const RtpPacketReceived& packet) override {
MutexLock lock(&receive_statistics_lock_);
return impl_.OnRtpPacket(packet);
}
StreamStatistician* GetStatistician(uint32_t ssrc) const override {
MutexLock lock(&receive_statistics_lock_);
return impl_.GetStatistician(ssrc);
}
void SetMaxReorderingThreshold(int max_reordering_threshold) override {
MutexLock lock(&receive_statistics_lock_);
return impl_.SetMaxReorderingThreshold(max_reordering_threshold);
}
void SetMaxReorderingThreshold(uint32_t ssrc,
int max_reordering_threshold) override {
MutexLock lock(&receive_statistics_lock_);
return impl_.SetMaxReorderingThreshold(ssrc, max_reordering_threshold);
}
void EnableRetransmitDetection(uint32_t ssrc, bool enable) override {
MutexLock lock(&receive_statistics_lock_);
return impl_.EnableRetransmitDetection(ssrc, enable);
}
private:
mutable Mutex receive_statistics_lock_;
ReceiveStatisticsImpl impl_ RTC_GUARDED_BY(&receive_statistics_lock_);
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RECEIVE_STATISTICS_IMPL_H_

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/rtp_rtcp/include/remote_ntp_time_estimator.h"
#include <cstdint>
#include "modules/rtp_rtcp/source/time_util.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/clock.h"
#include "system_wrappers/include/ntp_time.h"
namespace webrtc {
namespace {
constexpr int kMinimumNumberOfSamples = 2;
constexpr TimeDelta kTimingLogInterval = TimeDelta::Seconds(10);
constexpr int kClocksOffsetSmoothingWindow = 100;
// Subtracts two NtpTime values keeping maximum precision.
int64_t Subtract(NtpTime minuend, NtpTime subtrahend) {
uint64_t a = static_cast<uint64_t>(minuend);
uint64_t b = static_cast<uint64_t>(subtrahend);
return a >= b ? static_cast<int64_t>(a - b) : -static_cast<int64_t>(b - a);
}
NtpTime Add(NtpTime lhs, int64_t rhs) {
uint64_t result = static_cast<uint64_t>(lhs);
if (rhs >= 0) {
result += static_cast<uint64_t>(rhs);
} else {
result -= static_cast<uint64_t>(-rhs);
}
return NtpTime(result);
}
} // namespace
// TODO(wu): Refactor this class so that it can be shared with
// vie_sync_module.cc.
RemoteNtpTimeEstimator::RemoteNtpTimeEstimator(Clock* clock)
: clock_(clock),
ntp_clocks_offset_estimator_(kClocksOffsetSmoothingWindow) {}
bool RemoteNtpTimeEstimator::UpdateRtcpTimestamp(TimeDelta rtt,
NtpTime sender_send_time,
uint32_t rtp_timestamp) {
switch (rtp_to_ntp_.UpdateMeasurements(sender_send_time, rtp_timestamp)) {
case RtpToNtpEstimator::kInvalidMeasurement:
return false;
case RtpToNtpEstimator::kSameMeasurement:
// No new RTCP SR since last time this function was called.
return true;
case RtpToNtpEstimator::kNewMeasurement:
break;
}
// Assume connection is symmetric and thus time to deliver the packet is half
// the round trip time.
int64_t deliver_time_ntp = ToNtpUnits(rtt) / 2;
// Update extrapolator with the new arrival time.
NtpTime receiver_arrival_time = clock_->CurrentNtpTime();
int64_t remote_to_local_clocks_offset =
Subtract(receiver_arrival_time, sender_send_time) - deliver_time_ntp;
ntp_clocks_offset_estimator_.Insert(remote_to_local_clocks_offset);
return true;
}
NtpTime RemoteNtpTimeEstimator::EstimateNtp(uint32_t rtp_timestamp) {
NtpTime sender_capture = rtp_to_ntp_.Estimate(rtp_timestamp);
if (!sender_capture.Valid()) {
return sender_capture;
}
int64_t remote_to_local_clocks_offset =
ntp_clocks_offset_estimator_.GetFilteredValue();
NtpTime receiver_capture = Add(sender_capture, remote_to_local_clocks_offset);
Timestamp now = clock_->CurrentTime();
if (now - last_timing_log_ > kTimingLogInterval) {
RTC_LOG(LS_INFO) << "RTP timestamp: " << rtp_timestamp
<< " in NTP clock: " << sender_capture.ToMs()
<< " estimated time in receiver NTP clock: "
<< receiver_capture.ToMs();
last_timing_log_ = now;
}
return receiver_capture;
}
absl::optional<int64_t>
RemoteNtpTimeEstimator::EstimateRemoteToLocalClockOffset() {
if (ntp_clocks_offset_estimator_.GetNumberOfSamplesStored() <
kMinimumNumberOfSamples) {
return absl::nullopt;
}
return ntp_clocks_offset_estimator_.GetFilteredValue();
}
} // namespace webrtc

View file

@ -0,0 +1,29 @@
/*
* 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 "modules/rtp_rtcp/source/rtcp_nack_stats.h"
#include "modules/include/module_common_types_public.h"
namespace webrtc {
RtcpNackStats::RtcpNackStats()
: max_sequence_number_(0), requests_(0), unique_requests_(0) {}
void RtcpNackStats::ReportRequest(uint16_t sequence_number) {
if (requests_ == 0 ||
IsNewerSequenceNumber(sequence_number, max_sequence_number_)) {
max_sequence_number_ = sequence_number;
++unique_requests_;
}
++requests_;
}
} // namespace webrtc

View file

@ -0,0 +1,40 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_RTCP_NACK_STATS_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_NACK_STATS_H_
#include <stdint.h>
namespace webrtc {
class RtcpNackStats {
public:
RtcpNackStats();
// Updates stats with requested sequence number.
// This function should be called for each NACK request to calculate the
// number of unique NACKed RTP packets.
void ReportRequest(uint16_t sequence_number);
// Gets the number of NACKed RTP packets.
uint32_t requests() const { return requests_; }
// Gets the number of unique NACKed RTP packets.
uint32_t unique_requests() const { return unique_requests_; }
private:
uint16_t max_sequence_number_;
uint32_t requests_;
uint32_t unique_requests_;
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_NACK_STATS_H_

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/rtp_rtcp/source/rtcp_packet.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "rtc_base/checks.h"
namespace webrtc {
namespace rtcp {
constexpr size_t RtcpPacket::kHeaderLength;
rtc::Buffer RtcpPacket::Build() const {
rtc::Buffer packet(BlockLength());
size_t length = 0;
bool created = Create(packet.data(), &length, packet.capacity(), nullptr);
RTC_DCHECK(created) << "Invalid packet is not supported.";
RTC_DCHECK_EQ(length, packet.size())
<< "BlockLength mispredicted size used by Create";
return packet;
}
bool RtcpPacket::Build(size_t max_length, PacketReadyCallback callback) const {
RTC_CHECK_LE(max_length, IP_PACKET_SIZE);
uint8_t buffer[IP_PACKET_SIZE];
size_t index = 0;
if (!Create(buffer, &index, max_length, callback))
return false;
return OnBufferFull(buffer, &index, callback);
}
bool RtcpPacket::OnBufferFull(uint8_t* packet,
size_t* index,
PacketReadyCallback callback) const {
if (*index == 0)
return false;
RTC_DCHECK(callback) << "Fragmentation not supported.";
callback(rtc::ArrayView<const uint8_t>(packet, *index));
*index = 0;
return true;
}
size_t RtcpPacket::HeaderLength() const {
size_t length_in_bytes = BlockLength();
RTC_DCHECK_GT(length_in_bytes, 0);
RTC_DCHECK_EQ(length_in_bytes % 4, 0)
<< "Padding must be handled by each subclass.";
// Length in 32-bit words without common header.
return (length_in_bytes - kHeaderLength) / 4;
}
// From RFC 3550, RTP: A Transport Protocol for Real-Time Applications.
//
// RTP header format.
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| RC/FMT | PT | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
void RtcpPacket::CreateHeader(
size_t count_or_format, // Depends on packet type.
uint8_t packet_type,
size_t length,
uint8_t* buffer,
size_t* pos) {
CreateHeader(count_or_format, packet_type, length, /*padding=*/false, buffer,
pos);
}
void RtcpPacket::CreateHeader(
size_t count_or_format, // Depends on packet type.
uint8_t packet_type,
size_t length,
bool padding,
uint8_t* buffer,
size_t* pos) {
RTC_DCHECK_LE(length, 0xffffU);
RTC_DCHECK_LE(count_or_format, 0x1f);
constexpr uint8_t kVersionBits = 2 << 6;
uint8_t padding_bit = padding ? 1 << 5 : 0;
buffer[*pos + 0] =
kVersionBits | padding_bit | static_cast<uint8_t>(count_or_format);
buffer[*pos + 1] = packet_type;
buffer[*pos + 2] = (length >> 8) & 0xff;
buffer[*pos + 3] = length & 0xff;
*pos += kHeaderLength;
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2014 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_H_
#include <stddef.h>
#include <stdint.h>
#include "api/array_view.h"
#include "api/function_view.h"
#include "rtc_base/buffer.h"
namespace webrtc {
namespace rtcp {
// Class for building RTCP packets.
//
// Example:
// ReportBlock report_block;
// report_block.SetMediaSsrc(234);
// report_block.SetFractionLost(10);
//
// ReceiverReport rr;
// rr.SetSenderSsrc(123);
// rr.AddReportBlock(report_block);
//
// Fir fir;
// fir.SetSenderSsrc(123);
// fir.AddRequestTo(234, 56);
//
// size_t length = 0; // Builds an intra frame request
// uint8_t packet[kPacketSize]; // with sequence number 56.
// fir.Build(packet, &length, kPacketSize);
//
// rtc::Buffer packet = fir.Build(); // Returns a RawPacket holding
// // the built rtcp packet.
//
// CompoundPacket compound; // Builds a compound RTCP packet with
// compound.Append(&rr); // a receiver report, report block
// compound.Append(&fir); // and fir message.
// rtc::Buffer packet = compound.Build();
class RtcpPacket {
public:
// Callback used to signal that an RTCP packet is ready. Note that this may
// not contain all data in this RtcpPacket; if a packet cannot fit in
// max_length bytes, it will be fragmented and multiple calls to this
// callback will be made.
using PacketReadyCallback =
rtc::FunctionView<void(rtc::ArrayView<const uint8_t> packet)>;
virtual ~RtcpPacket() = default;
void SetSenderSsrc(uint32_t ssrc) { sender_ssrc_ = ssrc; }
uint32_t sender_ssrc() const { return sender_ssrc_; }
// Convenience method mostly used for test. Creates packet without
// fragmentation using BlockLength() to allocate big enough buffer.
rtc::Buffer Build() const;
// Returns true if call to Create succeeded.
bool Build(size_t max_length, PacketReadyCallback callback) const;
// Size of this packet in bytes (including headers).
virtual size_t BlockLength() const = 0;
// Creates packet in the given buffer at the given position.
// Calls PacketReadyCallback::OnPacketReady if remaining buffer is too small
// and assume buffer can be reused after OnPacketReady returns.
virtual bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const = 0;
protected:
// Size of the rtcp common header.
static constexpr size_t kHeaderLength = 4;
RtcpPacket() {}
static void CreateHeader(size_t count_or_format,
uint8_t packet_type,
size_t block_length, // Payload size in 32bit words.
uint8_t* buffer,
size_t* pos);
static void CreateHeader(size_t count_or_format,
uint8_t packet_type,
size_t block_length, // Payload size in 32bit words.
bool padding, // True if there are padding bytes.
uint8_t* buffer,
size_t* pos);
bool OnBufferFull(uint8_t* packet,
size_t* index,
PacketReadyCallback callback) const;
// Size of the rtcp packet as written in header.
size_t HeaderLength() const;
private:
uint32_t sender_ssrc_ = 0;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_H_

View file

@ -0,0 +1,103 @@
/*
* 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.
*/
#include "modules/rtp_rtcp/source/rtcp_packet/app.h"
#include <string.h>
#include <cstdint>
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
constexpr uint8_t App::kPacketType;
constexpr size_t App::kMaxDataSize;
// Application-Defined packet (APP) (RFC 3550).
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| subtype | PT=APP=204 | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 0 | SSRC/CSRC |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 4 | name (ASCII) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 8 | application-dependent data ...
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
App::App() : sub_type_(0), name_(0) {}
App::~App() = default;
bool App::Parse(const CommonHeader& packet) {
RTC_DCHECK_EQ(packet.type(), kPacketType);
if (packet.payload_size_bytes() < kAppBaseLength) {
RTC_LOG(LS_WARNING) << "Packet is too small to be a valid APP packet";
return false;
}
if (packet.payload_size_bytes() % 4 != 0) {
RTC_LOG(LS_WARNING)
<< "Packet payload must be 32 bits aligned to make a valid APP packet";
return false;
}
sub_type_ = packet.fmt();
SetSenderSsrc(ByteReader<uint32_t>::ReadBigEndian(&packet.payload()[0]));
name_ = ByteReader<uint32_t>::ReadBigEndian(&packet.payload()[4]);
data_.SetData(packet.payload() + kAppBaseLength,
packet.payload_size_bytes() - kAppBaseLength);
return true;
}
void App::SetSubType(uint8_t subtype) {
RTC_DCHECK_LE(subtype, 0x1f);
sub_type_ = subtype;
}
void App::SetData(const uint8_t* data, size_t data_length) {
RTC_DCHECK(data);
RTC_DCHECK_EQ(data_length % 4, 0) << "Data must be 32 bits aligned.";
RTC_DCHECK_LE(data_length, kMaxDataSize)
<< "App data size " << data_length << " exceed maximum of "
<< kMaxDataSize << " bytes.";
data_.SetData(data, data_length);
}
size_t App::BlockLength() const {
return kHeaderLength + kAppBaseLength + data_.size();
}
bool App::Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const {
while (*index + BlockLength() > max_length) {
if (!OnBufferFull(packet, index, callback))
return false;
}
const size_t index_end = *index + BlockLength();
CreateHeader(sub_type_, kPacketType, HeaderLength(), packet, index);
ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 0], sender_ssrc());
ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 4], name_);
if (!data_.empty()) {
memcpy(&packet[*index + 8], data_.data(), data_.size());
}
*index += (8 + data_.size());
RTC_DCHECK_EQ(index_end, *index);
return true;
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,67 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_APP_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_APP_H_
#include <stddef.h>
#include <stdint.h>
#include "modules/rtp_rtcp/source/rtcp_packet.h"
#include "rtc_base/buffer.h"
namespace webrtc {
namespace rtcp {
class CommonHeader;
class App : public RtcpPacket {
public:
static constexpr uint8_t kPacketType = 204;
App();
App(App&&) = default;
~App() override;
// Parse assumes header is already parsed and validated.
bool Parse(const CommonHeader& packet);
void SetSubType(uint8_t subtype);
void SetName(uint32_t name) { name_ = name; }
void SetData(const uint8_t* data, size_t data_length);
uint8_t sub_type() const { return sub_type_; }
uint32_t name() const { return name_; }
size_t data_size() const { return data_.size(); }
const uint8_t* data() const { return data_.data(); }
size_t BlockLength() const override;
bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const override;
static inline constexpr uint32_t NameToInt(const char name[5]) {
return static_cast<uint32_t>(name[0]) << 24 |
static_cast<uint32_t>(name[1]) << 16 |
static_cast<uint32_t>(name[2]) << 8 | static_cast<uint32_t>(name[3]);
}
private:
static constexpr size_t kAppBaseLength = 8; // Ssrc and Name.
static constexpr size_t kMaxDataSize = 0xffff * 4 - kAppBaseLength;
uint8_t sub_type_;
uint32_t name_;
rtc::Buffer data_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_APP_H_

View file

@ -0,0 +1,141 @@
/*
* 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.
*/
#include "modules/rtp_rtcp/source/rtcp_packet/bye.h"
#include <string.h>
#include <cstdint>
#include <utility>
#include "absl/strings/string_view.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
constexpr uint8_t Bye::kPacketType;
// Bye packet (BYE) (RFC 3550).
//
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| SC | PT=BYE=203 | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC/CSRC |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : ... :
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// (opt) | length | reason for leaving ...
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Bye::Bye() = default;
Bye::~Bye() = default;
bool Bye::Parse(const CommonHeader& packet) {
RTC_DCHECK_EQ(packet.type(), kPacketType);
const uint8_t src_count = packet.count();
// Validate packet.
if (packet.payload_size_bytes() < 4u * src_count) {
RTC_LOG(LS_WARNING)
<< "Packet is too small to contain CSRCs it promise to have.";
return false;
}
const uint8_t* const payload = packet.payload();
bool has_reason = packet.payload_size_bytes() > 4u * src_count;
uint8_t reason_length = 0;
if (has_reason) {
reason_length = payload[4u * src_count];
if (packet.payload_size_bytes() - 4u * src_count < 1u + reason_length) {
RTC_LOG(LS_WARNING) << "Invalid reason length: " << reason_length;
return false;
}
}
// Once sure packet is valid, copy values.
if (src_count == 0) { // A count value of zero is valid, but useless.
SetSenderSsrc(0);
csrcs_.clear();
} else {
SetSenderSsrc(ByteReader<uint32_t>::ReadBigEndian(payload));
csrcs_.resize(src_count - 1);
for (size_t i = 1; i < src_count; ++i)
csrcs_[i - 1] = ByteReader<uint32_t>::ReadBigEndian(&payload[4 * i]);
}
if (has_reason) {
reason_.assign(reinterpret_cast<const char*>(&payload[4u * src_count + 1]),
reason_length);
} else {
reason_.clear();
}
return true;
}
bool Bye::Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const {
while (*index + BlockLength() > max_length) {
if (!OnBufferFull(packet, index, callback))
return false;
}
const size_t index_end = *index + BlockLength();
CreateHeader(1 + csrcs_.size(), kPacketType, HeaderLength(), packet, index);
// Store srcs of the leaving clients.
ByteWriter<uint32_t>::WriteBigEndian(&packet[*index], sender_ssrc());
*index += sizeof(uint32_t);
for (uint32_t csrc : csrcs_) {
ByteWriter<uint32_t>::WriteBigEndian(&packet[*index], csrc);
*index += sizeof(uint32_t);
}
// Store the reason to leave.
if (!reason_.empty()) {
uint8_t reason_length = static_cast<uint8_t>(reason_.size());
packet[(*index)++] = reason_length;
memcpy(&packet[*index], reason_.data(), reason_length);
*index += reason_length;
// Add padding bytes if needed.
size_t bytes_to_pad = index_end - *index;
RTC_DCHECK_LE(bytes_to_pad, 3);
if (bytes_to_pad > 0) {
memset(&packet[*index], 0, bytes_to_pad);
*index += bytes_to_pad;
}
}
RTC_DCHECK_EQ(index_end, *index);
return true;
}
bool Bye::SetCsrcs(std::vector<uint32_t> csrcs) {
if (csrcs.size() > kMaxNumberOfCsrcs) {
RTC_LOG(LS_WARNING) << "Too many CSRCs for Bye packet.";
return false;
}
csrcs_ = std::move(csrcs);
return true;
}
void Bye::SetReason(absl::string_view reason) {
RTC_DCHECK_LE(reason.size(), 0xffu);
reason_ = std::string(reason);
}
size_t Bye::BlockLength() const {
size_t src_count = (1 + csrcs_.size());
size_t reason_size_in_32bits = reason_.empty() ? 0 : (reason_.size() / 4 + 1);
return kHeaderLength + 4 * (src_count + reason_size_in_32bits);
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,57 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_BYE_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_BYE_H_
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "modules/rtp_rtcp/source/rtcp_packet.h"
namespace webrtc {
namespace rtcp {
class CommonHeader;
class Bye : public RtcpPacket {
public:
static constexpr uint8_t kPacketType = 203;
Bye();
~Bye() override;
// Parse assumes header is already parsed and validated.
bool Parse(const CommonHeader& packet);
bool SetCsrcs(std::vector<uint32_t> csrcs);
void SetReason(absl::string_view reason);
const std::vector<uint32_t>& csrcs() const { return csrcs_; }
const std::string& reason() const { return reason_; }
size_t BlockLength() const override;
bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const override;
private:
static const int kMaxNumberOfCsrcs = 0x1f - 1; // First item is sender SSRC.
std::vector<uint32_t> csrcs_;
std::string reason_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_BYE_H_

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
constexpr size_t CommonHeader::kHeaderSizeBytes;
// 0 1 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 0 |V=2|P| C/F |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 1 | Packet Type |
// ----------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 2 | length |
// --------------------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Common header for all RTCP packets, 4 octets.
bool CommonHeader::Parse(const uint8_t* buffer, size_t size_bytes) {
const uint8_t kVersion = 2;
if (size_bytes < kHeaderSizeBytes) {
RTC_LOG(LS_WARNING)
<< "Too little data (" << size_bytes << " byte"
<< (size_bytes != 1 ? "s" : "")
<< ") remaining in buffer to parse RTCP header (4 bytes).";
return false;
}
uint8_t version = buffer[0] >> 6;
if (version != kVersion) {
RTC_LOG(LS_WARNING) << "Invalid RTCP header: Version must be "
<< static_cast<int>(kVersion) << " but was "
<< static_cast<int>(version);
return false;
}
bool has_padding = (buffer[0] & 0x20) != 0;
count_or_format_ = buffer[0] & 0x1F;
packet_type_ = buffer[1];
payload_size_ = ByteReader<uint16_t>::ReadBigEndian(&buffer[2]) * 4;
payload_ = buffer + kHeaderSizeBytes;
padding_size_ = 0;
if (size_bytes < kHeaderSizeBytes + payload_size_) {
RTC_LOG(LS_WARNING) << "Buffer too small (" << size_bytes
<< " bytes) to fit an RtcpPacket with a header and "
<< payload_size_ << " bytes.";
return false;
}
if (has_padding) {
if (payload_size_ == 0) {
RTC_LOG(LS_WARNING)
<< "Invalid RTCP header: Padding bit set but 0 payload "
"size specified.";
return false;
}
padding_size_ = payload_[payload_size_ - 1];
if (padding_size_ == 0) {
RTC_LOG(LS_WARNING)
<< "Invalid RTCP header: Padding bit set but 0 padding "
"size specified.";
return false;
}
if (padding_size_ > payload_size_) {
RTC_LOG(LS_WARNING) << "Invalid RTCP header: Too many padding bytes ("
<< padding_size_ << ") for a packet payload size of "
<< payload_size_ << " bytes.";
return false;
}
payload_size_ -= padding_size_;
}
return true;
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMMON_HEADER_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMMON_HEADER_H_
#include <stddef.h>
#include <stdint.h>
namespace webrtc {
namespace rtcp {
class CommonHeader {
public:
static constexpr size_t kHeaderSizeBytes = 4;
CommonHeader() {}
CommonHeader(const CommonHeader&) = default;
CommonHeader& operator=(const CommonHeader&) = default;
bool Parse(const uint8_t* buffer, size_t size_bytes);
uint8_t type() const { return packet_type_; }
// Depending on packet type same header field can be used either as count or
// as feedback message type (fmt). Caller expected to know how it is used.
uint8_t fmt() const { return count_or_format_; }
uint8_t count() const { return count_or_format_; }
size_t payload_size_bytes() const { return payload_size_; }
const uint8_t* payload() const { return payload_; }
size_t packet_size() const {
return kHeaderSizeBytes + payload_size_ + padding_size_;
}
// Returns pointer to the next RTCP packet in compound packet.
const uint8_t* NextPacket() const {
return payload_ + payload_size_ + padding_size_;
}
private:
uint8_t packet_type_ = 0;
uint8_t count_or_format_ = 0;
uint8_t padding_size_ = 0;
uint32_t payload_size_ = 0;
const uint8_t* payload_ = nullptr;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMMON_HEADER_H_

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/source/rtcp_packet/compound_packet.h"
#include <memory>
#include <utility>
#include "rtc_base/checks.h"
namespace webrtc {
namespace rtcp {
CompoundPacket::CompoundPacket() = default;
CompoundPacket::~CompoundPacket() = default;
void CompoundPacket::Append(std::unique_ptr<RtcpPacket> packet) {
RTC_CHECK(packet);
appended_packets_.push_back(std::move(packet));
}
bool CompoundPacket::Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const {
for (const auto& appended : appended_packets_) {
if (!appended->Create(packet, index, max_length, callback))
return false;
}
return true;
}
size_t CompoundPacket::BlockLength() const {
size_t block_length = 0;
for (const auto& appended : appended_packets_) {
block_length += appended->BlockLength();
}
return block_length;
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMPOUND_PACKET_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMPOUND_PACKET_H_
#include <memory>
#include <vector>
#include "modules/rtp_rtcp/source/rtcp_packet.h"
namespace webrtc {
namespace rtcp {
class CompoundPacket : public RtcpPacket {
public:
CompoundPacket();
~CompoundPacket() override;
CompoundPacket(const CompoundPacket&) = delete;
CompoundPacket& operator=(const CompoundPacket&) = delete;
void Append(std::unique_ptr<RtcpPacket> packet);
// Size of this packet in bytes (i.e. total size of nested packets).
size_t BlockLength() const override;
// Returns true if all calls to Create succeeded.
bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const override;
protected:
std::vector<std::unique_ptr<RtcpPacket>> appended_packets_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMPOUND_PACKET_H_

View file

@ -0,0 +1,94 @@
/*
* 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.
*/
#include "modules/rtp_rtcp/source/rtcp_packet/dlrr.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
namespace rtcp {
// DLRR Report Block (RFC 3611).
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT=5 | reserved | block length |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// | SSRC_1 (SSRC of first receiver) | sub-
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
// | last RR (LRR) | 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | delay since last RR (DLRR) |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// | SSRC_2 (SSRC of second receiver) | sub-
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
// : ... : 2
Dlrr::Dlrr() = default;
Dlrr::Dlrr(const Dlrr& other) = default;
Dlrr::~Dlrr() = default;
bool Dlrr::Parse(const uint8_t* buffer, uint16_t block_length_32bits) {
RTC_DCHECK(buffer[0] == kBlockType);
// kReserved = buffer[1];
RTC_DCHECK_EQ(block_length_32bits,
ByteReader<uint16_t>::ReadBigEndian(&buffer[2]));
if (block_length_32bits % 3 != 0) {
RTC_LOG(LS_WARNING) << "Invalid size for dlrr block.";
return false;
}
size_t blocks_count = block_length_32bits / 3;
const uint8_t* read_at = buffer + kBlockHeaderLength;
sub_blocks_.resize(blocks_count);
for (ReceiveTimeInfo& sub_block : sub_blocks_) {
sub_block.ssrc = ByteReader<uint32_t>::ReadBigEndian(&read_at[0]);
sub_block.last_rr = ByteReader<uint32_t>::ReadBigEndian(&read_at[4]);
sub_block.delay_since_last_rr =
ByteReader<uint32_t>::ReadBigEndian(&read_at[8]);
read_at += kSubBlockLength;
}
return true;
}
size_t Dlrr::BlockLength() const {
if (sub_blocks_.empty())
return 0;
return kBlockHeaderLength + kSubBlockLength * sub_blocks_.size();
}
void Dlrr::Create(uint8_t* buffer) const {
if (sub_blocks_.empty()) // No subblocks, no need to write header either.
return;
// Create block header.
const uint8_t kReserved = 0;
buffer[0] = kBlockType;
buffer[1] = kReserved;
ByteWriter<uint16_t>::WriteBigEndian(
&buffer[2], rtc::dchecked_cast<uint16_t>(3 * sub_blocks_.size()));
// Create sub blocks.
uint8_t* write_at = buffer + kBlockHeaderLength;
for (const ReceiveTimeInfo& sub_block : sub_blocks_) {
ByteWriter<uint32_t>::WriteBigEndian(&write_at[0], sub_block.ssrc);
ByteWriter<uint32_t>::WriteBigEndian(&write_at[4], sub_block.last_rr);
ByteWriter<uint32_t>::WriteBigEndian(&write_at[8],
sub_block.delay_since_last_rr);
write_at += kSubBlockLength;
}
RTC_DCHECK_EQ(buffer + BlockLength(), write_at);
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,80 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_DLRR_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_DLRR_H_
#include <stddef.h>
#include <stdint.h>
#include <vector>
namespace webrtc {
namespace rtcp {
struct ReceiveTimeInfo {
// RFC 3611 4.5
ReceiveTimeInfo() : ssrc(0), last_rr(0), delay_since_last_rr(0) {}
ReceiveTimeInfo(uint32_t ssrc, uint32_t last_rr, uint32_t delay)
: ssrc(ssrc), last_rr(last_rr), delay_since_last_rr(delay) {}
uint32_t ssrc;
uint32_t last_rr;
uint32_t delay_since_last_rr;
};
inline bool operator==(const ReceiveTimeInfo& lhs, const ReceiveTimeInfo& rhs) {
return lhs.ssrc == rhs.ssrc && lhs.last_rr == rhs.last_rr &&
lhs.delay_since_last_rr == rhs.delay_since_last_rr;
}
inline bool operator!=(const ReceiveTimeInfo& lhs, const ReceiveTimeInfo& rhs) {
return !(lhs == rhs);
}
// DLRR Report Block: Delay since the Last Receiver Report (RFC 3611).
class Dlrr {
public:
static const uint8_t kBlockType = 5;
Dlrr();
Dlrr(const Dlrr& other);
~Dlrr();
Dlrr& operator=(const Dlrr& other) = default;
// Dlrr without items treated same as no dlrr block.
explicit operator bool() const { return !sub_blocks_.empty(); }
// Second parameter is value read from block header,
// i.e. size of block in 32bits excluding block header itself.
bool Parse(const uint8_t* buffer, uint16_t block_length_32bits);
size_t BlockLength() const;
// Fills buffer with the Dlrr.
// Consumes BlockLength() bytes.
void Create(uint8_t* buffer) const;
void ClearItems() { sub_blocks_.clear(); }
void AddDlrrItem(const ReceiveTimeInfo& time_info) {
sub_blocks_.push_back(time_info);
}
const std::vector<ReceiveTimeInfo>& sub_blocks() const { return sub_blocks_; }
private:
static const size_t kBlockHeaderLength = 4;
static const size_t kSubBlockLength = 12;
std::vector<ReceiveTimeInfo> sub_blocks_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_DLRR_H_

View file

@ -0,0 +1,195 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h"
#include <vector>
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
constexpr uint8_t ExtendedReports::kPacketType;
constexpr size_t ExtendedReports::kMaxNumberOfDlrrItems;
// From RFC 3611: RTP Control Protocol Extended Reports (RTCP XR).
//
// Format for XR packets:
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P|reserved | PT=XR=207 | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : report blocks :
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Extended report block:
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Block Type | reserved | block length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : type-specific block contents :
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
ExtendedReports::ExtendedReports() = default;
ExtendedReports::ExtendedReports(const ExtendedReports& xr) = default;
ExtendedReports::~ExtendedReports() = default;
bool ExtendedReports::Parse(const CommonHeader& packet) {
RTC_DCHECK_EQ(packet.type(), kPacketType);
if (packet.payload_size_bytes() < kXrBaseLength) {
RTC_LOG(LS_WARNING)
<< "Packet is too small to be an ExtendedReports packet.";
return false;
}
SetSenderSsrc(ByteReader<uint32_t>::ReadBigEndian(packet.payload()));
rrtr_block_.reset();
dlrr_block_.ClearItems();
target_bitrate_ = absl::nullopt;
const uint8_t* current_block = packet.payload() + kXrBaseLength;
const uint8_t* const packet_end =
packet.payload() + packet.payload_size_bytes();
constexpr size_t kBlockHeaderSizeBytes = 4;
while (current_block + kBlockHeaderSizeBytes <= packet_end) {
uint8_t block_type = ByteReader<uint8_t>::ReadBigEndian(current_block);
uint16_t block_length =
ByteReader<uint16_t>::ReadBigEndian(current_block + 2);
const uint8_t* next_block =
current_block + kBlockHeaderSizeBytes + block_length * 4;
if (next_block > packet_end) {
RTC_LOG(LS_WARNING)
<< "Report block in extended report packet is too big.";
return false;
}
switch (block_type) {
case Rrtr::kBlockType:
ParseRrtrBlock(current_block, block_length);
break;
case Dlrr::kBlockType:
ParseDlrrBlock(current_block, block_length);
break;
case TargetBitrate::kBlockType:
ParseTargetBitrateBlock(current_block, block_length);
break;
default:
// Unknown block, ignore.
RTC_LOG(LS_WARNING)
<< "Unknown extended report block type " << block_type;
break;
}
current_block = next_block;
}
return true;
}
void ExtendedReports::SetRrtr(const Rrtr& rrtr) {
if (rrtr_block_)
RTC_LOG(LS_WARNING) << "Rrtr already set, overwriting.";
rrtr_block_.emplace(rrtr);
}
bool ExtendedReports::AddDlrrItem(const ReceiveTimeInfo& time_info) {
if (dlrr_block_.sub_blocks().size() >= kMaxNumberOfDlrrItems) {
RTC_LOG(LS_WARNING) << "Reached maximum number of DLRR items.";
return false;
}
dlrr_block_.AddDlrrItem(time_info);
return true;
}
void ExtendedReports::SetTargetBitrate(const TargetBitrate& bitrate) {
if (target_bitrate_)
RTC_LOG(LS_WARNING) << "TargetBitrate already set, overwriting.";
target_bitrate_ = bitrate;
}
size_t ExtendedReports::BlockLength() const {
return kHeaderLength + kXrBaseLength + RrtrLength() + DlrrLength() +
TargetBitrateLength();
}
bool ExtendedReports::Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const {
while (*index + BlockLength() > max_length) {
if (!OnBufferFull(packet, index, callback))
return false;
}
size_t index_end = *index + BlockLength();
const uint8_t kReserved = 0;
CreateHeader(kReserved, kPacketType, HeaderLength(), packet, index);
ByteWriter<uint32_t>::WriteBigEndian(packet + *index, sender_ssrc());
*index += sizeof(uint32_t);
if (rrtr_block_) {
rrtr_block_->Create(packet + *index);
*index += Rrtr::kLength;
}
if (dlrr_block_) {
dlrr_block_.Create(packet + *index);
*index += dlrr_block_.BlockLength();
}
if (target_bitrate_) {
target_bitrate_->Create(packet + *index);
*index += target_bitrate_->BlockLength();
}
RTC_CHECK_EQ(*index, index_end);
return true;
}
size_t ExtendedReports::TargetBitrateLength() const {
if (target_bitrate_)
return target_bitrate_->BlockLength();
return 0;
}
void ExtendedReports::ParseRrtrBlock(const uint8_t* block,
uint16_t block_length) {
if (block_length != Rrtr::kBlockLength) {
RTC_LOG(LS_WARNING) << "Incorrect rrtr block size " << block_length
<< " Should be " << Rrtr::kBlockLength;
return;
}
if (rrtr_block_) {
RTC_LOG(LS_WARNING)
<< "Two rrtr blocks found in same Extended Report packet";
return;
}
rrtr_block_.emplace();
rrtr_block_->Parse(block);
}
void ExtendedReports::ParseDlrrBlock(const uint8_t* block,
uint16_t block_length) {
if (dlrr_block_) {
RTC_LOG(LS_WARNING)
<< "Two Dlrr blocks found in same Extended Report packet";
return;
}
dlrr_block_.Parse(block, block_length);
}
void ExtendedReports::ParseTargetBitrateBlock(const uint8_t* block,
uint16_t block_length) {
target_bitrate_.emplace();
target_bitrate_->Parse(block, block_length);
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_EXTENDED_REPORTS_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_EXTENDED_REPORTS_H_
#include <vector>
#include "absl/types/optional.h"
#include "modules/rtp_rtcp/source/rtcp_packet.h"
#include "modules/rtp_rtcp/source/rtcp_packet/dlrr.h"
#include "modules/rtp_rtcp/source/rtcp_packet/rrtr.h"
#include "modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h"
namespace webrtc {
namespace rtcp {
class CommonHeader;
// From RFC 3611: RTP Control Protocol Extended Reports (RTCP XR).
class ExtendedReports : public RtcpPacket {
public:
static constexpr uint8_t kPacketType = 207;
static constexpr size_t kMaxNumberOfDlrrItems = 50;
ExtendedReports();
ExtendedReports(const ExtendedReports& xr);
~ExtendedReports() override;
// Parse assumes header is already parsed and validated.
bool Parse(const CommonHeader& packet);
void SetRrtr(const Rrtr& rrtr);
bool AddDlrrItem(const ReceiveTimeInfo& time_info);
void SetTargetBitrate(const TargetBitrate& target_bitrate);
const absl::optional<Rrtr>& rrtr() const { return rrtr_block_; }
const Dlrr& dlrr() const { return dlrr_block_; }
const absl::optional<TargetBitrate>& target_bitrate() const {
return target_bitrate_;
}
size_t BlockLength() const override;
bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const override;
private:
static constexpr size_t kXrBaseLength = 4;
size_t RrtrLength() const { return rrtr_block_ ? Rrtr::kLength : 0; }
size_t DlrrLength() const { return dlrr_block_.BlockLength(); }
size_t TargetBitrateLength() const;
void ParseRrtrBlock(const uint8_t* block, uint16_t block_length);
void ParseDlrrBlock(const uint8_t* block, uint16_t block_length);
void ParseTargetBitrateBlock(const uint8_t* block, uint16_t block_length);
absl::optional<Rrtr> rrtr_block_;
Dlrr dlrr_block_; // Dlrr without items treated same as no dlrr block.
absl::optional<TargetBitrate> target_bitrate_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_EXTENDED_REPORTS_H_

View file

@ -0,0 +1,113 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/source/rtcp_packet/fir.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
constexpr uint8_t Fir::kFeedbackMessageType;
// RFC 4585: Feedback format.
// Common packet format:
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| FMT | PT | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of packet sender |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of media source (unused) = 0 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : Feedback Control Information (FCI) :
// : :
// Full intra request (FIR) (RFC 5104).
// The Feedback Control Information (FCI) for the Full Intra Request
// consists of one or more FCI entries.
// FCI:
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Seq nr. | Reserved = 0 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Fir::Fir() = default;
Fir::Fir(const Fir& fir) = default;
Fir::~Fir() = default;
bool Fir::Parse(const CommonHeader& packet) {
RTC_DCHECK_EQ(packet.type(), kPacketType);
RTC_DCHECK_EQ(packet.fmt(), kFeedbackMessageType);
// The FCI field MUST contain one or more FIR entries.
if (packet.payload_size_bytes() < kCommonFeedbackLength + kFciLength) {
RTC_LOG(LS_WARNING) << "Packet is too small to be a valid FIR packet.";
return false;
}
if ((packet.payload_size_bytes() - kCommonFeedbackLength) % kFciLength != 0) {
RTC_LOG(LS_WARNING) << "Invalid size for a valid FIR packet.";
return false;
}
ParseCommonFeedback(packet.payload());
size_t number_of_fci_items =
(packet.payload_size_bytes() - kCommonFeedbackLength) / kFciLength;
const uint8_t* next_fci = packet.payload() + kCommonFeedbackLength;
items_.resize(number_of_fci_items);
for (Request& request : items_) {
request.ssrc = ByteReader<uint32_t>::ReadBigEndian(next_fci);
request.seq_nr = ByteReader<uint8_t>::ReadBigEndian(next_fci + 4);
next_fci += kFciLength;
}
return true;
}
size_t Fir::BlockLength() const {
return kHeaderLength + kCommonFeedbackLength + kFciLength * items_.size();
}
bool Fir::Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const {
RTC_DCHECK(!items_.empty());
while (*index + BlockLength() > max_length) {
if (!OnBufferFull(packet, index, callback))
return false;
}
size_t index_end = *index + BlockLength();
CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), packet,
index);
RTC_DCHECK_EQ(Psfb::media_ssrc(), 0);
CreateCommonFeedback(packet + *index);
*index += kCommonFeedbackLength;
constexpr uint32_t kReserved = 0;
for (const Request& request : items_) {
ByteWriter<uint32_t>::WriteBigEndian(packet + *index, request.ssrc);
ByteWriter<uint8_t>::WriteBigEndian(packet + *index + 4, request.seq_nr);
ByteWriter<uint32_t, 3>::WriteBigEndian(packet + *index + 5, kReserved);
*index += kFciLength;
}
RTC_CHECK_EQ(*index, index_end);
return true;
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_FIR_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_FIR_H_
#include <vector>
#include "modules/rtp_rtcp/source/rtcp_packet/psfb.h"
namespace webrtc {
namespace rtcp {
class CommonHeader;
// Full intra request (FIR) (RFC 5104).
class Fir : public Psfb {
public:
static constexpr uint8_t kFeedbackMessageType = 4;
struct Request {
Request() : ssrc(0), seq_nr(0) {}
Request(uint32_t ssrc, uint8_t seq_nr) : ssrc(ssrc), seq_nr(seq_nr) {}
uint32_t ssrc;
uint8_t seq_nr;
};
Fir();
Fir(const Fir& fir);
~Fir() override;
// Parse assumes header is already parsed and validated.
bool Parse(const CommonHeader& packet);
void AddRequestTo(uint32_t ssrc, uint8_t seq_num) {
items_.emplace_back(ssrc, seq_num);
}
const std::vector<Request>& requests() const { return items_; }
size_t BlockLength() const override;
bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const override;
private:
static constexpr size_t kFciLength = 8;
// SSRC of media source is not used in FIR packet. Shadow base functions.
void SetMediaSsrc(uint32_t ssrc);
uint32_t media_ssrc() const;
std::vector<Request> items_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_FIR_H_

View file

@ -0,0 +1,133 @@
/*
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/rtp_rtcp/source/rtcp_packet/loss_notification.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
// Loss Notification
// -----------------
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| FMT=15 | PT=206 | length |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// 0 | SSRC of packet sender |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 4 | SSRC of media source |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 8 | Unique identifier 'L' 'N' 'T' 'F' |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 12 | Last Decoded Sequence Number | Last Received SeqNum Delta |D|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
LossNotification::LossNotification()
: last_decoded_(0), last_received_(0), decodability_flag_(false) {}
LossNotification::LossNotification(uint16_t last_decoded,
uint16_t last_received,
bool decodability_flag)
: last_decoded_(last_decoded),
last_received_(last_received),
decodability_flag_(decodability_flag) {}
LossNotification::LossNotification(const LossNotification& rhs) = default;
LossNotification::~LossNotification() = default;
size_t LossNotification::BlockLength() const {
return kHeaderLength + kCommonFeedbackLength + kLossNotificationPayloadLength;
}
bool LossNotification::Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const {
while (*index + BlockLength() > max_length) {
if (!OnBufferFull(packet, index, callback))
return false;
}
const size_t index_end = *index + BlockLength();
// Note: `index` updated by the function below.
CreateHeader(Psfb::kAfbMessageType, kPacketType, HeaderLength(), packet,
index);
CreateCommonFeedback(packet + *index);
*index += kCommonFeedbackLength;
ByteWriter<uint32_t>::WriteBigEndian(packet + *index, kUniqueIdentifier);
*index += sizeof(uint32_t);
ByteWriter<uint16_t>::WriteBigEndian(packet + *index, last_decoded_);
*index += sizeof(uint16_t);
const uint16_t last_received_delta = last_received_ - last_decoded_;
RTC_DCHECK_LE(last_received_delta, 0x7fff);
const uint16_t last_received_delta_and_decodability =
(last_received_delta << 1) | (decodability_flag_ ? 0x0001 : 0x0000);
ByteWriter<uint16_t>::WriteBigEndian(packet + *index,
last_received_delta_and_decodability);
*index += sizeof(uint16_t);
RTC_DCHECK_EQ(index_end, *index);
return true;
}
bool LossNotification::Parse(const CommonHeader& packet) {
RTC_DCHECK_EQ(packet.type(), kPacketType);
RTC_DCHECK_EQ(packet.fmt(), Psfb::kAfbMessageType);
if (packet.payload_size_bytes() <
kCommonFeedbackLength + kLossNotificationPayloadLength) {
return false;
}
const uint8_t* const payload = packet.payload();
if (ByteReader<uint32_t>::ReadBigEndian(&payload[8]) != kUniqueIdentifier) {
return false;
}
ParseCommonFeedback(payload);
last_decoded_ = ByteReader<uint16_t>::ReadBigEndian(&payload[12]);
const uint16_t last_received_delta_and_decodability =
ByteReader<uint16_t>::ReadBigEndian(&payload[14]);
last_received_ = last_decoded_ + (last_received_delta_and_decodability >> 1);
decodability_flag_ = (last_received_delta_and_decodability & 0x0001);
return true;
}
bool LossNotification::Set(uint16_t last_decoded,
uint16_t last_received,
bool decodability_flag) {
const uint16_t delta = last_received - last_decoded;
if (delta > 0x7fff) {
return false;
}
last_received_ = last_received;
last_decoded_ = last_decoded;
decodability_flag_ = decodability_flag;
return true;
}
} // namespace rtcp
} // namespace webrtc

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.
*/
#ifndef MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_LOSS_NOTIFICATION_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_LOSS_NOTIFICATION_H_
#include "absl/base/attributes.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "modules/rtp_rtcp/source/rtcp_packet/psfb.h"
namespace webrtc {
namespace rtcp {
class LossNotification : public Psfb {
public:
LossNotification();
LossNotification(uint16_t last_decoded,
uint16_t last_received,
bool decodability_flag);
LossNotification(const LossNotification& other);
~LossNotification() override;
size_t BlockLength() const override;
ABSL_MUST_USE_RESULT
bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const override;
// Parse assumes header is already parsed and validated.
ABSL_MUST_USE_RESULT
bool Parse(const CommonHeader& packet);
// Set all of the values transmitted by the loss notification message.
// If the values may not be represented by a loss notification message,
// false is returned, and no change is made to the object; this happens
// when `last_received` is ahead of `last_decoded` by more than 0x7fff.
// This is because `last_received` is represented on the wire as a delta,
// and only 15 bits are available for that delta.
ABSL_MUST_USE_RESULT
bool Set(uint16_t last_decoded,
uint16_t last_received,
bool decodability_flag);
// RTP sequence number of the first packet belong to the last decoded
// non-discardable frame.
uint16_t last_decoded() const { return last_decoded_; }
// RTP sequence number of the last received packet.
uint16_t last_received() const { return last_received_; }
// A decodability flag, whose specific meaning depends on the last-received
// RTP sequence number. The decodability flag is true if and only if all of
// the frame's dependencies are known to be decodable, and the frame itself
// is not yet known to be unassemblable.
// * Clarification #1: In a multi-packet frame, the first packet's
// dependencies are known, but it is not yet known whether all parts
// of the current frame will be received.
// * Clarification #2: In a multi-packet frame, the dependencies would be
// unknown if the first packet was not received. Then, the packet will
// be known-unassemblable.
bool decodability_flag() const { return decodability_flag_; }
private:
static constexpr uint32_t kUniqueIdentifier = 0x4C4E5446; // 'L' 'N' 'T' 'F'.
static constexpr size_t kLossNotificationPayloadLength = 8;
uint16_t last_decoded_;
uint16_t last_received_;
bool decodability_flag_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_LOSS_NOTIFICATION_H_

View file

@ -0,0 +1,176 @@
/*
* 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.
*/
#include "modules/rtp_rtcp/source/rtcp_packet/nack.h"
#include <algorithm>
#include <cstdint>
#include <utility>
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
constexpr uint8_t Nack::kFeedbackMessageType;
constexpr size_t Nack::kNackItemLength;
// RFC 4585: Feedback format.
//
// Common packet format:
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| FMT | PT | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 0 | SSRC of packet sender |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 4 | SSRC of media source |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : Feedback Control Information (FCI) :
// : :
//
// Generic NACK (RFC 4585).
//
// FCI:
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | PID | BLP |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Nack::Nack() = default;
Nack::Nack(const Nack& rhs) = default;
Nack::~Nack() = default;
bool Nack::Parse(const CommonHeader& packet) {
RTC_DCHECK_EQ(packet.type(), kPacketType);
RTC_DCHECK_EQ(packet.fmt(), kFeedbackMessageType);
if (packet.payload_size_bytes() < kCommonFeedbackLength + kNackItemLength) {
RTC_LOG(LS_WARNING) << "Payload length " << packet.payload_size_bytes()
<< " is too small for a Nack.";
return false;
}
size_t nack_items =
(packet.payload_size_bytes() - kCommonFeedbackLength) / kNackItemLength;
ParseCommonFeedback(packet.payload());
const uint8_t* next_nack = packet.payload() + kCommonFeedbackLength;
packet_ids_.clear();
packed_.resize(nack_items);
for (size_t index = 0; index < nack_items; ++index) {
packed_[index].first_pid = ByteReader<uint16_t>::ReadBigEndian(next_nack);
packed_[index].bitmask = ByteReader<uint16_t>::ReadBigEndian(next_nack + 2);
next_nack += kNackItemLength;
}
Unpack();
return true;
}
size_t Nack::BlockLength() const {
return kHeaderLength + kCommonFeedbackLength +
packed_.size() * kNackItemLength;
}
bool Nack::Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const {
RTC_DCHECK(!packed_.empty());
// If nack list can't fit in packet, try to fragment.
constexpr size_t kNackHeaderLength = kHeaderLength + kCommonFeedbackLength;
for (size_t nack_index = 0; nack_index < packed_.size();) {
size_t bytes_left_in_buffer = max_length - *index;
if (bytes_left_in_buffer < kNackHeaderLength + kNackItemLength) {
if (!OnBufferFull(packet, index, callback))
return false;
continue;
}
size_t num_nack_fields =
std::min((bytes_left_in_buffer - kNackHeaderLength) / kNackItemLength,
packed_.size() - nack_index);
size_t payload_size_bytes =
kCommonFeedbackLength + (num_nack_fields * kNackItemLength);
size_t payload_size_32bits =
rtc::CheckedDivExact<size_t>(payload_size_bytes, 4);
CreateHeader(kFeedbackMessageType, kPacketType, payload_size_32bits, packet,
index);
CreateCommonFeedback(packet + *index);
*index += kCommonFeedbackLength;
size_t nack_end_index = nack_index + num_nack_fields;
for (; nack_index < nack_end_index; ++nack_index) {
const PackedNack& item = packed_[nack_index];
ByteWriter<uint16_t>::WriteBigEndian(packet + *index + 0, item.first_pid);
ByteWriter<uint16_t>::WriteBigEndian(packet + *index + 2, item.bitmask);
*index += kNackItemLength;
}
RTC_DCHECK_LE(*index, max_length);
}
return true;
}
void Nack::SetPacketIds(const uint16_t* nack_list, size_t length) {
RTC_DCHECK(nack_list);
SetPacketIds(std::vector<uint16_t>(nack_list, nack_list + length));
}
void Nack::SetPacketIds(std::vector<uint16_t> nack_list) {
RTC_DCHECK(packet_ids_.empty());
RTC_DCHECK(packed_.empty());
packet_ids_ = std::move(nack_list);
Pack();
}
void Nack::Pack() {
RTC_DCHECK(!packet_ids_.empty());
RTC_DCHECK(packed_.empty());
auto it = packet_ids_.begin();
const auto end = packet_ids_.end();
while (it != end) {
PackedNack item;
item.first_pid = *it++;
// Bitmask specifies losses in any of the 16 packets following the pid.
item.bitmask = 0;
while (it != end) {
uint16_t shift = static_cast<uint16_t>(*it - item.first_pid - 1);
if (shift <= 15) {
item.bitmask |= (1 << shift);
++it;
} else {
break;
}
}
packed_.push_back(item);
}
}
void Nack::Unpack() {
RTC_DCHECK(packet_ids_.empty());
RTC_DCHECK(!packed_.empty());
for (const PackedNack& item : packed_) {
packet_ids_.push_back(item.first_pid);
uint16_t pid = item.first_pid + 1;
for (uint16_t bitmask = item.bitmask; bitmask != 0; bitmask >>= 1, ++pid) {
if (bitmask & 1)
packet_ids_.push_back(pid);
}
}
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,59 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_NACK_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_NACK_H_
#include <vector>
#include "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h"
namespace webrtc {
namespace rtcp {
class CommonHeader;
class Nack : public Rtpfb {
public:
static constexpr uint8_t kFeedbackMessageType = 1;
Nack();
Nack(const Nack&);
~Nack() override;
// Parse assumes header is already parsed and validated.
bool Parse(const CommonHeader& packet);
void SetPacketIds(const uint16_t* nack_list, size_t length);
void SetPacketIds(std::vector<uint16_t> nack_list);
const std::vector<uint16_t>& packet_ids() const { return packet_ids_; }
size_t BlockLength() const override;
bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const override;
private:
static constexpr size_t kNackItemLength = 4;
struct PackedNack {
uint16_t first_pid;
uint16_t bitmask;
};
void Pack(); // Fills packed_ using packed_ids_. (used in SetPacketIds).
void Unpack(); // Fills packet_ids_ using packed_. (used in Parse).
std::vector<PackedNack> packed_;
std::vector<uint16_t> packet_ids_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_NACK_H_

View file

@ -0,0 +1,79 @@
/*
* 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.
*/
#include "modules/rtp_rtcp/source/rtcp_packet/pli.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
constexpr uint8_t Pli::kFeedbackMessageType;
// RFC 4585: Feedback format.
//
// Common packet format:
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| FMT | PT | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of packet sender |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of media source |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : Feedback Control Information (FCI) :
// : :
Pli::Pli() = default;
Pli::Pli(const Pli& pli) = default;
Pli::~Pli() = default;
//
// Picture loss indication (PLI) (RFC 4585).
// FCI: no feedback control information.
bool Pli::Parse(const CommonHeader& packet) {
RTC_DCHECK_EQ(packet.type(), kPacketType);
RTC_DCHECK_EQ(packet.fmt(), kFeedbackMessageType);
if (packet.payload_size_bytes() < kCommonFeedbackLength) {
RTC_LOG(LS_WARNING) << "Packet is too small to be a valid PLI packet";
return false;
}
ParseCommonFeedback(packet.payload());
return true;
}
size_t Pli::BlockLength() const {
return kHeaderLength + kCommonFeedbackLength;
}
bool Pli::Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const {
while (*index + BlockLength() > max_length) {
if (!OnBufferFull(packet, index, callback))
return false;
}
CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), packet,
index);
CreateCommonFeedback(packet + *index);
*index += kCommonFeedbackLength;
return true;
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,39 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_PLI_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_PLI_H_
#include "modules/rtp_rtcp/source/rtcp_packet/psfb.h"
namespace webrtc {
namespace rtcp {
class CommonHeader;
// Picture loss indication (PLI) (RFC 4585).
class Pli : public Psfb {
public:
static constexpr uint8_t kFeedbackMessageType = 1;
Pli();
Pli(const Pli& pli);
~Pli() override;
bool Parse(const CommonHeader& packet);
size_t BlockLength() const override;
bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const override;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_PLI_H_

View file

@ -0,0 +1,47 @@
/*
* 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.
*/
#include "modules/rtp_rtcp/source/rtcp_packet/psfb.h"
#include "modules/rtp_rtcp/source/byte_io.h"
namespace webrtc {
namespace rtcp {
constexpr uint8_t Psfb::kPacketType;
constexpr uint8_t Psfb::kAfbMessageType;
constexpr size_t Psfb::kCommonFeedbackLength;
// RFC 4585: Feedback format.
//
// Common packet format:
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| FMT | PT | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 0 | SSRC of packet sender |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 4 | SSRC of media source |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : Feedback Control Information (FCI) :
// : :
void Psfb::ParseCommonFeedback(const uint8_t* payload) {
SetSenderSsrc(ByteReader<uint32_t>::ReadBigEndian(&payload[0]));
SetMediaSsrc(ByteReader<uint32_t>::ReadBigEndian(&payload[4]));
}
void Psfb::CreateCommonFeedback(uint8_t* payload) const {
ByteWriter<uint32_t>::WriteBigEndian(&payload[0], sender_ssrc());
ByteWriter<uint32_t>::WriteBigEndian(&payload[4], media_ssrc());
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,48 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_PSFB_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_PSFB_H_
#include <stddef.h>
#include <stdint.h>
#include "modules/rtp_rtcp/source/rtcp_packet.h"
namespace webrtc {
namespace rtcp {
// PSFB: Payload-specific feedback message.
// RFC 4585, Section 6.3.
class Psfb : public RtcpPacket {
public:
static constexpr uint8_t kPacketType = 206;
static constexpr uint8_t kAfbMessageType = 15;
Psfb() = default;
~Psfb() override = default;
void SetMediaSsrc(uint32_t ssrc) { media_ssrc_ = ssrc; }
uint32_t media_ssrc() const { return media_ssrc_; }
protected:
static constexpr size_t kCommonFeedbackLength = 8;
void ParseCommonFeedback(const uint8_t* payload);
void CreateCommonFeedback(uint8_t* payload) const;
private:
uint32_t media_ssrc_ = 0;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_PSFB_H_

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
constexpr uint8_t RapidResyncRequest::kFeedbackMessageType;
// RFC 4585: Feedback format.
// Rapid Resynchronisation Request (draft-perkins-avt-rapid-rtp-sync-03).
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| FMT=5 | PT=205 | length=2 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of packet sender |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of media source |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
bool RapidResyncRequest::Parse(const CommonHeader& packet) {
RTC_DCHECK_EQ(packet.type(), kPacketType);
RTC_DCHECK_EQ(packet.fmt(), kFeedbackMessageType);
if (packet.payload_size_bytes() != kCommonFeedbackLength) {
RTC_LOG(LS_WARNING) << "Packet payload size should be "
<< kCommonFeedbackLength << " instead of "
<< packet.payload_size_bytes()
<< " to be a valid Rapid Resynchronisation Request";
return false;
}
ParseCommonFeedback(packet.payload());
return true;
}
size_t RapidResyncRequest::BlockLength() const {
return kHeaderLength + kCommonFeedbackLength;
}
bool RapidResyncRequest::Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const {
while (*index + BlockLength() > max_length) {
if (!OnBufferFull(packet, index, callback))
return false;
}
CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), packet,
index);
CreateCommonFeedback(packet + *index);
*index += kCommonFeedbackLength;
return true;
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RAPID_RESYNC_REQUEST_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RAPID_RESYNC_REQUEST_H_
#include "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h"
namespace webrtc {
namespace rtcp {
class CommonHeader;
// draft-perkins-avt-rapid-rtp-sync-03
class RapidResyncRequest : public Rtpfb {
public:
static constexpr uint8_t kFeedbackMessageType = 5;
RapidResyncRequest() {}
~RapidResyncRequest() override {}
// Parse assumes header is already parsed and validated.
bool Parse(const CommonHeader& header);
size_t BlockLength() const override;
bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const override;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RAPID_RESYNC_REQUEST_H_

View file

@ -0,0 +1,112 @@
/*
* 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.
*/
#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h"
#include <utility>
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
constexpr uint8_t ReceiverReport::kPacketType;
constexpr size_t ReceiverReport::kMaxNumberOfReportBlocks;
// RTCP receiver report (RFC 3550).
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| RC | PT=RR=201 | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of packet sender |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// | report block(s) |
// | .... |
ReceiverReport::ReceiverReport() = default;
ReceiverReport::ReceiverReport(const ReceiverReport& rhs) = default;
ReceiverReport::~ReceiverReport() = default;
bool ReceiverReport::Parse(const CommonHeader& packet) {
RTC_DCHECK_EQ(packet.type(), kPacketType);
const uint8_t report_blocks_count = packet.count();
if (packet.payload_size_bytes() <
kRrBaseLength + report_blocks_count * ReportBlock::kLength) {
RTC_LOG(LS_WARNING) << "Packet is too small to contain all the data.";
return false;
}
SetSenderSsrc(ByteReader<uint32_t>::ReadBigEndian(packet.payload()));
const uint8_t* next_report_block = packet.payload() + kRrBaseLength;
report_blocks_.resize(report_blocks_count);
for (ReportBlock& block : report_blocks_) {
block.Parse(next_report_block, ReportBlock::kLength);
next_report_block += ReportBlock::kLength;
}
RTC_DCHECK_LE(next_report_block - packet.payload(),
static_cast<ptrdiff_t>(packet.payload_size_bytes()));
return true;
}
size_t ReceiverReport::BlockLength() const {
return kHeaderLength + kRrBaseLength +
report_blocks_.size() * ReportBlock::kLength;
}
bool ReceiverReport::Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const {
while (*index + BlockLength() > max_length) {
if (!OnBufferFull(packet, index, callback))
return false;
}
CreateHeader(report_blocks_.size(), kPacketType, HeaderLength(), packet,
index);
ByteWriter<uint32_t>::WriteBigEndian(packet + *index, sender_ssrc());
*index += kRrBaseLength;
for (const ReportBlock& block : report_blocks_) {
block.Create(packet + *index);
*index += ReportBlock::kLength;
}
return true;
}
bool ReceiverReport::AddReportBlock(const ReportBlock& block) {
if (report_blocks_.size() >= kMaxNumberOfReportBlocks) {
RTC_LOG(LS_WARNING) << "Max report blocks reached.";
return false;
}
report_blocks_.push_back(block);
return true;
}
bool ReceiverReport::SetReportBlocks(std::vector<ReportBlock> blocks) {
if (blocks.size() > kMaxNumberOfReportBlocks) {
RTC_LOG(LS_WARNING) << "Too many report blocks (" << blocks.size()
<< ") for receiver report.";
return false;
}
report_blocks_ = std::move(blocks);
return true;
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,60 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RECEIVER_REPORT_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RECEIVER_REPORT_H_
#include <stddef.h>
#include <stdint.h>
#include <vector>
#include "modules/rtp_rtcp/source/rtcp_packet.h"
#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
namespace webrtc {
namespace rtcp {
class CommonHeader;
class ReceiverReport : public RtcpPacket {
public:
static constexpr uint8_t kPacketType = 201;
static constexpr size_t kMaxNumberOfReportBlocks = 0x1f;
ReceiverReport();
ReceiverReport(const ReceiverReport&);
~ReceiverReport() override;
// Parse assumes header is already parsed and validated.
bool Parse(const CommonHeader& packet);
bool AddReportBlock(const ReportBlock& block);
bool SetReportBlocks(std::vector<ReportBlock> blocks);
const std::vector<ReportBlock>& report_blocks() const {
return report_blocks_;
}
size_t BlockLength() const override;
bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const override;
private:
static const size_t kRrBaseLength = 4;
std::vector<ReportBlock> report_blocks_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RECEIVER_REPORT_H_

View file

@ -0,0 +1,143 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/source/rtcp_packet/remb.h"
#include <cstdint>
#include <utility>
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
// Receiver Estimated Max Bitrate (REMB) (draft-alvestrand-rmcat-remb).
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| FMT=15 | PT=206 | length |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// 0 | SSRC of packet sender |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 4 | Unused = 0 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 8 | Unique identifier 'R' 'E' 'M' 'B' |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 12 | Num SSRC | BR Exp | BR Mantissa |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 16 | SSRC feedback |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : ... :
Remb::Remb() : bitrate_bps_(0) {}
Remb::Remb(const Remb& rhs) = default;
Remb::~Remb() = default;
bool Remb::Parse(const CommonHeader& packet) {
RTC_DCHECK(packet.type() == kPacketType);
RTC_DCHECK_EQ(packet.fmt(), Psfb::kAfbMessageType);
if (packet.payload_size_bytes() < 16) {
RTC_LOG(LS_WARNING) << "Payload length " << packet.payload_size_bytes()
<< " is too small for Remb packet.";
return false;
}
const uint8_t* const payload = packet.payload();
if (kUniqueIdentifier != ByteReader<uint32_t>::ReadBigEndian(&payload[8])) {
return false;
}
uint8_t number_of_ssrcs = payload[12];
if (packet.payload_size_bytes() !=
kCommonFeedbackLength + (2 + number_of_ssrcs) * 4) {
RTC_LOG(LS_WARNING) << "Payload size " << packet.payload_size_bytes()
<< " does not match " << number_of_ssrcs << " ssrcs.";
return false;
}
ParseCommonFeedback(payload);
uint8_t exponent = payload[13] >> 2;
uint64_t mantissa = (static_cast<uint32_t>(payload[13] & 0x03) << 16) |
ByteReader<uint16_t>::ReadBigEndian(&payload[14]);
bitrate_bps_ = (mantissa << exponent);
bool shift_overflow =
(static_cast<uint64_t>(bitrate_bps_) >> exponent) != mantissa;
if (bitrate_bps_ < 0 || shift_overflow) {
RTC_LOG(LS_ERROR) << "Invalid remb bitrate value : " << mantissa << "*2^"
<< static_cast<int>(exponent);
return false;
}
const uint8_t* next_ssrc = payload + 16;
ssrcs_.clear();
ssrcs_.reserve(number_of_ssrcs);
for (uint8_t i = 0; i < number_of_ssrcs; ++i) {
ssrcs_.push_back(ByteReader<uint32_t>::ReadBigEndian(next_ssrc));
next_ssrc += sizeof(uint32_t);
}
return true;
}
bool Remb::SetSsrcs(std::vector<uint32_t> ssrcs) {
if (ssrcs.size() > kMaxNumberOfSsrcs) {
RTC_LOG(LS_WARNING) << "Not enough space for all given SSRCs.";
return false;
}
ssrcs_ = std::move(ssrcs);
return true;
}
size_t Remb::BlockLength() const {
return kHeaderLength + kCommonFeedbackLength + (2 + ssrcs_.size()) * 4;
}
bool Remb::Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const {
while (*index + BlockLength() > max_length) {
if (!OnBufferFull(packet, index, callback))
return false;
}
size_t index_end = *index + BlockLength();
CreateHeader(Psfb::kAfbMessageType, kPacketType, HeaderLength(), packet,
index);
RTC_DCHECK_EQ(0, Psfb::media_ssrc());
CreateCommonFeedback(packet + *index);
*index += kCommonFeedbackLength;
ByteWriter<uint32_t>::WriteBigEndian(packet + *index, kUniqueIdentifier);
*index += sizeof(uint32_t);
const uint32_t kMaxMantissa = 0x3ffff; // 18 bits.
uint64_t mantissa = bitrate_bps_;
uint8_t exponenta = 0;
while (mantissa > kMaxMantissa) {
mantissa >>= 1;
++exponenta;
}
packet[(*index)++] = static_cast<uint8_t>(ssrcs_.size());
packet[(*index)++] = (exponenta << 2) | (mantissa >> 16);
ByteWriter<uint16_t>::WriteBigEndian(packet + *index, mantissa & 0xffff);
*index += sizeof(uint16_t);
for (uint32_t ssrc : ssrcs_) {
ByteWriter<uint32_t>::WriteBigEndian(packet + *index, ssrc);
*index += sizeof(uint32_t);
}
RTC_DCHECK_EQ(index_end, *index);
return true;
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REMB_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REMB_H_
#include <vector>
#include "modules/rtp_rtcp/source/rtcp_packet/psfb.h"
namespace webrtc {
namespace rtcp {
class CommonHeader;
// Receiver Estimated Max Bitrate (REMB) (draft-alvestrand-rmcat-remb).
class Remb : public Psfb {
public:
static constexpr size_t kMaxNumberOfSsrcs = 0xff;
Remb();
Remb(const Remb&);
~Remb() override;
// Parse assumes header is already parsed and validated.
bool Parse(const CommonHeader& packet);
bool SetSsrcs(std::vector<uint32_t> ssrcs);
void SetBitrateBps(int64_t bitrate_bps) { bitrate_bps_ = bitrate_bps; }
int64_t bitrate_bps() const { return bitrate_bps_; }
const std::vector<uint32_t>& ssrcs() const { return ssrcs_; }
size_t BlockLength() const override;
bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const override;
private:
static constexpr uint32_t kUniqueIdentifier = 0x52454D42; // 'R' 'E' 'M' 'B'.
// Media ssrc is unused, shadow base class setter and getter.
void SetMediaSsrc(uint32_t);
uint32_t media_ssrc() const;
int64_t bitrate_bps_;
std::vector<uint32_t> ssrcs_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REMB_H_

View file

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

View file

@ -0,0 +1,59 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REMOTE_ESTIMATE_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REMOTE_ESTIMATE_H_
#include <memory>
#include <vector>
#include "api/transport/network_types.h"
#include "modules/rtp_rtcp/source/rtcp_packet/app.h"
namespace webrtc {
namespace rtcp {
class CommonHeader;
class RemoteEstimateSerializer {
public:
virtual bool Parse(rtc::ArrayView<const uint8_t> src,
NetworkStateEstimate* target) const = 0;
virtual rtc::Buffer Serialize(const NetworkStateEstimate& src) const = 0;
virtual ~RemoteEstimateSerializer() = default;
};
// Using a static global implementation to avoid incurring initialization
// overhead of the serializer every time RemoteEstimate is created.
const RemoteEstimateSerializer* GetRemoteEstimateSerializer();
// The RemoteEstimate packet provides network estimation results from the
// receive side. This functionality is experimental and subject to change
// without notice.
class RemoteEstimate : public App {
public:
RemoteEstimate();
explicit RemoteEstimate(App&& app);
// Note, sub type must be unique among all app messages with "goog" name.
static constexpr uint8_t kSubType = 13;
static constexpr uint32_t kName = NameToInt("goog");
static TimeDelta GetTimestampPeriod();
bool ParseData();
void SetEstimate(NetworkStateEstimate estimate);
NetworkStateEstimate estimate() const { return estimate_; }
private:
NetworkStateEstimate estimate_;
const RemoteEstimateSerializer* const serializer_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REMOTE_ESTIMATE_H_

View file

@ -0,0 +1,91 @@
/*
* 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.
*/
#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
// From RFC 3550, RTP: A Transport Protocol for Real-Time Applications.
//
// RTCP report block (RFC 3550).
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// 0 | SSRC_1 (SSRC of first source) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 4 | fraction lost | cumulative number of packets lost |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 8 | extended highest sequence number received |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 12 | interarrival jitter |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 16 | last SR (LSR) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 20 | delay since last SR (DLSR) |
// 24 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
ReportBlock::ReportBlock()
: source_ssrc_(0),
fraction_lost_(0),
cumulative_lost_(0),
extended_high_seq_num_(0),
jitter_(0),
last_sr_(0),
delay_since_last_sr_(0) {}
bool ReportBlock::Parse(const uint8_t* buffer, size_t length) {
RTC_DCHECK(buffer != nullptr);
if (length < ReportBlock::kLength) {
RTC_LOG(LS_ERROR) << "Report Block should be 24 bytes long";
return false;
}
source_ssrc_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[0]);
fraction_lost_ = buffer[4];
cumulative_lost_ = ByteReader<int32_t, 3>::ReadBigEndian(&buffer[5]);
extended_high_seq_num_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[8]);
jitter_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[12]);
last_sr_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[16]);
delay_since_last_sr_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[20]);
return true;
}
void ReportBlock::Create(uint8_t* buffer) const {
// Runtime check should be done while setting cumulative_lost.
RTC_DCHECK_LT(cumulative_lost(), (1 << 23)); // Have only 3 bytes for it.
ByteWriter<uint32_t>::WriteBigEndian(&buffer[0], source_ssrc());
ByteWriter<uint8_t>::WriteBigEndian(&buffer[4], fraction_lost());
ByteWriter<int32_t, 3>::WriteBigEndian(&buffer[5], cumulative_lost());
ByteWriter<uint32_t>::WriteBigEndian(&buffer[8], extended_high_seq_num());
ByteWriter<uint32_t>::WriteBigEndian(&buffer[12], jitter());
ByteWriter<uint32_t>::WriteBigEndian(&buffer[16], last_sr());
ByteWriter<uint32_t>::WriteBigEndian(&buffer[20], delay_since_last_sr());
}
bool ReportBlock::SetCumulativeLost(int32_t cumulative_lost) {
// We have only 3 bytes to store it, and it's a signed value.
if (cumulative_lost >= (1 << 23) || cumulative_lost < -(1 << 23)) {
RTC_LOG(LS_WARNING)
<< "Cumulative lost is too big to fit into Report Block";
return false;
}
cumulative_lost_ = cumulative_lost;
return true;
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,70 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REPORT_BLOCK_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REPORT_BLOCK_H_
#include <stddef.h>
#include <stdint.h>
namespace webrtc {
namespace rtcp {
// A ReportBlock represents the Sender Report packet from
// RFC 3550 section 6.4.1.
class ReportBlock {
public:
static const size_t kLength = 24;
ReportBlock();
~ReportBlock() {}
bool Parse(const uint8_t* buffer, size_t length);
// Fills buffer with the ReportBlock.
// Consumes ReportBlock::kLength bytes.
void Create(uint8_t* buffer) const;
void SetMediaSsrc(uint32_t ssrc) { source_ssrc_ = ssrc; }
void SetFractionLost(uint8_t fraction_lost) {
fraction_lost_ = fraction_lost;
}
bool SetCumulativeLost(int32_t cumulative_lost);
void SetExtHighestSeqNum(uint32_t ext_highest_seq_num) {
extended_high_seq_num_ = ext_highest_seq_num;
}
void SetJitter(uint32_t jitter) { jitter_ = jitter; }
void SetLastSr(uint32_t last_sr) { last_sr_ = last_sr; }
void SetDelayLastSr(uint32_t delay_last_sr) {
delay_since_last_sr_ = delay_last_sr;
}
uint32_t source_ssrc() const { return source_ssrc_; }
uint8_t fraction_lost() const { return fraction_lost_; }
int32_t cumulative_lost() const { return cumulative_lost_; }
uint32_t extended_high_seq_num() const { return extended_high_seq_num_; }
uint32_t jitter() const { return jitter_; }
uint32_t last_sr() const { return last_sr_; }
uint32_t delay_since_last_sr() const { return delay_since_last_sr_; }
private:
uint32_t source_ssrc_; // 32 bits
uint8_t fraction_lost_; // 8 bits representing a fixed point value 0..1
int32_t cumulative_lost_; // Signed 24-bit value
uint32_t extended_high_seq_num_; // 32 bits
uint32_t jitter_; // 32 bits
uint32_t last_sr_; // 32 bits
uint32_t delay_since_last_sr_; // 32 bits, units of 1/65536 seconds
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REPORT_BLOCK_H_

View file

@ -0,0 +1,49 @@
/*
* 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.
*/
#include "modules/rtp_rtcp/source/rtcp_packet/rrtr.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "rtc_base/checks.h"
namespace webrtc {
namespace rtcp {
// Receiver Reference Time Report Block (RFC 3611).
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT=4 | reserved | block length = 2 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | NTP timestamp, most significant word |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | NTP timestamp, least significant word |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
void Rrtr::Parse(const uint8_t* buffer) {
RTC_DCHECK(buffer[0] == kBlockType);
// reserved = buffer[1];
RTC_DCHECK(ByteReader<uint16_t>::ReadBigEndian(&buffer[2]) == kBlockLength);
uint32_t seconds = ByteReader<uint32_t>::ReadBigEndian(&buffer[4]);
uint32_t fraction = ByteReader<uint32_t>::ReadBigEndian(&buffer[8]);
ntp_.Set(seconds, fraction);
}
void Rrtr::Create(uint8_t* buffer) const {
const uint8_t kReserved = 0;
buffer[0] = kBlockType;
buffer[1] = kReserved;
ByteWriter<uint16_t>::WriteBigEndian(&buffer[2], kBlockLength);
ByteWriter<uint32_t>::WriteBigEndian(&buffer[4], ntp_.seconds());
ByteWriter<uint32_t>::WriteBigEndian(&buffer[8], ntp_.fractions());
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,59 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RRTR_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RRTR_H_
#include <stddef.h>
#include <stdint.h>
#include "system_wrappers/include/ntp_time.h"
namespace webrtc {
namespace rtcp {
class Rrtr {
public:
static const uint8_t kBlockType = 4;
static const uint16_t kBlockLength = 2;
static const size_t kLength = 4 * (kBlockLength + 1); // 12
Rrtr() {}
Rrtr(const Rrtr&) = default;
~Rrtr() {}
Rrtr& operator=(const Rrtr&) = default;
void Parse(const uint8_t* buffer);
// Fills buffer with the Rrtr.
// Consumes Rrtr::kLength bytes.
void Create(uint8_t* buffer) const;
void SetNtp(NtpTime ntp) { ntp_ = ntp; }
NtpTime ntp() const { return ntp_; }
private:
NtpTime ntp_;
};
inline bool operator==(const Rrtr& rrtr1, const Rrtr& rrtr2) {
return rrtr1.ntp() == rrtr2.ntp();
}
inline bool operator!=(const Rrtr& rrtr1, const Rrtr& rrtr2) {
return !(rrtr1 == rrtr2);
}
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RRTR_H_

View file

@ -0,0 +1,50 @@
/*
* 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.
*/
#include "modules/rtp_rtcp/source/rtcp_packet/rrtr.h"
#include "test/gtest.h"
using webrtc::rtcp::Rrtr;
namespace webrtc {
namespace {
const uint32_t kNtpSec = 0x12345678;
const uint32_t kNtpFrac = 0x23456789;
const uint8_t kBlock[] = {0x04, 0x00, 0x00, 0x02, 0x12, 0x34,
0x56, 0x78, 0x23, 0x45, 0x67, 0x89};
const size_t kBlockSizeBytes = sizeof(kBlock);
static_assert(
kBlockSizeBytes == Rrtr::kLength,
"Size of manually created Rrtr block should match class constant");
TEST(RtcpPacketRrtrTest, Create) {
uint8_t buffer[Rrtr::kLength];
Rrtr rrtr;
rrtr.SetNtp(NtpTime(kNtpSec, kNtpFrac));
rrtr.Create(buffer);
EXPECT_EQ(0, memcmp(buffer, kBlock, kBlockSizeBytes));
}
TEST(RtcpPacketRrtrTest, Parse) {
Rrtr read_rrtr;
read_rrtr.Parse(kBlock);
// Run checks on const object to ensure all accessors have const modifier.
const Rrtr& parsed = read_rrtr;
EXPECT_EQ(kNtpSec, parsed.ntp().seconds());
EXPECT_EQ(kNtpFrac, parsed.ntp().fractions());
}
} // namespace
} // namespace webrtc

View file

@ -0,0 +1,45 @@
/*
* 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.
*/
#include "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h"
#include "modules/rtp_rtcp/source/byte_io.h"
namespace webrtc {
namespace rtcp {
constexpr uint8_t Rtpfb::kPacketType;
// RFC 4585, Section 6.1: Feedback format.
//
// Common packet format:
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| FMT | PT | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 0 | SSRC of packet sender |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 4 | SSRC of media source |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : Feedback Control Information (FCI) :
// : :
void Rtpfb::ParseCommonFeedback(const uint8_t* payload) {
SetSenderSsrc(ByteReader<uint32_t>::ReadBigEndian(&payload[0]));
SetMediaSsrc(ByteReader<uint32_t>::ReadBigEndian(&payload[4]));
}
void Rtpfb::CreateCommonFeedback(uint8_t* payload) const {
ByteWriter<uint32_t>::WriteBigEndian(&payload[0], sender_ssrc());
ByteWriter<uint32_t>::WriteBigEndian(&payload[4], media_ssrc());
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,47 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RTPFB_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RTPFB_H_
#include <stddef.h>
#include <stdint.h>
#include "modules/rtp_rtcp/source/rtcp_packet.h"
namespace webrtc {
namespace rtcp {
// RTPFB: Transport layer feedback message.
// RFC4585, Section 6.2
class Rtpfb : public RtcpPacket {
public:
static constexpr uint8_t kPacketType = 205;
Rtpfb() = default;
~Rtpfb() override = default;
void SetMediaSsrc(uint32_t ssrc) { media_ssrc_ = ssrc; }
uint32_t media_ssrc() const { return media_ssrc_; }
protected:
static constexpr size_t kCommonFeedbackLength = 8;
void ParseCommonFeedback(const uint8_t* payload);
void CreateCommonFeedback(uint8_t* payload) const;
private:
uint32_t media_ssrc_ = 0;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RTPFB_H_

View file

@ -0,0 +1,199 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/source/rtcp_packet/sdes.h"
#include <string.h>
#include <utility>
#include "absl/strings/string_view.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
constexpr uint8_t Sdes::kPacketType;
constexpr size_t Sdes::kMaxNumberOfChunks;
// Source Description (SDES) (RFC 3550).
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// header |V=2|P| SC | PT=SDES=202 | length |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// chunk | SSRC/CSRC_1 |
// 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SDES items |
// | ... |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// chunk | SSRC/CSRC_2 |
// 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SDES items |
// | ... |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// Canonical End-Point Identifier SDES Item (CNAME)
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | CNAME=1 | length | user and domain name ...
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
namespace {
const uint8_t kTerminatorTag = 0;
const uint8_t kCnameTag = 1;
size_t ChunkSize(const Sdes::Chunk& chunk) {
// Chunk:
// SSRC/CSRC (4 bytes) | CNAME=1 (1 byte) | length (1 byte) | cname | padding.
size_t chunk_payload_size = 4 + 1 + 1 + chunk.cname.size();
size_t padding_size = 4 - (chunk_payload_size % 4); // Minimum 1.
return chunk_payload_size + padding_size;
}
} // namespace
Sdes::Sdes() : block_length_(RtcpPacket::kHeaderLength) {}
Sdes::~Sdes() {}
bool Sdes::Parse(const CommonHeader& packet) {
RTC_DCHECK_EQ(packet.type(), kPacketType);
uint8_t number_of_chunks = packet.count();
std::vector<Chunk> chunks; // Read chunk into temporary array, so that in
// case of an error original array would stay
// unchanged.
size_t block_length = kHeaderLength;
if (packet.payload_size_bytes() % 4 != 0) {
RTC_LOG(LS_WARNING) << "Invalid payload size "
<< packet.payload_size_bytes()
<< " bytes for a valid Sdes packet. Size should be"
" multiple of 4 bytes";
}
const uint8_t* const payload_end =
packet.payload() + packet.payload_size_bytes();
const uint8_t* looking_at = packet.payload();
chunks.resize(number_of_chunks);
for (size_t i = 0; i < number_of_chunks;) {
// Each chunk consumes at least 8 bytes.
if (payload_end - looking_at < 8) {
RTC_LOG(LS_WARNING) << "Not enough space left for chunk #" << (i + 1);
return false;
}
chunks[i].ssrc = ByteReader<uint32_t>::ReadBigEndian(looking_at);
looking_at += sizeof(uint32_t);
bool cname_found = false;
uint8_t item_type;
while ((item_type = *(looking_at++)) != kTerminatorTag) {
if (looking_at >= payload_end) {
RTC_LOG(LS_WARNING)
<< "Unexpected end of packet while reading chunk #" << (i + 1)
<< ". Expected to find size of the text.";
return false;
}
uint8_t item_length = *(looking_at++);
const size_t kTerminatorSize = 1;
if (looking_at + item_length + kTerminatorSize > payload_end) {
RTC_LOG(LS_WARNING)
<< "Unexpected end of packet while reading chunk #" << (i + 1)
<< ". Expected to find text of size " << item_length;
return false;
}
if (item_type == kCnameTag) {
if (cname_found) {
RTC_LOG(LS_WARNING)
<< "Found extra CNAME for same ssrc in chunk #" << (i + 1);
return false;
}
cname_found = true;
chunks[i].cname.assign(reinterpret_cast<const char*>(looking_at),
item_length);
}
looking_at += item_length;
}
if (cname_found) {
// block_length calculates length of the packet that would be generated by
// Build/Create functions. Adjust it same way WithCName function does.
block_length += ChunkSize(chunks[i]);
++i;
} else {
// RFC states CNAME item is mandatory.
// But same time it allows chunk without items.
// So while parsing, ignore all chunks without cname,
// but do not fail the parse.
RTC_LOG(LS_WARNING) << "CNAME not found for ssrc " << chunks[i].ssrc;
--number_of_chunks;
chunks.resize(number_of_chunks);
}
// Adjust to 32bit boundary.
looking_at += (payload_end - looking_at) % 4;
}
chunks_ = std::move(chunks);
block_length_ = block_length;
return true;
}
bool Sdes::AddCName(uint32_t ssrc, absl::string_view cname) {
RTC_DCHECK_LE(cname.length(), 0xffu);
if (chunks_.size() >= kMaxNumberOfChunks) {
RTC_LOG(LS_WARNING) << "Max SDES chunks reached.";
return false;
}
Chunk chunk;
chunk.ssrc = ssrc;
chunk.cname = std::string(cname);
chunks_.push_back(chunk);
block_length_ += ChunkSize(chunk);
return true;
}
size_t Sdes::BlockLength() const {
return block_length_;
}
bool Sdes::Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const {
while (*index + BlockLength() > max_length) {
if (!OnBufferFull(packet, index, callback))
return false;
}
const size_t index_end = *index + BlockLength();
CreateHeader(chunks_.size(), kPacketType, HeaderLength(), packet, index);
for (const Sdes::Chunk& chunk : chunks_) {
ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 0], chunk.ssrc);
ByteWriter<uint8_t>::WriteBigEndian(&packet[*index + 4], kCnameTag);
ByteWriter<uint8_t>::WriteBigEndian(
&packet[*index + 5], static_cast<uint8_t>(chunk.cname.size()));
memcpy(&packet[*index + 6], chunk.cname.data(), chunk.cname.size());
*index += (6 + chunk.cname.size());
// In each chunk, the list of items must be terminated by one or more null
// octets. The next chunk must start on a 32-bit boundary.
// CNAME (1 byte) | length (1 byte) | name | padding.
size_t padding_size = 4 - ((6 + chunk.cname.size()) % 4);
const int kPadding = 0;
memset(packet + *index, kPadding, padding_size);
*index += padding_size;
}
RTC_CHECK_EQ(*index, index_end);
return true;
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_SDES_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_SDES_H_
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "modules/rtp_rtcp/source/rtcp_packet.h"
namespace webrtc {
namespace rtcp {
class CommonHeader;
// Source Description (SDES) (RFC 3550).
class Sdes : public RtcpPacket {
public:
struct Chunk {
uint32_t ssrc;
std::string cname;
};
static constexpr uint8_t kPacketType = 202;
static constexpr size_t kMaxNumberOfChunks = 0x1f;
Sdes();
~Sdes() override;
// Parse assumes header is already parsed and validated.
bool Parse(const CommonHeader& packet);
bool AddCName(uint32_t ssrc, absl::string_view cname);
const std::vector<Chunk>& chunks() const { return chunks_; }
size_t BlockLength() const override;
bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const override;
private:
std::vector<Chunk> chunks_;
size_t block_length_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_SDES_H_

View file

@ -0,0 +1,141 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
#include <utility>
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
constexpr uint8_t SenderReport::kPacketType;
constexpr size_t SenderReport::kMaxNumberOfReportBlocks;
constexpr size_t SenderReport::kSenderBaseLength;
// Sender report (SR) (RFC 3550).
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| RC | PT=SR=200 | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 0 | SSRC of sender |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// 4 | NTP timestamp, most significant word |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 8 | NTP timestamp, least significant word |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 12 | RTP timestamp |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 16 | sender's packet count |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 20 | sender's octet count |
// 24 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
SenderReport::SenderReport()
: rtp_timestamp_(0), sender_packet_count_(0), sender_octet_count_(0) {}
SenderReport::SenderReport(const SenderReport&) = default;
SenderReport::SenderReport(SenderReport&&) = default;
SenderReport& SenderReport::operator=(const SenderReport&) = default;
SenderReport& SenderReport::operator=(SenderReport&&) = default;
SenderReport::~SenderReport() = default;
bool SenderReport::Parse(const CommonHeader& packet) {
RTC_DCHECK_EQ(packet.type(), kPacketType);
const uint8_t report_block_count = packet.count();
if (packet.payload_size_bytes() <
kSenderBaseLength + report_block_count * ReportBlock::kLength) {
RTC_LOG(LS_WARNING) << "Packet is too small to contain all the data.";
return false;
}
// Read SenderReport header.
const uint8_t* const payload = packet.payload();
SetSenderSsrc(ByteReader<uint32_t>::ReadBigEndian(&payload[0]));
uint32_t secs = ByteReader<uint32_t>::ReadBigEndian(&payload[4]);
uint32_t frac = ByteReader<uint32_t>::ReadBigEndian(&payload[8]);
ntp_.Set(secs, frac);
rtp_timestamp_ = ByteReader<uint32_t>::ReadBigEndian(&payload[12]);
sender_packet_count_ = ByteReader<uint32_t>::ReadBigEndian(&payload[16]);
sender_octet_count_ = ByteReader<uint32_t>::ReadBigEndian(&payload[20]);
report_blocks_.resize(report_block_count);
const uint8_t* next_block = payload + kSenderBaseLength;
for (ReportBlock& block : report_blocks_) {
bool block_parsed = block.Parse(next_block, ReportBlock::kLength);
RTC_DCHECK(block_parsed);
next_block += ReportBlock::kLength;
}
// Double check we didn't read beyond provided buffer.
RTC_DCHECK_LE(next_block - payload,
static_cast<ptrdiff_t>(packet.payload_size_bytes()));
return true;
}
size_t SenderReport::BlockLength() const {
return kHeaderLength + kSenderBaseLength +
report_blocks_.size() * ReportBlock::kLength;
}
bool SenderReport::Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const {
while (*index + BlockLength() > max_length) {
if (!OnBufferFull(packet, index, callback))
return false;
}
const size_t index_end = *index + BlockLength();
CreateHeader(report_blocks_.size(), kPacketType, HeaderLength(), packet,
index);
// Write SenderReport header.
ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 0], sender_ssrc());
ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 4], ntp_.seconds());
ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 8], ntp_.fractions());
ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 12], rtp_timestamp_);
ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 16],
sender_packet_count_);
ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 20],
sender_octet_count_);
*index += kSenderBaseLength;
// Write report blocks.
for (const ReportBlock& block : report_blocks_) {
block.Create(packet + *index);
*index += ReportBlock::kLength;
}
// Ensure bytes written match expected.
RTC_DCHECK_EQ(*index, index_end);
return true;
}
bool SenderReport::AddReportBlock(const ReportBlock& block) {
if (report_blocks_.size() >= kMaxNumberOfReportBlocks) {
RTC_LOG(LS_WARNING) << "Max report blocks reached.";
return false;
}
report_blocks_.push_back(block);
return true;
}
bool SenderReport::SetReportBlocks(std::vector<ReportBlock> blocks) {
if (blocks.size() > kMaxNumberOfReportBlocks) {
RTC_LOG(LS_WARNING) << "Too many report blocks (" << blocks.size()
<< ") for sender report.";
return false;
}
report_blocks_ = std::move(blocks);
return true;
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_SENDER_REPORT_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_SENDER_REPORT_H_
#include <vector>
#include "modules/rtp_rtcp/source/rtcp_packet.h"
#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
#include "system_wrappers/include/ntp_time.h"
namespace webrtc {
namespace rtcp {
class CommonHeader;
class SenderReport : public RtcpPacket {
public:
static constexpr uint8_t kPacketType = 200;
static constexpr size_t kMaxNumberOfReportBlocks = 0x1f;
SenderReport();
SenderReport(const SenderReport&);
SenderReport(SenderReport&&);
SenderReport& operator=(const SenderReport&);
SenderReport& operator=(SenderReport&&);
~SenderReport() override;
// Parse assumes header is already parsed and validated.
bool Parse(const CommonHeader& packet);
void SetNtp(NtpTime ntp) { ntp_ = ntp; }
void SetRtpTimestamp(uint32_t rtp_timestamp) {
rtp_timestamp_ = rtp_timestamp;
}
void SetPacketCount(uint32_t packet_count) {
sender_packet_count_ = packet_count;
}
void SetOctetCount(uint32_t octet_count) {
sender_octet_count_ = octet_count;
}
bool AddReportBlock(const ReportBlock& block);
bool SetReportBlocks(std::vector<ReportBlock> blocks);
void ClearReportBlocks() { report_blocks_.clear(); }
NtpTime ntp() const { return ntp_; }
uint32_t rtp_timestamp() const { return rtp_timestamp_; }
uint32_t sender_packet_count() const { return sender_packet_count_; }
uint32_t sender_octet_count() const { return sender_octet_count_; }
const std::vector<ReportBlock>& report_blocks() const {
return report_blocks_;
}
size_t BlockLength() const override;
bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const override;
private:
static constexpr size_t kSenderBaseLength = 24;
NtpTime ntp_;
uint32_t rtp_timestamp_;
uint32_t sender_packet_count_;
uint32_t sender_octet_count_;
std::vector<ReportBlock> report_blocks_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_SENDER_REPORT_H_

View file

@ -0,0 +1,127 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "rtc_base/checks.h"
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
namespace rtcp {
constexpr size_t kTargetBitrateHeaderSizeBytes = 4;
constexpr uint8_t TargetBitrate::kBlockType;
const size_t TargetBitrate::kBitrateItemSizeBytes = 4;
TargetBitrate::BitrateItem::BitrateItem()
: spatial_layer(0), temporal_layer(0), target_bitrate_kbps(0) {}
TargetBitrate::BitrateItem::BitrateItem(uint8_t spatial_layer,
uint8_t temporal_layer,
uint32_t target_bitrate_kbps)
: spatial_layer(spatial_layer),
temporal_layer(temporal_layer),
target_bitrate_kbps(target_bitrate_kbps) {}
// RFC 4585: Feedback format.
//
// Common packet format:
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT=42 | reserved | block length |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// Target bitrate item (repeat as many times as necessary).
//
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | S | T | Target Bitrate |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : ... :
//
// Spatial Layer (S): 4 bits
// Indicates which temporal layer this bitrate concerns.
//
// Temporal Layer (T): 4 bits
// Indicates which temporal layer this bitrate concerns.
//
// Target Bitrate: 24 bits
// The encoder target bitrate for this layer, in kbps.
//
// As an example of how S and T are intended to be used, VP8 simulcast will
// use a separate TargetBitrate message per stream, since they are transmitted
// on separate SSRCs, with temporal layers grouped by stream.
// If VP9 SVC is used, there will be only one SSRC, so each spatial and
// temporal layer combo used shall be specified in the TargetBitrate packet.
TargetBitrate::TargetBitrate() = default;
TargetBitrate::TargetBitrate(const TargetBitrate&) = default;
TargetBitrate& TargetBitrate::operator=(const TargetBitrate&) = default;
TargetBitrate::~TargetBitrate() = default;
void TargetBitrate::Parse(const uint8_t* block, uint16_t block_length) {
// Validate block header (should already have been parsed and checked).
RTC_DCHECK_EQ(block[0], kBlockType);
RTC_DCHECK_EQ(block_length, ByteReader<uint16_t>::ReadBigEndian(&block[2]));
// Header specifies block length - 1, but since we ignore the header, which
// occupies exactly on block, we can just treat this as payload length.
const size_t payload_bytes = block_length * 4;
const size_t num_items = payload_bytes / kBitrateItemSizeBytes;
size_t index = kTargetBitrateHeaderSizeBytes;
bitrates_.clear();
for (size_t i = 0; i < num_items; ++i) {
uint8_t layers = block[index];
uint32_t bitrate_kbps =
ByteReader<uint32_t, 3>::ReadBigEndian(&block[index + 1]);
index += kBitrateItemSizeBytes;
AddTargetBitrate((layers >> 4) & 0x0F, layers & 0x0F, bitrate_kbps);
}
}
void TargetBitrate::AddTargetBitrate(uint8_t spatial_layer,
uint8_t temporal_layer,
uint32_t target_bitrate_kbps) {
RTC_DCHECK_LE(spatial_layer, 0x0F);
RTC_DCHECK_LE(temporal_layer, 0x0F);
RTC_DCHECK_LE(target_bitrate_kbps, 0x00FFFFFFU);
bitrates_.push_back(
BitrateItem(spatial_layer, temporal_layer, target_bitrate_kbps));
}
const std::vector<TargetBitrate::BitrateItem>&
TargetBitrate::GetTargetBitrates() const {
return bitrates_;
}
size_t TargetBitrate::BlockLength() const {
return kTargetBitrateHeaderSizeBytes +
bitrates_.size() * kBitrateItemSizeBytes;
}
void TargetBitrate::Create(uint8_t* buffer) const {
buffer[0] = kBlockType;
buffer[1] = 0; // Reserved.
uint16_t block_length_words =
rtc::dchecked_cast<uint16_t>((BlockLength() / 4) - 1);
ByteWriter<uint16_t>::WriteBigEndian(&buffer[2], block_length_words);
size_t index = kTargetBitrateHeaderSizeBytes;
for (const BitrateItem& item : bitrates_) {
buffer[index] = (item.spatial_layer << 4) | item.temporal_layer;
ByteWriter<uint32_t, 3>::WriteBigEndian(&buffer[index + 1],
item.target_bitrate_kbps);
index += kBitrateItemSizeBytes;
}
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TARGET_BITRATE_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TARGET_BITRATE_H_
#include <stddef.h>
#include <stdint.h>
#include <vector>
namespace webrtc {
namespace rtcp {
class TargetBitrate {
public:
// TODO(sprang): This block type is just a place holder. We need to get an
// id assigned by IANA.
static constexpr uint8_t kBlockType = 42;
static const size_t kBitrateItemSizeBytes;
struct BitrateItem {
BitrateItem();
BitrateItem(uint8_t spatial_layer,
uint8_t temporal_layer,
uint32_t target_bitrate_kbps);
uint8_t spatial_layer;
uint8_t temporal_layer;
uint32_t target_bitrate_kbps;
};
TargetBitrate();
TargetBitrate(const TargetBitrate&);
TargetBitrate& operator=(const TargetBitrate&);
~TargetBitrate();
void AddTargetBitrate(uint8_t spatial_layer,
uint8_t temporal_layer,
uint32_t target_bitrate_kbps);
const std::vector<BitrateItem>& GetTargetBitrates() const;
void Parse(const uint8_t* block, uint16_t block_length);
size_t BlockLength() const;
void Create(uint8_t* buffer) const;
private:
std::vector<BitrateItem> bitrates_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TARGET_BITRATE_H_

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/source/rtcp_packet/tmmb_item.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
TmmbItem::TmmbItem(uint32_t ssrc, uint64_t bitrate_bps, uint16_t overhead)
: ssrc_(ssrc), bitrate_bps_(bitrate_bps), packet_overhead_(overhead) {
RTC_DCHECK_LE(overhead, 0x1ffu);
}
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 0 | SSRC |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 4 | MxTBR Exp | MxTBR Mantissa |Measured Overhead|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
bool TmmbItem::Parse(const uint8_t* buffer) {
ssrc_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[0]);
// Read 4 bytes into 1 block.
uint32_t compact = ByteReader<uint32_t>::ReadBigEndian(&buffer[4]);
// Split 1 block into 3 components.
uint8_t exponent = compact >> 26; // 6 bits.
uint64_t mantissa = (compact >> 9) & 0x1ffff; // 17 bits.
uint16_t overhead = compact & 0x1ff; // 9 bits.
// Combine 3 components into 2 values.
bitrate_bps_ = (mantissa << exponent);
bool shift_overflow = (bitrate_bps_ >> exponent) != mantissa;
if (shift_overflow) {
RTC_LOG(LS_ERROR) << "Invalid tmmb bitrate value : " << mantissa << "*2^"
<< static_cast<int>(exponent);
return false;
}
packet_overhead_ = overhead;
return true;
}
void TmmbItem::Create(uint8_t* buffer) const {
constexpr uint64_t kMaxMantissa = 0x1ffff; // 17 bits.
uint64_t mantissa = bitrate_bps_;
uint32_t exponent = 0;
while (mantissa > kMaxMantissa) {
mantissa >>= 1;
++exponent;
}
ByteWriter<uint32_t>::WriteBigEndian(&buffer[0], ssrc_);
uint32_t compact = (exponent << 26) | (mantissa << 9) | packet_overhead_;
ByteWriter<uint32_t>::WriteBigEndian(&buffer[4], compact);
}
void TmmbItem::set_packet_overhead(uint16_t overhead) {
RTC_DCHECK_LE(overhead, 0x1ffu);
packet_overhead_ = overhead;
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMB_ITEM_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMB_ITEM_H_
#include <stddef.h>
#include <stdint.h>
namespace webrtc {
namespace rtcp {
// RFC5104, Section 3.5.4
// Temporary Maximum Media Stream Bitrate Request/Notification.
// Used both by TMMBR and TMMBN rtcp packets.
class TmmbItem {
public:
static const size_t kLength = 8;
TmmbItem() : ssrc_(0), bitrate_bps_(0), packet_overhead_(0) {}
TmmbItem(uint32_t ssrc, uint64_t bitrate_bps, uint16_t overhead);
bool Parse(const uint8_t* buffer);
void Create(uint8_t* buffer) const;
void set_ssrc(uint32_t ssrc) { ssrc_ = ssrc; }
void set_bitrate_bps(uint64_t bitrate_bps) { bitrate_bps_ = bitrate_bps; }
void set_packet_overhead(uint16_t overhead);
uint32_t ssrc() const { return ssrc_; }
uint64_t bitrate_bps() const { return bitrate_bps_; }
uint16_t packet_overhead() const { return packet_overhead_; }
private:
// Media stream id.
uint32_t ssrc_;
// Maximum total media bit rate that the media receiver is
// currently prepared to accept for this media stream.
uint64_t bitrate_bps_;
// Per-packet overhead that the media receiver has observed
// for this media stream at its chosen reference protocol layer.
uint16_t packet_overhead_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMB_ITEM_H_

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/source/rtcp_packet/tmmbn.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
constexpr uint8_t Tmmbn::kFeedbackMessageType;
// RFC 4585: Feedback format.
// Common packet format:
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| FMT | PT | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of packet sender |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of media source (unused) = 0 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : Feedback Control Information (FCI) :
// : :
// Temporary Maximum Media Stream Bit Rate Notification (TMMBN) (RFC 5104).
// The Feedback Control Information (FCI) consists of zero, one, or more
// TMMBN FCI entries.
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | MxTBR Exp | MxTBR Mantissa |Measured Overhead|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Tmmbn::Tmmbn() = default;
Tmmbn::~Tmmbn() = default;
bool Tmmbn::Parse(const CommonHeader& packet) {
RTC_DCHECK_EQ(packet.type(), kPacketType);
RTC_DCHECK_EQ(packet.fmt(), kFeedbackMessageType);
if (packet.payload_size_bytes() < kCommonFeedbackLength) {
RTC_LOG(LS_WARNING) << "Payload length " << packet.payload_size_bytes()
<< " is too small for TMMBN.";
return false;
}
size_t items_size_bytes = packet.payload_size_bytes() - kCommonFeedbackLength;
if (items_size_bytes % TmmbItem::kLength != 0) {
RTC_LOG(LS_WARNING) << "Payload length " << packet.payload_size_bytes()
<< " is not valid for TMMBN.";
return false;
}
ParseCommonFeedback(packet.payload());
const uint8_t* next_item = packet.payload() + kCommonFeedbackLength;
size_t number_of_items = items_size_bytes / TmmbItem::kLength;
items_.resize(number_of_items);
for (TmmbItem& item : items_) {
if (!item.Parse(next_item))
return false;
next_item += TmmbItem::kLength;
}
return true;
}
void Tmmbn::AddTmmbr(const TmmbItem& item) {
items_.push_back(item);
}
size_t Tmmbn::BlockLength() const {
return kHeaderLength + kCommonFeedbackLength +
TmmbItem::kLength * items_.size();
}
bool Tmmbn::Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const {
while (*index + BlockLength() > max_length) {
if (!OnBufferFull(packet, index, callback))
return false;
}
const size_t index_end = *index + BlockLength();
CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), packet,
index);
RTC_DCHECK_EQ(0, Rtpfb::media_ssrc());
CreateCommonFeedback(packet + *index);
*index += kCommonFeedbackLength;
for (const TmmbItem& item : items_) {
item.Create(packet + *index);
*index += TmmbItem::kLength;
}
RTC_CHECK_EQ(index_end, *index);
return true;
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBN_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBN_H_
#include <vector>
#include "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h"
#include "modules/rtp_rtcp/source/rtcp_packet/tmmb_item.h"
namespace webrtc {
namespace rtcp {
class CommonHeader;
// Temporary Maximum Media Stream Bit Rate Notification (TMMBN).
// RFC 5104, Section 4.2.2.
class Tmmbn : public Rtpfb {
public:
static constexpr uint8_t kFeedbackMessageType = 4;
Tmmbn();
~Tmmbn() override;
// Parse assumes header is already parsed and validated.
bool Parse(const CommonHeader& packet);
void AddTmmbr(const TmmbItem& item);
const std::vector<TmmbItem>& items() const { return items_; }
size_t BlockLength() const override;
bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const override;
private:
// Media ssrc is unused, shadow base class setter and getter.
void SetMediaSsrc(uint32_t ssrc);
uint32_t media_ssrc() const;
std::vector<TmmbItem> items_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBN_H_

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 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 "modules/rtp_rtcp/source/rtcp_packet/tmmbr.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace rtcp {
constexpr uint8_t Tmmbr::kFeedbackMessageType;
// RFC 4585: Feedback format.
// Common packet format:
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| FMT | PT | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of packet sender |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of media source (unused) = 0 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : Feedback Control Information (FCI) :
// : :
// Temporary Maximum Media Stream Bit Rate Request (TMMBR) (RFC 5104).
// The Feedback Control Information (FCI) for the TMMBR
// consists of one or more FCI entries.
// FCI:
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | MxTBR Exp | MxTBR Mantissa |Measured Overhead|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Tmmbr::Tmmbr() = default;
Tmmbr::~Tmmbr() = default;
bool Tmmbr::Parse(const CommonHeader& packet) {
RTC_DCHECK_EQ(packet.type(), kPacketType);
RTC_DCHECK_EQ(packet.fmt(), kFeedbackMessageType);
if (packet.payload_size_bytes() < kCommonFeedbackLength + TmmbItem::kLength) {
RTC_LOG(LS_WARNING) << "Payload length " << packet.payload_size_bytes()
<< " is too small for a TMMBR.";
return false;
}
size_t items_size_bytes = packet.payload_size_bytes() - kCommonFeedbackLength;
if (items_size_bytes % TmmbItem::kLength != 0) {
RTC_LOG(LS_WARNING) << "Payload length " << packet.payload_size_bytes()
<< " is not valid for a TMMBR.";
return false;
}
ParseCommonFeedback(packet.payload());
const uint8_t* next_item = packet.payload() + kCommonFeedbackLength;
size_t number_of_items = items_size_bytes / TmmbItem::kLength;
items_.resize(number_of_items);
for (TmmbItem& item : items_) {
if (!item.Parse(next_item))
return false;
next_item += TmmbItem::kLength;
}
return true;
}
void Tmmbr::AddTmmbr(const TmmbItem& item) {
items_.push_back(item);
}
size_t Tmmbr::BlockLength() const {
return kHeaderLength + kCommonFeedbackLength +
TmmbItem::kLength * items_.size();
}
bool Tmmbr::Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const {
RTC_DCHECK(!items_.empty());
while (*index + BlockLength() > max_length) {
if (!OnBufferFull(packet, index, callback))
return false;
}
const size_t index_end = *index + BlockLength();
CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), packet,
index);
RTC_DCHECK_EQ(0, Rtpfb::media_ssrc());
CreateCommonFeedback(packet + *index);
*index += kCommonFeedbackLength;
for (const TmmbItem& item : items_) {
item.Create(packet + *index);
*index += TmmbItem::kLength;
}
RTC_CHECK_EQ(index_end, *index);
return true;
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBR_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBR_H_
#include <vector>
#include "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h"
#include "modules/rtp_rtcp/source/rtcp_packet/tmmb_item.h"
namespace webrtc {
namespace rtcp {
class CommonHeader;
// Temporary Maximum Media Stream Bit Rate Request (TMMBR).
// RFC 5104, Section 4.2.1.
class Tmmbr : public Rtpfb {
public:
static constexpr uint8_t kFeedbackMessageType = 3;
Tmmbr();
~Tmmbr() override;
// Parse assumes header is already parsed and validated.
bool Parse(const CommonHeader& packet);
void AddTmmbr(const TmmbItem& item);
const std::vector<TmmbItem>& requests() const { return items_; }
size_t BlockLength() const override;
bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
PacketReadyCallback callback) const override;
private:
// Media ssrc is unused, shadow base class setter.
void SetMediaSsrc(uint32_t ssrc);
std::vector<TmmbItem> items_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBR_H_

View file

@ -0,0 +1,737 @@
/*
* 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.
*/
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
#include <algorithm>
#include <cstdint>
#include <numeric>
#include <utility>
#include "absl/algorithm/container.h"
#include "modules/include/module_common_types_public.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/trace_event.h"
namespace webrtc {
namespace rtcp {
namespace {
// Header size:
// * 4 bytes Common RTCP Packet Header
// * 8 bytes Common Packet Format for RTCP Feedback Messages
// * 8 bytes FeedbackPacket header
constexpr size_t kTransportFeedbackHeaderSizeBytes = 4 + 8 + 8;
constexpr size_t kChunkSizeBytes = 2;
// TODO(sprang): Add support for dynamic max size for easier fragmentation,
// eg. set it to what's left in the buffer or IP_PACKET_SIZE.
// Size constraint imposed by RTCP common header: 16bit size field interpreted
// as number of four byte words minus the first header word.
constexpr size_t kMaxSizeBytes = (1 << 16) * 4;
// Payload size:
// * 8 bytes Common Packet Format for RTCP Feedback Messages
// * 8 bytes FeedbackPacket header.
// * 2 bytes for one chunk.
constexpr size_t kMinPayloadSizeBytes = 8 + 8 + 2;
constexpr TimeDelta kBaseTimeTick = TransportFeedback::kDeltaTick * (1 << 8);
constexpr TimeDelta kTimeWrapPeriod = kBaseTimeTick * (1 << 24);
// Message format
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| FMT=15 | PT=205 | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 0 | SSRC of packet sender |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 4 | SSRC of media source |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 8 | base sequence number | packet status count |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 12 | reference time | fb pkt. count |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 16 | packet chunk | packet chunk |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// . .
// . .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | packet chunk | recv delta | recv delta |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// . .
// . .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | recv delta | recv delta | zero padding |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
} // namespace
constexpr uint8_t TransportFeedback::kFeedbackMessageType;
constexpr size_t TransportFeedback::kMaxReportedPackets;
constexpr size_t TransportFeedback::LastChunk::kMaxRunLengthCapacity;
constexpr size_t TransportFeedback::LastChunk::kMaxOneBitCapacity;
constexpr size_t TransportFeedback::LastChunk::kMaxTwoBitCapacity;
constexpr size_t TransportFeedback::LastChunk::kMaxVectorCapacity;
TransportFeedback::LastChunk::LastChunk() {
Clear();
}
bool TransportFeedback::LastChunk::Empty() const {
return size_ == 0;
}
void TransportFeedback::LastChunk::Clear() {
size_ = 0;
all_same_ = true;
has_large_delta_ = false;
}
bool TransportFeedback::LastChunk::CanAdd(DeltaSize delta_size) const {
RTC_DCHECK_LE(delta_size, 2);
if (size_ < kMaxTwoBitCapacity)
return true;
if (size_ < kMaxOneBitCapacity && !has_large_delta_ && delta_size != kLarge)
return true;
if (size_ < kMaxRunLengthCapacity && all_same_ &&
delta_sizes_[0] == delta_size)
return true;
return false;
}
void TransportFeedback::LastChunk::Add(DeltaSize delta_size) {
RTC_DCHECK(CanAdd(delta_size));
if (size_ < kMaxVectorCapacity)
delta_sizes_[size_] = delta_size;
size_++;
all_same_ = all_same_ && delta_size == delta_sizes_[0];
has_large_delta_ = has_large_delta_ || delta_size == kLarge;
}
void TransportFeedback::LastChunk::AddMissingPackets(size_t num_missing) {
RTC_DCHECK_EQ(size_, 0);
RTC_DCHECK(all_same_);
RTC_DCHECK(!has_large_delta_);
RTC_DCHECK_LT(num_missing, kMaxRunLengthCapacity);
absl::c_fill(delta_sizes_, DeltaSize(0));
size_ = num_missing;
}
uint16_t TransportFeedback::LastChunk::Emit() {
RTC_DCHECK(!CanAdd(0) || !CanAdd(1) || !CanAdd(2));
if (all_same_) {
uint16_t chunk = EncodeRunLength();
Clear();
return chunk;
}
if (size_ == kMaxOneBitCapacity) {
uint16_t chunk = EncodeOneBit();
Clear();
return chunk;
}
RTC_DCHECK_GE(size_, kMaxTwoBitCapacity);
uint16_t chunk = EncodeTwoBit(kMaxTwoBitCapacity);
// Remove `kMaxTwoBitCapacity` encoded delta sizes:
// Shift remaining delta sizes and recalculate all_same_ && has_large_delta_.
size_ -= kMaxTwoBitCapacity;
all_same_ = true;
has_large_delta_ = false;
for (size_t i = 0; i < size_; ++i) {
DeltaSize delta_size = delta_sizes_[kMaxTwoBitCapacity + i];
delta_sizes_[i] = delta_size;
all_same_ = all_same_ && delta_size == delta_sizes_[0];
has_large_delta_ = has_large_delta_ || delta_size == kLarge;
}
return chunk;
}
uint16_t TransportFeedback::LastChunk::EncodeLast() const {
RTC_DCHECK_GT(size_, 0);
if (all_same_)
return EncodeRunLength();
if (size_ <= kMaxTwoBitCapacity)
return EncodeTwoBit(size_);
return EncodeOneBit();
}
// Appends content of the Lastchunk to `deltas`.
void TransportFeedback::LastChunk::AppendTo(
std::vector<DeltaSize>* deltas) const {
if (all_same_) {
deltas->insert(deltas->end(), size_, delta_sizes_[0]);
} else {
deltas->insert(deltas->end(), delta_sizes_.begin(),
delta_sizes_.begin() + size_);
}
}
void TransportFeedback::LastChunk::Decode(uint16_t chunk, size_t max_size) {
if ((chunk & 0x8000) == 0) {
DecodeRunLength(chunk, max_size);
} else if ((chunk & 0x4000) == 0) {
DecodeOneBit(chunk, max_size);
} else {
DecodeTwoBit(chunk, max_size);
}
}
// One Bit Status Vector Chunk
//
// 0 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |T|S| symbol list |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// T = 1
// S = 0
// Symbol list = 14 entries where 0 = not received, 1 = received 1-byte delta.
uint16_t TransportFeedback::LastChunk::EncodeOneBit() const {
RTC_DCHECK(!has_large_delta_);
RTC_DCHECK_LE(size_, kMaxOneBitCapacity);
uint16_t chunk = 0x8000;
for (size_t i = 0; i < size_; ++i)
chunk |= delta_sizes_[i] << (kMaxOneBitCapacity - 1 - i);
return chunk;
}
void TransportFeedback::LastChunk::DecodeOneBit(uint16_t chunk,
size_t max_size) {
RTC_DCHECK_EQ(chunk & 0xc000, 0x8000);
size_ = std::min(kMaxOneBitCapacity, max_size);
has_large_delta_ = false;
all_same_ = false;
for (size_t i = 0; i < size_; ++i)
delta_sizes_[i] = (chunk >> (kMaxOneBitCapacity - 1 - i)) & 0x01;
}
// Two Bit Status Vector Chunk
//
// 0 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |T|S| symbol list |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// T = 1
// S = 1
// symbol list = 7 entries of two bits each.
uint16_t TransportFeedback::LastChunk::EncodeTwoBit(size_t size) const {
RTC_DCHECK_LE(size, size_);
uint16_t chunk = 0xc000;
for (size_t i = 0; i < size; ++i)
chunk |= delta_sizes_[i] << 2 * (kMaxTwoBitCapacity - 1 - i);
return chunk;
}
void TransportFeedback::LastChunk::DecodeTwoBit(uint16_t chunk,
size_t max_size) {
RTC_DCHECK_EQ(chunk & 0xc000, 0xc000);
size_ = std::min(kMaxTwoBitCapacity, max_size);
has_large_delta_ = true;
all_same_ = false;
for (size_t i = 0; i < size_; ++i)
delta_sizes_[i] = (chunk >> 2 * (kMaxTwoBitCapacity - 1 - i)) & 0x03;
}
// Run Length Status Vector Chunk
//
// 0 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |T| S | Run Length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// T = 0
// S = symbol
// Run Length = Unsigned integer denoting the run length of the symbol
uint16_t TransportFeedback::LastChunk::EncodeRunLength() const {
RTC_DCHECK(all_same_);
RTC_DCHECK_LE(size_, kMaxRunLengthCapacity);
return (delta_sizes_[0] << 13) | static_cast<uint16_t>(size_);
}
void TransportFeedback::LastChunk::DecodeRunLength(uint16_t chunk,
size_t max_count) {
RTC_DCHECK_EQ(chunk & 0x8000, 0);
size_ = std::min<size_t>(chunk & 0x1fff, max_count);
DeltaSize delta_size = (chunk >> 13) & 0x03;
has_large_delta_ = delta_size >= kLarge;
all_same_ = true;
// To make it consistent with Add function, populate delta_sizes_ beyond 1st.
for (size_t i = 0; i < std::min<size_t>(size_, kMaxVectorCapacity); ++i)
delta_sizes_[i] = delta_size;
}
TransportFeedback::TransportFeedback()
: TransportFeedback(/*include_timestamps=*/true) {}
TransportFeedback::TransportFeedback(bool include_timestamps)
: base_seq_no_(0),
num_seq_no_(0),
base_time_ticks_(0),
feedback_seq_(0),
include_timestamps_(include_timestamps),
last_timestamp_(Timestamp::Zero()),
size_bytes_(kTransportFeedbackHeaderSizeBytes) {}
TransportFeedback::TransportFeedback(const TransportFeedback&) = default;
TransportFeedback::TransportFeedback(TransportFeedback&& other)
: base_seq_no_(other.base_seq_no_),
num_seq_no_(other.num_seq_no_),
base_time_ticks_(other.base_time_ticks_),
feedback_seq_(other.feedback_seq_),
include_timestamps_(other.include_timestamps_),
last_timestamp_(other.last_timestamp_),
received_packets_(std::move(other.received_packets_)),
all_packets_(std::move(other.all_packets_)),
encoded_chunks_(std::move(other.encoded_chunks_)),
last_chunk_(other.last_chunk_),
size_bytes_(other.size_bytes_) {
other.Clear();
}
TransportFeedback::~TransportFeedback() {}
void TransportFeedback::SetBase(uint16_t base_sequence,
Timestamp ref_timestamp) {
RTC_DCHECK_EQ(num_seq_no_, 0);
base_seq_no_ = base_sequence;
base_time_ticks_ =
(ref_timestamp.us() % kTimeWrapPeriod.us()) / kBaseTimeTick.us();
last_timestamp_ = BaseTime();
}
void TransportFeedback::SetFeedbackSequenceNumber(uint8_t feedback_sequence) {
feedback_seq_ = feedback_sequence;
}
bool TransportFeedback::AddReceivedPacket(uint16_t sequence_number,
Timestamp timestamp) {
// Set delta to zero if timestamps are not included, this will simplify the
// encoding process.
int16_t delta = 0;
if (include_timestamps_) {
// Convert to ticks and round.
if (last_timestamp_ > timestamp) {
timestamp += (last_timestamp_ - timestamp).RoundUpTo(kTimeWrapPeriod);
}
RTC_DCHECK_GE(timestamp, last_timestamp_);
int64_t delta_full =
(timestamp - last_timestamp_).us() % kTimeWrapPeriod.us();
if (delta_full > kTimeWrapPeriod.us() / 2) {
delta_full -= kTimeWrapPeriod.us();
delta_full -= kDeltaTick.us() / 2;
} else {
delta_full += kDeltaTick.us() / 2;
}
delta_full /= kDeltaTick.us();
delta = static_cast<int16_t>(delta_full);
// If larger than 16bit signed, we can't represent it - need new fb packet.
if (delta != delta_full) {
RTC_LOG(LS_WARNING) << "Delta value too large ( >= 2^16 ticks )";
return false;
}
}
uint16_t next_seq_no = base_seq_no_ + num_seq_no_;
if (sequence_number != next_seq_no) {
uint16_t last_seq_no = next_seq_no - 1;
if (!IsNewerSequenceNumber(sequence_number, last_seq_no))
return false;
uint16_t num_missing_packets = sequence_number - next_seq_no;
if (!AddMissingPackets(num_missing_packets))
return false;
}
DeltaSize delta_size = (delta >= 0 && delta <= 0xff) ? 1 : 2;
if (!AddDeltaSize(delta_size))
return false;
received_packets_.emplace_back(sequence_number, delta);
last_timestamp_ += delta * kDeltaTick;
if (include_timestamps_) {
size_bytes_ += delta_size;
}
return true;
}
const std::vector<TransportFeedback::ReceivedPacket>&
TransportFeedback::GetReceivedPackets() const {
return received_packets_;
}
void TransportFeedback::ForAllPackets(
rtc::FunctionView<void(uint16_t, TimeDelta)> handler) const {
TimeDelta delta_since_base = TimeDelta::Zero();
auto received_it = received_packets_.begin();
const uint16_t last_seq_num = base_seq_no_ + num_seq_no_;
for (uint16_t seq_num = base_seq_no_; seq_num != last_seq_num; ++seq_num) {
if (received_it != received_packets_.end() &&
received_it->sequence_number() == seq_num) {
delta_since_base += received_it->delta();
handler(seq_num, delta_since_base);
++received_it;
} else {
handler(seq_num, TimeDelta::PlusInfinity());
}
}
RTC_DCHECK(received_it == received_packets_.end());
}
uint16_t TransportFeedback::GetBaseSequence() const {
return base_seq_no_;
}
Timestamp TransportFeedback::BaseTime() const {
// Add an extra kTimeWrapPeriod to allow add received packets arrived earlier
// than the first added packet (and thus allow to record negative deltas)
// even when base_time_ticks_ == 0.
return Timestamp::Zero() + kTimeWrapPeriod +
int64_t{base_time_ticks_} * kBaseTimeTick;
}
TimeDelta TransportFeedback::GetBaseDelta(Timestamp prev_timestamp) const {
TimeDelta delta = BaseTime() - prev_timestamp;
// Compensate for wrap around.
if ((delta - kTimeWrapPeriod).Abs() < delta.Abs()) {
delta -= kTimeWrapPeriod; // Wrap backwards.
} else if ((delta + kTimeWrapPeriod).Abs() < delta.Abs()) {
delta += kTimeWrapPeriod; // Wrap forwards.
}
return delta;
}
// De-serialize packet.
bool TransportFeedback::Parse(const CommonHeader& packet) {
RTC_DCHECK_EQ(packet.type(), kPacketType);
RTC_DCHECK_EQ(packet.fmt(), kFeedbackMessageType);
TRACE_EVENT0("webrtc", "TransportFeedback::Parse");
if (packet.payload_size_bytes() < kMinPayloadSizeBytes) {
RTC_LOG(LS_WARNING) << "Buffer too small (" << packet.payload_size_bytes()
<< " bytes) to fit a "
"FeedbackPacket. Minimum size = "
<< kMinPayloadSizeBytes;
return false;
}
const uint8_t* const payload = packet.payload();
ParseCommonFeedback(payload);
base_seq_no_ = ByteReader<uint16_t>::ReadBigEndian(&payload[8]);
uint16_t status_count = ByteReader<uint16_t>::ReadBigEndian(&payload[10]);
base_time_ticks_ = ByteReader<uint32_t, 3>::ReadBigEndian(&payload[12]);
feedback_seq_ = payload[15];
Clear();
size_t index = 16;
const size_t end_index = packet.payload_size_bytes();
if (status_count == 0) {
RTC_LOG(LS_WARNING) << "Empty feedback messages not allowed.";
return false;
}
std::vector<uint8_t> delta_sizes;
delta_sizes.reserve(status_count);
while (delta_sizes.size() < status_count) {
if (index + kChunkSizeBytes > end_index) {
RTC_LOG(LS_WARNING) << "Buffer overflow while parsing packet.";
Clear();
return false;
}
uint16_t chunk = ByteReader<uint16_t>::ReadBigEndian(&payload[index]);
index += kChunkSizeBytes;
encoded_chunks_.push_back(chunk);
last_chunk_.Decode(chunk, status_count - delta_sizes.size());
last_chunk_.AppendTo(&delta_sizes);
}
// Last chunk is stored in the `last_chunk_`.
encoded_chunks_.pop_back();
RTC_DCHECK_EQ(delta_sizes.size(), status_count);
num_seq_no_ = status_count;
uint16_t seq_no = base_seq_no_;
size_t recv_delta_size = absl::c_accumulate(delta_sizes, 0);
// Determine if timestamps, that is, recv_delta are included in the packet.
if (end_index >= index + recv_delta_size) {
for (size_t delta_size : delta_sizes) {
RTC_DCHECK_LE(index + delta_size, end_index);
switch (delta_size) {
case 0:
break;
case 1: {
int16_t delta = payload[index];
received_packets_.emplace_back(seq_no, delta);
last_timestamp_ += delta * kDeltaTick;
index += delta_size;
break;
}
case 2: {
int16_t delta = ByteReader<int16_t>::ReadBigEndian(&payload[index]);
received_packets_.emplace_back(seq_no, delta);
last_timestamp_ += delta * kDeltaTick;
index += delta_size;
break;
}
case 3:
Clear();
RTC_LOG(LS_WARNING) << "Invalid delta_size for seq_no " << seq_no;
return false;
default:
RTC_DCHECK_NOTREACHED();
break;
}
++seq_no;
}
} else {
// The packet does not contain receive deltas.
include_timestamps_ = false;
for (size_t delta_size : delta_sizes) {
// Use delta sizes to detect if packet was received.
if (delta_size > 0) {
received_packets_.emplace_back(seq_no, 0);
}
++seq_no;
}
}
size_bytes_ = RtcpPacket::kHeaderLength + index;
RTC_DCHECK_LE(index, end_index);
return true;
}
std::unique_ptr<TransportFeedback> TransportFeedback::ParseFrom(
const uint8_t* buffer,
size_t length) {
CommonHeader header;
if (!header.Parse(buffer, length))
return nullptr;
if (header.type() != kPacketType || header.fmt() != kFeedbackMessageType)
return nullptr;
std::unique_ptr<TransportFeedback> parsed(new TransportFeedback);
if (!parsed->Parse(header))
return nullptr;
return parsed;
}
bool TransportFeedback::IsConsistent() const {
size_t packet_size = kTransportFeedbackHeaderSizeBytes;
std::vector<DeltaSize> delta_sizes;
LastChunk chunk_decoder;
for (uint16_t chunk : encoded_chunks_) {
chunk_decoder.Decode(chunk, kMaxReportedPackets);
chunk_decoder.AppendTo(&delta_sizes);
packet_size += kChunkSizeBytes;
}
if (!last_chunk_.Empty()) {
last_chunk_.AppendTo(&delta_sizes);
packet_size += kChunkSizeBytes;
}
if (num_seq_no_ != delta_sizes.size()) {
RTC_LOG(LS_ERROR) << delta_sizes.size() << " packets encoded. Expected "
<< num_seq_no_;
return false;
}
Timestamp timestamp = BaseTime();
auto packet_it = received_packets_.begin();
uint16_t seq_no = base_seq_no_;
for (DeltaSize delta_size : delta_sizes) {
if (delta_size > 0) {
if (packet_it == received_packets_.end()) {
RTC_LOG(LS_ERROR) << "Failed to find delta for seq_no " << seq_no;
return false;
}
if (packet_it->sequence_number() != seq_no) {
RTC_LOG(LS_ERROR) << "Expected to find delta for seq_no " << seq_no
<< ". Next delta is for "
<< packet_it->sequence_number();
return false;
}
if (delta_size == 1 &&
(packet_it->delta_ticks() < 0 || packet_it->delta_ticks() > 0xff)) {
RTC_LOG(LS_ERROR) << "Delta " << packet_it->delta_ticks()
<< " for seq_no " << seq_no
<< " doesn't fit into one byte";
return false;
}
timestamp += packet_it->delta();
++packet_it;
}
if (include_timestamps_) {
packet_size += delta_size;
}
++seq_no;
}
if (packet_it != received_packets_.end()) {
RTC_LOG(LS_ERROR) << "Unencoded delta for seq_no "
<< packet_it->sequence_number();
return false;
}
if (timestamp != last_timestamp_) {
RTC_LOG(LS_ERROR) << "Last timestamp mismatch. Calculated: "
<< ToLogString(timestamp)
<< ". Saved: " << ToLogString(last_timestamp_);
return false;
}
if (size_bytes_ != packet_size) {
RTC_LOG(LS_ERROR) << "Rtcp packet size mismatch. Calculated: "
<< packet_size << ". Saved: " << size_bytes_;
return false;
}
return true;
}
size_t TransportFeedback::BlockLength() const {
// Round size_bytes_ up to multiple of 32bits.
return (size_bytes_ + 3) & (~static_cast<size_t>(3));
}
size_t TransportFeedback::PaddingLength() const {
return BlockLength() - size_bytes_;
}
// Serialize packet.
bool TransportFeedback::Create(uint8_t* packet,
size_t* position,
size_t max_length,
PacketReadyCallback callback) const {
if (num_seq_no_ == 0)
return false;
while (*position + BlockLength() > max_length) {
if (!OnBufferFull(packet, position, callback))
return false;
}
const size_t position_end = *position + BlockLength();
const size_t padding_length = PaddingLength();
bool has_padding = padding_length > 0;
CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), has_padding,
packet, position);
CreateCommonFeedback(packet + *position);
*position += kCommonFeedbackLength;
ByteWriter<uint16_t>::WriteBigEndian(&packet[*position], base_seq_no_);
*position += 2;
ByteWriter<uint16_t>::WriteBigEndian(&packet[*position], num_seq_no_);
*position += 2;
ByteWriter<uint32_t, 3>::WriteBigEndian(&packet[*position], base_time_ticks_);
*position += 3;
packet[(*position)++] = feedback_seq_;
for (uint16_t chunk : encoded_chunks_) {
ByteWriter<uint16_t>::WriteBigEndian(&packet[*position], chunk);
*position += 2;
}
if (!last_chunk_.Empty()) {
uint16_t chunk = last_chunk_.EncodeLast();
ByteWriter<uint16_t>::WriteBigEndian(&packet[*position], chunk);
*position += 2;
}
if (include_timestamps_) {
for (const auto& received_packet : received_packets_) {
int16_t delta = received_packet.delta_ticks();
if (delta >= 0 && delta <= 0xFF) {
packet[(*position)++] = delta;
} else {
ByteWriter<int16_t>::WriteBigEndian(&packet[*position], delta);
*position += 2;
}
}
}
if (padding_length > 0) {
for (size_t i = 0; i < padding_length - 1; ++i) {
packet[(*position)++] = 0;
}
packet[(*position)++] = padding_length;
}
RTC_DCHECK_EQ(*position, position_end);
return true;
}
void TransportFeedback::Clear() {
num_seq_no_ = 0;
last_timestamp_ = BaseTime();
received_packets_.clear();
all_packets_.clear();
encoded_chunks_.clear();
last_chunk_.Clear();
size_bytes_ = kTransportFeedbackHeaderSizeBytes;
}
bool TransportFeedback::AddDeltaSize(DeltaSize delta_size) {
if (num_seq_no_ == kMaxReportedPackets)
return false;
size_t add_chunk_size = last_chunk_.Empty() ? kChunkSizeBytes : 0;
if (size_bytes_ + delta_size + add_chunk_size > kMaxSizeBytes)
return false;
if (last_chunk_.CanAdd(delta_size)) {
size_bytes_ += add_chunk_size;
last_chunk_.Add(delta_size);
++num_seq_no_;
return true;
}
if (size_bytes_ + delta_size + kChunkSizeBytes > kMaxSizeBytes)
return false;
encoded_chunks_.push_back(last_chunk_.Emit());
size_bytes_ += kChunkSizeBytes;
last_chunk_.Add(delta_size);
++num_seq_no_;
return true;
}
bool TransportFeedback::AddMissingPackets(size_t num_missing_packets) {
size_t new_num_seq_no = num_seq_no_ + num_missing_packets;
if (new_num_seq_no > kMaxReportedPackets) {
return false;
}
if (!last_chunk_.Empty()) {
while (num_missing_packets > 0 && last_chunk_.CanAdd(0)) {
last_chunk_.Add(0);
--num_missing_packets;
}
if (num_missing_packets == 0) {
num_seq_no_ = new_num_seq_no;
return true;
}
encoded_chunks_.push_back(last_chunk_.Emit());
}
RTC_DCHECK(last_chunk_.Empty());
size_t full_chunks = num_missing_packets / LastChunk::kMaxRunLengthCapacity;
size_t partial_chunk = num_missing_packets % LastChunk::kMaxRunLengthCapacity;
size_t num_chunks = full_chunks + (partial_chunk > 0 ? 1 : 0);
if (size_bytes_ + kChunkSizeBytes * num_chunks > kMaxSizeBytes) {
num_seq_no_ = (new_num_seq_no - num_missing_packets);
return false;
}
size_bytes_ += kChunkSizeBytes * num_chunks;
// T = 0, S = 0, run length = kMaxRunLengthCapacity, see EncodeRunLength().
encoded_chunks_.insert(encoded_chunks_.end(), full_chunks,
LastChunk::kMaxRunLengthCapacity);
last_chunk_.AddMissingPackets(partial_chunk);
num_seq_no_ = new_num_seq_no;
return true;
}
} // namespace rtcp
} // namespace webrtc

View file

@ -0,0 +1,185 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TRANSPORT_FEEDBACK_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TRANSPORT_FEEDBACK_H_
#include <array>
#include <memory>
#include <vector>
#include "absl/base/attributes.h"
#include "api/function_view.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h"
namespace webrtc {
namespace rtcp {
class CommonHeader;
class TransportFeedback : public Rtpfb {
public:
class ReceivedPacket {
public:
ReceivedPacket(uint16_t sequence_number, int16_t delta_ticks)
: sequence_number_(sequence_number), delta_ticks_(delta_ticks) {}
ReceivedPacket(const ReceivedPacket&) = default;
ReceivedPacket& operator=(const ReceivedPacket&) = default;
uint16_t sequence_number() const { return sequence_number_; }
int16_t delta_ticks() const { return delta_ticks_; }
TimeDelta delta() const { return delta_ticks_ * kDeltaTick; }
private:
uint16_t sequence_number_;
int16_t delta_ticks_;
};
// TODO(sprang): IANA reg?
static constexpr uint8_t kFeedbackMessageType = 15;
// Convert to multiples of 0.25ms.
static constexpr TimeDelta kDeltaTick = TimeDelta::Micros(250);
// Maximum number of packets (including missing) TransportFeedback can report.
static constexpr size_t kMaxReportedPackets = 0xffff;
TransportFeedback();
// If `include_timestamps` is set to false, the created packet will not
// contain the receive delta block.
explicit TransportFeedback(bool include_timestamps);
TransportFeedback(const TransportFeedback&);
TransportFeedback(TransportFeedback&&);
~TransportFeedback() override;
void SetBase(uint16_t base_sequence, // Seq# of first packet in this msg.
Timestamp ref_timestamp); // Reference timestamp for this msg.
void SetFeedbackSequenceNumber(uint8_t feedback_sequence);
// NOTE: This method requires increasing sequence numbers (excepting wraps).
bool AddReceivedPacket(uint16_t sequence_number, Timestamp timestamp);
const std::vector<ReceivedPacket>& GetReceivedPackets() const;
// Calls `handler` for all packets this feedback describes.
// For received packets pass receieve time as `delta_since_base` since the
// `BaseTime()`. For missed packets calls `handler` with `delta_since_base =
// PlusInfinity()`.
void ForAllPackets(
rtc::FunctionView<void(uint16_t sequence_number,
TimeDelta delta_since_base)> handler) const;
uint16_t GetBaseSequence() const;
// Returns number of packets (including missing) this feedback describes.
size_t GetPacketStatusCount() const { return num_seq_no_; }
// Get the reference time including any precision loss.
Timestamp BaseTime() const;
// Get the unwrapped delta between current base time and `prev_timestamp`.
TimeDelta GetBaseDelta(Timestamp prev_timestamp) const;
// Does the feedback packet contain timestamp information?
bool IncludeTimestamps() const { return include_timestamps_; }
bool Parse(const CommonHeader& packet);
static std::unique_ptr<TransportFeedback> ParseFrom(const uint8_t* buffer,
size_t length);
// Pre and postcondition for all public methods. Should always return true.
// This function is for tests.
bool IsConsistent() const;
size_t BlockLength() const override;
size_t PaddingLength() const;
bool Create(uint8_t* packet,
size_t* position,
size_t max_length,
PacketReadyCallback callback) const override;
private:
// Size in bytes of a delta time in rtcp packet.
// Valid values are 0 (packet wasn't received), 1 or 2.
using DeltaSize = uint8_t;
// Keeps DeltaSizes that can be encoded into single chunk if it is last chunk.
class LastChunk {
public:
using DeltaSize = TransportFeedback::DeltaSize;
static constexpr size_t kMaxRunLengthCapacity = 0x1fff;
LastChunk();
bool Empty() const;
void Clear();
// Return if delta sizes still can be encoded into single chunk with added
// `delta_size`.
bool CanAdd(DeltaSize delta_size) const;
// Add `delta_size`, assumes `CanAdd(delta_size)`,
void Add(DeltaSize delta_size);
// Equivalent to calling Add(0) `num_missing` times. Assumes `Empty()`.
void AddMissingPackets(size_t num_missing);
// Encode chunk as large as possible removing encoded delta sizes.
// Assume CanAdd() == false for some valid delta_size.
uint16_t Emit();
// Encode all stored delta_sizes into single chunk, pad with 0s if needed.
uint16_t EncodeLast() const;
// Decode up to `max_size` delta sizes from `chunk`.
void Decode(uint16_t chunk, size_t max_size);
// Appends content of the Lastchunk to `deltas`.
void AppendTo(std::vector<DeltaSize>* deltas) const;
private:
static constexpr size_t kMaxOneBitCapacity = 14;
static constexpr size_t kMaxTwoBitCapacity = 7;
static constexpr size_t kMaxVectorCapacity = kMaxOneBitCapacity;
static constexpr DeltaSize kLarge = 2;
uint16_t EncodeOneBit() const;
void DecodeOneBit(uint16_t chunk, size_t max_size);
uint16_t EncodeTwoBit(size_t size) const;
void DecodeTwoBit(uint16_t chunk, size_t max_size);
uint16_t EncodeRunLength() const;
void DecodeRunLength(uint16_t chunk, size_t max_size);
std::array<DeltaSize, kMaxVectorCapacity> delta_sizes_;
size_t size_;
bool all_same_;
bool has_large_delta_;
};
// Reset packet to consistent empty state.
void Clear();
bool AddDeltaSize(DeltaSize delta_size);
// Adds `num_missing_packets` deltas of size 0.
bool AddMissingPackets(size_t num_missing_packets);
uint16_t base_seq_no_;
uint16_t num_seq_no_;
uint32_t base_time_ticks_;
uint8_t feedback_seq_;
bool include_timestamps_;
Timestamp last_timestamp_;
std::vector<ReceivedPacket> received_packets_;
std::vector<ReceivedPacket> all_packets_;
// All but last encoded packet chunks.
std::vector<uint16_t> encoded_chunks_;
LastChunk last_chunk_;
size_t size_bytes_;
};
} // namespace rtcp
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TRANSPORT_FEEDBACK_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,421 @@
/*
* 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 MODULES_RTP_RTCP_SOURCE_RTCP_RECEIVER_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_RECEIVER_H_
#include <list>
#include <map>
#include <string>
#include <vector>
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "api/sequence_checker.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "modules/rtp_rtcp/include/report_block_data.h"
#include "modules/rtp_rtcp/include/rtcp_statistics.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/rtcp_nack_stats.h"
#include "modules/rtp_rtcp/source/rtcp_packet/dlrr.h"
#include "modules/rtp_rtcp/source/rtcp_packet/tmmb_item.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
#include "rtc_base/containers/flat_map.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/thread_annotations.h"
#include "system_wrappers/include/ntp_time.h"
namespace webrtc {
class ModuleRtpRtcpImpl2;
class VideoBitrateAllocationObserver;
namespace rtcp {
class CommonHeader;
class ReportBlock;
class Rrtr;
class TargetBitrate;
class TmmbItem;
} // namespace rtcp
class RTCPReceiver final {
public:
class ModuleRtpRtcp {
public:
virtual void SetTmmbn(std::vector<rtcp::TmmbItem> bounding_set) = 0;
virtual void OnRequestSendReport() = 0;
virtual void OnReceivedNack(
const std::vector<uint16_t>& nack_sequence_numbers) = 0;
virtual void OnReceivedRtcpReportBlocks(
rtc::ArrayView<const ReportBlockData> report_blocks) = 0;
protected:
virtual ~ModuleRtpRtcp() = default;
};
// Standardized stats derived from the non-sender RTT.
class NonSenderRttStats {
public:
NonSenderRttStats() = default;
NonSenderRttStats(const NonSenderRttStats&) = default;
NonSenderRttStats& operator=(const NonSenderRttStats&) = default;
~NonSenderRttStats() = default;
void Update(TimeDelta non_sender_rtt_seconds) {
round_trip_time_ = non_sender_rtt_seconds;
total_round_trip_time_ += non_sender_rtt_seconds;
round_trip_time_measurements_++;
}
void Invalidate() { round_trip_time_.reset(); }
// https://www.w3.org/TR/webrtc-stats/#dom-rtcremoteoutboundrtpstreamstats-roundtriptime
absl::optional<TimeDelta> round_trip_time() const {
return round_trip_time_;
}
// https://www.w3.org/TR/webrtc-stats/#dom-rtcremoteoutboundrtpstreamstats-totalroundtriptime
TimeDelta total_round_trip_time() const { return total_round_trip_time_; }
// https://www.w3.org/TR/webrtc-stats/#dom-rtcremoteoutboundrtpstreamstats-roundtriptimemeasurements
int round_trip_time_measurements() const {
return round_trip_time_measurements_;
}
private:
absl::optional<TimeDelta> round_trip_time_;
TimeDelta total_round_trip_time_ = TimeDelta::Zero();
int round_trip_time_measurements_ = 0;
};
RTCPReceiver(const RtpRtcpInterface::Configuration& config,
ModuleRtpRtcp* owner);
RTCPReceiver(const RtpRtcpInterface::Configuration& config,
ModuleRtpRtcpImpl2* owner);
~RTCPReceiver();
void IncomingPacket(rtc::ArrayView<const uint8_t> packet);
int64_t LastReceivedReportBlockMs() const;
void set_local_media_ssrc(uint32_t ssrc);
uint32_t local_media_ssrc() const;
void SetRemoteSSRC(uint32_t ssrc);
uint32_t RemoteSSRC() const;
bool receiver_only() const { return receiver_only_; }
// Returns stats based on the received RTCP Sender Reports.
absl::optional<RtpRtcpInterface::SenderReportStats> GetSenderReportStats()
const;
std::vector<rtcp::ReceiveTimeInfo> ConsumeReceivedXrReferenceTimeInfo();
absl::optional<TimeDelta> AverageRtt() const;
absl::optional<TimeDelta> LastRtt() const;
// Returns non-sender RTT metrics for the remote SSRC.
NonSenderRttStats GetNonSenderRTT() const;
void SetNonSenderRttMeasurement(bool enabled);
absl::optional<TimeDelta> GetAndResetXrRrRtt();
// Called once per second on the worker thread to do rtt calculations.
// Returns an optional rtt value if one is available.
absl::optional<TimeDelta> OnPeriodicRttUpdate(Timestamp newer_than,
bool sending);
// A snapshot of Report Blocks with additional data of interest to statistics.
// Within this list, the source SSRC is unique and ReportBlockData represents
// the latest Report Block that was received for that SSRC.
std::vector<ReportBlockData> GetLatestReportBlockData() const;
// Returns true if we haven't received an RTCP RR for several RTCP
// intervals, but only triggers true once.
bool RtcpRrTimeout();
// Returns true if we haven't received an RTCP RR telling the receive side
// has not received RTP packets for too long, i.e. extended highest sequence
// number hasn't increased for several RTCP intervals. The function only
// returns true once until a new RR is received.
bool RtcpRrSequenceNumberTimeout();
std::vector<rtcp::TmmbItem> TmmbrReceived();
// Return true if new bandwidth should be set.
bool UpdateTmmbrTimers();
std::vector<rtcp::TmmbItem> BoundingSet(bool* tmmbr_owner);
// Set new bandwidth and notify remote clients about it.
void NotifyTmmbrUpdated();
private:
#if RTC_DCHECK_IS_ON
class CustomSequenceChecker : public SequenceChecker {
public:
explicit CustomSequenceChecker(bool disable_checks)
: disable_checks_(disable_checks) {}
bool IsCurrent() const {
if (disable_checks_)
return true;
return SequenceChecker::IsCurrent();
}
private:
const bool disable_checks_;
};
#else
class CustomSequenceChecker : public SequenceChecker {
public:
explicit CustomSequenceChecker(bool) {}
};
#endif
// A lightweight inlined set of local SSRCs.
class RegisteredSsrcs {
public:
static constexpr size_t kMediaSsrcIndex = 0;
static constexpr size_t kMaxSsrcs = 3;
// Initializes the set of registered local SSRCS by extracting them from the
// provided `config`. The `disable_sequence_checker` flag is a workaround
// to be able to use a sequence checker without breaking downstream
// code that currently doesn't follow the same threading rules as webrtc.
RegisteredSsrcs(bool disable_sequence_checker,
const RtpRtcpInterface::Configuration& config);
// Indicates if `ssrc` is in the set of registered local SSRCs.
bool contains(uint32_t ssrc) const;
uint32_t media_ssrc() const;
void set_media_ssrc(uint32_t ssrc);
private:
RTC_NO_UNIQUE_ADDRESS CustomSequenceChecker packet_sequence_checker_;
absl::InlinedVector<uint32_t, kMaxSsrcs> ssrcs_
RTC_GUARDED_BY(packet_sequence_checker_);
};
struct PacketInformation;
// Structure for handing TMMBR and TMMBN rtcp messages (RFC5104,
// section 3.5.4).
struct TmmbrInformation {
struct TimedTmmbrItem {
rtcp::TmmbItem tmmbr_item;
Timestamp last_updated = Timestamp::Zero();
};
Timestamp last_time_received = Timestamp::Zero();
bool ready_for_delete = false;
std::vector<rtcp::TmmbItem> tmmbn;
std::map<uint32_t, TimedTmmbrItem> tmmbr;
};
// Structure for storing received RRTR RTCP messages (RFC3611, section 4.4).
struct RrtrInformation {
RrtrInformation(uint32_t ssrc,
uint32_t received_remote_mid_ntp_time,
uint32_t local_receive_mid_ntp_time)
: ssrc(ssrc),
received_remote_mid_ntp_time(received_remote_mid_ntp_time),
local_receive_mid_ntp_time(local_receive_mid_ntp_time) {}
uint32_t ssrc;
// Received NTP timestamp in compact representation.
uint32_t received_remote_mid_ntp_time;
// NTP time when the report was received in compact representation.
uint32_t local_receive_mid_ntp_time;
};
struct LastFirStatus {
LastFirStatus(Timestamp now, uint8_t sequence_number)
: request(now), sequence_number(sequence_number) {}
Timestamp request;
uint8_t sequence_number;
};
class RttStats {
public:
RttStats() = default;
RttStats(const RttStats&) = default;
RttStats& operator=(const RttStats&) = default;
void AddRtt(TimeDelta rtt);
TimeDelta last_rtt() const { return last_rtt_; }
TimeDelta average_rtt() const { return sum_rtt_ / num_rtts_; }
private:
TimeDelta last_rtt_ = TimeDelta::Zero();
TimeDelta sum_rtt_ = TimeDelta::Zero();
size_t num_rtts_ = 0;
};
bool ParseCompoundPacket(rtc::ArrayView<const uint8_t> packet,
PacketInformation* packet_information);
void TriggerCallbacksFromRtcpPacket(
const PacketInformation& packet_information);
TmmbrInformation* FindOrCreateTmmbrInfo(uint32_t remote_ssrc)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
// Update TmmbrInformation (if present) is alive.
void UpdateTmmbrRemoteIsAlive(uint32_t remote_ssrc)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
TmmbrInformation* GetTmmbrInformation(uint32_t remote_ssrc)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
bool HandleSenderReport(const rtcp::CommonHeader& rtcp_block,
PacketInformation* packet_information)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
bool HandleReceiverReport(const rtcp::CommonHeader& rtcp_block,
PacketInformation* packet_information)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
void HandleReportBlock(const rtcp::ReportBlock& report_block,
PacketInformation* packet_information,
uint32_t remote_ssrc)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
bool HandleSdes(const rtcp::CommonHeader& rtcp_block,
PacketInformation* packet_information)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
bool HandleXr(const rtcp::CommonHeader& rtcp_block,
PacketInformation* packet_information,
bool& contains_dlrr,
uint32_t& ssrc)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
void HandleXrReceiveReferenceTime(uint32_t sender_ssrc,
const rtcp::Rrtr& rrtr)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
void HandleXrDlrrReportBlock(uint32_t ssrc, const rtcp::ReceiveTimeInfo& rti)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
void HandleXrTargetBitrate(uint32_t ssrc,
const rtcp::TargetBitrate& target_bitrate,
PacketInformation* packet_information)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
bool HandleNack(const rtcp::CommonHeader& rtcp_block,
PacketInformation* packet_information)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
bool HandleApp(const rtcp::CommonHeader& rtcp_block,
PacketInformation* packet_information)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
bool HandleBye(const rtcp::CommonHeader& rtcp_block)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
bool HandlePli(const rtcp::CommonHeader& rtcp_block,
PacketInformation* packet_information)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
void HandlePsfbApp(const rtcp::CommonHeader& rtcp_block,
PacketInformation* packet_information)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
bool HandleTmmbr(const rtcp::CommonHeader& rtcp_block,
PacketInformation* packet_information)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
bool HandleTmmbn(const rtcp::CommonHeader& rtcp_block,
PacketInformation* packet_information)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
bool HandleSrReq(const rtcp::CommonHeader& rtcp_block,
PacketInformation* packet_information)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
bool HandleFir(const rtcp::CommonHeader& rtcp_block,
PacketInformation* packet_information)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
void HandleTransportFeedback(const rtcp::CommonHeader& rtcp_block,
PacketInformation* packet_information)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
bool RtcpRrTimeoutLocked(Timestamp now)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
bool RtcpRrSequenceNumberTimeoutLocked(Timestamp now)
RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
Clock* const clock_;
const bool receiver_only_;
ModuleRtpRtcp* const rtp_rtcp_;
// The set of registered local SSRCs.
RegisteredSsrcs registered_ssrcs_;
NetworkLinkRtcpObserver* const network_link_rtcp_observer_;
RtcpIntraFrameObserver* const rtcp_intra_frame_observer_;
RtcpLossNotificationObserver* const rtcp_loss_notification_observer_;
NetworkStateEstimateObserver* const network_state_estimate_observer_;
VideoBitrateAllocationObserver* const bitrate_allocation_observer_;
const TimeDelta report_interval_;
mutable Mutex rtcp_receiver_lock_;
uint32_t remote_ssrc_ RTC_GUARDED_BY(rtcp_receiver_lock_);
// Received sender report.
RtpRtcpInterface::SenderReportStats remote_sender_
RTC_GUARDED_BY(rtcp_receiver_lock_);
// Received RRTR information in ascending receive time order.
std::list<RrtrInformation> received_rrtrs_
RTC_GUARDED_BY(rtcp_receiver_lock_);
// Received RRTR information mapped by remote ssrc.
flat_map<uint32_t, std::list<RrtrInformation>::iterator>
received_rrtrs_ssrc_it_ RTC_GUARDED_BY(rtcp_receiver_lock_);
// Estimated rtt, nullopt when there is no valid estimate.
bool xr_rrtr_status_ RTC_GUARDED_BY(rtcp_receiver_lock_);
absl::optional<TimeDelta> xr_rr_rtt_;
Timestamp oldest_tmmbr_info_ RTC_GUARDED_BY(rtcp_receiver_lock_);
// Mapped by remote ssrc.
flat_map<uint32_t, TmmbrInformation> tmmbr_infos_
RTC_GUARDED_BY(rtcp_receiver_lock_);
// Round-Trip Time per remote sender ssrc.
flat_map<uint32_t, RttStats> rtts_ RTC_GUARDED_BY(rtcp_receiver_lock_);
// Non-sender Round-trip time per remote ssrc.
flat_map<uint32_t, NonSenderRttStats> non_sender_rtts_
RTC_GUARDED_BY(rtcp_receiver_lock_);
// Report blocks per local source ssrc.
flat_map<uint32_t, ReportBlockData> received_report_blocks_
RTC_GUARDED_BY(rtcp_receiver_lock_);
flat_map<uint32_t, LastFirStatus> last_fir_
RTC_GUARDED_BY(rtcp_receiver_lock_);
// The last time we received an RTCP Report block for this module.
Timestamp last_received_rb_ RTC_GUARDED_BY(rtcp_receiver_lock_) =
Timestamp::PlusInfinity();
// The time we last received an RTCP RR telling we have successfully
// delivered RTP packet to the remote side.
Timestamp last_increased_sequence_number_ = Timestamp::PlusInfinity();
RtcpCnameCallback* const cname_callback_;
ReportBlockDataObserver* const report_block_data_observer_;
RtcpPacketTypeCounterObserver* const packet_type_counter_observer_;
RtcpPacketTypeCounter packet_type_counter_;
RtcpNackStats nack_stats_;
size_t num_skipped_packets_;
Timestamp last_skipped_packets_warning_;
};
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_RECEIVER_H_

View file

@ -0,0 +1,910 @@
/*
* 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 "modules/rtp_rtcp/source/rtcp_sender.h"
#include <string.h> // memcpy
#include <algorithm> // std::min
#include <memory>
#include <utility>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/rtp_headers.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h"
#include "modules/rtp_rtcp/source/rtcp_packet/app.h"
#include "modules/rtp_rtcp/source/rtcp_packet/bye.h"
#include "modules/rtp_rtcp/source/rtcp_packet/compound_packet.h"
#include "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h"
#include "modules/rtp_rtcp/source/rtcp_packet/fir.h"
#include "modules/rtp_rtcp/source/rtcp_packet/loss_notification.h"
#include "modules/rtp_rtcp/source/rtcp_packet/nack.h"
#include "modules/rtp_rtcp/source/rtcp_packet/pli.h"
#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h"
#include "modules/rtp_rtcp/source/rtcp_packet/remb.h"
#include "modules/rtp_rtcp/source/rtcp_packet/sdes.h"
#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
#include "modules/rtp_rtcp/source/rtcp_packet/tmmbn.h"
#include "modules/rtp_rtcp/source/rtcp_packet/tmmbr.h"
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
#include "modules/rtp_rtcp/source/time_util.h"
#include "modules/rtp_rtcp/source/tmmbr_help.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/trace_event.h"
namespace webrtc {
namespace {
const uint32_t kRtcpAnyExtendedReports = kRtcpXrReceiverReferenceTime |
kRtcpXrDlrrReportBlock |
kRtcpXrTargetBitrate;
constexpr int32_t kDefaultVideoReportInterval = 1000;
constexpr int32_t kDefaultAudioReportInterval = 5000;
} // namespace
// Helper to put several RTCP packets into lower layer datagram RTCP packet.
class RTCPSender::PacketSender {
public:
PacketSender(rtcp::RtcpPacket::PacketReadyCallback callback,
size_t max_packet_size)
: callback_(callback), max_packet_size_(max_packet_size) {
RTC_CHECK_LE(max_packet_size, IP_PACKET_SIZE);
}
~PacketSender() { RTC_DCHECK_EQ(index_, 0) << "Unsent rtcp packet."; }
// Appends a packet to pending compound packet.
// Sends rtcp packet if buffer is full and resets the buffer.
void AppendPacket(const rtcp::RtcpPacket& packet) {
packet.Create(buffer_, &index_, max_packet_size_, callback_);
}
// Sends pending rtcp packet.
void Send() {
if (index_ > 0) {
callback_(rtc::ArrayView<const uint8_t>(buffer_, index_));
index_ = 0;
}
}
private:
const rtcp::RtcpPacket::PacketReadyCallback callback_;
const size_t max_packet_size_;
size_t index_ = 0;
uint8_t buffer_[IP_PACKET_SIZE];
};
RTCPSender::FeedbackState::FeedbackState()
: packets_sent(0),
media_bytes_sent(0),
send_bitrate(DataRate::Zero()),
remote_sr(0),
receiver(nullptr) {}
RTCPSender::FeedbackState::FeedbackState(const FeedbackState&) = default;
RTCPSender::FeedbackState::FeedbackState(FeedbackState&&) = default;
RTCPSender::FeedbackState::~FeedbackState() = default;
class RTCPSender::RtcpContext {
public:
RtcpContext(const FeedbackState& feedback_state,
int32_t nack_size,
const uint16_t* nack_list,
Timestamp now)
: feedback_state_(feedback_state),
nack_size_(nack_size),
nack_list_(nack_list),
now_(now) {}
const FeedbackState& feedback_state_;
const int32_t nack_size_;
const uint16_t* nack_list_;
const Timestamp now_;
};
RTCPSender::Configuration RTCPSender::Configuration::FromRtpRtcpConfiguration(
const RtpRtcpInterface::Configuration& configuration) {
RTCPSender::Configuration result;
result.audio = configuration.audio;
result.local_media_ssrc = configuration.local_media_ssrc;
result.clock = configuration.clock;
result.outgoing_transport = configuration.outgoing_transport;
result.non_sender_rtt_measurement = configuration.non_sender_rtt_measurement;
result.event_log = configuration.event_log;
if (configuration.rtcp_report_interval_ms) {
result.rtcp_report_interval =
TimeDelta::Millis(configuration.rtcp_report_interval_ms);
}
result.receive_statistics = configuration.receive_statistics;
result.rtcp_packet_type_counter_observer =
configuration.rtcp_packet_type_counter_observer;
return result;
}
RTCPSender::RTCPSender(Configuration config)
: audio_(config.audio),
ssrc_(config.local_media_ssrc),
clock_(config.clock),
random_(clock_->TimeInMicroseconds()),
method_(RtcpMode::kOff),
event_log_(config.event_log),
transport_(config.outgoing_transport),
report_interval_(config.rtcp_report_interval.value_or(
TimeDelta::Millis(config.audio ? kDefaultAudioReportInterval
: kDefaultVideoReportInterval))),
schedule_next_rtcp_send_evaluation_function_(
std::move(config.schedule_next_rtcp_send_evaluation_function)),
sending_(false),
timestamp_offset_(0),
last_rtp_timestamp_(0),
remote_ssrc_(0),
receive_statistics_(config.receive_statistics),
sequence_number_fir_(0),
remb_bitrate_(0),
tmmbr_send_bps_(0),
packet_oh_send_(0),
max_packet_size_(IP_PACKET_SIZE - 28), // IPv4 + UDP by default.
xr_send_receiver_reference_time_enabled_(
config.non_sender_rtt_measurement),
packet_type_counter_observer_(config.rtcp_packet_type_counter_observer),
send_video_bitrate_allocation_(false),
last_payload_type_(-1) {
RTC_DCHECK(transport_ != nullptr);
builders_[kRtcpSr] = &RTCPSender::BuildSR;
builders_[kRtcpRr] = &RTCPSender::BuildRR;
builders_[kRtcpSdes] = &RTCPSender::BuildSDES;
builders_[kRtcpPli] = &RTCPSender::BuildPLI;
builders_[kRtcpFir] = &RTCPSender::BuildFIR;
builders_[kRtcpRemb] = &RTCPSender::BuildREMB;
builders_[kRtcpBye] = &RTCPSender::BuildBYE;
builders_[kRtcpLossNotification] = &RTCPSender::BuildLossNotification;
builders_[kRtcpTmmbr] = &RTCPSender::BuildTMMBR;
builders_[kRtcpTmmbn] = &RTCPSender::BuildTMMBN;
builders_[kRtcpNack] = &RTCPSender::BuildNACK;
builders_[kRtcpAnyExtendedReports] = &RTCPSender::BuildExtendedReports;
}
RTCPSender::~RTCPSender() {}
RtcpMode RTCPSender::Status() const {
MutexLock lock(&mutex_rtcp_sender_);
return method_;
}
void RTCPSender::SetRTCPStatus(RtcpMode new_method) {
MutexLock lock(&mutex_rtcp_sender_);
if (new_method == RtcpMode::kOff) {
next_time_to_send_rtcp_ = absl::nullopt;
} else if (method_ == RtcpMode::kOff) {
// When switching on, reschedule the next packet
SetNextRtcpSendEvaluationDuration(report_interval_ / 2);
}
method_ = new_method;
}
bool RTCPSender::Sending() const {
MutexLock lock(&mutex_rtcp_sender_);
return sending_;
}
void RTCPSender::SetSendingStatus(const FeedbackState& feedback_state,
bool sending) {
MutexLock lock(&mutex_rtcp_sender_);
sending_ = sending;
}
void RTCPSender::SetNonSenderRttMeasurement(bool enabled) {
MutexLock lock(&mutex_rtcp_sender_);
xr_send_receiver_reference_time_enabled_ = enabled;
}
int32_t RTCPSender::SendLossNotification(const FeedbackState& feedback_state,
uint16_t last_decoded_seq_num,
uint16_t last_received_seq_num,
bool decodability_flag,
bool buffering_allowed) {
int32_t error_code = -1;
auto callback = [&](rtc::ArrayView<const uint8_t> packet) {
transport_->SendRtcp(packet);
error_code = 0;
if (event_log_) {
event_log_->Log(std::make_unique<RtcEventRtcpPacketOutgoing>(packet));
}
};
absl::optional<PacketSender> sender;
{
MutexLock lock(&mutex_rtcp_sender_);
if (!loss_notification_.Set(last_decoded_seq_num, last_received_seq_num,
decodability_flag)) {
return -1;
}
SetFlag(kRtcpLossNotification, /*is_volatile=*/true);
if (buffering_allowed) {
// The loss notification will be batched with additional feedback
// messages.
return 0;
}
sender.emplace(callback, max_packet_size_);
auto result = ComputeCompoundRTCPPacket(
feedback_state, RTCPPacketType::kRtcpLossNotification, 0, nullptr,
*sender);
if (result) {
return *result;
}
}
sender->Send();
return error_code;
}
void RTCPSender::SetRemb(int64_t bitrate_bps, std::vector<uint32_t> ssrcs) {
RTC_CHECK_GE(bitrate_bps, 0);
MutexLock lock(&mutex_rtcp_sender_);
if (method_ == RtcpMode::kOff) {
RTC_LOG(LS_WARNING) << "Can't send RTCP if it is disabled.";
return;
}
remb_bitrate_ = bitrate_bps;
remb_ssrcs_ = std::move(ssrcs);
SetFlag(kRtcpRemb, /*is_volatile=*/false);
// Send a REMB immediately if we have a new REMB. The frequency of REMBs is
// throttled by the caller.
SetNextRtcpSendEvaluationDuration(TimeDelta::Zero());
}
void RTCPSender::UnsetRemb() {
MutexLock lock(&mutex_rtcp_sender_);
// Stop sending REMB each report until it is reenabled and REMB data set.
ConsumeFlag(kRtcpRemb, /*forced=*/true);
}
bool RTCPSender::TMMBR() const {
MutexLock lock(&mutex_rtcp_sender_);
return IsFlagPresent(RTCPPacketType::kRtcpTmmbr);
}
void RTCPSender::SetMaxRtpPacketSize(size_t max_packet_size) {
MutexLock lock(&mutex_rtcp_sender_);
max_packet_size_ = max_packet_size;
}
void RTCPSender::SetTimestampOffset(uint32_t timestamp_offset) {
MutexLock lock(&mutex_rtcp_sender_);
timestamp_offset_ = timestamp_offset;
}
void RTCPSender::SetLastRtpTime(uint32_t rtp_timestamp,
absl::optional<Timestamp> capture_time,
absl::optional<int8_t> payload_type) {
MutexLock lock(&mutex_rtcp_sender_);
// For compatibility with clients who don't set payload type correctly on all
// calls.
if (payload_type.has_value()) {
last_payload_type_ = *payload_type;
}
last_rtp_timestamp_ = rtp_timestamp;
if (!capture_time.has_value()) {
// We don't currently get a capture time from VoiceEngine.
last_frame_capture_time_ = clock_->CurrentTime();
} else {
last_frame_capture_time_ = *capture_time;
}
}
void RTCPSender::SetRtpClockRate(int8_t payload_type, int rtp_clock_rate_hz) {
MutexLock lock(&mutex_rtcp_sender_);
rtp_clock_rates_khz_[payload_type] = rtp_clock_rate_hz / 1000;
}
uint32_t RTCPSender::SSRC() const {
MutexLock lock(&mutex_rtcp_sender_);
return ssrc_;
}
void RTCPSender::SetSsrc(uint32_t ssrc) {
MutexLock lock(&mutex_rtcp_sender_);
ssrc_ = ssrc;
}
void RTCPSender::SetRemoteSSRC(uint32_t ssrc) {
MutexLock lock(&mutex_rtcp_sender_);
remote_ssrc_ = ssrc;
}
int32_t RTCPSender::SetCNAME(absl::string_view c_name) {
RTC_DCHECK_LT(c_name.size(), RTCP_CNAME_SIZE);
MutexLock lock(&mutex_rtcp_sender_);
cname_ = std::string(c_name);
return 0;
}
bool RTCPSender::TimeToSendRTCPReport(bool send_keyframe_before_rtp) const {
Timestamp now = clock_->CurrentTime();
MutexLock lock(&mutex_rtcp_sender_);
RTC_DCHECK(
(method_ == RtcpMode::kOff && !next_time_to_send_rtcp_.has_value()) ||
(method_ != RtcpMode::kOff && next_time_to_send_rtcp_.has_value()));
if (method_ == RtcpMode::kOff)
return false;
if (!audio_ && send_keyframe_before_rtp) {
// For video key-frames we want to send the RTCP before the large key-frame
// if we have a 100 ms margin
now += TimeDelta::Millis(100);
}
return now >= *next_time_to_send_rtcp_;
}
void RTCPSender::BuildSR(const RtcpContext& ctx, PacketSender& sender) {
// Timestamp shouldn't be estimated before first media frame.
RTC_DCHECK(last_frame_capture_time_.has_value());
// The timestamp of this RTCP packet should be estimated as the timestamp of
// the frame being captured at this moment. We are calculating that
// timestamp as the last frame's timestamp + the time since the last frame
// was captured.
int rtp_rate = rtp_clock_rates_khz_[last_payload_type_];
if (rtp_rate <= 0) {
rtp_rate =
(audio_ ? kBogusRtpRateForAudioRtcp : kVideoPayloadTypeFrequency) /
1000;
}
// Round now_us_ to the closest millisecond, because Ntp time is rounded
// when converted to milliseconds,
uint32_t rtp_timestamp =
timestamp_offset_ + last_rtp_timestamp_ +
((ctx.now_.us() + 500) / 1000 - last_frame_capture_time_->ms()) *
rtp_rate;
rtcp::SenderReport report;
report.SetSenderSsrc(ssrc_);
report.SetNtp(clock_->ConvertTimestampToNtpTime(ctx.now_));
report.SetRtpTimestamp(rtp_timestamp);
report.SetPacketCount(ctx.feedback_state_.packets_sent);
report.SetOctetCount(ctx.feedback_state_.media_bytes_sent);
report.SetReportBlocks(CreateReportBlocks(ctx.feedback_state_));
sender.AppendPacket(report);
}
void RTCPSender::BuildSDES(const RtcpContext& ctx, PacketSender& sender) {
size_t length_cname = cname_.length();
RTC_CHECK_LT(length_cname, RTCP_CNAME_SIZE);
rtcp::Sdes sdes;
sdes.AddCName(ssrc_, cname_);
sender.AppendPacket(sdes);
}
void RTCPSender::BuildRR(const RtcpContext& ctx, PacketSender& sender) {
rtcp::ReceiverReport report;
report.SetSenderSsrc(ssrc_);
report.SetReportBlocks(CreateReportBlocks(ctx.feedback_state_));
if (method_ == RtcpMode::kCompound || !report.report_blocks().empty()) {
sender.AppendPacket(report);
}
}
void RTCPSender::BuildPLI(const RtcpContext& ctx, PacketSender& sender) {
rtcp::Pli pli;
pli.SetSenderSsrc(ssrc_);
pli.SetMediaSsrc(remote_ssrc_);
++packet_type_counter_.pli_packets;
sender.AppendPacket(pli);
}
void RTCPSender::BuildFIR(const RtcpContext& ctx, PacketSender& sender) {
++sequence_number_fir_;
rtcp::Fir fir;
fir.SetSenderSsrc(ssrc_);
fir.AddRequestTo(remote_ssrc_, sequence_number_fir_);
++packet_type_counter_.fir_packets;
sender.AppendPacket(fir);
}
void RTCPSender::BuildREMB(const RtcpContext& ctx, PacketSender& sender) {
rtcp::Remb remb;
remb.SetSenderSsrc(ssrc_);
remb.SetBitrateBps(remb_bitrate_);
remb.SetSsrcs(remb_ssrcs_);
sender.AppendPacket(remb);
}
void RTCPSender::SetTargetBitrate(unsigned int target_bitrate) {
MutexLock lock(&mutex_rtcp_sender_);
tmmbr_send_bps_ = target_bitrate;
}
void RTCPSender::BuildTMMBR(const RtcpContext& ctx, PacketSender& sender) {
if (ctx.feedback_state_.receiver == nullptr)
return;
// Before sending the TMMBR check the received TMMBN, only an owner is
// allowed to raise the bitrate:
// * If the sender is an owner of the TMMBN -> send TMMBR
// * If not an owner but the TMMBR would enter the TMMBN -> send TMMBR
// get current bounding set from RTCP receiver
bool tmmbr_owner = false;
// holding mutex_rtcp_sender_ while calling RTCPreceiver which
// will accuire criticalSectionRTCPReceiver_ is a potental deadlock but
// since RTCPreceiver is not doing the reverse we should be fine
std::vector<rtcp::TmmbItem> candidates =
ctx.feedback_state_.receiver->BoundingSet(&tmmbr_owner);
if (!candidates.empty()) {
for (const auto& candidate : candidates) {
if (candidate.bitrate_bps() == tmmbr_send_bps_ &&
candidate.packet_overhead() == packet_oh_send_) {
// Do not send the same tuple.
return;
}
}
if (!tmmbr_owner) {
// Use received bounding set as candidate set.
// Add current tuple.
candidates.emplace_back(ssrc_, tmmbr_send_bps_, packet_oh_send_);
// Find bounding set.
std::vector<rtcp::TmmbItem> bounding =
TMMBRHelp::FindBoundingSet(std::move(candidates));
tmmbr_owner = TMMBRHelp::IsOwner(bounding, ssrc_);
if (!tmmbr_owner) {
// Did not enter bounding set, no meaning to send this request.
return;
}
}
}
if (!tmmbr_send_bps_)
return;
rtcp::Tmmbr tmmbr;
tmmbr.SetSenderSsrc(ssrc_);
rtcp::TmmbItem request;
request.set_ssrc(remote_ssrc_);
request.set_bitrate_bps(tmmbr_send_bps_);
request.set_packet_overhead(packet_oh_send_);
tmmbr.AddTmmbr(request);
sender.AppendPacket(tmmbr);
}
void RTCPSender::BuildTMMBN(const RtcpContext& ctx, PacketSender& sender) {
rtcp::Tmmbn tmmbn;
tmmbn.SetSenderSsrc(ssrc_);
for (const rtcp::TmmbItem& tmmbr : tmmbn_to_send_) {
if (tmmbr.bitrate_bps() > 0) {
tmmbn.AddTmmbr(tmmbr);
}
}
sender.AppendPacket(tmmbn);
}
void RTCPSender::BuildAPP(const RtcpContext& ctx, PacketSender& sender) {
rtcp::App app;
app.SetSenderSsrc(ssrc_);
sender.AppendPacket(app);
}
void RTCPSender::BuildLossNotification(const RtcpContext& ctx,
PacketSender& sender) {
loss_notification_.SetSenderSsrc(ssrc_);
loss_notification_.SetMediaSsrc(remote_ssrc_);
sender.AppendPacket(loss_notification_);
}
void RTCPSender::BuildNACK(const RtcpContext& ctx, PacketSender& sender) {
rtcp::Nack nack;
nack.SetSenderSsrc(ssrc_);
nack.SetMediaSsrc(remote_ssrc_);
nack.SetPacketIds(ctx.nack_list_, ctx.nack_size_);
// Report stats.
for (int idx = 0; idx < ctx.nack_size_; ++idx) {
nack_stats_.ReportRequest(ctx.nack_list_[idx]);
}
packet_type_counter_.nack_requests = nack_stats_.requests();
packet_type_counter_.unique_nack_requests = nack_stats_.unique_requests();
++packet_type_counter_.nack_packets;
sender.AppendPacket(nack);
}
void RTCPSender::BuildBYE(const RtcpContext& ctx, PacketSender& sender) {
rtcp::Bye bye;
bye.SetSenderSsrc(ssrc_);
bye.SetCsrcs(csrcs_);
sender.AppendPacket(bye);
}
void RTCPSender::BuildExtendedReports(const RtcpContext& ctx,
PacketSender& sender) {
rtcp::ExtendedReports xr;
xr.SetSenderSsrc(ssrc_);
if (!sending_ && xr_send_receiver_reference_time_enabled_) {
rtcp::Rrtr rrtr;
rrtr.SetNtp(clock_->ConvertTimestampToNtpTime(ctx.now_));
xr.SetRrtr(rrtr);
}
for (const rtcp::ReceiveTimeInfo& rti : ctx.feedback_state_.last_xr_rtis) {
xr.AddDlrrItem(rti);
}
if (send_video_bitrate_allocation_) {
rtcp::TargetBitrate target_bitrate;
for (int sl = 0; sl < kMaxSpatialLayers; ++sl) {
for (int tl = 0; tl < kMaxTemporalStreams; ++tl) {
if (video_bitrate_allocation_.HasBitrate(sl, tl)) {
target_bitrate.AddTargetBitrate(
sl, tl, video_bitrate_allocation_.GetBitrate(sl, tl) / 1000);
}
}
}
xr.SetTargetBitrate(target_bitrate);
send_video_bitrate_allocation_ = false;
}
sender.AppendPacket(xr);
}
int32_t RTCPSender::SendRTCP(const FeedbackState& feedback_state,
RTCPPacketType packet_type,
int32_t nack_size,
const uint16_t* nack_list) {
int32_t error_code = -1;
auto callback = [&](rtc::ArrayView<const uint8_t> packet) {
if (transport_->SendRtcp(packet)) {
error_code = 0;
if (event_log_) {
event_log_->Log(std::make_unique<RtcEventRtcpPacketOutgoing>(packet));
}
}
};
absl::optional<PacketSender> sender;
{
MutexLock lock(&mutex_rtcp_sender_);
sender.emplace(callback, max_packet_size_);
auto result = ComputeCompoundRTCPPacket(feedback_state, packet_type,
nack_size, nack_list, *sender);
if (result) {
return *result;
}
}
sender->Send();
return error_code;
}
absl::optional<int32_t> RTCPSender::ComputeCompoundRTCPPacket(
const FeedbackState& feedback_state,
RTCPPacketType packet_type,
int32_t nack_size,
const uint16_t* nack_list,
PacketSender& sender) {
if (method_ == RtcpMode::kOff) {
RTC_LOG(LS_WARNING) << "Can't send RTCP if it is disabled.";
return -1;
}
// Add the flag as volatile. Non volatile entries will not be overwritten.
// The new volatile flag will be consumed by the end of this call.
SetFlag(packet_type, true);
// Prevent sending streams to send SR before any media has been sent.
const bool can_calculate_rtp_timestamp = last_frame_capture_time_.has_value();
if (!can_calculate_rtp_timestamp) {
bool consumed_sr_flag = ConsumeFlag(kRtcpSr);
bool consumed_report_flag = sending_ && ConsumeFlag(kRtcpReport);
bool sender_report = consumed_report_flag || consumed_sr_flag;
if (sender_report && AllVolatileFlagsConsumed()) {
// This call was for Sender Report and nothing else.
return 0;
}
if (sending_ && method_ == RtcpMode::kCompound) {
// Not allowed to send any RTCP packet without sender report.
return -1;
}
}
// We need to send our NTP even if we haven't received any reports.
RtcpContext context(feedback_state, nack_size, nack_list,
clock_->CurrentTime());
PrepareReport(feedback_state);
bool create_bye = false;
auto it = report_flags_.begin();
while (it != report_flags_.end()) {
uint32_t rtcp_packet_type = it->type;
if (it->is_volatile) {
report_flags_.erase(it++);
} else {
++it;
}
// If there is a BYE, don't append now - save it and append it
// at the end later.
if (rtcp_packet_type == kRtcpBye) {
create_bye = true;
continue;
}
auto builder_it = builders_.find(rtcp_packet_type);
if (builder_it == builders_.end()) {
RTC_DCHECK_NOTREACHED()
<< "Could not find builder for packet type " << rtcp_packet_type;
} else {
BuilderFunc func = builder_it->second;
(this->*func)(context, sender);
}
}
// Append the BYE now at the end
if (create_bye) {
BuildBYE(context, sender);
}
if (packet_type_counter_observer_ != nullptr) {
packet_type_counter_observer_->RtcpPacketTypesCounterUpdated(
remote_ssrc_, packet_type_counter_);
}
RTC_DCHECK(AllVolatileFlagsConsumed());
return absl::nullopt;
}
TimeDelta RTCPSender::ComputeTimeUntilNextReport(DataRate send_bitrate) {
/*
For audio we use a configurable interval (default: 5 seconds)
For video we use a configurable interval (default: 1 second)
for a BW smaller than ~200 kbit/s, technicaly we break the max 5% RTCP
BW for video but that should be extremely rare
From RFC 3550, https://www.rfc-editor.org/rfc/rfc3550#section-6.2
The RECOMMENDED value for the reduced minimum in seconds is 360
divided by the session bandwidth in kilobits/second. This minimum
is smaller than 5 seconds for bandwidths greater than 72 kb/s.
The interval between RTCP packets is varied randomly over the
range [0.5,1.5] times the calculated interval to avoid unintended
synchronization of all participants
*/
TimeDelta min_interval = report_interval_;
if (!audio_ && sending_ && send_bitrate > DataRate::BitsPerSec(72'000)) {
// Calculate bandwidth for video; 360 / send bandwidth in kbit/s per
// https://www.rfc-editor.org/rfc/rfc3550#section-6.2 recommendation.
min_interval = std::min(TimeDelta::Seconds(360) / send_bitrate.kbps(),
report_interval_);
}
// The interval between RTCP packets is varied randomly over the
// range [1/2,3/2] times the calculated interval.
int min_interval_int = rtc::dchecked_cast<int>(min_interval.ms());
TimeDelta time_to_next = TimeDelta::Millis(
random_.Rand(min_interval_int * 1 / 2, min_interval_int * 3 / 2));
// To be safer clamp the result.
return std::max(time_to_next, TimeDelta::Millis(1));
}
void RTCPSender::PrepareReport(const FeedbackState& feedback_state) {
bool generate_report;
if (IsFlagPresent(kRtcpSr) || IsFlagPresent(kRtcpRr)) {
// Report type already explicitly set, don't automatically populate.
generate_report = true;
RTC_DCHECK(ConsumeFlag(kRtcpReport) == false);
} else {
generate_report =
(ConsumeFlag(kRtcpReport) && method_ == RtcpMode::kReducedSize) ||
method_ == RtcpMode::kCompound;
if (generate_report)
SetFlag(sending_ ? kRtcpSr : kRtcpRr, true);
}
if (IsFlagPresent(kRtcpSr) || (IsFlagPresent(kRtcpRr) && !cname_.empty()))
SetFlag(kRtcpSdes, true);
if (generate_report) {
if ((!sending_ && xr_send_receiver_reference_time_enabled_) ||
!feedback_state.last_xr_rtis.empty() ||
send_video_bitrate_allocation_) {
SetFlag(kRtcpAnyExtendedReports, true);
}
SetNextRtcpSendEvaluationDuration(
ComputeTimeUntilNextReport(feedback_state.send_bitrate));
// RtcpSender expected to be used for sending either just sender reports
// or just receiver reports.
RTC_DCHECK(!(IsFlagPresent(kRtcpSr) && IsFlagPresent(kRtcpRr)));
}
}
std::vector<rtcp::ReportBlock> RTCPSender::CreateReportBlocks(
const FeedbackState& feedback_state) {
std::vector<rtcp::ReportBlock> result;
if (!receive_statistics_)
return result;
result = receive_statistics_->RtcpReportBlocks(RTCP_MAX_REPORT_BLOCKS);
if (!result.empty() && feedback_state.last_rr.Valid()) {
// Get our NTP as late as possible to avoid a race.
uint32_t now = CompactNtp(clock_->CurrentNtpTime());
uint32_t receive_time = CompactNtp(feedback_state.last_rr);
uint32_t delay_since_last_sr = now - receive_time;
for (auto& report_block : result) {
report_block.SetLastSr(feedback_state.remote_sr);
report_block.SetDelayLastSr(delay_since_last_sr);
}
}
return result;
}
void RTCPSender::SetCsrcs(const std::vector<uint32_t>& csrcs) {
RTC_DCHECK_LE(csrcs.size(), kRtpCsrcSize);
MutexLock lock(&mutex_rtcp_sender_);
csrcs_ = csrcs;
}
void RTCPSender::SetTmmbn(std::vector<rtcp::TmmbItem> bounding_set) {
MutexLock lock(&mutex_rtcp_sender_);
tmmbn_to_send_ = std::move(bounding_set);
SetFlag(kRtcpTmmbn, true);
}
void RTCPSender::SetFlag(uint32_t type, bool is_volatile) {
if (type & kRtcpAnyExtendedReports) {
report_flags_.insert(ReportFlag(kRtcpAnyExtendedReports, is_volatile));
} else {
report_flags_.insert(ReportFlag(type, is_volatile));
}
}
bool RTCPSender::IsFlagPresent(uint32_t type) const {
return report_flags_.find(ReportFlag(type, false)) != report_flags_.end();
}
bool RTCPSender::ConsumeFlag(uint32_t type, bool forced) {
auto it = report_flags_.find(ReportFlag(type, false));
if (it == report_flags_.end())
return false;
if (it->is_volatile || forced)
report_flags_.erase((it));
return true;
}
bool RTCPSender::AllVolatileFlagsConsumed() const {
for (const ReportFlag& flag : report_flags_) {
if (flag.is_volatile)
return false;
}
return true;
}
void RTCPSender::SetVideoBitrateAllocation(
const VideoBitrateAllocation& bitrate) {
MutexLock lock(&mutex_rtcp_sender_);
if (method_ == RtcpMode::kOff) {
RTC_LOG(LS_WARNING) << "Can't send RTCP if it is disabled.";
return;
}
// Check if this allocation is first ever, or has a different set of
// spatial/temporal layers signaled and enabled, if so trigger an rtcp report
// as soon as possible.
absl::optional<VideoBitrateAllocation> new_bitrate =
CheckAndUpdateLayerStructure(bitrate);
if (new_bitrate) {
video_bitrate_allocation_ = *new_bitrate;
RTC_LOG(LS_INFO) << "Emitting TargetBitrate XR for SSRC " << ssrc_
<< " with new layers enabled/disabled: "
<< video_bitrate_allocation_.ToString();
SetNextRtcpSendEvaluationDuration(TimeDelta::Zero());
} else {
video_bitrate_allocation_ = bitrate;
}
send_video_bitrate_allocation_ = true;
SetFlag(kRtcpAnyExtendedReports, true);
}
absl::optional<VideoBitrateAllocation> RTCPSender::CheckAndUpdateLayerStructure(
const VideoBitrateAllocation& bitrate) const {
absl::optional<VideoBitrateAllocation> updated_bitrate;
for (size_t si = 0; si < kMaxSpatialLayers; ++si) {
for (size_t ti = 0; ti < kMaxTemporalStreams; ++ti) {
if (!updated_bitrate &&
(bitrate.HasBitrate(si, ti) !=
video_bitrate_allocation_.HasBitrate(si, ti) ||
(bitrate.GetBitrate(si, ti) == 0) !=
(video_bitrate_allocation_.GetBitrate(si, ti) == 0))) {
updated_bitrate = bitrate;
}
if (video_bitrate_allocation_.GetBitrate(si, ti) > 0 &&
bitrate.GetBitrate(si, ti) == 0) {
// Make sure this stream disabling is explicitly signaled.
updated_bitrate->SetBitrate(si, ti, 0);
}
}
}
return updated_bitrate;
}
void RTCPSender::SendCombinedRtcpPacket(
std::vector<std::unique_ptr<rtcp::RtcpPacket>> rtcp_packets) {
size_t max_packet_size;
uint32_t ssrc;
{
MutexLock lock(&mutex_rtcp_sender_);
if (method_ == RtcpMode::kOff) {
RTC_LOG(LS_WARNING) << "Can't send RTCP if it is disabled.";
return;
}
max_packet_size = max_packet_size_;
ssrc = ssrc_;
}
RTC_DCHECK_LE(max_packet_size, IP_PACKET_SIZE);
auto callback = [&](rtc::ArrayView<const uint8_t> packet) {
if (transport_->SendRtcp(packet)) {
if (event_log_)
event_log_->Log(std::make_unique<RtcEventRtcpPacketOutgoing>(packet));
}
};
PacketSender sender(callback, max_packet_size);
for (auto& rtcp_packet : rtcp_packets) {
rtcp_packet->SetSenderSsrc(ssrc);
sender.AppendPacket(*rtcp_packet);
}
sender.Send();
}
void RTCPSender::SetNextRtcpSendEvaluationDuration(TimeDelta duration) {
next_time_to_send_rtcp_ = clock_->CurrentTime() + duration;
// TODO(bugs.webrtc.org/11581): make unconditional once downstream consumers
// are using the callback method.
if (schedule_next_rtcp_send_evaluation_function_)
schedule_next_rtcp_send_evaluation_function_(duration);
}
} // namespace webrtc

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