From 4c7be615f1c551c365309129770d5ed76f7f3465 Mon Sep 17 00:00:00 2001 From: Emiliano Frascaro Date: Thu, 4 Jun 2026 09:07:06 +0200 Subject: [PATCH] Fix freeze YouTube al cambio punteggio: PNG atomica e relay stabile rsvg scrive su .tmp poi rename; il feeder ignora PNG parziali. YouTube relay: no doppio avvio, reconnect RTMP; overlay restart solo dopo 3 miss. Co-authored-by: Cursor --- backend/app/services/streams/overlay/refresh.rb | 4 +++- backend/app/services/streams/overlay_relay.rb | 13 ++++++++++++- backend/app/services/streams/youtube_relay.rb | 3 +++ backend/bin/overlay_png_feeder.rb | 15 +++++++++++++-- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/backend/app/services/streams/overlay/refresh.rb b/backend/app/services/streams/overlay/refresh.rb index 5cad538..685fb7f 100644 --- a/backend/app/services/streams/overlay/refresh.rb +++ b/backend/app/services/streams/overlay/refresh.rb @@ -37,10 +37,12 @@ module Streams end def render_png!(svg_path, png_path) - unless system("rsvg-convert", "-w", "1280", "-h", "720", "-o", png_path.to_s, svg_path.to_s, out: File::NULL, err: File::NULL) + tmp = "#{png_path}.tmp" + unless system("rsvg-convert", "-w", "1280", "-h", "720", "-o", tmp, svg_path.to_s, out: File::NULL, err: File::NULL) raise Error, "rsvg-convert fallito per session #{@session.id}" end + File.rename(tmp, png_path) FileUtils.touch(png_path) end end diff --git a/backend/app/services/streams/overlay_relay.rb b/backend/app/services/streams/overlay_relay.rb index c0d3bd2..3b65191 100644 --- a/backend/app/services/streams/overlay_relay.rb +++ b/backend/app/services/streams/overlay_relay.rb @@ -86,8 +86,15 @@ module Streams end if running?(session.id) - Rails.logger.warn("[OverlayRelay] _air non attivo, restart session=#{session.id}") + misses = redis.incr(overlay_air_miss_key(session.id)).to_i + redis.expire(overlay_air_miss_key(session.id), 120) + return if misses < 3 + + redis.del(overlay_air_miss_key(session.id)) + Rails.logger.warn("[OverlayRelay] _air non attivo dopo #{misses} tentativi, restart session=#{session.id}") stop(session) + else + redis.del(overlay_air_miss_key(session.id)) end start(session) @@ -181,6 +188,10 @@ module Streams redis.get(format(REDIS_KEY, session_id)) end + def overlay_air_miss_key(session_id) + format("overlay_relay:air_miss:%s", session_id) + end + def process_alive?(pid) Process.kill(0, pid.to_i) true diff --git a/backend/app/services/streams/youtube_relay.rb b/backend/app/services/streams/youtube_relay.rb index 29e2ff0..89f789f 100644 --- a/backend/app/services/streams/youtube_relay.rb +++ b/backend/app/services/streams/youtube_relay.rb @@ -12,6 +12,8 @@ module Streams return if session.stream_key.blank? return if session.terminal? + return pid_for(session.id).to_i if running?(session.id) + stop(session) if pid_for(session.id).present? log_path = log_file(session) @@ -80,6 +82,7 @@ module Streams "-fflags", "+genpts+discardcorrupt", "-use_wallclock_as_timestamps", "1", "-analyzeduration", "10000000", "-probesize", "10000000", "-rw_timeout", "15000000", + "-reconnect", "1", "-reconnect_at_eof", "1", "-reconnect_streamed", "1", "-reconnect_delay_max", "5", "-i", intake, "-f", "lavfi", "-i", "anullsrc=channel_layout=mono:sample_rate=48000", "-map", "0:v:0", "-map", "1:a:0", diff --git a/backend/bin/overlay_png_feeder.rb b/backend/bin/overlay_png_feeder.rb index 3de695e..4ead83a 100644 --- a/backend/bin/overlay_png_feeder.rb +++ b/backend/bin/overlay_png_feeder.rb @@ -16,13 +16,24 @@ trap("TERM") { stop = true } trap("INT") { stop = true } # image2pipe richiede frame continui: inviamo la PNG a ritmo fisso ( anche se invariata ). +last_bytes = nil File.open(pipe_path, "wb") do |pipe| loop do break if stop if File.exist?(png_path) - pipe.write(File.binread(png_path)) - pipe.flush + bytes = File.binread(png_path) + # Evita frame corrotti durante rsvg-convert (file parziale prima del rename atomico). + next if bytes.bytesize < 512 + + if bytes != last_bytes + pipe.write(bytes) + pipe.flush + last_bytes = bytes + elsif last_bytes + pipe.write(last_bytes) + pipe.flush + end end sleep poll_sec