Android Open Source - spydroid-ipcamera Custom Http Server






From Project

Back to project page spydroid-ipcamera.

License

The source code is released under:

GNU General Public License

If you think the Android project spydroid-ipcamera listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (C) 2011-2013 GUIGUI Simon, fyhertz@gmail.com
 * /*w ww .j  av a 2 s .  c  o  m*/
 * This file is part of Spydroid (http://code.google.com/p/spydroid-ipcamera/)
 * 
 * Spydroid is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This source code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this source code; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package net.majorkernelpanic.spydroid.api;

import static net.majorkernelpanic.streaming.SessionBuilder.AUDIO_NONE;
import static net.majorkernelpanic.streaming.SessionBuilder.VIDEO_NONE;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.List;
import java.util.WeakHashMap;

import net.majorkernelpanic.http.TinyHttpServer;
import net.majorkernelpanic.spydroid.SpydroidApplication;
import net.majorkernelpanic.streaming.Session;
import net.majorkernelpanic.streaming.rtsp.UriParser;

import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.ContentProducer;
import org.apache.http.entity.EntityTemplate;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpRequestHandler;
import org.apache.http.util.EntityUtils;

import android.util.Log;

/**
 * 
 * HTTP server of Spydroid.
 * 
 * Its document root is assets/www, it contains a little user-friendly website to control spydroid from a browser.
 * 
 * Some commands can be sent to it by sending POST request to "/request.json".
 * See {@link RequestHandler} to find out what kind of commands can be sent.
 * 
 * Streams can also be started/stopped by sending GET request to "/spydroid.sdp".
 * The HTTP server then responds with a proper Session Description (SDP).
 * All supported options are described in {@link UriParser}
 *
 */
public class CustomHttpServer extends TinyHttpServer {

  /** A stream failed to start. */
  public final static int ERROR_START_FAILED = 0xFE;

  /** Streaming started. */
  public final static int MESSAGE_STREAMING_STARTED = 0X00;

  /** Streaming stopped. */
  public final static int MESSAGE_STREAMING_STOPPED = 0X01;

  /** Maximal number of streams that you can start from the HTTP server. **/
  protected static final int MAX_STREAM_NUM = 2;

  private DescriptionRequestHandler mDescriptionRequestHandler;
  private WeakHashMap<Session,Object> mSessions = new WeakHashMap<Session,Object>(2);

  public CustomHttpServer() {

    // The common name that appears in the CA of the HTTPS server of Spydroid
    mCACommonName = "Spydroid CA";

    // If at some point a stream cannot start the exception is stored so that
    // it can be fetched in the HTTP interface to display an appropriate message
    addCallbackListener(mListener);

    // HTTP is used by default for now
    mHttpEnabled = true;
    mHttpsEnabled = false;

  }

  private CallbackListener mListener = new CallbackListener() {
    @Override
    public void onError(TinyHttpServer server, Exception e, int error) {
      if (error==ERROR_START_FAILED) {
        SpydroidApplication.getInstance().lastCaughtException = e;
      }
    }
    @Override
    public void onMessage(TinyHttpServer server, int message) {}
  };

  @Override
  public void onCreate() {
    super.onCreate();
    mDescriptionRequestHandler = new DescriptionRequestHandler();
    addRequestHandler("/spydroid.sdp*", mDescriptionRequestHandler);
    addRequestHandler("/request.json*", new CustomRequestHandler());
  }

  @Override
  public void stop() {
    super.stop();
    // If user has started a session with the HTTP Server, we need to stop it
    for (int i=0;i<mDescriptionRequestHandler.mSessionList.length;i++) {
      if (mDescriptionRequestHandler.mSessionList[i].session != null) {
        boolean streaming = isStreaming();
        mDescriptionRequestHandler.mSessionList[i].session.stop();
        if (streaming && !isStreaming()) {
          postMessage(MESSAGE_STREAMING_STOPPED);
        }
        mDescriptionRequestHandler.mSessionList[i].session.release();
        mDescriptionRequestHandler.mSessionList[i].session = null;
      }
    }

  }

  public boolean isStreaming() {
    for ( Session session : mSessions.keySet() ) {
        if ( session != null ) {
          if (session.isStreaming()) return true;
        } 
    }
    return false;
  }

  public long getBitrate() {
    long bitrate = 0;
    for ( Session session : mSessions.keySet() ) {
        if ( session != null ) {
          if (session.isStreaming()) bitrate += session.getBitrate();
        } 
    }
    return bitrate;
  }
  
  class CustomRequestHandler implements HttpRequestHandler {

    public CustomRequestHandler() {}

    public void handle(HttpRequest request, HttpResponse response, HttpContext arg2) throws HttpException, IOException {

      if (request.getRequestLine().getMethod().equals("POST")) {

        // Retrieve the POST content
        HttpEntityEnclosingRequest post = (HttpEntityEnclosingRequest) request;
        byte[] entityContent = EntityUtils.toByteArray(post.getEntity());
        String content = new String(entityContent, Charset.forName("UTF-8"));

        // Execute the request
        final String json = RequestHandler.handle(content);

        // Return the response
        EntityTemplate body = new EntityTemplate(new ContentProducer() {
          public void writeTo(final OutputStream outstream) throws IOException {
            OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
            writer.write(json);
            writer.flush();
          }
        });
        response.setStatusCode(HttpStatus.SC_OK);
        body.setContentType("application/json; charset=UTF-8");
        response.setEntity(body);
      }

    }
  }

  /** 
   * Allows to start streams (a session contains one or more streams) from the HTTP server by requesting 
   * this URL: http://ip/spydroid.sdp (the RTSP server is not needed here). 
   **/
  class DescriptionRequestHandler implements HttpRequestHandler {

    private final SessionInfo[] mSessionList = new SessionInfo[MAX_STREAM_NUM];

    private class SessionInfo {
      public Session session;
      public String uri;
      public String description;
    }

    public DescriptionRequestHandler() {
      for (int i=0;i<MAX_STREAM_NUM;i++) {
        mSessionList[i] = new SessionInfo();
      }
    }

    public synchronized void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException {
      Socket socket = ((TinyHttpServer.MHttpContext)context).getSocket();
      String uri = request.getRequestLine().getUri();
      int id = 0;
      boolean stop = false;

      try {

        // A stream id can be specified in the URI, this id is associated to a session
        List<NameValuePair> params = URLEncodedUtils.parse(URI.create(uri),"UTF-8");
        uri = "";
        if (params.size()>0) {
          for (Iterator<NameValuePair> it = params.iterator();it.hasNext();) {
            NameValuePair param = it.next();
            if (param.getName().equalsIgnoreCase("id")) {
              try {  
                id = Integer.parseInt(param.getValue());
              } catch (Exception ignore) {}
            }
            else if (param.getName().equalsIgnoreCase("stop")) {
              stop = true;
            }
          }  
        }

        params.remove("id");
        uri = "http://c?" + URLEncodedUtils.format(params, "UTF-8");

        if (!uri.equals(mSessionList[id].uri)) {

          mSessionList[id].uri = uri;

          // Stops all streams if a Session already exists
          if (mSessionList[id].session != null) {
            boolean streaming = isStreaming();
            mSessionList[id].session.syncStop();
            if (streaming && !isStreaming()) {
              postMessage(MESSAGE_STREAMING_STOPPED);
            }
            mSessionList[id].session.release();
            mSessionList[id].session = null;
          }

          if (!stop) {
            
            boolean b = false;
            if (mSessionList[id].session != null) {
              InetAddress dest = InetAddress.getByName(mSessionList[id].session.getDestination());
              if (!dest.isMulticastAddress()) {
                b = true;
              }
            }
            if (mSessionList[id].session == null || b) {
              // Parses URI and creates the Session
              mSessionList[id].session = UriParser.parse(uri);
              mSessions.put(mSessionList[id].session, null);
            } 

            // Sets proper origin & dest
            mSessionList[id].session.setOrigin(socket.getLocalAddress().getHostAddress());
            if (mSessionList[id].session.getDestination()==null) {
              mSessionList[id].session.setDestination(socket.getInetAddress().getHostAddress());
            }
            
            // Starts all streams associated to the Session
            boolean streaming = isStreaming();
            mSessionList[id].session.syncStart();
            if (!streaming && isStreaming()) {
              postMessage(MESSAGE_STREAMING_STARTED);
            }

            mSessionList[id].description = mSessionList[id].session.getSessionDescription().replace("Unnamed", "Stream-"+id);
            Log.v(TAG, mSessionList[id].description);
            
          }
        }

        final int fid = id; final boolean fstop = stop;
        response.setStatusCode(HttpStatus.SC_OK);
        EntityTemplate body = new EntityTemplate(new ContentProducer() {
          public void writeTo(final OutputStream outstream) throws IOException {
            OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
            if (!fstop) {
              writer.write(mSessionList[fid].description);
            } else {
              writer.write("STOPPED");
            }
            writer.flush();
          }
        });
        body.setContentType("application/sdp; charset=UTF-8");
        response.setEntity(body);

      } catch (Exception e) {
        mSessionList[id].uri = "";
        response.setStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
        Log.e(TAG,e.getMessage()!=null?e.getMessage():"An unknown error occurred");
        e.printStackTrace();
        postError(e,ERROR_START_FAILED);
      }

    }

  }

}




Java Source Code List

net.majorkernelpanic.http.ModAssetServer.java
net.majorkernelpanic.http.ModInternationalization.java
net.majorkernelpanic.http.ModSSL.java
net.majorkernelpanic.http.TinyHttpServer.java
net.majorkernelpanic.spydroid.SpydroidApplication.java
net.majorkernelpanic.spydroid.Utilities.java
net.majorkernelpanic.spydroid.api.CustomHttpServer.java
net.majorkernelpanic.spydroid.api.CustomRtspServer.java
net.majorkernelpanic.spydroid.api.RequestHandler.java
net.majorkernelpanic.spydroid.ui.AboutFragment.java
net.majorkernelpanic.spydroid.ui.HandsetFragment.java
net.majorkernelpanic.spydroid.ui.OptionsActivity.java
net.majorkernelpanic.spydroid.ui.PreviewFragment.java
net.majorkernelpanic.spydroid.ui.SpydroidActivity.java
net.majorkernelpanic.spydroid.ui.TabletFragment.java
net.majorkernelpanic.streaming.MediaStream.java
net.majorkernelpanic.streaming.SessionBuilder.java
net.majorkernelpanic.streaming.Session.java
net.majorkernelpanic.streaming.Stream.java
net.majorkernelpanic.streaming.audio.AACStream.java
net.majorkernelpanic.streaming.audio.AMRNBStream.java
net.majorkernelpanic.streaming.audio.AudioQuality.java
net.majorkernelpanic.streaming.audio.AudioStream.java
net.majorkernelpanic.streaming.exceptions.CameraInUseException.java
net.majorkernelpanic.streaming.exceptions.ConfNotSupportedException.java
net.majorkernelpanic.streaming.exceptions.InvalidSurfaceException.java
net.majorkernelpanic.streaming.exceptions.StorageUnavailableException.java
net.majorkernelpanic.streaming.gl.SurfaceManager.java
net.majorkernelpanic.streaming.gl.SurfaceView.java
net.majorkernelpanic.streaming.gl.TextureManager.java
net.majorkernelpanic.streaming.hw.CodecManager.java
net.majorkernelpanic.streaming.hw.EncoderDebugger.java
net.majorkernelpanic.streaming.hw.NV21Convertor.java
net.majorkernelpanic.streaming.mp4.MP4Config.java
net.majorkernelpanic.streaming.mp4.MP4Parser.java
net.majorkernelpanic.streaming.rtcp.SenderReport.java
net.majorkernelpanic.streaming.rtp.AACADTSPacketizer.java
net.majorkernelpanic.streaming.rtp.AACLATMPacketizer.java
net.majorkernelpanic.streaming.rtp.AMRNBPacketizer.java
net.majorkernelpanic.streaming.rtp.AbstractPacketizer.java
net.majorkernelpanic.streaming.rtp.H263Packetizer.java
net.majorkernelpanic.streaming.rtp.H264Packetizer.java
net.majorkernelpanic.streaming.rtp.MediaCodecInputStream.java
net.majorkernelpanic.streaming.rtp.RtpSocket.java
net.majorkernelpanic.streaming.rtsp.RtspClient.java
net.majorkernelpanic.streaming.rtsp.RtspServer.java
net.majorkernelpanic.streaming.rtsp.UriParser.java
net.majorkernelpanic.streaming.video.CodecManager.java
net.majorkernelpanic.streaming.video.H263Stream.java
net.majorkernelpanic.streaming.video.H264Stream.java
net.majorkernelpanic.streaming.video.VideoQuality.java
net.majorkernelpanic.streaming.video.VideoStream.java