您的位置:首页 > 移动开发 > Android开发

[置顶] 基于UDP实现的android局域网视频同步播放

2016-08-19 17:58 911 查看
前段时间给公司的项目实现了一个局域网视频同步播放的功能,最近稍微空闲一些,所以稍微整理下,分享给大家学习下,文末附有下载地址。

一.概述

实现局域网视频同步播放,首先需要这些设备都有相同的文件,大家同时去播放相同的文件就可以了。所以我们选定一台设备作为主机,将自己当前播放的视频文件 以及进度告诉给其他设备,其他设备收到消息后去播放这个视频,并且seek到指定的进度位置。

在实现之前先科普下UDP的三种形式:“单播(Unicast)”、“多播(Multicast)”和“广播(Broadcast)”。

单播:好比一个人对另外一个人说话,此时信息的接收和传递 只在两个节点之间进行 。

广播:主机之间“一对所有”的通讯模式,网络对其中每一台主机发出的信号都进行无条件复制并转发,所有主机都可以接收到所有信

息(不管你是否需要),由于其不用路径选择,所以其网络成本可以很低廉。

组播:主机之间”一对一组”的通讯模式,也就是加入了同一个组的主机可以接受到此组内的所有数据,网络中的交换机和路由器只向

有需求者复制并转发其所需数据。

以下以分别以UDP的 广播 和组播形式实现局域网的同步播放。

广播和多播使用的都是D类IP地址,D类IP地址第一个字节以“lll0”开始,它是一个专门保留的地址。它并不指向特定的网络,目前这一类地址被用在多点广播(Multicast)中。多点广播地址用来一次寻址一组计算机,它标识共享同一协议的一组计算机。

所以我们可以通过广播或者组播形式来告诉局域网其他设备当前播放的情况从而实现视频同步播放。

先来讲解下整体实现思路:定义一个While(true)的线程去监测UDP请求,当收到主机发来数据包后我们对数据包进行截取,获取到有效信信息后发送广播出去做行进相应的处理.

我们所关心的是通过udp通信我们得到的是什么数据,所以在这里我们定义了2组指令数据包:

(1) “11#autosyncplay#” + AutoSyncGroup + “#” + System.currentTimeMillis() + “#” + final_asi + “#” + 同步路径

(2) “22#autosyncplay#” + AutoSyncGroup + “#” + index + “#” + position;

第一串指令是用来发起同步用的,第二组指令是用来通知进度改变的。11是标示发起同步用的,22是标示同步进度的,以#来分割信息,获取数据时候按#来分割获取。

所以在获取到数据之后我们就从中提取出我们需要的的信息进行处理,以下为处理数据部分代码:

private void doDealData(String data, String AutoSyncGroup,String type) {
if (data.startsWith("11#autosyncplay#" + AutoSyncGroup + "#")) { //开启同步指令
String[] darr = data.split("#");
String asi = "";
if (darr.length >= 5)
asi = darr[4];
String dir = "";
if (darr.length >= 6)
dir = darr[5];
AppDebug.Log(tag, type+"...AutoSyncGroup=" + AutoSyncGroup + ",asi=" + asi);
Intent intent = new Intent();
intent.setAction(MainService.ACT_SYNC_RELOAD_PAGE);
intent.putExtra("AutoSyncInterval", asi);
intent.putExtra("dir", dir);
MainService.getAppContext().sendBroadcast(intent);

} else if (data.startsWith("22#autosyncplay#" + AutoSyncGroup + "#")) { //同步进度指令

String[] darr = data.split("#");
int fileindex = 0;

if (darr.length >= 4)
fileindex = Integer.parseInt(darr[3]);
int position = 0;
if (darr.length >= 5)
position = Integer.parseInt(darr[4]);

AppDebug.Log(tag, type+"...fileindex=" + fileindex + ",position=" + position);

Intent intent = new Intent();
intent.setAction(MainService.ACT_SYNC_PROGRESS);
intent.putExtra("fileindex", fileindex);
intent.putExtra("position", position);
MainService.getAppContext().sendBroadcast(intent);
}
}


二.广播形式实现

1)受限广播

它不被路由发送,但会被送到相同物理网络段上的所有主机IP地址的网络字段和主机字段

全为1就是地址255.255.255.255(假如路由器将此类地址数据转发出去全世界都能收到

你发出的消息想想都恐怖)

2)直接广播

网络广播会被路由,并会发送到专门网络上的每台主机IP地址的网络字段定义这个网络,

主机字段通常全为1,如 192.168.10.255 .

前面已经说了需要一台设备作为Server发出自己的进度,其他设备作为Client接收到数据后做相应的

处理所以有一个Server发出数据包,和多个Clinet接收相应的数据。

1.Server发起同步

//UDP广播形式发送
public static void DatagramClientSend(int port, String cmd) {
String host = "255.255.255.255";//广播地址
DatagramSocket multiSocket;

try {
InetAddress adds = InetAddress.getByName(host);
AppDebug.Log(tag, "发送广播信息:" + cmd);
multiSocket = new DatagramSocket();

byte[] sendMSG = cmd.getBytes();
DatagramPacket dp = new DatagramPacket(sendMSG,
sendMSG.length, adds, port);
multiSocket.send(dp);
multiSocket.close();
} catch (UnknownHostException e) {
AppDebug.Log(tag, "发送广播信息...UnknownHostException"+e.getMessage());
e.printStackTrace();
} catch (SocketException e) {
AppDebug.Log(tag, "发送广播信息...SocketException"+e.getMessage());
e.printStackTrace();
} catch (IOException e) {
AppDebug.Log(tag, "发送广播信息...IOException"+e.getMessage());
e.printStackTrace();
}
}


因为不知道大家的路由网关地址,所以就以255这个受限地址作为目标地址,方便大家导入查看效果。

2.Clinet接收数据包

//UDP广播形式接收
public void DatagramServerStart(int localPort) {
int RECEIVE_LENGTH = 1024;
try {
DatagramSocket receiveDatagram = new DatagramSocket(localPort);
DatagramPacket dp = new DatagramPacket(new byte[RECEIVE_LENGTH], RECEIVE_LENGTH);
while (running) {
receiveDatagram.receive(dp);
String data = (new String(dp.getData())).substring(0,
dp.getLength()); //获取数据包实际长度
String AutoSyncGroup = "1";   //自动同步播放广播组,默认为1
if (AutoSyncGroup == null || AutoSyncGroup.equals(""))
AutoSyncGroup = "1";
AppDebug.Log(tag, "收到广播信息[" + dp.getLength() + "]:" + data);
doDealData(data, AutoSyncGroup,"DatagramServerStart");
}
receiveDatagram.close();
} catch (SocketException e) {
AppDebug.Log(tag, "收到广播信息...SocketException"+e.getMessage());
e.printStackTrace();
} catch (IOException e) {
AppDebug.Log(tag, "收到广播信息..IOException"+e.getMessage());
e.printStackTrace();
}
}


三.组播形式实现

组播和广播实现上基本类似,但是需要加入组,Server和Clinet需要同时加入相同组播地址 joinGroup(InetAddress groupAddr) 才可以。

1.Server发起同步

//UDP组播形式发送
public static boolean MulticastClientSend(int port, String cmd) {
String destAddressStr = "224.1.2.3";//目标地址
int destPort = port;
int TTL = 4;
boolean ret = false;

MulticastSocket multiSocket;
try {
InetAddress destAddress = InetAddress.getByName(destAddressStr);

if (!destAddress.isMulticastAddress()) {// 检测该地址是否是多播地址
return false;
}
AppDebug.Log(tag, "发送组播信息:" + cmd);

multiSocket = new MulticastSocket();

multiSocket.setTimeToLive(TTL);

byte[] sendMSG = cmd.getBytes();

DatagramPacket dp = new DatagramPacket(sendMSG, sendMSG.length, destAddress, destPort);

multiSocket.send(dp);

multiSocket.close();
ret = true;
} catch (IOException e) {
e.printStackTrace();
}
return ret;

}


需要注意的是组播需要路由器支持,有的路由并没有严格按照的UPnP协议来实现所以会出现自己能收到消息别人收不到, 或者自己收不到别人也收不到等情况。

2.Clinet接收组播数据包

public void MulticastServerStart(int localPort) {
String multicastHost = "224.1.2.3";
int RECEIVE_LENGTH = 1024;
InetAddress receiveAddress;
try {
receiveAddress = InetAddress.getByName(multicastHost);
int port = localPort;
MulticastSocket receiveMulticast = new MulticastSocket(port);
receiveMulticast.joinGroup(receiveAddress);//加入组播
DatagramPacket dp = new DatagramPacket(new byte[RECEIVE_LENGTH], RECEIVE_LENGTH);
while (running) {
receiveMulticast.receive(dp);
String data = (new String(dp.getData())).substring(0, dp.getLength());
String AutoSyncGroup = "1";   //自动同步播放广播组,默认为1

if (AutoSyncGroup == null || AutoSyncGroup.equals(""))
AutoSyncGroup = "1";
AppDebug.Log(tag, "收到组播信息[" + dp.getLength() + "]:" + data);
doDealData(data, AutoSyncGroup,"MulticastServerStart");
}
receiveMulticast.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}


源码下载,点击这里

github地址:https://github.com/mythace/Udp_LAN_SysPlay 欢迎satr or fork .
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: