有损传输实例

下周终于可去公司了,有点快放假的感觉。阳台吹着晚风,用Java写个柔性有损传输的例子:

  • 发送端发送的任何数据包都可丢弃,不影响接收端解码。
  • 发送端编码不做FEC,不做冗余。
  • 发送端不检测丢包不重传,接收端在很短时隙中能收多少算多少。

这绝对具有超低延时效果,但要结合一个好的编解码算法。背后的思想是:

  • 信息(特别是像素或帧差分)具有连续性特征,大概率不会突变。
  • 预测像素和帧差分比预测网络拥塞和RTT容易得多,且更准确。

实现了一个简单的UDP柔性有损传输,下面是运行代码后的实际效果,本地lo口传输,tc netem模拟丢包。文件名 dst-x.jpg,x表示丢包率,第一个是原图,后面分别是1%,5%,10%,40%,80%丢包率:

时间关系,例子很简单,只传输一幅jpeg图片,但可想象,可以做得更好。

看效果,丢包导致的损失是有规律的损失,因为我偷了懒,我采用固定步长的像素间隔整合packet,比如将1,101,201,301,401整合成一个packet,2,202,302,402整合成下一个packet,正确的做法应该是随机整合,或根据图片像素的意义整合,比如不均匀编码,像背景之类的相似像素尽量集中,反之则尽量分散以换更大冗余。

可以眼见,即使80%的丢包率,也还能显示一个不错的轮廓。

结合不错的编解码,肯定效果更不错。深挖下去的话,结合像素的意义,编码纠错效果再加上来,可以弥补很多由于丢包带来的损失,比如黑色皮鞋面,像素丢失后很容易猜出是黑色,预测这个比预测网络拥塞容易多了,不是吗?

总之,主要还是编解码算法,至于传输效果,交给香农定律吧。

以上实例10%的丢包率场景,有损传输的分辨率效果还不错,皮鞋清晰可见,没有重传,可保证最短时延中只包括传输时延和编解码时延,但是如果使用TCP呢?为了换取原图效果,延时增加多少呢?
下面是丢包率分别为1%和80%时有损传输的时间,使用tcpdump -ttttt:
有损传输实例_第1张图片
下面是TCP无损传输在15%丢包率时传输时间:
在这里插入图片描述
重传引入这么大的延时换无损清晰度,值得吗?还是相信香农定律吧,有噪信道编码定理。
附上代码,我不怎么会编程,但也不是一点也不会,稍微会一点,编的不好,代码是搜来的一些片段拼凑的,自己也写了点,不多:

import java.util.Random;
import java.awt.image.*;
import java.io.*;
import java.net.*;
import javax.imageio.ImageIO;

public class ImgTrans {

	public static int [] dst_array = null;
	public static int DELTA = 500;

	public static int[] getPixels(String path) throws Exception {
		BufferedImage img = null;
		int w, h;
		int data[] = null;
		img = ImageIO.read(new File(path));
		w = img.getWidth(); h = img.getHeight();
		data = new int[2 + w * h];
		data[0] = w; data[1] = h;
		for (int i = 0; i < h; i ++) {
			for (int j = 0; j < w; j ++) {
				data[2 + w * i + j] = img.getRGB(j, i);
			}
		}
		return data;
	}

	public static void setPixels(int [] data, String path) throws Exception {
		BufferedImage img = null;
		File outfile = new File(path);
		int w, h, k = 0;
		w = data[0]; h = data[1];
		img = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR);
		for (int i = 0; i < h; i ++) {
			for (int j = 0; j < w; j ++) {
				img.setRGB(j, i, data[2 + w * i + j]);
			}
		}
		ImageIO.write(img, "jpg", outfile);
	}

	public static void receiveImg(int [] sub) {
		int delta = ImgTrans.DELTA;
		int len = sub[3];
		int offset = sub[4];
		int count  = len/delta;

		if (dst_array == null) {
			dst_array = new int[len + 2];
			for (int i = 0; i < len + 2; i ++) {
				dst_array[i] = 0;
			}
			dst_array[0] = sub[0]; dst_array[1] = sub[1];
		}

		for (int j = 0; j < count; j ++) {
			dst_array[2 + offset + j*ImgTrans.DELTA] = sub[j + 5];
		}
		if (offset < len%ImgTrans.DELTA) {
			dst_array[2 + offset + count*ImgTrans.DELTA] = sub[count + 5];
		}
	}

	public static void sendImg(int [] data) throws Exception {
		int delta = ImgTrans.DELTA;
		int count;
		int len = data.length - 2;
		int k = 0;
		int[] sub;
		DatagramSocket socket = new DatagramSocket();
		count = len/delta;

		for (int i = 0; i < ImgTrans.DELTA; i ++) {
			if (i < len%ImgTrans.DELTA) {
				sub = new int[count + 6];
				sub[2] = count + 6;
			} else {
				sub = new int[count + 5];
				sub[2] = count + 5;
			}
			sub[0] = data[0];
			sub[1] = data[1];
			sub[3] = len;
			sub[4] = i;
			for (int j = 0; j < count; j ++) {
				sub[5 + j] = data[i + j*ImgTrans.DELTA + 2];
			}
			if (i < len % ImgTrans.DELTA) {
				sub[count] = data[count*ImgTrans.DELTA + i + 2];
			}
			byte[] bs = ImgTrans.iatoba(sub);
			DatagramPacket subpkt = new DatagramPacket(bs, bs.length, InetAddress.getLocalHost(), 1234);
			socket.send(subpkt);
			/* for test
			Random rand = new Random();
			int r = rand.nextInt(200);
			if (r < 190) {
				System.out.println(sub.length + " len  count " + count);
				receiveImg(sub);
			}
			*/
		}
	}

	public static byte[] iatoba(int[] intarr) {
		int blen = intarr.length * 4;
		byte[] bt = new byte[blen];
		int curint = 0;
		for (int j = 0, k = 0; j < intarr.length; j ++, k += 4) {
			curint = intarr[j];
			bt[k] = (byte)((curint>>24) & 0xFF);
			bt[k+1] = (byte)((curint>>16) & 0xFF);
			bt[k+2] = (byte)((curint>>8) & 0xFF);
			bt[k+3] = (byte)((curint>>0 )& 0xFF);
		}
		return bt;
	}

	public static int[] batoia(byte[] btarr) {
		int i1,i2,i3,i4;
		int[] intarr = new int[btarr.length/4];
		for (int j = 0, k = 0; j < intarr.length; j ++, k += 4) {
			i1 = btarr[k];
			i2 = btarr[k+1];
			i3 = btarr[k+2];
			i4 = btarr[k+3];

			if (i1 < 0)
				i1 += 256;
			if (i2 < 0)
				i2 += 256;
			if(i3 < 0)
				i3+=256;
			if(i4 < 0)
				i4+=256;
			intarr[j] = (i1<<24) + (i2<<16) + (i3<<8) + (i4<<0);
		}
		return intarr;
	}

	static class RecvThread extends Thread {
		private DatagramSocket mSocket;
		public void run() {
			try {
				mSocket = new DatagramSocket(1234);
				while(true) {
					byte[] bs = new byte[5000];
					DatagramPacket subpkt = new DatagramPacket(bs, bs.length);
					mSocket.receive(subpkt);
					ImgTrans.receiveImg(ImgTrans.batoia(bs));
				}
			} catch (Exception e) { }
		}
	}
        // tc qdisc add dev lo root netem loss 1%
        // tc qdisc add dev lo root netem loss 5%
        // tc qdisc add dev lo root netem loss 10%
        // tc qdisc add dev lo root netem loss 40%
        // tc qdisc add dev lo root netem loss 80%
	public static void main(String []args) throws Exception {
		int data[] = null;
		RecvThread thread = new RecvThread();
		thread.start();
		Thread.sleep(100);
		data = getPixels(args[0]);
		sendImg(data);
		Thread.sleep(1000);
		setPixels(dst_array, args[1]);
		System.exit(0);
    	}
}

傍晚凉风体感不错,我不是一直说什么有损柔性传输吗,写个有损传输的demo,看下效果。

浙江温州皮鞋湿,下雨进水不会胖。

你可能感兴趣的:(有损传输)