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 { /// /// Interaction logic for GstVideoView.xaml /// 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}"); } } } }