cubeb: ignore callbacks for unknown streams or contexts

This commit is contained in:
Megamouse 2022-08-20 00:43:03 +02:00
parent c75c47fdd2
commit 36b1004e26
2 changed files with 72 additions and 7 deletions

View file

@ -229,7 +229,7 @@ protected:
shared_mutex m_cb_mutex{}; shared_mutex m_cb_mutex{};
std::function<u32(u32, void *)> m_write_callback{}; std::function<u32(u32, void *)> m_write_callback{};
shared_mutex m_state_cb_mutex{}; std::recursive_mutex m_state_cb_mutex{};
std::function<void(AudioStateEvent)> m_state_callback{}; std::function<void(AudioStateEvent)> m_state_callback{};
bool m_playing = false; bool m_playing = false;

View file

@ -22,6 +22,8 @@ CubebBackend::CubebBackend()
} }
#endif #endif
std::lock_guard lock(m_dev_sw_mutex);
if (int err = cubeb_init(&m_ctx, "RPCS3", nullptr)) if (int err = cubeb_init(&m_ctx, "RPCS3", nullptr))
{ {
Cubeb.error("cubeb_init() failed: %i", err); Cubeb.error("cubeb_init() failed: %i", err);
@ -92,6 +94,7 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize
std::lock_guard lock(m_cb_mutex); std::lock_guard lock(m_cb_mutex);
std::lock_guard dev_sw_lock{m_dev_sw_mutex}; std::lock_guard dev_sw_lock{m_dev_sw_mutex};
std::lock_guard state_cb_lock{m_state_cb_mutex};
CloseUnlocked(); CloseUnlocked();
const bool use_default_device = dev_id.empty() || dev_id == audio_device_enumerator::DEFAULT_DEV_ID; const bool use_default_device = dev_id.empty() || dev_id == audio_device_enumerator::DEFAULT_DEV_ID;
@ -207,6 +210,7 @@ void CubebBackend::Close()
{ {
std::lock_guard lock(m_cb_mutex); std::lock_guard lock(m_cb_mutex);
std::lock_guard dev_sw_lock{m_dev_sw_mutex}; std::lock_guard dev_sw_lock{m_dev_sw_mutex};
std::lock_guard state_cb_lock(m_state_cb_mutex);
CloseUnlocked(); CloseUnlocked();
} }
@ -398,7 +402,7 @@ CubebBackend::device_handle CubebBackend::GetDefaultDeviceAlt(AudioFreq freq, Au
return GetDevice(out_dev_name); return GetDevice(out_dev_name);
} }
long CubebBackend::data_cb(cubeb_stream* /* stream */, void* user_ptr, void const* /* input_buffer */, void* output_buffer, long nframes) long CubebBackend::data_cb(cubeb_stream* stream, void* user_ptr, void const* /* input_buffer */, void* output_buffer, long nframes)
{ {
if (nframes <= 0) if (nframes <= 0)
{ {
@ -412,11 +416,23 @@ long CubebBackend::data_cb(cubeb_stream* /* stream */, void* user_ptr, void cons
return CUBEB_ERROR; return CUBEB_ERROR;
} }
if (!stream)
{
Cubeb.error("data_cb called with invalid stream");
return CUBEB_ERROR;
}
CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr); CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr);
ensure(cubeb); ensure(cubeb);
std::unique_lock lock(cubeb->m_cb_mutex, std::defer_lock); std::unique_lock lock(cubeb->m_cb_mutex, std::defer_lock);
if (stream != cubeb->m_stream)
{
Cubeb.error("data_cb called with unknown stream");
return CUBEB_ERROR;
}
if (!cubeb->m_reset_req.observe() && lock.try_lock() && cubeb->m_write_callback && cubeb->m_playing) if (!cubeb->m_reset_req.observe() && lock.try_lock() && cubeb->m_write_callback && cubeb->m_playing)
{ {
const u32 sample_size = cubeb->full_sample_size.observe(); const u32 sample_size = cubeb->full_sample_size.observe();
@ -444,33 +460,82 @@ long CubebBackend::data_cb(cubeb_stream* /* stream */, void* user_ptr, void cons
return nframes; return nframes;
} }
void CubebBackend::state_cb(cubeb_stream* /* stream */, void* user_ptr, cubeb_state state) void CubebBackend::state_cb(cubeb_stream* stream, void* user_ptr, cubeb_state state)
{ {
if (!stream)
{
Cubeb.error("state_cb called with invalid stream");
return;
}
CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr); CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr);
ensure(cubeb); ensure(cubeb);
if (state == CUBEB_STATE_ERROR) std::lock_guard lock(cubeb->m_state_cb_mutex);
if (stream != cubeb->m_stream)
{
Cubeb.error("state_cb called with unknown stream");
return;
}
switch (state)
{
case CUBEB_STATE_ERROR:
{ {
Cubeb.error("Stream entered error state"); Cubeb.error("Stream entered error state");
std::lock_guard lock(cubeb->m_state_cb_mutex);
if (!cubeb->m_reset_req.test_and_set() && cubeb->m_state_callback) if (!cubeb->m_reset_req.test_and_set() && cubeb->m_state_callback)
{ {
cubeb->m_state_callback(AudioStateEvent::UNSPECIFIED_ERROR); cubeb->m_state_callback(AudioStateEvent::UNSPECIFIED_ERROR);
} }
break;
}
case CUBEB_STATE_STARTED:
{
Cubeb.notice("Stream started");
break;
}
case CUBEB_STATE_STOPPED:
{
Cubeb.notice("Stream stopped");
break;
}
case CUBEB_STATE_DRAINED:
{
Cubeb.notice("Stream drained");
break;
}
default:
{
Cubeb.notice("Stream entered unknown state %d", static_cast<u32>(state));
break;
}
} }
} }
void CubebBackend::device_collection_changed_cb(cubeb* /* context */, void* user_ptr) void CubebBackend::device_collection_changed_cb(cubeb* context, void* user_ptr)
{ {
Cubeb.notice("Device collection changed"); Cubeb.notice("Device collection changed");
if (!context)
{
Cubeb.error("device_collection_changed_cb called with invalid stream");
return;
}
CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr); CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr);
ensure(cubeb); ensure(cubeb);
std::lock_guard lock{cubeb->m_dev_sw_mutex}; std::lock_guard lock{cubeb->m_dev_sw_mutex};
if (context != cubeb->m_ctx)
{
Cubeb.error("device_collection_changed_cb called with unkown context");
return;
}
// Non default device is used (or default device cannot be detected) // Non default device is used (or default device cannot be detected)
if (cubeb->m_default_device.empty()) if (cubeb->m_default_device.empty())
{ {