From 7021b92525cbcd99c83af35dfa4c3f355997246a Mon Sep 17 00:00:00 2001 From: Danil Chapovalov Date: Tue, 15 Mar 2016 17:39:32 +0100 Subject: [PATCH] introduced rtcp::CommonHeader class this class replace and extend RTCPUtility::RtcpCommonHeader structure and RTCPUtility::RtcpParseCommonHeader function. In addition to header fields, payload pointer is stored because rtcp header without payload is rarely useful. Sample usage can be checked in 'RTCP Parser sketched' CL: https://codereview.webrtc.org/1555683002/ BUG=webrtc:5260 R=asapersson@webrtc.org, sprang@webrtc.org Review URL: https://codereview.webrtc.org/1575413002 . Cr-Commit-Position: refs/heads/master@{#11999} --- webrtc/modules/modules.gyp | 1 + webrtc/modules/rtp_rtcp/BUILD.gn | 2 + webrtc/modules/rtp_rtcp/rtp_rtcp.gypi | 2 + .../source/rtcp_packet/common_header.cc | 86 +++++++++++++++ .../source/rtcp_packet/common_header.h | 46 ++++++++ .../rtcp_packet/common_header_unittest.cc | 104 ++++++++++++++++++ 6 files changed, 241 insertions(+) create mode 100644 webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.cc create mode 100644 webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.h create mode 100644 webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header_unittest.cc diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index 5946bafb85..d65507d316 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -306,6 +306,7 @@ 'rtp_rtcp/source/rtcp_packet_unittest.cc', 'rtp_rtcp/source/rtcp_packet/app_unittest.cc', 'rtp_rtcp/source/rtcp_packet/bye_unittest.cc', + 'rtp_rtcp/source/rtcp_packet/common_header_unittest.cc', 'rtp_rtcp/source/rtcp_packet/compound_packet_unittest.cc', 'rtp_rtcp/source/rtcp_packet/dlrr_unittest.cc', 'rtp_rtcp/source/rtcp_packet/extended_jitter_report_unittest.cc', diff --git a/webrtc/modules/rtp_rtcp/BUILD.gn b/webrtc/modules/rtp_rtcp/BUILD.gn index e238dfa999..d386951cb0 100644 --- a/webrtc/modules/rtp_rtcp/BUILD.gn +++ b/webrtc/modules/rtp_rtcp/BUILD.gn @@ -50,6 +50,8 @@ source_set("rtp_rtcp") { "source/rtcp_packet/app.h", "source/rtcp_packet/bye.cc", "source/rtcp_packet/bye.h", + "source/rtcp_packet/common_header.cc", + "source/rtcp_packet/common_header.h", "source/rtcp_packet/compound_packet.cc", "source/rtcp_packet/compound_packet.h", "source/rtcp_packet/dlrr.cc", diff --git a/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi b/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi index d14ac2b927..23a64b752f 100644 --- a/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi +++ b/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi @@ -45,6 +45,8 @@ 'source/rtcp_packet/app.h', 'source/rtcp_packet/bye.cc', 'source/rtcp_packet/bye.h', + 'source/rtcp_packet/common_header.cc', + 'source/rtcp_packet/common_header.h', 'source/rtcp_packet/compound_packet.cc', 'source/rtcp_packet/compound_packet.h', 'source/rtcp_packet/dlrr.cc', diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.cc new file mode 100644 index 0000000000..69d78ce4bb --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.cc @@ -0,0 +1,86 @@ +/* + * 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 "webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.h" + +#include "webrtc/base/logging.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" + +namespace webrtc { +namespace rtcp { +// 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 size_t kHeaderSizeBytes = 4; + const uint8_t kVersion = 2; + + if (size_bytes < kHeaderSizeBytes) { + 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) { + LOG(LS_WARNING) << "Invalid RTCP header: Version must be " + << static_cast(kVersion) << " but was " + << static_cast(version); + return false; + } + + bool has_padding = (buffer[0] & 0x20) != 0; + count_or_format_ = buffer[0] & 0x1F; + packet_type_ = buffer[1]; + payload_size_ = ByteReader::ReadBigEndian(&buffer[2]) * 4; + payload_ = buffer + kHeaderSizeBytes; + padding_size_ = 0; + + if (size_bytes < kHeaderSizeBytes + payload_size_) { + 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) { + 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) { + LOG(LS_WARNING) << "Invalid RTCP header: Padding bit set but 0 padding " + "size specified."; + return false; + } + if (padding_size_ > payload_size_) { + 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 diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.h new file mode 100644 index 0000000000..b4ce511b5f --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.h @@ -0,0 +1,46 @@ +/* + * 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 WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMMON_HEADER_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMMON_HEADER_H_ + +#include "webrtc/base/basictypes.h" + +namespace webrtc { +namespace rtcp { +class CommonHeader { + public: + 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_; } + // 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 // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMMON_HEADER_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header_unittest.cc new file mode 100644 index 0000000000..d83de1469b --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header_unittest.cc @@ -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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.h" + +#include "testing/gtest/include/gtest/gtest.h" + +using webrtc::rtcp::CommonHeader; + +namespace webrtc { +namespace { +const size_t kHeaderSizeBytes = 4; +} // namespace + +TEST(RtcpCommonHeaderTest, TooSmallBuffer) { + uint8_t buffer[] = {0x80, 0x00, 0x00, 0x00}; + CommonHeader header; + // Buffer needs to be able to hold the header. + EXPECT_FALSE(header.Parse(buffer, 0)); + EXPECT_FALSE(header.Parse(buffer, 1)); + EXPECT_FALSE(header.Parse(buffer, 2)); + EXPECT_FALSE(header.Parse(buffer, 3)); + EXPECT_TRUE(header.Parse(buffer, 4)); +} + +TEST(RtcpCommonHeaderTest, Version) { + uint8_t buffer[] = {0x00, 0x00, 0x00, 0x00}; + CommonHeader header; + // Version 2 is the only allowed. + buffer[0] = 0 << 6; + EXPECT_FALSE(header.Parse(buffer, sizeof(buffer))); + buffer[0] = 1 << 6; + EXPECT_FALSE(header.Parse(buffer, sizeof(buffer))); + buffer[0] = 2 << 6; + EXPECT_TRUE(header.Parse(buffer, sizeof(buffer))); + buffer[0] = 3 << 6; + EXPECT_FALSE(header.Parse(buffer, sizeof(buffer))); +} + +TEST(RtcpCommonHeaderTest, PacketSize) { + uint8_t buffer[] = {0x80, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; + CommonHeader header; + EXPECT_FALSE(header.Parse(buffer, sizeof(buffer) - 1)); + EXPECT_TRUE(header.Parse(buffer, sizeof(buffer))); + EXPECT_EQ(8u, header.payload_size_bytes()); + EXPECT_EQ(buffer + sizeof(buffer), header.NextPacket()); +} + +TEST(RtcpCommonHeaderTest, PaddingAndPayloadSize) { + // Set v = 2, p = 1, but leave fmt, pt as 0. + uint8_t buffer[] = {0xa0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; + CommonHeader header; + // Padding bit set, but no byte for padding (can't specify padding length). + EXPECT_FALSE(header.Parse(buffer, 4)); + + buffer[3] = 2; // Set payload size to 2x32bit. + const size_t kPayloadSizeBytes = buffer[3] * 4; + const size_t kPaddingAddress = kHeaderSizeBytes + kPayloadSizeBytes - 1; + + // Padding one byte larger than possible. + buffer[kPaddingAddress] = kPayloadSizeBytes + 1; + EXPECT_FALSE(header.Parse(buffer, sizeof(buffer))); + + // Invalid zero padding size. + buffer[kPaddingAddress] = 0; + EXPECT_FALSE(header.Parse(buffer, sizeof(buffer))); + + // Pure padding packet. + buffer[kPaddingAddress] = kPayloadSizeBytes; + EXPECT_TRUE(header.Parse(buffer, sizeof(buffer))); + EXPECT_EQ(0u, header.payload_size_bytes()); + EXPECT_EQ(buffer + sizeof(buffer), header.NextPacket()); + EXPECT_EQ(header.payload(), buffer + kHeaderSizeBytes); + + // Single byte of actual data. + buffer[kPaddingAddress] = kPayloadSizeBytes - 1; + EXPECT_TRUE(header.Parse(buffer, sizeof(buffer))); + EXPECT_EQ(1u, header.payload_size_bytes()); + EXPECT_EQ(buffer + sizeof(buffer), header.NextPacket()); +} + +TEST(RtcpCommonHeaderTest, FormatAndPayloadType) { + uint8_t buffer[] = {0x9e, 0xab, 0x00, 0x00}; + CommonHeader header; + EXPECT_TRUE(header.Parse(buffer, sizeof(buffer))); + + EXPECT_EQ(header.count(), 0x1e); + EXPECT_EQ(header.fmt(), 0x1e); + EXPECT_EQ(header.type(), 0xab); + EXPECT_EQ(header.payload_size_bytes(), 0u); + EXPECT_EQ(header.payload(), buffer + kHeaderSizeBytes); +} +} // namespace webrtc