1 /*
2 * Copyright (c) 2015 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 */
10
11 #include "webrtc/modules/audio_coding/acm2/codec_manager.h"
12
13 #include "webrtc/base/checks.h"
14 #include "webrtc/base/format_macros.h"
15 #include "webrtc/engine_configurations.h"
16 #include "webrtc/modules/audio_coding/acm2/rent_a_codec.h"
17 #include "webrtc/system_wrappers/include/trace.h"
18
19 namespace webrtc {
20 namespace acm2 {
21
22 namespace {
23
24 // Check if the given codec is a valid to be registered as send codec.
IsValidSendCodec(const CodecInst & send_codec)25 int IsValidSendCodec(const CodecInst& send_codec) {
26 int dummy_id = 0;
27 if ((send_codec.channels != 1) && (send_codec.channels != 2)) {
28 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
29 "Wrong number of channels (%" PRIuS ", only mono and stereo "
30 "are supported)",
31 send_codec.channels);
32 return -1;
33 }
34
35 auto maybe_codec_id = RentACodec::CodecIdByInst(send_codec);
36 if (!maybe_codec_id) {
37 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
38 "Invalid codec setting for the send codec.");
39 return -1;
40 }
41
42 // Telephone-event cannot be a send codec.
43 if (!STR_CASE_CMP(send_codec.plname, "telephone-event")) {
44 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
45 "telephone-event cannot be a send codec");
46 return -1;
47 }
48
49 if (!RentACodec::IsSupportedNumChannels(*maybe_codec_id, send_codec.channels)
50 .value_or(false)) {
51 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
52 "%" PRIuS " number of channels not supportedn for %s.",
53 send_codec.channels, send_codec.plname);
54 return -1;
55 }
56 return RentACodec::CodecIndexFromId(*maybe_codec_id).value_or(-1);
57 }
58
IsOpus(const CodecInst & codec)59 bool IsOpus(const CodecInst& codec) {
60 return
61 #ifdef WEBRTC_CODEC_OPUS
62 !STR_CASE_CMP(codec.plname, "opus") ||
63 #endif
64 false;
65 }
66
67 } // namespace
68
CodecManager()69 CodecManager::CodecManager() {
70 thread_checker_.DetachFromThread();
71 }
72
73 CodecManager::~CodecManager() = default;
74
RegisterEncoder(const CodecInst & send_codec)75 bool CodecManager::RegisterEncoder(const CodecInst& send_codec) {
76 RTC_DCHECK(thread_checker_.CalledOnValidThread());
77 int codec_id = IsValidSendCodec(send_codec);
78
79 // Check for reported errors from function IsValidSendCodec().
80 if (codec_id < 0) {
81 return false;
82 }
83
84 int dummy_id = 0;
85 switch (RentACodec::RegisterRedPayloadType(
86 &codec_stack_params_.red_payload_types, send_codec)) {
87 case RentACodec::RegistrationResult::kOk:
88 return true;
89 case RentACodec::RegistrationResult::kBadFreq:
90 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
91 "RegisterSendCodec() failed, invalid frequency for RED"
92 " registration");
93 return false;
94 case RentACodec::RegistrationResult::kSkip:
95 break;
96 }
97 switch (RentACodec::RegisterCngPayloadType(
98 &codec_stack_params_.cng_payload_types, send_codec)) {
99 case RentACodec::RegistrationResult::kOk:
100 return true;
101 case RentACodec::RegistrationResult::kBadFreq:
102 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
103 "RegisterSendCodec() failed, invalid frequency for CNG"
104 " registration");
105 return false;
106 case RentACodec::RegistrationResult::kSkip:
107 break;
108 }
109
110 if (IsOpus(send_codec)) {
111 // VAD/DTX not supported.
112 codec_stack_params_.use_cng = false;
113 }
114
115 send_codec_inst_ = rtc::Optional<CodecInst>(send_codec);
116 codec_stack_params_.speech_encoder = nullptr; // Caller must recreate it.
117 return true;
118 }
119
ForgeCodecInst(const AudioEncoder * external_speech_encoder)120 CodecInst CodecManager::ForgeCodecInst(
121 const AudioEncoder* external_speech_encoder) {
122 CodecInst ci;
123 ci.channels = external_speech_encoder->NumChannels();
124 ci.plfreq = external_speech_encoder->SampleRateHz();
125 ci.pacsize = rtc::CheckedDivExact(
126 static_cast<int>(external_speech_encoder->Max10MsFramesInAPacket() *
127 ci.plfreq),
128 100);
129 ci.pltype = -1; // Not valid.
130 ci.rate = -1; // Not valid.
131 static const char kName[] = "external";
132 memcpy(ci.plname, kName, sizeof(kName));
133 return ci;
134 }
135
SetCopyRed(bool enable)136 bool CodecManager::SetCopyRed(bool enable) {
137 if (enable && codec_stack_params_.use_codec_fec) {
138 WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, 0,
139 "Codec internal FEC and RED cannot be co-enabled.");
140 return false;
141 }
142 if (enable && send_codec_inst_ &&
143 codec_stack_params_.red_payload_types.count(send_codec_inst_->plfreq) <
144 1) {
145 WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, 0,
146 "Cannot enable RED at %i Hz.", send_codec_inst_->plfreq);
147 return false;
148 }
149 codec_stack_params_.use_red = enable;
150 return true;
151 }
152
SetVAD(bool enable,ACMVADMode mode)153 bool CodecManager::SetVAD(bool enable, ACMVADMode mode) {
154 // Sanity check of the mode.
155 RTC_DCHECK(mode == VADNormal || mode == VADLowBitrate || mode == VADAggr ||
156 mode == VADVeryAggr);
157
158 // Check that the send codec is mono. We don't support VAD/DTX for stereo
159 // sending.
160 const bool stereo_send =
161 codec_stack_params_.speech_encoder
162 ? (codec_stack_params_.speech_encoder->NumChannels() != 1)
163 : false;
164 if (enable && stereo_send) {
165 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0,
166 "VAD/DTX not supported for stereo sending");
167 return false;
168 }
169
170 // TODO(kwiberg): This doesn't protect Opus when injected as an external
171 // encoder.
172 if (send_codec_inst_ && IsOpus(*send_codec_inst_)) {
173 // VAD/DTX not supported, but don't fail.
174 enable = false;
175 }
176
177 codec_stack_params_.use_cng = enable;
178 codec_stack_params_.vad_mode = mode;
179 return true;
180 }
181
SetCodecFEC(bool enable_codec_fec)182 bool CodecManager::SetCodecFEC(bool enable_codec_fec) {
183 if (enable_codec_fec && codec_stack_params_.use_red) {
184 WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, 0,
185 "Codec internal FEC and RED cannot be co-enabled.");
186 return false;
187 }
188
189 codec_stack_params_.use_codec_fec = enable_codec_fec;
190 return true;
191 }
192
193 } // namespace acm2
194 } // namespace webrtc
195