Browse Source

Reserve broadcast capture exclusively while preserving shared voice I/O

master
evgeny 4 days ago
parent
commit
29313cce54
  1. 1
      src/stream/stream_publisher.c
  2. 30
      tools/chatgui/src/audio_capture_lease.h
  3. 2
      tools/chatgui/src/stream_publish_engine.h

1
src/stream/stream_publisher.c

@ -73,6 +73,7 @@ static void publisher_close(struct stream_publisher *p, int reason) {
p->handle = NULL; records_clear(&p->video); records_clear(&p->audio);
p->status.state = reason ? STREAM_PUBLISHER_FAILED : STREAM_PUBLISHER_ENDED; p->status.reason = reason;
p->status.video_bytes = p->status.audio_bytes = p->status.total_bytes = 0;
p->status.stream_bytes = p->status.transport_bytes = p->status.inflight_bytes = p->status.acked_1s = 0;
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "publisher closed id=%llu epoch=%llx reason=%d",
(unsigned long long)p->status.id, (unsigned long long)p->status.epoch, reason);
}

30
tools/chatgui/src/audio_capture_lease.h

@ -1,20 +1,34 @@
/* Единственный владелец desktop-микрофона: звонок, рация, запись либо трансляция.
/* Desktop-микрофон: publisher получает Exclusive, существующие voice-контуры — Shared.
* Сохраняется совместная работа звонка/рации/записи; publisher исключает остальные captures в обе стороны.
* Lease берётся до открытия backend, сохраняется при recovery и освобождается после close/join.
* Inline состояние общее для translation units; callback не обращается к lease. */
* Каждый lease использует управляющий поток, общее atomic состояние доступно из разных потоков. */
#pragma once
#include <atomic>
class AudioCaptureLease {
public:
AudioCaptureLease() = default;
enum class Mode { Shared, Exclusive };
explicit AudioCaptureLease(Mode mode = Mode::Shared) : m_mode(mode) {}
~AudioCaptureLease() { release(); }
AudioCaptureLease(const AudioCaptureLease&) = delete;
AudioCaptureLease& operator=(const AudioCaptureLease&) = delete;
bool acquire() {
const void* expected = nullptr;
return owner.compare_exchange_strong(expected, this) || expected == this;
if (m_held) return true;
int expected = 0;
if (m_mode == Mode::Exclusive) m_held = owners.compare_exchange_strong(expected, -1);
else {
expected = owners.load();
while (expected >= 0) if (owners.compare_exchange_weak(expected, expected + 1)) { m_held = true; break; }
}
return m_held;
}
void release() { const void* expected = this; owner.compare_exchange_strong(expected, nullptr); }
bool held() const { return owner.load() == this; }
void release() {
if (!m_held) return;
if (m_mode == Mode::Exclusive) owners.store(0); else owners.fetch_sub(1);
m_held = false;
}
bool held() const { return m_held; }
private:
static inline std::atomic<const void*> owner{nullptr};
Mode m_mode;
bool m_held = false;
static inline std::atomic<int> owners{0};
};

2
tools/chatgui/src/stream_publish_engine.h

@ -54,7 +54,7 @@ private:
struct Impl;
std::unique_ptr<Impl> m;
AudioDevice m_capture;
AudioCaptureLease m_microphone;
AudioCaptureLease m_microphone{AudioCaptureLease::Mode::Exclusive};
QTimer m_timer;
bool openSource(const StreamPublishConfig& config);
void closeSource();

Loading…
Cancel
Save