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?
This commit is contained in:
wang-bin
2013-06-03 13:33:49 +08:00
parent f6c3a6b442
commit b971c51180
8 changed files with 230 additions and 77 deletions
+110 -35
View File
@@ -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;
}
+1 -1
View File
@@ -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;
+105 -41
View File
@@ -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<qreal>(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);
}
+1
View File
@@ -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;
}
}
+2
View File
@@ -51,6 +51,8 @@ public:
public slots:
void stop();
void pause(bool p);
private slots:
void notifyEnd();
protected:
virtual void run();
+9
View File
@@ -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();
+1
View File
@@ -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;
+1
View File
@@ -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;
}
}