/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2016 Wang Bin * 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 #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::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& 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(&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(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::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(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(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