How to save data to the ML2 Filesystem

ML2 OS version: 1.4.1
MLSDK version: 1.5.0
Host OS: MacOS

Hello,

I am currently developing my first App for the ML2 and struggle at some points. Currently I try saving the Frames from the Depth Camera and the Data from the regular Camera (RGB) to the local Filesystem on the ML2. However I have not yet been very successful with the standard c++ way of using ofstream, as my file.is_open() is always false.

So I am wondering if this is the right way to do it and I am just missing something or if there is another way to do this. Hence, Is this the right way to do it or is there another way? And, Is there some guide in which this is explained or a sample to which I can look for help?

Saving frames as images in an Android app is a common task, but it's important to use the appropriate methods provided by the Android framework rather than standard C++ techniques like ofstream. Android has specific file system access rules, especially starting with Android 10 (API level 29) due to scoped storage, which can prevent traditional file I/O from working as expected.

Here's a general approach to saving images on Android:

    • Request the necessary permissions - in your AndroidManifest.xml file, such as WRITE_EXTERNAL_STORAGE
  1. Use Android's MediaStore API to save images. This API provides a standard way to save images to the shared storage without needing to request file permissions.
  2. Handle file paths and URIs correctly. Android 10 introduces scoped storage, which means you can't directly access file paths. Instead, you should use URIs provided by the MediaStore API.

For more detailed guidance, you might want to check out Android's official documentation or look for tutorials on using the MediaStore API to save images.

Thank you for your response! However, as this is not only my first ML App, but my first Android App in general I still struggle to use the MediaStorage API. From what I found out it appears that I have to use JNI. Correct? Because I struggle to integrate JNI into my Project. Especially using/calling JavaVM and JNIEnv. Can you provide me any insights in how to properly integrate this in the ML App structure?

You may have luck finding more information regarding file saving and integrating JNI into your Android project on Android's forums and documentation. Note, you do not have to use scoped storage if you are saving to your local application path.

Here is some background information on understanding JavaVM and JNIEnv

  • JavaVM: This represents the Java Virtual Machine within your native code. A JavaVM pointer allows you to start your Java code from your C++. You'll usually obtain this pointer from within a JNI function.
  • JNIEnv: This is an interface pointer that provides the functionality for interacting with the Java environment. You'll use JNIEnv to perform actions like:
    • Finding and calling Java classes/methods.
    • Converting between Java and C++ data types.
    • Accessing Java objects.

Additional Resources:

Add C and C++ code to your project | Android Studio | Android Developers
JNI · android/ndk Wiki · GitHub
JNI tips | Android NDK | Android Developers

Regarding writing files:

This is just a brief overview but if you want to access local app storage you can do so using JNIEnv inside the C++ class. (pseudo code)

std::string getInternalFilesDir() {
    JNIEnv* env = getJNIEnv();  // Obtain JNI environment
    jobject context = getContext(); // Get the application context

    jmethodID getFilesDirId = env->GetMethodID(env->GetObjectClass(context), 
                                               "getFilesDir", "()Ljava/io/File;");
    jobject filesDir = env->CallObjectMethod(context, getFilesDirId); 

    jmethodID getAbsolutePath = env->GetMethodID(env->GetObjectClass(filesDir), 
                                                 "getAbsolutePath", "()Ljava/lang/String;");
    jstring pathStr = (jstring)env->CallObjectMethod(filesDir, getAbsolutePath);

    const char* nativePath = env->GetStringUTFChars(pathStr, 0);
    std::string filePath = nativePath;
    env->ReleaseStringUTFChars(pathStr, nativePath);

    return filePath;
}

Then you could use it inside your application like so:

// ... include statements from above ...

int main() {

  // ... existing code ...

  // Get and construct filepath
  std::string internalFilesDir = getInternalFilesDir();
  std::string filePath = internalFilesDir + "/my_text_file.txt";

  // Write to file
  std::ofstream outFile(filePath);
  if (outFile.is_open()) {
      outFile << "This is the content of my text file.\n";
      outFile.close();
  } else {
      // Handle error, unable to open file
  } 

  // ... rest of your application code ...

}

Thanks a lot for the explanation. It seems I was really close with what I tried, but my main Issue with calling/obtaining the JavaVm and JNIEnv remains. If I want to use them I need to get them from somewhere and I cannot figure out how to do this. Sadly most resources on the internet are for integrating native code in the java application, but not calling java methods from within native code.

My main issue is with, what is in your Pseudocode as getJNIEnv() and getContext(). I cannot just create/initialize a new environment and also my experiments with JNIEXPORT and JNICALL have not yielded any results. So how do I obtain those? Could you provide me more Info on how to do this? This would be much appreciated

It should be fine to use stl classes to write to a file. (std::ofstream) How are you implementing your application? Are you building a native application using Android's standard NativeActivity? Assuming you're using NativeActivity, the implementation of android_native_app_glue should provide a reference to an android_app struct in the android_main callback function. android_app Struct Reference  |  Android Developers

You should be able to get a pointer to JNIEnv and even the app's external data path as a string from the config on the android_app struct.
https://developer.android.com/reference/games/game-activity/struct/game-activity#externaldatapath

1 Like