first commit
This commit is contained in:
commit
ab606d358a
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
bin/
|
||||||
|
build/
|
||||||
|
*.pro.user
|
277
BoxController.cpp
Normal file
277
BoxController.cpp
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
#include "BoxController.h"
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QTime>
|
||||||
|
|
||||||
|
const char* path = "/root/usb";
|
||||||
|
|
||||||
|
BoxController::BoxController(QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
// init output in constructor;
|
||||||
|
videoOutput = Link::create("OutputVo");
|
||||||
|
QVariantMap dataVo;
|
||||||
|
dataVo["type"] = "hdmi";
|
||||||
|
dataVo["interface"] = "HDMI-OUT0";
|
||||||
|
videoOutput->setData(dataVo);
|
||||||
|
videoOutput->start();
|
||||||
|
|
||||||
|
// init video duration
|
||||||
|
videoDuration = 1 * 60 * 1000;
|
||||||
|
recordTimer = new QTimer();
|
||||||
|
recordTimer->setInterval(videoDuration);
|
||||||
|
recordTimer->stop();
|
||||||
|
connect(recordTimer, SIGNAL(timeout()), this, SLOT(onTimeout()));
|
||||||
|
|
||||||
|
// init record params
|
||||||
|
videoEncodeParams["codec"] = "h264";
|
||||||
|
videoEncodeParams["width"] = 1920;
|
||||||
|
videoEncodeParams["height"] = 1080;
|
||||||
|
videoEncodeParams["bitrate"] = 4000;
|
||||||
|
videoEncodeParams["framerate"] = 30;
|
||||||
|
|
||||||
|
audioEncodeParams["codec"] = "aac";
|
||||||
|
audioEncodeParams["samplerate"] = 48000;
|
||||||
|
audioEncodeParams["bitrate"] = 128000;
|
||||||
|
audioEncodeParams["channels"] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief initialize "Link" components
|
||||||
|
*/
|
||||||
|
void BoxController::init()
|
||||||
|
{
|
||||||
|
// audio input by external microphone
|
||||||
|
audioInput = Link::create("InputAlsa");
|
||||||
|
QVariantMap dataAi;
|
||||||
|
dataAi["path"] = "hw:0,0";
|
||||||
|
dataAi["channels"] = 2;
|
||||||
|
audioInput->start(dataAi);
|
||||||
|
|
||||||
|
// audio output by external speaker
|
||||||
|
audioOutput = Link::create("OutputAlsa");
|
||||||
|
QVariantMap dataAo;
|
||||||
|
dataAo["path"] = "hw:0,0";
|
||||||
|
audioOutput->start(dataAo);
|
||||||
|
|
||||||
|
videoInputA = Link::create("InputVi");
|
||||||
|
QVariantMap dataVi;
|
||||||
|
dataVi["interface"] = "HDMI-A";
|
||||||
|
videoInputA->start(dataVi);
|
||||||
|
|
||||||
|
// capture picture from video
|
||||||
|
snap = Link::create("EncodeV");
|
||||||
|
QVariantMap dataSnap;
|
||||||
|
dataSnap["codec"] = "jpeg";
|
||||||
|
dataSnap["snap"] = true;
|
||||||
|
dataSnap["width"] = videoEncodeParams.value("width");
|
||||||
|
dataSnap["height"] = videoEncodeParams.value("height");
|
||||||
|
snap->start(dataSnap);
|
||||||
|
videoInputA->linkV(snap);
|
||||||
|
// sleep 1s, if didnt sleep , function snap would return flase and capture failed
|
||||||
|
QThread::sleep(1);
|
||||||
|
|
||||||
|
videoInputA->linkV(videoOutput);
|
||||||
|
|
||||||
|
// video encode
|
||||||
|
videoEncoder = Link::create("EncodeV");
|
||||||
|
videoEncoder->start(videoEncodeParams);
|
||||||
|
|
||||||
|
// audio encode
|
||||||
|
audioEncoder = Link::create("EncodeA");
|
||||||
|
audioEncoder->start(audioEncodeParams);
|
||||||
|
|
||||||
|
// record
|
||||||
|
record = Link::create("Mux");
|
||||||
|
QVariantMap dataMp4;
|
||||||
|
dataMp4["format"] = "mp4";
|
||||||
|
dataMp4["mute"] = true;
|
||||||
|
record->setData(dataMp4);
|
||||||
|
videoInputA->linkV(videoEncoder)->linkV(record);
|
||||||
|
audioInput->linkA(audioEncoder)->linkA(record);
|
||||||
|
|
||||||
|
// rtmp server
|
||||||
|
rtmpServer = Link::create("Mux");
|
||||||
|
QVariantMap dataRtmp;
|
||||||
|
dataRtmp["path"] = "rtmp://127.0.0.1/live/stream";
|
||||||
|
dataRtmp["mute"] = true;
|
||||||
|
rtmpServer->setData(dataRtmp);
|
||||||
|
videoInputA->linkV(videoEncoder)->linkV(rtmpServer);
|
||||||
|
audioInput->linkA(audioEncoder)->linkA(rtmpServer);
|
||||||
|
|
||||||
|
// init file and decoder
|
||||||
|
file = Link::create("InputFile");
|
||||||
|
file->start();
|
||||||
|
videoDecoder = Link::create("DecodeV");
|
||||||
|
videoDecoder->start();
|
||||||
|
audioDecoder = Link::create("DecodeA");
|
||||||
|
audioDecoder->start();
|
||||||
|
file->linkV(videoDecoder)->linkV(videoOutput);
|
||||||
|
file->linkA(audioDecoder)->linkA(audioOutput);
|
||||||
|
|
||||||
|
connect(file, &LinkObject::newEvent, [=](QString type, QVariant) {
|
||||||
|
if (type == "EOF") {
|
||||||
|
qDebug() << "one video playback end";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief start record
|
||||||
|
*/
|
||||||
|
void BoxController::startRecord()
|
||||||
|
{
|
||||||
|
if (!isMountDisk()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString curTime = QDateTime::currentDateTime().toString("yyyy-MM-dd_hh:mm:ss");
|
||||||
|
QVariantMap dataRecord;
|
||||||
|
dataRecord["path"] = QString("%1/videos/%2.mp4").arg(path).arg(curTime);
|
||||||
|
record->setData(dataRecord);
|
||||||
|
record->start();
|
||||||
|
isRecord = true;
|
||||||
|
|
||||||
|
recordTimer->start();
|
||||||
|
snap->invoke("snapSync", QString("%1/snap/%2.jpg").arg(path).arg(curTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief stop record
|
||||||
|
*/
|
||||||
|
void BoxController::stopRecord()
|
||||||
|
{
|
||||||
|
record->stop(true);
|
||||||
|
isRecord = false;
|
||||||
|
recordTimer->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief BoxController::onTimeout
|
||||||
|
*/
|
||||||
|
void BoxController::onTimeout()
|
||||||
|
{
|
||||||
|
if (!isMountDisk()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
record->stop(true);
|
||||||
|
recordTimer->stop();
|
||||||
|
|
||||||
|
QString curTime = QDateTime::currentDateTime().toString("yyyy-MM-dd_hh:mm:ss");
|
||||||
|
QVariantMap dataRecord;
|
||||||
|
dataRecord["path"] = QString("%1/videos/%2.mp4").arg(path).arg(curTime);
|
||||||
|
record->setData(dataRecord);
|
||||||
|
record->start();
|
||||||
|
|
||||||
|
recordTimer->start();
|
||||||
|
snap->invoke("snapSync", QString("%1/snap/%2.jpg").arg(path).arg(curTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoxController::startRtmpServer()
|
||||||
|
{
|
||||||
|
if (rtmpServer->getState() != "started") {
|
||||||
|
rtmpServer->start();
|
||||||
|
qDebug() << "start rtmp server...";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief open console process, and use it by command
|
||||||
|
* @param com
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
QString BoxController::writeCom(const QString& com)
|
||||||
|
{
|
||||||
|
QProcess proc;
|
||||||
|
QStringList argList;
|
||||||
|
argList << "-c" << com;
|
||||||
|
proc.start("/bin/sh", argList);
|
||||||
|
// wait process start
|
||||||
|
proc.waitForFinished();
|
||||||
|
proc.waitForReadyRead();
|
||||||
|
// read data from console
|
||||||
|
QByteArray procOutput = proc.readAll();
|
||||||
|
proc.close();
|
||||||
|
return QString(procOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief judge the disk whether mounted
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
bool BoxController::isMountDisk()
|
||||||
|
{
|
||||||
|
QString mount = writeCom(QString("df %1").arg(path));
|
||||||
|
if (!mount.contains(path))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief start playback and output hdmi signals
|
||||||
|
* @param fileName
|
||||||
|
*/
|
||||||
|
void BoxController::startPlayback(QString fileName)
|
||||||
|
{
|
||||||
|
QString path = "/root/usb/videos/" + fileName;
|
||||||
|
videoInputA->unLinkV(videoOutput);
|
||||||
|
QVariantMap dataFile;
|
||||||
|
dataFile["path"] = path;
|
||||||
|
file->start(dataFile);
|
||||||
|
emit playListNeedHide();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief play live
|
||||||
|
*/
|
||||||
|
void BoxController::startPlayLive()
|
||||||
|
{
|
||||||
|
videoInputA->linkV(videoOutput);
|
||||||
|
file->stop(true);
|
||||||
|
emit playListNeedHide();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief playback -10s
|
||||||
|
*/
|
||||||
|
void BoxController::back()
|
||||||
|
{
|
||||||
|
qDebug() << "back";
|
||||||
|
int curPos = file->invoke("getPosition").toInt();
|
||||||
|
curPos -= 10 * 100;
|
||||||
|
file->invoke("seek", curPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief playback +10s
|
||||||
|
*/
|
||||||
|
void BoxController::forward()
|
||||||
|
{
|
||||||
|
qDebug() << "forward";
|
||||||
|
int curPos = file->invoke("getPosition").toInt();
|
||||||
|
curPos += 10 * 100;
|
||||||
|
file->invoke("seek", curPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief playback pause
|
||||||
|
*/
|
||||||
|
void BoxController::pause()
|
||||||
|
{
|
||||||
|
QString state = file->getState();
|
||||||
|
if (state != "started") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file->invoke("pause", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief playback resume
|
||||||
|
*/
|
||||||
|
void BoxController::resume()
|
||||||
|
{
|
||||||
|
QString state = file->getState();
|
||||||
|
if (state != "started") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file->invoke("pause", false);
|
||||||
|
}
|
72
BoxController.h
Normal file
72
BoxController.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#ifndef BOXCONTROLLER_H
|
||||||
|
#define BOXCONTROLLER_H
|
||||||
|
|
||||||
|
#include "Link.h"
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
class BoxController : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
BoxController(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
// io
|
||||||
|
LinkObject* audioInput;
|
||||||
|
LinkObject* videoInputA;
|
||||||
|
LinkObject* videoInputB;
|
||||||
|
LinkObject* audioOutput;
|
||||||
|
LinkObject* videoOutput;
|
||||||
|
|
||||||
|
// record
|
||||||
|
LinkObject* audioEncoder;
|
||||||
|
LinkObject* videoEncoder;
|
||||||
|
LinkObject* overlay;
|
||||||
|
LinkObject* record;
|
||||||
|
LinkObject* snap;
|
||||||
|
|
||||||
|
// stream server
|
||||||
|
LinkObject* rtmpServer;
|
||||||
|
|
||||||
|
// play
|
||||||
|
LinkObject* file;
|
||||||
|
LinkObject* videoDecoder;
|
||||||
|
LinkObject* audioDecoder;
|
||||||
|
|
||||||
|
QVariantMap videoEncodeParams;
|
||||||
|
QVariantMap audioEncodeParams;
|
||||||
|
|
||||||
|
// record flag
|
||||||
|
bool isRecord = false;
|
||||||
|
// video file format
|
||||||
|
QString format;
|
||||||
|
|
||||||
|
void startRecord();
|
||||||
|
void stopRecord();
|
||||||
|
void startRtmpServer();
|
||||||
|
void init();
|
||||||
|
|
||||||
|
void startPlayback(QString fileName);
|
||||||
|
void startPlayLive();
|
||||||
|
void back();
|
||||||
|
void forward();
|
||||||
|
void pause();
|
||||||
|
void resume();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void playListNeedHide();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onTimeout();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// timer used to limit record time each time
|
||||||
|
QTimer* recordTimer;
|
||||||
|
// duration each video file
|
||||||
|
int videoDuration;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString writeCom(const QString& com);
|
||||||
|
bool isMountDisk();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BOXCONTROLLER_H
|
304
Channel.cpp
Normal file
304
Channel.cpp
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
#include "Channel.h"
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QTime>
|
||||||
|
|
||||||
|
LinkObject* Channel::audioInput = nullptr;
|
||||||
|
LinkObject* Channel::audioOutput = nullptr;
|
||||||
|
LinkObject* Channel::rtspServer = nullptr;
|
||||||
|
LinkObject* Channel::file = nullptr;
|
||||||
|
LinkObject* Channel::audioDecoder = nullptr;
|
||||||
|
LinkObject* Channel::videoDecoder = nullptr;
|
||||||
|
|
||||||
|
extern LinkObject* vo;
|
||||||
|
extern LinkObject* vo1;
|
||||||
|
|
||||||
|
const char* VideoPath = "/root/usb/videos";
|
||||||
|
const char* SnapPath = "/root/usb/snap";
|
||||||
|
|
||||||
|
Channel::Channel(QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
timer = new QTimer();
|
||||||
|
timer->setInterval(duration);
|
||||||
|
timer->stop();
|
||||||
|
|
||||||
|
videoInput = nullptr;
|
||||||
|
videoEncoder = nullptr;
|
||||||
|
videoOutput = nullptr;
|
||||||
|
audioEncoder = nullptr;
|
||||||
|
record = nullptr;
|
||||||
|
// file = nullptr;
|
||||||
|
// videoDecoder = nullptr;
|
||||||
|
// audioDecoder = nullptr;
|
||||||
|
rtsp = nullptr;
|
||||||
|
|
||||||
|
snap = Link::create("EncodeV");
|
||||||
|
overLay = Link::create("Overlay");
|
||||||
|
|
||||||
|
if (audioInput == nullptr) {
|
||||||
|
audioInput = Link::create("InputAlsa");
|
||||||
|
QVariantMap dataIn;
|
||||||
|
dataIn["path"] = "hw:0,0";
|
||||||
|
dataIn["channels"] = 2;
|
||||||
|
audioInput->start(dataIn);
|
||||||
|
}
|
||||||
|
if (audioOutput == nullptr) {
|
||||||
|
audioOutput = Link::create("OutputAlsa");
|
||||||
|
QVariantMap dataOut;
|
||||||
|
dataOut["path"] = "hw:0,0";
|
||||||
|
audioOutput->start(dataOut);
|
||||||
|
}
|
||||||
|
if (rtspServer == nullptr) {
|
||||||
|
rtspServer = Link::create("Rtsp");
|
||||||
|
rtspServer->start();
|
||||||
|
}
|
||||||
|
if (file == nullptr) {
|
||||||
|
file = Link::create("InputFile");
|
||||||
|
file->start();
|
||||||
|
|
||||||
|
// one video playback end
|
||||||
|
connect(file, &LinkObject::newEvent, [=](QString type, QVariant) {
|
||||||
|
if (type == "EOF") {
|
||||||
|
qDebug() << "one video playback end";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (audioDecoder == nullptr) {
|
||||||
|
audioDecoder = Link::create("DecodeA");
|
||||||
|
audioDecoder->start();
|
||||||
|
file->linkA(audioDecoder)->linkA(audioOutput);
|
||||||
|
}
|
||||||
|
if (videoDecoder == nullptr) {
|
||||||
|
videoDecoder = Link::create("DecodeV");
|
||||||
|
videoDecoder->start();
|
||||||
|
file->linkV(videoDecoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief load configuration and init components
|
||||||
|
*/
|
||||||
|
void Channel::init()
|
||||||
|
{
|
||||||
|
if (channelName.isEmpty()) {
|
||||||
|
qDebug() << "channel name is empty!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (channelName == "HDMI-A") {
|
||||||
|
videoOutput = vo;
|
||||||
|
} else {
|
||||||
|
videoOutput = vo1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init video input by channel name
|
||||||
|
videoInput = Link::create("InputVi");
|
||||||
|
QVariantMap dataVi;
|
||||||
|
dataVi["interface"] = channelName;
|
||||||
|
videoInput->start(dataVi);
|
||||||
|
|
||||||
|
// start water mask
|
||||||
|
overLay->start();
|
||||||
|
|
||||||
|
// link video input and output, and add water mask
|
||||||
|
videoInput->linkV(overLay)->linkV(videoOutput);
|
||||||
|
|
||||||
|
// capture picture from video
|
||||||
|
QVariantMap dataSnap;
|
||||||
|
dataSnap["codec"] = "jpeg";
|
||||||
|
dataSnap["snap"] = true;
|
||||||
|
dataSnap["width"] = 1920;
|
||||||
|
dataSnap["height"] = 1080;
|
||||||
|
snap->start(dataSnap);
|
||||||
|
videoInput->linkV(snap);
|
||||||
|
// sleep 1s, if didnt sleep , function snap would return flase and capture failed
|
||||||
|
QThread::sleep(1);
|
||||||
|
|
||||||
|
// video encode
|
||||||
|
videoEncoder = Link::create("EncodeV");
|
||||||
|
videoEncoder->start(videoEncoderParams);
|
||||||
|
|
||||||
|
// audio encode
|
||||||
|
audioEncoder = Link::create("EncodeA");
|
||||||
|
audioEncoder->start(audioEncoderParams);
|
||||||
|
|
||||||
|
// record
|
||||||
|
record = Link::create("Mux");
|
||||||
|
QVariantMap dataMp4;
|
||||||
|
dataMp4["format"] = "mp4";
|
||||||
|
record->setData(dataMp4);
|
||||||
|
videoInput->linkV(videoEncoder)->linkV(record);
|
||||||
|
audioInput->linkA(audioEncoder)->linkA(record);
|
||||||
|
|
||||||
|
// rtsp
|
||||||
|
rtsp = Link::create("Mux");
|
||||||
|
QVariantMap dataRtsp;
|
||||||
|
dataRtsp["path"] = QString("mem://%1").arg(pushCode);
|
||||||
|
dataRtsp["format"] = "rtsp";
|
||||||
|
rtsp->start(dataRtsp);
|
||||||
|
videoInput->linkV(videoEncoder)->linkV(rtsp)->linkV(rtspServer);
|
||||||
|
audioInput->linkA(audioEncoder)->linkA(rtsp)->linkA(rtspServer);
|
||||||
|
|
||||||
|
// init file and decoder
|
||||||
|
// file = Link::create("InputFile");
|
||||||
|
// file->start();
|
||||||
|
|
||||||
|
// videoDecoder = Link::create("DecodeV");
|
||||||
|
// videoDecoder->start();
|
||||||
|
// audioDecoder = Link::create("DecodeA");
|
||||||
|
// audioDecoder->start();
|
||||||
|
|
||||||
|
// file->linkV(videoDecoder)->linkV(videoOutput);
|
||||||
|
// file->linkA(audioDecoder)->linkA(audioOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief end previous record and start new record
|
||||||
|
*/
|
||||||
|
void Channel::onTimeout()
|
||||||
|
{
|
||||||
|
record->stop(true);
|
||||||
|
timer->stop();
|
||||||
|
|
||||||
|
if (!isMountDisk()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString curTime = QDateTime::currentDateTime().toString("yyyy-MM-dd_hh:mm:ss");
|
||||||
|
QVariantMap dataRecord;
|
||||||
|
dataRecord["path"] = QString("%1/%2/%3.mp4").arg(VideoPath).arg(channelName).arg(curTime);
|
||||||
|
record->setData(dataRecord);
|
||||||
|
record->start();
|
||||||
|
|
||||||
|
timer->start();
|
||||||
|
snap->invoke("snapSync", QString("%1/%2/%3.jpg").arg(SnapPath).arg(channelName).arg(curTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief start record
|
||||||
|
*/
|
||||||
|
void Channel::startRecord()
|
||||||
|
{
|
||||||
|
if (!isMountDisk()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString curTime = QDateTime::currentDateTime().toString("yyyy-MM-dd_hh:mm:ss");
|
||||||
|
QVariantMap dataRecord;
|
||||||
|
// /usb/root/videos/HDMI-A/2024-1-1_10:00:00
|
||||||
|
dataRecord["path"] = QString("%1/%2/%3.mp4").arg(VideoPath).arg(channelName).arg(curTime);
|
||||||
|
record->setData(dataRecord);
|
||||||
|
record->start();
|
||||||
|
setOverlay("Record");
|
||||||
|
|
||||||
|
timer->start();
|
||||||
|
snap->invoke("snapSync", QString("%1/%2/%3.jpg").arg(SnapPath).arg(channelName).arg(curTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief stop record
|
||||||
|
*/
|
||||||
|
void Channel::stopRecord()
|
||||||
|
{
|
||||||
|
record->stop(true);
|
||||||
|
setOverlay("No Record");
|
||||||
|
timer->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief start playback and output hdmi signals
|
||||||
|
* @param fileName
|
||||||
|
*/
|
||||||
|
void Channel::startPlayback(QString fileName)
|
||||||
|
{
|
||||||
|
qDebug() << channelName << "start play back, file name:" << fileName;
|
||||||
|
QString path = QString("%1/%2/%3").arg(VideoPath).arg(channelName).arg(fileName);
|
||||||
|
|
||||||
|
// break video input and output
|
||||||
|
videoInput->unLinkV(videoOutput);
|
||||||
|
|
||||||
|
file->linkV(videoDecoder)->linkV(videoOutput);
|
||||||
|
|
||||||
|
QVariantMap dataFile;
|
||||||
|
dataFile["path"] = path;
|
||||||
|
file->setData(dataFile);
|
||||||
|
// file->start();
|
||||||
|
|
||||||
|
isPlayback = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief playback -10s
|
||||||
|
*/
|
||||||
|
void Channel::back()
|
||||||
|
{
|
||||||
|
qDebug() << "back 10s";
|
||||||
|
int curPos = file->invoke("getPosition").toInt();
|
||||||
|
curPos -= 10 * 100;
|
||||||
|
file->invoke("seek", curPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief playback +10s
|
||||||
|
*/
|
||||||
|
void Channel::forward()
|
||||||
|
{
|
||||||
|
qDebug() << "forward 10s";
|
||||||
|
int curPos = file->invoke("getPosition").toInt();
|
||||||
|
curPos += 10 * 100;
|
||||||
|
file->invoke("seek", curPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Channel::togglePause
|
||||||
|
*/
|
||||||
|
void Channel::togglePause()
|
||||||
|
{
|
||||||
|
isPause = !isPause;
|
||||||
|
file->invoke("pause", isPause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief play live
|
||||||
|
*/
|
||||||
|
void Channel::startPlayLive()
|
||||||
|
{
|
||||||
|
// stop playback
|
||||||
|
videoDecoder->unLinkV(videoOutput);
|
||||||
|
|
||||||
|
file->stop(true);
|
||||||
|
isPlayback = false;
|
||||||
|
|
||||||
|
videoInput->linkV(videoOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief open console process, and use it by command
|
||||||
|
* @param com
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
QString Channel::writeCom(const QString& com)
|
||||||
|
{
|
||||||
|
QProcess proc;
|
||||||
|
QStringList argList;
|
||||||
|
argList << "-c" << com;
|
||||||
|
proc.start("/bin/sh", argList);
|
||||||
|
// wait process start
|
||||||
|
proc.waitForFinished();
|
||||||
|
proc.waitForReadyRead();
|
||||||
|
// read data from console
|
||||||
|
QByteArray procOutput = proc.readAll();
|
||||||
|
proc.close();
|
||||||
|
return QString(procOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief judge the disk whether mounted
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
bool Channel::isMountDisk()
|
||||||
|
{
|
||||||
|
QString mount = writeCom("df /root/usb");
|
||||||
|
if (!mount.contains("/root/usb"))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
89
Channel.h
Normal file
89
Channel.h
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#ifndef CHANNEL_H
|
||||||
|
#define CHANNEL_H
|
||||||
|
|
||||||
|
#include "Link.h"
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
class Channel : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit Channel(QObject* parent = nullptr);
|
||||||
|
void init();
|
||||||
|
|
||||||
|
QString channelName;
|
||||||
|
|
||||||
|
LinkObject* videoInput;
|
||||||
|
LinkObject* videoEncoder;
|
||||||
|
QVariantMap videoEncoderParams;
|
||||||
|
LinkObject* videoOutput;
|
||||||
|
|
||||||
|
static LinkObject* audioInput;
|
||||||
|
static LinkObject* audioOutput;
|
||||||
|
LinkObject* audioEncoder;
|
||||||
|
QVariantMap audioEncoderParams;
|
||||||
|
|
||||||
|
LinkObject* record;
|
||||||
|
// 1 hour each video
|
||||||
|
int duration = 1 * 60 * 1000;
|
||||||
|
bool isRecord = false;
|
||||||
|
|
||||||
|
LinkObject* snap;
|
||||||
|
LinkObject* overLay;
|
||||||
|
|
||||||
|
static LinkObject* file;
|
||||||
|
static LinkObject* videoDecoder;
|
||||||
|
static LinkObject* audioDecoder;
|
||||||
|
|
||||||
|
bool isPlayback = false;
|
||||||
|
bool isPause = false;
|
||||||
|
|
||||||
|
static LinkObject* rtspServer;
|
||||||
|
LinkObject* rtsp;
|
||||||
|
QString pushCode;
|
||||||
|
|
||||||
|
void startRecord();
|
||||||
|
void stopRecord();
|
||||||
|
void recordPause();
|
||||||
|
|
||||||
|
void startPlayback(QString fileName);
|
||||||
|
void back();
|
||||||
|
void forward();
|
||||||
|
void togglePause();
|
||||||
|
|
||||||
|
void startPlayLive();
|
||||||
|
signals:
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onTimeout();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// timer used to record segement
|
||||||
|
QTimer* timer;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString writeCom(const QString& com);
|
||||||
|
bool isMountDisk();
|
||||||
|
inline void setOverlay(QString str);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void Channel::setOverlay(QString str)
|
||||||
|
{
|
||||||
|
QVariantMap dataOver;
|
||||||
|
QVariantList lays;
|
||||||
|
QVariantMap lay;
|
||||||
|
lay["type"] = "text";
|
||||||
|
lay["enable"] = true;
|
||||||
|
lay["font"] = "/link/res/font.ttf";
|
||||||
|
lay["content"] = str;
|
||||||
|
lay["x"] = 0.1;
|
||||||
|
lay["y"] = 0.1;
|
||||||
|
lay["scale"] = 2;
|
||||||
|
lay["color"] = "#669900";
|
||||||
|
lay["alpha"] = 1;
|
||||||
|
lays << lay;
|
||||||
|
dataOver["lays"] = lays;
|
||||||
|
overLay->setData(dataOver);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CHANNEL_H
|
59
Config.cpp
Normal file
59
Config.cpp
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#include "Config.h"
|
||||||
|
#include "Channel.h"
|
||||||
|
#include "Json.h"
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
QList<Channel*> channelList;
|
||||||
|
|
||||||
|
Config::Config()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::loadConfig(QString path)
|
||||||
|
{
|
||||||
|
QFileInfo fileInfo(path);
|
||||||
|
if (!fileInfo.exists()) {
|
||||||
|
qDebug() << "config.json does not exist, exit";
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
// read the configuration
|
||||||
|
QVariantMap config = Json::loadFile(path).toMap();
|
||||||
|
QVariantList list = config["interface"].toList();
|
||||||
|
for (int i = 0; i < list.count(); i++) {
|
||||||
|
Channel* chn = new Channel();
|
||||||
|
QVariantMap cfg = list.at(i).toMap();
|
||||||
|
chn->channelName = cfg["name"].toString();
|
||||||
|
|
||||||
|
QVariantMap encV = cfg["encV"].toMap();
|
||||||
|
chn->videoEncoderParams = encV;
|
||||||
|
|
||||||
|
QVariantMap encA = cfg["encA"].toMap();
|
||||||
|
chn->audioEncoderParams = encA;
|
||||||
|
|
||||||
|
chn->pushCode = cfg["pushCode"].toString();
|
||||||
|
chn->init();
|
||||||
|
|
||||||
|
bool autoRecord = cfg["autoRecord"].toBool();
|
||||||
|
if (autoRecord) {
|
||||||
|
// chn->startRecord();
|
||||||
|
}
|
||||||
|
channelList.push_back(chn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief find the channel from channel list by channel name
|
||||||
|
* @param name
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Channel* Config::findChannelByName(QString name)
|
||||||
|
{
|
||||||
|
for (Channel* chn : channelList) {
|
||||||
|
if (chn->channelName == name) {
|
||||||
|
return chn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
17
Config.h
Normal file
17
Config.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
|
#include "Channel.h"
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class Config {
|
||||||
|
public:
|
||||||
|
Config();
|
||||||
|
static void loadConfig(QString path);
|
||||||
|
static Channel* findChannelByName(QString name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initConfig();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CONFIG_H
|
42
Menu.cpp
Normal file
42
Menu.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#include "Menu.h"
|
||||||
|
#include "ui_Menu.h"
|
||||||
|
|
||||||
|
Menu::Menu(QWidget* parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, ui(new Ui::Menu)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
ui->btnChannelA->setChecked(true);
|
||||||
|
|
||||||
|
// set window background transparent
|
||||||
|
// QPalette pal = palette();
|
||||||
|
// pal.setBrush(QPalette::Base, Qt::transparent);
|
||||||
|
// setPalette(pal);
|
||||||
|
// setAttribute(Qt::WA_TranslucentBackground, true);
|
||||||
|
|
||||||
|
QPoint globalPos = parent->mapToGlobal(QPoint(0, 0)); //父窗口绝对坐标
|
||||||
|
int x = globalPos.x() + (parent->width() - this->width()) / 2; //x坐标
|
||||||
|
int y = globalPos.y() + (parent->height() - this->height()) / 2; //y坐标
|
||||||
|
this->move(x, y); //窗口移动
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu::~Menu()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Menu::getCurChannel()
|
||||||
|
{
|
||||||
|
if (ui->btnChannelA->isChecked())
|
||||||
|
return "HDMI-A";
|
||||||
|
else
|
||||||
|
return "HDMI-B";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::setCurChannel(QString channel)
|
||||||
|
{
|
||||||
|
if (channel == "HDMI-A")
|
||||||
|
ui->btnChannelA->setChecked(true);
|
||||||
|
else
|
||||||
|
ui->btnChannelB->setChecked(true);
|
||||||
|
}
|
23
Menu.h
Normal file
23
Menu.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#ifndef MENU_H
|
||||||
|
#define MENU_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class Menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Menu : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Menu(QWidget* parent = 0);
|
||||||
|
~Menu();
|
||||||
|
QString getCurChannel();
|
||||||
|
void setCurChannel(QString channel);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::Menu* ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MENU_H
|
100
Menu.ui
Normal file
100
Menu.ui
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Menu</class>
|
||||||
|
<widget class="QWidget" name="Menu">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>300</width>
|
||||||
|
<height>200</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">QWidget#Menu{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton {
|
||||||
|
border: none;
|
||||||
|
background: rbga(0, 0, 0, 0.5);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton::checked{
|
||||||
|
color: #0082E5;
|
||||||
|
background: rgba(0, 130, 229, 0.5);
|
||||||
|
}</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btnChannelA">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>100</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>17</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>视频源1</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="autoExclusive">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btnChannelB">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>100</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>17</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>视频源2</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="autoExclusive">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
47
RecordControlApplication.pro
Normal file
47
RecordControlApplication.pro
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#-------------------------------------------------
|
||||||
|
#
|
||||||
|
# Project created by QtCreator 2024-01-05T00:12:20
|
||||||
|
#
|
||||||
|
#-------------------------------------------------
|
||||||
|
|
||||||
|
QT += core gui network serialport
|
||||||
|
|
||||||
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
TARGET = RecordControlApplication
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
include(../../work/Example/build.pri)
|
||||||
|
|
||||||
|
# The following define makes your compiler emit warnings if you use
|
||||||
|
# any feature of Qt which has been marked as deprecated (the exact warnings
|
||||||
|
# depend on your compiler). Please consult the documentation of the
|
||||||
|
# deprecated API in order to know how to port your code away from it.
|
||||||
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
|
||||||
|
# You can also make your code fail to compile if you use deprecated APIs.
|
||||||
|
# In order to do so, uncomment the following line.
|
||||||
|
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||||
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
main.cpp \
|
||||||
|
Widget.cpp \
|
||||||
|
TcpServer.cpp \
|
||||||
|
TcpController.cpp \
|
||||||
|
Config.cpp \
|
||||||
|
Channel.cpp \
|
||||||
|
Menu.cpp\
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
Widget.h \
|
||||||
|
TcpServer.h \
|
||||||
|
TcpController.h \
|
||||||
|
Config.h \
|
||||||
|
Channel.h \
|
||||||
|
Menu.h \
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
Widget.ui \
|
||||||
|
Menu.ui
|
97
TcpController.cpp
Normal file
97
TcpController.cpp
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#include "TcpController.h"
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
TcpController::TcpController(QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
// map the requesr url and the handler
|
||||||
|
routes.insert("set_ipaddr", std::bind(&TcpController::setIpaddr, this, std::placeholders::_1));
|
||||||
|
routes.insert("set_video_enc", std::bind(&TcpController::setVideoEnc, this, std::placeholders::_1));
|
||||||
|
routes.insert("get_file_list", std::bind(&TcpController::getFileList, this, std::placeholders::_1));
|
||||||
|
routes.insert("delete_file", std::bind(&TcpController::deleteFile, this, std::placeholders::_1));
|
||||||
|
}
|
||||||
|
|
||||||
|
Routes TcpController::getRoutes()
|
||||||
|
{
|
||||||
|
return this->routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief set box's ip address
|
||||||
|
* @param socket
|
||||||
|
*/
|
||||||
|
void TcpController::setIpaddr(QTcpSocket* socket)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief set the video encode params
|
||||||
|
* @param socket
|
||||||
|
*/
|
||||||
|
void TcpController::setVideoEnc(QTcpSocket* socket)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get the video file list
|
||||||
|
* @param socket
|
||||||
|
*/
|
||||||
|
void TcpController::getFileList(QTcpSocket* socket)
|
||||||
|
{
|
||||||
|
QDir dir("/root/usb/videos");
|
||||||
|
if (!dir.exists()) {
|
||||||
|
qDebug() << "dictionary /root/usb/videos dont exists";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dir.setFilter(QDir::Files);
|
||||||
|
dir.setSorting(QDir::Name);
|
||||||
|
QStringList nameFilters;
|
||||||
|
nameFilters << "*.mp4";
|
||||||
|
dir.setNameFilters(nameFilters);
|
||||||
|
QStringList fileList = dir.entryList();
|
||||||
|
QString data = fileList.join(",");
|
||||||
|
socket->write(data.toStdString().data());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief download video file
|
||||||
|
* @param socket
|
||||||
|
*/
|
||||||
|
void TcpController::downloadFile(QTcpSocket* socket)
|
||||||
|
{
|
||||||
|
QString url = socket->readLine().trimmed();
|
||||||
|
QString fileName = url.split("=")[1];
|
||||||
|
fileName = "/root/usb/videos/" + fileName;
|
||||||
|
qDebug() << fileName;
|
||||||
|
|
||||||
|
QFile file(fileName);
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
qDebug() << "read file failed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int64_t sendSize = 0;
|
||||||
|
int64_t fileSize = file.size();
|
||||||
|
|
||||||
|
socket->write(QString("fileSize=%1\r\n").arg(fileSize).toStdString().data());
|
||||||
|
|
||||||
|
// loop reading and sending file, 15k bytes once
|
||||||
|
while (sendSize < fileSize) {
|
||||||
|
char buf[1024 * 15] = { 0 };
|
||||||
|
int len = file.read(buf, sizeof(buf));
|
||||||
|
len = socket->write(buf, len);
|
||||||
|
sendSize += len;
|
||||||
|
}
|
||||||
|
qDebug() << "send file to " << socket << "end!";
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief delete video file by file name
|
||||||
|
* @param socket
|
||||||
|
*/
|
||||||
|
void TcpController::deleteFile(QTcpSocket* socket)
|
||||||
|
{
|
||||||
|
QString data = socket->readLine().trimmed();
|
||||||
|
QString fileName = data.split("=")[1];
|
||||||
|
}
|
29
TcpController.h
Normal file
29
TcpController.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef TCPCONTROLLER_H
|
||||||
|
#define TCPCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QMap>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTcpSocket>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
using Routes = QMap<QString, std::function<void(QTcpSocket*)>>;
|
||||||
|
|
||||||
|
class TcpController : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
TcpController(QObject* parent = nullptr);
|
||||||
|
Routes getRoutes();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Routes routes;
|
||||||
|
|
||||||
|
void setIpaddr(QTcpSocket* socket);
|
||||||
|
void setVideoEnc(QTcpSocket* socket);
|
||||||
|
void getFileList(QTcpSocket* socket);
|
||||||
|
void downloadFile(QTcpSocket* socket);
|
||||||
|
void deleteFile(QTcpSocket* socket);
|
||||||
|
void playLive(QTcpSocket* socket);
|
||||||
|
void playFile(QTcpSocket* socket);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TCPCONTROLLER_H
|
42
TcpServer.cpp
Normal file
42
TcpServer.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#include "TcpServer.h"
|
||||||
|
#include <QTcpSocket>
|
||||||
|
|
||||||
|
TcpServer::TcpServer(QObject* parent)
|
||||||
|
: QTcpServer(parent)
|
||||||
|
{
|
||||||
|
controller = new TcpController();
|
||||||
|
connect(this, &TcpServer::newConnection, this, &TcpServer::onNewConnect);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief new connection slots
|
||||||
|
*/
|
||||||
|
void TcpServer::onNewConnect()
|
||||||
|
{
|
||||||
|
if (this->hasPendingConnections()) {
|
||||||
|
QTcpSocket* socket = this->nextPendingConnection();
|
||||||
|
clients.push_back(socket);
|
||||||
|
connect(socket, &QTcpSocket::readyRead, this, &TcpServer::onReadyRead);
|
||||||
|
connect(socket, &QTcpSocket::disconnected, [=] {
|
||||||
|
QTcpSocket* sk = static_cast<QTcpSocket*>(sender());
|
||||||
|
qDebug() << "clients " << sk << "disconnected";
|
||||||
|
clients.removeOne(sk);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief receive data from clients
|
||||||
|
*/
|
||||||
|
void TcpServer::onReadyRead()
|
||||||
|
{
|
||||||
|
QTcpSocket* socket = static_cast<QTcpSocket*>(sender());
|
||||||
|
QString url = socket->readLine().trimmed();
|
||||||
|
bool contains = controller->getRoutes().contains(url);
|
||||||
|
if (contains) {
|
||||||
|
auto handler = controller->getRoutes().value(url);
|
||||||
|
handler(socket);
|
||||||
|
} else {
|
||||||
|
socket->write("error: request url not found");
|
||||||
|
}
|
||||||
|
}
|
22
TcpServer.h
Normal file
22
TcpServer.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#ifndef TCPSERVER_H
|
||||||
|
#define TCPSERVER_H
|
||||||
|
|
||||||
|
#include "TcpController.h"
|
||||||
|
#include <QTcpServer>
|
||||||
|
|
||||||
|
class TcpServer : public QTcpServer {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
TcpServer(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onReadyRead();
|
||||||
|
void onNewConnect();
|
||||||
|
|
||||||
|
private:
|
||||||
|
TcpController* controller;
|
||||||
|
QList<QTcpSocket*> clients;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TCPSERVER_H
|
249
Widget.cpp
Normal file
249
Widget.cpp
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
#include "Widget.h"
|
||||||
|
#include "BoxController.h"
|
||||||
|
#include "Channel.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include "TcpServer.h"
|
||||||
|
#include "ui_Widget.h"
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QScrollBar>
|
||||||
|
|
||||||
|
extern char* VideoPath;
|
||||||
|
extern QList<Channel*> channelList;
|
||||||
|
|
||||||
|
extern Widget::Widget(QWidget* parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, ui(new Ui::Widget)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
menu = new Menu(this);
|
||||||
|
menu->hide();
|
||||||
|
|
||||||
|
// set window background transparent
|
||||||
|
QPalette pal = palette();
|
||||||
|
pal.setBrush(QPalette::Base, Qt::transparent);
|
||||||
|
setPalette(pal);
|
||||||
|
setAttribute(Qt::WA_TranslucentBackground, true);
|
||||||
|
|
||||||
|
// set list no scroll bar
|
||||||
|
ui->listWidget->horizontalScrollBar()->hide();
|
||||||
|
ui->listWidget->verticalScrollBar()->hide();
|
||||||
|
ui->listWidget->hide();
|
||||||
|
|
||||||
|
// serial port
|
||||||
|
initSerialPort();
|
||||||
|
|
||||||
|
// init command map
|
||||||
|
initCommandMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget::~Widget()
|
||||||
|
{
|
||||||
|
serialPort->close();
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief open serial port and link the signals and slots
|
||||||
|
*/
|
||||||
|
void Widget::initSerialPort()
|
||||||
|
{
|
||||||
|
// timer used to reopen serial port
|
||||||
|
timer = new QTimer();
|
||||||
|
timer->setInterval(1000);
|
||||||
|
timer->stop();
|
||||||
|
|
||||||
|
// serialport
|
||||||
|
serialPort = new QSerialPort();
|
||||||
|
serialPort->setPortName("ttyAMA2");
|
||||||
|
serialPort->setBaudRate(QSerialPort::Baud115200);
|
||||||
|
serialPort->setDataBits(QSerialPort::Data8);
|
||||||
|
serialPort->setParity(QSerialPort::NoParity);
|
||||||
|
serialPort->setStopBits(QSerialPort::OneStop);
|
||||||
|
serialPort->setFlowControl(QSerialPort::NoFlowControl);
|
||||||
|
if (serialPort->open(QIODevice::ReadWrite)) {
|
||||||
|
qDebug() << "open serial port: /dev/ttyAMA2 success";
|
||||||
|
} else {
|
||||||
|
qDebug() << "open serial port: /dev/ttyAMA2 failed, ready to retry...";
|
||||||
|
timer->start();
|
||||||
|
}
|
||||||
|
connect(serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(onSerialError(QSerialPort::SerialPortError)));
|
||||||
|
connect(serialPort, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get the video list of current channel, and show
|
||||||
|
*/
|
||||||
|
void Widget::showPlayList()
|
||||||
|
{
|
||||||
|
// find the videos in dictionary "/root/usb/video"
|
||||||
|
QString dirPath = QString("%1/%2").arg(VideoPath).arg(curChannelName);
|
||||||
|
QDir dir(dirPath);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
qDebug() << "dictionary /root/usb/videos dont exists";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dir.setFilter(QDir::Files);
|
||||||
|
dir.setSorting(QDir::Name);
|
||||||
|
QStringList nameFilters;
|
||||||
|
nameFilters << "*.mp4";
|
||||||
|
dir.setNameFilters(nameFilters);
|
||||||
|
fileList = dir.entryList();
|
||||||
|
|
||||||
|
ui->listWidget->clear();
|
||||||
|
// append fileList to qlistwidget and show
|
||||||
|
for (QString& file : fileList) {
|
||||||
|
QListWidgetItem* item = new QListWidgetItem(file);
|
||||||
|
ui->listWidget->addItem(item);
|
||||||
|
}
|
||||||
|
ui->listWidget->show();
|
||||||
|
ui->listWidget->setCurrentRow(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief try to reopen serial port
|
||||||
|
*/
|
||||||
|
void Widget::onTimeout()
|
||||||
|
{
|
||||||
|
timer->stop();
|
||||||
|
bool ret = serialPort->open(QIODevice::ReadWrite);
|
||||||
|
// if open failed, then start timer and reopen
|
||||||
|
if (!ret) {
|
||||||
|
qDebug() << "reopen serial port: /dev/ttyAMA2 success";
|
||||||
|
timer->start();
|
||||||
|
} else {
|
||||||
|
qDebug() << "reopen serial port: /dev/ttyAMA2 failed, ready to retry...";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief handling error
|
||||||
|
* @param error
|
||||||
|
*/
|
||||||
|
void Widget::onSerialError(QSerialPort::SerialPortError error)
|
||||||
|
{
|
||||||
|
if (error == QSerialPort::ResourceError) {
|
||||||
|
qDebug() << "serial port break, try to reopen";
|
||||||
|
serialPort->close();
|
||||||
|
timer->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief map command from string to int
|
||||||
|
*/
|
||||||
|
void Widget::initCommandMap()
|
||||||
|
{
|
||||||
|
commandMap.insert("A1", Playback);
|
||||||
|
commandMap.insert("A2", Next);
|
||||||
|
commandMap.insert("A3", Previous);
|
||||||
|
commandMap.insert("A4", Forward);
|
||||||
|
commandMap.insert("A5", Back);
|
||||||
|
commandMap.insert("A6", Pause);
|
||||||
|
commandMap.insert("A7", Return);
|
||||||
|
commandMap.insert("A8", Enter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief receive data from serial port, and handle it
|
||||||
|
*/
|
||||||
|
void Widget::onReadyRead()
|
||||||
|
{
|
||||||
|
serialPort->waitForReadyRead(100);
|
||||||
|
QString data = serialPort->readLine();
|
||||||
|
if (!commandMap.contains(data)) {
|
||||||
|
qDebug() << QString("receive unkown command %1 from serial port: /dev/ttyAMA2").arg(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Command cmd = commandMap.value(data);
|
||||||
|
switch (cmd) {
|
||||||
|
case Playback: {
|
||||||
|
if (menu->isVisible())
|
||||||
|
return;
|
||||||
|
menu->show();
|
||||||
|
}
|
||||||
|
case Previous: {
|
||||||
|
// if show menu, then switch menu select
|
||||||
|
if (menu->isVisible()) {
|
||||||
|
menu->setCurChannel("HDMI-A");
|
||||||
|
}
|
||||||
|
// if menu hide, then select previous of listwidget
|
||||||
|
else {
|
||||||
|
int curRow = ui->listWidget->currentRow();
|
||||||
|
if (curRow == 0)
|
||||||
|
curRow = ui->listWidget->count();
|
||||||
|
ui->listWidget->setCurrentRow(curRow - 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Next: {
|
||||||
|
if (menu->isVisible()) {
|
||||||
|
menu->setCurChannel("HDMI-B");
|
||||||
|
} else {
|
||||||
|
int curRow = ui->listWidget->currentRow();
|
||||||
|
if (curRow == ui->listWidget->count() - 1) {
|
||||||
|
curRow = -1;
|
||||||
|
}
|
||||||
|
ui->listWidget->setCurrentRow(curRow + 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Forward:
|
||||||
|
if (isPlayback) {
|
||||||
|
Channel* chn = Config::findChannelByName(curChannelName);
|
||||||
|
chn->forward();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Back:
|
||||||
|
if (isPlayback) {
|
||||||
|
Channel* chn = Config::findChannelByName(curChannelName);
|
||||||
|
chn->back();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Pause:
|
||||||
|
if (isPlayback) {
|
||||||
|
Channel* chn = Config::findChannelByName(curChannelName);
|
||||||
|
chn->togglePause();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Enter:
|
||||||
|
// if menu show, then hide menu and show play list
|
||||||
|
if (menu->isVisible()) {
|
||||||
|
curChannelName = menu->getCurChannel();
|
||||||
|
menu->hide();
|
||||||
|
showPlayList();
|
||||||
|
}
|
||||||
|
// if menu hide, then current channel playback and other channel play live
|
||||||
|
else {
|
||||||
|
QString fileName = ui->listWidget->currentItem()->text();
|
||||||
|
Channel* channel = nullptr;
|
||||||
|
for (int i = 0; i < channelList.count(); i++) {
|
||||||
|
if (channelList.at(i)->channelName == curChannelName) {
|
||||||
|
channel = channelList.at(i);
|
||||||
|
}
|
||||||
|
if (channelList.at(i)->isPlayback) {
|
||||||
|
channelList.at(i)->startPlayLive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (channel)
|
||||||
|
channel->startPlayback(fileName);
|
||||||
|
ui->listWidget->hide();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Return:
|
||||||
|
if (menu->isVisible()) {
|
||||||
|
menu->hide();
|
||||||
|
}
|
||||||
|
if (ui->listWidget->isVisible()) {
|
||||||
|
ui->listWidget->hide();
|
||||||
|
}
|
||||||
|
if (isPlayback) {
|
||||||
|
Channel* chn = Config::findChannelByName(curChannelName);
|
||||||
|
chn->startPlayLive();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
59
Widget.h
Normal file
59
Widget.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#ifndef WIDG_H
|
||||||
|
#define WIDG_H
|
||||||
|
|
||||||
|
#include "Menu.h"
|
||||||
|
#include <QMap>
|
||||||
|
#include <QSerialPort>
|
||||||
|
#include <QSerialPortInfo>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class Widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Widget : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
enum Command {
|
||||||
|
Playback,
|
||||||
|
Next,
|
||||||
|
Previous,
|
||||||
|
Forward,
|
||||||
|
Back,
|
||||||
|
Pause,
|
||||||
|
Return,
|
||||||
|
Enter
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Widget(QWidget* parent = 0);
|
||||||
|
~Widget();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void showPlayList();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onSerialError(QSerialPort::SerialPortError error);
|
||||||
|
void onTimeout();
|
||||||
|
void onReadyRead();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::Widget* ui;
|
||||||
|
QStringList fileList;
|
||||||
|
|
||||||
|
QTimer* timer;
|
||||||
|
QSerialPort* serialPort;
|
||||||
|
QMap<QString, Command> commandMap;
|
||||||
|
|
||||||
|
bool isPlayback = false;
|
||||||
|
QString curChannelName;
|
||||||
|
QString curFileName;
|
||||||
|
|
||||||
|
Menu* menu;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initSerialPort();
|
||||||
|
void initCommandMap();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WIDG_H
|
98
Widget.ui
Normal file
98
Widget.ui
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Widget</class>
|
||||||
|
<widget class="QWidget" name="Widget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>1920</width>
|
||||||
|
<height>1080</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>20</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Widget</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>1511</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="listWidget">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>400</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>20</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">QListWidget {
|
||||||
|
color: rgba(255, 255, 255);
|
||||||
|
outline: none;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#listWidget::item {
|
||||||
|
|
||||||
|
border: transparent;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#listWidget::item:selected {
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
border-left: 10px solid 00AEEC;
|
||||||
|
color: #00AEEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QScrollBar {
|
||||||
|
width: 0;
|
||||||
|
}</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
79
main.cpp
Normal file
79
main.cpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#include "BoxController.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include "Link.h"
|
||||||
|
#include "TcpServer.h"
|
||||||
|
#include "Widget.h"
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
TcpServer* server;
|
||||||
|
LinkObject* vo;
|
||||||
|
LinkObject* vo1;
|
||||||
|
const char* ConfigurationPath = "/opt/RecordControlApplication/bin/config.json";
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
// init Link
|
||||||
|
if (!Link::init()) {
|
||||||
|
qDebug() << "Link init failed!";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init videooOutput and linuxfb before init QApplication,
|
||||||
|
// otherwise dump because cant init screen and we have ui components
|
||||||
|
|
||||||
|
// video output in channel HDMI-OUT0
|
||||||
|
vo = Link::create("OutputVo");
|
||||||
|
QVariantMap dataVo;
|
||||||
|
dataVo["vid"] = 0;
|
||||||
|
dataVo["type"] = "hdmi";
|
||||||
|
vo->start(dataVo);
|
||||||
|
|
||||||
|
// video output in channel HDMI-OUT1
|
||||||
|
vo1 = Link::create("OutputVo");
|
||||||
|
QVariantMap dataVo1;
|
||||||
|
dataVo1["vid"] = 1;
|
||||||
|
dataVo1["ui"] = false;
|
||||||
|
dataVo1["type"] = "vga|bt1120";
|
||||||
|
vo1->start(dataVo1);
|
||||||
|
|
||||||
|
// special settings used channel HDMI-OUT1
|
||||||
|
static int lastNorm = 0;
|
||||||
|
int norm = 0;
|
||||||
|
QString str = "1080P60";
|
||||||
|
if (str == "1080P60")
|
||||||
|
norm = 9;
|
||||||
|
else if (str == "1080P50")
|
||||||
|
norm = 10;
|
||||||
|
else if (str == "1080P30")
|
||||||
|
norm = 12;
|
||||||
|
else if (str == "720P60")
|
||||||
|
norm = 5;
|
||||||
|
else if (str == "720P50")
|
||||||
|
norm = 6;
|
||||||
|
else if (str == "3840x2160_30")
|
||||||
|
norm = 14;
|
||||||
|
if (norm != lastNorm) {
|
||||||
|
lastNorm = norm;
|
||||||
|
QString cmd = "rmmod hi_lt8618sx_lp.ko";
|
||||||
|
system(cmd.toLatin1().data());
|
||||||
|
cmd = "insmod /ko/extdrv/hi_lt8618sx_lp.ko norm=" + QString::number(norm);
|
||||||
|
system(cmd.toLatin1().data());
|
||||||
|
}
|
||||||
|
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
|
||||||
|
// initialize configuration
|
||||||
|
Config::loadConfig(ConfigurationPath);
|
||||||
|
|
||||||
|
// create server and listen port 8080
|
||||||
|
// server = new TcpServer();
|
||||||
|
// server->listen(QHostAddress::Any, 8080);
|
||||||
|
|
||||||
|
Widget w;
|
||||||
|
w.show();
|
||||||
|
|
||||||
|
return a.exec();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user