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)
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
Thank you for this Post… I had a query. I want to stream contents from Android-Device A to Android-Device B. Device A will hold your code which you have explained. What should Device B have?
LikeLiked by 1 person
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
LikeLike
Hi srihary… Thanks for the quick reply… I will try the same and let you know…
LikeLike
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.
LikeLike
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
LikeLike
It’s very nice. It works.
But I want to play live video from camera. Do you know how to do it?
LikeLiked by 1 person
Hi Ananya, this post is intended Http Streaming, for live streaming of media you need to look into RTSP. Below link has complete tutorial on how to stream form camera to web/other devices:
http://www.androidhive.info/2014/06/android-streaming-live-camera-video-to-web-page/
Hope this helps.
LikeLike
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.
LikeLike
Hi,
by any chance you know how to stream media files over internet i.e. not only on LAN?
LikeLike
Hi Srihari,
Can iget full source code?
LikeLike
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 .
LikeLike
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,
LikeLike
Hi ,
I need to play multiple files at a time on browser through http server . Please suggest . Thanks In advance.
Vaibhav Jain
LikeLike
What about creating multiple clients support with this?
LikeLike