开发者

Copying only a part of a buffer from native code to Java using JNI

开发者 https://www.devze.com 2023-03-11 18:24 出处:网络
I have a very large char buffer in C and need to copy some part of it to a Java array. Specifically, I need the elements starting at 16,384 and ending at 32000. How can I do this?

I have a very large char buffer in C and need to copy some part of it to a Java array.

Specifically, I need the elements starting at 16,384 and ending at 32000. How can I do this?

Initially I tried this:

jbyte * bytes = (* env) -> GetByteArrayElements (env, array, NULL); 
memmove (bytes, (jbyte *) buffer, buffer_size);
(* Env) -> ReleaseByteArrayElements (env, array, (jbyte *) bytes, 0); 
(* Env) -> CallStaticVoidMethod (env, cls, mid, buffer_size); 

But with this code the entire buffer is transmitted, and it is very big (more than 40 MB). I need only a small part of buffer.

EDIT: Thank you very much, but your version is not quite working. I applied it as follows: memmove (array, (jbyte *) (buffer + numbers), 16385); in the buffer - at each iteration is copied to the "buffer" new 16384 bytes. such figures in the amount of buffer and numbers, respectively: 16384 - 0

32,768 - 16,385

49152 - 32769

65536 - 49153

81,920 - 65,537

98,304 - 81,921

Ie at each iteration by turns in the "numbers" of right-hand column. As a result, - bytes are copied are not always successful. the first iteration is always successful. Further, by turns both successful and unsuccessful. memtcpy gives the same result. What will advise? How to solve the problem?

EDIT2: My code:

JNIEXPORT jint work (JNIEnv * env, jobject obj, jbyteArray array) 
{ 
int argc; 
char * args [3]; 
char * argv [3]; 
argv [1] = "Music/Tg.mp3"; 
argv [2] = "testwavS3.flac"; 
argc = 3; 
sox_effects_chain_t * chain; 
sox_effect_t * e; 
static sox_format_t * in, * out; / * input and output files开发者_开发知识库 * / 

         char * buffer; 
         size_t buffer_size; 

         size_t number_read; 



/ * All libSoX applications must start by initialising the SoX library * / 
sox_init (); 

/ * Open the input file (with default parameters) * / 
in = sox_open_read (argv [1], NULL, NULL, NULL); 
# Define MAX_SAMPLES (size_t) 8192 
__android_log_write (ANDROID_LOG_ERROR, "Read", "Haha"); 
sox_sample_t samples [MAX_SAMPLES]; / * Temporary store whilst copying. * / 


jclass cls; 
jmethodID mid, mid2; 
cls = (* env) -> GetObjectClass (env, obj); 
mid = (* env) -> GetStaticMethodID (env, cls, "testt", 
"(I) V"); 

jbyteArray bytearrayBuffer = (* env) -> NewByteArray (env, & in-> signal.length); / / construct a new byte array 
out = sox_open_memstream_write (& buffer, & buffer_size, & in-> signal, NULL, "sox", NULL); 
int numbers = 0; 

in-> encoding.bits_per_sample = 16; 
out-> encoding.bits_per_sample = 16; 
(* Env) -> GetByteArrayElements (env, array, NULL); 
while (number_read = sox_read (in, samples, MAX_SAMPLES)) { 

sox_write (out, samples, number_read); 

memmove (array, (jbyte *) (buffer + numbers), (buffer_size-numbers)); 


numbers + = buffer_size-numbers; 

(* Env) -> CallStaticVoidMethod (env, cls, mid, buffer_size); 



} 

          sox_close (out); 

          sox_close (in); 

        # If! Defined FIXED_BUFFER 
          free (buffer); 
        # Endif 




} 

using SoX to decode the audio and give buffer in Java.


If you want only a part of the Java array, look at the JNI methods Get<PrimitiveType>ArrayRegion and Release<PrimitiveType>ArrayRegion.

If you want only a part of the C++ array, you can pass a different starting address and length in memmove.


Why not just:

memmove(bytes, (jbyte*)(buffer+16384),(32001-16384));

with appropriate changes to the target Java array, and appropriate bounds checking of the C++ buffer.

As an aside: My C/C++ is rusty, but is not memcpy more efficient than memmove if you know you don't have overlapping memory?

Edit 2011-06-13

Looking over the full function that you posted, it's still not entirely clear what you are really intending to do. However, I can see several potential problems in what is posted, not the least of which is that it appears it should not compile. I will leave you to follow up on these observations and either correct the code or rule them out (recall it's been a while since I coded C full-time, so I may not be entirely accurate in every observation).

  1. The function declares the environment pointer as JNIEnv *env, but several of the JNI calls use (*Env) instead of *env - if this compiles at all, it's probably a mistake, and even if the pointer happens to be valid, it's horrible form - use the environment pointer passed in the function call.
  2. Unless sox_open_memstream_write allocates buffer, the pointer is not valid.
  3. The byte array bytearrayBuffer is constructed but never used.
  4. The free(buffer) releases memory that was never allocated.
  5. The loop is doing memmove to array which is a jbyteArray and is not a pointer returned from locking the array for read/write from JNI (using, e.g. GetByteArrayElements). I would have expect a point cast warning from the C compiler.
  6. The return of (*Env) -> GetByteArrayElements (env, array, NULL); is not being used; therefore you have no valid pointer to the array contents, and you have leaked memory since the Java array is not released using ReleaseByteArrayElements.
  7. The use of numbers seems illogical; the first time through the value will be 0, then next time through it will be buffer_size, and after that it will be indexing off the end of the buffer.
  8. The invocation (*Env) -> CallStaticVoidMethod (env, cls, mid, buffer_size); does not appear to be able to anything useful. If it's meant to tell Java about the buffer size, then either return the value or create a Java array size of exactly that size.

Apart from the Java array being incorrectly accessed, I think the heart of your problem could be that you need:

numbers + = number_read;

instead of

numbers + = buffer_size-numbers;

assuming the buffer is created by sox_open_memstream_write and is created large enough to write the entire output (which seems nonsensical since the structure of the API would seem to indicate reading chunks of input and writing processed output to a temporary output buffer which output buffer is being reused for each write.

Maybe the loop should be something like:

for(int ofs=0,number_read; (number_read=sox_read(in,samples,MAX_SAMPLES))!=0; ofs+=number_read) {
    sox_write(out,samples,number_read);
    if(ofs>=16,384 && ofs<32000) {                          // from 16384
        memcpy(jvatgt,buffer,min(number_read,(32000-ofs)); // to 32000 (see question)
        }
    //(*Env) -> CallStaticVoidMethod (env, cls, mid, buffer_size); WTH??
    }

(PS: Please stop emailing me - I will respond here when and if I feel like it)

0

精彩评论

暂无评论...
验证码 换一张
取 消