vitalidze/chromecast-java-api-v2

ChromeCasts.get() is always empty on DHCP connections

angsuman opened this issue · 19 comments

I have tested this code on three devices, two running Ubuntu 16.04 and on Raspbian. One of the Ubuntu device (laptop connecting over DHCP) is unable to find ChromeCast devices even after a long period, whereas the other two (on static IP) never had any problems.
When I changed the connection from DHCP to Static IP (Manual) on the laptop, it connected fine.
I have eliminated all other possibilities.

Was this on the same interface ? Are you sure it was getting an address via DHCP ?
If yes, i don't see a reason why it wouldn't work either.

If they were two different interfaces, and one didn't work, you can specify which interface to use
by giving its address to the ChromeCasts.startDiscovery method.
So in case of DHCP you would have to get a list of your interfaces and get the address of one of them, listing the interfaces is very straight-forward in Java i think.

For me it sounds like an issue with networking configuration. It seems that your laptop connecting via DHCP is unable to receive multicast packets. This can be a setting on your laptop, or can be even filtered somehow on your router.

I tested that the laptop can receive multicast packets in DHCP mode using netcat. I am including two screenshots of connection information while in Static & in DHCP mode. In DHCP mode, the IP address is 192.168.1.102 whereas in static mode it is 192.168.1.10. Otherwise, as you will see, they look identical.

screenshot from 2017-11-27 09-47-18
screenshot from 2017-11-27 09-48-41

What is even more strange is that I can use Chrome to connect even in DHCP mode.

It should be receiving multicast DNS packets. See additional info here: https://en.wikipedia.org/wiki/Multicast_DNS

Are you sure it can receive these packets? I think you can check this out with netcat as well. Also maybe it makes sense to enable debug logging for the jmDNS (for the javax.jmdns.impl package for example).

I could not get jmDNS log turned on. I tried different adapters for slf4j. Wrote a comment on an existing defect for jmDNS.

From the Stack trace I found the following jmDNS threads:

"JmDNS(fe80:0:0:0:faca:b8ff:fe1b:6ddf%enp7s0.local.).State.Timer" #12 prio=5 os_prio=0 tid=0x00007f8ba0235800 nid=0x472e in Object.wait() [0x00007f8b84d1b000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)"JmDNS(fe80:0:0:0:faca:b8ff:fe1b:6ddf%enp7s0.local.).State.Timer" #12 prio=5 os_prio=0 tid=0x00007f8ba0235800 nid=0x472e in Object.wait() [0x00007f8b84d1b000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000000d6f84bd8> (a java.util.TaskQueue)
	at java.util.TimerThread.mainLoop(Timer.java:552)
	- locked <0x00000000d6f84bd8> (a java.util.TaskQueue)
	at java.util.TimerThread.run(Timer.java:505)

"JmDNS(fe80:0:0:0:faca:b8ff:fe1b:6ddf%enp7s0.local.).Timer" #11 daemon prio=5 os_prio=0 tid=0x00007f8ba0234000 nid=0x472d in Object.wait() [0x00007f8b84e1c000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000000d6f84298> (a java.util.TaskQueue)
	at java.util.TimerThread.mainLoop(Timer.java:552)
	- locked <0x00000000d6f84298> (a java.util.TaskQueue)
	at java.util.TimerThread.run(Timer.java:505)

"SocketListener(fe80:0:0:0:faca:b8ff:fe1b:6ddf%enp7s0.local.)" #10 daemon prio=5 os_prio=0 tid=0x00007f8ba0231800 nid=0x472c runnable [0x00007f8b84f1d000]
   java.lang.Thread.State: RUNNABLE
	at java.net.PlainDatagramSocketImpl.receive0(Native Method)
	- locked <0x00000000d6f762e0> (a java.net.PlainDatagramSocketImpl)
	at java.net.AbstractPlainDatagramSocketImpl.receive(AbstractPlainDatagramSocketImpl.java:143)
	- locked <0x00000000d6f762e0> (a java.net.PlainDatagramSocketImpl)
	at java.net.DatagramSocket.receive(DatagramSocket.java:812)
	- locked <0x00000000d7009760> (a java.net.DatagramPacket)
	- locked <0x00000000d6f720d8> (a java.net.MulticastSocket)
	**at javax.jmdns.impl.SocketListener.run(SocketListener.java:41)**

	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000000d6f84bd8> (a java.util.TaskQueue)
	at java.util.TimerThread.mainLoop(Timer.java:552)
	- locked <0x00000000d6f84bd8> (a java.util.TaskQueue)
	at java.util.TimerThread.run(Timer.java:505)

"JmDNS(fe80:0:0:0:faca:b8ff:fe1b:6ddf%enp7s0.local.).Timer" #11 daemon prio=5 os_prio=0 tid=0x00007f8ba0234000 nid=0x472d in Object.wait() [0x00007f8b84e1c000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000000d6f84298> (a java.util.TaskQueue)
	at java.util.TimerThread.mainLoop(Timer.java:552)
	- locked <0x00000000d6f84298> (a java.util.TaskQueue)
	at java.util.TimerThread.run(Timer.java:505)

"SocketListener(fe80:0:0:0:faca:b8ff:fe1b:6ddf%enp7s0.local.)" #10 daemon prio=5 os_prio=0 tid=0x00007f8ba0231800 nid=0x472c runnable [0x00007f8b84f1d000]
   java.lang.Thread.State: RUNNABLE
	at java.net.PlainDatagramSocketImpl.receive0(Native Method)
	- locked <0x00000000d6f762e0> (a java.net.PlainDatagramSocketImpl)
	at java.net.AbstractPlainDatagramSocketImpl.receive(AbstractPlainDatagramSocketImpl.java:143)
	- locked <0x00000000d6f762e0> (a java.net.PlainDatagramSocketImpl)
	at java.net.DatagramSocket.receive(DatagramSocket.java:812)
	- locked <0x00000000d7009760> (a java.net.DatagramPacket)
	- locked <0x00000000d6f720d8> (a java.net.MulticastSocket)
	**at javax.jmdns.impl.SocketListener.run(SocketListener.java:41)**

I ran the jmDNS client and server code. It communicates bi-directionally even over DHCP.
Machine with DHCP as Client:

Service added: [ServiceInfoImpl@880675387 name: 'example._http._tcp.local.' address: '(null):0' status: 'DNS: vostro.local. [vostro/127.0.1.1] state: probing 1 task: null', has NO data
	example._http._tcp.local.: ]
Service resolved: [ServiceInfoImpl@1048994123 name: 'example._http._tcp.local.' address: '/127.0.1.1:1234 ' status: 'DNS: vostro.local. [vostro/127.0.1.1] state: probing 1 task: null' is persistent, has data
	path: index.html]

Machine with DHCP as Server:

Service added: [ServiceInfoImpl@549751250 name: 'example._http._tcp.local.' address: '(null):0' status: 'DNS: satyaki.local. [satyaki/127.0.1.1] state: probing 1 task: null', has NO data
	example._http._tcp.local.: ]
Service resolved: [ServiceInfoImpl@771974074 name: 'example._http._tcp.local.' address: '/127.0.1.1:1234 ' status: 'DNS: satyaki.local. [satyaki/127.0.1.1] state: probing 1 task: null' is persistent, has data
	path: index.html]

I have doubts that testing mDNS on the same machine is a correct way to prove that you machine receives multicast messages. The chromecast is being discovered through mDNS, so it should be replying via broadcast address once your client starts sending discovery requests (maybe I am wrong here). From my point of view this "discovery data" is not received on your linux machine for some reason.

I tested on two machine, one connected via DHCP (laptop) and the other via static IP. One was client and the other was server and then vice versa. The code was lifted from jmDNS Github home page.
Is your code acting as server or client?

You should check whether your machines can receive data from chromecast devices.

Is your code acting as server or client?

It's acting as client. I mean that it does not provide any service info, instead of this it tries to look for available services on the network. From my understanding this is a work for "client" application.

Thanks. I just tested with avahi-browse. It can detect on both machines, the chromecast device:

  • enp7s0 IPv4 Chromecast-b11e05ebbe573caeef905b872c7ec33b _googlecast._tcp local

Updated:
I made a change to the Client code to listen to _googlecast._tcp local. It is working over DHCP too. However the chromecast-v2 is still hanging.
Here is the code I used:

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;

import javax.jmdns.JmDNS;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceListener;

public class Client {
    private static class SampleListener implements ServiceListener {
        @Override
        public void serviceAdded(ServiceEvent event) {
            System.out.println("Service added: " + event.getInfo());
        }

        @Override
        public void serviceRemoved(ServiceEvent event) {
            System.out.println("Service removed: " + event.getInfo());
        }

        @Override
        public void serviceResolved(ServiceEvent event) {
            System.out.println("Service resolved: " + event.getInfo());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        try {
            // Create a JmDNS instance
            JmDNS jmdns = JmDNS.create(InetAddress.getLocalHost());

            // Add a service listener
            jmdns.addServiceListener("_googlecast._tcp.local.", new SampleListener());

            // Wait a bit
            Thread.sleep(30000);
        } catch (UnknownHostException e) {
            System.out.println(e.getMessage());
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
}

I get the following response:

Service added: [ServiceInfoImpl@73598594 name: 'Chromecast-b11e05ebbe573caeef905b881b7ec33b._googlecast._tcp.local.' address: '(null):0' status: 'DNS: vostro.local. [vostro/127.0.1.1] state: probing 1 task: null', has NO data
	Chromecast-b11e05ebbe573caeef905b881b7ec33b._googlecast._tcp.local.: ]
Service resolved: [ServiceInfoImpl@245067250 name: 'Chromecast-b11e05ebbe573caeef905b881b7ec33b._googlecast._tcp.local.' address: '/192.168.1.105:8009 ' status: 'DNS: vostro.local. [vostro/127.0.1.1] state: probing 1 task: null' is persistent, has data
	id: b11e05ebbe573caeef905b881b7ec33b
	ic: /setup/icon.png
	cd: DF6B4763BAE02F6108D5FB1CD253CB19
	nf: 1
	rs: Now Casting: Mahabharat-270
	ca: 4101
	ve: 05
	rm: 
	md: Chromecast
	fn: mbr
	bs: FA8FCA7BB63E
	st: 1]

Quick question: Any reason why you are processing serviceAdded() instead of serviceResolved() where we get all the data in ChromeCasts.java?

PS. The dummy jmDNS client code is working identically. Not sure why chromecast is hanging. Investigating.

Found the solution. Consider deprecating startDiscovery() or change invocation of INSTANCE.doStartDiscovery(null) to INSTANCE.doStartDiscovery(java.net.InetAddress.getLocalHost())

There is a warning for JmDNS.create() - "Check that your platform correctly handle the default localhost IP address and the local hostname. In doubt use the explicit constructor."

Calling startDiscovery(java.net.InetAddress.getLocalHost()) also works for the same reason.

Hope that helps.

Thank you for sharing your solution.

You are most welcome. And thanks for sharing this wonderful library.