diff --git a/libs/bcpkix-jdk15on-150.jar b/libs/bcpkix-jdk15on-151.jar similarity index 62% rename from libs/bcpkix-jdk15on-150.jar rename to libs/bcpkix-jdk15on-151.jar index 5dc125fc..57e00806 100644 Binary files a/libs/bcpkix-jdk15on-150.jar and b/libs/bcpkix-jdk15on-151.jar differ diff --git a/libs/bcprov-jdk15on-150.jar b/libs/bcprov-jdk15on-151.jar similarity index 59% rename from libs/bcprov-jdk15on-150.jar rename to libs/bcprov-jdk15on-151.jar index d4b510d7..4076e116 100644 Binary files a/libs/bcprov-jdk15on-150.jar and b/libs/bcprov-jdk15on-151.jar differ diff --git a/libs/limelight-common.jar b/libs/limelight-common.jar index 18d3a422..3e58ac36 100644 Binary files a/libs/limelight-common.jar and b/libs/limelight-common.jar differ diff --git a/src/com/limelight/binding/crypto/AndroidCryptoProvider.java b/src/com/limelight/binding/crypto/AndroidCryptoProvider.java index 242749f3..01d62db6 100644 --- a/src/com/limelight/binding/crypto/AndroidCryptoProvider.java +++ b/src/com/limelight/binding/crypto/AndroidCryptoProvider.java @@ -23,15 +23,16 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Calendar; import java.util.Date; +import java.util.Locale; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.X500NameBuilder; import org.bouncycastle.asn1.x500.style.BCStyle; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.PEMWriter; +import org.bouncycastle.openssl.jcajce.JcaPEMWriter; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; @@ -152,7 +153,8 @@ public class AndroidCryptoProvider implements LimelightCryptoProvider { nameBuilder.addRDN(BCStyle.CN, "NVIDIA GameStream Client"); X500Name name = nameBuilder.build(); - X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(name, serial, now, expirationDate, name, keyPair.getPublic()); + X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(name, serial, now, expirationDate, Locale.ENGLISH, name, + SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded())); try { ContentSigner sigGen = new JcaContentSignerBuilder("SHA1withRSA").setProvider(BouncyCastleProvider.PROVIDER_NAME).build(keyPair.getPrivate()); @@ -179,7 +181,7 @@ public class AndroidCryptoProvider implements LimelightCryptoProvider { // Write the certificate in OpenSSL PEM format (important for the server) StringWriter strWriter = new StringWriter(); - PEMWriter pemWriter = new PEMWriter(strWriter); + JcaPEMWriter pemWriter = new JcaPEMWriter(strWriter); pemWriter.writeObject(cert); pemWriter.close(); diff --git a/src/com/limelight/binding/input/evdev/EvdevReader.java b/src/com/limelight/binding/input/evdev/EvdevReader.java index a2d7810c..eb0cfda9 100644 --- a/src/com/limelight/binding/input/evdev/EvdevReader.java +++ b/src/com/limelight/binding/input/evdev/EvdevReader.java @@ -3,6 +3,7 @@ package com.limelight.binding.input.evdev; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; +import java.util.Locale; import com.limelight.LimeLog; @@ -20,7 +21,7 @@ public class EvdevReader { OutputStream stdin = p.getOutputStream(); for (String file : files) { - stdin.write(String.format("chmod %o %s\n", octalPermissions, file).getBytes("UTF-8")); + stdin.write(String.format((Locale)null, "chmod %o %s\n", octalPermissions, file).getBytes("UTF-8")); } stdin.write("exit\n".getBytes("UTF-8")); stdin.flush(); diff --git a/src/com/limelight/binding/video/MediaCodecDecoderRenderer.java b/src/com/limelight/binding/video/MediaCodecDecoderRenderer.java index 2a96821c..57c4516c 100644 --- a/src/com/limelight/binding/video/MediaCodecDecoderRenderer.java +++ b/src/com/limelight/binding/video/MediaCodecDecoderRenderer.java @@ -3,6 +3,7 @@ package com.limelight.binding.video; import java.nio.ByteBuffer; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.concurrent.locks.LockSupport; import org.jcodec.codecs.h264.io.model.SeqParameterSet; @@ -53,9 +54,6 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer { static { preferredDecoders = new LinkedList(); - - // This is the most reliable of Samsung's decoders - preferredDecoders.add("OMX.SEC.AVC.Decoder"); } static { @@ -317,21 +315,62 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer { @Override public void run() { BufferInfo info = new BufferInfo(); - DecodeUnit du; + DecodeUnit du = null; + int inputIndex = -1; while (!isInterrupted()) { - du = depacketizer.pollNextDecodeUnit(); - if (du != null) { - if (!submitDecodeUnit(du)) { - // Thread was interrupted - depacketizer.freeDecodeUnit(du); - return; - } - else { - depacketizer.freeDecodeUnit(du); + // In order to get as much data to the decoder as early as possible, + // try to submit up to 5 decode units at once without blocking. + if (inputIndex == -1 && du == null) { + for (int i = 0; i < 5; i++) { + inputIndex = videoDecoder.dequeueInputBuffer(0); + du = depacketizer.pollNextDecodeUnit(); + + // Stop if we can't get a DU or input buffer + if (du == null || inputIndex == -1) { + break; + } + + submitDecodeUnit(du, inputIndex); + + du = null; + inputIndex = -1; } } + // Grab an input buffer if we don't have one already. + // This way we can have one ready hopefully by the time + // the depacketizer is done with this frame. It's important + // that this can timeout because it's possible that we could exhaust + // the decoder's input buffers and deadlocks because aren't pulling + // frames out of the other end. + if (inputIndex == -1) { + try { + // If we've got a DU waiting to be given to the decoder, + // wait a full 3 ms for an input buffer. Otherwise + // just see if we can get one immediately. + inputIndex = videoDecoder.dequeueInputBuffer(du != null ? 3000 : 0); + } catch (Exception e) { + throw new RendererException(MediaCodecDecoderRenderer.this, e); + } + } + + // Grab a decode unit if we don't have one already + if (du == null) { + du = depacketizer.pollNextDecodeUnit(); + } + + // If we've got both a decode unit and an input buffer, we'll + // submit now. Otherwise, we wait until we have one. + if (du != null && inputIndex >= 0) { + submitDecodeUnit(du, inputIndex); + + // DU and input buffer have both been consumed + du = null; + inputIndex = -1; + } + + // Try to output a frame try { int outIndex = videoDecoder.dequeueOutputBuffer(info, 0); @@ -358,7 +397,11 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer { } else { switch (outIndex) { case MediaCodec.INFO_TRY_AGAIN_LATER: - LockSupport.parkNanos(1); + // Getting an input buffer may already block + // so don't park if we still need to do that + if (inputIndex >= 0) { + LockSupport.parkNanos(1); + } break; case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: LimeLog.info("Output buffers changed"); @@ -414,22 +457,8 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer { } } - private boolean submitDecodeUnit(DecodeUnit decodeUnit) { - int inputIndex; - - do { - if (Thread.interrupted()) { - return false; - } - - try { - inputIndex = videoDecoder.dequeueInputBuffer(100000); - } catch (Exception e) { - throw new RendererException(this, e); - } - } while (inputIndex < 0); - - ByteBuffer buf = videoDecoderInputBuffers[inputIndex]; + private void submitDecodeUnit(DecodeUnit decodeUnit, int inputBufferIndex) { + ByteBuffer buf = videoDecoderInputBuffers[inputBufferIndex]; long currentTime = System.currentTimeMillis(); long delta = currentTime-decodeUnit.getReceiveTimestamp(); @@ -492,13 +521,15 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer { sps.write(buf); try { - videoDecoder.queueInputBuffer(inputIndex, + videoDecoder.queueInputBuffer(inputBufferIndex, 0, buf.position(), currentTime * 1000, codecFlags); } catch (Exception e) { throw new RendererException(this, e, buf, codecFlags); } - return true; + + depacketizer.freeDecodeUnit(decodeUnit); + return; } } else if (header.data[header.offset+4] == 0x68) { numPpsIn++; @@ -512,14 +543,15 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer { } try { - videoDecoder.queueInputBuffer(inputIndex, + videoDecoder.queueInputBuffer(inputBufferIndex, 0, decodeUnit.getDataLength(), currentTime * 1000, codecFlags); } catch (Exception e) { throw new RendererException(this, e, buf, codecFlags); } - return true; + depacketizer.freeDecodeUnit(decodeUnit); + return; } @Override @@ -575,7 +607,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer { str += "Current buffer: "; currentBuffer.flip(); while (currentBuffer.hasRemaining() && currentBuffer.position() < 10) { - str += String.format("%02x ", currentBuffer.get()); + str += String.format((Locale)null, "%02x ", currentBuffer.get()); } str += "\n"; str += "Buffer codec flags: "+currentCodecFlags+"\n"; diff --git a/src/com/limelight/computers/ComputerDatabaseManager.java b/src/com/limelight/computers/ComputerDatabaseManager.java index e83649db..78630166 100644 --- a/src/com/limelight/computers/ComputerDatabaseManager.java +++ b/src/com/limelight/computers/ComputerDatabaseManager.java @@ -4,6 +4,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.UUID; import com.limelight.LimeLog; @@ -44,7 +45,7 @@ public class ComputerDatabaseManager { private void initializeDb() { // Create tables if they aren't already there - computerDb.execSQL(String.format("CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY," + + computerDb.execSQL(String.format((Locale)null, "CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY," + " %s TEXT NOT NULL, %s TEXT NOT NULL, %s TEXT NOT NULL, %s TEXT NOT NULL)", COMPUTER_TABLE_NAME, COMPUTER_NAME_COLUMN_NAME, COMPUTER_UUID_COLUMN_NAME, LOCAL_IP_COLUMN_NAME, diff --git a/src/com/limelight/computers/ComputerManagerService.java b/src/com/limelight/computers/ComputerManagerService.java index 456d32df..de264b49 100644 --- a/src/com/limelight/computers/ComputerManagerService.java +++ b/src/com/limelight/computers/ComputerManagerService.java @@ -1,11 +1,6 @@ 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; @@ -24,11 +19,8 @@ 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; @@ -234,93 +226,6 @@ 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(), @@ -373,26 +278,7 @@ public class ComputerManagerService extends Service { } 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); - } + return pollComputer(details, true); } private Runnable getPollingRunnable(final ComputerDetails details) { diff --git a/src/com/limelight/computers/IdentityManager.java b/src/com/limelight/computers/IdentityManager.java index db463852..ab33ee35 100644 --- a/src/com/limelight/computers/IdentityManager.java +++ b/src/com/limelight/computers/IdentityManager.java @@ -4,6 +4,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; +import java.util.Locale; import java.util.Random; import com.limelight.LimeLog; @@ -61,7 +62,7 @@ public class IdentityManager { private static String generateNewUniqueId(Context c) { // Generate a new UID hex string LimeLog.info("Generating new UID"); - String uidStr = String.format("%016x", new Random().nextLong()); + String uidStr = String.format((Locale)null, "%016x", new Random().nextLong()); OutputStreamWriter writer = null; try {