From c93812179f0fbad3859c96b1bb9ee8f466771c51 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Mon, 17 Feb 2014 13:47:43 -0500 Subject: [PATCH] Updated config code for artifact-free 1080p60 streaming --- .../nvstream/control/ByteConfigTuple.java | 17 ++ .../limelight/nvstream/control/Config.java | 273 ++++++++++-------- .../nvstream/control/ConfigTuple.java | 53 ++++ .../nvstream/control/IntConfigTuple.java | 23 ++ .../nvstream/control/ShortConfigTuple.java | 23 ++ 5 files changed, 274 insertions(+), 115 deletions(-) create mode 100644 moonlight-common/src/com/limelight/nvstream/control/ByteConfigTuple.java create mode 100644 moonlight-common/src/com/limelight/nvstream/control/ConfigTuple.java create mode 100644 moonlight-common/src/com/limelight/nvstream/control/IntConfigTuple.java create mode 100644 moonlight-common/src/com/limelight/nvstream/control/ShortConfigTuple.java diff --git a/moonlight-common/src/com/limelight/nvstream/control/ByteConfigTuple.java b/moonlight-common/src/com/limelight/nvstream/control/ByteConfigTuple.java new file mode 100644 index 00000000..78b6a253 --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/control/ByteConfigTuple.java @@ -0,0 +1,17 @@ +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}; + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/control/Config.java b/moonlight-common/src/com/limelight/nvstream/control/Config.java index b7e090bd..bfd21b52 100644 --- a/moonlight-common/src/com/limelight/nvstream/control/Config.java +++ b/moonlight-common/src/com/limelight/nvstream/control/Config.java @@ -2,141 +2,184 @@ package com.limelight.nvstream.control; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.HashSet; import com.limelight.nvstream.StreamConfiguration; public class Config { - public static final int[] UNKNOWN_CONFIG = + public static final ConfigTuple[] CONFIG_720_60 = { - 70151, - 68291329, - 1280, - 68291584, - 1280, - 68291840, - 15360, - 68292096, - 25600, - 68292352, - 2048, - 68292608, - 1024, - 68289024, - 262144, - 17957632, - 302055424, - 134217729, - 16777490, - 70153, - 68293120, - 768000, - 17961216, - 303235072, - 335609857, - 838861842, - 352321536, - 1006634002, - 369098752, - 335545362, - 385875968, - 1042, - 402653184, - 134218770, - 419430400, - 167773202, - 436207616, - 855638290, - 266779, - 10000, - 266780, - 2000, - 266781, - 50, - 266782, - 3000, - 266783, - 2, - 266794, - 5000, - 266795, - 500, - 266784, - 75, - 266785, - 25, - 266786, - 10, - 266787, - 60, - 266788, - 30, - 266789, - 3, - 266790, - 1000, - 266791, - 5000, - 266792, - 5000, - 266793, - 5000, - 70190, - 68301063, - 10240, - 68301312, - 6400, - 68301568, - 768000, - 68299776, - 768, - 68300032, - 2560, - 68300544, - 0, - 34746368, - (int)0xFE000000 + new IntConfigTuple((short)0x1206, 1), + new ByteConfigTuple((short)0x1207, (byte)1), + new IntConfigTuple((short)0x120b, 7), + new IntConfigTuple((short)0x120c, 7), + new IntConfigTuple((short)0x120d, 60), + new IntConfigTuple((short)0x120e, 100), + new IntConfigTuple((short)0x120f, 5), + new IntConfigTuple((short)0x1210, 4), + new IntConfigTuple((short)0x1202, 1024), + new ByteConfigTuple((short)0x1203, (byte)0), + new ByteConfigTuple((short)0x1201, (byte)0), + new ByteConfigTuple((short)0x1234, (byte)0), + new ByteConfigTuple((short)0x1248, (byte)0), + new ByteConfigTuple((short)0x1208, (byte)1), + new ByteConfigTuple((short)0x1209, (byte)0), + new IntConfigTuple((short)0x1212, 3000), + new IntConfigTuple((short)0x1238, 10000), + new ByteConfigTuple((short)0x1211, (byte)0), + new ByteConfigTuple((short)0x1213, (byte)1), + new IntConfigTuple((short)0x1214, 50), + new IntConfigTuple((short)0x1215, 60), + new IntConfigTuple((short)0x1216, 20), + new IntConfigTuple((short)0x1217, 0), + new IntConfigTuple((short)0x1218, 8), + new IntConfigTuple((short)0x1219, 10), + new IntConfigTuple((short)0x121a, 311), + new IntConfigTuple((short)0x121b, 10000), + new IntConfigTuple((short)0x121c, 2000), + new IntConfigTuple((short)0x121d, 50), + new IntConfigTuple((short)0x121e, 3000), + new IntConfigTuple((short)0x121f, 2), + new IntConfigTuple((short)0x122a, 5000), + new IntConfigTuple((short)0x122b, 500), + new IntConfigTuple((short)0x1220, 75), + new IntConfigTuple((short)0x1221, 25), + new IntConfigTuple((short)0x1222, 10), + new IntConfigTuple((short)0x1223, 60), + new IntConfigTuple((short)0x1224, 30), + new IntConfigTuple((short)0x1225, 3), + new IntConfigTuple((short)0x1226, 1000), + new IntConfigTuple((short)0x1227, 5000), + new IntConfigTuple((short)0x1228, 5000), + new IntConfigTuple((short)0x124e, 110), + new IntConfigTuple((short)0x1237, 10), + new IntConfigTuple((short)0x1236, 6), + new IntConfigTuple((short)0x1235, 4), + new IntConfigTuple((short)0x1242, 20000), + 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), + new ByteConfigTuple((short)0x122e, (byte)7), + new IntConfigTuple((short)0x1231, 40), + new IntConfigTuple((short)0x1232, 25), + new IntConfigTuple((short)0x1233, 3000), + new IntConfigTuple((short)0x122c, 3), + new IntConfigTuple((short)0x122d, 10), + 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), + new ShortConfigTuple((short)0x1230, (short)0), + new IntConfigTuple((short)0x1239, 0), + new IntConfigTuple((short)0x123a, 0), + new IntConfigTuple((short)0x123d, 96000), + new IntConfigTuple((short)0x123e, 5), + new IntConfigTuple((short)0x123f, 1), + new IntConfigTuple((short)0x1243, 100) }; - public static final int CONFIG_SIZE = ((8 + UNKNOWN_CONFIG.length) * 4) + 3; + public static final ConfigTuple[] CONFIG_1080_30_DIFF = + { + new IntConfigTuple((short)0x120b, 10), + new IntConfigTuple((short)0x120c, 10), + new IntConfigTuple((short)0x121c, 4000), + 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), + }; + public static final ConfigTuple[] CONFIG_1080_60_DIFF = + { + new IntConfigTuple((short)0x120b, 30), + new IntConfigTuple((short)0x120c, 30), + new IntConfigTuple((short)0x120f, 4), + new IntConfigTuple((short)0x121b, 30000), + new IntConfigTuple((short)0x121c, 25000), + 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(HashSet set, ConfigTuple[] config) + { + for (ConfigTuple tuple : config) + { + // Remove any existing tuple of this type + set.remove(tuple); + + set.add(tuple); + } + } + + private int getConfigOnWireSize(HashSet tupleSet) + { + int size = 0; + + for (ConfigTuple t : tupleSet) + { + size += ConfigTuple.HEADER_LENGTH + t.payloadLength; + } + + return size; + } + + private HashSet generateTupleSet() { + HashSet tupleSet = new HashSet(); + + // 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); + } + } + + tupleSet.add(new IntConfigTuple((short)0x1204, streamConfig.getWidth())); + tupleSet.add(new IntConfigTuple((short)0x1205, streamConfig.getHeight())); + tupleSet.add(new IntConfigTuple((short)0x120A, streamConfig.getRefreshRate())); + + return tupleSet; + } + public byte[] toWire() { - ByteBuffer bb = ByteBuffer.allocate(CONFIG_SIZE).order(ByteOrder.LITTLE_ENDIAN); + HashSet tupleSet = generateTupleSet(); + ByteBuffer bb = ByteBuffer.allocate(getConfigOnWireSize(tupleSet) + 4).order(ByteOrder.LITTLE_ENDIAN); - // Width - bb.putShort((short) 0x1204); - bb.putShort((short) 0x0004); - bb.putInt(streamConfig.getWidth()); - - // Height - bb.putShort((short) 0x1205); - bb.putShort((short) 0x0004); - bb.putInt(streamConfig.getHeight()); - - // Unknown - bb.putShort((short) 0x1206); - bb.putShort((short) 0x0004); - bb.putInt(1); - - // Refresh rate - bb.putShort((short) 0x120A); - bb.putShort((short) 0x0004); - bb.putInt(streamConfig.getRefreshRate()); - - // The rest are hardcoded - for (int i : UNKNOWN_CONFIG) { - bb.putInt(i); + for (ConfigTuple t : tupleSet) + { + bb.put(t.toWire()); } // Config tail - bb.putShort((short) 0x0013); - bb.put((byte) 0x00); + bb.putShort((short) 0x13fe); + bb.putShort((short) 0x00); return bb.array(); } diff --git a/moonlight-common/src/com/limelight/nvstream/control/ConfigTuple.java b/moonlight-common/src/com/limelight/nvstream/control/ConfigTuple.java new file mode 100644 index 00000000..ffd4457a --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/control/ConfigTuple.java @@ -0,0 +1,53 @@ +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; + } + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/control/IntConfigTuple.java b/moonlight-common/src/com/limelight/nvstream/control/IntConfigTuple.java new file mode 100644 index 00000000..9fe43c14 --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/control/IntConfigTuple.java @@ -0,0 +1,23 @@ +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(); + } +} diff --git a/moonlight-common/src/com/limelight/nvstream/control/ShortConfigTuple.java b/moonlight-common/src/com/limelight/nvstream/control/ShortConfigTuple.java new file mode 100644 index 00000000..7f684ca0 --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/control/ShortConfigTuple.java @@ -0,0 +1,23 @@ +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(); + } +} \ No newline at end of file