mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-28 20:06:14 +08:00
Compare commits
14 Commits
sdl3
...
win-virtua
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
288bb96e0c | ||
|
|
5fed09c1aa | ||
|
|
8e499772f9 | ||
|
|
0da812e7e9 | ||
|
|
3d67b6e9d6 | ||
|
|
506b2893c6 | ||
|
|
56abe9e690 | ||
|
|
ab13fa582d | ||
|
|
b9e8192eee | ||
|
|
5590aaeb1e | ||
|
|
adfe14809f | ||
|
|
a21dbc8d69 | ||
|
|
b9c70f54d3 | ||
|
|
9cd617d078 |
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.h linguist-language=C++
|
||||
*.cpp linguist-language=C++
|
||||
*.lua linguist-language=Xmake
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Build and Release CrossDesk
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -140,7 +140,7 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x64
|
||||
runner: macos-13
|
||||
runner: macos-15-intel
|
||||
cache-key: intel
|
||||
out-dir: ./build/macosx/x86_64/release/crossdesk
|
||||
package_script: ./scripts/macosx/pkg_x64.sh
|
||||
42
.github/workflows/close-issue.yml
vendored
Normal file
42
.github/workflows/close-issue.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Close Inactive Issues
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# run every day at midnight
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
close_inactive_issues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check inactive issues and close them
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const { data: issues } = await github.rest.issues.listForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
});
|
||||
|
||||
const now = new Date().getTime();
|
||||
const inactivePeriod = 7 * 24 * 60 * 60 * 1000; // 7 days
|
||||
|
||||
for (const issue of issues) {
|
||||
const lastUpdated = new Date(issue.updated_at).getTime();
|
||||
|
||||
// if the issue hasn't been updated in the past week, close it
|
||||
if (now - lastUpdated > inactivePeriod && issue.labels.length === 0) {
|
||||
console.log(`Closing inactive issue: ${issue.number} (No labels)`);
|
||||
await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
state: 'closed',
|
||||
});
|
||||
} else if (issue.labels.length === 0) {
|
||||
console.log(`Skipping issue ${issue.number} (No labels) as it has been recently updated.`);
|
||||
} else {
|
||||
console.log(`Skipping issue ${issue.number} (Has labels).`);
|
||||
}
|
||||
}
|
||||
219
README.md
219
README.md
@@ -1,9 +1,15 @@
|
||||
# CrossDesk
|
||||
|
||||
#### 跨界连接,高效如一
|
||||
[]()
|
||||
[](https://www.gnu.org/licenses/lgpl-3.0)
|
||||
[](https://github.com/kunkundi/crossdesk/commits/self-hosted-server)
|
||||
[](https://github.com/kunkundi/crossdesk/actions)
|
||||
[](https://hub.docker.com/r/crossdesk/crossdesk-server/tags)
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
|
||||
----
|
||||
[English](README_EN.md) / [中文](README.md)
|
||||
[ [English](README_EN.md) / 中文 ]
|
||||
|
||||

|
||||
|
||||
@@ -49,19 +55,7 @@ git submodule init
|
||||
|
||||
git submodule update
|
||||
|
||||
xmake b crossdesk
|
||||
```
|
||||
#### 无 CUDA 环境下的开发支持
|
||||
|
||||
对于未安装 **CUDA 环境** 的Linux开发者,这里提供了预配置的 [Ubuntu 22.04 Docker 镜像](https://hub.docker.com/r/crossdesk/ubuntu22.04)。
|
||||
该镜像内置必要的构建依赖,可在容器中开箱即用,无需额外配置即可直接编译项目。
|
||||
|
||||
进入容器,下载工程后执行:
|
||||
```
|
||||
export CUDA_PATH=/usr/local/cuda
|
||||
export XMAKE_GLOBALDIR=/data
|
||||
|
||||
xmake b --root crossdesk
|
||||
xmake b -vy crossdesk
|
||||
```
|
||||
|
||||
运行
|
||||
@@ -69,10 +63,43 @@ xmake b --root crossdesk
|
||||
xmake r crossdesk
|
||||
```
|
||||
|
||||
### 无 CUDA 环境下的开发支持
|
||||
|
||||
对于未安装 **CUDA 环境** 的 Linux 开发者,这里提供了预配置的 [Ubuntu 22.04 Docker 镜像](https://hub.docker.com/r/crossdesk/ubuntu22.04)。该镜像内置必要的构建依赖,可在容器中开箱即用,无需额外配置即可直接编译项目。
|
||||
|
||||
进入容器,下载工程后执行:
|
||||
```
|
||||
export CUDA_PATH=/usr/local/cuda
|
||||
export XMAKE_GLOBALDIR=/data
|
||||
|
||||
xmake b --root -vy crossdesk
|
||||
```
|
||||
|
||||
对于未安装 **CUDA 环境** 的 Windows 开发者,执行下面的命令安装 CUDA 编译环境:
|
||||
```
|
||||
xmake require -vy "cuda 12.6.3"
|
||||
```
|
||||
安装完成后执行:
|
||||
```
|
||||
xmake require --info "cuda 12.6.3"
|
||||
```
|
||||
输出如下:
|
||||
|
||||
<img width="860" height="226" alt="Image" src="https://github.com/user-attachments/assets/999ac365-581a-4b9a-806e-05eb3e4cf44d" />
|
||||
|
||||
根据上述输出获取到 CUDA 的安装目录,即 installdir 指向的位置。将 CUDA_PATH 加入系统环境变量,或在终端中输入:
|
||||
```
|
||||
set CUDA_PATH=path_to_cuda_installdir
|
||||
```
|
||||
重新执行:
|
||||
```
|
||||
xmake b -vy crossdesk
|
||||
```
|
||||
|
||||
#### 注意
|
||||
运行时如果客户端状态栏显示 **未连接服务器**,请先在 [CrossDesk 官方网站](https://www.crossdesk.cn/) 安装客户端,以便在环境中安装所需的证书文件。
|
||||
|
||||
<img width="129" height="60" alt="image" src="https://github.com/user-attachments/assets/1812f7d6-516b-4b4f-8a3d-98bee505cc5a" />
|
||||
<img width="256" height="120" alt="image" src="https://github.com/user-attachments/assets/1812f7d6-516b-4b4f-8a3d-98bee505cc5a" />
|
||||
|
||||
## 关于 Xmake
|
||||
|
||||
@@ -110,3 +137,161 @@ xmake b -vy crossdesk
|
||||
xmake r -d crossdesk
|
||||
```
|
||||
更多使用方法可参考 [Xmake官方文档](https://xmake.io/guide/quick-start.html) 。
|
||||
|
||||
## 自托管服务器
|
||||
推荐使用Docker部署CrossDesk Server。
|
||||
```
|
||||
sudo docker run -d \
|
||||
--name crossdesk_server \
|
||||
--network host \
|
||||
-e EXTERNAL_IP=xxx.xxx.xxx.xxx \
|
||||
-e INTERNAL_IP=xxx.xxx.xxx.xxx \
|
||||
-e CROSSDESK_SERVER_PORT=9099 \
|
||||
-v /path/to/your/certs:/crossdesk-server/certs \
|
||||
-v /path/to/your/db:/crossdesk-server/db \
|
||||
-v /path/to/your/logs:/crossdesk-server/logs \
|
||||
crossdesk/crossdesk-server:latest
|
||||
```
|
||||
|
||||
上述命令中,用户需注意的参数如下:
|
||||
|
||||
- EXTERNAL_IP:服务器公网 IP , 对应 CrossDesk 客户端**自托管服务器配置**中填写的**服务器地址**
|
||||
|
||||
- INTERNAL_IP:服务器内网 IP
|
||||
|
||||
- CROSSDESK_SERVER_PORT:自托管服务使用的端口,对应 CrossDesk 客户端**自托管服务器配置**中填写的**服务器端口**
|
||||
|
||||
- /path/to/your/certs:证书文件目录
|
||||
|
||||
- /path/to/your/db:CrossDesk Server 设备管理数据库
|
||||
|
||||
- /path/to/your/logs:日志目录
|
||||
|
||||
**注意**:
|
||||
- **/path/to/your/ 是示例路径,请替换为你自己的实际路径。挂载的目录必须事先创建好,否则容器会报错。**
|
||||
- **服务器需开放端口:3478/udp,3478/tcp,30000-60000/udp,CROSSDESK_SERVER_PORT/tcp,443/tcp。**
|
||||
|
||||
## 证书文件
|
||||
客户端需加载根证书文件,服务端需加载服务器私钥和服务器证书文件。
|
||||
|
||||
如果已有SSL证书的用户,可以忽略下面的证书生成步骤。
|
||||
|
||||
对于无证书的用户,可使用下面的脚本自行生成证书文件:
|
||||
```
|
||||
# 创建证书生成脚本
|
||||
vim generate_certs.sh
|
||||
```
|
||||
拷贝到脚本中
|
||||
```
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# 检查参数
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <SERVER_IP>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SERVER_IP="$1"
|
||||
|
||||
# 文件名
|
||||
ROOT_KEY="crossdesk.cn_root.key"
|
||||
ROOT_CERT="crossdesk.cn_root.crt"
|
||||
SERVER_KEY="crossdesk.cn.key"
|
||||
SERVER_CSR="crossdesk.cn.csr"
|
||||
SERVER_CERT="crossdesk.cn_bundle.crt"
|
||||
FULLCHAIN_CERT="crossdesk.cn_fullchain.crt"
|
||||
|
||||
# 证书主题
|
||||
SUBJ="/C=CN/ST=Zhejiang/L=Hangzhou/O=CrossDesk/OU=CrossDesk/CN=$SERVER_IP"
|
||||
|
||||
# 1. 生成根证书
|
||||
echo "Generating root private key..."
|
||||
openssl genrsa -out "$ROOT_KEY" 4096
|
||||
|
||||
echo "Generating self-signed root certificate..."
|
||||
openssl req -x509 -new -nodes -key "$ROOT_KEY" -sha256 -days 3650 -out "$ROOT_CERT" -subj "$SUBJ"
|
||||
|
||||
# 2. 生成服务器私钥
|
||||
echo "Generating server private key..."
|
||||
openssl genrsa -out "$SERVER_KEY" 2048
|
||||
|
||||
# 3. 生成服务器 CSR
|
||||
echo "Generating server CSR..."
|
||||
openssl req -new -key "$SERVER_KEY" -out "$SERVER_CSR" -subj "$SUBJ"
|
||||
|
||||
# 4. 生成临时 OpenSSL 配置文件,加入 SAN
|
||||
SAN_CONF="san.cnf"
|
||||
cat > $SAN_CONF <<EOL
|
||||
[ req ]
|
||||
default_bits = 2048
|
||||
distinguished_name = req_distinguished_name
|
||||
req_extensions = req_ext
|
||||
prompt = no
|
||||
|
||||
[ req_distinguished_name ]
|
||||
C = CN
|
||||
ST = Zhejiang
|
||||
L = Hangzhou
|
||||
O = CrossDesk
|
||||
OU = CrossDesk
|
||||
CN = $SERVER_IP
|
||||
|
||||
[ req_ext ]
|
||||
subjectAltName = IP:$SERVER_IP
|
||||
EOL
|
||||
|
||||
# 5. 用根证书签发服务器证书(包含 SAN)
|
||||
echo "Signing server certificate with root certificate..."
|
||||
openssl x509 -req -in "$SERVER_CSR" -CA "$ROOT_CERT" -CAkey "$ROOT_KEY" -CAcreateserial \
|
||||
-out "$SERVER_CERT" -days 3650 -sha256 -extfile "$SAN_CONF" -extensions req_ext
|
||||
|
||||
# 6. 生成完整链证书
|
||||
cat "$SERVER_CERT" "$ROOT_CERT" > "$FULLCHAIN_CERT"
|
||||
|
||||
# 7. 清理中间文件
|
||||
rm -f "$ROOT_CERT.srl" "$SAN_CONF" "$ROOT_KEY" "$SERVER_CSR" "FULLCHAIN_CERT"
|
||||
|
||||
echo "Generation complete. Deployment files:"
|
||||
echo " Client root certificate: $ROOT_CERT"
|
||||
echo " Server private key: $SERVER_KEY"
|
||||
echo " Server certificate: $SERVER_CERT"
|
||||
```
|
||||
执行
|
||||
```
|
||||
chmod +x generate_certs.sh
|
||||
./generate_certs.sh 服务器公网IP
|
||||
|
||||
# 例如 ./generate_certs.sh 111.111.111.111
|
||||
```
|
||||
输出如下:
|
||||
```
|
||||
Generating root private key...
|
||||
Generating self-signed root certificate...
|
||||
Generating server private key...
|
||||
Generating server CSR...
|
||||
Signing server certificate with root certificate...
|
||||
Certificate request self-signature ok
|
||||
subject=C = CN, ST = Zhejiang, L = Hangzhou, O = CrossDesk, OU = CrossDesk, CN = xxx.xxx.xxx.xxx
|
||||
cleaning up intermediate files...
|
||||
Generation complete. Deployment files::
|
||||
Client root certificate:: crossdesk.cn_root.crt
|
||||
Server private key: crossdesk.cn.key
|
||||
Server certificate: crossdesk.cn_bundle.crt
|
||||
```
|
||||
|
||||
#### 服务端
|
||||
将 **crossdesk.cn.key** 和 **crossdesk.cn_bundle.crt** 放置到 **/path/to/your/certs** 目录下。
|
||||
|
||||
#### 客户端
|
||||
1. 点击右上角设置进入设置页面。<br>
|
||||
<img width="600" height="210" alt="image" src="https://github.com/user-attachments/assets/6431131d-b32a-4726-8783-6788f47baa3b" /><br><br>
|
||||
|
||||
3. 点击点击**自托管服务器配置**。<br><br>
|
||||
<img width="600" height="160" alt="image" src="https://github.com/user-attachments/assets/24c761a3-1985-4d7e-84be-787383c2afb8" /><br><br>
|
||||
|
||||
5. 在**证书文件路径**选择框中找到 **crossdesk.cn_root.crt** 的存放路径,选中 **crossdesk.cn_root.crt**,点击确认。<br><br>
|
||||
<img width="600" height="220" alt="image" src="https://github.com/user-attachments/assets/4af7cd3a-c72e-44fb-b032-30e050019c2a" /><br><br>
|
||||
|
||||
7. 勾选使用**自托管服务器配置**,点击确认配置生效。<br><br>
|
||||
<img width="600" height="160" alt="image" src="https://github.com/user-attachments/assets/1e455dc3-4087-4f37-a544-1ff9f8789383" /><br><br>
|
||||
|
||||
228
README_EN.md
228
README_EN.md
@@ -1,9 +1,15 @@
|
||||
# CrossDesk
|
||||
|
||||
#### Bridging work, uniting efficiency
|
||||
[]()
|
||||
[](https://www.gnu.org/licenses/lgpl-3.0)
|
||||
[](https://github.com/kunkundi/crossdesk/commits/self-hosted-server)
|
||||
[](https://github.com/kunkundi/crossdesk/actions)
|
||||
[](https://hub.docker.com/r/crossdesk/crossdesk-server/tags)
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
|
||||
----
|
||||
[中文](README.md) / [English](README_EN.md)
|
||||
[ [中文](README.md) / English ]
|
||||
|
||||

|
||||
|
||||
@@ -17,15 +23,15 @@ CrossDesk is an experimental application of [MiniRTC](https://github.com/kunkund
|
||||
|
||||
Enter the remote desktop ID in the menu bar’s “Remote ID” field and click “→” to initiate a remote connection.
|
||||
|
||||

|
||||

|
||||
|
||||
If the remote desktop requires a connection password, you must enter the correct password on your side to successfully establish the connection.
|
||||
|
||||

|
||||

|
||||
|
||||
Before connecting, you can customize configuration options in the settings, such as language and video encoding format.
|
||||
|
||||

|
||||

|
||||
|
||||
## How to build
|
||||
|
||||
@@ -49,20 +55,7 @@ git submodule init
|
||||
|
||||
git submodule update
|
||||
|
||||
xmake b crossdesk
|
||||
```
|
||||
|
||||
#### Development Without CUDA Environment
|
||||
|
||||
For developers who do not have a **CUDA environment** installed, a preconfigured [Ubuntu 22.04 Docker image](https://hub.docker.com/r/crossdesk/ubuntu22.04) is provided.
|
||||
This image comes with all required build dependencies and allows you to build the project directly inside the container without any additional setup.
|
||||
|
||||
After entering the container, download the project and run:
|
||||
```
|
||||
export CUDA_PATH=/usr/local/cuda
|
||||
export XMAKE_GLOBALDIR=/data
|
||||
|
||||
xmake b --root crossdesk
|
||||
xmake b -vy crossdesk
|
||||
```
|
||||
|
||||
Run:
|
||||
@@ -70,10 +63,45 @@ Run:
|
||||
xmake r crossdesk
|
||||
```
|
||||
|
||||
#### Development Without CUDA Environment
|
||||
|
||||
For **Linux** developers who do not have a **CUDA environment** installed, a preconfigured [Ubuntu 22.04 Docker image](https://hub.docker.com/r/crossdesk/ubuntu22.04) is provided.
|
||||
This image comes with all required build dependencies and allows you to build the project directly inside the container without any additional setup.
|
||||
|
||||
After entering the container, download the project and run:
|
||||
```
|
||||
export CUDA_PATH=/usr/local/cuda
|
||||
export XMAKE_GLOBALDIR=/data
|
||||
|
||||
xmake b --root -vy crossdesk
|
||||
```
|
||||
|
||||
For **Windows** developers without a **CUDA environment** installed, run the following command to install the CUDA build environment:
|
||||
```
|
||||
xmake require -vy "cuda 12.6.3"
|
||||
```
|
||||
After the installation is complete, execute:
|
||||
```
|
||||
xmake require --info "cuda 12.6.3"
|
||||
```
|
||||
The output will look like this:
|
||||
|
||||
<img width="860" height="226" alt="Image" src="https://github.com/user-attachments/assets/999ac365-581a-4b9a-806e-05eb3e4cf44d" />
|
||||
|
||||
From the output above, locate the CUDA installation directory — this is the path pointed to by installdir.
|
||||
Add this path to your system environment variable CUDA_PATH, or set it in the terminal using:
|
||||
```
|
||||
set CUDA_PATH=path_to_cuda_installdir:
|
||||
```
|
||||
Then re-run:
|
||||
```
|
||||
xmake b -vy crossdesk
|
||||
```
|
||||
|
||||
#### Notice
|
||||
If the client status bar shows **Disconnected** during runtime, please first install the client from the [CrossDesk official website](https://www.crossdesk.cn/) to ensure the required certificate files are available in the environment.
|
||||
|
||||
<img width="108" height="57" alt="image" src="https://github.com/user-attachments/assets/26e8b9f3-b326-410e-9466-dd073eaf675a" />
|
||||
<img width="256" height="120" alt="image" src="https://github.com/user-attachments/assets/1812f7d6-516b-4b4f-8a3d-98bee505cc5a" />
|
||||
|
||||
## About Xmake
|
||||
#### Installing Xmake
|
||||
@@ -114,3 +142,161 @@ xmake r -d crossdesk
|
||||
```
|
||||
|
||||
For more information, please refer to the [official Xmake documentation](https://xmake.io/guide/quick-start.html) .
|
||||
|
||||
## Self-Hosted Server
|
||||
It is recommended to deploy CrossDesk Server using Docker.
|
||||
```
|
||||
sudo docker run -d \
|
||||
--name crossdesk_server \
|
||||
--network host \
|
||||
-e EXTERNAL_IP=150.158.81.30 \
|
||||
-e INTERNAL_IP=10.0.4.3 \
|
||||
-e CROSSDESK_SERVER_PORT=9099 \
|
||||
-v /path/to/your/certs:/crossdesk-server/certs \
|
||||
-v /path/to/your/db:/crossdesk-server/db \
|
||||
-v /path/to/your/logs:/crossdesk-server/logs \
|
||||
crossdesk/crossdesk-server:latest
|
||||
```
|
||||
|
||||
The parameters you need to pay attention to are as follows:
|
||||
|
||||
- **EXTERNAL_IP**: The server's public IP, corresponding to the **Server Address** in the CrossDesk client **Self-Hosted Server Configuration**.
|
||||
|
||||
- **INTERNAL_IP**: The server's internal IP.
|
||||
|
||||
- **CROSSDESK_SERVER_PORT**: The port used by the self-hosted server, corresponding to the **Server Port** in the CrossDesk client **Self-Hosted Server Configuration**.
|
||||
|
||||
- **/path/to/your/certs**: Directory for certificate files.
|
||||
|
||||
- **/path/to/your/db**: CrossDesk Server device management database.
|
||||
|
||||
- **/path/to/your/logs**: Log directory.
|
||||
|
||||
**Note**:
|
||||
- **/path/to/your/ is an example path; please replace it with your actual path. The mounted directories must be created in advance, otherwise the container will fail.**
|
||||
- **The server must open the following ports: 3478/udp, 3478/tcp, 30000-60000/udp, CROSSDESK_SERVER_PORT/tcp, 443/tcp.**
|
||||
|
||||
## Certificate Files
|
||||
The client needs to load the root certificate, and the server needs to load the server private key and server certificate.
|
||||
|
||||
If you already have an SSL certificate, you can skip the following certificate generation steps.
|
||||
|
||||
For users without a certificate, you can use the script below to generate the certificate files:
|
||||
```
|
||||
# Create certificate generation script
|
||||
vim generate_certs.sh
|
||||
```
|
||||
Copy the following into the script:
|
||||
```
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Check arguments
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <SERVER_IP>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SERVER_IP="$1"
|
||||
|
||||
# Filenames
|
||||
ROOT_KEY="crossdesk.cn_root.key"
|
||||
ROOT_CERT="crossdesk.cn_root.crt"
|
||||
SERVER_KEY="crossdesk.cn.key"
|
||||
SERVER_CSR="crossdesk.cn.csr"
|
||||
SERVER_CERT="crossdesk.cn_bundle.crt"
|
||||
FULLCHAIN_CERT="crossdesk.cn_fullchain.crt"
|
||||
|
||||
# Certificate subject
|
||||
SUBJ="/C=CN/ST=Zhejiang/L=Hangzhou/O=CrossDesk/OU=CrossDesk/CN=$SERVER_IP"
|
||||
|
||||
# 1. Generate root certificate
|
||||
echo "Generating root private key..."
|
||||
openssl genrsa -out "$ROOT_KEY" 4096
|
||||
|
||||
echo "Generating self-signed root certificate..."
|
||||
openssl req -x509 -new -nodes -key "$ROOT_KEY" -sha256 -days 3650 -out "$ROOT_CERT" -subj "$SUBJ"
|
||||
|
||||
# 2. Generate server private key
|
||||
echo "Generating server private key..."
|
||||
openssl genrsa -out "$SERVER_KEY" 2048
|
||||
|
||||
# 3. Generate server CSR
|
||||
echo "Generating server CSR..."
|
||||
openssl req -new -key "$SERVER_KEY" -out "$SERVER_CSR" -subj "$SUBJ"
|
||||
|
||||
# 4. Create temporary OpenSSL config file with SAN
|
||||
SAN_CONF="san.cnf"
|
||||
cat > $SAN_CONF <<EOL
|
||||
[ req ]
|
||||
default_bits = 2048
|
||||
distinguished_name = req_distinguished_name
|
||||
req_extensions = req_ext
|
||||
prompt = no
|
||||
|
||||
[ req_distinguished_name ]
|
||||
C = CN
|
||||
ST = Zhejiang
|
||||
L = Hangzhou
|
||||
O = CrossDesk
|
||||
OU = CrossDesk
|
||||
CN = $SERVER_IP
|
||||
|
||||
[ req_ext ]
|
||||
subjectAltName = IP:$SERVER_IP
|
||||
EOL
|
||||
|
||||
# 5. Sign server certificate with root certificate (including SAN)
|
||||
echo "Signing server certificate with root certificate..."
|
||||
openssl x509 -req -in "$SERVER_CSR" -CA "$ROOT_CERT" -CAkey "$ROOT_KEY" -CAcreateserial \
|
||||
-out "$SERVER_CERT" -days 3650 -sha256 -extfile "$SAN_CONF" -extensions req_ext
|
||||
|
||||
# 6. Generate full chain certificate
|
||||
cat "$SERVER_CERT" "$ROOT_CERT" > "$FULLCHAIN_CERT"
|
||||
|
||||
# 7. Clean up intermediate files
|
||||
rm -f "$ROOT_CERT.srl" "$SAN_CONF" "$ROOT_KEY" "$SERVER_CSR" "FULLCHAIN_CERT"
|
||||
|
||||
echo "Generation complete. Deployment files:"
|
||||
echo " Client root certificate: $ROOT_CERT"
|
||||
echo " Server private key: $SERVER_KEY"
|
||||
echo " Server certificate: $SERVER_CERT"
|
||||
```
|
||||
Execute:
|
||||
```
|
||||
chmod +x generate_certs.sh
|
||||
./generate_certs.sh EXTERNAL_IP
|
||||
|
||||
# example ./generate_certs.sh 111.111.111.111
|
||||
```
|
||||
Expected output:
|
||||
```
|
||||
Generating root private key...
|
||||
Generating self-signed root certificate...
|
||||
Generating server private key...
|
||||
Generating server CSR...
|
||||
Signing server certificate with root certificate...
|
||||
Certificate request self-signature ok
|
||||
subject=C = CN, ST = Zhejiang, L = Hangzhou, O = CrossDesk, OU = CrossDesk, CN = xxx.xxx.xxx.xxx
|
||||
cleaning up intermediate files...
|
||||
Generation complete. Deployment files::
|
||||
Client root certificate:: crossdesk.cn_root.crt
|
||||
Server private key: crossdesk.cn.key
|
||||
Server certificate: crossdesk.cn_bundle.crt
|
||||
```
|
||||
|
||||
#### Server Side
|
||||
Place **crossdesk.cn.key** and **crossdesk.cn_bundle.crt** into the **/path/to/your/certs** directory.
|
||||
|
||||
#### Client Side
|
||||
1. Click the settings icon in the top-right corner to enter the settings page.<br>
|
||||
<img width="600" height="210" alt="image" src="https://github.com/user-attachments/assets/6431131d-b32a-4726-8783-6788f47baa3b" /><br><br>
|
||||
|
||||
2. Click **Self-Hosted Server Configuration**.<br><br>
|
||||
<img width="600" height="160" alt="image" src="https://github.com/user-attachments/assets/24c761a3-1985-4d7e-84be-787383c2afb8" /><br><br>
|
||||
|
||||
3. In the **Certificate File Path** selection, locate and select the **crossdesk.cn_root.crt** file.<br><br>
|
||||
<img width="600" height="220" alt="image" src="https://github.com/user-attachments/assets/4af7cd3a-c72e-44fb-b032-30e050019c2a" /><br><br>
|
||||
|
||||
4. Check the option to use **Self-Hosted Server Configuration**.<br><br>
|
||||
<img width="600" height="160" alt="image" src="https://github.com/user-attachments/assets/1e455dc3-4087-4f37-a544-1ff9f8789383" /><br><br>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
3644
src/config_center/SimpleIni.h
Normal file
3644
src/config_center/SimpleIni.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,64 +1,229 @@
|
||||
#include "config_center.h"
|
||||
|
||||
#include "rd_log.h"
|
||||
|
||||
ConfigCenter::ConfigCenter() {}
|
||||
ConfigCenter::ConfigCenter(const std::string& config_path,
|
||||
const std::string& cert_file_path)
|
||||
: config_path_(config_path),
|
||||
cert_file_path_(cert_file_path),
|
||||
cert_file_path_default_(cert_file_path) {
|
||||
ini_.SetUnicode(true);
|
||||
Load();
|
||||
}
|
||||
|
||||
ConfigCenter::~ConfigCenter() {}
|
||||
|
||||
int ConfigCenter::Load() {
|
||||
SI_Error rc = ini_.LoadFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
Save();
|
||||
return -1;
|
||||
}
|
||||
|
||||
language_ = static_cast<LANGUAGE>(
|
||||
ini_.GetLongValue(section_, "language", static_cast<long>(language_)));
|
||||
|
||||
video_quality_ = static_cast<VIDEO_QUALITY>(ini_.GetLongValue(
|
||||
section_, "video_quality", static_cast<long>(video_quality_)));
|
||||
|
||||
video_frame_rate_ = static_cast<VIDEO_FRAME_RATE>(ini_.GetLongValue(
|
||||
section_, "video_frame_rate", static_cast<long>(video_frame_rate_)));
|
||||
|
||||
video_encode_format_ = static_cast<VIDEO_ENCODE_FORMAT>(
|
||||
ini_.GetLongValue(section_, "video_encode_format",
|
||||
static_cast<long>(video_encode_format_)));
|
||||
|
||||
hardware_video_codec_ = ini_.GetBoolValue(section_, "hardware_video_codec",
|
||||
hardware_video_codec_);
|
||||
|
||||
enable_turn_ = ini_.GetBoolValue(section_, "enable_turn", enable_turn_);
|
||||
enable_srtp_ = ini_.GetBoolValue(section_, "enable_srtp", enable_srtp_);
|
||||
server_host_ = ini_.GetValue(section_, "server_host", server_host_.c_str());
|
||||
server_port_ = static_cast<int>(
|
||||
ini_.GetLongValue(section_, "server_port", server_port_));
|
||||
cert_file_path_ =
|
||||
ini_.GetValue(section_, "cert_file_path", cert_file_path_.c_str());
|
||||
enable_self_hosted_ =
|
||||
ini_.GetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::Save() {
|
||||
ini_.SetLongValue(section_, "language", static_cast<long>(language_));
|
||||
ini_.SetLongValue(section_, "video_quality",
|
||||
static_cast<long>(video_quality_));
|
||||
ini_.SetLongValue(section_, "video_frame_rate",
|
||||
static_cast<long>(video_frame_rate_));
|
||||
ini_.SetLongValue(section_, "video_encode_format",
|
||||
static_cast<long>(video_encode_format_));
|
||||
ini_.SetBoolValue(section_, "hardware_video_codec", hardware_video_codec_);
|
||||
ini_.SetBoolValue(section_, "enable_turn", enable_turn_);
|
||||
ini_.SetBoolValue(section_, "enable_srtp", enable_srtp_);
|
||||
ini_.SetValue(section_, "server_host", server_host_.c_str());
|
||||
ini_.SetLongValue(section_, "server_port", static_cast<long>(server_port_));
|
||||
ini_.SetValue(section_, "cert_file_path", cert_file_path_.c_str());
|
||||
ini_.SetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);
|
||||
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// setters
|
||||
|
||||
int ConfigCenter::SetLanguage(LANGUAGE language) {
|
||||
language_ = language;
|
||||
ini_.SetLongValue(section_, "language", static_cast<long>(language_));
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetVideoQuality(VIDEO_QUALITY video_quality) {
|
||||
video_quality_ = video_quality;
|
||||
ini_.SetLongValue(section_, "video_quality",
|
||||
static_cast<long>(video_quality_));
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetVideoFrameRate(VIDEO_FRAME_RATE video_frame_rate) {
|
||||
video_frame_rate_ = video_frame_rate;
|
||||
ini_.SetLongValue(section_, "video_frame_rate",
|
||||
static_cast<long>(video_frame_rate_));
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetVideoEncodeFormat(
|
||||
VIDEO_ENCODE_FORMAT video_encode_format) {
|
||||
video_encode_format_ = video_encode_format;
|
||||
ini_.SetLongValue(section_, "video_encode_format",
|
||||
static_cast<long>(video_encode_format_));
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetHardwareVideoCodec(bool hardware_video_codec) {
|
||||
hardware_video_codec_ = hardware_video_codec;
|
||||
ini_.SetBoolValue(section_, "hardware_video_codec", hardware_video_codec_);
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetTurn(bool enable_turn) {
|
||||
enable_turn_ = enable_turn;
|
||||
ini_.SetBoolValue(section_, "enable_turn", enable_turn_);
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetSrtp(bool enable_srtp) {
|
||||
enable_srtp_ = enable_srtp;
|
||||
ini_.SetBoolValue(section_, "enable_srtp", enable_srtp_);
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() { return language_; }
|
||||
int ConfigCenter::SetServerHost(const std::string& server_host) {
|
||||
server_host_ = server_host;
|
||||
ini_.SetValue(section_, "server_host", server_host_.c_str());
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ConfigCenter::VIDEO_QUALITY ConfigCenter::GetVideoQuality() {
|
||||
int ConfigCenter::SetServerPort(int server_port) {
|
||||
server_port_ = server_port;
|
||||
ini_.SetLongValue(section_, "server_port", static_cast<long>(server_port_));
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetCertFilePath(const std::string& cert_file_path) {
|
||||
cert_file_path_ = cert_file_path;
|
||||
ini_.SetValue(section_, "cert_file_path", cert_file_path_.c_str());
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetSelfHosted(bool enable_self_hosted) {
|
||||
enable_self_hosted_ = enable_self_hosted;
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// getters
|
||||
|
||||
ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() const { return language_; }
|
||||
|
||||
ConfigCenter::VIDEO_QUALITY ConfigCenter::GetVideoQuality() const {
|
||||
return video_quality_;
|
||||
}
|
||||
|
||||
int ConfigCenter::GetVideoFrameRate() {
|
||||
int fps = video_frame_rate_ == VIDEO_FRAME_RATE::FPS_30 ? 30 : 60;
|
||||
return fps;
|
||||
ConfigCenter::VIDEO_FRAME_RATE ConfigCenter::GetVideoFrameRate() const {
|
||||
return video_frame_rate_;
|
||||
}
|
||||
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT ConfigCenter::GetVideoEncodeFormat() {
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT ConfigCenter::GetVideoEncodeFormat() const {
|
||||
return video_encode_format_;
|
||||
}
|
||||
|
||||
bool ConfigCenter::IsHardwareVideoCodec() { return hardware_video_codec_; }
|
||||
bool ConfigCenter::IsHardwareVideoCodec() const {
|
||||
return hardware_video_codec_;
|
||||
}
|
||||
|
||||
bool ConfigCenter::IsEnableTurn() { return enable_turn_; }
|
||||
bool ConfigCenter::IsEnableTurn() const { return enable_turn_; }
|
||||
|
||||
bool ConfigCenter::IsEnableSrtp() { return enable_srtp_; }
|
||||
bool ConfigCenter::IsEnableSrtp() const { return enable_srtp_; }
|
||||
|
||||
std::string ConfigCenter::GetServerHost() const { return server_host_; }
|
||||
|
||||
int ConfigCenter::GetServerPort() const { return server_port_; }
|
||||
|
||||
std::string ConfigCenter::GetCertFilePath() const { return cert_file_path_; }
|
||||
|
||||
std::string ConfigCenter::GetDefaultServerHost() const {
|
||||
return server_host_default_;
|
||||
}
|
||||
|
||||
int ConfigCenter::GetDefaultServerPort() const { return server_port_default_; }
|
||||
|
||||
std::string ConfigCenter::GetDefaultCertFilePath() const {
|
||||
return cert_file_path_default_;
|
||||
}
|
||||
|
||||
bool ConfigCenter::IsSelfHosted() const { return enable_self_hosted_; }
|
||||
@@ -7,18 +7,24 @@
|
||||
#ifndef _CONFIG_CENTER_H_
|
||||
#define _CONFIG_CENTER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "SimpleIni.h"
|
||||
|
||||
class ConfigCenter {
|
||||
public:
|
||||
enum class LANGUAGE { CHINESE = 0, ENGLISH = 1 };
|
||||
enum class VIDEO_QUALITY { LOW = 0, MEDIUM = 1, HIGH = 2 };
|
||||
enum class VIDEO_FRAME_RATE { FPS_30 = 0, FPS_60 = 1 };
|
||||
enum class VIDEO_ENCODE_FORMAT { AV1 = 0, H264 = 1 };
|
||||
enum class VIDEO_ENCODE_FORMAT { H264 = 0, AV1 = 1 };
|
||||
|
||||
public:
|
||||
ConfigCenter();
|
||||
explicit ConfigCenter(
|
||||
const std::string& config_path = "config.ini",
|
||||
const std::string& cert_file_path = "crossdesk.cn_root.crt");
|
||||
~ConfigCenter();
|
||||
|
||||
public:
|
||||
// write config
|
||||
int SetLanguage(LANGUAGE language);
|
||||
int SetVideoQuality(VIDEO_QUALITY video_quality);
|
||||
int SetVideoFrameRate(VIDEO_FRAME_RATE video_frame_rate);
|
||||
@@ -26,25 +32,50 @@ class ConfigCenter {
|
||||
int SetHardwareVideoCodec(bool hardware_video_codec);
|
||||
int SetTurn(bool enable_turn);
|
||||
int SetSrtp(bool enable_srtp);
|
||||
int SetServerHost(const std::string& server_host);
|
||||
int SetServerPort(int server_port);
|
||||
int SetCertFilePath(const std::string& cert_file_path);
|
||||
int SetSelfHosted(bool enable_self_hosted);
|
||||
|
||||
public:
|
||||
LANGUAGE GetLanguage();
|
||||
VIDEO_QUALITY GetVideoQuality();
|
||||
int GetVideoFrameRate();
|
||||
VIDEO_ENCODE_FORMAT GetVideoEncodeFormat();
|
||||
bool IsHardwareVideoCodec();
|
||||
bool IsEnableTurn();
|
||||
bool IsEnableSrtp();
|
||||
// read config
|
||||
|
||||
LANGUAGE GetLanguage() const;
|
||||
VIDEO_QUALITY GetVideoQuality() const;
|
||||
VIDEO_FRAME_RATE GetVideoFrameRate() const;
|
||||
VIDEO_ENCODE_FORMAT GetVideoEncodeFormat() const;
|
||||
bool IsHardwareVideoCodec() const;
|
||||
bool IsEnableTurn() const;
|
||||
bool IsEnableSrtp() const;
|
||||
std::string GetServerHost() const;
|
||||
int GetServerPort() const;
|
||||
std::string GetCertFilePath() const;
|
||||
std::string GetDefaultServerHost() const;
|
||||
int GetDefaultServerPort() const;
|
||||
std::string GetDefaultCertFilePath() const;
|
||||
bool IsSelfHosted() const;
|
||||
|
||||
int Load();
|
||||
int Save();
|
||||
|
||||
private:
|
||||
// Default value should be same with parameters in localization.h
|
||||
std::string config_path_;
|
||||
std::string cert_file_path_;
|
||||
CSimpleIniA ini_;
|
||||
const char* section_ = "Settings";
|
||||
|
||||
LANGUAGE language_ = LANGUAGE::CHINESE;
|
||||
VIDEO_QUALITY video_quality_ = VIDEO_QUALITY::MEDIUM;
|
||||
VIDEO_FRAME_RATE video_frame_rate_ = VIDEO_FRAME_RATE::FPS_30;
|
||||
VIDEO_ENCODE_FORMAT video_encode_format_ = VIDEO_ENCODE_FORMAT::AV1;
|
||||
VIDEO_ENCODE_FORMAT video_encode_format_ = VIDEO_ENCODE_FORMAT::H264;
|
||||
bool hardware_video_codec_ = false;
|
||||
bool enable_turn_ = false;
|
||||
bool enable_srtp_ = false;
|
||||
std::string server_host_ = "api.crossdesk.cn";
|
||||
int server_port_ = 9099;
|
||||
std::string server_host_default_ = "api.crossdesk.cn";
|
||||
int server_port_default_ = 9099;
|
||||
std::string cert_file_path_default_ = "";
|
||||
bool enable_self_hosted_ = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
2710
src/gui/assets/fonts/OPPOSans_Regular.h
Normal file
2710
src/gui/assets/fonts/OPPOSans_Regular.h
Normal file
File diff suppressed because it is too large
Load Diff
56
src/gui/assets/layouts/layout.h
Normal file
56
src/gui/assets/layouts/layout.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-06-14
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LAYOUT_STYLE_H_
|
||||
#define _LAYOUT_STYLE_H_
|
||||
|
||||
#define MENU_WINDOW_WIDTH_CN 300
|
||||
#define MENU_WINDOW_HEIGHT_CN 280
|
||||
#define LOCAL_WINDOW_WIDTH_CN 300
|
||||
#define LOCAL_WINDOW_HEIGHT_CN 280
|
||||
#define REMOTE_WINDOW_WIDTH_CN 300
|
||||
#define REMOTE_WINDOW_HEIGHT_CN 280
|
||||
#define MENU_WINDOW_WIDTH_EN 190
|
||||
#define MENU_WINDOW_HEIGHT_EN 245
|
||||
#define IPUT_WINDOW_WIDTH 160
|
||||
#define INPUT_WINDOW_PADDING_CN 66
|
||||
#define INPUT_WINDOW_PADDING_EN 96
|
||||
#define SETTINGS_WINDOW_WIDTH_CN 202
|
||||
#define SETTINGS_WINDOW_WIDTH_EN 248
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 315
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 315
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN 228
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN 275
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN 165
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN 165
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 120
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 167
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 120
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 167
|
||||
#define VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_CN 120
|
||||
#define VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_EN 167
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 120
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 167
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 171
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_CN 171
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_SRTP_CHECKBOX_PADDING_CN 171
|
||||
#define ENABLE_SRTP_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN 171
|
||||
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN 218
|
||||
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_CN 90
|
||||
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_EN 137
|
||||
#define SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN 90
|
||||
#define SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN 137
|
||||
#define SETTINGS_SELECT_WINDOW_WIDTH 73
|
||||
#define SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH 130
|
||||
#define SETTINGS_OK_BUTTON_PADDING_CN 65
|
||||
#define SETTINGS_OK_BUTTON_PADDING_EN 83
|
||||
#define SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_CN 78
|
||||
#define SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_EN 91
|
||||
|
||||
#endif
|
||||
@@ -91,10 +91,22 @@ static std::vector<std::string> enable_hardware_video_codec = {
|
||||
"Enable Hardware Video Codec:"};
|
||||
static std::vector<std::string> enable_turn = {
|
||||
reinterpret_cast<const char*>(u8"启用中继服务:"), "Enable TURN Service:"};
|
||||
|
||||
static std::vector<std::string> enable_srtp = {
|
||||
reinterpret_cast<const char*>(u8"启用SRTP:"), "Enable SRTP:"};
|
||||
|
||||
static std::vector<std::string> self_hosted_server_config = {
|
||||
reinterpret_cast<const char*>(u8"自托管服务器配置"),
|
||||
"Self-Hosted Server Config"};
|
||||
static std::vector<std::string> self_hosted_server_settings = {
|
||||
reinterpret_cast<const char*>(u8"自托管服务器设置"),
|
||||
"Self-Hosted Server Settings"};
|
||||
static std::vector<std::string> self_hosted_server_address = {
|
||||
reinterpret_cast<const char*>(u8"服务器地址:"), "Server Address:"};
|
||||
static std::vector<std::string> self_hosted_server_port = {
|
||||
reinterpret_cast<const char*>(u8"服务器端口:"), "Server Port:"};
|
||||
static std::vector<std::string> self_hosted_server_certificate_path = {
|
||||
reinterpret_cast<const char*>(u8"证书文件路径:"), "Certificate File Path:"};
|
||||
static std::vector<std::string> select_a_file = {
|
||||
reinterpret_cast<const char*>(u8"请选择文件"), "Please select a file"};
|
||||
static std::vector<std::string> ok = {reinterpret_cast<const char*>(u8"确认"),
|
||||
"OK"};
|
||||
static std::vector<std::string> cancel = {
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <random>
|
||||
|
||||
#include "layout_style.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "layout_style.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "device_controller_factory.h"
|
||||
#include "fa_regular_400.h"
|
||||
#include "fa_solid_900.h"
|
||||
#include "layout_style.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "platform.h"
|
||||
#include "rd_log.h"
|
||||
@@ -171,7 +171,8 @@ Render::~Render() {}
|
||||
|
||||
int Render::SaveSettingsIntoCacheFile() {
|
||||
cd_cache_mutex_.lock();
|
||||
std::ofstream cd_cache_file(cache_path_ + "/cache.cd", std::ios::binary);
|
||||
std::ofstream cd_cache_file(cache_path_ + "/secure_cache.enc",
|
||||
std::ios::binary);
|
||||
if (!cd_cache_file) {
|
||||
cd_cache_mutex_.unlock();
|
||||
return -1;
|
||||
@@ -181,18 +182,6 @@ int Render::SaveSettingsIntoCacheFile() {
|
||||
sizeof(cd_cache_.client_id_with_password));
|
||||
memcpy(cd_cache_.client_id_with_password, client_id_with_password_,
|
||||
sizeof(client_id_with_password_));
|
||||
memcpy(&cd_cache_.language, &language_button_value_,
|
||||
sizeof(language_button_value_));
|
||||
memcpy(&cd_cache_.video_quality, &video_quality_button_value_,
|
||||
sizeof(video_quality_button_value_));
|
||||
memcpy(&cd_cache_.video_frame_rate, &video_frame_rate_button_value_,
|
||||
sizeof(video_frame_rate_button_value_));
|
||||
memcpy(&cd_cache_.video_encode_format, &video_encode_format_button_value_,
|
||||
sizeof(video_encode_format_button_value_));
|
||||
memcpy(&cd_cache_.enable_hardware_video_codec, &enable_hardware_video_codec_,
|
||||
sizeof(enable_hardware_video_codec_));
|
||||
memcpy(&cd_cache_.enable_turn, &enable_turn_, sizeof(enable_turn_));
|
||||
memcpy(&cd_cache_.enable_srtp, &enable_srtp_, sizeof(enable_srtp_));
|
||||
memcpy(&cd_cache_.key, &aes128_key_, sizeof(aes128_key_));
|
||||
memcpy(&cd_cache_.iv, &aes128_iv_, sizeof(aes128_iv_));
|
||||
|
||||
@@ -200,49 +189,19 @@ int Render::SaveSettingsIntoCacheFile() {
|
||||
cd_cache_file.close();
|
||||
cd_cache_mutex_.unlock();
|
||||
|
||||
config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_);
|
||||
config_center_.SetVideoQuality(
|
||||
(ConfigCenter::VIDEO_QUALITY)video_quality_button_value_);
|
||||
config_center_.SetVideoFrameRate(
|
||||
(ConfigCenter::VIDEO_FRAME_RATE)video_frame_rate_button_value_);
|
||||
config_center_.SetVideoEncodeFormat(
|
||||
(ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_);
|
||||
config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_);
|
||||
config_center_.SetTurn(enable_turn_);
|
||||
config_center_.SetSrtp(enable_srtp_);
|
||||
|
||||
LOG_INFO("Save settings into cache file success");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Render::LoadSettingsFromCacheFile() {
|
||||
cd_cache_mutex_.lock();
|
||||
std::ifstream cd_cache_file(cache_path_ + "/cache.cd", std::ios::binary);
|
||||
std::ifstream cd_cache_file(cache_path_ + "/secure_cache.enc",
|
||||
std::ios::binary);
|
||||
if (!cd_cache_file) {
|
||||
cd_cache_mutex_.unlock();
|
||||
|
||||
LOG_INFO("Init cache file by using default settings");
|
||||
memset(password_saved_, 0, sizeof(password_saved_));
|
||||
memset(aes128_key_, 0, sizeof(aes128_key_));
|
||||
memset(aes128_iv_, 0, sizeof(aes128_iv_));
|
||||
language_button_value_ = 0;
|
||||
video_quality_button_value_ = 0;
|
||||
video_encode_format_button_value_ = 1;
|
||||
enable_hardware_video_codec_ = false;
|
||||
enable_turn_ = false;
|
||||
enable_srtp_ = false;
|
||||
|
||||
config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_);
|
||||
config_center_.SetVideoQuality(
|
||||
(ConfigCenter::VIDEO_QUALITY)video_quality_button_value_);
|
||||
config_center_.SetVideoFrameRate(
|
||||
(ConfigCenter::VIDEO_FRAME_RATE)video_frame_rate_button_value_);
|
||||
config_center_.SetVideoEncodeFormat(
|
||||
(ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_);
|
||||
config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_);
|
||||
config_center_.SetTurn(enable_turn_);
|
||||
config_center_.SetSrtp(enable_srtp_);
|
||||
|
||||
thumbnail_.reset();
|
||||
thumbnail_ = std::make_unique<Thumbnail>(cache_path_ + "/thumbnails/");
|
||||
@@ -289,13 +248,14 @@ int Render::LoadSettingsFromCacheFile() {
|
||||
thumbnail_ = std::make_unique<Thumbnail>(cache_path_ + "/thumbnails/",
|
||||
aes128_key_, aes128_iv_);
|
||||
|
||||
language_button_value_ = cd_cache_.language;
|
||||
video_quality_button_value_ = cd_cache_.video_quality;
|
||||
video_frame_rate_button_value_ = cd_cache_.video_frame_rate;
|
||||
video_encode_format_button_value_ = cd_cache_.video_encode_format;
|
||||
enable_hardware_video_codec_ = cd_cache_.enable_hardware_video_codec;
|
||||
enable_turn_ = cd_cache_.enable_turn;
|
||||
enable_srtp_ = cd_cache_.enable_srtp;
|
||||
language_button_value_ = (int)config_center_->GetLanguage();
|
||||
video_quality_button_value_ = (int)config_center_->GetVideoQuality();
|
||||
video_frame_rate_button_value_ = (int)config_center_->GetVideoFrameRate();
|
||||
video_encode_format_button_value_ =
|
||||
(int)config_center_->GetVideoEncodeFormat();
|
||||
enable_hardware_video_codec_ = config_center_->IsHardwareVideoCodec();
|
||||
enable_turn_ = config_center_->IsEnableTurn();
|
||||
enable_srtp_ = config_center_->IsEnableSrtp();
|
||||
|
||||
language_button_value_last_ = language_button_value_;
|
||||
video_quality_button_value_last_ = video_quality_button_value_;
|
||||
@@ -304,17 +264,6 @@ int Render::LoadSettingsFromCacheFile() {
|
||||
enable_turn_last_ = enable_turn_;
|
||||
enable_srtp_last_ = enable_srtp_;
|
||||
|
||||
config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_);
|
||||
config_center_.SetVideoQuality(
|
||||
(ConfigCenter::VIDEO_QUALITY)video_quality_button_value_);
|
||||
config_center_.SetVideoFrameRate(
|
||||
(ConfigCenter::VIDEO_FRAME_RATE)video_frame_rate_button_value_);
|
||||
config_center_.SetVideoEncodeFormat(
|
||||
(ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_);
|
||||
config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_);
|
||||
config_center_.SetTurn(enable_turn_);
|
||||
config_center_.SetSrtp(enable_srtp_);
|
||||
|
||||
LOG_INFO("Load settings from cache file");
|
||||
|
||||
return 0;
|
||||
@@ -328,18 +277,21 @@ int Render::ScreenCapturerInit() {
|
||||
last_frame_time_ = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count();
|
||||
int fps = config_center_.GetVideoFrameRate();
|
||||
int fps = config_center_->GetVideoFrameRate() ==
|
||||
ConfigCenter::VIDEO_FRAME_RATE::FPS_30
|
||||
? 30
|
||||
: 60;
|
||||
LOG_INFO("Init screen capturer with {} fps", fps);
|
||||
|
||||
int screen_capturer_init_ret = screen_capturer_->Init(
|
||||
fps,
|
||||
[this](unsigned char* data, int size, int width, int height,
|
||||
const char* display_name) -> void {
|
||||
[this, fps](unsigned char* data, int size, int width, int height,
|
||||
const char* display_name) -> void {
|
||||
auto now_time = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count();
|
||||
auto duration = now_time - last_frame_time_;
|
||||
if (duration * config_center_.GetVideoFrameRate() >= 1000) { // ~60 FPS
|
||||
if (duration * fps >= 1000) { // ~60 FPS
|
||||
XVideoFrame frame;
|
||||
frame.data = (const char*)data;
|
||||
frame.size = size;
|
||||
@@ -474,25 +426,53 @@ int Render::StopKeyboardCapturer() {
|
||||
|
||||
int Render::CreateConnectionPeer() {
|
||||
params_.use_cfg_file = false;
|
||||
params_.signal_server_ip = "api.crossdesk.cn";
|
||||
params_.signal_server_port = 9099;
|
||||
params_.stun_server_ip = "150.158.81.30";
|
||||
|
||||
std::string server_ip;
|
||||
int server_port;
|
||||
std::string server_cert_path;
|
||||
|
||||
if (config_center_->IsSelfHosted()) {
|
||||
server_ip = config_center_->GetServerHost();
|
||||
server_port = config_center_->GetServerPort();
|
||||
server_cert_path = config_center_->GetCertFilePath();
|
||||
} else {
|
||||
server_ip = config_center_->GetDefaultServerHost();
|
||||
server_port = config_center_->GetDefaultServerPort();
|
||||
server_cert_path = config_center_->GetDefaultCertFilePath();
|
||||
}
|
||||
|
||||
strncpy((char*)params_.signal_server_ip, server_ip.c_str(),
|
||||
sizeof(params_.signal_server_ip) - 1);
|
||||
params_.signal_server_ip[sizeof(params_.signal_server_ip) - 1] = '\0';
|
||||
params_.signal_server_port = server_port;
|
||||
strncpy((char*)params_.stun_server_ip, server_ip.c_str(),
|
||||
sizeof(params_.stun_server_ip) - 1);
|
||||
params_.stun_server_ip[sizeof(params_.stun_server_ip) - 1] = '\0';
|
||||
params_.stun_server_port = 3478;
|
||||
params_.turn_server_ip = "150.158.81.30";
|
||||
strncpy((char*)params_.turn_server_ip, server_ip.c_str(),
|
||||
sizeof(params_.turn_server_ip) - 1);
|
||||
params_.turn_server_ip[sizeof(params_.turn_server_ip) - 1] = '\0';
|
||||
params_.turn_server_port = 3478;
|
||||
params_.turn_server_username = "dijunkun";
|
||||
params_.turn_server_password = "dijunkunpw";
|
||||
params_.tls_cert_path = std::filesystem::exists(cert_path_)
|
||||
? cert_path_.c_str()
|
||||
: "certs/crossdesk.cn_root.crt";
|
||||
params_.log_path = dll_log_path_.c_str();
|
||||
params_.hardware_acceleration = config_center_.IsHardwareVideoCodec();
|
||||
params_.av1_encoding = config_center_.GetVideoEncodeFormat() ==
|
||||
strncpy((char*)params_.turn_server_username, "dijunkun",
|
||||
sizeof(params_.turn_server_username) - 1);
|
||||
params_.turn_server_username[sizeof(params_.turn_server_username) - 1] = '\0';
|
||||
strncpy((char*)params_.turn_server_password, "dijunkunpw",
|
||||
sizeof(params_.turn_server_password) - 1);
|
||||
params_.turn_server_password[sizeof(params_.turn_server_password) - 1] = '\0';
|
||||
strncpy(params_.tls_cert_path, server_cert_path.c_str(),
|
||||
sizeof(params_.tls_cert_path) - 1);
|
||||
params_.tls_cert_path[sizeof(params_.tls_cert_path) - 1] = '\0';
|
||||
|
||||
strncpy(params_.log_path, dll_log_path_.c_str(),
|
||||
sizeof(params_.log_path) - 1);
|
||||
params_.log_path[sizeof(params_.log_path) - 1] = '\0';
|
||||
params_.hardware_acceleration = config_center_->IsHardwareVideoCodec();
|
||||
params_.av1_encoding = config_center_->GetVideoEncodeFormat() ==
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1
|
||||
? true
|
||||
: false;
|
||||
params_.enable_turn = config_center_.IsEnableTurn();
|
||||
params_.enable_srtp = config_center_.IsEnableSrtp();
|
||||
params_.enable_turn = config_center_->IsEnableTurn();
|
||||
params_.enable_srtp = config_center_->IsEnableSrtp();
|
||||
params_.on_receive_video_buffer = nullptr;
|
||||
params_.on_receive_audio_buffer = OnReceiveAudioBufferCb;
|
||||
params_.on_receive_data_buffer = OnReceiveDataBufferCb;
|
||||
@@ -890,6 +870,20 @@ int Render::Run() {
|
||||
exec_log_path_ = path_manager_->GetLogPath().string();
|
||||
dll_log_path_ = path_manager_->GetLogPath().string();
|
||||
cache_path_ = path_manager_->GetCachePath().string();
|
||||
config_center_ =
|
||||
std::make_unique<ConfigCenter>(cache_path_ + "/config.ini", cert_path_);
|
||||
strncpy(signal_server_ip_tmp_, config_center_->GetServerHost().c_str(),
|
||||
sizeof(signal_server_ip_tmp_) - 1);
|
||||
signal_server_ip_tmp_[sizeof(signal_server_ip_tmp_) - 1] = '\0';
|
||||
strncpy(signal_server_port_tmp_,
|
||||
std::to_string(config_center_->GetServerPort()).c_str(),
|
||||
sizeof(signal_server_port_tmp_) - 1);
|
||||
signal_server_port_tmp_[sizeof(signal_server_port_tmp_) - 1] = '\0';
|
||||
strncpy(cert_file_path_, cert_path_.c_str(), sizeof(cert_file_path_) - 1);
|
||||
cert_file_path_[sizeof(cert_file_path_) - 1] = '\0';
|
||||
} else {
|
||||
std::cerr << "Failed to create PathManager" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
InitializeLogger();
|
||||
@@ -152,6 +152,8 @@ class Render {
|
||||
int RemoteWindow();
|
||||
int RecentConnectionsWindow();
|
||||
int SettingWindow();
|
||||
int SelfHostedServerWindow();
|
||||
int ShowSimpleFileBrowser();
|
||||
int ControlWindow(std::shared_ptr<SubStreamWindowProperties>& props);
|
||||
int ControlBar(std::shared_ptr<SubStreamWindowProperties>& props);
|
||||
int AboutWindow();
|
||||
@@ -261,7 +263,7 @@ class Render {
|
||||
private:
|
||||
CDCache cd_cache_;
|
||||
std::mutex cd_cache_mutex_;
|
||||
ConfigCenter config_center_;
|
||||
std::unique_ptr<ConfigCenter> config_center_;
|
||||
ConfigCenter::LANGUAGE localization_language_ =
|
||||
ConfigCenter::LANGUAGE::CHINESE;
|
||||
std::unique_ptr<PathManager> path_manager_;
|
||||
@@ -383,6 +385,7 @@ class Render {
|
||||
bool password_validating_ = false;
|
||||
uint32_t password_validating_time_ = 0;
|
||||
bool show_settings_window_ = false;
|
||||
bool show_self_hosted_server_config_window_ = false;
|
||||
bool rejoin_ = false;
|
||||
bool local_id_copied_ = false;
|
||||
bool show_password_ = true;
|
||||
@@ -431,13 +434,22 @@ class Render {
|
||||
bool enable_hardware_video_codec_ = false;
|
||||
bool enable_turn_ = false;
|
||||
bool enable_srtp_ = false;
|
||||
char signal_server_ip_[256] = "api.crossdesk.cn";
|
||||
char signal_server_port_[6] = "9099";
|
||||
char cert_file_path_[256] = "";
|
||||
bool enable_self_hosted_server_ = false;
|
||||
int language_button_value_last_ = 0;
|
||||
int video_quality_button_value_last_ = 0;
|
||||
int video_encode_format_button_value_last_ = 0;
|
||||
bool enable_hardware_video_codec_last_ = false;
|
||||
bool enable_turn_last_ = false;
|
||||
bool enable_srtp_last_ = false;
|
||||
char signal_server_ip_tmp_[256] = "api.crossdesk.cn";
|
||||
char signal_server_port_tmp_[6] = "9099";
|
||||
bool settings_window_pos_reset_ = true;
|
||||
bool self_hosted_server_config_window_pos_reset_ = true;
|
||||
std::string selected_current_file_path_ = "";
|
||||
std::string selected_file_ = "";
|
||||
/* ------ main window property end ------ */
|
||||
|
||||
/* ------ sub stream window property start ------ */
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "layout_style.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
@@ -57,6 +57,7 @@ int Render::TitleBar(bool main_window) {
|
||||
|
||||
{
|
||||
SettingWindow();
|
||||
SelfHostedServerWindow();
|
||||
AboutWindow();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "layout_style.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "layout_style.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "layout_style.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
@@ -108,7 +108,7 @@ int Render::SettingWindow() {
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
const char* video_frame_rate_items[] = {"30", "60"};
|
||||
const char* video_frame_rate_items[] = {"30 fps", "60 fps"};
|
||||
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
@@ -133,8 +133,8 @@ int Render::SettingWindow() {
|
||||
|
||||
{
|
||||
const char* video_encode_format_items[] = {
|
||||
localization::av1[localization_language_index_].c_str(),
|
||||
localization::h264[localization_language_index_].c_str()};
|
||||
localization::h264[localization_language_index_].c_str(),
|
||||
localization::av1[localization_language_index_].c_str()};
|
||||
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
@@ -211,6 +211,28 @@ int Render::SettingWindow() {
|
||||
ImGui::Checkbox("##enable_srtp", &enable_srtp_);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
|
||||
if (ImGui::Button(localization::self_hosted_server_config
|
||||
[localization_language_index_]
|
||||
.c_str())) {
|
||||
show_self_hosted_server_config_window_ = true;
|
||||
}
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::Checkbox("##enable_self_hosted_server",
|
||||
&enable_self_hosted_server_);
|
||||
}
|
||||
|
||||
if (stream_window_inited_) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
@@ -221,7 +243,7 @@ int Render::SettingWindow() {
|
||||
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_EN);
|
||||
}
|
||||
|
||||
settings_items_offset += settings_items_padding;
|
||||
settings_items_offset += settings_items_padding + 10;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
@@ -229,12 +251,13 @@ int Render::SettingWindow() {
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str())) {
|
||||
show_settings_window_ = false;
|
||||
show_self_hosted_server_config_window_ = false;
|
||||
|
||||
// Language
|
||||
if (language_button_value_ == 0) {
|
||||
config_center_.SetLanguage(ConfigCenter::LANGUAGE::CHINESE);
|
||||
config_center_->SetLanguage(ConfigCenter::LANGUAGE::CHINESE);
|
||||
} else {
|
||||
config_center_.SetLanguage(ConfigCenter::LANGUAGE::ENGLISH);
|
||||
config_center_->SetLanguage(ConfigCenter::LANGUAGE::ENGLISH);
|
||||
}
|
||||
language_button_value_last_ = language_button_value_;
|
||||
localization_language_ = (ConfigCenter::LANGUAGE)language_button_value_;
|
||||
@@ -244,50 +267,55 @@ int Render::SettingWindow() {
|
||||
|
||||
// Video quality
|
||||
if (video_quality_button_value_ == 0) {
|
||||
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::HIGH);
|
||||
config_center_->SetVideoQuality(ConfigCenter::VIDEO_QUALITY::HIGH);
|
||||
} else if (video_quality_button_value_ == 1) {
|
||||
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::MEDIUM);
|
||||
config_center_->SetVideoQuality(ConfigCenter::VIDEO_QUALITY::MEDIUM);
|
||||
} else {
|
||||
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::LOW);
|
||||
config_center_->SetVideoQuality(ConfigCenter::VIDEO_QUALITY::LOW);
|
||||
}
|
||||
video_quality_button_value_last_ = video_quality_button_value_;
|
||||
|
||||
// Video encode format
|
||||
if (video_encode_format_button_value_ == 0) {
|
||||
config_center_.SetVideoEncodeFormat(
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1);
|
||||
} else if (video_encode_format_button_value_ == 1) {
|
||||
config_center_.SetVideoEncodeFormat(
|
||||
config_center_->SetVideoEncodeFormat(
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT::H264);
|
||||
} else if (video_encode_format_button_value_ == 1) {
|
||||
config_center_->SetVideoEncodeFormat(
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1);
|
||||
}
|
||||
video_encode_format_button_value_last_ =
|
||||
video_encode_format_button_value_;
|
||||
|
||||
// Hardware video codec
|
||||
if (enable_hardware_video_codec_) {
|
||||
config_center_.SetHardwareVideoCodec(true);
|
||||
config_center_->SetHardwareVideoCodec(true);
|
||||
} else {
|
||||
config_center_.SetHardwareVideoCodec(false);
|
||||
config_center_->SetHardwareVideoCodec(false);
|
||||
}
|
||||
enable_hardware_video_codec_last_ = enable_hardware_video_codec_;
|
||||
|
||||
// TURN mode
|
||||
if (enable_turn_) {
|
||||
config_center_.SetTurn(true);
|
||||
config_center_->SetTurn(true);
|
||||
} else {
|
||||
config_center_.SetTurn(false);
|
||||
config_center_->SetTurn(false);
|
||||
}
|
||||
enable_turn_last_ = enable_turn_;
|
||||
|
||||
// SRTP
|
||||
if (enable_srtp_) {
|
||||
config_center_.SetSrtp(true);
|
||||
config_center_->SetSrtp(true);
|
||||
} else {
|
||||
config_center_.SetSrtp(false);
|
||||
config_center_->SetSrtp(false);
|
||||
}
|
||||
enable_srtp_last_ = enable_srtp_;
|
||||
|
||||
SaveSettingsIntoCacheFile();
|
||||
if (enable_self_hosted_server_) {
|
||||
config_center_->SetSelfHosted(true);
|
||||
} else {
|
||||
config_center_->SetSelfHosted(false);
|
||||
}
|
||||
|
||||
settings_window_pos_reset_ = true;
|
||||
|
||||
// Recreate peer instance
|
||||
@@ -306,6 +334,8 @@ int Render::SettingWindow() {
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str())) {
|
||||
show_settings_window_ = false;
|
||||
show_self_hosted_server_config_window_ = false;
|
||||
|
||||
if (language_button_value_ != language_button_value_last_) {
|
||||
language_button_value_ = language_button_value_last_;
|
||||
}
|
||||
270
src/gui/windows/server_settings_window.cpp
Normal file
270
src/gui/windows/server_settings_window.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
std::vector<std::string> GetRootEntries() {
|
||||
std::vector<std::string> roots;
|
||||
#ifdef _WIN32
|
||||
DWORD mask = GetLogicalDrives();
|
||||
for (char letter = 'A'; letter <= 'Z'; ++letter) {
|
||||
if (mask & 1) {
|
||||
roots.push_back(std::string(1, letter) + ":\\");
|
||||
}
|
||||
mask >>= 1;
|
||||
}
|
||||
#else
|
||||
roots.push_back("/");
|
||||
#endif
|
||||
return roots;
|
||||
}
|
||||
|
||||
int Render::ShowSimpleFileBrowser() {
|
||||
std::string display_text;
|
||||
if (!selected_file_.empty()) {
|
||||
display_text = std::filesystem::path(selected_file_).filename().string();
|
||||
} else if (selected_current_file_path_ != "Root") {
|
||||
display_text =
|
||||
std::filesystem::path(selected_current_file_path_).filename().string();
|
||||
if (display_text.empty()) {
|
||||
display_text = selected_current_file_path_;
|
||||
}
|
||||
}
|
||||
|
||||
if (display_text.empty()) {
|
||||
display_text =
|
||||
localization::select_a_file[localization_language_index_].c_str();
|
||||
}
|
||||
|
||||
if (ImGui::BeginCombo("##select_a_file", display_text.c_str())) {
|
||||
if (selected_current_file_path_ == "Root" ||
|
||||
!std::filesystem::exists(selected_current_file_path_) ||
|
||||
!std::filesystem::is_directory(selected_current_file_path_)) {
|
||||
auto roots = GetRootEntries();
|
||||
for (const auto& root : roots) {
|
||||
if (ImGui::Selectable(root.c_str())) {
|
||||
selected_current_file_path_ = root;
|
||||
selected_file_.clear();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::filesystem::path p(selected_current_file_path_);
|
||||
|
||||
if (ImGui::Selectable("..")) {
|
||||
if (p.has_parent_path() && p != p.root_path())
|
||||
selected_current_file_path_ = p.parent_path().string();
|
||||
else
|
||||
selected_current_file_path_ = "Root";
|
||||
selected_file_.clear();
|
||||
}
|
||||
|
||||
try {
|
||||
for (const auto& entry :
|
||||
std::filesystem::directory_iterator(selected_current_file_path_)) {
|
||||
std::string name = entry.path().filename().string();
|
||||
if (entry.is_directory()) {
|
||||
if (ImGui::Selectable(name.c_str())) {
|
||||
selected_current_file_path_ = entry.path().string();
|
||||
selected_file_.clear();
|
||||
}
|
||||
} else {
|
||||
if (ImGui::Selectable(name.c_str())) {
|
||||
selected_file_ = entry.path().string();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
ImGui::TextColored(ImVec4(1, 0, 0, 1), "Error: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Render::SelfHostedServerWindow() {
|
||||
if (show_self_hosted_server_config_window_) {
|
||||
if (self_hosted_server_config_window_pos_reset_) {
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
|
||||
SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN) /
|
||||
2,
|
||||
(viewport->WorkSize.y - viewport->WorkPos.y -
|
||||
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN) /
|
||||
2));
|
||||
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN,
|
||||
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN));
|
||||
} else {
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
|
||||
SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN) /
|
||||
2,
|
||||
(viewport->WorkSize.y - viewport->WorkPos.y -
|
||||
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN) /
|
||||
2));
|
||||
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN,
|
||||
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN));
|
||||
}
|
||||
|
||||
self_hosted_server_config_window_pos_reset_ = false;
|
||||
}
|
||||
|
||||
// Settings
|
||||
{
|
||||
static int settings_items_padding = 30;
|
||||
int settings_items_offset = 0;
|
||||
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
||||
|
||||
ImGui::Begin(localization::self_hosted_server_settings
|
||||
[localization_language_index_]
|
||||
.c_str(),
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
ImGui::Text("%s", localization::self_hosted_server_address
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
|
||||
|
||||
ImGui::InputText("##signal_server_ip_tmp_", signal_server_ip_tmp_,
|
||||
IM_ARRAYSIZE(signal_server_ip_tmp_),
|
||||
ImGuiInputTextFlags_AlwaysOverwrite);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::self_hosted_server_port[localization_language_index_]
|
||||
.c_str());
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
|
||||
|
||||
ImGui::InputText("##signal_server_port_tmp_", signal_server_port_tmp_,
|
||||
IM_ARRAYSIZE(signal_server_port_tmp_));
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
ImGui::Text("%s", localization::self_hosted_server_certificate_path
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
|
||||
|
||||
ShowSimpleFileBrowser();
|
||||
}
|
||||
|
||||
if (stream_window_inited_) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_EN);
|
||||
}
|
||||
|
||||
settings_items_offset += settings_items_padding + 10;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
// OK
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str())) {
|
||||
show_self_hosted_server_config_window_ = false;
|
||||
|
||||
config_center_->SetServerHost(signal_server_ip_tmp_);
|
||||
config_center_->SetServerPort(atoi(signal_server_port_tmp_));
|
||||
config_center_->SetCertFilePath(selected_file_);
|
||||
strncpy(signal_server_ip_, signal_server_ip_tmp_,
|
||||
sizeof(signal_server_ip_) - 1);
|
||||
signal_server_ip_[sizeof(signal_server_ip_) - 1] = '\0';
|
||||
strncpy(signal_server_port_, signal_server_port_tmp_,
|
||||
sizeof(signal_server_port_) - 1);
|
||||
signal_server_port_[sizeof(signal_server_port_) - 1] = '\0';
|
||||
strncpy(cert_file_path_, selected_file_.c_str(),
|
||||
sizeof(cert_file_path_) - 1);
|
||||
cert_file_path_[sizeof(cert_file_path_) - 1] = '\0';
|
||||
|
||||
self_hosted_server_config_window_pos_reset_ = true;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// Cancel
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str())) {
|
||||
show_self_hosted_server_config_window_ = false;
|
||||
self_hosted_server_config_window_pos_reset_ = true;
|
||||
|
||||
strncpy(signal_server_ip_tmp_, signal_server_ip_,
|
||||
sizeof(signal_server_ip_tmp_) - 1);
|
||||
signal_server_ip_tmp_[sizeof(signal_server_ip_tmp_) - 1] = '\0';
|
||||
strncpy(signal_server_port_tmp_, signal_server_port_,
|
||||
sizeof(signal_server_port_tmp_) - 1);
|
||||
signal_server_port_tmp_[sizeof(signal_server_port_tmp_) - 1] = '\0';
|
||||
config_center_->SetServerHost(signal_server_ip_tmp_);
|
||||
config_center_->SetServerPort(atoi(signal_server_port_tmp_));
|
||||
selected_file_.clear();
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
static std::vector<DisplayInfo> gs_display_list;
|
||||
|
||||
std::string WideToUtf8(const wchar_t *wideStr) {
|
||||
std::string WideToUtf8(const wchar_t* wideStr) {
|
||||
int size_needed = WideCharToMultiByte(CP_UTF8, 0, wideStr, -1, nullptr, 0,
|
||||
nullptr, nullptr);
|
||||
std::string result(size_needed, 0);
|
||||
@@ -31,14 +31,14 @@ BOOL WINAPI EnumMonitorProc(HMONITOR hmonitor, [[maybe_unused]] HDC hdc,
|
||||
if (monitor_info_.dwFlags & MONITORINFOF_PRIMARY) {
|
||||
gs_display_list.insert(
|
||||
gs_display_list.begin(),
|
||||
{(void *)hmonitor, WideToUtf8(monitor_info_.szDevice),
|
||||
{(void*)hmonitor, WideToUtf8(monitor_info_.szDevice),
|
||||
(monitor_info_.dwFlags & MONITORINFOF_PRIMARY) ? true : false,
|
||||
monitor_info_.rcMonitor.left, monitor_info_.rcMonitor.top,
|
||||
monitor_info_.rcMonitor.right, monitor_info_.rcMonitor.bottom});
|
||||
*(HMONITOR *)data = hmonitor;
|
||||
*(HMONITOR*)data = hmonitor;
|
||||
} else {
|
||||
gs_display_list.push_back(DisplayInfo(
|
||||
(void *)hmonitor, WideToUtf8(monitor_info_.szDevice),
|
||||
(void*)hmonitor, WideToUtf8(monitor_info_.szDevice),
|
||||
(monitor_info_.dwFlags & MONITORINFOF_PRIMARY) ? true : false,
|
||||
monitor_info_.rcMonitor.left, monitor_info_.rcMonitor.top,
|
||||
monitor_info_.rcMonitor.right, monitor_info_.rcMonitor.bottom));
|
||||
@@ -81,7 +81,7 @@ bool ScreenCapturerWgc::IsWgcSupported() {
|
||||
/* no contract for IGraphicsCaptureItemInterop, verify 10.0.18362.0 */
|
||||
return winrt::Windows::Foundation::Metadata::ApiInformation::
|
||||
IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 8);
|
||||
} catch (const winrt::hresult_error &) {
|
||||
} catch (const winrt::hresult_error&) {
|
||||
return false;
|
||||
} catch (...) {
|
||||
return false;
|
||||
@@ -115,7 +115,7 @@ int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) {
|
||||
}
|
||||
|
||||
for (int i = 0; i < display_info_list_.size(); i++) {
|
||||
const auto &display = display_info_list_[i];
|
||||
const auto& display = display_info_list_[i];
|
||||
LOG_INFO(
|
||||
"index: {}, display name: {}, is primary: {}, bounds: ({}, {}) - "
|
||||
"({}, {})",
|
||||
@@ -243,26 +243,28 @@ int ScreenCapturerWgc::SwitchTo(int monitor_index) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame &frame,
|
||||
void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame& frame,
|
||||
int id) {
|
||||
if (on_data_) {
|
||||
if (!nv12_frame_) {
|
||||
nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2];
|
||||
}
|
||||
|
||||
libyuv::ARGBToNV12((const uint8_t *)frame.data, frame.width * 4,
|
||||
(uint8_t *)nv12_frame_, frame.width,
|
||||
(uint8_t *)(nv12_frame_ + frame.width * frame.height),
|
||||
frame.width, frame.width, frame.height);
|
||||
|
||||
on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width,
|
||||
frame.height, display_info_list_[id].name.c_str());
|
||||
if (!on_data_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nv12_frame_) {
|
||||
nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2];
|
||||
}
|
||||
|
||||
libyuv::ARGBToNV12((const uint8_t*)frame.data, frame.width * 4,
|
||||
(uint8_t*)nv12_frame_, frame.width,
|
||||
(uint8_t*)(nv12_frame_ + frame.width * frame.height),
|
||||
frame.width, frame.width, frame.height);
|
||||
|
||||
on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width,
|
||||
frame.height, display_info_list_[id].name.c_str());
|
||||
}
|
||||
|
||||
void ScreenCapturerWgc::CleanUp() {
|
||||
if (inited_) {
|
||||
for (auto &session : sessions_) {
|
||||
for (auto& session : sessions_) {
|
||||
if (session.session_) {
|
||||
session.session_->Stop();
|
||||
}
|
||||
@@ -43,6 +43,8 @@ class ScreenCapturerWgc : public ScreenCapturer,
|
||||
std::vector<DisplayInfo> display_info_list_;
|
||||
int monitor_index_ = 0;
|
||||
|
||||
HWND hwnd_ = nullptr;
|
||||
|
||||
private:
|
||||
class WgcSessionInfo {
|
||||
public:
|
||||
@@ -63,6 +65,9 @@ class ScreenCapturerWgc : public ScreenCapturer,
|
||||
|
||||
unsigned char* nv12_frame_ = nullptr;
|
||||
unsigned char* nv12_frame_scaled_ = nullptr;
|
||||
|
||||
private:
|
||||
bool CreateHiddenWindow();
|
||||
};
|
||||
|
||||
#endif
|
||||
68
src/screen_capturer/windows/screen_capturer_wgc.h.bak
Normal file
68
src/screen_capturer/windows/screen_capturer_wgc.h.bak
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef _SCREEN_CAPTURER_WGC_H_
|
||||
#define _SCREEN_CAPTURER_WGC_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "screen_capturer.h"
|
||||
#include "wgc_session.h"
|
||||
#include "wgc_session_impl.h"
|
||||
|
||||
class ScreenCapturerWgc : public ScreenCapturer,
|
||||
public WgcSession::wgc_session_observer {
|
||||
public:
|
||||
ScreenCapturerWgc();
|
||||
~ScreenCapturerWgc();
|
||||
|
||||
public:
|
||||
bool IsWgcSupported();
|
||||
|
||||
int Init(const int fps, cb_desktop_data cb) override;
|
||||
int Destroy() override;
|
||||
int Start() override;
|
||||
int Stop() override;
|
||||
|
||||
int Pause(int monitor_index) override;
|
||||
int Resume(int monitor_index) override;
|
||||
|
||||
std::vector<DisplayInfo> GetDisplayInfoList() { return display_info_list_; }
|
||||
|
||||
int SwitchTo(int monitor_index);
|
||||
|
||||
void OnFrame(const WgcSession::wgc_session_frame& frame, int id);
|
||||
|
||||
protected:
|
||||
void CleanUp();
|
||||
|
||||
private:
|
||||
HMONITOR monitor_;
|
||||
MONITORINFOEX monitor_info_;
|
||||
std::vector<DisplayInfo> display_info_list_;
|
||||
int monitor_index_ = 0;
|
||||
|
||||
private:
|
||||
class WgcSessionInfo {
|
||||
public:
|
||||
std::unique_ptr<WgcSession> session_;
|
||||
bool inited_ = false;
|
||||
bool running_ = false;
|
||||
bool paused_ = false;
|
||||
};
|
||||
|
||||
std::vector<WgcSessionInfo> sessions_;
|
||||
|
||||
std::atomic_bool running_;
|
||||
std::atomic_bool inited_;
|
||||
|
||||
int fps_ = 60;
|
||||
|
||||
cb_desktop_data on_data_ = nullptr;
|
||||
|
||||
unsigned char* nv12_frame_ = nullptr;
|
||||
unsigned char* nv12_frame_scaled_ = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
187
src/screen_capturer/windows/screen_capturer_wgc_warp.cpp
Normal file
187
src/screen_capturer/windows/screen_capturer_wgc_warp.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
#include <Windows.h>
|
||||
#include <d3d11_4.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "libyuv.h"
|
||||
#include "rd_log.h"
|
||||
#include "screen_capturer_wgc.h"
|
||||
|
||||
// Dummy window proc for hidden window
|
||||
static LRESULT CALLBACK DummyWndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
LPARAM lParam) {
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
// ======================= 构造函数 / 析构函数 =======================
|
||||
ScreenCapturerWgc::ScreenCapturerWgc()
|
||||
: monitor_(nullptr),
|
||||
hwnd_(nullptr),
|
||||
monitor_index_(0),
|
||||
running_(false),
|
||||
inited_(false),
|
||||
fps_(60),
|
||||
on_data_(nullptr),
|
||||
nv12_frame_(nullptr),
|
||||
nv12_frame_scaled_(nullptr) {}
|
||||
|
||||
ScreenCapturerWgc::~ScreenCapturerWgc() {
|
||||
Stop();
|
||||
CleanUp();
|
||||
|
||||
if (nv12_frame_) {
|
||||
delete[] nv12_frame_;
|
||||
nv12_frame_ = nullptr;
|
||||
}
|
||||
|
||||
if (nv12_frame_scaled_) {
|
||||
delete[] nv12_frame_scaled_;
|
||||
nv12_frame_scaled_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================= 隐藏窗口创建 =======================
|
||||
bool ScreenCapturerWgc::CreateHiddenWindow() {
|
||||
const wchar_t kClassName[] = L"ScreenCapturerHiddenWindow";
|
||||
|
||||
WNDCLASSW wc = {};
|
||||
wc.lpfnWndProc = DummyWndProc;
|
||||
wc.hInstance = GetModuleHandle(nullptr);
|
||||
wc.lpszClassName = kClassName;
|
||||
|
||||
if (!RegisterClassW(&wc)) {
|
||||
std::cerr << "Failed to register dummy window class\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
hwnd_ = CreateWindowW(kClassName, L"", WS_OVERLAPPEDWINDOW, 0, 0, 1, 1,
|
||||
nullptr, nullptr, wc.hInstance, nullptr);
|
||||
if (!hwnd_) {
|
||||
std::cerr << "Failed to create dummy window\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
ShowWindow(hwnd_, SW_HIDE);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ======================= 初始化 =======================
|
||||
int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) {
|
||||
if (inited_) return 0;
|
||||
|
||||
fps_ = fps;
|
||||
on_data_ = cb;
|
||||
|
||||
// 创建隐藏窗口
|
||||
if (!CreateHiddenWindow()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 初始化 WGC Session
|
||||
sessions_.push_back(
|
||||
{std::make_unique<WgcSessionImpl>(0), false, false, false});
|
||||
sessions_.back().session_->RegisterObserver(this);
|
||||
int error = sessions_.back().session_->Initialize(hwnd_);
|
||||
if (error != 0) {
|
||||
std::cerr << "WGC session init failed\n";
|
||||
return error;
|
||||
}
|
||||
sessions_[0].inited_ = true;
|
||||
|
||||
inited_ = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerWgc::Destroy() {
|
||||
Stop();
|
||||
CleanUp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerWgc::Pause(int monitor_index) {
|
||||
// 目前只支持隐藏窗口,所以忽略 monitor_index
|
||||
if (!running_) return -1;
|
||||
sessions_[0].session_->Pause();
|
||||
sessions_[0].paused_ = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerWgc::Resume(int monitor_index) {
|
||||
if (!running_) return -1;
|
||||
sessions_[0].session_->Resume();
|
||||
sessions_[0].paused_ = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerWgc::SwitchTo(int monitor_index) {
|
||||
// 单隐藏窗口模式,不支持切换
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ======================= 开始 =======================
|
||||
int ScreenCapturerWgc::Start() {
|
||||
if (!inited_) return -1;
|
||||
if (running_) return 0;
|
||||
if (sessions_.empty()) return -1;
|
||||
|
||||
sessions_[0].session_->Start();
|
||||
sessions_[0].running_ = true;
|
||||
running_ = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ======================= 停止 =======================
|
||||
int ScreenCapturerWgc::Stop() {
|
||||
if (!running_) return 0;
|
||||
|
||||
if (!sessions_.empty()) {
|
||||
sessions_[0].session_->Stop();
|
||||
sessions_[0].running_ = false;
|
||||
}
|
||||
|
||||
running_ = false;
|
||||
|
||||
if (hwnd_) {
|
||||
DestroyWindow(hwnd_);
|
||||
hwnd_ = nullptr;
|
||||
}
|
||||
|
||||
inited_ = false;
|
||||
sessions_.clear();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ======================= 帧回调 =======================
|
||||
void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame& frame,
|
||||
int id) {
|
||||
if (!on_data_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nv12_frame_) {
|
||||
nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2];
|
||||
}
|
||||
|
||||
libyuv::ARGBToNV12((const uint8_t*)frame.data, frame.width * 4,
|
||||
(uint8_t*)nv12_frame_, frame.width,
|
||||
(uint8_t*)(nv12_frame_ + frame.width * frame.height),
|
||||
frame.width, frame.width, frame.height);
|
||||
|
||||
on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width,
|
||||
frame.height, "hidden_window");
|
||||
}
|
||||
|
||||
// ======================= 清理 =======================
|
||||
void ScreenCapturerWgc::CleanUp() {
|
||||
if (inited_) {
|
||||
for (auto& session : sessions_) {
|
||||
if (session.session_) {
|
||||
session.session_->Stop();
|
||||
}
|
||||
}
|
||||
sessions_.clear();
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "rd_log.h"
|
||||
|
||||
#define CHECK_INIT \
|
||||
if (!is_initialized_) { \
|
||||
std::cout << "AE_NEED_INIT" << std::endl; \
|
||||
@@ -20,122 +22,118 @@
|
||||
|
||||
extern "C" {
|
||||
HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(
|
||||
::IDXGIDevice *dxgiDevice, ::IInspectable **graphicsDevice);
|
||||
::IDXGIDevice* dxgiDevice, ::IInspectable** graphicsDevice);
|
||||
}
|
||||
|
||||
WgcSessionImpl::WgcSessionImpl(int id) : id_(id) {}
|
||||
|
||||
WgcSessionImpl::~WgcSessionImpl() {
|
||||
Stop();
|
||||
CleanUp();
|
||||
try {
|
||||
Stop();
|
||||
CleanUp();
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
void WgcSessionImpl::Release() { delete this; }
|
||||
|
||||
int WgcSessionImpl::Initialize(HWND hwnd) {
|
||||
std::lock_guard locker(lock_);
|
||||
|
||||
std::scoped_lock locker(lock_);
|
||||
target_.hwnd = hwnd;
|
||||
target_.is_window = true;
|
||||
return Initialize();
|
||||
}
|
||||
|
||||
int WgcSessionImpl::Initialize(HMONITOR hmonitor) {
|
||||
std::lock_guard locker(lock_);
|
||||
|
||||
std::scoped_lock locker(lock_);
|
||||
target_.hmonitor = hmonitor;
|
||||
target_.is_window = false;
|
||||
return Initialize();
|
||||
}
|
||||
|
||||
void WgcSessionImpl::RegisterObserver(wgc_session_observer *observer) {
|
||||
std::lock_guard locker(lock_);
|
||||
void WgcSessionImpl::RegisterObserver(wgc_session_observer* observer) {
|
||||
std::scoped_lock locker(lock_);
|
||||
observer_ = observer;
|
||||
}
|
||||
|
||||
int WgcSessionImpl::Start() {
|
||||
std::lock_guard locker(lock_);
|
||||
std::scoped_lock locker(lock_);
|
||||
CHECK_INIT;
|
||||
|
||||
if (is_running_) return 0;
|
||||
|
||||
int error = 1;
|
||||
|
||||
CHECK_INIT;
|
||||
try {
|
||||
if (!capture_session_) {
|
||||
auto current_size = capture_item_.Size();
|
||||
capture_framepool_ =
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
|
||||
CreateFreeThreaded(d3d11_direct_device_,
|
||||
winrt::Windows::Graphics::DirectX::
|
||||
DirectXPixelFormat::B8G8R8A8UIntNormalized,
|
||||
2, current_size);
|
||||
capture_session_ = capture_framepool_.CreateCaptureSession(capture_item_);
|
||||
capture_frame_size_ = current_size;
|
||||
capture_framepool_trigger_ = capture_framepool_.FrameArrived(
|
||||
winrt::auto_revoke, {this, &WgcSessionImpl::OnFrame});
|
||||
capture_close_trigger_ = capture_item_.Closed(
|
||||
winrt::auto_revoke, {this, &WgcSessionImpl::OnClosed});
|
||||
if (!capture_item_) {
|
||||
std::cout << "AE_NO_CAPTURE_ITEM" << std::endl;
|
||||
LOG_ERROR("No capture item");
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (!capture_framepool_) throw std::exception();
|
||||
auto current_size = capture_item_.Size();
|
||||
capture_framepool_ =
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
|
||||
CreateFreeThreaded(d3d11_direct_device_,
|
||||
winrt::Windows::Graphics::DirectX::
|
||||
DirectXPixelFormat::B8G8R8A8UIntNormalized,
|
||||
2, current_size);
|
||||
|
||||
is_running_ = true;
|
||||
capture_session_ = capture_framepool_.CreateCaptureSession(capture_item_);
|
||||
capture_frame_size_ = current_size;
|
||||
|
||||
// we do not need to crate a thread to enter a message loop coz we use
|
||||
// CreateFreeThreaded instead of Create to create a capture frame pool,
|
||||
// we need to test the performance later
|
||||
// loop_ = std::thread(std::bind(&WgcSessionImpl::message_func, this));
|
||||
|
||||
capture_session_.StartCapture();
|
||||
capture_framepool_trigger_ = capture_framepool_.FrameArrived(
|
||||
winrt::auto_revoke, {this, &WgcSessionImpl::OnFrame});
|
||||
capture_close_trigger_ = capture_item_.Closed(
|
||||
winrt::auto_revoke, {this, &WgcSessionImpl::OnClosed});
|
||||
|
||||
capture_session_.IsCursorCaptureEnabled(false);
|
||||
capture_session_.StartCapture();
|
||||
|
||||
error = 0;
|
||||
} catch (winrt::hresult_error) {
|
||||
std::cout << "AE_WGC_CREATE_CAPTURER_FAILED" << std::endl;
|
||||
is_running_ = true;
|
||||
is_paused_ = false;
|
||||
return 0;
|
||||
} catch (winrt::hresult_error const& e) {
|
||||
LOG_ERROR("Create WGC Capture Failed: {}", winrt::to_string(e.message()));
|
||||
return 86;
|
||||
} catch (...) {
|
||||
return 86;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int WgcSessionImpl::Stop() {
|
||||
std::lock_guard locker(lock_);
|
||||
|
||||
std::scoped_lock locker(lock_);
|
||||
CHECK_INIT;
|
||||
|
||||
if (!is_running_) return 0;
|
||||
is_running_ = false;
|
||||
|
||||
// if (loop_.joinable()) loop_.join();
|
||||
|
||||
if (capture_framepool_trigger_) capture_framepool_trigger_.revoke();
|
||||
|
||||
if (capture_session_) {
|
||||
capture_session_.Close();
|
||||
capture_session_ = nullptr;
|
||||
try {
|
||||
if (capture_framepool_trigger_) capture_framepool_trigger_.revoke();
|
||||
if (capture_close_trigger_) capture_close_trigger_.revoke();
|
||||
if (capture_session_) {
|
||||
capture_session_.Close();
|
||||
capture_session_ = nullptr;
|
||||
}
|
||||
if (capture_framepool_) {
|
||||
capture_framepool_.Close();
|
||||
capture_framepool_ = nullptr;
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WgcSessionImpl::Pause() {
|
||||
std::lock_guard locker(lock_);
|
||||
|
||||
is_paused_ = true;
|
||||
|
||||
std::scoped_lock locker(lock_);
|
||||
CHECK_INIT;
|
||||
is_paused_ = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WgcSessionImpl::Resume() {
|
||||
std::lock_guard locker(lock_);
|
||||
|
||||
is_paused_ = false;
|
||||
|
||||
std::scoped_lock locker(lock_);
|
||||
CHECK_INIT;
|
||||
is_paused_ = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -143,19 +141,15 @@ auto WgcSessionImpl::CreateD3D11Device() {
|
||||
winrt::com_ptr<ID3D11Device> d3d_device;
|
||||
HRESULT hr;
|
||||
|
||||
WINRT_ASSERT(!d3d_device);
|
||||
|
||||
D3D_DRIVER_TYPE type = D3D_DRIVER_TYPE_HARDWARE;
|
||||
UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
|
||||
hr = D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0,
|
||||
D3D11_SDK_VERSION, d3d_device.put(), nullptr, nullptr);
|
||||
hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags,
|
||||
nullptr, 0, D3D11_SDK_VERSION, d3d_device.put(),
|
||||
nullptr, nullptr);
|
||||
|
||||
if (DXGI_ERROR_UNSUPPORTED == hr) {
|
||||
// change D3D_DRIVER_TYPE
|
||||
type = D3D_DRIVER_TYPE_WARP;
|
||||
hr = D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0,
|
||||
D3D11_SDK_VERSION, d3d_device.put(), nullptr,
|
||||
nullptr);
|
||||
hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, flags,
|
||||
nullptr, 0, D3D11_SDK_VERSION, d3d_device.put(),
|
||||
nullptr, nullptr);
|
||||
}
|
||||
|
||||
winrt::check_hresult(hr);
|
||||
@@ -168,26 +162,28 @@ auto WgcSessionImpl::CreateD3D11Device() {
|
||||
}
|
||||
|
||||
auto WgcSessionImpl::CreateCaptureItemForWindow(HWND hwnd) {
|
||||
auto activation_factory = winrt::get_activation_factory<
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
|
||||
auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
|
||||
auto interop_factory =
|
||||
winrt::get_activation_factory<
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>()
|
||||
.as<IGraphicsCaptureItemInterop>();
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item{nullptr};
|
||||
interop_factory->CreateForWindow(
|
||||
hwnd,
|
||||
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
|
||||
reinterpret_cast<void **>(winrt::put_abi(item)));
|
||||
reinterpret_cast<void**>(winrt::put_abi(item)));
|
||||
return item;
|
||||
}
|
||||
|
||||
auto WgcSessionImpl::CreateCaptureItemForMonitor(HMONITOR hmonitor) {
|
||||
auto activation_factory = winrt::get_activation_factory<
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
|
||||
auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
|
||||
auto interop_factory =
|
||||
winrt::get_activation_factory<
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>()
|
||||
.as<IGraphicsCaptureItemInterop>();
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item{nullptr};
|
||||
interop_factory->CreateForMonitor(
|
||||
hmonitor,
|
||||
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
|
||||
reinterpret_cast<void **>(winrt::put_abi(item)));
|
||||
reinterpret_cast<void**>(winrt::put_abi(item)));
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -196,13 +192,10 @@ HRESULT WgcSessionImpl::CreateMappedTexture(
|
||||
unsigned int height) {
|
||||
D3D11_TEXTURE2D_DESC src_desc;
|
||||
src_texture->GetDesc(&src_desc);
|
||||
D3D11_TEXTURE2D_DESC map_desc;
|
||||
map_desc.Width = width == 0 ? src_desc.Width : width;
|
||||
map_desc.Height = height == 0 ? src_desc.Height : height;
|
||||
map_desc.MipLevels = src_desc.MipLevels;
|
||||
map_desc.ArraySize = src_desc.ArraySize;
|
||||
map_desc.Format = src_desc.Format;
|
||||
map_desc.SampleDesc = src_desc.SampleDesc;
|
||||
|
||||
D3D11_TEXTURE2D_DESC map_desc = src_desc;
|
||||
map_desc.Width = width ? width : src_desc.Width;
|
||||
map_desc.Height = height ? height : src_desc.Height;
|
||||
map_desc.Usage = D3D11_USAGE_STAGING;
|
||||
map_desc.BindFlags = 0;
|
||||
map_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
@@ -216,163 +209,101 @@ HRESULT WgcSessionImpl::CreateMappedTexture(
|
||||
}
|
||||
|
||||
void WgcSessionImpl::OnFrame(
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const &sender,
|
||||
[[maybe_unused]] winrt::Windows::Foundation::IInspectable const &args) {
|
||||
std::lock_guard locker(lock_);
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const& sender,
|
||||
[[maybe_unused]] winrt::Windows::Foundation::IInspectable const& args) {
|
||||
if (!is_running_ || is_paused_) return;
|
||||
std::scoped_lock locker(lock_);
|
||||
|
||||
auto is_new_size = false;
|
||||
if (!observer_) return;
|
||||
|
||||
{
|
||||
auto frame = sender.TryGetNextFrame();
|
||||
auto frame_size = frame.ContentSize();
|
||||
auto frame = sender.TryGetNextFrame();
|
||||
if (!frame) return;
|
||||
|
||||
if (frame_size.Width != capture_frame_size_.Width ||
|
||||
frame_size.Height != capture_frame_size_.Height) {
|
||||
// The thing we have been capturing has changed size.
|
||||
// We need to resize our swap chain first, then blit the pixels.
|
||||
// After we do that, retire the frame and then recreate our frame pool.
|
||||
is_new_size = true;
|
||||
capture_frame_size_ = frame_size;
|
||||
}
|
||||
|
||||
// copy to mapped texture
|
||||
{
|
||||
if (is_paused_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto frame_captured =
|
||||
GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
|
||||
|
||||
if (!d3d11_texture_mapped_ || is_new_size)
|
||||
CreateMappedTexture(frame_captured);
|
||||
|
||||
d3d11_device_context_->CopyResource(d3d11_texture_mapped_.get(),
|
||||
frame_captured.get());
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE map_result;
|
||||
HRESULT hr = d3d11_device_context_->Map(
|
||||
d3d11_texture_mapped_.get(), 0, D3D11_MAP_READ,
|
||||
0 /*coz we use CreateFreeThreaded, so we cant use flags
|
||||
D3D11_MAP_FLAG_DO_NOT_WAIT*/
|
||||
,
|
||||
&map_result);
|
||||
if (FAILED(hr)) {
|
||||
OutputDebugStringW(
|
||||
(L"map resource failed: " + std::to_wstring(hr)).c_str());
|
||||
}
|
||||
|
||||
// copy data from map_result.pData
|
||||
if (map_result.pData && observer_) {
|
||||
observer_->OnFrame(
|
||||
wgc_session_frame{static_cast<unsigned int>(frame_size.Width),
|
||||
static_cast<unsigned int>(frame_size.Height),
|
||||
map_result.RowPitch,
|
||||
const_cast<const unsigned char *>(
|
||||
(unsigned char *)map_result.pData)},
|
||||
id_);
|
||||
}
|
||||
|
||||
d3d11_device_context_->Unmap(d3d11_texture_mapped_.get(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_new_size) {
|
||||
auto frame_size = frame.ContentSize();
|
||||
bool size_changed = (frame_size.Width != capture_frame_size_.Width ||
|
||||
frame_size.Height != capture_frame_size_.Height);
|
||||
if (size_changed) {
|
||||
capture_frame_size_ = frame_size;
|
||||
capture_framepool_.Recreate(d3d11_direct_device_,
|
||||
winrt::Windows::Graphics::DirectX::
|
||||
DirectXPixelFormat::B8G8R8A8UIntNormalized,
|
||||
2, capture_frame_size_);
|
||||
}
|
||||
|
||||
auto frame_surface =
|
||||
GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
|
||||
|
||||
if (!d3d11_texture_mapped_ || size_changed)
|
||||
CreateMappedTexture(frame_surface, frame_size.Width, frame_size.Height);
|
||||
|
||||
d3d11_device_context_->CopyResource(d3d11_texture_mapped_.get(),
|
||||
frame_surface.get());
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE map_result;
|
||||
HRESULT hr = d3d11_device_context_->Map(d3d11_texture_mapped_.get(), 0,
|
||||
D3D11_MAP_READ, 0, &map_result);
|
||||
if (SUCCEEDED(hr)) {
|
||||
wgc_session_frame frame_info{
|
||||
static_cast<unsigned int>(frame_size.Width),
|
||||
static_cast<unsigned int>(frame_size.Height), map_result.RowPitch,
|
||||
reinterpret_cast<const unsigned char*>(map_result.pData)};
|
||||
observer_->OnFrame(frame_info, id_);
|
||||
d3d11_device_context_->Unmap(d3d11_texture_mapped_.get(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void WgcSessionImpl::OnClosed(
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem const &,
|
||||
winrt::Windows::Foundation::IInspectable const &) {
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem const&,
|
||||
winrt::Windows::Foundation::IInspectable const&) {
|
||||
OutputDebugStringW(L"WgcSessionImpl::OnClosed");
|
||||
Stop();
|
||||
}
|
||||
|
||||
int WgcSessionImpl::Initialize() {
|
||||
if (is_initialized_) return 0;
|
||||
|
||||
if (!(d3d11_direct_device_ = CreateD3D11Device())) {
|
||||
d3d11_direct_device_ = CreateD3D11Device();
|
||||
if (!d3d11_direct_device_) {
|
||||
std::cout << "AE_D3D_CREATE_DEVICE_FAILED" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
if (target_.is_window)
|
||||
capture_item_ = CreateCaptureItemForWindow(target_.hwnd);
|
||||
else
|
||||
capture_item_ = CreateCaptureItemForMonitor(target_.hmonitor);
|
||||
capture_item_ = target_.is_window
|
||||
? CreateCaptureItemForWindow(target_.hwnd)
|
||||
: CreateCaptureItemForMonitor(target_.hmonitor);
|
||||
|
||||
// Set up
|
||||
auto d3d11_device =
|
||||
auto d3d_device =
|
||||
GetDXGIInterfaceFromObject<ID3D11Device>(d3d11_direct_device_);
|
||||
d3d11_device->GetImmediateContext(d3d11_device_context_.put());
|
||||
|
||||
} catch (winrt::hresult_error) {
|
||||
std::cout << "AE_WGC_CREATE_CAPTURER_FAILED" << std::endl;
|
||||
return 86;
|
||||
d3d_device->GetImmediateContext(d3d11_device_context_.put());
|
||||
} catch (...) {
|
||||
LOG_ERROR("AE_WGC_CREATE_CAPTURER_FAILED");
|
||||
return 86;
|
||||
}
|
||||
|
||||
is_initialized_ = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WgcSessionImpl::CleanUp() {
|
||||
std::lock_guard locker(lock_);
|
||||
|
||||
auto expected = false;
|
||||
if (cleaned_.compare_exchange_strong(expected, true)) {
|
||||
capture_close_trigger_.revoke();
|
||||
capture_framepool_trigger_.revoke();
|
||||
std::scoped_lock locker(lock_);
|
||||
if (cleaned_.exchange(true)) return;
|
||||
|
||||
try {
|
||||
if (capture_framepool_trigger_) capture_framepool_trigger_.revoke();
|
||||
if (capture_close_trigger_) capture_close_trigger_.revoke();
|
||||
if (capture_framepool_) capture_framepool_.Close();
|
||||
|
||||
if (capture_session_) capture_session_.Close();
|
||||
|
||||
capture_framepool_ = nullptr;
|
||||
capture_session_ = nullptr;
|
||||
capture_item_ = nullptr;
|
||||
|
||||
is_initialized_ = false;
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
capture_framepool_ = nullptr;
|
||||
capture_session_ = nullptr;
|
||||
capture_item_ = nullptr;
|
||||
d3d11_texture_mapped_ = nullptr;
|
||||
d3d11_device_context_ = nullptr;
|
||||
d3d11_direct_device_ = nullptr;
|
||||
|
||||
is_initialized_ = false;
|
||||
is_running_ = false;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM w_param,
|
||||
LPARAM l_param) {
|
||||
return DefWindowProc(window, message, w_param, l_param);
|
||||
}
|
||||
|
||||
// void WgcSessionImpl::message_func() {
|
||||
// const std::wstring kClassName = L"am_fake_window";
|
||||
|
||||
// WNDCLASS wc = {};
|
||||
|
||||
// wc.style = CS_HREDRAW | CS_VREDRAW;
|
||||
// wc.lpfnWndProc = DefWindowProc;
|
||||
// wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
// wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW);
|
||||
// wc.lpszClassName = kClassName.c_str();
|
||||
|
||||
// if (!::RegisterClassW(&wc)) return;
|
||||
|
||||
// hwnd_ = ::CreateWindowW(kClassName.c_str(), nullptr, WS_OVERLAPPEDWINDOW,
|
||||
// 0,
|
||||
// 0, 0, 0, nullptr, nullptr, nullptr, nullptr);
|
||||
// MSG msg;
|
||||
// while (is_running_) {
|
||||
// while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||
// if (!is_running_) break;
|
||||
// TranslateMessage(&msg);
|
||||
// DispatchMessage(&msg);
|
||||
// }
|
||||
// Sleep(10);
|
||||
// }
|
||||
|
||||
// ::CloseWindow(hwnd_);
|
||||
// ::DestroyWindow(hwnd_);
|
||||
// }
|
||||
380
src/screen_capturer/windows/wgc_session_impl.cpp.bak
Normal file
380
src/screen_capturer/windows/wgc_session_impl.cpp.bak
Normal file
@@ -0,0 +1,380 @@
|
||||
#include "wgc_session_impl.h"
|
||||
|
||||
#include <Windows.Graphics.Capture.Interop.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "rd_log.h"
|
||||
|
||||
#define CHECK_INIT \
|
||||
if (!is_initialized_) { \
|
||||
std::cout << "AE_NEED_INIT" << std::endl; \
|
||||
return 4; \
|
||||
}
|
||||
|
||||
#define CHECK_CLOSED \
|
||||
if (cleaned_.load() == true) { \
|
||||
throw winrt::hresult_error(RO_E_CLOSED); \
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(
|
||||
::IDXGIDevice* dxgiDevice, ::IInspectable** graphicsDevice);
|
||||
}
|
||||
|
||||
WgcSessionImpl::WgcSessionImpl(int id) : id_(id) {}
|
||||
|
||||
WgcSessionImpl::~WgcSessionImpl() {
|
||||
Stop();
|
||||
CleanUp();
|
||||
}
|
||||
|
||||
void WgcSessionImpl::Release() { delete this; }
|
||||
|
||||
int WgcSessionImpl::Initialize(HWND hwnd) {
|
||||
std::lock_guard locker(lock_);
|
||||
|
||||
target_.hwnd = hwnd;
|
||||
target_.is_window = true;
|
||||
return Initialize();
|
||||
}
|
||||
|
||||
int WgcSessionImpl::Initialize(HMONITOR hmonitor) {
|
||||
std::lock_guard locker(lock_);
|
||||
|
||||
target_.hmonitor = hmonitor;
|
||||
target_.is_window = false;
|
||||
return Initialize();
|
||||
}
|
||||
|
||||
void WgcSessionImpl::RegisterObserver(wgc_session_observer* observer) {
|
||||
std::lock_guard locker(lock_);
|
||||
observer_ = observer;
|
||||
}
|
||||
|
||||
int WgcSessionImpl::Start() {
|
||||
std::lock_guard locker(lock_);
|
||||
|
||||
if (is_running_) return 0;
|
||||
|
||||
int error = 1;
|
||||
|
||||
CHECK_INIT;
|
||||
try {
|
||||
if (!capture_session_) {
|
||||
auto current_size = capture_item_.Size();
|
||||
capture_framepool_ =
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
|
||||
CreateFreeThreaded(d3d11_direct_device_,
|
||||
winrt::Windows::Graphics::DirectX::
|
||||
DirectXPixelFormat::B8G8R8A8UIntNormalized,
|
||||
2, current_size);
|
||||
capture_session_ = capture_framepool_.CreateCaptureSession(capture_item_);
|
||||
capture_frame_size_ = current_size;
|
||||
capture_framepool_trigger_ = capture_framepool_.FrameArrived(
|
||||
winrt::auto_revoke, {this, &WgcSessionImpl::OnFrame});
|
||||
capture_close_trigger_ = capture_item_.Closed(
|
||||
winrt::auto_revoke, {this, &WgcSessionImpl::OnClosed});
|
||||
}
|
||||
|
||||
if (!capture_framepool_) throw std::exception();
|
||||
|
||||
is_running_ = true;
|
||||
|
||||
// we do not need to crate a thread to enter a message loop coz we use
|
||||
// CreateFreeThreaded instead of Create to create a capture frame pool,
|
||||
// we need to test the performance later
|
||||
// loop_ = std::thread(std::bind(&WgcSessionImpl::message_func, this));
|
||||
|
||||
capture_session_.StartCapture();
|
||||
|
||||
capture_session_.IsCursorCaptureEnabled(false);
|
||||
|
||||
error = 0;
|
||||
} catch (winrt::hresult_error) {
|
||||
LOG_ERROR("Create WGC Capture Failed");
|
||||
return 86;
|
||||
} catch (...) {
|
||||
return 86;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int WgcSessionImpl::Stop() {
|
||||
std::lock_guard locker(lock_);
|
||||
|
||||
CHECK_INIT;
|
||||
|
||||
is_running_ = false;
|
||||
|
||||
// if (loop_.joinable()) loop_.join();
|
||||
|
||||
if (capture_framepool_trigger_) capture_framepool_trigger_.revoke();
|
||||
|
||||
if (capture_session_) {
|
||||
capture_session_.Close();
|
||||
capture_session_ = nullptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WgcSessionImpl::Pause() {
|
||||
std::lock_guard locker(lock_);
|
||||
|
||||
is_paused_ = true;
|
||||
|
||||
CHECK_INIT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WgcSessionImpl::Resume() {
|
||||
std::lock_guard locker(lock_);
|
||||
|
||||
is_paused_ = false;
|
||||
|
||||
CHECK_INIT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto WgcSessionImpl::CreateD3D11Device() {
|
||||
winrt::com_ptr<ID3D11Device> d3d_device;
|
||||
HRESULT hr;
|
||||
|
||||
WINRT_ASSERT(!d3d_device);
|
||||
|
||||
D3D_DRIVER_TYPE type = D3D_DRIVER_TYPE_HARDWARE;
|
||||
UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
|
||||
hr = D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0,
|
||||
D3D11_SDK_VERSION, d3d_device.put(), nullptr, nullptr);
|
||||
|
||||
if (DXGI_ERROR_UNSUPPORTED == hr) {
|
||||
// change D3D_DRIVER_TYPE
|
||||
type = D3D_DRIVER_TYPE_WARP;
|
||||
hr = D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0,
|
||||
D3D11_SDK_VERSION, d3d_device.put(), nullptr,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
winrt::check_hresult(hr);
|
||||
|
||||
winrt::com_ptr<::IInspectable> d3d11_device;
|
||||
winrt::check_hresult(CreateDirect3D11DeviceFromDXGIDevice(
|
||||
d3d_device.as<IDXGIDevice>().get(), d3d11_device.put()));
|
||||
return d3d11_device
|
||||
.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>();
|
||||
}
|
||||
|
||||
auto WgcSessionImpl::CreateCaptureItemForWindow(HWND hwnd) {
|
||||
auto activation_factory = winrt::get_activation_factory<
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
|
||||
auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
|
||||
interop_factory->CreateForWindow(
|
||||
hwnd,
|
||||
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
|
||||
reinterpret_cast<void**>(winrt::put_abi(item)));
|
||||
return item;
|
||||
}
|
||||
|
||||
auto WgcSessionImpl::CreateCaptureItemForMonitor(HMONITOR hmonitor) {
|
||||
auto activation_factory = winrt::get_activation_factory<
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
|
||||
auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
|
||||
interop_factory->CreateForMonitor(
|
||||
hmonitor,
|
||||
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
|
||||
reinterpret_cast<void**>(winrt::put_abi(item)));
|
||||
return item;
|
||||
}
|
||||
|
||||
HRESULT WgcSessionImpl::CreateMappedTexture(
|
||||
winrt::com_ptr<ID3D11Texture2D> src_texture, unsigned int width,
|
||||
unsigned int height) {
|
||||
D3D11_TEXTURE2D_DESC src_desc;
|
||||
src_texture->GetDesc(&src_desc);
|
||||
D3D11_TEXTURE2D_DESC map_desc;
|
||||
map_desc.Width = width == 0 ? src_desc.Width : width;
|
||||
map_desc.Height = height == 0 ? src_desc.Height : height;
|
||||
map_desc.MipLevels = src_desc.MipLevels;
|
||||
map_desc.ArraySize = src_desc.ArraySize;
|
||||
map_desc.Format = src_desc.Format;
|
||||
map_desc.SampleDesc = src_desc.SampleDesc;
|
||||
map_desc.Usage = D3D11_USAGE_STAGING;
|
||||
map_desc.BindFlags = 0;
|
||||
map_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
map_desc.MiscFlags = 0;
|
||||
|
||||
auto d3dDevice =
|
||||
GetDXGIInterfaceFromObject<ID3D11Device>(d3d11_direct_device_);
|
||||
|
||||
return d3dDevice->CreateTexture2D(&map_desc, nullptr,
|
||||
d3d11_texture_mapped_.put());
|
||||
}
|
||||
|
||||
void WgcSessionImpl::OnFrame(
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const& sender,
|
||||
[[maybe_unused]] winrt::Windows::Foundation::IInspectable const& args) {
|
||||
std::lock_guard locker(lock_);
|
||||
|
||||
auto is_new_size = false;
|
||||
|
||||
{
|
||||
auto frame = sender.TryGetNextFrame();
|
||||
auto frame_size = frame.ContentSize();
|
||||
|
||||
if (frame_size.Width != capture_frame_size_.Width ||
|
||||
frame_size.Height != capture_frame_size_.Height) {
|
||||
// The thing we have been capturing has changed size.
|
||||
// We need to resize our swap chain first, then blit the pixels.
|
||||
// After we do that, retire the frame and then recreate our frame pool.
|
||||
is_new_size = true;
|
||||
capture_frame_size_ = frame_size;
|
||||
}
|
||||
|
||||
// copy to mapped texture
|
||||
{
|
||||
if (is_paused_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto frame_captured =
|
||||
GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
|
||||
|
||||
if (!d3d11_texture_mapped_ || is_new_size)
|
||||
CreateMappedTexture(frame_captured);
|
||||
|
||||
d3d11_device_context_->CopyResource(d3d11_texture_mapped_.get(),
|
||||
frame_captured.get());
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE map_result;
|
||||
HRESULT hr = d3d11_device_context_->Map(
|
||||
d3d11_texture_mapped_.get(), 0, D3D11_MAP_READ,
|
||||
0 /*coz we use CreateFreeThreaded, so we cant use flags
|
||||
D3D11_MAP_FLAG_DO_NOT_WAIT*/
|
||||
,
|
||||
&map_result);
|
||||
if (FAILED(hr)) {
|
||||
OutputDebugStringW(
|
||||
(L"map resource failed: " + std::to_wstring(hr)).c_str());
|
||||
}
|
||||
|
||||
// copy data from map_result.pData
|
||||
if (map_result.pData && observer_) {
|
||||
observer_->OnFrame(
|
||||
wgc_session_frame{static_cast<unsigned int>(frame_size.Width),
|
||||
static_cast<unsigned int>(frame_size.Height),
|
||||
map_result.RowPitch,
|
||||
const_cast<const unsigned char*>(
|
||||
(unsigned char*)map_result.pData)},
|
||||
id_);
|
||||
}
|
||||
|
||||
d3d11_device_context_->Unmap(d3d11_texture_mapped_.get(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_new_size) {
|
||||
capture_framepool_.Recreate(d3d11_direct_device_,
|
||||
winrt::Windows::Graphics::DirectX::
|
||||
DirectXPixelFormat::B8G8R8A8UIntNormalized,
|
||||
2, capture_frame_size_);
|
||||
}
|
||||
}
|
||||
|
||||
void WgcSessionImpl::OnClosed(
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem const&,
|
||||
winrt::Windows::Foundation::IInspectable const&) {
|
||||
OutputDebugStringW(L"WgcSessionImpl::OnClosed");
|
||||
}
|
||||
|
||||
int WgcSessionImpl::Initialize() {
|
||||
if (is_initialized_) return 0;
|
||||
|
||||
if (!(d3d11_direct_device_ = CreateD3D11Device())) {
|
||||
std::cout << "AE_D3D_CREATE_DEVICE_FAILED" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
if (target_.is_window)
|
||||
capture_item_ = CreateCaptureItemForWindow(target_.hwnd);
|
||||
else
|
||||
capture_item_ = CreateCaptureItemForMonitor(target_.hmonitor);
|
||||
|
||||
// Set up
|
||||
auto d3d11_device =
|
||||
GetDXGIInterfaceFromObject<ID3D11Device>(d3d11_direct_device_);
|
||||
d3d11_device->GetImmediateContext(d3d11_device_context_.put());
|
||||
|
||||
} catch (winrt::hresult_error) {
|
||||
LOG_ERROR("AE_WGC_CREATE_CAPTURER_FAILED");
|
||||
return 86;
|
||||
} catch (...) {
|
||||
return 86;
|
||||
}
|
||||
|
||||
is_initialized_ = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WgcSessionImpl::CleanUp() {
|
||||
std::lock_guard locker(lock_);
|
||||
|
||||
auto expected = false;
|
||||
if (cleaned_.compare_exchange_strong(expected, true)) {
|
||||
capture_close_trigger_.revoke();
|
||||
capture_framepool_trigger_.revoke();
|
||||
|
||||
if (capture_framepool_) capture_framepool_.Close();
|
||||
|
||||
if (capture_session_) capture_session_.Close();
|
||||
|
||||
capture_framepool_ = nullptr;
|
||||
capture_session_ = nullptr;
|
||||
capture_item_ = nullptr;
|
||||
|
||||
is_initialized_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM w_param,
|
||||
LPARAM l_param) {
|
||||
return DefWindowProc(window, message, w_param, l_param);
|
||||
}
|
||||
|
||||
// void WgcSessionImpl::message_func() {
|
||||
// const std::wstring kClassName = L"am_fake_window";
|
||||
|
||||
// WNDCLASS wc = {};
|
||||
|
||||
// wc.style = CS_HREDRAW | CS_VREDRAW;
|
||||
// wc.lpfnWndProc = DefWindowProc;
|
||||
// wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
// wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW);
|
||||
// wc.lpszClassName = kClassName.c_str();
|
||||
|
||||
// if (!::RegisterClassW(&wc)) return;
|
||||
|
||||
// hwnd_ = ::CreateWindowW(kClassName.c_str(), nullptr, WS_OVERLAPPEDWINDOW,
|
||||
// 0,
|
||||
// 0, 0, 0, nullptr, nullptr, nullptr, nullptr);
|
||||
// MSG msg;
|
||||
// while (is_running_) {
|
||||
// while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||
// if (!is_running_) break;
|
||||
// TranslateMessage(&msg);
|
||||
// DispatchMessage(&msg);
|
||||
// }
|
||||
// Sleep(10);
|
||||
// }
|
||||
|
||||
// ::CloseWindow(hwnd_);
|
||||
// ::DestroyWindow(hwnd_);
|
||||
// }
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-06-14
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LAYOUT_STYLE_H_
|
||||
#define _LAYOUT_STYLE_H_
|
||||
|
||||
#define MENU_WINDOW_WIDTH_CN 300
|
||||
#define MENU_WINDOW_HEIGHT_CN 280
|
||||
#define LOCAL_WINDOW_WIDTH_CN 300
|
||||
#define LOCAL_WINDOW_HEIGHT_CN 280
|
||||
#define REMOTE_WINDOW_WIDTH_CN 300
|
||||
#define REMOTE_WINDOW_HEIGHT_CN 280
|
||||
#define MENU_WINDOW_WIDTH_EN 190
|
||||
#define MENU_WINDOW_HEIGHT_EN 245
|
||||
#define IPUT_WINDOW_WIDTH 160
|
||||
#define INPUT_WINDOW_PADDING_CN 66
|
||||
#define INPUT_WINDOW_PADDING_EN 96
|
||||
#define SETTINGS_WINDOW_WIDTH_CN 182
|
||||
#define SETTINGS_WINDOW_WIDTH_EN 248
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 275
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 275
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 100
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 167
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 100
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 167
|
||||
#define VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_CN 100
|
||||
#define VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_EN 167
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 100
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 167
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 151
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_CN 151
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_SRTP_CHECKBOX_PADDING_CN 151
|
||||
#define ENABLE_SRTP_CHECKBOX_PADDING_EN 218
|
||||
#define SETTINGS_SELECT_WINDOW_WIDTH 73
|
||||
#define SETTINGS_OK_BUTTON_PADDING_CN 55
|
||||
#define SETTINGS_OK_BUTTON_PADDING_EN 78
|
||||
|
||||
#endif
|
||||
2
thirdparty/minirtc
vendored
2
thirdparty/minirtc
vendored
Submodule thirdparty/minirtc updated: dd3f6df18b...40eaf93b42
34
xmake.lua
34
xmake.lua
@@ -120,26 +120,38 @@ target("device_controller")
|
||||
"src/device_controller/keyboard/linux", {public = true})
|
||||
end
|
||||
|
||||
target("thumbnail")
|
||||
set_kind("object")
|
||||
add_packages("libyuv", "openssl3")
|
||||
add_deps("rd_log", "common")
|
||||
add_files("src/thumbnail/*.cpp")
|
||||
add_includedirs("src/thumbnail", {public = true})
|
||||
|
||||
target("config_center")
|
||||
set_kind("object")
|
||||
add_deps("rd_log")
|
||||
add_files("src/config_center/*.cpp")
|
||||
add_includedirs("src/config_center", {public = true})
|
||||
|
||||
target("localization")
|
||||
target("assets")
|
||||
set_kind("headeronly")
|
||||
add_includedirs("src/localization", {public = true})
|
||||
add_includedirs("src/gui/assets/localization",
|
||||
"src/gui/assets/fonts",
|
||||
"src/gui/assets/icons",
|
||||
"src/gui/assets/layouts", {public = true})
|
||||
|
||||
target("single_window")
|
||||
target("gui")
|
||||
set_kind("object")
|
||||
add_packages("libyuv", "openssl3")
|
||||
add_deps("rd_log", "common", "localization", "config_center", "minirtc",
|
||||
"path_manager", "screen_capturer", "speaker_capturer", "device_controller")
|
||||
add_files("src/single_window/*.cpp")
|
||||
add_includedirs("src/single_window", {public = true})
|
||||
add_includedirs("fonts", {public = true})
|
||||
add_packages("libyuv")
|
||||
add_deps("rd_log", "common", "assets", "config_center", "minirtc",
|
||||
"path_manager", "screen_capturer", "speaker_capturer",
|
||||
"device_controller", "thumbnail")
|
||||
add_files("src/gui/*.cpp", "src/gui/panels/*.cpp", "src/gui/toolbars/*.cpp",
|
||||
"src/gui/windows/*.cpp")
|
||||
add_includedirs("src/gui", "src/gui/panels", "src/gui/toolbars",
|
||||
"src/gui/windows", {public = true})
|
||||
|
||||
target("crossdesk")
|
||||
set_kind("binary")
|
||||
add_deps("rd_log", "common", "single_window")
|
||||
add_files("src/gui/main.cpp")
|
||||
add_deps("rd_log", "common", "gui")
|
||||
add_files("src/app/main.cpp")
|
||||
Reference in New Issue
Block a user