Загрузка данных



#include "streamingwt.h"

#include "easyav/transport/servers/mpegtsudpserver.h"
#include "easyav/transport/servers/rtptransportserver.h"
#include "easyav/transport/servers/shmemtransportserver.h"
#include "lib_qt_common/qlogtools.h"
#include "ui_streamingwt.h"
#include <QDir>
#include <QFileDialog>
#include <QTimer>
#ifdef BUILD_OPENCV_SUPPORT_EASYSTREAMER
#include "easyapi_cvadapter/cveasyframe.h"
#endif
Q_DEVELOPER_LOG_CATGS(easystreamer, StreamingWt);

StreamingWt::StreamingWt(MediaPlayerWt *player, QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::StreamingWt)
    , mPlayer(player)
    , mSender()
    , mRcvrStreamsModel(new StreamsModel(this))
    , mSndrStreamsModel(new StreamsModel(this))
    , mWtVideoConfig(new VideoEncoderConfigWt(this))
    , mWtAudioConfig(new AudioEncoderConfigWt(this))
    , mWtFilterHwConfig(new HwConfigWt(this))
    , mWtControl(new MediaControlWt(this, false))
{
    qCDebug(catgMem) << _FUNC_RAW_;
    ui->setupUi(this);

    setupWidgets();
    setupStreamMonitor();

    loadSettings();
    auto defaultFilterString = "aresample=44100,aformat=sample_fmts=fltp:channel_layouts=stereo";
    ui->leFilterAudio->setText(QString::fromStdString(defaultFilterString));

    this->setMinimumWidth(500);
}

StreamingWt::~StreamingWt()
{
    dbgMemStart(catgMem);
    stopCvInput();
    stopArvCamera();
    mSender.stopAllPipelines();
    saveSettings();
    delete ui;
    dbgMemFinish(catgMem);
}

void StreamingWt::onInputReady() {}

void StreamingWt::setupStreamMonitor()
{
    ui->pbAddStream->setEnabled(false);
    connect(mRcvrStreamsModel, &StreamsModel::selectionChanged, this, [this](const EasyAV::StreamInfo &val) {
        if (val.mediaType == EasyAV::MediaType::video)
        {
            ui->stackedWidget->setCurrentIndex(0);
        }
        else if (val.mediaType == EasyAV::MediaType::audio)
        {
            ui->stackedWidget->setCurrentIndex(1);
        }
        else
            ui->stackedWidget->setCurrentIndex(2);
        ui->pbAddStream->setEnabled(true);
    });

    connect(mRcvrStreamsModel, &StreamsModel::selectionRemoved, this, [this]() { ui->pbAddStream->setEnabled(false); });

    auto timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, [this]() {
        if (!mPlayer->reciever().pipelineExists(1))
            return;
        mRcvrStreamsModel->updateData(mPlayer->reciever().getStreamInfos(1));
    });

    timer->start(1000);
}

void StreamingWt::setupWidgets()
{
    QRegularExpression ipRegex("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}"
                               "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
    QRegularExpressionValidator *ipValidator = new QRegularExpressionValidator(ipRegex, this);
    ui->leIpRtp->setValidator(ipValidator);
    ui->sbPortRtp->setRange(1, 65535);
    ui->leIpMpegUdp->setValidator(ipValidator);
    ui->sbPortMpegUdp->setRange(1, 65535);

    ui->leShmName->setText("test_shmem");
    int memSize = 1280 * 720 * 3 / 2 /* + 100*/;    // yuv420p = 3/2 bytes per pix
    ui->leSizeBytes->setText(QString::number(memSize));
    ui->leSizeBytes->setValidator(new QIntValidator(this));

    ui->tvRcvrStreams->setModel(mRcvrStreamsModel);
    ui->tvSndrStreams->setModel(mSndrStreamsModel);
    ui->tvRcvrStreams->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->tvRcvrStreams->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->tvRcvrStreams->header()->setSectionResizeMode(0, QHeaderView::Stretch);
    ui->tvRcvrStreams->header()->setSectionResizeMode(1, QHeaderView::Stretch);
    ui->tvSndrStreams->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->tvSndrStreams->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->tvSndrStreams->header()->setSectionResizeMode(0, QHeaderView::Stretch);
    ui->tvSndrStreams->header()->setSectionResizeMode(1, QHeaderView::Stretch);
    mRcvrStreamsModel->setSelector(ui->tvRcvrStreams->selectionModel());
    mSndrStreamsModel->setSelector(ui->tvSndrStreams->selectionModel());
    ui->loVideoConfig->addWidget(mWtVideoConfig);
    ui->loAudioConfig->addWidget(mWtAudioConfig);
    ui->loFilterHwConfig->addWidget(mWtFilterHwConfig);
    connect(ui->pbAddStream, &QPushButton::clicked, this, &StreamingWt::onAddStreamClicked);
    connect(ui->pbAddOverlay, &QPushButton::clicked, this, &StreamingWt::onAddOverlayClicked);
    // ui->cbFiltepHwStrategy->addItem("none", QVariant(static_cast<int>(EasyAV::HwStrategy::none)));
    // ui->cbFilterHwStrategy->addItem("derive",
    //                                 QVariant(static_cast<int>(EasyAV::HwStrategy::derive)));
    // ui->cbFilterHwStrategy->addItem("unique",
    //                                 QVariant(static_cast<int>(EasyAV::HwStrategy::unique)));

    connect(ui->pbSetupRtp, &QPushButton::clicked, this, &StreamingWt::onSetupRtpClicked);
    connect(ui->pbSetupMpegUdp, &QPushButton::clicked, this, &StreamingWt::onSetupMpegTsUdpClicked);
    connect(ui->pbSetupShMem, &QPushButton::clicked, this, &StreamingWt::onSetupShMemClicked);
    // connect(mPlayer, &MediaPlayerWt::overlayPayloadReady, this, &StreamingWt::onOverlayPayload);
    connect(ui->pbAddCvCamera, &QPushButton::clicked, this, &StreamingWt::onAddCameraClicked);
    connect(ui->pbAddAravisCamera, &QPushButton::clicked, this, &StreamingWt::onAddArvCameraClicked);
    connect(ui->pbAddCvFile, &QPushButton::clicked, this, [this]() {
        QString filter = "Video Files (*.mp4 *.avi *.mkv);;All Files (*.*)";
        QString filename = QFileDialog::getOpenFileName(this, "Select a video file", "", filter);
        if (filename.isEmpty())
        {
            qCCritical(QLoggingCategory("EasyStreamer")) << "Empty filename for OpenCV file";
            return;
        }
        this->onAddVideoClicked(filename);
    });
    ui->loPlayerControls->addWidget(mWtControl);
#ifndef BUILD_OPENCV_SUPPORT_EASYSTREAMER
    ui->pbAddCvCamera->hide();
    ui->pbAddCvFile->hide();
#endif

    mWtControl->setPlayCallback([this]() -> bool { return onPlay(); });

    mWtControl->setStopCallback([this]() { return onStop(); });
}

// void StreamingWt::onOverlayPayload(const QByteArray &payload)
// {
//     AVFrame *frame = av_frame_alloc();
//     if (!frame)
//         return;

//     frame->format = -1;    // custom data
//     frame->buf[0] = av_buffer_alloc(payload.size());
//     if (!frame->buf[0])
//     {
//         av_frame_free(&frame);
//         return;
//     }
//     memcpy(frame->buf[0]->data, payload.constData(), payload.size());
//     frame->data[0] = frame->buf[0]->data;
//     frame->linesize[0] = static_cast<int>(payload.size());

//     long long lastVideoUs = mLastVideoPtsUs.load(std::memory_order_relaxed);
//     int64_t usec_ts = 0;
//     static auto startTime = std::chrono::steady_clock::now();

//     if (lastVideoUs > 0)
//     {
//         usec_ts = static_cast<int64_t>(lastVideoUs);
//     }
//     else
//     {
//         auto now = std::chrono::steady_clock::now();
//         usec_ts = static_cast<int64_t>(std::chrono::duration_cast<std::chrono::microseconds>(now - startTime).count());
//     }
//     AVRational src_tb{1, 1000000};
//     AVRational dst_tb{1, 90000};    // если твой overlay stream timebase = 1/90000
//     frame->pts = av_rescale_q(usec_ts, src_tb, dst_tb);
//     frame->pkt_dts = frame->pts;
//     frame->duration = 1;

//     auto easyFrame = EasyAV::AVEasyFrame::createFromAVFrame(frame, std::chrono::microseconds(usec_ts));
//     bool ok = mSender.pushFrame(pipelineId, overlayIdx, easyFrame);
// }

void StreamingWt::loadSettings()
{
    ui->leIpRtp->setText("127.0.0.1");
    ui->sbPortRtp->setValue(3535);
    ui->leIpMpegUdp->setText("127.0.0.1");
    ui->sbPortMpegUdp->setValue(3535);
    ui->leFilterVideo->setText("scale_vaapi=w=1280:h=720,hwdownload,format=yuv420p");
    EasyAV::VideoEncoderConfig videoConfig;
    videoConfig.video = {1280, 720, EasyAV::PixelFormat::YUV420P};
    videoConfig.fps = 30;
    videoConfig.bitrate = 2'500'000;    // 2.5 Mbps for 720p
    mWtVideoConfig->setConfig(videoConfig);
    EasyAV::AudioEncoderConfig audioConfig;
    mWtAudioConfig->setConfig(audioConfig);
}

void StreamingWt::saveSettings() {}

void StreamingWt::onAddArvCameraClicked()
{
#ifdef BUILD_ARAVIS_SUPPORT_EASYSTREAMER
    if (mArvTimer)
    {
        mArvTimer->stop();
        mArvTimer->deleteLater();
        mArvTimer = nullptr;
    }
    if (mArvStream)
    {
        g_object_unref(mArvStream);
        mArvStream = nullptr;
    }
    if (mArvCamera)
    {
        g_object_unref(mArvCamera);
        mArvCamera = nullptr;
    }

    mArvCamera = arv_camera_new(nullptr, nullptr);
    if (!mArvCamera)
    {
        qCCritical(QLoggingCategory("EasyStreamer")) << "Failed to open Aravis camera!";
        return;
    }
    GError *setErr = nullptr;
    arv_camera_set_pixel_format(mArvCamera, ARV_PIXEL_FORMAT_MONO_8, &setErr);
    if (setErr)
    {
        qCWarning(QLoggingCategory("EasyStreamer")) << "Could not set format:" << setErr->message;
        g_clear_error(&setErr);
    }

    arv_camera_set_acquisition_mode(mArvCamera, ARV_ACQUISITION_MODE_CONTINUOUS, nullptr);
    arv_camera_start_acquisition(mArvCamera, nullptr);

    GError *error = nullptr;
    mArvStream = arv_camera_create_stream(mArvCamera, nullptr, nullptr, &error);
    if (error)
    {
        qCCritical(QLoggingCategory("EasyStreamer")) << "Stream creation error:" << error->message;
        g_error_free(error);
        g_object_unref(mArvCamera);
        return;
    }

    size_t payload_size = arv_camera_get_payload(mArvCamera, nullptr);
    for (int i = 0; i < 50; i++)
    {
        arv_stream_push_buffer(mArvStream, arv_buffer_new(payload_size, nullptr));
    }

    std::string filter = ui->leFilterVideo->text().toStdString();
    EasyAV::VideoEncoderConfig config = mWtVideoConfig->getConfig();

    int arvWidth = 0, arvHeight = 0;
    arv_camera_get_region(mArvCamera, nullptr, nullptr, &arvWidth, &arvHeight, nullptr);
    config.video.width = arvWidth;
    config.video.height = arvHeight;

    auto optStreamIdx = mSender.makeVideoStreamInPipeline(pipelineId, config, {1, 1000}, {filter, mWtFilterHwConfig->getConfig()});

    if (optStreamIdx.has_value())
    {
        mVideoIdx = optStreamIdx.value();
    }
    else
    {
        qCWarning(catg) << _FUNC_RAW_ << "Couldn't make video stream for Aravis";
        return;
    }
    mArvTimer = new QTimer(this);
    connect(mArvTimer, &QTimer::timeout, this, [this]() {
        if (!mArvStream)
            return;

        ArvBuffer *buffer = arv_stream_try_pop_buffer(mArvStream);
        if (!buffer)
            return;

        if (arv_buffer_get_status(buffer) == ARV_BUFFER_STATUS_SUCCESS)
        {
            auto easyFrame = std::make_shared<EasyApi::ArvEasyFrame>(buffer);
            mSender.pushFrame(pipelineId, mVideoIdx, easyFrame);
        }

        arv_stream_push_buffer(mArvStream, buffer);
    });

    mArvTimer->start(30);
#endif
}

void StreamingWt::stopArvCamera()
{
#ifdef BUILD_ARAVIS_SUPPORT_EASYSTREAMER
        if (mArvTimer)
        {
            mArvTimer->stop();
            delete mArvTimer;
            mArvTimer = nullptr;
        }

        if (mArvCamera)
        {
            arv_camera_stop_acquisition(mArvCamera, nullptr);

            if (mArvStream)
            {
                ArvBuffer *buffer = nullptr;
                while ((buffer = arv_stream_try_pop_buffer(mArvStream)) != nullptr)
                    arv_stream_push_buffer(mArvStream, buffer);
                g_object_unref(mArvStream);
                mArvStream = nullptr;
            }

            g_object_unref(mArvCamera);
            mArvCamera = nullptr;
        }
#endif
}

void StreamingWt::stopCvInput()
{
    if (mCvTimer)
    {
        mCvTimer->stop();
        mCvTimer->deleteLater();
        mCvTimer = nullptr;
    }
#ifdef BUILD_OPENCV_SUPPORT_EASYSTREAMER
    mCvCapture.reset();
#endif
}

void StreamingWt::onAddCameraClicked(int index = 0)
{
#ifdef BUILD_OPENCV_SUPPORT_EASYSTREAMER
    stopCvInput();
    mCvCapture = std::make_unique<cv::VideoCapture>(index);
    if (!mCvCapture->isOpened())
    {
        qCCritical(QLoggingCategory("EasyStreamer")) << "Failed to open camera:" << index;
        return;
    }

    mCvTimer = new QTimer(this);

    std::string filter = ui->leFilterVideo->text().toStdString();
    EasyAV::VideoEncoderConfig config = mWtVideoConfig->getConfig();

    auto optStreamIdx = mSender.makeVideoStreamInPipeline(pipelineId, config, {1, 30},
                                                          {filter, mWtFilterHwConfig->getConfig()});    // time base of recieving
    if (optStreamIdx.has_value())
        mVideoIdx = optStreamIdx.value();
    else
    {
        qCWarning(catg) << _FUNC_RAW_ << "couldn't make video stream";
        return;
    }
    connect(mCvTimer, &QTimer::timeout, this, [this]() {
        cv::Mat frame;
        if (!mCvCapture->read(frame))
            return;
        auto easyFrame = EasyApi::CVEasyFrame::createFromMat(frame);

        mSender.pushFrame(pipelineId, mVideoIdx, easyFrame);
    });
    mCvTimer->start(30);    // ~30 FPS
#endif
}

void StreamingWt::onAddVideoClicked(QString path)
{
#ifdef BUILD_OPENCV_SUPPORT_EASYSTREAMER

    stopCvInput();
    if (!mSender.pipelineExists(pipelineId))
    {
        mActiveTransportServer = std::make_shared<EasyAV::RtpTransportServer>(
            "rtp://" + ui->leIpRtp->text().toStdString() + ":" + ui->sbPortRtp->text().toStdString(), nullptr);
        mSender.addMediaPipeline(pipelineId, mActiveTransportServer);
    }

    mCvCapture = std::make_unique<cv::VideoCapture>(path.toStdString(), cv::CAP_FFMPEG);
    //  qCInfo(QLoggingCategory("EasyStreamer")) << "Backend:" << mCvCapture->getBackendName().c_str();
    if (!mCvCapture->isOpened())
    {
        qCCritical(QLoggingCategory("EasyStreamer")) << "Failed to open OpenCV file:" << path;
        return;
    }

    mCvTimer = new QTimer(this);
    std::string filter = ui->leFilterVideo->text().toStdString();
    EasyAV::VideoEncoderConfig config = mWtVideoConfig->getConfig();

    auto optStreamIdx = mSender.makeVideoStreamInPipeline(pipelineId, config, {1, 1000},
                                                          {filter, mWtFilterHwConfig->getConfig()});    // time base of recieving
    if (optStreamIdx.has_value())
        mVideoIdx = optStreamIdx.value();
    else
    {
        qCWarning(catg) << _FUNC_RAW_ << "couldn't make video stream";
        return;
    }

    connect(mCvTimer, &QTimer::timeout, this, [this]() {
        if (!mCvCapture || !mCvCapture->isOpened())
            return;

        cv::Mat frame;
        if (!mCvCapture->read(frame) || frame.empty())
            return;

        auto easyFrame = EasyApi::CVEasyFrame::createFromMat(frame);
        mSender.pushFrame(pipelineId, mVideoIdx, easyFrame);
    });

    mCvTimer->start(33);

#endif
}

void StreamingWt::onAddStreamClicked()
{
    EasyAV::StreamInfo selectedStream = mRcvrStreamsModel->getSelectedStream();
    auto &reciever = mPlayer->reciever();
    if (selectedStream.mediaType == EasyAV::MediaType::video)
    {
        std::string filter = ui->leFilterVideo->text().toStdString();
        EasyAV::VideoEncoderConfig config = mWtVideoConfig->getConfig();
        auto optStreamIdx = mSender.makeVideoStreamInPipeline(pipelineId, config, selectedStream.streamTimebase,
                                                              {filter, mWtFilterHwConfig->getConfig()});    // time base of recieving
        if (!optStreamIdx.has_value())
        {
            qCWarning(catg) << _FUNC_RAW_ << "couldn't make video stream";
            return;
        }

        mVideoIdx = optStreamIdx.value();
        reciever.addRenderer(
            pipelineId, EasyAV::SourceIdx{selectedStream.index.value}, [this](std::shared_ptr<EasyApi::IEasyFrame> videoFrame) {
                // auto avVideo = std::dynamic_pointer_cast<EasyApi::AVEasyFrame>(videoFrame);
                mSender.pushFrame(pipelineId, mVideoIdx, std::dynamic_pointer_cast<EasyApi::AVEasyFrame>(videoFrame));
                if (mOverlayIdx.value != -1)
                {
                    auto overlayFrame = makeOverlayFrame(videoFrame);
                    /*int64_t videoPts = avVideo->getRawAVFrame()->pts;
                    int64_t overlayPts = std::dynamic_pointer_cast<EasyApi::AVEasyFrame>(overlayFrame)->getRawAVFrame()->pts;
                    qCCritical(QLoggingCategory("EasyStreamer"))
                        << "VIDEO pts=" << videoPts
                        << " OVERLAY pts=" << overlayPts
                        << " diff=" << (videoPts - overlayPts);*/
                    mSender.pushFrame(pipelineId, mOverlayIdx, std::dynamic_pointer_cast<EasyApi::AVEasyFrame>(overlayFrame));
                }
            });
    }
    else if (selectedStream.mediaType == EasyAV::MediaType::audio)
    {
        ui->stackedWidget->setCurrentIndex(1);
        std::string filter = ui->leFilterAudio->text().toStdString();
        auto optStreamIdx = mSender.makeAudioStreamInPipeline(pipelineId, mWtAudioConfig->getConfig(), selectedStream.streamTimebase,
                                                              ui->leFilterAudio->text().toStdString());    // time base to codec timebase
        if (!optStreamIdx.has_value())
        {
            qCWarning(catg) << _FUNC_RAW_ << "couldn't make audio stream";
            return;
        }

        mAudioIdx = optStreamIdx.value();
        reciever.addRenderer(1, EasyAV::SourceIdx{selectedStream.index.value}, [this](std::shared_ptr<EasyApi::IEasyFrame> frame) {
            mSender.pushFrame(pipelineId, mAudioIdx, std::dynamic_pointer_cast<EasyApi::AVEasyFrame>(frame));
        });
    }
    else if (selectedStream.mediaType == EasyAV::MediaType::data)
    {
        ui->stackedWidget->setCurrentIndex(2);
    }
    else
        ui->stackedWidget->setCurrentIndex(2);
}

void StreamingWt::onAddOverlayClicked()
{
    EasyAV::StreamInfo selectedStream = mRcvrStreamsModel->getSelectedStream();

    ui->stackedWidget->setCurrentIndex(2);

    // 1. Создаём data-stream в sender
    auto optStreamIdx = mSender.makeOverlayStreamInPipeline(pipelineId, selectedStream.streamTimebase);

    if (!optStreamIdx.has_value())
        return;

    mOverlayIdx = optStreamIdx.value();

    mPlayer->setOverlayEnabled(true);
}

void StreamingWt::onSetupRtpClicked()
{
    // TODO edit format AVDictionary;
    // TODO edit -> add shMem;
    mActiveTransportServer = std::make_shared<EasyAV::RtpTransportServer>(
        "rtp://" + ui->leIpRtp->text().toStdString() + ":" + ui->sbPortRtp->text().toStdString(), nullptr);

    auto rtpServer = std::dynamic_pointer_cast<EasyAV::RtpTransportServer>(mActiveTransportServer);
    if (rtpServer)
    {
        rtpServer->setSdpOutputFilename(QString(QDir::currentPath() + "/rtpstream.sdp").toStdString());
    }

    bool success = mSender.addMediaPipeline(1, mActiveTransportServer);
    if (success)
    {
        ui->pbSetupRtp->setEnabled(false);
        ui->pbSetupShMem->setEnabled(false);
        ui->pbSetupMpegUdp->setEnabled(false);
    }
}

void StreamingWt::onSetupMpegTsUdpClicked()
{
    std::string url = "udp://" + ui->leIpMpegUdp->text().toStdString() + ":" + ui->sbPortMpegUdp->text().toStdString();
    mActiveTransportServer = std::make_shared<EasyAV::MpegTsUdpServer>(url, nullptr);
    bool success = mSender.addMediaPipeline(1, mActiveTransportServer);
    if (success)
    {
        ui->pbSetupRtp->setEnabled(false);
        ui->pbSetupShMem->setEnabled(false);
        ui->pbSetupMpegUdp->setEnabled(false);
    }
    else
    {
        qCWarning(catg) << _FUNC_RAW_ << "couldn't setup MPEGTS UDP transport; sender pipeline already exists";
        ui->pbSetupMpegUdp->setEnabled(true);
    }
}
void StreamingWt::onSetupShMemClicked()
{
    // auto shMemSharedServer =
    //     std::make_shared<EasyAV::ShMemTransportServer>(ui->leShmName->text().toStdString(), ui->leSizeBytes->text().toInt(), nullptr);
    // bool success = mSender.addMediaPipeline(1, shMemSharedServer);
    // ui->pbSetupRtp->setEnabled(!success);
    // ui->pbSetupShMem->setEnabled(!success);
}

void StreamingWt::setupSndrStreamModel()
{

    auto timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, [this]() {
        if (!mSender.pipelineExists(1))
            return;
        mSndrStreamsModel->updateData(mSender.getStreamInfos(1));
    });

    timer->start(1000);
}

bool StreamingWt::onPlay()
{
    bool ok = mSender.playPipeline(1);
    if (ok)
        setupSndrStreamModel();
    return ok;
}

void StreamingWt::onStop()
{
    stopCvInput();
    mSender.stopPipeline(1);
}

std::shared_ptr<EasyApi::IEasyFrame> StreamingWt::makeOverlayFrame(std::shared_ptr<EasyApi::IEasyFrame> videoFrame)
{
    auto avEasy = std::dynamic_pointer_cast<EasyApi::AVEasyFrame>(videoFrame);

    if (!avEasy)
        return {};
    AVFrame *videoAv = avEasy->getRawAVFrame();
    if (!videoAv || videoAv->pts == AV_NOPTS_VALUE)
        return {};
    auto payload = mPlayer->getOverlayData();
    AVFrame *frame = av_frame_alloc();
    if (!frame)
        return {};
    frame->format = AV_PIX_FMT_NONE;
    frame->pts = videoAv->pts;
    frame->pkt_dts = videoAv->pts;
    frame->duration = frame->duration = videoAv->duration > 0 ? videoAv->duration : 1;

    frame->buf[0] = av_buffer_alloc(payload.size());
    if (!frame->buf[0])
    {
        av_frame_free(&frame);
        return {};
    }

    frame->data[0] = frame->buf[0]->data;
    frame->linesize[0] = payload.size();

    memcpy(frame->data[0], payload.constData(), payload.size());

    auto easyFrame = EasyAV::AVEasyFrame::createFromAVFrame(frame, std::chrono::microseconds(0));
    if (!easyFrame)
        return nullptr;
    return easyFrame;
}
#pragma once

#include "audioencoderconfigwt.h"
#include "easyapi_avadapter/codecsettings.h"
#include "easyav/reciever/mediareciever.h"
#include "easyav/sender/mediasender.h"
#include <QTreeView>
#include <QWidget>
#ifdef BUILD_ARAVIS_SUPPORT_EASYSTREAMER
#include "easyapi_arvadapter/arveasyframe.h"
#endif
#include "mediaplayerwt.h"
#include "streamsmodel.h"
#include "videoencoderconfigwt.h"

namespace Ui
{
class StreamingWt;
}

class StreamingWt : public QWidget
{
    Q_OBJECT

  public:
    explicit StreamingWt(MediaPlayerWt *player, QWidget *parent = nullptr);
    ~StreamingWt();

  public slots:
    void onInputReady();
    void onSetupRtpClicked();
    void onSetupShMemClicked();
    void onSetupMpegTsUdpClicked();

  private slots:
    // void onOverlayPayload(const QByteArray &payload);

  private:
    bool mOverlayListed = false;

    std::atomic<long long> mLastVideoPtsUs{0};
    //  bool mOverlayAdded = 0;

    static constexpr int pipelineId{1};
    Ui::StreamingWt *ui;
    MediaPlayerWt *mPlayer;
    EasyAV::MediaSender mSender;
    StreamsModel *mRcvrStreamsModel;
    StreamsModel *mSndrStreamsModel;
    QTimer *mCvTimer = nullptr;
    void onAddArvCameraClicked(); // Слот для кнопки

#ifdef BUILD_ARAVIS_SUPPORT_EASYSTREAMER
    ArvCamera* mArvCamera = nullptr;
    ArvStream* mArvStream = nullptr;
#endif
    QTimer* mArvTimer = nullptr;
    std::shared_ptr<EasyAV::ITransportServer> mActiveTransportServer;

#ifdef BUILD_OPENCV_SUPPORT_EASYSTREAMER
    std::unique_ptr<cv::VideoCapture> mCvCapture;
#endif
    EasyAV::VideoEncoderConfig mCurVideoConfig;
    VideoEncoderConfigWt *mWtVideoConfig;
    AudioEncoderConfigWt *mWtAudioConfig;
    HwConfigWt *mWtFilterHwConfig;
    MediaControlWt *mWtControl;

    EasyAV::SourceIdx mVideoIdx{-1};
    EasyAV::SourceIdx mAudioIdx{-1};
    EasyAV::SourceIdx mOverlayIdx{-1};
    void setupStreamMonitor();
    void setupSndrStreamModel();
    void setupWidgets();
    void loadSettings();
    void saveSettings();
    void stopCvInput();
    void stopArvCamera();

    void onAddStreamClicked();
    void onAddOverlayClicked();
    void onAddCameraClicked(int index);
    void onAddVideoClicked(QString path);
    bool onFrameImported(EasyAV::MediaReciever::PipelineId pipeId, int32_t streamIdx, EasyAV::MediaType mt, AVFrame *frame);
    bool onPlay();
    void onStop();
    std::shared_ptr<EasyApi::IEasyFrame> makeOverlayFrame(std::shared_ptr<EasyApi::IEasyFrame> videoFrame);
};