param( [Parameter(Position = 0)] [string]$Action = "start", [string]$MediaConfig = "configs/sample_cam1.json", [string]$VideoFile = "", [string]$SafesightdConfig = "safesightd.json" ) $ErrorActionPreference = "Stop" $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $controlDir = (Resolve-Path (Join-Path $scriptDir "..")).Path $edgeDir = (Resolve-Path (Join-Path $controlDir "..\safesight-edge")).Path # ── paths ────────────────────────────────────────────── $mediamtx = "C:\Software\mediamtx\mediamtx.exe" $ffmpeg = "ffmpeg" $minio = "C:\Users\Tellme\minio\minio.exe" $minioData = "C:\Users\Tellme\minio\myminio" $mockAlarm = Join-Path $scriptDir "../safesight-edge/scripts/mock_alarm_server.py" $safesightdExe = Join-Path $controlDir "safesightd.exe" $safesightdConfig = Join-Path $controlDir $SafesightdConfig $mediaServerConfig = Join-Path $edgeDir $MediaConfig $mediaServerExe = Join-Path $edgeDir "build\media-server.exe" $rtspUrl = "rtsp://10.0.0.49:8554/cam" $defaultVideo = "C:\Users\Tellme\Pictures\人脸库\reg_008_unk_011_多人_正面_黑色鞋_白色鞋_1.mp4" function Write-Banner { param([string]$Text) Write-Host ("`n" + ("=" * 54)) -ForegroundColor Cyan Write-Host " $Text" -ForegroundColor Cyan Write-Host ("=" * 54) -ForegroundColor Cyan } function Start-Detached { param([string]$Name, [string]$Exe, [string[]]$Args, [switch]$Visible) $found = (Test-Path $Exe) -or (Get-Command $Exe -ErrorAction SilentlyContinue) if (-not $found) { Write-Host " [SKIP] $Name : 未找到 $Exe" -ForegroundColor Yellow return $null } Write-Host " [START] $Name" -ForegroundColor Green $style = if ($Visible) { "Normal" } else { "Minimized" } if ($Args -and $Args.Count -gt 0) { $proc = Start-Process -FilePath $Exe -ArgumentList $Args -WindowStyle $style -PassThru } else { $proc = Start-Process -FilePath $Exe -WindowStyle $style -PassThru } Start-Sleep -Milliseconds 500 return $proc } function Stop-ByName { param([string]$Name) $procs = Get-Process -Name $Name -ErrorAction SilentlyContinue if ($procs) { $procs | Stop-Process -Force Write-Host " [STOP] $Name" -ForegroundColor DarkYellow } } # ════════════════════════════════════════════════════════ # START # ════════════════════════════════════════════════════════ function Invoke-Start { # 1. RTSP server Write-Banner "1/5 RTSP Server (mediamtx)" Start-Detached "mediamtx" $mediamtx # 2. ffmpeg push Write-Banner "2/5 RTSP 推流 (ffmpeg)" $videoToUse = if ($VideoFile -and (Test-Path $VideoFile)) { $VideoFile } elseif (Test-Path $defaultVideo) { $defaultVideo } else { $null } if ($videoToUse) { $ffArgs = @("-re", "-stream_loop", "-1", "-i", $videoToUse, "-c", "copy", "-rtsp_transport", "tcp", "-f", "rtsp", $rtspUrl) Write-Host " 视频文件: $videoToUse" } else { # Try to find a webcam $webcam = (& $ffmpeg -list_devices true -f dshow -i dummy 2>&1 | Select-String '\[dshow.*"(.+)"' | ForEach-Object { $_.Matches.Groups[1].Value } | Select-Object -First 1) if ($webcam) { Write-Host " 摄像头: $webcam" $ffArgs = @("-f", "dshow", "-rtbufsize", "100M", "-video_size", "1280x720", "-framerate", "30", "-vcodec", "mjpeg", "-i", "video=$webcam", "-c:v", "libx264", "-preset", "ultrafast", "-pix_fmt", "yuv420p", "-f", "rtsp", $rtspUrl) } else { Write-Host " [SKIP] 未指定视频文件,也未检测到摄像头" -ForegroundColor Yellow $ffArgs = $null } } if ($ffArgs) { Start-Detached "ffmpeg" $ffmpeg $ffArgs -Visible } # 3. Media server (runs on RK3588, not local) Write-Banner "3/5 Media Server (3588 Edge)" Write-Host " [INFO] media-server 运行在 3588 上,不在本地启动" -ForegroundColor DarkGray # 4. Mock alarm server (if uv available) Write-Banner "4/5 Mock Alarm Server" if (Get-Command uv -ErrorAction SilentlyContinue) { $alarmProc = Start-Process -FilePath "uv" ` -ArgumentList "run", "--with", "flask", $mockAlarm ` -WorkingDirectory (Join-Path $edgeDir "scripts") ` -WindowStyle Minimized ` -PassThru Write-Host " [START] mock_alarm_server (PID=$($alarmProc.Id))" -ForegroundColor Green } else { Write-Host " [SKIP] uv 未安装" -ForegroundColor Yellow } # 5. Management server (safesightd) Write-Banner "5/5 Management Server (safesightd)" if (Test-Path $safesightdExe) { Start-Process -FilePath $safesightdExe ` -ArgumentList $safesightdConfig ` -WorkingDirectory $controlDir ` -WindowStyle Minimized Write-Host " [START] safesightd" -ForegroundColor Green Write-Host " URL: http://localhost:18080" -ForegroundColor White } elseif (Get-Command go -ErrorAction SilentlyContinue) { Write-Host " [START] go run (safesightd 未编译)" -ForegroundColor Green Start-Process -FilePath "go" ` -ArgumentList "run", ".\cmd\safesightd\", $safesightdConfig ` -WorkingDirectory $controlDir ` -WindowStyle Minimized } else { Write-Host " [SKIP] 既无 exe 也无 go" -ForegroundColor Yellow } # 6. MinIO (optional) Write-Banner "MinIO (object storage)" if (Test-Path $minio) { if (-not (Test-Path $minioData)) { New-Item -ItemType Directory -Path $minioData -Force | Out-Null } Start-Detached "minio" $minio @("server", $minioData, "--address", ":9000", "--console-address", ":9001") Write-Host " Console: http://localhost:9001 (admin/password)" -ForegroundColor White } # Summary Write-Banner "全部启动完成" Write-Host " RTSP 输入: $rtspUrl" Write-Host " 默认视频: $defaultVideo" Write-Host " 管理端: http://localhost:18080" Write-Host " 视频监控: http://localhost:18080/ui/monitor" Write-Host " MinIO: http://localhost:9001" Write-Host "" Write-Host " 停止所有: powershell .\scripts\dev-env.ps1 stop" -ForegroundColor DarkGray } # ════════════════════════════════════════════════════════ # STOP # ════════════════════════════════════════════════════════ function Invoke-Stop { Write-Banner "停止所有模拟环境进程" Stop-ByName "mediamtx" Stop-ByName "ffmpeg" Stop-ByName "media-server" Stop-ByName "minio" Stop-ByName "safesightd" # Kill python mock_alarm if running Get-Process -Name "python" -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -match "mock_alarm" } | Stop-Process -Force Write-Host "" Write-Host " 已全部停止" -ForegroundColor Green } # ════════════════════════════════════════════════════════ # STATUS # ════════════════════════════════════════════════════════ function Invoke-Status { Write-Banner "模拟环境状态" @( @{Name="mediamtx"; Port=8554; URL="rtsp://localhost:8554"}, @{Name="safesightd"; Port=18080; URL="http://localhost:18080/health"}, @{Name="minio"; Port=9000; URL="http://localhost:9000"} ) | ForEach-Object { $proc = Get-Process -Name $_.Name -ErrorAction SilentlyContinue $status = if ($proc) { "● 运行中" } else { "○ 未运行" } $color = if ($proc) { "Green" } else { "DarkGray" } Write-Host " $status $($_.Name) $($_.URL)" -ForegroundColor $color } } # ════════════════════════════════════════════════════════ switch ($Action.ToLowerInvariant()) { "start" { Invoke-Start } "stop" { Invoke-Stop } "status" { Invoke-Status } default { Write-Host "用法: powershell .\scripts\dev-env.ps1 [start|stop|status]" Write-Host "" Write-Host "可选参数 (仅 start):" Write-Host " -MediaConfig media-server 配置 (默认: configs/sample_cam1.json)" Write-Host " -VideoFile 推流视频文件路径" } }