diff --git a/Android/BebopDroneStreaming/.gitignore b/Android/BebopDroneStreaming/.gitignore new file mode 100644 index 0000000..afbdab3 --- /dev/null +++ b/Android/BebopDroneStreaming/.gitignore @@ -0,0 +1,6 @@ +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build diff --git a/Android/BebopDroneStreaming/.idea/.name b/Android/BebopDroneStreaming/.idea/.name new file mode 100644 index 0000000..7674633 --- /dev/null +++ b/Android/BebopDroneStreaming/.idea/.name @@ -0,0 +1 @@ +BebopDroneStreaming \ No newline at end of file diff --git a/Android/BebopDroneStreaming/.idea/compiler.xml b/Android/BebopDroneStreaming/.idea/compiler.xml new file mode 100644 index 0000000..96cc43e --- /dev/null +++ b/Android/BebopDroneStreaming/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Android/BebopDroneStreaming/.idea/copyright/profiles_settings.xml b/Android/BebopDroneStreaming/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/Android/BebopDroneStreaming/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Android/BebopDroneStreaming/.idea/gradle.xml b/Android/BebopDroneStreaming/.idea/gradle.xml new file mode 100644 index 0000000..8d2df47 --- /dev/null +++ b/Android/BebopDroneStreaming/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/Android/BebopDroneStreaming/.idea/misc.xml b/Android/BebopDroneStreaming/.idea/misc.xml new file mode 100644 index 0000000..e4a5fa4 --- /dev/null +++ b/Android/BebopDroneStreaming/.idea/misc.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Android/BebopDroneStreaming/.idea/modules.xml b/Android/BebopDroneStreaming/.idea/modules.xml new file mode 100644 index 0000000..86b71fe --- /dev/null +++ b/Android/BebopDroneStreaming/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/Android/BebopDroneStreaming/.idea/vcs.xml b/Android/BebopDroneStreaming/.idea/vcs.xml new file mode 100644 index 0000000..6564d52 --- /dev/null +++ b/Android/BebopDroneStreaming/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Android/BebopDroneStreaming/BebopDroneStreaming.iml b/Android/BebopDroneStreaming/BebopDroneStreaming.iml new file mode 100644 index 0000000..a358558 --- /dev/null +++ b/Android/BebopDroneStreaming/BebopDroneStreaming.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Android/BebopDroneStreaming/README b/Android/BebopDroneStreaming/README new file mode 100644 index 0000000..6f2420e --- /dev/null +++ b/Android/BebopDroneStreaming/README @@ -0,0 +1,10 @@ +================== +Purpose of BebopDroneStreaming +================== + +This sample shows how to receive video stream from a Bebop Drone, decode it and display the decoded stream, as well as to send and receive commands to communicate with a Parrot Bebop drone on an Android device. + +This example enables you to **discover** and **connect** to a Bebop and **display the video stream** and **send and receive commands** to pilot it and get its battery level. + +These sample sources are an Android gradle project building for Android devices. + diff --git a/Android/BebopDroneStreaming/README~ b/Android/BebopDroneStreaming/README~ new file mode 100644 index 0000000..8cd0558 --- /dev/null +++ b/Android/BebopDroneStreaming/README~ @@ -0,0 +1,10 @@ +================== +Purpose of BebopDronePiloting +================== + +This sample shows how to send and receive commands to communicate with a Parrot Bebop drone on an Android device. + +This example enables you to **discover** and **connect** to a Bebop and **send and receive commands** to pilot it and get its battery level. + +These sample sources are an Android gradle project building for Android devices. + diff --git a/Android/BebopDroneStreaming/app/.gitignore b/Android/BebopDroneStreaming/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/Android/BebopDroneStreaming/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/Android/BebopDroneStreaming/app/AndroidManifest.xml b/Android/BebopDroneStreaming/app/AndroidManifest.xml new file mode 100644 index 0000000..ff52214 --- /dev/null +++ b/Android/BebopDroneStreaming/app/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Android/BebopDroneStreaming/app/AndroidManifest.xml~ b/Android/BebopDroneStreaming/app/AndroidManifest.xml~ new file mode 100644 index 0000000..a1dba21 --- /dev/null +++ b/Android/BebopDroneStreaming/app/AndroidManifest.xml~ @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Android/BebopDroneStreaming/app/app.iml b/Android/BebopDroneStreaming/app/app.iml new file mode 100644 index 0000000..044469a --- /dev/null +++ b/Android/BebopDroneStreaming/app/app.iml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Android/BebopDroneStreaming/app/build.gradle b/Android/BebopDroneStreaming/app/build.gradle new file mode 100644 index 0000000..29b846f --- /dev/null +++ b/Android/BebopDroneStreaming/app/build.gradle @@ -0,0 +1,34 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.1" + defaultConfig { + applicationId 'com.parrot.bebopsample' + minSdkVersion 15 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + productFlavors { + } +} + +dependencies { + compile fileTree(include: ['*.jar'], dir: 'libs') + compile 'com.android.support:appcompat-v7:21.0.3' + + compile group: 'org.bytedeco', name: 'javacv', version: '0.11' + compile group: 'org.bytedeco', name: 'javacpp', version: '0.11' + compile group: 'org.bytedeco.javacpp-presets', name: 'opencv', version: '2.4.11-0.11', classifier: 'android-arm' + compile group: 'org.bytedeco.javacpp-presets', name: 'opencv', version: '2.4.11-0.11', classifier: 'android-x86' + compile group: 'org.bytedeco.javacpp-presets', name: 'ffmpeg', version: '2.6.1-0.11', classifier: 'android-arm' + compile group: 'org.bytedeco.javacpp-presets', name: 'ffmpeg', version: '2.6.1-0.11', classifier: 'android-x86' + +} diff --git a/Android/BebopDroneStreaming/app/libs/jmdns.jar b/Android/BebopDroneStreaming/app/libs/jmdns.jar new file mode 120000 index 0000000..ec3a97f --- /dev/null +++ b/Android/BebopDroneStreaming/app/libs/jmdns.jar @@ -0,0 +1 @@ +../../../../../ARSDKBuildUtils/Targets/Android/Install/jars/release/jmdns.jar \ No newline at end of file diff --git a/Android/BebopDroneStreaming/app/libs/libARCommands.jar b/Android/BebopDroneStreaming/app/libs/libARCommands.jar new file mode 120000 index 0000000..684b99e --- /dev/null +++ b/Android/BebopDroneStreaming/app/libs/libARCommands.jar @@ -0,0 +1 @@ +../../../../../ARSDKBuildUtils/Targets/Android/Install/jars/release/libARCommands.jar \ No newline at end of file diff --git a/Android/BebopDroneStreaming/app/libs/libARDiscovery.jar b/Android/BebopDroneStreaming/app/libs/libARDiscovery.jar new file mode 120000 index 0000000..12be583 --- /dev/null +++ b/Android/BebopDroneStreaming/app/libs/libARDiscovery.jar @@ -0,0 +1 @@ +../../../../../ARSDKBuildUtils/Targets/Android/Install/jars/release/libARDiscovery.jar \ No newline at end of file diff --git a/Android/BebopDroneStreaming/app/libs/libARNetwork.jar b/Android/BebopDroneStreaming/app/libs/libARNetwork.jar new file mode 120000 index 0000000..72d88dd --- /dev/null +++ b/Android/BebopDroneStreaming/app/libs/libARNetwork.jar @@ -0,0 +1 @@ +../../../../../ARSDKBuildUtils/Targets/Android/Install/jars/release/libARNetwork.jar \ No newline at end of file diff --git a/Android/BebopDroneStreaming/app/libs/libARNetworkAL.jar b/Android/BebopDroneStreaming/app/libs/libARNetworkAL.jar new file mode 120000 index 0000000..57b572f --- /dev/null +++ b/Android/BebopDroneStreaming/app/libs/libARNetworkAL.jar @@ -0,0 +1 @@ +../../../../../ARSDKBuildUtils/Targets/Android/Install/jars/release/libARNetworkAL.jar \ No newline at end of file diff --git a/Android/BebopDroneStreaming/app/libs/libARSAL.jar b/Android/BebopDroneStreaming/app/libs/libARSAL.jar new file mode 120000 index 0000000..1850e34 --- /dev/null +++ b/Android/BebopDroneStreaming/app/libs/libARSAL.jar @@ -0,0 +1 @@ +../../../../../ARSDKBuildUtils/Targets/Android/Install/jars/release/libARSAL.jar \ No newline at end of file diff --git a/Android/BebopDroneStreaming/app/libs/libARStream.jar b/Android/BebopDroneStreaming/app/libs/libARStream.jar new file mode 100644 index 0000000..855ed6e Binary files /dev/null and b/Android/BebopDroneStreaming/app/libs/libARStream.jar differ diff --git a/Android/BebopDronePiloting/app/libs/libjson.jar b/Android/BebopDroneStreaming/app/libs/libjson.jar similarity index 100% rename from Android/BebopDronePiloting/app/libs/libjson.jar rename to Android/BebopDroneStreaming/app/libs/libjson.jar diff --git a/Android/BebopDroneStreaming/app/proguard-rules.pro b/Android/BebopDroneStreaming/app/proguard-rules.pro new file mode 100644 index 0000000..73c5b89 --- /dev/null +++ b/Android/BebopDroneStreaming/app/proguard-rules.pro @@ -0,0 +1,45 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /home/m_maitre/dev/sdk/adt-bundle-linux-x86/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +-keepattributes *Annotation* + +# JavaCV +-keep @org.bytedeco.javacpp.annotation interface * { + *; +} + +-keep @org.bytedeco.javacpp.annotation.Platform public class * + +-keepclasseswithmembernames class * { + @org.bytedeco.* ; +} + +-keepclasseswithmembernames class * { + @org.bytedeco.* ; +} + +-keepattributes EnclosingMethod +-keep @interface org.bytedeco.javacpp.annotation.*,javax.inject.* + +-keepattributes *Annotation*, Exceptions, Signature, Deprecated, SourceFile, SourceDir, LineNumberTable, LocalVariableTable, LocalVariableTypeTable, Synthetic, EnclosingMethod, RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations, RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations, AnnotationDefault, InnerClasses +-keep class org.bytedeco.javacpp.** {*;} +-dontwarn java.awt.** +-dontwarn org.bytedeco.javacv.** +-dontwarn org.bytedeco.javacpp.** + +# end javacv \ No newline at end of file diff --git a/Android/BebopDroneStreaming/app/src/main/AndroidManifest.xml b/Android/BebopDroneStreaming/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1bc663b --- /dev/null +++ b/Android/BebopDroneStreaming/app/src/main/AndroidManifest.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Android/BebopDroneStreaming/app/src/main/AndroidManifest.xml~ b/Android/BebopDroneStreaming/app/src/main/AndroidManifest.xml~ new file mode 100644 index 0000000..8fd72b6 --- /dev/null +++ b/Android/BebopDroneStreaming/app/src/main/AndroidManifest.xml~ @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/DeviceController.java b/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/DeviceController.java new file mode 100644 index 0000000..a6219b4 --- /dev/null +++ b/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/DeviceController.java @@ -0,0 +1,1245 @@ +package com.parrot.bebopdronestreaming; + + +import android.os.SystemClock; +import android.util.Log; +import android.graphics.Bitmap; + +import com.parrot.arsdk.arcommands.ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM; +import com.parrot.arsdk.arcommands.ARCOMMANDS_DECODER_ERROR_ENUM; +import com.parrot.arsdk.arcommands.ARCOMMANDS_GENERATOR_ERROR_ENUM; +import com.parrot.arsdk.arcommands.ARCommand; +import com.parrot.arsdk.arcommands.ARCommandARDrone3PilotingStateFlyingStateChangedListener; +import com.parrot.arsdk.arcommands.ARCommandCommonCommonStateAllStatesChangedListener; +import com.parrot.arsdk.arcommands.ARCommandCommonCommonStateBatteryStateChangedListener; +import com.parrot.arsdk.arcommands.ARCommandCommonSettingsStateAllSettingsChangedListener; +import com.parrot.arsdk.ardiscovery.ARDISCOVERY_ERROR_ENUM; +import com.parrot.arsdk.ardiscovery.ARDiscoveryConnection; +import com.parrot.arsdk.ardiscovery.ARDiscoveryDeviceBLEService; +import com.parrot.arsdk.ardiscovery.ARDiscoveryDeviceNetService; +import com.parrot.arsdk.ardiscovery.ARDiscoveryDeviceService; +import com.parrot.arsdk.arnetwork.ARNETWORK_ERROR_ENUM; +import com.parrot.arsdk.arnetwork.ARNETWORK_MANAGER_CALLBACK_RETURN_ENUM; +import com.parrot.arsdk.arnetwork.ARNETWORK_MANAGER_CALLBACK_STATUS_ENUM; +import com.parrot.arsdk.arnetwork.ARNetworkIOBufferParam; +import com.parrot.arsdk.arnetwork.ARNetworkManager; +import com.parrot.arsdk.arnetworkal.ARNETWORKAL_ERROR_ENUM; +import com.parrot.arsdk.arnetworkal.ARNETWORKAL_FRAME_TYPE_ENUM; +import com.parrot.arsdk.arnetworkal.ARNetworkALManager; +import com.parrot.arsdk.arsal.ARNativeData; +import com.parrot.arsdk.arsal.ARSALPrint; +/*** video mods start ***/ +import com.parrot.arsdk.arstream.ARStreamReader; +import com.parrot.bebopdronestreaming.video.ARStreamManager; +/*** video mods end ***/ + +import org.json.JSONException; +import org.json.JSONObject; + +import java.sql.Date; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.Semaphore; + +public class DeviceController implements ARCommandCommonCommonStateBatteryStateChangedListener, ARCommandCommonSettingsStateAllSettingsChangedListener, ARCommandCommonCommonStateAllStatesChangedListener, ARCommandARDrone3PilotingStateFlyingStateChangedListener +{ + /*** video mods start ***/ + private static final int DEFAULT_VIDEO_FRAGMENT_SIZE = 1000; + private static final int DEFAULT_VIDEO_FRAGMENT_MAXIMUM_NUMBER = 128; + private static final int VIDEO_RECEIVE_TIMEOUT= 500; + /*** video mods end ***/ + + private static String TAG = "DeviceController"; + + private static final int PCMD_LOOP_IN_MS = 25; + + private final static int iobufferC2dNack = 10; + private final static int iobufferC2dAck = 11; + private final static int iobufferC2dEmergency = 12; + private final static int iobufferD2cNavdata = 127; + private final static int iobufferD2cEvents = 126; + /*** video mods start ***/ + // from BebopDroneReceiveStream + //private final static int iobufferC2dVideoAck = 13; + //private final static int iobufferD2cVideoData = 125; + //private final static int iobufferD2cVideoFragSize = 1000; //DEFAULT_VIDEO_FRAGMENT_SIZE + //private final static int iobufferD2cVideoMaxNumberOfFrag = 128; //DEFAULT_VIDEO_FRAGMENT_MAXIMUM_NUMBER + private final static boolean hasVideo = true; + //private final static int videoMaxAckInterval = ARStreamReader.DEFAULT_MAX_ACK_INTERVAL; + + // from ARNetworkConfig + private int iobufferD2cArstreamData = 125; + private int iobufferC2dArstreamAck = 13; + private int videoFragmentSize = DEFAULT_VIDEO_FRAGMENT_SIZE; + private int videoFragmentMaximumNumber = DEFAULT_VIDEO_FRAGMENT_MAXIMUM_NUMBER; + //private int videoMaxAckInterval = ARStreamReader.DEFAULT_MAX_ACK_INTERVAL; + private int videoMaxAckInterval = 5; + + /*** video mods end ***/ + + protected static List c2dParams = new ArrayList(); + protected static List d2cParams = new ArrayList(); + protected static int commandsBuffers[] = {}; + + private android.content.Context context; + + private ARNetworkALManager alManager; + private ARNetworkManager netManager; + private boolean mediaOpened; + + private int c2dPort; // read from json + private int d2cPort = 43210; // set by the app, sent through json + private Thread rxThread; + private Thread txThread; + + private List readerThreads; + private Semaphore discoverSemaphore; + private ARDiscoveryConnection discoveryData; + + private LooperThread looperThread; + /*** video mods start ***/ + private VideoThread videoThread; + /*** video mods end ***/ + + private DataPCMD dataPCMD; + private ARDiscoveryDeviceService deviceService; + + private DeviceControllerListener listener; + + private Semaphore cmdGetAllSettingsSent; + private Semaphore cmdGetAllStatesSent; + + + static + { + c2dParams.clear(); + c2dParams.add (new ARNetworkIOBufferParam (iobufferC2dNack, + ARNETWORKAL_FRAME_TYPE_ENUM.ARNETWORKAL_FRAME_TYPE_DATA, + 20, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_INFINITE_NUMBER, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_INFINITE_NUMBER, + 1, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_DATACOPYMAXSIZE_USE_MAX, + true)); + c2dParams.add (new ARNetworkIOBufferParam (iobufferC2dAck, + ARNETWORKAL_FRAME_TYPE_ENUM.ARNETWORKAL_FRAME_TYPE_DATA_WITH_ACK, + 20, + 500, + 3, + 20, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_DATACOPYMAXSIZE_USE_MAX, + false)); + c2dParams.add (new ARNetworkIOBufferParam (iobufferC2dEmergency, + ARNETWORKAL_FRAME_TYPE_ENUM.ARNETWORKAL_FRAME_TYPE_DATA_WITH_ACK, + 1, + 100, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_INFINITE_NUMBER, + 1, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_DATACOPYMAXSIZE_USE_MAX, + false)); + + d2cParams.clear(); + d2cParams.add (new ARNetworkIOBufferParam (iobufferD2cNavdata, + ARNETWORKAL_FRAME_TYPE_ENUM.ARNETWORKAL_FRAME_TYPE_DATA, + 20, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_INFINITE_NUMBER, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_INFINITE_NUMBER, + 20, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_DATACOPYMAXSIZE_USE_MAX, + false)); + d2cParams.add (new ARNetworkIOBufferParam (iobufferD2cEvents, + ARNETWORKAL_FRAME_TYPE_ENUM.ARNETWORKAL_FRAME_TYPE_DATA_WITH_ACK, + 20, + 500, + 3, + 20, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_DATACOPYMAXSIZE_USE_MAX, + false)); + + commandsBuffers = new int[] { + iobufferD2cNavdata, + iobufferD2cEvents, + }; + + } + + public DeviceController (android.content.Context context, ARDiscoveryDeviceService service) + { + dataPCMD = new DataPCMD(); + deviceService = service; + this.context = context; + readerThreads = new ArrayList(); + cmdGetAllSettingsSent = new Semaphore (0); + cmdGetAllStatesSent = new Semaphore (0); + } + + public boolean start() + { + Log.d(TAG, "start ..."); + + boolean failed = false; + + registerARCommandsListener (); + + failed = startNetwork(); + + if (!failed) + { + /* start the reader threads */ + startReadThreads(); + } + + /*** video mods start ***/ + if (!failed) + { + /* start video thread */ + startVideoThread(); + } + /*** video mods end ***/ + + if (!failed) + { + /* start the looper thread */ + startLooperThread(); + } + + // send date + Date currentDate = new Date(System.currentTimeMillis()); + if (!failed) + { + failed = !sendDate(currentDate); + } + + if (!failed) + { + failed = !sendTime(currentDate); + } + + // get all settings + if (!failed) + { + failed = !getInitialSettings(); + } + + // get all states + if (!failed) + { + failed = !getInitialStates(); + } + + if (!failed) + { + failed = !sendBeginStream(); + } + + return failed; + } + + public void stop() + { + Log.d(TAG, "stop ..."); + + unregisterARCommandsListener(); + + /* Cancel the looper thread and block until it is stopped. */ + stopLooperThread(); + + /* cancel all reader threads and block until they are all stopped. */ + stopReaderThreads(); + + /* Stop the video streamer */ + stopVideoThread(); + + /* ARNetwork cleanup */ + stopNetwork(); + + } + + private boolean startNetwork() + { + ARNETWORKAL_ERROR_ENUM netALError = ARNETWORKAL_ERROR_ENUM.ARNETWORKAL_OK; + boolean failed = false; + int pingDelay = 0; /* 0 means default, -1 means no ping */ + + /* Create the looper ARNetworkALManager */ + alManager = new ARNetworkALManager(); + + + /* setup ARNetworkAL for Wifi */ + ARDiscoveryDeviceNetService netDeviceService = (ARDiscoveryDeviceNetService) deviceService.getDevice(); + + if (!ardiscoveryConnect(netDeviceService.getIp(), netDeviceService.getPort())) + { + failed = true; + } + + /*** video mods start ***/ + /* TODO : if ardiscoveryConnect ok */ + //netConfig.addStreamReader(videoFragmentSize, videoFragmentMaximumNumber); + /* add the Stream parameters for the new connection */ + c2dParams.add (ARStreamReader.newAckARNetworkIOBufferParam(iobufferC2dArstreamAck)); + d2cParams.add (ARStreamReader.newDataARNetworkIOBufferParam (iobufferD2cArstreamData, videoFragmentSize, videoFragmentMaximumNumber)); + /*** video mods end ***/ + + /* setup ARNetworkAL for wifi */ + netALError = alManager.initWifiNetwork(netDeviceService.getIp(), c2dPort, d2cPort, 3); + //netALError = alManager.initWNetwork(context, bleDevice.getBluetoothDevice(), 1, bleNotificationIDs); + + if (netALError == ARNETWORKAL_ERROR_ENUM.ARNETWORKAL_OK) + { + mediaOpened = true; + } + else + { + ARSALPrint.e(TAG, "error occured: " + netALError.toString()); + failed = true; + } + + if (failed == false) + { + /* Create the ARNetworkManager */ + netManager = new ARNetworkManagerExtend(alManager, c2dParams.toArray(new ARNetworkIOBufferParam[c2dParams.size()]), d2cParams.toArray(new ARNetworkIOBufferParam[d2cParams.size()]), pingDelay); + + if (netManager.isCorrectlyInitialized() == false) + { + ARSALPrint.e (TAG, "new ARNetworkManager failed"); + failed = true; + } + } + + if (failed == false) + { + /* Create and start Tx and Rx threads */ + rxThread = new Thread (netManager.m_receivingRunnable); + rxThread.start(); + + txThread = new Thread (netManager.m_sendingRunnable); + txThread.start(); + } + + return failed; + } + + private boolean ardiscoveryConnect(String ip, int port) + { + boolean ok = true; + ARDISCOVERY_ERROR_ENUM error = ARDISCOVERY_ERROR_ENUM.ARDISCOVERY_OK; + discoverSemaphore = new Semaphore (0); + + discoveryData = new ARDiscoveryConnection() + { + @Override + public String onSendJson () + { + /* send a json with the Device to controller port */ + JSONObject jsonObject = new JSONObject(); + + try + { + jsonObject.put(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_D2CPORT_KEY, d2cPort); + } + catch (JSONException e) + { + e.printStackTrace(); + } + try + { + Log.e(TAG, "android.os.Build.MODEL: "+android.os.Build.MODEL); + jsonObject.put(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_CONTROLLER_NAME_KEY, android.os.Build.MODEL); + } + catch (JSONException e) + { + e.printStackTrace(); + } + try + { + Log.e(TAG, "android.os.Build.DEVICE: " + android.os.Build.DEVICE); + jsonObject.put(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_CONTROLLER_TYPE_KEY, android.os.Build.DEVICE); + } + catch (JSONException e) + { + e.printStackTrace(); + } + + return jsonObject.toString(); + } + + @Override + public ARDISCOVERY_ERROR_ENUM onReceiveJson (String dataRx, String ip) + { + /* Receive a json with the controller to Device port */ + ARDISCOVERY_ERROR_ENUM error = ARDISCOVERY_ERROR_ENUM.ARDISCOVERY_OK; + try + { + /* Convert String to json */ + JSONObject jsonObject = new JSONObject(dataRx); + if (!jsonObject.isNull(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_C2DPORT_KEY)) + { + c2dPort = jsonObject.getInt(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_C2DPORT_KEY); + } + if (!jsonObject.isNull(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_ARSTREAM_FRAGMENT_SIZE_KEY)) + { + videoFragmentSize = jsonObject.getInt(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_ARSTREAM_FRAGMENT_SIZE_KEY); + } + if (!jsonObject.isNull(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_ARSTREAM_FRAGMENT_MAXIMUM_NUMBER_KEY)) + { + videoFragmentMaximumNumber = jsonObject.getInt(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_ARSTREAM_FRAGMENT_MAXIMUM_NUMBER_KEY); + } + if (!jsonObject.isNull(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_ARSTREAM_MAX_ACK_INTERVAL_KEY)) + { + videoMaxAckInterval = jsonObject.getInt(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_ARSTREAM_MAX_ACK_INTERVAL_KEY); + } + /* + if (!jsonObject.isNull(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_SKYCONTROLLER_VERSION)) + { + napSoftVersion = jsonObject.getString(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_SKYCONTROLLER_VERSION); + } + */ + + } + catch (JSONException e) + { + e.printStackTrace(); + error = ARDISCOVERY_ERROR_ENUM.ARDISCOVERY_ERROR; + } + return error; + } + }; + + if (ok == true) + { + ConnectionThread connectionThread = new ConnectionThread(ip, port); + connectionThread.start(); + try + { + discoverSemaphore.acquire(); + error = connectionThread.getError(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + + if (discoveryData != null) + { + discoveryData.dispose(); + discoveryData = null; + } + + return ok && (error == ARDISCOVERY_ERROR_ENUM.ARDISCOVERY_OK); + } + + private void startReadThreads() + { + /* Create the reader threads */ + for (int bufferId : commandsBuffers) + { + ReaderThread readerThread = new ReaderThread(bufferId); + readerThreads.add(readerThread); + } + + /* Mark all reader threads as started */ + for (ReaderThread readerThread : readerThreads) + { + readerThread.start(); + } + } + + /*** video mods start ***/ + private void startVideoThread() + { + /* Create a ARStreamReader and create the video thread if the target supports video streaming */ + if (hasVideo) + { + /* Reset the video listener to prevent forwarding frames to it before we return from this method */ + //videoStreamListener = null; //TODO:see + + /* Create the video thread */ + videoThread = new VideoThread(); + + /* Start the video thread */ + videoThread.start(); + } + } + /*** video mods end ***/ + + private void startLooperThread() + { + /* Create the looper thread */ + looperThread = new ControllerLooperThread(); + + /* Start the looper thread. */ + looperThread.start(); + } + + private void stopLooperThread() + { + /* Cancel the looper thread and block until it is stopped. */ + if (null != looperThread) + { + looperThread.stopThread(); + try + { + looperThread.join(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + } + + private void stopReaderThreads() + { + if(readerThreads != null) + { + /* cancel all reader threads and block until they are all stopped. */ + for (ReaderThread thread : readerThreads) + { + thread.stopThread(); + } + for (ReaderThread thread : readerThreads) + { + try + { + thread.join(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + readerThreads.clear(); + } + } + + /*** video mods start ***/ + private void stopVideoThread() + { + /* Stop the video streamer */ + if (videoThread != null) + { + videoThread.stopThread(); + try + { + videoThread.join(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + + } + } + } + /*** video mods end ***/ + + private void stopNetwork() + { + if(netManager != null) + { + netManager.stop(); + + try + { + if (txThread != null) + { + txThread.join(); + } + if (rxThread != null) + { + rxThread.join(); + } + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + + netManager.dispose(); + } + + if ((alManager != null) && (mediaOpened)) + { + if (deviceService.getDevice() instanceof ARDiscoveryDeviceNetService) + { + alManager.closeWifiNetwork(); + } + else if (deviceService.getDevice() instanceof ARDiscoveryDeviceBLEService) + { + alManager.closeBLENetwork(context); + } + + mediaOpened = false; + alManager.dispose(); + } + } + + /*** video mods start ***/ + /** + * Set the video listener object. One of its methods will be called for each + * received video frame. + * @param listener + */ + /* + public void setVideoListener (DeviceControllerVideoStreamListener listener) + { + if (deviceControllerBridgeClass == null) + { + videoStreamListener = listener; + } + else + { + if (deviceControllerBridge != null) + { + deviceControllerBridge.setVideoListener(listener); + } + } + } + */ + /*** video mods end ***/ + + protected void registerARCommandsListener () + { + ARCommand.setCommonSettingsStateAllSettingsChangedListener(this); + ARCommand.setCommonCommonStateAllStatesChangedListener(this); + ARCommand.setCommonCommonStateBatteryStateChangedListener(this); + ARCommand.setARDrone3PilotingStateFlyingStateChangedListener(this); + } + + protected void unregisterARCommandsListener () + { + ARCommand.setCommonSettingsStateAllSettingsChangedListener(null); + ARCommand.setCommonCommonStateAllStatesChangedListener(null); + ARCommand.setCommonCommonStateBatteryStateChangedListener(null); + ARCommand.setARDrone3PilotingStateFlyingStateChangedListener(null); + } + + public boolean getInitialSettings() + { + /* Attempt to get initial settings */ + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + cmdError = cmd.setCommonSettingsAllSettings(); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + /* Send data with ARNetwork */ + // The commands sent by event should be sent to an buffer acknowledged ; here iobufferC2dAck + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dAck, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send AllSettings command."); + } + else + { + try + { + cmdGetAllSettingsSent.acquire(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + sentStatus = false; + } + } + + return sentStatus; + } + + protected boolean getInitialStates() + { + /* Attempt to get initial states */ + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + cmdError = cmd.setCommonCommonAllStates(); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + /* Send data with ARNetwork */ + // The commands sent by event should be sent to an buffer acknowledged ; here iobufferC2dAck + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dAck, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send AllStates command."); + } + else + { + try + { + cmdGetAllStatesSent.acquire(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + sentStatus = false; + } + } + + return sentStatus; + } + + /*** video mods start ***/ + private boolean sendBeginStream() + { + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + // Send Streaming begin command + byte _enable = 1; + cmdError = cmd.setARDrone3MediaStreamingVideoEnable(_enable); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dAck, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send begin command."); + } + + return sentStatus; + } + /*** video mods end ***/ + + private boolean sendPCMD() + { + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + cmdError = cmd.setARDrone3PilotingPCMD(dataPCMD.flag, dataPCMD.roll, dataPCMD.pitch, dataPCMD.yaw, dataPCMD.gaz, dataPCMD.psi); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + /* Send data with ARNetwork */ + // The commands sent in loop should be sent to a buffer not acknowledged ; here iobufferC2dNack + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dNack, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send PCMD command."); + } + + return sentStatus; + } + + public boolean sendTakeoff() + { + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + cmdError = cmd.setARDrone3PilotingTakeOff(); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + /* Send data with ARNetwork */ + // The commands sent by event should be sent to an buffer acknowledged ; here iobufferC2dAck + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dAck, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send TakeOff command."); + } + + return sentStatus; + } + + public boolean sendLanding() + { + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + cmdError = cmd.setARDrone3PilotingLanding(); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + /* Send data with ARNetwork */ + // The commands sent by event should be sent to an buffer acknowledged ; here iobufferC2dAck + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dAck, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send Landing command."); + } + + return sentStatus; + } + + public boolean sendEmergency() + { + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + cmdError = cmd.setARDrone3PilotingEmergency(); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + /* Send data with ARNetwork */ + // The command emergency should be sent to its own buffer acknowledged ; here iobufferC2dEmergency + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dEmergency, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send Emergency command."); + } + + return sentStatus; + } + + public boolean sendDate(Date currentDate) + { + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + SimpleDateFormat formattedDate = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + + cmdError = cmd.setCommonCommonCurrentDate(formattedDate.format(currentDate)); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + /* Send data with ARNetwork */ + // The command emergency should be sent to its own buffer acknowledged ; here iobufferC2dAck + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dAck, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send date command."); + } + + return sentStatus; + } + + public boolean sendTime(Date currentDate) + { + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + SimpleDateFormat formattedTime = new SimpleDateFormat("'T'HHmmssZZZ", Locale.getDefault()); + + cmdError = cmd.setCommonCommonCurrentTime(formattedTime.format(currentDate)); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + /* Send data with ARNetwork */ + // The command emergency should be sent to its own buffer acknowledged ; here iobufferC2dAck + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dAck, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send time command."); + } + + return sentStatus; + } + + public void setFlag(byte flag) + { + dataPCMD.flag = flag; + } + + public void setGaz(byte gaz) + { + dataPCMD.gaz = gaz; + } + + public void setRoll (byte roll) + { + dataPCMD.roll = roll; + } + + public void setPitch (byte pitch) + { + dataPCMD.pitch = pitch; + } + + public void setYaw (byte yaw) + { + dataPCMD.yaw = yaw; + } + + public void setListener(DeviceControllerListener listener) + { + this.listener = listener; + } + + @Override + public void onCommonSettingsStateAllSettingsChangedUpdate () + { + cmdGetAllSettingsSent.release(); + Log.i(TAG, "All settings received"); + } + + @Override + public void onCommonCommonStateAllStatesChangedUpdate () + { + cmdGetAllStatesSent.release(); + Log.i(TAG, "All states received"); + } + + @Override + public void onCommonCommonStateBatteryStateChangedUpdate(byte b) + { + Log.d(TAG, "onCommonCommonStateBatteryStateChangedUpdate ..."); + + if (listener != null) + { + listener.onUpdateBattery(b); + } + } + + @Override + public void onARDrone3PilotingStateFlyingStateChangedUpdate(ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM state) + { + Log.d(TAG, "onARDrone3PilotingStateFlyingStateChangedUpdate : " + state); + + if (listener != null) { + listener.onFlyingStateChanged(state); + } + } + + /** + * Extend of ARNetworkManager implementing the callback + */ + private class ARNetworkManagerExtend extends ARNetworkManager + { + public ARNetworkManagerExtend(ARNetworkALManager osSpecificManager, ARNetworkIOBufferParam[] inputParamArray, ARNetworkIOBufferParam[] outputParamArray, int timeBetweenPingsMs) + { + super(osSpecificManager, inputParamArray, outputParamArray, timeBetweenPingsMs); + } + + private static final String TAG = "ARNetworkManagerExtend"; + + @Override + public ARNETWORK_MANAGER_CALLBACK_RETURN_ENUM onCallback(int ioBufferId, ARNativeData data, ARNETWORK_MANAGER_CALLBACK_STATUS_ENUM status, Object customData) + { + ARNETWORK_MANAGER_CALLBACK_RETURN_ENUM retVal = ARNETWORK_MANAGER_CALLBACK_RETURN_ENUM.ARNETWORK_MANAGER_CALLBACK_RETURN_DEFAULT; + + if (status == ARNETWORK_MANAGER_CALLBACK_STATUS_ENUM.ARNETWORK_MANAGER_CALLBACK_STATUS_TIMEOUT) + { + retVal = ARNETWORK_MANAGER_CALLBACK_RETURN_ENUM.ARNETWORK_MANAGER_CALLBACK_RETURN_DATA_POP; + } + + return retVal; + } + + @Override + public void onDisconnect(ARNetworkALManager arNetworkALManager) + { + Log.d(TAG, "onDisconnect ..."); + + if (listener != null) + { + listener.onDisconnect(); + } + } + } + + private class DataPCMD + { + public byte flag; + public byte roll; + public byte pitch; + public byte yaw; + public byte gaz; + public float psi; + + public DataPCMD () + { + flag = 0; + roll = 0; + pitch = 0; + yaw = 0; + gaz = 0; + psi = 0.0f; + } + } + + + private abstract class LooperThread extends Thread + { + private boolean isAlive; + private boolean isRunning; + + public LooperThread () + { + this.isRunning = false; + this.isAlive = true; + } + + @Override + public void run() + { + this.isRunning = true; + + onStart (); + + while (this.isAlive) + { + onloop(); + } + onStop(); + + this.isRunning = false; + } + + public void onStart () + { + + } + + public abstract void onloop (); + + public void onStop () + { + + } + + public void stopThread() + { + isAlive = false; + } + + public boolean isRunning() + { + return this.isRunning; + } + } + + private class ReaderThread extends LooperThread + { + int bufferId; + ARCommand dataRecv; + + public ReaderThread (int bufferId) + { + this.bufferId = bufferId; + dataRecv = new ARCommand (128 * 1024);//TODO define + } + + @Override + public void onStart () + { + + } + + @Override + public void onloop() + { + boolean skip = false; + ARNETWORK_ERROR_ENUM netError = ARNETWORK_ERROR_ENUM.ARNETWORK_OK; + + /* read data*/ + netError = netManager.readDataWithTimeout (bufferId, dataRecv, 1000); //TODO define + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + if(netError != ARNETWORK_ERROR_ENUM.ARNETWORK_ERROR_BUFFER_EMPTY) + { +// ARSALPrint.e (TAG, "ReaderThread readDataWithTimeout() failed. " + netError + " bufferId: " + bufferId); + } + + skip = true; + } + + if (skip == false) + { + ARCOMMANDS_DECODER_ERROR_ENUM decodeStatus = dataRecv.decode(); + if ((decodeStatus != ARCOMMANDS_DECODER_ERROR_ENUM.ARCOMMANDS_DECODER_OK) && (decodeStatus != ARCOMMANDS_DECODER_ERROR_ENUM.ARCOMMANDS_DECODER_ERROR_NO_CALLBACK) && (decodeStatus != ARCOMMANDS_DECODER_ERROR_ENUM.ARCOMMANDS_DECODER_ERROR_UNKNOWN_COMMAND)) + { + ARSALPrint.e(TAG, "ARCommand.decode() failed. " + decodeStatus); + } + } + } + + @Override + public void onStop() + { + dataRecv.dispose(); + super.onStop(); + } + } + + protected class ControllerLooperThread extends LooperThread + { + public ControllerLooperThread() + { + + } + + @Override + public void onloop() + { + long lastTime = SystemClock.elapsedRealtime(); + + sendPCMD(); + + long sleepTime = (SystemClock.elapsedRealtime() + PCMD_LOOP_IN_MS) - lastTime; + + try + { + Thread.sleep(sleepTime); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + } + + /*** video mods start ***/ + private class VideoThread extends LooperThread + { + private ARStreamManager streamManager; + + public VideoThread () + { + streamManager = new ARStreamManager (netManager, iobufferD2cArstreamData, + iobufferC2dArstreamAck, videoFragmentSize, videoMaxAckInterval); + } + + @Override + public void onStart() + { + super.onStart(); + + streamManager.startStream(); + } + + @Override + public void onloop() + { + Bitmap bitmap = streamManager.getFrameWithTimeout(VIDEO_RECEIVE_TIMEOUT); + //display frame + if (listener != null) { + listener.onUpdateStream(bitmap); + } + } + + @Override + public void onStop() + { + streamManager.stopStream(); + + super.onStop(); + } + } + /*** video mods end ***/ + + private class ConnectionThread extends Thread + { + private ARDISCOVERY_ERROR_ENUM error; + private String mDiscoveryIp; + private int mDiscoveryPort; + + + public ConnectionThread(String ip, int port) + { + mDiscoveryIp = ip; + mDiscoveryPort = port; + } + + public void run() + { + error = discoveryData.ControllerConnection(mDiscoveryPort, mDiscoveryIp); + if (error != ARDISCOVERY_ERROR_ENUM.ARDISCOVERY_OK) + { + ARSALPrint.e(TAG, "Error while opening discovery connection : " + error); + } + + /* discoverSemaphore can be disposed */ + discoverSemaphore.release(); + } + + public ARDISCOVERY_ERROR_ENUM getError() + { + return error; + } + } +} diff --git a/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/DeviceController.java~ b/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/DeviceController.java~ new file mode 100644 index 0000000..75d00d8 --- /dev/null +++ b/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/DeviceController.java~ @@ -0,0 +1,1061 @@ +package com.parrot.bobopdronepiloting; + + +import android.os.SystemClock; +import android.util.Log; + +import com.parrot.arsdk.arcommands.ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM; +import com.parrot.arsdk.arcommands.ARCOMMANDS_DECODER_ERROR_ENUM; +import com.parrot.arsdk.arcommands.ARCOMMANDS_GENERATOR_ERROR_ENUM; +import com.parrot.arsdk.arcommands.ARCommand; +import com.parrot.arsdk.arcommands.ARCommandARDrone3PilotingStateFlyingStateChangedListener; +import com.parrot.arsdk.arcommands.ARCommandCommonCommonStateAllStatesChangedListener; +import com.parrot.arsdk.arcommands.ARCommandCommonCommonStateBatteryStateChangedListener; +import com.parrot.arsdk.arcommands.ARCommandCommonSettingsStateAllSettingsChangedListener; +import com.parrot.arsdk.ardiscovery.ARDISCOVERY_ERROR_ENUM; +import com.parrot.arsdk.ardiscovery.ARDiscoveryConnection; +import com.parrot.arsdk.ardiscovery.ARDiscoveryDeviceBLEService; +import com.parrot.arsdk.ardiscovery.ARDiscoveryDeviceNetService; +import com.parrot.arsdk.ardiscovery.ARDiscoveryDeviceService; +import com.parrot.arsdk.arnetwork.ARNETWORK_ERROR_ENUM; +import com.parrot.arsdk.arnetwork.ARNETWORK_MANAGER_CALLBACK_RETURN_ENUM; +import com.parrot.arsdk.arnetwork.ARNETWORK_MANAGER_CALLBACK_STATUS_ENUM; +import com.parrot.arsdk.arnetwork.ARNetworkIOBufferParam; +import com.parrot.arsdk.arnetwork.ARNetworkManager; +import com.parrot.arsdk.arnetworkal.ARNETWORKAL_ERROR_ENUM; +import com.parrot.arsdk.arnetworkal.ARNETWORKAL_FRAME_TYPE_ENUM; +import com.parrot.arsdk.arnetworkal.ARNetworkALManager; +import com.parrot.arsdk.arsal.ARNativeData; +import com.parrot.arsdk.arsal.ARSALPrint; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.sql.Date; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.Semaphore; + +public class DeviceController implements ARCommandCommonCommonStateBatteryStateChangedListener, ARCommandCommonSettingsStateAllSettingsChangedListener, ARCommandCommonCommonStateAllStatesChangedListener, ARCommandARDrone3PilotingStateFlyingStateChangedListener +{ + private static String TAG = "DeviceController"; + + private static final int PCMD_LOOP_IN_MS = 25; + + private final static int iobufferC2dNack = 10; + private final static int iobufferC2dAck = 11; + private final static int iobufferC2dEmergency = 12; + private final static int iobufferD2cNavdata = 127; + private final static int iobufferD2cEvents = 126; + /*** stream mods start ***/ + private final static int iobufferC2dVideoAck = 13; + private final static int iobufferD2cVideoData = 125 + private final static int iobufferD2cVideoFragSize = 1000; + private final static int iobufferD2cVideoMaxNumberOfFrag = 128; + /*** stream mods end ***/ + + protected static List c2dParams = new ArrayList(); + protected static List d2cParams = new ArrayList(); + protected static int commandsBuffers[] = {}; + + private android.content.Context context; + + private ARNetworkALManager alManager; + private ARNetworkManager netManager; + private boolean mediaOpened; + + private int c2dPort; // read from json + private int d2cPort = 43210; // set by the app, sent through json + private Thread rxThread; + private Thread txThread; + + private List readerThreads; + private Semaphore discoverSemaphore; + private ARDiscoveryConnection discoveryData; + + private LooperThread looperThread; + + private DataPCMD dataPCMD; + private ARDiscoveryDeviceService deviceService; + + private DeviceControllerListener listener; + + private Semaphore cmdGetAllSettingsSent; + private Semaphore cmdGetAllStatesSent; + + + static + { + c2dParams.clear(); + c2dParams.add (new ARNetworkIOBufferParam (iobufferC2dNack, + ARNETWORKAL_FRAME_TYPE_ENUM.ARNETWORKAL_FRAME_TYPE_DATA, + 20, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_INFINITE_NUMBER, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_INFINITE_NUMBER, + 1, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_DATACOPYMAXSIZE_USE_MAX, + true)); + c2dParams.add (new ARNetworkIOBufferParam (iobufferC2dAck, + ARNETWORKAL_FRAME_TYPE_ENUM.ARNETWORKAL_FRAME_TYPE_DATA_WITH_ACK, + 20, + 500, + 3, + 20, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_DATACOPYMAXSIZE_USE_MAX, + false)); + c2dParams.add (new ARNetworkIOBufferParam (iobufferC2dEmergency, + ARNETWORKAL_FRAME_TYPE_ENUM.ARNETWORKAL_FRAME_TYPE_DATA_WITH_ACK, + 1, + 100, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_INFINITE_NUMBER, + 1, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_DATACOPYMAXSIZE_USE_MAX, + false)); + + d2cParams.clear(); + d2cParams.add (new ARNetworkIOBufferParam (iobufferD2cNavdata, + ARNETWORKAL_FRAME_TYPE_ENUM.ARNETWORKAL_FRAME_TYPE_DATA, + 20, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_INFINITE_NUMBER, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_INFINITE_NUMBER, + 20, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_DATACOPYMAXSIZE_USE_MAX, + false)); + d2cParams.add (new ARNetworkIOBufferParam (iobufferD2cEvents, + ARNETWORKAL_FRAME_TYPE_ENUM.ARNETWORKAL_FRAME_TYPE_DATA_WITH_ACK, + 20, + 500, + 3, + 20, + ARNetworkIOBufferParam.ARNETWORK_IOBUFFERPARAM_DATACOPYMAXSIZE_USE_MAX, + false)); + + commandsBuffers = new int[] { + iobufferD2cNavdata, + iobufferD2cEvents, + }; + + } + + public DeviceController (android.content.Context context, ARDiscoveryDeviceService service) + { + dataPCMD = new DataPCMD(); + deviceService = service; + this.context = context; + readerThreads = new ArrayList(); + cmdGetAllSettingsSent = new Semaphore (0); + cmdGetAllStatesSent = new Semaphore (0); + } + + public boolean start() + { + Log.d(TAG, "start ..."); + + boolean failed = false; + + registerARCommandsListener (); + + failed = startNetwork(); + + if (!failed) + { + /* start the reader threads */ + startReadThreads(); + } + + if (!failed) + { + /* start the looper thread */ + startLooperThread(); + } + + // send date + Date currentDate = new Date(System.currentTimeMillis()); + if (!failed) + { + failed = !sendDate(currentDate); + } + + if (!failed) + { + failed = !sendTime(currentDate); + } + + // get all settings + if (!failed) + { + failed = !getInitialSettings(); + } + + // get all states + if (!failed) + { + failed = !getInitialStates(); + } + + return failed; + } + + public void stop() + { + Log.d(TAG, "stop ..."); + + unregisterARCommandsListener(); + + /* Cancel the looper thread and block until it is stopped. */ + stopLooperThread(); + + /* cancel all reader threads and block until they are all stopped. */ + stopReaderThreads(); + + /* ARNetwork cleanup */ + stopNetwork(); + + } + + private boolean startNetwork() + { + ARNETWORKAL_ERROR_ENUM netALError = ARNETWORKAL_ERROR_ENUM.ARNETWORKAL_OK; + boolean failed = false; + int pingDelay = 0; /* 0 means default, -1 means no ping */ + + /* Create the looper ARNetworkALManager */ + alManager = new ARNetworkALManager(); + + + /* setup ARNetworkAL for Wifi */ + ARDiscoveryDeviceNetService netDeviceService = (ARDiscoveryDeviceNetService) deviceService.getDevice(); + + if (!ardiscoveryConnect(netDeviceService.getIp(), netDeviceService.getPort())) + { + failed = true; + } + + netALError = alManager.initWifiNetwork(netDeviceService.getIp(), c2dPort, d2cPort, 3); + //netALError = alManager.initWNetwork(context, bleDevice.getBluetoothDevice(), 1, bleNotificationIDs); + + if (netALError == ARNETWORKAL_ERROR_ENUM.ARNETWORKAL_OK) + { + mediaOpened = true; + } + else + { + ARSALPrint.e(TAG, "error occured: " + netALError.toString()); + failed = true; + } + + if (failed == false) + { + /* Create the ARNetworkManager */ + netManager = new ARNetworkManagerExtend(alManager, c2dParams.toArray(new ARNetworkIOBufferParam[c2dParams.size()]), d2cParams.toArray(new ARNetworkIOBufferParam[d2cParams.size()]), pingDelay); + + if (netManager.isCorrectlyInitialized() == false) + { + ARSALPrint.e (TAG, "new ARNetworkManager failed"); + failed = true; + } + } + + if (failed == false) + { + /* Create and start Tx and Rx threads */ + rxThread = new Thread (netManager.m_receivingRunnable); + rxThread.start(); + + txThread = new Thread (netManager.m_sendingRunnable); + txThread.start(); + } + + return failed; + } + + private boolean ardiscoveryConnect(String ip, int port) + { + boolean ok = true; + ARDISCOVERY_ERROR_ENUM error = ARDISCOVERY_ERROR_ENUM.ARDISCOVERY_OK; + discoverSemaphore = new Semaphore (0); + + discoveryData = new ARDiscoveryConnection() + { + @Override + public String onSendJson () + { + /* send a json with the Device to controller port */ + JSONObject jsonObject = new JSONObject(); + + try + { + jsonObject.put(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_D2CPORT_KEY, d2cPort); + } + catch (JSONException e) + { + e.printStackTrace(); + } + try + { + Log.e(TAG, "android.os.Build.MODEL: "+android.os.Build.MODEL); + jsonObject.put(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_CONTROLLER_NAME_KEY, android.os.Build.MODEL); + } + catch (JSONException e) + { + e.printStackTrace(); + } + try + { + Log.e(TAG, "android.os.Build.DEVICE: " + android.os.Build.DEVICE); + jsonObject.put(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_CONTROLLER_TYPE_KEY, android.os.Build.DEVICE); + } + catch (JSONException e) + { + e.printStackTrace(); + } + + return jsonObject.toString(); + } + + @Override + public ARDISCOVERY_ERROR_ENUM onReceiveJson (String dataRx, String ip) + { + /* Receive a json with the controller to Device port */ + ARDISCOVERY_ERROR_ENUM error = ARDISCOVERY_ERROR_ENUM.ARDISCOVERY_OK; + try + { + /* Convert String to json */ + JSONObject jsonObject = new JSONObject(dataRx); + if (!jsonObject.isNull(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_C2DPORT_KEY)) + { + c2dPort = jsonObject.getInt(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_C2DPORT_KEY); + } + /*if (!jsonObject.isNull(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_ARSTREAM_FRAGMENT_SIZE_KEY)) + { + videoFragmentSize = jsonObject.getInt(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_ARSTREAM_FRAGMENT_SIZE_KEY); + } + if (!jsonObject.isNull(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_ARSTREAM_FRAGMENT_MAXIMUM_NUMBER_KEY)) + { + videoFragmentMaximumNumber = jsonObject.getInt(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_ARSTREAM_FRAGMENT_MAXIMUM_NUMBER_KEY); + } + if (!jsonObject.isNull(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_ARSTREAM_MAX_ACK_INTERVAL_KEY)) + { + videoMaxAckInterval = jsonObject.getInt(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_ARSTREAM_MAX_ACK_INTERVAL_KEY); + } + if (!jsonObject.isNull(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_SKYCONTROLLER_VERSION)) + { + napSoftVersion = jsonObject.getString(ARDiscoveryConnection.ARDISCOVERY_CONNECTION_JSON_SKYCONTROLLER_VERSION); + }*/ + + } + catch (JSONException e) + { + e.printStackTrace(); + error = ARDISCOVERY_ERROR_ENUM.ARDISCOVERY_ERROR; + } + return error; + } + }; + + if (ok == true) + { + ConnectionThread connectionThread = new ConnectionThread(ip, port); + connectionThread.start(); + try + { + discoverSemaphore.acquire(); + error = connectionThread.getError(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + + if (discoveryData != null) + { + discoveryData.dispose(); + discoveryData = null; + } + + return ok && (error == ARDISCOVERY_ERROR_ENUM.ARDISCOVERY_OK); + } + + private void startReadThreads() + { + /* Create the reader threads */ + for (int bufferId : commandsBuffers) + { + ReaderThread readerThread = new ReaderThread(bufferId); + readerThreads.add(readerThread); + } + + /* Mark all reader threads as started */ + for (ReaderThread readerThread : readerThreads) + { + readerThread.start(); + } + } + + private void startLooperThread() + { + /* Create the looper thread */ + looperThread = new ControllerLooperThread(); + + /* Start the looper thread. */ + looperThread.start(); + } + + private void stopLooperThread() + { + /* Cancel the looper thread and block until it is stopped. */ + if (null != looperThread) + { + looperThread.stopThread(); + try + { + looperThread.join(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + } + + private void stopReaderThreads() + { + if(readerThreads != null) + { + /* cancel all reader threads and block until they are all stopped. */ + for (ReaderThread thread : readerThreads) + { + thread.stopThread(); + } + for (ReaderThread thread : readerThreads) + { + try + { + thread.join(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + readerThreads.clear(); + } + } + + private void stopNetwork() + { + if(netManager != null) + { + netManager.stop(); + + try + { + if (txThread != null) + { + txThread.join(); + } + if (rxThread != null) + { + rxThread.join(); + } + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + + netManager.dispose(); + } + + if ((alManager != null) && (mediaOpened)) + { + if (deviceService.getDevice() instanceof ARDiscoveryDeviceNetService) + { + alManager.closeWifiNetwork(); + } + else if (deviceService.getDevice() instanceof ARDiscoveryDeviceBLEService) + { + alManager.closeBLENetwork(context); + } + + mediaOpened = false; + alManager.dispose(); + } + } + + + + protected void registerARCommandsListener () + { + ARCommand.setCommonSettingsStateAllSettingsChangedListener(this); + ARCommand.setCommonCommonStateAllStatesChangedListener(this); + ARCommand.setCommonCommonStateBatteryStateChangedListener(this); + ARCommand.setARDrone3PilotingStateFlyingStateChangedListener(this); + } + + protected void unregisterARCommandsListener () + { + ARCommand.setCommonSettingsStateAllSettingsChangedListener(null); + ARCommand.setCommonCommonStateAllStatesChangedListener(null); + ARCommand.setCommonCommonStateBatteryStateChangedListener(null); + ARCommand.setARDrone3PilotingStateFlyingStateChangedListener(null); + } + + public boolean getInitialSettings() + { + /* Attempt to get initial settings */ + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + cmdError = cmd.setCommonSettingsAllSettings(); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + /* Send data with ARNetwork */ + // The commands sent by event should be sent to an buffer acknowledged ; here iobufferC2dAck + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dAck, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send AllSettings command."); + } + else + { + try + { + cmdGetAllSettingsSent.acquire(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + sentStatus = false; + } + } + + return sentStatus; + } + + protected boolean getInitialStates() + { + /* Attempt to get initial states */ + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + cmdError = cmd.setCommonCommonAllStates(); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + /* Send data with ARNetwork */ + // The commands sent by event should be sent to an buffer acknowledged ; here iobufferC2dAck + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dAck, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send AllStates command."); + } + else + { + try + { + cmdGetAllStatesSent.acquire(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + sentStatus = false; + } + } + + return sentStatus; + } + + private boolean sendPCMD() + { + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + cmdError = cmd.setARDrone3PilotingPCMD(dataPCMD.flag, dataPCMD.roll, dataPCMD.pitch, dataPCMD.yaw, dataPCMD.gaz, dataPCMD.psi); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + /* Send data with ARNetwork */ + // The commands sent in loop should be sent to a buffer not acknowledged ; here iobufferC2dNack + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dNack, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send PCMD command."); + } + + return sentStatus; + } + + public boolean sendTakeoff() + { + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + cmdError = cmd.setARDrone3PilotingTakeOff(); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + /* Send data with ARNetwork */ + // The commands sent by event should be sent to an buffer acknowledged ; here iobufferC2dAck + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dAck, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send TakeOff command."); + } + + return sentStatus; + } + + public boolean sendLanding() + { + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + cmdError = cmd.setARDrone3PilotingLanding(); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + /* Send data with ARNetwork */ + // The commands sent by event should be sent to an buffer acknowledged ; here iobufferC2dAck + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dAck, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send Landing command."); + } + + return sentStatus; + } + + public boolean sendEmergency() + { + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + cmdError = cmd.setARDrone3PilotingEmergency(); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + /* Send data with ARNetwork */ + // The command emergency should be sent to its own buffer acknowledged ; here iobufferC2dEmergency + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dEmergency, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send Emergency command."); + } + + return sentStatus; + } + + public boolean sendDate(Date currentDate) + { + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + SimpleDateFormat formattedDate = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + + cmdError = cmd.setCommonCommonCurrentDate(formattedDate.format(currentDate)); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + /* Send data with ARNetwork */ + // The command emergency should be sent to its own buffer acknowledged ; here iobufferC2dAck + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dAck, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send date command."); + } + + return sentStatus; + } + + public boolean sendTime(Date currentDate) + { + ARCOMMANDS_GENERATOR_ERROR_ENUM cmdError = ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK; + boolean sentStatus = true; + ARCommand cmd = new ARCommand(); + + SimpleDateFormat formattedTime = new SimpleDateFormat("'T'HHmmssZZZ", Locale.getDefault()); + + cmdError = cmd.setCommonCommonCurrentTime(formattedTime.format(currentDate)); + if (cmdError == ARCOMMANDS_GENERATOR_ERROR_ENUM.ARCOMMANDS_GENERATOR_OK) + { + /* Send data with ARNetwork */ + // The command emergency should be sent to its own buffer acknowledged ; here iobufferC2dAck + ARNETWORK_ERROR_ENUM netError = netManager.sendData (iobufferC2dAck, cmd, null, true); + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + ARSALPrint.e(TAG, "netManager.sendData() failed. " + netError.toString()); + sentStatus = false; + } + + cmd.dispose(); + } + + if (sentStatus == false) + { + ARSALPrint.e(TAG, "Failed to send time command."); + } + + return sentStatus; + } + + public void setFlag(byte flag) + { + dataPCMD.flag = flag; + } + + public void setGaz(byte gaz) + { + dataPCMD.gaz = gaz; + } + + public void setRoll (byte roll) + { + dataPCMD.roll = roll; + } + + public void setPitch (byte pitch) + { + dataPCMD.pitch = pitch; + } + + public void setYaw (byte yaw) + { + dataPCMD.yaw = yaw; + } + + public void setListener(DeviceControllerListener listener) + { + this.listener = listener; + } + + @Override + public void onCommonSettingsStateAllSettingsChangedUpdate () + { + cmdGetAllSettingsSent.release(); + Log.i(TAG, "All settings received"); + } + + @Override + public void onCommonCommonStateAllStatesChangedUpdate () + { + cmdGetAllStatesSent.release(); + Log.i(TAG, "All states received"); + } + + @Override + public void onCommonCommonStateBatteryStateChangedUpdate(byte b) + { + Log.d(TAG, "onCommonCommonStateBatteryStateChangedUpdate ..."); + + if (listener != null) + { + listener.onUpdateBattery(b); + } + } + + @Override + public void onARDrone3PilotingStateFlyingStateChangedUpdate(ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM state) + { + Log.d(TAG, "onARDrone3PilotingStateFlyingStateChangedUpdate : " + state); + + if (listener != null) { + listener.onFlyingStateChanged(state); + } + } + + /** + * Extend of ARNetworkManager implementing the callback + */ + private class ARNetworkManagerExtend extends ARNetworkManager + { + public ARNetworkManagerExtend(ARNetworkALManager osSpecificManager, ARNetworkIOBufferParam[] inputParamArray, ARNetworkIOBufferParam[] outputParamArray, int timeBetweenPingsMs) + { + super(osSpecificManager, inputParamArray, outputParamArray, timeBetweenPingsMs); + } + + private static final String TAG = "ARNetworkManagerExtend"; + + @Override + public ARNETWORK_MANAGER_CALLBACK_RETURN_ENUM onCallback(int ioBufferId, ARNativeData data, ARNETWORK_MANAGER_CALLBACK_STATUS_ENUM status, Object customData) + { + ARNETWORK_MANAGER_CALLBACK_RETURN_ENUM retVal = ARNETWORK_MANAGER_CALLBACK_RETURN_ENUM.ARNETWORK_MANAGER_CALLBACK_RETURN_DEFAULT; + + if (status == ARNETWORK_MANAGER_CALLBACK_STATUS_ENUM.ARNETWORK_MANAGER_CALLBACK_STATUS_TIMEOUT) + { + retVal = ARNETWORK_MANAGER_CALLBACK_RETURN_ENUM.ARNETWORK_MANAGER_CALLBACK_RETURN_DATA_POP; + } + + return retVal; + } + + @Override + public void onDisconnect(ARNetworkALManager arNetworkALManager) + { + Log.d(TAG, "onDisconnect ..."); + + if (listener != null) + { + listener.onDisconnect(); + } + } + } + + private class DataPCMD + { + public byte flag; + public byte roll; + public byte pitch; + public byte yaw; + public byte gaz; + public float psi; + + public DataPCMD () + { + flag = 0; + roll = 0; + pitch = 0; + yaw = 0; + gaz = 0; + psi = 0.0f; + } + } + + + private abstract class LooperThread extends Thread + { + private boolean isAlive; + private boolean isRunning; + + public LooperThread () + { + this.isRunning = false; + this.isAlive = true; + } + + @Override + public void run() + { + this.isRunning = true; + + onStart (); + + while (this.isAlive) + { + onloop(); + } + onStop(); + + this.isRunning = false; + } + + public void onStart () + { + + } + + public abstract void onloop (); + + public void onStop () + { + + } + + public void stopThread() + { + isAlive = false; + } + + public boolean isRunning() + { + return this.isRunning; + } + } + + private class ReaderThread extends LooperThread + { + int bufferId; + ARCommand dataRecv; + + public ReaderThread (int bufferId) + { + this.bufferId = bufferId; + dataRecv = new ARCommand (128 * 1024);//TODO define + } + + @Override + public void onStart () + { + + } + + @Override + public void onloop() + { + boolean skip = false; + ARNETWORK_ERROR_ENUM netError = ARNETWORK_ERROR_ENUM.ARNETWORK_OK; + + /* read data*/ + netError = netManager.readDataWithTimeout (bufferId, dataRecv, 1000); //TODO define + + if (netError != ARNETWORK_ERROR_ENUM.ARNETWORK_OK) + { + if(netError != ARNETWORK_ERROR_ENUM.ARNETWORK_ERROR_BUFFER_EMPTY) + { +// ARSALPrint.e (TAG, "ReaderThread readDataWithTimeout() failed. " + netError + " bufferId: " + bufferId); + } + + skip = true; + } + + if (skip == false) + { + ARCOMMANDS_DECODER_ERROR_ENUM decodeStatus = dataRecv.decode(); + if ((decodeStatus != ARCOMMANDS_DECODER_ERROR_ENUM.ARCOMMANDS_DECODER_OK) && (decodeStatus != ARCOMMANDS_DECODER_ERROR_ENUM.ARCOMMANDS_DECODER_ERROR_NO_CALLBACK) && (decodeStatus != ARCOMMANDS_DECODER_ERROR_ENUM.ARCOMMANDS_DECODER_ERROR_UNKNOWN_COMMAND)) + { + ARSALPrint.e(TAG, "ARCommand.decode() failed. " + decodeStatus); + } + } + } + + @Override + public void onStop() + { + dataRecv.dispose(); + super.onStop(); + } + } + + protected class ControllerLooperThread extends LooperThread + { + public ControllerLooperThread() + { + + } + + @Override + public void onloop() + { + long lastTime = SystemClock.elapsedRealtime(); + + sendPCMD(); + + long sleepTime = (SystemClock.elapsedRealtime() + PCMD_LOOP_IN_MS) - lastTime; + + try + { + Thread.sleep(sleepTime); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + } + + private class ConnectionThread extends Thread + { + private ARDISCOVERY_ERROR_ENUM error; + private String mDiscoveryIp; + private int mDiscoveryPort; + + + public ConnectionThread(String ip, int port) + { + mDiscoveryIp = ip; + mDiscoveryPort = port; + } + + public void run() + { + error = discoveryData.ControllerConnection(mDiscoveryPort, mDiscoveryIp); + if (error != ARDISCOVERY_ERROR_ENUM.ARDISCOVERY_OK) + { + ARSALPrint.e(TAG, "Error while opening discovery connection : " + error); + } + + /* discoverSemaphore can be disposed */ + discoverSemaphore.release(); + } + + public ARDISCOVERY_ERROR_ENUM getError() + { + return error; + } + } +} diff --git a/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/DeviceControllerListener.java b/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/DeviceControllerListener.java new file mode 100644 index 0000000..6c97c1c --- /dev/null +++ b/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/DeviceControllerListener.java @@ -0,0 +1,12 @@ +package com.parrot.bebopdronestreaming; + +import com.parrot.arsdk.arcommands.ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM; +import android.graphics.Bitmap; + +public interface DeviceControllerListener +{ + public void onDisconnect(); + public void onUpdateBattery(final byte percent); + public void onFlyingStateChanged(ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM state); + public void onUpdateStream(Bitmap bitmap); +} diff --git a/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/MainActivity.java b/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/MainActivity.java new file mode 100644 index 0000000..74ce941 --- /dev/null +++ b/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/MainActivity.java @@ -0,0 +1,286 @@ +package com.parrot.bebopdronestreaming; + +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.support.v4.content.LocalBroadcastManager; +import android.support.v7.app.ActionBarActivity; +import android.util.Log; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +import com.parrot.arsdk.ardiscovery.ARDISCOVERY_PRODUCT_ENUM; +import com.parrot.arsdk.ardiscovery.ARDiscoveryDeviceService; +import com.parrot.arsdk.ardiscovery.ARDiscoveryService; +import com.parrot.arsdk.ardiscovery.receivers.ARDiscoveryServicesDevicesListUpdatedReceiver; +import com.parrot.arsdk.ardiscovery.receivers.ARDiscoveryServicesDevicesListUpdatedReceiverDelegate; +import com.parrot.arsdk.arsal.ARSALPrint; + +import org.bytedeco.javacpp.Loader; + +import static org.bytedeco.javacpp.avcodec.*; +import static org.bytedeco.javacpp.avdevice.*; +import static org.bytedeco.javacpp.avformat.*; + +import java.util.ArrayList; +import java.util.List; + + +public class MainActivity extends ActionBarActivity implements ARDiscoveryServicesDevicesListUpdatedReceiverDelegate +{ + private static String TAG = MainActivity.class.getSimpleName(); + + static + { + try + { + System.loadLibrary("arsal"); + System.loadLibrary("arsal_android"); + System.loadLibrary("arnetworkal"); + System.loadLibrary("arnetworkal_android"); + System.loadLibrary("arnetwork"); + System.loadLibrary("arnetwork_android"); + System.loadLibrary("arcommands"); + System.loadLibrary("arcommands_android"); + System.loadLibrary("ardiscovery"); + System.loadLibrary("ardiscovery_android"); + + /*** video mods start ***/ + System.loadLibrary("arstream"); + System.loadLibrary("arstream_android"); + System.loadLibrary("avutil"); + System.loadLibrary("arstream_android"); + + Loader.load(org.bytedeco.javacpp.avutil.class); + Loader.load(org.bytedeco.javacpp.swresample.class); + Loader.load(org.bytedeco.javacpp.avcodec.class); + Loader.load(org.bytedeco.javacpp.avformat.class); + Loader.load(org.bytedeco.javacpp.swscale.class); + + // Register all formats and codec + avcodec_register_all(); + av_register_all(); + avformat_network_init(); + + Loader.load(org.bytedeco.javacpp.avdevice.class); + avdevice_register_all(); + /*** video mods end ***/ + + ARSALPrint.enableDebugPrints(); + + } + catch (Exception e) + { + Log.e(TAG, "Oops (LoadLibrary)", e); + } + } + + private ListView listView ; + private List deviceList; + private String[] deviceNameList; + + private ARDiscoveryService ardiscoveryService; + private boolean ardiscoveryServiceBound = false; + private ServiceConnection ardiscoveryServiceConnection; + public IBinder discoveryServiceBinder; + private BroadcastReceiver ardiscoveryServicesDevicesListUpdatedReceiver; + + @Override + protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + startServices(); + initBroadcastReceiver(); + initServiceConnection(); + + listView = (ListView) findViewById(R.id.list); + + deviceList = new ArrayList(); + deviceNameList = new String[]{}; + + ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, deviceNameList); + + + // Assign adapter to ListView + listView.setAdapter(adapter); + + // ListView Item Click Listener + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() + { + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) + { + + ARDiscoveryDeviceService service = deviceList.get(position); + + Intent intent = new Intent(MainActivity.this, PilotingActivity.class); + intent.putExtra(PilotingActivity.EXTRA_DEVICE_SERVICE, service); + + startActivity(intent); + } + + }); + } + + private void startServices() + { + //startService(new Intent(this, ARDiscoveryService.class)); + } + + private void initServices() + { + if (discoveryServiceBinder == null) + { + Intent i = new Intent(getApplicationContext(), ARDiscoveryService.class); + getApplicationContext().bindService(i, ardiscoveryServiceConnection, Context.BIND_AUTO_CREATE); + } + else + { + ardiscoveryService = ((ARDiscoveryService.LocalBinder) discoveryServiceBinder).getService(); + ardiscoveryServiceBound = true; + + ardiscoveryService.start(); + } + } + + private void closeServices() + { + Log.d(TAG, "closeServices ..."); + + if (ardiscoveryServiceBound) + { + new Thread(new Runnable() { + @Override + public void run() + { + ardiscoveryService.stop(); + + getApplicationContext().unbindService(ardiscoveryServiceConnection); + ardiscoveryServiceBound = false; + discoveryServiceBinder = null; + ardiscoveryService = null; + } + }).start(); + + + } + } + + private void initBroadcastReceiver() + { + ardiscoveryServicesDevicesListUpdatedReceiver = new ARDiscoveryServicesDevicesListUpdatedReceiver(this); + } + + private void initServiceConnection() + { + ardiscoveryServiceConnection = new ServiceConnection() + { + @Override + public void onServiceConnected(ComponentName name, IBinder service) + { + discoveryServiceBinder = service; + ardiscoveryService = ((ARDiscoveryService.LocalBinder) service).getService(); + ardiscoveryServiceBound = true; + + ardiscoveryService.start(); + } + + @Override + public void onServiceDisconnected(ComponentName name) + { + ardiscoveryService = null; + ardiscoveryServiceBound = false; + } + }; + } + + private void registerReceivers() + { + LocalBroadcastManager localBroadcastMgr = LocalBroadcastManager.getInstance(getApplicationContext()); + localBroadcastMgr.registerReceiver(ardiscoveryServicesDevicesListUpdatedReceiver, new IntentFilter(ARDiscoveryService.kARDiscoveryServiceNotificationServicesDevicesListUpdated)); + + } + + private void unregisterReceivers() + { + LocalBroadcastManager localBroadcastMgr = LocalBroadcastManager.getInstance(getApplicationContext()); + localBroadcastMgr.unregisterReceiver(ardiscoveryServicesDevicesListUpdatedReceiver); + } + + + @Override + public void onResume() + { + super.onResume(); + + Log.d(TAG, "onResume ..."); + + onServicesDevicesListUpdated(); + + registerReceivers(); + + initServices(); + + } + + @Override + public void onPause() + { + Log.d(TAG, "onPause ..."); + + unregisterReceivers(); + closeServices(); + + super.onPause(); + } + + @Override + public void onServicesDevicesListUpdated() + { + Log.d(TAG, "onServicesDevicesListUpdated ..."); + + List list; + + if (ardiscoveryService != null) + { + list = ardiscoveryService.getDeviceServicesArray(); + + deviceList = new ArrayList (); + List deviceNames = new ArrayList(); + + if(list != null) + { + for (ARDiscoveryDeviceService service : list) + { + Log.e(TAG, "service : "+ service + " name = " + service.getName()); + ARDISCOVERY_PRODUCT_ENUM product = ARDiscoveryService.getProductFromProductID(service.getProductID()); + Log.e(TAG, "product : "+ product); + // only display Bebop drones + if (ARDISCOVERY_PRODUCT_ENUM.ARDISCOVERY_PRODUCT_ARDRONE.equals(product)) + { + deviceList.add(service); + deviceNames.add(service.getName()); + } + } + } + + deviceNameList = deviceNames.toArray(new String[deviceNames.size()]); + + ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, deviceNameList); + + // Assign adapter to ListView + listView.setAdapter(adapter); + } + + } +} diff --git a/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/PilotingActivity.java b/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/PilotingActivity.java new file mode 100644 index 0000000..b151997 --- /dev/null +++ b/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/PilotingActivity.java @@ -0,0 +1,524 @@ +package com.parrot.bebopdronestreaming; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Intent; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.ImageView; +import android.graphics.Bitmap; + +import com.parrot.arsdk.arcommands.ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM; +import com.parrot.arsdk.ardiscovery.ARDiscoveryDeviceService; + +public class PilotingActivity extends Activity implements DeviceControllerListener +{ + private static String TAG = PilotingActivity.class.getSimpleName(); + public static String EXTRA_DEVICE_SERVICE = "pilotingActivity.extra.device.service"; + + public DeviceController deviceController; + public ARDiscoveryDeviceService service; + + private Button emergencyBt; + private Button takeoffBt; + private Button landingBt; + + private Button gazUpBt; + private Button gazDownBt; + private Button yawLeftBt; + private Button yawRightBt; + + private Button forwardBt; + private Button backBt; + private Button rollLeftBt; + private Button rollRightBt; + + private TextView batteryLabel; + + private ImageView streamImageView; + + private AlertDialog alertDialog; + + + + @Override + protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_piloting); + + emergencyBt = (Button) findViewById(R.id.emergencyBt); + emergencyBt.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) + { + if (deviceController != null) + { + deviceController.sendEmergency(); + } + } + }); + + takeoffBt = (Button) findViewById(R.id.takeoffBt); + takeoffBt.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) + { + if (deviceController != null) + { + deviceController.sendTakeoff(); + } + } + }); + landingBt = (Button) findViewById(R.id.landingBt); + landingBt.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) + { + if (deviceController != null) + { + deviceController.sendLanding(); + } + } + }); + + gazUpBt = (Button) findViewById(R.id.gazUpBt); + gazUpBt.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) + { + switch (event.getAction()) + { + case MotionEvent.ACTION_DOWN: + v.setPressed(true); + if (deviceController != null) + { + deviceController.setGaz((byte) 50); + } + break; + + case MotionEvent.ACTION_UP: + v.setPressed(false); + if (deviceController != null) + { + deviceController.setGaz((byte)0); + + } + break; + + default: + + break; + } + + return true; + } + }); + + gazDownBt = (Button) findViewById(R.id.gazDownBt); + gazDownBt.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) + { + switch (event.getAction()) + { + case MotionEvent.ACTION_DOWN: + v.setPressed(true); + if (deviceController != null) + { + deviceController.setGaz((byte)-50); + + } + break; + + case MotionEvent.ACTION_UP: + v.setPressed(false); + if (deviceController != null) + { + deviceController.setGaz((byte)0); + } + break; + + default: + + break; + } + + return true; + } + }); + yawLeftBt = (Button) findViewById(R.id.yawLeftBt); + yawLeftBt.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) + { + switch (event.getAction()) + { + case MotionEvent.ACTION_DOWN: + v.setPressed(true); + if (deviceController != null) + { + deviceController.setYaw((byte)-50); + + } + break; + + case MotionEvent.ACTION_UP: + v.setPressed(false); + if (deviceController != null) + { + deviceController.setYaw((byte) 0); + } + break; + + default: + + break; + } + + return true; + } + }); + yawRightBt = (Button) findViewById(R.id.yawRightBt); + yawRightBt.setOnTouchListener(new View.OnTouchListener() + { + @Override + public boolean onTouch(View v, MotionEvent event) + { + switch (event.getAction()) + { + case MotionEvent.ACTION_DOWN: + v.setPressed(true); + if (deviceController != null) + { + deviceController.setYaw((byte) 50); + + } + break; + + case MotionEvent.ACTION_UP: + v.setPressed(false); + if (deviceController != null) + { + deviceController.setYaw((byte) 0); + } + break; + + default: + + break; + } + + return true; + } + }); + + forwardBt = (Button) findViewById(R.id.forwardBt); + forwardBt.setOnTouchListener(new View.OnTouchListener() + { + @Override + public boolean onTouch(View v, MotionEvent event) + { + switch (event.getAction()) + { + case MotionEvent.ACTION_DOWN: + v.setPressed(true); + if (deviceController != null) + { + deviceController.setPitch((byte) 50); + deviceController.setFlag((byte) 1); + } + break; + + case MotionEvent.ACTION_UP: + v.setPressed(false); + if (deviceController != null) + { + deviceController.setPitch((byte) 0); + deviceController.setFlag((byte) 0); + } + break; + + default: + + break; + } + + return true; + } + }); + backBt = (Button) findViewById(R.id.backBt); + backBt.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) + { + switch (event.getAction()) + { + case MotionEvent.ACTION_DOWN: + v.setPressed(true); + if (deviceController != null) + { + deviceController.setPitch((byte)-50); + deviceController.setFlag((byte)1); + } + break; + + case MotionEvent.ACTION_UP: + v.setPressed(false); + if (deviceController != null) + { + deviceController.setPitch((byte)0); + deviceController.setFlag((byte)0); + } + break; + + default: + + break; + } + + return true; + } + }); + rollLeftBt = (Button) findViewById(R.id.rollLeftBt); + rollLeftBt.setOnTouchListener(new View.OnTouchListener() + { + @Override + public boolean onTouch(View v, MotionEvent event) + { + switch (event.getAction()) + { + case MotionEvent.ACTION_DOWN: + v.setPressed(true); + if (deviceController != null) + { + deviceController.setRoll((byte) -50); + deviceController.setFlag((byte) 1); + } + break; + + case MotionEvent.ACTION_UP: + v.setPressed(false); + if (deviceController != null) + { + deviceController.setRoll((byte) 0); + deviceController.setFlag((byte) 0); + } + break; + + default: + + break; + } + + return true; + } + }); + rollRightBt = (Button) findViewById(R.id.rollRightBt); + rollRightBt.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) + { + switch (event.getAction()) + { + case MotionEvent.ACTION_DOWN: + v.setPressed(true); + if (deviceController != null) + { + deviceController.setRoll((byte)50); + deviceController.setFlag((byte)1); + } + break; + + case MotionEvent.ACTION_UP: + v.setPressed(false); + if (deviceController != null) + { + deviceController.setRoll((byte)0); + deviceController.setFlag((byte)0); + } + break; + + default: + + break; + } + + return true; + } + }); + + batteryLabel = (TextView) findViewById(R.id.batteryLabel); + + streamImageView = (ImageView) findViewById(R.id.streamImageView); + + Intent intent = getIntent(); + service = intent.getParcelableExtra(EXTRA_DEVICE_SERVICE); + + deviceController = new DeviceController(this, service); + deviceController.setListener(this); + } + + @Override + public void onStart() + { + super.onStart(); + + if (deviceController != null) + { + final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(PilotingActivity.this); + + // set title + alertDialogBuilder.setTitle("Connecting ..."); + + + // create alert dialog + alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + + new Thread(new Runnable() { + @Override + public void run() + { + boolean failed = false; + + failed = deviceController.start(); + + runOnUiThread(new Runnable() { + @Override + public void run() + { + //alertDialog.hide(); + alertDialog.dismiss(); + } + }); + + if (failed) + { + finish(); + } + } + }).start(); + + } + } + + private void stopDeviceController() + { + if (deviceController != null) + { + final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(PilotingActivity.this); + + // set title + alertDialogBuilder.setTitle("Disconnecting ..."); + + // show it + runOnUiThread(new Runnable() { + @Override + public void run() { + // create alert dialog + alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + + new Thread(new Runnable() { + @Override + public void run() { + deviceController.stop(); + deviceController = null; + + runOnUiThread(new Runnable() { + @Override + public void run() { + //alertDialog.hide(); + alertDialog.dismiss(); + finish(); + } + }); + + } + }).start(); + } + }); + //alertDialog.show(); + + } + } + + @Override + protected void onStop() + { + if (deviceController != null) + { + deviceController.stop(); + deviceController = null; + } + + super.onStop(); + } + + @Override + public void onBackPressed() + { + stopDeviceController(); + } + + @Override + public void onDisconnect() + { + stopDeviceController(); + } + + @Override + public void onUpdateBattery(final byte percent) + { + runOnUiThread(new Runnable() { + @Override + public void run() { + batteryLabel.setText(String.format("%d%%", percent)); + } + }); + + } + + @Override + public void onFlyingStateChanged(final ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_ENUM state) + { + // on the UI thread, disable and enable buttons according to flying state + runOnUiThread(new Runnable() + { + @Override + public void run() + { + switch (state) { + case ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_LANDED: + takeoffBt.setEnabled(true); + landingBt.setEnabled(false); + break; + case ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_FLYING: + case ARCOMMANDS_ARDRONE3_PILOTINGSTATE_FLYINGSTATECHANGED_STATE_HOVERING: + takeoffBt.setEnabled(false); + landingBt.setEnabled(true); + break; + default: + // in all other cases, take of and landing are not enabled + takeoffBt.setEnabled(false); + landingBt.setEnabled(false); + break; + } + } + }); + } + + @Override + public void onUpdateStream(final Bitmap bitmap) + { + runOnUiThread(new Runnable() { + @Override + public void run() { + streamImageView.setImageBitmap(bitmap); + } + }); + + } + +} diff --git a/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/video/ARFrame.java b/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/video/ARFrame.java new file mode 100644 index 0000000..4a592ee --- /dev/null +++ b/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/video/ARFrame.java @@ -0,0 +1,201 @@ +package com.parrot.bebopdronestreaming.video; + +import java.nio.Buffer; + +import org.bytedeco.javacpp.DoublePointer; +import org.bytedeco.javacpp.avcodec; +import org.bytedeco.javacpp.avcodec.AVCodec; +import org.bytedeco.javacpp.avcodec.AVCodecContext; +import org.bytedeco.javacpp.avcodec.AVPacket; +import org.bytedeco.javacpp.avcodec.AVPicture; +import org.bytedeco.javacpp.BytePointer; +import org.bytedeco.javacpp.avutil; +import org.bytedeco.javacpp.PointerPointer; +import org.bytedeco.javacpp.avutil.AVFrame; +import org.bytedeco.javacpp.swscale; + +import org.bytedeco.javacv.Frame; +import org.bytedeco.javacv.FrameGrabber.ImageMode; + +/** + * Created by root on 5/27/15. + */ +public class ARFrame { + public static int IMAGE_WIDTH = 640; + public static int IMAGE_HEIGHT = 368; + public static int PIXEL_FORMAT = 3; + public static AVCodecContext video_c = null; + public static ImageMode IMAGE_MODE = ImageMode.COLOR; + public static boolean DEINTERLACE = true; + + private static String TAG = "ARFrame"; + + /*** data buffer ***/ + public byte[] rawData; + /*** size of the buffer ***/ + public int size; + /*** I-frame ***/ + /* Also known as key frames, I-frames are completely self-referential and don't use any information + * from any other frames. These are the largest frames, and the highest quality, but the least + * efficient from a compression perspective. */ + public boolean isFlushFrame; + /*** P-frame ***/ + /* P-frames are "predicted" frames. When producing a P-frame, the encoder can look backwards to + * previous I or P-frames for redundant picture information. */ + + /*** Frame ***/ + public Frame frame; + public int frameNo; + + public ARFrame(byte[] rawData, int dataSize, boolean isFlushFrame, int frameNo) { + this.rawData = rawData; + this.size = dataSize; + this.isFlushFrame = isFlushFrame; + this.frameNo = frameNo; + } + + public int getImageWidth() { + return IMAGE_WIDTH; + } + + public int getImageHeight() { + return IMAGE_HEIGHT; + } + + public ImageMode getImageMode() { + return IMAGE_MODE; + } + + public int getPixelFormat() { return PIXEL_FORMAT; } + + public Frame decodeFromVideo() { + frame = new Frame(); + AVFrame picture = null; + AVFrame picture_rgb = null; + AVPacket receivedVideoPacket = null; + swscale.SwsContext img_convert_ctx = null; + BytePointer[] image_ptr = new BytePointer[] { null }; + Buffer[] image_buf = new Buffer[] { null }; + + // Initialize receivedVideoPacket with byte[] rawData + receivedVideoPacket = new AVPacket(size); + receivedVideoPacket.data(new BytePointer(rawData)); + + //Initialize optional fields of a packet with default values. Excluding data and size information + avcodec.av_init_packet(receivedVideoPacket); + + //Allocates enough memory for the data array and copies it. + BytePointer videoData = new BytePointer(rawData); + + /*** I-Frame ***/ + if (isFlushFrame == true) { + + AVCodec codec = avcodec.avcodec_find_decoder(avcodec.AV_CODEC_ID_H264); + + if (codec != null) { + + //Allocate an AVCodecContext and set its fields to default values + video_c = avcodec.avcodec_alloc_context3(codec); + + video_c.width(getImageWidth()); + video_c.height(getImageHeight()); + //Pixel format, see AV_PIX_FMT_xxx.May be set by the demuxer if known from headers. May be overridden by the decoder if it knows better + video_c.pix_fmt(avutil.AV_PIX_FMT_YUV420P); + video_c.codec_type(avutil.AVMEDIA_TYPE_VIDEO); + video_c.extradata(videoData); + video_c.extradata_size(videoData.capacity()); + //encoding: Set by user | decoding: Set by user + video_c.flags2(video_c.flags2() | avcodec.CODEC_FLAG2_CHUNKS); + + //Initialize the AVCodecContext to use the given AVCodec. + avcodec.avcodec_open2(video_c, codec, (PointerPointer) null); + + } else { + return null; + } + } + + // First I-frame have not been received, exit decoding + if (video_c == null) { + return null; + } + + //old - Allocates an AVFrame and sets its fields to default values + //Allocate video frame and an AVFrame for the RGB image + if ((picture = avcodec.avcodec_alloc_frame()) == null) { + return null; + } + if ((picture_rgb = avcodec.avcodec_alloc_frame()) == null) { + return null; + } + + int width = getImageWidth(); + int height = getImageHeight(); + int fmt = getPixelFormat(); + + //old - Calculate the size in bytes that a picture of the given width and height would occupy if stored in the given picture format + //Determine required buffer size and allocate buffer + int size = avcodec.avpicture_get_size(fmt, width, height); + image_ptr = new BytePointer[] { new BytePointer(avutil.av_malloc(size)).capacity(size)}; + image_buf = new Buffer[] { image_ptr[0].asBuffer() }; + + //old - Setup the picture fields based on the specified image parameters and the provided image data buffer. + //Assign appropriate parts of buffer to image planes in picture rgb + //Note that picture_rgb is an AVFrame, but AVFrame is a superset of AVPicture + avcodec.avpicture_fill(new AVPicture(picture_rgb), image_ptr[0], fmt, width, height); + picture_rgb.format(fmt); + picture_rgb.width(width); + picture_rgb.height(height); + + receivedVideoPacket.data(videoData); + receivedVideoPacket.size(videoData.capacity()); + + int decodedFrameLength; + //Zero if no frame could be decompressed, otherwise, it is nonzero + int[] isVideoDecoded = new int[1]; + + //Decode the video frame of size avpkt->size from avpkt->data into picture. + //AVCodecContext avContext, AVFrame picture, int[] got_picture_ptr, AVPacket avpkt + decodedFrameLength = avcodec.avcodec_decode_video2(video_c, + picture, isVideoDecoded, receivedVideoPacket); + + // Did we get a video frame? + + if ((decodedFrameLength >= 0) && (isVideoDecoded[0] != 0)) { + + /*** Process image same as javacv ***/ + frame.imageWidth = video_c.width(); + frame.imageHeight = video_c.height(); + frame.imageDepth = Frame.DEPTH_UBYTE; + // AVFrame -> Frame + // Convert the image + + // Deinterlace the picture + /* + if (DEINTERLACE) { + AVPicture p = new AVPicture(picture); + avcodec.avpicture_deinterlace(p, p, video_c.pix_fmt(), video_c.width(), video_c.height()); + } + */ + + // Convert the image into BGR or GRAY format that OpenCV uses + img_convert_ctx = swscale.sws_getCachedContext(img_convert_ctx, video_c.width(), video_c.height(), video_c.pix_fmt(), + frame.imageWidth, frame.imageHeight, getPixelFormat(), swscale.SWS_BILINEAR, null, null, (DoublePointer)null); + if (img_convert_ctx == null) { + return null; + } + + //Convert the image from its native format to RGB or GRAY + swscale.sws_scale(img_convert_ctx, new PointerPointer(picture), picture.linesize(), 0, + video_c.height(), new PointerPointer(picture_rgb), picture_rgb.linesize()); + frame.imageStride = picture_rgb.linesize(0); + frame.image = image_buf; + + frame.image[0].limit(frame.imageHeight * frame.imageStride); + frame.imageChannels = frame.imageStride / frame.imageWidth; + } else { + return null; + } + return frame; + } +} \ No newline at end of file diff --git a/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/video/ARStreamManager.java b/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/video/ARStreamManager.java new file mode 100644 index 0000000..6f35135 --- /dev/null +++ b/Android/BebopDroneStreaming/app/src/main/java/com/parrot/bebopdronestreaming/video/ARStreamManager.java @@ -0,0 +1,177 @@ +package com.parrot.bebopdronestreaming.video; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import android.util.Log; +import android.graphics.Bitmap; + +import com.parrot.arsdk.arsal.ARNativeData; +import com.parrot.arsdk.arstream.ARSTREAM_READER_CAUSE_ENUM; +import com.parrot.arsdk.arstream.ARStreamReader; +import com.parrot.arsdk.arnetwork.ARNetworkManager; +import com.parrot.arsdk.arstream.ARStreamReaderListener; + +import org.bytedeco.javacv.AndroidFrameConverter; + + +/** + * Created by root on 5/27/15. + */ +public class ARStreamManager { + + public ARStreamReader streamReader; + public Thread videoRxThread; + public Thread videoTxThread; + public ARNativeData data; + public ARStreamReaderListener listener; + public static BlockingQueue frameQueue; + + public static int success = 0; + + private static String TAG = "ARStreamManager"; + + public ARStreamManager () + { + + } + + public ARStreamManager (ARNetworkManager netManager, + int iobufferD2cArstreamData, + int iobufferC2dArstreamAck, + int videoFragmentSize, + int videoMaxAckInterval) + { + frameQueue = new LinkedBlockingQueue(); + data = new ARNativeData(42000); + listener = new ARStreamReaderCallBack(frameQueue); + streamReader = new ARStreamReader(netManager, iobufferD2cArstreamData, + iobufferC2dArstreamAck, data, listener, videoFragmentSize, videoMaxAckInterval); + } + + public void startStream() + { + /* Create and start videoTx and videoRx threads */ + videoRxThread = new Thread (streamReader.getDataRunnable()); + videoRxThread.start(); + videoTxThread = new Thread (streamReader.getAckRunnable()); + videoTxThread.start(); + } + + public Bitmap getFrameWithTimeout(int video_receive_timeout) + { + if (frameQueue.size() == 0) { + try { + Thread.sleep(video_receive_timeout); + } catch (InterruptedException e) { + Log.e(TAG, "InterruptedException"); + } + return null; + } + + Bitmap bitmap = null; + ARFrame freeFrame = frameQueue.poll(); + + freeFrame.frame = freeFrame.decodeFromVideo(); + + if (freeFrame.frame != null) + { + AndroidFrameConverter converterToBitmap = new AndroidFrameConverter(); + bitmap = converterToBitmap.convert(freeFrame.frame); + + // Save to image file to see omg + /* + String file_path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/BebopDronePiloting"; + File dir = new File(file_path); + + if (!dir.exists()) { + dir.mkdirs(); + } + + try { + File file = new File(dir, "image_" + paddedFrameNo + ".png"); + FileOutputStream fOut = new FileOutputStream(file); + bitmap.compress(Bitmap.CompressFormat.PNG, 85, fOut); + fOut.flush(); + fOut.close(); + } catch (FileNotFoundException e) { + Log.e(TAG, "FileNotFoundException"); + } catch (IOException e) { + Log.e(TAG, "IOException "); + } + */ + } else { + Log.i(TAG, freeFrame.frameNo + ": failed"); + } + return bitmap; + } + + public void freeFrame(ARFrame frame) + { + //TODO + } + + public void stopStream() + { + streamReader.stop(); + } +} + +class ARStreamReaderCallBack implements ARStreamReaderListener +{ + private static String TAG = "ARStreamReaderCallBack"; + public static BlockingQueue frameQueue; + public static int count = 1; + + public ARStreamReaderCallBack () { } + + public ARStreamReaderCallBack (BlockingQueue frameQueue) { + this.frameQueue = frameQueue; + } + + /*** This method will be called by the system ***/ + @Override + public ARNativeData didUpdateFrameStatus(ARSTREAM_READER_CAUSE_ENUM cause, + ARNativeData currentFrame, + boolean isFlushFrame, + int nbSkippedFrames, + int newBufferCapacity) { + //Log.i(TAG, "didUpdateFrameStatus"); + //Log.i(TAG, "ARSTREAM_READER_CAUSE_ENUM: " + cause); + //Log.i(TAG, "ARNativeData: " + currentFrame); + //Log.i(TAG, "isFlushFrame: " + isFlushFrame); + //Log.i(TAG, "nbSkippedFrames: " + nbSkippedFrames); + //Log.i(TAG, "newBufferCapacity: " + newBufferCapacity); + //Log.i(TAG, "frames received: " + count); + switch (cause) + { + case ARSTREAM_READER_CAUSE_FRAME_COMPLETE: + ARFrame freeFrame = new ARFrame(currentFrame.getByteData(), currentFrame.getDataSize(), isFlushFrame, count++); + + /*** I-Frame ***/ + if (isFlushFrame) { + frameQueue.clear(); + } + + frameQueue.offer(freeFrame); + + return currentFrame; + + case ARSTREAM_READER_CAUSE_FRAME_TOO_SMALL: + /* This case should not happen, as we've allocated a frame pointer to the maximum possible size. */ + ARNativeData enlargedFrame = new ARNativeData(newBufferCapacity); + return enlargedFrame; + + case ARSTREAM_READER_CAUSE_COPY_COMPLETE: + /* Same as before ... but return value are ignored, so we just do nothing */ + return null; + + case ARSTREAM_READER_CAUSE_CANCEL: + /* Same as before ... but return value are ignored, so we just do nothing */ + return null; + + default: + return null; + } + } +} \ No newline at end of file diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libavcodec.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libavcodec.so new file mode 100644 index 0000000..8c28d19 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libavcodec.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libavdevice.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libavdevice.so new file mode 100644 index 0000000..4ab38fd Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libavdevice.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libavfilter.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libavfilter.so new file mode 100644 index 0000000..786ef7d Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libavfilter.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libavformat.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libavformat.so new file mode 100644 index 0000000..8dfa07a Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libavformat.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libavutil.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libavutil.so new file mode 100644 index 0000000..eb34681 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libavutil.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniavcodec.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniavcodec.so new file mode 100755 index 0000000..a2e0c6c Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniavcodec.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniavdevice.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniavdevice.so new file mode 100755 index 0000000..a043e01 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniavdevice.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniavfilter.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniavfilter.so new file mode 100755 index 0000000..159ef15 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniavfilter.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniavformat.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniavformat.so new file mode 100755 index 0000000..05462c3 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniavformat.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniavutil.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniavutil.so new file mode 100755 index 0000000..2e8f90c Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniavutil.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjnicvkernels.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjnicvkernels.so new file mode 100755 index 0000000..44ceaec Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjnicvkernels.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_calib3d.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_calib3d.so new file mode 100755 index 0000000..2182053 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_calib3d.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_contrib.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_contrib.so new file mode 100755 index 0000000..ea5e841 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_contrib.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_core.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_core.so new file mode 100755 index 0000000..d38c22c Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_core.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_features2d.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_features2d.so new file mode 100755 index 0000000..466cecc Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_features2d.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_flann.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_flann.so new file mode 100755 index 0000000..5c3d8f5 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_flann.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_highgui.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_highgui.so new file mode 100755 index 0000000..70e1a51 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_highgui.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_imgproc.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_imgproc.so new file mode 100755 index 0000000..466ea18 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_imgproc.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_legacy.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_legacy.so new file mode 100755 index 0000000..6c6cb71 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_legacy.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_ml.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_ml.so new file mode 100755 index 0000000..0526775 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_ml.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_nonfree.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_nonfree.so new file mode 100755 index 0000000..12cbc45 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_nonfree.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_objdetect.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_objdetect.so new file mode 100755 index 0000000..2a45adc Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_objdetect.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_photo.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_photo.so new file mode 100755 index 0000000..09f8165 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_photo.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_stitching.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_stitching.so new file mode 100755 index 0000000..473940a Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_stitching.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_superres.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_superres.so new file mode 100755 index 0000000..9373c90 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_superres.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_video.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_video.so new file mode 100755 index 0000000..6960368 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_video.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_videostab.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_videostab.so new file mode 100755 index 0000000..bc2dcca Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniopencv_videostab.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjnipostproc.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjnipostproc.so new file mode 100755 index 0000000..f202870 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjnipostproc.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniswresample.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniswresample.so new file mode 100755 index 0000000..912c66b Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniswresample.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniswscale.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniswscale.so new file mode 100755 index 0000000..be80a76 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libjniswscale.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r2.2.0.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r2.2.0.so new file mode 100644 index 0000000..aac6634 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r2.2.0.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r2.3.3.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r2.3.3.so new file mode 100644 index 0000000..d523f69 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r2.3.3.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r3.0.1.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r3.0.1.so new file mode 100644 index 0000000..e386bf4 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r3.0.1.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.0.0.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.0.0.so new file mode 100644 index 0000000..028ab7d Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.0.0.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.0.3.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.0.3.so new file mode 100644 index 0000000..48cbdd0 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.0.3.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.1.1.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.1.1.so new file mode 100644 index 0000000..7fe5087 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.1.1.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.2.0.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.2.0.so new file mode 100644 index 0000000..15827d8 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.2.0.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.3.0.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.3.0.so new file mode 100644 index 0000000..ec1edfb Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.3.0.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.4.0.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.4.0.so new file mode 100644 index 0000000..1bcd733 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libnative_camera_r4.4.0.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_calib3d.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_calib3d.so new file mode 100644 index 0000000..e20a501 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_calib3d.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_contrib.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_contrib.so new file mode 100644 index 0000000..2fcbac1 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_contrib.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_core.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_core.so new file mode 100644 index 0000000..a62b35f Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_core.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_features2d.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_features2d.so new file mode 100644 index 0000000..aaa8d9a Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_features2d.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_flann.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_flann.so new file mode 100644 index 0000000..90727e3 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_flann.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_gpu.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_gpu.so new file mode 100644 index 0000000..ebfa3e9 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_gpu.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_highgui.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_highgui.so new file mode 100644 index 0000000..cc57d33 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_highgui.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_imgproc.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_imgproc.so new file mode 100644 index 0000000..21a02fb Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_imgproc.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_legacy.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_legacy.so new file mode 100644 index 0000000..b905dd8 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_legacy.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_ml.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_ml.so new file mode 100644 index 0000000..b61042d Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_ml.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_nonfree.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_nonfree.so new file mode 100644 index 0000000..30915ef Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_nonfree.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_objdetect.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_objdetect.so new file mode 100644 index 0000000..ed347e9 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_objdetect.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_photo.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_photo.so new file mode 100644 index 0000000..2ef2934 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_photo.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_stitching.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_stitching.so new file mode 100644 index 0000000..380c447 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_stitching.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_superres.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_superres.so new file mode 100644 index 0000000..526bbd3 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_superres.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_video.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_video.so new file mode 100644 index 0000000..b62ff07 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_video.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_videostab.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_videostab.so new file mode 100644 index 0000000..1a2586d Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libopencv_videostab.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libpostproc.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libpostproc.so new file mode 100644 index 0000000..47a2401 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libpostproc.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libswresample.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libswresample.so new file mode 100644 index 0000000..550da71 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libswresample.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libswscale.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libswscale.so new file mode 100644 index 0000000..4b4b48d Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libswscale.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libtbb.so b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libtbb.so new file mode 100644 index 0000000..e0ee92e Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/jniLibs/armeabi-v7a/libtbb.so differ diff --git a/Android/BebopDroneStreaming/app/src/main/res/drawable-hdpi/ic_launcher.png b/Android/BebopDroneStreaming/app/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..96a442e Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/Android/BebopDroneStreaming/app/src/main/res/drawable-mdpi/ic_launcher.png b/Android/BebopDroneStreaming/app/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..359047d Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/Android/BebopDroneStreaming/app/src/main/res/drawable-xhdpi/ic_launcher.png b/Android/BebopDroneStreaming/app/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..71c6d76 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/Android/BebopDroneStreaming/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/Android/BebopDroneStreaming/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..4df1894 Binary files /dev/null and b/Android/BebopDroneStreaming/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/Android/BebopDroneStreaming/app/src/main/res/layout/activity_main.xml b/Android/BebopDroneStreaming/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..d5ac33e --- /dev/null +++ b/Android/BebopDroneStreaming/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/Android/BebopDroneStreaming/app/src/main/res/layout/activity_piloting.xml b/Android/BebopDroneStreaming/app/src/main/res/layout/activity_piloting.xml new file mode 100644 index 0000000..45bb253 --- /dev/null +++ b/Android/BebopDroneStreaming/app/src/main/res/layout/activity_piloting.xml @@ -0,0 +1,171 @@ + + + + + + +