blob: 9a569640984a767e155f5656e72f2eaa714950d8 [file] [log] [blame]
// 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