diff --git a/src/AVDemuxThread.cpp b/src/AVDemuxThread.cpp index 01c5f085..6d6f0b59 100644 --- a/src/AVDemuxThread.cpp +++ b/src/AVDemuxThread.cpp @@ -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); @@ -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; diff --git a/src/AVPlayer.cpp b/src/AVPlayer.cpp index 1fc2affe..b68a4d9e 100644 --- a/src/AVPlayer.cpp +++ b/src/AVPlayer.cpp @@ -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()); diff --git a/src/AVThread.cpp b/src/AVThread.cpp index 7a38915b..b4634fb9 100644 --- a/src/AVThread.cpp +++ b/src/AVThread.cpp @@ -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() { @@ -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(); diff --git a/src/AVThread.h b/src/AVThread.h index c6397560..24800022 100644 --- a/src/AVThread.h +++ b/src/AVThread.h @@ -75,8 +75,6 @@ public: // 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(); diff --git a/src/AVThread_p.h b/src/AVThread_p.h index 918c2e94..be949120 100644 --- a/src/AVThread_p.h +++ b/src/AVThread_p.h @@ -51,7 +51,7 @@ public: , delay(0) , statistics(0) , ready(false) - , render_pts0(0) + , render_pts0(-1) { tasks.blockFull(false); } diff --git a/src/AudioThread.cpp b/src/AudioThread.cpp index 351a061e..70e7bf78 100644 --- a/src/AudioThread.cpp +++ b/src/AudioThread.cpp @@ -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) { @@ -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(); /* @@ -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()); //!!! diff --git a/src/Packet.cpp b/src/Packet.cpp index bad5ec38..c127954f 100644 --- a/src/Packet.cpp +++ b/src/Packet.cpp @@ -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) @@ -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 @@ -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) { } @@ -232,5 +232,4 @@ const AVPacket *Packet::asAVPacket() const } return p; } - } //namespace QtAV diff --git a/src/VideoThread.cpp b/src/VideoThread.cpp index dad5ba2a..7e445742 100644 --- a/src/VideoThread.cpp +++ b/src/VideoThread.cpp @@ -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. @@ -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? @@ -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) { @@ -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)) { @@ -393,7 +392,10 @@ 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 @@ -401,7 +403,6 @@ void VideoThread::run() } // 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; @@ -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(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) { @@ -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); @@ -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