From 0f4e5a4585d048a5813f1b8cde061da887ba0c74 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Fri, 4 Jul 2014 14:51:57 -0700 Subject: [PATCH] Make remote discovery faster. Fix the bitrate slider. Change some toast text. --- libs/limelight-common.jar | Bin 408690 -> 408696 bytes src/com/limelight/AdvancedSettings.java | 31 ++- src/com/limelight/AppView.java | 4 + src/com/limelight/Game.java | 116 +--------- src/com/limelight/PcView.java | 13 +- .../computers/ComputerManagerService.java | 202 +++++++++++++++--- 6 files changed, 221 insertions(+), 145 deletions(-) diff --git a/libs/limelight-common.jar b/libs/limelight-common.jar index c77477999fb84e4724f96f77dacda0f27a4c8312..1e437dcc39f1ddaea10f0024b446b918beeae0fa 100644 GIT binary patch delta 1609 zcmX|>dpOg39LN1eUvp10Ny1hft>w1am`g6>n(la?&;eh zTMu+>!o8m0hF_)kPGlJ5nnw=^8{Bg&Jub~Ze4N@hmV=c|9?wf2nm+JmYiErEPk#?* zx_o3{Y_8ucI@cdR7WS4Ha#U8b$+2zr#l97ys_b3Q3ck*#{a5aUB)c&=PwxvZYLAv0 zVCx(l^BxD$4nJo*z7h(J{K|qp@Fry21l-Zy8Pz8tTit`Pu^%GZgva!v)JNZ_+H zsnLvzN60`$#D2&qrx47u>q4ipzTMPGiQqp7-lSevpYarTC? zbHR3`o_lN#T~~lhD6ZtxkmvBapKov(y@?l(nxNYo}eHhc>UqCICOCuZMO$S?whkY=N1P2T^;6`%d@J}IHioO;w#q@ZbKPvLbE0}?G`o7S7HMa$UB);a6`J@Y_jEZ$M8WGrgdtyt@l z(+UT9U~H4b3Dh33%P&iOI4!hepBp+dGRW&WztqWH_I=d;PL1f2F}W*s=AUKbXUaOU ztk#B%(C$$h_Q+hVjjyzsv~KTB5pSKNft_iseW4S<4q< zns3ifCfQ^qD^^`tDmIW`k=wId8m;GfpxsMKLc&QVv(QlEd>ewS2DKW=)kv*oO-&mD zC^!^>Rr4JWf|Pz7utUZm^heQa41yF^Puk93bKnjVU?J78k_7gmev^V#B%ld>r4UUx zXa?k=D;a2_hJaXW!!$A=qe?fx5fiWlPLP2G>WhXLw4l8SutGU#|Af=ph%%%ZfVJ?6 z2~bDwz+n^MkEB9t3J5?Lu#f@*QE|kde>YQs1yTtEsDOb=p}{~ZFoMF>2UWU542E!@ zDKJC@?-BzZmYV`oRKK$rw!!r-h$7TB0|b<^t9ZW+(ApJQ56_4VKH!&G16$2jm&*?O zX|!m-4k17W4N#CNc$EgI$S{0C116FKYy?iT5iHa)2LvP&I++72$(%4LSP>?Lg#}{y c7Iv8fSHushTL3b07rI%1O<(~JBqSvM2ct3C^Z)<= delta 1648 zcmYk6doE~8Y-rTeour{nuQ?{hxy*ZXtce|~0X4L{BrYSDdF(0V8} zH8qr&S*NvXMpT2>Y4DwS3;!CB_ZuN27>Zn?oYA@HwK#vyb2~lah)9cBC#2 z(EH)Xy-U=(e0z)4d8{Oh$P$Iy6kMjoW%gWtIHV)>6GaeG2Q){Adrn7o4d2FJqI3(y3n!Due8=s7d zTMowrvE`l(FgYMcKdHUmZqhB|Y2C5v)H9_uvF+7_ytL=6JfpL0*C@#VC)l#hbb_?H zqdRu*81nC83EQi$djOM_xrxes;_jhYdcwwNQRSYC)&1_gisUcyvawh+E^I%umc3Ox zso&?&(Q6zc^PnE6s|?br(L9q}B=l@G(xTkg@d`bc+MQ@SnVxZR^s&>Cu*&>Mtq{Zh z1mFpO$8;R8>XHkSox6->n}eh3_FgRrJ{s8=SofOw!o@2p?OGT2?&f&H{%TQ#EM4bH z`32Q$G_F67Y8p=C`nIdTrd<_}Sop1EZKU$dN>gsDTvInzaj6kk3j^_YWGgXkxO}OM zm><6)$mJ=&AtE*WR9n8)kgm(rg#P%%bYsLRMpGaL{2UhsO${~(77s=p7?9>9KT!#_ z+q0t3TWGwu&gvzm+oa5(s37D-tkwX&nwkB1b~P{0QBRQ}9KX`s*~twdOTT1~GMcC% ze5dIAY$@SGPl@O&Y5zN)MVQ@7`)cpSq+l7{bV*pBF29UfTvAhSla!UU>0V_aZ8x>; z+djvS?3nKrH?vcC>(;%%HgThvch#z@%I~|%W6;O&yhULeXF|1u|D#`vvl_Af`%-{k z*-ail@<;1kgA}PM$$~@6c>YRGcq$0)v%5E5P?*!~nO?+rWf(e?Aa05*5@d}=G}_=5 z^>Mm_6#EP18apY|<9CJ>4jR|p`c^3(u5Z37{_)d?5~muSaJ=|ID~~T%!xAk^25(V? zlH8>+rmnq|>7#edryWe>2`96kot!=R;Q+6o%9f#XV2gIbJBY8=t&ok27}9%}Ost0C zP(_zi=zOcigW8G8pYg9PxduRI4vSmnRa(zoc zzj^zVZ5$I@)cH^6!5go>zF$5$Zcu+Cyl$l5gfHohH?{QMwjwj`U{loAZ#k*W%f7z- zhUc93tbT64MA7mSru(X@l^Qhh=x7v*aalWZ(=9 zB9F<+x;_N!2G&9Xgzg3?&mwSx#xB)+CJ7VSzh!*9`0Lng^mCTBcjZ2K8`(gk} Q`Mi~%jYFY6C{q;be@|M?2LJ#7 diff --git a/src/com/limelight/AdvancedSettings.java b/src/com/limelight/AdvancedSettings.java index 86bbacff..40941ed7 100644 --- a/src/com/limelight/AdvancedSettings.java +++ b/src/com/limelight/AdvancedSettings.java @@ -9,6 +9,7 @@ import android.os.Bundle; import android.widget.CompoundButton; import android.widget.RadioButton; import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; import android.widget.CompoundButton.OnCheckedChangeListener; @@ -17,6 +18,9 @@ public class AdvancedSettings extends Activity { private RadioButton forceSoftDec, autoDec, forceHardDec; private SeekBar bitrateSlider; private TextView bitrateLabel; + + private static final int BITRATE_FLOOR = 1; + private static final int BITRATE_CEILING = 100; @Override public void onPause() { @@ -49,7 +53,7 @@ public class AdvancedSettings extends Activity { prefs = getSharedPreferences(Game.PREFS_FILE_NAME, Context.MODE_MULTI_PROCESS); - bitrateSlider.setMax(Game.BITRATE_CEILING); + bitrateSlider.setMax(BITRATE_CEILING); bitrateSlider.setProgress(prefs.getInt(Game.BITRATE_PREF_STRING, Game.DEFAULT_BITRATE)); updateBitrateLabel(); @@ -94,6 +98,31 @@ public class AdvancedSettings extends Activity { forceSoftDec.setOnCheckedChangeListener(occl); forceHardDec.setOnCheckedChangeListener(occl); autoDec.setOnCheckedChangeListener(occl); + + this.bitrateSlider.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, + boolean fromUser) { + + // Verify the user's selection + if (fromUser) { + if (progress < BITRATE_FLOOR) { + seekBar.setProgress(BITRATE_FLOOR); + return; + } + } + + updateBitrateLabel(); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + }); } private void updateBitrateLabel() { diff --git a/src/com/limelight/AppView.java b/src/com/limelight/AppView.java index bf6f5ac6..ef2787f8 100644 --- a/src/com/limelight/AppView.java +++ b/src/com/limelight/AppView.java @@ -35,6 +35,7 @@ public class AppView extends Activity { private ArrayAdapter appListAdapter; private InetAddress ipAddress; private String uniqueId; + private boolean remote; private final static int RESUME_ID = 1; private final static int QUIT_ID = 2; @@ -42,6 +43,7 @@ public class AppView extends Activity { public final static String ADDRESS_EXTRA = "Address"; public final static String UNIQUEID_EXTRA = "UniqueId"; public final static String NAME_EXTRA = "Name"; + public final static String REMOTE_EXTRA = "Remote"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -50,6 +52,7 @@ public class AppView extends Activity { byte[] address = getIntent().getByteArrayExtra(ADDRESS_EXTRA); uniqueId = getIntent().getStringExtra(UNIQUEID_EXTRA); + remote = getIntent().getBooleanExtra(REMOTE_EXTRA, false); if (address == null || uniqueId == null) { return; } @@ -195,6 +198,7 @@ public class AppView extends Activity { intent.putExtra(Game.EXTRA_HOST, ipAddress.getHostAddress()); intent.putExtra(Game.EXTRA_APP, app.getAppName()); intent.putExtra(Game.EXTRA_UNIQUEID, uniqueId); + intent.putExtra(Game.EXTRA_STREAMING_REMOTE, remote); startActivity(intent); } diff --git a/src/com/limelight/Game.java b/src/com/limelight/Game.java index c359069c..5c7e7cf4 100644 --- a/src/com/limelight/Game.java +++ b/src/com/limelight/Game.java @@ -1,11 +1,5 @@ package com.limelight; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.net.InterfaceAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.util.Enumeration; import com.limelight.binding.PlatformBinding; import com.limelight.binding.input.ControllerHandler; @@ -26,7 +20,6 @@ import android.content.SharedPreferences; import android.graphics.Point; import android.media.AudioManager; import android.net.ConnectivityManager; -import android.net.NetworkInfo; import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Handler; @@ -74,6 +67,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM public static final String EXTRA_HOST = "Host"; public static final String EXTRA_APP = "App"; public static final String EXTRA_UNIQUEID = "UniqueId"; + public static final String EXTRA_STREAMING_REMOTE = "Remote"; public static final String PREFS_FILE_NAME = "gameprefs"; @@ -83,18 +77,11 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM public static final String DECODER_PREF_STRING = "Decoder"; public static final String BITRATE_PREF_STRING = "Bitrate"; - public static final int BITRATE_FLOOR_720_30 = 2; - public static final int BITRATE_FLOOR_720_60 = 4; - public static final int BITRATE_FLOOR_1080_30 = 4; - public static final int BITRATE_FLOOR_1080_60 = 10; - public static final int BITRATE_DEFAULT_720_30 = 5; public static final int BITRATE_DEFAULT_720_60 = 10; public static final int BITRATE_DEFAULT_1080_30 = 10; public static final int BITRATE_DEFAULT_1080_60 = 30; - - public static final int BITRATE_CEILING = 50; - + public static final int DEFAULT_WIDTH = 1280; public static final int DEFAULT_HEIGHT = 720; public static final int DEFAULT_REFRESH_RATE = 60; @@ -179,21 +166,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM String host = Game.this.getIntent().getStringExtra(EXTRA_HOST); String app = Game.this.getIntent().getStringExtra(EXTRA_APP); String uniqueId = Game.this.getIntent().getStringExtra(EXTRA_UNIQUEID); - InetAddress addr; - boolean enableLargePackets; - try { - // If this does a DNS lookup, it could cause a NetworkOnMainThread exception - // Chances are if it has to do this, we're not on the same network anyways - addr = InetAddress.getByName(host); - - // Check if we can enable large packets if we get this far - enableLargePackets = shouldEnableLargePackets(addr); - } catch (Exception e) { - // We don't want to deal with any exceptions here. The user will be notified - // when the connection fails - enableLargePackets = false; - } - + boolean enableLargePackets = !Game.this.getIntent().getBooleanExtra(EXTRA_STREAMING_REMOTE, true); LimeLog.info("Using large packets? "+enableLargePackets); // Start the connection @@ -208,89 +181,6 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM sh.addCallback(this); } - private boolean shouldEnableLargePackets(InetAddress targetAddress) { - ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo activeNetworkInfo = connMgr.getActiveNetworkInfo(); - String matchingPrefix; - - if (activeNetworkInfo == null) { - return false; - } - - switch (activeNetworkInfo.getType()) - { - case ConnectivityManager.TYPE_ETHERNET: - matchingPrefix = "eth"; - break; - case ConnectivityManager.TYPE_WIFI: - matchingPrefix = "wlan"; - break; - - default: - // Must be on Ethernet or Wifi to consider that we can send large packets - return false; - } - - // Try to find the interface that corresponds to the active network - try { - Enumeration ifaceList = NetworkInterface.getNetworkInterfaces(); - while (ifaceList.hasMoreElements()) { - NetworkInterface iface = ifaceList.nextElement(); - - // Look for an interface that matches the prefix we expect - if (iface.isUp() && iface.getName().startsWith(matchingPrefix)) { - // Find the IPv4 address for the interface - for (InterfaceAddress addr : iface.getInterfaceAddresses()) { - if (!(addr.getAddress() instanceof Inet4Address)) { - // Skip non-IPv4 addresses - continue; - } - - // Found the right address on the right interface - return isOnSameSubnet(targetAddress, addr.getAddress(), addr.getNetworkPrefixLength()); - } - } - } - } catch (SocketException e) { - e.printStackTrace(); - } - - // We didn't find the interface or something else went wrong - return false; - } - - private boolean isOnSameSubnet(InetAddress targetAddress, InetAddress localAddress, short networkPrefixLength) { - byte[] targetBytes = targetAddress.getAddress(); - byte[] localBytes = localAddress.getAddress(); - - for (int byteIndex = 0; networkPrefixLength > 0; byteIndex++) { - byte target = targetBytes[byteIndex]; - byte local = localBytes[byteIndex]; - - if (networkPrefixLength >= 8) { - // Do a full byte comparison - if (target != local) { - return false; - } - - networkPrefixLength -= 8; - } - else { - target &= (byte)(0xFF << (8 - networkPrefixLength)); - local &= (byte)(0xFF << (8 - networkPrefixLength)); - - // Do a masked comparison - if (target != local) { - return false; - } - - networkPrefixLength = 0; - } - } - - return true; - } - private void checkDataConnection() { ConnectivityManager mgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); diff --git a/src/com/limelight/PcView.java b/src/com/limelight/PcView.java index be8096e0..d3ac3da1 100644 --- a/src/com/limelight/PcView.java +++ b/src/com/limelight/PcView.java @@ -223,6 +223,11 @@ public class PcView extends Activity { Toast.makeText(PcView.this, "Computer is offline", Toast.LENGTH_SHORT).show(); return; } + if (computer.runningGameId != 0) { + Toast.makeText(PcView.this, "Computer is currently in a game. " + + "You must close the game before pairing.", Toast.LENGTH_LONG).show(); + return; + } Toast.makeText(PcView.this, "Pairing...", Toast.LENGTH_SHORT).show(); new Thread(new Runnable() { @@ -305,7 +310,7 @@ public class PcView extends Activity { message = "It may take a few seconds for your PC to wake up. " + "If it doesn't, make sure it's configured properly for Wake-On-LAN."; } catch (IOException e) { - message = "Failed to send wake-on-lan packets"; + message = "Failed to send Wake-On-LAN packets"; } final String toastMessage = message; @@ -388,9 +393,11 @@ public class PcView extends Activity { if (computer.reachability == ComputerDetails.Reachability.LOCAL) { i.putExtra(AppView.ADDRESS_EXTRA, computer.localIp.getAddress()); + i.putExtra(AppView.REMOTE_EXTRA, false); } else { i.putExtra(AppView.ADDRESS_EXTRA, computer.remoteIp.getAddress()); + i.putExtra(AppView.REMOTE_EXTRA, true); } startActivity(i); } @@ -459,8 +466,8 @@ public class PcView extends Activity { } private void addListPlaceholder() { - pcListAdapter.add(new ComputerObject("No computers found yet. Make sure your computer is running GFE " + - "or add your PC manually on the settings page.", null)); + pcListAdapter.add(new ComputerObject("Discovery is running. No computers found yet. " + + "Make sure your computer is running GFE or add your PC manually on the settings page.", null)); } private void removeListView(ComputerDetails details) { diff --git a/src/com/limelight/computers/ComputerManagerService.java b/src/com/limelight/computers/ComputerManagerService.java index 9d49a646..b9c6702b 100644 --- a/src/com/limelight/computers/ComputerManagerService.java +++ b/src/com/limelight/computers/ComputerManagerService.java @@ -1,6 +1,11 @@ package com.limelight.computers; +import java.net.Inet4Address; import java.net.InetAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; import java.util.List; import java.util.Timer; import java.util.TimerTask; @@ -18,8 +23,11 @@ import com.limelight.nvstream.mdns.MdnsDiscoveryListener; import android.app.Service; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.os.Binder; import android.os.IBinder; @@ -180,41 +188,179 @@ public class ComputerManagerService extends Service { }; } + private int getActiveNetworkType() { + ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo activeNetworkInfo = connMgr.getActiveNetworkInfo(); + if (activeNetworkInfo == null) { + return -1; + } + + return activeNetworkInfo.getType(); + } + + private InterfaceAddress getActiveInterfaceAddress() { + String matchingPrefix; + + switch (getActiveNetworkType()) + { + case ConnectivityManager.TYPE_ETHERNET: + matchingPrefix = "eth"; + break; + case ConnectivityManager.TYPE_WIFI: + matchingPrefix = "wlan"; + break; + + default: + // Must be on Ethernet or Wifi to consider that we can send large packets + return null; + } + + // Try to find the interface that corresponds to the active network + try { + Enumeration ifaceList = NetworkInterface.getNetworkInterfaces(); + while (ifaceList.hasMoreElements()) { + NetworkInterface iface = ifaceList.nextElement(); + + // Look for an interface that matches the prefix we expect + if (iface.isUp() && iface.getName().startsWith(matchingPrefix)) { + // Find the IPv4 address for the interface + for (InterfaceAddress addr : iface.getInterfaceAddresses()) { + if (!(addr.getAddress() instanceof Inet4Address)) { + // Skip non-IPv4 addresses + continue; + } + + // Found the right address on the right interface + return addr; + } + } + } + } catch (SocketException e) { + e.printStackTrace(); + } + + // We didn't find the interface or something else went wrong + return null; + } + + private boolean isOnSameSubnet(InetAddress targetAddress, InetAddress localAddress, short networkPrefixLength) { + byte[] targetBytes = targetAddress.getAddress(); + byte[] localBytes = localAddress.getAddress(); + + for (int byteIndex = 0; networkPrefixLength > 0; byteIndex++) { + byte target = targetBytes[byteIndex]; + byte local = localBytes[byteIndex]; + + if (networkPrefixLength >= 8) { + // Do a full byte comparison + if (target != local) { + return false; + } + + networkPrefixLength -= 8; + } + else { + target &= (byte)(0xFF << (8 - networkPrefixLength)); + local &= (byte)(0xFF << (8 - networkPrefixLength)); + + // Do a masked comparison + if (target != local) { + return false; + } + + networkPrefixLength = 0; + } + } + + return true; + } + + private ComputerDetails tryPollIp(InetAddress ipAddr) { + try { + NvHTTP http = new NvHTTP(ipAddr, idManager.getUniqueId(), + null, PlatformBinding.getCryptoProvider(ComputerManagerService.this)); + + return http.getComputerDetails(); + } catch (Exception e) { + return null; + } + } + + private boolean pollComputer(ComputerDetails details, boolean localFirst) { + ComputerDetails polledDetails; + + if (localFirst) { + polledDetails = tryPollIp(details.localIp); + } + else { + polledDetails = tryPollIp(details.remoteIp); + } + + if (polledDetails == null) { + // Failed, so let's try the fallback + if (!localFirst) { + polledDetails = tryPollIp(details.localIp); + } + else { + polledDetails = tryPollIp(details.remoteIp); + } + + // The fallback poll worked + if (polledDetails != null) { + polledDetails.reachability = !localFirst ? ComputerDetails.Reachability.LOCAL : + ComputerDetails.Reachability.REMOTE; + } + } else { + polledDetails.reachability = localFirst ? ComputerDetails.Reachability.LOCAL : + ComputerDetails.Reachability.REMOTE; + } + + // Machine was unreachable both tries + if (polledDetails == null) { + return false; + } + + // If we got here, it's reachable + details.update(polledDetails); + return true; + } + + private boolean doPollMachine(ComputerDetails details) { + // Get the network type + int networkType = getActiveNetworkType(); + switch (networkType) { + // We'll check local first on these if we find + // we're on the same subnet + case ConnectivityManager.TYPE_ETHERNET: + case ConnectivityManager.TYPE_WIFI: + InterfaceAddress ifaceAddr = getActiveInterfaceAddress(); + if (ifaceAddr != null) { + if (isOnSameSubnet(details.localIp, ifaceAddr.getAddress(), ifaceAddr.getNetworkPrefixLength())) { + // It's on the same subnet, so poll local first + LimeLog.info("Machine looks local; trying local IP first"); + return pollComputer(details, true); + } + } + // Fall through to remote first + default: + LimeLog.info("Machine looks remote; trying remote IP first"); + return pollComputer(details, false); + } + } + private Runnable getPollingRunnable(final ComputerDetails details) { return new Runnable() { @Override public void run() { - boolean newPc = details.name == null; + boolean newPc = (details.name == null); - try { - // Try the local IP first - NvHTTP http = new NvHTTP(details.localIp, idManager.getUniqueId(), - null, PlatformBinding.getCryptoProvider(ComputerManagerService.this)); - - ComputerDetails localDetails = http.getComputerDetails(); - - // If we got here, it's reachable - details.reachability = ComputerDetails.Reachability.LOCAL; - details.update(localDetails); - } catch (Exception e) { - // This isn't horrible yet; we'll try remote - try { - NvHTTP http = new NvHTTP(details.remoteIp, idManager.getUniqueId(), - null, PlatformBinding.getCryptoProvider(ComputerManagerService.this)); - - ComputerDetails remoteDetails = http.getComputerDetails(); - - // If we got here, it's reachable - details.reachability = ComputerDetails.Reachability.REMOTE; - details.update(remoteDetails); - } catch (Exception e1) { - // No good, it's offline - details.state = ComputerDetails.State.OFFLINE; - details.reachability = ComputerDetails.Reachability.OFFLINE; - } + // Poll the machine + if (!doPollMachine(details)) { + details.state = ComputerDetails.State.OFFLINE; + details.reachability = ComputerDetails.Reachability.OFFLINE; } - + // If it's online, update our persistent state if (details.state == ComputerDetails.State.ONLINE) { if (!newPc) {