From 0f7425d47318abe1a4df9a8ae1b42b211bdbc253 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 6 Mar 2019 00:56:33 -0800 Subject: [PATCH] check queue for retrieved frame --- project/clip.cpp | 17 +++---- rendering/cacher.cpp | 98 ++++++++++++++++++----------------------- rendering/cacher.h | 15 +++---- rendering/clipqueue.cpp | 5 +++ rendering/clipqueue.h | 9 ++++ 5 files changed, 71 insertions(+), 73 deletions(-) diff --git a/project/clip.cpp b/project/clip.cpp index 0a0902b5f..7c61c4fe0 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -521,12 +521,8 @@ bool Clip::IsOpen() } void Clip::Cache(long playhead, bool scrubbing, QVector& nests, int playback_speed) { -// qint64 time = QDateTime::currentMSecsSinceEpoch(); - cacher.Cache(playhead, scrubbing, nests, playback_speed); cacher_frame = playhead; - -// qDebug() << "Clip::Cache took" << (QDateTime::currentMSecsSinceEpoch() - time); } bool Clip::Retrieve() @@ -535,13 +531,14 @@ bool Clip::Retrieve() if (UsesCacher()) { -// qint64 time = QDateTime::currentMSecsSinceEpoch(); - AVFrame* frame = cacher.Retrieve(); - cacher.QueueLock(); + cacher.queue()->lock(); - if (frame != nullptr) { + // Check if we retrieved a frame (nullptr) and if the queue stil contains this frame. + // + // In some situations it's + if (frame != nullptr && cacher.queue()->contains(frame)) { // check if the opengl texture exists yet, create it if not if (texture == nullptr) { @@ -599,14 +596,12 @@ bool Clip::Retrieve() glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); -// qDebug() << "Clip::Retrieve took" << (QDateTime::currentMSecsSinceEpoch() - time); - ret = true; } else { qCritical() << "Failed to retrieve frame for clip" << name(); } - cacher.QueueUnlock(); + cacher.queue()->unlock(); } return ret; diff --git a/rendering/cacher.cpp b/rendering/cacher.cpp index 046cd936c..f7cccf495 100644 --- a/rendering/cacher.cpp +++ b/rendering/cacher.cpp @@ -167,7 +167,7 @@ void Cacher::CacheAudioWorker() { } else if (clip->media()->get_type() == MEDIA_TYPE_FOOTAGE) { double timebase = av_q2d(stream->time_base); - frame = queue.at(0); + frame = queue_.at(0); // retrieve frame bool new_frame = false; @@ -233,7 +233,7 @@ void Cacher::CacheAudioWorker() { if (reverse_audio) { if (loop > 1) { - AVFrame* rev_frame = queue.at(1); + AVFrame* rev_frame = queue_.at(1); if (ret != AVERROR_EOF) { if (loop == 2) { #ifdef AUDIOWARNINGS @@ -368,7 +368,7 @@ void Cacher::CacheAudioWorker() { } } - if (reverse_audio) frame = queue.at(1); + if (reverse_audio) frame = queue_.at(1); #ifdef AUDIOWARNINGS dout << "j" << frame_sample_index << nb_bytes; @@ -459,7 +459,7 @@ void Cacher::CacheVideoWorker() { // for efficiency, we do slightly different things for a still image // if we already queued a frame, we don't actually need to cache anything, so we only retrieve a frame if not - if (queue.size() == 0) { + if (queue_.size() == 0) { // retrieve a single frame @@ -470,9 +470,9 @@ void Cacher::CacheVideoWorker() { if (RetrieveFrameAndProcess(&still_image_frame) >= 0) { - queue.lock(); - queue.append(still_image_frame); - queue.unlock(); + queue_.lock(); + queue_.append(still_image_frame); + queue_.unlock(); SetRetrievedFrame(still_image_frame); } @@ -496,13 +496,13 @@ void Cacher::CacheVideoWorker() { int64_t latest_pts = INT64_MIN; int frames_greater_than_target = 0; - for (int i=0;ipts); - latest_pts = qMax(latest_pts, queue.at(i)->pts); + earliest_pts = qMin(earliest_pts, queue_.at(i)->pts); + latest_pts = qMax(latest_pts, queue_.at(i)->pts); // count upcoming frames - if (queue.at(i)->pts > target_pts) { + if (queue_.at(i)->pts > target_pts) { frames_greater_than_target++; } } @@ -510,16 +510,16 @@ void Cacher::CacheVideoWorker() { // check if the frame is within this queue or if we'll have to seek elsewhere to get it // (we check for one second of time after latest_pts, because if it's within that range it'll likely be faster to // play up to that frame than seek to it) - if (target_pts < earliest_pts || target_pts > latest_pts + second_pts || queue.size() == 0) { + if (target_pts < earliest_pts || target_pts > latest_pts + second_pts || queue_.size() == 0) { // we need to seek to retrieve this frame avcodec_flush_buffers(codecCtx); av_seek_frame(formatCtx, clip->media_stream_index(), target_pts, AVSEEK_FLAG_BACKWARD); // also we assume none of the frames in the queue are usable - queue.lock(); - queue.clear(); - queue.unlock(); + queue_.lock(); + queue_.clear(); + queue_.unlock(); // reset upcoming frame count and latest pts for later calculations frames_greater_than_target = 0; @@ -595,15 +595,15 @@ void Cacher::CacheVideoWorker() { if (decoded_frame->pts == target_pts) { SetRetrievedFrame(decoded_frame); } else if (decoded_frame->pts > target_pts - && queue.size() > 0) { - SetRetrievedFrame(queue.last()); + && queue_.size() > 0) { + SetRetrievedFrame(queue_.last()); } } // add the frame to the queue - queue.lock(); - queue.append(decoded_frame); - queue.unlock(); + queue_.lock(); + queue_.append(decoded_frame); + queue_.unlock(); // check the amount of previous frames in the queue by using the current queue size for if we need to // remove any old entries (assumes the queue is chronological) @@ -613,13 +613,13 @@ void Cacher::CacheVideoWorker() { if (decoded_frame->pts < target_pts) { // if this frame is before the target frame, make sure we don't add too many of them - previous_frame_count = queue.size(); + previous_frame_count = queue_.size(); } else { // if this frame is after the target frame, clean up any previous frames before it // TODO is there a faster way to do this? - for (int i=0;ipts > target_pts) { + for (int i=0;ipts > target_pts) { break; } else { previous_frame_count++; @@ -630,9 +630,9 @@ void Cacher::CacheVideoWorker() { // remove frames while the amount of previous frames exceeds the maximum while (previous_frame_count > minimum_ts) { - queue.lock(); - queue.removeFirst(); - queue.unlock(); + queue_.lock(); + queue_.removeFirst(); + queue_.unlock(); previous_frame_count--; } @@ -668,14 +668,14 @@ void Cacher::CacheVideoWorker() { qWarning() << clip->name() << "frame had no PTS value"; av_frame_free(&decoded_frame); - if (retrieve_code == AVERROR_EOF && retrieved_frame == nullptr && !queue.isEmpty()) { + if (retrieve_code == AVERROR_EOF && retrieved_frame == nullptr && !queue_.isEmpty()) { // if we reached the end of the file, it's not an error but there are no more frames to retrieve // some formats EOF before the end of the duration that Olive calculates. In this event, we simply // return the last frame we retrieved // // TODO: Check duration formula - SetRetrievedFrame(queue.last()); + SetRetrievedFrame(queue_.last()); } else { @@ -899,7 +899,7 @@ void Cacher::OpenWorker() { if (codecCtx->channel_layout == 0) codecCtx->channel_layout = av_get_default_channel_layout(stream->codecpar->channels); // set up cache - queue.append(av_frame_alloc()); + queue_.append(av_frame_alloc()); // if (clip->reverse) { if (true) { AVFrame* reverse_frame = av_frame_alloc(); @@ -910,7 +910,7 @@ void Cacher::OpenWorker() { reverse_frame->channels = av_get_channel_layout_nb_channels(clip->sequence->audio_layout); av_frame_get_buffer(reverse_frame, 0); - queue.append(reverse_frame); + queue_.append(reverse_frame); } snprintf(filter_args, sizeof(filter_args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%" PRIx64, @@ -1002,9 +1002,9 @@ void Cacher::CacheWorker() { void Cacher::CloseWorker() { retrieved_frame = nullptr; - queue.lock(); - queue.clear(); - queue.unlock(); + queue_.lock(); + queue_.clear(); + queue_.unlock(); if (frame_ != nullptr) { av_frame_free(&frame_); @@ -1075,9 +1075,9 @@ void Cacher::Open() void Cacher::Cache(long playhead, bool scrubbing, QVector& nests, int playback_speed) { if (clip->media_stream() != nullptr - && queue.size() > 0 + && queue_.size() > 0 && clip->media_stream()->infinite_length) { - retrieved_frame = queue.at(0); + retrieved_frame = queue_.at(0); return; } @@ -1092,28 +1092,28 @@ void Cacher::Cache(long playhead, bool scrubbing, QVector& nests, int pla if (clip->media() != nullptr) { // see if we already have this frame retrieve_lock_.lock(); - queue.lock(); + queue_.lock(); retrieved_frame = nullptr; int64_t target_pts = seconds_to_timestamp(clip, playhead_to_clip_seconds(clip, playhead_)); - for (int i=0;ipts == target_pts) { + if (queue_.at(i)->pts == target_pts) { // the queue has a frame with the exact timestamp - retrieved_frame = queue.at(i); + retrieved_frame = queue_.at(i); wait_for_cacher_to_respond = false; break; - } else if (i > 0 && queue.at(i-1)->pts < target_pts && queue.at(i)->pts > target_pts) { + } else if (i > 0 && queue_.at(i-1)->pts < target_pts && queue_.at(i)->pts > target_pts) { // the queue has a frame with a close timestamp that we'll assume is different due to a rounding error - retrieved_frame = queue.at(i-1); + retrieved_frame = queue_.at(i-1); wait_for_cacher_to_respond = false; break; } } - queue.unlock(); + queue_.unlock(); retrieve_lock_.unlock(); } @@ -1140,8 +1140,6 @@ void Cacher::Cache(long playhead, bool scrubbing, QVector& nests, int pla AVFrame *Cacher::Retrieve() { -// qint64 time = QDateTime::currentMSecsSinceEpoch(); - if (!caching_) { return nullptr; } @@ -1160,7 +1158,6 @@ AVFrame *Cacher::Retrieve() } else { // cacher is running, wait for it to give a frame -// qDebug() << "====> retrieve lock waiting"; retrieve_lock_.lock(); retrieve_wait_.wait(&retrieve_lock_); retrieve_lock_.unlock(); @@ -1169,8 +1166,6 @@ AVFrame *Cacher::Retrieve() } -// qDebug() << "Cacher::Retrieve took" << (QDateTime::currentMSecsSinceEpoch() - time) << "and retrieved" << retrieved_frame; - return retrieved_frame; } @@ -1211,14 +1206,9 @@ AVRational Cacher::media_time_base() return stream->time_base; } -void Cacher::QueueLock() +ClipQueue *Cacher::queue() { - queue.lock(); -} - -void Cacher::QueueUnlock() -{ - queue.unlock(); + return &queue_; } int Cacher::RetrieveFrameFromDecoder(AVFrame* f) { diff --git a/rendering/cacher.h b/rendering/cacher.h index e8d100852..8003420cd 100644 --- a/rendering/cacher.h +++ b/rendering/cacher.h @@ -243,14 +243,13 @@ public: AVRational media_time_base(); /** - * @brief Wrapper function for queue::lock() + * @brief Get cacher queue object + * + * @return + * + * A pointer to the cacher's internal frame queue */ - void QueueLock(); - - /** - * @brief Wrapper function for queue::unlock() - */ - void QueueUnlock(); + ClipQueue* queue(); private: /** @@ -263,7 +262,7 @@ private: * * Valid fames are cached into this, which also does memory handling when necessary. */ - ClipQueue queue; + ClipQueue queue_; /** * @brief Main wait condition diff --git a/rendering/clipqueue.cpp b/rendering/clipqueue.cpp index 6ec335347..e1e6e2fe2 100644 --- a/rendering/clipqueue.cpp +++ b/rendering/clipqueue.cpp @@ -98,3 +98,8 @@ bool ClipQueue::isEmpty() { return queue.isEmpty(); } + +bool ClipQueue::contains(AVFrame *frame) +{ + return queue.contains(frame); +} diff --git a/rendering/clipqueue.h b/rendering/clipqueue.h index 3a023c62d..700ed285e 100644 --- a/rendering/clipqueue.h +++ b/rendering/clipqueue.h @@ -168,6 +168,15 @@ public: */ bool isEmpty(); + /** + * @brief Returns whether the queue contains a frame or not + * + * @return + * + * **TRUE** if the queue contains the specified frame, **FALSE** if not. + */ + bool contains(AVFrame* frame); + private: QVector queue; QMutex queue_lock;