fix: use cumulative CPU average for ffmpeg monitoring
Delta sampling over 2s returns 0-1 integer ticks for low-CPU processes (ffmpeg copy mode ~0.3% CPU), always rounding to 0.0%. Switch to cumulative average (total_cpu_ticks / clk_tck / elapsed) — same method ps uses — which gives stable, accurate readings. Also show 2 decimal places so values like 0.34% are visible.
This commit is contained in:
+18
-16
@@ -106,6 +106,7 @@ class BroadcastGroup:
|
||||
|
||||
# ffmpeg process tracking
|
||||
self._ffmpeg_pid: int | None = None
|
||||
self._ffmpeg_start_time: float | None = None # wall time when ffmpeg started
|
||||
self._ffmpeg_cpu: float = 0.0
|
||||
self._cpu_task: asyncio.Task | None = None
|
||||
|
||||
@@ -210,7 +211,7 @@ class BroadcastGroup:
|
||||
"stream_info": self.stream_info,
|
||||
# ffmpeg process info
|
||||
"ffmpeg_pid": self._ffmpeg_pid,
|
||||
"ffmpeg_cpu_pct": round(self._ffmpeg_cpu, 1),
|
||||
"ffmpeg_cpu_pct": round(self._ffmpeg_cpu, 2),
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
@@ -218,32 +219,31 @@ class BroadcastGroup:
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
async def _monitor_cpu(self) -> None:
|
||||
"""Sample /proc/{pid}/stat every 2 s and update self._ffmpeg_cpu (%)."""
|
||||
"""Update self._ffmpeg_cpu every 3 s using cumulative average since start.
|
||||
|
||||
Delta-based sampling fails for low-CPU processes (copy-mode ffmpeg uses
|
||||
<1% CPU) because the tick delta in 2 s is 0-1 integer ticks, making the
|
||||
result always 0.0%. Cumulative average ((total_cpu_ticks / clk_tck) /
|
||||
elapsed_wall) gives a stable, accurate reading — the same method ps uses.
|
||||
"""
|
||||
clk_tck = float(os.sysconf(os.sysconf_names.get("SC_CLK_TCK", 2)) or 100)
|
||||
prev_ticks: int | None = None
|
||||
prev_wall: float | None = None
|
||||
|
||||
while self._running:
|
||||
await asyncio.sleep(2)
|
||||
await asyncio.sleep(3)
|
||||
pid = self._ffmpeg_pid
|
||||
if pid is None:
|
||||
start_time = self._ffmpeg_start_time
|
||||
if pid is None or start_time is None:
|
||||
self._ffmpeg_cpu = 0.0
|
||||
prev_ticks = prev_wall = None
|
||||
continue
|
||||
try:
|
||||
with open(f"/proc/{pid}/stat") as f:
|
||||
parts = f.read().split()
|
||||
ticks = int(parts[13]) + int(parts[14]) # utime + stime
|
||||
wall = time.time()
|
||||
if prev_ticks is not None and prev_wall is not None:
|
||||
dt = wall - prev_wall
|
||||
if dt > 0:
|
||||
self._ffmpeg_cpu = (ticks - prev_ticks) / clk_tck / dt * 100
|
||||
prev_ticks = ticks
|
||||
prev_wall = wall
|
||||
cpu_ticks = int(parts[13]) + int(parts[14]) # utime + stime
|
||||
elapsed = time.time() - start_time
|
||||
if elapsed > 0:
|
||||
self._ffmpeg_cpu = cpu_ticks / clk_tck / elapsed * 100
|
||||
except (FileNotFoundError, IndexError, ValueError, OSError):
|
||||
self._ffmpeg_cpu = 0.0
|
||||
prev_ticks = prev_wall = None
|
||||
|
||||
async def _probe_info(self) -> None:
|
||||
from .probe import probe_stream
|
||||
@@ -331,6 +331,7 @@ class BroadcastGroup:
|
||||
self._status = "ok"
|
||||
self._last_data_at = time.time()
|
||||
self._ffmpeg_pid = proc.pid
|
||||
self._ffmpeg_start_time = time.time()
|
||||
logger.info(
|
||||
f"[ch{self.channel_id}] ffmpeg started pid={proc.pid} "
|
||||
f"(reconnects so far: {self._reconnect_count})"
|
||||
@@ -369,6 +370,7 @@ class BroadcastGroup:
|
||||
|
||||
finally:
|
||||
self._ffmpeg_pid = None
|
||||
self._ffmpeg_start_time = None
|
||||
self._ffmpeg_cpu = 0.0
|
||||
stderr_task.cancel()
|
||||
try:
|
||||
|
||||
@@ -237,7 +237,7 @@ export default function Dashboard() {
|
||||
<StatCard
|
||||
icon={<Cpu size={20} className={totalCpu > 80 ? 'text-red-400' : totalCpu > 40 ? 'text-yellow-400' : 'text-cyan-400'} />}
|
||||
label="ffmpeg CPU total"
|
||||
value={`${totalCpu.toFixed(1)}%`}
|
||||
value={`${totalCpu.toFixed(2)}%`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -330,7 +330,7 @@ export default function Dashboard() {
|
||||
s.ffmpeg_cpu_pct > 40 ? 'text-yellow-400' :
|
||||
'text-gray-400'
|
||||
}`}>
|
||||
{s.ffmpeg_cpu_pct.toFixed(1)}%
|
||||
{s.ffmpeg_cpu_pct.toFixed(2)}%
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user