/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * This file is part of QtAV This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ******************************************************************************/ #ifndef QTAV_AVCLOCK_H #define QTAV_AVCLOCK_H #include "_fav_constants.h" #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) #include #else #include typedef QTime QElapsedTimer; #endif /* * AVClock is created by AVPlayer. The only way to access AVClock is through AVPlayer::masterClock() * The default clock type is Audio's clock, i.e. vedio synchronizes to audio. If audio stream is not * detected, then the clock will set to External clock automatically. * I name it ExternalClock because the clock can be corrected outside, though it is a clock inside AVClock */ namespace FAV { static const double kThousandth = 0.001; class Q_AV_EXPORT AVClock : public QObject { Q_OBJECT public: typedef enum { AudioClock = 0, ExternalClock = 1, VideoClock = 2, //sync to video timestamp } ClockType; AVClock(ClockType c, QObject* parent = 0); AVClock(QObject* parent = 0); void setClockType(ClockType ct); ClockType clockType() const; bool isActive() const; /*! * \brief setInitialValue * Usually for ExternalClock. For example, media start time is not 0, clock have to set initial value as media start time */ void setInitialValue(double v); double initialValue() const; /* * auto clock: use audio clock if audio stream found, otherwise use external clock */ void setClockAuto(bool a); bool isClockAuto() const; /*in seconds*/ inline double pts() const; /*! * \brief value * the real timestamp in seconds: pts + delay * \return */ inline double value() const; inline void updateValue(double pts); //update the pts /*used when seeking and correcting from external*/ void updateExternalClock(qint64 msecs); /*external clock outside still running, so it's more accurate for syncing multiple clocks serially*/ void updateExternalClock(const AVClock& clock); inline void updateVideoTime(double pts); inline double videoTime() const; inline double delay() const; //playing audio spends some time inline void updateDelay(double delay); inline qreal diff() const; void setSpeed(qreal speed); inline qreal speed() const; bool isPaused() const; /*! * \brief syncStart * For internal use now * Start to sync "count" objects. Call syncEndOnce(id) "count" times to end sync. * \param count Number of objects to sync. Each one should call syncEndOnce(int id) * \return an id */ int syncStart(int count); int syncId() const {return sync_id;} /*! * \brief syncEndOnce * Decrease sync objects count if id is current sync id. * \return true if sync is end for id or id is not current sync id */ bool syncEndOnce(int id); Q_SIGNALS: void paused(bool); void paused(); //equals to paused(true) void resumed();//equals to paused(false) void started(); void resetted(); public Q_SLOTS: //these slots are not frequently used. so not inline /*start the external clock*/ void start(); /*pause external clock*/ void pause(bool p); /*reset clock intial value and external clock parameters (and stop timer). keep speed() and isClockAuto()*/ void reset(); protected: virtual void timerEvent(QTimerEvent *event); private Q_SLOTS: /// make sure QBasic timer start/stop in a right thread void restartCorrectionTimer(); void stopCorrectionTimer(); private: bool auto_clock; int m_state; ClockType clock_type; mutable double pts_; mutable double pts_v; double delay_; mutable QElapsedTimer timer; qreal mSpeed; double value0; /*! * \brief correction_schedule_timer * accumulative error is too large using QElapsedTimer.restart() frequently. * we periodically correct value() to keep the error always less * than the error of calling QElapsedTimer.restart() once * see github issue 46, 307 etc */ QBasicTimer correction_schedule_timer; qint64 t; // absolute time for elapsed timer correction static const int kCorrectionInterval = 1; // 1000ms double last_pts; double avg_err; // average error of restart() mutable int nb_restarted; QAtomicInt nb_sync; int sync_id; }; double AVClock::value() const { if (clock_type == AudioClock) { // TODO: audio clock need a timer too // timestamp from media stream is >= value0 return pts_ == 0 ? value0 : pts_ + delay_; } else if (clock_type == ExternalClock) { if (timer.isValid()) { ++nb_restarted; pts_ += (double(timer.restart()) * kThousandth + avg_err)* speed(); } else {//timer is paused //qDebug("clock is paused. return the last value %f", pts_); } return pts_ + value0; } else { return pts_v; // value0 is 1st video pts_v already } } void AVClock::updateValue(double pts) { if (clock_type == AudioClock) { pts_ = pts; } } void AVClock::updateVideoTime(double pts) { pts_v = pts; if (clock_type == VideoClock) timer.restart(); } double AVClock::videoTime() const { return pts_v; } double AVClock::delay() const { return delay_; } void AVClock::updateDelay(double delay) { delay_ = delay; } qreal AVClock::diff() const { return value() - videoTime(); } qreal AVClock::speed() const { return mSpeed; } } //namespace FAV #endif // QTAV_AVCLOCK_H