mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-20 11:33:06 +00:00
Initial work on updating for GFE 2.0.1's new RTSP-based handshake protocol
This commit is contained in:
parent
4ee99a78b2
commit
d01a28c57f
@ -3,5 +3,6 @@
|
|||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="src" path="src"/>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||||
<classpathentry kind="lib" path="libs/xpp3-1.1.4c.jar"/>
|
<classpathentry kind="lib" path="libs/xpp3-1.1.4c.jar"/>
|
||||||
|
<classpathentry kind="lib" path="libs/tinyrtsp.jar"/>
|
||||||
<classpathentry kind="output" path="bin"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
BIN
moonlight-common/libs/tinyrtsp.jar
Normal file
BIN
moonlight-common/libs/tinyrtsp.jar
Normal file
Binary file not shown.
@ -1,133 +0,0 @@
|
|||||||
package com.limelight.nvstream;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.Socket;
|
|
||||||
|
|
||||||
public class Handshake {
|
|
||||||
public static final int PORT = 47991;
|
|
||||||
|
|
||||||
public static final int HANDSHAKE_TIMEOUT = 5000;
|
|
||||||
|
|
||||||
public static final byte[] PLATFORM_HELLO =
|
|
||||||
{
|
|
||||||
(byte)0x07,
|
|
||||||
(byte)0x00,
|
|
||||||
(byte)0x00,
|
|
||||||
(byte)0x00,
|
|
||||||
|
|
||||||
// android in ASCII
|
|
||||||
(byte)0x61,
|
|
||||||
(byte)0x6e,
|
|
||||||
(byte)0x64,
|
|
||||||
(byte)0x72,
|
|
||||||
(byte)0x6f,
|
|
||||||
(byte)0x69,
|
|
||||||
(byte)0x64,
|
|
||||||
|
|
||||||
(byte)0x03,
|
|
||||||
(byte)0x01,
|
|
||||||
(byte)0x00,
|
|
||||||
(byte)0x00
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final byte[] PACKET_2 =
|
|
||||||
{
|
|
||||||
(byte)0x01,
|
|
||||||
(byte)0x03,
|
|
||||||
(byte)0x02,
|
|
||||||
(byte)0x00,
|
|
||||||
(byte)0x08,
|
|
||||||
(byte)0x00
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final byte[] PACKET_3 =
|
|
||||||
{
|
|
||||||
(byte)0x04,
|
|
||||||
(byte)0x01,
|
|
||||||
(byte)0x00,
|
|
||||||
(byte)0x00,
|
|
||||||
|
|
||||||
(byte)0x00,
|
|
||||||
(byte)0x00,
|
|
||||||
(byte)0x00,
|
|
||||||
(byte)0x00
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final byte[] PACKET_4 =
|
|
||||||
{
|
|
||||||
(byte)0x01,
|
|
||||||
(byte)0x01,
|
|
||||||
(byte)0x00,
|
|
||||||
(byte)0x00
|
|
||||||
};
|
|
||||||
|
|
||||||
private static boolean waitAndDiscardResponse(InputStream in)
|
|
||||||
{
|
|
||||||
// Wait for response and discard response
|
|
||||||
try {
|
|
||||||
in.read();
|
|
||||||
|
|
||||||
// Wait for the full response to come in
|
|
||||||
Thread.sleep(250);
|
|
||||||
|
|
||||||
for (int i = 0; i < in.available(); i++)
|
|
||||||
in.read();
|
|
||||||
|
|
||||||
} catch (IOException e1) {
|
|
||||||
return false;
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean performHandshake(InetAddress host) throws IOException
|
|
||||||
{
|
|
||||||
Socket s = new Socket();
|
|
||||||
s.connect(new InetSocketAddress(host, PORT), HANDSHAKE_TIMEOUT);
|
|
||||||
s.setSoTimeout(HANDSHAKE_TIMEOUT);
|
|
||||||
OutputStream out = s.getOutputStream();
|
|
||||||
InputStream in = s.getInputStream();
|
|
||||||
|
|
||||||
// First packet
|
|
||||||
out.write(PLATFORM_HELLO);
|
|
||||||
out.flush();
|
|
||||||
|
|
||||||
if (!waitAndDiscardResponse(in)) {
|
|
||||||
s.close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Second packet
|
|
||||||
out.write(PACKET_2);
|
|
||||||
out.flush();
|
|
||||||
|
|
||||||
if (!waitAndDiscardResponse(in)) {
|
|
||||||
s.close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Third packet
|
|
||||||
out.write(PACKET_3);
|
|
||||||
out.flush();
|
|
||||||
|
|
||||||
if (!waitAndDiscardResponse(in)) {
|
|
||||||
s.close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fourth packet
|
|
||||||
out.write(PACKET_4);
|
|
||||||
out.flush();
|
|
||||||
|
|
||||||
// Done
|
|
||||||
s.close();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -22,6 +22,7 @@ import com.limelight.nvstream.http.GfeHttpResponseException;
|
|||||||
import com.limelight.nvstream.http.NvApp;
|
import com.limelight.nvstream.http.NvApp;
|
||||||
import com.limelight.nvstream.http.NvHTTP;
|
import com.limelight.nvstream.http.NvHTTP;
|
||||||
import com.limelight.nvstream.input.NvController;
|
import com.limelight.nvstream.input.NvController;
|
||||||
|
import com.limelight.nvstream.rtsp.RtspConnection;
|
||||||
|
|
||||||
public class NvConnection {
|
public class NvConnection {
|
||||||
private String host;
|
private String host;
|
||||||
@ -190,9 +191,16 @@ public class NvConnection {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean doRtspHandshake() throws IOException
|
||||||
|
{
|
||||||
|
RtspConnection r = new RtspConnection(hostAddr);
|
||||||
|
r.doRtspHandshake(config);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean startControlStream() throws IOException
|
private boolean startControlStream() throws IOException
|
||||||
{
|
{
|
||||||
controlStream = new ControlStream(hostAddr, listener, config);
|
controlStream = new ControlStream(hostAddr, listener);
|
||||||
controlStream.initialize();
|
controlStream.initialize();
|
||||||
controlStream.start();
|
controlStream.start();
|
||||||
return true;
|
return true;
|
||||||
@ -237,8 +245,8 @@ public class NvConnection {
|
|||||||
success = startSteamBigPicture();
|
success = startSteamBigPicture();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HANDSHAKE:
|
case RTSP_HANDSHAKE:
|
||||||
success = Handshake.performHandshake(hostAddr);
|
success = doRtspHandshake();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONTROL_START:
|
case CONTROL_START:
|
||||||
|
@ -4,7 +4,7 @@ public interface NvConnectionListener {
|
|||||||
|
|
||||||
public enum Stage {
|
public enum Stage {
|
||||||
LAUNCH_APP("app"),
|
LAUNCH_APP("app"),
|
||||||
HANDSHAKE("handshake"),
|
RTSP_HANDSHAKE("RTSP handshake"),
|
||||||
CONTROL_START("control connection"),
|
CONTROL_START("control connection"),
|
||||||
VIDEO_START("video stream"),
|
VIDEO_START("video stream"),
|
||||||
AUDIO_START("audio stream"),
|
AUDIO_START("audio stream"),
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
package com.limelight.nvstream.control;
|
|
||||||
|
|
||||||
public class ByteConfigTuple extends ConfigTuple {
|
|
||||||
public static final short PAYLOAD_LENGTH = 1;
|
|
||||||
|
|
||||||
public byte payload;
|
|
||||||
|
|
||||||
public ByteConfigTuple(short packetType, byte payload) {
|
|
||||||
super(packetType, PAYLOAD_LENGTH);
|
|
||||||
this.payload = payload;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] payloadToWire() {
|
|
||||||
return new byte[] {payload};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,200 +0,0 @@
|
|||||||
package com.limelight.nvstream.control;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import com.limelight.nvstream.StreamConfiguration;
|
|
||||||
|
|
||||||
public class Config {
|
|
||||||
|
|
||||||
public static final ConfigTuple[] CONFIG_720_60 =
|
|
||||||
{
|
|
||||||
new ByteConfigTuple((short)0x1207, (byte)1), //iFrameOnDemand
|
|
||||||
new IntConfigTuple((short)0x120b, 7), //averageBitrate
|
|
||||||
new IntConfigTuple((short)0x120c, 7), //peakBitrate
|
|
||||||
new IntConfigTuple((short)0x120d, 60), //gopLength
|
|
||||||
new IntConfigTuple((short)0x120e, 100), //vbvMultiplier
|
|
||||||
new IntConfigTuple((short)0x120f, 5), //rateControlMode
|
|
||||||
new IntConfigTuple((short)0x1210, 4), //slicesPerFrame
|
|
||||||
new IntConfigTuple((short)0x1202, 1024), //packetSize
|
|
||||||
new ByteConfigTuple((short)0x1203, (byte)0), //recordServerStats
|
|
||||||
new ByteConfigTuple((short)0x1201, (byte)0), //serverCapture
|
|
||||||
new ByteConfigTuple((short)0x1234, (byte)0), //serverNetworkCapture
|
|
||||||
new ByteConfigTuple((short)0x1248, (byte)0),
|
|
||||||
new ByteConfigTuple((short)0x1208, (byte)1), //refPicInvalidation
|
|
||||||
new ByteConfigTuple((short)0x1209, (byte)0), //enableFrameRateCtrl
|
|
||||||
new IntConfigTuple((short)0x1212, 3000), //pingBackIntervalMs
|
|
||||||
new IntConfigTuple((short)0x1238, 10000), //pingBackTimeoutMs
|
|
||||||
new ByteConfigTuple((short)0x1211, (byte)0), //enableSubframeEncoding
|
|
||||||
new ByteConfigTuple((short)0x1213, (byte)1), //videoQoSFecEnable
|
|
||||||
new IntConfigTuple((short)0x1214, 50), //videoQoSFecNumSrcPackets
|
|
||||||
new IntConfigTuple((short)0x1215, 60), //videoQoSFecNumOutPackets
|
|
||||||
new IntConfigTuple((short)0x1216, 20), //videoQoSFecRepairPercent
|
|
||||||
new IntConfigTuple((short)0x1217, 0), //videoQoSTsEnable
|
|
||||||
new IntConfigTuple((short)0x1218, 8), //videoQoSTsAverageBitrate
|
|
||||||
new IntConfigTuple((short)0x1219, 10), //videoQoSTsMaximumBitrate
|
|
||||||
new IntConfigTuple((short)0x121a, 311), //videoQoSBwFlags
|
|
||||||
new IntConfigTuple((short)0x121b, 10000), //videoQoSBwMaximumBitrate
|
|
||||||
new IntConfigTuple((short)0x121c, 2000), //videoQoSBwMinimumBitrate
|
|
||||||
new IntConfigTuple((short)0x121d, 50), //videoQoSBwStatsTime
|
|
||||||
new IntConfigTuple((short)0x121e, 3000), //videoQoSBwZeroLossCount
|
|
||||||
new IntConfigTuple((short)0x121f, 2), //videoQoSBwLossThreshold
|
|
||||||
new IntConfigTuple((short)0x122a, 5000), //videoQoSBwOwdThreshold
|
|
||||||
new IntConfigTuple((short)0x122b, 500), //videoQoSBwOwdReference
|
|
||||||
new IntConfigTuple((short)0x1220, 75), //videoQoSBwLossWaitTime
|
|
||||||
new IntConfigTuple((short)0x1221, 25), //videoQoSBwRateDropMultiplier
|
|
||||||
new IntConfigTuple((short)0x1222, 10), //videoQoSBwRateGainMultiplier
|
|
||||||
new IntConfigTuple((short)0x1223, 60), //videoQoSBwMaxFps
|
|
||||||
new IntConfigTuple((short)0x1224, 30), //videoQoSBwMinFps
|
|
||||||
new IntConfigTuple((short)0x1225, 3), //videoQoSBwFpsThreshold
|
|
||||||
new IntConfigTuple((short)0x1226, 1000), //videoQoSBwJitterThreshold
|
|
||||||
new IntConfigTuple((short)0x1227, 5000), //videoQoSBwJitterWaitTime
|
|
||||||
new IntConfigTuple((short)0x1228, 5000), //videoQoSBwNoJitterWaitTime
|
|
||||||
new IntConfigTuple((short)0x124e, 110),
|
|
||||||
new IntConfigTuple((short)0x1237, 10), //videoQoSBwEarlyDetectionEnableL1Threshold
|
|
||||||
new IntConfigTuple((short)0x1236, 6), //videoQoSBwEarlyDetectionEnableL0Threshold
|
|
||||||
new IntConfigTuple((short)0x1235, 4), //videoQoSBwEarlyDetectionDisableThreshold
|
|
||||||
new IntConfigTuple((short)0x1242, 20000), //videoQoSBwEarlyDetectionWaitTime
|
|
||||||
new IntConfigTuple((short)0x1244, 100),
|
|
||||||
new IntConfigTuple((short)0x1245, 1000),
|
|
||||||
new IntConfigTuple((short)0x1246, 720),
|
|
||||||
new IntConfigTuple((short)0x1247, 480),
|
|
||||||
new IntConfigTuple((short)0x1229, 5000), //videoQosVideoQualityScoreUpdateTime
|
|
||||||
new ByteConfigTuple((short)0x122e, (byte)7), //videoQosTrafficType
|
|
||||||
new IntConfigTuple((short)0x1231, 40), //videoQosBnNotifyUpBoundThreshold
|
|
||||||
new IntConfigTuple((short)0x1232, 25), //videoQosBnNotifyLowBoundThreshold
|
|
||||||
new IntConfigTuple((short)0x1233, 3000), //videoQosBnNotifyWaitTime
|
|
||||||
new IntConfigTuple((short)0x122c, 3), //videoQosInvalidateThreshold
|
|
||||||
new IntConfigTuple((short)0x122d, 10), //videoQosInvalidateSkipPercentage
|
|
||||||
/*new IntConfigTuple((short)0x123b, 12),
|
|
||||||
new IntConfigTuple((short)0x123c, 3),
|
|
||||||
new IntConfigTuple((short)0x1249, 0),
|
|
||||||
new IntConfigTuple((short)0x124a, 4000),
|
|
||||||
new IntConfigTuple((short)0x124b, 5000),
|
|
||||||
new IntConfigTuple((short)0x124c, 6000),
|
|
||||||
new IntConfigTuple((short)0x124d, 1000),*/
|
|
||||||
new IntConfigTuple((short)0x122f, 0), //riSecurityProtocol
|
|
||||||
new ShortConfigTuple((short)0x1230, (short)0), //riSecInfoUsePredefinedCert
|
|
||||||
new IntConfigTuple((short)0x1239, 0), //videoFrameDropIntervalNumber
|
|
||||||
new IntConfigTuple((short)0x123a, 0), //videoFrameDropContinualNumber
|
|
||||||
new IntConfigTuple((short)0x123d, 96000), //audioQosBitRate
|
|
||||||
new IntConfigTuple((short)0x123e, 5), //audioQosPacketDuration
|
|
||||||
new IntConfigTuple((short)0x123f, 1), //audioQosEnablePacketLossPercentage
|
|
||||||
new IntConfigTuple((short)0x1243, 100) //audioQosPacketLossPercentageUpdateInterval
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final ConfigTuple[] CONFIG_1080_30_DIFF =
|
|
||||||
{
|
|
||||||
new IntConfigTuple((short)0x120b, 10), //averageBitrate
|
|
||||||
new IntConfigTuple((short)0x120c, 10), //peakBitrate
|
|
||||||
|
|
||||||
// HACK: Streaming 1080p30 without these options causes the encoder
|
|
||||||
// to step down to 720p which breaks the CPU decoder
|
|
||||||
new IntConfigTuple((short)0x121b, 25000), //videoQoSBwMaximumBitrate
|
|
||||||
new IntConfigTuple((short)0x121c, 25000), //videoQoSBwMinimumBitrate
|
|
||||||
|
|
||||||
new IntConfigTuple((short)0x1246, 1280),
|
|
||||||
new IntConfigTuple((short)0x1247, 720),
|
|
||||||
/*new IntConfigTuple((short)0x124a, 5000),
|
|
||||||
new IntConfigTuple((short)0x124c, 7000),*/
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final ConfigTuple[] CONFIG_1080_60_DIFF =
|
|
||||||
{
|
|
||||||
new IntConfigTuple((short)0x120b, 30), //averageBitrate
|
|
||||||
new IntConfigTuple((short)0x120c, 30), //peakBitrate
|
|
||||||
new IntConfigTuple((short)0x120f, 4), //rateControlMode
|
|
||||||
new IntConfigTuple((short)0x121b, 30000), //videoQoSBwMaximumBitrate
|
|
||||||
new IntConfigTuple((short)0x121c, 25000), //videoQoSBwMinimumBitrate
|
|
||||||
new IntConfigTuple((short)0x1245, 3000),
|
|
||||||
new IntConfigTuple((short)0x1246, 1280),
|
|
||||||
new IntConfigTuple((short)0x1247, 720),
|
|
||||||
/*new IntConfigTuple((short)0x124a, 5000),
|
|
||||||
new IntConfigTuple((short)0x124c, 7000),*/
|
|
||||||
};
|
|
||||||
|
|
||||||
private StreamConfiguration streamConfig;
|
|
||||||
|
|
||||||
public Config(StreamConfiguration streamConfig) {
|
|
||||||
this.streamConfig = streamConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateSetWithConfig(ArrayList<ConfigTuple> set, ConfigTuple[] config)
|
|
||||||
{
|
|
||||||
for (ConfigTuple tuple : config)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < set.size(); i++) {
|
|
||||||
ConfigTuple existingTuple = set.get(i);
|
|
||||||
if (existingTuple.packetType == tuple.packetType) {
|
|
||||||
set.remove(i);
|
|
||||||
set.add(i, tuple);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == set.size()) {
|
|
||||||
set.add(tuple);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getConfigOnWireSize(ArrayList<ConfigTuple> tupleSet)
|
|
||||||
{
|
|
||||||
int size = 0;
|
|
||||||
|
|
||||||
for (ConfigTuple t : tupleSet)
|
|
||||||
{
|
|
||||||
size += ConfigTuple.HEADER_LENGTH + t.payloadLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList<ConfigTuple> generateTupleSet() {
|
|
||||||
ArrayList<ConfigTuple> tupleSet = new ArrayList<ConfigTuple>();
|
|
||||||
|
|
||||||
tupleSet.add(new IntConfigTuple((short)0x1204, streamConfig.getWidth()));
|
|
||||||
tupleSet.add(new IntConfigTuple((short)0x1205, streamConfig.getHeight()));
|
|
||||||
tupleSet.add(new IntConfigTuple((short)0x1206, 1)); //videoTransferProtocol
|
|
||||||
tupleSet.add(new IntConfigTuple((short)0x120A, streamConfig.getRefreshRate()));
|
|
||||||
|
|
||||||
// Start with the initial config for 720p60
|
|
||||||
updateSetWithConfig(tupleSet, CONFIG_720_60);
|
|
||||||
|
|
||||||
if (streamConfig.getWidth() >= 1920 &&
|
|
||||||
streamConfig.getHeight() >= 1080)
|
|
||||||
{
|
|
||||||
if (streamConfig.getRefreshRate() >= 60)
|
|
||||||
{
|
|
||||||
// Update the initial set with the changed 1080p60 options
|
|
||||||
updateSetWithConfig(tupleSet, CONFIG_1080_60_DIFF);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Update the initial set with the changed 1080p30 options
|
|
||||||
updateSetWithConfig(tupleSet, CONFIG_1080_30_DIFF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tupleSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] toWire() {
|
|
||||||
ArrayList<ConfigTuple> tupleSet = generateTupleSet();
|
|
||||||
ByteBuffer bb = ByteBuffer.allocate(getConfigOnWireSize(tupleSet) + 4).order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
|
|
||||||
for (ConfigTuple t : tupleSet)
|
|
||||||
{
|
|
||||||
bb.put(t.toWire());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Config tail
|
|
||||||
bb.putShort((short) 0x13fe);
|
|
||||||
bb.putShort((short) 0x00);
|
|
||||||
|
|
||||||
return bb.array();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
package com.limelight.nvstream.control;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
|
|
||||||
public abstract class ConfigTuple {
|
|
||||||
public short packetType;
|
|
||||||
public short payloadLength;
|
|
||||||
|
|
||||||
public static final short HEADER_LENGTH = 4;
|
|
||||||
|
|
||||||
public ConfigTuple(short packetType, short payloadLength)
|
|
||||||
{
|
|
||||||
this.packetType = packetType;
|
|
||||||
this.payloadLength = payloadLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract byte[] payloadToWire();
|
|
||||||
|
|
||||||
public byte[] toWire()
|
|
||||||
{
|
|
||||||
byte[] payload = payloadToWire();
|
|
||||||
ByteBuffer bb = ByteBuffer.allocate(HEADER_LENGTH + (payload != null ? payload.length : 0))
|
|
||||||
.order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
|
|
||||||
bb.putShort(packetType);
|
|
||||||
bb.putShort(payloadLength);
|
|
||||||
|
|
||||||
if (payload != null) {
|
|
||||||
bb.put(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bb.array();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode()
|
|
||||||
{
|
|
||||||
return packetType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o)
|
|
||||||
{
|
|
||||||
// We only compare the packet types on purpose
|
|
||||||
if (o instanceof ConfigTuple) {
|
|
||||||
return ((ConfigTuple)o).packetType == packetType;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package com.limelight.nvstream.control;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
|
|
||||||
public class IntConfigTuple extends ConfigTuple {
|
|
||||||
|
|
||||||
public static final short PAYLOAD_LENGTH = 4;
|
|
||||||
|
|
||||||
public int payload;
|
|
||||||
|
|
||||||
public IntConfigTuple(short packetType, int payload) {
|
|
||||||
super(packetType, PAYLOAD_LENGTH);
|
|
||||||
this.payload = payload;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] payloadToWire() {
|
|
||||||
ByteBuffer bb = ByteBuffer.allocate(PAYLOAD_LENGTH).order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
bb.putInt(payload);
|
|
||||||
return bb.array();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package com.limelight.nvstream.control;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
|
|
||||||
public class ShortConfigTuple extends ConfigTuple {
|
|
||||||
|
|
||||||
public static final short PAYLOAD_LENGTH = 2;
|
|
||||||
|
|
||||||
public short payload;
|
|
||||||
|
|
||||||
public ShortConfigTuple(short packetType, short payload) {
|
|
||||||
super(packetType, PAYLOAD_LENGTH);
|
|
||||||
this.payload = payload;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] payloadToWire() {
|
|
||||||
ByteBuffer bb = ByteBuffer.allocate(PAYLOAD_LENGTH).order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
bb.putShort(payload);
|
|
||||||
return bb.array();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,144 @@
|
|||||||
|
package com.limelight.nvstream.rtsp;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Inet6Address;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import com.limelight.nvstream.StreamConfiguration;
|
||||||
|
import com.tinyrtsp.rtsp.message.RtspMessage;
|
||||||
|
import com.tinyrtsp.rtsp.message.RtspRequest;
|
||||||
|
import com.tinyrtsp.rtsp.message.RtspResponse;
|
||||||
|
import com.tinyrtsp.rtsp.parser.RtspStream;
|
||||||
|
|
||||||
|
public class RtspConnection {
|
||||||
|
public static final int PORT = 48010;
|
||||||
|
public static final int RTSP_TIMEOUT = 5000;
|
||||||
|
|
||||||
|
// SHIELD Update 77
|
||||||
|
public static final int CLIENT_VERSION = 9;
|
||||||
|
|
||||||
|
private int sequenceNumber = 1;
|
||||||
|
private int sessionId = 0;
|
||||||
|
|
||||||
|
private String host;
|
||||||
|
|
||||||
|
public RtspConnection(InetAddress host) {
|
||||||
|
if (host instanceof Inet6Address) {
|
||||||
|
// RFC2732-formatted IPv6 address for use in URL
|
||||||
|
this.host = "["+host.getHostAddress()+"]";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.host = host.getHostAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private RtspRequest createRtspRequest(String command, String target) {
|
||||||
|
RtspRequest m = new RtspRequest(command, target, "RTSP/1.0",
|
||||||
|
sequenceNumber++, new HashMap<String, String>(), null);
|
||||||
|
m.setOption("X-GS-ClientVersion", ""+CLIENT_VERSION);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RtspResponse transactRtspMessage(RtspMessage m) throws IOException {
|
||||||
|
Socket s = new Socket();
|
||||||
|
try {
|
||||||
|
s.setTcpNoDelay(true);
|
||||||
|
s.connect(new InetSocketAddress(host, PORT), RTSP_TIMEOUT);
|
||||||
|
|
||||||
|
RtspStream rtspStream = new RtspStream(s.getInputStream(), s.getOutputStream());
|
||||||
|
try {
|
||||||
|
rtspStream.write(m);
|
||||||
|
return (RtspResponse) rtspStream.read();
|
||||||
|
} finally {
|
||||||
|
rtspStream.close();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private RtspResponse requestOptions() throws IOException {
|
||||||
|
RtspRequest m = createRtspRequest("OPTIONS", "rtsp://"+host);
|
||||||
|
return transactRtspMessage(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RtspResponse requestDescribe() throws IOException {
|
||||||
|
RtspRequest m = createRtspRequest("DESCRIBE", "rtsp://"+host);
|
||||||
|
m.setOption("Accept", "application/sdp");
|
||||||
|
m.setOption("If-Modified-Since", "Thu, 01 Jan 1970 00:00:00 GMT");
|
||||||
|
return transactRtspMessage(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RtspResponse setupStream(String streamName) throws IOException {
|
||||||
|
RtspRequest m = createRtspRequest("SETUP", "streamid="+streamName);
|
||||||
|
if (sessionId != 0) {
|
||||||
|
m.setOption("Session", ""+sessionId);
|
||||||
|
}
|
||||||
|
m.setOption("Transport", " ");
|
||||||
|
m.setOption("If-Modified-Since", "Thu, 01 Jan 1970 00:00:00 GMT");
|
||||||
|
return transactRtspMessage(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RtspResponse playStream(String streamName) throws IOException {
|
||||||
|
RtspRequest m = createRtspRequest("PLAY", "streamid="+streamName);
|
||||||
|
m.setOption("Session", ""+sessionId);
|
||||||
|
return transactRtspMessage(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RtspResponse sendVideoAnnounce(StreamConfiguration sc) throws IOException {
|
||||||
|
RtspRequest m = createRtspRequest("ANNOUNCE", "streamid=video");
|
||||||
|
m.setOption("Session", ""+sessionId);
|
||||||
|
m.setOption("Content-type", "application/sdp");
|
||||||
|
// FIXME: IP jank
|
||||||
|
m.setPayload(SdpGenerator.generateSdpFromConfig(InetAddress.getByName(host), sc));
|
||||||
|
m.setOption("Content-length", ""+m.getPayload().length());
|
||||||
|
return transactRtspMessage(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doRtspHandshake(StreamConfiguration sc) throws IOException {
|
||||||
|
RtspResponse r;
|
||||||
|
|
||||||
|
r = requestOptions();
|
||||||
|
if (r.getStatusCode() != 200) {
|
||||||
|
throw new IOException("RTSP OPTIONS request failed: "+r.getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
r = requestDescribe();
|
||||||
|
if (r.getStatusCode() != 200) {
|
||||||
|
throw new IOException("RTSP DESCRIBE request failed: "+r.getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
r = setupStream("audio");
|
||||||
|
if (r.getStatusCode() != 200) {
|
||||||
|
throw new IOException("RTSP SETUP request failed: "+r.getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
sessionId = Integer.parseInt(r.getOption("Session"));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IOException("RTSP SETUP response was malformed");
|
||||||
|
}
|
||||||
|
|
||||||
|
r = setupStream("video");
|
||||||
|
if (r.getStatusCode() != 200) {
|
||||||
|
throw new IOException("RTSP SETUP request failed: "+r.getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sendVideoAnnounce(sc);
|
||||||
|
if (r.getStatusCode() != 200) {
|
||||||
|
throw new IOException("RTSP ANNOUNCE request failed: "+r.getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
r = playStream("video");
|
||||||
|
if (r.getStatusCode() != 200) {
|
||||||
|
throw new IOException("RTSP PLAY request failed: "+r.getStatusCode());
|
||||||
|
}
|
||||||
|
r = playStream("audio");
|
||||||
|
if (r.getStatusCode() != 200) {
|
||||||
|
throw new IOException("RTSP PLAY request failed: "+r.getStatusCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,236 @@
|
|||||||
|
package com.limelight.nvstream.rtsp;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Inet6Address;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
|
||||||
|
import com.limelight.nvstream.StreamConfiguration;
|
||||||
|
|
||||||
|
public class SdpGenerator {
|
||||||
|
private static void addSessionAttributeBytes(StringBuilder config, String attribute, byte[] value) {
|
||||||
|
try {
|
||||||
|
addSessionAttribute(config, attribute, new String(value, "IBM437"));
|
||||||
|
} catch (UnsupportedEncodingException e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addSessionAttributeInts(StringBuilder config, String attribute, int[] value) {
|
||||||
|
ByteBuffer b = ByteBuffer.allocate(value.length * 4).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
|
||||||
|
for (int val : value) {
|
||||||
|
b.putInt(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
addSessionAttributeBytes(config, attribute, b.array());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addSessionAttributeInt(StringBuilder config, String attribute, int value) {
|
||||||
|
ByteBuffer b = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
b.putInt(value);
|
||||||
|
addSessionAttributeBytes(config, attribute, b.array());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addSessionAttribute(StringBuilder config, String attribute, String value) {
|
||||||
|
config.append("a="+attribute+":"+value+" \r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String generateSdpFromConfig(InetAddress host, StreamConfiguration sc) {
|
||||||
|
StringBuilder config = new StringBuilder();
|
||||||
|
config.append("v=0").append("\r\n"); // SDP Version 0
|
||||||
|
config.append("o=android 0 9 IN ");
|
||||||
|
if (host instanceof Inet6Address) {
|
||||||
|
config.append("IPv6 ");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
config.append("IPv4 ");
|
||||||
|
}
|
||||||
|
config.append(host.getHostAddress());
|
||||||
|
config.append("\r\n");
|
||||||
|
config.append("s=NVIDIA Streaming Client").append("\r\n");
|
||||||
|
|
||||||
|
addSessionAttributeBytes(config, "x-nv-callbacks", new byte[] {
|
||||||
|
0x50, 0x51, 0x49, 0x4a, 0x0d,
|
||||||
|
(byte)0xad, 0x30, 0x4a, (byte)0xf1, (byte)0xbd, 0x30, 0x4a, (byte)0xd5,
|
||||||
|
(byte)0xac, 0x30, 0x4a, 0x21, (byte)0xbc, 0x30, 0x4a, (byte)0xc1,
|
||||||
|
(byte)0xbb, 0x30, 0x4a, 0x7d, (byte)0xbb, 0x30, 0x4a, 0x19,
|
||||||
|
(byte)0xbb, 0x30, 0x4a, 0x00, 0x00, 0x00, 0x00
|
||||||
|
});
|
||||||
|
addSessionAttributeBytes(config, "x-nv-videoDecoder", new byte[] {
|
||||||
|
0x50, 0x51, 0x49, 0x4a, 0x65, (byte)0xad, 0x30, 0x4a, 0x01,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0xd1, (byte)0xac, 0x30,
|
||||||
|
0x4a, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x4d, (byte)0xad, 0x30, 0x4a
|
||||||
|
});
|
||||||
|
addSessionAttributeBytes(config, "x-nv-audioRenderer", new byte[] {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
});
|
||||||
|
|
||||||
|
addSessionAttribute(config, "x-nv-general.serverAddress", host.getHostAddress());
|
||||||
|
|
||||||
|
addSessionAttributeInts(config, "x-nv-general.serverPorts", new int[] {
|
||||||
|
0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
|
||||||
|
0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
|
||||||
|
0xffffffff, 0x00000000, 0xffffffff, 0xffffffff,
|
||||||
|
|
||||||
|
0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
|
||||||
|
0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
|
||||||
|
0xffffffff, 0x00000000, 0xffffffff, 0xffffffff,
|
||||||
|
|
||||||
|
0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
|
||||||
|
0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
|
||||||
|
0xffffffff, 0x00000000, 0xffffffff, 0xffffffff,
|
||||||
|
|
||||||
|
0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
|
||||||
|
0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
|
||||||
|
0xffffffff, 0x00000000, 0xffffffff, 0xffffffff,
|
||||||
|
|
||||||
|
0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
|
||||||
|
0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
|
||||||
|
0xffffffff, 0x00000000, 0xffffffff, 0xffffffff,
|
||||||
|
|
||||||
|
0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
|
||||||
|
0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
|
||||||
|
0xffffffff, 0x00000000, 0xffffffff, 0xffffffff,
|
||||||
|
|
||||||
|
0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
|
||||||
|
0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
|
||||||
|
0xffffffff, 0x00000000, 0xffffffff, 0xffffffff,
|
||||||
|
|
||||||
|
0x00000000, 0xffffffff, 0xffffffff, 0x00000000,
|
||||||
|
0xffffffff, 0xffffffff, 0x00000000, 0xffffffff,
|
||||||
|
0xffffffff, 0x00000000, 0xffffffff, 0xffffffff
|
||||||
|
});
|
||||||
|
|
||||||
|
addSessionAttribute(config, "x-nv-general.videoSyncAudioDelayAdjust", "10000");
|
||||||
|
addSessionAttribute(config, "x-nv-general.startTime", "0");
|
||||||
|
addSessionAttributeInt(config, "x-nv-general.featureFlags", 0xffffffff);
|
||||||
|
addSessionAttribute(config, "x-nv-general.userIdleWarningTimeout", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-general.userIdleSessionTimeout", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-general.serverCapture", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-general.clientCapture", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-general.rtpQueueMaxPackets", "16");
|
||||||
|
addSessionAttribute(config, "x-nv-general.rtpQueueMaxDurationMs", "40");
|
||||||
|
addSessionAttribute(config, "x-nv-general.useRtspClient", "257");
|
||||||
|
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].clientViewportWd", ""+sc.getWidth());
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].clientViewportHt", ""+sc.getHeight());
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].adapterNumber", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].maxFPS", "30");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].iFrameOnDemand", "1");
|
||||||
|
|
||||||
|
// FIXME: Handle other settings
|
||||||
|
addSessionAttributeInt(config, "x-nv-video[0].transferProtocol", 1);
|
||||||
|
addSessionAttributeInt(config, "x-nv-video[0].rateControlMode", 5);
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].averageBitrate", "7");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].peakBitrate", "7");
|
||||||
|
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].gopLength", "60");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].vbvMultiplier", "100");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].slicesPerFrame", "4");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].numTemporalLayers", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].packetSize", "1024");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].enableSubframeEncoding", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].refPicInvalidation", "1");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].pingBackIntervalMs", "3000");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].pingBackTimeoutMs", "10000");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].timeoutLengthMs", "7000");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].fullFrameAssembly", "1");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].decodeIncompleteFrames", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].enableIntraRefresh", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].enableLongTermReferences", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].enableFrameRateCtrl", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].rtpDynamicPort", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].framesWithInvalidRefThreshold", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-video[0].consecutiveFrameLostThreshold", "0");
|
||||||
|
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].ts.enable", "0");
|
||||||
|
|
||||||
|
// FIXME: Handle other settings
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].ts.averageBitrate", "8");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].ts.maximumBitrate", "10");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.flags", "823");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.maximumBitrate", "10000");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.minimumBitrate", "2000");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.statsTime", "50");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.zeroLossCount", "3000");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.lossThreshold", "2");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.owdThreshold", "5000");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.owdReference", "500");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.lossWaitTime", "75");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.rateDropMultiplier", "25");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.rateGainMultiplier", "10");
|
||||||
|
|
||||||
|
// FIXME: Other settings?
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.maxFps", "60");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.minFps", "30");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.fpsThreshold", "3");
|
||||||
|
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.jitterThreshold", "1000");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.jitterWaitTime", "5000");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.noJitterWaitTime", "5000");
|
||||||
|
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.earlyDetectionEnableBitRatePercentThreshold", "110");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.earlyDetectionEnableL1Threshold", "10");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.earlyDetectionEnableL0Threshold", "6");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.earlyDetectionDisableThreshold", "4");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.earlyDetectionDisableWaitTime", "20000");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.earlyDetectionDisableWaitPercent", "100");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.earlyDetectionLowerBoundRate", "1000");
|
||||||
|
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.earlyDetectionLowerBoundWidth", "720");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.earlyDetectionLowerBoundHeight", "480");
|
||||||
|
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.pf.enableFlags", "3");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.pf.lowBitrate30FpsThreshold", "4000");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.pf.lowBitrate60FpsThreshold", "5000");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.pf.highBitrateThreshold", "6000");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bw.pf.bitrateStepSize", "1000");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bn.notifyUpBoundThreshold", "40");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bn.notifyLowBoundThreshold", "25");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].bn.notifyWaitTime", "3000");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].fec.enable", "1");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].fec.numSrcPackets", "50");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].fec.numOutPackets", "60");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].fec.repairPercent", "20");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].pictureRefreshIntervalMs", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].videoQualityScoreUpdateTime", "5000");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].invalidateThreshold", "3");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].invalidateSkipPercentage", "10");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].qosTrafficType", "7");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].videoQoSMaxRoundTripLatencyFrames", "12");
|
||||||
|
addSessionAttribute(config, "x-nv-vqos[0].videoQoSMaxConsecutiveDrops", "3");
|
||||||
|
addSessionAttributeInt(config, "x-nv-vqos[0].profile", 0);
|
||||||
|
|
||||||
|
addSessionAttributeInt(config, "x-nv-aqos.mode", 1);
|
||||||
|
addSessionAttribute(config, "x-nv-aqos.enableAudioStats", "1");
|
||||||
|
addSessionAttribute(config, "x-nv-aqos.audioStatsUpdateIntervalMs", "70");
|
||||||
|
addSessionAttribute(config, "x-nv-aqos.enablePacketLossPercentage", "1");
|
||||||
|
addSessionAttribute(config, "x-nv-aqos.bitRate", "96000");
|
||||||
|
addSessionAttribute(config, "x-nv-aqos.packetDuration", "5");
|
||||||
|
addSessionAttribute(config, "x-nv-aqos.packetLossPercentageUpdateIntervalMs", "100");
|
||||||
|
addSessionAttribute(config, "x-nv-aqos.qosTrafficType", "4");
|
||||||
|
|
||||||
|
addSessionAttribute(config, "x-nv-runtime.recordClientStats", "8");
|
||||||
|
addSessionAttribute(config, "x-nv-runtime.recordServerStats", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-runtime.clientNetworkCapture", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-runtime.clientTraceCapture", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-runtime.serverNetworkCapture", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-runtime.serverTraceCapture", "0");
|
||||||
|
|
||||||
|
addSessionAttributeInt(config, "x-nv-ri.protocol", 0);
|
||||||
|
addSessionAttribute(config, "x-nv-ri.sendStatus", "0");
|
||||||
|
addSessionAttributeInt(config, "x-nv-ri.securityProtocol", 0);
|
||||||
|
addSessionAttributeBytes(config, "x-nv-ri.secInfo", new byte[0x20a]);
|
||||||
|
addSessionAttribute(config, "x-nv-videoFrameDropIntervalNumber", "0");
|
||||||
|
addSessionAttribute(config, "x-nv-videoFrameDropContinualNumber", "0");
|
||||||
|
|
||||||
|
config.append("t=0 0").append("\r\n");
|
||||||
|
|
||||||
|
config.append("m=video 47996 ").append("\r\n");
|
||||||
|
|
||||||
|
return config.toString();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user