Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implemented UPnP to map the RTSP port.

We still have issues due to RTP working around NAT systems that
implement port randomization.  At this point I'm leaning toward
implementing HTTP live streaming as an alternative to RTP to give user's
more streaming flexibility.
  • Loading branch information...
commit b949c3f2a037446572de98a9f8036884ab4aea14 1 parent 6fe6bcb
@jasta authored
View
7 .classpath
@@ -4,5 +4,12 @@
<classpathentry kind="src" path="gen"/>
<classpathentry kind="src" path="tests/src"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry exported="true" kind="lib" path="libs/teleal-common-1.0.9.jar"/>
+ <classpathentry exported="true" kind="lib" path="libs/cling-core-1.0.5.jar" sourcepath="/home/jasta/software/cling-distribution-1.0.5/core/cling-core-1.0.5-sources.jar">
+ <attributes>
+ <attribute name="javadoc_location" value="file:/home/jasta/software/cling-distribution-1.0.5/core/apidocs/"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry exported="true" kind="lib" path="libs/cling-support-1.0.5.jar" sourcepath="/home/jasta/software/cling-distribution-1.0.5/support/cling-support-1.0.5-sources.jar"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>
View
4 AndroidManifest.xml
@@ -14,6 +14,10 @@
during initial set-up) -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <!-- Required by cling (our UPnP library). -->
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
+
<!-- Required only for debugging at the moment... -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
View
BIN  libs/cling-core-1.0.5.jar
Binary file not shown
View
BIN  libs/cling-support-1.0.5.jar
Binary file not shown
View
BIN  libs/teleal-common-1.0.9.jar
Binary file not shown
View
12 src/org/devtcg/rojocam/CamcorderNodeService.java
@@ -31,6 +31,8 @@
public class CamcorderNodeService extends Service {
private static final String TAG = CamcorderNodeService.class.getSimpleName();
+ private static final int RTSP_PORT = 5454;
+
public static final String ACTION_ACTIVATE_CAMERA_NODE =
"org.devtcg.rojocam.intent.action.ACTIVATE_CAMERA_NODE";
public static final String ACTION_DEACTIVATE_CAMERA_NODE =
@@ -51,6 +53,8 @@
private SimpleRtspServer mRtspServer;
+ private UPnPPortMapper mPortMapper;
+
/**
* Wake lock active only when the camera is active (that is, a stream is
* being sent).
@@ -163,10 +167,12 @@ private void activateNode(ResultReceiver receiver) {
try {
/* XXX: We should only bind on WiFi! */
mRtspServer = new SimpleRtspServer();
- mRtspServer.bind(new InetSocketAddress((InetAddress)null, 5454));
+ mRtspServer.bind(new InetSocketAddress((InetAddress)null, RTSP_PORT));
mRtspServer.registerMedia("test1.rtp", new CamcorderMediaHandler(mCamcorderRef));
mRtspServer.start();
+ mPortMapper = UPnPPortMapper.mapPortIfNecessary(this, "rojocam mapping", RTSP_PORT);
+
changeState(State.ACTIVE);
if (receiver != null) {
@@ -206,6 +212,10 @@ private void deactivateNode(ResultReceiver receiver) {
mRtspServer = null;
}
+ if (mPortMapper != null) {
+ mPortMapper.unmapPorts();
+ }
+
changeState(State.DEACTIVE);
}
View
143 src/org/devtcg/rojocam/UPnPPortMapper.java
@@ -0,0 +1,143 @@
+package org.devtcg.rojocam;
+
+import org.teleal.cling.UpnpService;
+import org.teleal.cling.UpnpServiceImpl;
+import org.teleal.cling.android.AndroidUpnpServiceConfiguration;
+import org.teleal.cling.android.AndroidWifiSwitchableRouter;
+import org.teleal.cling.protocol.ProtocolFactory;
+import org.teleal.cling.registry.Registry;
+import org.teleal.cling.registry.RegistryListener;
+import org.teleal.cling.support.igd.PortMappingListener;
+import org.teleal.cling.support.model.PortMapping;
+import org.teleal.cling.transport.Router;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+/**
+ * Provides UPnP services (NAT port mapping). This class is incredibly
+ * inflexible, offering only the most basic features we need (single port
+ * mapping and unmapping on demand). Currently no error reporting is provided...
+ */
+public class UPnPPortMapper {
+ private static final int SCAN_TIMEOUT = 5000;
+
+ private WeakReference<Context> mContext;
+
+ private InetAddress mLocalAddress;
+ private UpnpService mUpnpService;
+
+ private String mDescription;
+ private int mPort;
+
+ private WifiManager mWifiMan;
+ private ConnectivityManager mConnMan;
+
+ /**
+ * Create a port mapper handle and attempt to map the specified port. This
+ * begins an asynchronous operation to scan the network and ultimately try
+ * to add the port mapping to any InternetGatewayDevices found.
+ *
+ * @return True if necessary and an attempt is underway; false otherwise.
+ * @throws IOException
+ */
+ public static UPnPPortMapper mapPortIfNecessary(Context context, String description, int port)
+ throws IOException {
+ UPnPPortMapper mapper = new UPnPPortMapper(context, description, port);
+ if (mapper.needsPortMapping()) {
+ mapper.mapPorts();
+ return mapper;
+ } else {
+ return null;
+ }
+ }
+
+ private UPnPPortMapper(Context context, String description, int port) {
+ mContext = new WeakReference<Context>(context);
+ mDescription = description;
+ mPort = port;
+
+ mWifiMan = (WifiManager)getContext().getSystemService(Context.WIFI_SERVICE);
+ mConnMan = (ConnectivityManager)getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
+ private final Context getContext() {
+ return mContext.get();
+ }
+
+ private WifiManager getWifiManager() {
+ return mWifiMan;
+ }
+
+ private ConnectivityManager getConnectivityManager() {
+ return mConnMan;
+ }
+
+ private boolean needsPortMapping() throws IOException {
+ return getDefaultRouteLocalAddress().isSiteLocalAddress();
+ }
+
+ private void mapPorts() throws IOException {
+ if (mUpnpService != null) {
+ throw new IllegalStateException();
+ }
+
+ String internalIp = getDefaultRouteLocalAddress().getHostAddress();
+ PortMapping mapping = new PortMapping(mPort, internalIp, PortMapping.Protocol.TCP, mDescription);
+ mUpnpService = new MyUpnpServiceImpl(new PortMappingListener(mapping));
+ mUpnpService.getControlPoint().search(SCAN_TIMEOUT);
+ }
+
+ /**
+ * Disable UPnP and remove any port mappings we registered. This should be
+ * called during application shutdown.
+ */
+ public void unmapPorts() {
+ mUpnpService.shutdown();
+ }
+
+ /**
+ * Determine the source IP (our IP) of our route to www.google.com:80. If
+ * the user has layers of indirection to access the Internet, it is our
+ * assumption that this function will return an address in the RFC1918
+ * private space; otherwise, no IGD mappings are necessary.
+ *
+ * @throws IOException
+ */
+ private static InetAddress determineDefaultRouteLocalAddress() throws IOException {
+ Socket socket = new Socket();
+ try {
+ socket.connect(new InetSocketAddress("www.google.com", 80));
+ return socket.getLocalAddress();
+ } finally {
+ socket.close();
+ }
+ }
+
+ private synchronized InetAddress getDefaultRouteLocalAddress() throws IOException {
+ if (mLocalAddress == null) {
+ mLocalAddress = determineDefaultRouteLocalAddress();
+ }
+
+ return mLocalAddress;
+ }
+
+ private class MyUpnpServiceImpl extends UpnpServiceImpl {
+ public MyUpnpServiceImpl(RegistryListener... listeners) {
+ super(new AndroidUpnpServiceConfiguration(getWifiManager()), listeners);
+ }
+
+ @Override
+ protected Router createRouter(ProtocolFactory protocolFactory, Registry registry) {
+ return new AndroidWifiSwitchableRouter(configuration, protocolFactory,
+ getWifiManager(), getConnectivityManager());
+ }
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.