Go边缘代理: 单文件可执行+嵌入式MediaMTX+监控+注册
- edge-agent/: Go项目, 支持Windows托盘/Linux无头两种模式 - 使用go:embed将MediaMTX二进制嵌入单一可执行文件 - 首次运行自动释放MediaMTX并生成边缘节点配置 - 自动注册到origin (POST /api/nodes/register) - 10秒心跳上报CPU/RAM/带宽/连接数 (gopsutil采集) - Windows: getlantern/systray托盘菜单(状态/CPU/MEM/带宽/退出) - Linux: headless模式,适合systemd守护 - build.sh/build.ps1: 交叉编译脚本(Linux/Windows) - 更新start.sh/品牌名StreamCore
This commit is contained in:
1 parent
980b91f41f
commit
409e5d314e
14 files changed
+800
-11
No files matched your search
@@ -0,0 +1,132 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MediaMTXManager struct {
|
||||
cfg Config
|
||||
fs embed.FS
|
||||
binPath string
|
||||
configPath string
|
||||
cmd *exec.Cmd
|
||||
}
|
||||
|
||||
func NewMediaMTXManager(cfg Config, fs embed.FS) *MediaMTXManager {
|
||||
return &MediaMTXManager{
|
||||
cfg: cfg,
|
||||
fs: fs,
|
||||
binPath: filepath.Join(cfg.DataDir, "mediamtx"),
|
||||
configPath: filepath.Join(cfg.DataDir, "mediamtx.yml"),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MediaMTXManager) Extract() error {
|
||||
if _, err := os.Stat(m.binPath); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
srcPath := "mediamtx/"
|
||||
if runtime.GOOS == "windows" {
|
||||
srcPath += "mediamtx.exe"
|
||||
} else {
|
||||
srcPath += "mediamtx"
|
||||
}
|
||||
|
||||
entries, err := m.fs.ReadDir("mediamtx")
|
||||
if err != nil {
|
||||
return fmt.Errorf("read embedded dir: %w", err)
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
name := entry.Name()
|
||||
if runtime.GOOS == "windows" && !strings.Contains(name, ".exe") {
|
||||
continue
|
||||
}
|
||||
if runtime.GOOS != "windows" && strings.Contains(name, ".exe") {
|
||||
continue
|
||||
}
|
||||
|
||||
src, err := m.fs.Open("mediamtx/" + name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open embedded %s: %w", name, err)
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
destName := "mediamtx"
|
||||
if runtime.GOOS == "windows" {
|
||||
destName = "mediamtx.exe"
|
||||
}
|
||||
destPath := filepath.Join(m.cfg.DataDir, destName)
|
||||
dest, err := os.OpenFile(destPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
|
||||
if err != nil {
|
||||
src.Close()
|
||||
return fmt.Errorf("create %s: %w", destPath, err)
|
||||
}
|
||||
|
||||
if _, err := io.Copy(dest, src); err != nil {
|
||||
dest.Close()
|
||||
src.Close()
|
||||
return fmt.Errorf("copy: %w", err)
|
||||
}
|
||||
dest.Close()
|
||||
src.Close()
|
||||
m.binPath = destPath
|
||||
break
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MediaMTXManager) WriteConfig() error {
|
||||
config := fmt.Sprintf(`logLevel: info
|
||||
logDestinations: [stdout]
|
||||
|
||||
rtmp: no
|
||||
rtsp: no
|
||||
|
||||
hls: yes
|
||||
hlsAddress: :%d
|
||||
|
||||
api: yes
|
||||
apiAddress: :%d
|
||||
|
||||
paths:
|
||||
all:
|
||||
source: rtmp://%s:1935/live/$MTX_PATH
|
||||
sourceOnDemand: yes
|
||||
`,
|
||||
m.cfg.HlsPort,
|
||||
m.cfg.ApiPort,
|
||||
strings.TrimPrefix(strings.TrimPrefix(m.cfg.OriginURL, "http://"), "https://"),
|
||||
)
|
||||
|
||||
return os.WriteFile(m.configPath, []byte(config), 0644)
|
||||
}
|
||||
|
||||
func (m *MediaMTXManager) Start() error {
|
||||
m.cmd = exec.Command(m.binPath, m.configPath)
|
||||
m.cmd.Stdout = os.Stdout
|
||||
m.cmd.Stderr = os.Stderr
|
||||
if err := m.cmd.Start(); err != nil {
|
||||
return fmt.Errorf("start mediamtx: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MediaMTXManager) Stop() {
|
||||
if m.cmd != nil && m.cmd.Process != nil {
|
||||
m.cmd.Process.Kill()
|
||||
m.cmd.Wait()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user