# 实况照片播放器 **Repository Path**: user_gitee/LivePhotoPlayer ## Basic Information - **Project Name**: 实况照片播放器 - **Description**: 一款专门用于播放主流手机厂商(以 OPPO 为代表)拍摄的实况照片的桌面应用程序。软件基于 Python 3.13 开发,采用 PyQt5 构建图形界面。 - **Primary Language**: Python - **License**: AGPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-02 - **Last Updated**: 2026-07-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 实况照片播放器 - 开发文档 **版本:3.18.0** **最后更新:2026-05-02** **作者:XAF** --- ## 1. 项目概述 实况照片播放器是一款专门用于播放主流手机厂商(以 OPPO 为代表)拍摄的实况照片的桌面应用程序。软件基于 Python 3.13 开发,采用 PyQt5 构建图形界面,能够: - 支持拖拽加载 JPG/JPEG/HEIC 格式的实况图。 - 自动提取内嵌的 MP4 视频流(基于 XMP 元数据或回退搜索 `ftyp` 标记)。 - 使用内嵌 VLC 播放器在界面内播放视频,视频显示区域与照片显示区域大小一致。 - 提供系统托盘、完整日志记录、错误处理、配置记忆等辅助功能。 - 支持外部视频文件(MP4、AVI、MKV、MOV)直接播放,带封面预览。 - 支持图片/视频文件列表管理、导出封面与视频、窗口大小记忆、自动/手动播放模式切换。 --- ## 2. 核心功能 | 功能模块 | 描述 | |---------|------| | **实况照片解析** | 自动识别 OPPO 实况照片的 XMP 元数据(`Item:Length`),提取尾部 MP4 视频流;若失败则回退搜索最后一个 `ftyp` 标记。 | | **图像浏览** | 支持以鼠标为中心的缩放(滚轮,1.05 倍步进,范围 0.1~10)、拖拽移动、双击恢复适应窗口。适应窗口模式下,单击左/中/右区域分别实现上一张/播放/下一张。 | | **视频播放** | 内嵌 VLC 引擎,支持播放实况视频或外部视频文件。提供播放、暂停、停止、速度调节(0.25x~2x)、静音功能。外部视频显示封面(`cover/video.png`),单击封面播放。 | | **自动播放模式** | 右键菜单可切换“自动播放”/“点击播放”。自动播放模式下,切换实况照片后自动播放视频,播放结束自动返回图片视图,无黑屏闪烁。 | | **文件列表** | 自动列出当前文件夹内所有支持格式的图片(jpg、jpeg、heic、png、bmp、tiff),单击切换,高亮当前项。 | | **导出功能** | 导出照片(HEIC 自动转 JPEG)到 `./照片`;导出视频到 `./视频`。文件名格式:`原文件名_photo_时分秒_四位随机数.扩展名`。 | | **配置记忆** | 保存上次打开的目录、窗口大小、播放速度索引、静音状态、自动播放模式。窗口位置不再记录,每次启动默认居中。 | | **系统托盘** | 关闭窗口后隐藏至托盘,支持双击恢复、右键退出。退出操作采用异步调用,避免卡死。 | | **日志系统** | 每次运行生成独立日志文件,位于 `./日志/YYYYMMDD_HHMMSS_随机数.log`,记录关键操作和错误信息。 | | **右键菜单** | 提供打开文件、恢复大小、初始状态、关闭照片/视频、上一张/下一张、导出照片/视频、打开照片/视频文件夹、显示/隐藏文件列表、自动播放切换、退出等选项。 | | **全局快捷键** | 左右/上下箭头切换图片,`Enter` 键播放/暂停视频。 | | **拖拽与命令行** | 支持将图片或视频文件拖拽至窗口,也支持通过命令行参数传递文件路径(拖拽至 EXE 图标)。采用延迟加载(100ms)避免启动卡顿。 | --- ## 3. 技术栈 | 组件 | 技术选型 / 版本 | |------|----------------| | 编程语言 | Python 3.13(64 位) | | 界面框架 | PyQt5 5.15.9+ | | 视频解码 | VLC 引擎 + python-vlc 3.0.20123 | | 图像处理 | Pillow (PIL) 10.2.0+ | | 日志记录 | Python logging | | 打包工具 | PyInstaller 6.20.0+ | | 版本信息 | 使用 PyInstaller 内建 `VSVersionInfo` 生成 Windows 文件属性 | --- ## 4. 目录结构 ``` 项目根目录/ ├── main.py # 主程序入口 ├── app.ico # 应用程序图标(32x32 或更大) ├── vlc/ # VLC 运行时库(64 位) │ ├── libvlc.dll │ ├── libvlccore.dll │ └── plugins/ # 完整插件目录(保持子文件夹结构) ├── cover/ # 封面图片目录 │ └── video.png # 外部视频封面(可任意拉伸铺满) ├── 照片/ # 自动创建,存放导出的照片 ├── 视频/ # 自动创建,存放导出的视频 ├── logs/ # 自动创建,存放日志文件 ├── config/ # 自动创建,存放配置文件 │ └── config.json # 配置信息(JSON 格式) └── App13324.spec # PyInstaller 打包配置文件 ``` **注意**:`vlc` 目录需从 VLC 安装目录(如 `C:\Program Files\VideoLAN\VLC`)复制,并确保与 Python 位数匹配(64 位)。 --- ## 5. 主要模块与代码说明 ### 5.1 配置管理 (`ConfigManager`) - 位置:`main.py` 中定义的类。 - 功能:读写 `config/config.json`,保存/加载上次打开目录、窗口大小、速度索引、静音状态、自动播放模式。 - 关键方法: - `get_last_dir()` / `set_last_dir()` - `get_window_size()` / `set_window_size()` - `get_speed_index()` / `set_speed_index()` - `get_mute_state()` / `set_mute_state()` - `get_auto_play()` / `set_auto_play()` **注**:窗口位置不再保存,每次启动强制居中。 ### 5.2 实况视频提取 - **XMP 元数据解析**:搜索 `Item:Length="数字"`,取得视频字节长度。 - **偏移计算**:`offset = total_size - video_len`,从该位置读取数据并验证 `ftyp` 魔数(第 4-7 字节为 `b'ftyp'`)。 - **回退策略**:若 XMP 缺失或偏移处无效,则在整个文件二进制中搜索最后一个 `ftyp` 位置并提取至文件尾。 - **临时文件**:将提取的视频数据保存为系统临时目录下的 `livephoto_*.mp4`,程序退出时自动删除。 ### 5.3 图片滚动视图 (`ImageScrollView`) - 继承 `QWidget`,嵌入 `QScrollArea` 实现滚动和缩放。 - **缩放实现**:`wheelEvent` 中计算鼠标位置在图片上的归一化坐标,缩放后重新计算滚动条值以实现以鼠标为中心的效果。缩放因子 1.05,范围 0.1~10。 - **拖拽实现**:`mousePressEvent` 中判断是否在缩放模式(`zoom > auto_zoom`),若是则启用 `dragging` 标志,`mouseMoveEvent` 中调整滚动条值。 - **区域单击**:仅在适应窗口模式(`abs(zoom - auto_zoom) < 0.01`)下,根据点击位置占图片宽度的比例触发上一张(<0.33)、播放(0.33~0.67)、下一张(>0.67)。 ### 5.4 视频播放与封面管理 - **区分实况照片与外部视频**:通过布尔标志 `self.is_live_photo`。 - 实况照片:播放时直接显示视频画面,不涉及封面;停止/结束后切回图片视图。 - 外部视频:加载后显示封面(`cover/video.png` 或黑色背景文字),单击封面开始播放,播放中单击画面暂停/继续,右键停止并回到封面。 - **异步停止**:停止按钮调用 `stop_playback_async()`,使用 `QTimer.singleShot(0, ...)` 避免阻塞 UI,解决 VLC 可能导致的卡死问题。 - **播放结束处理**:`on_playback_end` 中针对外部视频调用 `stop_playback_async()`,使其自动回到封面并归零播放位置。 ### 5.5 自动播放模式实现 - 配置项 `auto_play` 存储在 `config.json`。 - 在 `_on_image_loaded` 中:自动播放模式下,仍然设置图片 pixmap(但不切换到图片视图),避免闪烁。 - 在 `_on_video_prepared` 中:若自动播放模式且视频准备就绪,延迟 10ms 调用 `toggle_play()` 开始播放。 - 播放结束后(`_on_playback_end_ui`)切回图片视图。 ### 5.6 文件列表与导航 - `refresh_file_list()`:扫描当前图片所在文件夹,按文件名排序,更新 `QListWidget`。 - 单击列表项调用 `load_file()` 切换。 - 顶部工具栏“显示列表”按钮控制 `QDockWidget` 的显隐,按钮文字同步切换。 - 支持键盘左右/上下箭头切换,连动当前索引和列表选区。 ### 5.7 导出功能 - 导出照片:将当前图片(或 HEIC 转换后的 JPEG)复制到 `./照片` 文件夹,文件名格式 `原文件名_photo_HHMMSS_四位随机数.扩展名`。 - 导出视频:将当前临时视频文件复制到 `./视频` 文件夹。 - “打开照片”按钮打开 `./照片` 文件夹,“打开视频”按钮打开 `./视频` 文件夹。 ### 5.8 异步加载线程 (`LoadImageThread`) - 为避免加载大图片或提取视频时界面卡死,使用 `QThread` 执行耗时操作。 - 发射信号 `image_loaded` 和 `video_prepared` 更新 UI。 - 通过序列号 `load_seq` 机制避免快速切换文件时旧线程干扰新加载的文件。 ### 5.9 日志系统 - 每次启动生成唯一日志文件,路径:`logs/YYYYMMDD_HHMMSS_随机数.log`。 - 记录 INFO、ERROR、CRITICAL 等级日志,便于排查问题。 - 全局异常钩子 `sys.excepthook` 捕获未处理异常并写入日志。 ### 5.10 系统托盘 - `QSystemTrayIcon` 实现,图标来自 `app.ico`。 - 最小化窗口时隐藏至托盘,托盘右键菜单包含“显示主窗口”和“退出”。 - 退出操作采用异步调度,防止卡死。 --- ## 6. 打包部署 ### 6.1 环境准备 1. 安装 Python 3.13(64 位)。 2. 安装依赖: ```bash pip install PyQt5 Pillow python-vlc ``` 3. 准备 VLC 运行库: - 从 VLC 安装目录复制 `libvlc.dll`、`libvlccore.dll` 和 `plugins` 文件夹到项目下的 `vlc` 目录。 - 确保与 Python 位数匹配(64 位)。 ### 6.2 使用 PyInstaller 打包 项目根目录下已提供 `App13324.spec` 文件,执行以下命令即可生成单文件 EXE: ```bash pyinstaller App13324.spec ``` ### 6.3 spec 文件重点说明 - `datas` 中包含 `('vlc', 'vlc')`、`('cover', 'cover')`、`('app.ico', '.')`,确保运行时资源可用。 - `version` 参数使用 `VSVersionInfo` 构造版本信息(3.18.0)。 - `upx=True` 启用 UPX 压缩(需提前安装 UPX 并加入 PATH)。 - `console=False` 屏蔽控制台窗口。 ### 6.4 生成产物 - `dist/App13324.exe` 为最终可执行文件。 - 运行 EXE 时,会自动在当前目录生成 `日志/`、`config/`、`照片/`、`视频/` 文件夹,以及 `vlc`、`cover` 子目录(已内嵌,无需手动放置)。 --- ## 7. 使用说明 ### 7.1 启动程序 - 双击 `App13324.exe` 运行,窗口默认居中,大小 800x600(可调整)。 - 支持命令行参数传递文件路径:`App13324.exe "D:\photo.jpg"`,程序启动后延迟 100ms 加载文件。 ### 7.2 加载媒体 - **拖拽文件**:将图片或视频文件直接拖入窗口。 - **打开文件**:点击工具栏“打开文件”或右键菜单选择。 ### 7.3 基本操作 | 操作 | 方式 | |------|------| | 切换图片 | 单击图片左/右区域(适应窗口时)、工具栏“上一张/下一张”按钮、键盘左右/上下箭头、文件列表单击 | | 播放/暂停 | 单击图片中间区域(适应窗口时)、底部“播放/暂停”按钮、`Enter` 键、播放中单击视频画面 | | 停止视频 | 底部“停止”按钮、右键单击视频画面(外部视频) | | 缩放图片 | 鼠标滚轮(以光标为中心) | | 拖动图片 | 放大后按住左键移动 | | 恢复图片大小 | 双击图片、工具栏“恢复大小”按钮、右键菜单“恢复大小” | | 初始状态 | 工具栏“初始状态”按钮、右键菜单“初始状态”(重置所有状态和窗口大小,窗口位置居中) | | 自动/手动播放 | 右键菜单切换,当前模式显示“点击播放”或“自动播放”,切换后保存配置 | | 导出照片/视频 | 工具栏或右键菜单 | | 文件列表 | 工具栏“显示列表”按钮、右键菜单 | | 托盘操作 | 关闭窗口最小化到托盘,双击托盘图标恢复,右键退出 | ### 7.4 配置记忆 - 程序自动保存:上次打开的目录、窗口宽度/高度、播放速度索引、静音状态、自动播放模式。 - 窗口位置不保存,每次启动强制居中。 ### 7.5 日志查看 - 日志文件位于 `logs/` 文件夹,命名规则 `YYYYMMDD_HHMMSS_随机数.log`,可用文本编辑器查看。 --- ## 8. 常见问题与解决方案 | 问题 | 解决办法 | |------|----------| | 拖拽实况照片后提示“未检测到实况视频” | 检查图片是否为 OPPO 实况图;或使用 HxD 等工具确认文件尾部是否有 `ftyp` 标记。 | | 视频播放黑屏或无声 | 确认 `vlc` 目录完整(包含 `plugins`);检查系统音量、静音状态。 | | 打包后运行提示 `libvlc.dll not found` | 确保 spec 文件中 `datas` 包含 `('vlc', 'vlc')`,或手动将 `vlc` 目录放置于 EXE 同级。 | | 界面卡死无响应 | 可能是 VLC 操作阻塞,程序已使用异步停止机制,可尝试右键托盘退出后重新启动。 | | HEIC 图片无法显示 | 需要 Pillow 支持 `libheif`,通常 Windows 下 Pillow 已内置,无需额外操作。 | | 自动播放切换照片时黑屏 | 已修复:自动播放模式下预先设置图片但不切换界面,播放结束再切换,实现无缝播放。 | | 窗口位置记录失效 | 新版本已移除位置记忆,每次启动默认居中。若需恢复位置可修改代码启用 `moveEvent` 保存。 | --- ## 9. 扩展性设计 - **增加其他厂商实况照片支持**:在 `extract_video_data` 中增加对不同 XMP 命名空间的解析,或添加额外的 Magic Number 校验。 - **自定义封面**:替换 `cover/video.png` 即可。 - **支持更多视频格式**:VLC 本身支持广泛格式,只需在文件打开对话框和拖拽识别中添加对应扩展名。 - **增加播放列表**:可扩展 `file_list` 模块,支持多文件夹扫描。 - **窗口位置记忆恢复**:如需记录窗口位置,可在 `resizeEvent` 和 `moveEvent` 中保存坐标,并在 `__init__` 中读取后调用 `move()`。 --- ## 10. 版本历史 | 版本 | 日期 | 说明 | |------|------|------| | 1.0 | 2026-04-30 | 初始版本,支持 OPPO 实况图,内嵌 VLC 播放,基本托盘/拖拽功能。 | | 2.0 | 2026-04-30 | 增加文件列表、导出、配置记忆、缩放中心、拖拽修复、恢复大小、初始状态、响应式布局。 | | 3.0 | 2026-05-01 | 增加视频封面、右键菜单增强、异步停止、位置记忆、自动播放模式、播放结束自动回封面。 | | 3.18.0 | 2026-05-02 | 修复自动播放黑屏、优化提示文字、移除位置记忆并改为居中启动、增加延迟加载命令行文件;最终稳定版。 | --- ## 11. 许可与致谢 - 本软件为独立开发,版权归作者所有。 - 使用 PyQt5(GPL v3)、python-vlc(LGPL)、Pillow(HPND)等开源库,遵循相应许可协议。 - 特别感谢 PyInstaller 和 VLC 团队提供的优秀工具。 --- **文档结束**