Skip to content

Commit

Permalink
simplify seek. emit seekFinished correctly
Browse files Browse the repository at this point in the history
TODO: refine playNextFrame()
  • Loading branch information
wang-bin committed May 8, 2015
1 parent 7beba20 commit 84700f2
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 100 deletions.
18 changes: 8 additions & 10 deletions src/AVDemuxThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,6 @@ void AVDemuxThread::seek(qint64 pos, SeekType type)
void AVDemuxThread::seekInternal(qint64 pos, SeekType type)
{
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()->blockEmpty(false); // was buffering
t->packetQueue()->clear();
}
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);
demuxer->seek(pos);
Expand All @@ -176,10 +169,15 @@ void AVDemuxThread::seekInternal(qint64 pos, SeekType type)
if (!t)
continue;
t->packetQueue()->clear();
t->packetQueue()->setBlocking(true); // blockEmpty was false when eof is read.
// TODO: the first frame (key frame) will not be decoded correctly if flush() is called.
if (type == AccurateSeek)
t->packetQueue()->put(Packet());
if (type == AccurateSeek) {
Packet pkt;
pkt.pts = qreal(pos)/1000.0;
//qDebug("put seek packet. %d/%d-%d, progress: %.3f", pb->buffered(), pb->bufferValue(), pb->bufferMax(), pb->bufferProgress());
t->packetQueue()->setBlocking(false); // aqueue bufferValue can be small (1), we can not put and take
t->packetQueue()->put(pkt);
}
t->packetQueue()->setBlocking(true); // blockEmpty was false when eof is read.
}
if (isPaused() && (video_thread || audio_thread)) {
AVThread *thread = video_thread ? video_thread : audio_thread;
Expand Down
8 changes: 0 additions & 8 deletions src/AVPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -811,14 +811,6 @@ void AVPlayer::setPosition(qint64 position)
d->seeking = true;
d->seek_target = position;
qreal s = (qreal)pos_pts/1000.0;
if (seekType() == AccurateSeek) {
if (d->athread) {
d->athread->skipRenderUntil(s);
}
if (d->vthread) {
d->vthread->skipRenderUntil(s);
}
}
masterClock()->updateValue(double(pos_pts)/1000.0); //what is duration == 0
masterClock()->updateExternalClock(pos_pts); //in msec. ignore usec part using t/1000
d->read_thread->seek(pos_pts, seekType());
Expand Down
26 changes: 1 addition & 25 deletions src/AVThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,30 +108,6 @@ void AVThread::scheduleTask(QRunnable *task)
d_func().tasks.put(task);
}

void AVThread::skipRenderUntil(qreal pts)
{
/*
* Lock here is useless because in Audio/VideoThread, the lock scope is very small.
* So render_pts0 may be reset to 0 after set here
*/
DPTR_D(AVThread);
class SetRenderPTS0Task : public QRunnable {
public:
SetRenderPTS0Task(qreal* pts0, qreal value)
: ptr(pts0)
, pts(value)
{}
void run() {
*ptr = pts;
}
private:
qreal *ptr;
qreal pts;
};

scheduleTask(new SetRenderPTS0Task(&d.render_pts0, pts));
}

// TODO: shall we close decoder here?
void AVThread::stop()
{
Expand Down Expand Up @@ -245,7 +221,7 @@ void AVThread::resetState()
DPTR_D(AVThread);
pause(false);
d.tasks.clear();
d.render_pts0 = 0;
d.render_pts0 = -1;
d.stop = false;
d.packets.setBlocking(true);
d.packets.clear();
Expand Down
2 changes: 0 additions & 2 deletions src/AVThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ class AVThread : public QThread

// TODO: resample, resize task etc.
void scheduleTask(QRunnable *task);
//only decode video without display or skip decode audio until pts reaches
void skipRenderUntil(qreal pts);

public slots:
virtual void stop();
Expand Down
2 changes: 1 addition & 1 deletion src/AVThread_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class AVThreadPrivate : public DPtrPrivate<AVThread>
, delay(0)
, statistics(0)
, ready(false)
, render_pts0(0)
, render_pts0(-1)
{
tasks.blockFull(false);
}
Expand Down
14 changes: 12 additions & 2 deletions src/AudioThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,11 @@ void AudioThread::run()
Q_UNUSED(locker);
if (d.dec) //maybe set to null in setDecoder()
d.dec->flush();
d.render_pts0 = pkt.pts;
continue;
}
qreal dts = pkt.dts; //FIXME: pts and dts
// no key frame for audio. so if pts reaches, try decode and skip render if got frame pts does not reach
bool skip_render = pkt.pts < d.render_pts0;
// audio has no key frame, skip rendering equals to skip decoding
if (skip_render) {
Expand All @@ -130,7 +132,6 @@ void AudioThread::run()
pkt = Packet(); //mark invalid to take next
continue;
}
d.render_pts0 = 0;
if (is_external_clock) {
d.delay = dts - d.clock->value();
/*
Expand Down Expand Up @@ -223,7 +224,16 @@ void AudioThread::run()
#if USE_AUDIO_FRAME
AudioFrame frame(dec->frame());
if (frame) {
//TODO: apply filters here
if (d.render_pts0 >= 0.0) { // seeking
if (frame.timestamp() < d.render_pts0) {
qDebug("skip audio rendering: %f-%f", frame.timestamp(), d.render_pts0);
d.clock->updateValue(frame.timestamp());
pkt = Packet();
continue;
}
d.render_pts0 = -1.0;
Q_EMIT seekFinished(qint64(frame.timestamp()*1000.0));
}
if (has_ao) {
applyFilters(frame);
frame.setAudioResampler(dec->resampler()); //!!!
Expand Down
13 changes: 6 additions & 7 deletions src/Packet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Packet Packet::createEOF()

bool Packet::isEOF() const
{
return data == "eof";
return data == "eof" && pts < 0.0 && dts < 0.0;
}

Packet Packet::fromAVPacket(const AVPacket *avpkt, double time_base)
Expand Down Expand Up @@ -131,7 +131,7 @@ bool Packet::fromAVPacket(Packet* pkt, const AVPacket *avpkt, double time_base)
#if AVPACKET_REF
av_packet_ref(p, (AVPacket*)avpkt); //properties are copied internally
// add ref without copy, bytearray does not copy either. bytearray options linke remove() is safe. omit FF_INPUT_BUFFER_PADDING_SIZE
pkt->data =QByteArray::fromRawData((const char*)p->data, p->size);
pkt->data = QByteArray::fromRawData((const char*)p->data, p->size);
#else
if (avpkt->data) {
// copy packet data. packet will be reset after AVDemuxer.readFrame() and in next av_read_frame
Expand Down Expand Up @@ -165,10 +165,10 @@ bool Packet::fromAVPacket(Packet* pkt, const AVPacket *avpkt, double time_base)
Packet::Packet()
: hasKeyFrame(false)
, isCorrupt(false)
, pts(0)
, duration(0)
, dts(0)
, position(0)
, pts(-1)
, duration(-1)
, dts(-1)
, position(-1)
{
}

Expand Down Expand Up @@ -232,5 +232,4 @@ const AVPacket *Packet::asAVPacket() const
}
return p;
}

} //namespace QtAV
67 changes: 22 additions & 45 deletions src/VideoThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,6 @@ void VideoThread::run()
int nb_dec_slow = 0;
int nb_dec_fast = 0;

bool is_pkt_bf_seek = true; // when seeking, a packet token from queue maybe before queue.clear() and packet is old. we may skip compare, wait and decode.
qint32 seek_count = 0; // wm4 says: 1st seek can not use frame drop for decoder
// TODO: kNbSlowSkip depends on video fps, ensure slow time <= 2s
/* kNbSlowSkip: if video frame slow count >= kNbSlowSkip, skip decoding all frames until next keyframe reaches.
Expand All @@ -307,10 +306,10 @@ void VideoThread::run()
const int kNbSlowSkip = 120; // about 1s for 120fps video
// kNbSlowFrameDrop: if video frame slow count > kNbSlowFrameDrop, skip decoding nonref frames. only some of ffmpeg based decoders support it.
const int kNbSlowFrameDrop = 10;
int seek_done_count = 0; // seek_done_count > 1 means seeking is really finished. theorically can't be >1 if seeking!
bool sync_audio = d.clock->clockType() == AVClock::AudioClock;
bool sync_video = d.clock->clockType() == AVClock::VideoClock; // no frame drop
const qint64 start_time = QDateTime::currentMSecsSinceEpoch();
bool skip_render = false; // keep true if decoded frame does not reach desired time
while (!d.stop) {
processNextTask();
//TODO: why put it at the end of loop then playNextFrame() not work?
Expand All @@ -336,6 +335,7 @@ void VideoThread::run()
wait_key_frame = true;
qDebug("Invalid packet! flush video codec context!!!!!!!!!! video packet queue size: %d", d.packets.size());
dec->flush();
d.render_pts0 = pkt.pts;
continue;
}
if (d.clock->clockType() == AVClock::AudioClock) {
Expand All @@ -358,14 +358,13 @@ void VideoThread::run()
} else {
nb_dec_fast /= 2;
}
bool seeking = !qFuzzyIsNull(d.render_pts0);
bool seeking = d.render_pts0 >= 0.0;
if (seeking) {
nb_dec_slow = 0;
nb_dec_fast = 0;
}
//qDebug("nb_fast: %d", nb_dec_fast);
//qDebug("nb_fast: %d. diff: %f, dts: %f, clock: %f", nb_dec_fast, diff, dts, clock()->value());
if (d.delay < -0.5 && d.delay > diff) {
seeking = !qFuzzyIsNull(d.render_pts0);
if (!seeking) {
// ensure video will not later than 2s
if (diff < -2 || (nb_dec_slow > kNbSlowSkip && diff < -1.0 && !pkt.hasKeyFrame)) {
Expand Down Expand Up @@ -393,15 +392,17 @@ void VideoThread::run()
*after seeking forward, a packet may be the old, v packet may be
*the new packet, then the d.delay is very large, omit it.
*/
bool skip_render = dts < d.render_pts0; // FIXME: check after decode()
if (pkt.pts < d.render_pts0) // skip rendering until decoded frame reaches desired pts
skip_render = true;
if (seeking)
diff = 0; // TODO: here?
if (!sync_audio && diff > 0) {
// wait to dts reaches
waitAndCheck(diff*1000UL, dts); // TODO: count decoding and filter time
diff = 0; // TODO: can not change delay!
}
// update here after wait
d.clock->updateVideoTime(dts); // FIXME: dts or pts?
seeking = !qFuzzyIsNull(d.render_pts0);
if (qAbs(diff) < 0.5) {
if (diff < -kSyncThreshold) { //Speed up. drop frame?
//continue;
Expand All @@ -425,41 +426,25 @@ void VideoThread::run()
}
}
} else {
// video too fast if old packet before seek backward compared with new audio packet after seek backward
// what about video too late?
if (is_pkt_bf_seek && diff > 2.0) {
// if seeking, we can not continue without decoding
// FIXME: also happens after seek but not seeking. so must check seek_done_count
is_pkt_bf_seek = false;
if (seek_done_count <= 1) { // is seeking
qDebug("old video packet before seek detected!!!!!!!!!!!!!");
pkt = Packet();
continue; // seeking and this v packet is before seeking
}
}
const double s = qMin<qreal>(0.01*(nb_dec_fast>>1), diff);
qWarning("video too fast!!! sleep %.2f s, nb fast: %d", s, nb_dec_fast);
waitAndCheck(s*1000UL, dts);
diff = 0;
}
}
seeking = !qFuzzyIsNull(d.render_pts0);
//qDebug("d.render_pts0: %f, seeking: %d", d.render_pts0, seeking);
//audio packet not cleaned up?
if (diff > 0 && diff < 1.0 && !seeking) {
// can not change d.delay here! we need it to comapre to next loop
waitAndCheck(diff*1000UL, dts);
}
if (wait_key_frame) {
if (pkt.hasKeyFrame)
wait_key_frame = false;
else {
if (!pkt.hasKeyFrame) {
qDebug("waiting for key frame. queue size: %d. pkt.size: %d", d.packets.size(), pkt.data.size());
pkt = Packet();
continue;
}
wait_key_frame = false;
}
seeking = !qFuzzyIsNull(d.render_pts0);
QVariantHash *dec_opt_old = dec_opt;
if (!seeking) { // MAYBE not seeking
if (nb_dec_slow < kNbSlowFrameDrop) {
Expand Down Expand Up @@ -505,30 +490,23 @@ void VideoThread::run()
qWarning() << "invalid video frame from decoder";
continue;
}
is_pkt_bf_seek = true;
if (skip_render) {
pkt = Packet(); //mark invalid to take next
continue;
}
if (frame.timestamp() == 0)
frame.setTimestamp(pkt.pts); // pkt.pts is wrong. >= real timestamp
const qreal pts = frame.timestamp();
// can not check only pts > render_pts0 for seek backward. delta can not be too small(smaller than 1/fps)
// FIXME: what if diff too large?
if (d.render_pts0 > 0 && pts > d.render_pts0) {
if (seek_done_count > 1)
seek_done_count = 0;
seek_done_count++;
if (seek_done_count > 1) { // theorically can't be >1 if seeking!
qDebug("reset render_pts0");
d.render_pts0 = 0;
Q_EMIT seekFinished(qint64(pts*1000.0));
// 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);
if (d.render_pts0 >= 0.0) {
if (pts < d.render_pts0) {
pkt = Packet();
continue;
}
d.render_pts0 = -1;
Q_EMIT seekFinished(qint64(pts*1000.0));
if (seek_count == -1)
seek_count = 1;
else if (seek_count > 0)
seek_count++;
}
if (seek_count == -1 && seek_done_count > 0)
seek_count = 1;
else if (seek_count > 0)
seek_count++;
Q_ASSERT(d.statistics);
d.statistics->video.current_time = QTime(0, 0, 0).addMSecs(int(pts * 1000.0)); //TODO: is it expensive?
applyFilters(frame);
Expand Down Expand Up @@ -564,7 +542,6 @@ void VideoThread::run()
++d.force_dt;
}
} else if (false) { //FIXME: may block a while when seeking
seeking = !qFuzzyIsNull(d.render_pts0);
const qreal display_wait = pts - clock()->value();
if (!seeking && display_wait > 0.0) {
// wait to pts reaches. TODO: count rendering time
Expand Down

0 comments on commit 84700f2

Please sign in to comment.