/*
 * Kylin-video
 *
 * Copyright (C) 2021, Tianjin KYLIN Information Technology Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * Authors: Liu Cong <liucong1@kylinos.cn>
 *
 */

#include "mpvcore.h"
#include <QCoreApplication>
#include <QFileInfo>
#include <QDateTime>
#include <QProcess>
#include <QThread>
#include <QDebug>
#include <QTimer>

#include <syslog.h>
#include <unistd.h>
#include <MediaInfo/MediaInfo.h>
#include <ZenLib/Ztring.h>
#include <ukui-log4qt.h>

#include "core/util.h"
#include "playglwidget.h"
#include "global/globalsignal.h"
#include "global/functions.h"
#include "global/global.h"
#include "global/paths.h"

#include "elog.h"
#define LOG_TAG "core"

using namespace MediaInfoLib;
using namespace ZenLib;
using namespace Global;

#define wstring2QString(_DATA) \
    QString::fromUtf8(Ztring(_DATA).To_UTF8().c_str())
#define QString2wstring(_DATA) \
    Ztring().From_UTF8(_DATA.toUtf8())

extern QString int2ts(int64_t time, bool use_msec);

static void wakeup(void *ctx)
{
    MpvCore *mpvhandler = (MpvCore*)ctx;
    QCoreApplication::postEvent(mpvhandler, new QEvent(QEvent::User));
}

MpvCore::MpvCore(PlayGLWidget *pw, QObject *parent) : QObject(parent)
{
    setlocale(LC_NUMERIC, "C");
    playState = Mpv::Idle;
    m_playWidget = pw;
    initMpvHandle();
    initGlobalSig();
}

QVariant MpvCore::Command(const QVariant &params)
{
    if(mpv_h)
        return mpv::qt::command_variant(mpv_h, params);
}

void MpvCore::LoadOptions()
{
    // 播放之前先设置参数
    // 1.截图格式和截图存放位置
    SetOption("screenshot-format", gsetup->screenShotFormat.first);

    SetOption("screenshot-directory", gsetup->screenShotPath.first == ""
              ? QDir::homePath().append("/").append(tr("Pictures"))
              : gsetup->screenShotPath.first);

    // 2.设置字幕
    SetOption("sub-font", gsetup->subFontFamily.first);
    SetOption("sub-font-size", QString::number(gsetup->subFontSize.first));

    // 3.设置音频输出驱动
    SetOption("ao", gsetup->audioOut.first);

    // 4.设置倍速
    SetOption("speed", g_settings->value("General/speed").toString());

    SetOption("osd-level", "2");
    SetOption("osd-msg2", " ");
    SetOption("osd-font-size", "35");
    // 设置字体大小不随窗口变化而变化，否则 mini 模式osd显示字体特别小
    SetOption("osd-scale-by-window", "no");

    SetOption("brightness", QString::number(brightness));

#if 0
    if (Functions::isKirinCpu()) {

        MediaInfoLib::MediaInfo mi;
        mi.Open(QString2wstring(file));
        auto wi = wstring2QString(mi.Get(Stream_Video, 0, __T("Width"))).toInt();
        auto hi = wstring2QString(mi.Get(Stream_Video, 0, __T("Height"))).toInt();

        if (gsetup->videoDecoder.first == "omx" || gsetup->videoDecoder.first == "default" || gsetup->videoDecoder.first.length() == 0)
        {
            qDebug() << "[" << wi << "x" << hi <<"] use hwdec : " << mpv_get_property_string(mpv_h, "hwdec");
            if(wi > OMX_DEC_MAX_WIDTH || hi > OMX_DEC_MAX_HEIGHT) {
                // 如果超过硬解分辨率并且用的 omx 解码则设置成软解
                SetOption("hwdec", "no");
            }
            else {
                SetOption("hwdec", "omx");
            }
        }
    }
    else {
        SetOption("hwdec", gsetup->videoDecoder.first);
    }
#endif
    // 设置视频解码相关
    SetOption("hwdec", gsetup->videoDecoder.first + ",");
    SetOption("vo", gsetup->videoOutput.first + ",");
    SetOption("vd-lavc-threads", QString::number(gsetup->videoDecodeThreads.first));
}

void MpvCore::SetOption(QString key, QString val)
{
    QByteArray tmp1 = key.toUtf8(),
               tmp2 = val.toUtf8();
    mpv::qt::set_option_variant(mpv_h, tmp1.constData(), tmp2.constData());
}

int MpvCore::AsyncCommand(const char *args[])
{
    return mpv_command_async(mpv_h, MPV_REPLY_COMMAND, args);
}

void MpvCore::setProperty(const QString &name, const QVariant &value)
{
    mpv::qt::set_property_variant(mpv_h, name, value);
}

QVariant MpvCore::getProperty(const QString &name) const
{
    return mpv::qt::get_property_variant(mpv_h, name);
}

void MpvCore::Open(QString file, int start)
{
    // 打开之前先判断文件是否存在，如果不存在做判断
    QFile f(file);
    if(!f.exists())
    {
        KyInfo() << QString("file %1 not exit!").arg(file);
        g_core_signal->notifyFileNotExist(file);
        return;
    }

    lastStopTime = g_sqlite->getLastTime(file);

    LoadOptions();
    mediaInfo = QString();
    m_playingFile = file;
    subDelay = 0;
    lastTime = 0;
    rotate = 0;
    fps = 0;

    if (start > 0) {
        needSeek = true;
        seekTime = start;
    }
    // 如果是播放状态先暂停，不然拖入播放有问题，原因可能是停止的时候没有清空缓存或者清空配置
    Command(QStringList() << "loadfile" << file);
}

void MpvCore::Play()
{
    if (playState == Mpv::Playing)
        return;
    if(playState > 0 && mpv_h)
    {
        int f = 0;
        mpv_set_property_async(mpv_h, MPV_REPLY_PROPERTY, "pause", MPV_FORMAT_FLAG, &f);
    }
}

void MpvCore::Pause()
{
    if (playState == Mpv::Paused)
        return;
    if (playState > 0 && mpv_h) {
        int f = 1;
        mpv_set_property_async(mpv_h, MPV_REPLY_PROPERTY, "pause", MPV_FORMAT_FLAG, &f);
    }
}

void MpvCore::Stop()
{
    if(playState < 0)
        return;
    isManualStop = true;
    if(playState > 0)
    {
        // 如果是手动停止的话，记录上次停止位置
        g_sqlite->updateLastTime(fileInfo.file_path, lastTime);
    }
    if(playState > 0 && mpv_h)
        Command(QStringList() << "stop");
}

void MpvCore::Restart()
{
    if(m_playingFile == QString() || playState < 0)
        return;
    // 因为 open 是非阻塞的 所以跳转要在播放状态为 加载之后.
    seekTime = lastTime;
    if(seekTime > 0)
        needSeek = true;
    Stop();
    Play();
    Open(m_playingFile);
}

QString MpvCore::getMediaInfo()
{
    if(mediaInfo.length() != 0) {
        return mediaInfo;
    }

    QFileInfo fi(m_playingFile);
    if(!fi.exists()) {
        return QString();
    }

    double fps, vbitrate, abitrate;
    mpv_get_property(mpv_h, "estimated-vf-fps", MPV_FORMAT_DOUBLE, &fps);

    QString current_vo, current_ao, hwdec_active;

    char *property_string = nullptr;
    property_string = mpv_get_property_string(mpv_h, "current-vo");
    if(property_string) {
        current_vo = property_string;
        mpv_free(property_string);
        property_string = nullptr;
    }
    property_string = mpv_get_property_string(mpv_h, "current-ao");
    if(property_string) {
        current_ao = property_string;
        mpv_free(property_string);
        property_string = nullptr;
    }
    property_string = mpv_get_property_string(mpv_h, "hwdec-active");
    if(property_string) {
        hwdec_active = property_string;
        mpv_free(property_string);
        property_string = nullptr;
    }

    fileInfo.video_params.fps = fps;

    MediaInfoLib::MediaInfo mi;
    mi.Open(QString2wstring(m_playingFile));
    fps = wstring2QString(mi.Get(Stream_Video, 0, __T("FrameRate"))).toDouble();

    vbitrate = wstring2QString(mi.Get(Stream_Video, 0, __T("BitRate"))).toDouble();
    abitrate = wstring2QString(mi.Get(Stream_Audio, 0, __T("BitRate"))).toDouble();

    for(auto &track : fileInfo.tracks)
    {
        if(track.type == "video")
            ++vtracks;
        else if(track.type == "audio")
            ++atracks;
        else if(track.type == "sub")
            ++stracks;
    }

    const QString outer = "%0: %1\n", inner = "    %0: %1\n";

    QString out = outer.arg(tr("File"), fi.fileName()) +
            inner.arg(tr("Title"), fileInfo.media_title) +
            inner.arg(tr("File size"), Util::HumanSize(fi.size())) +
            inner.arg(tr("Date created"), fi.created().toString()) +
            inner.arg(tr("Media length"), Util::FormatTime(fileInfo.length, fileInfo.length)) + '\n';
    if(fileInfo.video_params.codec != QString())
        out += outer.arg(tr("Video (x%0)").arg(QString::number(vtracks)), fileInfo.video_params.codec) +
            inner.arg(tr("Video Output"), QString("%0 (hwdec %1)").arg(current_vo, hwdec_active)) +
            inner.arg(tr("Resolution"), QString("%0 x %1 (%2)").arg(QString::number(fileInfo.video_params.width),
                                                                    QString::number(fileInfo.video_params.height),
                                                                    Util::Ratio(fileInfo.video_params.width, fileInfo.video_params.height))) +
            inner.arg(tr("FPS"), QString::number(fps)) +
            inner.arg(tr("Bitrate"), tr("%0 kbps").arg(vbitrate/1000)) + '\n';
    if(fileInfo.audio_params.codec != QString())
        out += outer.arg(tr("Audio (x%0)").arg(QString::number(atracks)), fileInfo.audio_params.codec) +
            inner.arg(tr("Audio Output"), current_ao) +
            inner.arg(tr("Sample Rate"), QString::number(fileInfo.audio_params.samplerate)) +
            inner.arg(tr("Channels"), QString::number(fileInfo.audio_params.channels)) +
            inner.arg(tr("Bitrate"), tr("%0 kbps").arg(abitrate/1000)) + '\n';

    if(fileInfo.chapters.length() > 0)
    {
        out += outer.arg(tr("Chapters"), QString());
        int n = 1;
        for(auto &chapter : fileInfo.chapters)
            out += inner.arg(QString::number(n++), chapter.title);
        out += '\n';
    }

    if(fileInfo.metadata.size() > 0)
    {
        out += outer.arg(tr("Metadata"), QString());
        for(auto data = fileInfo.metadata.begin(); data != fileInfo.metadata.end(); ++data)
            out += inner.arg(data.key(), *data);
        out += '\n';
    }

    return out;
}

void MpvCore::LoadTracks()
{
    mpv_node node;
    mpv_get_property(mpv_h, "track-list", MPV_FORMAT_NODE, &node);
    std::lock_guard<std::mutex> lg(m_mtxTracks);
    fileInfo.tracks.clear();
    subCount = 0;
    if(node.format == MPV_FORMAT_NODE_ARRAY)
    {
        for(int i = 0; i < node.u.list->num; i++)
        {
            if(node.u.list->values[i].format == MPV_FORMAT_NODE_MAP)
            {
                Mpv::Track track;
                for(int n = 0; n < node.u.list->values[i].u.list->num; n++)
                {
                    if(QString(node.u.list->values[i].u.list->keys[n]) == "id")
                    {
                        if(node.u.list->values[i].u.list->values[n].format == MPV_FORMAT_INT64)
                            track.id = node.u.list->values[i].u.list->values[n].u.int64;
                    }
                    else if(QString(node.u.list->values[i].u.list->keys[n]) == "type")
                    {
                        if(node.u.list->values[i].u.list->values[n].format == MPV_FORMAT_STRING)
                        {
                            track.type = node.u.list->values[i].u.list->values[n].u.string;
                            if(track.type == "sub")
                                subCount++;
                        }
                    }
                    else if(QString(node.u.list->values[i].u.list->keys[n]) == "src-id")
                    {
                        if(node.u.list->values[i].u.list->values[n].format == MPV_FORMAT_INT64)
                            track.src_id = node.u.list->values[i].u.list->values[n].u.int64;
                    }
                    else if(QString(node.u.list->values[i].u.list->keys[n]) == "title")
                    {
                        if(node.u.list->values[i].u.list->values[n].format == MPV_FORMAT_STRING)
                            track.title = node.u.list->values[i].u.list->values[n].u.string;
                    }
                    else if(QString(node.u.list->values[i].u.list->keys[n]) == "lang")
                    {
                        if(node.u.list->values[i].u.list->values[n].format == MPV_FORMAT_STRING)
                            track.lang = node.u.list->values[i].u.list->values[n].u.string;
                    }
                    else if(QString(node.u.list->values[i].u.list->keys[n]) == "albumart")
                    {
                        if(node.u.list->values[i].u.list->values[n].format == MPV_FORMAT_FLAG)
                            track.albumart = node.u.list->values[i].u.list->values[n].u.flag;
                    }
                    else if(QString(node.u.list->values[i].u.list->keys[n]) == "default")
                    {
                        if(node.u.list->values[i].u.list->values[n].format == MPV_FORMAT_FLAG)
                            track._default = node.u.list->values[i].u.list->values[n].u.flag;
                    }
                    else if(QString(node.u.list->values[i].u.list->keys[n]) == "external")
                    {
                        if(node.u.list->values[i].u.list->values[n].format == MPV_FORMAT_FLAG)
                            track.external = node.u.list->values[i].u.list->values[n].u.flag;
                    }
                    else if(QString(node.u.list->values[i].u.list->keys[n]) == "external-filename")
                    {
                        if(node.u.list->values[i].u.list->values[n].format == MPV_FORMAT_STRING)
                            track.external_filename = node.u.list->values[i].u.list->values[n].u.string;
                    }
                    else if(QString(node.u.list->values[i].u.list->keys[n]) == "codec")
                    {
                        if(node.u.list->values[i].u.list->values[n].format == MPV_FORMAT_STRING)
                            track.codec = node.u.list->values[i].u.list->values[n].u.string;
                    }
                }
                fileInfo.tracks.push_back(track);
            }
        }
    }
    mpv_free_node_contents(&node);
    g_core_signal->notifyTracks(fileInfo.tracks);
}

void MpvCore::LoadChapters()
{
    fileInfo.chapters.clear();
    mpv_node node;
    mpv_get_property(mpv_h, "chapter-list", MPV_FORMAT_NODE, &node);
    if(node.format == MPV_FORMAT_NODE_ARRAY)
    {
        for(int i = 0; i < node.u.list->num; i++)
        {
            if(node.u.list->values[i].format == MPV_FORMAT_NODE_MAP)
            {
                Mpv::Chapter ch;
                for(int n = 0; n < node.u.list->values[i].u.list->num; n++)
                {
                    if(QString(node.u.list->values[i].u.list->keys[n]) == "title")
                    {
                        if(node.u.list->values[i].u.list->values[n].format == MPV_FORMAT_STRING)
                            ch.title = node.u.list->values[i].u.list->values[n].u.string;
                    }
                    else if(QString(node.u.list->values[i].u.list->keys[n]) == "time")
                    {
                        if(node.u.list->values[i].u.list->values[n].format == MPV_FORMAT_DOUBLE)
                            ch.time = (int)node.u.list->values[i].u.list->values[n].u.double_;
                    }
                }
                fileInfo.chapters.push_back(ch);
            }
        }
    }
    mpv_free_node_contents(&node);
}

void MpvCore::LoadVideoParams()
{
    char *property_string = nullptr;
    property_string = mpv_get_property_string(mpv_h, "video-codec");
    fileInfo.video_params.codec = property_string;
    if(property_string) {
        isVideo = true;
        if (fileInfo.video_params.codec.indexOf("jpeg") >= 0 ||
            fileInfo.video_params.codec.indexOf("png") >= 0) {
            // jpeg 图片，不能去获取缩略图了就，想要获取缩略图要换其他方式
            isVideo = false;
        }
        else {
            isVideo = true;
        }
        mpv_free(property_string);
    }
    mpv_get_property(mpv_h, "width",        MPV_FORMAT_INT64, &fileInfo.video_params.width);
    mpv_get_property(mpv_h, "height",       MPV_FORMAT_INT64, &fileInfo.video_params.height);
    mpv_get_property(mpv_h, "dwidth",       MPV_FORMAT_INT64, &fileInfo.video_params.dwidth);
    mpv_get_property(mpv_h, "dheight",      MPV_FORMAT_INT64, &fileInfo.video_params.dheight);
    // though this has become useless, removing it causes a segfault--no clue:
    mpv_get_property(mpv_h, "video-aspect-override", MPV_FORMAT_INT64, &fileInfo.video_params.aspect);

}

void MpvCore::LoadAudioParams()
{
    char *property_string = nullptr;
    property_string = mpv_get_property_string(mpv_h, "audio-codec");
    if(property_string) {
        fileInfo.audio_params.codec = property_string;
        mpv_free(property_string);
    }
    mpv_node node;
    mpv_get_property(mpv_h, "audio-params", MPV_FORMAT_NODE, &node);
    if(node.format == MPV_FORMAT_NODE_MAP)
    {
        for(int i = 0; i < node.u.list->num; i++)
        {
            if(QString(node.u.list->keys[i]) == "samplerate")
            {
                if(node.u.list->values[i].format == MPV_FORMAT_INT64)
                    fileInfo.audio_params.samplerate = node.u.list->values[i].u.int64;
            }
            else if(QString(node.u.list->keys[i]) == "channel-count")
            {
                if (node.u.list->values[i].format == MPV_FORMAT_INT64) {
                    fileInfo.audio_params.channels = node.u.list->values[i].u.int64;

                    // 声道设置，加载声道之后再设置声道，如果是单声道的时候设置右声道播放左声道
                    Channel((Mpv::Channel)(gsetup->audioChannel.first+1));
                }
            }
        }
    }
    mpv_free_node_contents(&node);
}

void MpvCore::LoadMetadata()
{
    fileInfo.metadata.clear();
    mpv_node node;
    mpv_get_property(mpv_h, "metadata", MPV_FORMAT_NODE, &node);
    if(node.format == MPV_FORMAT_NODE_MAP)
        for(int n = 0; n < node.u.list->num; n++)
            if(node.u.list->values[n].format == MPV_FORMAT_STRING)
                fileInfo.metadata[node.u.list->keys[n]] = node.u.list->values[n].u.string;
    mpv_free_node_contents(&node);
}

void MpvCore::LoadOsdSize()
{
    mpv_get_property(mpv_h, "osd-width", MPV_FORMAT_INT64, &osdWidth);
    mpv_get_property(mpv_h, "osd-height", MPV_FORMAT_INT64, &osdHeight);
}

void MpvCore::Volume(int level)
{
    if (volume == level)
        return;

    if(level > 100) level = 100;
    else if(level < 0) level = 0;
    double v = level;
    volume = level;

    if(playState > 0)
    {
        mpv_set_property_async(mpv_h, MPV_REPLY_PROPERTY, "volume", MPV_FORMAT_DOUBLE, &v);
        Mute(false);
    }
    else
    {
        mpv_set_option(mpv_h, "volume", MPV_FORMAT_DOUBLE, &v);
        // 设置音量参数，此时需要手动设置配置文件中音量
        g_settings->setValue("General/volume", volume);
    }
}

/**
** @brief         : 设置声道，需要考虑如果是单声道或者多声道的时候声音如何处理
** @param[in]     :
** @param[out]    :
** @return        :
***/
void MpvCore::Channel(Mpv::Channel c)
{
    const char *args[] = {"af", "", "", NULL};
    switch (c) {
    case Mpv::Default:
        args[1] = "set";args[2] = "";
        AsyncCommand(args);
        break;
    case Mpv::Stereo:
        args[1] = "set";args[2] = "";
        AsyncCommand(args);
        ShowText(tr("Stereo"));
        break;
    case Mpv::Left:
        args[1] = "set";args[2] = "lavfi=[pan=stereo|c0=c0|c1=0*c1]";
        AsyncCommand(args);
        ShowText(tr("Left Channel"));
        break;
    case Mpv::Right:
        args[1] = "set";
        args[2] = fileInfo.audio_params.channels == 1 ?
                    "lavfi=[pan=stereo|c0=0*c0|c1=c0]" :
                    "lavfi=[pan=stereo|c0=0*c0|c1=c1]";
        AsyncCommand(args);
        ShowText(tr("Right Channel"));
        break;
    default:
        break;
    }

    gsetup->audioChannel.second = (int)c - 1;
    gsetup->flushChange();
}

void MpvCore::Speed(double d)
{
    if(playState > 0) {
        Command(QStringList() << "set" << "speed" << QString::number(d));
//        mpv_set_property_async(mpv_h, MPV_REPLY_PROPERTY, "speed", MPV_FORMAT_DOUBLE, &d);
    }
    else {
        SetOption("speed", QString::number(d));
    }
}

void MpvCore::SpeedUp()
{
    if(playState > 0 && speed < 1.9)
    {
        if(speed > 1.4)
            speed += 0.5;
        else
            speed += 0.25;
    }
    Speed(speed);
}

void MpvCore::SpeedDown()
{
    if(playState > 0 && speed > 0.6)
    {
        if(speed > 1.9)
            speed -= 0.5;
        else
            speed -= 0.25;
    }
    Speed(speed);
}

void MpvCore::Mute(bool m)
{
    if (playState > 0)
    {
        const char *args[] = {"set", "mute", m ? "yes" : "no", NULL};
        AsyncCommand(args);
        mute = m;
    }
}

void MpvCore::Seek(int pos, bool relative, bool osd)
{
    if (duration < 0)
        return;
    {
        // 判断跳转是否越界（<0 或者 >视频长度）
        pos = pos < 0 ? 0 : pos;
        pos = pos > fileInfo.length ? fileInfo.length : pos;
    }
    if(playState > 0)
    {
        if(relative)
        {
            const QByteArray tmp = (((pos >= 0) ? "+" : QString())+QString::number(pos)).toUtf8();
            if(osd)
            {
                Command(QStringList() << "osd-msg" << "seek" << tmp.constData());
            }
            else
            {
                Command(QStringList() << "seek" << tmp.constData());
            }
        }
        else
        {
            if(osd)
            {
                Command(QVariantList() << "osd-msg" << "seek" << pos << "absolute");
            }
            else
            {
                Command(QVariantList() << "seek" << pos << "absolute");
            }
        }
    }
}

void MpvCore::BrightnessUp()
{
    if(brightness < 100)
    {
        brightness += 2;
        g_settings->setValue("General/brightness", brightness);
    }
    Command(QStringList() << "set" << "brightness" << QString::number(brightness));
    ShowText(tr("brightness : %1").arg((brightness+100)/2));
}

void MpvCore::BrightnessDown()
{
    if(brightness > -100)
    {
        brightness -= 2;
        g_settings->setValue("General/brightness", brightness);
    }
    Command(QStringList() << "set" << "brightness" << QString::number(brightness));
    ShowText(tr("brightness : %1").arg((brightness+100)/2));
}

void MpvCore::SubId(int id)
{
    Command(QStringList() << "set" << "sid" << QString::number(id));
}

void MpvCore::AudioId(int id)
{
    const char *args[] = {"set", "aid", QString("%1").arg(id).toStdString().c_str(), NULL};
    AsyncCommand(args);
}

void MpvCore::AudioNext()
{
    if (currentAid < atracks) {
        const char *args[] = {"set", "aid", QString("%1").arg(currentAid++).toStdString().c_str(), NULL};
        AsyncCommand(args);
    }
    else {
        const char *args[] = {"set", "aid", QString("%1").arg(aid).toStdString().c_str(), NULL};
        AsyncCommand(args);
    }
}

/**
** @brief         : 下一帧
** @param[in]     :
** @param[out]    :
** @return        :
***/
void MpvCore::NextFrame()
{
    const char *args[] = {"frame_step", NULL};
    AsyncCommand(args);
}

/**
** @brief         : 上一帧
** @param[in]     :
** @param[out]    :
** @return        :
***/
void MpvCore::PrevFrame()
{
    const char *args[] = {"frame_back_step", NULL};
    AsyncCommand(args);
}

/**
** @brief         : 添加字幕
** @param[in]     : sub 要添加的字幕文件
** @param[out]    :
** @return        :
***/
void MpvCore::AddSub(QString sub)
{
    // 0 为没有字幕
    int sub_index = m_subs.indexOf(sub) + 1;
    qDebug() << sub << " | " << sub_index;
    if(sub_index > 0) {
        SubId(sub_index);
        return;
    }
    Command(QStringList() << "sub_add" << sub);
    m_subs.push_back(sub);
    LoadTracks();
}

/**
** @brief         : 添加多个字幕
** @param[in]     : files 要添加的多个字幕文件
** @param[out]    :
** @return        :
***/
void MpvCore::AddSubs(QStringList files)
{
    for(auto sub : files)
    {
        AddSub(sub);
    }
}

/**
** @brief         : 字幕上移
** @param[in]     :
** @param[out]    :
** @return        :
***/
void MpvCore::SubMoveUp()
{
    if(subPos > 0)
        subPos -= 1;
    else
        return;
    SetOption("sub-pos", QString::number(subPos));
}

/**
** @brief         : 字幕下移
** @param[in]     :
** @param[out]    :
** @return        :
***/
void MpvCore::SubMoveDown()
{
    if(subPos < 100)
        subPos += 1;
    else
        return;
    SetOption("sub-pos", QString::number(subPos));
}

/**
** @brief         : 下一个字幕
** @param[in]     :
** @param[out]    :
** @return        :
***/
void MpvCore::SubNext()
{
    if(sid < subCount-1)
        Command(QStringList() << "set" << "sid" << QString::number(sid+1));
    else
        Command(QStringList() << "set" << "sid" << QString::number(0));
}

void MpvCore::SubForward()
{
    subDelay -= 0.5;
    Command(QStringList() << "set" << "sub-delay" << QString::number(subDelay));
    ShowText(tr("subtitle delay : %1s").arg(subDelay));
}

void MpvCore::SubBackward()
{
    subDelay += 0.5;
    Command(QStringList() << "set" << "sub-delay" << QString::number(subDelay));
    ShowText(tr("subtitle delay : %1s").arg(subDelay));
}

void MpvCore::SubFontSize(int size)
{
    SetOption("sub-font-size", QString("%1").arg(size));
}

void MpvCore::SubFontFamily(QString family)
{
    SetOption("sub-font", family);
}

// 添加书签
// desc : 书签描述
void MpvCore::AddBookMark(QString desc)
{
    QFileInfo fi(m_playingFile);
    if (!fi.exists() || playState < 0) {
        ShowText(tr("Add mark error"));
        return;
    }

    // 获取一个截图，作为预览图
    // 判断预览文件夹是否存在
    QThread::create([this, desc](){
        // 获取书签数据表名字，如果文件夹不存在的话创建文件夹
        QString mark_dir = Paths::configPath().append("/").append(g_sqlite->getMarkCharacter(m_playingFile));
        QDir d;
        if (!d.exists(mark_dir)) {
            if (!d.mkdir(mark_dir)) {
                qDebug() << "create " << mark_dir << " error";
                return;
            }
        }
        QString mark_view = mark_dir.append("/").append(QString::number(m_current_time)).append(".png");

        // 如果只有音频的话预览图为默认图片
        if (vid < 0 || !isVideo)
            mark_view = ":/ico/no-preview.png";
        else {
            // 保存当前图片
            m_videoTbr->setSeekTime(Functions::timeToStr(m_current_time).toStdString());
            m_videoTbr->generateThumbnail(m_playingFile.toStdString(), Png, mark_view.toStdString());
        }
        g_core_signal->sigMarkAdded(m_playingFile, m_current_time, desc, mark_view);
        ShowText(tr("Add mark ok").append(" : ").append(Functions::timeToStr(m_current_time)));
    })->start();
}

void MpvCore::Aspect(Mpv::VideoAspect va)
{
    if (!isVideo || vid < 0) {
        return;
    }
    QString arg;
    switch (va) {
    case Mpv::AUTO:
        arg = "-1";
        break;
    case Mpv::DIV_4_3:
        arg = "4:3";
        break;
    case Mpv::DIV_16_9:
        arg = "16:9";
        break;
    case Mpv::FULL:
        return;
    }
    SetAspect(arg);
}

void MpvCore::SetAspect(QString scale)
{
    Command(QStringList() << "set" << "video-aspect-override" << scale);
}

/** **********************************************
 * 画面还原
 *************************************************/
void MpvCore::RestoreFrame()
{
    Command(QStringList() << "set" << "video-rotate" << "0");
    rotate = 0;
    if(isHFlip)
    {
        Command(QStringList() << "vf" << "del" << "hflip");
        isHFlip = false;
    }
    if(isVFlip)
    {
        Command(QStringList() << "vf" << "del" << "vflip");
        isVFlip = false;
    }
    Command(QStringList() << "set" << "video-aspect-override" << "-1");
    ShowText(tr("restore frame"));
}

/** **********************************************
 * 顺时针旋转
 ** **********************************************/
void MpvCore::ClockwiseRotate()
{
    if (!isVideo || vid < 0) {
        return;
    }
    rotate = (rotate + 90) % 360;
    Command(QStringList() << "set" << "video-rotate" << QString::number(rotate));
}

/** **********************************************
 * 逆时针旋转
 ** **********************************************/
void MpvCore::CounterClockwiseRotate()
{
    if (!isVideo || vid < 0) {
        return;
    }
    if(rotate >= 90)
        rotate = (rotate - 90) % 360;
    else
        rotate = 270;
    Command(QStringList() << "set" << "video-rotate" << QString::number(rotate));
}

/** **********************************************
 * 水平翻转
 ** **********************************************/
void MpvCore::FlipHorizontally()
{
    if (!isVideo || vid < 0) {
        return;
    }
    QString s = isHFlip ? "del" : "add";
    if(mpv_h && playState > 0)
    {
        Command(QStringList() << "vf" << s << "hflip");
        ShowText(tr("Horizontal Flip: ").append(isHFlip ? tr("close") : tr("open")));
    }
    isHFlip = !isHFlip;
}

/** **********************************************
 * 竖直翻转
 ** **********************************************/
void MpvCore::FlipVertically()
{
    if (!isVideo || vid < 0) {
        return;
    }
    QString s = isVFlip ? "del" : "add";
    if(mpv_h && playState > 0)
    {
        Command(QStringList() << "vf" << s << "vflip");
        ShowText(tr("Vertical Flip: ").append(isVFlip ? tr("close") : tr("open")));
    }
    isVFlip = !isVFlip;
}

void MpvCore::ScreenShot(bool with_sub)
{
    if(vid < 0 || !isVideo)
        return;
    const char *args[] = {"screenshot", (with_sub ? "subtitles" : (gsetup->screenShotCurrentSize.first ? "window" : "video")), NULL};
    AsyncCommand(args);
    if(canSaveScreenShot)
        ShowText(tr("ScreenShot OK"));
    else
        ShowText(tr("ScreenShot Failed, folder has no write permission or folder not exit."));
}

void MpvCore::ScreenshotFormat(QString s)
{
    SetOption("screenshot-format", s);
}

/**
** @brief         : 设置截图文件夹
** @param[in]     : dir : 要设置的文件夹绝对路径
** @param[out]    :
** @return        :
***/
void MpvCore::ScreenshotDirectory(QString dir)
{
    SetOption("screenshot-directory", dir);
    // 如果文件夹没有权限的话，每次截图都报错就行
    QFile fi(dir.append("/.test"));
    if(!fi.open(QIODevice::ReadWrite)) {
        canSaveScreenShot = false;
    }
    else {
        // 删除创建的临时文件
        QProcess::execute(QString("rm -rf ").append(dir));
        canSaveScreenShot = true;
    }
}

void MpvCore::ShowInfo(bool is_show)
{
    if(is_show)
    {
        showProfile = true;
        showInfoTimer->start();
    }
    else
    {
        showProfile = false;
        showInfoTimer->stop();
    }
    ShowText("");
}

void MpvCore::ShowText(QString text, int duration)
{
    // 只有在播放或者暂停状态才去显示
    if (playState < 0 || this->duration < 0.5)
        return;

    // 其他osd内容显示的时候停止profile刷新，显示完成后profile刷新继续
    if(text != "" && showProfile)
    {
        showInfoTimer->stop();
        QTimer::singleShot(2400, [&](){if(showProfile) showInfoTimer->start();});
    }
    // 如果打开osd显示信息开关则需要将显示内容追加到fps和profile信息之后
    if(showProfile)
    {
        QString tab = "   ";
        QString nl = "\n";
        QString path = fileInfo.file_path;
        mpv_get_property(mpv_h, "estimated-vf-fps", MPV_FORMAT_DOUBLE, &fps);

        QString info = tr("File:") + path.right(path.length() - path.lastIndexOf("/") - 1) + nl +
                nl;
        QString infov =
                tr("Video:") + QString(" (x%0) %1").arg(QString::number(vtracks)).arg(fileInfo.video_params.codec) + nl +
                tab + tr("Resolution:") + QString(" %0x%1").arg(fileInfo.video_params.width).arg(fileInfo.video_params.height) + nl +
                tab + tr("fps:") + QString(" %0").arg(fps) + nl +
                tab + tr("Bitrate:") + QString(" %0").arg(videoBitrate==0 ? "(unavailable)" : QString::number((double)videoBitrate/1000.0).append("kbps")) + nl +
                nl;
        QString infoa =
                tr("Audio:") + QString(" (x%0) %1").arg(QString::number(vtracks)).arg(fileInfo.audio_params.codec) + nl +
                tab + tr("Sample Rate:") + QString(" %0Hz").arg(fileInfo.audio_params.samplerate) + nl +
                tab + tr("Channels:") + QString(" %0").arg(fileInfo.audio_params.channels) + nl +
                tab + tr("Bitrate:") + QString(" %0").arg(audioBitrate==0 ? "(unavailable)" : QString::number((double)audioBitrate/1000.0).append("kbps")) + nl +
                nl;
        if(vtracks > 0)
            info += infov;
        if(atracks > 0)
            info += infoa;
        if(vtracks > 0 && atracks > 0)
            info += tr("Audio/video synchronization:") + QString(" %0ms").arg(avsync);

        text = QString("%1\n%2").arg(info).arg(text);
    }

    if (vid >= 0) {
        const QByteArray tmp1 = text.toUtf8(),
                         tmp2 = QString::number(duration).toUtf8(),
                         tmp3 = QString::number(2).toUtf8();
        const char *args[] = {"show_text", tmp1.constData(), tmp2.constData(), tmp3.constData(), NULL};
        AsyncCommand(args);
    }
    else {
        // 让无视频流界面也显示一下
        emit sigShowText(text);
    }
}

void MpvCore::VideoDecoder(QString _decoder)
{
    mpv::qt::set_option_variant(mpv_h, "hwdec", _decoder);
    if(playState > 0)
    {
        Restart();
    }
}

void MpvCore::VideoOutput(QString output)
{
    mpv::qt::set_option_variant(mpv_h, "vo", output+",");
    if(playState > 0)
    {
        Restart();
    }
}

void MpvCore::DecodeThreads(int threads)
{
    mpv::qt::set_option_variant(mpv_h, "vd-lavc-threads", threads);
    if(playState > 0)
    {
        Restart();
    }
}

void MpvCore::LoadFileInfo()
{
    std::lock_guard<std::mutex> lg(m_mtxFileInfo);
    char *property_string = nullptr;
    isHFlip = false;
    isVFlip = false;
    vtracks = atracks = stracks = 0;
    fileInfo.file_path = m_playingFile;
    // get media-title
    property_string = mpv_get_property_string(mpv_h, "media-title");
    if(property_string) {
        fileInfo.media_title = property_string;
        mpv_free(property_string);
    }
    // get length
    double len;
    mpv_get_property(mpv_h, "duration", MPV_FORMAT_DOUBLE, &len);
    fileInfo.length = (int)len;

    LoadTracks();
    LoadChapters();
    LoadVideoParams();
    LoadAudioParams();
    LoadMetadata();

    getMediaInfo();

    // 通知其他人播放的文件改变了
    g_core_signal->notifyFileInfo(fileInfo);

    mpv_get_property(mpv_h, "vid", MPV_FORMAT_INT64, &vid);
    g_core_signal->notifyVideoId(vid);
    mpv_get_property(mpv_h, "sid", MPV_FORMAT_INT64, &sid);
    g_core_signal->notifySubId(sid);
    mpv_get_property(mpv_h, "aid", MPV_FORMAT_INT64, &aid);
    g_core_signal->notifyAudioId(aid);
    mpv_get_property(mpv_h, "sub-pos", MPV_FORMAT_INT64, &subPos);

}

void MpvCore::SetProperties()
{
    volume = g_settings->value("General/volume").toInt();
    Volume(volume);
    Speed(g_settings->value("General/speed").toDouble());
    Mute(g_settings->value("General/mute").toBool());
    // 声道设置
    Channel((Mpv::Channel)(gsetup->audioChannel.first+1));
}

void MpvCore::initMpvHandle()
{
    m_videoTbr = new VideoThumbnailer;
    m_videoTbr->setThumbnailSize(176);

    mpv_h = mpv::qt::Handle::FromRawHandle(mpv_create());
    if (!mpv_h)
    {
        log_e("could not create mpv context");
        qDebug("could not create mpv context");
        return;
    }
    mpv_set_option_string(mpv_h, "terminal", "yes");
    mpv_set_option_string(mpv_h, "msg-level", "all=error");

    if (mpv_initialize(mpv_h) < 0)
    {
        log_e("could not initialize mpv context");
    }


    // Request hw decoding
    QString t_video_decoder = g_settings->value("General/video_decoder").toString();
    if(t_video_decoder == "")
        t_video_decoder = "default";

//    SetOption("vo", "opengl-cb");
    SetOption("hwdec", t_video_decoder + ",");
    SetOption("vf", "fps");
    SetOption("wid", QString::number(m_playWidget->winId()));

    mpv_observe_property(mpv_h, 0, "mute",          MPV_FORMAT_FLAG);
    mpv_observe_property(mpv_h, 0, "volume",        MPV_FORMAT_INT64);
    mpv_observe_property(mpv_h, 0, "duration",      MPV_FORMAT_DOUBLE);
    mpv_observe_property(mpv_h, 0, "time-pos",      MPV_FORMAT_DOUBLE);
    mpv_observe_property(mpv_h, 0, "speed",         MPV_FORMAT_DOUBLE);
    mpv_observe_property(mpv_h, 0, "video-bitrate", MPV_FORMAT_DOUBLE);
    mpv_observe_property(mpv_h, 0, "audio-bitrate", MPV_FORMAT_DOUBLE);
    mpv_observe_property(mpv_h, 0, "avsync",        MPV_FORMAT_DOUBLE);
    mpv_set_wakeup_callback(mpv_h, wakeup, this);
//    m_playWidget->setMpvHandle(mpv_h);

    volume = g_settings->value("General/volume").toInt();
    brightness = g_settings->value("General/brightness").toInt();

    // osd显示文件信息
    showInfoTimer = new QTimer;
    showInfoTimer->setInterval(500);
    connect(showInfoTimer, &QTimer::timeout, [&](){
        ShowText("");
    });
}

/** **********************************************
 * 绑定全局信号，主要控制用
 ** **********************************************/
void MpvCore::initGlobalSig()
{
    connect(g_user_signal, &GlobalUserSignal::sigForword, [&](bool b){
        int seekto;
        if(b) seekto = lastTime+10>fileInfo.length ? fileInfo.length : lastTime+10;
        else  seekto = lastTime+30>fileInfo.length ? fileInfo.length : lastTime+30;
        Seek(seekto);
        ShowText(QString("▶▶ ").append(int2ts(seekto, false)));
    });

    connect(g_user_signal, &GlobalUserSignal::sigBackword, [&](bool b){
        int seekto;
        if(b) seekto = lastTime-10<0 ? 0 : lastTime-10;
        else  seekto = lastTime-30<0 ? 0 : lastTime-30;
        Seek(seekto);
        ShowText(QString("◀◀ ").append(int2ts(seekto, false)));
    });

    connect(g_user_signal, &GlobalUserSignal::sigAudioNext, this, &MpvCore::AudioNext);
    connect(g_user_signal, &GlobalUserSignal::sigVideoDecoder, this, &MpvCore::VideoDecoder);
    connect(g_user_signal, &GlobalUserSignal::sigVideoOutput, this, &MpvCore::VideoOutput);
    connect(g_user_signal, &GlobalUserSignal::sigVideoDecodeThread, this, &MpvCore::DecodeThreads);
    connect(g_user_signal, &GlobalUserSignal::sigChannel, this, &MpvCore::Channel);
    connect(g_user_signal, &GlobalUserSignal::sigSeek, [&](int time){Seek(time);});
    connect(g_user_signal, &GlobalUserSignal::sigVolume, this, &MpvCore::Volume);
    connect(g_user_signal, SIGNAL(sigMute(bool)), this, SLOT(Mute(bool)));
    connect(g_user_signal, &GlobalUserSignal::sigVolumeUp, [&](){Volume(volume+10);});
    connect(g_user_signal, &GlobalUserSignal::sigVolumeDown, [&](){Volume(volume-10);});
    connect(g_user_signal, &GlobalUserSignal::sigAspect, this, &MpvCore::Aspect);
    connect(g_user_signal, &GlobalUserSignal::sigRestoreFrame, this, &MpvCore::RestoreFrame);
    connect(g_user_signal, &GlobalUserSignal::sigHorizontallyFlip, this, &MpvCore::FlipHorizontally);
    connect(g_user_signal, &GlobalUserSignal::sigVerticalFlip, this, &MpvCore::FlipVertically);
    connect(g_user_signal, &GlobalUserSignal::sigClockwiseRotate, this, &MpvCore::ClockwiseRotate);
    connect(g_user_signal, &GlobalUserSignal::sigCounterClockwiseRotate, this, &MpvCore::CounterClockwiseRotate);
    connect(g_user_signal, &GlobalUserSignal::sigBrightnessUp, this, &MpvCore::BrightnessUp);
    connect(g_user_signal, &GlobalUserSignal::sigBrightnessDown, this, &MpvCore::BrightnessDown);
    connect(g_user_signal, &GlobalUserSignal::sigRestart, this, &MpvCore::Restart);
    connect(g_user_signal, &GlobalUserSignal::sigStop, this, &MpvCore::Stop);
    connect(g_user_signal, &GlobalUserSignal::sigPlay, this, &MpvCore::Play);
    connect(g_user_signal, &GlobalUserSignal::sigPause, this, &MpvCore::Pause);
    connect(g_user_signal, &GlobalUserSignal::sigSpeed, this, &MpvCore::Speed);
    connect(g_user_signal, &GlobalUserSignal::sigSpeedUp, this, &MpvCore::SpeedUp);
    connect(g_user_signal, &GlobalUserSignal::sigSpeedDown, this, &MpvCore::SpeedDown);
    connect(g_user_signal, &GlobalUserSignal::sigAudioId, this, &MpvCore::AudioId);
    connect(g_user_signal, &GlobalUserSignal::sigSubId, this, &MpvCore::SubId);
    connect(g_user_signal, &GlobalUserSignal::sigAddSub, this, &MpvCore::AddSub);
    connect(g_user_signal, &GlobalUserSignal::sigSubUp, this, &MpvCore::SubMoveUp);
    connect(g_user_signal, &GlobalUserSignal::sigSubDown, this, &MpvCore::SubMoveDown);
    connect(g_user_signal, &GlobalUserSignal::sigSubNext, this, &MpvCore::SubNext);
    connect(g_user_signal, &GlobalUserSignal::sigSubForward, this, &MpvCore::SubForward);
    connect(g_user_signal, &GlobalUserSignal::sigSubBackward, this, &MpvCore::SubBackward);
    connect(g_user_signal, &GlobalUserSignal::sigSubSize, this, &MpvCore::SubFontSize);
    connect(g_user_signal, &GlobalUserSignal::sigSubFont, this, &MpvCore::SubFontFamily);
    connect(g_user_signal, &GlobalUserSignal::sigAddBookMark, this, &MpvCore::AddBookMark);
    connect(g_user_signal, &GlobalUserSignal::sigScreenShot, this, &MpvCore::ScreenShot);
    connect(g_user_signal, &GlobalUserSignal::sigScreenShotDir, this, &MpvCore::ScreenshotDirectory);
    connect(g_user_signal, &GlobalUserSignal::sigScreenShotFormat, this, &MpvCore::ScreenshotFormat);
    connect(g_user_signal, &GlobalUserSignal::sigShowInfo, this, &MpvCore::ShowInfo);
    connect(g_core_signal, &GlobalCoreSignal::sig10FrameUseTime, [&](qint64 use_time){
        if(playState == Mpv::Paused)
            return;
        fps = 10000.0 / (double)use_time;
        fps = ((double)((int)((fps+0.005)*100)))/100;
    });
}

bool MpvCore::event(QEvent *event)
{
    if(event->type() == QEvent::User)
    {
        int sid, vid, aid;
        while(mpv_h)
        {
            mpv_event *event = mpv_wait_event(mpv_h, 0);
            if(event == nullptr || event->event_id == MPV_EVENT_NONE)
            {
                break;
            }
            switch (event->event_id)
            {
            case MPV_EVENT_PROPERTY_CHANGE:
            {
                mpv_event_property *prop = (mpv_event_property*)event->data;
                if(QString(prop->name) == "video-bitrate" && prop->format == MPV_FORMAT_DOUBLE)
                {
                    videoBitrate = *(double*)prop->data;
                }
                else if(QString(prop->name) == "audio-bitrate" && prop->format == MPV_FORMAT_DOUBLE)
                {
                    audioBitrate = *(double*)prop->data;
                }
                else if(QString(prop->name) == "avsync" && prop->format == MPV_FORMAT_DOUBLE)
                {
                    avsync = *(double*)prop->data;
                    if(avsync < 0)
                        avsync = -avsync;
                    avsync = ((double)(int)(avsync * 10000 * 1000 + 0.5)) / 1000;
                }
                else if(QString(prop->name) == "duration") // playback-time does the same thing as time-pos but works for streaming media
                {
                    if(prop->format == MPV_FORMAT_DOUBLE)
                    {
                        KyInfo() << " file started : " << QDateTime::currentMSecsSinceEpoch();
                        duration = *(double*)prop->data;

                        // 重启之后是否需要跳转，获取到总长度才能跳转
                        if (needSeek)
                            Seek(seekTime);
                        needSeek = false;

                        g_core_signal->notifyDuration(m_playingFile, duration);
                    }
                    // 从上次停止位置播放，收到时长改变才能跳转，不然会跳转失败
                    if (gsetup->playLastPos.first) {
                        Seek(lastStopTime);
                    }
                }
                else if(QString(prop->name) == "time-pos") // playback-time does the same thing as time-pos but works for streaming media
                {
                    if(prop->format == MPV_FORMAT_DOUBLE)
                    {
                        lastTime = *(double*)prop->data;
                        m_current_time = lastTime;
                        g_core_signal->notifyCurrentTime(lastTime);
                    }
                }
                else if(QString(prop->name) == "volume")
                {
                    if(prop->format == MPV_FORMAT_INT64)
                    {
                        g_core_signal->notifyVolume(*(int*)prop->data);
                        ShowText(QString(tr("volume : %1")).arg(*(int*)prop->data));
                    }
                }
                else if(QString(prop->name) == "speed")
                {
                    speed = *(double*)prop->data;
                    g_core_signal->notifySpeed(speed);
                    ShowText(QString(tr("speed : %1x")).arg(*(double*)prop->data));
                }
                else if(QString(prop->name) == "sid")
                {
                    if(prop->format == MPV_FORMAT_INT64)
                    {
                        setSid(*(int*)prop->data);
                    }
                }
                else if(QString(prop->name) == "aid")
                {
                    if(prop->format == MPV_FORMAT_INT64)
                        setAid(*(int*)prop->data);
                }
                else if(QString(prop->name) == "sub-visibility")
                {
                    if(prop->format == MPV_FORMAT_FLAG)
                        setSubtitleVisibility((bool)*(unsigned*)prop->data);
                }
                else if(QString(prop->name) == "mute")
                {
                    if(prop->format == MPV_FORMAT_FLAG)
                    {
                        g_core_signal->notifyMute((bool)*(unsigned*)prop->data);
                        ShowText((bool)*(unsigned*)prop->data ? tr("Mute") : tr("Cancel Mute"));
                    }
                }
                else if(QString(prop->name) == "core-idle")
                {
                    if(prop->format == MPV_FORMAT_FLAG)
                    {
//                        if((bool)*(unsigned*)prop->data && playState == Mpv::Playing)
//                            ShowText(tr("Buffering..."), 0);
//                        else
//                            ShowText(QString(), 0);
                    }
                }
                else if(QString(prop->name) == "paused-for-cache")
                {
                    if(prop->format == MPV_FORMAT_FLAG)
                    {
                        if((bool)*(unsigned*)prop->data && playState == Mpv::Playing)
                            ShowText(tr("Your network is slow or stuck, please wait a bit"), 0);
                        else
                            ShowText(QString(), 0);
                    }
                }
                break;
            }
            case MPV_EVENT_IDLE:
                fileInfo.length = 0;
                playState = Mpv::Idle;
                g_core_signal->notifyCurrentTime(0);
                g_core_signal->notifyPlayState(Mpv::Idle);
                break;
                // these two look like they're reversed but they aren't. the names are misleading.
            case MPV_EVENT_START_FILE:
                g_core_signal->notifyPlayState(Mpv::Started);
                playState = Mpv::Started;
                break;
            case MPV_EVENT_FILE_LOADED:
                isLoaded = true;
                LoadFileInfo();
                SetProperties();
                isManualStop = false;
                g_core_signal->notifyPlayState(Mpv::Loaded);
                playState = Mpv::Loaded;
                //加载之后上次停止时间需要设置为0
                g_sqlite->updateLastTime(m_playingFile, 0);

                Play();
                ShowText("");
                // 不要 break 有莫名其妙的问题，待查
            case MPV_EVENT_UNPAUSE:
                // 防止重复设置状态
                if(playState == Mpv::Playing || playState < 0)
                    break;
                g_core_signal->notifyPlayState(Mpv::Playing);
                if(playState == Mpv::Paused)
                    ShowText(tr("Playing"));
                playState = Mpv::Playing;
                break;
            case MPV_EVENT_PAUSE:
                if (playState == Mpv::Paused || playState < 0)
                    break;
                g_core_signal->notifyPlayState(Mpv::Paused);
                playState = Mpv::Paused;
                if (this->duration >= 0)
                    QTimer::singleShot(100, [this](){ShowText(tr("Paused"));});
                break;
            case MPV_EVENT_END_FILE:
                duration = -1;
                // 如果没有 loaded 说明文件打开失败（如果快速切换的时候会偶尔没有加载上就需要停止，需要单独处理）
                if (!isLoaded) {
                    g_core_signal->notifyFileLoadedError(m_playingFile);
                    lastTime = 0;
                }
                isLoaded = false;
                m_subs.clear();
                mediaInfo = QString();
                playState = Mpv::Stopped;
                videoBitrate = 0;
                audioBitrate = 0;
                g_core_signal->notifyPlayState(Mpv::Stopped);
                this->vid = this->aid = this->sid = -1;
                if(!isManualStop)
                {
                    if(lastTime != 0)
                        g_user_signal->playNext(false);
                }
                break;
            case MPV_EVENT_SHUTDOWN:
                QCoreApplication::quit();
                break;
            case MPV_EVENT_LOG_MESSAGE:
            {
                mpv_event_log_message *message = static_cast<mpv_event_log_message*>(event->data);
                if(message != nullptr)
                    qDebug() << message->text << " <<<<<<<<<<<<<<<<<<<<<<<<<";
                break;
            }
            case MPV_EVENT_TRACK_SWITCHED:
                // 轨道id改变
                mpv_get_property(mpv_h, "sid", MPV_FORMAT_INT64, &sid);
                g_core_signal->notifySubId(sid);
                if(sid != this->sid && sid >= 0)
                {
                    this->sid = sid;
                    // 字幕改变
                    for(Mpv::Track tck : fileInfo.tracks)
                        if(tck.type == "sub" && tck.id == sid)
                            ShowText(tr("subtitle : ").append(tck.title));
                }
                mpv_get_property(mpv_h, "aid", MPV_FORMAT_INT64, &aid);
                g_core_signal->notifyAudioId(aid);
                mpv_get_property(mpv_h, "vid", MPV_FORMAT_INT64, &vid);
                g_core_signal->notifyVideoId(vid);
                break;
            default: // unhandled events
                break;
            }
        }
        return false;
    }
    return QObject::event(event);
}
