Merge branch 'events'
[qtgstreamer:qtgstreamer.git] / examples / player / main.cpp
1 /*
2     Copyright (C) 2010  George Kiagiadakis <kiagiadakis.george@gmail.com>
3
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.
8
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.
13
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/>.
16 */
17 #include <iostream>
18 #include <signal.h>
19 #include <QtCore/QCoreApplication>
20 #include <QtCore/QFile>
21 #include <QtCore/QTime>
22 #include <QGlib/Signal>
23 #include <QGst/Global>
24 #include <QGst/Pipeline>
25 #include <QGst/ElementFactory>
26 #include <QGst/GhostPad>
27 #include <QGst/Structure>
28 #include <QGst/Bus>
29 #include <QGst/Message>
30 #include <QGst/Query>
31 #include <QGst/Clock>
32 #include <QGst/Event>
33
34 /* This is a simple example of a command-line audio player. It accepts the filename of
35  * an audio file as the first command line argument and then constructs a pipeline
36  * that uses decodebin2 to decode the audio stream and autoaudiosink to output it to
37  * the sound card. In the future this example will be expanded to handle video as well
38  * and perhaps it will gain a simple GUI too. */
39
40 class Player
41 {
42 public:
43     Player(const QString & fileName);
44     ~Player();
45
46 private:
47     void onBusSyncMessage(const QGst::MessagePtr & message);
48     void onNewDecodedPad(QGst::PadPtr newPad);
49     static QGst::BinPtr createAudioSinkBin();
50
51     /* This smart pointer is needed to keep a reference to the underlying GstPipeline object.
52      * Even if we are not going to use it, we must keep a reference to the pipeline,
53      * so that it remains in memory, together with all its child elements. If this
54      * reference is gone, the pipeline and all the elements will be destroyed.
55      * Note that we don't need to keep references to the individual elements, because
56      * when they are added in the pipeline, the pipeline keeps a reference on them. */
57     QGst::PipelinePtr m_pipeline;
58 };
59
60 Player::Player(const QString & fileName)
61 {
62     m_pipeline = QGst::Pipeline::create();
63
64     QGst::ElementPtr filesrc = QGst::ElementFactory::make("filesrc");
65     QGst::ElementPtr decodebin = QGst::ElementFactory::make("decodebin2");
66
67     filesrc->setProperty("location", fileName);
68     QGlib::Signal::connect(decodebin, "new-decoded-pad", this, &Player::onNewDecodedPad);
69
70     m_pipeline->add(filesrc);
71     m_pipeline->add(decodebin);
72     filesrc->link(decodebin);
73
74     QGst::BusPtr bus = m_pipeline->bus();
75     bus->enableSyncMessageEmission();
76     QGlib::Signal::connect(bus, "sync-message", this, &Player::onBusSyncMessage);
77
78     m_pipeline->setState(QGst::StatePlaying);
79 }
80
81 Player::~Player()
82 {
83     m_pipeline->setState(QGst::StateNull);
84
85     /* When m_pipeline is destructed, the last reference to our pipeline will be gone,
86      * and with it all the elements, buses, etc will be destroyed too. As a result,
87      * there is no need to cleanup here. */
88 }
89
90 void Player::onBusSyncMessage(const QGst::MessagePtr & message)
91 {
92     /* WARNING this slot gets called in a different thread */
93     switch(message->type()) {
94     case QGst::MessageEos: //End of stream. We reached the end of the file.
95     case QGst::MessageError: //Some error occurred.
96         /* QCoreApplication::quit is safe to be called in another thread.
97          * It will schedule the main event loop to exit, when execution
98          * in the main thread has reached the event loop. */
99         QCoreApplication::quit();
100         break;
101     case QGst::MessageAsyncDone:
102         {
103         //File prerolled, queries the pipeline to get the file duration
104         QGst::DurationQueryPtr query = QGst::DurationQuery::create(QGst::FormatTime);
105         //This will create a temporary (cast to query).
106         m_pipeline->query(query);
107
108         //qDebug() << QGst::Clock::timeFromClockTime(query->duration());
109
110         /*Set the pipeline to seek to 6 seconds in the stream and play until it reaches 15 secs
111          * Notice that using element->seek() is probably better in most cases
112          *
113         QGst::SeekEventPtr evt = QGst::SeekEvent::create(1.0, QGst::FormatTime,
114                                                      QGst::SeekFlagNone,
115                                                      QGst::SeekTypeSet,
116                                                      QGst::Clock::clockTimeFromTime(QTime(0,0,6)),
117                                                      QGst::SeekTypeSet,
118                                                      QGst::Clock::clockTimeFromTime(QTime(0,0,15))
119                                                      );
120         m_pipeline->sendEvent(evt);*/
121
122         /* this is the simple seek version
123         m_pipeline->seek(QGst::FormatTime, QGst::SeekFlagNone,
124                          QGst::Clock::clockTimeFromTime(QTime(0,0,15)) );*/
125         }
126         break;
127     default:
128         break;
129     }
130 }
131
132 /* This method will be called every time a new "src" pad is available on the decodebin2 element.
133  * Here we have to check what kind of data this pad transfers (usually it is either "audio/x-raw-*"
134  * or "video/x-raw-*") and connect an appropriate sink that can handle this type of data. */
135 void Player::onNewDecodedPad(QGst::PadPtr newPad)
136 {
137
138     QGst::CapsPtr caps = newPad->caps();
139     QGst::SharedStructure structure = caps->structure(0);
140
141     /* The caps' first structure's name tells us what kind of data the pad transfers.
142      * Here we want to handle either audio/x-raw-int or audio/x-raw-float. Both types
143      * can be handled by the audioconvert element that is contained in the audioSinkBin,
144      * so there is no need to handle them separately */
145     if (structure.name().contains("audio/x-raw")) {
146         QGst::BinPtr audioSinkBin = createAudioSinkBin();
147         m_pipeline->add(audioSinkBin);
148
149         /* The newly created bin must go to the playing state in order to function.
150          * Here we tell it to synchronise its state with its parent, the pipeline,
151          * which is scheduled to go to the playing state. */
152         audioSinkBin->syncStateWithParent();
153
154         newPad->link(audioSinkBin->getStaticPad("sink"));
155     }
156 }
157
158 QGst::BinPtr Player::createAudioSinkBin()
159 {
160     QGst::BinPtr bin = QGst::Bin::create();
161
162     QGst::ElementPtr audioconvert = QGst::ElementFactory::make("audioconvert");
163     QGst::ElementPtr audiosink = QGst::ElementFactory::make("autoaudiosink");
164
165     bin->add(audioconvert);
166     bin->add(audiosink);
167     audioconvert->link(audiosink);
168
169     /* Add a sink pad to the bin that proxies the sink pad of the audioconvert element */
170     bin->addPad(QGst::GhostPad::create(audioconvert->getStaticPad("sink"), "sink"));
171
172     return bin;
173 }
174
175 static void sighandler(int code)
176 {
177     Q_UNUSED(code);
178     QCoreApplication::quit();
179 }
180
181 int main(int argc, char **argv)
182 {
183     QCoreApplication app(argc, argv);
184     QGst::init(&argc, &argv);
185
186     QString fileName;
187     if (argc > 1) {
188         fileName = QFile::decodeName(argv[1]);
189     }
190
191     if (!QFile::exists(fileName)) {
192         std::cerr << "Usage: " << argv[0] << " fileToPlay" << std::endl;
193         return 1;
194     }
195
196     Player *p = new Player(fileName);
197
198     signal(SIGINT, sighandler);
199     int result = app.exec();
200
201     delete p; // we must delete all gstreamer objects before calling QGst::cleanup()
202     QGst::cleanup();
203
204     return result;
205 }