// Copyright (C) 2020 ~ 2021 Uniontech Software Technology Co.,Ltd.
// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "record_process.h"
#include "utils/configsettings.h"
#include "utils.h"
#include "utils/audioutils.h"
#include "gstrecord/gstinterface.h"
#include "utils/log.h"
#ifdef KF5_WAYLAND_FLAGE_ON
#include "waylandrecord/avlibinterface.h"
#include "waylandrecord/waylandintegration_p.h"
#include "waylandrecord/recordadmin.h"
#endif
#include "ext-image-capture/extcapturerecorder.h"
#include "ext-image-capture/extcapturebridge.h"
#include <QApplication>
#include <QDate>
#include <QDebug>
#include <QDir>
#include <QStandardPaths>
#include <QtDBus>
#include <QScreen>
#include <QMetaType>
#include <QClipboard>
#include <QUrl>
#include <QEventLoop>
#include <QTimer>
#include <dlfcn.h>
#include <signal.h>

const int RecordProcess::RECORD_MOUSE_NULL = 0;
const int RecordProcess::RECORD_MOUSE_CURSE = 1;
const int RecordProcess::RECORD_MOUSE_CHECK = 2;
const int RecordProcess::RECORD_MOUSE_CURSE_CHECK = 3;

const int RecordProcess::RECORD_FRAMERATE_5 = 5;
const int RecordProcess::RECORD_FRAMERATE_10 = 10;
const int RecordProcess::RECORD_FRAMERATE_20 = 20;
const int RecordProcess::RECORD_FRAMERATE_24 = 24;
const int RecordProcess::RECORD_FRAMERATE_30 = 30;

// 用于任务栏图标DBus通信调用的函数
const QString DBUS_FUNC_ON_START = "onStart";
const QString DBUS_FUNC_ON_STOP = "onStop";
const QString DBUS_FUNC_ON_RECORDING = "onRecording";
const QString DBUS_FUNC_ON_PAUSE = "onPause";

/**
   @brief 调用托盘图标"录制中"的对应函数，改变显示状态
 */
static QDBusMessage callTrayTimeIcon(const QString &function)
{
    qCDebug(dsrApp) << "Calling tray time icon function:" << function;
    QDBusInterface timeInterface("com.deepin.ScreenRecorder.time",
                                 "/com/deepin/ScreenRecorder/time",
                                 "com.deepin.ScreenRecorder.time",
                                 QDBusConnection::sessionBus());
    return timeInterface.call(function);
}

/**
   @brief 调用托盘图标"截图"的对应函数，改变显示状态
 */
static QDBusMessage callTrayShotIcon(const QString &function)
{
    qCDebug(dsrApp) << "Calling tray shot icon function:" << function;
    QDBusInterface shotInterface("com.deepin.ShotRecorder.PanelStatus",
                                 "/com/deepin/ShotRecorder/PanelStatus",
                                 "com.deepin.ShotRecorder.PanelStatus",
                                 QDBusConnection::sessionBus());
    return shotInterface.call(function);
}

/**
   @brief 调用托盘图标"录屏"的对应函数，改变显示状态
 */
static QDBusMessage callTrayRecorderIcon(const QString &function)
{
    qCDebug(dsrApp) << "Calling tray recorder icon function:" << function;
    QDBusInterface recorderInterface("com.deepin.ShotRecorder.Recorder.PanelStatus",
                                     "/com/deepin/ShotRecorder/Recorder/PanelStatus",
                                     "com.deepin.ShotRecorder.Recorder.PanelStatus",
                                     QDBusConnection::sessionBus());
    return recorderInterface.call(function);
}

static void closeFdoNotification(quint32 id)
{
    if (id == RecordProcess::RECORD_SAVING_NOTIFY_ID_INVALID)
        return;
    QDBusInterface notification("org.freedesktop.Notifications",
                                "/org/freedesktop/Notifications",
                                "org.freedesktop.Notifications",
                                QDBusConnection::sessionBus());
    notification.call("CloseNotification", id);
}

void RecordProcess::setRecordSavingNotifyId(quint32 id)
{
    m_recordSavingNotifyId = id;
}

RecordProcess::RecordProcess(QObject *parent) : QObject(parent)
{
    qCDebug(dsrApp) << "Record process control class initialized.";
    m_settings = ConfigSettings::instance();
    qCDebug(dsrApp) << "ConfigSettings instance obtained.";

    saveTempDir = QStandardPaths::standardLocations(QStandardPaths::TempLocation).first();
    qCDebug(dsrApp) << "Temporary save directory:" << saveTempDir;
    displayNumber = QString(std::getenv("DISPLAY"));
    qCDebug(dsrApp) << "Display number:" << displayNumber;


    qRegisterMetaType<QProcess::ProcessState>("ProcessState");
    m_recordingFlag = false;
    m_gstRecordX = nullptr;
    m_framerate = 0;
    qCDebug(dsrApp) << "Recording flag, gstRecordX, and framerate initialized.";
}

RecordProcess::~RecordProcess()
{
    qCDebug(dsrApp) << "RecordProcess destructor called.";
    /*
    if (m_pTranscodeProcess) {
        delete m_pTranscodeProcess;
        m_pTranscodeProcess = nullptr;
    }

    if (m_recorderProcess) {
        delete m_recorderProcess;
        m_recorderProcess = nullptr;
    }
    */
    if (m_gstRecordX) {
        qCDebug(dsrApp) << "Deleting m_gstRecordX.";
        delete m_gstRecordX;
        m_gstRecordX = nullptr;
    }
}
//设置录屏的基础信息
void RecordProcess::setRecordInfo(const QRect &recordRect, const QString &filename)
{
    qCDebug(dsrApp) << "Setting record information. Record Rect:" << recordRect << ", Filename:" << filename;
    m_recordRect = recordRect;
    saveAreaName = filename;
    m_recordType = m_settings->getValue("recorder", "format").toInt();
    m_audioType = m_settings->getValue("recorder", "audio").toInt();
    m_mouseType = m_settings->getValue("recorder", "cursor").toInt();
    m_framerate = m_settings->getValue("recorder", "frame_rate").toInt();
    qCDebug(dsrApp) << "Record type, audio type, mouse type, and framerate obtained.";
}

//开始将mp4视频转码成gif
void RecordProcess::onStartTranscode()
{
    qCDebug(dsrApp) << "Starting video transcoding (mp4 to gif)...";

    qCDebug(dsrApp) << "Attempting to convert palette first.";
    QString captureTempDir = saveTempDir;
    QString captureSavePath = savePath;

    QThreadPool::globalInstance()->start([ = ](){
        QString cachePalette = captureTempDir + QDir::separator() + "deepin_screen_recorder_palette.png";
        QProcess paletteProcess;
        qCDebug(dsrApp) << "Starting palette generation process.";
        paletteProcess.start("ffmpeg", {"-i", captureSavePath, "-vf", "select=not(mod(n\\,30)),palettegen=stats_mode=diff", cachePalette, "-y"});
        qCDebug(dsrApp) << "Palette process command:" << paletteProcess.program() << paletteProcess.arguments().join(' ');

        if (!paletteProcess.waitForFinished(10 * 60 * 1000)) {
            qCWarning(dsrApp) << "Convert palette failed!" << paletteProcess.errorString();
            cachePalette.clear();
        }
        qCDebug(dsrApp) << "Palette generation finished. Cache palette path:" << cachePalette;

        QMetaObject::invokeMethod(this, [ = ](){
            onTranscodePaletteFinished(cachePalette);
        }, Qt::QueuedConnection);
        qCDebug(dsrApp) << "Invoked onTranscodePaletteFinished.";
    });
}

void RecordProcess::onTranscodePaletteFinished(const QString &palettePng)
{
    qCDebug(dsrApp) << "Transcode palette finished callback. Palette PNG:" << palettePng;
    QProcess *transcodeProcess = new QProcess(this);

#if (QT_VERSION_MAJOR == 5)
    connect(transcodeProcess, QOverload<QProcess::ProcessError>::of(&QProcess::error),
            [ = ](QProcess::ProcessError processError) {
                qCDebug(dsrApp) << "Transcode process error (Qt5):" << processError;
            });
#elif (QT_VERSION_MAJOR == 6)
    // Qt6 syntax for error signal
    connect(transcodeProcess, &QProcess::errorOccurred,
            [ = ](QProcess::ProcessError processError) {
                qCDebug(dsrApp) << "Transcode process error (Qt6):" << processError;
            });
#endif

#if (QT_VERSION_MAJOR == 5)
    // Qt6 syntax for finished signal
    connect(transcodeProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
            [ = ](int exitCode, QProcess::ExitStatus exitStatus) {
                qCDebug(dsrApp) << "Transcode process finished (Qt5). Exit Code:" << exitCode << ", Exit Status:" << exitStatus;
                //转换进程是否正常退出
                if (exitStatus == QProcess::ExitStatus::NormalExit) {
                    qCDebug(dsrApp) << "Transcode process exited normally, calling onTranscodeFinish.";
                    onTranscodeFinish();
                } else {
                    qCDebug(dsrApp) << "Transcode process crashed!";
                }
            });
#elif (QT_VERSION_MAJOR == 6)
    // Qt6 syntax for finished signal
    connect(transcodeProcess, &QProcess::finished,
            [ = ](int exitCode, QProcess::ExitStatus exitStatus) {
                qCDebug(dsrApp) << "Transcode process finished (Qt6). Exit Code:" << exitCode << ", Exit Status:" << exitStatus;
                //转换进程是否正常退出
                if (exitStatus == QProcess::ExitStatus::NormalExit) {
                    qCDebug(dsrApp) << "Transcode process exited normally, calling onTranscodeFinish.";
                    onTranscodeFinish();
                } else {
                    qCDebug(dsrApp) << "Transcode process crashed!";
                }
            });
#endif

    QString path = savePath;
    QStringList arg;
    arg << "-i";
    arg << savePath;
    qCDebug(dsrApp) << "Transcoding arguments setup. Input path:" << savePath;

    if (!palettePng.isEmpty()) {
        qCDebug(dsrApp) << "Palette PNG is not empty, adding as input.";
        arg << "-i";
        arg << palettePng;
        arg << "-filter_complex";
        arg << "fps=12,paletteuse=dither=none:diff_mode=rectangle";
    }

    arg << "-r";
    arg << "12";
    arg << path.replace("mp4", "gif");
    transcodeProcess->start("ffmpeg", arg);
    qCDebug(dsrApp) << transcodeProcess->program() << transcodeProcess->arguments().join(' ');

    //部分hw arm架构的机型需要这样设置
#if defined (__aarch64__)
    if (Utils::isWaylandMode) {
        qCDebug(dsrApp) << "Waiting transcode gif end (aarch64 Wayland)...";
        transcodeProcess->waitForFinished();
    }
#endif
}

//转码完成后通知栏弹出提示
void RecordProcess::onTranscodeFinish()
{
    qCDebug(dsrApp) << "Transcoding finished callback.";
    QString path = savePath;
    QString gifOldPath = path.replace("mp4", "gif");
    QString gifNewPath = QDir(saveDir).filePath(saveBaseName).replace(QString("mp4"), QString("gif"));
    qCDebug(dsrApp) << "Original path:" << savePath << ", Old GIF path:" << gifOldPath << ", New GIF path:" << gifNewPath;
    QFile::rename(gifOldPath, gifNewPath);
    qCDebug(dsrApp) << "GIF file renamed.";
    exitRecord(gifNewPath);
}

//录屏结束后弹出通知
void RecordProcess::onRecordFinish()
{
    qCDebug(dsrApp) << "Record finish callback: Ending screen recording...";
    //x11录屏结束
    if (!Utils::isWaylandMode) {
        qCDebug(dsrApp) << "X11 recording mode.";
        if (QProcess::ProcessState::NotRunning != m_recorderProcess->exitCode()) {
            qCDebug(dsrApp) << "Recorder process exited with an error.";
            foreach (auto line, (m_recorderProcess->readAllStandardError().split('\n'))) {
                qCDebug(dsrApp) << "Recorder process error output:" << line;
            }
        } else {
            qCDebug(dsrApp) << "Recorder process exited normally. Output:" << m_recorderProcess->readAllStandardOutput() << ", Error:" << m_recorderProcess->readAllStandardError();
        }
    }

    // Move file to save directory.
    QString newSavePath = QDir(saveDir).filePath(saveBaseName);
    QFile::remove(newSavePath);
    QFile::rename(savePath, newSavePath);
    exitRecord(newSavePath);
}

//x11录制视频
void RecordProcess::recordVideo()
{
    qCDebug(dsrApp) << "Starting X11 FFmpeg video recording.";
    initProcess();
    //取系统音频的通道号
    AudioUtils *audioUtils = new AudioUtils();
    QString t_currentAudioChannel = audioUtils->currentAudioChannel();
    //-1表示系统音频的通道号错误
    if (t_currentAudioChannel == "-1") {
        qCWarning(dsrApp) << "Current system audio channel error!";
        //系统音频通道获取错误时，要么不录制声音，要么只录制麦克风音频
        if (m_audioType == Utils::kSystemAudio) {
            m_audioType = Utils::kNoAudio;
            qCWarning(dsrApp) << "Selected audio changed: System audio changed to no audio!";
        } else if (m_audioType == Utils::kMicAndSystemAudio) {
            m_audioType = Utils::kMic;
            qCWarning(dsrApp) << "Selected audio changed: Mix audio changed to microphone only!";
        }
    }
    qCDebug(dsrApp) << "Current system audio channel:" << t_currentAudioChannel;
    QStringList arguments;

    QString arch = QSysInfo::currentCpuArchitecture();

#if defined (__mips__) || defined (__sw_64__) || defined (__loongarch_64__) || defined (__loongarch__)
    // mips sw 视频编码 mpeg4 音频编码 mp3
    /*
         * mkv
         ffmpeg -f x11grab -draw_mouse 1 -framerate 25 -video_size 1920x1080 -i :0+0,0 -f pulse -i vokoscreenMix.monitor
        -pix_fmt yuv420p -c:v mpeg4 -c:a libmp3lame -q:v 1 -s 1920x1080 -f matroska ./out.mkv
        */
    /*
         * mp4
         ffmpeg -f x11grab -draw_mouse 1 -framerate 25 -video_size 1920x1080 -i :0+0,0 -f pulse -i vokoscreenMix.monitor
        -pix_fmt yuv420p -c:v mpeg4 -c:a libmp3lame -q:v 1 -s 1920x1080 -f mp4 ./out.mp4
        */
    arguments << QString("-f");
    arguments << QString("x11grab"); // 视频源
    arguments << QString("-framerate"); // 视频帧数
    arguments << QString("%1").arg(m_framerate);
    arguments << QString("-video_size"); // 视频分辨率
    arguments << QString("%1x%2").arg(m_recordRect.width()).arg(m_recordRect.height()); // 录制区域宽高
    if (m_mouseType == RECORD_MOUSE_NULL || m_mouseType == RECORD_MOUSE_CHECK) { // 不录制光标
        arguments << QString("-draw_mouse");
        arguments << QString("0");
    }
    arguments << QString("-thread_queue_size"); // 输入线程缓冲区大小
    arguments << QString("128");
    arguments << QString("-i");
    arguments << QString("%1+%2,%3").arg(displayNumber).arg(m_recordRect.x()).arg(m_recordRect.y()); // 录制区域左上角坐标
    if (m_audioType == Utils::kSystemAudio || m_audioType == Utils::kMicAndSystemAudio) {
        arguments << QString("-thread_queue_size");
        arguments << QString("2048");
        arguments << QString("-fragment_size");
        arguments << QString("2048");
        arguments << QString("-f");
        arguments << QString("pulse");// 音频源
        arguments << QString("-ac"); // 输出通道数
        arguments << QString("2");
        arguments << QString("-ar"); // 音频采样率
        arguments << QString("44100");
        arguments << QString("-i"); // 系统音频id
        arguments << QString("%1").arg(t_currentAudioChannel);
    }
    if (m_audioType == Utils::kMic || m_audioType == Utils::kMicAndSystemAudio) {
        arguments << QString("-thread_queue_size");
        arguments << QString("2048");
        arguments << QString("-fragment_size");
        arguments << QString("2048");
        arguments << QString("-f");
        arguments << QString("pulse");
        arguments << QString("-ac");
        arguments << QString("2");
        arguments << QString("-ar");
        arguments << QString("44100");
        arguments << QString("-i"); // 麦克风音频id，值为固定"default"
        arguments << QString("default");
    }
    if (m_audioType == Utils::kMicAndSystemAudio) {
        arguments << QString("-filter_complex");
        arguments << QString("amerge");
    }

    arguments << QString("-pix_fmt"); // 像素格式
    arguments << QString("yuv420p");
    arguments << QString("-c:v"); // 视频编码器
    arguments << QString("libx264");
    //arguments << QString("mpeg2video");
    //arguments << QString("mpeg1video");
    //arguments << QString("libx264rgb");
    //arguments << QString("h263p");
    //arguments << QString("mjpeg");
    //arguments << QString("flv");
    //arguments << QString("-c:a"); // 音频编码器
    //arguments << QString("libmp3lame");
    arguments << QString("-crf"); // 视频质量，值越小，画质越好。
    arguments << QString("23");
    arguments << QString("-preset");
    arguments << QString("ultrafast");
    arguments << QString("-vsync");
    arguments << QString("passthrough");
    if (m_recordType == Utils::kMKV) {
        arguments << QString("-f");
        arguments << QString("matroska"); // mkv视频
    } else {
        arguments << QString("-f");
        arguments << QString("mp4"); // mp4视频
    }
    arguments << QString("-vf");
    arguments << QString("scale=trunc(iw/2)*2:trunc(ih/2)*2");
#else

    if (m_audioType == Utils::kSystemAudio || m_audioType == Utils::kMicAndSystemAudio) {
        qCDebug(dsrApp) << "X11 FFmpeg recording system audio: true.";
        arguments << QString("-thread_queue_size");
        arguments << QString("2048");
        arguments << QString("-fragment_size");
        arguments << QString("4096");
        arguments << QString("-f");
        arguments << QString("pulse");
        arguments << QString("-ac");
        arguments << QString("2");
        arguments << QString("-ar");
        arguments << QString("44100");
        arguments << QString("-i");
        arguments << QString("%1").arg(t_currentAudioChannel);
    }
    if (m_audioType == Utils::kMic || m_audioType == Utils::kMicAndSystemAudio) {
        qCDebug(dsrApp) << "X11 FFmpeg recording microphone: true.";
        arguments << QString("-thread_queue_size");
        arguments << QString("2048");
        arguments << QString("-fragment_size");
        arguments << QString("4096");
        arguments << QString("-f");
        arguments << QString("pulse");
        arguments << QString("-ac");
        arguments << QString("2");
        arguments << QString("-ar");
        arguments << QString("44100");
        arguments << QString("-i");
        arguments << QString("default");
    }

    // Audio settings need to be in front of.
    // Otherwise, audio/video desynchronisation may occur during recording.
    // Link: https://github.com/linuxdeepin/deepin-screen-recorder/pull/256
    arguments << QString("-video_size");
    arguments << QString("%1x%2").arg(m_recordRect.width()).arg(m_recordRect.height());
    if (m_mouseType == RECORD_MOUSE_NULL || m_mouseType == RECORD_MOUSE_CHECK) {
        qCDebug(dsrApp) << "X11 FFmpeg not recording cursor.";
        arguments << QString("-draw_mouse");
        arguments << QString("0");
    }
    arguments << QString("-framerate");
    arguments << QString("%1").arg(m_framerate);
    arguments << QString("-probesize");
    arguments << QString("24M");
    arguments << QString("-thread_queue_size");
    arguments << QString("64");
    arguments << QString("-f");
    arguments << QString("x11grab");
    arguments << QString("-i");
    arguments << QString("%1+%2,%3").arg(displayNumber).arg(m_recordRect.x()).arg(m_recordRect.y());
    
    //v25 arm上面参数需要放在最后
    if (m_audioType == Utils::kSystemAudio || m_audioType == Utils::kMicAndSystemAudio) {
        if (m_audioType == Utils::kSystemAudio) {
            if ((arch.startsWith("ARM", Qt::CaseInsensitive))) {
                arguments << QString("-af");
                arguments << QString("volume=20dB");
            }
        }
    }

    if (m_audioType == Utils::kMicAndSystemAudio) {
        qCDebug(dsrApp) << "X11 FFmpeg recording mixed audio: true.";
        arguments << QString("-filter_complex");
        if ((arch.startsWith("ARM", Qt::CaseInsensitive))) {
            arguments << QString("[0:a]volume=30dB[a1];[a1][1:a]amix=inputs=2:duration=first:dropout_transition=0[out]");
            arguments << QString("-map");
            arguments << QString("2:v");
            arguments << QString("-map");
            arguments << QString("[out]");
        } else {
            arguments << QString("amerge");
        }
    }

    arguments << QString("-c:v");
    arguments << QString("libx264");
    arguments << QString("-pix_fmt");
    arguments << QString("yuv420p");
    // baseline 算法，录制的视频在windos自带播放器不能播放
    //arguments << QString("-profile:v");
    //arguments << QString("baseline");
    if (m_recordType == Utils::kMKV) {
        arguments << QString("-qp");
        arguments << QString("23");
    } else {
        arguments << QString("-crf");
        arguments << QString("23");
    }
    arguments << QString("-preset");
    arguments << QString("ultrafast");
    arguments << QString("-vsync");
    arguments << QString("passthrough");
    arguments << QString("-vf");
    arguments << QString("scale=trunc(iw/2)*2:trunc(ih/2)*2");
#endif
    arguments << savePath;
    qCDebug(dsrApp) << "Final FFmpeg arguments:" << arguments;
    m_recorderProcess->start("ffmpeg", arguments);
    qCDebug(dsrApp) << "FFmpeg recording process started.";
}

//初始化x11 FFmpeg录屏进程
void RecordProcess::initProcess()
{
    m_recorderProcess = new QProcess(this);
    QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
    env.insert("GDK_SCALE", "1");
    m_recorderProcess->setProcessEnvironment(env);
    connect(m_recorderProcess, SIGNAL(finished(int)), m_recorderProcess, SLOT(deleteLater()));

    // Build temp save path.
    QString date = QDateTime::currentDateTime().toString("yyyyMMddhhmmss");
    QString fileExtension = "mp4";
    if (m_recordType == Utils::kMKV) {
        fileExtension = "mkv";
    }
    if(saveAreaName.isEmpty()){
        saveBaseName = QString("%1_%2.%3").arg(tr("Record")).arg(date).arg(fileExtension);
    }else{
        if(m_isFullScreenRecord){
            saveBaseName = QString("%1.%2").arg(saveAreaName).arg(fileExtension);
        }else {
            saveBaseName = QString("%1_%2_%3.%4").arg(tr("Record")).arg(saveAreaName).arg(date).arg(fileExtension);
        }
    }
    savePath = QDir(saveTempDir).filePath(saveBaseName);
    // Remove same cache file first.
    QFile::remove(savePath);
}

void RecordProcess::getScreenRecordSavePath()
{
    ConfigSettings *settings = ConfigSettings::instance();
    if (settings->getValue("recorder", "save_dir").toString() == "") {
        qCWarning(dsrApp) << "配置文件中录屏保存路径为空,采用默认路径";
        saveDir = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation).first() + QDir::separator() + "Screen Recordings" + QDir::separator();
    } else {
        int t_saveIndex = settings->getValue("recorder", "save_op").toInt();
        switch (t_saveIndex) {
        case 1: {
            //保存到桌面
            saveDir = settings->getValue("recorder", "save_dir").toString() + QDir::separator();
            qCInfo(dsrApp) << "保存到桌面: " << saveDir;
            break;
        }
        default:
            //保存到视频目录中的Screen Recordings目录
            saveDir = settings->getValue("recorder", "save_dir").toString() + QDir::separator() + "Screen Recordings" + QDir::separator();
            qCInfo(dsrApp) << "保存到视频目录: " << saveDir;
            break;
        }
    }
    if ((!QDir(saveDir).exists() && QDir().mkdir(saveDir) == false) ||   // 文件不存在，且创建失败
            (QDir(saveDir).exists() && !QFileInfo(saveDir).isWritable())) {   // 文件存在，且不能写
        saveDir = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation).first();
    }
    qCInfo(dsrApp) << "录屏保存目录: " << saveDir;
}

void RecordProcess::save2Clipboard(QString file)
{
    qCDebug(dsrApp) << "Saving file to clipboard:" << file;
    //录屏文件保存到剪切板
    QClipboard *cb = qApp->clipboard();
    // Ownership of the new data is transferred to the clipboard.
    QMimeData *newMimeData = new QMimeData();

    QByteArray gnomeFormat = QByteArray("copy\n");
    QString text;
    QList<QUrl> dataUrls;

    dataUrls << QUrl::fromLocalFile(file);
    gnomeFormat.append(QUrl::fromLocalFile(file).toEncoded()).append("\n");

    newMimeData->setText(text.endsWith('\n') ? text.left(text.length() - 1) : text);
    newMimeData->setUrls(dataUrls);

    gnomeFormat.remove(gnomeFormat.length() - 1, 1);

    newMimeData->setData("x-special/gnome-copied-files", gnomeFormat);

    cb->setMimeData(newMimeData, QClipboard::Clipboard);
    qCDebug(dsrApp) << "File saved to clipboard.";
}

//wayland录制视频
void RecordProcess::waylandRecord()
{
#ifdef KF5_WAYLAND_FLAGE_ON
    qCInfo(dsrApp) << "正在加载ffmpeg依赖库...";
    avlibInterface::initFunctions();
    qCInfo(dsrApp) << "ffmpeg依赖库已加载";
    qCDebug(dsrApp) << "wayland 录屏！";
    // 启动wayland录屏
    initProcess();
    AudioUtils *audioUtils = new AudioUtils();
    QStringList arguments;
    arguments << QString("%1").arg(m_recordType);
    arguments << QString("%1").arg(m_recordRect.width()) << QString("%1").arg(m_recordRect.height());
    arguments << QString("%1").arg(m_recordRect.x()) << QString("%1").arg(m_recordRect.y());
    arguments << QString("%1").arg(m_framerate);
    arguments << QString("%1").arg(savePath);
    arguments << QString("%1").arg(m_audioType);
    arguments << QString(audioUtils->getDefaultDeviceName(AudioUtils::DefaultAudioType::Source));
    arguments << QString(audioUtils->getDefaultDeviceName(AudioUtils::DefaultAudioType::Sink));
    qCDebug(dsrApp) << arguments;
    WaylandIntegration::init(arguments);
#endif
    return;
}

//treeland录制视频
void RecordProcess::treelandRecord()
{
    qCDebug(dsrApp) << "treeland ext-image-copy-capture 录屏！";
    
    initProcess();
    
    ExtCaptureRecorder *extRecorder = new ExtCaptureRecorder(this);
    
    // 检查协议可用性
    if (!extRecorder->isAvailable()) {
        qCWarning(dsrApp) << "ext-image-copy-capture协议暂未就绪，等待协议初始化...";
        
        QEventLoop waitLoop;
        QTimer timeoutTimer;
        timeoutTimer.setSingleShot(true);
        timeoutTimer.setInterval(1000); 
        
        connect(&timeoutTimer, &QTimer::timeout, &waitLoop, &QEventLoop::quit);
        
        QMetaObject::Connection protocolConnection;
        protocolConnection = connect(extRecorder, &ExtCaptureRecorder::protocolAvailable, 
                                   [&waitLoop, &protocolConnection]() {
            qCWarning(dsrApp) << "协议现在可用，继续录制";
            QObject::disconnect(protocolConnection);
            waitLoop.quit();
        });
        
        timeoutTimer.start();
        waitLoop.exec();
        
        if (!extRecorder->isAvailable()) {
            qCCritical(dsrApp) << "等待超时，ext-image-copy-capture协议仍不可用";
            delete extRecorder;
            return;
        } else {
            qCWarning(dsrApp) << "协议现在可用，继续录制流程";
        }
    }
    
    QScreen *screen = QGuiApplication::primaryScreen();
    if (!screen) {
        qCCritical(dsrApp) << "无法获取屏幕信息";
        delete extRecorder;
        return;
    }
    
    connect(extRecorder, &ExtCaptureRecorder::recordingStarted, this, [this]() {
        qCDebug(dsrApp) << "TreeLand录制已开始";
    });
    
    connect(extRecorder, &ExtCaptureRecorder::recordingStopped, this, [this]() {
        qCDebug(dsrApp) << "TreeLand录制已停止";
    });
    
    connect(extRecorder, &ExtCaptureRecorder::error, this, [this](const QString &message) {
        qCCritical(dsrApp) << "TreeLand录制错误:" << message;
    });
    
    bool startResult = extRecorder->startRecording(screen, m_mouseType != RECORD_MOUSE_NULL, savePath, m_framerate);
    
    if (!startResult) {
        qCCritical(dsrApp) << "启动TreeLand录制失败";
        delete extRecorder;
        return;
    }
    
    setProperty("extCaptureRecorder", QVariant::fromValue(extRecorder));
    
    qCInfo(dsrApp) << "TreeLand录制启动完成";
}

//gstreamer录制视频
void RecordProcess::GstStartRecord()
{
    int argc = 1;
    //gstreamer接口初始化
    qCInfo(dsrApp) << "正在加载gstreamer依赖库...";
    gstInterface::initFunctions();
    qCInfo(dsrApp) << "gstreamer依赖库已加载";
    gstInterface::m_gst_init(&argc, nullptr);
    qCDebug(dsrApp) << "Gstreamer 录屏开始！";
    GstRecordX::VideoType videoType = GstRecordX::VideoType::webm;
    GstRecordX::AudioType audioType = GstRecordX::AudioType::None;
    m_gstRecordX = new GstRecordX(this);
    //设置参数
    m_gstRecordX->setFramerate(m_framerate);
    m_gstRecordX->setRecordArea(m_recordRect);
    //这里设置音频设备名称（输入和输出），即使名称为空也不影响。
    AudioUtils *audioUtils = new AudioUtils();
    m_gstRecordX->setInputDeviceName(audioUtils->getDefaultDeviceName(AudioUtils::DefaultAudioType::Source));
    m_gstRecordX->setOutputDeviceName(audioUtils->getDefaultDeviceName(AudioUtils::DefaultAudioType::Sink));
    //这里才会设置究竟采集哪些音频设备的音频数据
    if (m_audioType == Utils::kMicAndSystemAudio) {
        audioType =  GstRecordX::AudioType::Mix;
    } else if (m_audioType == Utils::kMic) {
        audioType =  GstRecordX::AudioType::Mic;
    } else if (m_audioType == Utils::kSystemAudio) {
        audioType =  GstRecordX::AudioType::Sys;
    }
    m_gstRecordX->setAudioType(audioType);
    QDateTime date = QDateTime::currentDateTime();
    QString fileExtension = "webm";
    if (m_recordType == Utils::kMKV) {
        videoType =  GstRecordX::VideoType::ogg;
        fileExtension = "ogg";
    } else {
        videoType =  GstRecordX::VideoType::webm;
        fileExtension = "webm";
    }
    if(saveAreaName.isEmpty()){
        saveBaseName = QString("%1_%2.%3").arg(tr("Record")).arg(date.toString("yyyyMMddhhmmss")).arg(fileExtension);
    }else{
        if(m_isFullScreenRecord){
            saveBaseName = QString("%1.%2").arg(saveAreaName).arg(fileExtension);
        }else {
            saveBaseName = QString("%1_%2_%3.%4").arg(tr("Record")).arg(saveAreaName).arg(date.toString("yyyyMMddhhmmss")).arg(fileExtension);
        }
    }

    savePath = QDir(saveTempDir).filePath(saveBaseName);
    // Remove same cache file first.
    QFile::remove(savePath);
    m_gstRecordX->setVidoeType(videoType);
    m_gstRecordX->setSavePath(savePath);
    m_gstRecordX->setX11RecordMouse(m_mouseType);
    //开始录制
    if (Utils::isWaylandMode) {
#ifdef KF5_WAYLAND_FLAGE_ON
        //wayland下停止录屏需要通过信号槽控制，避免Gstreamer管道数据未写完程序就被退出了
        connect(m_gstRecordX, &GstRecordX::waylandGstRecrodFinish, this, &RecordProcess::onExitGstRecord);
        //wayland模式需打开视频画面采集
        QStringList arguments;
        arguments << QString("%1").arg(videoType);
        arguments << QString("%1").arg(m_recordRect.width()) << QString("%1").arg(m_recordRect.height());
        arguments << QString("%1").arg(m_recordRect.x()) << QString("%1").arg(m_recordRect.y());
        arguments << QString("%1").arg(m_framerate);
        arguments << QString("%1").arg(savePath);
        arguments << QString("%1").arg(m_audioType);
        qCDebug(dsrApp) << arguments;
        WaylandIntegration::init(arguments, m_gstRecordX);
#endif
    } else {
        m_gstRecordX->x11GstStartRecord();

    }
}

//gstreamer停止录制视频
void RecordProcess::GstStopRecord()
{
    if (Utils::isTreelandMode) {
        qCInfo(dsrApp) << "停止TreeLand GStreamer录屏...";
        
        ExtCaptureRecorder *extRecorder = property("extCaptureRecorder").value<ExtCaptureRecorder*>();
        ExtCaptureBridge *bridge = property("extCaptureBridge").value<ExtCaptureBridge*>();
        
        if (extRecorder) {
            extRecorder->stopRecording();
        }
        
        if (bridge) {
            bridge->stopBridge();
        }
        
        if (m_gstRecordX) {
            // 注意：TreeLand下可能没有使用GstRecordX，需要检查
            // m_gstRecordX->waylandGstStopRecord();
        }
        
        onExitGstRecord();
    }
    else if (Utils::isWaylandMode) {
#ifdef KF5_WAYLAND_FLAGE_ON
        WaylandIntegration::stopStreaming();
#endif
    } else {
        //x11 gstreamer录屏
        m_gstRecordX->x11GstStopRecord();
        onExitGstRecord();
    }
}

//退出gstreamer wayland录屏，需要Gstreamer录屏类触发
void RecordProcess::onExitGstRecord()
{
    QString newSavePath = QDir(saveDir).filePath(saveBaseName);
    QFile::remove(newSavePath);
    QFile::rename(savePath, newSavePath);
    //注销gstreamer相关库加载
    gstInterface::unloadFunctions();
    qCDebug(dsrApp) << "Gstreamer 录屏结束！";
    exitRecord(newSavePath);
}

//开始录屏
void RecordProcess::startRecord()
{
    m_recordSavingNotifyId = RECORD_SAVING_NOTIFY_ID_INVALID;
    getScreenRecordSavePath();
    //使用QtConcurrent::run受cpu核心线程数的影响，线程池默认大小为CPU核心线程数大小，由于程序中通过此方法启动的线程数超出4个，故再次设置线程池大小
    QThreadPool::globalInstance()->setMaxThreadCount(QThreadPool::globalInstance()->maxThreadCount() > 6 ? QThreadPool::globalInstance()->maxThreadCount() : 10);

    if (!Utils::isFFmpegEnv) {
        GstStartRecord();
    } else {
        if (Utils::isTreelandMode) {
            treelandRecord();
        }
        //x11下的录屏
        else if (!Utils::isWaylandMode) {
            recordVideo();
        }
        //wayland下的录屏
        else {
            waylandRecord();
        }
    }

    if (Utils::isSysHighVersion1040() == false) {
        qCInfo(dsrApp) << "系统版本小于1040, 不显示录制时长功能。";
        return;
    }
    //1040及以上的版本可通过此方式启动状态栏图标闪烁
    qCDebug(dsrApp) << "通知录屏插件开始录屏! currentTime: " << QTime::currentTime();
    QDBusMessage message = callTrayTimeIcon(DBUS_FUNC_ON_START);
    callTrayShotIcon(DBUS_FUNC_ON_START);
    callTrayRecorderIcon(DBUS_FUNC_ON_START);

    qCDebug(dsrApp) << "已通知录屏插件开始录屏! currentTime: " << QTime::currentTime();
    m_recordingFlag = true;
    
    // 发送录屏开始信号
    emit recordingStarted();
    qCDebug(dsrApp) << "Recording started signal emitted";
    
    QtConcurrent::run([this]() {
        this->emitRecording();
    });
    if (QDBusMessage::ReplyMessage == message.type()) {
        if (!message.arguments().takeFirst().toBool())
            qCDebug(dsrApp) << "dde dock screen-recorder-plugin did not receive start message!";
    }
}
void RecordProcess::emitRecording()
{
    while (m_recordingFlag) {
        //qDebug() << "录屏正在进行中! currentTime: " << QTime::currentTime();

        callTrayTimeIcon(DBUS_FUNC_ON_RECORDING);
        callTrayShotIcon(DBUS_FUNC_ON_RECORDING);
        callTrayRecorderIcon(DBUS_FUNC_ON_RECORDING);

        QThread::msleep(1000);
    }
}

void RecordProcess::setFullScreenRecord(bool flag)
{
    m_isFullScreenRecord = flag;
}

void RecordProcess::stopRecord()
{
    if (Utils::isSysHighVersion1040() == true) {
        qCInfo(dsrApp) << __FUNCTION__ << __LINE__ << "正在暂停录屏计时...";
        qCDebug(dsrApp) << "Pause the screen recording timer!";

        //系统托盘图标停止闪烁，时间暂停，但还没有结束
        QDBusMessage message = callTrayTimeIcon(DBUS_FUNC_ON_PAUSE);

        if (QDBusMessage::ReplyMessage == message.type()) {
            if (!message.arguments().takeFirst().toBool())
                qCDebug(dsrApp) << "dde dock screen-recorder-plugin did not receive stop message!";
        }
        callTrayShotIcon(DBUS_FUNC_ON_PAUSE);
        callTrayRecorderIcon(DBUS_FUNC_ON_PAUSE);

        qCInfo(dsrApp) << __FUNCTION__ << __LINE__ << "录屏计时已暂停";
    }
    if (Utils::isFFmpegEnv) {
        //停止treeland录屏
        if (Utils::isTreelandMode) {
            // 获取存储的组件实例
            ExtCaptureRecorder *extRecorder = property("extCaptureRecorder").value<ExtCaptureRecorder*>();
            
            // 改为"等待停止完成"的异步流程，确保底层完全停止后再收尾，避免残留帧
            if (extRecorder) {
                // 断开可能的重复连接，确保只触发一次
                disconnect(extRecorder, nullptr, this, nullptr);
                
                // 连接停止完成信号后再调用stopRecording
#if (QT_VERSION_MAJOR == 5)
                connect(extRecorder, &ExtCaptureRecorder::recordingStopped, this, [this, extRecorder]() {
#elif (QT_VERSION_MAJOR == 6)
                connect(extRecorder, &ExtCaptureRecorder::recordingStopped, this, [this, extRecorder]() {
#endif
                    qCInfo(dsrApp) << "TreeLand录屏已停止";
                    
                    // 清理对象引用
                    setProperty("extCaptureRecorder", QVariant());
                    if (extRecorder) extRecorder->deleteLater();
                    
                    // 停止完成后再执行收尾流程（转码/完成）
                    if (Utils::kGIF == m_recordType) {
                        onStartTranscode();
                    } else {
                        onRecordFinish();
                    }
                });
                
                // 执行停止
                extRecorder->stopRecording();
                
                // 等待异步回调，不要立刻继续后续逻辑
                return;
            } else {
                if (Utils::kGIF == m_recordType) {
                    onStartTranscode();
                } else {
                    onRecordFinish();
                }
            }
        }
        //停止wayland录屏
        else if (Utils::isWaylandMode) {
#ifdef KF5_WAYLAND_FLAGE_ON
            WaylandIntegration::stopStreaming();
            if (Utils::kGIF == m_recordType) {
                onStartTranscode();
            } else {
                onRecordFinish();
            }
#endif
        }
        //停止x11录屏
        else {
            //录制的视频类型是否是gif格式，是gif的话需要进行转码
            if (Utils::kGIF == m_recordType) {
                connect(m_recorderProcess, SIGNAL(finished(int)), this, SLOT(onStartTranscode()));
            } else {
                connect(m_recorderProcess, SIGNAL(finished(int)), this, SLOT(onRecordFinish()));
            }
            m_recorderProcess->write("q");
        }
    } else {
        GstStopRecord();
    }
}

//退出录屏（先停止，再弹提示，最后退出）
void RecordProcess::exitRecord(QString newSavePath)
{
    if (!Utils::isRootUser && !m_isFullScreenRecord) {
        qCInfo(dsrApp) << __LINE__ << __func__ << "关闭「正在保存」通知（若有）并弹出保存完成通知";
        if (m_recordSavingNotifyId != RECORD_SAVING_NOTIFY_ID_INVALID) {
            closeFdoNotification(m_recordSavingNotifyId);
            m_recordSavingNotifyId = RECORD_SAVING_NOTIFY_ID_INVALID;
        }
        // Popup notify.
        QDBusInterface notification("org.freedesktop.Notifications",
                                    "/org/freedesktop/Notifications",
                                    "org.freedesktop.Notifications",
                                    QDBusConnection::sessionBus());

        QStringList actions;
        actions << "_open" << tr("View");
        actions << "_open1" << tr("Open Folder");

        QVariantMap hints;
        hints["x-deepin-action-_open"] = QStringList{"xdg-open", newSavePath};
        QStringList savepathcommand = QStringList{"dde-file-manager", "--show-item", newSavePath};
        hints["x-deepin-action-_open1"] = savepathcommand;
        int timeout = -1;
        unsigned int id = 0;

        QList<QVariant> arg;
        arg << Utils::appName  //(QCoreApplication::applicationName())                 // appname
            << id                                                    // id
            << QString("deepin-screen-recorder")                     // icon
            << tr("Recording finished")                              // summary
            << QString(tr("Saved to %1")).arg(newSavePath)           // body
            << actions                                               // actions
            << hints                                                 // hints
            << timeout;                                              // timeout
        notification.callWithArgumentList(QDBus::AutoDetect, "Notify", arg);
        qCInfo(dsrApp) << __LINE__ << __func__ << "已弹出通知消息";
    }
    
    if (m_recordType == Utils::kGIF) {
        QFile::remove(savePath);
    }
    
    if (Utils::isWaylandMode) {
#ifdef KF5_WAYLAND_FLAGE_ON
        avlibInterface::unloadFunctions();
#endif
    }

    m_recordingFlag = false;
    
    if (Utils::isSysHighVersion1040() == true) {
        qCInfo(dsrApp) << __LINE__ << __func__ << "正在退出录屏计时图标...";
        qCDebug(dsrApp) << __LINE__ << ": Stop the screen recording timer!";
        //系统托盘图标结束并退出
        callTrayTimeIcon(DBUS_FUNC_ON_STOP);
        callTrayShotIcon(DBUS_FUNC_ON_STOP);
        callTrayRecorderIcon(DBUS_FUNC_ON_STOP);
        qCInfo(dsrApp) << __LINE__ << __func__ << "录屏计时图标已退出";
    }
    
    // 发送录屏的信号
    emit recordingStopped();
    qCDebug(dsrApp) << "Recording stopped signal emitted";

    //保存到剪切板
    save2Clipboard(newSavePath);

    qCInfo(dsrApp) << __LINE__ << __func__ <<"录屏已退出";

    // 延迟退出，确保剪切板操作完成
    QTimer::singleShot(500, []() {
        QApplication::quit();

        // Treeland 和 Wayland 模式下都强制退出，避免残留事件循环导致进程无法退出
        if (Utils::isWaylandMode || Utils::isTreelandMode) {
            qCInfo(dsrApp) << (Utils::isTreelandMode ? "treeland" : "wayland") << "record exit! (_Exit(0))";
            QTimer::singleShot(100, []() {
                _Exit(0);
            });
        }
    });
}
