From eed52bff8d3e2d83207da624ec0a8f6441f1c98f Mon Sep 17 00:00:00 2001 From: nisse Date: Fri, 19 May 2017 06:15:19 -0700 Subject: [PATCH] New class RtxReceiveStream. BUG=webrtc:7135 Review-Url: https://codereview.webrtc.org/2888093002 Cr-Commit-Position: refs/heads/master@{#18212} --- webrtc/call/BUILD.gn | 3 + webrtc/call/rtx_receive_stream.cc | 56 +++++++++ webrtc/call/rtx_receive_stream.h | 40 ++++++ webrtc/call/rtx_receive_stream_unittest.cc | 135 +++++++++++++++++++++ 4 files changed, 234 insertions(+) create mode 100644 webrtc/call/rtx_receive_stream.cc create mode 100644 webrtc/call/rtx_receive_stream.h create mode 100644 webrtc/call/rtx_receive_stream_unittest.cc diff --git a/webrtc/call/BUILD.gn b/webrtc/call/BUILD.gn index 0d87b2f9f7..7728104cd1 100644 --- a/webrtc/call/BUILD.gn +++ b/webrtc/call/BUILD.gn @@ -42,6 +42,8 @@ rtc_static_library("call") { "rtp_demuxer.cc", "rtp_transport_controller_send.cc", "rtp_transport_controller_send.h", + "rtx_receive_stream.cc", + "rtx_receive_stream.h", ] if (!build_with_chromium && is_clang) { @@ -87,6 +89,7 @@ if (rtc_include_tests) { "bitrate_estimator_tests.cc", "call_unittest.cc", "flexfec_receive_stream_unittest.cc", + "rtx_receive_stream_unittest.cc", ] deps = [ ":call", diff --git a/webrtc/call/rtx_receive_stream.cc b/webrtc/call/rtx_receive_stream.cc new file mode 100644 index 0000000000..286f867faa --- /dev/null +++ b/webrtc/call/rtx_receive_stream.cc @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 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 + +#include "webrtc/call/rtx_receive_stream.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_packet_received.h" + +namespace webrtc { + +RtxReceiveStream::RtxReceiveStream( + RtpPacketSinkInterface* media_sink, + std::map rtx_payload_type_map, + uint32_t media_ssrc) + : media_sink_(media_sink), + rtx_payload_type_map_(std::move(rtx_payload_type_map)), + media_ssrc_(media_ssrc) {} + +void RtxReceiveStream::OnRtpPacket(const RtpPacketReceived& rtx_packet) { + rtc::ArrayView payload = rtx_packet.payload(); + + if (payload.size() < kRtxHeaderSize) { + return; + } + + auto it = rtx_payload_type_map_.find(rtx_packet.PayloadType()); + if (it == rtx_payload_type_map_.end()) { + return; + } + RtpPacketReceived media_packet; + media_packet.CopyHeaderFrom(rtx_packet); + + media_packet.SetSsrc(media_ssrc_); + media_packet.SetSequenceNumber((payload[0] << 8) + payload[1]); + media_packet.SetPayloadType(it->second); + + // Skip the RTX header. + rtc::ArrayView rtx_payload = + payload.subview(kRtxHeaderSize); + + uint8_t* media_payload = media_packet.AllocatePayload(rtx_payload.size()); + RTC_DCHECK(media_payload != nullptr); + + memcpy(media_payload, rtx_payload.data(), rtx_payload.size()); + + media_sink_->OnRtpPacket(media_packet); +} + +} // namespace webrtc diff --git a/webrtc/call/rtx_receive_stream.h b/webrtc/call/rtx_receive_stream.h new file mode 100644 index 0000000000..6e028519f4 --- /dev/null +++ b/webrtc/call/rtx_receive_stream.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017 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_CALL_RTX_RECEIVE_STREAM_H_ +#define WEBRTC_CALL_RTX_RECEIVE_STREAM_H_ + +#include + +#include "webrtc/call/rtp_demuxer.h" + +namespace webrtc { + +class RtxReceiveStream : public RtpPacketSinkInterface { + public: + RtxReceiveStream(RtpPacketSinkInterface* media_sink, + std::map rtx_payload_type_map, + uint32_t media_ssrc); + + // RtpPacketSinkInterface. + void OnRtpPacket(const RtpPacketReceived& packet) override; + + private: + RtpPacketSinkInterface* const media_sink_; + // Mapping rtx_payload_type_map_[rtx] = associated. + const std::map rtx_payload_type_map_; + // TODO(nisse): Ultimately, the media receive stream shouldn't care about the + // ssrc, and we should delete this. + const uint32_t media_ssrc_; +}; + +} // namespace webrtc + +#endif // WEBRTC_CALL_RTX_RECEIVE_STREAM_H_ diff --git a/webrtc/call/rtx_receive_stream_unittest.cc b/webrtc/call/rtx_receive_stream_unittest.cc new file mode 100644 index 0000000000..6bb067b81b --- /dev/null +++ b/webrtc/call/rtx_receive_stream_unittest.cc @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2017 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/call/rtx_receive_stream.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_packet_received.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_header_extension.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "webrtc/test/gmock.h" +#include "webrtc/test/gtest.h" + +namespace webrtc { + +namespace { + +using ::testing::_; +using ::testing::StrictMock; + +class MockRtpPacketSink : public RtpPacketSinkInterface { + public: + MOCK_METHOD1(OnRtpPacket, void(const RtpPacketReceived&)); +}; + +constexpr int kMediaPayloadType = 100; +constexpr int kRtxPayloadType = 98; +constexpr uint32_t kMediaSSRC = 0x3333333; +constexpr uint16_t kMediaSeqno = 0x5657; + +constexpr uint8_t kRtxPacket[] = { + 0x80, // Version 2. + 98, // Payload type. + 0x12, 0x34, // Seqno. + 0x11, 0x11, 0x11, 0x11, // Timestamp. + 0x22, 0x22, 0x22, 0x22, // SSRC. + // RTX header. + 0x56, 0x57, // Orig seqno. + // Payload. + 0xee, +}; + +constexpr uint8_t kRtxPacketWithCVO[] = { + 0x90, // Version 2, X set. + 98, // Payload type. + 0x12, 0x34, // Seqno. + 0x11, 0x11, 0x11, 0x11, // Timestamp. + 0x22, 0x22, 0x22, 0x22, // SSRC. + 0xbe, 0xde, 0x00, 0x01, // Extension header. + 0x30, 0x01, 0x00, 0x00, // 90 degree rotation. + // RTX header. + 0x56, 0x57, // Orig seqno. + // Payload. + 0xee, +}; + +std::map PayloadTypeMapping() { + std::map m; + m[kRtxPayloadType] = kMediaPayloadType; + return m; +} + +template +rtc::ArrayView Truncate(rtc::ArrayView a, size_t drop) { + return a.subview(0, a.size() - drop); +} + +} // namespace + +TEST(RtxReceiveStreamTest, RestoresPacketPayload) { + StrictMock media_sink; + RtxReceiveStream rtx_sink(&media_sink, PayloadTypeMapping(), kMediaSSRC); + RtpPacketReceived rtx_packet; + EXPECT_TRUE(rtx_packet.Parse(rtc::ArrayView(kRtxPacket))); + + EXPECT_CALL(media_sink, OnRtpPacket(_)).WillOnce(testing::Invoke( + [](const RtpPacketReceived& packet) { + EXPECT_EQ(packet.SequenceNumber(), kMediaSeqno); + EXPECT_EQ(packet.Ssrc(), kMediaSSRC); + EXPECT_EQ(packet.PayloadType(), kMediaPayloadType); + EXPECT_THAT(packet.payload(), testing::ElementsAre(0xee)); + })); + + rtx_sink.OnRtpPacket(rtx_packet); +} + +TEST(RtxReceiveStreamTest, IgnoresUnknownPayloadType) { + StrictMock media_sink; + RtxReceiveStream rtx_sink(&media_sink, std::map(), kMediaSSRC); + RtpPacketReceived rtx_packet; + EXPECT_TRUE(rtx_packet.Parse(rtc::ArrayView(kRtxPacket))); + rtx_sink.OnRtpPacket(rtx_packet); +} + +TEST(RtxReceiveStreamTest, IgnoresTruncatedPacket) { + StrictMock media_sink; + RtxReceiveStream rtx_sink(&media_sink, PayloadTypeMapping(), kMediaSSRC); + RtpPacketReceived rtx_packet; + EXPECT_TRUE( + rtx_packet.Parse(Truncate(rtc::ArrayView(kRtxPacket), 2))); + rtx_sink.OnRtpPacket(rtx_packet); +} + +TEST(RtxReceiveStreamTest, CopiesRtpHeaderExtensions) { + StrictMock media_sink; + RtxReceiveStream rtx_sink(&media_sink, PayloadTypeMapping(), kMediaSSRC); + RtpHeaderExtensionMap extension_map; + extension_map.RegisterByType(3, kRtpExtensionVideoRotation); + RtpPacketReceived rtx_packet(&extension_map); + EXPECT_TRUE(rtx_packet.Parse( + rtc::ArrayView(kRtxPacketWithCVO))); + + VideoRotation rotation = kVideoRotation_0; + EXPECT_TRUE(rtx_packet.GetExtension(&rotation)); + EXPECT_EQ(kVideoRotation_90, rotation); + + EXPECT_CALL(media_sink, OnRtpPacket(_)).WillOnce(testing::Invoke( + [](const RtpPacketReceived& packet) { + EXPECT_EQ(packet.SequenceNumber(), kMediaSeqno); + EXPECT_EQ(packet.Ssrc(), kMediaSSRC); + EXPECT_EQ(packet.PayloadType(), kMediaPayloadType); + EXPECT_THAT(packet.payload(), testing::ElementsAre(0xee)); + VideoRotation rotation = kVideoRotation_0; + EXPECT_TRUE(packet.GetExtension(&rotation)); + EXPECT_EQ(rotation, kVideoRotation_90); + })); + + rtx_sink.OnRtpPacket(rtx_packet); +} + +} // namespace webrtc