Revision control
Copy as Markdown
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "playbackengine/qffmpegtimecontroller_p.h"
QT_BEGIN_NAMESPACE
namespace QFFmpeg {
TimeController::TimeController()
{
sync();
}
TimeController::PlaybackRate TimeController::playbackRate() const
{
return m_playbackRate;
}
void TimeController::setPlaybackRate(PlaybackRate playbackRate)
{
if (playbackRate == m_playbackRate)
return;
Q_ASSERT(playbackRate > 0.f);
scrollTimeTillNow();
m_playbackRate = playbackRate;
if (m_softSyncData)
m_softSyncData = makeSoftSyncData(m_timePoint, m_position, m_softSyncData->dstTimePoint);
}
void TimeController::sync(TrackPosition trackPos)
{
sync(SteadyClock::now(), trackPos);
}
void TimeController::sync(TimePoint tp, TrackPosition pos)
{
m_softSyncData.reset();
m_position = pos;
m_timePoint = tp;
}
void TimeController::syncSoft(TimePoint tp, TrackPosition pos, SteadyClock::duration fixingTime)
{
const auto srcTime = SteadyClock::now();
const auto srcPos = positionFromTime(srcTime, true);
const auto dstTime = srcTime + fixingTime;
m_position = pos;
m_timePoint = tp;
m_softSyncData = makeSoftSyncData(srcTime, srcPos, dstTime);
}
TrackPosition TimeController::currentPosition(SteadyClock::duration offset) const
{
return positionFromTime(SteadyClock::now() + offset);
}
void TimeController::start()
{
m_started = true;
updateActive();
}
void TimeController::deactivate()
{
m_started = false;
m_paused = true;
updateActive();
}
void TimeController::setPaused(bool paused)
{
m_paused = paused;
updateActive();
}
void TimeController::updateActive()
{
const bool active = !m_paused && m_started;
if (m_active == active)
return;
scrollTimeTillNow();
m_active = active;
}
TrackPosition TimeController::positionFromTime(TimePoint tp, bool ignoreInactive) const
{
tp = !m_active && !ignoreInactive ? m_timePoint : tp;
if (m_softSyncData && tp < m_softSyncData->dstTimePoint) {
const PlaybackRate rate =
tp > m_softSyncData->srcTimePoint ? m_softSyncData->internalRate : m_playbackRate;
return m_softSyncData->srcPosition
+ toTrackDuration(tp - m_softSyncData->srcTimePoint, rate);
}
return positionFromTimeInternal(tp);
}
TimeController::TimePoint TimeController::timeFromPosition(TrackPosition pos,
bool ignoreInactive) const
{
auto position = !m_active && !ignoreInactive ? m_position : TrackPosition(pos);
if (m_softSyncData && position < m_softSyncData->dstPosition) {
const auto rate = position > m_softSyncData->srcPosition ? m_softSyncData->internalRate
: m_playbackRate;
return m_softSyncData->srcTimePoint
+ toClockDuration(position - m_softSyncData->srcPosition, rate);
}
return timeFromPositionInternal(position);
}
TimeController::SoftSyncData TimeController::makeSoftSyncData(const TimePoint &srcTp,
const TrackPosition &srcPos,
const TimePoint &dstTp) const
{
SoftSyncData result;
result.srcTimePoint = srcTp;
result.srcPosition = srcPos;
result.dstTimePoint = dstTp;
result.srcPosOffest = srcPos - positionFromTimeInternal(srcTp);
result.dstPosition = positionFromTimeInternal(dstTp);
result.internalRate =
static_cast<PlaybackRate>(toClockDuration(result.dstPosition - srcPos).count())
/ (dstTp - srcTp).count();
return result;
}
TrackPosition TimeController::positionFromTimeInternal(const TimePoint &tp) const
{
return m_position + toTrackDuration(tp - m_timePoint, m_playbackRate);
}
TimeController::TimePoint TimeController::timeFromPositionInternal(const TrackPosition &pos) const
{
return m_timePoint + toClockDuration(pos - m_position, m_playbackRate);
}
void TimeController::scrollTimeTillNow()
{
const auto now = SteadyClock::now();
if (m_active) {
m_position = positionFromTimeInternal(now);
// let's forget outdated syncronizations
if (m_softSyncData && m_softSyncData->dstTimePoint <= now)
m_softSyncData.reset();
} else if (m_softSyncData) {
m_softSyncData->dstTimePoint += now - m_timePoint;
m_softSyncData->srcTimePoint += now - m_timePoint;
}
m_timePoint = now;
}
SteadyClock::duration TimeController::toClockDuration(TrackDuration trackDuration, PlaybackRate rate)
{
return std::chrono::duration_cast<SteadyClock::duration>(
std::chrono::microseconds(trackDuration.get()) / rate);
}
TrackDuration TimeController::toTrackDuration(SteadyClock::duration clockDuration, PlaybackRate rate)
{
return TrackDuration(
std::chrono::duration_cast<std::chrono::microseconds>(clockDuration * rate).count());
}
} // namespace QFFmpeg
QT_END_NAMESPACE