1/*
2 *  Copyright (c) 2012 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/voice_engine/voe_codec_impl.h"
12
13#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h"
14#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
15#include "webrtc/system_wrappers/interface/trace.h"
16#include "webrtc/voice_engine/channel.h"
17#include "webrtc/voice_engine/include/voe_errors.h"
18#include "webrtc/voice_engine/voice_engine_impl.h"
19
20namespace webrtc
21{
22
23VoECodec* VoECodec::GetInterface(VoiceEngine* voiceEngine)
24{
25#ifndef WEBRTC_VOICE_ENGINE_CODEC_API
26    return NULL;
27#else
28    if (NULL == voiceEngine)
29    {
30        return NULL;
31    }
32    VoiceEngineImpl* s = static_cast<VoiceEngineImpl*>(voiceEngine);
33    s->AddRef();
34    return s;
35#endif
36}
37
38#ifdef WEBRTC_VOICE_ENGINE_CODEC_API
39
40VoECodecImpl::VoECodecImpl(voe::SharedData* shared) : _shared(shared)
41{
42    WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
43                 "VoECodecImpl() - ctor");
44}
45
46VoECodecImpl::~VoECodecImpl()
47{
48    WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
49                 "~VoECodecImpl() - dtor");
50}
51
52int VoECodecImpl::NumOfCodecs()
53{
54    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
55                 "NumOfCodecs()");
56
57    // Number of supported codecs in the ACM
58    uint8_t nSupportedCodecs = AudioCodingModule::NumberOfCodecs();
59
60    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
61        VoEId(_shared->instance_id(), -1),
62        "NumOfCodecs() => %u", nSupportedCodecs);
63    return (nSupportedCodecs);
64}
65
66int VoECodecImpl::GetCodec(int index, CodecInst& codec)
67{
68    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
69                 "GetCodec(index=%d, codec=?)", index);
70    CodecInst acmCodec;
71    if (AudioCodingModule::Codec(index, &acmCodec)
72            == -1)
73    {
74        _shared->SetLastError(VE_INVALID_LISTNR, kTraceError,
75            "GetCodec() invalid index");
76        return -1;
77    }
78    ACMToExternalCodecRepresentation(codec, acmCodec);
79    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
80        VoEId(_shared->instance_id(), -1),
81        "GetCodec() => plname=%s, pacsize=%d, plfreq=%d, pltype=%d, "
82        "channels=%d, rate=%d", codec.plname, codec.pacsize,
83        codec.plfreq, codec.pltype, codec.channels, codec.rate);
84    return 0;
85}
86
87int VoECodecImpl::SetSendCodec(int channel, const CodecInst& codec)
88{
89    CodecInst copyCodec;
90    ExternalToACMCodecRepresentation(copyCodec, codec);
91
92    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
93                 "SetSendCodec(channel=%d, codec)", channel);
94    WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
95                 "codec: plname=%s, pacsize=%d, plfreq=%d, pltype=%d, "
96                 "channels=%d, rate=%d", codec.plname, codec.pacsize,
97                 codec.plfreq, codec.pltype, codec.channels, codec.rate);
98    if (!_shared->statistics().Initialized())
99    {
100        _shared->SetLastError(VE_NOT_INITED, kTraceError);
101        return -1;
102    }
103    // External sanity checks performed outside the ACM
104    if ((STR_CASE_CMP(copyCodec.plname, "L16") == 0) &&
105            (copyCodec.pacsize >= 960))
106    {
107        _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
108            "SetSendCodec() invalid L16 packet size");
109        return -1;
110    }
111    if (!STR_CASE_CMP(copyCodec.plname, "CN")
112            || !STR_CASE_CMP(copyCodec.plname, "TELEPHONE-EVENT")
113            || !STR_CASE_CMP(copyCodec.plname, "RED"))
114    {
115        _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
116            "SetSendCodec() invalid codec name");
117        return -1;
118    }
119    if ((copyCodec.channels != 1) && (copyCodec.channels != 2))
120    {
121        _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
122            "SetSendCodec() invalid number of channels");
123        return -1;
124    }
125    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
126    voe::Channel* channelPtr = ch.channel();
127    if (channelPtr == NULL)
128    {
129        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
130            "GetSendCodec() failed to locate channel");
131        return -1;
132    }
133    if (!AudioCodingModule::IsCodecValid(
134            (CodecInst&) copyCodec))
135    {
136        _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
137            "SetSendCodec() invalid codec");
138        return -1;
139    }
140    if (channelPtr->SetSendCodec(copyCodec) != 0)
141    {
142        _shared->SetLastError(VE_CANNOT_SET_SEND_CODEC, kTraceError,
143            "SetSendCodec() failed to set send codec");
144        return -1;
145    }
146
147    return 0;
148}
149
150int VoECodecImpl::GetSendCodec(int channel, CodecInst& codec)
151{
152    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
153                 "GetSendCodec(channel=%d, codec=?)", channel);
154    if (!_shared->statistics().Initialized())
155    {
156        _shared->SetLastError(VE_NOT_INITED, kTraceError);
157        return -1;
158    }
159    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
160    voe::Channel* channelPtr = ch.channel();
161    if (channelPtr == NULL)
162    {
163        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
164            "GetSendCodec() failed to locate channel");
165        return -1;
166    }
167    CodecInst acmCodec;
168    if (channelPtr->GetSendCodec(acmCodec) != 0)
169    {
170        _shared->SetLastError(VE_CANNOT_GET_SEND_CODEC, kTraceError,
171            "GetSendCodec() failed to get send codec");
172        return -1;
173    }
174    ACMToExternalCodecRepresentation(codec, acmCodec);
175    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
176        VoEId(_shared->instance_id(), -1),
177        "GetSendCodec() => plname=%s, pacsize=%d, plfreq=%d, "
178        "channels=%d, rate=%d", codec.plname, codec.pacsize,
179        codec.plfreq, codec.channels, codec.rate);
180    return 0;
181}
182
183int VoECodecImpl::GetRecCodec(int channel, CodecInst& codec)
184{
185    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
186                 "GetRecCodec(channel=%d, codec=?)", channel);
187    if (!_shared->statistics().Initialized())
188    {
189        _shared->SetLastError(VE_NOT_INITED, kTraceError);
190        return -1;
191    }
192    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
193    voe::Channel* channelPtr = ch.channel();
194    if (channelPtr == NULL)
195    {
196        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
197            "GetRecCodec() failed to locate channel");
198        return -1;
199    }
200    CodecInst acmCodec;
201    if (channelPtr->GetRecCodec(acmCodec) != 0)
202    {
203        _shared->SetLastError(VE_CANNOT_GET_REC_CODEC, kTraceError,
204            "GetRecCodec() failed to get received codec");
205        return -1;
206    }
207    ACMToExternalCodecRepresentation(codec, acmCodec);
208    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
209        VoEId(_shared->instance_id(), -1),
210        "GetRecCodec() => plname=%s, pacsize=%d, plfreq=%d, "
211        "channels=%d, rate=%d", codec.plname, codec.pacsize,
212        codec.plfreq, codec.channels, codec.rate);
213    return 0;
214}
215
216int VoECodecImpl::SetRecPayloadType(int channel, const CodecInst& codec)
217{
218    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
219                 "SetRecPayloadType(channel=%d, codec)", channel);
220    WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
221               "codec: plname=%s, plfreq=%d, pltype=%d, channels=%u, "
222               "pacsize=%d, rate=%d", codec.plname, codec.plfreq, codec.pltype,
223               codec.channels, codec.pacsize, codec.rate);
224    if (!_shared->statistics().Initialized())
225    {
226        _shared->SetLastError(VE_NOT_INITED, kTraceError);
227        return -1;
228    }
229    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
230    voe::Channel* channelPtr = ch.channel();
231    if (channelPtr == NULL)
232    {
233        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
234            "GetRecPayloadType() failed to locate channel");
235        return -1;
236    }
237    return channelPtr->SetRecPayloadType(codec);
238}
239
240int VoECodecImpl::GetRecPayloadType(int channel, CodecInst& codec)
241{
242    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
243                 "GetRecPayloadType(channel=%d, codec)", channel);
244    if (!_shared->statistics().Initialized())
245    {
246        _shared->SetLastError(VE_NOT_INITED, kTraceError);
247        return -1;
248    }
249    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
250    voe::Channel* channelPtr = ch.channel();
251    if (channelPtr == NULL)
252    {
253        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
254            "GetRecPayloadType() failed to locate channel");
255        return -1;
256    }
257    return channelPtr->GetRecPayloadType(codec);
258}
259
260int VoECodecImpl::SetSendCNPayloadType(int channel, int type,
261                                       PayloadFrequencies frequency)
262{
263    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
264                 "SetSendCNPayloadType(channel=%d, type=%d, frequency=%d)",
265                 channel, type, frequency);
266    if (!_shared->statistics().Initialized())
267    {
268        _shared->SetLastError(VE_NOT_INITED, kTraceError);
269        return -1;
270    }
271    if (type < 96 || type > 127)
272    {
273        // Only allow dynamic range: 96 to 127
274        _shared->SetLastError(VE_INVALID_PLTYPE, kTraceError,
275            "SetSendCNPayloadType() invalid payload type");
276        return -1;
277    }
278    if ((frequency != kFreq16000Hz) && (frequency != kFreq32000Hz))
279    {
280        // It is not possible to modify the payload type for CN/8000.
281        // We only allow modification of the CN payload type for CN/16000
282        // and CN/32000.
283        _shared->SetLastError(VE_INVALID_PLFREQ, kTraceError,
284            "SetSendCNPayloadType() invalid payload frequency");
285        return -1;
286    }
287    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
288    voe::Channel* channelPtr = ch.channel();
289    if (channelPtr == NULL)
290    {
291        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
292            "SetSendCNPayloadType() failed to locate channel");
293        return -1;
294    }
295    return channelPtr->SetSendCNPayloadType(type, frequency);
296}
297
298int VoECodecImpl::SetFECStatus(int channel, bool enable) {
299  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
300               "SetCodecFECStatus(channel=%d, enable=%d)", channel, enable);
301  if (!_shared->statistics().Initialized()) {
302    _shared->SetLastError(VE_NOT_INITED, kTraceError);
303    return -1;
304  }
305  voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
306  voe::Channel* channelPtr = ch.channel();
307  if (channelPtr == NULL) {
308    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
309                          "SetCodecFECStatus() failed to locate channel");
310    return -1;
311  }
312  return channelPtr->SetCodecFECStatus(enable);
313}
314
315int VoECodecImpl::GetFECStatus(int channel, bool& enabled) {
316  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
317               "GetCodecFECStatus(channel=%d)", channel);
318  if (!_shared->statistics().Initialized()) {
319    _shared->SetLastError(VE_NOT_INITED, kTraceError);
320    return -1;
321  }
322  voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
323  voe::Channel* channelPtr = ch.channel();
324  if (channelPtr == NULL) {
325    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
326                          "GetFECStatus() failed to locate channel");
327    return -1;
328  }
329  enabled = channelPtr->GetCodecFECStatus();
330  return 0;
331}
332
333int VoECodecImpl::SetVADStatus(int channel, bool enable, VadModes mode,
334                               bool disableDTX)
335{
336    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
337                 "SetVADStatus(channel=%i, enable=%i, mode=%i, disableDTX=%i)",
338                 channel, enable, mode, disableDTX);
339
340    if (!_shared->statistics().Initialized())
341    {
342        _shared->SetLastError(VE_NOT_INITED, kTraceError);
343        return -1;
344    }
345    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
346    voe::Channel* channelPtr = ch.channel();
347    if (channelPtr == NULL)
348    {
349        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
350            "SetVADStatus failed to locate channel");
351        return -1;
352    }
353
354    ACMVADMode vadMode(VADNormal);
355    switch (mode)
356    {
357        case kVadConventional:
358            vadMode = VADNormal;
359            break;
360        case kVadAggressiveLow:
361            vadMode = VADLowBitrate;
362            break;
363        case kVadAggressiveMid:
364            vadMode = VADAggr;
365            break;
366        case kVadAggressiveHigh:
367            vadMode = VADVeryAggr;
368            break;
369    }
370    return channelPtr->SetVADStatus(enable, vadMode, disableDTX);
371}
372
373int VoECodecImpl::GetVADStatus(int channel, bool& enabled, VadModes& mode,
374                               bool& disabledDTX)
375{
376    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
377                 "GetVADStatus(channel=%i)", channel);
378
379    if (!_shared->statistics().Initialized())
380    {
381        _shared->SetLastError(VE_NOT_INITED, kTraceError);
382        return -1;
383    }
384    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
385    voe::Channel* channelPtr = ch.channel();
386    if (channelPtr == NULL)
387    {
388        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
389            "GetVADStatus failed to locate channel");
390        return -1;
391    }
392
393    ACMVADMode vadMode;
394    int ret = channelPtr->GetVADStatus(enabled, vadMode, disabledDTX);
395
396    if (ret != 0)
397    {
398        _shared->SetLastError(VE_INVALID_OPERATION, kTraceError,
399            "GetVADStatus failed to get VAD mode");
400        return -1;
401    }
402    switch (vadMode)
403    {
404        case VADNormal:
405            mode = kVadConventional;
406            break;
407        case VADLowBitrate:
408            mode = kVadAggressiveLow;
409            break;
410        case VADAggr:
411            mode = kVadAggressiveMid;
412            break;
413        case VADVeryAggr:
414            mode = kVadAggressiveHigh;
415            break;
416    }
417
418    return 0;
419}
420
421void VoECodecImpl::ACMToExternalCodecRepresentation(CodecInst& toInst,
422                                                    const CodecInst& fromInst)
423{
424    toInst = fromInst;
425    if (STR_CASE_CMP(fromInst.plname,"SILK") == 0)
426    {
427        if (fromInst.plfreq == 12000)
428        {
429            if (fromInst.pacsize == 320)
430            {
431                toInst.pacsize = 240;
432            }
433            else if (fromInst.pacsize == 640)
434            {
435                toInst.pacsize = 480;
436            }
437            else if (fromInst.pacsize == 960)
438            {
439                toInst.pacsize = 720;
440            }
441        }
442        else if (fromInst.plfreq == 24000)
443        {
444            if (fromInst.pacsize == 640)
445            {
446                toInst.pacsize = 480;
447            }
448            else if (fromInst.pacsize == 1280)
449            {
450                toInst.pacsize = 960;
451            }
452            else if (fromInst.pacsize == 1920)
453            {
454                toInst.pacsize = 1440;
455            }
456        }
457    }
458}
459
460void VoECodecImpl::ExternalToACMCodecRepresentation(CodecInst& toInst,
461                                                    const CodecInst& fromInst)
462{
463    toInst = fromInst;
464    if (STR_CASE_CMP(fromInst.plname,"SILK") == 0)
465    {
466        if (fromInst.plfreq == 12000)
467        {
468            if (fromInst.pacsize == 240)
469            {
470                toInst.pacsize = 320;
471            }
472            else if (fromInst.pacsize == 480)
473            {
474                toInst.pacsize = 640;
475            }
476            else if (fromInst.pacsize == 720)
477            {
478                toInst.pacsize = 960;
479            }
480        }
481        else if (fromInst.plfreq == 24000)
482        {
483            if (fromInst.pacsize == 480)
484            {
485                toInst.pacsize = 640;
486            }
487            else if (fromInst.pacsize == 960)
488            {
489                toInst.pacsize = 1280;
490            }
491            else if (fromInst.pacsize == 1440)
492            {
493                toInst.pacsize = 1920;
494            }
495        }
496    }
497}
498
499int VoECodecImpl::SetSecondarySendCodec(int channel, const CodecInst& codec,
500                                        int red_payload_type) {
501  CodecInst copy_codec;
502  ExternalToACMCodecRepresentation(copy_codec, codec);
503
504  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
505               "SetSecondarySendCodec(channel=%d, codec)", channel);
506  WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
507               "codec: plname=%s, pacsize=%d, plfreq=%d, pltype=%d, "
508               "channels=%d, rate=%d", codec.plname, codec.pacsize,
509               codec.plfreq, codec.pltype, codec.channels, codec.rate);
510  if (!_shared->statistics().Initialized()) {
511    _shared->SetLastError(VE_NOT_INITED, kTraceError);
512    return -1;
513  }
514
515  // External sanity checks performed outside the ACM
516  if ((STR_CASE_CMP(copy_codec.plname, "L16") == 0) &&
517      (copy_codec.pacsize >= 960)) {
518    _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
519                          "SetSecondarySendCodec() invalid L16 packet size");
520    return -1;
521  }
522
523  // None of the following codecs can be registered as the secondary encoder.
524  if (!STR_CASE_CMP(copy_codec.plname, "CN") ||
525      !STR_CASE_CMP(copy_codec.plname, "TELEPHONE-EVENT") ||
526      !STR_CASE_CMP(copy_codec.plname, "RED"))  {
527    _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
528                          "SetSecondarySendCodec() invalid codec name");
529    return -1;
530  }
531
532  // Only mono and stereo are supported.
533  if ((copy_codec.channels != 1) && (copy_codec.channels != 2)) {
534    _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
535                          "SetSecondarySendCodec() invalid number of channels");
536    return -1;
537  }
538  voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
539  voe::Channel* channelPtr = ch.channel();
540  if (channelPtr == NULL) {
541    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
542                          "SetSecondarySendCodec() failed to locate channel");
543    return -1;
544  }
545  if (!AudioCodingModule::IsCodecValid(copy_codec)) {
546    _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
547                          "SetSecondarySendCodec() invalid codec");
548    return -1;
549  }
550  if (channelPtr->SetSecondarySendCodec(copy_codec, red_payload_type) != 0) {
551    _shared->SetLastError(VE_CANNOT_SET_SECONDARY_SEND_CODEC, kTraceError,
552                          "SetSecondarySendCodec() failed to set secondary "
553                          "send codec");
554    return -1;
555  }
556  return 0;
557}
558
559int VoECodecImpl::GetSecondarySendCodec(int channel, CodecInst& codec) {
560  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
561               "GetSecondarySendCodec(channel=%d, codec=?)", channel);
562  if (!_shared->statistics().Initialized()) {
563    _shared->SetLastError(VE_NOT_INITED, kTraceError);
564    return -1;
565  }
566  voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
567  voe::Channel* channelPtr = ch.channel();
568  if (channelPtr == NULL) {
569    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
570                          "GetSecondarySendCodec() failed to locate channel");
571    return -1;
572  }
573  CodecInst acm_codec;
574  if (channelPtr->GetSecondarySendCodec(&acm_codec) != 0) {
575    _shared->SetLastError(VE_CANNOT_GET_SECONDARY_SEND_CODEC, kTraceError,
576                          "GetSecondarySendCodec() failed to get secondary "
577                          "send codec");
578    return -1;
579  }
580  ACMToExternalCodecRepresentation(codec, acm_codec);
581  WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
582               VoEId(_shared->instance_id(), -1),
583               "GetSecondarySendCodec() => plname=%s, pacsize=%d, plfreq=%d, "
584               "channels=%d, rate=%d", codec.plname, codec.pacsize,
585               codec.plfreq, codec.channels, codec.rate);
586  return 0;
587}
588
589int VoECodecImpl::RemoveSecondarySendCodec(int channel) {
590  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
591               "RemoveSecondarySendCodec(channel=%d)", channel);
592  voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
593  voe::Channel* channelPtr = ch.channel();
594  if (channelPtr == NULL) {
595    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
596                          "RemoveSecondarySendCodec() failed to locate "
597                          "channel");
598    return -1;
599  }
600  channelPtr->RemoveSecondarySendCodec();
601  return 0;
602}
603
604#endif  // WEBRTC_VOICE_ENGINE_CODEC_API
605
606}  // namespace webrtc
607