# HG changeset patch # User Shinji KONO # Date 1557129565 -32400 # Node ID a3d0ba67e8cf4d1e34782d3c0b7486c29987f772 # Parent 4be31e10712142d58b2e1c92302bfeab875e866e try 512 tiles / 256 tiles flush diff -r 4be31e107121 -r a3d0ba67e8cf src/main/java/com/glavsoft/rfb/encoding/decoder/ZRLEDecoder.java --- a/src/main/java/com/glavsoft/rfb/encoding/decoder/ZRLEDecoder.java Sun May 05 18:32:55 2019 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/decoder/ZRLEDecoder.java Mon May 06 16:59:25 2019 +0900 @@ -31,12 +31,9 @@ import jp.ac.u_ryukyu.treevnc.CheckDelay; import jp.ac.u_ryukyu.treevnc.TreeRFBProto; -import java.io.ByteArrayInputStream; -import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.util.LinkedList; -import java.util.zip.DataFormatException; import java.util.zip.Deflater; public class ZRLEDecoder extends ZlibDecoder { @@ -55,6 +52,11 @@ private int prevoffset; private Deflater deflater; private int rectPos; + private int ztileInLine; + private int hwidth; + private int hc1width; + private int hoffset; + private int hc1offset; /** * Multicast framebufferUpdate to children. @@ -104,19 +106,33 @@ c1.putInt(0); // should be data length width = 0; rectPos = 4; + ztileInLine = 0; } int spanGap = 128; /** * + * Series of tiles compose at most three rectangles. SYNC_FLUSH is necessary on + * rectangle boundaries. + * * +----+ - * | | phase 0 + * | | | phase 0 * +---------------+ - * | | phase 1 + * | | | phase 1 * +----+----------+ - * | | phase 2 + * | | | phase 2 * +----+ * + * Broadcast packet have to less than 64kbytes + * A tile 8x8x3 192byte, a packet can contain 340 raw tiles, when these are + * compressed 500 to 2000 tiles can be stored. It is impossible to predict the + * compression rate. To check the compressed capacity, Deflate.needsInputs() can + * be used. If needsInputs() is false on SYNC_FLUSH, smaller input is necessary. + * + * We'll try 512 tiles before SYNC_FLUSH in a phase, if it fails try 256. + * If it will failed again, flush the previous line and try again. + * If 256 tiles are failed in new packet, discard it. + * * @param rfb * @param last * @param rect @@ -125,6 +141,9 @@ * @param tileW * @param tileH */ + + int MAX_ZTILE = 512; + public void multicastPut(TreeRFBProto rfb, boolean last, FramebufferUpdateRectangle rect, byte[] bytes, int offset, int tileW, int tileH) { int span = offset - prevoffset; deflater.setInput(bytes,prevoffset,span); @@ -132,33 +151,23 @@ c1rect.width += tileW; width += tileW; if (c1rect.x > rect.x) { // phase 0 if (c1rect.x+c1rect.width < rect.x+rect.width) { - if (c1.remaining() > spanGap ) { - deflater.deflate(c1, Deflater.NO_FLUSH); - if (!deflater.needsInput()) flushRectangle(rect); - return; - } + compressAndCheckFlush(rfb, rect, bytes, offset,false, last); } else { c1rect.width = rect.x+rect.width-c1rect.x ; c1rect.height += tileH; - deflater.deflate(c1, Deflater.NO_FLUSH); - flushRectangle(rect); + compressAndCheckFlush(rfb,rect,bytes,offset,true, last); } - return; - } - if (!last && c1.remaining() > spanGap) { // phase 1 + } else if (!last && c1.remaining() > spanGap) { // phase 1 if (width >= rect.width) { c1rect.width = rect.width; width = 0; c1rect.height += tileH; prevLineOffset = offset; prevC1Offset = c1.position(); - deflater.deflate(c1, Deflater.SYNC_FLUSH); - if (!deflater.needsInput()) flushRectangle(rect); + compressAndCheckFlush(rfb,rect,bytes,offset,true, last); } else { - deflater.deflate(c1, Deflater.NO_FLUSH); - if (!deflater.needsInput()) flushRectangle(rect); + compressAndCheckFlush(rfb,rect,bytes,offset,false, last); } - return; } else { // phase2 // rewind to the last line finish phase 1 int savew = width; @@ -168,12 +177,71 @@ // recompress overrun and flush phase 2 c1rect.width = savew; c1rect.height = tileH; - deflater.setInput(bytes, prevLineOffset, span); + compressAndCheckFlush(rfb,rect,bytes,offset,true, last); + } + } + } + + private void compressAndCheckFlush(TreeRFBProto rfb, FramebufferUpdateRectangle rect, byte[] bytes, int offset, boolean flush, boolean last) { + if (!flush && ztileInLine++ < MAX_ZTILE) { + if (ztileInLine == MAX_ZTILE/2) { + hwidth = width; hc1width = c1rect.width; hoffset = offset; hc1offset = c1.position(); + } + deflater.deflate(c1, Deflater.NO_FLUSH); + } else { + deflater.deflate(c1, Deflater.SYNC_FLUSH); + if (!deflater.needsInput()) { + // too large, try half line + width = hwidth; + c1rect.width = hc1width; + c1.position(hc1offset); + deflater.setInput(bytes, prevLineOffset, hoffset - prevC1Offset); deflater.deflate(c1, Deflater.SYNC_FLUSH); - flushRectangle(rect); + int from, len; + if (!deflater.needsInput()) { + // flush previous line and start new packet + c1.position(prevC1Offset); + unputrectangle(); + flushMuticast(rfb); + newMulticastPacket(rfb, rect); + nextRectangle(rect); + // we already reached MIX_ZTILE do half of them, do compress right now + from = prevC1Offset; + len = offset - prevLineOffset; + deflater.setInput(bytes, from, len); + deflater.deflate(c1, Deflater.SYNC_FLUSH); + if (deflater.needsInput()) { + flushRectangle(rect); // we are the flushed last line + return; + } + // half size should always succeed + from = prevC1Offset; + len = hoffset - prevLineOffset; + deflater.setInput(bytes, from, len); + deflater.deflate(c1, Deflater.SYNC_FLUSH); + if (!deflater.needsInput()) { /* fatal error discard this line */ + if (!last) { + newMulticastPacket(rfb, rect); + nextRectangle(rect); + } + return; + } else + flushRectangle(rect); // normal case + // later half is remain continue + } else { + flushMuticast(rfb); + newMulticastPacket(rfb,rect); + nextRectangle(rect); + } + // do later half in new Packet + from = hoffset ; len = offset - hoffset; + deflater.setInput(bytes, from, len); + deflater.deflate(c1, Deflater.NO_FLUSH); + hwidth = width; + hoffset = offset; + hc1offset = c1.position(); + ztileInLine = MAX_ZTILE/2; } - flushMuticast(rfb); - if (!last) newMulticastPacket(rfb,rect); } } @@ -193,6 +261,11 @@ rectPos = c1.position(); c1.putShort(2,(short)(c1.getShort(2)+1)); // increment rectangle count nextRectangle(rect); + ztileInLine = 0; + } + + private void unputrectangle() { + c1.putShort(2,(short)(c1.getShort(2)-1)); // last rectangle is canceled } private void nextRectangle(FramebufferUpdateRectangle rect) { @@ -208,8 +281,6 @@ } private void flushMuticast(TreeRFBProto rfb) { - deflater.deflate(c1, Deflater.FULL_FLUSH); - deflater.finish(); c1.flip(); //System.out.println("multicastPut: " + c1rect + " length: " + (c1.remaining()-c1headerPos-header.limit())); try { diff -r 4be31e107121 -r a3d0ba67e8cf src/main/java/jp/ac/u_ryukyu/treevnc/TreeRFBProto.java --- a/src/main/java/jp/ac/u_ryukyu/treevnc/TreeRFBProto.java Sun May 05 18:32:55 2019 +0900 +++ b/src/main/java/jp/ac/u_ryukyu/treevnc/TreeRFBProto.java Mon May 06 16:59:25 2019 +0900 @@ -78,7 +78,9 @@ private static int uniqueNodeId = 0; // uniquenodeid in all trees (less than MAX_UNIQUE_NODE_ID) private boolean stopBroadcast; - public boolean multicastBlocking = false; + // multicastBlocking is an internal test flag + // perform blocking on TCP connection + public boolean multicastBlocking = true; public TreeRFBProto(boolean isTreeManager, ViewerInterface viewer) { nets.setMyRfb(this); @@ -905,7 +907,7 @@ setProtocolContext(workingProtocol); this.connectionPresenter = connectionPresenter; connectionPresenter.viewer.setConnectionPresenter(connectionPresenter); - if (connectionPresenter.isUseMulticast()) multicastBlocking = true; + // if (connectionPresenter.isUseMulticast()) multicastBlocking = true; connectionPresenter.addModel("ConnectionParamsModel", connectionPresenter.getConnectionParams()); if (previousContext != null && isTreeManager() && hasParent()) { Reader previousReader = previousContext.getReader();