/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "voe_codec_impl.h"

#include "audio_coding_module.h"
#include "channel.h"
#include "critical_section_wrapper.h"
#include "trace.h"
#include "voe_errors.h"
#include "voice_engine_impl.h"

namespace webrtc
{

VoECodec* VoECodec::GetInterface(VoiceEngine* voiceEngine)
{
#ifndef WEBRTC_VOICE_ENGINE_CODEC_API
    return NULL;
#else
    if (NULL == voiceEngine)
    {
        return NULL;
    }
    VoiceEngineImpl* s =
            reinterpret_cast<VoiceEngineImpl*> (voiceEngine);
    VoECodecImpl* d = s;
    (*d)++;
    return (d);
#endif
}

#ifdef WEBRTC_VOICE_ENGINE_CODEC_API

VoECodecImpl::VoECodecImpl()
{
    WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
                 "VoECodecImpl() - ctor");
}

VoECodecImpl::~VoECodecImpl()
{
    WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
                 "~VoECodecImpl() - dtor");
}

int VoECodecImpl::Release()
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "VoECodecImpl::Release()");
    (*this)--;
    int refCount = GetCount();
    if (refCount < 0)
    {
        Reset();
        _engineStatistics.SetLastError(VE_INTERFACE_NOT_FOUND,
                                       kTraceWarning);
        return (-1);
    }
    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
                 "VoECodecImpl reference counter = %d", refCount);
    return (refCount);
}

int VoECodecImpl::NumOfCodecs()
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "NumOfCodecs()");

    // Number of supported codecs in the ACM
    WebRtc_UWord8 nSupportedCodecs = AudioCodingModule::NumberOfCodecs();

    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
                 "NumOfCodecs() => %u", nSupportedCodecs);
    return (nSupportedCodecs);
}

int VoECodecImpl::GetCodec(int index, CodecInst& codec)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "GetCodec(index=%d, codec=?)", index);
    CodecInst acmCodec;
    if (AudioCodingModule::Codec(index, (CodecInst&) acmCodec)
            == -1)
    {
        _engineStatistics.SetLastError(VE_INVALID_LISTNR, kTraceError,
                                       "GetCodec() invalid index");
        return -1;
    }
    ACMToExternalCodecRepresentation(codec, acmCodec);
    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
                 "GetCodec() => plname=%s, pacsize=%d, plfreq=%d, pltype=%d, "
                 "channels=%d, rate=%d", codec.plname, codec.pacsize,
                   codec.plfreq, codec.pltype, codec.channels, codec.rate);
    return 0;
}

int VoECodecImpl::SetSendCodec(int channel, const CodecInst& codec)
{
    CodecInst copyCodec;
    ExternalToACMCodecRepresentation(copyCodec, codec);

    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "SetSendCodec(channel=%d, codec)", channel);
    WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
                 "codec: plname=%s, pacsize=%d, plfreq=%d, pltype=%d, "
                 "channels=%d, rate=%d", codec.plname, codec.pacsize,
                 codec.plfreq, codec.pltype, codec.channels, codec.rate);
    if (!_engineStatistics.Initialized())
    {
        _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    // External sanity checks performed outside the ACM
    if ((STR_CASE_CMP(copyCodec.plname, "L16") == 0) &&
            (copyCodec.pacsize >= 960))
    {
        _engineStatistics.SetLastError(VE_INVALID_ARGUMENT, kTraceError,
                                       "SetSendCodec() invalid L16 packet "
                                       "size");
        return -1;
    }
    if (!STR_CASE_CMP(copyCodec.plname, "CN")
            || !STR_CASE_CMP(copyCodec.plname, "TELEPHONE-EVENT")
            || !STR_CASE_CMP(copyCodec.plname, "RED"))
    {
        _engineStatistics.SetLastError(VE_INVALID_ARGUMENT, kTraceError,
                                       "SetSendCodec() invalid codec name");
        return -1;
    }
    if ((copyCodec.channels != 1) && (copyCodec.channels != 2))
    {
        _engineStatistics.SetLastError(VE_INVALID_ARGUMENT, kTraceError,
                                       "SetSendCodec() invalid number of "
                                       "channels");
        return -1;
    }
    voe::ScopedChannel sc(_channelManager, channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL)
    {
        _engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                                       "GetSendCodec() failed to locate "
                                       "channel");
        return -1;
    }
    if (!AudioCodingModule::IsCodecValid(
            (CodecInst&) copyCodec))
    {
        _engineStatistics.SetLastError(VE_INVALID_ARGUMENT, kTraceError,
                                       "SetSendCodec() invalid codec");
        return -1;
    }
    if (channelPtr->SetSendCodec(copyCodec) != 0)
    {
        _engineStatistics.SetLastError(VE_CANNOT_SET_SEND_CODEC,
                                       kTraceError,
                                       "SetSendCodec() failed to set send "
                                       "codec");
        return -1;
    }

    // Need to check if we should change APM settings for mono/stereo.
    // We'll check all channels (sending or not), so we don't have to
    // check this again when starting/stopping sending.

    voe::ScopedChannel sc2(_channelManager);
    void* iterator(NULL);
    channelPtr = sc2.GetFirstChannel(iterator);
    int maxNumChannels = 1;
    while (channelPtr != NULL)
    {   
        CodecInst tmpCdc;
        channelPtr->GetSendCodec(tmpCdc);
        if (tmpCdc.channels > maxNumChannels)
           maxNumChannels = tmpCdc.channels;
        
        channelPtr = sc2.GetNextChannel(iterator);
    }

    bool available(false);
    _audioDevicePtr->StereoRecordingIsAvailable(&available);
    int recordingChannels = available ? 2 : 1;

    if (_audioProcessingModulePtr->set_num_channels(recordingChannels, maxNumChannels) != 0)
    {
        _engineStatistics.SetLastError(VE_SOUNDCARD_ERROR, kTraceError,
            "Init() failed to set APM channels for the send audio stream");
        return -1;
    }


    return 0;
}

int VoECodecImpl::GetSendCodec(int channel, CodecInst& codec)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "GetSendCodec(channel=%d, codec=?)", channel);
    if (!_engineStatistics.Initialized())
    {
        _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    voe::ScopedChannel sc(_channelManager, channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL)
    {
        _engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                                       "GetSendCodec() failed to locate "
                                       "channel");
        return -1;
    }
    CodecInst acmCodec;
    if (channelPtr->GetSendCodec(acmCodec) != 0)
    {
        _engineStatistics.SetLastError(VE_CANNOT_GET_SEND_CODEC, kTraceError,
                                       "GetSendCodec() failed to get send "
                                       "codec");
        return -1;
    }
    ACMToExternalCodecRepresentation(codec, acmCodec);
    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
                 "GetSendCodec() => plname=%s, pacsize=%d, plfreq=%d, "
                 "channels=%d, rate=%d", codec.plname, codec.pacsize,
                 codec.plfreq, codec.channels, codec.rate);
    return 0;
}

int VoECodecImpl::GetRecCodec(int channel, CodecInst& codec)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "GetRecCodec(channel=%d, codec=?)", channel);
    if (!_engineStatistics.Initialized())
    {
        _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    voe::ScopedChannel sc(_channelManager, channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL)
    {
        _engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                                       "GetRecCodec() failed to locate "
                                       "channel");
        return -1;
    }
    CodecInst acmCodec;
    if (channelPtr->GetRecCodec(acmCodec) != 0)
    {
        _engineStatistics.SetLastError(VE_CANNOT_GET_REC_CODEC, kTraceError,
                                       "GetRecCodec() failed to get received "
                                       "codec");
        return -1;
    }
    ACMToExternalCodecRepresentation(codec, acmCodec);
    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
                 "GetRecCodec() => plname=%s, pacsize=%d, plfreq=%d, "
                 "channels=%d, rate=%d", codec.plname, codec.pacsize,
                 codec.plfreq, codec.channels, codec.rate);
    return 0;
}

int VoECodecImpl::SetAMREncFormat(int channel, AmrMode mode)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "SetAMREncFormat(channel=%d, mode=%d)", channel, mode);
#ifdef WEBRTC_CODEC_GSMAMR
    if (!_engineStatistics.Initialized())
    {
        _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    voe::ScopedChannel sc(_channelManager, channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL)
    {
        _engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                                       "SetAMREncFormat() failed to locate "
                                       "channel");
        return -1;
    }
    return channelPtr->SetAMREncFormat(mode);
#else
    _engineStatistics.SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
                                   "SetAMREncFormat() AMR codec is not "
                                   "supported");
    return -1;
#endif
}

int VoECodecImpl::SetAMRDecFormat(int channel, AmrMode mode)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "SetAMRDecFormat(channel=%i, mode=%i)", channel, mode);
#ifdef WEBRTC_CODEC_GSMAMR
    if (!_engineStatistics.Initialized())
    {
        _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    voe::ScopedChannel sc(_channelManager, channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL)
    {
        _engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                                       "SetAMRDecFormat() failed to locate "
                                       "channel");
        return -1;
    }
    return channelPtr->SetAMRDecFormat(mode);
#else
    _engineStatistics.SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
                                   "SetAMRDecFormat() AMR codec is not "
                                   "supported");
    return -1;
#endif
}

int VoECodecImpl::SetAMRWbEncFormat(int channel, AmrMode mode)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "SetAMRWbEncFormat(channel=%d, mode=%d)", channel, mode);
    ANDROID_NOT_SUPPORTED(); IPHONE_NOT_SUPPORTED();
#ifdef WEBRTC_CODEC_GSMAMRWB
    if (!_engineStatistics.Initialized())
    {
        _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    voe::ScopedChannel sc(_channelManager, channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL)
    {
        _engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                                       "SetAMRWbEncFormat() failed to locate "
                                       "channel");
        return -1;
    }
    return channelPtr->SetAMRWbEncFormat(mode);
#else
    _engineStatistics.SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
                                   "SetAMRWbEncFormat() AMR-wb codec is not "
                                   "supported");
    return -1;
#endif
}

int VoECodecImpl::SetAMRWbDecFormat(int channel, AmrMode mode)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "SetAMRWbDecFormat(channel=%i, mode=%i)", channel, mode);
    ANDROID_NOT_SUPPORTED(); IPHONE_NOT_SUPPORTED();
#ifdef WEBRTC_CODEC_GSMAMRWB
    if (!_engineStatistics.Initialized())
    {
        _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    voe::ScopedChannel sc(_channelManager, channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL)
    {
        _engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                                       "SetAMRWbDecFormat() failed to locate "
                                       "channel");
        return -1;
    }
    return channelPtr->SetAMRWbDecFormat(mode);
#else
    _engineStatistics.SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
                                   "SetAMRWbDecFormat() AMR-wb codec is not "
                                   "supported");
    return -1;
#endif
}

int VoECodecImpl::SetRecPayloadType(int channel, const CodecInst& codec)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "SetRecPayloadType(channel=%d, codec)", channel);
    WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
               "codec: plname=%s, plfreq=%d, pltype=%d, channels=%u, "
               "pacsize=%d, rate=%d", codec.plname, codec.plfreq, codec.pltype,
               codec.channels, codec.pacsize, codec.rate);
    if (!_engineStatistics.Initialized())
    {
        _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    voe::ScopedChannel sc(_channelManager, channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL)
    {
        _engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                                       "GetRecPayloadType() failed to locate "
                                       "channel");
        return -1;
    }
    return channelPtr->SetRecPayloadType(codec);
}

int VoECodecImpl::GetRecPayloadType(int channel, CodecInst& codec)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "GetRecPayloadType(channel=%d, codec)", channel);
    if (!_engineStatistics.Initialized())
    {
        _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    voe::ScopedChannel sc(_channelManager, channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL)
    {
        _engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                                       "GetRecPayloadType() failed to locate "
                                       "channel");
        return -1;
    }
    return channelPtr->GetRecPayloadType(codec);
}

int VoECodecImpl::SetSendCNPayloadType(int channel, int type,
                                       PayloadFrequencies frequency)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "SetSendCNPayloadType(channel=%d, type=%d, frequency=%d)",
                 channel, type, frequency);
    if (!_engineStatistics.Initialized())
    {
        _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    if (type < 96 || type > 127)
    {
        // Only allow dynamic range: 96 to 127
        _engineStatistics.SetLastError(VE_INVALID_PLTYPE, kTraceError,
                                       "SetSendCNPayloadType() invalid payload "
                                       "type");
        return -1;
    }
    if ((frequency != kFreq16000Hz) && (frequency != kFreq32000Hz))
    {
        // It is not possible to modify the payload type for CN/8000.
        // We only allow modification of the CN payload type for CN/16000
        // and CN/32000.
        _engineStatistics.SetLastError(VE_INVALID_PLFREQ, kTraceError,
                                       "SetSendCNPayloadType() invalid payload"
                                       " frequency");
        return -1;
    }
    voe::ScopedChannel sc(_channelManager, channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL)
    {
        _engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                                       "SetSendCNPayloadType() failed to "
                                       "locate channel");
        return -1;
    }
    if (channelPtr->Sending())
    {
        _engineStatistics.SetLastError(VE_SENDING, kTraceError,
                                       "SetSendCNPayloadType unable so set "
                                       "payload type while sending");
        return -1;
    }
    return channelPtr->SetSendCNPayloadType(type, frequency);
}

int VoECodecImpl::SetISACInitTargetRate(int channel, int rateBps,
                                        bool useFixedFrameSize)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "SetISACInitTargetRate(channel=%d, rateBps=%d, "
                 "useFixedFrameSize=%d)", channel, rateBps, useFixedFrameSize);
    ANDROID_NOT_SUPPORTED(); IPHONE_NOT_SUPPORTED();
#ifdef WEBRTC_CODEC_ISAC
    if (!_engineStatistics.Initialized())
    {
        _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    voe::ScopedChannel sc(_channelManager, channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL)
    {
        _engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                                       "SetISACInitTargetRate() failed to "
                                       "locate channel");
        return -1;
    }
    return channelPtr->SetISACInitTargetRate(rateBps, useFixedFrameSize);
#else
    _engineStatistics.SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
                                   "SetISACInitTargetRate() iSAC codec is not "
                                   "supported");
    return -1;
#endif
}

int VoECodecImpl::SetISACMaxRate(int channel, int rateBps)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "SetISACMaxRate(channel=%d, rateBps=%d)", channel, rateBps);
    ANDROID_NOT_SUPPORTED(); IPHONE_NOT_SUPPORTED();
#ifdef WEBRTC_CODEC_ISAC
    if (!_engineStatistics.Initialized())
    {
        _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    voe::ScopedChannel sc(_channelManager, channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL)
    {
        _engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                                       "SetISACMaxRate() failed to locate "
                                       "channel");
        return -1;
    }
    return channelPtr->SetISACMaxRate(rateBps);
#else
    _engineStatistics.SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
                                   "SetISACMaxRate() iSAC codec is not "
                                   "supported");
    return -1;
#endif
}

int VoECodecImpl::SetISACMaxPayloadSize(int channel, int sizeBytes)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "SetISACMaxPayloadSize(channel=%d, sizeBytes=%d)", channel,
                 sizeBytes);
    ANDROID_NOT_SUPPORTED(); IPHONE_NOT_SUPPORTED();
#ifdef WEBRTC_CODEC_ISAC
    if (!_engineStatistics.Initialized())
    {
        _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    voe::ScopedChannel sc(_channelManager, channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL)
    {
        _engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                                       "SetISACMaxPayloadSize() failed to "
                                       "locate channel");
        return -1;
    }
    return channelPtr->SetISACMaxPayloadSize(sizeBytes);
#else
    _engineStatistics.SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
                                   "SetISACMaxPayloadSize() iSAC codec is not "
                                   "supported");
    return -1;
#endif
    return 0;
}

int VoECodecImpl::SetVADStatus(int channel, bool enable, VadModes mode,
                               bool disableDTX)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "SetVADStatus(channel=%i, enable=%i, mode=%i, disableDTX=%i)",
                 channel, enable, mode, disableDTX);

    if (!_engineStatistics.Initialized())
    {
        _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    voe::ScopedChannel sc(_channelManager, channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL)
    {
        _engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                                       "SetVADStatus failed to locate channel");
        return -1;
    }

    ACMVADMode vadMode(VADNormal);
    switch (mode)
    {
        case kVadConventional:
            vadMode = VADNormal;
            break;
        case kVadAggressiveLow:
            vadMode = VADLowBitrate;
            break;
        case kVadAggressiveMid:
            vadMode = VADAggr;
            break;
        case kVadAggressiveHigh:
            vadMode = VADVeryAggr;
            break;
        default:
            _engineStatistics.SetLastError(VE_INVALID_ARGUMENT, kTraceError,
                                           "SetVADStatus() invalid VAD mode");
            return -1;
    }
    return channelPtr->SetVADStatus(enable, vadMode, disableDTX);
}

int VoECodecImpl::GetVADStatus(int channel, bool& enabled, VadModes& mode,
                               bool& disabledDTX)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
                 "GetVADStatus(channel=%i)", channel);

    if (!_engineStatistics.Initialized())
    {
        _engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    voe::ScopedChannel sc(_channelManager, channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL)
    {
        _engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                                       "GetVADStatus failed to locate channel");
        return -1;
    }

    ACMVADMode vadMode;
    int ret = channelPtr->GetVADStatus(enabled, vadMode, disabledDTX);

    if (ret != 0)
    {
        _engineStatistics.SetLastError(VE_INVALID_OPERATION, kTraceError,
                                       "GetVADStatus failed to get VAD mode");
        return -1;
    }
    switch (vadMode)
    {
        case VADNormal:
            mode = kVadConventional;
            break;
        case VADLowBitrate:
            mode = kVadAggressiveLow;
            break;
        case VADAggr:
            mode = kVadAggressiveMid;
            break;
        case VADVeryAggr:
            mode = kVadAggressiveHigh;
            break;
        default:
            _engineStatistics.SetLastError(VE_AUDIO_CODING_MODULE_ERROR,
                                           kTraceError,
                                           "GetVADStatus() invalid VAD mode");
            return -1;
    }

    return 0;
}

void VoECodecImpl::ACMToExternalCodecRepresentation(CodecInst& toInst,
                                                    const CodecInst& fromInst)
{
    toInst = fromInst;
    if (STR_CASE_CMP(fromInst.plname,"SILK") == 0)
    {
        if (fromInst.plfreq == 12000)
        {
            if (fromInst.pacsize == 320)
            {
                toInst.pacsize = 240;
            }
            else if (fromInst.pacsize == 640)
            {
                toInst.pacsize = 480;
            }
            else if (fromInst.pacsize == 960)
            {
                toInst.pacsize = 720;
            }
        }
        else if (fromInst.plfreq == 24000)
        {
            if (fromInst.pacsize == 640)
            {
                toInst.pacsize = 480;
            }
            else if (fromInst.pacsize == 1280)
            {
                toInst.pacsize = 960;
            }
            else if (fromInst.pacsize == 1920)
            {
                toInst.pacsize = 1440;
            }
        }
    }
}

void VoECodecImpl::ExternalToACMCodecRepresentation(CodecInst& toInst,
                                                    const CodecInst& fromInst)
{
    toInst = fromInst;
    if (STR_CASE_CMP(fromInst.plname,"SILK") == 0)
    {
        if (fromInst.plfreq == 12000)
        {
            if (fromInst.pacsize == 240)
            {
                toInst.pacsize = 320;
            }
            else if (fromInst.pacsize == 480)
            {
                toInst.pacsize = 640;
            }
            else if (fromInst.pacsize == 720)
            {
                toInst.pacsize = 960;
            }
        }
        else if (fromInst.plfreq == 24000)
        {
            if (fromInst.pacsize == 480)
            {
                toInst.pacsize = 640;
            }
            else if (fromInst.pacsize == 960)
            {
                toInst.pacsize = 1280;
            }
            else if (fromInst.pacsize == 1440)
            {
                toInst.pacsize = 1920;
            }
        }
    }
}

#endif  // WEBRTC_VOICE_ENGINE_CODEC_API

} // namespace webrtc
