/* 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/video_coding/codecs/vp8/default_temporal_layers.h"

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

#include "webrtc/modules/interface/module_common_types.h"
#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"

#include "vpx/vpx_encoder.h"
#include "vpx/vp8cx.h"

namespace webrtc {

DefaultTemporalLayers::DefaultTemporalLayers(int numberOfTemporalLayers,
                                             uint8_t initial_tl0_pic_idx)
    : number_of_temporal_layers_(numberOfTemporalLayers),
      temporal_ids_length_(0),
      temporal_pattern_length_(0),
      tl0_pic_idx_(initial_tl0_pic_idx),
      pattern_idx_(255),
      timestamp_(0),
      last_base_layer_sync_(false) {
  assert(kMaxTemporalStreams >= numberOfTemporalLayers);
  memset(temporal_ids_, 0, sizeof(temporal_ids_));
  memset(temporal_pattern_, 0, sizeof(temporal_pattern_));
}

int DefaultTemporalLayers::CurrentLayerId() const {
  assert(temporal_ids_length_ > 0);
  int index = pattern_idx_ % temporal_ids_length_;
  assert(index >= 0);
  return temporal_ids_[index];
 }

bool DefaultTemporalLayers::ConfigureBitrates(int bitrateKbit,
                                              int max_bitrate_kbit,
                                              int framerate,
                                              vpx_codec_enc_cfg_t* cfg) {
  switch (number_of_temporal_layers_) {
    case 0:
    case 1:
      temporal_ids_length_ = 1;
      temporal_ids_[0] = 0;
      cfg->ts_number_layers = number_of_temporal_layers_;
      cfg->ts_periodicity = temporal_ids_length_;
      cfg->ts_target_bitrate[0] = bitrateKbit;
      cfg->ts_rate_decimator[0] = 1;
      memcpy(cfg->ts_layer_id,
             temporal_ids_,
             sizeof(unsigned int) * temporal_ids_length_);
      temporal_pattern_length_ = 1;
      temporal_pattern_[0] = kTemporalUpdateLastRefAll;
      break;
    case 2:
      temporal_ids_length_ = 2;
      temporal_ids_[0] = 0;
      temporal_ids_[1] = 1;
      cfg->ts_number_layers = number_of_temporal_layers_;
      cfg->ts_periodicity = temporal_ids_length_;
      // Split stream 60% 40%.
      // Bitrate API for VP8 is the agregated bitrate for all lower layers.
      cfg->ts_target_bitrate[0] = bitrateKbit * kVp8LayerRateAlloction[1][0];
      cfg->ts_target_bitrate[1] = bitrateKbit;
      cfg->ts_rate_decimator[0] = 2;
      cfg->ts_rate_decimator[1] = 1;
      memcpy(cfg->ts_layer_id,
             temporal_ids_,
             sizeof(unsigned int) * temporal_ids_length_);
      temporal_pattern_length_ = 8;
      temporal_pattern_[0] = kTemporalUpdateLastAndGoldenRefAltRef;
      temporal_pattern_[1] = kTemporalUpdateGoldenWithoutDependencyRefAltRef;
      temporal_pattern_[2] = kTemporalUpdateLastRefAltRef;
      temporal_pattern_[3] = kTemporalUpdateGoldenRefAltRef;
      temporal_pattern_[4] = kTemporalUpdateLastRefAltRef;
      temporal_pattern_[5] = kTemporalUpdateGoldenRefAltRef;
      temporal_pattern_[6] = kTemporalUpdateLastRefAltRef;
      temporal_pattern_[7] = kTemporalUpdateNone;
      break;
    case 3:
      temporal_ids_length_ = 4;
      temporal_ids_[0] = 0;
      temporal_ids_[1] = 2;
      temporal_ids_[2] = 1;
      temporal_ids_[3] = 2;
      cfg->ts_number_layers = number_of_temporal_layers_;
      cfg->ts_periodicity = temporal_ids_length_;
      // Split stream 40% 20% 40%.
      // Bitrate API for VP8 is the agregated bitrate for all lower layers.
      cfg->ts_target_bitrate[0] = bitrateKbit * kVp8LayerRateAlloction[2][0];
      cfg->ts_target_bitrate[1] = bitrateKbit * kVp8LayerRateAlloction[2][1];
      cfg->ts_target_bitrate[2] = bitrateKbit;
      cfg->ts_rate_decimator[0] = 4;
      cfg->ts_rate_decimator[1] = 2;
      cfg->ts_rate_decimator[2] = 1;
      memcpy(cfg->ts_layer_id,
             temporal_ids_,
             sizeof(unsigned int) * temporal_ids_length_);
      temporal_pattern_length_ = 8;
      temporal_pattern_[0] = kTemporalUpdateLastAndGoldenRefAltRef;
      temporal_pattern_[1] = kTemporalUpdateNoneNoRefGoldenRefAltRef;
      temporal_pattern_[2] = kTemporalUpdateGoldenWithoutDependencyRefAltRef;
      temporal_pattern_[3] = kTemporalUpdateNone;
      temporal_pattern_[4] = kTemporalUpdateLastRefAltRef;
      temporal_pattern_[5] = kTemporalUpdateNone;
      temporal_pattern_[6] = kTemporalUpdateGoldenRefAltRef;
      temporal_pattern_[7] = kTemporalUpdateNone;
      break;
    case 4:
      temporal_ids_length_ = 8;
      temporal_ids_[0] = 0;
      temporal_ids_[1] = 3;
      temporal_ids_[2] = 2;
      temporal_ids_[3] = 3;
      temporal_ids_[4] = 1;
      temporal_ids_[5] = 3;
      temporal_ids_[6] = 2;
      temporal_ids_[7] = 3;
      // Split stream 25% 15% 20% 40%.
      // Bitrate API for VP8 is the agregated bitrate for all lower layers.
      cfg->ts_number_layers = 4;
      cfg->ts_periodicity = temporal_ids_length_;
      cfg->ts_target_bitrate[0] = bitrateKbit * kVp8LayerRateAlloction[3][0];
      cfg->ts_target_bitrate[1] = bitrateKbit * kVp8LayerRateAlloction[3][1];
      cfg->ts_target_bitrate[2] = bitrateKbit * kVp8LayerRateAlloction[3][2];
      cfg->ts_target_bitrate[3] = bitrateKbit;
      cfg->ts_rate_decimator[0] = 8;
      cfg->ts_rate_decimator[1] = 4;
      cfg->ts_rate_decimator[2] = 2;
      cfg->ts_rate_decimator[3] = 1;
      memcpy(cfg->ts_layer_id,
             temporal_ids_,
             sizeof(unsigned int) * temporal_ids_length_);
      temporal_pattern_length_ = 16;
      temporal_pattern_[0] = kTemporalUpdateLast;
      temporal_pattern_[1] = kTemporalUpdateNone;
      temporal_pattern_[2] = kTemporalUpdateAltrefWithoutDependency;
      temporal_pattern_[3] = kTemporalUpdateNone;
      temporal_pattern_[4] = kTemporalUpdateGoldenWithoutDependency;
      temporal_pattern_[5] = kTemporalUpdateNone;
      temporal_pattern_[6] = kTemporalUpdateAltref;
      temporal_pattern_[7] = kTemporalUpdateNone;
      temporal_pattern_[8] = kTemporalUpdateLast;
      temporal_pattern_[9] = kTemporalUpdateNone;
      temporal_pattern_[10] = kTemporalUpdateAltref;
      temporal_pattern_[11] = kTemporalUpdateNone;
      temporal_pattern_[12] = kTemporalUpdateGolden;
      temporal_pattern_[13] = kTemporalUpdateNone;
      temporal_pattern_[14] = kTemporalUpdateAltref;
      temporal_pattern_[15] = kTemporalUpdateNone;
      break;
    default:
      assert(false);
      return false;
  }
  return true;
}

int DefaultTemporalLayers::EncodeFlags(uint32_t timestamp) {
  assert(number_of_temporal_layers_ > 0);
  assert(kMaxTemporalPattern >= temporal_pattern_length_);
  assert(0 < temporal_pattern_length_);
  int flags = 0;
  int patternIdx = ++pattern_idx_ % temporal_pattern_length_;
  assert(kMaxTemporalPattern >= patternIdx);
  switch (temporal_pattern_[patternIdx]) {
    case kTemporalUpdateLast:
      flags |= VP8_EFLAG_NO_UPD_GF;
      flags |= VP8_EFLAG_NO_UPD_ARF;
      flags |= VP8_EFLAG_NO_REF_GF;
      flags |= VP8_EFLAG_NO_REF_ARF;
      break;
    case kTemporalUpdateGoldenWithoutDependency:
      flags |= VP8_EFLAG_NO_REF_GF;
      // Deliberately no break here.
      FALLTHROUGH();
    case kTemporalUpdateGolden:
      flags |= VP8_EFLAG_NO_REF_ARF;
      flags |= VP8_EFLAG_NO_UPD_ARF;
      flags |= VP8_EFLAG_NO_UPD_LAST;
      break;
    case kTemporalUpdateAltrefWithoutDependency:
      flags |= VP8_EFLAG_NO_REF_ARF;
      flags |= VP8_EFLAG_NO_REF_GF;
      // Deliberately no break here.
      FALLTHROUGH();
    case kTemporalUpdateAltref:
      flags |= VP8_EFLAG_NO_UPD_GF;
      flags |= VP8_EFLAG_NO_UPD_LAST;
      break;
    case kTemporalUpdateNoneNoRefAltref:
      flags |= VP8_EFLAG_NO_REF_ARF;
      // Deliberately no break here.
      FALLTHROUGH();
    case kTemporalUpdateNone:
      flags |= VP8_EFLAG_NO_UPD_GF;
      flags |= VP8_EFLAG_NO_UPD_ARF;
      flags |= VP8_EFLAG_NO_UPD_LAST;
      flags |= VP8_EFLAG_NO_UPD_ENTROPY;
      break;
    case kTemporalUpdateNoneNoRefGoldenRefAltRef:
      flags |= VP8_EFLAG_NO_REF_GF;
      flags |= VP8_EFLAG_NO_UPD_GF;
      flags |= VP8_EFLAG_NO_UPD_ARF;
      flags |= VP8_EFLAG_NO_UPD_LAST;
      flags |= VP8_EFLAG_NO_UPD_ENTROPY;
      break;
    case kTemporalUpdateGoldenWithoutDependencyRefAltRef:
      flags |= VP8_EFLAG_NO_REF_GF;
      flags |= VP8_EFLAG_NO_UPD_ARF;
      flags |= VP8_EFLAG_NO_UPD_LAST;
      break;
    case kTemporalUpdateLastRefAltRef:
      flags |= VP8_EFLAG_NO_UPD_GF;
      flags |= VP8_EFLAG_NO_UPD_ARF;
      flags |= VP8_EFLAG_NO_REF_GF;
      break;
    case kTemporalUpdateGoldenRefAltRef:
      flags |= VP8_EFLAG_NO_UPD_ARF;
      flags |= VP8_EFLAG_NO_UPD_LAST;
      break;
    case kTemporalUpdateLastAndGoldenRefAltRef:
      flags |= VP8_EFLAG_NO_UPD_ARF;
      flags |= VP8_EFLAG_NO_REF_GF;
      break;
    case kTemporalUpdateLastRefAll:
      flags |= VP8_EFLAG_NO_UPD_ARF;
      flags |= VP8_EFLAG_NO_UPD_GF;
      break;
  }
  return flags;
}

void DefaultTemporalLayers::PopulateCodecSpecific(
    bool base_layer_sync,
    CodecSpecificInfoVP8 *vp8_info,
    uint32_t timestamp) {
  assert(number_of_temporal_layers_ > 0);
  assert(0 < temporal_ids_length_);

  if (number_of_temporal_layers_ == 1) {
    vp8_info->temporalIdx = kNoTemporalIdx;
    vp8_info->layerSync = false;
    vp8_info->tl0PicIdx = kNoTl0PicIdx;
  } else {
    if (base_layer_sync) {
    vp8_info->temporalIdx = 0;
    vp8_info->layerSync = true;
    } else {
      vp8_info->temporalIdx = CurrentLayerId();
      TemporalReferences temporal_reference =
          temporal_pattern_[pattern_idx_ % temporal_pattern_length_];

      if (temporal_reference == kTemporalUpdateAltrefWithoutDependency ||
          temporal_reference == kTemporalUpdateGoldenWithoutDependency ||
          temporal_reference ==
              kTemporalUpdateGoldenWithoutDependencyRefAltRef ||
          temporal_reference == kTemporalUpdateNoneNoRefGoldenRefAltRef ||
          (temporal_reference == kTemporalUpdateNone &&
              number_of_temporal_layers_ == 4)) {
        vp8_info->layerSync = true;
      } else {
        vp8_info->layerSync = false;
      }
    }
    if (last_base_layer_sync_ && vp8_info->temporalIdx != 0) {
      // Regardless of pattern the frame after a base layer sync will always
      // be a layer sync.
      vp8_info->layerSync = true;
    }
    if (vp8_info->temporalIdx == 0 && timestamp != timestamp_) {
      timestamp_ = timestamp;
      tl0_pic_idx_++;
    }
    last_base_layer_sync_ = base_layer_sync;
    vp8_info->tl0PicIdx = tl0_pic_idx_;
  }
}

TemporalLayers* TemporalLayers::Factory::Create(
    int temporal_layers,
    uint8_t initial_tl0_pic_idx) const {
  return new DefaultTemporalLayers(temporal_layers, initial_tl0_pic_idx);
}
}  // namespace webrtc
