Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
5
TMessagesProj/jni/voip/webrtc/audio/OWNERS
Normal file
5
TMessagesProj/jni/voip/webrtc/audio/OWNERS
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
alessiob@webrtc.org
|
||||
gustaf@webrtc.org
|
||||
henrik.lundin@webrtc.org
|
||||
jakobi@webrtc.org
|
||||
peah@webrtc.org
|
||||
98
TMessagesProj/jni/voip/webrtc/audio/audio_level.cc
Normal file
98
TMessagesProj/jni/voip/webrtc/audio/audio_level.cc
Normal 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
|
||||
75
TMessagesProj/jni/voip/webrtc/audio/audio_level.h
Normal file
75
TMessagesProj/jni/voip/webrtc/audio/audio_level.h
Normal 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_
|
||||
475
TMessagesProj/jni/voip/webrtc/audio/audio_receive_stream.cc
Normal file
475
TMessagesProj/jni/voip/webrtc/audio/audio_receive_stream.cc
Normal 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
|
||||
173
TMessagesProj/jni/voip/webrtc/audio/audio_receive_stream.h
Normal file
173
TMessagesProj/jni/voip/webrtc/audio/audio_receive_stream.h
Normal 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_
|
||||
891
TMessagesProj/jni/voip/webrtc/audio/audio_send_stream.cc
Normal file
891
TMessagesProj/jni/voip/webrtc/audio/audio_send_stream.cc
Normal 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
|
||||
225
TMessagesProj/jni/voip/webrtc/audio/audio_send_stream.h
Normal file
225
TMessagesProj/jni/voip/webrtc/audio/audio_send_stream.h
Normal 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_
|
||||
212
TMessagesProj/jni/voip/webrtc/audio/audio_state.cc
Normal file
212
TMessagesProj/jni/voip/webrtc/audio/audio_state.cc
Normal 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
|
||||
92
TMessagesProj/jni/voip/webrtc/audio/audio_state.h
Normal file
92
TMessagesProj/jni/voip/webrtc/audio/audio_state.h
Normal 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_
|
||||
285
TMessagesProj/jni/voip/webrtc/audio/audio_transport_impl.cc
Normal file
285
TMessagesProj/jni/voip/webrtc/audio/audio_transport_impl.cc
Normal 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
|
||||
117
TMessagesProj/jni/voip/webrtc/audio/audio_transport_impl.h
Normal file
117
TMessagesProj/jni/voip/webrtc/audio/audio_transport_impl.h
Normal 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_
|
||||
1135
TMessagesProj/jni/voip/webrtc/audio/channel_receive.cc
Normal file
1135
TMessagesProj/jni/voip/webrtc/audio/channel_receive.cc
Normal file
File diff suppressed because it is too large
Load diff
194
TMessagesProj/jni/voip/webrtc/audio/channel_receive.h
Normal file
194
TMessagesProj/jni/voip/webrtc/audio/channel_receive.h
Normal 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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
908
TMessagesProj/jni/voip/webrtc/audio/channel_send.cc
Normal file
908
TMessagesProj/jni/voip/webrtc/audio/channel_send.cc
Normal 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
|
||||
135
TMessagesProj/jni/voip/webrtc/audio/channel_send.h
Normal file
135
TMessagesProj/jni/voip/webrtc/audio/channel_send.h
Normal 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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
30
TMessagesProj/jni/voip/webrtc/audio/conversion.h
Normal file
30
TMessagesProj/jni/voip/webrtc/audio/conversion.h
Normal 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_
|
||||
188
TMessagesProj/jni/voip/webrtc/audio/mock_voe_channel_proxy.h
Normal file
188
TMessagesProj/jni/voip/webrtc/audio/mock_voe_channel_proxy.h
Normal 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_
|
||||
91
TMessagesProj/jni/voip/webrtc/audio/remix_resample.cc
Normal file
91
TMessagesProj/jni/voip/webrtc/audio/remix_resample.cc
Normal 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
|
||||
44
TMessagesProj/jni/voip/webrtc/audio/remix_resample.h
Normal file
44
TMessagesProj/jni/voip/webrtc/audio/remix_resample.h
Normal 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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
99
TMessagesProj/jni/voip/webrtc/audio/utility/channel_mixer.cc
Normal file
99
TMessagesProj/jni/voip/webrtc/audio/utility/channel_mixer.cc
Normal 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
|
||||
86
TMessagesProj/jni/voip/webrtc/audio/utility/channel_mixer.h
Normal file
86
TMessagesProj/jni/voip/webrtc/audio/utility/channel_mixer.h
Normal 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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
173
TMessagesProj/jni/voip/webrtc/audio/voip/audio_channel.cc
Normal file
173
TMessagesProj/jni/voip/webrtc/audio/voip/audio_channel.cc
Normal 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
|
||||
131
TMessagesProj/jni/voip/webrtc/audio/voip/audio_channel.h
Normal file
131
TMessagesProj/jni/voip/webrtc/audio/voip/audio_channel.h
Normal 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_
|
||||
190
TMessagesProj/jni/voip/webrtc/audio/voip/audio_egress.cc
Normal file
190
TMessagesProj/jni/voip/webrtc/audio/voip/audio_egress.cc
Normal 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
|
||||
158
TMessagesProj/jni/voip/webrtc/audio/voip/audio_egress.h
Normal file
158
TMessagesProj/jni/voip/webrtc/audio/voip/audio_egress.h
Normal 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_
|
||||
290
TMessagesProj/jni/voip/webrtc/audio/voip/audio_ingress.cc
Normal file
290
TMessagesProj/jni/voip/webrtc/audio/voip/audio_ingress.cc
Normal 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
|
||||
145
TMessagesProj/jni/voip/webrtc/audio/voip/audio_ingress.h
Normal file
145
TMessagesProj/jni/voip/webrtc/audio/voip/audio_ingress.h
Normal 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_
|
||||
500
TMessagesProj/jni/voip/webrtc/audio/voip/voip_core.cc
Normal file
500
TMessagesProj/jni/voip/webrtc/audio/voip/voip_core.cc
Normal 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
|
||||
174
TMessagesProj/jni/voip/webrtc/audio/voip/voip_core.h
Normal file
174
TMessagesProj/jni/voip/webrtc/audio/voip/voip_core.h
Normal 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_
|
||||
Loading…
Add table
Add a link
Reference in a new issue