]> git.sesse.net Git - pkanalytics/commitdiff
Make Frame allocated on a freelist, in preparation for PBOs.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 22 Jul 2023 10:35:27 +0000 (12:35 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 22 Jul 2023 17:44:46 +0000 (19:44 +0200)
video_widget.cpp
video_widget.h

index e6708a9257cb8f4c5834fae55d720b94b1d826ae..9c37cd79bb770dbd631d8eb51fe5984ebe142a84 100644 (file)
@@ -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<Frame> 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<Frame> 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<Frame> 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<Frame> 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::Frame> 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>{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>{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::Frame> 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<Frame> 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);
 
index c0f28d75ec7ad8ff99e14e245e49e51054385a93..24aaea3b6a8129e179ff6c7c23343b7dc4872874 100644 (file)
@@ -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<uint8_t[]> 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<uint8_t[]> data;  // Y, followed by Cb, followed by Cr.
-       };
+       std::shared_ptr<Frame> 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<Frame> current_frame;  // Protected by current_frame_mu.
+       std::mutex freelist_mu;
+       std::deque<Frame *> frame_freelist;  // Protected by freelist_mu.
        std::deque<AVFrameWithDeleter> 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<Frame> 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);