RecordControlApplication/Widget.cpp

558 lines
15 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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 <QDir>
#include <QMutex>
#include <QScrollBar>
#include <QWaitCondition>
// 多线程中使用
QMutex mutex;
QWaitCondition condition;
QString curFilename;
extern QList<Channel*> 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<LinkObject*>(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<Channel*>(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;
}