| /* |
| * 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/environment/environment.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( |
| const Environment& env, |
| 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) |
| : env_(env), |
| random_(env_.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_( |
| env_, |
| ForwardErrorCorrection::CreateFlexfec(ssrc, protected_media_ssrc)), |
| 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 * |
| env_.clock().TimeInMilliseconds())); |
| // Set "capture time" so that the TransmissionOffset header extension |
| // can be set by the RTPSender. |
| fec_packet_to_send->set_capture_time(env_.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 = env_.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(env_.clock().CurrentTime()) |
| .value_or(DataRate::Zero()); |
| } |
| |
| std::optional<RtpState> FlexfecSender::GetRtpState() { |
| RtpState rtp_state; |
| rtp_state.sequence_number = seq_num_; |
| rtp_state.start_timestamp = timestamp_offset_; |
| return rtp_state; |
| } |
| |
| } // namespace webrtc |