From 86c1674e30b19750b55581743c3bdc9a2853dd13 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sat, 22 Jul 2023 12:35:27 +0200 Subject: [PATCH] Make Frame allocated on a freelist, in preparation for PBOs. --- video_widget.cpp | 81 ++++++++++++++++++++++++++++++++++-------------- video_widget.h | 22 ++++++++----- 2 files changed, 73 insertions(+), 30 deletions(-) diff --git a/video_widget.cpp b/video_widget.cpp index e6708a9..9c37cd7 100644 --- a/video_widget.cpp +++ b/video_widget.cpp @@ -194,10 +194,10 @@ bool VideoWidget::process_queued_commands(AVFormatContext *format_ctx, AVCodecCo queue.pop_front(); queued_frames = std::move(queue); } - Frame *new_frame = new Frame(make_video_frame(frame.get())); + shared_ptr new_frame = make_video_frame(frame.get()); { lock_guard lock(current_frame_mu); - current_frame.reset(new_frame); + current_frame = std::move(new_frame); } update(); store_pts(frame->pts); @@ -229,10 +229,10 @@ bool VideoWidget::process_queued_commands(AVFormatContext *format_ctx, AVCodecCo if (frame == nullptr || error) { return true; } - Frame *new_frame = new Frame(make_video_frame(frame.get())); + shared_ptr new_frame = make_video_frame(frame.get()); { lock_guard lock(current_frame_mu); - current_frame.reset(new_frame); + current_frame = std::move(new_frame); } update(); store_pts(frame->pts); @@ -938,10 +938,10 @@ bool VideoWidget::play_video(const string &pathname) bool finished_wakeup; finished_wakeup = producer_thread_should_quit.sleep_until(next_frame_start); if (finished_wakeup) { - Frame *new_frame = new Frame(make_video_frame(frame.get())); + shared_ptr new_frame = make_video_frame(frame.get()); { lock_guard lock(current_frame_mu); - current_frame.reset(new_frame); + current_frame = std::move(new_frame); } last_frame = steady_clock::now(); update(); @@ -956,10 +956,10 @@ bool VideoWidget::play_video(const string &pathname) if (paused) { // Just paused, so present the frame immediately and then go into deep sleep. - Frame *new_frame = new Frame(make_video_frame(frame.get())); + shared_ptr new_frame = make_video_frame(frame.get()); { lock_guard lock(current_frame_mu); - current_frame.reset(new_frame); + current_frame = std::move(new_frame); } last_frame = steady_clock::now(); update(); @@ -998,9 +998,46 @@ float compute_chroma_offset(float pos, unsigned subsampling_factor, unsigned res } } -VideoWidget::Frame VideoWidget::make_video_frame(const AVFrame *frame) +shared_ptr VideoWidget::alloc_frame(unsigned width, unsigned height, unsigned chroma_width, unsigned chroma_height) +{ + lock_guard lock(freelist_mu); + for (auto it = frame_freelist.begin(); it != frame_freelist.end(); ++it) { + if ((*it)->width == width && + (*it)->height == height && + (*it)->chroma_width == chroma_width && + (*it)->chroma_height == chroma_height) { + Frame *frame = *it; + frame_freelist.erase(it); + return shared_ptr{frame, free_frame}; + } + } + + Frame *frame = new Frame; + frame->owner = this; + frame->width = width; + frame->height = height; + frame->chroma_width = chroma_width; + frame->chroma_height = chroma_height; + + size_t len = frame->width * frame->height + 2 * frame->chroma_width * frame->chroma_height; + frame->data.reset(new uint8_t[len]); + + return shared_ptr{frame, free_frame}; +} + +void VideoWidget::free_frame(VideoWidget::Frame *frame) +{ + VideoWidget *self = frame->owner; + lock_guard lock(self->freelist_mu); + if (self->frame_freelist.size() >= 16) { + delete self->frame_freelist.front(); + self->frame_freelist.pop_front(); + } + self->frame_freelist.push_back(frame); +} + +shared_ptr VideoWidget::make_video_frame(const AVFrame *frame) { - Frame video_frame; AVFrameWithDeleter sw_frame; if (frame->format == AV_PIX_FMT_VAAPI || @@ -1041,26 +1078,24 @@ VideoWidget::Frame VideoWidget::make_video_frame(const AVFrame *frame) int linesizes[4] = { 0, 0, 0, 0 }; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(sws_dst_format); - video_frame.width = frame->width; - video_frame.height = frame->height; - video_frame.chroma_width = AV_CEIL_RSHIFT(int(frame->width), desc->log2_chroma_w); - video_frame.chroma_height = AV_CEIL_RSHIFT(int(frame->height), desc->log2_chroma_h); + shared_ptr video_frame = alloc_frame( + frame->width, + frame->height, + AV_CEIL_RSHIFT(int(frame->width), desc->log2_chroma_w), + AV_CEIL_RSHIFT(int(frame->height), desc->log2_chroma_h)); // We always assume left chroma placement for now. - cbcr_offset[0] = compute_chroma_offset(0.0f, 1 << desc->log2_chroma_w, video_frame.chroma_width); - cbcr_offset[1] = compute_chroma_offset(0.5f, 1 << desc->log2_chroma_h, video_frame.chroma_height); - - size_t len = frame->width * frame->height + 2 * video_frame.chroma_width * video_frame.chroma_height; - video_frame.data.reset(new uint8_t[len]); + cbcr_offset[0] = compute_chroma_offset(0.0f, 1 << desc->log2_chroma_w, video_frame->chroma_width); + cbcr_offset[1] = compute_chroma_offset(0.5f, 1 << desc->log2_chroma_h, video_frame->chroma_height); - pic_data[0] = video_frame.data.get(); + pic_data[0] = video_frame->data.get(); linesizes[0] = frame->width; pic_data[1] = pic_data[0] + frame->width * frame->height; - linesizes[1] = video_frame.chroma_width; + linesizes[1] = video_frame->chroma_width; - pic_data[2] = pic_data[1] + video_frame.chroma_width * video_frame.chroma_height; - linesizes[2] = video_frame.chroma_width; + pic_data[2] = pic_data[1] + video_frame->chroma_width * video_frame->chroma_height; + linesizes[2] = video_frame->chroma_width; sws_scale(sws_ctx.get(), frame->data, frame->linesize, 0, frame->height, pic_data, linesizes); diff --git a/video_widget.h b/video_widget.h index c0f28d7..24aaea3 100644 --- a/video_widget.h +++ b/video_widget.h @@ -44,20 +44,28 @@ public: void mouseReleaseEvent(QMouseEvent *e); void mouseMoveEvent(QMouseEvent *e); + // Should really have a PBO, but this is OK for now. + // public due to shared_ptr. + struct Frame { + unsigned width, height; + unsigned chroma_width, chroma_height; + std::unique_ptr data; // Y, followed by Cb, followed by Cr. + VideoWidget *owner; // For the freelist. + }; + signals: void position_changed(uint64_t pos); void mouse_back_clicked(); void mouse_forward_clicked(); private: - // Should really have been persistent and a PBO, but this is OK for now. - struct Frame { - unsigned width, height; - unsigned chroma_width, chroma_height; - std::unique_ptr data; // Y, followed by Cb, followed by Cr. - }; + std::shared_ptr alloc_frame(unsigned width, unsigned height, unsigned chroma_width, unsigned chroma_height); + static void free_frame(Frame *frame); + std::mutex current_frame_mu; std::shared_ptr current_frame; // Protected by current_frame_mu. + std::mutex freelist_mu; + std::deque frame_freelist; // Protected by freelist_mu. std::deque queued_frames; // Frames decoded but not displayed. Only used when frame-stepping backwards. GLuint ycbcr_vertex_shader, ycbcr_fragment_shader, ycbcr_program; @@ -110,7 +118,7 @@ private: AVFrameWithDeleter decode_frame(AVFormatContext *format_ctx, AVCodecContext *video_codec_ctx, const std::string &pathname, int video_stream_index, bool *error); - Frame make_video_frame(const AVFrame *frame); + std::shared_ptr make_video_frame(const AVFrame *frame); bool process_queued_commands(AVFormatContext *format_ctx, AVCodecContext *video_codec_ctx, int video_stream_index, bool *seeked); void store_pts(int64_t pts); -- 2.39.2