diff --git a/HighPrecisionTimer/HighPrecisionTimer.csproj b/HighPrecisionTimer/HighPrecisionTimer.csproj index 2fc9fb0..338e500 100644 --- a/HighPrecisionTimer/HighPrecisionTimer.csproj +++ b/HighPrecisionTimer/HighPrecisionTimer.csproj @@ -9,7 +9,7 @@ Properties HighPrecisionTimer HighPrecisionTimer - v4.6.2 + v4.7.2 512 @@ -58,4 +58,4 @@ --> - \ No newline at end of file + diff --git a/Popcron.Sheets/Popcron.Sheets.csproj b/Popcron.Sheets/Popcron.Sheets.csproj index 469efc7..0ce1cea 100644 --- a/Popcron.Sheets/Popcron.Sheets.csproj +++ b/Popcron.Sheets/Popcron.Sheets.csproj @@ -9,7 +9,7 @@ Properties Popcron.Sheets Popcron.Sheets - v4.6.2 + v4.7.2 512 true @@ -216,4 +216,4 @@ - \ No newline at end of file + diff --git a/README.md b/README.md index d800333..7a1e841 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,15 @@ vMix Universal Title Controller (UTC) is program, which work with vMix through official vMix API. It can replace some paid or expensive software for making titles/scoreboards/lower thirds (by using pure vMix XAML/GT titles) and add ability of base scripting. + +--- + +## Plugins disponíveis + +### SRT Monitor Data Provider + +Monitore streams SRT diretamente dentro do vMixUTC, em modo Listener ou Caller, com preview de vídeo embutido. + +**[⬇ Baixar SrtMonitorDataProvider.zip](https://github.com/rafalau/vMixUTC/releases/download/v1.1.0-srt/SrtMonitorDataProvider.zip)** (~86 MB — já inclui VLC) — *v1.1: Enable Auto Sync* + +Instruções de instalação: [vMixUTCSRTDataProvider/README.md](vMixUTCSRTDataProvider/README.md) diff --git a/vMixAPI/State.cs b/vMixAPI/State.cs index 2e0e95f..eab0500 100644 --- a/vMixAPI/State.cs +++ b/vMixAPI/State.cs @@ -363,7 +363,7 @@ public bool Update() } } - public void UpdateAsync() + public void UpdateAsync(bool ignoreCache = false) { SendFunction("", true, x => { @@ -453,7 +453,7 @@ public void UpdateAsync() else Dispatcher.BeginInvoke(updateAction); - }); + }, ignoreCache: ignoreCache); } // Hold callback state strongly until async response is received and callback is dispatched. diff --git a/vMixController/Classes/MainWindowSettings.cs b/vMixController/Classes/MainWindowSettings.cs index 0a891df..b8cd880 100644 --- a/vMixController/Classes/MainWindowSettings.cs +++ b/vMixController/Classes/MainWindowSettings.cs @@ -772,6 +772,19 @@ public void UpdatePages() RaisePropertyChanged(PagesPropertyName); } + private bool _autoSync = false; + + public bool AutoSync + { + get { return _autoSync; } + set + { + if (_autoSync == value) return; + _autoSync = value; + RaisePropertyChanged(nameof(AutoSync)); + } + } + internal void AddRecentFile(string fileName) { if (RecentFiles == null) diff --git a/vMixController/Classes/VmixTcpSubscriber.cs b/vMixController/Classes/VmixTcpSubscriber.cs new file mode 100644 index 0000000..255c348 --- /dev/null +++ b/vMixController/Classes/VmixTcpSubscriber.cs @@ -0,0 +1,91 @@ +using System; +using System.IO; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace vMixController.Classes +{ + internal sealed class VmixTcpSubscriber : IDisposable + { + public event EventHandler ActsReceived; + + private CancellationTokenSource _cts; + private Task _loopTask; + private Timer _debounce; + private const int TcpPort = 8099; + private const int DebounceMs = 300; + + public void Start(string host) + { + Stop(); + _cts = new CancellationTokenSource(); + var token = _cts.Token; + _loopTask = Task.Run(() => LoopAsync(host, token)); + } + + public void Stop() + { + _cts?.Cancel(); + _loopTask = null; + _cts?.Dispose(); + _cts = null; + } + + private async Task LoopAsync(string host, CancellationToken ct) + { + while (!ct.IsCancellationRequested) + { + try + { + using (var client = new TcpClient()) + { + client.ReceiveTimeout = 2000; + await client.ConnectAsync(host, TcpPort).ConfigureAwait(false); + + using (var stream = client.GetStream()) + using (var writer = new StreamWriter(stream) { AutoFlush = true, NewLine = "\r\n" }) + using (var reader = new StreamReader(stream)) + { + await writer.WriteLineAsync("SUBSCRIBE ACTS").ConfigureAwait(false); + + while (!ct.IsCancellationRequested) + { + string line; + try + { + line = await reader.ReadLineAsync().ConfigureAwait(false); + } + catch (IOException) { break; } + + if (line == null) break; + + // "SUBSCRIBE OK ACTS" is the subscription confirmation — skip it + // actual events arrive as "ACTS OK ..." + if (line.StartsWith("ACTS ", StringComparison.Ordinal)) + FireDebounced(); + } + } + } + } + catch (OperationCanceledException) { break; } + catch { /* connection failed — retry after delay */ } + + try { await Task.Delay(3000, ct).ConfigureAwait(false); } + catch (OperationCanceledException) { break; } + } + } + + private void FireDebounced() + { + _debounce?.Dispose(); + _debounce = new Timer(_ => ActsReceived?.Invoke(this, EventArgs.Empty), null, DebounceMs, Timeout.Infinite); + } + + public void Dispose() + { + Stop(); + _debounce?.Dispose(); + } + } +} diff --git a/vMixController/Controls/vMixControlContainer.xaml b/vMixController/Controls/vMixControlContainer.xaml index 858f12f..0a8bf4d 100644 --- a/vMixController/Controls/vMixControlContainer.xaml +++ b/vMixController/Controls/vMixControlContainer.xaml @@ -249,6 +249,7 @@ + diff --git a/vMixController/MainWindow.xaml b/vMixController/MainWindow.xaml index c791e91..4eda31c 100644 --- a/vMixController/MainWindow.xaml +++ b/vMixController/MainWindow.xaml @@ -498,6 +498,14 @@ + + + + + + + + diff --git a/vMixController/Skins/ControlTemplates.xaml b/vMixController/Skins/ControlTemplates.xaml index e184734..6d87991 100644 --- a/vMixController/Skins/ControlTemplates.xaml +++ b/vMixController/Skins/ControlTemplates.xaml @@ -915,8 +915,8 @@ - - + + + + + + + + diff --git a/vMixUTCSRTDataProvider/OnWidgetUI.xaml.cs b/vMixUTCSRTDataProvider/OnWidgetUI.xaml.cs new file mode 100644 index 0000000..6f9d19c --- /dev/null +++ b/vMixUTCSRTDataProvider/OnWidgetUI.xaml.cs @@ -0,0 +1,207 @@ +using LibVLCSharp.Shared; +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Threading; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using VlcMediaPlayer = LibVLCSharp.Shared.MediaPlayer; + +namespace UTCSRTDataProvider +{ + public partial class OnWidgetUI : UserControl, IDisposable + { + private static int _nextId = 0; + private readonly int _id = Interlocked.Increment(ref _nextId); + + private VlcMediaPlayer _mediaPlayer; + private LibVLC _vlc; + private string _lastUrl; + private bool _isConnected; + private bool _reconnecting; + private bool _disposed; + + static OnWidgetUI() + { + Trace.Listeners.Remove("Default"); + } + + public OnWidgetUI() + { + InitializeComponent(); + + if (DesignerProperties.GetIsInDesignMode(this)) + return; + + VideoPlaceholder.Text = $"Aguardando... [#{_id}]"; + DataContextChanged += OnDataContextChanged; + } + + private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) + { + if (e.NewValue is SRTDataProvider neu) + AudioCheckBox.IsChecked = neu.IsAudioEnabled; + } + + private SRTDataProvider Provider => DataContext as SRTDataProvider; + + private void AudioCheckBox_Checked(object sender, RoutedEventArgs e) + { + if (Provider != null) Provider.IsAudioEnabled = true; + if (_isConnected) DoReconnect(); + } + + private void AudioCheckBox_Unchecked(object sender, RoutedEventArgs e) + { + if (Provider != null) Provider.IsAudioEnabled = false; + if (_isConnected) DoReconnect(); + } + + private Media BuildMedia() + { + var media = new Media(_vlc, _lastUrl, FromType.FromLocation); + media.AddOption(":network-caching=300"); + if (AudioCheckBox.IsChecked != true) + media.AddOption(":no-audio"); + return media; + } + + // Reconnect without explicit Stop — Play(newMedia) makes VLC transition internally. + // _reconnecting suppresses the intermediate Stopped event so video stays visible. + private void DoReconnect() + { + if (_mediaPlayer == null || _vlc == null || string.IsNullOrEmpty(_lastUrl)) return; + _reconnecting = true; + SetStatus("Reconectando...", null); + var media = BuildMedia(); + _mediaPlayer.Play(media); + media.Dispose(); + } + + private void ShowVideo() + { + VideoView.Visibility = Visibility.Visible; + VideoPlaceholder.Visibility = Visibility.Collapsed; + } + + private void HideVideo() + { + VideoView.Visibility = Visibility.Collapsed; + VideoPlaceholder.Visibility = Visibility.Visible; + } + + private void ConnectButton_Click(object sender, RoutedEventArgs e) + { + _vlc = Provider?.LibVLCInstance; + if (_vlc == null) + { + SetStatus(Provider?.VlcInitError ?? "VLC não inicializado", false); + return; + } + + _lastUrl = Provider?.BuildUrl(); + if (string.IsNullOrEmpty(_lastUrl)) + { + SetStatus(Provider?.Mode == SRTMode.Caller + ? "Preencha IP e Porta" + : "Preencha a Porta", false); + return; + } + + if (_mediaPlayer == null) + { + _mediaPlayer = new VlcMediaPlayer(_vlc); + _mediaPlayer.Playing += VlcMediaPlayer_Playing; + _mediaPlayer.EncounteredError += VlcMediaPlayer_Error; + _mediaPlayer.Stopped += VlcMediaPlayer_Stopped; + _mediaPlayer.EndReached += VlcMediaPlayer_Stopped; + } + + ShowVideo(); + VideoView.MediaPlayer = _mediaPlayer; + _mediaPlayer.Stop(); + SetStatus("Conectando...", null); + + var media = BuildMedia(); + _mediaPlayer.Play(media); + media.Dispose(); + } + + private void DisconnectButton_Click(object sender, RoutedEventArgs e) + { + _reconnecting = false; + _isConnected = false; + _mediaPlayer?.Stop(); + SetStatus("Desconectado", false); + HideVideo(); + } + + private void VlcMediaPlayer_Playing(object sender, EventArgs e) + { + Dispatcher.BeginInvoke(new Action(() => + { + _reconnecting = false; + _isConnected = true; + SetStatus("Conectado", true); + })); + } + + private void VlcMediaPlayer_Error(object sender, EventArgs e) + { + Dispatcher.BeginInvoke(new Action(() => + { + _reconnecting = false; + _isConnected = false; + SetStatus("Erro de conexão", false); + HideVideo(); + })); + } + + private void VlcMediaPlayer_Stopped(object sender, EventArgs e) + { + Dispatcher.BeginInvoke(new Action(() => + { + if (_reconnecting) return; + if (StatusText.Text == "Conectado") + { + _isConnected = false; + SetStatus("Desconectado", false); + HideVideo(); + } + })); + } + + private void SetStatus(string text, bool? connected) + { + StatusText.Text = text; + var color = connected == true + ? Colors.LimeGreen + : connected == false + ? Colors.OrangeRed + : Colors.Orange; + var brush = new SolidColorBrush(color); + StatusText.Foreground = brush; + StatusIndicator.Fill = brush; + } + + public void Dispose() + { + if (_disposed) return; + _disposed = true; + + DataContextChanged -= OnDataContextChanged; + + if (_mediaPlayer != null) + { + _mediaPlayer.Playing -= VlcMediaPlayer_Playing; + _mediaPlayer.EncounteredError -= VlcMediaPlayer_Error; + _mediaPlayer.Stopped -= VlcMediaPlayer_Stopped; + _mediaPlayer.EndReached -= VlcMediaPlayer_Stopped; + _mediaPlayer.Stop(); + _mediaPlayer.Dispose(); + _mediaPlayer = null; + } + } + } +} diff --git a/vMixUTCSRTDataProvider/Properties/AssemblyInfo.cs b/vMixUTCSRTDataProvider/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b35c68a --- /dev/null +++ b/vMixUTCSRTDataProvider/Properties/AssemblyInfo.cs @@ -0,0 +1,17 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("vMixUTCSRTDataProvider")] +[assembly: AssemblyDescription("SRT Monitor")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("vMixUTCSRTDataProvider")] +[assembly: AssemblyCopyright("Copyright © 2025")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] +[assembly: Guid("f8a24b3c-e1d2-4f56-a78b-c90d12ef5678")] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/vMixUTCSRTDataProvider/README.md b/vMixUTCSRTDataProvider/README.md new file mode 100644 index 0000000..79cee48 --- /dev/null +++ b/vMixUTCSRTDataProvider/README.md @@ -0,0 +1,61 @@ +# SRT Monitor — Data Provider para vMixUTC + +Widget de monitoramento de streams SRT integrado ao [vMixUTC](https://github.com/elgarf/vMixUTC), desenvolvido como um **Data Provider** externo. + +--- + +## Download e Instalação + +**[⬇ Baixar SrtMonitorDataProvider.zip](https://github.com/rafalau/vMixUTC/releases/download/v1.1.0-srt/SrtMonitorDataProvider.zip)** (~86 MB — já inclui VLC) + +1. Extraia o ZIP +2. Copie a pasta `DataProviders` para dentro da pasta de instalação do **vMixUTC**, mesclando com a pasta existente +3. Reinicie o vMixUTC — o widget **SRT Monitor** aparecerá na lista de Data Providers + +--- + +## O que é + +O **SRT Monitor** permite receber e visualizar streams de vídeo via protocolo **SRT (Secure Reliable Transport)** diretamente dentro do vMixUTC, sem precisar abrir um player externo. Ideal para monitorar sinais de entrada antes de colocá-los no ar no vMix. + +--- + +## Funcionalidades + +| Recurso | Descrição | +|---|---| +| Modo Listener | Aguarda conexão de entrada na porta configurada | +| Modo Caller | Conecta ativamente em um host remoto (IP + Porta) | +| Preview de vídeo | Janela de preview embutida no widget com redimensionamento vertical | +| Controle de áudio | Checkbox por instância — isola completamente o áudio de cada widget | +| IP padrão automático | Pré-preenche o IP do host vMix já configurado no UTC | +| Configurações avançadas | Latência, AES passphrase, key length (128/192/256 bits), Stream ID | +| Ícones ▶ / ■ | Botões conectar/desconectar com ícones Font Awesome | +| Múltiplas instâncias | Cada widget opera de forma completamente independente | + +--- + +## Enable Auto Sync *(v1.1)* + +O vMixUTC não atualiza automaticamente quando algo muda no vMix — é necessário clicar em **Sync** manualmente. A partir da v1.1, o menu do UTC conta com o toggle **Enable Auto Sync**. + +Quando ativado, o UTC consulta o vMix a cada segundo e detecta qualquer alteração — inputs adicionados, textos modificados, configurações trocadas — atualizando a interface automaticamente. + +--- + +## Tecnologias utilizadas + +| Biblioteca | Versão | Finalidade | +|---|---|---| +| [LibVLCSharp](https://github.com/videolan/libvlcsharp) | 3.8.5 | Wrapper .NET para libvlc | +| [LibVLCSharp.WPF](https://github.com/videolan/libvlcsharp) | 3.8.5 | Controle WPF para preview de vídeo | +| libvlc (VLC) | 3.x | Engine de reprodução / SRT decoder | +| .NET Framework | 4.7.2 | Target framework do vMixUTC | +| Costura.Fody | — | Embute as DLLs gerenciadas no assembly final | + +--- + +## Créditos + +Idealizado por **[rafalau](https://github.com/rafalau)**. +Implementado com assistência de **Claude Sonnet 4.6** (Anthropic). diff --git a/vMixUTCSRTDataProvider/SRTDataProvider.cs b/vMixUTCSRTDataProvider/SRTDataProvider.cs new file mode 100644 index 0000000..9437e13 --- /dev/null +++ b/vMixUTCSRTDataProvider/SRTDataProvider.cs @@ -0,0 +1,231 @@ +using LibVLCSharp.Shared; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Windows; +using vMixAPI; +using vMixControllerDataProvider; + +namespace UTCSRTDataProvider +{ + public enum SRTMode + { + Listener = 0, + Caller = 1 + } + + [Description("SRT Monitor")] + public class SRTDataProvider : IvMixDataProvider, IDisposable, INotifyPropertyChanged + { + // Core.Initialize only needs to run once; LibVLC instances are per-provider + // so each SRT Monitor has fully isolated audio state. + private static bool _coreInitialized; + private static readonly object _initLock = new object(); + private static string _vlcDir; + + internal LibVLC LibVLCInstance { get; private set; } + internal string VlcInitError { get; private set; } + + private static string GetDefaultIp() + { + try + { + var url = StateFabrique.GetUrl(); + var uri = new Uri(url); + return uri.Host; + } + catch + { + return "127.0.0.1"; + } + } + + internal void EnsureVlcInitialized() + { + if (LibVLCInstance != null) return; + lock (_initLock) + { + if (!_coreInitialized) + { + try + { + var dir = Path.GetDirectoryName(typeof(SRTDataProvider).Assembly.Location); + var arch = Environment.Is64BitProcess ? "win-x64" : "win-x86"; + _vlcDir = Path.Combine(dir, "libvlc", arch); + Core.Initialize(_vlcDir); + _coreInitialized = true; + } + catch (Exception ex) + { + VlcInitError = $"Core.Initialize falhou em '{_vlcDir}': {ex.GetType().Name}: {ex.Message}"; + return; + } + } + } + try + { + LibVLCInstance = new LibVLC(enableDebugLogs: false); + } + catch (Exception ex) + { + VlcInitError = $"new LibVLC() falhou: {ex.GetType().Name}: {ex.Message}"; + } + } + + private OnWidgetUI _ui; + private SRTMode _mode = SRTMode.Listener; + private string _ip = GetDefaultIp(); + private int _port = 4000; + private int _latency = 200; + private int _keyLength = 32; + private string _passphrase = ""; + private string _streamId = ""; + private bool _isAudioEnabled = true; + + public SRTMode Mode + { + get => _mode; + set + { + if (_mode == value) return; + _mode = value; + Notify(nameof(Mode)); + Notify(nameof(IsCallerMode)); + Notify(nameof(ModeIndex)); + } + } + + public bool IsCallerMode => Mode == SRTMode.Caller; + + public int ModeIndex + { + get => (int)Mode; + set { Mode = (SRTMode)value; } + } + + public string IP + { + get => _ip; + set { if (_ip == value) return; _ip = value; Notify(nameof(IP)); } + } + + public int Port + { + get => _port; + set { if (_port == value) return; _port = value; Notify(nameof(Port)); } + } + + public int Latency + { + get => _latency; + set { if (_latency == value) return; _latency = value; Notify(nameof(Latency)); } + } + + public int KeyLength + { + get => _keyLength; + set + { + if (_keyLength == value) return; + _keyLength = value; + Notify(nameof(KeyLength)); + Notify(nameof(KeyLengthIndex)); + } + } + + public int KeyLengthIndex + { + get => _keyLength == 16 ? 0 : _keyLength == 24 ? 1 : 2; + set { KeyLength = value == 0 ? 16 : value == 1 ? 24 : 32; } + } + + public string Passphrase + { + get => _passphrase; + set { if (_passphrase == value) return; _passphrase = value; Notify(nameof(Passphrase)); } + } + + public string StreamId + { + get => _streamId; + set { if (_streamId == value) return; _streamId = value; Notify(nameof(StreamId)); } + } + + public bool IsAudioEnabled + { + get => _isAudioEnabled; + set { if (_isAudioEnabled == value) return; _isAudioEnabled = value; Notify(nameof(IsAudioEnabled)); } + } + + public event PropertyChangedEventHandler PropertyChanged; + private void Notify(string n) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(n)); + + public SRTDataProvider() + { + EnsureVlcInitialized(); + _ui = new OnWidgetUI { DataContext = this }; + } + + internal string BuildUrl() + { + if (_port <= 0 || _port > 65535) return null; + if (Mode == SRTMode.Caller && string.IsNullOrWhiteSpace(_ip)) return null; + + var sb = new System.Text.StringBuilder("srt://"); + + if (Mode == SRTMode.Caller) + sb.Append(_ip.Trim()); + + sb.Append($":{_port}"); + sb.Append($"?mode={Mode.ToString().ToLower()}"); + sb.Append($"&latency={_latency}"); + + if (!string.IsNullOrWhiteSpace(_passphrase)) + { + sb.Append($"&passphrase={Uri.EscapeDataString(_passphrase.Trim())}"); + sb.Append($"&pbkeylen={_keyLength}"); + } + + if (!string.IsNullOrWhiteSpace(_streamId)) + sb.Append($"&streamid={Uri.EscapeDataString(_streamId.Trim())}"); + + return sb.ToString(); + } + + public int Period { get; set; } + public bool IsProvidingCustomProperties => false; + public UIElement CustomUI => _ui; + public string[] Values => new string[0]; + public void ShowProperties(Window owner) { } + + public List GetProperties() => new List + { + (int)Mode, IP, Port, Latency, KeyLength, Passphrase, StreamId, IsAudioEnabled + }; + + public void SetProperties(List props) + { + if (props == null || props.Count == 0) return; + try + { + if (props.Count > 0 && props[0] != null) Mode = (SRTMode)Convert.ToInt32(props[0]); + if (props.Count > 1) IP = props[1] as string ?? ""; + if (props.Count > 2 && props[2] != null) Port = Convert.ToInt32(props[2]); + if (props.Count > 3 && props[3] != null) Latency = Convert.ToInt32(props[3]); + if (props.Count > 4 && props[4] != null) KeyLength = Convert.ToInt32(props[4]); + if (props.Count > 5) Passphrase = props[5] as string ?? ""; + if (props.Count > 6) StreamId = props[6] as string ?? ""; + if (props.Count > 7 && props[7] != null) IsAudioEnabled = Convert.ToBoolean(props[7]); + } + catch { } + } + + public void Dispose() + { + (_ui as IDisposable)?.Dispose(); + LibVLCInstance?.Dispose(); + LibVLCInstance = null; + } + } +} diff --git a/vMixUTCSRTDataProvider/app.config b/vMixUTCSRTDataProvider/app.config new file mode 100644 index 0000000..561f337 --- /dev/null +++ b/vMixUTCSRTDataProvider/app.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/vMixUTCSRTDataProvider/packages.config b/vMixUTCSRTDataProvider/packages.config new file mode 100644 index 0000000..43f6271 --- /dev/null +++ b/vMixUTCSRTDataProvider/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/vMixUTCSRTDataProvider/vMixUTCSRTDataProvider.csproj b/vMixUTCSRTDataProvider/vMixUTCSRTDataProvider.csproj new file mode 100644 index 0000000..b9212a5 --- /dev/null +++ b/vMixUTCSRTDataProvider/vMixUTCSRTDataProvider.csproj @@ -0,0 +1,155 @@ + + + + + + Debug + AnyCPU + {F8A24B3C-E1D2-4F56-A78B-C90D12EF5678} + Library + Properties + UTCSRTDataProvider + SrtMonitorDataProvider + v4.7.2 + 512 + + + + + + true + full + false + ..\vMixController\bin\Debug\DataProviders\ + DEBUG;TRACE + prompt + 4 + AnyCPU + + + pdbonly + true + ..\vMixController\bin\Release\DataProviders\ + TRACE + prompt + 4 + AnyCPU + + + + + ..\packages\LibVLCSharp.3.8.5\lib\net471\LibVLCSharp.dll + + + ..\packages\LibVLCSharp.WPF.3.8.5\lib\net461\LibVLCSharp.WPF.dll + + + + ..\packages\MvvmLightLibs.5.4.1.1\lib\net45\GalaSoft.MvvmLight.dll + + + ..\packages\MvvmLightLibs.5.4.1.1\lib\net45\GalaSoft.MvvmLight.Extras.dll + + + ..\packages\MvvmLightLibs.5.4.1.1\lib\net45\GalaSoft.MvvmLight.Platform.dll + + + ..\packages\Costura.Fody.6.0.0\lib\netstandard2.0\Costura.dll + + + + ..\packages\Extended.Wpf.Toolkit.5.0.0\lib\net40\Xceed.Wpf.Toolkit.dll + False + + + + + + + + + + + + + ..\packages\MvvmLightLibs.5.4.1.1\lib\net45\System.Windows.Interactivity.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll + + + + + + + OnWidgetUI.xaml + + + + + Designer + MSBuild:Compile + + + + + {41B25F7D-B035-401B-A50A-C5BB3983C2AC} + vMixAPI + False + + + {21c1a5ec-9fd2-44f5-bdac-98f0787102c3} + vMixControllerDataProvider + + + {90bfeee2-82e3-47d2-9b56-47ddb4c611f2} + vMixControllerSkin + False + + + + + + + + + + + + + + + + + + +cd $(OutDir) +if not exist vlc mkdir vlc +if exist libvlc.dll move /y libvlc.dll vlc\libvlc.dll +if exist libvlccore.dll move /y libvlccore.dll vlc\libvlccore.dll +if exist plugins xcopy /e /q /y plugins\ vlc\plugins\ +if exist plugins rmdir /s /q plugins +del vMixControllerDataProvider.dll /q 2>nul +for /f %%F in ('dir /b /a-d ^| findstr /vile "DataProvider.dll"') do del "%%F" /Q /S + + + + + Pacote NuGet ausente: {0}. Use "Restaurar Pacotes NuGet" no Visual Studio. + + + + +