diff --git a/.gitignore b/.gitignore index 41823552..027b1b67 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,7 @@ __pycache__/ # Benchmark results tools/benchmark_results_*.txt + +# iKuai package outputs +ikuai-support/.staging/ +ikuai-support/dist/ diff --git a/ikuai-support/rtp2httpd/app/.env b/ikuai-support/rtp2httpd/app/.env new file mode 100644 index 00000000..956e02f9 --- /dev/null +++ b/ikuai-support/rtp2httpd/app/.env @@ -0,0 +1,7 @@ +HOST_IP=0.0.0.0 +APP_PORT_WEB=5140 +RTP2HTTPD_CONFIG=app/config/rtp2httpd.conf +RTP2HTTPD_MAXCLIENTS=20 +RTP2HTTPD_WORKERS=1 +RTP2HTTPD_VERBOSITY=2 +RTP2HTTPD_EXTRA_ARGS= diff --git a/ikuai-support/rtp2httpd/app/bin/.gitkeep b/ikuai-support/rtp2httpd/app/bin/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/ikuai-support/rtp2httpd/app/config/rtp2httpd.conf b/ikuai-support/rtp2httpd/app/config/rtp2httpd.conf new file mode 100644 index 00000000..e57099b7 --- /dev/null +++ b/ikuai-support/rtp2httpd/app/config/rtp2httpd.conf @@ -0,0 +1,50 @@ +# rtp2httpd configuration for native iKuai package. +# +# scripts/start.sh passes listen address, port, max clients, worker count, +# and verbosity from app/.env as command-line arguments. Values below are +# kept as safe defaults and examples for advanced settings. + +[global] +verbosity = 2 +maxclients = 20 +workers = 1 + +# Keep UDPxy-compatible URLs enabled. Example: +# http://192.168.1.1:5140/rtp/239.253.64.120:5140 +udpxy = yes + +# Set an upstream interface only if the iKuai host routing table cannot +# reach the IPTV multicast network correctly. +;upstream-interface = eth0 +;upstream-interface-multicast = eth0 +;upstream-interface-fcc = eth0 +;upstream-interface-rtsp = eth0 +;upstream-interface-http = eth0 + +# Optional external playlist. HTTP/HTTPS fetching requires curl on the host. +;external-m3u = https://example.com/playlist.m3u +external-m3u-update-interval = 7200 + +# Enable only if your network drops multicast membership periodically. +;mcast-rejoin-interval = 60 + +# Restrict FCC UDP ports when upstream NAT or firewall rules require it. +;fcc-listen-port-range = 40000-40100 + +# Enable only on kernels that support MSG_ZEROCOPY and when locked memory +# limits are configured properly. +zerocopy-on-send = no + +# Enable only if ffmpeg is installed and reachable from iKuai's shell. +video-snapshot = no +;ffmpeg-path = /usr/bin/ffmpeg +;ffmpeg-args = -hwaccel none + +[bind] +* 5140 + +[services] +# You can paste an M3U playlist here as an alternative to external-m3u. +;#EXTM3U +;#EXTINF:-1 group-title="IPTV",CCTV-1 +;rtp://239.253.64.120:5140 diff --git a/ikuai-support/rtp2httpd/app/environment b/ikuai-support/rtp2httpd/app/environment new file mode 100644 index 00000000..c850888a --- /dev/null +++ b/ikuai-support/rtp2httpd/app/environment @@ -0,0 +1,2 @@ +# Runtime environment variables loaded by scripts/start.sh after app/.env. +# TZ=Asia/Shanghai diff --git a/ikuai-support/rtp2httpd/app/option.json b/ikuai-support/rtp2httpd/app/option.json new file mode 100644 index 00000000..590e3001 --- /dev/null +++ b/ikuai-support/rtp2httpd/app/option.json @@ -0,0 +1,80 @@ +[ + { + "default": "0.0.0.0", + "attrname": "HOST_IP", + "label": { + "en": "Listen address", + "zh": "监听地址" + }, + "required": true, + "scope": "config", + "type": "text", + "min": 1, + "max": 64 + }, + { + "default": 5140, + "attrname": "APP_PORT_WEB", + "label": { + "en": "Web and stream port", + "zh": "Web 与流服务端口" + }, + "required": true, + "scope": "config", + "type": "integer", + "min": 1, + "max": 65535 + }, + { + "default": 20, + "attrname": "RTP2HTTPD_MAXCLIENTS", + "label": { + "en": "Maximum clients", + "zh": "最大客户端数" + }, + "required": true, + "scope": "config", + "type": "integer", + "min": 1, + "max": 4096 + }, + { + "default": 1, + "attrname": "RTP2HTTPD_WORKERS", + "label": { + "en": "Worker processes", + "zh": "工作进程数" + }, + "required": true, + "scope": "config", + "type": "integer", + "min": 1, + "max": 64 + }, + { + "default": 2, + "attrname": "RTP2HTTPD_VERBOSITY", + "label": { + "en": "Log verbosity", + "zh": "日志级别" + }, + "required": true, + "scope": "config", + "type": "integer", + "min": 0, + "max": 4 + }, + { + "default": "", + "attrname": "RTP2HTTPD_EXTRA_ARGS", + "label": { + "en": "Extra command arguments", + "zh": "额外启动参数" + }, + "required": false, + "scope": "config", + "type": "text", + "min": 0, + "max": 1024 + } +] diff --git a/ikuai-support/rtp2httpd/changelog b/ikuai-support/rtp2httpd/changelog new file mode 100644 index 00000000..056c9cfd --- /dev/null +++ b/ikuai-support/rtp2httpd/changelog @@ -0,0 +1,3 @@ +v3.12.2 + +首次上架爱快应用市场。 diff --git a/ikuai-support/rtp2httpd/log/install.log b/ikuai-support/rtp2httpd/log/install.log new file mode 100644 index 00000000..e69de29b diff --git a/ikuai-support/rtp2httpd/log/run.log b/ikuai-support/rtp2httpd/log/run.log new file mode 100644 index 00000000..e69de29b diff --git a/ikuai-support/rtp2httpd/manifest.json b/ikuai-support/rtp2httpd/manifest.json new file mode 100644 index 00000000..267b8cb3 --- /dev/null +++ b/ikuai-support/rtp2httpd/manifest.json @@ -0,0 +1,17 @@ +{ + "name": "rtp2httpd", + "version": "v3.12.2", + "display_name": "rtp2httpd", + "image": "rtp2httpd-3.12.2-x86_64-static", + "description": "面向 IPTV 场景的组播、RTSP、HTTP 流媒体转发服务,内置状态页面和 Web 播放器。可替代 udpxy 和 msd_lite。", + "type": "0", + "maintainer": "stackia", + "maintainer_url": "https://github.com/stackia/rtp2httpd", + "distributor": "stackia", + "distributor_url": "https://github.com/stackia/rtp2httpd", + "requirements": { + "memory": "64MB", + "storage": "16MB", + "os_min_version": "4.0.0" + } +} diff --git a/ikuai-support/rtp2httpd/readme b/ikuai-support/rtp2httpd/readme new file mode 100644 index 00000000..da924256 --- /dev/null +++ b/ikuai-support/rtp2httpd/readme @@ -0,0 +1,7 @@ +rtp2httpd 通过 scripts/start.sh 和 scripts/stop.sh 在爱快系统中以原生方式运行,不依赖 Docker Compose。 + +此软件包需要在 app/bin/rtp2httpd 放置静态 Linux 二进制文件。可以使用 scripts/build-rtp2httpd-ipkg.sh 从官方 Release 二进制构建完整 .ipkg,也可以通过 BINARY_PATH 指定本地二进制文件。 + +服务监听 APP_PORT_WEB,默认端口为 5140,同时提供状态页面、Web 播放器和 IPTV 流媒体代理入口。默认状态页面为 /status,Web 播放器为 /player。 + +组播接收依赖爱快主机网络环境。如果无法播放,请先确认爱快主机能够访问 IPTV 组播网络,并检查 IGMP、组播转发和路由配置。若系统路由不能正确选择 IPTV 接口,可在 app/config/rtp2httpd.conf 中配置 upstream-interface 相关参数。 diff --git a/ikuai-support/rtp2httpd/scripts/POST_INST.sh b/ikuai-support/rtp2httpd/scripts/POST_INST.sh new file mode 100755 index 00000000..5d4c1edc --- /dev/null +++ b/ikuai-support/rtp2httpd/scripts/POST_INST.sh @@ -0,0 +1,13 @@ +#!/bin/sh +set -eu + +SCRIPT_DIR=$(CDPATH= cd "$(dirname "$0")" && pwd) +PKG_DIR=$(dirname "$SCRIPT_DIR") +INSTALL_LOG="$PKG_DIR/log/install.log" + +if [ ! -x "$PKG_DIR/app/bin/rtp2httpd" ]; then + printf '%s POST_INST warning: app/bin/rtp2httpd is missing or not executable\n' "$(date '+%Y-%m-%d %H:%M:%S')" >> "$INSTALL_LOG" + exit 0 +fi + +printf '%s POST_INST completed\n' "$(date '+%Y-%m-%d %H:%M:%S')" >> "$INSTALL_LOG" diff --git a/ikuai-support/rtp2httpd/scripts/POST_UNINST.sh b/ikuai-support/rtp2httpd/scripts/POST_UNINST.sh new file mode 100755 index 00000000..1c505ae0 --- /dev/null +++ b/ikuai-support/rtp2httpd/scripts/POST_UNINST.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -eu + +SCRIPT_DIR=$(CDPATH= cd "$(dirname "$0")" && pwd) +PKG_DIR=$(dirname "$SCRIPT_DIR") +INSTALL_LOG="$PKG_DIR/log/install.log" + +rm -f "$PKG_DIR/app/cache/rtp2httpd.pid" +printf '%s POST_UNINST completed\n' "$(date '+%Y-%m-%d %H:%M:%S')" >> "$INSTALL_LOG" diff --git a/ikuai-support/rtp2httpd/scripts/PRE_INST.sh b/ikuai-support/rtp2httpd/scripts/PRE_INST.sh new file mode 100755 index 00000000..7bf03c4d --- /dev/null +++ b/ikuai-support/rtp2httpd/scripts/PRE_INST.sh @@ -0,0 +1,14 @@ +#!/bin/sh +set -eu + +SCRIPT_DIR=$(CDPATH= cd "$(dirname "$0")" && pwd) +PKG_DIR=$(dirname "$SCRIPT_DIR") +INSTALL_LOG="$PKG_DIR/log/install.log" + +mkdir -p "$PKG_DIR/app/bin" "$PKG_DIR/app/cache" "$PKG_DIR/app/config" "$PKG_DIR/app/data" "$PKG_DIR/log" + +if [ -f "$PKG_DIR/app/bin/rtp2httpd" ]; then + chmod 755 "$PKG_DIR/app/bin/rtp2httpd" +fi + +printf '%s PRE_INST completed\n' "$(date '+%Y-%m-%d %H:%M:%S')" >> "$INSTALL_LOG" diff --git a/ikuai-support/rtp2httpd/scripts/PRE_UNINST.sh b/ikuai-support/rtp2httpd/scripts/PRE_UNINST.sh new file mode 100755 index 00000000..6c9a39ea --- /dev/null +++ b/ikuai-support/rtp2httpd/scripts/PRE_UNINST.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -eu + +SCRIPT_DIR=$(CDPATH= cd "$(dirname "$0")" && pwd) +PKG_DIR=$(dirname "$SCRIPT_DIR") +INSTALL_LOG="$PKG_DIR/log/install.log" + +if [ -x "$SCRIPT_DIR/stop.sh" ]; then + "$SCRIPT_DIR/stop.sh" || true +fi + +printf '%s PRE_UNINST completed\n' "$(date '+%Y-%m-%d %H:%M:%S')" >> "$INSTALL_LOG" diff --git a/ikuai-support/rtp2httpd/scripts/start.sh b/ikuai-support/rtp2httpd/scripts/start.sh new file mode 100755 index 00000000..13164166 --- /dev/null +++ b/ikuai-support/rtp2httpd/scripts/start.sh @@ -0,0 +1,93 @@ +#!/bin/sh +set -eu + +SCRIPT_DIR=$(CDPATH= cd "$(dirname "$0")" && pwd) +PKG_DIR=$(dirname "$SCRIPT_DIR") +CONFIG_ENV="$PKG_DIR/app/.env" +RUNTIME_ENV="$PKG_DIR/app/environment" +BIN="$PKG_DIR/app/bin/rtp2httpd" +PIDFILE="$PKG_DIR/app/cache/rtp2httpd.pid" +RUN_LOG="$PKG_DIR/log/run.log" + +log() { + printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" >> "$RUN_LOG" +} + +load_env_file() { + if [ -f "$1" ]; then + set -a + . "$1" + set +a + fi +} + +mkdir -p "$PKG_DIR/app/cache" "$PKG_DIR/app/data" "$PKG_DIR/log" +load_env_file "$CONFIG_ENV" +load_env_file "$RUNTIME_ENV" + +: "${HOST_IP:=0.0.0.0}" +: "${APP_PORT_WEB:=5140}" +: "${RTP2HTTPD_CONFIG:=app/config/rtp2httpd.conf}" +: "${RTP2HTTPD_MAXCLIENTS:=20}" +: "${RTP2HTTPD_WORKERS:=1}" +: "${RTP2HTTPD_VERBOSITY:=2}" +: "${RTP2HTTPD_EXTRA_ARGS:=}" + +case "$RTP2HTTPD_CONFIG" in + /*) CONFIG_FILE="$RTP2HTTPD_CONFIG" ;; + *) CONFIG_FILE="$PKG_DIR/$RTP2HTTPD_CONFIG" ;; +esac + +if [ ! -x "$BIN" ]; then + log "Binary is missing or not executable: $BIN" + exit 1 +fi + +if [ ! -f "$CONFIG_FILE" ]; then + log "Config file is missing: $CONFIG_FILE" + exit 1 +fi + +if [ -f "$PIDFILE" ]; then + OLD_PID=$(cat "$PIDFILE" 2>/dev/null || true) + if [ -n "$OLD_PID" ] && kill -0 "$OLD_PID" 2>/dev/null; then + log "rtp2httpd is already running, pid=$OLD_PID" + exit 0 + fi + rm -f "$PIDFILE" +fi + +if [ "$HOST_IP" = "*" ] || [ "$HOST_IP" = "0.0.0.0" ]; then + LISTEN="$APP_PORT_WEB" +else + LISTEN="$HOST_IP:$APP_PORT_WEB" +fi + +export HOME="$PKG_DIR/app/data" + +set -- "$BIN" \ + --config "$CONFIG_FILE" \ + --listen "$LISTEN" \ + --maxclients "$RTP2HTTPD_MAXCLIENTS" \ + --workers "$RTP2HTTPD_WORKERS" \ + --verbose "$RTP2HTTPD_VERBOSITY" + +if [ -n "$RTP2HTTPD_EXTRA_ARGS" ]; then + # Intentionally split extra arguments so advanced users can pass native CLI flags. + set -- "$@" $RTP2HTTPD_EXTRA_ARGS +fi + +log "Starting rtp2httpd: $*" +nohup "$@" >> "$RUN_LOG" 2>&1 & +PID=$! +printf '%s\n' "$PID" > "$PIDFILE" + +sleep 1 +if kill -0 "$PID" 2>/dev/null; then + log "rtp2httpd started, pid=$PID" + exit 0 +fi + +rm -f "$PIDFILE" +log "rtp2httpd failed to start" +exit 1 diff --git a/ikuai-support/rtp2httpd/scripts/stop.sh b/ikuai-support/rtp2httpd/scripts/stop.sh new file mode 100755 index 00000000..0e831fd0 --- /dev/null +++ b/ikuai-support/rtp2httpd/scripts/stop.sh @@ -0,0 +1,41 @@ +#!/bin/sh +set -eu + +SCRIPT_DIR=$(CDPATH= cd "$(dirname "$0")" && pwd) +PKG_DIR=$(dirname "$SCRIPT_DIR") +PIDFILE="$PKG_DIR/app/cache/rtp2httpd.pid" +RUN_LOG="$PKG_DIR/log/run.log" + +log() { + mkdir -p "$PKG_DIR/log" + printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" >> "$RUN_LOG" +} + +if [ ! -f "$PIDFILE" ]; then + log "rtp2httpd is not running: pid file missing" + exit 0 +fi + +PID=$(cat "$PIDFILE" 2>/dev/null || true) +if [ -z "$PID" ] || ! kill -0 "$PID" 2>/dev/null; then + rm -f "$PIDFILE" + log "rtp2httpd is not running: stale pid file removed" + exit 0 +fi + +log "Stopping rtp2httpd, pid=$PID" +kill "$PID" 2>/dev/null || true + +COUNT=0 +while kill -0 "$PID" 2>/dev/null; do + COUNT=$((COUNT + 1)) + if [ "$COUNT" -ge 10 ]; then + log "rtp2httpd did not stop after TERM, sending KILL" + kill -9 "$PID" 2>/dev/null || true + break + fi + sleep 1 +done + +rm -f "$PIDFILE" +log "rtp2httpd stopped" diff --git a/ikuai-support/rtp2httpd/ui/ico/.gitkeep b/ikuai-support/rtp2httpd/ui/ico/.gitkeep new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/ikuai-support/rtp2httpd/ui/ico/.gitkeep @@ -0,0 +1 @@ + diff --git a/ikuai-support/rtp2httpd/ui/images/1.png b/ikuai-support/rtp2httpd/ui/images/1.png new file mode 100644 index 00000000..bc3c7b84 Binary files /dev/null and b/ikuai-support/rtp2httpd/ui/images/1.png differ diff --git a/ikuai-support/rtp2httpd/ui/images/2.png b/ikuai-support/rtp2httpd/ui/images/2.png new file mode 100644 index 00000000..1e42560d Binary files /dev/null and b/ikuai-support/rtp2httpd/ui/images/2.png differ diff --git a/scripts/build-rtp2httpd-ipkg.sh b/scripts/build-rtp2httpd-ipkg.sh new file mode 100755 index 00000000..2fc7bf5b --- /dev/null +++ b/scripts/build-rtp2httpd-ipkg.sh @@ -0,0 +1,68 @@ +#!/bin/sh +set -eu + +export COPYFILE_DISABLE=1 +export COPY_EXTENDED_ATTRIBUTES_DISABLE=1 + +SCRIPT_DIR=$(CDPATH= cd "$(dirname "$0")" && pwd) +PROJECT_ROOT=$(dirname "$SCRIPT_DIR") +SUPPORT_DIR="$PROJECT_ROOT/ikuai-support" +PACKAGE_SRC="$SUPPORT_DIR/rtp2httpd" +STAGING_ROOT="$SUPPORT_DIR/.staging" +STAGING_PACKAGE="$STAGING_ROOT/rtp2httpd" +DIST_DIR="$SUPPORT_DIR/dist" +MANIFEST="$PACKAGE_SRC/manifest.json" +ICON_SOURCE="$PROJECT_ROOT/web-ui/public/assets/icon.png" + +VERSION=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"v\{0,1\}\([^"]*\)".*/\1/p' "$MANIFEST" | head -1) +VERSION=${1:-$VERSION} +ARCH=${2:-x86_64} + +if [ -z "$VERSION" ]; then + echo "Version is required" >&2 + exit 1 +fi + +BINARY_NAME="rtp2httpd-$VERSION-$ARCH" +RELEASE_URL="https://github.com/stackia/rtp2httpd/releases/download/v$VERSION/$BINARY_NAME" + +rm -rf "$STAGING_ROOT" +mkdir -p "$STAGING_PACKAGE" "$DIST_DIR" +cp -R "$PACKAGE_SRC"/. "$STAGING_PACKAGE"/ +rm -f "$STAGING_PACKAGE/app/bin/.gitkeep" + +if [ ! -f "$ICON_SOURCE" ]; then + echo "Icon source does not exist: $ICON_SOURCE" >&2 + exit 1 +fi +mkdir -p "$STAGING_PACKAGE/ui/ico" +rm -f "$STAGING_PACKAGE/ui/ico/app.png" +cp "$ICON_SOURCE" "$STAGING_PACKAGE/ui/ico/app.png" + +if [ -n "${BINARY_PATH:-}" ]; then + if [ ! -f "$BINARY_PATH" ]; then + echo "BINARY_PATH does not exist: $BINARY_PATH" >&2 + exit 1 + fi + cp "$BINARY_PATH" "$STAGING_PACKAGE/app/bin/rtp2httpd" +else + if command -v curl >/dev/null 2>&1; then + curl -fL "$RELEASE_URL" -o "$STAGING_PACKAGE/app/bin/rtp2httpd" + elif command -v wget >/dev/null 2>&1; then + wget -O "$STAGING_PACKAGE/app/bin/rtp2httpd" "$RELEASE_URL" + else + echo "curl or wget is required unless BINARY_PATH is set" >&2 + exit 1 + fi +fi + +chmod 755 "$STAGING_PACKAGE/app/bin/rtp2httpd" +chmod 755 "$STAGING_PACKAGE"/scripts/*.sh + +OUTPUT="$DIST_DIR/rtp2httpd-$VERSION-$ARCH.ipkg" +( + cd "$STAGING_ROOT" + tar -czf "$OUTPUT" rtp2httpd +) + +echo "$OUTPUT"