#include "topic_replay.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace AudioVideoManagement { class TopicReplay::ExportScript : public QObject { Q_OBJECT public: ExportScript(const QString& topLevelExportDir, TopicEditorBase::ExportNotifier* exportStep); public slots: void avScriptFinished(const QString& exportDir, AvaStreamType streamType, bool success); private: QString audioVideoPlaylistErstellen(const QString& dir, AvaStreamType streamType); bool writeFile(const QByteArray& data, const QString& fileName); QString m_topLevelExportDir; TopicEditorBase::ExportNotifier* m_exportStep; }; TopicReplay::TopicReplay(DataInterface* dataIfc, bool isAudio) : m_isAudio(isAudio), m_dataIfc(dataIfc) { DEBUG_LOG(m_logger) << "TopicReplay::TopicReplay()"; Q_ASSERT(dataIfc); } TopicReplay::~TopicReplay() { DEBUG_LOG(m_logger) << "TopicReplay::~TopicReplay()"; if (m_isPlaying) playbackStop(); } QWidget* TopicReplay::createWidget() { DEBUG_LOG(m_logger) << "TopicReplay::createWidget()"; Q_ASSERT(!m_screenPlacementHelper); if (m_isAudio) return nullptr; m_screenPlacementHelper.reset(new ScreenPlacementHelper()); connect(m_screenPlacementHelper.data(), &ScreenPlacementHelper::widgetPlaced, this, &TopicReplay::onWidgetPlaced); return m_screenPlacementHelper.data(); } void TopicReplay::slotSetScriptResponse(const AudioVideoManagement::AvaStreamInfo& streamInfo, AudioVideoManagement::AvaStreamState streamState) { if (streamState == AudioVideoManagement::AvaStreamState::NotPossible) ERROR_LOG(m_logger) << "Script could not be set"; else ERROR_LOG(m_logger) << "Script set!"; ERROR_LOG(m_logger) << "streamInfo: start" << streamInfo.duration.count(); ERROR_LOG(m_logger) << "streamState: " << static_cast(streamState); } bool TopicReplay::playbackInit(const QJsonObject& attributes) { DEBUG_LOG(m_logger) << "TopicReplay::playbackInit()"; DEBUG_LOG(m_logger) << QJsonDocument(attributes).toJson(); if (m_screenPlacementHelper && !m_isAudio) m_screenPlacementHelper->show(); m_playbackAttributes = attributes; AvaControl* ctrl = getAvaControl(); if (ctrl == nullptr) { ERROR_LOG(m_logger) << "no ava control!"; return false; } AvaTopicData topicData(m_playbackAttributes); if (!topicData.isValid) { ERROR_LOG(m_logger) << "topicData is invalid"; return false; } m_scriptName = topicData.scriptName(); Q_ASSERT(topicData.isAudio() == m_isAudio); PlaybackApp* app = m_isAudio ? m_dataIfc->audioPresentationApp() : m_dataIfc->videoPresentationApp(); if (app == nullptr || app->inUse()) { ERROR_LOG(m_logger) << "app invalid or in use!"; return false; } if (!m_dialogGeometry) { if (m_isAudio) m_appHandle = PlaybackAppHandle(app); else m_appHandle = PlaybackAppHandle(app, QRect(0, 0, 0, 0)); } else m_appHandle = PlaybackAppHandle(app, *m_dialogGeometry); if (!m_appHandle) { ERROR_LOG(m_logger) << "appHandle: missing app after creating appHandle!"; return false; } NGL::Settings s({}); bool isLocal = s.isDatabaseConnectionLocal(); DEBUG_LOG(m_logger) << "isLocal? " << isLocal; AvaScriptSource source; if (isLocal) { if (m_isAudio) source = AvaScriptSource::LocalAudio; else source = AvaScriptSource::LocalVideo; } else { if (m_isAudio) source = AvaScriptSource::ServerAudio; else source = AvaScriptSource::ServerVideo; } DEBUG_LOG(m_logger) << "source: " << static_cast(source); ctrl->setScript(m_appHandle.dialogId(), m_scriptName, source, topicData.streamList(), LtsUtils::createConnection(ctrl, &AudioVideoManagement::AvaControl::setScriptResponse, this, &TopicReplay::slotSetScriptResponse)); return true; } bool TopicReplay::playbackStart(int secondsOffset) { DEBUG_LOG(m_logger) << "TopicReplay::playbackStart()"; if (m_screenPlacementHelper && !m_isAudio) m_screenPlacementHelper->show(); AvaControl* ctrl = getAvaControl(); if (ctrl == nullptr) { ERROR_LOG(m_logger) << "ctrl == nullptr"; return false; } AvaTimePoint p{AvaDuration{std::chrono::seconds(secondsOffset)}}; if (!m_appHandle) { ERROR_LOG(m_logger) << "appHandle: missing app!"; return false; } ctrl->setAbsPositionScript(m_appHandle.dialogId(), m_scriptName, p); ctrl->startScript(m_appHandle.dialogId(), m_scriptName); m_isPlaying = true; DEBUG_LOG(m_logger) << "m_isPlaying = true"; return true; } bool TopicReplay::playbackPause() { DEBUG_LOG(m_logger) << "TopicReplay::playbackPause()"; if (m_screenPlacementHelper) m_screenPlacementHelper->hide(); if (!m_isPlaying) return true; AvaControl* ctrl = getAvaControl(); if (ctrl == nullptr) { ERROR_LOG(m_logger) << "no ctrl!"; return false; } if (!m_appHandle) { ERROR_LOG(m_logger) << "appHandle: missing app!"; return false; } ctrl->pauseScript(m_appHandle.dialogId(), m_scriptName); return true; } bool TopicReplay::playbackStop() { DEBUG_LOG(m_logger) << "TopicReplay::playbackStop()"; if (m_screenPlacementHelper) m_screenPlacementHelper->hide(); if (!m_isPlaying) return true; AvaControl* ctrl = getAvaControl(); if (ctrl == nullptr) { ERROR_LOG(m_logger) << "no ctrl!"; return false; } if (!m_appHandle) { ERROR_LOG(m_logger) << "appHandle: missing app!"; return false; } ctrl->stopScript(m_appHandle.dialogId(), m_scriptName); ctrl->releaseScript(m_appHandle.dialogId(), m_scriptName); m_isPlaying = false; return true; } bool TopicReplay::exportTopic(const QJsonObject& attributes, const QString& folderPath, TopicEditorBase::ExportNotifier* exportNotifier) { Q_ASSERT(!m_exportScript); VdControl* ctrl = getVdControl(); if (ctrl == nullptr) { ERROR_LOG(m_logger) << "ctrl missing!"; exportNotifier->setExportFailed(); return false; } m_exportScript.reset(new ExportScript(folderPath, exportNotifier)); AvaTopicData topicData(attributes); m_scriptName = topicData.scriptName(); if (!topicData.isValid) { ERROR_LOG(m_logger) << "topicData is invalid"; exportNotifier->setExportFailed(); return false; } QFileInfo fileInfo(QDir(folderPath).filePath(topicData.scriptName())); Q_ASSERT(topicData.isAudio() == m_isAudio); ctrl->vdSetScript(m_scriptName, m_isAudio ? AvaStreamType::Audio : AvaStreamType::Video, 0, topicData.streamList(), fileInfo.baseName(), fileInfo.absolutePath(), this, &TopicReplay::avScriptFinished); return true; } void TopicReplay::onWidgetPlaced(const QRect& geometry) { WARNING_LOG(m_logger) << "TopicReplay::onWidgetPlaced() - geo: " << geometry.x() << "/" << geometry.y() << " m_isAudio=" << m_isAudio; m_dialogGeometry = geometry; if (m_isAudio) return; PlaybackApp* app = m_isAudio ? m_dataIfc->audioPresentationApp() : m_dataIfc->videoPresentationApp(); if (app == nullptr) { ERROR_LOG(m_logger) << "app missing!"; return; } if (!app->inUse()) { ERROR_LOG(m_logger) << "app not in use - waiting for playbackInit to aquire it"; return; } DEBUG_LOG(m_logger) << "setGeometry"; app->setGeometry(*m_dialogGeometry); } AvaControl* TopicReplay::getAvaControl() { if (!m_dataIfc->hasAvsSession()) return nullptr; AvaSession* avaSession = m_dataIfc->avaSession(); if (!avaSession->isEstablished()) return nullptr; return avaSession->control(); } VdControl* TopicReplay::getVdControl() { if (!m_dataIfc->hasAvsSession()) return nullptr; VdSession* vdSession = m_dataIfc->vdSession(); if (!vdSession->isEstablished()) return nullptr; return vdSession->control(); } void TopicReplay::avScriptFinished(const QString& exportDir, AvaStreamType streamType, bool success) { VdControl* ctrl = getVdControl(); ctrl->vdReleaseScript(m_scriptName); m_exportScript->avScriptFinished(exportDir, streamType, success); } TopicReplay::ExportScript::ExportScript(const QString& topLevelExportDir, TopicEditorBase::ExportNotifier* exportStep) : m_topLevelExportDir(topLevelExportDir), m_exportStep(exportStep) { connect(exportStep, &QObject::destroyed, this, [this]() { if (static_cast(sender()) != m_exportStep) return; m_exportStep = nullptr; }); Q_ASSERT(exportStep); } void TopicReplay::ExportScript::avScriptFinished(const QString& exportDir, AvaStreamType streamType, bool success) { QString absPathToPlaylist = audioVideoPlaylistErstellen(exportDir, streamType); QDir toplevelExportDir(m_topLevelExportDir); QString relativePathToPlaylist = toplevelExportDir.relativeFilePath(absPathToPlaylist); if (m_exportStep == nullptr) return; if (success) m_exportStep->setExportSuccessful(relativePathToPlaylist); else m_exportStep->setExportFailed(); } QString TopicReplay::ExportScript::audioVideoPlaylistErstellen(const QString& dir, AvaStreamType streamType) { QDir avDir(dir); const QFileInfoList dateienList = avDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name); QByteArray dataForFile; QString data; dataForFile.append("[playlist]\n"); int numberOfEntries = 0; int i = 1; const QString tmpl1(QStringLiteral("File %1=%2\n")); const QString tmpl2(QStringLiteral("# File %1=%2\n")); for (const QFileInfo& fileInfo : dateienList) { if (streamType == AvaStreamType::Video && fileInfo.suffix() == QLatin1String(".wav")) { data += tmpl2.arg(QString::number(i), fileInfo.fileName()); i--; } else { data += tmpl1.arg(QString::number(i), fileInfo.fileName()); numberOfEntries++; } i++; } QString numberOfEntriesString = QStringLiteral("NumberOfEntries=") + QString::number(numberOfEntries) + QStringLiteral("\n"); dataForFile.append(numberOfEntriesString.toUtf8()); dataForFile.append(data.toUtf8()); QString fileName; switch (streamType) { case AvaStreamType::Audio: fileName = avDir.filePath(QStringLiteral("audio.pls")); break; case AvaStreamType::Video: fileName = avDir.filePath(QStringLiteral("video.pls")); break; } writeFile(dataForFile, fileName); return fileName; } bool TopicReplay::ExportScript::writeFile(const QByteArray& data, const QString& fileName) { QFile file(fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) return false; if (file.write(data) == -1) { file.close(); return false; } file.close(); return true; } } // namespace AudioVideoManagement #include "topic_replay.moc"