使用JNI仅将本机代码的一部分缓冲区复制到Java

时间:2011-06-08 17:28:14

标签: java c java-native-interface byte bytearray

我在C中有一个非常大的char缓冲区,需要将它的一部分复制到Java数组中。

具体来说,我需要从16,384开始到32000结束的元素。我该怎么做?

最初我试过这个:

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); 

但是使用此代码传输整个缓冲区,并且它非常大(超过40 MB)。我只需要一小部分缓冲区。

编辑: 非常感谢,但你的版本不太合适。我应用如下: memmove(array,(jbyte *)(buffer + numbers),16385); 在缓冲区中 - 在每次迭代时将复制到“缓冲区”新的16384字节。 这些数字分别表示缓冲区和数字: 16384 - 0

32,768 - 16,385

49152 - 32769

65536 - 49153

81,920 - 65,537

98,304 - 81,921

即每次迭代轮流在右侧列的“数字”中。 因此, - 复制的字节并不总是成功。第一次迭代总是成功的。此外,轮流既成功又不成功。 memtcpy给出了相同的结果。会有什么建议?如何解决问题?

EDIT2: 我的代码:

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 




} 

使用SoX解码音频并用Java提供缓冲区。

2 个答案:

答案 0 :(得分:3)

如果您只想要Java数组的一部分,请查看JNI方法Get<PrimitiveType>ArrayRegionRelease<PrimitiveType>ArrayRegion

如果只想要C ++数组的一部分,可以在memmove中传递不同的起始地址和长度。

答案 1 :(得分:2)

为什么不呢:

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

对目标Java数组进行适当的更改,并对C ++缓冲区进行 适当的边界检查

顺便说一下:我的C / C ++生锈了,但如果你知道没有重叠的内存,那么memcpy的效率会比memmove更高效吗?

编辑2011-06-13

查看您发布的完整功能,仍然不完全清楚您真正打算做什么。但是,我可以看到发布的几个潜在问题,其中最重要的是它似乎不应该编译。我将让你跟进这些观察结果,并纠正代码或排除它们(回想起我已经有一段时间了,因为我编写了C全时,所以在每次观察时我可能都不完全准确。)

  1. 该函数将环境指针声明为JNIEnv *env,但是有几个JNI调用使用(*Env)而不是*env - 如果这完全编译,则可能是错误,即使指针恰好是有效的,它是 可怕的 形式 - 使用函数调用中传递的环境指针。
  2. 除非sox_open_memstream_write分配buffer指针无效。
  3. 构造了字节数组bytearrayBuffer但从未使用过。
  4. free(buffer)释放从未分配的内存。
  5. 循环正在执行memmovearray这是一个jbyteArray,并且不是从JNI锁定数组以进行读/写返回的指针(使用,例如GetByteArrayElements)。我希望C编译器发出一个点播警告。
  6. (*Env) -> GetByteArrayElements (env, array, NULL);的返回未被使用;因此,您没有指向数组内容的有效指针, 您泄漏了内存,因为未使用ReleaseByteArrayElements发布Java数组。
  7. 使用numbers似乎不合逻辑;第一次通过该值将为0,然后通过它将是buffer_size,之后它将从缓冲区的末尾开始索引。
  8. 调用(*Env) -> CallStaticVoidMethod (env, cls, mid, buffer_size);似乎没有任何用处。如果它要告诉Java有关缓冲区大小的信息,那么要么返回值,要么创建一个完全相同大小的Java数组大小。
  9. 除了错误地访问Java数组之外,我认为问题的核心可能是您需要的

    numbers + = number_read;
    

    而不是

    numbers + = buffer_size-numbers;
    

    假设缓冲区由sox_open_memstream_write创建,并且创建的大小足以写入整个输出(这似乎是荒谬的,因为API的结构似乎表明读取输入块并将处理后的输出写入临时输出缓冲区每次写入重用哪个输出缓冲区。

    也许循环应该是这样的:

    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:请停止向我发送电子邮件 - 如果我愿意,我会在此回复)