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

453 lines
11 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
******************************************************************************/
#include "AVThread.h"
#include <limits>
#include "AVThread_p.h"
#include "AVClock.h"
#include "AVDecoder.h"
#include "AVOutput.h"
#include "Filter.h"
#include "OutputSet.h"
#include "Logger.h"
namespace FAV {
QVariantHash AVThreadPrivate::dec_opt_framedrop;
QVariantHash AVThreadPrivate::dec_opt_normal;
AVThreadPrivate::~AVThreadPrivate() {
stop = true;
if (!paused) {
qDebug("~AVThreadPrivate wake up paused thread");
paused = false;
next_pause = false;
cond.wakeAll();
}
packets.setBlocking(true); //???
packets.clear();
QList<Filter*>::iterator it = filters.begin();
while (it != filters.end()) {
if ((*it)->isOwnedByTarget() && !(*it)->parent())
delete *it;
++it;
}
filters.clear();
}
AVThread::AVThread(QObject *parent) :
QThread(parent)
{
playerID = -1;
#if (USE_DURATION_IN_THREAD)
durationSec = 0;
#endif
#if (PREVENT_OVER_DURATION_RENDER)
duration = 0.0;
#endif
connect(this, SIGNAL(started()), SLOT(onStarted()), Qt::DirectConnection);
connect(this, SIGNAL(finished()), SLOT(onFinished()), Qt::DirectConnection);
}
AVThread::AVThread(AVThreadPrivate &d, QObject *parent)
:QThread(parent),DPTR_INIT(&d)
{
#if (USE_DURATION_IN_THREAD)
durationSec = 0;
#endif
#if (FIX_PLAYER_END_CLIP)
endStop = false;
#endif
connect(this, SIGNAL(started()), SLOT(onStarted()), Qt::DirectConnection);
connect(this, SIGNAL(finished()), SLOT(onFinished()), Qt::DirectConnection);
}
AVThread::~AVThread()
{
//d_ptr destroyed automatically
}
bool AVThread::isPaused() const
{
DPTR_D(const AVThread);
//if d.next_pause is true, the thread will pause soon, may happens before you can handle the result
return d.paused || d.next_pause;
}
bool AVThread::installFilter(Filter *filter, int index, bool lock)
{
DPTR_D(AVThread);
int p = index;
if (p < 0)
p += d.filters.size();
if (p < 0)
p = 0;
if (p > d.filters.size())
p = d.filters.size();
const int old = d.filters.indexOf(filter);
// already installed at desired position
if (p == old)
return true;
if (lock) {
QMutexLocker locker(&d.mutex);
if (p >= 0)
d.filters.removeAt(p);
d.filters.insert(p, filter);
} else {
if (p >= 0)
d.filters.removeAt(p);
d.filters.insert(p, filter);
}
return true;
}
bool AVThread::uninstallFilter(Filter *filter, bool lock)
{
DPTR_D(AVThread);
if (lock) {
QMutexLocker locker(&d.mutex);
return d.filters.removeOne(filter);
}
return d.filters.removeOne(filter);
}
const QList<Filter*>& AVThread::filters() const
{
return d_func().filters;
}
void AVThread::scheduleTask(QRunnable *task)
{
d_func().tasks.put(task);
}
void AVThread::requestSeek()
{
class SeekPTS : public QRunnable {
AVThread *self;
public:
SeekPTS(AVThread* thread) : self(thread) {}
void run() Q_DECL_OVERRIDE {
self->d_func().seek_requested = true;
}
};
scheduleTask(new SeekPTS(this));
}
void AVThread::scheduleFrameDrop(bool value)
{
class FrameDropTask : public QRunnable {
AVDecoder *decoder;
bool drop;
public:
FrameDropTask(AVDecoder *dec, bool value) : decoder(dec), drop(value) {}
void run() Q_DECL_OVERRIDE {
if (!decoder)
return;
if (drop)
decoder->setOptions(AVThreadPrivate::dec_opt_framedrop);
else
decoder->setOptions(AVThreadPrivate::dec_opt_normal);
}
};
scheduleTask(new FrameDropTask(decoder(), value));
}
qreal AVThread::previousHistoryPts() const
{
DPTR_D(const AVThread);
if (d.pts_history.empty()) {
qDebug("pts history is EMPTY");
return 0;
}
if (d.pts_history.size() == 1)
return -d.pts_history.back();
const qreal current_pts = d.pts_history.back();
for (int i = d.pts_history.size() - 2; i > 0; --i) {
if (d.pts_history.at(i) < current_pts)
return d.pts_history.at(i);
}
return -d.pts_history.front();
}
qreal AVThread::decodeFrameRate() const
{
DPTR_D(const AVThread);
if (d.pts_history.size() <= 1)
return 0;
const qreal dt = d.pts_history.back() - d.pts_history.front();
if (dt <= 0)
return 0;
return d.pts_history.size()/dt;
}
void AVThread::setDropFrameOnSeek(bool value)
{
d_func().drop_frame_seek = value;
}
// TODO: shall we close decoder here?
void AVThread::stop()
{
DPTR_D(AVThread);
d.stop = true; //stop as soon as possible
QMutexLocker locker(&d.mutex);
Q_UNUSED(locker);
d.packets.setBlocking(false); //stop blocking take()
d.packets.clear();
pause(false);
//terminate();
}
// 짧은 동영상 후반부 SEEK 처리시 PAUSED 가 풀림 ->
// UI 에서 SLIDER MOUSE PRESS 시 PAUSED 상태를 확인할때
// 이전에 이미 SEEK 가 처리되고 있으면 PAUSED 가 아닌 PLAY 상태로 인식되어
// SLIDER RELESE 후 다시 PAUSE(false) 처리한 문제였음
void AVThread::pause(bool p)
{
DPTR_D(AVThread);
if (d.paused == p)
{
return;
}
// 왜 동일한 PAUSE 가 두번씩 호출되는지 확인
// RMPlayer::onSliderPress 에서 강제 pause 1차 요청함
PLAYER_DEBUG_V2("!!!!PAUSE:",50,p,this);
// if(playerID == 0 && p == true) {
// int i = 0;
// qInfo() << i << __FUNCTION__;
// }
d.paused = p;
if (!d.paused) {
qDebug("wake up paused thread");
d.next_pause = false;
d.cond.wakeAll();
}
}
void AVThread::nextAndPause()
{
DPTR_D(AVThread);
d.next_pause = true;
d.paused = true;
d.cond.wakeAll();
}
void AVThread::lock()
{
d_func().mutex.lock();
}
void AVThread::unlock()
{
d_func().mutex.unlock();
}
void AVThread::setClock(AVClock *clock)
{
d_func().clock = clock;
}
AVClock* AVThread::clock() const
{
return d_func().clock;
}
PacketBuffer* AVThread::packetQueue() const
{
return const_cast<PacketBuffer*>(&d_func().packets);
}
void AVThread::setDecoder(AVDecoder *decoder)
{
DPTR_D(AVThread);
QMutexLocker lock(&d.mutex);
Q_UNUSED(lock);
d.dec = decoder;
}
AVDecoder* AVThread::decoder() const
{
return d_func().dec;
}
void AVThread::setOutput(AVOutput *out)
{
DPTR_D(AVThread);
QMutexLocker lock(&d.mutex);
Q_UNUSED(lock);
if (!d.outputSet)
return;
if (!out) {
d.outputSet->clearOutputs();
return;
}
d.outputSet->addOutput(out);
}
AVOutput* AVThread::output() const
{
DPTR_D(const AVThread);
if (!d.outputSet || d.outputSet->outputs().isEmpty())
return 0;
return d.outputSet->outputs().first();
}
// TODO: remove?
void AVThread::setOutputSet(OutputSet *set)
{
d_func().outputSet = set;
}
OutputSet* AVThread::outputSet() const
{
return d_func().outputSet;
}
void AVThread::onStarted()
{
d_func().sem.release();
}
void AVThread::onFinished()
{
if (d_func().sem.available() > 0)
d_func().sem.acquire(d_func().sem.available());
}
void AVThread::resetState()
{
DPTR_D(AVThread);
pause(false);
d.pts_history = ring<qreal>(d.pts_history.capacity());
d.tasks.clear();
d.render_pts0 = -1;
d.stop = false;
d.packets.setBlocking(true);
d.packets.clear();
d.wait_err = 0;
d.wait_timer.invalidate();
}
bool AVThread::tryPause(unsigned long timeout)
{
DPTR_D(AVThread);
if (!isPaused())
return false;
QMutexLocker lock(&d.mutex);
Q_UNUSED(lock);
return d.cond.wait(&d.mutex, timeout);
qDebug("paused thread waked up!!!");
return true;
}
#if (FIX_PLAYER_END_CLIP)
void AVThread::endStopBy()
{
DPTR_D(AVThread);
QMutexLocker lock(&d.mutex);
endStop = true;
}
#endif
bool AVThread::processNextTask()
{
DPTR_D(AVThread);
if (d.tasks.isEmpty())
return true;
QRunnable *task = d.tasks.take();
task->run();
if (task->autoDelete()) {
delete task;
}
return true;
}
void AVThread::setStatistics(Statistics *statistics)
{
DPTR_D(AVThread);
d.statistics = statistics;
}
bool AVThread::waitForStarted(int msec)
{
if (!d_func().sem.tryAcquire(1, msec > 0 ? msec : std::numeric_limits<int>::max()))
return false;
d_func().sem.release(1); //ensure another waitForStarted() continues
return true;
}
void AVThread::waitAndCheck(ulong value, qreal pts)
{
DPTR_D(AVThread);
if (value <= 0)
{
return;
}
if(value > 1000) {
qInfo() << __FUNCTION__ << __LINE__<< value << "WAIT>>>>>>>";
}
value += d.wait_err;
d.wait_timer.restart();
//qDebug("wating for %lu msecs", value);
ulong us = value * 1000UL;
const ulong ms = value;
static const ulong kWaitSlice = K_WAIT_SLICE * 1000UL; //20ms
while (us > kWaitSlice)
{
usleep(kWaitSlice);
if (d.stop)
us = 0;
else
us -= kWaitSlice;
if (pts > 0) {
us = qMin(us, ulong((double)(qMax<qreal>(0, pts - d.clock->value()))*1000000.0));
}
//qDebug("us: %lu/%lu, pts: %f, clock: %f", us, ms-et.elapsed(), pts, d.clock->value());
processNextTask();
us = qMin<ulong>(us, (ms-d.wait_timer.elapsed())*1000);
}
if (us > 0)
{
usleep(us);
// if(us > 100000) {
// qInfo() << "USLEEP!_____________" << us << __FUNCTION__ << __LINE__;
// }
}
//qDebug("wait elapsed: %lu %d/%lld", us, ms, et.elapsed());
const int de = ((ms-d.wait_timer.elapsed()) - d.wait_err);
if (de > -3 && de < 3)
{
d.wait_err += de;
}
else
{
d.wait_err += de > 0 ? 1 : -1;
}
//qDebug("err: %lld", d.wait_err);
}
} //namespace FAV