279 lines
7.2 KiB
C++
Executable File
279 lines
7.2 KiB
C++
Executable File
#include "Channel.h"
|
||
#include "Constant.h"
|
||
#include "Log.h"
|
||
#include "Tool.h"
|
||
#include <QCoreApplication>
|
||
#include <QFileInfo>
|
||
#include <QProcess>
|
||
#include <QTime>
|
||
#include <chrono>
|
||
|
||
LinkObject* Channel::audioInput = nullptr;
|
||
LinkObject* Channel::audioOutput = nullptr;
|
||
LinkObject* Channel::rtspServer = nullptr;
|
||
QString Channel::curRecordFilename = "";
|
||
|
||
extern LinkObject* vo;
|
||
extern LinkObject* vo1;
|
||
|
||
Channel::Channel(QObject* parent)
|
||
: QObject(parent)
|
||
{
|
||
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();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @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["segmentDuration"] = duration;
|
||
dataMp4["lowLatency"] = true;
|
||
// dataMp4["filecache"] = 20480000;
|
||
record->setData(dataMp4);
|
||
videoInput->linkV(videoEncoder)->linkV(record);
|
||
audioInput->linkA(audioEncoder)->linkA(record);
|
||
|
||
connect(record, &LinkObject::newEvent, [=](QString type, QVariant data) {
|
||
if (type == "newSegment") {
|
||
int id = data.toInt();
|
||
curRecordFilename = QString("%1_%2.mp4")
|
||
.arg(curRecordFilename.split("_")[0])
|
||
.arg(id, 2, 10, QLatin1Char('0'));
|
||
}
|
||
});
|
||
|
||
// 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::startRecord()
|
||
{
|
||
// 阻塞到整分钟才开始录制,方便上位机进行回放
|
||
while (int secs = QTime::currentTime().second() % 60 != 0) {
|
||
QCoreApplication::processEvents();
|
||
}
|
||
QString curTime = QDateTime::currentDateTime().toString("yyyyMMddhhmm");
|
||
curRecordFilename = curTime + "_00.mp4";
|
||
QVariantMap dataRecord;
|
||
QString path = QString("%1/%2/%3.mp4").arg(Constant::VideoPath).arg(channelName).arg(curTime + "_%02d");
|
||
dataRecord["path"] = path;
|
||
record->start(dataRecord);
|
||
isRecord = true;
|
||
Log::info("{} start recording...", channelName.toStdString());
|
||
emit showRecordState(true);
|
||
}
|
||
|
||
/**
|
||
* @brief 停止录制
|
||
*/
|
||
void Channel::stopRecord()
|
||
{
|
||
Log::info("{} stop recording...", channelName.toStdString());
|
||
record->stop(true);
|
||
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());
|
||
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 显示播放结束提示
|
||
*/
|
||
void Channel::showFinishPromot()
|
||
{
|
||
videoInput->unLinkV(videoDecoder);
|
||
QVariantMap dataImage;
|
||
dataImage["path"] = Constant::FinishImagePath;
|
||
image->setData(dataImage);
|
||
image->start(dataImage);
|
||
image->linkV(videoOutput);
|
||
state = Finish;
|
||
} |