RecordControlApplication/Channel.cpp

484 lines
14 KiB
C++
Raw Normal View History

2024-01-18 15:41:43 +08:00
#include "Channel.h"
2024-03-04 16:22:40 +08:00
#include "Constant.h"
2024-08-12 11:26:42 +08:00
#include "DatabaseManager.h"
#include "Json.h"
2024-03-04 16:22:40 +08:00
#include "Log.h"
#include "Tool.h"
#include <QCoreApplication>
#include <QElapsedTimer>
2024-03-04 16:22:40 +08:00
#include <QFileInfo>
2024-01-18 15:41:43 +08:00
#include <QProcess>
#include <QTime>
#include <chrono>
2024-01-18 15:41:43 +08:00
2024-08-12 11:26:42 +08:00
LinkObject* Channel::lineIn = nullptr;
LinkObject* Channel::lineOut = nullptr;
2024-01-18 15:41:43 +08:00
LinkObject* Channel::rtspServer = nullptr;
2024-08-12 11:26:42 +08:00
LinkObject* Channel::resample = nullptr;
LinkObject* Channel::gain = nullptr;
LinkObject* Channel::volume = nullptr;
// 这里设置最大12dB增益最小30dB增益(超过12dB会爆音)
int Channel::maxGian = 12;
int Channel::minGain = -30;
int Channel::curGain = 0;
2024-01-18 15:41:43 +08:00
extern LinkObject* vo;
extern LinkObject* vo1;
2024-08-12 11:26:42 +08:00
extern DatabaseManager* db;
2024-01-18 15:41:43 +08:00
Channel::Channel(QObject* parent)
: QObject(parent)
{
videoInput = nullptr;
videoOutput = nullptr;
2024-08-12 11:26:42 +08:00
audioInput = nullptr;
audioOutput = nullptr;
2024-01-19 09:04:21 +08:00
videoEncoder = nullptr;
2024-01-18 15:41:43 +08:00
audioEncoder = nullptr;
record = nullptr;
rtsp = nullptr;
2024-08-12 11:26:42 +08:00
inputFile = nullptr;
2024-03-04 16:22:40 +08:00
videoDecoder = nullptr;
audioDecoder = nullptr;
image = Link::create("InputImage");
2024-01-18 15:41:43 +08:00
2024-08-12 11:26:42 +08:00
if (lineIn == nullptr) {
lineIn = Link::create("InputAlsa");
2024-01-18 15:41:43 +08:00
QVariantMap dataIn;
2024-08-23 13:34:58 +08:00
dataIn["path"] = "hw:0";
dataIn["channels"] = 2;
2024-08-12 11:26:42 +08:00
lineIn->start(dataIn);
}
if (resample = nullptr) {
resample = Link::create("Resample");
resample->start();
lineIn->linkA(resample);
2024-01-18 15:41:43 +08:00
}
2024-08-12 11:26:42 +08:00
if (lineOut == nullptr) {
lineOut = Link::create("OutputAlsa");
2024-01-18 15:41:43 +08:00
QVariantMap dataOut;
dataOut["path"] = "hw:0";
2024-08-12 11:26:42 +08:00
lineOut->start(dataOut);
2024-01-18 15:41:43 +08:00
}
if (gain == nullptr) {
gain = Link::create("Gain");
gain->start();
}
if (volume == nullptr) {
volume = Link::create("Volume");
volume->start();
gain->linkA(volume);
gain->linkA(lineOut);
}
2024-01-18 15:41:43 +08:00
if (rtspServer == nullptr) {
rtspServer = Link::create("Rtsp");
rtspServer->start();
}
}
Channel::~Channel()
{
}
2024-01-18 15:41:43 +08:00
/**
2024-03-04 16:22:40 +08:00
* @brief
2024-01-18 15:41:43 +08:00
*/
void Channel::init()
{
if (channelName.isEmpty()) {
2024-03-04 16:22:40 +08:00
Log::error("channel name is empty!");
2024-01-18 15:41:43 +08:00
return;
}
2024-08-12 11:26:42 +08:00
// 通道音频输出
audioOutput = Link::create("OutputAo");
QVariantMap dataVo;
2024-03-04 16:22:40 +08:00
if (channelName == Constant::MainChannel) {
2024-01-18 15:41:43 +08:00
videoOutput = vo;
2024-08-12 11:26:42 +08:00
dataVo["interface"] = "HDMI-OUT0";
2024-01-18 15:41:43 +08:00
} else {
videoOutput = vo1;
2024-08-12 11:26:42 +08:00
dataVo["interface"] = "HDMI-OUT1";
2024-01-18 15:41:43 +08:00
}
2024-08-12 11:26:42 +08:00
loadOverlayConfig();
// 水印,用于显示录制状态
overlay = Link::create("Overlay");
overlay->start(norecordOverlay);
overlay->linkV(videoOutput);
2024-01-18 15:41:43 +08:00
2024-03-04 16:22:40 +08:00
// 视频输入
2024-01-18 15:41:43 +08:00
videoInput = Link::create("InputVi");
QVariantMap dataVi;
dataVi["interface"] = channelName;
2024-03-04 16:22:40 +08:00
dataVi["width"] = 1920;
dataVi["height"] = 1080;
2024-01-18 15:41:43 +08:00
videoInput->start(dataVi);
2024-08-12 11:26:42 +08:00
videoInput->linkV(overlay)->linkV(videoOutput);
2024-01-18 15:41:43 +08:00
2024-08-12 11:26:42 +08:00
// 通道音频输入
audioInput = Link::create("InputAi");
QVariantMap dataAi;
dataAi["interface"] = channelName;
dataAi["channels"] = 2;
audioInput->start(dataAi);
audioInput->linkA(audioOutput);
2024-03-04 16:22:40 +08:00
// 音频编码
2024-01-18 15:41:43 +08:00
audioEncoder = Link::create("EncodeA");
audioEncoder->start(audioEncoderParams);
2024-08-12 11:26:42 +08:00
// 视频编码
videoEncoder = Link::create("EncodeV");
videoEncoder->start(videoEncoderParams);
2024-03-04 16:22:40 +08:00
// 录制
2024-01-18 15:41:43 +08:00
record = Link::create("Mux");
QVariantMap dataMp4;
dataMp4["format"] = "mp4";
2024-08-12 11:26:42 +08:00
dataMp4["filecache"] = 20480000;
dataMp4["lowLatency"] = true;
2024-08-12 11:26:42 +08:00
dataMp4["thread"] = true;
dataMp4["segmentDuration"] = duration;
2024-01-18 15:41:43 +08:00
record->setData(dataMp4);
videoInput->linkV(videoEncoder)->linkV(record);
2024-08-12 11:26:42 +08:00
audioInput->linkV(audioEncoder)->linkV(record);
resample->linkA(audioEncoder)->linkA(record);
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");
// });
2024-03-04 16:22:40 +08:00
// rstp流
2024-01-18 15:41:43 +08:00
rtsp = Link::create("Mux");
QVariantMap dataRtsp;
dataRtsp["path"] = QString("mem://%1").arg(pushCode);
dataRtsp["format"] = "rtsp";
rtsp->start(dataRtsp);
videoInput->linkV(videoEncoder)->linkV(rtsp)->linkV(rtspServer);
2024-08-12 11:26:42 +08:00
audioInput->linkA(audioEncoder)->linkV(rtsp)->linkV(rtspServer);
resample->linkA(audioEncoder)->linkA(rtsp)->linkA(rtspServer);
2024-03-04 16:22:40 +08:00
// 播放文件
2024-08-12 11:26:42 +08:00
inputFile = Link::create("InputFile");
connect(inputFile, &LinkObject::newEvent, [=](QString type, QVariant msg) {
2024-03-04 16:22:40 +08:00
if (type == "EOF") {
Log::info("{} one video playback end", channelName.toStdString());
emit playEnd();
}
});
// 音频解码只在HDMI-OUT0输出音频
audioDecoder = Link::create("DecodeA");
audioDecoder->start();
if (channelName == Constant::MainChannel) {
inputFile->linkA(audioDecoder)->linkA(gain);
2024-03-04 16:22:40 +08:00
} else {
2024-08-12 11:26:42 +08:00
inputFile->linkA(audioDecoder);
2024-03-04 16:22:40 +08:00
}
// 视频解码
videoDecoder = Link::create("DecodeV");
videoDecoder->start();
2024-08-12 11:26:42 +08:00
inputFile->linkV(videoDecoder)->linkV(videoOutput);
2024-01-18 15:41:43 +08:00
}
/**
2024-03-04 16:22:40 +08:00
* @brief
2024-01-18 15:41:43 +08:00
*/
void Channel::startRecord()
{
2024-08-12 11:26:42 +08:00
// 记录本次录制开始时间以及当前视频录制的开始时间
2024-08-23 13:34:58 +08:00
QString time = QDateTime::currentDateTime().toString("yyyyMMddhhmmss");
startTime = time;
curTime = time;
2024-01-18 15:41:43 +08:00
QVariantMap dataRecord;
2024-08-12 11:26:42 +08:00
QString path = QString("%1/%2/%3_%d.mp4").arg(Constant::VideoPath).arg(channelName).arg(curTime);
2024-03-04 16:22:40 +08:00
dataRecord["path"] = path;
record->start(dataRecord);
isRecord = true;
Log::info("{} start recording...", channelName.toStdString());
2024-08-12 11:26:42 +08:00
// 显示录制状态水印
overlay->setData(recordOverlay);
}
/**
2024-08-23 13:34:58 +08:00
* @brief
2024-08-12 11:26:42 +08:00
* @param msg
* @param data
*/
void Channel::onNewEvent(QString msg, QVariant data)
{
if (msg == "newSegment") {
2024-08-23 13:34:58 +08:00
QString datetime = curTime;
// 重新设置视频的录制起始时间
curTime = QDateTime::currentDateTime().toString("yyyyMMddhhmmss");
segmentId = data.toInt();
2024-08-23 13:34:58 +08:00
// 修改文件名本次录制起始时间_%d ==> 当前视频录制起始时间
QString filename = QString("%1_%2.mp4").arg(startTime).arg(segmentId - 1);
QString newFilename = QString("%1/%2/%3.mp4").arg(Constant::VideoPath).arg(channelName).arg(datetime);
QString path = QString("%1/%2/%3").arg(Constant::VideoPath).arg(channelName).arg(filename);
QFile file(path);
if (!file.rename(newFilename)) {
Log::error("rename file name failed in function onNewEvent, old filename: {}, target filename: {} , channel name: {},reason: {}",
path.toStdString(),
newFilename.toStdString(),
channelName.toStdString(),
file.errorString().toStdString());
return;
}
// 将录制完的文件信息保存到数据库
DatabaseManager::File fileInfo;
fileInfo.channel = channelName == Constant::MainChannel
2024-08-12 11:26:42 +08:00
? DatabaseManager::MainChannel
: DatabaseManager::SecondaryChannel;
2024-08-23 13:34:58 +08:00
fileInfo.datetime = QDateTime::fromString(datetime, "yyyyMMddhhmmss").toString("yyyy-MM-dd hh:mm:ss");
fileInfo.filename = QString("%1.mp4").arg(datetime);
if (db->insert(fileInfo)) {
2024-08-12 11:26:42 +08:00
Log::info("insert one record into database success, name: {}, channel: {}",
2024-08-23 13:34:58 +08:00
fileInfo.filename.toStdString(),
2024-08-12 11:26:42 +08:00
channelName.toStdString());
} else {
Log::error("insert one record into database failed, name: {}, channel: {}",
2024-08-23 13:34:58 +08:00
fileInfo.filename.toStdString(),
2024-08-12 11:26:42 +08:00
channelName.toStdString());
}
// 更新当前录制录制视频的时间
emit appendOneVideo(channelName);
2024-08-12 11:26:42 +08:00
}
2024-01-18 15:41:43 +08:00
}
/**
2024-03-04 16:22:40 +08:00
* @brief
2024-01-18 15:41:43 +08:00
*/
void Channel::stopRecord()
{
2024-03-04 16:22:40 +08:00
Log::info("{} stop recording...", channelName.toStdString());
2024-01-18 15:41:43 +08:00
record->stop(true);
2024-08-23 13:34:58 +08:00
// 修改文件名本次录制起始时间_%d ==> 当前视频录制起始时间
QString filename = QString("%1_%2.mp4").arg(startTime).arg(segmentId);
QString newFilename = QString("%1/%2/%3.mp4").arg(Constant::VideoPath).arg(channelName).arg(curTime);
QString path = QString("%1/%2/%3").arg(Constant::VideoPath).arg(channelName).arg(filename);
QFile file(path);
if (!file.rename(newFilename)) {
Log::error("rename file name failed in function onNewEvent, old filename: {}, target filename: {} , channel name: {},reason: {}",
path.toStdString(),
newFilename.toStdString(),
channelName.toStdString(),
file.errorString().toStdString());
return;
}
2024-08-12 11:26:42 +08:00
// 将录制文件的信息存入数据库
2024-08-23 13:34:58 +08:00
DatabaseManager::File fileInfo;
fileInfo.channel = channelName == Constant::MainChannel
2024-08-12 11:26:42 +08:00
? DatabaseManager::MainChannel
: DatabaseManager::SecondaryChannel;
2024-08-23 13:34:58 +08:00
fileInfo.datetime = QDateTime::fromString(curTime, "yyyyMMddhhmmss").toString("yyyy-MM-dd hh:mm:ss");
fileInfo.filename = QString("%1.mp4").arg(curTime);
if (db->insert(fileInfo)) {
Log::info("insert one record into database success, name: {}, channel: {}",
fileInfo.filename.toStdString(),
channelName.toStdString());
} else {
Log::error("insert one record into database failed, name: {}, channel: {}",
fileInfo.filename.toStdString(),
channelName.toStdString());
}
// 更新当前录制录制视频的时间
emit appendOneVideo(channelName);
2024-08-12 11:26:42 +08:00
overlay->setData(norecordOverlay);
2024-08-23 13:34:58 +08:00
// 重置时间
startTime = "";
curTime = "";
2024-01-18 15:41:43 +08:00
}
/**
2024-03-04 16:22:40 +08:00
* @brief
* @param path
* @return /
2024-01-18 15:41:43 +08:00
*/
2024-03-04 16:22:40 +08:00
bool Channel::startPlayback(QString path)
2024-01-18 15:41:43 +08:00
{
2024-03-04 16:22:40 +08:00
QFileInfo info(path);
if (!info.exists()) {
Log::error("cannot open video {} , video file does not exist", path.toStdString());
videoInput->unLinkV(videoOutput);
QVariantMap dataImage;
dataImage["path"] = Constant::EmptyImagePath;
image->start(dataImage);
image->linkV(videoOutput);
state = Error;
return false;
}
2024-01-18 15:41:43 +08:00
2024-03-04 16:22:40 +08:00
// 开始回放
2024-01-18 15:41:43 +08:00
QVariantMap dataFile;
dataFile["path"] = path;
2024-08-12 11:26:42 +08:00
dataFile["sync"] = true;
inputFile->start(dataFile);
2024-01-18 15:41:43 +08:00
2024-03-04 16:22:40 +08:00
// 判断视频是否损坏,如果损坏则输出提示图片
2024-08-12 11:26:42 +08:00
int duration = inputFile->invoke("getDuration", path).toInt();
2024-03-04 16:22:40 +08:00
if (duration == 0) {
Log::error("cannot open video {}, video file was corrupted", path.toStdString());
2024-08-12 11:26:42 +08:00
inputFile->stop();
2024-03-04 16:22:40 +08:00
videoInput->unLinkV(videoOutput);
QVariantMap dataImage;
dataImage["path"] = Constant::ErrorImagePath;
image->start(dataImage);
image->linkV(videoOutput);
state = Error;
return false;
}
2024-01-18 15:41:43 +08:00
2024-08-12 11:26:42 +08:00
// 断开视频信号输出,启动回放输出
overlay->unLinkV(videoOutput);
2024-03-04 16:22:40 +08:00
videoDecoder->linkV(videoOutput);
2024-08-12 11:26:42 +08:00
// 断开音频信号输出,启动回放输出
audioInput->unLinkA(audioOutput);
audioDecoder->linkA(gain);
2024-08-12 11:26:42 +08:00
2024-03-04 16:22:40 +08:00
playbackDuration = duration;
state = Playback;
return true;
}
2024-01-19 09:04:21 +08:00
/**
2024-03-04 16:22:40 +08:00
* @brief
2024-01-19 09:04:21 +08:00
*/
void Channel::startPlayLive()
{
2024-03-04 16:22:40 +08:00
if (state == Playback) {
videoDecoder->unLinkV(videoOutput);
2024-08-12 11:26:42 +08:00
audioDecoder->unLinkA(audioOutput);
inputFile->stop(true);
2024-03-04 16:22:40 +08:00
} else if (state == Error) {
image->unLinkV(videoOutput);
image->stop(true);
}
2024-08-12 11:26:42 +08:00
// 打开视频和音频输出
overlay->linkV(videoOutput);
audioInput->linkA(audioOutput);
// 关闭外部音频输出
audioDecoder->unLinkA(gain);
2024-03-04 16:22:40 +08:00
state = Stop;
2024-01-19 09:04:21 +08:00
}
2024-01-18 15:41:43 +08:00
/**
2024-03-04 16:22:40 +08:00
* @brief 退10s
2024-01-18 15:41:43 +08:00
*/
void Channel::back()
{
2024-03-04 16:22:40 +08:00
Log::info("{} back 10s", channelName.toStdString());
2024-08-12 11:26:42 +08:00
int curPos = inputFile->invoke("getPosition").toInt();
curPos -= 10 * 1000;
2024-08-12 11:26:42 +08:00
inputFile->invoke("seek", curPos);
2024-01-18 15:41:43 +08:00
}
/**
2024-03-04 16:22:40 +08:00
* @brief 10s
2024-01-18 15:41:43 +08:00
*/
void Channel::forward()
{
2024-03-04 16:22:40 +08:00
Log::info("{} forward 10s", channelName.toStdString());
2024-08-12 11:26:42 +08:00
int curPos = inputFile->invoke("getPosition").toInt();
curPos += 10 * 1000;
2024-08-12 11:26:42 +08:00
inputFile->invoke("seek", curPos);
2024-01-18 15:41:43 +08:00
}
/**
2024-03-04 16:22:40 +08:00
* @brief
2024-01-18 15:41:43 +08:00
*/
void Channel::togglePause()
{
2024-03-04 16:22:40 +08:00
if (state == Stop || state == Error)
return;
if (state == Playback)
state = Pause;
else
state = Playback;
2024-08-12 11:26:42 +08:00
inputFile->invoke("pause", state == Pause);
2024-01-18 15:41:43 +08:00
}
/**
* @brief
2024-01-18 15:41:43 +08:00
*/
void Channel::showFinishPromot()
2024-01-18 15:41:43 +08:00
{
videoInput->unLinkV(videoDecoder);
QVariantMap dataImage;
dataImage["path"] = Constant::FinishImagePath;
image->start(dataImage);
image->linkV(videoOutput);
state = Finish;
2024-08-12 11:26:42 +08:00
}
/**
* @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);
Log::info("current volumn gain: {}dB", curGain);
2024-08-12 11:26:42 +08:00
}
/**
* @brief
*/
void Channel::volumeDown()
{
if (curGain > minGain)
curGain -= 6;
QVariantMap data;
data["gain"] = curGain;
gain->setData(data);
Log::info("current volumn gain: {}dB", curGain);
2024-08-12 11:26:42 +08:00
}
/**
* @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);
}