Tests that all available audio devices can be selected and used by the ADM.
New tests are: - AudioDeviceTest.StartStopPlayoutWithRealDevice - AudioDeviceTest.StartStopRecordingWithRealDevice (the comments below only affects ADM2 on Windows): When adding these tests it was found that we could hit the same known issue as in https://bugs.chromium.org/p/chromium/issues/detail?id=803056 and the same solution as in Chrome was therefore ported from Chrome to WebRTC. Hence, this change also adds support for core_audio_utility::WaveFormatWrapper to support devices that can return a format where only the WAVEFORMATEX parts is initialized. The old version would only DCHECK for these devices and that could lead to an unpredictable behavior. Tbr: minyue Bug: webrtc:11093 Change-Id: Icb238c5475100f251ce4e55e39a03653da04dbda Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/159982 Reviewed-by: Henrik Andreassson <henrika@webrtc.org> Commit-Queue: Henrik Andreassson <henrika@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29824}
This commit is contained in:
@ -23,6 +23,7 @@
|
||||
#include "api/task_queue/task_queue_factory.h"
|
||||
#include "modules/audio_device/audio_device_impl.h"
|
||||
#include "modules/audio_device/include/mock_audio_transport.h"
|
||||
#include "rtc_base/arraysize.h"
|
||||
#include "rtc_base/buffer.h"
|
||||
#include "rtc_base/critical_section.h"
|
||||
#include "rtc_base/event.h"
|
||||
@ -808,6 +809,60 @@ TEST_P(MAYBE_AudioDeviceTest, StartStopRecording) {
|
||||
StopRecording();
|
||||
}
|
||||
|
||||
// Tests Start/Stop playout for all available input devices to ensure that
|
||||
// the selected device can be created and used as intended.
|
||||
TEST_P(MAYBE_AudioDeviceTest, StartStopPlayoutWithRealDevice) {
|
||||
SKIP_TEST_IF_NOT(requirements_satisfied());
|
||||
int num_devices = audio_device()->PlayoutDevices();
|
||||
if (NewWindowsAudioDeviceModuleIsUsed()) {
|
||||
num_devices += 2;
|
||||
}
|
||||
EXPECT_GT(num_devices, 0);
|
||||
// Verify that all available playout devices can be set and used.
|
||||
for (int i = 0; i < num_devices; ++i) {
|
||||
EXPECT_EQ(0, audio_device()->SetPlayoutDevice(i));
|
||||
StartPlayout();
|
||||
StopPlayout();
|
||||
}
|
||||
#ifdef WEBRTC_WIN
|
||||
AudioDeviceModule::WindowsDeviceType device_role[] = {
|
||||
AudioDeviceModule::kDefaultDevice,
|
||||
AudioDeviceModule::kDefaultCommunicationDevice};
|
||||
for (size_t i = 0; i < arraysize(device_role); ++i) {
|
||||
EXPECT_EQ(0, audio_device()->SetPlayoutDevice(device_role[i]));
|
||||
StartPlayout();
|
||||
StopPlayout();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Tests Start/Stop recording for all available input devices to ensure that
|
||||
// the selected device can be created and used as intended.
|
||||
TEST_P(MAYBE_AudioDeviceTest, StartStopRecordingWithRealDevice) {
|
||||
SKIP_TEST_IF_NOT(requirements_satisfied());
|
||||
int num_devices = audio_device()->RecordingDevices();
|
||||
if (NewWindowsAudioDeviceModuleIsUsed()) {
|
||||
num_devices += 2;
|
||||
}
|
||||
EXPECT_GT(num_devices, 0);
|
||||
// Verify that all available recording devices can be set and used.
|
||||
for (int i = 0; i < num_devices; ++i) {
|
||||
EXPECT_EQ(0, audio_device()->SetRecordingDevice(i));
|
||||
StartRecording();
|
||||
StopRecording();
|
||||
}
|
||||
#ifdef WEBRTC_WIN
|
||||
AudioDeviceModule::WindowsDeviceType device_role[] = {
|
||||
AudioDeviceModule::kDefaultDevice,
|
||||
AudioDeviceModule::kDefaultCommunicationDevice};
|
||||
for (size_t i = 0; i < arraysize(device_role); ++i) {
|
||||
EXPECT_EQ(0, audio_device()->SetRecordingDevice(device_role[i]));
|
||||
StartRecording();
|
||||
StopRecording();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Tests Init/Stop/Init recording without any registered audio callback.
|
||||
// See https://bugs.chromium.org/p/webrtc/issues/detail?id=8041 for details
|
||||
// on why this test is useful.
|
||||
|
@ -391,7 +391,7 @@ bool CoreAudioBase::Init() {
|
||||
format_.dwChannelMask =
|
||||
format->nChannels == 1 ? KSAUDIO_SPEAKER_MONO : KSAUDIO_SPEAKER_STEREO;
|
||||
format_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
RTC_DLOG(INFO) << core_audio_utility::WaveFormatExToString(&format_);
|
||||
RTC_DLOG(INFO) << core_audio_utility::WaveFormatToString(&format_);
|
||||
|
||||
// Verify that the format is supported but exclude the test if the default
|
||||
// sample rate has been overridden. If so, the WASAPI audio engine will do
|
||||
|
@ -157,50 +157,20 @@ std::string ChannelMaskToString(DWORD channel_mask) {
|
||||
#define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
|
||||
#endif
|
||||
|
||||
// Converts from channel mask to DirectSound speaker configuration.
|
||||
// The values below are copied from ksmedia.h.
|
||||
// Example: KSAUDIO_SPEAKER_STEREO = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT).
|
||||
const char* DirectSoundConfigToString(DWORD channel_mask) {
|
||||
switch (channel_mask) {
|
||||
case KSAUDIO_SPEAKER_DIRECTOUT:
|
||||
return "KSAUDIO_DIRECTOUT";
|
||||
case KSAUDIO_SPEAKER_MONO:
|
||||
// Front center (C)
|
||||
return "KSAUDIO_MONO";
|
||||
case KSAUDIO_SPEAKER_1POINT1:
|
||||
return "KSAUDIO_1POINT1";
|
||||
case KSAUDIO_SPEAKER_STEREO:
|
||||
// Front left (L), front right (R).
|
||||
return "KSAUDIO_STEREO";
|
||||
case KSAUDIO_SPEAKER_2POINT1:
|
||||
return "KSAUDIO_2POINT1";
|
||||
case KSAUDIO_SPEAKER_3POINT0:
|
||||
return "KSAUDIO_3POINT0";
|
||||
case KSAUDIO_SPEAKER_3POINT1:
|
||||
return "KSAUDIO_3POINT1";
|
||||
case KSAUDIO_SPEAKER_QUAD:
|
||||
// L, R, back left (Lb), back right (Rb).
|
||||
return "KSAUDIO_QUAD";
|
||||
case KSAUDIO_SPEAKER_SURROUND:
|
||||
// L, R, front center (C), back center (Cb).
|
||||
return "KSAUDIO_SURROUND";
|
||||
case KSAUDIO_SPEAKER_5POINT0:
|
||||
return "KSAUDIO_5POINT0";
|
||||
case KSAUDIO_SPEAKER_5POINT1:
|
||||
return "KSAUDIO_5POINT1";
|
||||
case KSAUDIO_SPEAKER_7POINT0:
|
||||
return "KSAUDIO_7POINT0";
|
||||
case KSAUDIO_SPEAKER_7POINT1:
|
||||
// L, R, C, Lb, Rb, front left-of-center, front right-of-center, LFE.
|
||||
return "KSAUDIO_7POINT1";
|
||||
case KSAUDIO_SPEAKER_5POINT1_SURROUND:
|
||||
// L, R, C, side left (Ls), side right (Rs), LFE.
|
||||
return "KSAUDIO_5POINT1_SURROUND";
|
||||
case KSAUDIO_SPEAKER_7POINT1_SURROUND:
|
||||
// L, R, C, Lb, Rb, Ls, Rs, LFE.
|
||||
return "KSAUDIO_7POINT1_SURROUND";
|
||||
// Converts the most common format tags defined in mmreg.h into string
|
||||
// equivalents. Mainly intended for log messages.
|
||||
const char* WaveFormatTagToString(WORD format_tag) {
|
||||
switch (format_tag) {
|
||||
case WAVE_FORMAT_UNKNOWN:
|
||||
return "WAVE_FORMAT_UNKNOWN";
|
||||
case WAVE_FORMAT_PCM:
|
||||
return "WAVE_FORMAT_PCM";
|
||||
case WAVE_FORMAT_IEEE_FLOAT:
|
||||
return "WAVE_FORMAT_IEEE_FLOAT";
|
||||
case WAVE_FORMAT_EXTENSIBLE:
|
||||
return "WAVE_FORMAT_EXTENSIBLE";
|
||||
default:
|
||||
return "KSAUDIO_INVALID";
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
@ -589,6 +559,31 @@ HRESULT GetPreferredAudioParametersInternal(IAudioClient* client,
|
||||
|
||||
namespace core_audio_utility {
|
||||
|
||||
// core_audio_utility::WaveFormatWrapper implementation.
|
||||
WAVEFORMATEXTENSIBLE* WaveFormatWrapper::GetExtensible() const {
|
||||
RTC_CHECK(IsExtensible());
|
||||
return reinterpret_cast<WAVEFORMATEXTENSIBLE*>(ptr_);
|
||||
}
|
||||
|
||||
bool WaveFormatWrapper::IsExtensible() const {
|
||||
return ptr_->wFormatTag == WAVE_FORMAT_EXTENSIBLE && ptr_->cbSize >= 22;
|
||||
}
|
||||
|
||||
bool WaveFormatWrapper::IsPcm() const {
|
||||
return IsExtensible() ? GetExtensible()->SubFormat == KSDATAFORMAT_SUBTYPE_PCM
|
||||
: ptr_->wFormatTag == WAVE_FORMAT_PCM;
|
||||
}
|
||||
|
||||
bool WaveFormatWrapper::IsFloat() const {
|
||||
return IsExtensible()
|
||||
? GetExtensible()->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
|
||||
: ptr_->wFormatTag == WAVE_FORMAT_IEEE_FLOAT;
|
||||
}
|
||||
|
||||
size_t WaveFormatWrapper::size() const {
|
||||
return sizeof(*ptr_) + ptr_->cbSize;
|
||||
}
|
||||
|
||||
bool IsSupported() {
|
||||
RTC_DLOG(INFO) << "IsSupported";
|
||||
static bool g_is_supported = IsSupportedInternal();
|
||||
@ -904,19 +899,52 @@ HRESULT GetSharedModeMixFormat(IAudioClient* client,
|
||||
WAVEFORMATEXTENSIBLE* format) {
|
||||
RTC_DLOG(INFO) << "GetSharedModeMixFormat";
|
||||
RTC_DCHECK(client);
|
||||
ScopedCoMem<WAVEFORMATEXTENSIBLE> format_ex;
|
||||
|
||||
// The GetMixFormat method retrieves the stream format that the audio engine
|
||||
// uses for its internal processing of shared-mode streams. The method
|
||||
// allocates the storage for the structure and this memory will be released
|
||||
// when |mix_format| goes out of scope. The GetMixFormat method retrieves a
|
||||
// format descriptor that is in the form of a WAVEFORMATEXTENSIBLE structure
|
||||
// instead of a standalone WAVEFORMATEX structure. The method outputs a
|
||||
// pointer to the WAVEFORMATEX structure that is embedded at the start of
|
||||
// this WAVEFORMATEXTENSIBLE structure.
|
||||
// Note that, crbug/803056 indicates that some devices can return a format
|
||||
// where only the WAVEFORMATEX parts is initialized and we must be able to
|
||||
// account for that.
|
||||
ScopedCoMem<WAVEFORMATEXTENSIBLE> mix_format;
|
||||
_com_error error =
|
||||
client->GetMixFormat(reinterpret_cast<WAVEFORMATEX**>(&format_ex));
|
||||
client->GetMixFormat(reinterpret_cast<WAVEFORMATEX**>(&mix_format));
|
||||
if (FAILED(error.Error())) {
|
||||
RTC_LOG(LS_ERROR) << "IAudioClient::GetMixFormat failed: "
|
||||
<< ErrorToString(error);
|
||||
return error.Error();
|
||||
}
|
||||
|
||||
size_t bytes = sizeof(WAVEFORMATEX) + format_ex->Format.cbSize;
|
||||
RTC_DCHECK_EQ(bytes, sizeof(WAVEFORMATEXTENSIBLE));
|
||||
memcpy(format, format_ex, bytes);
|
||||
RTC_DLOG(INFO) << WaveFormatExToString(format);
|
||||
// Use a wave format wrapper to make things simpler.
|
||||
WaveFormatWrapper wrapped_format(mix_format.Get());
|
||||
|
||||
// Verify that the reported format can be mixed by the audio engine in
|
||||
// shared mode.
|
||||
if (!wrapped_format.IsPcm() && !wrapped_format.IsFloat()) {
|
||||
RTC_DLOG(LS_ERROR)
|
||||
<< "Only pure PCM or float audio streams can be mixed in shared mode";
|
||||
return AUDCLNT_E_UNSUPPORTED_FORMAT;
|
||||
}
|
||||
|
||||
// Log a warning for the rare case where |mix_format| only contains a
|
||||
// stand-alone WAVEFORMATEX structure but don't return.
|
||||
if (!wrapped_format.IsExtensible()) {
|
||||
RTC_DLOG(WARNING)
|
||||
<< "The returned format contains no extended information. "
|
||||
"The size is "
|
||||
<< wrapped_format.size() << " bytes.";
|
||||
}
|
||||
|
||||
// Copy the correct number of bytes into |*format| taking into account if
|
||||
// the returned structure is correctly extended or not.
|
||||
RTC_CHECK_LE(wrapped_format.size(), sizeof(WAVEFORMATEXTENSIBLE));
|
||||
memcpy(format, wrapped_format.get(), wrapped_format.size());
|
||||
RTC_DLOG(INFO) << WaveFormatToString(format);
|
||||
|
||||
return error.Error();
|
||||
}
|
||||
@ -926,7 +954,7 @@ bool IsFormatSupported(IAudioClient* client,
|
||||
const WAVEFORMATEXTENSIBLE* format) {
|
||||
RTC_DLOG(INFO) << "IsFormatSupported";
|
||||
RTC_DCHECK(client);
|
||||
ScopedCoMem<WAVEFORMATEXTENSIBLE> closest_match;
|
||||
ScopedCoMem<WAVEFORMATEX> closest_match;
|
||||
// This method provides a way for a client to determine, before calling
|
||||
// IAudioClient::Initialize, whether the audio engine supports a particular
|
||||
// stream format or not. In shared mode, the audio engine always supports
|
||||
@ -934,7 +962,9 @@ bool IsFormatSupported(IAudioClient* client,
|
||||
// TODO(henrika): verify support for exclusive mode as well?
|
||||
_com_error error = client->IsFormatSupported(
|
||||
share_mode, reinterpret_cast<const WAVEFORMATEX*>(format),
|
||||
reinterpret_cast<WAVEFORMATEX**>(&closest_match));
|
||||
&closest_match);
|
||||
RTC_LOG(INFO) << WaveFormatToString(
|
||||
const_cast<WAVEFORMATEXTENSIBLE*>(format));
|
||||
if ((error.Error() == S_OK) && (closest_match == nullptr)) {
|
||||
RTC_DLOG(INFO)
|
||||
<< "The audio endpoint device supports the specified stream format";
|
||||
@ -943,7 +973,7 @@ bool IsFormatSupported(IAudioClient* client,
|
||||
// only be triggered for shared mode.
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "Exact format is not supported, but a closest match exists";
|
||||
RTC_LOG(INFO) << WaveFormatExToString(closest_match);
|
||||
RTC_LOG(INFO) << WaveFormatToString(closest_match.Get());
|
||||
} else if ((error.Error() == AUDCLNT_E_UNSUPPORTED_FORMAT) &&
|
||||
(closest_match == nullptr)) {
|
||||
// The audio engine does not support the caller-specified format or any
|
||||
@ -1381,31 +1411,34 @@ bool FillRenderEndpointBufferWithSilence(IAudioClient* client,
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string WaveFormatExToString(const WAVEFORMATEXTENSIBLE* format) {
|
||||
RTC_DCHECK_EQ(format->Format.wFormatTag, WAVE_FORMAT_EXTENSIBLE);
|
||||
std::string WaveFormatToString(const WaveFormatWrapper format) {
|
||||
char ss_buf[1024];
|
||||
rtc::SimpleStringBuilder ss(ss_buf);
|
||||
ss.AppendFormat("wFormatTag: WAVE_FORMAT_EXTENSIBLE");
|
||||
ss.AppendFormat(", nChannels: %d", format->Format.nChannels);
|
||||
ss.AppendFormat(", nSamplesPerSec: %d", format->Format.nSamplesPerSec);
|
||||
ss.AppendFormat(", nAvgBytesPerSec: %d", format->Format.nAvgBytesPerSec);
|
||||
ss.AppendFormat(", nBlockAlign: %d", format->Format.nBlockAlign);
|
||||
ss.AppendFormat(", wBitsPerSample: %d", format->Format.wBitsPerSample);
|
||||
ss.AppendFormat(", cbSize: %d", format->Format.cbSize);
|
||||
ss.AppendFormat(", wValidBitsPerSample: %d",
|
||||
format->Samples.wValidBitsPerSample);
|
||||
ss.AppendFormat(", dwChannelMask: 0x%X", format->dwChannelMask);
|
||||
if (format->SubFormat == KSDATAFORMAT_SUBTYPE_PCM) {
|
||||
ss << ", SubFormat: KSDATAFORMAT_SUBTYPE_PCM";
|
||||
} else if (format->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) {
|
||||
ss << ", SubFormat: KSDATAFORMAT_SUBTYPE_IEEE_FLOAT";
|
||||
// Start with the WAVEFORMATEX part (which always exists).
|
||||
ss.AppendFormat("wFormatTag: %s (0x%X)",
|
||||
WaveFormatTagToString(format->wFormatTag),
|
||||
format->wFormatTag);
|
||||
ss.AppendFormat(", nChannels: %d", format->nChannels);
|
||||
ss.AppendFormat(", nSamplesPerSec: %d", format->nSamplesPerSec);
|
||||
ss.AppendFormat(", nAvgBytesPerSec: %d", format->nAvgBytesPerSec);
|
||||
ss.AppendFormat(", nBlockAlign: %d", format->nBlockAlign);
|
||||
ss.AppendFormat(", wBitsPerSample: %d", format->wBitsPerSample);
|
||||
ss.AppendFormat(", cbSize: %d", format->cbSize);
|
||||
if (!format.IsExtensible())
|
||||
return ss.str();
|
||||
|
||||
// Append the WAVEFORMATEXTENSIBLE part (which we know exists).
|
||||
ss.AppendFormat(
|
||||
" [+] wValidBitsPerSample: %d, dwChannelMask: %s",
|
||||
format.GetExtensible()->Samples.wValidBitsPerSample,
|
||||
ChannelMaskToString(format.GetExtensible()->dwChannelMask).c_str());
|
||||
if (format.IsPcm()) {
|
||||
ss.AppendFormat("%s", ", SubFormat: KSDATAFORMAT_SUBTYPE_PCM");
|
||||
} else if (format.IsFloat()) {
|
||||
ss.AppendFormat("%s", ", SubFormat: KSDATAFORMAT_SUBTYPE_IEEE_FLOAT");
|
||||
} else {
|
||||
ss << ", SubFormat: NOT_SUPPORTED";
|
||||
ss.AppendFormat("%s", ", SubFormat: NOT_SUPPORTED");
|
||||
}
|
||||
ss.AppendFormat("\nChannel configuration: %s",
|
||||
ChannelMaskToString(format->dwChannelMask).c_str());
|
||||
ss.AppendFormat("\nDirectSound configuration : %s",
|
||||
DirectSoundConfigToString(format->dwChannelMask));
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
@ -327,6 +327,32 @@ class ScopedHandle {
|
||||
// These methods are based on media::CoreAudioUtil in Chrome.
|
||||
namespace core_audio_utility {
|
||||
|
||||
// Helper class which automates casting between WAVEFORMATEX and
|
||||
// WAVEFORMATEXTENSIBLE raw pointers using implicit constructors and
|
||||
// operator overloading. Note that, no memory is allocated by this utility
|
||||
// structure. It only serves as a handle (or a wrapper) of the structure
|
||||
// provided to it at construction.
|
||||
class WaveFormatWrapper {
|
||||
public:
|
||||
WaveFormatWrapper(WAVEFORMATEXTENSIBLE* p)
|
||||
: ptr_(reinterpret_cast<WAVEFORMATEX*>(p)) {}
|
||||
WaveFormatWrapper(WAVEFORMATEX* p) : ptr_(p) {}
|
||||
~WaveFormatWrapper() = default;
|
||||
|
||||
operator WAVEFORMATEX*() const { return ptr_; }
|
||||
WAVEFORMATEX* operator->() const { return ptr_; }
|
||||
WAVEFORMATEX* get() const { return ptr_; }
|
||||
WAVEFORMATEXTENSIBLE* GetExtensible() const;
|
||||
|
||||
bool IsExtensible() const;
|
||||
bool IsPcm() const;
|
||||
bool IsFloat() const;
|
||||
size_t size() const;
|
||||
|
||||
private:
|
||||
WAVEFORMATEX* ptr_;
|
||||
};
|
||||
|
||||
// Returns true if Windows Core Audio is supported.
|
||||
// Always verify that this method returns true before using any of the
|
||||
// other methods in this class.
|
||||
@ -576,8 +602,10 @@ Microsoft::WRL::ComPtr<ISimpleAudioVolume> CreateSimpleAudioVolume(
|
||||
// given by |render_client|.
|
||||
bool FillRenderEndpointBufferWithSilence(IAudioClient* client,
|
||||
IAudioRenderClient* render_client);
|
||||
// Transforms a WAVEFORMATEXTENSIBLE struct to a human-readable string.
|
||||
std::string WaveFormatExToString(const WAVEFORMATEXTENSIBLE* format);
|
||||
|
||||
// Prints/logs all fields of the format structure in |format|.
|
||||
// Also supports extended versions (WAVEFORMATEXTENSIBLE).
|
||||
std::string WaveFormatToString(const WaveFormatWrapper format);
|
||||
|
||||
// Converts Windows internal REFERENCE_TIME (100 nanosecond units) into
|
||||
// generic webrtc::TimeDelta which then can be converted to any time unit.
|
||||
|
@ -81,6 +81,68 @@ class CoreAudioUtilityWinTest : public ::testing::Test {
|
||||
ScopedCOMInitializer com_init_;
|
||||
};
|
||||
|
||||
TEST_F(CoreAudioUtilityWinTest, WaveFormatWrapper) {
|
||||
// Use default constructor for WAVEFORMATEX and verify its size.
|
||||
WAVEFORMATEX format = {};
|
||||
core_audio_utility::WaveFormatWrapper wave_format(&format);
|
||||
EXPECT_FALSE(wave_format.IsExtensible());
|
||||
EXPECT_EQ(wave_format.size(), sizeof(WAVEFORMATEX));
|
||||
EXPECT_EQ(wave_format->cbSize, 0);
|
||||
|
||||
// Ensure that the stand-alone WAVEFORMATEX structure has a valid format tag
|
||||
// and that all accessors work.
|
||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
EXPECT_FALSE(wave_format.IsExtensible());
|
||||
EXPECT_EQ(wave_format.size(), sizeof(WAVEFORMATEX));
|
||||
EXPECT_EQ(wave_format.get()->wFormatTag, WAVE_FORMAT_PCM);
|
||||
EXPECT_EQ(wave_format->wFormatTag, WAVE_FORMAT_PCM);
|
||||
|
||||
// Next, ensure that the size is valid. Stand-alone is not extended.
|
||||
EXPECT_EQ(wave_format.size(), sizeof(WAVEFORMATEX));
|
||||
|
||||
// Verify format types for the stand-alone version.
|
||||
EXPECT_TRUE(wave_format.IsPcm());
|
||||
EXPECT_FALSE(wave_format.IsFloat());
|
||||
format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
EXPECT_TRUE(wave_format.IsFloat());
|
||||
}
|
||||
|
||||
TEST_F(CoreAudioUtilityWinTest, WaveFormatWrapperExtended) {
|
||||
// Use default constructor for WAVEFORMATEXTENSIBLE and verify that it
|
||||
// results in same size as for WAVEFORMATEX even if the size of |format_ex|
|
||||
// equals the size of WAVEFORMATEXTENSIBLE.
|
||||
WAVEFORMATEXTENSIBLE format_ex = {};
|
||||
core_audio_utility::WaveFormatWrapper wave_format_ex(&format_ex);
|
||||
EXPECT_FALSE(wave_format_ex.IsExtensible());
|
||||
EXPECT_EQ(wave_format_ex.size(), sizeof(WAVEFORMATEX));
|
||||
EXPECT_EQ(wave_format_ex->cbSize, 0);
|
||||
|
||||
// Ensure that the extended structure has a valid format tag and that all
|
||||
// accessors work.
|
||||
format_ex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
EXPECT_FALSE(wave_format_ex.IsExtensible());
|
||||
EXPECT_EQ(wave_format_ex.size(), sizeof(WAVEFORMATEX));
|
||||
EXPECT_EQ(wave_format_ex->wFormatTag, WAVE_FORMAT_EXTENSIBLE);
|
||||
EXPECT_EQ(wave_format_ex.get()->wFormatTag, WAVE_FORMAT_EXTENSIBLE);
|
||||
|
||||
// Next, ensure that the size is valid (sum of stand-alone and extended).
|
||||
// Now the structure qualifies as extended.
|
||||
format_ex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
EXPECT_TRUE(wave_format_ex.IsExtensible());
|
||||
EXPECT_EQ(wave_format_ex.size(), sizeof(WAVEFORMATEXTENSIBLE));
|
||||
EXPECT_TRUE(wave_format_ex.GetExtensible());
|
||||
EXPECT_EQ(wave_format_ex.GetExtensible()->Format.wFormatTag,
|
||||
WAVE_FORMAT_EXTENSIBLE);
|
||||
|
||||
// Verify format types for the extended version.
|
||||
EXPECT_FALSE(wave_format_ex.IsPcm());
|
||||
format_ex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
EXPECT_TRUE(wave_format_ex.IsPcm());
|
||||
EXPECT_FALSE(wave_format_ex.IsFloat());
|
||||
format_ex.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
EXPECT_TRUE(wave_format_ex.IsFloat());
|
||||
}
|
||||
|
||||
TEST_F(CoreAudioUtilityWinTest, NumberOfActiveDevices) {
|
||||
ABORT_TEST_IF_NOT(DevicesAvailable());
|
||||
int render_devices = core_audio_utility::NumberOfActiveDevices(eRender);
|
||||
@ -438,14 +500,20 @@ TEST_F(CoreAudioUtilityWinTest, GetSharedModeMixFormat) {
|
||||
EXPECT_TRUE(client.Get());
|
||||
|
||||
// Perform a simple sanity test of the acquired format structure.
|
||||
WAVEFORMATPCMEX format;
|
||||
WAVEFORMATEXTENSIBLE format;
|
||||
EXPECT_TRUE(SUCCEEDED(
|
||||
core_audio_utility::GetSharedModeMixFormat(client.Get(), &format)));
|
||||
EXPECT_GE(format.Format.nChannels, 1);
|
||||
EXPECT_GE(format.Format.nSamplesPerSec, 8000u);
|
||||
EXPECT_GE(format.Format.wBitsPerSample, 16);
|
||||
EXPECT_GE(format.Samples.wValidBitsPerSample, 16);
|
||||
EXPECT_EQ(format.Format.wFormatTag, WAVE_FORMAT_EXTENSIBLE);
|
||||
core_audio_utility::WaveFormatWrapper wformat(&format);
|
||||
EXPECT_GE(wformat->nChannels, 1);
|
||||
EXPECT_GE(wformat->nSamplesPerSec, 8000u);
|
||||
EXPECT_GE(wformat->wBitsPerSample, 16);
|
||||
if (wformat.IsExtensible()) {
|
||||
EXPECT_EQ(wformat->wFormatTag, WAVE_FORMAT_EXTENSIBLE);
|
||||
EXPECT_GE(wformat->cbSize, 22);
|
||||
EXPECT_GE(wformat.GetExtensible()->Samples.wValidBitsPerSample, 16);
|
||||
} else {
|
||||
EXPECT_EQ(wformat->cbSize, 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CoreAudioUtilityWinTest, IsFormatSupported) {
|
||||
|
Reference in New Issue
Block a user