diff --git a/app/src/main/java/org/sifacai/vlcjellyfin/Dlna/AVTransportAdapter.java b/app/src/main/java/org/sifacai/vlcjellyfin/Dlna/AVTransportAdapter.java index 65cf1ba..513451d 100644 --- a/app/src/main/java/org/sifacai/vlcjellyfin/Dlna/AVTransportAdapter.java +++ b/app/src/main/java/org/sifacai/vlcjellyfin/Dlna/AVTransportAdapter.java @@ -18,9 +18,9 @@ public class AVTransportAdapter extends RecyclerView.Adapter{ private Context context; private ArrayList avTransports; - public AVTransportAdapter(Context context) { + public AVTransportAdapter(Context context,ArrayList avTransports) { this.context = context; - this.avTransports = new ArrayList<>(); + this.avTransports = avTransports; } @NonNull diff --git a/app/src/main/java/org/sifacai/vlcjellyfin/Dlna/BroadCastThread.java b/app/src/main/java/org/sifacai/vlcjellyfin/Dlna/BroadCastThread.java new file mode 100644 index 0000000..a93a2b0 --- /dev/null +++ b/app/src/main/java/org/sifacai/vlcjellyfin/Dlna/BroadCastThread.java @@ -0,0 +1,189 @@ +package org.sifacai.vlcjellyfin.Dlna; + +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +import com.lzy.okgo.OkGo; + +import org.sifacai.vlcjellyfin.Utils.JfClient; +import org.xml.sax.SAXException; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.net.MulticastSocket; +import java.util.ArrayList; +import java.util.HashMap; + +import javax.xml.parsers.ParserConfigurationException; + +public class BroadCastThread extends Thread { + private String TAG = "广播线程"; + + public static final int TYPE_DEVICE = 1; //发现新设备 + public static final int TYPE_NEW_DEVICE_ADDED = 2; //新加了新设备 + public static final int TYPE_DEVICE_DEL = 3; //设备离线 + public static final int TYPE_MSG = 9; //消息 + + public static final String GroupAddress = "239.255.255.250"; + + private byte[] NOTIFY_rootdevice = ("M-SEARCH * HTTP/1.1\n" + + "ST: upnp:rootdevice\n" + + "MX: 10\n" + + "MAN: \"ssdp:discover\"\n" + + "Content-Length: 0\n" + + "HOST: 239.255.255.250:1900").getBytes(); + + private byte[] NOTIFY_MediaRenderer = ("M-SEARCH * HTTP/1.1\n" + + "ST: urn:schemas-upnp-org:device:MediaRenderer:1\n" + + "MX: 10\n" + + "MAN: \"ssdp:discover\"\n" + + "Content-Length: 0\n" + + "HOST: 239.255.255.250:1900").getBytes(); + + private Handler handler; + private MulticastSocket mSocket; + private ArrayList avTransports; + private boolean isStop = false; + + public BroadCastThread() { + avTransports = new ArrayList<>(); + try { + mSocket = new MulticastSocket(1900); + mSocket.joinGroup(InetAddress.getByName(GroupAddress)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void run() { + while (mSocket != null && !isStop) { + listenr(); + } + } + + public void SetHandler(Handler handler) { + this.handler = handler; + } + + public ArrayList GetAVTransportArray() { + return avTransports; + } + + /** + * 发送M-SEARCH + */ + public void refresh() { + new Thread(new Runnable() { + @Override + public void run() { + DatagramPacket packet = new DatagramPacket(NOTIFY_rootdevice, NOTIFY_rootdevice.length); + try { + packet.setAddress(InetAddress.getByName(GroupAddress)); + packet.setPort(1900); + mSocket.send(packet); + packet.setData(NOTIFY_MediaRenderer); + mSocket.send(packet); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }).start(); + } + + /** + * 监听广播 + */ + private void listenr() { + try { + byte[] buff = new byte[1024]; + DatagramPacket packet = new DatagramPacket(buff, buff.length); + mSocket.receive(packet); + String clientIP = packet.getAddress().getHostAddress(); + int clientPort = packet.getPort(); + String data = new String(packet.getData()).trim(); + ProgressNOTIFY(data); + } catch (IOException | XmlPullParserException e) { + throw new RuntimeException(e); + } + } + + public void ProgressNOTIFY(String data) throws IOException, XmlPullParserException { + Log.d(TAG, "ProgressNOTIFY: " + data); + if (data.startsWith("M-SEARCH")) return; + HashMap dh = DlnaDevice.parseNOTIFY(data); + if (isExitsOrByeBye(dh)) return; + boolean isav = DlnaDevice.isMediaRenderer(dh.get("nt")); + String location = dh.get("location"); + if (location == null) location = ""; + if (isav && !location.equals("")) { + String xmlstr = JfClient.SendGet(location); + if (xmlstr == null || xmlstr.equals("")) return; + findDevice(location, xmlstr); + } + } + + public void findDevice(String location, String xml) { + DlnaDevice device; + try { + device = XmlParser.parseX(xml); + for (int i = 0; i < device.DlnaServices.size(); i++) { + DlnaService ds = device.DlnaServices.get(i); + if (ds.serviceType.indexOf("service:AVTransport") > -1) { + int si = location.indexOf("/", 8); + String url = si > -1 ? location.substring(0, si) : location; + String moduleName = device.friendlyName.equals("") ? device.modelName : device.friendlyName; + AVTransport av = new AVTransport(); + av.moduleName = moduleName; + av.serviceId = ds.serviceId; + av.UDN = device.UDN; + av.controlURL = url + (ds.controlURL.startsWith("/") ? ds.controlURL : "/" + ds.controlURL); + av.eventSubURL = url + (ds.eventSubURL.startsWith("/") ? ds.eventSubURL : "/" + ds.eventSubURL); + av.iconurl = device.icon.size() > 0 ? url + "/" + device.icon.get(0) : ""; + avTransports.add(av); + if (handler != null) { + Message msg = new Message(); + msg.what = TYPE_NEW_DEVICE_ADDED; + handler.sendMessage(msg); + } + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } catch (SAXException e) { + throw new RuntimeException(e); + } + } + + /** + * 是否已存在或byebye + * @param dh + * @return + */ + public boolean isExitsOrByeBye(HashMap dh) { + String USN = dh.get("usn"); + String[] usns = USN.split("::"); + if (usns.length < 2) return true; + for (AVTransport av : avTransports) { + if (usns[0].equals(av.UDN)) { + if(dh.get("nts")!=null && dh.get("nts").toLowerCase().indexOf("byebye") >= 0){ + avTransports.remove(av); + Message msg = new Message(); + msg.what = TYPE_DEVICE_DEL; + handler.sendMessage(msg); + } + return true; + } + } + return false; + } + + public void Stop() { + isStop = true; + } +} diff --git a/app/src/main/java/org/sifacai/vlcjellyfin/Dlna/DlnaActivity.java b/app/src/main/java/org/sifacai/vlcjellyfin/Dlna/DlnaActivity.java index 59de906..fa8307d 100644 --- a/app/src/main/java/org/sifacai/vlcjellyfin/Dlna/DlnaActivity.java +++ b/app/src/main/java/org/sifacai/vlcjellyfin/Dlna/DlnaActivity.java @@ -28,42 +28,25 @@ public class DlnaActivity extends BaseActivity { private JRecyclerView rv; private AVTransportAdapter avTransportAdapter; - private MulticastSocket mSocket; - private Thread listen_thread; - private byte[] NOTIFY_rootdevice = ("M-SEARCH * HTTP/1.1\n" + - "ST: upnp:rootdevice\n" + - "MX: 10\n" + - "MAN: \"ssdp:discover\"\n" + - "Content-Length: 0\n" + - "HOST: 239.255.255.250:1900").getBytes(); + BroadCastThread broadCastTheader = new BroadCastThread(); - private byte[] NOTIFY_MediaRenderer = ("M-SEARCH * HTTP/1.1\n" + - "ST: urn:schemas-upnp-org:device:MediaRenderer:1\n" + - "MX: 10\n" + - "MAN: \"ssdp:discover\"\n" + - "Content-Length: 0\n" + - "HOST: 239.255.255.250:1900").getBytes(); - - private Handler handler = new Handler() { + private Handler handler= new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { - case 1: - Bundle b = msg.getData(); - AVTransport avt = new AVTransport(); - avt.moduleName = b.getString("moduleName"); - avt.serviceId = b.getString("serviceId"); - avt.UDN = b.getString("UDN"); - avt.controlURL = b.getString("controlURL"); - avt.eventSubURL = b.getString("eventSubURL"); - avt.iconurl = b.getString("iconurl"); - avTransportAdapter.addDevice(avt); + case BroadCastThread.TYPE_DEVICE: + break; + case BroadCastThread.TYPE_NEW_DEVICE_ADDED: + avTransportAdapter.notifyDataSetChanged(); + break; + case BroadCastThread.TYPE_DEVICE_DEL: + avTransportAdapter.notifyDataSetChanged(); break; } } - }; + };; @Override protected void onCreate(Bundle savedInstanceState) { @@ -71,19 +54,12 @@ public class DlnaActivity extends BaseActivity { setContentView(R.layout.activity_dlna); getSupportActionBar().hide(); - try { - mSocket = new MulticastSocket(1900); - mSocket.joinGroup(InetAddress.getByName("239.255.255.250")); - } catch (IOException e) { - throw new RuntimeException(e); - } - - listen_thread = new Thread(listen_Runnable); + broadCastTheader.SetHandler(handler); findViewById(R.id.refresh).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - refresh(); + broadCastTheader.refresh(); } }); @@ -91,7 +67,7 @@ public class DlnaActivity extends BaseActivity { V7LinearLayoutManager layoutManager = new V7LinearLayoutManager(rv.getContext()); layoutManager.setOrientation(V7LinearLayoutManager.VERTICAL); rv.setLayoutManager(layoutManager); - avTransportAdapter = new AVTransportAdapter(this); + avTransportAdapter = new AVTransportAdapter(this, broadCastTheader.GetAVTransportArray()); rv.setAdapter(avTransportAdapter); avTransportAdapter.setOnItemClickListener(new AVTransportAdapter.OnItemClickListener() { @Override @@ -103,10 +79,10 @@ public class DlnaActivity extends BaseActivity { Controller.SetAVTransportURI(avTransport.controlURL, vurl, new JfClient.JJCallBack() { @Override public void onSuccess(String str) { - Controller.GetMediaInfo(avTransport.controlURL,new JfClient.JJCallBack(){ + Controller.GetMediaInfo(avTransport.controlURL, new JfClient.JJCallBack() { @Override public void onSuccess(String str) { - Controller.Play(avTransport.controlURL,new JfClient.JJCallBack(){ + Controller.Play(avTransport.controlURL, new JfClient.JJCallBack() { @Override public void onSuccess(String str) { ShowToask("已发送!"); @@ -140,19 +116,18 @@ public class DlnaActivity extends BaseActivity { @Override protected void onStart() { super.onStart(); - if (!listen_thread.isAlive()) listen_thread.start(); - refresh(); + broadCastTheader.start(); } @Override protected void onPause() { super.onPause(); - listen_thread.interrupt(); } @Override protected void onStop() { super.onStop(); + broadCastTheader.Stop(); } @Override @@ -160,99 +135,4 @@ public class DlnaActivity extends BaseActivity { super.onDestroy(); } - /** - * 刷新设备 - */ - private void refresh() { - new Thread(new Runnable() { - @Override - public void run() { - DatagramPacket packet = new DatagramPacket(NOTIFY_rootdevice, NOTIFY_rootdevice.length); - try { - packet.setAddress(InetAddress.getByName("239.255.255.250")); - packet.setPort(1900); - mSocket.send(packet); - packet.setData(NOTIFY_MediaRenderer); - mSocket.send(packet); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }).start(); - } - - private Runnable listen_Runnable = new Runnable() { - @Override - public void run() { - while (mSocket != null) { - try { - byte[] buff = new byte[1024]; - DatagramPacket packet = new DatagramPacket(buff, buff.length); - mSocket.receive(packet); - String clientIP = packet.getAddress().getHostAddress(); - int clientPort = packet.getPort(); - String data = new String(packet.getData()).trim(); - ProgressNOTIFY(data); - } catch (IOException | XmlPullParserException e) { - throw new RuntimeException(e); - } - } - handler.post(listen_thread); - } - }; - - public void ProgressNOTIFY(String data) throws IOException, XmlPullParserException { - //Log.d(TAG, "ProgressNOTIFY: " + data); - if(data.startsWith("M-SEARCH")) return; - HashMap dh = DlnaDevice.parseNOTIFY(data); - boolean isav = DlnaDevice.isMediaRenderer(dh.get("nt")); - String location = dh.get("location"); - if(location == null) location = ""; - if (isav && !location.equals("")) { - String finalLocation = location; - JfClient.SendGet(location, new JfClient.JJCallBack() { - @Override - public void onSuccess(String str) { - findDevice(finalLocation, str); - } - }, new JfClient.JJCallBack() { - @Override - public void onError(String str) { - ShowToask(str); - } - }); - } - } - - public void findDevice(String location, String xml) { - DlnaDevice device; - try { - device = XmlParser.parseX(xml); - for (int i = 0; i < device.DlnaServices.size(); i++) { - DlnaService ds = device.DlnaServices.get(i); - if (ds.serviceType.indexOf("service:AVTransport") > -1) { - int si = location.indexOf("/", 8); - String url = si > -1 ? location.substring(0, si) : location; - String moduleName = device.friendlyName.equals("") ? device.modelName : device.friendlyName; - Bundle bundle = new Bundle(); - bundle.putString("moduleName", moduleName); - bundle.putString("UDN", device.UDN); - bundle.putString("serviceId", ds.serviceId); - bundle.putString("controlURL", url + (ds.controlURL.startsWith("/") ? ds.controlURL : "/" + ds.controlURL)); - bundle.putString("eventSubURL", url + "/" + ds.eventSubURL); - bundle.putString("iconurl", device.icon.size() > 0 ? url + "/" + device.icon.get(0) : ""); - Message msg = new Message(); - msg.what = 1; - msg.setData(bundle); - handler.sendMessage(msg); - } - } - } catch (IOException e) { - throw new RuntimeException(e); - } catch (ParserConfigurationException e) { - throw new RuntimeException(e); - } catch (SAXException e) { - throw new RuntimeException(e); - } - } } \ No newline at end of file