/*
 *  Copyright (c) 2014 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/codecs/opus/opus_interface.h"
#include "webrtc/modules/audio_coding/codecs/tools/audio_codec_speed_test.h"

using ::std::string;

namespace webrtc {

static const int kOpusBlockDurationMs = 20;
static const int kOpusSamplingKhz = 48;

class OpusSpeedTest : public AudioCodecSpeedTest {
 protected:
  OpusSpeedTest();
  void SetUp() override;
  void TearDown() override;
  float EncodeABlock(int16_t* in_data, uint8_t* bit_stream,
                     size_t max_bytes, size_t* encoded_bytes) override;
  float DecodeABlock(const uint8_t* bit_stream, size_t encoded_bytes,
                     int16_t* out_data) override;
  WebRtcOpusEncInst* opus_encoder_;
  WebRtcOpusDecInst* opus_decoder_;
};

OpusSpeedTest::OpusSpeedTest()
    : AudioCodecSpeedTest(kOpusBlockDurationMs,
                          kOpusSamplingKhz,
                          kOpusSamplingKhz),
      opus_encoder_(NULL),
      opus_decoder_(NULL) {
}

void OpusSpeedTest::SetUp() {
  AudioCodecSpeedTest::SetUp();
  // If channels_ == 1, use Opus VOIP mode, otherwise, audio mode.
  int app = channels_ == 1 ? 0 : 1;
  /* Create encoder memory. */
  EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, channels_, app));
  EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, channels_));
  /* Set bitrate. */
  EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, bit_rate_));
}

void OpusSpeedTest::TearDown() {
  AudioCodecSpeedTest::TearDown();
  /* Free memory. */
  EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_));
  EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_));
}

float OpusSpeedTest::EncodeABlock(int16_t* in_data, uint8_t* bit_stream,
                                  size_t max_bytes, size_t* encoded_bytes) {
  clock_t clocks = clock();
  int value = WebRtcOpus_Encode(opus_encoder_, in_data,
                                input_length_sample_, max_bytes,
                                bit_stream);
  clocks = clock() - clocks;
  EXPECT_GT(value, 0);
  *encoded_bytes = static_cast<size_t>(value);
  return 1000.0 * clocks / CLOCKS_PER_SEC;
}

float OpusSpeedTest::DecodeABlock(const uint8_t* bit_stream,
                                  size_t encoded_bytes, int16_t* out_data) {
  int value;
  int16_t audio_type;
  clock_t clocks = clock();
  value = WebRtcOpus_Decode(opus_decoder_, bit_stream, encoded_bytes, out_data,
                            &audio_type);
  clocks = clock() - clocks;
  EXPECT_EQ(output_length_sample_, static_cast<size_t>(value));
  return 1000.0 * clocks / CLOCKS_PER_SEC;
}

#define ADD_TEST(complexity) \
TEST_P(OpusSpeedTest, OpusSetComplexityTest##complexity) { \
  /* Test audio length in second. */ \
  size_t kDurationSec = 400; \
  /* Set complexity. */ \
  printf("Setting complexity to %d ...\n", complexity); \
  EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_encoder_, complexity)); \
  EncodeDecode(kDurationSec); \
}

ADD_TEST(10);
ADD_TEST(9);
ADD_TEST(8);
ADD_TEST(7);
ADD_TEST(6);
ADD_TEST(5);
ADD_TEST(4);
ADD_TEST(3);
ADD_TEST(2);
ADD_TEST(1);
ADD_TEST(0);

// List all test cases: (channel, bit rat, filename, extension).
const coding_param param_set[] =
    {::std::tr1::make_tuple(1, 64000,
                            string("audio_coding/speech_mono_32_48kHz"),
                            string("pcm"), true),
     ::std::tr1::make_tuple(1, 32000,
                            string("audio_coding/speech_mono_32_48kHz"),
                            string("pcm"), true),
     ::std::tr1::make_tuple(2, 64000,
                            string("audio_coding/music_stereo_48kHz"),
                            string("pcm"), true)};

INSTANTIATE_TEST_CASE_P(AllTest, OpusSpeedTest,
                        ::testing::ValuesIn(param_set));

}  // namespace webrtc
