You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
172 lines
5.8 KiB
172 lines
5.8 KiB
/*
|
|
* 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 "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(AudioCodingModule::Config())),
|
|
encoder_queue_(task_queue_factory->CreateTaskQueue(
|
|
"AudioEncoder",
|
|
TaskQueueFactory::Priority::NORMAL)) {
|
|
audio_coding_->RegisterTransportCallback(this);
|
|
}
|
|
|
|
AudioEgress::~AudioEgress() {
|
|
audio_coding_->RegisterTransportCallback(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));
|
|
}
|
|
|
|
void AudioEgress::StartSend() {
|
|
rtp_rtcp_->SetSendingMediaStatus(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_);
|
|
if (!rtp_rtcp_->SendingMedia()) {
|
|
return;
|
|
}
|
|
|
|
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_);
|
|
|
|
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(frame_type, payload_type, rtp_timestamp,
|
|
payload.data(), payload.size())) {
|
|
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_);
|
|
encoder_context_.mute_ = mute;
|
|
});
|
|
}
|
|
|
|
} // namespace webrtc
|