blob: 4a1efb1fb6313c53fb80f864188429d2a7358d80 [file]
/*
* Copyright 2026 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can 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/rtp_packetizer_sframe.h"
#include <algorithm>
#include <bitset>
#include <cstddef>
#include <cstdint>
#include <span>
#include <utility>
#include "api/media_types.h"
#include "api/rtc_error.h"
#include "modules/rtp_rtcp/source/rtp_format.h"
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
#include "modules/rtp_rtcp/source/sframe_descriptor.h"
#include "rtc_base/checks.h"
namespace webrtc {
namespace {
// Returns S and E bits for the Sframe descriptor.
// - Video uses frame fragmentation flags on `packet`;
// - Audio always sets both (one RTP packet per frame).
std::pair<bool, bool> GetSframeStartAndEndBits(MediaType media_type,
const RtpPacketToSend& packet) {
if (media_type == MediaType::AUDIO) {
return {true, true};
}
return {packet.is_first_packet_of_frame(), packet.is_last_packet_of_frame()};
}
uint8_t BuildDescriptorByte(bool start,
bool end,
SframeEncryptionLevel encryption_level) {
std::bitset<SframeDescriptor::kNumBits> bits;
bits.set(SframeDescriptor::kSBit, start);
bits.set(SframeDescriptor::kEBit, end);
bits.set(SframeDescriptor::kTBit,
encryption_level == SframeEncryptionLevel::kPacket);
return static_cast<uint8_t>(bits.to_ulong());
}
} // namespace
RtpPacketizerSframe::RtpPacketizerSframe(std::span<const uint8_t> payload,
PayloadSizeLimits limits,
bool is_last_frame_in_picture)
: remaining_payload_(payload),
is_last_frame_in_picture_(is_last_frame_in_picture) {
limits.max_payload_len -= SframeDescriptor::kSize;
payload_sizes_ = SplitAboutEqually(payload.size(), limits);
current_packet_ = payload_sizes_.begin();
}
RtpPacketizerSframe::~RtpPacketizerSframe() = default;
size_t RtpPacketizerSframe::NumPackets() const {
return payload_sizes_.end() - current_packet_;
}
bool RtpPacketizerSframe::NextPacket(RtpPacketToSend* packet) {
RTC_DCHECK(packet);
if (current_packet_ == payload_sizes_.end()) {
return false;
}
const bool first_packet = current_packet_ == payload_sizes_.begin();
const size_t media_size = *current_packet_;
++current_packet_;
const bool last_packet = current_packet_ == payload_sizes_.end();
const size_t payload_size = SframeDescriptor::kSize + media_size;
std::span<uint8_t> payload(packet->AllocatePayload(payload_size),
payload_size);
payload[0] = BuildDescriptorByte(first_packet, last_packet,
SframeEncryptionLevel::kFrame);
std::ranges::copy(remaining_payload_.first(media_size),
payload.subspan(SframeDescriptor::kSize).begin());
remaining_payload_ = remaining_payload_.subspan(media_size);
// Packets left to produce and data left to split end at the same time.
RTC_DCHECK_EQ(last_packet, remaining_payload_.empty());
packet->SetMarker(last_packet && is_last_frame_in_picture_);
return true;
}
RTCError PacketizeSframeRtpPacketOrError(
MediaType media_type,
RtpPacketToSend* packet,
SframeEncryptionLevel encryption_level) {
RTC_DCHECK(media_type == MediaType::AUDIO || media_type == MediaType::VIDEO);
RTC_DCHECK(packet);
const size_t payload_size = packet->payload_size();
const size_t padding_size = packet->padding_size();
const size_t restored_padding_size =
std::max(padding_size, SframeDescriptor::kSize) - SframeDescriptor::kSize;
if (payload_size + SframeDescriptor::kSize + restored_padding_size >
packet->MaxPayloadSize()) {
return RTCError::InvalidParameter(
"RTP packet has no room for Sframe descriptor");
}
packet->SetPadding(0);
std::span<uint8_t> payload(
packet->SetPayloadSize(payload_size + SframeDescriptor::kSize),
payload_size + SframeDescriptor::kSize);
if (payload_size > 0) {
auto src = payload.first(payload_size);
std::copy_backward(src.begin(), src.end(), payload.end());
}
const auto [start, end] = GetSframeStartAndEndBits(media_type, *packet);
payload[0] = BuildDescriptorByte(start, end, encryption_level);
if (restored_padding_size > 0 && !packet->SetPadding(restored_padding_size)) {
return RTCError::InternalError("Failed to restore RTP padding");
}
return RTCError::OK();
}
} // namespace webrtc