/** * @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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 &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(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(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 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 &streamName, const boost::optional &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(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 opmDtc; m_session->getDtc(opmDtc); std::shared_ptr flmGui; m_session->getForeignGuiInterface(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(std::chrono::milliseconds(sm))); AvaTimePoint e(std::chrono::duration_cast(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::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()); 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() == 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().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().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().internalName)); } } bool AvmPlaybackMenu::trySelectedStreams() { if (!m_ui->lwStreams->count()) return false; QVector vec; for (int i = 0; i < m_ui->lwStreams->count(); i++) vec.append(m_ui->lwStreams->item(i)->data(Qt::UserRole).value()); onStreamSelectionChanged(vec); return true; } void AvmPlaybackMenu::onStreamSelected() { if (trySelectedStreams()) return; if (m_ui->cbStreams->currentIndex() < 0) return; QVector vec; AvaStream stream = m_ui->cbStreams->currentData().value(); 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()) { const AvaStream stream(data.value()); 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 stream = m_ui->cbStreams->itemData(i).value(); 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("%1").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("%1").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(); 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().internalName /* <= fallback stream */); } void AvmPlaybackMenu::onStreamListReceived(const QVector &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().internalName); m_savedPos = m_presetStartTime.get(); m_presetStartTime.reset(); } else onStartClicked(); } void AvmPlaybackMenu::setSavedPos(qint64 msecs) { m_savedPos = AvaTimePoint(std::chrono::duration_cast(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 &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().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 vec; AvaStream stream = m_ui->cbStreams->itemData(idx).value(); 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(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(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" //