Skip to content

Commit

Permalink
MythPlayerCaptionsUI: Cleanup threading for interactive TV support
Browse files Browse the repository at this point in the history
- not complete as some methods need QObject support in the decoder -
which is not possible yet.
  • Loading branch information
mark-kendall committed Oct 29, 2020
1 parent 42719fb commit 1cafc2b
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 66 deletions.
12 changes: 8 additions & 4 deletions mythtv/libs/libmythtv/mheg/mhi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1049,7 +1049,8 @@ bool MHIContext::BeginStream(const QString &stream, MHStream *notify)
if (QUrl(stream).authority().isEmpty())
return false;

return m_parent->GetPlayer()->SetStream(stream);
emit m_parent->GetPlayer()->SetInteractiveStream(stream);
return !stream.isEmpty();
}

int chan = GetChannelIndex(stream);
Expand Down Expand Up @@ -1082,7 +1083,7 @@ void MHIContext::EndStream()
.arg((quintptr)m_notify,0,16) );

m_notify = nullptr;
(void)m_parent->GetPlayer()->SetStream(QString());
emit m_parent->GetPlayer()->SetInteractiveStream(QString());
}

// Callback from MythPlayer when a stream starts or stops
Expand Down Expand Up @@ -1156,14 +1157,17 @@ long MHIContext::GetStreamMaxPos()
// Set current stream position
long MHIContext::SetStreamPos(long pos)
{
return m_parent->GetPlayer() ? m_parent->GetPlayer()->SetStreamPos(pos) : -1;
if (m_parent->GetPlayer())
emit m_parent->GetPlayer()->SetInteractiveStreamPos(pos);
// Note: return value is never used
return 0;
}

// Play or pause a stream
void MHIContext::StreamPlay(bool play)
{
if (m_parent->GetPlayer())
m_parent->GetPlayer()->StreamPlay(play);
emit m_parent->GetPlayer()->PlayInteractiveStream(play);
}

// Create a new object to draw dynamic line art.
Expand Down
83 changes: 55 additions & 28 deletions mythtv/libs/libmythtv/mythplayercaptionsui.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// MythTV
#include "livetvchain.h"
#include "tv_play.h"
#include "interactivetv.h"
#include "mythplayercaptionsui.h"

Expand All @@ -9,6 +10,18 @@ MythPlayerCaptionsUI::MythPlayerCaptionsUI(MythMainWindow* MainWindow, TV* Tv, P
: MythPlayerAudioUI(MainWindow, Tv, Context, Flags)
{
m_itvEnabled = gCoreContext->GetBoolSetting("EnableMHEG", false);

// Connect outgoing
connect(this, &MythPlayerCaptionsUI::CaptionsStateChanged, m_tv, &TV::CaptionsStateChanged);

// Inbound connections
connect(m_tv, &TV::RestartITV, this, &MythPlayerCaptionsUI::ITVRestart);
connect(m_tv, &TV::HandleITVAction, this, &MythPlayerCaptionsUI::ITVHandleAction);

// Signalled connections (from MHIContext)
connect(this, &MythPlayerCaptionsUI::SetInteractiveStream, this, &MythPlayerCaptionsUI::SetStream);
connect(this, &MythPlayerCaptionsUI::SetInteractiveStreamPos, this, &MythPlayerCaptionsUI::SetStreamPos);
connect(this, &MythPlayerCaptionsUI::PlayInteractiveStream, this, &MythPlayerCaptionsUI::StreamPlay);
}

MythPlayerCaptionsUI::~MythPlayerCaptionsUI()
Expand Down Expand Up @@ -474,31 +487,44 @@ bool MythPlayerCaptionsUI::HandleTeletextAction(const QString& Action)
InteractiveTV* MythPlayerCaptionsUI::GetInteractiveTV()
{
#ifdef USING_MHEG
if (!m_interactiveTV && m_itvEnabled && !FlagIsSet(kNoITV))
bool update = false;
{
QMutexLocker lock1(&m_osdLock);
QMutexLocker lock2(&m_itvLock);
m_interactiveTV = new InteractiveTV(this);
if (!m_interactiveTV && m_itvEnabled && !FlagIsSet(kNoITV))
{
m_interactiveTV = new InteractiveTV(this);
m_captionsState.m_haveITV = true;
update = true;
}
}
if (update)
emit CaptionsStateChanged(m_captionsState);
#endif
return m_interactiveTV;
}

bool MythPlayerCaptionsUI::ITVHandleAction(const QString &Action)
/*! \brief Submit Action to the interactiveTV object
*
* This is a little contrived as this method is signalled from the parent object. Rather
* than return a value (which is not possible with a signal) we update the Handled
* parameter. This is fine as long as there is only one signal/slot connection but
* I'm guessing won't work as well if signalled across threads.
*/
void MythPlayerCaptionsUI::ITVHandleAction(const QString &Action, bool& Handled)
{
bool result = false;

#ifdef USING_MHEG
if (!GetInteractiveTV())
return result;
{
Handled = false;
return;
}

QMutexLocker locker(&m_itvLock);
result = m_interactiveTV->OfferKey(Action);
Handled = m_interactiveTV->OfferKey(Action);
#else
Q_UNUSED(Action);
#endif

return result;
}

/// \brief Restart the MHEG/MHP engine.
Expand All @@ -518,8 +544,13 @@ void MythPlayerCaptionsUI::ITVRestart(uint Chanid, uint Cardid, bool IsLiveTV)
#endif
}

/// \brief Selects the audio stream using the DVB component tag.
// Called from the interactiveTV (MHIContext) thread
/*! \brief Selects the audio stream using the DVB component tag.
*
* This is called from the InteractiveTV thread and really needs to be processed
* in the decoder thread. So for the time being do not convert this to a signal/slot
* (as there are no slots in the decoder classes yet) and just pass through with
* the protection of the decoder change lock.
*/
bool MythPlayerCaptionsUI::SetAudioByComponentTag(int Tag)
{
QMutexLocker locker(&m_decoderChangeLock);
Expand All @@ -528,8 +559,10 @@ bool MythPlayerCaptionsUI::SetAudioByComponentTag(int Tag)
return false;
}

/// \brief Selects the video stream using the DVB component tag.
// Called from the interactiveTV (MHIContext) thread
/*! \brief Selects the video stream using the DVB component tag.
*
* See SetAudioByComponentTag comments
*/
bool MythPlayerCaptionsUI::SetVideoByComponentTag(int Tag)
{
QMutexLocker locker(&m_decoderChangeLock);
Expand All @@ -540,23 +573,21 @@ bool MythPlayerCaptionsUI::SetVideoByComponentTag(int Tag)

double MythPlayerCaptionsUI::SafeFPS()
{
QMutexLocker locker(&m_decoderChangeLock);
if (!m_decoder)
return 25;
double fps = m_decoder->GetFPS();
return fps > 0 ? fps : 25.0;
}

// Called from the interactiveTV (MHIContext) thread
bool MythPlayerCaptionsUI::SetStream(const QString& Stream)
void MythPlayerCaptionsUI::SetStream(const QString& Stream)
{
// The stream name is empty if the stream is closing
LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SetStream '%1'").arg(Stream));

QMutexLocker locker(&m_streamLock);
m_newStream = Stream;
locker.unlock();
// Stream will be changed by JumpToStream called from EventLoop
// If successful will call m_interactiveTV->StreamStarted();
m_newStream = Stream;

if (Stream.isEmpty() && m_playerCtx->m_tvchain &&
m_playerCtx->m_buffer->GetType() == kMythBufferMHEG)
Expand All @@ -566,8 +597,6 @@ bool MythPlayerCaptionsUI::SetStream(const QString& Stream)
m_playerCtx->m_tvchain->JumpToNext(false, 0);
m_playerCtx->m_tvchain->JumpToNext(true, 0);
}

return !Stream.isEmpty();
}

// Called from the interactiveTV (MHIContext) thread
Expand All @@ -584,20 +613,18 @@ long MythPlayerCaptionsUI::GetStreamMaxPos()
return maxpos > pos ? maxpos : pos;
}

// Called from the interactiveTV (MHIContext) thread
long MythPlayerCaptionsUI::SetStreamPos(long Ms)
long MythPlayerCaptionsUI::SetStreamPos(long Position)
{
auto frameNum = static_cast<uint64_t>((Ms * SafeFPS()) / 1000);
auto frameNum = static_cast<uint64_t>((Position * SafeFPS()) / 1000);
LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SetStreamPos %1 mS = frame %2, now=%3")
.arg(Ms).arg(frameNum).arg(GetFramesPlayed()) );
.arg(Position).arg(frameNum).arg(GetFramesPlayed()) );
JumpToFrame(frameNum);
return Ms;
return Position;
}

// Called from the interactiveTV (MHIContext) thread
void MythPlayerCaptionsUI::StreamPlay(bool play)
void MythPlayerCaptionsUI::StreamPlay(bool Playing)
{
if (play)
if (Playing)
Play();
else
Pause();
Expand Down
26 changes: 17 additions & 9 deletions mythtv/libs/libmythtv/mythplayercaptionsui.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ class MTV_PUBLIC MythPlayerCaptionsUI : public MythPlayerAudioUI
Q_OBJECT

signals:
void CaptionsStateChanged(MythCaptionsState CaptionsState);
void ResizeForInteractiveTV(const QRect& Rect);
void SetInteractiveStream(const QString& Stream);
void SetInteractiveStreamPos(long Position);
void PlayInteractiveStream(bool Play);

public:
MythPlayerCaptionsUI(MythMainWindow* MainWindow, TV* Tv, PlayerContext* Context, PlayerFlags Flags);
Expand Down Expand Up @@ -45,24 +49,28 @@ class MTV_PUBLIC MythPlayerCaptionsUI : public MythPlayerAudioUI

bool SetAudioByComponentTag(int Tag);
bool SetVideoByComponentTag(int Tag);
bool SetStream(const QString& Stream);
long GetStreamPos();
long GetStreamMaxPos();
long SetStreamPos(long Ms);
void StreamPlay(bool play = true);
InteractiveTV* GetInteractiveTV() override;
bool ITVHandleAction(const QString& Action);

protected slots:
void ITVHandleAction(const QString& Action, bool& Handled);
void ITVRestart(uint Chanid, uint Cardid, bool IsLiveTV);

private slots:
void SetStream(const QString& Stream);
long SetStreamPos(long Position);
void StreamPlay(bool Playing = true);

protected:
double SafeFPS();

MythCaptionsState m_captionsState { };
InteractiveTV *m_interactiveTV { nullptr };
QMutex m_itvLock { };
bool m_itvEnabled { false };
bool m_itvVisible { false };
QMutex m_streamLock { };
QString m_newStream { };
QMutex m_itvLock { };
bool m_itvEnabled { false };
bool m_itvVisible { false };
QString m_newStream { };
};

#endif
5 changes: 5 additions & 0 deletions mythtv/libs/libmythtv/mythplayerstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,8 @@ MythVisualiserState::MythVisualiserState(bool Embedding, bool Visualising,
m_visualiserList(Visualisers)
{
}

MythCaptionsState::MythCaptionsState(bool ITV)
: m_haveITV(ITV)
{
}
9 changes: 9 additions & 0 deletions mythtv/libs/libmythtv/mythplayerstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,13 @@ class MTV_PUBLIC MythVisualiserState
QStringList m_visualiserList { };
};

class MTV_PUBLIC MythCaptionsState
{
public:
MythCaptionsState() = default;
MythCaptionsState(bool ITV);

bool m_haveITV { false };
};

#endif
11 changes: 3 additions & 8 deletions mythtv/libs/libmythtv/mythplayerui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,15 +168,10 @@ void MythPlayerUI::EventLoop()
}

// Change interactive stream if requested
if (!m_newStream.isEmpty())
{
QMutexLocker locker(&m_streamLock);
if (!m_newStream.isEmpty())
{
QString stream = m_newStream;
m_newStream.clear();
locker.unlock();
JumpToStream(stream);
}
JumpToStream(m_newStream);
m_newStream = QString();
}

// Disable fastforward if we are too close to the end of the buffer
Expand Down
23 changes: 9 additions & 14 deletions mythtv/libs/libmythtv/tv_play.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3475,8 +3475,7 @@ bool TV::ProcessKeypressOrGesture(QEvent* Event)
if (m_player && (m_player->GetCaptionMode() == kDisplayTeletextMenu))
{
QStringList tt_actions;
handled = TranslateKeyPressOrGesture(
"Teletext Menu", Event, tt_actions, isLiveTV);
handled = TranslateKeyPressOrGesture("Teletext Menu", Event, tt_actions, isLiveTV);

if (!handled && !tt_actions.isEmpty())
{
Expand All @@ -3492,19 +3491,20 @@ bool TV::ProcessKeypressOrGesture(QEvent* Event)
}

// Interactive television
if (m_player && m_player->GetInteractiveTV())
if (m_captionsState.m_haveITV)
{
if (!alreadyTranslatedPlayback)
{
handled = TranslateKeyPressOrGesture(
"TV Playback", Event, actions, isLiveTV);
handled = TranslateKeyPressOrGesture("TV Playback", Event, actions, isLiveTV);
alreadyTranslatedPlayback = true;
}

if (!handled && !actions.isEmpty())
{
for (int i = 0; i < actions.size(); i++)
{
if (m_player->ITVHandleAction(actions[i]))
emit HandleITVAction(actions[i], handled);
if (handled)
{
m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
return true;
Expand All @@ -3515,10 +3515,8 @@ bool TV::ProcessKeypressOrGesture(QEvent* Event)
m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);

if (!alreadyTranslatedPlayback)
{
handled = TranslateKeyPressOrGesture(
"TV Playback", Event, actions, isLiveTV);
}
handled = TranslateKeyPressOrGesture("TV Playback", Event, actions, isLiveTV);

if (handled || actions.isEmpty())
return handled;

Expand Down Expand Up @@ -9901,10 +9899,7 @@ void TV::ITVRestart(bool IsLive)
}
m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);

m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
if (m_player)
m_player->ITVRestart(static_cast<uint>(chanid), static_cast<uint>(sourceid), IsLive);
m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
emit RestartITV(static_cast<uint>(chanid), static_cast<uint>(sourceid), IsLive);
}

void TV::DoJumpFFWD()
Expand Down
7 changes: 6 additions & 1 deletion mythtv/libs/libmythtv/tvplaybackstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,16 @@ void TVPlaybackState::AudioPlayerStateChanged(const MythAudioPlayerState& AudioP
m_audioPlayerState = AudioPlayerState;
}

void TVPlaybackState::AudioStateChanged(const MythAudioState &AudioState)
void TVPlaybackState::AudioStateChanged(const MythAudioState& AudioState)
{
m_audioState = AudioState;
}

void TVPlaybackState::CaptionsStateChanged(const MythCaptionsState& CaptionsState)
{
m_captionsState = CaptionsState;
}

void TVPlaybackState::VideoBoundsStateChanged(const MythVideoBoundsState& VideoBoundsState)
{
m_videoBoundsState = VideoBoundsState;
Expand Down
Loading

0 comments on commit 1cafc2b

Please sign in to comment.