Saltar a contenido

Captura MP4 de Cámara ONVIF con Postprocesamiento

Media Blocks SDK .Net

Soporte multiplataforma

El Media Blocks SDK funciona en Windows, macOS, Linux, Android e iOS a través de GStreamer. Consulte la matriz de soporte de plataformas para detalles de códecs y aceleración por hardware, y la guía de despliegue para Linux para Ubuntu / NVIDIA Jetson / Raspberry Pi.

Info

Para ejemplos de trabajo completos, consulte: - RTSP Preview Demo — muestra vista previa de cámara ONVIF con postprocesamiento - IP Capture Demo (Video Capture SDK) — alternativa usando Video Capture SDK

Para documentación completa de ONVIF, consulte la Guía de Integración de Cámaras IP ONVIF. !!!

Tabla de Contenidos

Resumen

Esta guía demuestra cómo capturar video de cámaras IP ONVIF aplicando varios efectos de postprocesamiento antes de codificar a MP4. A diferencia de la grabación pass-through que preserva el stream original, el postprocesamiento requiere decodificar el video, aplicar transformaciones y volver a codificar.

Este enfoque es útil cuando necesita: - Redimensionar o recortar video - Aplicar correcciones de brillo, contraste o color - Agregar marcas de agua o logos - Desenfocar rostros por privacidad - Aplicar efectos artísticos o filtros - Combinar múltiples pasos de procesamiento

Cuándo Usar Postprocesamiento

Use postprocesamiento cuando: - Necesita redimensionar video (ej. de 4K a 1080p) - Quiere aplicar efectos de video (brillo, contraste, etc.) - Necesita agregar superposiciones o marcas de agua - Los requisitos de privacidad demandan desenfoque de rostros - Está combinando múltiples feeds de cámaras - Necesita aplicar algoritmos IA/CV

Use pass-through (sin postprocesamiento) cuando: - Quiere preservar la calidad original del video - Necesita minimizar el uso de CPU - El espacio de almacenamiento no es una preocupación - La duración de grabación es larga (horas/días)

Para grabación pass-through, consulte Guardar Stream RTSP sin Re-codificar.

Requisitos Previos

  1. VisioForge Media Blocks SDK .NET instalado
  2. Cámara ONVIF accesible en su red
  3. Credenciales válidas de cámara (usuario y contraseña)
  4. Conocimiento básico de:
  5. C# async/await
  6. Protocolo ONVIF
  7. Parámetros de codificación de video

Configuración Básica: Descubrimiento y Conexión ONVIF

Primero, descubra y conecte con su cámara ONVIF:

using VisioForge.Core.ONVIFDiscovery;
using VisioForge.Core.ONVIFX;

// Descubrir cámaras ONVIF
var discovery = new Discovery();
var cts = new CancellationTokenSource();
string cameraUrl = null;

await discovery.Discover(5, (device) =>
{
    if (device.XAdresses?.Any() == true)
    {
        cameraUrl = device.XAdresses.FirstOrDefault();
        Console.WriteLine($"Cámara encontrada: {cameraUrl}");
    }
}, cts.Token);

if (string.IsNullOrEmpty(cameraUrl))
{
    Console.WriteLine("No se encontraron cámaras ONVIF");
    return;
}

// Conectar a la cámara
var onvifClient = new ONVIFClientX();
bool connected = await onvifClient.ConnectAsync(cameraUrl, "admin", "password");

if (!connected)
{
    Console.WriteLine("Error al conectar con la cámara");
    return;
}

// Obtener URL del stream RTSP
var profiles = await onvifClient.GetProfilesAsync();
var streamUri = await onvifClient.GetStreamUriAsync(profiles[0]);
string rtspUrl = streamUri.Uri;

Console.WriteLine($"URL RTSP: {rtspUrl}");

Ejemplo 1: Redimensionar Video

Redimensione el video de una cámara ONVIF antes de guardarlo a MP4. La API de Media Blocks recibe objetos de configuración — RTSPSourceBlock se construye desde un RTSPSourceSettings, el codificador desde su objeto de settings, el sink MP4 entrega pads de entrada mediante CreateNewInput(...), y los enlaces se declaran sobre el pipeline.

using VisioForge.Core.MediaBlocks;
using VisioForge.Core.MediaBlocks.Sources;
using VisioForge.Core.MediaBlocks.VideoProcessing;
using VisioForge.Core.MediaBlocks.VideoEncoders;
using VisioForge.Core.MediaBlocks.AudioEncoders;
using VisioForge.Core.MediaBlocks.Sinks;
using VisioForge.Core.Types.X.Sources;
using VisioForge.Core.Types;

// Crear pipeline
var pipeline = new MediaBlocksPipeline();

// Fuente RTSP de la cámara ONVIF. Credenciales y latencia viven en el objeto
// settings; use la factoría async para que el settings descubra la info de códecs.
var rtspSettings = await RTSPSourceSettings.CreateAsync(
    new Uri(rtspUrl), "admin", "password", audioEnabled: true);
var rtspSource = new RTSPSourceBlock(rtspSettings);

// Bloque de redimensionamiento — reducir a 1280x720. El ctor (ancho, alto) es un
// atajo para `new VideoResizeBlock(new ResizeVideoEffect(w, h))`.
var videoResize = new VideoResizeBlock(1280, 720);

// Codificador H.264. Elige una clase concreta de settings — Bitrate está en Kbit/s (2000 = 2 Mbps).
// OpenH264EncoderSettings funciona en todas las plataformas; cámbialo por NVENC / QSV / AMF / MFH264 para aceleración GPU.
var h264Settings = new OpenH264EncoderSettings { Bitrate = 2000 };
var h264Encoder = new H264EncoderBlock(h264Settings);

// Codificador de audio AAC (Bitrate en Kbit/s — 128 = 128 kbps).
var aacEncoder = new AACEncoderBlock(new VOAACEncoderSettings { Bitrate = 128 });

// Sink MP4 — el ctor por nombre de archivo es la ruta más corta a un escritor MP4 válido.
var mp4Sink = new MP4SinkBlock("output_resized.mp4");

// Agregar cada bloque al pipeline antes de cablear los enlaces
pipeline.AddBlock(rtspSource);
pipeline.AddBlock(videoResize);
pipeline.AddBlock(h264Encoder);
pipeline.AddBlock(aacEncoder);
pipeline.AddBlock(mp4Sink);

// Cablear el camino de video (RTSP video → resize → H.264 → pad de video del MP4)
pipeline.Connect(rtspSource.VideoOutput, videoResize.Input);
pipeline.Connect(videoResize.Output, h264Encoder.Input);
pipeline.Connect(h264Encoder.Output, mp4Sink.CreateNewInput(MediaBlockPadMediaType.Video));

// Cablear el camino de audio (RTSP audio → AAC → pad de audio del MP4)
pipeline.Connect(rtspSource.AudioOutput, aacEncoder.Input);
pipeline.Connect(aacEncoder.Output, mp4Sink.CreateNewInput(MediaBlockPadMediaType.Audio));

// Iniciar grabación
await pipeline.StartAsync();

Console.WriteLine("Grabando con redimensionamiento... Presione Enter para detener.");
Console.ReadLine();

// Detener y limpiar
await pipeline.StopAsync();
await pipeline.DisposeAsync();

Console.WriteLine("Grabación completa: output_resized.mp4");

Ejemplo 2: Aplicar Efectos de Video

Aplique ajustes de brillo, contraste, tono y saturación. Los bloques de procesamiento reciben un objeto settings en el ctor; los settings llevan las perillas.

using VisioForge.Core.MediaBlocks.VideoProcessing;
using VisioForge.Core.Types.X.VideoEffects;

// Crear pipeline
var pipeline = new MediaBlocksPipeline();

// Fuente RTSP
var rtspSettings = await RTSPSourceSettings.CreateAsync(
    new Uri(rtspUrl), "admin", "password", audioEnabled: true);
var rtspSource = new RTSPSourceBlock(rtspSettings);

// Bloque de balance de video — las perillas viven en el objeto settings.
// Brightness: -1.0..1.0 (0.2 = ligeramente más brillante)
// Contrast:    0.0..2.0 (1.15 = +15% contraste)
// Saturation:  0.0..2.0 (1.3  = +30% saturación)
// Hue:        -1.0..1.0 (0.0  = sin cambio)
var balanceSettings = new VideoBalanceVideoEffect
{
    Brightness = 0.2,
    Contrast   = 1.15,
    Saturation = 1.3,
    Hue        = 0.0,
};
var videoBalance = new VideoBalanceBlock(balanceSettings);

// El bloque de efectos de color recibe un preset directamente en el ctor
var colorEffects = new ColorEffectsBlock(ColorEffectsPreset.Sepia);

// Codificador H.264 (3 Mbps)
var h264Settings = new OpenH264EncoderSettings { Bitrate = 3000 };
var h264Encoder = new H264EncoderBlock(h264Settings);

// AAC audio
var aacEncoder = new AACEncoderBlock(new VOAACEncoderSettings { Bitrate = 128 });

// Salida MP4
var mp4Sink = new MP4SinkBlock("output_enhanced.mp4");

// Agregar todo al pipeline, luego cablear
pipeline.AddBlock(rtspSource);
pipeline.AddBlock(videoBalance);
pipeline.AddBlock(colorEffects);
pipeline.AddBlock(h264Encoder);
pipeline.AddBlock(aacEncoder);
pipeline.AddBlock(mp4Sink);

// Cadena de video: RTSP → balance → color-effects → H.264 → MP4
pipeline.Connect(rtspSource.VideoOutput, videoBalance.Input);
pipeline.Connect(videoBalance.Output, colorEffects.Input);
pipeline.Connect(colorEffects.Output, h264Encoder.Input);
pipeline.Connect(h264Encoder.Output, mp4Sink.CreateNewInput(MediaBlockPadMediaType.Video));

// Cadena de audio
pipeline.Connect(rtspSource.AudioOutput, aacEncoder.Input);
pipeline.Connect(aacEncoder.Output, mp4Sink.CreateNewInput(MediaBlockPadMediaType.Audio));

// Iniciar
await pipeline.StartAsync();

Console.WriteLine("Grabando con mejoras...");
await Task.Delay(TimeSpan.FromMinutes(5)); // Grabar por 5 minutos

await pipeline.StopAsync();
await pipeline.DisposeAsync();

Ejemplo 3: Desenfoque de Rostros en Tiempo Real

Aplique detección y desenfoque de rostros para proteger la privacidad:

using VisioForge.Core.MediaBlocks.OpenCV;
using VisioForge.Core.Types.X.OpenCV;

// Crear pipeline
var pipeline = new MediaBlocksPipeline();

// Fuente RTSP
var rtspSettings = await RTSPSourceSettings.CreateAsync(
    new Uri(rtspUrl), "admin", "password", audioEnabled: true);
var rtspSource = new RTSPSourceBlock(rtspSettings);

// CVFaceBlurBlock — detección y desenfoque automático de rostros vía OpenCV
var faceBlurSettings = new CVFaceBlurSettings
{
    ScaleFactor      = 1.25,
    MinNeighbors     = 3,
    MinSize          = new Size(30, 30),
    MainCascadeFile  = "haarcascade_frontalface_default.xml",
};
var faceBlur = new CVFaceBlurBlock(faceBlurSettings);

// Codificador H.264
var h264Settings = new OpenH264EncoderSettings { Bitrate = 3000 };
var h264Encoder = new H264EncoderBlock(h264Settings);

// AAC audio
var aacEncoder = new AACEncoderBlock(new VOAACEncoderSettings { Bitrate = 128 });

// Salida MP4
var mp4Sink = new MP4SinkBlock("output_face_blur.mp4");

// Agregar + cablear
pipeline.AddBlock(rtspSource);
pipeline.AddBlock(faceBlur);
pipeline.AddBlock(h264Encoder);
pipeline.AddBlock(aacEncoder);
pipeline.AddBlock(mp4Sink);

pipeline.Connect(rtspSource.VideoOutput, faceBlur.Input);
pipeline.Connect(faceBlur.Output, h264Encoder.Input);
pipeline.Connect(h264Encoder.Output, mp4Sink.CreateNewInput(MediaBlockPadMediaType.Video));

pipeline.Connect(rtspSource.AudioOutput, aacEncoder.Input);
pipeline.Connect(aacEncoder.Output, mp4Sink.CreateNewInput(MediaBlockPadMediaType.Audio));

// Iniciar
await pipeline.StartAsync();

Añada una marca de agua y una superposición de texto. ImageOverlayBlock recibe un nombre de archivo o un ImageOverlaySettings; la posición/transparencia viven en el settings. TextOverlayBlock siempre recibe un TextOverlaySettings.

using VisioForge.Core.MediaBlocks.VideoProcessing;
using VisioForge.Core.Types.X.VideoEffects;
using SkiaSharp;

// Crear pipeline
var pipeline = new MediaBlocksPipeline();

// Fuente RTSP
var rtspSettings = await RTSPSourceSettings.CreateAsync(
    new Uri(rtspUrl), "admin", "password", audioEnabled: true);
var rtspSource = new RTSPSourceBlock(rtspSettings);

// Logo / marca de agua: el ctor por nombre de archivo carga la imagen. Las
// perillas de posición viven en el settings; la transparencia es Alpha (0..1).
var logoOverlay = new ImageOverlayBlock(new ImageOverlaySettings("watermark.png")
{
    X     = 10,   // 10 px desde la izquierda
    Y     = 10,   // 10 px desde arriba
    Alpha = 0.7,  // 0..1
});

// Texto estático. TextOverlaySettings lleva Text, posición, Color (SKColor) y
// perillas de fuente — consulte la referencia de OverlayManagerText para todas.
var textOverlay = new TextOverlayBlock(new TextOverlaySettings("Camera 1")
{
    Color = SKColors.White,
});

// Codificador H.264
var h264Settings = new OpenH264EncoderSettings { Bitrate = 3000 };
var h264Encoder = new H264EncoderBlock(h264Settings);

// AAC audio
var aacEncoder = new AACEncoderBlock(new VOAACEncoderSettings { Bitrate = 128 });

// Salida MP4
var mp4Sink = new MP4SinkBlock("output_watermarked.mp4");

// Agregar + cablear
pipeline.AddBlock(rtspSource);
pipeline.AddBlock(logoOverlay);
pipeline.AddBlock(textOverlay);
pipeline.AddBlock(h264Encoder);
pipeline.AddBlock(aacEncoder);
pipeline.AddBlock(mp4Sink);

// Cadena de video: RTSP → logo → texto → H.264 → MP4
pipeline.Connect(rtspSource.VideoOutput, logoOverlay.Input);
pipeline.Connect(logoOverlay.Output, textOverlay.Input);
pipeline.Connect(textOverlay.Output, h264Encoder.Input);
pipeline.Connect(h264Encoder.Output, mp4Sink.CreateNewInput(MediaBlockPadMediaType.Video));

// Cadena de audio
pipeline.Connect(rtspSource.AudioOutput, aacEncoder.Input);
pipeline.Connect(aacEncoder.Output, mp4Sink.CreateNewInput(MediaBlockPadMediaType.Audio));

// Iniciar
await pipeline.StartAsync();

Consideraciones de Rendimiento

  1. Uso de CPU: El procesamiento de video consume CPU. Cada efecto añade sobrecarga:
  2. Redimensionamiento simple: ~10-20% CPU por stream
  3. Corrección de color: ~5-15% CPU
  4. Detección de rostros: ~30-50% CPU (depende de la resolución)
  5. Múltiples efectos: uso aditivo de CPU

  6. Aceleración por GPU: Use codificadores acelerados por hardware cuando estén disponibles. H264EncoderBlock.GetDefaultSettings() ya prefiere NVENC / QSV / AMF cuando la plataforma los soporta, pero puede forzar un backend específico:

// Codificador H.264 NVIDIA NVENC (Bitrate en Kbit/s — 4000 = 4 Mbps)
var nvencSettings = new NVENCH264EncoderSettings { Bitrate = 4000 };
var h264Encoder   = new H264EncoderBlock(nvencSettings);
  1. Manejo de errores: Suscríbase a OnError para conocer los fallos del pipeline:
pipeline.OnError += (sender, e) =>
{
    Console.WriteLine($"Error del pipeline: {e.Message}");
};
  1. Balance de ajustes de codificación:
  2. Calidad: mayor bitrate, preset más lento = mejor calidad, más CPU
  3. Rendimiento: menor bitrate, preset más rápido = menor calidad, menos CPU
  4. Tamaño del archivo: el bitrate afecta directamente al tamaño

Mejores Prácticas

  1. Pruebe el rendimiento primero:
  2. Comience con un pipeline simple
  3. Añada efectos uno a la vez
  4. Monitoree uso de CPU/memoria
  5. Ajuste según el hardware

  6. Elija bitrates apropiados (todos los valores en Kbit/s):

  7. 720p: 1000-2000
  8. 1080p: 2000-4000
  9. 4K: 8000-15000

  10. Libere recursos:

try
{
    await pipeline.StartAsync();
    // ... grabación ...
}
finally
{
    await pipeline.StopAsync();
    await pipeline.DisposeAsync();
    onvifClient?.Dispose();
}
  1. Observe los eventos de ciclo de vida del pipeline:
pipeline.OnStart  += (s, e) => Console.WriteLine("Pipeline iniciado");
pipeline.OnStop   += (s, e) => Console.WriteLine("Pipeline detenido");
pipeline.OnPause  += (s, e) => Console.WriteLine("Pipeline pausado");
pipeline.OnResume += (s, e) => Console.WriteLine("Pipeline reanudado");

Solución de Problemas

Alto uso de CPU: - Reducir la resolución de video - Preset de codificación más rápido - Eliminar efectos innecesarios - Usar aceleración por GPU si está disponible

Cuadros perdidos: - Verificar si la CPU está saturada - Reducir la tasa de cuadros - Reducir el bitrate - Simplificar el pipeline de procesamiento

Mala calidad de video: - Aumentar bitrate - Usar preset de codificación más lento - Verificar la calidad del video fuente - Verificar el ancho de banda de red para RTSP

Fugas de memoria: - Asegurar la liberación adecuada de bloques - Verificar referencias circulares - Monitorear grabaciones largas

Los efectos no se aplican: - Verificar las conexiones de bloques con pipeline.Connect(outPad, inPad) - Comprobar que los parámetros del efecto son válidos - Asegurar que cada bloque se registra vía pipeline.AddBlock(...) antes de StartAsync - Revisar el orden del pipeline (cadena de efectos)


Para grabación más simple sin postprocesamiento, consulte Guardar Stream RTSP sin Re-codificar. Visite nuestra página de GitHub para ejemplos completos.