/*
 *  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 "webrtc/modules/rtp_rtcp/source/rtp_sender_video.h"

#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
#include "webrtc/modules/rtp_rtcp/source/producer_fec.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/logging.h"
#include "webrtc/system_wrappers/interface/trace_event.h"

namespace webrtc {
enum { REDForFECHeaderLength = 1 };

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

RTPSenderVideo::RTPSenderVideo(Clock* clock,
                               RTPSenderInterface* rtpSender)
    : _rtpSender(*rtpSender),
      _sendVideoCritsect(CriticalSectionWrapper::CreateCriticalSection()),
      _videoType(kRtpVideoGeneric),
      _videoCodecInformation(NULL),
      _maxBitrate(0),
      _retransmissionSettings(kRetransmitBaseLayer),

      // Generic FEC
      _fec(),
      _fecEnabled(false),
      _payloadTypeRED(-1),
      _payloadTypeFEC(-1),
      _numberFirstPartition(0),
      delta_fec_params_(),
      key_fec_params_(),
      producer_fec_(&_fec),
      _fecOverheadRate(clock, NULL),
      _videoBitrate(clock, NULL) {
  memset(&delta_fec_params_, 0, sizeof(delta_fec_params_));
  memset(&key_fec_params_, 0, sizeof(key_fec_params_));
  delta_fec_params_.max_fec_frames = key_fec_params_.max_fec_frames = 1;
  delta_fec_params_.fec_mask_type = key_fec_params_.fec_mask_type =
        kFecMaskRandom;
}

RTPSenderVideo::~RTPSenderVideo()
{
    if(_videoCodecInformation)
    {
        delete _videoCodecInformation;
    }
    delete _sendVideoCritsect;
}

void
RTPSenderVideo::SetVideoCodecType(RtpVideoCodecTypes videoType)
{
    CriticalSectionScoped cs(_sendVideoCritsect);
    _videoType = videoType;
}

RtpVideoCodecTypes
RTPSenderVideo::VideoCodecType() const
{
    return _videoType;
}

int32_t RTPSenderVideo::RegisterVideoPayload(
    const char payloadName[RTP_PAYLOAD_NAME_SIZE],
    const int8_t payloadType,
    const uint32_t maxBitRate,
    RtpUtility::Payload*& payload) {
  CriticalSectionScoped cs(_sendVideoCritsect);

  RtpVideoCodecTypes videoType = kRtpVideoGeneric;
  if (RtpUtility::StringCompare(payloadName, "VP8", 3)) {
    videoType = kRtpVideoVp8;
  } else if (RtpUtility::StringCompare(payloadName, "H264", 4)) {
    videoType = kRtpVideoH264;
  } else if (RtpUtility::StringCompare(payloadName, "I420", 4)) {
    videoType = kRtpVideoGeneric;
  } else {
    videoType = kRtpVideoGeneric;
  }
  payload = new RtpUtility::Payload;
  payload->name[RTP_PAYLOAD_NAME_SIZE - 1] = 0;
  strncpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE - 1);
  payload->typeSpecific.Video.videoCodecType = videoType;
  payload->typeSpecific.Video.maxRate = maxBitRate;
  payload->audio = false;
  return 0;
}

int32_t
RTPSenderVideo::SendVideoPacket(uint8_t* data_buffer,
                                const uint16_t payload_length,
                                const uint16_t rtp_header_length,
                                const uint32_t capture_timestamp,
                                int64_t capture_time_ms,
                                StorageType storage,
                                bool protect) {
  if(_fecEnabled) {
    int ret = 0;
    int fec_overhead_sent = 0;
    int video_sent = 0;

    RedPacket* red_packet = producer_fec_.BuildRedPacket(data_buffer,
                                                         payload_length,
                                                         rtp_header_length,
                                                         _payloadTypeRED);
    TRACE_EVENT_INSTANT2("webrtc_rtp", "Video::PacketRed",
                         "timestamp", capture_timestamp,
                         "seqnum", _rtpSender.SequenceNumber());
    // Sending the media packet with RED header.
    int packet_success = _rtpSender.SendToNetwork(
        red_packet->data(),
        red_packet->length() - rtp_header_length,
        rtp_header_length,
        capture_time_ms,
        storage,
        PacedSender::kNormalPriority);

    ret |= packet_success;

    if (packet_success == 0) {
      video_sent += red_packet->length();
    }
    delete red_packet;
    red_packet = NULL;

    if (protect) {
      ret = producer_fec_.AddRtpPacketAndGenerateFec(data_buffer,
                                                     payload_length,
                                                     rtp_header_length);
      if (ret != 0)
        return ret;
    }

    while (producer_fec_.FecAvailable()) {
      red_packet = producer_fec_.GetFecPacket(
          _payloadTypeRED,
          _payloadTypeFEC,
          _rtpSender.IncrementSequenceNumber(),
          rtp_header_length);
      StorageType storage = kDontRetransmit;
      if (_retransmissionSettings & kRetransmitFECPackets) {
        storage = kAllowRetransmission;
      }
      TRACE_EVENT_INSTANT2("webrtc_rtp", "Video::PacketFec",
                           "timestamp", capture_timestamp,
                           "seqnum", _rtpSender.SequenceNumber());
      // Sending FEC packet with RED header.
      int packet_success = _rtpSender.SendToNetwork(
          red_packet->data(),
          red_packet->length() - rtp_header_length,
          rtp_header_length,
          capture_time_ms,
          storage,
          PacedSender::kNormalPriority);

      ret |= packet_success;

      if (packet_success == 0) {
        fec_overhead_sent += red_packet->length();
      }
      delete red_packet;
      red_packet = NULL;
    }
    _videoBitrate.Update(video_sent);
    _fecOverheadRate.Update(fec_overhead_sent);
    return ret;
  }
  TRACE_EVENT_INSTANT2("webrtc_rtp", "Video::PacketNormal",
                       "timestamp", capture_timestamp,
                       "seqnum", _rtpSender.SequenceNumber());
  int ret = _rtpSender.SendToNetwork(data_buffer,
                                     payload_length,
                                     rtp_header_length,
                                     capture_time_ms,
                                     storage,
                                     PacedSender::kNormalPriority);
  if (ret == 0) {
    _videoBitrate.Update(payload_length + rtp_header_length);
  }
  return ret;
}

int32_t
RTPSenderVideo::SendRTPIntraRequest()
{
    // RFC 2032
    // 5.2.1.  Full intra-frame Request (FIR) packet

    uint16_t length = 8;
    uint8_t data[8];
    data[0] = 0x80;
    data[1] = 192;
    data[2] = 0;
    data[3] = 1; // length

    RtpUtility::AssignUWord32ToBuffer(data + 4, _rtpSender.SSRC());

    TRACE_EVENT_INSTANT1("webrtc_rtp",
                         "Video::IntraRequest",
                         "seqnum", _rtpSender.SequenceNumber());
    return _rtpSender.SendToNetwork(data, 0, length, -1, kDontStore,
                                    PacedSender::kNormalPriority);
}

int32_t
RTPSenderVideo::SetGenericFECStatus(const bool enable,
                                    const uint8_t payloadTypeRED,
                                    const uint8_t payloadTypeFEC)
{
    _fecEnabled = enable;
    _payloadTypeRED = payloadTypeRED;
    _payloadTypeFEC = payloadTypeFEC;
    memset(&delta_fec_params_, 0, sizeof(delta_fec_params_));
    memset(&key_fec_params_, 0, sizeof(key_fec_params_));
    delta_fec_params_.max_fec_frames = key_fec_params_.max_fec_frames = 1;
    delta_fec_params_.fec_mask_type = key_fec_params_.fec_mask_type =
          kFecMaskRandom;
    return 0;
}

int32_t
RTPSenderVideo::GenericFECStatus(bool& enable,
                                 uint8_t& payloadTypeRED,
                                 uint8_t& payloadTypeFEC) const
{
    enable = _fecEnabled;
    payloadTypeRED = _payloadTypeRED;
    payloadTypeFEC = _payloadTypeFEC;
    return 0;
}

uint16_t
RTPSenderVideo::FECPacketOverhead() const
{
    if (_fecEnabled)
    {
      // Overhead is FEC headers plus RED for FEC header plus anything in RTP
      // header beyond the 12 bytes base header (CSRC list, extensions...)
      // This reason for the header extensions to be included here is that
      // from an FEC viewpoint, they are part of the payload to be protected.
      // (The base RTP header is already protected by the FEC header.)
      return ForwardErrorCorrection::PacketOverhead() + REDForFECHeaderLength +
             (_rtpSender.RTPHeaderLength() - kRtpHeaderSize);
    }
    return 0;
}

int32_t RTPSenderVideo::SetFecParameters(
    const FecProtectionParams* delta_params,
    const FecProtectionParams* key_params) {
  assert(delta_params);
  assert(key_params);
  delta_fec_params_ = *delta_params;
  key_fec_params_ = *key_params;
  return 0;
}

int32_t RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType,
                                  const FrameType frameType,
                                  const int8_t payloadType,
                                  const uint32_t captureTimeStamp,
                                  int64_t capture_time_ms,
                                  const uint8_t* payloadData,
                                  const uint32_t payloadSize,
                                  const RTPFragmentationHeader* fragmentation,
                                  VideoCodecInformation* codecInfo,
                                  const RTPVideoTypeHeader* rtpTypeHdr) {
  if (payloadSize == 0) {
    return -1;
  }

  if (frameType == kVideoFrameKey) {
    producer_fec_.SetFecParameters(&key_fec_params_, _numberFirstPartition);
  } else {
    producer_fec_.SetFecParameters(&delta_fec_params_, _numberFirstPartition);
  }

  // Default setting for number of first partition packets:
  // Will be extracted in SendVP8 for VP8 codec; other codecs use 0
  _numberFirstPartition = 0;

  switch (videoType) {
    case kRtpVideoGeneric:
      return SendGeneric(frameType,
                         payloadType,
                         captureTimeStamp,
                         capture_time_ms,
                         payloadData,
                         payloadSize);
    case kRtpVideoVp8:
      return SendVP8(frameType,
                     payloadType,
                     captureTimeStamp,
                     capture_time_ms,
                     payloadData,
                     payloadSize,
                     fragmentation,
                     rtpTypeHdr);
    case kRtpVideoH264:
      return SendH264(frameType,
                      payloadType,
                      captureTimeStamp,
                      capture_time_ms,
                      payloadData,
                      payloadSize,
                      fragmentation,
                      rtpTypeHdr)
                 ? 0
                 : -1;
    default:
      assert(false);
      break;
  }
  return 0;
}

int32_t RTPSenderVideo::SendGeneric(const FrameType frame_type,
                                    const int8_t payload_type,
                                    const uint32_t capture_timestamp,
                                    int64_t capture_time_ms,
                                    const uint8_t* payload,
                                    uint32_t size) {
  assert(frame_type == kVideoFrameKey || frame_type == kVideoFrameDelta);
  uint16_t rtp_header_length = _rtpSender.RTPHeaderLength();
  uint16_t max_length = _rtpSender.MaxPayloadLength() - FECPacketOverhead() -
                        rtp_header_length - (1 /* generic header length */);

  // Fragment packets more evenly by splitting the payload up evenly.
  uint32_t num_packets = (size + max_length - 1) / max_length;
  uint32_t payload_length = (size + num_packets - 1) / num_packets;
  assert(payload_length <= max_length);

  // Fragment packet into packets of max MaxPayloadLength bytes payload.
  uint8_t buffer[IP_PACKET_SIZE];

  uint8_t generic_header = RtpFormatVideoGeneric::kFirstPacketBit;
  if (frame_type == kVideoFrameKey) {
    generic_header |= RtpFormatVideoGeneric::kKeyFrameBit;
  }

  while (size > 0) {
    if (size < payload_length) {
      payload_length = size;
    }
    size -= payload_length;

    // MarkerBit is 1 on final packet (bytes_to_send == 0)
    if (_rtpSender.BuildRTPheader(buffer, payload_type, size == 0,
                                  capture_timestamp,
                                  capture_time_ms) != rtp_header_length) {
      return -1;
    }

    uint8_t* out_ptr = &buffer[rtp_header_length];

    // Put generic header in packet
    *out_ptr++ = generic_header;
    // Remove first-packet bit, following packets are intermediate
    generic_header &= ~RtpFormatVideoGeneric::kFirstPacketBit;

    // Put payload in packet
    memcpy(out_ptr, payload, payload_length);
    payload += payload_length;

    if (SendVideoPacket(buffer, payload_length + 1, rtp_header_length,
                        capture_timestamp, capture_time_ms,
                        kAllowRetransmission, true)) {
      return -1;
    }
  }
  return 0;
}

VideoCodecInformation*
RTPSenderVideo::CodecInformationVideo()
{
    return _videoCodecInformation;
}

void
RTPSenderVideo::SetMaxConfiguredBitrateVideo(const uint32_t maxBitrate)
{
    _maxBitrate = maxBitrate;
}

uint32_t
RTPSenderVideo::MaxConfiguredBitrateVideo() const
{
    return _maxBitrate;
}

int32_t
RTPSenderVideo::SendVP8(const FrameType frameType,
                        const int8_t payloadType,
                        const uint32_t captureTimeStamp,
                        int64_t capture_time_ms,
                        const uint8_t* payloadData,
                        const uint32_t payloadSize,
                        const RTPFragmentationHeader* fragmentation,
                        const RTPVideoTypeHeader* rtpTypeHdr)
{
    const uint16_t rtpHeaderLength = _rtpSender.RTPHeaderLength();

    int32_t payloadBytesToSend = payloadSize;
    const uint8_t* data = payloadData;

    uint16_t maxPayloadLengthVP8 = _rtpSender.MaxDataPayloadLength();

    assert(rtpTypeHdr);
    // Initialize disregarding partition boundaries: this will use kEqualSize
    // packetization mode, which produces ~equal size packets for each frame.
    RtpPacketizerVp8 packetizer(rtpTypeHdr->VP8, maxPayloadLengthVP8);
    packetizer.SetPayloadData(data, payloadBytesToSend, NULL);

    StorageType storage = kAllowRetransmission;
    if (rtpTypeHdr->VP8.temporalIdx == 0 &&
        !(_retransmissionSettings & kRetransmitBaseLayer)) {
      storage = kDontRetransmit;
    } else if (rtpTypeHdr->VP8.temporalIdx != kNoTemporalIdx &&
        rtpTypeHdr->VP8.temporalIdx > 0 &&
        !(_retransmissionSettings & kRetransmitHigherLayers)) {
      storage = kDontRetransmit;
    }

    bool last = false;
    _numberFirstPartition = 0;
    // |rtpTypeHdr->VP8.temporalIdx| is zero for base layers, or kNoTemporalIdx
    // if the field isn't used (so all layers are the base layer).  We currently
    // only protect base layers, so look for these two cases.
    bool protect = rtpTypeHdr->VP8.temporalIdx == 0 ||
        rtpTypeHdr->VP8.temporalIdx == kNoTemporalIdx;
    while (!last)
    {
        // Write VP8 Payload Descriptor and VP8 payload.
        uint8_t dataBuffer[IP_PACKET_SIZE] = {0};
        size_t payloadBytesInPacket = 0;
        if (!packetizer.NextPacket(
                &dataBuffer[rtpHeaderLength], &payloadBytesInPacket, &last))
          return -1;

        // Write RTP header.
        // Set marker bit true if this is the last packet in frame.
        _rtpSender.BuildRTPheader(dataBuffer, payloadType, last,
            captureTimeStamp, capture_time_ms);
        if (-1 == SendVideoPacket(dataBuffer, payloadBytesInPacket,
                                  rtpHeaderLength, captureTimeStamp,
                                  capture_time_ms, storage, protect))
        {
          LOG(LS_WARNING)
              << "RTPSenderVideo::SendVP8 failed to send packet number "
              << _rtpSender.SequenceNumber();
        }
    }
    TRACE_EVENT_ASYNC_END1("webrtc", "Video", capture_time_ms,
                           "timestamp", _rtpSender.Timestamp());
    return 0;
}

bool RTPSenderVideo::SendH264(const FrameType frameType,
                              const int8_t payloadType,
                              const uint32_t captureTimeStamp,
                              int64_t capture_time_ms,
                              const uint8_t* payloadData,
                              const uint32_t payloadSize,
                              const RTPFragmentationHeader* fragmentation,
                              const RTPVideoTypeHeader* rtpTypeHdr) {
  size_t rtp_header_length = _rtpSender.RTPHeaderLength();
  int32_t payload_bytes_to_send = payloadSize;
  const uint8_t* data = payloadData;
  size_t max_payload_length = _rtpSender.MaxDataPayloadLength();

  scoped_ptr<RtpPacketizer> packetizer(
      RtpPacketizer::Create(kRtpVideoH264, max_payload_length));
  packetizer->SetPayloadData(data, payload_bytes_to_send, fragmentation);

  StorageType storage = kAllowRetransmission;
  bool protect = (frameType == kVideoFrameKey);
  bool last = false;

  while (!last) {
    // Write H264 payload.
    uint8_t dataBuffer[IP_PACKET_SIZE] = {0};
    size_t payload_bytes_in_packet = 0;
    if (!packetizer->NextPacket(
            &dataBuffer[rtp_header_length], &payload_bytes_in_packet, &last)) {
      return false;
    }

    // Write RTP header.
    // Set marker bit true if this is the last packet in frame.
    _rtpSender.BuildRTPheader(
        dataBuffer, payloadType, last, captureTimeStamp, capture_time_ms);
    if (SendVideoPacket(dataBuffer,
                        payload_bytes_in_packet,
                        rtp_header_length,
                        captureTimeStamp,
                        capture_time_ms,
                        storage,
                        protect)) {
      LOG(LS_WARNING)
          << "RTPSenderVideo::SendH264 failed to send packet number "
          << _rtpSender.SequenceNumber();
    }
  }
  return true;
}

void RTPSenderVideo::ProcessBitrate() {
  _videoBitrate.Process();
  _fecOverheadRate.Process();
}

uint32_t RTPSenderVideo::VideoBitrateSent() const {
  return _videoBitrate.BitrateLast();
}

uint32_t RTPSenderVideo::FecOverheadRate() const {
  return _fecOverheadRate.BitrateLast();
}

int RTPSenderVideo::SelectiveRetransmissions() const {
  return _retransmissionSettings;
}

int RTPSenderVideo::SetSelectiveRetransmissions(uint8_t settings) {
  _retransmissionSettings = settings;
  return 0;
}

}  // namespace webrtc
