Compare commits

..
5 Commits
6 changed files with 117 additions and 48 deletions

No files matched your search

+2 -2
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.limelight" package="com.limelight"
android:versionCode="30" android:versionCode="31"
android:versionName="2.5.3" > android:versionName="2.5.3.1" >
<uses-sdk <uses-sdk
android:minSdkVersion="16" 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. 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. 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. 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 - 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 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.
@@ -9,6 +9,7 @@ import android.view.MotionEvent;
import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.input.ControllerPacket; import com.limelight.nvstream.input.ControllerPacket;
import com.limelight.utils.Vector2d;
public class ControllerHandler { public class ControllerHandler {
private short inputMap = 0x0000; 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_SPECIAL_UP_DELAY_MS = 100;
private static final int EMULATED_SELECT_UP_DELAY_MS = 30; 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 HashMap<String, ControllerMapping> mappings = new HashMap<String, ControllerMapping>();
private NvConnection conn; private NvConnection conn;
@@ -129,22 +133,16 @@ public class ControllerHandler {
if (mapping.leftStickXAxis != -1 && mapping.leftStickYAxis != -1) { if (mapping.leftStickXAxis != -1 && mapping.leftStickYAxis != -1) {
InputDevice.MotionRange lsXRange = dev.getMotionRange(mapping.leftStickXAxis); InputDevice.MotionRange lsXRange = dev.getMotionRange(mapping.leftStickXAxis);
InputDevice.MotionRange lsYRange = dev.getMotionRange(mapping.leftStickYAxis); InputDevice.MotionRange lsYRange = dev.getMotionRange(mapping.leftStickYAxis);
if (lsXRange != null) { if (lsXRange != null && lsYRange != null) {
mapping.leftStickXAxisDeadzone = lsXRange.getFlat(); mapping.leftStickDeadzoneRadius = Math.max(lsXRange.getFlat(), lsYRange.getFlat());
}
if (lsYRange != null) {
mapping.leftStickYAxisDeadzone = lsYRange.getFlat();
} }
} }
if (mapping.rightStickXAxis != -1 && mapping.rightStickYAxis != -1) { if (mapping.rightStickXAxis != -1 && mapping.rightStickYAxis != -1) {
InputDevice.MotionRange rsXRange = dev.getMotionRange(mapping.rightStickXAxis); InputDevice.MotionRange rsXRange = dev.getMotionRange(mapping.rightStickXAxis);
InputDevice.MotionRange rsYRange = dev.getMotionRange(mapping.rightStickYAxis); InputDevice.MotionRange rsYRange = dev.getMotionRange(mapping.rightStickYAxis);
if (rsXRange != null) { if (rsXRange != null && rsYRange != null) {
mapping.rightStickXAxisDeadzone = rsXRange.getFlat(); mapping.rightStickDeadzoneRadius = Math.max(rsXRange.getFlat(), rsYRange.getFlat());
}
if (rsYRange != null) {
mapping.rightStickYAxisDeadzone = rsYRange.getFlat();
} }
} }
@@ -232,6 +230,39 @@ public class ControllerHandler {
return keyCode; 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) { public boolean handleMotionEvent(MotionEvent event) {
ControllerMapping mapping = getMappingForDevice(event.getDevice()); ControllerMapping mapping = getMappingForDevice(event.getDevice());
if (mapping == null) { if (mapping == null) {
@@ -240,30 +271,20 @@ public class ControllerHandler {
// Handle left stick events outside of the deadzone // Handle left stick events outside of the deadzone
if (mapping.leftStickXAxis != -1 && mapping.leftStickYAxis != -1) { if (mapping.leftStickXAxis != -1 && mapping.leftStickYAxis != -1) {
float LS_X = event.getAxisValue(mapping.leftStickXAxis); Vector2d leftStickVector = handleDeadZone(event.getAxisValue(mapping.leftStickXAxis),
float LS_Y = event.getAxisValue(mapping.leftStickYAxis); event.getAxisValue(mapping.leftStickYAxis), mapping.leftStickDeadzoneRadius);
if (LS_X >= -mapping.leftStickXAxisDeadzone && LS_X <= mapping.leftStickXAxisDeadzone) {
LS_X = 0; leftStickX = (short)(leftStickVector.getX() * 0x7FFE);
} leftStickY = (short)(-leftStickVector.getY() * 0x7FFE);
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);
} }
// Handle right stick events outside of the deadzone // Handle right stick events outside of the deadzone
if (mapping.rightStickXAxis != -1 && mapping.rightStickYAxis != -1) { if (mapping.rightStickXAxis != -1 && mapping.rightStickYAxis != -1) {
float RS_X = event.getAxisValue(mapping.rightStickXAxis); Vector2d rightStickVector = handleDeadZone(event.getAxisValue(mapping.rightStickXAxis),
float RS_Y = event.getAxisValue(mapping.rightStickYAxis); event.getAxisValue(mapping.rightStickYAxis), mapping.rightStickDeadzoneRadius);
if (RS_X >= -mapping.rightStickXAxisDeadzone && RS_X <= mapping.rightStickXAxisDeadzone) {
RS_X = 0; rightStickX = (short)(rightStickVector.getX() * 0x7FFE);
} rightStickY = (short)(-rightStickVector.getY() * 0x7FFE);
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);
} }
// Handle controllers with analog triggers // Handle controllers with analog triggers
@@ -276,8 +297,8 @@ public class ControllerHandler {
R2 = (R2 + 1) / 2; R2 = (R2 + 1) / 2;
} }
leftTrigger = (byte)Math.round(L2 * 0xFF); leftTrigger = (byte)(L2 * 0xFF);
rightTrigger = (byte)Math.round(R2 * 0xFF); rightTrigger = (byte)(R2 * 0xFF);
} }
// Hats emulate d-pad events // Hats emulate d-pad events
@@ -527,17 +548,13 @@ public class ControllerHandler {
} }
class ControllerMapping { class ControllerMapping {
public int leftStickXAxis = -1; public int leftStickXAxis = -1;
public float leftStickXAxisDeadzone;
public int leftStickYAxis = -1; public int leftStickYAxis = -1;
public float leftStickYAxisDeadzone; public float leftStickDeadzoneRadius;
public int rightStickXAxis = -1; public int rightStickXAxis = -1;
public float rightStickXAxisDeadzone;
public int rightStickYAxis = -1; public int rightStickYAxis = -1;
public float rightStickYAxisDeadzone; public float rightStickDeadzoneRadius;
public int leftTriggerAxis = -1; public int leftTriggerAxis = -1;
public int rightTriggerAxis = -1; public int rightTriggerAxis = -1;
@@ -36,7 +36,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
private final static byte[] BITSTREAM_RESTRICTIONS = new byte[] {(byte) 0xF1, (byte) 0x83, 0x2A, 0x00}; 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> blacklistedDecoderPrefixes;
public static final List<String> spsFixupBitsreamFixupDecoderPrefixes; public static final List<String> spsFixupBitstreamFixupDecoderPrefixes;
public static final List<String> spsFixupNumRefFixupDecoderPrefixes; public static final List<String> spsFixupNumRefFixupDecoderPrefixes;
static { static {
@@ -46,13 +46,15 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
} }
static { static {
spsFixupBitsreamFixupDecoderPrefixes = new LinkedList<String>(); spsFixupBitstreamFixupDecoderPrefixes = new LinkedList<String>();
spsFixupBitsreamFixupDecoderPrefixes.add("omx.nvidia"); spsFixupBitstreamFixupDecoderPrefixes.add("omx.nvidia");
spsFixupBitsreamFixupDecoderPrefixes.add("omx.qcom"); spsFixupBitstreamFixupDecoderPrefixes.add("omx.qcom");
spsFixupBitstreamFixupDecoderPrefixes.add("omx.sec");
spsFixupNumRefFixupDecoderPrefixes = new LinkedList<String>(); spsFixupNumRefFixupDecoderPrefixes = new LinkedList<String>();
spsFixupNumRefFixupDecoderPrefixes.add("omx.TI"); spsFixupNumRefFixupDecoderPrefixes.add("omx.TI");
spsFixupNumRefFixupDecoderPrefixes.add("omx.qcom"); spsFixupNumRefFixupDecoderPrefixes.add("omx.qcom");
spsFixupNumRefFixupDecoderPrefixes.add("omx.sec");
} }
private static boolean isDecoderInList(List<String> decoderList, String decoderName) { private static boolean isDecoderInList(List<String> decoderList, String decoderName) {
@@ -137,7 +139,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
MediaCodecInfo safeDecoder = findSafeDecoder(); MediaCodecInfo safeDecoder = findSafeDecoder();
if (safeDecoder != null) { if (safeDecoder != null) {
videoDecoder = MediaCodec.createByCodecName(safeDecoder.getName()); videoDecoder = MediaCodec.createByCodecName(safeDecoder.getName());
needsSpsBitstreamFixup = isDecoderInList(spsFixupBitsreamFixupDecoderPrefixes, safeDecoder.getName()); needsSpsBitstreamFixup = isDecoderInList(spsFixupBitstreamFixupDecoderPrefixes, safeDecoder.getName());
needsSpsNumRefFixup = isDecoderInList(spsFixupNumRefFixupDecoderPrefixes, safeDecoder.getName()); needsSpsNumRefFixup = isDecoderInList(spsFixupNumRefFixupDecoderPrefixes, safeDecoder.getName());
if (needsSpsBitstreamFixup) { if (needsSpsBitstreamFixup) {
LimeLog.info("Decoder "+safeDecoder.getName()+" needs SPS bitstream restrictions fixup"); LimeLog.info("Decoder "+safeDecoder.getName()+" needs SPS bitstream restrictions fixup");
+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;
}
}