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