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