Files
fmviewer3/project/fm_viewer/fav/AVClock.h
2026-02-21 17:11:31 +09:00

222 lines
6.5 KiB
C++

/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* 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 <QtCore/QAtomicInt>
#include <QtCore/QBasicTimer>
#include <QtCore/QObject>
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
#include <QtCore/QElapsedTimer>
#else
#include <QtCore/QTime>
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