Skip to content

Commit 2f89863

Browse files
authored
Merge pull request #14 from OpenIPC/fix/onvif-discovery-android-platform-not-supported
fix(onvif): make WS-Discovery work on Android/iOS
2 parents a401d97 + 9ab9c01 commit 2f89863

2 files changed

Lines changed: 47 additions & 2 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System.Net;
2+
using System.Net.Sockets;
3+
using System.Threading.Tasks;
4+
using Onvif.Core.Discovery.Interfaces;
5+
6+
namespace OpenIPC.Viewer.Devices.Onvif.Discovery;
7+
8+
// Drop-in IUdpClient for Onvif.Core's WSDiscovery. The stock UdpClientWrapper
9+
// calls IPGlobalProperties.GetActiveTcpListeners() in its constructor (a
10+
// port-collision pre-check) — that API is unimplemented on Android/iOS and
11+
// throws PlatformNotSupportedException ("Arg_PlatformNotSupported"), so device
12+
// discovery died before sending a single probe on mobile.
13+
//
14+
// This implementation skips that check: it binds an ephemeral UDP socket and
15+
// sends the multicast probe to 239.255.255.250:3702. WS-Discovery ProbeMatch
16+
// replies come back as *unicast* to our source port, so we never join a
17+
// multicast group — which also means no Android WifiManager.MulticastLock is
18+
// required. Works identically on desktop, so no per-platform branching.
19+
internal sealed class PlatformSafeUdpClient : IUdpClient
20+
{
21+
private readonly UdpClient _client;
22+
23+
public PlatformSafeUdpClient()
24+
{
25+
// Port 0 = let the OS pick a free ephemeral port. The stock wrapper
26+
// hard-bound port 80, which is both privileged and collision-prone.
27+
_client = new UdpClient(new IPEndPoint(IPAddress.Any, 0)) { EnableBroadcast = true };
28+
}
29+
30+
public short Ttl
31+
{
32+
get => _client.Ttl;
33+
set => _client.Ttl = value;
34+
}
35+
36+
public Task<int> SendAsync(byte[] datagram, int bytes, IPEndPoint endPoint) =>
37+
_client.SendAsync(datagram, bytes, endPoint);
38+
39+
public Task<UdpReceiveResult> ReceiveAsync() => _client.ReceiveAsync();
40+
41+
public void Close() => _client.Close();
42+
43+
public void Dispose() => _client.Dispose();
44+
}

‎src/OpenIPC.Viewer.Devices/Onvif/Discovery/WsDiscoveryService.cs‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System.Threading.Tasks;
77
using Microsoft.Extensions.Logging;
88
using Onvif.Core.Discovery;
9-
using Onvif.Core.Discovery.Common;
109
using Onvif.Core.Discovery.Models;
1110
using OpenIPC.Viewer.Core.Onvif.Discovery;
1211

@@ -32,7 +31,9 @@ public async IAsyncEnumerable<DiscoveredCamera> ScanAsync(
3231
{
3332
var seconds = Math.Max(1, (int)Math.Ceiling(timeout.TotalSeconds));
3433
var ws = new WSDiscovery();
35-
var client = new UdpClientWrapper();
34+
// Our own IUdpClient — the stock UdpClientWrapper crashes on Android/iOS
35+
// (GetActiveTcpListeners is PlatformNotSupported). See PlatformSafeUdpClient.
36+
var client = new PlatformSafeUdpClient();
3637

3738
_logger.LogDebug("WS-Discovery scan starting (timeout={Seconds}s)", seconds);
3839
IEnumerable<DiscoveryDevice> devices;

0 commit comments

Comments
 (0)