From 94efe979295647c68631192647b97f50389986b5 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 14 Oct 2018 00:19:50 +1100 Subject: [PATCH] bic boi video caching rewrite --- effects/audio/volumeeffect.cpp | 2 +- playback/cacher.cpp | 219 +++++++++++++-------------------- playback/cacher.h | 4 +- playback/playback.cpp | 137 +++++++++++++++++---- playback/playback.h | 15 ++- project/clip.cpp | 21 ++-- project/clip.h | 22 ++-- ui/viewerwidget.cpp | 12 +- 8 files changed, 243 insertions(+), 189 deletions(-) diff --git a/effects/audio/volumeeffect.cpp b/effects/audio/volumeeffect.cpp index 51039af86..d4fba8ac0 100644 --- a/effects/audio/volumeeffect.cpp +++ b/effects/audio/volumeeffect.cpp @@ -13,7 +13,7 @@ VolumeEffect::VolumeEffect(Clip* c) : Effect(c, EFFECT_TYPE_AUDIO, AUDIO_VOLUME_ EffectRow* volume_row = add_row("Volume:"); volume_val = volume_row->add_field(EFFECT_FIELD_DOUBLE); volume_val->set_double_minimum_value(0); - volume_val->set_double_maximum_value(400); +// volume_val->set_double_maximum_value(1000); // set defaults volume_val->set_double_default_value(100); diff --git a/playback/cacher.cpp b/playback/cacher.cpp index 15e402702..d49c930f3 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -247,7 +247,7 @@ void cache_audio_worker(Clip* c, Clip* nest) { if (c->audio_just_reset) { // get precise sample offset for the elected clip_in from this audio frame - double target_sts = playhead_to_seconds(c, c->audio_target_frame); + double target_sts = playhead_to_clip_seconds(c, c->audio_target_frame); double frame_sts = (frame->pts * timebase); int nb_samples = qRound((target_sts - frame_sts)*c->sequence->audio_frequency); c->frame_sample_index = nb_samples * 4; @@ -354,89 +354,43 @@ void cache_audio_worker(Clip* c, Clip* nest) { } } -void cache_video_worker(Clip* c, long playhead, ClipCache* cache) { - cache->mutex.lock(); +void cache_video_worker(Clip* c) { + int read_ret, send_ret, retr_ret; + while (c->queue.size() < c->max_queue_size) { + AVFrame* frame = av_frame_alloc(); - cache->offset = playhead; - - bool error = false; - - int i = 0; - - /* old swscale solution - if (!c->reached_end) { - while (i < c->cache_size) { - retrieve_next_frame_raw_data(c, cache->frames[i]); - - if (c->reached_end) break; - i++; - } - }*/ - cache->unread = true; - - /* new AVFilter solution */ - if (!c->reached_end) { - while (i < c->cache_size) { - if (c->skip_type == SKIP_TYPE_SEEK) { - long seek_frame = refactor_frame_number(i + cache->offset, c->getMediaFrameRate(), c->getMediaFrameRate()*c->speed); - reset_cache(c, seek_frame); - } - - av_frame_unref(cache->frames[i]); - - int ret = (c->filter_graph == NULL) ? AVERROR(EAGAIN) : av_buffersink_get_frame(c->buffersink_ctx, cache->frames[i]); - - if (ret < 0) { - if (ret == AVERROR(EAGAIN)) { - ret = retrieve_next_frame(c, c->frame); - - if (ret >= 0) { - if ((ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, c->frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) { - qDebug() << "[ERROR] Could not feed filtergraph -" << ret; - error = true; - break; - } - } else { - if (ret == AVERROR_EOF) { - // TODO sorta hacky? not even sure if "reached_end" serves a proper purpose anymore - if (!c->reverse) c->reached_end = true; - } else { - qDebug() << "[WARNING] Raw frame data could not be retrieved." << ret; - error = true; - } - break; - } - } else { - if (ret != AVERROR_EOF) { - qDebug() << "[ERROR] Could not pull from filtergraph"; - error = true; - } + while ((retr_ret = av_buffersink_get_frame(c->buffersink_ctx, frame)) == AVERROR(EAGAIN)) { + AVFrame* send_frame = c->frame; + read_ret = (c->use_existing_frame) ? 0 : retrieve_next_frame(c, send_frame); + c->use_existing_frame = false; + if (read_ret >= 0 || read_ret == AVERROR_EOF) { + if (read_ret == AVERROR_EOF) send_frame = NULL; + if ((send_ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, send_frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) { + qDebug() << "[ERROR] Failed to add frame to buffer source." << send_ret; break; } + if (read_ret >= 0) { + av_frame_unref(c->frame); + } } else { - qDebug() << "retrieved PTS:" << cache->frames[i]->pts << c->stream->time_base.num << "/" << c->stream->time_base.den; - - cache->written = true; - i++; - cache->write_count = i; + qDebug() << "[ERROR] Failed to read frame." << read_ret; + break; } } + + if (retr_ret < 0) { + qDebug() << "[ERROR] Failed to retrieve frame from buffersink." << retr_ret; + av_frame_free(&frame); + break; + } else { + // thread-safety while adding frame to the queue + c->queue_lock.lock(); + c->queue.append(frame); + c->queue_lock.unlock(); + } } - - cache->write_count = i; - - /*if (!error) { - // setting the cache to written even if it hasn't reached_end prevents playback from - // signaling a seek/reset because it wasn't able to find the frame - cache->written = true; - cache->unread = true; - }*/ - - cache->mutex.unlock(); } - - void reset_cache(Clip* c, long target_frame) { // if we seek to a whole other place in the timeline, we'll need to reset the cache with new values switch (c->media_type) { @@ -445,48 +399,63 @@ void reset_cache(Clip* c, long target_frame) { c->reached_end = false; MediaStream* ms = static_cast(c->media)->get_stream_from_file_index(c->track < 0, c->media_stream); if (!ms->infinite_length) { - // flush ffmpeg codecs - avcodec_flush_buffers(c->codecCtx); - - // flush filtergraph - /*while (av_buffersink_get_frame(c->buffersink_ctx, temp) >= 0) { - qDebug() << "flushed?"; - av_frame_unref(temp); - }*/ - /*av_buffersrc_add_frame(c->buffersrc_ctx, NULL); - av_frame_unref(c->frame); - while (av_buffersink_get_frame(c->buffersink_ctx, c->frame) >= 0) { - av_frame_unref(c->frame); - }*/ - - double timebase = av_q2d(c->stream->time_base); - if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - if (target_frame > 0) { - AVFrame* temp = av_frame_alloc(); + // clear current queue + c->clear_queue(); - // seeks to nearest keyframe (target_frame represents internal clip frame) - int64_t seek_ts = qRound(clip_frame_to_seconds(c, target_frame) / timebase); - qDebug() << "requesting: " << seek_ts; - av_seek_frame(c->formatCtx, ms->file_index, seek_ts, AVSEEK_FLAG_BACKWARD); + // seeks to nearest keyframe (target_frame represents internal clip frame) + int64_t target_ts = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame)); +// if (ms->video_interlacing != VIDEO_PROGRESSIVE) target_ts *= 2; + int64_t seek_ts = target_ts; - // play up to the frame we actually want - int ret; - do { - ret = retrieve_next_frame(c, temp); + int64_t timebase_half_second = qRound(av_q2d(av_inv_q(c->stream->time_base))); + while (true) { + // flush ffmpeg codecs + avcodec_flush_buffers(c->codecCtx); + + if (seek_ts > 0) { + av_seek_frame(c->formatCtx, ms->file_index, seek_ts, AVSEEK_FLAG_BACKWARD); + + // play up to the frame we actually want + /*int ret; + do { + av_frame_unref(c->frame); + ret = retrieve_next_frame(c, c->frame); + if (ret < 0) { + qDebug() << "[WARNING] Seeking terminated prematurely"; + break; + } + qDebug() << "ret pts:" << c->frame->pts << "dur:" << c->frame->pkt_duration << "target:" << target_ts; + } while (c->frame->pts + c->frame->pkt_duration + c->frame->pkt_duration < target_ts); + if (c->frame->pts <= target_ts) { + c->use_existing_frame = true; + break; + } else { + seek_ts -= timebase_second; + }*/ + av_frame_unref(c->frame); + int ret = retrieve_next_frame(c, c->frame); if (ret < 0) { qDebug() << "[WARNING] Seeking terminated prematurely"; break; } - } while (temp->pts + temp->pkt_duration + temp->pkt_duration < seek_ts); - av_frame_unref(temp); - av_frame_free(&temp); - } else { - av_seek_frame(c->formatCtx, ms->file_index, 0, AVSEEK_FLAG_BACKWARD); + qDebug() << "seek ts:" << seek_ts << "target ts:" << target_ts << "retrieved ts:" << c->frame->pts; + if (c->frame->pts <= target_ts) { + c->use_existing_frame = true; + break; + } else { + seek_ts -= timebase_half_second; + } + } else { + av_frame_unref(c->frame); + av_seek_frame(c->formatCtx, ms->file_index, 0, AVSEEK_FLAG_BACKWARD); + c->use_existing_frame = false; + break; + } } } else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { // seek (target_frame represents timeline timecode in frames, not clip timecode) - int64_t timestamp = qRound(playhead_to_seconds(c, target_frame) / timebase); // TODO qRound here might lead to clicking? or might fix it... who knows + int64_t timestamp = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame)); // TODO qRound here might lead to clicking? or might fix it... who knows if (c->reverse) { c->rev_target = timestamp; timestamp -= av_q2d(av_inv_q(c->stream->time_base)); @@ -550,6 +519,8 @@ void open_clip_worker(Clip* clip) { clip->codecCtx = avcodec_alloc_context3(clip->codec); avcodec_parameters_to_context(clip->codecCtx, clip->stream->codecpar); + clip->max_queue_size = 30; + AVDictionary* opts = NULL; // optimized decoding settings @@ -597,14 +568,6 @@ void open_clip_worker(Clip* clip) { // if (clip->skip_type == SKIP_TYPE_SEEK) clip->cache_size *= 2; if (ms->video_interlacing != VIDEO_PROGRESSIVE) clip->cache_size *= 2; - clip->cache_A.frames = new AVFrame* [clip->cache_size]; - clip->cache_B.frames = new AVFrame* [clip->cache_size]; - - for (int i=0;icache_size;i++) { - clip->cache_A.frames[i] = av_frame_alloc(); - clip->cache_B.frames[i] = av_frame_alloc(); - } - snprintf(filter_args, sizeof(filter_args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", clip->stream->codecpar->width, clip->stream->codecpar->height, @@ -750,7 +713,7 @@ void open_clip_worker(Clip* clip) { qDebug() << "[INFO] Clip opened on track" << clip->track; } -void cache_clip_worker(Clip* clip, long playhead, bool write_A, bool write_B, bool reset, Clip* nest) { +void cache_clip_worker(Clip* clip, long playhead, bool reset, Clip* nest) { if (reset) { // note: for video, playhead is in "internal clip" frames - for audio, it's the timeline playhead reset_cache(clip, playhead); @@ -760,14 +723,7 @@ void cache_clip_worker(Clip* clip, long playhead, bool write_A, bool write_B, bo switch (clip->media_type) { case MEDIA_TYPE_FOOTAGE: if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - if (write_A) { - cache_video_worker(clip, playhead, &clip->cache_A); - playhead += clip->cache_size; - } - - if (write_B) { - cache_video_worker(clip, playhead, &clip->cache_B); - } + cache_video_worker(clip); } else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { cache_audio_worker(clip, nest); } @@ -782,8 +738,7 @@ void close_clip_worker(Clip* clip) { clip->finished_opening = false; if (clip->media_type == MEDIA_TYPE_FOOTAGE) { - // closes ffmpeg file handle and frees any memory used for caching - MediaStream* ms = static_cast(clip->media)->get_stream_from_file_index(clip->track < 0, clip->media_stream); + clip->clear_queue(); avfilter_graph_free(&clip->filter_graph); @@ -791,12 +746,12 @@ void close_clip_worker(Clip* clip) { avcodec_free_context(&clip->codecCtx); avformat_close_input(&clip->formatCtx); - for (int i=0;icache_size;i++) { - av_frame_free(&clip->cache_A.frames[i]); - if (!ms->infinite_length && clip->track < 0) av_frame_free(&clip->cache_B.frames[i]); + if (clip->track >= 0) { + for (int i=0;icache_size;i++) { + av_frame_free(&clip->cache_A.frames[i]); + } + delete [] clip->cache_A.frames; } - delete [] clip->cache_A.frames; - if (!ms->infinite_length) delete [] clip->cache_B.frames; } av_frame_free(&clip->frame); @@ -820,7 +775,7 @@ void Cacher::run() { if (!caching) { break; } else { - cache_clip_worker(clip, playhead, write_A, write_B, reset, nest); + cache_clip_worker(clip, playhead, reset, nest); } } diff --git a/playback/cacher.h b/playback/cacher.h index 42ed0dfd7..4f02b5794 100644 --- a/playback/cacher.h +++ b/playback/cacher.h @@ -16,8 +16,6 @@ public: // must be set before caching long playhead; - bool write_A; - bool write_B; bool reset; Clip* nest; @@ -26,7 +24,7 @@ private: }; void open_clip_worker(Clip* clip); -void cache_clip_worker(Clip* clip, long playhead, bool write_A, bool write_B, bool reset, Clip *nest); +void cache_clip_worker(Clip* clip, long playhead, bool reset, Clip *nest); void close_clip_worker(Clip* clip); #endif // CACHER_H diff --git a/playback/playback.cpp b/playback/playback.cpp index 491a93149..f56fefc15 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -18,7 +18,7 @@ extern "C" { #include } -#include +#include #include #include #include @@ -37,7 +37,7 @@ void open_clip(Clip* clip, bool multithreaded) { // maybe keep cacher instance in memory while clip exists for performance? clip->cacher = new Cacher(clip); QObject::connect(clip->cacher, SIGNAL(finished()), clip->cacher, SLOT(deleteLater())); - clip->cacher->start((clip->track < 0) ? QThread::LowPriority : QThread::TimeCriticalPriority); + clip->cacher->start((clip->track < 0) ? QThread::NormalPriority : QThread::TimeCriticalPriority); } } else { clip->finished_opening = false; @@ -89,23 +89,116 @@ void close_clip(Clip* clip) { } } -void cache_clip(Clip* clip, long playhead, bool write_A, bool write_B, bool reset, Clip* nest) { +void cache_clip(Clip* clip, long playhead, bool reset, Clip* nest) { if (clip->media_type == MEDIA_TYPE_FOOTAGE || clip->media_type == MEDIA_TYPE_TONE) { if (clip->multithreaded) { clip->cacher->playhead = playhead; - clip->cacher->write_A = write_A; - clip->cacher->write_B = write_B; clip->cacher->reset = reset; clip->cacher->nest = nest; clip->can_cache.wakeAll(); } else { - cache_clip_worker(clip, playhead, write_A, write_B, reset, nest); + cache_clip_worker(clip, playhead, reset, nest); } } } -bool get_clip_frame(Clip* c, long playhead) { +void get_clip_frame(Clip* c, long playhead) { + if (c->finished_opening) { + MediaStream* ms = static_cast(c->media)->get_stream_from_file_index(c->track < 0, c->media_stream); + + int64_t target_pts = playhead_to_timestamp(c, playhead); + int64_t second_pts = qRound(av_q2d(av_inv_q(c->stream->time_base))); + if (ms->video_interlacing != VIDEO_PROGRESSIVE) { + target_pts *= 2; + second_pts *= 2; + } + AVFrame* target_frame = NULL; + +// qDebug() << "target pts:" << target_pts; + + bool reset = false; + + /*while (c->queue.size() > 0) { + if (c->queue.first()->pts == target_pts) { + target_frame = c->queue.first(); + + qDebug() << "GCF ==> USE PRECISE"; + break; + } else if (c->queue.size() > 1 && c->queue.at(1)->pts > target_pts) { + // use this frame + target_frame = c->queue.first(); + + qDebug() << "GCF ==> USE IMPRECISE"; + break; + } else { + + break; + } else { + // skip this frame + c->queue_mutex.lock(); // thread safety when removing from queue + av_frame_free(&c->queue.first()); // may be a little heavy for the UI thread? + c->queue.removeFirst(); + c->queue_mutex.unlock(); + qDebug() << "GCF ==> SKIP"; + } + }*/ + + if (c->queue.size() > 0) { + // correct frame may be somewhere else in the queue + int closest_frame = 0; + for (int i=1;iqueue.size();i++) { + if (c->queue.at(i)->pts == target_pts) { + qDebug() << "GCF ==> USE PRECISE"; + closest_frame = i; + break; + } else if (c->queue.at(i)->pts > c->queue.at(closest_frame)->pts && c->queue.at(i)->pts < target_pts) { + closest_frame = i; + } + } + + // remove all frames earlier than this one from the queue + target_frame = c->queue.at(closest_frame); + c->queue_lock.lock(); + for (int i=0;iqueue.size();i++) { + if (c->queue.at(i)->pts < target_frame->pts) { + av_frame_free(&c->queue[i]); // may be a little heavy for the UI thread? + c->queue.removeAt(i); + i--; + } + } + c->queue_lock.unlock(); + + // if this frame is more than one second out, we probably need to reset + if (qAbs(target_pts - target_frame->pts) > second_pts) { + target_frame = NULL; +// qDebug() << "GCF ==> COMPLACENT"; + qDebug() << "GCF ==> RESET"; + reset = true; + } else { + qDebug() << "GCF ==> USE IMPRECISE"; + } + } + + // get more frames + cache_clip(c, playhead, reset, NULL); + + if (target_frame == NULL || reset) { + // reset cache + texture_failed = true; + qDebug() << "[INFO] Cache couldn't keep up - either the user seeked or the system is overloaded"; + } + + if (target_frame != NULL) { + // add gate if this is the same frame + glPixelStorei(GL_UNPACK_ROW_LENGTH, target_frame->linesize[0]/4); + c->texture->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, target_frame->data[0]); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + } + } +} + +/*bool get_clip_frame(Clip* c, long playhead) { if (c->finished_opening) { // do we need to update the texture? MediaStream* ms = static_cast(c->media)->get_stream_from_file_index(c->track < 0, c->media_stream); @@ -225,30 +318,25 @@ bool get_clip_frame(Clip* c, long playhead) { } } return false; +}*/ + +long playhead_to_clip_frame(Clip* c, long playhead) { + return (qMax(0L, playhead - c->timeline_in) + c->clip_in); } -double playhead_to_seconds(Clip* c, long playhead) { +double playhead_to_clip_seconds(Clip* c, long playhead) { // returns time in seconds - if (c->reverse) { - return ((c->getMaximumLength() - (qMax(0L, playhead - c->timeline_in) + c->clip_in))/c->sequence->frame_rate)*c->speed; - } else { - return ((qMax(0L, playhead - c->timeline_in) + c->clip_in)/c->sequence->frame_rate)*c->speed; - } + long clip_frame = playhead_to_clip_frame(c, playhead); + if (c->reverse) clip_frame = c->getMaximumLength() - clip_frame; + return ((double) clip_frame/c->sequence->frame_rate)*c->speed; } -long seconds_to_clip_frame(Clip* c, double seconds) { - // returns time as frame number (according to clip's frame rate) - if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - return floor(seconds*c->getMediaFrameRate()); - } else { - qDebug() << "[ERROR] seconds_to_clip_frame only works on video streams"; - return 0; - } +int64_t seconds_to_timestamp(Clip* c, double seconds) { + return qFloor((seconds * c->stream->time_base.den)/c->stream->time_base.num); } -double clip_frame_to_seconds(Clip* c, long clip_frame) { - // returns frame number in decimal seconds - return (double) clip_frame / c->getMediaFrameRate(); +int64_t playhead_to_timestamp(Clip* c, long playhead) { + return seconds_to_timestamp(c, playhead_to_clip_seconds(c, playhead)); } int retrieve_next_frame(Clip* c, AVFrame* f) { @@ -256,6 +344,7 @@ int retrieve_next_frame(Clip* c, AVFrame* f) { int receive_ret; // do we need to retrieve a new packet for a new frame? + av_frame_unref(f); while ((receive_ret = avcodec_receive_frame(c->codecCtx, f)) == AVERROR(EAGAIN)) { int read_ret = 0; do { diff --git a/playback/playback.h b/playback/playback.h index 2b9b6ab3a..b7688c6b2 100644 --- a/playback/playback.h +++ b/playback/playback.h @@ -12,16 +12,19 @@ struct AVFrame; extern bool texture_failed; void open_clip(Clip* clip, bool multithreaded); -void cache_clip(Clip* clip, long playhead, bool write_A, bool write_B, bool reset, Clip *nest); +void cache_clip(Clip* clip, long playhead, bool reset, Clip *nest); void close_clip(Clip* clip); void cache_audio_worker(Clip* c, bool write_A); -void cache_video_worker(Clip* c, long playhead, ClipCache* cache); +void cache_video_worker(Clip* c); void handle_media(Sequence* sequence, long playhead, bool multithreaded); void reset_cache(Clip* c, long target_frame); -bool get_clip_frame(Clip* c, long playhead); -double playhead_to_seconds(Clip* c, long playhead); -long seconds_to_clip_frame(Clip* c, double seconds); -double clip_frame_to_seconds(Clip* c, long clip_frame); +void get_clip_frame(Clip* c, long playhead); + +long playhead_to_clip_frame(Clip* c, long playhead); +double playhead_to_clip_seconds(Clip* c, long playhead); +int64_t seconds_to_timestamp(Clip* c, double seconds); +int64_t playhead_to_timestamp(Clip* c, long playhead); + int retrieve_next_frame(Clip* c, AVFrame* f); bool is_clip_active(Clip* c, long playhead); void get_next_audio(Clip* c, bool mix); diff --git a/project/clip.cpp b/project/clip.cpp index ade8eea7d..c2825e691 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -36,7 +36,8 @@ Clip::Clip(Sequence* s) : fbo(NULL), autoscale(config.autoscale_by_default), maintain_audio_pitch(false), - reverse(false) + reverse(false), + use_existing_frame(false) { reset(); } @@ -75,17 +76,10 @@ Clip* Clip::copy(Sequence* s) { } void Clip::reset() { - cache_size = false; audio_just_reset = false; - cache_A.offset = false; - cache_B.offset = false; open = false; finished_opening = false; - pkt_written = false; - cache_A.written = false; - cache_B.written = false; - cache_A.unread = false; - cache_B.unread = false; + pkt_written = false; reached_end = false; audio_reset = false; frame_sample_index = -1; @@ -96,8 +90,6 @@ void Clip::reset() { codec = NULL; codecCtx = NULL; texture = NULL; - cache_A.frames = NULL; - cache_B.frames = NULL; } void Clip::reset_audio() { @@ -137,6 +129,13 @@ void Clip::refresh() { recalculateMaxLength(); } +void Clip::clear_queue() { + while (queue.size() > 0) { + av_frame_free(&queue.first()); + queue.removeFirst(); + } +} + Clip::~Clip() { if (open) { close_clip(this); diff --git a/project/clip.h b/project/clip.h index 7d009e356..4f978b1fd 100644 --- a/project/clip.h +++ b/project/clip.h @@ -50,6 +50,7 @@ struct Clip void reset_audio(); void reset(); void refresh(); + void clear_queue(); // timeline variables Sequence* sequence; @@ -95,21 +96,24 @@ struct Clip AVPacket* pkt; AVFrame* frame; - bool pkt_written; - bool reached_end; + bool reached_end; // deprecated + bool pkt_written; bool open; bool finished_opening; bool replaced; int64_t rev_target; - // caching functions + // caching functions + bool use_existing_frame; bool multithreaded; Cacher* cacher; + int cache_size; // deprecated QWaitCondition can_cache; - int cache_size; - ClipCache cache_A; - ClipCache cache_B; + int max_queue_size; + QVector queue; + ClipCache cache_A; // deprecated + QMutex queue_lock; QMutex lock; QMutex open_lock; @@ -130,11 +134,7 @@ struct Clip int audio_buffer_write; bool audio_reset; bool audio_just_reset; - long audio_target_frame; - - // statistics - int stat_seek_time; - int stat_read_time; + long audio_target_frame; }; #endif // CLIP_H diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 77242c0e8..da26aa071 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -255,6 +255,16 @@ GLuint ViewerWidget::compose_sequence(Clip* nest, bool render_audio) { int video_height = c->getHeight(); if (c->media_type == MEDIA_TYPE_FOOTAGE) { + // set up opengl texture + if (c->texture == NULL) { + c->texture = new QOpenGLTexture(QOpenGLTexture::Target2D); + c->texture->setSize(c->stream->codecpar->width, c->stream->codecpar->height); + c->texture->setFormat(QOpenGLTexture::RGBA8_UNorm); + c->texture->setMipLevels(c->texture->maximumMipLevels()); + c->texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); + c->texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); + } + get_clip_frame(c, playhead); if (c->texture != NULL) textureID = c->texture->textureId(); } else if (c->media_type == MEDIA_TYPE_SEQUENCE) { @@ -394,7 +404,7 @@ GLuint ViewerWidget::compose_sequence(Clip* nest, bool render_audio) { case MEDIA_TYPE_TONE: if (c->lock.tryLock()) { // clip is not caching, start caching audio - cache_clip(c, playhead, false, false, c->audio_reset, nest); + cache_clip(c, playhead, c->audio_reset, nest); c->lock.unlock(); } break;