]> git.sesse.net Git - pkanalytics/blobdiff - video_widget.h
Support filtering passes by thrower and receiver.
[pkanalytics] / video_widget.h
index c0f28d75ec7ad8ff99e14e245e49e51054385a93..8d823d8ed69e118fdf47e7ecbe1346b0dc321b5b 100644 (file)
@@ -1,7 +1,8 @@
 #ifndef _VIDEO_WIDGET_H
 #define _VIDEO_WIDGET_H 1
 
-#include <QOpenGLWidget>
+#include <QWidget>
+#include <QOpenGLWindow>
 #include <string>
 #include <atomic>
 #include <thread>
@@ -16,14 +17,18 @@ extern "C" {
 #include "ffmpeg_raii.h"
 #include "quittable_sleeper.h"
 
+class QOpenGLFunctions_4_5_Compatibility;
+
 // Because QVideoWidget sucks, sadly. (Don't use GStreamer, kids.)
 
-class VideoWidget : public QOpenGLWidget {
+class VideoWindow;
+
+class VideoWidget : public QWidget {
        Q_OBJECT
 
 public:
        VideoWidget(QWidget *parent);
-       ~VideoWidget() { stop(); }
+       ~VideoWidget();
 
        bool open(const std::string &filename);  // False on error.
        void play();
@@ -34,9 +39,6 @@ public:
        void seek_frames(int64_t relative_frames);  // Relative seek.
        void seek_absolute(int64_t position_ms);  // Absolute seek.
 
-       void initializeGL() override;
-       void resizeGL(int w, int h) override;
-       void paintGL() override;
        void wheelEvent(QWheelEvent *event) override;
 
        // For dragging, and for back/forward button presses.
@@ -44,30 +46,31 @@ public:
        void mouseReleaseEvent(QMouseEvent *e);
        void mouseMoveEvent(QMouseEvent *e);
 
+       // Public due to shared_ptr.
+       struct Frame {
+               unsigned width, height;
+               unsigned chroma_width, chroma_height;
+               VideoWidget *owner;  // For the freelist.
+               GLuint pbo;
+               uint8_t *data;  // Persistently mapped into the PBO. Y, followed by Cb, followed by Cr.
+               size_t need_flush_len;  // 0 = no flush needed.
+       };
+
 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;
-       GLuint bilinear_sampler;
-
-       GLuint tex[3];
-       GLuint last_width = 0, last_height = 0;
-       GLuint last_chroma_width = 0, last_chroma_height = 0;
-       GLfloat cbcr_offset[2];
-       double display_aspect = 1.0;
        double zoom_matrix[9] = {  // Column-major, to match with OpenGL.
                1.0, 0.0, 0.0,
                0.0, 1.0, 0.0,
@@ -104,17 +107,70 @@ private:
        bool dragging = false;
        float last_drag_x, last_drag_y;
 
+       VideoWindow *video_window;
+
        void producer_thread_func();
        bool play_video(const std::string &pathname);
        void internal_rewind();
        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);
 
        void fixup_zoom_matrix();
 };
 
+class VideoWindow : public QOpenGLWindow {  // Private implementation of VideoWidget, but moc does not support nested classes.
+       Q_OBJECT;
+
+public:
+       VideoWindow(VideoWidget *video) : video(video) {}
+       void initializeGL() override;
+       void resizeGL(int w, int h) override;
+       void paintGL() override;
+       void set_current_frame(std::shared_ptr<VideoWidget::Frame> new_frame);  // Takes ownership. Thread-safe.
+       void set_cbcr_offset(float x, float y)  // FIXME: Thread-safe?
+       {
+               cbcr_offset[0] = x;
+               cbcr_offset[1] = y;
+       }
+       void set_zoom_matrix(double new_zoom_matrix[9]) {
+               memcpy(zoom_matrix, new_zoom_matrix, sizeof(zoom_matrix));
+               update();
+       }
+
+       void wheelEvent(QWheelEvent *e) override { emit mouse_wheel(e); }
+       void mousePressEvent(QMouseEvent *e) override { emit mouse_pressed(e); }
+       void mouseReleaseEvent(QMouseEvent *e) override { emit mouse_released(e); }
+       void mouseMoveEvent(QMouseEvent *e) override { emit mouse_moved(e); }
+
+signals:
+       void mouse_wheel(QWheelEvent *e);
+       void mouse_pressed(QMouseEvent *e);
+       void mouse_released(QMouseEvent *e);
+       void mouse_moved(QMouseEvent *e);
+
+private:
+       QOpenGLFunctions_4_5_Compatibility *gl = nullptr;
+       VideoWidget *video;
+       GLuint ycbcr_vertex_shader, ycbcr_fragment_shader, ycbcr_program;
+       GLuint bilinear_sampler;
+       GLuint tex[3];
+       GLuint last_width = 0, last_height = 0;
+       GLuint last_chroma_width = 0, last_chroma_height = 0;
+       double display_aspect = 1.0;
+       GLfloat cbcr_offset[2];
+
+       double zoom_matrix[9] = {  // Column-major, to match with OpenGL.
+               1.0, 0.0, 0.0,
+               0.0, 1.0, 0.0,
+               0.0, 0.0, 1.0,
+       };
+
+       std::mutex current_frame_mu;
+       std::shared_ptr<VideoWidget::Frame> current_frame;  // Protected by current_frame_mu.
+};
+
 #endif  // !defined(_VIDEO_WIDGET_H)