/* * 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) : 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 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); 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(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 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); 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::WriteBigEndian(restored_packet + 2, original_sequence_number); ByteWriter::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(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 { const RtpUtility::Payload* payload = PayloadTypeToPayload(payload_type); if (!payload) { return -1; } CriticalSectionScoped cs(crit_sect_.get()); return rtp_payload_strategy_->GetPayloadTypeFrequency(*payload); } const RtpUtility::Payload* RTPPayloadRegistry::PayloadTypeToPayload( uint8_t payload_type) 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 nullptr; } return it->second; } 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 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 { 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 { payload->typeSpecific.Video.maxRate = 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 { 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