#define MS_CLASS "RTC::RtpParameters"
#include "Logger.hpp"
#include "MediaSoupErrors.hpp"
#include "RTC/RtpDictionaries.hpp"
#include <unordered_set>
namespace RTC
{
std::unordered_map<std::string, RtpParameters::Type> RtpParameters::string2Type =
{
{ "none", RtpParameters::Type::NONE },
{ "simple", RtpParameters::Type::SIMPLE },
{ "simulcast", RtpParameters::Type::SIMULCAST },
{ "svc", RtpParameters::Type::SVC },
{ "pipe", RtpParameters::Type::PIPE }
};
std::map<RtpParameters::Type, std::string> RtpParameters::type2String =
{
{ RtpParameters::Type::NONE, "none" },
{ RtpParameters::Type::SIMPLE, "simple" },
{ RtpParameters::Type::SIMULCAST, "simulcast" },
{ RtpParameters::Type::SVC, "svc" },
{ RtpParameters::Type::PIPE, "pipe" }
};
RtpParameters::Type RtpParameters::GetType(const RtpParameters& rtpParameters)
{
MS_TRACE();
if (rtpParameters.encodings.size() == 1)
{
auto& encoding = rtpParameters.encodings[0];
if (encoding.spatialLayers > 1 || encoding.temporalLayers > 1)
return RtpParameters::Type::SVC;
else
return RtpParameters::Type::SIMPLE;
}
else if (rtpParameters.encodings.size() > 1)
{
return RtpParameters::Type::SIMULCAST;
}
return RtpParameters::Type::NONE;
}
RtpParameters::Type RtpParameters::GetType(std::string& str)
{
MS_TRACE();
auto it = RtpParameters::string2Type.find(str);
if (it == RtpParameters::string2Type.end())
MS_THROW_TYPE_ERROR("invalid RtpParameters type [type:%s]", str.c_str());
return it->second;
}
RtpParameters::Type RtpParameters::GetType(std::string&& str)
{
MS_TRACE();
auto it = RtpParameters::string2Type.find(str);
if (it == RtpParameters::string2Type.end())
MS_THROW_TYPE_ERROR("invalid RtpParameters type [type:%s]", str.c_str());
return it->second;
}
std::string& RtpParameters::GetTypeString(RtpParameters::Type type)
{
MS_TRACE();
return RtpParameters::type2String.at(type);
}
RtpParameters::RtpParameters(json& data)
{
MS_TRACE();
if (!data.is_object())
MS_THROW_TYPE_ERROR("data is not an object");
auto jsonMidIt = data.find("mid");
auto jsonCodecsIt = data.find("codecs");
auto jsonEncodingsIt = data.find("encodings");
auto jsonHeaderExtensionsIt = data.find("headerExtensions");
auto jsonRtcpIt = data.find("rtcp");
if (jsonMidIt != data.end() && jsonMidIt->is_string())
{
this->mid = jsonMidIt->get<std::string>();
if (this->mid.empty())
MS_THROW_TYPE_ERROR("empty mid");
}
if (jsonCodecsIt == data.end() || !jsonCodecsIt->is_array())
MS_THROW_TYPE_ERROR("missing codecs");
this->codecs.reserve(jsonCodecsIt->size());
for (auto& entry : *jsonCodecsIt)
{
this->codecs.emplace_back(entry);
}
if (this->codecs.empty())
MS_THROW_TYPE_ERROR("empty codecs");
if (jsonEncodingsIt == data.end() || !jsonEncodingsIt->is_array())
MS_THROW_TYPE_ERROR("missing encodings");
this->encodings.reserve(jsonEncodingsIt->size());
for (auto& entry : *jsonEncodingsIt)
{
this->encodings.emplace_back(entry);
}
if (this->encodings.empty())
MS_THROW_TYPE_ERROR("empty encodings");
if (jsonHeaderExtensionsIt != data.end() && jsonHeaderExtensionsIt->is_array())
{
this->headerExtensions.reserve(jsonHeaderExtensionsIt->size());
for (auto& entry : *jsonHeaderExtensionsIt)
{
this->headerExtensions.emplace_back(entry);
}
}
if (jsonRtcpIt != data.end() && jsonRtcpIt->is_object())
{
this->rtcp = RTC::RtcpParameters(*jsonRtcpIt);
this->hasRtcp = true;
}
ValidateCodecs();
ValidateEncodings();
}
void RtpParameters::FillJson(json& jsonObject) const
{
MS_TRACE();
if (!this->mid.empty())
jsonObject["mid"] = this->mid;
jsonObject["codecs"] = json::array();
auto jsonCodecsIt = jsonObject.find("codecs");
for (size_t i{ 0 }; i < this->codecs.size(); ++i)
{
jsonCodecsIt->emplace_back(json::value_t::object);
auto& jsonEntry = (*jsonCodecsIt)[i];
auto& codec = this->codecs[i];
codec.FillJson(jsonEntry);
}
jsonObject["encodings"] = json::array();
auto jsonEncodingsIt = jsonObject.find("encodings");
for (size_t i{ 0 }; i < this->encodings.size(); ++i)
{
jsonEncodingsIt->emplace_back(json::value_t::object);
auto& jsonEntry = (*jsonEncodingsIt)[i];
auto& encoding = this->encodings[i];
encoding.FillJson(jsonEntry);
}
jsonObject["headerExtensions"] = json::array();
auto jsonHeaderExtensionsIt = jsonObject.find("headerExtensions");
for (size_t i{ 0 }; i < this->headerExtensions.size(); ++i)
{
jsonHeaderExtensionsIt->emplace_back(json::value_t::object);
auto& jsonEntry = (*jsonHeaderExtensionsIt)[i];
auto& headerExtension = this->headerExtensions[i];
headerExtension.FillJson(jsonEntry);
}
if (this->hasRtcp)
this->rtcp.FillJson(jsonObject["rtcp"]);
else
jsonObject["rtcp"] = json::object();
}
const RTC::RtpCodecParameters* RtpParameters::GetCodecForEncoding(RtpEncodingParameters& encoding) const
{
MS_TRACE();
uint8_t payloadType = encoding.codecPayloadType;
auto it = this->codecs.begin();
for (; it != this->codecs.end(); ++it)
{
auto& codec = *it;
if (codec.payloadType == payloadType)
return std::addressof(codec);
}
if (it == this->codecs.end())
MS_ABORT("no valid codec payload type for the given encoding");
return nullptr;
}
const RTC::RtpCodecParameters* RtpParameters::GetRtxCodecForEncoding(RtpEncodingParameters& encoding) const
{
MS_TRACE();
static const std::string AptString{ "apt" };
uint8_t payloadType = encoding.codecPayloadType;
for (const auto& codec : this->codecs)
{
if (codec.mimeType.IsFeatureCodec() && codec.parameters.GetInteger(AptString) == payloadType)
{
return std::addressof(codec);
}
}
return nullptr;
}
void RtpParameters::ValidateCodecs()
{
MS_TRACE();
static const std::string AptString{ "apt" };
std::unordered_set<uint8_t> payloadTypes;
for (auto& codec : this->codecs)
{
if (payloadTypes.find(codec.payloadType) != payloadTypes.end())
MS_THROW_TYPE_ERROR("duplicated payloadType");
payloadTypes.insert(codec.payloadType);
switch (codec.mimeType.subtype)
{
case RTC::RtpCodecMimeType::Subtype::RTX:
{
int32_t apt = codec.parameters.GetInteger(AptString);
auto it = this->codecs.begin();
for (; it != this->codecs.end(); ++it)
{
auto codec = *it;
if (static_cast<int32_t>(codec.payloadType) == apt)
{
if (codec.mimeType.subtype == RTC::RtpCodecMimeType::Subtype::RTX)
MS_THROW_TYPE_ERROR("apt in RTX codec points to a RTX codec");
else if (codec.mimeType.subtype == RTC::RtpCodecMimeType::Subtype::ULPFEC)
MS_THROW_TYPE_ERROR("apt in RTX codec points to a ULPFEC codec");
else if (codec.mimeType.subtype == RTC::RtpCodecMimeType::Subtype::FLEXFEC)
MS_THROW_TYPE_ERROR("apt in RTX codec points to a FLEXFEC codec");
else
break;
}
}
if (it == this->codecs.end())
MS_THROW_TYPE_ERROR("apt in RTX codec points to a non existing codec");
break;
}
default:;
}
}
}
void RtpParameters::ValidateEncodings()
{
uint8_t firstMediaPayloadType{ 0 };
{
auto it = this->codecs.begin();
for (; it != this->codecs.end(); ++it)
{
auto& codec = *it;
if (codec.mimeType.IsMediaCodec())
{
firstMediaPayloadType = codec.payloadType;
break;
}
}
if (it == this->codecs.end())
MS_THROW_TYPE_ERROR("no media codecs found");
}
for (auto& encoding : this->encodings)
{
if (encoding.spatialLayers > 1 && this->encodings.size() > 1)
{
MS_THROW_TYPE_ERROR(
"cannot use both simulcast and encodings with multiple SVC spatial layers");
}
if (!encoding.hasCodecPayloadType)
{
encoding.codecPayloadType = firstMediaPayloadType;
encoding.hasCodecPayloadType = true;
}
else
{
auto it = this->codecs.begin();
for (; it != this->codecs.end(); ++it)
{
auto codec = *it;
if (codec.payloadType == encoding.codecPayloadType)
{
if (codec.mimeType.IsMediaCodec())
break;
MS_THROW_TYPE_ERROR("invalid codecPayloadType");
}
}
if (it == this->codecs.end())
MS_THROW_TYPE_ERROR("unknown codecPayloadType");
}
}
}
}