diff options
Diffstat (limited to 'audio/out')
29 files changed, 3404 insertions, 734 deletions
diff --git a/audio/out/ao.c b/audio/out/ao.c index 52a38b63be..75fcbac6fa 100644 --- a/audio/out/ao.c +++ b/audio/out/ao.c @@ -35,11 +35,15 @@ #include "common/common.h" #include "common/global.h" +extern const struct ao_driver audio_out_oss; extern const struct ao_driver audio_out_audiotrack; extern const struct ao_driver audio_out_audiounit; extern const struct ao_driver audio_out_coreaudio; extern const struct ao_driver audio_out_coreaudio_exclusive; +extern const struct ao_driver audio_out_avfoundation; extern const struct ao_driver audio_out_rsound; +extern const struct ao_driver audio_out_pipewire; +extern const struct ao_driver audio_out_sndio; extern const struct ao_driver audio_out_pulse; extern const struct ao_driver audio_out_jack; extern const struct ao_driver audio_out_openal; @@ -62,6 +66,12 @@ static const struct ao_driver * const audio_out_drivers[] = { #if HAVE_COREAUDIO &audio_out_coreaudio, #endif +#if HAVE_AVFOUNDATION + &audio_out_avfoundation, +#endif +#if HAVE_PIPEWIRE + &audio_out_pipewire, +#endif #if HAVE_PULSE &audio_out_pulse, #endif @@ -71,6 +81,9 @@ static const struct ao_driver * const audio_out_drivers[] = { #if HAVE_WASAPI &audio_out_wasapi, #endif +#if HAVE_OSS_AUDIO + &audio_out_oss, +#endif // wrappers: #if HAVE_JACK &audio_out_jack, @@ -84,18 +97,20 @@ static const struct ao_driver * const audio_out_drivers[] = { #if HAVE_SDL2_AUDIO &audio_out_sdl, #endif +#if HAVE_SNDIO + &audio_out_sndio, +#endif &audio_out_null, #if HAVE_COREAUDIO &audio_out_coreaudio_exclusive, #endif &audio_out_pcm, &audio_out_lavc, - NULL }; static bool get_desc(struct m_obj_desc *dst, int index) { - if (index >= MP_ARRAY_SIZE(audio_out_drivers) - 1) + if (index >= MP_ARRAY_SIZE(audio_out_drivers)) return false; const struct ao_driver *ao = audio_out_drivers[index]; *dst = (struct m_obj_desc) { @@ -116,7 +131,6 @@ static bool get_desc(struct m_obj_desc *dst, int index) static const struct m_obj_list ao_obj_list = { .get_desc = get_desc, .description = "audio outputs", - .allow_unknown_entries = true, .allow_trailer = true, .disallow_positional_parameters = true, .use_global_options = true, @@ -302,7 +316,7 @@ struct ao *ao_init_best(struct mpv_global *global, } if (autoprobe) { - for (int n = 0; audio_out_drivers[n]; n++) { + for (int n = 0; n < MP_ARRAY_SIZE(audio_out_drivers); n++) { const struct ao_driver *driver = audio_out_drivers[n]; if (driver == &audio_out_null) break; @@ -442,8 +456,9 @@ struct ao_hotplug { void *wakeup_ctx; // A single AO instance is used to listen to hotplug events. It wouldn't // make much sense to allow multiple AO drivers; all sane platforms have - // a single such audio API. - // This is _not_ the same AO instance as used for playing audio. + // a single audio API providing all events. + // This is _not_ necessarily the same AO instance as used for playing + // audio. struct ao *ao; // cached struct ao_device_list *list; @@ -483,7 +498,8 @@ bool ao_hotplug_check_update(struct ao_hotplug *hp) } // The return value is valid until the next call to this API. -struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp) +struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp, + struct ao *playback_ao) { if (hp->list && !hp->needs_update) return hp->list; @@ -495,7 +511,20 @@ struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp) MP_TARRAY_APPEND(list, list->devices, list->num_devices, (struct ao_device_desc){"auto", "Autoselect device"}); - for (int n = 0; audio_out_drivers[n]; n++) { + // Try to use the same AO for hotplug handling as for playback. + // Different AOs may not agree and the playback one is the only one the + // user knows about and may even have configured explicitly. + if (!hp->ao && playback_ao && playback_ao->driver->hotplug_init) { + struct ao *ao = ao_alloc(true, hp->global, hp->wakeup_cb, hp->wakeup_ctx, + (char *)playback_ao->driver->name); + if (playback_ao->driver->hotplug_init(ao) >= 0) { + hp->ao = ao; + } else { + talloc_free(ao); + } + } + + for (int n = 0; n < MP_ARRAY_SIZE(audio_out_drivers); n++) { const struct ao_driver *d = audio_out_drivers[n]; if (d == &audio_out_null) break; // don't add unsafe/special entries @@ -506,10 +535,13 @@ struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp) continue; if (ao->driver->hotplug_init) { - if (!hp->ao && ao->driver->hotplug_init(ao) >= 0) - hp->ao = ao; // keep this one - if (hp->ao && hp->ao->driver == d) - get_devices(hp->ao, list); + if (ao->driver->hotplug_init(ao) >= 0) { + get_devices(ao, list); + if (hp->ao) + ao->driver->hotplug_uninit(ao); + else + hp->ao = ao; // keep this one + } } else { get_devices(ao, list); } @@ -558,10 +590,11 @@ static void dummy_wakeup(void *ctx) { } -void ao_print_devices(struct mpv_global *global, struct mp_log *log) +void ao_print_devices(struct mpv_global *global, struct mp_log *log, + struct ao *playback_ao) { struct ao_hotplug *hp = ao_hotplug_create(global, dummy_wakeup, NULL); - struct ao_device_list *list = ao_hotplug_get_device_list(hp); + struct ao_device_list *list = ao_hotplug_get_device_list(hp, playback_ao); mp_info(log, "List of detected audio devices:\n"); for (int n = 0; n < list->num_devices; n++) { struct ao_device_desc *desc = &list->devices[n]; @@ -583,7 +616,7 @@ void ao_set_gain(struct ao *ao, float gain) #define MUL_GAIN_f(d, num_samples, gain) \ for (int n = 0; n < (num_samples); n++) \ - (d)[n] = MPCLAMP(((d)[n]) * (gain), -1.0, 1.0) + (d)[n] = (d)[n] * (gain) static void process_plane(struct ao *ao, void *data, int num_samples) { @@ -672,7 +705,7 @@ static void convert_plane(int type, void *data, int num_samples) break; } default: - abort(); + MP_ASSERT_UNREACHABLE(); } } diff --git a/audio/out/ao.h b/audio/out/ao.h index cfcb39790f..18c7cdc02f 100644 --- a/audio/out/ao.h +++ b/audio/out/ao.h @@ -26,8 +26,7 @@ #include "audio/chmap_sel.h" enum aocontrol { - // _VOLUME commands take struct ao_control_vol pointer for input/output. - // If there's only one volume, SET should use average of left/right. + // _VOLUME commands take a pointer to float for input/output. AOCONTROL_GET_VOLUME, AOCONTROL_SET_VOLUME, // _MUTE commands take a pointer to bool @@ -57,13 +56,10 @@ enum { AO_INIT_STREAM_SILENCE = 1 << 2, // Force exclusive mode, i.e. lock out the system mixer. AO_INIT_EXCLUSIVE = 1 << 3, + // Initialize with music role. + AO_INIT_MEDIA_ROLE_MUSIC = 1 << 4, }; -typedef struct ao_control_vol { - float left; - float right; -} ao_control_vol_t; - struct ao_device_desc { const char *name; // symbolic name; will be set on ao->device const char *desc; // verbose human readable name @@ -102,7 +98,7 @@ void ao_set_gain(struct ao *ao, float gain); double ao_get_delay(struct ao *ao); void ao_reset(struct ao *ao); void ao_start(struct ao *ao); -void ao_set_paused(struct ao *ao, bool paused); +void ao_set_paused(struct ao *ao, bool paused, bool eof); void ao_drain(struct ao *ao); bool ao_is_playing(struct ao *ao); struct mp_async_queue; @@ -119,8 +115,8 @@ struct ao_hotplug *ao_hotplug_create(struct mpv_global *global, void *wakeup_ctx); void ao_hotplug_destroy(struct ao_hotplug *hp); bool ao_hotplug_check_update(struct ao_hotplug *hp); -struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp); +struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp, struct ao *playback_ao); -void ao_print_devices(struct mpv_global *global, struct mp_log *log); +void ao_print_devices(struct mpv_global *global, struct mp_log *log, struct ao *playback_ao); #endif /* MPLAYER_AUDIO_OUT_H */ diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c index 762805541f..92ea0db237 100644 --- a/audio/out/ao_alsa.c +++ b/audio/out/ao_alsa.c @@ -34,7 +34,6 @@ #include <math.h> #include <string.h> -#include "config.h" #include "options/options.h" #include "options/m_config.h" #include "options/m_option.h" @@ -57,9 +56,9 @@ struct ao_alsa_opts { char *mixer_device; char *mixer_name; int mixer_index; - int resample; - int ni; - int ignore_chmap; + bool resample; + bool ni; + bool ignore_chmap; int buffer_time; int frags; }; @@ -67,12 +66,12 @@ struct ao_alsa_opts { #define OPT_BASE_STRUCT struct ao_alsa_opts static const struct m_sub_options ao_alsa_conf = { .opts = (const struct m_option[]) { - {"alsa-resample", OPT_FLAG(resample)}, + {"alsa-resample", OPT_BOOL(resample)}, {"alsa-mixer-device", OPT_STRING(mixer_device)}, {"alsa-mixer-name", OPT_STRING(mixer_name)}, {"alsa-mixer-index", OPT_INT(mixer_index), M_RANGE(0, 99)}, - {"alsa-non-interleaved", OPT_FLAG(ni)}, - {"alsa-ignore-chmap", OPT_FLAG(ignore_chmap)}, + {"alsa-non-interleaved", OPT_BOOL(ni)}, + {"alsa-ignore-chmap", OPT_BOOL(ignore_chmap)}, {"alsa-buffer-time", OPT_INT(buffer_time), M_RANGE(0, INT_MAX)}, {"alsa-periods", OPT_INT(frags), M_RANGE(0, INT_MAX)}, {0} @@ -80,8 +79,6 @@ static const struct m_sub_options ao_alsa_conf = { .defaults = &(const struct ao_alsa_opts) { .mixer_device = "default", .mixer_name = "Master", - .mixer_index = 0, - .ni = 0, .buffer_time = 100000, .frags = 4, }, @@ -168,15 +165,13 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg) switch (cmd) { case AOCONTROL_SET_VOLUME: { - ao_control_vol_t *vol = arg; - set_vol = vol->left / f_multi + pmin + 0.5; + float *vol = arg; + set_vol = *vol / f_multi + pmin + 0.5; err = snd_mixer_selem_set_playback_volume(elem, 0, set_vol); CHECK_ALSA_ERROR("Error setting left channel"); MP_DBG(ao, "left=%li, ", set_vol); - set_vol = vol->right / f_multi + pmin + 0.5; - err = snd_mixer_selem_set_playback_volume(elem, 1, set_vol); CHECK_ALSA_ERROR("Error setting right channel"); MP_DBG(ao, "right=%li, pmin=%li, pmax=%li, mult=%f\n", @@ -184,12 +179,14 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg) break; } case AOCONTROL_GET_VOLUME: { - ao_control_vol_t *vol = arg; + float *vol = arg; + float left, right; snd_mixer_selem_get_playback_volume(elem, 0, &get_vol); - vol->left = (get_vol - pmin) * f_multi; + left = (get_vol - pmin) * f_multi; snd_mixer_selem_get_playback_volume(elem, 1, &get_vol); - vol->right = (get_vol - pmin) * f_multi; - MP_DBG(ao, "left=%f, right=%f\n", vol->left, vol->right); + right = (get_vol - pmin) * f_multi; + *vol = (left + right) / 2.0; + MP_DBG(ao, "vol=%f\n", *vol); break; } case AOCONTROL_SET_MUTE: { @@ -563,7 +560,7 @@ static char *append_params(void *ta_parent, const char *device, const char *p) /* a simple list of parameters: add it at the end of the list */ return talloc_asprintf(ta_parent, "%s,%s", device, p); } - abort(); + MP_ASSERT_UNREACHABLE(); } static int try_open_device(struct ao *ao, const char *device, int mode) @@ -626,7 +623,8 @@ static void uninit(struct ao *ao) CHECK_ALSA_ERROR("pcm close error"); } -alsa_error: ; +alsa_error: + snd_config_update_free_global(); } #define INIT_DEVICE_ERR_GENERIC -1 diff --git a/audio/out/ao_audiotrack.c b/audio/out/ao_audiotrack.c index 7a1715d373..db1da9c407 100644 --- a/audio/out/ao_audiotrack.c +++ b/audio/out/ao_audiotrack.c @@ -54,34 +54,32 @@ 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; jmethodID release; jmethodID getState; jmethodID getPlayState; @@ -91,9 +89,9 @@ struct JNIAudioTrack { jmethodID pause; jmethodID write; jmethodID writeFloat; - jmethodID writeV23; jmethodID writeShortV23; jmethodID writeBufferV21; + jmethodID getBufferSizeInFramesV23; jmethodID getPlaybackHeadPosition; jmethodID getTimestamp; jmethodID getLatency; @@ -109,133 +107,234 @@ 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", "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", "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 + +static struct JNIAudioAttributes { + jclass clazz; + jint CONTENT_TYPE_MOVIE; + jint CONTENT_TYPE_MUSIC; + jint USAGE_MEDIA; +} 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 + +static struct JNIAudioAttributesBuilder { + jclass clazz; + jmethodID ctor; + jmethodID setUsage; + jmethodID setContentType; + jmethodID build; +} 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 + +static struct JNIAudioFormatBuilder { + jclass clazz; + jmethodID ctor; + jmethodID setEncoding; + jmethodID setSampleRate; + jmethodID setChannelMask; + jmethodID build; +} 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 -}}; +}; +#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) { struct priv *p = ao->priv; JNIEnv *env = MP_JNI_GET_ENV(ao); + jobject audiotrack = NULL; - jobject audiotrack = MP_JNI_NEW( - AudioTrack.clazz, - AudioTrack.ctor, - AudioManager.STREAM_MUSIC, - p->samplerate, - p->channel_config, - p->format, - p->size, - AudioTrack.MODE_STREAM, - p->cfg_session_id - ); - if (!audiotrack || MP_JNI_EXCEPTION_LOG(ao) < 0) { + if (AudioTrack.ctorV21) { + MP_VERBOSE(ao, "Using API21 initializer\n"); + jobject tmp = NULL; + + 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_LOCAL_FREEP(&tmp); + tmp = MP_JNI_CALL_OBJECT(format_builder, AudioFormatBuilder.setSampleRate, p->samplerate); + MP_JNI_LOCAL_FREEP(&tmp); + tmp = MP_JNI_CALL_OBJECT(format_builder, AudioFormatBuilder.setChannelMask, p->channel_config); + MP_JNI_LOCAL_FREEP(&tmp); + jobject format = MP_JNI_CALL_OBJECT(format_builder, AudioFormatBuilder.build); + 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_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_CAL |