From b971c511803e41ea42c1dc0a4b9bccc72d2ab415 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Mon, 3 Jun 2013 13:33:49 +0800 Subject: [PATCH] allow null a/v thread. demux thread exits only when a/v thread exits TODO: if demuxer read the eof packet, then it will not get that again if seek again, why? miss resetting something? --- src/AVDemuxThread.cpp | 145 ++++++++++++++++++++++++++++---------- src/AVDemuxer.cpp | 2 +- src/AVPlayer.cpp | 146 ++++++++++++++++++++++++++++----------- src/AudioThread.cpp | 1 + src/QtAV/AVDemuxThread.h | 2 + src/QtAV/AVDemuxer.h | 9 +++ src/QtAV/AVPlayer.h | 1 + src/VideoThread.cpp | 1 + 8 files changed, 230 insertions(+), 77 deletions(-) diff --git a/src/AVDemuxThread.cpp b/src/AVDemuxThread.cpp index 127d8c40..2190d689 100644 --- a/src/AVDemuxThread.cpp +++ b/src/AVDemuxThread.cpp @@ -75,23 +75,41 @@ void AVDemuxThread::setDemuxer(AVDemuxer *dmx) void AVDemuxThread::setAudioThread(AVThread *thread) { + if (audio_thread == thread) + return; if (audio_thread) { - audio_thread->stop(); + if (audio_thread->isRunning()) + audio_thread->stop(); + disconnect(audio_thread, SIGNAL(terminated()), this, SLOT(notifyEnd())); + disconnect(audio_thread, SIGNAL(finished()), this, SLOT(notifyEnd())); delete audio_thread; audio_thread = 0; } + if (!thread) + return; audio_thread = thread; + connect(audio_thread, SIGNAL(terminated()), this, SLOT(notifyEnd()), Qt::DirectConnection); + connect(audio_thread, SIGNAL(finished()), this, SLOT(notifyEnd()), Qt::DirectConnection); audio_thread->packetQueue()->setEmptyCallback(new QueueEmptyCall(this)); } void AVDemuxThread::setVideoThread(AVThread *thread) { + if (video_thread == thread) + return; if (video_thread) { - video_thread->stop(); + if (video_thread->isRunning()) + video_thread->stop(); + disconnect(video_thread, SIGNAL(terminated()), this, SLOT(notifyEnd())); + disconnect(video_thread, SIGNAL(finished()), this, SLOT(notifyEnd())); delete video_thread; video_thread = 0; } + if (!thread) + return; video_thread = thread; + connect(video_thread, SIGNAL(terminated()), this, SLOT(notifyEnd()), Qt::DirectConnection); + connect(video_thread, SIGNAL(finished()), this, SLOT(notifyEnd()), Qt::DirectConnection); video_thread->packetQueue()->setEmptyCallback(new QueueEmptyCall(this)); } @@ -107,58 +125,89 @@ AVThread* AVDemuxThread::audioThread() void AVDemuxThread::seek(qreal pos) { + qDebug("demux thread start to seek..."); seeking = true; - audio_thread->packetQueue()->clear(); - video_thread->packetQueue()->clear(); + end = false; + if (audio_thread) { + audio_thread->setDemuxEnded(false); + audio_thread->packetQueue()->clear(); + } + if (video_thread) { + video_thread->setDemuxEnded(false); + video_thread->packetQueue()->clear(); + } demuxer->seek(pos); seeking = false; seek_cond.wakeAll(); if (isPaused()) { pause(false); - video_thread->pause(false); + AVThread *avthread = video_thread; + if (!avthread) + avthread = audio_thread; + avthread->pause(false); QEventLoop loop; QTimer::singleShot(40, &loop, SLOT(quit())); loop.exec(); pause(true); - video_thread->pause(true); + avthread->pause(true); } } void AVDemuxThread::seekForward() { seeking = true; - audio_thread->packetQueue()->clear(); - video_thread->packetQueue()->clear(); + end = false; + if (audio_thread) { + audio_thread->setDemuxEnded(false); + audio_thread->packetQueue()->clear(); + } + if (video_thread) { + video_thread->setDemuxEnded(false); + video_thread->packetQueue()->clear(); + } demuxer->seekForward(); seeking = false; seek_cond.wakeAll(); if (isPaused()) { pause(false); - video_thread->pause(false); + AVThread *avthread = video_thread; + if (!avthread) + avthread = audio_thread; + avthread->pause(false); QEventLoop loop; QTimer::singleShot(40, &loop, SLOT(quit())); loop.exec(); pause(true); - video_thread->pause(true); + avthread->pause(true); } } void AVDemuxThread::seekBackward() { seeking = true; - audio_thread->packetQueue()->clear(); - video_thread->packetQueue()->clear(); + end = false; + if (audio_thread) { + audio_thread->setDemuxEnded(false); + audio_thread->packetQueue()->clear(); + } + if (video_thread) { + video_thread->setDemuxEnded(false); + video_thread->packetQueue()->clear(); + } demuxer->seekBackward(); seeking = false; seek_cond.wakeAll(); if (isPaused()) { pause(false); - video_thread->pause(false); + AVThread *avthread = video_thread; + if (!avthread) + avthread = audio_thread; + avthread->pause(false); QEventLoop loop; QTimer::singleShot(40, &loop, SLOT(quit())); loop.exec(); pause(true); - video_thread->pause(true); + avthread->pause(true); } } @@ -178,16 +227,22 @@ void AVDemuxThread::stop() qDebug("void AVDemuxThread::stop()"); end = true; //this will not affect the pause state if we pause the output - audio_thread->setDemuxEnded(true); - video_thread->setDemuxEnded(true); - //TODO: why remove the following 2 lines can not play another file? - audio_thread->packetQueue()->blockFull(false); //?? - video_thread->packetQueue()->blockFull(false); //? + //TODO: why remove blockFull(false) can not play another file? + if (audio_thread) { + audio_thread->setDemuxEnded(true); + audio_thread->packetQueue()->blockFull(false); //?? + } + if (video_thread) { + video_thread->setDemuxEnded(false); + video_thread->packetQueue()->clear(); + video_thread->packetQueue()->blockFull(false); //? + } pause(false); } void AVDemuxThread::pause(bool p) { + qDebug("demux thread pause %d", p); if (paused == p) return; paused = p; @@ -195,34 +250,50 @@ void AVDemuxThread::pause(bool p) cond.wakeAll(); } +void AVDemuxThread::notifyEnd() +{ + qDebug("avthread end. notify demux thread<<<<<<"); + end = true; + seek_cond.wakeAll(); + cond.wakeAll(); +} + void AVDemuxThread::run() { + qDebug("demux thread start running..."); end = false; //TODO: no video thread is ok - Q_ASSERT(audio_thread != 0); - Q_ASSERT(video_thread != 0); - if (!audio_thread->isRunning()) + if (audio_thread && !audio_thread->isRunning()) audio_thread->start(QThread::HighPriority); - if (!video_thread->isRunning()) + if (video_thread && !video_thread->isRunning()) video_thread->start(); audio_stream = demuxer->audioStream(); video_stream = demuxer->videoStream(); int index = 0; Packet pkt; - end = false; pause(false); - PacketQueue *aqueue = audio_thread->packetQueue(); - PacketQueue *vqueue = video_thread->packetQueue(); - aqueue->setBlocking(true); - vqueue->setBlocking(true); - bool _has_audio = audio_thread->decoder()->isAvailable(); - bool _has_video = video_thread->decoder()->isAvailable(); - while (!end) { + qDebug("get av queue"); + PacketQueue *aqueue = audio_thread ? audio_thread->packetQueue() : 0; + PacketQueue *vqueue = video_thread ? video_thread->packetQueue() : 0; + if (aqueue) + aqueue->setBlocking(true); + if (vqueue) + vqueue->setBlocking(true); + while (true) { if (tryPause()) continue; //the queue is empty and will block QMutexLocker locker(&buffer_mutex); Q_UNUSED(locker); + if (end) { + if ((audio_thread && audio_thread->isRunning()) + || (video_thread && video_thread->isRunning())) { + qDebug("demux read end but avthread still running. waiting for finish..."); + seek_cond.wait(&buffer_mutex); + } + if (end) + break; + } if (seeking) { qDebug("Demuxer is seeking... wait for seek end"); if (!seek_cond.wait(&buffer_mutex, 1000)) { //will return the same state(i.e. lock) @@ -255,13 +326,17 @@ void AVDemuxThread::run() /* if vqueue if not blocked and full, and aqueue is empty, then put to * vqueue will block demuex thread */ - if (_has_video) + if (aqueue) { + qDebug("put aqueue"); aqueue->blockFull(vqueue->size() >= vqueue->threshold()); - aqueue->put(pkt); //affect video_thread + aqueue->put(pkt); //affect video_thread + } } else if (index == video_stream) { - if (_has_audio) + if (vqueue) { + qDebug("put vqueue"); vqueue->blockFull(aqueue->size() >= aqueue->threshold()); - vqueue->put(pkt); //affect audio_thread + vqueue->put(pkt); //affect audio_thread + } } else { //subtitle continue; } diff --git a/src/AVDemuxer.cpp b/src/AVDemuxer.cpp index 0a67c43a..f75896f9 100644 --- a/src/AVDemuxer.cpp +++ b/src/AVDemuxer.cpp @@ -344,7 +344,7 @@ bool AVDemuxer::loadFile(const QString &fileName) if (ret < 0) { //if (avformat_open_input(&format_context, qPrintable(filename), NULL, NULL)) { - qWarning("Can't open video: %s", av_err2str(ret)); + qWarning("Can't open media: %s", av_err2str(ret)); return false; } format_context->flags |= AVFMT_FLAG_GENPTS; diff --git a/src/AVPlayer.cpp b/src/AVPlayer.cpp index 13985e3b..280f24c8 100644 --- a/src/AVPlayer.cpp +++ b/src/AVPlayer.cpp @@ -53,6 +53,10 @@ namespace QtAV { AVPlayer::AVPlayer(QObject *parent) : QObject(parent),loaded(false),capture_dir("capture"),_renderer(0),_audio(0) + , audio_dec(0) + , video_dec(0) + , audio_thread(0) + , video_thread(0) , event_filter(0),video_capture(0) { qDebug("%s", aboutQtAV_PlainText().toUtf8().constData()); @@ -65,33 +69,9 @@ AVPlayer::AVPlayer(QObject *parent) : //clock->setClockType(AVClock::ExternalClock); demuxer.setClock(clock); connect(&demuxer, SIGNAL(started()), clock, SLOT(start())); -#if HAVE_OPENAL - _audio = new AOOpenAL(); -#elif HAVE_PORTAUDIO - _audio = new AOPortAudio(); -#endif - if (_audio) { - _audio->setStatistics(&mStatistics); - } - audio_dec = new AudioDecoder(); - audio_thread = new AudioThread(this); - audio_thread->setClock(clock); - //audio_thread->setPacketQueue(&audio_queue); - audio_thread->setDecoder(audio_dec); - audio_thread->setOutput(_audio); - audio_thread->setStatistics(&mStatistics); - - video_dec = new VideoDecoder(); - - video_thread = new VideoThread(this); - video_thread->setClock(clock); - video_thread->setDecoder(video_dec); - video_thread->setStatistics(&mStatistics); demuxer_thread = new AVDemuxThread(this); demuxer_thread->setDemuxer(&demuxer); - demuxer_thread->setAudioThread(audio_thread); - demuxer_thread->setVideoThread(video_thread); setPlayerEventFilter(new EventFilter(this)); setVideoCapture(new VideoCapture()); @@ -122,13 +102,23 @@ AVClock* AVPlayer::masterClock() //TODO: check components compatiblity(also when change the filter chain) VideoRenderer* AVPlayer::setRenderer(VideoRenderer *r) { + qDebug(">>>>>>>>>>>>>>>AVPlayer::setRenderer"); + if (r == _renderer) { + qDebug("renderer not changed"); + if (video_thread && video_thread->output() == r) + return 0; + } VideoRenderer *old = _renderer; _renderer = r; + if (!video_thread) { + qDebug("video thread not ready. can not set output."); + return 0; + } //FIXME: what if isPaused()==false but pause(true) in another thread? bool need_lock = isPlaying() && !video_thread->isPaused(); if (need_lock) video_thread->lock(); - + qDebug("set video thread renderer"); video_thread->setOutput(_renderer); if (_renderer) { _renderer->setStatistics(&mStatistics); @@ -136,6 +126,7 @@ VideoRenderer* AVPlayer::setRenderer(VideoRenderer *r) video_thread->unlock(); _renderer->resizeRenderer(_renderer->rendererSize()); //IMPORTANT: the swscaler will resize } + //old may be in stack. do not delete here return old; } @@ -144,6 +135,11 @@ VideoRenderer *AVPlayer::renderer() return _renderer; } +AudioOutput* AVPlayer::setAudioOutput(AudioOutput* ao) +{ + +} + AudioOutput* AVPlayer::audio() { return _audio; @@ -210,7 +206,6 @@ VideoCapture* AVPlayer::setVideoCapture(VideoCapture *cap) { VideoCapture *old = video_capture; video_capture = cap; - video_thread->setVideoCapture(video_capture); return old; } @@ -231,7 +226,7 @@ void AVPlayer::setCaptureSaveDir(const QString &dir) bool AVPlayer::captureVideo() { - if (!video_capture) + if (!video_capture || !video_thread) return false; bool pause_old = isPaused(); if (!video_capture->isAsync()) @@ -260,7 +255,9 @@ bool AVPlayer::play(const QString& path) bool AVPlayer::isPlaying() const { - return demuxer_thread->isRunning() || audio_thread->isRunning() || video_thread->isRunning(); + return (demuxer_thread &&demuxer_thread->isRunning()) + || (audio_thread && audio_thread->isRunning()) + || (video_thread && video_thread->isRunning()); } void AVPlayer::togglePause() @@ -327,15 +324,77 @@ bool AVPlayer::load() formatCtx = demuxer.formatContext(); aCodecCtx = demuxer.audioCodecContext(); vCodecCtx = demuxer.videoCodecContext(); - if (_audio && aCodecCtx) { + if (aCodecCtx) { + qDebug("has audio"); + if (!audio_dec) { + audio_dec = new AudioDecoder(); + } + qDebug("setCodecContext"); + audio_dec->setCodecContext(aCodecCtx); + //TODO: setAudioOutput() like vo + if (!_audio) { + qDebug("new audio output"); +#if HAVE_OPENAL + _audio = new AOOpenAL(); +#elif HAVE_PORTAUDIO + _audio = new AOPortAudio(); +#endif + _audio->setStatistics(&mStatistics); + } _audio->setSampleRate(aCodecCtx->sample_rate); _audio->setChannels(aCodecCtx->channels); if (!_audio->open()) { //return; //audio not ready } + if (!audio_thread) { + qDebug("new audio thread"); + audio_thread = new AudioThread(this); + audio_thread->setClock(clock); + //audio_thread->setPacketQueue(&audio_queue); + audio_thread->setDecoder(audio_dec); + audio_thread->setOutput(_audio); + audio_thread->setStatistics(&mStatistics); + } + } else { + //releaseAudioResource() + if (audio_thread) { + audio_thread->terminate(); + delete audio_thread; + audio_thread = 0; + } + if (audio_dec) { + delete audio_dec; + audio_dec = 0; + } + //DO NOT delete AVOutput. it is setted by user } - audio_dec->setCodecContext(aCodecCtx); - video_dec->setCodecContext(vCodecCtx); + qDebug("demux thread setAudioThread"); + demuxer_thread->setAudioThread(audio_thread); + if (vCodecCtx) { + if (!video_dec) { + video_dec = new VideoDecoder(); + } + video_dec->setCodecContext(vCodecCtx); + if (!video_thread) { + video_thread = new VideoThread(this); + video_thread->setClock(clock); + video_thread->setDecoder(video_dec); + video_thread->setVideoCapture(video_capture); + } + setRenderer(_renderer); + } else { + if (video_thread) { + video_thread->terminate(); + delete video_thread; + video_thread = 0; + } + if (video_dec) { + delete video_dec; + video_dec = 0; + } + //DO NOT delete AVOutput. it is setted by user + } + demuxer_thread->setVideoThread(video_thread); //TODO: init statistics return loaded; } @@ -374,19 +433,19 @@ void AVPlayer::play() int queue_min = 0.61803*qMax(24.0, mStatistics.video.fps); int queue_max = int(1.61803*(qreal)queue_min); //about 1 second if (vCodecCtx) { + connect(video_thread, SIGNAL(finished()), this, SIGNAL(stopped()), Qt::DirectConnection); video_thread->packetQueue()->setThreshold(queue_min); video_thread->packetQueue()->setCapacity(queue_max); qDebug("Starting video thread..."); video_thread->start(); - connect(video_thread, SIGNAL(finished()), this, SIGNAL(stopped()), Qt::DirectConnection); } if (aCodecCtx) { + if (!vCodecCtx) //avoid emit stopped() mulltiple times + connect(audio_thread, SIGNAL(finished()), this, SIGNAL(stopped()), Qt::DirectConnection); audio_thread->packetQueue()->setThreshold(queue_min); audio_thread->packetQueue()->setCapacity(queue_max); qDebug("Starting audio thread..."); - audio_thread->start(QThread::HighestPriority); - if (!vCodecCtx) //avoid emit stopped() mulltiple times - connect(audio_thread, SIGNAL(finished()), this, SIGNAL(stopped()), Qt::DirectConnection); + audio_thread->start(); } demuxer_thread->start(); //blockSignals(false); @@ -398,8 +457,10 @@ void AVPlayer::stop() if (!isPlaying()) return; qDebug("AVPlayer::stop"); - disconnect(video_thread, SIGNAL(finished()), this, SIGNAL(stopped())); - disconnect(audio_thread, SIGNAL(finished()), this, SIGNAL(stopped())); + if (video_thread) + disconnect(video_thread, SIGNAL(finished()), this, SIGNAL(stopped())); + if (audio_thread) + disconnect(audio_thread, SIGNAL(finished()), this, SIGNAL(stopped())); //blockSignals(true); //TODO: move emit stopped() before it. or connect avthread.finished() to tryEmitStop() {if (!called_by_stop) emit} if (demuxer_thread->isRunning()) { qDebug("stop d"); @@ -410,7 +471,7 @@ void AVPlayer::stop() demuxer_thread->terminate(); //Terminate() causes the wait condition destroyed without waking up } } - if (audio_thread->isRunning()) { + if (audio_thread && audio_thread->isRunning()) { qDebug("stop a"); audio_thread->stop(); if (!audio_thread->wait(1000)) { @@ -418,7 +479,7 @@ void AVPlayer::stop() audio_thread->terminate(); } } - if (video_thread->isRunning()) { + if (video_thread && video_thread->isRunning()) { qDebug("stopv"); video_thread->stop(); if (!video_thread->wait(1000)) { @@ -427,6 +488,7 @@ void AVPlayer::stop() } } //TODO: why AVThread not trigger the signal? + qDebug("demux thread, avthreads stopped..."); emit stopped(); } //FIXME: If not playing, it will just play but not play one frame. @@ -437,8 +499,10 @@ void AVPlayer::playNextFrame() } demuxer_thread->pause(false); clock->pause(false); - audio_thread->nextAndPause(); - video_thread->nextAndPause(); + if (audio_thread) + audio_thread->nextAndPause(); + if (video_thread) + video_thread->nextAndPause(); clock->pause(true); demuxer_thread->pause(true); } diff --git a/src/AudioThread.cpp b/src/AudioThread.cpp index c0c32eeb..150ad22e 100644 --- a/src/AudioThread.cpp +++ b/src/AudioThread.cpp @@ -73,6 +73,7 @@ void AudioThread::run() 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; } } diff --git a/src/QtAV/AVDemuxThread.h b/src/QtAV/AVDemuxThread.h index ead0ab0b..080675dc 100644 --- a/src/QtAV/AVDemuxThread.h +++ b/src/QtAV/AVDemuxThread.h @@ -51,6 +51,8 @@ public: public slots: void stop(); void pause(bool p); +private slots: + void notifyEnd(); protected: virtual void run(); diff --git a/src/QtAV/AVDemuxer.h b/src/QtAV/AVDemuxer.h index 5003fe5b..3cc17f61 100644 --- a/src/QtAV/AVDemuxer.h +++ b/src/QtAV/AVDemuxer.h @@ -48,6 +48,15 @@ class Q_EXPORT AVDemuxer : public QObject //QIODevice? { Q_OBJECT public: + enum SeekFlag { + SeekByByte, + SeekByTime + }; + enum SeekFrame { + SeekIframe, + SeekAnyFrame + }; + AVDemuxer(const QString& fileName = QString(), QObject *parent = 0); ~AVDemuxer(); diff --git a/src/QtAV/AVPlayer.h b/src/QtAV/AVPlayer.h index ebb57a12..c717d990 100644 --- a/src/QtAV/AVPlayer.h +++ b/src/QtAV/AVPlayer.h @@ -69,6 +69,7 @@ public: //TODO: addRenderer; renderers() VideoRenderer* setRenderer(VideoRenderer* renderer); VideoRenderer* renderer(); + AudioOutput* setAudioOutput(AudioOutput* ao); AudioOutput* audio(); void setMute(bool mute); bool isMute() const; diff --git a/src/VideoThread.cpp b/src/VideoThread.cpp index cc518a76..3781f9ea 100644 --- a/src/VideoThread.cpp +++ b/src/VideoThread.cpp @@ -101,6 +101,7 @@ void VideoThread::run() if (d.packets.isEmpty() && !d.stop) { d.stop = d.demux_end; if (d.stop) { + qDebug("video queue empty and demux end. break video thread"); break; } }