Fix YouTube relay instabile e timer LIVE a zero in app
ffmpeg: probesize/analyzeduration su _air, restart più rapido e avvio relay al go_live. App: sincronizza started_at dal server e fallback al passaggio live. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -17,7 +17,9 @@ module Mediamtx
|
|||||||
# RTMP ancora connesso in pausa: non forzare live/reconnect.
|
# RTMP ancora connesso in pausa: non forzare live/reconnect.
|
||||||
elsif @session.may_go_live?
|
elsif @session.may_go_live?
|
||||||
@session.go_live!
|
@session.go_live!
|
||||||
Streams::Overlay::Refresh.call(@session.reload)
|
@session.reload
|
||||||
|
Streams::Overlay::Refresh.call(@session)
|
||||||
|
Streams::YoutubeRelay.ensure_publishing!(@session) if @session.platform == "youtube"
|
||||||
elsif @session.reconnecting? && @session.may_reconnect?
|
elsif @session.reconnecting? && @session.may_reconnect?
|
||||||
@session.reconnect!
|
@session.reconnect!
|
||||||
cancel_timeout
|
cancel_timeout
|
||||||
|
|||||||
@@ -56,10 +56,13 @@ module Streams
|
|||||||
return if session.stream_key.blank?
|
return if session.stream_key.blank?
|
||||||
return unless Streams::OverlayRelay.running?(session.id)
|
return unless Streams::OverlayRelay.running?(session.id)
|
||||||
|
|
||||||
|
pid = pid_for(session.id)
|
||||||
|
clear_pid(session.id) if pid.present? && !process_alive?(pid.to_i)
|
||||||
|
|
||||||
return if running?(session.id)
|
return if running?(session.id)
|
||||||
|
|
||||||
last_restart = redis.get(restart_debounce_key(session.id)).to_i
|
last_restart = redis.get(restart_debounce_key(session.id)).to_i
|
||||||
return if last_restart.positive? && (Time.now.to_i - last_restart) < 20
|
return if last_restart.positive? && (Time.now.to_i - last_restart) < 5
|
||||||
|
|
||||||
start(session)
|
start(session)
|
||||||
redis.set(restart_debounce_key(session.id), Time.now.to_i, ex: 300)
|
redis.set(restart_debounce_key(session.id), Time.now.to_i, ex: 300)
|
||||||
@@ -75,6 +78,7 @@ module Streams
|
|||||||
[
|
[
|
||||||
"ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "warning",
|
"ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "warning",
|
||||||
"-fflags", "+genpts+discardcorrupt", "-use_wallclock_as_timestamps", "1",
|
"-fflags", "+genpts+discardcorrupt", "-use_wallclock_as_timestamps", "1",
|
||||||
|
"-analyzeduration", "10000000", "-probesize", "10000000",
|
||||||
"-rw_timeout", "15000000",
|
"-rw_timeout", "15000000",
|
||||||
"-i", intake,
|
"-i", intake,
|
||||||
"-f", "lavfi", "-i", "anullsrc=channel_layout=mono:sample_rate=48000",
|
"-f", "lavfi", "-i", "anullsrc=channel_layout=mono:sample_rate=48000",
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ class _CameraScreenState extends ConsumerState<CameraScreen> {
|
|||||||
static const _previewKey = ValueKey<String>('camera_streaming_preview');
|
static const _previewKey = ValueKey<String>('camera_streaming_preview');
|
||||||
|
|
||||||
Timer? _elapsedTimer;
|
Timer? _elapsedTimer;
|
||||||
|
Timer? _sessionSyncTimer;
|
||||||
Timer? _telemetryTimer;
|
Timer? _telemetryTimer;
|
||||||
Timer? _statsTimer;
|
Timer? _statsTimer;
|
||||||
Timer? _scorePullTimer;
|
Timer? _scorePullTimer;
|
||||||
@@ -75,6 +76,9 @@ class _CameraScreenState extends ConsumerState<CameraScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
_sessionSyncTimer = Timer.periodic(const Duration(seconds: 15), (_) {
|
||||||
|
unawaited(_syncSessionStartedAt());
|
||||||
|
});
|
||||||
_telemetryTimer = Timer.periodic(const Duration(seconds: 10), (_) => _sendTelemetry());
|
_telemetryTimer = Timer.periodic(const Duration(seconds: 10), (_) => _sendTelemetry());
|
||||||
_statsTimer = Timer.periodic(const Duration(seconds: 30), (_) => _fetchStats());
|
_statsTimer = Timer.periodic(const Duration(seconds: 30), (_) => _fetchStats());
|
||||||
_scorePullTimer = Timer.periodic(const Duration(seconds: 2), (_) {
|
_scorePullTimer = Timer.periodic(const Duration(seconds: 2), (_) {
|
||||||
@@ -263,6 +267,20 @@ class _CameraScreenState extends ConsumerState<CameraScreen> {
|
|||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Il server imposta `started_at` solo al primo go_live; sincronizza per il timer LIVE.
|
||||||
|
Future<void> _syncSessionStartedAt() async {
|
||||||
|
final current = ref.read(sessionProvider).session;
|
||||||
|
if (current == null || current.startedAt != null || !current.isLive) return;
|
||||||
|
try {
|
||||||
|
final fresh = await ref.read(apiClientProvider).fetchSession(widget.sessionId);
|
||||||
|
if (!mounted || fresh.startedAt == null) return;
|
||||||
|
ref.read(sessionProvider.notifier).setSession(
|
||||||
|
fresh,
|
||||||
|
match: ref.read(sessionProvider).match,
|
||||||
|
);
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _lockOrientations() async {
|
Future<void> _lockOrientations() async {
|
||||||
await SystemChrome.setPreferredOrientations([
|
await SystemChrome.setPreferredOrientations([
|
||||||
DeviceOrientation.landscapeLeft,
|
DeviceOrientation.landscapeLeft,
|
||||||
@@ -497,6 +515,7 @@ class _CameraScreenState extends ConsumerState<CameraScreen> {
|
|||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_elapsedTimer?.cancel();
|
_elapsedTimer?.cancel();
|
||||||
|
_sessionSyncTimer?.cancel();
|
||||||
_telemetryTimer?.cancel();
|
_telemetryTimer?.cancel();
|
||||||
_statsTimer?.cancel();
|
_statsTimer?.cancel();
|
||||||
_scorePullTimer?.cancel();
|
_scorePullTimer?.cancel();
|
||||||
|
|||||||
@@ -111,6 +111,8 @@ class SessionNotifier extends StateNotifier<SessionState> {
|
|||||||
state = state.copyWith(cameraDevice: device);
|
state = state.copyWith(cameraDevice: device);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
final startedAt = s.startedAt ??
|
||||||
|
((incoming == 'live' || incoming == 'reconnecting') ? DateTime.now() : null);
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
session: StreamSession(
|
session: StreamSession(
|
||||||
id: s.id,
|
id: s.id,
|
||||||
@@ -127,7 +129,7 @@ class SessionNotifier extends StateNotifier<SessionState> {
|
|||||||
privacyStatus: s.privacyStatus,
|
privacyStatus: s.privacyStatus,
|
||||||
qualityPreset: s.qualityPreset,
|
qualityPreset: s.qualityPreset,
|
||||||
targetBitrate: s.targetBitrate,
|
targetBitrate: s.targetBitrate,
|
||||||
startedAt: s.startedAt,
|
startedAt: startedAt,
|
||||||
disconnectionCount: s.disconnectionCount,
|
disconnectionCount: s.disconnectionCount,
|
||||||
score: s.score,
|
score: s.score,
|
||||||
devices: s.devices,
|
devices: s.devices,
|
||||||
|
|||||||
Reference in New Issue
Block a user