/*
 *  Copyright (c) 2013 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/audio_coding/main/acm2/initial_delay_manager.h"

namespace webrtc {

namespace acm2 {

InitialDelayManager::InitialDelayManager(int initial_delay_ms,
                                         int late_packet_threshold)
    : last_packet_type_(kUndefinedPacket),
      last_receive_timestamp_(0),
      timestamp_step_(0),
      audio_payload_type_(kInvalidPayloadType),
      initial_delay_ms_(initial_delay_ms),
      buffered_audio_ms_(0),
      buffering_(true),
      playout_timestamp_(0),
      late_packet_threshold_(late_packet_threshold) {
  last_packet_rtp_info_.header.payloadType = kInvalidPayloadType;
  last_packet_rtp_info_.header.ssrc = 0;
  last_packet_rtp_info_.header.sequenceNumber = 0;
  last_packet_rtp_info_.header.timestamp = 0;
}

void InitialDelayManager::UpdateLastReceivedPacket(
    const WebRtcRTPHeader& rtp_info,
    uint32_t receive_timestamp,
    PacketType type,
    bool new_codec,
    int sample_rate_hz,
    SyncStream* sync_stream) {
  assert(sync_stream);

  // If payload of audio packets is changing |new_codec| has to be true.
  assert(!(!new_codec && type == kAudioPacket &&
         rtp_info.header.payloadType != audio_payload_type_));

  // Just shorthands.
  const RTPHeader* current_header = &rtp_info.header;
  RTPHeader* last_header = &last_packet_rtp_info_.header;

  // Don't do anything if getting DTMF. The chance of DTMF in applications where
  // initial delay is required is very low (we don't know of any). This avoids a
  // lot of corner cases. The effect of ignoring DTMF packet is minimal. Note
  // that DTMFs are inserted into NetEq just not accounted here.
  if (type == kAvtPacket ||
      (last_packet_type_ != kUndefinedPacket &&
      !IsNewerSequenceNumber(current_header->sequenceNumber,
                             last_header->sequenceNumber))) {
    sync_stream->num_sync_packets = 0;
    return;
  }

  // Either if it is a new packet or the first packet record and set variables.
  if (new_codec ||
      last_packet_rtp_info_.header.payloadType == kInvalidPayloadType) {
    timestamp_step_ = 0;
    if (type == kAudioPacket)
      audio_payload_type_ = rtp_info.header.payloadType;
    else
      audio_payload_type_ = kInvalidPayloadType;  // Invalid.

    RecordLastPacket(rtp_info, receive_timestamp, type);
    sync_stream->num_sync_packets = 0;
    buffered_audio_ms_ = 0;
    buffering_ = true;

    // If |buffering_| is set then |playout_timestamp_| should have correct
    // value.
    UpdatePlayoutTimestamp(*current_header, sample_rate_hz);
    return;
  }

  uint32_t timestamp_increase = current_header->timestamp -
      last_header->timestamp;

  // |timestamp_increase| is invalid if this is the first packet. The effect is
  // that |buffered_audio_ms_| is not increased.
  if (last_packet_type_ == kUndefinedPacket) {
    timestamp_increase = 0;
  }

  if (buffering_) {
    buffered_audio_ms_ += timestamp_increase * 1000 / sample_rate_hz;

    // A timestamp that reflects the initial delay, while buffering.
    UpdatePlayoutTimestamp(*current_header, sample_rate_hz);

    if (buffered_audio_ms_ >= initial_delay_ms_)
      buffering_ = false;
  }

  if (current_header->sequenceNumber == last_header->sequenceNumber + 1) {
    // Two consecutive audio packets, the previous packet-type is audio, so we
    // can update |timestamp_step_|.
    if (last_packet_type_ == kAudioPacket)
      timestamp_step_ = timestamp_increase;
    RecordLastPacket(rtp_info, receive_timestamp, type);
    sync_stream->num_sync_packets = 0;
    return;
  }

  uint16_t packet_gap = current_header->sequenceNumber -
      last_header->sequenceNumber - 1;

  // For smooth transitions leave a gap between audio and sync packets.
  sync_stream->num_sync_packets = last_packet_type_ == kSyncPacket ?
      packet_gap - 1 : packet_gap - 2;

  // Do nothing if we haven't received any audio packet.
  if (sync_stream->num_sync_packets > 0 &&
      audio_payload_type_ != kInvalidPayloadType) {
    if (timestamp_step_ == 0) {
      // Make an estimate for |timestamp_step_| if it is not updated, yet.
      assert(packet_gap > 0);
      timestamp_step_ = timestamp_increase / (packet_gap + 1);
    }
    sync_stream->timestamp_step = timestamp_step_;

    // Build the first sync-packet based on the current received packet.
    memcpy(&sync_stream->rtp_info, &rtp_info, sizeof(rtp_info));
    sync_stream->rtp_info.header.payloadType = audio_payload_type_;

    uint16_t sequence_number_update = sync_stream->num_sync_packets + 1;
    uint32_t timestamp_update = timestamp_step_ * sequence_number_update;

    // Rewind sequence number and timestamps. This will give a more accurate
    // description of the missing packets.
    //
    // Note that we leave a gap between the last packet in sync-stream and the
    // current received packet, so it should be compensated for in the following
    // computation of timestamps and sequence number.
    sync_stream->rtp_info.header.sequenceNumber -= sequence_number_update;
    sync_stream->receive_timestamp = receive_timestamp - timestamp_update;
    sync_stream->rtp_info.header.timestamp -= timestamp_update;
    sync_stream->rtp_info.header.payloadType = audio_payload_type_;
  } else {
    sync_stream->num_sync_packets = 0;
  }

  RecordLastPacket(rtp_info, receive_timestamp, type);
  return;
}

void InitialDelayManager::RecordLastPacket(const WebRtcRTPHeader& rtp_info,
                                           uint32_t receive_timestamp,
                                           PacketType type) {
  last_packet_type_ = type;
  last_receive_timestamp_ = receive_timestamp;
  memcpy(&last_packet_rtp_info_, &rtp_info, sizeof(rtp_info));
}

void InitialDelayManager::LatePackets(
    uint32_t timestamp_now, SyncStream* sync_stream) {
  assert(sync_stream);
  sync_stream->num_sync_packets = 0;

  // If there is no estimate of timestamp increment, |timestamp_step_|, then
  // we cannot estimate the number of late packets.
  // If the last packet has been CNG, estimating late packets is not meaningful,
  // as a CNG packet is on unknown length.
  // We can set a higher threshold if the last packet is CNG and continue
  // execution, but this is how ACM1 code was written.
  if (timestamp_step_ <= 0 ||
      last_packet_type_ == kCngPacket ||
      last_packet_type_ == kUndefinedPacket ||
      audio_payload_type_ == kInvalidPayloadType)  // No audio packet received.
    return;

  int num_late_packets = (timestamp_now - last_receive_timestamp_) /
      timestamp_step_;

  if (num_late_packets < late_packet_threshold_)
    return;

  int sync_offset = 1;  // One gap at the end of the sync-stream.
  if (last_packet_type_ != kSyncPacket) {
    ++sync_offset;  // One more gap at the beginning of the sync-stream.
    --num_late_packets;
  }
  uint32_t timestamp_update = sync_offset * timestamp_step_;

  sync_stream->num_sync_packets = num_late_packets;
  if (num_late_packets == 0)
    return;

  // Build the first sync-packet in the sync-stream.
  memcpy(&sync_stream->rtp_info, &last_packet_rtp_info_,
         sizeof(last_packet_rtp_info_));

  // Increase sequence number and timestamps.
  sync_stream->rtp_info.header.sequenceNumber += sync_offset;
  sync_stream->rtp_info.header.timestamp += timestamp_update;
  sync_stream->receive_timestamp = last_receive_timestamp_ + timestamp_update;
  sync_stream->timestamp_step = timestamp_step_;

  // Sync-packets have audio payload-type.
  sync_stream->rtp_info.header.payloadType = audio_payload_type_;

  uint16_t sequence_number_update = num_late_packets + sync_offset - 1;
  timestamp_update = sequence_number_update * timestamp_step_;

  // Fake the last RTP, assuming the caller will inject the whole sync-stream.
  last_packet_rtp_info_.header.timestamp += timestamp_update;
  last_packet_rtp_info_.header.sequenceNumber += sequence_number_update;
  last_packet_rtp_info_.header.payloadType = audio_payload_type_;
  last_receive_timestamp_ += timestamp_update;

  last_packet_type_ = kSyncPacket;
  return;
}

bool InitialDelayManager::GetPlayoutTimestamp(uint32_t* playout_timestamp) {
  if (!buffering_) {
    return false;
  }
  *playout_timestamp = playout_timestamp_;
  return true;
}

void InitialDelayManager::DisableBuffering() {
  buffering_ = false;
}

void InitialDelayManager::UpdatePlayoutTimestamp(
    const RTPHeader& current_header, int sample_rate_hz) {
  playout_timestamp_ = current_header.timestamp - static_cast<uint32_t>(
      initial_delay_ms_ * sample_rate_hz / 1000);
}

}  // namespace acm2

}  // namespace webrtc
