Source code

Revision control

Copy as Markdown

Other Tools

/*
* 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(const Environment& env, RtpRtcpInterface* rtp_rtcp)
: rtp_rtcp_(rtp_rtcp),
rtp_sender_audio_(&env.clock(), rtp_rtcp_->RtpSender()),
audio_coding_(AudioCodingModule::Create()),
encoder_queue_(env.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