Receive DJI Tello's video stream using Java - java

I'm trying to write a java application for controlling the DJI Tello drone.
I'm already able to send simple commands to the drone by using java.net.DatagramSocket as client.
The Tello SDK says:
It says I have to use java.net.DatagramSocket as the server to receive the video stream.
This is my try to receive the video stream:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
public class TelloCamera extends Thread {
private boolean isStreamOn;
private DatagramSocket serverSocket;
private byte[] receiveData = new byte[1470];
public TelloCamera() {
isStreamOn = true;
}
public void run() {
try {
serverSocket = new DatagramSocket(11111);
} catch (SocketException e) {
e.printStackTrace();
return;
}
while (isStreamOn) {
receiveData = new byte[1470];
try {
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
serverSocket.receive(receivePacket);
String z = new String(receivePacket.getData());
System.out.println(z);
} catch (IOException e) {
e.printStackTrace();
}
isStreamOn = false;
}
serverSocket.close();
}
public boolean isStreamOn() {
return isStreamOn;
}
public void setStreamOn(boolean streamOn) {
isStreamOn = streamOn;
}
}
Main:
package tellotest;
public class maintellotest {
public static void main(String[] args) {
TelloCommander tello = new TelloCommander();
tello.sendCommand("command");
tello.sendCommand("streamon");
TelloCamera camera = new TelloCamera();
camera.start();
}
}
Does anybody know why I get no string printed to console?

You have to send "streamon" command at 8889 port to Tello first and then listen at 11111 port. But in your code code, there is no code for sending the command. Try adding the code to send command first, send "command" then send "streamon" then listen on port 11111.

I've also searched for a solution to this and came across a well implemented Tello java library, JTello # https://github.com/xrv0/JTello.
In the library the author make use of JCodec (http://jcodec.org/) to decode the H.246 stream:
For example:
// byte[] message refers to the content of the datagram received from the drone over port 1111
// Allocate output frame of max size
Picture out = Picture.create(1920, 1088, ColorSpace.YUV420);
Picture real = decoder.decodeFrame(ByteBuffer.wrap(message), out.getData());

You need to create an InetSocketAddress with IP "0.0.0.0" and port 11111 and then bind the socket to it. And also remove isStreamOn = false from the while loop.

Related

Socket connection not workin in flutter release apk

I am new to working with sockets, and I am working on this project where a connection between my android flutter app and a java server is needed, to do this I am trying socket programming.
The server code is fairly simple, I create a new thread for every client connected and I give them a bunch of URLs, later on, this should be replaced by a query result. here is the java code:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class CrawlitServer {
// The port number on which the server will listen for incoming connections.
public static final int PORT = 6666;
//main method
public static void main(String[] args) {
System.out.println("The server started .. ");
// Create a new server socket
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(PORT);
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
// Listen for incoming connections and create a new thread for each one
while (true) {
try {
new CrawlitServerThread(serverSocket.accept()).start();
}
catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}
public static class CrawlitServerThread extends Thread {
private final Socket socket;
public CrawlitServerThread(Socket socket) {
this.socket = socket;
}
public void run() {
List<String> list = new ArrayList<>();
//assign a value to list
list.add("http://www.google.com");
list.add("http://www.yahoo.com");
list.add("http://www.bing.com");
list.add("http://www.facebook.com");
list.add("http://www.twitter.com");
list.add("http://www.linkedin.com");
list.add("http://www.youtube.com");
list.add("http://www.wikipedia.com");
list.add("http://www.amazon.com");
list.add("http://www.ebay.com");
list.add("http://stackoverflow.com");
list.add("http://github.com");
list.add("http://quora.com");
list.add("http://reddit.com");
list.add("http://wikipedia.org");
try {
// Get the input stream from the socket
DataInputStream inputStream = new DataInputStream(socket.getInputStream());
Scanner scanner = new Scanner(inputStream);
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
PrintWriter writer = new PrintWriter(outputStream, true);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
System.out.println("Received Message from client: " + line);
writer.println(list + "\n");
}
}
catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}
}
Now I run this server and connect to it using sockets in Flutter, I give it the IP address I get from the ipconfig command, and here is the dart code:
import 'dart:async';
import 'dart:io';
//Utilities that manage connections with server sockets.
//ServerUtil Class
class ServerUtil {
static const port = 6666;
static const host = MY_IP_GOES_HERE;
static late Socket socket;
static bool connected = false;
//a list of urls returned by the server
static List<String> urls = [];
//Constructor
ServerUtil() {
//Initialize the socket.
Socket.connect(host, port).then((Socket sock) {
socket = sock;
connected = true;
socket.listen(dataHandler,
onError: errorHandler, onDone: doneHandler, cancelOnError: false);
//send a message to the server.
}).catchError((e) {
print("Unable to connect: $e");
});
}
//Query method that sends a message to the server. The server will return a list of urls.
//The urls will be added to the urls list.
//The urls list will be returned.
static Future<List<String>> query(String userQuery) async {
urls.clear();
//check if socket is connected.
if (connected) {
//send the query to the server.
socket.writeln(userQuery);
await Future.delayed(const Duration(milliseconds: 200));
print(urls);
return urls;
}
//if socket is not connected, wait for 5 seconds and try again.
await Future.delayed(const Duration(milliseconds: 50));
return query(userQuery);
}
//Handles data from the server.
void dataHandler(data) {
//String of received data.
String dataString = String.fromCharCodes(data).trim();
//remove first and last character from the string.
dataString = dataString.substring(1, dataString.length - 1);
//remove all the whitespace characters from the string.
dataString = dataString.replaceAll(RegExp(r'\s+'), '');
urls = dataString.split(',');
}
//Handles errors from the server.
void errorHandler(error, StackTrace trace) {
print(error);
}
//Handles when the connection is done.
void doneHandler() {
socket.destroy();
}
}
This works perfectly fine while using a debug apk running it on my real Note 9 device. The problem however is that when I build a release apk and try it out, nothing happens.
The way I set it up is that I wait for the query method in an async and then I send the result to a new screen and push that screen into the navigator.
But in the release apk nothing happens, the new screen doesn't load.
So this leads me to my first question:
Is there a way to debug a release apk? see what exceptions it throws or print some stuff to console?
I have the server running on my Laptop, and the app runs on my phone which is on the same WIFI network.
My second question is:
Do I need to enable some sort of option with my router or my laptop to allow my phone to connect? it does connect in debug mode without any modifications
I tried some random things, like using 'localhost' instead of my IP, as I would normally connect say with a java client for example, but it didn't work.
My last question is:
Does the release apk or like android OS prevent connections to local hosts, maybe because it thinks it is not secure? but then it still connects in debug mode.
Thank you for your time.

Messaging between Android and Desktop

I'm trying to make an Android app that's able to send a message to a computer and receive one from it. It's pretty basic. The thing is, I have accomplished this through multicasting, although not exactly. My app is able to receive messages from the computer (which uses a java application I made to receive and send the messages). But, when I try to send a message from the device to the computer, the message doesn't arrive to the computer. I mean, to the application.
Both the desktop app and the Android app use the same Client - Server classes. This is what gets me so confused. Because, as I am using the same classes, why does it work one way but not the other? I just don't no.
The desktop app runs on windows.
Also, when the Android app receives a message, it receives it the following way: "Message 1���������������������������..." when the message should be received: "Message 1". I don't know if this could be relevant.
The code is the following:
Server Class:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class MulticastSocketServer implements Runnable{
final static String INET_ADDR = "224.0.0.3";
final static int PORT = 8888;
static String msg;
public MulticastSocketServer(String message) throws UnknownHostException, InterruptedException {
msg = message;
Thread thread = new Thread(this);
thread.start();
}
#Override
public void run() {
// TODO Auto-generated method stub
// Get the address that we are going to connect to.
InetAddress addr = null;
try {
addr = InetAddress.getByName(INET_ADDR);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Open a new DatagramSocket, which will be used to send the data.
try (DatagramSocket serverSocket = new DatagramSocket()) {
msg += "\\0";
for (int i = 0; i < 10; i++) {
// Create a packet that will contain the data
// (in the form of bytes) and send it.
DatagramPacket msgPacket = new DatagramPacket(msg.getBytes(),
msg.getBytes().length, addr, PORT);
serverSocket.send(msgPacket);
System.out.println("Server sent packet with msg: " + msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
serverSocket.disconnect();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Client Class:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;
import java.util.Timer;
import java.util.TimerTask;
public class MulticastSocketClient implements Runnable {
final static String INET_ADDR = "224.0.0.3";
final static int PORT = 8888;
Connection360 conn;
public MulticastSocketClient (Connection360 connection) throws UnknownHostException {
conn = connection;
Thread thread = new Thread(this);
thread.start();
}
#Override
public void run() {
try{
// Get the address that we are going to connect to.
InetAddress address = InetAddress.getByName(INET_ADDR);
// Create a buffer of bytes, which will be used to store
// the incoming bytes containing the information from the server.
// Since the message is small here, 256 bytes should be enough.
byte[] buf = new byte[256];
// Create a new Multicast socket (that will allow other sockets/programs
// to join it as well.
try (final MulticastSocket clientSocket = new MulticastSocket(PORT)){
//Joint the Multicast group.
clientSocket.joinGroup(address);
System.out.println("Connected");
//while (true) {
// Receive the information and print it.
DatagramPacket msgPacket = new DatagramPacket(buf, buf.length);
Timer timer = new Timer("tmr");
timer.schedule(new TimerTask() {
#Override
public void run() {
clientSocket.disconnect();
}
},10000);
clientSocket.receive(msgPacket);
String msg = new String(buf, 0, buf.length);
System.out.println("Socket 1 received msg: " + msg.substring(0, msg.indexOf("\\0")));
conn.MessageReceived(msg.substring(0, msg.indexOf("\\0")));
clientSocket.disconnect();
//}
} catch (IOException ex) {
ex.printStackTrace();
}
}catch (UnknownHostException ex){
}
}
}
This classes are the ones I made for the desktop app. The classes I made for the Android app are the same, but I had to change the System.out.println() to Log.v(). As for the rest, it's exactly the same.
So, if you happen to know what could be happening, I would really appreciate your assistance with the topic.
Thank you!
When you read the incoming packet, you don't use its size but the size of the buffer instead:
String msg = new String(buf, 0, buf.length);
// should be:
String msg = new String(buf, 0, msgPacket.getLength());
// or even better:
String msg = new String(msgPacket.getData());
If the incoming packet is shorter, the rest of the buffer contains random data which is what you got. Java strings are not NUL-terminated so msg.indexOf("\\0") does not work.

How to close a server port after we are done using it?

Note: I found a similar question here:
How to close port after using server sockets
But did not find any satisfactory answer there.
Here is my code for the client program:
package hf;
import java.io.*;
import java.net.*;
public class DailyAdviceClient
{
private static final int chatPort = 4242;
public static void main(String[] args)
{
DailyAdviceClient client = new DailyAdviceClient();
client.go();
}
private void go()
{
try
{
Socket socket = new Socket("127.0.0.1",chatPort);
InputStreamReader inputStream = new InputStreamReader(socket.getInputStream());
BufferedReader bufferedReader = new BufferedReader(inputStream);
String advice = bufferedReader.readLine();
System.out.println("Advice received by the client for today is "+advice);
bufferedReader.close();
}
catch(Exception e)
{
System.out.println("Failed to connect to the server");
}
}
}
And here is the code for the server program:
package hf;
import java.io.*;
import java.net.*;
public class DailyAdviceServer
{
private String[] adviceList = {"Take smaller bites",
"Go for the tight jeans. No they do NOT make you look fat.",
"One word: inappropriate",
"Just for today, be honest. Tell your boss what you *really* think",
"You might want to rethink that haircut."};
private static final int chatPort = 4242;
public static void main(String[] args)
{
DailyAdviceServer server = new DailyAdviceServer();
server.go();
}
private void go()
{
try
{
ServerSocket serverSocket = new ServerSocket(chatPort);
while(true)
{
Socket socket = serverSocket.accept();
PrintWriter writer = new PrintWriter(socket.getOutputStream());
String advice = getTodaysAdvice();
writer.println(advice);
writer.close();
}
}
catch(Exception e)
{
System.out.println("Error in establishing connection with the client");
}
}
private String getTodaysAdvice()
{
String advice = null;
int randomIndex = (int) (Math.random()*adviceList.length);
advice = adviceList[randomIndex];
return advice;
}
}
In the application, whenever a client program connects to the server program, it receives a String that contains advice for the day.
When I run
netstat -an
In the command prompt of my Windows computer as suggested in one of the answers in the aforementioned link, I get a message that the port 4242 is
LISTENING
How do I close the port and make it available for future re-use?
To get rid of the LISTENING port you have to call serverSocket.close().
You have to use socket.close() after closing the writer and bufferedReader. So the Port will be free for another communication.

How do I run these two loops simultaneously? (Java)

I'm trying to write a simple console program that allows me to send and receive String messages. The problem I am encountering though, is that I don't know how to run the receiving code and the sending code simultaneously.
Individually, the classes are working. I can receive packets and send packets, but making them run at once seems impossible to me.
I've looked into multi-threading but since my knowledge is still very basic, I can't seem to understand how it really works.
This is the code I'm currently using. I wrote the Dialog class myself and found the other two classes on the internet.
Dialog class:
import java.util.Scanner;
public class Dialog {
Scanner scanner = new Scanner(System.in);
User user = new User();
Network net = new Network();
ThreadReceive tr = new ThreadReceive();
ThreadSend ts = new ThreadSend();
public void run() {
System.out.println("WELCOME");
System.out.print("Port: ");
while(!user.setPort(giveInput())) {
System.out.println("Enter a valid port.");
}
System.out.print("IP: ");
user.setIP(giveInput());
System.out.println();
System.out.println("--- CONVERSATION STARTED ---");
tr.receive(user.getIP(), user.getPort()); // Starts receiving loop (within ThreadReceive class).
while (true) { // Starts sending loop.
ts.sendMessage(giveInput(), user.getIP(), user.getPort()); // Sends packet when input is given.
}
}
private String giveInput() {
String input = scanner.nextLine();
return input;
}
}
Receiving class:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class ThreadReceive extends Thread {
public void receive(String ip, int port) {
try {
// Create a socket to listen on the port.
DatagramSocket dsocket = new DatagramSocket(port);
// Create a buffer to read datagrams into. If a
// packet is larger than this buffer, the
// excess will simply be discarded!
byte[] buffer = new byte[2048];
// Create a packet to receive data into the buffer
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// Now loop forever, waiting to receive packets and printing them.
while (true) {
// Wait to receive a datagram
dsocket.receive(packet);
// Convert the contents to a string, and display them
String msg = new String(buffer, 0, packet.getLength());
System.out.println(packet.getAddress().getHostName() + ": " + msg);
// Reset the length of the packet before reusing it.
packet.setLength(buffer.length);
}
}
catch (Exception e) {
System.err.println(e);
}
}
}
Sending class:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class ThreadSend extends Thread {
public void sendMessage(String message, String ip, int port) {
try {
byte[] data = message.getBytes();
InetAddress address = InetAddress.getByName(ip);
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
DatagramSocket datagramSocket = new DatagramSocket();
datagramSocket.send(packet);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Also, is there any way to test if I can receive packets? I've been testing it with a friend but it would be much more convenient to do it myself.
Thanks!
You are not using Threads correctly.
The logic should be in the run method.
I suggest you use a queue such as an ArrayBlockingQueue to pass parameters to your threads. For instance, you could have a method to add elements to this queue
public void addMessage(String message) {
synchronized(inputQueue) {
inputQueue.offer(r);
inputQueue.notify();
}
}
And the run method will use these elements as so :
public void run() {
try {
while(!running)
synchronized (inputQueue) {
inputQueue.wait(); // you can have a timeout also...
String message = this.inputQueue.poll();
// use the message item....
// in your case send it to the other user.
}
}
}
} catch (Exception e) {
/////// your exception handler
}
}
Also Remember to start your threads :
Thread t = new MyThread();
t.start(); /// Start the thread !!!
PS : The messages can be any object here is used strings as i based this on some of my code where i'm using a
Queue<String>
Check out Beej's Guide to Network Programming: http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html -- This will give you some more examples to take a look at. As far as testing goes, you could set up a virtual machine or use another computer you own. Back when I had to learn networking in school we would ssh into two separate Linux boxes to test our code.
EDIT: Also to make sure you are receiving correctly you could make the sender and receiver both print out the packet data they receive. Or you could just have them print a simple string to say the packet was received.
You also might want to check out TCP rather than UDP if you are wanting a continuous stream. UDP just creates a datagram packet and sends it out on the network, whereas TCP creates a persistant connection between two hosts.

Trying to get a java socket program to work, but getting "java.net.BindException: Address already in use 6666 "

Here is the code
I have written a server and client. But when i run them, (as you can see in the last program), I get the following error:
Whoop s! java.net.BindException: Address already in use 6666
6666 is the port no. i specified.
import java.net.*;
import java.io.*;
import java.lang.*;
public class processSendHelper
{
Process p;
String address;
int port;
long msg_data;
processSendHelper(int pid, int current_round, long address, long msg_data, int port)
{
try
{
ServerSocket sSoc = new ServerSocket(port);
Socket inSoc = sSoc.accept();
msg_Thread msgT = new msg_Thread(inSoc, msg_data);
msgT.start();
Thread.sleep(5000);
sSoc.close();
}
catch(Exception e)
{
System.out.println("Whoop s! " + e.toString());
}
}
}
/* sends out (or rather just makes available) the provided msg
* */
class msg_Thread extends Thread
{
Socket threadSoc;
long msg_data;
msg_Thread (Socket inSoc, long msg_data)
{
threadSoc = inSoc;
this.msg_data = msg_data;
}
public void run()
{
try
{
PrintStream SocOut = new
PrintStream(threadSoc.getOutputStream());
SocOut.println(msg_data);
}
catch (Exception e)
{
System.out.println("Whoops!" + e.toString());
}
try
{
threadSoc.close();
}
catch (Exception e)
{
System.out.println("Oh no! " +
e.toString());
}
}
}
import java.net.*;
import java.io.*;
public class processReceiveHelper
{
Socket appSoc;
BufferedReader in;
String message;
String host;
int port;
processReceiveHelper(String host,int port)
{
try
{
appSoc = new Socket(host,port);
in = new BufferedReader(new InputStreamReader(appSoc.getInputStream()));
message = in.readLine();
System.out.println(message);
/* Tokenizer code comes here
* Alongwith the code for
* updating the process object's
* data
* */
}
catch (Exception e)
{
System.out.println("Died... " +
e.toString());
}
}
}
public class Orchestrator
{
public static void main(String[] args)
{
processSendHelper psh = new processSendHelper(1, 2, 1237644, 6666, 2002);
processReceiveHelper prh = new processReceiveHelper("localhost", 2002);
}
}
EDIT:
I found the problem. The reason was that i was running both the server and client from the same main program.
the following worked:
That means there is already an application operating on port 6666 preventing your Java application using it. However, it is equally possible there is a running process of your Java application still holding onto 6666. Terminate any running java processes and try re-running the code - if it still fails then you have some other application using 6666 and you would be better using a different port.
That means that the port 6666 is already being used. There are two main causes/solutions for this:
Some other program is using that port. Solution: Choose a different port.
Your old Java program is hanging and still "using" that port. Close all of your hanging Java programs and try again. If that doesn't solve your problem, choose a different port.
Does it happen when you run the program for the second time? You may want to setReuseAddress(true) on this socket.

Categories