Limit the size of queues. Switch to a blacklist instead of a whitelist for hardware decoding. Currently, Nvidia, TI, and Google H264 codecs are on the blacklist. Reenable large heap since we're allocating more objects now.

This commit is contained in:
Cameron Gutman
2013-11-23 02:44:10 -05:00
parent 4d5849f448
commit 125bcb370f
6 changed files with 66 additions and 26 deletions
+1
View File
@@ -12,6 +12,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application <application
android:largeHeap="true"
android:allowBackup="true" android:allowBackup="true"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
@@ -6,7 +6,7 @@ import java.net.DatagramSocket;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.SocketException; import java.net.SocketException;
import java.util.LinkedList; 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.AvByteBufferDescriptor;
import com.limelight.nvstream.av.AvRtpPacket; import com.limelight.nvstream.av.AvRtpPacket;
@@ -22,7 +22,7 @@ public class NvAudioStream {
public static final int RTP_PORT = 48000; public static final int RTP_PORT = 48000;
public static final int RTCP_PORT = 47999; public static final int RTCP_PORT = 47999;
private LinkedBlockingQueue<AvRtpPacket> packets = new LinkedBlockingQueue<AvRtpPacket>(); private ArrayBlockingQueue<AvRtpPacket> packets = new ArrayBlockingQueue<AvRtpPacket>(100);
private AudioTrack track; private AudioTrack track;
@@ -201,10 +201,10 @@ public class NvAudioStream {
desc.data = packet.getData(); desc.data = packet.getData();
// Give the packet to the depacketizer thread // Give the packet to the depacketizer thread
packets.add(new AvRtpPacket(desc)); if (packets.offer(new AvRtpPacket(desc))) {
// Get a new buffer from the buffer pool
// Get a new buffer from the buffer pool packet.setData(new byte[1500], 0, 1500);
packet.setData(new byte[1500], 0, 1500); }
} }
} }
}; };
@@ -9,7 +9,7 @@ import java.net.InetSocketAddress;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
import java.util.LinkedList; 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.AvByteBufferDescriptor;
import com.limelight.nvstream.av.AvDecodeUnit; import com.limelight.nvstream.av.AvDecodeUnit;
@@ -31,7 +31,7 @@ public class NvVideoStream {
public static final int FIRST_FRAME_TIMEOUT = 3000; public static final int FIRST_FRAME_TIMEOUT = 3000;
private LinkedBlockingQueue<AvRtpPacket> packets = new LinkedBlockingQueue<AvRtpPacket>(); private ArrayBlockingQueue<AvRtpPacket> packets = new ArrayBlockingQueue<AvRtpPacket>(100);
private InetAddress host; private InetAddress host;
private DatagramSocket rtp; private DatagramSocket rtp;
@@ -135,7 +135,7 @@ public class NvVideoStream {
// Emulator - don't render video (it's slow!) // Emulator - don't render video (it's slow!)
decrend = null; decrend = null;
} }
else if (MediaCodecDecoderRenderer.hasWhitelistedDecoder()) { else if (MediaCodecDecoderRenderer.findSafeDecoder() != null) {
// Hardware decoding // Hardware decoding
decrend = new MediaCodecDecoderRenderer(); decrend = new MediaCodecDecoderRenderer();
} }
@@ -260,10 +260,10 @@ public class NvVideoStream {
desc.data = packet.getData(); desc.data = packet.getData();
// Give the packet to the depacketizer thread // Give the packet to the depacketizer thread
packets.add(new AvRtpPacket(desc)); if (packets.offer(new AvRtpPacket(desc))) {
// Get a new buffer from the buffer pool
// Get a new buffer from the buffer pool packet.setData(new byte[1500], 0, 1500);
packet.setData(new byte[1500], 0, 1500); }
} }
} }
}; };
@@ -1,14 +1,14 @@
package com.limelight.nvstream.av.audio; 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.AvByteBufferDescriptor;
import com.limelight.nvstream.av.AvRtpPacket; import com.limelight.nvstream.av.AvRtpPacket;
import com.limelight.nvstream.av.AvShortBufferDescriptor; import com.limelight.nvstream.av.AvShortBufferDescriptor;
public class AvAudioDepacketizer { public class AvAudioDepacketizer {
private LinkedBlockingQueue<AvShortBufferDescriptor> decodedUnits = private ArrayBlockingQueue<AvShortBufferDescriptor> decodedUnits =
new LinkedBlockingQueue<AvShortBufferDescriptor>(15); new ArrayBlockingQueue<AvShortBufferDescriptor>(15);
// Sequencing state // Sequencing state
private short lastSequenceNumber; private short lastSequenceNumber;
@@ -24,7 +24,10 @@ public class AvAudioDepacketizer {
decodeLen *= OpusDecoder.getChannelCount(); decodeLen *= OpusDecoder.getChannelCount();
// Put it on the decoded queue // 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();
}
} }
} }
@@ -1,7 +1,7 @@
package com.limelight.nvstream.av.video; package com.limelight.nvstream.av.video;
import java.util.LinkedList; 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.AvByteBufferDescriptor;
import com.limelight.nvstream.av.AvDecodeUnit; import com.limelight.nvstream.av.AvDecodeUnit;
@@ -22,7 +22,8 @@ public class AvVideoDepacketizer {
private ConnectionStatusListener controlListener; private ConnectionStatusListener controlListener;
private LinkedBlockingQueue<AvDecodeUnit> decodedUnits = new LinkedBlockingQueue<AvDecodeUnit>(); private static final int DU_LIMIT = 30;
private ArrayBlockingQueue<AvDecodeUnit> decodedUnits = new ArrayBlockingQueue<AvDecodeUnit>(DU_LIMIT);
public AvVideoDepacketizer(ConnectionStatusListener controlListener) public AvVideoDepacketizer(ConnectionStatusListener controlListener)
{ {
@@ -89,7 +90,11 @@ public class AvVideoDepacketizer {
// Construct the H264 decode unit // Construct the H264 decode unit
AvDecodeUnit du = new AvDecodeUnit(AvDecodeUnit.TYPE_H264, avcNalDataChain, avcNalDataLength, flags); 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 // Clear old state
avcNalDataChain = null; avcNalDataChain = null;
@@ -1,6 +1,8 @@
package com.limelight.nvstream.av.video; package com.limelight.nvstream.av.video;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;
import com.limelight.nvstream.av.AvByteBufferDescriptor; import com.limelight.nvstream.av.AvByteBufferDescriptor;
import com.limelight.nvstream.av.AvDecodeUnit; import com.limelight.nvstream.av.AvDecodeUnit;
@@ -20,28 +22,57 @@ public class MediaCodecDecoderRenderer implements DecoderRenderer {
private ByteBuffer[] videoDecoderInputBuffers; private ByteBuffer[] videoDecoderInputBuffers;
private MediaCodec videoDecoder; private MediaCodec videoDecoder;
private Thread rendererThread; private Thread rendererThread;
public static final List<String> blacklistedDecoderPrefixes;
static {
blacklistedDecoderPrefixes = new LinkedList<String>();
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++) { for (int i = 0; i < MediaCodecList.getCodecCount(); i++) {
MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
boolean badCodec = false;
// Skip encoders // Skip encoders
if (codecInfo.isEncoder()) { if (codecInfo.isEncoder()) {
continue; continue;
} }
if (codecInfo.getName().equalsIgnoreCase("omx.qcom.video.decoder.avc") || for (String badPrefix : blacklistedDecoderPrefixes) {
codecInfo.getName().equalsIgnoreCase("OMX.Exynos.AVC.Decoder")) { String name = codecInfo.getName();
return true; 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 @Override
public void setup(int width, int height, Surface renderTarget) { 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); MediaFormat videoFormat = MediaFormat.createVideoFormat("video/avc", width, height);
videoDecoder.configure(videoFormat, renderTarget, null, 0); videoDecoder.configure(videoFormat, renderTarget, null, 0);