Bug 1213414 - Implement channelCount audio constraint. r=jib,padenot

MozReview-Commit-ID: K95iBYOE1nR

--HG--
extra : rebase_source : 5c73eea4902933faec75e37d90e42a2f38c51b81
This commit is contained in:
Alex Chronopoulos 2017-06-29 21:01:17 -07:00
parent d39881db34
commit 41737dc1af
12 changed files with 138 additions and 24 deletions

View File

@ -675,10 +675,9 @@ AudioCallbackDriver::Init()
#ifdef MOZ_WEBRTC
if (mGraphImpl->mInputWanted) {
StaticMutexAutoLock lock(AudioInputCubeb::Mutex());
uint32_t maxInputChannels = 0;
if (AudioInputCubeb::GetDeviceMaxChannels(mGraphImpl->mInputDeviceID, maxInputChannels) == 0) {
input.channels = mInputChannels = maxInputChannels;
}
uint32_t userChannels = 0;
AudioInputCubeb::GetUserChannelCount(mGraphImpl->mInputDeviceID, userChannels);
input.channels = mInputChannels = userChannels;
}
#endif

View File

@ -1815,6 +1815,7 @@ MediaManager::MediaManager()
#endif
mPrefs.mPlayoutDelay = 0;
mPrefs.mFullDuplex = false;
mPrefs.mChannels = 0; // max channels default
nsresult rv;
nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
if (NS_SUCCEEDED(rv)) {
@ -1825,12 +1826,14 @@ MediaManager::MediaManager()
}
LOG(("%s: default prefs: %dx%d @%dfps (min %d), %dHz test tones, aec: %s,"
"agc: %s, noise: %s, aec level: %d, agc level: %d, noise level: %d,"
"playout delay: %d, %sfull_duplex, extended aec %s, delay_agnostic %s",
"playout delay: %d, %sfull_duplex, extended aec %s, delay_agnostic %s "
"channels %d",
__FUNCTION__, mPrefs.mWidth, mPrefs.mHeight,
mPrefs.mFPS, mPrefs.mMinFPS, mPrefs.mFreq, mPrefs.mAecOn ? "on" : "off",
mPrefs.mAgcOn ? "on": "off", mPrefs.mNoiseOn ? "on": "off", mPrefs.mAec,
mPrefs.mAgc, mPrefs.mNoise, mPrefs.mPlayoutDelay, mPrefs.mFullDuplex ? "" : "not ",
mPrefs.mExtendedFilter ? "on" : "off", mPrefs.mDelayAgnostic ? "on" : "off"));
mPrefs.mExtendedFilter ? "on" : "off", mPrefs.mDelayAgnostic ? "on" : "off",
mPrefs.mChannels));
}
NS_IMPL_ISUPPORTS(MediaManager, nsIMediaManagerService, nsIObserver)
@ -1904,6 +1907,7 @@ MediaManager::Get() {
prefs->AddObserver("media.getusermedia.noise", sSingleton, false);
prefs->AddObserver("media.getusermedia.playout_delay", sSingleton, false);
prefs->AddObserver("media.ondevicechange.fakeDeviceChangeEvent.enabled", sSingleton, false);
prefs->AddObserver("media.getusermedia.channels", sSingleton, false);
#endif
}
@ -2937,6 +2941,7 @@ MediaManager::GetPrefs(nsIPrefBranch *aBranch, const char *aData)
GetPref(aBranch, "media.getusermedia.playout_delay", aData, &mPrefs.mPlayoutDelay);
GetPrefBool(aBranch, "media.getusermedia.aec_extended_filter", aData, &mPrefs.mExtendedFilter);
GetPrefBool(aBranch, "media.getusermedia.aec_aec_delay_agnostic", aData, &mPrefs.mDelayAgnostic);
GetPref(aBranch, "media.getusermedia.channels", aData, &mPrefs.mChannels);
GetPrefBool(aBranch, "media.ondevicechange.fakeDeviceChangeEvent.enabled", aData, &mPrefs.mFakeDeviceChangeEventOn);
#endif
GetPrefBool(aBranch, "media.navigator.audio.full_duplex", aData, &mPrefs.mFullDuplex);
@ -2975,6 +2980,7 @@ MediaManager::Shutdown()
prefs->RemoveObserver("media.getusermedia.noise", this);
prefs->RemoveObserver("media.getusermedia.playout_delay", this);
prefs->RemoveObserver("media.ondevicechange.fakeDeviceChangeEvent.enabled", this);
prefs->RemoveObserver("media.getusermedia.channels", this);
#endif
prefs->RemoveObserver("media.navigator.audio.full_duplex", this);
}

View File

@ -3181,6 +3181,22 @@ SourceMediaStream::HasPendingAudioTrack()
return audioTrackPresent;
}
bool
SourceMediaStream::OpenNewAudioCallbackDriver(AudioDataListener * aListener)
{
MOZ_ASSERT(GraphImpl()->mLifecycleState ==
MediaStreamGraphImpl::LifecycleState::LIFECYCLE_RUNNING);
AudioCallbackDriver* nextDriver = new AudioCallbackDriver(GraphImpl());
nextDriver->SetInputListener(aListener);
{
MonitorAutoLock lock(GraphImpl()->GetMonitor());
GraphImpl()->CurrentDriver()->SwitchAtNextIteration(nextDriver);
}
return true;
}
void
MediaInputPort::Init()
{

View File

@ -808,6 +808,8 @@ public:
return mStreamTracksStartTimeStamp;
}
bool OpenNewAudioCallbackDriver(AudioDataListener *aListener);
// XXX need a Reset API
friend class MediaStreamGraphImpl;

View File

@ -74,7 +74,7 @@ var tests = [
var mustSupport = [
'width', 'height', 'frameRate', 'facingMode', 'deviceId',
'echoCancellation', 'noiseSuppression', 'autoGainControl',
'echoCancellation', 'noiseSuppression', 'autoGainControl', 'channelCount',
// Yet to add:
// 'aspectRatio', 'volume', 'sampleRate', 'sampleSize', 'latency', 'groupId'

View File

@ -105,6 +105,7 @@ public:
, mExtendedFilter(false)
, mDelayAgnostic(false)
, mFakeDeviceChangeEventOn(false)
, mChannels(0)
{}
int32_t mWidth;
@ -123,6 +124,7 @@ public:
bool mExtendedFilter;
bool mDelayAgnostic;
bool mFakeDeviceChangeEventOn;
int32_t mChannels;
// mWidth and/or mHeight may be zero (=adaptive default), so use functions.

View File

@ -41,6 +41,7 @@ nsTArray<nsCString>* AudioInputCubeb::mDeviceNames;
cubeb_device_collection AudioInputCubeb::mDevices = { nullptr, 0 };
bool AudioInputCubeb::mAnyInUse = false;
StaticMutex AudioInputCubeb::sMutex;
uint32_t AudioInputCubeb::sUserChannelCount = 0;
// AudioDeviceID is an annoying opaque value that's really a string
// pointer, and is freed when the cubeb_device_collection is destroyed

View File

@ -152,10 +152,12 @@ public:
virtual int GetRecordingDeviceName(int aIndex, char (&aStrNameUTF8)[128],
char aStrGuidUTF8[128]) = 0;
virtual int GetRecordingDeviceStatus(bool& aIsAvailable) = 0;
virtual int GetChannelCount(int aDeviceIndex, uint32_t& aChannels) = 0;
virtual void GetChannelCount(uint32_t& aChannels) = 0;
virtual int GetMaxAvailableChannels(uint32_t& aChannels) = 0;
virtual void StartRecording(SourceMediaStream *aStream, AudioDataListener *aListener) = 0;
virtual void StopRecording(SourceMediaStream *aStream) = 0;
virtual int SetRecordingDevice(int aIndex) = 0;
virtual void SetUserChannelCount(uint32_t aChannels) = 0;
protected:
// Protected destructor, to discourage deletion outside of Release():
@ -264,9 +266,19 @@ public:
return 0;
}
int GetChannelCount(int aDeviceIndex, uint32_t& aChannels)
void GetChannelCount(uint32_t& aChannels)
{
return GetDeviceMaxChannels(aDeviceIndex, aChannels);
GetUserChannelCount(mSelectedDevice, aChannels);
}
static void GetUserChannelCount(int aDeviceIndex, uint32_t& aChannels)
{
aChannels = sUserChannelCount;
}
int GetMaxAvailableChannels(uint32_t& aChannels)
{
return GetDeviceMaxChannels(mSelectedDevice, aChannels);
}
static int GetDeviceMaxChannels(int aDeviceIndex, uint32_t& aChannels)
@ -283,6 +295,18 @@ public:
return 0;
}
void SetUserChannelCount(uint32_t aChannels)
{
if (GetDeviceMaxChannels(mSelectedDevice, sUserChannelCount)) {
sUserChannelCount = 1; // error capture mono
return;
}
if (aChannels && aChannels < sUserChannelCount) {
sUserChannelCount = aChannels;
}
}
void StartRecording(SourceMediaStream *aStream, AudioDataListener *aListener)
{
#ifdef MOZ_WIDGET_ANDROID
@ -344,6 +368,7 @@ private:
static cubeb_device_collection mDevices;
static bool mAnyInUse;
static StaticMutex sMutex;
static uint32_t sUserChannelCount;
};
class AudioInputWebRTC final : public AudioInput
@ -384,12 +409,20 @@ public:
return 0;
}
int GetChannelCount(int aDeviceIndex, uint32_t& aChannels)
void GetChannelCount(uint32_t& aChannels)
{
aChannels = 1; // default to mono
}
int GetMaxAvailableChannels(uint32_t& aChannels)
{
aChannels = 1;
return 0;
}
void SetUserChannelCount(uint32_t aChannels)
{}
void StartRecording(SourceMediaStream *aStream, AudioDataListener *aListener) {}
void StopRecording(SourceMediaStream *aStream) {}

View File

@ -217,6 +217,7 @@ MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource(
mSettings.mEchoCancellation.Construct(0);
mSettings.mAutoGainControl.Construct(0);
mSettings.mNoiseSuppression.Construct(0);
mSettings.mChannelCount.Construct(0);
// We'll init lazily as needed
}
@ -288,12 +289,31 @@ MediaEngineWebRTCMicrophoneSource::UpdateSingleSource(
prefs.mAecOn = c.mEchoCancellation.Get(prefs.mAecOn);
prefs.mAgcOn = c.mAutoGainControl.Get(prefs.mAgcOn);
prefs.mNoiseOn = c.mNoiseSuppression.Get(prefs.mNoiseOn);
uint32_t maxChannels = 1;
if (mAudioInput->GetMaxAvailableChannels(maxChannels) != 0) {
return NS_ERROR_FAILURE;
}
// Check channelCount violation
if (static_cast<int32_t>(maxChannels) < c.mChannelCount.mMin ||
static_cast<int32_t>(maxChannels) > c.mChannelCount.mMax) {
*aOutBadConstraint = "channelCount";
return NS_ERROR_FAILURE;
}
// Clamp channelCount to a valid value
if (prefs.mChannels <= 0) {
prefs.mChannels = static_cast<int32_t>(maxChannels);
}
prefs.mChannels = c.mChannelCount.Get(std::min(prefs.mChannels,
static_cast<int32_t>(maxChannels)));
// Clamp channelCount to a valid value
prefs.mChannels = std::max(1, std::min(prefs.mChannels, static_cast<int32_t>(maxChannels)));
LOG(("Audio config: aec: %d, agc: %d, noise: %d, delay: %d",
prefs.mAecOn ? prefs.mAec : -1,
prefs.mAgcOn ? prefs.mAgc : -1,
prefs.mNoiseOn ? prefs.mNoise : -1,
prefs.mPlayoutDelay));
LOG(("Audio config: aec: %d, agc: %d, noise: %d, delay: %d, channels: %d",
prefs.mAecOn ? prefs.mAec : -1,
prefs.mAgcOn ? prefs.mAgc : -1,
prefs.mNoiseOn ? prefs.mNoise : -1,
prefs.mPlayoutDelay,
prefs.mChannels));
mPlayoutDelay = prefs.mPlayoutDelay;
@ -310,21 +330,48 @@ MediaEngineWebRTCMicrophoneSource::UpdateSingleSource(
// (Bug 1238038) fail allocation for a second device
return NS_ERROR_FAILURE;
}
if (mAudioInput->SetRecordingDevice(mCapIndex)) {
return NS_ERROR_FAILURE;
}
mAudioInput->SetUserChannelCount(prefs.mChannels);
if (!AllocChannel()) {
FreeChannel();
LOG(("Audio device is not initalized"));
return NS_ERROR_FAILURE;
}
if (mAudioInput->SetRecordingDevice(mCapIndex)) {
FreeChannel();
return NS_ERROR_FAILURE;
}
LOG(("Audio device %d allocated", mCapIndex));
{
// Update with the actual applied channelCount in order
// to store it in settings.
uint32_t channelCount = 0;
mAudioInput->GetChannelCount(channelCount);
MOZ_ASSERT(channelCount > 0);
prefs.mChannels = channelCount;
}
break;
case kStarted:
if (prefs == mLastPrefs) {
return NS_OK;
}
if (prefs.mChannels != mLastPrefs.mChannels) {
MOZ_ASSERT(mSources.Length() > 0);
auto& source = mSources.LastElement();
mAudioInput->SetUserChannelCount(prefs.mChannels);
// Get validated number of channel
uint32_t channelCount = 0;
mAudioInput->GetChannelCount(channelCount);
MOZ_ASSERT(channelCount > 0 && mLastPrefs.mChannels > 0);
// Check if new validated channels is the same as previous
if (static_cast<uint32_t>(mLastPrefs.mChannels) != channelCount
&& !source->OpenNewAudioCallbackDriver(mListener)) {
return NS_ERROR_FAILURE;
}
// Update settings
prefs.mChannels = channelCount;
}
if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) {
MonitorAutoLock lock(mMonitor);
if (mSources.IsEmpty()) {
@ -384,6 +431,7 @@ MediaEngineWebRTCMicrophoneSource::SetLastPrefs(
that->mSettings.mEchoCancellation.Value() = aPrefs.mAecOn;
that->mSettings.mAutoGainControl.Value() = aPrefs.mAgcOn;
that->mSettings.mNoiseSuppression.Value() = aPrefs.mNoiseOn;
that->mSettings.mChannelCount.Value() = aPrefs.mChannels;
return NS_OK;
}));
}
@ -796,9 +844,9 @@ MediaEngineWebRTCMicrophoneSource::AllocChannel()
webrtc::CodecInst codec;
strcpy(codec.plname, ENCODING);
codec.channels = CHANNELS;
uint32_t channels = 0;
if (mAudioInput->GetChannelCount(mCapIndex, channels) == 0) {
codec.channels = channels;
uint32_t maxChannels = 0;
if (mAudioInput->GetMaxAvailableChannels(maxChannels) == 0) {
codec.channels = maxChannels;
}
MOZ_ASSERT(mSampleFrequency == 16000 || mSampleFrequency == 32000);
codec.rate = SAMPLE_RATE(mSampleFrequency);

View File

@ -378,6 +378,9 @@ FlattenedConstraints::FlattenedConstraints(const NormalizedConstraints& aOther)
if (mAutoGainControl.Intersects(set.mAutoGainControl)) {
mAutoGainControl.Intersect(set.mAutoGainControl);
}
if (mChannelCount.Intersects(set.mChannelCount)) {
mChannelCount.Intersect(set.mChannelCount);
}
}
}

View File

@ -226,6 +226,7 @@ public:
StringRange mDeviceId;
LongRange mViewportOffsetX, mViewportOffsetY, mViewportWidth, mViewportHeight;
BooleanRange mEchoCancellation, mNoiseSuppression, mAutoGainControl;
LongRange mChannelCount;
private:
typedef NormalizedConstraintSet T;
public:
@ -258,7 +259,9 @@ public:
aOther.mNoiseSuppression,
advanced, aList)
, mAutoGainControl(&T::mAutoGainControl, "autoGainControl",
aOther.mAutoGainControl, advanced, aList) {}
aOther.mAutoGainControl, advanced, aList)
, mChannelCount(&T::mChannelCount, "channelCount",
aOther.mChannelCount, advanced, aList) {}
};
template<> bool NormalizedConstraintSet::Range<bool>::Merge(const Range& aOther);

View File

@ -488,6 +488,7 @@ pref("media.peerconnection.video.h264_enabled", false);
pref("media.peerconnection.video.vp9_enabled", true);
pref("media.getusermedia.aec", 1);
pref("media.getusermedia.browser.enabled", false);
pref("media.getusermedia.channels", 0);
// Desktop is typically VGA capture or more; and qm_select will not drop resolution
// below 1/2 in each dimension (or so), so QVGA (320x200) is the lowest here usually.
pref("media.peerconnection.video.min_bitrate", 0);