#ifndef _VIDEO_WIDGET_H
#define _VIDEO_WIDGET_H 1
-#include <QOpenGLWidget>
+#include <QWidget>
+#include <QOpenGLWindow>
#include <string>
#include <atomic>
#include <thread>
// 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();
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.
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,
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:
+ 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)