blob: 283c2846e1bc8fbf70c8d7e90b4ad887412d8040 [file] [log] [blame]
/*
* 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/include/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)
: 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),
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 size_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);
rtc::CritScope cs(&crit_sect_);
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) {
rtc::CritScope cs(&crit_sect_);
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 size_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 size_t channels,
const uint32_t rate,
int8_t* payload_type) const {
assert(payload_type);
size_t payload_name_length = strlen(payload_name);
rtc::CritScope cs(&crit_sect_);
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 {
rtc::CritScope cs(&crit_sect_);
return rtx_;
}
bool RTPPayloadRegistry::IsRtx(const RTPHeader& header) const {
rtc::CritScope cs(&crit_sect_);
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 {
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);
rtc::CritScope cs(&crit_sect_);
if (!rtx_)
return true;
int associated_payload_type;
auto apt_mapping = rtx_payload_type_map_.find(header.payloadType);
if (apt_mapping == rtx_payload_type_map_.end())
return false;
associated_payload_type = apt_mapping->second;
if (red_payload_type_ != -1) {
// Assume red will be used if it's configured.
// This is a workaround for a Chrome sdp bug where rtx is associated
// with the media codec even though media is sent over red.
// TODO(holmer): Remove once the Chrome versions exposing this bug are
// old enough, which should be once Chrome Stable reaches M53 as this
// work-around went into M50.
associated_payload_type = red_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) {
rtc::CritScope cs(&crit_sect_);
ssrc_rtx_ = ssrc;
rtx_ = true;
}
bool RTPPayloadRegistry::GetRtxSsrc(uint32_t* ssrc) const {
rtc::CritScope cs(&crit_sect_);
*ssrc = ssrc_rtx_;
return rtx_;
}
void RTPPayloadRegistry::SetRtxPayloadType(int payload_type,
int associated_payload_type) {
rtc::CritScope cs(&crit_sect_);
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 {
rtc::CritScope cs(&crit_sect_);
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 {
rtc::CritScope cs(&crit_sect_);
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 {
const RtpUtility::Payload* payload = PayloadTypeToPayload(payload_type);
if (!payload) {
return -1;
}
rtc::CritScope cs(&crit_sect_);
return rtp_payload_strategy_->GetPayloadTypeFrequency(*payload);
}
const RtpUtility::Payload* RTPPayloadRegistry::PayloadTypeToPayload(
uint8_t payload_type) const {
rtc::CritScope cs(&crit_sect_);
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 nullptr;
}
return it->second;
}
void RTPPayloadRegistry::SetIncomingPayloadType(const RTPHeader& header) {
rtc::CritScope cs(&crit_sect_);
if (!IsRtxInternal(header))
incoming_payload_type_ = header.payloadType;
}
bool RTPPayloadRegistry::ReportMediaPayloadType(uint8_t media_payload_type) {
rtc::CritScope cs(&crit_sect_);
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 size_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 size_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 override {
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 size_t channels,
const uint32_t rate) const override {
return !payload.audio;
}
void UpdatePayloadRate(RtpUtility::Payload* payload,
const uint32_t rate) const override {}
RtpUtility::Payload* CreatePayloadType(
const char payloadName[RTP_PAYLOAD_NAME_SIZE],
const int8_t payloadType,
const uint32_t frequency,
const size_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->audio = false;
return payload;
}
int GetPayloadTypeFrequency(
const RtpUtility::Payload& payload) const override {
return kVideoPayloadTypeFrequency;
}
};
RTPPayloadStrategy* RTPPayloadStrategy::CreateStrategy(
const bool handling_audio) {
if (handling_audio) {
return new RTPPayloadAudioStrategy();
} else {
return new RTPPayloadVideoStrategy();
}
}
} // namespace webrtc