|
|
|
@ -98,6 +98,7 @@ AudioCapture::~AudioCapture() { close(); } |
|
|
|
|
|
|
|
|
|
|
|
bool AudioCapture::open(const QString& input, Role role, int channels, uint64_t tag, Ready ready, |
|
|
|
bool AudioCapture::open(const QString& input, Role role, int channels, uint64_t tag, Ready ready, |
|
|
|
std::function<void()> stopped, Callback callback) { |
|
|
|
std::function<void()> stopped, Callback callback) { |
|
|
|
|
|
|
|
if (closing()) { DEBUG_WARN(DEBUG_CATEGORY_CALL, "capture: previous device is still closing"); return false; } |
|
|
|
close(); |
|
|
|
close(); |
|
|
|
static std::vector<std::weak_ptr<Session>> sessions; // Только GUI; владение у подписчиков.
|
|
|
|
static std::vector<std::weak_ptr<Session>> sessions; // Только GUI; владение у подписчиков.
|
|
|
|
QString key = input; |
|
|
|
QString key = input; |
|
|
|
@ -129,13 +130,39 @@ bool AudioCapture::open(const QString& input, Role role, int channels, uint64_t |
|
|
|
return running(); |
|
|
|
return running(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void AudioCapture::close() { |
|
|
|
std::shared_ptr<AudioCapture::Session> AudioCapture::detach() { |
|
|
|
if (!m_session) return; |
|
|
|
if (!m_session) return {}; |
|
|
|
auto& slot = m_session->consumers[m_role == Role::Stream ? 0 : 1]; |
|
|
|
auto& slot = m_session->consumers[m_role == Role::Stream ? 0 : 1]; |
|
|
|
m_session->detach(slot); slot.client = nullptr; |
|
|
|
m_session->detach(slot); slot.client = nullptr; |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "capture: detached role=%s remaining=%d", m_role == Role::Stream ? "stream" : "radio", |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "capture: detached role=%s remaining=%d", m_role == Role::Stream ? "stream" : "radio", |
|
|
|
int(m_session->consumers[0].client != nullptr) + int(m_session->consumers[1].client != nullptr)); |
|
|
|
int(m_session->consumers[0].client != nullptr) + int(m_session->consumers[1].client != nullptr)); |
|
|
|
m_session.reset(); m_callback = {}; m_ready = {}; m_stopped = {}; |
|
|
|
m_callback = {}; m_ready = {}; m_stopped = {}; |
|
|
|
|
|
|
|
return std::move(m_session); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void AudioCapture::close() { |
|
|
|
|
|
|
|
if (m_closeThread.joinable()) m_closeThread.join(); |
|
|
|
|
|
|
|
detach().reset(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Callback отсоединён в GUI; session и контекст должны жить до завершения ma_device_uninit. */ |
|
|
|
|
|
|
|
void AudioCapture::closeAsync() { |
|
|
|
|
|
|
|
if (closing()) return; |
|
|
|
|
|
|
|
if (m_closeThread.joinable()) m_closeThread.join(); |
|
|
|
|
|
|
|
auto session = detach(); |
|
|
|
|
|
|
|
if (!session) return; |
|
|
|
|
|
|
|
m_closeDone.store(false, std::memory_order_release); |
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "capture: asynchronous close started shared=%d", session.use_count() > 1); |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
m_closeThread = std::thread([this, session = std::move(session)]() mutable { |
|
|
|
|
|
|
|
session.reset(); |
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "capture: asynchronous close completed"); |
|
|
|
|
|
|
|
m_closeDone.store(true, std::memory_order_release); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} catch (const std::system_error& error) { |
|
|
|
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "capture: close worker failed: %s", error.what()); |
|
|
|
|
|
|
|
m_closeDone.store(true, std::memory_order_release); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void AudioCapture::recover() { if (m_session) m_session->recover(); } |
|
|
|
void AudioCapture::recover() { if (m_session) m_session->recover(); } |
|
|
|
|