blob: cb689ee424226ff6c958ad312f69fc0ca617e50f [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/source/rtp_payload_registry.h"
#include "webrtc/system_wrappers/interface/trace.h"
namespace webrtc {
RTPPayloadRegistry::RTPPayloadRegistry(
const int32_t id,
RTPPayloadStrategy* rtp_payload_strategy)
: id_(id),
rtp_payload_strategy_(rtp_payload_strategy),
red_payload_type_(-1),
last_received_payload_type_(-1),
last_received_media_payload_type_(-1) {
}
RTPPayloadRegistry::~RTPPayloadRegistry() {
while (!payload_type_map_.empty()) {
ModuleRTPUtility::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.
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_,
"%s invalid payloadtype:%d",
__FUNCTION__, payload_type);
return -1;
default:
break;
}
size_t payload_name_length = strlen(payload_name);
ModuleRTPUtility::PayloadTypeMap::iterator it =
payload_type_map_.find(payload_type);
if (it != payload_type_map_.end()) {
// We already use this payload type.
ModuleRTPUtility::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 &&
ModuleRTPUtility::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;
}
}
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_,
"%s invalid argument payload_type:%d already registered",
__FUNCTION__, payload_type);
return -1;
}
if (rtp_payload_strategy_->CodecsMustBeUnique()) {
DeregisterAudioCodecOrRedTypeRegardlessOfPayloadType(
payload_name, payload_name_length, frequency, channels, rate);
}
ModuleRTPUtility::Payload* payload = NULL;
// Save the RED payload type. Used in both audio and video.
if (ModuleRTPUtility::StringCompare(payload_name, "red", 3)) {
red_payload_type_ = payload_type;
payload = new ModuleRTPUtility::Payload;
payload->audio = false;
payload->name[RTP_PAYLOAD_NAME_SIZE - 1] = 0;
strncpy(payload->name, payload_name, RTP_PAYLOAD_NAME_SIZE - 1);
} else {
*created_new_payload = true;
payload = rtp_payload_strategy_->CreatePayloadType(
payload_name, payload_type, frequency, channels, rate);
}
payload_type_map_[payload_type] = payload;
// 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) {
ModuleRTPUtility::PayloadTypeMap::iterator it =
payload_type_map_.find(payload_type);
if (it == payload_type_map_.end()) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_,
"%s failed to find payload_type:%d",
__FUNCTION__, payload_type);
return -1;
}
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.
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) {
ModuleRTPUtility::PayloadTypeMap::iterator iterator =
payload_type_map_.begin();
for (; iterator != payload_type_map_.end(); ++iterator) {
ModuleRTPUtility::Payload* payload = iterator->second;
size_t name_length = strlen(payload->name);
if (payload_name_length == name_length
&& ModuleRTPUtility::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 (ModuleRTPUtility::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 {
if (payload_type == NULL) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_,
"%s invalid argument", __FUNCTION__);
return -1;
}
size_t payload_name_length = strlen(payload_name);
ModuleRTPUtility::PayloadTypeMap::const_iterator it =
payload_type_map_.begin();
for (; it != payload_type_map_.end(); ++it) {
ModuleRTPUtility::Payload* payload = it->second;
assert(payload);
size_t name_length = strlen(payload->name);
if (payload_name_length == name_length &&
ModuleRTPUtility::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;
}
int32_t RTPPayloadRegistry::PayloadTypeToPayload(
const uint8_t payload_type,
ModuleRTPUtility::Payload*& payload) const {
ModuleRTPUtility::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 -1;
}
payload = it->second;
return 0;
}
bool RTPPayloadRegistry::ReportMediaPayloadType(
uint8_t media_payload_type) {
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 { return true; }
bool PayloadIsCompatible(
const ModuleRTPUtility::Payload& payload,
const uint32_t frequency,
const uint8_t channels,
const uint32_t rate) const {
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(
ModuleRTPUtility::Payload* payload,
const uint32_t rate) const {
payload->typeSpecific.Audio.rate = rate;
}
ModuleRTPUtility::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 {
ModuleRTPUtility::Payload* payload = new ModuleRTPUtility::Payload;
payload->name[RTP_PAYLOAD_NAME_SIZE - 1] = 0;
strncpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE - 1);
payload->typeSpecific.Audio.frequency = frequency;
payload->typeSpecific.Audio.channels = channels;
payload->typeSpecific.Audio.rate = rate;
payload->audio = true;
return payload;
}
};
class RTPPayloadVideoStrategy : public RTPPayloadStrategy {
public:
bool CodecsMustBeUnique() const { return false; }
bool PayloadIsCompatible(
const ModuleRTPUtility::Payload& payload,
const uint32_t frequency,
const uint8_t channels,
const uint32_t rate) const {
return !payload.audio;
}
void UpdatePayloadRate(
ModuleRTPUtility::Payload* payload,
const uint32_t rate) const {
payload->typeSpecific.Video.maxRate = rate;
}
ModuleRTPUtility::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 {
RtpVideoCodecTypes videoType = kRtpGenericVideo;
if (ModuleRTPUtility::StringCompare(payloadName, "VP8", 3)) {
videoType = kRtpVp8Video;
} else if (ModuleRTPUtility::StringCompare(payloadName, "I420", 4)) {
videoType = kRtpGenericVideo;
} else if (ModuleRTPUtility::StringCompare(payloadName, "ULPFEC", 6)) {
videoType = kRtpFecVideo;
} else {
videoType = kRtpGenericVideo;
}
ModuleRTPUtility::Payload* payload = new ModuleRTPUtility::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;
}
};
RTPPayloadStrategy* RTPPayloadStrategy::CreateStrategy(
const bool handling_audio) {
if (handling_audio) {
return new RTPPayloadAudioStrategy();
} else {
return new RTPPayloadVideoStrategy();
}
}
} // namespace webrtc