Загрузка данных
#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>
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<IVideoProcessor *>(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 "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_ARAVIS_SUPPORT_EASYSTREAMER
#include "easyapi_arvadapter/arveasyframe.h"
#endif
#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::initAravisCamera(const std::optional<QString> &cameraName)
{
#ifdef BUILD_ARAVIS_SUPPORT_EASYSTREAMER
if (mAravisTimer)
{
mAravisTimer->stop();
mAravisTimer->deleteLater();
mAravisTimer = nullptr;
}
arv_update_device_list();
qDebug() << "Attempting to open camera:" << cameraName;
unsigned int n_devices = arv_get_n_devices();
qDebug() << "Found Aravis devices:" << n_devices;
QByteArray idBytes;
const char *targetId = nullptr;
if (cameraName.has_value() && !cameraName->isEmpty())
{
idBytes = cameraName->toUtf8();
targetId = idBytes.constData();
qDebug() << "Searching for specific camera:" << targetId;
}
GError *error = nullptr;
ArvCamera *camera = arv_camera_new(targetId, &error);
if (error != nullptr)
{
qCCritical(QLoggingCategory("EasyStreamer")) << "Failed to open Aravis camera:" << error->message;
g_clear_error(&error);
return;
}
GError* setErr = nullptr;
arv_camera_set_pixel_format(camera, ARV_PIXEL_FORMAT_MONO_8, &setErr);
if (setErr)
{
qCWarning(QLoggingCategory("EasyStreamer")) << "Could not set формат:" << setErr->message;
g_clear_error(&setErr);
}
ArvStream *stream = arv_camera_create_stream(camera, nullptr, nullptr, &error);
if (error != nullptr)
{
qCCritical(QLoggingCategory("EasyStreamer")) << "Failed to create stream:" << error->message;
g_object_unref(camera);
g_clear_error(&error);
return;
}
gint payload_size = arv_camera_get_payload(camera, &error);
for (int i = 0; i < 30; i++)
{
arv_stream_push_buffer(stream, arv_buffer_new(payload_size, nullptr));
}
arv_camera_start_acquisition(camera, &error);
QPointer<MediaPlayerWt> guard(this);
mAravisTimer = new QTimer(this);
connect(mAravisTimer, &QTimer::timeout, this, [guard, stream]() {
if (!guard || guard->isClosing())
return;
ArvBuffer *buffer = arv_stream_pop_buffer(stream);
if (buffer != nullptr)
{
if (arv_buffer_get_status(buffer) == ARV_BUFFER_STATUS_SUCCESS)
{
auto easyFrame = std::make_shared<EasyApi::ArvEasyFrame>(buffer);
guard->submitVideoFrame(easyFrame);
}
arv_stream_push_buffer(stream, buffer);
}
});
connect(mAravisTimer, &QObject::destroyed, [camera, stream]() {
arv_camera_stop_acquisition(camera, nullptr);
g_object_unref(stream);
g_object_unref(camera);
});
mAravisTimer->start(10);
emit ready();
#endif
}
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;
}#include "easyav/reciever/mediareciever.h"
#include "pure_common/logtools.h"
RAW_DEVELOPER_LOG_CATGS(easyav, MediaReciever)
using namespace EasyAV;
MediaReciever::MediaReciever()
: mDecodersFactory()
{
RawLogger::debug(catgMem) << _P_FUNC_RAW_;
av_log_set_level(AV_LOG_WARNING);
#ifdef NOFFMPEGLOG
av_log_set_level(AV_LOG_QUIET);
#endif
}
MediaReciever::~MediaReciever()
{
RawLogger::debug(catgMem) << _P_FUNC_RAW_;
stopThreads();
}
bool MediaReciever::pipelineExists(PipelineId id)
{
std::unique_lock<std::mutex> ml(mPipelinesMutex);
auto itPipe = mPipelines.find(id);
if (itPipe == mPipelines.end())
{
return false;
}
return true;
}
bool MediaReciever::addMediaPipeline(PipelineId id, std::shared_ptr<ITransportClient> client)
{
RawLogger::debug(catg) << _P_FUNC_RAW_ << id;
if (!mThreadsInited)
initThreads();
if (!mThreadPools.decodingVideoThreadPool || !mThreadPools.decodingAudioThreadPool || !mThreadPools.decodingMiscThreadPool ||
!mThreadPools.demuxThreadPool || !mThreadPools.syncThreadPool || !mThreadPools.filterThreadPool)
{
RawLogger::debug(catg) << _P_FUNC_RAW_ << "can't create pipeline, threads are not initialized";
return false;
}
std::unique_lock<std::mutex> ml(mPipelinesMutex);
auto mediaPipeline = std::make_shared<MediaRecievePipeline>(id, client, mDecodersFactory, mThreadPools);
mPipelines.insert({id, mediaPipeline});
return true;
}
// std::mutex &MediaReciever::getGpuMutex(PipelineId id, int streamIdx)
// {
// auto itPipe = mPipelines.find(id);
// if (itPipe == mPipelines.end())
// {
// RawLogger::critical(catg) << _P_FUNC_RAW_ << "can't find pipeline " << id;
// }
// return itPipe->second->mStreamsById[streamIdx].decoder->gpuMutex;
// }
std::vector<StreamInfo> MediaReciever::getStreamInfos(PipelineId id)
{
std::unique_lock<std::mutex> ml(mPipelinesMutex);
auto itPipe = mPipelines.find(id);
if (itPipe == mPipelines.end())
{
RawLogger::critical(catg) << _P_FUNC_RAW_ << "can't find pipeline " << id;
return {};
}
return itPipe->second->getStreamInfos();
}
bool MediaReciever::addRenderer(PipelineId id, SourceIdx streamIdx, const MediaRecievePipeline::CbRenderer &renderer)
{
RawLogger::debug(catg) << _P_FUNC_RAW_ << id << streamIdx << static_cast<bool>(renderer);
std::unique_lock<std::mutex> ml(mPipelinesMutex);
auto itPipe = mPipelines.find(id);
if (itPipe == mPipelines.end())
{
RawLogger::critical(catg) << _P_FUNC_RAW_ << "can't find pipeline " << id;
return false;
}
auto pipe = itPipe->second;
pipe->addRenderer(streamIdx, renderer);
return true;
}
bool MediaReciever::addRenderer(PipelineId id, MediaType mediaType, const MediaRecievePipeline::CbRenderer &renderer)
{
RawLogger::debug(catg) << _P_FUNC_RAW_ << id << toString(mediaType) << static_cast<bool>(renderer);
std::unique_lock<std::mutex> ml(mPipelinesMutex);
auto itPipe = mPipelines.find(id);
if (itPipe == mPipelines.end())
{
RawLogger::critical(catg) << _P_FUNC_RAW_ << "can't find pipeline " << id;
return false;
}
auto pipe = itPipe->second;
pipe->addRenderer(mediaType, renderer);
return true;
}
bool MediaReciever::addRenderer(PipelineId id, MediaType mediaType, const MediaRecievePipeline::CbRenderer &renderer,
const FilterConfig &filterConfig)
{
RawLogger::debug(catg) << _P_FUNC_RAW_ << id << toString(mediaType) << static_cast<bool>(renderer);
std::unique_lock<std::mutex> ml(mPipelinesMutex);
auto itPipe = mPipelines.find(id);
if (itPipe == mPipelines.end())
{
RawLogger::critical(catg) << _P_FUNC_RAW_ << "can't find pipeline " << id;
return false;
}
auto pipe = itPipe->second;
pipe->addRenderer(mediaType, renderer, filterConfig);
return true;
}
bool MediaReciever::addRenderer(PipelineId id, SourceIdx streamIdx, const MediaRecievePipeline::CbRenderer &renderer,
const FilterConfig &filterConfig)
{
RawLogger::debug(catg) << _P_FUNC_RAW_ << id << streamIdx << static_cast<bool>(renderer);
std::unique_lock<std::mutex> ml(mPipelinesMutex);
auto itPipe = mPipelines.find(id);
if (itPipe == mPipelines.end())
{
RawLogger::critical(catg) << _P_FUNC_RAW_ << "can't find pipeline " << id;
return false;
}
auto pipe = itPipe->second;
pipe->addRenderer(streamIdx, renderer, filterConfig);
return true;
}
void MediaReciever::startThreads()
{
RawLogger::debug(catg) << _P_FUNC_RAW_;
if (mThreadPools.demuxThreadPool)
mThreadPools.demuxThreadPool->start();
if (mThreadPools.decodingAudioThreadPool)
mThreadPools.decodingAudioThreadPool->start();
if (mThreadPools.decodingVideoThreadPool)
mThreadPools.decodingVideoThreadPool->start();
if (mThreadPools.decodingMiscThreadPool)
mThreadPools.decodingMiscThreadPool->start();
if (mThreadPools.syncThreadPool)
mThreadPools.syncThreadPool->start();
if (mThreadPools.filterThreadPool)
mThreadPools.filterThreadPool->start();
}
void MediaReciever::stopThreads()
{
RawLogger::debug(catg) << _P_FUNC_RAW_;
if (mThreadPools.demuxThreadPool)
mThreadPools.demuxThreadPool->stop();
if (mThreadPools.decodingAudioThreadPool)
mThreadPools.decodingAudioThreadPool->stop();
if (mThreadPools.decodingVideoThreadPool)
mThreadPools.decodingVideoThreadPool->stop();
if (mThreadPools.decodingMiscThreadPool)
mThreadPools.decodingMiscThreadPool->stop();
if (mThreadPools.syncThreadPool)
mThreadPools.syncThreadPool->stop();
if (mThreadPools.filterThreadPool)
mThreadPools.filterThreadPool->stop();
}
void MediaReciever::stopAllPipelines()
{
RawLogger::debug(catg) << _P_FUNC_RAW_;
std::unique_lock<std::mutex> ml(mPipelinesMutex);
for (auto &[_, pipe] : mPipelines)
{
pipe->stop(EasyAV::MediaRecievePipeline::StopMode::fullReset);
}
}
bool MediaReciever::isPipelineExists(PipelineId id)
{
RawLogger::debug(catg) << _P_FUNC_RAW_;
std::unique_lock<std::mutex> ml(mPipelinesMutex);
auto itPipe = mPipelines.find(id);
if (itPipe == mPipelines.end())
{
return false;
}
return true;
}
void MediaReciever::seekForPipeline(PipelineId id, std::chrono::milliseconds timepoint)
{
RawLogger::debug(catg) << _P_FUNC_RAW_;
std::unique_lock<std::mutex> ml(mPipelinesMutex);
auto itPipe = mPipelines.find(id);
if (itPipe == mPipelines.end())
{
RawLogger::critical(catg) << _P_FUNC_RAW_ << "can't find pipeline " << id;
return;
}
auto pipe = itPipe->second;
pipe->seek(timepoint);
}
void MediaReciever::setFinishCb(PipelineId id, std::function<void()> cbOnFinish)
{
RawLogger::debug(catg) << _P_FUNC_RAW_;
std::unique_lock<std::mutex> ml(mPipelinesMutex);
auto itPipe = mPipelines.find(id);
if (itPipe == mPipelines.end())
{
RawLogger::critical(catg) << _P_FUNC_RAW_ << "can't find pipeline " << id;
return;
}
auto pipe = itPipe->second;
pipe->setCbOnFinish(cbOnFinish);
}
bool MediaReciever::initPipeline(PipelineId id)
{
RawLogger::debug(catg) << _P_FUNC_RAW_;
std::unique_lock<std::mutex> ml(mPipelinesMutex);
auto itPipe = mPipelines.find(id);
if (itPipe == mPipelines.end())
{
RawLogger::critical(catg) << _P_FUNC_RAW_ << "can't find pipeline " << id;
return false;
}
auto pipe = itPipe->second;
return pipe->init();
}
bool MediaReciever::playPipeline(PipelineId id)
{
RawLogger::debug(catg) << _P_FUNC_RAW_;
std::unique_lock<std::mutex> ml(mPipelinesMutex);
auto itPipe = mPipelines.find(id);
if (itPipe == mPipelines.end())
{
RawLogger::critical(catg) << _P_FUNC_RAW_ << "can't find pipeline " << id;
return false;
}
std::shared_ptr<MediaRecievePipeline> pipe = itPipe->second;
if (pipe->getState() == MediaRecievePipeline::State::idle)
pipe->init();
return pipe->play();
}
bool MediaReciever::stopPipeline(PipelineId id)
{
RawLogger::debug(catg) << _P_FUNC_RAW_;
std::unique_lock<std::mutex> ml(mPipelinesMutex);
auto itPipe = mPipelines.find(id);
if (itPipe == mPipelines.end())
{
RawLogger::critical(catg) << _P_FUNC_RAW_ << "can't find pipeline " << id;
return false;
}
auto pipe = itPipe->second;
pipe->stop(EasyAV::MediaRecievePipeline::StopMode::keepReady);
return true;
}
bool MediaReciever::pausePipeline(PipelineId id)
{
RawLogger::debug(catg) << _P_FUNC_RAW_;
std::unique_lock<std::mutex> ml(mPipelinesMutex);
auto itPipe = mPipelines.find(id);
if (itPipe == mPipelines.end())
{
RawLogger::critical(catg) << _P_FUNC_RAW_ << "can't find pipeline " << id;
return false;
}
auto pipe = itPipe->second;
pipe->pause();
return true;
}
bool MediaReciever::resumePipeline(PipelineId id)
{
RawLogger::debug(catg) << _P_FUNC_RAW_;
std::unique_lock<std::mutex> ml(mPipelinesMutex);
auto itPipe = mPipelines.find(id);
if (itPipe == mPipelines.end())
{
RawLogger::critical(catg) << _P_FUNC_RAW_ << "can't find pipeline " << id;
return false;
}
auto pipe = itPipe->second;
pipe->resume();
return true;
}
bool MediaReciever::removePipeline(PipelineId id)
{
RawLogger::debug(catg) << _P_FUNC_RAW_;
std::unique_lock<std::mutex> ml(mPipelinesMutex);
auto itPipe = mPipelines.find(id);
if (itPipe == mPipelines.end())
{
RawLogger::critical(catg) << _P_FUNC_RAW_ << "can't find pipeline " << id;
return false;
}
auto pipe = itPipe->second;
pipe->stop(EasyAV::MediaRecievePipeline::StopMode::fullReset);
mPipelines.erase(itPipe);
return true;
}
void MediaReciever::initThreads()
{
uint8_t demuxThreadCount{1};
uint8_t videoDecodingThreadCount{2};
uint8_t audioDecodingThreadCount{1};
uint8_t miscDecodingThreadCount{1};
uint8_t syncThreadCount{1};
uint8_t filterThreadCount{1};
mThreadPools.demuxThreadPool = std::make_shared<WorkingTools::ThreadPool>();
mThreadPools.decodingVideoThreadPool = std::make_shared<WorkingTools::ThreadPool>();
mThreadPools.decodingAudioThreadPool = std::make_shared<WorkingTools::ThreadPool>();
mThreadPools.decodingMiscThreadPool = std::make_shared<WorkingTools::ThreadPool>();
mThreadPools.syncThreadPool = std::make_shared<WorkingTools::ThreadPool>();
mThreadPools.filterThreadPool = std::make_shared<WorkingTools::ThreadPool>();
for (int i = 0; i < demuxThreadCount; i++)
{
mThreadPools.demuxThreadPool->addThread(1ms);
}
for (int i = 0; i < videoDecodingThreadCount; i++)
{
mThreadPools.decodingVideoThreadPool->addThread(1ms);
}
for (int i = 0; i < audioDecodingThreadCount; i++)
{
mThreadPools.decodingAudioThreadPool->addThread(1ms);
}
for (int i = 0; i < miscDecodingThreadCount; i++)
{
mThreadPools.decodingMiscThreadPool->addThread(5ms);
}
for (int i = 0; i < syncThreadCount; i++)
{
mThreadPools.syncThreadPool->addThread(1ms);
}
for (int i = 0; i < filterThreadCount; i++)
{
mThreadPools.filterThreadPool->addThread(1ms);
}
startThreads();
mThreadsInited = true;
}
void MediaReciever::setHardwareDeviceForCodec(PipelineId id, HardwareConfig hwConfig) { mDecodersFactory.setHardwareDevice(id, hwConfig); }
void MediaReciever::setCustomVideoCodecName(PipelineId id, std::string customCodecName)
{
mDecodersFactory.setVideoCodecName(id, customCodecName);
}
// bool MediaReciever::addCustomStream(PipelineId id, EasyAV::StreamInfo &info)
// {
// RawLogger::debug(catg) << _P_FUNC_RAW_ << id;
// auto itPipe = mPipelines.find(id);
// if (itPipe == mPipelines.end())
// {
// RawLogger::critical(catg) << _P_FUNC_RAW_ << "can't find pipeline " << id;
// return false;
// }
// return itPipe->second->addCustomStream(info);
// }
в cameramanager заменить получение на mediareciever с rtpcliet посмотри как реализровано в MediaPlayerWt с rtp