diff --git a/api/BUILD.gn b/api/BUILD.gn index 4bf0a3b3d3..2617a19f7b 100644 --- a/api/BUILD.gn +++ b/api/BUILD.gn @@ -78,6 +78,24 @@ rtc_source_set("rtp_headers") { ] } +rtc_source_set("rtp_packet_info") { + visibility = [ "*" ] + sources = [ + "rtp_packet_info.cc", + "rtp_packet_info.h", + "rtp_packet_infos.h", + ] + deps = [ + ":array_view", + ":refcountedbase", + ":rtp_headers", + ":scoped_refptr", + "..:webrtc_common", + "../rtc_base:rtc_base_approved", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + rtc_static_library("libjingle_peerconnection_api") { visibility = [ "*" ] cflags = [] @@ -148,6 +166,7 @@ rtc_static_library("libjingle_peerconnection_api") { ":libjingle_logging_api", ":network_state_predictor_api", ":rtc_stats_api", + ":rtp_packet_info", ":scoped_refptr", "audio:audio_mixer_api", "audio_codecs:audio_codecs_api", @@ -811,6 +830,8 @@ if (rtc_include_tests) { "function_view_unittest.cc", "rtc_error_unittest.cc", "rtc_event_log_output_file_unittest.cc", + "rtp_packet_info_unittest.cc", + "rtp_packet_infos_unittest.cc", "rtp_parameters_unittest.cc", "test/loopback_media_transport_unittest.cc", ] @@ -821,6 +842,7 @@ if (rtc_include_tests) { ":libjingle_peerconnection_api", ":loopback_media_transport", ":rtc_event_log_output_file", + ":rtp_packet_info", "../rtc_base:checks", "../rtc_base:gunit_helpers", "../rtc_base:rtc_base_approved", diff --git a/api/rtp_headers.h b/api/rtp_headers.h index 44972ecb08..a7ae5cdf86 100644 --- a/api/rtp_headers.h +++ b/api/rtp_headers.h @@ -51,7 +51,7 @@ struct RTPHeaderExtension { absl::optional feedback_request; // Audio Level includes both level in dBov and voiced/unvoiced bit. See: - // https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/ + // https://tools.ietf.org/html/rfc6464#section-3 bool hasAudioLevel; bool voiceActivity; uint8_t audioLevel; diff --git a/api/rtp_packet_info.cc b/api/rtp_packet_info.cc new file mode 100644 index 0000000000..a61b1733fc --- /dev/null +++ b/api/rtp_packet_info.cc @@ -0,0 +1,58 @@ +/* + * 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 "api/rtp_packet_info.h" + +#include +#include + +namespace webrtc { + +RtpPacketInfo::RtpPacketInfo() + : ssrc_(0), sequence_number_(0), rtp_timestamp_(0), receive_time_ms_(-1) {} + +RtpPacketInfo::RtpPacketInfo(uint32_t ssrc, + std::vector csrcs, + uint16_t sequence_number, + uint32_t rtp_timestamp, + absl::optional audio_level, + int64_t receive_time_ms) + : ssrc_(ssrc), + csrcs_(std::move(csrcs)), + sequence_number_(sequence_number), + rtp_timestamp_(rtp_timestamp), + audio_level_(audio_level), + receive_time_ms_(receive_time_ms) {} + +RtpPacketInfo::RtpPacketInfo(const RTPHeader& rtp_header, + int64_t receive_time_ms) + : ssrc_(rtp_header.ssrc), + sequence_number_(rtp_header.sequenceNumber), + rtp_timestamp_(rtp_header.timestamp), + receive_time_ms_(receive_time_ms) { + const auto& extension = rtp_header.extension; + const auto csrcs_count = std::min(rtp_header.numCSRCs, kRtpCsrcSize); + + csrcs_.assign(&rtp_header.arrOfCSRCs[0], &rtp_header.arrOfCSRCs[csrcs_count]); + + if (extension.hasAudioLevel) { + audio_level_ = extension.audioLevel; + } +} + +bool operator==(const RtpPacketInfo& lhs, const RtpPacketInfo& rhs) { + return (lhs.ssrc() == rhs.ssrc()) && (lhs.csrcs() == rhs.csrcs()) && + (lhs.sequence_number() == rhs.sequence_number()) && + (lhs.rtp_timestamp() == rhs.rtp_timestamp()) && + (lhs.audio_level() == rhs.audio_level()) && + (lhs.receive_time_ms() == rhs.receive_time_ms()); +} + +} // namespace webrtc diff --git a/api/rtp_packet_info.h b/api/rtp_packet_info.h new file mode 100644 index 0000000000..7809067694 --- /dev/null +++ b/api/rtp_packet_info.h @@ -0,0 +1,84 @@ +/* + * 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 API_RTP_PACKET_INFO_H_ +#define API_RTP_PACKET_INFO_H_ + +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/rtp_headers.h" + +namespace webrtc { + +// Structure to hold information about a received |RtpPacket|. +class RtpPacketInfo { + public: + RtpPacketInfo(); + + RtpPacketInfo(uint32_t ssrc, + std::vector csrcs, + uint16_t sequence_number, + uint32_t rtp_timestamp, + absl::optional audio_level, + int64_t receive_time_ms); + + RtpPacketInfo(const RTPHeader& rtp_header, int64_t receive_time_ms); + + RtpPacketInfo(const RtpPacketInfo& other) = default; + RtpPacketInfo(RtpPacketInfo&& other) = default; + RtpPacketInfo& operator=(const RtpPacketInfo& other) = default; + RtpPacketInfo& operator=(RtpPacketInfo&& other) = default; + + uint32_t ssrc() const { return ssrc_; } + void set_ssrc(uint32_t value) { ssrc_ = value; } + + const std::vector& csrcs() const { return csrcs_; } + void set_csrcs(std::vector value) { csrcs_ = std::move(value); } + + uint16_t sequence_number() const { return sequence_number_; } + void set_sequence_number(uint16_t value) { sequence_number_ = value; } + + uint32_t rtp_timestamp() const { return rtp_timestamp_; } + void set_rtp_timestamp(uint32_t value) { rtp_timestamp_ = value; } + + absl::optional audio_level() const { return audio_level_; } + void set_audio_level(absl::optional value) { audio_level_ = value; } + + int64_t receive_time_ms() const { return receive_time_ms_; } + void set_receive_time_ms(int64_t value) { receive_time_ms_ = value; } + + private: + // Fields from the RTP header: + // https://tools.ietf.org/html/rfc3550#section-5.1 + uint32_t ssrc_; + std::vector csrcs_; + uint16_t sequence_number_; + uint32_t rtp_timestamp_; + + // Fields from the Audio Level header extension: + // https://tools.ietf.org/html/rfc6464#section-3 + absl::optional audio_level_; + + // Local |webrtc::Clock|-based timestamp of when the packet was received. + int64_t receive_time_ms_; +}; + +bool operator==(const RtpPacketInfo& lhs, const RtpPacketInfo& rhs); + +inline bool operator!=(const RtpPacketInfo& lhs, const RtpPacketInfo& rhs) { + return !(lhs == rhs); +} + +} // namespace webrtc + +#endif // API_RTP_PACKET_INFO_H_ diff --git a/api/rtp_packet_info_unittest.cc b/api/rtp_packet_info_unittest.cc new file mode 100644 index 0000000000..b9ad1ce359 --- /dev/null +++ b/api/rtp_packet_info_unittest.cc @@ -0,0 +1,180 @@ +/* + * 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 "api/rtp_packet_infos.h" + +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +TEST(RtpPacketInfoTest, Ssrc) { + const uint32_t value = 4038189233; + + RtpPacketInfo lhs; + RtpPacketInfo rhs; + + EXPECT_TRUE(lhs == rhs); + EXPECT_FALSE(lhs != rhs); + + rhs.set_ssrc(value); + EXPECT_EQ(rhs.ssrc(), value); + + EXPECT_FALSE(lhs == rhs); + EXPECT_TRUE(lhs != rhs); + + lhs = rhs; + + EXPECT_TRUE(lhs == rhs); + EXPECT_FALSE(lhs != rhs); + + rhs = RtpPacketInfo(); + EXPECT_NE(rhs.ssrc(), value); + + rhs = RtpPacketInfo(value, {}, {}, {}, {}, {}); + EXPECT_EQ(rhs.ssrc(), value); +} + +TEST(RtpPacketInfoTest, Csrcs) { + const std::vector value = {4038189233, 3016333617, 1207992985}; + + RtpPacketInfo lhs; + RtpPacketInfo rhs; + + EXPECT_TRUE(lhs == rhs); + EXPECT_FALSE(lhs != rhs); + + rhs.set_csrcs(value); + EXPECT_EQ(rhs.csrcs(), value); + + EXPECT_FALSE(lhs == rhs); + EXPECT_TRUE(lhs != rhs); + + lhs = rhs; + + EXPECT_TRUE(lhs == rhs); + EXPECT_FALSE(lhs != rhs); + + rhs = RtpPacketInfo(); + EXPECT_NE(rhs.csrcs(), value); + + rhs = RtpPacketInfo({}, value, {}, {}, {}, {}); + EXPECT_EQ(rhs.csrcs(), value); +} + +TEST(RtpPacketInfoTest, SequenceNumber) { + const uint16_t value = 20238; + + RtpPacketInfo lhs; + RtpPacketInfo rhs; + + EXPECT_TRUE(lhs == rhs); + EXPECT_FALSE(lhs != rhs); + + rhs.set_sequence_number(value); + EXPECT_EQ(rhs.sequence_number(), value); + + EXPECT_FALSE(lhs == rhs); + EXPECT_TRUE(lhs != rhs); + + lhs = rhs; + + EXPECT_TRUE(lhs == rhs); + EXPECT_FALSE(lhs != rhs); + + rhs = RtpPacketInfo(); + EXPECT_NE(rhs.sequence_number(), value); + + rhs = RtpPacketInfo({}, {}, value, {}, {}, {}); + EXPECT_EQ(rhs.sequence_number(), value); +} + +TEST(RtpPacketInfoTest, RtpTimestamp) { + const uint32_t value = 4038189233; + + RtpPacketInfo lhs; + RtpPacketInfo rhs; + + EXPECT_TRUE(lhs == rhs); + EXPECT_FALSE(lhs != rhs); + + rhs.set_rtp_timestamp(value); + EXPECT_EQ(rhs.rtp_timestamp(), value); + + EXPECT_FALSE(lhs == rhs); + EXPECT_TRUE(lhs != rhs); + + lhs = rhs; + + EXPECT_TRUE(lhs == rhs); + EXPECT_FALSE(lhs != rhs); + + rhs = RtpPacketInfo(); + EXPECT_NE(rhs.rtp_timestamp(), value); + + rhs = RtpPacketInfo({}, {}, {}, value, {}, {}); + EXPECT_EQ(rhs.rtp_timestamp(), value); +} + +TEST(RtpPacketInfoTest, AudioLevel) { + const absl::optional value = 31; + + RtpPacketInfo lhs; + RtpPacketInfo rhs; + + EXPECT_TRUE(lhs == rhs); + EXPECT_FALSE(lhs != rhs); + + rhs.set_audio_level(value); + EXPECT_EQ(rhs.audio_level(), value); + + EXPECT_FALSE(lhs == rhs); + EXPECT_TRUE(lhs != rhs); + + lhs = rhs; + + EXPECT_TRUE(lhs == rhs); + EXPECT_FALSE(lhs != rhs); + + rhs = RtpPacketInfo(); + EXPECT_NE(rhs.audio_level(), value); + + rhs = RtpPacketInfo({}, {}, {}, {}, value, {}); + EXPECT_EQ(rhs.audio_level(), value); +} + +TEST(RtpPacketInfoTest, ReceiveTimeMs) { + const int64_t value = 8868963877546349045LL; + + RtpPacketInfo lhs; + RtpPacketInfo rhs; + + EXPECT_TRUE(lhs == rhs); + EXPECT_FALSE(lhs != rhs); + + rhs.set_receive_time_ms(value); + EXPECT_EQ(rhs.receive_time_ms(), value); + + EXPECT_FALSE(lhs == rhs); + EXPECT_TRUE(lhs != rhs); + + lhs = rhs; + + EXPECT_TRUE(lhs == rhs); + EXPECT_FALSE(lhs != rhs); + + rhs = RtpPacketInfo(); + EXPECT_NE(rhs.receive_time_ms(), value); + + rhs = RtpPacketInfo({}, {}, {}, {}, {}, value); + EXPECT_EQ(rhs.receive_time_ms(), value); +} + +} // namespace webrtc diff --git a/api/rtp_packet_infos.h b/api/rtp_packet_infos.h new file mode 100644 index 0000000000..a19163f019 --- /dev/null +++ b/api/rtp_packet_infos.h @@ -0,0 +1,109 @@ +/* + * 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 API_RTP_PACKET_INFOS_H_ +#define API_RTP_PACKET_INFOS_H_ + +#include +#include + +#include "api/ref_counted_base.h" +#include "api/rtp_packet_info.h" +#include "api/scoped_refptr.h" + +namespace webrtc { + +// Semi-immutable structure to hold information about packets used to assemble +// an audio or video frame. Uses internal reference counting to make it very +// cheap to copy. +// +// We should ideally just use |std::vector| and have it +// |std::move()|-ed as the per-packet information is transferred from one object +// to another. But moving the info, instead of copying it, is not easily done +// for the current video code. +class RtpPacketInfos { + public: + using vector_type = std::vector; + + using value_type = vector_type::value_type; + using size_type = vector_type::size_type; + using difference_type = vector_type::difference_type; + using const_reference = vector_type::const_reference; + using const_pointer = vector_type::const_pointer; + using const_iterator = vector_type::const_iterator; + using const_reverse_iterator = vector_type::const_reverse_iterator; + + using reference = const_reference; + using pointer = const_pointer; + using iterator = const_iterator; + using reverse_iterator = const_reverse_iterator; + + RtpPacketInfos() {} + explicit RtpPacketInfos(vector_type entries) : data_(Data::Create(entries)) {} + + RtpPacketInfos(const RtpPacketInfos& other) = default; + RtpPacketInfos(RtpPacketInfos&& other) = default; + RtpPacketInfos& operator=(const RtpPacketInfos& other) = default; + RtpPacketInfos& operator=(RtpPacketInfos&& other) = default; + + const_reference operator[](size_type pos) const { return entries()[pos]; } + + const_reference at(size_type pos) const { return entries().at(pos); } + const_reference front() const { return entries().front(); } + const_reference back() const { return entries().back(); } + + const_iterator begin() const { return entries().begin(); } + const_iterator end() const { return entries().end(); } + const_reverse_iterator rbegin() const { return entries().rbegin(); } + const_reverse_iterator rend() const { return entries().rend(); } + + const_iterator cbegin() const { return entries().cbegin(); } + const_iterator cend() const { return entries().cend(); } + const_reverse_iterator crbegin() const { return entries().crbegin(); } + const_reverse_iterator crend() const { return entries().crend(); } + + bool empty() const { return entries().empty(); } + size_type size() const { return entries().size(); } + + private: + class Data : public rtc::RefCountedBase { + public: + static rtc::scoped_refptr Create(vector_type entries) { + return new Data(entries); + } + + const vector_type& entries() const { return entries_; } + + private: + explicit Data(vector_type entries) : entries_(entries) {} + ~Data() override {} + + const vector_type entries_; + }; + + static const vector_type& empty_entries() { + static const vector_type& value = *new vector_type(); + return value; + } + + const vector_type& entries() const { + if (data_ != nullptr) { + return data_->entries(); + } else { + return empty_entries(); + } + } + + rtc::scoped_refptr data_; +}; + +} // namespace webrtc + +#endif // API_RTP_PACKET_INFOS_H_ diff --git a/api/rtp_packet_infos_unittest.cc b/api/rtp_packet_infos_unittest.cc new file mode 100644 index 0000000000..0e1b89a86d --- /dev/null +++ b/api/rtp_packet_infos_unittest.cc @@ -0,0 +1,85 @@ +/* + * 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 "api/rtp_packet_infos.h" + +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +using ::testing::ElementsAre; +using ::testing::SizeIs; + +template +RtpPacketInfos::vector_type ToVector(Iterator begin, Iterator end) { + return RtpPacketInfos::vector_type(begin, end); +} + +} // namespace + +TEST(RtpPacketInfosTest, BasicFunctionality) { + RtpPacketInfo p0(123, {1, 2}, 42, 89, 5, 7); + RtpPacketInfo p1(456, {3, 4}, 37, 89, 4, 1); + RtpPacketInfo p2(789, {5, 6}, 91, 88, 1, 7); + + RtpPacketInfos x({p0, p1, p2}); + + ASSERT_THAT(x, SizeIs(3)); + + EXPECT_EQ(x[0], p0); + EXPECT_EQ(x[1], p1); + EXPECT_EQ(x[2], p2); + + EXPECT_EQ(x.front(), p0); + EXPECT_EQ(x.back(), p2); + + EXPECT_THAT(ToVector(x.begin(), x.end()), ElementsAre(p0, p1, p2)); + EXPECT_THAT(ToVector(x.rbegin(), x.rend()), ElementsAre(p2, p1, p0)); + + EXPECT_THAT(ToVector(x.cbegin(), x.cend()), ElementsAre(p0, p1, p2)); + EXPECT_THAT(ToVector(x.crbegin(), x.crend()), ElementsAre(p2, p1, p0)); + + EXPECT_FALSE(x.empty()); +} + +TEST(RtpPacketInfosTest, CopyShareData) { + RtpPacketInfo p0(123, {1, 2}, 42, 89, 5, 7); + RtpPacketInfo p1(456, {3, 4}, 37, 89, 4, 1); + RtpPacketInfo p2(789, {5, 6}, 91, 88, 1, 7); + + RtpPacketInfos lhs({p0, p1, p2}); + RtpPacketInfos rhs = lhs; + + ASSERT_THAT(lhs, SizeIs(3)); + ASSERT_THAT(rhs, SizeIs(3)); + + for (size_t i = 0; i < lhs.size(); ++i) { + EXPECT_EQ(lhs[i], rhs[i]); + } + + EXPECT_EQ(lhs.front(), rhs.front()); + EXPECT_EQ(lhs.back(), rhs.back()); + + EXPECT_EQ(lhs.begin(), rhs.begin()); + EXPECT_EQ(lhs.end(), rhs.end()); + EXPECT_EQ(lhs.rbegin(), rhs.rbegin()); + EXPECT_EQ(lhs.rend(), rhs.rend()); + + EXPECT_EQ(lhs.cbegin(), rhs.cbegin()); + EXPECT_EQ(lhs.cend(), rhs.cend()); + EXPECT_EQ(lhs.crbegin(), rhs.crbegin()); + EXPECT_EQ(lhs.crend(), rhs.crend()); + + EXPECT_EQ(lhs.empty(), rhs.empty()); +} + +} // namespace webrtc