465 lines
11 KiB
C++
Executable File
465 lines
11 KiB
C++
Executable File
|
|
|
|
|
|
#include "topic_replay.h"
|
|
#include <AudioVideoManagement/ava_types.h>
|
|
#include <AudioVideoManagement/avm_data_plugin_interface.h>
|
|
#include <AudioVideoManagement/ava_control.h>
|
|
#include <AudioVideoManagement/ava_session.h>
|
|
#include <AudioVideoManagement/vd_control.h>
|
|
#include <AudioVideoManagement/vd_session.h>
|
|
#include <TopicEditorBase/topic_editor_base.h>
|
|
#include <LtsUtilsCore/json_object.h>
|
|
#include <nglsettings/nglsettings_settings.h>
|
|
#include <QWidget>
|
|
#include <QFileInfo>
|
|
#include <QJsonDocument>
|
|
#include <QDir>
|
|
#include <QtGlobal>
|
|
#include <QRect>
|
|
#include <QDebug>
|
|
#include <vector>
|
|
#include <chrono>
|
|
|
|
|
|
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<int>(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<int>(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<TopicEditorBase::ExportNotifier *>(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;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
#include "topic_replay.moc"
|