Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 37cf572c0c | |||
| 33c5254d6f | |||
| faa82ca9d6 | |||
| 0d35ea5207 | |||
| 579645c07c | |||
| 869cbe2e81 | |||
| 329a938bf8 | |||
| 411931cc27 |
+3
-2
@@ -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="16"
|
android:versionCode="18"
|
||||||
android:versionName="2.2.1.2" >
|
android:versionName="2.4" >
|
||||||
|
|
||||||
<uses-sdk
|
<uses-sdk
|
||||||
android:minSdkVersion="16"
|
android:minSdkVersion="16"
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -1,5 +1,6 @@
|
|||||||
package com.limelight;
|
package com.limelight;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
@@ -251,7 +252,7 @@ public class Connection extends Activity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toast.makeText(Connection.this, "Pairing...", Toast.LENGTH_LONG).show();
|
Toast.makeText(Connection.this, "Pairing...", Toast.LENGTH_SHORT).show();
|
||||||
new Thread(new Runnable() {
|
new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -297,8 +298,11 @@ public class Connection extends Activity {
|
|||||||
message = null;
|
message = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (UnknownHostException e1) {
|
} catch (UnknownHostException e) {
|
||||||
message = "Failed to resolve host";
|
message = "Failed to resolve host";
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
message = "GFE returned an HTTP 404 error. Make sure your PC is running a supported GPU. Using remote desktop software can also cause this error. "
|
||||||
|
+ "Try rebooting your machine or reinstalling GFE.";
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
message = e.getMessage();
|
message = e.getMessage();
|
||||||
}
|
}
|
||||||
|
|||||||
+128
-2
@@ -1,5 +1,12 @@
|
|||||||
package com.limelight;
|
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.PlatformBinding;
|
||||||
import com.limelight.binding.input.ControllerHandler;
|
import com.limelight.binding.input.ControllerHandler;
|
||||||
import com.limelight.binding.input.KeyboardTranslator;
|
import com.limelight.binding.input.KeyboardTranslator;
|
||||||
@@ -19,6 +26,8 @@ import android.content.SharedPreferences;
|
|||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.NetworkInfo;
|
||||||
|
import android.net.wifi.WifiManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
@@ -56,6 +65,8 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
|
|||||||
private boolean connecting = false;
|
private boolean connecting = false;
|
||||||
private boolean connected = false;
|
private boolean connected = false;
|
||||||
|
|
||||||
|
private WifiManager.WifiLock wifiLock;
|
||||||
|
|
||||||
private int drFlags = 0;
|
private int drFlags = 0;
|
||||||
|
|
||||||
public static final String PREFS_FILE_NAME = "gameprefs";
|
public static final String PREFS_FILE_NAME = "gameprefs";
|
||||||
@@ -152,10 +163,35 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
|
|||||||
|
|
||||||
// Warn the user if they're on a metered connection
|
// Warn the user if they're on a metered connection
|
||||||
checkDataConnection();
|
checkDataConnection();
|
||||||
|
|
||||||
|
// Make sure Wi-Fi is fully powered up
|
||||||
|
WifiManager wifiMgr = (WifiManager) getSystemService(Context.WIFI_SERVICE);
|
||||||
|
wifiLock = wifiMgr.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "Limelight");
|
||||||
|
wifiLock.setReferenceCounted(false);
|
||||||
|
wifiLock.acquire();
|
||||||
|
|
||||||
|
String host = Game.this.getIntent().getStringExtra("host");
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
LimeLog.info("Using large packets? "+enableLargePackets);
|
||||||
|
|
||||||
// Start the connection
|
// Start the connection
|
||||||
conn = new NvConnection(Game.this.getIntent().getStringExtra("host"), Game.this,
|
conn = new NvConnection(host, Game.this,
|
||||||
new StreamConfiguration(width, height, refreshRate, bitrate * 1000), PlatformBinding.getCryptoProvider(this));
|
new StreamConfiguration(width, height, refreshRate, bitrate * 1000,
|
||||||
|
enableLargePackets ? 1460 : 1024), PlatformBinding.getCryptoProvider(this));
|
||||||
keybTranslator = new KeyboardTranslator(conn);
|
keybTranslator = new KeyboardTranslator(conn);
|
||||||
controllerHandler = new ControllerHandler(conn);
|
controllerHandler = new ControllerHandler(conn);
|
||||||
|
|
||||||
@@ -163,6 +199,89 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
|
|||||||
sh.addCallback(this);
|
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()
|
private void checkDataConnection()
|
||||||
{
|
{
|
||||||
ConnectivityManager mgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
ConnectivityManager mgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
@@ -214,6 +333,13 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM
|
|||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
|
||||||
|
wifiLock.release();
|
||||||
|
}
|
||||||
|
|
||||||
private static byte getModifierState(KeyEvent event) {
|
private static byte getModifierState(KeyEvent event) {
|
||||||
byte modifier = 0;
|
byte modifier = 0;
|
||||||
if (event.isShiftPressed()) {
|
if (event.isShiftPressed()) {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class ControllerHandler {
|
|||||||
private long lastRbUpTime = 0;
|
private long lastRbUpTime = 0;
|
||||||
private static final int MAXIMUM_BUMPER_UP_DELAY_MS = 100;
|
private static final int MAXIMUM_BUMPER_UP_DELAY_MS = 100;
|
||||||
|
|
||||||
private static final int MINIMUM_BUTTON_DOWN_TIME_MS = 10;
|
private static final int MINIMUM_BUTTON_DOWN_TIME_MS = 25;
|
||||||
|
|
||||||
private static final int EMULATING_SPECIAL = 0x1;
|
private static final int EMULATING_SPECIAL = 0x1;
|
||||||
private static final int EMULATING_SELECT = 0x2;
|
private static final int EMULATING_SELECT = 0x2;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import com.limelight.LimeLog;
|
|||||||
import com.limelight.nvstream.av.ByteBufferDescriptor;
|
import com.limelight.nvstream.av.ByteBufferDescriptor;
|
||||||
import com.limelight.nvstream.av.DecodeUnit;
|
import com.limelight.nvstream.av.DecodeUnit;
|
||||||
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
||||||
|
import com.limelight.nvstream.av.video.VideoDepacketizer;
|
||||||
import com.limelight.nvstream.av.video.cpu.AvcDecoder;
|
import com.limelight.nvstream.av.video.cpu.AvcDecoder;
|
||||||
|
|
||||||
public class AndroidCpuDecoderRenderer implements VideoDecoderRenderer {
|
public class AndroidCpuDecoderRenderer implements VideoDecoderRenderer {
|
||||||
@@ -141,22 +142,23 @@ public class AndroidCpuDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() {
|
public void start(final VideoDepacketizer depacketizer) {
|
||||||
rendererThread = new Thread() {
|
rendererThread = new Thread() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
long nextFrameTime = System.currentTimeMillis();
|
long nextFrameTime = System.currentTimeMillis();
|
||||||
|
DecodeUnit du;
|
||||||
while (!isInterrupted())
|
while (!isInterrupted())
|
||||||
{
|
{
|
||||||
|
du = depacketizer.pollNextDecodeUnit();
|
||||||
|
if (du != null) {
|
||||||
|
submitDecodeUnit(du);
|
||||||
|
}
|
||||||
|
|
||||||
long diff = nextFrameTime - System.currentTimeMillis();
|
long diff = nextFrameTime - System.currentTimeMillis();
|
||||||
|
|
||||||
if (diff > WAIT_CEILING_MS) {
|
if (diff > WAIT_CEILING_MS) {
|
||||||
try {
|
continue;
|
||||||
Thread.sleep(diff);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nextFrameTime = computePresentationTimeMs(targetFps);
|
nextFrameTime = computePresentationTimeMs(targetFps);
|
||||||
@@ -165,6 +167,7 @@ public class AndroidCpuDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
rendererThread.setName("Video - Renderer (CPU)");
|
rendererThread.setName("Video - Renderer (CPU)");
|
||||||
|
rendererThread.setPriority(Thread.MAX_PRIORITY);
|
||||||
rendererThread.start();
|
rendererThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,8 +189,7 @@ public class AndroidCpuDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
AvcDecoder.destroy();
|
AvcDecoder.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private boolean submitDecodeUnit(DecodeUnit decodeUnit) {
|
||||||
public boolean submitDecodeUnit(DecodeUnit decodeUnit) {
|
|
||||||
byte[] data;
|
byte[] data;
|
||||||
|
|
||||||
// Use the reserved decoder buffer if this decode unit will fit
|
// Use the reserved decoder buffer if this decode unit will fit
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.limelight.binding.video;
|
package com.limelight.binding.video;
|
||||||
|
|
||||||
import com.limelight.nvstream.av.DecodeUnit;
|
|
||||||
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
||||||
|
import com.limelight.nvstream.av.video.VideoDepacketizer;
|
||||||
|
|
||||||
public class ConfigurableDecoderRenderer implements VideoDecoderRenderer {
|
public class ConfigurableDecoderRenderer implements VideoDecoderRenderer {
|
||||||
|
|
||||||
@@ -26,8 +26,8 @@ public class ConfigurableDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() {
|
public void start(VideoDepacketizer depacketizer) {
|
||||||
decoderRenderer.start();
|
decoderRenderer.start(depacketizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -35,11 +35,6 @@ public class ConfigurableDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
decoderRenderer.stop();
|
decoderRenderer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean submitDecodeUnit(DecodeUnit du) {
|
|
||||||
return decoderRenderer.submitDecodeUnit(du);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCapabilities() {
|
public int getCapabilities() {
|
||||||
return decoderRenderer.getCapabilities();
|
return decoderRenderer.getCapabilities();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.limelight.LimeLog;
|
|||||||
import com.limelight.nvstream.av.ByteBufferDescriptor;
|
import com.limelight.nvstream.av.ByteBufferDescriptor;
|
||||||
import com.limelight.nvstream.av.DecodeUnit;
|
import com.limelight.nvstream.av.DecodeUnit;
|
||||||
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
||||||
|
import com.limelight.nvstream.av.video.VideoDepacketizer;
|
||||||
|
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaCodecInfo;
|
import android.media.MediaCodecInfo;
|
||||||
@@ -23,15 +24,13 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
private ByteBuffer[] videoDecoderInputBuffers;
|
private ByteBuffer[] videoDecoderInputBuffers;
|
||||||
private MediaCodec videoDecoder;
|
private MediaCodec videoDecoder;
|
||||||
private Thread rendererThread;
|
private Thread rendererThread;
|
||||||
private int redrawRate;
|
|
||||||
private boolean needsSpsBitstreamFixup;
|
private boolean needsSpsBitstreamFixup;
|
||||||
private boolean needsSpsNumRefFixup;
|
private boolean needsSpsNumRefFixup;
|
||||||
private boolean fastInputQueueing;
|
private VideoDepacketizer depacketizer;
|
||||||
|
|
||||||
public static final List<String> blacklistedDecoderPrefixes;
|
public static final List<String> blacklistedDecoderPrefixes;
|
||||||
public static final List<String> spsFixupBitsreamFixupDecoderPrefixes;
|
public static final List<String> spsFixupBitsreamFixupDecoderPrefixes;
|
||||||
public static final List<String> spsFixupNumRefFixupDecoderPrefixes;
|
public static final List<String> spsFixupNumRefFixupDecoderPrefixes;
|
||||||
public static final List<String> fastInputQueueingPrefixes;
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
blacklistedDecoderPrefixes = new LinkedList<String>();
|
blacklistedDecoderPrefixes = new LinkedList<String>();
|
||||||
@@ -45,11 +44,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
|
|
||||||
spsFixupNumRefFixupDecoderPrefixes = new LinkedList<String>();
|
spsFixupNumRefFixupDecoderPrefixes = new LinkedList<String>();
|
||||||
spsFixupNumRefFixupDecoderPrefixes.add("omx.TI");
|
spsFixupNumRefFixupDecoderPrefixes.add("omx.TI");
|
||||||
}
|
spsFixupNumRefFixupDecoderPrefixes.add("omx.qcom");
|
||||||
|
|
||||||
static {
|
|
||||||
fastInputQueueingPrefixes = new LinkedList<String>();
|
|
||||||
fastInputQueueingPrefixes.add("omx.nvidia");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isDecoderInList(List<String> decoderList, String decoderName) {
|
private static boolean isDecoderInList(List<String> decoderList, String decoderName) {
|
||||||
@@ -124,9 +119,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setup(int width, int height, int redrawRate, Object renderTarget, int drFlags) {
|
public void setup(int width, int height, int redrawRate, Object renderTarget, int drFlags) {
|
||||||
this.redrawRate = redrawRate;
|
|
||||||
|
|
||||||
//dumpDecoders();
|
//dumpDecoders();
|
||||||
|
|
||||||
MediaCodecInfo safeDecoder = findSafeDecoder();
|
MediaCodecInfo safeDecoder = findSafeDecoder();
|
||||||
@@ -140,16 +133,11 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
if (needsSpsNumRefFixup) {
|
if (needsSpsNumRefFixup) {
|
||||||
LimeLog.info("Decoder "+safeDecoder.getName()+" needs SPS ref num fixup");
|
LimeLog.info("Decoder "+safeDecoder.getName()+" needs SPS ref num fixup");
|
||||||
}
|
}
|
||||||
fastInputQueueing = isDecoderInList(fastInputQueueingPrefixes, safeDecoder.getName());
|
|
||||||
if (fastInputQueueing) {
|
|
||||||
LimeLog.info("Decoder "+safeDecoder.getName()+" supports fast input queueing");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
videoDecoder = MediaCodec.createDecoderByType("video/avc");
|
videoDecoder = MediaCodec.createDecoderByType("video/avc");
|
||||||
needsSpsBitstreamFixup = false;
|
needsSpsBitstreamFixup = false;
|
||||||
needsSpsNumRefFixup = false;
|
needsSpsNumRefFixup = false;
|
||||||
fastInputQueueing = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaFormat videoFormat = MediaFormat.createVideoFormat("video/avc", width, height);
|
MediaFormat videoFormat = MediaFormat.createVideoFormat("video/avc", width, height);
|
||||||
@@ -167,59 +155,51 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
rendererThread = new Thread() {
|
rendererThread = new Thread() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
long nextFrameTimeUs = 0;
|
|
||||||
BufferInfo info = new BufferInfo();
|
BufferInfo info = new BufferInfo();
|
||||||
|
DecodeUnit du;
|
||||||
while (!isInterrupted())
|
while (!isInterrupted())
|
||||||
{
|
{
|
||||||
// Block for a maximum of 100 ms
|
du = depacketizer.pollNextDecodeUnit();
|
||||||
int outIndex = videoDecoder.dequeueOutputBuffer(info, 100000);
|
if (du != null) {
|
||||||
switch (outIndex) {
|
submitDecodeUnit(du);
|
||||||
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
|
}
|
||||||
LimeLog.info("Output buffers changed");
|
|
||||||
break;
|
int outIndex = videoDecoder.dequeueOutputBuffer(info, 0);
|
||||||
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
|
|
||||||
LimeLog.info("Output format changed");
|
|
||||||
LimeLog.info("New output Format: " + videoDecoder.getOutputFormat());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outIndex >= 0) {
|
if (outIndex >= 0) {
|
||||||
int lastIndex = outIndex;
|
int lastIndex = outIndex;
|
||||||
boolean render = false;
|
|
||||||
|
|
||||||
if (currentTimeUs() >= nextFrameTimeUs) {
|
|
||||||
render = true;
|
|
||||||
nextFrameTimeUs = computePresentationTime(redrawRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the last output buffer in the queue
|
// Get the last output buffer in the queue
|
||||||
while ((outIndex = videoDecoder.dequeueOutputBuffer(info, 0)) >= 0) {
|
while ((outIndex = videoDecoder.dequeueOutputBuffer(info, 0)) >= 0) {
|
||||||
videoDecoder.releaseOutputBuffer(lastIndex, false);
|
videoDecoder.releaseOutputBuffer(lastIndex, false);
|
||||||
lastIndex = outIndex;
|
lastIndex = outIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render that buffer if it's time for the next frame
|
// Render the last buffer
|
||||||
videoDecoder.releaseOutputBuffer(lastIndex, render);
|
videoDecoder.releaseOutputBuffer(lastIndex, true);
|
||||||
|
} else {
|
||||||
|
switch (outIndex) {
|
||||||
|
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
|
||||||
|
LimeLog.info("Output buffers changed");
|
||||||
|
break;
|
||||||
|
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
|
||||||
|
LimeLog.info("Output format changed");
|
||||||
|
LimeLog.info("New output Format: " + videoDecoder.getOutputFormat());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
rendererThread.setName("Video - Renderer (MediaCodec)");
|
rendererThread.setName("Video - Renderer (MediaCodec)");
|
||||||
|
rendererThread.setPriority(Thread.MAX_PRIORITY);
|
||||||
rendererThread.start();
|
rendererThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long currentTimeUs() {
|
|
||||||
return System.nanoTime() / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
private long computePresentationTime(int frameRate) {
|
|
||||||
return currentTimeUs() + (1000000 / frameRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() {
|
public void start(VideoDepacketizer depacketizer) {
|
||||||
|
this.depacketizer = depacketizer;
|
||||||
startRendererThread();
|
startRendererThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,24 +219,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private boolean submitDecodeUnit(DecodeUnit decodeUnit) {
|
||||||
public boolean submitDecodeUnit(DecodeUnit decodeUnit) {
|
|
||||||
if (decodeUnit.getType() != DecodeUnit.TYPE_H264) {
|
|
||||||
System.err.println("Unknown decode unit type");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mcFlags = 0;
|
|
||||||
|
|
||||||
if ((decodeUnit.getFlags() & DecodeUnit.DU_FLAG_CODEC_CONFIG) != 0) {
|
|
||||||
LimeLog.info("Codec config");
|
|
||||||
mcFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
|
|
||||||
}
|
|
||||||
if ((decodeUnit.getFlags() & DecodeUnit.DU_FLAG_SYNC_FRAME) != 0) {
|
|
||||||
LimeLog.info("Sync frame");
|
|
||||||
mcFlags |= MediaCodec.BUFFER_FLAG_SYNC_FRAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
int inputIndex = videoDecoder.dequeueInputBuffer(-1);
|
int inputIndex = videoDecoder.dequeueInputBuffer(-1);
|
||||||
if (inputIndex >= 0)
|
if (inputIndex >= 0)
|
||||||
{
|
{
|
||||||
@@ -272,7 +235,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
if (needsSpsNumRefFixup) {
|
if (needsSpsNumRefFixup) {
|
||||||
LimeLog.info("Fixing up num ref frames");
|
LimeLog.info("Fixing up num ref frames");
|
||||||
this.replace(header, 80, 9, new byte[] {0x40}, 3);
|
this.replace(header, 80, 9, new byte[] {0x40}, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The SPS that comes in the current H264 bytestream doesn't set bitstream_restriction_flag
|
// 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.
|
// or max_dec_frame_buffering which increases decoding latency on Tegra.
|
||||||
@@ -312,7 +275,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
|
|
||||||
videoDecoder.queueInputBuffer(inputIndex,
|
videoDecoder.queueInputBuffer(inputIndex,
|
||||||
0, spsLength,
|
0, spsLength,
|
||||||
0, mcFlags);
|
0, 0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,7 +288,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
|
|
||||||
videoDecoder.queueInputBuffer(inputIndex,
|
videoDecoder.queueInputBuffer(inputIndex,
|
||||||
0, decodeUnit.getDataLength(),
|
0, decodeUnit.getDataLength(),
|
||||||
0, mcFlags);
|
0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -333,7 +296,7 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCapabilities() {
|
public int getCapabilities() {
|
||||||
return fastInputQueueing ? VideoDecoderRenderer.CAPABILITY_DIRECT_SUBMIT : 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user