/* * Copyright (c) 2016 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/rtcp_packet/sdes.h" #include "webrtc/base/logging.h" #include "webrtc/modules/rtp_rtcp/source/byte_io.h" using webrtc::RTCPUtility::PT_SDES; namespace webrtc { namespace rtcp { namespace { void AssignUWord8(uint8_t* buffer, size_t* offset, uint8_t value) { buffer[(*offset)++] = value; } void AssignUWord32(uint8_t* buffer, size_t* offset, uint32_t value) { ByteWriter::WriteBigEndian(buffer + *offset, value); *offset += 4; } // Source Description (SDES) (RFC 3550). // // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // header |V=2|P| SC | PT=SDES=202 | length | // +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // chunk | SSRC/CSRC_1 | // 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | SDES items | // | ... | // +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // chunk | SSRC/CSRC_2 | // 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | SDES items | // | ... | // +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // Canonical End-Point Identifier SDES Item (CNAME) // // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | CNAME=1 | length | user and domain name ... // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ void CreateSdes(const std::vector& chunks, uint8_t* buffer, size_t* pos) { const uint8_t kSdesItemType = 1; for (std::vector::const_iterator it = chunks.begin(); it != chunks.end(); ++it) { AssignUWord32(buffer, pos, (*it).ssrc); AssignUWord8(buffer, pos, kSdesItemType); AssignUWord8(buffer, pos, (*it).name.length()); memcpy(buffer + *pos, (*it).name.data(), (*it).name.length()); *pos += (*it).name.length(); memset(buffer + *pos, 0, (*it).null_octets); *pos += (*it).null_octets; } } } // namespace bool Sdes::Create(uint8_t* packet, size_t* index, size_t max_length, RtcpPacket::PacketReadyCallback* callback) const { assert(!chunks_.empty()); while (*index + BlockLength() > max_length) { if (!OnBufferFull(packet, index, callback)) return false; } CreateHeader(chunks_.size(), PT_SDES, HeaderLength(), packet, index); CreateSdes(chunks_, packet, index); return true; } bool Sdes::WithCName(uint32_t ssrc, const std::string& cname) { assert(cname.length() <= 0xff); if (chunks_.size() >= kMaxNumberOfChunks) { LOG(LS_WARNING) << "Max SDES chunks reached."; return false; } // In each chunk, the list of items must be terminated by one or more null // octets. The next chunk must start on a 32-bit boundary. // CNAME (1 byte) | length (1 byte) | name | padding. int null_octets = 4 - ((2 + cname.length()) % 4); Chunk chunk; chunk.ssrc = ssrc; chunk.name = cname; chunk.null_octets = null_octets; chunks_.push_back(chunk); return true; } size_t Sdes::BlockLength() const { // Header (4 bytes). // Chunk: // SSRC/CSRC (4 bytes) | CNAME (1 byte) | length (1 byte) | name | padding. size_t length = kHeaderLength; for (const Chunk& chunk : chunks_) length += 6 + chunk.name.length() + chunk.null_octets; assert(length % 4 == 0); return length; } } // namespace rtcp } // namespace webrtc