Skip to content

Getting started

Melih Ercan edited this page Sep 21, 2026 · 4 revisions

Getting started

Installing the packages and wiring them into an app. Fifteen minutes, and at the end of it Hello world proves the native half actually loaded.

If you want the shortest possible path: create the project, add the package, do the one platform-specific step your platform needs, then run Hello world.

Before you start

  • .NET 10 SDK. dotnet --version should report 10.0.400 or later.
  • MAUI workload, if you are building a MAUI app: dotnet workload install maui.
  • Whatever your platform normally needs to build and deploy — Android SDK and a JDK, Xcode and a Mac for iOS and Mac Catalyst, the Windows 10 SDK for Windows.

Three platform requirements fail silently if you miss them — a hang with no message, or a restore error that does not name this package. They are covered in Platform prerequisites, and each one is repeated in place below.

Choose a package

reference what it gives you
I have my own signalling and UI WebRTCme the API and the bindings
I want a call app without writing plumbing WebRTCme.Middleware the above, plus managers, a video tile and a connection layer

The middleware depends on the API package, so referencing it brings both. Never reference a binding directly — they are inside WebRTCme.


Blazor WebAssembly

WebAssembly only. Blazor Server and Blazor Hybrid are not supported.

1. Create the project and add the package

dotnet new blazorwasm -n HelloWebRtc
cd HelloWebRtc
dotnet add package WebRTCme --version 26.9.21

Add WebRTCme.Middleware too if you want the middleware:

dotnet add package WebRTCme.Middleware --version 26.9.21

2. Reference the interop script

This one is silent if you miss it. The Blazor binding is JSInterop over the browser's own WebRTC API, and the JavaScript half ships as a static web asset. Without the tag nothing works and nothing says why.

In wwwroot/index.html, before _framework/blazor.webassembly.js:

<script src="_content/WebRTCme/JsInterop.js"></script>

Coming from 2.0.0? The path changed. It used to be _content/WebRTCme.Bindings.Blazor/JsInterop.js.

3. Register the services

Only needed if you are using WebRTCme.Middleware. The API package on its own needs no registration at all — see step 4.

using WebRTCme;
using WebRTCme.Middleware;

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");

var middleware = CrossWebRtcMiddlewareBlazor.Current;
builder.Services.AddSingleton(middleware.WebRtc);
builder.Services.AddSingleton(middleware);
builder.Services.AddBlazorMiddleware();

await builder.Build().RunAsync();

4. Reach the API

Blazor is the one platform where Window() needs an argument: the browser's IJSRuntime, which a component gets by injection and a plain class has no way to reach.

@inject IJSRuntime JsRuntime

@code {
    protected override void OnInitialized()
    {
        var window = CrossWebRtc.Current.Window(JsRuntime);
        var pc = window.RTCPeerConnection(new RTCConfiguration { IceServers = [] });
    }
}

The other four platforms ignore the argument, so passing it unconditionally is correct everywhere. That is why every sample here passes it.


.NET MAUI

One project, four platforms: Android, iOS, Mac Catalyst and Windows.

1. Create the project and add the packages

dotnet new maui -n HelloWebRtc
cd HelloWebRtc
dotnet add package WebRTCme.Middleware --version 26.9.21

2. Pin the MAUI package versions

Your restore will fail without this, and the error will not mention WebRTCme:

error NU1605: Detected package downgrade: Microsoft.Maui.Controls from 10.0.101 to 10.0.20

The packages depend on Microsoft.Maui.Controls 10.0.101. Your MAUI workload's implicit reference is whatever that workload bundles, which is usually lower, and NuGet calls the difference a downgrade and refuses. Name the versions yourself, exactly as NU1605 instructs:

<ItemGroup>
  <PackageReference Include="Microsoft.Maui.Controls" Version="10.0.101" />
  <PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="10.0.101" />
</ItemGroup>

This is not optional and it is not discoverable until the restore fails. Keep it in step when you upgrade WebRTCme — Releases states the version each release wants.

3. Windows: the Windows App Runtime

Skip this and your app hangs at startup with no error at all.

WebRTCme is a MAUI library on Windows, so it brings the Windows App SDK, which injects a bootstrapper into the module constructor of anything referencing it. If that bootstrapper cannot find a matching Windows App Runtime framework package it does not throw — it blocks. Your app stops before Main, having written nothing to any log.

Your development machine already has the runtime, because Visual Studio's MAUI workload installs it. A clean machine does not. The simplest fix is to carry it:

<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
  <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
</PropertyGroup>

A packaged (MSIX) app does not need this — the dependency is declared and the installer supplies it. An unpackaged app, which is what plain dotnet publish produces, does. The alternative is shipping WindowsAppRuntimeInstall.exe and asking users to run it.

Full story in Platform prerequisites.

4. Register the services

In MauiProgram.cs:

using WebRTCme;
using WebRTCme.Middleware;

public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();
    builder
        .UseMauiApp<App>()
        .ConfigureMauiHandlers(handlers =>
        {
            // Only needed if you use the middleware's video tile.
            handlers.AddHandler(typeof(Media), typeof(MediaHandler));
        });

    var middleware = CrossWebRtcMiddlewareMaui.Current;
    builder.Services.AddSingleton(middleware.WebRtc);
    builder.Services.AddSingleton(middleware);
    builder.Services.AddMauiMiddleware();

    return builder.Build();
}

Using WebRTCme alone? Drop the handler line and all three service registrations — CrossWebRtc.Current needs no container.

5. Ask for camera and microphone

Not needed for Hello world, which uses a data channel and touches no hardware — but needed the moment you call GetUserMedia.

Android, in Platforms/Android/AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

iOS and Mac Catalyst, in Platforms/iOS/Info.plist and Platforms/MacCatalyst/Info.plist:

<key>NSCameraUsageDescription</key>
<string>Allow video camera access</string>
<key>NSMicrophoneUsageDescription</key>
<string>Allow microphone access</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Local network access is required to reach the signalling server and connect calls.</string>

Check it worked

Restoring and compiling proves less than you would like. NuGet falls back: a net10.0-android project with no Android slice resolves lib/net10.0/ instead and compiles perfectly, because the common API is identical across slices. What it would not have is any Android binding — and nothing says so until the app runs on a phone and finds nothing behind the interface.

So the check that means something is a running one. Hello world negotiates a real peer connection in about sixty lines, with no signalling server, no camera and no network. If it prints a message, then on your platform the native half loaded, SDP marshalled both ways, ICE candidates survived the round trip, and DTLS and SCTP completed.

Where next

Hello world Prove it works, then turn it into a real call
The unified API What the whole surface looks like
Connection Let the middleware do the signalling — peer-to-peer or through an SFU
Demo apps Two complete apps, in the repository
Troubleshooting When one of the above did not go as written

Clone this wiki locally