summaryrefslogtreecommitdiffstats
path: root/audio/out/ao_audiotrack.c
diff options
context:
space:
mode:
Diffstat (limited to 'audio/out/ao_audiotrack.c')
-rw-r--r--audio/out/ao_audiotrack.c548
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(&timestamp);
+ // 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));