From f04624e8fb88b83f19dab59876987372ab4f17b0 Mon Sep 17 00:00:00 2001 From: spycrab Date: Mon, 12 Feb 2018 14:03:33 +0100 Subject: [PATCH] Implement CUBEB_STREAM_PREF_EXCLUSIVE --- include/cubeb/cubeb.h | 15 ++++++++++----- src/cubeb.c | 5 +++++ src/cubeb_wasapi.cpp | 38 +++++++++++++++++++++++++++++++------- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/include/cubeb/cubeb.h b/include/cubeb/cubeb.h index 64f3a8d0f..cc5ac5fec 100644 --- a/include/cubeb/cubeb.h +++ b/include/cubeb/cubeb.h @@ -217,11 +217,16 @@ typedef enum { /** Miscellaneous stream preferences. */ typedef enum { - CUBEB_STREAM_PREF_NONE = 0x00, /**< No stream preferences are requested. */ - CUBEB_STREAM_PREF_LOOPBACK = 0x01 /**< Request a loopback stream. Should be - specified on the input params and an - output device to loopback from should - be passed in place of an input device. */ + CUBEB_STREAM_PREF_NONE = 0x00, /**< No stream preferences are requested. */ + CUBEB_STREAM_PREF_LOOPBACK = 0x01, /**< Request a loopback stream. Should be + specified on the input params and an + output device to loopback from should + be passed in place of an input device. */ + CUBEB_STREAM_PREF_EXCLUSIVE = 0x02 /**< (Windows / WASAPI only) Request that + this stream is run in exclusive mode + which results in lower latency but + doesn't allow other applications to + use the same device */ } cubeb_stream_prefs; /** Stream format initialization parameters. */ diff --git a/src/cubeb.c b/src/cubeb.c index f78f4aff1..590a70a05 100644 --- a/src/cubeb.c +++ b/src/cubeb.c @@ -318,6 +318,11 @@ cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n return r; } + if ((output_stream_params && output_stream_params->prefs) & CUBEB_STREAM_PREF_EXCLUSIVE || (input_stream_params && input_stream_params->prefs & CUBEB_STREAM_PREF_EXCLUSIVE)) { + if (strcmp(cubeb_get_backend_id(context), "wasapi") != 0) + return CUBEB_ERROR_NOT_SUPPORTED; + } + r = context->ops->stream_init(context, stream, stream_name, input_device, input_stream_params, diff --git a/src/cubeb_wasapi.cpp b/src/cubeb_wasapi.cpp index d040e216a..518b39784 100644 --- a/src/cubeb_wasapi.cpp +++ b/src/cubeb_wasapi.cpp @@ -617,9 +617,20 @@ refill(cubeb_stream * stm, void * input_buffer, long input_frames_count, int wasapi_stream_reset_default_device(cubeb_stream * stm); +/* Helper for making get_input_buffer work in exclusive mode */ +HRESULT get_next_packet_size(cubeb_stream * stm, PUINT32 next) +{ + if (stm->input_stream_params.prefs & CUBEB_STREAM_PREF_EXCLUSIVE) { + *next = stm->input_buffer_frame_count; + return S_OK; + } else { + return stm->capture_client->GetNextPacketSize(next); + } +} + /* This helper grabs all the frames available from a capture client, put them in * linear_input_buffer. linear_input_buffer should be cleared before the - * callback exits. This helper does not work with exclusive mode streams. */ + * callback exits. */ bool get_input_buffer(cubeb_stream * stm) { XASSERT(has_input(stm)); @@ -636,9 +647,9 @@ bool get_input_buffer(cubeb_stream * stm) // single packet each time. However, if we're pulling from the stream we may // need to grab multiple packets worth of frames that have accumulated (so // need a loop). - for (hr = stm->capture_client->GetNextPacketSize(&next); + for (hr = get_next_packet_size(stm, &next); next > 0; - hr = stm->capture_client->GetNextPacketSize(&next)) { + hr = get_next_packet_size(stm, &next)) { if (hr == AUDCLNT_E_DEVICE_INVALIDATED) { // Application can recover from this error. More info // https://msdn.microsoft.com/en-us/library/windows/desktop/dd316605(v=vs.85).aspx @@ -705,6 +716,9 @@ bool get_input_buffer(cubeb_stream * stm) return false; } offset += packet_size; + + if (stm->input_stream_params.prefs & CUBEB_STREAM_PREF_EXCLUSIVE) + break; } XASSERT(stm->linear_input_buffer->length() >= offset); @@ -1395,8 +1409,11 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten return CUBEB_ERROR; } - /* The second parameter is for exclusive mode, that we don't use. */ - hr = client->GetDevicePeriod(&default_period, NULL); + if (params.prefs & CUBEB_STREAM_PREF_EXCLUSIVE) + hr = client->GetDevicePeriod(NULL, &default_period); + else + hr = client->GetDevicePeriod(&default_period, NULL); + if (FAILED(hr)) { LOG("Could not get device period: %lx", hr); return CUBEB_ERROR; @@ -1531,7 +1548,7 @@ handle_channel_layout(cubeb_stream * stm, EDataFlow direction, com_heap_ptrIsFormatSupported(AUDCLNT_SHAREMODE_SHARED, + HRESULT hr = audio_client->IsFormatSupported(stream_params->prefs & CUBEB_STREAM_PREF_EXCLUSIVE ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED, mix_format.get(), &closest); if (hr == S_FALSE) { @@ -1665,6 +1682,13 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm, // audio according to layout. LOG("Channel count is different from the layout standard!\n"); } + + + if (stream_params->prefs & CUBEB_STREAM_PREF_EXCLUSIVE) + LOG("Setup requested=[f=%d r=%u c=%u] mix=[f=%d r=%u c=%u]", + stream_params->format, stream_params->rate, stream_params->channels, + mix_params->format, mix_params->rate, mix_params->channels); + else LOG("Setup requested=[f=%d r=%u c=%u l=%s] mix=[f=%d r=%u c=%u l=%s]", stream_params->format, stream_params->rate, stream_params->channels, CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].name, @@ -1681,7 +1705,7 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm, flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK; } - hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, + hr = audio_client->Initialize(stream_params->prefs & CUBEB_STREAM_PREF_EXCLUSIVE ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED, flags, frames_to_hns(stm, stm->latency), 0,