diff options
author | Andreas Pehrson <[email protected]> | 2024-06-24 09:36:37 +0200 |
---|---|---|
committer | Andreas Pehrson <[email protected]> | 2024-07-04 22:41:26 +0200 |
commit | 4f72bb725ed244baa9cdf540fb3339de576069c4 (patch) | |
tree | 3dfafe9788d8f4dbda32b6319d63a631d34bbe05 /src/cubeb_aaudio.cpp | |
parent | 5b1ec90ed4b1df70a82df50477667e7ff623cba0 (diff) | |
download | cubeb-4f72bb725ed244baa9cdf540fb3339de576069c4.tar.gz cubeb-4f72bb725ed244baa9cdf540fb3339de576069c4.zip |
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.
Diffstat (limited to 'src/cubeb_aaudio.cpp')
-rw-r--r-- | src/cubeb_aaudio.cpp | 192 |
1 files changed, 183 insertions, 9 deletions
diff --git a/src/cubeb_aaudio.cpp b/src/cubeb_aaudio.cpp index c801204..6c0c3af 100644 --- a/src/cubeb_aaudio.cpp +++ b/src/cubeb_aaudio.cpp @@ -48,6 +48,7 @@ using namespace std; X(AAudioStreamBuilder_delete) \ X(AAudioStreamBuilder_setDataCallback) \ X(AAudioStreamBuilder_setErrorCallback) \ + X(AAudioStreamBuilder_setSessionId) \ X(AAudioStream_close) \ X(AAudioStream_read) \ X(AAudioStream_requestStart) \ @@ -66,6 +67,7 @@ using namespace std; X(AAudioStream_getFramesWritten) \ X(AAudioStream_getFramesPerBurst) \ X(AAudioStream_getInputPreset) \ + X(AAudioStream_getSessionId) \ X(AAudioStreamBuilder_setInputPreset) \ X(AAudioStreamBuilder_setUsage) \ X(AAudioStreamBuilder_setFramesPerDataCallback) @@ -81,10 +83,8 @@ using namespace std; // X(AAudioStream_getXRunCount) \ // X(AAudioStream_isMMapUsed) \ // X(AAudioStreamBuilder_setContentType) \ - // X(AAudioStreamBuilder_setSessionId) \ // X(AAudioStream_getUsage) \ // X(AAudioStream_getContentType) \ - // X(AAudioStream_getSessionId) \ // X(AAudioStream_setBufferSizeInFrames) \ // END: not needed or added later on @@ -203,6 +203,142 @@ input_processing_params_to_str(cubeb_input_processing_params params) 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 { /* Note: Must match cubeb_stream layout in cubeb.c. */ cubeb * context{}; @@ -240,6 +376,7 @@ struct cubeb_stream { unsigned out_frame_size{}; bool voice_input{}; bool voice_output{}; + std::optional<AAudioInputConfigHandle> in_config{}; cubeb_input_processing_params input_processing_params{}; uint64_t previous_clock{}; }; @@ -261,6 +398,7 @@ struct cubeb { // streams[i].in_use signals whether a stream is used struct cubeb_stream streams[MAX_STREAMS]; + AAudioSharedInputConfig shared_input_config{}; }; struct AutoInCallback { @@ -1079,11 +1217,15 @@ realize_stream(AAudioStreamBuilder * sb, const cubeb_stream_params * params, static void 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; 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 @@ -1254,13 +1396,40 @@ aaudio_stream_init_impl(cubeb_stream * stm, lock_guard<mutex> & lock) // input cubeb_stream_params in_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; if (stm->voice_input && - cubeb_jni_set_communication_mode(stm, ENABLED) == CUBEB_OK) { - preset = *input_processing_params_to_input_preset( - stm->input_processing_params); + cubeb_jni_set_communication_mode(stm, ENABLED) != CUBEB_OK) { + // If we cannot enter COMMUNICATION mode, reject a voice stream. + // 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_setDirection)(sb, AAUDIO_DIRECTION_INPUT); 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; } + 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 rate = WRAP(AAudioStream_getSampleRate)(stm->istream); 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.rate = rate; stm->in_frame_size = frame_size; + stm->in_config = std::move(handle); } // initialize resampler |