first commit
This commit is contained in:
452
project/fm_viewer/fav/AVThread.cpp
Normal file
452
project/fm_viewer/fav/AVThread.cpp
Normal file
@@ -0,0 +1,452 @@
|
||||
/******************************************************************************
|
||||
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
|
||||
Reference in New Issue
Block a user