#include "FFmpegResample.h"
#include "BoostLog.h"
#include "FFmpegResample.h"

extern "C" {
#include <libavcodec/avcodec.h>
#include <libswresample/swresample.h>
}

FFmpegResample::~FFmpegResample() {
    if (m_buffer != nullptr) {
        delete[] m_buffer;
    }
}

void FFmpegResample::initialize(int32_t sampleRateIn, int32_t channelIn, int32_t sampleRateOut, int32_t channelOut,
                                int32_t period) {
    m_sampleRateOut = sampleRateOut;
    m_channelOut = channelOut;
    m_sampleRateIn = sampleRateIn;
    m_channelIn = channelIn;

    int64_t outChLayout = channelOut == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO;
    int64_t inChLayout = channelIn == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO;

    m_swrContext = swr_alloc_set_opts(nullptr, outChLayout, AV_SAMPLE_FMT_S16, sampleRateOut, inChLayout, AV_SAMPLE_FMT_S16,
                                      sampleRateIn, 0, nullptr);
    if (m_swrContext == nullptr) {
        LOG(error) << "swr_alloc_set_opts() failed.";
    }
    m_buffer = new uint8_t[sampleRateOut / 1000 * period * channelOut * sizeof(int16_t)];
    int status = swr_init(m_swrContext);
    if (status < 0) {
        LOG(error) << "swr_init() failed.";
    }
}

FFmpegResample::Frame FFmpegResample::resample(const uint8_t *pcm, int32_t size) {
    auto begin = std::chrono::system_clock::now();
    FFmpegResample::Frame ret;
    // 1. 输入进来的是 1channel 16khz 16bit pcm
    // 2. 重采样为 2channel 48khz 16bit pcm
    // 3. 编码为 opus

    int32_t inSamples = size / m_channelIn / sizeof(int16_t);
    int32_t outSamples = inSamples * m_sampleRateOut / m_sampleRateIn;
    uint8_t *outBuffer = buffer.data();
    int samples = swr_convert(m_swrContext, &outBuffer, outSamples, &pcm, inSamples);
    if (samples < 0) {
        char buffer[512] = {0};
        LOG(error) << "avcodec_receive_frame() failed, error: " << av_make_error_string(buffer, sizeof(buffer), samples);
        return ret;
    }

    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - begin);

    ret.data = outBuffer;
    ret.byteSize = samples * m_channelOut * sizeof(int16_t);
    // LOG(info) << "inSamples: " << inSamples << ", samples: " << samples << ", elapsed: " << elapsed.count();
    return ret;
}
