Back to project page spydroid-ipcamera.
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.
/* * 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); } } } }