Implements method on RtpPacket to extract extension.

Removing extension will be used in DatagramDtlsAdaptor to remove transport sequence number to avoid having both datagram and RTP feedback loops. The sequence number will be stored in temporary map and used to re-create RTCP fdeedback packed when we receive datagram ACK. It would enable integration of Datagram transport without any changes in the upper layers of RTP stack. Note that Datagram adaptor changes will be implemented in a separate changelist.

In this change:
- Implement method to remove extension by rebuilding entire packet without given extension type.
- Fails if extension is not registered or not set.
- Unit test

Bug: webrtc:9719
Change-Id: I9d3811d5d97fadde5a294d5da643b2ebc6a8196e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/145100
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Reviewed-by: Bjorn Mellem <mellem@webrtc.org>
Commit-Queue: Anton Sukhanov <sukhanov@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28530}
This commit is contained in:
Anton Sukhanov
2019-07-09 13:04:07 -07:00
committed by Commit Bot
parent ca5f21e293
commit ff25b873bf
3 changed files with 170 additions and 0 deletions

View File

@ -18,6 +18,7 @@
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/strings/string_builder.h"
namespace webrtc {
namespace {
@ -627,4 +628,72 @@ bool RtpPacket::IsExtensionReserved(ExtensionType type) const {
return FindExtensionInfo(id) != nullptr;
}
bool RtpPacket::RemoveExtension(ExtensionType type) {
uint8_t id_to_remove = extensions_.GetId(type);
if (id_to_remove == ExtensionManager::kInvalidId) {
// Extension not registered.
RTC_LOG(LS_ERROR) << "Extension not registered, type=" << type
<< ", packet=" << ToString();
return false;
}
// Rebuild new packet from scratch.
RtpPacket new_packet;
new_packet.SetMarker(Marker());
new_packet.SetPayloadType(PayloadType());
new_packet.SetSequenceNumber(SequenceNumber());
new_packet.SetTimestamp(Timestamp());
new_packet.SetSsrc(Ssrc());
new_packet.IdentifyExtensions(extensions_);
// Copy all extensions, except the one we are removing.
bool found_extension = false;
for (const ExtensionInfo& ext : extension_entries_) {
if (ext.id == id_to_remove) {
found_extension = true;
} else {
auto extension_data = new_packet.AllocateRawExtension(ext.id, ext.length);
if (extension_data.size() != ext.length) {
RTC_LOG(LS_ERROR) << "Failed to allocate extension id=" << ext.id
<< ", length=" << ext.length
<< ", packet=" << ToString();
return false;
}
// Copy extension data to new packet.
memcpy(extension_data.data(), ReadAt(ext.offset), ext.length);
}
}
if (!found_extension) {
RTC_LOG(LS_WARNING) << "Extension not present in RTP packet, type=" << type
<< ", packet=" << ToString();
return false;
}
// Copy payload data to new packet.
memcpy(new_packet.AllocatePayload(payload_size()), payload().data(),
payload_size());
// Allocate padding -- must be last!
new_packet.SetPadding(padding_size());
// Success, replace current packet with newly built packet.
*this = new_packet;
return true;
}
std::string RtpPacket::ToString() const {
rtc::StringBuilder result;
result << "{payload_type=" << payload_type_ << "marker=" << marker_
<< ", sequence_number=" << sequence_number_
<< ", padding_size=" << padding_size_ << ", timestamp=" << timestamp_
<< ", ssrc=" << ssrc_ << ", payload_offset=" << payload_offset_
<< ", payload_size=" << payload_size_ << ", total_size=" << size()
<< "}";
return result.Release();
}
} // namespace webrtc

View File

@ -10,6 +10,7 @@
#ifndef MODULES_RTP_RTCP_SOURCE_RTP_PACKET_H_
#define MODULES_RTP_RTCP_SOURCE_RTP_PACKET_H_
#include <string>
#include <vector>
#include "absl/types/optional.h"
@ -38,6 +39,9 @@ class RtpPacket {
RtpPacket& operator=(const RtpPacket&) = default;
// Parse and copy given buffer into Packet.
// Does not require extension map to be registered (map is only required to
// read or allocate extensions in methods GetExtension, AllocateExtension,
// etc.)
bool Parse(const uint8_t* buffer, size_t size);
bool Parse(rtc::ArrayView<const uint8_t> packet);
@ -89,6 +93,12 @@ class RtpPacket {
// which are modified after FEC protection is generated.
void CopyAndZeroMutableExtensions(rtc::ArrayView<uint8_t> buffer) const;
// Removes extension of given |type|, returns false is extension was not
// registered in packet's extension map or not present in the packet. Only
// extension that should be removed must be registered, other extensions may
// not be registered and will be preserved as is.
bool RemoveExtension(ExtensionType type);
// Writes csrc list. Assumes:
// a) There is enough room left in buffer.
// b) Extension headers, payload or padding data has not already been added.
@ -134,6 +144,9 @@ class RtpPacket {
bool SetPadding(size_t padding_size);
// Returns debug string of RTP packet (without detailed extension info).
std::string ToString() const;
private:
struct ExtensionInfo {
explicit ExtensionInfo(uint8_t id) : ExtensionInfo(id, 0, 0) {}
@ -168,6 +181,7 @@ class RtpPacket {
uint8_t* WriteAt(size_t offset) { return buffer_.data() + offset; }
void WriteAt(size_t offset, uint8_t byte) { buffer_.data()[offset] = byte; }
const uint8_t* ReadAt(size_t offset) const { return buffer_.data() + offset; }
// Header.
bool marker_;

View File

@ -1031,4 +1031,91 @@ TEST(RtpPacketTest, IsExtensionReserved) {
EXPECT_TRUE(packet.IsExtensionReserved<TransmissionOffset>());
}
// Tests that RtpPacket::RemoveExtension can successfully remove extensions.
TEST(RtpPacketTest, RemoveMultipleExtensions) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
extensions.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
RtpPacketToSend packet(&extensions);
packet.SetPayloadType(kPayloadType);
packet.SetSequenceNumber(kSeqNum);
packet.SetTimestamp(kTimestamp);
packet.SetSsrc(kSsrc);
packet.SetExtension<TransmissionOffset>(kTimeOffset);
packet.SetExtension<AudioLevel>(kVoiceActive, kAudioLevel);
EXPECT_THAT(kPacketWithTOAndAL,
ElementsAreArray(packet.data(), packet.size()));
// Remove one of two extensions.
EXPECT_TRUE(packet.RemoveExtension(kRtpExtensionAudioLevel));
EXPECT_THAT(kPacketWithTO, ElementsAreArray(packet.data(), packet.size()));
// Remove remaining extension.
EXPECT_TRUE(packet.RemoveExtension(kRtpExtensionTransmissionTimeOffset));
EXPECT_THAT(kMinimumPacket, ElementsAreArray(packet.data(), packet.size()));
}
// Tests that RtpPacket::RemoveExtension can successfully remove extension when
// other extensions are present but not registered.
TEST(RtpPacketTest, RemoveExtensionPreservesOtherUnregisteredExtensions) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
extensions.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
RtpPacketToSend packet(&extensions);
packet.SetPayloadType(kPayloadType);
packet.SetSequenceNumber(kSeqNum);
packet.SetTimestamp(kTimestamp);
packet.SetSsrc(kSsrc);
packet.SetExtension<TransmissionOffset>(kTimeOffset);
packet.SetExtension<AudioLevel>(kVoiceActive, kAudioLevel);
EXPECT_THAT(kPacketWithTOAndAL,
ElementsAreArray(packet.data(), packet.size()));
// "Unregister" kRtpExtensionTransmissionTimeOffset.
RtpPacketToSend::ExtensionManager extensions1;
extensions1.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
packet.IdentifyExtensions(extensions1);
// Make sure we can not delete extension which is set but not registered.
EXPECT_FALSE(packet.RemoveExtension(kRtpExtensionTransmissionTimeOffset));
// Remove registered extension.
EXPECT_TRUE(packet.RemoveExtension(kRtpExtensionAudioLevel));
EXPECT_THAT(kPacketWithTO, ElementsAreArray(packet.data(), packet.size()));
}
// Tests that RtpPacket::RemoveExtension fails if extension is not present or
// not registered and does not modify packet.
TEST(RtpPacketTest, RemoveExtensionFailure) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
extensions.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
RtpPacketToSend packet(&extensions);
packet.SetPayloadType(kPayloadType);
packet.SetSequenceNumber(kSeqNum);
packet.SetTimestamp(kTimestamp);
packet.SetSsrc(kSsrc);
packet.SetExtension<TransmissionOffset>(kTimeOffset);
EXPECT_THAT(kPacketWithTO, ElementsAreArray(packet.data(), packet.size()));
// Try to remove extension, which was registered, but not set.
EXPECT_FALSE(packet.RemoveExtension(kRtpExtensionAudioLevel));
EXPECT_THAT(kPacketWithTO, ElementsAreArray(packet.data(), packet.size()));
// Try to remove extension, which was not registered.
EXPECT_FALSE(packet.RemoveExtension(kRtpExtensionPlayoutDelay));
EXPECT_THAT(kPacketWithTO, ElementsAreArray(packet.data(), packet.size()));
}
} // namespace webrtc