Android Question I need help on UPnP Subscription Service

Discussion in 'Android Questions' started by GuyBooth, Mar 19, 2015.

  1. GuyBooth

    GuyBooth Active Member Licensed User

    I'm writing a control point for streaming from a NAS to an Audio player. I have succeeded in writing the code for loading and playing tracks, stopping and starting the player, but I don't understand how to handle the "Subscription Service" that is required for receiving events (presumably these will tell the Control Point when a track has finished playing or the player has stopped, for example).
    The format for playing etc in b4a is, for example:
    Code:
    Dim UPnPReq As HttpJob
        UPnPReq.Initialize(
    "AVTAction " & CodeName, Me)
        UPnPReq.PostString(ActionURL, Action)
        UPnPReq.GetRequest.SetHeader(
    "SOAPAction", sSOAPHeader)
        UPnPReq.GetRequest.SetContentEncoding(
    "utf-8")
        UPnPReq.GetRequest.SetContentType(
    "text/xml; charset=""utf-8""")
        UPnPReq.GetRequest.Timeout = 
    5000
    I have seen documents that refer to the following:

    Code:
    SUBSCRIBE publisher path HTTP/1.1
    HOST: publisher host:publisher port
    CALLBACK:
    NT: upnp:
    event
    TIMEOUT: Second-requested subscription duration
    Are these headers? How to I write this in b4a?
    How do I listen for the events? Is there something special I have to set up in http2utils?
    Do I use the word "SUBSCRIBE" in place of "POST", or is it totally different?

    I'm pretty proud of myself for having made it this far with the UPnP coding, but now feeling lost again!
    Any help would be appreciated.
     
  2. Erel

    Erel Administrator Staff Member Licensed User

    This is not a http message. There is no such method in HTTP.

    You will need to use a standard socket with AsyncStreams and send this text.
     
  3. RandomCoder

    RandomCoder Well-Known Member Licensed User

    @GuyBooth sorry for the late reply. I too have delved into UPNP control with B4A, although my application is somewhat simpler in that I only browser UPNP devices and play content locally on the device. My source code can be found here.... UPNP Browser - Source Files Included
    The app is also published on the play store here... https://play.google.com/store/apps/details?id=b4a.upnpBrowser&hl=en_GB (it has close to 800 downloads and a 30% retention rate. Only 2 ANR's to date both linked to obtaining device information that wasn't present - I think!)

    Anyhow, with regard to your question. I think that to subscribe to events you just need to repeat the process, or at least a similar process to what you used when requesting all media devices to respond to a search request.
    Here's the section of code I used to find devices...
    Code:
    'Send a multicast message to search for devices of device type defined by 'st'
    'mx - Maximum wait time in seconds
    'st - Search target as defined by the UPNP device architecture
    '
    'Example: <code>
    'M_Search(3, "ssdp:all")</code>
    Private Sub M_Search(mx As Int, st As String)
        
    ' Compile HTTP broadcast message
        Dim msg As String       
        msg = 
    "M-SEARCH * HTTP/1.1~" & _
              
    "HOST: 239.255.255.250:1900~" & _
              
    "MAN: " & Chr(34) & "ssdp:discover" & Chr(34) & "~" & _
              
    "MX: " & mx & "~" & _
              
    "ST: " & st & "~~"
        
    ' Replace "~" with "\r\n" [NOTE:- CRLF only sends Chr(13) eg."\r"]
        msg = msg.Replace("~"Chr(13) & Chr(10))
        Debug.debugLog(msg) 
    'DEBUG
        ' Convert string to UTF8
        Dim data() As Byte
        data = msg.GetBytes(
    "UTF8")
        
    ' Initialise packet and send
        Dim packet As UDPPacket
        packet.Initialize(data, 
    "239.255.255.250"1900)
        udpSocket1.Send(packet)
    End Sub

    Private Sub UDP_PacketArrived (Packet As UDPPacket)
        
    ' Restart 3s timer each time a new packet arrives
        EOT_Delay_Reset(3000)
        
    ' Convert received data to UTF8
        serverResponse = BytesToString(Packet.Data, Packet.Offset, Packet.Length, "UTF8")
        Debug.debugLog(
    CRLF & serverResponse) 'DEBUG
        ' Extract device uuid and description file url
        Get_Details
    End Sub
    I also found the attached document particularly useful (you've probably already read it?)

    kind regards,
    RandomCoder
     

    Attached Files:

  4. GuyBooth

    GuyBooth Active Member Licensed User

    RandomCoder,

    I actually found your code a couple of weeks ago - thank heavens! It put me on the right track to get started. I built on it, and had to deal with a few anomolies (it worked fine for my servers, but the XML parser choked on my Renderer, so I built a new parser using SAX parsing which worked fine). Thank you for publishing that code on the b4a forums!

    I actually have my app working as far as choosing a file to play, loading it into the Renderer and playing it, and loading the Next file into the renderer so it will automatically follow on. The trick now is "Eventing", receiving events from the renderer so I know when a track has finished playing and I can load a new track into the "NextURL" on the renderer. For this there is a special UPnP approach, and this is the one I am trying to implement now. Since my HTTP and XML skills are weak, and UPnP is so complex, I am struggling to understand exactly what message I need to send to start the "Subscription" service they use for this. It isn't the same as repeating the search request, and requires giving the renderer information so it can broadcast event messages to the Control Point. The best information I have found so far is in the Sonos Forums but they are working in C (I don't speak C :() and I don't know enough about HTTP headers to completely follow what they are saying.

    To me, the message I have to send (see the start of this thread) is ALL header (in the discussion they actually state there is no "Body") - but am I missing something at the very beginning? Is it in fact a POST message (sorry if my terminology is all wrong)? Yesterday I succeeded in sending the message on an Async Stream but the reply that came back was "412 - Precondition Failed".

    My next step may be to try to sniff another control points messages, but I need to solve a problem I have with a local server (that one's on another thread in this forum).
    Guy
     
  5. RandomCoder

    RandomCoder Well-Known Member Licensed User

    I'm glad to hear it's helped someone, that was my intention. I'm sure there are better ways of programming it as I'm only really an amateur/hobbiest but hopefully its not too far off the mark.

    I can fully sympathise with you, it was a first for me to play with networking protocols in B4A and I also knew nothing about UPNP other than it allowed streaming content over a network.

    I think that you have misunderstood what I previously said. I was only suggesting that the approach is similar to what you previously did to search for devices. At least that's how I have interpreted it. Check page 60 of the document I uploaded, it has plenty of detail about eventing. It states that you need to send the following network message...
    Code:
    SUBSCRIBE publisher path HTTP/1.1
    HOST: publisher host:publisher port
    CALLBACK: <delivery URL>
    NT: upnp:
    event
    TIMEOUT: Second-requested subscription duration
    I too had to do a little WireShark'ing, one of the problems that I stumbled upon is that CRLF does not actually send a Carriage Return and a Line Feed, you only get the Carriage Return and so I had to send Chr(13) & Chr(10) for my search request to be accepted by all devices (some would respond but other wouldn't when the Line Feed was missing).
    Another trick I used a lot was to convert all result strings to LowerCase because of the subtle differences between devices which kept breaking my code. It is certainly a challenge getting your head around UPNP protocols but I also think its highly rewarding once you get it to work. If you need further help just let me know, maybe I could attempt to add UPNP control and Eventing to my App. I've no idea how long it will take though as I only do a little coding each evening alongside checking the latest Forum posts.

    Good luck,
    RandomCoder ;)
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice