diff --git a/src/com/limelight/Game.java b/src/com/limelight/Game.java index fbdc13b4..eb70de99 100644 --- a/src/com/limelight/Game.java +++ b/src/com/limelight/Game.java @@ -4,6 +4,7 @@ import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.input.NvControllerPacket; import android.app.Activity; +import android.content.ComponentCallbacks2; import android.os.Bundle; import android.view.InputDevice; import android.view.KeyEvent; @@ -68,11 +69,21 @@ public class Game extends Activity implements OnGenericMotionListener, OnTouchLi super.onPause(); } + @Override public void onDestroy() { conn.stop(); super.onDestroy(); } + @Override + public void onTrimMemory(int trimLevel) { + if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) + { + System.out.println("Trimming for level: "+trimLevel); + conn.trim(); + } + } + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { diff --git a/src/com/limelight/nvstream/NvAudioStream.java b/src/com/limelight/nvstream/NvAudioStream.java index 44f7d65c..d5b4a684 100644 --- a/src/com/limelight/nvstream/NvAudioStream.java +++ b/src/com/limelight/nvstream/NvAudioStream.java @@ -105,6 +105,11 @@ public class NvAudioStream { session.addParticipant(new Participant(host, RTP_PORT, 0)); } + public void trim() + { + depacketizer.trim(); + } + private void setupAudio() { int channelConfig; diff --git a/src/com/limelight/nvstream/NvConnection.java b/src/com/limelight/nvstream/NvConnection.java index 6ac1edf8..cf204d97 100644 --- a/src/com/limelight/nvstream/NvConnection.java +++ b/src/com/limelight/nvstream/NvConnection.java @@ -79,6 +79,12 @@ public class NvConnection { inputStream = null; } } + + public void trim() + { + videoStream.trim(); + audioStream.trim(); + } public void start() { diff --git a/src/com/limelight/nvstream/NvVideoStream.java b/src/com/limelight/nvstream/NvVideoStream.java index 2f238d05..004f8fa5 100644 --- a/src/com/limelight/nvstream/NvVideoStream.java +++ b/src/com/limelight/nvstream/NvVideoStream.java @@ -20,6 +20,7 @@ import com.limelight.nvstream.av.video.AvVideoPacket; import jlibrtp.Participant; import jlibrtp.RTPSession; +import android.content.ComponentCallbacks2; import android.media.MediaCodec; import android.media.MediaCodec.BufferInfo; import android.media.MediaFormat; @@ -78,6 +79,11 @@ public class NvVideoStream { threads.clear(); } + public void trim() + { + depacketizer.trim(); + } + private InputStream openFirstFrameInputStream(String host) throws UnknownHostException, IOException { Socket s = new Socket(host, FIRST_FRAME_PORT); @@ -122,7 +128,6 @@ public class NvVideoStream { public void setupDecoders(Surface surface) { videoDecoder = MediaCodec.createDecoderByType("video/avc"); - //videoDecoder = MediaCodec.createByCodecName("OMX.google.h264.decoder"); MediaFormat videoFormat = MediaFormat.createVideoFormat("video/avc", 1280, 720); videoDecoder.configure(videoFormat, surface, null, 0); diff --git a/src/com/limelight/nvstream/av/AvByteBufferPool.java b/src/com/limelight/nvstream/av/AvByteBufferPool.java index 2ce0433e..a4892eaf 100644 --- a/src/com/limelight/nvstream/av/AvByteBufferPool.java +++ b/src/com/limelight/nvstream/av/AvByteBufferPool.java @@ -11,6 +11,11 @@ public class AvByteBufferPool { this.bufferSize = size; } + public synchronized void purge() + { + this.bufferList = new LinkedList(); + } + public synchronized byte[] allocate() { if (bufferList.isEmpty()) diff --git a/src/com/limelight/nvstream/av/AvShortBufferPool.java b/src/com/limelight/nvstream/av/AvShortBufferPool.java index 5e8cc043..0b000483 100644 --- a/src/com/limelight/nvstream/av/AvShortBufferPool.java +++ b/src/com/limelight/nvstream/av/AvShortBufferPool.java @@ -11,6 +11,11 @@ public class AvShortBufferPool { this.bufferSize = size; } + public synchronized void purge() + { + this.bufferList = new LinkedList(); + } + public synchronized short[] allocate() { if (bufferList.isEmpty()) diff --git a/src/com/limelight/nvstream/av/audio/AvAudioDepacketizer.java b/src/com/limelight/nvstream/av/audio/AvAudioDepacketizer.java index 73cd0fe5..beffca91 100644 --- a/src/com/limelight/nvstream/av/audio/AvAudioDepacketizer.java +++ b/src/com/limelight/nvstream/av/audio/AvAudioDepacketizer.java @@ -9,13 +9,18 @@ import com.limelight.nvstream.av.AvShortBufferPool; public class AvAudioDepacketizer { private LinkedBlockingQueue decodedUnits = - new LinkedBlockingQueue(); + new LinkedBlockingQueue(15); - private AvShortBufferPool pool = new AvShortBufferPool(OpusDecoder.getMaxOutputShorts()); + private AvShortBufferPool pool = new AvShortBufferPool(512); // Sequencing state private short lastSequenceNumber; + public void trim() + { + pool.purge(); + } + public void decodeInputData(AvRtpPacket packet) { short seq = packet.getSequenceNumber(); @@ -50,9 +55,13 @@ public class AvAudioDepacketizer { decodeLen *= OpusDecoder.getChannelCount(); // Put it on the decoded queue - decodedUnits.add(new AvShortBufferDescriptor(pcmData, 0, decodeLen)); + if (!decodedUnits.offer(new AvShortBufferDescriptor(pcmData, 0, decodeLen))) + { + pool.free(pcmData); + } } else { + System.out.println("decode failed: "+decodeLen); pool.free(pcmData); } } diff --git a/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java b/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java index fd3d58ee..7ad0d199 100644 --- a/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java +++ b/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java @@ -29,6 +29,11 @@ public class AvVideoDepacketizer { return pool.allocate(); } + public void trim() + { + pool.purge(); + } + private void clearAvcNalState() { if (avcNalDataChain != null) @@ -114,7 +119,10 @@ public class AvVideoDepacketizer { // Construct the H264 decode unit AvDecodeUnit du = new AvDecodeUnit(AvDecodeUnit.TYPE_H264, avcNalDataChain, avcNalDataLength, flags); - decodedUnits.add(du); + if (!decodedUnits.offer(du)) + { + releaseDecodeUnit(du); + } // Clear old state avcNalDataChain = null; @@ -172,25 +180,33 @@ public class AvVideoDepacketizer { // Move to the next special sequence while (location.length != 0) { - specialSeq = NAL.getSpecialSequenceDescriptor(location); - - // Check if this should end the current NAL - if (specialSeq != null) + // Catch the easy case first where byte 0 != 0x00 + if (location.data[location.offset] == 0x00) { - break; + specialSeq = NAL.getSpecialSequenceDescriptor(location); + + // Check if this should end the current NAL + if (specialSeq != null) + { + // Only stop if we're decoding something or this + // isn't padding + if (currentlyDecoding != AvDecodeUnit.TYPE_UNKNOWN || + !NAL.isPadding(specialSeq)) + { + break; + } + } } - else - { - // This byte is part of the NAL data - location.offset++; - location.length--; - } - } - AvByteBufferDescriptor data = new AvByteBufferDescriptor(location.data, start, location.offset-start); + // This byte is part of the NAL data + location.offset++; + location.length--; + } if (currentlyDecoding == AvDecodeUnit.TYPE_H264 && avcNalDataChain != null) { + AvByteBufferDescriptor data = new AvByteBufferDescriptor(location.data, start, location.offset-start); + // Attach the current packet as the buffer context and increment the refcount data.context = packet; packet.addRef();