diff options
Diffstat (limited to 'audio/out/ao_audiotrack.c')
-rw-r--r-- | audio/out/ao_audiotrack.c | 548 |
1 files changed, 277 insertions, 271 deletions
diff --git a/audio/out/ao_audiotrack.c b/audio/out/ao_audiotrack.c index fbbdaa1c7a..db1da9c407 100644 --- a/audio/out/ao_audiotrack.c +++ b/audio/out/ao_audiotrack.c @@ -54,32 +54,29 @@ struct priv { jfloatArray floatarray; jobject bbuf; - int cfg_pcm_float; + bool cfg_pcm_float; int cfg_session_id; - bool needs_timestamp_offset; - int64_t timestamp_offset; - bool thread_terminate; bool thread_created; - pthread_t thread; - pthread_mutex_t lock; - pthread_cond_t wakeup; + mp_thread thread; + mp_mutex lock; + mp_cond wakeup; }; -struct JNIByteBuffer { +static struct JNIByteBuffer { jclass clazz; jmethodID clear; - struct MPJniField mapping[]; -} ByteBuffer = {.mapping = { - #define OFFSET(member) offsetof(struct JNIByteBuffer, member) - {"java/nio/ByteBuffer", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, - {"java/nio/ByteBuffer", "clear", "()Ljava/nio/Buffer;", MP_JNI_METHOD, OFFSET(clear), 1}, +} ByteBuffer; +#define OFFSET(member) offsetof(struct JNIByteBuffer, member) +static const struct MPJniField ByteBuffer_mapping[] = { + {"java/nio/ByteBuffer", NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, + {"clear", "()Ljava/nio/Buffer;", MP_JNI_METHOD, OFFSET(clear), 1}, {0}, - #undef OFFSET -}}; +}; +#undef OFFSET -struct JNIAudioTrack { +static struct JNIAudioTrack { jclass clazz; jmethodID ctor; jmethodID ctorV21; @@ -92,7 +89,6 @@ struct JNIAudioTrack { jmethodID pause; jmethodID write; jmethodID writeFloat; - jmethodID writeV23; jmethodID writeShortV23; jmethodID writeBufferV21; jmethodID getBufferSizeInFramesV23; @@ -111,172 +107,174 @@ struct JNIAudioTrack { jint ERROR_INVALID_OPERATION; jint WRITE_BLOCKING; jint WRITE_NON_BLOCKING; - struct MPJniField mapping[]; -} AudioTrack = {.mapping = { - #define OFFSET(member) offsetof(struct JNIAudioTrack, member) - {"android/media/AudioTrack", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, - {"android/media/AudioTrack", "<init>", "(IIIIIII)V", MP_JNI_METHOD, OFFSET(ctor), 1}, - {"android/media/AudioTrack", "<init>", "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;III)V", MP_JNI_METHOD, OFFSET(ctorV21), 0}, - {"android/media/AudioTrack", "release", "()V", MP_JNI_METHOD, OFFSET(release), 1}, - {"android/media/AudioTrack", "getState", "()I", MP_JNI_METHOD, OFFSET(getState), 1}, - {"android/media/AudioTrack", "getPlayState", "()I", MP_JNI_METHOD, OFFSET(getPlayState), 1}, - {"android/media/AudioTrack", "play", "()V", MP_JNI_METHOD, OFFSET(play), 1}, - {"android/media/AudioTrack", "stop", "()V", MP_JNI_METHOD, OFFSET(stop), 1}, - {"android/media/AudioTrack", "flush", "()V", MP_JNI_METHOD, OFFSET(flush), 1}, - {"android/media/AudioTrack", "pause", "()V", MP_JNI_METHOD, OFFSET(pause), 1}, - {"android/media/AudioTrack", "write", "([BII)I", MP_JNI_METHOD, OFFSET(write), 1}, - {"android/media/AudioTrack", "write", "([FIII)I", MP_JNI_METHOD, OFFSET(writeFloat), 1}, - {"android/media/AudioTrack", "write", "([BIII)I", MP_JNI_METHOD, OFFSET(writeV23), 0}, - {"android/media/AudioTrack", "write", "([SIII)I", MP_JNI_METHOD, OFFSET(writeShortV23), 0}, - {"android/media/AudioTrack", "write", "(Ljava/nio/ByteBuffer;II)I", MP_JNI_METHOD, OFFSET(writeBufferV21), 1}, - {"android/media/AudioTrack", "getBufferSizeInFrames", "()I", MP_JNI_METHOD, OFFSET(getBufferSizeInFramesV23), 0}, - {"android/media/AudioTrack", "getTimestamp", "(Landroid/media/AudioTimestamp;)Z", MP_JNI_METHOD, OFFSET(getTimestamp), 1}, - {"android/media/AudioTrack", "getPlaybackHeadPosition", "()I", MP_JNI_METHOD, OFFSET(getPlaybackHeadPosition), 1}, - {"android/media/AudioTrack", "getLatency", "()I", MP_JNI_METHOD, OFFSET(getLatency), 1}, - {"android/media/AudioTrack", "getMinBufferSize", "(III)I", MP_JNI_STATIC_METHOD, OFFSET(getMinBufferSize), 1}, - {"android/media/AudioTrack", "getNativeOutputSampleRate", "(I)I", MP_JNI_STATIC_METHOD, OFFSET(getNativeOutputSampleRate), 1}, - {"android/media/AudioTrack", "WRITE_BLOCKING", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(WRITE_BLOCKING), 0}, - {"android/media/AudioTrack", "WRITE_NON_BLOCKING", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(WRITE_NON_BLOCKING), 0}, - {"android/media/AudioTrack", "STATE_INITIALIZED", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(STATE_INITIALIZED), 1}, - {"android/media/AudioTrack", "PLAYSTATE_STOPPED", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(PLAYSTATE_STOPPED), 1}, - {"android/media/AudioTrack", "PLAYSTATE_PAUSED", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(PLAYSTATE_PAUSED), 1}, - {"android/media/AudioTrack", "PLAYSTATE_PLAYING", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(PLAYSTATE_PLAYING), 1}, - {"android/media/AudioTrack", "MODE_STREAM", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(MODE_STREAM), 1}, - {"android/media/AudioTrack", "ERROR", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ERROR), 1}, - {"android/media/AudioTrack", "ERROR_BAD_VALUE", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ERROR_BAD_VALUE), 1}, - {"android/media/AudioTrack", "ERROR_INVALID_OPERATION", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ERROR_INVALID_OPERATION), 1}, +} AudioTrack; +#define OFFSET(member) offsetof(struct JNIAudioTrack, member) +static const struct MPJniField AudioTrack_mapping[] = { + {"android/media/AudioTrack", NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, + {"<init>", "(IIIIIII)V", MP_JNI_METHOD, OFFSET(ctor), 1}, + {"<init>", "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;III)V", MP_JNI_METHOD, OFFSET(ctorV21), 0}, + {"release", "()V", MP_JNI_METHOD, OFFSET(release), 1}, + {"getState", "()I", MP_JNI_METHOD, OFFSET(getState), 1}, + {"getPlayState", "()I", MP_JNI_METHOD, OFFSET(getPlayState), 1}, + {"play", "()V", MP_JNI_METHOD, OFFSET(play), 1}, + {"stop", "()V", MP_JNI_METHOD, OFFSET(stop), 1}, + {"flush", "()V", MP_JNI_METHOD, OFFSET(flush), 1}, + {"pause", "()V", MP_JNI_METHOD, OFFSET(pause), 1}, + {"write", "([BII)I", MP_JNI_METHOD, OFFSET(write), 1}, + {"write", "([FIII)I", MP_JNI_METHOD, OFFSET(writeFloat), 1}, + {"write", "([SIII)I", MP_JNI_METHOD, OFFSET(writeShortV23), 0}, + {"write", "(Ljava/nio/ByteBuffer;II)I", MP_JNI_METHOD, OFFSET(writeBufferV21), 1}, + {"getBufferSizeInFrames", "()I", MP_JNI_METHOD, OFFSET(getBufferSizeInFramesV23), 0}, + {"getTimestamp", "(Landroid/media/AudioTimestamp;)Z", MP_JNI_METHOD, OFFSET(getTimestamp), 1}, + {"getPlaybackHeadPosition", "()I", MP_JNI_METHOD, OFFSET(getPlaybackHeadPosition), 1}, + {"getLatency", "()I", MP_JNI_METHOD, OFFSET(getLatency), 1}, + {"getMinBufferSize", "(III)I", MP_JNI_STATIC_METHOD, OFFSET(getMinBufferSize), 1}, + {"getNativeOutputSampleRate", "(I)I", MP_JNI_STATIC_METHOD, OFFSET(getNativeOutputSampleRate), 1}, + {"WRITE_BLOCKING", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(WRITE_BLOCKING), 0}, + {"WRITE_NON_BLOCKING", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(WRITE_NON_BLOCKING), 0}, + {"STATE_INITIALIZED", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(STATE_INITIALIZED), 1}, + {"PLAYSTATE_STOPPED", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(PLAYSTATE_STOPPED), 1}, + {"PLAYSTATE_PAUSED", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(PLAYSTATE_PAUSED), 1}, + {"PLAYSTATE_PLAYING", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(PLAYSTATE_PLAYING), 1}, + {"MODE_STREAM", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(MODE_STREAM), 1}, + {"ERROR", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ERROR), 1}, + {"ERROR_BAD_VALUE", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ERROR_BAD_VALUE), 1}, + {"ERROR_INVALID_OPERATION", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ERROR_INVALID_OPERATION), 1}, {0} - #undef OFFSET -}}; +}; +#undef OFFSET -struct JNIAudioAttributes { +static struct JNIAudioAttributes { jclass clazz; jint CONTENT_TYPE_MOVIE; + jint CONTENT_TYPE_MUSIC; jint USAGE_MEDIA; - struct MPJniField mapping[]; -} AudioAttributes = {.mapping = { - #define OFFSET(member) offsetof(struct JNIAudioAttributes, member) - {"android/media/AudioAttributes", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 0}, - {"android/media/AudioAttributes", "CONTENT_TYPE_MOVIE", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CONTENT_TYPE_MOVIE), 0}, - {"android/media/AudioAttributes", "USAGE_MEDIA", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(USAGE_MEDIA), 0}, +} AudioAttributes; +#define OFFSET(member) offsetof(struct JNIAudioAttributes, member) +static const struct MPJniField AudioAttributes_mapping[] = { + {"android/media/AudioAttributes", NULL, MP_JNI_CLASS, OFFSET(clazz), 0}, + {"CONTENT_TYPE_MOVIE", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CONTENT_TYPE_MOVIE), 0}, + {"CONTENT_TYPE_MUSIC", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CONTENT_TYPE_MUSIC), 0}, + {"USAGE_MEDIA", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(USAGE_MEDIA), 0}, {0} - #undef OFFSET -}}; +}; +#undef OFFSET -struct JNIAudioAttributesBuilder { +static struct JNIAudioAttributesBuilder { jclass clazz; jmethodID ctor; jmethodID setUsage; jmethodID setContentType; jmethodID build; - struct MPJniField mapping[]; -} AudioAttributesBuilder = {.mapping = { - #define OFFSET(member) offsetof(struct JNIAudioAttributesBuilder, member) - {"android/media/AudioAttributes$Builder", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 0}, - {"android/media/AudioAttributes$Builder", "<init>", "()V", MP_JNI_METHOD, OFFSET(ctor), 0}, - {"android/media/AudioAttributes$Builder", "setUsage", "(I)Landroid/media/AudioAttributes$Builder;", MP_JNI_METHOD, OFFSET(setUsage), 0}, - {"android/media/AudioAttributes$Builder", "setContentType", "(I)Landroid/media/AudioAttributes$Builder;", MP_JNI_METHOD, OFFSET(setContentType), 0}, - {"android/media/AudioAttributes$Builder", "build", "()Landroid/media/AudioAttributes;", MP_JNI_METHOD, OFFSET(build), 0}, +} AudioAttributesBuilder; +#define OFFSET(member) offsetof(struct JNIAudioAttributesBuilder, member) +static const struct MPJniField AudioAttributesBuilder_mapping[] = { + {"android/media/AudioAttributes$Builder", NULL, MP_JNI_CLASS, OFFSET(clazz), 0}, + {"<init>", "()V", MP_JNI_METHOD, OFFSET(ctor), 0}, + {"setUsage", "(I)Landroid/media/AudioAttributes$Builder;", MP_JNI_METHOD, OFFSET(setUsage), 0}, + {"setContentType", "(I)Landroid/media/AudioAttributes$Builder;", MP_JNI_METHOD, OFFSET(setContentType), 0}, + {"build", "()Landroid/media/AudioAttributes;", MP_JNI_METHOD, OFFSET(build), 0}, {0} - #undef OFFSET -}}; +}; +#undef OFFSET -struct JNIAudioFormat { +static struct JNIAudioFormat { jclass clazz; jint ENCODING_PCM_8BIT; jint ENCODING_PCM_16BIT; jint ENCODING_PCM_FLOAT; jint ENCODING_IEC61937; - jint ENCODING_AC3; jint CHANNEL_OUT_MONO; jint CHANNEL_OUT_STEREO; - jint CHANNEL_OUT_FRONT_LEFT; - jint CHANNEL_OUT_FRONT_RIGHT; - jint CHANNEL_OUT_BACK_LEFT; - jint CHANNEL_OUT_BACK_RIGHT; jint CHANNEL_OUT_FRONT_CENTER; - jint CHANNEL_OUT_LOW_FREQUENCY; - jint CHANNEL_OUT_BACK_CENTER; + jint CHANNEL_OUT_QUAD; jint CHANNEL_OUT_5POINT1; - jint CHANNEL_OUT_SIDE_LEFT; - jint CHANNEL_OUT_SIDE_RIGHT; - struct MPJniField mapping[]; -} AudioFormat = {.mapping = { - #define OFFSET(member) offsetof(struct JNIAudioFormat, member) - {"android/media/AudioFormat", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, - {"android/media/AudioFormat", "ENCODING_PCM_8BIT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_PCM_8BIT), 1}, - {"android/media/AudioFormat", "ENCODING_PCM_16BIT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_PCM_16BIT), 1}, - {"android/media/AudioFormat", "ENCODING_PCM_FLOAT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_PCM_FLOAT), 1}, - {"android/media/AudioFormat", "ENCODING_AC3", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_AC3), 0}, - {"android/media/AudioFormat", "ENCODING_IEC61937", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_IEC61937), 0}, - {"android/media/AudioFormat", "CHANNEL_OUT_MONO", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_MONO), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_STEREO", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_STEREO), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_5POINT1", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_5POINT1), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_FRONT_LEFT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_FRONT_LEFT), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_FRONT_RIGHT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_FRONT_RIGHT), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_FRONT_CENTER", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_FRONT_CENTER), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_LOW_FREQUENCY", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_LOW_FREQUENCY), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_BACK_LEFT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_BACK_LEFT), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_BACK_RIGHT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_BACK_RIGHT), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_BACK_CENTER", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_BACK_CENTER), 1}, - {"android/media/AudioFormat", "CHANNEL_OUT_SIDE_LEFT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_SIDE_LEFT), 0}, - {"android/media/AudioFormat", "CHANNEL_OUT_SIDE_RIGHT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_SIDE_RIGHT), 0}, + jint CHANNEL_OUT_BACK_CENTER; + jint CHANNEL_OUT_7POINT1_SURROUND; +} AudioFormat; +#define OFFSET(member) offsetof(struct JNIAudioFormat, member) +static const struct MPJniField AudioFormat_mapping[] = { + {"android/media/AudioFormat", NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, + {"ENCODING_PCM_8BIT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_PCM_8BIT), 1}, + {"ENCODING_PCM_16BIT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_PCM_16BIT), 1}, + {"ENCODING_PCM_FLOAT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_PCM_FLOAT), 1}, + {"ENCODING_IEC61937", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ENCODING_IEC61937), 0}, + {"CHANNEL_OUT_MONO", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_MONO), 1}, + {"CHANNEL_OUT_STEREO", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_STEREO), 1}, + {"CHANNEL_OUT_FRONT_CENTER", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_FRONT_CENTER), 1}, + {"CHANNEL_OUT_QUAD", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_QUAD), 1}, + {"CHANNEL_OUT_5POINT1", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_5POINT1), 1}, + {"CHANNEL_OUT_BACK_CENTER", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_BACK_CENTER), 1}, + {"CHANNEL_OUT_7POINT1_SURROUND", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(CHANNEL_OUT_7POINT1_SURROUND), 0}, {0} - #undef OFFSET -}}; +}; +#undef OFFSET -struct JNIAudioFormatBuilder { +static struct JNIAudioFormatBuilder { jclass clazz; jmethodID ctor; jmethodID setEncoding; jmethodID setSampleRate; jmethodID setChannelMask; jmethodID build; - struct MPJniField mapping[]; -} AudioFormatBuilder = {.mapping = { - #define OFFSET(member) offsetof(struct JNIAudioFormatBuilder, member) - {"android/media/AudioFormat$Builder", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 0}, - {"android/media/AudioFormat$Builder", "<init>", "()V", MP_JNI_METHOD, OFFSET(ctor), 0}, - {"android/media/AudioFormat$Builder", "setEncoding", "(I)Landroid/media/AudioFormat$Builder;", MP_JNI_METHOD, OFFSET(setEncoding), 0}, - {"android/media/AudioFormat$Builder", "setSampleRate", "(I)Landroid/media/AudioFormat$Builder;", MP_JNI_METHOD, OFFSET(setSampleRate), 0}, - {"android/media/AudioFormat$Builder", "setChannelMask", "(I)Landroid/media/AudioFormat$Builder;", MP_JNI_METHOD, OFFSET(setChannelMask), 0}, - {"android/media/AudioFormat$Builder", "build", "()Landroid/media/AudioFormat;", MP_JNI_METHOD, OFFSET(build), 0}, +} AudioFormatBuilder; +#define OFFSET(member) offsetof(struct JNIAudioFormatBuilder, member) +static const struct MPJniField AudioFormatBuilder_mapping[] = { + {"android/media/AudioFormat$Builder", NULL, MP_JNI_CLASS, OFFSET(clazz), 0}, + {"<init>", "()V", MP_JNI_METHOD, OFFSET(ctor), 0}, + {"setEncoding", "(I)Landroid/media/AudioFormat$Builder;", MP_JNI_METHOD, OFFSET(setEncoding), 0}, + {"setSampleRate", "(I)Landroid/media/AudioFormat$Builder;", MP_JNI_METHOD, OFFSET(setSampleRate), 0}, + {"setChannelMask", "(I)Landroid/media/AudioFormat$Builder;", MP_JNI_METHOD, OFFSET(setChannelMask), 0}, + {"build", "()Landroid/media/AudioFormat;", MP_JNI_METHOD, OFFSET(build), 0}, {0} - #undef OFFSET -}}; - +}; +#undef OFFSET -struct JNIAudioManager { +static struct JNIAudioManager { jclass clazz; jint ERROR_DEAD_OBJECT; jint STREAM_MUSIC; - struct MPJniField mapping[]; -} AudioManager = {.mapping = { - #define OFFSET(member) offsetof(struct JNIAudioManager, member) - {"android/media/AudioManager", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, - {"android/media/AudioManager", "STREAM_MUSIC", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(STREAM_MUSIC), 1}, - {"android/media/AudioManager", "ERROR_DEAD_OBJECT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ERROR_DEAD_OBJECT), 0}, +} AudioManager; +#define OFFSET(member) offsetof(struct JNIAudioManager, member) +static const struct MPJniField AudioManager_mapping[] = { + {"android/media/AudioManager", NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, + {"STREAM_MUSIC", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(STREAM_MUSIC), 1}, + {"ERROR_DEAD_OBJECT", "I", MP_JNI_STATIC_FIELD_AS_INT, OFFSET(ERROR_DEAD_OBJECT), 0}, {0} - #undef OFFSET -}}; +}; +#undef OFFSET -struct JNIAudioTimestamp { +static struct JNIAudioTimestamp { jclass clazz; jmethodID ctor; jfieldID framePosition; jfieldID nanoTime; - struct MPJniField mapping[]; -} AudioTimestamp = {.mapping = { - #define OFFSET(member) offsetof(struct JNIAudioTimestamp, member) - {"android/media/AudioTimestamp", NULL, NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, - {"android/media/AudioTimestamp", "<init>", "()V", MP_JNI_METHOD, OFFSET(ctor), 1}, - {"android/media/AudioTimestamp", "framePosition", "J", MP_JNI_FIELD, OFFSET(framePosition), 1}, - {"android/media/AudioTimestamp", "nanoTime", "J", MP_JNI_FIELD, OFFSET(nanoTime), 1}, +} AudioTimestamp; +#define OFFSET(member) offsetof(struct JNIAudioTimestamp, member) +static const struct MPJniField AudioTimestamp_mapping[] = { + {"android/media/AudioTimestamp", NULL, MP_JNI_CLASS, OFFSET(clazz), 1}, + {"<init>", "()V", MP_JNI_METHOD, OFFSET(ctor), 1}, + {"framePosition", "J", MP_JNI_FIELD, OFFSET(framePosition), 1}, + {"nanoTime", "J", MP_JNI_FIELD, OFFSET(nanoTime), 1}, {0} - #undef OFFSET -}}; - -#define MP_JNI_DELETELOCAL(o) (*env)->DeleteLocalRef(env, o) +}; +#undef OFFSET + +#define ENTRY(name) { &name, name ## _mapping } +static const struct { + void *fields; + const struct MPJniField *mapping; +} jclass_list[] = { + ENTRY(ByteBuffer), + ENTRY(AudioTrack), + ENTRY(AudioAttributes), + ENTRY(AudioAttributesBuilder), + ENTRY(AudioFormat), + ENTRY(AudioFormatBuilder), + ENTRY(AudioManager), + ENTRY(AudioTimestamp), +}; +#undef ENTRY static int AudioTrack_New(struct ao *ao) { @@ -291,22 +289,24 @@ static int AudioTrack_New(struct ao *ao) jobject format_builder = MP_JNI_NEW(AudioFormatBuilder.clazz, AudioFormatBuilder.ctor); MP_JNI_EXCEPTION_LOG(ao); tmp = MP_JNI_CALL_OBJECT(format_builder, AudioFormatBuilder.setEncoding, p->format); - MP_JNI_DELETELOCAL(tmp); + MP_JNI_LOCAL_FREEP(&tmp); tmp = MP_JNI_CALL_OBJECT(format_builder, AudioFormatBuilder.setSampleRate, p->samplerate); - MP_JNI_DELETELOCAL(tmp); + MP_JNI_LOCAL_FREEP(&tmp); tmp = MP_JNI_CALL_OBJECT(format_builder, AudioFormatBuilder.setChannelMask, p->channel_config); - MP_JNI_DELETELOCAL(tmp); + MP_JNI_LOCAL_FREEP(&tmp); jobject format = MP_JNI_CALL_OBJECT(format_builder, AudioFormatBuilder.build); - MP_JNI_DELETELOCAL(format_builder); + MP_JNI_LOCAL_FREEP(&format_builder); jobject attr_builder = MP_JNI_NEW(AudioAttributesBuilder.clazz, AudioAttributesBuilder.ctor); MP_JNI_EXCEPTION_LOG(ao); tmp = MP_JNI_CALL_OBJECT(attr_builder, AudioAttributesBuilder.setUsage, AudioAttributes.USAGE_MEDIA); - MP_JNI_DELETELOCAL(tmp); - tmp = MP_JNI_CALL_OBJECT(attr_builder, AudioAttributesBuilder.setContentType, AudioAttributes.CONTENT_TYPE_MOVIE); - MP_JNI_DELETELOCAL(tmp); + MP_JNI_LOCAL_FREEP(&tmp); + jint content_type = (ao->init_flags & AO_INIT_MEDIA_ROLE_MUSIC) ? + AudioAttributes.CONTENT_TYPE_MUSIC : AudioAttributes.CONTENT_TYPE_MOVIE; + tmp = MP_JNI_CALL_OBJECT(attr_builder, AudioAttributesBuilder.setContentType, content_type); + MP_JNI_LOCAL_FREEP(&tmp); jobject attr = MP_JNI_CALL_OBJECT(attr_builder, AudioAttributesBuilder.build); - MP_JNI_DELETELOCAL(attr_builder); + MP_JNI_LOCAL_FREEP(&attr_builder); audiotrack = MP_JNI_NEW( AudioTrack.clazz, @@ -318,8 +318,8 @@ static int AudioTrack_New(struct ao *ao) p->cfg_session_id ); - MP_JNI_DELETELOCAL(format); - MP_JNI_DELETELOCAL(attr); + MP_JNI_LOCAL_FREEP(&format); + MP_JNI_LOCAL_FREEP(&attr); } else { MP_VERBOSE(ao, "Using legacy initializer\n"); audiotrack = MP_JNI_NEW( @@ -334,7 +334,7 @@ static int AudioTrack_New(struct ao *ao) p->cfg_session_id ); } - if (!audiotrack || MP_JNI_EXCEPTION_LOG(ao) < 0) { + if (MP_JNI_EXCEPTION_LOG(ao) < 0 || !audiotrack) { MP_FATAL(ao, "AudioTrack Init failed\n"); return -1; } @@ -342,7 +342,7 @@ static int AudioTrack_New(struct ao *ao) if (MP_JNI_CALL_INT(audiotrack, AudioTrack.getState) != AudioTrack.STATE_INITIALIZED) { MP_JNI_CALL_VOID(audiotrack, AudioTrack.release); MP_JNI_EXCEPTION_LOG(ao); - (*env)->DeleteLocalRef(env, audiotrack); + MP_JNI_LOCAL_FREEP(&audiotrack); MP_ERR(ao, "AudioTrack.getState failed\n"); return -1; } @@ -356,7 +356,7 @@ static int AudioTrack_New(struct ao *ao) } p->audiotrack = (*env)->NewGlobalRef(env, audiotrack); - (*env)->DeleteLocalRef(env, audiotrack); + MP_JNI_LOCAL_FREEP(&audiotrack); if (!p->audiotrack) return -1; @@ -370,8 +370,7 @@ static int AudioTrack_Recreate(struct ao *ao) MP_JNI_CALL_VOID(p->audiotrack, AudioTrack.release); MP_JNI_EXCEPTION_LOG(ao); - (*env)->DeleteGlobalRef(env, p->audiotrack); - p->audiotrack = NULL; + MP_JNI_GLOBAL_FREEP(&p->audiotrack); return AudioTrack_New(ao); } @@ -382,26 +381,26 @@ static uint32_t AudioTrack_getPlaybackHeadPosition(struct ao *ao) return 0; JNIEnv *env = MP_JNI_GET_ENV(ao); uint32_t pos = 0; - int64_t now = mp_raw_time_us(); + int64_t now = mp_raw_time_ns(); int state = MP_JNI_CALL_INT(p->audiotrack, AudioTrack.getPlayState); int stable_count = 20; - int64_t wait = p->timestamp_stable < stable_count ? 50000 : 3000000; + int64_t wait = p->timestamp_stable < stable_count ? 50000000 : 3000000000; if (state == AudioTrack.PLAYSTATE_PLAYING && p->format != AudioFormat.ENCODING_IEC61937 && (p->timestamp_fetched == 0 || now - p->timestamp_fetched >= wait)) { if (!p->timestamp_fetched) p->timestamp_stable = 0; - int64_t utime1 = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime) / 1000; + int64_t time1 = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime); if (MP_JNI_CALL_BOOL(p->audiotrack, AudioTrack.getTimestamp, p->timestamp)) { p->timestamp_set = true; p->timestamp_fetched = now; if (p->timestamp_stable < stable_count) { uint32_t fpos = 0xFFFFFFFFL & MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.framePosition); - int64_t utime2 = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime) / 1000; - //MP_VERBOSE(ao, "getTimestamp: fpos= %u / time= %"PRId64" / now= %"PRId64" / stable= %d\n", fpos, utime2, now, p->timestamp_stable); - if (utime1 != utime2 && utime2 != 0 && fpos != 0) { + int64_t time2 = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime); + //MP_VERBOSE(ao, "getTimestamp: fpos= %u / time= %"PRId64" / now= %"PRId64" / stable= %d\n", fpos, time2, now, p->timestamp_stable); + if (time1 != time2 && time2 != 0 && fpos != 0) { p->timestamp_stable++; } } @@ -414,19 +413,14 @@ static uint32_t AudioTrack_getPlaybackHeadPosition(struct ao *ao) if (p->timestamp_set) { pos = 0xFFFFFFFFL & MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.framePosition); uint32_t fpos = pos; - int64_t utime = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime) / 1000; - if (utime == 0) + int64_t time = MP_JNI_GET_LONG(p->timestamp, AudioTimestamp.nanoTime); + if (time == 0) fpos = pos = 0; - if (p->needs_timestamp_offset) { - if (utime != 0 && !p->timestamp_offset) - p->timestamp_offset = now - utime; - utime += p->timestamp_offset; - } - if (fpos != 0 && utime != 0 && state == AudioTrack.PLAYSTATE_PLAYING) { - double diff = (double)(now - utime) / 1e6; + if (fpos != 0 && time != 0 && state == AudioTrack.PLAYSTATE_PLAYING) { + double diff = (double)(now - time) / 1e9; pos += diff * ao->samplerate; } - //MP_VERBOSE(ao, "position = %u via getTimestamp (state = %d / fpos= %u / time= %"PRId64")\n", pos, state, fpos, utime); + //MP_VERBOSE(ao, "position = %u via getTimestamp (state = %d / fpos= %u / time= %"PRId64")\n", pos, state, fpos, time); } else { pos = 0xFFFFFFFFL & MP_JNI_CALL_INT(p->audiotrack, AudioTrack.getPlaybackHeadPosition); //MP_VERBOSE(ao, "playbackHeadPosition = %u (reset_pending=%d)\n", pos, p->reset_pending); @@ -503,6 +497,14 @@ static int AudioTrack_write(struct ao *ao, int len) if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1; if (ret > 0) ret *= 2; + } else if (AudioTrack.writeBufferV21) { + // reset positions for reading + jobject bbuf = MP_JNI_CALL_OBJECT(p->bbuf, ByteBuffer.clear); + if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1; + MP_JNI_LOCAL_FREEP(&bbuf); + ret = MP_JNI_CALL_INT(p->audiotrack, AudioTrack.writeBufferV21, p->bbuf, len, AudioTrack.WRITE_BLOCKING); + if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1; + } else if (p->format == AudioFormat.ENCODING_PCM_FLOAT) { (*env)->SetFloatArrayRegion(env, p->floatarray, 0, len / sizeof(float), buf); if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1; @@ -510,13 +512,6 @@ static int AudioTrack_write(struct ao *ao, int len) if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1; if (ret > 0) ret *= sizeof(float); - } else if (AudioTrack.writeBufferV21) { - jobject bbuf = MP_JNI_CALL_OBJECT(p->bbuf, ByteBuffer.clear); - if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1; - (*env)->DeleteLocalRef(env, bbuf); - ret = MP_JNI_CALL_INT(p->audiotrack, AudioTrack.writeBufferV21, p->bbuf, len, AudioTrack.WRITE_BLOCKING); - if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1; - } else { (*env)->SetByteArrayRegion(env, p->bytearray, 0, len, buf); if (MP_JNI_EXCEPTION_LOG(ao) < 0) return -1; @@ -530,41 +525,35 @@ static int AudioTrack_write(struct ao *ao, int len) static void uninit_jni(struct ao *ao) { JNIEnv *env = MP_JNI_GET_ENV(ao); - mp_jni_reset_jfields(env, &AudioTrack, AudioTrack.mapping, 1, ao->log); - mp_jni_reset_jfields(env, &AudioTimestamp, AudioTimestamp.mapping, 1, ao->log); - mp_jni_reset_jfields(env, &AudioManager, AudioManager.mapping, 1, ao->log); - mp_jni_reset_jfields(env, &AudioFormat, AudioFormat.mapping, 1, ao->log); - mp_jni_reset_jfields(env, &AudioFormatBuilder, AudioFormatBuilder.mapping, 1, ao->log); - mp_jni_reset_jfields(env, &AudioAttributes, AudioAttributes.mapping, 1, ao->log); - mp_jni_reset_jfields(env, &AudioAttributesBuilder, AudioAttributesBuilder.mapping, 1, ao->log); - mp_jni_reset_jfields(env, &ByteBuffer, ByteBuffer.mapping, 1, ao->log); + for (int i = 0; i < MP_ARRAY_SIZE(jclass_list); i++) { + mp_jni_reset_jfields(env, jclass_list[i].fields, + jclass_list[i].mapping, 1, ao->log); + } } static int init_jni(struct ao *ao) { JNIEnv *env = MP_JNI_GET_ENV(ao); - if (mp_jni_init_jfields(env, &AudioTrack, AudioTrack.mapping, 1, ao->log) < 0 || - mp_jni_init_jfields(env, &ByteBuffer, ByteBuffer.mapping, 1, ao->log) < 0 || - mp_jni_init_jfields(env, &AudioTimestamp, AudioTimestamp.mapping, 1, ao->log) < 0 || - mp_jni_init_jfields(env, &AudioManager, AudioManager.mapping, 1, ao->log) < 0 || - mp_jni_init_jfields(env, &AudioAttributes, AudioAttributes.mapping, 1, ao->log) < 0 || - mp_jni_init_jfields(env, &AudioAttributesBuilder, AudioAttributesBuilder.mapping, 1, ao->log) < 0 || - mp_jni_init_jfields(env, &AudioFormatBuilder, AudioFormatBuilder.mapping, 1, ao->log) < 0 || - mp_jni_init_jfields(env, &AudioFormat, AudioFormat.mapping, 1, ao->log) < 0) { - uninit_jni(ao); - return -1; + for (int i = 0; i < MP_ARRAY_SIZE(jclass_list); i++) { + if (mp_jni_init_jfields(env, jclass_list[i].fields, + jclass_list[i].mapping, 1, ao->log) < 0) { + goto error; + } } - return 0; + +error: + uninit_jni(ao); + return -1; } -static void *playthread(void *arg) +static MP_THREAD_VOID ao_thread(void *arg) { struct ao *ao = arg; struct priv *p = ao->priv; JNIEnv *env = MP_JNI_GET_ENV(ao); - mpthread_set_name("audiotrack"); - pthread_mutex_lock(&p->lock); + mp_thread_set_name("ao/audiotrack"); + mp_mutex_lock(&p->lock); while (!p->thread_terminate) { int state = AudioTrack.PLAYSTATE_PAUSED; if (p->audiotrack) { @@ -572,12 +561,11 @@ static void *playthread(void *arg) } if (state == AudioTrack.PLAYSTATE_PLAYING) { int read_samples = p->chunksize / ao->sstride; - int64_t ts = mp_time_us(); - ts += (read_samples / (double)(ao->samplerate)) * 1e6; - ts += AudioTrack_getLatency(ao) * 1e6; - int samples = ao_read_data(ao, &p->chunk, read_samples, ts); - int write_samples = read_samples; - int ret = AudioTrack_write(ao, write_samples * ao->sstride); + int64_t ts = mp_time_ns(); + ts += MP_TIME_S_TO_NS(read_samples / (double)(ao->samplerate)); + ts += MP_TIME_S_TO_NS(AudioTrack_getLatency(ao)); + int samples = ao_read_data_nonblocking(ao, &p->chunk, read_samples, ts); + int ret = AudioTrack_write(ao, samples * ao->sstride); if (ret >= 0) { p->written_frames += ret / ao->sstride; } else if (ret == AudioManager.ERROR_DEAD_OBJECT) { @@ -589,12 +577,11 @@ static void *playthread(void *arg) MP_ERR(ao, "AudioTrack.write failed with %d\n", ret); } } else { - struct timespec wait = mp_rel_time_to_timespec(0.300); - pthread_cond_timedwait(&p->wakeup, &p->lock, &wait); + mp_cond_timedwait(&p->wakeup, &p->lock, MP_TIME_MS_TO_NS(300)); } } - pthread_mutex_unlock(&p->lock); - return NULL; + mp_mutex_unlock(&p->lock); + MP_THREAD_RETURN(); } static void uninit(struct ao *ao) @@ -608,53 +595,32 @@ static void uninit(struct ao *ao) MP_JNI_EXCEPTION_LOG(ao); } - pthread_mutex_lock(&p->lock); + mp_mutex_lock(&p->lock); p->thread_terminate = true; - pthread_cond_signal(&p->wakeup); - pthread_mutex_unlock(&p->lock); + mp_cond_signal(&p->wakeup); + mp_mutex_unlock(&p->lock); if (p->thread_created) - pthread_join(p->thread, NULL); + mp_thread_join(p->thread); if (p->audiotrack) { MP_JNI_CALL_VOID(p->audiotrack, AudioTrack.release); MP_JNI_EXCEPTION_LOG(ao); - (*env)->DeleteGlobalRef(env, p->audiotrack); - p->audiotrack = NULL; - } - - if (p->bytearray) { - (*env)->DeleteGlobalRef(env, p->bytearray); - p->bytearray = NULL; + MP_JNI_GLOBAL_FREEP(&p->audiotrack); } - if (p->shortarray) { - (*env)->DeleteGlobalRef(env, p->shortarray); - p->shortarray = NULL; - } + MP_JNI_GLOBAL_FREEP(&p->bytearray); - if (p->floatarray) { - (*env)->DeleteGlobalRef(env, p->floatarray); - p->floatarray = NULL; - } + MP_JNI_GLOBAL_FREEP(&p->shortarray); - if (p->bbuf) { - (*env)->DeleteGlobalRef(env, p->bbuf); - p->bbuf = NULL; - } + MP_JNI_GLOBAL_FREEP(&p->floatarray); - if (p->timestamp) { - (*env)->DeleteGlobalRef(env, p->timestamp); - p->timestamp = NULL; - } + MP_JNI_GLOBAL_FREEP(&p->bbuf); - if (p->chunk) { - free(p->chunk); - p->chunk = NULL; - } + MP_JNI_GLOBAL_FREEP(&p->timestamp); - pthread_cond_destroy(&p->wakeup); - pthread_mutex_destroy(&p->lock); + mp_cond_destroy(&p->wakeup); + mp_mutex_destroy(&p->lock); uninit_jni(ao); } @@ -666,17 +632,21 @@ static int init(struct ao *ao) if (!env) return -1; - pthread_mutex_init(&p->lock, NULL); - pthread_cond_init(&p->wakeup, NULL); + mp_mutex_init(&p->lock); + mp_cond_init(&p->wakeup); if (init_jni(ao) < 0) return -1; if (af_fmt_is_spdif(ao->format)) { p->format = AudioFormat.ENCODING_IEC61937; + if (!p->format || !AudioTrack.writeShortV23) { + MP_ERR(ao, "spdif passthrough not supported by API\n"); + return -1; + } } else if (ao->format == AF_FORMAT_U8) { p->format = AudioFormat.ENCODING_PCM_8BIT; - } else if (p->cfg_pcm_float && (ao->format == AF_FORMAT_FLOAT || ao->format == AF_FORMAT_FLOATP)) { + } else if (p->cfg_pcm_float && af_fmt_is_float(ao->format)) { ao->format = AF_FORMAT_FLOAT; p->format = AudioFormat.ENCODING_PCM_FLOAT; } else { @@ -691,22 +661,48 @@ static int init(struct ao *ao) AudioManager.STREAM_MUSIC ); if (MP_JNI_EXCEPTION_LOG(ao) == 0) { - ao->samplerate = samplerate; MP_VERBOSE(ao, "AudioTrack.nativeOutputSampleRate = %d\n", samplerate); + ao->samplerate = MPMIN(samplerate, ao->samplerate); } } p->samplerate = ao->samplerate; + /* https://developer.android.com/reference/android/media/AudioFormat#channelPositionMask */ + static const struct mp_chmap layouts[] = { + {0}, // empty + MP_CHMAP_INIT_MONO, // mono + MP_CHMAP_INIT_STEREO, // stereo + MP_CHMAP3(FL, FR, FC), // 3.0 + MP_CHMAP4(FL, FR, BL, BR), // quad + MP_CHMAP5(FL, FR, FC, BL, BR), // 5.0 + MP_CHMAP6(FL, FR, FC, LFE, BL, BR), // 5.1 + MP_CHMAP7(FL, FR, FC, LFE, BL, BR, BC), // 6.1 + MP_CHMAP8(FL, FR, FC, LFE, BL, BR, SL, SR), // 7.1 + }; + const jint layout_map[] = { + 0, + AudioFormat.CHANNEL_OUT_MONO, + AudioFormat.CHANNEL_OUT_STEREO, + AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER, + AudioFormat.CHANNEL_OUT_QUAD, + AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER, + AudioFormat.CHANNEL_OUT_5POINT1, + AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER, + AudioFormat.CHANNEL_OUT_7POINT1_SURROUND, + }; + static_assert(MP_ARRAY_SIZE(layout_map) == MP_ARRAY_SIZE(layouts), ""); if (p->format == AudioFormat.ENCODING_IEC61937) { p->channel_config = AudioFormat.CHANNEL_OUT_STEREO; - } else if (ao->channels.num == 1) { - p->channel_config = AudioFormat.CHANNEL_OUT_MONO; - } else if (ao->channels.num == 6) { - p->channel_config = AudioFormat.CHANNEL_OUT_5POINT1; - ao->channels = (struct mp_chmap)MP_CHMAP6(FL, FR, FC, LFE, BL, BR); } else { - p->channel_config = AudioFormat.CHANNEL_OUT_STEREO; - ao->channels = (struct mp_chmap)MP_CHMAP_INIT_STEREO; + struct mp_chmap_sel sel = {0}; + for (int i = 0; i < MP_ARRAY_SIZE(layouts); i++) { + if (layout_map[i]) + mp_chmap_sel_add_map(&sel, &layouts[i]); + } + if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) + goto error; + p->channel_config = layout_map[ao->channels.num]; + assert(p->channel_config); } jint buffer_size = MP_JNI_CALL_STATIC_INT( @@ -716,44 +712,52 @@ static int init(struct ao *ao) p->channel_config, p->format ); - if (buffer_size <= 0 || MP_JNI_EXCEPTION_LOG(ao) < 0) { + if (MP_JNI_EXCEPTION_LOG(ao) < 0 || buffer_size <= 0) { MP_FATAL(ao, "AudioTrack.getMinBufferSize returned an invalid size: %d", buffer_size); return -1; } - int min = 0.075 * p->samplerate * af_fmt_to_bytes(ao->format) * ao->channels.num; + // Choose double of the minimum buffer size suggested by the driver, but not + // less than 75ms or more than 150ms. + const int bps = af_fmt_to_bytes(ao->format); + int min = 0.075 * p->samplerate * bps * ao->channels.num; int max = min * 2; + min = MP_ALIGN_UP(min, bps); + max = MP_ALIGN_UP(max, bps); p->size = MPCLAMP(buffer_size * 2, min, max); MP_VERBOSE(ao, "Setting bufferSize = %d (driver=%d, min=%d, max=%d)\n", p->size, buffer_size, min, max); - ao->device_buffer = p->size / af_fmt_to_bytes(ao->format); + assert(p->size % bps == 0); + ao->device_buffer = p->size / bps; p->chunksize = p->size; - p->chunk = malloc(p->size); + p->chunk = talloc_size(ao, p->size); jobject timestamp = MP_JNI_NEW(AudioTimestamp.clazz, AudioTimestamp.ctor); - if (!timestamp || MP_JNI_EXCEPTION_LOG(ao) < 0) { + if (MP_JNI_EXCEPTION_LOG(ao) < 0 || !timestamp) { MP_FATAL(ao, "AudioTimestamp could not be created\n"); return -1; } p->timestamp = (*env)->NewGlobalRef(env, timestamp); - (*env)->DeleteLocalRef(env, timestamp); + MP_JNI_LOCAL_FREEP(×tamp); + // decide and create buffer of right type if (p->format == AudioFormat.ENCODING_IEC61937) { jshortArray shortarray = (*env)->NewShortArray(env, p->chunksize / 2); p->shortarray = (*env)->NewGlobalRef(env, shortarray); - (*env)->DeleteLocalRef(env, shortarray); - } else if (p->format == AudioFormat.ENCODING_PCM_FLOAT) { - jfloatArray floatarray = (*env)->NewFloatArray(env, p->chunksize / sizeof(float)); |