#include "Channel.h" #include "Constant.h" #include "Log.h" #include "Tool.h" #include #include #include 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; snap = Link::create("EncodeV"); overlay = Link::create("Overlay"); 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); // 测试水印 QVariantMap dataOver; QVariantList lays; QVariantMap lay; lay["type"] = "time"; lay["enable"] = true; lay["font"] = "/link/res/font.ttf"; lay["content"] = "yyyy年MM月dd日 hh:mm:ss"; lay["x"] = 0.1; lay["y"] = 0.5; lay["scale"] = 2; lay["color"] = "#ffffff"; lay["alpha"] = 1; lays << lay; dataOver["lays"] = lays; overlay->start(dataOver); videoInput->linkV(overlay)->linkV(videoOutput); // videoInput->linkV(videoOutput); // 截图 QVariantMap dataSnap; dataSnap["codec"] = "jpeg"; dataSnap["snap"] = true; dataSnap["width"] = 1920; dataSnap["height"] = 1080; snap->start(dataSnap); videoInput->linkV(snap); // 等待1秒,防止截图失败 QThread::sleep(1); // 视频编码 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; 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() { QString curTime = QDateTime::currentDateTime().toString("yyyy-MM-dd_hh:mm:ss"); QString path = QString("%1/%2/%3.mp4").arg(Constant::VideoPath).arg(channelName).arg(curTime); // 重新分片 record->invoke("segment", path); // path = QString("%1/%2/%3.jpg").arg(Constant::SnapPath).arg(channelName).arg(curTime); // snap->invoke("snapSync", path); } /** * @brief 开始录制 */ void Channel::startRecord() { if (!isMountDisk()) { Log::error("there are no disk mounted"); return; } QString curTime = QDateTime::currentDateTime().toString("yyyy-MM-dd_hh:mm:ss"); 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(); // path = QString("%1/%2/%3.jpg").arg(Constant::SnapPath).arg(channelName).arg(curTime); // snap->invoke("snapSync", path); 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; }