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