Bug 1266623 - Up/down mix WASAPI capture streams when stream formats don't match. r=padenot

This commit is contained in:
Matthew Gregan 2016-04-26 13:31:49 +12:00
Родитель db9357edba
Коммит ff1d7be7b4
3 изменённых файлов: 68 добавлений и 25 удалений

Просмотреть файл

@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system.
The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
The git commit ID used was dbdfb3904deb7a0381588209cdd7388217d96e61.
The git commit ID used was 05abb60b99a46d1d21abe58d9e5f3bdb4f0df4a8.

Просмотреть файл

@ -133,6 +133,20 @@ public:
length_ += length;
}
/** Prepend `length` zero-ed elements to the end of the array, resizing the
* array if needed.
* @parameter length the number of elements to prepend to the array.
*/
void push_front_silence(size_t length)
{
if (length_ + length > capacity_) {
reserve(length + length_);
}
PodMove(data_ + length, data_, length_);
PodZero(data_, length);
length_ += length;
}
/** Return the number of free elements in the array. */
size_t available() const
{

Просмотреть файл

@ -366,7 +366,7 @@ public:
LOG("Audio device default changed.\n");
/* we only support a single stream type for now. */
if (flow != eRender && role != eMultimedia) {
if (flow != eRender && role != eConsole) {
return S_OK;
}
@ -586,7 +586,7 @@ bool get_input_buffer(cubeb_stream * stm)
/* Get input packets until we have captured enough frames, and put them in a
* contiguous buffer. */
uint32_t offset = 0;
uint32_t input_channel_count = stm->input_stream_params.channels;
uint32_t input_channel_count = stm->input_mix_params.channels;
while (offset != total_available_input * input_channel_count &&
total_available_input) {
hr = stm->capture_client->GetNextPacketSize(&next);
@ -602,20 +602,41 @@ bool get_input_buffer(cubeb_stream * stm)
UINT32 packet_size;
hr = stm->capture_client->GetBuffer(&input_packet,
&packet_size,
&flags,
&dev_pos,
NULL);
&packet_size,
&flags,
&dev_pos,
NULL);
if (FAILED(hr)) {
LOG("GetBuffer failed for capture: %x\n", hr);
return false;
}
XASSERT(packet_size == next);
if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
stm->linear_input_buffer.push_silence(packet_size * input_channel_count);
LOG("insert silence: ps=%u\n", packet_size);
stm->linear_input_buffer.push_silence(packet_size * stm->input_stream_params.channels);
} else {
stm->linear_input_buffer.push(reinterpret_cast<float*>(input_packet),
packet_size * input_channel_count);
if (should_upmix(stm->input_mix_params, stm->input_stream_params)) {
bool ok = stm->linear_input_buffer.reserve(stm->linear_input_buffer.length() +
packet_size * stm->input_stream_params.channels);
assert(ok);
upmix(reinterpret_cast<float*>(input_packet), packet_size,
stm->linear_input_buffer.data() + stm->linear_input_buffer.length(),
input_channel_count,
stm->input_stream_params.channels);
stm->linear_input_buffer.set_length(stm->linear_input_buffer.length() + packet_size * stm->input_stream_params.channels);
} else if (should_downmix(stm->input_mix_params, stm->input_stream_params)) {
bool ok = stm->linear_input_buffer.reserve(stm->linear_input_buffer.length() +
packet_size * stm->input_stream_params.channels);
assert(ok);
downmix(reinterpret_cast<float*>(input_packet), packet_size,
stm->linear_input_buffer.data() + stm->linear_input_buffer.length(),
input_channel_count,
stm->input_stream_params.channels);
stm->linear_input_buffer.set_length(stm->linear_input_buffer.length() + packet_size * stm->input_stream_params.channels);
} else {
stm->linear_input_buffer.push(reinterpret_cast<float*>(input_packet),
packet_size * stm->input_stream_params.channels);
}
}
hr = stm->capture_client->ReleaseBuffer(packet_size);
if (FAILED(hr)) {
@ -698,8 +719,13 @@ refill_callback_duplex(cubeb_stream * stm)
}
// When WASAPI has not filled the input buffer yet, send silence.
if (stm->linear_input_buffer.length() == 0) {
stm->linear_input_buffer.push_silence(output_frames* stm->output_mix_params.channels);
size_t input_frames = stm->linear_input_buffer.length() / stm->input_stream_params.channels;
double output_duration = double(output_frames) / stm->output_mix_params.rate;
double input_duration = double(input_frames) / stm->input_mix_params.rate;
if (input_duration < output_duration) {
size_t padding = round((output_duration - input_duration) * stm->input_mix_params.rate);
LOG("padding silence: out=%f in=%f pad=%u\n", output_duration, input_duration, padding);
stm->linear_input_buffer.push_front_silence(padding * stm->input_stream_params.channels);
}
refill(stm,
@ -986,11 +1012,7 @@ HRESULT get_default_endpoint(IMMDevice ** device, EDataFlow direction)
LOG("Could not get device enumerator: %x\n", hr);
return hr;
}
/* eMultimedia is okay for now ("Music, movies, narration, [...]").
We will need to change this when we distinguish streams by use-case, other
possible values being eConsole ("Games, system notification sounds [...]")
and eCommunication ("Voice communication"). */
hr = enumerator->GetDefaultAudioEndpoint(direction, eMultimedia, device);
hr = enumerator->GetDefaultAudioEndpoint(direction, eConsole, device);
if (FAILED(hr)) {
LOG("Could not get default audio endpoint: %x\n", hr);
SafeRelease(enumerator);
@ -1243,7 +1265,7 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten
return CUBEB_ERROR;
}
LOG("default device period: %ld\n", default_period);
LOG("default device period: %lld\n", default_period);
/* According to the docs, the best latency we can achieve is by synchronizing
the stream and the engine.
@ -1430,6 +1452,9 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
mix_params->format = CUBEB_SAMPLE_FLOAT32NE;
mix_params->rate = mix_format->nSamplesPerSec;
mix_params->channels = mix_format->nChannels;
LOG("Setup requested=[f=%d r=%u c=%u] mix=[f=%d r=%u c=%u]\n",
stream_params->format, stream_params->rate, stream_params->channels,
mix_params->format, mix_params->rate, mix_params->channels);
hr = (*audio_client)->Initialize(AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
@ -1452,8 +1477,10 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
return CUBEB_ERROR;
}
if (should_upmix(*stream_params, *mix_params) ||
should_downmix(*stream_params, *mix_params)) {
// Input is up/down mixed when depacketized in get_input_buffer.
if (has_output(stm) &&
(should_upmix(*stream_params, *mix_params) ||
should_downmix(*stream_params, *mix_params))) {
stm->mix_buffer = (float *)malloc(frames_to_bytes_before_mix(stm, *buffer_frame_count));
}
@ -1490,6 +1517,7 @@ int setup_wasapi_stream(cubeb_stream * stm)
XASSERT(!stm->output_client && "WASAPI stream already setup, close it first.");
if (has_input(stm)) {
LOG("Setup capture: device=%x\n", (int)stm->input_device);
rv = setup_wasapi_stream_one_side(stm,
&stm->input_stream_params,
stm->input_device,
@ -1506,6 +1534,7 @@ int setup_wasapi_stream(cubeb_stream * stm)
}
if (has_output(stm)) {
LOG("Setup render: device=%x\n", (int)stm->output_device);
rv = setup_wasapi_stream_one_side(stm,
&stm->output_stream_params,
stm->output_device,
@ -1547,7 +1576,7 @@ int setup_wasapi_stream(cubeb_stream * stm)
if (has_input(stm) && has_output(stm)) {
assert(stm->input_stream_params.rate == stm->output_stream_params.rate);
target_sample_rate = stm->input_stream_params.rate;
} else if (has_input(stm)) {
} else if (has_input(stm)) {
target_sample_rate = stm->input_stream_params.rate;
} else {
XASSERT(has_output(stm));
@ -1748,7 +1777,7 @@ int stream_start_one_side(cubeb_stream * stm, StreamDirection dir)
HRESULT hr = dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start();
if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
LOG("audioclient invalidated for %s device, reconfiguring\n",
dir == OUTPUT ? "output" : "input", hr);
dir == OUTPUT ? "output" : "input");
BOOL ok = ResetEvent(stm->reconfigure_event);
if (!ok) {
@ -1765,7 +1794,7 @@ int stream_start_one_side(cubeb_stream * stm, StreamDirection dir)
HRESULT hr = dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start();
if (FAILED(hr)) {
LOG("could not start the %s stream after reconfig: %x (%s)\n",
LOG("could not start the %s stream after reconfig: %x\n",
dir == OUTPUT ? "output" : "input", hr);
return CUBEB_ERROR;
}
@ -2049,7 +2078,7 @@ wasapi_create_device(IMMDeviceEnumerator * enumerator, IMMDevice * dev)
}
ret->preferred = CUBEB_DEVICE_PREF_NONE;
if (wasapi_is_default_device(flow, eMultimedia, device_id, enumerator))
if (wasapi_is_default_device(flow, eConsole, device_id, enumerator))
ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_MULTIMEDIA);
if (wasapi_is_default_device(flow, eCommunications, device_id, enumerator))
ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_VOICE);
@ -2137,7 +2166,7 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
}
if (type == CUBEB_DEVICE_TYPE_OUTPUT) flow = eRender;
else if (type == CUBEB_DEVICE_TYPE_INPUT) flow = eCapture;
else if (type == CUBEB_DEVICE_TYPE_INPUT) flow = eCapture;
else if (type & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_INPUT)) flow = eAll;
else return CUBEB_ERROR;