mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
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:
@@ -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();
|
||||
|
||||
@@ -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
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
Reference in New Issue
Block a user