Delete RtpReceiver and related code.
The RtpReceiver class is no longer used. Together with it, delete RTPPayloadRegistry, RtpReceiverStrategy, and the tests under modules/rtp_rtcp/test/testAPI/. Bug: webrtc:8995 Change-Id: Ia9924d2f0f4315914a0dce6b7375ebb3601a6f96 Reviewed-on: https://webrtc-review.googlesource.com/c/103503 Reviewed-by: Danil Chapovalov <danilchap@webrtc.org> Reviewed-by: Patrik Höglund <phoglund@webrtc.org> Commit-Queue: Niels Moller <nisse@webrtc.org> Cr-Commit-Position: refs/heads/master@{#24968}
This commit is contained in:
parent
e9b74a77b1
commit
4a72ba99a7
@ -30,7 +30,6 @@
|
||||
#include "modules/pacing/packet_router.h"
|
||||
#include "modules/rtp_rtcp/include/receive_statistics.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_receiver_strategy.h"
|
||||
#include "modules/utility/include/process_thread.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/criticalsection.h"
|
||||
|
||||
@ -111,8 +111,6 @@ rtc_static_library("rtp_rtcp") {
|
||||
"include/receive_statistics.h",
|
||||
"include/remote_ntp_time_estimator.h",
|
||||
"include/rtp_header_parser.h",
|
||||
"include/rtp_payload_registry.h",
|
||||
"include/rtp_receiver.h",
|
||||
"include/rtp_rtcp.h",
|
||||
"include/ulpfec_receiver.h",
|
||||
"source/contributing_sources.cc",
|
||||
@ -159,15 +157,6 @@ rtc_static_library("rtp_rtcp") {
|
||||
"source/rtp_header_parser.cc",
|
||||
"source/rtp_packet_history.cc",
|
||||
"source/rtp_packet_history.h",
|
||||
"source/rtp_payload_registry.cc",
|
||||
"source/rtp_receiver_audio.cc",
|
||||
"source/rtp_receiver_audio.h",
|
||||
"source/rtp_receiver_impl.cc",
|
||||
"source/rtp_receiver_impl.h",
|
||||
"source/rtp_receiver_strategy.cc",
|
||||
"source/rtp_receiver_strategy.h",
|
||||
"source/rtp_receiver_video.cc",
|
||||
"source/rtp_receiver_video.h",
|
||||
"source/rtp_rtcp_config.h",
|
||||
"source/rtp_rtcp_impl.cc",
|
||||
"source/rtp_rtcp_impl.h",
|
||||
@ -410,8 +399,6 @@ if (rtc_include_tests) {
|
||||
"source/rtp_header_extension_size_unittest.cc",
|
||||
"source/rtp_packet_history_unittest.cc",
|
||||
"source/rtp_packet_unittest.cc",
|
||||
"source/rtp_payload_registry_unittest.cc",
|
||||
"source/rtp_receiver_unittest.cc",
|
||||
"source/rtp_rtcp_impl_unittest.cc",
|
||||
"source/rtp_sender_unittest.cc",
|
||||
"source/rtp_utility_unittest.cc",
|
||||
@ -419,11 +406,6 @@ if (rtc_include_tests) {
|
||||
"source/ulpfec_generator_unittest.cc",
|
||||
"source/ulpfec_header_reader_writer_unittest.cc",
|
||||
"source/ulpfec_receiver_unittest.cc",
|
||||
"test/testAPI/test_api.cc",
|
||||
"test/testAPI/test_api.h",
|
||||
"test/testAPI/test_api_audio.cc",
|
||||
"test/testAPI/test_api_rtcp.cc",
|
||||
"test/testAPI/test_api_video.cc",
|
||||
]
|
||||
deps = [
|
||||
":fec_test_helper",
|
||||
|
||||
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 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 MODULES_RTP_RTCP_INCLUDE_RTP_PAYLOAD_REGISTRY_H_
|
||||
#define MODULES_RTP_RTCP_INCLUDE_RTP_PAYLOAD_REGISTRY_H_
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/audio_codecs/audio_format.h"
|
||||
#include "api/video_codecs/video_codec.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_utility.h"
|
||||
#include "rtc_base/criticalsection.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RTPPayloadRegistry {
|
||||
public:
|
||||
RTPPayloadRegistry();
|
||||
~RTPPayloadRegistry();
|
||||
|
||||
// TODO(magjed): Split RTPPayloadRegistry into separate Audio and Video class
|
||||
// and simplify the code. http://crbug/webrtc/6743.
|
||||
|
||||
// Replace all audio receive payload types with the given map.
|
||||
void SetAudioReceivePayloads(std::map<int, SdpAudioFormat> codecs);
|
||||
|
||||
int32_t RegisterReceivePayload(int payload_type,
|
||||
const SdpAudioFormat& audio_format,
|
||||
bool* created_new_payload_type);
|
||||
int32_t RegisterReceivePayload(const VideoCodec& video_codec);
|
||||
|
||||
int32_t DeRegisterReceivePayload(int8_t payload_type);
|
||||
|
||||
int GetPayloadTypeFrequency(uint8_t payload_type) const;
|
||||
|
||||
absl::optional<RtpUtility::Payload> PayloadTypeToPayload(
|
||||
uint8_t payload_type) const;
|
||||
|
||||
private:
|
||||
// Prunes the payload type map of the specific payload type, if it exists.
|
||||
void DeregisterAudioCodecOrRedTypeRegardlessOfPayloadType(
|
||||
const SdpAudioFormat& audio_format);
|
||||
|
||||
rtc::CriticalSection crit_sect_;
|
||||
std::map<int, RtpUtility::Payload> payload_type_map_;
|
||||
|
||||
// As a first step in splitting this class up in separate cases for audio and
|
||||
// video, DCHECK that no instance is used for both audio and video.
|
||||
#if RTC_DCHECK_IS_ON
|
||||
bool used_for_audio_ RTC_GUARDED_BY(crit_sect_) = false;
|
||||
bool used_for_video_ RTC_GUARDED_BY(crit_sect_) = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_INCLUDE_RTP_PAYLOAD_REGISTRY_H_
|
||||
@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 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 MODULES_RTP_RTCP_INCLUDE_RTP_RECEIVER_H_
|
||||
#define MODULES_RTP_RTCP_INCLUDE_RTP_RECEIVER_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "api/rtpreceiverinterface.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RTPPayloadRegistry;
|
||||
class VideoCodec;
|
||||
|
||||
class RtpReceiver {
|
||||
public:
|
||||
// Creates a video-enabled RTP receiver.
|
||||
static RtpReceiver* CreateVideoReceiver(
|
||||
Clock* clock,
|
||||
RtpData* incoming_payload_callback,
|
||||
RTPPayloadRegistry* rtp_payload_registry);
|
||||
|
||||
// Creates an audio-enabled RTP receiver.
|
||||
static RtpReceiver* CreateAudioReceiver(
|
||||
Clock* clock,
|
||||
RtpData* incoming_payload_callback,
|
||||
RTPPayloadRegistry* rtp_payload_registry);
|
||||
|
||||
virtual ~RtpReceiver() {}
|
||||
|
||||
// Registers a receive payload in the payload registry and notifies the media
|
||||
// receiver strategy.
|
||||
virtual int32_t RegisterReceivePayload(
|
||||
int payload_type,
|
||||
const SdpAudioFormat& audio_format) = 0;
|
||||
|
||||
// Deprecated version of the above.
|
||||
int32_t RegisterReceivePayload(const CodecInst& audio_codec);
|
||||
|
||||
// Registers a receive payload in the payload registry.
|
||||
virtual int32_t RegisterReceivePayload(const VideoCodec& video_codec) = 0;
|
||||
|
||||
// De-registers |payload_type| from the payload registry.
|
||||
virtual int32_t DeRegisterReceivePayload(const int8_t payload_type) = 0;
|
||||
|
||||
// Parses the media specific parts of an RTP packet and updates the receiver
|
||||
// state. This for instance means that any changes in SSRC and payload type is
|
||||
// detected and acted upon.
|
||||
virtual bool IncomingRtpPacket(const RTPHeader& rtp_header,
|
||||
const uint8_t* payload,
|
||||
size_t payload_length,
|
||||
PayloadUnion payload_specific) = 0;
|
||||
// TODO(nisse): Deprecated version, delete as soon as downstream
|
||||
// applications are updated.
|
||||
bool IncomingRtpPacket(const RTPHeader& rtp_header,
|
||||
const uint8_t* payload,
|
||||
size_t payload_length,
|
||||
PayloadUnion payload_specific,
|
||||
bool in_order /* Ignored */) {
|
||||
return IncomingRtpPacket(rtp_header, payload, payload_length,
|
||||
payload_specific);
|
||||
}
|
||||
|
||||
// Gets the RTP timestamp and the corresponding monotonic system
|
||||
// time for the most recent in-order packet. Returns true on
|
||||
// success, false if no packet has been received.
|
||||
virtual bool GetLatestTimestamps(uint32_t* timestamp,
|
||||
int64_t* receive_time_ms) const = 0;
|
||||
|
||||
// Returns the remote SSRC of the currently received RTP stream.
|
||||
virtual uint32_t SSRC() const = 0;
|
||||
|
||||
virtual std::vector<RtpSource> GetSources() const = 0;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_INCLUDE_RTP_RECEIVER_H_
|
||||
@ -1,218 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 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/include/rtp_payload_registry.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "modules/audio_coding/codecs/audio_format_conversion.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/stringutils.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
bool PayloadIsCompatible(const RtpUtility::Payload& payload,
|
||||
const SdpAudioFormat& audio_format) {
|
||||
return payload.typeSpecific.is_audio() &&
|
||||
audio_format.Matches(payload.typeSpecific.audio_payload().format);
|
||||
}
|
||||
|
||||
bool PayloadIsCompatible(const RtpUtility::Payload& payload,
|
||||
const VideoCodec& video_codec) {
|
||||
if (!payload.typeSpecific.is_video() ||
|
||||
_stricmp(payload.name, CodecTypeToPayloadString(video_codec.codecType)) !=
|
||||
0)
|
||||
return false;
|
||||
// For H264, profiles must match as well.
|
||||
if (video_codec.codecType == kVideoCodecH264) {
|
||||
return video_codec.H264().profile ==
|
||||
payload.typeSpecific.video_payload().h264_profile;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
RtpUtility::Payload CreatePayloadType(const SdpAudioFormat& audio_format) {
|
||||
RTC_DCHECK_GE(audio_format.clockrate_hz, 1000);
|
||||
return {audio_format.name.c_str(),
|
||||
PayloadUnion(AudioPayload{audio_format, 0})};
|
||||
}
|
||||
|
||||
RtpUtility::Payload CreatePayloadType(const VideoCodec& video_codec) {
|
||||
VideoPayload p;
|
||||
p.videoCodecType = video_codec.codecType;
|
||||
if (video_codec.codecType == kVideoCodecH264)
|
||||
p.h264_profile = video_codec.H264().profile;
|
||||
return {CodecTypeToPayloadString(video_codec.codecType), PayloadUnion(p)};
|
||||
}
|
||||
|
||||
bool IsPayloadTypeValid(int8_t payload_type) {
|
||||
assert(payload_type >= 0);
|
||||
|
||||
// Sanity check.
|
||||
switch (payload_type) {
|
||||
// Reserved payload types to avoid RTCP conflicts when marker bit is set.
|
||||
case 64: // 192 Full INTRA-frame request.
|
||||
case 72: // 200 Sender report.
|
||||
case 73: // 201 Receiver report.
|
||||
case 74: // 202 Source description.
|
||||
case 75: // 203 Goodbye.
|
||||
case 76: // 204 Application-defined.
|
||||
case 77: // 205 Transport layer FB message.
|
||||
case 78: // 206 Payload-specific FB message.
|
||||
case 79: // 207 Extended report.
|
||||
RTC_LOG(LS_ERROR) << "Can't register invalid receiver payload type: "
|
||||
<< payload_type;
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RTPPayloadRegistry::RTPPayloadRegistry() = default;
|
||||
|
||||
RTPPayloadRegistry::~RTPPayloadRegistry() = default;
|
||||
|
||||
void RTPPayloadRegistry::SetAudioReceivePayloads(
|
||||
std::map<int, SdpAudioFormat> codecs) {
|
||||
rtc::CritScope cs(&crit_sect_);
|
||||
|
||||
#if RTC_DCHECK_IS_ON
|
||||
RTC_DCHECK(!used_for_video_);
|
||||
used_for_audio_ = true;
|
||||
#endif
|
||||
|
||||
payload_type_map_.clear();
|
||||
for (const auto& kv : codecs) {
|
||||
const int& rtp_payload_type = kv.first;
|
||||
const SdpAudioFormat& audio_format = kv.second;
|
||||
RTC_DCHECK(IsPayloadTypeValid(rtp_payload_type));
|
||||
payload_type_map_.emplace(rtp_payload_type,
|
||||
CreatePayloadType(audio_format));
|
||||
}
|
||||
}
|
||||
|
||||
int32_t RTPPayloadRegistry::RegisterReceivePayload(
|
||||
int payload_type,
|
||||
const SdpAudioFormat& audio_format,
|
||||
bool* created_new_payload) {
|
||||
rtc::CritScope cs(&crit_sect_);
|
||||
|
||||
#if RTC_DCHECK_IS_ON
|
||||
RTC_DCHECK(!used_for_video_);
|
||||
used_for_audio_ = true;
|
||||
#endif
|
||||
|
||||
*created_new_payload = false;
|
||||
if (!IsPayloadTypeValid(payload_type))
|
||||
return -1;
|
||||
|
||||
const auto it = payload_type_map_.find(payload_type);
|
||||
if (it != payload_type_map_.end()) {
|
||||
// We already use this payload type. Check if it's the same as we already
|
||||
// have. If same, ignore sending an error.
|
||||
if (PayloadIsCompatible(it->second, audio_format)) {
|
||||
it->second.typeSpecific.audio_payload().rate = 0;
|
||||
return 0;
|
||||
}
|
||||
RTC_LOG(LS_ERROR) << "Payload type already registered: " << payload_type;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Audio codecs must be unique.
|
||||
DeregisterAudioCodecOrRedTypeRegardlessOfPayloadType(audio_format);
|
||||
|
||||
const auto insert_status =
|
||||
payload_type_map_.emplace(payload_type, CreatePayloadType(audio_format));
|
||||
RTC_DCHECK(insert_status.second); // Insertion succeeded.
|
||||
*created_new_payload = true;
|
||||
|
||||
// Successful set of payload type.
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t RTPPayloadRegistry::RegisterReceivePayload(
|
||||
const VideoCodec& video_codec) {
|
||||
rtc::CritScope cs(&crit_sect_);
|
||||
|
||||
#if RTC_DCHECK_IS_ON
|
||||
RTC_DCHECK(!used_for_audio_);
|
||||
used_for_video_ = true;
|
||||
#endif
|
||||
|
||||
if (!IsPayloadTypeValid(video_codec.plType))
|
||||
return -1;
|
||||
|
||||
auto it = payload_type_map_.find(video_codec.plType);
|
||||
if (it != payload_type_map_.end()) {
|
||||
// We already use this payload type. Check if it's the same as we already
|
||||
// have. If same, ignore sending an error.
|
||||
if (PayloadIsCompatible(it->second, video_codec))
|
||||
return 0;
|
||||
RTC_LOG(LS_ERROR) << "Payload type already registered: "
|
||||
<< static_cast<int>(video_codec.plType);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto insert_status = payload_type_map_.emplace(
|
||||
video_codec.plType, CreatePayloadType(video_codec));
|
||||
RTC_DCHECK(insert_status.second); // Insertion succeeded.
|
||||
|
||||
// Successful set of payload type.
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t RTPPayloadRegistry::DeRegisterReceivePayload(
|
||||
const int8_t payload_type) {
|
||||
rtc::CritScope cs(&crit_sect_);
|
||||
payload_type_map_.erase(payload_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// There can't be several codecs with the same rate, frequency and channels
|
||||
// for audio codecs, but there can for video.
|
||||
// Always called from within a critical section.
|
||||
void RTPPayloadRegistry::DeregisterAudioCodecOrRedTypeRegardlessOfPayloadType(
|
||||
const SdpAudioFormat& audio_format) {
|
||||
for (auto iterator = payload_type_map_.begin();
|
||||
iterator != payload_type_map_.end(); ++iterator) {
|
||||
if (PayloadIsCompatible(iterator->second, audio_format)) {
|
||||
// Remove old setting.
|
||||
payload_type_map_.erase(iterator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int RTPPayloadRegistry::GetPayloadTypeFrequency(uint8_t payload_type) const {
|
||||
const auto payload = PayloadTypeToPayload(payload_type);
|
||||
if (!payload) {
|
||||
return -1;
|
||||
}
|
||||
rtc::CritScope cs(&crit_sect_);
|
||||
return payload->typeSpecific.is_audio()
|
||||
? payload->typeSpecific.audio_payload().format.clockrate_hz
|
||||
: kVideoPayloadTypeFrequency;
|
||||
}
|
||||
|
||||
absl::optional<RtpUtility::Payload> RTPPayloadRegistry::PayloadTypeToPayload(
|
||||
uint8_t payload_type) const {
|
||||
rtc::CritScope cs(&crit_sect_);
|
||||
const auto it = payload_type_map_.find(payload_type);
|
||||
return it == payload_type_map_.end()
|
||||
? absl::nullopt
|
||||
: absl::optional<RtpUtility::Payload>(it->second);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -1,186 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 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 <memory>
|
||||
|
||||
#include "common_types.h" // NOLINT(build/include)
|
||||
#include "modules/rtp_rtcp/include/rtp_header_parser.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_payload_registry.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_utility.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::Return;
|
||||
using ::testing::StrEq;
|
||||
using ::testing::_;
|
||||
|
||||
TEST(RtpPayloadRegistryTest,
|
||||
RegistersAndRemembersVideoPayloadsUntilDeregistered) {
|
||||
RTPPayloadRegistry rtp_payload_registry;
|
||||
const uint8_t payload_type = 97;
|
||||
VideoCodec video_codec;
|
||||
video_codec.codecType = kVideoCodecVP8;
|
||||
video_codec.plType = payload_type;
|
||||
|
||||
EXPECT_EQ(0, rtp_payload_registry.RegisterReceivePayload(video_codec));
|
||||
|
||||
const auto retrieved_payload =
|
||||
rtp_payload_registry.PayloadTypeToPayload(payload_type);
|
||||
EXPECT_TRUE(retrieved_payload);
|
||||
|
||||
// We should get back the corresponding payload that we registered.
|
||||
EXPECT_STREQ("VP8", retrieved_payload->name);
|
||||
EXPECT_TRUE(retrieved_payload->typeSpecific.is_video());
|
||||
EXPECT_EQ(kVideoCodecVP8,
|
||||
retrieved_payload->typeSpecific.video_payload().videoCodecType);
|
||||
|
||||
// Now forget about it and verify it's gone.
|
||||
EXPECT_EQ(0, rtp_payload_registry.DeRegisterReceivePayload(payload_type));
|
||||
EXPECT_FALSE(rtp_payload_registry.PayloadTypeToPayload(payload_type));
|
||||
}
|
||||
|
||||
TEST(RtpPayloadRegistryTest,
|
||||
RegistersAndRemembersAudioPayloadsUntilDeregistered) {
|
||||
RTPPayloadRegistry rtp_payload_registry;
|
||||
constexpr int payload_type = 97;
|
||||
const SdpAudioFormat audio_format("name", 44000, 1);
|
||||
bool new_payload_created = false;
|
||||
EXPECT_EQ(0, rtp_payload_registry.RegisterReceivePayload(
|
||||
payload_type, audio_format, &new_payload_created));
|
||||
|
||||
EXPECT_TRUE(new_payload_created) << "A new payload WAS created.";
|
||||
|
||||
const auto retrieved_payload =
|
||||
rtp_payload_registry.PayloadTypeToPayload(payload_type);
|
||||
EXPECT_TRUE(retrieved_payload);
|
||||
|
||||
// We should get back the corresponding payload that we registered.
|
||||
EXPECT_STREQ("name", retrieved_payload->name);
|
||||
EXPECT_TRUE(retrieved_payload->typeSpecific.is_audio());
|
||||
EXPECT_EQ(audio_format,
|
||||
retrieved_payload->typeSpecific.audio_payload().format);
|
||||
|
||||
// Now forget about it and verify it's gone.
|
||||
EXPECT_EQ(0, rtp_payload_registry.DeRegisterReceivePayload(payload_type));
|
||||
EXPECT_FALSE(rtp_payload_registry.PayloadTypeToPayload(payload_type));
|
||||
}
|
||||
|
||||
TEST(RtpPayloadRegistryTest,
|
||||
DoesNotAcceptSamePayloadTypeTwiceExceptIfPayloadIsCompatible) {
|
||||
constexpr int payload_type = 97;
|
||||
RTPPayloadRegistry rtp_payload_registry;
|
||||
|
||||
bool ignored = false;
|
||||
const SdpAudioFormat audio_format("name", 44000, 1);
|
||||
EXPECT_EQ(0, rtp_payload_registry.RegisterReceivePayload(
|
||||
payload_type, audio_format, &ignored));
|
||||
|
||||
const SdpAudioFormat audio_format_2("name", 44001, 1); // Not compatible.
|
||||
EXPECT_EQ(-1, rtp_payload_registry.RegisterReceivePayload(
|
||||
payload_type, audio_format_2, &ignored))
|
||||
<< "Adding incompatible codec with same payload type = bad.";
|
||||
|
||||
// Change payload type.
|
||||
EXPECT_EQ(0, rtp_payload_registry.RegisterReceivePayload(
|
||||
payload_type - 1, audio_format_2, &ignored))
|
||||
<< "With a different payload type is fine though.";
|
||||
|
||||
// Ensure both payloads are preserved.
|
||||
const auto retrieved_payload1 =
|
||||
rtp_payload_registry.PayloadTypeToPayload(payload_type);
|
||||
EXPECT_TRUE(retrieved_payload1);
|
||||
EXPECT_STREQ("name", retrieved_payload1->name);
|
||||
EXPECT_TRUE(retrieved_payload1->typeSpecific.is_audio());
|
||||
EXPECT_EQ(audio_format,
|
||||
retrieved_payload1->typeSpecific.audio_payload().format);
|
||||
|
||||
const auto retrieved_payload2 =
|
||||
rtp_payload_registry.PayloadTypeToPayload(payload_type - 1);
|
||||
EXPECT_TRUE(retrieved_payload2);
|
||||
EXPECT_STREQ("name", retrieved_payload2->name);
|
||||
EXPECT_TRUE(retrieved_payload2->typeSpecific.is_audio());
|
||||
EXPECT_EQ(audio_format_2,
|
||||
retrieved_payload2->typeSpecific.audio_payload().format);
|
||||
|
||||
// Ok, update the rate for one of the codecs. If either the incoming rate or
|
||||
// the stored rate is zero it's not really an error to register the same
|
||||
// codec twice, and in that case roughly the following happens.
|
||||
EXPECT_EQ(0, rtp_payload_registry.RegisterReceivePayload(
|
||||
payload_type, audio_format, &ignored));
|
||||
}
|
||||
|
||||
TEST(RtpPayloadRegistryTest,
|
||||
RemovesCompatibleCodecsOnRegistryIfCodecsMustBeUnique) {
|
||||
constexpr int payload_type = 97;
|
||||
RTPPayloadRegistry rtp_payload_registry;
|
||||
|
||||
bool ignored = false;
|
||||
const SdpAudioFormat audio_format("name", 44000, 1);
|
||||
EXPECT_EQ(0, rtp_payload_registry.RegisterReceivePayload(
|
||||
payload_type, audio_format, &ignored));
|
||||
EXPECT_EQ(0, rtp_payload_registry.RegisterReceivePayload(
|
||||
payload_type - 1, audio_format, &ignored));
|
||||
|
||||
EXPECT_FALSE(rtp_payload_registry.PayloadTypeToPayload(payload_type))
|
||||
<< "The first payload should be "
|
||||
"deregistered because the only thing that differs is payload type.";
|
||||
EXPECT_TRUE(rtp_payload_registry.PayloadTypeToPayload(payload_type - 1))
|
||||
<< "The second payload should still be registered though.";
|
||||
|
||||
// Now ensure non-compatible codecs aren't removed. Make |audio_format_2|
|
||||
// incompatible by changing the frequency.
|
||||
const SdpAudioFormat audio_format_2("name", 44001, 1);
|
||||
EXPECT_EQ(0, rtp_payload_registry.RegisterReceivePayload(
|
||||
payload_type + 1, audio_format_2, &ignored));
|
||||
|
||||
EXPECT_TRUE(rtp_payload_registry.PayloadTypeToPayload(payload_type - 1))
|
||||
<< "Not compatible; both payloads should be kept.";
|
||||
EXPECT_TRUE(rtp_payload_registry.PayloadTypeToPayload(payload_type + 1))
|
||||
<< "Not compatible; both payloads should be kept.";
|
||||
}
|
||||
|
||||
class ParameterizedRtpPayloadRegistryTest
|
||||
: public ::testing::TestWithParam<int> {};
|
||||
|
||||
TEST_P(ParameterizedRtpPayloadRegistryTest,
|
||||
FailsToRegisterKnownPayloadsWeAreNotInterestedIn) {
|
||||
RTPPayloadRegistry rtp_payload_registry;
|
||||
|
||||
bool ignored;
|
||||
const int payload_type = GetParam();
|
||||
const SdpAudioFormat audio_format("whatever", 1900, 1);
|
||||
EXPECT_EQ(-1, rtp_payload_registry.RegisterReceivePayload(
|
||||
payload_type, audio_format, &ignored));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(TestKnownBadPayloadTypes,
|
||||
ParameterizedRtpPayloadRegistryTest,
|
||||
testing::Values(64, 72, 73, 74, 75, 76, 77, 78, 79));
|
||||
|
||||
class RtpPayloadRegistryGenericTest : public ::testing::TestWithParam<int> {};
|
||||
|
||||
TEST_P(RtpPayloadRegistryGenericTest, RegisterGenericReceivePayloadType) {
|
||||
RTPPayloadRegistry rtp_payload_registry;
|
||||
|
||||
bool ignored;
|
||||
const int payload_type = GetParam();
|
||||
const SdpAudioFormat audio_format("generic-codec", 1900, 1); // Dummy values.
|
||||
EXPECT_EQ(0, rtp_payload_registry.RegisterReceivePayload(
|
||||
payload_type, audio_format, &ignored));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(TestDynamicRange,
|
||||
RtpPayloadRegistryGenericTest,
|
||||
testing::Range(96, 127 + 1));
|
||||
|
||||
} // namespace webrtc
|
||||
@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 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_receiver_audio.h"
|
||||
|
||||
#include <assert.h> // assert
|
||||
#include <math.h> // pow()
|
||||
#include <string.h> // memcpy()
|
||||
|
||||
#include "common_types.h" // NOLINT(build/include)
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/trace_event.h"
|
||||
|
||||
namespace webrtc {
|
||||
RTPReceiverStrategy* RTPReceiverStrategy::CreateAudioStrategy(
|
||||
RtpData* data_callback) {
|
||||
return new RTPReceiverAudio(data_callback);
|
||||
}
|
||||
|
||||
RTPReceiverAudio::RTPReceiverAudio(RtpData* data_callback)
|
||||
: RTPReceiverStrategy(data_callback) {}
|
||||
|
||||
RTPReceiverAudio::~RTPReceiverAudio() = default;
|
||||
|
||||
// - Sample based or frame based codecs based on RFC 3551
|
||||
// -
|
||||
// - NOTE! There is one error in the RFC, stating G.722 uses 8 bits/samples.
|
||||
// - The correct rate is 4 bits/sample.
|
||||
// -
|
||||
// - name of sampling default
|
||||
// - encoding sample/frame bits/sample rate ms/frame ms/packet
|
||||
// -
|
||||
// - Sample based audio codecs
|
||||
// - DVI4 sample 4 var. 20
|
||||
// - G722 sample 4 16,000 20
|
||||
// - G726-40 sample 5 8,000 20
|
||||
// - G726-32 sample 4 8,000 20
|
||||
// - G726-24 sample 3 8,000 20
|
||||
// - G726-16 sample 2 8,000 20
|
||||
// - L8 sample 8 var. 20
|
||||
// - L16 sample 16 var. 20
|
||||
// - PCMA sample 8 var. 20
|
||||
// - PCMU sample 8 var. 20
|
||||
// -
|
||||
// - Frame based audio codecs
|
||||
// - G723 frame N/A 8,000 30 30
|
||||
// - G728 frame N/A 8,000 2.5 20
|
||||
// - G729 frame N/A 8,000 10 20
|
||||
// - G729D frame N/A 8,000 10 20
|
||||
// - G729E frame N/A 8,000 10 20
|
||||
// - GSM frame N/A 8,000 20 20
|
||||
// - GSM-EFR frame N/A 8,000 20 20
|
||||
// - LPC frame N/A 8,000 20 20
|
||||
// - MPA frame N/A var. var.
|
||||
// -
|
||||
// - G7221 frame N/A
|
||||
|
||||
int32_t RTPReceiverAudio::ParseRtpPacket(WebRtcRTPHeader* rtp_header,
|
||||
const PayloadUnion& specific_payload,
|
||||
const uint8_t* payload,
|
||||
size_t payload_length,
|
||||
int64_t timestamp_ms) {
|
||||
if (first_packet_received_()) {
|
||||
RTC_LOG(LS_INFO) << "Received first audio RTP packet";
|
||||
}
|
||||
|
||||
return ParseAudioCodecSpecific(rtp_header, payload, payload_length,
|
||||
specific_payload.audio_payload());
|
||||
}
|
||||
|
||||
// We are not allowed to have any critsects when calling data_callback.
|
||||
int32_t RTPReceiverAudio::ParseAudioCodecSpecific(
|
||||
WebRtcRTPHeader* rtp_header,
|
||||
const uint8_t* payload_data,
|
||||
size_t payload_length,
|
||||
const AudioPayload& audio_specific) {
|
||||
RTC_DCHECK_GE(payload_length, rtp_header->header.paddingLength);
|
||||
const size_t payload_data_length =
|
||||
payload_length - rtp_header->header.paddingLength;
|
||||
if (payload_data_length == 0) {
|
||||
rtp_header->frameType = kEmptyFrame;
|
||||
return data_callback_->OnReceivedPayloadData(nullptr, 0, rtp_header);
|
||||
}
|
||||
|
||||
return data_callback_->OnReceivedPayloadData(payload_data,
|
||||
payload_data_length, rtp_header);
|
||||
}
|
||||
} // namespace webrtc
|
||||
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 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 MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_AUDIO_H_
|
||||
#define MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_AUDIO_H_
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "modules/rtp_rtcp/include/rtp_receiver.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_receiver_strategy.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_utility.h"
|
||||
#include "rtc_base/onetimeevent.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Handles audio RTP packets. This class is thread-safe.
|
||||
class RTPReceiverAudio : public RTPReceiverStrategy {
|
||||
public:
|
||||
explicit RTPReceiverAudio(RtpData* data_callback);
|
||||
~RTPReceiverAudio() override;
|
||||
|
||||
int32_t ParseRtpPacket(WebRtcRTPHeader* rtp_header,
|
||||
const PayloadUnion& specific_payload,
|
||||
const uint8_t* packet,
|
||||
size_t payload_length,
|
||||
int64_t timestamp_ms) override;
|
||||
|
||||
private:
|
||||
int32_t ParseAudioCodecSpecific(WebRtcRTPHeader* rtp_header,
|
||||
const uint8_t* payload_data,
|
||||
size_t payload_length,
|
||||
const AudioPayload& audio_specific);
|
||||
|
||||
ThreadUnsafeOneTimeEvent first_packet_received_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_AUDIO_H_
|
||||
@ -1,246 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 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_receiver_impl.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "common_types.h" // NOLINT(build/include)
|
||||
#include "modules/audio_coding/codecs/audio_format_conversion.h"
|
||||
#include "modules/include/module_common_types.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_payload_registry.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_receiver_strategy.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
bool InOrderPacket(absl::optional<uint16_t> latest_sequence_number,
|
||||
uint16_t current_sequence_number) {
|
||||
if (!latest_sequence_number)
|
||||
return true;
|
||||
|
||||
// We need to distinguish between a late or retransmitted packet,
|
||||
// and a sequence number discontinuity.
|
||||
if (IsNewerSequenceNumber(current_sequence_number, *latest_sequence_number)) {
|
||||
return true;
|
||||
} else {
|
||||
// If we have a restart of the remote side this packet is still in order.
|
||||
return !IsNewerSequenceNumber(
|
||||
current_sequence_number,
|
||||
*latest_sequence_number - kDefaultMaxReorderingThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
using RtpUtility::Payload;
|
||||
|
||||
// Only return the sources in the last 10 seconds.
|
||||
const int64_t kGetSourcesTimeoutMs = 10000;
|
||||
|
||||
RtpReceiver* RtpReceiver::CreateVideoReceiver(
|
||||
Clock* clock,
|
||||
RtpData* incoming_payload_callback,
|
||||
RTPPayloadRegistry* rtp_payload_registry) {
|
||||
RTC_DCHECK(incoming_payload_callback != nullptr);
|
||||
return new RtpReceiverImpl(
|
||||
clock, rtp_payload_registry,
|
||||
RTPReceiverStrategy::CreateVideoStrategy(incoming_payload_callback));
|
||||
}
|
||||
|
||||
RtpReceiver* RtpReceiver::CreateAudioReceiver(
|
||||
Clock* clock,
|
||||
RtpData* incoming_payload_callback,
|
||||
RTPPayloadRegistry* rtp_payload_registry) {
|
||||
RTC_DCHECK(incoming_payload_callback != nullptr);
|
||||
return new RtpReceiverImpl(
|
||||
clock, rtp_payload_registry,
|
||||
RTPReceiverStrategy::CreateAudioStrategy(incoming_payload_callback));
|
||||
}
|
||||
|
||||
int32_t RtpReceiver::RegisterReceivePayload(const CodecInst& audio_codec) {
|
||||
return RegisterReceivePayload(audio_codec.pltype,
|
||||
CodecInstToSdp(audio_codec));
|
||||
}
|
||||
|
||||
RtpReceiverImpl::RtpReceiverImpl(Clock* clock,
|
||||
RTPPayloadRegistry* rtp_payload_registry,
|
||||
RTPReceiverStrategy* rtp_media_receiver)
|
||||
: clock_(clock),
|
||||
rtp_payload_registry_(rtp_payload_registry),
|
||||
rtp_media_receiver_(rtp_media_receiver),
|
||||
ssrc_(0),
|
||||
last_received_timestamp_(0),
|
||||
last_received_frame_time_ms_(-1) {}
|
||||
|
||||
RtpReceiverImpl::~RtpReceiverImpl() {}
|
||||
|
||||
int32_t RtpReceiverImpl::RegisterReceivePayload(
|
||||
int payload_type,
|
||||
const SdpAudioFormat& audio_format) {
|
||||
rtc::CritScope lock(&critical_section_rtp_receiver_);
|
||||
|
||||
// TODO(phoglund): Try to streamline handling of the RED codec and some other
|
||||
// cases which makes it necessary to keep track of whether we created a
|
||||
// payload or not.
|
||||
bool created_new_payload = false;
|
||||
int32_t result = rtp_payload_registry_->RegisterReceivePayload(
|
||||
payload_type, audio_format, &created_new_payload);
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t RtpReceiverImpl::RegisterReceivePayload(const VideoCodec& video_codec) {
|
||||
rtc::CritScope lock(&critical_section_rtp_receiver_);
|
||||
return rtp_payload_registry_->RegisterReceivePayload(video_codec);
|
||||
}
|
||||
|
||||
int32_t RtpReceiverImpl::DeRegisterReceivePayload(const int8_t payload_type) {
|
||||
rtc::CritScope lock(&critical_section_rtp_receiver_);
|
||||
return rtp_payload_registry_->DeRegisterReceivePayload(payload_type);
|
||||
}
|
||||
|
||||
uint32_t RtpReceiverImpl::SSRC() const {
|
||||
rtc::CritScope lock(&critical_section_rtp_receiver_);
|
||||
return ssrc_;
|
||||
}
|
||||
|
||||
bool RtpReceiverImpl::IncomingRtpPacket(const RTPHeader& rtp_header,
|
||||
const uint8_t* payload,
|
||||
size_t payload_length,
|
||||
PayloadUnion payload_specific) {
|
||||
// Trigger our callbacks.
|
||||
CheckSSRCChanged(rtp_header);
|
||||
|
||||
if (payload_length == 0) {
|
||||
// OK, keep-alive packet.
|
||||
return true;
|
||||
}
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
|
||||
{
|
||||
rtc::CritScope lock(&critical_section_rtp_receiver_);
|
||||
|
||||
csrcs_.Update(
|
||||
now_ms, rtc::MakeArrayView(rtp_header.arrOfCSRCs, rtp_header.numCSRCs));
|
||||
}
|
||||
|
||||
WebRtcRTPHeader webrtc_rtp_header{};
|
||||
webrtc_rtp_header.header = rtp_header;
|
||||
|
||||
auto audio_level =
|
||||
rtp_header.extension.hasAudioLevel
|
||||
? absl::optional<uint8_t>(rtp_header.extension.audioLevel)
|
||||
: absl::nullopt;
|
||||
UpdateSources(audio_level);
|
||||
|
||||
int32_t ret_val = rtp_media_receiver_->ParseRtpPacket(
|
||||
&webrtc_rtp_header, payload_specific, payload, payload_length, now_ms);
|
||||
|
||||
if (ret_val < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
rtc::CritScope lock(&critical_section_rtp_receiver_);
|
||||
|
||||
// TODO(nisse): Do not rely on InOrderPacket for recovered packets, when
|
||||
// packet is passed as RtpPacketReceived and that information is available.
|
||||
// We should ideally never record timestamps for retransmitted or recovered
|
||||
// packets.
|
||||
if (InOrderPacket(last_received_sequence_number_,
|
||||
rtp_header.sequenceNumber)) {
|
||||
last_received_sequence_number_.emplace(rtp_header.sequenceNumber);
|
||||
last_received_timestamp_ = rtp_header.timestamp;
|
||||
last_received_frame_time_ms_ = clock_->TimeInMilliseconds();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<RtpSource> RtpReceiverImpl::GetSources() const {
|
||||
rtc::CritScope lock(&critical_section_rtp_receiver_);
|
||||
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
RTC_DCHECK(std::is_sorted(ssrc_sources_.begin(), ssrc_sources_.end(),
|
||||
[](const RtpSource& lhs, const RtpSource& rhs) {
|
||||
return lhs.timestamp_ms() < rhs.timestamp_ms();
|
||||
}));
|
||||
std::vector<RtpSource> sources = csrcs_.GetSources(now_ms);
|
||||
|
||||
std::set<uint32_t> selected_ssrcs;
|
||||
for (auto rit = ssrc_sources_.rbegin(); rit != ssrc_sources_.rend(); ++rit) {
|
||||
if ((now_ms - rit->timestamp_ms()) > kGetSourcesTimeoutMs) {
|
||||
break;
|
||||
}
|
||||
if (selected_ssrcs.insert(rit->source_id()).second) {
|
||||
sources.push_back(*rit);
|
||||
}
|
||||
}
|
||||
return sources;
|
||||
}
|
||||
|
||||
bool RtpReceiverImpl::GetLatestTimestamps(uint32_t* timestamp,
|
||||
int64_t* receive_time_ms) const {
|
||||
rtc::CritScope lock(&critical_section_rtp_receiver_);
|
||||
if (!last_received_sequence_number_)
|
||||
return false;
|
||||
|
||||
*timestamp = last_received_timestamp_;
|
||||
*receive_time_ms = last_received_frame_time_ms_;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(nisse): Delete.
|
||||
// Implementation note: must not hold critsect when called.
|
||||
void RtpReceiverImpl::CheckSSRCChanged(const RTPHeader& rtp_header) {
|
||||
rtc::CritScope lock(&critical_section_rtp_receiver_);
|
||||
ssrc_ = rtp_header.ssrc;
|
||||
}
|
||||
|
||||
void RtpReceiverImpl::UpdateSources(
|
||||
const absl::optional<uint8_t>& ssrc_audio_level) {
|
||||
rtc::CritScope lock(&critical_section_rtp_receiver_);
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
|
||||
// If this is the first packet or the SSRC is changed, insert a new
|
||||
// contributing source that uses the SSRC.
|
||||
if (ssrc_sources_.empty() || ssrc_sources_.rbegin()->source_id() != ssrc_) {
|
||||
ssrc_sources_.emplace_back(now_ms, ssrc_, RtpSourceType::SSRC);
|
||||
} else {
|
||||
ssrc_sources_.rbegin()->update_timestamp_ms(now_ms);
|
||||
}
|
||||
|
||||
ssrc_sources_.back().set_audio_level(ssrc_audio_level);
|
||||
|
||||
RemoveOutdatedSources(now_ms);
|
||||
}
|
||||
|
||||
void RtpReceiverImpl::RemoveOutdatedSources(int64_t now_ms) {
|
||||
std::vector<RtpSource>::iterator vec_it;
|
||||
for (vec_it = ssrc_sources_.begin(); vec_it != ssrc_sources_.end();
|
||||
++vec_it) {
|
||||
if ((now_ms - vec_it->timestamp_ms()) <= kGetSourcesTimeoutMs) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ssrc_sources_.erase(ssrc_sources_.begin(), vec_it);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 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 MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_IMPL_H_
|
||||
#define MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_IMPL_H_
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_receiver.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "modules/rtp_rtcp/source/contributing_sources.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_receiver_strategy.h"
|
||||
#include "rtc_base/criticalsection.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RtpReceiverImpl : public RtpReceiver {
|
||||
public:
|
||||
// Callbacks passed in here may not be NULL (use Null Object callbacks if you
|
||||
// want callbacks to do nothing). This class takes ownership of the media
|
||||
// receiver but nothing else.
|
||||
RtpReceiverImpl(Clock* clock,
|
||||
RTPPayloadRegistry* rtp_payload_registry,
|
||||
RTPReceiverStrategy* rtp_media_receiver);
|
||||
|
||||
~RtpReceiverImpl() override;
|
||||
|
||||
int32_t RegisterReceivePayload(int payload_type,
|
||||
const SdpAudioFormat& audio_format) override;
|
||||
int32_t RegisterReceivePayload(const VideoCodec& video_codec) override;
|
||||
|
||||
int32_t DeRegisterReceivePayload(const int8_t payload_type) override;
|
||||
|
||||
bool IncomingRtpPacket(const RTPHeader& rtp_header,
|
||||
const uint8_t* payload,
|
||||
size_t payload_length,
|
||||
PayloadUnion payload_specific) override;
|
||||
|
||||
bool GetLatestTimestamps(uint32_t* timestamp,
|
||||
int64_t* receive_time_ms) const override;
|
||||
|
||||
uint32_t SSRC() const override;
|
||||
|
||||
std::vector<RtpSource> GetSources() const override;
|
||||
|
||||
private:
|
||||
void CheckSSRCChanged(const RTPHeader& rtp_header);
|
||||
|
||||
void UpdateSources(const absl::optional<uint8_t>& ssrc_audio_level);
|
||||
void RemoveOutdatedSources(int64_t now_ms);
|
||||
|
||||
Clock* clock_;
|
||||
rtc::CriticalSection critical_section_rtp_receiver_;
|
||||
|
||||
RTPPayloadRegistry* const rtp_payload_registry_
|
||||
RTC_PT_GUARDED_BY(critical_section_rtp_receiver_);
|
||||
const std::unique_ptr<RTPReceiverStrategy> rtp_media_receiver_;
|
||||
|
||||
// SSRCs.
|
||||
uint32_t ssrc_ RTC_GUARDED_BY(critical_section_rtp_receiver_);
|
||||
|
||||
ContributingSources csrcs_ RTC_GUARDED_BY(critical_section_rtp_receiver_);
|
||||
|
||||
// Sequence number and timestamps for the latest in-order packet.
|
||||
absl::optional<uint16_t> last_received_sequence_number_
|
||||
RTC_GUARDED_BY(critical_section_rtp_receiver_);
|
||||
uint32_t last_received_timestamp_
|
||||
RTC_GUARDED_BY(critical_section_rtp_receiver_);
|
||||
int64_t last_received_frame_time_ms_
|
||||
RTC_GUARDED_BY(critical_section_rtp_receiver_);
|
||||
|
||||
// The RtpSource objects are sorted chronologically.
|
||||
std::vector<RtpSource> ssrc_sources_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_IMPL_H_
|
||||
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 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_receiver_strategy.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
RTPReceiverStrategy::RTPReceiverStrategy(RtpData* data_callback)
|
||||
: data_callback_(data_callback) {}
|
||||
|
||||
RTPReceiverStrategy::~RTPReceiverStrategy() = default;
|
||||
|
||||
} // namespace webrtc
|
||||
@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 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 MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_STRATEGY_H_
|
||||
#define MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_STRATEGY_H_
|
||||
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_utility.h"
|
||||
#include "rtc_base/criticalsection.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
struct CodecInst;
|
||||
|
||||
// This strategy deals with media-specific RTP packet processing.
|
||||
// This class is not thread-safe and must be protected by its caller.
|
||||
class RTPReceiverStrategy {
|
||||
public:
|
||||
static RTPReceiverStrategy* CreateVideoStrategy(RtpData* data_callback);
|
||||
static RTPReceiverStrategy* CreateAudioStrategy(RtpData* data_callback);
|
||||
|
||||
virtual ~RTPReceiverStrategy();
|
||||
|
||||
// Parses the RTP packet and calls the data callback with the payload data.
|
||||
// Implementations are encouraged to use the provided packet buffer and RTP
|
||||
// header as arguments to the callback; implementations are also allowed to
|
||||
// make changes in the data as necessary. The specific_payload argument
|
||||
// provides audio or video-specific data.
|
||||
virtual int32_t ParseRtpPacket(WebRtcRTPHeader* rtp_header,
|
||||
const PayloadUnion& specific_payload,
|
||||
const uint8_t* payload,
|
||||
size_t payload_length,
|
||||
int64_t timestamp_ms) = 0;
|
||||
|
||||
protected:
|
||||
// The data callback is where we should send received payload data.
|
||||
// See ParseRtpPacket. This class does not claim ownership of the callback.
|
||||
// Implementations must NOT hold any critical sections while calling the
|
||||
// callback.
|
||||
//
|
||||
// Note: Implementations may call the callback for other reasons than calls
|
||||
// to ParseRtpPacket, for instance if the implementation somehow recovers a
|
||||
// packet.
|
||||
explicit RTPReceiverStrategy(RtpData* data_callback);
|
||||
|
||||
rtc::CriticalSection crit_sect_;
|
||||
RtpData* data_callback_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_STRATEGY_H_
|
||||
@ -1,461 +0,0 @@
|
||||
/*
|
||||
* 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 <memory>
|
||||
|
||||
#include "common_types.h" // NOLINT(build/include)
|
||||
#include "modules/rtp_rtcp/include/rtp_header_parser.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_payload_registry.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_receiver.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "modules/rtp_rtcp/mocks/mock_rtp_rtcp.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::UnorderedElementsAre;
|
||||
|
||||
const uint32_t kTestRate = 64000u;
|
||||
const uint8_t kTestPayload[] = {'t', 'e', 's', 't'};
|
||||
const uint8_t kPcmuPayloadType = 96;
|
||||
const int64_t kGetSourcesTimeoutMs = 10000;
|
||||
const uint32_t kSsrc1 = 123;
|
||||
const uint32_t kSsrc2 = 124;
|
||||
const uint32_t kCsrc1 = 111;
|
||||
const uint32_t kCsrc2 = 222;
|
||||
|
||||
static uint32_t rtp_timestamp(int64_t time_ms) {
|
||||
return static_cast<uint32_t>(time_ms * kTestRate / 1000);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class RtpReceiverTest : public ::testing::Test {
|
||||
protected:
|
||||
RtpReceiverTest()
|
||||
: fake_clock_(123456),
|
||||
rtp_receiver_(
|
||||
RtpReceiver::CreateAudioReceiver(&fake_clock_,
|
||||
&mock_rtp_data_,
|
||||
&rtp_payload_registry_)) {
|
||||
rtp_receiver_->RegisterReceivePayload(kPcmuPayloadType,
|
||||
SdpAudioFormat("PCMU", 8000, 1));
|
||||
}
|
||||
~RtpReceiverTest() {}
|
||||
|
||||
bool FindSourceByIdAndType(const std::vector<RtpSource>& sources,
|
||||
uint32_t source_id,
|
||||
RtpSourceType type,
|
||||
RtpSource* source) {
|
||||
for (size_t i = 0; i < sources.size(); ++i) {
|
||||
if (sources[i].source_id() == source_id &&
|
||||
sources[i].source_type() == type) {
|
||||
(*source) = sources[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SimulatedClock fake_clock_;
|
||||
NiceMock<MockRtpData> mock_rtp_data_;
|
||||
RTPPayloadRegistry rtp_payload_registry_;
|
||||
std::unique_ptr<RtpReceiver> rtp_receiver_;
|
||||
};
|
||||
|
||||
TEST_F(RtpReceiverTest, GetSources) {
|
||||
int64_t now_ms = fake_clock_.TimeInMilliseconds();
|
||||
|
||||
RTPHeader header;
|
||||
header.payloadType = kPcmuPayloadType;
|
||||
header.ssrc = kSsrc1;
|
||||
header.timestamp = rtp_timestamp(now_ms);
|
||||
header.numCSRCs = 2;
|
||||
header.arrOfCSRCs[0] = kCsrc1;
|
||||
header.arrOfCSRCs[1] = kCsrc2;
|
||||
const PayloadUnion payload_specific{
|
||||
AudioPayload{SdpAudioFormat("foo", 8000, 1), 0}};
|
||||
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
auto sources = rtp_receiver_->GetSources();
|
||||
// One SSRC source and two CSRC sources.
|
||||
EXPECT_THAT(sources, UnorderedElementsAre(
|
||||
RtpSource(now_ms, kSsrc1, RtpSourceType::SSRC),
|
||||
RtpSource(now_ms, kCsrc1, RtpSourceType::CSRC),
|
||||
RtpSource(now_ms, kCsrc2, RtpSourceType::CSRC)));
|
||||
|
||||
// Advance the fake clock and the method is expected to return the
|
||||
// contributing source object with same source id and updated timestamp.
|
||||
fake_clock_.AdvanceTimeMilliseconds(1);
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
sources = rtp_receiver_->GetSources();
|
||||
now_ms = fake_clock_.TimeInMilliseconds();
|
||||
EXPECT_THAT(sources, UnorderedElementsAre(
|
||||
RtpSource(now_ms, kSsrc1, RtpSourceType::SSRC),
|
||||
RtpSource(now_ms, kCsrc1, RtpSourceType::CSRC),
|
||||
RtpSource(now_ms, kCsrc2, RtpSourceType::CSRC)));
|
||||
|
||||
// Test the edge case that the sources are still there just before the
|
||||
// timeout.
|
||||
int64_t prev_time_ms = fake_clock_.TimeInMilliseconds();
|
||||
fake_clock_.AdvanceTimeMilliseconds(kGetSourcesTimeoutMs);
|
||||
sources = rtp_receiver_->GetSources();
|
||||
EXPECT_THAT(sources,
|
||||
UnorderedElementsAre(
|
||||
RtpSource(prev_time_ms, kSsrc1, RtpSourceType::SSRC),
|
||||
RtpSource(prev_time_ms, kCsrc1, RtpSourceType::CSRC),
|
||||
RtpSource(prev_time_ms, kCsrc2, RtpSourceType::CSRC)));
|
||||
|
||||
// Time out.
|
||||
fake_clock_.AdvanceTimeMilliseconds(1);
|
||||
sources = rtp_receiver_->GetSources();
|
||||
// All the sources should be out of date.
|
||||
ASSERT_EQ(0u, sources.size());
|
||||
}
|
||||
|
||||
// Test the case that the SSRC is changed.
|
||||
TEST_F(RtpReceiverTest, GetSourcesChangeSSRC) {
|
||||
int64_t prev_time_ms = -1;
|
||||
int64_t now_ms = fake_clock_.TimeInMilliseconds();
|
||||
|
||||
RTPHeader header;
|
||||
header.payloadType = kPcmuPayloadType;
|
||||
header.ssrc = kSsrc1;
|
||||
header.timestamp = rtp_timestamp(now_ms);
|
||||
const PayloadUnion payload_specific{
|
||||
AudioPayload{SdpAudioFormat("foo", 8000, 1), 0}};
|
||||
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
auto sources = rtp_receiver_->GetSources();
|
||||
EXPECT_THAT(sources, UnorderedElementsAre(
|
||||
RtpSource(now_ms, kSsrc1, RtpSourceType::SSRC)));
|
||||
|
||||
// The SSRC is changed and the old SSRC is expected to be returned.
|
||||
fake_clock_.AdvanceTimeMilliseconds(100);
|
||||
prev_time_ms = now_ms;
|
||||
now_ms = fake_clock_.TimeInMilliseconds();
|
||||
header.ssrc = kSsrc2;
|
||||
header.timestamp = rtp_timestamp(now_ms);
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
sources = rtp_receiver_->GetSources();
|
||||
EXPECT_THAT(sources, UnorderedElementsAre(
|
||||
RtpSource(prev_time_ms, kSsrc1, RtpSourceType::SSRC),
|
||||
RtpSource(now_ms, kSsrc2, RtpSourceType::SSRC)));
|
||||
|
||||
// The SSRC is changed again and happen to be changed back to 1. No
|
||||
// duplication is expected.
|
||||
fake_clock_.AdvanceTimeMilliseconds(100);
|
||||
header.ssrc = kSsrc1;
|
||||
header.timestamp = rtp_timestamp(now_ms);
|
||||
prev_time_ms = now_ms;
|
||||
now_ms = fake_clock_.TimeInMilliseconds();
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
sources = rtp_receiver_->GetSources();
|
||||
EXPECT_THAT(sources, UnorderedElementsAre(
|
||||
RtpSource(prev_time_ms, kSsrc2, RtpSourceType::SSRC),
|
||||
RtpSource(now_ms, kSsrc1, RtpSourceType::SSRC)));
|
||||
|
||||
// Old SSRC source timeout.
|
||||
fake_clock_.AdvanceTimeMilliseconds(kGetSourcesTimeoutMs);
|
||||
now_ms = fake_clock_.TimeInMilliseconds();
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
sources = rtp_receiver_->GetSources();
|
||||
EXPECT_THAT(sources, UnorderedElementsAre(
|
||||
RtpSource(now_ms, kSsrc1, RtpSourceType::SSRC)));
|
||||
}
|
||||
|
||||
TEST_F(RtpReceiverTest, GetSourcesRemoveOutdatedSource) {
|
||||
int64_t now_ms = fake_clock_.TimeInMilliseconds();
|
||||
|
||||
RTPHeader header;
|
||||
header.payloadType = kPcmuPayloadType;
|
||||
header.timestamp = rtp_timestamp(now_ms);
|
||||
const PayloadUnion payload_specific{
|
||||
AudioPayload{SdpAudioFormat("foo", 8000, 1), 0}};
|
||||
header.numCSRCs = 1;
|
||||
size_t kSourceListSize = 20;
|
||||
|
||||
for (size_t i = 0; i < kSourceListSize; ++i) {
|
||||
header.ssrc = i;
|
||||
header.arrOfCSRCs[0] = (i + 1);
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
}
|
||||
|
||||
RtpSource source(0, 0, RtpSourceType::SSRC);
|
||||
auto sources = rtp_receiver_->GetSources();
|
||||
// Expect |kSourceListSize| SSRC sources and |kSourceListSize| CSRC sources.
|
||||
ASSERT_EQ(2 * kSourceListSize, sources.size());
|
||||
for (size_t i = 0; i < kSourceListSize; ++i) {
|
||||
// The SSRC source IDs are expected to be 19, 18, 17 ... 0
|
||||
ASSERT_TRUE(
|
||||
FindSourceByIdAndType(sources, i, RtpSourceType::SSRC, &source));
|
||||
EXPECT_EQ(now_ms, source.timestamp_ms());
|
||||
|
||||
// The CSRC source IDs are expected to be 20, 19, 18 ... 1
|
||||
ASSERT_TRUE(
|
||||
FindSourceByIdAndType(sources, (i + 1), RtpSourceType::CSRC, &source));
|
||||
EXPECT_EQ(now_ms, source.timestamp_ms());
|
||||
}
|
||||
|
||||
fake_clock_.AdvanceTimeMilliseconds(kGetSourcesTimeoutMs);
|
||||
for (size_t i = 0; i < kSourceListSize; ++i) {
|
||||
// The SSRC source IDs are expected to be 19, 18, 17 ... 0
|
||||
ASSERT_TRUE(
|
||||
FindSourceByIdAndType(sources, i, RtpSourceType::SSRC, &source));
|
||||
EXPECT_EQ(now_ms, source.timestamp_ms());
|
||||
|
||||
// The CSRC source IDs are expected to be 20, 19, 18 ... 1
|
||||
ASSERT_TRUE(
|
||||
FindSourceByIdAndType(sources, (i + 1), RtpSourceType::CSRC, &source));
|
||||
EXPECT_EQ(now_ms, source.timestamp_ms());
|
||||
}
|
||||
|
||||
// Timeout. All the existing objects are out of date and are expected to be
|
||||
// removed.
|
||||
fake_clock_.AdvanceTimeMilliseconds(1);
|
||||
header.ssrc = kSsrc1;
|
||||
header.arrOfCSRCs[0] = kCsrc1;
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
now_ms = fake_clock_.TimeInMilliseconds();
|
||||
sources = rtp_receiver_->GetSources();
|
||||
EXPECT_THAT(sources, UnorderedElementsAre(
|
||||
RtpSource(now_ms, kSsrc1, RtpSourceType::SSRC),
|
||||
RtpSource(now_ms, kCsrc1, RtpSourceType::CSRC)));
|
||||
}
|
||||
|
||||
// The audio level from the RTPHeader extension should be stored in the
|
||||
// RtpSource with the matching SSRC.
|
||||
TEST_F(RtpReceiverTest, GetSourcesContainsAudioLevelExtension) {
|
||||
RTPHeader header;
|
||||
int64_t time1_ms = fake_clock_.TimeInMilliseconds();
|
||||
header.payloadType = kPcmuPayloadType;
|
||||
header.ssrc = kSsrc1;
|
||||
header.timestamp = rtp_timestamp(time1_ms);
|
||||
header.extension.hasAudioLevel = true;
|
||||
header.extension.audioLevel = 10;
|
||||
const PayloadUnion payload_specific{
|
||||
AudioPayload{SdpAudioFormat("foo", 8000, 1), 0}};
|
||||
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
auto sources = rtp_receiver_->GetSources();
|
||||
EXPECT_THAT(sources, UnorderedElementsAre(RtpSource(
|
||||
time1_ms, kSsrc1, RtpSourceType::SSRC, 10)));
|
||||
|
||||
// Receive a packet from a different SSRC with a different level and check
|
||||
// that they are both remembered.
|
||||
fake_clock_.AdvanceTimeMilliseconds(1);
|
||||
int64_t time2_ms = fake_clock_.TimeInMilliseconds();
|
||||
header.ssrc = kSsrc2;
|
||||
header.timestamp = rtp_timestamp(time2_ms);
|
||||
header.extension.hasAudioLevel = true;
|
||||
header.extension.audioLevel = 20;
|
||||
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
sources = rtp_receiver_->GetSources();
|
||||
EXPECT_THAT(sources,
|
||||
UnorderedElementsAre(
|
||||
RtpSource(time1_ms, kSsrc1, RtpSourceType::SSRC, 10),
|
||||
RtpSource(time2_ms, kSsrc2, RtpSourceType::SSRC, 20)));
|
||||
|
||||
// Receive a packet from the first SSRC again and check that the level is
|
||||
// updated.
|
||||
fake_clock_.AdvanceTimeMilliseconds(1);
|
||||
int64_t time3_ms = fake_clock_.TimeInMilliseconds();
|
||||
header.ssrc = kSsrc1;
|
||||
header.timestamp = rtp_timestamp(time3_ms);
|
||||
header.extension.hasAudioLevel = true;
|
||||
header.extension.audioLevel = 30;
|
||||
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
sources = rtp_receiver_->GetSources();
|
||||
EXPECT_THAT(sources,
|
||||
UnorderedElementsAre(
|
||||
RtpSource(time3_ms, kSsrc1, RtpSourceType::SSRC, 30),
|
||||
RtpSource(time2_ms, kSsrc2, RtpSourceType::SSRC, 20)));
|
||||
}
|
||||
|
||||
TEST_F(RtpReceiverTest,
|
||||
MissingAudioLevelHeaderExtensionClearsRtpSourceAudioLevel) {
|
||||
RTPHeader header;
|
||||
int64_t time1_ms = fake_clock_.TimeInMilliseconds();
|
||||
header.payloadType = kPcmuPayloadType;
|
||||
header.ssrc = kSsrc1;
|
||||
header.timestamp = rtp_timestamp(time1_ms);
|
||||
header.extension.hasAudioLevel = true;
|
||||
header.extension.audioLevel = 10;
|
||||
const PayloadUnion payload_specific{
|
||||
AudioPayload{SdpAudioFormat("foo", 8000, 1), 0}};
|
||||
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
auto sources = rtp_receiver_->GetSources();
|
||||
EXPECT_THAT(sources, UnorderedElementsAre(RtpSource(
|
||||
time1_ms, kSsrc1, RtpSourceType::SSRC, 10)));
|
||||
|
||||
// Receive a second packet without the audio level header extension and check
|
||||
// that the audio level is cleared.
|
||||
fake_clock_.AdvanceTimeMilliseconds(1);
|
||||
int64_t time2_ms = fake_clock_.TimeInMilliseconds();
|
||||
header.timestamp = rtp_timestamp(time2_ms);
|
||||
header.extension.hasAudioLevel = false;
|
||||
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
sources = rtp_receiver_->GetSources();
|
||||
EXPECT_THAT(sources, UnorderedElementsAre(
|
||||
RtpSource(time2_ms, kSsrc1, RtpSourceType::SSRC)));
|
||||
}
|
||||
|
||||
TEST_F(RtpReceiverTest, UpdatesTimestampsIfAndOnlyIfPacketArrivesInOrder) {
|
||||
RTPHeader header;
|
||||
int64_t time1_ms = fake_clock_.TimeInMilliseconds();
|
||||
header.payloadType = kPcmuPayloadType;
|
||||
header.ssrc = kSsrc1;
|
||||
header.timestamp = rtp_timestamp(time1_ms);
|
||||
header.extension.hasAudioLevel = true;
|
||||
header.extension.audioLevel = 10;
|
||||
header.sequenceNumber = 0xfff0;
|
||||
|
||||
const PayloadUnion payload_specific{
|
||||
AudioPayload{SdpAudioFormat("foo", 8000, 1), 0}};
|
||||
uint32_t latest_timestamp;
|
||||
int64_t latest_receive_time_ms;
|
||||
|
||||
// No packet received yet.
|
||||
EXPECT_FALSE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp,
|
||||
&latest_receive_time_ms));
|
||||
// Initial packet
|
||||
const uint32_t timestamp_1 = header.timestamp;
|
||||
const int64_t receive_time_1 = fake_clock_.TimeInMilliseconds();
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
EXPECT_TRUE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp,
|
||||
&latest_receive_time_ms));
|
||||
EXPECT_EQ(latest_timestamp, timestamp_1);
|
||||
EXPECT_EQ(latest_receive_time_ms, receive_time_1);
|
||||
|
||||
// Late packet, timestamp not recorded.
|
||||
fake_clock_.AdvanceTimeMilliseconds(10);
|
||||
header.timestamp -= 900;
|
||||
header.sequenceNumber -= 2;
|
||||
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
EXPECT_TRUE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp,
|
||||
&latest_receive_time_ms));
|
||||
EXPECT_EQ(latest_timestamp, timestamp_1);
|
||||
EXPECT_EQ(latest_receive_time_ms, receive_time_1);
|
||||
|
||||
// New packet, still late, no wraparound.
|
||||
fake_clock_.AdvanceTimeMilliseconds(10);
|
||||
header.timestamp += 1800;
|
||||
header.sequenceNumber += 1;
|
||||
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
EXPECT_TRUE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp,
|
||||
&latest_receive_time_ms));
|
||||
EXPECT_EQ(latest_timestamp, timestamp_1);
|
||||
EXPECT_EQ(latest_receive_time_ms, receive_time_1);
|
||||
|
||||
// New packet, new timestamp recorded
|
||||
fake_clock_.AdvanceTimeMilliseconds(10);
|
||||
header.timestamp += 900;
|
||||
header.sequenceNumber += 2;
|
||||
const uint32_t timestamp_2 = header.timestamp;
|
||||
const int64_t receive_time_2 = fake_clock_.TimeInMilliseconds();
|
||||
const uint16_t seqno_2 = header.sequenceNumber;
|
||||
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
EXPECT_TRUE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp,
|
||||
&latest_receive_time_ms));
|
||||
EXPECT_EQ(latest_timestamp, timestamp_2);
|
||||
EXPECT_EQ(latest_receive_time_ms, receive_time_2);
|
||||
|
||||
// New packet, timestamp wraps around
|
||||
fake_clock_.AdvanceTimeMilliseconds(10);
|
||||
header.timestamp += 900;
|
||||
header.sequenceNumber += 20;
|
||||
const uint32_t timestamp_3 = header.timestamp;
|
||||
const int64_t receive_time_3 = fake_clock_.TimeInMilliseconds();
|
||||
EXPECT_LT(header.sequenceNumber, seqno_2); // Wrap-around
|
||||
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
EXPECT_TRUE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp,
|
||||
&latest_receive_time_ms));
|
||||
EXPECT_EQ(latest_timestamp, timestamp_3);
|
||||
EXPECT_EQ(latest_receive_time_ms, receive_time_3);
|
||||
}
|
||||
|
||||
TEST_F(RtpReceiverTest, UpdatesTimestampsWhenStreamResets) {
|
||||
RTPHeader header;
|
||||
int64_t time1_ms = fake_clock_.TimeInMilliseconds();
|
||||
header.payloadType = kPcmuPayloadType;
|
||||
header.ssrc = kSsrc1;
|
||||
header.timestamp = rtp_timestamp(time1_ms);
|
||||
header.extension.hasAudioLevel = true;
|
||||
header.extension.audioLevel = 10;
|
||||
header.sequenceNumber = 0xfff0;
|
||||
|
||||
const PayloadUnion payload_specific{
|
||||
AudioPayload{SdpAudioFormat("foo", 8000, 1), 0}};
|
||||
uint32_t latest_timestamp;
|
||||
int64_t latest_receive_time_ms;
|
||||
|
||||
// No packet received yet.
|
||||
EXPECT_FALSE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp,
|
||||
&latest_receive_time_ms));
|
||||
// Initial packet
|
||||
const uint32_t timestamp_1 = header.timestamp;
|
||||
const int64_t receive_time_1 = fake_clock_.TimeInMilliseconds();
|
||||
const uint16_t seqno_1 = header.sequenceNumber;
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
EXPECT_TRUE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp,
|
||||
&latest_receive_time_ms));
|
||||
EXPECT_EQ(latest_timestamp, timestamp_1);
|
||||
EXPECT_EQ(latest_receive_time_ms, receive_time_1);
|
||||
|
||||
// Packet with far in the past seqno, but unlikely to be a wrap-around.
|
||||
// Treated as a seqno discontinuity, and timestamp is recorded.
|
||||
fake_clock_.AdvanceTimeMilliseconds(10);
|
||||
header.timestamp += 900;
|
||||
header.sequenceNumber = 0x9000;
|
||||
|
||||
const uint32_t timestamp_2 = header.timestamp;
|
||||
const int64_t receive_time_2 = fake_clock_.TimeInMilliseconds();
|
||||
const uint16_t seqno_2 = header.sequenceNumber;
|
||||
EXPECT_LT(seqno_1 - seqno_2, 0x8000); // In the past.
|
||||
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, kTestPayload, sizeof(kTestPayload), payload_specific));
|
||||
EXPECT_TRUE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp,
|
||||
&latest_receive_time_ms));
|
||||
EXPECT_EQ(latest_timestamp, timestamp_2);
|
||||
EXPECT_EQ(latest_receive_time_ms, receive_time_2);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 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_receiver_video.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "modules/rtp_rtcp/include/rtp_cvo.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_payload_registry.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_format.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_format_video_generic.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_utility.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/trace_event.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
RTPReceiverStrategy* RTPReceiverStrategy::CreateVideoStrategy(
|
||||
RtpData* data_callback) {
|
||||
return new RTPReceiverVideo(data_callback);
|
||||
}
|
||||
|
||||
RTPReceiverVideo::RTPReceiverVideo(RtpData* data_callback)
|
||||
: RTPReceiverStrategy(data_callback) {}
|
||||
|
||||
RTPReceiverVideo::~RTPReceiverVideo() {}
|
||||
|
||||
int32_t RTPReceiverVideo::ParseRtpPacket(WebRtcRTPHeader* rtp_header,
|
||||
const PayloadUnion& specific_payload,
|
||||
const uint8_t* payload,
|
||||
size_t payload_length,
|
||||
int64_t timestamp_ms) {
|
||||
rtp_header->video_header().codec =
|
||||
specific_payload.video_payload().videoCodecType;
|
||||
|
||||
RTC_DCHECK_GE(payload_length, rtp_header->header.paddingLength);
|
||||
const size_t payload_data_length =
|
||||
payload_length - rtp_header->header.paddingLength;
|
||||
|
||||
if (payload == NULL || payload_data_length == 0) {
|
||||
return data_callback_->OnReceivedPayloadData(NULL, 0, rtp_header) == 0 ? 0
|
||||
: -1;
|
||||
}
|
||||
|
||||
if (first_packet_received_()) {
|
||||
RTC_LOG(LS_INFO) << "Received first video RTP packet";
|
||||
}
|
||||
|
||||
// We are not allowed to hold a critical section when calling below functions.
|
||||
std::unique_ptr<RtpDepacketizer> depacketizer(
|
||||
RtpDepacketizer::Create(rtp_header->video_header().codec));
|
||||
if (depacketizer.get() == NULL) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to create depacketizer.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
RtpDepacketizer::ParsedPayload parsed_payload;
|
||||
if (!depacketizer->Parse(&parsed_payload, payload, payload_data_length))
|
||||
return -1;
|
||||
|
||||
rtp_header->frameType = parsed_payload.frame_type;
|
||||
rtp_header->video_header() = parsed_payload.video_header();
|
||||
rtp_header->video_header().rotation = kVideoRotation_0;
|
||||
rtp_header->video_header().content_type = VideoContentType::UNSPECIFIED;
|
||||
rtp_header->video_header().video_timing.flags = VideoSendTiming::kInvalid;
|
||||
rtp_header->video_header().frame_marking.temporal_id = kNoTemporalIdx;
|
||||
|
||||
// Retrieve the video rotation information.
|
||||
if (rtp_header->header.extension.hasVideoRotation) {
|
||||
rtp_header->video_header().rotation =
|
||||
rtp_header->header.extension.videoRotation;
|
||||
}
|
||||
|
||||
if (rtp_header->header.extension.hasVideoContentType) {
|
||||
rtp_header->video_header().content_type =
|
||||
rtp_header->header.extension.videoContentType;
|
||||
}
|
||||
|
||||
if (rtp_header->header.extension.has_video_timing) {
|
||||
rtp_header->video_header().video_timing =
|
||||
rtp_header->header.extension.video_timing;
|
||||
}
|
||||
|
||||
rtp_header->video_header().playout_delay =
|
||||
rtp_header->header.extension.playout_delay;
|
||||
|
||||
if (rtp_header->header.extension.has_frame_marking) {
|
||||
rtp_header->video_header().frame_marking =
|
||||
rtp_header->header.extension.frame_marking;
|
||||
}
|
||||
|
||||
return data_callback_->OnReceivedPayloadData(parsed_payload.payload,
|
||||
parsed_payload.payload_length,
|
||||
rtp_header) == 0
|
||||
? 0
|
||||
: -1;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 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 MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_VIDEO_H_
|
||||
#define MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_VIDEO_H_
|
||||
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_receiver_strategy.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_utility.h"
|
||||
#include "rtc_base/onetimeevent.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RTPReceiverVideo : public RTPReceiverStrategy {
|
||||
public:
|
||||
explicit RTPReceiverVideo(RtpData* data_callback);
|
||||
|
||||
~RTPReceiverVideo() override;
|
||||
|
||||
int32_t ParseRtpPacket(WebRtcRTPHeader* rtp_header,
|
||||
const PayloadUnion& specific_payload,
|
||||
const uint8_t* packet,
|
||||
size_t packet_length,
|
||||
int64_t timestamp) override;
|
||||
|
||||
void SetPacketOverHead(uint16_t packet_over_head);
|
||||
|
||||
private:
|
||||
OneTimeEvent first_packet_received_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_VIDEO_H_
|
||||
@ -14,7 +14,6 @@
|
||||
#include <utility>
|
||||
|
||||
#include "modules/rtp_rtcp/source/byte_io.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_receiver_video.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
|
||||
@ -1,206 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 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/test/testAPI/test_api.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/rate_limiter.h"
|
||||
#include "test/null_transport.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
void LoopBackTransport::SetSendModule(RtpRtcp* rtp_rtcp_module,
|
||||
RTPPayloadRegistry* payload_registry,
|
||||
RtpReceiver* receiver,
|
||||
ReceiveStatistics* receive_statistics) {
|
||||
rtp_rtcp_module_ = rtp_rtcp_module;
|
||||
rtp_payload_registry_ = payload_registry;
|
||||
rtp_receiver_ = receiver;
|
||||
receive_statistics_ = receive_statistics;
|
||||
}
|
||||
|
||||
void LoopBackTransport::DropEveryNthPacket(int n) {
|
||||
packet_loss_ = n;
|
||||
}
|
||||
|
||||
bool LoopBackTransport::SendRtp(const uint8_t* data,
|
||||
size_t len,
|
||||
const PacketOptions& options) {
|
||||
count_++;
|
||||
if (packet_loss_ > 0) {
|
||||
if ((count_ % packet_loss_) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
RtpPacketReceived parsed_packet;
|
||||
if (!parsed_packet.Parse(data, len)) {
|
||||
return false;
|
||||
}
|
||||
const auto pl =
|
||||
rtp_payload_registry_->PayloadTypeToPayload(parsed_packet.PayloadType());
|
||||
if (!pl) {
|
||||
return false;
|
||||
}
|
||||
receive_statistics_->OnRtpPacket(parsed_packet);
|
||||
RTPHeader header;
|
||||
parsed_packet.GetHeader(&header);
|
||||
return rtp_receiver_->IncomingRtpPacket(
|
||||
header, parsed_packet.payload().data(),
|
||||
// RtpReceiver expects length including padding.
|
||||
parsed_packet.payload_size() + parsed_packet.padding_size(),
|
||||
pl->typeSpecific);
|
||||
}
|
||||
|
||||
bool LoopBackTransport::SendRtcp(const uint8_t* data, size_t len) {
|
||||
rtp_rtcp_module_->IncomingRtcpPacket((const uint8_t*)data, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t TestRtpReceiver::OnReceivedPayloadData(
|
||||
const uint8_t* payload_data,
|
||||
size_t payload_size,
|
||||
const webrtc::WebRtcRTPHeader* rtp_header) {
|
||||
EXPECT_LE(payload_size, sizeof(payload_data_));
|
||||
memcpy(payload_data_, payload_data, payload_size);
|
||||
memcpy(&rtp_header_, rtp_header, sizeof(rtp_header_));
|
||||
payload_size_ = payload_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
class RtpRtcpAPITest : public ::testing::Test {
|
||||
protected:
|
||||
RtpRtcpAPITest()
|
||||
: fake_clock_(123456), retransmission_rate_limiter_(&fake_clock_, 1000) {
|
||||
}
|
||||
~RtpRtcpAPITest() override = default;
|
||||
|
||||
void SetUp() override {
|
||||
const uint32_t kInitialSsrc = 8888;
|
||||
RtpRtcp::Configuration configuration;
|
||||
configuration.audio = true;
|
||||
configuration.clock = &fake_clock_;
|
||||
configuration.outgoing_transport = &null_transport_;
|
||||
configuration.retransmission_rate_limiter = &retransmission_rate_limiter_;
|
||||
module_.reset(RtpRtcp::CreateRtpRtcp(configuration));
|
||||
module_->SetSSRC(kInitialSsrc);
|
||||
}
|
||||
|
||||
SimulatedClock fake_clock_;
|
||||
test::NullTransport null_transport_;
|
||||
RateLimiter retransmission_rate_limiter_;
|
||||
std::unique_ptr<RtpRtcp> module_;
|
||||
};
|
||||
|
||||
TEST_F(RtpRtcpAPITest, Basic) {
|
||||
const uint16_t kSequenceNumber = 2345;
|
||||
module_->SetSequenceNumber(kSequenceNumber);
|
||||
EXPECT_EQ(kSequenceNumber, module_->SequenceNumber());
|
||||
|
||||
const uint32_t kTimestamp = 4567;
|
||||
module_->SetStartTimestamp(kTimestamp);
|
||||
EXPECT_EQ(kTimestamp, module_->StartTimestamp());
|
||||
|
||||
EXPECT_FALSE(module_->Sending());
|
||||
EXPECT_EQ(0, module_->SetSendingStatus(true));
|
||||
EXPECT_TRUE(module_->Sending());
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAPITest, PacketSize) {
|
||||
module_->SetMaxRtpPacketSize(1234);
|
||||
EXPECT_EQ(1234u, module_->MaxRtpPacketSize());
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAPITest, SSRC) {
|
||||
const uint32_t kSsrc = 3456;
|
||||
module_->SetSSRC(kSsrc);
|
||||
EXPECT_EQ(kSsrc, module_->SSRC());
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAPITest, RTCP) {
|
||||
EXPECT_EQ(RtcpMode::kOff, module_->RTCP());
|
||||
module_->SetRTCPStatus(RtcpMode::kCompound);
|
||||
EXPECT_EQ(RtcpMode::kCompound, module_->RTCP());
|
||||
|
||||
EXPECT_EQ(0, module_->SetCNAME("john.doe@test.test"));
|
||||
|
||||
EXPECT_FALSE(module_->TMMBR());
|
||||
module_->SetTMMBRStatus(true);
|
||||
EXPECT_TRUE(module_->TMMBR());
|
||||
module_->SetTMMBRStatus(false);
|
||||
EXPECT_FALSE(module_->TMMBR());
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAPITest, RtxSender) {
|
||||
module_->SetRtxSendStatus(kRtxRetransmitted);
|
||||
EXPECT_EQ(kRtxRetransmitted, module_->RtxSendStatus());
|
||||
|
||||
module_->SetRtxSendStatus(kRtxOff);
|
||||
EXPECT_EQ(kRtxOff, module_->RtxSendStatus());
|
||||
|
||||
module_->SetRtxSendStatus(kRtxRetransmitted);
|
||||
EXPECT_EQ(kRtxRetransmitted, module_->RtxSendStatus());
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAPITest, LegalMidName) {
|
||||
static const std::string kLegalMidNames[] = {
|
||||
// clang-format off
|
||||
"audio",
|
||||
"audio0",
|
||||
"audio_0",
|
||||
// clang-format on
|
||||
};
|
||||
for (const auto& name : kLegalMidNames) {
|
||||
EXPECT_TRUE(StreamId::IsLegalMidName(name))
|
||||
<< "Mid should be legal: " << name;
|
||||
}
|
||||
|
||||
static const std::string kNonLegalMidNames[] = {
|
||||
// clang-format off
|
||||
"",
|
||||
"(audio0)",
|
||||
// clang-format on
|
||||
};
|
||||
for (const auto& name : kNonLegalMidNames) {
|
||||
EXPECT_FALSE(StreamId::IsLegalMidName(name))
|
||||
<< "Mid should not be legal: " << name;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAPITest, LegalRsidName) {
|
||||
static const std::string kLegalRsidNames[] = {
|
||||
// clang-format off
|
||||
"audio",
|
||||
"audio0",
|
||||
// clang-format on
|
||||
};
|
||||
for (const auto& name : kLegalRsidNames) {
|
||||
EXPECT_TRUE(StreamId::IsLegalRsidName(name))
|
||||
<< "Rsid should be legal: " << name;
|
||||
}
|
||||
|
||||
static const std::string kNonLegalRsidNames[] = {
|
||||
// clang-format off
|
||||
"",
|
||||
"audio_0",
|
||||
"(audio0)",
|
||||
// clang-format on
|
||||
};
|
||||
for (const auto& name : kNonLegalRsidNames) {
|
||||
EXPECT_FALSE(StreamId::IsLegalRsidName(name))
|
||||
<< "Rsid should not be legal: " << name;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 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 MODULES_RTP_RTCP_TEST_TESTAPI_TEST_API_H_
|
||||
#define MODULES_RTP_RTCP_TEST_TESTAPI_TEST_API_H_
|
||||
|
||||
#include "api/call/transport.h"
|
||||
#include "common_types.h" // NOLINT(build/include)
|
||||
#include "modules/rtp_rtcp/include/receive_statistics.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_header_parser.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_payload_registry.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_receiver.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// This class sends all its packet straight to the provided RtpRtcp module.
|
||||
// with optional packet loss.
|
||||
class LoopBackTransport : public Transport {
|
||||
public:
|
||||
LoopBackTransport()
|
||||
: count_(0),
|
||||
packet_loss_(0),
|
||||
rtp_payload_registry_(NULL),
|
||||
rtp_receiver_(NULL),
|
||||
rtp_rtcp_module_(NULL) {}
|
||||
void SetSendModule(RtpRtcp* rtp_rtcp_module,
|
||||
RTPPayloadRegistry* payload_registry,
|
||||
RtpReceiver* receiver,
|
||||
ReceiveStatistics* receive_statistics);
|
||||
void DropEveryNthPacket(int n);
|
||||
bool SendRtp(const uint8_t* data,
|
||||
size_t len,
|
||||
const PacketOptions& options) override;
|
||||
bool SendRtcp(const uint8_t* data, size_t len) override;
|
||||
|
||||
private:
|
||||
int count_;
|
||||
int packet_loss_;
|
||||
ReceiveStatistics* receive_statistics_;
|
||||
RTPPayloadRegistry* rtp_payload_registry_;
|
||||
RtpReceiver* rtp_receiver_;
|
||||
RtpRtcp* rtp_rtcp_module_;
|
||||
};
|
||||
|
||||
class TestRtpReceiver : public RtpData {
|
||||
public:
|
||||
int32_t OnReceivedPayloadData(
|
||||
const uint8_t* payload_data,
|
||||
size_t payload_size,
|
||||
const webrtc::WebRtcRTPHeader* rtp_header) override;
|
||||
|
||||
const uint8_t* payload_data() const { return payload_data_; }
|
||||
size_t payload_size() const { return payload_size_; }
|
||||
webrtc::WebRtcRTPHeader rtp_header() const { return rtp_header_; }
|
||||
|
||||
private:
|
||||
uint8_t payload_data_[1500];
|
||||
size_t payload_size_;
|
||||
webrtc::WebRtcRTPHeader rtp_header_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_RTP_RTCP_TEST_TESTAPI_TEST_API_H_
|
||||
@ -1,288 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 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 <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common_types.h" // NOLINT(build/include)
|
||||
#include "modules/audio_coding/codecs/audio_format_conversion.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_receiver_audio.h"
|
||||
#include "modules/rtp_rtcp/test/testAPI/test_api.h"
|
||||
#include "rtc_base/rate_limiter.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
const uint32_t kTestRate = 64000u;
|
||||
const uint8_t kTestPayload[] = {'t', 'e', 's', 't'};
|
||||
const uint8_t kPcmuPayloadType = 96;
|
||||
const uint8_t kDtmfPayloadType = 97;
|
||||
const uint32_t kSsrc = 3456;
|
||||
const uint32_t kTimestamp = 4567;
|
||||
|
||||
struct CngCodecSpec {
|
||||
int payload_type;
|
||||
int clockrate_hz;
|
||||
};
|
||||
|
||||
const CngCodecSpec kCngCodecs[] = {{13, 8000},
|
||||
{103, 16000},
|
||||
{104, 32000},
|
||||
{105, 48000}};
|
||||
|
||||
// Rough sanity check of DTMF payload.
|
||||
void VerifyDtmf(const uint8_t* payloadData,
|
||||
size_t payloadSize) {
|
||||
EXPECT_EQ(payloadSize, 4u);
|
||||
uint8_t p0 = (payloadSize > 0) ? payloadData[0] : 0xff;
|
||||
uint8_t p1 = (payloadSize > 1) ? payloadData[1] : 0xff;
|
||||
uint8_t p2 = (payloadSize > 2) ? payloadData[2] : 0xff;
|
||||
uint8_t p3 = (payloadSize > 3) ? payloadData[3] : 0xff;
|
||||
uint8_t event = p0;
|
||||
bool reserved = (p1 >> 6) & 1;
|
||||
uint8_t volume = p1 & 63;
|
||||
uint16_t duration = (p2 << 8) | p3;
|
||||
|
||||
// 0-15 are digits, #, *, A-D, 32 is answer tone (see rfc 4734)
|
||||
EXPECT_LE(event, 32u);
|
||||
EXPECT_TRUE(event < 16u || event == 32u);
|
||||
EXPECT_FALSE(reserved);
|
||||
EXPECT_EQ(volume, 10u);
|
||||
// Long duration for answer tone events only
|
||||
EXPECT_TRUE(duration <= 1280 || event == 32u);
|
||||
}
|
||||
|
||||
class VerifyingAudioReceiver : public RtpData {
|
||||
public:
|
||||
int32_t OnReceivedPayloadData(
|
||||
const uint8_t* payloadData,
|
||||
size_t payloadSize,
|
||||
const webrtc::WebRtcRTPHeader* rtpHeader) override {
|
||||
const uint8_t payload_type = rtpHeader->header.payloadType;
|
||||
if (payload_type == kPcmuPayloadType) {
|
||||
EXPECT_EQ(sizeof(kTestPayload), payloadSize);
|
||||
// All our test vectors for PCMU are equal to |kTestPayload|.
|
||||
const size_t min_size = std::min(sizeof(kTestPayload), payloadSize);
|
||||
EXPECT_EQ(0, memcmp(payloadData, kTestPayload, min_size));
|
||||
} else if (payload_type == kDtmfPayloadType) {
|
||||
VerifyDtmf(payloadData, payloadSize);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class RtpRtcpAudioTest : public ::testing::Test {
|
||||
protected:
|
||||
RtpRtcpAudioTest()
|
||||
: fake_clock_(123456),
|
||||
retransmission_rate_limiter_(&fake_clock_, 1000),
|
||||
receive_statistics1_(ReceiveStatistics::Create(&fake_clock_)),
|
||||
receive_statistics2_(ReceiveStatistics::Create(&fake_clock_)),
|
||||
rtp_receiver1_(
|
||||
RtpReceiver::CreateAudioReceiver(&fake_clock_,
|
||||
&data_receiver1_,
|
||||
&rtp_payload_registry1_)),
|
||||
rtp_receiver2_(
|
||||
RtpReceiver::CreateAudioReceiver(&fake_clock_,
|
||||
&data_receiver2_,
|
||||
&rtp_payload_registry2_)) {
|
||||
RtpRtcp::Configuration configuration;
|
||||
configuration.audio = true;
|
||||
configuration.clock = &fake_clock_;
|
||||
configuration.receive_statistics = receive_statistics1_.get();
|
||||
configuration.outgoing_transport = &transport1_;
|
||||
configuration.retransmission_rate_limiter = &retransmission_rate_limiter_;
|
||||
module1_.reset(RtpRtcp::CreateRtpRtcp(configuration));
|
||||
|
||||
configuration.receive_statistics = receive_statistics2_.get();
|
||||
configuration.outgoing_transport = &transport2_;
|
||||
module2_.reset(RtpRtcp::CreateRtpRtcp(configuration));
|
||||
|
||||
transport1_.SetSendModule(module2_.get(), &rtp_payload_registry2_,
|
||||
rtp_receiver2_.get(), receive_statistics2_.get());
|
||||
transport2_.SetSendModule(module1_.get(), &rtp_payload_registry1_,
|
||||
rtp_receiver1_.get(), receive_statistics1_.get());
|
||||
}
|
||||
|
||||
~RtpRtcpAudioTest() override = default;
|
||||
|
||||
void RegisterPayload(const CodecInst& codec) {
|
||||
EXPECT_EQ(0, module1_->RegisterSendPayload(codec));
|
||||
EXPECT_EQ(0, rtp_receiver1_->RegisterReceivePayload(codec.pltype,
|
||||
CodecInstToSdp(codec)));
|
||||
EXPECT_EQ(0, module2_->RegisterSendPayload(codec));
|
||||
EXPECT_EQ(0, rtp_receiver2_->RegisterReceivePayload(codec.pltype,
|
||||
CodecInstToSdp(codec)));
|
||||
}
|
||||
|
||||
SimulatedClock fake_clock_;
|
||||
RateLimiter retransmission_rate_limiter_;
|
||||
VerifyingAudioReceiver data_receiver1_;
|
||||
VerifyingAudioReceiver data_receiver2_;
|
||||
std::unique_ptr<ReceiveStatistics> receive_statistics1_;
|
||||
std::unique_ptr<ReceiveStatistics> receive_statistics2_;
|
||||
RTPPayloadRegistry rtp_payload_registry1_;
|
||||
RTPPayloadRegistry rtp_payload_registry2_;
|
||||
std::unique_ptr<RtpReceiver> rtp_receiver1_;
|
||||
std::unique_ptr<RtpReceiver> rtp_receiver2_;
|
||||
std::unique_ptr<RtpRtcp> module1_;
|
||||
std::unique_ptr<RtpRtcp> module2_;
|
||||
LoopBackTransport transport1_;
|
||||
LoopBackTransport transport2_;
|
||||
};
|
||||
|
||||
TEST_F(RtpRtcpAudioTest, Basic) {
|
||||
module1_->SetSSRC(kSsrc);
|
||||
module1_->SetStartTimestamp(kTimestamp);
|
||||
|
||||
// Test detection at the end of a DTMF tone.
|
||||
// EXPECT_EQ(0, module2_->SetTelephoneEventForwardToDecoder(true));
|
||||
|
||||
EXPECT_EQ(0, module1_->SetSendingStatus(true));
|
||||
|
||||
// Start basic RTP test.
|
||||
|
||||
// Send an empty RTP packet.
|
||||
// Should fail since we have not registered the payload type.
|
||||
EXPECT_FALSE(module1_->SendOutgoingData(webrtc::kAudioFrameSpeech,
|
||||
kPcmuPayloadType, 0, -1, nullptr, 0,
|
||||
nullptr, nullptr, nullptr));
|
||||
|
||||
CodecInst voice_codec = {};
|
||||
voice_codec.pltype = kPcmuPayloadType;
|
||||
voice_codec.plfreq = 8000;
|
||||
voice_codec.rate = kTestRate;
|
||||
memcpy(voice_codec.plname, "PCMU", 5);
|
||||
RegisterPayload(voice_codec);
|
||||
|
||||
EXPECT_TRUE(module1_->SendOutgoingData(webrtc::kAudioFrameSpeech,
|
||||
kPcmuPayloadType, 0, -1, kTestPayload,
|
||||
4, nullptr, nullptr, nullptr));
|
||||
|
||||
EXPECT_EQ(kSsrc, rtp_receiver2_->SSRC());
|
||||
uint32_t timestamp;
|
||||
int64_t receive_time_ms;
|
||||
EXPECT_TRUE(
|
||||
rtp_receiver2_->GetLatestTimestamps(×tamp, &receive_time_ms));
|
||||
EXPECT_EQ(kTimestamp, timestamp);
|
||||
EXPECT_EQ(fake_clock_.TimeInMilliseconds(), receive_time_ms);
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAudioTest, DTMF) {
|
||||
CodecInst voice_codec = {};
|
||||
voice_codec.pltype = kPcmuPayloadType;
|
||||
voice_codec.plfreq = 8000;
|
||||
voice_codec.rate = kTestRate;
|
||||
memcpy(voice_codec.plname, "PCMU", 5);
|
||||
RegisterPayload(voice_codec);
|
||||
|
||||
module1_->SetSSRC(kSsrc);
|
||||
module1_->SetStartTimestamp(kTimestamp);
|
||||
EXPECT_EQ(0, module1_->SetSendingStatus(true));
|
||||
|
||||
// Prepare for DTMF.
|
||||
voice_codec.pltype = kDtmfPayloadType;
|
||||
voice_codec.plfreq = 8000;
|
||||
memcpy(voice_codec.plname, "telephone-event", 16);
|
||||
|
||||
EXPECT_EQ(0, module1_->RegisterSendPayload(voice_codec));
|
||||
EXPECT_EQ(0, rtp_receiver2_->RegisterReceivePayload(
|
||||
voice_codec.pltype, CodecInstToSdp(voice_codec)));
|
||||
|
||||
// Start DTMF test.
|
||||
int timeStamp = 160;
|
||||
|
||||
// Send a DTMF tone using RFC 2833 (4733).
|
||||
for (int i = 0; i < 16; i++) {
|
||||
EXPECT_EQ(0, module1_->SendTelephoneEventOutband(i, timeStamp, 10));
|
||||
}
|
||||
timeStamp += 160; // Prepare for next packet.
|
||||
|
||||
// Send RTP packets for 16 tones a 160 ms 100ms
|
||||
// pause between = 2560ms + 1600ms = 4160ms
|
||||
for (; timeStamp <= 250 * 160; timeStamp += 160) {
|
||||
EXPECT_TRUE(module1_->SendOutgoingData(
|
||||
webrtc::kAudioFrameSpeech, kPcmuPayloadType, timeStamp, -1,
|
||||
kTestPayload, 4, nullptr, nullptr, nullptr));
|
||||
fake_clock_.AdvanceTimeMilliseconds(20);
|
||||
module1_->Process();
|
||||
}
|
||||
EXPECT_EQ(0, module1_->SendTelephoneEventOutband(32, 9000, 10));
|
||||
|
||||
for (; timeStamp <= 740 * 160; timeStamp += 160) {
|
||||
EXPECT_TRUE(module1_->SendOutgoingData(
|
||||
webrtc::kAudioFrameSpeech, kPcmuPayloadType, timeStamp, -1,
|
||||
kTestPayload, 4, nullptr, nullptr, nullptr));
|
||||
fake_clock_.AdvanceTimeMilliseconds(20);
|
||||
module1_->Process();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAudioTest, ComfortNoise) {
|
||||
module1_->SetSSRC(kSsrc);
|
||||
module1_->SetStartTimestamp(kTimestamp);
|
||||
|
||||
EXPECT_EQ(0, module1_->SetSendingStatus(true));
|
||||
|
||||
// Register PCMU and all four comfort noise codecs.
|
||||
CodecInst voice_codec = {};
|
||||
voice_codec.pltype = kPcmuPayloadType;
|
||||
voice_codec.plfreq = 8000;
|
||||
voice_codec.rate = kTestRate;
|
||||
memcpy(voice_codec.plname, "PCMU", 5);
|
||||
RegisterPayload(voice_codec);
|
||||
|
||||
for (const auto& c : kCngCodecs) {
|
||||
CodecInst cng_codec = {};
|
||||
cng_codec.pltype = c.payload_type;
|
||||
cng_codec.plfreq = c.clockrate_hz;
|
||||
memcpy(cng_codec.plname, "CN", 3);
|
||||
RegisterPayload(cng_codec);
|
||||
}
|
||||
|
||||
// Transmit comfort noise packets interleaved by PCMU packets.
|
||||
uint32_t in_timestamp = 0;
|
||||
for (const auto& c : kCngCodecs) {
|
||||
uint32_t timestamp;
|
||||
int64_t receive_time_ms;
|
||||
EXPECT_TRUE(module1_->SendOutgoingData(
|
||||
webrtc::kAudioFrameSpeech, kPcmuPayloadType, in_timestamp, -1,
|
||||
kTestPayload, 4, nullptr, nullptr, nullptr));
|
||||
|
||||
EXPECT_EQ(kSsrc, rtp_receiver2_->SSRC());
|
||||
EXPECT_TRUE(
|
||||
rtp_receiver2_->GetLatestTimestamps(×tamp, &receive_time_ms));
|
||||
EXPECT_EQ(kTimestamp + in_timestamp, timestamp);
|
||||
EXPECT_EQ(fake_clock_.TimeInMilliseconds(), receive_time_ms);
|
||||
in_timestamp += 10;
|
||||
fake_clock_.AdvanceTimeMilliseconds(20);
|
||||
|
||||
EXPECT_TRUE(module1_->SendOutgoingData(
|
||||
webrtc::kAudioFrameCN, c.payload_type, in_timestamp, -1, kTestPayload,
|
||||
1, nullptr, nullptr, nullptr));
|
||||
|
||||
EXPECT_EQ(kSsrc, rtp_receiver2_->SSRC());
|
||||
EXPECT_TRUE(
|
||||
rtp_receiver2_->GetLatestTimestamps(×tamp, &receive_time_ms));
|
||||
EXPECT_EQ(kTimestamp + in_timestamp, timestamp);
|
||||
EXPECT_EQ(fake_clock_.TimeInMilliseconds(), receive_time_ms);
|
||||
in_timestamp += 10;
|
||||
fake_clock_.AdvanceTimeMilliseconds(20);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -1,186 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 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 <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common_types.h" // NOLINT(build/include)
|
||||
#include "modules/audio_coding/codecs/audio_format_conversion.h"
|
||||
#include "modules/rtp_rtcp/include/receive_statistics.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_receiver_audio.h"
|
||||
#include "modules/rtp_rtcp/test/testAPI/test_api.h"
|
||||
#include "rtc_base/rate_limiter.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
const uint16_t kSequenceNumber = 2345;
|
||||
const uint32_t kSsrc = 3456;
|
||||
const uint32_t kTimestamp = 4567;
|
||||
|
||||
class RtcpCallback : public RtcpIntraFrameObserver {
|
||||
public:
|
||||
void OnReceivedIntraFrameRequest(uint32_t ssrc) override {}
|
||||
};
|
||||
|
||||
class RtpRtcpRtcpTest : public ::testing::Test {
|
||||
protected:
|
||||
RtpRtcpRtcpTest()
|
||||
: fake_clock_(123456),
|
||||
retransmission_rate_limiter_(&fake_clock_, 1000),
|
||||
receive_statistics1_(ReceiveStatistics::Create(&fake_clock_)),
|
||||
receive_statistics2_(ReceiveStatistics::Create(&fake_clock_)),
|
||||
rtp_receiver1_(
|
||||
RtpReceiver::CreateAudioReceiver(&fake_clock_,
|
||||
&receiver_,
|
||||
&rtp_payload_registry1_)),
|
||||
rtp_receiver2_(
|
||||
RtpReceiver::CreateAudioReceiver(&fake_clock_,
|
||||
&receiver_,
|
||||
&rtp_payload_registry2_)) {}
|
||||
~RtpRtcpRtcpTest() override = default;
|
||||
|
||||
void SetUp() override {
|
||||
RtpRtcp::Configuration configuration;
|
||||
configuration.audio = true;
|
||||
configuration.clock = &fake_clock_;
|
||||
configuration.receive_statistics = receive_statistics1_.get();
|
||||
configuration.outgoing_transport = &transport1_;
|
||||
configuration.intra_frame_callback = &rtcp_callback1_;
|
||||
configuration.retransmission_rate_limiter = &retransmission_rate_limiter_;
|
||||
module1_.reset(RtpRtcp::CreateRtpRtcp(configuration));
|
||||
|
||||
configuration.receive_statistics = receive_statistics2_.get();
|
||||
configuration.outgoing_transport = &transport2_;
|
||||
configuration.intra_frame_callback = &rtcp_callback2_;
|
||||
module2_.reset(RtpRtcp::CreateRtpRtcp(configuration));
|
||||
|
||||
transport1_.SetSendModule(module2_.get(), &rtp_payload_registry2_,
|
||||
rtp_receiver2_.get(), receive_statistics2_.get());
|
||||
transport2_.SetSendModule(module1_.get(), &rtp_payload_registry1_,
|
||||
rtp_receiver1_.get(), receive_statistics1_.get());
|
||||
|
||||
module1_->SetRTCPStatus(RtcpMode::kCompound);
|
||||
module2_->SetRTCPStatus(RtcpMode::kCompound);
|
||||
|
||||
module2_->SetSSRC(kSsrc + 1);
|
||||
module2_->SetRemoteSSRC(kSsrc);
|
||||
module1_->SetSSRC(kSsrc);
|
||||
module1_->SetSequenceNumber(kSequenceNumber);
|
||||
module1_->SetStartTimestamp(kTimestamp);
|
||||
|
||||
module1_->SetCsrcs(kCsrcs);
|
||||
EXPECT_EQ(0, module1_->SetCNAME("john.doe@test.test"));
|
||||
|
||||
EXPECT_EQ(0, module1_->SetSendingStatus(true));
|
||||
|
||||
CodecInst voice_codec;
|
||||
voice_codec.pltype = 96;
|
||||
voice_codec.plfreq = 8000;
|
||||
voice_codec.rate = 64000;
|
||||
memcpy(voice_codec.plname, "PCMU", 5);
|
||||
|
||||
EXPECT_EQ(0, module1_->RegisterSendPayload(voice_codec));
|
||||
EXPECT_EQ(0, rtp_receiver1_->RegisterReceivePayload(
|
||||
voice_codec.pltype, CodecInstToSdp(voice_codec)));
|
||||
EXPECT_EQ(0, module2_->RegisterSendPayload(voice_codec));
|
||||
EXPECT_EQ(0, rtp_receiver2_->RegisterReceivePayload(
|
||||
voice_codec.pltype, CodecInstToSdp(voice_codec)));
|
||||
|
||||
// We need to send one RTP packet to get the RTCP packet to be accepted by
|
||||
// the receiving module.
|
||||
// Send RTP packet with the data "testtest".
|
||||
const uint8_t test[9] = "testtest";
|
||||
EXPECT_EQ(true,
|
||||
module1_->SendOutgoingData(webrtc::kAudioFrameSpeech, 96, 0, -1,
|
||||
test, 8, nullptr, nullptr, nullptr));
|
||||
}
|
||||
|
||||
const std::vector<uint32_t> kCsrcs = {1234, 2345};
|
||||
SimulatedClock fake_clock_;
|
||||
RateLimiter retransmission_rate_limiter_;
|
||||
RtcpCallback rtcp_callback1_;
|
||||
RtcpCallback rtcp_callback2_;
|
||||
RTPPayloadRegistry rtp_payload_registry1_;
|
||||
RTPPayloadRegistry rtp_payload_registry2_;
|
||||
TestRtpReceiver receiver_;
|
||||
std::unique_ptr<ReceiveStatistics> receive_statistics1_;
|
||||
std::unique_ptr<ReceiveStatistics> receive_statistics2_;
|
||||
std::unique_ptr<RtpReceiver> rtp_receiver1_;
|
||||
std::unique_ptr<RtpReceiver> rtp_receiver2_;
|
||||
std::unique_ptr<RtpRtcp> module1_;
|
||||
std::unique_ptr<RtpRtcp> module2_;
|
||||
LoopBackTransport transport1_;
|
||||
LoopBackTransport transport2_;
|
||||
};
|
||||
|
||||
TEST_F(RtpRtcpRtcpTest, RTCP_CNAME) {
|
||||
// Set cname of mixed.
|
||||
EXPECT_EQ(0, module1_->AddMixedCNAME(kCsrcs[0], "john@192.168.0.1"));
|
||||
EXPECT_EQ(0, module1_->AddMixedCNAME(kCsrcs[1], "jane@192.168.0.2"));
|
||||
|
||||
EXPECT_EQ(-1, module1_->RemoveMixedCNAME(kCsrcs[0] + 1));
|
||||
EXPECT_EQ(0, module1_->RemoveMixedCNAME(kCsrcs[1]));
|
||||
EXPECT_EQ(0, module1_->AddMixedCNAME(kCsrcs[1], "jane@192.168.0.2"));
|
||||
|
||||
// Send RTCP packet, triggered by timer.
|
||||
fake_clock_.AdvanceTimeMilliseconds(7500);
|
||||
module1_->Process();
|
||||
fake_clock_.AdvanceTimeMilliseconds(100);
|
||||
module2_->Process();
|
||||
|
||||
char cName[RTCP_CNAME_SIZE];
|
||||
EXPECT_EQ(-1, module2_->RemoteCNAME(rtp_receiver2_->SSRC() + 1, cName));
|
||||
|
||||
// Check multiple CNAME.
|
||||
EXPECT_EQ(0, module2_->RemoteCNAME(rtp_receiver2_->SSRC(), cName));
|
||||
EXPECT_EQ(0, strncmp(cName, "john.doe@test.test", RTCP_CNAME_SIZE));
|
||||
|
||||
EXPECT_EQ(0, module2_->RemoteCNAME(kCsrcs[0], cName));
|
||||
EXPECT_EQ(0, strncmp(cName, "john@192.168.0.1", RTCP_CNAME_SIZE));
|
||||
|
||||
EXPECT_EQ(0, module2_->RemoteCNAME(kCsrcs[1], cName));
|
||||
EXPECT_EQ(0, strncmp(cName, "jane@192.168.0.2", RTCP_CNAME_SIZE));
|
||||
|
||||
EXPECT_EQ(0, module1_->SetSendingStatus(false));
|
||||
|
||||
// Test that BYE clears the CNAME.
|
||||
EXPECT_EQ(-1, module2_->RemoteCNAME(rtp_receiver2_->SSRC(), cName));
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpRtcpTest, RemoteRTCPStatRemote) {
|
||||
std::vector<RTCPReportBlock> report_blocks;
|
||||
EXPECT_EQ(0, module1_->RemoteRTCPStat(&report_blocks));
|
||||
EXPECT_EQ(0u, report_blocks.size());
|
||||
|
||||
// Send RTCP packet, triggered by timer.
|
||||
fake_clock_.AdvanceTimeMilliseconds(7500);
|
||||
module1_->Process();
|
||||
fake_clock_.AdvanceTimeMilliseconds(100);
|
||||
module2_->Process();
|
||||
|
||||
EXPECT_EQ(0, module1_->RemoteRTCPStat(&report_blocks));
|
||||
ASSERT_EQ(1u, report_blocks.size());
|
||||
|
||||
// |kSsrc+1| is the SSRC of module2 that send the report.
|
||||
EXPECT_EQ(kSsrc + 1, report_blocks[0].sender_ssrc);
|
||||
EXPECT_EQ(kSsrc, report_blocks[0].source_ssrc);
|
||||
|
||||
EXPECT_EQ(0, report_blocks[0].packets_lost);
|
||||
EXPECT_LT(0u, report_blocks[0].delay_since_last_sender_report);
|
||||
EXPECT_EQ(kSequenceNumber, report_blocks[0].extended_highest_sequence_number);
|
||||
EXPECT_EQ(0u, report_blocks[0].fraction_lost);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
@ -1,166 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 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 <stdlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "api/video_codecs/video_codec.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_payload_registry.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "modules/rtp_rtcp/source/byte_io.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_receiver_video.h"
|
||||
#include "modules/rtp_rtcp/test/testAPI/test_api.h"
|
||||
#include "rtc_base/rate_limiter.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace {
|
||||
const uint32_t kSsrc = 3456;
|
||||
const unsigned char kPayloadType = 100;
|
||||
};
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RtpRtcpVideoTest : public ::testing::Test {
|
||||
protected:
|
||||
RtpRtcpVideoTest()
|
||||
: fake_clock_(123456),
|
||||
retransmission_rate_limiter_(&fake_clock_, 1000),
|
||||
receive_statistics_(ReceiveStatistics::Create(&fake_clock_)),
|
||||
rtp_receiver_(
|
||||
RtpReceiver::CreateVideoReceiver(&fake_clock_,
|
||||
&receiver_,
|
||||
&rtp_payload_registry_)) {}
|
||||
~RtpRtcpVideoTest() override = default;
|
||||
|
||||
void SetUp() override {
|
||||
RtpRtcp::Configuration configuration;
|
||||
configuration.audio = false;
|
||||
configuration.clock = &fake_clock_;
|
||||
configuration.outgoing_transport = &transport_;
|
||||
configuration.retransmission_rate_limiter = &retransmission_rate_limiter_;
|
||||
video_module_.reset(RtpRtcp::CreateRtpRtcp(configuration));
|
||||
|
||||
video_module_->SetRTCPStatus(RtcpMode::kCompound);
|
||||
video_module_->SetSSRC(kSsrc);
|
||||
video_module_->SetStorePacketsStatus(true, 600);
|
||||
EXPECT_EQ(0, video_module_->SetSendingStatus(true));
|
||||
|
||||
transport_.SetSendModule(video_module_.get(), &rtp_payload_registry_,
|
||||
rtp_receiver_.get(), receive_statistics_.get());
|
||||
|
||||
VideoCodec video_codec;
|
||||
memset(&video_codec, 0, sizeof(video_codec));
|
||||
video_codec.plType = 123;
|
||||
video_codec.codecType = kVideoCodecI420;
|
||||
|
||||
video_module_->RegisterVideoSendPayload(123, "I420");
|
||||
EXPECT_EQ(0, rtp_payload_registry_.RegisterReceivePayload(video_codec));
|
||||
|
||||
payload_data_length_ = sizeof(video_frame_);
|
||||
|
||||
for (size_t n = 0; n < payload_data_length_; n++) {
|
||||
video_frame_[n] = n % 10;
|
||||
}
|
||||
}
|
||||
|
||||
size_t BuildRTPheader(uint8_t* buffer,
|
||||
uint32_t timestamp,
|
||||
uint32_t sequence_number) {
|
||||
buffer[0] = static_cast<uint8_t>(0x80); // version 2
|
||||
buffer[1] = static_cast<uint8_t>(kPayloadType);
|
||||
ByteWriter<uint16_t>::WriteBigEndian(buffer + 2, sequence_number);
|
||||
ByteWriter<uint32_t>::WriteBigEndian(buffer + 4, timestamp);
|
||||
ByteWriter<uint32_t>::WriteBigEndian(buffer + 8, 0x1234); // SSRC.
|
||||
size_t rtpHeaderLength = 12;
|
||||
return rtpHeaderLength;
|
||||
}
|
||||
|
||||
size_t PaddingPacket(uint8_t* buffer,
|
||||
uint32_t timestamp,
|
||||
uint32_t sequence_number,
|
||||
size_t bytes) {
|
||||
// Max in the RFC 3550 is 255 bytes, we limit it to be modulus 32 for SRTP.
|
||||
size_t max_length = 224;
|
||||
|
||||
size_t padding_bytes_in_packet = max_length;
|
||||
if (bytes < max_length) {
|
||||
padding_bytes_in_packet = (bytes + 16) & 0xffe0; // Keep our modulus 32.
|
||||
}
|
||||
// Correct seq num, timestamp and payload type.
|
||||
size_t header_length = BuildRTPheader(buffer, timestamp, sequence_number);
|
||||
buffer[0] |= 0x20; // Set padding bit.
|
||||
int32_t* data = reinterpret_cast<int32_t*>(&(buffer[header_length]));
|
||||
|
||||
// Fill data buffer with random data.
|
||||
for (size_t j = 0; j < (padding_bytes_in_packet >> 2); j++) {
|
||||
data[j] = rand(); // NOLINT
|
||||
}
|
||||
// Set number of padding bytes in the last byte of the packet.
|
||||
buffer[header_length + padding_bytes_in_packet - 1] =
|
||||
padding_bytes_in_packet;
|
||||
return padding_bytes_in_packet + header_length;
|
||||
}
|
||||
|
||||
uint8_t video_frame_[65000];
|
||||
size_t payload_data_length_;
|
||||
SimulatedClock fake_clock_;
|
||||
RateLimiter retransmission_rate_limiter_;
|
||||
std::unique_ptr<ReceiveStatistics> receive_statistics_;
|
||||
RTPPayloadRegistry rtp_payload_registry_;
|
||||
TestRtpReceiver receiver_;
|
||||
std::unique_ptr<RtpReceiver> rtp_receiver_;
|
||||
std::unique_ptr<RtpRtcp> video_module_;
|
||||
LoopBackTransport transport_;
|
||||
};
|
||||
|
||||
TEST_F(RtpRtcpVideoTest, BasicVideo) {
|
||||
uint32_t timestamp = 3000;
|
||||
RTPVideoHeader video_header;
|
||||
EXPECT_TRUE(video_module_->SendOutgoingData(
|
||||
kVideoFrameDelta, 123, timestamp, timestamp / 90, video_frame_,
|
||||
payload_data_length_, nullptr, &video_header, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpVideoTest, PaddingOnlyFrames) {
|
||||
const size_t kPadSize = 255;
|
||||
uint8_t padding_packet[kPadSize];
|
||||
uint32_t seq_num = 0;
|
||||
uint32_t timestamp = 3000;
|
||||
VideoCodec codec;
|
||||
codec.codecType = kVideoCodecVP8;
|
||||
codec.plType = kPayloadType;
|
||||
EXPECT_EQ(0, rtp_payload_registry_.RegisterReceivePayload(codec));
|
||||
for (int frame_idx = 0; frame_idx < 10; ++frame_idx) {
|
||||
for (int packet_idx = 0; packet_idx < 5; ++packet_idx) {
|
||||
size_t packet_size =
|
||||
PaddingPacket(padding_packet, timestamp, seq_num, kPadSize);
|
||||
++seq_num;
|
||||
RTPHeader header;
|
||||
std::unique_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
|
||||
EXPECT_TRUE(parser->Parse(padding_packet, packet_size, &header));
|
||||
const auto pl =
|
||||
rtp_payload_registry_.PayloadTypeToPayload(header.payloadType);
|
||||
EXPECT_TRUE(pl);
|
||||
const uint8_t* payload = padding_packet + header.headerLength;
|
||||
const size_t payload_length = packet_size - header.headerLength;
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(
|
||||
header, payload, payload_length, pl->typeSpecific));
|
||||
EXPECT_EQ(0u, receiver_.payload_size());
|
||||
EXPECT_EQ(payload_length, receiver_.rtp_header().header.paddingLength);
|
||||
}
|
||||
timestamp += 3000;
|
||||
fake_clock_.AdvanceTimeMilliseconds(33);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
Loading…
x
Reference in New Issue
Block a user