3

I'm trying to use my phone as a realtime MJPEG video source. So far, capturing frames and converting them into JPEGs is no big deal. My real issue is sending the multipart response properly. There's tons of documentation about sending multipart responses out there, but the issue with them is that they all expect that all of the images are available at the time the HTTP request comes in (such as would be used for a multi-image upload). In order to stream in realtime, of course, I need to be able to begin to send the multipart response while continually adding jpegs in the body. I'm by no means a HTTP buff, so it's not desirable for me be required to roll my own HTTP response and write directly to a socket. Is there a library out there that supports this kind of behavior? I've scoured the internet for solutions, but I really don't see anything useful out there.

Any ideas? Worst case scenario, I'd be willing to look at human-readable documentation of how to write a multipart response by hand, but I'd really just rather use a library if that's possible.

Thanks in advance.

edit: got it working using the orielly servlet library as per sigmavirus' suggestion. Note that the MJPEG stream is more or less implicitly inferred from the fact that I'm sending a multipart/x-mixed-replace that only has image/jpeg's in it. Check out the comment in my code for a tutorial that shows what jetty libraries you'll need to get this running. Of course, you'll additionally need cos.jar, the Orielly servlet library. The code follows:

package edu.stevens.arpac.webclient;

import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Collections;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.http.conn.util.InetAddressUtils;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.Request;

import com.oreilly.servlet.MultipartResponse;
import com.oreilly.servlet.ServletUtils;


import android.os.Environment;
import android.util.Log;
// holla at http://puregeekjoy.blogspot.com/2011/06/running-embedded-jetty-in-android-app.html
public class JettyServer extends Thread 
{
private static final String TAG = "JettyServer";
private Server webServer;
private Boolean isStarted = false;

public JettyServer()
{
    super();
    Log.i(TAG, "Initializing server to port 8080");
    webServer = new Server(8080);

    Handler handler = new AbstractHandler() {
        public void handle(String target, Request request, HttpServletRequest servletRequest,
                HttpServletResponse servletResponse) throws IOException, ServletException {

            ServletOutputStream out = servletResponse.getOutputStream();

             MultipartResponse multi = new MultipartResponse(servletResponse);


             Boolean go = true;
             while( go )
             {

                 try
                 {
                     multi.startResponse("image/jpeg");
                     ServletUtils.returnFile(Environment.getExternalStorageDirectory().getPath() + "/ARPac/twi.jpg", out);
                     multi.endResponse();
                 }
                 catch(IOException ex)
                 {
                    go = false;
                    Log.i(TAG, "IO Failed with exception " + ex.getMessage());
                 }
             }
             request.setHandled(true);
        }
    };
    webServer.setHandler(handler);

    try {
        webServer.start();
        Log.d(TAG, "started Web server @ " + getIPAddress());
        isStarted = true;

    }
    catch (Exception e) {
        Log.d(TAG, "unexpected exception starting Web server: " + e);
    }
}

/**
 * Get IP address from first non-localhost interface
 * @return  address or empty string
 */
private String getIPAddress() 
{
    try 
    {
        List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
        for (NetworkInterface intf : interfaces) 
        {
            List<InetAddress> addrs = Collections.list(intf.getInetAddresses());
            for (InetAddress addr : addrs) 
            {
                if (!addr.isLoopbackAddress())
                {
                    String sAddr = addr.getHostAddress().toUpperCase(); 
                    if (InetAddressUtils.isIPv4Address(sAddr))
                    {
                        //Log.d(TAG, "IP address is: " + sAddr);
                        return sAddr;
                    } 
                }
            }
        }
    } 
    catch (Exception ex) 
    { 
        Log.e(TAG, "could not get IP address: " + ex.getMessage());
    } // for now eat exceptions
    Log.e(TAG, "Could not find a non-loopback IPv4 address!");
    return "";
}

public void teardown()
{
    if( isStarted )
    {
        try {
            webServer.stop();
            isStarted = false;
        } catch (Exception e) {
            Log.e(TAG, "Couldn't stop server. Probably was called when server already stopped.");
        }
    }
}

public void run() 
 {

 }

}

mercurytw
  • 93
  • 1
  • 8
  • Excuse the fact that my JettyServer class extends Thread but does nothing with it. You don't need to do that. It's just the direction my code is headed. – mercurytw Jan 10 '13 at 01:01

1 Answers1

1

Have you seen this? http://www.servlets.com/cos/javadoc/com/oreilly/servlet/MultipartResponse.html It looks like the example sends each part individually and waits a specified time limit before sending the next or receiving an interrupt.

Ian Stapleton Cordasco
  • 26,944
  • 4
  • 67
  • 72
  • you know, I remember coming across this. This library depends on some other libraries that don't come stock with Android, so I didn't pursue it. I think I'll give this another shot since it does seem to do what I need. – mercurytw Jan 09 '13 at 18:19
  • Like I said, if you post it over @ quora, I'll make sure 200+ people see it too. They might have some insights over there. – Ian Stapleton Cordasco Jan 09 '13 at 18:24