Skip to content

Commit

Permalink
stop AVThread as soon as possible. reduce lock scope
Browse files Browse the repository at this point in the history
split long time operations and check stop condition
  • Loading branch information
wang-bin committed Oct 23, 2013
1 parent e902e52 commit cd77f81
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 65 deletions.
2 changes: 1 addition & 1 deletion src/AVDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ bool AVDecoder::close()
return true;
}
DPTR_D(AVDecoder);
d.is_open = false;
// TODO: reset config?
if (!d.codec_ctx) {
qWarning("FFmpeg codec context not ready");
Expand All @@ -112,7 +113,6 @@ bool AVDecoder::close()
qWarning("failed to close decoder: %s", av_err2str(ret));
return false;
}
d.is_open = false;
return true;
}

Expand Down
7 changes: 1 addition & 6 deletions src/AVDemuxer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
******************************************************************************/

#include <QtAV/AVClock.h>
#include <QtAV/AVDemuxer.h>
#include <QtAV/Packet.h>
#include <QtAV/QtAV_Compat.h>
Expand Down Expand Up @@ -137,13 +136,9 @@ bool AVDemuxer::readFrame()
qWarning("[AVDemuxer] error: %s", av_err2str(ret));
return false;
}

stream_idx = packet.stream_index; //TODO: check index
//check whether the 1st frame is alreay got. emit only once
if (!started_ && v_codec_context && v_codec_context->frame_number == 0) {
started_ = true;
emit started();
} else if (!started_ && a_codec_context && a_codec_context->frame_number == 0) {
if (!started_) {
started_ = true;
emit started();
}
Expand Down
43 changes: 19 additions & 24 deletions src/AVPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ AVPlayer::AVPlayer(QObject *parent) :
//reenter stop() and reset start_pos. will not emit stopped() again because demuxer thread is the last finished thread(i.e. after avthread.finished())
//use direct connection otherwise replay may stop immediatly because slot stop() is called after play()
//FIXME: stop() may be called twice because of demuxer thread may emit finished() before a avthread.finished()
connect(demuxer_thread, SIGNAL(finished()), this, SLOT(stop()), Qt::DirectConnection);
//connect(demuxer_thread, SIGNAL(finished()), this, SLOT(stop()), Qt::DirectConnection);

setPlayerEventFilter(new EventFilter(this));
video_capture = new VideoCapture(this);
Expand Down Expand Up @@ -652,6 +652,7 @@ void AVPlayer::play()
* must setFile() agian to reload an unseekable stream
*/
//TODO: no eof if replay by seek(0)
#if EOF_ISSUE_SOLVED
if (!isLoaded()) { //if (!isLoaded() && !load())
if (!load(false)) {
mStatistics.reset();
Expand All @@ -660,7 +661,6 @@ void AVPlayer::play()
initStatistics();
}
} else {
#if EOF_ISSUE_SOLVED
qDebug("seek(%f)", start_pos);
demuxer.seek(start_pos); //FIXME: now assume it is seekable. for unseekable, setFile() again
#else
Expand All @@ -671,7 +671,9 @@ void AVPlayer::play()
initStatistics();
}
#endif //EOF_ISSUE_SOLVED
#if EOF_ISSUE_SOLVED
}
#endif //EOF_ISSUE_SOLVED

if (aCodecCtx && audio_thread) {
qDebug("Starting audio thread...");
Expand Down Expand Up @@ -700,7 +702,7 @@ void AVPlayer::stop()
qDebug("Not playing~");
return;
}
//blockSignals(true); //TODO: move emit stopped() before it. or connect avthread.finished() to tryEmitStop() {if (!called_by_stop) emit}

struct avthreads_t {
AVThread *thread;
const char *name;
Expand All @@ -713,33 +715,24 @@ void AVPlayer::stop()
AVThread *thread = threads[i].thread;
if (!thread)
continue;
if (thread->isRunning()) {
thread->blockSignals(true);
while (thread->isRunning()) {
qDebug("stopping %s...", threads[i].name);
//avoid emit stopped multiple times. AVThread.stopped() connects to AVDemuxThread.stopped().
thread->blockSignals(true);
thread->stop();
if (!thread->wait(1000)) {
qWarning("Timeout waiting for %s stopped. Terminate it.", threads[i].name);
thread->terminate();
}
thread->blockSignals(false);
}
thread->blockSignals(false);
}
if (audio_dec && audio_dec->isOpen())
audio_dec->close();
if (video_dec && video_dec->isOpen())
video_dec->close();

//stop demux thread after avthread is better. otherwise demux thread may be terminated when waiting for avthread ?
// stop feeding packet queue
if (demuxer_thread->isRunning()) {
qDebug("stopping demux thread...");
demuxer_thread->stop();
//wait for finish then we can safely set the vars, e.g. a/v decoders
if (!demuxer_thread->wait(1000)) {
qWarning("Timeout waiting for demux thread stopped. Terminate it.");
demuxer_thread->terminate(); //Terminate() causes the wait condition destroyed without waking up
}
}
} //blockSignals(true); //TODO: move emit stopped() before it. or connect avthread.finished() to tryEmitStop() {if (!called_by_stop) emit}

qDebug("all threads [a|v|d] stopped...");
emit stopped();
}
Expand All @@ -761,7 +754,7 @@ void AVPlayer::playNextFrame()

void AVPlayer::seek(qreal pos)
{
if (!demuxer_thread->isRunning())
if (!isPlaying())
return;
qDebug("seek %f%%", pos*100.0);
masterClock()->updateValue(pos*duration()); //what is duration == 0
Expand Down Expand Up @@ -860,7 +853,7 @@ bool AVPlayer::setupAudioThread()
if (!audio_dec) {
audio_dec = new AudioDecoder();
} else {
if (!audio_dec->close()) {
if (audio_dec->isOpen() && !audio_dec->close()) {
return false;
}
}
Expand Down Expand Up @@ -922,7 +915,7 @@ bool AVPlayer::setupAudioThread()
*audio thread will not change. so connection should be here
*/
if (!vCodecCtx) {
disconnect(audio_thread, SIGNAL(finished()), this, SIGNAL(stopped()));
disconnect(video_thread, SIGNAL(finished()), this, SIGNAL(stopped()));
connect(audio_thread, SIGNAL(finished()), this, SIGNAL(stopped()), Qt::DirectConnection);
}
int queue_min = 0.61803*qMax<qreal>(24.0, mStatistics.video.fps);
Expand All @@ -940,7 +933,7 @@ bool AVPlayer::setupVideoThread()
if (!video_dec) {
video_dec = VideoDecoderFactory::create(VideoDecoderId_FFmpeg);
} else {
if (!video_dec->close()) {
if (video_dec->isOpen() && !video_dec->close()) {
return false;
}
}
Expand All @@ -957,8 +950,7 @@ bool AVPlayer::setupVideoThread()
video_thread->setVideoCapture(video_capture);
video_thread->setOutputSet(mpVOSet);
demuxer_thread->setVideoThread(video_thread);
//reconnect if disconnected
connect(video_thread, SIGNAL(finished()), this, SIGNAL(stopped()), Qt::DirectConnection);

QList<Filter*> filters = FilterManager::instance().videoFilters(this);
if (filters.size() > 0) {
foreach (Filter *filter, filters) {
Expand All @@ -969,6 +961,9 @@ bool AVPlayer::setupVideoThread()
#if V1_2
setRenderer(_renderer);
#endif
//reconnect if disconnected
connect(video_thread, SIGNAL(finished()), this, SIGNAL(stopped()), Qt::DirectConnection);
disconnect(audio_thread, SIGNAL(finished()), this, SIGNAL(stopped()));
int queue_min = 0.61803*qMax<qreal>(24.0, mStatistics.video.fps);
int queue_max = int(1.61803*(qreal)queue_min); //about 1 second
video_thread->packetQueue()->setThreshold(queue_min);
Expand Down
11 changes: 7 additions & 4 deletions src/AVThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,17 @@ void AVThread::scheduleTask(QRunnable *task)
// TODO: shall we close decoder here?
void AVThread::stop()
{
pause(false);
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);
if (d.writer)
d.writer->pause(false); //stop waiting
d.stop = true;
//terminate();
d.packets.setBlocking(false); //stop blocking take()
d.packets.clear();

}

//TODO: output set
Expand Down
37 changes: 26 additions & 11 deletions src/AudioThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,12 @@ void AudioThread::run()
if (isPaused())
continue;
}
QMutexLocker locker(&d.mutex);
Q_UNUSED(locker);
if (d.packets.isEmpty() && !d.stop) {
d.stop = d.demux_end;
if (d.stop) {
qDebug("audio queue empty and demux end. break audio thread");
break;
}
}
if (d.stop) {
qDebug("audio thread stop before take packet");
break;
}
if (!pkt.isValid()) {
pkt = d.packets.take(); //wait to dequeue
Expand All @@ -104,13 +102,20 @@ void AudioThread::run()
* 2. use last delay when seeking
*/
if (qAbs(d.delay) < 2.718) {
if (d.delay > kSyncThreshold) { //Slow down
if (d.delay < -kSyncThreshold) { //Speed up. drop frame?
//continue;
}
while (d.delay > kSyncThreshold) { //Slow down
//d.delay_cond.wait(&d.mutex, d.delay*1000); //replay may fail. why?
//qDebug("~~~~~wating for %f msecs", d.delay*1000);
usleep(d.delay * 1000000);
} else if (d.delay < -kSyncThreshold) { //Speed up. drop frame?
//continue;
usleep(kSyncThreshold * 1000000UL);
if (d.stop)
d.delay = 0;
else
d.delay -= kSyncThreshold;
}
if (d.delay > 0)
usleep(d.delay * 1000000UL);
} else { //when to drop off?
qDebug("delay %f/%f", d.delay, d.clock->value());
if (d.delay > 0) {
Expand Down Expand Up @@ -141,6 +146,12 @@ void AudioThread::run()
}
}
}
if (d.stop) {
qDebug("audio thread stop before decode()");
break;
}
QMutexLocker locker(&d.mutex);
Q_UNUSED(locker);
if (dec->decode(pkt.data)) {
QByteArray decoded(dec->data());
int decodedSize = decoded.size();
Expand All @@ -150,6 +161,10 @@ void AudioThread::run()
AudioFormat &af = dec->resampler()->inAudioFormat();
qreal byte_rate = af.bytesPerSecond();
while (decodedSize > 0) {
if (d.stop) {
qDebug("audio thread stop after decode()");
break;
}
int chunk = qMin(decodedSize, int(max_len*byte_rate));
qreal chunk_delay = (qreal)chunk/(qreal)byte_rate;
pkt.pts += chunk_delay;
Expand Down Expand Up @@ -225,7 +240,7 @@ void AudioThread::run()
} else { //???
qWarning("Decode audio failed");
qreal dt = pkt.pts - d.last_pts;
if (abs(dt) > 0.618 || dt < 0) {
if (dt > 0.618 || dt < 0) {
dt = 0;
}
//qDebug("sleep %f", dt);
Expand Down
4 changes: 0 additions & 4 deletions src/QtAV/AVDemuxer.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ struct AVStream;
// TODO: force codec name. clean code
namespace QtAV {

class AVClock;
class Packet;
class Q_AV_EXPORT AVDemuxer : public QObject //QIODevice?
{
Expand Down Expand Up @@ -80,9 +79,6 @@ class Q_AV_EXPORT AVDemuxer : public QObject //QIODevice?
Packet* packet() const; //current readed packet
int stream() const; //current readed stream index

void setClock(AVClock *c);
AVClock *clock() const;

bool isSeekable() const;
void setSeekUnit(SeekUnit unit);
SeekUnit seekUnit() const;
Expand Down
2 changes: 1 addition & 1 deletion src/QtAV/private/AVThread_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
class QRunnable;
namespace QtAV {

const double kSyncThreshold = 0.005; // 5 ms
const double kSyncThreshold = 0.2; // 200 ms

class AVDecoder;
class AVOutput;
Expand Down
Loading

0 comments on commit cd77f81

Please sign in to comment.