mirror of
https://github.com/mozilla/cubeb
synced 2024-11-21 14:29:11 -07:00
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:
parent
5b1ec90ed4
commit
4f72bb725e
1 changed files with 183 additions and 9 deletions
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue