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 | |
38 | class QTimer; |
39 | typedef QMultiMap<QString, QString> TagMap; |
40 | |
41 | namespace Phonon |
42 | { |
43 | |
44 | class Mrl; |
45 | |
46 | namespace Gstreamer |
47 | { |
48 | |
49 | class VideoWidget; |
50 | class AudioPath; |
51 | class VideoPath; |
52 | class AudioOutput; |
53 | |
54 | class 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 | |
70 | public: |
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 | |
147 | public Q_SLOTS: |
148 | void requestState(Phonon::State); |
149 | |
150 | Q_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 (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 | |
178 | protected: |
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 | |
192 | private 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 | |
209 | private: |
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> () const; |
218 | void (MediaController::NavigationMenu ); |
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 | |