src/output: Move event and spsc_queue into thread object

This commit is contained in:
Shen-Ta Hsieh 2020-12-02 07:33:40 +08:00 committed by Max Kellermann
parent 2974737746
commit db7caa2dac

View File

@ -118,15 +118,14 @@ inline constexpr const unsigned int kErrorId = -1;
class WasapiOutputThread : public Thread { class WasapiOutputThread : public Thread {
public: public:
enum class Status : uint32_t { FINISH, PLAY, PAUSE }; enum class Status : uint32_t { FINISH, PLAY, PAUSE };
WasapiOutputThread(std::shared_ptr<WinEvent> _event, ComPtr<IAudioClient> _client, WasapiOutputThread(IAudioClient *_client,
ComPtr<IAudioRenderClient> &&_render_client, ComPtr<IAudioRenderClient> &&_render_client,
const UINT32 _frame_size, const UINT32 _buffer_size_in_frames, const UINT32 _frame_size, const UINT32 _buffer_size_in_frames,
bool _is_exclusive, bool _is_exclusive)
boost::lockfree::spsc_queue<BYTE> &_spsc_buffer) : Thread(BIND_THIS_METHOD(Work)), client(_client),
: Thread(BIND_THIS_METHOD(Work)), event(std::move(_event)), render_client(std::move(_render_client)), frame_size(_frame_size),
client(std::move(_client)), render_client(std::move(_render_client)), buffer_size_in_frames(_buffer_size_in_frames), is_exclusive(_is_exclusive),
frame_size(_frame_size), buffer_size_in_frames(_buffer_size_in_frames), spsc_buffer(_buffer_size_in_frames * 4 * _frame_size) {}
is_exclusive(_is_exclusive), spsc_buffer(_spsc_buffer) {}
void Finish() noexcept { return SetStatus(Status::FINISH); } void Finish() noexcept { return SetStatus(Status::FINISH); }
void Play() noexcept { return SetStatus(Status::PLAY); } void Play() noexcept { return SetStatus(Status::PLAY); }
void Pause() noexcept { return SetStatus(Status::PAUSE); } void Pause() noexcept { return SetStatus(Status::PAUSE); }
@ -144,13 +143,13 @@ public:
} }
private: private:
std::shared_ptr<WinEvent> event; friend class WasapiOutput;
ComPtr<IAudioClient> client; WinEvent event;
IAudioClient *client;
ComPtr<IAudioRenderClient> render_client; ComPtr<IAudioRenderClient> render_client;
const UINT32 frame_size; const UINT32 frame_size;
const UINT32 buffer_size_in_frames; const UINT32 buffer_size_in_frames;
bool is_exclusive; bool is_exclusive;
boost::lockfree::spsc_queue<BYTE> &spsc_buffer;
alignas(BOOST_LOCKFREE_CACHELINE_BYTES) std::atomic<Status> status = alignas(BOOST_LOCKFREE_CACHELINE_BYTES) std::atomic<Status> status =
Status::PAUSE; Status::PAUSE;
alignas(BOOST_LOCKFREE_CACHELINE_BYTES) struct { alignas(BOOST_LOCKFREE_CACHELINE_BYTES) struct {
@ -162,10 +161,11 @@ private:
Cond cond; Cond cond;
std::exception_ptr error_ptr = nullptr; std::exception_ptr error_ptr = nullptr;
} error{}; } error{};
boost::lockfree::spsc_queue<BYTE> spsc_buffer;
void SetStatus(Status s) noexcept { void SetStatus(Status s) noexcept {
status.store(s); status.store(s);
event->Set(); event.Set();
} }
void Work() noexcept; void Work() noexcept;
}; };
@ -203,13 +203,11 @@ private:
bool enumerate_devices; bool enumerate_devices;
std::string device_config; std::string device_config;
std::vector<std::pair<unsigned int, AllocatedString>> device_desc; std::vector<std::pair<unsigned int, AllocatedString>> device_desc;
std::shared_ptr<WinEvent> event;
ComPtr<IMMDeviceEnumerator> enumerator; ComPtr<IMMDeviceEnumerator> enumerator;
ComPtr<IMMDevice> device; ComPtr<IMMDevice> device;
ComPtr<IAudioClient> client; ComPtr<IAudioClient> client;
WAVEFORMATEXTENSIBLE device_format; WAVEFORMATEXTENSIBLE device_format;
std::unique_ptr<WasapiOutputThread> thread; std::optional<WasapiOutputThread> thread;
std::unique_ptr<boost::lockfree::spsc_queue<BYTE>> spsc_buffer;
std::size_t watermark; std::size_t watermark;
friend bool wasapi_is_exclusive(WasapiOutput &output) noexcept; friend bool wasapi_is_exclusive(WasapiOutput &output) noexcept;
@ -248,7 +246,7 @@ void WasapiOutputThread::Work() noexcept {
COM com{true}; COM com{true};
while (true) { while (true) {
try { try {
event->Wait(INFINITE); event.Wait(INFINITE);
Status current_state = status.load(); Status current_state = status.load();
if (current_state == Status::FINISH) { if (current_state == Status::FINISH) {
@ -330,13 +328,10 @@ void WasapiOutput::DoDisable() noexcept {
err.what()); err.what());
} }
thread.reset(); thread.reset();
spsc_buffer.reset();
client.reset(); client.reset();
} }
device.reset(); device.reset();
enumerator.reset(); enumerator.reset();
com.reset();
event.reset();
} }
/// run inside COMWorkerThread /// run inside COMWorkerThread
@ -442,11 +437,6 @@ void WasapiOutput::DoOpen(AudioFormat &audio_format) {
throw FormatHResultError(result, "Unable to get new render client"); throw FormatHResultError(result, "Unable to get new render client");
} }
result = client->SetEventHandle(event->handle());
if (FAILED(result)) {
throw FormatHResultError(result, "Unable to set event handler");
}
UINT32 buffer_size_in_frames; UINT32 buffer_size_in_frames;
result = client->GetBufferSize(&buffer_size_in_frames); result = client->GetBufferSize(&buffer_size_in_frames);
if (FAILED(result)) { if (FAILED(result)) {
@ -455,11 +445,14 @@ void WasapiOutput::DoOpen(AudioFormat &audio_format) {
} }
watermark = buffer_size_in_frames * 3 * FrameSize(); watermark = buffer_size_in_frames * 3 * FrameSize();
spsc_buffer = std::make_unique<boost::lockfree::spsc_queue<BYTE>>( thread.emplace(client.get(), std::move(render_client), FrameSize(),
buffer_size_in_frames * 4 * FrameSize()); buffer_size_in_frames, is_exclusive);
thread = std::make_unique<WasapiOutputThread>(
event, client, std::move(render_client), FrameSize(), result = client->SetEventHandle(thread->event.handle());
buffer_size_in_frames, is_exclusive, *spsc_buffer); if (FAILED(result)) {
throw FormatHResultError(result, "Unable to set event handler");
}
thread->Start(); thread->Start();
} }
@ -472,7 +465,6 @@ void WasapiOutput::Close() noexcept {
thread.reset(); thread.reset();
client.reset(); client.reset();
}).get(); }).get();
spsc_buffer.reset();
} }
std::chrono::steady_clock::duration WasapiOutput::Delay() const noexcept { std::chrono::steady_clock::duration WasapiOutput::Delay() const noexcept {
@ -480,7 +472,9 @@ std::chrono::steady_clock::duration WasapiOutput::Delay() const noexcept {
return std::chrono::steady_clock::duration::zero(); return std::chrono::steady_clock::duration::zero();
} }
const size_t data_size = spsc_buffer->read_available(); assert(thread);
const size_t data_size = thread->spsc_buffer.read_available();
const size_t delay_size = std::max(data_size, watermark) - watermark; const size_t delay_size = std::max(data_size, watermark) - watermark;
using s = std::chrono::seconds; using s = std::chrono::seconds;
@ -490,13 +484,11 @@ std::chrono::steady_clock::duration WasapiOutput::Delay() const noexcept {
} }
size_t WasapiOutput::Play(const void *chunk, size_t size) { size_t WasapiOutput::Play(const void *chunk, size_t size) {
if (!client || !thread) { assert(thread);
return 0;
}
do { do {
const size_t consumed_size = const size_t consumed_size =
spsc_buffer->push(static_cast<const BYTE *>(chunk), size); thread->spsc_buffer.push(static_cast<const BYTE *>(chunk), size);
if (consumed_size == 0) { if (consumed_size == 0) {
assert(is_started); assert(is_started);
thread->WaitWrite(); thread->WaitWrite();
@ -523,11 +515,6 @@ size_t WasapiOutput::Play(const void *chunk, size_t size) {
} while (true); } while (true);
} }
void WasapiOutput::Drain() {
spsc_buffer->consume_all([](auto &&) {});
thread->CheckException();
}
bool WasapiOutput::Pause() { bool WasapiOutput::Pause() {
if (!client || !thread) { if (!client || !thread) {
return false; return false;
@ -549,6 +536,13 @@ bool WasapiOutput::Pause() {
return true; return true;
} }
void WasapiOutput::Drain() {
assert(thread);
thread->spsc_buffer.consume_all([](auto &&) {});
thread->CheckException();
}
/// run inside COMWorkerThread /// run inside COMWorkerThread
void WasapiOutput::OpenDevice() { void WasapiOutput::OpenDevice() {
enumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, enumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr,