Skip to content

Commit

Permalink
video stepBackward()/Forward(). deprecate playNextFrame()
Browse files Browse the repository at this point in the history
currently only step backward to the previous displayed frame
  • Loading branch information
wang-bin committed Oct 5, 2015
1 parent cb318eb commit dbea492
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 13 deletions.
5 changes: 4 additions & 1 deletion examples/player/EventFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,10 @@ bool EventFilter::eventFilter(QObject *watched, QEvent *event)
player->videoCapture()->capture();
break;
case Qt::Key_N: //check playing?
player->playNextFrame();
player->stepForward();
break;
case Qt::Key_B:
player->stepBackward();
break;
case Qt::Key_P:
player->play();
Expand Down
2 changes: 1 addition & 1 deletion examples/videowall/VideoWall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ bool VideoWall::eventFilter(QObject *watched, QEvent *event)
break;
case Qt::Key_N: //check playing?
foreach (AVPlayer* player, players) {
player->playNextFrame();
player->stepForward();
}
break;

Expand Down
2 changes: 1 addition & 1 deletion qml/QmlAVPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ void QmlAVPlayer::nextFrame()
{
if (!mpPlayer)
return;
mpPlayer->playNextFrame();
mpPlayer->stepForward();
}

void QmlAVPlayer::seek(int offset)
Expand Down
66 changes: 66 additions & 0 deletions src/AVDemuxThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,67 @@ 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) - 1000LL);
QVector<qreal> 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);
}
ts.pop_back();
pts = ts.back();
}
qDebug("step backward: %lld, %f", qint64(pts*1000.0), pts);
demux_thread->seekInternal(qint64(pts*1000.0), AccurateSeek);
}
private:
AVDemuxThread *demux_thread;
qreal pts;
};

pause(true);
// set clock first
if (clock_type < 0)
clock_type = (int)video_thread->clock()->isClockAuto() + 2*(int)video_thread->clock()->clockType();
video_thread->clock()->setClockType(AVClock::VideoClock);
t->packetQueue()->clear(); // will put new packets before task run
t->packetQueue();
Packet pkt;
pkt.pts = qreal(pre_pts)/1000.0;
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)
{
end = false;
Expand Down Expand Up @@ -346,6 +407,11 @@ void AVDemuxThread::seekOnPauseFinished()
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;
}
}

void AVDemuxThread::frameDeliveredNextFrame()
Expand Down
2 changes: 2 additions & 0 deletions src/AVDemuxThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class AVDemuxThread : public QThread
AVThread* audioThread();
void setVideoThread(AVThread *thread);
AVThread* videoThread();
void stepBackward();
void seek(qint64 pos, SeekType type); //ms
//AVDemuxer* demuxer
bool isPaused() const;
Expand Down Expand Up @@ -99,6 +100,7 @@ private slots:
QMutex next_frame_mutex;
int clock_type; // change happens in different threads(direct connection)
friend class SeekTask;
friend class stepBackwardTask;
};

} //namespace QtAV
Expand Down
17 changes: 12 additions & 5 deletions src/AVPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ void AVPlayer::clearVideoRenderers()
d->vos->clearOutputs();
}

//TODO: check components compatiblity(also when change the filter chain)
void AVPlayer::setRenderer(VideoRenderer *r)
{
VideoRenderer *vo = renderer();
Expand Down Expand Up @@ -539,7 +538,6 @@ bool AVPlayer::play(const QString& path)
return true;//isPlaying();
}

// TODO: check repeat status?
bool AVPlayer::isPlaying() const
{
return (d->read_thread &&d->read_thread->isRunning())
Expand Down Expand Up @@ -868,7 +866,7 @@ qreal AVPlayer::positionF() const

qint64 AVPlayer::position() const
{
const qint64 pts = d->clock->value()*1000.0; //TODO: avoid *1000.0
const qint64 pts = d->clock->value()*1000.0;
if (relativeTimeMode())
return pts - absoluteMediaStartPosition();
return pts;
Expand Down Expand Up @@ -1079,7 +1077,6 @@ bool AVPlayer::setSubtitleStream(int n)
{
if (d->subtitle_track == n)
return true;
// TODO: xxxchanged signal
d->subtitle_track = n;
Q_EMIT subtitleStreamChanged(n);
if (!d->demuxer.isLoaded())
Expand Down Expand Up @@ -1457,14 +1454,24 @@ void AVPlayer::timerEvent(QTimerEvent *te)
}
}

//FIXME: If not playing, it will just play but not play one frame.
void AVPlayer::playNextFrame()
{
stepForward();
}

//FIXME: If not playing, it will just play but not play one frame.
void AVPlayer::stepForward()
{
// pause clock
pause(true); // must pause AVDemuxThread (set user_paused true)
d->read_thread->nextFrame();
}

void AVPlayer::stepBackward()
{
d->read_thread->stepBackward();
}

void AVPlayer::seek(qreal r)
{
seek(qint64(r*double(duration())));
Expand Down
12 changes: 12 additions & 0 deletions src/AVThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,18 @@ void AVThread::scheduleFrameDrop(bool value)
scheduleTask(new FrameDropTask(decoder(), value));
}

qreal AVThread::previousHistoryPts() const
{
DPTR_D(const AVThread);
if (d.pts_history.empty()) {
qDebug("pts history is EMPTY");
return 0;
}
if (d.pts_history.size() == 1)
return -d.pts_history.back();
return d.pts_history.at(d.pts_history.size() - 2);
}

// TODO: shall we close decoder here?
void AVThread::stop()
{
Expand Down
1 change: 1 addition & 0 deletions src/AVThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class AVThread : public QThread
// TODO: resample, resize task etc.
void scheduleTask(QRunnable *task);
void scheduleFrameDrop(bool value = true);
qreal previousHistoryPts() const;

public slots:
virtual void stop();
Expand Down
3 changes: 3 additions & 0 deletions src/AVThread_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <QtCore/QVariant>
#include <QtCore/QWaitCondition>
#include "PacketBuffer.h"
#include "utils/ring.h"

class QRunnable;
namespace QtAV {
Expand All @@ -53,6 +54,7 @@ class AVThreadPrivate : public DPtrPrivate<AVThread>
, statistics(0)
, ready(false)
, render_pts0(-1)
, pts_history(30, -1)
{
tasks.blockFull(false);

Expand Down Expand Up @@ -83,6 +85,7 @@ class AVThreadPrivate : public DPtrPrivate<AVThread>
qreal render_pts0;

static QVariantHash dec_opt_framedrop, dec_opt_normal;
ring<qreal> pts_history;
};

} //namespace QtAV
Expand Down
14 changes: 11 additions & 3 deletions src/QtAV/AVPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,17 @@ public slots:
*/
void play(); //replay
void stop();
void playNextFrame(); //deprecated
//void stepForward();
//void stepBackward();
QTAV_DEPRECATED void playNextFrame(); //deprecated. use stepForward instead
/*!
* \brief stepForward
* Play the next frame and pause
*/
void stepForward();
/*!
* \brief stepBackward
* Play the previous frame and pause. Currently only support the previous decoded frames
*/
void stepBackward();

void setRelativeTimeMode(bool value);
/*!
Expand Down
8 changes: 6 additions & 2 deletions src/VideoThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,9 @@ void VideoThread::run()
const char* pkt_data = NULL; // workaround for libav9 decode fail but error code >= 0
while (true) {
processNextTask();
//TODO: why put it at the end of loop then playNextFrame() not work?
//TODO: why put it at the end of loop then stepForward() not work?
//processNextTask tryPause(timeout) and and continue outter loop
if (tryPause()) { //DO NOT continue, or playNextFrame() will fail
if (tryPause()) { //DO NOT continue, or stepForward() will fail

} else {
if (isPaused())
Expand All @@ -312,6 +312,7 @@ void VideoThread::run()
qDebug("Invalid packet! flush video codec context!!!!!!!!!! video packet queue size: %d", d.packets.size());
d.dec->flush(); //d.dec instead of dec because d.dec maybe changed in processNextTask() but dec is not
d.render_pts0 = pkt.pts;
d.pts_history = ring<qreal>(d.pts_history.capacity(), -1);
continue;
}
}
Expand Down Expand Up @@ -487,13 +488,16 @@ void VideoThread::run()
const qreal pts = frame.timestamp();
// seek finished because we can ensure no packet before seek decoded when render_pts0 is set
//qDebug("pts0: %f, pts: %f", d.render_pts0, pts);
d.pts_history.push_back(pts);

if (d.render_pts0 >= 0.0) {
if (pts < d.render_pts0) {
if (!pkt.isEOF())
pkt = Packet();
continue;
}
d.render_pts0 = -1;
qDebug("seek finished @%f", pts);
Q_EMIT seekFinished(qint64(pts*1000.0));
if (seek_count == -1)
seek_count = 1;
Expand Down

0 comments on commit dbea492

Please sign in to comment.