diff --git a/Channel.cpp b/Channel.cpp index fd686f8..e426ecf 100755 --- a/Channel.cpp +++ b/Channel.cpp @@ -1,5 +1,7 @@ #include "Channel.h" #include "Constant.h" +#include "DatabaseManager.h" +#include "Json.h" #include "Log.h" #include "Tool.h" #include @@ -8,40 +10,50 @@ #include #include -LinkObject* Channel::audioInput = nullptr; -LinkObject* Channel::audioOutput = nullptr; +#include + +LinkObject* Channel::lineIn = nullptr; +LinkObject* Channel::lineOut = nullptr; LinkObject* Channel::rtspServer = nullptr; -QString Channel::curRecordFilename = ""; +LinkObject* Channel::resample = nullptr; extern LinkObject* vo; extern LinkObject* vo1; +extern DatabaseManager* db; Channel::Channel(QObject* parent) : QObject(parent) { videoInput = nullptr; videoOutput = nullptr; + audioInput = nullptr; + audioOutput = nullptr; videoEncoder = nullptr; audioEncoder = nullptr; record = nullptr; rtsp = nullptr; - file = nullptr; + inputFile = nullptr; videoDecoder = nullptr; audioDecoder = nullptr; image = Link::create("InputImage"); - if (audioInput == nullptr) { - audioInput = Link::create("InputAlsa"); + if (lineIn == nullptr) { + lineIn = Link::create("InputAlsa"); QVariantMap dataIn; dataIn["path"] = "hw:0,0"; dataIn["channels"] = 2; - audioInput->start(dataIn); + lineIn->start(dataIn); } - if (audioOutput == nullptr) { - audioOutput = Link::create("OutputAlsa"); + if (resample = nullptr) { + resample = Link::create("Resample"); + resample->start(); + lineIn->linkA(resample); + } + if (lineOut == nullptr) { + lineOut = Link::create("OutputAlsa"); QVariantMap dataOut; dataOut["path"] = "hw:0,0"; - audioOutput->start(dataOut); + lineOut->start(dataOut); } if (rtspServer == nullptr) { rtspServer = Link::create("Rtsp"); @@ -58,11 +70,22 @@ void Channel::init() Log::error("channel name is empty!"); return; } + // 通道音频输出 + audioOutput = Link::create("OutputAo"); + QVariantMap dataVo; if (channelName == Constant::MainChannel) { videoOutput = vo; + dataVo["interface"] = "HDMI-OUT0"; } else { videoOutput = vo1; + dataVo["interface"] = "HDMI-OUT1"; } + loadOverlayConfig(); + + // 水印,用于显示录制状态 + overlay = Link::create("Overlay"); + overlay->start(norecordOverlay); + overlay->linkV(videoOutput); // 视频输入 videoInput = Link::create("InputVi"); @@ -71,35 +94,54 @@ void Channel::init() dataVi["width"] = 1920; dataVi["height"] = 1080; videoInput->start(dataVi); - videoInput->linkV(videoOutput); + videoInput->linkV(overlay)->linkV(videoOutput); - // 视频编码 - videoEncoder = Link::create("EncodeV"); - videoEncoder->start(videoEncoderParams); + // 通道音频输入 + audioInput = Link::create("InputAi"); + QVariantMap dataAi; + dataAi["interface"] = channelName; + dataAi["channels"] = 2; + audioInput->start(dataAi); + audioInput->linkA(audioOutput); + + // 音量 + gain = Link::create("Gain"); + gain->start(); + volume = Link::create("Volume"); + volume->start(); + gain->linkA(volume); + lineOut->linkA(gain); // 音频编码 audioEncoder = Link::create("EncodeA"); audioEncoder->start(audioEncoderParams); + // 视频编码 + videoEncoder = Link::create("EncodeV"); + videoEncoder->start(videoEncoderParams); + // 录制 record = Link::create("Mux"); QVariantMap dataMp4; dataMp4["format"] = "mp4"; - dataMp4["segmentDuration"] = duration; + dataMp4["filecache"] = 20480000; dataMp4["lowLatency"] = true; - // dataMp4["filecache"] = 20480000; + dataMp4["thread"] = true; + dataMp4["segmentDuration"] = duration; record->setData(dataMp4); videoInput->linkV(videoEncoder)->linkV(record); - audioInput->linkA(audioEncoder)->linkA(record); + audioInput->linkV(audioEncoder)->linkV(record); + resample->linkA(audioEncoder)->linkA(record); - connect(record, &LinkObject::newEvent, [=](QString type, QVariant data) { - if (type == "newSegment") { - int id = data.toInt(); - curRecordFilename = QString("%1_%2.mp4") - .arg(curRecordFilename.split("_")[0]) - .arg(id, 2, 10, QLatin1Char('0')); - } - }); + connect(record, SIGNAL(newEvent(QString, QVariant)), this, SLOT(onNewEvent(QString, QVariant))); + // 测试执行时间,用时18-20ms + // connect(record, &LinkObject::newEvent, [=](QString msg, QVariant data) { + // Tool::getCostTime( + // [=] { + // onNewEvent(msg, data); + // }, + // "onNewEvent"); + // }); // rstp流 rtsp = Link::create("Mux"); @@ -108,11 +150,12 @@ void Channel::init() dataRtsp["format"] = "rtsp"; rtsp->start(dataRtsp); videoInput->linkV(videoEncoder)->linkV(rtsp)->linkV(rtspServer); - audioInput->linkA(audioEncoder)->linkA(rtsp)->linkA(rtspServer); + audioInput->linkA(audioEncoder)->linkV(rtsp)->linkV(rtspServer); + resample->linkA(audioEncoder)->linkA(rtsp)->linkA(rtspServer); // 播放文件 - file = Link::create("InputFile"); - connect(file, &LinkObject::newEvent, [=](QString type, QVariant msg) { + inputFile = Link::create("InputFile"); + connect(inputFile, &LinkObject::newEvent, [=](QString type, QVariant msg) { if (type == "EOF") { Log::info("{} one video playback end", channelName.toStdString()); emit playEnd(); @@ -123,15 +166,15 @@ void Channel::init() audioDecoder = Link::create("DecodeA"); audioDecoder->start(); if (channelName == Constant::MainChannel) { - file->linkA(audioDecoder)->linkA(audioOutput); + inputFile->linkA(audioDecoder)->linkA(lineOut); } else { - file->linkA(audioDecoder); + inputFile->linkA(audioDecoder); } // 视频解码 videoDecoder = Link::create("DecodeV"); videoDecoder->start(); - file->linkV(videoDecoder)->linkV(videoOutput); + inputFile->linkV(videoDecoder)->linkV(videoOutput); } /** @@ -139,19 +182,60 @@ void Channel::init() */ void Channel::startRecord() { - // 阻塞到整分钟才开始录制,方便上位机进行回放 - while (int secs = QTime::currentTime().second() % 60 != 0) { - QCoreApplication::processEvents(); - } - QString curTime = QDateTime::currentDateTime().toString("yyyyMMddhhmm"); - curRecordFilename = curTime + "_00.mp4"; + QElapsedTimer mstimer; + mstimer.start(); + // 记录本次录制开始时间以及当前视频录制的开始时间 + QString curTime = QDateTime::currentDateTime().toString("yyyyMMddhhmmss"); + startTime = curTime; + currentTime = curTime; QVariantMap dataRecord; - QString path = QString("%1/%2/%3.mp4").arg(Constant::VideoPath).arg(channelName).arg(curTime + "_%02d"); + QString path = QString("%1/%2/%3_%d.mp4").arg(Constant::VideoPath).arg(channelName).arg(curTime); dataRecord["path"] = path; record->start(dataRecord); isRecord = true; Log::info("{} start recording...", channelName.toStdString()); - emit showRecordState(true); + Log::info("open done {}", path.toStdString()); + + // 显示录制状态水印 + overlay->setData(recordOverlay); + + float time = (double)mstimer.nsecsElapsed() / (double)1000000; + qDebug() << channelName << "startRecord cast time:" << time << "ms"; +} + +/** + * @brief 新事件槽函数 + * @param msg 时间类型 + * @param data 数据 + */ +void Channel::onNewEvent(QString msg, QVariant data) +{ + if (msg == "newSegment") { + int id = data.toInt(); + // 将上一次的文件信息存入数据库中 + DatabaseManager::File file; + file.channel = channelName == Constant::MainChannel + ? DatabaseManager::MainChannel + : DatabaseManager::SecondaryChannel; + // 设置当前视频的录制时间信息 + file.year = currentTime.mid(0, 4); + file.month = currentTime.mid(4, 2); + file.day = currentTime.mid(6, 2); + file.time = currentTime.mid(8, 6); + // 设置当前视频的文件名,格式:本次录制开始时间_第几次分片 + file.filename = QString("%1_%2.mp4").arg(startTime).arg(id - 1); + if (db->insert(file)) { + Log::info("insert one record into database success, name: {}, channel: {}", + file.filename.toStdString(), + channelName.toStdString()); + } else { + Log::error("insert one record into database failed, name: {}, channel: {}", + file.filename.toStdString(), + channelName.toStdString()); + } + // 更新当前录制录制视频的时间 + currentTime = QDateTime::currentDateTime().toString("yyyyMMddhhmmss"); + } } /** @@ -161,7 +245,16 @@ void Channel::stopRecord() { Log::info("{} stop recording...", channelName.toStdString()); record->stop(true); - emit showRecordState(false); + // 将录制文件的信息存入数据库 + DatabaseManager::File file; + file.channel = channelName == Constant::MainChannel + ? DatabaseManager::MainChannel + : DatabaseManager::SecondaryChannel; + file.year = startTime.mid(0, 4); + file.month = startTime.mid(4, 2); + file.day = startTime.mid(6, 2); + file.time = startTime.mid(8, 6); + overlay->setData(norecordOverlay); } /** @@ -177,7 +270,6 @@ bool Channel::startPlayback(QString path) videoInput->unLinkV(videoOutput); QVariantMap dataImage; dataImage["path"] = Constant::EmptyImagePath; - image->setData(dataImage); image->start(dataImage); image->linkV(videoOutput); state = Error; @@ -187,26 +279,30 @@ bool Channel::startPlayback(QString path) // 开始回放 QVariantMap dataFile; dataFile["path"] = path; - dataFile["async"] = false; - file->start(dataFile); + dataFile["sync"] = true; + inputFile->start(dataFile); // 判断视频是否损坏,如果损坏则输出提示图片 - int duration = file->invoke("getDuration", path).toInt() / 1000; + int duration = inputFile->invoke("getDuration", path).toInt(); if (duration == 0) { Log::error("cannot open video {}, video file was corrupted", path.toStdString()); - file->stop(); + inputFile->stop(); videoInput->unLinkV(videoOutput); QVariantMap dataImage; dataImage["path"] = Constant::ErrorImagePath; - image->setData(dataImage); image->start(dataImage); image->linkV(videoOutput); state = Error; return false; } - videoInput->unLinkV(videoOutput); + // 断开视频信号输出,启动回放输出 + overlay->unLinkV(videoOutput); videoDecoder->linkV(videoOutput); + // 断开音频信号输出,启动回放输出 + audioInput->unLinkA(audioOutput); + audioDecoder->linkA(lineOut); + playbackDuration = duration; state = Playback; return true; @@ -219,12 +315,17 @@ void Channel::startPlayLive() { if (state == Playback) { videoDecoder->unLinkV(videoOutput); - file->stop(true); + audioDecoder->unLinkA(audioOutput); + inputFile->stop(true); } else if (state == Error) { image->unLinkV(videoOutput); image->stop(true); } - videoInput->linkV(videoOutput); + // 打开视频和音频输出 + overlay->linkV(videoOutput); + audioInput->linkA(audioOutput); + // 关闭外部音频输出 + audioDecoder->unLinkA(lineOut); state = Stop; } @@ -234,9 +335,9 @@ void Channel::startPlayLive() void Channel::back() { Log::info("{} back 10s", channelName.toStdString()); - int curPos = file->invoke("getPosition").toInt(); + int curPos = inputFile->invoke("getPosition").toInt(); curPos -= 10 * 1000; - file->invoke("seek", curPos); + inputFile->invoke("seek", curPos); } /** @@ -245,9 +346,9 @@ void Channel::back() void Channel::forward() { Log::info("{} forward 10s", channelName.toStdString()); - int curPos = file->invoke("getPosition").toInt(); + int curPos = inputFile->invoke("getPosition").toInt(); curPos += 10 * 1000; - file->invoke("seek", curPos); + inputFile->invoke("seek", curPos); } /** @@ -261,7 +362,7 @@ void Channel::togglePause() state = Pause; else state = Playback; - file->invoke("pause", state == Pause); + inputFile->invoke("pause", state == Pause); } /** @@ -272,8 +373,68 @@ void Channel::showFinishPromot() videoInput->unLinkV(videoDecoder); QVariantMap dataImage; dataImage["path"] = Constant::FinishImagePath; - image->setData(dataImage); image->start(dataImage); image->linkV(videoOutput); state = Finish; -} \ No newline at end of file +} + +/** + * @brief 获取音量 + * @return L左声道音量,R右声道音量 + */ +QVariantMap Channel::getVolume() +{ + QVariantMap result; + QVariantMap data = volume->invoke("getVolume").toMap(); + result["L"] = data["max"].toInt(); + if (data["avg"].toInt() < 15) + result["L"] = 0; + result["R"] = data["max2"].toInt(); + if (data["avg2"].toInt() < 15) + result["R"] = 0; + return result; +} + +/** + * @brief 增大音量 + */ +void Channel::volumeUp() +{ + if (curGain < maxGian) + curGain += 6; + QVariantMap data; + data["gain"] = curGain; + gain->setData(data); +} + +/** + * @brief 减小音量 + */ +void Channel::volumeDown() +{ + if (curGain > minGain) + curGain -= 6; + QVariantMap data; + data["gain"] = curGain; + gain->setData(data); +} + +/** + * @brief 将水印配置属性加载到内存中 + */ +void Channel::loadOverlayConfig() +{ + auto loadFromJson = [](const QString& path) { + QVariantMap dataOver; + QVariantList list = Json::loadFile(path).toList(); + QVariantList list2; + for (int i = 0; i < list.count(); i++) { + QVariantMap map = list[i].toMap(); + list2 << map; + } + dataOver["lays"] = list2; + return dataOver; + }; + recordOverlay = loadFromJson(Constant::RecordOverlay); + norecordOverlay = loadFromJson(Constant::NoRecordOverlay); +} diff --git a/Channel.h b/Channel.h index 2465740..15b11ea 100755 --- a/Channel.h +++ b/Channel.h @@ -1,6 +1,7 @@ #ifndef CHANNEL_H #define CHANNEL_H +#include "Json.h" #include "Link.h" #include #include @@ -18,6 +19,24 @@ public: explicit Channel(QObject* parent = nullptr); void init(); + // 录制 + void startRecord(); + void stopRecord(); + + // 回放 + bool startPlayback(QString path); + void forward(); + void back(); + void togglePause(); + void startPlayLive(); + void showFinishPromot(); + + // 音量 + QVariantMap getVolume(); + void volumeUp(); + void volumeDown(); + +public: QString channelName; LinkObject* videoInput; @@ -25,40 +44,50 @@ public: QVariantMap videoEncoderParams; LinkObject* videoOutput; - static LinkObject* audioInput; - static LinkObject* audioOutput; - LinkObject* audioEncoder; - QVariantMap audioEncoderParams; + static LinkObject* lineIn; // 外部音频输入 + static LinkObject* resample; // 重采样 + static LinkObject* lineOut; // 外部音频输出 + LinkObject* gain; // 音量增益 + LinkObject* volume; // 音量 + int maxGian = 30; + int minGain = -30; + int curGain = 0; + + LinkObject* audioInput; // 通道音频输入 + LinkObject* audioOutput; // 通道音频输入 + LinkObject* audioEncoder; // 音频编码器 + QVariantMap audioEncoderParams; // 编码参数 LinkObject* record; int duration = 1 * 60 * 1000; // 单个视频时长 bool isRecord = false; - static QString curRecordFilename; // 当前正在录制的文件名 + QString startTime; // 本次录制文件的开始时间 + QString currentTime; // 当前录制文件的开始时间 + int segmentId = 0; + LinkObject* overlay; // 水印,提示是否在录制视频 + QVariantMap recordOverlay; // 录制状态下的水印参数 + QVariantMap norecordOverlay; // 非录制状态下的水印参数 - int playbackDuration = 0; - LinkObject* file; - LinkObject* image; + int playbackDuration = 0; // 当前播放视频的时长,单位ms + LinkObject* inputFile; LinkObject* videoDecoder; LinkObject* audioDecoder; + LinkObject* image; PlaybackState state = Stop; static LinkObject* rtspServer; LinkObject* rtsp; QString pushCode; - void startRecord(); - void stopRecord(); - bool startPlayback(QString path); - void forward(); - void back(); - void togglePause(); - - void startPlayLive(); - void showFinishPromot(); +private slots: + void onNewEvent(QString msg, QVariant data); signals: void playEnd(); void showRecordState(bool state); + +private: + void loadOverlayConfig(); }; #endif // CHANNEL_H diff --git a/ChannelSetting.cpp b/ChannelSetting.cpp deleted file mode 100644 index 030d1f6..0000000 --- a/ChannelSetting.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include "ChannelSetting.h" -#include "Constant.h" -#include "SerialPortTool.h" -#include "ui_ChannelSetting.h" -#include -#include -#include -#include -#include - -extern SerialPortTool* serialPortTool; - -ChannelSetting::ChannelSetting(QWidget* parent) - : QWidget(parent) - , ui(new Ui::ChannelSetting) -{ - ui->setupUi(this); - - QPoint globalPos = parent->mapToGlobal(QPoint(0, 0)); - int x = globalPos.x() + (parent->width() - this->width()) / 2; - int y = globalPos.y() + (parent->height() - this->height()) / 2; - this->move(x, y); -} - -ChannelSetting::~ChannelSetting() -{ - delete ui; -} - -/** - * @brief 重写show方法 - */ -void ChannelSetting::show() -{ - // 默认聚焦确定按钮 - ui->btnDone->setFocus(); - // 获取配置文件中保存的输入源类型(HDMI or VGA) - QVariantMap config = Json::loadFile(Constant::ConfigurationPath).toMap(); - QVariantList list = config["interface"].toList(); - for (int i = 0; i < list.count(); i++) { - QVariantMap cfg = list.at(i).toMap(); - QString channelName = cfg["name"].toString(); - QString protocol = cfg["protocol"].toString(); - if (channelName == Constant::MainChannel) { - protocol1 = protocol; - } else { - protocol2 = protocol; - } - } - if (protocol1 == "HDMI") { - ui->btn_hdmi_1->setChecked(true); - } else { - ui->btn_vga_1->setChecked(true); - } - if (protocol2 == "HDMI") { - ui->btn_hdmi_2->setChecked(true); - } else { - ui->btn_vga_2->setChecked(true); - } - QWidget::show(); -} - -void ChannelSetting::onReceiveNext() -{ - QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier); - QApplication::sendEvent(focusWidget(), &keyEvent); - - QKeyEvent keyEvent1(QEvent::KeyRelease, Qt::Key_Down, Qt::NoModifier); - QApplication::sendEvent(focusWidget(), &keyEvent1); -} - -void ChannelSetting::onReceivePre() -{ - QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier); - QApplication::sendEvent(focusWidget(), &keyEvent); - - QKeyEvent keyEvent1(QEvent::KeyRelease, Qt::Key_Up, Qt::NoModifier); - QApplication::sendEvent(focusWidget(), &keyEvent1); -} - -void ChannelSetting::onReceiveEnter() -{ - // 聚焦的控件 - QString objName = focusWidget()->objectName(); - // 通道一 - if (objName.endsWith("1")) { - if (objName == "btn_hdmi_1") { - ui->btn_hdmi_1->setChecked(true); - ui->btn_vga_1->setChecked(false); - protocol1 = "HDMI"; - } else { - ui->btn_hdmi_1->setChecked(false); - ui->btn_vga_1->setChecked(true); - protocol1 = "VGA"; - } - } - // 通道二 - else if (objName.endsWith("2")) { - if (objName == "btn_hdmi_2") { - ui->btn_hdmi_2->setChecked(true); - ui->btn_vga_2->setChecked(false); - protocol2 = "HDMI"; - } else { - ui->btn_hdmi_2->setChecked(false); - ui->btn_vga_2->setChecked(true); - protocol2 = "VGA"; - } - } - // 确认按钮 - else { - // 保存配置 - QVariantMap config = Json::loadFile(Constant::ConfigurationPath).toMap(); - QVariantList list = config["interface"].toList(); - for (int i = 0; i < list.count(); i++) { - QVariantMap cfg = list.at(i).toMap(); - QString channelName = cfg["name"].toString(); - if (channelName == Constant::MainChannel) { - cfg["protocol"] = protocol1; - } else { - cfg["protocol"] = protocol2; - } - list[i] = cfg; - } - config["interface"] = list; - Json::saveFile(config, Constant::ConfigurationPath); - // 发送指令 - if (protocol1 == "HDMI") { - serialPortTool->onMainChannelHDMI(); - } else { - serialPortTool->onMainChannelVGA(); - } - QThread::msleep(100); - if (protocol2 == "HDMI") { - serialPortTool->onSecondaryChannelHDMI(); - } else { - serialPortTool->onSecondaryChannelVGA(); - } - hide(); - } -} \ No newline at end of file diff --git a/ChannelSetting.h b/ChannelSetting.h deleted file mode 100644 index aa5a9be..0000000 --- a/ChannelSetting.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef CHANNELSETTING_H -#define CHANNELSETTING_H - -#include - -namespace Ui { -class ChannelSetting; -} - -class ChannelSetting : public QWidget { - Q_OBJECT - -public: - explicit ChannelSetting(QWidget* parent = 0); - ~ChannelSetting(); - void show(); - -public slots: - void onReceiveEnter(); - void onReceivePre(); - void onReceiveNext(); - -private: - Ui::ChannelSetting* ui; - QString protocol1; // 通道1输入信号类型 - QString protocol2; // 通道2输入信号类型 -}; - -#endif // CHANNELSETTING_H diff --git a/ChannelSetting.ui b/ChannelSetting.ui deleted file mode 100644 index 9d1bdce..0000000 --- a/ChannelSetting.ui +++ /dev/null @@ -1,269 +0,0 @@ - - - ChannelSetting - - - - 0 - 0 - 391 - 307 - - - - Form - - - QPushButton { - outline: none; - border: none; -} - -QPushButton:focus { - border: 5px solid red; -} - -QPushButton:checked { - background: blue; -} - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QWidget { - color: #ffffff; -} - -QWidget#widget{ - background: rgba(0, 0, 0, 0.8); -} - -QPushButton { - outline: none; - border: none; - background: rgba(100,100,100,0.8); -} - -QPushButton:focus { - border: 5px solid red; -} - -QPushButton:checked { - background: rgba(0,0,255,0.8); -} - - - - - - - 14 - - - - 视频源设置 - - - Qt::AlignCenter - - - - - - - - - - 16777213 - 16777215 - - - - - 12 - - - - 通道1 - - - Qt::AlignCenter - - - - - - - 10 - - - 10 - - - - - - 0 - 0 - - - - - 12 - - - - HDMI - - - true - - - - - - - - 0 - 0 - - - - - 12 - - - - VGA - - - true - - - - - - - - - - - - - - 16777213 - 16777215 - - - - - 12 - - - - 通道2 - - - Qt::AlignCenter - - - - - - - 10 - - - 10 - - - - - - 0 - 0 - - - - - 12 - - - - HDMI - - - true - - - - - - - - 0 - 0 - - - - - 12 - - - - VGA - - - true - - - - - - - - - - - - 0 - 40 - - - - - 12 - - - - background: rgba(0,0,255,0.8); - - - 确定 - - - - - - - - - - - diff --git a/CheckStorageThread.cpp b/CheckStorageThread.cpp index e8c34e2..5228847 100755 --- a/CheckStorageThread.cpp +++ b/CheckStorageThread.cpp @@ -1,5 +1,6 @@ #include "CheckStorageThread.h" #include "Constant.h" +#include "DatabaseManager.h" #include "Log.h" #include "Tool.h" #include @@ -14,6 +15,7 @@ extern QString curFilename; extern QMutex mutex; extern QWaitCondition condition; +extern DatabaseManager* db; CheckStorageThread::CheckStorageThread() { @@ -29,10 +31,13 @@ void CheckStorageThread::run() if (available < THRESHOLD) { Log::info("there are not enough storage, then remove some files..."); emit diskWillFull(); - // 获取文件列表 - QStringList fileList = Tool::getFileList(QString("%1/%2").arg(Constant::VideoPath).arg(Constant::MainChannel)); - if (!fileList.isEmpty()) { - QString filename = fileList.first(); + // 从数据库中取出前两条数据,找到相关的文件删除 + QList fileList = db->getTopTwo(); + for (auto& file : fileList) { + QString filename = file.time; + QString channel = file.channel == DatabaseManager::MainChannel + ? Constant::MainChannel + : Constant::SecondaryChannel; // 判断文件是否再回放,如果在回放就阻塞等待 mutex.lock(); if (filename == curFilename) { @@ -42,20 +47,15 @@ void CheckStorageThread::run() } mutex.unlock(); // 删除文件 - QString path = QString("%1/%2/%3").arg(Constant::VideoPath).arg(Constant::MainChannel).arg(filename); + QString path = QString("%1/%2/%3").arg(Constant::VideoPath).arg(channel).arg(filename); bool ret = Tool::removeFile(path); if (!ret) { Log::error("remove file {} failed", path.toStdString()); } else { Log::info("remove file {} success", path.toStdString()); } - path = QString("%1/%2/%3").arg(Constant::VideoPath).arg(Constant::SecondaryChannel).arg(filename); - ret = Tool::removeFile(path); - if (!ret) { - Log::error("remove file {} failed", path.toStdString()); - } else { - Log::info("remove file {} success", path.toStdString()); - } + // 从数据库清除这条记录 + db->remove(file.channel, file.time); } } else { emit diskNotFull(); diff --git a/Constant.h b/Constant.h index 1b3b626..4b7206c 100755 --- a/Constant.h +++ b/Constant.h @@ -3,7 +3,7 @@ class Constant { public: - Constant() { } + Constant() {} enum RecordMode { NoChannelRecord, OneChannelRecord, @@ -14,17 +14,21 @@ public: OneChannelPlayback = 1, TwoChannelPlayback = 2 }; + static constexpr char* LogPath = "/opt/RecordControlApplication/logs/log.txt"; static constexpr char* ConfigurationPath = "/opt/RecordControlApplication/configuration/config.json"; static constexpr char* NetConfigPath = "/opt/RecordControlApplication/configuration/net.json"; static constexpr char* NetScriptPath = "/opt/RecordControlApplication/scripts/setNetwork.sh"; + static constexpr char* DatabasePath = "/opt/RecordControlApplication/database/data.db"; static constexpr char* VideoPath = "/root/usb/videos"; static constexpr char* MountedPath = "/root/usb"; static constexpr char* ErrorImagePath = "/opt/RecordControlApplication/images/error.jpeg"; static constexpr char* EmptyImagePath = "/opt/RecordControlApplication/images/empty.jpeg"; static constexpr char* FinishImagePath = "/opt/RecordControlApplication/images/finish.jpeg"; - static constexpr char* MainChannel = "HDMI-C"; - static constexpr char* SecondaryChannel = "HDMI-D"; + static constexpr char* MainChannel = "HDMI-A"; + static constexpr char* SecondaryChannel = "HDMI-B"; + static constexpr char* RecordOverlay = "/opt/RecordControlApplication/overlay/record.json"; + static constexpr char* NoRecordOverlay = "/opt/RecordControlApplication/overlay/no-record.json"; }; #endif // CONSTANT_H diff --git a/DatabaseManager.cpp b/DatabaseManager.cpp new file mode 100755 index 0000000..5cc7a28 --- /dev/null +++ b/DatabaseManager.cpp @@ -0,0 +1,279 @@ +#include "DatabaseManager.h" +#include "Constant.h" +#include "Log.h" +#include +#include +#include +#include + +DatabaseManager* DatabaseManager::instance = nullptr; + +DatabaseManager::DatabaseManager() +{ + if (QSqlDatabase::contains("qt_sql_default_connection")) { + db = QSqlDatabase::database("qt_sql_default_connection"); + } else { + db = QSqlDatabase::addDatabase("QSQLITE"); + db.setDatabaseName(Constant::DatabasePath); + } +} + +DatabaseManager::~DatabaseManager() +{ + db.close(); +} + +DatabaseManager* DatabaseManager::getInstace() +{ + if (instance == nullptr) { + static QMutex mutex; + QMutexLocker locker(&mutex); + if (instance == nullptr) { + instance = new DatabaseManager(); + } + } + return instance; +} + +/** + * @brief 打开数据库 + * @return + */ +bool DatabaseManager::open() +{ + if (!db.open()) { + Log::error("database open failed"); + qDebug() << db.lastError(); + return false; + } + Log::info("database open success"); + return true; +} + +/** + * @brief 关闭数据库 + */ +void DatabaseManager::close() +{ + if (db.isOpen()) + db.close(); +} + +/** + * @brief 添加数据 + * @param file + */ +bool DatabaseManager::insert(File file) +{ + QSqlQuery query; + query.prepare("insert into file (channel, name, year, month, day) values (?, ?, ?, ?, ?)"); + query.bindValue(0, file.channel); + query.bindValue(1, file.time); + query.bindValue(2, file.year); + query.bindValue(3, file.month); + query.bindValue(4, file.day); + if (query.exec()) { + return true; + } else { + Log::error("insert one record into database failed, reason: {}", + query.lastError().databaseText().toStdString()); + return false; + } +} + +/** + * @brief 查询数据 + * @param params 参数 + * @return + */ +QList DatabaseManager::get(QVariantMap params) +{ + QList result; + QSqlQuery query; + QString sql = "select * from file where channel=? and year = ? and month = ? and day = ?"; + QString year = params.value("year").toString(); + QString month = params.value("month").toString(); + QString day = params.value("day").toString(); + Channel chn = static_cast(params.value("channel").toInt()); + query.prepare(sql); + query.bindValue(0, chn); + query.bindValue(1, year); + query.bindValue(2, month); + query.bindValue(3, day); + if (year.isEmpty() || month.isEmpty() || day.isEmpty() || (chn != MainChannel && chn != SecondaryChannel)) { + Log::error("select from database error, params error"); + return result; + } + if (query.exec()) { + while (query.next()) { + DatabaseManager::File file; + file.id = query.value("id").toInt(); + file.year = query.value("year").toString(); + file.month = query.value("month").toString(); + file.day = query.value("day").toString(); + file.channel = static_cast(query.value("channel").toInt()); + file.time = query.value("time").toString(); + file.filename = query.value("filename").toString(); + result.push_back(file); + } + } else { + Log::error("select from database failed, reason: {}", + query.lastError().databaseText().toStdString()); + } + Log::info("record of one day: {}", result.length()); + return result; +} + +/** + * @brief 获取某个通道的所有文件列表 + * @param chn + * @return + */ +QList DatabaseManager::get(DatabaseManager::Channel chn) +{ + QList result; + QSqlQuery query; + QString sql = "select * from file where channel=?"; + query.prepare(sql); + query.bindValue(0, chn); + if (query.exec()) { + while (query.next()) { + DatabaseManager::File file; + file.id = query.value("id").toInt(); + file.year = query.value("year").toString(); + file.month = query.value("month").toString(); + file.day = query.value("day").toString(); + file.channel = static_cast(query.value("channel").toInt()); + file.time = query.value("time").toString(); + file.filename = query.value("filename").toString(); + result.push_back(file); + } + } else { + Log::error("select from database failed, reason: {}", + query.lastError().databaseText().toStdString()); + } + return result; +} + +/** + * @brief 获取前两条记录 + */ +QList DatabaseManager::getTopTwo() +{ + QList result; + QSqlQuery query; + query.prepare("select * from file limit 0,2"); + if (query.exec()) { + while (query.next()) { + DatabaseManager::File file; + file.id = query.value("id").toInt(); + file.year = query.value("year").toString(); + file.month = query.value("month").toString(); + file.day = query.value("day").toString(); + file.channel = static_cast(query.value("channel").toInt()); + file.time = query.value("time").toString(); + file.filename = query.value("filename").toString(); + result.push_back(file); + } + } else { + Log::error("select top two records from database failed, reason: {}", + query.lastError().databaseText().toStdString()); + } + return result; +} + +/** + * @brief 获取所有的年份 + * @return + */ +QStringList DatabaseManager::getAllYears(Channel chn) +{ + QStringList result; + QSqlQuery query; + query.prepare("select distinct year from file"); + if (query.exec()) { + while (query.next()) { + QString year = query.value(0).toString(); + result.push_back(year); + } + } else { + Log::error("select all years from database failed, reason: {}", + query.lastError().databaseText().toStdString()); + } + Log::info("number of year: {}", result.length()); + return result; +} + +/** + * @brief 获取某年所有的月份 + * @param year + * @return + */ +QStringList DatabaseManager::getAllMonths(Channel chn, QString year) +{ + QStringList result; + QSqlQuery query; + query.prepare("select distinct month from file where channel = ? and year=?"); + query.bindValue(0, chn); + query.bindValue(1, year); + if (query.exec()) { + while (query.next()) { + QString month = query.value(0).toString(); + result.push_back(month); + } + } else { + Log::error("select all months of one year from database failed, reason: {}", + query.lastError().databaseText().toStdString()); + } + Log::info("number of month: {}", result.length()); + return result; +} + +/** + * @brief 某年某月的天数 + * @param year + * @param month + * @return + */ +QStringList DatabaseManager::getAllDays(Channel chn, QString year, QString month) +{ + QStringList result; + QSqlQuery query; + query.prepare("select distinct day from file where channel = ? and year=? and month=?"); + query.bindValue(0, chn); + query.bindValue(1, year); + query.bindValue(2, month); + if (query.exec()) { + while (query.next()) { + QString day = query.value(0).toString(); + result.push_back(day); + } + } else { + Log::error("select all days of one month from database failed, reason: {}", + query.lastError().databaseText().toStdString()); + } + Log::info("number of day: {}", result.length()); + return result; +} + +/** + * @brief 根据通道和文件名删除记录 + * @param id + * @return + */ +bool DatabaseManager::remove(DatabaseManager::Channel chn, QString name) +{ + QSqlQuery query; + query.prepare("delet from file where channel = ? and name = ?"); + query.bindValue(0, chn); + query.bindValue(1, name); + if (query.exec()) { + return true; + } else { + Log::error("delete one record from database failed, channel = {}, name = {}, reason: {}", + (int)chn, + name.toStdString(), + query.lastError().databaseText().toStdString()); + return false; + } +} diff --git a/DatabaseManager.h b/DatabaseManager.h new file mode 100755 index 0000000..5173718 --- /dev/null +++ b/DatabaseManager.h @@ -0,0 +1,48 @@ +#ifndef DATABASEMANAGER_H +#define DATABASEMANAGER_H +#include +#include + +// 数据库管理类 +class DatabaseManager { +public: + enum Channel { + MainChannel = 1, + SecondaryChannel + }; + struct File { + int id; // id + Channel channel; // 通道 + QString year; // 年 + QString month; // 月 + QString day; // 日 + QString time; // 时分秒,hh:mm:ss + QString filename; // 真实路径 + }; + static DatabaseManager* getInstace(); + ~DatabaseManager(); + + bool open(); + void close(); + + bool insert(File file); + + bool remove(DatabaseManager::Channel chn, QString name); + + QList getTopTwo(); + + QList get(QVariantMap params); + QList get(Channel chn); + QStringList getAllYears(Channel chn); + QStringList getAllMonths(Channel chn, QString year); + QStringList getAllDays(Channel, QString year, QString month); + +private: + DatabaseManager(); + +private: + static DatabaseManager* instance; + QSqlDatabase db; +}; + +#endif // DATABASEMANAGER_H diff --git a/FocusWindow.cpp b/FocusWindow.cpp new file mode 100755 index 0000000..e1e3dda --- /dev/null +++ b/FocusWindow.cpp @@ -0,0 +1,320 @@ +#include +#include "FocusWindow.h" + +using namespace FW; + +/** + * @brief жijؼǷΪijϵĺѡؼ + * @param focused ǰ۽Ŀؼȫλ + * @param focusable ǰӵнĿؼȫλ + * @param direction + * @return + */ +bool FocusWindow::isCandidate(QRect focused, QRect focusable, Direction direction) +{ + switch (direction) + { + case Up: + return (focused.bottom() > focusable.bottom() || focused.top() >= focusable.bottom()) + && focused.top() > focusable.top(); + case Down: + return (focused.top() < focusable.top() || focused.bottom() <= focusable.top()) + && focused.bottom() < focusable.bottom(); + case Left: + return (focused.right() > focused.right() || focused.left() >= focusable.right()) + && focused.left() > focusable.left(); + case Right: + return (focused.left() < focusable.left() || focused.right() <= focusable.left()) + && focused.right() < focusable.right(); + default: + return false; + } +} + +/** + * @brief жrect1rect2directionǷص + * @param direction + * @param rect1 һ + * @param rect2 ڶ + * @return + */ +bool FocusWindow::beamsOverlap(Direction direction, QRect rect1, QRect rect2) +{ + switch (direction) + { + case Left: + case Right: + return (rect2.bottom() >= rect1.top()) && (rect2.top() <= rect1.bottom()); + case Up: + case Down: + return (rect2.right() >= rect1.left()) && (rect2.left() <= rect1.right()); + } +} + +/** + * @brief жdestǷsourcedirection + * @param direction + * @param source + * @param dest + * @return + */ +bool FocusWindow::isToDirectionOf(Direction direction, QRect source, QRect dest) +{ + switch (direction) + { + case Up: + return source.top() >= dest.bottom(); + case Down: + return source.bottom() <= dest.top(); + case Left: + return source.left() >= dest.right(); + case Right: + return source.right() <= dest.left(); + } +} + +/** + * @brief ᷽ϵľ + * @param direction + * @param source + * @param dest + * @return + */ +int FocusWindow::majorAxisDistance(Direction direction, QRect source, QRect dest) +{ + return qMax(0, majorAxisDistanceRaw(direction, source, dest)); +} + +/** + * @brief ᷽ľ + * @param direction + * @param source + * @param dest + * @return + */ +int FocusWindow::majorAxisDistanceRaw(Direction direction, QRect source, QRect dest) +{ + switch (direction) + { + case Up: + return source.top() - dest.bottom(); + case Down: + return dest.top() - source.bottom(); + case Left: + return source.left() - dest.right(); + case Right: + return dest.left() - source.right(); + default: + return 0; + } +} + +/** + * @brief ᷽Ե + * @param dircetion + * @param source + * @param dest + * @return + */ +int FocusWindow::majorAxisDistanceToFarEdge(Direction dircetion, QRect source, QRect dest) +{ + return qMax(1, majorAxisDistanceToFarEdgeRaw(dircetion, source, dest)); +} + +/** + * @brief ᷽Ե + * @param dircetion + * @param source + * @param dest + * @return + */ +int FocusWindow::majorAxisDistanceToFarEdgeRaw(Direction dircetion, QRect source, QRect dest) +{ + switch (dircetion) + { + case Up: + return source.top() - dest.top(); + case Down: + return dest.bottom() - source.bottom(); + case Left: + return source.left() - dest.left(); + case Right: + return dest.right() - source.right(); + default: + return 0; + } +} + +/** + * @brief 㸱᷽ϵľ(ĵľ) + * @param direction + * @param source + * @param dest + * @return + */ +int FocusWindow::minorAxisDistance(Direction direction, QRect source, QRect dest) +{ + switch (direction) + { + case Up: + case Down: + return qAbs((source.left() + source.width() / 2) - (dest.left() + dest.width() / 2)); + case Left: + case Right: + return qAbs((source.top() + source.height() / 2) - (dest.top() + dest.height() / 2)); + default: + return 0; + } +} + +/** + * @brief ۺϾ, 㹫ʽ 13 * major^2 + minor ^ 2(13ΪȨ) + * @param direction + * @param majorAxisDistance ᷽ľ + * @param minorAxisDistance ᷽ľ + * @return + */ +int FocusWindow::getWeightDistanceFor(int majorAxisDistance, int minorAxisDistance) +{ + return 13 * majorAxisDistance * majorAxisDistance + minorAxisDistance * minorAxisDistance; +} + +/** + * @brief жrect1rect2sourcedircetionǷ + * @param dircetion + * @param source + * @param rect1 һ + * @param rect2 ڶ + * @return + */ +bool FocusWindow::beamBeats(Direction direction, QRect source, QRect rect1, QRect rect2) +{ + bool rect1InSrcBeam = beamsOverlap(direction, source, rect1); + bool rect2InSrcBeam = beamsOverlap(direction, source, rect2); + // rect2صrect1ص + if (rect2InSrcBeam || !rect1InSrcBeam) + { + return false; + } + // rect1صrect2ص + // + // rect2Ƿdirection + if (!isToDirectionOf(direction, source, rect2)) + { + return true; + } + + // ˮƽһ + if (direction == Left || direction == Right) + { + return true; + } + return (majorAxisDistance(direction, source, rect1) + < majorAxisDistanceToFarEdge(direction, source, rect2)); +} + +/** + * @brief жǷǸõĺѡ + * @param direction + * @param focused ǰ۽Ŀؼȫλ + * @param focusable ǰ۽Ŀؼȫλ + * @param curCandidate ǰѺѡȫλ + * @return + */ +bool FocusWindow::isBetterCandidate(Direction direction, QRect focused, QRect focusable, QRect curCandidate) +{ + // ǰĿؼ(direction) + if (!isCandidate(focused, focusable, direction)) + { + return false; + } + // ѡؼ + if (!isCandidate(focused, curCandidate, direction)) + return true; + // ǰĿؼԭĺѡؼжԱ + if (beamBeats(direction, focused, focusable, curCandidate)) + { + return true; + } + // ǰѡؼ뵱ǰؼжԱ + if (beamBeats(direction, focused, curCandidate, focusable)) + { + return false; + } + + // 㵱ǰؼ۽ؼۺϾǷСںѡؼ۽ؼۺϾ + return (getWeightDistanceFor(majorAxisDistance(direction, focused, focusable), + minorAxisDistance(direction, focused, focusable)) + < getWeightDistanceFor(majorAxisDistance(direction, focused, curCandidate), + minorAxisDistance(direction, focused, curCandidate))); +} + +/** + * @brief ָһؼ + * @param direction + * @return + */ +QWidget* FocusWindow::getNextFocusWidget(Direction direction) +{ + focusableList = getAllFocusabelWidget(); + QWidget* nextFocus = nullptr; + // ѡб + QList candidates; + + QWidget* focusedWidget = QApplication::focusWidget(); + if (!focusedWidget) + return nextFocus; + + // 㵱ǰ۽ؼȫλ + QRect focusedRect = QRect(focusedWidget->mapToGlobal(QPoint(0, 0)), focusedWidget->size()); + // 鹹һѡԭλ෴ƶһ + QRect betterCandidateRect = focusedRect; + switch (direction) + { + case Up: + betterCandidateRect = QRect(QPoint(focusedRect.x(), focusedRect.y() + 1), focusedRect.size()); + break; + case Down: + betterCandidateRect = QRect(QPoint(focusedRect.x(), focusedRect.y() - 1), focusedRect.size()); + break; + case Left: + betterCandidateRect = QRect(QPoint(focusedRect.x() + 1, focusedRect.y()), focusedRect.size()); + break; + case Right: + betterCandidateRect = QRect(QPoint(focusedRect.x() - 1, focusedRect.y()), focusedRect.size()); + break; + default: + break; + } + // ѭȽϿؼλãҵкѡؼ + for (int i = 0; i < focusableList.length(); i++) + { + QWidget* focusable = focusableList.at(i); + // ɾ۽ؼǵǰýĿؼתȽ + if (focusable == focusedWidget) + continue; + // ɾ۽ؼλתΪȫֵλ + QRect focusableRect = QRect(focusable->mapToGlobal(QPoint(0, 0)), focusable->size()); + // ǷΪźѡ + if (isBetterCandidate(direction, focusedRect, focusableRect, betterCandidateRect)) + { + betterCandidateRect = focusableRect; + nextFocus = focusable; + } + } + return nextFocus; +} + +/** + * @brief ۽һؼ + * @param direction + */ +void FocusWindow::focusNext(Direction direction) +{ + QWidget* nextFocus = getNextFocusWidget(direction); + if (nextFocus) + { + nextFocus->setFocus(); + } +} + diff --git a/FocusWindow.h b/FocusWindow.h new file mode 100755 index 0000000..03dce62 --- /dev/null +++ b/FocusWindow.h @@ -0,0 +1,53 @@ +/***************************************************************** + * @file FocusWindow.h + * @brief qt役ܹƶ࣬ڲʵȫandroidԴ뽹ƶ֡ + * ʹõʱҪ̳༴ɲдgetAllFocusabelWidget + * @author luoxiang + * @date May 23 2024 +****************************************************************/ +#include +#ifndef FOCUS_WINDOW_H +#define FOCUS_WINDOW_H + +namespace FW +{ + class FocusWindow + { + public: + // ƶ + enum Direction + { + Up, + Down, + Left, + Right + }; + void focusNext(Direction direction); + + protected: + // 麯дԻȡǰпԻýĿؼ + virtual QList getAllFocusabelWidget() = 0; + + private: + QList focusableList; + + private: + // 뽹ƶصĺ + QWidget* getNextFocusWidget(Direction direction); + bool isCandidate(QRect focused, QRect focusable, Direction direction); + bool isBetterCandidate(Direction direction, QRect focused, QRect focusable, QRect curCandidate); + bool beamBeats(Direction dircetion, QRect source, QRect rect1, QRect rect2); + bool beamsOverlap(Direction direction, QRect rect1, QRect rect2); + bool isToDirectionOf(Direction direction, QRect source, QRect dest); + int majorAxisDistance(Direction direction, QRect source, QRect dest); + int majorAxisDistanceRaw(Direction direction, QRect source, QRect dest); + int minorAxisDistance(Direction direction, QRect source, QRect dest); + int getWeightDistanceFor(int majorAxisDistance, int minorAxisDistance); + int majorAxisDistanceToFarEdge(Direction dircetion, QRect source, QRect dest); + int majorAxisDistanceToFarEdgeRaw(Direction dircetion, QRect source, QRect dest); + }; +} + +#endif // !FOCUS_WINDOW_H + + diff --git a/Log.cpp b/Log.cpp index a6e94f5..d03a6be 100644 --- a/Log.cpp +++ b/Log.cpp @@ -3,6 +3,7 @@ #include "spdlog/sinks/daily_file_sink.h" #include "spdlog/sinks/stdout_sinks.h" #include "spdlog/spdlog.h" +#include #include #include diff --git a/Menu.cpp b/Menu.cpp index 6e36ffc..3c4a4e0 100755 --- a/Menu.cpp +++ b/Menu.cpp @@ -1,37 +1,367 @@ -#include "Menu.h" -#include "Constant.h" -#include "ui_Menu.h" - -Menu::Menu(QWidget* parent) - : QWidget(parent) - , ui(new Ui::Menu) -{ - ui->setupUi(this); - ui->btnChannelA->setChecked(true); - - QPoint globalPos = parent->mapToGlobal(QPoint(0, 0)); - int x = globalPos.x() + (parent->width() - this->width()) / 2; - int y = globalPos.y() + (parent->height() - this->height()) / 2; - this->move(x, y); -} - -Menu::~Menu() -{ - delete ui; -} - -QString Menu::getCurChannel() -{ - if (ui->btnChannelA->isChecked()) - return Constant::MainChannel; - else - return Constant::SecondaryChannel; -} - -void Menu::setCurChannel(QString channel) -{ - if (channel == Constant::MainChannel) - ui->btnChannelA->setChecked(true); - else - ui->btnChannelB->setChecked(true); -} +#include "Menu.h" +#include "Constant.h" +#include "DatabaseManager.h" +#include "Log.h" +#include "ui_Menu.h" +#include +#include +#include +#include + +#define CONTENT_ROW 6 // 列表行 +#define CONTENT_COLUMN 6 // 列表列 +#define CONTENT_CELL_HEIGHT 56 // 单元格高度 + +Menu::Menu(QWidget* parent) + : QWidget(parent) + , FocusWindow() + , ui(new Ui::Menu) +{ + ui->setupUi(this); + setAttribute(Qt::WA_WState_WindowOpacitySet); + setWindowFlags(windowFlags() | Qt::FramelessWindowHint); + setWindowOpacity(0.5); + setFixedSize(800, 600); + ui->stackedWidget->setCurrentIndex(0); + ui->btnChannelSetting->setFocus(); + + QDesktopWidget* deskdop = QApplication::desktop(); + QWidget::move((deskdop->width() - this->width()) / 2, (deskdop->height() - this->height()) / 2); + + ui->cmbYear->setView(new QListView()); + ui->cmbMonth->setView(new QListView()); + ui->cmbDay->setView(new QListView()); + + // 设置ScrollArea为栅格布局 + QGridLayout* layout = new QGridLayout(ui->scrollArea); + ui->scrollAreaWidgetContents->setLayout(layout); + + db = DatabaseManager::getInstace(); + if (!db->open()) + return; + + connect(ui->cmbYear, &QComboBox::currentTextChanged, [=](QString text) { + renderComboBoxMonth(); + }); + connect(ui->cmbMonth, &QComboBox::currentTextChanged, [=](QString text) { + renderComboBoxDay(); + }); + // 默认设置当前操作选择主通道 + currentChannel = DatabaseManager::MainChannel; + QTimer::singleShot(200, [=] { + emit curChannelChanged(Constant::MainChannel); + }); +} + +Menu::~Menu() +{ + delete ui; + delete db; +} + +/** + * @brief 重写菜单show方法 + */ +void Menu::show() +{ + renderComboBoxYear(); + getContents(); + QWidget::show(); +} + +/** + * @brief 设置是否显示通道选择 + * @param visible + */ +void Menu::setChannelSelectVisible(bool visible) +{ + ui->widget_chnSelect->setVisible(visible); +} + +/** + * @brief 渲染年份 + */ +void Menu::renderComboBoxYear() +{ + QStringList years = db->getAllYears(currentChannel); + + ui->cmbYear->clear(); + for (auto& str : years) { + ui->cmbYear->addItem(str); + } + ui->cmbYear->setCurrentIndex(0); +} + +/** + * @brief 渲染月份 + */ +void Menu::renderComboBoxMonth() +{ + QString year = ui->cmbYear->currentText(); + if (year.isEmpty()) { + return; + } + QStringList months = db->getAllMonths(currentChannel, year); + ui->cmbMonth->clear(); + for (auto& str : months) { + ui->cmbMonth->addItem(str); + } + ui->cmbMonth->setCurrentIndex(0); +} + +/** + * @brief 渲染天数 + */ +void Menu::renderComboBoxDay() +{ + QString year = ui->cmbYear->currentText(); + QString month = ui->cmbMonth->currentText(); + if (year.isEmpty() || year.isEmpty()) { + return; + } + QStringList days = db->getAllDays(currentChannel, year, month); + ui->cmbDay->clear(); + for (auto& str : days) { + ui->cmbDay->addItem(str); + } + ui->cmbDay->setCurrentIndex(0); +} + +/** + * @brief 获取数据 + */ +void Menu::getContents() +{ + QVariantMap params; + params["channel"] = currentChannel; + params["year"] = ui->cmbYear->currentText(); + params["month"] = ui->cmbMonth->currentText(); + params["day"] = ui->cmbDay->currentText(); + contentList = db->get(params); + renderContents(); +} + +/** + * @brief 将视频名称列表渲染成一堆按钮放到ScrollArea中,6*6 + */ +void Menu::renderContents() +{ + QGridLayout* layout = qobject_cast(ui->scrollAreaWidgetContents->layout()); + + // 清除原来的控件防止内存泄漏 + QList widgets = ui->scrollAreaWidgetContents->findChildren(); + for (QWidget* w : widgets) { + delete w; + } + // 重新生成若干个按钮 + for (int i = 0; i < contentList.length(); i++) { + // 文件名格式: yyyyMMddhhmmss.mp4,只将时分秒显示到界面上 + DatabaseManager::File file = contentList.at(i); + QPushButton* btn = new QPushButton(file.time, ui->scrollArea); + btn->setProperty("name", file.filename); + btn->setMinimumHeight(CONTENT_CELL_HEIGHT); + btn->setStyleSheet("QPushButton{border-radius: 5px;}"); + btn->setCheckable(true); + btn->setAutoExclusive(true); + btn->setObjectName(QString("btn_video_%1").arg(i)); + int row = i / CONTENT_COLUMN; + int column = i % CONTENT_COLUMN; + layout->addWidget(btn, row, column); + } + + // 少于36个用空的widget占位置 + if (contentList.length() < CONTENT_ROW * CONTENT_COLUMN) { + int lastRow = (contentList.length() - 1) / CONTENT_COLUMN; + int lastColum = (contentList.length() - 1) % CONTENT_COLUMN; + + for (int i = lastRow; i < CONTENT_ROW; i++) { + for (int j = 0; j < CONTENT_COLUMN; j++) { + if (i == lastRow && j <= lastColum) { + continue; + } + QWidget* widget = new QWidget(ui->scrollArea); + widget->setMinimumHeight(CONTENT_CELL_HEIGHT); + layout->addWidget(widget, i, j); + } + } + } +} + +/** + * @brief 移动 + * @param direction + */ +void Menu::move(Direction direction) +{ + QWidget* focusWidget = QApplication::focusWidget(); + // 当前焦点是否在展开后的ComboBox上 + bool isCmbPopup = (strcmp(focusWidget->metaObject()->className(), "QListView") == 0); + // 下拉框展开,则只进行上下选择 + if (isCmbPopup) { + // 随便赋值一个不影响功能的按键 + Qt::Key key = Qt::Key_Right; + if (direction == Up) { + key == Qt::Key_Up; + } else if (direction == Down) { + key == Qt::Key_Down; + } + QKeyEvent pressEvent(QEvent::KeyPress, key, Qt::NoModifier); + QApplication::sendEvent(focusWidget, &pressEvent); + QKeyEvent releaseEvent(QEvent::KeyRelease, key, Qt::NoModifier); + QApplication::sendEvent(focusWidget, &releaseEvent); + } else { + focusNext(direction); + focusWidget = QApplication::focusWidget(); + int drawedHeight = focusWidget->visibleRegion().boundingRect().height(); + int height = focusWidget->height(); + // 绘制的高度小于实际高度,则表示控件显示不完全 + if (drawedHeight < height) { + ui->scrollArea->ensureWidgetVisible(focusWidget); + } + if (focusWidget == ui->btnChannelSetting) { + ui->btnChannelSetting->setChecked(true); + ui->stackedWidget->setCurrentIndex(0); + } else if (focusWidget == ui->btnPlayback) { + ui->btnPlayback->setChecked(true); + ui->stackedWidget->setCurrentIndex(1); + } + } +} + +/** + * @brief 确认 + */ +void Menu::confirm() +{ + int index = ui->stackedWidget->currentIndex(); + QWidget* focusWidget = QApplication::focusWidget(); + switch (index) { + // 通道设置界面 + case 0: { + QPushButton* btn = qobject_cast(focusWidget); + btn->setChecked(true); + if (btn == ui->btn_hdmi_1) { + emit btnHdmi1Checked(); + } else if (btn == ui->btn_hdmi_2) { + emit btnHdmi2Checked(); + } else if (btn == ui->btn_vga_1) { + emit btnVga1Checked(); + } else if (btn == ui->btn_vga_2) { + emit btnVga2Checked(); + } + break; + } + // 回放界面 + case 1: { + // 通道选择时按下确认 + if (focusWidget == ui->btn_channel_1 || focusWidget == ui->btn_channel_2) { + QPushButton* btn = qobject_cast(focusWidget); + btn->setChecked(true); + if (btn == ui->btn_channel_1) { + currentChannel = DatabaseManager::MainChannel; + emit curChannelChanged(Constant::MainChannel); + } else { + currentChannel = DatabaseManager::SecondaryChannel; + emit curChannelChanged(Constant::SecondaryChannel); + } + renderComboBoxYear(); + } + // 下拉框未展开时按下确认 + else if (strcmp(focusWidget->metaObject()->className(), "QComboBox") == 0) { + QComboBox* cmb = qobject_cast(focusWidget); + cmb->showPopup(); + } + // 下拉框展开时按下确认 + else if (strcmp(focusWidget->metaObject()->className(), "QListView") == 0) { + // QComboBox的层级 + // QComboBox + // |- QComboBoxPrivateContainer(Popup widget) + // |- QListView(or QListWidget) + // |-QViewPort(Child of QListView) + QComboBox* cmb = static_cast(focusWidget->parent()->parent()); + if (cmb) { + cmb->hidePopup(); + } + } + // 选择视频时按下确认 + else if (focusWidget->objectName().contains("btn_video")) { + QPushButton* btn = qobject_cast(focusWidget); + btn->setChecked(true); + QString filename = btn->property("name").toString(); + emit btnVideoClicked(filename); + currentFilename = filename; + } + // 确认按钮按下确认 + else if (focusWidget->objectName() == "btn_done") { + getContents(); + } + break; + } + default: + break; + } +} + +/** + * @brief 点击查找当前列表中的上一个视频和下一个视频 + */ +void Menu::clickVideo(QString type) +{ + getContents(); + int index = -1; + for (int i = 0; i < contentList.length(); i++) { + DatabaseManager::File file = contentList[i]; + if (file.filename == currentFilename) { + index = i; + break; + } + } + QString filename; + // 上一个视频 + if (type == "previous") { + if (index == 0) { + emit btnVideoClicked("first"); + return; + } + filename = contentList[index - 1].filename; + } + // 下一个视频 + else { + if (index == contentList.length() - 1) { + emit btnVideoClicked("last"); + return; + } + filename = contentList[index + 1].filename; + } + filename = contentList[index + 1].filename; + emit btnVideoClicked(filename); + QString h = filename.mid(8, 2); + QString m = filename.mid(10, 2); + QString s = filename.mid(12, 2); + QString str = QString("%1:%2:%3").arg(h).arg(m).arg(s); + // 选中要播放的按钮 + QList btns = ui->scrollArea->findChildren(); + for (QPushButton* btn : btns) { + if (btn->text() == str) { + btn->setChecked(true); + } + } +} + +/** + * @brief 遍历当前窗体可以获取焦点的所有控件 + */ +QList Menu::getAllFocusabelWidget() +{ + QList list = findChildren(); + QList result; + for (int i = 0; i < list.length(); i++) { + QWidget* w = list.at(i); + if (w->focusPolicy() == Qt::NoFocus || !w->isVisible()) + continue; + else { + result.push_back(w); + } + } + return result; +} diff --git a/Menu.h b/Menu.h index 3e13ce2..82be039 100755 --- a/Menu.h +++ b/Menu.h @@ -1,23 +1,54 @@ -#ifndef MENU_H -#define MENU_H - -#include - -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 +#ifndef WIDGET_H +#define WIDGET_H + +#include "DatabaseManager.h" +#include "FocusWindow.h" +#include + +QT_BEGIN_NAMESPACE +namespace Ui { +class Menu; +} +QT_END_NAMESPACE + +class Menu : public QWidget, public FW::FocusWindow { + Q_OBJECT +public: + Menu(QWidget* parent = nullptr); + ~Menu(); + void show(); + void setChannelSelectVisible(bool visible); + +public slots: + void move(Direction direction); + void confirm(); + void clickVideo(QString type); + +signals: + void btnHdmi1Checked(); + void btnHdmi2Checked(); + void btnVga1Checked(); + void btnVga2Checked(); + void btnVideoClicked(QString name); + void curChannelChanged(QString channel); + +protected: + // 重写父类的方法,以获取当前界面的所有可以获取焦点的控件 + QList getAllFocusabelWidget() override; + +private: + Ui::Menu* ui; + // 视频名数组列表 + QList contentList; + DatabaseManager* db; + DatabaseManager::Channel currentChannel; + QString currentFilename; + +private: + void getContents(); + void renderContents(); + void renderComboBoxYear(); + void renderComboBoxMonth(); + void renderComboBoxDay(); +}; +#endif // WIDGET_H diff --git a/Menu.ui b/Menu.ui index 4cd7132..5cef797 100755 --- a/Menu.ui +++ b/Menu.ui @@ -1,101 +1,909 @@ - - - Menu - - - - 0 - 0 - 130 - 100 - - - - - 12 - - - - Form - - - QPushButton { - border: none; - background: rgba(0, 0, 0, 0.8); - color: #ffffff; -} - -QPushButton::checked{ - /*color: #1668dc;*/ -color:#3c89e8; -} - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 50 - - - - - 12 - - - - 视频源1 - - - true - - - true - - - - - - - - 0 - 50 - - - - - 12 - - - - 视频源2 - - - true - - - true - - - - - - - - + + + Menu + + + + 0 + 0 + 800 + 600 + + + + Widget + + + QWidget#Menu{ + background: rgba(44,44,46, 0.5); +} + +QWidget { +color: rgba(255,255,255,0.5); +font: 10pt "微软雅黑"; +} + +QPushButton { + border: none; + outline: none; +} + +QWidget:focus{ + border: 3px solid rgba(15,216,82, 0.5); +} + +QPushButton:checked { + background: rgba(94,92,230, 0.5); +} + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame#navBar{ +margin-bottom:2px; +border-bottom: 2px solid rgba(227,227,227,0.5); +} + +QPushButton { + background: rgba(100, 100, 100, 0.5); + font: 10pt "微软雅黑"; + min-height: 40px; + max-height:40px; + min-width: 100px; +} + +QPushButton:checked { + background: rgba(94,92,230,0.5); +} + + + + 0 + + + 0 + + + 10 + + + 0 + + + 10 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 100 + 40 + + + + + 16777215 + 40 + + + + + 微软雅黑 + 10 + 50 + false + false + + + + + + + 通道设置 + + + true + + + true + + + true + + + + + + + + 100 + 40 + + + + + 16777215 + 40 + + + + + 微软雅黑 + 10 + 50 + false + false + + + + + + + 回放 + + + true + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + + + + 1 + + + + QPushButton { +background: rgba(100,100,100, 0.5); + font: 10pt "微软雅黑"; + min-height: 60px; + min-width: 100px; + border-radius: 5px; +} + +QPushButton:checked { + background: rgba(94,92,230, 0.5); +} + +QFrame#line +{ + color:rgba(51,51,51,0.5); +} + + + + 20 + + + 10 + + + 10 + + + 10 + + + 10 + + + + + + 0 + 40 + + + + + 20 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 微软雅黑 + 10 + 50 + false + false + + + + Qt::RightToLeft + + + 通道1: + + + + + + + + 100 + 60 + + + + + 微软雅黑 + 10 + 50 + false + false + + + + HDMI + + + true + + + true + + + true + + + + + + + + 100 + 60 + + + + + 微软雅黑 + 10 + 50 + false + false + + + + VGA + + + true + + + true + + + + + + + + + + + 0 + 40 + + + + + 20 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 微软雅黑 + 10 + 50 + false + false + + + + Qt::RightToLeft + + + 通道2: + + + + + + + + 100 + 60 + + + + + 微软雅黑 + 10 + 50 + false + false + + + + HDMI + + + true + + + true + + + true + + + + + + + + 100 + 60 + + + + + 微软雅黑 + 10 + 50 + false + false + + + + VGA + + + true + + + true + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + QPushButton { + background: rgba(100,100,100,50%); + font: 10pt "微软雅黑"; +} + +QWidget:focus{ + border: 3px solid rgba(15,216,82,0.5); +} + +QPushButton:checked { + background: rgba(94,92,230,0.5); +} + + +QComboBox{ + background:rgba(255,255,255,0.5); + font: 10pt "微软雅黑"; + padding: 1px 15px 1px 3px; + border:1px solid rgba(228,228,228,0.5); + border-radius:5px 5px 0px 0px; +} + QComboBox::drop-down { + subcontrol-origin: padding; + subcontrol-position: top right; + width: 15px; + border:none; + } + QComboBox QAbstractItemView{ + background:rgba(255,255,255,0.5); + border:1px solid rgba(228,228,228,0.5); + border-radius:0px 0px 5px 5px; + font-size:14px; + outline: 0px; + } +QComboBox QAbstractItemView::item{ + height:36px; + color:#666666; + padding-left:9px; + background-color:rgba(255,255,255,0.5); +} +QComboBox QAbstractItemView::item:hover{ + background-color:rgba(64,156,225,0.5); + color:#ffffff; +} +QComboBox QAbstractItemView::item:selected{ + background-color:rgba(64,156,225,0.5); + color:#ffffff; +} +QComboBox:on { + padding-top: 3px; + padding-left: 4px; + } + QComboBox::down-arrow:on { + top: 1px; + left: 1px; + } + +QScrollBar:vertical{ +width:3px; +background:transparent; +margin:0px,0px,0px,0px; +padding-top:0px; +padding-bottom:0px; +} +QScrollBar::handle:vertical +{ +width:3px; +background:rgba(69,178,255,70%); +border-radius:4px; +min-height:20 +} +QScrollBar::handle:vertical:hover{ +width:3px; +background:rgba(69,178,255,70%); +border-radius:4px; +min-height:20; +} +QScrollBar::add-line:vertical +{ +height:0px;width:4px; +subcontrol-position:bottom; +} +QScrollBar::sub-line:vertical +{ +height:0px;width:4px; +subcontrol-position:top; +} +QScrollBar::add-page:vertical,QScrollBar::sub-page:vertical +{ +background:rgba(232,234,239,0.5); +border-radius:4px; +} + + + + 6 + + + 10 + + + 10 + + + 10 + + + 10 + + + + + 0 + + + 0 + + + + + + 7 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + 通道: + + + + + + + + 100 + 40 + + + + + 16777215 + 16777215 + + + + 通道1 + + + true + + + true + + + true + + + + + + + + 100 + 40 + + + + + 16777215 + 16777215 + + + + + + + 通道2 + + + true + + + false + + + false + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 7 + + + + + 日期: + + + + + + + + 100 + 40 + + + + + 100 + 16777215 + + + + color: #000000; + + + + + + + + 100 + 40 + + + + + 100 + 16777215 + + + + color: #000000; + + + + + + + + 100 + 40 + + + + + 100 + 16777215 + + + + color: #000000; + + + + + + + + 100 + 40 + + + + + 16777215 + 16777215 + + + + 确认 + + + true + + + true + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::NoFocus + + + QScrollArea { + background-color:rgba(25, 25, 25, 0.5); +} + +QScrollBar:vertical{ +width:8px; +background:transparent; +margin:0px,0px,0px,0px; +padding-top:0px; +padding-bottom:0px; +} +QScrollBar::handle:vertical +{ +width:8px; +background:rgba(69,178,255,70%); +border-radius:4px; +min-height:20 +} +QScrollBar::handle:vertical:hover{ +width:8px; +background:rgba(69,178,255,70%); +border-radius:4px; +min-height:20; +} +QScrollBar::add-line:vertical +{ +height:0px;width:8px; +subcontrol-position:bottom; +} +QScrollBar::sub-line:vertical +{ +height:0px;width:8px; +subcontrol-position:top; +} +QScrollBar::add-page:vertical,QScrollBar::sub-page:vertical +{ +background:rgba(232,234,239,0.5); +border-radius:4px; +} + + + true + + + + + 0 + 0 + 776 + 416 + + + + QWidget#scrollAreaWidgetContents { + background: rgba(25, 25, 25, 0.5); + border: none; +} + + + + + + + + + + + + + + + + + diff --git a/ProgressBar.cpp b/ProgressBar.cpp index 6109592..31a083c 100755 --- a/ProgressBar.cpp +++ b/ProgressBar.cpp @@ -69,7 +69,7 @@ void ProgressBar::setDuration(int dur) { this->duration = dur; ui->horizontalSlider->setMaximum(dur); - QString time = this->secToString(dur); + QString time = this->msecToString(dur); this->durationStr = time; ui->label->setText(QString("00:00:00/%1").arg(time)); } @@ -77,7 +77,7 @@ void ProgressBar::setDuration(int dur) void ProgressBar::setCurrent(int cur) { ui->horizontalSlider->setValue(cur); - QString time = this->secToString(cur); + QString time = this->msecToString(cur); ui->label->setText(QString("%1/%2").arg(time).arg(durationStr)); } diff --git a/ProgressBar.h b/ProgressBar.h index 9b2c47b..70991b0 100755 --- a/ProgressBar.h +++ b/ProgressBar.h @@ -1,6 +1,7 @@ #ifndef PROGRESSBAR_H #define PROGRESSBAR_H +#include #include #include #include @@ -23,7 +24,7 @@ public: void setDuration(int dur); void setCurrent(int cur); void setState(PlayState state); - inline QString secToString(int msc); + inline QString msecToString(int msc); private slots: void onTimeout(); @@ -40,9 +41,10 @@ private: QString sliderMinStyles; }; -inline QString ProgressBar::secToString(int msc) +inline QString ProgressBar::msecToString(int msc) { - QString formatStr = QTime(0, 0, 0).addSecs(msc).toString(QString::fromUtf8("hh:mm:ss")); + QString formatStr = QTime(0, 0, 0).addMSecs(msc).toString(QString::fromUtf8("hh:mm:ss")); + qDebug() << msc; return formatStr; } diff --git a/ProgressBar.ui b/ProgressBar.ui index b0d0539..abc7643 100755 --- a/ProgressBar.ui +++ b/ProgressBar.ui @@ -13,6 +13,9 @@ Form + + + 0 @@ -57,6 +60,9 @@ + + 999999999 + Qt::Horizontal diff --git a/RecordControlApplication.pro b/RecordControlApplication.pro index 9c81698..fecb16e 100755 --- a/RecordControlApplication.pro +++ b/RecordControlApplication.pro @@ -4,7 +4,7 @@ # #------------------------------------------------- -QT += core gui network serialport +QT += core gui network serialport sql greaterThan(QT_MAJOR_VERSION, 4): QT += widgets @@ -45,7 +45,8 @@ SOURCES += \ TcpRequestHandler.cpp \ TcpResponse.cpp \ SerialPortTool.cpp \ - ChannelSetting.cpp + FocusWindow.cpp \ + DatabaseManager.cpp HEADERS += \ Widget.h \ @@ -62,13 +63,13 @@ HEADERS += \ TcpRequestHandler.h \ TcpResponse.h \ SerialPortTool.h \ - ChannelSetting.h + FocusWindow.h \ + DatabaseManager.h FORMS += \ Menu.ui \ ProgressBar.ui \ - Widget.ui \ - ChannelSetting.ui + Widget.ui RESOURCES += \ res.qrc diff --git a/SerialPortTool.cpp b/SerialPortTool.cpp index 797e89e..0abea12 100644 --- a/SerialPortTool.cpp +++ b/SerialPortTool.cpp @@ -106,10 +106,10 @@ void SerialPortTool::onTimeout() timer->stop(); bool ret = serialPort->open(QIODevice::ReadWrite); if (!ret) { - Log::info("reopen serial port: /dev/ttyAMA2 success"); + Log::error("reopen serial port: /dev/ttyAMA2 failed, ready to retry..."); timer->start(); } else { - Log::error("reopen serial port: /dev/ttyAMA2 failed, ready to retry..."); + Log::info("reopen serial port: /dev/ttyAMA2 success"); } } @@ -126,7 +126,7 @@ void SerialPortTool::onReayRead() // 当发送数据频率很大时,可以使用另外的缓冲区来存储读到的数据,然后读取缓冲区的数据进行指令的解析 // 将缓冲区的数据全部读取出来 QByteArray data = serialPort->readAll(); - qDebug() << "data:" << data; + qDebug() << "receive command:" << data; // 对接受的数据进行协议解析 // 查找包头和包尾 @@ -155,31 +155,32 @@ void SerialPortTool::onReayRead() switch (frame[4]) { case 1: - emit btnPlaybackClicked(); + // 菜单 + emit btnMenuClicked(); break; case 2: - emit btnPreviousClicked(); + // 上 + emit btnUpClicked(); break; case 3: - emit btnPauseClicked(); + // 下 + emit btnDownClicked(); break; case 4: - emit btnReturnClicked(); + // 左 + emit btnLeftClicked(); break; case 5: - emit btnBackClicked(); + // 右 + emit btnRightClicked(); break; case 6: - emit btnNextClicked(); + // 确认 + emit btnConfirmClicked(); break; case 7: - emit btnForwardClicked(); - break; - case 8: - emit btnDoneClicked(); - break; - case 9: - emit btnChannelSettingClicked(); + // 返回 + emit btnReturnClicked(); break; default: break; @@ -300,24 +301,28 @@ void SerialPortTool::onSecondaryChannelOff() void SerialPortTool::onMainChannelHDMI() { + qDebug() << "main channel hdmi:" << cmdMap.value(MainChannelHDMI); serialPort->write(cmdMap.value(MainChannelHDMI)); serialPort->flush(); } void SerialPortTool::onMainChannelVGA() { + qDebug() << "main channel vga:" << cmdMap.value(MainChannelVGA); serialPort->write(cmdMap.value(MainChannelVGA)); serialPort->flush(); } void SerialPortTool::onSecondaryChannelHDMI() { + qDebug() << "secondary channel hdmi:" << cmdMap.value(SecondaryChannelHDMI); serialPort->write(cmdMap.value(SecondaryChannelHDMI)); serialPort->flush(); } void SerialPortTool::onSecondaryChannelVGA() { + qDebug() << "secondary channel vga:" << cmdMap.value(SecondaryChanneVGA); serialPort->write(cmdMap.value(SecondaryChanneVGA)); serialPort->flush(); } diff --git a/SerialPortTool.cpp.bak b/SerialPortTool.cpp.bak new file mode 100644 index 0000000..5a10437 --- /dev/null +++ b/SerialPortTool.cpp.bak @@ -0,0 +1,327 @@ +#include "SerialPortTool.h" +#include "Log.h" + +SerialPortTool::SerialPortTool(QObject* parent) + : QObject(parent) +{ + + serialPort = new QSerialPort(); + connect(serialPort, SIGNAL(readyRead()), this, SLOT(onReayRead())); + connect(serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(onSerialError(QSerialPort::SerialPortError))); + + timer = new QTimer(); + timer->setInterval(3000); + timer->stop(); + connect(timer, SIGNAL(timeout()), this, SLOT(onTimeout())); + + auto makeCommand = [=](int type, QString data, QString enable) { + // 命令类型 + QString t; + if (type == 2) + t = "02"; // 指示灯 + else + t = "03"; // 使能 + return QByteArray::fromHex(QString("A2 B2 08 %1 %2 %3 C2 D2") + .arg(t) + .arg(data) + .arg(enable) + .toLatin1()); + }; + + cmdMap[Reset] = makeCommand(2, "00", "00"); + cmdMap[PlaybackStart] = makeCommand(2, "01", "01"); + cmdMap[PlaybackEnd] = makeCommand(2, "01", "00"); + cmdMap[RecordStart] = makeCommand(2, "02", "01"); + cmdMap[RecordEnd] = makeCommand(2, "02", "00"); + cmdMap[PlayPause] = makeCommand(2, "03", "01"); + cmdMap[PlayResume] = makeCommand(2, "03", "00"); + cmdMap[DiskWillFull] = makeCommand(2, "04", "01"); + cmdMap[DiskNotFull] = makeCommand(2, "04", "00"); + cmdMap[PowerOn] = makeCommand(2, "05", "01"); + cmdMap[PowerOff] = makeCommand(2, "05", "00"); + cmdMap[Forward] = makeCommand(2, "08", "01"); + cmdMap[LoopOn] = makeCommand(2, "07", "01"); + cmdMap[LoopOff] = makeCommand(2, "07", "00"); + cmdMap[Back] = makeCommand(2, "06", "01"); + cmdMap[MainChannelOn] = makeCommand(2, "09", "01"); + cmdMap[MainChannelOff] = makeCommand(2, "09", "00"); + cmdMap[SecondaryChannelOn] = makeCommand(2, "0A", "01"); + cmdMap[SecondaryChannelOff] = makeCommand(2, "0A", "00"); + cmdMap[MainChannelHDMI] = makeCommand(3, "03", "00"); + cmdMap[MainChannelVGA] = makeCommand(3, "03", "01"); + cmdMap[SecondaryChannelHDMI] = makeCommand(3, "04", "00"); + cmdMap[SecondaryChanneVGA] = makeCommand(3, "04", "01"); +} + +SerialPortTool::~SerialPortTool() +{ + serialPort->close(); + delete serialPort; +} + +/** + * @brief 打开串口 + */ +void SerialPortTool::open() +{ + 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)) { + Log::info("open serial port: /dev/ttyAMA2 success"); + } else { + Log::error("open serial port: /dev/ttyAMA2 failed, ready to retry..."); + timer->start(); + } +} + +/** + * @brief 关闭串口 + */ +void SerialPortTool::close() +{ + serialPort->close(); +} + +/** + * @brief 串口报错槽函数 + */ +void SerialPortTool::onSerialError(QSerialPort::SerialPortError error) +{ + if (error == QSerialPort::ResourceError) { + Log::error("serial port break, try to reopen"); + serialPort->close(); + timer->start(); + } +} + +/** + * @brief 重连串口 + */ +void SerialPortTool::onTimeout() +{ + timer->stop(); + bool ret = serialPort->open(QIODevice::ReadWrite); + if (!ret) { + Log::error("reopen serial port: /dev/ttyAMA2 failed, ready to retry..."); + timer->start(); + } else { + Log::info("reopen serial port: /dev/ttyAMA2 success"); + } +} + +/** + * @brief 读取并处理收到的串口指令 + */ +void SerialPortTool::onReayRead() +{ + // 当缓冲区中有大于8个字节的数据时才处理 + if (serialPort->bytesAvailable() < 8) + return; + + // 由于本项目串口的数据频率小,可以将全部读到的数据当做一条指令来解析 + // 当发送数据频率很大时,可以使用另外的缓冲区来存储读到的数据,然后读取缓冲区的数据进行指令的解析 + // 将缓冲区的数据全部读取出来 + QByteArray data = serialPort->readAll(); + qDebug() << "receive command:" << data; + // 对接受的数据进行协议解析 + + // 查找包头和包尾 + QByteArray head = QByteArray::fromHex("A2 B2"); + QByteArray tail = QByteArray::fromHex("C2 D2"); + //... 0xA2 0xB2 0x08 0x01 0x01 0x00 0xC2 0xD2 ... + // || || + // indexHead indexTail + int indexHead = data.indexOf(head); + int indexTail = data.indexOf(tail); + // 没有查找到包头和包尾 + if (indexHead == -1 || indexTail == -1) + return; + + // 从接收的数据中截取出一帧数据 + QByteArray frame = data.mid(indexHead, indexTail + 2 - indexHead); + if (frame.length() <= 4) + return; + // 对一帧数据进行校验 + // 长度校验不通过 + if (frame[2] != frame.length()) + return; + // 命令类型 + if (data.at(3) != 0x01) + return; + + switch (frame[4]) { + case 1: + emit btnPlaybackClicked(); + break; + case 2: + emit btnPreviousClicked(); + break; + case 3: + emit btnPauseClicked(); + break; + case 4: + emit btnReturnClicked(); + break; + case 5: + emit btnBackClicked(); + break; + case 6: + emit btnNextClicked(); + break; + case 7: + emit btnForwardClicked(); + break; + case 8: + emit btnDoneClicked(); + break; + case 9: + emit btnChannelSettingClicked(); + break; + default: + break; + } +} + +void SerialPortTool::onReset() +{ + serialPort->write(cmdMap.value(Reset)); + serialPort->flush(); +} + +void SerialPortTool::onPlaybackStart() +{ + serialPort->write(cmdMap.value(PlaybackStart)); + serialPort->flush(); +} + +void SerialPortTool::onPlaybackEnd() +{ + serialPort->write(cmdMap.value(PlaybackEnd)); + serialPort->flush(); +} + +void SerialPortTool::onRecordStart() +{ + serialPort->write(cmdMap.value(RecordStart)); + serialPort->flush(); +} + +void SerialPortTool::onRecordEnd() +{ + serialPort->write(cmdMap.value(RecordEnd)); + serialPort->flush(); +} + +void SerialPortTool::onPlayPause() +{ + serialPort->write(cmdMap.value(PlayPause)); + serialPort->flush(); +} + +void SerialPortTool::onPlayResume() +{ + serialPort->write(cmdMap.value(PlayResume)); + serialPort->flush(); +} + +void SerialPortTool::onDiskWillFull() +{ + serialPort->write(cmdMap.value(DiskWillFull)); + serialPort->flush(); +} + +void SerialPortTool::onDiskNotFull() +{ + serialPort->write(cmdMap.value(DiskNotFull)); + serialPort->flush(); +} +void SerialPortTool::onPowerOn() +{ + serialPort->write(cmdMap.value(PowerOn)); + serialPort->flush(); +} +void SerialPortTool::onPowerOff() +{ + serialPort->write(cmdMap.value(PowerOff)); + serialPort->flush(); +} + +void SerialPortTool::onFoward() +{ + serialPort->write(cmdMap.value(Forward)); + serialPort->flush(); +} + +void SerialPortTool::onBack() +{ + serialPort->write(cmdMap.value(Back)); + serialPort->flush(); +} + +void SerialPortTool::onLoopOn() +{ + serialPort->write(cmdMap.value(LoopOn)); + serialPort->flush(); +} + +void SerialPortTool::onLoopOff() +{ + serialPort->write(cmdMap.value(LoopOff)); + serialPort->flush(); +} + +void SerialPortTool::onMainChannelOn() +{ + serialPort->write(cmdMap.value(MainChannelOn)); + serialPort->flush(); +} + +void SerialPortTool::onMainChannelOff() +{ + serialPort->write(cmdMap.value(MainChannelOff)); + serialPort->flush(); +} + +void SerialPortTool::onSecondaryChannelOn() +{ + serialPort->write(cmdMap.value(SecondaryChannelOn)); + serialPort->flush(); +} + +void SerialPortTool::onSecondaryChannelOff() +{ + serialPort->write(cmdMap.value(SecondaryChannelOff)); + serialPort->flush(); +} + +void SerialPortTool::onMainChannelHDMI() +{ + qDebug() << "main channel hdmi:" << cmdMap.value(MainChannelHDMI); + serialPort->write(cmdMap.value(MainChannelHDMI)); + serialPort->flush(); +} + +void SerialPortTool::onMainChannelVGA() +{ + qDebug() << "main channel vga:" << cmdMap.value(MainChannelVGA); + serialPort->write(cmdMap.value(MainChannelVGA)); + serialPort->flush(); +} + +void SerialPortTool::onSecondaryChannelHDMI() +{ + qDebug() << "secondary channel hdmi:" << cmdMap.value(SecondaryChannelHDMI); + serialPort->write(cmdMap.value(SecondaryChannelHDMI)); + serialPort->flush(); +} + +void SerialPortTool::onSecondaryChannelVGA() +{ + qDebug() << "secondary channel vga:" << cmdMap.value(SecondaryChanneVGA); + serialPort->write(cmdMap.value(SecondaryChanneVGA)); + serialPort->flush(); +} diff --git a/SerialPortTool.h b/SerialPortTool.h index 870a4af..9b73989 100644 --- a/SerialPortTool.h +++ b/SerialPortTool.h @@ -42,16 +42,15 @@ private slots: void onSerialError(QSerialPort::SerialPortError error); void onTimeout(); void onReayRead(); + signals: - void btnPlaybackClicked(); - void btnPreviousClicked(); - void btnPauseClicked(); + void btnMenuClicked(); + void btnUpClicked(); + void btnDownClicked(); + void btnLeftClicked(); + void btnRightClicked(); + void btnConfirmClicked(); void btnReturnClicked(); - void btnBackClicked(); - void btnNextClicked(); - void btnForwardClicked(); - void btnDoneClicked(); - void btnChannelSettingClicked(); private: enum CommandType { diff --git a/SerialPortTool.h.bak b/SerialPortTool.h.bak new file mode 100644 index 0000000..870a4af --- /dev/null +++ b/SerialPortTool.h.bak @@ -0,0 +1,88 @@ +#ifndef SERIALPORT_H +#define SERIALPORT_H +#include +#include +#include +#include + +class SerialPortTool : public QObject { + Q_OBJECT + +public: + explicit SerialPortTool(QObject* parent = 0); + ~SerialPortTool(); + void open(); + void close(); +public slots: + void onReset(); + void onPlaybackStart(); + void onPlaybackEnd(); + void onRecordStart(); + void onRecordEnd(); + void onPlayPause(); + void onPlayResume(); + void onDiskWillFull(); + void onDiskNotFull(); + void onPowerOn(); + void onPowerOff(); + void onFoward(); + void onBack(); + void onLoopOn(); + void onLoopOff(); + void onMainChannelOn(); + void onMainChannelOff(); + void onSecondaryChannelOn(); + void onSecondaryChannelOff(); + void onMainChannelHDMI(); + void onMainChannelVGA(); + void onSecondaryChannelHDMI(); + void onSecondaryChannelVGA(); + +private slots: + void onSerialError(QSerialPort::SerialPortError error); + void onTimeout(); + void onReayRead(); +signals: + void btnPlaybackClicked(); + void btnPreviousClicked(); + void btnPauseClicked(); + void btnReturnClicked(); + void btnBackClicked(); + void btnNextClicked(); + void btnForwardClicked(); + void btnDoneClicked(); + void btnChannelSettingClicked(); + +private: + enum CommandType { + Reset, + PlaybackStart, + PlaybackEnd, + RecordStart, + RecordEnd, + PlayPause, + PlayResume, + DiskWillFull, + DiskNotFull, + PowerOn, + PowerOff, + Forward, + Back, + LoopOn, + LoopOff, + MainChannelOn, + MainChannelOff, + SecondaryChannelOff, + SecondaryChannelOn, + MainChannelHDMI, + MainChannelVGA, + SecondaryChannelHDMI, + SecondaryChanneVGA + }; + + QSerialPort* serialPort; // 串口 + QMap cmdMap; // 指令列表 + QTimer* timer; +}; + +#endif // SERIALPORT_H diff --git a/TcpRequestHandler.cpp b/TcpRequestHandler.cpp index cc3db73..b297c01 100755 --- a/TcpRequestHandler.cpp +++ b/TcpRequestHandler.cpp @@ -1,6 +1,7 @@ #include "TcpRequestHandler.h" #include "Channel.h" #include "Constant.h" +#include "DatabaseManager.h" #include "Json.h" #include "Log.h" #include "Tool.h" @@ -8,6 +9,7 @@ #include extern const QList channelList; +extern DatabaseManager* db; TcpRequestHandler::TcpRequestHandler(QObject* parent) : QObject(parent) @@ -180,21 +182,21 @@ void TcpRequestHandler::getVideoEnc(TcpRequest* request, TcpResponse* reponse) void TcpRequestHandler::getFileList(TcpRequest* request, TcpResponse* reponse) { QVariantMap params = request->getBodyParams(); - if (params.isEmpty() || !params.contains("interface")) { - Log::error("getFileList params error, missing param \"interface\""); - reponse->error("缺少接口参数"); + QString chn = params.value("interface").toString(); + if (chn != Constant::MainChannel || chn != Constant::SecondaryChannel) { + Log::error("getFileList params error, error param \"interface\""); + reponse->error("接口参数错误"); return; } - + DatabaseManager::Channel channel = chn == Constant::MainChannel ? DatabaseManager::MainChannel + : DatabaseManager::SecondaryChannel; QString interface = params.value("interface").toString(); - QString videoPath = QString("%1/%2").arg(Constant::VideoPath).arg(interface); - - QStringList videoList = Tool::getFileList(videoPath); - // 如果最后一个文件名是当前正在录制的文件名,则去除 - if (videoList.last() == Channel::curRecordFilename) { - videoList.pop_back(); + QList fileList = db->get(channel); + QStringList result; + for (const DatabaseManager::File& file : fileList) { + result.push_back(file.filename); } - reponse->success("获取文件成功", videoList); + reponse->success("获取文件成功", result); } /** @@ -204,18 +206,25 @@ void TcpRequestHandler::getFileList(TcpRequest* request, TcpResponse* reponse) void TcpRequestHandler::deleteFile(TcpRequest* request, TcpResponse* reponse) { QVariantMap params = request->getBodyParams(); - if (!params.contains("interface") || !params.contains("filename")) { - Log::error("deleteFile params error, missing params \"interface\" or \"filename\""); - reponse->error("缺少端口或文件名"); + QString chn = params.value("interface").toString(); + QString filename = params.value("filename").toString(); + if (chn != Constant::MainChannel || chn != Constant::SecondaryChannel) { + Log::error("deleteFile params error, error params \"interface\" or \"filename\""); + reponse->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(Constant::VideoPath).arg(interface).arg(fileName); + if (filename.isEmpty()) { + Log::error("deleteFile params error, missing params \"filename\""); + reponse->error("缺少文件名参数"); + return; + } + // 从数据库删除指定记录 + DatabaseManager::Channel channel = chn == Constant::MainChannel + ? DatabaseManager::MainChannel + : DatabaseManager::SecondaryChannel; + db->remove(channel, filename); + // 删除相对于的视频文件 + QString filePath = QString("%1/%2/%3").arg(Constant::VideoPath).arg(chn).arg(filename); QFile video(filePath); if (video.exists()) { video.remove(); @@ -334,4 +343,4 @@ void TcpRequestHandler::setCurrentTime(TcpRequest* request, TcpResponse* reponse } else { reponse->success("时间同步成功"); } -} \ No newline at end of file +} diff --git a/Tool.cpp b/Tool.cpp index 0ad1e1c..c1cba9f 100755 --- a/Tool.cpp +++ b/Tool.cpp @@ -1,6 +1,7 @@ #include "Tool.h" #include #include +#include #include #include @@ -121,3 +122,18 @@ int64_t Tool::getAvailableStorage(QString mountedPath) return 0; #endif } + +/** + * @brief 获取代码块运行的时间 + * @param callback + * @return + */ +double Tool::getCostTime(std::function callback, const char* callbackName) +{ + QElapsedTimer mstimer; + mstimer.start(); + callback(); + double time = (double)mstimer.nsecsElapsed() / (double)1000000; + qDebug() << callbackName << "cast time:" << time << "ms"; + return time; +} diff --git a/Tool.h b/Tool.h index f521e6b..d427a01 100755 --- a/Tool.h +++ b/Tool.h @@ -1,17 +1,22 @@ #ifndef TOOL_H #define TOOL_H #include +#include class Tool { public: Tool(); static QStringList getFileList(QString path); + static bool removeFile(QString path); + #ifdef Q_OS_LINUX static QString writeCom(QString path); #endif static int64_t getAvailableStorage(QString mountedPath); + + static double getCostTime(std::function callback, const char* callbackName = ""); }; #endif // TOOL_H diff --git a/Widget.cpp b/Widget.cpp index be32b40..40b8f6d 100755 --- a/Widget.cpp +++ b/Widget.cpp @@ -26,32 +26,21 @@ Widget::Widget(QWidget* parent) , ui(new Ui::Widget) { ui->setupUi(this); - ui->lblA->setPixmap(QPixmap(":/images/no-record.png")); - ui->lblB->setPixmap(QPixmap(":/images/no-record.png")); + ui->lblImgA->setPixmap(QPixmap(":/images/no-record.png")); + ui->lblImgB->setPixmap(QPixmap(":/images/no-record.png")); + ui->lblTxtA->setText("未录制"); + ui->lblTxtB->setText("未录制"); + ui->recordWidget->hide(); - menu = new Menu(this); + menu = new Menu(); menu->hide(); - - progressBar = new ProgressBar(this); - progressBar->hide(); - - channelSetting = new ChannelSetting(this); - channelSetting->hide(); - - // 设置窗体背景透明 - QPalette pal = palette(); - pal.setBrush(QPalette::Base, Qt::transparent); - setPalette(pal); - setAttribute(Qt::WA_TranslucentBackground, true); - - // 设置listwidget无滚动条 - ui->listWidget->horizontalScrollBar()->hide(); - ui->listWidget->verticalScrollBar()->hide(); - ui->listWidget->hide(); + // 设置是否显示通道选择 + menu->setChannelSelectVisible(playbackMode == Constant::OneChannelPlayback); + ui->progressBar->hide(); // 每5秒更新一次进度条 progressTimer = new QTimer(); - progressTimer->setInterval(500); + progressTimer->setInterval(200); connect(progressTimer, SIGNAL(timeout()), this, SLOT(onProgressTimeout())); for (Channel* chn : channelList) { @@ -63,8 +52,7 @@ Widget::Widget(QWidget* parent) QVariantMap data = msg.toMap(); bool available = data["avalible"].toBool(); if (available) { - Log::info("video input {} is available", - chn->channelName == Constant::MainChannel ? "HDMI-C" : "HDMI-D"); + Log::info("video input {} is available", chn->channelName == Constant::MainChannel ? Constant::MainChannel : Constant::SecondaryChannel); if (chn->channelName == Constant::MainChannel) { serialPortTool->onMainChannelOn(); QThread::msleep(100); @@ -73,8 +61,7 @@ Widget::Widget(QWidget* parent) QThread::msleep(100); } } else { - Log::info("video input {} is not available", - chn->channelName == Constant::MainChannel ? "HDMI-C" : "HDMI-D"); + Log::info("video input {} is not available", chn->channelName == Constant::MainChannel ? Constant::MainChannel : Constant::SecondaryChannel); if (chn->channelName == Constant::MainChannel) { serialPortTool->onMainChannelOff(); QThread::msleep(100); @@ -87,15 +74,17 @@ Widget::Widget(QWidget* parent) }); } - connect(serialPortTool, SIGNAL(btnPlaybackClicked()), this, SLOT(onBtnPlaybackClicked())); - connect(serialPortTool, SIGNAL(btnPreviousClicked()), this, SLOT(onBtnPreviousClicked())); - connect(serialPortTool, SIGNAL(btnPauseClicked()), this, SLOT(onBtnPasueClicked())); + connect(serialPortTool, SIGNAL(btnMenuClicked()), this, SLOT(onBtnMenuClicked())); + connect(serialPortTool, SIGNAL(btnUpClicked()), this, SLOT(onBtnUpClicked())); + connect(serialPortTool, SIGNAL(btnDownClicked()), this, SLOT(onBtnDownClicked())); + connect(serialPortTool, SIGNAL(btnLeftClicked()), this, SLOT(onBtnLeftClicked())); + connect(serialPortTool, SIGNAL(btnRightClicked()), this, SLOT(onBtnRightClicked())); + connect(serialPortTool, SIGNAL(btnConfirmClicked()), this, SLOT(onBtnConfirmClicked())); connect(serialPortTool, SIGNAL(btnReturnClicked()), this, SLOT(onBtnReturnClicked())); - connect(serialPortTool, SIGNAL(btnBackClicked()), this, SLOT(onBtnBackClicked())); - connect(serialPortTool, SIGNAL(btnNextClicked()), this, SLOT(onBtnNextClicked())); - connect(serialPortTool, SIGNAL(btnForwardClicked()), this, SLOT(onBtnForwardClicked())); - connect(serialPortTool, SIGNAL(btnDoneClicked()), this, SLOT(onBtnEnterClicked())); - connect(serialPortTool, SIGNAL(btnChannelSettingClicked()), this, SLOT(onBtnChannelSettingClicked())); + + connect(menu, SIGNAL(curChannelChanged(QString)), this, SLOT(onCurChannelChanged(QString))); + connect(menu, SIGNAL(btnVideoClicked(QString)), this, SLOT(onBtnVideoClicked(QString))); + connect(this, SIGNAL(needPlayVideo(QString)), menu, SLOT(clickVideo(QString))); } Widget::~Widget() @@ -103,57 +92,6 @@ Widget::~Widget() delete ui; delete progressTimer; delete menu; - delete progressBar; - delete channelSetting; -} - -void Widget::renderList() -{ - ui->listWidget->clear(); - if (fileList.isEmpty()) { - ui->listWidget->show(); - return; - } - // 如果在录制中应该出去正在录制的文件名 - if (fileList.last() == Channel::curRecordFilename) { - fileList.pop_back(); - } - - QListWidgetItem* curItem = nullptr; - for (QString& file : fileList) { - QListWidgetItem* item = new QListWidgetItem(file); - ui->listWidget->addItem(item); - if (isPlayback) { - // 一路回放 - if (playbackMode == Constant::OneChannelPlayback) { - if (curPlayChannel == curSelectChannel && item->text() == curPlayFilename) { - curItem = item; - } - } - // 两路回放 - else { - if (item->text() == curPlayFilename) { - curItem = item; - } - } - } - } - ui->listWidget->show(); - if (curItem) { - ui->listWidget->setCurrentItem(curItem); - } else { - ui->listWidget->setCurrentRow(0); - } -} - -/** - * @brief 获取视频文件列表并显示 - */ -void Widget::showPlayList() -{ - QString dirPath = QString("%1/%2").arg(Constant::VideoPath).arg(curSelectChannel); - fileList = Tool::getFileList(dirPath); - renderList(); } /** @@ -162,153 +100,34 @@ void Widget::showPlayList() void Widget::onProgressTimeout() { Channel* chn = findChannelByName(Constant::MainChannel); - int pos = chn->file->invoke("getPosition").toInt() / 1000; - progressBar->setCurrent(pos); + if (chn) { + // 获取当前播放位置,单位ms + int pos = chn->inputFile->invoke("getPosition").toInt(); + ui->progressBar->setCurrent(pos); + } } /** - * @brief 收到回放指令 + * @brief 按下菜单按键 */ -void Widget::onBtnPlaybackClicked() +void Widget::onBtnMenuClicked() { - // 如果在进行通道设置屏蔽回放操作 - if (channelSetting->isVisible()) - return; - Log::info("receive command playback, playback mode: {}", playbackMode); - // 如果是一路回放,显示选择菜单 - if (playbackMode == Constant::OneChannelPlayback) { - if (!menu->isVisible()) - menu->show(); - } - // 两路回放不显示菜单,直接显示列表 - else if (playbackMode == Constant::TwoChannelPlayback) { - curSelectChannel = Constant::MainChannel; - showPlayList(); + if (!menu->isVisible()) { + menu->show(); } } /** - * @brief 收到上段指令, 优先级 - * 处理优先级: 通道设置菜单 ==> 通道选择菜单 ==> 列表 - */ -void Widget::onBtnPreviousClicked() -{ - if (channelSetting->isVisible()) { - channelSetting->onReceivePre(); - return; - } - // 菜单显示则切换菜单选中 - if (menu->isVisible()) { - menu->setCurChannel(Constant::MainChannel); - return; - } - // 列表显示,则选中上一个视频 - if (ui->listWidget->isVisible()) { - int curRow = ui->listWidget->currentRow(); - if (curRow == 0) - curRow = ui->listWidget->count(); - ui->listWidget->setCurrentRow(curRow - 1); - return; - } -} - -/** - * @brief 收到下段指令 - * 处理优先级: 通道设置菜单 ==> 通道选择菜单 ==> 列表 - */ -void Widget::onBtnNextClicked() -{ - if (channelSetting->isVisible()) { - channelSetting->onReceiveNext(); - return; - } - if (menu->isVisible()) { - menu->setCurChannel(Constant::SecondaryChannel); - return; - } - if (ui->listWidget->isVisible()) { - int curRow = ui->listWidget->currentRow(); - if (curRow == ui->listWidget->count() - 1) { - curRow = -1; - } - ui->listWidget->setCurrentRow(curRow + 1); - return; - } -} - -/** - * @brief 收到暂停指令 - */ -void Widget::onBtnPasueClicked() -{ - if (!isPlayback) - return; - for (Channel* chn : channelList) { - if (chn->state == Channel::Playback || chn->state == Channel::Pause) { - chn->togglePause(); - if (chn->channelName == Constant::MainChannel) { - progressBar->showMax(); - if (chn->state == Channel::Pause) { - progressBar->setState(ProgressBar::Pause); - // 打开暂停灯 - serialPortTool->onPlayPause(); - } else { - progressBar->setState(ProgressBar::Play); - // 关闭暂停灯 - serialPortTool->onPlayResume(); - } - } - } - } -} - -/** - * @brief 收到确定指令 - * 通道设置菜单 ==> 通道选择菜单 ==> 列表 - */ -void Widget::onBtnEnterClicked() -{ - if (channelSetting->isVisible()) { - channelSetting->onReceiveEnter(); - return; - } - // 菜单显示,则关闭菜单显示列表 - if (menu->isVisible()) { - curSelectChannel = menu->getCurChannel(); - menu->hide(); - showPlayList(); - return; - } - if (ui->listWidget->isVisible()) { - if (ui->listWidget->count() == 0) - return; - if (playbackMode == Constant::OneChannelPlayback) - playOneChannel(); - else if (playbackMode == Constant::TwoChannelPlayback) - playTwoChannels(); - serialPortTool->onPlaybackStart(); - return; - } -} - -/** - * @brief 收到返回指令 - * 处理优先级: 通道设置菜单 ==> 通道选择菜单 ==> 列表 ==> 回放 + * @brief 按下返回按键。优先级:菜单 > 播放 */ void Widget::onBtnReturnClicked() { - if (channelSetting->isVisible()) { - channelSetting->hide(); - return; - } + // 关闭菜单 if (menu->isVisible()) { menu->hide(); return; } - if (ui->listWidget->isVisible()) { - ui->listWidget->hide(); - return; - } + // 关闭回放 if (isPlayback) { // 停止回放 for (Channel* chn : channelList) { @@ -318,12 +137,215 @@ void Widget::onBtnReturnClicked() curPlayFilename = ""; } } - progressBar->hide(); - ui->lblA->show(); - ui->lblB->show(); + // 隐藏进度条并关闭定时器 + ui->progressBar->hide(); + progressTimer->stop(); + // 显示录制状态 + // ui->recordWidget->show(); } } +/** + * @brief 按下上键。优先级: 菜单 > 播放 + */ +void Widget::onBtnUpClicked() +{ + if (menu->isVisible()) { + menu->move(Menu::Up); + return; + } + if (isPlayback) { + emit needPlayVideo("previous"); + } +} + +/** + * @brief 按下下键。 优先级: 菜单 > 播放 + */ +void Widget::onBtnDownClicked() +{ + if (menu->isVisible()) { + menu->move(Menu::Down); + return; + } + if (isPlayback) { + emit needPlayVideo("next"); + } +} + +/** + * @brief 按下左键。优先级: 菜单 > 播放。 + */ +void Widget::onBtnLeftClicked() +{ + if (menu->isVisible()) { + menu->move(Menu::Left); + return; + } + if (isPlayback) { + seek("back"); + } +} + +/** + * @brief 按下左键。优先级: 菜单 > 播放 + */ +void Widget::onBtnRightClicked() +{ + if (menu->isVisible()) { + menu->move(Menu::Right); + return; + } + if (isPlayback) { + seek("forward"); + } +} + +/** + * @brief 按下ok键。优先级: 菜单 > 回放 + */ +void Widget::onBtnConfirmClicked() +{ + if (menu->isVisible()) { + menu->confirm(); + return; + } + if (isPlayback) { + // 切换暂停和播放 + for (Channel* chn : channelList) { + if (chn->state == Channel::Playback || chn->state == Channel::Pause) { + chn->togglePause(); + if (chn->channelName == Constant::MainChannel) { + ui->progressBar->showMax(); + if (chn->state == Channel::Pause) { + ui->progressBar->setState(ProgressBar::Pause); + // 打开暂停灯 + serialPortTool->onPlayPause(); + } else { + ui->progressBar->setState(ProgressBar::Play); + // 关闭暂停灯 + serialPortTool->onPlayResume(); + } + } + } + } + } +} + +/** + * @brief 回放视频 + */ +void Widget::onBtnVideoClicked(QString filename) +{ + if (filename == "first") { + // 显示提示弹窗 + } else if (filename == "last") { + if (playbackMode == Constant::OneChannelPlayback) { + Channel* chn = findChannelByName(Constant::MainChannel); + chn->showFinishPromot(); + } else { + for (Channel* chn : channelList) + chn->showFinishPromot(); + } + } else { + if (playbackMode == Constant::OneChannelPlayback) { + playOneChannel(filename); + } else { + playTwoChannels(filename); + } + serialPortTool->onPlaybackStart(); + } +} + +void Widget::onCurChannelChanged(QString channel) +{ + curSelectChannel = channel; + Log::info("channel switch, current channel:", channel.toStdString()); +} + +/** + * @brief 一路回放,将当前选中播放的视频从HDMI-OUT0口输出 + */ +void Widget::playOneChannel(QString filename) +{ + QString path = QString("%1/%2/%3").arg(Constant::VideoPath).arg(curSelectChannel).arg(filename); + Channel* channel = findChannelByName(Constant::MainChannel); + for (Channel* chn : channelList) { + chn->startPlayLive(); + } + bool ret = channel->startPlayback(path); + if (!ret) { + Log::info("play back error"); + ui->progressBar->hide(); + } + + isPlayback = true; + curPlayChannel = curSelectChannel; + curPlayFilename = filename; + + // 全局保存当前播放的视频 + mutex.lock(); + curFilename = filename; + condition.wakeAll(); + mutex.unlock(); + + int duration = channel->playbackDuration; + ui->progressBar->setDuration(duration); + ui->progressBar->setCurrent(0); + ui->progressBar->show(); + ui->progressBar->showMax(); + ui->progressBar->setState(ProgressBar::Play); + + if (progressTimer->isActive()) { + progressTimer->stop(); + progressTimer->start(); + } else { + progressTimer->start(); + } + ui->recordWidget->hide(); +} + +/** + * @brief 两路回放,以主通道的视频为主分别从HDMI-OUT0和HDMI-OUT1输出 + */ +void Widget::playTwoChannels(QString filename) +{ + for (Channel* chn : channelList) { + QString path = QString("%1/%2/%3").arg(Constant::VideoPath).arg(chn->channelName).arg(filename); + int ret = chn->startPlayback(path); + if (chn->channelName == Constant::MainChannel) { + if (ret) { + int duration = chn->playbackDuration; + ui->progressBar->setDuration(duration); + ui->progressBar->setCurrent(0); + ui->progressBar->show(); + ui->progressBar->showMax(); + ui->progressBar->setState(ProgressBar::Play); + if (progressTimer->isActive()) { + progressTimer->stop(); + progressTimer->start(); + } else { + progressTimer->start(); + } + ui->recordWidget->hide(); + } else { + ui->progressBar->hide(); + ui->recordWidget->hide(); + } + } + } + + curPlayFilename = filename; + curPlayChannel = Constant::MainChannel; + isPlayback = true; + + // 全局保存正在播放的视频 + mutex.lock(); + curFilename = filename; + condition.wakeAll(); + mutex.unlock(); +} + /** * @brief 视频跳转 */ @@ -342,133 +364,11 @@ void Widget::seek(QString type) } if (chn->channelName == Constant::MainChannel) - progressBar->showMax(); + ui->progressBar->showMax(); } } } -/** - * @brief 快进 - */ -void Widget::onBtnForwardClicked() -{ - seek("forward"); -} - -/** - * @brief 快退 - */ -void Widget::onBtnBackClicked() -{ - seek("back"); -} - -/** - * @brief 打开通道设置HDMI or VGA - */ -void Widget::onBtnChannelSettingClicked() -{ - // 关闭通道选择菜单 - if (menu->isVisible()) - menu->hide(); - // 打开通道设置菜单 - if (channelSetting->isVisible()) - return; - channelSetting->show(); -} - -/** - * @brief 一路回放,将当前选中播放的视频从HDMI-OUT0口输出 - */ -void Widget::playOneChannel() -{ - QString filename = ui->listWidget->currentItem()->text(); - QString path = QString("%1/%2/%3").arg(Constant::VideoPath).arg(curSelectChannel).arg(filename); - Channel* channel = findChannelByName(Constant::MainChannel); - for (Channel* chn : channelList) { - chn->startPlayLive(); - } - bool ret = channel->startPlayback(path); - if (!ret) - progressBar->hide(); - - isPlayback = true; - curPlayChannel = curSelectChannel; - curPlayFilename = filename; - - // 全局保存当前播放的视频 - mutex.lock(); - curFilename = filename; - condition.wakeAll(); - mutex.unlock(); - - // 保存当前播放视频列表 - QString dirPath = QString("%1/%2").arg(Constant::VideoPath).arg(curPlayChannel); - fileList = Tool::getFileList(dirPath); - - int duration = channel->playbackDuration; - progressBar->setDuration(duration); - progressBar->setCurrent(0); - progressBar->show(); - progressBar->showMax(); - progressBar->setState(ProgressBar::Play); - - if (progressTimer->isActive()) { - progressTimer->stop(); - progressTimer->start(); - } else { - progressTimer->start(); - } - ui->lblA->hide(); - ui->lblB->hide(); -} - -/** - * @brief 两路回放,以主通道的视频为主分别从HDMI-OUT0和HDMI-OUT1输出 - */ -void Widget::playTwoChannels() -{ - QString filename = ui->listWidget->currentItem()->text(); - for (Channel* chn : channelList) { - QString path = QString("%1/%2/%3").arg(Constant::VideoPath).arg(chn->channelName).arg(filename); - int ret = chn->startPlayback(path); - if (chn->channelName == Constant::MainChannel) { - if (ret) { - int duration = chn->playbackDuration; - progressBar->setDuration(duration); - progressBar->setCurrent(0); - progressBar->show(); - progressBar->showMax(); - progressBar->setState(ProgressBar::Play); - if (progressTimer->isActive()) { - progressTimer->stop(); - progressTimer->start(); - } else { - progressTimer->start(); - } - ui->lblA->hide(); - ui->lblB->hide(); - } else { - progressBar->hide(); - } - } - } - - curPlayFilename = filename; - curPlayChannel = Constant::MainChannel; - isPlayback = true; - - // 全局保存正在播放的视频 - mutex.lock(); - curFilename = filename; - condition.wakeAll(); - mutex.unlock(); - - // 将主通道的视频列表作为主列表 - QString dirPath = QString("%1/%2").arg(Constant::VideoPath).arg(Constant::MainChannel); - fileList = Tool::getFileList(dirPath); -} - /** * @brief 当前视频播放完毕 */ @@ -478,81 +378,14 @@ void Widget::onPlayEnd() if (playbackMode == Constant::TwoChannelPlayback) { LinkObject* file = static_cast(sender()); for (Channel* chn : channelList) { - if (chn->file == file) { + if (chn->inputFile == file) { if (chn->channelName != Constant::MainChannel) { return; } } } } - bool needRefresh = false; - // 计算下一个文件名 - int index = fileList.indexOf(curPlayFilename); - if (index == fileList.length() - 1) { - // 重新刷新文件列表 - QString dirPath = QString("%1/%2").arg(Constant::VideoPath).arg(curPlayChannel); - fileList = Tool::getFileList(dirPath); - // 刷新文件列表后还是最后一个文件 - if (index == fileList.length() - 1) { - Log::info("there is no file to play"); - // 显示播放完毕的信息 - if (playbackMode == Constant::OneChannelPlayback) { - Channel* chn = findChannelByName(Constant::MainChannel); - if (chn) - chn->showFinishPromot(); - } else if (playbackMode == Constant::TwoChannelPlayback) { - for (Channel* chn : channelList) { - chn->showFinishPromot(); - } - } - return; - } - needRefresh = true; - Log::info("refresh current play file list"); - } - curPlayFilename = fileList.at(index + 1); - - // 播放下一个视频 - if (playbackMode == Constant::OneChannelPlayback) { - Channel* chn = findChannelByName(Constant::MainChannel); - if (chn) { - QString path = QString("%1/%2/%3").arg(Constant::VideoPath).arg(curPlayChannel).arg(curPlayFilename); - bool ret = chn->startPlayback(path); - if (chn->channelName == Constant::MainChannel) { - if (!ret) { - progressBar->hide(); - return; - } - int duration = chn->playbackDuration; - progressBar->setDuration(duration); - progressBar->showMax(); - } - } - } else if (playbackMode == Constant::TwoChannelPlayback) { - for (Channel* chn : channelList) { - QString path = QString("%1/%2/%3").arg(Constant::VideoPath).arg(chn->channelName).arg(curPlayFilename); - bool ret = chn->startPlayback(path); - if (chn->channelName == Constant::MainChannel) { - if (!ret) { - progressBar->hide(); - continue; - } - int duration = chn->playbackDuration; - progressBar->setDuration(duration); - progressBar->showMax(); - } - } - } - if (needRefresh) { - renderList(); - } else { - ui->listWidget->setCurrentRow(ui->listWidget->currentRow() + 1); - } - - mutex.lock(); - curFilename = curPlayFilename; - condition.wakeAll(); - mutex.unlock(); + emit needPlayVideo("next"); } /** @@ -562,16 +395,22 @@ void Widget::onPlayEnd() void Widget::onShowRecordLabel(bool show) { Channel* chn = static_cast(sender()); - QLabel* label; + QLabel* lblImg; + QLabel* lblTxt; if (chn->channelName == Constant::MainChannel) { - label = ui->lblA; + lblImg = ui->lblImgA; + lblTxt = ui->lblTxtA; } else { - label = ui->lblB; + lblImg = ui->lblImgB; + lblTxt = ui->lblTxtB; } if (show) { - label->setPixmap(QPixmap(":/images/record.png")); - } else - label->setPixmap(QPixmap(":/images/no-record.png")); + lblImg->setPixmap(QPixmap(":/images/record.png")); + lblTxt->setText("录制中"); + } else { + lblImg->setPixmap(QPixmap(":/images/no-record.png")); + lblTxt->setText("未录制"); + } } /** diff --git a/Widget.h b/Widget.h index dee8895..fdc04ec 100755 --- a/Widget.h +++ b/Widget.h @@ -2,7 +2,6 @@ #define WIDG_H #include "Channel.h" -#include "ChannelSetting.h" #include "Menu.h" #include "Message.h" #include "ProgressBar.h" @@ -21,26 +20,29 @@ public: explicit Widget(QWidget* parent = 0); ~Widget(); -public slots: - void showPlayList(); +signals: + void needPlayVideo(QString types); private slots: void onProgressTimeout(); void onPlayEnd(); void onShowRecordLabel(bool show); - void onBtnPlaybackClicked(); - void onBtnPreviousClicked(); - void onBtnNextClicked(); - void onBtnPasueClicked(); - void onBtnEnterClicked(); + + // 按键操作。上下左右确认返回 + void onBtnMenuClicked(); + void onBtnUpClicked(); + void onBtnDownClicked(); + void onBtnLeftClicked(); + void onBtnRightClicked(); + void onBtnConfirmClicked(); void onBtnReturnClicked(); - void onBtnForwardClicked(); - void onBtnBackClicked(); - void onBtnChannelSettingClicked(); + + // 播放视频 + void onBtnVideoClicked(QString filename); + void onCurChannelChanged(QString channel); private: Ui::Widget* ui; - QStringList fileList; QTimer* progressTimer; bool isPlayback = false; @@ -49,15 +51,12 @@ private: QString curPlayFilename; Menu* menu; - ProgressBar* progressBar; - ChannelSetting* channelSetting; private: void seek(QString type); - void playOneChannel(); - void playTwoChannels(); + void playOneChannel(QString filename); + void playTwoChannels(QString filename); Channel* findChannelByName(QString name); - void renderList(); }; #endif // WIDG_H diff --git a/Widget.ui b/Widget.ui index 8cb38f2..91eeabe 100755 --- a/Widget.ui +++ b/Widget.ui @@ -19,7 +19,7 @@ Widget - + background: rgba(0,0,0,0); @@ -51,48 +51,155 @@ 0 - + + + Qt::Horizontal + + + + 40 + 20 + + + + + + - 32 - 32 + 50 + 0 - - - 32 - 32 - - - - - - - :/images/record.png + + color: white; + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + + 32 + 32 + + + + + 32 + 32 + + + + + + + :/images/record.png + + + + + + + + 10 + 50 + false + + + + color: white; + + + 录制中 + + + + + + + + + 0 + + + + + + 32 + 32 + + + + + 32 + 32 + + + + + + + :/images/record.png + + + + + + + + 10 + 50 + false + + + + color: white; + + + 1 + + + 录制中 + + + + + + - - + + + Qt::Horizontal + + - 32 - 32 + 40 + 20 - - - 32 - 32 - - - - - - - :/images/record.png - - + @@ -109,70 +216,22 @@ + + + - - - - Qt::Horizontal - - - - 1511 - 20 - - - - - - - - - 280 - 0 - - - - - 290 - 16777215 - - - - - 14 - - - - QListWidget { - color: rgba(255, 255, 255); - outline: none; - background-color: rgba(0, 0, 0, 0.4); - border: none; -} - -#listWidget::item { - - border: transparent; - padding: 6px; -} - -#listWidget::item:selected { - background-color: rgba(0, 0, 0, 0.5); - border-left: 10px solid 00AEEC; - color: #00AEEC; -} - - -QScrollBar { - width: 0; -} - - - + + + ProgressBar + QWidget +
ProgressBar.h
+ 1 +
+
diff --git a/main.cpp b/main.cpp index 29917c6..a954377 100755 --- a/main.cpp +++ b/main.cpp @@ -1,6 +1,7 @@ #include "Channel.h" #include "CheckStorageThread.h" #include "Constant.h" +#include "DatabaseManager.h" #include "Json.h" #include "Link.h" #include "Log.h" @@ -9,6 +10,7 @@ #include "Tool.h" #include "Widget.h" #include +#include #include #include @@ -16,6 +18,8 @@ TcpServer* server; // 串口工具 SerialPortTool* serialPortTool; +// 数据库管理 +DatabaseManager* db; // 视频输出通道 LinkObject* vo; LinkObject* vo1; @@ -26,6 +30,8 @@ QList channelList; Constant::RecordMode recordMode; // 回放模式 Constant::PlaybackMode playbackMode; +QString mainChannelProtocol; +QString secondaryChannelProtocol; /** * @brief 加载配置文件 @@ -47,6 +53,11 @@ bool loadConfiguration(QString path) Channel* chn = new Channel(); QVariantMap cfg = list.at(i).toMap(); chn->channelName = cfg["name"].toString(); + if (chn->channelName == Constant::MainChannel) { + mainChannelProtocol = cfg["protocol"].toString(); + } else if (chn->channelName == Constant::SecondaryChannel) { + secondaryChannelProtocol = cfg["protocol"].toString(); + } QVariantMap encV = cfg["encV"].toMap(); chn->videoEncoderParams = encV; @@ -62,15 +73,6 @@ bool loadConfiguration(QString path) return true; } -/** - * @brief 关闭所有灯光 - */ -void resetLight() -{ - printf("reset all lights"); - serialPortTool->onReset(); -} - /** * @brief 开始录制 */ @@ -84,16 +86,16 @@ void startRecord() QThread::msleep(100); return; } - + // 一路录制,只录制主通道 if (recordMode == Constant::OneChannelRecord) { for (Channel* chn : channelList) { if (chn->channelName == Constant::MainChannel) { chn->startRecord(); - QThread::msleep(100); + // QThread::msleep(100); serialPortTool->onRecordStart(); QThread::msleep(100); - serialPortTool->onLoopOff(); + // serialPortTool->onLoopOff(); } } } @@ -103,19 +105,34 @@ void startRecord() chn->startRecord(); serialPortTool->onRecordStart(); QThread::msleep(100); - serialPortTool->onLoopOff(); - QThread::msleep(100); + // serialPortTool->onLoopOff(); + // QThread::msleep(100); } } // 无录制 else { - qDebug() << "no record"; // 打开环路灯 serialPortTool->onLoopOn(); QThread::msleep(100); } } +void setChannelProtocol() +{ + if (mainChannelProtocol == "HDMI") { + serialPortTool->onMainChannelHDMI(); + } else if (mainChannelProtocol == "VGA") { + serialPortTool->onMainChannelVGA(); + } + QThread::msleep(100); + if (secondaryChannelProtocol == "HDMI") { + serialPortTool->onSecondaryChannelHDMI(); + } else if (secondaryChannelProtocol == "VGA") { + serialPortTool->onSecondaryChannelVGA(); + } + QThread::msleep(100); +} + int main(int argc, char* argv[]) { // std::atexit(resetLight); @@ -133,16 +150,15 @@ int main(int argc, char* argv[]) // HDMI-OUT0视频输出,只将ui输出在0口 vo = Link::create("OutputVo"); QVariantMap dataVo; - dataVo["vid"] = 0; + dataVo["ui"] = true; dataVo["type"] = "hdmi"; vo->start(dataVo); // HDMI-OUT1视频输出 vo1 = Link::create("OutputVo"); QVariantMap dataVo1; - dataVo1["vid"] = 1; dataVo1["ui"] = false; - dataVo1["type"] = "vga|bt1120"; + dataVo1["type"] = "bt1120"; vo1->start(dataVo1); /** @@ -152,6 +168,8 @@ int main(int argc, char* argv[]) // HDMI-OUT1口特殊设置 static int lastNorm = 0; int norm = 0; + int ddr = 0; + // 获取到设置的输出分辨率 QString str = "1080P60"; if (str == "1080P60") norm = 9; @@ -163,13 +181,15 @@ int main(int argc, char* argv[]) norm = 5; else if (str == "720P50") norm = 6; - else if (str == "3840x2160_30") + else if (str == "3840x2160_30") { norm = 14; + ddr = 1; + } 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); + cmd = cmd.sprintf("insmod /ko/extdrv/hi_lt8618sx_lp.ko norm=%d USE_DDRCLK=%d", norm, ddr); system(cmd.toLatin1().data()); } @@ -185,6 +205,7 @@ int main(int argc, char* argv[]) serialPortTool = new SerialPortTool(); serialPortTool->open(); + // setChannelProtocol(); serialPortTool->onPowerOn(); // 打开电源灯 QThread::msleep(100);