1705 lines
51 KiB
C++
Executable File
1705 lines
51 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* AvmPlaybackMenu
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
* Created by Christian Fröbel, SLSE, 2017-06-26
|
|
*/
|
|
|
|
#include "avm_playback_menu.h"
|
|
#include "TopicEditor/avm_sequence_model.h"
|
|
#include <AudioVideoManagement/av_helper.h>
|
|
#include <AudioVideoManagement/ava_comm.h>
|
|
#include <AudioVideoManagement/ava_control.h>
|
|
#include <AudioVideoManagement/ava_session.h>
|
|
#include <AudioVideoManagement/stream_name_mapping.h>
|
|
|
|
#include <utility>
|
|
#include <QVector>
|
|
#include <QModelIndex>
|
|
#include <QStringList>
|
|
#include <QSignalBlocker>
|
|
#include <QIcon>
|
|
#include <QtGlobal>
|
|
#include <QInputDialog>
|
|
#include <QMenu>
|
|
#include <QAction>
|
|
|
|
#include <ngltools/ngltools_dtg.h>
|
|
#include <PluginInterfaces/gui_plugin_interface.h>
|
|
#include <LtsSession/lts_session_data.h>
|
|
#include <LtsWidgets/lts_message_dialog.h>
|
|
#include <LtsWidgets/lts_msec_date_time_edit.h>
|
|
#include <MarkerManagement/Gui/Common/mrk_dialog_model.h>
|
|
#include <MarkerManagement/mrk_data_plugin_interface.h>
|
|
#include <OperatorManagement/opm_data_plugin_interface.h>
|
|
#include <LtsUtilsGuiDds/request_helper.h>
|
|
#include <LtsUtilsSettings/settings.h>
|
|
|
|
|
|
namespace
|
|
{
|
|
|
|
// stolen from ngltools_dtg.cpp
|
|
bool isTimezoneValid(char tz)
|
|
{
|
|
tz = toupper(tz);
|
|
return !(tz < 'A' || tz == 'J' || tz > 'Z');
|
|
}
|
|
|
|
int timezone2Hours(char tz, bool *bOk)
|
|
{
|
|
if (!isTimezoneValid(tz))
|
|
{
|
|
if (bOk)
|
|
*bOk = false;
|
|
return 0;
|
|
}
|
|
if (bOk)
|
|
*bOk = true;
|
|
if (tz < 'J')
|
|
return tz - 'A' + 1;
|
|
if (tz < 'N')
|
|
return tz - 'A';
|
|
if (tz < 'Z')
|
|
return 'Z' - tz - 13;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
const QString &ALL_STREAMS()
|
|
{
|
|
static const QString ALL_STREAMS_ = QStringLiteral("*");
|
|
return ALL_STREAMS_;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
namespace AudioVideoManagement
|
|
{
|
|
|
|
|
|
/**
|
|
* Small helper class to build the Timeline for a set of streams.
|
|
*/
|
|
class AvmPlaybackMenu::TimelineBuilder : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
explicit TimelineBuilder(AvaControl *avaCtrl)
|
|
: m_avaCtrl(avaCtrl)
|
|
{
|
|
Q_ASSERT(avaCtrl);
|
|
}
|
|
|
|
/**
|
|
* Can be called even if the previous queryTimeline() is not yet complete.
|
|
* The previous call will be cancelled then: there will only be a timelineComplete() signal
|
|
* for the last issued queryTimeline() call.
|
|
*/
|
|
void queryTimeline(const QVector<AvaStream> &streams)
|
|
{
|
|
m_timeline.clear();
|
|
m_ignoreTimelineCount += m_awaitedTimelineCount;
|
|
m_awaitedTimelineCount = streams.size();
|
|
for (const auto &stream : streams)
|
|
m_avaCtrl->queryTimeline(stream.toString(), this, &TimelineBuilder::onTimelineReceived);
|
|
}
|
|
|
|
const AvaTimeline &timeline() const
|
|
{
|
|
// cfroebel 2018-04-19: Must be activated again, once onStreamSelectionChanged() will not
|
|
// be called from third parties anymore. It was supposed to be private.
|
|
// Q_ASSERT(m_awaitedTimelineCount == 0);
|
|
return m_timeline;
|
|
}
|
|
|
|
/**
|
|
* Must be called when the connection to AVS_Workstation breaks in case we
|
|
* have some outstanding queries for which we will never receive the response then.
|
|
*/
|
|
void reset()
|
|
{
|
|
m_ignoreTimelineCount = 0;
|
|
m_awaitedTimelineCount = 0;
|
|
}
|
|
|
|
signals:
|
|
void timelineComplete(const AvaTimeline &timeline);
|
|
|
|
private slots:
|
|
void onTimelineReceived(const AvaTimeline &timeline)
|
|
{
|
|
Q_ASSERT(m_awaitedTimelineCount > 0);
|
|
|
|
if (m_ignoreTimelineCount > 0)
|
|
{
|
|
--m_ignoreTimelineCount;
|
|
return;
|
|
}
|
|
|
|
m_timeline.merge(timeline);
|
|
|
|
if (--m_awaitedTimelineCount > 0)
|
|
return;
|
|
|
|
emit timelineComplete(m_timeline);
|
|
}
|
|
|
|
private:
|
|
AvaControl *m_avaCtrl;
|
|
AvaTimeline m_timeline;
|
|
int m_ignoreTimelineCount{0};
|
|
int m_awaitedTimelineCount{0};
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* Helper class to control the interaction between the DateTimeEdit and
|
|
* the slider.
|
|
*/
|
|
class AvmPlaybackMenu::TimeControl : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
TimeControl(LtsWidgets::LtsMSecDateTimeEdit *dt, QSlider *slider, QLabel *lblStart, QLabel *lblEnd)
|
|
: m_dt(dt)
|
|
, m_slider(slider)
|
|
, m_lblStart(lblStart)
|
|
, m_lblEnd(lblEnd)
|
|
{
|
|
Q_ASSERT(dt);
|
|
Q_ASSERT(slider);
|
|
Q_ASSERT(lblStart);
|
|
Q_ASSERT(lblEnd);
|
|
|
|
connect(m_dt, &LtsWidgets::LtsMSecDateTimeEdit::dateTimeChanged, this, &TimeControl::onDtChanged);
|
|
connect(m_slider, &QSlider::valueChanged, this, &TimeControl::onSliderChanged);
|
|
}
|
|
|
|
|
|
QString dateTimeToString(const QDateTime &dt)
|
|
{
|
|
return dt.toString(QStringLiteral("dd.MM.yy HH:mm:ss.zzz"));
|
|
}
|
|
|
|
|
|
QString msecsSinceEpochToString(qint64 msecs)
|
|
{
|
|
return dateTimeToString(QDateTime::fromMSecsSinceEpoch(msecs, Qt::TimeSpec::OffsetFromUTC, m_timezoneHoursOffset * 3600));
|
|
}
|
|
|
|
|
|
void setTimezoneHoursOffset(int timezoneHoursOffset)
|
|
{
|
|
m_timezoneHoursOffset = timezoneHoursOffset;
|
|
|
|
if (!m_interval.is_initialized())
|
|
return;
|
|
|
|
// update start & end time labels
|
|
// NOTE: dt's timezone is set outside of TimeControl
|
|
m_lblStart->setText(msecsSinceEpochToString(toMSecSinceEpoch(m_interval->first)));
|
|
m_lblEnd->setText(msecsSinceEpochToString(toMSecSinceEpoch(m_interval->second)));
|
|
}
|
|
|
|
|
|
void setTime(AvaTimePoint t)
|
|
{
|
|
QSignalBlocker b1(m_dt);
|
|
|
|
if (!m_interval.is_initialized())
|
|
{
|
|
m_dt->setMsecsSinceEpoch(AudioVideoManagement::toMSecSinceEpoch(t));
|
|
return;
|
|
}
|
|
|
|
t = timePointInInterval(t);
|
|
m_dt->setMsecsSinceEpoch(AudioVideoManagement::toMSecSinceEpoch(t));
|
|
|
|
// Avoid that changing the slider in return changes the DTE as the slider has only seconds-accuracy.
|
|
QSignalBlocker blocker(m_slider);
|
|
AvaDuration delta = t - m_interval->first;
|
|
std::chrono::seconds deltaS = std::chrono::duration_cast<std::chrono::seconds>(delta + std::chrono::milliseconds(500)); // +0.5s for rounding
|
|
m_slider->setValue(deltaS.count());
|
|
}
|
|
|
|
|
|
void setInterval(AvaTimelinePart interval)
|
|
{
|
|
std::chrono::seconds intervalLength = std::chrono::duration_cast<std::chrono::seconds>(interval.second - interval.first);
|
|
|
|
QSignalBlocker b1(m_slider);
|
|
m_slider->setMinimum(0);
|
|
m_slider->setMaximum(intervalLength.count());
|
|
m_lblStart->setText(msecsSinceEpochToString(toMSecSinceEpoch(interval.first)));
|
|
m_lblEnd->setText(msecsSinceEpochToString(toMSecSinceEpoch(interval.second)));
|
|
|
|
m_interval = interval;
|
|
}
|
|
|
|
|
|
AvaTimePoint timePointInInterval(AvaTimePoint t)
|
|
{
|
|
Q_ASSERT(m_interval.is_initialized());
|
|
if (!m_interval.is_initialized())
|
|
return {};
|
|
|
|
if (t < m_interval->first)
|
|
return m_interval->first;
|
|
|
|
if (t > m_interval->second)
|
|
return m_interval->second;
|
|
|
|
return t;
|
|
}
|
|
|
|
|
|
AvaTimePoint time() const { return toTimePoint(m_dt->msecsSinceEpoch()); }
|
|
qint64 timeMSec() const { return m_dt->msecsSinceEpoch(); }
|
|
|
|
signals:
|
|
void timeChanged(AvaTimePoint currentTime);
|
|
|
|
private slots:
|
|
void onDtChanged()
|
|
{
|
|
emit timeChanged(toTimePoint(m_dt->msecsSinceEpoch()));
|
|
}
|
|
|
|
|
|
void onSliderChanged(int seconds)
|
|
{
|
|
QSignalBlocker b1(m_dt);
|
|
AvaTimePoint t = m_interval->first + std::chrono::seconds(seconds);
|
|
m_dt->setMsecsSinceEpoch(toMSecSinceEpoch(t));
|
|
emit timeChanged(toTimePoint(m_dt->msecsSinceEpoch()));
|
|
}
|
|
|
|
private:
|
|
LtsWidgets::LtsMSecDateTimeEdit *m_dt;
|
|
QSlider *m_slider;
|
|
QLabel *m_lblStart;
|
|
QLabel *m_lblEnd;
|
|
boost::optional<AvaTimelinePart> m_interval;
|
|
int m_timezoneHoursOffset = 0;
|
|
};
|
|
|
|
|
|
// QueryTimeline
|
|
QueryTimeline::QueryTimeline(const AvaStream &stream, const AvaTimePoint &start, const AvaTimePoint &end, AvaControl *control)
|
|
: QObject()
|
|
{
|
|
connect(control, &AvaControl::queryTimelineResponse,
|
|
this, &QueryTimeline::signalTimelineComplete);
|
|
connect(control, &AvaControl::queryTimelineResponse,
|
|
this, &QObject::deleteLater);
|
|
|
|
control->queryTimeline(stream.toString(), start, end);
|
|
}
|
|
|
|
|
|
// AvmPlaybackMenu
|
|
AvmPlaybackMenu::AvmPlaybackMenu(QWidget *parent)
|
|
: QWidget(parent)
|
|
, m_ui(new Ui::Playback)
|
|
, m_requestHelper(new LtsUtils::RequestHelper(this))
|
|
, m_logger(getLogger("AvmPlaybackMenu"))
|
|
{}
|
|
|
|
|
|
AvmPlaybackMenu::~AvmPlaybackMenu()
|
|
{
|
|
if (m_selectedStreams)
|
|
m_avaCtrl->releaseStream(m_playbackApp.dialogId(), ALL_STREAMS());
|
|
m_playbackApp.hide(true /* and close */);
|
|
}
|
|
|
|
|
|
QueryTimeline *AvmPlaybackMenu::queryTimeline(const AvaTimePoint &start, const AvaTimePoint &end) // .. of current stream
|
|
{
|
|
if (!m_currentStream)
|
|
return nullptr;
|
|
|
|
if (m_queryTimelineActive)
|
|
return nullptr;
|
|
|
|
QueryTimeline *qt = new QueryTimeline(m_currentStream.get(), start, end, m_avaCtrl); // don't parent, probably uses dumb connections
|
|
|
|
// if (!qt->init())
|
|
// {
|
|
// qt->deleteLater();
|
|
// return nullptr;
|
|
// }
|
|
|
|
m_queryTimelineActive = true;
|
|
emit signalSetOverlay(new QWidget()); // NOTE: doesn't overlay if not its own dialog (e.g. inside topic editor)
|
|
|
|
connect(qt, &QueryTimeline::signalTimelineComplete,
|
|
this, [this]()
|
|
{
|
|
emit signalUnsetOverlay(); // s.a.a.
|
|
m_queryTimelineActive = false;
|
|
});
|
|
|
|
return qt;
|
|
}
|
|
|
|
|
|
void AvmPlaybackMenu::setPlaybackPreset(const QString &streamName, const AvaTimePoint &startTime, bool autostartPresetAfterInit)
|
|
{
|
|
m_presetStreamName = streamName;
|
|
m_presetStartTime = startTime;
|
|
m_autostartOnce = autostartPresetAfterInit;
|
|
|
|
if (m_timelineIsComplete) // dialog was already open and has a selected stream - set time, stop and start playback
|
|
{
|
|
onStopClicked();
|
|
processPresets();
|
|
}
|
|
}
|
|
|
|
|
|
void AvmPlaybackMenu::setMarkerPreset(int markerId, bool autostartPresetAfterInit)
|
|
{
|
|
m_markerIdPreset = markerId;
|
|
m_autostartOnce = autostartPresetAfterInit;
|
|
|
|
m_mrkDataIfc->getDtc()->waitForHistoricalDataMarker(8000);
|
|
|
|
m_ui->tabWidget->setCurrentIndex(1); // markers tab
|
|
|
|
auto idx = m_markerModel->findIndexForId(*m_markerIdPreset);
|
|
auto mappedIdx = m_markerProxy->mapFromSource(idx);
|
|
m_ui->tvMarker->selectRow(mappedIdx);
|
|
m_ui->tvMarker->scrollTo(mappedIdx);
|
|
selectMarker(markerId);
|
|
|
|
qWarning() << m_ui->tvMarker->selectionModel()->selectedRows();
|
|
}
|
|
|
|
|
|
bool AvmPlaybackMenu::init(LtsSession::LtsSession *session, AvaSession *avaSession, PlaybackAppHandle &&appHandle,
|
|
StreamNameMapping *streamNameMapping, bool isAudio, bool insideTopicEditor,
|
|
const boost::optional<QString> &streamName, const boost::optional<AvaTimePoint> &startTime,
|
|
bool setInitialSearchContentMarkers)
|
|
{
|
|
Q_ASSERT(avaSession);
|
|
|
|
// todo: At the moment we do not allow to instantiate this dialog when we don't have access to the PlaybackApp.
|
|
// We could instead just disable the dialog and maybe show a message indicating a reason...
|
|
Q_ASSERT(appHandle);
|
|
|
|
m_isAudio = isAudio;
|
|
m_streamNameMapping = streamNameMapping;
|
|
|
|
m_presetStreamName = streamName;
|
|
|
|
if (m_presetStreamName)
|
|
DEBUG_LOG(m_logger) << "init stream preset: " << m_presetStreamName.get();
|
|
|
|
m_presetStartTime = startTime;
|
|
|
|
if (m_presetStartTime)
|
|
DEBUG_LOG(m_logger) << "init time preset: " << std::chrono::milliseconds(m_presetStartTime.get().time_since_epoch()).count();
|
|
|
|
m_session = session;
|
|
m_playbackApp = std::move(appHandle);
|
|
m_avaCtrl = avaSession->control();
|
|
m_timelineBuilder.reset(new TimelineBuilder(avaSession->control()));
|
|
|
|
m_ui->setupUi(this);
|
|
|
|
m_ui->pbDebug->hide();
|
|
|
|
#ifndef LTS_DEBUG
|
|
m_ui->lbAvsConnectionStatus->hide();
|
|
m_ui->lblAvsConnectionStatus->hide();
|
|
#endif
|
|
|
|
m_ui->tabWidget->setCurrentIndex(0); // make sure clips is always the default selected tab
|
|
|
|
|
|
if (!insideTopicEditor)
|
|
m_ui->topicEditorEssentialsWidget->hide();
|
|
else
|
|
{
|
|
setContentsMargins(QMargins());
|
|
m_ui->tabWidget->setTabText(0, m_isAudio ? tr("Audio clips") : tr("Video clips"));
|
|
m_ui->multiStreamWidget->hide();
|
|
m_ui->pbAddStreamClip->hide();
|
|
m_ui->pbAddStreamMarker->hide();
|
|
|
|
|
|
// action for the interval view to set start & end time
|
|
m_ui->tavIntervals->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(m_ui->tavIntervals, &QTableView::customContextMenuRequested,
|
|
this, [this](const QPoint & pos)
|
|
{
|
|
QMenu *m = new QMenu(this);
|
|
|
|
auto a1 = new QAction(tr("Set start and end time"), this);
|
|
|
|
connect(a1, &QAction::triggered,
|
|
this, [this]()
|
|
{
|
|
auto idxs = m_ui->tavIntervals->selectionModel()->selectedRows();
|
|
|
|
if (idxs.isEmpty())
|
|
return;
|
|
|
|
AvaTimelinePart interval = m_intervalModel.getRowData(idxs.at(0).row());
|
|
emit signalSetStart(toMSecSinceEpoch(interval.first));
|
|
emit signalSetEnd(toMSecSinceEpoch(interval.second));
|
|
});
|
|
|
|
m->addAction(a1);
|
|
|
|
m->popup(m_ui->tavIntervals->viewport()->mapToGlobal(pos));
|
|
});
|
|
}
|
|
|
|
m_timeControl.reset(new TimeControl(m_ui->dtCurrentTime, m_ui->slSequenceSlider, m_ui->lblStartSeq, m_ui->lblEndSeq));
|
|
|
|
if (m_isAudio)
|
|
m_maxSelectedStreams = 8;
|
|
|
|
m_ui->tavIntervals->setModel(&m_intervalModel);
|
|
m_ui->tavIntervals->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
m_ui->tavIntervals->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
m_ui->tavIntervals->resizeColumnsToContents();
|
|
|
|
// optional
|
|
if (m_session->getForeignDataInterface<MarkerManagement::DataInterface>(m_mrkDataIfc))
|
|
{
|
|
m_markerModel.reset(new MarkerManagement::MarkerManagementDialogModel(this));
|
|
m_markerProxy.reset(new MarkerManagement::MarkerSortFilterProxyModel(this));
|
|
m_markerProxy->setSourceModel(m_markerModel.get());
|
|
std::shared_ptr<OperatorManagement::Interface::Dtc> opmDtc;
|
|
m_session->getDtc<OperatorManagement::DataInterface>(opmDtc);
|
|
std::shared_ptr<FileManagement::ForeignGuiInterface> flmGui;
|
|
m_session->getForeignGuiInterface<FileManagement::ForeignGuiInterface>(flmGui);
|
|
m_markerModel->initModel(m_mrkDataIfc->getDtc(), opmDtc, flmGui, true /* we decide which timezone the marker time is shown in! */);
|
|
m_ui->tvMarker->setModel(m_markerProxy.get());
|
|
m_ui->tvMarker->hideColumn(MarkerManagement::MarkerManagementDialogModel::ColImage);
|
|
m_ui->tvMarker->hideColumn(MarkerManagement::MarkerManagementDialogModel::ColFileId);
|
|
m_ui->tvMarker->hideColumn(MarkerManagement::MarkerManagementDialogModel::ColOffset);
|
|
#ifndef LTS_DEBUG
|
|
m_ui->tvMarker->hideColumn(MarkerManagement::MarkerManagementDialogModel::ColStreams);
|
|
#endif
|
|
|
|
if (setInitialSearchContentMarkers)
|
|
m_ui->tvMarker->setInitialSearchContent(MarkerManagement::MarkerManagementDialogModel::ColOperator,
|
|
LtsSessionData::LtsSessionData::getInstance()->getOperatorId());
|
|
|
|
connect(m_ui->tvMarker, &LtsDataView::LtsDataView::activated, this, &AvmPlaybackMenu::onMarkerClicked);
|
|
connect(m_ui->pbSetMarker, &QPushButton::clicked, this, &AvmPlaybackMenu::onSetMarkerClicked);
|
|
}
|
|
else
|
|
{
|
|
m_ui->tabWidget->setTabEnabled(1, false); // disable marker tab completely
|
|
}
|
|
|
|
|
|
QSignalBlocker blocker1(m_ui->slSpeed);
|
|
m_ui->slSpeed->setMinimum(0);
|
|
m_ui->slSpeed->setMaximum(AvaSpeed::allowedValues().size() - 1);
|
|
m_ui->slSpeed->setSingleStep(1);
|
|
m_ui->slSpeed->setPageStep(1);
|
|
m_ui->slSpeed->setValue(AvaSpeed::allowedValues().indexOf(AvaSpeed(100)));
|
|
m_ui->lbSpeedValue->setText(QString::number(100));
|
|
|
|
//hide things
|
|
m_ui->slSpeed->setVisible(!m_isAudio);
|
|
m_ui->lbSpeedValue->setVisible(!m_isAudio);
|
|
m_ui->lbSpeed->setVisible(!m_isAudio);
|
|
if (m_isAudio)
|
|
m_ui->spacerSpeedVolume->changeSize(0, 0);
|
|
|
|
|
|
QSignalBlocker blocker2(m_ui->slVolume);
|
|
m_ui->slVolume->setMinimum(AvaVolume::min());
|
|
m_ui->slVolume->setMaximum(AvaVolume::max());
|
|
m_ui->slVolume->setSingleStep(1);
|
|
m_ui->slVolume->setPageStep(10);
|
|
m_ui->slVolume->setValue(0);
|
|
m_ui->lbVolumeValue->setText(QString::number(0));
|
|
|
|
m_ui->pbSkipPause->setIcon(QIcon(QStringLiteral("icon:media-vcr-nopause.svg")));
|
|
|
|
connect(avaSession, &AvaSession::sessionEstablished, this, &AvmPlaybackMenu::onSessionEstablished);
|
|
connect(avaSession, &AvaSession::disconnected, this, &AvmPlaybackMenu::onDisconnected);
|
|
|
|
connect(m_timelineBuilder.get(), &TimelineBuilder::timelineComplete, this, &AvmPlaybackMenu::onTimelineComplete);
|
|
connect(m_timeControl.get(), &TimeControl::timeChanged, this, &AvmPlaybackMenu::onUiTimeChanged);
|
|
|
|
connect(m_avaCtrl->comm(), &AvaComm::streamPositionReceived, this, &AvmPlaybackMenu::onStreamPositionReceived);
|
|
connect(m_avaCtrl->comm(), &AvaComm::volumeReceived, this, &AvmPlaybackMenu::onVolumeReceived);
|
|
connect(m_avaCtrl, &AvaControl::commandQueueEmpty, this, &AvmPlaybackMenu::onCommandQueueEmpty);
|
|
|
|
connect(m_ui->cbStreams, &QComboBox::currentTextChanged, this, &AvmPlaybackMenu::onStreamSelected);
|
|
connect(m_ui->cbMarkerStreams, &QComboBox::currentTextChanged, this, &AvmPlaybackMenu::onMarkerStreamSelected);
|
|
|
|
|
|
connect(&m_intervalModel, &IntervalModel::currentIntervalChanged, this, &AvmPlaybackMenu::onCurrentIntervalChanged);
|
|
connect(m_ui->pbStart, &QToolButton::clicked, this, &AvmPlaybackMenu::onStartClicked);
|
|
connect(m_ui->pbPause, &QToolButton::clicked, this, &AvmPlaybackMenu::onPauseClicked);
|
|
connect(m_ui->pbStop, &QToolButton::clicked, this, &AvmPlaybackMenu::onStopClicked);
|
|
connect(m_ui->pbIntervalBackward, &QToolButton::clicked, this, &AvmPlaybackMenu::onIntervalBackwardClicked);
|
|
connect(m_ui->pbIntervalForward, &QToolButton::clicked, this, &AvmPlaybackMenu::onIntervalForwardClicked);
|
|
connect(m_ui->slSpeed, &QSlider::valueChanged, this, &AvmPlaybackMenu::onSpeedChanged);
|
|
connect(m_ui->slVolume, &QSlider::valueChanged, this, &AvmPlaybackMenu::onVolumeChanged);
|
|
connect(m_ui->tavIntervals, &QTableView::activated, this, &AvmPlaybackMenu::onIntervalActivated);
|
|
connect(m_ui->pbSkipPause, &QToolButton::clicked, this, &AvmPlaybackMenu::onSkipPauseClicked);
|
|
connect(m_ui->pbAllStreams, &QPushButton::clicked, this, &AvmPlaybackMenu::onAllStreams);
|
|
|
|
connect(m_ui->tabWidget, &QTabWidget::currentChanged, this, &AvmPlaybackMenu::onTabChanged);
|
|
|
|
connect(m_streamNameMapping, &StreamNameMapping::changed,
|
|
this, &AvmPlaybackMenu::onStreamNameMappingChanged);
|
|
|
|
connect(m_ui->pbAddStreamClip, &QPushButton::clicked, this, &AvmPlaybackMenu::onAddStreamClip);
|
|
connect(m_ui->pbAddStreamMarker, &QPushButton::clicked, this, &AvmPlaybackMenu::onAddStreamMarker);
|
|
connect(m_ui->pbRemoveSelectedStream, &QPushButton::clicked, this, &AvmPlaybackMenu::onRemoveSelectedStreams);
|
|
|
|
connect(m_ui->pbAsStart, &QPushButton::clicked, this, [this]()
|
|
{
|
|
emit signalSetStart(m_ui->dtCurrentTime->msecsSinceEpoch());
|
|
});
|
|
|
|
connect(m_ui->pbAsEnd, &QPushButton::clicked, this, [this]()
|
|
{
|
|
emit signalSetEnd(m_ui->dtCurrentTime->msecsSinceEpoch());
|
|
});
|
|
|
|
//m_ui->dteCurrentTime->setRetrieveCurrentTimeCallback([this]() { return m_session->partitionTime(); });
|
|
|
|
|
|
#ifndef LTS_DEBUG
|
|
if (LtsSessionData::LtsSessionData::getInstance()->debugFlagValue(QStringLiteral("AVM.Debug")) == QVariant(true))
|
|
#endif
|
|
{
|
|
m_ui->tavIntervals->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(m_ui->tavIntervals, &QTableView::customContextMenuRequested,
|
|
this, [this](const QPoint & pos)
|
|
{
|
|
QMenu *menu = new QMenu;
|
|
menu->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
QAction *a = menu->addAction(QStringLiteral("[debug]"));
|
|
a->setEnabled(false);
|
|
|
|
auto sim = [this]()
|
|
{
|
|
AvaTimelinePart interval = m_intervalModel.getRowData(m_ui->tavIntervals->selectionModel()->currentIndex().row());
|
|
new LtsWidgets::LtsMessageDialogNew(this, QStringLiteral("start: %1 - end: %2 - duration: %3").arg(toMSecSinceEpoch(interval.first)).arg(toMSecSinceEpoch(interval.second)).arg(toMSecSinceEpoch(interval.second) - toMSecSinceEpoch(interval.first)));
|
|
};
|
|
|
|
menu->addAction(QStringLiteral("show in msecs"), sim);
|
|
menu->popup(m_ui->tavIntervals->mapToGlobal(pos));
|
|
});
|
|
|
|
|
|
|
|
QMenu *menu = new QMenu(this);
|
|
|
|
menu->addAction(QStringLiteral("add test stream entry"), this, [this]()
|
|
{
|
|
qint64 sm = QDateTime::currentMSecsSinceEpoch();
|
|
qint64 em = sm + 48163;
|
|
AvaTimePoint s(std::chrono::duration_cast<AvaDuration>(std::chrono::milliseconds(sm)));
|
|
AvaTimePoint e(std::chrono::duration_cast<AvaDuration>(std::chrono::milliseconds(em)));
|
|
m_intervalModel.insertRow({s, e});
|
|
});
|
|
|
|
menu->addAction(QStringLiteral("enable view"), this, [this]()
|
|
{
|
|
m_ui->tavIntervals->setEnabled(true);
|
|
});
|
|
|
|
m_ui->pbDebug->setMenu(menu);
|
|
m_ui->pbDebug->show();
|
|
}
|
|
|
|
|
|
onDisconnected();
|
|
if (avaSession->isEstablished())
|
|
onSessionEstablished();
|
|
|
|
|
|
|
|
// stolen from operator_settings_dialog.cpp
|
|
for (int tz = 'Y'; tz <= 'Z'; ++tz)
|
|
m_ui->cbTimeZone->addItem(QString(QChar(tz)), tz);
|
|
m_ui->cbTimeZone->addItem(tr("Exercise default (%1)").arg(QLatin1Char(DTG::timezoneExerciseDefault())), 0);
|
|
for (int tz = 'A'; tz <= 'F'; ++tz)
|
|
m_ui->cbTimeZone->addItem(QString(QChar(tz)), tz);
|
|
|
|
const QChar tz = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("TimeSettings"), QStringLiteral("TimeZone"), QChar(QLatin1Char('Z'))).toChar();
|
|
int idx = m_ui->cbTimeZone->findData(tz.isNull() ? 0 : tz);
|
|
//qWarning() << "tz?" << tz << "is null?" << tz.isNull() << "idx" << idx << "raw" << LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("TimeSettings"), QStringLiteral("TimeZone"), QChar(QLatin1Char('-')));
|
|
if (idx >= 0)
|
|
m_ui->cbTimeZone->setCurrentIndex(idx);
|
|
else
|
|
m_ui->cbTimeZone->setCurrentIndex(m_ui->cbTimeZone->findData(0));
|
|
|
|
|
|
|
|
|
|
// timezone stuff
|
|
bool k = false;
|
|
m_ui->dtCurrentTime->setTimezoneHoursOffset(timezone2Hours(tz.toLatin1(), &k));
|
|
|
|
auto timezoneChanged = [this](int)
|
|
{
|
|
char tz = m_ui->cbTimeZone->currentData().toInt(); // careful
|
|
|
|
if (tz == 0) // 'exercise default' entry
|
|
tz = DTG::timezoneExerciseDefault();
|
|
|
|
bool k = false;
|
|
int tzh = timezone2Hours(tz, &k);
|
|
|
|
if (!k)
|
|
return;
|
|
|
|
m_currentOffset = tzh;
|
|
|
|
emit signalTimezoneChanged(tzh);
|
|
|
|
QSignalBlocker b1(m_ui->dtCurrentTime);
|
|
m_ui->dtCurrentTime->setTimezoneHoursOffset(tzh);
|
|
|
|
m_timeControl->setTimezoneHoursOffset(tzh);
|
|
|
|
m_intervalModel.setTimezoneHoursOffset(tzh);
|
|
m_markerModel->setTimezoneHoursOffset(tzh);
|
|
};
|
|
|
|
|
|
connect(m_ui->cbTimeZone, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
this, [timezoneChanged](int)
|
|
{
|
|
timezoneChanged(0);
|
|
});
|
|
|
|
|
|
timezoneChanged(0); // initial setting
|
|
|
|
|
|
|
|
|
|
|
|
|
|
connect(m_playbackApp.objectWrapper(), &ObjectWrapper::signalVisible,
|
|
this, [this]()
|
|
{
|
|
m_videoScreenSelectionDone = true;
|
|
|
|
if (!m_timelineIsComplete)
|
|
return;
|
|
|
|
if (!m_autostartOnce)
|
|
return;
|
|
|
|
m_autostartOnce = false;
|
|
startPlayback();
|
|
});
|
|
|
|
connect(m_playbackApp.objectWrapper(), &ObjectWrapper::signalPause,
|
|
this, &AvmPlaybackMenu::onPauseFromPlaybackApp);
|
|
|
|
connect(m_playbackApp.objectWrapper(), &ObjectWrapper::signalResume,
|
|
this, &AvmPlaybackMenu::onResumeFromPlaybackApp);
|
|
|
|
return true;
|
|
}
|
|
|
|
void AvmPlaybackMenu::onTabChanged(int index)
|
|
{
|
|
switch (index)
|
|
{
|
|
case 0: // clips
|
|
{
|
|
onStreamSelected();
|
|
break;
|
|
}
|
|
|
|
case 1: // markers
|
|
{
|
|
m_ui->pbIntervalBackward->setEnabled(false);
|
|
m_ui->pbIntervalForward->setEnabled(false);
|
|
onMarkerStreamSelected();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
Q_ASSERT(index > 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AvmPlaybackMenu::onPauseFromPlaybackApp()
|
|
{
|
|
m_buttonStateBeforePlaybackAppPause = m_currentButtonState;
|
|
|
|
switch (m_currentButtonState)
|
|
{
|
|
case ButtonStates::AllOff:
|
|
return;
|
|
case ButtonStates::Started:
|
|
onPauseClicked();
|
|
Q_FALLTHROUGH();
|
|
case ButtonStates::Paused:
|
|
Q_FALLTHROUGH();
|
|
case ButtonStates::Stopped:
|
|
Q_FALLTHROUGH();
|
|
default:
|
|
setButtonStates(ButtonStates::AllOff);
|
|
disableAll();
|
|
emit setTopicButtonStates(false);
|
|
return;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void AvmPlaybackMenu::onResumeFromPlaybackApp()
|
|
{
|
|
qWarning() << "onResumeFromPlaybackApp";
|
|
|
|
switch (m_buttonStateBeforePlaybackAppPause)
|
|
{
|
|
case ButtonStates::AllOff:
|
|
return;
|
|
case ButtonStates::Started:
|
|
enableAll();
|
|
onStartClicked();
|
|
emit setTopicButtonStates(true);
|
|
return;
|
|
case ButtonStates::Paused:
|
|
Q_FALLTHROUGH();
|
|
case ButtonStates::Stopped:
|
|
Q_FALLTHROUGH();
|
|
default:
|
|
enableAll();
|
|
setButtonStates(m_buttonStateBeforePlaybackAppPause);
|
|
emit setTopicButtonStates(true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void AvmPlaybackMenu::addStream(QComboBox *cb)
|
|
{
|
|
const auto currentStream(cb->currentData().value<AvaStream>());
|
|
|
|
if (currentStream.internalName.isEmpty())
|
|
return;
|
|
|
|
const int streamCount = m_ui->lwStreams->count();
|
|
if (streamCount >= m_maxSelectedStreams)
|
|
{
|
|
new LtsWidgets::LtsMessageDialogNew(this, tr("Cannot add more than %1 streams").arg(m_maxSelectedStreams));
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < m_ui->lwStreams->count(); i++)
|
|
{
|
|
const QListWidgetItem *item = m_ui->lwStreams->item(i);
|
|
if (item->data(Qt::UserRole).value<AvaStream>() == currentStream)
|
|
return;
|
|
}
|
|
|
|
auto item = new QListWidgetItem(currentStream.internalName);
|
|
item->setData(Qt::UserRole, QVariant::fromValue(currentStream));
|
|
|
|
m_ui->lwStreams->addItem(item);
|
|
|
|
trySelectedStreams();
|
|
}
|
|
|
|
void AvmPlaybackMenu::onAddStreamClip()
|
|
{
|
|
addStream(m_ui->cbStreams);
|
|
}
|
|
|
|
void AvmPlaybackMenu::onAddStreamMarker()
|
|
{
|
|
addStream(m_ui->cbMarkerStreams);
|
|
}
|
|
|
|
void AvmPlaybackMenu::onRemoveSelectedStreams()
|
|
{
|
|
const QModelIndexList indexList = m_ui->lwStreams->selectionModel()->selectedRows();
|
|
|
|
for (const QModelIndex &idx : indexList)
|
|
delete m_ui->lwStreams->item(idx.row());
|
|
}
|
|
|
|
void AvmPlaybackMenu::onStreamNameMappingChanged(/*int32_t \*mountingPointId*\, const QString &internalName, const QString &displayName*/)
|
|
{
|
|
QSignalBlocker block1(m_ui->cbStreams);
|
|
for (int i = 0; i < m_ui->cbStreams->count(); i++)
|
|
m_ui->cbStreams->setItemText(i, m_streamNameMapping->displayName(m_ui->cbStreams->itemData(i).value<AvaStream>().internalName));
|
|
|
|
QSignalBlocker block2(m_ui->cbMarkerStreams);
|
|
for (int i = 0; i < m_ui->cbMarkerStreams->count(); i++)
|
|
m_ui->cbMarkerStreams->setItemText(i, m_streamNameMapping->displayName(m_ui->cbMarkerStreams->itemData(i).value<AvaStream>().internalName));
|
|
|
|
for (int i = 0; i < m_ui->lwStreams->count(); i++)
|
|
{
|
|
QListWidgetItem *item = m_ui->lwStreams->item(i);
|
|
item->setData(Qt::UserRole, m_streamNameMapping->displayName(item->data(Qt::UserRole).value<AvaStream>().internalName));
|
|
}
|
|
}
|
|
|
|
bool AvmPlaybackMenu::trySelectedStreams()
|
|
{
|
|
if (!m_ui->lwStreams->count())
|
|
return false;
|
|
|
|
QVector<AvaStream> vec;
|
|
|
|
for (int i = 0; i < m_ui->lwStreams->count(); i++)
|
|
vec.append(m_ui->lwStreams->item(i)->data(Qt::UserRole).value<AvaStream>());
|
|
|
|
onStreamSelectionChanged(vec);
|
|
|
|
return true;
|
|
}
|
|
|
|
void AvmPlaybackMenu::onStreamSelected()
|
|
{
|
|
if (trySelectedStreams())
|
|
return;
|
|
|
|
if (m_ui->cbStreams->currentIndex() < 0)
|
|
return;
|
|
|
|
QVector<AvaStream> vec;
|
|
AvaStream stream = m_ui->cbStreams->currentData().value<AvaStream>();
|
|
vec.append(stream);
|
|
onStreamSelectionChanged(vec);
|
|
|
|
m_currentStream = stream;
|
|
emit signalCurrentStreamChanged(m_streamNameMapping->displayName(stream.internalName));
|
|
}
|
|
|
|
void AvmPlaybackMenu::onMarkerStreamSelected()
|
|
{
|
|
if (trySelectedStreams())
|
|
return;
|
|
|
|
if (m_ui->cbMarkerStreams->currentIndex() < 0)
|
|
return;
|
|
|
|
const QVariant data(m_ui->cbMarkerStreams->currentData());
|
|
if (data.isValid() && data.canConvert<AvaStream>())
|
|
{
|
|
const AvaStream stream(data.value<AvaStream>());
|
|
onStreamSelectionChanged({stream});
|
|
|
|
m_currentStream = stream;
|
|
emit signalCurrentStreamChanged(m_streamNameMapping->displayName(stream.internalName));
|
|
}
|
|
else
|
|
{
|
|
m_currentStream = boost::none;
|
|
emit signalCurrentStreamChanged({});
|
|
}
|
|
}
|
|
|
|
void AvmPlaybackMenu::setStreamByName(const QString &name)
|
|
{
|
|
using AudioVideoManagement::AvaStream;
|
|
|
|
for (int i = 0; i < m_ui->cbStreams->count(); ++i)
|
|
{
|
|
QVariant data = m_ui->cbStreams->itemData(i);
|
|
Q_ASSERT(data.canConvert<AvaStream>());
|
|
AvaStream stream = m_ui->cbStreams->itemData(i).value<AvaStream>();
|
|
if (stream.internalName == name)
|
|
{
|
|
m_ui->cbStreams->setCurrentIndex(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_ui->cbStreams->setCurrentIndex(0);
|
|
}
|
|
|
|
void AvmPlaybackMenu::onMarkerModifyReply(const Core::FunctionReplyObject &replyObject)
|
|
{
|
|
m_requestHelper->requestReply(replyObject);
|
|
}
|
|
|
|
void AvmPlaybackMenu::retranslate()
|
|
{
|
|
m_ui->retranslateUi(this);
|
|
m_ui->tabWidget->setTabText(0, m_isAudio ? tr("Audio clips") : tr("Video clips"));
|
|
}
|
|
|
|
void AvmPlaybackMenu::setStreamTime(qint64 time)
|
|
{
|
|
m_ui->dtCurrentTime->setMsecsSinceEpoch(time);
|
|
}
|
|
|
|
//void AvmPlaybackMenu::setStreamTime(AvaTimePoint t)
|
|
//{
|
|
// m_savedPos = t;
|
|
// m_intervalModel.setCurrentPosition(t);
|
|
// m_timeControl->setTime(t);
|
|
//}
|
|
|
|
void AvmPlaybackMenu::onSessionEstablished()
|
|
{
|
|
m_ui->lblAvsConnectionStatus->setText(QStringLiteral("<font color='green'>%1</font>").arg(tr("established")));
|
|
|
|
enableAll();
|
|
|
|
if (!m_intervalModel.currentPosition())
|
|
{
|
|
m_ui->pbIntervalBackward->setEnabled(false);
|
|
m_ui->pbIntervalForward->setEnabled(false);
|
|
}
|
|
else
|
|
{
|
|
bool forceSignal = true;
|
|
m_intervalModel.setCurrentPosition(*m_intervalModel.currentPosition(), forceSignal);
|
|
}
|
|
|
|
m_avaCtrl->setVolume(m_ui->slVolume->value());
|
|
|
|
if (!m_selectedStreams)
|
|
{
|
|
// This is the first connection. Or a reconnect and no streams were available before.
|
|
// Query the list of available streams in order to initially fill the ComboBox.
|
|
AvaStreamType type = AvaStreamType::Video;
|
|
if (m_isAudio)
|
|
type = AvaStreamType::Audio;
|
|
m_avaCtrl->getStreamList(type, AvaStreamSource::Server, this, &AvmPlaybackMenu::onStreamListReceived);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// A re-connect happend.
|
|
// Then, we just want to restore the previously selected streams.
|
|
onStreamSelectionChanged(*m_selectedStreams);
|
|
}
|
|
}
|
|
|
|
void AvmPlaybackMenu::onDisconnected()
|
|
{
|
|
m_ui->lblAvsConnectionStatus->setText(QStringLiteral("<font color='orange'>%1</font>").arg(tr("connecting...")));
|
|
setButtonStates(ButtonStates::AllOff);
|
|
emit setTopicButtonStates(false);
|
|
disableAll();
|
|
m_timelineBuilder->reset();
|
|
}
|
|
|
|
|
|
void AvmPlaybackMenu::preselectStream(const QString &fallbackInternalStreamName)
|
|
{
|
|
if (m_markerIdPreset)
|
|
{
|
|
m_ui->tabWidget->setCurrentIndex(1); // marker tab
|
|
m_ui->tvMarker->selectRow(m_markerModel->findIndexForId(*m_markerIdPreset));
|
|
selectMarker(*m_markerIdPreset);
|
|
m_markerIdPreset.reset();
|
|
return;
|
|
}
|
|
|
|
{
|
|
const QString currentStreamName(m_presetStreamName.is_initialized() ? *m_presetStreamName : QString());
|
|
const QString currentInternalName(currentStreamName.isEmpty() ? fallbackInternalStreamName : QString());
|
|
|
|
DEBUG_LOG(m_logger) << "currentStreamName: " << currentStreamName;
|
|
DEBUG_LOG(m_logger) << "currentInternalName: " << currentInternalName;
|
|
|
|
m_presetStreamName.reset();
|
|
|
|
if (!currentInternalName.isEmpty() || !currentStreamName.isEmpty())
|
|
{
|
|
DEBUG_LOG(m_logger) << "stream preset set - searching for '" << currentStreamName << "'..";
|
|
|
|
bool found = false;
|
|
for (int i = 0; i < m_ui->cbStreams->count(); i++)
|
|
{
|
|
const AvaStream &curStream = m_ui->cbStreams->itemData(i).value<AvaStream>();
|
|
|
|
DEBUG_LOG(m_logger) << "stream " << i << ": " << curStream.displayName << " (" << curStream.internalName << ")";
|
|
|
|
if ((!currentInternalName.isEmpty() && curStream.displayName == currentInternalName) ||
|
|
(!currentStreamName.isEmpty() && curStream.internalName == currentStreamName))
|
|
{
|
|
found = true;
|
|
DEBUG_LOG(m_logger) << "stream preset set, found & selected";
|
|
m_ui->cbStreams->setCurrentIndex(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
WARNING_LOG(m_logger) << "new stream list does not contain old selected stream (" << currentInternalName << ") !";
|
|
}
|
|
else
|
|
DEBUG_LOG(m_logger) << "no stream preset set";
|
|
}
|
|
|
|
|
|
onStreamSelected();
|
|
}
|
|
|
|
|
|
void AvmPlaybackMenu::processPresets()
|
|
{
|
|
if (m_presetStartTime)
|
|
m_savedPos = *m_presetStartTime;
|
|
|
|
m_presetStartTime.reset();
|
|
|
|
preselectStream(m_ui->cbStreams->currentData().value<AvaStream>().internalName /* <= fallback stream */);
|
|
}
|
|
|
|
|
|
void AvmPlaybackMenu::onStreamListReceived(const QVector<AvaStream> &streams)
|
|
{
|
|
m_streams = streams;
|
|
|
|
QSignalBlocker block1(m_ui->cbStreams);
|
|
m_ui->cbStreams->clear();
|
|
for (const AvaStream &s : streams)
|
|
{
|
|
QString displayName = m_streamNameMapping->displayName(s.internalName);
|
|
m_ui->cbStreams->addItem(displayName, QVariant::fromValue(s));
|
|
}
|
|
|
|
processPresets();
|
|
}
|
|
|
|
|
|
void AvmPlaybackMenu::startPlayback(bool usePresets)
|
|
{
|
|
Q_ASSERT(m_currentButtonState != ButtonStates::AllOff);
|
|
|
|
if (m_currentButtonState == ButtonStates::Paused || m_currentButtonState == ButtonStates::Started)
|
|
onStopClicked();
|
|
|
|
if (usePresets && m_presetStartTime)
|
|
{
|
|
preselectStream(m_ui->cbStreams->currentData().value<AvaStream>().internalName);
|
|
m_savedPos = m_presetStartTime.get();
|
|
m_presetStartTime.reset();
|
|
}
|
|
else
|
|
onStartClicked();
|
|
}
|
|
|
|
|
|
void AvmPlaybackMenu::setSavedPos(qint64 msecs)
|
|
{
|
|
m_savedPos = AvaTimePoint(std::chrono::duration_cast<AvaDuration>(std::chrono::milliseconds(msecs)));
|
|
}
|
|
|
|
|
|
void AvmPlaybackMenu::onTimelineComplete(const AvaTimeline &timeline)
|
|
{
|
|
DEBUG_LOG(m_logger) << "onTimelineComplete";
|
|
|
|
m_intervalModel.setData(timeline.parts());
|
|
m_ui->tavIntervals->resizeColumnsToContents();
|
|
setButtonStates(ButtonStates::Stopped);
|
|
emit setTopicButtonStates(true);
|
|
|
|
if (timeline.isEmpty())
|
|
return;
|
|
|
|
AvaTimePoint newPos = timeline.startTime();
|
|
|
|
if (m_savedPos)
|
|
{
|
|
newPos = *m_savedPos;
|
|
m_savedPos.reset();
|
|
}
|
|
|
|
DEBUG_LOG(m_logger) << "onTimelineComplete " << std::chrono::milliseconds(newPos.time_since_epoch()).count();
|
|
m_avaCtrl->setAbsPosition(m_playbackApp.dialogId(), ALL_STREAMS(), newPos);
|
|
|
|
m_timelineIsComplete = true;
|
|
|
|
if (m_autostartOnce)
|
|
{
|
|
/* audio clips can be played as soon as it can, but as videos need a screen selection
|
|
* first, we need to wait for the selection first
|
|
* but if the selection was done faster than the timeline query, then it can already
|
|
* be played. otherwise the playback will start after the user selected the video screen
|
|
*/
|
|
if (m_isAudio || m_videoScreenSelectionDone)
|
|
{
|
|
m_autostartOnce = false;
|
|
startPlayback();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* When we receive a stream position from AVS we only update the UI when command queue is empty.
|
|
* Otherwise we just save the position for later. Then, when the command queue is empty again,
|
|
* we apply the last saved position.
|
|
*/
|
|
void AvmPlaybackMenu::onStreamPositionReceived(const QString &dialogId, const QString &streamName, AvaTimePoint pos)
|
|
{
|
|
Q_UNUSED(streamName);
|
|
|
|
if (dialogId != m_playbackApp.dialogId())
|
|
return;
|
|
|
|
m_lastAvsPos = pos;
|
|
DEBUG_LOG(m_logger) << "onStreamPositionReceived: last avs pos now: " << std::chrono::milliseconds((*m_lastAvsPos).time_since_epoch()).count() << " - direct apply? " << m_avaCtrl->isEmpty();
|
|
|
|
if (m_avaCtrl->isEmpty())
|
|
applyAvsPos();
|
|
}
|
|
|
|
void AvmPlaybackMenu::onVolumeReceived(AvaVolume db)
|
|
{
|
|
m_lastAvsVolume = db;
|
|
if (m_avaCtrl->isEmpty())
|
|
applyAvsVolume();
|
|
}
|
|
|
|
void AvmPlaybackMenu::onCommandQueueEmpty()
|
|
{
|
|
// >> is needed to make the clip selection work, but ... why?? that's weird, because a stream selection does an applyAvsPos and then this method does it again....
|
|
|
|
if (m_lastAvsPos)
|
|
applyAvsPos();
|
|
|
|
if (m_lastAvsVolume)
|
|
applyAvsVolume();
|
|
}
|
|
|
|
void AvmPlaybackMenu::applyAvsPos()
|
|
{
|
|
Q_ASSERT(m_lastAvsPos);
|
|
|
|
// cfroebel 2018-02-28: What is this good for?
|
|
// int difTimes = AudioVideoManagement::toDateTime(*m_lastAvsPos).toMSecsSinceEpoch() - m_ui->dteCurrentTime->dateTime().toMSecsSinceEpoch();
|
|
// if (difTimes > 0 && difTimes < 750)
|
|
// return;
|
|
|
|
|
|
//qint64 y = std::chrono::milliseconds((*m_lastAvsPos).time_since_epoch()).count();
|
|
//DEBUG_LOG(m_logger) << "applyAvsPos: " << y << " (" << msecsSinceEpochToString(y) << ")";
|
|
|
|
|
|
m_intervalModel.setCurrentPosition(*m_lastAvsPos);
|
|
m_timeControl->setTime(*m_lastAvsPos);
|
|
m_lastAvsPos.reset();
|
|
}
|
|
|
|
void AvmPlaybackMenu::applyAvsVolume()
|
|
{
|
|
Q_ASSERT(m_lastAvsVolume);
|
|
m_ui->slVolume->setValue(*m_lastAvsVolume);
|
|
}
|
|
|
|
|
|
void AvmPlaybackMenu::onUiTimeChanged(AvaTimePoint t)
|
|
{
|
|
if (m_currentButtonState != ButtonStates::Paused &&
|
|
m_currentButtonState != ButtonStates::Stopped)
|
|
return;
|
|
|
|
DEBUG_LOG(m_logger) << "onUiTimeChanged " << std::chrono::milliseconds(t.time_since_epoch()).count();
|
|
m_avaCtrl->setAbsPosition(m_playbackApp.dialogId(), ALL_STREAMS(), t);
|
|
}
|
|
|
|
void AvmPlaybackMenu::onStreamSelectionChanged(const QVector<AvaStream> &selectedStreams)
|
|
{
|
|
DEBUG_LOG(m_logger) << "onStreamSelectionChanged count = " << selectedStreams.count();
|
|
|
|
Q_ASSERT(!selectedStreams.isEmpty());
|
|
|
|
// Save the currentPosition and apply it again after we received all queryTimeline-Responses.
|
|
if (!m_savedPos)
|
|
m_savedPos = m_intervalModel.currentPosition();
|
|
|
|
m_selectedStreams = selectedStreams;
|
|
|
|
onStopClicked();
|
|
|
|
m_intervalModel.reset();
|
|
setButtonStates(ButtonStates::AllOff);
|
|
emit setTopicButtonStates(false);
|
|
m_timelineBuilder->queryTimeline(selectedStreams); // => onTimelineReceived
|
|
|
|
// use chooseStreamOverview even when there's only one stream
|
|
m_avaCtrl->chooseStreamOverview(m_playbackApp.dialogId(), AvaStream::toStringList(selectedStreams));
|
|
m_avaCtrl->setPint(m_playbackApp.dialogId(), ALL_STREAMS(), m_skipEmpty);
|
|
}
|
|
|
|
/**<
|
|
* Called when a stream interval was activated in the TableView.
|
|
*/
|
|
void AvmPlaybackMenu::onIntervalActivated(const QModelIndex &index)
|
|
{
|
|
AvaTimelinePart interval = m_intervalModel.getRowData(index.row());
|
|
DEBUG_LOG(m_logger) << "start = " << std::chrono::milliseconds(interval.first.time_since_epoch()).count();
|
|
m_avaCtrl->setAbsPosition(m_playbackApp.dialogId(), ALL_STREAMS(), interval.first); // => onStreamPositionm_savedPosReceived
|
|
/* NOTE: above method's slot 'onStreamPositionReceived' does:
|
|
* m_intervalModel.setCurrentPosition(...);
|
|
* m_timeControl->setTime(...);
|
|
*/
|
|
|
|
//clear marker selection
|
|
m_ui->tvMarker->selectionModel()->clear();
|
|
m_ui->tavIntervals->selectionModel()->clear();
|
|
|
|
//emit signalSetStart(toMSecSinceEpoch(interval.first));
|
|
//emit signalSetEnd(toMSecSinceEpoch(interval.second));
|
|
}
|
|
|
|
void AvmPlaybackMenu::setStreamName(const QString &internalStreamName)
|
|
{
|
|
int idx = -1;
|
|
|
|
for (int i = 0; i < m_ui->cbStreams->count(); i++)
|
|
{
|
|
if (m_ui->cbStreams->itemData(i).value<AvaStream>().internalName == internalStreamName)
|
|
{
|
|
idx = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (idx == -1)
|
|
{
|
|
new LtsWidgets::LtsMessageDialogNew(this, tr("The sequence uses a stream that is not available anymore"), tr("Stream from sequence not found"));
|
|
m_ui->cbStreams->setCurrentText(QString());
|
|
return;
|
|
}
|
|
|
|
// select stream
|
|
QVector<AvaStream> vec;
|
|
AvaStream stream = m_ui->cbStreams->itemData(idx).value<AvaStream>();
|
|
vec.append(stream);
|
|
onStreamSelectionChanged(vec);
|
|
|
|
m_ui->cbStreams->setCurrentIndex(idx);
|
|
}
|
|
|
|
bool AvmPlaybackMenu::getSkipPause() const
|
|
{
|
|
return m_skipEmpty;
|
|
}
|
|
|
|
void AvmPlaybackMenu::setSkipPause(bool bSkip)
|
|
{
|
|
if (m_skipEmpty == bSkip)
|
|
return;
|
|
m_skipEmpty = !bSkip; // onSkipPauseClicked toggles the state
|
|
onSkipPauseClicked();
|
|
}
|
|
|
|
QString AvmPlaybackMenu::getInternalStreamName() const
|
|
{
|
|
if (m_currentStream)
|
|
return m_currentStream.get().internalName;
|
|
return {};
|
|
}
|
|
|
|
void AvmPlaybackMenu::selectMarker(const MarkerManagement::Marker &marker)
|
|
{
|
|
if (marker.id() == 0) // invalid?
|
|
return;
|
|
|
|
QSignalBlocker blocker(m_ui->cbMarkerStreams);
|
|
(void)blocker;
|
|
|
|
const auto &rowData = marker;//m_markerModel->getRowData(index.row());
|
|
DEBUG_LOG(m_logger) << "AvmPlaybackMenu::onMarkerClicked name = " << QString::fromStdString(rowData.name())
|
|
<< " note = " << QString::fromStdString(rowData.note());
|
|
|
|
bool selectMarkerStream = false;
|
|
|
|
for (const MarkerManagement::MarkerDescription &desc : rowData.descriptions())
|
|
{
|
|
if ((desc.markerType() == MarkerManagement::MarkerType::Video && m_isAudio == false) ||
|
|
(desc.markerType() == MarkerManagement::MarkerType::Audio && m_isAudio == true))
|
|
{
|
|
m_ui->cbMarkerStreams->clear();
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (const MarkerManagement::MarkerDescription &desc : rowData.descriptions())
|
|
{
|
|
DEBUG_LOG(m_logger) << " description = " << QString::fromStdString(desc.content())
|
|
<< " desc.markerType()=" << desc.markerType();
|
|
if ((desc.markerType() == MarkerManagement::MarkerType::Video && m_isAudio == false) ||
|
|
(desc.markerType() == MarkerManagement::MarkerType::Audio && m_isAudio == true))
|
|
{
|
|
AvaStream avaStream;
|
|
avaStream.type = m_isAudio ? AvaStreamType::Audio : AvaStreamType::Video;
|
|
avaStream.source = AvaStreamSource::Server;
|
|
|
|
AvaStream selectedStream;
|
|
const QStringList streams(QString::fromStdString(desc.content()).split(QLatin1Char(',')));
|
|
|
|
for (const QString &sr : streams)
|
|
{
|
|
bool bIsSelected = false;
|
|
avaStream.internalName = sr.simplified();
|
|
|
|
if (sr.endsWith(QLatin1Char('*')))
|
|
{
|
|
QString &in = avaStream.internalName;
|
|
in = in.left(in.size() - 1);
|
|
bIsSelected = true;
|
|
}
|
|
|
|
avaStream.displayName = m_streamNameMapping->displayName(avaStream.internalName);
|
|
m_ui->cbMarkerStreams->addItem(avaStream.displayName, QVariant::fromValue(avaStream));
|
|
DEBUG_LOG(m_logger) << "streams[" << avaStream.internalName << "]"
|
|
<< " internaName=" << avaStream.internalName
|
|
<< " displayName=" << avaStream.displayName;
|
|
if (bIsSelected && selectedStream.internalName.isEmpty())
|
|
selectedStream = avaStream;
|
|
}
|
|
|
|
if (!selectedStream.displayName.isEmpty())
|
|
{
|
|
const QVariant data(QVariant::fromValue(selectedStream));
|
|
m_ui->cbMarkerStreams->setCurrentIndex(m_ui->cbMarkerStreams->findData(data));
|
|
selectMarkerStream = true; // don't select hundreds of streams, only do it once
|
|
}
|
|
}
|
|
}
|
|
|
|
qint64 time = rowData.timeStamp() - rowData.timeStampOffset();
|
|
|
|
DEBUG_LOG(m_logger) << "marker time: " << time << " (" << QDateTime::fromMSecsSinceEpoch(time).toString(QStringLiteral("dd.MM.yy HH:mm:ss.zzz")) << ")";
|
|
|
|
std::chrono::milliseconds t(time);
|
|
AvaTimePoint p(std::chrono::duration_cast<AvaDuration>(t));
|
|
|
|
//QSignalBlocker blocker(m_timeControl.get());
|
|
//onStopClicked();
|
|
|
|
m_savedPos = p;
|
|
|
|
// m_timeControl->setTime(p);
|
|
// m_avaCtrl->setAbsPosition(m_playbackApp.dialogId(), ALL_STREAMS(), p);
|
|
|
|
if (selectMarkerStream)
|
|
onMarkerStreamSelected();
|
|
|
|
emit markerSelected(QString::fromStdString(rowData.note()));
|
|
//emit signalSetStart(time);
|
|
|
|
m_ui->tavIntervals->selectionModel()->clear();
|
|
}
|
|
|
|
void AvmPlaybackMenu::selectMarker(int markerId)
|
|
{
|
|
selectMarker(m_mrkDataIfc->getDtc()->marker(markerId));
|
|
}
|
|
|
|
void AvmPlaybackMenu::onMarkerClicked(const QModelIndex &index)
|
|
{
|
|
if (!index.isValid())
|
|
return;
|
|
|
|
selectMarker(m_markerModel->getRowData(index.row()));
|
|
}
|
|
|
|
void AvmPlaybackMenu::onSetMarkerClicked()
|
|
{
|
|
if (!m_selectedStreams || m_selectedStreams.get().isEmpty())
|
|
{
|
|
new LtsWidgets::LtsMessageDialogNew(this, tr("No stream selected to create a marker from."));
|
|
WARNING_LOG(m_logger) << "onSetMarkerClicked called, but no selected streams available";
|
|
return;
|
|
}
|
|
|
|
bool ok = false;
|
|
QString note = QInputDialog::getText(this, tr("Enter Note"),
|
|
tr("Note:"), QLineEdit::Normal, QString(), &ok);
|
|
|
|
if (!ok)
|
|
return;
|
|
|
|
MarkerManagement::Marker mrk;
|
|
MarkerManagement::MarkerDescription ma;
|
|
ma.markerType(m_isAudio ? MarkerManagement::MarkerType::Audio : MarkerManagement::MarkerType::Video);
|
|
auto &descriptions = mrk.descriptions();
|
|
QStringList streamList;
|
|
streamList.reserve(m_selectedStreams->size());
|
|
const QLatin1Char star = QLatin1Char('*');
|
|
const auto &x = *m_selectedStreams;
|
|
for (const AvaStream &stream : x)
|
|
{
|
|
streamList += stream.internalName;
|
|
streamList.last().append(star);
|
|
}
|
|
ma.content() = streamList.join(QStringLiteral(", ")).toStdString();
|
|
descriptions.resize(descriptions.size() + 1, ma);
|
|
mrk.note(note.toStdString());
|
|
mrk.timeStamp(m_timeControl->timeMSec());
|
|
mrk.timeStampOffset(0);
|
|
mrk.operatorId(LtsSessionData::LtsSessionData::getInstance()->getOperatorId());
|
|
|
|
m_requestHelper->startSingleRequest([mrk, this]()
|
|
{
|
|
return m_mrkDataIfc->setMarker(mrk, this);
|
|
});
|
|
}
|
|
|
|
void AvmPlaybackMenu::onStartClicked()
|
|
{
|
|
// QString displayName = m_ui->cbMarkerStreams->currentText();
|
|
// QString streamName = m_streamNameMapping->internalName(displayName);
|
|
DEBUG_LOG(m_logger) << "onStartClicked"
|
|
<< " dialogId=" << m_playbackApp.dialogId()
|
|
<< " currentSpeed=" << currentSpeed()
|
|
// << " displayName=" << displayName
|
|
// << " streamName=" << streamName
|
|
;
|
|
|
|
if (!m_selectedStreams || m_selectedStreams.get().isEmpty())
|
|
{
|
|
if (qobject_cast<QAbstractButton *>(sender()) != nullptr) // called by pressing a button => pressing a button should never do nothing on an error, but show an error message
|
|
{
|
|
new LtsWidgets::LtsMessageDialogNew(this, tr("No stream selected to start a replay from."));
|
|
return;
|
|
}
|
|
|
|
WARNING_LOG(m_logger) << "onStartClicked called, but no selected streams available";
|
|
return;
|
|
}
|
|
|
|
m_avaCtrl->startStream(m_playbackApp.dialogId(), ALL_STREAMS(), currentSpeed());
|
|
m_avaCtrl->positionUpdate(m_playbackApp.dialogId(), m_selectedStreams->first().toString(), std::chrono::seconds(1));
|
|
setButtonStates(ButtonStates::Started);
|
|
}
|
|
|
|
void AvmPlaybackMenu::onPauseClicked()
|
|
{
|
|
m_avaCtrl->pauseStream(m_playbackApp.dialogId(), ALL_STREAMS());
|
|
setButtonStates(ButtonStates::Paused);
|
|
}
|
|
|
|
void AvmPlaybackMenu::onStopClicked()
|
|
{
|
|
m_avaCtrl->stopStream(m_playbackApp.dialogId(), ALL_STREAMS());
|
|
setButtonStates(ButtonStates::Stopped);
|
|
}
|
|
|
|
void AvmPlaybackMenu::onSkipPauseClicked()
|
|
{
|
|
m_skipEmpty = !m_skipEmpty;
|
|
if (m_skipEmpty)
|
|
m_ui->pbSkipPause->setIcon(QIcon(QStringLiteral("icon:media-vcr-nopause.svg")));
|
|
else
|
|
m_ui->pbSkipPause->setIcon(QIcon(QStringLiteral("icon:media-vcr-withpause.svg")));
|
|
m_avaCtrl->setPint(m_playbackApp.dialogId(), ALL_STREAMS(), m_skipEmpty);
|
|
}
|
|
|
|
void AvmPlaybackMenu::onAllStreams()
|
|
{
|
|
QSignalBlocker block2(m_ui->cbMarkerStreams);
|
|
m_ui->cbMarkerStreams->clear();
|
|
for (int i = 0; i < m_ui->cbStreams->count(); i++)
|
|
m_ui->cbMarkerStreams->addItem(m_ui->cbStreams->itemText(i), m_ui->cbStreams->itemData(i));
|
|
}
|
|
|
|
void AvmPlaybackMenu::onIntervalBackwardClicked()
|
|
{
|
|
const AvaTimelinePart &targetInterval = m_intervalModel.previousInterval();
|
|
DEBUG_LOG(m_logger) << "onIntervalBackwardClicked " << std::chrono::milliseconds(targetInterval.first.time_since_epoch()).count();
|
|
m_avaCtrl->setAbsPosition(m_playbackApp.dialogId(), ALL_STREAMS(), targetInterval.first);
|
|
}
|
|
|
|
void AvmPlaybackMenu::onIntervalForwardClicked()
|
|
{
|
|
const AvaTimelinePart &targetInterval = m_intervalModel.nextInterval();
|
|
DEBUG_LOG(m_logger) << "onIntervalForwardClicked " << std::chrono::milliseconds(targetInterval.first.time_since_epoch()).count();
|
|
m_avaCtrl->setAbsPosition(m_playbackApp.dialogId(), ALL_STREAMS(), targetInterval.first);
|
|
}
|
|
|
|
/**
|
|
* Called when the value of the speed slider changed.
|
|
*/
|
|
void AvmPlaybackMenu::onSpeedChanged()
|
|
{
|
|
m_ui->lbSpeedValue->setText(QString::number(currentSpeed()));
|
|
m_avaCtrl->setStreamSpeed(m_playbackApp.dialogId(), ALL_STREAMS(), currentSpeed());
|
|
if ( m_currentButtonState == ButtonStates::Paused)
|
|
m_avaCtrl->pauseStream(m_playbackApp.dialogId(), ALL_STREAMS());
|
|
else if ( m_currentButtonState == ButtonStates::Stopped)
|
|
m_avaCtrl->stopStream(m_playbackApp.dialogId(), ALL_STREAMS());
|
|
}
|
|
|
|
void AvmPlaybackMenu::onVolumeChanged(int percent)
|
|
{
|
|
m_ui->lbVolumeValue->setText(QString::number(percent));
|
|
m_avaCtrl->setVolume(percent);
|
|
}
|
|
|
|
/**
|
|
* Called when the current interval changed as a result of
|
|
* a position update or when the IntervalModel is reset.
|
|
*/
|
|
void AvmPlaybackMenu::onCurrentIntervalChanged(bool hasPreviousInterval, bool hasNextInterval, const QModelIndex &rowIndex)
|
|
{
|
|
if (m_ui->tabWidget->currentIndex() == 0)
|
|
{
|
|
m_ui->pbIntervalBackward->setEnabled(hasPreviousInterval);
|
|
m_ui->pbIntervalForward->setEnabled(hasNextInterval);
|
|
}
|
|
else
|
|
{
|
|
m_ui->pbIntervalBackward->setEnabled(false);
|
|
m_ui->pbIntervalForward->setEnabled(false);
|
|
}
|
|
|
|
if (rowIndex.isValid())
|
|
{
|
|
m_ui->tavIntervals->scrollTo(rowIndex);
|
|
|
|
m_timeControl->setInterval(m_intervalModel.getRowData(rowIndex.row()));
|
|
return;
|
|
}
|
|
|
|
if (m_timelineBuilder->timeline().isEmpty())
|
|
return;
|
|
|
|
AvaTimelinePart interval{m_timelineBuilder->timeline().startTime(), m_timelineBuilder->timeline().endTime()};
|
|
m_timeControl->setInterval(interval);
|
|
}
|
|
|
|
AvaSpeed AvmPlaybackMenu::currentSpeed() const
|
|
{
|
|
int uiValue = m_ui->slSpeed->value();
|
|
Q_ASSERT(uiValue < AvaSpeed::allowedValues().size());
|
|
AvaSpeed speed = AvaSpeed::allowedValues().at(uiValue);
|
|
return speed;
|
|
}
|
|
|
|
/**
|
|
* For now deals only with the start, pause, stop buttons.
|
|
*/
|
|
void AvmPlaybackMenu::setButtonStates(ButtonStates s)
|
|
{
|
|
switch (s)
|
|
{
|
|
case ButtonStates::AllOff:
|
|
m_ui->pbStart->setEnabled(false);
|
|
m_ui->pbStart->show();
|
|
m_ui->pbPause->setEnabled(false);
|
|
m_ui->pbPause->hide();
|
|
m_ui->pbStop->setEnabled(false);
|
|
m_ui->tvMarker->setEnabled(false);
|
|
m_ui->dtCurrentTime->setEnabled(false);
|
|
m_ui->slSequenceSlider->setEnabled(false);
|
|
m_ui->pbSetMarker->setEnabled(false);
|
|
break;
|
|
|
|
case ButtonStates::Started:
|
|
m_ui->pbStart->setEnabled(false);
|
|
m_ui->pbStart->hide();
|
|
m_ui->pbPause->setEnabled(true);
|
|
m_ui->pbPause->show();
|
|
m_ui->pbStop->setEnabled(true);
|
|
m_ui->tvMarker->setEnabled(false);
|
|
m_ui->dtCurrentTime->setEnabled(false);
|
|
m_ui->slSequenceSlider->setEnabled(false);
|
|
m_ui->pbSetMarker->setEnabled(false);
|
|
break;
|
|
|
|
case ButtonStates::Paused:
|
|
m_ui->pbStart->setEnabled(true);
|
|
m_ui->pbStart->show();
|
|
m_ui->pbPause->setEnabled(false);
|
|
m_ui->pbPause->hide();
|
|
m_ui->pbStop->setEnabled(true);
|
|
m_ui->tvMarker->setEnabled(true);
|
|
m_ui->dtCurrentTime->setEnabled(true);
|
|
m_ui->slSequenceSlider->setEnabled(true);
|
|
m_ui->pbSetMarker->setEnabled(true);
|
|
break;
|
|
|
|
case ButtonStates::Stopped:
|
|
m_ui->pbStart->setEnabled(true);
|
|
m_ui->pbStart->show();
|
|
m_ui->pbPause->setEnabled(false);
|
|
m_ui->pbPause->hide();
|
|
m_ui->pbStop->setEnabled(false);
|
|
m_ui->tvMarker->setEnabled(true);
|
|
m_ui->dtCurrentTime->setEnabled(true);
|
|
m_ui->slSequenceSlider->setEnabled(true);
|
|
m_ui->pbSetMarker->setEnabled(true);
|
|
break;
|
|
}
|
|
m_currentButtonState = s;
|
|
}
|
|
|
|
void AvmPlaybackMenu::enableAll(bool enabled)
|
|
{
|
|
m_ui->pbIntervalBackward->setEnabled(enabled);
|
|
m_ui->pbIntervalForward->setEnabled(enabled);
|
|
m_ui->pbSkipPause->setEnabled(enabled);
|
|
m_ui->slSpeed->setEnabled(enabled);
|
|
m_ui->slVolume->setEnabled(enabled);
|
|
m_ui->tavIntervals->setEnabled(enabled);
|
|
m_ui->tvMarker->setEnabled(enabled);
|
|
m_ui->cbStreams->setEnabled(enabled);
|
|
}
|
|
|
|
void AvmPlaybackMenu::hideStreamSelection()
|
|
{
|
|
// m_ui->wStreams->hide();
|
|
// m_ui->layoutWStream->removeItem(m_ui->vSpace);
|
|
m_ui->lblCbStreams->hide();
|
|
m_ui->cbStreams->hide();
|
|
}
|
|
|
|
qint64 AvmPlaybackMenu::getCurrentTime() const
|
|
{
|
|
return m_timeControl->timeMSec();
|
|
}
|
|
|
|
AvaSpeed AvmPlaybackMenu::getSpeed() const
|
|
{
|
|
return currentSpeed();
|
|
}
|
|
|
|
void AvmPlaybackMenu::setSpeed(AvaSpeed newSpeed)
|
|
{
|
|
m_ui->slSpeed->setValue(AvaSpeed::allowedValues().indexOf(newSpeed));
|
|
}
|
|
|
|
|
|
int AvmPlaybackMenu::getVolume() const
|
|
{
|
|
return m_ui->slVolume->value();
|
|
}
|
|
|
|
void AvmPlaybackMenu::setVolume(int newVolume)
|
|
{
|
|
m_ui->slVolume->setValue(newVolume);
|
|
}
|
|
} // namespace AudioVideoManagement
|
|
|
|
#include "avm_playback_menu.moc"
|
|
//
|