mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-23 13:02:45 +00:00
Stop allocating RtpPacket and VideoPacket objects in the reassembly path
This commit is contained in:
parent
6c5ec3d2e9
commit
86e2657613
@ -8,6 +8,8 @@ public class RtpPacket {
|
|||||||
private short seqNum;
|
private short seqNum;
|
||||||
private ByteBufferDescriptor buffer;
|
private ByteBufferDescriptor buffer;
|
||||||
|
|
||||||
|
public static final int HEADER_SIZE = 12;
|
||||||
|
|
||||||
public RtpPacket(ByteBufferDescriptor buffer)
|
public RtpPacket(ByteBufferDescriptor buffer)
|
||||||
{
|
{
|
||||||
this.buffer = new ByteBufferDescriptor(buffer);
|
this.buffer = new ByteBufferDescriptor(buffer);
|
||||||
@ -41,6 +43,6 @@ public class RtpPacket {
|
|||||||
|
|
||||||
public ByteBufferDescriptor getNewPayloadDescriptor()
|
public ByteBufferDescriptor getNewPayloadDescriptor()
|
||||||
{
|
{
|
||||||
return new ByteBufferDescriptor(buffer.data, buffer.offset+12, buffer.length-12);
|
return new ByteBufferDescriptor(buffer.data, buffer.offset+HEADER_SIZE, buffer.length-HEADER_SIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ 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.RtpPacket;
|
|
||||||
import com.limelight.nvstream.av.ConnectionStatusListener;
|
import com.limelight.nvstream.av.ConnectionStatusListener;
|
||||||
|
|
||||||
public class VideoDepacketizer {
|
public class VideoDepacketizer {
|
||||||
@ -26,7 +25,8 @@ public class VideoDepacketizer {
|
|||||||
private long frameStartTime;
|
private long frameStartTime;
|
||||||
|
|
||||||
// Cached objects
|
// Cached objects
|
||||||
private ByteBufferDescriptor cachedDesc = new ByteBufferDescriptor(null, 0, 0);
|
private ByteBufferDescriptor cachedReassemblyDesc = new ByteBufferDescriptor(null, 0, 0);
|
||||||
|
private ByteBufferDescriptor cachedSpecialDesc = new ByteBufferDescriptor(null, 0, 0);
|
||||||
|
|
||||||
private ConnectionStatusListener controlListener;
|
private ConnectionStatusListener controlListener;
|
||||||
|
|
||||||
@ -78,15 +78,15 @@ public class VideoDepacketizer {
|
|||||||
int start = location.offset;
|
int start = location.offset;
|
||||||
|
|
||||||
// Check for a special sequence
|
// Check for a special sequence
|
||||||
if (NAL.getSpecialSequenceDescriptor(location, cachedDesc))
|
if (NAL.getSpecialSequenceDescriptor(location, cachedSpecialDesc))
|
||||||
{
|
{
|
||||||
if (NAL.isAvcStartSequence(cachedDesc))
|
if (NAL.isAvcStartSequence(cachedSpecialDesc))
|
||||||
{
|
{
|
||||||
// We're decoding H264 now
|
// We're decoding H264 now
|
||||||
currentlyDecoding = DecodeUnit.TYPE_H264;
|
currentlyDecoding = DecodeUnit.TYPE_H264;
|
||||||
|
|
||||||
// Check if it's the end of the last frame
|
// Check if it's the end of the last frame
|
||||||
if (NAL.isAvcFrameStart(cachedDesc))
|
if (NAL.isAvcFrameStart(cachedSpecialDesc))
|
||||||
{
|
{
|
||||||
// Reassemble any pending AVC NAL
|
// Reassemble any pending AVC NAL
|
||||||
reassembleAvcFrame(packet.getFrameIndex());
|
reassembleAvcFrame(packet.getFrameIndex());
|
||||||
@ -97,14 +97,14 @@ public class VideoDepacketizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Skip the start sequence
|
// Skip the start sequence
|
||||||
location.length -= cachedDesc.length;
|
location.length -= cachedSpecialDesc.length;
|
||||||
location.offset += cachedDesc.length;
|
location.offset += cachedSpecialDesc.length;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Check if this is padding after a full AVC frame
|
// Check if this is padding after a full AVC frame
|
||||||
if (currentlyDecoding == DecodeUnit.TYPE_H264 &&
|
if (currentlyDecoding == DecodeUnit.TYPE_H264 &&
|
||||||
NAL.isPadding(cachedDesc)) {
|
NAL.isPadding(cachedSpecialDesc)) {
|
||||||
// The decode unit is complete
|
// The decode unit is complete
|
||||||
reassembleAvcFrame(packet.getFrameIndex());
|
reassembleAvcFrame(packet.getFrameIndex());
|
||||||
}
|
}
|
||||||
@ -125,12 +125,12 @@ public class VideoDepacketizer {
|
|||||||
if (location.data[location.offset] == 0x00)
|
if (location.data[location.offset] == 0x00)
|
||||||
{
|
{
|
||||||
// Check if this should end the current NAL
|
// Check if this should end the current NAL
|
||||||
if (NAL.getSpecialSequenceDescriptor(location, cachedDesc))
|
if (NAL.getSpecialSequenceDescriptor(location, cachedSpecialDesc))
|
||||||
{
|
{
|
||||||
// Only stop if we're decoding something or this
|
// Only stop if we're decoding something or this
|
||||||
// isn't padding
|
// isn't padding
|
||||||
if (currentlyDecoding != DecodeUnit.TYPE_UNKNOWN ||
|
if (currentlyDecoding != DecodeUnit.TYPE_UNKNOWN ||
|
||||||
!NAL.isPadding(cachedDesc))
|
!NAL.isPadding(cachedSpecialDesc))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -163,19 +163,20 @@ public class VideoDepacketizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the payload data to the chain
|
// Add the payload data to the chain
|
||||||
avcFrameDataChain.add(location);
|
avcFrameDataChain.add(new ByteBufferDescriptor(location));
|
||||||
avcFrameDataLength += location.length;
|
avcFrameDataLength += location.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addInputData(VideoPacket packet)
|
public void addInputData(VideoPacket packet)
|
||||||
{
|
{
|
||||||
ByteBufferDescriptor location = packet.getNewPayloadDescriptor();
|
// Load our reassembly descriptor
|
||||||
|
packet.initializePayloadDescriptor(cachedReassemblyDesc);
|
||||||
|
|
||||||
// Runt packets get decoded using the slow path
|
// Runt packets get decoded using the slow path
|
||||||
// These packets stand alone so there's no need to verify
|
// These packets stand alone so there's no need to verify
|
||||||
// sequencing before submitting
|
// sequencing before submitting
|
||||||
if (location.length < 968) {
|
if (cachedReassemblyDesc.length < 968) {
|
||||||
addInputDataSlow(packet, location);
|
addInputDataSlow(packet, cachedReassemblyDesc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,21 +278,22 @@ public class VideoDepacketizer {
|
|||||||
nextPacketNumber++;
|
nextPacketNumber++;
|
||||||
|
|
||||||
// Remove extra padding
|
// Remove extra padding
|
||||||
location.length = packet.getPayloadLength();
|
cachedReassemblyDesc.length = packet.getPayloadLength();
|
||||||
|
|
||||||
if (firstPacket)
|
if (firstPacket)
|
||||||
{
|
{
|
||||||
if (NAL.getSpecialSequenceDescriptor(location, cachedDesc) && NAL.isAvcFrameStart(cachedDesc)
|
if (NAL.getSpecialSequenceDescriptor(cachedReassemblyDesc, cachedSpecialDesc)
|
||||||
&& cachedDesc.data[cachedDesc.offset+cachedDesc.length] == 0x67)
|
&& NAL.isAvcFrameStart(cachedSpecialDesc)
|
||||||
|
&& cachedSpecialDesc.data[cachedSpecialDesc.offset+cachedSpecialDesc.length] == 0x67)
|
||||||
{
|
{
|
||||||
// SPS and PPS prefix is padded between NALs, so we must decode it with the slow path
|
// SPS and PPS prefix is padded between NALs, so we must decode it with the slow path
|
||||||
clearAvcFrameState();
|
clearAvcFrameState();
|
||||||
addInputDataSlow(packet, location);
|
addInputDataSlow(packet, cachedReassemblyDesc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addInputDataFast(packet, location, firstPacket);
|
addInputDataFast(packet, cachedReassemblyDesc, firstPacket);
|
||||||
|
|
||||||
// We can't use the EOF flag here because real frames can be split across
|
// We can't use the EOF flag here because real frames can be split across
|
||||||
// multiple "frames" when packetized to fit under the bandwidth ceiling
|
// multiple "frames" when packetized to fit under the bandwidth ceiling
|
||||||
@ -313,12 +315,6 @@ public class VideoDepacketizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addInputData(RtpPacket packet)
|
|
||||||
{
|
|
||||||
ByteBufferDescriptor rtpPayload = packet.getNewPayloadDescriptor();
|
|
||||||
addInputData(new VideoPacket(rtpPayload));
|
|
||||||
}
|
|
||||||
|
|
||||||
public DecodeUnit takeNextDecodeUnit() throws InterruptedException
|
public DecodeUnit takeNextDecodeUnit() throws InterruptedException
|
||||||
{
|
{
|
||||||
return decodedUnits.take();
|
return decodedUnits.take();
|
||||||
|
@ -4,9 +4,13 @@ import java.nio.ByteBuffer;
|
|||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
import com.limelight.nvstream.av.ByteBufferDescriptor;
|
import com.limelight.nvstream.av.ByteBufferDescriptor;
|
||||||
|
import com.limelight.nvstream.av.RtpPacket;
|
||||||
|
|
||||||
public class VideoPacket {
|
public class VideoPacket {
|
||||||
private ByteBufferDescriptor buffer;
|
private ByteBufferDescriptor buffer;
|
||||||
|
private ByteBuffer byteBuffer;
|
||||||
|
|
||||||
|
private int dataOffset;
|
||||||
|
|
||||||
private int frameIndex;
|
private int frameIndex;
|
||||||
private int packetIndex;
|
private int packetIndex;
|
||||||
@ -18,19 +22,47 @@ public class VideoPacket {
|
|||||||
public static final int FLAG_EOF = 0x2;
|
public static final int FLAG_EOF = 0x2;
|
||||||
public static final int FLAG_SOF = 0x4;
|
public static final int FLAG_SOF = 0x4;
|
||||||
|
|
||||||
public VideoPacket(ByteBufferDescriptor rtpPayload)
|
public VideoPacket(byte[] buffer)
|
||||||
{
|
{
|
||||||
buffer = new ByteBufferDescriptor(rtpPayload);
|
this.buffer = new ByteBufferDescriptor(buffer, 0, buffer.length);
|
||||||
|
this.byteBuffer = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initializeWithLengthNoRtpHeader(int length)
|
||||||
|
{
|
||||||
|
// Read the video header fields
|
||||||
|
frameIndex = byteBuffer.getInt();
|
||||||
|
packetIndex = byteBuffer.getInt();
|
||||||
|
totalPackets = byteBuffer.getInt();
|
||||||
|
flags = byteBuffer.getInt();
|
||||||
|
payloadLength = byteBuffer.getInt();
|
||||||
|
streamPacketIndex = byteBuffer.getInt();
|
||||||
|
|
||||||
ByteBuffer bb = ByteBuffer.wrap(buffer.data).order(ByteOrder.LITTLE_ENDIAN);
|
// Data offset without the RTP header
|
||||||
bb.position(buffer.offset);
|
dataOffset = 56;
|
||||||
|
|
||||||
frameIndex = bb.getInt();
|
// Update descriptor length
|
||||||
packetIndex = bb.getInt();
|
buffer.length = length;
|
||||||
totalPackets = bb.getInt();
|
}
|
||||||
flags = bb.getInt();
|
|
||||||
payloadLength = bb.getInt();
|
public void initializeWithLength(int length)
|
||||||
streamPacketIndex = bb.getInt();
|
{
|
||||||
|
// Skip the RTP header
|
||||||
|
byteBuffer.position(RtpPacket.HEADER_SIZE);
|
||||||
|
|
||||||
|
// Read the video header fields
|
||||||
|
frameIndex = byteBuffer.getInt();
|
||||||
|
packetIndex = byteBuffer.getInt();
|
||||||
|
totalPackets = byteBuffer.getInt();
|
||||||
|
flags = byteBuffer.getInt();
|
||||||
|
payloadLength = byteBuffer.getInt();
|
||||||
|
streamPacketIndex = byteBuffer.getInt();
|
||||||
|
|
||||||
|
// Data offset includes the RTP header
|
||||||
|
dataOffset = RtpPacket.HEADER_SIZE + 56;
|
||||||
|
|
||||||
|
// Update descriptor length
|
||||||
|
buffer.length = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFlags()
|
public int getFlags()
|
||||||
@ -63,8 +95,13 @@ public class VideoPacket {
|
|||||||
return streamPacketIndex;
|
return streamPacketIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ByteBufferDescriptor getNewPayloadDescriptor()
|
public byte[] getBuffer()
|
||||||
{
|
{
|
||||||
return new ByteBufferDescriptor(buffer.data, buffer.offset+56, buffer.length-56);
|
return buffer.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initializePayloadDescriptor(ByteBufferDescriptor bb)
|
||||||
|
{
|
||||||
|
bb.reinitialize(buffer.data, buffer.offset+dataOffset, buffer.length-dataOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,6 @@ import java.util.LinkedList;
|
|||||||
|
|
||||||
import com.limelight.nvstream.NvConnectionListener;
|
import com.limelight.nvstream.NvConnectionListener;
|
||||||
import com.limelight.nvstream.StreamConfiguration;
|
import com.limelight.nvstream.StreamConfiguration;
|
||||||
import com.limelight.nvstream.av.ByteBufferDescriptor;
|
|
||||||
import com.limelight.nvstream.av.RtpPacket;
|
|
||||||
import com.limelight.nvstream.av.ConnectionStatusListener;
|
import com.limelight.nvstream.av.ConnectionStatusListener;
|
||||||
|
|
||||||
public class VideoStream {
|
public class VideoStream {
|
||||||
@ -118,7 +116,9 @@ public class VideoStream {
|
|||||||
offset += bytesRead;
|
offset += bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
depacketizer.addInputData(new VideoPacket(new ByteBufferDescriptor(firstFrame, 0, offset)));
|
VideoPacket packet = new VideoPacket(firstFrame);
|
||||||
|
packet.initializeWithLengthNoRtpHeader(offset);
|
||||||
|
depacketizer.addInputData(packet);
|
||||||
} finally {
|
} finally {
|
||||||
firstFrameSocket.close();
|
firstFrameSocket.close();
|
||||||
firstFrameSocket = null;
|
firstFrameSocket = null;
|
||||||
@ -179,31 +179,29 @@ public class VideoStream {
|
|||||||
Thread t = new Thread() {
|
Thread t = new Thread() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
ByteBufferDescriptor ring[] = new ByteBufferDescriptor[VIDEO_RING_SIZE];
|
VideoPacket ring[] = new VideoPacket[VIDEO_RING_SIZE];
|
||||||
int ringIndex = 0;
|
int ringIndex = 0;
|
||||||
|
|
||||||
// Preinitialize the ring buffer
|
// Preinitialize the ring buffer
|
||||||
for (int i = 0; i < VIDEO_RING_SIZE; i++) {
|
for (int i = 0; i < VIDEO_RING_SIZE; i++) {
|
||||||
ring[i] = new ByteBufferDescriptor(new byte[MAX_PACKET_SIZE], 0, MAX_PACKET_SIZE);
|
ring[i] = new VideoPacket(new byte[MAX_PACKET_SIZE]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteBufferDescriptor desc;
|
byte[] buffer;
|
||||||
DatagramPacket packet = new DatagramPacket(new byte[1], 1); // Placeholder array
|
DatagramPacket packet = new DatagramPacket(new byte[1], 1); // Placeholder array
|
||||||
while (!isInterrupted())
|
while (!isInterrupted())
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
// Pull the next buffer in the ring and reset it
|
// Pull the next buffer in the ring and reset it
|
||||||
desc = ring[ringIndex];
|
buffer = ring[ringIndex].getBuffer();
|
||||||
desc.length = MAX_PACKET_SIZE;
|
|
||||||
desc.offset = 0;
|
|
||||||
|
|
||||||
// Read the video data off the network
|
// Read the video data off the network
|
||||||
packet.setData(desc.data, desc.offset, desc.length);
|
packet.setData(buffer, 0, buffer.length);
|
||||||
rtp.receive(packet);
|
rtp.receive(packet);
|
||||||
|
|
||||||
// Submit video data to the depacketizer
|
// Submit video data to the depacketizer
|
||||||
desc.length = packet.getLength();
|
ring[ringIndex].initializeWithLength(packet.getLength());
|
||||||
depacketizer.addInputData(new RtpPacket(desc));
|
depacketizer.addInputData(ring[ringIndex]);
|
||||||
ringIndex = (ringIndex + 1) % VIDEO_RING_SIZE;
|
ringIndex = (ringIndex + 1) % VIDEO_RING_SIZE;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
listener.connectionTerminated(e);
|
listener.connectionTerminated(e);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user