
#include "basic/frame.h"

extern "C" {
#include <libswscale/swscale.h>
}

AVFrame* Frame<MediaType::AUDIO>::Alloc(AVSampleFormat sampleFmt,
    const AVChannelLayout* channel_layout,
    int sampleRate, int nbSamples)
{
    AVFrame* frame = av_frame_alloc();
    if (!frame) {
        __DebugPrint("av_frame_alloc failed");
        return nullptr;
    }
    frame->format = sampleFmt;
    av_channel_layout_copy(&frame->ch_layout, channel_layout);
    frame->sample_rate = sampleRate;
    frame->nb_samples = nbSamples;

    /* allocate the buffers for the frame data */
    if (av_frame_get_buffer(frame, 0) < 0) {
        __DebugPrint("av_frame_get_buffer failed");
        av_frame_free(&frame);
        return nullptr;
    }
    return frame;
}

Frame<MediaType::AUDIO>::Frame(AVSampleFormat sampleFmt,
    const AVChannelLayout* channel_layout, int sampleRate,
    int nbSamples)
{
    frame = Alloc(sampleFmt, channel_layout, sampleRate, nbSamples);
    if (!frame) {
        __DebugPrint("Frame<MediaType::AUDIO>::Alloc failed");
        return;
    }
}

Frame<MediaType::AUDIO>::Frame(AVFrame* frame)
{
    if (frame == nullptr) {
        this->frame = nullptr;
        return;
    }
    this->frame = Alloc(AVSampleFormat(frame->format), &frame->ch_layout, frame->sample_rate, frame->nb_samples);
    if (!this->frame) {
        __DebugPrint("Alloc failed in AUDIO copy ctor");
        return;
    }
    if (av_frame_copy(this->frame, frame) < 0) {
        __DebugPrint("av_frame_copy failed in AUDIO copy ctor");
        av_frame_free(&this->frame);
        this->frame = nullptr;
        return;
    }
}

Frame<MediaType::VIDEO>::Frame(AVPixelFormat pixFmt, int width, int height)
{
    frame = Alloc(pixFmt, width, height);
    if (!frame) {
        __DebugPrint("Frame<MediaType::VIDEO>::Alloc failed");
        return;
    }
}

AVFrame* Frame<MediaType::VIDEO>::Alloc(AVPixelFormat pixFmt, int width, int height)
{
    AVFrame* frame = av_frame_alloc();
    if (!frame) {
        __DebugPrint("av_frame_alloc failed");
        return nullptr;
    }

    frame->format = pixFmt;
    frame->width = width;
    frame->height = height;

    /* allocate the buffers for the frame data */
    if (av_frame_get_buffer(frame, 0) < 0) {
        __DebugPrint("av_frame_get_buffer failed");
        av_frame_free(&frame);
        return nullptr;
    }
    return frame;
}

Frame<MediaType::VIDEO>::Frame(AVFrame* frame)
{
    if (frame == nullptr) {
        this->frame = nullptr;
        return;
    }
    this->frame = Alloc(AVPixelFormat(frame->format), frame->width, frame->height);
    if (!this->frame) {
        __DebugPrint("Alloc failed in VIDEO copy ctor");
        return;
    }
    if (av_frame_copy(this->frame, frame) < 0) {
        __DebugPrint("av_frame_copy failed in VIDEO copy ctor");
        av_frame_free(&this->frame);
        this->frame = nullptr;
        return;
    }
}

bool FfmpegConverter::SetSize(int width, int height)
{
    Free(_swsCtx, [this] { sws_freeContext(_swsCtx); });
    Free(_frameTo, [this] { av_frame_free(&_frameTo); });
    // 创建格式转换
    _swsCtx = sws_getContext(
                    width, height, _from,
                    width, height, _to,
                    0, NULL, NULL, NULL);
    if (!_swsCtx) {
        __DebugPrint("sws_getContext failed");
        return false;
    }

    _frameTo = Frame<MediaType::VIDEO>::Alloc(_to, width, height);
    if (!_frameTo) {
        __DebugPrint("Frame<MediaType::VIDEO>::Alloc failed");
        return false;
    }
    return true;
}

AVFrame* FfmpegConverter::Trans(AVFrame* frameFrom)
{
    // 如果是空指针，直接把缓存返回
    if (frameFrom == nullptr) {
        return _frameTo;
    }
    int rc = sws_scale(_swsCtx, (const uint8_t* const*)frameFrom->data,
            frameFrom->linesize, 0, frameFrom->height, _frameTo->data,
            _frameTo->linesize);
    if (rc < 0) {
        __DebugPrint("sws_scale failed: rc=%d", rc);
        return nullptr;
    }
    return _frameTo;
}

FfmpegConverter::~FfmpegConverter()
{
    Free(_swsCtx, [this] { sws_freeContext(_swsCtx); });
    Free(_frameTo, [this] { av_frame_free(&_frameTo); });
}
