Загрузка данных
/*#include "cameramanager.h"
#include "configkeys.h"
#include "lib_qt_common/qlogtools.h"
#include "uiconst.h"
#include "videocontrol/widgets/ivideoprocessor.h"
#include <QDebug>
#include <QLoggingCategory>
#include <QShortcut>
#include "easygl/videoglwidget.h"
Q_DEVELOPER_LOG_CATGS(arm, CameraManager)
CameraManager::CameraManager(VideoReceiver *shMemClient, QObject *parent)
: QObject(parent)
, mVideoReciever(shMemClient)
{}
void CameraManager::start(CamId camId)
{
qCDebug(catg) << Q_FUNC_INFO << this << camId;
mShMemCamId = camId;
mPaused = false;
}
void CameraManager::setRendererToStream(CameraWidget *wtCamera, CamId camId)
{
qCDebug(catg) << Q_FUNC_INFO << this << camId;
if (camId == mShMemCamId)
{
wtCamera->setVideoLayoutAspectRatio({ProjectUiConst::DefaultResolutionWidth, ProjectUiConst::DefaultResolutionHeight});
mVideoReciever->setScreenSize({ProjectUiConst::DefaultResolutionWidth, ProjectUiConst::DefaultResolutionHeight});
auto iVideoReciever = dynamic_cast<EasyGL::VideoGlWidget *>(wtCamera->getWtVideo());
connect(
mVideoReciever, &VideoReceiver::newData, this,
[wtCamera, iVideoReciever, this](quint64 frameNum, quint64 timestamp, QPixmap frame, QSize screenSize, QSize originalSize) {
if (mPaused)
return;
if (mShMemResolution != screenSize)
{
mShMemResolution = screenSize;
emit resolutionChanged(mShMemCamId, {originalSize.width(), originalSize.height()});
}
// adjust aspect ratio
QSize size = {frame.width(), frame.height()};
QSize curAspectRatio = wtCamera->getVideoLayoutAspectRatio();
int64_t left = size.width() * curAspectRatio.height();
int64_t right = size.height() * curAspectRatio.width();
if (left != right)
{
wtCamera->setVideoLayoutAspectRatio(size);
}
// update frame
auto wtSize = wtCamera->videoWidgetSize();
iVideoReciever->updatePixmap(frame.scaled(wtSize.width(), wtSize.height()), frameNum);
emit frameUpdated(mShMemCamId, originalSize, frameNum);
},
Qt::QueuedConnection);
return;
}
}
void CameraManager::pause(CamId camId)
{
qCDebug(catg) << Q_FUNC_INFO << this;
mPaused = true;
}
QSize CameraManager::getShMemResolution() const { return mShMemResolution; }
*/
#include "cameramanager.h"
#include "configkeys.h"
#include "lib_qt_common/qlogtools.h"
#include "uiconst.h"
#include "videocontrol/widgets/ivideoprocessor.h"
#include "easyav/transport/clients/livetransportclient.h"
#include "easyapi_avadapter/defaultoptions.h"
#include "easygl/videoglwidget.h"
#include <QDebug>
#include <QLoggingCategory>
#include <QShortcut>
#include <QPointer>
Q_DEVELOPER_LOG_CATGS(arm, CameraManager)
CameraManager::CameraManager(QObject *parent)
: QObject(parent)
, 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);
if (videoGlWidget)
{
videoGlWidget->submitFrame(frame);
}
if (guard->mShMemResolution != originalSize)
{
guard->mShMemResolution = originalSize;
emit guard->resolutionChanged(guard->mCamId, originalSize);
}
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);
}
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;
}
#ifndef CAMERAMANAGER_H
#define CAMERAMANAGER_H
#include "libs/lib_framedatabridge/src/receiver/videoreceiver.h"
#include "libs/wgt_common/src/camerawidget.h"
#include "easyav/reciever/mediareciever.h"
#include <QObject>
using CamId = uint32_t;
using FrameId = uint32_t;
using FrameSize = std::pair<uint16_t, uint16_t>;
struct CamStreamInfo
{
uint32_t id;
std::string ip;
std::string port;
std::string camName = "unknown cam";
FrameSize resolution{0, 0};
};
/*!
\brief Класс управляющий видеопотоками
*/
/*
class CameraManager : public QObject
{
Q_OBJECT
public:
explicit CameraManager(VideoReceiver *shMemClient, QObject *parent = nullptr);
void start(CamId camId);
void start(const QVector<CamId> &camIds);
void setRendererToStream(CameraWidget *wtCamera, CamId camId);
void pause(CamId camId);
QSize getShMemResolution() const;
signals:
void resolutionChanged(CamId, FrameSize);
void frameUpdated(CamId camId, QSize size, FrameId frameId);
private:
VideoReceiver *mVideoReciever{nullptr};
bool mPaused{false};
QSize mShMemResolution{0, 0};
CamId mShMemCamId{0};
};
#endif // CAMERAMANAGER_H
*/
class CameraManager : public QObject
{
Q_OBJECT
public:
explicit CameraManager(QObject *parent = nullptr);
~CameraManager() override = default;
// Добавляем URL для инициализации RTP потока
void start(CamId camId, const QString &rtpUrl);
void pause(CamId camId);
void setRendererToStream(CameraWidget *wtCamera, CamId camId);
QSize getShMemResolution() const;
signals:
void resolutionChanged(CamId camId, QSize originalSize);
void frameUpdated(CamId camId, QSize originalSize, quint64 frameNum);
private:
EasyAV::MediaReciever mReciever;
CamId mCamId;
QSize mShMemResolution;
bool mPaused = false;
};
#endif // CAMERAMANAGER_H
Нужно сделать так чтобы он принимал информацию с .rtp файла а не как сейчас ведь без него чисты ртп не воспринимает контекст можешь ориентироваться на mediaplyaerwt initfrom media #include "mediaplayerwt.h"
#include "./ui_mediaplayerwt.h"
#include "apparguments.h"
#include "easyav/transport/clients/filetransportclient.h"
#include "easyav/transport/clients/livetransportclient.h"
#include "easyav/transport/clients/shmemtransportclient.h"
#include "easyapi_avadapter/defaultoptions.h"
#include "lib_qt_common/qlogtools.h"
#include <QCoreApplication>
#include <QEvent>
#include <QPointer>
#include <QVBoxLayout>
#ifdef BUILD_OPENCV_SUPPORT_EASYSTREAMER
#include "easyapi_cvadapter/cveasyframe.h"
#include <opencv2/core/mat.hpp>
#endif
extern "C" {
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
Q_DEVELOPER_LOG_CATGS(easystreamer, MediaPlayerWt);
namespace
{
QOpenGLWidget *createVideoWidget(QWidget *parent)
{
const QString backend = AppArguments::argParser().value(AppArguments::videoWidgetOpt).trimmed().toLower();
if (backend == "shared" || backend == "sharedvideoglwidget" || backend == "shared-video-gl-widget")
{
qCInfo(catg) << "Using SharedVideoGlWidget backend";
return new EasyGL::SharedVideoGlWidget(parent);
}
if (backend != "videoglwidget" && backend != "video")
qCWarning(catg) << "Unknown video widget backend" << backend << ", using VideoGlWidget";
qCInfo(catg) << "Using VideoGlWidget backend";
return new EasyGL::VideoGlWidget(parent);
}
bool isRtpLowDelaySource(const QString &source)
{
const QString normalized = source.trimmed().toLower();
return normalized.startsWith("rtp://") || normalized.startsWith("rtp_mpegts://") || normalized.startsWith("udp://") ||
normalized.endsWith(".sdp");
}
AVDictionary *makeLiveClientOptions(const QString &source, bool useRtpLowDelay)
{
if (!useRtpLowDelay)
return nullptr;
if (!isRtpLowDelaySource(source))
{
qCWarning(catg) << "RTP low-delay receive options ignored for non-RTP source:" << source;
return nullptr;
}
qCInfo(catg) << "Using RTP low-delay receive options for source:" << source;
return EasyApi::DefaultOptions::makeRTPLowDelayFormatClientDict();
}
} // namespace
MediaPlayerWt::MediaPlayerWt(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MediaPlayerWt)
, mReciever()
, mWtVideoGl(createVideoWidget(this))
, mWtCodecHwConfig(new HwConfigWt(this))
, mWtFilterHwConfig(new HwConfigWt(this))
, mWtControl(new MediaControlWt(this))
, mVolumeSlider(new QSlider(Qt::Horizontal, this))
, mTimeBar(new QSlider(Qt::Horizontal, this))
{
qCDebug(catgMem) << _FUNC_RAW_;
ui->setupUi(this);
// setupLayout();
setupWidgets();
// loadSettings();
}
MediaPlayerWt::~MediaPlayerWt()
{
dbgMemStart(catgMem);
mClosing.store(true, std::memory_order_release);
QCoreApplication::removePostedEvents(this, QEvent::MetaCall);
#ifdef BUILD_OPENCV_SUPPORT_EASYSTREAMER
if (mCvTimer)
{
mCvTimer->stop();
delete mCvTimer;
mCvTimer = nullptr;
}
mCvCapture.reset();
#endif
mReciever.stopAllPipelines();
QCoreApplication::removePostedEvents(this, QEvent::MetaCall);
// saveSettings();
delete ui;
dbgMemFinish(catgMem);
}
void MediaPlayerWt::setRecieveSettingsVisible(bool visible) { ui->wtSettings->setVisible(visible); }
bool MediaPlayerWt::isVideoDetached() const { return mDetachedVideoDialog != nullptr; }
void MediaPlayerWt::setVideoDetached(bool detached)
{
if (detached == isVideoDetached())
return;
if (detached)
{
mDetachedVideoDialog = new QDialog(this, Qt::Window | Qt::WindowStaysOnTopHint);
mDetachedVideoDialog->setObjectName("detachedVideoDialog");
mDetachedVideoDialog->setWindowTitle("Video");
mDetachedVideoDialog->setMinimumSize(320, 240);
mDetachedVideoDialog->installEventFilter(this);
auto layout = new QVBoxLayout(mDetachedVideoDialog);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
QSize dialogSize = mWtVideoGl->size();
if (!dialogSize.isValid() || dialogSize.isEmpty())
dialogSize = {960, 540};
ui->wtCenter->removeWidget(mWtVideoGl);
if (mVideoPlaceholder)
ui->wtCenter->setCurrentWidget(mVideoPlaceholder);
mWtVideoGl->setParent(mDetachedVideoDialog);
layout->addWidget(mWtVideoGl);
mWtVideoGl->show();
mDetachedVideoDialog->resize(dialogSize);
mDetachedVideoDialog->show();
emit videoDetachedChanged(true);
return;
}
QDialog *dialog = mDetachedVideoDialog;
mDetachedVideoDialog = nullptr;
dialog->removeEventFilter(this);
if (auto layout = dialog->layout())
layout->removeWidget(mWtVideoGl);
if (ui->wtCenter->indexOf(mWtVideoGl) < 0)
ui->wtCenter->addWidget(mWtVideoGl);
ui->wtCenter->setCurrentWidget(mWtVideoGl);
dialog->deleteLater();
emit videoDetachedChanged(false);
}
void MediaPlayerWt::initFromMediaFile(const QString &filename)
{
auto fileClient = std::make_shared<EasyAV::FileTransportClient>(filename.toStdString(), nullptr);
if (mReciever.isPipelineExists(1))
{
mReciever.stopPipeline(1);
mReciever.removePipeline(1);
}
auto config = mWtCodecHwConfig->getConfig();
if (config.devicePath.empty() && config.deviceType == EasyAV::HWDeviceType::vaapi)
{
config.devicePath = "/dev/dri/renderD128";
}
mReciever.setHardwareDeviceForCodec(1, config);
mReciever.setCustomVideoCodecName(1, ui->cbCodec->currentText().toStdString());
mReciever.addMediaPipeline(1, fileClient);
// mReciever.setFinishCb(1, [this]() {
// QMetaObject::invokeMethod(this, [this]() { mReciever.seekForPipeline(1, 0s); });
// });
if (!isVideoDetached())
ui->wtCenter->setCurrentWidget(mWtVideoGl);
// EasyGL::IVideoProcessor *destWgt;
// if (ui->cbDisplay->currentIndex() == 0)
// destWgt = this->mWtVideoQt;
QPointer<MediaPlayerWt> guard(this);
EasyAV::MediaRecievePipeline::CbRenderer videoRenderer = [guard, fileClient](std::shared_ptr<EasyAV::IEasyFrame> frame) {
if (!guard || guard->isClosing() || !frame)
return;
auto pts = frame->getPts();
guard->submitVideoFrame(frame);
guard->invokeOnGui([pts, fileClient, frame](MediaPlayerWt &self) {
if (!self.mDurationSetuped)
{
auto duration = fileClient->getDuration();
self.mTimeBar->setRange(0, duration.count());
self.mTimeBar->setSingleStep(10); // Step size of 1 second
self.mDurationSetuped = true;
}
if (!self.mSeeking)
self.mTimeBar->setValue(std::chrono::duration_cast<std::chrono::milliseconds>(pts).count());
});
};
EasyAV::MediaRecievePipeline::CbRenderer audioRenderer = [guard](std::shared_ptr<EasyAV::IEasyFrame> frame) {
if (!guard || guard->isClosing())
return;
guard->mAudioRenderer.render(frame);
};
// mReciever.addRenderer(1, EasyAV::MediaType::video, videoRenderer, ui->leFilter->text().toStdString(),
// mWtFilterHwConfig->getConfig());
mReciever.addRenderer(1, EasyAV::MediaType::video, videoRenderer, {ui->leFilter->text().toStdString(), mWtFilterHwConfig->getConfig()});
mReciever.addRenderer(1, EasyAV::MediaType::audio, audioRenderer, {mAudioRenderer.buildFilterChain(), std::nullopt});
bool inited = mReciever.initPipeline(1);
if (!inited)
return;
emit ready();
mWtControl->reset();
}
void MediaPlayerWt::initFromSdpFile(const QString &sdp)
{
AVDictionary *dict = makeLiveClientOptions(sdp, ui->cbRtpLowDelay->isChecked());
if (mReciever.isPipelineExists(1))
{
mReciever.stopPipeline(1);
mReciever.removePipeline(1);
}
auto liveClient = std::make_shared<EasyAV::LiveTransportClient>(sdp.toStdString(), dict);
av_dict_free(&dict);
mReciever.setHardwareDeviceForCodec(1, mWtCodecHwConfig->getConfig());
mReciever.setCustomVideoCodecName(1, ui->cbCodec->currentText().toStdString());
mReciever.addMediaPipeline(1, liveClient);
// mReciever.setFinishCb(1, [this]() {
// QMetaObject::invokeMethod(this, [this]() { mReciever.seekForPipeline(1, 0s); });
// });
if (!isVideoDetached())
ui->wtCenter->setCurrentWidget(mWtVideoGl);
// EasyGL::IVideoProcessor *destWgt;
QPointer<MediaPlayerWt> guard(this);
EasyAV::MediaRecievePipeline::CbRenderer videoRenderer = [guard](std::shared_ptr<EasyAV::IEasyFrame> frame) {
if (!guard || guard->isClosing() || !frame)
return;
guard->submitVideoFrame(frame);
auto pts = frame->getPts();
guard->invokeOnGui([pts](MediaPlayerWt &self) {
if (!self.mSeeking)
self.mTimeBar->setValue(std::chrono::duration_cast<std::chrono::milliseconds>(pts).count());
});
};
EasyAV::MediaRecievePipeline::CbRenderer audioRenderer = [guard](std::shared_ptr<EasyAV::IEasyFrame> frame) {
if (!guard || guard->isClosing())
return;
guard->mAudioRenderer.render(frame);
};
mReciever.addRenderer(1, EasyAV::MediaType::video, videoRenderer, {ui->leFilter->text().toStdString(), mWtFilterHwConfig->getConfig()});
mReciever.addRenderer(1, EasyAV::MediaType::audio, audioRenderer, {mAudioRenderer.buildFilterChain(), std::nullopt});
// auto duration = liveClient->getDuration();
mTimeBar->setRange(0, 10000);
mTimeBar->setSingleStep(10); // Step size of 1 second
emit ready();
mWtControl->reset();
}
void MediaPlayerWt::initNetworkStream(const QString &url)
{
AVDictionary *dict = makeLiveClientOptions(url, ui->cbRtpLowDelay->isChecked());
auto liveClient = std::make_shared<EasyAV::LiveTransportClient>(url.toStdString(), dict);
av_dict_free(&dict);
if (mReciever.isPipelineExists(1))
{
mReciever.stopPipeline(1);
mReciever.removePipeline(1);
}
mReciever.setHardwareDeviceForCodec(1, mWtCodecHwConfig->getConfig());
mReciever.setCustomVideoCodecName(1, ui->cbCodec->currentText().toStdString());
mReciever.addMediaPipeline(1, liveClient);
// mReciever.setFinishCb(1, [this]() {
// QMetaObject::invokeMethod(this, [this]() { mReciever.seekForPipeline(1, 0s); });
// });
if (!isVideoDetached())
ui->wtCenter->setCurrentWidget(mWtVideoGl);
QPointer<MediaPlayerWt> guard(this);
EasyAV::MediaRecievePipeline::CbRenderer videoRenderer = [guard](std::shared_ptr<EasyAV::IEasyFrame> frame) {
if (!guard || guard->isClosing() || !frame)
return;
guard->submitVideoFrame(frame);
auto pts = frame->getPts();
guard->invokeOnGui([pts](MediaPlayerWt &self) {
if (!self.mSeeking)
self.mTimeBar->setValue(std::chrono::duration_cast<std::chrono::milliseconds>(pts).count());
});
};
EasyAV::MediaRecievePipeline::CbRenderer audioRenderer = [guard](std::shared_ptr<EasyAV::IEasyFrame> frame) {
if (!guard || guard->isClosing())
return;
guard->mAudioRenderer.render(frame);
};
EasyAV::MediaRecievePipeline::CbRenderer overlayRenderer = [guard](std::shared_ptr<EasyAV::IEasyFrame> frame) {
if (!guard || guard->isClosing())
return;
if (!frame || frame->getMediaType() != EasyAV::MediaType::data)
return;
std::vector<const uint8_t *> planes;
std::vector<int> strides;
if (frame->getDataPointers(planes, strides) && !planes.empty())
{
QByteArray payload(reinterpret_cast<const char *>(planes[0]), strides[0]);
guard->invokeOnGui([payload](MediaPlayerWt &self) {
if (self.mOverlay)
self.mOverlay->applyPayload(payload);
});
}
};
mReciever.addRenderer(1, EasyAV::MediaType::data, overlayRenderer);
mReciever.addRenderer(1, EasyAV::MediaType::video, videoRenderer, {ui->leFilter->text().toStdString(), mWtFilterHwConfig->getConfig()});
mReciever.addRenderer(1, EasyAV::MediaType::audio, audioRenderer, {mAudioRenderer.buildFilterChain(), std::nullopt});
// auto duration = liveClient->getDuration();
mTimeBar->setRange(0, 10000);
mTimeBar->setSingleStep(10); // Step size of 1 second
mReciever.playPipeline(1);
emit ready();
mWtControl->reset();
}
void MediaPlayerWt::initShMemStream(const std::string &memKey, const std::string &framerate, const std::string &videoSize,
const std::string &pixelFormat)
{
AVDictionary *dict = nullptr;
av_dict_set(&dict, "video_size", videoSize.data(), 0);
av_dict_set(&dict, "pixel_format", pixelFormat.data(), 0);
av_dict_set(&dict, "framerate", framerate.data(), 0);
auto shmemClient = std::make_shared<EasyAV::ShMemTransportClient>(memKey, dict);
if (mReciever.isPipelineExists(1))
{
mReciever.stopPipeline(1);
mReciever.removePipeline(1);
}
mReciever.setHardwareDeviceForCodec(1, mWtCodecHwConfig->getConfig());
mReciever.setCustomVideoCodecName(1, ui->cbCodec->currentText().toStdString());
mReciever.addMediaPipeline(1, shmemClient);
// mReciever.setFinishCb(1, [this]() {
// QMetaObject::invokeMethod(this, [this]() { mReciever.seekForPipeline(1, 0s); });
// });
if (!isVideoDetached())
ui->wtCenter->setCurrentWidget(mWtVideoGl);
QPointer<MediaPlayerWt> guard(this);
EasyAV::MediaRecievePipeline::CbRenderer videoRenderer = [guard](std::shared_ptr<EasyAV::IEasyFrame> frame) {
if (!guard || guard->isClosing() || !frame)
return;
guard->submitVideoFrame(frame);
auto pts = frame->getPts();
guard->invokeOnGui([pts](MediaPlayerWt &self) {
if (!self.mSeeking)
self.mTimeBar->setValue(std::chrono::duration_cast<std::chrono::milliseconds>(pts).count());
});
};
EasyAV::MediaRecievePipeline::CbRenderer audioRenderer = [guard](std::shared_ptr<EasyAV::IEasyFrame> frame) {
if (!guard || guard->isClosing())
return;
guard->mAudioRenderer.render(frame);
};
// EasyAV::MediaRecievePipeline::CbRenderer audioRenderer =
// [this](std::shared_ptr<EasyAV::EasyFrame> frame) {
// // QMetaObject::invokeMethod(this,
// // [this, frame]() mutable { mAudioRenderer.render(frame); });
// mAudioRenderer.render(frame);
// };
mReciever.addRenderer(1, EasyAV::MediaType::video, videoRenderer, {ui->leFilter->text().toStdString(), mWtFilterHwConfig->getConfig()});
// mReciever.addRenderer(1,
// EasyAV::MediaType::audio,
// audioRenderer,
// mAudioRenderer.buildFilterChain(),
// std::nullopt);
// auto duration = liveClient->getDuration();
mTimeBar->setRange(0, 10000);
mTimeBar->setSingleStep(10); // Step size of 1 second
emit ready();
}
void MediaPlayerWt::initFromOpenCvCamera(int index)
{
#ifdef BUILD_OPENCV_SUPPORT_EASYSTREAMER
if (mCvTimer)
{
mCvTimer->stop();
mCvTimer->deleteLater();
}
mCvCapture = std::make_unique<cv::VideoCapture>("/dev/video" + std::to_string(index));
if (!mCvCapture->isOpened())
{
qCCritical(QLoggingCategory("EasyStreamer")) << "Failed to open camera:" << index;
return;
}
mCvTimer = new QTimer(this);
connect(mCvTimer, &QTimer::timeout, this, [this]() {
cv::Mat frame;
if (!mCvCapture->read(frame))
return;
auto easyFrame = EasyApi::CVEasyFrame::createFromMat(frame);
std::shared_ptr<EasyAV::IEasyFrame> yuvFrame;
mCvYuvFilter->process(easyFrame, yuvFrame);
submitVideoFrame(yuvFrame);
});
mCvYuvFilter = std::make_unique<EasyAV::EasyFilter>("format=yuv420p");
mCvTimer->start(30); // ~30 FPS
emit ready();
#endif
}
void MediaPlayerWt::initFromOpenCvFile(const QString &path)
{
#ifdef BUILD_OPENCV_SUPPORT_EASYSTREAMER
if (mCvTimer)
{
mCvTimer->stop();
mCvTimer->deleteLater();
}
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);
connect(mCvTimer, &QTimer::timeout, this, [this]() {
cv::Mat frame;
if (!mCvCapture->read(frame))
return;
auto easyFrame = EasyApi::CVEasyFrame::createFromMat(frame);
std::shared_ptr<EasyAV::IEasyFrame> yuvFrame;
mCvYuvFilter->process(easyFrame, yuvFrame);
submitVideoFrame(yuvFrame);
});
mCvYuvFilter = std::make_unique<EasyAV::EasyFilter>("format=yuv420p");
mCvTimer->start(33);
emit ready();
#endif
}
EasyAV::MediaReciever &MediaPlayerWt::reciever() { return mReciever; }
AudioRenderer &MediaPlayerWt::audioRenderer() { return mAudioRenderer; }
bool MediaPlayerWt::isClosing() const { return mClosing.load(std::memory_order_acquire); }
void MediaPlayerWt::invokeOnGui(std::function<void(MediaPlayerWt &)> cb)
{
if (!cb || isClosing())
return;
QPointer<MediaPlayerWt> guard(this);
QMetaObject::invokeMethod(
this,
[guard, cb = std::move(cb)]() mutable {
if (!guard || guard->isClosing())
return;
cb(*guard);
},
Qt::QueuedConnection);
}
void MediaPlayerWt::submitVideoFrame(const std::shared_ptr<EasyAV::IEasyFrame> &frame)
{
if (isClosing())
return;
if (auto widget = qobject_cast<EasyGL::VideoGlWidget *>(mWtVideoGl))
{
widget->submitFrame(frame);
return;
}
if (auto widget = qobject_cast<EasyGL::SharedVideoGlWidget *>(mWtVideoGl))
{
widget->submitFrame(frame);
return;
}
qCWarning(catg) << _FUNC_RAW_ << "Unsupported video widget backend";
}
void MediaPlayerWt::setupWidgets()
{
qCDebug(catg) << _FUNC_RAW_;
ui->wtCenter->addWidget(mWtVideoGl);
mVideoPlaceholder = new QWidget(this);
mVideoPlaceholder->setObjectName("videoDetachedPlaceholder");
ui->wtCenter->addWidget(mVideoPlaceholder);
ui->wtCenter->setCurrentWidget(mWtVideoGl);
// ui->wtCenter->addWidget(mWtVideoQt);
ui->loBottom->addWidget(mTimeBar, 0, 0);
ui->loBottom->addWidget(mWtControl, 1, 0, Qt::AlignLeft);
ui->loBottom->addWidget(mVolumeSlider, 1, 1, Qt::AlignLeft);
ui->loHwConfig->addWidget(mWtCodecHwConfig);
ui->loHwConfigFilter->addWidget(mWtFilterHwConfig);
mOverlay = new OverlayWidget(mWtVideoGl);
mOverlay->setGeometry(mWtVideoGl->rect());
mOverlay->setParent(mWtVideoGl);
mOverlay->hide(); // по умолчанию скрыт
mOverlay->raise(); // поверх видео
// по умолчанию не перехватывает клики (см. OverlayWidget::setEnabledForClicks)
mOverlay->setEnabledForClicks(false);
// следим за изменением размера mWtVideoGl чтобы подгонять overlay
mWtVideoGl->installEventFilter(this);
// сигнал из overlay о клике (payload) — можно подключить к отправке в encoder/pipeline
connect(mOverlay, &OverlayWidget::overlayClicked, this, [this](const QByteArray &payload) { emit overlayPayloadReady(payload); });
ui->cbCodec->insertItem(0, "auto");
ui->cbCodec->insertItem(1, "h264");
ui->cbCodec->insertItem(2, "h265");
// ui->cbCodec->insertItem(3, "h264_vaapi");
// ui->cbCodec->insertItem(4, "h264_rkmpp");
ui->cbCodec->insertItem(5, "h264_cuvid");
// TODO add more
mWtControl->setPlayCallback([this]() -> bool { return onPlay(); });
mWtControl->setStopCallback([this]() { return onStop(); });
mWtControl->setPauseCallback([this]() { return onPause(); });
mWtControl->setResumeCallback([this]() { return onResume(); });
mVolumeSlider->setMaximum(100);
mVolumeSlider->setMinimum(0);
mVolumeSlider->setSliderPosition(100);
connect(mVolumeSlider, &QSlider::sliderMoved, this, [this](int pos) {
qreal volume = static_cast<qreal>(pos) / 100.0;
mAudioRenderer.getAudioSink()->setVolume(volume);
});
connect(mTimeBar, &QSlider::sliderReleased, this, [=, this]() {
mReciever.seekForPipeline(1, std::chrono::milliseconds(mTimeBar->value()));
mSeeking = false;
});
connect(mTimeBar, &QSlider::sliderPressed, this, [=, this]() { mSeeking = true; });
mTimeBar->setObjectName("timeBar_sof");
}
void MediaPlayerWt::loadSettings()
{
// TODO
// ui->leDeviceName->setText("/dev/dri/renderD128");
// ui->cbHwAccel->setCurrentText("vaapi"); //vaapi;
mWtCodecHwConfig->setConfig(EasyAV::HardwareConfig{EasyAV::HWDeviceType::vaapi, "/dev/dri/renderD128"});
mWtFilterHwConfig->setConfig(EasyAV::HardwareConfig{EasyAV::HWDeviceType::vaapi, "/dev/dri/renderD128"});
ui->leFilter->setText("");
}
void MediaPlayerWt::saveSettings()
{
// TODO
}
bool MediaPlayerWt::onPlay() { return mReciever.playPipeline(1); }
void MediaPlayerWt::onPause() { mReciever.pausePipeline(1); }
void MediaPlayerWt::onResume() { mReciever.resumePipeline(1); }
void MediaPlayerWt::onStop() { mReciever.stopPipeline(1); }
void MediaPlayerWt::setOverlayEnabled(bool enabled)
{
mOverlayEnabled = enabled;
if (!mOverlay)
return;
mOverlay->setEnabledForClicks(enabled);
if (enabled)
mOverlay->show();
else
mOverlay->hide();
}
QByteArray MediaPlayerWt::getOverlayData() { return mOverlay->getPayload(); }
bool MediaPlayerWt::eventFilter(QObject *watched, QEvent *event)
{
if (watched == mDetachedVideoDialog && event->type() == QEvent::Close)
{
setVideoDetached(false);
event->ignore();
return true;
}
if (watched == mWtVideoGl && event->type() == QEvent::Resize)
{
if (mOverlay)
{
mOverlay->setGeometry(mWtVideoGl->rect());
mOverlay->raise();
}
}
return QWidget::eventFilter(watched, event);
}
bool MediaPlayerWt::getOverlayEnabled() const { return mOverlayEnabled; }
QImage MediaPlayerWt::makeSwScaledImage(AVFrame *frameNV12, int scaledWidth, int scaledHeight)
{
qCDebug(catgV) << _FUNC_RAW_;
if (!frameNV12 || frameNV12->width <= 0 || frameNV12->height <= 0)
{
qCWarning(catg) << "Invalid input frame. Frame is null or has non-positive dimensions:" << "width="
<< (frameNV12 ? frameNV12->width : -1) << "height=" << (frameNV12 ? frameNV12->height : -1);
return {};
}
AVFrame *frameRGB = av_frame_alloc();
if (!frameRGB)
{
qCWarning(catg) << "Failed to allocate AVFrame for RGB conversion.";
return {};
}
SwsContext *convert_context = sws_getContext(frameNV12->width, frameNV12->height, AVPixelFormat(frameNV12->format), scaledWidth,
scaledHeight, AV_PIX_FMT_RGB24, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
if (!convert_context)
{
qCWarning(catg) << "Failed to create SwsContext:" << "srcWidth=" << frameNV12->width << "srcHeight=" << frameNV12->height
<< "srcFormat=" << frameNV12->format << "dstWidth=" << scaledWidth << "dstHeight=" << scaledHeight
<< "dstFormat=RGB24";
av_frame_free(&frameRGB);
return {};
}
frameRGB->width = scaledWidth;
frameRGB->height = scaledHeight;
frameRGB->format = AVPixelFormat::AV_PIX_FMT_RGB24;
if (av_image_alloc(frameRGB->data, frameRGB->linesize, frameRGB->width, frameRGB->height, AVPixelFormat::AV_PIX_FMT_RGB24, 16) < 0)
{
qCWarning(catg) << "Failed to allocate image buffer for RGB frame:" << "width=" << frameRGB->width << "height=" << frameRGB->height
<< "format=RGB24";
sws_freeContext(convert_context);
av_frame_free(&frameRGB);
return {};
}
sws_scale(convert_context, frameNV12->data, frameNV12->linesize, 0, frameNV12->height, frameRGB->data, frameRGB->linesize);
sws_freeContext(convert_context);
QImage rawImage(frameRGB->width, frameRGB->height, QImage::Format_RGB888);
if (rawImage.isNull())
{
qCWarning(catg) << "Failed to create QImage from RGB frame:" << "width=" << frameRGB->width << "height=" << frameRGB->height;
av_freep(&frameRGB->data[0]);
av_frame_free(&frameRGB);
return {};
}
for (std::size_t y = 0; y < frameRGB->height; ++y)
{
memcpy(rawImage.scanLine(y), frameRGB->data[0] + y * frameRGB->linesize[0], frameRGB->width * 3);
}
av_freep(&frameRGB->data[0]);
av_frame_free(&frameRGB);
return rawImage;
}