Tim Na | 11f92bc | 2020-04-21 16:39:25 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 10 | |
| 11 | #include "audio/voip/audio_egress.h" |
| 12 | |
| 13 | #include <utility> |
| 14 | #include <vector> |
| 15 | |
Danil Chapovalov | b1799b0 | 2024-01-18 10:57:48 | [diff] [blame] | 16 | #include "api/sequence_checker.h" |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 17 | #include "rtc_base/logging.h" |
| 18 | |
| 19 | namespace webrtc { |
| 20 | |
Danil Chapovalov | 943828b | 2024-08-02 09:31:59 | [diff] [blame] | 21 | AudioEgress::AudioEgress(const Environment& env, RtpRtcpInterface* rtp_rtcp) |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 22 | : rtp_rtcp_(rtp_rtcp), |
Danil Chapovalov | 943828b | 2024-08-02 09:31:59 | [diff] [blame] | 23 | rtp_sender_audio_(&env.clock(), rtp_rtcp_->RtpSender()), |
Henrik Lundin | 84f7569 | 2023-02-01 12:07:10 | [diff] [blame] | 24 | audio_coding_(AudioCodingModule::Create()), |
Danil Chapovalov | 943828b | 2024-08-02 09:31:59 | [diff] [blame] | 25 | encoder_queue_(env.task_queue_factory().CreateTaskQueue( |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 26 | "AudioEncoder", |
Danil Chapovalov | b1799b0 | 2024-01-18 10:57:48 | [diff] [blame] | 27 | TaskQueueFactory::Priority::NORMAL)), |
| 28 | encoder_queue_checker_(encoder_queue_.get()) { |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 29 | audio_coding_->RegisterTransportCallback(this); |
| 30 | } |
| 31 | |
| 32 | AudioEgress::~AudioEgress() { |
| 33 | audio_coding_->RegisterTransportCallback(nullptr); |
Danil Chapovalov | b1799b0 | 2024-01-18 10:57:48 | [diff] [blame] | 34 | |
| 35 | // Delete first to ensure that there are no running tasks when the other |
| 36 | // members are destroyed. |
| 37 | encoder_queue_ = nullptr; |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 38 | } |
| 39 | |
| 40 | bool AudioEgress::IsSending() const { |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 41 | return rtp_rtcp_->SendingMedia(); |
| 42 | } |
| 43 | |
| 44 | void AudioEgress::SetEncoder(int payload_type, |
| 45 | const SdpAudioFormat& encoder_format, |
| 46 | std::unique_ptr<AudioEncoder> encoder) { |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 47 | RTC_DCHECK_GE(payload_type, 0); |
| 48 | RTC_DCHECK_LE(payload_type, 127); |
| 49 | |
Tim Na | c0df5fc | 2020-05-05 18:03:54 | [diff] [blame] | 50 | SetEncoderFormat(encoder_format); |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 51 | |
| 52 | // The RTP/RTCP module needs to know the RTP timestamp rate (i.e. clockrate) |
| 53 | // as well as some other things, so we collect this info and send it along. |
| 54 | rtp_rtcp_->RegisterSendPayloadFrequency(payload_type, |
| 55 | encoder->RtpTimestampRateHz()); |
| 56 | rtp_sender_audio_.RegisterAudioPayload("audio", payload_type, |
| 57 | encoder->RtpTimestampRateHz(), |
| 58 | encoder->NumChannels(), 0); |
| 59 | |
| 60 | audio_coding_->SetEncoder(std::move(encoder)); |
| 61 | } |
| 62 | |
Jason Long | dba1f94 | 2020-08-06 19:16:04 | [diff] [blame] | 63 | bool AudioEgress::StartSend() { |
| 64 | if (!GetEncoderFormat()) { |
| 65 | RTC_DLOG(LS_WARNING) << "Send codec has not been set yet"; |
| 66 | return false; |
| 67 | } |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 68 | rtp_rtcp_->SetSendingMediaStatus(true); |
Jason Long | dba1f94 | 2020-08-06 19:16:04 | [diff] [blame] | 69 | return true; |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 70 | } |
| 71 | |
| 72 | void AudioEgress::StopSend() { |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 73 | rtp_rtcp_->SetSendingMediaStatus(false); |
| 74 | } |
| 75 | |
| 76 | void AudioEgress::SendAudioData(std::unique_ptr<AudioFrame> audio_frame) { |
| 77 | RTC_DCHECK_GT(audio_frame->samples_per_channel_, 0); |
| 78 | RTC_DCHECK_LE(audio_frame->num_channels_, 8); |
| 79 | |
Danil Chapovalov | b1799b0 | 2024-01-18 10:57:48 | [diff] [blame] | 80 | encoder_queue_->PostTask( |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 81 | [this, audio_frame = std::move(audio_frame)]() mutable { |
Danil Chapovalov | b1799b0 | 2024-01-18 10:57:48 | [diff] [blame] | 82 | RTC_DCHECK_RUN_ON(&encoder_queue_checker_); |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 83 | if (!rtp_rtcp_->SendingMedia()) { |
| 84 | return; |
| 85 | } |
| 86 | |
Tim Na | a58cae3 | 2020-11-13 19:07:43 | [diff] [blame] | 87 | double duration_seconds = |
| 88 | static_cast<double>(audio_frame->samples_per_channel_) / |
| 89 | audio_frame->sample_rate_hz_; |
| 90 | |
| 91 | input_audio_level_.ComputeLevel(*audio_frame, duration_seconds); |
| 92 | |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 93 | AudioFrameOperations::Mute(audio_frame.get(), |
| 94 | encoder_context_.previously_muted_, |
| 95 | encoder_context_.mute_); |
| 96 | encoder_context_.previously_muted_ = encoder_context_.mute_; |
| 97 | |
| 98 | audio_frame->timestamp_ = encoder_context_.frame_rtp_timestamp_; |
| 99 | |
| 100 | // This call will trigger AudioPacketizationCallback::SendData if |
| 101 | // encoding is done and payload is ready for packetization and |
| 102 | // transmission. Otherwise, it will return without invoking the |
| 103 | // callback. |
| 104 | if (audio_coding_->Add10MsData(*audio_frame) < 0) { |
| 105 | RTC_DLOG(LS_ERROR) << "ACM::Add10MsData() failed."; |
| 106 | return; |
| 107 | } |
| 108 | |
| 109 | encoder_context_.frame_rtp_timestamp_ += |
Evan Shrubsole | ef95b20 | 2025-02-24 14:55:04 | [diff] [blame] | 110 | dchecked_cast<uint32_t>(audio_frame->samples_per_channel_); |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 111 | }); |
| 112 | } |
| 113 | |
| 114 | int32_t AudioEgress::SendData(AudioFrameType frame_type, |
| 115 | uint8_t payload_type, |
| 116 | uint32_t timestamp, |
| 117 | const uint8_t* payload_data, |
| 118 | size_t payload_size) { |
Danil Chapovalov | b1799b0 | 2024-01-18 10:57:48 | [diff] [blame] | 119 | RTC_DCHECK_RUN_ON(&encoder_queue_checker_); |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 120 | |
Evan Shrubsole | 18287f6 | 2025-04-15 14:50:04 | [diff] [blame] | 121 | ArrayView<const uint8_t> payload(payload_data, payload_size); |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 122 | |
| 123 | // Currently we don't get a capture time from downstream modules (ADM, |
| 124 | // AudioTransportImpl). |
| 125 | // TODO(natim@webrtc.org): Integrate once it's ready. |
| 126 | constexpr uint32_t kUndefinedCaptureTime = -1; |
| 127 | |
| 128 | // Push data from ACM to RTP/RTCP-module to deliver audio frame for |
| 129 | // packetization. |
| 130 | if (!rtp_rtcp_->OnSendingRtpFrame(timestamp, kUndefinedCaptureTime, |
| 131 | payload_type, |
| 132 | /*force_sender_report=*/false)) { |
| 133 | return -1; |
| 134 | } |
| 135 | |
| 136 | const uint32_t rtp_timestamp = timestamp + rtp_rtcp_->StartTimestamp(); |
| 137 | |
| 138 | // This call will trigger Transport::SendPacket() from the RTP/RTCP module. |
Danil Chapovalov | 4c55621 | 2023-09-04 10:27:59 | [diff] [blame] | 139 | if (!rtp_sender_audio_.SendAudio({.type = frame_type, |
| 140 | .payload = payload, |
| 141 | .payload_id = payload_type, |
| 142 | .rtp_timestamp = rtp_timestamp})) { |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 143 | RTC_DLOG(LS_ERROR) |
| 144 | << "AudioEgress::SendData() failed to send data to RTP/RTCP module"; |
| 145 | return -1; |
| 146 | } |
| 147 | |
| 148 | return 0; |
| 149 | } |
| 150 | |
| 151 | void AudioEgress::RegisterTelephoneEventType(int rtp_payload_type, |
| 152 | int sample_rate_hz) { |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 153 | RTC_DCHECK_GE(rtp_payload_type, 0); |
| 154 | RTC_DCHECK_LE(rtp_payload_type, 127); |
| 155 | |
| 156 | rtp_rtcp_->RegisterSendPayloadFrequency(rtp_payload_type, sample_rate_hz); |
| 157 | rtp_sender_audio_.RegisterAudioPayload("telephone-event", rtp_payload_type, |
| 158 | sample_rate_hz, 0, 0); |
| 159 | } |
| 160 | |
| 161 | bool AudioEgress::SendTelephoneEvent(int dtmf_event, int duration_ms) { |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 162 | RTC_DCHECK_GE(dtmf_event, 0); |
| 163 | RTC_DCHECK_LE(dtmf_event, 255); |
| 164 | RTC_DCHECK_GE(duration_ms, 0); |
| 165 | RTC_DCHECK_LE(duration_ms, 65535); |
| 166 | |
| 167 | if (!IsSending()) { |
| 168 | return false; |
| 169 | } |
| 170 | |
| 171 | constexpr int kTelephoneEventAttenuationdB = 10; |
| 172 | |
| 173 | if (rtp_sender_audio_.SendTelephoneEvent(dtmf_event, duration_ms, |
| 174 | kTelephoneEventAttenuationdB) != 0) { |
| 175 | RTC_DLOG(LS_ERROR) << "SendTelephoneEvent() failed to send event"; |
| 176 | return false; |
| 177 | } |
| 178 | return true; |
| 179 | } |
| 180 | |
| 181 | void AudioEgress::SetMute(bool mute) { |
Danil Chapovalov | b1799b0 | 2024-01-18 10:57:48 | [diff] [blame] | 182 | encoder_queue_->PostTask([this, mute] { |
| 183 | RTC_DCHECK_RUN_ON(&encoder_queue_checker_); |
Tim Na | 8ab3c77 | 2020-03-27 00:16:51 | [diff] [blame] | 184 | encoder_context_.mute_ = mute; |
| 185 | }); |
| 186 | } |
| 187 | |
| 188 | } // namespace webrtc |