# HG changeset patch # User riono # Date 1559723344 -32400 # Node ID 623e409c976a90dd1ad34064bbd0e99b840e242b # Parent 170b1e852b26bd25122e5fdb107ee32e08f3e366# Parent 17a2d0ea5c035d89cff4fb5a867e1421d89345b7 fix diff -r 170b1e852b26 -r 623e409c976a src/main/java/com/glavsoft/rfb/encoding/decoder/ZRLEDecoder.java --- a/src/main/java/com/glavsoft/rfb/encoding/decoder/ZRLEDecoder.java Wed Jun 05 16:26:28 2019 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/decoder/ZRLEDecoder.java Wed Jun 05 17:29:04 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 { @@ -45,6 +42,8 @@ private int[] palette; class TileLoop { + private final boolean blocking; + private final int half; private int deflate_size = 65507; private ByteBuffer c1; private int width; // phase2 length @@ -55,6 +54,12 @@ private int prevoffset; private Deflater deflater; private int rectPos; + private int ztileInLine; + private int hwidth; + private int hc1width; + private int hoffset; + private int hc1offset; + private int discard; /** * Multicast framebufferUpdate to children. @@ -79,14 +84,25 @@ public TileLoop(int offset) { prevoffset = prevLineOffset = prevC1Offset = offset; + if (offset < deflate_size+spanGap) { + // packet size fit in broadcast send it all at once + blocking = false; + } else + blocking = true; + discard = 0; half = 0; } - private void zrleeBlocking(TreeRFBProto rfb, ByteBuffer header, FramebufferUpdateRectangle rect) { + private void zrleeBlocking(TreeRFBProto rfb, ByteBuffer header, FramebufferUpdateRectangle rect, byte bytes[]) { // dump32(inputs); deflater = rfb.deflater; newMulticastPacket(rfb, rect); c1.put(header.get(0)); c1rect = new FramebufferUpdateRectangle(rect.x, rect.y, 0, 0); + if (!blocking) { + deflater.setInput(bytes,0,prevoffset); + deflater.deflate(c1); + flushMuticast(rfb); + } return; } @@ -104,19 +120,36 @@ 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 64x64x3 11288byte, a packet can contain 5 raw tiles, when these are + * compressed 10 to 100 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 flush former 256 tiles. + * If it will failed again, flush the previous line and do flush 512 tiles in new Packet. + * If it failed again try former 256 tiles flushed, if this failes again dicard the former half. + * The last case cannot happen but former 256 tiles have to be flushed, because the next 256 lines + * may failed again and restart the from this point. + * The next packet start with later 256 tiles filled and unflushed. + * * @param rfb * @param last * @param rect @@ -125,40 +158,34 @@ * @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) { + if (!blocking) return; int span = offset - prevoffset; deflater.setInput(bytes,prevoffset,span); prevoffset = offset; 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 +195,73 @@ // 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) { + ztileInLine++; + 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 */ + discard++; + 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 +281,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 +301,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 { @@ -266,7 +357,7 @@ } if (rfbProto.multicastBlocking) { - tileloop.zrleeBlocking(rfbProto, header, rect); + tileloop.zrleeBlocking(rfbProto, header, rect,bytes); } for (int tileY = rect.y; tileY < maxY; tileY += MAX_TILE_SIZE) { int tileHeight = Math.min(maxY - tileY, MAX_TILE_SIZE); diff -r 170b1e852b26 -r 623e409c976a src/main/java/jp/ac/u_ryukyu/treevnc/TreeRFBProto.java --- a/src/main/java/jp/ac/u_ryukyu/treevnc/TreeRFBProto.java Wed Jun 05 16:26:28 2019 +0900 +++ b/src/main/java/jp/ac/u_ryukyu/treevnc/TreeRFBProto.java Wed Jun 05 17:29:04 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();