Compare commits

..

18 Commits

Author SHA1 Message Date
Cameron Gutman 08cc93d337 Update common and increment version 2014-08-29 16:06:55 -07:00
Cameron Gutman 5fdd9b773c Workaround decoder errata of the Exynos 4 2014-08-29 15:38:08 -07:00
Cameron Gutman e3e7ac1e68 Remove rounding on triggers 2014-08-20 22:14:03 -07:00
Cameron Gutman 10212bd38b Fix potential integer overflow issue with stick axes 2014-08-20 22:12:45 -07:00
Cameron Gutman 4bec02f47f Implement a scaled radial deadzone rather than our previous dumb axial deadzones 2014-08-20 22:10:59 -07:00
Cameron Gutman 79f888fe47 Update common 2014-08-09 03:41:03 -07:00
Cameron Gutman 9393bf7f79 Fix a regression in video scaling after the CPU decoding fix 2014-08-06 22:33:41 -07:00
Cameron Gutman 1b1d4399a9 Bump version and update common 2014-08-06 17:24:21 -07:00
Cameron Gutman b2ad259a7c Also add bitstream restrictions to Qualcomm devices to hopefully address the GS3 issue 2014-08-06 17:22:41 -07:00
Cameron Gutman ac5c264090 Only close spinner dialogs corresponding to the current activity 2014-08-06 15:05:26 -07:00
Cameron Gutman fcfdd4e323 Add missing Cursor.close() calls to fix a crash reported on Ouya 2014-08-06 15:01:18 -07:00
Cameron Gutman ea65bb2c0a Remove the extra app list update when the activity is first started 2014-08-06 14:30:19 -07:00
Cameron Gutman b340055588 If the activity is being finished, don't dismiss the dialog. This is already handled in the closeDialogs() function. 2014-08-06 14:26:13 -07:00
Cameron Gutman 2918039b6f Fix a bug where we'd add a null entry to the computer list 2014-08-05 23:44:04 -07:00
Cameron Gutman e8fd1f262a Update common jar 2014-08-05 23:19:37 -07:00
Cameron Gutman 8887401644 Fix surface sizing with scaling disabled on the CPU decoder 2014-08-05 23:16:02 -07:00
Cameron Gutman f892db6ee8 Fix ANR when switching from the PC view to the app view 2014-08-05 22:30:11 -07:00
Cameron Gutman 829532c572 Some devices don't properly get interrupted while waiting for an input buffer, so wait 100 ms then check the interrupt flag 2014-08-05 21:29:21 -07:00
12 changed files with 304 additions and 162 deletions
+2 -2
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.limelight"
android:versionCode="29"
android:versionName="2.5.2.1" >
android:versionCode="31"
android:versionName="2.5.3.1" >
<uses-sdk
android:minSdkVersion="16"
+5 -2
View File
@@ -1,10 +1,13 @@
This file serves to document some of the decoder errata when using MediaCodec hardware decoders on certain devices.
1. num_ref_frames is set to 16 by NVENC which causes decoders to allocate 16+ buffers. This can cause an OOM error on some devices.
- Affected decoders: TI OMAP4, possibly some Qualcomm chips too (Galaxy S3 on 4.3+)
- Affected decoders: TI OMAP4
2. Some decoders have a huge per-frame latency with the unmodified SPS sent from NVENC. Setting max_dec_frame_buffering fixes this latency issue.
- Affected decoders: NVIDIA Tegra 3 and 4
3. Some decoders strictly require that you pass BUFFER_FLAG_CODEC_CONFIG and crash upon the IDR frame if you don't
- Affected decoders: TI OMAP4
- Affected decoders: TI OMAP4
4. Some decoders require num_ref_frames=1 and max_dec_frame_buffering=1 to avoid crashing on SPS or first I-frame
- Affected decoders: Qualcomm in GS3 on 4.3+, Exynos 4
Binary file not shown.
+1 -1
View File
@@ -91,13 +91,13 @@ public class AppView extends Activity {
}
});
registerForContextMenu(appList);
updateAppList();
}
@Override
protected void onDestroy() {
super.onDestroy();
SpinnerDialog.closeDialogs(this);
Dialog.closeDialogs();
}
+10 -9
View File
@@ -164,13 +164,6 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
SurfaceView sv = (SurfaceView) findViewById(R.id.surfaceView);
sv.setOnGenericMotionListener(this);
sv.setOnTouchListener(this);
SurfaceHolder sh = sv.getHolder();
if (stretchToFit) {
// Set the surface to the size of the video
sh.setFixedSize(width, height);
}
// Warn the user if they're on a metered connection
checkDataConnection();
@@ -191,7 +184,15 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
PlatformBinding.getCryptoProvider(this));
keybTranslator = new KeyboardTranslator(conn);
controllerHandler = new ControllerHandler(conn);
decoderRenderer = new ConfigurableDecoderRenderer();
decoderRenderer.initializeWithFlags(drFlags);
SurfaceHolder sh = sv.getHolder();
if (stretchToFit || !decoderRenderer.isHardwareAccelerated()) {
// Set the surface to the size of the video
sh.setFixedSize(width, height);
}
// The connection will be started when the surface gets created
sh.addCallback(this);
@@ -254,7 +255,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
protected void onStop() {
super.onStop();
SpinnerDialog.closeDialogs();
SpinnerDialog.closeDialogs(this);
Dialog.closeDialogs();
displayedFailureDialog = true;
@@ -610,7 +611,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
// Resize the surface to match the aspect ratio of the video
// This must be done after the surface is created.
if (!stretchToFit) {
if (!stretchToFit && decoderRenderer.isHardwareAccelerated()) {
resizeSurfaceWithAspectRatio((SurfaceView) findViewById(R.id.surfaceView), width, height);
}
@@ -9,6 +9,7 @@ import android.view.MotionEvent;
import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.input.ControllerPacket;
import com.limelight.utils.Vector2d;
public class ControllerHandler {
private short inputMap = 0x0000;
@@ -37,6 +38,9 @@ public class ControllerHandler {
private static final int EMULATED_SPECIAL_UP_DELAY_MS = 100;
private static final int EMULATED_SELECT_UP_DELAY_MS = 30;
private Vector2d inputVector = new Vector2d();
private Vector2d normalizedInputVector = new Vector2d();
private HashMap<String, ControllerMapping> mappings = new HashMap<String, ControllerMapping>();
private NvConnection conn;
@@ -129,22 +133,16 @@ public class ControllerHandler {
if (mapping.leftStickXAxis != -1 && mapping.leftStickYAxis != -1) {
InputDevice.MotionRange lsXRange = dev.getMotionRange(mapping.leftStickXAxis);
InputDevice.MotionRange lsYRange = dev.getMotionRange(mapping.leftStickYAxis);
if (lsXRange != null) {
mapping.leftStickXAxisDeadzone = lsXRange.getFlat();
}
if (lsYRange != null) {
mapping.leftStickYAxisDeadzone = lsYRange.getFlat();
if (lsXRange != null && lsYRange != null) {
mapping.leftStickDeadzoneRadius = Math.max(lsXRange.getFlat(), lsYRange.getFlat());
}
}
if (mapping.rightStickXAxis != -1 && mapping.rightStickYAxis != -1) {
InputDevice.MotionRange rsXRange = dev.getMotionRange(mapping.rightStickXAxis);
InputDevice.MotionRange rsYRange = dev.getMotionRange(mapping.rightStickYAxis);
if (rsXRange != null) {
mapping.rightStickXAxisDeadzone = rsXRange.getFlat();
}
if (rsYRange != null) {
mapping.rightStickYAxisDeadzone = rsYRange.getFlat();
if (rsXRange != null && rsYRange != null) {
mapping.rightStickDeadzoneRadius = Math.max(rsXRange.getFlat(), rsYRange.getFlat());
}
}
@@ -232,6 +230,39 @@ public class ControllerHandler {
return keyCode;
}
private Vector2d handleDeadZone(float x, float y, float deadzoneRadius) {
// Reinitialize our cached Vector2d object
inputVector.initialize(x, y);
if (inputVector.getMagnitude() <= deadzoneRadius) {
// Deadzone -- return the zero vector
return Vector2d.ZERO;
}
else {
// Scale the input based on the distance from the deadzone
inputVector.getNormalized(normalizedInputVector);
normalizedInputVector.scalarMultiply((inputVector.getMagnitude() - deadzoneRadius) / (1.0f - deadzoneRadius));
// Bound the X value to -1.0 to 1.0
if (normalizedInputVector.getX() > 1.0f) {
normalizedInputVector.setX(1.0f);
}
else if (normalizedInputVector.getX() < -1.0f) {
normalizedInputVector.setX(-1.0f);
}
// Bound the Y value to -1.0 to 1.0
if (normalizedInputVector.getY() > 1.0f) {
normalizedInputVector.setY(1.0f);
}
else if (normalizedInputVector.getY() < -1.0f) {
normalizedInputVector.setY(-1.0f);
}
return normalizedInputVector;
}
}
public boolean handleMotionEvent(MotionEvent event) {
ControllerMapping mapping = getMappingForDevice(event.getDevice());
if (mapping == null) {
@@ -240,30 +271,20 @@ public class ControllerHandler {
// Handle left stick events outside of the deadzone
if (mapping.leftStickXAxis != -1 && mapping.leftStickYAxis != -1) {
float LS_X = event.getAxisValue(mapping.leftStickXAxis);
float LS_Y = event.getAxisValue(mapping.leftStickYAxis);
if (LS_X >= -mapping.leftStickXAxisDeadzone && LS_X <= mapping.leftStickXAxisDeadzone) {
LS_X = 0;
}
if (LS_Y >= -mapping.leftStickYAxisDeadzone && LS_Y <= mapping.leftStickYAxisDeadzone) {
LS_Y = 0;
}
leftStickX = (short)Math.round(LS_X * 0x7FFF);
leftStickY = (short)Math.round(-LS_Y * 0x7FFF);
Vector2d leftStickVector = handleDeadZone(event.getAxisValue(mapping.leftStickXAxis),
event.getAxisValue(mapping.leftStickYAxis), mapping.leftStickDeadzoneRadius);
leftStickX = (short)(leftStickVector.getX() * 0x7FFE);
leftStickY = (short)(-leftStickVector.getY() * 0x7FFE);
}
// Handle right stick events outside of the deadzone
if (mapping.rightStickXAxis != -1 && mapping.rightStickYAxis != -1) {
float RS_X = event.getAxisValue(mapping.rightStickXAxis);
float RS_Y = event.getAxisValue(mapping.rightStickYAxis);
if (RS_X >= -mapping.rightStickXAxisDeadzone && RS_X <= mapping.rightStickXAxisDeadzone) {
RS_X = 0;
}
if (RS_Y >= -mapping.rightStickYAxisDeadzone && RS_Y <= mapping.rightStickYAxisDeadzone) {
RS_Y = 0;
}
rightStickX = (short)Math.round(RS_X * 0x7FFF);
rightStickY = (short)Math.round(-RS_Y * 0x7FFF);
Vector2d rightStickVector = handleDeadZone(event.getAxisValue(mapping.rightStickXAxis),
event.getAxisValue(mapping.rightStickYAxis), mapping.rightStickDeadzoneRadius);
rightStickX = (short)(rightStickVector.getX() * 0x7FFE);
rightStickY = (short)(-rightStickVector.getY() * 0x7FFE);
}
// Handle controllers with analog triggers
@@ -276,8 +297,8 @@ public class ControllerHandler {
R2 = (R2 + 1) / 2;
}
leftTrigger = (byte)Math.round(L2 * 0xFF);
rightTrigger = (byte)Math.round(R2 * 0xFF);
leftTrigger = (byte)(L2 * 0xFF);
rightTrigger = (byte)(R2 * 0xFF);
}
// Hats emulate d-pad events
@@ -527,17 +548,13 @@ public class ControllerHandler {
}
class ControllerMapping {
public int leftStickXAxis = -1;
public float leftStickXAxisDeadzone;
public int leftStickXAxis = -1;
public int leftStickYAxis = -1;
public float leftStickYAxisDeadzone;
public float leftStickDeadzoneRadius;
public int rightStickXAxis = -1;
public float rightStickXAxisDeadzone;
public int rightStickYAxis = -1;
public float rightStickYAxisDeadzone;
public float rightStickDeadzoneRadius;
public int leftTriggerAxis = -1;
public int rightTriggerAxis = -1;
@@ -16,15 +16,28 @@ public class ConfigurableDecoderRenderer implements VideoDecoderRenderer {
@Override
public boolean setup(int width, int height, int redrawRate, Object renderTarget, int drFlags) {
if (decoderRenderer == null) {
throw new IllegalStateException("ConfigurableDecoderRenderer not initialized");
}
return decoderRenderer.setup(width, height, redrawRate, renderTarget, drFlags);
}
public void initializeWithFlags(int drFlags) {
if ((drFlags & VideoDecoderRenderer.FLAG_FORCE_HARDWARE_DECODING) != 0 ||
((drFlags & VideoDecoderRenderer.FLAG_FORCE_SOFTWARE_DECODING) == 0 &&
MediaCodecDecoderRenderer.findSafeDecoder() != null)) {
((drFlags & VideoDecoderRenderer.FLAG_FORCE_SOFTWARE_DECODING) == 0 &&
MediaCodecDecoderRenderer.findSafeDecoder() != null)) {
decoderRenderer = new MediaCodecDecoderRenderer();
}
else {
decoderRenderer = new AndroidCpuDecoderRenderer();
}
return decoderRenderer.setup(width, height, redrawRate, renderTarget, drFlags);
}
public boolean isHardwareAccelerated() {
if (decoderRenderer == null) {
throw new IllegalStateException("ConfigurableDecoderRenderer not initialized");
}
return (decoderRenderer instanceof MediaCodecDecoderRenderer);
}
@Override
@@ -36,7 +36,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
private final static byte[] BITSTREAM_RESTRICTIONS = new byte[] {(byte) 0xF1, (byte) 0x83, 0x2A, 0x00};
public static final List<String> blacklistedDecoderPrefixes;
public static final List<String> spsFixupBitsreamFixupDecoderPrefixes;
public static final List<String> spsFixupBitstreamFixupDecoderPrefixes;
public static final List<String> spsFixupNumRefFixupDecoderPrefixes;
static {
@@ -46,14 +46,17 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
}
static {
spsFixupBitsreamFixupDecoderPrefixes = new LinkedList<String>();
spsFixupBitsreamFixupDecoderPrefixes.add("omx.nvidia");
spsFixupBitstreamFixupDecoderPrefixes = new LinkedList<String>();
spsFixupBitstreamFixupDecoderPrefixes.add("omx.nvidia");
spsFixupBitstreamFixupDecoderPrefixes.add("omx.qcom");
spsFixupBitstreamFixupDecoderPrefixes.add("omx.sec");
spsFixupNumRefFixupDecoderPrefixes = new LinkedList<String>();
spsFixupNumRefFixupDecoderPrefixes.add("omx.TI");
spsFixupNumRefFixupDecoderPrefixes.add("omx.qcom");
spsFixupNumRefFixupDecoderPrefixes.add("omx.sec");
}
private static boolean isDecoderInList(List<String> decoderList, String decoderName) {
for (String badPrefix : decoderList) {
if (decoderName.length() >= badPrefix.length()) {
@@ -136,7 +139,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
MediaCodecInfo safeDecoder = findSafeDecoder();
if (safeDecoder != null) {
videoDecoder = MediaCodec.createByCodecName(safeDecoder.getName());
needsSpsBitstreamFixup = isDecoderInList(spsFixupBitsreamFixupDecoderPrefixes, safeDecoder.getName());
needsSpsBitstreamFixup = isDecoderInList(spsFixupBitstreamFixupDecoderPrefixes, safeDecoder.getName());
needsSpsNumRefFixup = isDecoderInList(spsFixupNumRefFixupDecoderPrefixes, safeDecoder.getName());
if (needsSpsBitstreamFixup) {
LimeLog.info("Decoder "+safeDecoder.getName()+" needs SPS bitstream restrictions fixup");
@@ -177,8 +180,14 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
{
du = depacketizer.pollNextDecodeUnit();
if (du != null) {
submitDecodeUnit(du);
depacketizer.freeDecodeUnit(du);
if (!submitDecodeUnit(du)) {
// Thread was interrupted
depacketizer.freeDecodeUnit(du);
return;
}
else {
depacketizer.freeDecodeUnit(du);
}
}
int outIndex = videoDecoder.dequeueOutputBuffer(info, 0);
@@ -250,105 +259,111 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
}
private boolean submitDecodeUnit(DecodeUnit decodeUnit) {
int inputIndex = videoDecoder.dequeueInputBuffer(-1);
if (inputIndex >= 0)
{
ByteBuffer buf = videoDecoderInputBuffers[inputIndex];
long currentTime = System.currentTimeMillis();
long delta = currentTime-decodeUnit.getReceiveTimestamp();
if (delta >= 0 && delta < 300) {
totalTimeMs += currentTime-decodeUnit.getReceiveTimestamp();
totalFrames++;
int inputIndex;
do {
if (Thread.interrupted()) {
return false;
}
// Clear old input data
buf.clear();
int codecFlags = 0;
int decodeUnitFlags = decodeUnit.getFlags();
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_CODEC_CONFIG) != 0) {
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
}
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_SYNC_FRAME) != 0) {
codecFlags |= MediaCodec.BUFFER_FLAG_SYNC_FRAME;
}
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_CODEC_CONFIG) != 0 &&
(needsSpsBitstreamFixup || needsSpsNumRefFixup)) {
ByteBufferDescriptor header = decodeUnit.getBufferList().get(0);
if (header.data[header.offset+4] == 0x67) {
byte last = header.data[header.length+header.offset-1];
inputIndex = videoDecoder.dequeueInputBuffer(100000);
} while (inputIndex < 0);
ByteBuffer buf = videoDecoderInputBuffers[inputIndex];
// TI OMAP4 requires a reference frame count of 1 to decode successfully
if (needsSpsNumRefFixup) {
LimeLog.info("Fixing up num ref frames");
this.replace(header, 80, 9, new byte[] {0x40}, 3);
}
long currentTime = System.currentTimeMillis();
long delta = currentTime-decodeUnit.getReceiveTimestamp();
if (delta >= 0 && delta < 300) {
totalTimeMs += currentTime-decodeUnit.getReceiveTimestamp();
totalFrames++;
}
// Clear old input data
buf.clear();
int codecFlags = 0;
int decodeUnitFlags = decodeUnit.getFlags();
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_CODEC_CONFIG) != 0) {
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
}
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_SYNC_FRAME) != 0) {
codecFlags |= MediaCodec.BUFFER_FLAG_SYNC_FRAME;
}
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_CODEC_CONFIG) != 0 &&
(needsSpsBitstreamFixup || needsSpsNumRefFixup)) {
ByteBufferDescriptor header = decodeUnit.getBufferList().get(0);
if (header.data[header.offset+4] == 0x67) {
byte last = header.data[header.length+header.offset-1];
// The SPS that comes in the current H264 bytestream doesn't set bitstream_restriction_flag
// or max_dec_frame_buffering which increases decoding latency on Tegra.
// We manually modify the SPS here to speed-up decoding if the decoder was flagged as needing it.
int spsLength;
if (needsSpsBitstreamFixup) {
if (!needsSpsNumRefFixup) {
switch (header.length) {
case 26:
LimeLog.info("Adding bitstream restrictions to SPS (26)");
buf.put(header.data, header.offset, 24);
buf.put((byte) 0x11);
buf.put((byte) 0xe3);
buf.put((byte) 0x06);
buf.put((byte) 0x50);
spsLength = header.length + 2;
break;
case 27:
LimeLog.info("Adding bitstream restrictions to SPS (27)");
buf.put(header.data, header.offset, 25);
buf.put((byte) 0x04);
buf.put((byte) 0x78);
buf.put((byte) 0xc1);
buf.put((byte) 0x94);
spsLength = header.length + 2;
break;
default:
LimeLog.warning("Unknown SPS of length "+header.length);
buf.put(header.data, header.offset, header.length);
spsLength = header.length;
break;
}
}
else {
// Set bitstream restrictions to only buffer single frame
// (starts 9 bits before stop bit and 6 bits earlier because of the shortening above)
this.replace(header, header.length*8+Integer.numberOfLeadingZeros(last & - last)%8-9-6, 2, BITSTREAM_RESTRICTIONS, 3*8);
// TI OMAP4 requires a reference frame count of 1 to decode successfully
if (needsSpsNumRefFixup) {
LimeLog.info("Fixing up num ref frames");
this.replace(header, 80, 9, new byte[] {0x40}, 3);
}
// The SPS that comes in the current H264 bytestream doesn't set bitstream_restriction_flag
// or max_dec_frame_buffering which increases decoding latency on Tegra.
// We manually modify the SPS here to speed-up decoding if the decoder was flagged as needing it.
int spsLength;
if (needsSpsBitstreamFixup) {
if (!needsSpsNumRefFixup) {
switch (header.length) {
case 26:
LimeLog.info("Adding bitstream restrictions to SPS (26)");
buf.put(header.data, header.offset, 24);
buf.put((byte) 0x11);
buf.put((byte) 0xe3);
buf.put((byte) 0x06);
buf.put((byte) 0x50);
spsLength = header.length + 2;
break;
case 27:
LimeLog.info("Adding bitstream restrictions to SPS (27)");
buf.put(header.data, header.offset, 25);
buf.put((byte) 0x04);
buf.put((byte) 0x78);
buf.put((byte) 0xc1);
buf.put((byte) 0x94);
spsLength = header.length + 2;
break;
default:
LimeLog.warning("Unknown SPS of length "+header.length);
buf.put(header.data, header.offset, header.length);
spsLength = header.length;
break;
}
}
else {
// Set bitstream restrictions to only buffer single frame
// (starts 9 bits before stop bit and 6 bits earlier because of the shortening above)
this.replace(header, header.length*8+Integer.numberOfLeadingZeros(last & - last)%8-9-6, 2, BITSTREAM_RESTRICTIONS, 3*8);
buf.put(header.data, header.offset, header.length);
spsLength = header.length;
}
videoDecoder.queueInputBuffer(inputIndex,
0, spsLength,
currentTime * 1000, codecFlags);
return true;
}
}
else {
buf.put(header.data, header.offset, header.length);
spsLength = header.length;
}
// Copy data from our buffer list into the input buffer
for (ByteBufferDescriptor desc : decodeUnit.getBufferList())
{
buf.put(desc.data, desc.offset, desc.length);
videoDecoder.queueInputBuffer(inputIndex,
0, spsLength,
currentTime * 1000, codecFlags);
return true;
}
videoDecoder.queueInputBuffer(inputIndex,
0, decodeUnit.getDataLength(),
currentTime * 1000, codecFlags);
}
// Copy data from our buffer list into the input buffer
for (ByteBufferDescriptor desc : decodeUnit.getBufferList())
{
buf.put(desc.data, desc.offset, desc.length);
}
videoDecoder.queueInputBuffer(inputIndex,
0, decodeUnit.getDataLength(),
currentTime * 1000, codecFlags);
return true;
}
@@ -109,6 +109,8 @@ public class ComputerDatabaseManager {
computerList.add(details);
}
c.close();
return computerList;
}
@@ -117,6 +119,7 @@ public class ComputerDatabaseManager {
ComputerDetails details = new ComputerDetails();
if (!c.moveToFirst()) {
// No matching computer
c.close();
return null;
}
@@ -146,6 +149,8 @@ public class ComputerDatabaseManager {
details.macAddress = c.getString(4);
c.close();
// If a field is corrupt or missing, delete the database entry
if (details.uuid == null || details.localIp == null || details.remoteIp == null ||
details.macAddress == null) {
@@ -12,6 +12,7 @@ import java.util.TimerTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import com.limelight.LimeLog;
import com.limelight.binding.PlatformBinding;
@@ -39,6 +40,8 @@ public class ComputerManagerService extends Service {
private ComputerManagerBinder binder = new ComputerManagerBinder();
private ComputerDatabaseManager dbManager;
private AtomicInteger dbRefCount = new AtomicInteger(0);
private IdentityManager idManager;
private ThreadPoolExecutor pollingPool;
private Timer pollingTimer;
@@ -173,15 +176,41 @@ public class ComputerManagerService extends Service {
}
public void removeComputer(String name) {
if (!getLocalDatabaseReference()) {
return;
}
// Remove it from the database
dbManager.deleteComputer(name);
releaseLocalDatabaseReference();
}
private boolean getLocalDatabaseReference() {
if (dbRefCount.get() == 0) {
return false;
}
dbRefCount.incrementAndGet();
return true;
}
private void releaseLocalDatabaseReference() {
if (dbRefCount.decrementAndGet() == 0) {
dbManager.close();
}
}
private TimerTask getTimerTask() {
return new TimerTask() {
@Override
public void run() {
if (!getLocalDatabaseReference()) {
return;
}
List<ComputerDetails> computerList = dbManager.getAllComputers();
releaseLocalDatabaseReference();
for (ComputerDetails computer : computerList) {
pollingPool.execute(getPollingRunnable(computer));
}
@@ -356,6 +385,10 @@ public class ComputerManagerService extends Service {
public void run() {
boolean newPc = (details.name == null);
if (!getLocalDatabaseReference()) {
return;
}
// Poll the machine
if (!doPollMachine(details)) {
details.state = ComputerDetails.State.OFFLINE;
@@ -369,6 +402,7 @@ public class ComputerManagerService extends Service {
// removed after this was issued
if (dbManager.getComputerByName(details.name) == null) {
// It's gone
releaseLocalDatabaseReference();
return;
}
}
@@ -376,10 +410,12 @@ public class ComputerManagerService extends Service {
dbManager.updateComputer(details);
}
// Update anyone listening
if (listener != null) {
// Don't call the listener if this is a failed lookup of a new PC
if ((!newPc || details.state == ComputerDetails.State.ONLINE) && listener != null) {
listener.notifyComputerUpdated(details);
}
releaseLocalDatabaseReference();
}
};
}
@@ -400,6 +436,7 @@ public class ComputerManagerService extends Service {
// Initialize the DB
dbManager = new ComputerDatabaseManager(this);
dbRefCount.set(1);
}
@Override
@@ -411,12 +448,11 @@ public class ComputerManagerService extends Service {
// Stop the thread pool
pollingPool.shutdownNow();
try {
pollingPool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
} catch (InterruptedException e) {}
// Close the DB
dbManager.close();
// FIXME: Should await termination here but we have timeout issues in HttpURLConnection
// Remove the initial DB reference
releaseLocalDatabaseReference();
}
@Override
+15 -10
View File
@@ -1,6 +1,7 @@
package com.limelight.utils;
import java.util.ArrayList;
import java.util.Iterator;
import android.app.Activity;
import android.app.ProgressDialog;
@@ -31,16 +32,19 @@ public class SpinnerDialog implements Runnable,OnCancelListener {
return spinner;
}
public static void closeDialogs()
public static void closeDialogs(Activity activity)
{
synchronized (rundownDialogs) {
for (SpinnerDialog d : rundownDialogs) {
if (d.progress.isShowing()) {
d.progress.dismiss();
Iterator<SpinnerDialog> i = rundownDialogs.iterator();
while (i.hasNext()) {
SpinnerDialog dialog = i.next();
if (dialog.activity == activity) {
i.remove();
if (dialog.progress.isShowing()) {
dialog.progress.dismiss();
}
}
}
rundownDialogs.clear();
}
}
@@ -62,13 +66,14 @@ public class SpinnerDialog implements Runnable,OnCancelListener {
@Override
public void run() {
// If we're dying, don't bother doing anything
if (activity.isFinishing()) {
return;
}
if (progress == null)
{
// If we're dying, don't bother creating a dialog
if (activity.isFinishing())
return;
progress = new ProgressDialog(activity);
progress.setTitle(title);
+47
View File
@@ -0,0 +1,47 @@
package com.limelight.utils;
public class Vector2d {
private float x;
private float y;
private double magnitude;
public static final Vector2d ZERO = new Vector2d();
public Vector2d() {
initialize(0, 0);
}
public void initialize(float x, float y) {
this.x = x;
this.y = y;
this.magnitude = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
}
public double getMagnitude() {
return magnitude;
}
public void getNormalized(Vector2d vector) {
vector.initialize((float)(x / magnitude), (float)(y / magnitude));
}
public void scalarMultiply(double factor) {
initialize((float)(x * factor), (float)(y * factor));
}
public void setX(float x) {
initialize(x, this.y);
}
public void setY(float y) {
initialize(this.x, y);
}
public float getX() {
return x;
}
public float getY() {
return y;
}
}