ao/aaudio: implement aaudio backend for android

This commit is contained in:
Jun Bo Bi
2023-08-27 04:46:35 -04:00
committed by sfan5
parent 24000e3b3f
commit 57d9d4eb42
8 changed files with 591 additions and 0 deletions
+16
View File
@@ -98,6 +98,22 @@ typedef const char * const (mp_ch_layout_tuple)[2];
{7, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e), MP_SP(f), MP_SP(g)}}
#define MP_CHMAP8(a, b, c, d, e, f, g, h) \
{8, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e), MP_SP(f), MP_SP(g), MP_SP(h)}}
#define MP_CHMAP9(a, b, c, d, e, f, g, h, i) \
{9, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e), MP_SP(f), MP_SP(g), MP_SP(h), MP_SP(i)}}
#define MP_CHMAP10(a, b, c, d, e, f, g, h, i, j) \
{10, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e), MP_SP(f), MP_SP(g), MP_SP(h), MP_SP(i), MP_SP(j)}}
#define MP_CHMAP11(a, b, c, d, e, f, g, h, i, j, k) \
{11, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e), MP_SP(f), MP_SP(g), MP_SP(h), MP_SP(i), MP_SP(j), MP_SP(k)}},
#define MP_CHMAP12(a, b, c, d, e, f, g, h, i, j, k, l) \
{12, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e), MP_SP(f), MP_SP(g), MP_SP(h), MP_SP(i), MP_SP(j), MP_SP(k), MP_SP(l)}}
#define MP_CHMAP13(a, b, c, d, e, f, g, h, i, j, k, l, m) \
{13, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e), MP_SP(f), MP_SP(g), MP_SP(h), MP_SP(i), MP_SP(j), MP_SP(k), MP_SP(l), MP_SP(m)}}
#define MP_CHMAP14(a, b, c, d, e, f, g, h, i, j, k, l, m, n) \
{14, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e), MP_SP(f), MP_SP(g), MP_SP(h), MP_SP(i), MP_SP(j), MP_SP(k), MP_SP(l), MP_SP(m), MP_SP(n)}}
#define MP_CHMAP15(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) \
{15, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e), MP_SP(f), MP_SP(g), MP_SP(h), MP_SP(i), MP_SP(j), MP_SP(k), MP_SP(l), MP_SP(m), MP_SP(n), MP_SP(o)}}
#define MP_CHMAP16(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
{16, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e), MP_SP(f), MP_SP(g), MP_SP(h), MP_SP(i), MP_SP(j), MP_SP(k), MP_SP(l), MP_SP(m), MP_SP(n), MP_SP(o), MP_SP(p)}}
#define MP_CHMAP_INIT_MONO {1, {MP_SPEAKER_ID_FC}}
#define MP_CHMAP_INIT_STEREO MP_CHMAP2(FL, FR)
+50
View File
@@ -0,0 +1,50 @@
/* Stream builder functions */
AAUDIO_FUNCTION(AAudio_createStreamBuilder, aaudio_result_t, AAudioStreamBuilder **builder)
AAUDIO_FUNCTION(AAudioStreamBuilder_setDeviceId, void, AAudioStreamBuilder *builder, int32_t deviceId)
AAUDIO_FUNCTION(AAudioStreamBuilder_setSampleRate, void, AAudioStreamBuilder *builder, int32_t sampleRate)
AAUDIO_FUNCTION(AAudioStreamBuilder_setChannelCount, void, AAudioStreamBuilder *builder, int32_t channelCount)
AAUDIO_FUNCTION(AAudioStreamBuilder_setSamplesPerFrame, void, AAudioStreamBuilder *builder, int32_t samplesPerFrame)
AAUDIO_FUNCTION(AAudioStreamBuilder_setFormat, void, AAudioStreamBuilder *builder, aaudio_format_t format)
AAUDIO_FUNCTION(AAudioStreamBuilder_setSharingMode, void, AAudioStreamBuilder *builder, aaudio_sharing_mode_t sharingMode)
AAUDIO_FUNCTION(AAudioStreamBuilder_setDirection, void, AAudioStreamBuilder *builder, aaudio_direction_t direction)
AAUDIO_FUNCTION(AAudioStreamBuilder_setBufferCapacityInFrames, void, AAudioStreamBuilder *builder, int32_t numFrames)
AAUDIO_FUNCTION(AAudioStreamBuilder_setPerformanceMode, void, AAudioStreamBuilder *builder, aaudio_performance_mode_t mode)
AAUDIO_FUNCTION(AAudioStreamBuilder_setDataCallback, void, AAudioStreamBuilder *builder, AAudioStream_dataCallback callback, void *userData)
AAUDIO_FUNCTION(AAudioStreamBuilder_setFramesPerDataCallback, void, AAudioStreamBuilder *builder, int32_t numFrames)
AAUDIO_FUNCTION(AAudioStreamBuilder_setErrorCallback, void, AAudioStreamBuilder *builder, AAudioStream_errorCallback callback, void *userData)
AAUDIO_FUNCTION(AAudioStreamBuilder_openStream, aaudio_result_t, AAudioStreamBuilder *builder, AAudioStream **stream)
AAUDIO_FUNCTION(AAudioStreamBuilder_delete, aaudio_result_t, AAudioStreamBuilder *builder)
/* Stream control functions */
AAUDIO_FUNCTION(AAudioStream_close, aaudio_result_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_requestStart, aaudio_result_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_requestPause, aaudio_result_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_requestFlush, aaudio_result_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_requestStop, aaudio_result_t, AAudioStream *stream)
/* Stream query functions */
AAUDIO_FUNCTION(AAudioStream_getState, aaudio_stream_state_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_waitForStateChange, aaudio_result_t, AAudioStream *stream, aaudio_stream_state_t inputState, aaudio_stream_state_t *nextState, int64_t timeoutNanoseconds)
AAUDIO_FUNCTION(AAudioStream_read, aaudio_result_t, AAudioStream *stream, void *buffer, int32_t numFrames, int64_t timeoutNanoseconds)
AAUDIO_FUNCTION(AAudioStream_write, aaudio_result_t, AAudioStream *stream, const void *buffer, int32_t numFrames, int64_t timeoutNanoseconds)
AAUDIO_FUNCTION(AAudioStream_setBufferSizeInFrames, aaudio_result_t, AAudioStream *stream, int32_t numFrames)
AAUDIO_FUNCTION(AAudioStream_getBufferSizeInFrames, int32_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_getFramesPerBurst, int32_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_getBufferCapacityInFrames, int32_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_getFramesPerDataCallback, int32_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_getXRunCount, int32_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_getSampleRate, int32_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_getChannelCount, int32_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_getSamplesPerFrame, int32_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_getDeviceId, int32_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_getFormat, aaudio_format_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_getSharingMode, aaudio_sharing_mode_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_getPerformanceMode, aaudio_performance_mode_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_getDirection, aaudio_direction_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_getFramesWritten, int64_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_getFramesRead, int64_t, AAudioStream *stream)
AAUDIO_FUNCTION(AAudioStream_getTimestamp, aaudio_result_t, AAudioStream *stream, clockid_t clockid, int64_t* framePosition, int64_t* timeNanoseconds)
/* Utility functions */
AAUDIO_FUNCTION(AAudio_convertResultToText, const char *, aaudio_result_t returnCode)
AAUDIO_FUNCTION(AAudio_convertStreamStateToText, const char*, aaudio_stream_state_t state)
+4
View File
@@ -0,0 +1,4 @@
/* Stream builder functions - API 28 */
AAUDIO_FUNCTION(AAudioStreamBuilder_setUsage, void, AAudioStreamBuilder *builder, aaudio_usage_t usage)
AAUDIO_FUNCTION(AAudioStreamBuilder_setContentType, void, AAudioStreamBuilder *builder, aaudio_content_type_t contentType)
AAUDIO_FUNCTION(AAudioStreamBuilder_setSessionId, void, AAudioStreamBuilder* builder, aaudio_session_id_t sessionId)
+1
View File
@@ -0,0 +1 @@
AAUDIO_FUNCTION(AAudioStreamBuilder_setChannelMask, void, AAudioStreamBuilder *builder, aaudio_channel_mask_t channelMask)
+4
View File
@@ -54,12 +54,16 @@ extern const struct ao_driver audio_out_wasapi;
extern const struct ao_driver audio_out_pcm;
extern const struct ao_driver audio_out_lavc;
extern const struct ao_driver audio_out_sdl;
extern const struct ao_driver audio_out_aaudio;
static const struct ao_driver * const audio_out_drivers[] = {
// native:
#if HAVE_AUDIOTRACK
&audio_out_audiotrack,
#endif
#if HAVE_AAUDIO
&audio_out_aaudio,
#endif
#if HAVE_AUDIOUNIT
&audio_out_audiounit,
#endif
+508
View File
@@ -0,0 +1,508 @@
/*
* AAudio audio output driver
*
* Copyright (C) 2024 Jun Bo Bi <jambonmcyeah@gmail.com>
*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <dlfcn.h>
#include <stdint.h>
#include <aaudio/AAudio.h>
#include <android/api-level.h>
#include "ao.h"
#include "audio/format.h"
#include "common/common.h"
#include "common/msg.h"
#include "internal.h"
#include "options/m_option.h"
#include "osdep/timer.h"
struct priv {
AAudioStream *stream;
int32_t device_id;
aaudio_session_id_t session_id;
int32_t buffer_capacity;
aaudio_performance_mode_t performance_mode;
int64_t presented;
int64_t discarded;
int device_api;
void *lib_handle;
#define AAUDIO_FUNCTION(name, ret, ...) ret (*name)(__VA_ARGS__);
#include "aaudio_functions26.inc"
#include "aaudio_functions28.inc"
#include "aaudio_functions32.inc"
#undef AAUDIO_FUNCTION
};
struct function_map {
const char *symbol;
int offset;
};
#define AAUDIO_FUNCTION(name, ret, ...) {#name, offsetof(struct priv, name)},
static const struct function_map lib_functions26[] = {
#include "aaudio_functions26.inc"
};
static const struct function_map lib_functions28[] = {
#include "aaudio_functions28.inc"
};
static const struct function_map lib_functions32[] = {
#include "aaudio_functions32.inc"
};
#undef AAUDIO_FUNCTION
static const struct {
int api_level;
int length;
const struct function_map *functions;
} lib_functions[] = {
{26, MP_ARRAY_SIZE(lib_functions26), lib_functions26},
{28, MP_ARRAY_SIZE(lib_functions28), lib_functions28},
{32, MP_ARRAY_SIZE(lib_functions32), lib_functions32}
};
/*
* There is no documentation in AAudio for the order of positions for AAudio.
* It's assumed to work the same way as AudioTrack (even the order of the bits
* for the position mask is the same for both)
* See https://developer.android.com/reference/android/media/AudioFormat#channelPositionMask
*/
static const struct mp_chmap aaudio_default_chmaps[] = {
{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 */
{0},
MP_CHMAP10(FL, FR, FC, LFE, BL, BR, SL, SR, TSL, TSR), /* 7.1.2 */
{0},
MP_CHMAP12(FL, FR, FC, LFE, BL, BR, SL, SR, TFL, TFR, TBL, TBR), /* 7.1.4 */
{0},
MP_CHMAP14(FL, FR, FC, LFE, BL, BR, SL, SR, TFL, TFR, TBL, TBR, WL, WR), /* 9.1.4 */
{0},
MP_CHMAP16(FL, FR, FC, LFE, BL, BR, SL, SR, TFL, TFR, TBL, TBR, TSL, TSR, WL, WR) /* 9.1.6 */
};
static const struct mp_chmap aaudio_chmaps[] = {
{0}, /* empty */
/*
* This should be `{1, {MP_SP(FL)}}` according to spec
* but `mp_chmap_sel` doesn't like it
*/
MP_CHMAP_INIT_MONO, /* mono */
MP_CHMAP_INIT_STEREO, /* stereo */
MP_CHMAP3(FL, FR, LFE), /* 2.1 */
MP_CHMAP3(FL, FR, FC), /* 3.0 */
MP_CHMAP3(FL, FR, BC), /* 3.0 (back) */
MP_CHMAP4(FL, FR, FC, LFE), /* 3.1 */
MP_CHMAP4(FL, FR, TSL, TSR), /* 2.0.2 */
MP_CHMAP5(FL, FR, LFE, TSL, TSR), /* 2.1.2 */
MP_CHMAP5(FL, FR, FC, TSL, TSR), /* 3.0.2 */
MP_CHMAP6(FL, FR, FC, LFE, TSL, TSR), /* 3.1.2 */
MP_CHMAP4(FL, FR, BL, BR), /* quad */
MP_CHMAP4(FL, FR, SL, SR), /* quad (side) */
MP_CHMAP4(FL, FR, FC, BC), /* quad (center) */
MP_CHMAP5(FL, FR, FC, BL, BR), /* 5.0 */
MP_CHMAP6(FL, FR, FC, LFE, BL, BR), /* 5.1 */
MP_CHMAP6(FL, FR, FC, LFE, SL, SR), /* 5.1 (side) */
MP_CHMAP7(FL, FR, FC, LFE, BL, BR, BC), /* 6.1 */
MP_CHMAP8(FL, FR, FC, LFE, BL, BR, SL, SR), /* 7.1 */
MP_CHMAP8(FL, FR, FC, LFE, BL, BR, TSL, TSR), /* 5.1.2 */
MP_CHMAP10(FL, FR, FC, LFE, BL, BR, TFL, TFR, TBL, TBR), /* 5.1.4 */
MP_CHMAP10(FL, FR, FC, LFE, BL, BR, SL, SR, TSL, TSR), /* 7.1.2 */
MP_CHMAP12(FL, FR, FC, LFE, BL, BR, SL, SR, TFL, TFR, TBL, TBR), /* 7.1.4 */
MP_CHMAP14(FL, FR, FC, LFE, BL, BR, SL, SR, TFL, TFR, TBL, TBR, WL, WR), /* 9.1.4 */
MP_CHMAP16(FL, FR, FC, LFE, BL, BR, SL, SR, TFL, TFR, TBL, TBR, TSL, TSR, WL, WR) /* 9.1.6 */
};
static const aaudio_channel_mask_t aaudio_masks[] = {
AAUDIO_CHANNEL_INVALID,
AAUDIO_CHANNEL_MONO,
AAUDIO_CHANNEL_STEREO,
AAUDIO_CHANNEL_2POINT1,
AAUDIO_CHANNEL_TRI,
AAUDIO_CHANNEL_TRI_BACK,
AAUDIO_CHANNEL_3POINT1,
AAUDIO_CHANNEL_2POINT0POINT2,
AAUDIO_CHANNEL_2POINT1POINT2,
AAUDIO_CHANNEL_3POINT0POINT2,
AAUDIO_CHANNEL_3POINT1POINT2,
AAUDIO_CHANNEL_QUAD,
AAUDIO_CHANNEL_QUAD_SIDE,
AAUDIO_CHANNEL_SURROUND,
AAUDIO_CHANNEL_PENTA,
AAUDIO_CHANNEL_5POINT1,
AAUDIO_CHANNEL_5POINT1_SIDE,
AAUDIO_CHANNEL_6POINT1,
AAUDIO_CHANNEL_7POINT1,
AAUDIO_CHANNEL_5POINT1POINT2,
AAUDIO_CHANNEL_5POINT1POINT4,
AAUDIO_CHANNEL_7POINT1POINT2,
AAUDIO_CHANNEL_7POINT1POINT4,
AAUDIO_CHANNEL_9POINT1POINT4,
AAUDIO_CHANNEL_9POINT1POINT6
};
static_assert(MP_ARRAY_SIZE(aaudio_chmaps) == MP_ARRAY_SIZE(aaudio_masks),
"`aaudio_masks` and `aaudio_chmaps` MUST have the same number of entries.");
/*
* The values corresponds to indexes of `aaudio_chmaps`
*
* Different devices may support different number of max channels
* depending on what they set `FCC_LIMIT` to
* See https://cs.android.com/android/platform/superproject/+/android-latest-release:system/media/audio/include/system/audio.h;l=234
*/
static const uint8_t aaudio_max_chnums[] = {
MP_ARRAY_SIZE(aaudio_chmaps),
22, /* FCC_12 */
19, /* FCC_8 */
2, /* FCC_2 */
1 /* FCC_1 */
};
static bool load_lib_functions(struct ao *ao)
{
struct priv *p = ao->priv;
p->device_api = android_get_device_api_level();
p->lib_handle = dlopen("libaaudio.so", RTLD_NOW | RTLD_GLOBAL);
if (!p->lib_handle)
return false;
for (int i = 0; i < MP_ARRAY_SIZE(lib_functions); i++) {
if (p->device_api < lib_functions[i].api_level)
break;
for (int j = 0; j < lib_functions[i].length; j++) {
const char *sym = lib_functions[i].functions[j].symbol;
void *fun = dlsym(p->lib_handle, sym);
if (!fun)
fun = dlsym(RTLD_DEFAULT, sym);
if (!fun) {
MP_WARN(ao, "Could not resolve symbol %s\n", sym);
return false;
}
*(void **)((uint8_t *)p + lib_functions[i].functions[j].offset) = fun;
}
}
return true;
}
static void error_callback(AAudioStream *stream, void *context, aaudio_result_t error)
{
struct ao *ao = context;
struct priv *p = ao->priv;
MP_ERR(ao, "%s, trying to reload...\n", p->AAudio_convertResultToText(error));
ao_request_reload(ao);
}
static aaudio_data_callback_result_t data_callback(AAudioStream *stream, void *context,
void *data, int32_t nframes)
{
struct ao *ao = context;
struct priv *p = ao->priv;
aaudio_result_t result;
int64_t presented, present_time;
int64_t written = p->AAudioStream_getFramesWritten(stream);
if ((result = p->AAudioStream_getTimestamp(stream, CLOCK_MONOTONIC, &presented, &present_time)) < 0) {
MP_TRACE(ao, "AAudioStream_getTimestamp() returned %s\n",
p->AAudio_convertResultToText(result));
presented = p->presented;
} else {
p->presented = presented;
}
int64_t end_time = mp_time_ns();
end_time += MP_TIME_S_TO_NS(nframes) / ao->samplerate;
end_time += MP_TIME_S_TO_NS(written - (presented + p->discarded)) / ao->samplerate;
ao_read_data(ao, &data, nframes, end_time, NULL, true, true);
return AAUDIO_CALLBACK_RESULT_CONTINUE;
}
static void uninit(struct ao *ao)
{
struct priv *p = ao->priv;
if (p->stream) {
p->AAudioStream_close(p->stream);
p->stream = NULL;
}
if (p->lib_handle) {
dlclose(p->lib_handle);
p->lib_handle = NULL;
}
}
static int init(struct ao *ao)
{
if (!load_lib_functions(ao))
return -1;
struct priv *p = ao->priv;
aaudio_result_t result;
AAudioStreamBuilder *builder;
if ((result = p->AAudio_createStreamBuilder(&builder)) < 0) {
MP_ERR(ao, "AAudio_createStreamBuilder() returned %s\n",
p->AAudio_convertResultToText(result));
return -1;
}
aaudio_format_t format;
if (p->device_api >= 34 && af_fmt_is_spdif(ao->format)) {
format = AAUDIO_FORMAT_IEC61937;
} else if (af_fmt_is_float(ao->format)) {
ao->format = AF_FORMAT_FLOAT;
format = AAUDIO_FORMAT_PCM_FLOAT;
} else if (af_fmt_is_int(ao->format)) {
if (af_fmt_to_bytes(ao->format) > 2 && p->device_api >= 31) {
ao->format = AF_FORMAT_S32;
format = AAUDIO_FORMAT_PCM_I32;
} else {
ao->format = AF_FORMAT_S16;
format = AAUDIO_FORMAT_PCM_I16;
}
} else {
ao->format = AF_FORMAT_S16;
format = AAUDIO_FORMAT_PCM_I16;
}
p->AAudioStreamBuilder_setDeviceId(builder, p->device_id);
p->AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
p->AAudioStreamBuilder_setSharingMode(builder,
(ao->init_flags & AO_INIT_EXCLUSIVE)
? AAUDIO_SHARING_MODE_EXCLUSIVE
: AAUDIO_SHARING_MODE_SHARED);
p->AAudioStreamBuilder_setFormat(builder, format);
p->AAudioStreamBuilder_setSampleRate(builder, ao->samplerate);
p->AAudioStreamBuilder_setErrorCallback(builder, error_callback, ao);
p->AAudioStreamBuilder_setBufferCapacityInFrames(builder,
p->buffer_capacity);
p->AAudioStreamBuilder_setPerformanceMode(builder, p->performance_mode);
p->AAudioStreamBuilder_setDataCallback(builder, data_callback, ao);
if (p->device_api >= 28) {
p->AAudioStreamBuilder_setContentType(builder,
(ao->init_flags &
AO_INIT_MEDIA_ROLE_MUSIC)
? AAUDIO_CONTENT_TYPE_MUSIC
: AAUDIO_CONTENT_TYPE_MOVIE);
p->AAudioStreamBuilder_setUsage(builder, AAUDIO_USAGE_MEDIA);
p->AAudioStreamBuilder_setSessionId(builder, p->session_id);
}
if (p->device_api >= 32) {
uint8_t i = 0;
struct mp_chmap channels = ao->channels;
do {
struct mp_chmap_sel sel = {0, .tmp = p};
aaudio_channel_mask_t channel_mask = AAUDIO_CHANNEL_INVALID;
for (int j = 1; j < aaudio_max_chnums[i]; j++)
mp_chmap_sel_add_map(&sel, &aaudio_chmaps[j]);
if (!ao_chmap_sel_adjust(ao, &sel, &channels)) {
MP_ERR(ao, "Failed to find channel map\n");
goto err;
}
for (uint8_t j = 0; j < aaudio_max_chnums[i]; j++) {
if (mp_chmap_equals(&aaudio_chmaps[j], &channels)) {
channel_mask = aaudio_masks[j];
break;
}
}
assert(channel_mask != AAUDIO_CHANNEL_INVALID);
p->AAudioStreamBuilder_setChannelMask(builder, channel_mask);
result = p->AAudioStreamBuilder_openStream(builder, &p->stream);
if (result != AAUDIO_ERROR_OUT_OF_RANGE) {
break;
}
} while (i++ < MP_ARRAY_SIZE(aaudio_max_chnums));
ao->channels = channels;
} else {
result = p->AAudioStreamBuilder_openStream(builder, &p->stream);
}
if (result < 0) {
MP_ERR(ao, "AAudioStreamBuilder_openStream() returned %s\n",
p->AAudio_convertResultToText(result));
goto err;
}
if (p->device_api < 32) {
int32_t channel_count = p->AAudioStream_getChannelCount(p->stream);
if (channel_count >= MP_ARRAY_SIZE(aaudio_default_chmaps) ||
(ao->channels = aaudio_default_chmaps[channel_count]).num == 0) {
MP_ERR(ao, "Unknown layout for channel count: %" PRId32 "\n",
channel_count);
goto err;
}
}
ao->device_buffer = p->AAudioStream_getBufferCapacityInFrames(p->stream);
p->AAudioStreamBuilder_delete(builder);
return 1;
err:
p->AAudioStreamBuilder_delete(builder);
return -1;
}
static void start(struct ao *ao)
{
struct priv *p = ao->priv;
aaudio_result_t result;
p->discarded = p->AAudioStream_getFramesWritten(p->stream) - p->presented;
if ((result = p->AAudioStream_requestStart(p->stream)) < 0) {
MP_ERR(ao, "AAudioStream_requestStart() returned %s\n",
p->AAudio_convertResultToText(result));
return ao_request_reload(ao);
}
}
static bool set_pause(struct ao *ao, bool paused)
{
struct priv *p = ao->priv;
aaudio_result_t result = paused
? p->AAudioStream_requestPause(p->stream)
: p->AAudioStream_requestStart(p->stream);
if (result < 0) {
MP_ERR(ao, "AAudioStream_request%s() returned %s\n",
paused ? "Pause" : "Start",
p->AAudio_convertResultToText(result));
ao_request_reload(ao);
return false;
}
return true;
}
static void reset(struct ao *ao)
{
struct priv *p = ao->priv;
aaudio_result_t result;
aaudio_stream_state_t state = p->AAudioStream_getState(p->stream);
switch (state) {
case AAUDIO_STREAM_STATE_STARTING:
case AAUDIO_STREAM_STATE_STOPPING:
case AAUDIO_STREAM_STATE_PAUSING:
case AAUDIO_STREAM_STATE_FLUSHING:
if ((result = p->AAudioStream_waitForStateChange(p->stream,
state, &state, INT64_MAX)) < 0) {
MP_ERR(ao, "AAudioStream_waitForStateChange() returned %s\n",
p->AAudio_convertResultToText(result));
return ao_request_reload(ao);
}
}
if (state != AAUDIO_STREAM_STATE_PAUSED) {
if ((result = p->AAudioStream_requestPause(p->stream)) < 0) {
MP_ERR(ao, "AAudioStream_requestPause() %s\n",
p->AAudio_convertResultToText(result));
return ao_request_reload(ao);
}
if ((result = p->AAudioStream_waitForStateChange(p->stream,
AAUDIO_STREAM_STATE_PAUSING, &state, INT64_MAX)) < 0) {
MP_ERR(ao, "AAudioStream_waitForStateChange() returned %s\n",
p->AAudio_convertResultToText(result));
return ao_request_reload(ao);
}
}
if ((result = p->AAudioStream_requestFlush(p->stream)) < 0) {
MP_ERR(ao, "AAudioStream_requestFlush() returned %s\n",
p->AAudio_convertResultToText(result));
return ao_request_reload(ao);
}
}
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_aaudio = {
.description = "AAudio audio output",
.name = "aaudio",
.init = init,
.uninit = uninit,
.start = start,
.reset = reset,
.set_pause = set_pause,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
.device_id = AAUDIO_UNSPECIFIED,
.session_id = AAUDIO_SESSION_ID_NONE,
.buffer_capacity = AAUDIO_UNSPECIFIED,
.performance_mode = AAUDIO_PERFORMANCE_MODE_NONE,
.presented = 0,
.discarded = 0,
.stream = NULL,
.lib_handle = NULL
},
.options_prefix = "aaudio",
.options =
(const struct m_option[]){
{"device-id", OPT_CHOICE(device_id,
{"auto", AAUDIO_UNSPECIFIED}),
M_RANGE(INT32_MIN, INT32_MAX)},
{"session-id", OPT_CHOICE(session_id,
{"none", AAUDIO_SESSION_ID_NONE}),
M_RANGE(INT32_MIN, INT32_MAX)},
{"buffer-capacity", OPT_CHOICE(buffer_capacity,
{"auto", AAUDIO_UNSPECIFIED}),
M_RANGE(1, INT32_MAX)},
{"performance-mode", OPT_CHOICE(performance_mode,
{"none", AAUDIO_PERFORMANCE_MODE_NONE},
{"low-latency", AAUDIO_PERFORMANCE_MODE_LOW_LATENCY},
{"power-saving", AAUDIO_PERFORMANCE_MODE_POWER_SAVING})},
{0}},
};
+7
View File
@@ -866,6 +866,13 @@ if features['audiotrack']
sources += files('audio/out/ao_audiotrack.c')
endif
aaudio_opt = get_option('aaudio').require(features['android'])
features += {'aaudio': cc.has_header_symbol('aaudio/AAudio.h',
'AAudioStream', required: aaudio_opt)}
if features['aaudio']
sources += files('audio/out/ao_aaudio.c')
endif
opensles = cc.find_library('OpenSLES', required: get_option('opensles'))
features += {'opensles': opensles.found()}
if features['opensles']
+1
View File
@@ -46,6 +46,7 @@ option('avfoundation', type: 'feature', value: 'auto', description: 'AVFoundatio
option('jack', type: 'feature', value: 'auto', description: 'JACK audio output')
option('openal', type: 'feature', value: 'disabled', description: 'OpenAL audio output')
option('audiotrack', type: 'feature', value: 'auto', description: 'Android AudioTrack audio output')
option('aaudio', type: 'feature', value: 'auto', description: 'Android AAudio audio output')
option('opensles', type: 'feature', value: 'auto', description: 'OpenSL ES audio output')
option('oss-audio', type: 'feature', value: 'auto', description: 'OSSv4 audio output')
option('pipewire', type: 'feature', value: 'auto', description: 'PipeWire audio output')