diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9d476fb3..7c4cd28c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -12,6 +12,7 @@
packets = new LinkedBlockingQueue();
+ private ArrayBlockingQueue packets = new ArrayBlockingQueue(100);
private AudioTrack track;
@@ -201,10 +201,10 @@ public class NvAudioStream {
desc.data = packet.getData();
// Give the packet to the depacketizer thread
- packets.add(new AvRtpPacket(desc));
-
- // Get a new buffer from the buffer pool
- packet.setData(new byte[1500], 0, 1500);
+ if (packets.offer(new AvRtpPacket(desc))) {
+ // Get a new buffer from the buffer pool
+ packet.setData(new byte[1500], 0, 1500);
+ }
}
}
};
diff --git a/src/com/limelight/nvstream/NvVideoStream.java b/src/com/limelight/nvstream/NvVideoStream.java
index 7269ada4..9627762d 100644
--- a/src/com/limelight/nvstream/NvVideoStream.java
+++ b/src/com/limelight/nvstream/NvVideoStream.java
@@ -9,7 +9,7 @@ import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.LinkedList;
-import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ArrayBlockingQueue;
import com.limelight.nvstream.av.AvByteBufferDescriptor;
import com.limelight.nvstream.av.AvDecodeUnit;
@@ -31,7 +31,7 @@ public class NvVideoStream {
public static final int FIRST_FRAME_TIMEOUT = 3000;
- private LinkedBlockingQueue packets = new LinkedBlockingQueue();
+ private ArrayBlockingQueue packets = new ArrayBlockingQueue(100);
private InetAddress host;
private DatagramSocket rtp;
@@ -135,7 +135,7 @@ public class NvVideoStream {
// Emulator - don't render video (it's slow!)
decrend = null;
}
- else if (MediaCodecDecoderRenderer.hasWhitelistedDecoder()) {
+ else if (MediaCodecDecoderRenderer.findSafeDecoder() != null) {
// Hardware decoding
decrend = new MediaCodecDecoderRenderer();
}
@@ -260,10 +260,10 @@ public class NvVideoStream {
desc.data = packet.getData();
// Give the packet to the depacketizer thread
- packets.add(new AvRtpPacket(desc));
-
- // Get a new buffer from the buffer pool
- packet.setData(new byte[1500], 0, 1500);
+ if (packets.offer(new AvRtpPacket(desc))) {
+ // Get a new buffer from the buffer pool
+ packet.setData(new byte[1500], 0, 1500);
+ }
}
}
};
diff --git a/src/com/limelight/nvstream/av/audio/AvAudioDepacketizer.java b/src/com/limelight/nvstream/av/audio/AvAudioDepacketizer.java
index b14b8381..7d97515b 100644
--- a/src/com/limelight/nvstream/av/audio/AvAudioDepacketizer.java
+++ b/src/com/limelight/nvstream/av/audio/AvAudioDepacketizer.java
@@ -1,14 +1,14 @@
package com.limelight.nvstream.av.audio;
-import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ArrayBlockingQueue;
import com.limelight.nvstream.av.AvByteBufferDescriptor;
import com.limelight.nvstream.av.AvRtpPacket;
import com.limelight.nvstream.av.AvShortBufferDescriptor;
public class AvAudioDepacketizer {
- private LinkedBlockingQueue decodedUnits =
- new LinkedBlockingQueue(15);
+ private ArrayBlockingQueue decodedUnits =
+ new ArrayBlockingQueue(15);
// Sequencing state
private short lastSequenceNumber;
@@ -24,7 +24,10 @@ public class AvAudioDepacketizer {
decodeLen *= OpusDecoder.getChannelCount();
// Put it on the decoded queue
- decodedUnits.offer(new AvShortBufferDescriptor(pcmData, 0, decodeLen));
+ if (!decodedUnits.offer(new AvShortBufferDescriptor(pcmData, 0, decodeLen))) {
+ // Clear out the queue
+ decodedUnits.clear();
+ }
}
}
diff --git a/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java b/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java
index 24f0f94b..c5881f0e 100644
--- a/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java
+++ b/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java
@@ -1,7 +1,7 @@
package com.limelight.nvstream.av.video;
import java.util.LinkedList;
-import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ArrayBlockingQueue;
import com.limelight.nvstream.av.AvByteBufferDescriptor;
import com.limelight.nvstream.av.AvDecodeUnit;
@@ -22,7 +22,8 @@ public class AvVideoDepacketizer {
private ConnectionStatusListener controlListener;
- private LinkedBlockingQueue decodedUnits = new LinkedBlockingQueue();
+ private static final int DU_LIMIT = 30;
+ private ArrayBlockingQueue decodedUnits = new ArrayBlockingQueue(DU_LIMIT);
public AvVideoDepacketizer(ConnectionStatusListener controlListener)
{
@@ -89,7 +90,11 @@ public class AvVideoDepacketizer {
// Construct the H264 decode unit
AvDecodeUnit du = new AvDecodeUnit(AvDecodeUnit.TYPE_H264, avcNalDataChain, avcNalDataLength, flags);
- decodedUnits.offer(du);
+ if (!decodedUnits.offer(du)) {
+ // We need a new IDR frame since we're discarding data now
+ decodedUnits.clear();
+ controlListener.connectionNeedsResync();
+ }
// Clear old state
avcNalDataChain = null;
diff --git a/src/com/limelight/nvstream/av/video/MediaCodecDecoderRenderer.java b/src/com/limelight/nvstream/av/video/MediaCodecDecoderRenderer.java
index 642d6fff..5c72a9cf 100644
--- a/src/com/limelight/nvstream/av/video/MediaCodecDecoderRenderer.java
+++ b/src/com/limelight/nvstream/av/video/MediaCodecDecoderRenderer.java
@@ -1,6 +1,8 @@
package com.limelight.nvstream.av.video;
import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
import com.limelight.nvstream.av.AvByteBufferDescriptor;
import com.limelight.nvstream.av.AvDecodeUnit;
@@ -20,28 +22,57 @@ public class MediaCodecDecoderRenderer implements DecoderRenderer {
private ByteBuffer[] videoDecoderInputBuffers;
private MediaCodec videoDecoder;
private Thread rendererThread;
+
+ public static final List blacklistedDecoderPrefixes;
+
+ static {
+ blacklistedDecoderPrefixes = new LinkedList();
+ blacklistedDecoderPrefixes.add("omx.google");
+ blacklistedDecoderPrefixes.add("omx.nvidia");
+ blacklistedDecoderPrefixes.add("omx.TI");
+ }
+
+ public static MediaCodecInfo findSafeDecoder() {
- public static boolean hasWhitelistedDecoder() {
for (int i = 0; i < MediaCodecList.getCodecCount(); i++) {
MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+ boolean badCodec = false;
// Skip encoders
if (codecInfo.isEncoder()) {
continue;
}
- if (codecInfo.getName().equalsIgnoreCase("omx.qcom.video.decoder.avc") ||
- codecInfo.getName().equalsIgnoreCase("OMX.Exynos.AVC.Decoder")) {
- return true;
+ for (String badPrefix : blacklistedDecoderPrefixes) {
+ String name = codecInfo.getName();
+ if (name.length() > badPrefix.length()) {
+ String prefix = name.substring(0, badPrefix.length());
+ if (prefix.equalsIgnoreCase(badPrefix)) {
+ badCodec = true;
+ break;
+ }
+ }
+ }
+
+ if (badCodec) {
+ System.out.println("Blacklisted decoder: "+codecInfo.getName());
+ continue;
+ }
+
+ for (String mime : codecInfo.getSupportedTypes()) {
+ if (mime.equalsIgnoreCase("video/avc")) {
+ System.out.println("Selected decoder: "+codecInfo.getName());
+ return codecInfo;
+ }
}
}
- return false;
+ return null;
}
@Override
public void setup(int width, int height, Surface renderTarget) {
- videoDecoder = MediaCodec.createDecoderByType("video/avc");
+ videoDecoder = MediaCodec.createByCodecName(findSafeDecoder().getName());
MediaFormat videoFormat = MediaFormat.createVideoFormat("video/avc", width, height);
videoDecoder.configure(videoFormat, renderTarget, null, 0);