From e8222a87efc891519708a1fec664427095a44ac0 Mon Sep 17 00:00:00 2001 From: zc Date: Wed, 24 Jan 2024 17:57:08 -0800 Subject: [PATCH] Modify the project from one channel to two channels --- Channel.cpp | 43 +++++-- Channel.h | 6 +- Config.cpp | 19 ++- Config.h | 4 +- Menu.ui | 23 ++-- ProgressBar.cpp | 120 +++++++++++++++++++ ProgressBar.h | 49 ++++++++ ProgressBar.ui | 138 ++++++++++++++++++++++ RecordControlApplication.pro | 8 +- TcpController.cpp | 222 ++++++++++++++++++++++++++++------- TcpController.h | 55 ++++++++- TcpServer.cpp | 20 ++-- Widget.cpp | 131 +++++++++++++++++---- Widget.h | 34 +++++- Widget.ui | 9 +- images/pause.png | Bin 0 -> 160 bytes images/pause.svg | 1 + images/start.png | Bin 0 -> 278 bytes images/start.svg | 1 + main.cpp | 18 +-- res.qrc | 8 ++ 21 files changed, 789 insertions(+), 120 deletions(-) create mode 100644 ProgressBar.cpp create mode 100644 ProgressBar.h create mode 100644 ProgressBar.ui create mode 100644 images/pause.png create mode 100644 images/pause.svg create mode 100644 images/start.png create mode 100644 images/start.svg create mode 100644 res.qrc diff --git a/Channel.cpp b/Channel.cpp index 15cf24b..c980195 100644 --- a/Channel.cpp +++ b/Channel.cpp @@ -11,9 +11,8 @@ LinkObject* Channel::videoDecoder = nullptr; extern LinkObject* vo; extern LinkObject* vo1; - -const char* VideoPath = "/root/usb/videos"; -const char* SnapPath = "/root/usb/snap"; +extern const char* VideoPath; +extern const char* SnapPath; Channel::Channel(QObject* parent) : QObject(parent) @@ -35,7 +34,7 @@ Channel::Channel(QObject* parent) audioInput = Link::create("InputAlsa"); QVariantMap dataIn; dataIn["path"] = "hw:0,0"; - + dataIn["channels"] = 2; audioInput->start(dataIn); } if (audioOutput == nullptr) { @@ -50,11 +49,11 @@ Channel::Channel(QObject* parent) } if (file == nullptr) { file = Link::create("InputFile"); - // one video playback end connect(file, &LinkObject::newEvent, [=](QString type, QVariant) { if (type == "EOF") { qDebug() << "one video playback end"; + emit playEnd(); } }); } @@ -91,7 +90,20 @@ void Channel::init() videoInput = Link::create("InputVi"); QVariantMap dataVi; dataVi["interface"] = channelName; + dataVi["width"] = 1280; + dataVi["height"] = 1024; videoInput->start(dataVi); + connect(videoInput, &LinkObject::newEvent, [=](QString type, QVariant msg) { + if (type == "signal") { + QVariantMap data = msg.toMap(); + bool avalible = data["avalible"].toBool(); + if (avalible) { + int width = data["width"].toInt(); + int height = data["height"].toInt(); + emit signalInputed(channelName, width, height); + } + } + }); // start water mask // overLay->start(); @@ -202,11 +214,24 @@ void Channel::startPlayback(QString fileName) QVariantMap dataFile; dataFile["path"] = path; + dataFile["async"] = false; file->start(dataFile); isPlayback = true; } +/** + * @brief get video duration of current channel + * @param fileName + * @return + */ +int Channel::getDuration(QString fileName) +{ + QString path = QString("%1/%2/%3").arg(VideoPath).arg(channelName).arg(fileName); + int duration = file->invoke("getDuration", path).toInt(); + return duration; +} + /** * @brief play live */ @@ -224,9 +249,9 @@ void Channel::startPlayLive() */ void Channel::back() { - qDebug() << "back 10s"; + qDebug() << channelName << "back 10s"; int curPos = file->invoke("getPosition").toInt(); - curPos -= 10 * 100; + curPos -= 10 * 1000; file->invoke("seek", curPos); } @@ -235,9 +260,9 @@ void Channel::back() */ void Channel::forward() { - qDebug() << "forward 10s"; + qDebug() << channelName << "forward 10s"; int curPos = file->invoke("getPosition").toInt(); - curPos += 10 * 100; + curPos += 10 * 1000; file->invoke("seek", curPos); } diff --git a/Channel.h b/Channel.h index fd0618c..0848c06 100644 --- a/Channel.h +++ b/Channel.h @@ -24,6 +24,7 @@ public: QVariantMap audioEncoderParams; LinkObject* record; + bool autoRecord = false; // 1 hour each video int duration = 1 * 60 * 1000; bool isRecord = false; @@ -50,9 +51,12 @@ public: void back(); void forward(); void togglePause(); - + int getDuration(QString fileName); void startPlayLive(); + signals: + void playEnd(); + void signalInputed(QString name, int width, int height); private slots: void onTimeout(); diff --git a/Config.cpp b/Config.cpp index 5a6b167..4bab66d 100644 --- a/Config.cpp +++ b/Config.cpp @@ -5,21 +5,26 @@ #include #include +// input channels list QList channelList; Config::Config() { } -void Config::loadConfig(QString path) +/** + * @brief load config when initialize + * @param path + */ +void Config::loadConfig(QString configPath) { - QFileInfo fileInfo(path); + QFileInfo fileInfo(configPath); if (!fileInfo.exists()) { qDebug() << "config.json does not exist, exit"; exit(0); } // read the configuration - QVariantMap config = Json::loadFile(path).toMap(); + QVariantMap config = Json::loadFile(configPath).toMap(); QVariantList list = config["interface"].toList(); for (int i = 0; i < list.count(); i++) { Channel* chn = new Channel(); @@ -37,10 +42,16 @@ void Config::loadConfig(QString path) bool autoRecord = cfg["autoRecord"].toBool(); if (autoRecord) { - // chn->startRecord(); + chn->autoRecord = autoRecord; } channelList.push_back(chn); } + + for (Channel* chn : channelList) { + if (chn->autoRecord) { + chn->startRecord(); + } + } } /** diff --git a/Config.h b/Config.h index 7734e49..8779a32 100644 --- a/Config.h +++ b/Config.h @@ -7,11 +7,13 @@ class Config { public: Config(); - static void loadConfig(QString path); + static void loadConfig(QString configPath); + static void upadteConfig(QString cfg); static Channel* findChannelByName(QString name); private: void initConfig(); + static QString path; }; #endif // CONFIG_H diff --git a/Menu.ui b/Menu.ui index 17e7269..b97bfd9 100644 --- a/Menu.ui +++ b/Menu.ui @@ -6,19 +6,20 @@ 0 0 - 300 - 200 + 130 + 100 + + + 12 + + Form - /*QWidget#Menu{ - background: rgba(0, 0, 0, 0.5); -}*/ - -QPushButton { + QPushButton { border: none; background: rgba(0, 0, 0, 0.7); color: #ffffff; @@ -49,12 +50,12 @@ QPushButton::checked{ 0 - 100 + 50 - 17 + 12 @@ -73,12 +74,12 @@ QPushButton::checked{ 0 - 100 + 50 - 17 + 12 diff --git a/ProgressBar.cpp b/ProgressBar.cpp new file mode 100644 index 0000000..6109592 --- /dev/null +++ b/ProgressBar.cpp @@ -0,0 +1,120 @@ +#include "ProgressBar.h" +#include "ui_ProgressBar.h" +#include + +#define DURATION 10000 + +ProgressBar::ProgressBar(QWidget* parent) + : QWidget(parent) + , ui(new Ui::ProgressBar) +{ + ui->setupUi(this); + + // move to bottom of screen + this->setFixedWidth(parent->width()); + int height = parent->height(); + this->move(0, height - this->height()); + + timer = new QTimer(); + timer->setInterval(DURATION); + timer->stop(); + + connect(timer, SIGNAL(timeout()), this, SLOT(onTimeout())); + + QString styles = "QSlider::groove:horizontal { \ + border: none; \ + height: 6px; \ + border-radius: 3px; \ + background: #FF5C38; \ + } \ + QSlider::sub-page:horizontal { \ + background: #FF5C38; \ + height: 4px; \ + border-radius: 3px; \ + } \ + QSlider::add-page:horizontal { \ + background: #949494; \ + height: 4px; \ + border-radius: 3px; \ + }"; + QString max = "QSlider::handle:horizontal {\ + border: none;\ + margin: -5px 0px;\ + width: 16px;\ + height: 16px;\ + border-radius: 8px;\ + background: #FF5C38;\ + }"; + QString min = "QSlider::handle:horizontal {\ + border: none;\ + margin: -5px -8px;\ + width: 16px;\ + height: 16px;\ + border-radius: 8px;\ + background: rgba(0, 0, 0, 0);\ + }"; + sliderMaxStyles = styles + max; + sliderMinStyles = styles + min; + + this->setStyleSheet(sliderMaxStyles); +} + +ProgressBar::~ProgressBar() +{ + delete ui; + delete timer; +} + +void ProgressBar::setDuration(int dur) +{ + this->duration = dur; + ui->horizontalSlider->setMaximum(dur); + QString time = this->secToString(dur); + this->durationStr = time; + ui->label->setText(QString("00:00:00/%1").arg(time)); +} + +void ProgressBar::setCurrent(int cur) +{ + ui->horizontalSlider->setValue(cur); + QString time = this->secToString(cur); + ui->label->setText(QString("%1/%2").arg(time).arg(durationStr)); +} + +void ProgressBar::showMax() +{ + if (timer->isActive()) { + timer->stop(); + timer->start(); + } else { + timer->start(); + } + if (!isMax) { + this->setFixedHeight(100); + ui->horizontalSlider->setStyleSheet(sliderMaxStyles); + this->move(0, parentWidget()->height() - this->height()); + ui->widget->setStyleSheet("QWidget#widget{background-color: rgba(0, 0, 0, 0.7);}"); + isMax = true; + ui->actionContainer->show(); + } +} + +void ProgressBar::onTimeout() +{ + ui->actionContainer->hide(); + ui->horizontalSlider->setStyleSheet(sliderMinStyles); + this->setFixedHeight(ui->horizontalSlider->height() + ui->widget->layout()->contentsMargins().top()); + this->move(0, parentWidget()->height() - this->height()); + ui->widget->setStyleSheet("QWidget#widget{background-color: rgba(0, 0, 0, 0);}"); + timer->stop(); + isMax = false; +} + +void ProgressBar::setState(PlayState state) +{ + if (state == Play) { + ui->lblStatus->setPixmap(QPixmap(":/images/pause.png")); + } else { + ui->lblStatus->setPixmap(QPixmap(":/images/start.png")); + } +} diff --git a/ProgressBar.h b/ProgressBar.h new file mode 100644 index 0000000..9b2c47b --- /dev/null +++ b/ProgressBar.h @@ -0,0 +1,49 @@ +#ifndef PROGRESSBAR_H +#define PROGRESSBAR_H + +#include +#include +#include + +namespace Ui { +class ProgressBar; +} + +class ProgressBar : public QWidget { + Q_OBJECT + +public: + enum PlayState { + Play, + Pause + }; + explicit ProgressBar(QWidget* parent = 0); + ~ProgressBar(); + void showMax(); + void setDuration(int dur); + void setCurrent(int cur); + void setState(PlayState state); + inline QString secToString(int msc); + +private slots: + void onTimeout(); + +private: + Ui::ProgressBar* ui; + int duration; + QString durationStr; + + QTimer* timer; + bool isMax = true; + + QString sliderMaxStyles; + QString sliderMinStyles; +}; + +inline QString ProgressBar::secToString(int msc) +{ + QString formatStr = QTime(0, 0, 0).addSecs(msc).toString(QString::fromUtf8("hh:mm:ss")); + return formatStr; +} + +#endif // PROGRESSBAR_H diff --git a/ProgressBar.ui b/ProgressBar.ui new file mode 100644 index 0000000..b0d0539 --- /dev/null +++ b/ProgressBar.ui @@ -0,0 +1,138 @@ + + + ProgressBar + + + + 0 + 0 + 776 + 100 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QWidget#widget { + background-color: rgba(0, 0, 0, 0.7); +} + + + + 0 + + + 0 + + + 30 + + + 0 + + + 0 + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 40 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 30 + 30 + + + + + + + :/images/begin.png + + + + + + + + 16777215 + 30 + + + + + 10 + + + + QLabel { + color: #ffffff; +} + + + 00:00:00/00:00:00 + + + + + + + + + + + + + + diff --git a/RecordControlApplication.pro b/RecordControlApplication.pro index 8558950..d16e9bd 100644 --- a/RecordControlApplication.pro +++ b/RecordControlApplication.pro @@ -33,6 +33,7 @@ SOURCES += \ Config.cpp \ Channel.cpp \ Menu.cpp\ + ProgressBar.cpp HEADERS += \ Widget.h \ @@ -41,7 +42,12 @@ HEADERS += \ Config.h \ Channel.h \ Menu.h \ + ProgressBar.h FORMS += \ Widget.ui \ - Menu.ui + Menu.ui \ + ProgressBar.ui + +RESOURCES += \ + res.qrc diff --git a/TcpController.cpp b/TcpController.cpp index 0567879..78ca399 100644 --- a/TcpController.cpp +++ b/TcpController.cpp @@ -1,6 +1,15 @@ #include "TcpController.h" +#include "Config.h" +#include "Json.h" #include #include +#include + +extern const char* VideoPath; +extern const char* SnapPath; +extern const char* NetConfigPath; +extern const char* NetScriptPath; +extern const char* ConfigurationPath; TcpController::TcpController(QObject* parent) : QObject(parent) @@ -8,8 +17,11 @@ TcpController::TcpController(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_video_enc", std::bind(&TcpController::getVideoEnc, 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.insert("set_name", std::bind(&TcpController::setName, this, std::placeholders::_1)); + routes.insert("get_name", std::bind(&TcpController::getName, this, std::placeholders::_1)); } Routes TcpController::getRoutes() @@ -23,6 +35,44 @@ Routes TcpController::getRoutes() */ void TcpController::setIpaddr(QTcpSocket* socket) { + QVariantMap params = this->parseParams(socket); + QVariantMap netMap = Json::loadFile(NetConfigPath).toMap(); + + if (params.contains("ip")) + netMap["ip"] = params.value("ip"); + if (params.contains("mask")) + netMap["mask"] = params.value("mask"); + if (params.contains("gateway")) + netMap["gateway"] = params.value("gateway"); + + Json::saveFile(netMap, NetConfigPath); + socket->write("url=set_ipaddr\r\nstatus=success"); + writeCom(NetScriptPath); +} + +/** + * @brief set box's name + * @param socket + */ +void TcpController::setName(QTcpSocket* socket) +{ + QVariantMap params = this->parseParams(socket); + if (params.isEmpty() || !params.contains("name")) { + socket->write("url=set_name\r\nstatus=error"); + return; + } + QVariantMap cfg = Json::loadFile(ConfigurationPath).toMap(); + cfg["name"] = params.value("name"); + Json::saveFile(cfg, ConfigurationPath); + socket->write("url=set_name\r\nstatus=success"); +} + +void TcpController::getName(QTcpSocket* socket) +{ + QVariantMap config = Json::loadFile(ConfigurationPath).toMap(); + QString name = config["name"].toString(); + QString data = QString("url=get_name\r\nstatus=success\r\nname=%1").arg(name); + socket->write(data.toStdString().data()); } /** @@ -31,6 +81,75 @@ void TcpController::setIpaddr(QTcpSocket* socket) */ void TcpController::setVideoEnc(QTcpSocket* socket) { + QVariantMap params = this->parseParams(socket); + if (params.isEmpty() || !params.contains("interface")) { + qDebug() << "set video encode params error"; + socket->write("url=set_video_enc\r\nstatus=error"); + return; + } + + // change resolution and framerate of channel + QString interface = params.value("interface").toString(); + Channel* chn = Config::findChannelByName(interface); + if (chn) + chn->videoEncoder->setData(params); + + // update config + QVariantMap cfg = Json::loadFile(ConfigurationPath).toMap(); + QVariantList interfaces = cfg.value("interface").toList(); + for (int i = 0; i < interfaces.length(); i++) { + QVariantMap item = interfaces.at(i).toMap(); + if (item.value("name").toString() == interface) { + QVariantMap encV = item.value("encV").toMap(); + if (params.contains("width")) + encV["width"] = params.value("width"); + if (params.contains("height")) + encV["height"] = params.value("height"); + if (params.contains("framerate")) + encV["framerate"] = params.value("framerate"); + item["encV"] = encV; + interfaces.replace(i, item); + } + } + cfg["interface"] = interfaces; + Json::saveFile(cfg, ConfigurationPath); + socket->write("url=set_video_enc\r\nstatus=success"); +} + +void TcpController::getVideoEnc(QTcpSocket* socket) +{ + QVariantMap params = this->parseParams(socket); + + if (params.contains("interface")) { + QString interface = params.value("interface").toString(); + + QVariantMap cfg = Json::loadFile(ConfigurationPath).toMap(); + QVariantList interfaces = cfg.value("interface").toList(); + + QVariantMap targetInterfaceCfg; + for (int i = 0; i < interfaces.length(); i++) { + QVariantMap item = interfaces.at(i).toMap(); + if (item.value("name").toString() == interface) { + targetInterfaceCfg = item; + } + } + QString width; + QString height; + QString framerate; + QVariantMap encodeV = targetInterfaceCfg.value("encV").toMap(); + width = encodeV.value("width").toString(); + height = encodeV.value("height").toString(); + framerate = encodeV.value("framerate").toString(); + QString data = QString("url=get_video_enc\r\nstatus=success\r\nwidth=%1\r\nheight=%2\r\nframerate=%3") + .arg(width) + .arg(height) + .arg(framerate); + socket->write(data.toStdString().data()); + } else { + qDebug() << "get video encode params error"; + socket->write("url=get_video_enc\r\nstatus=error"); + return; + } } /** @@ -39,59 +158,74 @@ void TcpController::setVideoEnc(QTcpSocket* socket) */ void TcpController::getFileList(QTcpSocket* socket) { - QDir dir("/root/usb/videos"); - if (!dir.exists()) { - qDebug() << "dictionary /root/usb/videos dont exists"; + QVariantMap params = this->parseParams(socket); + if (params.isEmpty() || !params.contains("interface")) { + qDebug() << "getFileList params error"; + socket->write("url=get_file_list\r\nstatus=error"); return; } - dir.setFilter(QDir::Files); - dir.setSorting(QDir::Name); - QStringList nameFilters; - nameFilters << "*.mp4"; - dir.setNameFilters(nameFilters); - QStringList fileList = dir.entryList(); - QString data = fileList.join(","); + + QString interface = params.value("interface").toString(); + QString videoPath = QString("%1/%2").arg(VideoPath).arg(interface); + + QStringList videoList = this->listFile(videoPath); + + if (videoList.isEmpty()) { + qDebug() << "error, video folders is empty "; + socket->write("url=get_file_list\r\nstatus=error"); + return; + } + QString data; + data = "url=get_file_list\r\nstatus=success\r\n" + videoList.join("\r\n"); 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]; + QVariantMap params = this->parseParams(socket); + if (params.isEmpty()) { + socket->write("url=delete_file\r\nstatus=error"); + return; + } + + QString interface = params.value("interface").toString(); + QString fileName = params.value("fileName").toString(); + // xxx.mp4 ==> xxx.jpg + QString snapName = fileName.split(".")[0] + ".jpg"; + + QString filePath = QString("%1/%2/%3").arg(VideoPath).arg(interface).arg(fileName); + QString snapPath = QString("%1/%2/%3").arg(SnapPath).arg(interface).arg(fileName); + QFile video(filePath); + if (video.exists()) { + video.remove(); + qDebug() << "remove video:" << filePath; + QFile image(snapPath); + if (image.exists()) { + image.remove(); + qDebug() << "remove image:" << snapPath; + } + socket->write("url=delete_file\r\nstatus=success"); + } else { + qDebug() << "error, file:" << filePath << "do not exist"; + socket->write("url=delete_file\r\nstatus=error"); + } +} + +QString TcpController::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); } diff --git a/TcpController.h b/TcpController.h index 65df6f3..f845917 100644 --- a/TcpController.h +++ b/TcpController.h @@ -1,6 +1,7 @@ #ifndef TCPCONTROLLER_H #define TCPCONTROLLER_H +#include #include #include #include @@ -16,14 +17,60 @@ public: private: Routes routes; - void setIpaddr(QTcpSocket* socket); + void setName(QTcpSocket* socket); + void getName(QTcpSocket* socket); void setVideoEnc(QTcpSocket* socket); + void getVideoEnc(QTcpSocket* socket); void getFileList(QTcpSocket* socket); - void downloadFile(QTcpSocket* socket); void deleteFile(QTcpSocket* socket); - void playLive(QTcpSocket* socket); - void playFile(QTcpSocket* socket); + + static QString writeCom(const QString& com); + inline QVariantMap parseParams(QTcpSocket* socket); + inline QList listFile(QString path); }; +/** + * @brief parse tcp request params + * @param socket + * @return + */ +inline QVariantMap TcpController::parseParams(QTcpSocket* socket) +{ + QVariantMap params; + // exmaple:data1=xxx\r\ndata2=xxx\r\n... + QStringList data = QString(socket->readAll()).split("\r\n"); + + for (int i = 0; i < data.length(); i++) { + if (data.at(i).contains("=")) { + QStringList pair = data.at(i).split("="); + params.insert(pair[0], pair[1]); + } + } + return params; +} + +/** + * @brief list all .mp4 file in the folder + * @param path + * @return + */ +inline QList TcpController::listFile(QString path) +{ + QStringList fileList; + QDir dir(path); + if (!dir.exists()) { + qDebug() << "folder " << path << " dont exists"; + return fileList; + } + dir.setFilter(QDir::Files); + dir.setSorting(QDir::Name); + QStringList nameFilters; + nameFilters << "*.mp4" + << "*.jpg"; + dir.setNameFilters(nameFilters); + fileList = dir.entryList(); + return fileList; +} + #endif // TCPCONTROLLER_H diff --git a/TcpServer.cpp b/TcpServer.cpp index 6e81711..4e5b56e 100644 --- a/TcpServer.cpp +++ b/TcpServer.cpp @@ -19,7 +19,7 @@ void TcpServer::onNewConnect() connect(socket, &QTcpSocket::readyRead, this, &TcpServer::onReadyRead); connect(socket, &QTcpSocket::disconnected, [=] { QTcpSocket* sk = static_cast(sender()); - qDebug() << "clients " << sk << "disconnected"; + qDebug() << "client " << sk << "disconnected"; clients.removeOne(sk); }); } @@ -31,12 +31,16 @@ void TcpServer::onNewConnect() void TcpServer::onReadyRead() { QTcpSocket* socket = static_cast(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"); + // url=xxx + QString data = socket->readLine().trimmed(); + if (data.contains("=")) { + QString url = data.split("=")[1]; + bool contains = controller->getRoutes().contains(url); + if (contains) { + auto handler = controller->getRoutes().value(url); + handler(socket); + } else { + socket->write(QString("url=%1\r\nstatus=error").arg(url).toStdString().data()); + } } } diff --git a/Widget.cpp b/Widget.cpp index 5f97d87..588511a 100644 --- a/Widget.cpp +++ b/Widget.cpp @@ -1,5 +1,4 @@ #include "Widget.h" -#include "BoxController.h" #include "Channel.h" #include "Config.h" #include "TcpServer.h" @@ -20,6 +19,9 @@ extern Widget::Widget(QWidget* parent) menu = new Menu(this); menu->hide(); + progressBar = new ProgressBar(this); + progressBar->hide(); + // set window background transparent QPalette pal = palette(); pal.setBrush(QPalette::Base, Qt::transparent); @@ -36,6 +38,16 @@ extern Widget::Widget(QWidget* parent) // init command map initCommandMap(); + + // get playback position each 0.5s + progressTimer = new QTimer(); + progressTimer->setInterval(500); + connect(progressTimer, SIGNAL(timeout()), this, SLOT(onProgressTimeout())); + + for (Channel* chn : channelList) { + connect(chn, SIGNAL(playEnd()), this, SLOT(onPlayEnd())); + connect(chn, SIGNAL(signalInputed(QString, int, int)), this, SLOT(onSignalInputed(QString, int, int))); + } } Widget::~Widget() @@ -51,7 +63,7 @@ void Widget::initSerialPort() { // timer used to reopen serial port timer = new QTimer(); - timer->setInterval(1000); + timer->setInterval(3000); timer->stop(); // serialport @@ -78,22 +90,12 @@ void Widget::initSerialPort() 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(); + QString dirPath = QString("%1/%2").arg(VideoPath).arg(curSelectChannel); + QStringList files = this->getFileList(dirPath); ui->listWidget->clear(); // append fileList to qlistwidget and show - for (QString& file : fileList) { + for (QString& file : files) { QListWidgetItem* item = new QListWidgetItem(file); ui->listWidget->addItem(item); } @@ -117,6 +119,27 @@ void Widget::onTimeout() } } +void Widget::onProgressTimeout() +{ + Channel* chn = Config::findChannelByName(curSelectChannel); + int pos = chn->file->invoke("getPosition").toInt() / 1000; + progressBar->setCurrent(pos); +} + +/** + * @brief call it when input hdmi signal + * @param name + * @param width + * @param height + */ +void Widget::onSignalInputed(QString name, int width, int height) +{ + if (name == "HDMI-A") { + this->setFixedSize(QSize(width, height)); + qDebug() << QString("set windows size: %1x%2").arg(width).arg(height); + } +} + /** * @brief handling error * @param error @@ -191,35 +214,45 @@ void Widget::onReadyRead() } case Forward: if (isPlayback) { - Channel* chn = Config::findChannelByName(curChannelName); + Channel* chn = Config::findChannelByName(curSelectChannel); chn->forward(); + progressBar->showMax(); } break; case Back: if (isPlayback) { - Channel* chn = Config::findChannelByName(curChannelName); + Channel* chn = Config::findChannelByName(curSelectChannel); chn->back(); + progressBar->showMax(); } break; case Pause: if (isPlayback) { - Channel* chn = Config::findChannelByName(curChannelName); + Channel* chn = Config::findChannelByName(curSelectChannel); chn->togglePause(); + progressBar->showMax(); + if (chn->isPause) { + progressBar->setState(ProgressBar::Pause); + } else { + progressBar->setState(ProgressBar::Play); + } } break; case Enter: // if menu show, then hide menu and show play list if (menu->isVisible()) { - curChannelName = menu->getCurChannel(); + curSelectChannel = menu->getCurChannel(); menu->hide(); showPlayList(); } else { // if list widget show, then current channel playback and other channel play live if (ui->listWidget->isVisible()) { + if (ui->listWidget->count() == 0) + return; QString fileName = ui->listWidget->currentItem()->text(); Channel* channel = nullptr; for (int i = 0; i < channelList.count(); i++) { - if (channelList.at(i)->channelName == curChannelName) { + if (channelList.at(i)->channelName == curSelectChannel) { channel = channelList.at(i); } if (channelList.at(i)->isPlayback) { @@ -227,10 +260,33 @@ void Widget::onReadyRead() } } if (channel) { + // play back and output hdmi channel->startPlayback(fileName); isPlayback = true; + + // only show progressbar in HDMI-A(HDMI-OUT0) + if (channel->channelName == "HDMI-A") { + // show progress bar maximize + int duration = channel->getDuration(fileName) / 1000; + progressBar->setDuration(duration); + progressBar->setCurrent(0); + progressBar->show(); + progressBar->showMax(); + progressBar->setState(ProgressBar::Play); + // if timer is active, then restart timer + if (progressTimer->isActive()) { + progressTimer->stop(); + progressTimer->start(); + } else { + progressTimer->start(); + } + curPlayChannel = curSelectChannel; + curFileName = fileName; + QString dirPath = QString("%1/%2").arg(VideoPath).arg(curPlayChannel); + fileList = this->getFileList(dirPath); + } } - ui->listWidget->hide(); + // ui->listWidget->hide(); } } break; @@ -238,15 +294,38 @@ void Widget::onReadyRead() if (menu->isVisible()) { menu->hide(); } - if (ui->listWidget->isVisible()) { - ui->listWidget->hide(); - } - if (isPlayback) { - Channel* chn = Config::findChannelByName(curChannelName); + if (isPlayback && !ui->listWidget->isVisible()) { + Channel* chn = Config::findChannelByName(curSelectChannel); chn->startPlayLive(); + progressBar->hide(); + } else { + ui->listWidget->hide(); } break; default: break; } } + +/** + * @brief current play end and then play next video + */ +void Widget::onPlayEnd() +{ + int index = fileList.indexOf(curFileName); + // cur video is last one of list, then refresh list + if (index == fileList.length() - 1) { + QString dirPath = QString("%1/%2").arg(VideoPath).arg(curPlayChannel); + this->getFileList(dirPath); + if (index == fileList.length() - 1) { + qDebug() << "no file to play"; + return; + } + qDebug() << "refresh current play file list"; + } + + curFileName = fileList.at(index + 1); + Channel* chn = Config::findChannelByName(curPlayChannel); + if (chn) + chn->startPlayback(curFileName); +} diff --git a/Widget.h b/Widget.h index 136c50d..58a12cd 100644 --- a/Widget.h +++ b/Widget.h @@ -2,6 +2,9 @@ #define WIDG_H #include "Menu.h" +#include "ProgressBar.h" +#include +#include #include #include #include @@ -36,6 +39,9 @@ private slots: void onSerialError(QSerialPort::SerialPortError error); void onTimeout(); void onReadyRead(); + void onProgressTimeout(); + void onPlayEnd(); + void onSignalInputed(QString name, int width, int height); private: Ui::Widget* ui; @@ -45,15 +51,41 @@ private: QSerialPort* serialPort; QMap commandMap; + QTimer* progressTimer; bool isPlayback = false; - QString curChannelName; + QString curPlayChannel; + QString curSelectChannel; QString curFileName; Menu* menu; + ProgressBar* progressBar; private: void initSerialPort(); void initCommandMap(); + inline QStringList getFileList(QString dirPath); }; +/** + * @brief get all files of cur channel + * @param path + * @return + */ +inline QStringList Widget::getFileList(QString dirPath) +{ + QStringList fileList; + QDir dir(dirPath); + if (!dir.exists()) { + qDebug() << dirPath << "do not exist"; + return fileList; + } + dir.setFilter(QDir::Files); + dir.setSorting(QDir::Name); + QStringList nameFilters; + nameFilters << "*.mp4"; + dir.setNameFilters(nameFilters); + fileList = dir.entryList(); + return fileList; +} + #endif // WIDG_H diff --git a/Widget.ui b/Widget.ui index 229b17c..8a4f3a6 100644 --- a/Widget.ui +++ b/Widget.ui @@ -22,6 +22,9 @@ + + 0 + 0 @@ -63,14 +66,14 @@ - 20 + 14 QListWidget { color: rgba(255, 255, 255); outline: none; - background-color: rgba(0, 0, 0, 0.5); + background-color: rgba(0, 0, 0, 0.4); border: none; } @@ -81,7 +84,7 @@ } #listWidget::item:selected { - background-color: rgba(0, 0, 0, 0.7); + background-color: rgba(0, 0, 0, 0.5); border-left: 10px solid 00AEEC; color: #00AEEC; } diff --git a/images/pause.png b/images/pause.png new file mode 100644 index 0000000000000000000000000000000000000000..fae81277382401313197f25e5e58b349507dbf10 GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}QJyZ2ArbCx zukRE*puppD@$RJ+XLcA{39Fec@i-X1F8N-LlJ?Fyd*WpR!WEQuElHm|F>&|XO~Ub) z6@%BmHGLoM&dBG$z{ny1ArxXCTst1}UEkrsv;!>b-`r7Lm|M~fw1vUb)z4*}Q$iB} DOkOt< literal 0 HcmV?d00001 diff --git a/images/pause.svg b/images/pause.svg new file mode 100644 index 0000000..010cafe --- /dev/null +++ b/images/pause.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/start.png b/images/start.png new file mode 100644 index 0000000000000000000000000000000000000000..9cc7034b7f310d207671a78142366f1434e37abe GIT binary patch literal 278 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}7d%}YLn7SY zPTRT?mL57WDfaljA72E$vY!9DY+wI-#sT@} zubIq0YS~?$xVTuS)qQ(0-4)1ocFAv+Sgp8-A+FqTLba%e*n!gTXKGe7WUvOjoxQU# zmU$D~fzp09qwNhL3@hFpRyY^gp~Z0h>IuG?C&HLDPE6^{;b$mU7P&c(Vd05?rVMYm z7WlrH{aJJ_d#EjgtjmT#o>;dHr7~S%TNtn1iewPCM#^CAd=d#Wzp$P#0UvH8C literal 0 HcmV?d00001 diff --git a/images/start.svg b/images/start.svg new file mode 100644 index 0000000..b159c91 --- /dev/null +++ b/images/start.svg @@ -0,0 +1 @@ + diff --git a/main.cpp b/main.cpp index b8cdc17..43a707b 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,3 @@ -#include "BoxController.h" #include "Config.h" #include "Link.h" #include "TcpServer.h" @@ -11,20 +10,24 @@ TcpServer* server; LinkObject* vo; LinkObject* vo1; -const char* ConfigurationPath = "/opt/RecordControlApplication/bin/config.json"; + +const char* ConfigurationPath = "/opt/RecordControlApplication/configuration/config.json"; +const char* NetConfigPath = "/opt/RecordControlApplication/configuration/net.json"; +const char* NetScriptPath = "/opt/RecordControlApplication/scripts/setNetwork.sh"; +const char* VideoPath = "/root/usb/videos"; +const char* SnapPath = "/root/usb/snap"; int main(int argc, char* argv[]) { - // init Link if (!Link::init()) { - qDebug() << "Link init failed!"; + qDebug() << "Link init failed, exit!"; 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 + // video output in channel HDMI-OUT0, only show ui in HDMI-OUT0 vo = Link::create("OutputVo"); QVariantMap dataVo; dataVo["vid"] = 0; @@ -69,8 +72,9 @@ int main(int argc, char* argv[]) Config::loadConfig(ConfigurationPath); // create server and listen port 8080 - // server = new TcpServer(); - // server->listen(QHostAddress::Any, 8080); + server = new TcpServer(); + server->listen(QHostAddress::Any, 8080); + qDebug() << "start tcp server, listen port 8080..."; Widget w; w.show(); diff --git a/res.qrc b/res.qrc new file mode 100644 index 0000000..b73cf0e --- /dev/null +++ b/res.qrc @@ -0,0 +1,8 @@ + + + images/start.svg + images/pause.svg + images/start.png + images/pause.png + +