// Copyright 2013 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 "media/cast/rtcp/rtcp_utility.h" #include "base/big_endian.h" #include "base/logging.h" #include "media/cast/transport/cast_transport_defines.h" namespace media { namespace cast { RtcpParser::RtcpParser(const uint8* rtcpData, size_t rtcpDataLength) : rtcp_data_begin_(rtcpData), rtcp_data_end_(rtcpData + rtcpDataLength), valid_packet_(false), rtcp_data_(rtcpData), rtcp_block_end_(NULL), state_(kStateTopLevel), number_of_blocks_(0), field_type_(kRtcpNotValidCode) { memset(&field_, 0, sizeof(field_)); Validate(); } RtcpParser::~RtcpParser() {} RtcpFieldTypes RtcpParser::FieldType() const { return field_type_; } const RtcpField& RtcpParser::Field() const { return field_; } RtcpFieldTypes RtcpParser::Begin() { rtcp_data_ = rtcp_data_begin_; return Iterate(); } RtcpFieldTypes RtcpParser::Iterate() { // Reset packet type field_type_ = kRtcpNotValidCode; if (!IsValid()) return kRtcpNotValidCode; switch (state_) { case kStateTopLevel: IterateTopLevel(); break; case kStateReportBlock: IterateReportBlockItem(); break; case kStateSdes: IterateSdesItem(); break; case kStateBye: IterateByeItem(); break; case kStateApplicationSpecificCastReceiverFrameLog: IterateCastReceiverLogFrame(); break; case kStateApplicationSpecificCastReceiverEventLog: IterateCastReceiverLogEvent(); break; case kStateApplicationSpecificCastSenderLog: IterateCastSenderLog(); break; case kStateExtendedReportBlock: IterateExtendedReportItem(); break; case kStateExtendedReportDelaySinceLastReceiverReport: IterateExtendedReportDelaySinceLastReceiverReportItem(); break; case kStateGenericRtpFeedbackNack: IterateNackItem(); break; case kStatePayloadSpecificRpsi: IterateRpsiItem(); break; case kStatePayloadSpecificFir: IterateFirItem(); break; case kStatePayloadSpecificApplication: IteratePayloadSpecificAppItem(); break; case kStatePayloadSpecificRemb: IteratePayloadSpecificRembItem(); break; case kStatePayloadSpecificCast: IteratePayloadSpecificCastItem(); break; case kStatePayloadSpecificCastNack: IteratePayloadSpecificCastNackItem(); break; } return field_type_; } void RtcpParser::IterateTopLevel() { for (;;) { RtcpCommonHeader header; bool success = RtcpParseCommonHeader(rtcp_data_, rtcp_data_end_, &header); if (!success) return; rtcp_block_end_ = rtcp_data_ + header.length_in_octets; if (rtcp_block_end_ > rtcp_data_end_) return; // Bad block! switch (header.PT) { case transport::kPacketTypeSenderReport: // number of Report blocks number_of_blocks_ = header.IC; ParseSR(); return; case transport::kPacketTypeReceiverReport: // number of Report blocks number_of_blocks_ = header.IC; ParseRR(); return; case transport::kPacketTypeSdes: // number of Sdes blocks number_of_blocks_ = header.IC; if (!ParseSdes()) { break; // Nothing supported found, continue to next block! } return; case transport::kPacketTypeBye: number_of_blocks_ = header.IC; if (!ParseBye()) { // Nothing supported found, continue to next block! break; } return; case transport::kPacketTypeApplicationDefined: if (!ParseApplicationDefined(header.IC)) { // Nothing supported found, continue to next block! break; } return; case transport::kPacketTypeGenericRtpFeedback: // Fall through! case transport::kPacketTypePayloadSpecific: if (!ParseFeedBackCommon(header)) { // Nothing supported found, continue to next block! break; } return; case transport::kPacketTypeXr: if (!ParseExtendedReport()) { break; // Nothing supported found, continue to next block! } return; default: // Not supported! Skip! EndCurrentBlock(); break; } } } void RtcpParser::IterateReportBlockItem() { bool success = ParseReportBlockItem(); if (!success) Iterate(); } void RtcpParser::IterateSdesItem() { bool success = ParseSdesItem(); if (!success) Iterate(); } void RtcpParser::IterateByeItem() { bool success = ParseByeItem(); if (!success) Iterate(); } void RtcpParser::IterateExtendedReportItem() { bool success = ParseExtendedReportItem(); if (!success) Iterate(); } void RtcpParser::IterateExtendedReportDelaySinceLastReceiverReportItem() { bool success = ParseExtendedReportDelaySinceLastReceiverReport(); if (!success) Iterate(); } void RtcpParser::IterateNackItem() { bool success = ParseNackItem(); if (!success) Iterate(); } void RtcpParser::IterateRpsiItem() { bool success = ParseRpsiItem(); if (!success) Iterate(); } void RtcpParser::IterateFirItem() { bool success = ParseFirItem(); if (!success) Iterate(); } void RtcpParser::IteratePayloadSpecificAppItem() { bool success = ParsePayloadSpecificAppItem(); if (!success) Iterate(); } void RtcpParser::IteratePayloadSpecificRembItem() { bool success = ParsePayloadSpecificRembItem(); if (!success) Iterate(); } void RtcpParser::IteratePayloadSpecificCastItem() { bool success = ParsePayloadSpecificCastItem(); if (!success) Iterate(); } void RtcpParser::IteratePayloadSpecificCastNackItem() { bool success = ParsePayloadSpecificCastNackItem(); if (!success) Iterate(); } void RtcpParser::IterateCastReceiverLogFrame() { bool success = ParseCastReceiverLogFrameItem(); if (!success) Iterate(); } void RtcpParser::IterateCastReceiverLogEvent() { bool success = ParseCastReceiverLogEventItem(); if (!success) Iterate(); } void RtcpParser::IterateCastSenderLog() { bool success = ParseCastSenderLogItem(); if (!success) Iterate(); } void RtcpParser::Validate() { if (rtcp_data_ == NULL) return; // NOT VALID RtcpCommonHeader header; bool success = RtcpParseCommonHeader(rtcp_data_begin_, rtcp_data_end_, &header); if (!success) return; // NOT VALID! valid_packet_ = true; } bool RtcpParser::IsValid() const { return valid_packet_; } void RtcpParser::EndCurrentBlock() { rtcp_data_ = rtcp_block_end_; } bool RtcpParser::RtcpParseCommonHeader(const uint8* data_begin, const uint8* data_end, RtcpCommonHeader* parsed_header) const { if (!data_begin || !data_end) return false; // 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| IC | PT | length | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // Common header for all Rtcp packets, 4 octets. if ((data_end - data_begin) < 4) return false; parsed_header->V = data_begin[0] >> 6; parsed_header->P = ((data_begin[0] & 0x20) == 0) ? false : true; parsed_header->IC = data_begin[0] & 0x1f; parsed_header->PT = data_begin[1]; parsed_header->length_in_octets = ((data_begin[2] << 8) + data_begin[3] + 1) * 4; if (parsed_header->length_in_octets == 0) return false; // Check if RTP version field == 2. if (parsed_header->V != 2) return false; return true; } bool RtcpParser::ParseRR() { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 8) return false; field_type_ = kRtcpRrCode; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.Skip(4); // Skip header big_endian_reader.ReadU32(&field_.receiver_report.sender_ssrc); field_.receiver_report.number_of_report_blocks = number_of_blocks_; rtcp_data_ += 8; // State transition state_ = kStateReportBlock; return true; } bool RtcpParser::ParseSR() { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 28) { EndCurrentBlock(); return false; } field_type_ = kRtcpSrCode; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.Skip(4); // Skip header big_endian_reader.ReadU32(&field_.sender_report.sender_ssrc); big_endian_reader.ReadU32(&field_.sender_report.ntp_most_significant); big_endian_reader.ReadU32(&field_.sender_report.ntp_least_significant); big_endian_reader.ReadU32(&field_.sender_report.rtp_timestamp); big_endian_reader.ReadU32(&field_.sender_report.sender_packet_count); big_endian_reader.ReadU32(&field_.sender_report.sender_octet_count); field_.sender_report.number_of_report_blocks = number_of_blocks_; rtcp_data_ += 28; if (number_of_blocks_ != 0) { // State transition. state_ = kStateReportBlock; } else { // Don't go to state report block item if 0 report blocks. state_ = kStateTopLevel; EndCurrentBlock(); } return true; } bool RtcpParser::ParseReportBlockItem() { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 24 || number_of_blocks_ <= 0) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.ReadU32(&field_.report_block_item.ssrc); big_endian_reader.ReadU8(&field_.report_block_item.fraction_lost); uint8 temp_number_of_packets_lost; big_endian_reader.ReadU8(&temp_number_of_packets_lost); field_.report_block_item.cumulative_number_of_packets_lost = temp_number_of_packets_lost << 16; big_endian_reader.ReadU8(&temp_number_of_packets_lost); field_.report_block_item.cumulative_number_of_packets_lost += temp_number_of_packets_lost << 8; big_endian_reader.ReadU8(&temp_number_of_packets_lost); field_.report_block_item.cumulative_number_of_packets_lost += temp_number_of_packets_lost; big_endian_reader.ReadU32( &field_.report_block_item.extended_highest_sequence_number); big_endian_reader.ReadU32(&field_.report_block_item.jitter); big_endian_reader.ReadU32(&field_.report_block_item.last_sender_report); big_endian_reader.ReadU32(&field_.report_block_item.delay_last_sender_report); rtcp_data_ += 24; number_of_blocks_--; field_type_ = kRtcpReportBlockItemCode; return true; } bool RtcpParser::ParseSdes() { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 8) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } rtcp_data_ += 4; // Skip header state_ = kStateSdes; field_type_ = kRtcpSdesCode; return true; } bool RtcpParser::ParseSdesItem() { if (number_of_blocks_ <= 0) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } number_of_blocks_--; // Find c_name item in a Sdes chunk. while (rtcp_data_ < rtcp_block_end_) { ptrdiff_t data_length = rtcp_block_end_ - rtcp_data_; if (data_length < 4) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } uint32 ssrc; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), data_length); big_endian_reader.ReadU32(&ssrc); rtcp_data_ += 4; bool found_c_name = ParseSdesTypes(); if (found_c_name) { field_.c_name.sender_ssrc = ssrc; return true; } } state_ = kStateTopLevel; EndCurrentBlock(); return false; } bool RtcpParser::ParseSdesTypes() { // Only the c_name item is mandatory. RFC 3550 page 46. bool found_c_name = false; ptrdiff_t length = rtcp_block_end_ - rtcp_data_; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); while (big_endian_reader.remaining() > 0) { uint8 tag; big_endian_reader.ReadU8(&tag); if (tag == 0) { // End tag! 4 octet aligned. rtcp_data_ = rtcp_block_end_; return found_c_name; } if (big_endian_reader.remaining() > 0) { uint8 len; big_endian_reader.ReadU8(&len); if (tag == 1) { // c_name. // Sanity check. if (big_endian_reader.remaining() < len) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } int i = 0; for (; i < len; ++i) { uint8 c; big_endian_reader.ReadU8(&c); if ((c < ' ') || (c > '{') || (c == '%') || (c == '\\')) { // Illegal char. state_ = kStateTopLevel; EndCurrentBlock(); return false; } field_.c_name.name[i] = c; } // Make sure we are null terminated. field_.c_name.name[i] = 0; field_type_ = kRtcpSdesChunkCode; found_c_name = true; } else { big_endian_reader.Skip(len); } } } // No end tag found! state_ = kStateTopLevel; EndCurrentBlock(); return false; } bool RtcpParser::ParseBye() { rtcp_data_ += 4; // Skip header. state_ = kStateBye; return ParseByeItem(); } bool RtcpParser::ParseByeItem() { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 4 || number_of_blocks_ == 0) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } field_type_ = kRtcpByeCode; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.ReadU32(&field_.bye.sender_ssrc); rtcp_data_ += 4; // We can have several CSRCs attached. if (length >= 4 * number_of_blocks_) { rtcp_data_ += (number_of_blocks_ - 1) * 4; } number_of_blocks_ = 0; return true; } bool RtcpParser::ParseApplicationDefined(uint8 subtype) { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 16 || !(subtype == kSenderLogSubtype || subtype == kReceiverLogSubtype)) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } uint32 sender_ssrc; uint32 name; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.Skip(4); // Skip header. big_endian_reader.ReadU32(&sender_ssrc); big_endian_reader.ReadU32(&name); if (name != kCast) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } rtcp_data_ += 12; switch (subtype) { case kSenderLogSubtype: state_ = kStateApplicationSpecificCastSenderLog; field_type_ = kRtcpApplicationSpecificCastSenderLogCode; field_.cast_sender_log.sender_ssrc = sender_ssrc; break; case kReceiverLogSubtype: state_ = kStateApplicationSpecificCastReceiverFrameLog; field_type_ = kRtcpApplicationSpecificCastReceiverLogCode; field_.cast_receiver_log.sender_ssrc = sender_ssrc; break; default: NOTREACHED(); } return true; } bool RtcpParser::ParseCastReceiverLogFrameItem() { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 12) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } uint32 rtp_timestamp; uint32 data; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.ReadU32(&rtp_timestamp); big_endian_reader.ReadU32(&data); rtcp_data_ += 8; field_.cast_receiver_log.rtp_timestamp = rtp_timestamp; // We have 24 LSB of the event timestamp base on the wire. field_.cast_receiver_log.event_timestamp_base = data & 0xffffff; number_of_blocks_ = 1 + static_cast(data >> 24); state_ = kStateApplicationSpecificCastReceiverEventLog; field_type_ = kRtcpApplicationSpecificCastReceiverLogFrameCode; return true; } bool RtcpParser::ParseCastReceiverLogEventItem() { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 4) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } if (number_of_blocks_ == 0) { // Continue parsing the next receiver frame event. state_ = kStateApplicationSpecificCastReceiverFrameLog; return false; } number_of_blocks_--; uint16 delay_delta_or_packet_id; uint16 event_type_and_timestamp_delta; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.ReadU16(&delay_delta_or_packet_id); big_endian_reader.ReadU16(&event_type_and_timestamp_delta); rtcp_data_ += 4; field_.cast_receiver_log.event = static_cast(event_type_and_timestamp_delta >> 12); field_.cast_receiver_log.delay_delta_or_packet_id = delay_delta_or_packet_id; field_.cast_receiver_log.event_timestamp_delta = event_type_and_timestamp_delta & 0xfff; field_type_ = kRtcpApplicationSpecificCastReceiverLogEventCode; return true; } bool RtcpParser::ParseCastSenderLogItem() { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 4) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } uint32 data; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.ReadU32(&data); rtcp_data_ += 4; field_.cast_sender_log.status = static_cast(data >> 24); // We have 24 LSB of the RTP timestamp on the wire. field_.cast_sender_log.rtp_timestamp = data & 0xffffff; field_type_ = kRtcpApplicationSpecificCastSenderLogCode; return true; } bool RtcpParser::ParseFeedBackCommon(const RtcpCommonHeader& header) { DCHECK((header.PT == transport::kPacketTypeGenericRtpFeedback) || (header.PT == transport::kPacketTypePayloadSpecific)) << "Invalid state"; ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 12) { // 4 * 3, RFC4585 section 6.1 EndCurrentBlock(); return false; } uint32 sender_ssrc; uint32 media_ssrc; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.Skip(4); // Skip header. big_endian_reader.ReadU32(&sender_ssrc); big_endian_reader.ReadU32(&media_ssrc); rtcp_data_ += 12; if (header.PT == transport::kPacketTypeGenericRtpFeedback) { // Transport layer feedback switch (header.IC) { case 1: // Nack field_type_ = kRtcpGenericRtpFeedbackNackCode; field_.nack.sender_ssrc = sender_ssrc; field_.nack.media_ssrc = media_ssrc; state_ = kStateGenericRtpFeedbackNack; return true; case 2: // Used to be ACK is this code point, which is removed conficts with // http://tools.ietf.org/html/draft-levin-avt-rtcp-burst-00 break; case 3: // Tmmbr break; case 4: // Tmmbn break; case 5: // RFC 6051 RTCP-sender_report-REQ Rapid Synchronisation of RTP Flows // Trigger a new Rtcp sender_report field_type_ = kRtcpGenericRtpFeedbackSrReqCode; // Note: No state transition, sender report REQ is empty! return true; default: break; } EndCurrentBlock(); return false; } else if (header.PT == transport::kPacketTypePayloadSpecific) { // Payload specific feedback switch (header.IC) { case 1: // PLI field_type_ = kRtcpPayloadSpecificPliCode; field_.pli.sender_ssrc = sender_ssrc; field_.pli.media_ssrc = media_ssrc; // Note: No state transition, PLI FCI is empty! return true; case 2: // Sli break; case 3: field_type_ = kRtcpPayloadSpecificRpsiCode; field_.rpsi.sender_ssrc = sender_ssrc; field_.rpsi.media_ssrc = media_ssrc; state_ = kStatePayloadSpecificRpsi; return true; case 4: // fir break; case 15: field_type_ = kRtcpPayloadSpecificAppCode; field_.application_specific.sender_ssrc = sender_ssrc; field_.application_specific.media_ssrc = media_ssrc; state_ = kStatePayloadSpecificApplication; return true; default: break; } EndCurrentBlock(); return false; } else { DCHECK(false) << "Invalid state"; EndCurrentBlock(); return false; } } bool RtcpParser::ParseRpsiItem() { // RFC 4585 6.3.3. Reference Picture Selection Indication (rpsi) /* 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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | PB |0| Payload Type| Native rpsi bit string | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | defined per codec ... | Padding (0) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 4) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } if (length > 2 + kRtcpRpsiDataSize) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } field_type_ = kRtcpPayloadSpecificRpsiCode; uint8 padding_bits; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.ReadU8(&padding_bits); big_endian_reader.ReadU8(&field_.rpsi.payload_type); big_endian_reader.ReadBytes(&field_.rpsi.native_bit_string, length - 2); field_.rpsi.number_of_valid_bits = static_cast(length - 2) * 8 - padding_bits; rtcp_data_ += length; return true; } bool RtcpParser::ParseNackItem() { // RFC 4585 6.2.1. Generic Nack ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 4) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } field_type_ = kRtcpGenericRtpFeedbackNackItemCode; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.ReadU16(&field_.nack_item.packet_id); big_endian_reader.ReadU16(&field_.nack_item.bitmask); rtcp_data_ += 4; return true; } bool RtcpParser::ParsePayloadSpecificAppItem() { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 4) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } uint32 name; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.ReadU32(&name); rtcp_data_ += 4; if (name == kRemb) { field_type_ = kRtcpPayloadSpecificRembCode; state_ = kStatePayloadSpecificRemb; return true; } else if (name == kCast) { field_type_ = kRtcpPayloadSpecificCastCode; state_ = kStatePayloadSpecificCast; return true; } state_ = kStateTopLevel; EndCurrentBlock(); return false; } bool RtcpParser::ParsePayloadSpecificRembItem() { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 4) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.ReadU8(&field_.remb_item.number_of_ssrcs); uint8 byte_1; uint8 byte_2; uint8 byte_3; big_endian_reader.ReadU8(&byte_1); big_endian_reader.ReadU8(&byte_2); big_endian_reader.ReadU8(&byte_3); rtcp_data_ += 4; uint8 br_exp = (byte_1 >> 2) & 0x3F; uint32 br_mantissa = ((byte_1 & 0x03) << 16) + (byte_2 << 8) + byte_3; field_.remb_item.bitrate = (br_mantissa << br_exp); ptrdiff_t length_ssrcs = rtcp_block_end_ - rtcp_data_; if (length_ssrcs < 4 * field_.remb_item.number_of_ssrcs) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } field_type_ = kRtcpPayloadSpecificRembItemCode; for (int i = 0; i < field_.remb_item.number_of_ssrcs; i++) { big_endian_reader.ReadU32(&field_.remb_item.ssrcs[i]); } return true; } bool RtcpParser::ParsePayloadSpecificCastItem() { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 4) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } field_type_ = kRtcpPayloadSpecificCastCode; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.ReadU8(&field_.cast_item.last_frame_id); big_endian_reader.ReadU8(&field_.cast_item.number_of_lost_fields); big_endian_reader.ReadU16(&field_.cast_item.target_delay_ms); rtcp_data_ += 4; if (field_.cast_item.number_of_lost_fields != 0) { // State transition state_ = kStatePayloadSpecificCastNack; } else { // Don't go to state cast nack item if got 0 fields. state_ = kStateTopLevel; EndCurrentBlock(); } return true; } bool RtcpParser::ParsePayloadSpecificCastNackItem() { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 4) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } field_type_ = kRtcpPayloadSpecificCastNackItemCode; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.ReadU8(&field_.cast_nack_item.frame_id); big_endian_reader.ReadU16(&field_.cast_nack_item.packet_id); big_endian_reader.ReadU8(&field_.cast_nack_item.bitmask); rtcp_data_ += 4; return true; } bool RtcpParser::ParseFirItem() { // RFC 5104 4.3.1. Full Intra Request (fir) ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 8) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } field_type_ = kRtcpPayloadSpecificFirItemCode; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.ReadU32(&field_.fir_item.ssrc); big_endian_reader.ReadU8(&field_.fir_item.command_sequence_number); rtcp_data_ += 8; return true; } bool RtcpParser::ParseExtendedReport() { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 8) return false; field_type_ = kRtcpXrCode; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.Skip(4); // Skip header. big_endian_reader.ReadU32(&field_.extended_report.sender_ssrc); rtcp_data_ += 8; state_ = kStateExtendedReportBlock; return true; } bool RtcpParser::ParseExtendedReportItem() { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 4) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } uint8 block_type; uint16 block_length; base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.ReadU8(&block_type); big_endian_reader.Skip(1); // Ignore reserved. big_endian_reader.ReadU16(&block_length); rtcp_data_ += 4; switch (block_type) { case 4: if (block_length != 2) { // Invalid block length. state_ = kStateTopLevel; EndCurrentBlock(); return false; } return ParseExtendedReportReceiverReferenceTimeReport(); case 5: if (block_length % 3 != 0) { // Invalid block length. state_ = kStateTopLevel; EndCurrentBlock(); return false; } if (block_length >= 3) { number_of_blocks_ = block_length / 3; state_ = kStateExtendedReportDelaySinceLastReceiverReport; return ParseExtendedReportDelaySinceLastReceiverReport(); } return true; default: if (length < block_length * 4) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } field_type_ = kRtcpXrUnknownItemCode; rtcp_data_ += block_length * 4; return true; } } bool RtcpParser::ParseExtendedReportReceiverReferenceTimeReport() { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 8) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.ReadU32(&field_.rrtr.ntp_most_significant); big_endian_reader.ReadU32(&field_.rrtr.ntp_least_significant); rtcp_data_ += 8; field_type_ = kRtcpXrRrtrCode; return true; } bool RtcpParser::ParseExtendedReportDelaySinceLastReceiverReport() { ptrdiff_t length = rtcp_block_end_ - rtcp_data_; if (length < 12) { state_ = kStateTopLevel; EndCurrentBlock(); return false; } if (number_of_blocks_ == 0) { // Continue parsing the extended report block. state_ = kStateExtendedReportBlock; return false; } base::BigEndianReader big_endian_reader( reinterpret_cast(rtcp_data_), length); big_endian_reader.ReadU32(&field_.dlrr.receivers_ssrc); big_endian_reader.ReadU32(&field_.dlrr.last_receiver_report); big_endian_reader.ReadU32(&field_.dlrr.delay_last_receiver_report); rtcp_data_ += 12; number_of_blocks_--; field_type_ = kRtcpXrDlrrCode; return true; } } // namespace cast } // namespace media