blob: 35616b6122a6cba10983cb2044798d71d2ecc467 [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 <algorithm>
#include "webrtc/common_types.h"
#include "webrtc/modules/audio_coding/codecs/audio_format_conversion.h"
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
#include "webrtc/rtc_base/checks.h"
#include "webrtc/rtc_base/logging.h"
#include "webrtc/rtc_base/stringutils.h"
namespace webrtc {
namespace {
bool PayloadIsCompatible(const RtpUtility::Payload& payload,
const CodecInst& audio_codec) {
if (!payload.audio)
return false;
if (_stricmp(payload.name, audio_codec.plname) != 0)
return false;
const AudioPayload& audio_payload = payload.typeSpecific.Audio;
return audio_payload.frequency == static_cast<uint32_t>(audio_codec.plfreq) &&
audio_payload.channels == audio_codec.channels;
}
bool PayloadIsCompatible(const RtpUtility::Payload& payload,
const VideoCodec& video_codec) {
if (payload.audio || _stricmp(payload.name, video_codec.plName) != 0)
return false;
// For H264, profiles must match as well.
if (video_codec.codecType == kVideoCodecH264) {
return video_codec.H264().profile ==
payload.typeSpecific.Video.h264_profile;
}
return true;
}
RtpUtility::Payload CreatePayloadType(const CodecInst& audio_codec) {
RtpUtility::Payload payload;
payload.name[RTP_PAYLOAD_NAME_SIZE - 1] = 0;
strncpy(payload.name, audio_codec.plname, RTP_PAYLOAD_NAME_SIZE - 1);
RTC_DCHECK_GE(audio_codec.plfreq, 1000);
payload.typeSpecific.Audio.frequency = audio_codec.plfreq;
payload.typeSpecific.Audio.channels = audio_codec.channels;
payload.typeSpecific.Audio.rate = 0;
payload.audio = true;
return payload;
}
RtpVideoCodecTypes ConvertToRtpVideoCodecType(VideoCodecType type) {
switch (type) {
case kVideoCodecVP8:
return kRtpVideoVp8;
case kVideoCodecVP9:
return kRtpVideoVp9;
case kVideoCodecH264:
return kRtpVideoH264;
case kVideoCodecRED:
case kVideoCodecULPFEC:
return kRtpVideoNone;
default:
return kRtpVideoGeneric;
}
}
RtpUtility::Payload CreatePayloadType(const VideoCodec& video_codec) {
RtpUtility::Payload payload;
payload.name[RTP_PAYLOAD_NAME_SIZE - 1] = 0;
strncpy(payload.name, video_codec.plName, RTP_PAYLOAD_NAME_SIZE - 1);
payload.typeSpecific.Video.videoCodecType =
ConvertToRtpVideoCodecType(video_codec.codecType);
if (video_codec.codecType == kVideoCodecH264)
payload.typeSpecific.Video.h264_profile = video_codec.H264().profile;
payload.audio = false;
return payload;
}
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.
LOG(LS_ERROR) << "Can't register invalid receiver payload type: "
<< payload_type;
return false;
default:
return true;
}
}
} // namespace
RTPPayloadRegistry::RTPPayloadRegistry()
: incoming_payload_type_(-1),
last_received_payload_type_(-1),
last_received_media_payload_type_(-1),
rtx_(false),
ssrc_rtx_(0) {}
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;
const CodecInst ci = SdpToCodecInst(rtp_payload_type, audio_format);
RTC_DCHECK(IsPayloadTypeValid(rtp_payload_type));
payload_type_map_.insert(
std::make_pair(rtp_payload_type, CreatePayloadType(ci)));
}
// Clear the value of last received payload type since it might mean
// something else now.
last_received_payload_type_ = -1;
last_received_media_payload_type_ = -1;
}
int32_t RTPPayloadRegistry::RegisterReceivePayload(const CodecInst& audio_codec,
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(audio_codec.pltype))
return -1;
auto it = payload_type_map_.find(audio_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, audio_codec)) {
it->second.typeSpecific.Audio.rate = 0;
return 0;
}
LOG(LS_ERROR) << "Payload type already registered: " << audio_codec.pltype;
return -1;
}
// Audio codecs must be unique.
DeregisterAudioCodecOrRedTypeRegardlessOfPayloadType(audio_codec);
payload_type_map_[audio_codec.pltype] = CreatePayloadType(audio_codec);
*created_new_payload = true;
// 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::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;
LOG(LS_ERROR) << "Payload type already registered: "
<< static_cast<int>(video_codec.plType);
return -1;
}
payload_type_map_[video_codec.plType] = CreatePayloadType(video_codec);
// 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_);
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 CodecInst& audio_codec) {
for (auto iterator = payload_type_map_.begin();
iterator != payload_type_map_.end(); ++iterator) {
if (PayloadIsCompatible(iterator->second, audio_codec)) {
// Remove old setting.
payload_type_map_.erase(iterator);
break;
}
}
}
int32_t RTPPayloadRegistry::ReceivePayloadType(const CodecInst& audio_codec,
int8_t* payload_type) const {
assert(payload_type);
rtc::CritScope cs(&crit_sect_);
for (const auto& it : payload_type_map_) {
if (PayloadIsCompatible(it.second, audio_codec)) {
*payload_type = it.first;
return 0;
}
}
return -1;
}
int32_t RTPPayloadRegistry::ReceivePayloadType(const VideoCodec& video_codec,
int8_t* payload_type) const {
assert(payload_type);
rtc::CritScope cs(&crit_sect_);
for (const auto& it : payload_type_map_) {
if (PayloadIsCompatible(it.second, video_codec)) {
*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) {
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;
auto apt_mapping = rtx_payload_type_map_.find(header.payloadType);
if (apt_mapping == rtx_payload_type_map_.end()) {
// No associated payload type found. Warn, unless we have already done so.
if (payload_types_with_suppressed_warnings_.find(header.payloadType) ==
payload_types_with_suppressed_warnings_.end()) {
LOG(LS_WARNING)
<< "No RTX associated payload type mapping was available; "
"not able to restore original packet from RTX packet "
"with payload type: "
<< static_cast<int>(header.payloadType) << ". "
<< "Suppressing further warnings for this payload type.";
payload_types_with_suppressed_warnings_.insert(header.payloadType);
}
return false;
}
restored_packet[1] = static_cast<uint8_t>(apt_mapping->second);
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;
}
bool RTPPayloadRegistry::IsRed(const RTPHeader& header) const {
rtc::CritScope cs(&crit_sect_);
auto it = payload_type_map_.find(header.payloadType);
return it != payload_type_map_.end() && _stricmp(it->second.name, "red") == 0;
}
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_);
auto 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 payload->audio ? payload->typeSpecific.Audio.frequency
: kVideoPayloadTypeFrequency;
}
const RtpUtility::Payload* RTPPayloadRegistry::PayloadTypeToPayload(
uint8_t payload_type) const {
rtc::CritScope cs(&crit_sect_);
auto 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;
}
// Returns -1 if a payload with name |payload_name| is not registered.
int8_t RTPPayloadRegistry::GetPayloadTypeWithName(
const char* payload_name) const {
rtc::CritScope cs(&crit_sect_);
for (const auto& it : payload_type_map_) {
if (_stricmp(it.second.name, payload_name) == 0)
return it.first;
}
return -1;
}
} // namespace webrtc