Skip to content

Commit

Permalink
fix seamless loop
Browse files Browse the repository at this point in the history
  • Loading branch information
andreysmykov committed Jul 4, 2018
1 parent 34afa14 commit 4b0549d
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 40 deletions.
35 changes: 35 additions & 0 deletions src/AVDemuxThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ AVDemuxThread::AVDemuxThread(QObject *parent) :
, audio_thread(0)
, video_thread(0)
, clock_type(-1)
, m_repeat_current(0)
, m_repeat_max(0)
{
seek_tasks.setCapacity(1);
seek_tasks.blockFull(false);
Expand All @@ -95,6 +97,8 @@ AVDemuxThread::AVDemuxThread(AVDemuxer *dmx, QObject *parent) :
, m_buffer(0)
, audio_thread(0)
, video_thread(0)
, m_repeat_current(0)
, m_repeat_max(0)
{
setDemuxer(dmx);
seek_tasks.setCapacity(1);
Expand Down Expand Up @@ -556,6 +560,14 @@ void AVDemuxThread::run()
//vthread maybe changed by AVPlayer.setPriority() from no dec case
vqueue = video_thread ? video_thread->packetQueue() : 0;
if (demuxer->atEnd()) {
if (getRepeatCurrent() < 0 || (getRepeatCurrent() >= m_repeat_max && m_repeat_max >= 0)) {
m_repeat_current = -1;
} else {
m_repeat_current++;
qDebug() << " m_repeat_current = " << m_repeat_current;
qDebug() << " m_repeat_max = " << m_repeat_max;
seek((qint64)0, SeekType::AccurateSeek);
}
// 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())) {
Expand Down Expand Up @@ -731,4 +743,27 @@ bool AVDemuxThread::tryPause(unsigned long timeout)
cond.wait(&buffer_mutex, timeout);
return true;
}

int AVDemuxThread::getRepeatCurrent()
{
return m_repeat_current;
}

void AVDemuxThread::setRepeatCurrent(int repeat_current)
{
m_repeat_current = repeat_current;
}

int AVDemuxThread::getRepeatMax()
{
return m_repeat_max;
}

void AVDemuxThread::setRepeatMax(int repeat_max)
{
m_repeat_max = repeat_max;
if (m_repeat_max < 0)
m_repeat_max = std::numeric_limits<int>::max();
}

} //namespace QtAV
6 changes: 6 additions & 0 deletions src/AVDemuxThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ class AVDemuxThread : public QThread
MediaEndAction mediaEndAction() const;
void setMediaEndAction(MediaEndAction value);
bool waitForStarted(int msec = -1);
int getRepeatCurrent();
int getRepeatMax();
void setRepeatCurrent(int repeat_current);
void setRepeatMax(int repeat_max);
Q_SIGNALS:
void requestClockPause(bool value);
void mediaStatusChanged(QtAV::MediaStatus);
Expand Down Expand Up @@ -106,6 +110,8 @@ private slots:
int clock_type; // change happens in different threads(direct connection)
friend class SeekTask;
friend class stepBackwardTask;
int m_repeat_current;
int m_repeat_max;
};

} //namespace QtAV
Expand Down
77 changes: 37 additions & 40 deletions src/AVPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -871,21 +871,19 @@ void AVPlayer::setPosition(qint64 position)

int AVPlayer::repeat() const
{
return d->repeat_max;
return d->read_thread->getRepeatMax();
}

int AVPlayer::currentRepeat() const
{
return d->repeat_current;
return d->read_thread->getRepeatCurrent();
}

// TODO: reset current_repeat?
void AVPlayer::setRepeat(int max)
{
d->repeat_max = max;
if (d->repeat_max < 0)
d->repeat_max = std::numeric_limits<int>::max();
Q_EMIT repeatChanged(d->repeat_max);
d->read_thread->setRepeatMax(max);
Q_EMIT repeatChanged(d->read_thread->getRepeatMax());
}

bool AVPlayer::setExternalAudio(const QString &file)
Expand Down Expand Up @@ -1250,8 +1248,8 @@ void AVPlayer::playInternal()
QMetaObject::invokeMethod(this, "startNotifyTimer", Qt::AutoConnection);
}
d->state = PlayingState;
if (d->repeat_current < 0)
d->repeat_current = 0;
if(currentRepeat() < 0)
d->read_thread->setRepeatCurrent(0);
} //end lock scoped here to avoid dead lock if connect started() to a slot that call unload()/play()
if (d->start_position_norm > 0) {
if (relativeTimeMode())
Expand All @@ -1267,34 +1265,30 @@ void AVPlayer::stopFromDemuxerThread()
{
qDebug("demuxer thread emit finished. repeat: %d/%d", currentRepeat(), repeat());
d->seeking = false;
if (currentRepeat() < 0 || (currentRepeat() >= repeat() && repeat() >= 0)) {
qreal stop_pts = masterClock()->videoTime();
if (stop_pts <= 0)
stop_pts = masterClock()->value();
masterClock()->reset();
QMetaObject::invokeMethod(this, "stopNotifyTimer");
// vars not set by user can be reset
d->repeat_current = -1;
d->start_position_norm = 0;
d->stop_position_norm = kInvalidPosition; // already stopped. so not 0 but invalid. 0 can stop the playback in timerEvent
d->media_end = kInvalidPosition;
qDebug("avplayer emit stopped()");
d->state = StoppedState;
QMetaObject::invokeMethod(this, "stateChanged", Q_ARG(QtAV::AVPlayer::State, d->state));
QMetaObject::invokeMethod(this, "stopped");
QMetaObject::invokeMethod(this, "stoppedAt", Q_ARG(qint64, qint64(stop_pts*1000.0)));
//Q_EMIT stateChanged(d->state);
//Q_EMIT stopped();
//Q_EMIT stoppedAt(stop_pts*1000.0);

/*
* currently preload is not supported. so always unload. Then some properties will be reset, e.g. duration()
*/
unload(); //TODO: invoke?
} else {
d->repeat_current++;
QMetaObject::invokeMethod(this, "play"); //ensure play() is called from player thread
}
qreal stop_pts = masterClock()->videoTime();
if (stop_pts <= 0)
stop_pts = masterClock()->value();
masterClock()->reset();
QMetaObject::invokeMethod(this, "stopNotifyTimer");
// vars not set by user can be reset

d->start_position_norm = 0;
d->stop_position_norm = kInvalidPosition; // already stopped. so not 0 but invalid. 0 can stop the playback in timerEvent
d->media_end = kInvalidPosition;
qDebug("avplayer emit stopped()");
d->state = StoppedState;
QMetaObject::invokeMethod(this, "stateChanged", Q_ARG(QtAV::AVPlayer::State, d->state));
QMetaObject::invokeMethod(this, "stopped");
QMetaObject::invokeMethod(this, "stoppedAt", Q_ARG(qint64, qint64(stop_pts*1000.0)));
//Q_EMIT stateChanged(d->state);
//Q_EMIT stopped();
//Q_EMIT stoppedAt(stop_pts*1000.0);

/*
* currently preload is not supported. so always unload. Then some properties will be reset, e.g. duration()
*/
unload(); //TODO: invoke?
}

void AVPlayer::aboutToQuitApp()
Expand Down Expand Up @@ -1455,7 +1449,7 @@ void AVPlayer::stop()
}
d->seeking = false;
d->reset_state = true;
d->repeat_current = -1;
d->read_thread->setRepeatCurrent(-1);
if (!isPlaying()) {
qDebug("Not playing~");
if (mediaStatus() == LoadingMedia || mediaStatus() == LoadedMedia) {
Expand Down Expand Up @@ -1515,22 +1509,25 @@ void AVPlayer::timerEvent(QTimerEvent *te)
qDebug("stopPosition() == 0, stop");
stop();
}
// t < d->start_position is ok. d->repeat_max<0 means repeat forever
// t < d->start_position is ok. repeat()< 0 means repeat forever

if (currentRepeat() >= repeat() && repeat() >= 0) {
d->reset_state = true; // true is default, can remove here
qDebug("stopPosition() %lld/%lld reached and no repeat: %d", t, stopPosition(), repeat());
stop();
return;
}

// FIXME: now stop instead of seek if reach media's end. otherwise will not get eof again
if (d->stop_position_norm == mediaStopPosition() || !isSeekable()) {
// if not seekable, how it can start to play at specified position?
qDebug("normalized stopPosition() == mediaStopPosition() or !seekable. d->repeat_current=%d", d->repeat_current);
qDebug("normalized stopPosition() == mediaStopPosition() or !seekable. currentRepeat()=%d", currentRepeat());
d->reset_state = false;
stop(); // repeat after all threads stopped
} else {
d->repeat_current++;
qDebug("noramlized stopPosition() != mediaStopPosition() and seekable. d->repeat_current=%d", d->repeat_current);
int repeat_current = d->read_thread->getRepeatCurrent();
d->read_thread->setRepeatCurrent(repeat_current++);
qDebug("noramlized stopPosition() != mediaStopPosition() and seekable. currentRepeat()=%d", currentRepeat());
setPosition(d->start_position_norm);
}
}
Expand Down

0 comments on commit 4b0549d

Please sign in to comment.