X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=video_widget.h;h=4361d3098c8f3fd2d6414d69ebd868f32a3f2ed6;hb=fb839f83f5113f02fb9ef3485f27f5b220919a4c;hp=dc7d0ab7b3f834285b1da2feabd3a2e06417a927;hpb=b6eaf3bbd5005eda6d9853897b2b5667c5fd63a7;p=pkanalytics diff --git a/video_widget.h b/video_widget.h index dc7d0ab..4361d30 100644 --- a/video_widget.h +++ b/video_widget.h @@ -1,7 +1,8 @@ #ifndef _VIDEO_WIDGET_H #define _VIDEO_WIDGET_H 1 -#include +#include +#include #include #include #include @@ -18,14 +19,16 @@ extern "C" { // 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(); } - void open(const std::string &filename); + bool open(const std::string &filename); // False on error. void play(); uint64_t get_position() const { return last_position; } // In milliseconds. void pause(); @@ -34,9 +37,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 +44,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 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; - 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, @@ -77,7 +78,8 @@ private: int64_t pts_origin; int64_t last_pts; std::atomic last_position{0}; // TODO: sort of redundant wrt. last_pts (but this one can be read from the other thread) - std::atomic running{false}; + enum RunState { NOT_RUNNING, STARTING, RUNNING, VIDEO_FILE_ERROR }; // NOT_RUNNING and VIDEO_FILE_ERROR both imply that the thread isn't running and can freely be restarted. + std::atomic running{NOT_RUNNING}; bool paused = false; std::chrono::steady_clock::time_point start, next_frame_start, last_frame; @@ -103,17 +105,69 @@ 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 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 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: + 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 current_frame; // Protected by current_frame_mu. +}; + #endif // !defined(_VIDEO_WIDGET_H)