Permalink
Browse files

libmythtv: Enable visualisations.

- libfftw3 support is required at compile time and MUST be forced with
--enable-libfftw3 (existing libfftw3 support in configure appears to be
a hangover from previous code, it isn't actually used for anything else)

- much of the code is taken from mythmusic.

- visualisations will only work with 2 track audio streams.

- the visualisation is rendered over any video (and Pip) but beneath the
OSD.

- 2 visualisations are currently available. The 'default' is a simple
implementation of Spectrum which uses libmythui rendering routines and
is available when using the VDPAU, OpenGL and Direct3D9 renderers(i.e.
those with accelerated, overlaid OSDs). Circles is available when using
OpenGL 2.0 (not currently enabled) and, while a little easier on the
eye, is more a proof of concept to ensure correct inheritance,
dependency checking etc is working. It doesn't actually rely on the
features available with the OpenGL 2.0 renderer - though drawing of
circles is accelerated with that renderer.

- there is no technical reason why either visualisation will not work
with software rendered OSDs (e.g. XVideo). The limitation is purely a
performance consideration.

- adds a new keybinding "TOGGLEVISUALISATION" which is unbound by
default, otherwise the visualisation can be toggled via the new entry in
the OSD menu or the http interface.

- it should be easy enough to port any other mythmusic visualisations,
though libvisual integration is more difficult (and may not be
possible). If additional visualisations are made available, I'll add a
mechanism to chose which to use.
  • Loading branch information...
1 parent 057f1a9 commit 8fe7f45f89767c0b7e40671eaa8a166adaae57d4 Mark Kendall committed Mar 22, 2011
@@ -24,7 +24,7 @@ DEPENDPATH += ../libmyth ../libmyth/audio
DEPENDPATH += ../libmythbase ../libmythhdhomerun
DEPENDPATH += ../libmythdvdnav/
DEPENDPATH += ../libmythbluray/
-DEPENDPATH += ./dvbdev ./mpeg ./iptv ./channelscan
+DEPENDPATH += ./dvbdev ./mpeg ./iptv ./channelscan ./visualisations
DEPENDPATH += ../libmythlivemedia/BasicUsageEnvironment/include
DEPENDPATH += ../libmythlivemedia/BasicUsageEnvironment
DEPENDPATH += ../libmythlivemedia/groupsock/include
@@ -320,12 +320,22 @@ using_frontend {
HEADERS += videodisplayprofile.h mythcodecid.h
HEADERS += videoouttypes.h util-osd.h
HEADERS += videooutwindow.h videocolourspace.h
+ HEADERS += videovisual.h videovisualdefs.h
SOURCES += videooutbase.cpp videoout_null.cpp
SOURCES += videobuffers.cpp vsync.cpp
SOURCES += jitterometer.cpp yuv2rgb.cpp
SOURCES += videodisplayprofile.cpp mythcodecid.cpp
SOURCES += videooutwindow.cpp util-osd.cpp
SOURCES += videocolourspace.cpp
+ SOURCES += videovisual.cpp
+
+ using_libfftw3 {
+ DEFINES += FFTW3_SUPPORT
+ HEADERS += videovisualspectrum.h
+ SOURCES += videovisualspectrum.cpp
+ using_opengl: HEADERS += videovisualcircles.h
+ using_opengl: SOURCES += videovisualcircles.cpp
+ }
using_quartz_video: DEFINES += USING_QUARTZ_VIDEO
using_quartz_video: HEADERS += videoout_quartz.h
@@ -4637,6 +4637,20 @@ void MythPlayer::ToggleStudioLevels(void)
SetOSDMessage(msg, kOSDTimeout_Med);
}
+bool MythPlayer::CanVisualise(void)
+{
+ if (videoOutput)
+ return videoOutput->CanVisualise(&audio, NULL);
+ return false;
+}
+
+bool MythPlayer::ToggleVisualisation(void)
+{
+ if (videoOutput)
+ return videoOutput->ToggleVisualisation(&audio);
+ return false;
+}
+
void MythPlayer::SetOSDMessage(const QString &msg, OSDTimeout timeout)
{
QMutexLocker locker(&osdLock);
@@ -291,6 +291,10 @@ class MTV_PUBLIC MythPlayer
// Public picture controls
void ToggleStudioLevels(void);
+ // Visualisations
+ bool CanVisualise(void);
+ bool ToggleVisualisation(void);
+
void SaveTotalDuration(void);
void ResetTotalDuration(void);
@@ -60,4 +60,7 @@
#define ACTION_TOGGLEBACKGROUND "TOGGLEBACKGROUND"
#define ACTION_REVEAL "REVEAL"
+/* Visualisations */
+#define ACTION_TOGGLEVISUALISATION "TOGGLEVISUALISATION"
+
#endif // TV_ACTIONS_H
@@ -752,6 +752,10 @@ void TV::InitKeys(void)
REG_KEY("Teletext Menu", ACTION_REVEAL, QT_TRANSLATE_NOOP("MythControls",
"Reveal hidden Text"), "F8");
+ /* Visualisations */
+ REG_KEY("TV Playback", ACTION_TOGGLEVISUALISATION,
+ QT_TRANSLATE_NOOP("MythControls", "Toggle audio visualisation"), "");
+
/*
keys already used:
@@ -4030,6 +4034,8 @@ bool TV::ToggleHandleAction(PlayerContext *ctx,
ToggleAdjustFill(ctx);
else if (has_action("TOGGLEAUDIOSYNC", actions))
ChangeAudioSync(ctx, 0); // just display
+ else if (has_action(ACTION_TOGGLEVISUALISATION, actions))
+ ToggleVisualisation(ctx);
else if (has_action("TOGGLEPICCONTROLS", actions))
DoTogglePictureAttribute(ctx, kAdjustingPicture_Playback);
else if (has_action(ACTION_TOGGLESTUDIOLEVELS, actions))
@@ -4075,6 +4081,18 @@ bool TV::ToggleHandleAction(PlayerContext *ctx,
return handled;
}
+void TV::ToggleVisualisation(const PlayerContext *ctx)
+{
+ ctx->LockDeletePlayer(__FILE__, __LINE__);
+ if (ctx->player && ctx->player->CanVisualise())
+ {
+ bool on = ctx->player->ToggleVisualisation();
+ SetOSDMessage(ctx, on ? tr("Visualisation On") :
+ tr("Visualisation Off"));
+ }
+ ctx->UnlockDeletePlayer(__FILE__, __LINE__);
+}
+
bool TV::PxPHandleAction(PlayerContext *ctx, const QStringList &actions)
{
if (!IsPIPSupported(ctx) && !IsPBPSupported(ctx))
@@ -9411,6 +9429,8 @@ void TV::OSDDialogEvent(int result, QString text, QString action)
}
else if (action.left(15) == "TOGGLEAUDIOSYNC")
ChangeAudioSync(actx, 0);
+ else if (action == ACTION_TOGGLEVISUALISATION)
+ ToggleVisualisation(actx);
else if (action.left(11) == ACTION_TOGGLESLEEP)
{
ToggleSleepTimer(actx, action.left(13));
@@ -9674,9 +9694,11 @@ void TV::FillOSDMenuAudio(const PlayerContext *ctx, OSD *osd,
QStringList tracks;
uint curtrack = ~0;
bool avsync = true;
+ bool visual = false;
ctx->LockDeletePlayer(__FILE__, __LINE__);
if (ctx->player)
{
+ visual = ctx->player->CanVisualise();
tracks = ctx->player->GetTracks(kTrackTypeAudio);
if (!tracks.empty())
curtrack = (uint) ctx->player->GetTrack(kTrackTypeAudio);
@@ -9704,6 +9726,11 @@ void TV::FillOSDMenuAudio(const PlayerContext *ctx, OSD *osd,
"DIALOG_MENU_AUDIOTRACKS_0", true,
selected == "AUDIOTRACKS");
}
+ if (visual)
+ {
+ osd->DialogAddButton(tr("Toggle Visualisation"),
+ ACTION_TOGGLEVISUALISATION);
+ }
if (avsync)
osd->DialogAddButton(tr("Adjust Audio Sync"), "TOGGLEAUDIOSYNC");
osd->DialogAddButton(tr("Toggle Audio Upmixer"), ACTION_TOGGLEUPMIX);
@@ -618,6 +618,9 @@ class MTV_PUBLIC TV : public QObject
void DVDJumpForward(PlayerContext*);
bool DiscMenuHandleAction(PlayerContext*, const QStringList &actions);
+ // Visualisations
+ void ToggleVisualisation(const PlayerContext*);
+
// Program jumping stuff
void SetLastProgram(const ProgramInfo *rcinfo);
ProgramInfo *GetLastProgram(void) const;
@@ -369,6 +369,10 @@ void VideoOutputD3D::PrepareFrame(VideoFrame *buffer, FrameScanType t,
(*it)->Draw();
}
}
+
+ if (m_visual)
+ m_visual->Draw(GetTotalOSDBounds(), m_osd_painter, NULL);
+
if (osd && m_osd_painter && !window.IsEmbedding())
osd->DrawDirect(m_osd_painter, GetTotalOSDBounds().size(),
true);
@@ -58,6 +58,11 @@ class VideoOutputD3D : public VideoOutput
virtual bool ApproveDeintFilter(const QString& filtername) const;
void* GetDXVA2Decoder(void);
+ virtual bool CanVisualise(AudioPlayer *audio, MythRender *render)
+ { return VideoOutput::CanVisualise(audio, m_render); }
+ virtual bool SetupVisualisation(AudioPlayer *audio, MythRender *render)
+ { return VideoOutput::SetupVisualisation(audio, m_render); }
+
private:
void TearDown(void);
bool SetupContext(void);
@@ -443,6 +443,9 @@ void VideoOutputOpenGL::PrepareFrame(VideoFrame *buffer, FrameScanType t,
}
}
+ if (m_visual)
+ m_visual->Draw(GetTotalOSDBounds(), gl_painter, NULL);
+
if (osd && gl_painter && !window.IsEmbedding())
osd->DrawDirect(gl_painter, GetTotalOSDBounds().size(), true);
@@ -51,6 +51,11 @@ class VideoOutputOpenGL : public VideoOutput
virtual bool ApproveDeintFilter(const QString& filtername) const;
virtual MythPainter *GetOSDPainter(void) { return (MythPainter*)gl_painter; }
+ virtual bool CanVisualise(AudioPlayer *audio, MythRender *render)
+ { return VideoOutput::CanVisualise(audio, gl_context); }
+ virtual bool SetupVisualisation(AudioPlayer *audio, MythRender *render)
+ { return VideoOutput::SetupVisualisation(audio, gl_context); }
+
protected:
virtual bool CreateBuffers(void);
bool CreatePauseFrame(void);
@@ -499,6 +499,9 @@ void VideoOutputVDPAU::PrepareFrame(VideoFrame *frame, FrameScanType scan,
m_pip_ready ? m_pip_layer : 0, 0))
VERBOSE(VB_PLAYBACK, LOC_ERR + QString("Prepare frame failed."));
+ if (m_visual)
+ m_visual->Draw(GetTotalOSDBounds(), m_osd_painter, NULL);
+
if (osd && m_osd_painter && !window.IsEmbedding())
osd->DrawDirect(m_osd_painter, GetTotalOSDBounds().size(), true);
@@ -64,6 +64,11 @@ class VideoOutputVDPAU : public VideoOutput
void SetNextFrameDisplayTimeOffset(int delayus) { m_frame_delay = delayus; }
virtual MythPainter* GetOSDPainter(void) { return (MythPainter*)m_osd_painter; }
+ virtual bool CanVisualise(AudioPlayer *audio, MythRender *render)
+ { return VideoOutput::CanVisualise(audio, m_render); }
+ virtual bool SetupVisualisation(AudioPlayer *audio, MythRender *render)
+ { return VideoOutput::SetupVisualisation(audio, m_render); }
+
private:
virtual bool hasFullScreenOSD(void) const { return true; }
void TearDown(void);
@@ -342,8 +342,10 @@ VideoOutput::VideoOutput() :
monitor_sz(640,480), monitor_dim(400,300),
// OSD
- osd_painter(NULL), osd_image(NULL)
+ osd_painter(NULL), osd_image(NULL),
+ // Visualisation
+ m_visual(NULL)
{
memset(&pip_tmp_image, 0, sizeof(pip_tmp_image));
db_display_dim = QSize(gCoreContext->GetNumSetting("DisplaySizeWidth", 0),
@@ -1292,6 +1294,13 @@ bool VideoOutput::DisplayOSD(VideoFrame *frame, OSD *osd)
return false;
}
+ if (m_visual)
+ {
+ VERBOSE(VB_IMPORTANT, LOC + "Visualiser not supported here");
+ // Clear the audio buffer
+ m_visual->Draw(QRect(), NULL, NULL);
+ }
+
QRegion dirty = QRegion();
QRegion visible = osd->Draw(osd_painter, osd_image, osd_size, dirty,
frame->codec == FMT_YV12 ? ALIGN_X_MMX : 0,
@@ -1340,6 +1349,34 @@ bool VideoOutput::DisplayOSD(VideoFrame *frame, OSD *osd)
return show;
}
+bool VideoOutput::ToggleVisualisation(AudioPlayer *audio)
+{
+ if (m_visual)
+ {
+ DestroyVisualisation();
+ return false;
+ }
+ return SetupVisualisation(audio, NULL);
+}
+
+bool VideoOutput::CanVisualise(AudioPlayer *audio, MythRender *render)
+{
+ return VideoVisual::CanVisualise(audio, render);
+}
+
+bool VideoOutput::SetupVisualisation(AudioPlayer *audio, MythRender *render)
+{
+ DestroyVisualisation();
+ m_visual = VideoVisual::Create(audio, render);
+ return m_visual;
+}
+
+void VideoOutput::DestroyVisualisation(void)
+{
+ delete m_visual;
+ m_visual = NULL;
+}
+
/**
* \fn VideoOutput::CopyFrame(VideoFrame*, const VideoFrame*)
* \brief Copies frame data from one VideoFrame to another.
@@ -23,6 +23,7 @@ extern "C" {
#include "DisplayRes.h"
#include "videodisplayprofile.h"
#include "videocolourspace.h"
+#include "visualisations/videovisual.h"
using namespace std;
@@ -34,6 +35,8 @@ class OSD;
class FilterChain;
class FilterManager;
class OpenGLContextGLX;
+class AudioPlayer;
+class MythRender;
typedef QMap<MythPlayer*,PIPLocation> PIPMap;
@@ -243,6 +246,13 @@ class VideoOutput
QRect GetImageRect(const QRect &rect, QRect *display = NULL);
QRect GetSafeRect(void);
+ // Visualisations
+ bool ToggleVisualisation(AudioPlayer *audio);
+ virtual bool CanVisualise(AudioPlayer *audio, MythRender *render);
+ virtual bool SetupVisualisation(AudioPlayer *audio, MythRender *render);
+ void DestroyVisualisation(void);
+
+
protected:
void InitBuffers(int numdecode, bool extra_for_pause, int need_free,
int needprebuffer_normal, int needprebuffer_small,
@@ -323,6 +333,9 @@ class VideoOutput
// OSD painter and surface
MythYUVAPainter *osd_painter;
MythImage *osd_image;
+
+ // Visualisation
+ VideoVisual *m_visual;
};
#endif

0 comments on commit 8fe7f45

Please sign in to comment.