// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "remoting/protocol/rtp_utils.h" #include "base/logging.h" #include "third_party/libjingle/source/talk/base/byteorder.h" using talk_base::GetBE16; using talk_base::GetBE32; using talk_base::SetBE16; using talk_base::SetBE32; namespace remoting { namespace protocol { namespace { const int kRtpBaseHeaderSize = 12; const uint8 kRtpVersionNumber = 2; const int kRtpMaxSources = 16; const int kBytesPerCSRC = 4; const int kRtcpBaseHeaderSize = 4; const int kRtcpReceiverReportSize = 28; const int kRtcpReceiverReportTotalSize = kRtcpBaseHeaderSize + kRtcpReceiverReportSize; const int kRtcpReceiverReportPacketType = 201; } // namespace RtpHeader::RtpHeader() : padding(false), extension(false), sources(0), marker(false), payload_type(0), sequence_number(0), timestamp(0), sync_source_id(0) { memset(source_id, 0, sizeof(source_id)); } RtcpReceiverReport::RtcpReceiverReport() : receiver_ssrc(0), sender_ssrc(0), loss_fraction(0), total_lost_packets(0), last_sequence_number(0), jitter(0), last_sender_report_timestamp(0), last_sender_report_delay(0) { } static inline uint8 ExtractBits(uint8 byte, int bits_count, int shift) { return (byte >> shift) & ((1 << bits_count) - 1); } // RTP Header format: // // 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 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |V=2|P|X| CC |M| PT | sequence number | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | timestamp | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | synchronization source (SSRC) identifier | // +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // | contributing source (CSRC) identifiers | // | .... | // +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // On the diagram above order of bytes and order of bits within each // byte are big-endian. So bits 0 and 7 are the most and the least // significant bits in the first byte, bit 8 is the most significant // bit in the second byte, etc. int GetRtpHeaderSize(const RtpHeader& header) { DCHECK_GE(header.sources, 0); DCHECK_LT(header.sources, kRtpMaxSources); return kRtpBaseHeaderSize + header.sources * kBytesPerCSRC; } void PackRtpHeader(const RtpHeader& header, uint8* buffer, int buffer_size) { DCHECK_LT(header.sources, kRtpMaxSources); DCHECK_LT(header.payload_type, 1 << 7); CHECK_GE(buffer_size, GetRtpHeaderSize(header)); buffer[0] = (kRtpVersionNumber << 6) | ((uint8)header.padding << 5) | ((uint8)header.extension << 4) | header.sources; buffer[1] = ((uint8)header.marker << 7) | header.payload_type; SetBE16(buffer + 2, header.sequence_number); SetBE32(buffer + 4, header.timestamp); SetBE32(buffer + 8, header.sync_source_id); for (int i = 0; i < header.sources; i++) { SetBE32(buffer + i * 4 + 12, header.source_id[i]); } } int UnpackRtpHeader(const uint8* buffer, int buffer_size, RtpHeader* header) { if (buffer_size < kRtpBaseHeaderSize) { return -1; } int version = ExtractBits(buffer[0], 2, 6); if (version != kRtpVersionNumber) { return -1; } header->padding = ExtractBits(buffer[0], 1, 5) != 0; header->extension = ExtractBits(buffer[0], 1, 4) != 0; header->sources = ExtractBits(buffer[0], 4, 0); header->marker = ExtractBits(buffer[1], 1, 7) != 0; header->payload_type = ExtractBits(buffer[1], 7, 0); header->sequence_number = GetBE16(buffer + 2); header->timestamp = GetBE32(buffer + 4); header->sync_source_id = GetBE32(buffer + 8); DCHECK_LT(header->sources, 16); if (buffer_size < GetRtpHeaderSize(*header)) { return -1; } for (int i = 0; i < header->sources; i++) { header->source_id[i] = GetBE32(buffer + i * 4 + 12); } return GetRtpHeaderSize(*header); } // RTCP receiver report: // // 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 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |V=2|P| RC=1 | PT=201 | length | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | SSRC of sender | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | SSRC of the reportee | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | fraction lost | cumulative number of packets lost | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | extended highest sequence number received | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | interarrival jitter | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | last SR (LSR) | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | delay since last SR (DLSR) | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ int GetRtcpReceiverReportSize(const RtcpReceiverReport& report) { return kRtcpReceiverReportTotalSize; } void PackRtcpReceiverReport(const RtcpReceiverReport& report, uint8* buffer, int buffer_size) { CHECK_GE(buffer_size, GetRtcpReceiverReportSize(report)); buffer[0] = (kRtpVersionNumber << 6) | 1 /* RC=1 */; buffer[1] = kRtcpReceiverReportPacketType; SetBE16(buffer + 2, kRtcpReceiverReportSize); SetBE32(buffer + 4, report.receiver_ssrc); SetBE32(buffer + 8, report.sender_ssrc); SetBE32(buffer + 12, report.total_lost_packets & 0xFFFFFF); buffer[12] = report.loss_fraction; SetBE32(buffer + 16, report.last_sequence_number); SetBE32(buffer + 20, report.jitter); SetBE32(buffer + 24, report.last_sender_report_timestamp); SetBE32(buffer + 28, report.last_sender_report_delay); } int UnpackRtcpReceiverReport(const uint8* buffer, int buffer_size, RtcpReceiverReport* report) { if (buffer_size < kRtcpReceiverReportTotalSize) { return -1; } int version = ExtractBits(buffer[0], 2, 6); if (version != kRtpVersionNumber) { return -1; } int report_count = ExtractBits(buffer[0], 5, 0); if (report_count != 1) { // Received RTCP packet with more than one report. This isn't // supported in the current implementation. return -1; } int packet_type = buffer[1]; if (packet_type != kRtcpReceiverReportPacketType) { // The packet isn't receiver report. return -1; } int report_size = GetBE16(buffer + 2); if (report_size != kRtcpReceiverReportSize) { // Invalid size of the report. return -1; } report->receiver_ssrc = GetBE32(buffer + 4); report->sender_ssrc = GetBE32(buffer + 8); report->loss_fraction = buffer[12]; report->total_lost_packets = GetBE32(buffer + 12) & 0xFFFFFF; report->last_sequence_number = GetBE32(buffer + 16); report->jitter = GetBE32(buffer + 20); report->last_sender_report_timestamp = GetBE32(buffer + 24); report->last_sender_report_delay = GetBE32(buffer + 28); return kRtcpReceiverReportTotalSize; } // VP8 Payload Descriptor format: // // 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 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | RSV |I|N|FI |B| PictureID (integer #bytes) | // +-+-+-+-+-+-+-+-+ | // : : // | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | : (VP8 data or VP8 payload header; byte aligned)| // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // On the diagram above order of bytes and order of bits within each // byte are big-endian. So bits 0 and 7 are the most and the least // significant bits in the first byte, bit 8 is the most significant // bit in the second byte, etc. // // RSV: 3 bits // Bits reserved for future use. MUST be equal to zero and MUST be // ignored by the receiver. // // I: 1 bit // PictureID present. When set to one, a PictureID is provided after // the first byte of the payload descriptor. When set to zero, the // PictureID is omitted, and the one-byte payload descriptor is // immediately followed by the VP8 payload. // // N: 1 bit // Non-reference frame. When set to one, the frame can be discarded // without affecting any other future or past frames. // // FI: 2 bits // Fragmentation information field. This field contains information // about the fragmentation of VP8 payloads carried in the RTP // packet. The four different values are listed below. // // FI Fragmentation status // 00 The RTP packet contains no fragmented VP8 partitions. The // payload is one or several complete partitions. // 01 The RTP packet contains the first part of a fragmented // partition. The fragment must be placed in its own RTP packet. // 10 The RTP packet contains a fragment that is neither the first nor // the last part of a fragmented partition. The fragment must be // placed in its own RTP packet. // 11 The RTP packet contains the last part of a fragmented // partition. The fragment must be placed in its own RTP packet. // // B: 1 bit // Beginning VP8 frame. When set to 1 this signals that a new VP8 // frame starts in this RTP packet. // // PictureID: Multiple of 8 bits // This is a running index of the frames. The field is present only if // the I bit is equal to one. The most significant bit of each byte is // an extension flag. The 7 following bits carry (parts of) the // PictureID. If the extension flag is one, the PictureID continues in // the next byte. If the extension flag is zero, the 7 remaining bits // are the last (and least significant) bits in the PictureID. The // sender may choose any number of bytes for the PictureID. The // PictureID SHALL start on a random number, and SHALL wrap after // reaching the maximum ID as chosen by the application int GetVp8DescriptorSize(const Vp8Descriptor& descriptor) { if (descriptor.picture_id == kuint32max) return 1; int result = 2; // We need 1 byte per each 7 bits in picture_id. uint32 picture_id = descriptor.picture_id >> 7; while (picture_id > 0) { picture_id >>= 7; ++result; } return result; } void PackVp8Descriptor(const Vp8Descriptor& descriptor, uint8* buffer, int buffer_size) { CHECK_GT(buffer_size, 0); buffer[0] = ((uint8)(descriptor.picture_id != kuint32max) << 4) | ((uint8)descriptor.non_reference_frame << 3) | (descriptor.fragmentation_info << 1) | ((uint8)descriptor.frame_beginning); uint32 picture_id = descriptor.picture_id; if (picture_id == kuint32max) return; int pos = 1; while (picture_id > 0) { CHECK_LT(pos, buffer_size); buffer[pos] = picture_id & 0x7F; picture_id >>= 7; // Set the extension bit if neccessary. if (picture_id > 0) buffer[pos] |= 0x80; ++pos; } } int UnpackVp8Descriptor(const uint8* buffer, int buffer_size, Vp8Descriptor* descriptor) { if (buffer_size <= 0) return -1; bool picture_id_present = ExtractBits(buffer[0], 1, 4) != 0; descriptor->non_reference_frame = ExtractBits(buffer[0], 1, 3) != 0; descriptor->fragmentation_info = ExtractBits(buffer[0], 2, 1); descriptor->frame_beginning = ExtractBits(buffer[0], 1, 0) != 0; // Return here if we don't need to decode PictureID. if (!picture_id_present) { descriptor->picture_id = kuint32max; return 1; } // Decode PictureID. bool extension = true; int pos = 1; descriptor->picture_id = 0; while (extension) { if (pos >= buffer_size) return -1; descriptor->picture_id |= buffer[pos] & 0x7F; extension = (buffer[pos] & 0x80) != 0; pos += 1; } return pos; } } // namespace protocol } // namespace remoting