RecordControlApplication/Widget.cpp

683 lines
20 KiB
C++
Executable File
Raw Permalink 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 "SerialPortTool.h"
#include "TcpServer.h"
#include "Tool.h"
#include "ui_Widget.h"
#include <QApplication>
#include <QDesktopWidget>
#include <QDir>
#include <QMutex>
#include <QScrollBar>
#include <QWaitCondition>
// 认为两个视频是连续的时间间隔阈值
#define TIME_GAP_THRRSHOLD 2
// 多线程中使用
QMutex mutex;
QWaitCondition condition;
QString curFilename;
extern QList<Channel*> channelList;
extern Constant::PlaybackMode playbackMode;
extern SerialPortTool* serialPortTool;
Widget::Widget(QWidget* parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
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();
ui->timeSlider->hide();
ui->timeSlider->setOpacity(0.5);
menu = new Menu();
menu->hide();
// 设置是否显示通道选择
menu->setChannelSelectVisible(playbackMode == Constant::OneChannelPlayback);
connect(menu, SIGNAL(btnPlaybackClicked(QVariantMap, int)), this, SLOT(onBtnPlaybackClicked(QVariantMap, int)));
connect(menu, SIGNAL(resolutionOutAChanged(int)), this, SLOT(onResolutionOutAChanged(int)));
connect(menu, SIGNAL(resolutionOutBChanged(int)), this, SLOT(onResolutionOutBChanged(int)));
// 每5秒更新一次进度条
progressTimer = new QTimer();
progressTimer->setInterval(200);
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)));
connect(chn, SIGNAL(appendOneVideo()), this, SLOT(onAppendVideo()));
// 监听输入信号的变化
connect(chn->videoInput, &LinkObject::newEvent, [=](QString type, QVariant msg) {
if (type == "signal") {
QVariantMap data = msg.toMap();
bool available = data["avalible"].toBool();
if (available) {
Log::info("video input {} is available",
chn->channelName == Constant::MainChannel
? Constant::MainChannel
: Constant::SecondaryChannel);
if (chn->channelName == Constant::MainChannel) {
serialPortTool->onMainChannelOn();
QThread::msleep(100);
} else {
serialPortTool->onSecondaryChannelOn();
QThread::msleep(100);
}
} else {
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);
} else {
serialPortTool->onSecondaryChannelOff();
QThread::msleep(100);
}
}
}
});
}
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(btnVolumnUpClicked()), this, SLOT(onBtnVolumnUpClicked()));
connect(serialPortTool, SIGNAL(btnVolumnDownClicked()), this, SLOT(onBtnVolumnDownClicked()));
}
Widget::~Widget()
{
delete ui;
delete progressTimer;
// delete menu;
}
/**
* @brief 实时获取当前回放视频的位置
*/
void Widget::onProgressTimeout()
{
// 获取当前回放的通道名称
QString channelName;
// 如果时一路回放则取当前回放的时候
if (playbackMode == Constant::OneChannelPlayback) {
DatabaseManager::Channel curChn = static_cast<DatabaseManager::Channel>(playbackParams.value("channel", 1).toInt());
channelName = curChn == DatabaseManager::MainChannel
? Constant::MainChannel
: Constant::SecondaryChannel;
}
// 否则取主通道
else {
channelName = Constant::MainChannel;
}
Channel* chn = findChannelByName(channelName);
if (chn) {
// 获取当前播放位置单位s
int pos = chn->inputFile->invoke("getPosition").toInt() / 1000;
int cur = segments[curSegmentIndex].startTime + pos;
ui->timeSlider->setCurrent(cur);
}
}
/**
* @brief 按下菜单按键
*/
void Widget::onBtnMenuClicked()
{
if (!menu->isVisible()) {
menu->show();
menu->moveToCenter();
}
}
/**
* @brief 按下返回按键。优先级:菜单 > 播放
*/
void Widget::onBtnReturnClicked()
{
// 关闭菜单
if (menu->isVisible()) {
menu->hide();
return;
}
// 关闭回放
if (isPlayback) {
// 停止回放
for (Channel* chn : channelList) {
if (chn->state != Channel::Stop) {
chn->startPlayLive();
serialPortTool->onPlaybackEnd();
}
}
progressTimer->stop();
isPlayback = false;
ui->timeSlider->hide();
}
}
/**
* @brief 按下上键。优先级: 菜单 > 播放
*/
void Widget::onBtnUpClicked()
{
if (menu->isVisible()) {
menu->move(Menu::Up);
return;
}
// 时间轴选中移动到上一个视频的开始
if (isPlayback) {
playPreSegment();
}
}
/**
* @brief 按下下键。 优先级: 菜单 > 播放
*/
void Widget::onBtnDownClicked()
{
if (menu->isVisible()) {
menu->move(Menu::Down);
return;
}
// 时间轴移动到下一个视频
if (isPlayback) {
playNextSegemnt();
}
}
/**
* @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) {
if (chn->state == Channel::Pause) {
// 打开暂停灯
serialPortTool->onPlayPause();
} else {
// 关闭暂停灯
serialPortTool->onPlayResume();
}
}
}
}
}
}
/**
* @brief 增大音量
*/
void Widget::onBtnVolumnUpClicked()
{
Channel::volumeUp();
}
/**
* @brief 减小音量
*/
void Widget::onBtnVolumnDownClicked()
{
Channel::volumeDown();
}
/**
* @brief 播放视频
* @param params 参数(通道,时间)
* @param segmentIndex 时间片索引
*/
void Widget::onBtnPlaybackClicked(QVariantMap params, int segmentIndex)
{
if (segmentIndex < 0) {
return;
}
// 获取当前通道
segments = Tool::calTimeSegments(params);
if (segmentIndex > segments.length() - 1) {
return;
}
playbackParams = params;
curSegmentIndex = segmentIndex;
isPlayback = true;
// 显示时间轴
ui->timeSlider->show();
ui->timeSlider->setTimeSegments(segments);
ui->timeSlider->setCurrent(segments[segmentIndex].startTime);
QString filename = segments[segmentIndex].filename;
// 开始回放
if (playbackMode == Constant::OneChannelPlayback) {
playOneChannel(filename);
} else {
playTwoChannels(filename);
}
}
/**
* @brief 通道A分辨率修改
* @param resolution 分辨率字符串
*/
void Widget::onResolutionOutAChanged(int resolution)
{
for (Channel* chn : channelList) {
if (chn->channelName == Constant::MainChannel) {
chn->changeOutputResolution(static_cast<Channel::Resolution>(resolution));
switch (resolution) {
case 0:
setFixedSize(1920, 1080);
break;
case 1:
setFixedSize(1600, 1200);
break;
case 2:
setFixedSize(1280, 1024);
break;
case 3:
setFixedSize(1024, 768);
break;
case 4:
setFixedSize(800, 600);
break;
default:
break;
}
menu->restartUI();
menu->update();
update();
}
}
}
/**
* @brief 通道B分辨率修改
* @param resolution
*/
void Widget::onResolutionOutBChanged(int resolution)
{
for (Channel* chn : channelList) {
if (chn->channelName == Constant::SecondaryChannel) {
chn->changeOutputResolution(static_cast<Channel::Resolution>(resolution));
}
}
}
void Widget::onResolutionInAChanged(int width, int height)
{
for (Channel* chn : channelList) {
if (chn->channelName == Constant::MainChannel) {
chn->changeInputResolution(width, height);
}
}
}
void Widget::onResolutionInBChanged(int width, int height)
{
for (Channel* chn : channelList) {
if (chn->channelName == Constant::SecondaryChannel) {
chn->changeInputResolution(width, height);
}
}
}
/**
* @brief 一路回放将当前选中播放的视频从HDMI-OUT0口输出
*/
void Widget::playOneChannel(QString filename)
{
// 获取当前回放的通道
QString curPlayChannel = menu->getCurPlayChannel() == DatabaseManager::MainChannel
? Constant::MainChannel
: Constant::SecondaryChannel;
QString path = QString("%1/%2/%3").arg(Constant::VideoPath).arg(curPlayChannel).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");
}
isPlayback = true;
// 全局保存当前播放的视频的文件名
mutex.lock();
curFilename = filename;
condition.wakeAll();
mutex.unlock();
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) {
if (progressTimer->isActive()) {
progressTimer->stop();
progressTimer->start();
} else {
progressTimer->start();
}
ui->recordWidget->hide();
} else {
ui->recordWidget->hide();
}
}
}
isPlayback = true;
// 全局保存正在播放的视频的文件名
mutex.lock();
curFilename = filename;
condition.wakeAll();
mutex.unlock();
}
/**
* @brief 视频跳转
*/
void Widget::seek(QString type)
{
if (!isPlayback)
return;
QString channelName;
if (playbackMode == Constant::OneChannelPlayback) {
DatabaseManager::Channel curChn = static_cast<DatabaseManager::Channel>(playbackParams.value("channel", 1).toInt());
channelName = curChn == DatabaseManager::MainChannel
? Constant::MainChannel
: Constant::SecondaryChannel;
} else {
channelName = Constant::MainChannel;
}
Channel* chn = findChannelByName(channelName);
int pos;
// 获取当前播放的位置
if (chn) {
pos = chn->inputFile->invoke("getPosition").toInt() / 1000;
}
if (type == "forward") {
pos += 10;
// 目标位置超过当前视频的时长
if (pos > segments[curSegmentIndex].duration) {
// 如果是最后一个视频,则跳转到视频最后为止
if (curSegmentIndex == segments.length() - 1) {
pos = segments[curSegmentIndex].duration;
}
// 如果不是最后一个视频
else {
int gap = segments[curSegmentIndex + 1].startTime
- segments[curSegmentIndex].startTime
- segments[curSegmentIndex].duration;
// 视频是连续的就跳转到响应为止认为连续的条件是间隔小于2S
if (gap <= TIME_GAP_THRRSHOLD) {
pos = pos - segments[curSegmentIndex].duration - gap;
curSegmentIndex++;
QString filename = segments[curSegmentIndex].filename;
// 开始回放
if (playbackMode == Constant::OneChannelPlayback) {
playOneChannel(filename);
} else {
playTwoChannels(filename);
}
}
// 视频不是连续的
else {
pos = segments[curSegmentIndex].duration;
}
}
}
} else {
pos -= 10;
// 目标位置小于0
if (pos < 0) {
if (curSegmentIndex == 0) {
pos = 0;
} else {
int gap = segments[curSegmentIndex].startTime
- segments[curSegmentIndex - 1].startTime
- segments[curSegmentIndex - 1].duration;
// 视频是连续的
if (gap <= TIME_GAP_THRRSHOLD) {
pos = segments[curSegmentIndex - 1].duration + gap + pos;
curSegmentIndex--;
QString filename = segments[curSegmentIndex].filename;
// 重新回放
if (playbackMode == Constant::OneChannelPlayback) {
playOneChannel(filename);
} else {
playTwoChannels(filename);
}
}
// 视频不是连续的
else {
pos = 0;
}
}
}
}
for (Channel* chn : channelList) {
if (chn->state == Channel::Playback) {
chn->inputFile->invoke("seek", pos * 1000);
Log::info("{} seek {} 10s", chn->channelName.toStdString(), type.toStdString());
}
}
}
/**
* @brief 当前视频播放完毕
*/
void Widget::onPlayEnd()
{
// 停下回放计时器
progressTimer->stop();
// 当两路回放时,只处理一次槽函数
if (playbackMode == Constant::TwoChannelPlayback) {
LinkObject* file = static_cast<LinkObject*>(sender());
for (Channel* chn : channelList) {
if (chn->inputFile == file) {
if (chn->channelName != Constant::MainChannel) {
return;
}
}
}
}
// 如果时列表最后一个视频则显示播放结束
if (curSegmentIndex == segments.length() - 1) {
for (Channel* chn : channelList) {
chn->showFinishPromot();
}
}
// 不是列表最后一个就播放下一个视频
else {
curSegmentIndex++;
ui->timeSlider->setCurrent(segments[curSegmentIndex].startTime);
QString filename = segments[curSegmentIndex].filename;
if (playbackMode == Constant::OneChannelPlayback) {
playOneChannel(filename);
} else {
playTwoChannels(filename);
}
}
}
/**
* @brief 显示录制状态的槽函数
* @param show 是否正在录制
*/
void Widget::onShowRecordLabel(bool show)
{
Channel* chn = static_cast<Channel*>(sender());
QLabel* lblImg;
QLabel* lblTxt;
if (chn->channelName == Constant::MainChannel) {
lblImg = ui->lblImgA;
lblTxt = ui->lblTxtA;
} else {
lblImg = ui->lblImgB;
lblTxt = ui->lblTxtB;
}
if (show) {
lblImg->setPixmap(QPixmap(":/images/record.png"));
lblTxt->setText("录制中");
} else {
lblImg->setPixmap(QPixmap(":/images/no-record.png"));
lblTxt->setText("未录制");
}
}
/**
* @brief 根据名称查找通道
* @param name 通道名称
*/
Channel* Widget::findChannelByName(QString name)
{
for (Channel* chn : channelList) {
if (chn->channelName == name) {
return chn;
}
}
return nullptr;
}
/**
* @brief 播放下一个视频片段
*/
void Widget::playNextSegemnt()
{
if (curSegmentIndex < segments.length() - 1) {
curSegmentIndex++;
// 设置时间轴指针的位置
int cur = segments[curSegmentIndex].startTime;
ui->timeSlider->setCurrent(cur);
// 跳转到下一个视频
QString filename = segments[curSegmentIndex].filename;
if (playbackMode == Constant::OneChannelPlayback) {
playOneChannel(filename);
} else {
playTwoChannels(filename);
}
Log::info("play next segment, filename: {}", filename.toStdString());
}
}
/**
* @brief 播放上一个视频片段
*/
void Widget::playPreSegment()
{
if (curSegmentIndex > 0) {
curSegmentIndex--;
// 设置时间轴指针的位置
int cur = segments[curSegmentIndex].startTime;
ui->timeSlider->setCurrent(cur);
// 跳转到下一个视频
QString filename = segments[curSegmentIndex].filename;
if (playbackMode == Constant::OneChannelPlayback) {
playOneChannel(filename);
} else {
playTwoChannels(filename);
}
Log::info("play previous segment, filename: {}", filename.toStdString());
}
}
/**
* @brief 一个录制片段完成的槽函数
*/
void Widget::onAppendVideo()
{
if (!isPlayback) {
return;
}
Channel* chn = static_cast<Channel*>(sender());
// 两路回放需要是主通道
if (playbackMode == Constant::TwoChannelPlayback) {
if (chn->channelName != Constant::MainChannel) {
return;
}
}
// 一路回放需要回放通道和录制通道一致
else {
DatabaseManager::Channel c = static_cast<DatabaseManager::Channel>(playbackParams.value("channel", 1).toInt());
QString channelName = c == DatabaseManager::MainChannel
? Constant::MainChannel
: Constant::SecondaryChannel;
if (chn->channelName != channelName) {
return;
}
}
// 刷新时间轴中时间片的显示
segments = Tool::calTimeSegments(playbackParams);
ui->timeSlider->setTimeSegments(segments);
}
void Widget::update()
{
QRect deskRect = QApplication::desktop()->availableGeometry();
qDebug() << "desktop rect:" << deskRect;
QWidget::update();
}