309 lines
7.6 KiB
C++
309 lines
7.6 KiB
C++
#include "Channel.h"
|
|
#include <QProcess>
|
|
#include <QTime>
|
|
|
|
LinkObject* Channel::audioInput = nullptr;
|
|
LinkObject* Channel::audioOutput = nullptr;
|
|
LinkObject* Channel::rtspServer = nullptr;
|
|
LinkObject* Channel::file = nullptr;
|
|
LinkObject* Channel::audioDecoder = nullptr;
|
|
LinkObject* Channel::videoDecoder = nullptr;
|
|
|
|
extern LinkObject* vo;
|
|
extern LinkObject* vo1;
|
|
extern const char* VideoPath;
|
|
extern const char* SnapPath;
|
|
|
|
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;
|
|
snap = Link::create("EncodeV");
|
|
overLay = Link::create("Overlay");
|
|
|
|
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();
|
|
}
|
|
if (file == nullptr) {
|
|
file = Link::create("InputFile");
|
|
// one video playback end
|
|
connect(file, &LinkObject::newEvent, [=](QString type, QVariant) {
|
|
if (type == "EOF") {
|
|
qDebug() << "one video playback end";
|
|
emit playEnd();
|
|
}
|
|
});
|
|
}
|
|
if (audioDecoder == nullptr) {
|
|
audioDecoder = Link::create("DecodeA");
|
|
audioDecoder->start();
|
|
file->linkA(audioDecoder)->linkA(audioOutput);
|
|
}
|
|
if (videoDecoder == nullptr) {
|
|
videoDecoder = Link::create("DecodeV");
|
|
videoDecoder->start();
|
|
file->linkV(videoDecoder);
|
|
}
|
|
|
|
connect(timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
|
|
}
|
|
|
|
/**
|
|
* @brief load configuration and init components
|
|
*/
|
|
void Channel::init()
|
|
{
|
|
if (channelName.isEmpty()) {
|
|
qDebug() << "channel name is empty!";
|
|
return;
|
|
}
|
|
if (channelName == "HDMI-A") {
|
|
videoOutput = vo;
|
|
} else {
|
|
videoOutput = vo1;
|
|
}
|
|
|
|
// init video input by channel name
|
|
videoInput = Link::create("InputVi");
|
|
QVariantMap dataVi;
|
|
dataVi["interface"] = channelName;
|
|
dataVi["width"] = 1280;
|
|
dataVi["height"] = 1024;
|
|
videoInput->start(dataVi);
|
|
connect(videoInput, &LinkObject::newEvent, [=](QString type, QVariant msg) {
|
|
if (type == "signal") {
|
|
QVariantMap data = msg.toMap();
|
|
bool avalible = data["avalible"].toBool();
|
|
if (avalible) {
|
|
int width = data["width"].toInt();
|
|
int height = data["height"].toInt();
|
|
emit signalInputed(channelName, width, height);
|
|
}
|
|
}
|
|
});
|
|
|
|
// start water mask
|
|
// overLay->start();
|
|
|
|
// link video input and output, and add water mask
|
|
videoInput->linkV(videoOutput);
|
|
|
|
// capture picture from video
|
|
QVariantMap dataSnap;
|
|
dataSnap["codec"] = "jpeg";
|
|
dataSnap["snap"] = true;
|
|
dataSnap["width"] = 1920;
|
|
dataSnap["height"] = 1080;
|
|
snap->start(dataSnap);
|
|
videoInput->linkV(snap);
|
|
// sleep 1s, if didnt sleep , function snap would return flase and capture failed
|
|
QThread::sleep(1);
|
|
|
|
// video encode
|
|
videoEncoder = Link::create("EncodeV");
|
|
videoEncoder->start(videoEncoderParams);
|
|
|
|
// audio encode
|
|
audioEncoder = Link::create("EncodeA");
|
|
audioEncoder->start(audioEncoderParams);
|
|
|
|
// record
|
|
record = Link::create("Mux");
|
|
QVariantMap dataMp4;
|
|
dataMp4["format"] = "mp4";
|
|
record->setData(dataMp4);
|
|
videoInput->linkV(videoEncoder)->linkV(record);
|
|
audioInput->linkA(audioEncoder)->linkA(record);
|
|
|
|
// rtsp
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* @brief end previous record and start new record
|
|
*/
|
|
void Channel::onTimeout()
|
|
{
|
|
record->stop(true);
|
|
timer->stop();
|
|
|
|
if (!isMountDisk()) {
|
|
return;
|
|
}
|
|
QString curTime = QDateTime::currentDateTime().toString("yyyy-MM-dd_hh:mm:ss");
|
|
QVariantMap dataRecord;
|
|
dataRecord["path"] = QString("%1/%2/%3.mp4").arg(VideoPath).arg(channelName).arg(curTime);
|
|
record->setData(dataRecord);
|
|
record->start();
|
|
|
|
timer->start();
|
|
snap->invoke("snapSync", QString("%1/%2/%3.jpg").arg(SnapPath).arg(channelName).arg(curTime));
|
|
}
|
|
|
|
/**
|
|
* @brief start record
|
|
*/
|
|
void Channel::startRecord()
|
|
{
|
|
if (!isMountDisk()) {
|
|
return;
|
|
}
|
|
QString curTime = QDateTime::currentDateTime().toString("yyyy-MM-dd_hh:mm:ss");
|
|
QVariantMap dataRecord;
|
|
// /usb/root/videos/HDMI-A/2024-1-1_10:00:00
|
|
dataRecord["path"] = QString("%1/%2/%3.mp4").arg(VideoPath).arg(channelName).arg(curTime);
|
|
record->setData(dataRecord);
|
|
record->start();
|
|
// setOverlay("Record");
|
|
|
|
timer->start();
|
|
snap->invoke("snapSync", QString("%1/%2/%3.jpg").arg(SnapPath).arg(channelName).arg(curTime));
|
|
}
|
|
|
|
/**
|
|
* @brief stop record
|
|
*/
|
|
void Channel::stopRecord()
|
|
{
|
|
record->stop(true);
|
|
// setOverlay("No Record");
|
|
timer->stop();
|
|
}
|
|
|
|
/**
|
|
* @brief start playback and output hdmi signals
|
|
* @param fileName
|
|
*/
|
|
void Channel::startPlayback(QString fileName)
|
|
{
|
|
QString path = QString("%1/%2/%3").arg(VideoPath).arg(channelName).arg(fileName);
|
|
|
|
// break video input and output
|
|
videoInput->unLinkV(videoOutput);
|
|
|
|
videoDecoder->linkV(videoOutput);
|
|
|
|
QVariantMap dataFile;
|
|
dataFile["path"] = path;
|
|
dataFile["async"] = false;
|
|
file->start(dataFile);
|
|
|
|
isPlayback = true;
|
|
}
|
|
|
|
/**
|
|
* @brief get video duration of current channel
|
|
* @param fileName
|
|
* @return
|
|
*/
|
|
int Channel::getDuration(QString fileName)
|
|
{
|
|
QString path = QString("%1/%2/%3").arg(VideoPath).arg(channelName).arg(fileName);
|
|
int duration = file->invoke("getDuration", path).toInt();
|
|
return duration;
|
|
}
|
|
|
|
/**
|
|
* @brief play live
|
|
*/
|
|
void Channel::startPlayLive()
|
|
{
|
|
videoDecoder->unLinkV(videoOutput);
|
|
file->stop(true);
|
|
|
|
videoInput->linkV(videoOutput);
|
|
isPlayback = false;
|
|
}
|
|
|
|
/**
|
|
* @brief playback -10s
|
|
*/
|
|
void Channel::back()
|
|
{
|
|
qDebug() << channelName << "back 10s";
|
|
int curPos = file->invoke("getPosition").toInt();
|
|
curPos -= 10 * 1000;
|
|
file->invoke("seek", curPos);
|
|
}
|
|
|
|
/**
|
|
* @brief playback +10s
|
|
*/
|
|
void Channel::forward()
|
|
{
|
|
qDebug() << channelName << "forward 10s";
|
|
int curPos = file->invoke("getPosition").toInt();
|
|
curPos += 10 * 1000;
|
|
file->invoke("seek", curPos);
|
|
}
|
|
|
|
/**
|
|
* @brief Channel::togglePause
|
|
*/
|
|
void Channel::togglePause()
|
|
{
|
|
isPause = !isPause;
|
|
file->invoke("pause", isPause);
|
|
}
|
|
|
|
/**
|
|
* @brief open console process, and use it by command
|
|
* @param com
|
|
* @return
|
|
*/
|
|
QString Channel::writeCom(const QString& com)
|
|
{
|
|
QProcess proc;
|
|
QStringList argList;
|
|
argList << "-c" << com;
|
|
proc.start("/bin/sh", argList);
|
|
// wait process start
|
|
proc.waitForFinished();
|
|
proc.waitForReadyRead();
|
|
// read data from console
|
|
QByteArray procOutput = proc.readAll();
|
|
proc.close();
|
|
return QString(procOutput);
|
|
}
|
|
|
|
/**
|
|
* @brief judge the disk whether mounted
|
|
* @return
|
|
*/
|
|
bool Channel::isMountDisk()
|
|
{
|
|
QString mount = writeCom("df /root/usb");
|
|
if (!mount.contains("/root/usb"))
|
|
return false;
|
|
return true;
|
|
}
|