/****************************************************************************** QtAV: Multimedia framework based on Qt and FFmpeg Copyright (C) 2012-2017 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 "AVDemuxThread.h" #include #include "AVClock.h" #include "AVDemuxer.h" #include "AVDecoder.h" #include "VideoThread.h" #include "AudioThread.h" #include #include "Logger.h" //#include "../../roadmovie/rm_constants.h" #if (RM_TESTING) #include "../tester/rm_test_dialog.h" #endif #define RESUME_ONCE_ON_SEEK 0 #define DEMUX_THREAD_DEBUG 1 namespace FAV { class AutoSem { QSemaphore *s; public: AutoSem(QSemaphore* sem) : s(sem) { s->release();} ~AutoSem() { if (s->available() > 0) s->acquire(s->available()); } }; class QueueEmptyCall : public PacketBuffer::StateChangeCallback { public: QueueEmptyCall(AVDemuxThread* thread): mDemuxThread(thread) {} virtual void call() { if (!mDemuxThread) return; if (mDemuxThread->isEnd()) return; if (mDemuxThread->atEndOfMedia()) return; mDemuxThread->updateBufferState(); // ensure detect buffering immediately AVThread *thread = mDemuxThread->videoThread(); //qDebug("try wake up video queue"); if (thread) thread->packetQueue()->blockFull(false); //qDebug("try wake up audio queue"); thread = mDemuxThread->audioThread(); if (thread) thread->packetQueue()->blockFull(false); } private: AVDemuxThread *mDemuxThread; }; AVDemuxThread::AVDemuxThread(QObject *parent) : QThread(parent) , paused(false) , user_paused(false) , end(false) , end_action(MediaEndAction_Default) , m_buffering(false) , m_buffer(0) , demuxer(0) , ademuxer(0) , audio_thread(0) , video_thread(0) , clock_type(-1) { seek_tasks.setCapacity(1); seek_tasks.blockFull(false); } AVDemuxThread::AVDemuxThread(AVDemuxer *dmx, QObject *parent) : QThread(parent) , paused(false) , end(false) , m_buffering(false) , m_buffer(0) , audio_thread(0) , video_thread(0) , playerID(-1) { setDemuxer(dmx); seek_tasks.setCapacity(1); seek_tasks.blockFull(false); } void AVDemuxThread::setDemuxer(AVDemuxer *dmx) { demuxer = dmx; } void AVDemuxThread::setAudioDemuxer(AVDemuxer *demuxer) { //QMutexLocker locker(&buffer_mutex); //Q_UNUSED(locker); ademuxer = demuxer; } void AVDemuxThread::setAVThread(AVThread*& pOld, AVThread *pNew) { if (pOld == pNew) return; if (pOld) { if (pOld->isRunning()) pOld->stop(); pOld->disconnect(this, SLOT(onAVThreadQuit())); } pOld = pNew; if (!pNew) return; pOld->packetQueue()->setEmptyCallback(new QueueEmptyCall(this)); connect(pOld, SIGNAL(finished()), SLOT(onAVThreadQuit())); } void AVDemuxThread::setAudioThread(AVThread *thread) { setAVThread(audio_thread, thread); } void AVDemuxThread::setVideoThread(AVThread *thread) { setAVThread(video_thread, thread); } AVThread* AVDemuxThread::videoThread() { return video_thread; } AVThread* AVDemuxThread::audioThread() { return audio_thread; } /* void AVDemuxThread::stepBackward() { if (!video_thread) return; AVThread *t = video_thread; const qreal pre_pts = video_thread->previousHistoryPts(); if (pre_pts == 0.0) { qWarning("can not get previous pts"); return; } end = false; // queue maybe blocked by put() if (audio_thread) { audio_thread->packetQueue()->clear(); // will put new packets before task run } class stepBackwardTask : public QRunnable { public: stepBackwardTask(AVDemuxThread *dt, qreal t) : demux_thread(dt) , pts(t) {} void run() { AVThread *avt = demux_thread->videoThread(); avt->packetQueue()->clear(); // clear here if (pts <= 0) { demux_thread->demuxer->seek(qint64(-pts*1000.0) - 500LL); QVector ts; qreal t = -1.0; while (t < -pts) { demux_thread->demuxer->readFrame(); if (demux_thread->demuxer->stream() != demux_thread->demuxer->videoStream()) continue; t = demux_thread->demuxer->packet().pts; ts.push_back(t); } const qreal t0 = ts.back(); ts.pop_back(); const qreal dt = t0 - ts.back(); pts = ts.back(); // FIXME: sometimes can not seek to the previous pts, the result pts is always current pts, so let the target pts a little earlier pts -= dt/2.0; } qDebug("step backward: %lld, %f", qint64(pts*1000.0), pts); demux_thread->video_thread->setDropFrameOnSeek(false); demux_thread->seekInternal(qint64(pts*1000.0), AccurateSeek); } private: AVDemuxThread *demux_thread; qreal pts; }; pause(true); t->packetQueue()->clear(); // will put new packets before task run t->packetQueue(); Packet pkt; pkt.pts = pre_pts; t->packetQueue()->put(pkt); // clear and put a seek packet to ensure not frames other than previous frame will be decoded and rendered video_thread->pause(false); newSeekRequest(new stepBackwardTask(this, pre_pts)); } */ void AVDemuxThread::seek(qint64 pos, SeekType type) { PLAYER_DEBUG_V("@@@SEEK_POS:",50,pos); end = false; // queue maybe blocked by put() if (audio_thread) { audio_thread->packetQueue()->clear(); } if (video_thread) { video_thread->packetQueue()->clear(); } class SeekTask : public QRunnable { public: SeekTask(AVDemuxThread *dt, qint64 t, SeekType st) : demux_thread(dt) , type(st) , position(t) {} void run() { if (demux_thread->video_thread) { demux_thread->video_thread->setDropFrameOnSeek(true); } demux_thread->seekInternal(position, type); } private: AVDemuxThread *demux_thread; SeekType type; qint64 position; }; newSeekRequest(new SeekTask(this, pos, type)); } void AVDemuxThread::seekInternal(qint64 pos, SeekType type) { AVThread* av[] = { audio_thread, video_thread}; //qDebug("seek to %s %lld ms (%f%%)", QTime(0, 0, 0).addMSecs(pos).toString().toUtf8().constData(), pos, double(pos - demuxer->startTime())/double(demuxer->duration())*100.0); demuxer->setSeekType(type); #if (UPDATE_LAST_PACKET_DURATION) // OVER SEEK if(!demuxer->seek(pos) && pos * 1000 > demuxer->durationFixed) { pos = (demuxer->durationFixed / 1000) - 100; } #else // UPDATE_LAST_PACKET_DURATION // AVDemuxer::seek 에서 // av_seek_frame 호출함 demuxer->seek(pos); #endif // UPDATE_LAST_PACKET_DURATION if (ademuxer) { ademuxer->setSeekType(type); ademuxer->seek(pos); } AVThread *watch_thread = 0; // TODO: why queue may not empty? int sync_id = 0; // Audio, Video Thread for 처리 for (size_t i = 0; i < sizeof(av)/sizeof(av[0]); ++i) { AVThread *t = av[i]; if (!t) { continue; } if (!sync_id) { sync_id = t->clock()->syncStart(!!audio_thread + (!!video_thread && !demuxer->hasAttacedPicture())); // 최초 SYNC 시 2가 리턴되며 계속 증가함 PLAYER_DEBUG_V("@@@SEEK_SYNC_ID:",90,sync_id); } Q_ASSERT(sync_id != 0); qDebug("demuxer sync id: %d/%d", sync_id, t->clock()->syncId()); t->packetQueue()->clear(); // PACKET QUEUE 제거 t->requestSeek(); // 각 thread 에 seek 요구 // TODO: the first frame (key frame) will not be decoded correctly if flush() is called. //PacketBuffer *pb = t->packetQueue(); //qDebug("%s put seek packet. %d/%d-%.3f, progress: %.3f", t->metaObject()->className(), pb->buffered(), pb->bufferValue(), pb->bufferMax(), pb->bufferProgress()); t->packetQueue()->setBlocking(false); // aqueue bufferValue can be small (1), we can not put and take // 빈(데이터가 없는) 패킷을 생성하여 위치만 지정하여 추가한다 Packet pkt; #if (PLAY_SYNC_FIX2) pkt._isSeek = true; #endif // PLAY_SYNC_FIX2 pkt.pts = qreal(pos)/1000.0; #if (REAR_SYNC_FRONT) if(playerID == 1 && rear_delay > 0.0) { //qInfo() << "SEEK DELAY pkt.pts:" << pkt.pts << " rear delay:" << rear_delay << " = " << pkt.pts-rear_delay << "pkt.position:" << sync_id; pkt.pts = qMax(0.0,pkt.pts-rear_delay); } #endif // REAR_SYNC_FRONT pkt.position = sync_id; t->packetQueue()->put(pkt); PLAYER_DEBUG_V("@@@SEEK_PACKET_AFTER_PUT SIZE:",100,t->packetQueue()->size()); t->packetQueue()->setBlocking(true); // blockEmpty was false when eof is read. if (isPaused()) { //TODO: deal with pause in AVThread? t->pause(false); watch_thread = t; //qInfo() << "is paused"; } } if (watch_thread) { pauseInternal(false); Q_EMIT requestClockPause(false); // need direct connection // direct connection is fine here #if (PLAY_SYNC_FIX2) connect(watch_thread, SIGNAL(seekFinished(qint64,qint64)), this, SLOT(seekOnPauseFinished()), Qt::DirectConnection); #else connect(watch_thread, SIGNAL(seekFinished(qint64)), this, SLOT(seekOnPauseFinished()), Qt::DirectConnection); #endif } } void AVDemuxThread::newSeekRequest(QRunnable *r) { if (seek_tasks.size() >= seek_tasks.capacity()) { QRunnable *r = seek_tasks.take(); if (r && r->autoDelete()) delete r; } seek_tasks.put(r); } void AVDemuxThread::processNextSeekTask() { if (seek_tasks.isEmpty()) return; QRunnable *task = seek_tasks.take(); if (!task) return; task->run(); if (task->autoDelete()) delete task; } void AVDemuxThread::pauseInternal(bool value) { paused = value; } bool AVDemuxThread::isPaused() const { return paused; } bool AVDemuxThread::isEnd() const { return end; } bool AVDemuxThread::atEndOfMedia() const { return demuxer->atEnd(); } PacketBuffer* AVDemuxThread::buffer() { return m_buffer; } void AVDemuxThread::updateBufferState() { if (!m_buffer) return; if (m_buffering) { // always report progress when buffering Q_EMIT bufferProgressChanged(m_buffer->bufferProgress()); } if (m_buffering == m_buffer->isBuffering()) return; m_buffering = m_buffer->isBuffering(); Q_EMIT mediaStatusChanged(m_buffering ? FAV::BufferingMedia : FAV::BufferedMedia); // state change to buffering, report progress immediately. otherwise we have to wait to read 1 packet. if (m_buffering) { Q_EMIT bufferProgressChanged(m_buffer->bufferProgress()); } } //No more data to put. So stop blocking the queue to take the reset elements void AVDemuxThread::stop() { //this will not affect the pause state if we pause the output //TODO: why remove blockFull(false) can not play another file? AVThread* av[] = { audio_thread, video_thread}; for (size_t i = 0; i < sizeof(av)/sizeof(av[0]); ++i) { AVThread* t = av[i]; if (!t) continue; t->packetQueue()->clear(); t->packetQueue()->blockFull(false); //?? while (t->isRunning()) { qDebug() << "stopping thread " << t; t->stop(); t->wait(500); } } pause(false); cond.wakeAll(); qDebug("all avthread finished. try to exit demux thread<<<<<<"); end = true; } void AVDemuxThread::pause(bool p, bool wait) { user_paused = p; if (paused == p) return; paused = p; if (!paused) cond.wakeAll(); else { if (wait) { // block until current loop finished buffer_mutex.lock(); buffer_mutex.unlock(); } } } void AVDemuxThread::setMediaEndAction(MediaEndAction value) { end_action = value; } MediaEndAction AVDemuxThread::mediaEndAction() const { return end_action; } /* void AVDemuxThread::stepForward() { if (end) return; // clock type will be wrong if no lock because slot frameDeliveredOnStepForward() is in video thread QMutexLocker locker(&next_frame_mutex); Q_UNUSED(locker); pause(true); // must pause AVDemuxThread (set user_paused true) AVThread* av[] = {video_thread, audio_thread}; bool connected = false; for (size_t i = 0; i < sizeof(av)/sizeof(av[0]); ++i) { AVThread *t = av[i]; if (!t) continue; // set clock first if (clock_type < 0) clock_type = (int)t->clock()->isClockAuto() + 2*(int)t->clock()->clockType(); t->clock()->setClockType(AVClock::VideoClock); t->scheduleFrameDrop(false); t->pause(false); t->packetQueue()->blockFull(false); if (!connected) { connect(t, SIGNAL(frameDelivered()), this, SLOT(frameDeliveredOnStepForward()), Qt::DirectConnection); connect(t, SIGNAL(eofDecoded()), this, SLOT(eofDecodedOnStepForward()), Qt::DirectConnection); connected = true; } } Q_EMIT requestClockPause(false); pauseInternal(false); } */ void AVDemuxThread::seekOnPauseFinished() { AVThread *thread = video_thread ? video_thread : audio_thread; Q_ASSERT(thread); #if (PLAY_SYNC_FIX2) disconnect(thread, SIGNAL(seekFinished(qint64,qint64)), this, SLOT(seekOnPauseFinished())); #else disconnect(thread, SIGNAL(seekFinished(qint64)), this, SLOT(seekOnPauseFinished())); #endif if (user_paused) { pause(true); // restore pause state Q_EMIT requestClockPause(true); // need direct connection // pause video/audio thread if (video_thread) video_thread->pause(true); if (audio_thread) audio_thread->pause(true); } } //void AVDemuxThread::frameDeliveredOnStepForward() //{ // AVThread *thread = video_thread ? video_thread : audio_thread; // Q_ASSERT(thread); // QMutexLocker locker(&next_frame_mutex); // Q_UNUSED(locker); // disconnect(thread, SIGNAL(frameDelivered()), this, SLOT(frameDeliveredOnStepForward())); // disconnect(thread, SIGNAL(eofDecoded()), this, SLOT(eofDecodedOnStepForward())); // if (user_paused) { // pause(true); // restore pause state // Q_EMIT requestClockPause(true); // need direct connection // // pause both video and audio thread // if (video_thread) // video_thread->pause(true); // if (audio_thread) // audio_thread->pause(true); // } // if (clock_type >= 0) { // thread->clock()->setClockAuto(clock_type & 1); // thread->clock()->setClockType(AVClock::ClockType(clock_type/2)); // clock_type = -1; // thread->clock()->updateExternalClock((thread->previousHistoryPts() - thread->clock()->initialValue())*1000.0); // } // Q_EMIT stepFinished(); //} //void AVDemuxThread::eofDecodedOnStepForward() //{ // AVThread *thread = video_thread ? video_thread : audio_thread; // Q_ASSERT(thread); // QMutexLocker locker(&next_frame_mutex); // Q_UNUSED(locker); // disconnect(thread, SIGNAL(frameDelivered()), this, SLOT(frameDeliveredOnStepForward())); // disconnect(thread, SIGNAL(eofDecoded()), this, SLOT(eofDecodedOnStepForward())); // pause(false); // end = true; // if (clock_type >= 0) { // thread->clock()->setClockAuto(clock_type & 1); // thread->clock()->setClockType(AVClock::ClockType(clock_type/2)); // clock_type = -1; // } // Q_EMIT stepFinished(); //} void AVDemuxThread::onAVThreadQuit() { AVThread* av[] = { audio_thread, video_thread}; for (size_t i = 0; i < sizeof(av)/sizeof(av[0]); ++i) { if (!av[i]) continue; if (av[i]->isRunning()) return; } end = true; //(!audio_thread || !audio_thread->isRunning()) && } bool AVDemuxThread::waitForStarted(int msec) { if (!sem.tryAcquire(1, msec > 0 ? msec : std::numeric_limits::max())) return false; sem.release(1); //ensure other waitForStarted() calls continue return true; } void AVDemuxThread::run() { PLAYER_DEBUG("@@@DEMUX_THREAD_START",100); #if (FORCE_BREAK_EOF_LOG) qInfo() << "-------------------------------------------------------------------"; qInfo() << __FUNCTION__ << __LINE__ << playerID; #endif // 종료 에러 확인용 #if ((RM_TESTING || PLAY_SYNC_FIX2) && VTHREAD_DEBUG_ON) g_et.start(); #endif m_buffering = false; end = false; if (audio_thread) { //#if (FORCE_BREAK_EOF) // // 이전 재생 정지 // if(audio_thread->isRunning()) // { // audio_thread->forceEnd = true; // audio_thread->stop(); // while (audio_thread->isRunning()) { // msleep(10); // } // } //#endif //QThread::HighPriority, QThread::HighPriority if(!audio_thread->isRunning()) { audio_thread->start(QThread::HighPriority); // QThread::HighPriority / HighestPriority } } if (video_thread) { //#if (FORCE_BREAK_EOF) // if(video_thread->isRunning()) // { // video_thread->forceEnd = true; // video_thread->stop(); // while (video_thread->isRunning()) { // msleep(10); // } // } //#endif if(!video_thread->isRunning()) { video_thread->start(); } } #if (FIXED_FPS_DURATION) // 종료 지점 설정 // qInfo() << "SET STOP POSITON:" << demuxer->durationFixed; #if (REAR_SYNC_FRONT) if(playerID == 1) { ((VideoThread*)video_thread)->stopPosition = demuxer->durationFixed + (demuxer->rear_delay * 1000000); } else #endif { ((VideoThread*)video_thread)->stopPosition = demuxer->durationFixed; } if(playerID == 0 && audio_thread != NULL) { ((AudioThread*)audio_thread)->stopPosition = demuxer->durationFixed - 300000; // 0.3 초 } #endif #if (USE_DURATION_IN_THREAD) double durationSec = ((double)demuxer->durationUs()) / 1000000.0; video_thread->durationSec = durationSec; if(audio_thread != NULL) { audio_thread->durationSec = durationSec; } #endif int stream = 0; Packet pkt; pause(false); #if !(OFF_OTHER_DEBUG) qDebug("get av queue a/v thread = %p %p", audio_thread, video_thread); #endif PacketBuffer *aqueue = audio_thread ? audio_thread->packetQueue() : 0; PacketBuffer *vqueue = video_thread ? video_thread->packetQueue() : 0; // aqueue as a primary buffer: music with/without cover AVThread* thread = !video_thread || (audio_thread && demuxer->hasAttacedPicture()) ? audio_thread : video_thread; m_buffer = thread->packetQueue(); const qint64 buf2 = aqueue ? aqueue->bufferValue() : 1; // TODO: may be changed by user. Deal with audio track change if (aqueue) { aqueue->clear(); aqueue->setBlocking(true); } if (vqueue) { vqueue->clear(); vqueue->setBlocking(true); } m_buffer->clear(); #if (PLAY_SYNC_FIX2) connect(thread, SIGNAL(seekFinished(qint64,qint64)), this, SIGNAL(seekFinished(qint64,qint64)), Qt::DirectConnection); #else connect(thread, SIGNAL(seekFinished(qint64)), this, SIGNAL(seekFinished(qint64)), Qt::DirectConnection); #endif seek_tasks.clear(); int was_end = 0; if (ademuxer) { ademuxer->seek(0LL); } qreal last_apts = 0; qreal last_vpts = 0; AutoSem as(&sem); Q_UNUSED(as); while (!end) { processNextSeekTask(); //vthread maybe changed by AVPlayer.setPriority() from no dec case vqueue = video_thread ? video_thread->packetQueue() : 0; // 12초 5FPS 영상의 경우 거의 즉시 모든 프레임을 읽고(readFrame) // packetQueue 에 모두 put 처리하여 atEnd 상태가 됨 if (demuxer->atEnd()) { // if avthread may skip 1st eof packet because of a/v sync const int kMaxEof = 1;//if buffer packet, we can use qMax(aqueue->bufferValue(), vqueue->bufferValue()) and not call blockEmpty(false); if (aqueue && (!was_end || aqueue->isEmpty())) { if (was_end < kMaxEof) { aqueue->put(Packet::createEOF()); } const qreal dpts = last_vpts - last_apts; if (dpts > 0.1) { Packet fake_apkt; fake_apkt.duration = last_vpts - qMin(thread->clock()->videoTime(), thread->clock()->value()); // FIXME: when clock value < 0? #if !(OFF_OTHER_DEBUG) qDebug("audio is too short than video: %.3f, fake_apkt.duration: %.3f", dpts, fake_apkt.duration); #endif last_apts = last_vpts = 0; // if not reset to 0, for example real eof pts, then no fake apkt after seek because dpts < 0 aqueue->put(fake_apkt); } aqueue->blockEmpty(was_end >= kMaxEof); // do not block if buffer is not enough. block again on seek } if (vqueue && (!was_end || vqueue->isEmpty())) { if (was_end < kMaxEof) { vqueue->put(Packet::createEOF()); //PLAYER_DEBUG_V("@@@CREATE_EOF LAST/VPTS:",60,last_vpts); } else { //PLAYER_DEBUG("@@@NO_EOF",50); } // vqueue->blockEmpty(was_end >= kMaxEof); } if (m_buffering) { m_buffering = false; Q_EMIT mediaStatusChanged(FAV::BufferedMedia); } was_end = qMin(was_end + 1, kMaxEof); bool exit_thread = !user_paused; if (aqueue) { #if (FORCE_BREAK_EOF && !PLAY_SYNC_FIX2) exit_thread &= (aqueue->isEmpty() || (aqueue->size() == 1 && aqueue->take().isEOF())); #else exit_thread &= aqueue->isEmpty(); #endif } if (vqueue) { // 비워지지 않으면 종료되지 않는다. #if (FORCE_BREAK_EOF && !PLAY_SYNC_FIX2) exit_thread &= (vqueue->isEmpty() || (vqueue->size() == 1 && vqueue->take().isEOF())); #else exit_thread &= vqueue->isEmpty(); #endif } if (exit_thread) { #if !(PLAY_SYNC_FIX2) if(video_thread) { video_thread->forceEnd = true; } if(audio_thread) { audio_thread->forceEnd = true; } #endif // PLAY_SYNC_FIX2 FIXED_SLEEP; //qInfo() << "EXIT THREAD" << playerID << __FUNCTION__ << __LINE__ << et.elapsed(); #if !(PLAY_SYNC_FIX2) if (!(mediaEndAction() & MediaEndAction_Pause)) { break; } #endif // #if !(PLAY_SYNC_FIX2) #if !(PLAY_SYNC_FIX2) // 정지할 경우 CLOCK 으로 업데이트가 불가능함... pause(true); Q_EMIT requestClockPause(true); if (aqueue) { aqueue->blockEmpty(true); } if (vqueue) { vqueue->blockEmpty(true); } #endif // // PLAY_SYNC_FIX2 } else { if(video_thread) { video_thread->aboutToEnd = true; } if(audio_thread) { audio_thread->aboutToEnd = true; } } // wait for a/v thread finished msleep(200); PLAYER_DEBUG("@@@END_CONTINUE",10); continue; } // if (demuxer->atEnd()) if (demuxer->mediaStatus() == StalledMedia) { #if !(OFF_OTHER_DEBUG) qDebug("stalled media. exiting demuxing thread"); #endif break; } was_end = 0; if (tryPause()) { PLAYER_DEBUG("@@@TRY_PAUSE_CONTINUE",10); continue; //the queue is empty and will block } #if !(FORCE_BREAK_EOF) updateBufferState(); #endif if (!demuxer->readFrame()) { continue; } stream = demuxer->stream(); pkt = demuxer->packet(); Packet apkt; bool audio_has_pic = demuxer->hasAttacedPicture(); int a_ext = 0; if (ademuxer) { QMutexLocker locker(&buffer_mutex); Q_UNUSED(locker); if (ademuxer) { a_ext = -1; audio_has_pic = ademuxer->hasAttacedPicture(); // FIXME: buffer full but buffering!!! // avoid read external track everytime. aqueue may not block full // vqueue will not block if aqueue is not enough if (!aqueue->isFull() || aqueue->isBuffering()) { if (ademuxer->readFrame()) { if (ademuxer->stream() == ademuxer->audioStream()) { a_ext = 1; apkt = ademuxer->packet(); } } // no continue otherwise. ademuxer finished earlier than demuxer } } } //qDebug("vqueue: %d, aqueue: %d/isbuffering %d isfull: %d, buffer: %d/%d", vqueue->size(), aqueue->size(), aqueue->isBuffering(), aqueue->isFull(), aqueue->buffered(), aqueue->bufferValue()); //QMutexLocker locker(&buffer_mutex); //TODO: seems we do not need to lock //Q_UNUSED(locker); /*1 is empty but another is enough, then do not block to ensure the empty one can put packets immediatly. But usually it will not happen, why? */ /* demux thread will be blocked only when 1 queue is full and still put * if vqueue is full and aqueue becomes empty, then demux thread * will be blocked. so we should wake up another queue when empty(or threshold?). * TODO: the video stream and audio stream may be group by group. provide it * stream data: aaaaaaavvvvvvvaaaaaaaavvvvvvvvvaaaaaa, it happens * stream data: aavavvavvavavavavavavavavvvaavavavava, it's ok */ //TODO: use cache queue, take from cache queue if not empty? const bool a_internal = stream == demuxer->audioStream(); if (a_internal || a_ext > 0) {//apkt.isValid()) { if (a_internal && !a_ext) // internal is always read even if external audio used apkt = demuxer->packet(); last_apts = apkt.pts; /* if vqueue if not blocked and full, and aqueue is empty, then put to * vqueue will block demuex thread */ if (aqueue) { if (!audio_thread || !audio_thread->isRunning()) { aqueue->clear(); continue; } // must ensure bufferValue set correctly before continue if (m_buffer != aqueue) { aqueue->setBufferValue(m_buffer->isBuffering() ? std::numeric_limits::max() : buf2); } // always block full if no vqueue because empty callback may set false // attached picture is cover for song, 1 frame aqueue->blockFull(!video_thread || !video_thread->isRunning() || !vqueue || audio_has_pic); // external audio: a_ext < 0, stream = audio_idx=>put invalid packet #if (FORCE_BREAK_EOF) // 여기서 종료 시 에러 발생함. (end 확인해서 PUT 하지 않도록 해야함) if (a_ext >= 0 && end == false) #else if (a_ext >= 0) #endif { aqueue->put(apkt); //affect video_thread } } } // always check video stream if use external audio if (stream == demuxer->videoStream()) { if (vqueue) { if (!video_thread || !video_thread->isRunning()) { vqueue->clear(); continue; } /* #if (FORCE_BREAK_EOF) // ! 종료 패킷생성 처리함.. (forceEOF, end 확인해서 PUT 하지 않도록 해야함) if(pkt.isEOF() == true && forceEOF == false && end == false) { // if(playerID == 1) // { // qInfo() << "V-CEOF:2" << __FUNCTION__ << __LINE__; // } vqueue->blockFull(true); Packet quit_pkt(Packet::createEOF()); quit_pkt.position = 0; //? vqueue->put(quit_pkt); if(aqueue != NULL && forceEOF == false && end == false) { Packet aquit_pkt(Packet::createEOF()); aquit_pkt.position = 0; aqueue->put(aquit_pkt); } forceEOF = true; // 정상 종료 메시지 continue; } // PUT 확인 if(end == true) { break; } #endif */ vqueue->blockFull(!audio_thread || !audio_thread->isRunning() || !aqueue || aqueue->isEnough()); vqueue->put(pkt); //affect audio_thread last_vpts = pkt.pts; // 5FPS 10초 영상의 경우 50 프레임이며 최종 PTS 는 9.8(+0.2 Duration)임 PLAYER_DEBUG_V2("@@@PACKET_ADD:",100,pkt.pts,pkt.dts); } } #if !(DO_NOT_USE_SUBTITLE) else if (demuxer->subtitleStreams().contains(stream)) { //subtitle Q_EMIT internalSubtitlePacketRead(demuxer->subtitleStreams().indexOf(stream), pkt); } #endif else { continue; } } #if (FIX_PLAYER_END_CLIP) // 정지된 경우 여기까지 오지 않음 PLAYER_DEBUG("@@@DEMUX_THREAD_DONE",100); //qInfo() << "EXIT" << playerID << "DEMUX(1):" << __FUNCTION__ << __LINE__;// << " time:" << tr.elapsed(); #endif // #if (FORCE_BREAK_EOF_LOG) QElapsedTimer ql; ql.start(); qInfo() << __FUNCTION__ << __LINE__ << "DONE" << playerID; #endif m_buffering = false; m_buffer = 0; while (audio_thread && audio_thread->isRunning()) { #if (FORCE_BREAK_EOF) audio_thread->forceEnd = true; #endif #if !(OFF_OTHER_DEBUG) qDebug("waiting audio thread......."); #endif Packet quit_pkt(Packet::createEOF()); quit_pkt.position = 0; aqueue->put(quit_pkt); aqueue->blockEmpty(false); //FIXME: why need this audio_thread->pause(false); audio_thread->wait(500); } #if (FORCE_BREAK_EOF_LOG) qInfo() << __FUNCTION__ << __LINE__ << "A THREAD DONE" << playerID; #endif while (video_thread && video_thread->isRunning()) { #if (FORCE_BREAK_EOF) video_thread->forceEnd = true; #endif #if !(OFF_OTHER_DEBUG) qDebug("waiting video thread......."); #endif Packet quit_pkt(Packet::createEOF()); quit_pkt.position = 0; vqueue->put(quit_pkt); vqueue->blockEmpty(false); video_thread->pause(false); video_thread->wait(500); } #if (PLAY_SYNC_FIX2) thread->disconnect(this, SIGNAL(seekFinished(qint64,qint64))); #else thread->disconnect(this, SIGNAL(seekFinished(qint64))); #endif #if !(OFF_OTHER_DEBUG) qDebug("Demux thread stops running...."); #endif #if (FORCE_BREAK_EOF_LOG) qInfo() << __FUNCTION__ << __LINE__ << "V THREAD DONE" << playerID << ql.elapsed(); #endif // qInfo() << "EXIT" << playerID << "DEMUX(2):" << __FUNCTION__ << __LINE__; if (demuxer->atEnd()) { Q_EMIT mediaStatusChanged(FAV::EndOfMedia); } else { #if (FORCE_END_OF_MEDIA) Q_EMIT mediaStatusChanged(FAV::EndOfMedia); #else Q_EMIT mediaStatusChanged(FAV::StalledMedia); #endif } } bool AVDemuxThread::tryPause(unsigned long timeout) { if (!paused) return false; QMutexLocker lock(&buffer_mutex); Q_UNUSED(lock); cond.wait(&buffer_mutex, timeout); return true; } } //namespace FAV