Search code examples
javaandroidcjava-native-interface

Having an OutputStream write in a C buffer using JNI


I am trying to process DNG files (which I get from DngCreator) in my C program. I don't want to save it to disk in Java and then load it from C, because it wastes time and wears down the flash memory (I need about 40 DNGs per session, so that's over 800 MB). I am not very familiar with Java, but is there a way to override OutputStream and add my own JNI call?


Solution

  • Assuming you have a DNG creation function that can work with an OutputStream, you could instantiate a ByteArrayOutputStream, pass it as a parameter to your DNG creation function, and later get the byte array with a method call toByteArray().

    This can then be passed to a native C library.

    In Code this could look like this:

    package com.software7.jni;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import static java.nio.charset.StandardCharsets.UTF_8;
    
    public class Main {
        private native void transferDng(byte[] data);
    
        private void someDNGCreationFunction(OutputStream outputStream)
                throws IOException {
            outputStream.write("hello world!".getBytes(UTF_8));
        }
    
        private void test() throws IOException {
            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                someDNGCreationFunction(baos);
                transferDng(baos.toByteArray());
            }
        }
    
        public static void main(String[] args) {
            System.loadLibrary("Native");
            Main m = new Main();
            try {
                m.test();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    The native side could then retrieve the data from the byte array, e.g. see this fine answer https://stackoverflow.com/a/38787614/2331445.

    #include <string.h>
    #include <stdlib.h>
    #include <jni.h>
    #include "com_software7_jni_Main.h"
    
    
    JNIEXPORT void JNICALL Java_com_software7_jni_Main_transferDng
      (JNIEnv *env, jobject obj, jbyteArray dng) {
        jsize num_bytes = (*env)->GetArrayLength(env, dng);
        jbyte *b = (*env)->GetByteArrayElements(env, dng, 0);
        char *buffer = malloc(num_bytes);
        memcpy(buffer, b, num_bytes);
        (*env)->ReleaseByteArrayElements(env, dng, b, 0);
        //do something with the buffer, here a printf only for testing 
        printf("%.*s", num_bytes, buffer);
        free(buffer);
        return;
    }