Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,5 @@
alessiob@webrtc.org
gustaf@webrtc.org
henrik.lundin@webrtc.org
jakobi@webrtc.org
peah@webrtc.org

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2012 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 "audio/audio_level.h"
#include "api/audio/audio_frame.h"
#include "common_audio/signal_processing/include/signal_processing_library.h"
namespace webrtc {
namespace voe {
AudioLevel::AudioLevel()
: abs_max_(0), count_(0), current_level_full_range_(0) {}
AudioLevel::~AudioLevel() {}
void AudioLevel::Reset() {
MutexLock lock(&mutex_);
abs_max_ = 0;
count_ = 0;
current_level_full_range_ = 0;
total_energy_ = 0.0;
total_duration_ = 0.0;
}
int16_t AudioLevel::LevelFullRange() const {
MutexLock lock(&mutex_);
return current_level_full_range_;
}
void AudioLevel::ResetLevelFullRange() {
MutexLock lock(&mutex_);
abs_max_ = 0;
count_ = 0;
current_level_full_range_ = 0;
}
double AudioLevel::TotalEnergy() const {
MutexLock lock(&mutex_);
return total_energy_;
}
double AudioLevel::TotalDuration() const {
MutexLock lock(&mutex_);
return total_duration_;
}
void AudioLevel::ComputeLevel(const AudioFrame& audioFrame, double duration) {
// Check speech level (works for 2 channels as well)
int16_t abs_value =
audioFrame.muted()
? 0
: WebRtcSpl_MaxAbsValueW16(
audioFrame.data(),
audioFrame.samples_per_channel_ * audioFrame.num_channels_);
// Protect member access using a lock since this method is called on a
// dedicated audio thread in the RecordedDataIsAvailable() callback.
MutexLock lock(&mutex_);
if (abs_value > abs_max_)
abs_max_ = abs_value;
// Update level approximately 9 times per second, assuming audio frame
// duration is approximately 10 ms. (The update frequency is every
// 11th (= |kUpdateFrequency+1|) call: 1000/(11*10)=9.09..., we should
// probably change this behavior, see https://crbug.com/webrtc/10784).
if (count_++ == kUpdateFrequency) {
current_level_full_range_ = abs_max_;
count_ = 0;
// Decay the absolute maximum (divide by 4)
abs_max_ >>= 2;
}
// See the description for "totalAudioEnergy" in the WebRTC stats spec
// (https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-totalaudioenergy)
// for an explanation of these formulas. In short, we need a value that can
// be used to compute RMS audio levels over different time intervals, by
// taking the difference between the results from two getStats calls. To do
// this, the value needs to be of units "squared sample value * time".
double additional_energy =
static_cast<double>(current_level_full_range_) / INT16_MAX;
additional_energy *= additional_energy;
total_energy_ += additional_energy * duration;
total_duration_ += duration;
}
} // namespace voe
} // namespace webrtc

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2011 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.
*/
#ifndef AUDIO_AUDIO_LEVEL_H_
#define AUDIO_AUDIO_LEVEL_H_
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h"
namespace webrtc {
class AudioFrame;
namespace voe {
// This class is thread-safe. However, TotalEnergy() and TotalDuration() are
// related, so if you call ComputeLevel() on a different thread than you read
// these values, you still need to use lock to read them as a pair.
class AudioLevel {
public:
AudioLevel();
~AudioLevel();
void Reset();
// Returns the current audio level linearly [0,32767], which gets updated
// every "kUpdateFrequency+1" call to ComputeLevel() based on the maximum
// audio level of any audio frame, decaying by a factor of 1/4 each time
// LevelFullRange() gets updated.
// Called on "API thread(s)" from APIs like VoEBase::CreateChannel(),
// VoEBase::StopSend().
int16_t LevelFullRange() const;
void ResetLevelFullRange();
// See the description for "totalAudioEnergy" in the WebRTC stats spec
// (https://w3c.github.io/webrtc-stats/#dom-rtcaudiohandlerstats-totalaudioenergy)
// In our implementation, the total audio energy increases by the
// energy-equivalent of LevelFullRange() at the time of ComputeLevel(), rather
// than the energy of the samples in that specific audio frame. As a result,
// we may report a higher audio energy and audio level than the spec mandates.
// TODO(https://crbug.com/webrtc/10784): We should either do what the spec
// says or update the spec to match our implementation. If we want to have a
// decaying audio level we should probably update both the spec and the
// implementation to reduce the complexity of the definition. If we want to
// continue to have decaying audio we should have unittests covering the
// behavior of the decay.
double TotalEnergy() const;
double TotalDuration() const;
// Called on a native capture audio thread (platform dependent) from the
// AudioTransport::RecordedDataIsAvailable() callback.
// In Chrome, this method is called on the AudioInputDevice thread.
void ComputeLevel(const AudioFrame& audioFrame, double duration);
private:
enum { kUpdateFrequency = 10 };
mutable Mutex mutex_;
int16_t abs_max_ RTC_GUARDED_BY(mutex_);
int16_t count_ RTC_GUARDED_BY(mutex_);
int16_t current_level_full_range_ RTC_GUARDED_BY(mutex_);
double total_energy_ RTC_GUARDED_BY(mutex_) = 0.0;
double total_duration_ RTC_GUARDED_BY(mutex_) = 0.0;
};
} // namespace voe
} // namespace webrtc
#endif // AUDIO_AUDIO_LEVEL_H_

View file

@ -0,0 +1,475 @@
/*
* Copyright (c) 2015 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 "audio/audio_receive_stream.h"
#include <string>
#include <utility>
#include "absl/memory/memory.h"
#include "api/array_view.h"
#include "api/audio_codecs/audio_format.h"
#include "api/call/audio_sink.h"
#include "api/rtp_parameters.h"
#include "api/sequence_checker.h"
#include "audio/audio_send_stream.h"
#include "audio/audio_state.h"
#include "audio/channel_receive.h"
#include "audio/conversion.h"
#include "call/rtp_config.h"
#include "call/rtp_stream_receiver_controller_interface.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/time_utils.h"
namespace webrtc {
std::string AudioReceiveStreamInterface::Config::Rtp::ToString() const {
char ss_buf[1024];
rtc::SimpleStringBuilder ss(ss_buf);
ss << "{remote_ssrc: " << remote_ssrc;
ss << ", local_ssrc: " << local_ssrc;
ss << ", nack: " << nack.ToString();
ss << '}';
return ss.str();
}
std::string AudioReceiveStreamInterface::Config::ToString() const {
char ss_buf[1024];
rtc::SimpleStringBuilder ss(ss_buf);
ss << "{rtp: " << rtp.ToString();
ss << ", rtcp_send_transport: "
<< (rtcp_send_transport ? "(Transport)" : "null");
if (!sync_group.empty()) {
ss << ", sync_group: " << sync_group;
}
ss << '}';
return ss.str();
}
namespace {
std::unique_ptr<voe::ChannelReceiveInterface> CreateChannelReceive(
Clock* clock,
webrtc::AudioState* audio_state,
NetEqFactory* neteq_factory,
const webrtc::AudioReceiveStreamInterface::Config& config,
RtcEventLog* event_log) {
RTC_DCHECK(audio_state);
internal::AudioState* internal_audio_state =
static_cast<internal::AudioState*>(audio_state);
return voe::CreateChannelReceive(
clock, neteq_factory, internal_audio_state->audio_device_module(),
config.rtcp_send_transport, event_log, config.rtp.local_ssrc,
config.rtp.remote_ssrc, config.jitter_buffer_max_packets,
config.jitter_buffer_fast_accelerate, config.jitter_buffer_min_delay_ms,
config.enable_non_sender_rtt, config.decoder_factory,
config.codec_pair_id, std::move(config.frame_decryptor),
config.crypto_options, std::move(config.frame_transformer));
}
} // namespace
AudioReceiveStreamImpl::AudioReceiveStreamImpl(
Clock* clock,
PacketRouter* packet_router,
NetEqFactory* neteq_factory,
const webrtc::AudioReceiveStreamInterface::Config& config,
const rtc::scoped_refptr<webrtc::AudioState>& audio_state,
webrtc::RtcEventLog* event_log)
: AudioReceiveStreamImpl(clock,
packet_router,
config,
audio_state,
event_log,
CreateChannelReceive(clock,
audio_state.get(),
neteq_factory,
config,
event_log)) {}
AudioReceiveStreamImpl::AudioReceiveStreamImpl(
Clock* clock,
PacketRouter* packet_router,
const webrtc::AudioReceiveStreamInterface::Config& config,
const rtc::scoped_refptr<webrtc::AudioState>& audio_state,
webrtc::RtcEventLog* event_log,
std::unique_ptr<voe::ChannelReceiveInterface> channel_receive)
: config_(config),
audio_state_(audio_state),
source_tracker_(clock),
channel_receive_(std::move(channel_receive)) {
RTC_LOG(LS_INFO) << "AudioReceiveStreamImpl: " << config.rtp.remote_ssrc;
RTC_DCHECK(config.decoder_factory);
RTC_DCHECK(config.rtcp_send_transport);
RTC_DCHECK(audio_state_);
RTC_DCHECK(channel_receive_);
RTC_DCHECK(packet_router);
// Configure bandwidth estimation.
channel_receive_->RegisterReceiverCongestionControlObjects(packet_router);
// When output is muted, ChannelReceive will directly notify the source
// tracker of "delivered" frames, so RtpReceiver information will continue to
// be updated.
channel_receive_->SetSourceTracker(&source_tracker_);
// Complete configuration.
// TODO(solenberg): Config NACK history window (which is a packet count),
// using the actual packet size for the configured codec.
channel_receive_->SetNACKStatus(config.rtp.nack.rtp_history_ms != 0,
config.rtp.nack.rtp_history_ms / 20);
channel_receive_->SetReceiveCodecs(config.decoder_map);
// `frame_transformer` and `frame_decryptor` have been given to
// `channel_receive_` already.
}
AudioReceiveStreamImpl::~AudioReceiveStreamImpl() {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
RTC_LOG(LS_INFO) << "~AudioReceiveStreamImpl: " << remote_ssrc();
Stop();
channel_receive_->SetAssociatedSendChannel(nullptr);
channel_receive_->ResetReceiverCongestionControlObjects();
}
void AudioReceiveStreamImpl::RegisterWithTransport(
RtpStreamReceiverControllerInterface* receiver_controller) {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
RTC_DCHECK(!rtp_stream_receiver_);
rtp_stream_receiver_ = receiver_controller->CreateReceiver(
remote_ssrc(), channel_receive_.get());
}
void AudioReceiveStreamImpl::UnregisterFromTransport() {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
rtp_stream_receiver_.reset();
}
void AudioReceiveStreamImpl::ReconfigureForTesting(
const webrtc::AudioReceiveStreamInterface::Config& config) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
// SSRC can't be changed mid-stream.
RTC_DCHECK_EQ(remote_ssrc(), config.rtp.remote_ssrc);
RTC_DCHECK_EQ(local_ssrc(), config.rtp.local_ssrc);
// Configuration parameters which cannot be changed.
RTC_DCHECK_EQ(config_.rtcp_send_transport, config.rtcp_send_transport);
// Decoder factory cannot be changed because it is configured at
// voe::Channel construction time.
RTC_DCHECK_EQ(config_.decoder_factory, config.decoder_factory);
// TODO(solenberg): Config NACK history window (which is a packet count),
// using the actual packet size for the configured codec.
RTC_DCHECK_EQ(config_.rtp.nack.rtp_history_ms, config.rtp.nack.rtp_history_ms)
<< "Use SetUseTransportCcAndNackHistory";
RTC_DCHECK(config_.decoder_map == config.decoder_map) << "Use SetDecoderMap";
RTC_DCHECK_EQ(config_.frame_transformer, config.frame_transformer)
<< "Use SetDepacketizerToDecoderFrameTransformer";
config_ = config;
}
void AudioReceiveStreamImpl::Start() {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
if (playing_) {
return;
}
RTC_LOG(LS_INFO) << "AudioReceiveStreamImpl::Start: " << remote_ssrc();
channel_receive_->StartPlayout();
playing_ = true;
audio_state()->AddReceivingStream(this);
}
void AudioReceiveStreamImpl::Stop() {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
if (!playing_) {
return;
}
RTC_LOG(LS_INFO) << "AudioReceiveStreamImpl::Stop: " << remote_ssrc();
channel_receive_->StopPlayout();
playing_ = false;
audio_state()->RemoveReceivingStream(this);
}
bool AudioReceiveStreamImpl::IsRunning() const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
return playing_;
}
void AudioReceiveStreamImpl::SetDepacketizerToDecoderFrameTransformer(
rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
channel_receive_->SetDepacketizerToDecoderFrameTransformer(
std::move(frame_transformer));
}
void AudioReceiveStreamImpl::SetDecoderMap(
std::map<int, SdpAudioFormat> decoder_map) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
config_.decoder_map = std::move(decoder_map);
channel_receive_->SetReceiveCodecs(config_.decoder_map);
}
void AudioReceiveStreamImpl::SetNackHistory(int history_ms) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
RTC_DCHECK_GE(history_ms, 0);
if (config_.rtp.nack.rtp_history_ms == history_ms)
return;
config_.rtp.nack.rtp_history_ms = history_ms;
// TODO(solenberg): Config NACK history window (which is a packet count),
// using the actual packet size for the configured codec.
channel_receive_->SetNACKStatus(history_ms != 0, history_ms / 20);
}
void AudioReceiveStreamImpl::SetNonSenderRttMeasurement(bool enabled) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
config_.enable_non_sender_rtt = enabled;
channel_receive_->SetNonSenderRttMeasurement(enabled);
}
void AudioReceiveStreamImpl::SetFrameDecryptor(
rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor) {
// TODO(bugs.webrtc.org/11993): This is called via WebRtcAudioReceiveStream,
// expect to be called on the network thread.
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
channel_receive_->SetFrameDecryptor(std::move(frame_decryptor));
}
webrtc::AudioReceiveStreamInterface::Stats AudioReceiveStreamImpl::GetStats(
bool get_and_clear_legacy_stats) const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
webrtc::AudioReceiveStreamInterface::Stats stats;
stats.remote_ssrc = remote_ssrc();
webrtc::CallReceiveStatistics call_stats =
channel_receive_->GetRTCPStatistics();
// TODO(solenberg): Don't return here if we can't get the codec - return the
// stats we *can* get.
auto receive_codec = channel_receive_->GetReceiveCodec();
if (!receive_codec) {
return stats;
}
stats.payload_bytes_received = call_stats.payload_bytes_received;
stats.header_and_padding_bytes_received =
call_stats.header_and_padding_bytes_received;
stats.packets_received = call_stats.packetsReceived;
stats.packets_lost = call_stats.cumulativeLost;
stats.nacks_sent = call_stats.nacks_sent;
stats.capture_start_ntp_time_ms = call_stats.capture_start_ntp_time_ms_;
stats.last_packet_received = call_stats.last_packet_received;
stats.codec_name = receive_codec->second.name;
stats.codec_payload_type = receive_codec->first;
int clockrate_khz = receive_codec->second.clockrate_hz / 1000;
if (clockrate_khz > 0) {
stats.jitter_ms = call_stats.jitterSamples / clockrate_khz;
}
stats.delay_estimate_ms = channel_receive_->GetDelayEstimate();
stats.audio_level = channel_receive_->GetSpeechOutputLevelFullRange();
stats.total_output_energy = channel_receive_->GetTotalOutputEnergy();
stats.total_output_duration = channel_receive_->GetTotalOutputDuration();
stats.estimated_playout_ntp_timestamp_ms =
channel_receive_->GetCurrentEstimatedPlayoutNtpTimestampMs(
rtc::TimeMillis());
// Get jitter buffer and total delay (alg + jitter + playout) stats.
auto ns = channel_receive_->GetNetworkStatistics(get_and_clear_legacy_stats);
stats.packets_discarded = ns.packetsDiscarded;
stats.fec_packets_received = ns.fecPacketsReceived;
stats.fec_packets_discarded = ns.fecPacketsDiscarded;
stats.jitter_buffer_ms = ns.currentBufferSize;
stats.jitter_buffer_preferred_ms = ns.preferredBufferSize;
stats.total_samples_received = ns.totalSamplesReceived;
stats.concealed_samples = ns.concealedSamples;
stats.silent_concealed_samples = ns.silentConcealedSamples;
stats.concealment_events = ns.concealmentEvents;
stats.jitter_buffer_delay_seconds =
static_cast<double>(ns.jitterBufferDelayMs) /
static_cast<double>(rtc::kNumMillisecsPerSec);
stats.jitter_buffer_emitted_count = ns.jitterBufferEmittedCount;
stats.jitter_buffer_target_delay_seconds =
static_cast<double>(ns.jitterBufferTargetDelayMs) /
static_cast<double>(rtc::kNumMillisecsPerSec);
stats.jitter_buffer_minimum_delay_seconds =
static_cast<double>(ns.jitterBufferMinimumDelayMs) /
static_cast<double>(rtc::kNumMillisecsPerSec);
stats.inserted_samples_for_deceleration = ns.insertedSamplesForDeceleration;
stats.removed_samples_for_acceleration = ns.removedSamplesForAcceleration;
stats.expand_rate = Q14ToFloat(ns.currentExpandRate);
stats.speech_expand_rate = Q14ToFloat(ns.currentSpeechExpandRate);
stats.secondary_decoded_rate = Q14ToFloat(ns.currentSecondaryDecodedRate);
stats.secondary_discarded_rate = Q14ToFloat(ns.currentSecondaryDiscardedRate);
stats.accelerate_rate = Q14ToFloat(ns.currentAccelerateRate);
stats.preemptive_expand_rate = Q14ToFloat(ns.currentPreemptiveRate);
stats.jitter_buffer_flushes = ns.packetBufferFlushes;
stats.delayed_packet_outage_samples = ns.delayedPacketOutageSamples;
stats.relative_packet_arrival_delay_seconds =
static_cast<double>(ns.relativePacketArrivalDelayMs) /
static_cast<double>(rtc::kNumMillisecsPerSec);
stats.interruption_count = ns.interruptionCount;
stats.total_interruption_duration_ms = ns.totalInterruptionDurationMs;
auto ds = channel_receive_->GetDecodingCallStatistics();
stats.decoding_calls_to_silence_generator = ds.calls_to_silence_generator;
stats.decoding_calls_to_neteq = ds.calls_to_neteq;
stats.decoding_normal = ds.decoded_normal;
stats.decoding_plc = ds.decoded_neteq_plc;
stats.decoding_codec_plc = ds.decoded_codec_plc;
stats.decoding_cng = ds.decoded_cng;
stats.decoding_plc_cng = ds.decoded_plc_cng;
stats.decoding_muted_output = ds.decoded_muted_output;
stats.last_sender_report_timestamp_ms =
call_stats.last_sender_report_timestamp_ms;
stats.last_sender_report_remote_timestamp_ms =
call_stats.last_sender_report_remote_timestamp_ms;
stats.sender_reports_packets_sent = call_stats.sender_reports_packets_sent;
stats.sender_reports_bytes_sent = call_stats.sender_reports_bytes_sent;
stats.sender_reports_reports_count = call_stats.sender_reports_reports_count;
stats.round_trip_time = call_stats.round_trip_time;
stats.round_trip_time_measurements = call_stats.round_trip_time_measurements;
stats.total_round_trip_time = call_stats.total_round_trip_time;
return stats;
}
void AudioReceiveStreamImpl::SetSink(AudioSinkInterface* sink) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
channel_receive_->SetSink(sink);
}
void AudioReceiveStreamImpl::SetGain(float gain) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
channel_receive_->SetChannelOutputVolumeScaling(gain);
}
bool AudioReceiveStreamImpl::SetBaseMinimumPlayoutDelayMs(int delay_ms) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
return channel_receive_->SetBaseMinimumPlayoutDelayMs(delay_ms);
}
int AudioReceiveStreamImpl::GetBaseMinimumPlayoutDelayMs() const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
return channel_receive_->GetBaseMinimumPlayoutDelayMs();
}
std::vector<RtpSource> AudioReceiveStreamImpl::GetSources() const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
return source_tracker_.GetSources();
}
AudioMixer::Source::AudioFrameInfo
AudioReceiveStreamImpl::GetAudioFrameWithInfo(int sample_rate_hz,
AudioFrame* audio_frame) {
AudioMixer::Source::AudioFrameInfo audio_frame_info =
channel_receive_->GetAudioFrameWithInfo(sample_rate_hz, audio_frame);
if (audio_frame_info != AudioMixer::Source::AudioFrameInfo::kError &&
!audio_frame->packet_infos_.empty()) {
source_tracker_.OnFrameDelivered(audio_frame->packet_infos_);
}
return audio_frame_info;
}
int AudioReceiveStreamImpl::Ssrc() const {
return remote_ssrc();
}
int AudioReceiveStreamImpl::PreferredSampleRate() const {
return channel_receive_->PreferredSampleRate();
}
uint32_t AudioReceiveStreamImpl::id() const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
return remote_ssrc();
}
absl::optional<Syncable::Info> AudioReceiveStreamImpl::GetInfo() const {
// TODO(bugs.webrtc.org/11993): This is called via RtpStreamsSynchronizer,
// expect to be called on the network thread.
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
return channel_receive_->GetSyncInfo();
}
bool AudioReceiveStreamImpl::GetPlayoutRtpTimestamp(uint32_t* rtp_timestamp,
int64_t* time_ms) const {
// Called on video capture thread.
return channel_receive_->GetPlayoutRtpTimestamp(rtp_timestamp, time_ms);
}
void AudioReceiveStreamImpl::SetEstimatedPlayoutNtpTimestampMs(
int64_t ntp_timestamp_ms,
int64_t time_ms) {
// Called on video capture thread.
channel_receive_->SetEstimatedPlayoutNtpTimestampMs(ntp_timestamp_ms,
time_ms);
}
bool AudioReceiveStreamImpl::SetMinimumPlayoutDelay(int delay_ms) {
// TODO(bugs.webrtc.org/11993): This is called via RtpStreamsSynchronizer,
// expect to be called on the network thread.
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
return channel_receive_->SetMinimumPlayoutDelay(delay_ms);
}
void AudioReceiveStreamImpl::AssociateSendStream(
internal::AudioSendStream* send_stream) {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
channel_receive_->SetAssociatedSendChannel(
send_stream ? send_stream->GetChannel() : nullptr);
associated_send_stream_ = send_stream;
}
void AudioReceiveStreamImpl::DeliverRtcp(const uint8_t* packet, size_t length) {
// TODO(solenberg): Tests call this function on a network thread, libjingle
// calls on the worker thread. We should move towards always using a network
// thread. Then this check can be enabled.
// RTC_DCHECK(!thread_checker_.IsCurrent());
channel_receive_->ReceivedRTCPPacket(packet, length);
}
void AudioReceiveStreamImpl::SetSyncGroup(absl::string_view sync_group) {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
config_.sync_group = std::string(sync_group);
}
void AudioReceiveStreamImpl::SetLocalSsrc(uint32_t local_ssrc) {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
// TODO(tommi): Consider storing local_ssrc in one place.
config_.rtp.local_ssrc = local_ssrc;
channel_receive_->OnLocalSsrcChange(local_ssrc);
}
uint32_t AudioReceiveStreamImpl::local_ssrc() const {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
RTC_DCHECK_EQ(config_.rtp.local_ssrc, channel_receive_->GetLocalSsrc());
return config_.rtp.local_ssrc;
}
const std::string& AudioReceiveStreamImpl::sync_group() const {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
return config_.sync_group;
}
const AudioSendStream*
AudioReceiveStreamImpl::GetAssociatedSendStreamForTesting() const {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
return associated_send_stream_;
}
internal::AudioState* AudioReceiveStreamImpl::audio_state() const {
auto* audio_state = static_cast<internal::AudioState*>(audio_state_.get());
RTC_DCHECK(audio_state);
return audio_state;
}
} // namespace webrtc

View file

@ -0,0 +1,173 @@
/*
* Copyright (c) 2015 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.
*/
#ifndef AUDIO_AUDIO_RECEIVE_STREAM_H_
#define AUDIO_AUDIO_RECEIVE_STREAM_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "api/audio/audio_mixer.h"
#include "api/neteq/neteq_factory.h"
#include "api/rtp_headers.h"
#include "api/sequence_checker.h"
#include "audio/audio_state.h"
#include "call/audio_receive_stream.h"
#include "call/syncable.h"
#include "modules/rtp_rtcp/source/source_tracker.h"
#include "rtc_base/system/no_unique_address.h"
#include "system_wrappers/include/clock.h"
namespace webrtc {
class PacketRouter;
class RtcEventLog;
class RtpStreamReceiverControllerInterface;
class RtpStreamReceiverInterface;
namespace voe {
class ChannelReceiveInterface;
} // namespace voe
namespace internal {
class AudioSendStream;
} // namespace internal
class AudioReceiveStreamImpl final : public webrtc::AudioReceiveStreamInterface,
public AudioMixer::Source,
public Syncable {
public:
AudioReceiveStreamImpl(
Clock* clock,
PacketRouter* packet_router,
NetEqFactory* neteq_factory,
const webrtc::AudioReceiveStreamInterface::Config& config,
const rtc::scoped_refptr<webrtc::AudioState>& audio_state,
webrtc::RtcEventLog* event_log);
// For unit tests, which need to supply a mock channel receive.
AudioReceiveStreamImpl(
Clock* clock,
PacketRouter* packet_router,
const webrtc::AudioReceiveStreamInterface::Config& config,
const rtc::scoped_refptr<webrtc::AudioState>& audio_state,
webrtc::RtcEventLog* event_log,
std::unique_ptr<voe::ChannelReceiveInterface> channel_receive);
AudioReceiveStreamImpl() = delete;
AudioReceiveStreamImpl(const AudioReceiveStreamImpl&) = delete;
AudioReceiveStreamImpl& operator=(const AudioReceiveStreamImpl&) = delete;
// Destruction happens on the worker thread. Prior to destruction the caller
// must ensure that a registration with the transport has been cleared. See
// `RegisterWithTransport` for details.
// TODO(tommi): As a further improvement to this, performing the full
// destruction on the network thread could be made the default.
~AudioReceiveStreamImpl() override;
// Called on the network thread to register/unregister with the network
// transport.
void RegisterWithTransport(
RtpStreamReceiverControllerInterface* receiver_controller);
// If registration has previously been done (via `RegisterWithTransport`) then
// `UnregisterFromTransport` must be called prior to destruction, on the
// network thread.
void UnregisterFromTransport();
// webrtc::AudioReceiveStreamInterface implementation.
void Start() override;
void Stop() override;
bool IsRunning() const override;
void SetDepacketizerToDecoderFrameTransformer(
rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
override;
void SetDecoderMap(std::map<int, SdpAudioFormat> decoder_map) override;
void SetNackHistory(int history_ms) override;
void SetNonSenderRttMeasurement(bool enabled) override;
void SetFrameDecryptor(rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
frame_decryptor) override;
webrtc::AudioReceiveStreamInterface::Stats GetStats(
bool get_and_clear_legacy_stats) const override;
void SetSink(AudioSinkInterface* sink) override;
void SetGain(float gain) override;
bool SetBaseMinimumPlayoutDelayMs(int delay_ms) override;
int GetBaseMinimumPlayoutDelayMs() const override;
std::vector<webrtc::RtpSource> GetSources() const override;
// AudioMixer::Source
AudioFrameInfo GetAudioFrameWithInfo(int sample_rate_hz,
AudioFrame* audio_frame) override;
int Ssrc() const override;
int PreferredSampleRate() const override;
// Syncable
uint32_t id() const override;
absl::optional<Syncable::Info> GetInfo() const override;
bool GetPlayoutRtpTimestamp(uint32_t* rtp_timestamp,
int64_t* time_ms) const override;
void SetEstimatedPlayoutNtpTimestampMs(int64_t ntp_timestamp_ms,
int64_t time_ms) override;
bool SetMinimumPlayoutDelay(int delay_ms) override;
void AssociateSendStream(internal::AudioSendStream* send_stream);
void DeliverRtcp(const uint8_t* packet, size_t length);
void SetSyncGroup(absl::string_view sync_group);
void SetLocalSsrc(uint32_t local_ssrc);
uint32_t local_ssrc() const;
uint32_t remote_ssrc() const override {
// The remote_ssrc member variable of config_ will never change and can be
// considered const.
return config_.rtp.remote_ssrc;
}
// Returns a reference to the currently set sync group of the stream.
// Must be called on the packet delivery thread.
const std::string& sync_group() const;
const AudioSendStream* GetAssociatedSendStreamForTesting() const;
// TODO(tommi): Remove this method.
void ReconfigureForTesting(
const webrtc::AudioReceiveStreamInterface::Config& config);
private:
internal::AudioState* audio_state() const;
RTC_NO_UNIQUE_ADDRESS SequenceChecker worker_thread_checker_;
// TODO(bugs.webrtc.org/11993): This checker conceptually represents
// operations that belong to the network thread. The Call class is currently
// moving towards handling network packets on the network thread and while
// that work is ongoing, this checker may in practice represent the worker
// thread, but still serves as a mechanism of grouping together concepts
// that belong to the network thread. Once the packets are fully delivered
// on the network thread, this comment will be deleted.
RTC_NO_UNIQUE_ADDRESS SequenceChecker packet_sequence_checker_{
SequenceChecker::kDetached};
webrtc::AudioReceiveStreamInterface::Config config_;
rtc::scoped_refptr<webrtc::AudioState> audio_state_;
SourceTracker source_tracker_;
const std::unique_ptr<voe::ChannelReceiveInterface> channel_receive_;
AudioSendStream* associated_send_stream_
RTC_GUARDED_BY(packet_sequence_checker_) = nullptr;
bool playing_ RTC_GUARDED_BY(worker_thread_checker_) = false;
std::unique_ptr<RtpStreamReceiverInterface> rtp_stream_receiver_
RTC_GUARDED_BY(packet_sequence_checker_);
};
} // namespace webrtc
#endif // AUDIO_AUDIO_RECEIVE_STREAM_H_

View file

@ -0,0 +1,891 @@
/*
* Copyright (c) 2015 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 "audio/audio_send_stream.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "api/audio_codecs/audio_encoder.h"
#include "api/audio_codecs/audio_encoder_factory.h"
#include "api/audio_codecs/audio_format.h"
#include "api/call/transport.h"
#include "api/crypto/frame_encryptor_interface.h"
#include "api/function_view.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/task_queue/task_queue_base.h"
#include "audio/audio_state.h"
#include "audio/channel_send.h"
#include "audio/conversion.h"
#include "call/rtp_config.h"
#include "call/rtp_transport_controller_send_interface.h"
#include "common_audio/vad/include/vad.h"
#include "logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h"
#include "logging/rtc_event_log/rtc_stream_config.h"
#include "media/base/media_channel.h"
#include "modules/audio_coding/codecs/cng/audio_encoder_cng.h"
#include "modules/audio_coding/codecs/red/audio_encoder_copy_red.h"
#include "modules/audio_processing/include/audio_processing.h"
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/strings/audio_format_to_string.h"
#include "rtc_base/trace_event.h"
namespace webrtc {
namespace {
void UpdateEventLogStreamConfig(RtcEventLog* event_log,
const AudioSendStream::Config& config,
const AudioSendStream::Config* old_config) {
using SendCodecSpec = AudioSendStream::Config::SendCodecSpec;
// Only update if any of the things we log have changed.
auto payload_types_equal = [](const absl::optional<SendCodecSpec>& a,
const absl::optional<SendCodecSpec>& b) {
if (a.has_value() && b.has_value()) {
return a->format.name == b->format.name &&
a->payload_type == b->payload_type;
}
return !a.has_value() && !b.has_value();
};
if (old_config && config.rtp.ssrc == old_config->rtp.ssrc &&
config.rtp.extensions == old_config->rtp.extensions &&
payload_types_equal(config.send_codec_spec,
old_config->send_codec_spec)) {
return;
}
auto rtclog_config = std::make_unique<rtclog::StreamConfig>();
rtclog_config->local_ssrc = config.rtp.ssrc;
rtclog_config->rtp_extensions = config.rtp.extensions;
if (config.send_codec_spec) {
rtclog_config->codecs.emplace_back(config.send_codec_spec->format.name,
config.send_codec_spec->payload_type, 0);
}
event_log->Log(std::make_unique<RtcEventAudioSendStreamConfig>(
std::move(rtclog_config)));
}
} // namespace
constexpr char AudioAllocationConfig::kKey[];
std::unique_ptr<StructParametersParser> AudioAllocationConfig::Parser() {
return StructParametersParser::Create( //
"min", &min_bitrate, //
"max", &max_bitrate, //
"prio_rate", &priority_bitrate, //
"prio_rate_raw", &priority_bitrate_raw, //
"rate_prio", &bitrate_priority);
}
AudioAllocationConfig::AudioAllocationConfig(
const FieldTrialsView& field_trials) {
Parser()->Parse(field_trials.Lookup(kKey));
if (priority_bitrate_raw && !priority_bitrate.IsZero()) {
RTC_LOG(LS_WARNING) << "'priority_bitrate' and '_raw' are mutually "
"exclusive but both were configured.";
}
}
namespace internal {
AudioSendStream::AudioSendStream(
Clock* clock,
const webrtc::AudioSendStream::Config& config,
const rtc::scoped_refptr<webrtc::AudioState>& audio_state,
TaskQueueFactory* task_queue_factory,
RtpTransportControllerSendInterface* rtp_transport,
BitrateAllocatorInterface* bitrate_allocator,
RtcEventLog* event_log,
RtcpRttStats* rtcp_rtt_stats,
const absl::optional<RtpState>& suspended_rtp_state,
const FieldTrialsView& field_trials)
: AudioSendStream(clock,
config,
audio_state,
task_queue_factory,
rtp_transport,
bitrate_allocator,
event_log,
suspended_rtp_state,
voe::CreateChannelSend(clock,
task_queue_factory,
config.send_transport,
rtcp_rtt_stats,
event_log,
config.frame_encryptor.get(),
config.crypto_options,
config.rtp.extmap_allow_mixed,
config.rtcp_report_interval_ms,
config.rtp.ssrc,
config.frame_transformer,
rtp_transport,
field_trials),
field_trials) {}
AudioSendStream::AudioSendStream(
Clock* clock,
const webrtc::AudioSendStream::Config& config,
const rtc::scoped_refptr<webrtc::AudioState>& audio_state,
TaskQueueFactory* task_queue_factory,
RtpTransportControllerSendInterface* rtp_transport,
BitrateAllocatorInterface* bitrate_allocator,
RtcEventLog* event_log,
const absl::optional<RtpState>& suspended_rtp_state,
std::unique_ptr<voe::ChannelSendInterface> channel_send,
const FieldTrialsView& field_trials)
: clock_(clock),
field_trials_(field_trials),
allocate_audio_without_feedback_(
field_trials_.IsEnabled("WebRTC-Audio-ABWENoTWCC")),
enable_audio_alr_probing_(
!field_trials_.IsDisabled("WebRTC-Audio-AlrProbing")),
allocation_settings_(field_trials_),
config_(Config(/*send_transport=*/nullptr)),
audio_state_(audio_state),
channel_send_(std::move(channel_send)),
event_log_(event_log),
use_legacy_overhead_calculation_(
field_trials_.IsEnabled("WebRTC-Audio-LegacyOverhead")),
enable_priority_bitrate_(
!field_trials_.IsDisabled("WebRTC-Audio-PriorityBitrate")),
bitrate_allocator_(bitrate_allocator),
rtp_transport_(rtp_transport),
rtp_rtcp_module_(channel_send_->GetRtpRtcp()),
suspended_rtp_state_(suspended_rtp_state) {
RTC_LOG(LS_INFO) << "AudioSendStream: " << config.rtp.ssrc;
RTC_DCHECK(audio_state_);
RTC_DCHECK(channel_send_);
RTC_DCHECK(bitrate_allocator_);
RTC_DCHECK(rtp_transport);
RTC_DCHECK(rtp_rtcp_module_);
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
ConfigureStream(config, true, nullptr);
}
AudioSendStream::~AudioSendStream() {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
RTC_LOG(LS_INFO) << "~AudioSendStream: " << config_.rtp.ssrc;
RTC_DCHECK(!sending_);
channel_send_->ResetSenderCongestionControlObjects();
}
const webrtc::AudioSendStream::Config& AudioSendStream::GetConfig() const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
return config_;
}
void AudioSendStream::Reconfigure(
const webrtc::AudioSendStream::Config& new_config,
SetParametersCallback callback) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
ConfigureStream(new_config, false, std::move(callback));
}
AudioSendStream::ExtensionIds AudioSendStream::FindExtensionIds(
const std::vector<RtpExtension>& extensions) {
ExtensionIds ids;
for (const auto& extension : extensions) {
if (extension.uri == RtpExtension::kAudioLevelUri) {
ids.audio_level = extension.id;
} else if (extension.uri == RtpExtension::kAbsSendTimeUri) {
ids.abs_send_time = extension.id;
} else if (extension.uri == RtpExtension::kTransportSequenceNumberUri) {
ids.transport_sequence_number = extension.id;
} else if (extension.uri == RtpExtension::kMidUri) {
ids.mid = extension.id;
} else if (extension.uri == RtpExtension::kRidUri) {
ids.rid = extension.id;
} else if (extension.uri == RtpExtension::kRepairedRidUri) {
ids.repaired_rid = extension.id;
} else if (extension.uri == RtpExtension::kAbsoluteCaptureTimeUri) {
ids.abs_capture_time = extension.id;
}
}
return ids;
}
int AudioSendStream::TransportSeqNumId(const AudioSendStream::Config& config) {
return FindExtensionIds(config.rtp.extensions).transport_sequence_number;
}
void AudioSendStream::ConfigureStream(
const webrtc::AudioSendStream::Config& new_config,
bool first_time,
SetParametersCallback callback) {
RTC_LOG(LS_INFO) << "AudioSendStream::ConfigureStream: "
<< new_config.ToString();
UpdateEventLogStreamConfig(event_log_, new_config,
first_time ? nullptr : &config_);
const auto& old_config = config_;
// Configuration parameters which cannot be changed.
RTC_DCHECK(first_time ||
old_config.send_transport == new_config.send_transport);
RTC_DCHECK(first_time || old_config.rtp.ssrc == new_config.rtp.ssrc);
if (suspended_rtp_state_ && first_time) {
rtp_rtcp_module_->SetRtpState(*suspended_rtp_state_);
}
if (first_time || old_config.rtp.c_name != new_config.rtp.c_name) {
channel_send_->SetRTCP_CNAME(new_config.rtp.c_name);
}
// Enable the frame encryptor if a new frame encryptor has been provided.
if (first_time || new_config.frame_encryptor != old_config.frame_encryptor) {
channel_send_->SetFrameEncryptor(new_config.frame_encryptor);
}
if (first_time ||
new_config.frame_transformer != old_config.frame_transformer) {
channel_send_->SetEncoderToPacketizerFrameTransformer(
new_config.frame_transformer);
}
if (first_time ||
new_config.rtp.extmap_allow_mixed != old_config.rtp.extmap_allow_mixed) {
rtp_rtcp_module_->SetExtmapAllowMixed(new_config.rtp.extmap_allow_mixed);
}
const ExtensionIds old_ids = FindExtensionIds(old_config.rtp.extensions);
const ExtensionIds new_ids = FindExtensionIds(new_config.rtp.extensions);
// Audio level indication
if (first_time || new_ids.audio_level != old_ids.audio_level) {
channel_send_->SetSendAudioLevelIndicationStatus(new_ids.audio_level != 0,
new_ids.audio_level);
}
if (first_time || new_ids.abs_send_time != old_ids.abs_send_time) {
absl::string_view uri = AbsoluteSendTime::Uri();
rtp_rtcp_module_->DeregisterSendRtpHeaderExtension(uri);
if (new_ids.abs_send_time) {
rtp_rtcp_module_->RegisterRtpHeaderExtension(uri, new_ids.abs_send_time);
}
}
bool transport_seq_num_id_changed =
new_ids.transport_sequence_number != old_ids.transport_sequence_number;
if (first_time ||
(transport_seq_num_id_changed && !allocate_audio_without_feedback_)) {
if (!first_time) {
channel_send_->ResetSenderCongestionControlObjects();
}
if (!allocate_audio_without_feedback_ &&
new_ids.transport_sequence_number != 0) {
rtp_rtcp_module_->RegisterRtpHeaderExtension(
TransportSequenceNumber::Uri(), new_ids.transport_sequence_number);
// Probing in application limited region is only used in combination with
// send side congestion control, wich depends on feedback packets which
// requires transport sequence numbers to be enabled.
// Optionally request ALR probing but do not override any existing
// request from other streams.
if (enable_audio_alr_probing_) {
rtp_transport_->EnablePeriodicAlrProbing(true);
}
}
channel_send_->RegisterSenderCongestionControlObjects(rtp_transport_);
}
// MID RTP header extension.
if ((first_time || new_ids.mid != old_ids.mid ||
new_config.rtp.mid != old_config.rtp.mid) &&
new_ids.mid != 0 && !new_config.rtp.mid.empty()) {
rtp_rtcp_module_->RegisterRtpHeaderExtension(RtpMid::Uri(), new_ids.mid);
rtp_rtcp_module_->SetMid(new_config.rtp.mid);
}
if (first_time || new_ids.abs_capture_time != old_ids.abs_capture_time) {
absl::string_view uri = AbsoluteCaptureTimeExtension::Uri();
rtp_rtcp_module_->DeregisterSendRtpHeaderExtension(uri);
if (new_ids.abs_capture_time) {
rtp_rtcp_module_->RegisterRtpHeaderExtension(uri,
new_ids.abs_capture_time);
}
}
if (!ReconfigureSendCodec(new_config)) {
RTC_LOG(LS_ERROR) << "Failed to set up send codec state.";
webrtc::InvokeSetParametersCallback(
callback, webrtc::RTCError(webrtc::RTCErrorType::INTERNAL_ERROR,
"Failed to set up send codec state."));
}
// Set currently known overhead (used in ANA, opus only).
UpdateOverheadPerPacket();
channel_send_->CallEncoder([this](AudioEncoder* encoder) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
if (!encoder) {
return;
}
frame_length_range_ = encoder->GetFrameLengthRange();
bitrate_range_ = encoder->GetBitrateRange();
});
if (sending_) {
ReconfigureBitrateObserver(new_config);
}
config_ = new_config;
webrtc::InvokeSetParametersCallback(callback, webrtc::RTCError::OK());
}
void AudioSendStream::Start() {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
if (sending_) {
return;
}
RTC_LOG(LS_INFO) << "AudioSendStream::Start: " << config_.rtp.ssrc;
if (!config_.has_dscp && config_.min_bitrate_bps != -1 &&
config_.max_bitrate_bps != -1 &&
(allocate_audio_without_feedback_ || TransportSeqNumId(config_) != 0)) {
rtp_transport_->AccountForAudioPacketsInPacedSender(true);
rtp_transport_->IncludeOverheadInPacedSender();
rtp_rtcp_module_->SetAsPartOfAllocation(true);
ConfigureBitrateObserver();
} else {
rtp_rtcp_module_->SetAsPartOfAllocation(false);
}
channel_send_->StartSend();
sending_ = true;
audio_state()->AddSendingStream(this, encoder_sample_rate_hz_,
encoder_num_channels_);
}
void AudioSendStream::Stop() {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
if (!sending_) {
return;
}
RTC_LOG(LS_INFO) << "AudioSendStream::Stop: " << config_.rtp.ssrc;
RemoveBitrateObserver();
channel_send_->StopSend();
sending_ = false;
audio_state()->RemoveSendingStream(this);
}
void AudioSendStream::SendAudioData(std::unique_ptr<AudioFrame> audio_frame) {
RTC_CHECK_RUNS_SERIALIZED(&audio_capture_race_checker_);
RTC_DCHECK_GT(audio_frame->sample_rate_hz_, 0);
TRACE_EVENT0("webrtc", "AudioSendStream::SendAudioData");
double duration = static_cast<double>(audio_frame->samples_per_channel_) /
audio_frame->sample_rate_hz_;
{
// Note: SendAudioData() passes the frame further down the pipeline and it
// may eventually get sent. But this method is invoked even if we are not
// connected, as long as we have an AudioSendStream (created as a result of
// an O/A exchange). This means that we are calculating audio levels whether
// or not we are sending samples.
// TODO(https://crbug.com/webrtc/10771): All "media-source" related stats
// should move from send-streams to the local audio sources or tracks; a
// send-stream should not be required to read the microphone audio levels.
MutexLock lock(&audio_level_lock_);
audio_level_.ComputeLevel(*audio_frame, duration);
}
channel_send_->ProcessAndEncodeAudio(std::move(audio_frame));
}
bool AudioSendStream::SendTelephoneEvent(int payload_type,
int payload_frequency,
int event,
int duration_ms) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
channel_send_->SetSendTelephoneEventPayloadType(payload_type,
payload_frequency);
return channel_send_->SendTelephoneEventOutband(event, duration_ms);
}
void AudioSendStream::SetMuted(bool muted) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
channel_send_->SetInputMute(muted);
}
webrtc::AudioSendStream::Stats AudioSendStream::GetStats() const {
return GetStats(true);
}
webrtc::AudioSendStream::Stats AudioSendStream::GetStats(
bool has_remote_tracks) const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
webrtc::AudioSendStream::Stats stats;
stats.local_ssrc = config_.rtp.ssrc;
stats.target_bitrate_bps = channel_send_->GetTargetBitrate();
webrtc::CallSendStatistics call_stats = channel_send_->GetRTCPStatistics();
stats.payload_bytes_sent = call_stats.payload_bytes_sent;
stats.header_and_padding_bytes_sent =
call_stats.header_and_padding_bytes_sent;
stats.retransmitted_bytes_sent = call_stats.retransmitted_bytes_sent;
stats.packets_sent = call_stats.packetsSent;
stats.total_packet_send_delay = call_stats.total_packet_send_delay;
stats.retransmitted_packets_sent = call_stats.retransmitted_packets_sent;
// RTT isn't known until a RTCP report is received. Until then, VoiceEngine
// returns 0 to indicate an error value.
if (call_stats.rttMs > 0) {
stats.rtt_ms = call_stats.rttMs;
}
if (config_.send_codec_spec) {
const auto& spec = *config_.send_codec_spec;
stats.codec_name = spec.format.name;
stats.codec_payload_type = spec.payload_type;
// Get data from the last remote RTCP report.
for (const ReportBlockData& block :
channel_send_->GetRemoteRTCPReportBlocks()) {
// Lookup report for send ssrc only.
if (block.source_ssrc() == stats.local_ssrc) {
stats.packets_lost = block.cumulative_lost();
stats.fraction_lost = block.fraction_lost();
if (spec.format.clockrate_hz > 0) {
stats.jitter_ms = block.jitter(spec.format.clockrate_hz).ms();
}
break;
}
}
}
{
MutexLock lock(&audio_level_lock_);
stats.audio_level = audio_level_.LevelFullRange();
stats.total_input_energy = audio_level_.TotalEnergy();
stats.total_input_duration = audio_level_.TotalDuration();
}
stats.ana_statistics = channel_send_->GetANAStatistics();
AudioProcessing* ap = audio_state_->audio_processing();
if (ap) {
stats.apm_statistics = ap->GetStatistics(has_remote_tracks);
}
stats.report_block_datas = std::move(call_stats.report_block_datas);
stats.nacks_received = call_stats.nacks_received;
return stats;
}
void AudioSendStream::DeliverRtcp(const uint8_t* packet, size_t length) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
channel_send_->ReceivedRTCPPacket(packet, length);
// Poll if overhead has changed, which it can do if ack triggers us to stop
// sending mid/rid.
UpdateOverheadPerPacket();
}
uint32_t AudioSendStream::OnBitrateUpdated(BitrateAllocationUpdate update) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
// Pick a target bitrate between the constraints. Overrules the allocator if
// it 1) allocated a bitrate of zero to disable the stream or 2) allocated a
// higher than max to allow for e.g. extra FEC.
absl::optional<TargetAudioBitrateConstraints> constraints =
GetMinMaxBitrateConstraints();
if (constraints) {
update.target_bitrate.Clamp(constraints->min, constraints->max);
update.stable_target_bitrate.Clamp(constraints->min, constraints->max);
}
channel_send_->OnBitrateAllocation(update);
// The amount of audio protection is not exposed by the encoder, hence
// always returning 0.
return 0;
}
void AudioSendStream::SetTransportOverhead(
int transport_overhead_per_packet_bytes) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
transport_overhead_per_packet_bytes_ = transport_overhead_per_packet_bytes;
UpdateOverheadPerPacket();
}
void AudioSendStream::UpdateOverheadPerPacket() {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
size_t overhead_per_packet_bytes =
transport_overhead_per_packet_bytes_ +
rtp_rtcp_module_->ExpectedPerPacketOverhead();
if (overhead_per_packet_ == overhead_per_packet_bytes) {
return;
}
overhead_per_packet_ = overhead_per_packet_bytes;
channel_send_->CallEncoder([&](AudioEncoder* encoder) {
encoder->OnReceivedOverhead(overhead_per_packet_bytes);
});
if (registered_with_allocator_) {
ConfigureBitrateObserver();
}
}
size_t AudioSendStream::TestOnlyGetPerPacketOverheadBytes() const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
return overhead_per_packet_;
}
RtpState AudioSendStream::GetRtpState() const {
return rtp_rtcp_module_->GetRtpState();
}
const voe::ChannelSendInterface* AudioSendStream::GetChannel() const {
return channel_send_.get();
}
internal::AudioState* AudioSendStream::audio_state() {
internal::AudioState* audio_state =
static_cast<internal::AudioState*>(audio_state_.get());
RTC_DCHECK(audio_state);
return audio_state;
}
const internal::AudioState* AudioSendStream::audio_state() const {
internal::AudioState* audio_state =
static_cast<internal::AudioState*>(audio_state_.get());
RTC_DCHECK(audio_state);
return audio_state;
}
void AudioSendStream::StoreEncoderProperties(int sample_rate_hz,
size_t num_channels) {
encoder_sample_rate_hz_ = sample_rate_hz;
encoder_num_channels_ = num_channels;
if (sending_) {
// Update AudioState's information about the stream.
audio_state()->AddSendingStream(this, sample_rate_hz, num_channels);
}
}
// Apply current codec settings to a single voe::Channel used for sending.
bool AudioSendStream::SetupSendCodec(const Config& new_config) {
RTC_DCHECK(new_config.send_codec_spec);
const auto& spec = *new_config.send_codec_spec;
RTC_DCHECK(new_config.encoder_factory);
std::unique_ptr<AudioEncoder> encoder =
new_config.encoder_factory->MakeAudioEncoder(
spec.payload_type, spec.format, new_config.codec_pair_id);
if (!encoder) {
RTC_DLOG(LS_ERROR) << "Unable to create encoder for "
<< rtc::ToString(spec.format);
return false;
}
// If a bitrate has been specified for the codec, use it over the
// codec's default.
if (spec.target_bitrate_bps) {
encoder->OnReceivedTargetAudioBitrate(*spec.target_bitrate_bps);
}
// Enable ANA if configured (currently only used by Opus).
if (new_config.audio_network_adaptor_config) {
if (encoder->EnableAudioNetworkAdaptor(
*new_config.audio_network_adaptor_config, event_log_)) {
RTC_LOG(LS_INFO) << "Audio network adaptor enabled on SSRC "
<< new_config.rtp.ssrc;
} else {
RTC_LOG(LS_INFO) << "Failed to enable Audio network adaptor on SSRC "
<< new_config.rtp.ssrc;
}
}
// Wrap the encoder in an AudioEncoderCNG, if VAD is enabled.
if (spec.cng_payload_type) {
AudioEncoderCngConfig cng_config;
cng_config.num_channels = encoder->NumChannels();
cng_config.payload_type = *spec.cng_payload_type;
cng_config.speech_encoder = std::move(encoder);
cng_config.vad_mode = Vad::kVadNormal;
encoder = CreateComfortNoiseEncoder(std::move(cng_config));
RegisterCngPayloadType(*spec.cng_payload_type,
new_config.send_codec_spec->format.clockrate_hz);
}
// Wrap the encoder in a RED encoder, if RED is enabled.
SdpAudioFormat format = spec.format;
if (spec.red_payload_type) {
AudioEncoderCopyRed::Config red_config;
red_config.payload_type = *spec.red_payload_type;
red_config.speech_encoder = std::move(encoder);
encoder = std::make_unique<AudioEncoderCopyRed>(std::move(red_config),
field_trials_);
format.name = cricket::kRedCodecName;
}
// Set currently known overhead (used in ANA, opus only).
// If overhead changes later, it will be updated in UpdateOverheadPerPacket.
if (overhead_per_packet_ > 0) {
encoder->OnReceivedOverhead(overhead_per_packet_);
}
StoreEncoderProperties(encoder->SampleRateHz(), encoder->NumChannels());
channel_send_->SetEncoder(new_config.send_codec_spec->payload_type, format,
std::move(encoder));
return true;
}
bool AudioSendStream::ReconfigureSendCodec(const Config& new_config) {
const auto& old_config = config_;
if (!new_config.send_codec_spec) {
// We cannot de-configure a send codec. So we will do nothing.
// By design, the send codec should have not been configured.
RTC_DCHECK(!old_config.send_codec_spec);
return true;
}
if (new_config.send_codec_spec == old_config.send_codec_spec &&
new_config.audio_network_adaptor_config ==
old_config.audio_network_adaptor_config) {
return true;
}
// If we have no encoder, or the format or payload type's changed, create a
// new encoder.
if (!old_config.send_codec_spec ||
new_config.send_codec_spec->format !=
old_config.send_codec_spec->format ||
new_config.send_codec_spec->payload_type !=
old_config.send_codec_spec->payload_type ||
new_config.send_codec_spec->red_payload_type !=
old_config.send_codec_spec->red_payload_type) {
return SetupSendCodec(new_config);
}
const absl::optional<int>& new_target_bitrate_bps =
new_config.send_codec_spec->target_bitrate_bps;
// If a bitrate has been specified for the codec, use it over the
// codec's default.
if (new_target_bitrate_bps &&
new_target_bitrate_bps !=
old_config.send_codec_spec->target_bitrate_bps) {
channel_send_->CallEncoder([&](AudioEncoder* encoder) {
encoder->OnReceivedTargetAudioBitrate(*new_target_bitrate_bps);
});
}
ReconfigureANA(new_config);
ReconfigureCNG(new_config);
return true;
}
void AudioSendStream::ReconfigureANA(const Config& new_config) {
if (new_config.audio_network_adaptor_config ==
config_.audio_network_adaptor_config) {
return;
}
if (new_config.audio_network_adaptor_config) {
channel_send_->CallEncoder([&](AudioEncoder* encoder) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
if (encoder->EnableAudioNetworkAdaptor(
*new_config.audio_network_adaptor_config, event_log_)) {
RTC_LOG(LS_INFO) << "Audio network adaptor enabled on SSRC "
<< new_config.rtp.ssrc;
if (overhead_per_packet_ > 0) {
encoder->OnReceivedOverhead(overhead_per_packet_);
}
} else {
RTC_LOG(LS_INFO) << "Failed to enable Audio network adaptor on SSRC "
<< new_config.rtp.ssrc;
}
});
} else {
channel_send_->CallEncoder(
[&](AudioEncoder* encoder) { encoder->DisableAudioNetworkAdaptor(); });
RTC_LOG(LS_INFO) << "Audio network adaptor disabled on SSRC "
<< new_config.rtp.ssrc;
}
}
void AudioSendStream::ReconfigureCNG(const Config& new_config) {
if (new_config.send_codec_spec->cng_payload_type ==
config_.send_codec_spec->cng_payload_type) {
return;
}
// Register the CNG payload type if it's been added, don't do anything if CNG
// is removed. Payload types must not be redefined.
if (new_config.send_codec_spec->cng_payload_type) {
RegisterCngPayloadType(*new_config.send_codec_spec->cng_payload_type,
new_config.send_codec_spec->format.clockrate_hz);
}
// Wrap or unwrap the encoder in an AudioEncoderCNG.
channel_send_->ModifyEncoder([&](std::unique_ptr<AudioEncoder>* encoder_ptr) {
std::unique_ptr<AudioEncoder> old_encoder(std::move(*encoder_ptr));
auto sub_encoders = old_encoder->ReclaimContainedEncoders();
if (!sub_encoders.empty()) {
// Replace enc with its sub encoder. We need to put the sub
// encoder in a temporary first, since otherwise the old value
// of enc would be destroyed before the new value got assigned,
// which would be bad since the new value is a part of the old
// value.
auto tmp = std::move(sub_encoders[0]);
old_encoder = std::move(tmp);
}
if (new_config.send_codec_spec->cng_payload_type) {
AudioEncoderCngConfig config;
config.speech_encoder = std::move(old_encoder);
config.num_channels = config.speech_encoder->NumChannels();
config.payload_type = *new_config.send_codec_spec->cng_payload_type;
config.vad_mode = Vad::kVadNormal;
*encoder_ptr = CreateComfortNoiseEncoder(std::move(config));
} else {
*encoder_ptr = std::move(old_encoder);
}
});
}
void AudioSendStream::ReconfigureBitrateObserver(
const webrtc::AudioSendStream::Config& new_config) {
// Since the Config's default is for both of these to be -1, this test will
// allow us to configure the bitrate observer if the new config has bitrate
// limits set, but would only have us call RemoveBitrateObserver if we were
// previously configured with bitrate limits.
if (config_.min_bitrate_bps == new_config.min_bitrate_bps &&
config_.max_bitrate_bps == new_config.max_bitrate_bps &&
config_.bitrate_priority == new_config.bitrate_priority &&
TransportSeqNumId(config_) == TransportSeqNumId(new_config) &&
config_.audio_network_adaptor_config ==
new_config.audio_network_adaptor_config) {
return;
}
if (!new_config.has_dscp && new_config.min_bitrate_bps != -1 &&
new_config.max_bitrate_bps != -1 && TransportSeqNumId(new_config) != 0) {
rtp_transport_->AccountForAudioPacketsInPacedSender(true);
rtp_transport_->IncludeOverheadInPacedSender();
// We may get a callback immediately as the observer is registered, so
// make sure the bitrate limits in config_ are up-to-date.
config_.min_bitrate_bps = new_config.min_bitrate_bps;
config_.max_bitrate_bps = new_config.max_bitrate_bps;
config_.bitrate_priority = new_config.bitrate_priority;
ConfigureBitrateObserver();
rtp_rtcp_module_->SetAsPartOfAllocation(true);
} else {
rtp_transport_->AccountForAudioPacketsInPacedSender(false);
RemoveBitrateObserver();
rtp_rtcp_module_->SetAsPartOfAllocation(false);
}
}
void AudioSendStream::ConfigureBitrateObserver() {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
// This either updates the current observer or adds a new observer.
// TODO(srte): Add overhead compensation here.
auto constraints = GetMinMaxBitrateConstraints();
RTC_DCHECK(constraints.has_value());
DataRate priority_bitrate = allocation_settings_.priority_bitrate;
if (use_legacy_overhead_calculation_) {
// OverheadPerPacket = Ipv4(20B) + UDP(8B) + SRTP(10B) + RTP(12)
constexpr int kOverheadPerPacket = 20 + 8 + 10 + 12;
const TimeDelta kMinPacketDuration = TimeDelta::Millis(20);
DataRate max_overhead =
DataSize::Bytes(kOverheadPerPacket) / kMinPacketDuration;
priority_bitrate += max_overhead;
} else {
RTC_DCHECK(frame_length_range_);
const DataSize overhead_per_packet = DataSize::Bytes(overhead_per_packet_);
DataRate min_overhead = overhead_per_packet / frame_length_range_->second;
priority_bitrate += min_overhead;
}
if (allocation_settings_.priority_bitrate_raw) {
priority_bitrate = *allocation_settings_.priority_bitrate_raw;
}
if (!enable_priority_bitrate_) {
priority_bitrate = DataRate::BitsPerSec(0);
}
bitrate_allocator_->AddObserver(
this,
MediaStreamAllocationConfig{
constraints->min.bps<uint32_t>(), constraints->max.bps<uint32_t>(), 0,
priority_bitrate.bps(), true,
allocation_settings_.bitrate_priority.value_or(
config_.bitrate_priority)});
registered_with_allocator_ = true;
}
void AudioSendStream::RemoveBitrateObserver() {
registered_with_allocator_ = false;
bitrate_allocator_->RemoveObserver(this);
}
absl::optional<AudioSendStream::TargetAudioBitrateConstraints>
AudioSendStream::GetMinMaxBitrateConstraints() const {
if (config_.min_bitrate_bps < 0 || config_.max_bitrate_bps < 0) {
RTC_LOG(LS_WARNING) << "Config is invalid: min_bitrate_bps="
<< config_.min_bitrate_bps
<< "; max_bitrate_bps=" << config_.max_bitrate_bps
<< "; both expected greater or equal to 0";
return absl::nullopt;
}
TargetAudioBitrateConstraints constraints{
DataRate::BitsPerSec(config_.min_bitrate_bps),
DataRate::BitsPerSec(config_.max_bitrate_bps)};
// If bitrates were explicitly overriden via field trial, use those values.
if (allocation_settings_.min_bitrate)
constraints.min = *allocation_settings_.min_bitrate;
if (allocation_settings_.max_bitrate)
constraints.max = *allocation_settings_.max_bitrate;
// Use encoder defined bitrate range if available.
if (bitrate_range_) {
constraints.min = bitrate_range_->first;
constraints.max = bitrate_range_->second;
}
RTC_DCHECK_GE(constraints.min, DataRate::Zero());
RTC_DCHECK_GE(constraints.max, DataRate::Zero());
if (constraints.max < constraints.min) {
RTC_LOG(LS_WARNING) << "TargetAudioBitrateConstraints::max is less than "
<< "TargetAudioBitrateConstraints::min";
return absl::nullopt;
}
if (use_legacy_overhead_calculation_) {
// OverheadPerPacket = Ipv4(20B) + UDP(8B) + SRTP(10B) + RTP(12)
const DataSize kOverheadPerPacket = DataSize::Bytes(20 + 8 + 10 + 12);
const TimeDelta kMaxFrameLength =
TimeDelta::Millis(60); // Based on Opus spec
const DataRate kMinOverhead = kOverheadPerPacket / kMaxFrameLength;
constraints.min += kMinOverhead;
constraints.max += kMinOverhead;
} else {
if (!frame_length_range_.has_value()) {
RTC_LOG(LS_WARNING) << "frame_length_range_ is not set";
return absl::nullopt;
}
const DataSize overhead_per_packet = DataSize::Bytes(overhead_per_packet_);
constraints.min += overhead_per_packet / frame_length_range_->second;
constraints.max += overhead_per_packet / frame_length_range_->first;
}
return constraints;
}
void AudioSendStream::RegisterCngPayloadType(int payload_type,
int clockrate_hz) {
channel_send_->RegisterCngPayloadType(payload_type, clockrate_hz);
}
} // namespace internal
} // namespace webrtc

View file

@ -0,0 +1,225 @@
/*
* Copyright (c) 2015 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.
*/
#ifndef AUDIO_AUDIO_SEND_STREAM_H_
#define AUDIO_AUDIO_SEND_STREAM_H_
#include <memory>
#include <utility>
#include <vector>
#include "absl/functional/any_invocable.h"
#include "api/field_trials_view.h"
#include "api/sequence_checker.h"
#include "api/task_queue/task_queue_base.h"
#include "audio/audio_level.h"
#include "audio/channel_send.h"
#include "call/audio_send_stream.h"
#include "call/audio_state.h"
#include "call/bitrate_allocator.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
#include "rtc_base/experiments/struct_parameters_parser.h"
#include "rtc_base/race_checker.h"
#include "rtc_base/synchronization/mutex.h"
namespace webrtc {
class RtcEventLog;
class RtcpRttStats;
class RtpTransportControllerSendInterface;
struct AudioAllocationConfig {
static constexpr char kKey[] = "WebRTC-Audio-Allocation";
// Field Trial configured bitrates to use as overrides over default/user
// configured bitrate range when audio bitrate allocation is enabled.
absl::optional<DataRate> min_bitrate;
absl::optional<DataRate> max_bitrate;
DataRate priority_bitrate = DataRate::Zero();
// By default the priority_bitrate is compensated for packet overhead.
// Use this flag to configure a raw value instead.
absl::optional<DataRate> priority_bitrate_raw;
absl::optional<double> bitrate_priority;
std::unique_ptr<StructParametersParser> Parser();
explicit AudioAllocationConfig(const FieldTrialsView& field_trials);
};
namespace internal {
class AudioState;
class AudioSendStream final : public webrtc::AudioSendStream,
public webrtc::BitrateAllocatorObserver {
public:
AudioSendStream(Clock* clock,
const webrtc::AudioSendStream::Config& config,
const rtc::scoped_refptr<webrtc::AudioState>& audio_state,
TaskQueueFactory* task_queue_factory,
RtpTransportControllerSendInterface* rtp_transport,
BitrateAllocatorInterface* bitrate_allocator,
RtcEventLog* event_log,
RtcpRttStats* rtcp_rtt_stats,
const absl::optional<RtpState>& suspended_rtp_state,
const FieldTrialsView& field_trials);
// For unit tests, which need to supply a mock ChannelSend.
AudioSendStream(Clock* clock,
const webrtc::AudioSendStream::Config& config,
const rtc::scoped_refptr<webrtc::AudioState>& audio_state,
TaskQueueFactory* task_queue_factory,
RtpTransportControllerSendInterface* rtp_transport,
BitrateAllocatorInterface* bitrate_allocator,
RtcEventLog* event_log,
const absl::optional<RtpState>& suspended_rtp_state,
std::unique_ptr<voe::ChannelSendInterface> channel_send,
const FieldTrialsView& field_trials);
AudioSendStream() = delete;
AudioSendStream(const AudioSendStream&) = delete;
AudioSendStream& operator=(const AudioSendStream&) = delete;
~AudioSendStream() override;
// webrtc::AudioSendStream implementation.
const webrtc::AudioSendStream::Config& GetConfig() const override;
void Reconfigure(const webrtc::AudioSendStream::Config& config,
SetParametersCallback callback) override;
void Start() override;
void Stop() override;
void SendAudioData(std::unique_ptr<AudioFrame> audio_frame) override;
bool SendTelephoneEvent(int payload_type,
int payload_frequency,
int event,
int duration_ms) override;
void SetMuted(bool muted) override;
webrtc::AudioSendStream::Stats GetStats() const override;
webrtc::AudioSendStream::Stats GetStats(
bool has_remote_tracks) const override;
void DeliverRtcp(const uint8_t* packet, size_t length);
// Implements BitrateAllocatorObserver.
uint32_t OnBitrateUpdated(BitrateAllocationUpdate update) override;
void SetTransportOverhead(int transport_overhead_per_packet_bytes);
RtpState GetRtpState() const;
const voe::ChannelSendInterface* GetChannel() const;
// Returns combined per-packet overhead.
size_t TestOnlyGetPerPacketOverheadBytes() const;
private:
class TimedTransport;
// Constraints including overhead.
struct TargetAudioBitrateConstraints {
DataRate min;
DataRate max;
};
internal::AudioState* audio_state();
const internal::AudioState* audio_state() const;
void StoreEncoderProperties(int sample_rate_hz, size_t num_channels)
RTC_RUN_ON(worker_thread_checker_);
void ConfigureStream(const Config& new_config,
bool first_time,
SetParametersCallback callback)
RTC_RUN_ON(worker_thread_checker_);
bool SetupSendCodec(const Config& new_config)
RTC_RUN_ON(worker_thread_checker_);
bool ReconfigureSendCodec(const Config& new_config)
RTC_RUN_ON(worker_thread_checker_);
void ReconfigureANA(const Config& new_config)
RTC_RUN_ON(worker_thread_checker_);
void ReconfigureCNG(const Config& new_config)
RTC_RUN_ON(worker_thread_checker_);
void ReconfigureBitrateObserver(const Config& new_config)
RTC_RUN_ON(worker_thread_checker_);
void ConfigureBitrateObserver() RTC_RUN_ON(worker_thread_checker_);
void RemoveBitrateObserver() RTC_RUN_ON(worker_thread_checker_);
// Returns bitrate constraints, maybe including overhead when enabled by
// field trial.
absl::optional<TargetAudioBitrateConstraints> GetMinMaxBitrateConstraints()
const RTC_RUN_ON(worker_thread_checker_);
// Sets per-packet overhead on encoded (for ANA) based on current known values
// of transport and packetization overheads.
void UpdateOverheadPerPacket();
void RegisterCngPayloadType(int payload_type, int clockrate_hz)
RTC_RUN_ON(worker_thread_checker_);
Clock* clock_;
const FieldTrialsView& field_trials_;
SequenceChecker worker_thread_checker_;
rtc::RaceChecker audio_capture_race_checker_;
const bool allocate_audio_without_feedback_;
const bool force_no_audio_feedback_ = allocate_audio_without_feedback_;
const bool enable_audio_alr_probing_;
const AudioAllocationConfig allocation_settings_;
webrtc::AudioSendStream::Config config_
RTC_GUARDED_BY(worker_thread_checker_);
rtc::scoped_refptr<webrtc::AudioState> audio_state_;
const std::unique_ptr<voe::ChannelSendInterface> channel_send_;
RtcEventLog* const event_log_;
const bool use_legacy_overhead_calculation_;
const bool enable_priority_bitrate_;
int encoder_sample_rate_hz_ RTC_GUARDED_BY(worker_thread_checker_) = 0;
size_t encoder_num_channels_ RTC_GUARDED_BY(worker_thread_checker_) = 0;
bool sending_ RTC_GUARDED_BY(worker_thread_checker_) = false;
mutable Mutex audio_level_lock_;
// Keeps track of audio level, total audio energy and total samples duration.
// https://w3c.github.io/webrtc-stats/#dom-rtcaudiohandlerstats-totalaudioenergy
webrtc::voe::AudioLevel audio_level_ RTC_GUARDED_BY(audio_level_lock_);
BitrateAllocatorInterface* const bitrate_allocator_
RTC_GUARDED_BY(worker_thread_checker_);
RtpTransportControllerSendInterface* const rtp_transport_;
RtpRtcpInterface* const rtp_rtcp_module_;
absl::optional<RtpState> const suspended_rtp_state_;
// RFC 5285: Each distinct extension MUST have a unique ID. The value 0 is
// reserved for padding and MUST NOT be used as a local identifier.
// So it should be safe to use 0 here to indicate "not configured".
struct ExtensionIds {
int audio_level = 0;
int abs_send_time = 0;
int abs_capture_time = 0;
int transport_sequence_number = 0;
int mid = 0;
int rid = 0;
int repaired_rid = 0;
};
static ExtensionIds FindExtensionIds(
const std::vector<RtpExtension>& extensions);
static int TransportSeqNumId(const Config& config);
// Current transport overhead (ICE, TURN, etc.)
size_t transport_overhead_per_packet_bytes_
RTC_GUARDED_BY(worker_thread_checker_) = 0;
// Total overhead, including transport and RTP headers.
size_t overhead_per_packet_ RTC_GUARDED_BY(worker_thread_checker_) = 0;
bool registered_with_allocator_ RTC_GUARDED_BY(worker_thread_checker_) =
false;
absl::optional<std::pair<TimeDelta, TimeDelta>> frame_length_range_
RTC_GUARDED_BY(worker_thread_checker_);
absl::optional<std::pair<DataRate, DataRate>> bitrate_range_
RTC_GUARDED_BY(worker_thread_checker_);
};
} // namespace internal
} // namespace webrtc
#endif // AUDIO_AUDIO_SEND_STREAM_H_

View file

@ -0,0 +1,212 @@
/*
* Copyright (c) 2015 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 "audio/audio_state.h"
#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include "api/sequence_checker.h"
#include "api/task_queue/task_queue_base.h"
#include "api/units/time_delta.h"
#include "audio/audio_receive_stream.h"
#include "audio/audio_send_stream.h"
#include "modules/audio_device/include/audio_device.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace internal {
AudioState::AudioState(const AudioState::Config& config)
: config_(config),
audio_transport_(config_.audio_mixer.get(),
config_.audio_processing.get(),
config_.async_audio_processing_factory.get()) {
RTC_DCHECK(config_.audio_mixer);
RTC_DCHECK(config_.audio_device_module);
}
AudioState::~AudioState() {
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK(receiving_streams_.empty());
RTC_DCHECK(sending_streams_.empty());
RTC_DCHECK(!null_audio_poller_.Running());
}
AudioProcessing* AudioState::audio_processing() {
return config_.audio_processing.get();
}
AudioTransport* AudioState::audio_transport() {
return &audio_transport_;
}
void AudioState::AddReceivingStream(
webrtc::AudioReceiveStreamInterface* stream) {
RTC_DCHECK_RUN_ON(&thread_checker_);
RTC_DCHECK_EQ(0, receiving_streams_.count(stream));
receiving_streams_.insert(stream);
if (!config_.audio_mixer->AddSource(
static_cast<AudioReceiveStreamImpl*>(stream))) {
RTC_DLOG(LS_ERROR) << "Failed to add source to mixer.";
}
// Make sure playback is initialized; start playing if enabled.
UpdateNullAudioPollerState();
auto* adm = config_.audio_device_module.get();
if (!adm->Playing()) {
if (adm->InitPlayout() == 0) {
if (playout_enabled_) {
adm->StartPlayout();
}
} else {
RTC_DLOG_F(LS_ERROR) << "Failed to initialize playout.";
}
}
}
void AudioState::RemoveReceivingStream(
webrtc::AudioReceiveStreamInterface* stream) {
RTC_DCHECK_RUN_ON(&thread_checker_);
auto count = receiving_streams_.erase(stream);
RTC_DCHECK_EQ(1, count);
config_.audio_mixer->RemoveSource(
static_cast<AudioReceiveStreamImpl*>(stream));
UpdateNullAudioPollerState();
if (receiving_streams_.empty()) {
config_.audio_device_module->StopPlayout();
}
}
void AudioState::AddSendingStream(webrtc::AudioSendStream* stream,
int sample_rate_hz,
size_t num_channels) {
RTC_DCHECK_RUN_ON(&thread_checker_);
auto& properties = sending_streams_[stream];
properties.sample_rate_hz = sample_rate_hz;
properties.num_channels = num_channels;
UpdateAudioTransportWithSendingStreams();
// Make sure recording is initialized; start recording if enabled.
auto* adm = config_.audio_device_module.get();
if (!adm->Recording()) {
if (adm->InitRecording() == 0) {
if (recording_enabled_) {
adm->StartRecording();
}
} else {
RTC_DLOG_F(LS_ERROR) << "Failed to initialize recording.";
}
}
}
void AudioState::RemoveSendingStream(webrtc::AudioSendStream* stream) {
RTC_DCHECK_RUN_ON(&thread_checker_);
auto count = sending_streams_.erase(stream);
RTC_DCHECK_EQ(1, count);
UpdateAudioTransportWithSendingStreams();
if (sending_streams_.empty()) {
config_.audio_device_module->StopRecording();
}
}
void AudioState::SetPlayout(bool enabled) {
RTC_LOG(LS_INFO) << "SetPlayout(" << enabled << ")";
RTC_DCHECK_RUN_ON(&thread_checker_);
if (playout_enabled_ != enabled) {
playout_enabled_ = enabled;
if (enabled) {
UpdateNullAudioPollerState();
if (!receiving_streams_.empty()) {
config_.audio_device_module->StartPlayout();
}
} else {
config_.audio_device_module->StopPlayout();
UpdateNullAudioPollerState();
}
}
}
void AudioState::SetRecording(bool enabled) {
RTC_LOG(LS_INFO) << "SetRecording(" << enabled << ")";
RTC_DCHECK_RUN_ON(&thread_checker_);
if (recording_enabled_ != enabled) {
recording_enabled_ = enabled;
if (enabled) {
if (!sending_streams_.empty()) {
config_.audio_device_module->StartRecording();
}
} else {
config_.audio_device_module->StopRecording();
}
}
}
void AudioState::SetStereoChannelSwapping(bool enable) {
RTC_DCHECK(thread_checker_.IsCurrent());
audio_transport_.SetStereoChannelSwapping(enable);
}
void AudioState::UpdateAudioTransportWithSendingStreams() {
RTC_DCHECK(thread_checker_.IsCurrent());
std::vector<AudioSender*> audio_senders;
int max_sample_rate_hz = 8000;
size_t max_num_channels = 1;
for (const auto& kv : sending_streams_) {
audio_senders.push_back(kv.first);
max_sample_rate_hz = std::max(max_sample_rate_hz, kv.second.sample_rate_hz);
max_num_channels = std::max(max_num_channels, kv.second.num_channels);
}
audio_transport_.UpdateAudioSenders(std::move(audio_senders),
max_sample_rate_hz, max_num_channels);
}
void AudioState::UpdateNullAudioPollerState() {
// Run NullAudioPoller when there are receiving streams and playout is
// disabled.
if (!receiving_streams_.empty() && !playout_enabled_) {
if (!null_audio_poller_.Running()) {
AudioTransport* audio_transport = &audio_transport_;
null_audio_poller_ = RepeatingTaskHandle::Start(
TaskQueueBase::Current(), [audio_transport] {
static constexpr size_t kNumChannels = 1;
static constexpr uint32_t kSamplesPerSecond = 48'000;
// 10ms of samples
static constexpr size_t kNumSamples = kSamplesPerSecond / 100;
// Buffer to hold the audio samples.
int16_t buffer[kNumSamples * kNumChannels];
// Output variables from `NeedMorePlayData`.
size_t n_samples;
int64_t elapsed_time_ms;
int64_t ntp_time_ms;
audio_transport->NeedMorePlayData(
kNumSamples, sizeof(int16_t), kNumChannels, kSamplesPerSecond,
buffer, n_samples, &elapsed_time_ms, &ntp_time_ms);
// Reschedule the next poll iteration.
return TimeDelta::Millis(10);
});
}
} else {
null_audio_poller_.Stop();
}
}
} // namespace internal
rtc::scoped_refptr<AudioState> AudioState::Create(
const AudioState::Config& config) {
return rtc::make_ref_counted<internal::AudioState>(config);
}
} // namespace webrtc

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2015 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.
*/
#ifndef AUDIO_AUDIO_STATE_H_
#define AUDIO_AUDIO_STATE_H_
#include <map>
#include <memory>
#include "api/sequence_checker.h"
#include "audio/audio_transport_impl.h"
#include "call/audio_state.h"
#include "rtc_base/containers/flat_set.h"
#include "rtc_base/ref_count.h"
#include "rtc_base/task_utils/repeating_task.h"
#include "rtc_base/thread_annotations.h"
namespace webrtc {
class AudioSendStream;
class AudioReceiveStreamInterface;
namespace internal {
class AudioState : public webrtc::AudioState {
public:
explicit AudioState(const AudioState::Config& config);
AudioState() = delete;
AudioState(const AudioState&) = delete;
AudioState& operator=(const AudioState&) = delete;
~AudioState() override;
AudioProcessing* audio_processing() override;
AudioTransport* audio_transport() override;
void SetPlayout(bool enabled) override;
void SetRecording(bool enabled) override;
void SetStereoChannelSwapping(bool enable) override;
AudioDeviceModule* audio_device_module() {
RTC_DCHECK(config_.audio_device_module);
return config_.audio_device_module.get();
}
void AddReceivingStream(webrtc::AudioReceiveStreamInterface* stream);
void RemoveReceivingStream(webrtc::AudioReceiveStreamInterface* stream);
void AddSendingStream(webrtc::AudioSendStream* stream,
int sample_rate_hz,
size_t num_channels);
void RemoveSendingStream(webrtc::AudioSendStream* stream);
private:
void UpdateAudioTransportWithSendingStreams();
void UpdateNullAudioPollerState() RTC_RUN_ON(&thread_checker_);
SequenceChecker thread_checker_;
SequenceChecker process_thread_checker_{SequenceChecker::kDetached};
const webrtc::AudioState::Config config_;
bool recording_enabled_ = true;
bool playout_enabled_ = true;
// Transports mixed audio from the mixer to the audio device and
// recorded audio to the sending streams.
AudioTransportImpl audio_transport_;
// Null audio poller is used to continue polling the audio streams if audio
// playout is disabled so that audio processing still happens and the audio
// stats are still updated.
RepeatingTaskHandle null_audio_poller_ RTC_GUARDED_BY(&thread_checker_);
webrtc::flat_set<webrtc::AudioReceiveStreamInterface*> receiving_streams_;
struct StreamProperties {
int sample_rate_hz = 0;
size_t num_channels = 0;
};
std::map<webrtc::AudioSendStream*, StreamProperties> sending_streams_;
};
} // namespace internal
} // namespace webrtc
#endif // AUDIO_AUDIO_STATE_H_

View file

@ -0,0 +1,285 @@
/*
* Copyright (c) 2016 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 "audio/audio_transport_impl.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "audio/remix_resample.h"
#include "audio/utility/audio_frame_operations.h"
#include "call/audio_sender.h"
#include "modules/async_audio_processing/async_audio_processing.h"
#include "modules/audio_processing/include/audio_frame_proxies.h"
#include "rtc_base/checks.h"
#include "rtc_base/trace_event.h"
namespace webrtc {
namespace {
// We want to process at the lowest sample rate and channel count possible
// without losing information. Choose the lowest native rate at least equal to
// the minimum of input and codec rates, choose lowest channel count, and
// configure the audio frame.
void InitializeCaptureFrame(int input_sample_rate,
int send_sample_rate_hz,
size_t input_num_channels,
size_t send_num_channels,
AudioFrame* audio_frame) {
RTC_DCHECK(audio_frame);
int min_processing_rate_hz = std::min(input_sample_rate, send_sample_rate_hz);
for (int native_rate_hz : AudioProcessing::kNativeSampleRatesHz) {
audio_frame->sample_rate_hz_ = native_rate_hz;
if (audio_frame->sample_rate_hz_ >= min_processing_rate_hz) {
break;
}
}
audio_frame->num_channels_ = std::min(input_num_channels, send_num_channels);
}
void ProcessCaptureFrame(uint32_t delay_ms,
bool key_pressed,
bool swap_stereo_channels,
AudioProcessing* audio_processing,
AudioFrame* audio_frame) {
RTC_DCHECK(audio_frame);
if (audio_processing) {
audio_processing->set_stream_delay_ms(delay_ms);
audio_processing->set_stream_key_pressed(key_pressed);
int error = ProcessAudioFrame(audio_processing, audio_frame);
RTC_DCHECK_EQ(0, error) << "ProcessStream() error: " << error;
}
if (swap_stereo_channels) {
AudioFrameOperations::SwapStereoChannels(audio_frame);
}
}
// Resample audio in `frame` to given sample rate preserving the
// channel count and place the result in `destination`.
int Resample(const AudioFrame& frame,
const int destination_sample_rate,
PushResampler<int16_t>* resampler,
int16_t* destination) {
TRACE_EVENT2("webrtc", "Resample", "frame sample rate", frame.sample_rate_hz_,
"destination_sample_rate", destination_sample_rate);
const int number_of_channels = static_cast<int>(frame.num_channels_);
const int target_number_of_samples_per_channel =
destination_sample_rate / 100;
resampler->InitializeIfNeeded(frame.sample_rate_hz_, destination_sample_rate,
number_of_channels);
// TODO(yujo): make resampler take an AudioFrame, and add special case
// handling of muted frames.
return resampler->Resample(
frame.data(), frame.samples_per_channel_ * number_of_channels,
destination, number_of_channels * target_number_of_samples_per_channel);
}
} // namespace
AudioTransportImpl::AudioTransportImpl(
AudioMixer* mixer,
AudioProcessing* audio_processing,
AsyncAudioProcessing::Factory* async_audio_processing_factory)
: audio_processing_(audio_processing),
async_audio_processing_(
async_audio_processing_factory
? async_audio_processing_factory->CreateAsyncAudioProcessing(
[this](std::unique_ptr<AudioFrame> frame) {
this->SendProcessedData(std::move(frame));
})
: nullptr),
mixer_(mixer) {
RTC_DCHECK(mixer);
}
AudioTransportImpl::~AudioTransportImpl() {}
int32_t AudioTransportImpl::RecordedDataIsAvailable(
const void* audio_data,
size_t number_of_frames,
size_t bytes_per_sample,
size_t number_of_channels,
uint32_t sample_rate,
uint32_t audio_delay_milliseconds,
int32_t clock_drift,
uint32_t volume,
bool key_pressed,
uint32_t& new_mic_volume) { // NOLINT: to avoid changing APIs
return RecordedDataIsAvailable(
audio_data, number_of_frames, bytes_per_sample, number_of_channels,
sample_rate, audio_delay_milliseconds, clock_drift, volume, key_pressed,
new_mic_volume, /*estimated_capture_time_ns=*/absl::nullopt);
}
// Not used in Chromium. Process captured audio and distribute to all sending
// streams, and try to do this at the lowest possible sample rate.
int32_t AudioTransportImpl::RecordedDataIsAvailable(
const void* audio_data,
size_t number_of_frames,
size_t bytes_per_sample,
size_t number_of_channels,
uint32_t sample_rate,
uint32_t audio_delay_milliseconds,
int32_t /*clock_drift*/,
uint32_t /*volume*/,
bool key_pressed,
uint32_t& /*new_mic_volume*/,
absl::optional<int64_t>
estimated_capture_time_ns) { // NOLINT: to avoid changing APIs
RTC_DCHECK(audio_data);
RTC_DCHECK_GE(number_of_channels, 1);
RTC_DCHECK_LE(number_of_channels, 2);
RTC_DCHECK_EQ(2 * number_of_channels, bytes_per_sample);
RTC_DCHECK_GE(sample_rate, AudioProcessing::NativeRate::kSampleRate8kHz);
// 100 = 1 second / data duration (10 ms).
RTC_DCHECK_EQ(number_of_frames * 100, sample_rate);
RTC_DCHECK_LE(bytes_per_sample * number_of_frames * number_of_channels,
AudioFrame::kMaxDataSizeBytes);
int send_sample_rate_hz = 0;
size_t send_num_channels = 0;
bool swap_stereo_channels = false;
{
MutexLock lock(&capture_lock_);
send_sample_rate_hz = send_sample_rate_hz_;
send_num_channels = send_num_channels_;
swap_stereo_channels = swap_stereo_channels_;
}
std::unique_ptr<AudioFrame> audio_frame(new AudioFrame());
InitializeCaptureFrame(sample_rate, send_sample_rate_hz, number_of_channels,
send_num_channels, audio_frame.get());
voe::RemixAndResample(static_cast<const int16_t*>(audio_data),
number_of_frames, number_of_channels, sample_rate,
&capture_resampler_, audio_frame.get());
ProcessCaptureFrame(audio_delay_milliseconds, key_pressed,
swap_stereo_channels, audio_processing_,
audio_frame.get());
if (estimated_capture_time_ns) {
audio_frame->set_absolute_capture_timestamp_ms(*estimated_capture_time_ns /
1000000);
}
RTC_DCHECK_GT(audio_frame->samples_per_channel_, 0);
if (async_audio_processing_)
async_audio_processing_->Process(std::move(audio_frame));
else
SendProcessedData(std::move(audio_frame));
return 0;
}
void AudioTransportImpl::SendProcessedData(
std::unique_ptr<AudioFrame> audio_frame) {
TRACE_EVENT0("webrtc", "AudioTransportImpl::SendProcessedData");
RTC_DCHECK_GT(audio_frame->samples_per_channel_, 0);
MutexLock lock(&capture_lock_);
if (audio_senders_.empty())
return;
auto it = audio_senders_.begin();
while (++it != audio_senders_.end()) {
auto audio_frame_copy = std::make_unique<AudioFrame>();
audio_frame_copy->CopyFrom(*audio_frame);
(*it)->SendAudioData(std::move(audio_frame_copy));
}
// Send the original frame to the first stream w/o copying.
(*audio_senders_.begin())->SendAudioData(std::move(audio_frame));
}
// Mix all received streams, feed the result to the AudioProcessing module, then
// resample the result to the requested output rate.
int32_t AudioTransportImpl::NeedMorePlayData(const size_t nSamples,
const size_t nBytesPerSample,
const size_t nChannels,
const uint32_t samplesPerSec,
void* audioSamples,
size_t& nSamplesOut,
int64_t* elapsed_time_ms,
int64_t* ntp_time_ms) {
TRACE_EVENT0("webrtc", "AudioTransportImpl::SendProcessedData");
RTC_DCHECK_EQ(sizeof(int16_t) * nChannels, nBytesPerSample);
RTC_DCHECK_GE(nChannels, 1);
RTC_DCHECK_LE(nChannels, 2);
RTC_DCHECK_GE(
samplesPerSec,
static_cast<uint32_t>(AudioProcessing::NativeRate::kSampleRate8kHz));
// 100 = 1 second / data duration (10 ms).
RTC_DCHECK_EQ(nSamples * 100, samplesPerSec);
RTC_DCHECK_LE(nBytesPerSample * nSamples * nChannels,
AudioFrame::kMaxDataSizeBytes);
mixer_->Mix(nChannels, &mixed_frame_);
*elapsed_time_ms = mixed_frame_.elapsed_time_ms_;
*ntp_time_ms = mixed_frame_.ntp_time_ms_;
if (audio_processing_) {
const auto error =
ProcessReverseAudioFrame(audio_processing_, &mixed_frame_);
RTC_DCHECK_EQ(error, AudioProcessing::kNoError);
}
nSamplesOut = Resample(mixed_frame_, samplesPerSec, &render_resampler_,
static_cast<int16_t*>(audioSamples));
RTC_DCHECK_EQ(nSamplesOut, nChannels * nSamples);
return 0;
}
// Used by Chromium - same as NeedMorePlayData() but because Chrome has its
// own APM instance, does not call audio_processing_->ProcessReverseStream().
void AudioTransportImpl::PullRenderData(int bits_per_sample,
int sample_rate,
size_t number_of_channels,
size_t number_of_frames,
void* audio_data,
int64_t* elapsed_time_ms,
int64_t* ntp_time_ms) {
TRACE_EVENT2("webrtc", "AudioTransportImpl::PullRenderData", "sample_rate",
sample_rate, "number_of_frames", number_of_frames);
RTC_DCHECK_EQ(bits_per_sample, 16);
RTC_DCHECK_GE(number_of_channels, 1);
RTC_DCHECK_GE(sample_rate, AudioProcessing::NativeRate::kSampleRate8kHz);
// 100 = 1 second / data duration (10 ms).
RTC_DCHECK_EQ(number_of_frames * 100, sample_rate);
// 8 = bits per byte.
RTC_DCHECK_LE(bits_per_sample / 8 * number_of_frames * number_of_channels,
AudioFrame::kMaxDataSizeBytes);
mixer_->Mix(number_of_channels, &mixed_frame_);
*elapsed_time_ms = mixed_frame_.elapsed_time_ms_;
*ntp_time_ms = mixed_frame_.ntp_time_ms_;
auto output_samples = Resample(mixed_frame_, sample_rate, &render_resampler_,
static_cast<int16_t*>(audio_data));
RTC_DCHECK_EQ(output_samples, number_of_channels * number_of_frames);
}
void AudioTransportImpl::UpdateAudioSenders(std::vector<AudioSender*> senders,
int send_sample_rate_hz,
size_t send_num_channels) {
MutexLock lock(&capture_lock_);
audio_senders_ = std::move(senders);
send_sample_rate_hz_ = send_sample_rate_hz;
send_num_channels_ = send_num_channels;
}
void AudioTransportImpl::SetStereoChannelSwapping(bool enable) {
MutexLock lock(&capture_lock_);
swap_stereo_channels_ = enable;
}
} // namespace webrtc

View file

@ -0,0 +1,117 @@
/*
* Copyright (c) 2016 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.
*/
#ifndef AUDIO_AUDIO_TRANSPORT_IMPL_H_
#define AUDIO_AUDIO_TRANSPORT_IMPL_H_
#include <memory>
#include <vector>
#include "api/audio/audio_mixer.h"
#include "api/scoped_refptr.h"
#include "common_audio/resampler/include/push_resampler.h"
#include "modules/async_audio_processing/async_audio_processing.h"
#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_processing/include/audio_processing.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h"
namespace webrtc {
class AudioSender;
class AudioTransportImpl : public AudioTransport {
public:
AudioTransportImpl(
AudioMixer* mixer,
AudioProcessing* audio_processing,
AsyncAudioProcessing::Factory* async_audio_processing_factory);
AudioTransportImpl() = delete;
AudioTransportImpl(const AudioTransportImpl&) = delete;
AudioTransportImpl& operator=(const AudioTransportImpl&) = delete;
~AudioTransportImpl() override;
// TODO(bugs.webrtc.org/13620) Deprecate this function
int32_t RecordedDataIsAvailable(const void* audioSamples,
size_t nSamples,
size_t nBytesPerSample,
size_t nChannels,
uint32_t samplesPerSec,
uint32_t totalDelayMS,
int32_t clockDrift,
uint32_t currentMicLevel,
bool keyPressed,
uint32_t& newMicLevel) override;
int32_t RecordedDataIsAvailable(
const void* audioSamples,
size_t nSamples,
size_t nBytesPerSample,
size_t nChannels,
uint32_t samplesPerSec,
uint32_t totalDelayMS,
int32_t clockDrift,
uint32_t currentMicLevel,
bool keyPressed,
uint32_t& newMicLevel,
absl::optional<int64_t> estimated_capture_time_ns) override;
int32_t NeedMorePlayData(size_t nSamples,
size_t nBytesPerSample,
size_t nChannels,
uint32_t samplesPerSec,
void* audioSamples,
size_t& nSamplesOut,
int64_t* elapsed_time_ms,
int64_t* ntp_time_ms) override;
void PullRenderData(int bits_per_sample,
int sample_rate,
size_t number_of_channels,
size_t number_of_frames,
void* audio_data,
int64_t* elapsed_time_ms,
int64_t* ntp_time_ms) override;
void UpdateAudioSenders(std::vector<AudioSender*> senders,
int send_sample_rate_hz,
size_t send_num_channels);
void SetStereoChannelSwapping(bool enable);
private:
void SendProcessedData(std::unique_ptr<AudioFrame> audio_frame);
// Shared.
AudioProcessing* audio_processing_ = nullptr;
// Capture side.
// Thread-safe.
const std::unique_ptr<AsyncAudioProcessing> async_audio_processing_;
mutable Mutex capture_lock_;
std::vector<AudioSender*> audio_senders_ RTC_GUARDED_BY(capture_lock_);
int send_sample_rate_hz_ RTC_GUARDED_BY(capture_lock_) = 8000;
size_t send_num_channels_ RTC_GUARDED_BY(capture_lock_) = 1;
bool swap_stereo_channels_ RTC_GUARDED_BY(capture_lock_) = false;
PushResampler<int16_t> capture_resampler_;
// Render side.
rtc::scoped_refptr<AudioMixer> mixer_;
AudioFrame mixed_frame_;
// Converts mixed audio to the audio device output rate.
PushResampler<int16_t> render_resampler_;
};
} // namespace webrtc
#endif // AUDIO_AUDIO_TRANSPORT_IMPL_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,194 @@
/*
* Copyright (c) 2012 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.
*/
#ifndef AUDIO_CHANNEL_RECEIVE_H_
#define AUDIO_CHANNEL_RECEIVE_H_
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include "absl/types/optional.h"
#include "api/audio/audio_mixer.h"
#include "api/audio_codecs/audio_decoder_factory.h"
#include "api/call/audio_sink.h"
#include "api/call/transport.h"
#include "api/crypto/crypto_options.h"
#include "api/frame_transformer_interface.h"
#include "api/neteq/neteq_factory.h"
#include "api/transport/rtp/rtp_source.h"
#include "call/rtp_packet_sink_interface.h"
#include "call/syncable.h"
#include "modules/audio_coding/include/audio_coding_module_typedefs.h"
#include "modules/rtp_rtcp/source/source_tracker.h"
#include "system_wrappers/include/clock.h"
// TODO(solenberg, nisse): This file contains a few NOLINT marks, to silence
// warnings about use of unsigned short.
// These need cleanup, in a separate cl.
namespace rtc {
class TimestampWrapAroundHandler;
}
namespace webrtc {
class AudioDeviceModule;
class FrameDecryptorInterface;
class PacketRouter;
class RateLimiter;
class ReceiveStatistics;
class RtcEventLog;
class RtpPacketReceived;
class RtpRtcp;
struct CallReceiveStatistics {
int cumulativeLost;
unsigned int jitterSamples;
int64_t payload_bytes_received = 0;
int64_t header_and_padding_bytes_received = 0;
int packetsReceived;
uint32_t nacks_sent = 0;
// The capture NTP time (in local timebase) of the first played out audio
// frame.
int64_t capture_start_ntp_time_ms_;
// The timestamp at which the last packet was received, i.e. the time of the
// local clock when it was received - not the RTP timestamp of that packet.
// https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-lastpacketreceivedtimestamp
absl::optional<Timestamp> last_packet_received;
// Remote outbound stats derived by the received RTCP sender reports.
// Note that the timestamps below correspond to the time elapsed since the
// Unix epoch.
// https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*
absl::optional<int64_t> last_sender_report_timestamp_ms;
absl::optional<int64_t> last_sender_report_remote_timestamp_ms;
uint64_t sender_reports_packets_sent = 0;
uint64_t sender_reports_bytes_sent = 0;
uint64_t sender_reports_reports_count = 0;
absl::optional<TimeDelta> round_trip_time;
TimeDelta total_round_trip_time = TimeDelta::Zero();
int round_trip_time_measurements;
};
namespace voe {
class ChannelSendInterface;
// Interface class needed for AudioReceiveStreamInterface tests that use a
// MockChannelReceive.
class ChannelReceiveInterface : public RtpPacketSinkInterface {
public:
virtual ~ChannelReceiveInterface() = default;
virtual void SetSink(AudioSinkInterface* sink) = 0;
virtual void SetReceiveCodecs(
const std::map<int, SdpAudioFormat>& codecs) = 0;
virtual void StartPlayout() = 0;
virtual void StopPlayout() = 0;
// Payload type and format of last received RTP packet, if any.
virtual absl::optional<std::pair<int, SdpAudioFormat>> GetReceiveCodec()
const = 0;
virtual void ReceivedRTCPPacket(const uint8_t* data, size_t length) = 0;
virtual void SetChannelOutputVolumeScaling(float scaling) = 0;
virtual int GetSpeechOutputLevelFullRange() const = 0;
// See description of "totalAudioEnergy" in the WebRTC stats spec:
// https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-totalaudioenergy
virtual double GetTotalOutputEnergy() const = 0;
virtual double GetTotalOutputDuration() const = 0;
// Stats.
virtual NetworkStatistics GetNetworkStatistics(
bool get_and_clear_legacy_stats) const = 0;
virtual AudioDecodingCallStats GetDecodingCallStatistics() const = 0;
// Audio+Video Sync.
virtual uint32_t GetDelayEstimate() const = 0;
virtual bool SetMinimumPlayoutDelay(int delay_ms) = 0;
virtual bool GetPlayoutRtpTimestamp(uint32_t* rtp_timestamp,
int64_t* time_ms) const = 0;
virtual void SetEstimatedPlayoutNtpTimestampMs(int64_t ntp_timestamp_ms,
int64_t time_ms) = 0;
virtual absl::optional<int64_t> GetCurrentEstimatedPlayoutNtpTimestampMs(
int64_t now_ms) const = 0;
// Audio quality.
// Base minimum delay sets lower bound on minimum delay value which
// determines minimum delay until audio playout.
virtual bool SetBaseMinimumPlayoutDelayMs(int delay_ms) = 0;
virtual int GetBaseMinimumPlayoutDelayMs() const = 0;
// Produces the transport-related timestamps; current_delay_ms is left unset.
virtual absl::optional<Syncable::Info> GetSyncInfo() const = 0;
virtual void RegisterReceiverCongestionControlObjects(
PacketRouter* packet_router) = 0;
virtual void ResetReceiverCongestionControlObjects() = 0;
virtual CallReceiveStatistics GetRTCPStatistics() const = 0;
virtual void SetNACKStatus(bool enable, int max_packets) = 0;
virtual void SetNonSenderRttMeasurement(bool enabled) = 0;
virtual AudioMixer::Source::AudioFrameInfo GetAudioFrameWithInfo(
int sample_rate_hz,
AudioFrame* audio_frame) = 0;
virtual int PreferredSampleRate() const = 0;
// Sets the source tracker to notify about "delivered" packets when output is
// muted.
virtual void SetSourceTracker(SourceTracker* source_tracker) = 0;
// Associate to a send channel.
// Used for obtaining RTT for a receive-only channel.
virtual void SetAssociatedSendChannel(
const ChannelSendInterface* channel) = 0;
// Sets a frame transformer between the depacketizer and the decoder, to
// transform the received frames before decoding them.
virtual void SetDepacketizerToDecoderFrameTransformer(
rtc::scoped_refptr<webrtc::FrameTransformerInterface>
frame_transformer) = 0;
virtual void SetFrameDecryptor(
rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor) = 0;
virtual void OnLocalSsrcChange(uint32_t local_ssrc) = 0;
virtual uint32_t GetLocalSsrc() const = 0;
};
std::unique_ptr<ChannelReceiveInterface> CreateChannelReceive(
Clock* clock,
NetEqFactory* neteq_factory,
AudioDeviceModule* audio_device_module,
Transport* rtcp_send_transport,
RtcEventLog* rtc_event_log,
uint32_t local_ssrc,
uint32_t remote_ssrc,
size_t jitter_buffer_max_packets,
bool jitter_buffer_fast_playout,
int jitter_buffer_min_delay_ms,
bool enable_non_sender_rtt,
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory,
absl::optional<AudioCodecPairId> codec_pair_id,
rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor,
const webrtc::CryptoOptions& crypto_options,
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer);
} // namespace voe
} // namespace webrtc
#endif // AUDIO_CHANNEL_RECEIVE_H_

View file

@ -0,0 +1,167 @@
/*
* Copyright (c) 2020 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 "audio/channel_receive_frame_transformer_delegate.h"
#include <string>
#include <utility>
#include "rtc_base/buffer.h"
namespace webrtc {
namespace {
class TransformableIncomingAudioFrame
: public TransformableAudioFrameInterface {
public:
TransformableIncomingAudioFrame(rtc::ArrayView<const uint8_t> payload,
const RTPHeader& header,
uint32_t ssrc,
const std::string& codec_mime_type)
: payload_(payload.data(), payload.size()),
header_(header),
ssrc_(ssrc),
codec_mime_type_(codec_mime_type) {}
~TransformableIncomingAudioFrame() override = default;
rtc::ArrayView<const uint8_t> GetData() const override { return payload_; }
void SetData(rtc::ArrayView<const uint8_t> data) override {
payload_.SetData(data.data(), data.size());
}
void SetRTPTimestamp(uint32_t timestamp) override {
header_.timestamp = timestamp;
}
uint8_t GetPayloadType() const override { return header_.payloadType; }
uint32_t GetSsrc() const override { return ssrc_; }
uint32_t GetTimestamp() const override { return header_.timestamp; }
rtc::ArrayView<const uint32_t> GetContributingSources() const override {
return rtc::ArrayView<const uint32_t>(header_.arrOfCSRCs, header_.numCSRCs);
}
Direction GetDirection() const override { return Direction::kReceiver; }
std::string GetMimeType() const override { return codec_mime_type_; }
const absl::optional<uint16_t> SequenceNumber() const override {
return header_.sequenceNumber;
}
absl::optional<uint64_t> AbsoluteCaptureTimestamp() const override {
// This could be extracted from received header extensions + extrapolation,
// if required in future, eg for being able to re-send received frames.
return absl::nullopt;
}
const RTPHeader& Header() const { return header_; }
FrameType Type() const override {
return header_.extension.voiceActivity ? FrameType::kAudioFrameSpeech
: FrameType::kAudioFrameCN;
}
private:
rtc::Buffer payload_;
RTPHeader header_;
uint32_t ssrc_;
std::string codec_mime_type_;
};
} // namespace
ChannelReceiveFrameTransformerDelegate::ChannelReceiveFrameTransformerDelegate(
ReceiveFrameCallback receive_frame_callback,
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
TaskQueueBase* channel_receive_thread)
: receive_frame_callback_(receive_frame_callback),
frame_transformer_(std::move(frame_transformer)),
channel_receive_thread_(channel_receive_thread) {}
void ChannelReceiveFrameTransformerDelegate::Init() {
RTC_DCHECK_RUN_ON(&sequence_checker_);
frame_transformer_->RegisterTransformedFrameCallback(
rtc::scoped_refptr<TransformedFrameCallback>(this));
}
void ChannelReceiveFrameTransformerDelegate::Reset() {
RTC_DCHECK_RUN_ON(&sequence_checker_);
frame_transformer_->UnregisterTransformedFrameCallback();
frame_transformer_ = nullptr;
receive_frame_callback_ = ReceiveFrameCallback();
}
void ChannelReceiveFrameTransformerDelegate::Transform(
rtc::ArrayView<const uint8_t> packet,
const RTPHeader& header,
uint32_t ssrc,
const std::string& codec_mime_type) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
if (short_circuit_) {
receive_frame_callback_(packet, header);
} else {
frame_transformer_->Transform(
std::make_unique<TransformableIncomingAudioFrame>(packet, header, ssrc,
codec_mime_type));
}
}
void ChannelReceiveFrameTransformerDelegate::OnTransformedFrame(
std::unique_ptr<TransformableFrameInterface> frame) {
rtc::scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate(this);
channel_receive_thread_->PostTask(
[delegate = std::move(delegate), frame = std::move(frame)]() mutable {
delegate->ReceiveFrame(std::move(frame));
});
}
void ChannelReceiveFrameTransformerDelegate::StartShortCircuiting() {
rtc::scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate(this);
channel_receive_thread_->PostTask([delegate = std::move(delegate)]() mutable {
RTC_DCHECK_RUN_ON(&delegate->sequence_checker_);
delegate->short_circuit_ = true;
});
}
void ChannelReceiveFrameTransformerDelegate::ReceiveFrame(
std::unique_ptr<TransformableFrameInterface> frame) const {
RTC_DCHECK_RUN_ON(&sequence_checker_);
if (!receive_frame_callback_)
return;
RTPHeader header;
if (frame->GetDirection() ==
TransformableFrameInterface::Direction::kSender) {
auto* transformed_frame =
static_cast<TransformableAudioFrameInterface*>(frame.get());
header.payloadType = transformed_frame->GetPayloadType();
header.timestamp = transformed_frame->GetTimestamp();
header.ssrc = transformed_frame->GetSsrc();
if (transformed_frame->AbsoluteCaptureTimestamp().has_value()) {
header.extension.absolute_capture_time = AbsoluteCaptureTime();
header.extension.absolute_capture_time->absolute_capture_timestamp =
transformed_frame->AbsoluteCaptureTimestamp().value();
}
} else {
auto* transformed_frame =
static_cast<TransformableIncomingAudioFrame*>(frame.get());
header = transformed_frame->Header();
}
// TODO(crbug.com/1464860): Take an explicit struct with the required
// information rather than the RTPHeader to make it easier to
// construct the required information when injecting transformed frames not
// originally from this receiver.
receive_frame_callback_(frame->GetData(), header);
}
rtc::scoped_refptr<FrameTransformerInterface>
ChannelReceiveFrameTransformerDelegate::FrameTransformer() {
RTC_DCHECK_RUN_ON(&sequence_checker_);
return frame_transformer_;
}
} // namespace webrtc

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2020 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.
*/
#ifndef AUDIO_CHANNEL_RECEIVE_FRAME_TRANSFORMER_DELEGATE_H_
#define AUDIO_CHANNEL_RECEIVE_FRAME_TRANSFORMER_DELEGATE_H_
#include <memory>
#include <string>
#include "api/frame_transformer_interface.h"
#include "api/sequence_checker.h"
#include "api/task_queue/task_queue_base.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/thread.h"
namespace webrtc {
// Delegates calls to FrameTransformerInterface to transform frames, and to
// ChannelReceive to receive the transformed frames using the
// `receive_frame_callback_` on the `channel_receive_thread_`.
class ChannelReceiveFrameTransformerDelegate : public TransformedFrameCallback {
public:
using ReceiveFrameCallback =
std::function<void(rtc::ArrayView<const uint8_t> packet,
const RTPHeader& header)>;
ChannelReceiveFrameTransformerDelegate(
ReceiveFrameCallback receive_frame_callback,
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
TaskQueueBase* channel_receive_thread);
// Registers `this` as callback for `frame_transformer_`, to get the
// transformed frames.
void Init();
// Unregisters and releases the `frame_transformer_` reference, and resets
// `receive_frame_callback_` on `channel_receive_thread_`. Called from
// ChannelReceive destructor to prevent running the callback on a dangling
// channel.
void Reset();
// Delegates the call to FrameTransformerInterface::Transform, to transform
// the frame asynchronously.
void Transform(rtc::ArrayView<const uint8_t> packet,
const RTPHeader& header,
uint32_t ssrc,
const std::string& codec_mime_type);
// Implements TransformedFrameCallback. Can be called on any thread.
void OnTransformedFrame(
std::unique_ptr<TransformableFrameInterface> frame) override;
void StartShortCircuiting() override;
// Delegates the call to ChannelReceive::OnReceivedPayloadData on the
// `channel_receive_thread_`, by calling `receive_frame_callback_`.
void ReceiveFrame(std::unique_ptr<TransformableFrameInterface> frame) const;
rtc::scoped_refptr<FrameTransformerInterface> FrameTransformer();
protected:
~ChannelReceiveFrameTransformerDelegate() override = default;
private:
RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_;
ReceiveFrameCallback receive_frame_callback_
RTC_GUARDED_BY(sequence_checker_);
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer_
RTC_GUARDED_BY(sequence_checker_);
TaskQueueBase* const channel_receive_thread_;
bool short_circuit_ RTC_GUARDED_BY(sequence_checker_) = false;
};
} // namespace webrtc
#endif // AUDIO_CHANNEL_RECEIVE_FRAME_TRANSFORMER_DELEGATE_H_

View file

@ -0,0 +1,908 @@
/*
* Copyright (c) 2012 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 "audio/channel_send.h"
#include <algorithm>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "api/array_view.h"
#include "api/call/transport.h"
#include "api/crypto/frame_encryptor_interface.h"
#include "api/rtc_event_log/rtc_event_log.h"
#include "api/sequence_checker.h"
#include "audio/channel_send_frame_transformer_delegate.h"
#include "audio/utility/audio_frame_operations.h"
#include "call/rtp_transport_controller_send_interface.h"
#include "logging/rtc_event_log/events/rtc_event_audio_playout.h"
#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h"
#include "modules/audio_coding/include/audio_coding_module.h"
#include "modules/audio_processing/rms_level.h"
#include "modules/pacing/packet_router.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h"
#include "rtc_base/checks.h"
#include "rtc_base/event.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/race_checker.h"
#include "rtc_base/rate_limiter.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/time_utils.h"
#include "rtc_base/trace_event.h"
#include "system_wrappers/include/clock.h"
#include "system_wrappers/include/metrics.h"
namespace webrtc {
namespace voe {
namespace {
constexpr int64_t kMaxRetransmissionWindowMs = 1000;
constexpr int64_t kMinRetransmissionWindowMs = 30;
class RtpPacketSenderProxy;
class TransportSequenceNumberProxy;
class ChannelSend : public ChannelSendInterface,
public AudioPacketizationCallback, // receive encoded
// packets from the ACM
public RtcpPacketTypeCounterObserver,
public ReportBlockDataObserver {
public:
ChannelSend(Clock* clock,
TaskQueueFactory* task_queue_factory,
Transport* rtp_transport,
RtcpRttStats* rtcp_rtt_stats,
RtcEventLog* rtc_event_log,
FrameEncryptorInterface* frame_encryptor,
const webrtc::CryptoOptions& crypto_options,
bool extmap_allow_mixed,
int rtcp_report_interval_ms,
uint32_t ssrc,
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
RtpTransportControllerSendInterface* transport_controller,
const FieldTrialsView& field_trials);
~ChannelSend() override;
// Send using this encoder, with this payload type.
void SetEncoder(int payload_type,
const SdpAudioFormat& encoder_format,
std::unique_ptr<AudioEncoder> encoder) override;
void ModifyEncoder(rtc::FunctionView<void(std::unique_ptr<AudioEncoder>*)>
modifier) override;
void CallEncoder(rtc::FunctionView<void(AudioEncoder*)> modifier) override;
// API methods
void StartSend() override;
void StopSend() override;
// Codecs
void OnBitrateAllocation(BitrateAllocationUpdate update) override;
int GetTargetBitrate() const override;
// Network
void ReceivedRTCPPacket(const uint8_t* data, size_t length) override;
// Muting, Volume and Level.
void SetInputMute(bool enable) override;
// Stats.
ANAStats GetANAStatistics() const override;
// Used by AudioSendStream.
RtpRtcpInterface* GetRtpRtcp() const override;
void RegisterCngPayloadType(int payload_type, int payload_frequency) override;
// DTMF.
bool SendTelephoneEventOutband(int event, int duration_ms) override;
void SetSendTelephoneEventPayloadType(int payload_type,
int payload_frequency) override;
// RTP+RTCP
void SetSendAudioLevelIndicationStatus(bool enable, int id) override;
void RegisterSenderCongestionControlObjects(
RtpTransportControllerSendInterface* transport) override;
void ResetSenderCongestionControlObjects() override;
void SetRTCP_CNAME(absl::string_view c_name) override;
std::vector<ReportBlockData> GetRemoteRTCPReportBlocks() const override;
CallSendStatistics GetRTCPStatistics() const override;
// ProcessAndEncodeAudio() posts a task on the shared encoder task queue,
// which in turn calls (on the queue) ProcessAndEncodeAudioOnTaskQueue() where
// the actual processing of the audio takes place. The processing mainly
// consists of encoding and preparing the result for sending by adding it to a
// send queue.
// The main reason for using a task queue here is to release the native,
// OS-specific, audio capture thread as soon as possible to ensure that it
// can go back to sleep and be prepared to deliver an new captured audio
// packet.
void ProcessAndEncodeAudio(std::unique_ptr<AudioFrame> audio_frame) override;
int64_t GetRTT() const override;
// E2EE Custom Audio Frame Encryption
void SetFrameEncryptor(
rtc::scoped_refptr<FrameEncryptorInterface> frame_encryptor) override;
// Sets a frame transformer between encoder and packetizer, to transform
// encoded frames before sending them out the network.
void SetEncoderToPacketizerFrameTransformer(
rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer)
override;
// RtcpPacketTypeCounterObserver.
void RtcpPacketTypesCounterUpdated(
uint32_t ssrc,
const RtcpPacketTypeCounter& packet_counter) override;
// ReportBlockDataObserver.
void OnReportBlockDataUpdated(ReportBlockData report_block) override;
private:
// From AudioPacketizationCallback in the ACM
int32_t SendData(AudioFrameType frameType,
uint8_t payloadType,
uint32_t rtp_timestamp,
const uint8_t* payloadData,
size_t payloadSize,
int64_t absolute_capture_timestamp_ms) override;
bool InputMute() const;
int32_t SendRtpAudio(AudioFrameType frameType,
uint8_t payloadType,
uint32_t rtp_timestamp_without_offset,
rtc::ArrayView<const uint8_t> payload,
int64_t absolute_capture_timestamp_ms,
rtc::ArrayView<const uint32_t> csrcs)
RTC_RUN_ON(encoder_queue_checker_);
void OnReceivedRtt(int64_t rtt_ms);
void InitFrameTransformerDelegate(
rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer);
// Thread checkers document and lock usage of some methods on voe::Channel to
// specific threads we know about. The goal is to eventually split up
// voe::Channel into parts with single-threaded semantics, and thereby reduce
// the need for locks.
RTC_NO_UNIQUE_ADDRESS SequenceChecker worker_thread_checker_;
// Methods accessed from audio and video threads are checked for sequential-
// only access. We don't necessarily own and control these threads, so thread
// checkers cannot be used. E.g. Chromium may transfer "ownership" from one
// audio thread to another, but access is still sequential.
rtc::RaceChecker audio_thread_race_checker_;
mutable Mutex volume_settings_mutex_;
const uint32_t ssrc_;
bool sending_ RTC_GUARDED_BY(&worker_thread_checker_) = false;
RtcEventLog* const event_log_;
std::unique_ptr<ModuleRtpRtcpImpl2> rtp_rtcp_;
std::unique_ptr<RTPSenderAudio> rtp_sender_audio_;
std::unique_ptr<AudioCodingModule> audio_coding_;
// This is just an offset, RTP module will add its own random offset.
uint32_t timestamp_ RTC_GUARDED_BY(audio_thread_race_checker_) = 0;
absl::optional<int64_t> last_capture_timestamp_ms_
RTC_GUARDED_BY(audio_thread_race_checker_);
RmsLevel rms_level_ RTC_GUARDED_BY(encoder_queue_checker_);
bool input_mute_ RTC_GUARDED_BY(volume_settings_mutex_) = false;
bool previous_frame_muted_ RTC_GUARDED_BY(encoder_queue_checker_) = false;
PacketRouter* packet_router_ RTC_GUARDED_BY(&worker_thread_checker_) =
nullptr;
const std::unique_ptr<RtpPacketSenderProxy> rtp_packet_pacer_proxy_;
const std::unique_ptr<RateLimiter> retransmission_rate_limiter_;
RTC_NO_UNIQUE_ADDRESS SequenceChecker construction_thread_;
std::atomic<bool> include_audio_level_indication_ = false;
std::atomic<bool> encoder_queue_is_active_ = false;
std::atomic<bool> first_frame_ = true;
// E2EE Audio Frame Encryption
rtc::scoped_refptr<FrameEncryptorInterface> frame_encryptor_
RTC_GUARDED_BY(encoder_queue_checker_);
// E2EE Frame Encryption Options
const webrtc::CryptoOptions crypto_options_;
// Delegates calls to a frame transformer to transform audio, and
// receives callbacks with the transformed frames; delegates calls to
// ChannelSend::SendRtpAudio to send the transformed audio.
rtc::scoped_refptr<ChannelSendFrameTransformerDelegate>
frame_transformer_delegate_ RTC_GUARDED_BY(encoder_queue_checker_);
mutable Mutex rtcp_counter_mutex_;
RtcpPacketTypeCounter rtcp_packet_type_counter_
RTC_GUARDED_BY(rtcp_counter_mutex_);
std::unique_ptr<TaskQueueBase, TaskQueueDeleter> encoder_queue_;
RTC_NO_UNIQUE_ADDRESS SequenceChecker encoder_queue_checker_;
SdpAudioFormat encoder_format_;
};
const int kTelephoneEventAttenuationdB = 10;
class RtpPacketSenderProxy : public RtpPacketSender {
public:
RtpPacketSenderProxy() : rtp_packet_pacer_(nullptr) {}
void SetPacketPacer(RtpPacketSender* rtp_packet_pacer) {
RTC_DCHECK(thread_checker_.IsCurrent());
MutexLock lock(&mutex_);
rtp_packet_pacer_ = rtp_packet_pacer;
}
void EnqueuePackets(
std::vector<std::unique_ptr<RtpPacketToSend>> packets) override {
MutexLock lock(&mutex_);
rtp_packet_pacer_->EnqueuePackets(std::move(packets));
}
void RemovePacketsForSsrc(uint32_t ssrc) override {
MutexLock lock(&mutex_);
rtp_packet_pacer_->RemovePacketsForSsrc(ssrc);
}
private:
RTC_NO_UNIQUE_ADDRESS SequenceChecker thread_checker_;
Mutex mutex_;
RtpPacketSender* rtp_packet_pacer_ RTC_GUARDED_BY(&mutex_);
};
int32_t ChannelSend::SendData(AudioFrameType frameType,
uint8_t payloadType,
uint32_t rtp_timestamp,
const uint8_t* payloadData,
size_t payloadSize,
int64_t absolute_capture_timestamp_ms) {
RTC_DCHECK_RUN_ON(&encoder_queue_checker_);
rtc::ArrayView<const uint8_t> payload(payloadData, payloadSize);
if (frame_transformer_delegate_) {
// Asynchronously transform the payload before sending it. After the payload
// is transformed, the delegate will call SendRtpAudio to send it.
char buf[1024];
rtc::SimpleStringBuilder mime_type(buf);
mime_type << MediaTypeToString(cricket::MEDIA_TYPE_AUDIO) << "/"
<< encoder_format_.name;
frame_transformer_delegate_->Transform(
frameType, payloadType, rtp_timestamp + rtp_rtcp_->StartTimestamp(),
payloadData, payloadSize, absolute_capture_timestamp_ms,
rtp_rtcp_->SSRC(), mime_type.str());
return 0;
}
return SendRtpAudio(frameType, payloadType, rtp_timestamp, payload,
absolute_capture_timestamp_ms, /*csrcs=*/{});
}
int32_t ChannelSend::SendRtpAudio(AudioFrameType frameType,
uint8_t payloadType,
uint32_t rtp_timestamp_without_offset,
rtc::ArrayView<const uint8_t> payload,
int64_t absolute_capture_timestamp_ms,
rtc::ArrayView<const uint32_t> csrcs) {
// E2EE Custom Audio Frame Encryption (This is optional).
// Keep this buffer around for the lifetime of the send call.
rtc::Buffer encrypted_audio_payload;
// We don't invoke encryptor if payload is empty, which means we are to send
// DTMF, or the encoder entered DTX.
// TODO(minyue): see whether DTMF packets should be encrypted or not. In
// current implementation, they are not.
if (!payload.empty()) {
if (frame_encryptor_ != nullptr) {
// TODO(benwright@webrtc.org) - Allocate enough to always encrypt inline.
// Allocate a buffer to hold the maximum possible encrypted payload.
size_t max_ciphertext_size = frame_encryptor_->GetMaxCiphertextByteSize(
cricket::MEDIA_TYPE_AUDIO, payload.size());
encrypted_audio_payload.SetSize(max_ciphertext_size);
// Encrypt the audio payload into the buffer.
size_t bytes_written = 0;
int encrypt_status = frame_encryptor_->Encrypt(
cricket::MEDIA_TYPE_AUDIO, rtp_rtcp_->SSRC(),
/*additional_data=*/nullptr, payload, encrypted_audio_payload,
&bytes_written);
if (encrypt_status != 0) {
RTC_DLOG(LS_ERROR)
<< "Channel::SendData() failed encrypt audio payload: "
<< encrypt_status;
return -1;
}
// Resize the buffer to the exact number of bytes actually used.
encrypted_audio_payload.SetSize(bytes_written);
// Rewrite the payloadData and size to the new encrypted payload.
payload = encrypted_audio_payload;
} else if (crypto_options_.sframe.require_frame_encryption) {
RTC_DLOG(LS_ERROR) << "Channel::SendData() failed sending audio payload: "
"A frame encryptor is required but one is not set.";
return -1;
}
}
// Push data from ACM to RTP/RTCP-module to deliver audio frame for
// packetization.
if (!rtp_rtcp_->OnSendingRtpFrame(rtp_timestamp_without_offset,
// Leaving the time when this frame was
// received from the capture device as
// undefined for voice for now.
-1, payloadType,
/*force_sender_report=*/false)) {
return -1;
}
// RTCPSender has it's own copy of the timestamp offset, added in
// RTCPSender::BuildSR, hence we must not add the in the offset for the above
// call.
// TODO(nisse): Delete RTCPSender:timestamp_offset_, and see if we can confine
// knowledge of the offset to a single place.
// This call will trigger Transport::SendPacket() from the RTP/RTCP module.
RTPSenderAudio::RtpAudioFrame frame = {
.type = frameType,
.payload = payload,
.payload_id = payloadType,
.rtp_timestamp =
rtp_timestamp_without_offset + rtp_rtcp_->StartTimestamp(),
.csrcs = csrcs};
if (absolute_capture_timestamp_ms > 0) {
frame.capture_time = Timestamp::Millis(absolute_capture_timestamp_ms);
}
if (include_audio_level_indication_.load()) {
frame.audio_level_dbov = rms_level_.Average();
}
if (!rtp_sender_audio_->SendAudio(frame)) {
RTC_DLOG(LS_ERROR)
<< "ChannelSend::SendData() failed to send data to RTP/RTCP module";
return -1;
}
return 0;
}
ChannelSend::ChannelSend(
Clock* clock,
TaskQueueFactory* task_queue_factory,
Transport* rtp_transport,
RtcpRttStats* rtcp_rtt_stats,
RtcEventLog* rtc_event_log,
FrameEncryptorInterface* frame_encryptor,
const webrtc::CryptoOptions& crypto_options,
bool extmap_allow_mixed,
int rtcp_report_interval_ms,
uint32_t ssrc,
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
RtpTransportControllerSendInterface* transport_controller,
const FieldTrialsView& field_trials)
: ssrc_(ssrc),
event_log_(rtc_event_log),
rtp_packet_pacer_proxy_(new RtpPacketSenderProxy()),
retransmission_rate_limiter_(
new RateLimiter(clock, kMaxRetransmissionWindowMs)),
frame_encryptor_(frame_encryptor),
crypto_options_(crypto_options),
encoder_queue_(task_queue_factory->CreateTaskQueue(
"AudioEncoder",
TaskQueueFactory::Priority::NORMAL)),
encoder_queue_checker_(encoder_queue_.get()),
encoder_format_("x-unknown", 0, 0) {
audio_coding_ = AudioCodingModule::Create();
RtpRtcpInterface::Configuration configuration;
configuration.report_block_data_observer = this;
configuration.network_link_rtcp_observer =
transport_controller->GetRtcpObserver();
configuration.transport_feedback_callback =
transport_controller->transport_feedback_observer();
configuration.clock = (clock ? clock : Clock::GetRealTimeClock());
configuration.audio = true;
configuration.outgoing_transport = rtp_transport;
configuration.paced_sender = rtp_packet_pacer_proxy_.get();
configuration.event_log = event_log_;
configuration.rtt_stats = rtcp_rtt_stats;
if (field_trials.IsDisabled("WebRTC-DisableRtxRateLimiter")) {
configuration.retransmission_rate_limiter =
retransmission_rate_limiter_.get();
}
configuration.extmap_allow_mixed = extmap_allow_mixed;
configuration.rtcp_report_interval_ms = rtcp_report_interval_ms;
configuration.rtcp_packet_type_counter_observer = this;
configuration.local_media_ssrc = ssrc;
rtp_rtcp_ = ModuleRtpRtcpImpl2::Create(configuration);
rtp_rtcp_->SetSendingMediaStatus(false);
rtp_sender_audio_ = std::make_unique<RTPSenderAudio>(configuration.clock,
rtp_rtcp_->RtpSender());
// Ensure that RTCP is enabled by default for the created channel.
rtp_rtcp_->SetRTCPStatus(RtcpMode::kCompound);
int error = audio_coding_->RegisterTransportCallback(this);
RTC_DCHECK_EQ(0, error);
if (frame_transformer)
InitFrameTransformerDelegate(std::move(frame_transformer));
}
ChannelSend::~ChannelSend() {
RTC_DCHECK(construction_thread_.IsCurrent());
// Resets the delegate's callback to ChannelSend::SendRtpAudio.
if (frame_transformer_delegate_)
frame_transformer_delegate_->Reset();
StopSend();
int error = audio_coding_->RegisterTransportCallback(NULL);
RTC_DCHECK_EQ(0, error);
// Delete the encoder task queue first to ensure that there are no running
// tasks when the other members are destroyed.
encoder_queue_ = nullptr;
}
void ChannelSend::StartSend() {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
RTC_DCHECK(!sending_);
sending_ = true;
RTC_DCHECK(packet_router_);
packet_router_->AddSendRtpModule(rtp_rtcp_.get(), /*remb_candidate=*/false);
rtp_rtcp_->SetSendingMediaStatus(true);
int ret = rtp_rtcp_->SetSendingStatus(true);
RTC_DCHECK_EQ(0, ret);
// It is now OK to start processing on the encoder task queue.
first_frame_.store(true);
encoder_queue_is_active_.store(true);
}
void ChannelSend::StopSend() {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
if (!sending_) {
return;
}
sending_ = false;
encoder_queue_is_active_.store(false);
// Wait until all pending encode tasks are executed and clear any remaining
// buffers in the encoder.
rtc::Event flush;
encoder_queue_->PostTask([this, &flush]() {
RTC_DCHECK_RUN_ON(&encoder_queue_checker_);
CallEncoder([](AudioEncoder* encoder) { encoder->Reset(); });
flush.Set();
});
flush.Wait(rtc::Event::kForever);
// Reset sending SSRC and sequence number and triggers direct transmission
// of RTCP BYE
if (rtp_rtcp_->SetSendingStatus(false) == -1) {
RTC_DLOG(LS_ERROR) << "StartSend() RTP/RTCP failed to stop sending";
}
rtp_rtcp_->SetSendingMediaStatus(false);
RTC_DCHECK(packet_router_);
packet_router_->RemoveSendRtpModule(rtp_rtcp_.get());
rtp_packet_pacer_proxy_->RemovePacketsForSsrc(rtp_rtcp_->SSRC());
}
void ChannelSend::SetEncoder(int payload_type,
const SdpAudioFormat& encoder_format,
std::unique_ptr<AudioEncoder> encoder) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
RTC_DCHECK_GE(payload_type, 0);
RTC_DCHECK_LE(payload_type, 127);
// The RTP/RTCP module needs to know the RTP timestamp rate (i.e. clockrate)
// as well as some other things, so we collect this info and send it along.
rtp_rtcp_->RegisterSendPayloadFrequency(payload_type,
encoder->RtpTimestampRateHz());
rtp_sender_audio_->RegisterAudioPayload("audio", payload_type,
encoder->RtpTimestampRateHz(),
encoder->NumChannels(), 0);
encoder_format_ = encoder_format;
audio_coding_->SetEncoder(std::move(encoder));
}
void ChannelSend::ModifyEncoder(
rtc::FunctionView<void(std::unique_ptr<AudioEncoder>*)> modifier) {
// This method can be called on the worker thread, module process thread
// or network thread. Audio coding is thread safe, so we do not need to
// enforce the calling thread.
audio_coding_->ModifyEncoder(modifier);
}
void ChannelSend::CallEncoder(rtc::FunctionView<void(AudioEncoder*)> modifier) {
ModifyEncoder([modifier](std::unique_ptr<AudioEncoder>* encoder_ptr) {
if (*encoder_ptr) {
modifier(encoder_ptr->get());
} else {
RTC_DLOG(LS_WARNING) << "Trying to call unset encoder.";
}
});
}
void ChannelSend::OnBitrateAllocation(BitrateAllocationUpdate update) {
// This method can be called on the worker thread, module process thread
// or on a TaskQueue via VideoSendStreamImpl::OnEncoderConfigurationChanged.
// TODO(solenberg): Figure out a good way to check this or enforce calling
// rules.
// RTC_DCHECK(worker_thread_checker_.IsCurrent() ||
// module_process_thread_checker_.IsCurrent());
CallEncoder([&](AudioEncoder* encoder) {
encoder->OnReceivedUplinkAllocation(update);
});
retransmission_rate_limiter_->SetMaxRate(update.target_bitrate.bps());
}
int ChannelSend::GetTargetBitrate() const {
return audio_coding_->GetTargetBitrate();
}
void ChannelSend::OnReportBlockDataUpdated(ReportBlockData report_block) {
float packet_loss_rate = report_block.fraction_lost();
CallEncoder([&](AudioEncoder* encoder) {
encoder->OnReceivedUplinkPacketLossFraction(packet_loss_rate);
});
}
void ChannelSend::ReceivedRTCPPacket(const uint8_t* data, size_t length) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
// Deliver RTCP packet to RTP/RTCP module for parsing
rtp_rtcp_->IncomingRtcpPacket(rtc::MakeArrayView(data, length));
int64_t rtt = GetRTT();
if (rtt == 0) {
// Waiting for valid RTT.
return;
}
int64_t nack_window_ms = rtt;
if (nack_window_ms < kMinRetransmissionWindowMs) {
nack_window_ms = kMinRetransmissionWindowMs;
} else if (nack_window_ms > kMaxRetransmissionWindowMs) {
nack_window_ms = kMaxRetransmissionWindowMs;
}
retransmission_rate_limiter_->SetWindowSize(nack_window_ms);
OnReceivedRtt(rtt);
}
void ChannelSend::SetInputMute(bool enable) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
MutexLock lock(&volume_settings_mutex_);
input_mute_ = enable;
}
bool ChannelSend::InputMute() const {
MutexLock lock(&volume_settings_mutex_);
return input_mute_;
}
bool ChannelSend::SendTelephoneEventOutband(int event, int duration_ms) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
RTC_DCHECK_LE(0, event);
RTC_DCHECK_GE(255, event);
RTC_DCHECK_LE(0, duration_ms);
RTC_DCHECK_GE(65535, duration_ms);
if (!sending_) {
return false;
}
if (rtp_sender_audio_->SendTelephoneEvent(
event, duration_ms, kTelephoneEventAttenuationdB) != 0) {
RTC_DLOG(LS_ERROR) << "SendTelephoneEvent() failed to send event";
return false;
}
return true;
}
void ChannelSend::RegisterCngPayloadType(int payload_type,
int payload_frequency) {
rtp_rtcp_->RegisterSendPayloadFrequency(payload_type, payload_frequency);
rtp_sender_audio_->RegisterAudioPayload("CN", payload_type, payload_frequency,
1, 0);
}
void ChannelSend::SetSendTelephoneEventPayloadType(int payload_type,
int payload_frequency) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
RTC_DCHECK_LE(0, payload_type);
RTC_DCHECK_GE(127, payload_type);
rtp_rtcp_->RegisterSendPayloadFrequency(payload_type, payload_frequency);
rtp_sender_audio_->RegisterAudioPayload("telephone-event", payload_type,
payload_frequency, 0, 0);
}
void ChannelSend::SetSendAudioLevelIndicationStatus(bool enable, int id) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
include_audio_level_indication_.store(enable);
if (enable) {
rtp_rtcp_->RegisterRtpHeaderExtension(AudioLevel::Uri(), id);
} else {
rtp_rtcp_->DeregisterSendRtpHeaderExtension(AudioLevel::Uri());
}
}
void ChannelSend::RegisterSenderCongestionControlObjects(
RtpTransportControllerSendInterface* transport) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
RtpPacketSender* rtp_packet_pacer = transport->packet_sender();
PacketRouter* packet_router = transport->packet_router();
RTC_DCHECK(rtp_packet_pacer);
RTC_DCHECK(packet_router);
RTC_DCHECK(!packet_router_);
rtp_packet_pacer_proxy_->SetPacketPacer(rtp_packet_pacer);
rtp_rtcp_->SetStorePacketsStatus(true, 600);
packet_router_ = packet_router;
}
void ChannelSend::ResetSenderCongestionControlObjects() {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
RTC_DCHECK(packet_router_);
rtp_rtcp_->SetStorePacketsStatus(false, 600);
packet_router_ = nullptr;
rtp_packet_pacer_proxy_->SetPacketPacer(nullptr);
}
void ChannelSend::SetRTCP_CNAME(absl::string_view c_name) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
// Note: SetCNAME() accepts a c string of length at most 255.
const std::string c_name_limited(c_name.substr(0, 255));
int ret = rtp_rtcp_->SetCNAME(c_name_limited.c_str()) != 0;
RTC_DCHECK_EQ(0, ret) << "SetRTCP_CNAME() failed to set RTCP CNAME";
}
std::vector<ReportBlockData> ChannelSend::GetRemoteRTCPReportBlocks() const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
// Get the report blocks from the latest received RTCP Sender or Receiver
// Report. Each element in the vector contains the sender's SSRC and a
// report block according to RFC 3550.
return rtp_rtcp_->GetLatestReportBlockData();
}
CallSendStatistics ChannelSend::GetRTCPStatistics() const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
CallSendStatistics stats = {0};
stats.rttMs = GetRTT();
StreamDataCounters rtp_stats;
StreamDataCounters rtx_stats;
rtp_rtcp_->GetSendStreamDataCounters(&rtp_stats, &rtx_stats);
stats.payload_bytes_sent =
rtp_stats.transmitted.payload_bytes + rtx_stats.transmitted.payload_bytes;
stats.header_and_padding_bytes_sent =
rtp_stats.transmitted.padding_bytes + rtp_stats.transmitted.header_bytes +
rtx_stats.transmitted.padding_bytes + rtx_stats.transmitted.header_bytes;
// TODO(https://crbug.com/webrtc/10555): RTX retransmissions should show up in
// separate outbound-rtp stream objects.
stats.retransmitted_bytes_sent = rtp_stats.retransmitted.payload_bytes;
stats.packetsSent =
rtp_stats.transmitted.packets + rtx_stats.transmitted.packets;
stats.total_packet_send_delay = rtp_stats.transmitted.total_packet_delay;
stats.retransmitted_packets_sent = rtp_stats.retransmitted.packets;
stats.report_block_datas = rtp_rtcp_->GetLatestReportBlockData();
{
MutexLock lock(&rtcp_counter_mutex_);
stats.nacks_received = rtcp_packet_type_counter_.nack_packets;
}
return stats;
}
void ChannelSend::RtcpPacketTypesCounterUpdated(
uint32_t ssrc,
const RtcpPacketTypeCounter& packet_counter) {
if (ssrc != ssrc_) {
return;
}
MutexLock lock(&rtcp_counter_mutex_);
rtcp_packet_type_counter_ = packet_counter;
}
void ChannelSend::ProcessAndEncodeAudio(
std::unique_ptr<AudioFrame> audio_frame) {
TRACE_EVENT0("webrtc", "ChannelSend::ProcessAndEncodeAudio");
RTC_DCHECK_RUNS_SERIALIZED(&audio_thread_race_checker_);
RTC_DCHECK_GT(audio_frame->samples_per_channel_, 0);
RTC_DCHECK_LE(audio_frame->num_channels_, 8);
if (!encoder_queue_is_active_.load()) {
return;
}
// Update `timestamp_` based on the capture timestamp for the first frame
// after sending is resumed.
if (first_frame_.load()) {
first_frame_.store(false);
if (last_capture_timestamp_ms_ &&
audio_frame->absolute_capture_timestamp_ms()) {
int64_t diff_ms = *audio_frame->absolute_capture_timestamp_ms() -
*last_capture_timestamp_ms_;
// Truncate to whole frames and subtract one since `timestamp_` was
// incremented after the last frame.
int64_t diff_frames = diff_ms * audio_frame->sample_rate_hz() / 1000 /
audio_frame->samples_per_channel() -
1;
timestamp_ += std::max<int64_t>(
diff_frames * audio_frame->samples_per_channel(), 0);
}
}
audio_frame->timestamp_ = timestamp_;
timestamp_ += audio_frame->samples_per_channel_;
last_capture_timestamp_ms_ = audio_frame->absolute_capture_timestamp_ms();
// Profile time between when the audio frame is added to the task queue and
// when the task is actually executed.
audio_frame->UpdateProfileTimeStamp();
encoder_queue_->PostTask(
[this, audio_frame = std::move(audio_frame)]() mutable {
RTC_DCHECK_RUN_ON(&encoder_queue_checker_);
if (!encoder_queue_is_active_.load()) {
return;
}
// Measure time between when the audio frame is added to the task queue
// and when the task is actually executed. Goal is to keep track of
// unwanted extra latency added by the task queue.
RTC_HISTOGRAM_COUNTS_10000("WebRTC.Audio.EncodingTaskQueueLatencyMs",
audio_frame->ElapsedProfileTimeMs());
bool is_muted = InputMute();
AudioFrameOperations::Mute(audio_frame.get(), previous_frame_muted_,
is_muted);
if (include_audio_level_indication_.load()) {
size_t length =
audio_frame->samples_per_channel_ * audio_frame->num_channels_;
RTC_CHECK_LE(length, AudioFrame::kMaxDataSizeBytes);
if (is_muted && previous_frame_muted_) {
rms_level_.AnalyzeMuted(length);
} else {
rms_level_.Analyze(
rtc::ArrayView<const int16_t>(audio_frame->data(), length));
}
}
previous_frame_muted_ = is_muted;
// This call will trigger AudioPacketizationCallback::SendData if
// encoding is done and payload is ready for packetization and
// transmission. Otherwise, it will return without invoking the
// callback.
if (audio_coding_->Add10MsData(*audio_frame) < 0) {
RTC_DLOG(LS_ERROR) << "ACM::Add10MsData() failed.";
return;
}
});
}
ANAStats ChannelSend::GetANAStatistics() const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
return audio_coding_->GetANAStats();
}
RtpRtcpInterface* ChannelSend::GetRtpRtcp() const {
return rtp_rtcp_.get();
}
int64_t ChannelSend::GetRTT() const {
std::vector<ReportBlockData> report_blocks =
rtp_rtcp_->GetLatestReportBlockData();
if (report_blocks.empty()) {
return 0;
}
// We don't know in advance the remote ssrc used by the other end's receiver
// reports, so use the first report block for the RTT.
return report_blocks.front().last_rtt().ms();
}
void ChannelSend::SetFrameEncryptor(
rtc::scoped_refptr<FrameEncryptorInterface> frame_encryptor) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
encoder_queue_->PostTask([this, frame_encryptor]() mutable {
RTC_DCHECK_RUN_ON(&encoder_queue_checker_);
frame_encryptor_ = std::move(frame_encryptor);
});
}
void ChannelSend::SetEncoderToPacketizerFrameTransformer(
rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
if (!frame_transformer)
return;
encoder_queue_->PostTask(
[this, frame_transformer = std::move(frame_transformer)]() mutable {
RTC_DCHECK_RUN_ON(&encoder_queue_checker_);
InitFrameTransformerDelegate(std::move(frame_transformer));
});
}
void ChannelSend::OnReceivedRtt(int64_t rtt_ms) {
// Invoke audio encoders OnReceivedRtt().
CallEncoder(
[rtt_ms](AudioEncoder* encoder) { encoder->OnReceivedRtt(rtt_ms); });
}
void ChannelSend::InitFrameTransformerDelegate(
rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer) {
RTC_DCHECK_RUN_ON(&encoder_queue_checker_);
RTC_DCHECK(frame_transformer);
RTC_DCHECK(!frame_transformer_delegate_);
// Pass a callback to ChannelSend::SendRtpAudio, to be called by the delegate
// to send the transformed audio.
ChannelSendFrameTransformerDelegate::SendFrameCallback send_audio_callback =
[this](AudioFrameType frameType, uint8_t payloadType,
uint32_t rtp_timestamp_with_offset,
rtc::ArrayView<const uint8_t> payload,
int64_t absolute_capture_timestamp_ms,
rtc::ArrayView<const uint32_t> csrcs) {
RTC_DCHECK_RUN_ON(&encoder_queue_checker_);
return SendRtpAudio(
frameType, payloadType,
rtp_timestamp_with_offset - rtp_rtcp_->StartTimestamp(), payload,
absolute_capture_timestamp_ms, csrcs);
};
frame_transformer_delegate_ =
rtc::make_ref_counted<ChannelSendFrameTransformerDelegate>(
std::move(send_audio_callback), std::move(frame_transformer),
encoder_queue_.get());
frame_transformer_delegate_->Init();
}
} // namespace
std::unique_ptr<ChannelSendInterface> CreateChannelSend(
Clock* clock,
TaskQueueFactory* task_queue_factory,
Transport* rtp_transport,
RtcpRttStats* rtcp_rtt_stats,
RtcEventLog* rtc_event_log,
FrameEncryptorInterface* frame_encryptor,
const webrtc::CryptoOptions& crypto_options,
bool extmap_allow_mixed,
int rtcp_report_interval_ms,
uint32_t ssrc,
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
RtpTransportControllerSendInterface* transport_controller,
const FieldTrialsView& field_trials) {
return std::make_unique<ChannelSend>(
clock, task_queue_factory, rtp_transport, rtcp_rtt_stats, rtc_event_log,
frame_encryptor, crypto_options, extmap_allow_mixed,
rtcp_report_interval_ms, ssrc, std::move(frame_transformer),
transport_controller, field_trials);
}
} // namespace voe
} // namespace webrtc

View file

@ -0,0 +1,135 @@
/*
* Copyright (c) 2012 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.
*/
#ifndef AUDIO_CHANNEL_SEND_H_
#define AUDIO_CHANNEL_SEND_H_
#include <memory>
#include <string>
#include <vector>
#include "api/audio/audio_frame.h"
#include "api/audio_codecs/audio_encoder.h"
#include "api/crypto/crypto_options.h"
#include "api/field_trials_view.h"
#include "api/frame_transformer_interface.h"
#include "api/function_view.h"
#include "api/task_queue/task_queue_factory.h"
#include "modules/rtp_rtcp/include/report_block_data.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
#include "modules/rtp_rtcp/source/rtp_sender_audio.h"
namespace webrtc {
class FrameEncryptorInterface;
class RtcEventLog;
class RtpTransportControllerSendInterface;
struct CallSendStatistics {
int64_t rttMs;
int64_t payload_bytes_sent;
int64_t header_and_padding_bytes_sent;
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-retransmittedbytessent
uint64_t retransmitted_bytes_sent;
int packetsSent;
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-totalpacketsenddelay
TimeDelta total_packet_send_delay = TimeDelta::Zero();
// https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-retransmittedpacketssent
uint64_t retransmitted_packets_sent;
// A snapshot of Report Blocks with additional data of interest to statistics.
// Within this list, the sender-source SSRC pair is unique and per-pair the
// ReportBlockData represents the latest Report Block that was received for
// that pair.
std::vector<ReportBlockData> report_block_datas;
uint32_t nacks_received;
};
namespace voe {
class ChannelSendInterface {
public:
virtual ~ChannelSendInterface() = default;
virtual void ReceivedRTCPPacket(const uint8_t* packet, size_t length) = 0;
virtual CallSendStatistics GetRTCPStatistics() const = 0;
virtual void SetEncoder(int payload_type,
const SdpAudioFormat& encoder_format,
std::unique_ptr<AudioEncoder> encoder) = 0;
virtual void ModifyEncoder(
rtc::FunctionView<void(std::unique_ptr<AudioEncoder>*)> modifier) = 0;
virtual void CallEncoder(rtc::FunctionView<void(AudioEncoder*)> modifier) = 0;
// Use 0 to indicate that the extension should not be registered.
virtual void SetRTCP_CNAME(absl::string_view c_name) = 0;
virtual void SetSendAudioLevelIndicationStatus(bool enable, int id) = 0;
virtual void RegisterSenderCongestionControlObjects(
RtpTransportControllerSendInterface* transport) = 0;
virtual void ResetSenderCongestionControlObjects() = 0;
virtual std::vector<ReportBlockData> GetRemoteRTCPReportBlocks() const = 0;
virtual ANAStats GetANAStatistics() const = 0;
virtual void RegisterCngPayloadType(int payload_type,
int payload_frequency) = 0;
virtual void SetSendTelephoneEventPayloadType(int payload_type,
int payload_frequency) = 0;
virtual bool SendTelephoneEventOutband(int event, int duration_ms) = 0;
virtual void OnBitrateAllocation(BitrateAllocationUpdate update) = 0;
virtual int GetTargetBitrate() const = 0;
virtual void SetInputMute(bool muted) = 0;
virtual void ProcessAndEncodeAudio(
std::unique_ptr<AudioFrame> audio_frame) = 0;
virtual RtpRtcpInterface* GetRtpRtcp() const = 0;
// In RTP we currently rely on RTCP packets (`ReceivedRTCPPacket`) to inform
// about RTT.
// In media transport we rely on the TargetTransferRateObserver instead.
// In other words, if you are using RTP, you should expect
// `ReceivedRTCPPacket` to be called, if you are using media transport,
// `OnTargetTransferRate` will be called.
//
// In future, RTP media will move to the media transport implementation and
// these conditions will be removed.
// Returns the RTT in milliseconds.
virtual int64_t GetRTT() const = 0;
virtual void StartSend() = 0;
virtual void StopSend() = 0;
// E2EE Custom Audio Frame Encryption (Optional)
virtual void SetFrameEncryptor(
rtc::scoped_refptr<FrameEncryptorInterface> frame_encryptor) = 0;
// Sets a frame transformer between encoder and packetizer, to transform
// encoded frames before sending them out the network.
virtual void SetEncoderToPacketizerFrameTransformer(
rtc::scoped_refptr<webrtc::FrameTransformerInterface>
frame_transformer) = 0;
};
std::unique_ptr<ChannelSendInterface> CreateChannelSend(
Clock* clock,
TaskQueueFactory* task_queue_factory,
Transport* rtp_transport,
RtcpRttStats* rtcp_rtt_stats,
RtcEventLog* rtc_event_log,
FrameEncryptorInterface* frame_encryptor,
const webrtc::CryptoOptions& crypto_options,
bool extmap_allow_mixed,
int rtcp_report_interval_ms,
uint32_t ssrc,
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
RtpTransportControllerSendInterface* transport_controller,
const FieldTrialsView& field_trials);
} // namespace voe
} // namespace webrtc
#endif // AUDIO_CHANNEL_SEND_H_

View file

@ -0,0 +1,213 @@
/*
* Copyright (c) 2020 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 "audio/channel_send_frame_transformer_delegate.h"
#include <utility>
#include <vector>
namespace webrtc {
namespace {
using IfaceFrameType = TransformableAudioFrameInterface::FrameType;
IfaceFrameType InternalFrameTypeToInterfaceFrameType(
const AudioFrameType frame_type) {
switch (frame_type) {
case AudioFrameType::kEmptyFrame:
return IfaceFrameType::kEmptyFrame;
case AudioFrameType::kAudioFrameSpeech:
return IfaceFrameType::kAudioFrameSpeech;
case AudioFrameType::kAudioFrameCN:
return IfaceFrameType::kAudioFrameCN;
}
RTC_DCHECK_NOTREACHED();
return IfaceFrameType::kEmptyFrame;
}
AudioFrameType InterfaceFrameTypeToInternalFrameType(
const IfaceFrameType frame_type) {
switch (frame_type) {
case IfaceFrameType::kEmptyFrame:
return AudioFrameType::kEmptyFrame;
case IfaceFrameType::kAudioFrameSpeech:
return AudioFrameType::kAudioFrameSpeech;
case IfaceFrameType::kAudioFrameCN:
return AudioFrameType::kAudioFrameCN;
}
RTC_DCHECK_NOTREACHED();
return AudioFrameType::kEmptyFrame;
}
class TransformableOutgoingAudioFrame
: public TransformableAudioFrameInterface {
public:
TransformableOutgoingAudioFrame(
AudioFrameType frame_type,
uint8_t payload_type,
uint32_t rtp_timestamp_with_offset,
const uint8_t* payload_data,
size_t payload_size,
absl::optional<uint64_t> absolute_capture_timestamp_ms,
uint32_t ssrc,
std::vector<uint32_t> csrcs,
const std::string& codec_mime_type,
absl::optional<uint16_t> sequence_number)
: frame_type_(frame_type),
payload_type_(payload_type),
rtp_timestamp_with_offset_(rtp_timestamp_with_offset),
payload_(payload_data, payload_size),
absolute_capture_timestamp_ms_(absolute_capture_timestamp_ms),
ssrc_(ssrc),
csrcs_(std::move(csrcs)),
codec_mime_type_(codec_mime_type),
sequence_number_(sequence_number) {}
~TransformableOutgoingAudioFrame() override = default;
rtc::ArrayView<const uint8_t> GetData() const override { return payload_; }
void SetData(rtc::ArrayView<const uint8_t> data) override {
payload_.SetData(data.data(), data.size());
}
uint32_t GetTimestamp() const override { return rtp_timestamp_with_offset_; }
uint32_t GetSsrc() const override { return ssrc_; }
IfaceFrameType Type() const override {
return InternalFrameTypeToInterfaceFrameType(frame_type_);
}
uint8_t GetPayloadType() const override { return payload_type_; }
Direction GetDirection() const override { return Direction::kSender; }
std::string GetMimeType() const override { return codec_mime_type_; }
rtc::ArrayView<const uint32_t> GetContributingSources() const override {
return csrcs_;
}
const absl::optional<uint16_t> SequenceNumber() const override {
return sequence_number_;
}
void SetRTPTimestamp(uint32_t rtp_timestamp_with_offset) override {
rtp_timestamp_with_offset_ = rtp_timestamp_with_offset;
}
absl::optional<uint64_t> AbsoluteCaptureTimestamp() const override {
return absolute_capture_timestamp_ms_;
}
private:
AudioFrameType frame_type_;
uint8_t payload_type_;
uint32_t rtp_timestamp_with_offset_;
rtc::Buffer payload_;
absl::optional<uint64_t> absolute_capture_timestamp_ms_;
uint32_t ssrc_;
std::vector<uint32_t> csrcs_;
std::string codec_mime_type_;
absl::optional<uint16_t> sequence_number_;
};
} // namespace
ChannelSendFrameTransformerDelegate::ChannelSendFrameTransformerDelegate(
SendFrameCallback send_frame_callback,
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
TaskQueueBase* encoder_queue)
: send_frame_callback_(send_frame_callback),
frame_transformer_(std::move(frame_transformer)),
encoder_queue_(encoder_queue) {}
void ChannelSendFrameTransformerDelegate::Init() {
frame_transformer_->RegisterTransformedFrameCallback(
rtc::scoped_refptr<TransformedFrameCallback>(this));
}
void ChannelSendFrameTransformerDelegate::Reset() {
frame_transformer_->UnregisterTransformedFrameCallback();
frame_transformer_ = nullptr;
MutexLock lock(&send_lock_);
send_frame_callback_ = SendFrameCallback();
}
void ChannelSendFrameTransformerDelegate::Transform(
AudioFrameType frame_type,
uint8_t payload_type,
uint32_t rtp_timestamp,
const uint8_t* payload_data,
size_t payload_size,
int64_t absolute_capture_timestamp_ms,
uint32_t ssrc,
const std::string& codec_mimetype) {
{
MutexLock lock(&send_lock_);
if (short_circuit_) {
send_frame_callback_(
frame_type, payload_type, rtp_timestamp,
rtc::ArrayView<const uint8_t>(payload_data, payload_size),
absolute_capture_timestamp_ms, /*csrcs=*/{});
return;
}
}
frame_transformer_->Transform(
std::make_unique<TransformableOutgoingAudioFrame>(
frame_type, payload_type, rtp_timestamp, payload_data, payload_size,
absolute_capture_timestamp_ms, ssrc,
/*csrcs=*/std::vector<uint32_t>(), codec_mimetype,
/*sequence_number=*/absl::nullopt));
}
void ChannelSendFrameTransformerDelegate::OnTransformedFrame(
std::unique_ptr<TransformableFrameInterface> frame) {
MutexLock lock(&send_lock_);
if (!send_frame_callback_)
return;
rtc::scoped_refptr<ChannelSendFrameTransformerDelegate> delegate(this);
encoder_queue_->PostTask(
[delegate = std::move(delegate), frame = std::move(frame)]() mutable {
delegate->SendFrame(std::move(frame));
});
}
void ChannelSendFrameTransformerDelegate::StartShortCircuiting() {
MutexLock lock(&send_lock_);
short_circuit_ = true;
}
void ChannelSendFrameTransformerDelegate::SendFrame(
std::unique_ptr<TransformableFrameInterface> frame) const {
MutexLock lock(&send_lock_);
RTC_DCHECK_RUN_ON(encoder_queue_);
if (!send_frame_callback_)
return;
auto* transformed_frame =
static_cast<TransformableAudioFrameInterface*>(frame.get());
send_frame_callback_(
InterfaceFrameTypeToInternalFrameType(transformed_frame->Type()),
transformed_frame->GetPayloadType(), transformed_frame->GetTimestamp(),
transformed_frame->GetData(),
transformed_frame->AbsoluteCaptureTimestamp()
? *transformed_frame->AbsoluteCaptureTimestamp()
: 0,
transformed_frame->GetContributingSources());
}
std::unique_ptr<TransformableAudioFrameInterface> CloneSenderAudioFrame(
TransformableAudioFrameInterface* original) {
std::vector<uint32_t> csrcs;
csrcs.assign(original->GetContributingSources().begin(),
original->GetContributingSources().end());
return std::make_unique<TransformableOutgoingAudioFrame>(
InterfaceFrameTypeToInternalFrameType(original->Type()),
original->GetPayloadType(), original->GetTimestamp(),
original->GetData().data(), original->GetData().size(),
original->AbsoluteCaptureTimestamp(), original->GetSsrc(),
std::move(csrcs), original->GetMimeType(), original->SequenceNumber());
}
} // namespace webrtc

View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2020 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.
*/
#ifndef AUDIO_CHANNEL_SEND_FRAME_TRANSFORMER_DELEGATE_H_
#define AUDIO_CHANNEL_SEND_FRAME_TRANSFORMER_DELEGATE_H_
#include <memory>
#include <string>
#include "api/frame_transformer_interface.h"
#include "api/sequence_checker.h"
#include "api/task_queue/task_queue_base.h"
#include "modules/audio_coding/include/audio_coding_module_typedefs.h"
#include "rtc_base/buffer.h"
#include "rtc_base/synchronization/mutex.h"
namespace webrtc {
// Delegates calls to FrameTransformerInterface to transform frames, and to
// ChannelSend to send the transformed frames using `send_frame_callback_` on
// the `encoder_queue_`.
// OnTransformedFrame() can be called from any thread, the delegate ensures
// thread-safe access to the ChannelSend callback.
class ChannelSendFrameTransformerDelegate : public TransformedFrameCallback {
public:
using SendFrameCallback =
std::function<int32_t(AudioFrameType frameType,
uint8_t payloadType,
uint32_t rtp_timestamp_with_offset,
rtc::ArrayView<const uint8_t> payload,
int64_t absolute_capture_timestamp_ms,
rtc::ArrayView<const uint32_t> csrcs)>;
ChannelSendFrameTransformerDelegate(
SendFrameCallback send_frame_callback,
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
TaskQueueBase* encoder_queue);
// Registers `this` as callback for `frame_transformer_`, to get the
// transformed frames.
void Init();
// Unregisters and releases the `frame_transformer_` reference, and resets
// `send_frame_callback_` under lock. Called from ChannelSend destructor to
// prevent running the callback on a dangling channel.
void Reset();
// Delegates the call to FrameTransformerInterface::TransformFrame, to
// transform the frame asynchronously.
void Transform(AudioFrameType frame_type,
uint8_t payload_type,
uint32_t rtp_timestamp,
const uint8_t* payload_data,
size_t payload_size,
int64_t absolute_capture_timestamp_ms,
uint32_t ssrc,
const std::string& codec_mime_type);
// Implements TransformedFrameCallback. Can be called on any thread.
void OnTransformedFrame(
std::unique_ptr<TransformableFrameInterface> frame) override;
void StartShortCircuiting() override;
// Delegates the call to ChannelSend::SendRtpAudio on the `encoder_queue_`,
// by calling `send_audio_callback_`.
void SendFrame(std::unique_ptr<TransformableFrameInterface> frame) const;
protected:
~ChannelSendFrameTransformerDelegate() override = default;
private:
mutable Mutex send_lock_;
SendFrameCallback send_frame_callback_ RTC_GUARDED_BY(send_lock_);
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer_;
TaskQueueBase* const encoder_queue_;
bool short_circuit_ RTC_GUARDED_BY(send_lock_) = false;
};
std::unique_ptr<TransformableAudioFrameInterface> CloneSenderAudioFrame(
TransformableAudioFrameInterface* original);
} // namespace webrtc
#endif // AUDIO_CHANNEL_SEND_FRAME_TRANSFORMER_DELEGATE_H_

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2015 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.
*/
#ifndef AUDIO_CONVERSION_H_
#define AUDIO_CONVERSION_H_
#include <stddef.h>
#include <stdint.h>
namespace webrtc {
// Convert fixed point number with 8 bit fractional part, to floating point.
inline float Q8ToFloat(uint32_t v) {
return static_cast<float>(v) / (1 << 8);
}
// Convert fixed point number with 14 bit fractional part, to floating point.
inline float Q14ToFloat(uint32_t v) {
return static_cast<float>(v) / (1 << 14);
}
} // namespace webrtc
#endif // AUDIO_CONVERSION_H_

View file

@ -0,0 +1,188 @@
/*
* Copyright (c) 2015 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.
*/
#ifndef AUDIO_MOCK_VOE_CHANNEL_PROXY_H_
#define AUDIO_MOCK_VOE_CHANNEL_PROXY_H_
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "api/crypto/frame_decryptor_interface.h"
#include "api/test/mock_frame_encryptor.h"
#include "audio/channel_receive.h"
#include "audio/channel_send.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "test/gmock.h"
namespace webrtc {
namespace test {
class MockChannelReceive : public voe::ChannelReceiveInterface {
public:
MOCK_METHOD(void, SetNACKStatus, (bool enable, int max_packets), (override));
MOCK_METHOD(void, SetNonSenderRttMeasurement, (bool enabled), (override));
MOCK_METHOD(void,
RegisterReceiverCongestionControlObjects,
(PacketRouter*),
(override));
MOCK_METHOD(void, ResetReceiverCongestionControlObjects, (), (override));
MOCK_METHOD(CallReceiveStatistics, GetRTCPStatistics, (), (const, override));
MOCK_METHOD(NetworkStatistics,
GetNetworkStatistics,
(bool),
(const, override));
MOCK_METHOD(AudioDecodingCallStats,
GetDecodingCallStatistics,
(),
(const, override));
MOCK_METHOD(int, GetSpeechOutputLevelFullRange, (), (const, override));
MOCK_METHOD(double, GetTotalOutputEnergy, (), (const, override));
MOCK_METHOD(double, GetTotalOutputDuration, (), (const, override));
MOCK_METHOD(uint32_t, GetDelayEstimate, (), (const, override));
MOCK_METHOD(void, SetSink, (AudioSinkInterface*), (override));
MOCK_METHOD(void, OnRtpPacket, (const RtpPacketReceived& packet), (override));
MOCK_METHOD(void,
ReceivedRTCPPacket,
(const uint8_t*, size_t length),
(override));
MOCK_METHOD(void, SetChannelOutputVolumeScaling, (float scaling), (override));
MOCK_METHOD(AudioMixer::Source::AudioFrameInfo,
GetAudioFrameWithInfo,
(int sample_rate_hz, AudioFrame*),
(override));
MOCK_METHOD(int, PreferredSampleRate, (), (const, override));
MOCK_METHOD(void, SetSourceTracker, (SourceTracker*), (override));
MOCK_METHOD(void,
SetAssociatedSendChannel,
(const voe::ChannelSendInterface*),
(override));
MOCK_METHOD(bool,
GetPlayoutRtpTimestamp,
(uint32_t*, int64_t*),
(const, override));
MOCK_METHOD(void,
SetEstimatedPlayoutNtpTimestampMs,
(int64_t ntp_timestamp_ms, int64_t time_ms),
(override));
MOCK_METHOD(absl::optional<int64_t>,
GetCurrentEstimatedPlayoutNtpTimestampMs,
(int64_t now_ms),
(const, override));
MOCK_METHOD(absl::optional<Syncable::Info>,
GetSyncInfo,
(),
(const, override));
MOCK_METHOD(bool, SetMinimumPlayoutDelay, (int delay_ms), (override));
MOCK_METHOD(bool, SetBaseMinimumPlayoutDelayMs, (int delay_ms), (override));
MOCK_METHOD(int, GetBaseMinimumPlayoutDelayMs, (), (const, override));
MOCK_METHOD((absl::optional<std::pair<int, SdpAudioFormat>>),
GetReceiveCodec,
(),
(const, override));
MOCK_METHOD(void,
SetReceiveCodecs,
((const std::map<int, SdpAudioFormat>& codecs)),
(override));
MOCK_METHOD(void, StartPlayout, (), (override));
MOCK_METHOD(void, StopPlayout, (), (override));
MOCK_METHOD(
void,
SetDepacketizerToDecoderFrameTransformer,
(rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer),
(override));
MOCK_METHOD(
void,
SetFrameDecryptor,
(rtc::scoped_refptr<webrtc::FrameDecryptorInterface> frame_decryptor),
(override));
MOCK_METHOD(void, OnLocalSsrcChange, (uint32_t local_ssrc), (override));
MOCK_METHOD(uint32_t, GetLocalSsrc, (), (const, override));
};
class MockChannelSend : public voe::ChannelSendInterface {
public:
MOCK_METHOD(void,
SetEncoder,
(int payload_type,
const SdpAudioFormat& encoder_format,
std::unique_ptr<AudioEncoder> encoder),
(override));
MOCK_METHOD(
void,
ModifyEncoder,
(rtc::FunctionView<void(std::unique_ptr<AudioEncoder>*)> modifier),
(override));
MOCK_METHOD(void,
CallEncoder,
(rtc::FunctionView<void(AudioEncoder*)> modifier),
(override));
MOCK_METHOD(void, SetRTCP_CNAME, (absl::string_view c_name), (override));
MOCK_METHOD(void,
SetSendAudioLevelIndicationStatus,
(bool enable, int id),
(override));
MOCK_METHOD(void,
RegisterSenderCongestionControlObjects,
(RtpTransportControllerSendInterface*),
(override));
MOCK_METHOD(void, ResetSenderCongestionControlObjects, (), (override));
MOCK_METHOD(CallSendStatistics, GetRTCPStatistics, (), (const, override));
MOCK_METHOD(std::vector<ReportBlockData>,
GetRemoteRTCPReportBlocks,
(),
(const, override));
MOCK_METHOD(ANAStats, GetANAStatistics, (), (const, override));
MOCK_METHOD(void,
RegisterCngPayloadType,
(int payload_type, int payload_frequency),
(override));
MOCK_METHOD(void,
SetSendTelephoneEventPayloadType,
(int payload_type, int payload_frequency),
(override));
MOCK_METHOD(bool,
SendTelephoneEventOutband,
(int event, int duration_ms),
(override));
MOCK_METHOD(void,
OnBitrateAllocation,
(BitrateAllocationUpdate update),
(override));
MOCK_METHOD(void, SetInputMute, (bool muted), (override));
MOCK_METHOD(void,
ReceivedRTCPPacket,
(const uint8_t*, size_t length),
(override));
MOCK_METHOD(void,
ProcessAndEncodeAudio,
(std::unique_ptr<AudioFrame>),
(override));
MOCK_METHOD(RtpRtcpInterface*, GetRtpRtcp, (), (const, override));
MOCK_METHOD(int, GetTargetBitrate, (), (const, override));
MOCK_METHOD(int64_t, GetRTT, (), (const, override));
MOCK_METHOD(void, StartSend, (), (override));
MOCK_METHOD(void, StopSend, (), (override));
MOCK_METHOD(void,
SetFrameEncryptor,
(rtc::scoped_refptr<FrameEncryptorInterface> frame_encryptor),
(override));
MOCK_METHOD(
void,
SetEncoderToPacketizerFrameTransformer,
(rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer),
(override));
};
} // namespace test
} // namespace webrtc
#endif // AUDIO_MOCK_VOE_CHANNEL_PROXY_H_

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2012 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 "audio/remix_resample.h"
#include "api/audio/audio_frame.h"
#include "audio/utility/audio_frame_operations.h"
#include "common_audio/resampler/include/push_resampler.h"
#include "rtc_base/checks.h"
namespace webrtc {
namespace voe {
void RemixAndResample(const AudioFrame& src_frame,
PushResampler<int16_t>* resampler,
AudioFrame* dst_frame) {
RemixAndResample(src_frame.data(), src_frame.samples_per_channel_,
src_frame.num_channels_, src_frame.sample_rate_hz_,
resampler, dst_frame);
dst_frame->timestamp_ = src_frame.timestamp_;
dst_frame->elapsed_time_ms_ = src_frame.elapsed_time_ms_;
dst_frame->ntp_time_ms_ = src_frame.ntp_time_ms_;
dst_frame->packet_infos_ = src_frame.packet_infos_;
}
void RemixAndResample(const int16_t* src_data,
size_t samples_per_channel,
size_t num_channels,
int sample_rate_hz,
PushResampler<int16_t>* resampler,
AudioFrame* dst_frame) {
const int16_t* audio_ptr = src_data;
size_t audio_ptr_num_channels = num_channels;
int16_t downmixed_audio[AudioFrame::kMaxDataSizeSamples];
// Downmix before resampling.
if (num_channels > dst_frame->num_channels_) {
RTC_DCHECK(num_channels == 2 || num_channels == 4)
<< "num_channels: " << num_channels;
RTC_DCHECK(dst_frame->num_channels_ == 1 || dst_frame->num_channels_ == 2)
<< "dst_frame->num_channels_: " << dst_frame->num_channels_;
AudioFrameOperations::DownmixChannels(
src_data, num_channels, samples_per_channel, dst_frame->num_channels_,
downmixed_audio);
audio_ptr = downmixed_audio;
audio_ptr_num_channels = dst_frame->num_channels_;
}
if (resampler->InitializeIfNeeded(sample_rate_hz, dst_frame->sample_rate_hz_,
audio_ptr_num_channels) == -1) {
RTC_FATAL() << "InitializeIfNeeded failed: sample_rate_hz = "
<< sample_rate_hz << ", dst_frame->sample_rate_hz_ = "
<< dst_frame->sample_rate_hz_
<< ", audio_ptr_num_channels = " << audio_ptr_num_channels;
}
// TODO(yujo): for muted input frames, don't resample. Either 1) allow
// resampler to return output length without doing the resample, so we know
// how much to zero here; or 2) make resampler accept a hint that the input is
// zeroed.
const size_t src_length = samples_per_channel * audio_ptr_num_channels;
int out_length =
resampler->Resample(audio_ptr, src_length, dst_frame->mutable_data(),
AudioFrame::kMaxDataSizeSamples);
if (out_length == -1) {
RTC_FATAL() << "Resample failed: audio_ptr = " << audio_ptr
<< ", src_length = " << src_length
<< ", dst_frame->mutable_data() = "
<< dst_frame->mutable_data();
}
dst_frame->samples_per_channel_ = out_length / audio_ptr_num_channels;
// Upmix after resampling.
if (num_channels == 1 && dst_frame->num_channels_ == 2) {
// The audio in dst_frame really is mono at this point; MonoToStereo will
// set this back to stereo.
dst_frame->num_channels_ = 1;
AudioFrameOperations::UpmixChannels(2, dst_frame);
}
}
} // namespace voe
} // namespace webrtc

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2012 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.
*/
#ifndef AUDIO_REMIX_RESAMPLE_H_
#define AUDIO_REMIX_RESAMPLE_H_
#include "api/audio/audio_frame.h"
#include "common_audio/resampler/include/push_resampler.h"
namespace webrtc {
namespace voe {
// Upmix or downmix and resample the audio to `dst_frame`. Expects `dst_frame`
// to have its sample rate and channels members set to the desired values.
// Updates the `samples_per_channel_` member accordingly.
//
// This version has an AudioFrame `src_frame` as input and sets the output
// `timestamp_`, `elapsed_time_ms_` and `ntp_time_ms_` members equals to the
// input ones.
void RemixAndResample(const AudioFrame& src_frame,
PushResampler<int16_t>* resampler,
AudioFrame* dst_frame);
// This version has a pointer to the samples `src_data` as input and receives
// `samples_per_channel`, `num_channels` and `sample_rate_hz` of the data as
// parameters.
void RemixAndResample(const int16_t* src_data,
size_t samples_per_channel,
size_t num_channels,
int sample_rate_hz,
PushResampler<int16_t>* resampler,
AudioFrame* dst_frame);
} // namespace voe
} // namespace webrtc
#endif // AUDIO_REMIX_RESAMPLE_H_

View file

@ -0,0 +1,294 @@
/*
* Copyright (c) 2012 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 "audio/utility/audio_frame_operations.h"
#include <string.h>
#include <algorithm>
#include <cstdint>
#include <utility>
#include "common_audio/include/audio_util.h"
#include "rtc_base/checks.h"
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
namespace {
// 2.7ms @ 48kHz, 4ms @ 32kHz, 8ms @ 16kHz.
const size_t kMuteFadeFrames = 128;
const float kMuteFadeInc = 1.0f / kMuteFadeFrames;
} // namespace
void AudioFrameOperations::Add(const AudioFrame& frame_to_add,
AudioFrame* result_frame) {
// Sanity check.
RTC_DCHECK(result_frame);
RTC_DCHECK_GT(result_frame->num_channels_, 0);
RTC_DCHECK_EQ(result_frame->num_channels_, frame_to_add.num_channels_);
bool no_previous_data = result_frame->muted();
if (result_frame->samples_per_channel_ != frame_to_add.samples_per_channel_) {
// Special case we have no data to start with.
RTC_DCHECK_EQ(result_frame->samples_per_channel_, 0);
result_frame->samples_per_channel_ = frame_to_add.samples_per_channel_;
no_previous_data = true;
}
if (result_frame->vad_activity_ == AudioFrame::kVadActive ||
frame_to_add.vad_activity_ == AudioFrame::kVadActive) {
result_frame->vad_activity_ = AudioFrame::kVadActive;
} else if (result_frame->vad_activity_ == AudioFrame::kVadUnknown ||
frame_to_add.vad_activity_ == AudioFrame::kVadUnknown) {
result_frame->vad_activity_ = AudioFrame::kVadUnknown;
}
if (result_frame->speech_type_ != frame_to_add.speech_type_)
result_frame->speech_type_ = AudioFrame::kUndefined;
if (!frame_to_add.muted()) {
const int16_t* in_data = frame_to_add.data();
int16_t* out_data = result_frame->mutable_data();
size_t length =
frame_to_add.samples_per_channel_ * frame_to_add.num_channels_;
if (no_previous_data) {
std::copy(in_data, in_data + length, out_data);
} else {
for (size_t i = 0; i < length; i++) {
const int32_t wrap_guard = static_cast<int32_t>(out_data[i]) +
static_cast<int32_t>(in_data[i]);
out_data[i] = rtc::saturated_cast<int16_t>(wrap_guard);
}
}
}
}
int AudioFrameOperations::MonoToStereo(AudioFrame* frame) {
if (frame->num_channels_ != 1) {
return -1;
}
UpmixChannels(2, frame);
return 0;
}
int AudioFrameOperations::StereoToMono(AudioFrame* frame) {
if (frame->num_channels_ != 2) {
return -1;
}
DownmixChannels(1, frame);
return frame->num_channels_ == 1 ? 0 : -1;
}
void AudioFrameOperations::QuadToStereo(const int16_t* src_audio,
size_t samples_per_channel,
int16_t* dst_audio) {
for (size_t i = 0; i < samples_per_channel; i++) {
dst_audio[i * 2] =
(static_cast<int32_t>(src_audio[4 * i]) + src_audio[4 * i + 1]) >> 1;
dst_audio[i * 2 + 1] =
(static_cast<int32_t>(src_audio[4 * i + 2]) + src_audio[4 * i + 3]) >>
1;
}
}
int AudioFrameOperations::QuadToStereo(AudioFrame* frame) {
if (frame->num_channels_ != 4) {
return -1;
}
RTC_DCHECK_LE(frame->samples_per_channel_ * 4,
AudioFrame::kMaxDataSizeSamples);
if (!frame->muted()) {
QuadToStereo(frame->data(), frame->samples_per_channel_,
frame->mutable_data());
}
frame->num_channels_ = 2;
return 0;
}
void AudioFrameOperations::DownmixChannels(const int16_t* src_audio,
size_t src_channels,
size_t samples_per_channel,
size_t dst_channels,
int16_t* dst_audio) {
if (src_channels > 1 && dst_channels == 1) {
DownmixInterleavedToMono(src_audio, samples_per_channel, src_channels,
dst_audio);
return;
} else if (src_channels == 4 && dst_channels == 2) {
QuadToStereo(src_audio, samples_per_channel, dst_audio);
return;
}
RTC_DCHECK_NOTREACHED() << "src_channels: " << src_channels
<< ", dst_channels: " << dst_channels;
}
void AudioFrameOperations::DownmixChannels(size_t dst_channels,
AudioFrame* frame) {
RTC_DCHECK_LE(frame->samples_per_channel_ * frame->num_channels_,
AudioFrame::kMaxDataSizeSamples);
if (frame->num_channels_ > 1 && dst_channels == 1) {
if (!frame->muted()) {
DownmixInterleavedToMono(frame->data(), frame->samples_per_channel_,
frame->num_channels_, frame->mutable_data());
}
frame->num_channels_ = 1;
} else if (frame->num_channels_ == 4 && dst_channels == 2) {
int err = QuadToStereo(frame);
RTC_DCHECK_EQ(err, 0);
} else {
RTC_DCHECK_NOTREACHED() << "src_channels: " << frame->num_channels_
<< ", dst_channels: " << dst_channels;
}
}
void AudioFrameOperations::UpmixChannels(size_t target_number_of_channels,
AudioFrame* frame) {
RTC_DCHECK_EQ(frame->num_channels_, 1);
RTC_DCHECK_LE(frame->samples_per_channel_ * target_number_of_channels,
AudioFrame::kMaxDataSizeSamples);
if (frame->num_channels_ != 1 ||
frame->samples_per_channel_ * target_number_of_channels >
AudioFrame::kMaxDataSizeSamples) {
return;
}
if (!frame->muted()) {
// Up-mixing done in place. Going backwards through the frame ensure nothing
// is irrevocably overwritten.
int16_t* frame_data = frame->mutable_data();
for (int i = frame->samples_per_channel_ - 1; i >= 0; i--) {
for (size_t j = 0; j < target_number_of_channels; ++j) {
frame_data[target_number_of_channels * i + j] = frame_data[i];
}
}
}
frame->num_channels_ = target_number_of_channels;
}
void AudioFrameOperations::SwapStereoChannels(AudioFrame* frame) {
RTC_DCHECK(frame);
if (frame->num_channels_ != 2 || frame->muted()) {
return;
}
int16_t* frame_data = frame->mutable_data();
for (size_t i = 0; i < frame->samples_per_channel_ * 2; i += 2) {
std::swap(frame_data[i], frame_data[i + 1]);
}
}
void AudioFrameOperations::Mute(AudioFrame* frame,
bool previous_frame_muted,
bool current_frame_muted) {
RTC_DCHECK(frame);
if (!previous_frame_muted && !current_frame_muted) {
// Not muted, don't touch.
} else if (previous_frame_muted && current_frame_muted) {
// Frame fully muted.
size_t total_samples = frame->samples_per_channel_ * frame->num_channels_;
RTC_DCHECK_GE(AudioFrame::kMaxDataSizeSamples, total_samples);
frame->Mute();
} else {
// Fade is a no-op on a muted frame.
if (frame->muted()) {
return;
}
// Limit number of samples to fade, if frame isn't long enough.
size_t count = kMuteFadeFrames;
float inc = kMuteFadeInc;
if (frame->samples_per_channel_ < kMuteFadeFrames) {
count = frame->samples_per_channel_;
if (count > 0) {
inc = 1.0f / count;
}
}
size_t start = 0;
size_t end = count;
float start_g = 0.0f;
if (current_frame_muted) {
// Fade out the last `count` samples of frame.
RTC_DCHECK(!previous_frame_muted);
start = frame->samples_per_channel_ - count;
end = frame->samples_per_channel_;
start_g = 1.0f;
inc = -inc;
} else {
// Fade in the first `count` samples of frame.
RTC_DCHECK(previous_frame_muted);
}
// Perform fade.
int16_t* frame_data = frame->mutable_data();
size_t channels = frame->num_channels_;
for (size_t j = 0; j < channels; ++j) {
float g = start_g;
for (size_t i = start * channels; i < end * channels; i += channels) {
g += inc;
frame_data[i + j] *= g;
}
}
}
}
void AudioFrameOperations::Mute(AudioFrame* frame) {
Mute(frame, true, true);
}
void AudioFrameOperations::ApplyHalfGain(AudioFrame* frame) {
RTC_DCHECK(frame);
RTC_DCHECK_GT(frame->num_channels_, 0);
if (frame->num_channels_ < 1 || frame->muted()) {
return;
}
int16_t* frame_data = frame->mutable_data();
for (size_t i = 0; i < frame->samples_per_channel_ * frame->num_channels_;
i++) {
frame_data[i] = frame_data[i] >> 1;
}
}
int AudioFrameOperations::Scale(float left, float right, AudioFrame* frame) {
if (frame->num_channels_ != 2) {
return -1;
} else if (frame->muted()) {
return 0;
}
int16_t* frame_data = frame->mutable_data();
for (size_t i = 0; i < frame->samples_per_channel_; i++) {
frame_data[2 * i] = static_cast<int16_t>(left * frame_data[2 * i]);
frame_data[2 * i + 1] = static_cast<int16_t>(right * frame_data[2 * i + 1]);
}
return 0;
}
int AudioFrameOperations::ScaleWithSat(float scale, AudioFrame* frame) {
if (frame->muted()) {
return 0;
}
int16_t* frame_data = frame->mutable_data();
for (size_t i = 0; i < frame->samples_per_channel_ * frame->num_channels_;
i++) {
frame_data[i] = rtc::saturated_cast<int16_t>(scale * frame_data[i]);
}
return 0;
}
} // namespace webrtc

View file

@ -0,0 +1,107 @@
/*
* Copyright (c) 2012 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.
*/
#ifndef AUDIO_UTILITY_AUDIO_FRAME_OPERATIONS_H_
#define AUDIO_UTILITY_AUDIO_FRAME_OPERATIONS_H_
#include <stddef.h>
#include <stdint.h>
#include "absl/base/attributes.h"
#include "api/audio/audio_frame.h"
namespace webrtc {
// TODO(andrew): consolidate this with utility.h and audio_frame_manipulator.h.
// Change reference parameters to pointers. Consider using a namespace rather
// than a class.
class AudioFrameOperations {
public:
// Add samples in `frame_to_add` with samples in `result_frame`
// putting the results in `results_frame`. The fields
// `vad_activity_` and `speech_type_` of the result frame are
// updated. If `result_frame` is empty (`samples_per_channel_`==0),
// the samples in `frame_to_add` are added to it. The number of
// channels and number of samples per channel must match except when
// `result_frame` is empty.
static void Add(const AudioFrame& frame_to_add, AudioFrame* result_frame);
// `frame.num_channels_` will be updated. This version checks for sufficient
// buffer size and that `num_channels_` is mono. Use UpmixChannels
// instead. TODO(bugs.webrtc.org/8649): remove.
ABSL_DEPRECATED("bugs.webrtc.org/8649")
static int MonoToStereo(AudioFrame* frame);
// `frame.num_channels_` will be updated. This version checks that
// `num_channels_` is stereo. Use DownmixChannels
// instead. TODO(bugs.webrtc.org/8649): remove.
ABSL_DEPRECATED("bugs.webrtc.org/8649")
static int StereoToMono(AudioFrame* frame);
// Downmixes 4 channels `src_audio` to stereo `dst_audio`. This is an in-place
// operation, meaning `src_audio` and `dst_audio` may point to the same
// buffer.
static void QuadToStereo(const int16_t* src_audio,
size_t samples_per_channel,
int16_t* dst_audio);
// `frame.num_channels_` will be updated. This version checks that
// `num_channels_` is 4 channels.
static int QuadToStereo(AudioFrame* frame);
// Downmixes `src_channels` `src_audio` to `dst_channels` `dst_audio`.
// This is an in-place operation, meaning `src_audio` and `dst_audio`
// may point to the same buffer. Supported channel combinations are
// Stereo to Mono, Quad to Mono, and Quad to Stereo.
static void DownmixChannels(const int16_t* src_audio,
size_t src_channels,
size_t samples_per_channel,
size_t dst_channels,
int16_t* dst_audio);
// `frame.num_channels_` will be updated. This version checks that
// `num_channels_` and `dst_channels` are valid and performs relevant downmix.
// Supported channel combinations are N channels to Mono, and Quad to Stereo.
static void DownmixChannels(size_t dst_channels, AudioFrame* frame);
// `frame.num_channels_` will be updated. This version checks that
// `num_channels_` and `dst_channels` are valid and performs relevant
// downmix. Supported channel combinations are Mono to N
// channels. The single channel is replicated.
static void UpmixChannels(size_t target_number_of_channels,
AudioFrame* frame);
// Swap the left and right channels of `frame`. Fails silently if `frame` is
// not stereo.
static void SwapStereoChannels(AudioFrame* frame);
// Conditionally zero out contents of `frame` for implementing audio mute:
// `previous_frame_muted` && `current_frame_muted` - Zero out whole frame.
// `previous_frame_muted` && !`current_frame_muted` - Fade-in at frame start.
// !`previous_frame_muted` && `current_frame_muted` - Fade-out at frame end.
// !`previous_frame_muted` && !`current_frame_muted` - Leave frame untouched.
static void Mute(AudioFrame* frame,
bool previous_frame_muted,
bool current_frame_muted);
// Zero out contents of frame.
static void Mute(AudioFrame* frame);
// Halve samples in `frame`.
static void ApplyHalfGain(AudioFrame* frame);
static int Scale(float left, float right, AudioFrame* frame);
static int ScaleWithSat(float scale, AudioFrame* frame);
};
} // namespace webrtc
#endif // AUDIO_UTILITY_AUDIO_FRAME_OPERATIONS_H_

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2012 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 "audio/utility/channel_mixer.h"
#include "audio/utility/channel_mixing_matrix.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
ChannelMixer::ChannelMixer(ChannelLayout input_layout,
ChannelLayout output_layout)
: input_layout_(input_layout),
output_layout_(output_layout),
input_channels_(ChannelLayoutToChannelCount(input_layout)),
output_channels_(ChannelLayoutToChannelCount(output_layout)) {
// Create the transformation matrix.
ChannelMixingMatrix matrix_builder(input_layout_, input_channels_,
output_layout_, output_channels_);
remapping_ = matrix_builder.CreateTransformationMatrix(&matrix_);
}
ChannelMixer::~ChannelMixer() = default;
void ChannelMixer::Transform(AudioFrame* frame) {
RTC_DCHECK(frame);
RTC_DCHECK_EQ(matrix_[0].size(), static_cast<size_t>(input_channels_));
RTC_DCHECK_EQ(matrix_.size(), static_cast<size_t>(output_channels_));
// Leave the audio frame intact if the channel layouts for in and out are
// identical.
if (input_layout_ == output_layout_) {
return;
}
if (IsUpMixing()) {
RTC_CHECK_LE(frame->samples_per_channel() * output_channels_,
frame->max_16bit_samples());
}
// Only change the number of output channels if the audio frame is muted.
if (frame->muted()) {
frame->num_channels_ = output_channels_;
frame->channel_layout_ = output_layout_;
return;
}
const int16_t* in_audio = frame->data();
// Only allocate fresh memory at first access or if the required size has
// increased.
// TODO(henrika): we might be able to do downmixing in-place and thereby avoid
// extra memory allocation and a memcpy.
const size_t num_elements = frame->samples_per_channel() * output_channels_;
if (audio_vector_ == nullptr || num_elements > audio_vector_size_) {
audio_vector_.reset(new int16_t[num_elements]);
audio_vector_size_ = num_elements;
}
int16_t* out_audio = audio_vector_.get();
// Modify the number of channels by creating a weighted sum of input samples
// where the weights (scale factors) for each output sample are given by the
// transformation matrix.
for (size_t i = 0; i < frame->samples_per_channel(); i++) {
for (size_t output_ch = 0; output_ch < output_channels_; ++output_ch) {
float acc_value = 0.0f;
for (size_t input_ch = 0; input_ch < input_channels_; ++input_ch) {
const float scale = matrix_[output_ch][input_ch];
// Scale should always be positive.
RTC_DCHECK_GE(scale, 0);
// Each output sample is a weighted sum of input samples.
acc_value += scale * in_audio[i * input_channels_ + input_ch];
}
const size_t index = output_channels_ * i + output_ch;
RTC_CHECK_LE(index, audio_vector_size_);
out_audio[index] = rtc::saturated_cast<int16_t>(acc_value);
}
}
// Update channel information.
frame->num_channels_ = output_channels_;
frame->channel_layout_ = output_layout_;
// Copy the output result to the audio frame in `frame`.
memcpy(
frame->mutable_data(), out_audio,
sizeof(int16_t) * frame->samples_per_channel() * frame->num_channels());
}
} // namespace webrtc

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2019 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.
*/
#ifndef AUDIO_UTILITY_CHANNEL_MIXER_H_
#define AUDIO_UTILITY_CHANNEL_MIXER_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <vector>
#include "api/audio/audio_frame.h"
#include "api/audio/channel_layout.h"
namespace webrtc {
// ChannelMixer is for converting audio between channel layouts. The conversion
// matrix is built upon construction and used during each Transform() call. The
// algorithm works by generating a conversion matrix mapping each output channel
// to list of input channels. The transform renders all of the output channels,
// with each output channel rendered according to a weighted sum of the relevant
// input channels as defined in the matrix.
// This file is derived from Chromium's media/base/channel_mixer.h.
class ChannelMixer {
public:
// To mix two channels into one and preserve loudness, we must apply
// (1 / sqrt(2)) gain to each.
static constexpr float kHalfPower = 0.707106781186547524401f;
ChannelMixer(ChannelLayout input_layout, ChannelLayout output_layout);
~ChannelMixer();
// Transforms all input channels corresponding to the selected `input_layout`
// to the number of channels in the selected `output_layout`.
// Example usage (downmix from stereo to mono):
//
// ChannelMixer mixer(CHANNEL_LAYOUT_STEREO, CHANNEL_LAYOUT_MONO);
// AudioFrame frame;
// frame.samples_per_channel_ = 160;
// frame.num_channels_ = 2;
// EXPECT_EQ(2u, frame.channels());
// mixer.Transform(&frame);
// EXPECT_EQ(1u, frame.channels());
//
void Transform(AudioFrame* frame);
private:
bool IsUpMixing() const { return output_channels_ > input_channels_; }
// Selected channel layouts.
const ChannelLayout input_layout_;
const ChannelLayout output_layout_;
// Channel counts for input and output.
const size_t input_channels_;
const size_t output_channels_;
// 2D matrix of output channels to input channels.
std::vector<std::vector<float> > matrix_;
// 1D array used as temporary storage during the transformation.
std::unique_ptr<int16_t[]> audio_vector_;
// Number of elements allocated for `audio_vector_`.
size_t audio_vector_size_ = 0;
// Optimization case for when we can simply remap the input channels to output
// channels, i.e., when all scaling factors in `matrix_` equals 1.0.
bool remapping_;
// Delete the copy constructor and assignment operator.
ChannelMixer(const ChannelMixer& other) = delete;
ChannelMixer& operator=(const ChannelMixer& other) = delete;
};
} // namespace webrtc
#endif // AUDIO_UTILITY_CHANNEL_MIXER_H_

View file

@ -0,0 +1,333 @@
/*
* Copyright (c) 2019 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 "audio/utility/channel_mixing_matrix.h"
#include <stddef.h>
#include <algorithm>
#include "audio/utility/channel_mixer.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
namespace {
// Selects the default usage of VoIP channel mapping adjustments.
bool UseChannelMappingAdjustmentsByDefault() {
return !field_trial::IsEnabled(
"WebRTC-VoIPChannelRemixingAdjustmentKillSwitch");
}
} // namespace
static void ValidateLayout(ChannelLayout layout) {
RTC_CHECK_NE(layout, CHANNEL_LAYOUT_NONE);
RTC_CHECK_LE(layout, CHANNEL_LAYOUT_MAX);
RTC_CHECK_NE(layout, CHANNEL_LAYOUT_UNSUPPORTED);
RTC_CHECK_NE(layout, CHANNEL_LAYOUT_DISCRETE);
RTC_CHECK_NE(layout, CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC);
// Verify there's at least one channel. Should always be true here by virtue
// of not being one of the invalid layouts, but lets double check to be sure.
int channel_count = ChannelLayoutToChannelCount(layout);
RTC_DCHECK_GT(channel_count, 0);
// If we have more than one channel, verify a symmetric layout for sanity.
// The unit test will verify all possible layouts, so this can be a DCHECK.
// Symmetry allows simplifying the matrix building code by allowing us to
// assume that if one channel of a pair exists, the other will too.
if (channel_count > 1) {
// Assert that LEFT exists if and only if RIGHT exists, and so on.
RTC_DCHECK_EQ(ChannelOrder(layout, LEFT) >= 0,
ChannelOrder(layout, RIGHT) >= 0);
RTC_DCHECK_EQ(ChannelOrder(layout, SIDE_LEFT) >= 0,
ChannelOrder(layout, SIDE_RIGHT) >= 0);
RTC_DCHECK_EQ(ChannelOrder(layout, BACK_LEFT) >= 0,
ChannelOrder(layout, BACK_RIGHT) >= 0);
RTC_DCHECK_EQ(ChannelOrder(layout, LEFT_OF_CENTER) >= 0,
ChannelOrder(layout, RIGHT_OF_CENTER) >= 0);
} else {
RTC_DCHECK_EQ(layout, CHANNEL_LAYOUT_MONO);
}
}
ChannelMixingMatrix::ChannelMixingMatrix(ChannelLayout input_layout,
int input_channels,
ChannelLayout output_layout,
int output_channels)
: use_voip_channel_mapping_adjustments_(
UseChannelMappingAdjustmentsByDefault()),
input_layout_(input_layout),
input_channels_(input_channels),
output_layout_(output_layout),
output_channels_(output_channels) {
// Stereo down mix should never be the output layout.
RTC_CHECK_NE(output_layout, CHANNEL_LAYOUT_STEREO_DOWNMIX);
// Verify that the layouts are supported
if (input_layout != CHANNEL_LAYOUT_DISCRETE)
ValidateLayout(input_layout);
if (output_layout != CHANNEL_LAYOUT_DISCRETE)
ValidateLayout(output_layout);
// Special case for 5.0, 5.1 with back channels when upmixed to 7.0, 7.1,
// which should map the back LR to side LR.
if (input_layout_ == CHANNEL_LAYOUT_5_0_BACK &&
output_layout_ == CHANNEL_LAYOUT_7_0) {
input_layout_ = CHANNEL_LAYOUT_5_0;
} else if (input_layout_ == CHANNEL_LAYOUT_5_1_BACK &&
output_layout_ == CHANNEL_LAYOUT_7_1) {
input_layout_ = CHANNEL_LAYOUT_5_1;
}
}
ChannelMixingMatrix::~ChannelMixingMatrix() = default;
bool ChannelMixingMatrix::CreateTransformationMatrix(
std::vector<std::vector<float>>* matrix) {
matrix_ = matrix;
// Size out the initial matrix.
matrix_->reserve(output_channels_);
for (int output_ch = 0; output_ch < output_channels_; ++output_ch)
matrix_->push_back(std::vector<float>(input_channels_, 0));
// First check for discrete case.
if (input_layout_ == CHANNEL_LAYOUT_DISCRETE ||
output_layout_ == CHANNEL_LAYOUT_DISCRETE) {
// If the number of input channels is more than output channels, then
// copy as many as we can then drop the remaining input channels.
// If the number of input channels is less than output channels, then
// copy them all, then zero out the remaining output channels.
int passthrough_channels = std::min(input_channels_, output_channels_);
for (int i = 0; i < passthrough_channels; ++i)
(*matrix_)[i][i] = 1;
return true;
}
// If specified, use adjusted channel mapping for the VoIP scenario.
if (use_voip_channel_mapping_adjustments_ &&
input_layout_ == CHANNEL_LAYOUT_MONO &&
ChannelLayoutToChannelCount(output_layout_) >= 2) {
// Only place the mono input in the front left and right channels.
(*matrix_)[0][0] = 1.f;
(*matrix_)[1][0] = 1.f;
for (size_t output_ch = 2; output_ch < matrix_->size(); ++output_ch) {
(*matrix_)[output_ch][0] = 0.f;
}
return true;
}
// Route matching channels and figure out which ones aren't accounted for.
for (Channels ch = LEFT; ch < CHANNELS_MAX + 1;
ch = static_cast<Channels>(ch + 1)) {
int input_ch_index = ChannelOrder(input_layout_, ch);
if (input_ch_index < 0)
continue;
int output_ch_index = ChannelOrder(output_layout_, ch);
if (output_ch_index < 0) {
unaccounted_inputs_.push_back(ch);
continue;
}
RTC_DCHECK_LT(static_cast<size_t>(output_ch_index), matrix_->size());
RTC_DCHECK_LT(static_cast<size_t>(input_ch_index),
(*matrix_)[output_ch_index].size());
(*matrix_)[output_ch_index][input_ch_index] = 1;
}
// If all input channels are accounted for, there's nothing left to do.
if (unaccounted_inputs_.empty()) {
// Since all output channels map directly to inputs we can optimize.
return true;
}
// Mix front LR into center.
if (IsUnaccounted(LEFT)) {
// When down mixing to mono from stereo, we need to be careful of full scale
// stereo mixes. Scaling by 1 / sqrt(2) here will likely lead to clipping
// so we use 1 / 2 instead.
float scale =
(output_layout_ == CHANNEL_LAYOUT_MONO && input_channels_ == 2)
? 0.5
: ChannelMixer::kHalfPower;
Mix(LEFT, CENTER, scale);
Mix(RIGHT, CENTER, scale);
}
// Mix center into front LR.
if (IsUnaccounted(CENTER)) {
// When up mixing from mono, just do a copy to front LR.
float scale =
(input_layout_ == CHANNEL_LAYOUT_MONO) ? 1 : ChannelMixer::kHalfPower;
MixWithoutAccounting(CENTER, LEFT, scale);
Mix(CENTER, RIGHT, scale);
}
// Mix back LR into: side LR || back center || front LR || front center.
if (IsUnaccounted(BACK_LEFT)) {
if (HasOutputChannel(SIDE_LEFT)) {
// If the input has side LR, mix back LR into side LR, but instead if the
// input doesn't have side LR (but output does) copy back LR to side LR.
float scale = HasInputChannel(SIDE_LEFT) ? ChannelMixer::kHalfPower : 1;
Mix(BACK_LEFT, SIDE_LEFT, scale);
Mix(BACK_RIGHT, SIDE_RIGHT, scale);
} else if (HasOutputChannel(BACK_CENTER)) {
// Mix back LR into back center.
Mix(BACK_LEFT, BACK_CENTER, ChannelMixer::kHalfPower);
Mix(BACK_RIGHT, BACK_CENTER, ChannelMixer::kHalfPower);
} else if (output_layout_ > CHANNEL_LAYOUT_MONO) {
// Mix back LR into front LR.
Mix(BACK_LEFT, LEFT, ChannelMixer::kHalfPower);
Mix(BACK_RIGHT, RIGHT, ChannelMixer::kHalfPower);
} else {
// Mix back LR into front center.
Mix(BACK_LEFT, CENTER, ChannelMixer::kHalfPower);
Mix(BACK_RIGHT, CENTER, ChannelMixer::kHalfPower);
}
}
// Mix side LR into: back LR || back center || front LR || front center.
if (IsUnaccounted(SIDE_LEFT)) {
if (HasOutputChannel(BACK_LEFT)) {
// If the input has back LR, mix side LR into back LR, but instead if the
// input doesn't have back LR (but output does) copy side LR to back LR.
float scale = HasInputChannel(BACK_LEFT) ? ChannelMixer::kHalfPower : 1;
Mix(SIDE_LEFT, BACK_LEFT, scale);
Mix(SIDE_RIGHT, BACK_RIGHT, scale);
} else if (HasOutputChannel(BACK_CENTER)) {
// Mix side LR into back center.
Mix(SIDE_LEFT, BACK_CENTER, ChannelMixer::kHalfPower);
Mix(SIDE_RIGHT, BACK_CENTER, ChannelMixer::kHalfPower);
} else if (output_layout_ > CHANNEL_LAYOUT_MONO) {
// Mix side LR into front LR.
Mix(SIDE_LEFT, LEFT, ChannelMixer::kHalfPower);
Mix(SIDE_RIGHT, RIGHT, ChannelMixer::kHalfPower);
} else {
// Mix side LR into front center.
Mix(SIDE_LEFT, CENTER, ChannelMixer::kHalfPower);
Mix(SIDE_RIGHT, CENTER, ChannelMixer::kHalfPower);
}
}
// Mix back center into: back LR || side LR || front LR || front center.
if (IsUnaccounted(BACK_CENTER)) {
if (HasOutputChannel(BACK_LEFT)) {
// Mix back center into back LR.
MixWithoutAccounting(BACK_CENTER, BACK_LEFT, ChannelMixer::kHalfPower);
Mix(BACK_CENTER, BACK_RIGHT, ChannelMixer::kHalfPower);
} else if (HasOutputChannel(SIDE_LEFT)) {
// Mix back center into side LR.
MixWithoutAccounting(BACK_CENTER, SIDE_LEFT, ChannelMixer::kHalfPower);
Mix(BACK_CENTER, SIDE_RIGHT, ChannelMixer::kHalfPower);
} else if (output_layout_ > CHANNEL_LAYOUT_MONO) {
// Mix back center into front LR.
// TODO(dalecurtis): Not sure about these values?
MixWithoutAccounting(BACK_CENTER, LEFT, ChannelMixer::kHalfPower);
Mix(BACK_CENTER, RIGHT, ChannelMixer::kHalfPower);
} else {
// Mix back center into front center.
// TODO(dalecurtis): Not sure about these values?
Mix(BACK_CENTER, CENTER, ChannelMixer::kHalfPower);
}
}
// Mix LR of center into: front LR || front center.
if (IsUnaccounted(LEFT_OF_CENTER)) {
if (HasOutputChannel(LEFT)) {
// Mix LR of center into front LR.
Mix(LEFT_OF_CENTER, LEFT, ChannelMixer::kHalfPower);
Mix(RIGHT_OF_CENTER, RIGHT, ChannelMixer::kHalfPower);
} else {
// Mix LR of center into front center.
Mix(LEFT_OF_CENTER, CENTER, ChannelMixer::kHalfPower);
Mix(RIGHT_OF_CENTER, CENTER, ChannelMixer::kHalfPower);
}
}
// Mix LFE into: front center || front LR.
if (IsUnaccounted(LFE)) {
if (!HasOutputChannel(CENTER)) {
// Mix LFE into front LR.
MixWithoutAccounting(LFE, LEFT, ChannelMixer::kHalfPower);
Mix(LFE, RIGHT, ChannelMixer::kHalfPower);
} else {
// Mix LFE into front center.
Mix(LFE, CENTER, ChannelMixer::kHalfPower);
}
}
// All channels should now be accounted for.
RTC_DCHECK(unaccounted_inputs_.empty());
// See if the output `matrix_` is simply a remapping matrix. If each input
// channel maps to a single output channel we can simply remap. Doing this
// programmatically is less fragile than logic checks on channel mappings.
for (int output_ch = 0; output_ch < output_channels_; ++output_ch) {
int input_mappings = 0;
for (int input_ch = 0; input_ch < input_channels_; ++input_ch) {
// We can only remap if each row contains a single scale of 1. I.e., each
// output channel is mapped from a single unscaled input channel.
if ((*matrix_)[output_ch][input_ch] != 1 || ++input_mappings > 1)
return false;
}
}
// If we've gotten here, `matrix_` is simply a remapping.
return true;
}
void ChannelMixingMatrix::AccountFor(Channels ch) {
unaccounted_inputs_.erase(
std::find(unaccounted_inputs_.begin(), unaccounted_inputs_.end(), ch));
}
bool ChannelMixingMatrix::IsUnaccounted(Channels ch) const {
return std::find(unaccounted_inputs_.begin(), unaccounted_inputs_.end(),
ch) != unaccounted_inputs_.end();
}
bool ChannelMixingMatrix::HasInputChannel(Channels ch) const {
return ChannelOrder(input_layout_, ch) >= 0;
}
bool ChannelMixingMatrix::HasOutputChannel(Channels ch) const {
return ChannelOrder(output_layout_, ch) >= 0;
}
void ChannelMixingMatrix::Mix(Channels input_ch,
Channels output_ch,
float scale) {
MixWithoutAccounting(input_ch, output_ch, scale);
AccountFor(input_ch);
}
void ChannelMixingMatrix::MixWithoutAccounting(Channels input_ch,
Channels output_ch,
float scale) {
int input_ch_index = ChannelOrder(input_layout_, input_ch);
int output_ch_index = ChannelOrder(output_layout_, output_ch);
RTC_DCHECK(IsUnaccounted(input_ch));
RTC_DCHECK_GE(input_ch_index, 0);
RTC_DCHECK_GE(output_ch_index, 0);
RTC_DCHECK_EQ((*matrix_)[output_ch_index][input_ch_index], 0);
(*matrix_)[output_ch_index][input_ch_index] = scale;
}
} // namespace webrtc

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2019 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.
*/
#ifndef AUDIO_UTILITY_CHANNEL_MIXING_MATRIX_H_
#define AUDIO_UTILITY_CHANNEL_MIXING_MATRIX_H_
#include <vector>
#include "api/audio/channel_layout.h"
namespace webrtc {
class ChannelMixingMatrix {
public:
ChannelMixingMatrix(ChannelLayout input_layout,
int input_channels,
ChannelLayout output_layout,
int output_channels);
~ChannelMixingMatrix();
// Create the transformation matrix of input channels to output channels.
// Updates the empty matrix with the transformation, and returns true
// if the transformation is just a remapping of channels (no mixing).
// The size of `matrix` is `output_channels` x `input_channels`, i.e., the
// number of rows equals the number of output channels and the number of
// columns corresponds to the number of input channels.
// This file is derived from Chromium's media/base/channel_mixing_matrix.h.
bool CreateTransformationMatrix(std::vector<std::vector<float>>* matrix);
private:
const bool use_voip_channel_mapping_adjustments_;
// Result transformation of input channels to output channels
std::vector<std::vector<float>>* matrix_;
// Input and output channel layout provided during construction.
ChannelLayout input_layout_;
int input_channels_;
ChannelLayout output_layout_;
int output_channels_;
// Helper variable for tracking which inputs are currently unaccounted,
// should be empty after construction completes.
std::vector<Channels> unaccounted_inputs_;
// Helper methods for managing unaccounted input channels.
void AccountFor(Channels ch);
bool IsUnaccounted(Channels ch) const;
// Helper methods for checking if `ch` exists in either `input_layout_` or
// `output_layout_` respectively.
bool HasInputChannel(Channels ch) const;
bool HasOutputChannel(Channels ch) const;
// Helper methods for updating `matrix_` with the proper value for
// mixing `input_ch` into `output_ch`. MixWithoutAccounting() does not
// remove the channel from `unaccounted_inputs_`.
void Mix(Channels input_ch, Channels output_ch, float scale);
void MixWithoutAccounting(Channels input_ch, Channels output_ch, float scale);
// Delete the copy constructor and assignment operator.
ChannelMixingMatrix(const ChannelMixingMatrix& other) = delete;
ChannelMixingMatrix& operator=(const ChannelMixingMatrix& other) = delete;
};
} // namespace webrtc
#endif // AUDIO_UTILITY_CHANNEL_MIXING_MATRIX_H_

View file

@ -0,0 +1,173 @@
/*
* Copyright (c) 2020 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 "audio/voip/audio_channel.h"
#include <utility>
#include <vector>
#include "api/audio_codecs/audio_format.h"
#include "api/task_queue/task_queue_factory.h"
#include "modules/rtp_rtcp/include/receive_statistics.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
constexpr int kRtcpReportIntervalMs = 5000;
} // namespace
AudioChannel::AudioChannel(
Transport* transport,
uint32_t local_ssrc,
TaskQueueFactory* task_queue_factory,
AudioMixer* audio_mixer,
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory)
: audio_mixer_(audio_mixer) {
RTC_DCHECK(task_queue_factory);
RTC_DCHECK(audio_mixer);
Clock* clock = Clock::GetRealTimeClock();
receive_statistics_ = ReceiveStatistics::Create(clock);
RtpRtcpInterface::Configuration rtp_config;
rtp_config.clock = clock;
rtp_config.audio = true;
rtp_config.receive_statistics = receive_statistics_.get();
rtp_config.rtcp_report_interval_ms = kRtcpReportIntervalMs;
rtp_config.outgoing_transport = transport;
rtp_config.local_media_ssrc = local_ssrc;
rtp_rtcp_ = ModuleRtpRtcpImpl2::Create(rtp_config);
rtp_rtcp_->SetSendingMediaStatus(false);
rtp_rtcp_->SetRTCPStatus(RtcpMode::kCompound);
ingress_ = std::make_unique<AudioIngress>(rtp_rtcp_.get(), clock,
receive_statistics_.get(),
std::move(decoder_factory));
egress_ =
std::make_unique<AudioEgress>(rtp_rtcp_.get(), clock, task_queue_factory);
// Set the instance of audio ingress to be part of audio mixer for ADM to
// fetch audio samples to play.
audio_mixer_->AddSource(ingress_.get());
}
AudioChannel::~AudioChannel() {
if (egress_->IsSending()) {
StopSend();
}
if (ingress_->IsPlaying()) {
StopPlay();
}
audio_mixer_->RemoveSource(ingress_.get());
// TODO(bugs.webrtc.org/11581): unclear if we still need to clear `egress_`
// here.
egress_.reset();
ingress_.reset();
}
bool AudioChannel::StartSend() {
// If encoder has not been set, return false.
if (!egress_->StartSend()) {
return false;
}
// Start sending with RTP stack if it has not been sending yet.
if (!rtp_rtcp_->Sending()) {
rtp_rtcp_->SetSendingStatus(true);
}
return true;
}
void AudioChannel::StopSend() {
egress_->StopSend();
// Deactivate RTP stack when both sending and receiving are stopped.
// SetSendingStatus(false) triggers the transmission of RTCP BYE
// message to remote endpoint.
if (!ingress_->IsPlaying() && rtp_rtcp_->Sending()) {
rtp_rtcp_->SetSendingStatus(false);
}
}
bool AudioChannel::StartPlay() {
// If decoders have not been set, return false.
if (!ingress_->StartPlay()) {
return false;
}
// If RTP stack is not sending then start sending as in recv-only mode, RTCP
// receiver report is expected.
if (!rtp_rtcp_->Sending()) {
rtp_rtcp_->SetSendingStatus(true);
}
return true;
}
void AudioChannel::StopPlay() {
ingress_->StopPlay();
// Deactivate RTP stack only when both sending and receiving are stopped.
if (!rtp_rtcp_->SendingMedia() && rtp_rtcp_->Sending()) {
rtp_rtcp_->SetSendingStatus(false);
}
}
IngressStatistics AudioChannel::GetIngressStatistics() {
IngressStatistics ingress_stats;
NetworkStatistics stats = ingress_->GetNetworkStatistics();
ingress_stats.neteq_stats.total_samples_received = stats.totalSamplesReceived;
ingress_stats.neteq_stats.concealed_samples = stats.concealedSamples;
ingress_stats.neteq_stats.concealment_events = stats.concealmentEvents;
ingress_stats.neteq_stats.jitter_buffer_delay_ms = stats.jitterBufferDelayMs;
ingress_stats.neteq_stats.jitter_buffer_emitted_count =
stats.jitterBufferEmittedCount;
ingress_stats.neteq_stats.jitter_buffer_target_delay_ms =
stats.jitterBufferTargetDelayMs;
ingress_stats.neteq_stats.inserted_samples_for_deceleration =
stats.insertedSamplesForDeceleration;
ingress_stats.neteq_stats.removed_samples_for_acceleration =
stats.removedSamplesForAcceleration;
ingress_stats.neteq_stats.silent_concealed_samples =
stats.silentConcealedSamples;
ingress_stats.neteq_stats.fec_packets_received = stats.fecPacketsReceived;
ingress_stats.neteq_stats.fec_packets_discarded = stats.fecPacketsDiscarded;
ingress_stats.neteq_stats.delayed_packet_outage_samples =
stats.delayedPacketOutageSamples;
ingress_stats.neteq_stats.relative_packet_arrival_delay_ms =
stats.relativePacketArrivalDelayMs;
ingress_stats.neteq_stats.interruption_count = stats.interruptionCount;
ingress_stats.neteq_stats.total_interruption_duration_ms =
stats.totalInterruptionDurationMs;
ingress_stats.total_duration = ingress_->GetOutputTotalDuration();
return ingress_stats;
}
ChannelStatistics AudioChannel::GetChannelStatistics() {
ChannelStatistics channel_stat = ingress_->GetChannelStatistics();
StreamDataCounters rtp_stats, rtx_stats;
rtp_rtcp_->GetSendStreamDataCounters(&rtp_stats, &rtx_stats);
channel_stat.bytes_sent =
rtp_stats.transmitted.payload_bytes + rtx_stats.transmitted.payload_bytes;
channel_stat.packets_sent =
rtp_stats.transmitted.packets + rtx_stats.transmitted.packets;
return channel_stat;
}
} // namespace webrtc

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2020 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.
*/
#ifndef AUDIO_VOIP_AUDIO_CHANNEL_H_
#define AUDIO_VOIP_AUDIO_CHANNEL_H_
#include <map>
#include <memory>
#include <queue>
#include <utility>
#include "api/task_queue/task_queue_factory.h"
#include "api/voip/voip_base.h"
#include "api/voip/voip_statistics.h"
#include "audio/voip/audio_egress.h"
#include "audio/voip/audio_ingress.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h"
#include "rtc_base/ref_count.h"
namespace webrtc {
// AudioChannel represents a single media session and provides APIs over
// AudioIngress and AudioEgress. Note that a single RTP stack is shared with
// these two classes as it has both sending and receiving capabilities.
class AudioChannel : public rtc::RefCountInterface {
public:
AudioChannel(Transport* transport,
uint32_t local_ssrc,
TaskQueueFactory* task_queue_factory,
AudioMixer* audio_mixer,
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory);
~AudioChannel() override;
// Set and get ChannelId that this audio channel belongs for debugging and
// logging purpose.
void SetId(ChannelId id) { id_ = id; }
ChannelId GetId() const { return id_; }
// APIs to start/stop audio channel on each direction.
// StartSend/StartPlay returns false if encoder/decoders
// have not been set, respectively.
bool StartSend();
void StopSend();
bool StartPlay();
void StopPlay();
// APIs relayed to AudioEgress.
bool IsSendingMedia() const { return egress_->IsSending(); }
AudioSender* GetAudioSender() { return egress_.get(); }
void SetEncoder(int payload_type,
const SdpAudioFormat& encoder_format,
std::unique_ptr<AudioEncoder> encoder) {
egress_->SetEncoder(payload_type, encoder_format, std::move(encoder));
}
absl::optional<SdpAudioFormat> GetEncoderFormat() const {
return egress_->GetEncoderFormat();
}
void RegisterTelephoneEventType(int rtp_payload_type, int sample_rate_hz) {
egress_->RegisterTelephoneEventType(rtp_payload_type, sample_rate_hz);
}
bool SendTelephoneEvent(int dtmf_event, int duration_ms) {
return egress_->SendTelephoneEvent(dtmf_event, duration_ms);
}
void SetMute(bool enable) { egress_->SetMute(enable); }
// APIs relayed to AudioIngress.
bool IsPlaying() const { return ingress_->IsPlaying(); }
void ReceivedRTPPacket(rtc::ArrayView<const uint8_t> rtp_packet) {
ingress_->ReceivedRTPPacket(rtp_packet);
}
void ReceivedRTCPPacket(rtc::ArrayView<const uint8_t> rtcp_packet) {
ingress_->ReceivedRTCPPacket(rtcp_packet);
}
void SetReceiveCodecs(const std::map<int, SdpAudioFormat>& codecs) {
ingress_->SetReceiveCodecs(codecs);
}
IngressStatistics GetIngressStatistics();
ChannelStatistics GetChannelStatistics();
// See comments on the methods used from AudioEgress and AudioIngress.
// Conversion to double is following what is done in
// DoubleAudioLevelFromIntAudioLevel method in rtc_stats_collector.cc to be
// consistent.
double GetInputAudioLevel() const {
return egress_->GetInputAudioLevel() / 32767.0;
}
double GetInputTotalEnergy() const { return egress_->GetInputTotalEnergy(); }
double GetInputTotalDuration() const {
return egress_->GetInputTotalDuration();
}
double GetOutputAudioLevel() const {
return ingress_->GetOutputAudioLevel() / 32767.0;
}
double GetOutputTotalEnergy() const {
return ingress_->GetOutputTotalEnergy();
}
double GetOutputTotalDuration() const {
return ingress_->GetOutputTotalDuration();
}
// Internal API for testing purpose.
void SendRTCPReportForTesting(RTCPPacketType type) {
int32_t result = rtp_rtcp_->SendRTCP(type);
RTC_DCHECK(result == 0);
}
private:
// ChannelId that this audio channel belongs for logging purpose.
ChannelId id_;
// Synchronization is handled internally by AudioMixer.
AudioMixer* audio_mixer_;
// Listed in order for safe destruction of AudioChannel object.
// Synchronization for these are handled internally.
std::unique_ptr<ReceiveStatistics> receive_statistics_;
std::unique_ptr<ModuleRtpRtcpImpl2> rtp_rtcp_;
std::unique_ptr<AudioIngress> ingress_;
std::unique_ptr<AudioEgress> egress_;
};
} // namespace webrtc
#endif // AUDIO_VOIP_AUDIO_CHANNEL_H_

View file

@ -0,0 +1,190 @@
/*
* Copyright (c) 2020 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 "audio/voip/audio_egress.h"
#include <utility>
#include <vector>
#include "api/sequence_checker.h"
#include "rtc_base/logging.h"
namespace webrtc {
AudioEgress::AudioEgress(RtpRtcpInterface* rtp_rtcp,
Clock* clock,
TaskQueueFactory* task_queue_factory)
: rtp_rtcp_(rtp_rtcp),
rtp_sender_audio_(clock, rtp_rtcp_->RtpSender()),
audio_coding_(AudioCodingModule::Create()),
encoder_queue_(task_queue_factory->CreateTaskQueue(
"AudioEncoder",
TaskQueueFactory::Priority::NORMAL)),
encoder_queue_checker_(encoder_queue_.get()) {
audio_coding_->RegisterTransportCallback(this);
}
AudioEgress::~AudioEgress() {
audio_coding_->RegisterTransportCallback(nullptr);
// Delete first to ensure that there are no running tasks when the other
// members are destroyed.
encoder_queue_ = nullptr;
}
bool AudioEgress::IsSending() const {
return rtp_rtcp_->SendingMedia();
}
void AudioEgress::SetEncoder(int payload_type,
const SdpAudioFormat& encoder_format,
std::unique_ptr<AudioEncoder> encoder) {
RTC_DCHECK_GE(payload_type, 0);
RTC_DCHECK_LE(payload_type, 127);
SetEncoderFormat(encoder_format);
// The RTP/RTCP module needs to know the RTP timestamp rate (i.e. clockrate)
// as well as some other things, so we collect this info and send it along.
rtp_rtcp_->RegisterSendPayloadFrequency(payload_type,
encoder->RtpTimestampRateHz());
rtp_sender_audio_.RegisterAudioPayload("audio", payload_type,
encoder->RtpTimestampRateHz(),
encoder->NumChannels(), 0);
audio_coding_->SetEncoder(std::move(encoder));
}
bool AudioEgress::StartSend() {
if (!GetEncoderFormat()) {
RTC_DLOG(LS_WARNING) << "Send codec has not been set yet";
return false;
}
rtp_rtcp_->SetSendingMediaStatus(true);
return true;
}
void AudioEgress::StopSend() {
rtp_rtcp_->SetSendingMediaStatus(false);
}
void AudioEgress::SendAudioData(std::unique_ptr<AudioFrame> audio_frame) {
RTC_DCHECK_GT(audio_frame->samples_per_channel_, 0);
RTC_DCHECK_LE(audio_frame->num_channels_, 8);
encoder_queue_->PostTask(
[this, audio_frame = std::move(audio_frame)]() mutable {
RTC_DCHECK_RUN_ON(&encoder_queue_checker_);
if (!rtp_rtcp_->SendingMedia()) {
return;
}
double duration_seconds =
static_cast<double>(audio_frame->samples_per_channel_) /
audio_frame->sample_rate_hz_;
input_audio_level_.ComputeLevel(*audio_frame, duration_seconds);
AudioFrameOperations::Mute(audio_frame.get(),
encoder_context_.previously_muted_,
encoder_context_.mute_);
encoder_context_.previously_muted_ = encoder_context_.mute_;
audio_frame->timestamp_ = encoder_context_.frame_rtp_timestamp_;
// This call will trigger AudioPacketizationCallback::SendData if
// encoding is done and payload is ready for packetization and
// transmission. Otherwise, it will return without invoking the
// callback.
if (audio_coding_->Add10MsData(*audio_frame) < 0) {
RTC_DLOG(LS_ERROR) << "ACM::Add10MsData() failed.";
return;
}
encoder_context_.frame_rtp_timestamp_ +=
rtc::dchecked_cast<uint32_t>(audio_frame->samples_per_channel_);
});
}
int32_t AudioEgress::SendData(AudioFrameType frame_type,
uint8_t payload_type,
uint32_t timestamp,
const uint8_t* payload_data,
size_t payload_size) {
RTC_DCHECK_RUN_ON(&encoder_queue_checker_);
rtc::ArrayView<const uint8_t> payload(payload_data, payload_size);
// Currently we don't get a capture time from downstream modules (ADM,
// AudioTransportImpl).
// TODO(natim@webrtc.org): Integrate once it's ready.
constexpr uint32_t kUndefinedCaptureTime = -1;
// Push data from ACM to RTP/RTCP-module to deliver audio frame for
// packetization.
if (!rtp_rtcp_->OnSendingRtpFrame(timestamp, kUndefinedCaptureTime,
payload_type,
/*force_sender_report=*/false)) {
return -1;
}
const uint32_t rtp_timestamp = timestamp + rtp_rtcp_->StartTimestamp();
// This call will trigger Transport::SendPacket() from the RTP/RTCP module.
if (!rtp_sender_audio_.SendAudio({.type = frame_type,
.payload = payload,
.payload_id = payload_type,
.rtp_timestamp = rtp_timestamp})) {
RTC_DLOG(LS_ERROR)
<< "AudioEgress::SendData() failed to send data to RTP/RTCP module";
return -1;
}
return 0;
}
void AudioEgress::RegisterTelephoneEventType(int rtp_payload_type,
int sample_rate_hz) {
RTC_DCHECK_GE(rtp_payload_type, 0);
RTC_DCHECK_LE(rtp_payload_type, 127);
rtp_rtcp_->RegisterSendPayloadFrequency(rtp_payload_type, sample_rate_hz);
rtp_sender_audio_.RegisterAudioPayload("telephone-event", rtp_payload_type,
sample_rate_hz, 0, 0);
}
bool AudioEgress::SendTelephoneEvent(int dtmf_event, int duration_ms) {
RTC_DCHECK_GE(dtmf_event, 0);
RTC_DCHECK_LE(dtmf_event, 255);
RTC_DCHECK_GE(duration_ms, 0);
RTC_DCHECK_LE(duration_ms, 65535);
if (!IsSending()) {
return false;
}
constexpr int kTelephoneEventAttenuationdB = 10;
if (rtp_sender_audio_.SendTelephoneEvent(dtmf_event, duration_ms,
kTelephoneEventAttenuationdB) != 0) {
RTC_DLOG(LS_ERROR) << "SendTelephoneEvent() failed to send event";
return false;
}
return true;
}
void AudioEgress::SetMute(bool mute) {
encoder_queue_->PostTask([this, mute] {
RTC_DCHECK_RUN_ON(&encoder_queue_checker_);
encoder_context_.mute_ = mute;
});
}
} // namespace webrtc

View file

@ -0,0 +1,158 @@
/*
* Copyright (c) 2020 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.
*/
#ifndef AUDIO_VOIP_AUDIO_EGRESS_H_
#define AUDIO_VOIP_AUDIO_EGRESS_H_
#include <memory>
#include <string>
#include "api/audio_codecs/audio_format.h"
#include "api/sequence_checker.h"
#include "api/task_queue/task_queue_base.h"
#include "api/task_queue/task_queue_factory.h"
#include "audio/audio_level.h"
#include "audio/utility/audio_frame_operations.h"
#include "call/audio_sender.h"
#include "modules/audio_coding/include/audio_coding_module.h"
#include "modules/rtp_rtcp/include/report_block_data.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
#include "modules/rtp_rtcp/source/rtp_sender_audio.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/time_utils.h"
namespace webrtc {
// AudioEgress receives input samples from AudioDeviceModule via
// AudioTransportImpl through AudioSender interface. Once it encodes the sample
// via selected encoder through AudioPacketizationCallback interface, the
// encoded payload will be packetized by the RTP stack, resulting in ready to
// send RTP packet to remote endpoint.
//
// TaskQueue is used to encode and send RTP asynchrounously as some OS platform
// uses the same thread for both audio input and output sample deliveries which
// can affect audio quality.
//
// Note that this class is originally based on ChannelSend in
// audio/channel_send.cc with non-audio related logic trimmed as aimed for
// smaller footprint.
class AudioEgress : public AudioSender, public AudioPacketizationCallback {
public:
AudioEgress(RtpRtcpInterface* rtp_rtcp,
Clock* clock,
TaskQueueFactory* task_queue_factory);
~AudioEgress() override;
// Set the encoder format and payload type for AudioCodingModule.
// It's possible to change the encoder type during its active usage.
// `payload_type` must be the type that is negotiated with peer through
// offer/answer.
void SetEncoder(int payload_type,
const SdpAudioFormat& encoder_format,
std::unique_ptr<AudioEncoder> encoder);
// Start or stop sending operation of AudioEgress. This will start/stop
// the RTP stack also causes encoder queue thread to start/stop
// processing input audio samples. StartSend will return false if
// a send codec has not been set.
bool StartSend();
void StopSend();
// Query the state of the RTP stack. This returns true if StartSend()
// called and false if StopSend() is called.
bool IsSending() const;
// Enable or disable Mute state.
void SetMute(bool mute);
// Retrieve current encoder format info. This returns encoder format set
// by SetEncoder() and if encoder is not set, this will return nullopt.
absl::optional<SdpAudioFormat> GetEncoderFormat() const {
MutexLock lock(&lock_);
return encoder_format_;
}
// Register the payload type and sample rate for DTMF (RFC 4733) payload.
void RegisterTelephoneEventType(int rtp_payload_type, int sample_rate_hz);
// Send DTMF named event as specified by
// https://tools.ietf.org/html/rfc4733#section-3.2
// `duration_ms` specifies the duration of DTMF packets that will be emitted
// in place of real RTP packets instead.
// This will return true when requested dtmf event is successfully scheduled
// otherwise false when the dtmf queue reached maximum of 20 events.
bool SendTelephoneEvent(int dtmf_event, int duration_ms);
// See comments on LevelFullRange, TotalEnergy, TotalDuration from
// audio/audio_level.h.
int GetInputAudioLevel() const { return input_audio_level_.LevelFullRange(); }
double GetInputTotalEnergy() const {
return input_audio_level_.TotalEnergy();
}
double GetInputTotalDuration() const {
return input_audio_level_.TotalDuration();
}
// Implementation of AudioSender interface.
void SendAudioData(std::unique_ptr<AudioFrame> audio_frame) override;
// Implementation of AudioPacketizationCallback interface.
int32_t SendData(AudioFrameType frame_type,
uint8_t payload_type,
uint32_t timestamp,
const uint8_t* payload_data,
size_t payload_size) override;
private:
void SetEncoderFormat(const SdpAudioFormat& encoder_format) {
MutexLock lock(&lock_);
encoder_format_ = encoder_format;
}
mutable Mutex lock_;
// Current encoder format selected by caller.
absl::optional<SdpAudioFormat> encoder_format_ RTC_GUARDED_BY(lock_);
// Synchronization is handled internally by RtpRtcp.
RtpRtcpInterface* const rtp_rtcp_;
// Synchronization is handled internally by RTPSenderAudio.
RTPSenderAudio rtp_sender_audio_;
// Synchronization is handled internally by AudioCodingModule.
const std::unique_ptr<AudioCodingModule> audio_coding_;
// Synchronization is handled internally by voe::AudioLevel.
voe::AudioLevel input_audio_level_;
// Struct that holds all variables used by encoder task queue.
struct EncoderContext {
// Offset used to mark rtp timestamp in sample rate unit in
// newly received audio frame from AudioTransport.
uint32_t frame_rtp_timestamp_ = 0;
// Flag to track mute state from caller. `previously_muted_` is used to
// track previous state as part of input to AudioFrameOperations::Mute
// to implement fading effect when (un)mute is invoked.
bool mute_ = false;
bool previously_muted_ = false;
};
EncoderContext encoder_context_ RTC_GUARDED_BY(encoder_queue_checker_);
std::unique_ptr<TaskQueueBase, TaskQueueDeleter> encoder_queue_;
RTC_NO_UNIQUE_ADDRESS SequenceChecker encoder_queue_checker_;
};
} // namespace webrtc
#endif // AUDIO_VOIP_AUDIO_EGRESS_H_

View file

@ -0,0 +1,290 @@
/*
* Copyright (c) 2020 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 "audio/voip/audio_ingress.h"
#include <algorithm>
#include <utility>
#include <vector>
#include "api/audio_codecs/audio_format.h"
#include "audio/utility/audio_frame_operations.h"
#include "modules/audio_coding/include/audio_coding_module.h"
#include "modules/rtp_rtcp/source/byte_io.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h"
#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_minmax.h"
#include "rtc_base/time_utils.h"
namespace webrtc {
namespace {
acm2::AcmReceiver::Config CreateAcmConfig(
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory) {
acm2::AcmReceiver::Config acm_config;
acm_config.neteq_config.enable_muted_state = true;
acm_config.decoder_factory = decoder_factory;
return acm_config;
}
} // namespace
AudioIngress::AudioIngress(
RtpRtcpInterface* rtp_rtcp,
Clock* clock,
ReceiveStatistics* receive_statistics,
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory)
: playing_(false),
remote_ssrc_(0),
first_rtp_timestamp_(-1),
rtp_receive_statistics_(receive_statistics),
rtp_rtcp_(rtp_rtcp),
acm_receiver_(CreateAcmConfig(decoder_factory)),
ntp_estimator_(clock) {}
AudioIngress::~AudioIngress() = default;
AudioMixer::Source::AudioFrameInfo AudioIngress::GetAudioFrameWithInfo(
int sampling_rate,
AudioFrame* audio_frame) {
audio_frame->sample_rate_hz_ = sampling_rate;
// Get 10ms raw PCM data from the ACM.
bool muted = false;
if (acm_receiver_.GetAudio(sampling_rate, audio_frame, &muted) == -1) {
RTC_DLOG(LS_ERROR) << "GetAudio() failed!";
// In all likelihood, the audio in this frame is garbage. We return an
// error so that the audio mixer module doesn't add it to the mix. As
// a result, it won't be played out and the actions skipped here are
// irrelevant.
return AudioMixer::Source::AudioFrameInfo::kError;
}
if (muted) {
AudioFrameOperations::Mute(audio_frame);
}
// Measure audio level.
constexpr double kAudioSampleDurationSeconds = 0.01;
output_audio_level_.ComputeLevel(*audio_frame, kAudioSampleDurationSeconds);
// If caller invoked StopPlay(), then mute the frame.
if (!playing_) {
AudioFrameOperations::Mute(audio_frame);
muted = true;
}
// Set first rtp timestamp with first audio frame with valid timestamp.
if (first_rtp_timestamp_ < 0 && audio_frame->timestamp_ != 0) {
first_rtp_timestamp_ = audio_frame->timestamp_;
}
if (first_rtp_timestamp_ >= 0) {
// Compute elapsed and NTP times.
int64_t unwrap_timestamp;
{
MutexLock lock(&lock_);
unwrap_timestamp =
timestamp_wrap_handler_.Unwrap(audio_frame->timestamp_);
audio_frame->ntp_time_ms_ =
ntp_estimator_.Estimate(audio_frame->timestamp_);
}
// For clock rate, default to the playout sampling rate if we haven't
// received any packets yet.
absl::optional<std::pair<int, SdpAudioFormat>> decoder =
acm_receiver_.LastDecoder();
int clock_rate = decoder ? decoder->second.clockrate_hz
: acm_receiver_.last_output_sample_rate_hz();
RTC_DCHECK_GT(clock_rate, 0);
audio_frame->elapsed_time_ms_ =
(unwrap_timestamp - first_rtp_timestamp_) / (clock_rate / 1000);
}
return muted ? AudioMixer::Source::AudioFrameInfo::kMuted
: AudioMixer::Source::AudioFrameInfo::kNormal;
}
bool AudioIngress::StartPlay() {
{
MutexLock lock(&lock_);
if (receive_codec_info_.empty()) {
RTC_DLOG(LS_WARNING) << "Receive codecs have not been set yet";
return false;
}
}
playing_ = true;
return true;
}
void AudioIngress::SetReceiveCodecs(
const std::map<int, SdpAudioFormat>& codecs) {
{
MutexLock lock(&lock_);
for (const auto& kv : codecs) {
receive_codec_info_[kv.first] = kv.second.clockrate_hz;
}
}
acm_receiver_.SetCodecs(codecs);
}
void AudioIngress::ReceivedRTPPacket(rtc::ArrayView<const uint8_t> rtp_packet) {
RtpPacketReceived rtp_packet_received;
rtp_packet_received.Parse(rtp_packet.data(), rtp_packet.size());
// Set payload type's sampling rate before we feed it into ReceiveStatistics.
{
MutexLock lock(&lock_);
const auto& it =
receive_codec_info_.find(rtp_packet_received.PayloadType());
// If sampling rate info is not available in our received codec set, it
// would mean that remote media endpoint is sending incorrect payload id
// which can't be processed correctly especially on payload type id in
// dynamic range.
if (it == receive_codec_info_.end()) {
RTC_DLOG(LS_WARNING) << "Unexpected payload id received: "
<< rtp_packet_received.PayloadType();
return;
}
rtp_packet_received.set_payload_type_frequency(it->second);
}
// Track current remote SSRC.
if (rtp_packet_received.Ssrc() != remote_ssrc_) {
rtp_rtcp_->SetRemoteSSRC(rtp_packet_received.Ssrc());
remote_ssrc_.store(rtp_packet_received.Ssrc());
}
rtp_receive_statistics_->OnRtpPacket(rtp_packet_received);
RTPHeader header;
rtp_packet_received.GetHeader(&header);
size_t packet_length = rtp_packet_received.size();
if (packet_length < header.headerLength ||
(packet_length - header.headerLength) < header.paddingLength) {
RTC_DLOG(LS_ERROR) << "Packet length(" << packet_length << ") header("
<< header.headerLength << ") padding("
<< header.paddingLength << ")";
return;
}
const uint8_t* payload = rtp_packet_received.data() + header.headerLength;
size_t payload_length = packet_length - header.headerLength;
size_t payload_data_length = payload_length - header.paddingLength;
auto data_view = rtc::ArrayView<const uint8_t>(payload, payload_data_length);
// Push the incoming payload (parsed and ready for decoding) into the ACM.
if (acm_receiver_.InsertPacket(header, data_view) != 0) {
RTC_DLOG(LS_ERROR) << "AudioIngress::ReceivedRTPPacket() unable to "
"push data to the ACM";
}
}
void AudioIngress::ReceivedRTCPPacket(
rtc::ArrayView<const uint8_t> rtcp_packet) {
rtcp::CommonHeader rtcp_header;
if (rtcp_header.Parse(rtcp_packet.data(), rtcp_packet.size()) &&
(rtcp_header.type() == rtcp::SenderReport::kPacketType ||
rtcp_header.type() == rtcp::ReceiverReport::kPacketType)) {
RTC_DCHECK_GE(rtcp_packet.size(), 8);
uint32_t sender_ssrc =
ByteReader<uint32_t>::ReadBigEndian(rtcp_packet.data() + 4);
// If we don't have remote ssrc at this point, it's likely that remote
// endpoint is receive-only or it could have restarted the media.
if (sender_ssrc != remote_ssrc_) {
rtp_rtcp_->SetRemoteSSRC(sender_ssrc);
remote_ssrc_.store(sender_ssrc);
}
}
// Deliver RTCP packet to RTP/RTCP module for parsing and processing.
rtp_rtcp_->IncomingRtcpPacket(rtcp_packet);
absl::optional<TimeDelta> rtt = rtp_rtcp_->LastRtt();
if (!rtt.has_value()) {
// Waiting for valid RTT.
return;
}
absl::optional<RtpRtcpInterface::SenderReportStats> last_sr =
rtp_rtcp_->GetSenderReportStats();
if (!last_sr.has_value()) {
// Waiting for RTCP.
return;
}
{
MutexLock lock(&lock_);
ntp_estimator_.UpdateRtcpTimestamp(*rtt, last_sr->last_remote_timestamp,
last_sr->last_remote_rtp_timestamp);
}
}
ChannelStatistics AudioIngress::GetChannelStatistics() {
ChannelStatistics channel_stats;
// Get clockrate for current decoder ahead of jitter calculation.
uint32_t clockrate_hz = 0;
absl::optional<std::pair<int, SdpAudioFormat>> decoder =
acm_receiver_.LastDecoder();
if (decoder) {
clockrate_hz = decoder->second.clockrate_hz;
}
StreamStatistician* statistician =
rtp_receive_statistics_->GetStatistician(remote_ssrc_);
if (statistician) {
RtpReceiveStats stats = statistician->GetStats();
channel_stats.packets_lost = stats.packets_lost;
channel_stats.packets_received = stats.packet_counter.packets;
channel_stats.bytes_received = stats.packet_counter.payload_bytes;
channel_stats.remote_ssrc = remote_ssrc_;
if (clockrate_hz > 0) {
channel_stats.jitter = static_cast<double>(stats.jitter) / clockrate_hz;
}
}
// Get RTCP report using remote SSRC.
const std::vector<ReportBlockData>& report_data =
rtp_rtcp_->GetLatestReportBlockData();
for (const ReportBlockData& rtcp_report : report_data) {
if (rtp_rtcp_->SSRC() != rtcp_report.source_ssrc() ||
remote_ssrc_ != rtcp_report.sender_ssrc()) {
continue;
}
RemoteRtcpStatistics remote_stat;
remote_stat.packets_lost = rtcp_report.cumulative_lost();
remote_stat.fraction_lost = rtcp_report.fraction_lost();
if (clockrate_hz > 0) {
remote_stat.jitter = rtcp_report.jitter(clockrate_hz).seconds<double>();
}
if (rtcp_report.has_rtt()) {
remote_stat.round_trip_time = rtcp_report.last_rtt().seconds<double>();
}
remote_stat.last_report_received_timestamp_ms =
rtcp_report.report_block_timestamp_utc().ms();
channel_stats.remote_rtcp = remote_stat;
// Receive only channel won't send any RTP packets.
if (!channel_stats.remote_ssrc.has_value()) {
channel_stats.remote_ssrc = remote_ssrc_;
}
break;
}
return channel_stats;
}
} // namespace webrtc

View file

@ -0,0 +1,145 @@
/*
* Copyright (c) 2020 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.
*/
#ifndef AUDIO_VOIP_AUDIO_INGRESS_H_
#define AUDIO_VOIP_AUDIO_INGRESS_H_
#include <algorithm>
#include <atomic>
#include <map>
#include <memory>
#include <utility>
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "api/audio/audio_mixer.h"
#include "api/rtp_headers.h"
#include "api/scoped_refptr.h"
#include "api/voip/voip_statistics.h"
#include "audio/audio_level.h"
#include "modules/audio_coding/acm2/acm_receiver.h"
#include "modules/audio_coding/include/audio_coding_module.h"
#include "modules/rtp_rtcp/include/receive_statistics.h"
#include "modules/rtp_rtcp/include/remote_ntp_time_estimator.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
#include "rtc_base/numerics/sequence_number_unwrapper.h"
#include "rtc_base/synchronization/mutex.h"
namespace webrtc {
// AudioIngress handles incoming RTP/RTCP packets from the remote
// media endpoint. Received RTP packets are injected into AcmReceiver and
// when audio output thread requests for audio samples to play through system
// output such as speaker device, AudioIngress provides the samples via its
// implementation on AudioMixer::Source interface.
//
// Note that this class is originally based on ChannelReceive in
// audio/channel_receive.cc with non-audio related logic trimmed as aimed for
// smaller footprint.
class AudioIngress : public AudioMixer::Source {
public:
AudioIngress(RtpRtcpInterface* rtp_rtcp,
Clock* clock,
ReceiveStatistics* receive_statistics,
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory);
~AudioIngress() override;
// Start or stop receiving operation of AudioIngress.
bool StartPlay();
void StopPlay() {
playing_ = false;
output_audio_level_.ResetLevelFullRange();
}
// Query the state of the AudioIngress.
bool IsPlaying() const { return playing_; }
// Set the decoder formats and payload type for AcmReceiver where the
// key type (int) of the map is the payload type of SdpAudioFormat.
void SetReceiveCodecs(const std::map<int, SdpAudioFormat>& codecs);
// APIs to handle received RTP/RTCP packets from caller.
void ReceivedRTPPacket(rtc::ArrayView<const uint8_t> rtp_packet);
void ReceivedRTCPPacket(rtc::ArrayView<const uint8_t> rtcp_packet);
// See comments on LevelFullRange, TotalEnergy, TotalDuration from
// audio/audio_level.h.
int GetOutputAudioLevel() const {
return output_audio_level_.LevelFullRange();
}
double GetOutputTotalEnergy() { return output_audio_level_.TotalEnergy(); }
double GetOutputTotalDuration() {
return output_audio_level_.TotalDuration();
}
NetworkStatistics GetNetworkStatistics() const {
NetworkStatistics stats;
acm_receiver_.GetNetworkStatistics(&stats,
/*get_and_clear_legacy_stats=*/false);
return stats;
}
ChannelStatistics GetChannelStatistics();
// Implementation of AudioMixer::Source interface.
AudioMixer::Source::AudioFrameInfo GetAudioFrameWithInfo(
int sampling_rate,
AudioFrame* audio_frame) override;
int Ssrc() const override {
return rtc::dchecked_cast<int>(remote_ssrc_.load());
}
int PreferredSampleRate() const override {
// If we haven't received any RTP packet from remote and thus
// last_packet_sampling_rate is not available then use NetEq's sampling
// rate as that would be what would be used for audio output sample.
return std::max(acm_receiver_.last_packet_sample_rate_hz().value_or(0),
acm_receiver_.last_output_sample_rate_hz());
}
private:
// Indicates AudioIngress status as caller invokes Start/StopPlaying.
// If not playing, incoming RTP data processing is skipped, thus
// producing no data to output device.
std::atomic<bool> playing_;
// Currently active remote ssrc from remote media endpoint.
std::atomic<uint32_t> remote_ssrc_;
// The first rtp timestamp of the output audio frame that is used to
// calculate elasped time for subsequent audio frames.
std::atomic<int64_t> first_rtp_timestamp_;
// Synchronizaton is handled internally by ReceiveStatistics.
ReceiveStatistics* const rtp_receive_statistics_;
// Synchronizaton is handled internally by RtpRtcpInterface.
RtpRtcpInterface* const rtp_rtcp_;
// Synchronizaton is handled internally by acm2::AcmReceiver.
acm2::AcmReceiver acm_receiver_;
// Synchronizaton is handled internally by voe::AudioLevel.
voe::AudioLevel output_audio_level_;
Mutex lock_;
RemoteNtpTimeEstimator ntp_estimator_ RTC_GUARDED_BY(lock_);
// For receiving RTP statistics, this tracks the sampling rate value
// per payload type set when caller set via SetReceiveCodecs.
std::map<int, int> receive_codec_info_ RTC_GUARDED_BY(lock_);
RtpTimestampUnwrapper timestamp_wrap_handler_ RTC_GUARDED_BY(lock_);
};
} // namespace webrtc
#endif // AUDIO_VOIP_AUDIO_INGRESS_H_

View file

@ -0,0 +1,500 @@
/*
* Copyright (c) 2020 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 "audio/voip/voip_core.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "api/audio_codecs/audio_format.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
// For Windows, use specific enum type to initialize default audio device as
// defined in AudioDeviceModule::WindowsDeviceType.
#if defined(WEBRTC_WIN)
constexpr AudioDeviceModule::WindowsDeviceType kAudioDeviceId =
AudioDeviceModule::WindowsDeviceType::kDefaultCommunicationDevice;
#else
constexpr uint16_t kAudioDeviceId = 0;
#endif // defined(WEBRTC_WIN)
// Maximum value range limit on ChannelId. This can be increased without any
// side effect and only set at this moderate value for better readability for
// logging.
static constexpr int kMaxChannelId = 100000;
} // namespace
VoipCore::VoipCore(rtc::scoped_refptr<AudioEncoderFactory> encoder_factory,
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory,
std::unique_ptr<TaskQueueFactory> task_queue_factory,
rtc::scoped_refptr<AudioDeviceModule> audio_device_module,
rtc::scoped_refptr<AudioProcessing> audio_processing) {
encoder_factory_ = std::move(encoder_factory);
decoder_factory_ = std::move(decoder_factory);
task_queue_factory_ = std::move(task_queue_factory);
audio_device_module_ = std::move(audio_device_module);
audio_processing_ = std::move(audio_processing);
audio_mixer_ = AudioMixerImpl::Create();
// AudioTransportImpl depends on audio mixer and audio processing instances.
audio_transport_ = std::make_unique<AudioTransportImpl>(
audio_mixer_.get(), audio_processing_.get(), nullptr);
}
bool VoipCore::InitializeIfNeeded() {
// `audio_device_module_` internally owns a lock and the whole logic here
// needs to be executed atomically once using another lock in VoipCore.
// Further changes in this method will need to make sure that no deadlock is
// introduced in the future.
MutexLock lock(&lock_);
if (initialized_) {
return true;
}
// Initialize ADM.
if (audio_device_module_->Init() != 0) {
RTC_LOG(LS_ERROR) << "Failed to initialize the ADM.";
return false;
}
// Note that failures on initializing default recording/speaker devices are
// not considered to be fatal here. In certain case, caller may not care about
// recording device functioning (e.g webinar where only speaker is available).
// It's also possible that there are other audio devices available that may
// work.
// Initialize default speaker device.
if (audio_device_module_->SetPlayoutDevice(kAudioDeviceId) != 0) {
RTC_LOG(LS_WARNING) << "Unable to set playout device.";
}
if (audio_device_module_->InitSpeaker() != 0) {
RTC_LOG(LS_WARNING) << "Unable to access speaker.";
}
// Initialize default recording device.
if (audio_device_module_->SetRecordingDevice(kAudioDeviceId) != 0) {
RTC_LOG(LS_WARNING) << "Unable to set recording device.";
}
if (audio_device_module_->InitMicrophone() != 0) {
RTC_LOG(LS_WARNING) << "Unable to access microphone.";
}
// Set number of channels on speaker device.
bool available = false;
if (audio_device_module_->StereoPlayoutIsAvailable(&available) != 0) {
RTC_LOG(LS_WARNING) << "Unable to query stereo playout.";
}
if (audio_device_module_->SetStereoPlayout(available) != 0) {
RTC_LOG(LS_WARNING) << "Unable to set mono/stereo playout mode.";
}
// Set number of channels on recording device.
available = false;
if (audio_device_module_->StereoRecordingIsAvailable(&available) != 0) {
RTC_LOG(LS_WARNING) << "Unable to query stereo recording.";
}
if (audio_device_module_->SetStereoRecording(available) != 0) {
RTC_LOG(LS_WARNING) << "Unable to set stereo recording mode.";
}
if (audio_device_module_->RegisterAudioCallback(audio_transport_.get()) !=
0) {
RTC_LOG(LS_WARNING) << "Unable to register audio callback.";
}
initialized_ = true;
return true;
}
ChannelId VoipCore::CreateChannel(Transport* transport,
absl::optional<uint32_t> local_ssrc) {
ChannelId channel_id;
// Set local ssrc to random if not set by caller.
if (!local_ssrc) {
Random random(rtc::TimeMicros());
local_ssrc = random.Rand<uint32_t>();
}
rtc::scoped_refptr<AudioChannel> channel =
rtc::make_ref_counted<AudioChannel>(transport, local_ssrc.value(),
task_queue_factory_.get(),
audio_mixer_.get(), decoder_factory_);
{
MutexLock lock(&lock_);
channel_id = static_cast<ChannelId>(next_channel_id_);
channels_[channel_id] = channel;
next_channel_id_++;
if (next_channel_id_ >= kMaxChannelId) {
next_channel_id_ = 0;
}
}
// Set ChannelId in audio channel for logging/debugging purpose.
channel->SetId(channel_id);
return channel_id;
}
VoipResult VoipCore::ReleaseChannel(ChannelId channel_id) {
// Destroy channel outside of the lock.
rtc::scoped_refptr<AudioChannel> channel;
bool no_channels_after_release = false;
{
MutexLock lock(&lock_);
auto iter = channels_.find(channel_id);
if (iter != channels_.end()) {
channel = std::move(iter->second);
channels_.erase(iter);
}
no_channels_after_release = channels_.empty();
}
VoipResult status_code = VoipResult::kOk;
if (!channel) {
RTC_LOG(LS_WARNING) << "Channel " << channel_id << " not found";
status_code = VoipResult::kInvalidArgument;
}
if (no_channels_after_release) {
// TODO(bugs.webrtc.org/11581): unclear if we still need to clear `channel`
// here.
channel = nullptr;
// Make sure to stop playout on ADM if it is playing.
if (audio_device_module_->Playing()) {
if (audio_device_module_->StopPlayout() != 0) {
RTC_LOG(LS_WARNING) << "StopPlayout failed";
status_code = VoipResult::kInternal;
}
}
}
return status_code;
}
rtc::scoped_refptr<AudioChannel> VoipCore::GetChannel(ChannelId channel_id) {
rtc::scoped_refptr<AudioChannel> channel;
{
MutexLock lock(&lock_);
auto iter = channels_.find(channel_id);
if (iter != channels_.end()) {
channel = iter->second;
}
}
if (!channel) {
RTC_LOG(LS_ERROR) << "Channel " << channel_id << " not found";
}
return channel;
}
bool VoipCore::UpdateAudioTransportWithSenders() {
std::vector<AudioSender*> audio_senders;
// Gather a list of audio channel that are currently sending along with
// highest sampling rate and channel numbers to configure into audio
// transport.
int max_sampling_rate = 8000;
size_t max_num_channels = 1;
{
MutexLock lock(&lock_);
// Reserve to prevent run time vector re-allocation.
audio_senders.reserve(channels_.size());
for (auto kv : channels_) {
rtc::scoped_refptr<AudioChannel>& channel = kv.second;
if (channel->IsSendingMedia()) {
auto encoder_format = channel->GetEncoderFormat();
if (!encoder_format) {
RTC_LOG(LS_ERROR)
<< "channel " << channel->GetId() << " encoder is not set";
continue;
}
audio_senders.push_back(channel->GetAudioSender());
max_sampling_rate =
std::max(max_sampling_rate, encoder_format->clockrate_hz);
max_num_channels =
std::max(max_num_channels, encoder_format->num_channels);
}
}
}
audio_transport_->UpdateAudioSenders(audio_senders, max_sampling_rate,
max_num_channels);
// Depending on availability of senders, turn on or off ADM recording.
if (!audio_senders.empty()) {
// Initialize audio device module and default device if needed.
if (!InitializeIfNeeded()) {
return false;
}
if (!audio_device_module_->Recording()) {
if (audio_device_module_->InitRecording() != 0) {
RTC_LOG(LS_ERROR) << "InitRecording failed";
return false;
}
if (audio_device_module_->StartRecording() != 0) {
RTC_LOG(LS_ERROR) << "StartRecording failed";
return false;
}
}
} else {
if (audio_device_module_->Recording() &&
audio_device_module_->StopRecording() != 0) {
RTC_LOG(LS_ERROR) << "StopRecording failed";
return false;
}
}
return true;
}
VoipResult VoipCore::StartSend(ChannelId channel_id) {
rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id);
if (!channel) {
return VoipResult::kInvalidArgument;
}
if (!channel->StartSend()) {
return VoipResult::kFailedPrecondition;
}
return UpdateAudioTransportWithSenders() ? VoipResult::kOk
: VoipResult::kInternal;
}
VoipResult VoipCore::StopSend(ChannelId channel_id) {
rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id);
if (!channel) {
return VoipResult::kInvalidArgument;
}
channel->StopSend();
return UpdateAudioTransportWithSenders() ? VoipResult::kOk
: VoipResult::kInternal;
}
VoipResult VoipCore::StartPlayout(ChannelId channel_id) {
rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id);
if (!channel) {
return VoipResult::kInvalidArgument;
}
if (channel->IsPlaying()) {
return VoipResult::kOk;
}
if (!channel->StartPlay()) {
return VoipResult::kFailedPrecondition;
}
// Initialize audio device module and default device if needed.
if (!InitializeIfNeeded()) {
return VoipResult::kInternal;
}
if (!audio_device_module_->Playing()) {
if (audio_device_module_->InitPlayout() != 0) {
RTC_LOG(LS_ERROR) << "InitPlayout failed";
return VoipResult::kInternal;
}
if (audio_device_module_->StartPlayout() != 0) {
RTC_LOG(LS_ERROR) << "StartPlayout failed";
return VoipResult::kInternal;
}
}
return VoipResult::kOk;
}
VoipResult VoipCore::StopPlayout(ChannelId channel_id) {
rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id);
if (!channel) {
return VoipResult::kInvalidArgument;
}
channel->StopPlay();
return VoipResult::kOk;
}
VoipResult VoipCore::ReceivedRTPPacket(
ChannelId channel_id,
rtc::ArrayView<const uint8_t> rtp_packet) {
rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id);
if (!channel) {
return VoipResult::kInvalidArgument;
}
channel->ReceivedRTPPacket(rtp_packet);
return VoipResult::kOk;
}
VoipResult VoipCore::ReceivedRTCPPacket(
ChannelId channel_id,
rtc::ArrayView<const uint8_t> rtcp_packet) {
rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id);
if (!channel) {
return VoipResult::kInvalidArgument;
}
channel->ReceivedRTCPPacket(rtcp_packet);
return VoipResult::kOk;
}
VoipResult VoipCore::SetSendCodec(ChannelId channel_id,
int payload_type,
const SdpAudioFormat& encoder_format) {
rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id);
if (!channel) {
return VoipResult::kInvalidArgument;
}
auto encoder = encoder_factory_->MakeAudioEncoder(
payload_type, encoder_format, absl::nullopt);
channel->SetEncoder(payload_type, encoder_format, std::move(encoder));
return VoipResult::kOk;
}
VoipResult VoipCore::SetReceiveCodecs(
ChannelId channel_id,
const std::map<int, SdpAudioFormat>& decoder_specs) {
rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id);
if (!channel) {
return VoipResult::kInvalidArgument;
}
channel->SetReceiveCodecs(decoder_specs);
return VoipResult::kOk;
}
VoipResult VoipCore::RegisterTelephoneEventType(ChannelId channel_id,
int rtp_payload_type,
int sample_rate_hz) {
rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id);
if (!channel) {
return VoipResult::kInvalidArgument;
}
channel->RegisterTelephoneEventType(rtp_payload_type, sample_rate_hz);
return VoipResult::kOk;
}
VoipResult VoipCore::SendDtmfEvent(ChannelId channel_id,
DtmfEvent dtmf_event,
int duration_ms) {
rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id);
if (!channel) {
return VoipResult::kInvalidArgument;
}
return (channel->SendTelephoneEvent(static_cast<int>(dtmf_event), duration_ms)
? VoipResult::kOk
: VoipResult::kFailedPrecondition);
}
VoipResult VoipCore::GetIngressStatistics(ChannelId channel_id,
IngressStatistics& ingress_stats) {
rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id);
if (!channel) {
return VoipResult::kInvalidArgument;
}
ingress_stats = channel->GetIngressStatistics();
return VoipResult::kOk;
}
VoipResult VoipCore::GetChannelStatistics(ChannelId channel_id,
ChannelStatistics& channel_stats) {
rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id);
if (!channel) {
return VoipResult::kInvalidArgument;
}
channel_stats = channel->GetChannelStatistics();
return VoipResult::kOk;
}
VoipResult VoipCore::SetInputMuted(ChannelId channel_id, bool enable) {
rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id);
if (!channel) {
return VoipResult::kInvalidArgument;
}
channel->SetMute(enable);
return VoipResult::kOk;
}
VoipResult VoipCore::GetInputVolumeInfo(ChannelId channel_id,
VolumeInfo& input_volume) {
rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id);
if (!channel) {
return VoipResult::kInvalidArgument;
}
input_volume.audio_level = channel->GetInputAudioLevel();
input_volume.total_energy = channel->GetInputTotalEnergy();
input_volume.total_duration = channel->GetInputTotalDuration();
return VoipResult::kOk;
}
VoipResult VoipCore::GetOutputVolumeInfo(ChannelId channel_id,
VolumeInfo& output_volume) {
rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id);
if (!channel) {
return VoipResult::kInvalidArgument;
}
output_volume.audio_level = channel->GetOutputAudioLevel();
output_volume.total_energy = channel->GetOutputTotalEnergy();
output_volume.total_duration = channel->GetOutputTotalDuration();
return VoipResult::kOk;
}
} // namespace webrtc

View file

@ -0,0 +1,174 @@
/*
* Copyright (c) 2020 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.
*/
#ifndef AUDIO_VOIP_VOIP_CORE_H_
#define AUDIO_VOIP_VOIP_CORE_H_
#include <map>
#include <memory>
#include <queue>
#include <unordered_map>
#include <vector>
#include "api/audio_codecs/audio_decoder_factory.h"
#include "api/audio_codecs/audio_encoder_factory.h"
#include "api/scoped_refptr.h"
#include "api/task_queue/task_queue_factory.h"
#include "api/voip/voip_base.h"
#include "api/voip/voip_codec.h"
#include "api/voip/voip_dtmf.h"
#include "api/voip/voip_engine.h"
#include "api/voip/voip_network.h"
#include "api/voip/voip_statistics.h"
#include "api/voip/voip_volume_control.h"
#include "audio/audio_transport_impl.h"
#include "audio/voip/audio_channel.h"
#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_mixer/audio_mixer_impl.h"
#include "modules/audio_processing/include/audio_processing.h"
#include "rtc_base/synchronization/mutex.h"
namespace webrtc {
// VoipCore is the implementatino of VoIP APIs listed in api/voip directory.
// It manages a vector of AudioChannel objects where each is mapped with a
// ChannelId (int) type. ChannelId is the primary key to locate a specific
// AudioChannel object to operate requested VoIP API from the caller.
//
// This class receives required audio components from caller at construction and
// owns the life cycle of them to orchestrate the proper destruction sequence.
class VoipCore : public VoipEngine,
public VoipBase,
public VoipNetwork,
public VoipCodec,
public VoipDtmf,
public VoipStatistics,
public VoipVolumeControl {
public:
// Construct VoipCore with provided arguments.
VoipCore(rtc::scoped_refptr<AudioEncoderFactory> encoder_factory,
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory,
std::unique_ptr<TaskQueueFactory> task_queue_factory,
rtc::scoped_refptr<AudioDeviceModule> audio_device_module,
rtc::scoped_refptr<AudioProcessing> audio_processing);
~VoipCore() override = default;
// Implements VoipEngine interfaces.
VoipBase& Base() override { return *this; }
VoipNetwork& Network() override { return *this; }
VoipCodec& Codec() override { return *this; }
VoipDtmf& Dtmf() override { return *this; }
VoipStatistics& Statistics() override { return *this; }
VoipVolumeControl& VolumeControl() override { return *this; }
// Implements VoipBase interfaces.
ChannelId CreateChannel(Transport* transport,
absl::optional<uint32_t> local_ssrc) override;
VoipResult ReleaseChannel(ChannelId channel_id) override;
VoipResult StartSend(ChannelId channel_id) override;
VoipResult StopSend(ChannelId channel_id) override;
VoipResult StartPlayout(ChannelId channel_id) override;
VoipResult StopPlayout(ChannelId channel_id) override;
// Implements VoipNetwork interfaces.
VoipResult ReceivedRTPPacket(
ChannelId channel_id,
rtc::ArrayView<const uint8_t> rtp_packet) override;
VoipResult ReceivedRTCPPacket(
ChannelId channel_id,
rtc::ArrayView<const uint8_t> rtcp_packet) override;
// Implements VoipCodec interfaces.
VoipResult SetSendCodec(ChannelId channel_id,
int payload_type,
const SdpAudioFormat& encoder_format) override;
VoipResult SetReceiveCodecs(
ChannelId channel_id,
const std::map<int, SdpAudioFormat>& decoder_specs) override;
// Implements VoipDtmf interfaces.
VoipResult RegisterTelephoneEventType(ChannelId channel_id,
int rtp_payload_type,
int sample_rate_hz) override;
VoipResult SendDtmfEvent(ChannelId channel_id,
DtmfEvent dtmf_event,
int duration_ms) override;
// Implements VoipStatistics interfaces.
VoipResult GetIngressStatistics(ChannelId channel_id,
IngressStatistics& ingress_stats) override;
VoipResult GetChannelStatistics(ChannelId channe_id,
ChannelStatistics& channel_stats) override;
// Implements VoipVolumeControl interfaces.
VoipResult SetInputMuted(ChannelId channel_id, bool enable) override;
VoipResult GetInputVolumeInfo(ChannelId channel_id,
VolumeInfo& volume_info) override;
VoipResult GetOutputVolumeInfo(ChannelId channel_id,
VolumeInfo& volume_info) override;
private:
// Initialize ADM and default audio device if needed.
// Returns true if ADM is successfully initialized or already in such state
// (e.g called more than once). Returns false when ADM fails to initialize
// which would presumably render further processing useless. Note that such
// failure won't necessarily succeed in next initialization attempt as it
// would mean changing the ADM implementation. From Android N and onwards, the
// mobile app may not be able to gain microphone access when in background
// mode. Therefore it would be better to delay the logic as late as possible.
bool InitializeIfNeeded();
// Fetches the corresponding AudioChannel assigned with given `channel`.
// Returns nullptr if not found.
rtc::scoped_refptr<AudioChannel> GetChannel(ChannelId channel_id);
// Updates AudioTransportImpl with a new set of actively sending AudioSender
// (AudioEgress). This needs to be invoked whenever StartSend/StopSend is
// involved by caller. Returns false when the selected audio device fails to
// initialize where it can't expect to deliver any audio input sample.
bool UpdateAudioTransportWithSenders();
// Synchronization for these are handled internally.
rtc::scoped_refptr<AudioEncoderFactory> encoder_factory_;
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory_;
std::unique_ptr<TaskQueueFactory> task_queue_factory_;
// Synchronization is handled internally by AudioProcessing.
// Must be placed before `audio_device_module_` for proper destruction.
rtc::scoped_refptr<AudioProcessing> audio_processing_;
// Synchronization is handled internally by AudioMixer.
// Must be placed before `audio_device_module_` for proper destruction.
rtc::scoped_refptr<AudioMixer> audio_mixer_;
// Synchronization is handled internally by AudioTransportImpl.
// Must be placed before `audio_device_module_` for proper destruction.
std::unique_ptr<AudioTransportImpl> audio_transport_;
// Synchronization is handled internally by AudioDeviceModule.
rtc::scoped_refptr<AudioDeviceModule> audio_device_module_;
Mutex lock_;
// Member to track a next ChannelId for new AudioChannel.
int next_channel_id_ RTC_GUARDED_BY(lock_) = 0;
// Container to track currently active AudioChannel objects mapped by
// ChannelId.
std::unordered_map<ChannelId, rtc::scoped_refptr<AudioChannel>> channels_
RTC_GUARDED_BY(lock_);
// Boolean flag to ensure initialization only occurs once.
bool initialized_ RTC_GUARDED_BY(lock_) = false;
};
} // namespace webrtc
#endif // AUDIO_VOIP_VOIP_CORE_H_