Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ao/aaudio: implement aaudio backend for android #12261

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions audio/out/aaudio_functions26.inc
Original file line number Diff line number Diff line change
@@ -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, int64_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)
3 changes: 3 additions & 0 deletions audio/out/aaudio_functions28.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/* 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)
6 changes: 5 additions & 1 deletion audio/out/ao.c
Original file line number Diff line number Diff line change
Expand Up @@ -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_ANDROID
#if HAVE_AUDIOTRACK
&audio_out_audiotrack,
#endif
#if HAVE_AAUDIO
&audio_out_aaudio,
#endif
#if HAVE_AUDIOUNIT
&audio_out_audiounit,
#endif
Expand Down
303 changes: 303 additions & 0 deletions audio/out/ao_aaudio.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
/*
* AAudio audio output driver
*
* Copyright (C) 2024 Jun Bo Bi <[email protected]>
*
* 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 <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <dlfcn.h>

#include <android/api-level.h>
#include <aaudio/AAudio.h>

#include "ao.h"
#include "audio/format.h"
#include "osdep/timer.h"
#include "common/msg.h"
#include "internal.h"
#include "options/m_option.h"

struct priv {
AAudioStreamBuilder *builder;
AAudioStream *stream;

int32_t device_id;
int32_t buffer_capacity;
aaudio_performance_mode_t performance_mode;

int device_api;
void *lib_handle;

#define AAUDIO_FUNCTION(name, ret, ...) ret (*name)(__VA_ARGS__);
#include "aaudio_functions26.inc"
#include "aaudio_functions28.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"
};
#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}
};

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;

int64_t written = p->AAudioStream_getFramesWritten(stream);

int64_t presented;
int64_t present_time;
p->AAudioStream_getTimestamp(stream, CLOCK_MONOTONIC, &presented, &present_time);

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) / ao->samplerate;

bool eof;
ao_read_data(ao, &data, nframes, end_time, &eof, true, true);

return eof ? AAUDIO_CALLBACK_RESULT_STOP : AAUDIO_CALLBACK_RESULT_CONTINUE;
}

static void uninit(struct ao *ao)
{
struct priv *p = ao->priv;

if (p->builder) {
p->AAudioStreamBuilder_delete(p->builder);
p->builder = NULL;
}

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)) {
struct priv *p = ao->priv;

aaudio_result_t result;
AAudioStreamBuilder *builder;

if ((result = p->AAudio_createStreamBuilder(&builder)) >= 0) {
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_setChannelCount(builder, ao->channels.num);
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, AAUDIO_CONTENT_TYPE_MOVIE);
p->AAudioStreamBuilder_setUsage(builder, AAUDIO_USAGE_MEDIA);
}

p->builder = builder;

if ((result = p->AAudioStreamBuilder_openStream(p->builder, &p->stream)) >= 0)
ao->device_buffer = p->AAudioStream_getBufferCapacityInFrames(p->stream);
}
if (result >= 0)
return 1;

MP_ERR(ao, "Failed to open stream: %s\n", p->AAudio_convertResultToText(result));
}

return -1;
}

static void start(struct ao *ao)
{
struct priv *p = ao->priv;

aaudio_result_t result = AAUDIO_OK;
if (!p->stream) {
if ((result = p->AAudioStreamBuilder_openStream(p->builder, &p->stream)) >= 0)
ao->device_buffer = p->AAudioStream_getBufferCapacityInFrames(p->stream);
}

if (result >= 0) {
aaudio_stream_state_t next;

if ((result = p->AAudioStream_requestStart(p->stream)) >= 0)
result = p->AAudioStream_waitForStateChange(
p->stream, AAUDIO_STREAM_STATE_STARTING, &next, INT64_MAX);
}

if (result < 0)
MP_ERR(ao, "Failed to start stream: %s\n", p->AAudio_convertResultToText(result));
}

static bool set_pause(struct ao *ao, bool paused)
{
struct priv *p = ao->priv;

aaudio_result_t result;
aaudio_stream_state_t state, next;

if (paused) {
result = p->AAudioStream_requestPause(p->stream);
state = AAUDIO_STREAM_STATE_PAUSING;
} else {
result = p->AAudioStream_requestStart(p->stream);
state = AAUDIO_STREAM_STATE_STARTING;
}

if (result >= 0) {
result = p->AAudioStream_waitForStateChange(p->stream, state, &next, INT64_MAX);
return true;
}

MP_ERR(ao, "Failed to pause stream: %s\n", p->AAudio_convertResultToText(result));
return false;
}

static void reset(struct ao *ao)
{
struct priv *p = ao->priv;

if (p->stream) {
p->AAudioStream_close(p->stream);
p->stream = NULL;
}
}

#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,
.buffer_capacity = AAUDIO_UNSPECIFIED,
.performance_mode = AAUDIO_PERFORMANCE_MODE_NONE,
.stream = NULL,
.lib_handle = NULL},
.options_prefix = "aaudio",
.options =
(const struct m_option[]){
{"device-id", OPT_CHOICE(device_id, {"auto", AAUDIO_UNSPECIFIED}),
M_RANGE(1, 96000)},
{"buffer-capacity", OPT_CHOICE(buffer_capacity, {"auto", AAUDIO_UNSPECIFIED}),
M_RANGE(1, 96000)},
{"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}},
};
Loading
Loading