diff options
Diffstat (limited to 'third_party/libwebrtc/webrtc/media/base/rtputils.cc')
-rw-r--r-- | third_party/libwebrtc/webrtc/media/base/rtputils.cc | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/third_party/libwebrtc/webrtc/media/base/rtputils.cc b/third_party/libwebrtc/webrtc/media/base/rtputils.cc new file mode 100644 index 0000000000..d0ba1cf72b --- /dev/null +++ b/third_party/libwebrtc/webrtc/media/base/rtputils.cc @@ -0,0 +1,473 @@ +/* + * Copyright (c) 2011 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 "media/base/rtputils.h" + +// PacketTimeUpdateParams is defined in asyncpacketsocket.h. +// TODO(sergeyu): Find more appropriate place for PacketTimeUpdateParams. +#include "media/base/turnutils.h" +#include "rtc_base/asyncpacketsocket.h" +#include "rtc_base/checks.h" +#include "rtc_base/messagedigest.h" + +namespace cricket { + +static const uint8_t kRtpVersion = 2; +static const size_t kRtpFlagsOffset = 0; +static const size_t kRtpPayloadTypeOffset = 1; +static const size_t kRtpSeqNumOffset = 2; +static const size_t kRtpTimestampOffset = 4; +static const size_t kRtpSsrcOffset = 8; +static const size_t kRtcpPayloadTypeOffset = 1; +static const size_t kRtpExtensionHeaderLen = 4; +static const size_t kAbsSendTimeExtensionLen = 3; +static const size_t kOneByteExtensionHeaderLen = 1; + +namespace { + +// Fake auth tag written by the sender when external authentication is enabled. +// HMAC in packet will be compared against this value before updating packet +// with actual HMAC value. +static const uint8_t kFakeAuthTag[10] = { + 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd +}; + +void UpdateAbsSendTimeExtensionValue(uint8_t* extension_data, + size_t length, + uint64_t time_us) { + // Absolute send time in RTP streams. + // + // The absolute send time is signaled to the receiver in-band using the + // general mechanism for RTP header extensions [RFC5285]. The payload + // of this extension (the transmitted value) is a 24-bit unsigned integer + // containing the sender's current time in seconds as a fixed point number + // with 18 bits fractional part. + // + // The form of the absolute send time extension block: + // + // 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 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | ID | len=2 | absolute send time | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + if (length != kAbsSendTimeExtensionLen) { + RTC_NOTREACHED(); + return; + } + + // Convert microseconds to a 6.18 fixed point value in seconds. + uint32_t send_time = ((time_us << 18) / 1000000) & 0x00FFFFFF; + extension_data[0] = static_cast<uint8_t>(send_time >> 16); + extension_data[1] = static_cast<uint8_t>(send_time >> 8); + extension_data[2] = static_cast<uint8_t>(send_time); +} + +// Assumes |length| is actual packet length + tag length. Updates HMAC at end of +// the RTP packet. +void UpdateRtpAuthTag(uint8_t* rtp, + size_t length, + const rtc::PacketTimeUpdateParams& packet_time_params) { + // If there is no key, return. + if (packet_time_params.srtp_auth_key.empty()) { + return; + } + + size_t tag_length = packet_time_params.srtp_auth_tag_len; + + // ROC (rollover counter) is at the beginning of the auth tag. + const size_t kRocLength = 4; + if (tag_length < kRocLength || tag_length > length) { + RTC_NOTREACHED(); + return; + } + + uint8_t* auth_tag = rtp + (length - tag_length); + + // We should have a fake HMAC value @ auth_tag. + RTC_DCHECK_EQ(0, memcmp(auth_tag, kFakeAuthTag, tag_length)); + + // Copy ROC after end of rtp packet. + memcpy(auth_tag, &packet_time_params.srtp_packet_index, kRocLength); + // Authentication of a RTP packet will have RTP packet + ROC size. + size_t auth_required_length = length - tag_length + kRocLength; + + uint8_t output[64]; + size_t result = rtc::ComputeHmac( + rtc::DIGEST_SHA_1, &packet_time_params.srtp_auth_key[0], + packet_time_params.srtp_auth_key.size(), rtp, + auth_required_length, output, sizeof(output)); + + if (result < tag_length) { + RTC_NOTREACHED(); + return; + } + + // Copy HMAC from output to packet. This is required as auth tag length + // may not be equal to the actual HMAC length. + memcpy(auth_tag, output, tag_length); +} + +} // namespace + +bool GetUint8(const void* data, size_t offset, int* value) { + if (!data || !value) { + return false; + } + *value = *(static_cast<const uint8_t*>(data) + offset); + return true; +} + +bool GetUint16(const void* data, size_t offset, int* value) { + if (!data || !value) { + return false; + } + *value = static_cast<int>( + rtc::GetBE16(static_cast<const uint8_t*>(data) + offset)); + return true; +} + +bool GetUint32(const void* data, size_t offset, uint32_t* value) { + if (!data || !value) { + return false; + } + *value = rtc::GetBE32(static_cast<const uint8_t*>(data) + offset); + return true; +} + +bool SetUint8(void* data, size_t offset, uint8_t value) { + if (!data) { + return false; + } + rtc::Set8(data, offset, value); + return true; +} + +bool SetUint16(void* data, size_t offset, uint16_t value) { + if (!data) { + return false; + } + rtc::SetBE16(static_cast<uint8_t*>(data) + offset, value); + return true; +} + +bool SetUint32(void* data, size_t offset, uint32_t value) { + if (!data) { + return false; + } + rtc::SetBE32(static_cast<uint8_t*>(data) + offset, value); + return true; +} + +bool GetRtpFlags(const void* data, size_t len, int* value) { + if (len < kMinRtpPacketLen) { + return false; + } + return GetUint8(data, kRtpFlagsOffset, value); +} + +bool GetRtpPayloadType(const void* data, size_t len, int* value) { + if (len < kMinRtpPacketLen) { + return false; + } + if (!GetUint8(data, kRtpPayloadTypeOffset, value)) { + return false; + } + *value &= 0x7F; + return true; +} + +bool GetRtpSeqNum(const void* data, size_t len, int* value) { + if (len < kMinRtpPacketLen) { + return false; + } + return GetUint16(data, kRtpSeqNumOffset, value); +} + +bool GetRtpTimestamp(const void* data, size_t len, uint32_t* value) { + if (len < kMinRtpPacketLen) { + return false; + } + return GetUint32(data, kRtpTimestampOffset, value); +} + +bool GetRtpSsrc(const void* data, size_t len, uint32_t* value) { + if (len < kMinRtpPacketLen) { + return false; + } + return GetUint32(data, kRtpSsrcOffset, value); +} + +bool GetRtpHeaderLen(const void* data, size_t len, size_t* value) { + if (!data || len < kMinRtpPacketLen || !value) return false; + const uint8_t* header = static_cast<const uint8_t*>(data); + // Get base header size + length of CSRCs (not counting extension yet). + size_t header_size = kMinRtpPacketLen + (header[0] & 0xF) * sizeof(uint32_t); + if (len < header_size) return false; + // If there's an extension, read and add in the extension size. + if (header[0] & 0x10) { + if (len < header_size + sizeof(uint32_t)) + return false; + header_size += + ((rtc::GetBE16(header + header_size + 2) + 1) * sizeof(uint32_t)); + if (len < header_size) return false; + } + *value = header_size; + return true; +} + +bool GetRtpHeader(const void* data, size_t len, RtpHeader* header) { + return (GetRtpPayloadType(data, len, &(header->payload_type)) && + GetRtpSeqNum(data, len, &(header->seq_num)) && + GetRtpTimestamp(data, len, &(header->timestamp)) && + GetRtpSsrc(data, len, &(header->ssrc))); +} + +bool GetRtcpType(const void* data, size_t len, int* value) { + if (len < kMinRtcpPacketLen) { + return false; + } + return GetUint8(data, kRtcpPayloadTypeOffset, value); +} + +// This method returns SSRC first of RTCP packet, except if packet is SDES. +// TODO(mallinath) - Fully implement RFC 5506. This standard doesn't restrict +// to send non-compound packets only to feedback messages. +bool GetRtcpSsrc(const void* data, size_t len, uint32_t* value) { + // Packet should be at least of 8 bytes, to get SSRC from a RTCP packet. + if (!data || len < kMinRtcpPacketLen + 4 || !value) return false; + int pl_type; + if (!GetRtcpType(data, len, &pl_type)) return false; + // SDES packet parsing is not supported. + if (pl_type == kRtcpTypeSDES) return false; + *value = rtc::GetBE32(static_cast<const uint8_t*>(data) + 4); + return true; +} + +bool SetRtpSsrc(void* data, size_t len, uint32_t value) { + return SetUint32(data, kRtpSsrcOffset, value); +} + +// Assumes version 2, no padding, no extensions, no csrcs. +bool SetRtpHeader(void* data, size_t len, const RtpHeader& header) { + if (!IsValidRtpPayloadType(header.payload_type) || + header.seq_num < 0 || header.seq_num > UINT16_MAX) { + return false; + } + return (SetUint8(data, kRtpFlagsOffset, kRtpVersion << 6) && + SetUint8(data, kRtpPayloadTypeOffset, header.payload_type & 0x7F) && + SetUint16(data, kRtpSeqNumOffset, + static_cast<uint16_t>(header.seq_num)) && + SetUint32(data, kRtpTimestampOffset, header.timestamp) && + SetRtpSsrc(data, len, header.ssrc)); +} + +bool IsRtpPacket(const void* data, size_t len) { + if (len < kMinRtpPacketLen) + return false; + + return (static_cast<const uint8_t*>(data)[0] >> 6) == kRtpVersion; +} + +bool IsValidRtpPayloadType(int payload_type) { + return payload_type >= 0 && payload_type <= 127; +} + +bool IsValidRtpRtcpPacketSize(bool rtcp, size_t size) { + return (rtcp ? size >= kMinRtcpPacketLen : size >= kMinRtpPacketLen) && + size <= kMaxRtpPacketLen; +} + +const char* RtpRtcpStringLiteral(bool rtcp) { + return rtcp ? "RTCP" : "RTP"; +} + +bool ValidateRtpHeader(const uint8_t* rtp, + size_t length, + size_t* header_length) { + if (header_length) { + *header_length = 0; + } + + if (length < kMinRtpPacketLen) { + return false; + } + + size_t cc_count = rtp[0] & 0x0F; + size_t header_length_without_extension = kMinRtpPacketLen + 4 * cc_count; + if (header_length_without_extension > length) { + return false; + } + + // If extension bit is not set, we are done with header processing, as input + // length is verified above. + if (!(rtp[0] & 0x10)) { + if (header_length) + *header_length = header_length_without_extension; + + return true; + } + + rtp += header_length_without_extension; + + if (header_length_without_extension + kRtpExtensionHeaderLen > length) { + return false; + } + + // Getting extension profile length. + // Length is in 32 bit words. + uint16_t extension_length_in_32bits = rtc::GetBE16(rtp + 2); + size_t extension_length = extension_length_in_32bits * 4; + + size_t rtp_header_length = extension_length + + header_length_without_extension + + kRtpExtensionHeaderLen; + + // Verify input length against total header size. + if (rtp_header_length > length) { + return false; + } + + if (header_length) { + *header_length = rtp_header_length; + } + return true; +} + +// ValidateRtpHeader() must be called before this method to make sure, we have +// a sane rtp packet. +bool UpdateRtpAbsSendTimeExtension(uint8_t* rtp, + size_t length, + int extension_id, + uint64_t time_us) { + // 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 | + // | .... | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + // Return if extension bit is not set. + if (!(rtp[0] & 0x10)) { + return true; + } + + size_t cc_count = rtp[0] & 0x0F; + size_t header_length_without_extension = kMinRtpPacketLen + 4 * cc_count; + + rtp += header_length_without_extension; + + // Getting extension profile ID and length. + uint16_t profile_id = rtc::GetBE16(rtp); + // Length is in 32 bit words. + uint16_t extension_length_in_32bits = rtc::GetBE16(rtp + 2); + size_t extension_length = extension_length_in_32bits * 4; + + rtp += kRtpExtensionHeaderLen; // Moving past extension header. + + bool found = false; + // WebRTC is using one byte header extension. + // TODO(mallinath) - Handle two byte header extension. + if (profile_id == 0xBEDE) { // OneByte extension header + // 0 + // 0 1 2 3 4 5 6 7 + // +-+-+-+-+-+-+-+-+ + // | ID |length | + // +-+-+-+-+-+-+-+-+ + + // 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 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | 0xBE | 0xDE | length=3 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | ID | L=0 | data | ID | L=1 | data... + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ...data | 0 (pad) | 0 (pad) | ID | L=3 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | data | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + const uint8_t* extension_start = rtp; + const uint8_t* extension_end = extension_start + extension_length; + + while (rtp < extension_end) { + const int id = (*rtp & 0xF0) >> 4; + const size_t length = (*rtp & 0x0F) + 1; + if (rtp + kOneByteExtensionHeaderLen + length > extension_end) { + return false; + } + // The 4-bit length is the number minus one of data bytes of this header + // extension element following the one-byte header. + if (id == extension_id) { + UpdateAbsSendTimeExtensionValue(rtp + kOneByteExtensionHeaderLen, + length, time_us); + found = true; + break; + } + rtp += kOneByteExtensionHeaderLen + length; + // Counting padding bytes. + while ((rtp < extension_end) && (*rtp == 0)) { + ++rtp; + } + } + } + return found; +} + +bool ApplyPacketOptions(uint8_t* data, + size_t length, + const rtc::PacketTimeUpdateParams& packet_time_params, + uint64_t time_us) { + RTC_DCHECK(data); + RTC_DCHECK(length); + + // if there is no valid |rtp_sendtime_extension_id| and |srtp_auth_key| in + // PacketOptions, nothing to be updated in this packet. + if (packet_time_params.rtp_sendtime_extension_id == -1 && + packet_time_params.srtp_auth_key.empty()) { + return true; + } + + // If there is a srtp auth key present then the packet must be an RTP packet. + // RTP packet may have been wrapped in a TURN Channel Data or TURN send + // indication. + size_t rtp_start_pos; + size_t rtp_length; + if (!UnwrapTurnPacket(data, length, &rtp_start_pos, &rtp_length)) { + RTC_NOTREACHED(); + return false; + } + + // Making sure we have a valid RTP packet at the end. + if (!IsRtpPacket(data + rtp_start_pos, rtp_length) || + !ValidateRtpHeader(data + rtp_start_pos, rtp_length, nullptr)) { + RTC_NOTREACHED(); + return false; + } + + uint8_t* start = data + rtp_start_pos; + // If packet option has non default value (-1) for sendtime extension id, + // then we should parse the rtp packet to update the timestamp. Otherwise + // just calculate HMAC and update packet with it. + if (packet_time_params.rtp_sendtime_extension_id != -1) { + UpdateRtpAbsSendTimeExtension(start, rtp_length, + packet_time_params.rtp_sendtime_extension_id, + time_us); + } + + UpdateRtpAuthTag(start, rtp_length, packet_time_params); + return true; +} + +} // namespace cricket |