Index: webrtc/modules/audio_mixer/source/new_audio_conference_mixer_impl.cc |
diff --git a/webrtc/modules/audio_mixer/source/new_audio_conference_mixer_impl.cc b/webrtc/modules/audio_mixer/source/new_audio_conference_mixer_impl.cc |
index c1a49f221bcaf815158e1e58a5fc4fa66f1a89fb..47eac8fff56edb5653a766ef2e345f78de64c64a 100644 |
--- a/webrtc/modules/audio_mixer/source/new_audio_conference_mixer_impl.cc |
+++ b/webrtc/modules/audio_mixer/source/new_audio_conference_mixer_impl.cc |
@@ -11,6 +11,7 @@ |
#include "webrtc/modules/audio_mixer/source/new_audio_conference_mixer_impl.h" |
#include <algorithm> |
+#include <functional> |
#include "webrtc/modules/audio_conference_mixer/source/audio_frame_manipulator.h" |
#include "webrtc/modules/audio_mixer/include/audio_mixer_defines.h" |
@@ -22,15 +23,40 @@ |
namespace webrtc { |
namespace { |
-struct AudioSourceWithFrame { |
- AudioSourceWithFrame(MixerAudioSource* p, AudioFrame* a, bool m) |
- : audio_source(p), audio_frame(a), muted(m) {} |
- MixerAudioSource* audio_source; |
- AudioFrame* audio_frame; |
- bool muted; |
-}; |
+class SourceFrame { |
+ public: |
+ SourceFrame(MixerAudioSource* p, AudioFrame* a, bool m, bool was_mixed_before) |
+ : audio_source_(p), |
+ audio_frame_(a), |
+ muted_(m), |
+ was_mixed_before_(was_mixed_before) { |
+ if (!muted_) { |
+ energy_ = CalculateEnergy(*a); |
+ } |
+ } |
-typedef std::list<AudioSourceWithFrame*> AudioSourceWithFrameList; |
+ // a.shouldMixBefore(b) is used to select mixer participants. |
+ bool shouldMixBefore(const SourceFrame& other) const { |
+ if (muted_ != other.muted_) { |
+ return other.muted_; |
+ } |
+ |
+ auto our_activity = audio_frame_->vad_activity_; |
+ auto other_activity = other.audio_frame_->vad_activity_; |
+ |
+ if (our_activity != other_activity) { |
+ return our_activity == AudioFrame::kVadActive; |
+ } |
+ |
+ return energy_ > other.energy_; |
+ } |
+ |
+ MixerAudioSource* audio_source_; |
+ AudioFrame* audio_frame_; |
+ bool muted_; |
+ uint32_t energy_; |
+ bool was_mixed_before_; |
+}; |
// Mix |frame| into |mixed_frame|, with saturation protection and upmixing. |
// These effects are applied to |frame| itself prior to mixing. Assumes that |
@@ -167,7 +193,6 @@ void NewAudioConferenceMixerImpl::Mix(AudioFrame* audio_frame_for_mixing) { |
size_t remainingAudioSourcesAllowedToMix = kMaximumAmountOfMixedAudioSources; |
RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
AudioFrameList mixList; |
- AudioFrameList rampOutList; |
AudioFrameList additionalFramesList; |
std::map<int, MixerAudioSource*> mixedAudioSourcesMap; |
{ |
@@ -214,20 +239,17 @@ void NewAudioConferenceMixerImpl::Mix(AudioFrame* audio_frame_for_mixing) { |
} |
} |
- UpdateToMix(&mixList, &rampOutList, &mixedAudioSourcesMap, |
- &remainingAudioSourcesAllowedToMix); |
- |
+ mixList = UpdateToMix(remainingAudioSourcesAllowedToMix); |
+ remainingAudioSourcesAllowedToMix -= mixList.size(); |
GetAdditionalAudio(&additionalFramesList); |
- UpdateMixedStatus(mixedAudioSourcesMap); |
} |
// TODO(aleloi): it might be better to decide the number of channels |
// with an API instead of dynamically. |
// Find the max channels over all mixing lists. |
- const size_t num_mixed_channels = std::max( |
- MaxNumChannels(&mixList), std::max(MaxNumChannels(&additionalFramesList), |
- MaxNumChannels(&rampOutList))); |
+ const size_t num_mixed_channels = |
+ std::max(MaxNumChannels(&mixList), MaxNumChannels(&additionalFramesList)); |
audio_frame_for_mixing->UpdateFrame( |
-1, _timeStamp, NULL, 0, _outputFrequency, AudioFrame::kNormalSpeech, |
@@ -245,7 +267,6 @@ void NewAudioConferenceMixerImpl::Mix(AudioFrame* audio_frame_for_mixing) { |
{ |
CriticalSectionScoped cs(_crit.get()); |
MixAnonomouslyFromList(audio_frame_for_mixing, additionalFramesList); |
- MixAnonomouslyFromList(audio_frame_for_mixing, rampOutList); |
if (audio_frame_for_mixing->samples_per_channel_ == 0) { |
// Nothing was mixed, set the audio samples to silence. |
@@ -256,10 +277,6 @@ void NewAudioConferenceMixerImpl::Mix(AudioFrame* audio_frame_for_mixing) { |
LimitMixedAudio(audio_frame_for_mixing); |
} |
} |
- |
- ClearAudioFrameList(&mixList); |
- ClearAudioFrameList(&rampOutList); |
- ClearAudioFrameList(&additionalFramesList); |
return; |
} |
@@ -426,177 +443,62 @@ int32_t NewAudioConferenceMixerImpl::GetLowestMixingFrequencyFromList( |
return highestFreq; |
} |
-void NewAudioConferenceMixerImpl::UpdateToMix( |
- AudioFrameList* mixList, |
- AudioFrameList* rampOutList, |
- std::map<int, MixerAudioSource*>* mixAudioSourceList, |
- size_t* maxAudioFrameCounter) const { |
- WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, _id, |
- "UpdateToMix(mixList,rampOutList,mixAudioSourceList,%d)", |
- *maxAudioFrameCounter); |
- const size_t mixListStartSize = mixList->size(); |
- AudioFrameList activeList; |
- // Struct needed by the passive lists to keep track of which AudioFrame |
- // belongs to which MixerAudioSource. |
- AudioSourceWithFrameList passiveWasNotMixedList; |
- AudioSourceWithFrameList passiveWasMixedList; |
- for (MixerAudioSourceList::const_iterator audio_source = |
- audio_source_list_.begin(); |
- audio_source != audio_source_list_.end(); ++audio_source) { |
- // Stop keeping track of passive audioSources if there are already |
- // enough audio sources available (they wont be mixed anyway). |
- bool mustAddToPassiveList = |
- (*maxAudioFrameCounter > |
- (activeList.size() + passiveWasMixedList.size() + |
- passiveWasNotMixedList.size())); |
- |
- bool wasMixed = false; |
- wasMixed = (*audio_source)->_mixHistory->WasMixed(); |
+AudioFrameList NewAudioConferenceMixerImpl::UpdateToMix( |
+ size_t maxAudioFrameCounter) const { |
+ AudioFrameList result; |
+ std::vector<SourceFrame> audioSourceMixingDataList; |
- auto audio_frame_with_info = |
- (*audio_source)->GetAudioFrameWithMuted(_id, _outputFrequency); |
- auto ret = audio_frame_with_info.audio_frame_info; |
- AudioFrame* audio_frame = audio_frame_with_info.audio_frame; |
- if (ret == MixerAudioSource::AudioFrameInfo::kError) { |
- continue; |
- } |
- const bool muted = (ret == MixerAudioSource::AudioFrameInfo::kMuted); |
- if (audio_source_list_.size() != 1) { |
- // TODO(wu): Issue 3390, add support for multiple audio sources case. |
- audio_frame->ntp_time_ms_ = -1; |
- } |
+ // Get audio source audio and put it in the struct vector. |
+ for (MixerAudioSource* audio_source : audio_source_list_) { |
+ auto audio_frame_with_info = audio_source->GetAudioFrameWithMuted( |
+ _id, static_cast<int>(_outputFrequency)); |
- // TODO(aleloi): this assert triggers in some test cases where SRTP is |
- // used which prevents NetEQ from making a VAD. Temporarily disable this |
- // assert until the problem is fixed on a higher level. |
- // RTC_DCHECK_NE(audio_frame->vad_activity_, AudioFrame::kVadUnknown); |
- if (audio_frame->vad_activity_ == AudioFrame::kVadUnknown) { |
+ auto audio_frame_info = audio_frame_with_info.audio_frame_info; |
+ AudioFrame* audio_source_audio_frame = audio_frame_with_info.audio_frame; |
+ |
+ if (audio_frame_info == MixerAudioSource::AudioFrameInfo::kError) { |
WEBRTC_TRACE(kTraceWarning, kTraceAudioMixerServer, _id, |
- "invalid VAD state from audio source"); |
+ "failed to GetAudioFrameWithMuted() from participant"); |
+ continue; |
} |
+ audioSourceMixingDataList.emplace_back( |
+ audio_source, audio_source_audio_frame, |
+ audio_frame_info == MixerAudioSource::AudioFrameInfo::kMuted, |
+ audio_source->_mixHistory->WasMixed()); |
+ } |
- if (audio_frame->vad_activity_ == AudioFrame::kVadActive) { |
- if (!wasMixed && !muted) { |
- RampIn(*audio_frame); |
- } |
+ // Sort frames by sorting function. |
+ std::sort(audioSourceMixingDataList.begin(), audioSourceMixingDataList.end(), |
+ std::mem_fn(&SourceFrame::shouldMixBefore)); |
- if (activeList.size() >= *maxAudioFrameCounter) { |
- // There are already more active audio sources than should be |
- // mixed. Only keep the ones with the highest energy. |
- AudioFrameList::iterator replaceItem; |
- uint32_t lowestEnergy = muted ? 0 : CalculateEnergy(*audio_frame); |
- |
- bool found_replace_item = false; |
- for (AudioFrameList::iterator iter = activeList.begin(); |
- iter != activeList.end(); ++iter) { |
- const uint32_t energy = muted ? 0 : CalculateEnergy(*iter->frame); |
- if (energy < lowestEnergy) { |
- replaceItem = iter; |
- lowestEnergy = energy; |
- found_replace_item = true; |
- } |
- } |
- if (found_replace_item) { |
- RTC_DCHECK(!muted); // Cannot replace with a muted frame. |
- FrameAndMuteInfo replaceFrame = *replaceItem; |
- |
- bool replaceWasMixed = false; |
- std::map<int, MixerAudioSource*>::const_iterator it = |
- mixAudioSourceList->find(replaceFrame.frame->id_); |
- |
- // When a frame is pushed to |activeList| it is also pushed |
- // to mixAudioSourceList with the frame's id. This means |
- // that the Find call above should never fail. |
- RTC_DCHECK(it != mixAudioSourceList->end()); |
- replaceWasMixed = it->second->_mixHistory->WasMixed(); |
- |
- mixAudioSourceList->erase(replaceFrame.frame->id_); |
- activeList.erase(replaceItem); |
- |
- activeList.push_front(FrameAndMuteInfo(audio_frame, muted)); |
- (*mixAudioSourceList)[audio_frame->id_] = *audio_source; |
- RTC_DCHECK_LE(mixAudioSourceList->size(), |
- static_cast<size_t>(kMaximumAmountOfMixedAudioSources)); |
- |
- if (replaceWasMixed) { |
- if (!replaceFrame.muted) { |
- RampOut(*replaceFrame.frame); |
- } |
- rampOutList->push_back(replaceFrame); |
- RTC_DCHECK_LE( |
- rampOutList->size(), |
- static_cast<size_t>(kMaximumAmountOfMixedAudioSources)); |
- } |
- } else { |
- if (wasMixed) { |
- if (!muted) { |
- RampOut(*audio_frame); |
- } |
- rampOutList->push_back(FrameAndMuteInfo(audio_frame, muted)); |
- RTC_DCHECK_LE( |
- rampOutList->size(), |
- static_cast<size_t>(kMaximumAmountOfMixedAudioSources)); |
- } |
- } |
- } else { |
- activeList.push_front(FrameAndMuteInfo(audio_frame, muted)); |
- (*mixAudioSourceList)[audio_frame->id_] = *audio_source; |
- RTC_DCHECK_LE(mixAudioSourceList->size(), |
- static_cast<size_t>(kMaximumAmountOfMixedAudioSources)); |
- } |
- } else { |
- if (wasMixed) { |
- AudioSourceWithFrame* part_struct = |
- new AudioSourceWithFrame(*audio_source, audio_frame, muted); |
- passiveWasMixedList.push_back(part_struct); |
- } else if (mustAddToPassiveList) { |
- if (!muted) { |
- RampIn(*audio_frame); |
- } |
- AudioSourceWithFrame* part_struct = |
- new AudioSourceWithFrame(*audio_source, audio_frame, muted); |
- passiveWasNotMixedList.push_back(part_struct); |
- } |
+ // Go through list in order and put things in mixList. |
+ for (SourceFrame& p : audioSourceMixingDataList) { |
+ // Filter muted. |
+ if (p.muted_) { |
+ p.audio_source_->_mixHistory->SetIsMixed(false); |
+ continue; |
} |
- } |
- RTC_DCHECK_LE(activeList.size(), *maxAudioFrameCounter); |
- // At this point it is known which audio sources should be mixed. Transfer |
- // this information to this functions output parameters. |
- for (AudioFrameList::const_iterator iter = activeList.begin(); |
- iter != activeList.end(); ++iter) { |
- mixList->push_back(*iter); |
- } |
- activeList.clear(); |
- // Always mix a constant number of AudioFrames. If there aren't enough |
- // active audio sources mix passive ones. Starting with those that was mixed |
- // last iteration. |
- for (AudioSourceWithFrameList::const_iterator iter = |
- passiveWasMixedList.begin(); |
- iter != passiveWasMixedList.end(); ++iter) { |
- if (mixList->size() < *maxAudioFrameCounter + mixListStartSize) { |
- mixList->push_back( |
- FrameAndMuteInfo((*iter)->audio_frame, (*iter)->muted)); |
- (*mixAudioSourceList)[(*iter)->audio_frame->id_] = (*iter)->audio_source; |
- RTC_DCHECK_LE(mixAudioSourceList->size(), |
- static_cast<size_t>(kMaximumAmountOfMixedAudioSources)); |
+ |
+ // Add frame to result vector for mixing. |
+ bool is_mixed = false; |
+ if (maxAudioFrameCounter > 0) { |
+ --maxAudioFrameCounter; |
+ if (!p.was_mixed_before_) { |
+ RampIn(*p.audio_frame_); |
+ } |
+ result.emplace_back(p.audio_frame_, false); |
+ is_mixed = true; |
} |
- delete *iter; |
- } |
- // And finally the ones that have not been mixed for a while. |
- for (AudioSourceWithFrameList::const_iterator iter = |
- passiveWasNotMixedList.begin(); |
- iter != passiveWasNotMixedList.end(); ++iter) { |
- if (mixList->size() < *maxAudioFrameCounter + mixListStartSize) { |
- mixList->push_back( |
- FrameAndMuteInfo((*iter)->audio_frame, (*iter)->muted)); |
- (*mixAudioSourceList)[(*iter)->audio_frame->id_] = (*iter)->audio_source; |
- RTC_DCHECK_LE(mixAudioSourceList->size(), |
- static_cast<size_t>(kMaximumAmountOfMixedAudioSources)); |
+ |
+ // Ramp out unmuted. |
+ if (p.was_mixed_before_ && !is_mixed) { |
+ RampOut(*p.audio_frame_); |
+ result.emplace_back(p.audio_frame_, false); |
} |
- delete *iter; |
+ |
+ p.audio_source_->_mixHistory->SetIsMixed(is_mixed); |
} |
- RTC_DCHECK_GE(*maxAudioFrameCounter + mixListStartSize, mixList->size()); |
- *maxAudioFrameCounter += mixListStartSize - mixList->size(); |
+ return result; |
} |
void NewAudioConferenceMixerImpl::GetAdditionalAudio( |
@@ -633,38 +535,6 @@ void NewAudioConferenceMixerImpl::GetAdditionalAudio( |
} |
} |
-void NewAudioConferenceMixerImpl::UpdateMixedStatus( |
- const std::map<int, MixerAudioSource*>& mixedAudioSourcesMap) const { |
- WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, _id, |
- "UpdateMixedStatus(mixedAudioSourcesMap)"); |
- RTC_DCHECK_LE(mixedAudioSourcesMap.size(), |
- static_cast<size_t>(kMaximumAmountOfMixedAudioSources)); |
- |
- // Loop through all audio_sources. If they are in the mix map they |
- // were mixed. |
- for (MixerAudioSourceList::const_iterator audio_source = |
- audio_source_list_.begin(); |
- audio_source != audio_source_list_.end(); ++audio_source) { |
- bool isMixed = false; |
- for (std::map<int, MixerAudioSource*>::const_iterator it = |
- mixedAudioSourcesMap.begin(); |
- it != mixedAudioSourcesMap.end(); ++it) { |
- if (it->second == *audio_source) { |
- isMixed = true; |
- break; |
- } |
- } |
- (*audio_source)->_mixHistory->SetIsMixed(isMixed); |
- } |
-} |
- |
-void NewAudioConferenceMixerImpl::ClearAudioFrameList( |
- AudioFrameList* audioFrameList) const { |
- WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, _id, |
- "ClearAudioFrameList(audioFrameList)"); |
- audioFrameList->clear(); |
-} |
- |
bool NewAudioConferenceMixerImpl::IsAudioSourceInList( |
const MixerAudioSource& audio_source, |
const MixerAudioSourceList& audioSourceList) const { |