Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
6
TMessagesProj/jni/voip/webrtc/modules/rtp_rtcp/OWNERS
Normal file
6
TMessagesProj/jni/voip/webrtc/modules/rtp_rtcp/OWNERS
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
stefan@webrtc.org
|
||||
henrik.lundin@webrtc.org
|
||||
mflodman@webrtc.org
|
||||
asapersson@webrtc.org
|
||||
danilchap@webrtc.org
|
||||
sprang@webrtc.org
|
||||
|
|
@ -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_INCLUDE_FLEXFEC_RECEIVER_H_
|
||||
#define MODULES_RTP_RTCP_INCLUDE_FLEXFEC_RECEIVER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "modules/rtp_rtcp/include/recovered_packet_receiver.h"
|
||||
#include "modules/rtp_rtcp/source/forward_error_correction.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
|
||||
#include "modules/rtp_rtcp/source/ulpfec_receiver.h"
|
||||
#include "rtc_base/system/no_unique_address.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class Clock;
|
||||
|
||||
class FlexfecReceiver {
|
||||
public:
|
||||
FlexfecReceiver(uint32_t ssrc,
|
||||
uint32_t protected_media_ssrc,
|
||||
RecoveredPacketReceiver* recovered_packet_receiver);
|
||||
FlexfecReceiver(Clock* clock,
|
||||
uint32_t ssrc,
|
||||
uint32_t protected_media_ssrc,
|
||||
RecoveredPacketReceiver* recovered_packet_receiver);
|
||||
~FlexfecReceiver();
|
||||
|
||||
// Inserts a received packet (can be either media or FlexFEC) into the
|
||||
// internal buffer, and sends the received packets to the erasure code.
|
||||
// All newly recovered packets are sent back through the callback.
|
||||
void OnRtpPacket(const RtpPacketReceived& packet);
|
||||
|
||||
// Returns a counter describing the added and recovered packets.
|
||||
FecPacketCounter GetPacketCounter() const;
|
||||
|
||||
// Protected to aid testing.
|
||||
protected:
|
||||
std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> AddReceivedPacket(
|
||||
const RtpPacketReceived& packet);
|
||||
void ProcessReceivedPacket(
|
||||
const ForwardErrorCorrection::ReceivedPacket& received_packet);
|
||||
|
||||
private:
|
||||
// Config.
|
||||
const uint32_t ssrc_;
|
||||
const uint32_t protected_media_ssrc_;
|
||||
|
||||
// Erasure code interfacing and callback.
|
||||
std::unique_ptr<ForwardErrorCorrection> erasure_code_
|
||||
RTC_GUARDED_BY(sequence_checker_);
|
||||
ForwardErrorCorrection::RecoveredPacketList recovered_packets_
|
||||
RTC_GUARDED_BY(sequence_checker_);
|
||||
RecoveredPacketReceiver* const recovered_packet_receiver_;
|
||||
|
||||
// Logging and stats.
|
||||
Clock* const clock_;
|
||||
Timestamp last_recovered_packet_ RTC_GUARDED_BY(sequence_checker_) =
|
||||
Timestamp::MinusInfinity();
|
||||
FecPacketCounter packet_counter_ RTC_GUARDED_BY(sequence_checker_);
|
||||
|
||||
RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_INCLUDE_FLEXFEC_RECEIVER_H_
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 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_INCLUDE_FLEXFEC_SENDER_H_
|
||||
#define MODULES_RTP_RTCP_INCLUDE_FLEXFEC_SENDER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/array_view.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/units/timestamp.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/rtp_header_extension_size.h"
|
||||
#include "modules/rtp_rtcp/source/ulpfec_generator.h"
|
||||
#include "modules/rtp_rtcp/source/video_fec_generator.h"
|
||||
#include "rtc_base/bitrate_tracker.h"
|
||||
#include "rtc_base/random.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class Clock;
|
||||
class RtpPacketToSend;
|
||||
|
||||
// Note that this class is not thread safe, and thus requires external
|
||||
// synchronization. Currently, this is done using the lock in PayloadRouter.
|
||||
|
||||
class FlexfecSender : public VideoFecGenerator {
|
||||
public:
|
||||
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);
|
||||
~FlexfecSender();
|
||||
|
||||
FecType GetFecType() const override {
|
||||
return VideoFecGenerator::FecType::kFlexFec;
|
||||
}
|
||||
absl::optional<uint32_t> FecSsrc() override { return ssrc_; }
|
||||
|
||||
// Sets the FEC rate, max frames sent before FEC packets are sent,
|
||||
// and what type of generator matrices are used.
|
||||
void SetProtectionParameters(const FecProtectionParams& delta_params,
|
||||
const FecProtectionParams& key_params) override;
|
||||
|
||||
// Adds a media packet to the internal buffer. When enough media packets
|
||||
// have been added, the FEC packets are generated and stored internally.
|
||||
// These FEC packets are then obtained by calling GetFecPackets().
|
||||
void AddPacketAndGenerateFec(const RtpPacketToSend& packet) override;
|
||||
|
||||
// Returns generated FlexFEC packets.
|
||||
std::vector<std::unique_ptr<RtpPacketToSend>> GetFecPackets() override;
|
||||
|
||||
// Returns the overhead, per packet, for FlexFEC.
|
||||
size_t MaxPacketOverhead() const override;
|
||||
|
||||
DataRate CurrentFecRate() const override;
|
||||
|
||||
// Only called on the VideoSendStream queue, after operation has shut down.
|
||||
absl::optional<RtpState> GetRtpState() override;
|
||||
|
||||
private:
|
||||
// Utility.
|
||||
Clock* const clock_;
|
||||
Random random_;
|
||||
Timestamp last_generated_packet_ = Timestamp::MinusInfinity();
|
||||
|
||||
// Config.
|
||||
const int payload_type_;
|
||||
const uint32_t timestamp_offset_;
|
||||
const uint32_t ssrc_;
|
||||
const uint32_t protected_media_ssrc_;
|
||||
// MID value to send in the MID header extension.
|
||||
const std::string mid_;
|
||||
// Sequence number of next packet to generate.
|
||||
uint16_t seq_num_;
|
||||
|
||||
// Implementation.
|
||||
UlpfecGenerator ulpfec_generator_;
|
||||
const RtpHeaderExtensionMap rtp_header_extension_map_;
|
||||
const size_t header_extensions_size_;
|
||||
|
||||
mutable Mutex mutex_;
|
||||
BitrateTracker fec_bitrate_ RTC_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_INCLUDE_FLEXFEC_SENDER_H_
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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_INCLUDE_RECEIVE_STATISTICS_H_
|
||||
#define MODULES_RTP_RTCP_INCLUDE_RECEIVE_STATISTICS_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "call/rtp_packet_sink_interface.h"
|
||||
#include "modules/rtp_rtcp/include/rtcp_statistics.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class Clock;
|
||||
|
||||
class ReceiveStatisticsProvider {
|
||||
public:
|
||||
virtual ~ReceiveStatisticsProvider() = default;
|
||||
// Collects receive statistic in a form of rtcp report blocks.
|
||||
// Returns at most `max_blocks` report blocks.
|
||||
virtual std::vector<rtcp::ReportBlock> RtcpReportBlocks(
|
||||
size_t max_blocks) = 0;
|
||||
};
|
||||
|
||||
class StreamStatistician {
|
||||
public:
|
||||
virtual ~StreamStatistician();
|
||||
|
||||
virtual RtpReceiveStats GetStats() const = 0;
|
||||
|
||||
// Returns average over the stream life time.
|
||||
virtual absl::optional<int> GetFractionLostInPercent() const = 0;
|
||||
|
||||
// TODO(bugs.webrtc.org/10679): Delete, migrate users to the above GetStats
|
||||
// method (and extend RtpReceiveStats if needed).
|
||||
// Gets receive stream data counters.
|
||||
virtual StreamDataCounters GetReceiveStreamDataCounters() const = 0;
|
||||
|
||||
virtual uint32_t BitrateReceived() const = 0;
|
||||
};
|
||||
|
||||
class ReceiveStatistics : public ReceiveStatisticsProvider,
|
||||
public RtpPacketSinkInterface {
|
||||
public:
|
||||
~ReceiveStatistics() override = default;
|
||||
|
||||
// Returns a thread-safe instance of ReceiveStatistics.
|
||||
// https://chromium.googlesource.com/chromium/src/+/lkgr/docs/threading_and_tasks.md#threading-lexicon
|
||||
static std::unique_ptr<ReceiveStatistics> Create(Clock* clock);
|
||||
// Returns a thread-compatible instance of ReceiveStatistics.
|
||||
static std::unique_ptr<ReceiveStatistics> CreateThreadCompatible(
|
||||
Clock* clock);
|
||||
|
||||
// Returns a pointer to the statistician of an ssrc.
|
||||
virtual StreamStatistician* GetStatistician(uint32_t ssrc) const = 0;
|
||||
|
||||
// TODO(bugs.webrtc.org/10669): Deprecated, delete as soon as downstream
|
||||
// projects are updated. This method sets the max reordering threshold of all
|
||||
// current and future streams.
|
||||
virtual void SetMaxReorderingThreshold(int max_reordering_threshold) = 0;
|
||||
|
||||
// Sets the max reordering threshold in number of packets.
|
||||
virtual void SetMaxReorderingThreshold(uint32_t ssrc,
|
||||
int max_reordering_threshold) = 0;
|
||||
// Detect retransmissions, enabling updates of the retransmitted counters. The
|
||||
// default is false.
|
||||
virtual void EnableRetransmitDetection(uint32_t ssrc, bool enable) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_RTP_RTCP_INCLUDE_RECEIVE_STATISTICS_H_
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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_INCLUDE_RECOVERED_PACKET_RECEIVER_H_
|
||||
#define MODULES_RTP_RTCP_INCLUDE_RECOVERED_PACKET_RECEIVER_H_
|
||||
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Callback interface for packets recovered by FlexFEC or ULPFEC. In
|
||||
// the FlexFEC case, the implementation should be able to demultiplex
|
||||
// the recovered RTP packets based on SSRC.
|
||||
class RecoveredPacketReceiver {
|
||||
public:
|
||||
virtual void OnRecoveredPacket(const RtpPacketReceived& packet) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~RecoveredPacketReceiver() = default;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_RTP_RTCP_INCLUDE_RECOVERED_PACKET_RECEIVER_H_
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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_INCLUDE_REMOTE_NTP_TIME_ESTIMATOR_H_
|
||||
#define MODULES_RTP_RTCP_INCLUDE_REMOTE_NTP_TIME_ESTIMATOR_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "rtc_base/numerics/moving_percentile_filter.h"
|
||||
#include "system_wrappers/include/rtp_to_ntp_estimator.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class Clock;
|
||||
|
||||
// RemoteNtpTimeEstimator can be used to estimate a given RTP timestamp's NTP
|
||||
// time in local timebase.
|
||||
// Note that it needs to be trained with at least 2 RTCP SR (by calling
|
||||
// `UpdateRtcpTimestamp`) before it can be used.
|
||||
class RemoteNtpTimeEstimator {
|
||||
public:
|
||||
explicit RemoteNtpTimeEstimator(Clock* clock);
|
||||
RemoteNtpTimeEstimator(const RemoteNtpTimeEstimator&) = delete;
|
||||
RemoteNtpTimeEstimator& operator=(const RemoteNtpTimeEstimator&) = delete;
|
||||
~RemoteNtpTimeEstimator() = default;
|
||||
|
||||
// Updates the estimator with round trip time `rtt` and
|
||||
// new NTP time <-> RTP timestamp mapping from an RTCP sender report.
|
||||
bool UpdateRtcpTimestamp(TimeDelta rtt,
|
||||
NtpTime sender_send_time,
|
||||
uint32_t rtp_timestamp);
|
||||
|
||||
// Estimates the NTP timestamp in local timebase from `rtp_timestamp`.
|
||||
// Returns the NTP timestamp in ms when success. -1 if failed.
|
||||
int64_t Estimate(uint32_t rtp_timestamp) {
|
||||
NtpTime ntp_time = EstimateNtp(rtp_timestamp);
|
||||
if (!ntp_time.Valid()) {
|
||||
return -1;
|
||||
}
|
||||
return ntp_time.ToMs();
|
||||
}
|
||||
|
||||
// Estimates the NTP timestamp in local timebase from `rtp_timestamp`.
|
||||
// Returns invalid NtpTime (i.e. NtpTime(0)) on failure.
|
||||
NtpTime EstimateNtp(uint32_t rtp_timestamp);
|
||||
|
||||
// Estimates the offset between the remote clock and the
|
||||
// local one. This is equal to local NTP clock - remote NTP clock.
|
||||
// The offset is returned in ntp time resolution, i.e. 1/2^32 sec ~= 0.2 ns.
|
||||
// Returns nullopt on failure.
|
||||
absl::optional<int64_t> EstimateRemoteToLocalClockOffset();
|
||||
|
||||
private:
|
||||
Clock* clock_;
|
||||
// Offset is measured with the same precision as NtpTime: in 1/2^32 seconds ~=
|
||||
// 0.2 ns.
|
||||
MovingMedianFilter<int64_t> ntp_clocks_offset_estimator_;
|
||||
RtpToNtpEstimator rtp_to_ntp_;
|
||||
Timestamp last_timing_log_ = Timestamp::MinusInfinity();
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_INCLUDE_REMOTE_NTP_TIME_ESTIMATOR_H_
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2019 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "modules/rtp_rtcp/include/report_block_data.h"
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
TimeDelta ReportBlockData::jitter(int rtp_clock_rate_hz) const {
|
||||
RTC_DCHECK_GT(rtp_clock_rate_hz, 0);
|
||||
// Conversion to TimeDelta and division are swapped to avoid conversion
|
||||
// to/from floating point types.
|
||||
return TimeDelta::Seconds(jitter()) / rtp_clock_rate_hz;
|
||||
}
|
||||
|
||||
void ReportBlockData::SetReportBlock(uint32_t sender_ssrc,
|
||||
const rtcp::ReportBlock& report_block,
|
||||
Timestamp report_block_timestamp_utc) {
|
||||
sender_ssrc_ = sender_ssrc;
|
||||
source_ssrc_ = report_block.source_ssrc();
|
||||
fraction_lost_raw_ = report_block.fraction_lost();
|
||||
cumulative_lost_ = report_block.cumulative_lost();
|
||||
extended_highest_sequence_number_ = report_block.extended_high_seq_num();
|
||||
jitter_ = report_block.jitter();
|
||||
report_block_timestamp_utc_ = report_block_timestamp_utc;
|
||||
}
|
||||
|
||||
void ReportBlockData::AddRoundTripTimeSample(TimeDelta rtt) {
|
||||
last_rtt_ = rtt;
|
||||
sum_rtt_ += rtt;
|
||||
++num_rtts_;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright 2019 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_RTP_RTCP_INCLUDE_REPORT_BLOCK_DATA_H_
|
||||
#define MODULES_RTP_RTCP_INCLUDE_REPORT_BLOCK_DATA_H_
|
||||
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Represents fields and derived information received in RTCP report block
|
||||
// attached to RTCP sender report or RTCP receiver report, as described in
|
||||
// https://www.rfc-editor.org/rfc/rfc3550#section-6.4.1
|
||||
class ReportBlockData {
|
||||
public:
|
||||
ReportBlockData() = default;
|
||||
|
||||
ReportBlockData(const ReportBlockData&) = default;
|
||||
ReportBlockData& operator=(const ReportBlockData&) = default;
|
||||
|
||||
// The SSRC identifier for the originator of this report block,
|
||||
// i.e. remote receiver of the RTP stream.
|
||||
uint32_t sender_ssrc() const { return sender_ssrc_; }
|
||||
|
||||
// The SSRC identifier of the source to which the information in this
|
||||
// reception report block pertains, i.e. local sender of the RTP stream.
|
||||
uint32_t source_ssrc() const { return source_ssrc_; }
|
||||
|
||||
// The fraction of RTP data packets from 'source_ssrc()' lost since the
|
||||
// previous report block was sent.
|
||||
// Fraction loss in range [0.0, 1.0].
|
||||
float fraction_lost() const { return fraction_lost_raw() / 256.0; }
|
||||
|
||||
// Fraction loss as was written in the raw packet: range is [0, 255] where 0
|
||||
// represents no loss, and 255 represents 99.6% loss (255/256 * 100%).
|
||||
uint8_t fraction_lost_raw() const { return fraction_lost_raw_; }
|
||||
|
||||
// The total number of RTP data packets from 'source_ssrc()' that have been
|
||||
// lost since the beginning of reception. This number is defined to be the
|
||||
// number of packets expected less the number of packets actually received,
|
||||
// where the number of packets received includes any which are late or
|
||||
// duplicates. Thus, packets that arrive late are not counted as lost, and the
|
||||
// loss may be negative if there are duplicates.
|
||||
int cumulative_lost() const { return cumulative_lost_; }
|
||||
|
||||
// The low 16 bits contain the highest sequence number received in an RTP data
|
||||
// packet from 'source_ssrc()', and the most significant 16 bits extend that
|
||||
// sequence number with the corresponding count of sequence number cycles.
|
||||
uint32_t extended_highest_sequence_number() const {
|
||||
return extended_highest_sequence_number_;
|
||||
}
|
||||
|
||||
// An estimate of the statistical variance of the RTP data packet interarrival
|
||||
// time, measured in RTP timestamp units. The interarrival jitter J is defined
|
||||
// to be the mean deviation (smoothed absolute value) of the difference D in
|
||||
// packet spacing at the receiver compared to the sender for a pair of
|
||||
// packets.
|
||||
uint32_t jitter() const { return jitter_; }
|
||||
|
||||
// Jitter converted to common time units.
|
||||
TimeDelta jitter(int rtp_clock_rate_hz) const;
|
||||
|
||||
// Time in utc epoch (Jan 1st, 1970) the report block was received.
|
||||
Timestamp report_block_timestamp_utc() const {
|
||||
return report_block_timestamp_utc_;
|
||||
}
|
||||
|
||||
// Round Trip Time measurments for given (sender_ssrc, source_ssrc) pair.
|
||||
// Min, max, sum, number of measurements are since beginning of the call.
|
||||
TimeDelta last_rtt() const { return last_rtt_; }
|
||||
TimeDelta sum_rtts() const { return sum_rtt_; }
|
||||
size_t num_rtts() const { return num_rtts_; }
|
||||
bool has_rtt() const { return num_rtts_ != 0; }
|
||||
|
||||
void set_sender_ssrc(uint32_t ssrc) { sender_ssrc_ = ssrc; }
|
||||
void set_source_ssrc(uint32_t ssrc) { source_ssrc_ = ssrc; }
|
||||
void set_fraction_lost_raw(uint8_t lost) { fraction_lost_raw_ = lost; }
|
||||
void set_cumulative_lost(int lost) { cumulative_lost_ = lost; }
|
||||
void set_extended_highest_sequence_number(uint32_t sn) {
|
||||
extended_highest_sequence_number_ = sn;
|
||||
}
|
||||
void set_jitter(uint32_t jitter) { jitter_ = jitter; }
|
||||
void set_report_block_timestamp_utc(Timestamp arrival_time) {
|
||||
report_block_timestamp_utc_ = arrival_time;
|
||||
}
|
||||
|
||||
void SetReportBlock(uint32_t sender_ssrc,
|
||||
const rtcp::ReportBlock& report_block,
|
||||
Timestamp report_block_timestamp_utc);
|
||||
void AddRoundTripTimeSample(TimeDelta rtt);
|
||||
|
||||
private:
|
||||
uint32_t sender_ssrc_ = 0;
|
||||
uint32_t source_ssrc_ = 0;
|
||||
uint8_t fraction_lost_raw_ = 0;
|
||||
int32_t cumulative_lost_ = 0;
|
||||
uint32_t extended_highest_sequence_number_ = 0;
|
||||
uint32_t jitter_ = 0;
|
||||
Timestamp report_block_timestamp_utc_ = Timestamp::Zero();
|
||||
TimeDelta last_rtt_ = TimeDelta::Zero();
|
||||
TimeDelta sum_rtt_ = TimeDelta::Zero();
|
||||
size_t num_rtts_ = 0;
|
||||
};
|
||||
|
||||
class ReportBlockDataObserver {
|
||||
public:
|
||||
virtual ~ReportBlockDataObserver() = default;
|
||||
|
||||
virtual void OnReportBlockDataUpdated(ReportBlockData report_block_data) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_INCLUDE_REPORT_BLOCK_DATA_H_
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_RTP_RTCP_INCLUDE_RTCP_STATISTICS_H_
|
||||
#define MODULES_RTP_RTCP_INCLUDE_RTCP_STATISTICS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Statistics for RTCP packet types.
|
||||
struct RtcpPacketTypeCounter {
|
||||
RtcpPacketTypeCounter()
|
||||
: nack_packets(0),
|
||||
fir_packets(0),
|
||||
pli_packets(0),
|
||||
nack_requests(0),
|
||||
unique_nack_requests(0) {}
|
||||
|
||||
void Add(const RtcpPacketTypeCounter& other) {
|
||||
nack_packets += other.nack_packets;
|
||||
fir_packets += other.fir_packets;
|
||||
pli_packets += other.pli_packets;
|
||||
nack_requests += other.nack_requests;
|
||||
unique_nack_requests += other.unique_nack_requests;
|
||||
}
|
||||
|
||||
void Subtract(const RtcpPacketTypeCounter& other) {
|
||||
nack_packets -= other.nack_packets;
|
||||
fir_packets -= other.fir_packets;
|
||||
pli_packets -= other.pli_packets;
|
||||
nack_requests -= other.nack_requests;
|
||||
unique_nack_requests -= other.unique_nack_requests;
|
||||
}
|
||||
|
||||
int UniqueNackRequestsInPercent() const {
|
||||
if (nack_requests == 0) {
|
||||
return 0;
|
||||
}
|
||||
return static_cast<int>((unique_nack_requests * 100.0f / nack_requests) +
|
||||
0.5f);
|
||||
}
|
||||
|
||||
uint32_t nack_packets; // Number of RTCP NACK packets.
|
||||
uint32_t fir_packets; // Number of RTCP FIR packets.
|
||||
uint32_t pli_packets; // Number of RTCP PLI packets.
|
||||
uint32_t nack_requests; // Number of NACKed RTP packets.
|
||||
uint32_t unique_nack_requests; // Number of unique NACKed RTP packets.
|
||||
};
|
||||
|
||||
class RtcpPacketTypeCounterObserver {
|
||||
public:
|
||||
virtual ~RtcpPacketTypeCounterObserver() {}
|
||||
virtual void RtcpPacketTypesCounterUpdated(
|
||||
uint32_t ssrc,
|
||||
const RtcpPacketTypeCounter& packet_counter) = 0;
|
||||
};
|
||||
|
||||
// Invoked for each cname passed in RTCP SDES blocks.
|
||||
class RtcpCnameCallback {
|
||||
public:
|
||||
virtual ~RtcpCnameCallback() = default;
|
||||
|
||||
virtual void OnCname(uint32_t ssrc, absl::string_view cname) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_RTP_RTCP_INCLUDE_RTCP_STATISTICS_H_
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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_INCLUDE_RTP_CVO_H_
|
||||
#define MODULES_RTP_RTCP_INCLUDE_RTP_CVO_H_
|
||||
|
||||
#include "api/video/video_rotation.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Please refer to http://www.etsi.org/deliver/etsi_ts/126100_126199/126114/
|
||||
// 12.07.00_60/ts_126114v120700p.pdf Section 7.4.5. The rotation of a frame is
|
||||
// the clockwise angle the frames must be rotated in order to display the frames
|
||||
// correctly if the display is rotated in its natural orientation.
|
||||
inline uint8_t ConvertVideoRotationToCVOByte(VideoRotation rotation) {
|
||||
switch (rotation) {
|
||||
case kVideoRotation_0:
|
||||
return 0;
|
||||
case kVideoRotation_90:
|
||||
return 1;
|
||||
case kVideoRotation_180:
|
||||
return 2;
|
||||
case kVideoRotation_270:
|
||||
return 3;
|
||||
}
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline VideoRotation ConvertCVOByteToVideoRotation(uint8_t cvo_byte) {
|
||||
// CVO byte: |0 0 0 0 C F R R|.
|
||||
const uint8_t rotation_bits = cvo_byte & 0x3;
|
||||
switch (rotation_bits) {
|
||||
case 0:
|
||||
return kVideoRotation_0;
|
||||
case 1:
|
||||
return kVideoRotation_90;
|
||||
case 2:
|
||||
return kVideoRotation_180;
|
||||
case 3:
|
||||
return kVideoRotation_270;
|
||||
default:
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return kVideoRotation_0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_RTP_RTCP_INCLUDE_RTP_CVO_H_
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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_INCLUDE_RTP_HEADER_EXTENSION_MAP_H_
|
||||
#define MODULES_RTP_RTCP_INCLUDE_RTP_HEADER_EXTENSION_MAP_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/array_view.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RtpHeaderExtensionMap {
|
||||
public:
|
||||
static constexpr RTPExtensionType kInvalidType = kRtpExtensionNone;
|
||||
static constexpr int kInvalidId = 0;
|
||||
|
||||
RtpHeaderExtensionMap();
|
||||
explicit RtpHeaderExtensionMap(bool extmap_allow_mixed);
|
||||
explicit RtpHeaderExtensionMap(rtc::ArrayView<const RtpExtension> extensions);
|
||||
|
||||
void Reset(rtc::ArrayView<const RtpExtension> extensions);
|
||||
|
||||
template <typename Extension>
|
||||
bool Register(int id) {
|
||||
return Register(id, Extension::kId, Extension::Uri());
|
||||
}
|
||||
bool RegisterByType(int id, RTPExtensionType type);
|
||||
bool RegisterByUri(int id, absl::string_view uri);
|
||||
|
||||
bool IsRegistered(RTPExtensionType type) const {
|
||||
return GetId(type) != kInvalidId;
|
||||
}
|
||||
// Return kInvalidType if not found.
|
||||
RTPExtensionType GetType(int id) const;
|
||||
// Return kInvalidId if not found.
|
||||
uint8_t GetId(RTPExtensionType type) const {
|
||||
RTC_DCHECK_GT(type, kRtpExtensionNone);
|
||||
RTC_DCHECK_LT(type, kRtpExtensionNumberOfExtensions);
|
||||
return ids_[type];
|
||||
}
|
||||
|
||||
void Deregister(absl::string_view uri);
|
||||
|
||||
// Corresponds to the SDP attribute extmap-allow-mixed, see RFC8285.
|
||||
// Set to true if it's allowed to mix one- and two-byte RTP header extensions
|
||||
// in the same stream.
|
||||
bool ExtmapAllowMixed() const { return extmap_allow_mixed_; }
|
||||
void SetExtmapAllowMixed(bool extmap_allow_mixed) {
|
||||
extmap_allow_mixed_ = extmap_allow_mixed;
|
||||
}
|
||||
|
||||
private:
|
||||
bool Register(int id, RTPExtensionType type, absl::string_view uri);
|
||||
|
||||
uint8_t ids_[kRtpExtensionNumberOfExtensions];
|
||||
bool extmap_allow_mixed_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_INCLUDE_RTP_HEADER_EXTENSION_MAP_H_
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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_INCLUDE_RTP_PACKET_SENDER_H_
|
||||
#define MODULES_RTP_RTCP_INCLUDE_RTP_PACKET_SENDER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RtpPacketSender {
|
||||
public:
|
||||
virtual ~RtpPacketSender() = default;
|
||||
|
||||
// Insert a set of packets into queue, for eventual transmission. Based on the
|
||||
// type of packets, they will be prioritized and scheduled relative to other
|
||||
// packets and the current target send rate.
|
||||
virtual void EnqueuePackets(
|
||||
std::vector<std::unique_ptr<RtpPacketToSend>> packets) = 0;
|
||||
|
||||
// Clear any pending packets with the given SSRC from the queue.
|
||||
// TODO(crbug.com/1395081): Make pure virtual when downstream code has been
|
||||
// updated.
|
||||
virtual void RemovePacketsForSsrc(uint32_t ssrc) {}
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_INCLUDE_RTP_PACKET_SENDER_H_
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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_INCLUDE_RTP_RTCP_H_
|
||||
#define MODULES_RTP_RTCP_INCLUDE_RTP_RTCP_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class ABSL_DEPRECATED("") RtpRtcp : public RtpRtcpInterface {
|
||||
public:
|
||||
// Instantiates a deprecated version of the RtpRtcp module.
|
||||
static std::unique_ptr<RtpRtcp> ABSL_DEPRECATED("")
|
||||
Create(const Configuration& configuration) {
|
||||
return DEPRECATED_Create(configuration);
|
||||
}
|
||||
|
||||
static std::unique_ptr<RtpRtcp> DEPRECATED_Create(
|
||||
const Configuration& configuration);
|
||||
|
||||
// Process any pending tasks such as timeouts.
|
||||
virtual void Process() = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_INCLUDE_RTP_RTCP_H_
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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/include/rtp_rtcp_defines.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "api/array_view.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_packet.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
constexpr size_t kMidRsidMaxSize = 16;
|
||||
|
||||
// Check if passed character is a "token-char" from RFC 4566.
|
||||
// https://datatracker.ietf.org/doc/html/rfc4566#section-9
|
||||
// token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39
|
||||
// / %x41-5A / %x5E-7E
|
||||
bool IsTokenChar(char ch) {
|
||||
return ch == 0x21 || (ch >= 0x23 && ch <= 0x27) || ch == 0x2a || ch == 0x2b ||
|
||||
ch == 0x2d || ch == 0x2e || (ch >= 0x30 && ch <= 0x39) ||
|
||||
(ch >= 0x41 && ch <= 0x5a) || (ch >= 0x5e && ch <= 0x7e);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool IsLegalMidName(absl::string_view name) {
|
||||
return (name.size() <= kMidRsidMaxSize && !name.empty() &&
|
||||
absl::c_all_of(name, IsTokenChar));
|
||||
}
|
||||
|
||||
bool IsLegalRsidName(absl::string_view name) {
|
||||
return (name.size() <= kMidRsidMaxSize && !name.empty() &&
|
||||
absl::c_all_of(name, isalnum));
|
||||
}
|
||||
|
||||
StreamDataCounters::StreamDataCounters() = default;
|
||||
|
||||
RtpPacketCounter::RtpPacketCounter(const RtpPacket& packet)
|
||||
: header_bytes(packet.headers_size()),
|
||||
payload_bytes(packet.payload_size()),
|
||||
padding_bytes(packet.padding_size()),
|
||||
packets(1) {}
|
||||
|
||||
RtpPacketCounter::RtpPacketCounter(const RtpPacketToSend& packet_to_send)
|
||||
: RtpPacketCounter(static_cast<const RtpPacket&>(packet_to_send)) {
|
||||
total_packet_delay =
|
||||
packet_to_send.time_in_send_queue().value_or(TimeDelta::Zero());
|
||||
}
|
||||
|
||||
void RtpPacketCounter::AddPacket(const RtpPacket& packet) {
|
||||
++packets;
|
||||
header_bytes += packet.headers_size();
|
||||
padding_bytes += packet.padding_size();
|
||||
payload_bytes += packet.payload_size();
|
||||
}
|
||||
|
||||
void RtpPacketCounter::AddPacket(const RtpPacketToSend& packet_to_send) {
|
||||
AddPacket(static_cast<const RtpPacket&>(packet_to_send));
|
||||
total_packet_delay +=
|
||||
packet_to_send.time_in_send_queue().value_or(TimeDelta::Zero());
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,420 @@
|
|||
/*
|
||||
* 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_INCLUDE_RTP_RTCP_DEFINES_H_
|
||||
#define MODULES_RTP_RTCP_INCLUDE_RTP_RTCP_DEFINES_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "absl/types/variant.h"
|
||||
#include "api/array_view.h"
|
||||
#include "api/audio_codecs/audio_format.h"
|
||||
#include "api/rtp_headers.h"
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.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/source/rtcp_packet/remote_estimate.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
|
||||
#define RTCP_CNAME_SIZE 256 // RFC 3550 page 44, including null termination
|
||||
#define IP_PACKET_SIZE 1500 // we assume ethernet
|
||||
|
||||
namespace webrtc {
|
||||
class RtpPacket;
|
||||
class RtpPacketToSend;
|
||||
namespace rtcp {
|
||||
class TransportFeedback;
|
||||
}
|
||||
|
||||
const int kVideoPayloadTypeFrequency = 90000;
|
||||
|
||||
// TODO(bugs.webrtc.org/6458): Remove this when all the depending projects are
|
||||
// updated to correctly set rtp rate for RtcpSender.
|
||||
const int kBogusRtpRateForAudioRtcp = 8000;
|
||||
|
||||
// Minimum RTP header size in bytes.
|
||||
const uint8_t kRtpHeaderSize = 12;
|
||||
|
||||
bool IsLegalMidName(absl::string_view name);
|
||||
bool IsLegalRsidName(absl::string_view name);
|
||||
|
||||
// This enum must not have any gaps, i.e., all integers between
|
||||
// kRtpExtensionNone and kRtpExtensionNumberOfExtensions must be valid enum
|
||||
// entries.
|
||||
enum RTPExtensionType : int {
|
||||
kRtpExtensionNone,
|
||||
kRtpExtensionTransmissionTimeOffset,
|
||||
kRtpExtensionAudioLevel,
|
||||
kRtpExtensionCsrcAudioLevel,
|
||||
kRtpExtensionInbandComfortNoise,
|
||||
kRtpExtensionAbsoluteSendTime,
|
||||
kRtpExtensionAbsoluteCaptureTime,
|
||||
kRtpExtensionVideoRotation,
|
||||
kRtpExtensionTransportSequenceNumber,
|
||||
kRtpExtensionTransportSequenceNumber02,
|
||||
kRtpExtensionPlayoutDelay,
|
||||
kRtpExtensionVideoContentType,
|
||||
kRtpExtensionVideoLayersAllocation,
|
||||
kRtpExtensionVideoTiming,
|
||||
kRtpExtensionRtpStreamId,
|
||||
kRtpExtensionRepairedRtpStreamId,
|
||||
kRtpExtensionMid,
|
||||
kRtpExtensionGenericFrameDescriptor,
|
||||
kRtpExtensionGenericFrameDescriptor00 [[deprecated]] =
|
||||
kRtpExtensionGenericFrameDescriptor,
|
||||
kRtpExtensionDependencyDescriptor,
|
||||
kRtpExtensionGenericFrameDescriptor02 [[deprecated]] =
|
||||
kRtpExtensionDependencyDescriptor,
|
||||
kRtpExtensionColorSpace,
|
||||
kRtpExtensionVideoFrameTrackingId,
|
||||
kRtpExtensionNumberOfExtensions // Must be the last entity in the enum.
|
||||
};
|
||||
|
||||
enum RTCPAppSubTypes { kAppSubtypeBwe = 0x00 };
|
||||
|
||||
// TODO(sprang): Make this an enum class once rtcp_receiver has been cleaned up.
|
||||
enum RTCPPacketType : uint32_t {
|
||||
kRtcpReport = 0x0001,
|
||||
kRtcpSr = 0x0002,
|
||||
kRtcpRr = 0x0004,
|
||||
kRtcpSdes = 0x0008,
|
||||
kRtcpBye = 0x0010,
|
||||
kRtcpPli = 0x0020,
|
||||
kRtcpNack = 0x0040,
|
||||
kRtcpFir = 0x0080,
|
||||
kRtcpTmmbr = 0x0100,
|
||||
kRtcpTmmbn = 0x0200,
|
||||
kRtcpSrReq = 0x0400,
|
||||
kRtcpLossNotification = 0x2000,
|
||||
kRtcpRemb = 0x10000,
|
||||
kRtcpTransmissionTimeOffset = 0x20000,
|
||||
kRtcpXrReceiverReferenceTime = 0x40000,
|
||||
kRtcpXrDlrrReportBlock = 0x80000,
|
||||
kRtcpTransportFeedback = 0x100000,
|
||||
kRtcpXrTargetBitrate = 0x200000
|
||||
};
|
||||
|
||||
enum class KeyFrameReqMethod : uint8_t {
|
||||
kNone, // Don't request keyframes.
|
||||
kPliRtcp, // Request keyframes through Picture Loss Indication.
|
||||
kFirRtcp // Request keyframes through Full Intra-frame Request.
|
||||
};
|
||||
|
||||
enum RtxMode {
|
||||
kRtxOff = 0x0,
|
||||
kRtxRetransmitted = 0x1, // Only send retransmissions over RTX.
|
||||
kRtxRedundantPayloads = 0x2 // Preventively send redundant payloads
|
||||
// instead of padding.
|
||||
};
|
||||
|
||||
const size_t kRtxHeaderSize = 2;
|
||||
|
||||
struct RtpState {
|
||||
uint16_t sequence_number = 0;
|
||||
uint32_t start_timestamp = 0;
|
||||
uint32_t timestamp = 0;
|
||||
Timestamp capture_time = Timestamp::MinusInfinity();
|
||||
Timestamp last_timestamp_time = Timestamp::MinusInfinity();
|
||||
bool ssrc_has_acked = false;
|
||||
};
|
||||
|
||||
class RtcpIntraFrameObserver {
|
||||
public:
|
||||
virtual ~RtcpIntraFrameObserver() {}
|
||||
|
||||
virtual void OnReceivedIntraFrameRequest(uint32_t ssrc) = 0;
|
||||
};
|
||||
|
||||
// Observer for incoming LossNotification RTCP messages.
|
||||
// See the documentation of LossNotification for details.
|
||||
class RtcpLossNotificationObserver {
|
||||
public:
|
||||
virtual ~RtcpLossNotificationObserver() = default;
|
||||
|
||||
virtual void OnReceivedLossNotification(uint32_t ssrc,
|
||||
uint16_t seq_num_of_last_decodable,
|
||||
uint16_t seq_num_of_last_received,
|
||||
bool decodability_flag) = 0;
|
||||
};
|
||||
|
||||
// Interface to watch incoming rtcp packets related to the link in general.
|
||||
// All message handlers have default empty implementation. This way users only
|
||||
// need to implement the ones they are interested in.
|
||||
// All message handles pass `receive_time` parameter, which is receive time
|
||||
// of the rtcp packet that triggered the update.
|
||||
class NetworkLinkRtcpObserver {
|
||||
public:
|
||||
virtual ~NetworkLinkRtcpObserver() = default;
|
||||
|
||||
virtual void OnTransportFeedback(Timestamp receive_time,
|
||||
const rtcp::TransportFeedback& feedback) {}
|
||||
virtual void OnReceiverEstimatedMaxBitrate(Timestamp receive_time,
|
||||
DataRate bitrate) {}
|
||||
|
||||
// Called on an RTCP packet with sender or receiver reports with non zero
|
||||
// report blocks. Report blocks are combined from all reports into one array.
|
||||
virtual void OnReport(Timestamp receive_time,
|
||||
rtc::ArrayView<const ReportBlockData> report_blocks) {}
|
||||
virtual void OnRttUpdate(Timestamp receive_time, TimeDelta rtt) {}
|
||||
};
|
||||
|
||||
// NOTE! `kNumMediaTypes` must be kept in sync with RtpPacketMediaType!
|
||||
static constexpr size_t kNumMediaTypes = 5;
|
||||
enum class RtpPacketMediaType : size_t {
|
||||
kAudio, // Audio media packets.
|
||||
kVideo, // Video media packets.
|
||||
kRetransmission, // Retransmisions, sent as response to NACK.
|
||||
kForwardErrorCorrection, // FEC packets.
|
||||
kPadding = kNumMediaTypes - 1, // RTX or plain padding sent to maintain BWE.
|
||||
// Again, don't forget to update `kNumMediaTypes` if you add another value!
|
||||
};
|
||||
|
||||
struct RtpPacketSendInfo {
|
||||
uint16_t transport_sequence_number = 0;
|
||||
absl::optional<uint32_t> media_ssrc;
|
||||
uint16_t rtp_sequence_number = 0; // Only valid if `media_ssrc` is set.
|
||||
uint32_t rtp_timestamp = 0;
|
||||
size_t length = 0;
|
||||
absl::optional<RtpPacketMediaType> packet_type;
|
||||
PacedPacketInfo pacing_info;
|
||||
};
|
||||
|
||||
class NetworkStateEstimateObserver {
|
||||
public:
|
||||
virtual void OnRemoteNetworkEstimate(NetworkStateEstimate estimate) = 0;
|
||||
virtual ~NetworkStateEstimateObserver() = default;
|
||||
};
|
||||
|
||||
class TransportFeedbackObserver {
|
||||
public:
|
||||
virtual ~TransportFeedbackObserver() = default;
|
||||
|
||||
virtual void OnAddPacket(const RtpPacketSendInfo& packet_info) = 0;
|
||||
};
|
||||
|
||||
// Interface for PacketRouter to send rtcp feedback on behalf of
|
||||
// congestion controller.
|
||||
// TODO(bugs.webrtc.org/8239): Remove and use RtcpTransceiver directly
|
||||
// when RtcpTransceiver always present in rtp transport.
|
||||
class RtcpFeedbackSenderInterface {
|
||||
public:
|
||||
virtual ~RtcpFeedbackSenderInterface() = default;
|
||||
virtual void SendCombinedRtcpPacket(
|
||||
std::vector<std::unique_ptr<rtcp::RtcpPacket>> rtcp_packets) = 0;
|
||||
virtual void SetRemb(int64_t bitrate_bps, std::vector<uint32_t> ssrcs) = 0;
|
||||
virtual void UnsetRemb() = 0;
|
||||
};
|
||||
|
||||
class StreamFeedbackObserver {
|
||||
public:
|
||||
struct StreamPacketInfo {
|
||||
bool received;
|
||||
|
||||
// `rtp_sequence_number` and `is_retransmission` are only valid if `ssrc`
|
||||
// is populated.
|
||||
absl::optional<uint32_t> ssrc;
|
||||
uint16_t rtp_sequence_number;
|
||||
bool is_retransmission;
|
||||
};
|
||||
virtual ~StreamFeedbackObserver() = default;
|
||||
|
||||
virtual void OnPacketFeedbackVector(
|
||||
std::vector<StreamPacketInfo> packet_feedback_vector) = 0;
|
||||
};
|
||||
|
||||
class StreamFeedbackProvider {
|
||||
public:
|
||||
virtual void RegisterStreamFeedbackObserver(
|
||||
std::vector<uint32_t> ssrcs,
|
||||
StreamFeedbackObserver* observer) = 0;
|
||||
virtual void DeRegisterStreamFeedbackObserver(
|
||||
StreamFeedbackObserver* observer) = 0;
|
||||
virtual ~StreamFeedbackProvider() = default;
|
||||
};
|
||||
|
||||
class RtcpRttStats {
|
||||
public:
|
||||
virtual void OnRttUpdate(int64_t rtt) = 0;
|
||||
|
||||
virtual int64_t LastProcessedRtt() const = 0;
|
||||
|
||||
virtual ~RtcpRttStats() {}
|
||||
};
|
||||
|
||||
struct RtpPacketCounter {
|
||||
RtpPacketCounter()
|
||||
: header_bytes(0), payload_bytes(0), padding_bytes(0), packets(0) {}
|
||||
|
||||
explicit RtpPacketCounter(const RtpPacket& packet);
|
||||
explicit RtpPacketCounter(const RtpPacketToSend& packet_to_send);
|
||||
|
||||
void Add(const RtpPacketCounter& other) {
|
||||
header_bytes += other.header_bytes;
|
||||
payload_bytes += other.payload_bytes;
|
||||
padding_bytes += other.padding_bytes;
|
||||
packets += other.packets;
|
||||
total_packet_delay += other.total_packet_delay;
|
||||
}
|
||||
|
||||
bool operator==(const RtpPacketCounter& other) const {
|
||||
return header_bytes == other.header_bytes &&
|
||||
payload_bytes == other.payload_bytes &&
|
||||
padding_bytes == other.padding_bytes && packets == other.packets &&
|
||||
total_packet_delay == other.total_packet_delay;
|
||||
}
|
||||
|
||||
// Not inlined, since use of RtpPacket would result in circular includes.
|
||||
void AddPacket(const RtpPacket& packet);
|
||||
void AddPacket(const RtpPacketToSend& packet_to_send);
|
||||
|
||||
size_t TotalBytes() const {
|
||||
return header_bytes + payload_bytes + padding_bytes;
|
||||
}
|
||||
|
||||
size_t header_bytes; // Number of bytes used by RTP headers.
|
||||
size_t payload_bytes; // Payload bytes, excluding RTP headers and padding.
|
||||
size_t padding_bytes; // Number of padding bytes.
|
||||
size_t packets; // Number of packets.
|
||||
// The total delay of all `packets`. For RtpPacketToSend packets, this is
|
||||
// `time_in_send_queue()`. For receive packets, this is zero.
|
||||
webrtc::TimeDelta total_packet_delay = webrtc::TimeDelta::Zero();
|
||||
};
|
||||
|
||||
// Data usage statistics for a (rtp) stream.
|
||||
struct StreamDataCounters {
|
||||
StreamDataCounters();
|
||||
|
||||
void Add(const StreamDataCounters& other) {
|
||||
transmitted.Add(other.transmitted);
|
||||
retransmitted.Add(other.retransmitted);
|
||||
fec.Add(other.fec);
|
||||
if (other.first_packet_time < first_packet_time) {
|
||||
// Use oldest time (excluding unsed value represented as plus infinity.
|
||||
first_packet_time = other.first_packet_time;
|
||||
}
|
||||
}
|
||||
|
||||
void MaybeSetFirstPacketTime(Timestamp now) {
|
||||
if (first_packet_time == Timestamp::PlusInfinity()) {
|
||||
first_packet_time = now;
|
||||
}
|
||||
}
|
||||
|
||||
// Return time since first packet is send/received, or zero if such event
|
||||
// haven't happen.
|
||||
TimeDelta TimeSinceFirstPacket(Timestamp now) const {
|
||||
return first_packet_time == Timestamp::PlusInfinity()
|
||||
? TimeDelta::Zero()
|
||||
: now - first_packet_time;
|
||||
}
|
||||
|
||||
// Returns the number of bytes corresponding to the actual media payload (i.e.
|
||||
// RTP headers, padding, retransmissions and fec packets are excluded).
|
||||
// Note this function does not have meaning for an RTX stream.
|
||||
size_t MediaPayloadBytes() const {
|
||||
return transmitted.payload_bytes - retransmitted.payload_bytes -
|
||||
fec.payload_bytes;
|
||||
}
|
||||
|
||||
// Time when first packet is sent/received.
|
||||
Timestamp first_packet_time = Timestamp::PlusInfinity();
|
||||
|
||||
RtpPacketCounter transmitted; // Number of transmitted packets/bytes.
|
||||
RtpPacketCounter retransmitted; // Number of retransmitted packets/bytes.
|
||||
RtpPacketCounter fec; // Number of redundancy packets/bytes.
|
||||
};
|
||||
|
||||
class RtpSendRates {
|
||||
template <std::size_t... Is>
|
||||
constexpr std::array<DataRate, sizeof...(Is)> make_zero_array(
|
||||
std::index_sequence<Is...>) {
|
||||
return {{(static_cast<void>(Is), DataRate::Zero())...}};
|
||||
}
|
||||
|
||||
public:
|
||||
RtpSendRates()
|
||||
: send_rates_(
|
||||
make_zero_array(std::make_index_sequence<kNumMediaTypes>())) {}
|
||||
RtpSendRates(const RtpSendRates& rhs) = default;
|
||||
RtpSendRates& operator=(const RtpSendRates&) = default;
|
||||
|
||||
DataRate& operator[](RtpPacketMediaType type) {
|
||||
return send_rates_[static_cast<size_t>(type)];
|
||||
}
|
||||
const DataRate& operator[](RtpPacketMediaType type) const {
|
||||
return send_rates_[static_cast<size_t>(type)];
|
||||
}
|
||||
DataRate Sum() const {
|
||||
return absl::c_accumulate(send_rates_, DataRate::Zero());
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<DataRate, kNumMediaTypes> send_rates_;
|
||||
};
|
||||
|
||||
// Callback, called whenever byte/packet counts have been updated.
|
||||
class StreamDataCountersCallback {
|
||||
public:
|
||||
virtual ~StreamDataCountersCallback() {}
|
||||
|
||||
virtual void DataCountersUpdated(const StreamDataCounters& counters,
|
||||
uint32_t ssrc) = 0;
|
||||
};
|
||||
|
||||
// Information exposed through the GetStats api.
|
||||
struct RtpReceiveStats {
|
||||
// `packets_lost` and `jitter` are defined by RFC 3550, and exposed in the
|
||||
// RTCReceivedRtpStreamStats dictionary, see
|
||||
// https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*
|
||||
int32_t packets_lost = 0;
|
||||
// Interarrival jitter in samples.
|
||||
uint32_t jitter = 0;
|
||||
// Interarrival jitter in time.
|
||||
webrtc::TimeDelta interarrival_jitter = webrtc::TimeDelta::Zero();
|
||||
|
||||
// Time of the last packet received in unix epoch,
|
||||
// i.e. Timestamp::Zero() represents 1st Jan 1970 00:00
|
||||
absl::optional<Timestamp> last_packet_received;
|
||||
|
||||
// Counters exposed in RTCInboundRtpStreamStats, see
|
||||
// https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*
|
||||
RtpPacketCounter packet_counter;
|
||||
};
|
||||
|
||||
// Callback, used to notify an observer whenever new rates have been estimated.
|
||||
class BitrateStatisticsObserver {
|
||||
public:
|
||||
virtual ~BitrateStatisticsObserver() {}
|
||||
|
||||
virtual void Notify(uint32_t total_bitrate_bps,
|
||||
uint32_t retransmit_bitrate_bps,
|
||||
uint32_t ssrc) = 0;
|
||||
};
|
||||
|
||||
// Callback, used to notify an observer whenever a packet is sent to the
|
||||
// transport.
|
||||
class SendPacketObserver {
|
||||
public:
|
||||
virtual ~SendPacketObserver() = default;
|
||||
virtual void OnSendPacket(absl::optional<uint16_t> packet_id,
|
||||
Timestamp capture_time,
|
||||
uint32_t ssrc) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_RTP_RTCP_INCLUDE_RTP_RTCP_DEFINES_H_
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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_MOCKS_MOCK_NETWORK_LINK_RTCP_OBSERVER_H_
|
||||
#define MODULES_RTP_RTCP_MOCKS_MOCK_NETWORK_LINK_RTCP_OBSERVER_H_
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "api/units/data_rate.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/rtp_rtcp_defines.h"
|
||||
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
|
||||
#include "test/gmock.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class MockNetworkLinkRtcpObserver : public NetworkLinkRtcpObserver {
|
||||
public:
|
||||
MOCK_METHOD(void,
|
||||
OnRttUpdate,
|
||||
(Timestamp receive_time, TimeDelta rtt),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
OnTransportFeedback,
|
||||
(Timestamp receive_time, const rtcp::TransportFeedback& feedback),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
OnReceiverEstimatedMaxBitrate,
|
||||
(Timestamp receive_time, DataRate bitrate),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
OnReport,
|
||||
(Timestamp receive_time,
|
||||
rtc::ArrayView<const ReportBlockData> report_blocks),
|
||||
(override));
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_RTP_RTCP_MOCKS_MOCK_NETWORK_LINK_RTCP_OBSERVER_H_
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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_MOCKS_MOCK_RECOVERED_PACKET_RECEIVER_H_
|
||||
#define MODULES_RTP_RTCP_MOCKS_MOCK_RECOVERED_PACKET_RECEIVER_H_
|
||||
|
||||
#include "modules/rtp_rtcp/include/flexfec_receiver.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
|
||||
#include "test/gmock.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class MockRecoveredPacketReceiver : public RecoveredPacketReceiver {
|
||||
public:
|
||||
MOCK_METHOD(void,
|
||||
OnRecoveredPacket,
|
||||
(const RtpPacketReceived& packet),
|
||||
(override));
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_MOCKS_MOCK_RECOVERED_PACKET_RECEIVER_H_
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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_MOCKS_MOCK_RTCP_RTT_STATS_H_
|
||||
#define MODULES_RTP_RTCP_MOCKS_MOCK_RTCP_RTT_STATS_H_
|
||||
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "test/gmock.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class MockRtcpRttStats : public RtcpRttStats {
|
||||
public:
|
||||
MOCK_METHOD(void, OnRttUpdate, (int64_t rtt), (override));
|
||||
MOCK_METHOD(int64_t, LastProcessedRtt, (), (const, override));
|
||||
};
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_RTP_RTCP_MOCKS_MOCK_RTCP_RTT_STATS_H_
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* 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_MOCKS_MOCK_RTP_RTCP_H_
|
||||
#define MODULES_RTP_RTCP_MOCKS_MOCK_RTP_RTCP_H_
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/video/video_bitrate_allocation.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
|
||||
#include "test/gmock.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class MockRtpRtcpInterface : public RtpRtcpInterface {
|
||||
public:
|
||||
MOCK_METHOD(void,
|
||||
IncomingRtcpPacket,
|
||||
(rtc::ArrayView<const uint8_t> packet),
|
||||
(override));
|
||||
MOCK_METHOD(void, SetRemoteSSRC, (uint32_t ssrc), (override));
|
||||
MOCK_METHOD(void, SetLocalSsrc, (uint32_t ssrc), (override));
|
||||
MOCK_METHOD(void, SetMaxRtpPacketSize, (size_t size), (override));
|
||||
MOCK_METHOD(size_t, MaxRtpPacketSize, (), (const, override));
|
||||
MOCK_METHOD(void,
|
||||
RegisterSendPayloadFrequency,
|
||||
(int payload_type, int frequency),
|
||||
(override));
|
||||
MOCK_METHOD(int32_t,
|
||||
DeRegisterSendPayload,
|
||||
(int8_t payload_type),
|
||||
(override));
|
||||
MOCK_METHOD(void, SetExtmapAllowMixed, (bool extmap_allow_mixed), (override));
|
||||
MOCK_METHOD(void,
|
||||
RegisterRtpHeaderExtension,
|
||||
(absl::string_view uri, int id),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
DeregisterSendRtpHeaderExtension,
|
||||
(absl::string_view uri),
|
||||
(override));
|
||||
MOCK_METHOD(bool, SupportsPadding, (), (const, override));
|
||||
MOCK_METHOD(bool, SupportsRtxPayloadPadding, (), (const, override));
|
||||
MOCK_METHOD(uint32_t, StartTimestamp, (), (const, override));
|
||||
MOCK_METHOD(void, SetStartTimestamp, (uint32_t timestamp), (override));
|
||||
MOCK_METHOD(uint16_t, SequenceNumber, (), (const, override));
|
||||
MOCK_METHOD(void, SetSequenceNumber, (uint16_t seq), (override));
|
||||
MOCK_METHOD(void, SetRtpState, (const RtpState& rtp_state), (override));
|
||||
MOCK_METHOD(void, SetRtxState, (const RtpState& rtp_state), (override));
|
||||
MOCK_METHOD(void, SetNonSenderRttMeasurement, (bool enabled), (override));
|
||||
MOCK_METHOD(RtpState, GetRtpState, (), (const, override));
|
||||
MOCK_METHOD(RtpState, GetRtxState, (), (const, override));
|
||||
MOCK_METHOD(uint32_t, SSRC, (), (const, override));
|
||||
MOCK_METHOD(void, SetMid, (absl::string_view mid), (override));
|
||||
MOCK_METHOD(void, SetRtxSendStatus, (int modes), (override));
|
||||
MOCK_METHOD(int, RtxSendStatus, (), (const, override));
|
||||
MOCK_METHOD(absl::optional<uint32_t>, RtxSsrc, (), (const, override));
|
||||
MOCK_METHOD(void, SetRtxSendPayloadType, (int, int), (override));
|
||||
MOCK_METHOD(absl::optional<uint32_t>, FlexfecSsrc, (), (const, override));
|
||||
MOCK_METHOD(int32_t, SetSendingStatus, (bool sending), (override));
|
||||
MOCK_METHOD(bool, Sending, (), (const, override));
|
||||
MOCK_METHOD(void, SetSendingMediaStatus, (bool sending), (override));
|
||||
MOCK_METHOD(bool, SendingMedia, (), (const, override));
|
||||
MOCK_METHOD(bool, IsAudioConfigured, (), (const, override));
|
||||
MOCK_METHOD(void, SetAsPartOfAllocation, (bool), (override));
|
||||
MOCK_METHOD(RtpSendRates, GetSendRates, (), (const, override));
|
||||
MOCK_METHOD(bool,
|
||||
OnSendingRtpFrame,
|
||||
(uint32_t, int64_t, int, bool),
|
||||
(override));
|
||||
MOCK_METHOD(bool,
|
||||
TrySendPacket,
|
||||
(std::unique_ptr<RtpPacketToSend> packet,
|
||||
const PacedPacketInfo& pacing_info),
|
||||
(override));
|
||||
MOCK_METHOD(void, OnBatchComplete, (), (override));
|
||||
MOCK_METHOD(void,
|
||||
SetFecProtectionParams,
|
||||
(const FecProtectionParams& delta_params,
|
||||
const FecProtectionParams& key_params),
|
||||
(override));
|
||||
MOCK_METHOD(std::vector<std::unique_ptr<RtpPacketToSend>>,
|
||||
FetchFecPackets,
|
||||
(),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
OnAbortedRetransmissions,
|
||||
(rtc::ArrayView<const uint16_t>),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
OnPacketsAcknowledged,
|
||||
(rtc::ArrayView<const uint16_t>),
|
||||
(override));
|
||||
MOCK_METHOD(std::vector<std::unique_ptr<RtpPacketToSend>>,
|
||||
GeneratePadding,
|
||||
(size_t target_size_bytes),
|
||||
(override));
|
||||
MOCK_METHOD(std::vector<RtpSequenceNumberMap::Info>,
|
||||
GetSentRtpPacketInfos,
|
||||
(rtc::ArrayView<const uint16_t> sequence_numbers),
|
||||
(const, override));
|
||||
MOCK_METHOD(size_t, ExpectedPerPacketOverhead, (), (const, override));
|
||||
MOCK_METHOD(void, OnPacketSendingThreadSwitched, (), (override));
|
||||
MOCK_METHOD(RtcpMode, RTCP, (), (const, override));
|
||||
MOCK_METHOD(void, SetRTCPStatus, (RtcpMode method), (override));
|
||||
MOCK_METHOD(int32_t, SetCNAME, (absl::string_view cname), (override));
|
||||
MOCK_METHOD(absl::optional<TimeDelta>, LastRtt, (), (const, override));
|
||||
MOCK_METHOD(TimeDelta, ExpectedRetransmissionTime, (), (const, override));
|
||||
MOCK_METHOD(int32_t, SendRTCP, (RTCPPacketType packet_type), (override));
|
||||
MOCK_METHOD(void,
|
||||
GetSendStreamDataCounters,
|
||||
(StreamDataCounters*, StreamDataCounters*),
|
||||
(const, override));
|
||||
MOCK_METHOD(std::vector<ReportBlockData>,
|
||||
GetLatestReportBlockData,
|
||||
(),
|
||||
(const, override));
|
||||
MOCK_METHOD(absl::optional<SenderReportStats>,
|
||||
GetSenderReportStats,
|
||||
(),
|
||||
(const, override));
|
||||
MOCK_METHOD(absl::optional<NonSenderRttStats>,
|
||||
GetNonSenderRttStats,
|
||||
(),
|
||||
(const, override));
|
||||
MOCK_METHOD(void,
|
||||
SetRemb,
|
||||
(int64_t bitrate, std::vector<uint32_t> ssrcs),
|
||||
(override));
|
||||
MOCK_METHOD(void, UnsetRemb, (), (override));
|
||||
MOCK_METHOD(int32_t,
|
||||
SendNACK,
|
||||
(const uint16_t* nack_list, uint16_t size),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
SendNack,
|
||||
(const std::vector<uint16_t>& sequence_numbers),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
SetStorePacketsStatus,
|
||||
(bool enable, uint16_t number_to_store),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
SendCombinedRtcpPacket,
|
||||
(std::vector<std::unique_ptr<rtcp::RtcpPacket>> rtcp_packets),
|
||||
(override));
|
||||
MOCK_METHOD(int32_t,
|
||||
SendLossNotification,
|
||||
(uint16_t last_decoded_seq_num,
|
||||
uint16_t last_received_seq_num,
|
||||
bool decodability_flag,
|
||||
bool buffering_allowed),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
SetVideoBitrateAllocation,
|
||||
(const VideoBitrateAllocation&),
|
||||
(override));
|
||||
MOCK_METHOD(RTPSender*, RtpSender, (), (override));
|
||||
MOCK_METHOD(const RTPSender*, RtpSender, (), (const, override));
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_MOCKS_MOCK_RTP_RTCP_H_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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 there’s 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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
402
TMessagesProj/jni/voip/webrtc/modules/rtp_rtcp/source/byte_io.h
Normal file
402
TMessagesProj/jni/voip/webrtc/modules/rtp_rtcp/source/byte_io.h
Normal 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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue