/*---------------------------------------------------------------------------*/
/* audio_io_channel.cpp                                                      */
/* copyright (c) innovaphone 2017                                            */
/*---------------------------------------------------------------------------*/

#include <platform.h>

#include "common/audio/soft_dsp/compand.h"
#include "common/audio/soft_dsp/sdsputil.h"
#include "common/audio/soft_dsp/g711plc.h"
#include "common/audio/soft_dsp/resample.h"
#include "common/audio/soft_dsp/g722.h"
#include "common/audio/g729ab/g729ab.h"
#include "common/audio/opus/include/opus.h"
#include "common/interface/environment.h"
#include "common/interface/file.h"
#include "common/interface/socket.h"
#include "common/interface/media.h"
#include "jitter_buffer.h"
#include "audio_io_channel.h"

#define R_RTP_FRAG_ms                   10

#define SAMPLE_RATE_ID_8000             0
#define SAMPLE_RATE_ID_16000            1
#define SAMPLE_RATE_COUNT               2

static unsigned int sample_rate_table[SAMPLE_RATE_COUNT] =
{
    8000,
    16000,
};

static word resample_mode_table[] =
{
    0,
    0,
    RESAMPLE_MODE_2,
    RESAMPLE_MODE_3,
    RESAMPLE_MODE_4,
    0,
    RESAMPLE_MODE_6
};

#ifdef _WINDOWS
static int cntRx = 0;
#endif

/*---------------------------------------------------------------------------*/

class IAudioIoChannel * IAudioIoChannel::Create(class IAudioIo * audioIo, class UMediaIoChannel * const user)
{
    return new class AudioIoChannel(audioIo, user);
}

/*---------------------------------------------------------------------------*/
/* AudioIoChannel                                                            */
/*---------------------------------------------------------------------------*/

AudioIoChannel::AudioIoChannel(class IAudioIo * audioIo, class UMediaIoChannel * const user)
{
    this->audioIo = audioIo;
    this->user = user;
    this->jitter_buffer = new class jitter_buffer(this);
    channel_opened = false;
    channel_attached = false;
    coder = AudioCoder::CODER_UNDEFINED;
    rtpStream = NULL;
    recordRtpStream = false;
}

AudioIoChannel::~AudioIoChannel()
{
    Close();
    if (audioIo) {
        audioIo->ChannelDestroyed(this);
    }
    if (rtpStream) {
        rtpStream->Close();
        delete rtpStream;
    }
    delete jitter_buffer;
}

void AudioIoChannel::update_dsp()
{
    int err;

    send_timestamp = 0;
    send_sequence = 0;
    send_data_pos = 0;
    send_sample_pos = 0;
    send_linear_pos = 0;

    recv_lost_burst = 0;
    recv_lost_packets = 0;
    recv_pull_pos = 0;
    recv_pull_end = 0;
    send_pull_pos = 0;
    send_pull_end = 0;
    g711plc_init(&g711plc);

    jitter_buffer_descriptor_time = jitter_buffer->Configure(coder,
        payload_ms, execInterval8khz,
        (0 / 160) * 20, (execJitter8khz / 160) * 20, (recv_stuffing_limit / 160) * 20);

    if(sample_rate_table[playback_sample_rate_id] > sample_rate_table[payload_sample_rate_id]) {
        resample_up_init(&decoder_resampler, resample_mode_table[
            sample_rate_table[playback_sample_rate_id] / sample_rate_table[payload_sample_rate_id]]);
    }
    switch(coder) {
    case AudioCoder::G722:
        g722_decoder_init(&decoder.g722);
        break;
    case AudioCoder::G729:
    case AudioCoder::G729A:
    case AudioCoder::G729B:
    case AudioCoder::G729AB:
        g729ab_init_decoder((g729ab_decoder *)(decoder.g729ab));
        break;
    case AudioCoder::OPUS_NB:
    case AudioCoder::OPUS_WB:
        err = opus_decoder_init((OpusDecoder *)(decoder.opus), sample_rate_table[payload_sample_rate_id], 1);
        if(err < 0) {
            debug->printf("Opus decoder initialization failed %s", opus_strerror(err));
        }
        break;
    default:
        break;
    }
    decode_resid_pos = 0;
    decode_resid_size = 0;
    if(sample_rate_table[record_sample_rate_id] > sample_rate_table[payload_sample_rate_id]) {
        resample_up_init(&feedback_resampler, resample_mode_table[
            sample_rate_table[record_sample_rate_id] / sample_rate_table[payload_sample_rate_id]]);
        resample_down_init(&encoder_resampler, resample_mode_table[
            sample_rate_table[record_sample_rate_id] / sample_rate_table[payload_sample_rate_id]]);
    }
    switch(coder) {
    case AudioCoder::G722:
        g722_encoder_init(&encoder.g722);
        break;
    case AudioCoder::G729:
    case AudioCoder::G729A:
    case AudioCoder::G729B:
    case AudioCoder::G729AB:
        g729ab_init_encoder((g729ab_encoder *)(encoder.g729ab), sc);
        break;
    case AudioCoder::OPUS_NB:
    case AudioCoder::OPUS_WB:
        err = opus_encoder_init((OpusEncoder *)(encoder.opus), sample_rate_table[payload_sample_rate_id], 1, OPUS_APPLICATION_VOIP);
        if(err < 0) {
            debug->printf("Opus encoder initialization failed %s", opus_strerror(err));
        }
        err = opus_encoder_ctl((OpusEncoder *)(encoder.opus), OPUS_SET_COMPLEXITY(DEFAULT_OPUS_COMPLEXITY));
        if(err < 0) {
            debug->printf("Opus encoder set complexity failed %s", opus_strerror(err));
        }
        break;
    default:
        break;
    }
}

void AudioIoChannel::Initialize(enum AudioCoder coder, bool sc, unsigned mediaPacketizationMs, unsigned execInterval8khz,
    unsigned execJitter8khz, unsigned pullSampleRate, unsigned putSampleRate, bool recordRtpStream)
{
    this->coder = coder;
    this->sc = sc;
    this->execInterval8khz = execInterval8khz;
    this->execJitter8khz = execJitter8khz;
    record_sample_rate_id = 0;
    while (sample_rate_table[record_sample_rate_id] != putSampleRate) {
        record_sample_rate_id++;
    }
    playback_sample_rate_id = 0;
    while (sample_rate_table[playback_sample_rate_id] != pullSampleRate) {
        playback_sample_rate_id++;
    }

    recv_stuffing_limit = (2 * execJitter8khz) + 160;
    word min_count = 20;			//	min frame in milliseconds
    word max_count = 40;	  		//	max frame 320byte=40 ms on AC49x
    word nob_count = R_RTP_FRAG_ms;	//	to compute NOB
    payload_ms = R_RTP_FRAG_ms;
    payload_sample_rate_id = SAMPLE_RATE_ID_8000;
    timestamp_rate_multiplier = 1;
    switch(coder) {
    case AudioCoder::G723_53:
    case AudioCoder::G723_63:
	    min_count = 30; max_count = 300; nob_count = 30;
	    break;
    case AudioCoder::G729:
    case AudioCoder::G729A:
    case AudioCoder::G729B:
    case AudioCoder::G729AB:
	    max_count= 320;
	    break;
    case AudioCoder::G722:
        payload_sample_rate_id = SAMPLE_RATE_ID_16000;
		max_count = 80;
	    break;
    case AudioCoder::G726_40:
    case AudioCoder::G726_32:
    case AudioCoder::G726_24:
    case AudioCoder::G726_16:
	    break;
    case AudioCoder::OPUS_NB:
    case AudioCoder::OPUS_WB:
        payload_sample_rate_id = (coder == AudioCoder::OPUS_WB) ? SAMPLE_RATE_ID_16000 : SAMPLE_RATE_ID_8000;
        timestamp_rate_multiplier = 6;
        max_count = 60; nob_count = 20;
	    break;
    default:
		max_count = 80;
	    break;
    }

    unsigned count = mediaPacketizationMs;
    if(count < min_count) count = min_count;
    if(count > max_count) count = max_count;
    unsigned blocks = count / nob_count;

    payload_ms = blocks * nob_count;

    if (channel_attached) {
        update_dsp();
    }

#ifdef _WINDOWS
    if (recordRtpStream && !rtpStream) {
        char buf[2048];
        char* envPathLocalAppData = Environment::GetEnv("LOCALAPPDATA");
        if (_PRODUCT_NAME_ && !strcmp(_PRODUCT_NAME_, myappsPluginForWindows)) sprintf(buf, "%s\\innovaphone\\myappsPlugin\\RTP-JB-Wave-%d", envPathLocalAppData, cntRx);
        else sprintf(buf, "%s\\innovaphone\\myapps\\RTP-JB-Wave-%d", envPathLocalAppData, cntRx);
        cntRx = cntRx >= 5 ? 0 : cntRx + 1;
        rtpStream = FileSystem::OpenWriteFile(buf, false, NULL);
        free(envPathLocalAppData);
    }
#endif
}

void AudioIoChannel::decode_frame(enum AudioCoder payload, unsigned length, const byte *data, short *buffer, short *feedback_buffer)
{
    short *decode_buffer;
    short s;
    byte tail;
    int i, decode_samples;

    decode_samples = jitter_buffer_descriptor_time * (sample_rate_table[payload_sample_rate_id] / 8000);
    if(sample_rate_table[playback_sample_rate_id] > sample_rate_table[payload_sample_rate_id]) {
        decode_buffer = &(buffer[jitter_buffer_descriptor_time * ((sample_rate_table[playback_sample_rate_id] - sample_rate_table[payload_sample_rate_id]) / 8000)]);
    }
    else {
        decode_buffer = buffer;
    }
    if((length != 0) && data) {
        if(recv_lost_burst != 0) {
            if(recv_lost_burst <= recv_stuffing_limit / jitter_buffer_descriptor_time) {
                recv_lost_packets += recv_lost_burst;
            }
            if((coder != AudioCoder::G729)
             &&(coder != AudioCoder::G729A)
             &&(coder != AudioCoder::G729B)
             &&(coder != AudioCoder::G729AB)) {
                debug->printf("RTP lost or late %u ms all %u ms", recv_lost_burst * (jitter_buffer_descriptor_time / 8), recv_lost_packets * (jitter_buffer_descriptor_time / 8));
            }
            recv_lost_burst = 0;
        }
        switch(payload) {
        case AudioCoder::G711_U:
            for(i = 0; i < ((int) jitter_buffer_descriptor_time); i++) {
                s = norm_expand_ulaw(data[i]) >> 1;
                decode_buffer[i] = g711plc_put_get_sample(&g711plc, s, s, &s, &tail);
            }
            break;
        default:
        case AudioCoder::G711_A:
            for(i = 0; i < ((int) jitter_buffer_descriptor_time); i++) {
                s = norm_expand_alaw(data[i]) >> 1;
                decode_buffer[i] = g711plc_put_get_sample(&g711plc, s, s, &s, &tail);
            }
            break;
        case AudioCoder::G722:
            for(i = 0; i < ((int) jitter_buffer_descriptor_time); i++) {
                g722_decoder_exec(&(decoder.g722), G722_DECODER_MODE_64KBITS, data[i], &(decode_buffer[i*2]));
                decode_buffer[i*2] = g711plc_put_get_sample(&g711plc, decode_buffer[i*2], decode_buffer[i*2+1], &(decode_buffer[i*2+1]), &tail);
            }
            break;
        case AudioCoder::G729:
        case AudioCoder::G729A:
        case AudioCoder::G729B:
        case AudioCoder::G729AB: {
            short codebits[82];
            codebits[0] = 0x6b21;
            codebits[1] = length << 3;
            for (i = 0; i < ((int) length); i++) {
                byte b = data[i];
                codebits[(i << 3) + 2 + 0] = (b & 0x80) ? 0x0081 : 0x007f;
                codebits[(i << 3) + 2 + 1] = (b & 0x40) ? 0x0081 : 0x007f;
                codebits[(i << 3) + 2 + 2] = (b & 0x20) ? 0x0081 : 0x007f;
                codebits[(i << 3) + 2 + 3] = (b & 0x10) ? 0x0081 : 0x007f;
                codebits[(i << 3) + 2 + 4] = (b & 0x08) ? 0x0081 : 0x007f;
                codebits[(i << 3) + 2 + 5] = (b & 0x04) ? 0x0081 : 0x007f;
                codebits[(i << 3) + 2 + 6] = (b & 0x02) ? 0x0081 : 0x007f;
                codebits[(i << 3) + 2 + 7] = (b & 0x01) ? 0x0081 : 0x007f;
            }
            g729ab_decode((g729ab_decoder *)(decoder.g729ab), codebits, decode_buffer);
            for(i = 0; i < 80; i++) {
                decode_buffer[i] = (decode_buffer[i] == 0x7fff) ? 0x3fff : (decode_buffer[i] + 1) >> 1;
            }
            break;
            }
        case AudioCoder::OPUS_NB:
        case AudioCoder::OPUS_WB:
            decode_resid_size = opus_decode((OpusDecoder *)(decoder.opus), data, length, decode_resid_buffer, sizeof(decode_resid_buffer) / sizeof(decode_resid_buffer[0]), 0);
            if(decode_resid_size < 0) {
                debug->printf("Opus decode error %s", opus_strerror(decode_resid_size));
            }
            for(i = 0; i < decode_resid_size; i++) {
                decode_resid_buffer[i] = (decode_resid_buffer[i] == 0x7fff) ? 0x3fff : (decode_resid_buffer[i] + 1) >> 1;
            }
            memcpy(decode_buffer, decode_resid_buffer, decode_samples * sizeof(short));
            decode_resid_pos = decode_samples;
            break;
        }
    }
    else if(decode_resid_pos < decode_resid_size) {
        memcpy(decode_buffer, &decode_resid_buffer[decode_resid_pos], decode_samples * sizeof(short));
        decode_resid_pos += decode_samples;
    }
    else {
        recv_lost_burst++;
        switch(payload) {
        case AudioCoder::G729:
        case AudioCoder::G729A:
        case AudioCoder::G729B:
        case AudioCoder::G729AB: {
            short codebits[82];
            codebits[0] = 0x6b21;
            codebits[1] = 80;
            for (i = 0; i < 80; i++) {
                codebits[2 + i] = 0;
            }
            g729ab_decode((g729ab_decoder *)(decoder.g729ab), codebits, decode_buffer);
            for(i = 0; i < 80; i++) {
                decode_buffer[i] = (decode_buffer[i] == 0x7fff) ? 0x3fff : (decode_buffer[i] + 1) >> 1;
            }
            break;
            }
        case AudioCoder::OPUS_NB:
        case AudioCoder::OPUS_WB:
            if (decode_resid_size <= 0) {
                decode_resid_size = decode_samples;
            }
            i = opus_decode((OpusDecoder *)(decoder.opus), 0, 0, decode_resid_buffer, decode_resid_size, 0);
            if(i < 0) {
                debug->printf("Opus decode error %s", opus_strerror(i));
            }
            decode_resid_size = i;
            for(i = 0; i < decode_resid_size; i++) {
                decode_resid_buffer[i] = (decode_resid_buffer[i] == 0x7fff) ? 0x3fff : (decode_resid_buffer[i] + 1) >> 1;
            }
            memcpy(decode_buffer, decode_resid_buffer, decode_samples * sizeof(short));
            decode_resid_pos = decode_samples;
            break;
        default:
            if (payload_sample_rate_id == SAMPLE_RATE_ID_16000) {
                for(i = 0; i < ((int) jitter_buffer_descriptor_time); i++) {
                    decode_buffer[i*2] = g711plc_get_plc_sample(&g711plc, &(decode_buffer[i*2+1]));
                }
            }
            else {
                for(i = 0; i < ((int) jitter_buffer_descriptor_time); i++) {
                    decode_buffer[i] = g711plc_get_plc_sample(&g711plc, &s);
                }
            }
            break;
        }
    }
    if(record_sample_rate_id == payload_sample_rate_id) {
        memcpy(feedback_buffer, decode_buffer, decode_samples * sizeof(short));
    }
    else if((record_sample_rate_id != playback_sample_rate_id) && (sample_rate_table[record_sample_rate_id] > sample_rate_table[payload_sample_rate_id])) {
        resample_up(&feedback_resampler, decode_buffer, decode_samples, feedback_buffer);
    }
    if(sample_rate_table[playback_sample_rate_id] > sample_rate_table[payload_sample_rate_id]) {
        resample_up(&decoder_resampler, decode_buffer, decode_samples, buffer);
    }
    if((record_sample_rate_id != payload_sample_rate_id) && (record_sample_rate_id == playback_sample_rate_id)) {
        memcpy(feedback_buffer, buffer, jitter_buffer_descriptor_time * (sample_rate_table[record_sample_rate_id] / 8000) * sizeof(short));
    }
}

void AudioIoChannel::JitterBufferPulled(IJitterBuffer * const jitterBuffer, const void * buf, size_t len)
{
    if(buf) {
        if(coder == AudioCoder::CODER_UNDEFINED) {
            memset(pull_buffer, 0, jitter_buffer_descriptor_time *
                (sample_rate_table[playback_sample_rate_id] / 8000) * sizeof(short));
            memset(feedback_pull_buffer, 0, jitter_buffer_descriptor_time *
                (sample_rate_table[record_sample_rate_id] / 8000) * sizeof(short));
            return;
        }
    }
    decode_frame(coder, len, (const byte *) buf, pull_buffer, feedback_pull_buffer);
}

bool AudioIoChannel::Pull()
{
    unsigned int samples, descriptor_samples, feedback_descriptor_samples;
    bool frames_pulled;

    frames_pulled = false;
    samples = execInterval8khz * (sample_rate_table[playback_sample_rate_id]/8000);
    descriptor_samples = jitter_buffer_descriptor_time * (sample_rate_table[playback_sample_rate_id]/8000);
    feedback_descriptor_samples = jitter_buffer_descriptor_time * (sample_rate_table[record_sample_rate_id]/8000);
    if(recv_pull_pos + samples > recv_pull_end) {
        if(recv_pull_pos < recv_pull_end) {
            memmove(recv_pull_buffer, &(recv_pull_buffer[recv_pull_pos]), (recv_pull_end - recv_pull_pos) * sizeof(short));
            recv_pull_end -= recv_pull_pos;
            memmove(send_pull_buffer, &(send_pull_buffer[send_pull_pos]), (send_pull_end - send_pull_pos) * sizeof(short));
            send_pull_end -= send_pull_pos;
        }
        else {
            recv_pull_end = 0;
            send_pull_end = 0;
        }
        recv_pull_pos = 0;
        send_pull_pos = 0;
        do {
            feedback_pull_buffer = &(send_pull_buffer[send_pull_end]);
            pull_buffer = &(recv_pull_buffer[recv_pull_end]);

            if (jitter_buffer->Pull(false)) {
                frames_pulled = true;
            }

            recv_pull_end += descriptor_samples;
            send_pull_end += feedback_descriptor_samples;
        }
        while(recv_pull_end < samples);
    }
    recv_pull_pos += samples;
    send_pull_pos += execInterval8khz * (sample_rate_table[record_sample_rate_id]/8000);
    return frames_pulled;
}

const short *AudioIoChannel::PulledSamples()
{
    return &(recv_pull_buffer[recv_pull_pos - execInterval8khz * (sample_rate_table[playback_sample_rate_id]/8000)]);
}

const short *AudioIoChannel::FeedbackSamples()
{
    return &(send_pull_buffer[send_pull_pos - execInterval8khz * (sample_rate_table[record_sample_rate_id]/8000)]);
}

void AudioIoChannel::PutSamples(const short *buffer)
{
    unsigned int i, n, pos, record_samples;
    int l;
    bool flush;

    pos = 0;
    record_samples = execInterval8khz * (sample_rate_table[record_sample_rate_id]/8000);
    unsigned send_packet_samples = payload_ms * ((sample_rate_table[record_sample_rate_id]) / 1000);
    unsigned send_frame_samples;

    flush = false;
    send_frame_samples = 0;
    if(send_sample_pos < send_packet_samples) {
        if((coder == AudioCoder::G729)
         ||(coder == AudioCoder::G729A)
         ||(coder == AudioCoder::G729B)
         ||(coder == AudioCoder::G729AB)
         ||(coder == AudioCoder::OPUS_NB)
         ||(coder == AudioCoder::OPUS_WB)) {
            send_frame_samples = 80 * (sample_rate_table[record_sample_rate_id] / 8000);
        }
        else {
            send_frame_samples = SEND_MAX_FRAME_SAMPLES * (sample_rate_table[record_sample_rate_id] / 8000);
            if(send_frame_samples > send_packet_samples - send_sample_pos) {
                send_frame_samples = send_packet_samples - send_sample_pos;
            }
        }
    }
    do {
        if((send_sample_pos < send_packet_samples) && (send_linear_pos + record_samples >= send_frame_samples)) {
            if (send_linear_pos < send_frame_samples) {
                record_samples -= send_frame_samples - send_linear_pos;
                memcpy(&(send_linear_buffer[send_linear_pos]), &(buffer[pos]), (send_frame_samples - send_linear_pos) * sizeof(short));
                pos += send_frame_samples - send_linear_pos;
                send_linear_pos = 0;
            }
            short resample_buffer[SEND_MAX_FRAME_SAMPLES * (MAX_CODEC_SAMPLE_RATE/8000)];
            unsigned int encode_samples;
            short *encode_buffer;

            encode_samples = send_frame_samples / (sample_rate_table[record_sample_rate_id] / sample_rate_table[payload_sample_rate_id]);
            encode_buffer = send_linear_buffer;
            if(record_sample_rate_id != payload_sample_rate_id) {
                encode_buffer = resample_buffer;
                resample_down(&encoder_resampler, send_linear_buffer, encode_samples, resample_buffer);
            }
            switch(coder) {
            case AudioCoder::G711_U:
                for(i = 0; i < encode_samples; i++) {
                    short s = (encode_buffer[i] < -16384) ? -32768 : ((encode_buffer[i] > 16383) ? 32767 : encode_buffer[i] << 1);
                    send_data.b[send_data_pos++] = norm_compress_ulaw(s);
                }
                break;
            default:
            case AudioCoder::G711_A:
                for(i = 0; i < encode_samples; i++) {
                    short s = (encode_buffer[i] < -16384) ? -32768 : ((encode_buffer[i] > 16383 - 2) ? 32767 : (encode_buffer[i] + 2) << 1);
                    send_data.b[send_data_pos++] = norm_compress_alaw(s);
                }
                break;
            case AudioCoder::G722:
                for(i = 0; i < encode_samples/2; i++) {
                    send_data.b[send_data_pos++] = g722_encoder_exec(&(encoder.g722), &(encode_buffer[i*2]));
                }
                break;
            case AudioCoder::G729:
            case AudioCoder::G729A:
            case AudioCoder::G729B:
            case AudioCoder::G729AB: {
                short b[80];
                for(i = 0; i < 80; i++) {
                    b[i] = (encode_buffer[i] < -16384) ? -32768 : ((encode_buffer[i] > 16383) ? 32767 : encode_buffer[i] << 1);
                }
                short codebits[82];
                g729ab_encode((g729ab_encoder *)(encoder.g729ab), b, codebits);
                n = codebits[1] >> 3;
                if((n == 10) || (n == 2)) {
                    for (i = 0; i < n; i++) {
                        /* The stream contains 0x007f for a '0' bit and 0x0081 for a '1' bit */
                        send_data.b[send_data_pos++] = (codebits[(i << 3) + 2 + 0] & 0x80) | ((codebits[(i << 3) + 2 + 1] & 0x80) >> 1) |
                            ((codebits[(i << 3) + 2 + 2] & 0x80) >> 2) | ((codebits[(i << 3) + 2 + 3] & 0x80) >> 3) |
                            ((codebits[(i << 3) + 2 + 4] & 0x80) >> 4) | ((codebits[(i << 3) + 2 + 5] & 0x80) >> 5) |
                            ((codebits[(i << 3) + 2 + 6] & 0x80) >> 6) | (codebits[(i << 3) + 2 + 7] >> 7);
                    }
                }
                if(n != 10) {
                    flush = true;
                }
                break;
                }
            case AudioCoder::OPUS_NB:
            case AudioCoder::OPUS_WB:
                for(i = 0; i < encode_samples; i++) {
                    send_data.s[send_data_pos++] = (encode_buffer[i] < -16384) ? -32768 : ((encode_buffer[i] > 16383) ? 32767 : encode_buffer[i] << 1);
                }
                break;
            }
            send_sample_pos += send_frame_samples;
            if (send_linear_pos >= send_frame_samples) {
                if (send_linear_pos != send_frame_samples) {
                    memmove(send_linear_buffer, &(send_linear_buffer[send_frame_samples]), (send_linear_pos - send_frame_samples) * sizeof(short));
                }
                send_linear_pos -= send_frame_samples;
            }
        }
        if((send_sample_pos >= send_packet_samples) || flush) {
            if(send_data_pos != 0) {
                if((coder != AudioCoder::CODER_UNDEFINED) && (payload_ms != 0)) {
                    if((coder == AudioCoder::OPUS_NB) || (coder == AudioCoder::OPUS_WB)) {
                        byte cbits[3 * 1276];
                        l = opus_encode((OpusEncoder *)(encoder.opus), send_data.s, send_data_pos, cbits, sizeof(cbits));
                        if(l <= 0) {
                            debug->printf("Opus encode error %s", opus_strerror(l));
                        }
                        else {
                            user->MediaIoRtpSend(cbits, l, send_timestamp, MediaType::AUDIO);
                        }
                    }
                    else {
                        user->MediaIoRtpSend(send_data.b, send_data_pos, send_timestamp, MediaType::AUDIO);
                    }
                }
                send_sequence++;
                send_data_pos = 0;
            }
            send_timestamp += timestamp_rate_multiplier * (send_sample_pos / (sample_rate_table[record_sample_rate_id] / 8000));
            send_sample_pos = 0;
            flush = false;
        }
        if((coder == AudioCoder::G729)
         ||(coder == AudioCoder::G729A)
         ||(coder == AudioCoder::G729B)
         ||(coder == AudioCoder::G729AB)
         ||(coder == AudioCoder::OPUS_NB)
         ||(coder == AudioCoder::OPUS_WB)) {
            send_frame_samples = 80 * (sample_rate_table[record_sample_rate_id] / 8000);
        }
        else {
            send_frame_samples = SEND_MAX_FRAME_SAMPLES * (sample_rate_table[record_sample_rate_id] / 8000);
            if(send_frame_samples > send_packet_samples - send_sample_pos) {
                send_frame_samples = send_packet_samples - send_sample_pos;
            }
        }
    }
    while(send_linear_pos + record_samples >= send_frame_samples);
    if (record_samples != 0) {
        memcpy(&(send_linear_buffer[send_linear_pos]), &(buffer[pos]), record_samples * sizeof(short));
        send_linear_pos += record_samples;
    }
}

void AudioIoChannel::Open()
{
    channel_opened = true;
    if (!channel_attached) {
        channel_attached = true;
        if (audioIo) {
            audioIo->ChannelOpened(this);
        }
        else {
            update_dsp();
        }
    }
}

void AudioIoChannel::Close()
{
    channel_opened = false;
    if (channel_attached) {
        channel_attached = false;
        if (audioIo) {
            audioIo->ChannelClosed(this);
        }
    }
}

void AudioIoChannel::RtpRecv(void* buf, size_t len, dword timestamp, short sequenceNumberDiff, bool marker) 
{ 
    if (coder != AudioCoder::CODER_UNDEFINED) {
        if (rtpStream) rtpStream->Write(buf, len);
        jitter_buffer->Push(buf, len, timestamp);
    }
}
