1/* This file is part of the KDE project.
2
3 Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 Copyright (C) 2011 Torrie Fischer <tdfischer@kde.org>
5
6 This library is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 2.1 or 3 of the License.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this library. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#ifndef Phonon_GSTREAMER_MEDIAOBJECT_H
20#define Phonon_GSTREAMER_MEDIAOBJECT_H
21
22#include "medianode.h"
23#include "pipeline.h"
24#include <phonon/mediaobjectinterface.h>
25#include <phonon/addoninterface.h>
26#include <phonon/objectdescription.h>
27#include <phonon/MediaController>
28
29#include <QtCore/QObject>
30#include <QtCore/QString>
31#include <QtCore/QStringList>
32#include <QtCore/QVariant>
33#include <QtCore/QWaitCondition>
34#include <QtCore/QMutex>
35
36#include "phonon-config-gstreamer.h" // krazy:exclude=includes
37
38class QTimer;
39typedef QMultiMap<QString, QString> TagMap;
40
41namespace Phonon
42{
43
44class Mrl;
45
46namespace Gstreamer
47{
48
49class VideoWidget;
50class AudioPath;
51class VideoPath;
52class AudioOutput;
53
54class MediaObject : public QObject, public MediaObjectInterface
55#ifndef QT_NO_PHONON_MEDIACONTROLLER
56 , public AddonInterface
57#endif
58 , public MediaNode
59{
60 friend class Stream;
61 friend class AudioDataOutput;
62 Q_OBJECT
63 Q_INTERFACES(Phonon::MediaObjectInterface
64#ifndef QT_NO_PHONON_MEDIACONTROLLER
65 Phonon::AddonInterface
66#endif
67 Phonon::Gstreamer::MediaNode
68 )
69
70public:
71 MediaObject(Backend *backend, QObject *parent);
72 ~MediaObject();
73 Phonon::State state() const;
74
75 bool hasVideo() const;
76 bool isSeekable() const;
77
78 qint64 currentTime() const;
79 qint32 tickInterval() const;
80
81 void setTickInterval(qint32 newTickInterval);
82
83 void play();
84 void pause();
85 void stop();
86 void seek(qint64 time);
87
88 Phonon::State translateState(GstState state) const;
89
90 QString errorString() const;
91 Phonon::ErrorType errorType() const;
92
93 qint64 totalTime() const;
94
95 qint32 prefinishMark() const;
96 void setPrefinishMark(qint32 newPrefinishMark);
97
98 qint32 transitionTime() const;
99 void setTransitionTime(qint32);
100 qint64 remainingTime() const;
101
102 void setSource(const MediaSource &source);
103 void setNextSource(const MediaSource &source);
104 MediaSource source() const;
105
106 // No additional interfaces currently supported
107#ifndef QT_NO_PHONON_MEDIACONTROLLER
108 bool hasInterface(Interface) const;
109 QVariant interfaceCall(Interface, int, const QList<QVariant> &);
110#endif
111 bool isLoading()
112 {
113 return m_loading;
114 }
115
116 bool audioAvailable()
117 {
118 return m_pipeline->audioIsAvailable();
119 }
120
121 bool videoAvailable()
122 {
123 return m_pipeline->videoIsAvailable();
124 }
125
126 GstElement *audioGraph()
127 {
128 return m_pipeline->audioGraph();
129 }
130
131 GstElement *videoGraph()
132 {
133 return m_pipeline->videoGraph();
134 }
135
136 Pipeline *pipeline()
137 {
138 return m_pipeline;
139 }
140
141 void saveState();
142 void resumeState();
143
144 QMultiMap<QString, QString> metaData();
145 void setMetaData(QMultiMap<QString, QString> newData);
146
147public Q_SLOTS:
148 void requestState(Phonon::State);
149
150Q_SIGNALS:
151 void currentSourceChanged(const MediaSource &newSource);
152 void stateChanged(Phonon::State newstate, Phonon::State oldstate);
153 void tick(qint64 time);
154 void metaDataChanged(QMultiMap<QString, QString>);
155 void seekableChanged(bool);
156 void hasVideoChanged(bool);
157
158 void finished();
159 void prefinishMarkReached(qint32);
160 void aboutToFinish();
161 void totalTimeChanged(qint64 length);
162 void bufferStatus(int percentFilled);
163
164 // AddonInterface:
165 void titleChanged(int);
166 void availableTitlesChanged(int);
167 void availableMenusChanged(QList<MediaController::NavigationMenu>);
168
169 // Not implemented
170 void chapterChanged(int);
171 void availableChaptersChanged(int);
172 void angleChanged(int);
173 void availableAnglesChanged(int);
174
175 void availableSubtitlesChanged();
176 void availableAudioChannelsChanged();
177
178protected:
179 void loadingComplete();
180 Q_INVOKABLE void setError(const QString &errorString, Phonon::ErrorType error = NormalError);
181
182 GstElement *audioElement()
183 {
184 return m_pipeline->audioPipe();
185 }
186
187 GstElement *videoElement()
188 {
189 return m_pipeline->videoPipe();
190 }
191
192private Q_SLOTS:
193 void handleTrackCountChange(int tracks);
194 void getSubtitleInfo(int stream);
195 void getAudioChannelInfo(int stream);
196 void emitTick();
197 void beginPlay();
198 void autoDetectSubtitle();
199 void logWarning(const QString &);
200
201 void handleEndOfStream();
202 void handleBuffering(int);
203 void handleStateChange(GstState oldState, GstState newState);
204 void handleDurationChange(qint64);
205
206 void handleAboutToFinish();
207 void handleStreamChange();
208
209private:
210 // GStreamer specific :
211 void setTotalTime(qint64 newTime);
212 qint64 getPipelinePos() const;
213
214 int _iface_availableTitles() const;
215 int _iface_currentTitle() const;
216 void _iface_setCurrentTitle(int title);
217 QList<MediaController::NavigationMenu> _iface_availableMenus() const;
218 void _iface_jumpToMenu(MediaController::NavigationMenu menu);
219 QList<SubtitleDescription> _iface_availableSubtitles() const;
220 SubtitleDescription _iface_currentSubtitle() const;
221 void _iface_setCurrentSubtitle(const SubtitleDescription &subtitle);
222 void changeTitle(const QString &format, int title);
223 void changeSubUri(const Mrl &mrl);
224
225 QList<AudioChannelDescription> _iface_availableAudioChannels() const;
226 AudioChannelDescription _iface_currentAudioChannel() const;
227 void _iface_setCurrentAudioChannel(const AudioChannelDescription &channel);
228
229 bool m_resumeState;
230 State m_oldState;
231 quint64 m_oldPos;
232
233 State m_state;
234 State m_pendingState;
235 QTimer *m_tickTimer;
236 qint32 m_tickInterval;
237
238 MediaSource m_nextSource;
239 qint32 m_prefinishMark;
240 qint32 m_transitionTime;
241 bool m_isStream;
242
243 qint64 m_posAtSeek;
244
245 bool m_prefinishMarkReachedNotEmitted;
246 bool m_aboutToFinishEmitted;
247 bool m_loading;
248
249 qint64 m_totalTime;
250 bool m_atStartOfStream;
251 Phonon::ErrorType m_error;
252 QString m_errorString;
253
254 Pipeline *m_pipeline;
255 bool m_autoplayTitles;
256 int m_availableTitles;
257 int m_currentTitle;
258 SubtitleDescription m_currentSubtitle;
259 AudioChannelDescription m_currentAudioChannel;
260 int m_pendingTitle;
261
262 // When we emit aboutToFinish(), libphonon calls setNextSource. To achieve gapless playback,
263 // the pipeline is immediately told to start using that new source. This can break seeking
264 // since the aboutToFinish signal tends to be emitted around 15 seconds or so prior to actually
265 // ending the current track.
266 // If we seek backwards in time, we'd still have the 'next' source but now be a different
267 // position than the start. This flag tells us to reset the pipeline's source to the 'previous'
268 // one prior to seeking.
269 //
270 // It also causes currentSourceChanged() to be emitted once the duration changes, which happens
271 // when we actually *do* start hearing the new source.
272 bool m_waitingForNextSource;
273 bool m_waitingForPreviousSource;
274
275 bool m_skippingEOS;
276 bool m_doingEOS; // To prevent superfluously signal emission.
277
278 // This keeps track of the source currently heard over the speakers.
279 // It can be different from the pipeline's current source due to how the
280 // almost-at-end gapless playback code works.
281 Phonon::MediaSource m_source;
282 QMultiMap<QString, QString> m_sourceMeta;
283
284 //This simply pauses the gst signal handler 'till we get something
285 QMutex m_aboutToFinishLock;
286 QWaitCondition m_aboutToFinishWait;
287
288 qint64 m_lastTime;
289 bool m_skipGapless;
290
291 /*** Tracks whereever the MO is actively handling an aboutToFinish CB right now. */
292 bool m_handlingAboutToFinish;
293};
294}
295} //namespace Phonon::Gstreamer
296
297#endif // Phonon_GSTREAMER_MEDIAOBJECT_H
298