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
19namespace webrtc {
20namespace acm2 {
21
22namespace {
23
24// Check if the given codec is a valid to be registered as send codec.
25int 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
59bool 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
69CodecManager::CodecManager() {
70  thread_checker_.DetachFromThread();
71}
72
73CodecManager::~CodecManager() = default;
74
75bool 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
120CodecInst 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
136bool 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
153bool 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
182bool 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