1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qmediaplayer.h"
41 #include "qvideosurfaces_p.h"
42 #include "qvideosurfaceoutput_p.h"
43 
44 #include "qmediaobject_p.h"
45 #include <qmediaservice.h>
46 #include <qmediaplayercontrol.h>
47 #include <qmediaserviceprovider_p.h>
48 #include <qmediaplaylist.h>
49 #include <qmediaplaylistcontrol_p.h>
50 #include <qmediaplaylistsourcecontrol_p.h>
51 #include <qmedianetworkaccesscontrol.h>
52 #include <qaudiorolecontrol.h>
53 #include <qcustomaudiorolecontrol.h>
54 
55 #include <QtCore/qcoreevent.h>
56 #include <QtCore/qmetaobject.h>
57 #include <QtCore/qtimer.h>
58 #include <QtCore/qdebug.h>
59 #include <QtCore/qpointer.h>
60 #include <QtCore/qfileinfo.h>
61 #include <QtCore/qtemporaryfile.h>
62 #include <QDir>
63 
64 QT_BEGIN_NAMESPACE
65 
66 /*!
67     \class QMediaPlayer
68     \brief The QMediaPlayer class allows the playing of a media source.
69     \inmodule QtMultimedia
70     \ingroup multimedia
71     \ingroup multimedia_playback
72 
73     The QMediaPlayer class is a high level media playback class. It can be used
74     to playback such content as songs, movies and internet radio. The content
75     to playback is specified as a QMediaContent object, which can be thought of as a
76     main or canonical URL with additional information attached. When provided
77     with a QMediaContent playback may be able to commence.
78 
79     \snippet multimedia-snippets/media.cpp Player
80 
81     QVideoWidget can be used with QMediaPlayer for video rendering and QMediaPlaylist
82     for accessing playlist functionality.
83 
84     \snippet multimedia-snippets/media.cpp Movie playlist
85 
86     Since QMediaPlayer is a QMediaObject, you can use several of the QMediaObject
87     functions for things like:
88 
89     \list
90     \li Accessing the currently playing media's metadata (\l {QMediaObject::metaData()} and \l {QMediaMetaData}{predefined meta-data keys})
91     \li Checking to see if the media playback service is currently available (\l {QMediaObject::availability()})
92     \endlist
93 
94     \sa QMediaObject, QMediaService, QVideoWidget, QMediaPlaylist
95 */
96 
qRegisterMediaPlayerMetaTypes()97 static void qRegisterMediaPlayerMetaTypes()
98 {
99     qRegisterMetaType<QMediaPlayer::State>("QMediaPlayer::State");
100     qRegisterMetaType<QMediaPlayer::MediaStatus>("QMediaPlayer::MediaStatus");
101     qRegisterMetaType<QMediaPlayer::Error>("QMediaPlayer::Error");
102 }
103 
104 Q_CONSTRUCTOR_FUNCTION(qRegisterMediaPlayerMetaTypes)
105 
106 #define MAX_NESTED_PLAYLISTS 16
107 
108 class QMediaPlayerPrivate : public QMediaObjectPrivate
109 {
110     Q_DECLARE_NON_CONST_PUBLIC(QMediaPlayer)
111 
112 public:
QMediaPlayerPrivate()113     QMediaPlayerPrivate()
114         : provider(nullptr)
115         , control(nullptr)
116         , audioRoleControl(nullptr)
117         , customAudioRoleControl(nullptr)
118         , playlist(nullptr)
119 #ifndef QT_NO_BEARERMANAGEMENT
120         , networkAccessControl(nullptr)
121 #endif
122         , state(QMediaPlayer::StoppedState)
123         , status(QMediaPlayer::UnknownMediaStatus)
124         , error(QMediaPlayer::NoError)
125         , ignoreNextStatusChange(-1)
126         , nestedPlaylists(0)
127         , hasStreamPlaybackFeature(false)
128     {}
129 
130     QMediaServiceProvider *provider;
131     QMediaPlayerControl* control;
132     QAudioRoleControl *audioRoleControl;
133     QCustomAudioRoleControl *customAudioRoleControl;
134     QString errorString;
135 
136     QPointer<QObject> videoOutput;
137     QMediaPlaylist *playlist;
138 #ifndef QT_NO_BEARERMANAGEMENT
139 QT_WARNING_PUSH
140 QT_WARNING_DISABLE_DEPRECATED
141     QMediaNetworkAccessControl *networkAccessControl;
142 QT_WARNING_POP
143 #endif
144     QVideoSurfaceOutput surfaceOutput;
145     QMediaContent qrcMedia;
146     QScopedPointer<QFile> qrcFile;
147 
148     QMediaContent rootMedia;
149     QMediaContent pendingPlaylist;
150     QMediaPlayer::State state;
151     QMediaPlayer::MediaStatus status;
152     QMediaPlayer::Error error;
153     int ignoreNextStatusChange;
154     int nestedPlaylists;
155     bool hasStreamPlaybackFeature;
156 
157     QMediaPlaylist *parentPlaylist(QMediaPlaylist *pls);
158     bool isInChain(const QUrl &url);
159 
160     void setMedia(const QMediaContent &media, QIODevice *stream = nullptr);
161 
162     void setPlaylist(QMediaPlaylist *playlist);
163     void setPlaylistMedia();
164     void loadPlaylist();
165     void disconnectPlaylist();
166     void connectPlaylist();
167 
168     void _q_stateChanged(QMediaPlayer::State state);
169     void _q_mediaStatusChanged(QMediaPlayer::MediaStatus status);
170     void _q_error(int error, const QString &errorString);
171     void _q_updateMedia(const QMediaContent&);
172     void _q_playlistDestroyed();
173     void _q_handleMediaChanged(const QMediaContent&);
174     void _q_handlePlaylistLoaded();
175     void _q_handlePlaylistLoadFailed();
176 };
177 
parentPlaylist(QMediaPlaylist * pls)178 QMediaPlaylist *QMediaPlayerPrivate::parentPlaylist(QMediaPlaylist *pls)
179 {
180     // This function finds a parent playlist for an item in the active chain of playlists.
181     // Every item in the chain comes from currentMedia() of its parent.
182     // We don't need to travers the whole tree of playlists,
183     // but only the subtree of active ones.
184     for (QMediaPlaylist *current = rootMedia.playlist(); current && current != pls; current = current->currentMedia().playlist())
185         if (current->currentMedia().playlist() == pls)
186             return current;
187     return nullptr;
188 }
189 
isInChain(const QUrl & url)190 bool QMediaPlayerPrivate::isInChain(const QUrl &url)
191 {
192     // Check whether a URL is already in the chain of playlists.
193     // Also see a comment in parentPlaylist().
194     for (QMediaPlaylist *current = rootMedia.playlist(); current && current != playlist; current = current->currentMedia().playlist())
195         if (current->currentMedia().request().url() == url) {
196             return true;
197         }
198     return false;
199 }
200 
_q_stateChanged(QMediaPlayer::State ps)201 void QMediaPlayerPrivate::_q_stateChanged(QMediaPlayer::State ps)
202 {
203     Q_Q(QMediaPlayer);
204 
205     // Backend switches into stopped state every time new media is about to be loaded.
206     // If media player has a playlist loaded make sure player doesn' stop.
207     if (playlist && playlist->currentIndex() != -1 && ps != state && ps == QMediaPlayer::StoppedState) {
208         if (control->mediaStatus() == QMediaPlayer::EndOfMedia ||
209                 control->mediaStatus() == QMediaPlayer::InvalidMedia) {
210             // if media player is not stopped, and
211             // we have finished playback for the current media,
212             // advance to the next item in the playlist
213             Q_ASSERT(state != QMediaPlayer::StoppedState);
214             playlist->next();
215             return;
216         } else if (control->mediaStatus() == QMediaPlayer::LoadingMedia) {
217             return;
218         }
219     }
220 
221     if (ps != state) {
222         state = ps;
223 
224         if (ps == QMediaPlayer::PlayingState)
225             q->addPropertyWatch("position");
226         else
227             q->removePropertyWatch("position");
228 
229         emit q->stateChanged(ps);
230     }
231 }
232 
_q_mediaStatusChanged(QMediaPlayer::MediaStatus s)233 void QMediaPlayerPrivate::_q_mediaStatusChanged(QMediaPlayer::MediaStatus s)
234 {
235     Q_Q(QMediaPlayer);
236 
237     if (int(s) == ignoreNextStatusChange) {
238         ignoreNextStatusChange = -1;
239         return;
240     }
241 
242     if (s != status) {
243         status = s;
244 
245         switch (s) {
246         case QMediaPlayer::StalledMedia:
247         case QMediaPlayer::BufferingMedia:
248             q->addPropertyWatch("bufferStatus");
249             break;
250         default:
251             q->removePropertyWatch("bufferStatus");
252             break;
253         }
254 
255         emit q->mediaStatusChanged(s);
256     }
257 }
258 
_q_error(int error,const QString & errorString)259 void QMediaPlayerPrivate::_q_error(int error, const QString &errorString)
260 {
261     Q_Q(QMediaPlayer);
262 
263     if (error == int(QMediaPlayer::MediaIsPlaylist)) {
264         loadPlaylist();
265     } else {
266         this->error = QMediaPlayer::Error(error);
267         this->errorString = errorString;
268         emit q->error(this->error);
269 
270         if (playlist)
271             playlist->next();
272     }
273 }
274 
_q_updateMedia(const QMediaContent & media)275 void QMediaPlayerPrivate::_q_updateMedia(const QMediaContent &media)
276 {
277     Q_Q(QMediaPlayer);
278 
279     if (!control)
280         return;
281 
282     // check if the current playlist is a top-level playlist
283     Q_ASSERT(playlist);
284     if (media.isNull() && playlist != rootMedia.playlist()) {
285         // switch back to parent playlist
286         QMediaPlaylist *pls = parentPlaylist(playlist);
287         Q_ASSERT(pls);
288         disconnectPlaylist();
289         playlist = pls;
290         connectPlaylist();
291 
292         Q_ASSERT(!pendingPlaylist.playlist());
293         nestedPlaylists--;
294         Q_ASSERT(nestedPlaylists >= 0);
295 
296         playlist->next();
297         return;
298     }
299 
300     if (media.playlist()) {
301         if (nestedPlaylists < MAX_NESTED_PLAYLISTS) {
302             nestedPlaylists++;
303             Q_ASSERT(!pendingPlaylist.playlist());
304 
305             // disconnect current playlist
306             disconnectPlaylist();
307             // new playlist signals are connected
308             // in the call to setPlaylist() in _q_handlePlaylistLoaded()
309             playlist = media.playlist();
310             emit q->currentMediaChanged(media);
311             _q_handlePlaylistLoaded();
312             return;
313         } else if (playlist) {
314             playlist->next();
315         }
316         return;
317     }
318 
319     const QMediaPlayer::State currentState = state;
320 
321     setMedia(media, nullptr);
322 
323     if (!media.isNull()) {
324         switch (currentState) {
325         case QMediaPlayer::PlayingState:
326             control->play();
327             break;
328         case QMediaPlayer::PausedState:
329             control->pause();
330             break;
331         default:
332             break;
333         }
334     }
335 
336     _q_stateChanged(control->state());
337 }
338 
_q_playlistDestroyed()339 void QMediaPlayerPrivate::_q_playlistDestroyed()
340 {
341     playlist = nullptr;
342     setMedia(QMediaContent(), nullptr);
343 }
344 
setMedia(const QMediaContent & media,QIODevice * stream)345 void QMediaPlayerPrivate::setMedia(const QMediaContent &media, QIODevice *stream)
346 {
347     Q_Q(QMediaPlayer);
348 
349     if (!control)
350         return;
351 
352     QScopedPointer<QFile> file;
353 
354     // Backends can't play qrc files directly.
355     // If the backend supports StreamPlayback, we pass a QFile for that resource.
356     // If it doesn't, we copy the data to a temporary file and pass its path.
357     if (!media.isNull() && !stream && media.request().url().scheme() == QLatin1String("qrc")) {
358         qrcMedia = media;
359 
360         file.reset(new QFile(QLatin1Char(':') + media.request().url().path()));
361         if (!file->open(QFile::ReadOnly)) {
362             QMetaObject::invokeMethod(q, "_q_error", Qt::QueuedConnection,
363                                       Q_ARG(int, QMediaPlayer::ResourceError),
364                                       Q_ARG(QString, QMediaPlayer::tr("Attempting to play invalid Qt resource")));
365             QMetaObject::invokeMethod(q, "_q_mediaStatusChanged", Qt::QueuedConnection,
366                                       Q_ARG(QMediaPlayer::MediaStatus, QMediaPlayer::InvalidMedia));
367             file.reset();
368             // Ignore the next NoMedia status change, we just want to clear the current media
369             // on the backend side since we can't load the new one and we want to be in the
370             // InvalidMedia status.
371             ignoreNextStatusChange = QMediaPlayer::NoMedia;
372             control->setMedia(QMediaContent(), nullptr);
373 
374         } else if (hasStreamPlaybackFeature) {
375             control->setMedia(media, file.data());
376         } else {
377 #if QT_CONFIG(temporaryfile)
378 #if defined(Q_OS_ANDROID)
379             QString tempFileName = QDir::tempPath() + media.request().url().path();
380             QDir().mkpath(QFileInfo(tempFileName).path());
381             QTemporaryFile *tempFile = QTemporaryFile::createNativeFile(*file);
382             if (!tempFile->rename(tempFileName))
383                 qWarning() << "Could not rename temporary file to:" << tempFileName;
384 #else
385             QTemporaryFile *tempFile = new QTemporaryFile;
386 
387             // Preserve original file extension, some backends might not load the file if it doesn't
388             // have an extension.
389             const QString suffix = QFileInfo(*file).suffix();
390             if (!suffix.isEmpty())
391                 tempFile->setFileTemplate(tempFile->fileTemplate() + QLatin1Char('.') + suffix);
392 
393             // Copy the qrc data into the temporary file
394             tempFile->open();
395             char buffer[4096];
396             while (true) {
397                 qint64 len = file->read(buffer, sizeof(buffer));
398                 if (len < 1)
399                     break;
400                 tempFile->write(buffer, len);
401             }
402             tempFile->close();
403 #endif
404             file.reset(tempFile);
405             control->setMedia(QMediaContent(QUrl::fromLocalFile(file->fileName())), nullptr);
406 #else
407             qWarning("Qt was built with -no-feature-temporaryfile: playback from resource file is not supported!");
408 #endif
409         }
410     } else {
411         qrcMedia = QMediaContent();
412         control->setMedia(media, stream);
413     }
414 
415     qrcFile.swap(file); // Cleans up any previous file
416 }
417 
_q_handleMediaChanged(const QMediaContent & media)418 void QMediaPlayerPrivate::_q_handleMediaChanged(const QMediaContent &media)
419 {
420     Q_Q(QMediaPlayer);
421 
422     emit q->currentMediaChanged(qrcMedia.isNull() ? media : qrcMedia);
423 }
424 
setPlaylist(QMediaPlaylist * pls)425 void QMediaPlayerPrivate::setPlaylist(QMediaPlaylist *pls)
426 {
427     disconnectPlaylist();
428     playlist = pls;
429 
430     setPlaylistMedia();
431 }
432 
setPlaylistMedia()433 void QMediaPlayerPrivate::setPlaylistMedia()
434 {
435     // This function loads current playlist media into backend.
436     // If current media is a playlist, the function recursively
437     // loads media from the playlist.
438     // It also makes sure the correct playlist signals are connected.
439     Q_Q(QMediaPlayer);
440 
441     if (playlist) {
442         connectPlaylist();
443         if (playlist->currentMedia().playlist()) {
444             if (nestedPlaylists < MAX_NESTED_PLAYLISTS) {
445                 emit q->currentMediaChanged(playlist->currentMedia());
446                 // rewind nested playlist to start
447                 playlist->currentMedia().playlist()->setCurrentIndex(0);
448                 nestedPlaylists++;
449                 setPlaylist(playlist->currentMedia().playlist());
450             } else {
451                 playlist->next();
452             }
453             return;
454         } else {
455             // If we've just switched to a new playlist,
456             // then last emitted currentMediaChanged was a playlist.
457             // Make sure we emit currentMediaChanged if new playlist has
458             // the same media as the previous one:
459             // sample.m3u
460             //      test.wav     -- processed by backend
461             //      nested.m3u   -- processed by frontend
462             //          test.wav -- processed by backend,
463             //                      media is not changed,
464             //                      frontend needs to emit currentMediaChanged
465             bool isSameMedia = (q->currentMedia() == playlist->currentMedia());
466             setMedia(playlist->currentMedia(), nullptr);
467             if (isSameMedia) {
468                 emit q->currentMediaChanged(q->currentMedia());
469             }
470         }
471     } else {
472         setMedia(QMediaContent(), nullptr);
473     }
474 }
475 
loadPlaylist()476 void QMediaPlayerPrivate::loadPlaylist()
477 {
478     Q_Q(QMediaPlayer);
479     Q_ASSERT(pendingPlaylist.isNull());
480 
481     // Do not load a playlist if there are more than MAX_NESTED_PLAYLISTS in the chain already,
482     // or if the playlist URL is already in the chain, i.e. do not allow recursive playlists and loops.
483     if (nestedPlaylists < MAX_NESTED_PLAYLISTS
484         && !q->currentMedia().request().url().isEmpty()
485         && !isInChain(q->currentMedia().request().url()))
486     {
487         pendingPlaylist = QMediaContent(new QMediaPlaylist, q->currentMedia().request().url(), true);
488         QObject::connect(pendingPlaylist.playlist(), SIGNAL(loaded()), q, SLOT(_q_handlePlaylistLoaded()));
489         QObject::connect(pendingPlaylist.playlist(), SIGNAL(loadFailed()), q, SLOT(_q_handlePlaylistLoadFailed()));
490         pendingPlaylist.playlist()->load(pendingPlaylist.request());
491     } else if (playlist) {
492         playlist->next();
493     }
494 }
495 
disconnectPlaylist()496 void QMediaPlayerPrivate::disconnectPlaylist()
497 {
498     Q_Q(QMediaPlayer);
499     if (playlist) {
500         QObject::disconnect(playlist, SIGNAL(currentMediaChanged(QMediaContent)),
501                             q, SLOT(_q_updateMedia(QMediaContent)));
502         QObject::disconnect(playlist, SIGNAL(destroyed()), q, SLOT(_q_playlistDestroyed()));
503         q->unbind(playlist);
504     }
505 }
506 
connectPlaylist()507 void QMediaPlayerPrivate::connectPlaylist()
508 {
509     Q_Q(QMediaPlayer);
510     if (playlist) {
511         q->bind(playlist);
512         QObject::connect(playlist, SIGNAL(currentMediaChanged(QMediaContent)),
513                          q, SLOT(_q_updateMedia(QMediaContent)));
514         QObject::connect(playlist, SIGNAL(destroyed()), q, SLOT(_q_playlistDestroyed()));
515     }
516 }
517 
_q_handlePlaylistLoaded()518 void QMediaPlayerPrivate::_q_handlePlaylistLoaded()
519 {
520     Q_Q(QMediaPlayer);
521 
522     if (pendingPlaylist.playlist()) {
523         Q_ASSERT(!q->currentMedia().playlist());
524         // if there is an active playlist
525         if (playlist) {
526             Q_ASSERT(playlist->currentIndex() >= 0);
527             disconnectPlaylist();
528             playlist->insertMedia(playlist->currentIndex() + 1, pendingPlaylist);
529             playlist->removeMedia(playlist->currentIndex());
530             nestedPlaylists++;
531         } else {
532             Q_ASSERT(!rootMedia.playlist());
533             rootMedia = pendingPlaylist;
534             emit q->mediaChanged(rootMedia);
535         }
536 
537         playlist = pendingPlaylist.playlist();
538         emit q->currentMediaChanged(pendingPlaylist);
539     }
540     pendingPlaylist = QMediaContent();
541 
542     playlist->next();
543     setPlaylistMedia();
544 
545     switch (state) {
546     case QMediaPlayer::PausedState:
547         control->pause();
548         break;
549     case QMediaPlayer::PlayingState:
550         control->play();
551         break;
552     case QMediaPlayer::StoppedState:
553         break;
554     }
555 }
556 
_q_handlePlaylistLoadFailed()557 void QMediaPlayerPrivate::_q_handlePlaylistLoadFailed()
558 {
559     pendingPlaylist = QMediaContent();
560 
561     if (!control)
562         return;
563 
564     if (playlist)
565         playlist->next();
566     else
567         setMedia(QMediaContent(), nullptr);
568 }
569 
playerService(QMediaPlayer::Flags flags)570 static QMediaService *playerService(QMediaPlayer::Flags flags)
571 {
572     QMediaServiceProvider *provider = QMediaServiceProvider::defaultServiceProvider();
573     if (flags) {
574         QMediaServiceProviderHint::Features features;
575         if (flags & QMediaPlayer::LowLatency)
576             features |= QMediaServiceProviderHint::LowLatencyPlayback;
577 
578         if (flags & QMediaPlayer::StreamPlayback)
579             features |= QMediaServiceProviderHint::StreamPlayback;
580 
581         if (flags & QMediaPlayer::VideoSurface)
582             features |= QMediaServiceProviderHint::VideoSurface;
583 
584         return provider->requestService(Q_MEDIASERVICE_MEDIAPLAYER,
585                                         QMediaServiceProviderHint(features));
586     }
587 
588     return provider->requestService(Q_MEDIASERVICE_MEDIAPLAYER);
589 }
590 
591 
592 /*!
593     Construct a QMediaPlayer instance
594     parented to \a parent and with \a flags.
595 */
596 
QMediaPlayer(QObject * parent,QMediaPlayer::Flags flags)597 QMediaPlayer::QMediaPlayer(QObject *parent, QMediaPlayer::Flags flags):
598     QMediaObject(*new QMediaPlayerPrivate,
599                  parent,
600                  playerService(flags))
601 {
602     Q_D(QMediaPlayer);
603 
604     d->provider = QMediaServiceProvider::defaultServiceProvider();
605     if (d->service == nullptr) {
606         d->error = ServiceMissingError;
607     } else {
608         d->control = qobject_cast<QMediaPlayerControl*>(d->service->requestControl(QMediaPlayerControl_iid));
609 #ifndef QT_NO_BEARERMANAGEMENT
610 QT_WARNING_PUSH
611 QT_WARNING_DISABLE_DEPRECATED
612         d->networkAccessControl = qobject_cast<QMediaNetworkAccessControl*>(d->service->requestControl(QMediaNetworkAccessControl_iid));
613 QT_WARNING_POP
614 #endif
615         if (d->control != nullptr) {
616             connect(d->control, SIGNAL(mediaChanged(QMediaContent)), SLOT(_q_handleMediaChanged(QMediaContent)));
617             connect(d->control, SIGNAL(stateChanged(QMediaPlayer::State)), SLOT(_q_stateChanged(QMediaPlayer::State)));
618             connect(d->control, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),
619                     SLOT(_q_mediaStatusChanged(QMediaPlayer::MediaStatus)));
620             connect(d->control, SIGNAL(error(int,QString)), SLOT(_q_error(int,QString)));
621 
622             connect(d->control, &QMediaPlayerControl::durationChanged, this, &QMediaPlayer::durationChanged);
623             connect(d->control, &QMediaPlayerControl::positionChanged, this, &QMediaPlayer::positionChanged);
624             connect(d->control, &QMediaPlayerControl::audioAvailableChanged, this, &QMediaPlayer::audioAvailableChanged);
625             connect(d->control, &QMediaPlayerControl::videoAvailableChanged, this, &QMediaPlayer::videoAvailableChanged);
626             connect(d->control, &QMediaPlayerControl::volumeChanged, this, &QMediaPlayer::volumeChanged);
627             connect(d->control, &QMediaPlayerControl::mutedChanged, this, &QMediaPlayer::mutedChanged);
628             connect(d->control, &QMediaPlayerControl::seekableChanged, this, &QMediaPlayer::seekableChanged);
629             connect(d->control, &QMediaPlayerControl::playbackRateChanged, this, &QMediaPlayer::playbackRateChanged);
630             connect(d->control, &QMediaPlayerControl::bufferStatusChanged, this, &QMediaPlayer::bufferStatusChanged);
631 
632             d->state = d->control->state();
633             d->status = d->control->mediaStatus();
634 
635             if (d->state == PlayingState)
636                 addPropertyWatch("position");
637 
638             if (d->status == StalledMedia || d->status == BufferingMedia)
639                 addPropertyWatch("bufferStatus");
640 
641             d->hasStreamPlaybackFeature = d->provider->supportedFeatures(d->service).testFlag(QMediaServiceProviderHint::StreamPlayback);
642 
643             d->audioRoleControl = qobject_cast<QAudioRoleControl*>(d->service->requestControl(QAudioRoleControl_iid));
644             if (d->audioRoleControl) {
645                 connect(d->audioRoleControl, &QAudioRoleControl::audioRoleChanged,
646                         this, &QMediaPlayer::audioRoleChanged);
647 
648                 d->customAudioRoleControl = qobject_cast<QCustomAudioRoleControl *>(
649                         d->service->requestControl(QCustomAudioRoleControl_iid));
650                 if (d->customAudioRoleControl) {
651                     connect(d->customAudioRoleControl,
652                             &QCustomAudioRoleControl::customAudioRoleChanged,
653                             this,
654                             &QMediaPlayer::customAudioRoleChanged);
655                 }
656             }
657         }
658 #ifndef QT_NO_BEARERMANAGEMENT
659         if (d->networkAccessControl != nullptr) {
660 QT_WARNING_PUSH
661 QT_WARNING_DISABLE_DEPRECATED
662             connect(d->networkAccessControl, &QMediaNetworkAccessControl::configurationChanged,
663                     this, &QMediaPlayer::networkConfigurationChanged);
664 QT_WARNING_POP
665         }
666 #endif
667     }
668 }
669 
670 
671 /*!
672     Destroys the player object.
673 */
674 
~QMediaPlayer()675 QMediaPlayer::~QMediaPlayer()
676 {
677     Q_D(QMediaPlayer);
678 
679     d->disconnectPlaylist();
680     // Disconnect everything to prevent notifying
681     // when a receiver is already destroyed.
682     disconnect();
683 
684     if (d->service) {
685         if (d->control)
686             d->service->releaseControl(d->control);
687         if (d->audioRoleControl)
688             d->service->releaseControl(d->audioRoleControl);
689         if (d->customAudioRoleControl)
690             d->service->releaseControl(d->customAudioRoleControl);
691 
692         d->provider->releaseService(d->service);
693     }
694 }
695 
media() const696 QMediaContent QMediaPlayer::media() const
697 {
698     Q_D(const QMediaPlayer);
699 
700     return d->rootMedia;
701 }
702 
703 /*!
704     Returns the stream source of media data.
705 
706     This is only valid if a stream was passed to setMedia().
707 
708     \sa setMedia()
709 */
710 
mediaStream() const711 const QIODevice *QMediaPlayer::mediaStream() const
712 {
713     Q_D(const QMediaPlayer);
714 
715     // When playing a resource file, we might have passed a QFile to the backend. Hide it from
716     // the user.
717     if (d->control && d->qrcMedia.isNull())
718         return d->control->mediaStream();
719 
720     return nullptr;
721 }
722 
playlist() const723 QMediaPlaylist *QMediaPlayer::playlist() const
724 {
725     Q_D(const QMediaPlayer);
726 
727     return d->rootMedia.playlist();
728 }
729 
currentMedia() const730 QMediaContent QMediaPlayer::currentMedia() const
731 {
732     Q_D(const QMediaPlayer);
733 
734     // When playing a resource file, don't return the backend's current media, which
735     // can be a temporary file.
736     if (!d->qrcMedia.isNull())
737         return d->qrcMedia;
738 
739     if (d->control)
740         return d->control->media();
741 
742     return QMediaContent();
743 }
744 
setPlaylist(QMediaPlaylist * playlist)745 void QMediaPlayer::setPlaylist(QMediaPlaylist *playlist)
746 {
747     QMediaContent m(playlist, QUrl(), false);
748     setMedia(m);
749 }
750 
751 #ifndef QT_NO_BEARERMANAGEMENT
752 QT_WARNING_PUSH
753 QT_WARNING_DISABLE_DEPRECATED
754 /*!
755     \obsolete
756 
757     Sets the network access points for remote media playback.
758     \a configurations contains, in ascending preferential order, a list of
759     configuration  that can be used for network access.
760 
761     This will invalidate the choice of previous configurations.
762 */
setNetworkConfigurations(const QList<QNetworkConfiguration> & configurations)763 void QMediaPlayer::setNetworkConfigurations(const QList<QNetworkConfiguration> &configurations)
764 {
765     Q_D(QMediaPlayer);
766 
767     if (d->networkAccessControl)
768         d->networkAccessControl->setConfigurations(configurations);
769 }
770 QT_WARNING_POP
771 #endif
772 
state() const773 QMediaPlayer::State QMediaPlayer::state() const
774 {
775     Q_D(const QMediaPlayer);
776 
777     // In case if EndOfMedia status is already received
778     // but state is not.
779     if (d->control != nullptr
780         && d->status == QMediaPlayer::EndOfMedia
781         && d->state != d->control->state()) {
782         return d->control->state();
783     }
784 
785     return d->state;
786 }
787 
mediaStatus() const788 QMediaPlayer::MediaStatus QMediaPlayer::mediaStatus() const
789 {
790     return d_func()->status;
791 }
792 
duration() const793 qint64 QMediaPlayer::duration() const
794 {
795     Q_D(const QMediaPlayer);
796 
797     if (d->control != nullptr)
798         return d->control->duration();
799 
800     return -1;
801 }
802 
position() const803 qint64 QMediaPlayer::position() const
804 {
805     Q_D(const QMediaPlayer);
806 
807     if (d->control != nullptr)
808         return d->control->position();
809 
810     return 0;
811 }
812 
volume() const813 int QMediaPlayer::volume() const
814 {
815     Q_D(const QMediaPlayer);
816 
817     if (d->control != nullptr)
818         return d->control->volume();
819 
820     return 0;
821 }
822 
isMuted() const823 bool QMediaPlayer::isMuted() const
824 {
825     Q_D(const QMediaPlayer);
826 
827     if (d->control != nullptr)
828         return d->control->isMuted();
829 
830     return false;
831 }
832 
bufferStatus() const833 int QMediaPlayer::bufferStatus() const
834 {
835     Q_D(const QMediaPlayer);
836 
837     if (d->control != nullptr)
838         return d->control->bufferStatus();
839 
840     return 0;
841 }
842 
isAudioAvailable() const843 bool QMediaPlayer::isAudioAvailable() const
844 {
845     Q_D(const QMediaPlayer);
846 
847     if (d->control != nullptr)
848         return d->control->isAudioAvailable();
849 
850     return false;
851 }
852 
isVideoAvailable() const853 bool QMediaPlayer::isVideoAvailable() const
854 {
855     Q_D(const QMediaPlayer);
856 
857     if (d->control != nullptr)
858         return d->control->isVideoAvailable();
859 
860     return false;
861 }
862 
isSeekable() const863 bool QMediaPlayer::isSeekable() const
864 {
865     Q_D(const QMediaPlayer);
866 
867     if (d->control != nullptr)
868         return d->control->isSeekable();
869 
870     return false;
871 }
872 
playbackRate() const873 qreal QMediaPlayer::playbackRate() const
874 {
875     Q_D(const QMediaPlayer);
876 
877     if (d->control != nullptr)
878         return d->control->playbackRate();
879 
880     return 0.0;
881 }
882 
883 /*!
884     Returns the current error state.
885 */
886 
error() const887 QMediaPlayer::Error QMediaPlayer::error() const
888 {
889     return d_func()->error;
890 }
891 
errorString() const892 QString QMediaPlayer::errorString() const
893 {
894     return d_func()->errorString;
895 }
896 
897 #ifndef QT_NO_BEARERMANAGEMENT
898 QT_WARNING_PUSH
899 QT_WARNING_DISABLE_DEPRECATED
900 /*!
901     \obsolete
902 
903     Returns the current network access point  in use.
904     If a default contructed QNetworkConfiguration is returned
905     this feature is not available or that none of the
906     current supplied configurations are in use.
907 */
currentNetworkConfiguration() const908 QNetworkConfiguration QMediaPlayer::currentNetworkConfiguration() const
909 {
910     Q_D(const QMediaPlayer);
911 
912     if (d->networkAccessControl)
913         return d_func()->networkAccessControl->currentConfiguration();
914 
915     return QNetworkConfiguration();
916 }
917 QT_WARNING_POP
918 #endif
919 
920 //public Q_SLOTS:
921 /*!
922     Start or resume playing the current source.
923 */
924 
play()925 void QMediaPlayer::play()
926 {
927     Q_D(QMediaPlayer);
928 
929     if (d->control == nullptr) {
930         QMetaObject::invokeMethod(this, "_q_error", Qt::QueuedConnection,
931                                     Q_ARG(int, QMediaPlayer::ServiceMissingError),
932                                     Q_ARG(QString, tr("The QMediaPlayer object does not have a valid service")));
933         return;
934     }
935 
936     //if playlist control is available, the service should advance itself
937     if (d->rootMedia.playlist() && !d->rootMedia.playlist()->isEmpty()) {
938         // switch to playing state
939         if (d->state != QMediaPlayer::PlayingState)
940             d->_q_stateChanged(QMediaPlayer::PlayingState);
941 
942         if (d->rootMedia.playlist()->currentIndex() == -1) {
943             if (d->playlist != d->rootMedia.playlist())
944                 d->setPlaylist(d->rootMedia.playlist());
945             Q_ASSERT(d->playlist == d->rootMedia.playlist());
946 
947             emit currentMediaChanged(d->rootMedia);
948             d->playlist->setCurrentIndex(0);
949         }
950     }
951 
952     // Reset error conditions
953     d->error = NoError;
954     d->errorString = QString();
955 
956     d->control->play();
957 }
958 
959 /*!
960     Pause playing the current source.
961 */
962 
pause()963 void QMediaPlayer::pause()
964 {
965     Q_D(QMediaPlayer);
966 
967     if (d->control != nullptr)
968         d->control->pause();
969 }
970 
971 /*!
972     Stop playing, and reset the play position to the beginning.
973 */
974 
stop()975 void QMediaPlayer::stop()
976 {
977     Q_D(QMediaPlayer);
978 
979     if (d->control != nullptr)
980         d->control->stop();
981 
982     // If media player didn't stop in response to control.
983     // This happens if we have an active playlist and control
984     // media status is
985     // QMediaPlayer::LoadingMedia, QMediaPlayer::InvalidMedia, or QMediaPlayer::EndOfMedia
986     // see QMediaPlayerPrivate::_q_stateChanged()
987     if (d->playlist && d->state != QMediaPlayer::StoppedState) {
988         d->state = QMediaPlayer::StoppedState;
989         removePropertyWatch("position");
990         emit stateChanged(QMediaPlayer::StoppedState);
991     }
992 }
993 
setPosition(qint64 position)994 void QMediaPlayer::setPosition(qint64 position)
995 {
996     Q_D(QMediaPlayer);
997 
998     if (d->control == nullptr)
999         return;
1000 
1001     d->control->setPosition(qMax(position, 0ll));
1002 }
1003 
setVolume(int v)1004 void QMediaPlayer::setVolume(int v)
1005 {
1006     Q_D(QMediaPlayer);
1007 
1008     if (d->control == nullptr)
1009         return;
1010 
1011     int clamped = qBound(0, v, 100);
1012     if (clamped == volume())
1013         return;
1014 
1015     d->control->setVolume(clamped);
1016 }
1017 
setMuted(bool muted)1018 void QMediaPlayer::setMuted(bool muted)
1019 {
1020     Q_D(QMediaPlayer);
1021 
1022     if (d->control == nullptr || muted == isMuted())
1023         return;
1024 
1025     d->control->setMuted(muted);
1026 }
1027 
setPlaybackRate(qreal rate)1028 void QMediaPlayer::setPlaybackRate(qreal rate)
1029 {
1030     Q_D(QMediaPlayer);
1031 
1032     if (d->control != nullptr)
1033         d->control->setPlaybackRate(rate);
1034 }
1035 
1036 /*!
1037     Sets the current \a media source.
1038 
1039     If a \a stream is supplied; media data will be read from it instead of resolving the media
1040     source. In this case the url should be provided to resolve additional information
1041     about the media such as mime type. The \a stream must be open and readable.
1042     For macOS the \a stream should be also seekable.
1043 
1044     Setting the media to a null QMediaContent will cause the player to discard all
1045     information relating to the current media source and to cease all I/O operations related
1046     to that media.
1047 
1048     \note This function returns immediately after recording the specified source of the media.
1049     It does not wait for the media to finish loading and does not check for errors. Listen for
1050     the mediaStatusChanged() and error() signals to be notified when the media is loaded and
1051     when an error occurs during loading.
1052 
1053     Since Qt 5.12.2, the url scheme \c gst-pipeline provides custom pipelines
1054     for the GStreamer backend.
1055 
1056     \snippet multimedia-snippets/media.cpp Pipeline
1057 
1058     If QAbstractVideoSurface is used as the video output,
1059     \c qtvideosink can be used as a video sink element directly in the pipeline.
1060     After that the surface will receive the video frames in QAbstractVideoSurface::present().
1061 
1062     \snippet multimedia-snippets/media.cpp Pipeline Surface
1063 
1064     If QVideoWidget is used as the video output
1065     and the pipeline contains a video sink element named \c qtvideosink,
1066     current QVideoWidget will be used to render the video.
1067 
1068     \snippet multimedia-snippets/media.cpp Pipeline Widget
1069 
1070     If the pipeline contains appsrc element, it will be used to push data from \a stream.
1071 
1072     \snippet multimedia-snippets/media.cpp Pipeline appsrc
1073 */
1074 
setMedia(const QMediaContent & media,QIODevice * stream)1075 void QMediaPlayer::setMedia(const QMediaContent &media, QIODevice *stream)
1076 {
1077     Q_D(QMediaPlayer);
1078     stop();
1079 
1080     QMediaContent oldMedia = d->rootMedia;
1081     d->disconnectPlaylist();
1082     d->playlist = nullptr;
1083     d->rootMedia = media;
1084     d->nestedPlaylists = 0;
1085 
1086     if (oldMedia != media)
1087         emit mediaChanged(d->rootMedia);
1088 
1089     if (media.playlist()) {
1090         // reset playlist to the 1st item
1091         media.playlist()->setCurrentIndex(0);
1092         d->setPlaylist(media.playlist());
1093     } else {
1094         d->setMedia(media, stream);
1095     }
1096 }
1097 
1098 /*!
1099     \internal
1100 */
1101 
bind(QObject * obj)1102 bool QMediaPlayer::bind(QObject *obj)
1103 {
1104     return QMediaObject::bind(obj);
1105 }
1106 
1107 /*!
1108     \internal
1109 */
1110 
unbind(QObject * obj)1111 void QMediaPlayer::unbind(QObject *obj)
1112 {
1113     QMediaObject::unbind(obj);
1114 }
1115 
1116 /*!
1117     Returns the level of support a media player has for a \a mimeType and a set of \a codecs.
1118 
1119     The \a flags argument allows additional requirements such as performance indicators to be
1120     specified.
1121 */
hasSupport(const QString & mimeType,const QStringList & codecs,Flags flags)1122 QMultimedia::SupportEstimate QMediaPlayer::hasSupport(const QString &mimeType,
1123                                                const QStringList& codecs,
1124                                                Flags flags)
1125 {
1126     return QMediaServiceProvider::defaultServiceProvider()->hasSupport(QByteArray(Q_MEDIASERVICE_MEDIAPLAYER),
1127                                                                     mimeType,
1128                                                                     codecs,
1129                                                                     flags);
1130 }
1131 
1132 /*!
1133     \deprecated
1134     Returns a list of MIME types supported by the media player.
1135 
1136     The \a flags argument causes the resultant list to be restricted to MIME types which can be supported
1137     given additional requirements, such as performance indicators.
1138 
1139     This function may not return useful results on some platforms, and support for a specific file of a
1140     given mime type is not guaranteed even if the mime type is in general supported.  In addition, in some
1141     cases this function will need to load all available media plugins and query them for their support, which
1142     may take some time.
1143 */
supportedMimeTypes(Flags flags)1144 QStringList QMediaPlayer::supportedMimeTypes(Flags flags)
1145 {
1146     return QMediaServiceProvider::defaultServiceProvider()->supportedMimeTypes(QByteArray(Q_MEDIASERVICE_MEDIAPLAYER),
1147                                                                                flags);
1148 }
1149 
1150 /*!
1151     \fn void QMediaPlayer::setVideoOutput(QVideoWidget* output)
1152 
1153     Attach a QVideoWidget video \a output to the media player.
1154 
1155     If the media player has already video output attached,
1156     it will be replaced with a new one.
1157 */
setVideoOutput(QVideoWidget * output)1158 void QMediaPlayer::setVideoOutput(QVideoWidget *output)
1159 {
1160     Q_D(QMediaPlayer);
1161 
1162     if (d->videoOutput)
1163         unbind(d->videoOutput);
1164 
1165     // We don't know (in this library) that QVideoWidget inherits QObject
1166     QObject *outputObject = reinterpret_cast<QObject*>(output);
1167 
1168     d->videoOutput = outputObject && bind(outputObject) ? outputObject : nullptr;
1169 }
1170 
1171 /*!
1172     \fn void QMediaPlayer::setVideoOutput(QGraphicsVideoItem* output)
1173 
1174     Attach a QGraphicsVideoItem video \a output to the media player.
1175 
1176     If the media player has already video output attached,
1177     it will be replaced with a new one.
1178 */
setVideoOutput(QGraphicsVideoItem * output)1179 void QMediaPlayer::setVideoOutput(QGraphicsVideoItem *output)
1180 {
1181     Q_D(QMediaPlayer);
1182 
1183     if (d->videoOutput)
1184         unbind(d->videoOutput);
1185 
1186     // We don't know (in this library) that QGraphicsVideoItem (multiply) inherits QObject
1187     // but QObject inheritance depends on QObject coming first, so try this out.
1188     QObject *outputObject = reinterpret_cast<QObject*>(output);
1189 
1190     d->videoOutput = outputObject && bind(outputObject) ? outputObject : nullptr;
1191 }
1192 
1193 /*!
1194     Sets a video \a surface as the video output of a media player.
1195 
1196     If a video output has already been set on the media player the new surface
1197     will replace it.
1198 */
1199 
setVideoOutput(QAbstractVideoSurface * surface)1200 void QMediaPlayer::setVideoOutput(QAbstractVideoSurface *surface)
1201 {
1202     Q_D(QMediaPlayer);
1203 
1204     d->surfaceOutput.setVideoSurface(surface);
1205 
1206     if (d->videoOutput != &d->surfaceOutput) {
1207         if (d->videoOutput)
1208             unbind(d->videoOutput);
1209 
1210         d->videoOutput = nullptr;
1211 
1212         if (surface && bind(&d->surfaceOutput))
1213             d->videoOutput =  &d->surfaceOutput;
1214     }  else if (!surface) {
1215         //unbind the surfaceOutput if null surface is set
1216         unbind(&d->surfaceOutput);
1217         d->videoOutput = nullptr;
1218     }
1219 }
1220 
1221 /*!
1222     \since 5.15
1223     Sets multiple video surfaces as the video output of a media player.
1224     This allows the media player to render video frames on different surfaces.
1225 
1226     All video surfaces must support at least one shared \c QVideoFrame::PixelFormat.
1227 
1228     If a video output has already been set on the media player the new surfaces
1229     will replace it.
1230 
1231     \sa QAbstractVideoSurface::supportedPixelFormats
1232 */
1233 
setVideoOutput(const QVector<QAbstractVideoSurface * > & surfaces)1234 void QMediaPlayer::setVideoOutput(const QVector<QAbstractVideoSurface *> &surfaces)
1235 {
1236     setVideoOutput(!surfaces.empty() ? new QVideoSurfaces(surfaces, this) : nullptr);
1237 }
1238 
1239 /*! \reimp */
availability() const1240 QMultimedia::AvailabilityStatus QMediaPlayer::availability() const
1241 {
1242     Q_D(const QMediaPlayer);
1243 
1244     if (!d->control)
1245         return QMultimedia::ServiceMissing;
1246 
1247     return QMediaObject::availability();
1248 }
1249 
audioRole() const1250 QAudio::Role QMediaPlayer::audioRole() const
1251 {
1252     Q_D(const QMediaPlayer);
1253 
1254     if (d->audioRoleControl != nullptr)
1255         return d->audioRoleControl->audioRole();
1256 
1257     return QAudio::UnknownRole;
1258 }
1259 
setAudioRole(QAudio::Role audioRole)1260 void QMediaPlayer::setAudioRole(QAudio::Role audioRole)
1261 {
1262     Q_D(QMediaPlayer);
1263 
1264     if (d->audioRoleControl) {
1265         if (d->customAudioRoleControl != nullptr && d->audioRoleControl->audioRole() != audioRole) {
1266             d->customAudioRoleControl->setCustomAudioRole(QString());
1267         }
1268 
1269         d->audioRoleControl->setAudioRole(audioRole);
1270     }
1271 }
1272 
1273 /*!
1274     Returns a list of supported audio roles.
1275 
1276     If setting the audio role is not supported, an empty list is returned.
1277 
1278     \since 5.6
1279     \sa audioRole
1280 */
supportedAudioRoles() const1281 QList<QAudio::Role> QMediaPlayer::supportedAudioRoles() const
1282 {
1283     Q_D(const QMediaPlayer);
1284 
1285     if (d->audioRoleControl)
1286         return d->audioRoleControl->supportedAudioRoles();
1287 
1288     return QList<QAudio::Role>();
1289 }
1290 
customAudioRole() const1291 QString QMediaPlayer::customAudioRole() const
1292 {
1293     Q_D(const QMediaPlayer);
1294 
1295     if (audioRole() != QAudio::CustomRole)
1296         return QString();
1297 
1298     if (d->customAudioRoleControl != nullptr)
1299         return d->customAudioRoleControl->customAudioRole();
1300 
1301     return QString();
1302 }
1303 
setCustomAudioRole(const QString & audioRole)1304 void QMediaPlayer::setCustomAudioRole(const QString &audioRole)
1305 {
1306     Q_D(QMediaPlayer);
1307 
1308     if (d->customAudioRoleControl) {
1309         Q_ASSERT(d->audioRoleControl);
1310         setAudioRole(QAudio::CustomRole);
1311         d->customAudioRoleControl->setCustomAudioRole(audioRole);
1312     }
1313 }
1314 
1315 /*!
1316     Returns a list of supported custom audio roles. An empty list may
1317     indicate that the supported custom audio roles aren't known. The
1318     list may not be complete.
1319 
1320     \since 5.11
1321     \sa customAudioRole
1322 */
supportedCustomAudioRoles() const1323 QStringList QMediaPlayer::supportedCustomAudioRoles() const
1324 {
1325     Q_D(const QMediaPlayer);
1326 
1327     if (d->customAudioRoleControl)
1328         return d->customAudioRoleControl->supportedCustomAudioRoles();
1329 
1330     return QStringList();
1331 }
1332 
1333 // Enums
1334 /*!
1335     \enum QMediaPlayer::State
1336 
1337     Defines the current state of a media player.
1338 
1339     \value StoppedState The media player is not playing content, playback will begin from the start
1340     of the current track.
1341     \value PlayingState The media player is currently playing content.
1342     \value PausedState The media player has paused playback, playback of the current track will
1343     resume from the position the player was paused at.
1344 */
1345 
1346 /*!
1347     \enum QMediaPlayer::MediaStatus
1348 
1349     Defines the status of a media player's current media.
1350 
1351     \value UnknownMediaStatus The status of the media cannot be determined.
1352     \value NoMedia The is no current media.  The player is in the StoppedState.
1353     \value LoadingMedia The current media is being loaded. The player may be in any state.
1354     \value LoadedMedia The current media has been loaded. The player is in the StoppedState.
1355     \value StalledMedia Playback of the current media has stalled due to insufficient buffering or
1356     some other temporary interruption.  The player is in the PlayingState or PausedState.
1357     \value BufferingMedia The player is buffering data but has enough data buffered for playback to
1358     continue for the immediate future.  The player is in the PlayingState or PausedState.
1359     \value BufferedMedia The player has fully buffered the current media.  The player is in the
1360     PlayingState or PausedState.
1361     \value EndOfMedia Playback has reached the end of the current media.  The player is in the
1362     StoppedState.
1363     \value InvalidMedia The current media cannot be played.  The player is in the StoppedState.
1364 */
1365 
1366 /*!
1367     \enum QMediaPlayer::Error
1368 
1369     Defines a media player error condition.
1370 
1371     \value NoError No error has occurred.
1372     \value ResourceError A media resource couldn't be resolved.
1373     \value FormatError The format of a media resource isn't (fully) supported.  Playback may still
1374     be possible, but without an audio or video component.
1375     \value NetworkError A network error occurred.
1376     \value AccessDeniedError There are not the appropriate permissions to play a media resource.
1377     \value ServiceMissingError A valid playback service was not found, playback cannot proceed.
1378     \omitvalue MediaIsPlaylist
1379 */
1380 
1381 // Signals
1382 /*!
1383     \fn QMediaPlayer::error(QMediaPlayer::Error error)
1384 
1385     Signals that an \a error condition has occurred.
1386 
1387     \sa errorString()
1388 */
1389 
1390 /*!
1391     \fn void QMediaPlayer::stateChanged(State state)
1392 
1393     Signal the \a state of the Player object has changed.
1394 */
1395 
1396 /*!
1397     \fn QMediaPlayer::mediaStatusChanged(QMediaPlayer::MediaStatus status)
1398 
1399     Signals that the \a status of the current media has changed.
1400 
1401     \sa mediaStatus()
1402 */
1403 
1404 /*!
1405     \fn void QMediaPlayer::mediaChanged(const QMediaContent &media);
1406 
1407     Signals that the media source has been changed to \a media.
1408 
1409     \sa media(), currentMediaChanged()
1410 */
1411 
1412 /*!
1413     \fn void QMediaPlayer::currentMediaChanged(const QMediaContent &media);
1414 
1415     Signals that the current playing content has been changed to \a media.
1416 
1417     \sa currentMedia(), mediaChanged()
1418 */
1419 
1420 /*!
1421     \fn void QMediaPlayer::playbackRateChanged(qreal rate);
1422 
1423     Signals the playbackRate has changed to \a rate.
1424 */
1425 
1426 /*!
1427     \fn void QMediaPlayer::seekableChanged(bool seekable);
1428 
1429     Signals the \a seekable status of the player object has changed.
1430 */
1431 
1432 /*!
1433     \fn void QMediaPlayer::audioRoleChanged(QAudio::Role role)
1434 
1435     Signals that the audio \a role of the media player has changed.
1436 
1437     \since 5.6
1438 */
1439 
1440 /*!
1441     \fn void QMediaPlayer::customAudioRoleChanged(const QString &role)
1442 
1443     Signals that the audio \a role of the media player has changed.
1444 
1445     \since 5.11
1446 */
1447 
1448 // Properties
1449 /*!
1450     \property QMediaPlayer::state
1451     \brief the media player's playback state.
1452 
1453     By default this property is QMediaPlayer::Stopped
1454 
1455     \sa mediaStatus(), play(), pause(), stop()
1456 */
1457 
1458 /*!
1459     \property QMediaPlayer::error
1460     \brief a string describing the last error condition.
1461 
1462     \sa error()
1463 */
1464 
1465 /*!
1466     \property QMediaPlayer::media
1467     \brief the active media source being used by the player object.
1468 
1469     The player object will use the QMediaContent for selection of the content to
1470     be played.
1471 
1472     By default this property has a null QMediaContent.
1473 
1474     Setting this property to a null QMediaContent will cause the player to discard all
1475     information relating to the current media source and to cease all I/O operations related
1476     to that media.
1477 
1478     \sa QMediaContent, currentMedia()
1479 */
1480 
1481 /*!
1482     \property QMediaPlayer::currentMedia
1483     \brief the current active media content being played by the player object.
1484     This value could be different from QMediaPlayer::media property if a playlist is used.
1485     In this case currentMedia indicates the current media content being processed
1486     by the player, while QMediaPlayer::media property contains the original playlist.
1487 
1488     \sa QMediaContent, media()
1489 */
1490 
1491 /*!
1492     \property QMediaPlayer::playlist
1493     \brief the media playlist being used by the player object.
1494 
1495     The player object will use the current playlist item for selection of the content to
1496     be played.
1497 
1498     By default this property is set to null.
1499 
1500     If the media playlist is used as a source, QMediaPlayer::currentMedia is updated with
1501     a current playlist item. The current source should be selected with
1502     QMediaPlaylist::setCurrentIndex(int) instead of QMediaPlayer::setMedia(),
1503     otherwise the current playlist will be discarded.
1504 
1505     \sa QMediaContent
1506 */
1507 
1508 
1509 /*!
1510     \property QMediaPlayer::mediaStatus
1511     \brief the status of the current media stream.
1512 
1513     The stream status describes how the playback of the current stream is
1514     progressing.
1515 
1516     By default this property is QMediaPlayer::NoMedia
1517 
1518     \sa state
1519 */
1520 
1521 /*!
1522     \property QMediaPlayer::duration
1523     \brief the duration of the current media.
1524 
1525     The value is the total playback time in milliseconds of the current media.
1526     The value may change across the life time of the QMediaPlayer object and
1527     may not be available when initial playback begins, connect to the
1528     durationChanged() signal to receive status notifications.
1529 */
1530 
1531 /*!
1532     \property QMediaPlayer::position
1533     \brief the playback position of the current media.
1534 
1535     The value is the current playback position, expressed in milliseconds since
1536     the beginning of the media. Periodically changes in the position will be
1537     indicated with the signal positionChanged(), the interval between updates
1538     can be set with QMediaObject's method setNotifyInterval().
1539 */
1540 
1541 /*!
1542     \property QMediaPlayer::volume
1543     \brief the current playback volume.
1544 
1545     The playback volume is scaled linearly, ranging from \c 0 (silence) to \c 100 (full volume).
1546     Values outside this range will be clamped.
1547 
1548     By default the volume is \c 100.
1549 
1550     UI volume controls should usually be scaled nonlinearly. For example, using a logarithmic scale
1551     will produce linear changes in perceived loudness, which is what a user would normally expect
1552     from a volume control. See QAudio::convertVolume() for more details.
1553 */
1554 
1555 /*!
1556     \property QMediaPlayer::muted
1557     \brief the muted state of the current media.
1558 
1559     The value will be true if the playback volume is muted; otherwise false.
1560 */
1561 
1562 /*!
1563     \property QMediaPlayer::bufferStatus
1564     \brief the percentage of the temporary buffer filled before playback begins or resumes, from
1565     \c 0 (empty) to \c 100 (full).
1566 
1567     When the player object is buffering; this property holds the percentage of
1568     the temporary buffer that is filled. The buffer will need to reach 100%
1569     filled before playback can start or resume, at which time mediaStatus() will return
1570     BufferedMedia or BufferingMedia. If the value is anything lower than \c 100, mediaStatus() will
1571     return StalledMedia.
1572 
1573     \sa mediaStatus()
1574 */
1575 
1576 /*!
1577     \property QMediaPlayer::audioAvailable
1578     \brief the audio availabilty status for the current media.
1579 
1580     As the life time of QMediaPlayer can be longer than the playback of one
1581     QMediaContent, this property may change over time, the
1582     audioAvailableChanged signal can be used to monitor it's status.
1583 */
1584 
1585 /*!
1586     \property QMediaPlayer::videoAvailable
1587     \brief the video availability status for the current media.
1588 
1589     If available, the QVideoWidget class can be used to view the video. As the
1590     life time of QMediaPlayer can be longer than the playback of one
1591     QMediaContent, this property may change over time, the
1592     videoAvailableChanged signal can be used to monitor it's status.
1593 
1594     \sa QVideoWidget, QMediaContent
1595 */
1596 
1597 /*!
1598     \property QMediaPlayer::seekable
1599     \brief the seek-able status of the current media
1600 
1601     If seeking is supported this property will be true; false otherwise. The
1602     status of this property may change across the life time of the QMediaPlayer
1603     object, use the seekableChanged signal to monitor changes.
1604 */
1605 
1606 /*!
1607     \property QMediaPlayer::playbackRate
1608     \brief the playback rate of the current media.
1609 
1610     This value is a multiplier applied to the media's standard play rate. By
1611     default this value is 1.0, indicating that the media is playing at the
1612     standard pace. Values higher than 1.0 will increase the rate of play.
1613     Values less than zero can be set and indicate the media should rewind at the
1614     multiplier of the standard pace.
1615 
1616     Not all playback services support change of the playback rate. It is
1617     framework defined as to the status and quality of audio and video
1618     while fast forwarding or rewinding.
1619 */
1620 
1621 /*!
1622     \property QMediaPlayer::audioRole
1623     \brief the role of the audio stream played by the media player.
1624 
1625     It can be set to specify the type of audio being played, allowing the system to make
1626     appropriate decisions when it comes to volume, routing or post-processing.
1627 
1628     The audio role must be set before calling setMedia().
1629 
1630     customAudioRole is cleared when this property is set to anything other than
1631     QAudio::CustomRole.
1632 
1633     \since 5.6
1634     \sa supportedAudioRoles()
1635 */
1636 
1637 /*!
1638     \property QMediaPlayer::customAudioRole
1639     \brief the role of the audio stream played by the media player.
1640 
1641     It can be set to specify the type of audio being played when the backend supports
1642     audio roles unknown to Qt. Specifying a role allows the system to make appropriate
1643     decisions when it comes to volume, routing or post-processing.
1644 
1645     The audio role must be set before calling setMedia().
1646 
1647     audioRole is set to QAudio::CustomRole when this property is set.
1648 
1649     \since 5.11
1650     \sa supportedCustomAudioRoles()
1651 */
1652 
1653 /*!
1654     \fn void QMediaPlayer::durationChanged(qint64 duration)
1655 
1656     Signal the duration of the content has changed to \a duration, expressed in milliseconds.
1657 */
1658 
1659 /*!
1660     \fn void QMediaPlayer::positionChanged(qint64 position)
1661 
1662     Signal the position of the content has changed to \a position, expressed in
1663     milliseconds.
1664 */
1665 
1666 /*!
1667     \fn void QMediaPlayer::volumeChanged(int volume)
1668 
1669     Signal the playback volume has changed to \a volume.
1670 */
1671 
1672 /*!
1673     \fn void QMediaPlayer::mutedChanged(bool muted)
1674 
1675     Signal the mute state has changed to \a muted.
1676 */
1677 
1678 /*!
1679     \fn void QMediaPlayer::videoAvailableChanged(bool videoAvailable)
1680 
1681     Signal the availability of visual content has changed to \a videoAvailable.
1682 */
1683 
1684 /*!
1685     \fn void QMediaPlayer::audioAvailableChanged(bool available)
1686 
1687     Signals the availability of audio content has changed to \a available.
1688 */
1689 
1690 /*!
1691     \fn void QMediaPlayer::bufferStatusChanged(int percentFilled)
1692 
1693     Signal the amount of the local buffer filled as a percentage by \a percentFilled.
1694 */
1695 
1696 /*!
1697    \fn void QMediaPlayer::networkConfigurationChanged(const QNetworkConfiguration &configuration)
1698    \obsolete
1699 
1700     Signal that the active in use network access point  has been changed to \a configuration and all subsequent network access will use this \a configuration.
1701 */
1702 
1703 /*!
1704     \enum QMediaPlayer::Flag
1705 
1706     \value LowLatency       The player is expected to be used with simple audio formats,
1707             but playback should start without significant delay.
1708             Such playback service can be used for beeps, ringtones, etc.
1709 
1710     \value StreamPlayback   The player is expected to play QIODevice based streams.
1711             If passed to QMediaPlayer constructor, the service supporting
1712             streams playback will be chosen.
1713 
1714     \value VideoSurface     The player is expected to be able to render to a
1715             QAbstractVideoSurface \l {setVideoOutput()}{output}.
1716 */
1717 
1718 QT_END_NAMESPACE
1719 
1720 #include "moc_qmediaplayer.cpp"
1721