Fix YouTube waiting: transcode verso RTMP e sync stato live.

Il relay non usa più copy (stream inactive su YouTube); PublisherSync
nel refresh overlay; activate solo quando lo stream YouTube è active.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-04 08:32:30 +02:00
parent 2c5f4f82eb
commit d08cd36527
3 changed files with 34 additions and 8 deletions

View File

@@ -26,6 +26,7 @@ class OverlayRefreshJob < ApplicationJob
return self.class.cancel_chain(session_id) unless Streams::OverlayRelay.running?(session_id)
Streams::Overlay::Refresh.call(session)
Mediamtx::PublisherSync.new(session).call if session.status.in?(%w[connecting reconnecting live paused])
if session.platform == "youtube"
Streams::YoutubeRelay.ensure_publishing!(session)
end

View File

@@ -19,14 +19,9 @@ module Streams
intake = mediamtx_intake_url(session)
output = "rtmp://a.rtmp.youtube.com/live2/#{session.stream_key}"
# YouTube richiede FLV H.264/AAC stabili; -c copy lascia spesso streamStatus=inactive.
pid = Process.spawn(
"ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "warning",
"-rw_timeout", "15000000",
"-i", intake,
"-c", "copy",
"-f", "flv",
output,
%i[out err] => log_path,
*youtube_ffmpeg_args(intake, output, log_path),
pgroup: true
)
Process.detach(pid)
@@ -62,13 +57,33 @@ module Streams
return if running?(session.id)
last_restart = redis.get(restart_debounce_key(session.id)).to_i
return if last_restart.positive? && (Time.now.to_i - last_restart) < 20
start(session)
redis.set(restart_debounce_key(session.id), Time.now.to_i, ex: 300)
rescue Error => e
Rails.logger.warn("[YoutubeRelay] ensure_publishing session=#{session.id}: #{e.message}")
end
private
def youtube_ffmpeg_args(intake, output, log_path)
[
"ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "warning",
"-fflags", "+genpts+discardcorrupt", "-use_wallclock_as_timestamps", "1",
"-rw_timeout", "15000000",
"-i", intake,
"-map", "0:v:0", "-map", "0:a:0",
"-c:v", "libx264", "-preset", "veryfast", "-tune", "zerolatency",
"-pix_fmt", "yuv420p", "-r", "30", "-g", "30", "-keyint_min", "30", "-sc_threshold", "0",
"-b:v", "4000k", "-maxrate", "4000k", "-bufsize", "8000k",
"-c:a", "aac", "-b:a", "128k", "-ar", "48000", "-ac", "1",
"-f", "flv", output,
%i[out err] => log_path
]
end
def schedule_youtube_activate(session)
return if session.youtube_broadcast_id.blank?
@@ -88,6 +103,10 @@ module Streams
@redis ||= Redis.new(url: ENV.fetch("REDIS_URL", "redis://localhost:6379/0"))
end
def restart_debounce_key(session_id)
format("youtube_relay:debounce:%s", session_id)
end
def store_pid(session_id, pid)
redis.set(format(REDIS_KEY, session_id), pid, ex: 48.hours.to_i)
end

View File

@@ -56,12 +56,18 @@ module Youtube
return if @credential.blank? || missing_oauth_config?
client = authorized_client
item = client.list_live_broadcasts("status", id: broadcast_id).items&.first
item = client.list_live_broadcasts("status", id: broadcast_id, mine: true).items&.first
return unless item
status = item.status.life_cycle_status.to_s
return if status == "live"
stream_id = item.content_details&.bound_stream_id
if stream_id.present?
stream = client.list_live_streams("status", id: stream_id, mine: true).items&.first
return if stream&.status&.stream_status != "active"
end
if %w[ready created].include?(status)
client.transition_live_broadcast("testing", broadcast_id, "id,status")
status = "testing"