mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-20 19:42:45 +00:00
Remove the remaining allocations in the AV paths
This commit is contained in:
parent
c2401e7a75
commit
70b50bd096
@ -17,7 +17,10 @@ public class DecodeUnit {
|
|||||||
private long receiveTimestamp;
|
private long receiveTimestamp;
|
||||||
private int flags;
|
private int flags;
|
||||||
|
|
||||||
public DecodeUnit(int type, List<ByteBufferDescriptor> bufferList, int dataLength, int frameNumber, long receiveTimestamp, int flags)
|
public DecodeUnit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize(int type, List<ByteBufferDescriptor> bufferList, int dataLength, int frameNumber, long receiveTimestamp, int flags)
|
||||||
{
|
{
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.bufferList = bufferList;
|
this.bufferList = bufferList;
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
package com.limelight.nvstream.av;
|
||||||
|
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
|
|
||||||
|
public class PopulatedBufferList<T> {
|
||||||
|
private ArrayBlockingQueue<T> populatedList;
|
||||||
|
private ArrayBlockingQueue<T> freeList;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public PopulatedBufferList(int maxQueueSize, BufferFactory factory) {
|
||||||
|
this.populatedList = new ArrayBlockingQueue<T>(maxQueueSize, false);
|
||||||
|
this.freeList = new ArrayBlockingQueue<T>(maxQueueSize, false);
|
||||||
|
|
||||||
|
for (int i = 0; i < maxQueueSize; i++) {
|
||||||
|
freeList.add((T) factory.createFreeBuffer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T pollFreeObject() {
|
||||||
|
return freeList.poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPopulatedObject(T object) {
|
||||||
|
populatedList.add(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void freePopulatedObject(T object) {
|
||||||
|
freeList.add(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearPopulatedObjects() {
|
||||||
|
T object;
|
||||||
|
while ((object = populatedList.poll()) != null) {
|
||||||
|
freeList.add(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T pollPopulatedObject() {
|
||||||
|
return populatedList.poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T peekPopulatedObject() {
|
||||||
|
return populatedList.peek();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T takePopulatedObject() throws InterruptedException {
|
||||||
|
return populatedList.take();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface BufferFactory {
|
||||||
|
public Object createFreeBuffer();
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,19 @@
|
|||||||
package com.limelight.nvstream.av.audio;
|
package com.limelight.nvstream.av.audio;
|
||||||
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
|
|
||||||
import com.limelight.LimeLog;
|
import com.limelight.LimeLog;
|
||||||
import com.limelight.nvstream.av.ByteBufferDescriptor;
|
import com.limelight.nvstream.av.ByteBufferDescriptor;
|
||||||
|
import com.limelight.nvstream.av.PopulatedBufferList;
|
||||||
import com.limelight.nvstream.av.RtpPacket;
|
import com.limelight.nvstream.av.RtpPacket;
|
||||||
|
|
||||||
public class AudioDepacketizer {
|
public class AudioDepacketizer {
|
||||||
|
|
||||||
private static final int DU_LIMIT = 15;
|
private static final int DU_LIMIT = 15;
|
||||||
private LinkedBlockingQueue<ByteBufferDescriptor> decodedUnits =
|
private PopulatedBufferList<ByteBufferDescriptor> decodedUnits;
|
||||||
new LinkedBlockingQueue<ByteBufferDescriptor>(DU_LIMIT);
|
|
||||||
|
|
||||||
// Direct submit state
|
// Direct submit state
|
||||||
private AudioRenderer directSubmitRenderer;
|
private AudioRenderer directSubmitRenderer;
|
||||||
private byte[] directSubmitData;
|
private byte[] directSubmitData;
|
||||||
|
|
||||||
// Non-direct submit state
|
|
||||||
private byte[][] pcmRing;
|
|
||||||
private int ringIndex;
|
|
||||||
|
|
||||||
// Cached objects
|
// Cached objects
|
||||||
private ByteBufferDescriptor cachedDesc = new ByteBufferDescriptor(null, 0, 0);
|
private ByteBufferDescriptor cachedDesc = new ByteBufferDescriptor(null, 0, 0);
|
||||||
|
|
||||||
@ -33,22 +27,35 @@ public class AudioDepacketizer {
|
|||||||
this.directSubmitData = new byte[OpusDecoder.getMaxOutputShorts()*2];
|
this.directSubmitData = new byte[OpusDecoder.getMaxOutputShorts()*2];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pcmRing = new byte[DU_LIMIT][OpusDecoder.getMaxOutputShorts()*2];
|
decodedUnits = new PopulatedBufferList<ByteBufferDescriptor>(DU_LIMIT, new PopulatedBufferList.BufferFactory() {
|
||||||
}
|
public Object createFreeBuffer() {
|
||||||
|
return new ByteBufferDescriptor(new byte[OpusDecoder.getMaxOutputShorts()*2], 0, OpusDecoder.getMaxOutputShorts()*2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decodeData(byte[] data, int off, int len)
|
private void decodeData(byte[] data, int off, int len)
|
||||||
{
|
{
|
||||||
// Submit this data to the decoder
|
// Submit this data to the decoder
|
||||||
int decodeLen;
|
int decodeLen;
|
||||||
byte[] pcmData;
|
ByteBufferDescriptor bb;
|
||||||
if (directSubmitData != null) {
|
if (directSubmitData != null) {
|
||||||
pcmData = null;
|
bb = null;
|
||||||
decodeLen = OpusDecoder.decode(data, off, len, directSubmitData);
|
decodeLen = OpusDecoder.decode(data, off, len, directSubmitData);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pcmData = pcmRing[ringIndex];
|
bb = decodedUnits.pollFreeObject();
|
||||||
decodeLen = OpusDecoder.decode(data, off, len, pcmData);
|
if (bb == null) {
|
||||||
|
LimeLog.warning("Audio player too slow! Forced to drop decoded samples");
|
||||||
|
decodedUnits.clearPopulatedObjects();
|
||||||
|
bb = decodedUnits.pollFreeObject();
|
||||||
|
if (bb == null) {
|
||||||
|
LimeLog.severe("Audio player is leaking buffers!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
decodeLen = OpusDecoder.decode(data, off, len, bb.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decodeLen > 0) {
|
if (decodeLen > 0) {
|
||||||
@ -58,14 +65,9 @@ public class AudioDepacketizer {
|
|||||||
if (directSubmitRenderer != null) {
|
if (directSubmitRenderer != null) {
|
||||||
directSubmitRenderer.playDecodedAudio(directSubmitData, 0, decodeLen);
|
directSubmitRenderer.playDecodedAudio(directSubmitData, 0, decodeLen);
|
||||||
}
|
}
|
||||||
else if (!decodedUnits.offer(new ByteBufferDescriptor(pcmData, 0, decodeLen))) {
|
|
||||||
LimeLog.warning("Audio player too slow! Forced to drop decoded samples");
|
|
||||||
// Clear out the queue
|
|
||||||
decodedUnits.clear();
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
// Frame successfully submitted for playback
|
bb.length = decodeLen;
|
||||||
ringIndex = (ringIndex + 1) % DU_LIMIT;
|
decodedUnits.addPopulatedObject(bb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,8 +97,11 @@ public class AudioDepacketizer {
|
|||||||
decodeData(cachedDesc.data, cachedDesc.offset, cachedDesc.length);
|
decodeData(cachedDesc.data, cachedDesc.offset, cachedDesc.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ByteBufferDescriptor getNextDecodedData() throws InterruptedException
|
public ByteBufferDescriptor getNextDecodedData() throws InterruptedException {
|
||||||
{
|
return decodedUnits.takePopulatedObject();
|
||||||
return decodedUnits.take();
|
}
|
||||||
|
|
||||||
|
public void freeDecodedData(ByteBufferDescriptor data) {
|
||||||
|
decodedUnits.freePopulatedObject(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ public class AudioStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamListener.playDecodedAudio(samples.data, samples.offset, samples.length);
|
streamListener.playDecodedAudio(samples.data, samples.offset, samples.length);
|
||||||
|
depacketizer.freeDecodedData(samples);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
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 com.limelight.LimeLog;
|
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.ConnectionStatusListener;
|
import com.limelight.nvstream.av.ConnectionStatusListener;
|
||||||
|
import com.limelight.nvstream.av.PopulatedBufferList;
|
||||||
|
|
||||||
public class VideoDepacketizer {
|
public class VideoDepacketizer {
|
||||||
|
|
||||||
@ -32,12 +32,18 @@ public class VideoDepacketizer {
|
|||||||
private int nominalPacketSize;
|
private int nominalPacketSize;
|
||||||
|
|
||||||
private static final int DU_LIMIT = 15;
|
private static final int DU_LIMIT = 15;
|
||||||
private LinkedBlockingQueue<DecodeUnit> decodedUnits = new LinkedBlockingQueue<DecodeUnit>(DU_LIMIT);
|
private PopulatedBufferList<DecodeUnit> decodedUnits;
|
||||||
|
|
||||||
public VideoDepacketizer(ConnectionStatusListener controlListener, int nominalPacketSize)
|
public VideoDepacketizer(ConnectionStatusListener controlListener, int nominalPacketSize)
|
||||||
{
|
{
|
||||||
this.controlListener = controlListener;
|
this.controlListener = controlListener;
|
||||||
this.nominalPacketSize = nominalPacketSize;
|
this.nominalPacketSize = nominalPacketSize;
|
||||||
|
|
||||||
|
decodedUnits = new PopulatedBufferList<DecodeUnit>(DU_LIMIT, new PopulatedBufferList.BufferFactory() {
|
||||||
|
public Object createFreeBuffer() {
|
||||||
|
return new DecodeUnit();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearAvcFrameState()
|
private void clearAvcFrameState()
|
||||||
@ -66,22 +72,32 @@ public class VideoDepacketizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Construct the H264 decode unit
|
// Construct the H264 decode unit
|
||||||
DecodeUnit du = new DecodeUnit(DecodeUnit.TYPE_H264, avcFrameDataChain,
|
DecodeUnit du = decodedUnits.pollFreeObject();
|
||||||
avcFrameDataLength, frameNumber, frameStartTime, flags);
|
if (du == null) {
|
||||||
if (!decodedUnits.offer(du)) {
|
|
||||||
LimeLog.warning("Video decoder is too slow! Forced to drop decode units");
|
LimeLog.warning("Video decoder is too slow! Forced to drop decode units");
|
||||||
|
|
||||||
// Invalidate all frames from the start of the DU queue
|
// Invalidate all frames from the start of the DU queue
|
||||||
controlListener.connectionSinkTooSlow(decodedUnits.remove().getFrameNumber(), frameNumber);
|
controlListener.connectionSinkTooSlow(decodedUnits.pollPopulatedObject().getFrameNumber(), frameNumber);
|
||||||
|
|
||||||
// Remove existing frames
|
// Remove existing frames
|
||||||
decodedUnits.clear();
|
decodedUnits.clearPopulatedObjects();
|
||||||
|
|
||||||
// Add this frame
|
// Try again
|
||||||
decodedUnits.add(du);
|
du = decodedUnits.pollFreeObject();
|
||||||
|
if (du == null) {
|
||||||
|
LimeLog.warning("Video decoder is leaking decode units!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize the free DU
|
||||||
|
du.initialize(DecodeUnit.TYPE_H264, avcFrameDataChain,
|
||||||
|
avcFrameDataLength, frameNumber, frameStartTime, flags);
|
||||||
|
|
||||||
controlListener.connectionReceivedFrame(frameNumber);
|
controlListener.connectionReceivedFrame(frameNumber);
|
||||||
|
|
||||||
|
// Submit the DU to the consumer
|
||||||
|
decodedUnits.addPopulatedObject(du);
|
||||||
|
|
||||||
// Clear old state
|
// Clear old state
|
||||||
clearAvcFrameState();
|
clearAvcFrameState();
|
||||||
@ -335,12 +351,17 @@ public class VideoDepacketizer {
|
|||||||
|
|
||||||
public DecodeUnit takeNextDecodeUnit() throws InterruptedException
|
public DecodeUnit takeNextDecodeUnit() throws InterruptedException
|
||||||
{
|
{
|
||||||
return decodedUnits.take();
|
return decodedUnits.takePopulatedObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DecodeUnit pollNextDecodeUnit()
|
public DecodeUnit pollNextDecodeUnit()
|
||||||
{
|
{
|
||||||
return decodedUnits.poll();
|
return decodedUnits.pollPopulatedObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void freeDecodeUnit(DecodeUnit du)
|
||||||
|
{
|
||||||
|
decodedUnits.freePopulatedObject(du);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user