222 lines
6.5 KiB
C++
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
|