diff --git a/media/libcubeb/README_MOZILLA b/media/libcubeb/README_MOZILLA index 4c6adc388b2b..b0446bd2ff63 100644 --- a/media/libcubeb/README_MOZILLA +++ b/media/libcubeb/README_MOZILLA @@ -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 21e96ac7fd456dc957cd9947a61da1366a1f862d. +The git commit ID used was 8977c13bf0dab9a7716501ae92ca5945fe4e1dae. diff --git a/media/libcubeb/gtest/test_resampler.cpp b/media/libcubeb/gtest/test_resampler.cpp index fa699a8609e2..e7f5fddbd5ff 100644 --- a/media/libcubeb/gtest/test_resampler.cpp +++ b/media/libcubeb/gtest/test_resampler.cpp @@ -717,7 +717,8 @@ TEST(cubeb, resampler_passthrough_duplex_callback_reordering) output_seq_idx += BUF_BASE_SIZE; - ASSERT_EQ(prebuffer_frames, static_cast(ARRAY_LENGTH(input_buffer_prebuffer) / input_params.channels)); + // prebuffer_frames will hold the frames used by the resampler. + ASSERT_EQ(prebuffer_frames, BUF_BASE_SIZE); ASSERT_EQ(got, BUF_BASE_SIZE); for (uint32_t i = 0; i < 300; i++) { diff --git a/media/libcubeb/src/cubeb_audiounit.cpp b/media/libcubeb/src/cubeb_audiounit.cpp index b3a69ed36de1..1cabcc9d8e5e 100644 --- a/media/libcubeb/src/cubeb_audiounit.cpp +++ b/media/libcubeb/src/cubeb_audiounit.cpp @@ -49,6 +49,16 @@ typedef UInt32 AudioFormatFlags; const char * DISPATCH_QUEUE_LABEL = "org.mozilla.cubeb"; +#ifdef ALOGV +#undef ALOGV +#endif +#define ALOGV(msg, ...) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{LOGV(msg, ##__VA_ARGS__);}) + +#ifdef ALOG +#undef ALOG +#endif +#define ALOG(msg, ...) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{LOG(msg, ##__VA_ARGS__);}) + /* Testing empirically, some headsets report a minimal latency that is very * low, but this does not work in practice. Lie and say the minimum is 256 * frames. */ @@ -221,13 +231,17 @@ struct cubeb_stream { /* Hold the input samples in every * input callback iteration */ std::unique_ptr input_linear_buffer; + // After the resampling some input data remains stored inside + // the resampler. This number is used in order to calculate + // the number of extra silence frames in input. + std::atomic available_input_frames{ 0 }; /* Frames on input buffer */ std::atomic input_buffer_frames{ 0 }; /* Frame counters */ std::atomic frames_played{ 0 }; uint64_t frames_queued = 0; std::atomic frames_read{ 0 }; - std::atomic shutdown{ false }; + std::atomic shutdown{ true }; std::atomic draining{ false }; /* Latency requested by the user. */ uint32_t latency_frames = 0; @@ -235,11 +249,6 @@ struct cubeb_stream { uint64_t hw_latency_frames = UINT64_MAX; std::atomic panning{ 0 }; std::unique_ptr resampler; - /* This is the number of output callback we got in a row. This is usually one, - * but can be two when the input and output rate are different, and more when - * a device has been plugged or unplugged, as there can be some time before - * the device is ready. */ - std::atomic output_callback_in_a_row{ 0 }; /* This is true if a device change callback is currently running. */ std::atomic switching_device{ false }; std::atomic buffer_size_change_state{ false }; @@ -377,16 +386,18 @@ audiounit_render_input(cubeb_stream * stm, stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData, input_frames * stm->input_desc.mChannelsPerFrame); - LOGV("(%p) input: buffers %u, size %u, channels %u, frames %d.", + /* Advance input frame counter. */ + assert(input_frames > 0); + stm->frames_read += input_frames; + stm->available_input_frames += input_frames; + + ALOGV("(%p) input: buffers %u, size %u, channels %u, rendered frames %d, total frames %d.", stm, (unsigned int) input_buffer_list.mNumberBuffers, (unsigned int) input_buffer_list.mBuffers[0].mDataByteSize, (unsigned int) input_buffer_list.mBuffers[0].mNumberChannels, - (unsigned int) input_frames); - - /* Advance input frame counter. */ - assert(input_frames > 0); - stm->frames_read += input_frames; + (unsigned int) input_frames, + stm->available_input_frames.load()); return noErr; } @@ -405,19 +416,10 @@ audiounit_input_callback(void * user_ptr, assert(AU_IN_BUS == bus); if (stm->shutdown) { - LOG("(%p) input shutdown", stm); + ALOG("(%p) input shutdown", stm); return noErr; } - // This happens when we're finally getting a new input callback after having - // switched device, we can clear the input buffer now, only keeping the data - // we just got. - if (stm->output_callback_in_a_row > stm->expected_output_callbacks_in_a_row) { - stm->input_linear_buffer->pop( - stm->input_linear_buffer->length() - - input_frames * stm->input_stream_params.channels); - } - OSStatus r = audiounit_render_input(stm, flags, tstamp, bus, input_frames); if (r != noErr) { return r; @@ -425,7 +427,6 @@ audiounit_input_callback(void * user_ptr, // Full Duplex. We'll call data_callback in the AudioUnit output callback. if (stm->output_unit != NULL) { - stm->output_callback_in_a_row = 0; return noErr; } @@ -449,6 +450,12 @@ audiounit_input_callback(void * user_ptr, return noErr; } +static uint32_t +minimum_resampling_input_frames(cubeb_stream *stm) +{ + return ceilf(stm->input_hw_rate / stm->output_hw_rate * stm->input_buffer_frames); +} + static bool is_extra_input_needed(cubeb_stream * stm) { @@ -457,17 +464,8 @@ is_extra_input_needed(cubeb_stream * stm) * Otherwise, if we had more than expected callbacks in a row, or we're currently * switching, we add some silence as well to compensate for the fact that * we're lacking some input data. */ - - /* If resampling is taking place after every output callback - * the input buffer expected to be empty. Any frame left over - * from resampling is stored inside the resampler available to - * be used in next iteration as needed. - * BUT when noop_resampler is operating we have left over - * frames since it does not store anything internally. */ return stm->frames_read == 0 || - (stm->input_linear_buffer->length() == 0 && - (stm->output_callback_in_a_row > stm->expected_output_callbacks_in_a_row || - stm->switching_device)); + stm->available_input_frames.load() < minimum_resampling_input_frames(stm); } static OSStatus @@ -483,20 +481,19 @@ audiounit_output_callback(void * user_ptr, cubeb_stream * stm = static_cast(user_ptr); - stm->output_callback_in_a_row++; + ALOGV("(%p) output: buffers %u, size %u, channels %u, frames %u, total input frames %d.", + stm, + (unsigned int) outBufferList->mNumberBuffers, + (unsigned int) outBufferList->mBuffers[0].mDataByteSize, + (unsigned int) outBufferList->mBuffers[0].mNumberChannels, + (unsigned int) output_frames, + stm->available_input_frames.load()); - LOGV("(%p) output: buffers %u, size %u, channels %u, frames %u.", - stm, - (unsigned int) outBufferList->mNumberBuffers, - (unsigned int) outBufferList->mBuffers[0].mDataByteSize, - (unsigned int) outBufferList->mBuffers[0].mNumberChannels, - (unsigned int) output_frames); - - long input_frames = 0; + long input_frames = 0, input_frames_before_fill = 0; void * output_buffer = NULL, * input_buffer = NULL; if (stm->shutdown) { - LOG("(%p) output shutdown.", stm); + ALOG("(%p) output shutdown.", stm); audiounit_make_silent(&outBufferList->mBuffers[0]); return noErr; } @@ -518,16 +515,19 @@ audiounit_output_callback(void * user_ptr, /* If Full duplex get also input buffer */ if (stm->input_unit != NULL) { if (is_extra_input_needed(stm)) { - uint32_t min_input_frames_required = ceilf(stm->input_hw_rate / stm->output_hw_rate * - stm->input_buffer_frames); - stm->input_linear_buffer->push_silence(min_input_frames_required * stm->input_desc.mChannelsPerFrame); - LOG("(%p) %s pushed %u frames of input silence.", stm, stm->frames_read == 0 ? "Input hasn't started," : - stm->switching_device ? "Device switching," : "Drop out,", min_input_frames_required); + uint32_t min_input_frames = minimum_resampling_input_frames(stm); + stm->input_linear_buffer->push_silence(min_input_frames * stm->input_desc.mChannelsPerFrame); + stm->available_input_frames += min_input_frames; + + ALOG("(%p) %s pushed %u frames of input silence.", stm, stm->frames_read == 0 ? "Input hasn't started," : + stm->switching_device ? "Device switching," : "Drop out,", min_input_frames); } - // The input buffer input_buffer = stm->input_linear_buffer->data(); - // Number of input frames in the buffer + // Number of input frames in the buffer. It will change to actually used frames + // inside fill input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame; + // Number of input frames pushed inside resampler. + input_frames_before_fill = input_frames; } /* Call user callback through resampler. */ @@ -538,7 +538,11 @@ audiounit_output_callback(void * user_ptr, output_frames); if (input_buffer) { - stm->input_linear_buffer->pop(input_frames * stm->input_desc.mChannelsPerFrame); + // Decrease counter by the number of frames used by resampler + stm->available_input_frames -= input_frames; + assert(stm->available_input_frames.load() >= 0); + // Pop from the buffer the frames pushed to the resampler. + stm->input_linear_buffer->pop(input_frames_before_fill * stm->input_desc.mChannelsPerFrame); } if (outframes < 0) { @@ -714,6 +718,9 @@ audiounit_property_listener_callback(AudioObjectID /* id */, UInt32 address_coun case kAudioDevicePropertyDataSource: LOG("Event[%u] - mSelector == kAudioHardwarePropertyDataSource", (unsigned int) i); break; + default: + LOG("Event[%u] - mSelector == Unexpected Event id %d, return", (unsigned int) i, addresses[i].mSelector); + return noErr; } } @@ -890,6 +897,16 @@ audiounit_uninstall_device_changed_callback(cubeb_stream * stm) if (r != noErr) { return CUBEB_ERROR; } + + /* Event to notify when the input is going away. */ + AudioDeviceID dev = stm->input_device ? stm->input_device : + audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_INPUT); + r = audiounit_remove_listener(stm, dev, kAudioDevicePropertyDeviceIsAlive, + kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); + if (r != noErr) { + PRINT_ERROR_CODE("AudioObjectRemovePropertyListener/input/kAudioDevicePropertyDeviceIsAlive", r); + return CUBEB_ERROR; + } } return CUBEB_OK; } @@ -1301,18 +1318,8 @@ audiounit_init_input_linear_buffer(cubeb_stream * stream, uint32_t capacity) } else { stream->input_linear_buffer.reset(new auto_array_wrapper_impl(size)); } - assert(stream->input_linear_buffer->length() == 0); - // Pre-buffer silence if needed - if (capacity != 1) { - size_t silence_size = stream->input_buffer_frames * - stream->input_desc.mChannelsPerFrame; - stream->input_linear_buffer->push_silence(silence_size); - - assert(stream->input_linear_buffer->length() == silence_size); - } - return CUBEB_OK; } diff --git a/media/libcubeb/src/cubeb_resampler.cpp b/media/libcubeb/src/cubeb_resampler.cpp index 12de294b4764..65dd0a86a097 100644 --- a/media/libcubeb/src/cubeb_resampler.cpp +++ b/media/libcubeb/src/cubeb_resampler.cpp @@ -72,6 +72,7 @@ long passthrough_resampler::fill(void * input_buffer, long * input_frames_cou if (input_buffer) { internal_input_buffer.pop(nullptr, frames_to_samples(output_frames)); + *input_frames_count = output_frames; } return rv; @@ -179,7 +180,7 @@ cubeb_resampler_speex /* process the input, and present exactly `output_frames_needed` in the * callback. */ input_processor->input(input_buffer, *input_frames_count); - resampled_input = input_processor->output(resampled_frame_count); + resampled_input = input_processor->output(resampled_frame_count, (size_t*)input_frames_count); long got = data_callback(stream, user_ptr, resampled_input, nullptr, resampled_frame_count); @@ -226,7 +227,7 @@ cubeb_resampler_speex * callback. */ input_processor->input(in_buffer, *input_frames_count); resampled_input = - input_processor->output(output_frames_before_processing); + input_processor->output(output_frames_before_processing, (size_t*)input_frames_count); } else { resampled_input = nullptr; } diff --git a/media/libcubeb/src/cubeb_resampler_internal.h b/media/libcubeb/src/cubeb_resampler_internal.h index 9c17a5171d4f..bb5da0572360 100644 --- a/media/libcubeb/src/cubeb_resampler_internal.h +++ b/media/libcubeb/src/cubeb_resampler_internal.h @@ -222,7 +222,7 @@ public: /** Returns a buffer containing exactly `output_frame_count` resampled frames. * The consumer should not hold onto the pointer. */ - T * output(size_t output_frame_count) + T * output(size_t output_frame_count, size_t * input_frames_used) { if (resampling_out_buffer.capacity() < frames_to_samples(output_frame_count)) { resampling_out_buffer.reserve(frames_to_samples(output_frame_count)); @@ -239,6 +239,7 @@ public: /* This shifts back any unresampled samples to the beginning of the input buffer. */ resampling_in_buffer.pop(nullptr, frames_to_samples(in_len)); + *input_frames_used = in_len; return resampling_out_buffer.data(); } @@ -376,7 +377,7 @@ public: * @parameter frames_needed the number of frames to be returned. * @return a buffer containing the delayed frames. The consumer should not * hold onto the pointer. */ - T * output(uint32_t frames_needed) + T * output(uint32_t frames_needed, size_t * input_frames_used) { if (delay_output_buffer.capacity() < frames_to_samples(frames_needed)) { delay_output_buffer.reserve(frames_to_samples(frames_needed)); @@ -386,6 +387,7 @@ public: delay_output_buffer.push(delay_input_buffer.data(), frames_to_samples(frames_needed)); delay_input_buffer.pop(nullptr, frames_to_samples(frames_needed)); + *input_frames_used = frames_needed; return delay_output_buffer.data(); }