This is a follow-on from my previous post around taking small steps with webRTC and UWP.
At the end of that post, I had some scrappy code which was fairly fixed in function in that it was a small UWP app which would use the UWP webRTC library to connect to a signalling service and then could begin a conversation with a peer that was also connected to the same signalling service.
The signalling service in question had to be the one provided with the UWP webRTC bits and the easiest way to test that my app was doing something was to run it against the PeerCC sample which also ships with the UWP webRTC bits and does way more than my app does by demonstrating lots of functionality that’s present in UWP webRTC.
The links to all the webRTC pieces that I’m referring to are in the previous 2 posts on this topic.
Tidying Up
The code that I had in the signalling branch of this github repo at the end of the previous post was quite messy and not really in a position to be re-used and so I spent a little time just pulling that code apart, refactoring some of the functionality behind interfaces and reducing the implicit dependencies in order to try and move the code towards being a little bit more re-usable (even if the functionality it currently implements isn’t of much actual use to a real user – I’m just experimenting).
What I was trying to move towards was some code that I knew sort of worked in this XAML based UWP app that I could then lift out of the app and re-use in a non-XAML based UWP app (i.e. a Unity app) so that I would have some control over the knowns and unknowns in trying out that process.
What I needed to do then was make sure that in refactoring things, I ended up with code that was clearly abstracted from its dependencies on anything in the XAML layer.
Firstly, I refactored the solution into two projects to make for a class library and an app project which referenced it;
and then I took some of the pieces of functionality that I had in there and abstracted it out into a set of interfaces;
with a view to making the dependencies between these interfaces explicit and the implementation pluggable.
This included putting the code which provides signalling by invoking the signalling service supplied with the original sample behind an interface. Note that I’m not at all trying to come up with a generic interface that could generally represent the notion of signalling in webRTC but, instead, I’m just trying to put an interface on to the existing signalling code that I took (almost) entirely from the PeerCC sample project in the UWP webRTC bits.
The other interfaces/services that I added here are hopefully named ‘reasonably well’ in terms of the functionality that they represent with perhaps the one that’s not quite so obvious obvious being the IConversationManager.
This interface is just my attempt to codify the minimum functionality that I need to bring the other interface implementations together in order to get any kind of conversation over webRTC up and running from my little sample app as it stands and that IConversationManager interface right now just looks as below;
and so the idea here is that a consumer of an IConversationManager can simply;
- Tell the manager whether it is meant to initiate conversations or simply wait for a remote peer to being a conversation with it
- In terms of initiating conversations – the code is ‘aggressive’ in that it simply finds the first peer that it sees provided by the signalling service and attempts to being a conversation with it.
- Call InitialiseAsync providing the name that the local peer wants to be represented by.
- Call ConnectToSignallingAsync with the IP Address and port where the signalling service is to be found.
From there, the implementation jumps in and tries to bring together all the right pieces to get a conversation flowing.
In making these abstractions, I found two places where I had to apply a little bit of thought and that was where;
- The UWP webRTC pieces need initialising with a Dispatcher object and so I abstracted that out into an interface so that an implementation can be injected into the underlying layer.
- There is a need at some point to do some work with UI objects to represent media streams. In the code to date, this has meant working with XAML MediaElements but in other scenarios (e.g. Unity UI) that wouldn’t work.
In order to try and abstract the library code from these media pieces, I made an IMediaManager interface with the intention being to write a different implementation for the different UI layers so to bring this library up inside of a Unity app I’d at least need to provide a Unity version of the highlighted implementation pieces below which are about IMediaManager in a XAML UI world;
My main project took a dependency on autofac to provide a container from which to serve up the implementations of my interfaces and I did a cheap trick of providing my own “container” embedded into the library and named CheapContainer in case the library was going to be used in a situation where autofac or some other IoC container wasn’t immediately available.
Configuration of the container then moves into my App.xaml.cs file and is fairly simple and I wrote it twice, once for autofac and once using my own CheapContainer;
#if !USE_CHEAP_CONTAINER Autofac.IContainer Container { get { if (this.iocContainer == null) { this.BuildContainer(); } return (this.iocContainer); } } #endif void BuildContainer() { #if USE_CHEAP_CONTAINER CheapContainer.Register<ISignallingService, Signaller>(); CheapContainer.Register<IDispatcherProvider, XamlMediaElementProvider>(); CheapContainer.Register<IXamlMediaElementProvider, XamlMediaElementProvider>(); CheapContainer.Register<IMediaManager, XamlMediaElementMediaManager>(); CheapContainer.Register<IPeerManager, PeerManager>(); CheapContainer.Register<IConversationManager, ConversationManager>(); #else var builder = new ContainerBuilder(); builder.RegisterType<Signaller>().As<ISignallingService>().SingleInstance(); builder.RegisterType<XamlMediaElementProvider>().As<IXamlMediaElementProvider>().As<IDispatcherProvider>().SingleInstance(); builder.RegisterType<XamlMediaElementMediaManager>().As<IMediaManager>().SingleInstance(); builder.RegisterType<PeerManager>().As<IPeerManager>().SingleInstance(); builder.RegisterType<ConversationManager>().As<IConversationManager>().SingleInstance(); builder.RegisterType<MainPage>().AsSelf().SingleInstance(); this.iocContainer = builder.Build(); #endif } #if USE_CHEAP_CONTAINER #else Autofac.IContainer iocContainer; #endif
and the code which now lives inside of my MainPage.xaml.cs file involved in actually getting the webRTC conversation up and running is reduced down to almost nothing;
async void OnConnectToSignallingAsync() { await this.conversationManager.InitialiseAsync(this.addressDetails.HostName); this.conversationManager.IsInitiator = this.isInitiator; this.HasConnected = await this.conversationManager.ConnectToSignallingAsync( this.addressDetails.IPAddress, this.addressDetails.Port); }
and so that seems a lot simpler, neater and more re-usable than what I’d had at the end of the previous blog post.
In subsequent posts, I’m going to see if I can now re-use this library inside of other environments (e.g. Unity) so as to bring this same (very limited) webRTC functionality that I’ve been playing with to that environment.
Pingback: Rough Notes on UWP and webRTC (Part 4–Adding some Unity and a little HoloLens) – Mike Taulty