From f7b7da8edbb06da972a0283d70188a22762fa964 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 04:48:29 +0000 Subject: [PATCH 1/3] Initial plan From b775224cc8309f3572568ced1e31d1420ffe1928 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 04:55:41 +0000 Subject: [PATCH 2/3] Update README files with improved format and latest features - Add badges for build status, version, and platform - Add detailed feature list and key highlights - Improve structure with clear sections and emojis - Add development and build information (v4.4) - Document GitHub Actions automated build workflow - Improve usage instructions with tables and formatting - Update FAQ section with better formatting - Add credits and support sections - Update all three language versions (zh-CN, zh-TW, en) Co-authored-by: Cross2pro <30151531+Cross2pro@users.noreply.github.com> --- README.md | 195 +++++++++++++++++++++++++++++++++++++---------- README.md.backup | 106 ++++++++++++++++++++++++++ README_en.md | 191 +++++++++++++++++++++++++++++++++++++--------- README_tc.md | 188 ++++++++++++++++++++++++++++++++++++--------- 4 files changed, 570 insertions(+), 110 deletions(-) create mode 100644 README.md.backup diff --git a/README.md b/README.md index 86abbe5..0a02a97 100644 --- a/README.md +++ b/README.md @@ -2,72 +2,187 @@ [简体中文](./README.md) | [繁體中文](./README_tc.md) | [English](./README_en.md) -基于Xposed的虚拟摄像头 +[![Android Build](https://github.com/w2016561536/android_virtual_cam/workflows/Android%20Build/badge.svg)](https://github.com/w2016561536/android_virtual_cam/actions) +[![Version](https://img.shields.io/badge/version-4.4-blue.svg)](https://github.com/w2016561536/android_virtual_cam/releases) +[![Platform](https://img.shields.io/badge/platform-Android%205.0%2B-green.svg)](https://github.com/w2016561536/android_virtual_cam) -# 请勿用于任何非法用途,所有后果自负。 +基于Xposed的虚拟摄像头模块,可将Android相机预览和拍照替换为自定义视频和图片。 -### 中国大陆加速地址(Gitee平台): https://gitee.com/w2016561536/android_virtual_cam +## ⚠️ 免责声明 -## 支持平台: +**请勿用于任何非法用途,所有后果自负。** -- 安卓5.0+ +本工具仅供学习和研究使用,请勿用于任何违法违规行为。使用者需承担使用本工具产生的一切后果和责任。 -## 使用方法 +## 🌟 主要特性 -1. 安装此模块,并在Xposed中启用此模块,Lsposed等包含作用域的框架需要选择目标app,无需选择系统框架。 - -2. 在系统设置中,授予目标应用读取本地存储的权限,并强制结束目标应用程序。若应用程序未申请此权限,请见步骤3。 - -3. 打开目标应用,若应用未能获得读取存储的权限,则会以气泡消息提示,`Camera1`目录被重定向至应用程序私有目录`/[内部存储]/Android/data/[应用包名]/files/Camera1/`。若未提示,则默认`Camera1`目录为`/[内部存储]/DCIM/Camera1/`。若目录不存在,请手动创建。 +- ✅ **双API支持**:支持Camera1和Camera2 API +- ✅ **视频替换**:将相机预览替换为自定义MP4视频 +- ✅ **照片替换**:将拍照结果替换为自定义BMP图片 +- ✅ **音频播放**:可选择播放视频音频 +- ✅ **应用配置界面**:5个可视化开关,无需手动创建文件 +- ✅ **私有目录支持**:支持为每个应用单独配置视频 +- ✅ **实时生效**:配置文件修改后立即生效,无需重启 +- ✅ **Toast消息控制**:可关闭提示消息 +- ✅ **自动化构建**:GitHub Actions自动构建和发布 -> 注意:私有目录下的`Camera1`仅对该应用单独生效。 +## 📱 支持平台 -4. 在目标应用中打开相机预览,会以气泡消息提示“宽:……高:……”,需要根据此分辨率数据制作替换视频,放置于`Camera1`目录下,并命名为`virtual.mp4`,若打开相机并无提示消息,则无需调整视频分辨率。 - -5. 若在目标应用中拍照却显示真实图片,且出现气泡消息`发现拍照`和分辨率,则需根据此分辨率数据准备一张照片,命名为`1000.bmp`,放入`Camera1`目录下(支持其它格式改后缀为bmp)。如果拍照时无气泡消息提示,则`1000.bmp`无效。 - -6. 如果需要播放视频的声音,需在`/[内部存储]/DCIM/Camera1/`目录下创建`no-silent.jpg`文件。(全局实时生效) +- Android 5.0+ (API 21+) +- 需要Xposed框架(如LSPosed、EdXposed等) + +## 📦 下载与安装 + +### 下载地址 + +- **GitHub Releases**:[最新版本](https://github.com/w2016561536/android_virtual_cam/releases) +- **中国大陆加速(Gitee)**:https://gitee.com/w2016561536/android_virtual_cam + +### 安装步骤 + +1. 下载最新版本的APK文件 +2. 在Android设备上安装APK +3. 在Xposed框架中启用此模块 +4. 重启设备使模块生效 + +## 📖 使用方法 + +### 基础配置 + +1. **启用模块** + - 在Xposed框架(如LSPosed)中启用本模块 + - LSPosed等包含作用域的框架需要选择目标app,**无需选择系统框架** + - 重启目标应用或设备 + +2. **授予权限** + - 在系统设置中,授予目标应用读取本地存储的权限 + - 强制结束目标应用程序 + - 若应用程序未申请此权限,请见步骤3 + +3. **确定工作目录** + - 打开目标应用,若应用未能获得读取存储的权限,会以Toast消息提示 + - 私有目录模式:`/[内部存储]/Android/data/[应用包名]/files/Camera1/` + - 共享目录模式:`/[内部存储]/DCIM/Camera1/` + - 若目录不存在,请手动创建 -7. 如果需要临时停用视频替换,需在`/[内部存储]/DCIM/Camera1/`目录下创建`disable.jpg`文件。(全局实时生效) + > 💡 **提示**:私有目录下的`Camera1`仅对该应用单独生效 + +### 视频和照片配置 + +4. **准备替换视频** + - 在目标应用中打开相机预览 + - 查看Toast消息显示的分辨率(宽×高) + - 根据此分辨率制作替换视频 + - 将视频命名为`virtual.mp4`,放置于`Camera1`目录下 + - 若无提示消息,则无需调整视频分辨率 + +5. **准备替换照片(可选)** + - 在目标应用中拍照时,若显示真实图片且有Toast提示`发现拍照` + - 查看Toast消息显示的照片分辨率 + - 准备一张相同分辨率的照片,命名为`1000.bmp` + - 放入`Camera1`目录下(支持其它格式改后缀为bmp) + - 如果拍照时无Toast提示,则无需准备`1000.bmp` + +### 功能开关配置 + +本模块提供了5个功能开关,有两种配置方式: + +#### 方式一:使用应用界面配置(推荐) + +打开VCAM应用,使用可视化开关进行配置: + +- **强制显示权限提醒**:重复显示目录重定向的Toast消息 +- **临时禁用模块**:临时停用视频替换功能 +- **播放视频声音**:播放替换视频的音频 +- **强制使用私有目录**:为每个应用单独分配视频 +- **禁用Toast消息**:关闭所有Toast提示 + +#### 方式二:手动创建文件配置 + +在`/[内部存储]/DCIM/Camera1/`目录下创建对应文件: + +| 功能 | 文件名 | 说明 | +|------|--------|------| +| 播放视频声音 | `no-silent.jpg` | 启用视频音频播放 | +| 临时禁用模块 | `disable.jpg` | 临时停用视频替换 | +| 禁用Toast消息 | `no_toast.jpg` | 关闭Toast提示 | +| 强制显示权限提醒 | `force_show.jpg` | 重复显示目录重定向消息 | +| 强制使用私有目录 | `private_dir.jpg` | 强制所有应用使用私有目录 | + +> 💡 **提示**:所有配置开关全局实时生效,修改后无需重启应用 + +## ❓ 常见问题 + +### Q1. 前置摄像头方向问题? +**A1.** 大多数情况下,替换前置摄像头的视频需要水平翻转并右旋90度,并且视频***处理后***的分辨率应与Toast消息内分辨率相同。但有时这并不需要,具体请根据实际情况判断。 + +### Q2. 画面黑屏,相机启动失败? +**A2.** 目前有些应用并不能成功替换(特别是系统相机)。或者是因为视频路径不对(是否创建了两级Camera1目录,如`./DCIM/Camera1/Camera1/virtual.mp4`,但只需要一级目录)。 + +### Q3. 画面花屏? +**A3.** 视频分辨率不对。 + +### Q4. 画面扭曲、变形? +**A4.** 请使用剪辑软件修改原视频来匹配屏幕。 -8. 如果觉得Toast消息烦,可以在`/[内部存储]/DCIM/Camera1/`目录下创建`no_toast.jpg`文件。(全局实时生效) +### Q5. 创建`disable.jpg`无效? +**A5.** +- 如果应用版本`<=4.0`,那么`[内部存储]/DCIM/Camera1`目录下的文件对**具有访问存储权限**的应用生效,其余无权限应用应在**私有目录**下创建 +- 如果应用版本`>=4.1`,那么应在`[内部存储]/DCIM/Camera1`创建,无论目标应用是否具有权限 -9. 目录重定向消息默认只显示一次,如果错过了目录重定向的Toast消息,可以在`/[内部存储]/DCIM/Camera1/`目录下创建`force_show.jpg`文件来覆盖默认设定。(全局实时生效) +## 🔧 开发与构建 -10. 如果需要为每一个应用程序分配视频,可以在`/[内部存储]/DCIM/Camera1/`目录下创建`private_dir.jpg`强制使用应用程序私有目录。(全局实时生效) +### 版本信息 -> 注意:6~10的配置开关均在应用程序中,您可以快捷地在应用程序中配置,也可以手动创建文件。 +- **当前版本**:v4.4 (versionCode 28) +- **编译SDK**:Android 12 (API 31) +- **最低支持**:Android 5.0 (API 21) +- **目标SDK**:Android 8.0 (API 26) -## 常见问题 +### 自动化构建 -A1. 前置摄像头方向问题? -Q1. 大多数情况下,替换前置摄像头的视频需要水平翻转并右旋90度,并且视频***处理后***的分辨率应与气泡消息内分辨率相同。但有时这并不需要,具体请根据实际情况判断。 +本项目使用GitHub Actions实现自动化构建和发布: +- **持续集成**:每次代码推送都会自动编译验证 +- **自动发布**:创建新标签(tag)时自动构建签名APK并发布Release +- **工作流配置**:参见 [.github/workflows/android-build.yml](.github/workflows/android-build.yml) +- **详细说明**:参见 [WORKFLOW_CHANGES.md](WORKFLOW_CHANGES.md) -Q2. 画面黑屏,相机启动失败? -A2. 目前有些应用并不能成功替换(特别是系统相机)。或者是因为视频路径不对(是否创建了两级Camera1目录,如`./DCIM/Camera1/Camera1/virtual.mp4`,但只需要一级目录)。 +### 本地构建 +```bash +# 克隆仓库 +git clone https://github.com/w2016561536/android_virtual_cam.git +cd android_virtual_cam -Q3. 画面花屏? -A3. 视频分辨率不对。 +# 构建调试版本 +./gradlew assembleDebug -Q4. 画面扭曲,变形? -A4. 请使用剪辑软件修改原视频来匹配屏幕。 +# 构建发布版本(需要签名配置) +./gradlew assembleRelease +``` -Q5. 创建`disable.jpg`无效? -A5. 如果应用版本`<=4.0`,那么`[内部存储]/DCIM/Camera1`目录下的文件对**具有访问存储权限**的应用生效,其余无权限应用应在**私有目录**下创建 -如果应用版本`>=4.1`,那么应在`[内部存储]/DCIM/Camera1`创建,无论目标应用是否具有权限。 +## 🐛 反馈问题 +请直接在[Issues](https://github.com/w2016561536/android_virtual_cam/issues)中反馈。 -## 反馈问题 +如果是BUG反馈,请附带以下信息: +- Xposed**模块**日志信息 +- Android系统版本 +- Xposed框架类型和版本 +- 目标应用信息 +- 复现步骤 -请直接在issues中反馈,如果为BUG反馈,请附带Xposed**模块**日志信息。 +## 🙏 致谢 +- 提供HOOK思路:[CameraHook](https://github.com/wangwei1237/CameraHook) +- H264硬解码:[Android-VideoToImages](https://github.com/zhantong/Android-VideoToImages) +- JPEG转YUV:[博客文章](https://blog.csdn.net/jacke121/article/details/73888732) -## 致谢: +## 📜 许可证 -提供HOOK思路: https://github.com/wangwei1237/CameraHook +本项目仅供学习和研究使用,请勿用于任何违法违规用途。 -H264硬解码: https://github.com/zhantong/Android-VideoToImages +## ⭐ 支持项目 -JPEG转YUV: https://blog.csdn.net/jacke121/article/details/73888732 +如果这个项目对你有帮助,请给个Star ⭐️ diff --git a/README.md.backup b/README.md.backup new file mode 100644 index 0000000..caa3bdc --- /dev/null +++ b/README.md.backup @@ -0,0 +1,106 @@ +# android_virtual_cam + +[简体中文](./README.md) | [繁體中文](./README_tc.md) | [English](./README_en.md) + +[![Android Build](https://github.com/w2016561536/android_virtual_cam/workflows/Android%20Build/badge.svg)](https://github.com/w2016561536/android_virtual_cam/actions) +[![Version](https://img.shields.io/badge/version-4.4-blue.svg)](https://github.com/w2016561536/android_virtual_cam/releases) +[![Platform](https://img.shields.io/badge/platform-Android%205.0%2B-green.svg)](https://github.com/w2016561536/android_virtual_cam) + +基于Xposed的虚拟摄像头模块,可将Android相机预览和拍照替换为自定义视频和图片。 + +## ⚠️ 免责声明 + +**请勿用于任何非法用途,所有后果自负。** + +本工具仅供学习和研究使用,请勿用于任何违法违规行为。使用者需承担使用本工具产生的一切后果和责任。 + +## 🌟 主要特性 + +- ✅ **双API支持**:支持Camera1和Camera2 API +- ✅ **视频替换**:将相机预览替换为自定义MP4视频 +- ✅ **照片替换**:将拍照结果替换为自定义BMP图片 +- ✅ **音频播放**:可选择播放视频音频 +- ✅ **应用配置界面**:5个可视化开关,无需手动创建文件 +- ✅ **私有目录支持**:支持为每个应用单独配置视频 +- ✅ **实时生效**:配置文件修改后立即生效,无需重启 +- ✅ **Toast消息控制**:可关闭提示消息 +- ✅ **自动化构建**:GitHub Actions自动构建和发布 + +## 📱 支持平台 + +- Android 5.0+ (API 21+) +- 需要Xposed框架(如LSPosed、EdXposed等) + +## 📦 下载与安装 + +### 下载地址 + +- **GitHub Releases**:[最新版本](https://github.com/w2016561536/android_virtual_cam/releases) +- **中国大陆加速(Gitee)**:https://gitee.com/w2016561536/android_virtual_cam + +### 安装步骤 + +1. 下载最新版本的APK文件 +2. 在Android设备上安装APK +3. 在Xposed框架中启用此模块 +4. 重启设备使模块生效 + +## 使用方法 + +1. 安装此模块,并在Xposed中启用此模块,Lsposed等包含作用域的框架需要选择目标app,无需选择系统框架。 + +2. 在系统设置中,授予目标应用读取本地存储的权限,并强制结束目标应用程序。若应用程序未申请此权限,请见步骤3。 + +3. 打开目标应用,若应用未能获得读取存储的权限,则会以气泡消息提示,`Camera1`目录被重定向至应用程序私有目录`/[内部存储]/Android/data/[应用包名]/files/Camera1/`。若未提示,则默认`Camera1`目录为`/[内部存储]/DCIM/Camera1/`。若目录不存在,请手动创建。 + +> 注意:私有目录下的`Camera1`仅对该应用单独生效。 + +4. 在目标应用中打开相机预览,会以气泡消息提示“宽:……高:……”,需要根据此分辨率数据制作替换视频,放置于`Camera1`目录下,并命名为`virtual.mp4`,若打开相机并无提示消息,则无需调整视频分辨率。 + +5. 若在目标应用中拍照却显示真实图片,且出现气泡消息`发现拍照`和分辨率,则需根据此分辨率数据准备一张照片,命名为`1000.bmp`,放入`Camera1`目录下(支持其它格式改后缀为bmp)。如果拍照时无气泡消息提示,则`1000.bmp`无效。 + +6. 如果需要播放视频的声音,需在`/[内部存储]/DCIM/Camera1/`目录下创建`no-silent.jpg`文件。(全局实时生效) + +7. 如果需要临时停用视频替换,需在`/[内部存储]/DCIM/Camera1/`目录下创建`disable.jpg`文件。(全局实时生效) + +8. 如果觉得Toast消息烦,可以在`/[内部存储]/DCIM/Camera1/`目录下创建`no_toast.jpg`文件。(全局实时生效) + +9. 目录重定向消息默认只显示一次,如果错过了目录重定向的Toast消息,可以在`/[内部存储]/DCIM/Camera1/`目录下创建`force_show.jpg`文件来覆盖默认设定。(全局实时生效) + +10. 如果需要为每一个应用程序分配视频,可以在`/[内部存储]/DCIM/Camera1/`目录下创建`private_dir.jpg`强制使用应用程序私有目录。(全局实时生效) + +> 注意:6~10的配置开关均在应用程序中,您可以快捷地在应用程序中配置,也可以手动创建文件。 + +## 常见问题 + +A1. 前置摄像头方向问题? +Q1. 大多数情况下,替换前置摄像头的视频需要水平翻转并右旋90度,并且视频***处理后***的分辨率应与气泡消息内分辨率相同。但有时这并不需要,具体请根据实际情况判断。 + + +Q2. 画面黑屏,相机启动失败? +A2. 目前有些应用并不能成功替换(特别是系统相机)。或者是因为视频路径不对(是否创建了两级Camera1目录,如`./DCIM/Camera1/Camera1/virtual.mp4`,但只需要一级目录)。 + + +Q3. 画面花屏? +A3. 视频分辨率不对。 + +Q4. 画面扭曲,变形? +A4. 请使用剪辑软件修改原视频来匹配屏幕。 + +Q5. 创建`disable.jpg`无效? +A5. 如果应用版本`<=4.0`,那么`[内部存储]/DCIM/Camera1`目录下的文件对**具有访问存储权限**的应用生效,其余无权限应用应在**私有目录**下创建 +如果应用版本`>=4.1`,那么应在`[内部存储]/DCIM/Camera1`创建,无论目标应用是否具有权限。 + + +## 反馈问题 + +请直接在issues中反馈,如果为BUG反馈,请附带Xposed**模块**日志信息。 + + +## 致谢: + +提供HOOK思路: https://github.com/wangwei1237/CameraHook + +H264硬解码: https://github.com/zhantong/Android-VideoToImages + +JPEG转YUV: https://blog.csdn.net/jacke121/article/details/73888732 diff --git a/README_en.md b/README_en.md index 499f842..17f6fe0 100644 --- a/README_en.md +++ b/README_en.md @@ -2,66 +2,187 @@ [简体中文](./README.md) | [繁體中文](./README_tc.md) | [English](./README_en.md) -A virtual camera based on Xposed +[![Android Build](https://github.com/w2016561536/android_virtual_cam/workflows/Android%20Build/badge.svg)](https://github.com/w2016561536/android_virtual_cam/actions) +[![Version](https://img.shields.io/badge/version-4.4-blue.svg)](https://github.com/w2016561536/android_virtual_cam/releases) +[![Platform](https://img.shields.io/badge/platform-Android%205.0%2B-green.svg)](https://github.com/w2016561536/android_virtual_cam) -## DO NOT USE FOR ANY ILLEGAL PURPOSE, YOU NEED TO TAKE ALL RESPONSIBILITY AND CONSEQUENCE! +An Xposed-based virtual camera module that replaces Android camera preview and photo capture with custom videos and images. -## Supported platform +## ⚠️ Disclaimer -- Android 5.0+ +**DO NOT USE FOR ANY ILLEGAL PURPOSE! YOU MUST TAKE ALL RESPONSIBILITY AND CONSEQUENCES!** -## Usage +This tool is for learning and research purposes only. Do not use it for any illegal activities. Users must assume all consequences and responsibilities arising from using this tool. -1. Install this module , enable it in Xposed . Lsposed and other framework which have a scope list, you need to choose target app instead of System Framework. +## 🌟 Key Features -2. In system Setting, authorize target to access local storage, and force stop the app. If the app does not request this permission, see step3. +- ✅ **Dual API Support**: Supports both Camera1 and Camera2 APIs +- ✅ **Video Replacement**: Replace camera preview with custom MP4 videos +- ✅ **Photo Replacement**: Replace captured photos with custom BMP images +- ✅ **Audio Playback**: Optional video audio playback +- ✅ **App Configuration UI**: 5 visual switches, no need to create files manually +- ✅ **Private Directory Support**: Separate video configuration for each app +- ✅ **Real-time Effect**: Configuration changes take effect immediately without restart +- ✅ **Toast Message Control**: Option to disable toast notifications +- ✅ **Automated Build**: GitHub Actions for automated building and releasing -3. open target app, if the app does not have the permission to access local storage. There will be a toast message showing that `Camera1` directory has been redirect to app's private directory `/[INTERNEL_STORAGE]/Android/data/[package_name]/files/Camera1/`. If there isn't the message, the default `Camera1` directory is `/[INTERNEL_STORAGE]/DCIM/Camera1/`. If the directory doesn't exist. Please create it by yourself. +## 📱 Supported Platforms -> Attention: `Camera1` in the private directory only works for single app. +- Android 5.0+ (API 21+) +- Requires Xposed framework (such as LSPosed, EdXposed, etc.) -4. Open the camera in target app. There will be a toast message showing the resolution (宽width: , 高height:) . And you need to adjust the replacing video's resolution to make them same. Name it as `virtual.mp4`, put it under `Camera1` directory. +## 📦 Download & Installation -5. If there is a toast message when you take photos in app ("发现拍照"),it shows the photo's resolution. You need to prepare a photo which has the same resolution. Name it as `1000.bmp` . Put it under `Camera1` directory. (it support other image format renamed to bmp ). If there isn't a toast message , `1000.bmp` will have nothing to do with replacing capture. +### Download -6. If you need to play video's sound, create `./DCIM/Camera1/Camera1/virtual.mp4` under `Camera1` directory. (Global real-time effective) +- **GitHub Releases**: [Latest Version](https://github.com/w2016561536/android_virtual_cam/releases) +- **China Mainland Mirror (Gitee)**: https://gitee.com/w2016561536/android_virtual_cam -7. If you need to turn off the module temporarily, create `./DCIM/Camera1/Camera1/virtual.mp4` under `Camera1` directory. (Global real-time effective) +### Installation Steps -8. If you find toast messages annoying, you can create a `no_toast.jpg` file in the `/[INTERNEL_STORAGE]/DCIM/Camera1/` directory. (Global real-time effective) +1. Download the latest APK file +2. Install the APK on your Android device +3. Enable the module in Xposed framework +4. Reboot device to activate the module -9. The directory redirection message is displayed only once by default. If you miss the toast message of directory redirection, you can create a `force_show.jpg` file in the `/[INTERNEL_STORAGE]/DCIM/Camera1/` directory to override the default setting. (Global real-time effective) +## 📖 Usage -10. If you need to allocate videos for each application, you can create `private_dir.jpg` in the `/[INTERNEL_STORAGE]/DCIM/Camera1/` directory to enforce apps use private directory. (Global real-time effective) +### Basic Configuration -> Note: the configuration of 6 ~ 10 are in the application. You can quickly configure them in the application or create files manually. +1. **Enable Module** + - Enable this module in Xposed framework (such as LSPosed) + - For scoped frameworks like LSPosed, select the target app, **no need to select System Framework** + - Reboot target app or device -## FAQ +2. **Grant Permissions** + - In system settings, grant storage read permission to target app + - Force stop the target app + - If the app doesn't request this permission, see step 3 -Q1. The problems of front camera? -A1. In most cases , the video for replacing front camera need to be flipped horizontally and rotated right 90 degrees. The video's resolution **after being processed** need to same with that in toast message. But in some came, it doesn't need to make adjustment, so you need to judge it according to situation. +3. **Determine Working Directory** + - Open target app; if it lacks storage permission, a toast message will appear + - Private directory mode: `/[Internal Storage]/Android/data/[package name]/files/Camera1/` + - Shared directory mode: `/[Internal Storage]/DCIM/Camera1/` + - Create the directory manually if it doesn't exist + + > 💡 **Tip**: `Camera1` in private directory only affects that specific app -Q2. Black screen ? Open camera fail ? -A2. Till now ,there are a few apps that can't be hooked, especially the system camera. Or it caused by wrong `Camera1` directory(Whether two levels of Camera1 directory were created, like `./DCIM/Camera1/Camera1/virtual.mp4`, only one level is needed). +### Video and Photo Configuration -Q3. Blurred screen? -A3. The resolution of video is wrong. +4. **Prepare Replacement Video** + - Open camera preview in target app + - Check the resolution (width × height) shown in toast message + - Create a replacement video matching this resolution + - Name the video as `virtual.mp4` and place it in `Camera1` directory + - If no toast message appears, no need to adjust video resolution -Q4. Distorted picture? -A4. Please use the video editing software to modify the original video to match the screen. +5. **Prepare Replacement Photo (Optional)** + - When taking photos in target app, if real photo shows and toast message appears with "发现拍照" + - Check the photo resolution in toast message + - Prepare an image with the same resolution, name it as `1000.bmp` + - Place it in `Camera1` directory (supports other formats renamed to bmp) + - If no toast message when taking photos, no need for `1000.bmp` -Q5. `disable.jpg` invalid? -A5. If the application version `<=4.0`, then the control files in the `[INTERNEL_STORAGE]/DCIM/Camera1` directory will take effect for the applications that **have access to storage permissions**, and for the rest of the applications without permission, control files should be created in the **private directory** -If the app version `>=4.1`, it should be created in `[INTERNEL_STORAGE]/DCIM/Camera1` regardless of whether the target app has permissions. +### Function Switch Configuration -## Question report: +This module provides 5 function switches with two configuration methods: -raise it in issues directly. If it is a bug, please attach with Xposed **modules** log. +#### Method 1: Use App Interface (Recommended) -## Credit +Open the VCAM app and configure using visual switches: -Provide hook method: https://github.com/wangwei1237/CameraHook +- **Force Show Permission Warning**: Repeatedly show directory redirection toast +- **Temporarily Disable Module**: Temporarily stop video replacement +- **Play Video Sound**: Play audio from replacement video +- **Force Private Directory**: Separate video for each app +- **Disable Toast Messages**: Turn off all toast notifications -H.264 hardware decode: https://github.com/zhantong/Android-VideoToImages +#### Method 2: Manual File Creation -JPEG-YUV convert: https://blog.csdn.net/jacke121/article/details/73888732 +Create corresponding files in `/[Internal Storage]/DCIM/Camera1/` directory: + +| Function | Filename | Description | +|----------|----------|-------------| +| Play video sound | `no-silent.jpg` | Enable video audio playback | +| Temporarily disable | `disable.jpg` | Temporarily stop replacement | +| Disable toast | `no_toast.jpg` | Turn off toast notifications | +| Force show warning | `force_show.jpg` | Repeatedly show redirection message | +| Force private dir | `private_dir.jpg` | Force all apps to use private directory | + +> 💡 **Tip**: All configuration switches take effect globally in real-time without restart + +## ❓ FAQ + +### Q1. Front camera orientation issue? +**A1.** In most cases, videos for replacing front camera need to be flipped horizontally and rotated 90 degrees clockwise, and the video resolution ***after processing*** should match the resolution in toast message. But sometimes this is not needed; please judge according to actual situation. + +### Q2. Black screen or camera fails to start? +**A2.** Currently some apps cannot be hooked successfully (especially system camera). Or it's due to wrong video path (whether two levels of Camera1 directory were created, like `./DCIM/Camera1/Camera1/virtual.mp4`, but only one level is needed). + +### Q3. Blurred screen? +**A3.** Wrong video resolution. + +### Q4. Distorted or deformed picture? +**A4.** Please use video editing software to modify the original video to match the screen. + +### Q5. Creating `disable.jpg` doesn't work? +**A5.** +- If app version `<=4.0`, files in `[Internal Storage]/DCIM/Camera1` directory work for apps **with storage access permission**; apps without permission should create in **private directory** +- If app version `>=4.1`, should create in `[Internal Storage]/DCIM/Camera1` regardless of whether target app has permission + +## 🔧 Development & Build + +### Version Info + +- **Current Version**: v4.4 (versionCode 28) +- **Compile SDK**: Android 12 (API 31) +- **Minimum Support**: Android 5.0 (API 21) +- **Target SDK**: Android 8.0 (API 26) + +### Automated Build + +This project uses GitHub Actions for automated building and releasing: + +- **Continuous Integration**: Automatic compilation verification on every code push +- **Auto Release**: Automatically build signed APK and publish Release when creating new tags +- **Workflow Config**: See [.github/workflows/android-build.yml](.github/workflows/android-build.yml) +- **Detailed Documentation**: See [WORKFLOW_CHANGES.md](WORKFLOW_CHANGES.md) + +### Local Build + +```bash +# Clone repository +git clone https://github.com/w2016561536/android_virtual_cam.git +cd android_virtual_cam + +# Build debug version +./gradlew assembleDebug + +# Build release version (requires signing configuration) +./gradlew assembleRelease +``` + +## 🐛 Report Issues + +Please report directly in [Issues](https://github.com/w2016561536/android_virtual_cam/issues). + +For bug reports, please include: +- Xposed **module** log information +- Android system version +- Xposed framework type and version +- Target app information +- Steps to reproduce + +## 🙏 Credits + +- HOOK method reference: [CameraHook](https://github.com/wangwei1237/CameraHook) +- H264 hardware decode: [Android-VideoToImages](https://github.com/zhantong/Android-VideoToImages) +- JPEG to YUV conversion: [Blog post](https://blog.csdn.net/jacke121/article/details/73888732) + +## 📜 License + +This project is for learning and research purposes only. Do not use for any illegal activities. + +## ⭐ Support + +If this project helps you, please give it a Star ⭐️ diff --git a/README_tc.md b/README_tc.md index a8d86c2..283d01d 100644 --- a/README_tc.md +++ b/README_tc.md @@ -2,69 +2,187 @@ [简体中文](./README.md) | [繁體中文](./README_tc.md) | [English](./README_en.md) -基於Xposed的虛擬攝影機 +[![Android Build](https://github.com/w2016561536/android_virtual_cam/workflows/Android%20Build/badge.svg)](https://github.com/w2016561536/android_virtual_cam/actions) +[![Version](https://img.shields.io/badge/version-4.4-blue.svg)](https://github.com/w2016561536/android_virtual_cam/releases) +[![Platform](https://img.shields.io/badge/platform-Android%205.0%2B-green.svg)](https://github.com/w2016561536/android_virtual_cam) -# 請勿用於任何非法用途,所有後果自負。 +基於Xposed的虛擬攝影機模組,可將Android相機預覽和拍照替換為自訂影片和圖片。 -## 支援平臺: +## ⚠️ 免責聲明 -- 安卓5.0+ +**請勿用於任何非法用途,所有後果自負。** -## 使用方法 +本工具僅供學習和研究使用,請勿用於任何違法違規行為。使用者需承擔使用本工具產生的一切後果和責任。 -1. 安裝此模組,並在Xposed中啟用此模組,Lsposed等包含作用域的框架需要選擇目標app,無需選擇系統框架。 +## 🌟 主要特性 -2. 在系統設定中,授予目標應用讀取本機儲存的許可權,並強制結束目標應用程式。 若應用程式未申請此許可權,請見步驟3。 +- ✅ **雙API支援**:支援Camera1和Camera2 API +- ✅ **影片替換**:將相機預覽替換為自訂MP4影片 +- ✅ **照片替換**:將拍照結果替換為自訂BMP圖片 +- ✅ **音訊播放**:可選擇播放影片音訊 +- ✅ **應用程式配置介面**:5個可視化開關,無需手動建立檔案 +- ✅ **私有目錄支援**:支援為每個應用程式單獨配置影片 +- ✅ **即時生效**:配置檔案修改後立即生效,無需重啟 +- ✅ **Toast訊息控制**:可關閉提示訊息 +- ✅ **自動化建置**:GitHub Actions自動建置和發布 -3. 打開目標應用,若應用未能獲得讀取儲存的許可權,則會以氣泡消息提示,`Camera1`目錄被重新導向至應用程式私有目錄`/[內部儲存]/Android/data/[應用包名]/files/Camera1/`。 若未提示,則預設`Camera1`目錄為`/[內部儲存]/DCIM/Camera1/`。 若目錄不存在,請手動建立。 +## 📱 支援平台 -> 注意:私有目錄下的`Camera1`僅對該應用單獨生效。 +- Android 5.0+ (API 21+) +- 需要Xposed框架(如LSPosed、EdXposed等) -4. 在目標應用中打開相機預覽,會以氣泡消息提示「寬:……高:……」,需要根據此解析度資料製作取代影片,放置於`Camera1`目錄下,並命名為`virtual.mp4`,若打開相機並無提示消息,則無需調整影片解析度。 +## 📦 下載與安裝 -5. 若在目標應用中拍照卻顯示真實圖片,且出現氣泡消息`發現拍照`和解析度,則需根據此解析度資料準備一張照片,命名為`1000.bmp`,放入`Camera1`目錄下(支援其它格式改尾碼為bmp)。 如果拍照時無氣泡消息提示,則`1000.bmp`無效。 +### 下載位址 -6. 如果需要播放影片的聲音,需在`/[內部儲存]/DCIM/Camera1/`目錄下建立`no-silent.jpg`檔案。(全域即時生效) +- **GitHub Releases**:[最新版本](https://github.com/w2016561536/android_virtual_cam/releases) +- **中國大陸加速(Gitee)**:https://gitee.com/w2016561536/android_virtual_cam -7. 如果需要臨時停用影片取代,需在`/[內部儲存]/DCIM/Camera1/`目錄下建立`disable.jpg`檔案。(全域即時生效) +### 安裝步驟 -8. 如果覺得Toast消息煩,可以在`/[內部儲存]/DCIM/Camera1/`目錄下建立`no_toast.jpg`檔案。 (全域即時生效) +1. 下載最新版本的APK檔案 +2. 在Android裝置上安裝APK +3. 在Xposed框架中啟用此模組 +4. 重啟裝置使模組生效 -9. 目錄重定向消息預設只顯示一次,如果錯過了目錄重定向的Toast消息,可以在`/[內部儲存]/DCIM/Camera1/`目錄下建立`force_show.jpg`檔案來覆蓋預設設定。 (全域即時生效) +## 📖 使用方法 -10.如果需要為每一個應用程式分配影片,可以在`/[內部儲存]/DCIM/Camera1/`目錄下建立`private_dir.jpg`強制使用應用程式私有目錄。 (全域即時生效) +### 基礎配置 ->注意:6~10的配寘開關均在應用程式中,您可以快捷地在應用程式中配寘,也可以手動建立檔案。 +1. **啟用模組** + - 在Xposed框架(如LSPosed)中啟用本模組 + - LSPosed等包含作用域的框架需要選擇目標app,**無需選擇系統框架** + - 重啟目標應用程式或裝置 +2. **授予許可權** + - 在系統設定中,授予目標應用程式讀取本機儲存的許可權 + - 強制結束目標應用程式 + - 若應用程式未申請此許可權,請見步驟3 -## 常見問題 +3. **確定工作目錄** + - 打開目標應用程式,若應用程式未能獲得讀取儲存的許可權,會以Toast訊息提示 + - 私有目錄模式:`/[內部儲存]/Android/data/[應用程式套件名稱]/files/Camera1/` + - 共用目錄模式:`/[內部儲存]/DCIM/Camera1/` + - 若目錄不存在,請手動建立 + + > 💡 **提示**:私有目錄下的`Camera1`僅對該應用程式單獨生效 -A1. 前置攝影機方向問題? -Q1.大多數情況下,取代前置攝影機的影片需要水準翻轉並右旋90度,並且影片***處理後***的解析度應與氣泡消息內解析度相同。 但有時這並不需要,具體請根據實際情況判斷。 +### 影片和照片配置 -Q2. 畫面黑屏,相機啟動失敗? -A2. 現時有些應用並不能成功取代(特別是系統相機)。 或者是因為影片路徑不對(是否建立了兩級Camera1目錄,如`./DCIM/Camera1/Camera1/virtual.mp4`,但只需要一級目錄)。 +4. **準備替換影片** + - 在目標應用程式中打開相機預覽 + - 檢視Toast訊息顯示的解析度(寬×高) + - 根據此解析度製作替換影片 + - 將影片命名為`virtual.mp4`,放置於`Camera1`目錄下 + - 若無提示訊息,則無需調整影片解析度 -Q3. 畫面花屏? -A3. 影片解析度不對。 +5. **準備替換照片(可選)** + - 在目標應用程式中拍照時,若顯示真實圖片且有Toast提示`發現拍照` + - 檢視Toast訊息顯示的照片解析度 + - 準備一張相同解析度的照片,命名為`1000.bmp` + - 放入`Camera1`目錄下(支援其它格式改尾碼為bmp) + - 如果拍照時無Toast提示,則無需準備`1000.bmp` -Q4. 畫面扭曲,變形? -A4. 請使用剪輯軟體修改原影片來匹配螢幕。 +### 功能開關配置 -Q5. 建立`disable. jpg`無效? -A5. 如果應用版本`<=4.0`,那麼`[內部儲存]/DCIM/Camera1`目錄下的檔案對**具有訪問儲存許可權**的應用生效,其餘無許可權應用應在**私有目錄**下建立 -如果應用版本`>=4.1`,那麼應在`[內部儲存]/DCIM/Camera1`建立,無論目標應用是否具有許可權。 +本模組提供了5個功能開關,有兩種配置方式: +#### 方式一:使用應用程式介面配置(推薦) -## 迴響問題 +打開VCAM應用程式,使用可視化開關進行配置: -請直接在issues中迴響,如果為BUG迴響,請附帶Xposed**模組**日誌資訊。 +- **強制顯示許可權提醒**:重複顯示目錄重新導向的Toast訊息 +- **臨時禁用模組**:臨時停用影片替換功能 +- **播放影片聲音**:播放替換影片的音訊 +- **強制使用私有目錄**:為每個應用程式單獨分配影片 +- **禁用Toast訊息**:關閉所有Toast提示 +#### 方式二:手動建立檔案配置 -##致謝: +在`/[內部儲存]/DCIM/Camera1/`目錄下建立對應檔案: -提供HOOK思路: https://github.com/wangwei1237/CameraHook +| 功能 | 檔案名稱 | 說明 | +|------|---------|------| +| 播放影片聲音 | `no-silent.jpg` | 啟用影片音訊播放 | +| 臨時禁用模組 | `disable.jpg` | 臨時停用影片替換 | +| 禁用Toast訊息 | `no_toast.jpg` | 關閉Toast提示 | +| 強制顯示許可權提醒 | `force_show.jpg` | 重複顯示目錄重新導向訊息 | +| 強制使用私有目錄 | `private_dir.jpg` | 強制所有應用程式使用私有目錄 | -H264硬解碼: https://github.com/zhantong/Android-VideoToImages +> 💡 **提示**:所有配置開關全域即時生效,修改後無需重啟應用程式 -JPEG轉YUV: https://blog.csdn.net/jacke121/article/details/73888732 +## ❓ 常見問題 + +### Q1. 前置攝影機方向問題? +**A1.** 大多數情況下,替換前置攝影機的影片需要水平翻轉並右旋90度,並且影片***處理後***的解析度應與Toast訊息內解析度相同。但有時這並不需要,具體請根據實際情況判斷。 + +### Q2. 畫面黑屏,相機啟動失敗? +**A2.** 目前有些應用程式並不能成功替換(特別是系統相機)。或者是因為影片路徑不對(是否建立了兩級Camera1目錄,如`./DCIM/Camera1/Camera1/virtual.mp4`,但只需要一級目錄)。 + +### Q3. 畫面花屏? +**A3.** 影片解析度不對。 + +### Q4. 畫面扭曲、變形? +**A4.** 請使用剪輯軟體修改原影片來匹配螢幕。 + +### Q5. 建立`disable.jpg`無效? +**A5.** +- 如果應用程式版本`<=4.0`,那麼`[內部儲存]/DCIM/Camera1`目錄下的檔案對**具有存取儲存許可權**的應用程式生效,其餘無許可權應用程式應在**私有目錄**下建立 +- 如果應用程式版本`>=4.1`,那麼應在`[內部儲存]/DCIM/Camera1`建立,無論目標應用程式是否具有許可權 + +## 🔧 開發與建置 + +### 版本資訊 + +- **當前版本**:v4.4 (versionCode 28) +- **編譯SDK**:Android 12 (API 31) +- **最低支援**:Android 5.0 (API 21) +- **目標SDK**:Android 8.0 (API 26) + +### 自動化建置 + +本專案使用GitHub Actions實現自動化建置和發布: + +- **持續整合**:每次程式碼推送都會自動編譯驗證 +- **自動發布**:建立新標籤(tag)時自動建置簽名APK並發布Release +- **工作流配置**:參見 [.github/workflows/android-build.yml](.github/workflows/android-build.yml) +- **詳細說明**:參見 [WORKFLOW_CHANGES.md](WORKFLOW_CHANGES.md) + +### 本機建置 + +```bash +# 複製儲存庫 +git clone https://github.com/w2016561536/android_virtual_cam.git +cd android_virtual_cam + +# 建置除錯版本 +./gradlew assembleDebug + +# 建置發布版本(需要簽名配置) +./gradlew assembleRelease +``` + +## 🐛 回饋問題 + +請直接在[Issues](https://github.com/w2016561536/android_virtual_cam/issues)中回饋。 + +如果是BUG回饋,請附帶以下資訊: +- Xposed**模組**日誌資訊 +- Android系統版本 +- Xposed框架類型和版本 +- 目標應用程式資訊 +- 復現步驟 + +## 🙏 致謝 + +- 提供HOOK思路:[CameraHook](https://github.com/wangwei1237/CameraHook) +- H264硬解碼:[Android-VideoToImages](https://github.com/zhantong/Android-VideoToImages) +- JPEG轉YUV:[部落格文章](https://blog.csdn.net/jacke121/article/details/73888732) + +## 📜 授權 + +本專案僅供學習和研究使用,請勿用於任何違法違規用途。 + +## ⭐ 支援專案 + +如果這個專案對你有幫助,請給個Star ⭐️ From 4e5a313a67c943e5e2b388edf7ff0cc863e04a62 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 04:55:57 +0000 Subject: [PATCH 3/3] Remove backup file --- README.md.backup | 106 ----------------------------------------------- 1 file changed, 106 deletions(-) delete mode 100644 README.md.backup diff --git a/README.md.backup b/README.md.backup deleted file mode 100644 index caa3bdc..0000000 --- a/README.md.backup +++ /dev/null @@ -1,106 +0,0 @@ -# android_virtual_cam - -[简体中文](./README.md) | [繁體中文](./README_tc.md) | [English](./README_en.md) - -[![Android Build](https://github.com/w2016561536/android_virtual_cam/workflows/Android%20Build/badge.svg)](https://github.com/w2016561536/android_virtual_cam/actions) -[![Version](https://img.shields.io/badge/version-4.4-blue.svg)](https://github.com/w2016561536/android_virtual_cam/releases) -[![Platform](https://img.shields.io/badge/platform-Android%205.0%2B-green.svg)](https://github.com/w2016561536/android_virtual_cam) - -基于Xposed的虚拟摄像头模块,可将Android相机预览和拍照替换为自定义视频和图片。 - -## ⚠️ 免责声明 - -**请勿用于任何非法用途,所有后果自负。** - -本工具仅供学习和研究使用,请勿用于任何违法违规行为。使用者需承担使用本工具产生的一切后果和责任。 - -## 🌟 主要特性 - -- ✅ **双API支持**:支持Camera1和Camera2 API -- ✅ **视频替换**:将相机预览替换为自定义MP4视频 -- ✅ **照片替换**:将拍照结果替换为自定义BMP图片 -- ✅ **音频播放**:可选择播放视频音频 -- ✅ **应用配置界面**:5个可视化开关,无需手动创建文件 -- ✅ **私有目录支持**:支持为每个应用单独配置视频 -- ✅ **实时生效**:配置文件修改后立即生效,无需重启 -- ✅ **Toast消息控制**:可关闭提示消息 -- ✅ **自动化构建**:GitHub Actions自动构建和发布 - -## 📱 支持平台 - -- Android 5.0+ (API 21+) -- 需要Xposed框架(如LSPosed、EdXposed等) - -## 📦 下载与安装 - -### 下载地址 - -- **GitHub Releases**:[最新版本](https://github.com/w2016561536/android_virtual_cam/releases) -- **中国大陆加速(Gitee)**:https://gitee.com/w2016561536/android_virtual_cam - -### 安装步骤 - -1. 下载最新版本的APK文件 -2. 在Android设备上安装APK -3. 在Xposed框架中启用此模块 -4. 重启设备使模块生效 - -## 使用方法 - -1. 安装此模块,并在Xposed中启用此模块,Lsposed等包含作用域的框架需要选择目标app,无需选择系统框架。 - -2. 在系统设置中,授予目标应用读取本地存储的权限,并强制结束目标应用程序。若应用程序未申请此权限,请见步骤3。 - -3. 打开目标应用,若应用未能获得读取存储的权限,则会以气泡消息提示,`Camera1`目录被重定向至应用程序私有目录`/[内部存储]/Android/data/[应用包名]/files/Camera1/`。若未提示,则默认`Camera1`目录为`/[内部存储]/DCIM/Camera1/`。若目录不存在,请手动创建。 - -> 注意:私有目录下的`Camera1`仅对该应用单独生效。 - -4. 在目标应用中打开相机预览,会以气泡消息提示“宽:……高:……”,需要根据此分辨率数据制作替换视频,放置于`Camera1`目录下,并命名为`virtual.mp4`,若打开相机并无提示消息,则无需调整视频分辨率。 - -5. 若在目标应用中拍照却显示真实图片,且出现气泡消息`发现拍照`和分辨率,则需根据此分辨率数据准备一张照片,命名为`1000.bmp`,放入`Camera1`目录下(支持其它格式改后缀为bmp)。如果拍照时无气泡消息提示,则`1000.bmp`无效。 - -6. 如果需要播放视频的声音,需在`/[内部存储]/DCIM/Camera1/`目录下创建`no-silent.jpg`文件。(全局实时生效) - -7. 如果需要临时停用视频替换,需在`/[内部存储]/DCIM/Camera1/`目录下创建`disable.jpg`文件。(全局实时生效) - -8. 如果觉得Toast消息烦,可以在`/[内部存储]/DCIM/Camera1/`目录下创建`no_toast.jpg`文件。(全局实时生效) - -9. 目录重定向消息默认只显示一次,如果错过了目录重定向的Toast消息,可以在`/[内部存储]/DCIM/Camera1/`目录下创建`force_show.jpg`文件来覆盖默认设定。(全局实时生效) - -10. 如果需要为每一个应用程序分配视频,可以在`/[内部存储]/DCIM/Camera1/`目录下创建`private_dir.jpg`强制使用应用程序私有目录。(全局实时生效) - -> 注意:6~10的配置开关均在应用程序中,您可以快捷地在应用程序中配置,也可以手动创建文件。 - -## 常见问题 - -A1. 前置摄像头方向问题? -Q1. 大多数情况下,替换前置摄像头的视频需要水平翻转并右旋90度,并且视频***处理后***的分辨率应与气泡消息内分辨率相同。但有时这并不需要,具体请根据实际情况判断。 - - -Q2. 画面黑屏,相机启动失败? -A2. 目前有些应用并不能成功替换(特别是系统相机)。或者是因为视频路径不对(是否创建了两级Camera1目录,如`./DCIM/Camera1/Camera1/virtual.mp4`,但只需要一级目录)。 - - -Q3. 画面花屏? -A3. 视频分辨率不对。 - -Q4. 画面扭曲,变形? -A4. 请使用剪辑软件修改原视频来匹配屏幕。 - -Q5. 创建`disable.jpg`无效? -A5. 如果应用版本`<=4.0`,那么`[内部存储]/DCIM/Camera1`目录下的文件对**具有访问存储权限**的应用生效,其余无权限应用应在**私有目录**下创建 -如果应用版本`>=4.1`,那么应在`[内部存储]/DCIM/Camera1`创建,无论目标应用是否具有权限。 - - -## 反馈问题 - -请直接在issues中反馈,如果为BUG反馈,请附带Xposed**模块**日志信息。 - - -## 致谢: - -提供HOOK思路: https://github.com/wangwei1237/CameraHook - -H264硬解码: https://github.com/zhantong/Android-VideoToImages - -JPEG转YUV: https://blog.csdn.net/jacke121/article/details/73888732