1 | /* This file is part of the KDE project |
2 | Copyright (C) 2005 Matthias Kretz <kretz@kde.org> |
3 | |
4 | This library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) version 3, or any |
8 | later version accepted by the membership of KDE e.V. (or its |
9 | successor approved by the membership of KDE e.V.), Nokia Corporation |
10 | (or its successors, if any) and the KDE Free Qt Foundation, which shall |
11 | act as a proxy defined in Section 6 of version 3 of the license. |
12 | |
13 | This library is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | Lesser General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU Lesser General Public |
19 | License along with this library. If not, see <http://www.gnu.org/licenses/>. |
20 | |
21 | */ |
22 | #ifndef Phonon_MEDIAOBJECT_H |
23 | #define Phonon_MEDIAOBJECT_H |
24 | |
25 | #include "medianode.h" |
26 | #include "mediasource.h" |
27 | #include "phonon_export.h" |
28 | #include "phonondefs.h" |
29 | #include "phononnamespace.h" |
30 | |
31 | |
32 | namespace Phonon |
33 | { |
34 | class MediaObjectPrivate; |
35 | |
36 | /** \class MediaObject mediaobject.h phonon/MediaObject |
37 | * \short Interface for media playback of a given URL. |
38 | * |
39 | * This class is the most important class in %Phonon. Use it to open a media |
40 | * file at an arbitrary location, a CD or DVD or to stream media data from |
41 | * the application to the backend. |
42 | * |
43 | * This class controls the state (play, pause, stop, seek) |
44 | * and you can use it to get a lot of information about the media data. |
45 | * |
46 | * Notice that most functions of this class are asynchronous. |
47 | * That means if you call play() the object only starts playing when the |
48 | * stateChanged() signal tells you that the object changed into PlayingState. |
49 | * The states you can expect are documented for those methods. |
50 | * |
51 | * A common usage example is the following: |
52 | * \code |
53 | * media = new MediaObject(this); |
54 | * connect(media, SIGNAL(finished()), SLOT(slotFinished()); |
55 | * media->setCurrentSource("/home/username/music/filename.ogg"); |
56 | * media->play(); |
57 | * \endcode |
58 | * |
59 | * If you want to play more than one media file (one after another) you can |
60 | * either tell MediaObject about all those files |
61 | * \code |
62 | * media->setCurrentSource(":/sounds/startsound.ogg"); |
63 | * media->enqueue("/home/username/music/song.mp3"); |
64 | * media->enqueue(":/sounds/endsound.ogg"); |
65 | * \endcode |
66 | * or provide the next file just in time: |
67 | * \code |
68 | * media->setCurrentSource(":/sounds/startsound.ogg"); |
69 | * connect(media, SIGNAL(aboutToFinish()), SLOT(enqueueNextSource())); |
70 | * } |
71 | * |
72 | * void enqueueNextSource() |
73 | * { |
74 | * media->enqueue("/home/username/music/song.mp3"); |
75 | * } |
76 | * \endcode |
77 | * |
78 | * This integration is opt-in only and can be enabled by setting the |
79 | * PlaybackTracking property to true: |
80 | * \code |
81 | * media->setProperty("PlaybackTracking", true); |
82 | * \endcode |
83 | * |
84 | * This kind of information is normally used to provide a universal history |
85 | * view to the user, such as what songs were played when, regardless of the |
86 | * media player. This is in addition to any emails read, IM conversations, |
87 | * websites viewed, etc. |
88 | * |
89 | * \ingroup Playback |
90 | * \ingroup Recording |
91 | * \author Matthias Kretz <kretz@kde.org> |
92 | */ |
93 | class PHONON_EXPORT MediaObject : public QObject, public MediaNode |
94 | { |
95 | friend class FrontendInterfacePrivate; |
96 | Q_OBJECT |
97 | P_DECLARE_PRIVATE(MediaObject) |
98 | PHONON_OBJECT(MediaObject) |
99 | /** |
100 | * \brief Defines the time between media sources. |
101 | * |
102 | * A positive transition time defines a gap of silence between queued |
103 | * media sources. |
104 | * |
105 | * A transition time of 0 ms requests gapless playback (sample precise |
106 | * queueing of the next source). |
107 | * |
108 | * A negative transition time defines a crossfade between the queued |
109 | * media sources. |
110 | * |
111 | * Defaults to 0 (gapless playback). |
112 | * |
113 | * \warning This feature might not work reliably on every platform. |
114 | */ |
115 | Q_PROPERTY(qint32 transitionTime READ transitionTime WRITE setTransitionTime) |
116 | |
117 | /** |
118 | * \brief Get a signal before playback finishes. |
119 | * |
120 | * This property specifies the time in milliseconds the |
121 | * prefinishMarkReached signal is |
122 | * emitted before the playback finishes. A value of \c 0 disables the |
123 | * signal. |
124 | * |
125 | * Defaults to \c 0 (disabled). |
126 | * |
127 | * \warning For some media data the total time cannot be determined |
128 | * accurately, therefore the accuracy of the prefinishMarkReached signal |
129 | * can be bad sometimes. Still, it is better to use this method than to |
130 | * look at totalTime() and currentTime() to emulate the behaviour |
131 | * because the backend might have more information available than your |
132 | * application does through totalTime and currentTime. |
133 | * |
134 | * \see prefinishMarkReached |
135 | */ |
136 | Q_PROPERTY(qint32 prefinishMark READ prefinishMark WRITE setPrefinishMark) |
137 | |
138 | /** |
139 | * \brief The time interval in milliseconds between two ticks. |
140 | * |
141 | * The %tick interval is the time that elapses between the emission of two tick signals. |
142 | * If you set the interval to \c 0 the tick signal gets disabled. |
143 | * |
144 | * Defaults to \c 0 (disabled). |
145 | * |
146 | * \warning The back-end is free to choose a different tick interval close |
147 | * to what you asked for. This means that the following code \em may \em fail: |
148 | * \code |
149 | * int x = 200; |
150 | * media->setTickInterval(x); |
151 | * Q_ASSERT(x == producer->tickInterval()); |
152 | * \endcode |
153 | * On the other hand the following is guaranteed: |
154 | * \code |
155 | * int x = 200; |
156 | * media->setTickInterval(x); |
157 | * Q_ASSERT(x >= producer->tickInterval() && |
158 | * x <= 2 * producer->tickInterval()); |
159 | * \endcode |
160 | * |
161 | * \see tick |
162 | */ |
163 | Q_PROPERTY(qint32 tickInterval READ tickInterval WRITE setTickInterval) |
164 | public: |
165 | /** |
166 | * Destroys the MediaObject. |
167 | */ |
168 | ~MediaObject() override; |
169 | |
170 | /** |
171 | * Get the current state. |
172 | * |
173 | * @return The state of the object. |
174 | * |
175 | * @see State |
176 | * \see stateChanged |
177 | */ |
178 | State state() const; |
179 | |
180 | /** |
181 | * Check whether the media data includes a video stream. |
182 | * |
183 | * \warning This information cannot be known immediately. It is best |
184 | * to also listen to the hasVideoChanged signal. |
185 | * |
186 | * \code |
187 | * connect(media, SIGNAL(hasVideoChanged(bool)), hasVideoChanged(bool)); |
188 | * media->setCurrentSource("somevideo.avi"); |
189 | * media->hasVideo(); // returns false; |
190 | * } |
191 | * |
192 | * void hasVideoChanged(bool b) |
193 | * { |
194 | * // b == true |
195 | * media->hasVideo(); // returns true; |
196 | * } |
197 | * \endcode |
198 | * |
199 | * \return \c true if the media contains video data. \c false |
200 | * otherwise. |
201 | * |
202 | * \see hasVideoChanged |
203 | */ |
204 | bool hasVideo() const; |
205 | |
206 | /** |
207 | * Check whether the current media may be seeked. |
208 | * |
209 | * \warning This information cannot be known immediately. It is best |
210 | * to also listen to the seekableChanged signal. |
211 | * |
212 | * \code |
213 | * connect(media, SIGNAL(seekableChanged(bool)), seekableChanged(bool)); |
214 | * media->setCurrentSource("somevideo.avi"); |
215 | * media->isSeekable(); // returns false; |
216 | * } |
217 | * |
218 | * void seekableChanged(bool b) |
219 | * { |
220 | * // b == true |
221 | * media->isSeekable(); // returns true; |
222 | * } |
223 | * \endcode |
224 | * |
225 | * \return \c true when the current media may be seeked. \c false |
226 | * otherwise. |
227 | * |
228 | * \see seekableChanged() |
229 | */ |
230 | bool isSeekable() const; |
231 | |
232 | /** |
233 | * \brief The time interval in milliseconds between two ticks. |
234 | * |
235 | * The %tick interval is the time that elapses between the emission |
236 | * of two tick signals. |
237 | * |
238 | * \returns the tick interval in milliseconds |
239 | */ |
240 | qint32 tickInterval() const; |
241 | |
242 | /** |
243 | * Returns the strings associated with the given \p key. |
244 | * |
245 | * Backends should use the keys specified in the Ogg Vorbis |
246 | * documentation: http://xiph.org/vorbis/doc/v-comment.html |
247 | * |
248 | * Therefore the following should work with every backend: |
249 | * |
250 | * A typical usage looks like this: |
251 | * \code |
252 | * setMetaArtist (media->metaData("ARTIST" )); |
253 | * setMetaAlbum (media->metaData("ALBUM" )); |
254 | * setMetaTitle (media->metaData("TITLE" )); |
255 | * setMetaDate (media->metaData("DATE" )); |
256 | * setMetaGenre (media->metaData("GENRE" )); |
257 | * setMetaTrack (media->metaData("TRACKNUMBER")); |
258 | * setMetaComment(media->metaData("DESCRIPTION")); |
259 | * \endcode |
260 | * |
261 | * For Audio CDs you can query |
262 | * \code |
263 | * metaData("MUSICBRAINZ_DISCID"); |
264 | * \endcode |
265 | * to get a DiscID hash that you can use with the MusicBrainz |
266 | * service: |
267 | * http://musicbrainz.org/doc/ClientHOWTO |
268 | */ |
269 | QStringList metaData(const QString &key) const; |
270 | |
271 | /** |
272 | * Returns the strings associated with the given \p key. |
273 | * |
274 | * Same as above except that the keys are defined in the |
275 | * Phonon::MetaData enum. |
276 | */ |
277 | QStringList metaData(Phonon::MetaData key) const; |
278 | |
279 | /** |
280 | * Returns all meta data. |
281 | */ |
282 | QMultiMap<QString, QString> metaData() const; |
283 | |
284 | /** |
285 | * Returns a human-readable description of the last error that occurred. |
286 | */ |
287 | QString errorString() const; |
288 | |
289 | /** |
290 | * Tells your program what to do about the error. |
291 | * |
292 | * \see Phonon::ErrorType |
293 | */ |
294 | ErrorType errorType() const; |
295 | |
296 | /** |
297 | * Returns the current media source. |
298 | * |
299 | * \see setCurrentSource |
300 | */ |
301 | MediaSource currentSource() const; |
302 | |
303 | /** |
304 | * Set the media source the MediaObject should use. |
305 | * |
306 | * \param source The MediaSource object to the media data. You can |
307 | * just as well use a QUrl or QString (for a local file) here. |
308 | * Setting an empty (invalid) source, will stop and remove the |
309 | * current source. |
310 | * |
311 | * \code |
312 | * QUrl url("http://www.example.com/music.ogg"); |
313 | * media->setCurrentSource(url); |
314 | * \endcode |
315 | * |
316 | * \see currentSource |
317 | */ |
318 | void setCurrentSource(const MediaSource &source); |
319 | |
320 | /** |
321 | * Returns the queued media sources. This list does not include |
322 | * the current source (returned by currentSource). |
323 | */ |
324 | QList<MediaSource> queue() const; |
325 | |
326 | /** |
327 | * Set the MediaSources to play when the current media has finished. |
328 | * |
329 | * This function will overwrite the current queue. |
330 | * |
331 | * \see clearQueue |
332 | * \see enqueue |
333 | */ |
334 | void setQueue(const QList<MediaSource> &sources); |
335 | |
336 | /** |
337 | * Set the MediaSources to play when the current media has finished. |
338 | * |
339 | * This function overwrites the current queue. |
340 | * |
341 | * \see clearQueue |
342 | * \see enqueue |
343 | */ |
344 | void setQueue(const QList<QUrl> &urls); |
345 | |
346 | /** |
347 | * Appends one source to the queue. Use this function to provide |
348 | * the next source just in time after the aboutToFinish signal was |
349 | * emitted. |
350 | * |
351 | * \see aboutToFinish |
352 | * \see setQueue |
353 | * \see clearQueue |
354 | */ |
355 | void enqueue(const MediaSource &source); |
356 | |
357 | /** |
358 | * Appends multiple sources to the queue. |
359 | * |
360 | * \see setQueue |
361 | * \see clearQueue |
362 | */ |
363 | void enqueue(const QList<MediaSource> &sources); |
364 | |
365 | /** |
366 | * Appends multiple sources to the queue. |
367 | * |
368 | * \see setQueue |
369 | * \see clearQueue |
370 | */ |
371 | void enqueue(const QList<QUrl> &urls); |
372 | |
373 | /** |
374 | * Clears the queue of sources. |
375 | */ |
376 | void clearQueue(); |
377 | |
378 | /** |
379 | * Get the current time (in milliseconds) of the file currently being played. |
380 | * |
381 | * \return The current time in milliseconds. |
382 | * |
383 | * \see tick |
384 | */ |
385 | qint64 currentTime() const; |
386 | |
387 | /** |
388 | * Get the total time (in milliseconds) of the file currently being played. |
389 | * |
390 | * \return The total time in milliseconds. |
391 | * |
392 | * \note The total time may change throughout playback as more accurate |
393 | * calculations become available, so it is recommended to connect |
394 | * and use the totalTimeChanged signal whenever possible unless |
395 | * best precision is not of importance. |
396 | * |
397 | * \warning The total time is undefined until the MediaObject entered |
398 | * the PlayingState. A valid total time is always indicated by |
399 | * emission of the totalTimeChanged signal. |
400 | * \see totalTimeChanged |
401 | */ |
402 | qint64 totalTime() const; |
403 | |
404 | /** |
405 | * Get the remaining time (in milliseconds) of the file currently being played. |
406 | * |
407 | * \return The remaining time in milliseconds. |
408 | */ |
409 | qint64 remainingTime() const; |
410 | |
411 | qint32 prefinishMark() const; |
412 | void setPrefinishMark(qint32 msecToEnd); |
413 | |
414 | qint32 transitionTime() const; |
415 | void setTransitionTime(qint32 msec); |
416 | |
417 | public Q_SLOTS: |
418 | |
419 | /** |
420 | * Sets the tick interval in milliseconds. |
421 | * |
422 | * \param newTickInterval the new tick interval in milliseconds. |
423 | * |
424 | * \see tickInterval |
425 | */ |
426 | void setTickInterval(qint32 newTickInterval); |
427 | |
428 | /** |
429 | * Requests playback of the media data to start. Playback only |
430 | * starts when stateChanged() signals that it goes into PlayingState, |
431 | * though. |
432 | * |
433 | * \par Possible states right after this call: |
434 | * \li BufferingState |
435 | * \li PlayingState |
436 | * \li ErrorState |
437 | */ |
438 | void play(); |
439 | |
440 | /** |
441 | * Requests playback to pause. If it was paused before nothing changes. |
442 | * If the media cannot be paused, some backends will internally call |
443 | * stop instead of pause. |
444 | * |
445 | * \par Possible states right after this call: |
446 | * \li PlayingState |
447 | * \li PausedState |
448 | * \li StoppedState |
449 | * \li ErrorState |
450 | */ |
451 | void pause(); |
452 | |
453 | /** |
454 | * Requests playback to stop. If it was stopped before nothing changes. |
455 | * |
456 | * \par Possible states right after this call: |
457 | * \li the state it was in before (e.g. PlayingState) |
458 | * \li StoppedState |
459 | * \li ErrorState |
460 | */ |
461 | void stop(); |
462 | |
463 | /** |
464 | * Requests a seek to the time indicated. |
465 | * |
466 | * You can only seek if state() == PlayingState, BufferingState or PausedState. |
467 | * |
468 | * The call is asynchronous, so currentTime can still be the old |
469 | * value right after this method was called. If all you need is a |
470 | * slider that shows the current position and allows the user to |
471 | * seek use the class SeekSlider. |
472 | * |
473 | * @param time The time in milliseconds where to continue playing. |
474 | * |
475 | * \par Possible states right after this call: |
476 | * \li BufferingState |
477 | * \li PlayingState |
478 | * \li ErrorState |
479 | * |
480 | * \see SeekSlider |
481 | */ |
482 | void seek(qint64 time); |
483 | |
484 | /** |
485 | * Stops and removes all playing and enqueued media sources. |
486 | * |
487 | * \see setCurrentSource |
488 | */ |
489 | void clear(); |
490 | |
491 | Q_SIGNALS: |
492 | /** |
493 | * Emitted when the state of the MediaObject has changed. |
494 | * |
495 | * @param newstate The state the Player is in now. |
496 | * @param oldstate The state the Player was in before. |
497 | */ |
498 | void stateChanged(Phonon::State newstate, Phonon::State oldstate); |
499 | |
500 | /** |
501 | * This signal gets emitted every tickInterval milliseconds. |
502 | * |
503 | * @param time The position of the media file in milliseconds. |
504 | * |
505 | * @see setTickInterval, tickInterval |
506 | */ |
507 | void tick(qint64 time); |
508 | |
509 | /** |
510 | * This signal is emitted whenever the audio/video data that is |
511 | * being played is associated with new meta data. E.g. for radio |
512 | * streams this happens when the next song is played. |
513 | * |
514 | * You can get the new meta data with the metaData methods. |
515 | */ |
516 | void metaDataChanged(); |
517 | |
518 | /** |
519 | * Emitted whenever the return value of isSeekable() changes. |
520 | * |
521 | * Normally you'll check isSeekable() first and then let this signal |
522 | * tell you whether seeking is possible now or not. That way you |
523 | * don't have to poll isSeekable(). |
524 | * |
525 | * \param isSeekable \p true if the stream is seekable (i.e. calling |
526 | * seek() works) |
527 | * \p false if the stream is not seekable (i.e. |
528 | * all calls to seek() will be ignored) |
529 | */ |
530 | void seekableChanged(bool isSeekable); |
531 | |
532 | /** |
533 | * Emitted whenever the return value of hasVideo() changes. |
534 | * |
535 | * Normally you'll check hasVideo() first and then let this signal |
536 | * tell you whether video is available now or not. That way you |
537 | * don't have to poll hasVideo(). |
538 | * |
539 | * \param hasVideo \p true The stream contains video and adding a |
540 | * VideoWidget will show a video. |
541 | * \p false There is no video data in the stream and |
542 | * adding a VideoWidget will show an empty (black) |
543 | * VideoWidget. |
544 | */ |
545 | #ifndef QT_NO_PHONON_VIDEO |
546 | void hasVideoChanged(bool hasVideo); |
547 | #endif //QT_NO_PHONON_VIDEO |
548 | |
549 | /** |
550 | * Tells about the status of the buffer. |
551 | * |
552 | * You can use this signal to show a progress bar to the user when |
553 | * in BufferingState: |
554 | * |
555 | * \code |
556 | * progressBar->setRange(0, 100); // this is the default |
557 | * connect(media, SIGNAL(bufferStatus(int)), progressBar, SLOT(setValue(int))); |
558 | * \endcode |
559 | * |
560 | * \param percentFilled A number between 0 and 100 telling you how |
561 | * much the buffer is filled. |
562 | */ // other names: bufferingProgress |
563 | void bufferStatus(int percentFilled); |
564 | |
565 | /** |
566 | * Emitted when the object has finished playback. |
567 | * It is not emitted if you call stop(), pause() or |
568 | * load(), but only on end-of-queue or a critical error. |
569 | * |
570 | * \warning This signal is not emitted when the current source has |
571 | * finished and there's another source in the queue. It is only |
572 | * emitted when the queue is empty. |
573 | * |
574 | * \see currentSourceChanged |
575 | * \see aboutToFinish |
576 | * \see prefinishMarkReached |
577 | */ |
578 | void finished(); |
579 | |
580 | /** |
581 | * Emitted when the MediaObject makes a transition to the next |
582 | * MediaSource in the queue(). |
583 | * |
584 | * In other words, it is emitted when an individual MediaSource is |
585 | * finished. |
586 | * |
587 | * \param newSource The source that starts to play at the time the |
588 | * signal is emitted. |
589 | */ |
590 | void currentSourceChanged(const Phonon::MediaSource &newSource); |
591 | |
592 | /** |
593 | * Emitted before the playback of the whole queue stops. When this |
594 | * signal is emitted you still have time to provide the next |
595 | * MediaSource (using enqueue()) so that playback continues. |
596 | * |
597 | * This signal can be used to provide the next MediaSource just in |
598 | * time for the transition still to work. |
599 | * |
600 | * \see enqueue |
601 | */ |
602 | void aboutToFinish(); |
603 | |
604 | /** |
605 | * Emitted when there are only \p msecToEnd milliseconds left |
606 | * for playback. |
607 | * |
608 | * \param msecToEnd The remaining time until the playback queue finishes. |
609 | * |
610 | * \warning This signal is not emitted when there is another source in the queue. |
611 | * It is only emitted when the queue is empty. |
612 | * |
613 | * \see setPrefinishMark |
614 | * \see prefinishMark |
615 | * \see aboutToFinish |
616 | * \see finished |
617 | */ |
618 | void prefinishMarkReached(qint32 msecToEnd); |
619 | |
620 | /** |
621 | * This signal is emitted as soon as the total time of the media file is |
622 | * known or has changed. For most non-local media data the total |
623 | * time of the media can only be known after some time. Initially the |
624 | * totalTime function can not return useful information. You have |
625 | * to wait for this signal to know the real total time. |
626 | * |
627 | * This signal may appear at any given point after a MediaSource was set. |
628 | * Namely in the LoadingState, BufferingState, PlayingState or PausedState. |
629 | * |
630 | * \note When changing the currentSource there is no signal emission until |
631 | * a reasonable value for the new source has been calculated. |
632 | * |
633 | * \param newTotalTime The length of the media file in milliseconds. |
634 | * |
635 | * \see totalTime |
636 | */ |
637 | void totalTimeChanged(qint64 newTotalTime); |
638 | |
639 | protected: |
640 | //MediaObject(Phonon::MediaObjectPrivate &dd, QObject *parent); |
641 | |
642 | private: |
643 | Q_PRIVATE_SLOT(k_func(), void _k_resumePlay()) |
644 | Q_PRIVATE_SLOT(k_func(), void _k_resumePause()) |
645 | Q_PRIVATE_SLOT(k_func(), void _k_metaDataChanged(const QMultiMap<QString, QString> &)) |
646 | #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM |
647 | Q_PRIVATE_SLOT(k_func(), void _k_stateChanged(Phonon::State, Phonon::State)) |
648 | #endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM |
649 | Q_PRIVATE_SLOT(k_func(), void _k_aboutToFinish()) |
650 | Q_PRIVATE_SLOT(k_func(), void _k_currentSourceChanged(const MediaSource &)) |
651 | Q_PRIVATE_SLOT(k_func(), void _k_stateChanged(Phonon::State, Phonon::State)) |
652 | }; |
653 | |
654 | /** |
655 | * Convenience function to create a MediaObject and AudioOutput connected by |
656 | * a path. |
657 | */ |
658 | PHONON_EXPORT MediaObject *createPlayer(Phonon::Category category, const MediaSource &source = MediaSource()); |
659 | } //namespace Phonon |
660 | |
661 | |
662 | // vim: sw=4 ts=4 tw=80 |
663 | #endif // Phonon_MEDIAOBJECT_H |
664 | |