#include "Widget.h" #include "Channel.h" #include "Constant.h" #include "Json.h" #include "Log.h" #include "TcpServer.h" #include "Tool.h" #include "ui_Widget.h" #include #include #include #include // 多线程中使用 QMutex mutex; QWaitCondition condition; QString curFilename; extern QList channelList; extern Constant::PlaybackMode playbackMode; Widget::Widget(QWidget* parent) : 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")); menu = new Menu(this); menu->hide(); progressBar = new ProgressBar(this); progressBar->hide(); message = new Message(this); // 设置窗体背景透明 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(); // 初始化串口 initSerialPort(); // 初始化指令列表 initCommandMap(); // 每5秒更新一次进度条 progressTimer = new QTimer(); progressTimer->setInterval(500); connect(progressTimer, SIGNAL(timeout()), this, SLOT(onProgressTimeout())); for (Channel* chn : channelList) { connect(chn, SIGNAL(playEnd()), this, SLOT(onPlayEnd())); connect(chn, SIGNAL(showRecordState(bool)), this, SLOT(onShowRecordLabel(bool))); } } Widget::~Widget() { serialPort->close(); delete ui; delete timer; delete progressTimer; delete menu; delete progressBar; delete message; } /** * @brief 初始化串口 */ void Widget::initSerialPort() { // 用于重连串口的timer timer = new QTimer(); timer->setInterval(3000); timer->stop(); serialPort = new QSerialPort(); serialPort->setPortName("ttyAMA2"); serialPort->setBaudRate(QSerialPort::Baud115200); serialPort->setDataBits(QSerialPort::Data8); serialPort->setParity(QSerialPort::NoParity); serialPort->setStopBits(QSerialPort::OneStop); serialPort->setFlowControl(QSerialPort::NoFlowControl); if (serialPort->open(QIODevice::ReadWrite)) { Log::info("open serial port: /dev/ttyAMA2 success"); } else { Log::error("open serial port: /dev/ttyAMA2 failed, ready to retry..."); timer->start(); } connect(serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(onSerialError(QSerialPort::SerialPortError))); connect(serialPort, SIGNAL(readyRead()), this, SLOT(onReadyRead())); } /** * @brief 获取视频文件列表并显示 */ void Widget::showPlayList() { QString dirPath = QString("%1/%2").arg(Constant::VideoPath).arg(curSelectChannel); QStringList files = Tool::getFileList(dirPath); ui->listWidget->clear(); for (QString& file : files) { QListWidgetItem* item = new QListWidgetItem(file); ui->listWidget->addItem(item); } ui->listWidget->show(); ui->listWidget->setCurrentRow(0); } /** * @brief 重连串口 */ void Widget::onTimeout() { timer->stop(); bool ret = serialPort->open(QIODevice::ReadWrite); if (!ret) { Log::info("reopen serial port: /dev/ttyAMA2 success"); timer->start(); } else { Log::error("reopen serial port: /dev/ttyAMA2 failed, ready to retry..."); } } /** * @brief 更新进度条 */ void Widget::onProgressTimeout() { Channel* chn = findChannelByName(Constant::MainChannel); int pos = chn->file->invoke("getPosition").toInt() / 1000; progressBar->setCurrent(pos); } /** * @brief 串口错误处理 * @param error */ void Widget::onSerialError(QSerialPort::SerialPortError error) { if (error == QSerialPort::ResourceError) { Log::error("serial port break, try to reopen"); serialPort->close(); timer->start(); } } /** * @brief 映射指令列表 */ void Widget::initCommandMap() { commandMap.insert("A1", CmdPlayback); commandMap.insert("A2", CmdNext); commandMap.insert("A3", CmdPrevious); commandMap.insert("A4", CmdForward); commandMap.insert("A5", CmdBack); commandMap.insert("A6", CmdPause); commandMap.insert("A7", CmdReturn); commandMap.insert("A8", CmdEnter); } /** * @brief 从串口缓冲区接收数据 */ void Widget::onReadyRead() { serialPort->waitForReadyRead(100); QString data = serialPort->readLine(); if (!commandMap.contains(data)) { Log::error("receive unkown command {} from serial port: /dev/ttyAMA2", data.toStdString()); return; } Command cmd = commandMap.value(data); switch (cmd) { case CmdPlayback: receivePlayback(); break; case CmdPrevious: receivePrevious(); break; case CmdNext: receiveNext(); break; case CmdForward: seek("forward"); break; case CmdBack: seek("next"); break; case CmdPause: receivePasue(); break; case CmdEnter: receiveEnter(); break; case CmdReturn: receiveReturn(); break; default: break; } } /** * @brief 收到回放指令 */ void Widget::receivePlayback() { 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(); } } /** * @brief 收到上段指令 */ void Widget::receivePrevious() { // 菜单显示则切换菜单选中 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; } if (isPlayback) { } } /** * @brief 收到下段指令 */ void Widget::receiveNext() { 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; } if (isPlayback) { } } /** * @brief 收到暂停指令 */ void Widget::receivePasue() { 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); } else { progressBar->setState(ProgressBar::Play); } } } } } /** * @brief 收到确定指令 */ void Widget::receiveEnter() { // 菜单显示,则关闭菜单显示列表 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(); return; } } /** * @brief 收到返回指令 * 处理优先级: 菜单 ==> 列表 ==> 回放 */ void Widget::receiveReturn() { if (menu->isVisible()) { menu->hide(); return; } if (ui->listWidget->isVisible()) { ui->listWidget->hide(); return; } if (isPlayback) { // 停止回放 for (Channel* chn : channelList) { if (chn->state != Channel::Stop) chn->startPlayLive(); } progressBar->hide(); ui->lblA->show(); ui->lblB->show(); } } /** * @brief 视频跳转 */ void Widget::seek(QString type) { if (!isPlayback) return; for (Channel* chn : channelList) { if (chn->state == Channel::Pause || chn->state == Channel::Playback) { if (type == "forward") chn->forward(); else chn->back(); if (chn->channelName == Constant::MainChannel) progressBar->showMax(); } } } /** * @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-A口的视频为主分别从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(); // 将HDMI-A通道的视频列表作为主列表 QString dirPath = QString("%1/%2").arg(Constant::VideoPath).arg(Constant::MainChannel); fileList = Tool::getFileList(dirPath); } /** * @brief 当前视频播放完毕 */ void Widget::onPlayEnd() { // 当两路回放时,只处理一次槽函数 if (playbackMode == Constant::TwoChannelPlayback) { LinkObject* file = static_cast(sender()); for (Channel* chn : channelList) { if (chn->file == file) { if (chn->channelName != Constant::MainChannel) { return; } } } } // 计算下一个文件名 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("no file to play"); return; } 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(); } } } mutex.lock(); curFilename = curPlayFilename; condition.wakeAll(); mutex.unlock(); } /** * @brief 显示录制状态的槽函数 * @param show 是否正在录制 */ void Widget::onShowRecordLabel(bool show) { Channel* chn = static_cast(sender()); QLabel* label; if (chn->channelName == Constant::MainChannel) { label = ui->lblA; } else { label = ui->lblB; } if (show) { label->setPixmap(QPixmap(":/images/record.png")); } else label->setPixmap(QPixmap(":/images/no-record.png")); } /** * @brief 根据名称查找通道 * @param name 通道名称 */ Channel* Widget::findChannelByName(QString name) { for (Channel* chn : channelList) { if (chn->channelName == name) { return chn; } } return nullptr; }