/*
 *  Copyright (c) 2012 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.
 */

/*
 * Implementation of the actual packet buffer data structure.
 */

#include <assert.h>
#include "packet_buffer.h"

#include <string.h> /* to define NULL */

#include "signal_processing_library.h"

#include "mcu_dsp_common.h"

#include "neteq_error_codes.h"

#ifdef NETEQ_DELAY_LOGGING
/* special code for offline delay logging */
#include "delay_logging.h"
#include <stdio.h>

extern FILE *delay_fid2; /* file pointer to delay log file */
extern uint32_t tot_received_packets;
#endif /* NETEQ_DELAY_LOGGING */


int WebRtcNetEQ_PacketBufferInit(PacketBuf_t *bufferInst, int maxNoOfPackets,
                                 int16_t *pw16_memory, int memorySize)
{
    int i;
    int pos = 0;

    /* Sanity check */
    if ((memorySize < PBUFFER_MIN_MEMORY_SIZE) || (pw16_memory == NULL)
        || (maxNoOfPackets < 2) || (maxNoOfPackets > 600))
    {
        /* Invalid parameters */
        return (PBUFFER_INIT_ERROR);
    }

    /* Clear the buffer instance */
    WebRtcSpl_MemSetW16((int16_t*) bufferInst, 0,
        sizeof(PacketBuf_t) / sizeof(int16_t));

    /* Clear the buffer memory */
    WebRtcSpl_MemSetW16((int16_t*) pw16_memory, 0, memorySize);

    /* Set maximum number of packets */
    bufferInst->maxInsertPositions = maxNoOfPackets;

    /* Initialize array pointers */
    /* After each pointer has been set, the index pos is advanced to point immediately
     * after the the recently allocated vector. Note that one step for the pos index
     * corresponds to a int16_t.
     */

    bufferInst->timeStamp = (uint32_t*) &pw16_memory[pos];
    pos += maxNoOfPackets << 1; /* advance maxNoOfPackets * uint32_t */

    bufferInst->payloadLocation = (int16_t**) &pw16_memory[pos];
    pos += maxNoOfPackets * (sizeof(int16_t*) / sizeof(int16_t)); /* advance */

    bufferInst->seqNumber = (uint16_t*) &pw16_memory[pos];
    pos += maxNoOfPackets; /* advance maxNoOfPackets * uint16_t */

    bufferInst->payloadType = &pw16_memory[pos];
    pos += maxNoOfPackets; /* advance maxNoOfPackets * int16_t */

    bufferInst->payloadLengthBytes = &pw16_memory[pos];
    pos += maxNoOfPackets; /* advance maxNoOfPackets * int16_t */

    bufferInst->rcuPlCntr = &pw16_memory[pos];
    pos += maxNoOfPackets; /* advance maxNoOfPackets * int16_t */

    bufferInst->waitingTime = (int*) (&pw16_memory[pos]);
    /* Advance maxNoOfPackets * sizeof(waitingTime element). */
    pos += maxNoOfPackets *
        sizeof(*bufferInst->waitingTime) / sizeof(*pw16_memory);

    /* The payload memory starts after the slot arrays */
    bufferInst->startPayloadMemory = &pw16_memory[pos];
    bufferInst->currentMemoryPos = bufferInst->startPayloadMemory;
    bufferInst->memorySizeW16 = (memorySize - pos); /* Remaining memory */

    /* Initialize each payload slot as empty with infinite delay */
    for (i = 0; i < bufferInst->maxInsertPositions; i++)
    {
        bufferInst->payloadType[i] = -1;
    }

    /* Reset buffer parameters */
    bufferInst->numPacketsInBuffer = 0;
    bufferInst->packSizeSamples = 0;
    bufferInst->insertPosition = 0;

    /* Reset buffer statistics */
    bufferInst->discardedPackets = 0;

    return (0);
}


int WebRtcNetEQ_PacketBufferFlush(PacketBuf_t *bufferInst)
{
    int i;

    /* Sanity check */
    if (bufferInst->startPayloadMemory == NULL)
    {
        /* Packet buffer has not been initialized */
        /* Don't do the flushing operation, since we do not
         know the state of the struct variables */
        return (0);
    }

    /* Set all payload lengths to zero */
    WebRtcSpl_MemSetW16(bufferInst->payloadLengthBytes, 0, bufferInst->maxInsertPositions);

    /* Reset buffer variables */
    bufferInst->numPacketsInBuffer = 0;
    bufferInst->currentMemoryPos = bufferInst->startPayloadMemory;
    bufferInst->insertPosition = 0;

    /* Clear all slots, starting with the last one */
    for (i = (bufferInst->maxInsertPositions - 1); i >= 0; i--)
    {
        bufferInst->payloadType[i] = -1;
        bufferInst->timeStamp[i] = 0;
        bufferInst->seqNumber[i] = 0;
    }

    return (0);
}


int WebRtcNetEQ_PacketBufferInsert(PacketBuf_t *bufferInst, const RTPPacket_t *RTPpacket,
                                   int16_t *flushed, int av_sync)
{
    int nextPos;
    int i;

#ifdef NETEQ_DELAY_LOGGING
    /* special code for offline delay logging */
    int temp_var;
#endif /* NETEQ_DELAY_LOGGING */

    /* Initialize to "no flush" */
    *flushed = 0;

    /* Sanity check */
    if (bufferInst->startPayloadMemory == NULL)
    {
        /* packet buffer has not been initialized */
        return (-1);
    }

    /* Sanity check for payload length
     (payloadLen in bytes and memory size in int16_t) */
    if ((RTPpacket->payloadLen > (bufferInst->memorySizeW16 << 1)) || (RTPpacket->payloadLen
        <= 0))
    {
        /* faulty or too long payload length */
        return (-1);
    }

    /* If we are in AV-sync mode, there is a risk that we have inserted a sync
     * packet but now received the real version of it. Or because of some timing
     * we might be overwriting a true payload with sync (I'm not sure why this
     * should happen in regular case, but in some FEC enabled case happens).
     * Go through packets and delete the sync version of the packet in hand. Or
     * if this is sync packet and the regular version of it exists in the buffer
     * refrain from inserting.
     *
     * TODO(turajs): Could we get this for free if we had set the RCU-counter of
     * the sync packet to a number larger than 2?
     */
    if (av_sync) {
      for (i = 0; i < bufferInst->maxInsertPositions; ++i) {
        /* Check if sequence numbers match and the payload actually exists. */
        if (bufferInst->seqNumber[i] == RTPpacket->seqNumber &&
            bufferInst->payloadLengthBytes[i] > 0) {
          if (WebRtcNetEQ_IsSyncPayload(RTPpacket->payload,
                                        RTPpacket->payloadLen)) {
            return 0;
          }

          if (WebRtcNetEQ_IsSyncPayload(bufferInst->payloadLocation[i],
                                        bufferInst->payloadLengthBytes[i])) {
            /* Clear the position in the buffer. */
            bufferInst->payloadType[i] = -1;
            bufferInst->payloadLengthBytes[i] = 0;

            /* Reduce packet counter by one. */
            bufferInst->numPacketsInBuffer--;
            /* TODO(turajs) if this is the latest packet better we rewind
             * insertPosition and related variables. */
            break;  /* There should be only one match. */
          }
        }
      }
    }

    /* Find a position in the buffer for this packet */
    if (bufferInst->numPacketsInBuffer != 0)
    {
        /* Get the next slot */
        bufferInst->insertPosition++;
        if (bufferInst->insertPosition >= bufferInst->maxInsertPositions)
        {
            /* "Wrap around" and start from the beginning */
            bufferInst->insertPosition = 0;
        }

        /* Check if there is enough space for the new packet */
        if (bufferInst->currentMemoryPos + ((RTPpacket->payloadLen + 1) >> 1)
            >= &bufferInst->startPayloadMemory[bufferInst->memorySizeW16])
        {
            int16_t *tempMemAddress;

            /*
             * Payload does not fit at the end of the memory, put it in the beginning
             * instead
             */
            bufferInst->currentMemoryPos = bufferInst->startPayloadMemory;

            /*
             * Now, we must search for the next non-empty payload,
             * finding the one with the lowest start address for the payload
             */
            tempMemAddress = &bufferInst->startPayloadMemory[bufferInst->memorySizeW16];
            nextPos = -1;

            /* Loop through all slots again */
            for (i = 0; i < bufferInst->maxInsertPositions; i++)
            {
                /* Look for the non-empty slot with the lowest
                 payload location address */
                if (bufferInst->payloadLengthBytes[i] != 0 && bufferInst->payloadLocation[i]
                    < tempMemAddress)
                {
                    tempMemAddress = bufferInst->payloadLocation[i];
                    nextPos = i;
                }
            }

            /* Check that we did find a previous payload */
            if (nextPos == -1)
            {
                /* The buffer is corrupt => flush and return error */
                WebRtcNetEQ_PacketBufferFlush(bufferInst);
                *flushed = 1;
                return (-1);
            }
        }
        else
        {
            /* Payload fits at the end of memory. */

            /* Find the next non-empty slot. */
            nextPos = bufferInst->insertPosition + 1;

            /* Increase nextPos until a non-empty slot is found or end of array is encountered*/
            while ((bufferInst->payloadLengthBytes[nextPos] == 0) && (nextPos
                < bufferInst->maxInsertPositions))
            {
                nextPos++;
            }

            if (nextPos == bufferInst->maxInsertPositions)
            {
                /*
                 * Reached the end of the array, so there must be a packet in the first
                 * position instead
                 */
                nextPos = 0;

                /* Increase nextPos until a non-empty slot is found */
                while (bufferInst->payloadLengthBytes[nextPos] == 0)
                {
                    nextPos++;
                }
            }
        } /* end if-else */

        /*
         * Check if the new payload will extend into a payload later in memory.
         * If so, the buffer is full.
         */
        if ((bufferInst->currentMemoryPos <= bufferInst->payloadLocation[nextPos])
            && ((&bufferInst->currentMemoryPos[(RTPpacket->payloadLen + 1) >> 1])
                > bufferInst->payloadLocation[nextPos]))
        {
            /* Buffer is full, so the buffer must be flushed */
            WebRtcNetEQ_PacketBufferFlush(bufferInst);
            *flushed = 1;
        }

        if (bufferInst->payloadLengthBytes[bufferInst->insertPosition] != 0)
        {
            /* All positions are already taken and entire buffer should be flushed */
            WebRtcNetEQ_PacketBufferFlush(bufferInst);
            *flushed = 1;
        }

    }
    else
    {
        /* Buffer is empty, just insert the packet at the beginning */
        bufferInst->currentMemoryPos = bufferInst->startPayloadMemory;
        bufferInst->insertPosition = 0;
    }

    /* Insert packet in the found position */
    if (RTPpacket->starts_byte1 == 0)
    {
        /* Payload is 16-bit aligned => just copy it */

        WEBRTC_SPL_MEMCPY_W8(bufferInst->currentMemoryPos,
            RTPpacket->payload, RTPpacket->payloadLen);
    }
    else
    {
        /* Payload is not 16-bit aligned => align it during copy operation */
        for (i = 0; i < RTPpacket->payloadLen; i++)
        {
            /* copy the (i+1)-th byte to the i-th byte */

            WEBRTC_SPL_SET_BYTE(bufferInst->currentMemoryPos,
                (WEBRTC_SPL_GET_BYTE(RTPpacket->payload, (i + 1))), i);
        }
    }

    /* Copy the packet information */
    bufferInst->payloadLocation[bufferInst->insertPosition] = bufferInst->currentMemoryPos;
    bufferInst->payloadLengthBytes[bufferInst->insertPosition] = RTPpacket->payloadLen;
    bufferInst->payloadType[bufferInst->insertPosition] = RTPpacket->payloadType;
    bufferInst->seqNumber[bufferInst->insertPosition] = RTPpacket->seqNumber;
    bufferInst->timeStamp[bufferInst->insertPosition] = RTPpacket->timeStamp;
    bufferInst->rcuPlCntr[bufferInst->insertPosition] = RTPpacket->rcuPlCntr;
    bufferInst->waitingTime[bufferInst->insertPosition] = 0;
    /* Update buffer parameters */
    bufferInst->numPacketsInBuffer++;
    bufferInst->currentMemoryPos += (RTPpacket->payloadLen + 1) >> 1;

#ifdef NETEQ_DELAY_LOGGING
    /* special code for offline delay logging */
    if (*flushed)
    {
        temp_var = NETEQ_DELAY_LOGGING_SIGNAL_FLUSH;
        if (fwrite(&temp_var, sizeof(int), 1, delay_fid2) != 1) {
          return -1;
        }
    }
    temp_var = NETEQ_DELAY_LOGGING_SIGNAL_RECIN;
    if ((fwrite(&temp_var, sizeof(int),
                1, delay_fid2) != 1) ||
        (fwrite(&RTPpacket->timeStamp, sizeof(uint32_t),
                1, delay_fid2) != 1) ||
        (fwrite(&RTPpacket->seqNumber, sizeof(uint16_t),
                1, delay_fid2) != 1) ||
        (fwrite(&RTPpacket->payloadType, sizeof(int),
                1, delay_fid2) != 1) ||
        (fwrite(&RTPpacket->payloadLen, sizeof(int16_t),
                1, delay_fid2) != 1)) {
      return -1;
    }
    tot_received_packets++;
#endif /* NETEQ_DELAY_LOGGING */

    return (0);
}


int WebRtcNetEQ_PacketBufferExtract(PacketBuf_t *bufferInst, RTPPacket_t *RTPpacket,
                                    int bufferPosition, int *waitingTime)
{

    /* Sanity check */
    if (bufferInst->startPayloadMemory == NULL)
    {
        /* packet buffer has not been initialized */
        return (PBUFFER_NOT_INITIALIZED);
    }

    if (bufferPosition < 0 || bufferPosition >= bufferInst->maxInsertPositions)
    {
        /* buffer position is outside valid range */
        return (NETEQ_OTHER_ERROR);
    }

    /* Check that there is a valid payload in the specified position */
    if (bufferInst->payloadLengthBytes[bufferPosition] <= 0)
    {
        /* The position does not contain a valid payload */
        RTPpacket->payloadLen = 0; /* Set zero length */
        return (PBUFFER_NONEXISTING_PACKET); /* Return error */
    }

    /* Payload exists => extract payload data */

    /* Copy the actual data payload to RTP packet struct */

    WEBRTC_SPL_MEMCPY_W16((int16_t*) RTPpacket->payload,
        bufferInst->payloadLocation[bufferPosition],
        (bufferInst->payloadLengthBytes[bufferPosition] + 1) >> 1); /*length in int16_t*/

    /* Copy payload parameters */
    RTPpacket->payloadLen = bufferInst->payloadLengthBytes[bufferPosition];
    RTPpacket->payloadType = bufferInst->payloadType[bufferPosition];
    RTPpacket->seqNumber = bufferInst->seqNumber[bufferPosition];
    RTPpacket->timeStamp = bufferInst->timeStamp[bufferPosition];
    RTPpacket->rcuPlCntr = bufferInst->rcuPlCntr[bufferPosition];
    *waitingTime = bufferInst->waitingTime[bufferPosition];
    RTPpacket->starts_byte1 = 0; /* payload is 16-bit aligned */

    /* Clear the position in the packet buffer */
    bufferInst->payloadType[bufferPosition] = -1;
    bufferInst->payloadLengthBytes[bufferPosition] = 0;
    bufferInst->seqNumber[bufferPosition] = 0;
    bufferInst->timeStamp[bufferPosition] = 0;
    bufferInst->waitingTime[bufferPosition] = 0;
    bufferInst->payloadLocation[bufferPosition] = bufferInst->startPayloadMemory;

    /* Reduce packet counter with one */
    bufferInst->numPacketsInBuffer--;

    return (0);
}

int WebRtcNetEQ_PacketBufferFindLowestTimestamp(PacketBuf_t* buffer_inst,
                                                uint32_t current_time_stamp,
                                                uint32_t* time_stamp,
                                                int* buffer_position,
                                                int erase_old_packets,
                                                int16_t* payload_type) {
  int32_t time_stamp_diff = WEBRTC_SPL_WORD32_MAX;  /* Smallest diff found. */
  int32_t new_diff;
  int i;
  int16_t rcu_payload_cntr;
  if (buffer_inst->startPayloadMemory == NULL) {
    /* Packet buffer has not been initialized. */
    return PBUFFER_NOT_INITIALIZED;
  }

  /* Initialize all return values. */
  *time_stamp = 0;
  *payload_type = -1;  /* Indicates that no packet was found. */
  *buffer_position = -1;  /* Indicates that no packet was found. */
  rcu_payload_cntr = WEBRTC_SPL_WORD16_MAX;  /* Indicates no packet found. */

  /* Check if buffer is empty. */
  if (buffer_inst->numPacketsInBuffer <= 0) {
    return 0;
  }

  /* Loop through all slots in buffer. */
  if (erase_old_packets) {  /* If old payloads should be discarded. */
    for (i = 0; i < buffer_inst->maxInsertPositions; ++i) {
      /* Calculate difference between this slot and current_time_stamp. */
      new_diff = (int32_t)(buffer_inst->timeStamp[i] - current_time_stamp);

      /* Check if payload should be discarded. */
      if ((new_diff < 0)  /* Payload is too old */
          && (new_diff > -30000)  /* Account for TS wrap-around. */
          && (buffer_inst->payloadLengthBytes[i] > 0)) {  /* Payload exists. */
        /* Throw away old packet. */

        /* Clear the position in the buffer. */
        buffer_inst->payloadType[i] = -1;
        buffer_inst->payloadLengthBytes[i] = 0;

        /* Reduce packet counter by one. */
        buffer_inst->numPacketsInBuffer--;
        /* Increase discard counter for in-call statistics. */
        buffer_inst->discardedPackets++;
      } else if (((new_diff < time_stamp_diff) 
                  || ((new_diff == time_stamp_diff)
                      && (buffer_inst->rcuPlCntr[i] < rcu_payload_cntr)))
                      && (buffer_inst->payloadLengthBytes[i] > 0)) {
        /* New diff is smaller than previous diffs or we have a candidate with a
         * time stamp as previous candidate but better RCU-counter; 
         * and the payload exists. 
         */ 
        /* Save this position as the best candidate. */
        *buffer_position = i;
        time_stamp_diff = new_diff;
        *payload_type = buffer_inst->payloadType[i];
        rcu_payload_cntr = buffer_inst->rcuPlCntr[i];
      }
    }
  } else {
    for (i = 0; i < buffer_inst->maxInsertPositions; ++i) {
      /* Calculate difference between this slot and current_time_stamp. */
      new_diff = (int32_t)(buffer_inst->timeStamp[i] - current_time_stamp);

      /* Check if this is the oldest packet. */
      if (((new_diff < time_stamp_diff) 
           || ((new_diff == time_stamp_diff)
               && (buffer_inst->rcuPlCntr[i] < rcu_payload_cntr)))
               && (buffer_inst->payloadLengthBytes[i] > 0)) {
        /* New diff is smaller than previous diffs or we have a candidate with a
         * time_stamp as previous candidate but better RCU-counter; 
         * and the payload exists. 
         */ 
        /* Save this position as the best candidate. */
        *buffer_position = i;
        time_stamp_diff = new_diff;
        *payload_type = buffer_inst->payloadType[i];
        rcu_payload_cntr = buffer_inst->rcuPlCntr[i];
      }
    }
  }

  /* Check that we did find a real position. */
  if (*buffer_position >= 0) {
    /* Get the time_stamp for the best position. */
    *time_stamp = buffer_inst->timeStamp[*buffer_position];
  }

  return 0;
}

int WebRtcNetEQ_PacketBufferGetPacketSize(const PacketBuf_t* buffer_inst,
                                          int buffer_pos,
                                          const CodecDbInst_t* codec_database,
                                          int codec_pos, int last_duration,
                                          int av_sync) {
  if (codec_database->funcDurationEst[codec_pos] == NULL) {
    return last_duration;
  }

  if (av_sync != 0 &&
      WebRtcNetEQ_IsSyncPayload(buffer_inst->payloadLocation[buffer_pos],
                                buffer_inst->payloadLengthBytes[buffer_pos])) {
    // In AV-sync and sync payload, report |last_duration| as current duration.
    return last_duration;
  }

  return (*codec_database->funcDurationEst[codec_pos])(
    codec_database->codec_state[codec_pos],
    (const uint8_t *)buffer_inst->payloadLocation[buffer_pos],
    buffer_inst->payloadLengthBytes[buffer_pos]);
}

int32_t WebRtcNetEQ_PacketBufferGetSize(const PacketBuf_t* buffer_inst,
                                        const CodecDbInst_t* codec_database,
                                        int av_sync) {
  int i, count;
  int last_duration;
  int last_codec_pos;
  int last_payload_type;
  int32_t size_samples;

  count = 0;
  last_duration = buffer_inst->packSizeSamples;
  last_codec_pos = -1;
  last_payload_type = -1;
  size_samples = 0;

  /* Loop through all slots in the buffer */
  for (i = 0; i < buffer_inst->maxInsertPositions; i++) {
    /* Only count the packets with non-zero size */
    if (buffer_inst->payloadLengthBytes[i] != 0) {
      int payload_type;
      int codec_pos;
      /* Figure out the codec database entry for this payload_type. */
      payload_type = buffer_inst->payloadType[i];
      /* Remember the last one, to avoid the database search. */
      if(payload_type == last_payload_type) {
        codec_pos = last_codec_pos;
      }
      else {
        codec_pos = WebRtcNetEQ_DbGetCodec(codec_database,
          payload_type);
        if (codec_pos >= 0) {
          codec_pos = codec_database->position[codec_pos];
        }
      }
      last_codec_pos = codec_pos;
      last_payload_type = payload_type;
      if (codec_pos >= 0) {
        /*
         * Right now WebRtcNetEQ_PacketBufferGetPacketSize either always
         * returns last_duration or always computes the real duration without
         * looking at last_duration. If an implementation really wanted to use
         * last_duration to compute a changing duration, we would have to
         * iterate through the packets in chronological order by timestamp.
         */
        /* Check for error before setting. */
        int temp_last_duration = WebRtcNetEQ_PacketBufferGetPacketSize(
            buffer_inst, i, codec_database, codec_pos,
            last_duration, av_sync);
        if (temp_last_duration >= 0)
          last_duration = temp_last_duration;
      }
      /* Add in the size of this packet. */
      size_samples += last_duration;
      count++;
    }
  }

  /* Sanity check; size cannot be negative */
  if (size_samples < 0) {
    size_samples = 0;
  }
  return size_samples;
}

void WebRtcNetEQ_IncrementWaitingTimes(PacketBuf_t *buffer_inst) {
  int i;
  /* Loop through all slots in the buffer. */
  for (i = 0; i < buffer_inst->maxInsertPositions; ++i) {
    /* Only increment waiting time for the packets with non-zero size. */
    if (buffer_inst->payloadLengthBytes[i] != 0) {
      buffer_inst->waitingTime[i]++;
    }
  }
}

int WebRtcNetEQ_GetDefaultCodecSettings(const enum WebRtcNetEQDecoder *codecID,
                                        int noOfCodecs, int *maxBytes,
                                        int *maxSlots,
                                        int* per_slot_overhead_bytes)
{
    int i;
    int ok = 0;
    int16_t w16_tmp;
    int16_t codecBytes;
    int16_t codecBuffers;

    /* Initialize return variables to zero */
    *maxBytes = 0;
    *maxSlots = 0;

    /* Loop through all codecs supplied to function */
    for (i = 0; i < noOfCodecs; i++)
    {
        /* Find current codec and set parameters accordingly */

        if ((codecID[i] == kDecoderPCMu) || (codecID[i] == kDecoderPCMu_2ch))
        {
            codecBytes = 1680; /* Up to 210ms @ 64kbps */
            codecBuffers = 30; /* Down to 5ms frames */
        }
        else if ((codecID[i] == kDecoderPCMa) ||
            (codecID[i] == kDecoderPCMa_2ch))
        {
            codecBytes = 1680; /* Up to 210ms @ 64kbps */
            codecBuffers = 30; /* Down to 5ms frames */
        }
        else if (codecID[i] == kDecoderILBC)
        {
            codecBytes = 380; /* 200ms @ 15.2kbps (20ms frames) */
            codecBuffers = 10;
        }
        else if (codecID[i] == kDecoderISAC)
        {
            codecBytes = 960; /* 240ms @ 32kbps (60ms frames) */
            codecBuffers = 8;
        }
        else if ((codecID[i] == kDecoderISACswb) ||
            (codecID[i] == kDecoderISACfb))
        {
            codecBytes = 1560; /* 240ms @ 52kbps (30ms frames) */
            codecBuffers = 8;
        }
        else if (codecID[i] == kDecoderOpus)
        {
            codecBytes = 15300; /* 240ms @ 510kbps (60ms frames) */
            codecBuffers = 30;  /* Replicating the value for PCMu/a */
        }
        else if (codecID[i] == kDecoderOpus)
        {
            codecBytes = 15300; /* 240ms @ 510kbps (60ms frames) */
            codecBuffers = 30;  /* ?? Codec supports down to 2.5-60 ms frames */
        }
        else if ((codecID[i] == kDecoderPCM16B) ||
            (codecID[i] == kDecoderPCM16B_2ch))
        {
            codecBytes = 3360; /* 210ms */
            codecBuffers = 15;
        }
        else if ((codecID[i] == kDecoderPCM16Bwb) ||
            (codecID[i] == kDecoderPCM16Bwb_2ch))
        {
            codecBytes = 6720; /* 210ms */
            codecBuffers = 15;
        }
        else if ((codecID[i] == kDecoderPCM16Bswb32kHz) ||
            (codecID[i] == kDecoderPCM16Bswb32kHz_2ch))
        {
            codecBytes = 13440; /* 210ms */
            codecBuffers = 15;
        }
        else if (codecID[i] == kDecoderPCM16Bswb48kHz)
        {
            codecBytes = 20160; /* 210ms */
            codecBuffers = 15;
        }
        else if ((codecID[i] == kDecoderG722) ||
            (codecID[i] == kDecoderG722_2ch))
        {
            codecBytes = 1680; /* 210ms @ 64kbps */
            codecBuffers = 15;
        }
        else if (codecID[i] == kDecoderRED)
        {
            codecBytes = 0; /* Should not be max... */
            codecBuffers = 0;
        }
        else if (codecID[i] == kDecoderAVT)
        {
            codecBytes = 0; /* Should not be max... */
            codecBuffers = 0;
        }
        else if (codecID[i] == kDecoderCNG)
        {
            codecBytes = 0; /* Should not be max... */
            codecBuffers = 0;
        }
        else if (codecID[i] == kDecoderG729)
        {
            codecBytes = 210; /* 210ms @ 8kbps */
            codecBuffers = 20; /* max 200ms supported for 10ms frames */
        }
        else if (codecID[i] == kDecoderG729_1)
        {
            codecBytes = 840; /* 210ms @ 32kbps */
            codecBuffers = 10; /* max 200ms supported for 20ms frames */
        }
        else if (codecID[i] == kDecoderG726_16)
        {
            codecBytes = 400; /* 200ms @ 16kbps */
            codecBuffers = 10;
        }
        else if (codecID[i] == kDecoderG726_24)
        {
            codecBytes = 600; /* 200ms @ 24kbps */
            codecBuffers = 10;
        }
        else if (codecID[i] == kDecoderG726_32)
        {
            codecBytes = 800; /* 200ms @ 32kbps */
            codecBuffers = 10;
        }
        else if (codecID[i] == kDecoderG726_40)
        {
            codecBytes = 1000; /* 200ms @ 40kbps */
            codecBuffers = 10;
        }
        else if (codecID[i] == kDecoderG722_1_16)
        {
            codecBytes = 420; /* 210ms @ 16kbps */
            codecBuffers = 10;
        }
        else if (codecID[i] == kDecoderG722_1_24)
        {
            codecBytes = 630; /* 210ms @ 24kbps */
            codecBuffers = 10;
        }
        else if (codecID[i] == kDecoderG722_1_32)
        {
            codecBytes = 840; /* 210ms @ 32kbps */
            codecBuffers = 10;
        }
        else if (codecID[i] == kDecoderG722_1C_24)
        {
            codecBytes = 630; /* 210ms @ 24kbps */
            codecBuffers = 10;
        }
        else if (codecID[i] == kDecoderG722_1C_32)
        {
            codecBytes = 840; /* 210ms @ 32kbps */
            codecBuffers = 10;
        }
        else if (codecID[i] == kDecoderG722_1C_48)
        {
            codecBytes = 1260; /* 210ms @ 48kbps */
            codecBuffers = 10;
        }
        else if (codecID[i] == kDecoderSPEEX_8)
        {
            codecBytes = 1250; /* 210ms @ 50kbps */
            codecBuffers = 10;
        }
        else if (codecID[i] == kDecoderSPEEX_16)
        {
            codecBytes = 1250; /* 210ms @ 50kbps */
            codecBuffers = 10;
        }
        else if ((codecID[i] == kDecoderCELT_32) ||
            (codecID[i] == kDecoderCELT_32_2ch))
        {
            codecBytes = 1250; /* 210ms @ 50kbps */
            codecBuffers = 10;
        }
        else if (codecID[i] == kDecoderGSMFR)
        {
            codecBytes = 340; /* 200ms */
            codecBuffers = 10;
        }
        else if (codecID[i] == kDecoderAMR)
        {
            codecBytes = 384; /* 240ms @ 12.2kbps+headers (60ms frames) */
            codecBuffers = 10;
        }
        else if (codecID[i] == kDecoderAMRWB)
        {
            codecBytes = 744;
            codecBuffers = 10;
        }
        else if (codecID[i] == kDecoderArbitrary)
        {
            codecBytes = 6720; /* Assume worst case uncompressed WB 210ms */
            codecBuffers = 15;
        }
        else
        {
            /*Unknow codec */
            codecBytes = 0;
            codecBuffers = 0;
            ok = CODEC_DB_UNKNOWN_CODEC;
        }

        /* Update max variables */
        *maxBytes = WEBRTC_SPL_MAX((*maxBytes), codecBytes);
        *maxSlots = WEBRTC_SPL_MAX((*maxSlots), codecBuffers);

    } /* end of for loop */

    /*
     * Add size needed by the additional pointers for each slot inside struct,
     * as indicated on each line below.
     */
    w16_tmp = (sizeof(uint32_t) /* timeStamp */
    + sizeof(int16_t*) /* payloadLocation */
    + sizeof(uint16_t) /* seqNumber */
    + sizeof(int16_t)  /* payloadType */
    + sizeof(int16_t)  /* payloadLengthBytes */
    + sizeof(int16_t)  /* rcuPlCntr   */
    + sizeof(int));          /* waitingTime */
    /* Add the extra size per slot to the memory count */
    *maxBytes += w16_tmp * (*maxSlots);

    *per_slot_overhead_bytes = w16_tmp;
    return ok;
}
