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);