// Copyright 2014 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/net/rtcp/rtcp_utility.h"

#include <stdint.h>

#include <cmath>

#include "base/logging.h"
#include "media/cast/net/cast_transport_defines.h"

namespace media {
namespace cast {

namespace {

// January 1970, in NTP seconds.
// Network Time Protocol (NTP), which is in seconds relative to 0h UTC on
// 1 January 1900.
const int64_t kUnixEpochInNtpSeconds = INT64_C(2208988800);

// Magic fractional unit. Used to convert time (in microseconds) to/from
// fractional NTP seconds.
const double kMagicFractionalUnit = 4.294967296E3;

}  // namespace

RtcpParser::RtcpParser(uint32_t local_ssrc, uint32_t remote_ssrc)
    : local_ssrc_(local_ssrc),
      remote_ssrc_(remote_ssrc),
      has_sender_report_(false),
      has_last_report_(false),
      has_cast_message_(false),
      has_cst2_message_(false),
      has_receiver_reference_time_report_(false),
      has_picture_loss_indicator_(false) {}

RtcpParser::~RtcpParser() {}

void RtcpParser::SetMaxValidFrameId(FrameId frame_id) {
  max_valid_frame_id_ = frame_id;
}

bool RtcpParser::Parse(base::BigEndianReader* reader) {
  // Reset.
  has_sender_report_ = false;
  sender_report_ = RtcpSenderInfo();
  has_last_report_ = false;
  receiver_log_.clear();
  has_cast_message_ = false;
  has_cst2_message_ = false;
  has_receiver_reference_time_report_ = false;
  has_picture_loss_indicator_ = false;

  while (reader->remaining()) {
    RtcpCommonHeader header;
    if (!ParseCommonHeader(reader, &header))
      return false;

    base::StringPiece tmp;
    if (!reader->ReadPiece(&tmp, header.length_in_octets - 4))
      return false;
    base::BigEndianReader chunk(tmp.data(), tmp.size());

    switch (header.PT) {
      case kPacketTypeSenderReport:
        if (!ParseSR(&chunk, header))
          return false;
        break;

      case kPacketTypeReceiverReport:
        if (!ParseRR(&chunk, header))
          return false;
        break;

      case kPacketTypeApplicationDefined:
        if (!ParseApplicationDefined(&chunk, header))
          return false;
        break;

      case kPacketTypePayloadSpecific: {
        if (!ParseFeedbackCommon(&chunk, header))
          return false;
        if (!ParsePli(&chunk, header))
          return false;
        break;
      }

      case kPacketTypeXr:
        if (!ParseExtendedReport(&chunk, header))
          return false;
        break;
    }
  }
  return true;
}

bool RtcpParser::ParseCommonHeader(base::BigEndianReader* reader,
                                   RtcpCommonHeader* parsed_header) {
  //  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.

  uint8_t byte;
  if (!reader->ReadU8(&byte))
    return false;
  parsed_header->V = byte >> 6;
  parsed_header->P = ((byte & 0x20) == 0) ? false : true;

  // Check if RTP version field == 2.
  if (parsed_header->V != 2)
    return false;

  parsed_header->IC = byte & 0x1f;
  if (!reader->ReadU8(&parsed_header->PT))
    return false;

  uint16_t bytes;
  if (!reader->ReadU16(&bytes))
    return false;

  parsed_header->length_in_octets = (static_cast<size_t>(bytes) + 1) * 4;

  if (parsed_header->length_in_octets == 0)
    return false;

  return true;
}

bool RtcpParser::ParseSR(base::BigEndianReader* reader,
                         const RtcpCommonHeader& header) {
  uint32_t sender_ssrc;
  if (!reader->ReadU32(&sender_ssrc))
    return false;

  if (sender_ssrc != remote_ssrc_)
    return true;

  uint32_t truncated_rtp_timestamp;
  uint32_t send_octet_count;
  if (!reader->ReadU32(&sender_report_.ntp_seconds) ||
      !reader->ReadU32(&sender_report_.ntp_fraction) ||
      !reader->ReadU32(&truncated_rtp_timestamp) ||
      !reader->ReadU32(&sender_report_.send_packet_count) ||
      !reader->ReadU32(&send_octet_count))
    return false;
  sender_report_.rtp_timestamp = last_parsed_sr_rtp_timestamp_ =
      last_parsed_sr_rtp_timestamp_.Expand(truncated_rtp_timestamp);
  sender_report_.send_octet_count = send_octet_count;
  has_sender_report_ = true;

  for (size_t block = 0; block < header.IC; block++)
    if (!ParseReportBlock(reader))
      return false;

  return true;
}

bool RtcpParser::ParseRR(base::BigEndianReader* reader,
                         const RtcpCommonHeader& header) {
  uint32_t receiver_ssrc;
  if (!reader->ReadU32(&receiver_ssrc))
    return false;

  if (receiver_ssrc != remote_ssrc_)
    return true;

  for (size_t block = 0; block < header.IC; block++)
    if (!ParseReportBlock(reader))
      return false;

  return true;
}

bool RtcpParser::ParseReportBlock(base::BigEndianReader* reader) {
  uint32_t ssrc, last_report, delay;
  if (!reader->ReadU32(&ssrc) ||
      !reader->Skip(12) ||
      !reader->ReadU32(&last_report) ||
      !reader->ReadU32(&delay))
    return false;

  if (ssrc == local_ssrc_) {
    last_report_ = last_report;
    delay_since_last_report_ = delay;
    has_last_report_ = true;
  }

  return true;
}

bool RtcpParser::ParsePli(base::BigEndianReader* reader,
                          const RtcpCommonHeader& header) {
  if (header.IC != 1)
    return true;

  uint32_t receiver_ssrc, sender_ssrc;
  if (!reader->ReadU32(&receiver_ssrc))
    return false;

  // Ignore this Rtcp if the receiver ssrc does not match.
  if (receiver_ssrc != remote_ssrc_)
    return true;

  if (!reader->ReadU32(&sender_ssrc))
    return false;

  // Ignore this Rtcp if the sender ssrc does not match.
  if (sender_ssrc != local_ssrc_)
    return true;

  has_picture_loss_indicator_ = true;
  return true;
}

bool RtcpParser::ParseApplicationDefined(base::BigEndianReader* reader,
                                         const RtcpCommonHeader& header) {
  uint32_t sender_ssrc;
  uint32_t name;
  if (!reader->ReadU32(&sender_ssrc) ||
      !reader->ReadU32(&name))
    return false;

  if (sender_ssrc != remote_ssrc_)
    return true;

  if (name != kCast)
    return false;

  switch (header.IC) {  // subtype
    case kReceiverLogSubtype:
      if (!ParseCastReceiverLogFrameItem(reader))
        return false;
      break;
  }
  return true;
}

bool RtcpParser::ParseCastReceiverLogFrameItem(
    base::BigEndianReader* reader) {

  while (reader->remaining()) {
    uint32_t truncated_rtp_timestamp;
    uint32_t data;
    if (!reader->ReadU32(&truncated_rtp_timestamp) || !reader->ReadU32(&data))
      return false;

    // We have 24 LSB of the event timestamp base on the wire.
    base::TimeTicks event_timestamp_base = base::TimeTicks() +
        base::TimeDelta::FromMilliseconds(data & 0xffffff);

    size_t num_events = 1 + static_cast<uint8_t>(data >> 24);

    const RtpTimeTicks frame_log_rtp_timestamp =
        last_parsed_frame_log_rtp_timestamp_.Expand(truncated_rtp_timestamp);
    RtcpReceiverFrameLogMessage frame_log(frame_log_rtp_timestamp);
    for (size_t event = 0; event < num_events; event++) {
      uint16_t delay_delta_or_packet_id;
      uint16_t event_type_and_timestamp_delta;
      if (!reader->ReadU16(&delay_delta_or_packet_id) ||
          !reader->ReadU16(&event_type_and_timestamp_delta))
        return false;

      RtcpReceiverEventLogMessage event_log;
      event_log.type = TranslateToLogEventFromWireFormat(
          static_cast<uint8_t>(event_type_and_timestamp_delta >> 12));
      event_log.event_timestamp =
          event_timestamp_base +
          base::TimeDelta::FromMilliseconds(
              event_type_and_timestamp_delta & 0xfff);
      if (event_log.type == PACKET_RECEIVED) {
        event_log.packet_id = delay_delta_or_packet_id;
      } else {
        event_log.delay_delta = base::TimeDelta::FromMilliseconds(
            static_cast<int16_t>(delay_delta_or_packet_id));
      }
      frame_log.event_log_messages_.push_back(event_log);
    }

    last_parsed_frame_log_rtp_timestamp_ = frame_log_rtp_timestamp;
    receiver_log_.push_back(frame_log);
  }

  return true;
}

// RFC 4585.
bool RtcpParser::ParseFeedbackCommon(base::BigEndianReader* reader,
                                     const RtcpCommonHeader& header) {
  // The client must provide, up-front, a reference point for expanding the
  // truncated frame ID values.  If missing, it does not intend to process Cast
  // Feedback messages, so just return early.
  if (max_valid_frame_id_.is_null()) {
    return true;
  }

  // See RTC 4585 Section 6.4 for application specific feedback messages.
  if (header.IC != 15) {
    return true;
  }
  uint32_t remote_ssrc;
  uint32_t media_ssrc;
  if (!reader->ReadU32(&remote_ssrc) ||
      !reader->ReadU32(&media_ssrc))
    return false;

  if (remote_ssrc != remote_ssrc_)
    return true;

  uint32_t name;
  if (!reader->ReadU32(&name))
    return false;

  if (name != kCast) {
    return true;
  }

  cast_message_.remote_ssrc = remote_ssrc;

  uint8_t truncated_last_frame_id;
  uint8_t number_of_lost_fields;
  if (!reader->ReadU8(&truncated_last_frame_id) ||
      !reader->ReadU8(&number_of_lost_fields) ||
      !reader->ReadU16(&cast_message_.target_delay_ms))
    return false;

  // TODO(miu): 8 bits is not enough.  If an RTCP packet is received very late
  // (e.g., more than 1.2 seconds late for 100 FPS audio), the frame ID here
  // will be mis-interpreted as a higher-numbered frame than what the packet
  // intends to represent.  This could make the sender's tracking of ACK'ed
  // frames inconsistent.
  cast_message_.ack_frame_id =
      max_valid_frame_id_.ExpandLessThanOrEqual(truncated_last_frame_id);

  cast_message_.missing_frames_and_packets.clear();
  cast_message_.received_later_frames.clear();
  for (size_t i = 0; i < number_of_lost_fields; i++) {
    uint8_t truncated_frame_id;
    uint16_t packet_id;
    uint8_t bitmask;
    if (!reader->ReadU8(&truncated_frame_id) || !reader->ReadU16(&packet_id) ||
        !reader->ReadU8(&bitmask))
      return false;
    const FrameId frame_id =
        cast_message_.ack_frame_id.Expand(truncated_frame_id);
    PacketIdSet& missing_packets =
        cast_message_.missing_frames_and_packets[frame_id];
    missing_packets.insert(packet_id);
    if (packet_id != kRtcpCastAllPacketsLost) {
      while (bitmask) {
        packet_id++;
        if (bitmask & 1)
          missing_packets.insert(packet_id);
        bitmask >>= 1;
      }
    }
  }

  has_cast_message_ = true;

  // Parse the extended feedback (Cst2). Ignore it if any error occurs.
  if ((!reader->ReadU32(&name)) || (name != kCst2))
    return true;
  if (!reader->ReadU8(&cast_message_.feedback_count))
    return true;
  uint8_t number_of_receiving_fields;
  if (!reader->ReadU8(&number_of_receiving_fields))
    return true;
  FrameId starting_frame_id = cast_message_.ack_frame_id + 2;
  for (size_t i = 0; i < number_of_receiving_fields; ++i) {
    uint8_t bitmask;
    if (!reader->ReadU8(&bitmask))
      return true;
    FrameId frame_id = starting_frame_id;
    while (bitmask) {
      if (bitmask & 1)
        cast_message_.received_later_frames.push_back(frame_id);
      ++frame_id;
      bitmask >>= 1;
    }
    starting_frame_id += 8;
  }

  has_cst2_message_ = true;
  return true;
}

bool RtcpParser::ParseExtendedReport(base::BigEndianReader* reader,
                                     const RtcpCommonHeader& header) {
  uint32_t remote_ssrc;
  if (!reader->ReadU32(&remote_ssrc))
    return false;

  // Is it for us?
  if (remote_ssrc != remote_ssrc_)
    return true;

  while (reader->remaining()) {
    uint8_t block_type;
    uint16_t block_length;
    if (!reader->ReadU8(&block_type) ||
        !reader->Skip(1) ||
        !reader->ReadU16(&block_length))
      return false;

    switch (block_type) {
      case 4:  // RRTR. RFC3611 Section 4.4.
        if (block_length != 2)
          return false;
        if (!ParseExtendedReportReceiverReferenceTimeReport(reader,
                                                            remote_ssrc))
          return false;
        break;

      default:
        // Skip unknown item.
        if (!reader->Skip(block_length * 4))
          return false;
    }
  }

  return true;
}

bool RtcpParser::ParseExtendedReportReceiverReferenceTimeReport(
    base::BigEndianReader* reader,
    uint32_t remote_ssrc) {
  receiver_reference_time_report_.remote_ssrc = remote_ssrc;
  if (!reader->ReadU32(&receiver_reference_time_report_.ntp_seconds) ||
      !reader->ReadU32(&receiver_reference_time_report_.ntp_fraction))
    return false;

  has_receiver_reference_time_report_ = true;
  return true;
}

// Converts a log event type to an integer value.
// NOTE: We have only allocated 4 bits to represent the type of event over the
// wire. Therefore, this function can only return values from 0 to 15.
uint8_t ConvertEventTypeToWireFormat(CastLoggingEvent event) {
  switch (event) {
    case FRAME_ACK_SENT:
      return 11;
    case FRAME_PLAYOUT:
      return 12;
    case FRAME_DECODED:
      return 13;
    case PACKET_RECEIVED:
      return 14;
    default:
      return 0;  // Not an interesting event.
  }
}

CastLoggingEvent TranslateToLogEventFromWireFormat(uint8_t event) {
  // TODO(imcheng): Remove the old mappings once they are no longer used.
  switch (event) {
    case 1:  // AudioAckSent
    case 5:  // VideoAckSent
    case 11:  // Unified
      return FRAME_ACK_SENT;
    case 2:  // AudioPlayoutDelay
    case 7:  // VideoRenderDelay
    case 12:  // Unified
      return FRAME_PLAYOUT;
    case 3:  // AudioFrameDecoded
    case 6:  // VideoFrameDecoded
    case 13:  // Unified
      return FRAME_DECODED;
    case 4:  // AudioPacketReceived
    case 8:  // VideoPacketReceived
    case 14:  // Unified
      return PACKET_RECEIVED;
    case 9:  // DuplicateAudioPacketReceived
    case 10:  // DuplicateVideoPacketReceived
    default:
      // If the sender adds new log messages we will end up here until we add
      // the new messages in the receiver.
      VLOG(1) << "Unexpected log message received: " << static_cast<int>(event);
      return UNKNOWN;
  }
}

void ConvertTimeToFractions(int64_t ntp_time_us,
                            uint32_t* seconds,
                            uint32_t* fractions) {
  DCHECK_GE(ntp_time_us, 0) << "Time must NOT be negative";
  const int64_t seconds_component =
      ntp_time_us / base::Time::kMicrosecondsPerSecond;
  // NTP time will overflow in the year 2036.  Also, make sure unit tests don't
  // regress and use an origin past the year 2036.  If this overflows here, the
  // inverse calculation fails to compute the correct TimeTicks value, throwing
  // off the entire system.
  DCHECK_LT(seconds_component, INT64_C(4263431296))
      << "One year left to fix the NTP year 2036 wrap-around issue!";
  *seconds = static_cast<uint32_t>(seconds_component);
  *fractions =
      static_cast<uint32_t>((ntp_time_us % base::Time::kMicrosecondsPerSecond) *
                            kMagicFractionalUnit);
}

void ConvertTimeTicksToNtp(const base::TimeTicks& time,
                           uint32_t* ntp_seconds,
                           uint32_t* ntp_fractions) {
  base::TimeDelta elapsed_since_unix_epoch =
      time - base::TimeTicks::UnixEpoch();

  int64_t ntp_time_us =
      elapsed_since_unix_epoch.InMicroseconds() +
      (kUnixEpochInNtpSeconds * base::Time::kMicrosecondsPerSecond);

  ConvertTimeToFractions(ntp_time_us, ntp_seconds, ntp_fractions);
}

base::TimeTicks ConvertNtpToTimeTicks(uint32_t ntp_seconds,
                                      uint32_t ntp_fractions) {
  // We need to ceil() here because the calculation of |fractions| in
  // ConvertTimeToFractions() effectively does a floor().
  int64_t ntp_time_us =
      ntp_seconds * base::Time::kMicrosecondsPerSecond +
      static_cast<int64_t>(std::ceil(ntp_fractions / kMagicFractionalUnit));

  base::TimeDelta elapsed_since_unix_epoch = base::TimeDelta::FromMicroseconds(
      ntp_time_us -
      (kUnixEpochInNtpSeconds * base::Time::kMicrosecondsPerSecond));
  return base::TimeTicks::UnixEpoch() + elapsed_since_unix_epoch;
}

uint32_t ConvertToNtpDiff(uint32_t delay_seconds, uint32_t delay_fraction) {
  return ((delay_seconds & 0x0000FFFF) << 16) +
         ((delay_fraction & 0xFFFF0000) >> 16);
}

namespace {
enum {
  // Minimum number of bytes required to make a valid RTCP packet.
  kMinLengthOfRtcp = 8,
};
}  // namespace

bool IsRtcpPacket(const uint8_t* packet, size_t length) {
  if (length < kMinLengthOfRtcp) {
    LOG(ERROR) << "Invalid RTCP packet received.";
    return false;
  }

  uint8_t packet_type = packet[1];
  return packet_type >= kPacketTypeLow && packet_type <= kPacketTypeHigh;
}

uint32_t GetSsrcOfSender(const uint8_t* rtcp_buffer, size_t length) {
  if (length < kMinLengthOfRtcp)
    return 0;
  uint32_t ssrc_of_sender;
  base::BigEndianReader big_endian_reader(
      reinterpret_cast<const char*>(rtcp_buffer), length);
  big_endian_reader.Skip(4);  // Skip header.
  big_endian_reader.ReadU32(&ssrc_of_sender);
  return ssrc_of_sender;
}

}  // namespace cast
}  // namespace media
