#include "VideoPlayer.h"
#include "BoostLog.h"
#include <QImage>
extern "C" {
#include "libavdevice/avdevice.h"
#include "libavformat/avformat.h"
}

VideoPlayer::VideoPlayer() {
    static bool init = false;
    if (!init) {
        avdevice_register_all();
        init = true;
    }
}

void VideoPlayer::setFrameCallback(FrameCallback &&callback) {
    m_callback = std::move(callback);
}

void VideoPlayer::setErrorCallback(ErrorCallback &&callback) {
    m_errorCallback = std::move(callback);
}

VideoPlayer::~VideoPlayer() {
    close();
}

bool VideoPlayer::open(const std::string &deviceName) {
    bool ret = true;
    m_formatContext = avformat_alloc_context();
    auto format = av_find_input_format("dshow");

    AVDictionary *dictionary = nullptr;
    // ffmpeg -f dshow -list_options true -i video="UVC Camera"
    av_dict_set(&dictionary, "video_size", "800*600", 0);

    std::ostringstream oss;
    oss << "video=" << deviceName;
    auto device = oss.str();
    int status = avformat_open_input(&m_formatContext, "video=UVC Camera", format, &dictionary);
    if (status != 0) {
        char message[256] = {0};
        av_make_error_string(message, sizeof(message), status);
        LOG(error) << "open device[" << device << "] failed: " << status << "," << message;
        ret = false;
    } else {
        m_exit = false;
        m_thread = std::thread(&VideoPlayer::run, this);
    }
    av_dict_free(&dictionary);

    return ret;
}

void VideoPlayer::close() {
    m_exit = true;
    if (m_thread.joinable()) {
        m_thread.join();
    }
    avformat_close_input(&m_formatContext);
    avformat_free_context(m_formatContext);
    m_formatContext = nullptr;
}

void VideoPlayer::run() {
    auto packet = av_packet_alloc();
    while (!m_exit) {
        auto status = av_read_frame(m_formatContext, packet);
        if (status == 0) {
            QImage image;
            image.loadFromData(packet->data, packet->size);
            if (!image.isNull() && m_callback) m_callback(image);
        } else {
            char message[256] = {0};
            av_make_error_string(message, sizeof(message), status);
            if (m_errorCallback) m_errorCallback(status, message);
            LOG(error) << "av_read_frame() failed: " << status << "," << message;
            if (status == -EIO) {
                break;
            }
        }
        av_packet_unref(packet);
    }

    av_packet_free(&packet);
    QImage image(800, 600, QImage::Format_RGB888);
    image.fill(Qt::black);
    if (m_callback) m_callback(image);
}
