milleph milleph - 9 months ago 123
Android Question

NanoHTTPD - convert https stream to http

To overcome Chromecast's restriction on streaming from self-certificated https servers (in my case the Subsonic music server) I'm utilizing an instance of the NanoHTTPD server already running as part of my Android app. The idea is to stream from the Subsonic server (SSL) and connect that stream to a new stream (non SSL) for the NanoHTTP.Response back to Chromecast. I have the InputStream working from the Subsonic server (which plays through the MediaPlayer) but don't know how to re-stream unencrypted for the following call:

new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, "audio/mpeg", newStream);


So in a nutshell, how do I convert an https encrypted audio stream to a decrypted audio stream on the fly?

Answer Source

Ok, managed to get all of this working. Subsonic server using https, accessible from Android and Chromecast using the server's self-signed certificate. If https is on then both Android and Chromecast use a nanohttpd proxy server running on the Android client to stream to the Android MediaPlayer and the html5 audio element respectively. The serve function override of the nanohttpd server running on Android contains the following code:-

int filesize = .... obtained from Subsonic server for the track to be played

// establish the https connection using the self-signed certificate
// placed in the Android assets folder (code not shown here)
HttpURLConnection con = _getConnection(subsonic_url,"subsonic.cer")

// Establish the InputStream from the Subsonic server and
// the Piped Streams for re-serving the unencrypted data 
// back to the requestor
final InputStream is = con.getInputStream();
PipedInputStream sink = new PipedInputStream();
final PipedOutputStream source = new PipedOutputStream(sink);

// On a separate thread, read from Subsonic and write to the pipe 
Thread t = new Thread(new Runnable() {
                public void run () {
                    try {
                        byte[] b = new byte[1024];
                        int len;
                        while ((len = is.read(b,0,1024)) != -1)
                            source.write(b, 0, len);
                        source.flush();
                    }
                    catch (IOException e) {
                    }
                }});

t.start();
sleep(200); // just to let the PipedOutputStream start up

// Return the PipedInputStream to the requestor.
// Important to have the filesize argument
return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, 
                              "audio/mpeg", sink, filesize);

I found that streaming flac files with mp3 transcoding on gave me the flac filesize but of course the mp3 stream. This proved difficult to handle for the html5 audio element so I reverted to adding &format=raw to the Subsonic api call for the stream. So regardless of user's configuration over https I stream raw format and it all seems to be working well so far in testing.