2 Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com>
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published
6 by the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include <QtCore/QCoreApplication>
20 #include <QtCore/QFile>
21 #include <QGlib/Signal>
22 #include <QGst/Global>
23 #include <QGst/Pipeline>
24 #include <QGst/ElementFactory>
25 #include <QGst/GhostPad>
26 #include <QGst/Structure>
28 #include <QGst/Message>
31 /* This is a simple example of a command-line audio player. It accepts the filename of
32 * an audio file as the first command line argument and then constructs a pipeline
33 * that uses decodebin2 to decode the audio stream and autoaudiosink to output it to
34 * the sound card. In the future this example will be expanded to handle video as well
35 * and perhaps it will gain a simple GUI too. */
40 Player(const QString & fileName);
44 void onBusSyncMessage(const QGst::MessagePtr & message);
45 void onNewDecodedPad(QGst::PadPtr newPad);
46 static QGst::BinPtr createAudioSinkBin();
48 /* This smart pointer is needed to keep a reference to the underlying GstPipeline object.
49 * Even if we are not going to use it, we must keep a reference to the pipeline,
50 * so that it remains in memory, together with all its child elements. If this
51 * reference is gone, the pipeline and all the elements will be destroyed.
52 * Note that we don't need to keep references to the individual elements, because
53 * when they are added in the pipeline, the pipeline keeps a reference on them. */
54 QGst::PipelinePtr m_pipeline;
57 Player::Player(const QString & fileName)
59 m_pipeline = QGst::Pipeline::create();
61 QGst::ElementPtr filesrc = QGst::ElementFactory::make("filesrc");
62 QGst::ElementPtr decodebin = QGst::ElementFactory::make("decodebin2");
64 filesrc->setProperty("location", fileName);
65 QGlib::Signal::connect(decodebin, "new-decoded-pad", this, &Player::onNewDecodedPad);
67 m_pipeline->add(filesrc);
68 m_pipeline->add(decodebin);
69 filesrc->link(decodebin);
71 QGst::BusPtr bus = m_pipeline->bus();
72 bus->enableSyncMessageEmission();
73 QGlib::Signal::connect(bus, "sync-message", this, &Player::onBusSyncMessage);
75 m_pipeline->setState(QGst::StatePlaying);
80 m_pipeline->setState(QGst::StateNull);
82 /* When m_pipeline is destructed, the last reference to our pipeline will be gone,
83 * and with it all the elements, buses, etc will be destroyed too. As a result,
84 * there is no need to cleanup here. */
87 void Player::onBusSyncMessage(const QGst::MessagePtr & message)
89 /* WARNING this slot gets called in a different thread */
90 switch(message->type()) {
91 case QGst::MessageEos: //End of stream. We reached the end of the file.
92 case QGst::MessageError: //Some error occurred.
93 /* QCoreApplication::quit is safe to be called in another thread.
94 * It will schedule the main event loop to exit, when execution
95 * in the main thread has reached the event loop. */
96 QCoreApplication::quit();
98 case QGst::MessageAsyncDone:
100 //File prerolled, queries the pipeline to get the file duration
101 QGst::DurationQueryPtr query = QGst::DurationQuery::create(QGst::FormatTime);
102 //This will create a temporary (cast to query).
103 m_pipeline->query(query);
105 qDebug() << query->duration();
113 /* This method will be called every time a new "src" pad is available on the decodebin2 element.
114 * Here we have to check what kind of data this pad transfers (usually it is either "audio/x-raw-*"
115 * or "video/x-raw-*") and connect an appropriate sink that can handle this type of data. */
116 void Player::onNewDecodedPad(QGst::PadPtr newPad)
119 QGst::CapsPtr caps = newPad->caps();
120 QGst::SharedStructure structure = caps->structure(0);
122 /* The caps' first structure's name tells us what kind of data the pad transfers.
123 * Here we want to handle either audio/x-raw-int or audio/x-raw-float. Both types
124 * can be handled by the audioconvert element that is contained in the audioSinkBin,
125 * so there is no need to handle them separately */
126 if (structure.name().contains("audio/x-raw")) {
127 QGst::BinPtr audioSinkBin = createAudioSinkBin();
128 m_pipeline->add(audioSinkBin);
130 /* The newly created bin must go to the playing state in order to function.
131 * Here we tell it to synchronise its state with its parent, the pipeline,
132 * which is scheduled to go to the playing state. */
133 audioSinkBin->syncStateWithParent();
135 newPad->link(audioSinkBin->getStaticPad("sink"));
139 QGst::BinPtr Player::createAudioSinkBin()
141 QGst::BinPtr bin = QGst::Bin::create();
143 QGst::ElementPtr audioconvert = QGst::ElementFactory::make("audioconvert");
144 QGst::ElementPtr audiosink = QGst::ElementFactory::make("autoaudiosink");
146 bin->add(audioconvert);
148 audioconvert->link(audiosink);
150 /* Add a sink pad to the bin that proxies the sink pad of the audioconvert element */
151 bin->addPad(QGst::GhostPad::create(audioconvert->getStaticPad("sink"), "sink"));
156 static void sighandler(int code)
159 QCoreApplication::quit();
162 int main(int argc, char **argv)
164 QCoreApplication app(argc, argv);
165 QGst::init(&argc, &argv);
169 fileName = QFile::decodeName(argv[1]);
172 if (!QFile::exists(fileName)) {
173 std::cerr << "Usage: " << argv[0] << " fileToPlay" << std::endl;
177 Player *p = new Player(fileName);
179 signal(SIGINT, sighandler);
180 int result = app.exec();
182 delete p; // we must delete all gstreamer objects before calling QGst::cleanup()