aboutsummaryrefslogtreecommitdiff
path: root/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc
diff options
context:
space:
mode:
Diffstat (limited to 'webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc')
-rw-r--r--webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc492
1 files changed, 492 insertions, 0 deletions
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc b/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc
new file mode 100644
index 0000000000..38d1450c23
--- /dev/null
+++ b/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc
@@ -0,0 +1,492 @@
+/*
+ * 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 "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h"
+
+#include "webrtc/base/logging.h"
+#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
+
+namespace webrtc {
+
+RTPPayloadRegistry::RTPPayloadRegistry(RTPPayloadStrategy* rtp_payload_strategy)
+ : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
+ rtp_payload_strategy_(rtp_payload_strategy),
+ red_payload_type_(-1),
+ ulpfec_payload_type_(-1),
+ incoming_payload_type_(-1),
+ last_received_payload_type_(-1),
+ last_received_media_payload_type_(-1),
+ rtx_(false),
+ rtx_payload_type_(-1),
+ use_rtx_payload_mapping_on_restore_(false),
+ ssrc_rtx_(0) {}
+
+RTPPayloadRegistry::~RTPPayloadRegistry() {
+ while (!payload_type_map_.empty()) {
+ RtpUtility::PayloadTypeMap::iterator it = payload_type_map_.begin();
+ delete it->second;
+ payload_type_map_.erase(it);
+ }
+}
+
+int32_t RTPPayloadRegistry::RegisterReceivePayload(
+ const char payload_name[RTP_PAYLOAD_NAME_SIZE],
+ const int8_t payload_type,
+ const uint32_t frequency,
+ const uint8_t channels,
+ const uint32_t rate,
+ bool* created_new_payload) {
+ assert(payload_type >= 0);
+ assert(payload_name);
+ *created_new_payload = false;
+
+ // 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.
+ LOG(LS_ERROR) << "Can't register invalid receiver payload type: "
+ << payload_type;
+ return -1;
+ default:
+ break;
+ }
+
+ size_t payload_name_length = strlen(payload_name);
+
+ CriticalSectionScoped cs(crit_sect_.get());
+
+ RtpUtility::PayloadTypeMap::iterator it =
+ payload_type_map_.find(payload_type);
+
+ if (it != payload_type_map_.end()) {
+ // We already use this payload type.
+ RtpUtility::Payload* payload = it->second;
+
+ assert(payload);
+
+ size_t name_length = strlen(payload->name);
+
+ // Check if it's the same as we already have.
+ // If same, ignore sending an error.
+ if (payload_name_length == name_length &&
+ RtpUtility::StringCompare(
+ payload->name, payload_name, payload_name_length)) {
+ if (rtp_payload_strategy_->PayloadIsCompatible(*payload, frequency,
+ channels, rate)) {
+ rtp_payload_strategy_->UpdatePayloadRate(payload, rate);
+ return 0;
+ }
+ }
+ LOG(LS_ERROR) << "Payload type already registered: "
+ << static_cast<int>(payload_type);
+ return -1;
+ }
+
+ if (rtp_payload_strategy_->CodecsMustBeUnique()) {
+ DeregisterAudioCodecOrRedTypeRegardlessOfPayloadType(
+ payload_name, payload_name_length, frequency, channels, rate);
+ }
+
+ RtpUtility::Payload* payload = rtp_payload_strategy_->CreatePayloadType(
+ payload_name, payload_type, frequency, channels, rate);
+
+ payload_type_map_[payload_type] = payload;
+ *created_new_payload = true;
+
+ if (RtpUtility::StringCompare(payload_name, "red", 3)) {
+ red_payload_type_ = payload_type;
+ } else if (RtpUtility::StringCompare(payload_name, "ulpfec", 6)) {
+ ulpfec_payload_type_ = payload_type;
+ }
+
+ // Successful set of payload type, clear the value of last received payload
+ // type since it might mean something else.
+ last_received_payload_type_ = -1;
+ last_received_media_payload_type_ = -1;
+ return 0;
+}
+
+int32_t RTPPayloadRegistry::DeRegisterReceivePayload(
+ const int8_t payload_type) {
+ CriticalSectionScoped cs(crit_sect_.get());
+ RtpUtility::PayloadTypeMap::iterator it =
+ payload_type_map_.find(payload_type);
+ assert(it != payload_type_map_.end());
+ delete it->second;
+ payload_type_map_.erase(it);
+ 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 char payload_name[RTP_PAYLOAD_NAME_SIZE],
+ const size_t payload_name_length,
+ const uint32_t frequency,
+ const uint8_t channels,
+ const uint32_t rate) {
+ RtpUtility::PayloadTypeMap::iterator iterator = payload_type_map_.begin();
+ for (; iterator != payload_type_map_.end(); ++iterator) {
+ RtpUtility::Payload* payload = iterator->second;
+ size_t name_length = strlen(payload->name);
+
+ if (payload_name_length == name_length &&
+ RtpUtility::StringCompare(
+ payload->name, payload_name, payload_name_length)) {
+ // We found the payload name in the list.
+ // If audio, check frequency and rate.
+ if (payload->audio) {
+ if (rtp_payload_strategy_->PayloadIsCompatible(*payload, frequency,
+ channels, rate)) {
+ // Remove old setting.
+ delete payload;
+ payload_type_map_.erase(iterator);
+ break;
+ }
+ } else if (RtpUtility::StringCompare(payload_name, "red", 3)) {
+ delete payload;
+ payload_type_map_.erase(iterator);
+ break;
+ }
+ }
+ }
+}
+
+int32_t RTPPayloadRegistry::ReceivePayloadType(
+ const char payload_name[RTP_PAYLOAD_NAME_SIZE],
+ const uint32_t frequency,
+ const uint8_t channels,
+ const uint32_t rate,
+ int8_t* payload_type) const {
+ assert(payload_type);
+ size_t payload_name_length = strlen(payload_name);
+
+ CriticalSectionScoped cs(crit_sect_.get());
+
+ RtpUtility::PayloadTypeMap::const_iterator it = payload_type_map_.begin();
+
+ for (; it != payload_type_map_.end(); ++it) {
+ RtpUtility::Payload* payload = it->second;
+ assert(payload);
+
+ size_t name_length = strlen(payload->name);
+ if (payload_name_length == name_length &&
+ RtpUtility::StringCompare(
+ payload->name, payload_name, payload_name_length)) {
+ // Name matches.
+ if (payload->audio) {
+ if (rate == 0) {
+ // [default] audio, check freq and channels.
+ if (payload->typeSpecific.Audio.frequency == frequency &&
+ payload->typeSpecific.Audio.channels == channels) {
+ *payload_type = it->first;
+ return 0;
+ }
+ } else {
+ // Non-default audio, check freq, channels and rate.
+ if (payload->typeSpecific.Audio.frequency == frequency &&
+ payload->typeSpecific.Audio.channels == channels &&
+ payload->typeSpecific.Audio.rate == rate) {
+ // extra rate condition added
+ *payload_type = it->first;
+ return 0;
+ }
+ }
+ } else {
+ // Video.
+ *payload_type = it->first;
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+
+bool RTPPayloadRegistry::RtxEnabled() const {
+ CriticalSectionScoped cs(crit_sect_.get());
+ return rtx_;
+}
+
+bool RTPPayloadRegistry::IsRtx(const RTPHeader& header) const {
+ CriticalSectionScoped cs(crit_sect_.get());
+ return IsRtxInternal(header);
+}
+
+bool RTPPayloadRegistry::IsRtxInternal(const RTPHeader& header) const {
+ return rtx_ && ssrc_rtx_ == header.ssrc;
+}
+
+bool RTPPayloadRegistry::RestoreOriginalPacket(uint8_t** restored_packet,
+ const uint8_t* packet,
+ size_t* packet_length,
+ uint32_t original_ssrc,
+ const RTPHeader& header) const {
+ return RestoreOriginalPacket(*restored_packet, packet, packet_length,
+ original_ssrc, header);
+}
+
+bool RTPPayloadRegistry::RestoreOriginalPacket(uint8_t* restored_packet,
+ const uint8_t* packet,
+ size_t* packet_length,
+ uint32_t original_ssrc,
+ const RTPHeader& header) const {
+ if (kRtxHeaderSize + header.headerLength + header.paddingLength >
+ *packet_length) {
+ return false;
+ }
+ const uint8_t* rtx_header = packet + header.headerLength;
+ uint16_t original_sequence_number = (rtx_header[0] << 8) + rtx_header[1];
+
+ // Copy the packet into the restored packet, except for the RTX header.
+ memcpy(restored_packet, packet, header.headerLength);
+ memcpy(restored_packet + header.headerLength,
+ packet + header.headerLength + kRtxHeaderSize,
+ *packet_length - header.headerLength - kRtxHeaderSize);
+ *packet_length -= kRtxHeaderSize;
+
+ // Replace the SSRC and the sequence number with the originals.
+ ByteWriter<uint16_t>::WriteBigEndian(restored_packet + 2,
+ original_sequence_number);
+ ByteWriter<uint32_t>::WriteBigEndian(restored_packet + 8, original_ssrc);
+
+ CriticalSectionScoped cs(crit_sect_.get());
+ if (!rtx_)
+ return true;
+
+ int associated_payload_type;
+ auto apt_mapping = rtx_payload_type_map_.find(header.payloadType);
+ if (use_rtx_payload_mapping_on_restore_ &&
+ apt_mapping != rtx_payload_type_map_.end()) {
+ associated_payload_type = apt_mapping->second;
+ } else {
+ // In the future, this will be a bug. For now, just assume this RTX packet
+ // matches the last non-RTX payload type we received. There are cases where
+ // this could break, especially where RTX is sent outside of NACKing (e.g.
+ // padding with redundant payloads).
+ if (rtx_payload_type_ == -1 || incoming_payload_type_ == -1) {
+ LOG(LS_WARNING) << "Incorrect RTX configuration, dropping packet.";
+ return false;
+ }
+ associated_payload_type = incoming_payload_type_;
+ }
+
+ restored_packet[1] = static_cast<uint8_t>(associated_payload_type);
+ if (header.markerBit) {
+ restored_packet[1] |= kRtpMarkerBitMask; // Marker bit is set.
+ }
+ return true;
+}
+
+void RTPPayloadRegistry::SetRtxSsrc(uint32_t ssrc) {
+ CriticalSectionScoped cs(crit_sect_.get());
+ ssrc_rtx_ = ssrc;
+ rtx_ = true;
+}
+
+bool RTPPayloadRegistry::GetRtxSsrc(uint32_t* ssrc) const {
+ CriticalSectionScoped cs(crit_sect_.get());
+ *ssrc = ssrc_rtx_;
+ return rtx_;
+}
+
+void RTPPayloadRegistry::SetRtxPayloadType(int payload_type,
+ int associated_payload_type) {
+ CriticalSectionScoped cs(crit_sect_.get());
+ if (payload_type < 0) {
+ LOG(LS_ERROR) << "Invalid RTX payload type: " << payload_type;
+ return;
+ }
+
+ rtx_payload_type_map_[payload_type] = associated_payload_type;
+ rtx_ = true;
+ rtx_payload_type_ = payload_type;
+}
+
+bool RTPPayloadRegistry::IsRed(const RTPHeader& header) const {
+ CriticalSectionScoped cs(crit_sect_.get());
+ return red_payload_type_ == header.payloadType;
+}
+
+bool RTPPayloadRegistry::IsEncapsulated(const RTPHeader& header) const {
+ return IsRed(header) || IsRtx(header);
+}
+
+bool RTPPayloadRegistry::GetPayloadSpecifics(uint8_t payload_type,
+ PayloadUnion* payload) const {
+ CriticalSectionScoped cs(crit_sect_.get());
+ RtpUtility::PayloadTypeMap::const_iterator it =
+ payload_type_map_.find(payload_type);
+
+ // Check that this is a registered payload type.
+ if (it == payload_type_map_.end()) {
+ return false;
+ }
+ *payload = it->second->typeSpecific;
+ return true;
+}
+
+int RTPPayloadRegistry::GetPayloadTypeFrequency(
+ uint8_t payload_type) const {
+ RtpUtility::Payload* payload;
+ if (!PayloadTypeToPayload(payload_type, payload)) {
+ return -1;
+ }
+ CriticalSectionScoped cs(crit_sect_.get());
+ return rtp_payload_strategy_->GetPayloadTypeFrequency(*payload);
+}
+
+bool RTPPayloadRegistry::PayloadTypeToPayload(
+ const uint8_t payload_type,
+ RtpUtility::Payload*& payload) const {
+ CriticalSectionScoped cs(crit_sect_.get());
+
+ RtpUtility::PayloadTypeMap::const_iterator it =
+ payload_type_map_.find(payload_type);
+
+ // Check that this is a registered payload type.
+ if (it == payload_type_map_.end()) {
+ return false;
+ }
+
+ payload = it->second;
+ return true;
+}
+
+void RTPPayloadRegistry::SetIncomingPayloadType(const RTPHeader& header) {
+ CriticalSectionScoped cs(crit_sect_.get());
+ if (!IsRtxInternal(header))
+ incoming_payload_type_ = header.payloadType;
+}
+
+bool RTPPayloadRegistry::ReportMediaPayloadType(uint8_t media_payload_type) {
+ CriticalSectionScoped cs(crit_sect_.get());
+ if (last_received_media_payload_type_ == media_payload_type) {
+ // Media type unchanged.
+ return true;
+ }
+ last_received_media_payload_type_ = media_payload_type;
+ return false;
+}
+
+class RTPPayloadAudioStrategy : public RTPPayloadStrategy {
+ public:
+ bool CodecsMustBeUnique() const override { return true; }
+
+ bool PayloadIsCompatible(const RtpUtility::Payload& payload,
+ const uint32_t frequency,
+ const uint8_t channels,
+ const uint32_t rate) const override {
+ return
+ payload.audio &&
+ payload.typeSpecific.Audio.frequency == frequency &&
+ payload.typeSpecific.Audio.channels == channels &&
+ (payload.typeSpecific.Audio.rate == rate ||
+ payload.typeSpecific.Audio.rate == 0 || rate == 0);
+ }
+
+ void UpdatePayloadRate(RtpUtility::Payload* payload,
+ const uint32_t rate) const override {
+ payload->typeSpecific.Audio.rate = rate;
+ }
+
+ RtpUtility::Payload* CreatePayloadType(
+ const char payloadName[RTP_PAYLOAD_NAME_SIZE],
+ const int8_t payloadType,
+ const uint32_t frequency,
+ const uint8_t channels,
+ const uint32_t rate) const override {
+ RtpUtility::Payload* payload = new RtpUtility::Payload;
+ payload->name[RTP_PAYLOAD_NAME_SIZE - 1] = 0;
+ strncpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE - 1);
+ assert(frequency >= 1000);
+ payload->typeSpecific.Audio.frequency = frequency;
+ payload->typeSpecific.Audio.channels = channels;
+ payload->typeSpecific.Audio.rate = rate;
+ payload->audio = true;
+ return payload;
+ }
+
+ int GetPayloadTypeFrequency(const RtpUtility::Payload& payload) const {
+ return payload.typeSpecific.Audio.frequency;
+ }
+};
+
+class RTPPayloadVideoStrategy : public RTPPayloadStrategy {
+ public:
+ bool CodecsMustBeUnique() const override { return false; }
+
+ bool PayloadIsCompatible(const RtpUtility::Payload& payload,
+ const uint32_t frequency,
+ const uint8_t channels,
+ const uint32_t rate) const override {
+ return !payload.audio;
+ }
+
+ void UpdatePayloadRate(RtpUtility::Payload* payload,
+ const uint32_t rate) const override {
+ payload->typeSpecific.Video.maxRate = rate;
+ }
+
+ RtpUtility::Payload* CreatePayloadType(
+ const char payloadName[RTP_PAYLOAD_NAME_SIZE],
+ const int8_t payloadType,
+ const uint32_t frequency,
+ const uint8_t channels,
+ const uint32_t rate) const override {
+ RtpVideoCodecTypes videoType = kRtpVideoGeneric;
+
+ if (RtpUtility::StringCompare(payloadName, "VP8", 3)) {
+ videoType = kRtpVideoVp8;
+ } else if (RtpUtility::StringCompare(payloadName, "VP9", 3)) {
+ videoType = kRtpVideoVp9;
+ } else if (RtpUtility::StringCompare(payloadName, "H264", 4)) {
+ videoType = kRtpVideoH264;
+ } else if (RtpUtility::StringCompare(payloadName, "I420", 4)) {
+ videoType = kRtpVideoGeneric;
+ } else if (RtpUtility::StringCompare(payloadName, "ULPFEC", 6) ||
+ RtpUtility::StringCompare(payloadName, "RED", 3)) {
+ videoType = kRtpVideoNone;
+ } else {
+ videoType = kRtpVideoGeneric;
+ }
+ RtpUtility::Payload* payload = new RtpUtility::Payload;
+
+ payload->name[RTP_PAYLOAD_NAME_SIZE - 1] = 0;
+ strncpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE - 1);
+ payload->typeSpecific.Video.videoCodecType = videoType;
+ payload->typeSpecific.Video.maxRate = rate;
+ payload->audio = false;
+ return payload;
+ }
+
+ int GetPayloadTypeFrequency(const RtpUtility::Payload& payload) const {
+ return kVideoPayloadTypeFrequency;
+ }
+};
+
+RTPPayloadStrategy* RTPPayloadStrategy::CreateStrategy(
+ const bool handling_audio) {
+ if (handling_audio) {
+ return new RTPPayloadAudioStrategy();
+ } else {
+ return new RTPPayloadVideoStrategy();
+ }
+}
+
+} // namespace webrtc