QtGStreamer
0.10.1
|
This is an example audio/video player using QtGStreamer.
In this example, the GStreamer playbin2 element is used to perform most of the tasks. Our code is mostly integrating the UI with the pipeline, handling state changes, doing queries to learn about the position and duration of the stream, performing seeking, etc...
player.h:
/* Copyright (C) 2010 Marco Ballesio <gibrovacco@gmail.com> Copyright (C) 2011 Collabora Ltd. @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk> 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/QTimer> #include <QtCore/QTime> #include <QGst/Pipeline> #include <QGst/Ui/VideoWidget> class Player : public QGst::Ui::VideoWidget { Q_OBJECT public: Player(QWidget *parent = 0); ~Player(); void setUri(const QString & uri); QTime position() const; void setPosition(const QTime & pos); QTime length() const; QGst::State state() const; public Q_SLOTS: void play(); void pause(); void stop(); Q_SIGNALS: void positionChanged(); void stateChanged(); private: void onBusMessage(const QGst::MessagePtr & message); void handlePipelineStateChange(const QGst::StateChangedMessagePtr & scm); QGst::PipelinePtr m_pipeline; QTimer m_positionTimer; }; #endif
player.cpp:
/* Copyright (C) 2010 Marco Ballesio <gibrovacco@gmail.com> Copyright (C) 2011 Collabora Ltd. @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk> 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 <QtCore/QDir> #include <QGlib/Connect> #include <QGlib/Error> #include <QGst/Pipeline> #include <QGst/ElementFactory> #include <QGst/Bus> #include <QGst/Message> #include <QGst/Query> #include <QGst/ClockTime> #include <QGst/Event> Player::Player(QWidget *parent) : QGst::Ui::VideoWidget(parent) { //this timer is used to tell the ui to change its position slider & label //every 100 ms, but only when the pipeline is playing connect(&m_positionTimer, SIGNAL(timeout()), this, SIGNAL(positionChanged())); } Player::~Player() { if (m_pipeline) { m_pipeline->setState(QGst::StateNull); stopPipelineWatch(); } } void Player::setUri(const QString & uri) { QString realUri = uri; //if uri is not a real uri, assume it is a file path if (realUri.indexOf("://") < 0) { realUri = "file://" + QFileInfo(realUri).absoluteFilePath(); } if (!m_pipeline) { m_pipeline = QGst::ElementFactory::make("playbin2").dynamicCast<QGst::Pipeline>(); if (m_pipeline) { //let the video widget watch the pipeline for new video sinks watchPipeline(m_pipeline); //watch the bus for messages QGst::BusPtr bus = m_pipeline->bus(); bus->addSignalWatch(); QGlib::connect(bus, "message", this, &Player::onBusMessage); } else { qCritical() << "Failed to create the pipeline"; } } if (m_pipeline) { m_pipeline->setProperty("uri", realUri); } } QTime Player::position() const { if (m_pipeline) { //here we query the pipeline about its position //and we request that the result is returned in time format QGst::PositionQueryPtr query = QGst::PositionQuery::create(QGst::FormatTime); m_pipeline->query(query); return QGst::ClockTime(query->position()).toTime(); } else { return QTime(); } } void Player::setPosition(const QTime & pos) { QGst::SeekEventPtr evt = QGst::SeekEvent::create( 1.0, QGst::FormatTime, QGst::SeekFlagFlush, QGst::SeekTypeSet, QGst::ClockTime::fromTime(pos), QGst::SeekTypeNone, QGst::ClockTime::None ); m_pipeline->sendEvent(evt); } QTime Player::length() const { if (m_pipeline) { //here we query the pipeline about the content's duration //and we request that the result is returned in time format QGst::DurationQueryPtr query = QGst::DurationQuery::create(QGst::FormatTime); m_pipeline->query(query); return QGst::ClockTime(query->duration()).toTime(); } else { return QTime(); } } QGst::State Player::state() const { return m_pipeline ? m_pipeline->currentState() : QGst::StateNull; } 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::StateNull); //once the pipeline stops, the bus is flushed so we will //not receive any StateChangedMessage about this. //so, to inform the ui, we have to emit this signal manually. Q_EMIT stateChanged(); } } void Player::onBusMessage(const QGst::MessagePtr & message) { switch (message->type()) { case QGst::MessageEos: //End of stream. We reached the end of the file. stop(); break; case QGst::MessageError: //Some error occurred. qCritical() << message.staticCast<QGst::ErrorMessage>()->error(); stop(); break; case QGst::MessageStateChanged: //The element in message->source() has changed state if (message->source() == m_pipeline) { handlePipelineStateChange(message.staticCast<QGst::StateChangedMessage>()); } break; default: break; } } void Player::handlePipelineStateChange(const QGst::StateChangedMessagePtr & scm) { switch (scm->newState()) { case QGst::StatePlaying: //start the timer when the pipeline starts playing m_positionTimer.start(100); break; case QGst::StatePaused: //stop the timer when the pipeline pauses if(scm->oldState() == QGst::StatePlaying) { m_positionTimer.stop(); } break; default: break; } Q_EMIT stateChanged(); } #include "player.moc"
mediaapp.h:
/* Copyright (C) 2010 Marco Ballesio <gibrovacco@gmail.com> Copyright (C) 2011 Collabora Ltd. @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk> 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 <QtGui/QStyle> #include <QtCore/QTimer> class Player; class QBoxLayout; class QLabel; class QSlider; class QToolButton; class QTimer; 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 onStateChanged(); void onPositionChanged(); void setPosition(int position); void showControls(bool show = true); void hideControls() { showControls(false); } protected: void mouseMoveEvent(QMouseEvent *event); private: QToolButton *initButton(QStyle::StandardPixmap icon, const QString & tip, QObject *dstobj, const char *slot_method, QLayout *layout); void createUI(QBoxLayout *appLayout); QString m_baseDir; Player *m_player; QToolButton *m_openButton; QToolButton *m_fullScreenButton; QToolButton *m_playButton; QToolButton *m_pauseButton; QToolButton *m_stopButton; QSlider *m_positionSlider; QLabel *m_positionLabel; QTimer m_fullScreenTimer; }; #endif
mediaapp.cpp:
/* Copyright (C) 2010 Marco Ballesio <gibrovacco@gmail.com> Copyright (C) 2011 Collabora Ltd. @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk> 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 "mediaapp.h" #include "player.h" #include <QtGui/QBoxLayout> #include <QtGui/QFileDialog> #include <QtGui/QToolButton> #include <QtGui/QLabel> #include <QtGui/QSlider> #include <QtGui/QMouseEvent> MediaApp::MediaApp(QWidget *parent) : QWidget(parent) { //create the player m_player = new Player(this); connect(m_player, SIGNAL(positionChanged()), this, SLOT(onPositionChanged())); connect(m_player, SIGNAL(stateChanged()), this, SLOT(onStateChanged())); //m_baseDir is used to remember the last directory that was used. //defaults to the current working directory m_baseDir = QLatin1String("."); //this timer (re-)hides the controls after a few seconds when we are in fullscreen mode m_fullScreenTimer.setSingleShot(true); connect(&m_fullScreenTimer, SIGNAL(timeout()), this, SLOT(hideControls())); //create the UI QVBoxLayout *appLayout = new QVBoxLayout; appLayout->setContentsMargins(0, 0, 0, 0); createUI(appLayout); setLayout(appLayout); onStateChanged(); //set the controls to their default state setWindowTitle(tr("QtGStreamer example player")); resize(400, 400); } MediaApp::~MediaApp() { delete m_player; } void MediaApp::openFile(const QString & fileName) { m_baseDir = QFileInfo(fileName).path(); m_player->stop(); m_player->setUri(fileName); m_player->play(); } void MediaApp::open() { QString fileName = QFileDialog::getOpenFileName(this, tr("Open a Movie"), m_baseDir); if (!fileName.isEmpty()) { openFile(fileName); } } void MediaApp::toggleFullScreen() { if (isFullScreen()) { setMouseTracking(false); m_player->setMouseTracking(false); m_fullScreenTimer.stop(); showControls(); showNormal(); } else { setMouseTracking(true); m_player->setMouseTracking(true); hideControls(); showFullScreen(); } } void MediaApp::onStateChanged() { QGst::State newState = m_player->state(); m_playButton->setEnabled(newState != QGst::StatePlaying); m_pauseButton->setEnabled(newState == QGst::StatePlaying); m_stopButton->setEnabled(newState != QGst::StateNull); m_positionSlider->setEnabled(newState != QGst::StateNull); //if we are in Null state, call onPositionChanged() to restore //the position of the slider and the text on the label if (newState == QGst::StateNull) { onPositionChanged(); } } /* Called when the positionChanged() is received from the player */ void MediaApp::onPositionChanged() { QTime length(0,0); QTime curpos(0,0); if (m_player->state() != QGst::StateReady && m_player->state() != QGst::StateNull) { length = m_player->length(); curpos = m_player->position(); } m_positionLabel->setText(curpos.toString("hh:mm:ss.zzz") + "/" + length.toString("hh:mm:ss.zzz")); if (length != QTime(0,0)) { m_positionSlider->setValue(curpos.msecsTo(QTime()) * 1000 / length.msecsTo(QTime())); } else { m_positionSlider->setValue(0); } if (curpos != QTime(0,0)) { m_positionLabel->setEnabled(true); m_positionSlider->setEnabled(true); } } /* Called when the user changes the slider's position */ void MediaApp::setPosition(int value) { uint length = -m_player->length().msecsTo(QTime()); if (length != 0 && value > 0) { QTime pos; pos = pos.addMSecs(length * (value / 1000.0)); m_player->setPosition(pos); } } void MediaApp::showControls(bool show) { m_openButton->setVisible(show); m_playButton->setVisible(show); m_pauseButton->setVisible(show); m_stopButton->setVisible(show); m_fullScreenButton->setVisible(show); m_positionSlider->setVisible(show); m_positionLabel->setVisible(show); } void MediaApp::mouseMoveEvent(QMouseEvent *event) { Q_UNUSED(event); if (isFullScreen()) { showControls(); m_fullScreenTimer.start(3000); //re-hide controls after 3s } } QToolButton *MediaApp::initButton(QStyle::StandardPixmap icon, const QString & 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(tip); connect(button, SIGNAL(clicked()), dstobj, slot_method); layout->addWidget(button); return button; } void MediaApp::createUI(QBoxLayout *appLayout) { appLayout->addWidget(m_player); m_positionLabel = new QLabel(); m_positionSlider = new QSlider(Qt::Horizontal); m_positionSlider->setTickPosition(QSlider::TicksBelow); m_positionSlider->setTickInterval(10); m_positionSlider->setMaximum(1000); connect(m_positionSlider, SIGNAL(sliderMoved(int)), this, SLOT(setPosition(int))); QGridLayout *posLayout = new QGridLayout; posLayout->addWidget(m_positionLabel, 1, 0); posLayout->addWidget(m_positionSlider, 1, 1, 1, 2); appLayout->addLayout(posLayout); QHBoxLayout *btnLayout = new QHBoxLayout; btnLayout->addStretch(); m_openButton = initButton(QStyle::SP_DialogOpenButton, tr("Open File"), this, SLOT(open()), btnLayout); m_playButton = initButton(QStyle::SP_MediaPlay, tr("Play"), m_player, SLOT(play()), btnLayout); m_pauseButton = initButton(QStyle::SP_MediaPause, tr("Pause"), m_player, SLOT(pause()), btnLayout); m_stopButton = initButton(QStyle::SP_MediaStop, tr("Stop"), m_player, SLOT(stop()), btnLayout); m_fullScreenButton = initButton(QStyle::SP_TitleBarMaxButton, tr("Fullscreen"), this, SLOT(toggleFullScreen()), btnLayout); btnLayout->addStretch(); appLayout->addLayout(btnLayout); } #include "mediaapp.moc"
main.cpp:
/* 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 "mediaapp.h" #include <QtGui/QApplication> #include <QGst/Init> int main(int argc, char *argv[]) { QApplication app(argc, argv); QGst::init(&argc, &argv); MediaApp media; media.show(); if (argc == 2) { media.openFile(argv[1]); } return app.exec(); }