android_audiotrack.c 15.8 KB
Newer Older
Z
Zhang Rui 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/*****************************************************************************
 * ijksdl_android_audiotrack.c
 *****************************************************************************
 *
 * copyright (c) 2013 Zhang Rui <bbcallen@gmail.com>
 *
 * This file is part of ijkPlayer.
 *
 * ijkPlayer is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * ijkPlayer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with ijkPlayer; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "android_audiotrack.h"

#include <assert.h>
Z
Zhang Rui 已提交
27
#include "ijkutil/ijkutil_android.h"
Z
Zhang Rui 已提交
28
#include "../ijksdl_inc_internal.h"
Z
Zhang Rui 已提交
29
#include "../ijksdl_audio.h"
30

Z
Zhang Rui 已提交
31 32
#ifdef SDLTRACE
#undef SDLTRACE
Z
Zhang Rui 已提交
33
#define SDLTRACE(...)
Z
Zhang Rui 已提交
34 35
#endif

36 37 38
typedef struct AudioChannelMapEntry {
    Uint8 sdl_channel;
    int android_channel;
Z
Zhang Rui 已提交
39 40
    const char *sdl_name;
    const char *android_name;
41 42
} AudioChannelMapEntry;
static AudioChannelMapEntry g_audio_channel_map[] = {
Z
Zhang Rui 已提交
43 44
    { 2, CHANNEL_OUT_STEREO, "2-chan", "CHANNEL_OUT_STEREO" },
    { 1, CHANNEL_OUT_MONO, "1-chan", "CHANNEL_OUT_MONO" },
45 46 47 48 49
};

typedef struct AudioFormatMapEntry {
    SDL_AudioFormat sdl_format;
    int android_format;
Z
Zhang Rui 已提交
50 51
    const char *sdl_name;
    const char *android_name;
52 53
} AudioFormatMapEntry;
static AudioFormatMapEntry g_audio_format_map[] = {
Z
Zhang Rui 已提交
54 55
    { AUDIO_S16SYS, ENCODING_PCM_16BIT, "AUDIO_S16SYS", "ENCODING_PCM_16BIT" },
    { AUDIO_U8, ENCODING_PCM_8BIT, "AUDIO_U8", "ENCODING_PCM_8BIT" },
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
};

static Uint8 find_sdl_channel(int android_channel)
{
    for (int i = 0; i < NELEM(g_audio_channel_map); ++i) {
        AudioChannelMapEntry *entry = &g_audio_channel_map[i];
        if (entry->android_channel == android_channel)
            return entry->sdl_channel;
    }
    return 0;
}

static int find_android_channel(int sdl_channel)
{
    for (int i = 0; i < NELEM(g_audio_channel_map); ++i) {
        AudioChannelMapEntry *entry = &g_audio_channel_map[i];
        if (entry->sdl_channel == sdl_channel)
            return entry->android_channel;
    }
    return CHANNEL_OUT_INVALID;
}

Z
Zhang Rui 已提交
78
static SDL_AudioFormat find_sdl_format(int android_format)
79 80 81 82 83 84
{
    for (int i = 0; i < NELEM(g_audio_format_map); ++i) {
        AudioFormatMapEntry *entry = &g_audio_format_map[i];
        if (entry->android_format == android_format)
            return entry->sdl_format;
    }
Z
Zhang Rui 已提交
85
    return AUDIO_INVALID;
86 87 88 89 90 91 92 93 94 95 96
}

static int find_android_format(int sdl_format)
{
    for (int i = 0; i < NELEM(g_audio_format_map); ++i) {
        AudioFormatMapEntry *entry = &g_audio_format_map[i];
        if (entry->sdl_format == sdl_format)
            return entry->android_format;
    }
    return ENCODING_INVALID;
}
Z
Zhang Rui 已提交
97 98 99 100 101 102 103 104 105

typedef struct SDL_AndroidAudioTrack {
    jobject thiz;

    SDL_AndroidAudioTrack_Spec spec;

    jbyteArray buffer;
    int buffer_capacity;
    int min_buffer_size;
Z
Zhang Rui 已提交
106 107
    float max_volume;
    float min_volume;
Z
Zhang Rui 已提交
108 109 110 111 112 113 114
} SDL_AndroidAudioTrack;

typedef struct audio_track_fields_t {
    jclass clazz;

    jmethodID constructor;
    jmethodID getMinBufferSize;
Z
Zhang Rui 已提交
115 116 117
    jmethodID getMaxVolume;
    jmethodID getMinVolume;

Z
Zhang Rui 已提交
118 119 120 121 122 123
    jmethodID play;
    jmethodID pause;
    jmethodID flush;
    jmethodID stop;
    jmethodID release;
    jmethodID write_byte;
Z
Zhang Rui 已提交
124
    jmethodID setStereoVolume;
Z
Zhang Rui 已提交
125 126 127 128 129
} audio_track_fields_t;
static audio_track_fields_t g_clazz;

int sdl_audiotrack_global_init(JNIEnv *env)
{
Z
Zhang Rui 已提交
130 131 132 133 134 135 136
    jclass clazz;

    clazz = (*env)->FindClass(env, "android/media/AudioTrack");
    IJK_CHECK_RET(clazz, -1, "missing AudioTrack");

    // FindClass returns LocalReference
    g_clazz.clazz = (*env)->NewGlobalRef(env, clazz);
Z
Zhang Rui 已提交
137 138
    IJK_CHECK_RET(g_clazz.clazz, -1, "AudioTrack NewGlobalRef failed");
    (*env)->DeleteLocalRef(env, clazz);
Z
Zhang Rui 已提交
139 140

    g_clazz.constructor = (*env)->GetMethodID(env, g_clazz.clazz, "<init>", "(IIIIII)V");
Z
Zhang Rui 已提交
141
    IJK_CHECK_RET(g_clazz.constructor, -1, "missing AudioTrack.<init>");
Z
Zhang Rui 已提交
142

Z
Zhang Rui 已提交
143 144
    g_clazz.getMinBufferSize = (*env)->GetStaticMethodID(env, g_clazz.clazz, "getMinBufferSize", "(III)I");
    IJK_CHECK_RET(g_clazz.getMinBufferSize, -1, "missing AudioTrack.getMinBufferSize");
Z
Zhang Rui 已提交
145

Z
Zhang Rui 已提交
146 147 148 149 150 151
    g_clazz.getMaxVolume = (*env)->GetStaticMethodID(env, g_clazz.clazz, "getMaxVolume", "()F");
    IJK_CHECK_RET(g_clazz.getMaxVolume, -1, "missing AudioTrack.getMaxVolume");

    g_clazz.getMinVolume = (*env)->GetStaticMethodID(env, g_clazz.clazz, "getMinVolume", "()F");
    IJK_CHECK_RET(g_clazz.getMinVolume, -1, "missing AudioTrack.getMinVolume");

Z
Zhang Rui 已提交
152 153
    g_clazz.play = (*env)->GetMethodID(env, g_clazz.clazz, "play", "()V");
    IJK_CHECK_RET(g_clazz.play, -1, "missing AudioTrack.play");
Z
Zhang Rui 已提交
154

Z
Zhang Rui 已提交
155 156
    g_clazz.pause = (*env)->GetMethodID(env, g_clazz.clazz, "pause", "()V");
    IJK_CHECK_RET(g_clazz.pause, -1, "missing AudioTrack.pause");
Z
Zhang Rui 已提交
157

Z
Zhang Rui 已提交
158
    g_clazz.flush = (*env)->GetMethodID(env, g_clazz.clazz, "flush", "()V");
159
    IJK_CHECK_RET(g_clazz.flush, -1, "missing AudioTrack.flush");
Z
Zhang Rui 已提交
160

Z
Zhang Rui 已提交
161
    g_clazz.stop = (*env)->GetMethodID(env, g_clazz.clazz, "stop", "()V");
162
    IJK_CHECK_RET(g_clazz.stop, -1, "missing AudioTrack.stop");
Z
Zhang Rui 已提交
163

Z
Zhang Rui 已提交
164
    g_clazz.release = (*env)->GetMethodID(env, g_clazz.clazz, "release", "()V");
165
    IJK_CHECK_RET(g_clazz.release, -1, "missing AudioTrack.release");
Z
Zhang Rui 已提交
166

Z
Zhang Rui 已提交
167
    g_clazz.write_byte = (*env)->GetMethodID(env, g_clazz.clazz, "write", "([BII)I");
168
    IJK_CHECK_RET(g_clazz.write_byte, -1, "missing AudioTrack.write");
Z
Zhang Rui 已提交
169

Z
Zhang Rui 已提交
170 171 172
    g_clazz.setStereoVolume = (*env)->GetMethodID(env, g_clazz.clazz, "setStereoVolume", "(FF)I");
    IJK_CHECK_RET(g_clazz.setStereoVolume, -1, "missing AudioTrack.setStereoVolume");

Z
Zhang Rui 已提交
173 174 175
    return 0;
}

176
static void sdl_audiotrack_get_default_spec(SDL_AndroidAudioTrack_Spec *spec)
Z
Zhang Rui 已提交
177 178 179 180 181 182 183 184 185 186
{
    assert(spec);
    spec->stream_type = STREAM_MUSIC;
    spec->sample_rate_in_hz = 0;
    spec->channel_config = CHANNEL_OUT_STEREO;
    spec->audio_format = ENCODING_PCM_16BIT;
    spec->buffer_size_in_bytes = 0;
    spec->mode = MODE_STREAM;
}

187 188
static int audiotrack_get_min_buffer_size(JNIEnv *env, SDL_AndroidAudioTrack_Spec *spec)
{
189
    SDLTRACE("audiotrack_get_min_buffer_size");
190 191 192 193 194
    int retval = (*env)->CallStaticIntMethod(env, g_clazz.clazz, g_clazz.getMinBufferSize,
        (int) spec->sample_rate_in_hz,
        (int) spec->channel_config,
        (int) spec->audio_format);
    if ((*env)->ExceptionCheck(env)) {
Z
Zhang Rui 已提交
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
        ALOGE("audiotrack_get_min_buffer_size: getMinBufferSize: Exception:");
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        return -1;
    }

    return retval;
}

static float audiotrack_get_max_volume(JNIEnv *env)
{
    SDLTRACE("audiotrack_get_max_volume");
    float retval = (*env)->CallStaticFloatMethod(env, g_clazz.clazz, g_clazz.getMaxVolume);
    if ((*env)->ExceptionCheck(env)) {
        ALOGE("audiotrack_get_max_volume: getMaxVolume: Exception:");
210 211 212 213 214 215 216
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        return -1;
    }

    return retval;
}
Z
Zhang Rui 已提交
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246

static float audiotrack_get_min_volume(JNIEnv *env)
{
    SDLTRACE("audiotrack_get_min_volume");
    float retval = (*env)->CallStaticFloatMethod(env, g_clazz.clazz, g_clazz.getMinVolume);
    if ((*env)->ExceptionCheck(env)) {
        ALOGE("audiotrack_get_min_volume: getMinVolume: Exception:");
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        return -1;
    }

    return retval;
}

static int audiotrack_set_stereo_volume(JNIEnv *env, SDL_AndroidAudioTrack *atrack, float left, float right)
{
    int retval = (*env)->CallIntMethod(env, atrack->thiz, g_clazz.setStereoVolume, left, right);
    if ((*env)->ExceptionCheck(env)) {
        ALOGE("audiotrack_set_stereo_volume: write_byte: Exception:");
        if ((*env)->ExceptionCheck(env)) {
            (*env)->ExceptionDescribe(env);
            (*env)->ExceptionClear(env);
        }
        return -1;
    }

    return retval;
}

247
SDL_AndroidAudioTrack *sdl_audiotrack_new_from_spec(JNIEnv *env, SDL_AndroidAudioTrack_Spec *spec)
Z
Zhang Rui 已提交
248 249 250
{
    assert(spec);

251 252
    switch (spec->channel_config) {
    case CHANNEL_OUT_MONO:
Z
Zhang Rui 已提交
253
        ALOGI("SDL_AndroidAudioTrack: %s", "CHANNEL_OUT_MONO");
254 255
        break;
    case CHANNEL_OUT_STEREO:
Z
Zhang Rui 已提交
256
        ALOGI("SDL_AndroidAudioTrack: %s", "CHANNEL_OUT_STEREO");
257 258 259 260 261 262 263 264
        break;
    default:
        ALOGE("sdl_audiotrack_new_from_spec: invalid channel %d", spec->channel_config);
        return NULL;
    }

    switch (spec->audio_format) {
    case ENCODING_PCM_16BIT:
Z
Zhang Rui 已提交
265
        ALOGI("SDL_AndroidAudioTrack: %s", "ENCODING_PCM_16BIT");
266 267
        break;
    case ENCODING_PCM_8BIT:
Z
Zhang Rui 已提交
268
        ALOGI("SDL_AndroidAudioTrack: %s", "ENCODING_PCM_8BIT");
269 270 271 272 273 274 275 276 277 278 279 280
        break;
    default:
        ALOGE("sdl_audiotrack_new_from_spec: invalid format %d", spec->audio_format);
        return NULL;
    }

    int min_buffer_size = audiotrack_get_min_buffer_size(env, spec);
    if (min_buffer_size <= 0) {
        ALOGE("sdl_audiotrack_new: sdl_audiotrack_get_min_buffer_size: return %d:", min_buffer_size);
        return NULL;
    }

Z
Zhang Rui 已提交
281
    jobject thiz = (*env)->NewObject(env, g_clazz.clazz, g_clazz.constructor,
Z
Zhang Rui 已提交
282 283
        (int) spec->stream_type, (int) spec->sample_rate_in_hz, (int) spec->channel_config,
        (int) spec->audio_format, (int) min_buffer_size, (int) spec->mode);
Z
Zhang Rui 已提交
284 285 286 287 288 289 290 291 292
    if (!thiz || (*env)->ExceptionCheck(env)) {
        ALOGE("sdl_audiotrack_new: NewObject: Exception:");
        if ((*env)->ExceptionCheck(env)) {
            (*env)->ExceptionDescribe(env);
            (*env)->ExceptionClear(env);
        }
        return NULL;
    }

Z
Zhang Rui 已提交
293
    SDL_AndroidAudioTrack *atrack = (SDL_AndroidAudioTrack*) mallocz(sizeof(SDL_AndroidAudioTrack));
294 295 296 297 298 299
    if (!atrack) {
        (*env)->CallVoidMethod(env, g_clazz.clazz, atrack->thiz, g_clazz.release);
        (*env)->DeleteLocalRef(env, thiz);
        return NULL;
    }

Z
Zhang Rui 已提交
300

301
    atrack->spec = *spec;
302
    atrack->min_buffer_size = min_buffer_size;
Z
Zhang Rui 已提交
303
    atrack->spec.buffer_size_in_bytes = min_buffer_size;
Z
Zhang Rui 已提交
304 305
    atrack->max_volume = audiotrack_get_max_volume(env);
    atrack->min_volume = audiotrack_get_min_volume(env);
306

Z
Zhang Rui 已提交
307 308 309
    atrack->thiz = (*env)->NewGlobalRef(env, thiz);
    (*env)->DeleteLocalRef(env, thiz);

Z
Zhang Rui 已提交
310 311 312 313 314
    // extra init
    float init_volume = 1.0f;
    init_volume = IJKMIN(init_volume, atrack->max_volume);
    init_volume = IJKMAX(init_volume, atrack->min_volume);
    ALOGI("sdl_audiotrack_new: init volume as %f/(%f,%f)", init_volume, atrack->min_volume, atrack->max_volume);
Z
Zhang Rui 已提交
315
    audiotrack_set_stereo_volume(env, atrack, init_volume, init_volume);
Z
Zhang Rui 已提交
316

Z
Zhang Rui 已提交
317 318 319
    return atrack;
}

320
SDL_AndroidAudioTrack *sdl_audiotrack_new_from_sdl_spec(JNIEnv *env, SDL_AudioSpec *sdl_spec)
Z
Zhang Rui 已提交
321
{
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
    SDL_AndroidAudioTrack_Spec atrack_spec;

    sdl_audiotrack_get_default_spec(&atrack_spec);
    atrack_spec.sample_rate_in_hz = sdl_spec->freq;
    atrack_spec.channel_config = find_android_channel(sdl_spec->channels);
    atrack_spec.audio_format = find_android_format(sdl_spec->format);
    atrack_spec.buffer_size_in_bytes = sdl_spec->size;

    return sdl_audiotrack_new_from_spec(env, &atrack_spec);
}

void sdl_audiotrack_free(JNIEnv *env, SDL_AndroidAudioTrack* atrack)
{
    if (atrack->buffer) {
        (*env)->DeleteGlobalRef(env, atrack->buffer);
        atrack->buffer = NULL;
Z
Zhang Rui 已提交
338
    }
339
    atrack->buffer_capacity = 0;
Z
Zhang Rui 已提交
340

341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
    if (atrack->thiz) {
        (*env)->DeleteGlobalRef(env, atrack->thiz);
        atrack->thiz = NULL;
    }

    free(atrack);
}

void sdl_audiotrack_get_target_spec(SDL_AndroidAudioTrack *atrack, SDL_AudioSpec *sdl_spec)
{
    SDL_AndroidAudioTrack_Spec *atrack_spec = &atrack->spec;

    sdl_spec->freq = atrack_spec->sample_rate_in_hz;
    sdl_spec->channels = find_sdl_channel(atrack_spec->channel_config);
    sdl_spec->format = find_sdl_format(atrack_spec->audio_format);
    sdl_spec->size = atrack_spec->buffer_size_in_bytes;
    sdl_spec->silence = 0;
    sdl_spec->padding = 0;
}

int sdl_audiotrack_get_min_buffer_size(SDL_AndroidAudioTrack* atrack)
{
    return atrack->min_buffer_size;
Z
Zhang Rui 已提交
364 365 366 367
}

void sdl_audiotrack_play(JNIEnv *env, SDL_AndroidAudioTrack *atrack)
{
368 369
    SDLTRACE("sdl_audiotrack_play");
    (*env)->CallVoidMethod(env, atrack->thiz, g_clazz.play);
Z
Zhang Rui 已提交
370 371 372 373 374 375 376 377 378 379
    if ((*env)->ExceptionCheck(env)) {
        ALOGE("sdl_audiotrack_play: play: Exception:");
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        return;
    }
}

void sdl_audiotrack_pause(JNIEnv *env, SDL_AndroidAudioTrack *atrack)
{
380 381
    SDLTRACE("sdl_audiotrack_pause");
    (*env)->CallVoidMethod(env, atrack->thiz, g_clazz.pause);
Z
Zhang Rui 已提交
382 383 384 385 386 387 388 389 390 391
    if ((*env)->ExceptionCheck(env)) {
        ALOGE("sdl_audiotrack_pause: pause: Exception:");
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        return;
    }
}

void sdl_audiotrack_flush(JNIEnv *env, SDL_AndroidAudioTrack *atrack)
{
392
    SDLTRACE("sdl_audiotrack_flush");
Z
Zhang Rui 已提交
393
    (*env)->CallVoidMethod(env, atrack->thiz, g_clazz.flush);
394
    SDLTRACE("sdl_audiotrack_flush()=void");
Z
Zhang Rui 已提交
395 396 397 398 399 400 401 402 403 404
    if ((*env)->ExceptionCheck(env)) {
        ALOGE("sdl_audiotrack_flush: flush: Exception:");
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        return;
    }
}

void sdl_audiotrack_stop(JNIEnv *env, SDL_AndroidAudioTrack *atrack)
{
405 406
    SDLTRACE("sdl_audiotrack_stop");
    (*env)->CallVoidMethod(env, atrack->thiz, g_clazz.stop);
Z
Zhang Rui 已提交
407 408 409 410 411 412 413 414 415 416
    if ((*env)->ExceptionCheck(env)) {
        ALOGE("sdl_audiotrack_stop: stop: Exception:");
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        return;
    }
}

void sdl_audiotrack_release(JNIEnv *env, SDL_AndroidAudioTrack *atrack)
{
417 418
    SDLTRACE("sdl_audiotrack_release");
    (*env)->CallVoidMethod(env, atrack->thiz, g_clazz.release);
Z
Zhang Rui 已提交
419 420 421 422 423 424 425 426 427 428 429 430 431 432
    if ((*env)->ExceptionCheck(env)) {
        ALOGE("sdl_audiotrack_release: release: Exception:");
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        return;
    }
}

int sdl_audiotrack_reserve_buffer(JNIEnv *env, SDL_AndroidAudioTrack *atrack, int len)
{
    if (atrack->buffer && len <= atrack->buffer_capacity)
        return len;

    if (atrack->buffer) {
Z
Zhang Rui 已提交
433
        (*env)->DeleteGlobalRef(env, atrack->buffer);
Z
Zhang Rui 已提交
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
        atrack->buffer = NULL;
        atrack->buffer_capacity = 0;
    }

    int capacity = IJKMAX(len, atrack->min_buffer_size);
    jbyteArray buffer = (*env)->NewByteArray(env, capacity);
    if (!buffer || (*env)->ExceptionCheck(env)) {
        ALOGE("sdl_audiotrack_reserve_buffer: NewByteArray: Exception:");
        if ((*env)->ExceptionCheck(env)) {
            (*env)->ExceptionDescribe(env);
            (*env)->ExceptionClear(env);
        }
        return -1;
    }

    atrack->buffer_capacity = capacity;
    atrack->buffer = (*env)->NewGlobalRef(env, buffer);
    (*env)->DeleteLocalRef(env, buffer);
    return capacity;
}

int sdl_audiotrack_write_byte(JNIEnv *env, SDL_AndroidAudioTrack *atrack, uint8_t *data, int len)
{
    if (len <= 0)
        return len;

    int reserved = sdl_audiotrack_reserve_buffer(env, atrack, len);
461 462
    if (reserved < len) {
        ALOGE("sdl_audiotrack_reserve_buffer failed %d < %d", reserved, len);
Z
Zhang Rui 已提交
463
        return -1;
464
    }
Z
Zhang Rui 已提交
465 466 467 468 469 470 471 472 473 474 475 476 477 478

    (*env)->SetByteArrayRegion(env, atrack->buffer, 0, len, (jbyte*) data);
    if ((*env)->ExceptionCheck(env)) {
        ALOGE("sdl_audiotrack_write_byte: SetByteArrayRegion: Exception:");
        if ((*env)->ExceptionCheck(env)) {
            (*env)->ExceptionDescribe(env);
            (*env)->ExceptionClear(env);
        }
        return -1;
    }

    int retval = (*env)->CallIntMethod(env, atrack->thiz, g_clazz.write_byte,
        atrack->buffer, 0, len);
    if ((*env)->ExceptionCheck(env)) {
479
        ALOGE("sdl_audiotrack_write_byte: write_byte: Exception:");
Z
Zhang Rui 已提交
480 481 482 483 484 485 486 487 488
        if ((*env)->ExceptionCheck(env)) {
            (*env)->ExceptionDescribe(env);
            (*env)->ExceptionClear(env);
        }
        return -1;
    }

    return retval;
}