From cf8b26759e206f5bd70617235c91432696515f1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= Date: Thu, 27 Dec 2018 12:55:30 +0100 Subject: [PATCH 01/10] Support for TaiSync on Android --- android.pri | 3 +- android/AndroidManifest.xml | 13 +- android/res/xml/device_filter.xml | 1 + .../org/mavlink/qgroundcontrol/QGCActivity.java | 113 +++++++++- .../src/org/mavlink/qgroundcontrol/TaiSync.java | 240 +++++++++++++++++++++ 5 files changed, 357 insertions(+), 13 deletions(-) create mode 100644 android/src/org/mavlink/qgroundcontrol/TaiSync.java diff --git a/android.pri b/android.pri index 8179a76..f655709 100644 --- a/android.pri +++ b/android.pri @@ -15,7 +15,8 @@ OTHER_FILES += \ $$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialProber.java \ $$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialRuntimeException.java \ $$PWD/android/src/org/mavlink/qgroundcontrol/QGCActivity.java \ - $$PWD/android/src/org/mavlink/qgroundcontrol/UsbIoManager.java + $$PWD/android/src/org/mavlink/qgroundcontrol/UsbIoManager.java \ + $$PWD/android/src/org/mavlink/qgroundcontrol/TaiSync.java DISTFILES += \ $$PWD/android/gradle/wrapper/gradle-wrapper.jar \ diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 7143aa2..beddd03 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -7,10 +7,12 @@ + + @@ -60,13 +62,14 @@ - - + + - + - - + + + diff --git a/android/res/xml/device_filter.xml b/android/res/xml/device_filter.xml index 782fae8..3147457 100644 --- a/android/res/xml/device_filter.xml +++ b/android/res/xml/device_filter.xml @@ -2,5 +2,6 @@ + diff --git a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java index 2f99cd2..756597b 100644 --- a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java +++ b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java @@ -29,10 +29,13 @@ package org.mavlink.qgroundcontrol; // //////////////////////////////////////////////////////////////////////////////////////////// +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.Timer; +import java.util.TimerTask; import java.io.IOException; import android.app.Activity; @@ -40,12 +43,16 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.hardware.usb.*; +import android.hardware.usb.UsbAccessory; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbManager; import android.widget.Toast; import android.util.Log; import android.os.PowerManager; -import android.view.WindowManager; import android.os.Bundle; +import android.app.PendingIntent; +import android.view.WindowManager; import com.hoho.android.usbserial.driver.*; import org.qtproject.qt5.android.bindings.QtActivity; @@ -65,6 +72,8 @@ public class QGCActivity extends QtActivity private final static ExecutorService m_Executor = Executors.newSingleThreadExecutor(); private static final String TAG = "QGC_QGCActivity"; private static PowerManager.WakeLock m_wl; + private static String USB_ACTION = "org.mavlink.qgroundcontrol.action.USB_PERMISSION"; + private TaiSync taiSync = null; public static Context m_context; @@ -85,6 +94,26 @@ public class QGCActivity extends QtActivity } }; + private final BroadcastReceiver mOpenAccessoryReceiver = + new BroadcastReceiver() + { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (USB_ACTION.equals(action)) { + UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); + if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { + openAccessory(accessory); + } + } else if( UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) { + UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); + if (accessory != null) { + closeAccessory(accessory); + } + } + } + }; + // NATIVE C++ FUNCTION THAT WILL BE CALLED IF THE DEVICE IS UNPLUGGED private static native void nativeDeviceHasDisconnected(int userDataA); private static native void nativeDeviceException(int userDataA, String messageA); @@ -120,10 +149,27 @@ public class QGCActivity extends QtActivity Log.i(TAG, "SCREEN_BRIGHT_WAKE_LOCK not acquired!!!"); } m_instance.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + if (m_manager == null) { + try { + m_manager = (UsbManager)m_instance.getSystemService(Context.USB_SERVICE); + taiSync = new TaiSync(); + + IntentFilter filter = new IntentFilter(USB_ACTION); + filter.addAction( UsbManager.ACTION_USB_ACCESSORY_DETACHED); + registerReceiver(mOpenAccessoryReceiver, filter); + + probeAccessories(); + } catch(Exception e) { + Log.e(TAG, "Exception getCurrentDevices(): " + e); + } + } } @Override - protected void onDestroy() { + protected void onDestroy() + { + unregisterReceiver(mOpenAccessoryReceiver); try { if(m_wl != null) { m_wl.release(); @@ -149,9 +195,6 @@ public class QGCActivity extends QtActivity if (m_instance == null) return false; - if (m_manager == null) - m_manager = (UsbManager)m_instance.getSystemService(Context.USB_SERVICE); - if (m_devices != null) m_devices.clear(); @@ -193,7 +236,7 @@ public class QGCActivity extends QtActivity int countL = 0; int iL; - // CHECK FOR ALREADY OPENED DEVICES AND DON"T INCLUDE THEM IN THE COUNT + // CHECK FOR ALREADY OPENED DEVICES AND DON'T INCLUDE THEM IN THE COUNT for (iL=0; iL= 0) { + synchronized (this) + { + if (!running) { + break; + } + } + + bytesRead = mFileInputStream.read(mBytes); + + if (bytesRead > 0) + { + if (mBytes[3] == PROTOCOL_VERSION) + { + vMaj = mBytes[19]; + Log.i("QGC_TaiSync", "Got protocol version message vMaj = " + mBytes[19]); + byte[] data = { 0x00, 0x00, 0x00, 0x01, // uint32 - protocol + 0x00, 0x00, 0x00, 0x00, // uint32 - dport + 0x00, 0x00, 0x00, 0x1C, // uint32 - length + 0x00, 0x00, 0x00, 0x00, // uint32 - magic + 0x00, 0x00, 0x00, vMaj, // uint32 - version major + 0x00, 0x00, 0x00, 0x00, // uint32 - version minor + 0x00, 0x00, 0x00, 0x00 }; // uint32 - padding + mFileOutputStream.write(data); + } + else if (mBytes[3] == PROTOCOL_CHANNEL) { + int dPort = ((mBytes[4] & 0xff)<< 24) | ((mBytes[5]&0xff) << 16) | ((mBytes[6]&0xff) << 8) | (mBytes[7] &0xff); + int dLength = ((mBytes[8] & 0xff)<< 24) | ((mBytes[9]&0xff) << 16) | ((mBytes[10]&0xff) << 8) | (mBytes[11] &0xff); + Log.i("QGC_TaiSync", "Read 2 port = " + dPort + " length = " + dLength); + byte[] data = { 0x00, 0x00, 0x00, 0x02, // uint32 - protocol + 0x00, 0x00, mBytes[6], mBytes[7], // uint32 - dport + 0x00, 0x00, 0x00, 0x1C, // uint32 - length + 0x00, 0x00, 0x00, 0x00, // uint32 - magic + 0x00, 0x00, 0x00, vMaj, // uint32 - version major + 0x00, 0x00, 0x00, 0x00, // uint32 - version minor + 0x00, 0x00, 0x00, 0x00 }; // uint32 - padding + mFileOutputStream.write(data); + } + else if (mBytes[3] == PROTOCOL_DATA) { + int dPort = ((mBytes[4] & 0xff)<< 24) | ((mBytes[5]&0xff) << 16) | ((mBytes[6]&0xff) << 8) | (mBytes[7] &0xff); + int dLength = ((mBytes[8] & 0xff)<< 24) | ((mBytes[9]&0xff) << 16) | ((mBytes[10]&0xff) << 8) | (mBytes[11] &0xff); + + int payloadOffset = 28; + int payloadLength = bytesRead - payloadOffset; + + if (dPort == 8000) { // Video + byte[] sBytes = new byte[payloadLength]; + System.arraycopy(mBytes, payloadOffset, sBytes, 0, payloadLength); + DatagramPacket packet = new DatagramPacket(sBytes, sBytes.length, address, VIDEO_PORT); + socket.send(packet); + + } else if (dPort == 8200) { // Command +/* + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < dLength; i++) { + sb.append(String.format("%02X ", mBytes[i])); + } + Log.i("QGC_TaiSync", "Read 3 port = " + dPort + " length = " + dLength + " - " + sb.toString()); +*/ + } else if (dPort == 8400) { // Telemetry + byte[] sBytes = new byte[payloadLength]; + System.arraycopy(mBytes, payloadOffset, sBytes, 0, payloadLength); + DatagramPacket packet = new DatagramPacket(sBytes, sBytes.length, address, TELEM_PORT); + socket.send(packet); + } + } + } + } + } catch (IOException e) { + Log.e("QGC_TaiSync", "Exception: " + e); + e.printStackTrace(); + } finally { + close(); + } + } + }); + + mThreadPool.execute(new Runnable() { + @Override + public void run() + { + byte[] inbuf = new byte[256]; + + try { + while (true) { + synchronized (this) + { + if (!running) { + break; + } + } + + DatagramPacket packet = new DatagramPacket(inbuf, inbuf.length); + socket.receive(packet); + + int dataPort = 8400; + byte portMSB = (byte)((dataPort >> 8) & 0xFF); + byte portLSB = (byte)(dataPort & 0xFF); + + byte[] lA = new byte[4]; + int len = 28 + packet.getLength(); + byte[] buffer = new byte[len]; + + for (int i = 3; i >= 0; i--) { + lA[i] = (byte)(len & 0xFF); + len >>= 8; + } + + byte[] header = { 0x00, 0x00, 0x00, PROTOCOL_DATA, // uint32 - protocol + 0x00, 0x00, portMSB, portLSB, // uint32 - dport + lA[0], lA[1], lA[2], lA[3], // uint32 - length + 0x00, 0x00, 0x00, 0x00, // uint32 - magic + 0x00, 0x00, 0x00, vMaj, // uint32 - version major + 0x00, 0x00, 0x00, 0x00, // uint32 - version minor + 0x00, 0x00, 0x00, 0x00 }; // uint32 - padding + + System.arraycopy(header, 0, buffer, 0, header.length); + System.arraycopy(packet.getData(), 0, buffer, header.length, packet.getLength()); + mFileOutputStream.write(buffer); + } + } catch (IOException e) { + Log.e("QGC_TaiSync", "Exception: " + e); + e.printStackTrace(); + } finally { + close(); + } + } + }); + } + + public void close() + { +// Log.i("QGC_TaiSync", "Close"); + synchronized (this) + { + running = false; + } + try { + if (socket != null) { + socket.close(); + socket = null; + } + if (mParcelFileDescriptor != null) { + mParcelFileDescriptor.close(); + mParcelFileDescriptor = null; + } + } catch (Exception e) { + } + socket = null; + } +} From cd4b4991313c9442fd9c3e9b279c88fe94143ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= Date: Thu, 27 Dec 2018 13:02:42 +0100 Subject: [PATCH 02/10] Restart video when stream ends to recover from loss of communication. --- src/VideoStreaming/VideoReceiver.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 148e071..5f04a75 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -535,7 +535,8 @@ VideoReceiver::_shutdownPipeline() { void VideoReceiver::_handleError() { qCDebug(VideoReceiverLog) << "Gstreamer error!"; - _shutdownPipeline(); + stop(); + start(); } #endif @@ -550,7 +551,8 @@ VideoReceiver::_handleEOS() { _shutdownRecordingBranch(); } else { qWarning() << "VideoReceiver: Unexpected EOS!"; - _shutdownPipeline(); + stop(); + start(); } } #endif From 0f99418c0354aba1b0b71f11d1e927aaa22862a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= Date: Fri, 28 Dec 2018 15:11:22 +0100 Subject: [PATCH 03/10] TaiSync Android - add support for settings protocol --- .../src/org/mavlink/qgroundcontrol/TaiSync.java | 202 ++++++++++++--------- 1 file changed, 118 insertions(+), 84 deletions(-) diff --git a/android/src/org/mavlink/qgroundcontrol/TaiSync.java b/android/src/org/mavlink/qgroundcontrol/TaiSync.java index 3efb77d..2638507 100644 --- a/android/src/org/mavlink/qgroundcontrol/TaiSync.java +++ b/android/src/org/mavlink/qgroundcontrol/TaiSync.java @@ -3,6 +3,8 @@ package org.mavlink.qgroundcontrol; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.io.InputStream; +import java.io.OutputStream; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -10,6 +12,7 @@ import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; +import java.net.Socket; import java.net.InetAddress; import android.os.ParcelFileDescriptor; @@ -17,15 +20,24 @@ import android.util.Log; public class TaiSync { - private static final int PROTOCOL_VERSION = 0x1; - private static final int PROTOCOL_CHANNEL = 0x02; - private static final int PROTOCOL_DATA = 0x03; + private static final int HEADER_SIZE = 0x1C; + + private static final byte PROTOCOL_REQUEST_CONNECTION = 0x00; + private static final byte PROTOCOL_VERSION = 0x01; + private static final byte PROTOCOL_CHANNEL = 0x02; + private static final byte PROTOCOL_DATA = 0x03; private static final int TELEM_PORT = 14550; private static final int VIDEO_PORT = 5000; + private static final int TAISYNC_VIDEO_PORT = 8000; + private static final int TAISYNC_SETTINGS_PORT = 8200; + private static final int TAISYNC_TELEMETRY_PORT = 8400; private boolean running = false; - private DatagramSocket socket = null; + private DatagramSocket udpSocket = null; + private Socket tcpSocket = null; + private InputStream tcpInStream = null; + private OutputStream tcpOutStream = null; private ParcelFileDescriptor mParcelFileDescriptor; private FileInputStream mFileInputStream; private FileOutputStream mFileOutputStream; @@ -40,8 +52,7 @@ public class TaiSync public boolean isRunning() { - synchronized (this) - { + synchronized (this) { return running; } } @@ -50,8 +61,7 @@ public class TaiSync { // Log.i("QGC_TaiSync", "Open"); - synchronized (this) - { + synchronized (this) { if (running) { return; } @@ -67,19 +77,17 @@ public class TaiSync mFileInputStream = new FileInputStream(fileDescriptor); mFileOutputStream = new FileOutputStream(fileDescriptor); - socket = new DatagramSocket(); + udpSocket = new DatagramSocket(); final InetAddress address = InetAddress.getByName("localhost"); + tcpSocket = new Socket(address, TAISYNC_SETTINGS_PORT); + tcpInStream = tcpSocket.getInputStream(); + tcpOutStream = tcpSocket.getOutputStream(); // Request connection packet - byte[] conn = { 0x00, 0x00, 0x00, 0x00, // uint32 - protocol - 0x00, 0x00, 0x00, 0x00, // uint32 - dport - 0x00, 0x00, 0x00, 0x1C, // uint32 - length - 0x00, 0x00, 0x00, 0x00, // uint32 - magic - 0x00, 0x00, 0x00, 0x00, // uint32 - version major - 0x00, 0x00, 0x00, 0x00, // uint32 - version minor - 0x00, 0x00, 0x00, 0x00 }; // uint32 - padding - mFileOutputStream.write(conn); + byte[] msg = constructTaiSyncMessage(PROTOCOL_REQUEST_CONNECTION, 0, null, 0); + mFileOutputStream.write(msg); + // Read multiplexed data stream coming from TaiSync accessory mThreadPool.execute(new Runnable() { @Override public void run() @@ -88,8 +96,7 @@ public class TaiSync try { while (bytesRead >= 0) { - synchronized (this) - { + synchronized (this) { if (!running) { break; } @@ -103,54 +110,34 @@ public class TaiSync { vMaj = mBytes[19]; Log.i("QGC_TaiSync", "Got protocol version message vMaj = " + mBytes[19]); - byte[] data = { 0x00, 0x00, 0x00, 0x01, // uint32 - protocol - 0x00, 0x00, 0x00, 0x00, // uint32 - dport - 0x00, 0x00, 0x00, 0x1C, // uint32 - length - 0x00, 0x00, 0x00, 0x00, // uint32 - magic - 0x00, 0x00, 0x00, vMaj, // uint32 - version major - 0x00, 0x00, 0x00, 0x00, // uint32 - version minor - 0x00, 0x00, 0x00, 0x00 }; // uint32 - padding - mFileOutputStream.write(data); + byte[] msg = constructTaiSyncMessage(PROTOCOL_VERSION, 0, null, 0); + mFileOutputStream.write(msg); } else if (mBytes[3] == PROTOCOL_CHANNEL) { int dPort = ((mBytes[4] & 0xff)<< 24) | ((mBytes[5]&0xff) << 16) | ((mBytes[6]&0xff) << 8) | (mBytes[7] &0xff); int dLength = ((mBytes[8] & 0xff)<< 24) | ((mBytes[9]&0xff) << 16) | ((mBytes[10]&0xff) << 8) | (mBytes[11] &0xff); Log.i("QGC_TaiSync", "Read 2 port = " + dPort + " length = " + dLength); - byte[] data = { 0x00, 0x00, 0x00, 0x02, // uint32 - protocol - 0x00, 0x00, mBytes[6], mBytes[7], // uint32 - dport - 0x00, 0x00, 0x00, 0x1C, // uint32 - length - 0x00, 0x00, 0x00, 0x00, // uint32 - magic - 0x00, 0x00, 0x00, vMaj, // uint32 - version major - 0x00, 0x00, 0x00, 0x00, // uint32 - version minor - 0x00, 0x00, 0x00, 0x00 }; // uint32 - padding - mFileOutputStream.write(data); + byte[] msg = constructTaiSyncMessage(PROTOCOL_CHANNEL, dPort, null, 0); + mFileOutputStream.write(msg); } else if (mBytes[3] == PROTOCOL_DATA) { int dPort = ((mBytes[4] & 0xff)<< 24) | ((mBytes[5]&0xff) << 16) | ((mBytes[6]&0xff) << 8) | (mBytes[7] &0xff); int dLength = ((mBytes[8] & 0xff)<< 24) | ((mBytes[9]&0xff) << 16) | ((mBytes[10]&0xff) << 8) | (mBytes[11] &0xff); - int payloadOffset = 28; + int payloadOffset = HEADER_SIZE; int payloadLength = bytesRead - payloadOffset; - if (dPort == 8000) { // Video - byte[] sBytes = new byte[payloadLength]; - System.arraycopy(mBytes, payloadOffset, sBytes, 0, payloadLength); + byte[] sBytes = new byte[payloadLength]; + System.arraycopy(mBytes, payloadOffset, sBytes, 0, payloadLength); + + if (dPort == TAISYNC_VIDEO_PORT) { DatagramPacket packet = new DatagramPacket(sBytes, sBytes.length, address, VIDEO_PORT); - socket.send(packet); - - } else if (dPort == 8200) { // Command -/* - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < dLength; i++) { - sb.append(String.format("%02X ", mBytes[i])); - } - Log.i("QGC_TaiSync", "Read 3 port = " + dPort + " length = " + dLength + " - " + sb.toString()); -*/ - } else if (dPort == 8400) { // Telemetry - byte[] sBytes = new byte[payloadLength]; - System.arraycopy(mBytes, payloadOffset, sBytes, 0, payloadLength); + udpSocket.send(packet); + } else if (dPort == TAISYNC_SETTINGS_PORT) { + tcpOutStream.write(sBytes); + } else if (dPort == TAISYNC_TELEMETRY_PORT) { DatagramPacket packet = new DatagramPacket(sBytes, sBytes.length, address, TELEM_PORT); - socket.send(packet); + udpSocket.send(packet); } } } @@ -164,6 +151,7 @@ public class TaiSync } }); + // Read command & control packets to be sent to telemetry port mThreadPool.execute(new Runnable() { @Override public void run() @@ -172,40 +160,46 @@ public class TaiSync try { while (true) { - synchronized (this) - { + synchronized (this) { if (!running) { break; } } DatagramPacket packet = new DatagramPacket(inbuf, inbuf.length); - socket.receive(packet); - - int dataPort = 8400; - byte portMSB = (byte)((dataPort >> 8) & 0xFF); - byte portLSB = (byte)(dataPort & 0xFF); + udpSocket.receive(packet); + byte[] msg = constructTaiSyncMessage(PROTOCOL_DATA, TAISYNC_TELEMETRY_PORT, packet.getData(), packet.getLength()); + mFileOutputStream.write(msg); + } + } catch (IOException e) { + Log.e("QGC_TaiSync", "Exception: " + e); + e.printStackTrace(); + } finally { + close(); + } + } + }); - byte[] lA = new byte[4]; - int len = 28 + packet.getLength(); - byte[] buffer = new byte[len]; + // Read incoming requests for settings socket + mThreadPool.execute(new Runnable() { + @Override + public void run() + { + byte[] inbuf = new byte[1024]; - for (int i = 3; i >= 0; i--) { - lA[i] = (byte)(len & 0xFF); - len >>= 8; + try { + while (true) { + synchronized (this) { + if (!running) { + break; + } } - byte[] header = { 0x00, 0x00, 0x00, PROTOCOL_DATA, // uint32 - protocol - 0x00, 0x00, portMSB, portLSB, // uint32 - dport - lA[0], lA[1], lA[2], lA[3], // uint32 - length - 0x00, 0x00, 0x00, 0x00, // uint32 - magic - 0x00, 0x00, 0x00, vMaj, // uint32 - version major - 0x00, 0x00, 0x00, 0x00, // uint32 - version minor - 0x00, 0x00, 0x00, 0x00 }; // uint32 - padding - - System.arraycopy(header, 0, buffer, 0, header.length); - System.arraycopy(packet.getData(), 0, buffer, header.length, packet.getLength()); - mFileOutputStream.write(buffer); + int bytesRead = tcpInStream.read(inbuf, 0, inbuf.length); + if (bytesRead > 0) { + byte[] msg = constructTaiSyncMessage(PROTOCOL_DATA, TAISYNC_SETTINGS_PORT, inbuf, bytesRead); + mFileOutputStream.write(msg); + } } } catch (IOException e) { Log.e("QGC_TaiSync", "Exception: " + e); @@ -217,24 +211,64 @@ public class TaiSync }); } + private byte[] constructTaiSyncMessage(byte protocol, int dataPort, byte[] data, int dataLen) + { + byte portMSB = (byte)((dataPort >> 8) & 0xFF); + byte portLSB = (byte)(dataPort & 0xFF); + + byte[] lA = new byte[4]; + int len = HEADER_SIZE + dataLen; + byte[] buffer = new byte[len]; + + for (int i = 3; i >= 0; i--) { + lA[i] = (byte)(len & 0xFF); + len >>= 8; + } + + byte[] header = { 0x00, 0x00, 0x00, protocol, // uint32 - protocol + 0x00, 0x00, portMSB, portLSB, // uint32 - dport + lA[0], lA[1], lA[2], lA[3], // uint32 - length + 0x00, 0x00, 0x00, 0x00, // uint32 - magic + 0x00, 0x00, 0x00, vMaj, // uint32 - version major + 0x00, 0x00, 0x00, 0x00, // uint32 - version minor + 0x00, 0x00, 0x00, 0x00 }; // uint32 - padding + + System.arraycopy(header, 0, buffer, 0, header.length); + if (data != null && dataLen > 0) { + System.arraycopy(data, 0, buffer, header.length, dataLen); + } + + return buffer; + } + public void close() { // Log.i("QGC_TaiSync", "Close"); - synchronized (this) - { + synchronized (this) { running = false; } try { - if (socket != null) { - socket.close(); - socket = null; + if (udpSocket != null) { + udpSocket.close(); + } + } catch (Exception e) { + } + try { + if (tcpSocket != null) { + tcpSocket.close(); } + } catch (Exception e) { + } + try { if (mParcelFileDescriptor != null) { mParcelFileDescriptor.close(); - mParcelFileDescriptor = null; } } catch (Exception e) { } - socket = null; + udpSocket = null; + tcpSocket = null; + tcpInStream = null; + tcpOutStream = null; + mParcelFileDescriptor = null; } } From daccbf9e21afd4836be65cdbb744a2153abd04d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= Date: Fri, 28 Dec 2018 19:09:51 +0100 Subject: [PATCH 04/10] Update VideoStreaming Readme to reflect upgrade of GStreamer to 1.14.4 --- src/VideoStreaming/README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/VideoStreaming/README.md b/src/VideoStreaming/README.md index eb8d8f4..6d14516 100644 --- a/src/VideoStreaming/README.md +++ b/src/VideoStreaming/README.md @@ -70,16 +70,19 @@ The installer places them under ~/Library/Developer/GStreamer/iPhone.sdk/GStream ### Android Binaries found in http://gstreamer.freedesktop.org/data/pkg/android -Download the [gstreamer-1.0-android-armv7-1.5.2.tar.bz2](http://gstreamer.freedesktop.org/data/pkg/android/1.5.2/gstreamer-1.0-android-armv7-1.5.2.tar.bz2) archive (assuming you want the ARM V7 platform. For x86, replace `armv7` with `x86` accordingly). +Download the [gstreamer-1.0-android-universal-1.14.4.tar.bz2](https://gstreamer.freedesktop.org/data/pkg/android/1.14.4/gstreamer-1.0-android-universal-1.14.4.tar.bz2) archive. -Create a directory named "gstreamer-1.0-android-armv7-1.5.2" under the root qgroundcontrol directory (the same directory qgroundcontrol.pro is located). Extract the gstreamer tar file under this directory. That's where the build system will look for it. Make sure your archive tool doesn't create any additional top level directories. The structure after extracting the archive should look like this: +Create a directory named "gstreamer-1.0-android-universal-1.14.4" under the root qgroundcontrol directory (the same directory qgroundcontrol.pro is located). Extract the downloaded archive under this directory. That's where the build system will look for it. Make sure your archive tool doesn't create any additional top level directories. The structure after extracting the archive should look like this: ``` qgroundcontrol -├── gstreamer-1.0-android-armv7-1.5.2 -│   ├── etc -│   ├── include -│   ├── lib -│   └── share +├── gstreamer-1.0-android-universal-1.14.4 +│ │ +│   ├──armv7 +│   │   ├── etc +│   │   ├── include +│   │   ├── lib +│   │   └── share +│   ├──x86 ``` ### Windows From b33d3c56d6b37ca6b94ea837fa5ef8aac2ba2bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= Date: Sun, 6 Jan 2019 13:29:07 +0100 Subject: [PATCH 05/10] Increase ThreadPool size to accomodate for settings thread --- android/src/org/mavlink/qgroundcontrol/TaiSync.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/org/mavlink/qgroundcontrol/TaiSync.java b/android/src/org/mavlink/qgroundcontrol/TaiSync.java index 2638507..8609461 100644 --- a/android/src/org/mavlink/qgroundcontrol/TaiSync.java +++ b/android/src/org/mavlink/qgroundcontrol/TaiSync.java @@ -47,7 +47,7 @@ public class TaiSync public TaiSync() { - mThreadPool = Executors.newFixedThreadPool(2); + mThreadPool = Executors.newFixedThreadPool(3); } public boolean isRunning() From ef75a925aff9cc5d63405aade2226b795debab96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= Date: Mon, 7 Jan 2019 11:04:47 +0100 Subject: [PATCH 06/10] Enabled TaiSync for Android & fixed race condition on USB accessory stream. --- QGCCommon.pri | 1 + android/src/org/mavlink/qgroundcontrol/TaiSync.java | 21 +++++++++------------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/QGCCommon.pri b/QGCCommon.pri index 7d98712..9c72586 100644 --- a/QGCCommon.pri +++ b/QGCCommon.pri @@ -36,6 +36,7 @@ linux { DEFINES += __android__ DEFINES += __STDC_LIMIT_MACROS DEFINES += QGC_ENABLE_BLUETOOTH + DEFINES += QGC_GST_TAISYNC_ENABLED target.path = $$DESTDIR equals(ANDROID_TARGET_ARCH, x86) { CONFIG += Androidx86Build diff --git a/android/src/org/mavlink/qgroundcontrol/TaiSync.java b/android/src/org/mavlink/qgroundcontrol/TaiSync.java index 8609461..d953088 100644 --- a/android/src/org/mavlink/qgroundcontrol/TaiSync.java +++ b/android/src/org/mavlink/qgroundcontrol/TaiSync.java @@ -84,8 +84,7 @@ public class TaiSync tcpOutStream = tcpSocket.getOutputStream(); // Request connection packet - byte[] msg = constructTaiSyncMessage(PROTOCOL_REQUEST_CONNECTION, 0, null, 0); - mFileOutputStream.write(msg); + sendTaiSyncMessage(PROTOCOL_REQUEST_CONNECTION, 0, null, 0); // Read multiplexed data stream coming from TaiSync accessory mThreadPool.execute(new Runnable() { @@ -110,15 +109,13 @@ public class TaiSync { vMaj = mBytes[19]; Log.i("QGC_TaiSync", "Got protocol version message vMaj = " + mBytes[19]); - byte[] msg = constructTaiSyncMessage(PROTOCOL_VERSION, 0, null, 0); - mFileOutputStream.write(msg); + sendTaiSyncMessage(PROTOCOL_VERSION, 0, null, 0); } else if (mBytes[3] == PROTOCOL_CHANNEL) { int dPort = ((mBytes[4] & 0xff)<< 24) | ((mBytes[5]&0xff) << 16) | ((mBytes[6]&0xff) << 8) | (mBytes[7] &0xff); int dLength = ((mBytes[8] & 0xff)<< 24) | ((mBytes[9]&0xff) << 16) | ((mBytes[10]&0xff) << 8) | (mBytes[11] &0xff); Log.i("QGC_TaiSync", "Read 2 port = " + dPort + " length = " + dLength); - byte[] msg = constructTaiSyncMessage(PROTOCOL_CHANNEL, dPort, null, 0); - mFileOutputStream.write(msg); + sendTaiSyncMessage(PROTOCOL_CHANNEL, dPort, null, 0); } else if (mBytes[3] == PROTOCOL_DATA) { int dPort = ((mBytes[4] & 0xff)<< 24) | ((mBytes[5]&0xff) << 16) | ((mBytes[6]&0xff) << 8) | (mBytes[7] &0xff); @@ -168,8 +165,7 @@ public class TaiSync DatagramPacket packet = new DatagramPacket(inbuf, inbuf.length); udpSocket.receive(packet); - byte[] msg = constructTaiSyncMessage(PROTOCOL_DATA, TAISYNC_TELEMETRY_PORT, packet.getData(), packet.getLength()); - mFileOutputStream.write(msg); + sendTaiSyncMessage(PROTOCOL_DATA, TAISYNC_TELEMETRY_PORT, packet.getData(), packet.getLength()); } } catch (IOException e) { Log.e("QGC_TaiSync", "Exception: " + e); @@ -197,8 +193,7 @@ public class TaiSync int bytesRead = tcpInStream.read(inbuf, 0, inbuf.length); if (bytesRead > 0) { - byte[] msg = constructTaiSyncMessage(PROTOCOL_DATA, TAISYNC_SETTINGS_PORT, inbuf, bytesRead); - mFileOutputStream.write(msg); + sendTaiSyncMessage(PROTOCOL_DATA, TAISYNC_SETTINGS_PORT, inbuf, bytesRead); } } } catch (IOException e) { @@ -211,7 +206,7 @@ public class TaiSync }); } - private byte[] constructTaiSyncMessage(byte protocol, int dataPort, byte[] data, int dataLen) + private void sendTaiSyncMessage(byte protocol, int dataPort, byte[] data, int dataLen) throws IOException { byte portMSB = (byte)((dataPort >> 8) & 0xFF); byte portLSB = (byte)(dataPort & 0xFF); @@ -238,7 +233,9 @@ public class TaiSync System.arraycopy(data, 0, buffer, header.length, dataLen); } - return buffer; + synchronized (this) { + mFileOutputStream.write(buffer); + } } public void close() From 05e3abe83002cb2d61f2262b2ee3d56e09a32357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= Date: Mon, 7 Jan 2019 14:32:56 +0100 Subject: [PATCH 07/10] Change device filter to allow only Taisync USB accessory --- android/res/xml/device_filter.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/res/xml/device_filter.xml b/android/res/xml/device_filter.xml index 3147457..3d5af12 100644 --- a/android/res/xml/device_filter.xml +++ b/android/res/xml/device_filter.xml @@ -2,6 +2,6 @@ - + From f90b91fe9ae05a11bdf28e6f5a58cc9c61b6f361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= Date: Wed, 9 Jan 2019 13:07:40 +0100 Subject: [PATCH 08/10] Taisync android - send telemetry messages to TCP port 8200 instead to UDP 14550. --- .../src/org/mavlink/qgroundcontrol/TaiSync.java | 53 ++++++++++++++-------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/android/src/org/mavlink/qgroundcontrol/TaiSync.java b/android/src/org/mavlink/qgroundcontrol/TaiSync.java index d953088..fb1e7af 100644 --- a/android/src/org/mavlink/qgroundcontrol/TaiSync.java +++ b/android/src/org/mavlink/qgroundcontrol/TaiSync.java @@ -27,17 +27,19 @@ public class TaiSync private static final byte PROTOCOL_CHANNEL = 0x02; private static final byte PROTOCOL_DATA = 0x03; - private static final int TELEM_PORT = 14550; - private static final int VIDEO_PORT = 5000; + private static final int VIDEO_PORT = 5600; private static final int TAISYNC_VIDEO_PORT = 8000; private static final int TAISYNC_SETTINGS_PORT = 8200; private static final int TAISYNC_TELEMETRY_PORT = 8400; private boolean running = false; private DatagramSocket udpSocket = null; - private Socket tcpSocket = null; - private InputStream tcpInStream = null; - private OutputStream tcpOutStream = null; + private Socket tcpSettingsSocket = null; + private InputStream settingsInStream = null; + private OutputStream settingsOutStream = null; + private Socket tcpTelemetrySocket = null; + private InputStream telemetryInStream = null; + private OutputStream telemetryOutStream = null; private ParcelFileDescriptor mParcelFileDescriptor; private FileInputStream mFileInputStream; private FileOutputStream mFileOutputStream; @@ -79,9 +81,12 @@ public class TaiSync udpSocket = new DatagramSocket(); final InetAddress address = InetAddress.getByName("localhost"); - tcpSocket = new Socket(address, TAISYNC_SETTINGS_PORT); - tcpInStream = tcpSocket.getInputStream(); - tcpOutStream = tcpSocket.getOutputStream(); + tcpTelemetrySocket = new Socket(address, TAISYNC_TELEMETRY_PORT); + telemetryInStream = tcpTelemetrySocket.getInputStream(); + telemetryOutStream = tcpTelemetrySocket.getOutputStream(); + tcpSettingsSocket = new Socket(address, TAISYNC_SETTINGS_PORT); + settingsInStream = tcpSettingsSocket.getInputStream(); + settingsOutStream = tcpSettingsSocket.getOutputStream(); // Request connection packet sendTaiSyncMessage(PROTOCOL_REQUEST_CONNECTION, 0, null, 0); @@ -131,10 +136,9 @@ public class TaiSync DatagramPacket packet = new DatagramPacket(sBytes, sBytes.length, address, VIDEO_PORT); udpSocket.send(packet); } else if (dPort == TAISYNC_SETTINGS_PORT) { - tcpOutStream.write(sBytes); + settingsOutStream.write(sBytes); } else if (dPort == TAISYNC_TELEMETRY_PORT) { - DatagramPacket packet = new DatagramPacket(sBytes, sBytes.length, address, TELEM_PORT); - udpSocket.send(packet); + telemetryOutStream.write(sBytes); } } } @@ -163,9 +167,10 @@ public class TaiSync } } - DatagramPacket packet = new DatagramPacket(inbuf, inbuf.length); - udpSocket.receive(packet); - sendTaiSyncMessage(PROTOCOL_DATA, TAISYNC_TELEMETRY_PORT, packet.getData(), packet.getLength()); + int bytesRead = telemetryInStream.read(inbuf, 0, inbuf.length); + if (bytesRead > 0) { + sendTaiSyncMessage(PROTOCOL_DATA, TAISYNC_TELEMETRY_PORT, inbuf, bytesRead); + } } } catch (IOException e) { Log.e("QGC_TaiSync", "Exception: " + e); @@ -191,7 +196,7 @@ public class TaiSync } } - int bytesRead = tcpInStream.read(inbuf, 0, inbuf.length); + int bytesRead = settingsInStream.read(inbuf, 0, inbuf.length); if (bytesRead > 0) { sendTaiSyncMessage(PROTOCOL_DATA, TAISYNC_SETTINGS_PORT, inbuf, bytesRead); } @@ -213,6 +218,7 @@ public class TaiSync byte[] lA = new byte[4]; int len = HEADER_SIZE + dataLen; + Log.i("QGC_TaiSync", "Sending to " + dataPort + " length = " + len); byte[] buffer = new byte[len]; for (int i = 3; i >= 0; i--) { @@ -251,8 +257,14 @@ public class TaiSync } catch (Exception e) { } try { - if (tcpSocket != null) { - tcpSocket.close(); + if (tcpTelemetrySocket != null) { + tcpTelemetrySocket.close(); + } + } catch (Exception e) { + } + try { + if (tcpSettingsSocket != null) { + tcpSettingsSocket.close(); } } catch (Exception e) { } @@ -263,9 +275,10 @@ public class TaiSync } catch (Exception e) { } udpSocket = null; - tcpSocket = null; - tcpInStream = null; - tcpOutStream = null; + tcpSettingsSocket = null; + tcpTelemetrySocket = null; + settingsInStream = null; + settingsOutStream = null; mParcelFileDescriptor = null; } } From 28272aa63441430ff8be0b83d4312eea7a20b8d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= Date: Thu, 17 Jan 2019 13:11:17 +0100 Subject: [PATCH 09/10] Change synchronization object --- android/src/org/mavlink/qgroundcontrol/TaiSync.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/android/src/org/mavlink/qgroundcontrol/TaiSync.java b/android/src/org/mavlink/qgroundcontrol/TaiSync.java index fb1e7af..1d746c9 100644 --- a/android/src/org/mavlink/qgroundcontrol/TaiSync.java +++ b/android/src/org/mavlink/qgroundcontrol/TaiSync.java @@ -32,6 +32,7 @@ public class TaiSync private static final int TAISYNC_SETTINGS_PORT = 8200; private static final int TAISYNC_TELEMETRY_PORT = 8400; + private Object runLock; private boolean running = false; private DatagramSocket udpSocket = null; private Socket tcpSettingsSocket = null; @@ -49,12 +50,13 @@ public class TaiSync public TaiSync() { + runLock = new Object(); mThreadPool = Executors.newFixedThreadPool(3); } public boolean isRunning() { - synchronized (this) { + synchronized (runLock) { return running; } } @@ -63,7 +65,7 @@ public class TaiSync { // Log.i("QGC_TaiSync", "Open"); - synchronized (this) { + synchronized (runLock) { if (running) { return; } @@ -100,7 +102,7 @@ public class TaiSync try { while (bytesRead >= 0) { - synchronized (this) { + synchronized (runLock) { if (!running) { break; } @@ -161,7 +163,7 @@ public class TaiSync try { while (true) { - synchronized (this) { + synchronized (runLock) { if (!running) { break; } @@ -190,7 +192,7 @@ public class TaiSync try { while (true) { - synchronized (this) { + synchronized (runLock) { if (!running) { break; } @@ -209,7 +211,7 @@ public class TaiSync } } }); - } + } private void sendTaiSyncMessage(byte protocol, int dataPort, byte[] data, int dataLen) throws IOException { @@ -239,7 +241,7 @@ public class TaiSync System.arraycopy(data, 0, buffer, header.length, dataLen); } - synchronized (this) { + synchronized (runLock) { mFileOutputStream.write(buffer); } } @@ -247,7 +249,7 @@ public class TaiSync public void close() { // Log.i("QGC_TaiSync", "Close"); - synchronized (this) { + synchronized (runLock) { running = false; } try { From 868b12c9aaa0a960245f51a1c52e796223691ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= Date: Tue, 5 Feb 2019 23:19:08 +0100 Subject: [PATCH 10/10] Fixed merge --- .../org/mavlink/qgroundcontrol/QGCActivity.java | 93 ++++------------------ 1 file changed, 14 insertions(+), 79 deletions(-) diff --git a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java index 70db6c0..f76242f 100644 --- a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java +++ b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java @@ -1,4 +1,4 @@ - +package org.mavlink.qgroundcontrol; /* Copyright 2013 Google Inc. * @@ -62,18 +62,6 @@ import org.qtproject.qt5.android.bindings.QtApplication; public class QGCActivity extends QtActivity { - public static int BAD_PORT = 0; - private static QGCActivity m_instance; - private static UsbManager m_manager; // ANDROID USB HOST CLASS - private static List m_devices; // LIST OF CURRENT DEVICES - private static HashMap m_openedDevices; // LIST OF OPENED DEVICES - private static HashMap m_ioManager; // THREADS FOR LISTENING FOR INCOMING DATA - private static HashMap m_userData; // CORRESPONDING USER DATA FOR OPENED DEVICES. USED IN DISCONNECT CALLBACK - // USED TO DETECT WHEN A DEVICE HAS BEEN UNPLUGGED - private BroadcastReceiver m_UsbReceiver = null; - private final static ExecutorService m_Executor = Executors.newSingleThreadExecutor(); - private static final String TAG = "QGC_QGCActivity"; - private static PowerManager.WakeLock m_wl; public static int BAD_DEVICE_ID = 0; private static QGCActivity _instance = null; private static UsbManager _usbManager = null; @@ -108,10 +96,6 @@ public class QGCActivity extends QtActivity } }; - // NATIVE C++ FUNCTION THAT WILL BE CALLED IF THE DEVICE IS UNPLUGGED - private static native void nativeDeviceHasDisconnected(int userDataA); - private static native void nativeDeviceException(int userDataA, String messageA); - private static native void nativeDeviceNewData(int userDataA, byte[] dataA); private final BroadcastReceiver mOpenAccessoryReceiver = new BroadcastReceiver() { @@ -210,12 +194,11 @@ public class QGCActivity extends QtActivity } else { Log.i(TAG, "SCREEN_BRIGHT_WAKE_LOCK not acquired!!!"); } - m_instance.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); _instance.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); _usbManager = (UsbManager)_instance.getSystemService(Context.USB_SERVICE); - // Register for USB Detach and USB Permission intent + // Register for USB Detach and USB Permission intent IntentFilter filter = new IntentFilter(); filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); filter.addAction(ACTION_USB_PERMISSION); @@ -224,24 +207,20 @@ public class QGCActivity extends QtActivity // Create intent for usb permission request _usbPermissionIntent = PendingIntent.getBroadcast(_instance, 0, new Intent(ACTION_USB_PERMISSION), 0); - if (m_manager == null) { - try { - m_manager = (UsbManager)m_instance.getSystemService(Context.USB_SERVICE); - taiSync = new TaiSync(); + try { + taiSync = new TaiSync(); - IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); - filter.addAction( UsbManager.ACTION_USB_ACCESSORY_DETACHED); - registerReceiver(mOpenAccessoryReceiver, filter); + IntentFilter accessoryFilter = new IntentFilter(ACTION_USB_PERMISSION); + filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED); + registerReceiver(mOpenAccessoryReceiver, accessoryFilter); - probeAccessories(); - } catch(Exception e) { - Log.e(TAG, "Exception getCurrentDevices(): " + e); - } + probeAccessories(); + } catch(Exception e) { + Log.e(TAG, "Exception: " + e); } } @Override - protected void onDestroy() { protected void onDestroy() { unregisterReceiver(mOpenAccessoryReceiver); @@ -263,11 +242,6 @@ public class QGCActivity extends QtActivity { List currentDrivers = UsbSerialProber.findAllDevices(_usbManager); - if (m_manager == null) - m_manager = (UsbManager)m_instance.getSystemService(Context.USB_SERVICE); - - if (m_devices != null) - m_devices.clear(); // Remove stale drivers for (int i=_drivers.size()-1; i>=0; i--) { boolean found = false; @@ -331,13 +305,6 @@ public class QGCActivity extends QtActivity String deviceInfo; UsbSerialDriver driver = _drivers.get(i); - // CHECK FOR ALREADY OPENED DEVICES AND DON"T INCLUDE THEM IN THE COUNT - for (iL=0; iL