summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/player/CMakeLists.txt5
-rw-r--r--examples/player/main.cpp195
-rw-r--r--examples/player/mediaapp.cpp183
-rw-r--r--examples/player/mediaapp.h68
-rw-r--r--examples/player/player.cpp195
-rw-r--r--examples/player/player.h76
-rw-r--r--examples/player/player.pro3
7 files changed, 539 insertions, 186 deletions
diff --git a/examples/player/CMakeLists.txt b/examples/player/CMakeLists.txt
index 6fb9305..a3e50d6 100644
--- a/examples/player/CMakeLists.txt
+++ b/examples/player/CMakeLists.txt
@@ -5,5 +5,6 @@ find_package(QtGStreamer REQUIRED)
include_directories(${QTGSTREAMER_INCLUDES})
add_definitions(${QTGSTREAMER_DEFINITIONS})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${QTGSTREAMER_FLAGS}")
-add_executable(player main.cpp)
-target_link_libraries(player ${QTGSTREAMER_LIBRARIES})
+set(player_SOURCES main.cpp player.cpp mediaapp.cpp)
+automoc4_add_executable(player ${player_SOURCES})
+target_link_libraries(player ${QTGSTREAMER_UI_LIBRARIES})
diff --git a/examples/player/main.cpp b/examples/player/main.cpp
index 492691f..515e5a7 100644
--- a/examples/player/main.cpp
+++ b/examples/player/main.cpp
@@ -1,5 +1,6 @@
/*
Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com>
+ Copyright (C) 2010 Marco Ballesio <gibrovacco@gmail.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@@ -14,192 +15,20 @@
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <iostream>
-#include <signal.h>
-#include <QtCore/QCoreApplication>
-#include <QtCore/QFile>
-#include <QtCore/QTime>
-#include <QGlib/Connect>
-#include <QGst/Init>
-#include <QGst/Pipeline>
-#include <QGst/ElementFactory>
-#include <QGst/GhostPad>
-#include <QGst/Structure>
-#include <QGst/Bus>
-#include <QGst/Message>
-#include <QGst/Query>
-#include <QGst/Clock>
-#include <QGst/Event>
+#include <QtGui/QApplication>
+#include <QGst/Global>
+#include "mediaapp.h"
-/* This is a simple example of a command-line audio player. It accepts the filename of
- * an audio file as the first command line argument and then constructs a pipeline
- * that uses decodebin2 to decode the audio stream and autoaudiosink to output it to
- * the sound card. In the future this example will be expanded to handle video as well
- * and perhaps it will gain a simple GUI too. */
-
-class Player : public QObject
-{
-public:
- Player(const QString & fileName);
- ~Player();
-
-private:
- void onBusSyncMessage(const QGst::MessagePtr & message);
- void onNewDecodedPad(QGst::PadPtr newPad);
- static QGst::BinPtr createAudioSinkBin();
-
- /* This smart pointer is needed to keep a reference to the underlying GstPipeline object.
- * Even if we are not going to use it, we must keep a reference to the pipeline,
- * so that it remains in memory, together with all its child elements. If this
- * reference is gone, the pipeline and all the elements will be destroyed.
- * Note that we don't need to keep references to the individual elements, because
- * when they are added in the pipeline, the pipeline keeps a reference on them. */
- QGst::PipelinePtr m_pipeline;
-};
-
-Player::Player(const QString & fileName)
-{
- m_pipeline = QGst::Pipeline::create();
-
- QGst::ElementPtr filesrc = QGst::ElementFactory::make("filesrc");
- QGst::ElementPtr decodebin = QGst::ElementFactory::make("decodebin2");
-
- filesrc->setProperty("location", fileName);
- QGlib::connect(decodebin, "new-decoded-pad", this, &Player::onNewDecodedPad);
-
- m_pipeline->add(filesrc);
- m_pipeline->add(decodebin);
- filesrc->link(decodebin);
-
- QGst::BusPtr bus = m_pipeline->bus();
- bus->enableSyncMessageEmission();
- QGlib::connect(bus, "sync-message", this, &Player::onBusSyncMessage);
-
- m_pipeline->setState(QGst::StatePlaying);
-}
-
-Player::~Player()
-{
- m_pipeline->setState(QGst::StateNull);
-
- /* When m_pipeline is destructed, the last reference to our pipeline will be gone,
- * and with it all the elements, buses, etc will be destroyed too. As a result,
- * there is no need to cleanup here. */
-}
-
-void Player::onBusSyncMessage(const QGst::MessagePtr & message)
-{
- /* WARNING this slot gets called in a different thread */
- switch(message->type()) {
- case QGst::MessageEos: //End of stream. We reached the end of the file.
- case QGst::MessageError: //Some error occurred.
- /* QCoreApplication::quit is safe to be called in another thread.
- * It will schedule the main event loop to exit, when execution
- * in the main thread has reached the event loop. */
- QCoreApplication::quit();
- break;
- case QGst::MessageAsyncDone:
- {
- //File prerolled, queries the pipeline to get the file duration
- QGst::DurationQueryPtr query = QGst::DurationQuery::create(QGst::FormatTime);
- //This will create a temporary (cast to query).
- m_pipeline->query(query);
-
- //qDebug() << QGst::Clock::timeFromClockTime(query->duration());
-
- /*Set the pipeline to seek to 6 seconds in the stream and play until it reaches 15 secs
- * Notice that using element->seek() is probably better in most cases
- *
- QGst::SeekEventPtr evt = QGst::SeekEvent::create(1.0, QGst::FormatTime,
- QGst::SeekFlagNone,
- QGst::SeekTypeSet,
- QGst::Clock::clockTimeFromTime(QTime(0,0,6)),
- QGst::SeekTypeSet,
- QGst::Clock::clockTimeFromTime(QTime(0,0,15))
- );
- m_pipeline->sendEvent(evt);*/
-
- /* this is the simple seek version
- m_pipeline->seek(QGst::FormatTime, QGst::SeekFlagNone,
- QGst::Clock::clockTimeFromTime(QTime(0,0,15)) );*/
- }
- break;
- default:
- break;
- }
-}
-
-/* This method will be called every time a new "src" pad is available on the decodebin2 element.
- * Here we have to check what kind of data this pad transfers (usually it is either "audio/x-raw-*"
- * or "video/x-raw-*") and connect an appropriate sink that can handle this type of data. */
-void Player::onNewDecodedPad(QGst::PadPtr newPad)
-{
-
- QGst::CapsPtr caps = newPad->caps();
- QGst::StructurePtr structure = caps->internalStructure(0);
-
- /* The caps' first structure's name tells us what kind of data the pad transfers.
- * Here we want to handle either audio/x-raw-int or audio/x-raw-float. Both types
- * can be handled by the audioconvert element that is contained in the audioSinkBin,
- * so there is no need to handle them separately */
- if (structure->name().contains("audio/x-raw")) {
- QGst::BinPtr audioSinkBin = createAudioSinkBin();
- m_pipeline->add(audioSinkBin);
-
- /* The newly created bin must go to the playing state in order to function.
- * Here we tell it to synchronise its state with its parent, the pipeline,
- * which is scheduled to go to the playing state. */
- audioSinkBin->syncStateWithParent();
-
- newPad->link(audioSinkBin->getStaticPad("sink"));
- }
-}
-
-QGst::BinPtr Player::createAudioSinkBin()
-{
- QGst::BinPtr bin = QGst::Bin::create();
-
- QGst::ElementPtr audioconvert = QGst::ElementFactory::make("audioconvert");
- QGst::ElementPtr audiosink = QGst::ElementFactory::make("autoaudiosink");
-
- bin->add(audioconvert);
- bin->add(audiosink);
- audioconvert->link(audiosink);
-
- /* Add a sink pad to the bin that proxies the sink pad of the audioconvert element */
- bin->addPad(QGst::GhostPad::create(audioconvert->getStaticPad("sink"), "sink"));
-
- return bin;
-}
-
-static void sighandler(int code)
-{
- Q_UNUSED(code);
- QCoreApplication::quit();
-}
-
-int main(int argc, char **argv)
-{
- QCoreApplication app(argc, argv);
+int main(int argc, char *argv[]) {
+ QApplication app(argc, argv);
QGst::init(&argc, &argv);
+ MediaApp media;
+ media.show();
- QString fileName;
- if (argc > 1) {
- fileName = QFile::decodeName(argv[1]);
- }
-
- if (!QFile::exists(fileName)) {
- std::cerr << "Usage: " << argv[0] << " fileToPlay" << std::endl;
- return 1;
+ if(argc == 2){
+ media.openFile(argv[1]);
}
- Player *p = new Player(fileName);
-
- signal(SIGINT, sighandler);
- int result = app.exec();
-
- delete p; // we must delete all gstreamer objects before calling QGst::cleanup()
- QGst::cleanup();
-
- return result;
+ return app.exec();
}
+
diff --git a/examples/player/mediaapp.cpp b/examples/player/mediaapp.cpp
new file mode 100644
index 0000000..5809340
--- /dev/null
+++ b/examples/player/mediaapp.cpp
@@ -0,0 +1,183 @@
+/*
+ Copyright (C) 2010 Marco Ballesio <gibrovacco@gmail.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QtGui>
+#include <QtCore/QTime>
+#include "mediaapp.h"
+
+MediaApp::MediaApp(QWidget *parent) : QWidget(parent) {
+ player = new Player(this);
+ player->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+ player->setAutoFillBackground(false);
+ connect(player, SIGNAL(positionChanged(QTime)), this, SLOT(posChanged()));
+ connect(player, SIGNAL(stateChanged(QGst::State)), this, SLOT(stateChanged()));
+
+ baseDir = ".";
+ fsTimer.setSingleShot(true);
+ connect(&fsTimer, SIGNAL(timeout()), this, SLOT(hideControls()));
+
+ QVBoxLayout *appLayout = new QVBoxLayout;
+ appLayout->setContentsMargins(0,0,0,0);
+ createUI(appLayout);
+ setLayout(appLayout);
+
+ stateChanged();
+ setWindowTitle(tr("QtGstreamer example player"));
+ resize(400, 400);
+}
+
+MediaApp::~MediaApp() {
+ delete player;
+}
+
+void MediaApp::open() {
+ player->stop();
+ QString fileName = QFileDialog::getOpenFileName(
+ this, tr("Open a Movie"), baseDir);
+
+ if (!fileName.isEmpty())
+ openFile(fileName);
+}
+
+void MediaApp::mouseMoveEvent(QMouseEvent* event) {
+ if(isFullScreen()){
+ if(!openButton->isVisible())
+ showControls(true);
+
+ fsTimer.start(3000);
+ }
+}
+
+
+void MediaApp::showControls(bool show) {
+ openButton->setVisible(show);
+ fullScreenButton->setVisible(show);
+ playButton->setVisible(show);
+ pauseButton->setVisible(show);
+ stopButton->setVisible(show);
+ posSlider->setVisible(show);
+ posLabel->setVisible(show);
+}
+
+void MediaApp::toggleFullScreen() {
+ if(isFullScreen()){
+ setMouseTracking(false);
+ fsTimer.stop();
+ showNormal();
+ } else {
+ showFullScreen();
+ setMouseTracking(true);
+ showControls(false);
+ }
+}
+
+void MediaApp::openFile(const QString &fileName) {
+ baseDir = QFileInfo(fileName).path();
+
+ player->setUri(fileName);
+ player->play();
+}
+
+void MediaApp::posChanged() {
+ QTime length = player->length();
+ QTime curpos = player->position();
+ posLabel->setText(curpos.toString() + "/" + length.toString());
+
+ if(length != QTime(0,0,0,0)){
+ posSlider->setValue(curpos.msecsTo(QTime(0,0,0,0)) * 100 /
+ length.msecsTo(QTime(0,0,0,0)));
+ }
+
+ if(curpos != QTime(0,0,0,0)){
+ posLabel->setEnabled(true);
+ posSlider->setEnabled(true);
+ }
+}
+
+void MediaApp::stateChanged() {
+ playButton->setEnabled(player->state() != QGst::StatePlaying);
+ pauseButton->setEnabled(player->state() == QGst::StatePlaying);
+ stopButton->setEnabled(player->state() != QGst::StateReady);
+ if(player->state() == QGst::StateReady){
+ posLabel->setText("00:00:00/00:00:00");
+ posSlider->setValue(0);
+ }
+}
+
+void MediaApp::setPos(int value) {
+ quint64 length = -player->length().msecsTo(QTime(0,0,0,0));
+ if(length != 0 && value > 0){
+ QTime pos(0,0,0,0);
+ pos = pos.addMSecs( length*value/100);
+ player->setPosition(pos);
+ }
+}
+
+inline QToolButton* MediaApp::initButton(
+ QStyle::StandardPixmap icon,
+ const char *tip,
+ QObject *dstobj, const char* slot_method,
+ QLayout *layout)
+{
+ QToolButton *button = new QToolButton;
+ button->setIcon(style()->standardIcon(icon));
+ button->setIconSize(QSize(36,36));
+ button->setToolTip(tr(tip));
+ connect(button, SIGNAL(clicked()), dstobj, slot_method);
+ layout->addWidget(button);
+
+ return button;
+}
+
+void MediaApp::createUI(QBoxLayout *appLayout) {
+ appLayout->addWidget(player);
+
+ posLabel = new QLabel();
+
+ posSlider = new QSlider(Qt::Horizontal);
+ posSlider->setTickPosition(QSlider::TicksBelow);
+ posSlider->setTickInterval(10);
+ posSlider->setMaximum(100);
+
+ connect(posSlider, SIGNAL(sliderMoved(int)), this, SLOT(setPos(int)));
+
+ QGridLayout *posLayout = new QGridLayout;
+ posLayout->addWidget(posLabel, 1, 0);
+ posLayout->addWidget(posSlider, 1, 1, 1, 2);
+ appLayout->addLayout(posLayout);
+
+ QHBoxLayout *btnLayout = new QHBoxLayout;
+ btnLayout->addStretch();
+
+ openButton = initButton(
+ QStyle::SP_DialogOpenButton, "Open File", this, SLOT(open()), btnLayout);
+
+ playButton = initButton(
+ QStyle::SP_MediaPlay, "Play", player, SLOT(play()), btnLayout);
+
+ pauseButton = initButton(
+ QStyle::SP_MediaPause, "Pause", player, SLOT(pause()), btnLayout);
+
+ stopButton = initButton(
+ QStyle::SP_MediaStop, "Stop", player, SLOT(stop()), btnLayout);
+
+ fullScreenButton = initButton(
+ QStyle::SP_TitleBarMaxButton, "Fullscreen", this, SLOT(toggleFullScreen()), btnLayout);
+
+ btnLayout->addStretch();
+ appLayout->addLayout(btnLayout);
+}
diff --git a/examples/player/mediaapp.h b/examples/player/mediaapp.h
new file mode 100644
index 0000000..1ab236b
--- /dev/null
+++ b/examples/player/mediaapp.h
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) 2010 Marco Ballesio <gibrovacco@gmail.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MEDIAAPP_H
+#define MEDIAAPP_H
+
+#include <QtGui/QWidget>
+#include <QStyle>
+#include "player.h"
+
+QT_BEGIN_NAMESPACE
+class QBoxLayout;
+class QLabel;
+class QSlider;
+class QToolButton;
+class QTimer;
+QT_END_NAMESPACE
+
+class MediaApp : public QWidget {
+ Q_OBJECT
+
+ public:
+ MediaApp(QWidget *parent = 0);
+ ~MediaApp();
+ void openFile(const QString &fileName);
+
+ private Q_SLOTS:
+ void open();
+ void toggleFullScreen();
+ void stateChanged();
+ void posChanged();
+ void setPos(int);
+ void hideControls(){ showControls(false); }
+
+ private:
+ QToolButton *initButton(
+ QStyle::StandardPixmap, const char* , QObject* , const char*, QLayout*);
+ void createUI(QBoxLayout *);
+ void showControls(bool);
+ void mouseMoveEvent(QMouseEvent*);
+
+ QString baseDir;
+ Player *player;
+ QToolButton *openButton;
+ QToolButton *fullScreenButton;
+ QToolButton *playButton;
+ QToolButton *pauseButton;
+ QToolButton *stopButton;
+ QSlider *posSlider;
+ QLabel *posLabel;
+ QTimer fsTimer;
+};
+
+#endif
diff --git a/examples/player/player.cpp b/examples/player/player.cpp
new file mode 100644
index 0000000..dd89314
--- /dev/null
+++ b/examples/player/player.cpp
@@ -0,0 +1,195 @@
+/*
+ Copyright (C) 2010 Marco Ballesio <gibrovacco@gmail.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "player.h"
+#include <iostream>
+#include <QtCore/QTime>
+#include <QtCore/QDir>
+#include <QtGui/QApplication>
+#include <QGlib/Signal>
+#include <QGst/Global>
+#include <QGst/Pipeline>
+#include <QGst/ElementFactory>
+#include <QGst/XOverlay>
+#include <QGst/Bus>
+#include <QGst/Message>
+#include <QGst/Query>
+#include <QGst/Clock>
+#include <QGst/Event>
+
+/* This is a simple example of a command-line player. It accepts the URI of
+ * a media file as the first command line argument and then constructs a pipeline
+ * that uses playbin2 to decode the stream. */
+
+Player::Player(QWidget *parent)
+ : QGst::Ui::VideoWidget(parent) {
+ connect(&m_positionTimer, SIGNAL(timeout()), this, SLOT(updatePosition()));
+ setMouseTracking(true);
+ }
+
+void Player::setUri(const QString & uri) {
+ QString realUri = uri;
+
+ if(uri.indexOf("://") < 0){
+ if(realUri[0] != '/'){
+ realUri = QDir::current().path() + "/" + realUri;
+ }
+ realUri = "file://" + realUri;
+ }
+
+ if(!m_pipeline){
+ QGst::ElementPtr playbin = QGst::ElementFactory::make("playbin2");
+ if(playbin) {
+ m_pipeline = playbin.dynamicCast<QGst::Pipeline>();
+ QGst::BusPtr bus = m_pipeline->bus();
+ QGlib::Signal::connect(bus, "message", this, &Player::busMessage);
+ bus->addSignalWatch();
+ }
+ }
+
+ if(m_pipeline){
+ m_pipeline->setProperty("uri", realUri);
+ }
+}
+
+Player::~Player() {
+ if(m_pipeline)
+ m_pipeline->setState(QGst::StateNull);
+}
+
+void Player::updatePosition() {
+ Q_EMIT positionChanged(position());
+}
+
+void Player::play() {
+ if(m_pipeline){
+ m_pipeline->setState(QGst::StatePlaying);
+ }
+}
+
+void Player::pause( ){
+ if(m_pipeline){
+ m_pipeline->setState(QGst::StatePaused);
+ }
+}
+
+void Player::stop() {
+ if(m_pipeline){
+ m_pipeline->setState(QGst::StateReady);
+ }
+}
+
+void Player::setPosition(QTime pos) {
+ QGst::SeekEventPtr evt = QGst::SeekEvent::create(
+ 1.0, QGst::FormatTime, QGst::SeekFlagFlush,
+ QGst::SeekTypeSet, QGst::Clock::clockTimeFromTime(pos),
+ QGst::SeekTypeNone, -1);
+
+ m_pipeline.dynamicCast<QGst::Element>()->sendEvent(evt);
+}
+
+QTime Player::position() {
+ if(m_pipeline){
+ QGst::PositionQueryPtr query = QGst::PositionQuery::create(QGst::FormatTime);
+ m_pipeline.dynamicCast<QGst::Element>()->query(query);
+ return QGst::Clock::timeFromClockTime(query->position());
+ }
+ return QTime(0, 0, 0, 0);
+}
+
+QTime Player::length() {
+ if(m_pipeline){
+ QGst::DurationQueryPtr query = QGst::DurationQuery::create(QGst::FormatTime);
+ m_pipeline->query(query);
+ return QGst::Clock::timeFromClockTime(query->duration());
+ }
+ return QTime(0, 0, 0, 0);
+}
+
+QGst::State Player::state() {
+ QGst::State state;
+ if(!m_pipeline ||
+ m_pipeline.dynamicCast<QGst::Element>()->getState(&state, NULL, 1e9) !=
+ QGst::StateChangeSuccess)
+ {
+ state = QGst::StateNull;
+ }
+ return state;
+}
+
+void Player::handleStateChange(QGst::StateChangedMessagePtr scm) {
+ switch(scm->newState()){
+ case QGst::StatePlaying:
+ Q_EMIT positionChanged(position());
+ m_positionTimer.start(500);
+ break;
+ case QGst::StatePaused:
+ if(scm->oldState() == QGst::StateReady){
+ QGst::ElementPtr sink =
+ m_pipeline->property("video-sink").get<QGst::ElementPtr>();
+ if(sink){
+ setVideoSink(sink);
+ QGst::ChildProxyPtr proxy = sink.dynamicCast<QGst::ChildProxy>();
+ proxy->childByIndex(0)->setProperty("force-aspect-ratio", true);
+ }
+ } else
+ m_positionTimer.stop();
+ break;
+ case QGst::StateReady:
+ if(scm->oldState() == QGst::StatePaused)
+ /* Remove the sink now to avoid inter-thread issues with Qt
+ for the next time the pipeline goes to StatePlaying */
+ setVideoSink(QGst::ElementPtr());
+ break;
+ default:
+ break;
+ }
+}
+
+void Player::busMessage(const QGst::MessagePtr & message) {
+ switch(message->type()) {
+ case QGst::MessageEos: //End of stream. We reached the end of the file.
+ qDebug() << "got eos";
+ m_pipeline->setState(QGst::StateReady);
+ break;
+ case QGst::MessageError: //Some error occurred.
+ /*TODO: send a message to UI */
+ break;
+ case QGst::MessageAsyncDone:
+ {
+ //File prerolled, queries the pipeline to get the file duration
+ QGst::DurationQueryPtr query = QGst::DurationQuery::create(QGst::FormatTime);
+ //This will create a temporary (cast to query).
+ m_pipeline->query(query);
+ }
+ break;
+ case QGst::MessageStateChanged:
+ if(QGlib::Type::fromInstance(message->source()).
+ isA(QGlib::GetType<QGst::Pipeline>()))
+ {
+ QGst::StateChangedMessagePtr scm =
+ message.dynamicCast<QGst::StateChangedMessage>();
+ handleStateChange(scm);
+ Q_EMIT stateChanged(scm->newState());
+ }
+
+ break;
+ default:
+ break;
+ }
+}
+
diff --git a/examples/player/player.h b/examples/player/player.h
new file mode 100644
index 0000000..ac442b0
--- /dev/null
+++ b/examples/player/player.h
@@ -0,0 +1,76 @@
+/*
+ Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com>
+ Copyright (C) 2010 Marco Ballesio <gibrovacco@gmail.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PLAYER_H__
+#define __PLAYER_H__
+
+#include <QtCore/QEvent>
+#include <QtCore/QTimer>
+#include <QtCore/QString>
+#include <QtCore/QTime>
+#include <QGst/Global>
+#include <QGst/Pipeline>
+#include <QGst/Ui/VideoWidget>
+
+/* This is a simple example of a command-line player. It accepts the URI of
+ * an media file as the first command line argument and then constructs a pipeline
+ * that uses playbin2 to decode the stream.
+ * In the future this example will perhaps gain a simple GUI. */
+
+class Player : public QGst::Ui::VideoWidget
+{
+ Q_OBJECT;
+ public:
+ Player(QWidget *parent = 0);
+ ~Player();
+
+ void setUri(const QString &uri);
+ QTime position();
+ QTime length();
+ QGst::State state();
+
+ /* Everything is compiled with QT_NO_KEYWORDS because otherwise Signals::emit wouldn't compile
+ * so use Q_SLOTS instead of "slots" */
+ public Q_SLOTS:
+ void stop();
+ void play();
+ void pause();
+ void setPosition(QTime pos);
+ void updatePosition();
+
+Q_SIGNALS:
+ void positionChanged(QTime);
+ void stateChanged(QGst::State state);
+
+ private:
+ void busMessage(const QGst::MessagePtr & message);
+ void elementAdded(const QGst::ElementPtr & element);
+ void handleStateChange(QGst::StateChangedMessagePtr);
+ QGst::XOverlayPtr binVideoSink(QGst::BinPtr & bin);
+
+ /* This smart pointer is needed to keep a reference to the underlying GstPipeline object.
+ * Even if we are not going to use it, we must keep a reference to the pipeline,
+ * so that it remains in memory, together with all its child elements. If this
+ * reference is gone, the pipeline and all the elements will be destroyed.
+ * Note that we don't need to keep references to the individual elements, because
+ * when they are added in the pipeline, the pipeline keeps a reference on them. */
+ QGst::PipelinePtr m_pipeline;
+ QTimer m_positionTimer;
+};
+
+#endif
diff --git a/examples/player/player.pro b/examples/player/player.pro
index b75e5d1..1d56905 100644
--- a/examples/player/player.pro
+++ b/examples/player/player.pro
@@ -21,4 +21,5 @@ DEFINES += QT_NO_KEYWORDS
QT -= gui
# Input
-SOURCES += main.cpp
+HEADERS += movieplayer.h player.h
+SOURCES += main.cpp movieplayer.cpp player.cpp