HttpServer on Android to stream local video files to other devices

This blogs explains how to embed a mini HttpServer into your android app to serve local video files from your device to  other devices/desktop browsers/mediaplayers

You can do it by serving the local file using Java ServerSocket : server-side socket that waits for incoming client connections. A ServerSocket handles the requests and sends back an appropriate reply.

I made a demo App explaining this functionality, check out the code from Github (demo has a sample mp4, you can find it in assets folder. Move it your SD card  root folder to run the demo successfully)

device-2014-09-06-210137

LocalFileStreamingServer is a Thread which implements Runnable interface, you can create server object by passing the video file object from your activity.

 mServer = new LocalFileStreamingServer(new File(  
                     Environment.getExternalStorageDirectory() + "/jellies.mp4"));  

Then call init() method on Server with IP address of the device on Wifi

      /**  
       * Prepare the server to start.  
       *   
       * This only needs to be called once per instance. Once initialized, the  
       * server can be started and stopped as needed.  
       */  
      public String init(String ip) {  
           String url = null;  
           try {  
                InetAddress inet = InetAddress.getByName(ip);  
                byte[] bytes = inet.getAddress();  
                /*
                 * if port==0, os assigns a random port
                 */
                socket = new ServerSocket(port, 0, InetAddress.getByAddress(bytes));  
                socket.setSoTimeout(10000);  
                port = socket.getLocalPort();  
                url = "http://" + socket.getInetAddress().getHostAddress() + ":"  
                          + port;  
                Log.e(TAG, "Server started at " + url);  
           } catch (UnknownHostException e) {  
                Log.e(TAG, "Error UnknownHostException server", e);  
           } catch (IOException e) {  
                Log.e(TAG, "Error IOException server", e);  
           }  
           return url;  
      }  

Above code creates ServerSocket and assigns a port(os assigns a port if you port param as 0) while creating server socket object.

 Now call start method on Server, start method creates a thread with Runnable of Server instance.

 /**  
       * Start the server.  
       */  
      public void start() {  
           thread = new Thread(this);  
           thread.start();  
           isRunning = true;  
      }  

 Run() method has a while loop which runs till it stops, each loop tries to create client socket if any connections open.

 @Override  
      public void run() {  
           Log.e(TAG, "running");  
           while (isRunning) {  
                try {  
                     Socket client = socket.accept();  
                     if (client == null) {  
                          continue;  
                     }  
                     Log.e(TAG, "client connected at " + port);  
                     ExternalResourceDataSource data = new ExternalResourceDataSource(  
                               mMovieFile);  
                     Log.e(TAG, "processing request...");  
                     processRequest(data, client);  
                } catch (SocketTimeoutException e) {  
                     Log.e(TAG, "No client connected, waiting for client...", e);  
                     // Do nothing  
                } catch (IOException e) {  
                     Log.e(TAG, "Error connecting to client", e);  
                     // break;  
                }  
           }  
           Log.e(TAG, "Server interrupted or stopped. Shutting down.");  
      }  

 Server class has many other utility methods to parse headers and create response headers.

It also handles RANGE requests to server partial file content.

ProcessRequest() method takes care of writing file content (in bytes) to client socket

      /*  
       * Sends the HTTP response to the client, including headers (as applicable)  
       * and content.  
       */  
      private void processRequest(ExternalResourceDataSource dataSource,  
                Socket client) throws IllegalStateException, IOException {  
           if (dataSource == null) {  
                Log.e(TAG, "Invalid (null) resource.");  
                client.close();  
                return;  
           }  
           InputStream is = client.getInputStream();  
           final int bufsize = 8192;  
           byte[] buf = new byte[bufsize];  
           int splitbyte = 0;  
           int rlen = 0;  
           {  
                int read = is.read(buf, 0, bufsize);  
                while (read > 0) {  
                     rlen += read;  
                     splitbyte = findHeaderEnd(buf, rlen);  
                     if (splitbyte > 0)  
                          break;  
                     read = is.read(buf, rlen, bufsize - rlen);  
                }  
           }  
           // Create a BufferedReader for parsing the header.  
           ByteArrayInputStream hbis = new ByteArrayInputStream(buf, 0, rlen);  
           BufferedReader hin = new BufferedReader(new InputStreamReader(hbis));  
           Properties pre = new Properties();  
           Properties parms = new Properties();  
           Properties header = new Properties();  
           try {  
                decodeHeader(hin, pre, parms, header);  
           } catch (InterruptedException e1) {  
                Log.e(TAG, "Exception: " + e1.getMessage());  
                e1.printStackTrace();  
           }  
           for (Entry<Object, Object> e : header.entrySet()) {  
                Log.e(TAG, "Header: " + e.getKey() + " : " + e.getValue());  
           }  
           String range = header.getProperty("range");  
           cbSkip = 0;  
           seekRequest = false;  
           if (range != null) {  
                Log.e(TAG, "range is: " + range);  
                seekRequest = true;  
                range = range.substring(6);  
                int charPos = range.indexOf('-');  
                if (charPos > 0) {  
                     range = range.substring(0, charPos);  
                }  
                cbSkip = Long.parseLong(range);  
                Log.e(TAG, "range found!! " + cbSkip);  
           }  
           String headers = "";  
           // Log.e(TAG, "is seek request: " + seekRequest);  
           if (seekRequest) {// It is a seek or skip request if there's a Range  
                // header  
                headers += "HTTP/1.1 206 Partial Content\r\n";  
                headers += "Content-Type: " + dataSource.getContentType() + "\r\n";  
                headers += "Accept-Ranges: bytes\r\n";  
                headers += "Content-Length: " + dataSource.getContentLength(false)  
                          + "\r\n";  
                headers += "Content-Range: bytes " + cbSkip + "-"  
                          + dataSource.getContentLength(true) + "/*\r\n";  
                headers += "\r\n";  
           } else {  
                headers += "HTTP/1.1 200 OK\r\n";  
                headers += "Content-Type: " + dataSource.getContentType() + "\r\n";  
                headers += "Accept-Ranges: bytes\r\n";  
                headers += "Content-Length: " + dataSource.getContentLength(false)  
                          + "\r\n";  
                headers += "\r\n";  
           }  
           InputStream data = null;  
           try {  
                data = dataSource.createInputStream();  
                byte[] buffer = headers.getBytes();  
                Log.e(TAG, "writing to client");  
                client.getOutputStream().write(buffer, 0, buffer.length);  
                // Start sending content.  
                byte[] buff = new byte[1024 * 50];  
                Log.e(TAG, "No of bytes skipped: " + data.skip(cbSkip));  
                int cbSentThisBatch = 0;  
                while (isRunning) {  
                     int cbRead = data.read(buff, 0, buff.length);  
                     if (cbRead == -1) {  
                          Log.e(TAG,  
                                    "readybytes are -1 and this is simulate streaming, close the ips and crate anotber ");  
                          data.close();  
                          data = dataSource.createInputStream();  
                          cbRead = data.read(buff, 0, buff.length);  
                          if (cbRead == -1) {  
                               Log.e(TAG, "error in reading bytess**********");  
                               throw new IOException(  
                                         "Error re-opening data source for looping.");  
                          }  
                     }  
                     client.getOutputStream().write(buff, 0, cbRead);  
                     client.getOutputStream().flush();  
                     cbSkip += cbRead;  
                     cbSentThisBatch += cbRead;  
                }  
                Log.e(TAG, "cbSentThisBatch: " + cbSentThisBatch);  
                // If we did nothing this batch, block for a second  
                if (cbSentThisBatch == 0) {  
                     Log.e(TAG, "Blocking until more data appears");  
                     Thread.sleep(1000);  
                }  
           } catch (SocketException e) {  
                // Ignore when the client breaks connection  
                Log.e(TAG, "Ignoring " + e.getMessage());  
           } catch (IOException e) {  
                Log.e(TAG, "Error getting content stream.", e);  
           } catch (Exception e) {  
                Log.e(TAG, "Error streaming file content.", e);  
           } finally {  
                if (data != null) {  
                     data.close();  
                }  
                client.close();  
           }  
      }  

 Last thing to remember is to get IP of the device, below code helps to fetch IP in Android:

           WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE);  
           String deviceIp = Formatter.formatIpAddress(wm.getConnectionInfo()  
                     .getIpAddress());  

Pass the IP to init() of server and start it. Once you get it started successfully , you can query server to get URL using getFileUrl()

Use the URL on other devices Browsers or mediaplayers to stream the video

I have joined above bits and pieces and made a demo app. Checkout now on Github from here.

***Dont forget to copy jellies.mp4 from assets folder to your SD card root folder

Advertisements

15 thoughts on “HttpServer on Android to stream local video files to other devices

    • Hi Vishal, you can use the method ‘getFileUrl()’ on Device A to get the URL which can be used by any client in same network. In your case if you want to stream to another Android device, just open any media player applications on device B and enter URL you get from Device A’s server app. Demo app hosted on Github will print the URL by default. NOTE: this code works only if devices are under same wifi

      Like

  1. saya says:

    Thanks for your codes. Can these codes support local streaming? I modified streamingActivity layout to add button and surfaceView such that I can set the url to mediaplayer. However, it is fail to play the video.

    Like

    • Hi Saya,
      If you are using demo app, make sure you copy video file form assets to your SD card root. And when server is started, try url from browser. If it plays in browser, then it shouldnt be a problem on you mediaplayer as well. Let me know

      Like

  2. silver says:

    Hi srihari,
    I tried to stream the file on vlc from my mobile device but the video plays only for 1 or 2 secs and it stops, even when i try to seek the video ahead of 1 or 2 secs the video resets. But this works on a client app that i have written and deployed on the same device. Can you help me out on this and I also noticed that the video is not buffering but it is fully sent on to the client side and the client has to wait till the complete video packets are sent.
    Thanx for your tutorial it is of great help.

    Like

  3. Vaibhav Jain says:

    Hi SriHary,

    I am able to play this video file over the browser , but when i try to play it on VLC , then says sockettimeout every time . Please help in resolving this .

    Like

  4. Vaibhav Jain says:

    Hi ,

    I am able to play video on browser , but when tried on VLC , then it says lib SocketTimeOutException due to libcore.io.ErrnoException . Please help me out,

    Like

  5. Vaibhav Jain says:

    Hi ,

    I need to play multiple files at a time on browser through http server . Please suggest . Thanks In advance.

    Vaibhav Jain

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s