RecordControlApplication/Channel.cpp

290 lines
7.2 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 "Channel.h"
#include "Constant.h"
#include "Log.h"
#include "Tool.h"
#include <QFileInfo>
#include <QProcess>
#include <QTime>
LinkObject* Channel::audioInput = nullptr;
LinkObject* Channel::audioOutput = nullptr;
LinkObject* Channel::rtspServer = nullptr;
extern LinkObject* vo;
extern LinkObject* vo1;
Channel::Channel(QObject* parent)
: QObject(parent)
{
timer = new QTimer();
timer->setInterval(duration);
timer->stop();
videoInput = nullptr;
videoOutput = nullptr;
videoEncoder = nullptr;
audioEncoder = nullptr;
record = nullptr;
rtsp = nullptr;
file = nullptr;
videoDecoder = nullptr;
audioDecoder = nullptr;
image = Link::create("InputImage");
if (audioInput == nullptr) {
audioInput = Link::create("InputAlsa");
QVariantMap dataIn;
dataIn["path"] = "hw:0,0";
dataIn["channels"] = 2;
audioInput->start(dataIn);
}
if (audioOutput == nullptr) {
audioOutput = Link::create("OutputAlsa");
QVariantMap dataOut;
dataOut["path"] = "hw:0,0";
audioOutput->start(dataOut);
}
if (rtspServer == nullptr) {
rtspServer = Link::create("Rtsp");
rtspServer->start();
}
connect(timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
}
/**
* @brief 初始化
*/
void Channel::init()
{
if (channelName.isEmpty()) {
Log::error("channel name is empty!");
return;
}
if (channelName == Constant::MainChannel) {
videoOutput = vo;
} else {
videoOutput = vo1;
}
// 视频输入
videoInput = Link::create("InputVi");
QVariantMap dataVi;
dataVi["interface"] = channelName;
dataVi["width"] = 1920;
dataVi["height"] = 1080;
videoInput->start(dataVi);
videoInput->linkV(videoOutput);
// 视频编码
videoEncoder = Link::create("EncodeV");
videoEncoder->start(videoEncoderParams);
// 音频编码
audioEncoder = Link::create("EncodeA");
audioEncoder->start(audioEncoderParams);
// 录制
record = Link::create("Mux");
QVariantMap dataMp4;
dataMp4["format"] = "mp4";
// dataMp4["lowLatency"] = true;
// dataMp4["filecache"] = 20480000;
record->setData(dataMp4);
videoInput->linkV(videoEncoder)->linkV(record);
audioInput->linkA(audioEncoder)->linkA(record);
// rstp流
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);
audioInput->linkA(audioEncoder)->linkA(rtsp)->linkA(rtspServer);
// 播放文件
file = Link::create("InputFile");
connect(file, &LinkObject::newEvent, [=](QString type, QVariant msg) {
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) {
file->linkA(audioDecoder)->linkA(audioOutput);
} else {
file->linkA(audioDecoder);
}
// 视频解码
videoDecoder = Link::create("DecodeV");
videoDecoder->start();
file->linkV(videoDecoder)->linkV(videoOutput);
}
/**
* @brief 一段录制完毕
*/
void Channel::onTimeout()
{
while (int secs = QTime::currentTime().second() % 60 != 0) {
}
QString curTime = QDateTime::currentDateTime().toString("yyyyMMddhhmm");
QString path = QString("%1/%2/%3.mp4").arg(Constant::VideoPath).arg(channelName).arg(curTime);
// 重新分片
record->invoke("segment", path);
}
/**
* @brief 开始录制
*/
void Channel::startRecord()
{
if (!isMountDisk()) {
Log::error("there are no disk mounted");
return;
}
while (int secs = QTime::currentTime().second() % 60 != 0) {
}
QString curTime = QDateTime::currentDateTime().toString("yyyyMMddhhmm");
QVariantMap dataRecord;
QString path = QString("%1/%2/%3.mp4").arg(Constant::VideoPath).arg(channelName).arg(curTime);
dataRecord["path"] = path;
record->start(dataRecord);
isRecord = true;
Log::info("{} start recording...", channelName.toStdString());
timer->start();
emit showRecordState(true);
}
/**
* @brief 停止录制
*/
void Channel::stopRecord()
{
Log::info("{} stop recording...", channelName.toStdString());
record->stop(true);
timer->stop();
emit showRecordState(false);
}
/**
* @brief 开始回放
* @param path 路径
* @return 成功/失败
*/
bool Channel::startPlayback(QString path)
{
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->setData(dataImage);
image->start(dataImage);
image->linkV(videoOutput);
state = Error;
return false;
}
// 开始回放
QVariantMap dataFile;
dataFile["path"] = path;
dataFile["async"] = false;
file->start(dataFile);
// 判断视频是否损坏,如果损坏则输出提示图片
int duration = file->invoke("getDuration", path).toInt() / 1000;
if (duration == 0) {
Log::error("cannot open video {}, video file was corrupted", path.toStdString());
file->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);
videoDecoder->linkV(videoOutput);
playbackDuration = duration;
state = Playback;
return true;
}
/**
* @brief 播放直播
*/
void Channel::startPlayLive()
{
if (state == Playback) {
videoDecoder->unLinkV(videoOutput);
file->stop(true);
} else if (state == Error) {
image->unLinkV(videoOutput);
image->stop(true);
}
videoInput->linkV(videoOutput);
state = Stop;
}
/**
* @brief 后退10s
*/
void Channel::back()
{
Log::info("{} back 10s", channelName.toStdString());
qDebug() << channelName << "back 10s";
int curPos = file->invoke("getPosition").toInt();
curPos -= 10 * 1000;
file->invoke("seek", curPos);
}
/**
* @brief 快进10s
*/
void Channel::forward()
{
Log::info("{} forward 10s", channelName.toStdString());
int curPos = file->invoke("getPosition").toInt();
curPos += 10 * 1000;
file->invoke("seek", curPos);
}
/**
* @brief 暂停
*/
void Channel::togglePause()
{
if (state == Stop || state == Error)
return;
if (state == Playback)
state = Pause;
else
state = Playback;
file->invoke("pause", state == Pause);
}
/**
* @brief 判断是否挂载磁盘
* @return
*/
bool Channel::isMountDisk()
{
QString info = Tool::writeCom(QString("df %1").arg(Constant::MountedPath));
if (!info.contains(Constant::MountedPath))
return false;
return true;
}