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


#include "cameramanager.h"
#include "configkeys.h"
#include "lib_qt_common/qlogtools.h"
#include "uiconst.h"

// Компоненты EasyStreamer / EasyAV
#include "easyav/transport/clients/livetransportclient.h"
#include "easyapi_avadapter/defaultoptions.h"
#include "easygl/videoglwidget.h"

#include <QDebug>
#include <QLoggingCategory>
#include <QPointer>
#include <variant>

Q_DEVELOPER_LOG_CATGS(arm, CameraManager)

CameraManager::CameraManager(FrameDataSetter *dataSetter, QObject *parent)
    : QObject(parent)
    , mFrameDataSetter(dataSetter)
    , mCamId(0) // Инициализируем дефолтным значением
{
}

void CameraManager::start(CamId camId, const QString &rtpUrl)
{
    qCDebug(catg) << Q_FUNC_INFO << this << "Запуск камеры:" << camId << "URL:" << rtpUrl;
    mCamId = camId;
    mPaused = false;

    // Сбрасываем старый пайплайн, если он существовал
    if (mReciever.isPipelineExists(1))
    {
        mReciever.stopPipeline(1);
        mReciever.removePipeline(1);
    }

    // Конфигурируем RTSP/RTP клиент на минимальную задержку (Low Delay)
    AVDictionary *dict = EasyApi::DefaultOptions::makeRTPLowDelayFormatClientDict();
    auto liveClient = std::make_shared<EasyAV::LiveTransportClient>(rtpUrl.toStdString(), dict);
    av_dict_free(&dict);

    // Создаем пайплайн с фиксированным ID = 1 (внутри этого менеджера)
    mReciever.addMediaPipeline(1, liveClient);
}

void CameraManager::setRendererToStream(CameraWidget *wtCamera, CamId camId)
{
    qCDebug(catg) << Q_FUNC_INFO << this << "Привязка рендерера для camId:" << camId;
    
    if (!wtCamera) return;

    // Проверяем, что настраиваем именно ту камеру, которая была запущена
    if (camId != mCamId)
    {
        qCWarning(catg) << "Запрошена привязка для camId" << camId << ", но текущий mCamId =" << mCamId;
        return;
    }

    // Извлекаем указатель на новый OpenGL виджет
    auto* videoGlWidget = qobject_cast<EasyGL::VideoGlWidget*>(wtCamera->getWtVideo());
    if (!videoGlWidget)
    {
        qCWarning(catg) << "Внутри CameraWidget используется старый виджет, ожидался EasyGL::VideoGlWidget!";
    }

    // Выставляем дефолтный Aspect Ratio до получения первого кадра
    wtCamera->setVideoLayoutAspectRatio({ProjectUiConst::DefaultResolutionWidth, ProjectUiConst::DefaultResolutionHeight});

    QPointer<CameraManager> guard(this);

    // Коллбэк для пула потоков MediaReciever
    EasyAV::MediaRecievePipeline::CbRenderer videoRenderer = [guard, videoGlWidget, wtCamera](std::shared_ptr<EasyApi::IEasyFrame> frame) {
        if (!guard || guard->mPaused || !frame)
            return;

        if (frame->getMediaType() != EasyAV::MediaType::video)
            return;

        // Перенаправляем обработку и отрисовку кадра в главный GUI-поток Qt
        QMetaObject::invokeMethod(guard.data(), [guard, videoGlWidget, wtCamera, frame]() {
            if (!guard || guard->mPaused)
                return;

            auto frameInfo = frame->getFrameInfo();
            
            // Безопасно извлекаем структуру VideoInfo с реальными размерами
            if (auto* videoInfo = std::get_if<EasyApi::VideoInfo>(&frameInfo)) 
            {
                QSize originalSize(videoInfo->width, videoInfo->height);

                // 1. Отдаем кадр напрямую в EasyGL::VideoGlWidget для отрисовки на GPU
                if (videoGlWidget) 
                {
                    videoGlWidget->submitFrame(frame);
                }

                // 2. Проверяем изменение разрешения
                if (guard->mShMemResolution != originalSize)
                {
                    guard->mShMemResolution = originalSize;
                    emit guard->resolutionChanged(guard->mCamId, originalSize);
                }

                // 3. Динамически корректируем Aspect Ratio контейнера под размер стрима
                QSize curAspectRatio = wtCamera->getVideoLayoutAspectRatio();
                int64_t left = static_cast<int64_t>(originalSize.width()) * curAspectRatio.height();
                int64_t right = static_cast<int64_t>(originalSize.height()) * curAspectRatio.width();
                
                if (left != right)
                {
                    wtCamera->setVideoLayoutAspectRatio(originalSize);
                }

                // 4. Синхронно обновляем телеметрию, прицелы и рамки нейросети
                // Передаем mCamId вместо хардкода
                if (guard->mFrameDataSetter) 
                {
                    guard->mFrameDataSetter->onNewFrameReceived(guard->mCamId, originalSize, frame->getPts().count());
                }

                emit guard->frameUpdated(guard->mCamId, originalSize, frame->getPts().count());
            }
        }, Qt::QueuedConnection);
    };

    // Регистрируем коллбэк рендерера в ресивере и запускаем декодирование
    mReciever.addRenderer(1, EasyAV::MediaType::video, videoRenderer);
    mReciever.playPipeline(1);
}

void CameraManager::pause(CamId camId)
{
    qCDebug(catg) << Q_FUNC_INFO << this << "Пауза для camId:" << camId;
    if (camId == mCamId)
    {
        mPaused = true;
        if (mReciever.isPipelineExists(1))
        {
            mReciever.pausePipeline(1);
        }
    }
}

QSize CameraManager::getShMemResolution() const 
{ 
    return mShMemResolution; 
}