Reconexión RTSP y Fallback Switch en C# / .NET¶
Video Capture SDK .NET Media Player SDK .NET Media Blocks SDK .NET
Soporte multiplataforma
Los eventos de reconexión funcionan en todos los SDK. El FallbackSwitch declarativo se basa en GStreamer y funciona en Windows, macOS, Linux, Android e iOS en los motores X modernos y Media Blocks — no está disponible en los motores clásicos solo-Windows DirectShow. Consulte la matriz de soporte de plataformas y la guía de despliegue en Linux.
Las cámaras IP pierden conexiones — parpadeos de red, ciclos de energía, reinicios de router, falta de keyframes. Los SDKs VisioForge le dan dos formas de manejarlo:
- Reactivo — su código se suscribe a un evento de desconexión, luego detiene y reinicia la fuente.
- Declarativo — configure un
FallbackSwitchen la fuente, el pipeline cambia automáticamente a una imagen / tarjeta de texto / stream alternativo, y su código nunca ve el fallo.
Elija según el SDK que use y la UX que quiera. Esta guía cubre ambos enfoques en todos los SDK de la familia.
Qué enfoque está disponible dónde¶
| SDK | Reactivo (detectar + reiniciar) | Declarativo (FallbackSwitch) |
|---|---|---|
| VideoCaptureCore (clásico, Windows) | ✅ OnNetworkSourceDisconnect + DisconnectEventInterval | ⛔ |
| MediaPlayerCore (clásico, Windows) | ✅ OnNetworkSourceStop (FFMPEG) / OnError | ⛔ |
| VideoCaptureCoreX (multiplataforma) | ✅ pipeline OnNetworkSourceDisconnect / OnError | ✅ vía RTSPSourceSettings.FallbackSwitch |
| MediaPlayerCoreX (multiplataforma) | ✅ pipeline OnNetworkSourceDisconnect / OnError | ✅ |
| Media Blocks SDK | ✅ evento de pipeline | ✅ FallbackSwitchSourceBlock |
Regla general: en los motores clásicos Windows solo tiene reactivo. En cualquier motor moderno (multiplataforma), prefiera declarativo para la UX, y añada reactivo encima para telemetría.
Reactivo — VideoCaptureCore (clásico)¶
IPCameraSourceSettings.DisconnectEventInterval define cuánto espera el SDK sin frames entrantes antes de disparar el evento de desconexión. El valor por defecto es 10 segundos; bájelo a 3–5 segundos para vigilancia.
using VisioForge.Core.VideoCapture;
using VisioForge.Core.Types.VideoCapture;
videoCapture1.IP_Camera_Source = new IPCameraSourceSettings
{
URL = new Uri("rtsp://192.168.1.100:554/stream1"),
Login = "admin",
Password = "admin123",
Type = IPSourceEngine.Auto_LAV,
DisconnectEventInterval = TimeSpan.FromSeconds(5)
};
videoCapture1.OnNetworkSourceDisconnect += async (s, e) =>
{
// Retroceso exponencial: 1s, 2s, 4s, 8s... limitado a 30s.
int attempt = 0;
int delayMs = 1000;
while (attempt < 10)
{
await videoCapture1.StopAsync();
await Task.Delay(delayMs);
if (await videoCapture1.StartAsync()) break;
delayMs = Math.Min(delayMs * 2, 30_000);
attempt++;
}
};
videoCapture1.Mode = VideoCaptureMode.IPPreview;
await videoCapture1.StartAsync();
OnNetworkSourceDisconnect se dispara desde un hilo temporizador — marshalle al hilo UI (Invoke / Dispatcher.Invoke) antes de tocar controles.
Reactivo — MediaPlayerCore (clásico)¶
El MediaPlayerCore clásico expone OnNetworkSourceStop en el motor FFMPEG; para otros motores y cualquier fallo genérico, suscríbase a OnError.
using VisioForge.Core.MediaPlayer;
mediaPlayer1.OnNetworkSourceStop += async (s, e) =>
{
await mediaPlayer1.StopAsync();
await Task.Delay(2000);
await mediaPlayer1.StartAsync();
};
mediaPlayer1.OnError += (s, e) =>
{
// Log pero sin teardown — deja que el bucle de reintentos arriba maneje las caídas reales.
Debug.WriteLine($"Player error: {e.Message}");
};
Reactivo — pipelines Media Blocks¶
MediaBlocksPipeline expone OnNetworkSourceDisconnect con NetworkSourceDisconnectEventArgs rico — útil cuando múltiples fuentes RTSP comparten una app y necesita saber cuál se cayó.
using VisioForge.Core.Types.Events;
pipeline.OnNetworkSourceDisconnect += (s, e) =>
{
// e.SourceElementName — el elemento GStreamer que falló
// e.ErrorMessage — descripción corta
// e.DebugInfo — salida de debug de GStreamer (puede ser null)
// e.Uri — la URL RTSP que falló (puede ser null)
// e.Timestamp — cuándo el SDK detectó el fallo
Log($"[{e.Timestamp:HH:mm:ss}] {e.SourceElementName} cayó: {e.ErrorMessage}");
};
pipeline.OnError += (s, e) =>
{
Log($"Error de pipeline: {e.Message}");
};
Reactivo — motores X (VideoCaptureCoreX / MediaPlayerCoreX)¶
VideoCaptureCoreX y MediaPlayerCoreX no exponen públicamente su MediaBlocksPipeline interno. Para reaccionar a desconexiones en estos motores:
- Suscríbase a
OnErroren el motor — se dispara para todos los errores a nivel de pipeline incluyendo pérdidas de fuentes RTSP. - Use el
FallbackSwitchdeclarativo (abajo) para mantener la UI viva durante caídas transitorias. - Si necesita el
NetworkSourceDisconnectEventArgsgranular por fuente, construya el pipeline de captura directamente conMediaBlocksPipeline+RTSPSourceBlocky use el patrón Media Blocks de arriba.
videoCaptureCoreX.OnError += (s, e) =>
{
// e.Message lleva el error a nivel de motor; para caídas RTSP el texto incluye
// el nombre del elemento fuente. Emparejar este handler con un bucle de reintentos o FallbackSwitch.
Log($"VideoCaptureCoreX error: {e.Message}");
};
Declarativo — Visión general de FallbackSwitch¶
FallbackSwitch envuelve una fuente en vivo con failover automático. Cuando la fuente principal deja de producir frames por más de TimeoutMs, el pipeline cambia a un fallback preconfigurado (texto, imagen o media alternativo) y mantiene su VideoView renderizando. Cuando la fuente principal se recupera, el pipeline cambia de vuelta.
El contenedor FallbackSwitchSettings¶
public class FallbackSwitchSettings
{
public bool Enabled { get; set; } = false;
public FallbackSwitchSettingsBase Fallback { get; set; }
public bool EnableVideo { get; set; } = true;
public bool EnableAudio { get; set; } = true;
public int MinLatencyMs { get; set; } = 0;
public string FallbackVideoCaps { get; set; } // ej. "video/x-raw,width=1920,height=1080"
public string FallbackAudioCaps { get; set; } // ej. "audio/x-raw,rate=48000,channels=2"
}
Parámetros base (compartidos por todos los tipos de fallback)¶
public abstract class FallbackSwitchSettingsBase
{
public int TimeoutMs { get; set; } = 5000; // silencio antes de que el fallback se active
public int RestartTimeoutMs { get; set; } = 5000; // espera entre intentos de reinicio de la fuente principal
public int RetryTimeoutMs { get; set; } = 60000; // ventana de rendición tras fallos repetidos
public bool ImmediateFallback{ get; set; } = false; // mostrar fallback desde el primer hueco de frames
public bool RestartOnEos { get; set; } = false; // tratar EOS como fallo (para bucles)
public bool ManualUnblock { get; set; } = false; // requerir que la app llame a Unblock() en recuperación
}
Tres tipos de fallback¶
StaticTextFallbackSettings— renderiza una tarjeta de texto "Cámara offline".StaticImageFallbackSettings— muestra un logo de marca, última instantánea o placa "reconectando".MediaBlockFallbackSettings— reproduce un stream en vivo alternativo o un archivo en bucle.
Fallback — texto estático¶
using SkiaSharp;
using VisioForge.Core.Types.X.Sources;
var fallback = new FallbackSwitchSettings
{
Enabled = true,
Fallback = new StaticTextFallbackSettings
{
Text = "Cámara offline — reconectando...",
FontFamily = "Arial",
FontSize = 32f,
TextColor = SKColors.White,
BackgroundColor = SKColors.Black,
Position = new SKPoint(0.5f, 0.5f), // centro
TimeoutMs = 3000,
RestartTimeoutMs= 5000,
},
};
Fallback — imagen estática¶
var fallback = new FallbackSwitchSettings
{
Enabled = true,
FallbackVideoCaps = "video/x-raw,width=1920,height=1080",
Fallback = new StaticImageFallbackSettings
{
ImagePath = @"C:\assets\camera-offline.png",
ScaleToFit = true,
MaintainAspectRatio = true,
BackgroundColor = SKColors.Black,
TimeoutMs = 3000,
},
};
La imagen se compone sobre un fotograma del tamaño de FallbackVideoCaps; si las caps no indican tamaño, la imagen se reproduce tal cual, en su propio tamaño, y ScaleToFit, MaintainAspectRatio, Position y BackgroundColor no se aplican. Position alinea la imagen: 0 pegada a la izquierda/arriba, 0.5 centrada, 1 pegada a la derecha/abajo.
Fallback — fuente de media alternativa¶
var fallback = new FallbackSwitchSettings
{
Enabled = true,
Fallback = new MediaBlockFallbackSettings
{
FallbackUri = "file:///C:/assets/standby-loop.mp4",
RestartOnEos = true, // reiniciar el bucle cuando el archivo termine
TimeoutMs = 3000,
},
};
FallbackUri acepta cualquier URI que GStreamer pueda leer — file://, otro rtsp://, HLS, HTTP.
Usar FallbackSwitch — alto nivel (RTSPSourceSettings.FallbackSwitch)¶
La vía más simple: adjunte el objeto de configuración directamente a RTSPSourceSettings y páselo a VideoCaptureCoreX como siempre — el motor envuelve la fuente con un fallback switch automáticamente. Sin plomería extra de pipeline.
En
MediaPlayerCoreXy el Media Blocks SDK esta propiedad no la aplica un bloque de fuente directamente (RTSPSourceBlockregistra una advertencia si la activa ahí) — use en su lugar elFallbackSwitchSourceBlockde bajo nivel de la siguiente sección.
var rtsp = await RTSPSourceSettings.CreateAsync(
new Uri("rtsp://192.168.1.100:554/stream1"),
"admin", "admin123", audioEnabled: false);
rtsp.LowLatencyMode = true;
rtsp.FallbackSwitch = new FallbackSwitchSettings
{
Enabled = true,
Fallback = new StaticImageFallbackSettings
{
ImagePath = "offline.png",
TimeoutMs = 3000,
},
};
_videoCapture.Video_Source = rtsp;
await _videoCapture.StartAsync();
Usar FallbackSwitch — bajo nivel (FallbackSwitchSourceBlock)¶
En el Media Blocks SDK cablee el fallback directamente a nivel de pipeline vía FallbackSwitchSourceBlock. Envuelve una fuente RTSP, SRT, RTMP, HTTP o NDI y entrega vídeo y audio decodificados.
using VisioForge.Core.MediaBlocks;
using VisioForge.Core.MediaBlocks.Sources;
var fallbackSwitch = new FallbackSwitchSourceBlock(
mainSourceSettings: rtspSettings, // IVideoSourceSettings
fallbackSettings: new FallbackSwitchSettings
{
Enabled = true,
Fallback = new StaticTextFallbackSettings { Text = "OFFLINE" },
});
// Telemetría — funciona junto al UI declarativo
pipeline.OnNetworkSourceDisconnect += (s, e) =>
metrics.RecordDisconnect(e.SourceElementName, e.Uri, e.Timestamp);
// FallbackSwitchSourceBlock expone pads VideoOutput / AudioOutput separados; Output es el de vídeo.
pipeline.Connect(fallbackSwitch.VideoOutput, renderer.Input);
await pipeline.StartAsync();
// Inspeccionar estado en runtime:
string status = fallbackSwitch.GetStatus();
var stats = fallbackSwitch.GetStatistics();
// Si ManualUnblock está configurado, llama Unblock() cuando quieras que la reproducción reanude la fuente principal.
// fallbackSwitch.Unblock();
Patrones Comunes¶
Retroceso exponencial — en el bucle reactivo, duplique el retraso en cada reconexión fallida, limite a 30 s. Evite que machaque la caché ARP de una cámara muerta.
UX declarativa + telemetría reactiva — deje que FallbackSwitch mantenga la pantalla viva, y use pipeline.OnNetworkSourceDisconnect para alimentar su dashboard de monitoreo / alerta Slack / log NVR. Ningún enfoque excluye al otro.
Muro multi-cámara — nunca derribe todo el grid por un fallo. Consulte la guía de grid RTSP multi-cámara para el patrón un-pipeline-por-cámara; envuelva la fuente de cada cámara con un FallbackSwitchSourceBlock (en VideoCaptureCoreX basta con asignar FallbackSwitch en los ajustes de la fuente).
Nota multiplataforma — FallbackSwitch depende del elemento GStreamer fallbackswitch, que viene con el redist X. Los clásicos solo-Windows VideoCaptureCore / MediaPlayerCore no lo tienen — use el enfoque reactivo allí.
Solución de problemas¶
El fallback nunca se activa — ¿Enabled = true? ¿TimeoutMs menor que el gap que quiere capturar? Los primeros frames necesitan tener éxito antes de que el timeout empiece a contar — una cámara que falla en el primer handshake es un error de inicio de pipeline, no un timeout.
El fallback se activa pero nunca sale — la fuente principal no se está recuperando. Verifique RestartTimeoutMs (muy alto retrasa el siguiente reintento) y RetryTimeoutMs (después de esta ventana el bloque deja de reintentar). Con ManualUnblock = true debe llamar fallbackSwitch.Unblock() usted mismo.
El fallback de imagen muestra negro — desajuste de caps de códec entre el pipeline principal y el decodificador de fallback. Establezca FallbackVideoCaps explícitamente, ej. "video/x-raw,width=1920,height=1080,format=RGB", que coincida con el formato esperado del renderizador.
Fallback de texto — fuente / posición incorrectas — FontFamily debe existir en la plataforma objetivo (Arial está en Windows y macOS; prefiera DejaVuSans en Linux). Position son fracciones 0.0–1.0 del cuadro de video.
OnNetworkSourceDisconnect se dispara repetidamente — el bloque de fuente está reintentando y fallando en rápida sucesión. Suba RestartTimeoutMs a 10–15 s en una red reconocidamente inestable, o envuelve el logging con un debounce.
Excepciones de hilo UI — OnNetworkSourceDisconnect se dispara desde el hilo del bus GStreamer. Use Dispatcher.Invoke (WPF) / Control.Invoke (WinForms) / MainThread.BeginInvokeOnMainThread (MAUI) antes de tocar controles.
Documentación relacionada¶
- Inmersión profunda en el protocolo RTSP — cómo funciona RTSP, opciones de transporte y arquitectura de streaming
- Configuración de fuente de cámara RTSP — referencia de
IPCameraSourceSettings/RTSPSourceSettings - Integración de cámara IP ONVIF — descubrimiento + PTZ; combina re-descubrimiento ONVIF con FallbackSwitch para cámaras difíciles
- Reproductor RTSP de Media Blocks — pipeline mono-cámara
- Grid RTSP multi-cámara (muro NVR) — muro 4×4 con fallback por celda
- Guardar stream RTSP sin re-codificación — archivar junto con la vista previa en vivo
- Salida servidor RTSP — resiliencia del lado servidor para su propio stream
- Matriz de soporte de plataformas — detalles de códecs y aceleración por hardware
Visite nuestra página de GitHub para ejemplos de código incluyendo demos RTSP de vista previa, grabación y MultiView.