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

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

Issue 2690793002: Add basic resample support to WASAPIAudioInputStream. (Closed)
Patch Set: Use AudioConverter for converting instead of the multi channel resampler. 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/channel_layout.h"
19 #include "media/base/limits.h"
20 #include "media/base/multi_channel_resampler.h"
18 21
19 using base::win::ScopedComPtr; 22 using base::win::ScopedComPtr;
20 using base::win::ScopedCOMInitializer; 23 using base::win::ScopedCOMInitializer;
21 24
22 namespace media { 25 namespace media {
23 26
24 WASAPIAudioInputStream::WASAPIAudioInputStream(AudioManagerWin* manager, 27 WASAPIAudioInputStream::WASAPIAudioInputStream(AudioManagerWin* manager,
25 const AudioParameters& params, 28 const AudioParameters& params,
26 const std::string& device_id) 29 const std::string& device_id)
27 : manager_(manager), 30 : manager_(manager),
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
116 // set during construction. 119 // set during construction.
117 if (!DesiredFormatIsSupported()) { 120 if (!DesiredFormatIsSupported()) {
118 open_result_ = OPEN_RESULT_FORMAT_NOT_SUPPORTED; 121 open_result_ = OPEN_RESULT_FORMAT_NOT_SUPPORTED;
119 ReportOpenResult(); 122 ReportOpenResult();
120 return false; 123 return false;
121 } 124 }
122 125
123 // Initialize the audio stream between the client and the device using 126 // Initialize the audio stream between the client and the device using
124 // shared mode and a lowest possible glitch-free latency. 127 // shared mode and a lowest possible glitch-free latency.
125 hr = InitializeAudioEngine(); 128 hr = InitializeAudioEngine();
129 if (SUCCEEDED(hr) && converter_)
130 open_result_ = OPEN_RESULT_OK_WITH_RESAMPLING;
126 ReportOpenResult(); // Report before we assign a value to |opened_|. 131 ReportOpenResult(); // Report before we assign a value to |opened_|.
127 opened_ = SUCCEEDED(hr); 132 opened_ = SUCCEEDED(hr);
128 DCHECK(open_result_ == OPEN_RESULT_OK || !opened_);
129 133
130 return opened_; 134 return opened_;
131 } 135 }
132 136
133 void WASAPIAudioInputStream::Start(AudioInputCallback* callback) { 137 void WASAPIAudioInputStream::Start(AudioInputCallback* callback) {
134 DCHECK(CalledOnValidThread()); 138 DCHECK(CalledOnValidThread());
135 DCHECK(callback); 139 DCHECK(callback);
136 DLOG_IF(ERROR, !opened_) << "Open() has not been called successfully"; 140 DLOG_IF(ERROR, !opened_) << "Open() has not been called successfully";
137 if (!opened_) 141 if (!opened_)
138 return; 142 return;
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
220 started_ = false; 224 started_ = false;
221 sink_ = NULL; 225 sink_ = NULL;
222 } 226 }
223 227
224 void WASAPIAudioInputStream::Close() { 228 void WASAPIAudioInputStream::Close() {
225 DVLOG(1) << "WASAPIAudioInputStream::Close()"; 229 DVLOG(1) << "WASAPIAudioInputStream::Close()";
226 // It is valid to call Close() before calling open or Start(). 230 // It is valid to call Close() before calling open or Start().
227 // It is also valid to call Close() after Start() has been called. 231 // It is also valid to call Close() after Start() has been called.
228 Stop(); 232 Stop();
229 233
234 if (converter_)
235 converter_->RemoveInput(this);
236
230 // Inform the audio manager that we have been closed. This will cause our 237 // Inform the audio manager that we have been closed. This will cause our
231 // destruction. 238 // destruction.
232 manager_->ReleaseInputStream(this); 239 manager_->ReleaseInputStream(this);
233 } 240 }
234 241
235 double WASAPIAudioInputStream::GetMaxVolume() { 242 double WASAPIAudioInputStream::GetMaxVolume() {
236 // Verify that Open() has been called succesfully, to ensure that an audio 243 // Verify that Open() has been called succesfully, to ensure that an audio
237 // session exists and that an ISimpleAudioVolume interface has been created. 244 // session exists and that an ISimpleAudioVolume interface has been created.
238 DLOG_IF(ERROR, !opened_) << "Open() has not been called successfully"; 245 DLOG_IF(ERROR, !opened_) << "Open() has not been called successfully";
239 if (!opened_) 246 if (!opened_)
(...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after
417 // each time SetVolume() is called through IPC by the render-side AGC. 424 // each time SetVolume() is called through IPC by the render-side AGC.
418 GetAgcVolume(&volume); 425 GetAgcVolume(&volume);
419 426
420 // Deliver captured data to the registered consumer using a packet 427 // Deliver captured data to the registered consumer using a packet
421 // size which was specified at construction. 428 // size which was specified at construction.
422 uint32_t delay_frames = static_cast<uint32_t>(audio_delay_frames + 0.5); 429 uint32_t delay_frames = static_cast<uint32_t>(audio_delay_frames + 0.5);
423 while (buffer_frame_index >= packet_size_frames_) { 430 while (buffer_frame_index >= packet_size_frames_) {
424 // Copy data to audio bus to match the OnData interface. 431 // Copy data to audio bus to match the OnData interface.
425 uint8_t* audio_data = 432 uint8_t* audio_data =
426 reinterpret_cast<uint8_t*>(capture_buffer.get()); 433 reinterpret_cast<uint8_t*>(capture_buffer.get());
427 audio_bus_->FromInterleaved(audio_data, audio_bus_->frames(), 434
428 format_.wBitsPerSample / 8); 435 if (converter_) {
436 convert_bus_->FromInterleaved(audio_data, convert_bus_->frames(),
437 format_.wBitsPerSample / 8);
438 data_was_converted_ = false;
439 converter_->ConvertWithDelay(delay_frames, audio_bus_.get());
440 if (!data_was_converted_) {
441 LOG(ERROR) << "Failed to convert enough samples.";
DaleCurtis 2017/02/16 02:03:36 Won't this trample whatever you have in audio_bus_
tommi (sloooow) - chröme 2017/02/16 21:59:46 Yes indeed it would. I wasn't able to repro this a
442 converter_->ConvertWithDelay(delay_frames, audio_bus_.get());
443 }
444 DCHECK(data_was_converted_);
445 } else {
446 audio_bus_->FromInterleaved(audio_data, audio_bus_->frames(),
447 format_.wBitsPerSample / 8);
448 }
429 449
430 // Deliver data packet, delay estimation and volume level to 450 // Deliver data packet, delay estimation and volume level to
431 // the user. 451 // the user.
432 sink_->OnData(this, audio_bus_.get(), delay_frames * frame_size_, 452 sink_->OnData(this, audio_bus_.get(), delay_frames * frame_size_,
433 volume); 453 volume);
434 454
435 // Store parts of the recorded data which can't be delivered 455 // Store parts of the recorded data which can't be delivered
436 // using the current packet size. The stored section will be used 456 // using the current packet size. The stored section will be used
437 // either in the next while-loop iteration or in the next 457 // either in the next while-loop iteration or in the next
438 // capture event. 458 // capture event.
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
588 // internal processing. However, the format for an application stream 608 // internal processing. However, the format for an application stream
589 // typically must have the same number of channels and the same sample 609 // typically must have the same number of channels and the same sample
590 // rate as the stream format used by the device. 610 // rate as the stream format used by the device.
591 // Many audio devices support both PCM and non-PCM stream formats. However, 611 // Many audio devices support both PCM and non-PCM stream formats. However,
592 // the audio engine can mix only PCM streams. 612 // the audio engine can mix only PCM streams.
593 base::win::ScopedCoMem<WAVEFORMATEX> closest_match; 613 base::win::ScopedCoMem<WAVEFORMATEX> closest_match;
594 HRESULT hr = audio_client_->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, 614 HRESULT hr = audio_client_->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
595 &format_, &closest_match); 615 &format_, &closest_match);
596 DLOG_IF(ERROR, hr == S_FALSE) << "Format is not supported " 616 DLOG_IF(ERROR, hr == S_FALSE) << "Format is not supported "
597 << "but a closest match exists."; 617 << "but a closest match exists.";
618 if (hr == S_FALSE &&
619 closest_match->nSamplesPerSec >= limits::kMinSampleRate &&
620 closest_match->nSamplesPerSec <= limits::kMaxSampleRate) {
621 // We want a 1:1 ratio between the buffers we get and the buffers we
622 // give to OnData so that each buffer we receive from the OS can be directly
623 // resampled to a buffer that matches with what the client asked for.
624 const double buffer_ratio =
625 format_.nSamplesPerSec / static_cast<double>(audio_bus_->frames());
626 const size_t new_frames_per_buffer =
627 static_cast<size_t>(closest_match->nSamplesPerSec / buffer_ratio);
628
629 const AudioParameters input(
630 AudioParameters::AUDIO_PCM_LOW_LATENCY,
631 GuessChannelLayout(closest_match->nChannels),
DaleCurtis 2017/02/16 02:03:36 Need to check the result of this that it's not CHA
632 closest_match->nSamplesPerSec,
633 // We need to be careful here to not pick the closest wBitsPerSample
DaleCurtis 2017/02/16 02:03:36 I think this statement is false, or at least shoul
tommi (sloooow) - chröme 2017/02/16 21:59:46 Oh interesting, thanks for pointing that out (and
634 // match as we need to use the PCM format (which might not be what
635 // closeest_match->wFormat is) and the internal resampler doesn't
636 // support all formats we might get here. So, we stick to the
637 // wBitsPerSample that was asked for originally (most likely 16).
638 format_.wBitsPerSample, new_frames_per_buffer);
639
640 const AudioParameters output(AudioParameters::AUDIO_PCM_LOW_LATENCY,
641 GuessChannelLayout(format_.nChannels),
642 format_.nSamplesPerSec, format_.wBitsPerSample,
643 audio_bus_->frames());
644
645 converter_.reset(new AudioConverter(input, output, false));
646 converter_->AddInput(this);
647 converter_->PrimeWithSilence();
648 convert_bus_ = AudioBus::Create(input);
649
650 // Now change the format we're going to ask for to better match with what
651 // the OS can provide. If we succeed in opening the stream with these
652 // params, we can take care of the required resampling.
653 format_.nSamplesPerSec = closest_match->nSamplesPerSec;
654 format_.nChannels = closest_match->nChannels;
655 format_.nBlockAlign = (format_.wBitsPerSample / 8) * format_.nChannels;
656 format_.nAvgBytesPerSec = format_.nSamplesPerSec * format_.nBlockAlign;
657
658 // Update our packet size assumptions based on the new format.
659 const auto new_bytes_per_buffer = convert_bus_->frames() *
660 format_.nChannels *
661 (format_.wBitsPerSample / 8);
662 packet_size_frames_ = new_bytes_per_buffer / format_.nBlockAlign;
663 packet_size_bytes_ = new_bytes_per_buffer;
664 frame_size_ = format_.nBlockAlign;
665 ms_to_frame_count_ = static_cast<double>(format_.nSamplesPerSec) / 1000.0;
666
667 // Indicate that we're good to go with a close match.
668 hr = S_OK;
669 }
670
598 return (hr == S_OK); 671 return (hr == S_OK);
599 } 672 }
600 673
601 HRESULT WASAPIAudioInputStream::InitializeAudioEngine() { 674 HRESULT WASAPIAudioInputStream::InitializeAudioEngine() {
602 DCHECK_EQ(OPEN_RESULT_OK, open_result_); 675 DCHECK_EQ(OPEN_RESULT_OK, open_result_);
603 DWORD flags; 676 DWORD flags;
604 // Use event-driven mode only fo regular input devices. For loopback the 677 // Use event-driven mode only fo regular input devices. For loopback the
605 // EVENTCALLBACK flag is specified when intializing 678 // EVENTCALLBACK flag is specified when intializing
606 // |audio_render_client_for_loopback_|. 679 // |audio_render_client_for_loopback_|.
607 if (device_id_ == AudioDeviceDescription::kLoopbackInputDeviceId || 680 if (device_id_ == AudioDeviceDescription::kLoopbackInputDeviceId ||
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
731 804
732 return hr; 805 return hr;
733 } 806 }
734 807
735 void WASAPIAudioInputStream::ReportOpenResult() const { 808 void WASAPIAudioInputStream::ReportOpenResult() const {
736 DCHECK(!opened_); // This method must be called before we set this flag. 809 DCHECK(!opened_); // This method must be called before we set this flag.
737 UMA_HISTOGRAM_ENUMERATION("Media.Audio.Capture.Win.Open", open_result_, 810 UMA_HISTOGRAM_ENUMERATION("Media.Audio.Capture.Win.Open", open_result_,
738 OPEN_RESULT_MAX + 1); 811 OPEN_RESULT_MAX + 1);
739 } 812 }
740 813
814 double WASAPIAudioInputStream::ProvideInput(AudioBus* audio_bus,
815 uint32_t frames_delayed) {
816 convert_bus_->CopyTo(audio_bus);
817 data_was_converted_ = true;
818 return 1.0;
819 }
820
741 } // namespace media 821 } // 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