Corregge avvio RTMP bloccato dopo errore o cambio sessione.

Il motore Android ignorava startStream in CONNECTING/RECONNECTING; la camera
ora resetta prima di pubblicare. YouTube relay parte solo con overlay attivo.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-04 07:32:15 +02:00
parent 1d67459bbe
commit 2ccbfea75e
5 changed files with 20 additions and 6 deletions

View File

@@ -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)

View File

@@ -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] },

View File

@@ -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

View File

@@ -165,7 +165,11 @@ class _CameraScreenState extends ConsumerState<CameraScreen> {
fresh.status == 'live' ||
fresh.status == 'reconnecting');
if (shouldAutoPublish) {
await Future<void>.delayed(const Duration(milliseconds: 500));
// Reset motore nativo (sessione precedente / RECONNECTING blocca startStream).
try {
await StreamingChannel.stopStream();
} catch (_) {}
await Future<void>.delayed(const Duration(milliseconds: 400));
final useFreshStart =
justStartedSession || fresh.status == 'connecting';
if (useFreshStart) {
@@ -497,6 +501,7 @@ class _CameraScreenState extends ConsumerState<CameraScreen> {
_statsTimer?.cancel();
_scorePullTimer?.cancel();
_metricsSub?.cancel();
unawaited(StreamingChannel.stopStream());
WakelockPlus.disable();
_unlockOrientation();
super.dispose();

View File

@@ -189,7 +189,7 @@ class _StepNetworkTestState extends ConsumerState<StepNetworkTest> {
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(