Skip to content

Commit 97a3021

Browse files
committed
Merge branch 'release/0.1.0'
* release/0.1.0: Update readme & abstract local file creation (GH-6) Add xml documentation to public members * fixes #6 (GH-3) Add Set/Get Date, Take picture sets date * fixes #3 (GH-4) Reduce external dependencies * fixes #4 Add TakePicture support for local uri Inital PoC
2 parents a45f0e8 + 1b2a9b4 commit 97a3021

37 files changed

+5075
-1
lines changed

README.md

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,79 @@
11
# OSC.Net
2-
.NET Standard Library for Open Spherical Camera API
2+
.NET Standard Library for Open Spherical Camera API 2.0
3+
4+
## Example
5+
```csharp
6+
var cameraClient = new CameraClient();
7+
cameraClient.TakePicture("test.jpg");
8+
```
9+
10+
## Client
11+
12+
The library exposes `CameraClient` class which implements an `ICameraClient` interface, which has a set of extension methods on it i.e. `TakePicture()`.
13+
14+
### Create client default IP (192.168.42.1) and Port (80)
15+
```csharp
16+
var cameraClient = new CameraClient();
17+
```
18+
19+
### Create client supply IP and Port
20+
```csharp
21+
var cameraClient = new CameraClient(new IPEndPoint(IPAddress.Parse("192.168.42.1"), 80));
22+
```
23+
24+
### Create client supply Uri
25+
```csharp
26+
var cameraClient = new CameraClient(new Uri("http://192.168.42.1"));
27+
```
28+
29+
### HttpClientFactoryHandler
30+
31+
All `CameraClient` constructors takes an optional `HttpClientFactoryHandler` `createClient`parameter which allows you to override how the internally used HttpClient is created.
32+
33+
Example usage with `System.Net` `IHttpClientFactory`
34+
```csharp
35+
var serviceProvider = new ServiceCollection().AddHttpClient().BuildServiceProvider();
36+
var httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
37+
38+
var httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
39+
40+
var cameraClient = new CameraClient(createClient: httpClientFactory.CreateClient);
41+
```
42+
43+
### CreateFileHandler
44+
45+
All `CameraClient` constructors takes an optional `CreateFileHandler` `createFile`parameter which allows you to override how local files are created.
46+
47+
```csharp
48+
var cameraClient = new CameraClient(
49+
createFile: path => File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None));
50+
```
51+
52+
## Take Picture
53+
54+
### Take picture and get uri to image
55+
56+
```csharp
57+
var pictureUri = await client.TakePicture();
58+
```
59+
60+
### Take picture and download image to supplied stream
61+
62+
```csharp
63+
var imageStream = new MemoryStream();
64+
await client.TakePicture(imageStream);
65+
```
66+
67+
### Take picture and save to supplied local path
68+
```csharp
69+
await client.TakePicture("test.jpg");
70+
```
71+
72+
### useLocalFileUri
73+
74+
All `TakePicture` methods takes an optional `useLocalFileUri` bool parameter, by default it's false. If set to true it'll use `ICameraClient.EndPoint` for construction an absolute uri to images. This is useful if using camera through proxy or firewall.
75+
76+
## Supported cameras
77+
78+
This library has been tested with
79+
* [Insta360 One X](https://www.insta360.com/product/insta360-onex/)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>netcoreapp3.0</TargetFramework>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" />
10+
<PackageReference Include="Microsoft.Extensions.Http" Version="3.0.0" />
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<ProjectReference Include="..\OSC.Net\OSC.Net.csproj" />
15+
</ItemGroup>
16+
17+
</Project>

src/OSC.Net.Console/Program.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System;
2+
using System.IO;
3+
using System.Net.Http;
4+
using static System.Console;
5+
using System.Threading.Tasks;
6+
using Microsoft.Extensions.DependencyInjection;
7+
using OSC.Net.Model;
8+
9+
namespace OSC.Net.Console
10+
{
11+
public static class Program
12+
{
13+
public static async Task Main(string[] args)
14+
{
15+
var serviceProvider = new ServiceCollection().AddHttpClient().BuildServiceProvider();
16+
var httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
17+
18+
19+
var client = Uri.TryCreate(
20+
Environment.GetEnvironmentVariable("OSC.Net.Console.EndPoint"),
21+
UriKind.Absolute,
22+
out var endpoint)
23+
? new CameraClient(endpoint, createClient: httpClientFactory.CreateClient)
24+
: new CameraClient(createClient: httpClientFactory.CreateClient);
25+
26+
27+
var currentDateTime = await client.GetDateTime();
28+
WriteLine(currentDateTime);
29+
30+
await client.SetDateTime(DateTimeOffset.Now);
31+
32+
currentDateTime = await client.GetDateTime();
33+
WriteLine(currentDateTime);
34+
35+
var pictureUri = await client.TakePicture(true);
36+
WriteLine("PictureUri: {0}", pictureUri);
37+
38+
var imageStream = new MemoryStream();
39+
await client.TakePicture(imageStream, true);
40+
WriteLine("Downloaded {0} bytes.", imageStream.Length);
41+
42+
await client.TakePicture("test.jpg", true);
43+
WriteLine("test.jpg {0}.", File.Exists("test.jpg") ? "exists" : "missing");
44+
45+
var captureMode = await client.GetCaptureMode();
46+
WriteLine("CaptureMode: {0}", captureMode);
47+
48+
await client.SetCaptureMode(CaptureMode.image);
49+
}
50+
}
51+
}

src/OSC.Net.sln

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.29318.209
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OSC.Net", "OSC.Net\OSC.Net.csproj", "{ADCC92AF-67AA-460C-B3D5-A20931C0A3E6}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OSC.Net.Console", "OSC.Net.Console\OSC.Net.Console.csproj", "{B9E66C20-2458-45E4-B811-40D9EAAD4F99}"
9+
EndProject
10+
Global
11+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
12+
Debug|Any CPU = Debug|Any CPU
13+
Release|Any CPU = Release|Any CPU
14+
EndGlobalSection
15+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
16+
{ADCC92AF-67AA-460C-B3D5-A20931C0A3E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17+
{ADCC92AF-67AA-460C-B3D5-A20931C0A3E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
18+
{ADCC92AF-67AA-460C-B3D5-A20931C0A3E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
19+
{ADCC92AF-67AA-460C-B3D5-A20931C0A3E6}.Release|Any CPU.Build.0 = Release|Any CPU
20+
{B9E66C20-2458-45E4-B811-40D9EAAD4F99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{B9E66C20-2458-45E4-B811-40D9EAAD4F99}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{B9E66C20-2458-45E4-B811-40D9EAAD4F99}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{B9E66C20-2458-45E4-B811-40D9EAAD4F99}.Release|Any CPU.Build.0 = Release|Any CPU
24+
EndGlobalSection
25+
GlobalSection(SolutionProperties) = preSolution
26+
HideSolutionNode = FALSE
27+
EndGlobalSection
28+
GlobalSection(ExtensibilityGlobals) = postSolution
29+
SolutionGuid = {98743999-18AB-4A71-AFE8-E57844A2F6EB}
30+
EndGlobalSection
31+
EndGlobal

src/OSC.Net/CameraClient.cs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using System;
2+
using System.IO;
3+
using System.Net;
4+
using System.Net.Http;
5+
using OSC.Net.Http;
6+
using OSC.Net.IO;
7+
8+
namespace OSC.Net
9+
{
10+
/// <summary>
11+
/// Standard implementation of <see cref="ICameraClient"/>.
12+
/// </summary>
13+
public class CameraClient : ICameraClient
14+
{
15+
private static Stream DefaultCreateFile(string path)
16+
=> File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None);
17+
18+
private static HttpClient DefaultCreateClient(string name) => new HttpClient();
19+
20+
/// <summary>
21+
/// The Default Camera Ip Address.
22+
/// </summary>
23+
/// <value>192.168.42.1</value>
24+
public const string DefaultIp = "192.168.42.1";
25+
26+
/// <summary>
27+
/// The Default Camera Port.
28+
/// </summary>
29+
/// <value>80</value>
30+
public const int DefaultPort = 80;
31+
32+
/// <summary>
33+
/// The Default Camera ip end point.
34+
/// </summary>
35+
/// <value>192.168.42.1:80</value>
36+
public static readonly IPEndPoint DefaultIpEndPoint = new IPEndPoint(IPAddress.Parse(DefaultIp), DefaultPort);
37+
38+
private HttpClientFactoryHandler CreateClient { get; }
39+
private CreateFileHandler CreateFile { get; }
40+
41+
/// <inheritdoc />
42+
public Uri EndPoint { get; }
43+
44+
/// <inheritdoc />
45+
HttpClient ICameraClient.GetHttpClient()
46+
{
47+
var client = CreateClient(nameof(CameraClient));
48+
client.BaseAddress = EndPoint;
49+
return client;
50+
}
51+
52+
/// <inheritdoc />
53+
Stream ICameraClient.CreateFile(string path) => CreateFile(path);
54+
55+
/// <summary>
56+
/// Initializes a new instance of the <see cref="CameraClient"/> class using <see cref="DefaultIpEndPoint"/>.
57+
/// </summary>
58+
/// <param name="createFile">Optional handler to override local file creation.</param>
59+
/// <param name="createClient">Optional handler to override http client creation.</param>
60+
public CameraClient(CreateFileHandler createFile = null, HttpClientFactoryHandler createClient = null) : this(DefaultIpEndPoint, createFile, createClient)
61+
{
62+
}
63+
64+
/// <summary>
65+
/// Initializes a new instance of the <see cref="CameraClient"/> class.
66+
/// </summary>
67+
/// <param name="ipEndPoint">The camera ip address and port.</param>
68+
/// <param name="createFile">Optional handler to override local file creation.</param>
69+
/// <param name="createClient">Optional handler to override http client creation.</param>
70+
public CameraClient(IPEndPoint ipEndPoint, CreateFileHandler createFile = null, HttpClientFactoryHandler createClient = null) : this(new Uri($"http://{ipEndPoint}"), createFile, createClient)
71+
{
72+
}
73+
74+
/// <summary>
75+
/// Initializes a new instance of the <see cref="CameraClient"/> class.
76+
/// </summary>
77+
/// <param name="endPoint">The camera http end point.</param>
78+
/// <param name="createFile">Optional handler to override local file creation.</param>
79+
/// <param name="createClient">Optional handler to override http client creation.</param>
80+
public CameraClient(Uri endPoint, CreateFileHandler createFile = null, HttpClientFactoryHandler createClient = null)
81+
{
82+
EndPoint = endPoint;
83+
CreateFile = createFile ?? DefaultCreateFile;
84+
CreateClient = createClient ?? DefaultCreateClient;
85+
}
86+
}
87+
}

src/OSC.Net/Commands.CaptureMode.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using OSC.Net.Model;
4+
using OSC.Net.Model.SetOptions;
5+
6+
namespace OSC.Net
7+
{
8+
public static partial class Commands
9+
{
10+
/// <summary>
11+
/// Gets camera current capture mode.
12+
/// </summary>
13+
/// <param name="client">The <see cref="ICameraClient"/> method extends.</param>
14+
/// <returns>Current <see cref="CaptureMode"/>.</returns>
15+
public static async Task<CaptureMode> GetCaptureMode(this ICameraClient client)
16+
{
17+
var result = await client.PostAsJson<Model.GetOptions.Result<Model.GetCaptureMode.Options>>(new
18+
{
19+
name = "camera.getOptions",
20+
parameters = new
21+
{
22+
optionNames = new[]
23+
{
24+
"captureMode"
25+
}
26+
}
27+
});
28+
29+
return Enum.TryParse(result?.results?.options?.captureMode, true, out CaptureMode mode)
30+
? mode
31+
: CaptureMode.unknown;
32+
}
33+
34+
/// <summary>
35+
/// Sets camera capture mode.
36+
/// </summary>
37+
/// <param name="client">The <see cref="ICameraClient"/> method extends.</param>
38+
/// <param name="captureMode">Supported capture modes <see cref="CaptureMode.image"/> and <see cref="CaptureMode.video"/>.</param>
39+
/// <returns></returns>
40+
public static async Task SetCaptureMode(this ICameraClient client, CaptureMode captureMode)
41+
{
42+
switch (captureMode)
43+
{
44+
case CaptureMode.image:
45+
case CaptureMode.video:
46+
break;
47+
48+
// ReSharper disable once RedundantCaseLabel
49+
case CaptureMode._other:
50+
// ReSharper disable once RedundantCaseLabel
51+
case CaptureMode.unknown:
52+
default:
53+
throw new ArgumentOutOfRangeException(nameof(captureMode), captureMode, null);
54+
}
55+
56+
var result = await client.PostAsJson<Result>(new
57+
{
58+
name = "camera.setOptions",
59+
parameters = new
60+
{
61+
options = new
62+
{
63+
captureMode = captureMode.ToString("F")
64+
}
65+
}
66+
});
67+
68+
if (result?.error != null)
69+
{
70+
throw new Exception($"Failed to set options (code: {result.error.code}, message: {result.error.message}).");
71+
}
72+
}
73+
}
74+
}

0 commit comments

Comments
 (0)