Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(963)

Side by Side Diff: media/audio/win/audio_low_latency_input_win.cc

Issue 2690793002: Add basic resample support to WASAPIAudioInputStream. (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/audio/win/audio_low_latency_input_win.h" 5 #include "media/audio/win/audio_low_latency_input_win.h"
6 6
7 #include <memory> 7 #include <memory>
8 8
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/metrics/histogram_macros.h" 10 #include "base/metrics/histogram_macros.h"
11 #include "base/strings/utf_string_conversions.h" 11 #include "base/strings/utf_string_conversions.h"
12 #include "base/trace_event/trace_event.h" 12 #include "base/trace_event/trace_event.h"
13 #include "media/audio/audio_device_description.h" 13 #include "media/audio/audio_device_description.h"
14 #include "media/audio/win/audio_manager_win.h" 14 #include "media/audio/win/audio_manager_win.h"
15 #include "media/audio/win/avrt_wrapper_win.h" 15 #include "media/audio/win/avrt_wrapper_win.h"
16 #include "media/audio/win/core_audio_util_win.h" 16 #include "media/audio/win/core_audio_util_win.h"
17 #include "media/base/audio_bus.h" 17 #include "media/base/audio_bus.h"
18 #include "media/base/multi_channel_resampler.h"
18 19
19 using base::win::ScopedComPtr; 20 using base::win::ScopedComPtr;
20 using base::win::ScopedCOMInitializer; 21 using base::win::ScopedCOMInitializer;
21 22
22 namespace media { 23 namespace media {
23 24
24 WASAPIAudioInputStream::WASAPIAudioInputStream(AudioManagerWin* manager, 25 WASAPIAudioInputStream::WASAPIAudioInputStream(AudioManagerWin* manager,
25 const AudioParameters& params, 26 const AudioParameters& params,
26 const std::string& device_id) 27 const std::string& device_id)
27 : manager_(manager), 28 : manager_(manager),
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
116 // set during construction. 117 // set during construction.
117 if (!DesiredFormatIsSupported()) { 118 if (!DesiredFormatIsSupported()) {
118 open_result_ = OPEN_RESULT_FORMAT_NOT_SUPPORTED; 119 open_result_ = OPEN_RESULT_FORMAT_NOT_SUPPORTED;
119 ReportOpenResult(); 120 ReportOpenResult();
120 return false; 121 return false;
121 } 122 }
122 123
123 // Initialize the audio stream between the client and the device using 124 // Initialize the audio stream between the client and the device using
124 // shared mode and a lowest possible glitch-free latency. 125 // shared mode and a lowest possible glitch-free latency.
125 hr = InitializeAudioEngine(); 126 hr = InitializeAudioEngine();
127 if (SUCCEEDED(hr) && resampler_.get() != nullptr)
DaleCurtis 2017/02/13 19:40:42 just "&& resampler_" it has safe-bool conversion b
tommi (sloooow) - chröme 2017/02/16 17:08:46 Done.
128 open_result_ = OPEN_RESULT_OK_WITH_RESAMPLING;
126 ReportOpenResult(); // Report before we assign a value to |opened_|. 129 ReportOpenResult(); // Report before we assign a value to |opened_|.
127 opened_ = SUCCEEDED(hr); 130 opened_ = SUCCEEDED(hr);
128 DCHECK(open_result_ == OPEN_RESULT_OK || !opened_);
129 131
130 return opened_; 132 return opened_;
131 } 133 }
132 134
133 void WASAPIAudioInputStream::Start(AudioInputCallback* callback) { 135 void WASAPIAudioInputStream::Start(AudioInputCallback* callback) {
134 DCHECK(CalledOnValidThread()); 136 DCHECK(CalledOnValidThread());
135 DCHECK(callback); 137 DCHECK(callback);
136 DLOG_IF(ERROR, !opened_) << "Open() has not been called successfully"; 138 DLOG_IF(ERROR, !opened_) << "Open() has not been called successfully";
137 if (!opened_) 139 if (!opened_)
138 return; 140 return;
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
308 avrt::AvSetMmThreadCharacteristics(L"Pro Audio", &task_index); 310 avrt::AvSetMmThreadCharacteristics(L"Pro Audio", &task_index);
309 bool mmcss_is_ok = 311 bool mmcss_is_ok =
310 (mm_task && avrt::AvSetMmThreadPriority(mm_task, AVRT_PRIORITY_CRITICAL)); 312 (mm_task && avrt::AvSetMmThreadPriority(mm_task, AVRT_PRIORITY_CRITICAL));
311 if (!mmcss_is_ok) { 313 if (!mmcss_is_ok) {
312 // Failed to enable MMCSS on this thread. It is not fatal but can lead 314 // Failed to enable MMCSS on this thread. It is not fatal but can lead
313 // to reduced QoS at high load. 315 // to reduced QoS at high load.
314 DWORD err = GetLastError(); 316 DWORD err = GetLastError();
315 LOG(WARNING) << "Failed to enable MMCSS (error code=" << err << ")."; 317 LOG(WARNING) << "Failed to enable MMCSS (error code=" << err << ").";
316 } 318 }
317 319
318 // Allocate a buffer with a size that enables us to take care of cases like: 320 // Allocate a buffer with a size that enables us to take care of cases like:
DaleCurtis 2017/02/13 19:40:42 You might consider that instead of uint8_t array +
tommi (sloooow) - chröme 2017/02/16 17:08:47 Acknowledged.
319 // 1) The recorded buffer size is smaller, or does not match exactly with, 321 // 1) The recorded buffer size is smaller, or does not match exactly with,
320 // the selected packet size used in each callback. 322 // the selected packet size used in each callback.
321 // 2) The selected buffer size is larger than the recorded buffer size in 323 // 2) The selected buffer size is larger than the recorded buffer size in
322 // each event. 324 // each event.
323 size_t buffer_frame_index = 0; 325 size_t buffer_frame_index = 0;
324 size_t capture_buffer_size = 326 size_t capture_buffer_size =
325 std::max(2 * endpoint_buffer_size_frames_ * frame_size_, 327 std::max(2 * endpoint_buffer_size_frames_ * frame_size_,
326 2 * packet_size_frames_ * frame_size_); 328 2 * packet_size_frames_ * frame_size_);
327 std::unique_ptr<uint8_t[]> capture_buffer(new uint8_t[capture_buffer_size]); 329 std::unique_ptr<uint8_t[]> capture_buffer(new uint8_t[capture_buffer_size]);
328 330
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
417 // each time SetVolume() is called through IPC by the render-side AGC. 419 // each time SetVolume() is called through IPC by the render-side AGC.
418 GetAgcVolume(&volume); 420 GetAgcVolume(&volume);
419 421
420 // Deliver captured data to the registered consumer using a packet 422 // Deliver captured data to the registered consumer using a packet
421 // size which was specified at construction. 423 // size which was specified at construction.
422 uint32_t delay_frames = static_cast<uint32_t>(audio_delay_frames + 0.5); 424 uint32_t delay_frames = static_cast<uint32_t>(audio_delay_frames + 0.5);
423 while (buffer_frame_index >= packet_size_frames_) { 425 while (buffer_frame_index >= packet_size_frames_) {
424 // Copy data to audio bus to match the OnData interface. 426 // Copy data to audio bus to match the OnData interface.
425 uint8_t* audio_data = 427 uint8_t* audio_data =
426 reinterpret_cast<uint8_t*>(capture_buffer.get()); 428 reinterpret_cast<uint8_t*>(capture_buffer.get());
427 audio_bus_->FromInterleaved(audio_data, audio_bus_->frames(), 429
428 format_.wBitsPerSample / 8); 430 if (resampler_.get()) {
DaleCurtis 2017/02/13 19:40:42 Ditto.
tommi (sloooow) - chröme 2017/02/16 17:08:46 Done.
431 resample_bus_->FromInterleaved(audio_data, resample_bus_->frames(),
432 format_.wBitsPerSample / 8);
433 resampler_->Resample(audio_bus_->frames(), audio_bus_.get());
434 } else {
435 audio_bus_->FromInterleaved(audio_data, audio_bus_->frames(),
436 format_.wBitsPerSample / 8);
437 }
429 438
430 // Deliver data packet, delay estimation and volume level to 439 // Deliver data packet, delay estimation and volume level to
431 // the user. 440 // the user.
432 sink_->OnData(this, audio_bus_.get(), delay_frames * frame_size_, 441 sink_->OnData(this, audio_bus_.get(), delay_frames * frame_size_,
433 volume); 442 volume);
434 443
435 // Store parts of the recorded data which can't be delivered 444 // Store parts of the recorded data which can't be delivered
436 // using the current packet size. The stored section will be used 445 // using the current packet size. The stored section will be used
437 // either in the next while-loop iteration or in the next 446 // either in the next while-loop iteration or in the next
438 // capture event. 447 // capture event.
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
588 // internal processing. However, the format for an application stream 597 // internal processing. However, the format for an application stream
589 // typically must have the same number of channels and the same sample 598 // typically must have the same number of channels and the same sample
590 // rate as the stream format used by the device. 599 // rate as the stream format used by the device.
591 // Many audio devices support both PCM and non-PCM stream formats. However, 600 // Many audio devices support both PCM and non-PCM stream formats. However,
592 // the audio engine can mix only PCM streams. 601 // the audio engine can mix only PCM streams.
593 base::win::ScopedCoMem<WAVEFORMATEX> closest_match; 602 base::win::ScopedCoMem<WAVEFORMATEX> closest_match;
594 HRESULT hr = audio_client_->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, 603 HRESULT hr = audio_client_->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
595 &format_, &closest_match); 604 &format_, &closest_match);
596 DLOG_IF(ERROR, hr == S_FALSE) << "Format is not supported " 605 DLOG_IF(ERROR, hr == S_FALSE) << "Format is not supported "
597 << "but a closest match exists."; 606 << "but a closest match exists.";
607 if (hr == S_FALSE && closest_match->nChannels == format_.nChannels) {
DaleCurtis 2017/02/13 19:40:42 If you use a AudioConverter it'll handle mismatche
DaleCurtis 2017/02/13 23:59:00 You should also sanity check the closest match sam
tommi (sloooow) - chröme 2017/02/16 17:08:46 Done. Also switched to AudioConverter.
608 // We want a 1:1 ratio between the buffers we get and the buffers we
609 // give to OnData so that each buffer we receive from the OS can be directly
610 // resampled to a buffer that matches with what the client asked for.
611 const double buffer_ratio =
612 format_.nSamplesPerSec / static_cast<double>(audio_bus_->frames());
613 const size_t new_frames_per_buffer =
614 static_cast<size_t>(closest_match->nSamplesPerSec / buffer_ratio);
615
616 const double sample_rate_ratio =
617 static_cast<double>(closest_match->nSamplesPerSec) /
618 static_cast<double>(format_.nSamplesPerSec);
619 resampler_.reset(new MultiChannelResampler(
DaleCurtis 2017/02/13 19:40:42 I think this works, though not how we've done it i
DaleCurtis 2017/02/13 23:59:00 Over a long enough time frame I think this might l
tommi (sloooow) - chröme 2017/02/16 17:08:47 I tried several ways of reproducing that, includin
tommi (sloooow) - chröme 2017/02/16 17:09:49 Oh please ignore this, I did more testing and figu
620 format_.nChannels, sample_rate_ratio, new_frames_per_buffer,
621 base::Bind(&WASAPIAudioInputStream::ProvideInput,
622 base::Unretained(this))));
623 resampler_->PrimeWithSilence();
624 resample_bus_ = AudioBus::Create(format_.nChannels, new_frames_per_buffer);
625
626 // Now change the format we're going to ask for to better match with what
627 // the OS can provide. If we succeed in opening the stream with these
628 // params, we can take care of the required resampling.
629 format_.nSamplesPerSec = closest_match->nSamplesPerSec;
630 format_.nAvgBytesPerSec = format_.nSamplesPerSec * format_.nBlockAlign;
631
632 // Update our packet size assumptions based on the new format.
633 const auto new_bytes_per_buffer = resample_bus_->frames() *
634 closest_match->nChannels *
635 (format_.wBitsPerSample / 8);
636 packet_size_frames_ = new_bytes_per_buffer / format_.nBlockAlign;
637 packet_size_bytes_ = new_bytes_per_buffer;
638 ms_to_frame_count_ =
639 static_cast<double>(closest_match->nSamplesPerSec) / 1000.0;
640
641 // Indicate that we're good to go with a close match.
642 hr = S_OK;
643 }
644
598 return (hr == S_OK); 645 return (hr == S_OK);
599 } 646 }
600 647
601 HRESULT WASAPIAudioInputStream::InitializeAudioEngine() { 648 HRESULT WASAPIAudioInputStream::InitializeAudioEngine() {
602 DCHECK_EQ(OPEN_RESULT_OK, open_result_); 649 DCHECK_EQ(OPEN_RESULT_OK, open_result_);
603 DWORD flags; 650 DWORD flags;
604 // Use event-driven mode only fo regular input devices. For loopback the 651 // Use event-driven mode only fo regular input devices. For loopback the
605 // EVENTCALLBACK flag is specified when intializing 652 // EVENTCALLBACK flag is specified when intializing
606 // |audio_render_client_for_loopback_|. 653 // |audio_render_client_for_loopback_|.
607 if (device_id_ == AudioDeviceDescription::kLoopbackInputDeviceId || 654 if (device_id_ == AudioDeviceDescription::kLoopbackInputDeviceId ||
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
731 778
732 return hr; 779 return hr;
733 } 780 }
734 781
735 void WASAPIAudioInputStream::ReportOpenResult() const { 782 void WASAPIAudioInputStream::ReportOpenResult() const {
736 DCHECK(!opened_); // This method must be called before we set this flag. 783 DCHECK(!opened_); // This method must be called before we set this flag.
737 UMA_HISTOGRAM_ENUMERATION("Media.Audio.Capture.Win.Open", open_result_, 784 UMA_HISTOGRAM_ENUMERATION("Media.Audio.Capture.Win.Open", open_result_,
738 OPEN_RESULT_MAX + 1); 785 OPEN_RESULT_MAX + 1);
739 } 786 }
740 787
788 void WASAPIAudioInputStream::ProvideInput(int resampler_frame_delay,
789 AudioBus* audio_bus) {
790 resample_bus_->CopyTo(audio_bus);
DaleCurtis 2017/02/13 19:40:42 Probably want a DCHECK this isn't called twice wit
tommi (sloooow) - chröme 2017/02/16 17:08:47 Done.
791 }
792
741 } // namespace media 793 } // namespace media
OLDNEW
« no previous file with comments | « media/audio/win/audio_low_latency_input_win.h ('k') | media/audio/win/audio_low_latency_input_win_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698