diff --git a/MainForm.Designer.cs b/MainForm.Designer.cs index 7667a29..c02f766 100644 --- a/MainForm.Designer.cs +++ b/MainForm.Designer.cs @@ -35,13 +35,12 @@ toolStripStatusDisco = new ToolStripStatusLabel(); menuStrip1 = new MenuStrip(); discoveryToolStripMenuItem = new ToolStripMenuItem(); - startToolStripMenuItem = new ToolStripMenuItem(); - stopToolStripMenuItem = new ToolStripMenuItem(); - forceRediscoverToolStripMenuItem = new ToolStripMenuItem(); + manualAddToolStripMenuItem = new ToolStripMenuItem(); toolStripSeparator1 = new ToolStripSeparator(); saveToolStripMenuItem = new ToolStripMenuItem(); loadToolStripMenuItem = new ToolStripMenuItem(); - manualAddToolStripMenuItem = new ToolStripMenuItem(); + toolStripSeparator2 = new ToolStripSeparator(); + discoverToolStripMenuItem = new ToolStripMenuItem(); statusStripMain.SuspendLayout(); menuStrip1.SuspendLayout(); SuspendLayout(); @@ -75,7 +74,7 @@ // toolStripStatusDisco.Name = "toolStripStatusDisco"; toolStripStatusDisco.Size = new Size(111, 17); - toolStripStatusDisco.Text = "Discovery : Stopped"; + toolStripStatusDisco.Text = ""; // // menuStrip1 // @@ -88,32 +87,17 @@ // // discoveryToolStripMenuItem // - discoveryToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { startToolStripMenuItem, stopToolStripMenuItem, manualAddToolStripMenuItem, forceRediscoverToolStripMenuItem, toolStripSeparator1, saveToolStripMenuItem, loadToolStripMenuItem }); + discoveryToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { discoverToolStripMenuItem, toolStripSeparator2, manualAddToolStripMenuItem, toolStripSeparator1, saveToolStripMenuItem, loadToolStripMenuItem }); discoveryToolStripMenuItem.Name = "discoveryToolStripMenuItem"; discoveryToolStripMenuItem.Size = new Size(70, 20); discoveryToolStripMenuItem.Text = "Discovery"; // - // startToolStripMenuItem + // manualAddToolStripMenuItem // - startToolStripMenuItem.Name = "startToolStripMenuItem"; - startToolStripMenuItem.Size = new Size(180, 22); - startToolStripMenuItem.Text = "Start"; - startToolStripMenuItem.Click += startToolStripMenuItem_Click; - // - // stopToolStripMenuItem - // - stopToolStripMenuItem.Enabled = false; - stopToolStripMenuItem.Name = "stopToolStripMenuItem"; - stopToolStripMenuItem.Size = new Size(180, 22); - stopToolStripMenuItem.Text = "Stop"; - stopToolStripMenuItem.Click += stopToolStripMenuItem_Click; - // - // forceRediscoverToolStripMenuItem - // - forceRediscoverToolStripMenuItem.Name = "forceRediscoverToolStripMenuItem"; - forceRediscoverToolStripMenuItem.Size = new Size(180, 22); - forceRediscoverToolStripMenuItem.Text = "Force re-discover"; - forceRediscoverToolStripMenuItem.Click += forceReloadToolStripMenuItem_Click; + manualAddToolStripMenuItem.Name = "manualAddToolStripMenuItem"; + manualAddToolStripMenuItem.Size = new Size(180, 22); + manualAddToolStripMenuItem.Text = "Add manually"; + manualAddToolStripMenuItem.Click += manualAddToolStripMenuItem_Click; // // toolStripSeparator1 // @@ -134,12 +118,16 @@ loadToolStripMenuItem.Text = "Load"; loadToolStripMenuItem.Click += loadToolStripMenuItem_Click; // - // manualAddToolStripMenuItem + // toolStripSeparator2 // - manualAddToolStripMenuItem.Name = "manualAddToolStripMenuItem"; - manualAddToolStripMenuItem.Size = new Size(180, 22); - manualAddToolStripMenuItem.Text = "Add manually"; - manualAddToolStripMenuItem.Click += manualAddToolStripMenuItem_Click; + toolStripSeparator2.Name = "toolStripSeparator2"; + toolStripSeparator2.Size = new Size(177, 6); + // + // discoverToolStripMenuItem + // + discoverToolStripMenuItem.Name = "discoverToolStripMenuItem"; + discoverToolStripMenuItem.Size = new Size(180, 22); + discoverToolStripMenuItem.Text = "Discover cameras"; // // MainForm // @@ -153,7 +141,6 @@ MainMenuStrip = menuStrip1; Name = "MainForm"; Text = "RoboSpot MotionCamera finder"; - FormClosing += MainForm_FormClosing; statusStripMain.ResumeLayout(false); statusStripMain.PerformLayout(); menuStrip1.ResumeLayout(false); @@ -169,13 +156,12 @@ private ToolStripStatusLabel toolStripStatusLabel; private MenuStrip menuStrip1; private ToolStripMenuItem discoveryToolStripMenuItem; - private ToolStripMenuItem startToolStripMenuItem; - private ToolStripMenuItem stopToolStripMenuItem; - private ToolStripMenuItem forceRediscoverToolStripMenuItem; private ToolStripMenuItem saveToolStripMenuItem; private ToolStripMenuItem loadToolStripMenuItem; private ToolStripSeparator toolStripSeparator1; private ToolStripStatusLabel toolStripStatusDisco; private ToolStripMenuItem manualAddToolStripMenuItem; + private ToolStripMenuItem discoverToolStripMenuItem; + private ToolStripSeparator toolStripSeparator2; } } \ No newline at end of file diff --git a/MainForm.cs b/MainForm.cs index bce0db0..d00d46a 100644 --- a/MainForm.cs +++ b/MainForm.cs @@ -1,27 +1,34 @@ using System.ComponentModel; using System.Diagnostics; using System.Net; +using System.Net.Sockets; using System.Net.NetworkInformation; using System.Text.Json; using System.Text.Json.Serialization; using System.Windows.Forms; -using Tmds.MDns; using static robospot_camera_finder.MainForm; +using System.Runtime.InteropServices; +using System.Text; namespace robospot_camera_finder { public partial class MainForm : Form { - private readonly ServiceBrowser browser = new(); - private BindingList all_cameras = new(); public static string cam_username = "admin"; public static string cam_password = "RoboSpot10"; - private static string browse_scope = "_rtsp._tcp"; + // UDP discovery constants + private const int SEND_PORT = 7701; + private const int RECEIVE_PORT = 7711; + private const string BROADCAST_ADDRESS = "10.255.255.255"; + private const byte DEF_REQ_SCAN = 1; + private const byte RES_REQ_SCAN = 11; - bool is_browsing = false; + private UdpClient sendClient; + private UdpClient receiveClient; + private bool isDiscovering = false; public class Camera { @@ -33,8 +40,6 @@ namespace robospot_camera_finder camera_serial = serial; } - public Camera() { } - public string camera_name { get; set; } public string camera_ip { get; set; } @@ -44,6 +49,51 @@ namespace robospot_camera_finder public string camera_serial { get; set; } } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct DataPacketIPv4 + { + public byte mode; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 18)] + public byte[] packet_id; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 18)] + public byte[] mac_addr; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public byte[] ip_addr; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public byte[] subnetmask; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public byte[] gateway; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public byte[] password; + + public byte reserved1; + public ushort port; + public byte status; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public byte[] device_name; + + public byte reserved2; + public ushort http_port; + public ushort device_port; + public ushort tcp_port; + public ushort udp_port; + public ushort upload_port; + public ushort multicast_port; + public byte network_mode; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public byte[] ddns_url; + + public byte reserved3; + } + public MainForm() { InitializeComponent(); @@ -66,6 +116,7 @@ namespace robospot_camera_finder if (!got_proper_ip) { DialogResult open_netsettings = MessageBox.Show("No Ethernet interface on the 10.0.0.0/8 subnet found. Cameras might not be detected. Do you want to open network settings ?", "Wrong IP configuration", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1); + if (open_netsettings == DialogResult.Yes) { ProcessStartInfo startInfo = new ProcessStartInfo("NCPA.cpl"); @@ -73,87 +124,32 @@ namespace robospot_camera_finder Process.Start(startInfo); } + } lbMain.DataSource = all_cameras; lbMain.DisplayMember = "camera_name"; lbMain.ValueMember = "camera_ip"; - browser.ServiceAdded += (sender, args) => - { - ServiceAdded(sender, args); - }; + // Initialize UDP clients + InitializeUdpClients(); - browser.ServiceChanged += (sender, args) => - { - ServiceAdded(sender, args); - }; - - browser.ServiceRemoved += (sender, args) => - { - if (browser.IsBrowsing) - { - ServiceRemoved(sender, args); - } - }; - - browse(true); + // Discover cameras + discover_cameras(); } - private void browse(bool browse_needed) + private void InitializeUdpClients() { - if (browse_needed) + try { - if (!browser.IsBrowsing) - { - browser.StartBrowse(browse_scope); - is_browsing = true; - toolStripStatusDisco.Text = "Discovery : Started"; - } + sendClient = new UdpClient(); + sendClient.EnableBroadcast = true; + + receiveClient = new UdpClient(RECEIVE_PORT); } - else + catch (Exception ex) { - if (browser.IsBrowsing) - { - browser.StopBrowse(); - is_browsing = false; - toolStripStatusDisco.Text = "Discovery : Stopped"; - } - } - - startToolStripMenuItem.Enabled = !browser.IsBrowsing; - stopToolStripMenuItem.Enabled = browser.IsBrowsing; - } - - private void ServiceAdded(object sender, ServiceAnnouncementEventArgs args) - { - foreach (var cam_addr in args.Announcement.Addresses) - { - if (cam_addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) - { - HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://" + cam_addr + "/stw-cgi/system.cgi?msubmenu=deviceinfo&action=view"); - request.Credentials = new NetworkCredential(MainForm.cam_username, MainForm.cam_password); - HttpWebResponse response = (HttpWebResponse)request.GetResponse(); - string text; - using (var sr = new StreamReader(response.GetResponseStream())) { text = sr.ReadToEnd(); } - string[] resp_lines = text.Split('\n'); - string location_line = Array.Find(resp_lines, line => line.Trim().StartsWith("DeviceLocation=")); - string serial_line = Array.Find(resp_lines, line => line.Trim().StartsWith("SerialNumber=")); - string location = ""; - string serial = ""; - if (location_line != null) - { - location = location_line.Substring("DeviceLocation=".Length).Trim(); - } - if (serial_line != null) - { - serial = serial_line.Substring("SerialNumber=".Length).Trim(); - } - - Camera new_camera = new Camera("Camera " + cam_addr + " - " + location + " - " + serial, cam_addr.ToString(), location, serial); - - add_camera(new_camera); - } + MessageBox.Show($"Failed to initialize UDP clients: {ex.Message}", "Network Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } @@ -179,29 +175,6 @@ namespace robospot_camera_finder } } - private void ServiceRemoved(object sender, ServiceAnnouncementEventArgs args) - { - foreach (var cam_ip in args.Announcement.Addresses) - { - if (cam_ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) - { - foreach (var camera in all_cameras) - { - if (cam_ip.ToString() == camera.camera_ip) - { - all_cameras.Remove(camera); - return; - } - } - } - } - } - - private void MainForm_FormClosing(object sender, FormClosingEventArgs e) - { - browser.StopBrowse(); - } - private void lbMain_MouseDoubleClick(object sender, MouseEventArgs e) { if (lbMain.SelectedItem != null) @@ -214,27 +187,6 @@ namespace robospot_camera_finder } } - private void startToolStripMenuItem_Click(object sender, EventArgs e) - { - browse(true); - } - - private void stopToolStripMenuItem_Click(object sender, EventArgs e) - { - browse(false); - } - - private void forceReloadToolStripMenuItem_Click(object sender, EventArgs e) - { - DialogResult result = MessageBox.Show("Are you sure you want to clear discovered cameras and restart the discovery process ?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); - if (result == DialogResult.Yes) - { - browse(false); - all_cameras.Clear(); - browse(true); - } - } - private void loadToolStripMenuItem_Click(object sender, EventArgs e) { string json = ""; @@ -283,5 +235,274 @@ namespace robospot_camera_finder add_camera(camera_to_add); } } + + // Helper method to convert structure to byte array + private byte[] StructureToByteArray(DataPacketIPv4 packet) + { + int size = Marshal.SizeOf(packet); + byte[] array = new byte[size]; + IntPtr ptr = Marshal.AllocHGlobal(size); + + try + { + Marshal.StructureToPtr(packet, ptr, true); + Marshal.Copy(ptr, array, 0, size); + } + finally + { + Marshal.FreeHGlobal(ptr); + } + + return array; + } + + // Helper method to convert byte array to structure + private DataPacketIPv4 ByteArrayToStructure(byte[] data) + { + DataPacketIPv4 packet = new DataPacketIPv4(); + int size = Marshal.SizeOf(packet); + IntPtr ptr = Marshal.AllocHGlobal(size); + + try + { + Marshal.Copy(data, 0, ptr, size); + packet = (DataPacketIPv4)Marshal.PtrToStructure(ptr, typeof(DataPacketIPv4)); + } + finally + { + Marshal.FreeHGlobal(ptr); + } + + return packet; + } + + // Helper method to create a string array field with null termination + private byte[] CreateStringField(string value, int fieldSize) + { + byte[] field = new byte[fieldSize]; + if (!string.IsNullOrEmpty(value)) + { + byte[] valueBytes = Encoding.ASCII.GetBytes(value); + int copyLength = Math.Min(valueBytes.Length, fieldSize - 1); // Leave space for null terminator + Array.Copy(valueBytes, field, copyLength); + } + return field; + } + + // Helper method to extract string from byte array + private string ExtractString(byte[] data) + { + if (data == null) return ""; + + // Find the null terminator + int nullIndex = Array.IndexOf(data, (byte)0); + if (nullIndex >= 0) + { + return Encoding.ASCII.GetString(data, 0, nullIndex); + } + else + { + return Encoding.ASCII.GetString(data).TrimEnd('\0'); + } + } + + // Create discovery packet + private DataPacketIPv4 CreateDiscoveryPacket() + { + DataPacketIPv4 packet = new DataPacketIPv4 + { + mode = DEF_REQ_SCAN, + packet_id = CreateStringField("CAM_FINDER_" + DateTime.Now.Ticks.ToString(), 18), + mac_addr = new byte[18], + ip_addr = new byte[16], + subnetmask = new byte[16], + gateway = new byte[16], + password = new byte[20], + reserved1 = 0, + port = 0, + status = 0, + device_name = new byte[10], + reserved2 = 0, + http_port = 0, + device_port = 0, + tcp_port = 0, + udp_port = 0, + upload_port = 0, + multicast_port = 0, + network_mode = 1, + ddns_url = new byte[128], + reserved3 = 0 + }; + + return packet; + } + + // Send discovery packet + private async Task SendDiscoveryPacketAsync(DataPacketIPv4 packet) + { + try + { + byte[] data = StructureToByteArray(packet); + IPEndPoint broadcastEndPoint = new IPEndPoint(IPAddress.Parse(BROADCAST_ADDRESS), SEND_PORT); + + int bytesSent = await sendClient.SendAsync(data, data.Length, broadcastEndPoint); + return bytesSent > 0; + } + catch (Exception ex) + { + MessageBox.Show($"Error sending discovery packet: {ex.Message}", "Network Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return false; + } + } + + // Listen for camera responses + private async Task ListenForResponsesAsync(int timeoutMs = 5000) + { + var startTime = DateTime.Now; + var endTime = startTime.AddMilliseconds(timeoutMs); + + try + { + while (DateTime.Now < endTime && isDiscovering) + { + var receiveTask = receiveClient.ReceiveAsync(); + var timeoutTask = Task.Delay(1000); // Check every second + + var completedTask = await Task.WhenAny(receiveTask, timeoutTask); + + if (completedTask == receiveTask) + { + var result = await receiveTask; + ProcessCameraResponse(result.Buffer, result.RemoteEndPoint); + } + } + } + catch (Exception ex) + { + if (isDiscovering) // Only show error if we're still supposed to be discovering + { + this.Invoke(new Action(() => + { + MessageBox.Show($"Error listening for responses: {ex.Message}", "Network Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + })); + } + } + } + + // Process received camera response + private void ProcessCameraResponse(byte[] data, IPEndPoint remoteEndPoint) + { + try + { + if (data.Length == Marshal.SizeOf()) + { + DataPacketIPv4 response = ByteArrayToStructure(data); + + // Check if this is a valid camera response + if (response.mode == RES_REQ_SCAN) + { + string cameraIP = ExtractString(response.ip_addr); + string deviceName = ExtractString(response.device_name); + string macAddress = ExtractString(response.mac_addr); + string packetId = ExtractString(response.packet_id); + + // Use the IP from the response or fall back to the sender's IP + if (string.IsNullOrEmpty(cameraIP)) + { + cameraIP = remoteEndPoint.Address.ToString(); + } + + // Create camera name + string cameraName = !string.IsNullOrEmpty(deviceName) ? deviceName : $"Camera {cameraIP}"; + + // Create camera object + Camera discoveredCamera = new Camera( + $"{deviceName} - ({cameraIP})", + cameraIP, + $"MAC: {macAddress}", + packetId // Using packet_id as serial for uniqueness + ); + + // Add camera to list (must be done on UI thread) + this.Invoke(new Action(() => + { + add_camera(discoveredCamera); + toolStripStatusLabel.Text = $"Found camera: {cameraName} ({cameraIP})"; + })); + } + } + } + catch (Exception ex) + { + this.Invoke(new Action(() => + { + MessageBox.Show($"Error processing camera response: {ex.Message}", "Processing Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); + })); + } + } + + // Main discovery method + private async void discover_cameras() + { + if (isDiscovering) + { + MessageBox.Show("Discovery is already in progress.", "Discovery", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + if (sendClient == null || receiveClient == null) + { + InitializeUdpClients(); + if (sendClient == null || receiveClient == null) + { + MessageBox.Show("Failed to initialize network clients.", "Network Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + + isDiscovering = true; + toolStripStatusLabel.Text = "Discovering cameras..."; + + try + { + // Create and send discovery packet + DataPacketIPv4 discoveryPacket = CreateDiscoveryPacket(); + bool sent = await SendDiscoveryPacketAsync(discoveryPacket); + + if (sent) + { + // Listen for responses for 10 seconds + await ListenForResponsesAsync(10000); + } + else + { + MessageBox.Show("Failed to send discovery packet.", "Network Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + catch (Exception ex) + { + MessageBox.Show($"Discovery error: {ex.Message}", "Discovery Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + finally + { + isDiscovering = false; + toolStripStatusLabel.Text = $"Discovery complete. Found {all_cameras.Count} cameras."; + } + } + + // Clean up resources when form is closing + protected override void OnFormClosing(FormClosingEventArgs e) + { + isDiscovering = false; + + try + { + sendClient?.Close(); + receiveClient?.Close(); + } + catch { } + + base.OnFormClosing(e); + } } } \ No newline at end of file diff --git a/MainForm.resx b/MainForm.resx index 968d0ca..e259a8a 100644 --- a/MainForm.resx +++ b/MainForm.resx @@ -1,7 +1,7 @@  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/StreamViewer.Designer.cs b/StreamViewer.Designer.cs index 1f71279..1afca2a 100644 --- a/StreamViewer.Designer.cs +++ b/StreamViewer.Designer.cs @@ -39,8 +39,6 @@ namespace robospot_camera_finder btnZoomMax = new Button(); tbRtspLink = new TextBox(); lblRtspLink = new Label(); - labelSerial = new Label(); - tbSerial = new TextBox(); cbTestZoom = new CheckBox(); timerZoomTestSeq = new System.Windows.Forms.Timer(components); ((System.ComponentModel.ISupportInitialize)videoView).BeginInit(); @@ -109,41 +107,22 @@ namespace robospot_camera_finder // tbRtspLink // tbRtspLink.Anchor = AnchorStyles.Bottom | AnchorStyles.Left; - tbRtspLink.Location = new Point(312, 492); + tbRtspLink.Location = new Point(75, 492); tbRtspLink.Name = "tbRtspLink"; tbRtspLink.ReadOnly = true; - tbRtspLink.Size = new Size(215, 23); + tbRtspLink.Size = new Size(559, 23); tbRtspLink.TabIndex = 8; // // lblRtspLink // lblRtspLink.Anchor = AnchorStyles.Bottom | AnchorStyles.Left; lblRtspLink.AutoSize = true; - lblRtspLink.Location = new Point(249, 494); + lblRtspLink.Location = new Point(12, 495); lblRtspLink.Name = "lblRtspLink"; lblRtspLink.Size = new Size(57, 15); lblRtspLink.TabIndex = 7; lblRtspLink.Text = "RTSP Link"; // - // labelSerial - // - labelSerial.Anchor = AnchorStyles.Bottom | AnchorStyles.Left; - labelSerial.AutoSize = true; - labelSerial.Location = new Point(9, 494); - labelSerial.Name = "labelSerial"; - labelSerial.Size = new Size(82, 15); - labelSerial.TabIndex = 6; - labelSerial.Text = "Serial Number"; - // - // tbSerial - // - tbSerial.Anchor = AnchorStyles.Bottom | AnchorStyles.Left; - tbSerial.Location = new Point(102, 492); - tbSerial.Name = "tbSerial"; - tbSerial.ReadOnly = true; - tbSerial.Size = new Size(124, 23); - tbSerial.TabIndex = 5; - // // cbTestZoom // cbTestZoom.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; @@ -170,9 +149,7 @@ namespace robospot_camera_finder Controls.Add(tbRtspLink); Controls.Add(lblRtspLink); Controls.Add(videoView); - Controls.Add(labelSerial); Controls.Add(btnZoomMax); - Controls.Add(tbSerial); Controls.Add(tbZoom); Controls.Add(btnZoomMin); Icon = (Icon)resources.GetObject("$this.Icon"); @@ -194,8 +171,6 @@ namespace robospot_camera_finder private System.Windows.Forms.Timer timerUpdateZoom; private Button btnZoomMin; private Button btnZoomMax; - private Label labelSerial; - private TextBox tbSerial; private TextBox tbRtspLink; private Label lblRtspLink; private CheckBox cbTestZoom; diff --git a/StreamViewer.cs b/StreamViewer.cs index 6c99c6e..705a786 100644 --- a/StreamViewer.cs +++ b/StreamViewer.cs @@ -29,7 +29,6 @@ namespace robospot_camera_finder updateZoomSlider(); - tbSerial.Text = camera.camera_serial; tbRtspLink.Text = rtsp_link; videoView.MediaPlayer = _mp; diff --git a/StreamViewer.resx b/StreamViewer.resx index 49180d8..19edc92 100644 --- a/StreamViewer.resx +++ b/StreamViewer.resx @@ -1,7 +1,7 @@