Initial 'proper' support for Video Playback

The code to select media and then start/stop/pause
playback during a workout has been implemented.

This requires libvlc to be installed at compile time
and will also require Videolan (and desired codecs)
installed at runtime.

Since the main headaches are likely to be related to
deployment rather than coding this update has been
pushed for deployment support to be enhanced and
tested before closing the feature request on the
bug tracker.
This commit is contained in:
Mark Liversedge
2011-10-24 22:25:14 +01:00
parent 508b684746
commit b1d61ff5f8
5 changed files with 209 additions and 12 deletions

View File

@@ -144,6 +144,7 @@ class MainWindow : public QMainWindow
// realtime signals
void notifyTelemetryUpdate(const RealtimeData &rtData) { telemetryUpdate(rtData); }
void notifyErgFileSelected(ErgFile *x) { ergFileSelected(x); }
void notifyMediaSelected( QString x) { mediaSelected(x); }
void notifySetNow(long now) { setNow(now); }
void notifyNewLap() { emit newLap(); }
void notifyStart() { emit start(); }
@@ -180,6 +181,7 @@ class MainWindow : public QMainWindow
// realtime
void telemetryUpdate(RealtimeData rtData);
void ergFileSelected(ErgFile *);
void mediaSelected(QString);
void setNow(long);
void newLap();
void start();

View File

@@ -35,6 +35,10 @@
#include "ANTlocalController.h"
#include "NullController.h"
#ifdef GC_HAVE_VLC
// Media selection helper
#include "VideoWindow.h"
#endif
TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), home(home), main(parent)
{
@@ -70,6 +74,19 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h
serverTree->expandItem(allServers);
#endif
#ifdef GC_HAVE_VLC
mediaTree = new QTreeWidget;
mediaTree->setFrameStyle(QFrame::NoFrame);
mediaTree->setColumnCount(1);
mediaTree->setSelectionMode(QAbstractItemView::SingleSelection);
mediaTree->header()->hide();
mediaTree->setAlternatingRowColors (false);
mediaTree->setIndentation(5);
allMedia = new QTreeWidgetItem(mediaTree, HEAD_TYPE);
allMedia->setText(0, tr("Video / Media"));
mediaTree->expandItem(allMedia);
#endif
deviceTree = new QTreeWidget;
deviceTree->setFrameStyle(QFrame::NoFrame);
deviceTree->setSelectionMode(QAbstractItemView::MultiSelection);
@@ -130,11 +147,17 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h
trainSplitter->addWidget(deviceTree);
//trainSplitter->addWidget(serverTree);
trainSplitter->addWidget(workoutTree);
#ifdef GC_HAVE_VLC
trainSplitter->addWidget(mediaTree);
#endif
// handle config changes
//connect(serverTree,SIGNAL(itemSelectionChanged()), this, SLOT(serverTreeWidgetSelectionChanged()));
connect(deviceTree,SIGNAL(itemSelectionChanged()), this, SLOT(deviceTreeWidgetSelectionChanged()));
connect(workoutTree,SIGNAL(itemSelectionChanged()), this, SLOT(workoutTreeWidgetSelectionChanged()));
#ifdef GC_HAVE_VLC
connect(mediaTree,SIGNAL(itemSelectionChanged()), this, SLOT(mediaTreeWidgetSelectionChanged()));
#endif
connect(main, SIGNAL(configChanged()), this, SLOT(configChanged()));
// connect train tool buttons!
@@ -243,6 +266,20 @@ TrainTool::configChanged()
work->setText(0, name);
}
#ifdef GC_HAVE_VLC
// MEDIA
QList<QTreeWidgetItem *> media = allMedia->takeChildren();
for (int i=0; i<media.count(); i++) delete media.at(i);
MediaHelper mediaHelper;
foreach(QString video, mediaHelper.listMedia(QDir(workoutDir.toString()))) {
// add a media line for the video (it might be a song though...)
QTreeWidgetItem *media = new QTreeWidgetItem(allMedia, WORKOUT_TYPE);
media->setText(0, video);
}
#endif
// Athlete
FTP=285; // default to 285 if zones are not set
int range = main->zones()->whichRange(QDate::currentDate());
@@ -396,6 +433,31 @@ TrainTool::listWorkoutFiles(const QDir &dir) const
return dir.entryList(filters, QDir::Files, QDir::Name);
}
void
TrainTool::mediaTreeWidgetSelectionChanged()
{
assert(mediaTree->selectedItems().size() <= 1);
if (mediaTree->selectedItems().isEmpty())
media = NULL;
else {
QTreeWidgetItem *which = mediaTree->selectedItems().first();
if (which->type() != WORKOUT_TYPE)
media = NULL;
else
media = which;
}
// which one is selected?
if (currentMedia() == NULL || currentMedia()->type() != WORKOUT_TYPE) {
main->notifyMediaSelected("");
return;
}
QVariant workoutDir = appsettings->value(this, GC_WORKOUTDIR);
QString fileName = workoutDir.toString() + "/" + currentMedia()->text(0); // filename
main->notifyMediaSelected(fileName);
}
/*--------------------------------------------------------------------------------
* Was realtime window, now local and manages controller and chart updates etc
*------------------------------------------------------------------------------*/

View File

@@ -80,7 +80,9 @@ class TrainTool : public GcWindow
GoldenClient *streamController; // send out to
const QTreeWidgetItem *currentWorkout() { return workout; }
const QTreeWidgetItem *currentMedia() { return media; }
const QTreeWidgetItem *workoutItems() { return allWorkouts; }
const QTreeWidgetItem *mediaItems() { return allMedia; }
const QTreeWidgetItem *currentServer() { return server; }
const QTreeWidgetItem *serverItems() { return allServers; }
@@ -118,6 +120,7 @@ class TrainTool : public GcWindow
void serverTreeWidgetSelectionChanged();
void deviceTreeWidgetSelectionChanged();
void workoutTreeWidgetSelectionChanged();
void mediaTreeWidgetSelectionChanged();
void configChanged();
public slots:
@@ -146,16 +149,20 @@ class TrainTool : public GcWindow
const QDir home;
MainWindow *main;
QSplitter *trainSplitter;
QTreeWidget *workoutTree;
QTreeWidget *deviceTree;
QTreeWidgetItem *allWorkouts;
QTreeWidgetItem *workout;
QSplitter *trainSplitter;
QTreeWidget *serverTree;
QTreeWidget *mediaTree;
QTreeWidgetItem *allServers;
QTreeWidgetItem *allMedia;
QTreeWidgetItem *allDevices;
QTreeWidgetItem *server;
QTreeWidgetItem *allWorkouts;
QTreeWidgetItem *workout;
QTreeWidgetItem *media;
// those buttons
QFrame *buttonPanel;

View File

@@ -47,19 +47,16 @@ GcWindow(parent), home(home), main(parent)
/* Create a new item */
// XXX need to add controls - not everyone is going to want to play a video from
// my desktop!!!
m = libvlc_media_new_path(inst, "/home/markl/Videos/fightclub.mp4");
m = NULL;
//vlc_exceptions(&exceptions);
/* Create a media player playing environement */
mp = libvlc_media_player_new (inst);
//vlc_exceptions(&exceptions);
/* set the media to playback */
libvlc_media_player_set_media (mp, m);
//vlc_exceptions(&exceptions);
/* No need to keep the media now */
libvlc_media_release (m);
/* This is a non working code that show how to hooks into a window,
* if we have a window around */
@@ -78,8 +75,12 @@ GcWindow(parent), home(home), main(parent)
libvlc_media_player_set_nsobject (mp, view);
#endif
/* play the media_player */
libvlc_media_player_play (mp);
connect(main, SIGNAL(stop()), this, SLOT(stopPlayback()));
connect(main, SIGNAL(start()), this, SLOT(startPlayback()));
connect(main, SIGNAL(pause()), this, SLOT(pausePlayback()));
connect(main, SIGNAL(unpause()), this, SLOT(resumePlayback()));
connect(main, SIGNAL(mediaSelected(QString)), this, SLOT(mediaSelected(QString)));
}
VideoWindow::~VideoWindow()
@@ -89,8 +90,12 @@ VideoWindow::~VideoWindow()
x11Container->discardClient();
#endif
// stop playback & wipe player
libvlc_media_player_stop (mp);
stopPlayback();
/* No need to keep the media now */
if (m) libvlc_media_release (m);
/* nor the player */
libvlc_media_player_release (mp);
// unload vlc
@@ -101,3 +106,102 @@ void VideoWindow::resizeEvent(QResizeEvent * )
{
// do nothing .. for now
}
void VideoWindow::startPlayback()
{
if (!m) return; // ignore if no media selected
// stop playback & wipe player
libvlc_media_player_stop (mp);
/* set the media to playback */
libvlc_media_player_set_media (mp, m);
/* play the media_player */
libvlc_media_player_play (mp);
}
void VideoWindow::stopPlayback()
{
if (!m) return; // ignore if no media selected
// stop playback & wipe player
libvlc_media_player_stop (mp);
}
void VideoWindow::pausePlayback()
{
if (!m) return; // ignore if no media selected
// stop playback & wipe player
libvlc_media_player_pause (mp);
}
void VideoWindow::resumePlayback()
{
if (!m) return; // ignore if no media selected
// stop playback & wipe player
libvlc_media_player_pause (mp);
}
void VideoWindow::mediaSelected(QString filename)
{
// stop any current playback
stopPlayback();
// release whatever is already loaded
if (m) libvlc_media_release(m);
m = NULL;
if (filename != "" && QFile(filename).exists()) {
/* open media */
m = libvlc_media_new_path(inst, filename.toLatin1());
/* set the media to playback */
if (m) libvlc_media_player_set_media (mp, m);
}
}
MediaHelper::MediaHelper()
{
// config paramaters to libvlc
const char * const vlc_args[] = {
"-I", "dummy", /* Don't use any interface */
"--ignore-config", /* Don't use VLC's config */
"--extraintf=logger", //log anything
"--verbose=-1" // -1 = no output at all
};
/* Load the VLC engine */
inst = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);
}
MediaHelper::~MediaHelper()
{
// unload vlc
libvlc_release (inst);
}
QStringList
MediaHelper::listMedia(QDir dir)
{
QStringList returning;
// whizz through every file in the directory
// and try and open it, if we succeed then huzzah
// otherwise ignore it
foreach(QString name, dir.entryList()) {
libvlc_media_t *m = libvlc_media_new_path(inst, QString(dir.absolutePath() + "/" + name).toLatin1());
if (m) {
libvlc_media_parse(m);
if (libvlc_media_get_duration(m) > 0) returning << name;
libvlc_media_release(m);
}
}
return returning;
}

View File

@@ -43,6 +43,22 @@ extern "C" {
#include <QX11EmbedContainer>
#endif
class MediaHelper
{
public:
MediaHelper();
~MediaHelper();
// get a list of supported media
// found in the supplied directory
QStringList listMedia(QDir directory);
private:
libvlc_instance_t * inst;
};
class VideoWindow : public GcWindow
{
Q_OBJECT
@@ -58,6 +74,12 @@ class VideoWindow : public GcWindow
public slots:
void startPlayback();
void stopPlayback();
void pausePlayback();
void resumePlayback();
void mediaSelected(QString filename);
protected:
void resizeEvent(QResizeEvent *);