/*
 *  Copyright (c) 2012 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 "modules/rtp_rtcp/source/producer_fec.h"

#include "modules/rtp_rtcp/source/forward_error_correction.h"
#include "modules/rtp_rtcp/source/rtp_utility.h"

namespace webrtc {

enum { kREDForFECHeaderLength = 1 };
// This controls the maximum amount of excess overhead (actual - target)
// allowed in order to trigger GenerateFEC(), before |params_.max_fec_frames|
// is reached. Overhead here is defined as relative to number of media packets.
enum { kMaxExcessOverhead = 50 };  // Q8.
// This is the minimum number of media packets required (above some protection
// level) in order to trigger GenerateFEC(), before |params_.max_fec_frames| is
// reached.
enum { kMinimumMediaPackets = 4 };
// Threshold on the received FEC protection level, above which we enforce at
// least |kMinimumMediaPackets| packets for the FEC code. Below this
// threshold |kMinimumMediaPackets| is set to default value of 1.
enum { kHighProtectionThreshold = 80 };  // Corresponds to ~30 overhead, range
// is 0 to 255, where 255 corresponds to 100% overhead (relative to number of
// media packets).

struct RtpPacket {
  WebRtc_UWord16 rtpHeaderLength;
  ForwardErrorCorrection::Packet* pkt;
};

RedPacket::RedPacket(int length)
    : data_(new uint8_t[length]),
      length_(length),
      header_length_(0) {
}

RedPacket::~RedPacket() {
  delete [] data_;
}

void RedPacket::CreateHeader(const uint8_t* rtp_header, int header_length,
                             int red_pl_type, int pl_type) {
  assert(header_length + kREDForFECHeaderLength <= length_);
  memcpy(data_, rtp_header, header_length);
  // Replace payload type.
  data_[1] &= 0x80;
  data_[1] += red_pl_type;
  // Add RED header
  // f-bit always 0
  data_[header_length] = pl_type;
  header_length_ = header_length + kREDForFECHeaderLength;
}

void RedPacket::SetSeqNum(int seq_num) {
  assert(seq_num >= 0 && seq_num < (1<<16));
  ModuleRTPUtility::AssignUWord16ToBuffer(&data_[2], seq_num);
}

void RedPacket::AssignPayload(const uint8_t* payload, int length) {
  assert(header_length_ + length <= length_);
  memcpy(data_ + header_length_, payload, length);
}

void RedPacket::ClearMarkerBit() {
  data_[1] &= 0x7F;
}

uint8_t* RedPacket::data() const {
  return data_;
}

int RedPacket::length() const {
  return length_;
}

ProducerFec::ProducerFec(ForwardErrorCorrection* fec)
    : fec_(fec),
      media_packets_fec_(),
      fec_packets_(),
      num_frames_(0),
      incomplete_frame_(false),
      num_first_partition_(0),
      minimum_media_packets_fec_(1),
      params_(),
      new_params_() {
  memset(&params_, 0, sizeof(params_));
  memset(&new_params_, 0, sizeof(new_params_));
}

ProducerFec::~ProducerFec() {
  DeletePackets();
}

void ProducerFec::SetFecParameters(const FecProtectionParams* params,
                                   int num_first_partition) {
  // Number of first partition packets cannot exceed kMaxMediaPackets
  assert(params->fec_rate >= 0 && params->fec_rate < 256);
  if (num_first_partition >
      static_cast<int>(ForwardErrorCorrection::kMaxMediaPackets)) {
      num_first_partition =
          ForwardErrorCorrection::kMaxMediaPackets;
  }
  // Store the new params and apply them for the next set of FEC packets being
  // produced.
  new_params_ = *params;
  num_first_partition_ = num_first_partition;
  if (params->fec_rate > kHighProtectionThreshold) {
    minimum_media_packets_fec_ = kMinimumMediaPackets;
  } else {
    minimum_media_packets_fec_ = 1;
  }
}

RedPacket* ProducerFec::BuildRedPacket(const uint8_t* data_buffer,
                                       int payload_length,
                                       int rtp_header_length,
                                       int red_pl_type) {
  RedPacket* red_packet = new RedPacket(payload_length +
                                        kREDForFECHeaderLength +
                                        rtp_header_length);
  int pl_type = data_buffer[1] & 0x7f;
  red_packet->CreateHeader(data_buffer, rtp_header_length,
                           red_pl_type, pl_type);
  red_packet->AssignPayload(data_buffer + rtp_header_length, payload_length);
  return red_packet;
}

int ProducerFec::AddRtpPacketAndGenerateFec(const uint8_t* data_buffer,
                                            int payload_length,
                                            int rtp_header_length) {
  assert(fec_packets_.empty());
  if (media_packets_fec_.empty()) {
    params_ = new_params_;
  }
  incomplete_frame_ = true;
  const bool marker_bit = (data_buffer[1] & kRtpMarkerBitMask) ? true : false;
  if (media_packets_fec_.size() < ForwardErrorCorrection::kMaxMediaPackets) {
    // Generic FEC can only protect up to kMaxMediaPackets packets.
    ForwardErrorCorrection::Packet* packet = new ForwardErrorCorrection::Packet;
    packet->length = payload_length + rtp_header_length;
    memcpy(packet->data, data_buffer, packet->length);
    media_packets_fec_.push_back(packet);
  }
  if (marker_bit) {
    ++num_frames_;
    incomplete_frame_ = false;
  }
  // Produce FEC over at most |params_.max_fec_frames| frames, or as soon as:
  // (1) the excess overhead (actual overhead - requested/target overhead) is
  // less than |kMaxExcessOverhead|, and
  // (2) at least |minimum_media_packets_fec_| media packets is reached.
  if (!incomplete_frame_ &&
      (num_frames_ == params_.max_fec_frames ||
          (ExcessOverheadBelowMax() && MinimumMediaPacketsReached()))) {
    assert(num_first_partition_ <=
           static_cast<int>(ForwardErrorCorrection::kMaxMediaPackets));
    int ret = fec_->GenerateFEC(media_packets_fec_,
                                params_.fec_rate,
                                num_first_partition_,
                                params_.use_uep_protection,
                                params_.fec_mask_type,
                                &fec_packets_);
    if (fec_packets_.empty()) {
      num_frames_ = 0;
      DeletePackets();
    }
    return ret;
  }
  return 0;
}

// Returns true if the excess overhead (actual - target) for the FEC is below
// the amount |kMaxExcessOverhead|. This effects the lower protection level
// cases and low number of media packets/frame. The target overhead is given by
// |params_.fec_rate|, and is only achievable in the limit of large number of
// media packets.
bool ProducerFec::ExcessOverheadBelowMax() {
  return ((Overhead() - params_.fec_rate) < kMaxExcessOverhead);
}

// Returns true if the media packet list for the FEC is at least
// |minimum_media_packets_fec_|. This condition tries to capture the effect
// that, for the same amount of protection/overhead, longer codes
// (e.g. (2k,2m) vs (k,m)) are generally more effective at recovering losses.
bool ProducerFec::MinimumMediaPacketsReached() {
  float avg_num_packets_frame = static_cast<float>(media_packets_fec_.size()) /
                                num_frames_;
  if (avg_num_packets_frame < 2.0f) {
  return (static_cast<int>(media_packets_fec_.size()) >=
      minimum_media_packets_fec_);
  } else {
    // For larger rates (more packets/frame), increase the threshold.
    return (static_cast<int>(media_packets_fec_.size()) >=
        minimum_media_packets_fec_ + 1);
  }
}

bool ProducerFec::FecAvailable() const {
  return (fec_packets_.size() > 0);
}

RedPacket* ProducerFec::GetFecPacket(int red_pl_type,
                                     int fec_pl_type,
                                     uint16_t seq_num,
                                     int rtp_header_length) {
  if (fec_packets_.empty())
    return NULL;
  // Build FEC packet. The FEC packets in |fec_packets_| doesn't
  // have RTP headers, so we're reusing the header from the last
  // media packet.
  ForwardErrorCorrection::Packet* packet_to_send = fec_packets_.front();
  ForwardErrorCorrection::Packet* last_media_packet = media_packets_fec_.back();
  RedPacket* return_packet = new RedPacket(packet_to_send->length +
                                           kREDForFECHeaderLength +
                                           rtp_header_length);
  return_packet->CreateHeader(last_media_packet->data,
                              rtp_header_length,
                              red_pl_type,
                              fec_pl_type);
  return_packet->SetSeqNum(seq_num);
  return_packet->ClearMarkerBit();
  return_packet->AssignPayload(packet_to_send->data, packet_to_send->length);
  fec_packets_.pop_front();
  if (fec_packets_.empty()) {
    // Done with all the FEC packets. Reset for next run.
    DeletePackets();
    num_frames_ = 0;
  }
  return return_packet;
}

int ProducerFec::Overhead() const {
  // Overhead is defined as relative to the number of media packets, and not
  // relative to total number of packets. This definition is inhereted from the
  // protection factor produced by video_coding module and how the FEC
  // generation is implemented.
  assert(!media_packets_fec_.empty());
  int num_fec_packets = fec_->GetNumberOfFecPackets(media_packets_fec_.size(),
                                                    params_.fec_rate);
  // Return the overhead in Q8.
  return (num_fec_packets << 8) / media_packets_fec_.size();
}

void ProducerFec::DeletePackets() {
  while (!media_packets_fec_.empty()) {
    delete media_packets_fec_.front();
    media_packets_fec_.pop_front();
  }
  assert(media_packets_fec_.empty());
}

}  // namespace webrtc
