Make remote discovery faster. Fix the bitrate slider. Change some toast text.

This commit is contained in:
Cameron Gutman 2014-07-04 14:51:57 -07:00
parent 9be149942a
commit 0f4e5a4585
6 changed files with 221 additions and 145 deletions

Binary file not shown.

View File

@ -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() {

View File

@ -35,6 +35,7 @@ public class AppView extends Activity {
private ArrayAdapter<AppObject> 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);
}

View File

@ -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<NetworkInterface> 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);

View File

@ -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) {

View File

@ -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<NetworkInterface> 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) {