YeongHwa Jin
YeongHwa Jin

Reputation: 455

Reading android asset file by JNI C++ is failed

I have made android app with jni C++ code.

To read model file while creating apk on android native code(C++), I placed the model file in assets folder. (so file path is my_app/src/main/assets/model.tflite)

Then, I tried to read the model with following code but an error has raised:

Kotlin code:

private val context: Context
    get() = getApplication<Application>().applicationContext

asset_manager = context.assets

C++ code:

AAssetManager* mgr = AAssetManager_fromJava(env, asset_manager);
AAssetDir* assetDir = AAssetManager_openDir(mgr, "");
const char* filename = (const char*)NULL;
while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL) {
    AAsset* asset = AAssetManager_open(mgr, filename, AASSET_MODE_STREAMING);
    __android_log_print(ANDROID_LOG_INFO, "tag", "file name is: %s", filename);  <- file name reading is ok
    char buf[BUFSIZ];
    int nb_read = 0;
    FILE* out = fopen(filename, "w");                            <- out has null FILE*
    while ((nb_read = AAsset_read(asset, buf, BUFSIZ)) > 0)
        fwrite(buf, nb_read, 1, out);
    fclose(out);
    AAsset_close(asset);
}
AAssetDir_close(assetDir);

logcat :

2022-06-19 22:45:54.449 12180-12180/com.my.first.app I/tag: file name is: model.tflite
2022-06-19 22:45:54.449 12180-12180/com.my.first.app A/libc: FORTIFY: fwrite: null FILE*
2022-06-19 22:45:54.450 12180-12180/com.my.first.app A/libc: Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 12180 (st.app), pid 12180 (st.app)

Why does fopen(filename, "w") return null? It seems that AAssetManager is work.

Upvotes: 1

Views: 1058

Answers (2)

Botje
Botje

Reputation: 30832

Looks like you want to combine this loading code to load the asset into a memory buffer and then use the BuildFromAllocation method:

std::unique_ptr<tflite::Allocation> readAsset(std::string target) {
  AAssetManager* mgr = AAssetManager_fromJava(env, asset_manager);
  AAssetDir* assetDir = AAssetManager_openDir(mgr, "");
  const char* filename = nullptr;
  while ((filename = AAssetDir_getNextFileName(assetDir)) != nullptr) {
    if (target != filename)
      continue;

    AAsset* asset = AAssetManager_open(mgr, filename, AASSET_MODE_STREAMING);
    auto len = AAsset_getLength(asset);
    uint8_t *buf = new uint8_t[len];
    std::copy_n(AAsset_getBuffer(asset), len, buf);
    auto ret = std::make_unique<tflite::Allocation>(buf , len);
    AAsset_close(asset);
    AAssetDir_close(assetDir);
    return ret;
  }
  AAssetDir_close(assetDir);
  return {};
}

and then use it later as:

auto ptr = readAssset("model.tflite");
if (!ptr) {
  // do something
}

auto modelPtr = tflite::FlatBufferModel::BuildFromAllocation(ptr, some_error_reporter);
if (!modelPtr) {
  // do something
}

Note: this will leak memory, as TFlite takes ownership of the buffer.

Upvotes: 1

YeongHwa Jin
YeongHwa Jin

Reputation: 455

Thanks to @Botje, I can load the model.tflite in assets by this way:

AAssetManager* mgr = AAssetManager_fromJava(env, asset_manager);
AAssetDir* assetDir = AAssetManager_openDir(mgr, "");
const char* filename = nullptr;
while ((filename = AAssetDir_getNextFileName(assetDir)) != nullptr) {
    AAsset* asset = AAssetManager_open(mgr, filename, AASSET_MODE_STREAMING);
    size_t size = AAsset_getLength(asset);
    buf = new char[size];
    memmove(buf, AAsset_getBuffer(asset), size);

    AAsset_close(asset);
  }
AAssetDir_close(assetDir);

std::unique_ptr<tflite::FlatBufferModel> model = tflite::FlatBufferModel::BuildFromBuffer((const char*)buf, size);
std::unique_ptr<tflite::Interpreter> interpreter;
tflite::ops::builtin::BuiltinOpResolver op_resolver;
tflite::InterpreterBuilder(*model, op_resolver)(&interpreter);

Upvotes: 1

Related Questions