mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 22:00:47 +01:00

* FrameCombiner is simpler. No additional channel pointers for buffers. * Improve consistency in using views in downstream classes. * Deprecate older methods (some have upstream dependencies). * Use samples per channel instead of sample rate where the former is really what's needed. Bug: chromium:335805780 Change-Id: I0dde8ed7a5a187bbddd18d3b6c649aa0865e6d4a Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/352582 Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org> Reviewed-by: Sam Zackrisson <saza@webrtc.org> Cr-Commit-Position: refs/heads/main@{#42575}
201 lines
7.6 KiB
C++
201 lines
7.6 KiB
C++
/*
|
|
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include "modules/audio_mixer/frame_combiner.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "api/array_view.h"
|
|
#include "api/audio/audio_processing.h"
|
|
#include "api/rtp_packet_info.h"
|
|
#include "api/rtp_packet_infos.h"
|
|
#include "common_audio/include/audio_util.h"
|
|
#include "modules/audio_mixer/audio_frame_manipulator.h"
|
|
#include "modules/audio_mixer/audio_mixer_impl.h"
|
|
#include "modules/audio_processing/include/audio_frame_view.h"
|
|
#include "modules/audio_processing/logging/apm_data_dumper.h"
|
|
#include "rtc_base/arraysize.h"
|
|
#include "rtc_base/checks.h"
|
|
#include "rtc_base/numerics/safe_conversions.h"
|
|
#include "system_wrappers/include/metrics.h"
|
|
|
|
namespace webrtc {
|
|
namespace {
|
|
|
|
void SetAudioFrameFields(rtc::ArrayView<const AudioFrame* const> mix_list,
|
|
size_t number_of_channels,
|
|
int sample_rate,
|
|
size_t number_of_streams,
|
|
AudioFrame* audio_frame_for_mixing) {
|
|
const size_t samples_per_channel =
|
|
SampleRateToDefaultChannelSize(sample_rate);
|
|
|
|
// TODO(minyue): Issue bugs.webrtc.org/3390.
|
|
// Audio frame timestamp. The 'timestamp_' field is set to dummy
|
|
// value '0', because it is only supported in the one channel case and
|
|
// is then updated in the helper functions.
|
|
audio_frame_for_mixing->UpdateFrame(
|
|
0, nullptr, samples_per_channel, sample_rate, AudioFrame::kUndefined,
|
|
AudioFrame::kVadUnknown, number_of_channels);
|
|
|
|
if (mix_list.empty()) {
|
|
audio_frame_for_mixing->elapsed_time_ms_ = -1;
|
|
} else {
|
|
audio_frame_for_mixing->timestamp_ = mix_list[0]->timestamp_;
|
|
audio_frame_for_mixing->elapsed_time_ms_ = mix_list[0]->elapsed_time_ms_;
|
|
audio_frame_for_mixing->ntp_time_ms_ = mix_list[0]->ntp_time_ms_;
|
|
std::vector<RtpPacketInfo> packet_infos;
|
|
for (const auto& frame : mix_list) {
|
|
audio_frame_for_mixing->timestamp_ =
|
|
std::min(audio_frame_for_mixing->timestamp_, frame->timestamp_);
|
|
audio_frame_for_mixing->ntp_time_ms_ =
|
|
std::min(audio_frame_for_mixing->ntp_time_ms_, frame->ntp_time_ms_);
|
|
audio_frame_for_mixing->elapsed_time_ms_ = std::max(
|
|
audio_frame_for_mixing->elapsed_time_ms_, frame->elapsed_time_ms_);
|
|
packet_infos.insert(packet_infos.end(), frame->packet_infos_.begin(),
|
|
frame->packet_infos_.end());
|
|
}
|
|
audio_frame_for_mixing->packet_infos_ =
|
|
RtpPacketInfos(std::move(packet_infos));
|
|
}
|
|
}
|
|
|
|
void MixFewFramesWithNoLimiter(rtc::ArrayView<const AudioFrame* const> mix_list,
|
|
AudioFrame* audio_frame_for_mixing) {
|
|
if (mix_list.empty()) {
|
|
audio_frame_for_mixing->Mute();
|
|
return;
|
|
}
|
|
RTC_DCHECK_LE(mix_list.size(), 1);
|
|
InterleavedView<int16_t> dst = audio_frame_for_mixing->mutable_data(
|
|
mix_list[0]->samples_per_channel_, mix_list[0]->num_channels_);
|
|
CopySamples(dst, mix_list[0]->data_view());
|
|
}
|
|
|
|
void MixToFloatFrame(rtc::ArrayView<const AudioFrame* const> mix_list,
|
|
DeinterleavedView<float>& mixing_buffer) {
|
|
const size_t number_of_channels = NumChannels(mixing_buffer);
|
|
// Clear the mixing buffer.
|
|
rtc::ArrayView<float> raw_data = mixing_buffer.data();
|
|
ClearSamples(raw_data);
|
|
|
|
// Convert to FloatS16 and mix.
|
|
for (size_t i = 0; i < mix_list.size(); ++i) {
|
|
InterleavedView<const int16_t> frame_data = mix_list[i]->data_view();
|
|
RTC_CHECK(!frame_data.empty());
|
|
for (size_t j = 0; j < number_of_channels; ++j) {
|
|
MonoView<float> channel = mixing_buffer[j];
|
|
for (size_t k = 0; k < SamplesPerChannel(channel); ++k) {
|
|
channel[k] += frame_data[number_of_channels * k + j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void RunLimiter(DeinterleavedView<float> deinterleaved, Limiter* limiter) {
|
|
limiter->SetSamplesPerChannel(deinterleaved.samples_per_channel());
|
|
limiter->Process(deinterleaved);
|
|
}
|
|
|
|
// Both interleaves and rounds.
|
|
void InterleaveToAudioFrame(DeinterleavedView<float> deinterleaved,
|
|
AudioFrame* audio_frame_for_mixing) {
|
|
InterleavedView<int16_t> mixing_data = audio_frame_for_mixing->mutable_data(
|
|
deinterleaved.samples_per_channel(), deinterleaved.num_channels());
|
|
// Put data in the result frame.
|
|
for (size_t i = 0; i < mixing_data.num_channels(); ++i) {
|
|
auto channel = deinterleaved[i];
|
|
for (size_t j = 0; j < mixing_data.samples_per_channel(); ++j) {
|
|
mixing_data[mixing_data.num_channels() * j + i] =
|
|
FloatS16ToS16(channel[j]);
|
|
}
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
constexpr size_t FrameCombiner::kMaximumNumberOfChannels;
|
|
constexpr size_t FrameCombiner::kMaximumChannelSize;
|
|
|
|
FrameCombiner::FrameCombiner(bool use_limiter)
|
|
: data_dumper_(new ApmDataDumper(0)),
|
|
limiter_(data_dumper_.get(), kMaximumChannelSize, "AudioMixer"),
|
|
use_limiter_(use_limiter) {
|
|
static_assert(kMaximumChannelSize * kMaximumNumberOfChannels <=
|
|
AudioFrame::kMaxDataSizeSamples,
|
|
"");
|
|
}
|
|
|
|
FrameCombiner::~FrameCombiner() = default;
|
|
|
|
void FrameCombiner::Combine(rtc::ArrayView<AudioFrame* const> mix_list,
|
|
size_t number_of_channels,
|
|
int sample_rate,
|
|
size_t number_of_streams,
|
|
AudioFrame* audio_frame_for_mixing) {
|
|
RTC_DCHECK(audio_frame_for_mixing);
|
|
RTC_DCHECK_GT(sample_rate, 0);
|
|
|
|
// Note: `mix_list` is allowed to be empty.
|
|
// See FrameCombiner.CombiningZeroFramesShouldProduceSilence.
|
|
|
|
// Make sure to cap `number_of_channels` to the kMaximumNumberOfChannels
|
|
// limits since processing from hereon out will be bound by them.
|
|
number_of_channels = std::min(number_of_channels, kMaximumNumberOfChannels);
|
|
|
|
SetAudioFrameFields(mix_list, number_of_channels, sample_rate,
|
|
number_of_streams, audio_frame_for_mixing);
|
|
|
|
size_t samples_per_channel = SampleRateToDefaultChannelSize(sample_rate);
|
|
|
|
#if RTC_DCHECK_IS_ON
|
|
for (const auto* frame : mix_list) {
|
|
RTC_DCHECK_EQ(samples_per_channel, frame->samples_per_channel_);
|
|
RTC_DCHECK_EQ(sample_rate, frame->sample_rate_hz_);
|
|
}
|
|
#endif
|
|
|
|
// The 'num_channels_' field of frames in 'mix_list' could be
|
|
// different from 'number_of_channels'.
|
|
for (auto* frame : mix_list) {
|
|
RemixFrame(number_of_channels, frame);
|
|
}
|
|
|
|
if (number_of_streams <= 1) {
|
|
MixFewFramesWithNoLimiter(mix_list, audio_frame_for_mixing);
|
|
return;
|
|
}
|
|
|
|
// Make sure that the size of the view based on the desired
|
|
// `samples_per_channel` and `number_of_channels` doesn't exceed the size of
|
|
// the `mixing_buffer_` buffer.
|
|
RTC_DCHECK_LE(samples_per_channel, kMaximumChannelSize);
|
|
// Since the above check is a DCHECK only, clamp down on `samples_per_channel`
|
|
// to make sure we don't exceed the buffer size in non-dcheck builds.
|
|
// See also FrameCombinerDeathTest.DebugBuildCrashesWithHighRate.
|
|
samples_per_channel = std::min(samples_per_channel, kMaximumChannelSize);
|
|
DeinterleavedView<float> deinterleaved(
|
|
mixing_buffer_.data(), samples_per_channel, number_of_channels);
|
|
MixToFloatFrame(mix_list, deinterleaved);
|
|
|
|
if (use_limiter_) {
|
|
RunLimiter(deinterleaved, &limiter_);
|
|
}
|
|
|
|
InterleaveToAudioFrame(deinterleaved, audio_frame_for_mixing);
|
|
}
|
|
|
|
} // namespace webrtc
|