From e7e5c89807a7f489a9c3462d4ab3cfad3401e9c2 Mon Sep 17 00:00:00 2001 From: luocai Date: Tue, 13 Aug 2024 20:06:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0=E7=94=BB?= =?UTF-8?q?=E7=BA=BF=E5=92=8C=E8=A7=86=E9=A2=91=E6=98=BE=E7=A4=BA=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AntiClipSettings.rc | 61 ++++++++++++++++++++++ Application.cpp | 71 +++++++++++++++++++++++++ Application.h | 47 +++++++++++++++++ CMakeLists.txt | 61 +++++++++++++++++++++- Configuration.h.in | 3 ++ DeviceConnection.cpp | 113 +++++++++++++++++++++++++++++++++++++++ DeviceConnection.h | 39 ++++++++++++++ DeviceView.qml | 116 +++++++++++++++++++++++++++++++++++++++++ H264Palyer.cpp | 65 +++++++++++++++++++++++ H264Palyer.h | 25 +++++++++ Main.qml | 33 +++++++++++- VideoFrameProvider.cpp | 26 +++++++++ VideoFrameProvider.h | 16 ++++++ main.cpp | 24 ++++----- resources/logo.ico | Bin 0 -> 1150 bytes resources/logo.png | Bin 0 -> 3353 bytes 16 files changed, 684 insertions(+), 16 deletions(-) create mode 100644 AntiClipSettings.rc create mode 100644 Application.cpp create mode 100644 Application.h create mode 100644 Configuration.h.in create mode 100644 DeviceConnection.cpp create mode 100644 DeviceConnection.h create mode 100644 DeviceView.qml create mode 100644 H264Palyer.cpp create mode 100644 H264Palyer.h create mode 100644 VideoFrameProvider.cpp create mode 100644 VideoFrameProvider.h create mode 100644 resources/logo.ico create mode 100644 resources/logo.png diff --git a/AntiClipSettings.rc b/AntiClipSettings.rc new file mode 100644 index 0000000..215aef4 --- /dev/null +++ b/AntiClipSettings.rc @@ -0,0 +1,61 @@ +// Microsoft Visual C++ generated resource script. +// + + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// 中文(简体,中国) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) +LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED +#pragma code_page(936) + +#ifdef APSTUDIO_INVOKED + ///////////////////////////////////////////////////////////////////////////// + // + // TEXTINCLUDE + // + + 2 TEXTINCLUDE BEGIN "#include " + "winres.h" + "\r\n" + "\0" END + + 3 TEXTINCLUDE BEGIN "\r\n" + "\0" END + +#endif // APSTUDIO_INVOKED + + ///////////////////////////////////////////////////////////////////////////// + // + // Icon + // + + // Icon with lowest ID value placed first to ensure application icon + // remains consistent on all systems. + IDI_ICON1 ICON "resources\\logo.ico" + +#endif // 中文(简体,中国) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Application.cpp b/Application.cpp new file mode 100644 index 0000000..a01c6f0 --- /dev/null +++ b/Application.cpp @@ -0,0 +1,71 @@ +#include "Application.h" +#include "BoostLog.h" +#include "Configuration.h" +#include "DeviceConnection.h" +#include "H264Palyer.h" +#include "VideoFrameProvider.h" +#include +#include + +Application::Application(int &argc, char **argv) + : m_app(std::make_shared(argc, argv)), m_videoFrameProvider(new VideoFrameProvider()), + m_player(std::make_shared()) { + m_app->setApplicationName(APPLICATION_NAME); + m_app->setApplicationVersion(QString("v%1_%2 build: %3 %4").arg(APP_VERSION, GIT_COMMIT_ID, __DATE__, __TIME__)); + m_player->open(); +} + +QList Application::currentOpenDoorAreaPoints() const { + return m_currentOpenDoorAreaPoints; +} + +void Application::setCurrentOpenDoorAreaPoints(const QList &points) { + if (m_currentOpenDoorAreaPoints != points) { + m_currentOpenDoorAreaPoints = points; + emit currentOpenDoorAreaPointsChanged(); + } +} + +void Application::onDeviceOpenDoorAreaPoints(const QList &points) { + setCurrentOpenDoorAreaPoints(points); + LOG(info) << "onDeviceOpenDoorAreaPoints: " << points.size(); +} + +int Application::exec() { + QQmlApplicationEngine engine; + engine.addImageProvider("videoframe", m_videoFrameProvider); + QObject::connect( + &engine, &QQmlApplicationEngine::objectCreationFailed, this, []() { QCoreApplication::exit(-1); }, + Qt::QueuedConnection); + engine.loadFromModule("AntiClipSettings", "Main"); + return m_app->exec(); +} + +void Application::open() { + LOG(info) << "Application::start"; + m_device = new DeviceConnection(); + connect(m_device, &DeviceConnection::currentOpenDoorAreaPointsChanged, this, + &Application::onDeviceOpenDoorAreaPoints); + m_device->connect(); + m_device->setH264FrameCallback([this](const char *data, uint32_t size) { + auto image = m_player->decode((const uint8_t *)data, size); + if (image) { + m_videoFrameProvider->setImage(*image); + emit newVideoFrame(); + } + }); +} + +void Application::start() { + m_device->start(); +} + +Application *Application::create(QQmlEngine *qmlEngine, QJSEngine *jsEngine) { + Application *ret = nullptr; + auto app = Amass::Singleton::instance(); + if (app) { + ret = app.get(); + QJSEngine::setObjectOwnership(ret, QJSEngine::CppOwnership); + } + return ret; +} diff --git a/Application.h b/Application.h new file mode 100644 index 0000000..b3f0f9a --- /dev/null +++ b/Application.h @@ -0,0 +1,47 @@ +#ifndef APPLICATION_H +#define APPLICATION_H + +#include "Singleton.h" +#include +#include +#include + +class QGuiApplication; +class DeviceConnection; +class VideoFrameProvider; +class H264Palyer; + +class Application : public QObject { + Q_OBJECT + QML_NAMED_ELEMENT(App) + QML_SINGLETON + Q_PROPERTY(QList currentOpenDoorAreaPoints READ currentOpenDoorAreaPoints WRITE + setCurrentOpenDoorAreaPoints NOTIFY currentOpenDoorAreaPointsChanged) + friend class Amass::Singleton; + +public: + QList currentOpenDoorAreaPoints() const; + void setCurrentOpenDoorAreaPoints(const QList &points); + int exec(); + Q_INVOKABLE void open(); + Q_INVOKABLE void start(); + static Application *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine); + +signals: + void newVideoFrame(); + void currentOpenDoorAreaPointsChanged(); + +protected: + Application(int &argc, char **argv); + void onDeviceOpenDoorAreaPoints(const QList &points); + +private: + std::shared_ptr m_app; + VideoFrameProvider *m_videoFrameProvider = nullptr; + std::shared_ptr m_player; + DeviceConnection *m_device = nullptr; + + QList m_currentOpenDoorAreaPoints; +}; + +#endif // APPLICATION_H diff --git a/CMakeLists.txt b/CMakeLists.txt index a928704..0d07238 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,22 +1,51 @@ cmake_minimum_required(VERSION 3.16) project(AntiClipSettings VERSION 0.1 LANGUAGES CXX) +set(APPLICATION_NAME "T009上位机") +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -find_package(Qt6 6.5 REQUIRED COMPONENTS Quick) +set(Projects_ROOT E:/Projects) +set(Libraries_ROOT ${Projects_ROOT}/Libraries) + +set(BOOST_ROOT ${Libraries_ROOT}/boost_1_85_0_msvc2022_64bit) +set(Boost_INCLUDE_DIR ${BOOST_ROOT}/include/boost-1_85) +option(Boost_USE_STATIC_LIBS OFF) +add_compile_definitions( + BOOST_USE_WINAPI_VERSION=BOOST_WINAPI_VERSION_WIN10 +) + +set(FFmpeg_ROOT ${Libraries_ROOT}/ffmpeg-7.0.1-full_build-shared) +set(FFmpeg_INCLUDE_DIR ${FFmpeg_ROOT}/include) +set(FFmpeg_LIB_DIR ${FFmpeg_ROOT}/lib) + +find_package(Boost REQUIRED COMPONENTS json) +find_package(Qt6 REQUIRED COMPONENTS Qml Quick) qt_standard_project_setup(REQUIRES 6.5) +execute_process( + COMMAND D:/msys64/usr/bin/git rev-parse --short HEAD + OUTPUT_VARIABLE GIT_COMMIT_ID + OUTPUT_STRIP_TRAILING_WHITESPACE +) +configure_file(Configuration.h.in Configuration.h) + qt_add_executable(AntiClipSettings + AntiClipSettings.rc main.cpp + Application.h Application.cpp + DeviceConnection.h DeviceConnection.cpp + H264Palyer.h H264Palyer.cpp + VideoFrameProvider.h VideoFrameProvider.cpp ) qt_add_qml_module(AntiClipSettings URI AntiClipSettings - VERSION 1.0 QML_FILES Main.qml + DeviceView.qml ) # Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. @@ -30,8 +59,27 @@ set_target_properties(AntiClipSettings PROPERTIES WIN32_EXECUTABLE TRUE ) +add_subdirectory(${Projects_ROOT}/Kylin/Universal Universal) + +target_include_directories(AntiClipSettings + PRIVATE ${FFmpeg_INCLUDE_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR} +) + +target_link_directories(AntiClipSettings + PRIVATE ${FFmpeg_LIB_DIR} +) + target_link_libraries(AntiClipSettings + PRIVATE Qt6::Qml PRIVATE Qt6::Quick + PRIVATE Boost::json + PRIVATE avcodec + PRIVATE swscale + PRIVATE avutil + PRIVATE avdevice + PRIVATE avformat + PRIVATE Universal ) include(GNUInstallDirs) @@ -40,3 +88,12 @@ install(TARGETS AntiClipSettings LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) + +qt_generate_deploy_qml_app_script( + TARGET AntiClipSettings + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM +) +install(SCRIPT ${deploy_script}) diff --git a/Configuration.h.in b/Configuration.h.in new file mode 100644 index 0000000..733e4b6 --- /dev/null +++ b/Configuration.h.in @@ -0,0 +1,3 @@ +#define APPLICATION_NAME "@APPLICATION_NAME@" +#define GIT_COMMIT_ID "@GIT_COMMIT_ID@" +#define APP_VERSION "@PROJECT_VERSION@" diff --git a/DeviceConnection.cpp b/DeviceConnection.cpp new file mode 100644 index 0000000..e2f25d5 --- /dev/null +++ b/DeviceConnection.cpp @@ -0,0 +1,113 @@ +#include "DeviceConnection.h" +#include "BoostLog.h" +#include +#include +#include +#include +#include +#include + +DeviceConnection::DeviceConnection(QObject *parent) : QObject{parent} { +} + +void DeviceConnection::connect() { + m_commandSocket = new QTcpSocket(this); + m_h264Socket = new QTcpSocket(this); + + QObject::connect(m_commandSocket, &QTcpSocket::connected, this, &DeviceConnection::onConnected); + QObject::connect(m_h264Socket, &QTcpSocket::connected, this, &DeviceConnection::onConnected); + + QObject::connect(m_h264Socket, &QTcpSocket::readyRead, this, &DeviceConnection::onH264ReadyRead); + QObject::connect(m_commandSocket, &QTcpSocket::readyRead, this, &DeviceConnection::onCommandReadyRead); + + m_commandSocket->connectToHost("192.168.10.2", 8000); + m_h264Socket->connectToHost("192.168.10.2", 8000); +} + +void DeviceConnection::start() { + boost::json::object request; + request["func"] = "openlivestream_setdata"; + request["deviceid"] = "0"; + + boost::json::object data; + data["value"] = "1"; + request["data"] = std::move(data); + auto text = boost::json::serialize(request); + m_h264Socket->write(text.data(), text.size()); +} + +void DeviceConnection::requestOpenDoorArea() { + boost::json::object request; + request["func"] = "a03opendoor1_getdata"; + request["deviceid"] = "0"; + boost::json::object data; + request["data"] = std::move(data); + auto text = boost::json::serialize(request); + m_commandSocket->write(text.data(), text.size()); + LOG(info) << "requestOpenDoorArea"; +} + +void DeviceConnection::handleCommand(const std::string_view &replyText) { + auto replyValue = boost::json::parse(replyText); + auto &reply = replyValue.as_object(); + auto &function = reply.at("func").as_string(); + if (function == "a03opendoor1_getdata") { + auto &data = reply.at("data").as_object(); + auto &pointArray = data.at("points").as_array(); + QList points; + for (auto &p : pointArray) { + QPointF point; + auto &obj = p.as_object(); + point.setX(obj.at("x").as_double()); + point.setY(obj.at("y").as_double()); + points.push_back(point); + } + emit currentOpenDoorAreaPointsChanged(points); + } +} + +void DeviceConnection::onConnected() { + LOG(info) << "onConnected"; + auto socket = dynamic_cast(sender()); + if (socket == m_commandSocket) { + requestOpenDoorArea(); + } +} + +void DeviceConnection::setH264FrameCallback(H264FrameCallback &&callback) { + m_frameCallback = std::move(callback); +} + +void DeviceConnection::onH264ReadyRead() { + auto data = m_h264Socket->readAll(); + + m_h264Buffer.push_back(data); + + while (!m_h264Buffer.isEmpty()) { + auto packageSize = ntohl(*reinterpret_cast(m_h264Buffer.data())); + if (m_h264Buffer.size() < (packageSize + sizeof(uint32_t))) break; + // LOG(info) << "onH264ReadyRead " << data.size() << " " << packageSize; + if (m_receivedFirstJsonReply) { + if (m_frameCallback) { + m_frameCallback(m_h264Buffer.data() + sizeof(uint32_t), packageSize); + } + } else { + LOG(info) << "h264 reply: " << m_h264Buffer.data() + sizeof(uint32_t); + m_receivedFirstJsonReply = true; + } + m_h264Buffer.remove(0, packageSize + sizeof(uint32_t)); + } +} + +void DeviceConnection::onCommandReadyRead() { + auto data = m_commandSocket->readAll(); + m_commandBuffer.push_back(data); + + while (!m_commandBuffer.isEmpty()) { + auto packageSize = ntohl(*reinterpret_cast(m_commandBuffer.data())); + if (m_commandBuffer.size() < (packageSize + sizeof(uint32_t))) break; + LOG(info) << "h264 reply: " << m_commandBuffer.data() + sizeof(uint32_t); + handleCommand(std::string_view(m_commandBuffer.data() + sizeof(uint32_t), packageSize)); + m_commandBuffer.remove(0, packageSize + sizeof(uint32_t)); + } +} diff --git a/DeviceConnection.h b/DeviceConnection.h new file mode 100644 index 0000000..0b0c1ce --- /dev/null +++ b/DeviceConnection.h @@ -0,0 +1,39 @@ +#ifndef DEVICECONNECTION_H +#define DEVICECONNECTION_H + +#include +#include + +class QTcpSocket; + +class DeviceConnection : public QObject { + Q_OBJECT +public: + using H264FrameCallback = std::function; + explicit DeviceConnection(QObject *parent = nullptr); + void setH264FrameCallback(H264FrameCallback &&callback); + void connect(); + void start(); + void requestOpenDoorArea(); + +signals: + void currentOpenDoorAreaPointsChanged(const QList &points); + +protected: + void onConnected(); + void onH264ReadyRead(); + void onCommandReadyRead(); + void handleCommand(const std::string_view &replyText); + +private: + QTcpSocket *m_commandSocket = nullptr; + + QTcpSocket *m_h264Socket = nullptr; + bool m_receivedFirstJsonReply = false; + + QByteArray m_commandBuffer; + QByteArray m_h264Buffer; + H264FrameCallback m_frameCallback; +}; + +#endif // DEVICECONNECTION_H diff --git a/DeviceView.qml b/DeviceView.qml new file mode 100644 index 0000000..d433cb7 --- /dev/null +++ b/DeviceView.qml @@ -0,0 +1,116 @@ +import QtQuick +import AntiClipSettings + +Item { + id: root + property int dargWidth: 10 + property var openDoorAreaPoints: [] + Image { + id: image + anchors.centerIn: parent + cache: false + fillMode: Image.PreserveAspectFit + source: "image://videoframe/" + property real aspectRatio: 16 / 9 + width: Math.min(root.width, root.height * aspectRatio) + height: width / aspectRatio + + Canvas { + id: canvas + anchors.fill: parent + onPaint: { + var ctx = canvas.getContext("2d") + ctx.clearRect(0, 0, canvas.width, canvas.height) + if (openDoorAreaPoints.length > 0) { + ctx.strokeStyle = "red" + ctx.lineWidth = 2 + + ctx.beginPath() + let point = scaledPoint(openDoorAreaPoints[0], + width, height) + ctx.moveTo(point.x, point.y) + for (var i = 1; i < openDoorAreaPoints.length; i++) { + point = scaledPoint(openDoorAreaPoints[i], + width, height) + ctx.lineTo(point.x, point.y) + } + ctx.closePath() + ctx.stroke() + } + } + } + + Repeater { + id: repeater + model: openDoorAreaPoints + delegate: Rectangle { + width: dargWidth + height: dargWidth + color: "red" + x: scaledPoint(modelData, canvas.width, + canvas.height).x - width / 2 + y: scaledPoint(modelData, canvas.width, + canvas.height).y - height / 2 + } + } + + MouseArea { + anchors.fill: parent + property int draggedPointIndex: -1 + onPressed: mouse => { + for (var i = 0; i < openDoorAreaPoints.length; i++) { + let point = scaledPoint(openDoorAreaPoints[i], + canvas.width, + canvas.height) + if (isInside(mouse.x, mouse.y, point)) { + draggedPointIndex = i + break + } + } + } + onReleased: { + draggedPointIndex = -1 + } + onPositionChanged: mouse => { + if (draggedPointIndex >= 0) { + openDoorAreaPoints[draggedPointIndex] = standardPoint( + Qt.point(mouse.x, mouse.y), + canvas.width, canvas.height) + canvas.requestPaint() + repeater.model = openDoorAreaPoints + } + } + + function isInside(x, y, point) { + let edge = dargWidth / 2 + return x >= point.x - edge && x <= point.x + edge + && y >= point.y - edge && y <= point.y + edge + } + } + } + + // 转换为显示画点 + function scaledPoint(point, width, height) { + let x = point.x * width / 640 + let y = point.y * height / 360 + return Qt.point(x, y) + } + + function standardPoint(point, width, height) { + // 转换为设备画点(640x360) + let x = point.x * 640 / width + let y = point.y * 360 / height + return Qt.point(x, y) + } + + Connections { + target: App + function onNewVideoFrame() { + image.source = "" + image.source = "image://videoframe/" + } + function onCurrentOpenDoorAreaPointsChanged() { + canvas.requestPaint() + } + } +} diff --git a/H264Palyer.cpp b/H264Palyer.cpp new file mode 100644 index 0000000..1facb8c --- /dev/null +++ b/H264Palyer.cpp @@ -0,0 +1,65 @@ +#include "H264Palyer.h" +#include "BoostLog.h" +extern "C" { +#include +#include +} + +H264Palyer::H264Palyer() { +} + +H264Palyer::~H264Palyer() { + if (m_packet != nullptr) { + av_packet_free(&m_packet); + } +} + +void H264Palyer::open() { + auto codec = avcodec_find_decoder(AV_CODEC_ID_H264); + if (!codec) { + LOG(error) << "cannot find h264 codec."; + return; + } + m_codecContext = avcodec_alloc_context3(codec); + if (m_codecContext == nullptr) { + LOG(error) << "Could not allocate video codec context"; + return; + } + if (avcodec_open2(m_codecContext, codec, nullptr) < 0) { + LOG(error) << "Could not open codec"; + return; + } + + m_packet = av_packet_alloc(); + if (m_packet == nullptr) { + LOG(error) << "Could not allocate AVPacket"; + } + m_frame = av_frame_alloc(); + if (m_frame == nullptr) { + LOG(error) << "Could not allocate AVFrame"; + } +} + +std::optional H264Palyer::decode(const uint8_t *data, uint32_t size) { + m_packet->data = const_cast(data); + m_packet->size = size; + int ret = avcodec_send_packet(m_codecContext, m_packet); + if (ret < 0) { + return std::nullopt; + } + ret = avcodec_receive_frame(m_codecContext, m_frame); + if (ret < 0) { + return std::nullopt; + } + + SwsContext *swsCtx = sws_getContext(m_frame->width, m_frame->height, m_codecContext->pix_fmt, m_frame->width, + m_frame->height, AV_PIX_FMT_RGB24, SWS_BILINEAR, nullptr, nullptr, nullptr); + + QImage image(m_frame->width, m_frame->height, QImage::Format_RGB888); + uint8_t *imageData[1] = {image.bits()}; + int linesize[1] = {3 * m_frame->width}; + sws_scale(swsCtx, m_frame->data, m_frame->linesize, 0, m_frame->height, imageData, linesize); + sws_freeContext(swsCtx); + // LOG(info) << image.width() << "x" << image.height(); + return image; +} diff --git a/H264Palyer.h b/H264Palyer.h new file mode 100644 index 0000000..f119bdb --- /dev/null +++ b/H264Palyer.h @@ -0,0 +1,25 @@ +#ifndef H264PALYER_H +#define H264PALYER_H + +#include +#include +#include + +typedef struct AVFrame AVFrame; +typedef struct AVPacket AVPacket; +typedef struct AVCodecContext AVCodecContext; + +class H264Palyer { +public: + H264Palyer(); + ~H264Palyer(); + void open(); + std::optional decode(const uint8_t *data, uint32_t size); + +private: + AVPacket *m_packet = nullptr; + AVFrame *m_frame = nullptr; + AVCodecContext *m_codecContext = nullptr; +}; + +#endif // H264PALYER_H diff --git a/Main.qml b/Main.qml index e06b9ae..ef6032d 100644 --- a/Main.qml +++ b/Main.qml @@ -1,8 +1,39 @@ import QtQuick +import QtQuick.Controls +import AntiClipSettings -Window { +ApplicationWindow { width: 640 height: 480 visible: true title: qsTr("Hello World") + + header: Row { + Button { + text: "连接" + onClicked: App.open() + } + + Button { + text: "开始" + onClicked: App.start() + } + } + + Rectangle { + id: deviceList + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + width: 250 + color: "red" + } + + DeviceView { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: deviceList.right + anchors.right: parent.right + openDoorAreaPoints: App.currentOpenDoorAreaPoints + } } diff --git a/VideoFrameProvider.cpp b/VideoFrameProvider.cpp new file mode 100644 index 0000000..822ff79 --- /dev/null +++ b/VideoFrameProvider.cpp @@ -0,0 +1,26 @@ +#include "VideoFrameProvider.h" + +VideoFrameProvider::VideoFrameProvider() + : QQuickImageProvider(QQuickImageProvider::Image), m_image(1280, 720, QImage::Format_RGB32) { + m_image.fill(Qt::black); +} + +QImage VideoFrameProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) { + Q_UNUSED(id); + + if (size) *size = m_image.size(); + + if (requestedSize.width() > 0 && requestedSize.height() > 0) + return m_image.scaled(requestedSize.width(), requestedSize.height(), Qt::KeepAspectRatio); + + return m_image; +} + +void VideoFrameProvider::setImage(const QImage &image) { + m_image = image; +} + +void VideoFrameProvider::reset() { + m_image = QImage(1280, 720, QImage::Format_RGB32); + m_image.fill(Qt::black); +} diff --git a/VideoFrameProvider.h b/VideoFrameProvider.h new file mode 100644 index 0000000..b919968 --- /dev/null +++ b/VideoFrameProvider.h @@ -0,0 +1,16 @@ +#ifndef __VIDEOFRAMEPROVIDER_H__ +#define __VIDEOFRAMEPROVIDER_H__ + +#include + +class VideoFrameProvider : public QQuickImageProvider { +public: + VideoFrameProvider(); + QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) final; + void setImage(const QImage &image); + void reset(); + +private: + QImage m_image; +}; +#endif // __VIDEOFRAMEPROVIDER_H__ diff --git a/main.cpp b/main.cpp index b335aa4..52e5bf7 100644 --- a/main.cpp +++ b/main.cpp @@ -1,18 +1,16 @@ +#include "Application.h" +#include "BoostLog.h" +#include "Configuration.h" #include #include -int main(int argc, char *argv[]) -{ - QGuiApplication app(argc, argv); +int main(int argc, char *argv[]) { + using namespace Amass; + boost::log::initialize("logs/app"); + LOG(info) << "Compiled on: " << __DATE__ << " " << __TIME__ << std::endl; + LOG(info) << "Git commit ID: " << GIT_COMMIT_ID << std::endl; + LOG(info) << "Program version: " << APP_VERSION << std::endl; - QQmlApplicationEngine engine; - QObject::connect( - &engine, - &QQmlApplicationEngine::objectCreationFailed, - &app, - []() { QCoreApplication::exit(-1); }, - Qt::QueuedConnection); - engine.loadFromModule("AntiClipSettings", "Main"); - - return app.exec(); + auto app = Singleton::instance(argc, argv); + return app->exec(); } diff --git a/resources/logo.ico b/resources/logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..ce91dac18d2f676574ed53d1ec76e4f70157053e GIT binary patch literal 1150 zcmb7AJxjzu5PgTDou&}sDzDQ*#9mOavG!m13pSk1`4?;jy*e=xKZ-RPE(rF5Tw!5h zD`;hP7>%=8=CZlnQ*XoU?7VsJ?PP%leVzweE$mGLi*#C{P*97aQSNl0bw8&=SMI$R zM?1H1P;m77;f$MUqgfn#N|#+mY6RTLv!<$T<_MDby9()DL>r@go?}E&m2$rcDc%?` z`Dq*;>N-5)zLljYNMD=r<~(?0pEM4xEDZxv9G6<{@y+nRGv6)6tKpMRtY<;xr~k>W zpP5=_?$uKj%RZHd{oZDe`Q!4ncpBdGhc52`pBY6L7*IZ;x?2Ft6zlWA<`l3s Tp!gzO1)U#0fR|YyUjlvrDSG;= literal 0 HcmV?d00001 diff --git a/resources/logo.png b/resources/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f77639fbb7d364ce04d2656a9d83fd7169c6e7e6 GIT binary patch literal 3353 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 jjb=o%_;&#Z07U--W{Z literal 0 HcmV?d00001