diff --git a/backend/app/services/streams/youtube_relay.rb b/backend/app/services/streams/youtube_relay.rb index 23d6bca..4f838db 100644 --- a/backend/app/services/streams/youtube_relay.rb +++ b/backend/app/services/streams/youtube_relay.rb @@ -57,6 +57,7 @@ module Streams return unless session.platform == "youtube" return if session.terminal? return if session.stream_key.blank? + return unless Streams::OverlayRelay.running?(session.id) return if running?(session.id) diff --git a/backend/app/services/youtube/setup_broadcast.rb b/backend/app/services/youtube/setup_broadcast.rb index f45b483..8acf146 100644 --- a/backend/app/services/youtube/setup_broadcast.rb +++ b/backend/app/services/youtube/setup_broadcast.rb @@ -33,7 +33,10 @@ module Youtube rtmp_url: broadcast[:rtmp_url] ) - Streams::YoutubeRelay.start(@session) + # Relay solo quando _air pubblica (overlay pronto); altrimenti ffmpeg esce subito. + if Streams::OverlayRelay.running?(@session.id) + Streams::YoutubeRelay.start(@session) + end @session.stream_events.create!( event_type: "youtube_ready", metadata: { broadcast_id: broadcast[:broadcast_id] }, diff --git a/mobile/android/app/src/main/kotlin/com/matchlivetv/match_live_tv/StreamingEngine.kt b/mobile/android/app/src/main/kotlin/com/matchlivetv/match_live_tv/StreamingEngine.kt index cab0e3a..c3dd981 100644 --- a/mobile/android/app/src/main/kotlin/com/matchlivetv/match_live_tv/StreamingEngine.kt +++ b/mobile/android/app/src/main/kotlin/com/matchlivetv/match_live_tv/StreamingEngine.kt @@ -231,12 +231,17 @@ class StreamingEngine( notifyError("StreamingEngine rilasciato") return } - if (state == StreamingState.STREAMING || - state == StreamingState.CONNECTING || - state == StreamingState.RECONNECTING + // GiĆ  in onda sullo stesso endpoint RTMP. + if (state == StreamingState.STREAMING && + config?.rtmpUrl == streamConfig.rtmpUrl && + genericStream?.isStreaming == true ) { return } + // Nuova sessione o retry dopo errore: reset completo (evita startStream ignorato). + if (state != StreamingState.IDLE && state != StreamingState.PREVIEWING) { + stopStream() + } config = streamConfig lastError = null diff --git a/mobile/lib/features/camera_mode/camera_screen.dart b/mobile/lib/features/camera_mode/camera_screen.dart index 7fcc26e..1a79953 100644 --- a/mobile/lib/features/camera_mode/camera_screen.dart +++ b/mobile/lib/features/camera_mode/camera_screen.dart @@ -165,7 +165,11 @@ class _CameraScreenState extends ConsumerState { fresh.status == 'live' || fresh.status == 'reconnecting'); if (shouldAutoPublish) { - await Future.delayed(const Duration(milliseconds: 500)); + // Reset motore nativo (sessione precedente / RECONNECTING blocca startStream). + try { + await StreamingChannel.stopStream(); + } catch (_) {} + await Future.delayed(const Duration(milliseconds: 400)); final useFreshStart = justStartedSession || fresh.status == 'connecting'; if (useFreshStart) { @@ -497,6 +501,7 @@ class _CameraScreenState extends ConsumerState { _statsTimer?.cancel(); _scorePullTimer?.cancel(); _metricsSub?.cancel(); + unawaited(StreamingChannel.stopStream()); WakelockPlus.disable(); _unlockOrientation(); super.dispose(); diff --git a/mobile/lib/features/setup_wizard/step_network_test.dart b/mobile/lib/features/setup_wizard/step_network_test.dart index 315c8d6..9cc467f 100644 --- a/mobile/lib/features/setup_wizard/step_network_test.dart +++ b/mobile/lib/features/setup_wizard/step_network_test.dart @@ -189,7 +189,7 @@ class _StepNetworkTestState extends ConsumerState { try { final ws = ref.read(websocketServiceProvider); - await ws.connect(sessionId: started.id, deviceRole: 'camera'); + await ws.connect(sessionId: started.id, deviceRole: 'controller'); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar(