mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2025-12-12 01:00:29 +08:00
Compare commits
27 Commits
build-2509
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96a6e7edcd | ||
|
|
50c3e6a32a | ||
|
|
c8305cc65d | ||
|
|
aae4e936db | ||
|
|
45a04f7570 | ||
|
|
53ba69f4aa | ||
|
|
53229a9055 | ||
|
|
f97df0d830 | ||
|
|
8ed5e4abc3 | ||
|
|
1e727ddc1b | ||
|
|
da84a6d09f | ||
|
|
9c35c68eda | ||
|
|
651f9a4f4e | ||
|
|
7777f5e490 | ||
|
|
3ab5e2b431 | ||
|
|
65874c6b43 | ||
|
|
67b943c151 | ||
|
|
593de19df5 | ||
|
|
5296e61281 | ||
|
|
1729badc55 | ||
|
|
9373790f37 | ||
|
|
edb9112435 | ||
|
|
0328163a9e | ||
|
|
0c9d94e1c5 | ||
|
|
d4bd94cb8a | ||
|
|
e7c891353b | ||
|
|
3f8a9e3b2c |
27
.github/workflows/build_img.yaml
vendored
27
.github/workflows/build_img.yaml
vendored
@ -15,6 +15,8 @@ on:
|
||||
- vm
|
||||
- e900v22c
|
||||
- octopus-flanet
|
||||
- orangepi-zero
|
||||
- oec-turbo
|
||||
- all
|
||||
create_release:
|
||||
description: 'Create GitHub Release'
|
||||
@ -48,6 +50,29 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Inject TURN config (optional)
|
||||
if: ${{ env.TURN_HOST != '' }}
|
||||
run: |
|
||||
mkdir -p configs/kvmd/override.d
|
||||
cat > configs/kvmd/override.d/turn.yaml <<EOF
|
||||
janus:
|
||||
stun:
|
||||
host: ${TURN_HOST}
|
||||
port: ${TURN_PORT}
|
||||
local_ice_servers:
|
||||
- urls:
|
||||
- "stun:${TURN_HOST}:${TURN_PORT}"
|
||||
- "turn:${TURN_HOST}:${TURN_PORT}?transport=udp"
|
||||
- "turn:${TURN_HOST}:${TURN_PORT}?transport=tcp"
|
||||
username: "${TURN_USER}"
|
||||
credential: "${TURN_PASS}"
|
||||
EOF
|
||||
env:
|
||||
TURN_HOST: ${{ secrets.TURN_HOST }}
|
||||
TURN_PORT: ${{ secrets.TURN_PORT }}
|
||||
TURN_USER: ${{ secrets.TURN_USER }}
|
||||
TURN_PASS: ${{ secrets.TURN_PASS }}
|
||||
|
||||
- name: Set build environment
|
||||
id: build_env
|
||||
shell: bash
|
||||
@ -182,4 +207,4 @@ jobs:
|
||||
if [ "${{ github.event.inputs.create_release }}" = "true" ]; then
|
||||
echo "| **Release** | [${{ env.RELEASE_TAG }}](${{ steps.release.outputs.url }}) |" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
48
.github/workflows/docker-build.yaml
vendored
48
.github/workflows/docker-build.yaml
vendored
@ -47,6 +47,29 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Inject TURN config (optional)
|
||||
if: ${{ env.TURN_HOST != '' }}
|
||||
run: |
|
||||
mkdir -p configs/kvmd/override.d
|
||||
cat > configs/kvmd/override.d/turn.yaml <<EOF
|
||||
janus:
|
||||
stun:
|
||||
host: ${TURN_HOST}
|
||||
port: ${TURN_PORT}
|
||||
local_ice_servers:
|
||||
- urls:
|
||||
- "stun:${TURN_HOST}:${TURN_PORT}"
|
||||
- "turn:${TURN_HOST}:${TURN_PORT}?transport=udp"
|
||||
- "turn:${TURN_HOST}:${TURN_PORT}?transport=tcp"
|
||||
username: "${TURN_USER}"
|
||||
credential: "${TURN_PASS}"
|
||||
EOF
|
||||
env:
|
||||
TURN_HOST: ${{ secrets.TURN_HOST }}
|
||||
TURN_PORT: ${{ secrets.TURN_PORT }}
|
||||
TURN_USER: ${{ secrets.TURN_USER }}
|
||||
TURN_PASS: ${{ secrets.TURN_PASS }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
@ -117,6 +140,29 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Inject TURN config (optional)
|
||||
if: ${{ env.TURN_HOST != '' }}
|
||||
run: |
|
||||
mkdir -p configs/kvmd/override.d
|
||||
cat > configs/kvmd/override.d/turn.yaml <<EOF
|
||||
janus:
|
||||
stun:
|
||||
host: ${TURN_HOST}
|
||||
port: ${TURN_PORT}
|
||||
local_ice_servers:
|
||||
- urls:
|
||||
- "stun:${TURN_HOST}:${TURN_PORT}"
|
||||
- "turn:${TURN_HOST}:${TURN_PORT}?transport=udp"
|
||||
- "turn:${TURN_HOST}:${TURN_PORT}?transport=tcp"
|
||||
username: "${TURN_USER}"
|
||||
credential: "${TURN_PASS}"
|
||||
EOF
|
||||
env:
|
||||
TURN_HOST: ${{ secrets.TURN_HOST }}
|
||||
TURN_PORT: ${{ secrets.TURN_PORT }}
|
||||
TURN_USER: ${{ secrets.TURN_USER }}
|
||||
TURN_PASS: ${{ secrets.TURN_PASS }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
@ -191,4 +237,4 @@ jobs:
|
||||
echo "- **Platforms**: ${{ github.event.inputs.platforms }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Aliyun Enabled**: ${{ github.event.inputs.enable_aliyun }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Tags**:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "${{ steps.meta.outputs.tags }}" | sed 's/^/ - /' >> $GITHUB_STEP_SUMMARY
|
||||
echo "${{ steps.meta.outputs.tags }}" | sed 's/^/ - /' >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
59
README.md
59
README.md
@ -11,8 +11,8 @@
|
||||
[](https://github.com/mofeng-git/One-KVM/blob/master/LICENSE)
|
||||
|
||||
<p>
|
||||
<a href="https://one-kvm.mofeng.run">📖 详细文档</a> •
|
||||
<a href="https://kvmd-demo.mofeng.run">🚀 在线演示</a> •
|
||||
<a href="https://docs.one-kvm.cn">📖 详细文档</a> •
|
||||
<a href="https://demo.one-kvm.cn/">🚀 在线演示</a> •
|
||||
<a href="#快速开始">⚡ 快速开始</a> •
|
||||
<a href="#功能介绍">📊 功能介绍</a>
|
||||
</p>
|
||||
@ -32,6 +32,8 @@
|
||||
|
||||
**One-KVM** 是基于开源 [PiKVM](https://github.com/pikvm/pikvm) 项目进行二次开发的 DIY IP-KVM 解决方案。该方案利用成本较低的硬件设备,实现 BIOS 级别的远程服务器或工作站管理功能。
|
||||
|
||||
> 本项目目前并无适配树莓派的计划。这是因为树莓派平台本质上属于 PiKVM 官方硬件生态和盈利的一部分。我们非常尊重和感谢上游项目 PiKVM ,因此 One-KVM 的设备适配主要聚焦于补充性场景,尽量避免与 PiKVM 官方产品产生重叠,以支持其可持续发展。
|
||||
|
||||
### 应用场景
|
||||
|
||||
- **家庭实验室主机管理** - 远程管理服务器和开发设备
|
||||
@ -68,7 +70,7 @@
|
||||
|:--------:|:-------:|:-----:|:------:|:------:|
|
||||
| 简体中文 WebUI | ✅ | ❌ | ✅ | ✅ |
|
||||
| 远程视频流 | MJPEG/H.264 | MJPEG/H.264 | MJPEG/H.264 | MJPEG/H.264 |
|
||||
| H.264 视频编码 | CPU | GPU | 未知 | GPU |
|
||||
| H.264 视频编码 | CPU/GPU | GPU | 未知 | GPU |
|
||||
| 远程音频流 | ✅ | ✅ | ✅ | ✅ |
|
||||
| 远程鼠键控制 | OTG/CH9329 | OTG/CH9329/Pico/Bluetooth | OTG | OTG |
|
||||
| VNC 控制 | ✅ | ✅ | ✅ | ✅ |
|
||||
@ -87,7 +89,7 @@ Docker 版本支持 OTG 或 CH9329 作为虚拟 HID,兼容 amd64、arm64、arm
|
||||
#### 一键脚本部署
|
||||
|
||||
```bash
|
||||
curl -sSL https://one-kvm.mofeng.run/quick_start.sh -o quick_start.sh && bash quick_start.sh
|
||||
curl -sSL https://docs.one-kvm.cn/quick_start.sh -o quick_start.sh && bash quick_start.sh
|
||||
```
|
||||
|
||||
#### 手动部署
|
||||
@ -157,6 +159,7 @@ sudo docker run --name kvmd -itd \
|
||||
- **GitHub Releases:** [https://github.com/mofeng-git/One-KVM/releases](https://github.com/mofeng-git/One-KVM/releases)
|
||||
|
||||
**其他下载方式:**
|
||||
- **免登录高速下载:** [http://sd1.files.one-kvm.cn/](http://sd1.files.one-kvm.cn/)(由群友赞助,支持直链,接入 EdgeOne CDN,建议使用多线程下载工具下载获取最高速度)
|
||||
- **免登录下载:** [https://pan.huang1111.cn/s/mxkx3T1](https://pan.huang1111.cn/s/mxkx3T1) (由 Huang1111公益计划 提供)
|
||||
- **百度网盘:** [https://pan.baidu.com/s/166-2Y8PBF4SbHXFkGmFJYg?pwd=o9aj](https://pan.baidu.com/s/166-2Y8PBF4SbHXFkGmFJYg?pwd=o9aj) (提取码:o9aj)
|
||||
|
||||
@ -170,19 +173,7 @@ sudo docker run --name kvmd -itd \
|
||||
| Virtualbox | Virtualbox-uefi | USB 采集卡、CH9329 | 241004 | ✅ |
|
||||
| s905l3a 通用包 | E900v22c | USB 采集卡、OTG | 241004 | ✅ |
|
||||
| 我家云 | Chainedbox | USB 采集卡、OTG | 241004 | ✅ |
|
||||
| 龙芯久久派 | 2k0300 | USB 采集卡、CH9329 | 241025 | ✅ |
|
||||
|
||||
## 🤝 贡献指南
|
||||
|
||||
欢迎各种形式的贡献!
|
||||
|
||||
### 如何贡献
|
||||
|
||||
1. **Fork 本仓库**
|
||||
2. **创建功能分支:** `git checkout -b feature/AmazingFeature`
|
||||
3. **提交更改:** `git commit -m 'Add some AmazingFeature'`
|
||||
4. **推送到分支:** `git push origin feature/AmazingFeature`
|
||||
5. **提交 Pull Request**
|
||||
| 龙芯久久派 | 2k0300 | USB 采集卡、CH9329 | 241025 | ❌ |
|
||||
|
||||
### 报告问题
|
||||
|
||||
@ -280,6 +271,30 @@ sudo docker run --name kvmd -itd \
|
||||
|
||||
- 爱发电用户_e3202
|
||||
|
||||
- 一语念白
|
||||
|
||||
- 云边
|
||||
|
||||
- 爱发电用户_5a711
|
||||
|
||||
- 爱发电用户_9a706
|
||||
|
||||
- T0m9ir1SUKI
|
||||
|
||||
- 爱发电用户_56d52
|
||||
|
||||
- 爱发电用户_3N6F
|
||||
|
||||
- DUSK
|
||||
|
||||
- 飘零
|
||||
|
||||
- .
|
||||
|
||||
- 饭太稀
|
||||
|
||||
- 葱
|
||||
|
||||
- ......
|
||||
|
||||
</details>
|
||||
@ -296,10 +311,18 @@ sudo docker run --name kvmd -itd \
|
||||
**文件存储服务:**
|
||||
- **[Huang1111公益计划](https://pan.huang1111.cn/s/mxkx3T1)** - 提供免登录下载服务
|
||||
|
||||
**云服务商**
|
||||
|
||||
- **[林枫云](https://www.dkdun.cn)** - 赞助了本项目宁波大带宽服务器
|
||||
|
||||

|
||||
|
||||
林枫云主营国内外地域的精品线路业务服务器、高主频游戏服务器和大带宽服务器。
|
||||
|
||||
## 📚 其他
|
||||
|
||||
### 使用的开源项目
|
||||
|
||||
本项目基于以下优秀开源项目进行二次开发:
|
||||
|
||||
- [PiKVM](https://github.com/pikvm/pikvm) - 开源的 DIY IP-KVM 解决方案
|
||||
- [PiKVM](https://github.com/pikvm/pikvm) - 开源的 DIY IP-KVM 解决方案
|
||||
|
||||
@ -68,8 +68,9 @@ RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/' /etc/apt/sources.lis
|
||||
;; \
|
||||
arm64) \
|
||||
apt-get install -y --no-install-recommends \
|
||||
v4l-utils libv4l-0 \
|
||||
libstdc++6 \
|
||||
v4l-utils libv4l-0 libavutil57 \
|
||||
libstdc++6 libavcodec59 libavformat59 \
|
||||
libswscale6 libavfilter8 libavdevice59 \
|
||||
libva2 libva-drm2 libva-x11-2 \
|
||||
libvdpau1 ocl-icd-libopencl1 \
|
||||
;; \
|
||||
@ -86,6 +87,18 @@ RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/' /etc/apt/sources.lis
|
||||
fi \
|
||||
&& curl https://github.com/tsl0922/ttyd/releases/download/1.7.7/ttyd.$ARCH -L -o /usr/local/bin/ttyd \
|
||||
&& chmod +x /usr/local/bin/ttyd \
|
||||
&& mkdir -p /tmp/gostc && cd /tmp/gostc \
|
||||
&& case ${TARGETARCH} in \
|
||||
amd64) GOSTC_ARCH=amd64_v1 ;; \
|
||||
arm) GOSTC_ARCH=arm_7 ;; \
|
||||
arm64) GOSTC_ARCH=arm64_v8.0 ;; \
|
||||
*) echo "Unsupported architecture for gostc: ${TARGETARCH}" && exit 1 ;; \
|
||||
esac \
|
||||
&& curl -L https://github.com/mofeng-git/gostc-open/releases/download/v2.0.8-beta.2/gostc_linux_${GOSTC_ARCH}.tar.gz -o gostc.tar.gz \
|
||||
&& tar -xzf gostc.tar.gz \
|
||||
&& mv gostc /usr/bin/ \
|
||||
&& chmod +x /usr/bin/gostc \
|
||||
&& cd / && rm -rf /tmp/gostc \
|
||||
&& adduser kvmd --gecos "" --disabled-password \
|
||||
&& ln -sf /usr/share/tesseract-ocr/*/tessdata /usr/share/tessdata \
|
||||
&& mkdir -p /etc/kvmd_backup/override.d \
|
||||
@ -97,6 +110,8 @@ RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/' /etc/apt/sources.lis
|
||||
/run/kvmd \
|
||||
/tmp/kvmd-nginx \
|
||||
&& touch /run/kvmd/ustreamer.sock \
|
||||
&& groupadd kvmd-selfauth \
|
||||
&& usermod -a -G kvmd-selfauth root \
|
||||
&& apt clean \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& rm -rf /tmp/lib /tmp/wheel \
|
||||
|
||||
@ -54,16 +54,13 @@ RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/' /etc/apt/sources.lis
|
||||
mesa-vdpau-drivers \
|
||||
v4l-utils \
|
||||
libv4l-dev \
|
||||
&& if [ ${TARGETARCH} != arm64 ]; then \
|
||||
apt-get install -y --no-install-recommends \
|
||||
ffmpeg \
|
||||
libavcodec-dev \
|
||||
libavformat-dev \
|
||||
libavutil-dev \
|
||||
libswscale-dev \
|
||||
libavfilter-dev \
|
||||
libavdevice-dev; \
|
||||
fi \
|
||||
ffmpeg \
|
||||
libavcodec-dev \
|
||||
libavformat-dev \
|
||||
libavutil-dev \
|
||||
libswscale-dev \
|
||||
libavfilter-dev \
|
||||
libavdevice-dev \
|
||||
&& if [ ${TARGETARCH} != arm ] && [ ${TARGETARCH} != arm64 ]; then \
|
||||
apt-get install -y --no-install-recommends \
|
||||
vainfo \
|
||||
@ -116,63 +113,76 @@ RUN git clone --depth=1 https://github.com/gvalkov/python-evdev.git /tmp/python-
|
||||
&& python3 setup.py bdist_wheel --dist-dir /tmp/wheel/ \
|
||||
&& rm -rf /tmp/python-evdev
|
||||
|
||||
# 编译安装 libnice、libsrtp、libwebsockets 和 janus-gateway
|
||||
RUN git clone --depth=1 https://gitlab.freedesktop.org/libnice/libnice /tmp/libnice \
|
||||
# 编译安装 libnice、libsrtp、libwebsockets 和 janus-gateway(显式 Release 与按架构优化)
|
||||
RUN export COMMON_CFLAGS='-O2 -pipe -fPIC -fstack-protector-strong -D_FORTIFY_SOURCE=2' \
|
||||
&& if [ "${TARGETARCH}" = arm64 ]; then export CFLAGS="$COMMON_CFLAGS -march=armv8-a"; \
|
||||
elif [ "${TARGETARCH}" = arm ]; then export CFLAGS="$COMMON_CFLAGS -march=armv7-a -mfpu=neon-vfpv4 -mfloat-abi=hard -mtune=cortex-a7"; \
|
||||
else export CFLAGS="$COMMON_CFLAGS -march=x86-64 -mtune=generic"; fi \
|
||||
&& export CXXFLAGS="$CFLAGS" LDFLAGS="-Wl,-O1 -Wl,--as-needed" \
|
||||
&& git clone --depth=1 https://gitlab.freedesktop.org/libnice/libnice /tmp/libnice \
|
||||
&& cd /tmp/libnice \
|
||||
&& meson --prefix=/usr build && ninja -C build && ninja -C build install \
|
||||
&& meson setup build --prefix=/usr --buildtype=release -Doptimization=2 -Dc_args="$CFLAGS" -Dcpp_args="$CXXFLAGS" \
|
||||
&& ninja -C build && ninja -C build install \
|
||||
&& rm -rf /tmp/libnice \
|
||||
&& curl https://github.com/cisco/libsrtp/archive/v2.2.0.tar.gz -L -o /tmp/libsrtp-2.2.0.tar.gz \
|
||||
&& cd /tmp \
|
||||
&& tar xf libsrtp-2.2.0.tar.gz \
|
||||
&& cd libsrtp-2.2.0 \
|
||||
&& ./configure --prefix=/usr --enable-openssl \
|
||||
&& CFLAGS="$CFLAGS" CXXFLAGS="$CXXFLAGS" ./configure --prefix=/usr --enable-openssl \
|
||||
&& make shared_library -j$(nproc) && make install \
|
||||
&& cd /tmp \
|
||||
&& rm -rf /tmp/libsrtp* \
|
||||
&& git clone --depth=1 https://github.com/warmcat/libwebsockets /tmp/libwebsockets \
|
||||
&& cd /tmp/libwebsockets \
|
||||
&& mkdir build && cd build \
|
||||
&& cmake -DLWS_MAX_SMP=1 -DLWS_WITHOUT_EXTENSIONS=0 -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_C_FLAGS="-fpic" .. \
|
||||
&& cmake -DLWS_MAX_SMP=1 -DLWS_WITHOUT_EXTENSIONS=0 -DCMAKE_INSTALL_PREFIX:PATH=/usr \
|
||||
-DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS_RELEASE="$CFLAGS -fPIC" -DCMAKE_CXX_FLAGS_RELEASE="$CXXFLAGS -fPIC" .. \
|
||||
&& make -j$(nproc) && make install \
|
||||
&& cd /tmp \
|
||||
&& rm -rf /tmp/libwebsockets \
|
||||
&& git clone --depth=1 https://github.com/meetecho/janus-gateway.git /tmp/janus-gateway \
|
||||
&& cd /tmp/janus-gateway \
|
||||
&& sh autogen.sh \
|
||||
&& ./configure --enable-static --enable-websockets --enable-plugin-audiobridge \
|
||||
&& CFLAGS="$CFLAGS" CXXFLAGS="$CXXFLAGS" ./configure --enable-static --enable-websockets --enable-plugin-audiobridge \
|
||||
--disable-data-channels --disable-rabbitmq --disable-mqtt --disable-all-plugins \
|
||||
--disable-all-loggers --prefix=/usr \
|
||||
&& make -j$(nproc) && make install \
|
||||
&& cd /tmp \
|
||||
&& rm -rf /tmp/janus-gateway
|
||||
|
||||
# 编译 Rockchip MPP、RGA 和 FFmpeg(仅 arm64)
|
||||
# 编译 Rockchip MPP、RGA(仅 arm64,显式 Release 与按架构优化)
|
||||
RUN if [ ${TARGETARCH} = arm64 ]; then \
|
||||
git clone -b jellyfin-mpp --depth=1 https://github.com/nyanmisaka/mpp.git /tmp/rkmpp \
|
||||
export COMMON_CFLAGS='-O2 -pipe -fPIC -fstack-protector-strong -D_FORTIFY_SOURCE=2' \
|
||||
&& export CFLAGS="$COMMON_CFLAGS -march=armv8-a" \
|
||||
&& export CXXFLAGS="$CFLAGS" \
|
||||
&& git clone --depth=1 https://github.com/rockchip-linux/mpp.git /tmp/rkmpp \
|
||||
&& mkdir -p /tmp/rkmpp/rkmpp_build && cd /tmp/rkmpp/rkmpp_build \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DBUILD_TEST=OFF .. \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DBUILD_TEST=OFF \
|
||||
-DCMAKE_C_FLAGS_RELEASE="$CFLAGS" -DCMAKE_CXX_FLAGS_RELEASE="$CXXFLAGS" .. \
|
||||
&& make -j$(nproc) \
|
||||
&& make install \
|
||||
&& git clone -b jellyfin-rga --depth=1 https://github.com/nyanmisaka/rk-mirrors.git /tmp/rkrga \
|
||||
&& cd /tmp/ \
|
||||
&& meson setup rkrga rkrga_build --prefix=/usr --libdir=lib --buildtype=release -Dcpp_args=-fpermissive -Dlibdrm=false -Dlibrga_demo=false \
|
||||
&& meson setup rkrga rkrga_build --prefix=/usr --libdir=lib --buildtype=release -Doptimization=2 \
|
||||
-Dc_args="$CFLAGS" -Dcpp_args="$CXXFLAGS -fpermissive" -Dlibdrm=false -Dlibrga_demo=false \
|
||||
&& meson configure rkrga_build > /dev/null \
|
||||
&& ninja -C rkrga_build install \
|
||||
&& git clone --depth=1 https://github.com/nyanmisaka/ffmpeg-rockchip.git /tmp/ffmpeg-rockchip \
|
||||
&& cd /tmp/ffmpeg-rockchip \
|
||||
&& ./configure --prefix=/usr --enable-gpl --enable-version3 --enable-libdrm --enable-rkmpp --enable-rkrga \
|
||||
--enable-libv4l2 --enable-libx264 --enable-shared --disable-programs \
|
||||
--disable-doc --disable-htmlpages --disable-manpages --disable-network --disable-protocols \
|
||||
--disable-decoders --disable-debug --disable-alsa \
|
||||
&& make -j$(nproc) \
|
||||
&& make install \
|
||||
&& rm -rf /tmp/rkmpp /tmp/rkrga /tmp/ffmpeg-rockchip; \
|
||||
&& rm -rf /tmp/rkmpp /tmp/rkrga; \
|
||||
fi
|
||||
|
||||
# 编译 ustreamer
|
||||
# 编译 ustreamer(按架构优化)
|
||||
RUN sed --in-place --expression 's|^#include "refcount.h"$|#include "../refcount.h"|g' /usr/include/janus/plugins/plugin.h \
|
||||
&& git clone --depth=1 https://github.com/mofeng-git/ustreamer /tmp/ustreamer \
|
||||
&& make -j$(nproc) WITH_PYTHON=1 WITH_JANUS=1 WITH_FFMPEG=1 -C /tmp/ustreamer \
|
||||
&& export COMMON_CFLAGS='-O2 -pipe -fPIC -fstack-protector-strong -D_FORTIFY_SOURCE=2' \
|
||||
&& if [ "${TARGETARCH}" = arm64 ]; then export CFLAGS="$COMMON_CFLAGS -march=armv8-a"; \
|
||||
elif [ "${TARGETARCH}" = arm ]; then export CFLAGS="$COMMON_CFLAGS -march=armv7-a -mfpu=neon-vfpv4 -mfloat-abi=hard -mtune=cortex-a7"; \
|
||||
else export CFLAGS="$COMMON_CFLAGS -march=x86-64 -mtune=generic"; fi \
|
||||
&& export CXXFLAGS="$CFLAGS" \
|
||||
&& if [ ${TARGETARCH} = arm64 ]; then \
|
||||
make -j$(nproc) CFLAGS="$CFLAGS" WITH_PYTHON=1 WITH_JANUS=1 WITH_FFMPEG=1 WITH_MPP=1 WITH_DRM=1 -C /tmp/ustreamer; \
|
||||
else \
|
||||
make -j$(nproc) CFLAGS="$CFLAGS" WITH_PYTHON=1 WITH_JANUS=1 WITH_FFMPEG=1 WITH_DRM=1 -C /tmp/ustreamer; \
|
||||
fi \
|
||||
&& /tmp/ustreamer/ustreamer -v \
|
||||
&& /tmp/ustreamer/ustreamer-dump -v \
|
||||
&& cp /tmp/ustreamer/python/dist/*.whl /tmp/wheel/
|
||||
@ -185,10 +195,8 @@ RUN mkdir /tmp/lib \
|
||||
/tmp/lib/ \
|
||||
&& find /usr/lib -name "libsrtp2.so.*" -exec cp {} /tmp/lib/ \; \
|
||||
&& find /usr/lib -name "libwebsockets.so.*" -exec cp {} /tmp/lib/ \; \
|
||||
&& if [ ${TARGETARCH} = arm64 ]; then \
|
||||
find /usr/lib -name "libav*.so.*" -exec cp {} /tmp/lib/ \; \
|
||||
&& find /usr/lib -name "libsw*.so.*" -exec cp {} /tmp/lib/ \; \
|
||||
&& find /usr/lib -name "libpostproc.so.*" -exec cp {} /tmp/lib/ \; \
|
||||
&& find /usr/lib -name "librockchip*" -exec cp {} /tmp/lib/ \; \
|
||||
&& find /usr/lib -name "librga.so.*" -exec cp {} /tmp/lib/ \; ; \
|
||||
fi
|
||||
&& [ "${TARGETARCH}" = "arm64" ] && \
|
||||
find /usr/lib -name "libsw*.so.*" -exec cp {} /tmp/lib/ \; && \
|
||||
find /usr/lib -name "libpostproc.so.*" -exec cp {} /tmp/lib/ \; && \
|
||||
find /usr/lib -name "librockchip*" -exec cp {} /tmp/lib/ \; && \
|
||||
find /usr/lib -name "librga.so.*" -exec cp {} /tmp/lib/ \; || true
|
||||
|
||||
@ -101,6 +101,20 @@ build_target() {
|
||||
onecloud_pro_rootfs
|
||||
local arch="aarch64"
|
||||
local device_type="gpio-onecloud-pro video1"
|
||||
local network_type="systemd-networkd"
|
||||
NEED_PREPARE_DNS=true
|
||||
;;
|
||||
orangepi-zero)
|
||||
orangepizero_rootfs
|
||||
local arch="armhf"
|
||||
local device_type=""
|
||||
local network_type=""
|
||||
NEED_PREPARE_DNS=true
|
||||
;;
|
||||
oec-turbo)
|
||||
oec_turbo_rootfs
|
||||
local arch="aarch64"
|
||||
local device_type="vpu"
|
||||
local network_type=""
|
||||
NEED_PREPARE_DNS=true
|
||||
;;
|
||||
@ -140,6 +154,12 @@ build_target() {
|
||||
onecloud-pro)
|
||||
pack_img "Onecloud-Pro"
|
||||
;;
|
||||
orangepi-zero)
|
||||
pack_img "Orangepi-Zero"
|
||||
;;
|
||||
oec-turbo)
|
||||
pack_img "OEC-Turbo"
|
||||
;;
|
||||
*)
|
||||
echo "错误:未知的打包类型 for '$target'" >&2
|
||||
;;
|
||||
@ -158,7 +178,7 @@ build_target() {
|
||||
# 检查是否提供了目标参数
|
||||
if [ -z "$1" ]; then
|
||||
echo "用法: $0 <target|all>"
|
||||
echo "可用目标: onecloud, cumebox2, chainedbox, vm, e900v22c, octopus-flanet, onecloud-pro"
|
||||
echo "可用目标: onecloud, cumebox2, chainedbox, vm, e900v22c, octopus-flanet, onecloud-pro, orangepi-zero, oec-turbo"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@ -178,6 +198,8 @@ if [ "$1" = "all" ]; then
|
||||
build_target "e900v22c"
|
||||
build_target "octopus-flanet"
|
||||
build_target "onecloud-pro"
|
||||
build_target "orangepi-zero"
|
||||
build_target "oec-turbo"
|
||||
echo "信息:所有目标构建完成。"
|
||||
else
|
||||
build_target "$1"
|
||||
|
||||
@ -214,6 +214,12 @@ octopus_flanet_rootfs() {
|
||||
sudo losetup --offset "$boot_offset" "$bootfs_loopdev" "$target_image" || { echo "错误:设置 boot 分区 loop 设备 $bootfs_loopdev 失败" >&2; exit 1; }
|
||||
sudo mount "$bootfs_loopdev" "$BOOTFS" || { echo "错误:挂载 boot 分区 ($bootfs_loopdev) 失败" >&2; exit 1; }
|
||||
BOOTFS_MOUNTED=1
|
||||
|
||||
# 自动下载 Octopus-Planet 相关文件
|
||||
local dtb_file="$SRCPATH/image/octopus-flanet/meson-gxm-octopus-planet.dtb"
|
||||
download_file_if_missing "$dtb_file" || echo "警告:下载 Octopus-Planet DTB 失败"
|
||||
sudo cp "$dtb_file" "$BOOTFS/dtb/amlogic/meson-gxm-octopus-planet.dtb" || echo "警告:复制 Octopus-Planet DTB 失败"
|
||||
|
||||
sudo sed -i "s/meson-gxm-octopus-planet.dtb/meson-gxm-khadas-vim2.dtb/g" "$BOOTFS/uEnv.txt" || { echo "错误:修改 uEnv.txt 失败" >&2; exit 1; }
|
||||
sudo umount "$BOOTFS" || { echo "警告:卸载 boot 分区 ($BOOTFS) 失败" >&2; BOOTFS_MOUNTED=0; }
|
||||
BOOTFS_MOUNTED=0
|
||||
@ -266,6 +272,40 @@ onecloud_pro_rootfs() {
|
||||
echo "信息:Octopus-Planet Rootfs 准备完成,loop 设备 $LOOPDEV 已就绪。"
|
||||
}
|
||||
|
||||
orangepizero_rootfs() {
|
||||
local source_image="$SRCPATH/image/orangepi-zero/Armbian_community_25.11.0-trunk.208_Orangepizero_bookworm_current_6.12.47_minimal.img"
|
||||
local target_image="$TMPDIR/rootfs.img"
|
||||
local offset=$((8192 * 512))
|
||||
local add_size_mb=600
|
||||
|
||||
echo "信息:准备 Orange Pi Zero Rootfs..."
|
||||
ensure_dir "$TMPDIR"
|
||||
|
||||
echo "信息:下载或使用本地 Orange Pi Zero 原始镜像..."
|
||||
download_file_if_missing "$source_image" || { echo "错误:下载 Orange Pi Zero 原始镜像失败" >&2; exit 1; }
|
||||
|
||||
cp "$source_image" "$target_image" || { echo "错误:复制 Orange Pi Zero 原始镜像失败" >&2; exit 1; }
|
||||
|
||||
echo "信息:扩展镜像文件 (${add_size_mb}MB)..."
|
||||
sudo dd if=/dev/zero bs=1M count="$add_size_mb" >> "$target_image" || { echo "错误:扩展镜像文件失败" >&2; exit 1; }
|
||||
|
||||
echo "信息:调整镜像分区大小..."
|
||||
sudo parted -s "$target_image" resizepart 1 100% || { echo "错误:使用 parted 调整分区大小失败" >&2; exit 1; }
|
||||
|
||||
find_loop_device
|
||||
sudo losetup -P "$LOOPDEV" "$target_image" || { echo "错误:设置 loop 设备失败" >&2; exit 1; }
|
||||
|
||||
echo "信息:检查并调整文件系统大小..."
|
||||
sudo e2fsck -y -f "${LOOPDEV}p1" || { echo "错误:文件系统检查失败" >&2; exit 1; }
|
||||
sudo resize2fs "${LOOPDEV}p1" || { echo "错误:调整文件系统大小失败" >&2; exit 1; }
|
||||
|
||||
# 重新设置 LOOPDEV 为分区
|
||||
sudo losetup -d "$LOOPDEV"
|
||||
sudo losetup "$LOOPDEV" "$target_image" -o "$offset" || { echo "错误:重新设置 loop 设备失败" >&2; exit 1; }
|
||||
|
||||
echo "信息:Orange Pi Zero Rootfs 准备完成。"
|
||||
}
|
||||
|
||||
# --- 特定设备的文件配置函数 ---
|
||||
|
||||
config_cumebox2_files() {
|
||||
@ -280,8 +320,7 @@ config_cumebox2_files() {
|
||||
download_file_if_missing "$dtb_file" || echo "警告:下载 Cumebox2 DTB 失败"
|
||||
download_file_if_missing "$ssd_file" || echo "警告:下载 Cumebox2 ssd 脚本失败"
|
||||
download_file_if_missing "$config_file" || echo "警告:下载 Cumebox2 配置文件失败"
|
||||
|
||||
# 注意 DTB 路径可能需要根据实际 Armbian 版本调整
|
||||
|
||||
sudo cp "$dtb_file" "$ROOTFS/boot/dtb/amlogic/meson-gxl-s905x-khadas-vim.dtb" || echo "警告:复制 Cumebox2 DTB 失败"
|
||||
sudo cp "$ssd_file" "$ROOTFS/usr/bin/" || echo "警告:复制 Cumebox2 ssd 脚本失败"
|
||||
sudo chmod +x "$ROOTFS/usr/bin/ssd" || echo "警告:设置 ssd 脚本执行权限失败"
|
||||
@ -290,11 +329,125 @@ config_cumebox2_files() {
|
||||
|
||||
config_octopus_flanet_files() {
|
||||
echo "信息:为 Octopus-Planet 配置特定文件 (model_database.conf)..."
|
||||
|
||||
|
||||
# 自动下载 Octopus-Planet 相关文件(如果不存在)
|
||||
local config_file="$SRCPATH/image/octopus-flanet/model_database.conf"
|
||||
|
||||
|
||||
download_file_if_missing "$config_file" || echo "警告:下载 Octopus-Planet 配置文件失败"
|
||||
|
||||
|
||||
sudo cp "$config_file" "$ROOTFS/etc/model_database.conf" || echo "警告:复制 model_database.conf 失败"
|
||||
}
|
||||
|
||||
echo "信息:为 Octopus-Planet 添加 DRM 设备支持..."
|
||||
run_in_chroot "sed -i \"/--device=\\/dev\\/video0/a\\ - \\\"--drm-device=/dev/dri/card0\\\"\" /etc/kvmd/override.yaml"
|
||||
}
|
||||
|
||||
config_orangepi_zero_files() {
|
||||
echo "信息:配置 Orange Pi Zero 特定文件..."
|
||||
|
||||
# 清空 modules.conf 文件,避免加载不必要的模块
|
||||
run_in_chroot "echo 'libcomposite' > /etc/modules-load.d/modules.conf"
|
||||
|
||||
echo "信息:Orange Pi Zero 特定配置完成。"
|
||||
}
|
||||
|
||||
config_onecloud_pro_files() {
|
||||
echo "信息:配置 Onecloud Pro 特定文件..."
|
||||
|
||||
echo "信息:为 Onecloud Pro 添加 DRM 设备支持..."
|
||||
run_in_chroot "sed -i \"/--device=\\/dev\\/video0/a\\ - \\\"--drm-device=/dev/dri/card0\\\"\" /etc/kvmd/override.yaml"
|
||||
}
|
||||
|
||||
config_onecloud_files() {
|
||||
echo "信息:配置 Onecloud 特定文件..."
|
||||
|
||||
echo "信息:为 Onecloud 添加 DRM 设备支持..."
|
||||
run_in_chroot "sed -i \"/--device=\\/dev\\/video0/a\\ - \\\"--drm-device=/dev/dri/card1\\\"\" /etc/kvmd/override.yaml"
|
||||
|
||||
echo "信息:Onecloud 特定配置完成。"
|
||||
}
|
||||
|
||||
oec_turbo_rootfs() {
|
||||
local source_image="$SRCPATH/image/oec-turbo/Flash_Armbian_25.05.0_rockchip_efused-wxy-oec_bookworm_6.1.99_server_2025.03.20.img"
|
||||
local target_image="$TMPDIR/rootfs.img"
|
||||
local rootfs_offset=$((1409024 * 512)) # 根据分区7的起始扇区计算
|
||||
|
||||
echo "信息:准备 OEC-Turbo Rootfs (Debian 12)..."
|
||||
ensure_dir "$TMPDIR"
|
||||
|
||||
echo "信息:下载或使用本地 OEC-Turbo 原始镜像..."
|
||||
download_file_if_missing "$source_image" || { echo "错误:下载 OEC-Turbo 原始镜像失败" >&2; exit 1; }
|
||||
|
||||
cp "$source_image" "$target_image" || { echo "错误:复制 OEC-Turbo 原始镜像失败" >&2; exit 1; }
|
||||
|
||||
find_loop_device
|
||||
# 设置 loop 设备指向 rootfs 分区 (分区7)
|
||||
sudo losetup "$LOOPDEV" "$target_image" -o "$rootfs_offset" || { echo "错误:设置 loop 设备失败" >&2; exit 1; }
|
||||
|
||||
echo "信息:OEC-Turbo Rootfs 准备完成,loop 设备 $LOOPDEV 已就绪。"
|
||||
}
|
||||
|
||||
config_oec_turbo_files() {
|
||||
echo "信息:配置 OEC-Turbo 特定文件..."
|
||||
|
||||
# 替换 override.yaml 中的硬件编码配置,启用 RK MPP 硬件编码
|
||||
echo "信息:配置 VPU 硬件编码支持..."
|
||||
run_in_chroot "sed -i 's/--h264-hwenc=disabled/--h264-hwenc=rkmpp/g' /etc/kvmd/override.yaml"
|
||||
|
||||
echo "信息:配置 udev 规则以授权 kvmd 组访问硬件设备..."
|
||||
run_in_chroot "cat > /etc/udev/rules.d/99-kvmd-hw-access.rules <<'EOF'
|
||||
# Generic hardware access for kvmd
|
||||
# Safe on all platforms — rules only apply if device exists
|
||||
|
||||
# Rockchip MPP (rkmpp)
|
||||
KERNEL==\"mpp_service\", GROUP=\"kvmd\", MODE=\"0660\"
|
||||
|
||||
# DMA-Heap (used by modern MPP)
|
||||
SUBSYSTEM==\"dma_heap\", KERNEL==\"system\", GROUP=\"kvmd\", MODE=\"0660\"
|
||||
SUBSYSTEM==\"dma_heap\", KERNEL==\"system-uncached\", GROUP=\"kvmd\", MODE=\"0660\"
|
||||
SUBSYSTEM==\"dma_heap\", KERNEL==\"reserved\", GROUP=\"kvmd\", MODE=\"0660\"
|
||||
|
||||
# Optional legacy Rockchip devices
|
||||
KERNEL==\"rkvdec\", GROUP=\"kvmd\", MODE=\"0660\"
|
||||
KERNEL==\"rkvenc\", GROUP=\"kvmd\", MODE=\"0660\"
|
||||
KERNEL==\"rga\", GROUP=\"kvmd\", MODE=\"0660\"
|
||||
|
||||
EOF"
|
||||
|
||||
# 替换 DTB 文件
|
||||
replace_oec_turbo_dtb
|
||||
|
||||
echo "信息:OEC-Turbo 特定配置完成。"
|
||||
}
|
||||
|
||||
replace_oec_turbo_dtb() {
|
||||
local dtb_source="$SRCPATH/image/oec-turbo/rk3566-onething-oec-box.dtb"
|
||||
local target_image="$TMPDIR/rootfs.img"
|
||||
local boot_offset=$((360448 * 512)) # boot 分区6的偏移
|
||||
local boot_mount="$TMPDIR/oec_boot_mount"
|
||||
local dtb_target_path="dtb/rockchip/rk3566-onething-oec-box.dtb"
|
||||
local boot_loopdev=""
|
||||
|
||||
echo "信息:替换 OEC-Turbo DTB 文件..."
|
||||
|
||||
if [ ! -f "$dtb_source" ]; then
|
||||
echo "信息:尝试下载 DTB 文件..."
|
||||
download_file_if_missing "$dtb_source"
|
||||
fi
|
||||
|
||||
echo "信息:为 boot 分区查找独立的 loop 设备..."
|
||||
# 查找一个新的loop设备用于boot分区
|
||||
boot_loopdev=$(losetup -f)
|
||||
ensure_dir "$boot_mount"
|
||||
|
||||
losetup -o "$boot_offset" "$boot_loopdev" "$target_image"
|
||||
mount "$boot_loopdev" "$boot_mount"
|
||||
|
||||
# 确保目标目录存在并复制 DTB 文件
|
||||
mkdir -p "$boot_mount/$(dirname "$dtb_target_path")"
|
||||
cp "$dtb_source" "$boot_mount/$dtb_target_path"
|
||||
echo "信息:DTB 文件替换成功: $dtb_target_path"
|
||||
|
||||
umount "$boot_mount"
|
||||
losetup -d "$boot_loopdev"
|
||||
rmdir "$boot_mount"
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ config_base_files() {
|
||||
sudo chmod +x "$ROOTFS/usr/bin/kvmd-gencert" "$ROOTFS/usr/bin/kvmd-bootconfig" "$ROOTFS/usr/bin/kvmd-certbot" "$ROOTFS/usr/bin/kvmd-udev-hdmiusb-check" "$ROOTFS/usr/bin/kvmd-udev-restart-pass" "$ROOTFS/usr/bin/kvmd-firstrun.sh"
|
||||
|
||||
# 尝试下载或使用本地 rc.local 文件
|
||||
download_rc_local "$platform_id"
|
||||
download_rc_local "$platform_id" || echo "信息:rc.local 文件不存在,跳过"
|
||||
if [ -f "$SRCPATH/image/$platform_id/rc.local" ]; then
|
||||
echo "信息:复制设备特定的 rc.local 文件..."
|
||||
sudo cp "$SRCPATH/image/$platform_id/rc.local" "$ROOTFS/etc/"
|
||||
@ -110,7 +110,7 @@ install_base_packages() {
|
||||
iptables network-manager curl kmod libmicrohttpd12 libjansson4 libssl3 \\
|
||||
libsofia-sip-ua0 libglib2.0-0 libopus0 libogg0 libcurl4 libconfig9 \\
|
||||
python3-pip net-tools libavcodec59 libavformat59 libavutil57 libswscale6 \\
|
||||
libavfilter8 libavdevice59 v4l-utils libv4l-0 nano unzip && \\
|
||||
libavfilter8 libavdevice59 v4l-utils libv4l-0 nano unzip dnsmasq python3-systemd && \\
|
||||
apt clean && \\
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
"
|
||||
@ -121,9 +121,9 @@ configure_network() {
|
||||
if [ "$network_type" = "systemd-networkd" ]; then
|
||||
echo "信息:在 chroot 环境中配置 systemd-networkd..."
|
||||
|
||||
# 检查是否为onecloud平台,如果是则使用随机MAC地址生成机制
|
||||
if [ "$TARGET_DEVICE_NAME" = "onecloud" ]; then
|
||||
echo "信息:为onecloud平台配置随机MAC地址生成机制..."
|
||||
# onecloud 与 onecloud-pro 均启用基于 SN 的 MAC 地址生成
|
||||
if [ "$TARGET_DEVICE_NAME" = "onecloud" ] || [ "$TARGET_DEVICE_NAME" = "onecloud-pro" ]; then
|
||||
echo "信息:为 ${TARGET_DEVICE_NAME} 平台配置基于 SN 的 MAC 地址生成机制..."
|
||||
|
||||
# 复制MAC地址生成脚本
|
||||
sudo cp "$SCRIPT_DIR/scripts/generate-random-mac.sh" "$ROOTFS/usr/local/bin/"
|
||||
@ -140,7 +140,7 @@ configure_network() {
|
||||
systemctl enable systemd-networkd systemd-resolved && \\
|
||||
systemctl enable kvmd-generate-mac.service
|
||||
"
|
||||
echo "信息:onecloud随机MAC地址生成机制配置完成"
|
||||
echo "信息:${TARGET_DEVICE_NAME} 基于 SN 的 MAC 地址生成机制配置完成"
|
||||
fi
|
||||
else
|
||||
echo "信息:使用默认的网络管理器 (NetworkManager)..."
|
||||
@ -181,6 +181,7 @@ configure_system() {
|
||||
cat /One-KVM/configs/os/sudoers/v2-hdmiusb >> /etc/sudoers && \\
|
||||
cat /One-KVM/configs/os/udev/v2-hdmiusb-rpi4.rules > /etc/udev/rules.d/99-kvmd.rules && \\
|
||||
echo 'libcomposite' >> /etc/modules && \\
|
||||
echo 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/99-kvmd-extra.conf && \\
|
||||
mv /usr/local/bin/kvmd* /usr/bin/ || echo '信息:/usr/local/bin/kvmd* 未找到或移动失败,可能已在/usr/bin' && \\
|
||||
cp -r /One-KVM/configs/os/services/* /etc/systemd/system/ && \\
|
||||
cp /One-KVM/configs/os/tmpfiles.conf /usr/lib/tmpfiles.d/ && \\
|
||||
@ -193,8 +194,10 @@ configure_system() {
|
||||
sed -i 's/8080/80/g' /etc/kvmd/override.yaml && \\
|
||||
sed -i 's/4430/443/g' /etc/kvmd/override.yaml && \\
|
||||
chown kvmd -R /var/lib/kvmd/msd/ && \\
|
||||
systemctl enable kvmd kvmd-otg kvmd-nginx kvmd-vnc kvmd-ipmi kvmd-webterm kvmd-janus kvmd-media && \\
|
||||
systemctl disable nginx && \\
|
||||
rm /etc/resolv.conf && \\
|
||||
printf '%s\\n' 'nameserver 1.1.1.1' 'nameserver 1.0.0.1' > /etc/resolv.conf && \
|
||||
systemctl enable dnsmasq kvmd kvmd-otg kvmd-nginx kvmd-vnc kvmd-ipmi kvmd-webterm kvmd-janus kvmd-media kvmd-gostc && \\
|
||||
systemctl disable nginx systemd-resolved && \\
|
||||
rm -rf /One-KVM
|
||||
"
|
||||
}
|
||||
@ -220,6 +223,54 @@ install_webterm() {
|
||||
"
|
||||
}
|
||||
|
||||
install_gostc() {
|
||||
local arch="$1" # armhf, aarch64, x86_64
|
||||
local gostc_arch="$arch"
|
||||
local gostc_version="v2.0.8-beta.2"
|
||||
|
||||
# 根据架构映射下载文件名
|
||||
case "$arch" in
|
||||
armhf) gostc_arch="arm_7" ;;
|
||||
aarch64) gostc_arch="arm64_v8.0" ;;
|
||||
x86_64|amd64) gostc_arch="amd64_v1" ;;
|
||||
*) echo "错误:不支持的架构 $arch"; exit 1 ;;
|
||||
esac
|
||||
|
||||
echo "信息:在 chroot 环境中下载并安装 gostc ($gostc_arch)..."
|
||||
run_in_chroot "
|
||||
mkdir -p /tmp/gostc && cd /tmp/gostc && \\
|
||||
curl -L https://github.com/mofeng-git/gostc-open/releases/download/${gostc_version}/gostc_linux_${gostc_arch}.tar.gz -o gostc.tar.gz && \\
|
||||
tar -xzf gostc.tar.gz && \\
|
||||
mv gostc /usr/bin/ && \\
|
||||
chmod +x /usr/bin/gostc && \\
|
||||
cd / && rm -rf /tmp/gostc
|
||||
"
|
||||
|
||||
echo "信息:创建 gostc systemd 服务文件..."
|
||||
run_in_chroot "
|
||||
cat > /etc/systemd/system/kvmd-gostc.service << 'EOF'
|
||||
[Unit]
|
||||
Description=基于FRP开发的内网穿透 客户端/节点
|
||||
ConditionFileIsExecutable=/usr/bin/gostc
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
StartLimitInterval=5
|
||||
StartLimitBurst=10
|
||||
ExecStart=/usr/bin/gostc \"-web-addr\" \"0.0.0.0:18080\"
|
||||
WorkingDirectory=/usr/bin
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
EnvironmentFile=-/etc/sysconfig/gostc
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
"
|
||||
|
||||
echo "信息:gostc 安装和配置完成"
|
||||
}
|
||||
|
||||
apply_kvmd_tweaks() {
|
||||
local arch="$1" # armhf, aarch64, x86_64
|
||||
local device_type="$2" # "gpio" or "video1" or other
|
||||
@ -282,6 +333,8 @@ apply_kvmd_tweaks() {
|
||||
fi
|
||||
fi
|
||||
echo "信息:KVMD 配置调整完成。"
|
||||
|
||||
run_in_chroot "apt remove -y --purge systemd-resolved"
|
||||
}
|
||||
|
||||
# --- 整体安装流程 ---
|
||||
@ -304,9 +357,11 @@ install_and_configure_kvmd() {
|
||||
config_base_files "$TARGET_DEVICE_NAME" # 使用全局变量传递设备名
|
||||
|
||||
# 特定设备的额外文件配置 (如果存在)
|
||||
if declare -f "config_${TARGET_DEVICE_NAME}_files" > /dev/null; then
|
||||
echo "信息:执行特定设备的文件配置函数 config_${TARGET_DEVICE_NAME}_files ..."
|
||||
"config_${TARGET_DEVICE_NAME}_files"
|
||||
# 将设备名中的连字符转换为下划线以匹配函数名
|
||||
local device_func_name="${TARGET_DEVICE_NAME//-/_}"
|
||||
if declare -f "config_${device_func_name}_files" > /dev/null; then
|
||||
echo "信息:执行特定设备的文件配置函数 config_${device_func_name}_files ..."
|
||||
"config_${device_func_name}_files"
|
||||
fi
|
||||
|
||||
# 某些镜像可能需要准备DNS和换源
|
||||
@ -321,6 +376,7 @@ install_and_configure_kvmd() {
|
||||
configure_network "$network_type"
|
||||
install_python_deps
|
||||
configure_kvmd_core
|
||||
install_gostc "$arch" # 安装 gostc
|
||||
configure_system
|
||||
install_webterm "$arch" # 传递原始架构名给ttyd下载
|
||||
apply_kvmd_tweaks "$arch" "$device_type"
|
||||
|
||||
@ -159,7 +159,7 @@ EOF
|
||||
fi
|
||||
|
||||
if [ "$NOIPMI" == "1" ]; then
|
||||
log_info "已禁用IPMI功能"
|
||||
log_info "已禁用 IPMI 功能"
|
||||
rm -r /usr/share/kvmd/extras/ipmi
|
||||
else
|
||||
cat >> /etc/kvmd/supervisord.conf << EOF
|
||||
@ -177,11 +177,30 @@ redirect_stderr=true
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ "$NOGOSTC" == "1" ]; then
|
||||
log_info "已禁用 GOSTC 功能"
|
||||
rm -rf /usr/share/kvmd/extras/gostc
|
||||
else
|
||||
cat >> /etc/kvmd/supervisord.conf << EOF
|
||||
|
||||
[program:kvmd-gostc]
|
||||
command=/usr/bin/gostc -web-addr 127.0.0.1:18080
|
||||
autostart=true
|
||||
autorestart=true
|
||||
startsecs=5
|
||||
priority=300
|
||||
stopasgroup=true
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes = 0
|
||||
redirect_stderr=true
|
||||
EOF
|
||||
fi
|
||||
|
||||
#switch OTG config
|
||||
if [ "$OTG" == "1" ]; then
|
||||
log_info "已启用 OTG 功能"
|
||||
sed -i "s/ch9329/otg/g" /etc/kvmd/override.yaml
|
||||
sed -i "s/device: \/dev\/ttyUSB0//g" /etc/kvmd/override.yaml
|
||||
sed -i "s|device: /dev/ttyUSB0||g" /etc/kvmd/override.yaml
|
||||
if [ "$NOMSD" == 1 ]; then
|
||||
log_info "已禁用 MSD 功能"
|
||||
else
|
||||
@ -190,8 +209,8 @@ EOF
|
||||
fi
|
||||
|
||||
if [ ! -z "$VIDEONUM" ]; then
|
||||
if sed -i "s/\/dev\/video0/\/dev\/video$VIDEONUM/g" /etc/kvmd/override.yaml && \
|
||||
sed -i "s/\/dev\/video0/\/dev\/video$VIDEONUM/g" /etc/kvmd/janus/janus.plugin.ustreamer.jcfg; then
|
||||
if sed -i "s|/dev/video0|/dev/video$VIDEONUM|g" /etc/kvmd/override.yaml && \
|
||||
sed -i "s|/dev/video0|/dev/video$VIDEONUM|g" /etc/kvmd/janus/janus.plugin.ustreamer.jcfg; then
|
||||
log_info "视频设备已设置为 /dev/video$VIDEONUM"
|
||||
fi
|
||||
fi
|
||||
@ -208,6 +227,12 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -z "$CH9329NUM" ]; then
|
||||
if sed -i "s|/dev/ttyUSB0|/dev/ttyUSB$CH9329NUM|g" /etc/kvmd/override.yaml; then
|
||||
log_info "CH9329 串口设备已设置为 $CH9329NUM"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -z "$CH9329TIMEOUT" ]; then
|
||||
if sed -i "s/read_timeout: 0.3/read_timeout: $CH9329TIMEOUT/g" /etc/kvmd/override.yaml; then
|
||||
log_info "CH9329 超时已设置为 $CH9329TIMEOUT 秒"
|
||||
@ -272,4 +297,4 @@ if [ "$OTG" == "1" ]; then
|
||||
fi
|
||||
|
||||
log_info "One-KVM 配置文件准备完成,正在启动服务..."
|
||||
exec supervisord -c /etc/kvmd/supervisord.conf
|
||||
exec supervisord -c /etc/kvmd/supervisord.conf
|
||||
|
||||
3
build/platform/oec-turbo
Normal file
3
build/platform/oec-turbo
Normal file
@ -0,0 +1,3 @@
|
||||
PIKVM_MODEL=v2_model
|
||||
PIKVM_VIDEO=usb_video
|
||||
PIKVM_BOARD=oec-turbo
|
||||
3
build/platform/orangepi-zero
Normal file
3
build/platform/orangepi-zero
Normal file
@ -0,0 +1,3 @@
|
||||
PIKVM_MODEL=v2_model
|
||||
PIKVM_VIDEO=usb_video
|
||||
PIKVM_BOARD=orangepi-zero
|
||||
@ -1,12 +1,45 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 为onecloud平台生成随机MAC地址的一次性脚本
|
||||
# 此脚本在首次开机时执行,为eth0网卡生成并应用随机MAC地址
|
||||
# 为玩客云/玩客云Pro 平台生成 MAC 地址的一次性脚本
|
||||
# 此脚本在首次开机时执行,为 eth0 网卡生成并应用基于 SN 的 MAC 地址,失败时回退到随机 MAC
|
||||
|
||||
set -e
|
||||
|
||||
NETWORK_CONFIG="/etc/systemd/network/99-eth0.network"
|
||||
LOCK_FILE="/var/lib/kvmd/.mac-generated"
|
||||
PLATFORM_FILE="/usr/share/kvmd/platform"
|
||||
EFUSE_SYSFS_PATH=""
|
||||
SN_PREFIX=""
|
||||
SN_EXPECTED_LENGTH=13
|
||||
|
||||
# 按平台设置 EFUSE 与 SN 参数;未知平台时按 efuse 路径探测
|
||||
detect_platform_params() {
|
||||
local platform=""
|
||||
if [ -f "$PLATFORM_FILE" ]; then
|
||||
platform=$(tr -d '\n' < "$PLATFORM_FILE")
|
||||
fi
|
||||
|
||||
case "$platform" in
|
||||
onecloud)
|
||||
EFUSE_SYSFS_PATH="/sys/bus/nvmem/devices/meson8b-efuse0/nvmem"
|
||||
SN_PREFIX="OCP"
|
||||
;;
|
||||
onecloud-pro)
|
||||
EFUSE_SYSFS_PATH="/sys/devices/platform/efuse/efuse0/nvmem"
|
||||
SN_PREFIX="ODC"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$EFUSE_SYSFS_PATH" ] || [ -z "$SN_PREFIX" ]; then
|
||||
if [ -e "/sys/devices/platform/efuse/efuse0/nvmem" ]; then
|
||||
EFUSE_SYSFS_PATH="/sys/devices/platform/efuse/efuse0/nvmem"
|
||||
SN_PREFIX="ODC"
|
||||
elif [ -e "/sys/bus/nvmem/devices/meson8b-efuse0/nvmem" ]; then
|
||||
EFUSE_SYSFS_PATH="/sys/bus/nvmem/devices/meson8b-efuse0/nvmem"
|
||||
SN_PREFIX="OCP"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查是否已经执行过
|
||||
if [ -f "$LOCK_FILE" ]; then
|
||||
@ -14,10 +47,29 @@ if [ -f "$LOCK_FILE" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 生成随机MAC地址 (使用本地管理的MAC地址前缀)
|
||||
# 生成MAC地址函数
|
||||
generate_random_mac() {
|
||||
# 使用本地管理的MAC地址前缀 (第二位设为2、6、A、E中的一个)
|
||||
# 这样可以避免与真实硬件MAC地址冲突
|
||||
detect_platform_params
|
||||
# 尝试根据 SN 生成唯一 MAC 地址
|
||||
if [ -f "$EFUSE_SYSFS_PATH" ]; then
|
||||
sn_offset=$(grep --binary-files=text -boP "$SN_PREFIX" "$EFUSE_SYSFS_PATH" | head -n1 | cut -d: -f1)
|
||||
if [ -n "$sn_offset" ]; then
|
||||
sn=$(cat "$EFUSE_SYSFS_PATH" | dd bs=1 skip="$sn_offset" count="$SN_EXPECTED_LENGTH" 2>/dev/null)
|
||||
if [ ${#sn} -eq $SN_EXPECTED_LENGTH ]; then
|
||||
echo "S/N: $sn" >&2 # 输出到 stderr,避免干扰返回值
|
||||
# 使用 SN 的 SHA-256 哈希生成后 5 字节(避免多余管道)
|
||||
sn_hash=$(printf %s "$sn" | sha256sum | cut -d' ' -f1)
|
||||
# 直接用 Bash 子串获取哈希末 10 个字符并插入分隔符
|
||||
mac_hex=${sn_hash: -10}
|
||||
mac_suffix=$(printf "%s:%s:%s:%s:%s" "${mac_hex:0:2}" "${mac_hex:2:2}" "${mac_hex:4:2}" "${mac_hex:6:2}" "${mac_hex:8:2}")
|
||||
printf "02:%s\n" "$mac_suffix"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# 若 SN 获取失败,回退到随机逻辑
|
||||
echo "警告: 无法获取 SN,回退到随机 MAC 生成" >&2
|
||||
printf "02:%02x:%02x:%02x:%02x:%02x\n" \
|
||||
$((RANDOM % 256)) \
|
||||
$((RANDOM % 256)) \
|
||||
@ -26,12 +78,18 @@ generate_random_mac() {
|
||||
$((RANDOM % 256))
|
||||
}
|
||||
|
||||
echo "正在为onecloud生成随机MAC地址..."
|
||||
echo "正在生成基于 SN 的 MAC 地址..."
|
||||
|
||||
# 生成新的MAC地址
|
||||
NEW_MAC=$(generate_random_mac)
|
||||
echo "生成的MAC地址: $NEW_MAC"
|
||||
|
||||
# 验证 MAC 地址格式
|
||||
if ! [[ $NEW_MAC =~ ^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$ ]]; then
|
||||
echo "错误: 生成的 MAC 地址格式无效: $NEW_MAC"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 备份原配置文件
|
||||
if [ -f "$NETWORK_CONFIG" ]; then
|
||||
cp "$NETWORK_CONFIG" "${NETWORK_CONFIG}.backup"
|
||||
@ -58,7 +116,7 @@ echo "MAC地址生成时间: $(date)" > "$LOCK_FILE"
|
||||
# 禁用此服务,确保只运行一次
|
||||
systemctl disable kvmd-generate-mac.service
|
||||
|
||||
echo "随机MAC地址生成完成: $NEW_MAC"
|
||||
echo "MAC地址生成完成: $NEW_MAC"
|
||||
echo "服务已自动禁用,下次开机不会再执行"
|
||||
|
||||
exit 0
|
||||
exit 0
|
||||
@ -2,6 +2,6 @@ video: {
|
||||
sink = "kvmd::ustreamer::h264"
|
||||
}
|
||||
acap: {
|
||||
device = "hw:0"
|
||||
device = "hw:0,0"
|
||||
tc358743 = "/dev/video0"
|
||||
}
|
||||
|
||||
@ -66,9 +66,7 @@ kvmd:
|
||||
- "--jpeg-sink-mode=0660"
|
||||
- "--h264-bitrate={h264_bitrate}"
|
||||
- "--h264-gop={h264_gop}"
|
||||
- "--h264-preset=ultrafast"
|
||||
- "--h264-hwenc=disabled"
|
||||
- "--h264-hwenc-fallback"
|
||||
- "--slowdown"
|
||||
gpio:
|
||||
drivers:
|
||||
@ -159,10 +157,6 @@ media:
|
||||
|
||||
jpeg:
|
||||
sink: 'kvmd::ustreamer::jpeg'
|
||||
janus:
|
||||
stun:
|
||||
host: stun.cloudflare.com
|
||||
port: 3478
|
||||
|
||||
otgnet:
|
||||
commands:
|
||||
|
||||
@ -63,4 +63,3 @@ stopasgroup=true
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes = 0
|
||||
redirect_stderr=true
|
||||
|
||||
|
||||
@ -6,4 +6,4 @@
|
||||
#
|
||||
# NEVER use the same passwords for KVMD, IPMI and VNCAuth users.
|
||||
|
||||
admin
|
||||
admin -> admin:admin
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[Unit]
|
||||
Description=One-KVM - The main daemon
|
||||
After=network.target network-online.target nss-lookup.target
|
||||
After=network.target network-online.target nss-lookup.target rc-local.service
|
||||
|
||||
[Service]
|
||||
User=kvmd
|
||||
|
||||
6
extras/gostc/manifest.yaml
Normal file
6
extras/gostc/manifest.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
name: GOSTC
|
||||
description: GOSTC Server
|
||||
icon: share/svg/gostc.svg
|
||||
path: extras/gostc
|
||||
daemon: kvmd-gostc
|
||||
place: 11
|
||||
7
extras/gostc/nginx.ctx-server.conf
Normal file
7
extras/gostc/nginx.ctx-server.conf
Normal file
@ -0,0 +1,7 @@
|
||||
location /extras/gostc {
|
||||
proxy_pass http://127.0.0.1:18080;
|
||||
include /etc/kvmd/nginx/loc-proxy.conf;
|
||||
include /etc/kvmd/nginx/loc-websocket.conf;
|
||||
include /etc/kvmd/nginx/loc-login.conf;
|
||||
include /etc/kvmd/nginx/loc-nocache.conf;
|
||||
}
|
||||
@ -81,6 +81,7 @@ from ..validators.net import valid_port
|
||||
from ..validators.net import valid_ports_list
|
||||
from ..validators.net import valid_mac
|
||||
from ..validators.net import valid_ssl_ciphers
|
||||
from ..validators.net import valid_ice_servers
|
||||
|
||||
from ..validators.hid import valid_hid_key
|
||||
from ..validators.hid import valid_hid_mouse_output
|
||||
@ -860,6 +861,7 @@ def _get_config_scheme() -> dict:
|
||||
], type=valid_command),
|
||||
"cmd_remove": Option([], type=valid_options),
|
||||
"cmd_append": Option([], type=valid_options),
|
||||
"local_ice_servers": Option([], type=valid_ice_servers, unpack_as="ice_servers"),
|
||||
},
|
||||
|
||||
"watchdog": {
|
||||
|
||||
@ -2,6 +2,8 @@ import asyncio
|
||||
import asyncio.subprocess
|
||||
import socket
|
||||
import dataclasses
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
import netifaces
|
||||
|
||||
@ -43,6 +45,7 @@ class JanusRunner: # pylint: disable=too-many-instance-attributes
|
||||
cmd: list[str],
|
||||
cmd_remove: list[str],
|
||||
cmd_append: list[str],
|
||||
ice_servers: list[dict[str, Any]],
|
||||
) -> None:
|
||||
|
||||
self.__stun = Stun(stun_host, stun_port, stun_timeout, stun_retries, stun_retries_delay)
|
||||
@ -52,6 +55,7 @@ class JanusRunner: # pylint: disable=too-many-instance-attributes
|
||||
self.__check_retries_delay = check_retries_delay
|
||||
|
||||
self.__cmd = tools.build_cmd(cmd, cmd_remove, cmd_append)
|
||||
self.__ice_servers = ice_servers
|
||||
|
||||
self.__janus_task: (asyncio.Task | None) = None
|
||||
self.__janus_proc: (asyncio.subprocess.Process | None) = None # pylint: disable=no-member
|
||||
@ -173,13 +177,25 @@ class JanusRunner: # pylint: disable=too-many-instance-attributes
|
||||
part.format(**placeholders)
|
||||
for part in cmd
|
||||
]
|
||||
self.__janus_proc = await aioproc.run_process(
|
||||
cmd=cmd,
|
||||
env={"JANUS_USTREAMER_WEB_ICE_URL": f"stun:{netcfg.stun_host}:{netcfg.stun_port}"},
|
||||
)
|
||||
env = {}
|
||||
ice_payload = self.__build_ice_payload(netcfg)
|
||||
if ice_payload:
|
||||
env["JANUS_USTREAMER_WEB_ICE_URL"] = ice_payload
|
||||
self.__janus_proc = await aioproc.run_process(cmd=cmd, env=env or None)
|
||||
get_logger(0).info("Started Janus pid=%d: %s", self.__janus_proc.pid, tools.cmdfmt(cmd))
|
||||
|
||||
async def __kill_janus_proc(self) -> None:
|
||||
if self.__janus_proc:
|
||||
await aioproc.kill_process(self.__janus_proc, 5, get_logger(0))
|
||||
self.__janus_proc = None
|
||||
|
||||
def __build_ice_payload(self, netcfg: _Netcfg) -> (str | None):
|
||||
if self.__ice_servers:
|
||||
try:
|
||||
return f"json:{json.dumps(self.__ice_servers, ensure_ascii=False)}"
|
||||
except Exception as ex: # pragma: no cover
|
||||
get_logger(0).error("Can't encode ICE servers: %s", tools.efmt(ex))
|
||||
return None
|
||||
if netcfg.stun_host and netcfg.stun_port:
|
||||
return f"stun:{netcfg.stun_host}:{netcfg.stun_port}"
|
||||
return None
|
||||
|
||||
@ -136,7 +136,12 @@ class Stun:
|
||||
return (StunNatType.FULL_CONE_NAT, resp)
|
||||
|
||||
if first.changed is None:
|
||||
raise RuntimeError(f"Changed addr is None: {first}")
|
||||
get_logger(0).warning(
|
||||
"STUN server %s:%d responded without CHANGED-ADDRESS; skipping NAT type detection",
|
||||
self.__host,
|
||||
self.__port,
|
||||
)
|
||||
return (StunNatType.ERROR, first)
|
||||
resp = await self.__make_request("Change request [ext_ip != src_ip]", first.changed, b"")
|
||||
if not resp.ok:
|
||||
return (StunNatType.CHANGED_ADDR_ERROR, resp)
|
||||
|
||||
@ -120,3 +120,39 @@ def valid_ssl_ciphers(arg: Any) -> str:
|
||||
def valid_url(arg: Any) -> str:
|
||||
# XXX: VERY primitive
|
||||
return check_re_match(arg, "HTTP(S) URL", r"^https?://[\[\w]+\S*")
|
||||
|
||||
|
||||
def valid_ice_servers(arg: Any) -> list[dict[str, Any]]:
|
||||
name = "ICE servers list"
|
||||
if arg is None:
|
||||
return []
|
||||
if not isinstance(arg, list):
|
||||
raise_error(arg, name)
|
||||
servers: list[dict[str, Any]] = []
|
||||
for item in arg:
|
||||
if not isinstance(item, dict):
|
||||
raise_error(item, "ICE server entry")
|
||||
urls = item.get("urls")
|
||||
if isinstance(urls, str):
|
||||
urls_list = [valid_stripped_string_not_empty(urls, "ICE server URL")]
|
||||
elif isinstance(urls, list):
|
||||
urls_list = [
|
||||
valid_stripped_string_not_empty(url, "ICE server URL")
|
||||
for url in urls
|
||||
]
|
||||
else:
|
||||
raise_error(urls, "ICE server URLs")
|
||||
if not urls_list:
|
||||
raise_error(urls, "ICE server URLs")
|
||||
server: dict[str, Any] = {"urls": urls_list}
|
||||
username = item.get("username")
|
||||
if username is not None:
|
||||
server["username"] = valid_stripped_string_not_empty(username, "ICE username")
|
||||
credential = item.get("credential")
|
||||
if credential is not None:
|
||||
server["credential"] = valid_stripped_string_not_empty(credential, "ICE credential")
|
||||
credential_type = item.get("credential_type") or item.get("credentialType")
|
||||
if credential_type is not None:
|
||||
server["credentialType"] = valid_stripped_string_not_empty(credential_type, "ICE credentialType")
|
||||
servers.append(server)
|
||||
return servers
|
||||
|
||||
@ -657,7 +657,7 @@
|
||||
</div>
|
||||
</td>
|
||||
<td> </td>
|
||||
<td>Writable:
|
||||
<td i18n="msd-rw-switch">Writable:
|
||||
</td>
|
||||
<td align="right">
|
||||
<div class="switch-box">
|
||||
@ -781,7 +781,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="macro_script_events">Scripted events:</td>
|
||||
<tdvalue id="hid-recorder-events-count">0</tdvalue>
|
||||
<td id="hid-recorder-events-count">0</td>
|
||||
<td><sup><i i18n="macro_include_delays">include delays</i></sup></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@ -103,10 +103,10 @@ function __showKvmdInfo(info) {
|
||||
|
||||
if (info.meta !== null && info.meta.server && info.meta.server.host) {
|
||||
$("kvmd-meta-server-host").innerText = info.meta.server.host;
|
||||
document.title = `${info.meta.server.host} | PiKVM Index`;
|
||||
document.title = `${info.meta.server.host} | One-KVM Index`;
|
||||
} else {
|
||||
$("kvmd-meta-server-host").innerHTML = "<i>Invalid meta</i>";
|
||||
document.title = "PiKVM Index";
|
||||
document.title = "One-KVM Index";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -118,7 +118,7 @@ export function Keyboard(__recordWsEvent) {
|
||||
}
|
||||
} else {
|
||||
if (is_captured) {
|
||||
title = "Keyboard captured, PiKVM offline";
|
||||
title = "Keyboard captured, One-KVM offline";
|
||||
}
|
||||
}
|
||||
$("hid-keyboard-led").className = led;
|
||||
|
||||
@ -169,7 +169,7 @@ export function Mouse(__getGeometry, __recordWsEvent) {
|
||||
}
|
||||
} else {
|
||||
if (is_captured) {
|
||||
title = "Mouse captured, PiKVM offline";
|
||||
title = "Mouse captured, One-KVM offline";
|
||||
}
|
||||
}
|
||||
$("hid-mouse-led").className = led;
|
||||
|
||||
@ -46,7 +46,7 @@ export function Streamer() {
|
||||
var __init__ = function() {
|
||||
__streamer = new MjpegStreamer(__setActive, __setInactive, __setInfo, __organizeHook);
|
||||
|
||||
$("stream-led").title = "No stream from PiKVM";
|
||||
$("stream-led").title = "No stream from One-KVM";
|
||||
|
||||
tools.slider.setParams($("stream-quality-slider"), 5, 100, 5, 80, function(value) {
|
||||
$("stream-quality-value").innerText = `${value}%`;
|
||||
@ -289,7 +289,7 @@ export function Streamer() {
|
||||
|
||||
var __setInactive = function() {
|
||||
$("stream-led").className = "led-gray";
|
||||
$("stream-led").title = "No stream from PiKVM";
|
||||
$("stream-led").title = "No stream from One-KVM";
|
||||
};
|
||||
|
||||
var __setControlsEnabled = function(enabled) {
|
||||
@ -317,7 +317,7 @@ export function Streamer() {
|
||||
if (text.length > 0) {
|
||||
title += text;
|
||||
} else {
|
||||
title += "No stream from PiKVM";
|
||||
title += "No stream from One-KVM";
|
||||
}
|
||||
}
|
||||
el_grab.innerText = el_info.innerText = title;
|
||||
|
||||
@ -111,7 +111,7 @@ export function JanusStreamer(__setActive, __setInactive, __setInfo, __organizeH
|
||||
}
|
||||
};
|
||||
|
||||
var __ensureJanus = function(internal) {
|
||||
var __ensureJanus = function(internal) {
|
||||
if (__janus === null && !__stop && (!__ensuring || internal)) {
|
||||
__ensuring = true;
|
||||
__setInactive();
|
||||
@ -131,10 +131,75 @@ export function JanusStreamer(__setActive, __setInactive, __setInfo, __organizeH
|
||||
});
|
||||
}
|
||||
};
|
||||
var __decodeIcePayload = function(payload) {
|
||||
if (typeof payload !== "string") {
|
||||
return null;
|
||||
}
|
||||
let data = payload.trim();
|
||||
if (data.startsWith("json:")) {
|
||||
data = data.slice(5).trim();
|
||||
}
|
||||
if (data.startsWith("{") || data.startsWith("[")) {
|
||||
try {
|
||||
let parsed = JSON.parse(data);
|
||||
if (Array.isArray(parsed)) {
|
||||
return parsed;
|
||||
} else if (parsed && Array.isArray(parsed.servers)) {
|
||||
return parsed.servers;
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
__logError("Can't parse ICE payload:", error);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
var __normalizeIceEntry = function(entry) {
|
||||
if (!entry || typeof entry !== "object") {
|
||||
return null;
|
||||
}
|
||||
let urls = entry.urls;
|
||||
if (typeof urls === "string") {
|
||||
urls = [urls];
|
||||
}
|
||||
if (!Array.isArray(urls) || urls.length === 0) {
|
||||
return null;
|
||||
}
|
||||
let normalized = {"urls": urls};
|
||||
if (entry.username) {
|
||||
normalized.username = entry.username;
|
||||
}
|
||||
if (entry.credential) {
|
||||
normalized.credential = entry.credential;
|
||||
}
|
||||
if (entry.credentialType) {
|
||||
normalized.credentialType = entry.credentialType;
|
||||
}
|
||||
return normalized;
|
||||
};
|
||||
|
||||
var __normalizeIceServers = function(payload) {
|
||||
let parsed = __decodeIcePayload(payload);
|
||||
if (!parsed) {
|
||||
return null;
|
||||
}
|
||||
let servers = [];
|
||||
for (let entry of parsed) {
|
||||
let normalized = __normalizeIceEntry(entry);
|
||||
if (normalized) {
|
||||
servers.push(normalized);
|
||||
}
|
||||
}
|
||||
return (servers.length > 0 ? servers : null);
|
||||
};
|
||||
|
||||
var __getIceServers = function() {
|
||||
if (__ice !== null && __ice.url) {
|
||||
__logInfo("Using the custom ICE Server got from uStreamer:", __ice);
|
||||
let normalized = __normalizeIceServers(__ice.url);
|
||||
if (normalized !== null) {
|
||||
return normalized;
|
||||
}
|
||||
return [{"urls": __ice.url}];
|
||||
} else {
|
||||
return [];
|
||||
|
||||
@ -166,7 +166,7 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __organizeH
|
||||
var __setupCodec = function(formats) {
|
||||
__closeDecoder();
|
||||
if (formats.h264 === undefined) {
|
||||
let msg = "No H.264 stream available on PiKVM";
|
||||
let msg = "No H.264 stream available on One-KVM";
|
||||
__setInfo(false, false, msg);
|
||||
__logInfo(msg);
|
||||
return;
|
||||
|
||||
1
web/share/svg/gostc.svg
Normal file
1
web/share/svg/gostc.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 30 KiB |
Loading…
x
Reference in New Issue
Block a user