agrobot_base/AgroBase/OperationControl/Controls/GstVideoView.xaml.cs

227 lines
6.6 KiB
C#

using System.Windows;
using Gst;
using GstVideo;
using Unosquare.FFME;
using Task = System.Threading.Tasks.Task;
using UserControl = System.Windows.Controls.UserControl;
namespace OperationControl.Controls
{
/// <summary>
/// Interaction logic for GstVideoView.xaml
/// </summary>
public partial class GstVideoView : UserControl
{
private Pipeline? _pipeline;
private Element? _videoSink;
private Bus? _bus;
#region DP: Host e Port
public string Host
{
get => (string)GetValue(HostProperty);
set => SetValue(HostProperty, value);
}
public static readonly DependencyProperty HostProperty =
DependencyProperty.Register(
nameof(Host),
typeof(string),
typeof(GstVideoView),
new PropertyMetadata("0.0.0.0", OnEndpointChanged));
public int Port
{
get => (int)GetValue(PortProperty);
set => SetValue(PortProperty, value);
}
public static readonly DependencyProperty PortProperty =
DependencyProperty.Register(
nameof(Port),
typeof(int),
typeof(GstVideoView),
new PropertyMetadata(5000, OnEndpointChanged));
private static void OnEndpointChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var view = (GstVideoView)d;
// Se já estiver carregado, reinicia o pipeline com IP/porta novos
if (view.IsLoaded)
{
//view.RestartPipeline();
}
}
#endregion
public string SourceUrl { get; set; } = "udp://0.0.0.0:5000";
public GstVideoView()
{
InitializeComponent();
InitGStreamer();
Loaded += (_, _) =>
{
//if (VideoPanel.IsHandleCreated == false)
// VideoPanel.CreateControl();
};
Unloaded += (_, _) => Stop();
}
private void InitGStreamer()
{
//Gst.Application.Init();
}
public void StartPipeline()
{
if (_pipeline != null)
return;
// pipeline simples só pra testar: videotestsrc → videoconvert → autovideosink
_pipeline = new Pipeline();
var src = ElementFactory.Make("videotestsrc", "src");
var conv = ElementFactory.Make("videoconvert", "conv");
var sink = ElementFactory.Make("autovideosink", "sink");
if (src is null || conv is null || sink is null)
throw new Exception("Falha ao criar algum elemento do pipeline.");
_pipeline.Add(src);
_pipeline.Add(conv);
_pipeline.Add(sink);
if (!src.Link(conv) || !conv.Link(sink))
throw new Exception("Falha ao linkar o pipeline.");
// >>> AQUI entra a correção do Bus (ver seção 2) <<<
_pipeline.SetState(State.Playing);
}
public void Start2()
{
if (_pipeline != null)
return;
//if (!VideoPanel.IsHandleCreated)
// VideoPanel.CreateControl();
// Monta o pipeline de recepção (ajuste caps se precisar)
string pipeDesc =
$"udpsrc port={Port} " +
"caps = application/x-rtp, media=video, encoding-name=H264, payload=96 " +
"! rtph264depay ! h264parse ! avdec_h264 ! videoconvert " +
"! d3d11videosink name=vsink sync=false";
// Cria pipeline a partir da string
_pipeline = new Pipeline(new Gst.Internal.PipelineHandle(1, false));
if (_pipeline is null)
throw new InvalidOperationException("Falha ao criar pipeline GStreamer.");
// Pega o sink pelo nome
_videoSink = _pipeline.GetChildByName("vsink") as Element;
if (_videoSink is null)
throw new InvalidOperationException("Não foi possível encontrar o elemento 'vsink' no pipeline.");
// Faz o cast para VideoOverlay
var overlay = _videoSink as VideoOverlay;
if (overlay is null)
throw new InvalidOperationException("O sink não implementa VideoOverlay.");
// Passa o handle da janela do Panel para o sink
//var hwnd = VideoPanel.Handle;
//overlay.SetWindowHandle((nuint)hwnd);
// (Opcional) configurar bus para logar erros
_bus = _pipeline.GetBus();
_bus.AddSignalWatch();
_bus.OnMessage += OnBusMessage;
// Play!
_pipeline.SetState(State.Playing);
}
public void Stop2()
{
if (_pipeline == null)
return;
_pipeline.SetState(State.Null);
if (_bus != null)
{
_bus.OnMessage -= OnBusMessage;
_bus.Dispose();
_bus = null;
}
_pipeline.Dispose();
_pipeline = null;
_videoSink = null;
}
private void OnBusMessage(object? sender, Bus.MessageSignalArgs e)
{
switch (e.Message.Type)
{
case MessageType.Error:
//e.Message.ParseError(out var err, out var debug);
//Console.WriteLine($"[GST ERROR] {err}: {debug}");
Console.WriteLine($"[GST ERROR] {e.Message}");
break;
case MessageType.Eos:
Console.WriteLine("[GST] End of stream");
// Se quiser, reinicia ou dá Stop();
break;
}
}
public async Task Start()
{
try
{
if (Media.IsOpen)
return;
// Abre o stream (udp, rtsp, http…)
await Media.Open(new System.Uri(SourceUrl));
await Media.Play();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[FFME] Erro ao iniciar vídeo: {ex}");
}
}
public async void Stop()
{
try
{
if (Media.IsOpen)
{
await Media.Stop();
await Media.Close();
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[FFME] Erro ao parar vídeo: {ex}");
}
}
}
}