--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>600</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>MainWindow</string>
+ </property>
+ <widget class="QWidget" name="centralwidget">
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <layout class="QGridLayout" name="gridLayout" rowstretch="1,0,0">
+ <item row="1" column="0">
+ <widget class="QLabel" name="timestamp">
+ <property name="text">
+ <string>1:02:03</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QVideoWidget" name="video" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>320</width>
+ <height>240</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QTableView" name="tableView"/>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>23</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>QVideoWidget</class>
+ <extends>QWidget</extends>
+ <header location="global">QVideoWidget</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null
+project('stats', 'cpp', default_options: ['buildtype=debugoptimized'], version: '0.0.1')
+
+qt6 = import('qt6')
+qt6deps = dependency('qt6', modules: ['Core', 'Gui', 'Widgets', 'Multimedia', 'MultimediaWidgets'])
+qt_files = qt6.preprocess(
+ moc_headers: ['mainwindow.h'],
+ ui_files: ['mainwindow.ui'],
+ dependencies: qt6deps)
+
+executable('stats', 'stats.cpp', qt_files, dependencies: [qt6deps])
--- /dev/null
+#include <QMediaPlayer>
+#include <QMainWindow>
+#include <QApplication>
+#include <QGridLayout>
+#include <QVideoWidget>
+#include <QShortcut>
+#include <iostream>
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+
+MainWindow::MainWindow()
+{
+ player = new QMediaPlayer;
+ //player->setSource(QUrl::fromLocalFile("/home/sesse/dev/stats/ultimate.mkv"));
+ player->setSource(QUrl::fromLocalFile("/home/sesse/dev/stats/ultimate-prores.mkv"));
+ player->play();
+
+ Ui::MainWindow *ui = new Ui::MainWindow;
+ ui->setupUi(this);
+
+ connect(player, &QMediaPlayer::positionChanged, [ui, this](uint64_t pos) {
+ int ms = pos % 1000;
+ pos /= 1000;
+ int sec = pos % 60;
+ pos /= 60;
+ int min = pos % 60;
+ int hour = pos / 60;
+
+ char buf[256];
+ snprintf(buf, sizeof(buf), "%d:%02d:%02d.%03d", hour, min, sec, ms);
+ ui->timestamp->setText(buf);
+ if (buffered_seek) {
+ player->setPosition(*buffered_seek);
+ buffered_seek.reset();
+ }
+ if (!playing) {
+ player->pause(); // We only played to get a picture.
+ }
+ });
+
+ player->setVideoOutput(ui->video);
+
+ QShortcut *key_k = new QShortcut(QKeySequence(Qt::Key_K), this);
+ connect(key_k, &QShortcut::activated, [this]() { seek(-10000); });
+
+ QShortcut *key_l = new QShortcut(QKeySequence(Qt::Key_L), this);
+ connect(key_l, &QShortcut::activated, [this]() { seek(10000); });
+
+ QShortcut *key_left = new QShortcut(QKeySequence(Qt::Key_Left), this);
+ connect(key_left, &QShortcut::activated, [this]() { seek(-1000); });
+
+ QShortcut *key_right = new QShortcut(QKeySequence(Qt::Key_Right), this);
+ connect(key_right, &QShortcut::activated, [this]() { seek(1000); });
+
+ // TODO: Would be nice to actually have a frame...
+ QShortcut *key_comma = new QShortcut(QKeySequence(Qt::Key_Comma), this);
+ connect(key_comma, &QShortcut::activated, [this]() { seek(-20); });
+
+ QShortcut *key_period = new QShortcut(QKeySequence(Qt::Key_Period), this);
+ connect(key_period, &QShortcut::activated, [this]() { seek(20); });
+
+ QShortcut *key_space = new QShortcut(QKeySequence(Qt::Key_Space), this);
+ connect(key_space, &QShortcut::activated, [this]() {
+ if (playing) {
+ player->pause();
+ } else {
+ player->setPlaybackRate(1.0);
+ player->play();
+ }
+ playing = !playing;
+ });
+}
+
+void MainWindow::seek(int64_t delta_ms)
+{
+ int64_t current_pos = buffered_seek ? *buffered_seek : player->position();
+ uint64_t pos = std::max<int64_t>(current_pos + delta_ms, 0);
+ buffered_seek = pos;
+ if (!playing) {
+ player->setPlaybackRate(0.01);
+ player->play(); // Or Qt won't show the seek.
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ MainWindow mainWindow;
+ mainWindow.resize(QSize(1280, 720));
+ mainWindow.show();
+
+ return app.exec();
+
+}