summaryrefslogtreecommitdiffstats
path: root/audio/out
diff options
context:
space:
mode:
Diffstat (limited to 'audio/out')
-rw-r--r--audio/out/ao.c65
-rw-r--r--audio/out/ao.h16
-rw-r--r--audio/out/ao_alsa.c36
-rw-r--r--audio/out/ao_audiotrack.c586
-rw-r--r--audio/out/ao_audiounit.m99
-rw-r--r--audio/out/ao_avfoundation.m372
-rw-r--r--audio/out/ao_coreaudio.c166
-rw-r--r--audio/out/ao_coreaudio_chmap.c232
-rw-r--r--audio/out/ao_coreaudio_chmap.h6
-rw-r--r--audio/out/ao_coreaudio_exclusive.c26
-rw-r--r--audio/out/ao_coreaudio_properties.c2
-rw-r--r--audio/out/ao_coreaudio_utils.c64
-rw-r--r--audio/out/ao_coreaudio_utils.h16
-rw-r--r--audio/out/ao_jack.c18
-rw-r--r--audio/out/ao_lavc.c29
-rw-r--r--audio/out/ao_null.c16
-rw-r--r--audio/out/ao_openal.c24
-rw-r--r--audio/out/ao_opensles.c17
-rw-r--r--audio/out/ao_oss.c401
-rw-r--r--audio/out/ao_pcm.c12
-rw-r--r--audio/out/ao_pipewire.c934
-rw-r--r--audio/out/ao_pulse.c64
-rw-r--r--audio/out/ao_sdl.c3
-rw-r--r--audio/out/ao_sndio.c323
-rw-r--r--audio/out/ao_wasapi.c147
-rw-r--r--audio/out/ao_wasapi.h9
-rw-r--r--audio/out/ao_wasapi_utils.c162
-rw-r--r--audio/out/buffer.c272
-rw-r--r--audio/out/internal.h21
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