Копирование только части буфера из нативного кода в Java с использованием JNI - PullRequest
1 голос
/ 08 июня 2011

У меня очень большой буфер символов в C, и мне нужно скопировать некоторую его часть в массив 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 МБ). Мне нужна только небольшая часть буфера.

EDIT: Большое спасибо, но ваша версия не совсем работает. Я применил это следующим образом: memmove (массив, (jbyte *) (буфер + числа), 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 ]

3 голосов
/ 08 июня 2011

Если вам нужна только часть массива Java, посмотрите на методы JNI Get<PrimitiveType>ArrayRegion и Release<PrimitiveType>ArrayRegion.

Если вам нужна только часть массива C ++, вы можете передать другой начальный адрес и длину в memmove.

2 голосов
/ 08 июня 2011

Почему бы просто:

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. Цикл выполняетсяmemmove до array, который является jbyteArray и не является указателем, возвращаемым из блокировки массива для чтения / записи из JNI (используя, например, GetByteArrayElements).Я бы ожидал предупреждение о приведении точки от компилятора C.
  6. Возвращение (*Env) -> GetByteArrayElements (env, array, NULL); не используется;следовательно, у вас нет действительного указателя на содержимое массива, и у вас произошла утечка памяти, поскольку массив Java не освобождается с использованием ReleaseByteArrayElements.
  7. Использование numbers кажется нелогичным;первый раз через значение будет 0, затем в следующий раз будет buffer_size, и после этого он будет индексировать конец буфера.
  8. Вызов (*Env) -> CallStaticVoidMethod (env, cls, mid, buffer_size); не отображаетсябыть в состоянии что-нибудь полезное.Если он предназначен для того, чтобы сообщить Java о размере буфера, то либо верните значение, либо создайте размер массива Java именно этого размера.

Помимо неправильного доступа к массиву 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: пожалуйста, перестаньте писать мне по электронной почте - я отвечу здесь, когда и если мне так захочется)

...