namespace media {
namespace cast {
-static const size_t kRtpCommonHeaderLength = 12;
-static const size_t kRtpCastHeaderLength = 7;
+static const size_t kRtpHeaderLength = 12;
+static const size_t kCastHeaderLength = 7;
+static const uint8 kRtpExtensionBitMask = 0x10;
+static const uint8 kRtpMarkerBitMask = 0x80;
static const uint8 kCastKeyFrameBitMask = 0x80;
static const uint8 kCastReferenceFrameIdBitMask = 0x40;
-RtpParser::RtpParser(const RtpParserConfig parser_config)
- : parser_config_(parser_config) {}
+RtpParser::RtpParser(uint32 expected_sender_ssrc, uint8 expected_payload_type)
+ : expected_sender_ssrc_(expected_sender_ssrc),
+ expected_payload_type_(expected_payload_type) {}
RtpParser::~RtpParser() {}
bool RtpParser::ParsePacket(const uint8* packet,
size_t length,
- RtpCastHeader* rtp_header) {
- if (length == 0)
+ RtpCastHeader* header,
+ const uint8** payload_data,
+ size_t* payload_size) {
+ DCHECK(packet);
+ DCHECK(header);
+ DCHECK(payload_data);
+ DCHECK(payload_size);
+
+ if (length < (kRtpHeaderLength + kCastHeaderLength))
return false;
- // Get RTP general header.
- if (!ParseCommon(packet, length, rtp_header))
- return false;
- if (rtp_header->webrtc.header.payloadType == parser_config_.payload_type &&
- rtp_header->webrtc.header.ssrc == parser_config_.ssrc) {
- return ParseCast(packet + kRtpCommonHeaderLength,
- length - kRtpCommonHeaderLength,
- rtp_header);
- }
- // Not a valid payload type / ssrc combination.
- return false;
-}
-bool RtpParser::ParseCommon(const uint8* packet,
- size_t length,
- RtpCastHeader* rtp_header) {
- if (length < kRtpCommonHeaderLength)
+ base::BigEndianReader reader(reinterpret_cast<const char*>(packet), length);
+
+ // Parse the RTP header. See
+ // http://en.wikipedia.org/wiki/Real-time_Transport_Protocol for an
+ // explanation of the standard RTP packet header.
+ uint8 bits;
+ if (!reader.ReadU8(&bits))
return false;
- uint8 version = packet[0] >> 6;
+ const uint8 version = bits >> 6;
if (version != 2)
return false;
- uint8 cc = packet[0] & 0x0f;
- bool marker = ((packet[1] & 0x80) != 0);
- int payload_type = packet[1] & 0x7f;
-
- uint16 sequence_number;
- uint32 rtp_timestamp, ssrc;
- base::BigEndianReader big_endian_reader(
- reinterpret_cast<const char*>(packet + 2), 10);
- big_endian_reader.ReadU16(&sequence_number);
- big_endian_reader.ReadU32(&rtp_timestamp);
- big_endian_reader.ReadU32(&ssrc);
-
- if (ssrc != parser_config_.ssrc)
+ if (bits & kRtpExtensionBitMask)
+ return false; // We lack the implementation to skip over an extension.
+ if (!reader.ReadU8(&bits))
return false;
-
- rtp_header->webrtc.header.markerBit = marker;
- rtp_header->webrtc.header.payloadType = payload_type;
- rtp_header->webrtc.header.sequenceNumber = sequence_number;
- rtp_header->webrtc.header.timestamp = rtp_timestamp;
- rtp_header->webrtc.header.ssrc = ssrc;
- rtp_header->webrtc.header.numCSRCs = cc;
-
- uint8 csrc_octs = cc * 4;
- rtp_header->webrtc.type.Audio.numEnergy = rtp_header->webrtc.header.numCSRCs;
- rtp_header->webrtc.header.headerLength = kRtpCommonHeaderLength + csrc_octs;
- rtp_header->webrtc.type.Audio.isCNG = false;
- rtp_header->webrtc.type.Audio.channel = parser_config_.audio_channels;
- // TODO(pwestin): look at x bit and skip data.
- return true;
-}
-
-bool RtpParser::ParseCast(const uint8* packet,
- size_t length,
- RtpCastHeader* rtp_header) {
- if (length < kRtpCastHeaderLength)
+ header->marker = !!(bits & kRtpMarkerBitMask);
+ header->payload_type = bits & ~kRtpMarkerBitMask;
+ if (header->payload_type != expected_payload_type_)
+ return false; // Punt: Unexpected payload type.
+ if (!reader.ReadU16(&header->sequence_number) ||
+ !reader.ReadU32(&header->rtp_timestamp) ||
+ !reader.ReadU32(&header->sender_ssrc)) {
return false;
-
- // Extract header.
- const uint8* data_ptr = packet;
- size_t data_length = length;
- rtp_header->is_key_frame = (data_ptr[0] & kCastKeyFrameBitMask);
- rtp_header->is_reference = (data_ptr[0] & kCastReferenceFrameIdBitMask);
- rtp_header->frame_id = frame_id_wrap_helper_.MapTo32bitsFrameId(data_ptr[1]);
-
- base::BigEndianReader big_endian_reader(
- reinterpret_cast<const char*>(data_ptr + 2), 4);
- big_endian_reader.ReadU16(&rtp_header->packet_id);
- big_endian_reader.ReadU16(&rtp_header->max_packet_id);
-
- if (rtp_header->is_reference) {
- rtp_header->reference_frame_id =
- reference_frame_id_wrap_helper_.MapTo32bitsFrameId(data_ptr[6]);
- data_ptr += kRtpCastHeaderLength;
- data_length -= kRtpCastHeaderLength;
- } else {
- data_ptr += kRtpCastHeaderLength - 1;
- data_length -= kRtpCastHeaderLength - 1;
}
+ if (header->sender_ssrc != expected_sender_ssrc_)
+ return false; // Punt: Sender's SSRC does not match the expected one.
- if (rtp_header->max_packet_id < rtp_header->packet_id)
+ // Parse the Cast header. Note that, from the RTP protocol's perspective, the
+ // Cast header is part of the payload (and not meant to be an extension
+ // header).
+ if (!reader.ReadU8(&bits))
+ return false;
+ header->is_key_frame = !!(bits & kCastKeyFrameBitMask);
+ const bool includes_specific_frame_reference =
+ !!(bits & kCastReferenceFrameIdBitMask);
+ uint8 truncated_frame_id;
+ if (!reader.ReadU8(&truncated_frame_id) ||
+ !reader.ReadU16(&header->packet_id) ||
+ !reader.ReadU16(&header->max_packet_id)) {
return false;
+ }
+ // Sanity-check: Do the packet ID values make sense w.r.t. each other?
+ if (header->max_packet_id < header->packet_id)
+ return false;
+ uint8 truncated_reference_frame_id;
+ if (!includes_specific_frame_reference) {
+ // By default, a key frame only references itself; and non-key frames
+ // reference their direct predecessor.
+ truncated_reference_frame_id = truncated_frame_id;
+ if (!header->is_key_frame)
+ --truncated_reference_frame_id;
+ } else if (!reader.ReadU8(&truncated_reference_frame_id)) {
+ return false;
+ }
+
+ // Only the lower 8 bits of the |frame_id| were serialized, so do some magic
+ // to restore the upper 24 bits.
+ //
+ // Note: The call to |frame_id_wrap_helper_| has side effects, so we must not
+ // call it until we know the entire deserialization will succeed.
+ header->frame_id =
+ frame_id_wrap_helper_.MapTo32bitsFrameId(truncated_frame_id);
+ // When the upper 24 bits are restored to |reference_frame_id|, make sure
+ // |reference_frame_id| will be strictly less than or equal to |frame_id|.
+ if (truncated_reference_frame_id <= truncated_frame_id)
+ header->reference_frame_id = header->frame_id & 0xffffff00;
+ else
+ header->reference_frame_id = (header->frame_id & 0xffffff00) - 0x00000100;
+ header->reference_frame_id |= truncated_reference_frame_id;
+
+ // All remaining data in the packet is the payload.
+ *payload_data = reinterpret_cast<const uint8*>(reader.ptr());
+ *payload_size = reader.remaining();
- OnReceivedPayloadData(data_ptr, data_length, *rtp_header);
return true;
}