File indexing completed on 2025-01-19 03:57:05

0001 /*********************************************************
0002  * Copyright (C) 2020, Val Doroshchuk <valbok@gmail.com> *
0003  *                                                       *
0004  * This file is part of QtAVPlayer.                      *
0005  * Free Qt Media Player based on FFmpeg.                 *
0006  *********************************************************/
0007 
0008 #include "qavplayer.h"
0009 #include "qavdemuxer_p.h"
0010 #include "qaviodevice.h"
0011 #include "qavvideocodec_p.h"
0012 #include "qavaudiocodec_p.h"
0013 #include "qavvideoframe.h"
0014 #include "qavaudioframe.h"
0015 #include "qavsubtitleframe.h"
0016 #include "qavpacketqueue_p.h"
0017 #include "qavfiltergraph_p.h"
0018 #include "qavvideofilter_p.h"
0019 #include "qavaudiofilter_p.h"
0020 #include "qavfilters_p.h"
0021 #include <QtConcurrent/qtconcurrentrun.h>
0022 #include <QLoggingCategory>
0023 #include <functional>
0024 
0025 extern "C" {
0026 #include <libavformat/avformat.h>
0027 }
0028 
0029 QT_BEGIN_NAMESPACE
0030 
0031 Q_LOGGING_CATEGORY(lcAVPlayer, "qt.QtAVPlayer")
0032 
0033 enum PendingMediaStatus
0034 {
0035     LoadingMedia,
0036     PlayingMedia,
0037     PausingMedia,
0038     StoppingMedia,
0039     SteppingMedia,
0040     SeekingMedia,
0041     EndOfMedia
0042 };
0043 
0044 class QAVPlayerPrivate
0045 {
0046     Q_DECLARE_PUBLIC(QAVPlayer)
0047 public:
0048     QAVPlayerPrivate(QAVPlayer *q)
0049         : q_ptr(q)
0050         , videoQueue(AVMEDIA_TYPE_VIDEO, demuxer)
0051         , audioQueue(AVMEDIA_TYPE_AUDIO, demuxer)
0052         , subtitleQueue(AVMEDIA_TYPE_SUBTITLE, demuxer)
0053     {
0054         threadPool.setMaxThreadCount(4);
0055     }
0056 
0057     QAVPlayer::Error currentError() const;
0058     void setMediaStatus(QAVPlayer::MediaStatus status);
0059     void resetPendingStatuses();
0060     void setPendingMediaStatus(PendingMediaStatus status);
0061     void step(bool hasFrame);
0062     bool doStep(PendingMediaStatus status, bool hasFrame);
0063     bool setState(QAVPlayer::State s);
0064     void setSeekable(bool seekable);
0065     void setError(QAVPlayer::Error err, const QString &str);
0066     void setDuration(double d);
0067     bool isSeeking() const;
0068     bool isEndOfFile() const;
0069     void endOfFile(bool v);
0070     void setVideoFrameRate(double v);
0071     void setPts(double v);
0072     double pts() const;
0073     void applyFilters();
0074     void applyFilters(bool reset, const QAVFrame &frame);
0075 
0076     void terminate();
0077 
0078     void doWait();
0079     void wait(bool v);
0080     void doLoad();
0081     void doDemux();
0082     bool skipFrame(
0083         bool master,
0084         const QAVStreamFrame &frame,
0085         bool isEmpty);
0086     bool doApplyFilters(
0087         const QAVFrame &decodedFrame,
0088         const std::vector<std::unique_ptr<QAVFilter>> &filters,
0089         QList<QAVFrame> &filteredFrames);
0090 
0091     void doPlayStep(
0092         bool &master,
0093         double refPts,
0094         QAVQueueClock &clock,
0095         QAVPacketQueue<QAVFrame> &queue,
0096         bool &sync,
0097         const std::function<void(const QAVFrame &frame)> &cb);
0098     void doPlayStep(
0099         QAVQueueClock &clock,
0100         QAVPacketQueue<QAVSubtitleFrame> &queue,
0101         bool &sync,
0102         const std::function<void(const QAVSubtitleFrame &frame)> &cb);
0103 
0104     void doPlayVideo();
0105     void doPlayAudio();
0106     void doPlaySubtitle();
0107 
0108     template <class T>
0109     void dispatch(T fn);
0110 
0111     QAVPlayer *q_ptr = nullptr;
0112     QString url;
0113     QSharedPointer<QAVIODevice> dev;
0114     QAVPlayer::MediaStatus mediaStatus = QAVPlayer::NoMedia;
0115     QList<PendingMediaStatus> pendingMediaStatuses;
0116     QAVPlayer::State state = QAVPlayer::StoppedState;
0117     mutable QMutex stateMutex;
0118 
0119     bool seekable = false;
0120     qreal speed = 1.0;
0121     mutable QMutex speedMutex;
0122     double videoFrameRate = 0.0;
0123 
0124     double duration = 0;
0125     double pendingPosition = 0;
0126     bool pendingSeek = false;
0127     double currPts = 0.0;
0128     mutable QMutex positionMutex;
0129     bool synced = true;
0130 
0131     QAVPlayer::Error error = QAVPlayer::NoError;
0132 
0133     QAVDemuxer demuxer;
0134 
0135     QThreadPool threadPool;
0136     QFuture<void> loaderFuture;
0137     QFuture<void> demuxerFuture;
0138 
0139     QFuture<void> videoPlayFuture;
0140     QAVPacketQueue<QAVFrame> videoQueue;
0141     QAVQueueClock videoClock;
0142 
0143     QFuture<void> audioPlayFuture;
0144     QAVPacketQueue<QAVFrame> audioQueue;
0145     QAVQueueClock audioClock;
0146 
0147     QFuture<void> subtitlePlayFuture;
0148     QAVPacketQueue<QAVSubtitleFrame> subtitleQueue;
0149     QAVQueueClock subtitleClock;
0150 
0151     bool quit = 0;
0152     bool isWaiting = false;
0153     mutable QMutex waitMutex;
0154     QWaitCondition waitCond;
0155     bool eof = false;
0156     std::atomic_bool startDemuxing {false};
0157 
0158     QList<QString> filterDescs;
0159     QAVFilters filters;
0160 };
0161 
0162 static QString err_str(int err)
0163 {
0164     char errbuf[128];
0165     const char *errbuf_ptr = errbuf;
0166     if (av_strerror(err, errbuf, sizeof(errbuf)) < 0)
0167         errbuf_ptr = strerror(AVUNERROR(err));
0168 
0169     return QString::fromUtf8(errbuf_ptr);
0170 }
0171 
0172 QAVPlayer::Error QAVPlayerPrivate::currentError() const
0173 {
0174     QMutexLocker locker(&stateMutex);
0175     return error;
0176 }
0177 
0178 void QAVPlayerPrivate::setMediaStatus(QAVPlayer::MediaStatus status)
0179 {
0180     {
0181         QMutexLocker locker(&stateMutex);
0182         if (mediaStatus == status)
0183             return;
0184 
0185         if (status != QAVPlayer::InvalidMedia)
0186             error = QAVPlayer::NoError;
0187 
0188         qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << mediaStatus << "->" << status;
0189         mediaStatus = status;
0190     }
0191 
0192     Q_EMIT q_ptr->mediaStatusChanged(status);
0193 }
0194 
0195 void QAVPlayerPrivate::resetPendingStatuses()
0196 {
0197     QMutexLocker locker(&stateMutex);
0198     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << pendingMediaStatuses;
0199     pendingMediaStatuses.clear();
0200     wait(true);
0201 }
0202 
0203 void QAVPlayerPrivate::setPendingMediaStatus(PendingMediaStatus status)
0204 {
0205     QMutexLocker locker(&stateMutex);
0206     pendingMediaStatuses.push_back(status);
0207     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << mediaStatus << "->" << pendingMediaStatuses;
0208 }
0209 
0210 bool QAVPlayerPrivate::setState(QAVPlayer::State s)
0211 {
0212     Q_Q(QAVPlayer);
0213     bool result = false;
0214     {
0215         QMutexLocker locker(&stateMutex);
0216         if (state == s)
0217             return result;
0218 
0219         qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << state << "->" << s;
0220         state = s;
0221         result = true;
0222     }
0223 
0224     Q_EMIT q->stateChanged(s);
0225     return result;
0226 }
0227 
0228 void QAVPlayerPrivate::setSeekable(bool s)
0229 {
0230     Q_Q(QAVPlayer);
0231     if (seekable == s)
0232         return;
0233 
0234     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << seekable << "->" << s;
0235     seekable = s;
0236     Q_EMIT q->seekableChanged(seekable);
0237 }
0238 
0239 void QAVPlayerPrivate::setDuration(double d)
0240 {
0241     Q_Q(QAVPlayer);
0242     if (qFuzzyCompare(duration, d))
0243         return;
0244 
0245     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << duration << "->" << d;
0246     duration = d;
0247     Q_EMIT q->durationChanged(q->duration());
0248 }
0249 
0250 bool QAVPlayerPrivate::isSeeking() const
0251 {
0252     QMutexLocker locker(&positionMutex);
0253     return pendingSeek;
0254 }
0255 
0256 bool QAVPlayerPrivate::isEndOfFile() const
0257 {
0258     QMutexLocker locker(&stateMutex);
0259     return eof;
0260 }
0261 
0262 void QAVPlayerPrivate::endOfFile(bool v)
0263 {
0264     QMutexLocker locker(&stateMutex);
0265     eof = v;
0266 }
0267 
0268 void QAVPlayerPrivate::setVideoFrameRate(double v)
0269 {
0270     Q_Q(QAVPlayer);
0271     if (qFuzzyCompare(videoFrameRate, v))
0272         return;
0273 
0274     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << videoFrameRate << "->" << v;
0275     videoFrameRate = v;
0276     Q_EMIT q->videoFrameRateChanged(v);
0277 }
0278 
0279 void QAVPlayerPrivate::setPts(double v)
0280 {
0281     QMutexLocker locker(&positionMutex);
0282     if (!isnan(v))
0283         currPts = v;
0284 }
0285 
0286 double QAVPlayerPrivate::pts() const
0287 {
0288     QMutexLocker locker(&positionMutex);
0289     return currPts;
0290 }
0291 
0292 template <class T>
0293 void QAVPlayerPrivate::dispatch(T fn)
0294 {
0295     QMetaObject::invokeMethod(q_ptr, fn, nullptr);
0296 }
0297 
0298 void QAVPlayerPrivate::setError(QAVPlayer::Error err, const QString &str)
0299 {
0300     Q_Q(QAVPlayer);
0301     {
0302         QMutexLocker locker(&stateMutex);
0303         error = err;
0304     }
0305 
0306     qWarning() << err << ":" << str;
0307     Q_EMIT q->errorOccurred(err, str);
0308     setMediaStatus(QAVPlayer::InvalidMedia);
0309     setState(QAVPlayer::StoppedState);
0310     resetPendingStatuses();
0311 }
0312 
0313 void QAVPlayerPrivate::terminate()
0314 {
0315     qCDebug(lcAVPlayer) << __FUNCTION__;
0316     setState(QAVPlayer::StoppedState);
0317     quit = true;
0318     wait(false);
0319     videoFrameRate = 0.0;
0320     videoQueue.clear();
0321     videoQueue.abort();
0322     videoClock.clear();
0323     audioQueue.clear();
0324     audioQueue.abort();
0325     audioClock.clear();
0326     subtitleQueue.clear();
0327     subtitleQueue.abort();
0328     subtitleClock.clear();
0329     if (dev)
0330         dev->abort(true);
0331     loaderFuture.waitForFinished();
0332     demuxerFuture.waitForFinished();
0333     videoPlayFuture.waitForFinished();
0334     audioPlayFuture.waitForFinished();
0335     pendingPosition = 0;
0336     pendingSeek = false;
0337     currPts = 0.0;
0338     pendingMediaStatuses.clear();
0339     filters.clear();
0340     setDuration(0);
0341     error = QAVPlayer::NoError;
0342     dev.reset();
0343     eof = false;
0344     startDemuxing = false;
0345 }
0346 
0347 void QAVPlayerPrivate::step(bool hasFrame)
0348 {
0349     QMutexLocker locker(&stateMutex);
0350     while (!pendingMediaStatuses.isEmpty()) {
0351         auto status = pendingMediaStatuses.first();
0352         locker.unlock();
0353         if (!doStep(status, hasFrame))
0354             break;
0355         locker.relock();
0356         if (!pendingMediaStatuses.isEmpty()) {
0357             pendingMediaStatuses.removeFirst();
0358             qCDebug(lcAVPlayer) << "Step done:" << status << ", pending" << pendingMediaStatuses;
0359         }
0360     }
0361 
0362     if (pendingMediaStatuses.isEmpty()) {
0363         videoQueue.wake(false);
0364         audioQueue.wake(false);
0365         subtitleQueue.wake(false);
0366     } else {
0367         wait(false);
0368     }
0369 }
0370 
0371 bool QAVPlayerPrivate::doStep(PendingMediaStatus status, bool hasFrame)
0372 {
0373     bool result = false;
0374     const bool valid = hasFrame && !isSeeking() && q_ptr->mediaStatus() != QAVPlayer::NoMedia;
0375     switch (status) {
0376         case PlayingMedia:
0377             if (valid) {
0378                 result = true;
0379                 qCDebug(lcAVPlayer) << "Played from pos:" << q_ptr->position();
0380                 Q_EMIT q_ptr->played(q_ptr->position());
0381                 wait(false);
0382             }
0383             break;
0384 
0385         case PausingMedia:
0386             if (valid) {
0387                 result = true;
0388                 qCDebug(lcAVPlayer) << "Paused to pos:" << q_ptr->position();
0389                 Q_EMIT q_ptr->paused(q_ptr->position());
0390                 wait(true);
0391             }
0392             break;
0393 
0394         case SeekingMedia:
0395             if (valid) {
0396                 result = true;
0397                 if (q_ptr->mediaStatus() == QAVPlayer::EndOfMedia)
0398                     setMediaStatus(QAVPlayer::LoadedMedia);
0399                 qCDebug(lcAVPlayer) << "Seeked to pos:" << q_ptr->position();
0400                 Q_EMIT q_ptr->seeked(q_ptr->position());
0401                 QAVPlayer::State currState = q_ptr->state();
0402                 if (currState == QAVPlayer::PausedState || currState == QAVPlayer::StoppedState)
0403                     wait(true);
0404             }
0405             break;
0406 
0407         case StoppingMedia:
0408             if (q_ptr->mediaStatus() != QAVPlayer::NoMedia) {
0409                 result = true;
0410                 qCDebug(lcAVPlayer) << "Stopped to pos:" << q_ptr->position();
0411                 Q_EMIT q_ptr->stopped(q_ptr->position());
0412                 wait(true);
0413             }
0414             break;
0415 
0416         case SteppingMedia:
0417             result = isEndOfFile();
0418             if (valid) {
0419                 result = true;
0420                 qCDebug(lcAVPlayer) << "Stepped to pos:" << q_ptr->position();
0421                 Q_EMIT q_ptr->stepped(q_ptr->position());
0422                 wait(true);
0423             }
0424             break;
0425 
0426         case LoadingMedia:
0427             result = true;
0428             setMediaStatus(QAVPlayer::LoadedMedia);
0429             break;
0430 
0431         case EndOfMedia:
0432             result = true;
0433             setMediaStatus(QAVPlayer::EndOfMedia);
0434             break;
0435 
0436         default:
0437             break;
0438     }
0439 
0440     // The step is finished but queues are empty => no more frames will be sent.
0441     // Need to skip current status and move to next to prevent the blocking.
0442     if (!result
0443         && demuxer.eof()
0444         && videoQueue.isEmpty()
0445         && audioQueue.isEmpty()
0446         && filters.isEmpty()
0447         && !isSeeking())
0448     {
0449         result = true;
0450         qCDebug(lcAVPlayer) << __FUNCTION__ << ": EndOfMedia -> skipping:" << status;
0451     }
0452 
0453     return result;
0454 }
0455 
0456 void QAVPlayerPrivate::doWait()
0457 {
0458     QMutexLocker lock(&waitMutex);
0459     if (isWaiting)
0460         waitCond.wait(&waitMutex);
0461 }
0462 
0463 void QAVPlayerPrivate::wait(bool v)
0464 {
0465     {
0466         QMutexLocker locker(&waitMutex);
0467         if (isWaiting != v)
0468             qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << isWaiting << "->" << v;
0469         isWaiting = v;
0470     }
0471 
0472     if (!v) {
0473         startDemuxing = true;
0474         waitCond.wakeAll();
0475     }
0476     videoQueue.wake(true);
0477     audioQueue.wake(true);
0478     subtitleQueue.wake(true);
0479 }
0480 
0481 void QAVPlayerPrivate::applyFilters()
0482 {
0483     applyFilters(false, {});
0484 }
0485 
0486 void QAVPlayerPrivate::applyFilters(bool reset, const QAVFrame &frame)
0487 {
0488     if ((filterDescs == filters.filterDescs()) && !reset)
0489         return;
0490     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << filters.filterDescs() << "->" << filterDescs << "reset:" << reset;
0491     int ret = filters.createFilters(filterDescs, frame, demuxer);
0492     if (ret < 0) {
0493         setError(QAVPlayer::FilterError, QLatin1String("Could not create filters: ") + err_str(ret));
0494         return;
0495     }
0496     videoQueue.clearFrames();
0497     audioQueue.clearFrames();
0498     if (error == QAVPlayer::FilterError)
0499         setMediaStatus(QAVPlayer::LoadedMedia);
0500 }
0501 
0502 void QAVPlayerPrivate::doLoad()
0503 {
0504     demuxer.abort(false);
0505     demuxer.unload();
0506     int ret = demuxer.load(url, dev.get());
0507     if (ret < 0) {
0508         setError(QAVPlayer::ResourceError, err_str(ret));
0509         return;
0510     }
0511 
0512     if (demuxer.currentVideoStreams().isEmpty() && demuxer.currentAudioStreams().isEmpty()) {
0513         setError(QAVPlayer::ResourceError, QLatin1String("No codecs found"));
0514         return;
0515     }
0516 
0517     applyFilters(true, {});
0518     dispatch([this] {
0519         qCDebug(lcAVPlayer) << "[" << url << "]: Loaded, seekable:" << demuxer.seekable() << ", duration:" << demuxer.duration();
0520         setSeekable(demuxer.seekable());
0521         setDuration(demuxer.duration());
0522         setVideoFrameRate(demuxer.videoFrameRate());
0523         step(false);
0524     });
0525 
0526 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0527     demuxerFuture = QtConcurrent::run(&threadPool, this, &QAVPlayerPrivate::doDemux);
0528     if (!q_ptr->availableVideoStreams().isEmpty())
0529         videoPlayFuture = QtConcurrent::run(&threadPool, this, &QAVPlayerPrivate::doPlayVideo);
0530     if (!q_ptr->availableAudioStreams().isEmpty())
0531         audioPlayFuture = QtConcurrent::run(&threadPool, this, &QAVPlayerPrivate::doPlayAudio);
0532     if (!q_ptr->availableSubtitleStreams().isEmpty())
0533         subtitlePlayFuture = QtConcurrent::run(&threadPool, this, &QAVPlayerPrivate::doPlaySubtitle);
0534 #else
0535     demuxerFuture = QtConcurrent::run(&threadPool, &QAVPlayerPrivate::doDemux, this);
0536     if (!q_ptr->availableVideoStreams().isEmpty())
0537         videoPlayFuture = QtConcurrent::run(&threadPool, &QAVPlayerPrivate::doPlayVideo, this);
0538     if (!q_ptr->availableAudioStreams().isEmpty())
0539         audioPlayFuture = QtConcurrent::run(&threadPool, &QAVPlayerPrivate::doPlayAudio, this);
0540     if (!q_ptr->availableSubtitleStreams().isEmpty())
0541         subtitlePlayFuture = QtConcurrent::run(&threadPool, &QAVPlayerPrivate::doPlaySubtitle, this);
0542 #endif
0543     qCDebug(lcAVPlayer) << __FUNCTION__ << "finished";
0544 }
0545 
0546 void QAVPlayerPrivate::doDemux()
0547 {
0548     const int maxQueueBytes = 15 * 1024 * 1024;
0549     QMutex waiterMutex;
0550     QWaitCondition waiter;
0551 
0552     while (!quit) {
0553         if (videoQueue.bytes() + audioQueue.bytes() > maxQueueBytes
0554             || (videoQueue.enough() && audioQueue.enough())
0555             || !startDemuxing)
0556         {
0557             QMutexLocker locker(&waiterMutex);
0558             waiter.wait(&waiterMutex, 10);
0559             continue;
0560         }
0561 
0562         {
0563             QMutexLocker locker(&positionMutex);
0564             if (pendingSeek) {
0565                 if (pendingPosition < 0)
0566                     pendingPosition += demuxer.duration();
0567                 if (pendingPosition < 0)
0568                     pendingPosition = 0;
0569                 const double pos = pendingPosition;
0570                 locker.unlock();
0571                 qCDebug(lcAVPlayer) << "Seeking to pos:" << pos * 1000;
0572                 int ret = demuxer.seek(pos);
0573                 if (ret >= 0) {
0574                     qCDebug(lcAVPlayer) << "Waiting video thread finished processing packets";
0575                     videoQueue.waitForEmpty();
0576                     videoClock.clear();
0577                     qCDebug(lcAVPlayer) << "Waiting audio thread finished processing packets";
0578                     audioQueue.waitForEmpty();
0579                     audioClock.clear();
0580                     qCDebug(lcAVPlayer) << "Waiting subtitle thread finished processing packets";
0581                     subtitleQueue.waitForEmpty();
0582                     subtitleClock.clear();
0583                     qCDebug(lcAVPlayer) << "Flush codec buffers";
0584                     demuxer.flushCodecBuffers();
0585                     qCDebug(lcAVPlayer) << "Reset filters";
0586                     applyFilters(true, {});
0587                     qCDebug(lcAVPlayer) << "Start reading packets from" << pos * 1000;
0588                 } else {
0589                     qWarning() << "Could not seek:" << ret << ":" << err_str(ret);
0590                 }
0591                 locker.relock();
0592                 if (qFuzzyCompare(pendingPosition, pos))
0593                     pendingSeek = false;
0594             }
0595         }
0596 
0597         auto packet = demuxer.read();
0598         if (packet.stream()) {
0599             endOfFile(false);
0600             // Empty packet points to EOF and it needs to flush codecs
0601             switch (demuxer.currentCodecType(packet.packet()->stream_index)) {
0602                 case AVMEDIA_TYPE_VIDEO:
0603                     videoQueue.enqueue(packet);
0604                     break;
0605                 case AVMEDIA_TYPE_AUDIO:
0606                     audioQueue.enqueue(packet);
0607                     break;
0608                 case AVMEDIA_TYPE_SUBTITLE:
0609                     subtitleQueue.enqueue(packet);
0610                     break;
0611                 default:
0612                     break;
0613             }
0614         } else {
0615             if (demuxer.eof()
0616                 && videoQueue.isEmpty()
0617                 && audioQueue.isEmpty()
0618                 && subtitleQueue.isEmpty()
0619                 && filters.isEmpty()
0620                 && !isEndOfFile())
0621             {
0622                 filters.flush();
0623                 endOfFile(true);
0624                 qCDebug(lcAVPlayer) << "EndOfMedia";
0625                 setPendingMediaStatus(EndOfMedia);
0626                 q_ptr->stop();
0627                 wait(false);
0628             }
0629 
0630             QMutexLocker locker(&waiterMutex);
0631             waiter.wait(&waiterMutex, 10);
0632         }
0633     }
0634     qCDebug(lcAVPlayer) << __FUNCTION__ << "finished";
0635 }
0636 
0637 static double streamDuration(const QAVStreamFrame &frame, const QAVDemuxer &demuxer)
0638 {
0639     double duration = demuxer.duration();
0640     const double stream_duration = frame.stream().duration();
0641     if (stream_duration > 0 && stream_duration < duration)
0642         duration = stream_duration;
0643     return duration;
0644 }
0645 
0646 static bool isLastFrame(const QAVStreamFrame &frame, const QAVDemuxer &demuxer)
0647 {
0648     bool result = false;
0649     if (!isnan(frame.duration()) && frame.duration() > 0) {
0650         const double requestedPos = streamDuration(frame, demuxer);
0651         const int frameNumber = frame.pts() / frame.duration();
0652         const int requestedFrameNumber = requestedPos / frame.duration();
0653         result = frameNumber + 1 >= requestedFrameNumber;
0654     }
0655     return result;
0656 }
0657 
0658 bool QAVPlayerPrivate::skipFrame(
0659     bool master,
0660     const QAVStreamFrame &frame,
0661     bool isEmpty)
0662 {
0663     QMutexLocker locker(&positionMutex);
0664     bool result = pendingSeek;
0665     if (!pendingSeek && pendingPosition > 0) {
0666         const bool isQueueEOF = demuxer.eof() && isEmpty;
0667         // Assume that no frames will be sent after this duration
0668         const double duration = streamDuration(frame, demuxer);
0669         const double requestedPos = qMin(pendingPosition, duration);
0670         double pos = frame.pts();
0671         // Show last frame if seeked to duration
0672         bool lastFrame = false;
0673         if (pendingPosition >= duration) {
0674             pos += frame.duration();
0675             // Additional check if frame rate has been changed,
0676             // thus last frame could be far away from duration by pts,
0677             // but frame number points to the latest frame.
0678             lastFrame = isLastFrame(frame, demuxer);
0679         }
0680         result = pos < requestedPos && !isQueueEOF && !lastFrame;
0681         if (master) {
0682             if (result)
0683                 qCDebug(lcAVPlayer) << __FUNCTION__ << pos << "<" << requestedPos;
0684             else
0685                 pendingPosition = 0;
0686         }
0687     }
0688 
0689     return result;
0690 }
0691 
0692 void QAVPlayerPrivate::doPlayStep(
0693     bool &master,
0694     double refPts,
0695     QAVQueueClock &clock,
0696     QAVPacketQueue<QAVFrame> &queue,
0697     bool &sync,
0698     const std::function<void(const QAVFrame &frame)> &cb)
0699 {
0700     doWait();
0701 
0702     // 1. Decode a frame
0703     QAVFrame decodedFrame;
0704     queue.frontFrame(decodedFrame);
0705     bool flushEvents = false;
0706     int ret = 0;
0707 
0708     // Determine if current thread is handling events and pts
0709     if (decodedFrame)
0710         master = demuxer.isMasterStream(decodedFrame.stream());
0711 
0712     // 2. Filter decoded frame
0713     QList<QAVFrame> filteredFrames;
0714     if (decodedFrame)
0715         ret = filters.write(queue.mediaType(), decodedFrame);
0716     if (ret >= 0 || ret == AVERROR(EAGAIN))
0717         ret = filters.read(queue.mediaType(), decodedFrame, filteredFrames);
0718     if (ret < 0 && ret != AVERROR(EAGAIN)) {
0719         // Try filters again
0720         filteredFrames.clear();
0721         if (ret != AVERROR(ENOTSUP)) {
0722             setError(QAVPlayer::FilterError, err_str(ret));
0723             return;
0724         }
0725         applyFilters(true, decodedFrame);
0726     } else {
0727         // The frame is already filtered, decode next one
0728         queue.popFrame();
0729     }
0730 
0731     // 3. Sync filtered frames
0732     while (!quit && !filteredFrames.isEmpty()) {
0733         auto &frame = filteredFrames.front();
0734         Q_ASSERT(frame);
0735         if (clock.wait(
0736                 synced ? sync : synced,
0737                 frame.pts(),
0738                 q_ptr->speed(),
0739                 refPts))
0740         {
0741             sync = !skipFrame(master, frame, queue.isEmpty());
0742             if (sync) {
0743                 if (master)
0744                     setPts(frame.pts());
0745                 if (!flushEvents)
0746                     flushEvents = true;
0747                 cb(frame);
0748                 demuxer.onFrameSent(frame);
0749             }
0750             filteredFrames.pop_front();
0751         } else {
0752             flushEvents = isLastFrame(frame, demuxer);
0753         }
0754     }
0755 
0756     if (master)
0757         step(flushEvents);
0758 }
0759 
0760 void QAVPlayerPrivate::doPlayVideo()
0761 {
0762     videoClock.setFrameRate(demuxer.videoFrameRate());
0763     bool master = true;
0764     bool sync = true;
0765 
0766     while (!quit) {
0767         doPlayStep(
0768             master,
0769             !demuxer.currentAudioStreams().isEmpty() ? audioClock.pts() : -1,
0770             videoClock,
0771             videoQueue,
0772             sync,
0773             [&](const QAVFrame &frame) { Q_EMIT q_ptr->videoFrame(frame); }
0774         );
0775     }
0776 
0777     videoQueue.clear();
0778     videoClock.clear();
0779     setMediaStatus(QAVPlayer::NoMedia);
0780     qCDebug(lcAVPlayer) << __FUNCTION__ << "finished";
0781 }
0782 
0783 void QAVPlayerPrivate::doPlayAudio()
0784 {
0785     bool master = false;
0786     const double ref = -1;
0787     bool sync = true;
0788 
0789     while (!quit) {
0790         doPlayStep(
0791             master,
0792             ref,
0793             audioClock,
0794             audioQueue,
0795             sync,
0796             [this](const QAVFrame &frame) {
0797                 frame.frame()->sample_rate *= q_ptr->speed();
0798                 Q_EMIT q_ptr->audioFrame(frame);
0799             }
0800         );
0801     }
0802 
0803     audioQueue.clear();
0804     audioClock.clear();
0805     if (master)
0806         setMediaStatus(QAVPlayer::NoMedia);
0807     qCDebug(lcAVPlayer) << __FUNCTION__ << "finished";
0808 }
0809 
0810 void QAVPlayerPrivate::doPlayStep(
0811     QAVQueueClock &clock,
0812     QAVPacketQueue<QAVSubtitleFrame> &queue,
0813     bool &sync,
0814     const std::function<void(const QAVSubtitleFrame &frame)> &cb)
0815 {
0816     doWait();
0817 
0818     // 1. Decode a frame
0819     QAVSubtitleFrame decodedFrame;
0820     if (!queue.frontFrame(decodedFrame))
0821         return;
0822 
0823     // 2. Sync decoded frame
0824     if (clock.wait(
0825             synced ? sync : synced,
0826             decodedFrame.pts(),
0827             q_ptr->speed(),
0828             -1))
0829     {
0830         sync = !skipFrame(false, decodedFrame, queue.isEmpty());
0831         if (sync && decodedFrame) {
0832             cb(decodedFrame);
0833             demuxer.onFrameSent(decodedFrame);
0834         }
0835         queue.popFrame();
0836     }
0837 }
0838 
0839 void QAVPlayerPrivate::doPlaySubtitle()
0840 {
0841     bool sync = true;
0842     while (!quit) {
0843         doPlayStep(
0844             subtitleClock,
0845             subtitleQueue,
0846             sync,
0847             [this](const QAVSubtitleFrame &frame) { Q_EMIT q_ptr->subtitleFrame(frame); }
0848         );
0849     }
0850 
0851     subtitleQueue.clear();
0852     subtitleClock.clear();
0853     qCDebug(lcAVPlayer) << __FUNCTION__ << "finished";
0854 }
0855 
0856 QAVPlayer::QAVPlayer(QObject *parent)
0857     : QObject(parent)
0858     , d_ptr(new QAVPlayerPrivate(this))
0859 {
0860     qRegisterMetaType<QAVAudioFrame>();
0861     qRegisterMetaType<QAVVideoFrame>();
0862     qRegisterMetaType<QAVSubtitleFrame>();
0863     qRegisterMetaType<State>();
0864     qRegisterMetaType<MediaStatus>();
0865     qRegisterMetaType<Error>();
0866     qRegisterMetaType<QAVStream>();
0867 }
0868 
0869 QAVPlayer::~QAVPlayer()
0870 {
0871     Q_D(QAVPlayer);
0872     d->terminate();
0873 }
0874 
0875 void QAVPlayer::setSource(const QString &url, const QSharedPointer<QAVIODevice> &dev)
0876 {
0877     Q_D(QAVPlayer);
0878     if (d->url == url)
0879         return;
0880 
0881     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << url;
0882 
0883     d->terminate();
0884     d->url = url;
0885     d->dev = dev;
0886     Q_EMIT sourceChanged(url);
0887     d->wait(true);
0888     d->quit = false;
0889     if (url.isEmpty())
0890         return;
0891 
0892     d->setPendingMediaStatus(LoadingMedia);
0893 
0894 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0895     d->loaderFuture = QtConcurrent::run(&d->threadPool, d, &QAVPlayerPrivate::doLoad);
0896 #else
0897     d->loaderFuture = QtConcurrent::run(&d->threadPool, &QAVPlayerPrivate::doLoad, d);
0898 #endif
0899 }
0900 
0901 QString QAVPlayer::source() const
0902 {
0903     return d_func()->url;
0904 }
0905 
0906 QList<QAVStream> QAVPlayer::availableVideoStreams() const
0907 {
0908     Q_D(const QAVPlayer);
0909     return d->demuxer.availableVideoStreams();
0910 }
0911 
0912 QList<QAVStream> QAVPlayer::currentVideoStreams() const
0913 {
0914     Q_D(const QAVPlayer);
0915     return d->demuxer.currentVideoStreams();
0916 }
0917 
0918 void QAVPlayer::setVideoStream(const QAVStream &stream)
0919 {
0920     Q_D(QAVPlayer);
0921     if (d->demuxer.currentVideoStreams() == QList<QAVStream>({stream}))
0922         return;
0923     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << d->demuxer.currentVideoStreams() << "->" << stream.index();
0924     if (d->demuxer.setVideoStreams({stream}))
0925         Q_EMIT videoStreamsChanged(d->demuxer.currentVideoStreams());
0926 }
0927 
0928 void QAVPlayer::setVideoStreams(const QList<QAVStream> &streams)
0929 {
0930     Q_D(QAVPlayer);
0931     if (d->demuxer.currentVideoStreams() == streams)
0932         return;
0933     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << d->demuxer.currentVideoStreams() << "->" << streams;
0934     if (d->demuxer.setVideoStreams(streams))
0935         Q_EMIT videoStreamsChanged(d->demuxer.currentVideoStreams());
0936 }
0937 
0938 QList<QAVStream> QAVPlayer::availableAudioStreams() const
0939 {
0940     Q_D(const QAVPlayer);
0941     return d->demuxer.availableAudioStreams();
0942 }
0943 
0944 QList<QAVStream> QAVPlayer::currentAudioStreams() const
0945 {
0946     Q_D(const QAVPlayer);
0947     return d->demuxer.currentAudioStreams();
0948 }
0949 
0950 void QAVPlayer::setAudioStream(const QAVStream &stream)
0951 {
0952     Q_D(QAVPlayer);
0953     if (d->demuxer.currentAudioStreams() == QList<QAVStream>({stream}))
0954         return;
0955     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << d->demuxer.currentAudioStreams() << "->" << stream.index();
0956     if (d->demuxer.setAudioStreams({stream}))
0957         Q_EMIT audioStreamsChanged(d->demuxer.currentAudioStreams());
0958 }
0959 
0960 void QAVPlayer::setAudioStreams(const QList<QAVStream> &streams)
0961 {
0962     Q_D(QAVPlayer);
0963     if (d->demuxer.currentAudioStreams() == streams)
0964         return;
0965     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << d->demuxer.currentAudioStreams() << "->" << streams;
0966     if (d->demuxer.setAudioStreams(streams))
0967         Q_EMIT audioStreamsChanged(d->demuxer.currentAudioStreams());
0968 }
0969 
0970 QList<QAVStream> QAVPlayer::availableSubtitleStreams() const
0971 {
0972     Q_D(const QAVPlayer);
0973     return d->demuxer.availableSubtitleStreams();
0974 }
0975 
0976 QList<QAVStream> QAVPlayer::currentSubtitleStreams() const
0977 {
0978     Q_D(const QAVPlayer);
0979     return d->demuxer.currentSubtitleStreams();
0980 }
0981 
0982 void QAVPlayer::setSubtitleStream(const QAVStream &stream)
0983 {
0984     Q_D(QAVPlayer);
0985     if (d->demuxer.currentSubtitleStreams() == QList<QAVStream>({stream}))
0986         return;
0987     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << d->demuxer.currentSubtitleStreams() << "->" << stream.index();
0988     if (d->demuxer.setSubtitleStreams({stream}))
0989         Q_EMIT subtitleStreamsChanged(d->demuxer.currentSubtitleStreams());
0990 }
0991 
0992 void QAVPlayer::setSubtitleStreams(const QList<QAVStream> &streams)
0993 {
0994     Q_D(QAVPlayer);
0995     if (d->demuxer.currentSubtitleStreams() == streams)
0996         return;
0997     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << d->demuxer.currentSubtitleStreams() << "->" << streams;
0998     if (d->demuxer.setSubtitleStreams(streams))
0999         Q_EMIT subtitleStreamsChanged(d->demuxer.currentSubtitleStreams());
1000 }
1001 
1002 QAVPlayer::State QAVPlayer::state() const
1003 {
1004     Q_D(const QAVPlayer);
1005     QMutexLocker locker(&d->stateMutex);
1006     return d->state;
1007 }
1008 
1009 QAVPlayer::MediaStatus QAVPlayer::mediaStatus() const
1010 {
1011     Q_D(const QAVPlayer);
1012     QMutexLocker locker(&d->stateMutex);
1013     return d->mediaStatus;
1014 }
1015 
1016 void QAVPlayer::play()
1017 {
1018     Q_D(QAVPlayer);
1019     if (d->url.isEmpty() || d->currentError() == QAVPlayer::ResourceError)
1020         return;
1021 
1022     qCDebug(lcAVPlayer) << __FUNCTION__;
1023     if (d->setState(QAVPlayer::PlayingState)) {
1024         if (d->isEndOfFile()) {
1025             qCDebug(lcAVPlayer) << "Playing from beginning";
1026             seek(0);
1027         }
1028         d->setPendingMediaStatus(PlayingMedia);
1029     }
1030     d->wait(false);
1031     if (mediaStatus() != QAVPlayer::NoMedia)
1032         d->applyFilters();
1033 }
1034 
1035 void QAVPlayer::pause()
1036 {
1037     Q_D(QAVPlayer);
1038     if (d->currentError() == QAVPlayer::ResourceError)
1039         return;
1040 
1041     qCDebug(lcAVPlayer) << __FUNCTION__;
1042     if (d->setState(QAVPlayer::PausedState)) {
1043         if (d->isEndOfFile()) {
1044             qCDebug(lcAVPlayer) << "Pausing from beginning";
1045             seek(0);
1046         }
1047         d->setPendingMediaStatus(PausingMedia);
1048         d->wait(false);
1049     } else {
1050         d->wait(true);
1051     }
1052     if (mediaStatus() != QAVPlayer::NoMedia)
1053         d->applyFilters();
1054 }
1055 
1056 void QAVPlayer::stop()
1057 {
1058     Q_D(QAVPlayer);
1059     if (d->currentError() == QAVPlayer::ResourceError)
1060         return;
1061 
1062     qCDebug(lcAVPlayer) << __FUNCTION__;
1063     if (d->setState(QAVPlayer::StoppedState)) {
1064         d->setPendingMediaStatus(StoppingMedia);
1065         d->wait(false);
1066     } else {
1067         d->wait(true);
1068     }
1069     if (mediaStatus() != QAVPlayer::NoMedia)
1070         d->applyFilters();
1071 }
1072 
1073 void QAVPlayer::stepForward()
1074 {
1075     Q_D(QAVPlayer);
1076     if (d->currentError() == QAVPlayer::ResourceError)
1077         return;
1078 
1079     qCDebug(lcAVPlayer) << __FUNCTION__;
1080     d->setState(QAVPlayer::PausedState);
1081     if (d->isEndOfFile()) {
1082         qCDebug(lcAVPlayer) << "Stepping from beginning";
1083         seek(0);
1084     }
1085     d->setPendingMediaStatus(SteppingMedia);
1086     d->wait(false);
1087     if (mediaStatus() != QAVPlayer::NoMedia)
1088         d->applyFilters();
1089 }
1090 
1091 void QAVPlayer::stepBackward()
1092 {
1093     Q_D(QAVPlayer);
1094     if (d->currentError() == QAVPlayer::ResourceError)
1095         return;
1096 
1097     qCDebug(lcAVPlayer) << __FUNCTION__;
1098     d->setState(QAVPlayer::PausedState);
1099     const qint64 pos = d->pts() > 0 ? (d->pts() - videoFrameRate()) * 1000 : duration();
1100     seek(pos);
1101     d->setPendingMediaStatus(SteppingMedia);
1102     d->wait(false);
1103     if (mediaStatus() != QAVPlayer::NoMedia)
1104         d->applyFilters();
1105 }
1106 
1107 bool QAVPlayer::isSeekable() const
1108 {
1109     return d_func()->seekable;
1110 }
1111 
1112 void QAVPlayer::seek(qint64 pos)
1113 {
1114     Q_D(QAVPlayer);
1115     if ((duration() > 0 && pos > duration()) || d->currentError() == QAVPlayer::ResourceError)
1116         return;
1117 
1118     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << "pos:" << pos;
1119     {
1120         QMutexLocker locker(&d->positionMutex);
1121         d->pendingSeek = true;
1122         d->pendingPosition = pos / 1000.0;
1123     }
1124 
1125     d->setPendingMediaStatus(SeekingMedia);
1126     d->wait(false);
1127     if (mediaStatus() != QAVPlayer::NoMedia)
1128         d->applyFilters();
1129 }
1130 
1131 qint64 QAVPlayer::duration() const
1132 {
1133     return d_func()->duration * 1000;
1134 }
1135 
1136 qint64 QAVPlayer::position() const
1137 {
1138     Q_D(const QAVPlayer);
1139 
1140     {
1141         QMutexLocker locker(&d->positionMutex);
1142         if (d->pendingSeek)
1143             return d->pendingPosition * 1000 + (d->pendingPosition < 0 ? duration() : 0);
1144     }
1145 
1146     if (mediaStatus() == QAVPlayer::EndOfMedia)
1147         return duration();
1148 
1149     return d->pts() * 1000;
1150 }
1151 
1152 void QAVPlayer::setSpeed(qreal r)
1153 {
1154     Q_D(QAVPlayer);
1155 
1156     {
1157         QMutexLocker locker(&d->speedMutex);
1158         if (qFuzzyCompare(d->speed, r))
1159             return;
1160 
1161         qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << d->speed << "->" << r;
1162         d->speed = r;
1163     }
1164     Q_EMIT speedChanged(r);
1165 }
1166 
1167 qreal QAVPlayer::speed() const
1168 {
1169     Q_D(const QAVPlayer);
1170 
1171     QMutexLocker locker(&d->speedMutex);
1172     return d->speed;
1173 }
1174 
1175 double QAVPlayer::videoFrameRate() const
1176 {
1177     return d_func()->videoFrameRate;
1178 }
1179 
1180 void QAVPlayer::setFilter(const QString &desc)
1181 {
1182     Q_D(QAVPlayer);
1183     {
1184         QMutexLocker locker(&d->stateMutex);
1185         if (d->filterDescs.size() == 1 && d->filterDescs.front() == desc)
1186             return;
1187 
1188         qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << d->filterDescs << "->" << desc;
1189         if (desc.isEmpty())
1190             d->filterDescs.clear();
1191         else
1192             d->filterDescs = {desc};
1193     }
1194 
1195     Q_EMIT filtersChanged({desc});
1196     if (mediaStatus() != QAVPlayer::NoMedia)
1197         d->applyFilters();
1198 }
1199 
1200 void QAVPlayer::setFilters(const QList<QString> &filters)
1201 {
1202     Q_D(QAVPlayer);
1203     {
1204         QMutexLocker locker(&d->stateMutex);
1205         qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << d->filterDescs << "->" << filters;
1206         d->filterDescs = filters;
1207     }
1208 
1209     Q_EMIT filtersChanged(filters);
1210     if (mediaStatus() != QAVPlayer::NoMedia)
1211         d->applyFilters();
1212 }
1213 
1214 QList<QString> QAVPlayer::filters() const
1215 {
1216     Q_D(const QAVPlayer);
1217     QMutexLocker locker(&d->stateMutex);
1218     return d->filterDescs;
1219 }
1220 
1221 void QAVPlayer::setBitstreamFilter(const QString &desc)
1222 {
1223     Q_D(QAVPlayer);
1224     QString bsf = d->demuxer.bitstreamFilter();
1225     if (bsf == desc)
1226         return;
1227 
1228     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << bsf << "->" << desc;
1229     int ret = d->demuxer.applyBitstreamFilter(desc);
1230     Q_EMIT bitstreamFilterChanged(desc);
1231     if (ret < 0)
1232         d->setError(QAVPlayer::FilterError, QLatin1String("Could not parse bitstream filter desc: ") + err_str(ret));
1233 }
1234 
1235 QString QAVPlayer::bitstreamFilter() const
1236 {
1237     Q_D(const QAVPlayer);
1238     return d->demuxer.bitstreamFilter();
1239 }
1240 
1241 bool QAVPlayer::isSynced() const
1242 {
1243     Q_D(const QAVPlayer);
1244     return d->synced;
1245 }
1246 
1247 void QAVPlayer::setSynced(bool sync)
1248 {
1249     Q_D(QAVPlayer);
1250     if (d->synced == sync)
1251         return;
1252 
1253     d->synced = sync;
1254     Q_EMIT syncedChanged(sync);
1255 }
1256 
1257 QString QAVPlayer::inputFormat() const
1258 {
1259     Q_D(const QAVPlayer);
1260     return d->demuxer.inputFormat();
1261 }
1262 
1263 void QAVPlayer::setInputFormat(const QString &format)
1264 {
1265     Q_D(QAVPlayer);
1266     QString current = inputFormat();
1267     if (format == current)
1268         return;
1269 
1270     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << current << "->" << format;
1271     d->demuxer.setInputFormat(format);
1272     Q_EMIT inputFormatChanged(format);
1273 }
1274 
1275 QString QAVPlayer::inputVideoCodec() const
1276 {
1277     Q_D(const QAVPlayer);
1278     return d->demuxer.inputVideoCodec();
1279 }
1280 
1281 void QAVPlayer::setInputVideoCodec(const QString &codec)
1282 {
1283     Q_D(QAVPlayer);
1284     QString current = inputVideoCodec();
1285     if (codec == current)
1286         return;
1287 
1288     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << current << "->" << codec;
1289     d->demuxer.setInputVideoCodec(codec);
1290     Q_EMIT inputVideoCodecChanged(codec);
1291 }
1292 
1293 QStringList QAVPlayer::supportedVideoCodecs()
1294 {
1295     return QAVDemuxer::supportedVideoCodecs();
1296 }
1297 
1298 QMap<QString, QString> QAVPlayer::inputOptions() const
1299 {
1300     Q_D(const QAVPlayer);
1301     return d->demuxer.inputOptions();
1302 }
1303 
1304 void QAVPlayer::setInputOptions(const QMap<QString, QString>  &opts)
1305 {
1306     Q_D(QAVPlayer);
1307     auto current = inputOptions();
1308     if (opts == current)
1309         return;
1310 
1311     qCDebug(lcAVPlayer) << __FUNCTION__ << ":" << current << "->" << opts;
1312     d->demuxer.setInputOptions(opts);
1313     Q_EMIT inputOptionsChanged(opts);
1314 }
1315 
1316 QAVStream::Progress QAVPlayer::progress(const QAVStream &s) const
1317 {
1318     return d_func()->demuxer.progress(s);
1319 }
1320 
1321 #ifndef QT_NO_DEBUG_STREAM
1322 QDebug operator<<(QDebug dbg, QAVPlayer::State state)
1323 {
1324     QDebugStateSaver saver(dbg);
1325     dbg.nospace();
1326     switch (state) {
1327         case QAVPlayer::StoppedState:
1328             return dbg << "StoppedState";
1329         case QAVPlayer::PlayingState:
1330             return dbg << "PlayingState";
1331         case QAVPlayer::PausedState:
1332             return dbg << "PausedState";
1333         default:
1334             return dbg << QString(QLatin1String("UserType(%1)" )).arg(int(state)).toLatin1().constData();
1335     }
1336 }
1337 
1338 QDebug operator<<(QDebug dbg, QAVPlayer::MediaStatus status)
1339 {
1340     QDebugStateSaver saver(dbg);
1341     dbg.nospace();
1342     switch (status) {
1343         case QAVPlayer::NoMedia:
1344             return dbg << "NoMedia";
1345         case QAVPlayer::LoadedMedia:
1346             return dbg << "LoadedMedia";
1347         case QAVPlayer::EndOfMedia:
1348             return dbg << "EndOfMedia";
1349         case QAVPlayer::InvalidMedia:
1350             return dbg << "InvalidMedia";
1351         default:
1352             return dbg << QString(QLatin1String("UserType(%1)" )).arg(int(status)).toLatin1().constData();
1353     }
1354 }
1355 
1356 QDebug operator<<(QDebug dbg, PendingMediaStatus status)
1357 {
1358     QDebugStateSaver saver(dbg);
1359     dbg.nospace();
1360     switch (status) {
1361         case LoadingMedia:
1362             return dbg << "LoadingMedia";
1363         case PlayingMedia:
1364             return dbg << "PlayingMedia";
1365         case PausingMedia:
1366             return dbg << "PausingMedia";
1367         case StoppingMedia:
1368             return dbg << "StoppingMedia";
1369         case SteppingMedia:
1370             return dbg << "SteppingMedia";
1371         case SeekingMedia:
1372             return dbg << "SeekingMedia";
1373         case EndOfMedia:
1374             return dbg << "EndOfMedia";
1375         default:
1376             return dbg << QString(QLatin1String("UserType(%1)" )).arg(int(status)).toLatin1().constData();
1377     }
1378 }
1379 
1380 QDebug operator<<(QDebug dbg, QAVPlayer::Error err)
1381 {
1382     QDebugStateSaver saver(dbg);
1383     dbg.nospace();
1384     switch (err) {
1385         case QAVPlayer::NoError:
1386             return dbg << "NoError";
1387         case QAVPlayer::ResourceError:
1388             return dbg << "ResourceError";
1389         case QAVPlayer::FilterError:
1390             return dbg << "FilterError";
1391         default:
1392             return dbg << QString(QLatin1String("UserType(%1)" )).arg(int(err)).toLatin1().constData();
1393     }
1394 }
1395 #endif
1396 
1397 Q_DECLARE_METATYPE(PendingMediaStatus)
1398 
1399 QT_END_NAMESPACE