Allocate and share session ID for input streams

A session ID is required for audiofx, even through presets.
The latter is not clear in the documentation, but see
https://android.googlesource.com/platform/frameworks/av/+/master/services/audioflinger/AudioFlinger.cpp#1035

There's a single system-wide audio route for input. Only a single input
preset at a time seems to be usable. This commit makes us reject the
init of a stream that doesn't match a preset that has already been
acquired in the context.
This commit is contained in:
Andreas Pehrson 2024-06-24 09:36:37 +02:00
parent 5b1ec90ed4
commit 4f72bb725e

View file

@ -48,6 +48,7 @@ using namespace std;
X(AAudioStreamBuilder_delete) \ X(AAudioStreamBuilder_delete) \
X(AAudioStreamBuilder_setDataCallback) \ X(AAudioStreamBuilder_setDataCallback) \
X(AAudioStreamBuilder_setErrorCallback) \ X(AAudioStreamBuilder_setErrorCallback) \
X(AAudioStreamBuilder_setSessionId) \
X(AAudioStream_close) \ X(AAudioStream_close) \
X(AAudioStream_read) \ X(AAudioStream_read) \
X(AAudioStream_requestStart) \ X(AAudioStream_requestStart) \
@ -66,6 +67,7 @@ using namespace std;
X(AAudioStream_getFramesWritten) \ X(AAudioStream_getFramesWritten) \
X(AAudioStream_getFramesPerBurst) \ X(AAudioStream_getFramesPerBurst) \
X(AAudioStream_getInputPreset) \ X(AAudioStream_getInputPreset) \
X(AAudioStream_getSessionId) \
X(AAudioStreamBuilder_setInputPreset) \ X(AAudioStreamBuilder_setInputPreset) \
X(AAudioStreamBuilder_setUsage) \ X(AAudioStreamBuilder_setUsage) \
X(AAudioStreamBuilder_setFramesPerDataCallback) X(AAudioStreamBuilder_setFramesPerDataCallback)
@ -81,10 +83,8 @@ using namespace std;
// X(AAudioStream_getXRunCount) \ // X(AAudioStream_getXRunCount) \
// X(AAudioStream_isMMapUsed) \ // X(AAudioStream_isMMapUsed) \
// X(AAudioStreamBuilder_setContentType) \ // X(AAudioStreamBuilder_setContentType) \
// X(AAudioStreamBuilder_setSessionId) \
// X(AAudioStream_getUsage) \ // X(AAudioStream_getUsage) \
// X(AAudioStream_getContentType) \ // X(AAudioStream_getContentType) \
// X(AAudioStream_getSessionId) \
// X(AAudioStream_setBufferSizeInFrames) \ // X(AAudioStream_setBufferSizeInFrames) \
// END: not needed or added later on // END: not needed or added later on
@ -203,6 +203,142 @@ input_processing_params_to_str(cubeb_input_processing_params params)
return "Unknown"; return "Unknown";
} }
template <typename T> struct no_delete {
void operator()(T *) {}
};
struct AAudioInputConfigHandle;
struct AAudioSharedInputConfig {
AAudioInputConfigHandle acquire(aaudio_input_preset_t preset);
void release();
unique_lock<mutex> set_preset(aaudio_input_preset_t p);
mutex mutex;
aaudio_input_preset_t preset{};
aaudio_session_id_t session_id{AAUDIO_SESSION_ID_NONE};
int8_t user_count{};
};
struct AAudioInputConfigHandle {
AAudioInputConfigHandle(AAudioSharedInputConfig * cfg,
aaudio_input_preset_t preset,
aaudio_session_id_t session_id,
unique_lock<mutex> alloc_lock)
: shared_config(cfg), preset(preset), session_id(session_id),
allocation_lock(std::move(alloc_lock))
{
#ifndef NDEBUG
assert(cfg);
#endif
}
AAudioInputConfigHandle(const AAudioInputConfigHandle &) = delete;
AAudioInputConfigHandle(AAudioInputConfigHandle &&) = default;
AAudioInputConfigHandle & operator=(const AAudioInputConfigHandle &) = delete;
AAudioInputConfigHandle & operator=(AAudioInputConfigHandle &&) = default;
~AAudioInputConfigHandle()
{
if (shared_config) {
shared_config->release();
}
}
aaudio_input_preset_t get_preset() const
{
#ifndef NDEBUG
assert(shared_config);
#endif
return preset;
}
// If successful, is the only user and has to update the session id as the
// allocation lock will be held.
bool set_preset(aaudio_input_preset_t p)
{
#ifndef NDEBUG
assert(shared_config);
#endif
allocation_lock = shared_config->set_preset(p);
if (allocation_lock.owns_lock()) {
preset = shared_config->preset;
session_id = shared_config->session_id;
return true;
}
return false;
}
aaudio_session_id_t get_session_id() const
{
#ifndef NDEBUG
assert(shared_config);
#endif
return session_id;
}
void update_session_id(aaudio_session_id_t id)
{
#ifndef NDEBUG
assert(shared_config);
assert(session_id == AAUDIO_SESSION_ID_ALLOCATE);
assert(id > 0);
assert(allocation_lock.owns_lock());
#endif
session_id = shared_config->session_id = id;
allocation_lock = {};
}
private:
// This could be a rawptr, but we use a unique_ptr with a noop deleter as it
// is not copyable -- it simplifies constructors and assignment operators.
unique_ptr<AAudioSharedInputConfig, no_delete<AAudioSharedInputConfig>>
shared_config;
aaudio_input_preset_t preset;
aaudio_session_id_t session_id;
unique_lock<mutex> allocation_lock;
};
AAudioInputConfigHandle
AAudioSharedInputConfig::acquire(aaudio_input_preset_t p)
{
unique_lock lock(mutex);
if (++user_count == 1) {
LOG("First input stream set preset to %s", input_preset_to_str(p));
preset = p;
session_id = AAUDIO_SESSION_ID_ALLOCATE;
return {this, preset, session_id, std::move(lock)};
}
#ifndef NDEBUG
assert(session_id != AAUDIO_SESSION_ID_ALLOCATE);
#endif
return {this, preset, session_id, unique_lock<std::mutex>()};
}
void
AAudioSharedInputConfig::release()
{
std::unique_lock lock(mutex);
if (--user_count == 0) {
LOG("Last active input stream released");
session_id = AAUDIO_SESSION_ID_ALLOCATE;
preset = AAUDIO_INPUT_PRESET_CAMCORDER;
}
assert(user_count >= 0);
}
unique_lock<mutex>
AAudioSharedInputConfig::set_preset(aaudio_input_preset_t p)
{
unique_lock lock(mutex);
if (user_count != 1) {
return {};
}
LOG("The only active input stream changed preset from %s to %s",
input_preset_to_str(preset), input_preset_to_str(p));
preset = p;
session_id = AAUDIO_SESSION_ID_ALLOCATE;
return lock;
}
struct cubeb_stream { struct cubeb_stream {
/* Note: Must match cubeb_stream layout in cubeb.c. */ /* Note: Must match cubeb_stream layout in cubeb.c. */
cubeb * context{}; cubeb * context{};
@ -240,6 +376,7 @@ struct cubeb_stream {
unsigned out_frame_size{}; unsigned out_frame_size{};
bool voice_input{}; bool voice_input{};
bool voice_output{}; bool voice_output{};
std::optional<AAudioInputConfigHandle> in_config{};
cubeb_input_processing_params input_processing_params{}; cubeb_input_processing_params input_processing_params{};
uint64_t previous_clock{}; uint64_t previous_clock{};
}; };
@ -261,6 +398,7 @@ struct cubeb {
// streams[i].in_use signals whether a stream is used // streams[i].in_use signals whether a stream is used
struct cubeb_stream streams[MAX_STREAMS]; struct cubeb_stream streams[MAX_STREAMS];
AAudioSharedInputConfig shared_input_config{};
}; };
struct AutoInCallback { struct AutoInCallback {
@ -1079,11 +1217,15 @@ realize_stream(AAudioStreamBuilder * sb, const cubeb_stream_params * params,
static void static void
aaudio_stream_destroy(cubeb_stream * stm) aaudio_stream_destroy(cubeb_stream * stm)
{ {
{
lock_guard lock(stm->mutex);
stm->in_use.store(false);
stm->in_config = nullopt;
aaudio_stream_destroy_locked(stm, lock);
}
constexpr bool DISABLED = false; constexpr bool DISABLED = false;
cubeb_jni_set_communication_mode(stm, DISABLED); cubeb_jni_set_communication_mode(stm, DISABLED);
lock_guard lock(stm->mutex);
stm->in_use.store(false);
aaudio_stream_destroy_locked(stm, lock);
} }
static void static void
@ -1254,13 +1396,40 @@ aaudio_stream_init_impl(cubeb_stream * stm, lock_guard<mutex> & lock)
// input // input
cubeb_stream_params in_params; cubeb_stream_params in_params;
if (stm->input_stream_params) { if (stm->input_stream_params) {
aaudio_input_preset_t preset = AAUDIO_INPUT_PRESET_CAMCORDER; cubeb_input_processing_params processing_params =
stm->voice_input ? stm->input_processing_params
: CUBEB_INPUT_PROCESSING_PARAM_NONE;
aaudio_input_preset_t preset =
*input_processing_params_to_input_preset(processing_params);
if (stm->in_config && stm->in_config->get_preset() != preset &&
!stm->in_config->set_preset(preset)) {
LOG("Stream %p has in_config but failed to change preset from %s to %s",
input_preset_to_str(stm->in_config->get_preset()),
input_preset_to_str(preset));
return CUBEB_ERROR;
}
AAudioInputConfigHandle handle =
stm->in_config ? *std::move(stm->in_config)
: stm->context->shared_input_config.acquire(preset);
if (handle.get_preset() != preset) {
// Reject the stream if we couldn't acquire the requested preset.
LOG("Acquired preset %s does not match requested preset %s.",
input_preset_to_str(handle.get_preset()),
input_preset_to_str(preset));
return CUBEB_ERROR;
}
constexpr bool ENABLED = true; constexpr bool ENABLED = true;
if (stm->voice_input && if (stm->voice_input &&
cubeb_jni_set_communication_mode(stm, ENABLED) == CUBEB_OK) { cubeb_jni_set_communication_mode(stm, ENABLED) != CUBEB_OK) {
preset = *input_processing_params_to_input_preset( // If we cannot enter COMMUNICATION mode, reject a voice stream.
stm->input_processing_params); // Note that the VOICE_COMMUNICATION preset requires COMMUNICATION mode
// for hw AEC to work. Other presets don't but we'll reject with voice
// anyway for consistency.
LOG("Setting communication mode failed with voice input.");
return CUBEB_ERROR;
} }
WRAP(AAudioStreamBuilder_setSessionId)(sb, handle.get_session_id());
WRAP(AAudioStreamBuilder_setInputPreset)(sb, preset); WRAP(AAudioStreamBuilder_setInputPreset)(sb, preset);
WRAP(AAudioStreamBuilder_setDirection)(sb, AAUDIO_DIRECTION_INPUT); WRAP(AAudioStreamBuilder_setDirection)(sb, AAUDIO_DIRECTION_INPUT);
WRAP(AAudioStreamBuilder_setDataCallback)(sb, in_data_callback, stm); WRAP(AAudioStreamBuilder_setDataCallback)(sb, in_data_callback, stm);
@ -1274,6 +1443,10 @@ aaudio_stream_init_impl(cubeb_stream * stm, lock_guard<mutex> & lock)
return res_err; return res_err;
} }
if (handle.get_session_id() == AAUDIO_SESSION_ID_ALLOCATE) {
handle.update_session_id(WRAP(AAudioStream_getSessionId)(stm->istream));
}
int bcap = WRAP(AAudioStream_getBufferCapacityInFrames)(stm->istream); int bcap = WRAP(AAudioStream_getBufferCapacityInFrames)(stm->istream);
int rate = WRAP(AAudioStream_getSampleRate)(stm->istream); int rate = WRAP(AAudioStream_getSampleRate)(stm->istream);
LOG("AAudio input stream sharing mode: %d", LOG("AAudio input stream sharing mode: %d",
@ -1293,6 +1466,7 @@ aaudio_stream_init_impl(cubeb_stream * stm, lock_guard<mutex> & lock)
in_params = *stm->input_stream_params; in_params = *stm->input_stream_params;
in_params.rate = rate; in_params.rate = rate;
stm->in_frame_size = frame_size; stm->in_frame_size = frame_size;
stm->in_config = std::move(handle);
} }
// initialize resampler // initialize resampler