Skip to main content
Headliner II
January 4, 2013

Fully managed C# library

Hi, I have been working on a simplified UPnP library which is aimed at Sonos that I want to make available to others.

You can find the source-code here:

https://github.com/jishi/Jishi.Intel.SonosUPnP

This is based upon Intels UPnP framework, which is opposed to the COM object that other attempts that I have found, based completely on managed code (no unmanaged code). This makes a huge difference if you are building anything multithreaded (WinForms, WPF etc).

I will try to put up some example code for people to look at.

Right now some of the features are pretty hacky since I'm still learning best practices, but hopefully it simplifies it a bit for someone.

This is built for .NET 4 Client Profile. The requirements is only .NET 4, previous versions doesn't need to exist.

Basic usage is this:

code:

// Create the main object
var discovery = new SonosDiscovery();

// Attach an event handler for topologychanged, this will be triggered as soon as it has found players (and when topology change)
discovery.TopologyChanged += TopologyChanged;

// This will start the UPnP-scanning, this is a manual call so that you are able to setup all event handlers first
discovery.StartScan();


If you want a more descriptive example, you can check out my Dashboard project:

https://github.com/jishi/Panagora.Sonos.Dashboard/blob/master/Panagora.Sonos.Dashboard/MainWindow.xaml.cs

Is a really simple fullscreen app that shows currently playing song for the most relevant player (Biggest group, longest queue).

Any suggestions and feedback is appreciated. As of today it only supports the following features:

Zone discovery
Player status notifications
Play/Pause
Enqueue
Seek (to queue position)
SetAVTransportURI
    This topic has been closed for further comments. You can use the search bar to find a similar topic, or create a new one by clicking Create Topic at the top of the page.

    26 replies

    Trending Lyricist I
    March 20, 2013
    the 600 is the subscription timeout, however it should re-subscribe after 10 minutes so that shouldn't be a problem. I'm running the same library on a screen here at work and it usually manage through a whole day without loosing the events.

    However, the other problem (where the player kills all subscriptions for a certain IP) has no real workaround without rewriting the library, since my library utilizes unique URLs for each subscription. The official client reuses the same URL, so a re-subscription would "cancel" the subscription kills.

    I have thought about rewriting it from scratch and not rely on a separate UPnP lib, but that would probably change the API drastically. I actually started out that way, but got into trouble with the subscriptions and decided to pass on it. However, since I started with my node.js API I have a better understanding on how it works and it might be easier to implement it this time around.


    Thanks for the info. I suppose the question, then, is how do I properly unsubscribe when my app exits? Is there a public function that unsubscribes or do I just need to properly dispose of the player (or some other) object?
    jishiAuthor
    Headliner II
    March 21, 2013
    Thanks for the info. I suppose the question, then, is how do I properly unsubscribe when my app exits? Is there a public function that unsubscribes or do I just need to properly dispose of the player (or some other) object?

    Well, you can't 🙂 I couldn't figure out how to reference the actual subscription for unsubcribing to it. I planned on using a IDisposable implementation and a finalizer to prevent this, but still haven't figured out how to do it.

    I didn't have the need for it personally, so I never bothered. I can look at it once again.
    Lyricist II
    July 29, 2013
    Hi Jishi,

    Appreciate your work on this library - no chance I would have been able to get anything done at my end without it.

    I'm having a bit of an issue though - my simple app needs to keep track of two separate players. If I monitor one player, it seems to work fine, but as soon as I'm monitoring two, I get wierdness (empty TransportState and CurrentTrackMetaData on both channels)

    Is there any tricks to getting it working against multiple controllers - I notice your sample app only connects to a single one?

    Thanks

    Kane
    jishiAuthor
    Headliner II
    July 29, 2013
    Hi Jishi,

    Appreciate your work on this library - no chance I would have been able to get anything done at my end without it.

    I'm having a bit of an issue though - my simple app needs to keep track of two separate players. If I monitor one player, it seems to work fine, but as soon as I'm monitoring two, I get wierdness (empty TransportState and CurrentTrackMetaData on both channels)

    Is there any tricks to getting it working against multiple controllers - I notice your sample app only connects to a single one?

    Thanks

    Kane


    Hi, I'm not sure about the issue you are having. When connecting to the system, it should subscribe to all players and also recieve notifications from all players, which means that it should keep state of all of them.

    Could you explain what you are doing in depth?
    Lyricist II
    July 31, 2013
    Hi, I'm not sure about the issue you are having. When connecting to the system, it should subscribe to all players and also recieve notifications from all players, which means that it should keep state of all of them.

    Could you explain what you are doing in depth?


    On further investigation, I think it's possible that some of my problems may be due to something being wrong with one of my devices - it drops off every now and then, and I am thinking my issues may have coincided with when it dropped off.

    Have a support request in on this, so will hopefully get some clarity (just need to get it to drop out now, as all of a sudden it seems to be behaving!), and then once that's sorted I'll do more testing on my software side of things.

    Thanks!
    Trending Lyricist I
    March 21, 2014
    Jishi-

    I can't thank you enough for all of the hard work you put into this library. I have been using it for the past 8 months or so with mostly good results. I have it integrated into a much more extensive server app that I wrote that controls various things in my house, including my Sonos.

    I spent a lot of time debugging performance issues with my server app and have isolated the issue to this Sonos library.

    I started a new project that ONLY imports the library and does the following:
    sonosDiscoverer = New SonosDiscovery
    sonosDiscoverer.StartScan()

    I have noticed that my app continually receives a large amount of UDP data... it seems like it is constantly querying the other UPNP devices on my network. I dug into your Sonos library code a little bit (although I know very little about UPNP or C#) to try to figure out a way to "stop" the scan in the "onDeviceAdded" callback for the UPnPSmartControlPoint ("ControlPoint").

    I couldn't figure out a way to do it. Is there some way to make it STOP scanning for new devices after it finds my Sonos player? Am I misunderstanding what it is doing? When I view the network traffic from the process using procmon I see a huge amount of UDP traffic from other UPNP devices on my network (Windows Media Center and Extenders). I'm running this on an atom CPU and it causes the app to use between 10-25% of the CPU. Functionally, it works well, but it bogs down the CPU way too much.

    Can you help me out or point me in the right direction?

    Here's a representative example of a few of the procmon entries:

    code:
    9:24:09.2769313 PM	HomeControllerServer2.vshost.exe	20136	TCP Send	Hauto.home:62893 -> MediaCenter:icslap	SUCCESS	Length: 128, startime: 790903, endtime: 790903, seqnum: 0, connid: 0
    9:24:09.2769563 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62893 -> MediaCenter:icslap SUCCESS Length: 190, seqnum: 0, connid: 0
    9:24:09.2769648 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62893 -> MediaCenter:icslap SUCCESS Length: 1440, seqnum: 0, connid: 0
    9:24:09.2772874 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62893 -> MediaCenter:icslap SUCCESS Length: 1440, seqnum: 0, connid: 0
    9:24:09.2773004 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62893 -> MediaCenter:icslap SUCCESS Length: 99, seqnum: 0, connid: 0
    9:24:09.2773226 PM HomeControllerServer2.vshost.exe 20136 TCP Send Hauto.home:62894 -> MediaCenter:icslap SUCCESS Length: 128, startime: 790903, endtime: 790903, seqnum: 0, connid: 0
    9:24:09.2773295 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62894 -> MediaCenter:icslap SUCCESS Length: 205, seqnum: 0, connid: 0
    9:24:09.2773465 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62894 -> MediaCenter:icslap SUCCESS Length: 1440, seqnum: 0, connid: 0
    9:24:09.2776708 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62894 -> MediaCenter:icslap SUCCESS Length: 1440, seqnum: 0, connid: 0
    9:24:09.2776907 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62894 -> MediaCenter:icslap SUCCESS Length: 1440, seqnum: 0, connid: 0
    9:24:09.2776975 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62894 -> MediaCenter:icslap SUCCESS Length: 1440, seqnum: 0, connid: 0
    9:24:09.2779922 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62894 -> MediaCenter:icslap SUCCESS Length: 603, seqnum: 0, connid: 0
    9:24:09.3090135 PM HomeControllerServer2.vshost.exe 20136 UDP Receive Hauto.home:56296 -> HDHR-1320159B.home:ssdp SUCCESS Length: 278, seqnum: 0, connid: 0
    9:24:09.3111922 PM HomeControllerServer2.vshost.exe 20136 UDP Receive Hauto.home:56296 -> HDHR-13201348.home:ssdp SUCCESS Length: 278, seqnum: 0, connid: 0
    9:24:09.3139335 PM HomeControllerServer2.vshost.exe 20136 TCP Connect Hauto.home:62895 -> DEL0015995DF2AA:http SUCCESS Length: 0, mss: 1440, sackopt: 1, tsopt: 0, wsopt: 1, rcvwin: 66240, rcvwinscale: 8, sndwinscale: 0, seqnum: 0, connid: 0
    9:24:09.3174199 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62879 -> 0005CD35CE19.home:8080 SUCCESS Length: 817, seqnum: 0, connid: 0
    9:24:09.3182590 PM HomeControllerServer2.vshost.exe 20136 TCP Send Hauto.home:62895 -> DEL0015995DF2AA:http SUCCESS Length: 76, startime: 790903, endtime: 790903, seqnum: 0, connid: 0
    9:24:09.3233376 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62895 -> DEL0015995DF2AA:http SUCCESS Length: 24, seqnum: 0, connid: 0
    9:24:09.3242665 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62895 -> DEL0015995DF2AA:http SUCCESS Length: 354, seqnum: 0, connid: 0
    9:24:09.3250879 PM HomeControllerServer2.vshost.exe 20136 TCP Disconnect Hauto.home:62895 -> DEL0015995DF2AA:http SUCCESS Length: 0, seqnum: 0, connid: 0
    9:24:09.4026198 PM HomeControllerServer2.vshost.exe 20136 UDP Receive Hauto.home:56296 -> MediaCenter.home:ssdp SUCCESS Length: 397, seqnum: 0, connid: 0
    9:24:09.4094544 PM HomeControllerServer2.vshost.exe 20136 TCP Connect Hauto.home:62896 -> MediaCenter.home:icslap SUCCESS Length: 0, mss: 1460, sackopt: 1, tsopt: 0, wsopt: 1, rcvwin: 65700, rcvwinscale: 8, sndwinscale: 8, seqnum: 0, connid: 0
    9:24:09.4103316 PM HomeControllerServer2.vshost.exe 20136 TCP Send Hauto.home:62896 -> MediaCenter.home:icslap SUCCESS Length: 139, startime: 790904, endtime: 790904, seqnum: 0, connid: 0
    9:24:09.4103578 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62896 -> MediaCenter.home:icslap SUCCESS Length: 205, seqnum: 0, connid: 0
    9:24:09.4105961 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62896 -> MediaCenter.home:icslap SUCCESS Length: 1460, seqnum: 0, connid: 0
    9:24:09.4106268 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62896 -> MediaCenter.home:icslap SUCCESS Length: 2395, seqnum: 0, connid: 0
    9:24:09.5751703 PM HomeControllerServer2.vshost.exe 20136 UDP Receive Hauto.home:56296 -> HDHR-13201348.home:ssdp SUCCESS Length: 278, seqnum: 0, connid: 0
    9:24:09.5769604 PM HomeControllerServer2.vshost.exe 20136 TCP Connect Hauto.home:62897 -> HDHR-13201348.home:http SUCCESS Length: 0, mss: 1460, sackopt: 0, tsopt: 0, wsopt: 1, rcvwin: 65700, rcvwinscale: 8, sndwinscale: 0, seqnum: 0, connid: 0
    9:24:09.5775259 PM HomeControllerServer2.vshost.exe 20136 TCP Send Hauto.home:62897 -> HDHR-13201348.home:http SUCCESS Length: 53, startime: 790906, endtime: 790906, seqnum: 0, connid: 0
    9:24:09.5775532 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62897 -> HDHR-13201348.home:http SUCCESS Length: 214, seqnum: 0, connid: 0
    9:24:09.5781084 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62897 -> HDHR-13201348.home:http SUCCESS Length: 312, seqnum: 0, connid: 0
    9:24:09.5781226 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62897 -> HDHR-13201348.home:http SUCCESS Length: 19, seqnum: 0, connid: 0
    9:24:09.5782420 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62897 -> HDHR-13201348.home:http SUCCESS Length: 257, seqnum: 0, connid: 0
    9:24:09.5783046 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62897 -> HDHR-13201348.home:http SUCCESS Length: 94, seqnum: 0, connid: 0
    jishiAuthor
    Headliner II
    March 21, 2014
    Hi tad.

    Unfortunately it is the underlying intel SDK that is responsible for this kind of traffic. I'm kind of surprised that it generates that much traffic, because scanning the network for new devices shouldn't be happening that often.

    You could try and update to the latest upnp_av.dll from here: http://opentools.homeip.net/dev-tools-for-upnp

    There has been a few updates since I built this, maybe it is something they have addressed.

    I have also resumed some work that I did before I based it on the intel SDK. Since it is possible to build a more lightweight implementation if only taking Sonos into account, I wanted to use this approach when building my StreamToSonos implementation. It will not be a compatible replacement, but hopefully easy enough to switch over to if needed. You can check it out here (it doesn't have it's own repo yet): https://github.com/jishi/Jishi.StreamToSonos
    Trending Lyricist I
    March 21, 2014
    Hi tad.

    Unfortunately it is the underlying intel SDK that is responsible for this kind of traffic. I'm kind of surprised that it generates that much traffic, because scanning the network for new devices shouldn't be happening that often.

    You could try and update to the latest upnp_av.dll from here: http://opentools.homeip.net/dev-tools-for-upnp

    There has been a few updates since I built this, maybe it is something they have addressed.

    I have also resumed some work that I did before I based it on the intel SDK. Since it is possible to build a more lightweight implementation if only taking Sonos into account, I wanted to use this approach when building my StreamToSonos implementation. It will not be a compatible replacement, but hopefully easy enough to switch over to if needed. You can check it out here (it doesn't have it's own repo yet): https://github.com/jishi/Jishi.StreamToSonos


    Jishi-

    I was afraid of something like that. What's weird is that this traffic continues throughout the duration that my program is running. There's no way to get it to stop scanning? It looks like it's getting a ton of traffic from my network-connected TV tuners.

    I tried updating to the latest build of the upnp_av.dll as you suggested, but that didn't help with the issue.

    I may try switching over to the implementation you used in StreamToSonos, I just hate to move away from something that is otherwise working so well!
    jishiAuthor
    Headliner II
    March 23, 2014
    Do note that the framework that is used for StreamToSonos lacks a lot of features. I only implemented the bare minimum required to make the streaming work. Although, the main stuff is there, so it is more a matter of adding the correct SOAP calls to support more features.
    Trending Lyricist I
    March 26, 2014
    Do note that the framework that is used for StreamToSonos lacks a lot of features. I only implemented the bare minimum required to make the streaming work. Although, the main stuff is there, so it is more a matter of adding the correct SOAP calls to support more features.

    I see that. I'm afraid that migrating this over to do what I need is a little beyond my comprehension. It doesn't look like you implement the callbacks to get Sonos device status (now playing metadata) like you do in the managed library - is that correct?

    I've done a bunch more investigating with the Intel UPNP tools - I get the same symptoms when I run any of their tools. CPU usage shoots up to 30-50%, approximately 100MB of ram used, and around 3000-4000 network events per minute shown in procmon. I'd be very interested in knowing whether other people are seeing similar behavior. Is this just the nature of UPNP? Or is there something wrong with the UPNP devices on my network (about a dozen).

    There has to be a more lightweight-way to talk to Sonos. Is it possible to feed the UPNP library the IP of the Sonos unit and avoid all of the UPNP scanning? At the heart of it, isn't it just SOAP requests to issue commands to the Sonos and HTTP callbacks made from the Sonos unit back to the app?

    Edit:

    The plot thickens. I'm not sure what all of the "network activity" in procmon is actually showing. When I fire up Wireshark, I see what I think is a perfectly reasonable amount of SSDP and UDP traffic for a network with a dozen UPNP devices - a SSDP notify packet every second or so for each device and about the same for UDP packets. There are a bunch of HTTP packets when I first launch the app and do a .startScan, but these look very normal as well.

    So, I really have no idea what is causing the upnp library to consume so much CPU and launch so many threads.