Fred913.IosMediaSession
0.2.0
dotnet add package Fred913.IosMediaSession --version 0.2.0
NuGet\Install-Package Fred913.IosMediaSession -Version 0.2.0
<PackageReference Include="Fred913.IosMediaSession" Version="0.2.0" />
<PackageVersion Include="Fred913.IosMediaSession" Version="0.2.0" />
<PackageReference Include="Fred913.IosMediaSession" />
paket add Fred913.IosMediaSession --version 0.2.0
#r "nuget: Fred913.IosMediaSession, 0.2.0"
#:package Fred913.IosMediaSession@0.2.0
#addin nuget:?package=Fred913.IosMediaSession&version=0.2.0
#tool nuget:?package=Fred913.IosMediaSession&version=0.2.0
Fred913.IosMediaSession
A small C# library for reading iPhone media metadata and controlling playback from Windows over Bluetooth LE, using Apple Media Service (AMS).
Targets .NET Standard 2.0, with a Windows Bluetooth backend. The required APIs are available on Windows 10 version 1703 (build 15063) or later. A BLE adapter and an iPhone exposing AMS are required. This is a Windows client for an iPhone; it does not run inside an iOS app. The package contains the class library only: no WinForms UI, GSMTC publisher, or artwork search.
dotnet add package Fred913.IosMediaSession --version 0.2.0
No Windows-specific target framework is required in the consuming project.
The same netstandard2.0 assembly has passed protocol tests and real Bluetooth
discovery in .NET Framework 4.8 and .NET 8 applications on Windows 11.
The Windows 10 minimum is based on the APIs used; this release has not been run on
that older OS. Other .NET Standard 2.0 runtimes, including Unity, are not individually
validated. macOS, Linux, Android, and iOS Bluetooth backends are not included.
Windows Runtime projections are embedded privately using
C#/WinRT.
Consumers do not need Microsoft.Windows.SDK.NET.dll, WinRT.Runtime.dll, or a
Windows SDK reference. The package carries the standard async and memory dependencies.
Migrating from 0.1.0
InitializeAsync's supported-command callback and SupportedCommandsReceived
now use IReadOnlyCollection<byte> instead of IReadOnlySet<byte>, which does
not exist in .NET Standard 2.0. Update explicitly typed handlers and recompile.
The collection still contains unique command IDs; commands.Contains(id) is
available through System.Linq. Other public methods and the AMS protocol are unchanged.
Usage
using Fred913.IosMediaSession;
var devices = await BleDeviceDiscovery.GetDevicesAsync();
foreach (var device in devices)
Console.WriteLine($"{device.AddressText} {device.DisplayName}");
// Let your UI/user choose an address from the discovery results.
ulong address = Convert.ToUInt64(args[0].Replace(":", ""), 16);
using var stop = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) => { e.Cancel = true; stop.Cancel(); };
await using var client = await AmsClient.ConnectAsync(address, Console.WriteLine);
void OnUpdate(AmsUpdate update) =>
Console.WriteLine($"{update.EntityId}/{update.AttributeId}: {update.Value}");
void OnCommands(IReadOnlyCollection<byte> commands) =>
Console.WriteLine($"Supported commands: {string.Join(", ", commands)}");
client.UpdateReceived += OnUpdate;
client.SupportedCommandsReceived += OnCommands;
await client.InitializeAsync(OnUpdate, OnCommands, stop.Token);
// While RunAsync is active, UI handlers can call:
// client.QueueRemoteCommand(0); // Play, only if the reported set contains 0.
// client.QueueRemoteCommand(1); // Pause, only if the reported set contains 1.
// client.QueueProgressRefresh();
try
{
await client.RunAsync(stop.Token);
}
catch (OperationCanceledException) when (stop.IsCancellationRequested)
{
// Normal shutdown; await using releases the BLE connection.
}
Connect → initialize once → run once → cancel and await → dispose. On errors or disconnection, dispose and create a new connection; automatic reconnection and periodic progress refresh are left to the host application. Do not dispose during initialization or while the run loop is active. Cancellation stops the run loop; Windows pairing and an in-flight GATT operation may need time to finish.
Initialization sends its initial values through the supplied callbacks. Subsequent
values arrive through the events during RunAsync. Use the same handlers for both.
UpdateReceived and SupportedCommandsReceived run on the processing context;
marshal to your UI as needed. NotificationReceived and TrackNotificationReceived
run directly on the Bluetooth callback thread: keep handlers short and never throw.
Discovery scans for four seconds and merges nearby and paired BLE devices.
SupportsAms is a hint from advertisements or the Windows service cache, not proof
that a device is currently reachable. The BLE address may differ from the classic
Bluetooth address. Unpaired devices require confirmation in Windows and on iPhone.
Attribute values
AmsUpdate preserves the AMS entity and attribute IDs. Values are strings; parse
numbers using CultureInfo.InvariantCulture. Empty means the attribute is absent.
Truncated notifications are read back in full before delivery.
| Entity | Attribute | Meaning / format |
|---|---|---|
| 0 Player | 0 | Player name |
| 0 Player | 1 | state,rate,elapsedSeconds |
| 0 Player | 2 | Volume, 0–1 |
| 1 Queue | 0 | Queue index, zero-based |
| 1 Queue | 1 | Queue count |
| 1 Queue | 2 | Shuffle: off (0), one (1), all (2) |
| 1 Queue | 3 | Repeat: off (0), one (1), all (2) |
| 2 Track | 0 | Artist |
| 2 Track | 1 | Album |
| 2 Track | 2 | Title |
| 2 Track | 3 | Duration in seconds |
Playback states: paused (0), playing (1), rewinding (2), fast-forwarding (3).
Between notifications, estimated position is elapsedSeconds + secondsSinceUpdate * rate.
Remote commands
| ID | Command | ID | Command |
|---|---|---|---|
| 0 | Play | 7 | Advance repeat mode |
| 1 | Pause | 8 | Advance shuffle mode |
| 2 | Toggle play/pause | 9 | Skip forward |
| 3 | Next track | 10 | Skip backward |
| 4 | Previous track | 11 | Like track |
| 5 | Volume up | 12 | Dislike track |
| 6 | Volume down | 13 | Bookmark track |
Only queue commands included in the latest supported-command set. A successful write means the command was forwarded, not that playback changed. AMS does not provide artwork or absolute seeking. Skip amounts are controlled by the media app.
Build
dotnet build -c Release
dotnet pack -c Release -o artifacts
dotnet run --project tests/SmokeTests.csproj -c Release -f net48
dotnet run --project tests/SmokeTests.csproj -c Release -f net8.0
Building from source requires Windows and a .NET 8+ SDK (C# 12). That compiler
requirement does not apply to applications consuming the package. The Windows
metadata and projection generator are restored from NuGet as build-only dependencies.
Add -- --bluetooth to a test command to exercise discovery with a real BLE adapter.
Protocol reference: Apple Media Service and attribute / command IDs.
MIT licensed. Source: fred913/Fred913.IosMediaSession.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.Bcl.AsyncInterfaces (>= 8.0.0)
- System.Memory (>= 4.6.3)
- System.Runtime.Extensions (>= 4.3.1)
- System.Threading.Channels (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Targets .NET Standard 2.0 with embedded C#/WinRT. Supported-command callbacks now use IReadOnlyCollection<byte>. Bluetooth discovery verified on .NET Framework 4.8 and .NET 8.