Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
a3e6fc1
Fix Bitrate Plot popup text white on white blending issue
th1nk3r-ing May 30, 2026
629ee4d
Extend FFmpeg struct version support to cover FFmpeg 6.x/7.x
th1nk3r-ing May 30, 2026
811363a
Add stream selection dropdown to Bitrate Plot tab
th1nk3r-ing May 30, 2026
9626cb6
Move Type field from Average to Bar info box in Bitrate Plot
th1nk3r-ing May 30, 2026
5ec6a64
Fix empty frame types in AVC/AV1/MPEG2 parsers: add missing 'meaning'…
th1nk3r-ing May 30, 2026
ff88099
Fix Y-axis range not updating when switching streams in Bitrate Plot
th1nk3r-ing May 30, 2026
7321431
Fix drag-and-drop regression in playlist
th1nk3r-ing May 30, 2026
7968a61
docs: 扩展 ff_libs.md 添加解码器架构与 Packet Analysis 解析说明
th1nk3r-ing May 30, 2026
61547b4
Fix: Use distinct blue-grey color for raw bitstream in Packet Analysis
th1nk3r-ing May 30, 2026
754fe5e
Fix: VVC Bitrate Plot Type field was always empty
th1nk3r-ing May 30, 2026
a94a7c9
Fix: add mutex to FFmpeg log callback to prevent race condition crash
th1nk3r-ing May 30, 2026
d490e52
fix: remove Up/Down global shortcuts to restore tree view keyboard na…
th1nk3r-ing May 31, 2026
55b7e3d
fix: reduce tree view Name column width from 600px to 400px
th1nk3r-ing May 31, 2026
31b59fd
feat: add hex view to bitstream analysis with crash fix
th1nk3r-ing May 31, 2026
e15a648
feat: add HexView with TreeView keyboard navigation improvements
th1nk3r-ing May 31, 2026
c42dbec
fix: hex view highlight accuracy and code cleanup
th1nk3r-ing May 31, 2026
4b48231
fix: clear HexView data when deleting playlist items
th1nk3r-ing Jun 1, 2026
5297b28
feat(): Replace hardcoded colors with theme-aware palette colors acro…
th1nk3r-ing Jun 1, 2026
020273f
chore(): add windows build.bat
th1nk3r-ing Jun 1, 2026
8c6e100
chroe(): remove test form build.bat
th1nk3r-ing Jun 2, 2026
90d90a4
fix: restore Libde265 statistics overlay controls (Slice Index, Opaci…
th1nk3r-ing Jun 2, 2026
e386bf5
feat(): add log support
th1nk3r-ing Jun 1, 2026
7bf405e
Fix signal-handler safety, use-after-free comment, isDarkTheme cache,…
th1nk3r-ing Jun 30, 2026
654c5db
Add FFmpeg 8.x (avcodec/avformat 62) support
th1nk3r-ing Jun 30, 2026
1bbdea4
Fix illegible text on selected packet row in BitstreamAnalysisWidget
th1nk3r-ing Jun 30, 2026
60aa19d
Fix dark-mode hover highlight and plain-row text color in Packet Anal…
th1nk3r-ing Jun 30, 2026
33e59fb
HexViewWidget: split rebuildDisplay into rebuildText/applyHighlight
th1nk3r-ing Jun 30, 2026
d2765d8
Stream Info: differentiate video/audio/subtitle fields and labels
th1nk3r-ing Jun 30, 2026
a8aeb52
Fix missing last AU in Bitrate Plot for Annex-B parsers
th1nk3r-ing Jul 1, 2026
54bc25f
Fix invisible tree expand arrows on dark themes and elided combobox e…
th1nk3r-ing Jul 1, 2026
fc4ee16
Fix build:
th1nk3r-ing Jul 1, 2026
be79cbd
Fix crash on non-ASCII file paths on Windows
th1nk3r-ing Jul 11, 2026
ecbcca5
Add RGBA 8-bit packed (alpha last) preset to RGB format list
th1nk3r-ing Jul 11, 2026
6fda2c6
Add build.sh - cross-platform build script for macOS and Linux
th1nk3r-ing Jul 13, 2026
167d877
Fix typo in macOS qmake: BIINDIR -> BINDIR
th1nk3r-ing Jul 13, 2026
0ae1409
Fix MSE::operator== comparing alpha channel to itself
th1nk3r-ing Jul 13, 2026
7de3824
Update YUV-RGB conversion coefficients slightly
th1nk3r-ing Jul 13, 2026
c37e176
Fix potential crash when guessing .raw format without frameSize
th1nk3r-ing Jul 13, 2026
e7aa303
Fix Y4M color space parsing and stride calculation
th1nk3r-ing Jul 13, 2026
99c099f
Handle std::bad_alloc and invalid formats on RAW file loading
th1nk3r-ing Jul 13, 2026
be3b890
Overhaul logging: runtime-controllable categories, thread ID, cerr cl…
th1nk3r-ing Jul 13, 2026
91ef09d
Add LogPanel level filter, file-write master switch, and UI fixes
th1nk3r-ing Jul 13, 2026
20d400b
Fix FFmpeg log routing, persist LogPanel settings, reduce log noise
th1nk3r-ing Jul 13, 2026
0ad544b
Fix negative PTS/DTS frames invisible in Bitrate Plot
th1nk3r-ing Jul 13, 2026
0b3e7f0
Color-code Bitrate Plot bars by frame type (I/P/B)
th1nk3r-ing Jul 13, 2026
830d002
fix: workaround QApplication crash on macOS Qt6
th1nk3r-ing Jul 13, 2026
fa63603
Fix heap-use-after-free in BitratePlotModel const methods on Qt6
th1nk3r-ing Jul 13, 2026
988ba4e
Refactor FFmpeg decoder setup: folder selection instead of 4 individu…
th1nk3r-ing Jul 13, 2026
597417b
Remove duplicate toUnsigned/toInt declarations in Functions.h
th1nk3r-ing Jul 13, 2026
8b01d46
Show QP statistics (min/max/avg) in Bitrate Plot hover info box
th1nk3r-ing Jul 13, 2026
93e864e
Cache raw YUV bytes instead of RGB QImage for YUV video handler
th1nk3r-ing Jul 13, 2026
3bd11b3
Fix Unix CI build: disambiguate QDebug << std::string_view in AVC parser
th1nk3r-ing Jul 14, 2026
2fb7bab
Fix rawDataCacheValid data race and remove dead loadFrameForCaching code
th1nk3r-ing Jul 14, 2026
a8c2469
Change default statistics grid color from black to white
th1nk3r-ing Jul 15, 2026
0a52112
Fix use-after-free when switching decoder engine while caching/loading
th1nk3r-ing Jul 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ YUView.pro.user
.vscode
issues
.DS_Store
build-asan/
114 changes: 114 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# AGENTS.md

Guidance for AI coding agents working on the YUView codebase.

## 构建项目

根目录两个脚本封装环境初始化、qmake 配置和并行编译:

### Windows(MSVC + Qt6 + jom)

```bat
build.bat
```

自动 `vcvars64.bat` → `build/` → `qmake ..` → `jom`。WSL 下用 `cmd.exe /c build.bat`。默认**不启用**测试;如需:
```bat
mkdir build && cd build
qmake .. CONFIG+=UNITTESTS
jom
```

### Linux / macOS

```bash
./build.sh # 默认启用单元测试
./build.sh --asan # ASan 构建,禁用测试,产物到 build-asan/
```

自动探测 `qmake6`/`qmake`,按 `nproc`/`sysctl` 并行;macOS 同此脚本。

### 启用单元测试

须加 `CONFIG+=UNITTESTS`,否则 `YUViewUnitTest` 和 googletest 不进 SUBDIRS。Linux/macOS 由 `build.sh` 默认加;Windows 需手动。

Windows 相关 target(见根 Makefile):`sub-submodules-googletest-qmake`(gtest.lib)、`sub-YUViewUnitTest`(测试 exe)。`jom sub-YUViewUnitTest` 不自动构建 gtest,需先 `jom sub-submodules-googletest-qmake` 或全量 `jom`。

## 运行测试

```bash
./build/YUViewUnitTest/YUViewUnitTest # Linux
QT_QPA_PLATFORM=offscreen ./build/YUViewUnitTest/YUViewUnitTest # CI/无显示
build\YUViewUnitTest\YUViewUnitTest.exe # Windows
build\YUViewUnitTest\YUViewUnitTest.exe --gtest_filter=StatisticsFileCSV.* # 单套件
```

套件名用 `--gtest_list_tests` 查看。`StatisticsFileCSVTest.cpp` 与 `StatisticsFileVTMBMSTest.cpp` 都定义了 `TEST(StatisticsFileCSV, testCSVFileParsing)`,同名注册为同套件两个用例。

## 子模块

```bash
git clone --recurse-submodules <repo-url>
# 或
git submodule update --init --recursive
```

`submodules/googletest` 是测试依赖。SSH 克隆失败可临时用 HTTPS(用 `git -c url.<...>.insteadOf=<...> submodule update --init` 或直接 clone 后 checkout 到记录的 commit)。

## 项目结构

- **YUViewLib/** — 核心业务逻辑(静态库):`parser/`(HEVC/AVC/VVC/AV1/MPEG2)、`decoder/`(FFmpeg/Dav1d/libde265/HM/VTM)、`video/`(RGB/YUV)、`playlistitem/`、`ui/`、`statistics/`
- **YUViewApp/** — Qt 应用入口
- **YUViewUnitTest/** — Google Test 单元测试
- **submodules/** — googletest 与 googletest-qmake
- **deployment/** — Windows NSIS 安装包脚本

## 架构模式

1. **解码器插件**:`decoderBase` 抽象接口,`QLibrary` 动态加载 dll;`decoderFFmpeg`/`Dav1d`/`Libde265`/`HM`/`VTM` 为实现
2. **播放列表项**:`playlistItem` 基类,子类对应不同媒体类型
3. **视频处理器**:`videoHandler` 及子类(`Difference`、`Resample`)
4. **解析器**:按编码器在 `parser/` 下组织
5. **统计绘制**:`stats::paintStatisticsData` 统一绘制,样式由 `StatisticsType::gridStyle`/`vectorStyle`(`LineDrawStyle`)控制

## 解码器库加载约定

- 运行时 `QLibrary` 动态加载,逻辑见 `decoderBase.cpp:80`(`loadDecoderLibrary`),候选名由 `getLibraryNames()` 返回按序尝试
- **internals 支持由符号解析(`QLibrary::resolve`)决定,不是文件名**:libde265 的 `["libde265-internals", "libde265"]` 仅为加载优先级,真正判据是能否 resolve 出 `de265_internals_*`(见 `decoderLibde265.cpp:166`)。`libde265-internals.dll` 导出 internals 接口提供编码结构统计(CTU/CU/PB/IntraDir/TU),官方 `libde265.dll` 只能解码。VTM/HM 同理靠 `internalsSupported` 标志

## 代码规范

- C++20,`.clang-format`(2 空格、Allman 风格),UTF8/LF
- 成员变量 CamelCase,**无前缀**(不要 `m_`/`i`/`p`)
- 参数:基本类型按值,复杂类型按 const 引用;const 正确性
- **核心代码优先 Qt-free**:业务逻辑用 `std` 而非 Qt 类型,Qt 仅用于 GUI
- 详细规范见 `HACKING.md`

### Include 顺序(`.cpp`)

1. `"foo.h"`(对应头文件,必须第一个,后空一行)
2. `<cfoo>` 标准库(C 头用 `<cmath>` 而非 `<math.h>`)
3. 系统头 → 4. 其它库头 → 5. Qt 头 → 6. YUView 本地头

`.h` 只 include 声明所必需的,按值持有必须 include,按指针/引用持有可前向声明。

## 提交约定

- 分支 `develop`;英文祈使句,首行简短,正文解释 why
- 示例:`Fix rawDataCacheValid data race and remove dead loadFrameForCaching code`
- **不要主动 commit**,除非用户明确要求

## 常见陷阱

- **改全局 git config**:临时 url 重写用 `git -c`,不要 `--global`
- **cmd.exe 下的 gtest_filter**:冒号 `:` 会被误解析,分多次运行或用单个 filter
- **`jom sub-YUViewUnitTest` 不构建 gtest**:需先 `sub-submodules-googletest-qmake` 或全量 `jom`
- **测试同名冲突**:两文件都定义 `TEST(StatisticsFileCSV, testCSVFileParsing)`,修改时两个都要同步
- **`StatisticsType` 默认值变更**:构造函数改默认值后 `setInitialState()` 捕获的 init 状态同步变化,相关单测断言可能需同步更新

## 依赖

- Qt6(qt6-base-dev / Qt 6.9.0 Windows 预编译包)
- FFmpeg(视频解码)
- libde265(HEVC 解码,可选;internals 变体提供统计)
- Google Test(git 子模块)
4 changes: 3 additions & 1 deletion YUViewApp/YUViewApp.pro
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ macx {
isEmpty(PREFIX) {
PREFIX = /
}
isEmpty(BIINDIR) {
isEmpty(BINDIR) {
BINDIR = Applications
}

Expand All @@ -78,6 +78,8 @@ win32 {
RC_FILE += images/WindowsAppIcon.rc
SVNN = $$system("git describe --tags")
DEFINES += NOMINMAX
# dbghelp: required for MiniDumpWriteDump and StackWalk64 in CrashHandlerWindows
LIBS += -ldbghelp
}

LASTHASH = $$system("git rev-parse HEAD")
Expand Down
36 changes: 33 additions & 3 deletions YUViewApp/src/yuviewapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,13 @@
*/

#include <QCoreApplication>
#include <QMessageBox>

#include <cstdlib>
#include <iostream>

#include <common/Typedef.h>
#include <logging/Macros.h>
#include <ui/YUViewApplication.h>

int main(int argc, char *argv[])
Expand All @@ -45,8 +50,33 @@ int main(int argc, char *argv[])
QCoreApplication::setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents,false);

qRegisterMetaType<recacheIndicator>("recacheIndicator");

YUViewApplication app(argc, argv);

return app.returnCode;
try
{
YUViewApplication app(argc, argv);
#if defined(Q_OS_MAC) && QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
LOG_DEBUG(logApp) << "main: app constructed, about to exit";
// Workaround for a Qt regression on macOS >=6.10: ~QApplication() crashes with
// a null pointer dereference inside the Cocoa platform plugin during teardown.
// All application-layer cleanup (Logger, MainWindow, threads) has already
// completed in the YUViewApplication constructor body above.
fflush(stderr);
std::_Exit(app.returnCode);
#else
return app.returnCode;
#endif
}
catch (const std::bad_alloc &)
{
try
{
if (QCoreApplication::instance())
QMessageBox::critical(nullptr, "YUView", "Out of memory.");
}
catch (...)
{
qWarning() << "YUView: Out of memory.";
}
return 1;
}
}
37 changes: 26 additions & 11 deletions YUViewLib/images/YUViewSimple.qss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ The colors we use here:
background: #backgroundColor
text and borders: #activeColor (active) #inactiveColor (inavtive)
active indication color: #highlightColor (used for hovering, active indication)
border color: #borderColor (used for widget borders)
*/
QToolTip
{
Expand All @@ -19,15 +20,29 @@ QWidget
/* The default values for all widgets */
color: #activeColor;
background-color: #backgroundColor;
/*selection-background-color:#DBA800;*/
/*selection-color: #BC9000; */
selection-background-color: #highlightColor;
selection-color: white;
}

QWidget:disabled
{
color: #inactiveColor;
}

QTreeView::item:selected,
QTreeWidget::item:selected
{
background-color: #highlightColor;
color: white;
}

QTreeView::item:selected:!active,
QTreeWidget::item:selected:!active
{
background-color: #inactiveColor;
color: white;
}

QCheckBox,
QRadioButton
{
Expand Down Expand Up @@ -76,7 +91,7 @@ QRadioButton::indicator:checked:disabled
QPushButton
{
border-width: 1px;
border-color: #C6C6C6;
border-color: #borderColor;
border-style: solid;
padding: 2px;
padding-left: 8px;
Expand All @@ -90,7 +105,7 @@ QTreeWidget,
QTextEdit
{
border-width: 1px;
border-color: #C6C6C6;
border-color: #borderColor;
border-style: solid;
padding: 2px;
}
Expand Down Expand Up @@ -148,7 +163,7 @@ QSpinBox
QProgressBar
{
border-width: 1px;
border-color: #C6C6C6;
border-color: #borderColor;
border-style: solid;
}
QProgressBar::chunk
Expand All @@ -160,7 +175,7 @@ QProgressBar::chunk
QDockWidget
{
background: #highlightColor;
color: black;
color: #activeColor;
border: 1px solid orange;
}

Expand Down Expand Up @@ -210,12 +225,12 @@ QMenuBar::item:selected
QMenuBar::item:pressed
{
background-color: #highlightColor;
color: black;
color: #activeColor;
}
/* The menu if opened */
QMenu
{
border: 1px solid #C6C6C6;
border: 1px solid #borderColor;
color: #activeColor;
}
QMenu::item:selected
Expand All @@ -233,13 +248,13 @@ QHeaderView::section
QFrame[frameShape="4"],
QFrame[frameShape="5"]
{
border: 1px solid #C6C6C6;
border: 1px solid #borderColor;
background-color: #backgroundColor;
}

/* The slider */
QSlider::handle:horizontal {
border: 1px solid #C6C6C6;
border: 1px solid #borderColor;
background-color: #backgroundColor;
width: 0.5em;
margin: -0.4em 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */
Expand All @@ -254,7 +269,7 @@ QSlider::handle:horizontal:hover
}
QSlider::groove:horizontal
{
background-color: #C6C6C6;
background-color: #borderColor;
height: 0.4em; /* the groove expands to the size of the slider by default. by giving it a height, it has a fixed size */
}
QSlider::groove:horizontal:disabled
Expand Down
16 changes: 13 additions & 3 deletions YUViewLib/src/common/Functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,14 @@ QStringList getThemeNameList()
ret.append("Default");
ret.append("Simple Dark/Blue");
ret.append("Simple Dark/Orange");
ret.append("Simple Light/Blue");
return ret;
}

QString getThemeFileName(QString themeName)
{
if (themeName == "Simple Dark/Blue" || themeName == "Simple Dark/Orange")
if (themeName == "Simple Dark/Blue" || themeName == "Simple Dark/Orange"
|| themeName == "Simple Light/Blue")
return ":YUViewSimple.qss";
return "";
}
Expand All @@ -113,12 +115,20 @@ QStringList getThemeColors(QString themeName)
return QStringList() << "#262626"
<< "#E0E0E0"
<< "#808080"
<< "#3daee9";
<< "#3daee9"
<< "#C6C6C6";
if (themeName == "Simple Dark/Orange")
return QStringList() << "#262626"
<< "#E0E0E0"
<< "#808080"
<< "#FFC300 ";
<< "#FFC300"
<< "#C6C6C6";
if (themeName == "Simple Light/Blue")
return QStringList() << "#F5F5F5"
<< "#202020"
<< "#A0A0A0"
<< "#0078D7"
<< "#C0C0C0";
return QStringList();
}

Expand Down
6 changes: 2 additions & 4 deletions YUViewLib/src/common/Functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ QStringList getThemeNameList();
QString getThemeFileName(QString themeName);
// For the given theme, return the primary colors to replace.
// In the qss file, we can use tags, which will be replaced by these colors. The tags are:
// #backgroundColor, #activeColor, #inactiveColor, #highlightColor
// #backgroundColor, #activeColor, #inactiveColor, #highlightColor, #borderColor
// The values to replace them by are returned in this order.
QStringList getThemeColors(QString themeName);

Expand All @@ -81,6 +81,7 @@ template <size_t N> QStringList toQStringList(const std::array<std::string_view,

std::string toLower(const std::string_view str);
std::optional<int> toInt(const std::string_view str);
std::optional<unsigned> toUnsigned(const std::string_view str);
std::vector<std::string_view> splitString(const std::string_view str, const char delimiter);
std::string_view stripWhitespace(std::string_view str);

Expand All @@ -104,7 +105,4 @@ template <typename T, typename R> inline T clip(T val, Range<R> range)
return (val < T(range.min)) ? T(range.min) : (val > T(range.max)) ? T(range.max) : val;
}

std::optional<unsigned> toUnsigned(const std::string_view text);
std::optional<int> toInt(const std::string_view text);

} // namespace functions
Loading