Skip to content

Commit a0f191a

Browse files
authored
2504: Add ONVIF media bicep (#118)
## Purpose Add ONVIF media bicep to support how-to in docs. ## Does this introduce a breaking change? ``` [ ] Yes [x] No ```
1 parent baee522 commit a0f191a

File tree

561 files changed

+18936
-4880
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

561 files changed

+18936
-4880
lines changed

samples/aio-onvif-connector-ptz-demo/Aio.Onvif.Connector.Ptz.Demo.sln

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,60 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 17
4-
VisualStudioVersion = 17.5.002.0
4+
VisualStudioVersion = 17.5.2.0
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aio.Onvif.Connector.Ptz.Demo", "Aio.Onvif.Connector.Ptz.Demo\Aio.Onvif.Connector.Ptz.Demo.csproj", "{BE93AC76-C663-47D1-9C3A-E18E32E18A3F}"
77
EndProject
8-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PTZ", "PTZ\PTZ.csproj", "{2EC99FE6-8C42-4AAE-ABCD-DE4291ED2655}"
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaClient", "MediaClient\MediaClient.csproj", "{5FC4EEFA-CD63-44CB-8426-6F54DA096328}"
9+
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PtzClient", "PtzClient\PtzClient.csproj", "{A3A934C1-6A52-4FCB-96C1-ACE0F4C50911}"
911
EndProject
1012
Global
1113
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1214
Debug|Any CPU = Debug|Any CPU
15+
Debug|x64 = Debug|x64
16+
Debug|x86 = Debug|x86
1317
Release|Any CPU = Release|Any CPU
18+
Release|x64 = Release|x64
19+
Release|x86 = Release|x86
1420
EndGlobalSection
1521
GlobalSection(ProjectConfigurationPlatforms) = postSolution
1622
{BE93AC76-C663-47D1-9C3A-E18E32E18A3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1723
{BE93AC76-C663-47D1-9C3A-E18E32E18A3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
24+
{BE93AC76-C663-47D1-9C3A-E18E32E18A3F}.Debug|x64.ActiveCfg = Debug|Any CPU
25+
{BE93AC76-C663-47D1-9C3A-E18E32E18A3F}.Debug|x64.Build.0 = Debug|Any CPU
26+
{BE93AC76-C663-47D1-9C3A-E18E32E18A3F}.Debug|x86.ActiveCfg = Debug|Any CPU
27+
{BE93AC76-C663-47D1-9C3A-E18E32E18A3F}.Debug|x86.Build.0 = Debug|Any CPU
1828
{BE93AC76-C663-47D1-9C3A-E18E32E18A3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
1929
{BE93AC76-C663-47D1-9C3A-E18E32E18A3F}.Release|Any CPU.Build.0 = Release|Any CPU
20-
{2EC99FE6-8C42-4AAE-ABCD-DE4291ED2655}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21-
{2EC99FE6-8C42-4AAE-ABCD-DE4291ED2655}.Debug|Any CPU.Build.0 = Debug|Any CPU
22-
{2EC99FE6-8C42-4AAE-ABCD-DE4291ED2655}.Release|Any CPU.ActiveCfg = Release|Any CPU
23-
{2EC99FE6-8C42-4AAE-ABCD-DE4291ED2655}.Release|Any CPU.Build.0 = Release|Any CPU
30+
{BE93AC76-C663-47D1-9C3A-E18E32E18A3F}.Release|x64.ActiveCfg = Release|Any CPU
31+
{BE93AC76-C663-47D1-9C3A-E18E32E18A3F}.Release|x64.Build.0 = Release|Any CPU
32+
{BE93AC76-C663-47D1-9C3A-E18E32E18A3F}.Release|x86.ActiveCfg = Release|Any CPU
33+
{BE93AC76-C663-47D1-9C3A-E18E32E18A3F}.Release|x86.Build.0 = Release|Any CPU
34+
{5FC4EEFA-CD63-44CB-8426-6F54DA096328}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35+
{5FC4EEFA-CD63-44CB-8426-6F54DA096328}.Debug|Any CPU.Build.0 = Debug|Any CPU
36+
{5FC4EEFA-CD63-44CB-8426-6F54DA096328}.Debug|x64.ActiveCfg = Debug|Any CPU
37+
{5FC4EEFA-CD63-44CB-8426-6F54DA096328}.Debug|x64.Build.0 = Debug|Any CPU
38+
{5FC4EEFA-CD63-44CB-8426-6F54DA096328}.Debug|x86.ActiveCfg = Debug|Any CPU
39+
{5FC4EEFA-CD63-44CB-8426-6F54DA096328}.Debug|x86.Build.0 = Debug|Any CPU
40+
{5FC4EEFA-CD63-44CB-8426-6F54DA096328}.Release|Any CPU.ActiveCfg = Release|Any CPU
41+
{5FC4EEFA-CD63-44CB-8426-6F54DA096328}.Release|Any CPU.Build.0 = Release|Any CPU
42+
{5FC4EEFA-CD63-44CB-8426-6F54DA096328}.Release|x64.ActiveCfg = Release|Any CPU
43+
{5FC4EEFA-CD63-44CB-8426-6F54DA096328}.Release|x64.Build.0 = Release|Any CPU
44+
{5FC4EEFA-CD63-44CB-8426-6F54DA096328}.Release|x86.ActiveCfg = Release|Any CPU
45+
{5FC4EEFA-CD63-44CB-8426-6F54DA096328}.Release|x86.Build.0 = Release|Any CPU
46+
{A3A934C1-6A52-4FCB-96C1-ACE0F4C50911}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47+
{A3A934C1-6A52-4FCB-96C1-ACE0F4C50911}.Debug|Any CPU.Build.0 = Debug|Any CPU
48+
{A3A934C1-6A52-4FCB-96C1-ACE0F4C50911}.Debug|x64.ActiveCfg = Debug|Any CPU
49+
{A3A934C1-6A52-4FCB-96C1-ACE0F4C50911}.Debug|x64.Build.0 = Debug|Any CPU
50+
{A3A934C1-6A52-4FCB-96C1-ACE0F4C50911}.Debug|x86.ActiveCfg = Debug|Any CPU
51+
{A3A934C1-6A52-4FCB-96C1-ACE0F4C50911}.Debug|x86.Build.0 = Debug|Any CPU
52+
{A3A934C1-6A52-4FCB-96C1-ACE0F4C50911}.Release|Any CPU.ActiveCfg = Release|Any CPU
53+
{A3A934C1-6A52-4FCB-96C1-ACE0F4C50911}.Release|Any CPU.Build.0 = Release|Any CPU
54+
{A3A934C1-6A52-4FCB-96C1-ACE0F4C50911}.Release|x64.ActiveCfg = Release|Any CPU
55+
{A3A934C1-6A52-4FCB-96C1-ACE0F4C50911}.Release|x64.Build.0 = Release|Any CPU
56+
{A3A934C1-6A52-4FCB-96C1-ACE0F4C50911}.Release|x86.ActiveCfg = Release|Any CPU
57+
{A3A934C1-6A52-4FCB-96C1-ACE0F4C50911}.Release|x86.Build.0 = Release|Any CPU
2458
EndGlobalSection
2559
GlobalSection(SolutionProperties) = preSolution
2660
HideSolutionNode = FALSE

samples/aio-onvif-connector-ptz-demo/Aio.Onvif.Connector.Ptz.Demo/Aio.Onvif.Connector.Ptz.Demo.csproj

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

3-
<ItemGroup>
4-
<ProjectReference Include="..\PTZ\PTZ.csproj" />
3+
<ItemGroup>
4+
<PackageReference Include="Azure.Iot.Operations.Mqtt" Version="0.9.*-*" />
5+
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
56
</ItemGroup>
67

78
<ItemGroup>
8-
<PackageReference Include="Azure.Iot.Operations.Mqtt" Version="0.4.225" />
9-
</ItemGroup>
9+
<ProjectReference Include="..\MediaClient\MediaClient.csproj" />
10+
<ProjectReference Include="..\PtzClient\PtzClient.csproj" />
11+
</ItemGroup>
1012

1113
<PropertyGroup>
1214
<OutputType>Exe</OutputType>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
using Azure.Iot.Operations.Protocol;
3+
using static MediaClient.Media.Media;
4+
5+
namespace Aio.Onvif.Connector.Ptz.Demo;
6+
7+
public class OnvifMediaClient : Client
8+
{
9+
public OnvifMediaClient(ApplicationContext applicationContext, IMqttPubSubClient mqttClient) : base(applicationContext, mqttClient)
10+
{
11+
}
12+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
using Azure.Iot.Operations.Protocol;
3+
using static PtzClient.Ptz.Ptz;
4+
5+
6+
namespace Aio.Onvif.Connector.Ptz.Demo;
7+
8+
public class OnvifPtzClient : Client
9+
{
10+
public OnvifPtzClient(ApplicationContext applicationContext, IMqttPubSubClient mqttClient) : base(applicationContext, mqttClient)
11+
{
12+
}
13+
}
Lines changed: 165 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,193 @@
1-
using Aio.Onvif.Connector.Ptz.Demo;
1+
using System.CommandLine;
2+
using Aio.Onvif.Connector.Ptz.Demo;
23
using Azure.Iot.Operations.Mqtt.Session;
4+
using Azure.Iot.Operations.Protocol;
35
using Azure.Iot.Operations.Protocol.Models;
4-
using PTZ.dtmi_onvif_ptz__1;
5-
6-
Console.Write("Mqtt Broker Host: ");
7-
var host = Console.ReadLine();
8-
if (string.IsNullOrWhiteSpace(host))
6+
using MediaClient.Media;
7+
using PtzClient.Ptz;
8+
9+
var hostOption = new Option<string>(["--mqtt-host", "-h"], description: "The Hostname or IP of the MQTT Listener the demo connects to", getDefaultValue: () => "localhost");
10+
var portOption = new Option<int>(["--mqtt-port", "-p"], description: "The port of the MQTT Listener the demo connects to", getDefaultValue: () => 1883);
11+
var namespaceOption = new Option<string>(["--namespace", "-n"], description: "The Kubernetes namespace AIO is deployed to", getDefaultValue: () => "azure-iot-operations");
12+
var ptzAssetOption = new Option<string>(["--ptz-asset", "-pa"], description: "The name of the PTZ asset") { IsRequired = true };
13+
var mediaAssetOption = new Option<string>(["--media-asset", "-ma"], description: "The name of the media asset") { IsRequired = true };
14+
var modeOption = new Option<string>(["--mode", "-m"], description: "The method that should be used to move the camera", getDefaultValue: () => "relative").FromAmong("relative", "continuous");
15+
16+
var rootCommand = new RootCommand("AIO ONVIF Connector Demo");
17+
rootCommand.AddOption(hostOption);
18+
rootCommand.AddOption(portOption);
19+
rootCommand.AddOption(namespaceOption);
20+
rootCommand.AddOption(ptzAssetOption);
21+
rootCommand.AddOption(mediaAssetOption);
22+
rootCommand.AddOption(modeOption);
23+
24+
rootCommand.SetHandler(async (host, port, @namespace, ptzAsset, mediaAsset, mode) =>
925
{
10-
Console.Error.WriteLine("Invalid host");
11-
Environment.Exit(1);
12-
}
26+
Console.WriteLine($"MQTT Host: {host}");
27+
Console.WriteLine($"MQTT Port: {port}");
28+
Console.WriteLine($"Namespace: {@namespace}");
29+
Console.WriteLine($"PTZ Asset: {ptzAsset}");
30+
Console.WriteLine($"Media Asset: {mediaAsset}");
31+
Console.WriteLine($"Mode: {mode}");
1332

14-
Console.Write("Mqtt Broker Port: ");
15-
if (!int.TryParse(Console.ReadLine(), out var port))
16-
{
17-
Console.Error.WriteLine("Invalid port number");
18-
Environment.Exit(1);
19-
}
33+
var mqttClientTcpOptions = new MqttClientTcpOptions(host, port);
2034

21-
Console.Write("AIO Namespace: ");
22-
var aioNamespace = Console.ReadLine();
23-
if (string.IsNullOrWhiteSpace(aioNamespace))
24-
{
25-
Console.Error.WriteLine("Invalid AIO namespace");
26-
Environment.Exit(1);
27-
}
35+
var mqttClientOptions = new MqttClientOptions(mqttClientTcpOptions) { SessionExpiryInterval = 60 };
2836

29-
Console.Write("Asset Name: ");
30-
var assetName = Console.ReadLine();
31-
if (string.IsNullOrWhiteSpace(assetName))
32-
{
33-
Console.Error.WriteLine("Invalid asset name");
34-
Environment.Exit(1);
35-
}
36-
37-
Console.Write("Profile Token: ");
38-
var profileToken = Console.ReadLine();
39-
if (string.IsNullOrWhiteSpace(profileToken))
40-
{
41-
Console.Error.WriteLine("Invalid profile token");
42-
Environment.Exit(1);
43-
}
44-
45-
Console.Clear();
37+
var mqttSessionClient = new MqttSessionClient(new MqttSessionClientOptions());
38+
await mqttSessionClient.ConnectAsync(mqttClientOptions);
39+
var applicationContext = new ApplicationContext();
40+
var ptzClient = new OnvifPtzClient(applicationContext, mqttSessionClient);
41+
var mediaClient = new OnvifMediaClient(applicationContext, mqttSessionClient);
4642

47-
var mqttClientTcpOptions = new MqttClientTcpOptions(host, port);
43+
var profiles = await mediaClient.GetProfilesAsync(new Dictionary<string, string>
44+
{
45+
{ "ex:namespace", @namespace },
46+
{ "ex:asset", mediaAsset }
47+
});
48+
var profile = profiles.GetProfilesCommandResponse.Profiles.First();
4849

49-
var mqttClientOptions = new MqttClientOptions(mqttClientTcpOptions) { SessionExpiryInterval = 60 };
50+
Console.WriteLine("Use arrow keys or WASD to move camera, Q to quit");
5051

51-
var mqttSessionClient = new MqttSessionClient(new MqttSessionClientOptions());
52+
if (mode == "relative")
53+
{
54+
await StartRelativeMoveAsync(ptzClient, profile, @namespace, ptzAsset);
55+
}
56+
else if (mode == "continuous")
57+
{
58+
await StartContinuousMoveAsync(ptzClient, profile, @namespace, ptzAsset);
59+
}
5260

53-
await mqttSessionClient.ConnectAsync(mqttClientOptions).ConfigureAwait(true);
54-
var client = new PtzClient(mqttSessionClient);
55-
client.CustomTopicTokenMap.Add("asset", assetName);
56-
client.CustomTopicTokenMap.Add("namespace", aioNamespace);
61+
await StartRelativeMoveAsync(ptzClient, profile, @namespace, ptzAsset);
62+
}, hostOption, portOption, namespaceOption, ptzAssetOption, mediaAssetOption, modeOption);
5763

58-
Console.WriteLine("Use arrow keys or WASD to move camera, Q to quit");
64+
await rootCommand.InvokeAsync(args);
5965

60-
while (true)
66+
async Task StartRelativeMoveAsync(OnvifPtzClient ptzClient, Profile profile, string @namespace, string ptzAssetName)
6167
{
62-
var key = Console.ReadKey(true).Key;
63-
if (key == ConsoleKey.Q)
68+
while (true)
6469
{
65-
break;
66-
}
70+
var key = Console.ReadKey(true).Key;
71+
if (key == ConsoleKey.Q)
72+
{
73+
return;
74+
}
6775

68-
(float x, float y)? delta = key switch
69-
{
70-
ConsoleKey.UpArrow => (0, 0.2f),
71-
ConsoleKey.DownArrow => (0, -0.2f),
72-
ConsoleKey.LeftArrow => (0.2f, 0),
73-
ConsoleKey.RightArrow => (-0.2f, 0),
74-
ConsoleKey.W => (0, 0.2f),
75-
ConsoleKey.S => (0, -0.2f),
76-
ConsoleKey.A => (0.2f, 0),
77-
ConsoleKey.D => (-0.2f, 0),
78-
_ => null
79-
};
80-
81-
if (delta == null)
82-
{
83-
continue;
84-
}
76+
(float x, float y)? delta = key switch
77+
{
78+
ConsoleKey.UpArrow => (0, 0.2f),
79+
ConsoleKey.DownArrow => (0, -0.2f),
80+
ConsoleKey.LeftArrow => (0.2f, 0),
81+
ConsoleKey.RightArrow => (-0.2f, 0),
82+
ConsoleKey.W => (0, 0.2f),
83+
ConsoleKey.S => (0, -0.2f),
84+
ConsoleKey.A => (0.2f, 0),
85+
ConsoleKey.D => (-0.2f, 0),
86+
_ => null
87+
};
8588

86-
try
87-
{
88-
var request = new RelativeMoveRequestPayload
89+
if (delta == null)
90+
{
91+
continue;
92+
}
93+
94+
try
8995
{
90-
RelativeMove = new Object_Onvif_Ptz_RelativeMove__1
96+
var request = new RelativeMoveRequestPayload
9197
{
92-
ProfileToken = profileToken,
93-
Translation = new Object_Onvif_Ptz_PTZVector__1
98+
RelativeMove = new RelativeMove
9499
{
95-
PanTilt = new Object_Onvif_Ptz_Vector2D__1
100+
ProfileToken = profile.Token,
101+
Translation = new Ptzvector
96102
{
97-
X = delta.Value.x,
98-
Y = delta.Value.y,
103+
PanTilt = new PtzClient.Ptz.Vector2d
104+
{
105+
X = delta.Value.x,
106+
Y = delta.Value.y,
107+
}
99108
}
100109
}
101-
}
102-
};
110+
};
103111

104-
await client.RelativeMoveAsync(request);
112+
await ptzClient.RelativeMoveAsync(request, new Dictionary<string, string>
113+
{
114+
{ "ex:namespace", @namespace },
115+
{ "ex:asset", ptzAssetName }
116+
});
117+
}
118+
catch (Exception e)
119+
{
120+
Console.WriteLine($"Error: {e.Message}");
121+
// Bad request is expected if the camera reaches the limit
122+
}
123+
124+
await Task.Delay(1000).ConfigureAwait(true);
105125
}
106-
catch (System.Exception)
126+
}
127+
128+
async Task StartContinuousMoveAsync(OnvifPtzClient ptzClient, Profile profile, string @namespace, string ptzAssetName)
129+
{
130+
while (true)
107131
{
108-
// Bad request is expected if the camera reaches the limit
109-
}
132+
var key = Console.ReadKey(true).Key;
133+
if (key == ConsoleKey.Q)
134+
{
135+
return;
136+
}
137+
138+
(float x, float y)? delta = key switch
139+
{
140+
ConsoleKey.UpArrow => (0, 0.2f),
141+
ConsoleKey.DownArrow => (0, -0.2f),
142+
ConsoleKey.LeftArrow => (-0.2f, 0),
143+
ConsoleKey.RightArrow => (0.2f, 0),
144+
ConsoleKey.W => (0, 0.2f),
145+
ConsoleKey.S => (0, -0.2f),
146+
ConsoleKey.A => (-0.2f, 0),
147+
ConsoleKey.D => (0.2f, 0),
148+
_ => null
149+
};
110150

111-
await Task.Delay(200).ConfigureAwait(true);
151+
if (delta == null)
152+
{
153+
continue;
154+
}
155+
156+
try
157+
{
158+
var request = new ContinuousMoveRequestPayload
159+
{
160+
ContinuousMove = new ContinuousMove
161+
{
162+
ProfileToken = profile.Token,
163+
Velocity = new PtzClient.Ptz.Ptzspeed
164+
{
165+
PanTilt = new PtzClient.Ptz.Vector2d
166+
{
167+
X = delta.Value.x,
168+
Y = delta.Value.y,
169+
}
170+
}
171+
}
172+
};
173+
174+
await ptzClient.ContinuousMoveAsync(request, new Dictionary<string, string>
175+
{
176+
{ "ex:namespace", @namespace },
177+
{ "ex:asset", ptzAssetName }
178+
});
179+
await Task.Delay(1000).ConfigureAwait(true);
180+
await ptzClient.StopAsync(new StopRequestPayload { Stop = new Stop { ProfileToken = profile.Token, PanTilt = true } }, new Dictionary<string, string>
181+
{
182+
{ "ex:namespace", @namespace },
183+
{ "ex:asset", ptzAssetName }
184+
});
185+
}
186+
catch (Exception e)
187+
{
188+
Console.WriteLine($"Error: {e.Message}");
189+
// Bad request is expected if the camera reaches the limit
190+
}
191+
}
112192
}
113193

0 commit comments

Comments
 (0)