518 lines
18 KiB
C
518 lines
18 KiB
C
|
/*
|
||
|
* 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.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Implementation of the RecIn function, which is the main function for inserting RTP
|
||
|
* packets into NetEQ.
|
||
|
*/
|
||
|
|
||
|
#include "mcu.h"
|
||
|
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "signal_processing_library.h"
|
||
|
|
||
|
#include "automode.h"
|
||
|
#include "dtmf_buffer.h"
|
||
|
#include "neteq_defines.h"
|
||
|
#include "neteq_error_codes.h"
|
||
|
|
||
|
|
||
|
int WebRtcNetEQ_RecInInternal(MCUInst_t *MCU_inst, RTPPacket_t *RTPpacketInput,
|
||
|
WebRtc_UWord32 uw32_timeRec)
|
||
|
{
|
||
|
RTPPacket_t RTPpacket[2];
|
||
|
int i_k;
|
||
|
int i_ok = 0, i_No_Of_Payloads = 1;
|
||
|
WebRtc_Word16 flushed = 0;
|
||
|
WebRtc_Word16 codecPos;
|
||
|
WebRtc_UWord32 diffTS, uw32_tmp;
|
||
|
int curr_Codec;
|
||
|
WebRtc_Word16 isREDPayload = 0;
|
||
|
WebRtc_Word32 temp_bufsize = MCU_inst->PacketBuffer_inst.numPacketsInBuffer;
|
||
|
#ifdef NETEQ_RED_CODEC
|
||
|
RTPPacket_t* RTPpacketPtr[2]; /* Support for redundancy up to 2 payloads */
|
||
|
RTPpacketPtr[0] = &RTPpacket[0];
|
||
|
RTPpacketPtr[1] = &RTPpacket[1];
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Copy from input RTP packet to local copy
|
||
|
* (mainly to enable multiple payloads using RED)
|
||
|
*/
|
||
|
|
||
|
WEBRTC_SPL_MEMCPY_W8(&RTPpacket[0], RTPpacketInput, sizeof(RTPPacket_t));
|
||
|
|
||
|
/* Reinitialize NetEq if it's needed (changed SSRC or first call) */
|
||
|
|
||
|
if ((RTPpacket[0].ssrc != MCU_inst->ssrc) || (MCU_inst->first_packet == 1))
|
||
|
{
|
||
|
WebRtcNetEQ_RTCPInit(&MCU_inst->RTCP_inst, RTPpacket[0].seqNumber);
|
||
|
MCU_inst->first_packet = 0;
|
||
|
|
||
|
/* Flush the buffer */
|
||
|
WebRtcNetEQ_PacketBufferFlush(&MCU_inst->PacketBuffer_inst);
|
||
|
|
||
|
/* Store new SSRC */
|
||
|
MCU_inst->ssrc = RTPpacket[0].ssrc;
|
||
|
|
||
|
/* Update codecs */
|
||
|
MCU_inst->timeStamp = RTPpacket[0].timeStamp;
|
||
|
MCU_inst->current_Payload = RTPpacket[0].payloadType;
|
||
|
|
||
|
/*Set MCU to update codec on next SignalMCU call */
|
||
|
MCU_inst->new_codec = 1;
|
||
|
|
||
|
/* Reset timestamp scaling */
|
||
|
MCU_inst->TSscalingInitialized = 0;
|
||
|
|
||
|
}
|
||
|
|
||
|
/* Call RTCP statistics */
|
||
|
i_ok |= WebRtcNetEQ_RTCPUpdate(&(MCU_inst->RTCP_inst), RTPpacket[0].seqNumber,
|
||
|
RTPpacket[0].timeStamp, uw32_timeRec);
|
||
|
|
||
|
/* If Redundancy is supported and this is the redundancy payload, separate the payloads */
|
||
|
#ifdef NETEQ_RED_CODEC
|
||
|
if (RTPpacket[0].payloadType == WebRtcNetEQ_DbGetPayload(&MCU_inst->codec_DB_inst,
|
||
|
kDecoderRED))
|
||
|
{
|
||
|
|
||
|
/* Split the payload into a main and a redundancy payloads */
|
||
|
i_ok = WebRtcNetEQ_RedundancySplit(RTPpacketPtr, 2, &i_No_Of_Payloads);
|
||
|
if (i_ok < 0)
|
||
|
{
|
||
|
/* error returned */
|
||
|
return i_ok;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Only accept a few redundancies of the same type as the main data,
|
||
|
* AVT events and CNG.
|
||
|
*/
|
||
|
if ((i_No_Of_Payloads > 1) && (RTPpacket[0].payloadType != RTPpacket[1].payloadType)
|
||
|
&& (RTPpacket[0].payloadType != WebRtcNetEQ_DbGetPayload(&MCU_inst->codec_DB_inst,
|
||
|
kDecoderAVT)) && (RTPpacket[1].payloadType != WebRtcNetEQ_DbGetPayload(
|
||
|
&MCU_inst->codec_DB_inst, kDecoderAVT)) && (!WebRtcNetEQ_DbIsCNGPayload(
|
||
|
&MCU_inst->codec_DB_inst, RTPpacket[0].payloadType))
|
||
|
&& (!WebRtcNetEQ_DbIsCNGPayload(&MCU_inst->codec_DB_inst, RTPpacket[1].payloadType)))
|
||
|
{
|
||
|
i_No_Of_Payloads = 1;
|
||
|
}
|
||
|
isREDPayload = 1;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* loop over the number of payloads */
|
||
|
for (i_k = 0; i_k < i_No_Of_Payloads; i_k++)
|
||
|
{
|
||
|
|
||
|
if (isREDPayload == 1)
|
||
|
{
|
||
|
RTPpacket[i_k].rcuPlCntr = i_k;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
RTPpacket[i_k].rcuPlCntr = 0;
|
||
|
}
|
||
|
|
||
|
/* Force update of SplitInfo if it's iLBC because of potential change between 20/30ms */
|
||
|
if (RTPpacket[i_k].payloadType == WebRtcNetEQ_DbGetPayload(&MCU_inst->codec_DB_inst,
|
||
|
kDecoderILBC))
|
||
|
{
|
||
|
i_ok = WebRtcNetEQ_DbGetSplitInfo(
|
||
|
&MCU_inst->PayloadSplit_inst,
|
||
|
(enum WebRtcNetEQDecoder) WebRtcNetEQ_DbGetCodec(&MCU_inst->codec_DB_inst,
|
||
|
RTPpacket[i_k].payloadType), RTPpacket[i_k].payloadLen);
|
||
|
if (i_ok < 0)
|
||
|
{
|
||
|
/* error returned */
|
||
|
return i_ok;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Get information about timestamp scaling for this payload type */
|
||
|
i_ok = WebRtcNetEQ_GetTimestampScaling(MCU_inst, RTPpacket[i_k].payloadType);
|
||
|
if (i_ok < 0)
|
||
|
{
|
||
|
/* error returned */
|
||
|
return i_ok;
|
||
|
}
|
||
|
|
||
|
if (MCU_inst->TSscalingInitialized == 0 && MCU_inst->scalingFactor != kTSnoScaling)
|
||
|
{
|
||
|
/* Must initialize scaling with current timestamps */
|
||
|
MCU_inst->externalTS = RTPpacket[i_k].timeStamp;
|
||
|
MCU_inst->internalTS = RTPpacket[i_k].timeStamp;
|
||
|
MCU_inst->TSscalingInitialized = 1;
|
||
|
}
|
||
|
|
||
|
/* Adjust timestamp if timestamp scaling is needed (e.g. SILK or G.722) */
|
||
|
if (MCU_inst->TSscalingInitialized == 1)
|
||
|
{
|
||
|
WebRtc_UWord32 newTS = WebRtcNetEQ_ScaleTimestampExternalToInternal(MCU_inst,
|
||
|
RTPpacket[i_k].timeStamp);
|
||
|
|
||
|
/* save the incoming timestamp for next time */
|
||
|
MCU_inst->externalTS = RTPpacket[i_k].timeStamp;
|
||
|
|
||
|
/* add the scaled difference to last scaled timestamp and save ... */
|
||
|
MCU_inst->internalTS = newTS;
|
||
|
|
||
|
RTPpacket[i_k].timeStamp = newTS;
|
||
|
}
|
||
|
|
||
|
/* Is this a DTMF packet?*/
|
||
|
if (RTPpacket[i_k].payloadType == WebRtcNetEQ_DbGetPayload(&MCU_inst->codec_DB_inst,
|
||
|
kDecoderAVT))
|
||
|
{
|
||
|
#ifdef NETEQ_ATEVENT_DECODE
|
||
|
if (MCU_inst->AVT_PlayoutOn)
|
||
|
{
|
||
|
i_ok = WebRtcNetEQ_DtmfInsertEvent(&MCU_inst->DTMF_inst,
|
||
|
RTPpacket[i_k].payload, RTPpacket[i_k].payloadLen,
|
||
|
RTPpacket[i_k].timeStamp);
|
||
|
if (i_ok != 0)
|
||
|
{
|
||
|
return i_ok;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
#ifdef NETEQ_STEREO
|
||
|
if (MCU_inst->usingStereo == 0)
|
||
|
{
|
||
|
/* do not set this for DTMF packets when using stereo mode */
|
||
|
MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF = 1;
|
||
|
}
|
||
|
#else
|
||
|
MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF = 1;
|
||
|
#endif
|
||
|
}
|
||
|
else if (WebRtcNetEQ_DbIsCNGPayload(&MCU_inst->codec_DB_inst,
|
||
|
RTPpacket[i_k].payloadType))
|
||
|
{
|
||
|
/* Is this a CNG packet? how should we handle this?*/
|
||
|
#ifdef NETEQ_CNG_CODEC
|
||
|
/* Get CNG sample rate */
|
||
|
WebRtc_UWord16 fsCng = WebRtcNetEQ_DbGetSampleRate(&MCU_inst->codec_DB_inst,
|
||
|
RTPpacket[i_k].payloadType);
|
||
|
if ((fsCng != MCU_inst->fs) && (fsCng > 8000))
|
||
|
{
|
||
|
/*
|
||
|
* We have received CNG with a different sample rate from what we are using
|
||
|
* now (must be > 8000, since we may use only one CNG type (default) for all
|
||
|
* frequencies). Flush buffer and signal new codec.
|
||
|
*/
|
||
|
WebRtcNetEQ_PacketBufferFlush(&MCU_inst->PacketBuffer_inst);
|
||
|
MCU_inst->new_codec = 1;
|
||
|
MCU_inst->current_Codec = -1;
|
||
|
}
|
||
|
i_ok = WebRtcNetEQ_PacketBufferInsert(&MCU_inst->PacketBuffer_inst,
|
||
|
&RTPpacket[i_k], &flushed);
|
||
|
if (i_ok < 0)
|
||
|
{
|
||
|
return RECIN_CNG_ERROR;
|
||
|
}
|
||
|
MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF = 1;
|
||
|
#else /* NETEQ_CNG_CODEC not defined */
|
||
|
return RECIN_UNKNOWNPAYLOAD;
|
||
|
#endif /* NETEQ_CNG_CODEC */
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Reinitialize the splitting if the payload and/or the payload length has changed */
|
||
|
curr_Codec = WebRtcNetEQ_DbGetCodec(&MCU_inst->codec_DB_inst,
|
||
|
RTPpacket[i_k].payloadType);
|
||
|
if (curr_Codec != MCU_inst->current_Codec)
|
||
|
{
|
||
|
if (curr_Codec < 0)
|
||
|
{
|
||
|
return RECIN_UNKNOWNPAYLOAD;
|
||
|
}
|
||
|
MCU_inst->current_Codec = curr_Codec;
|
||
|
MCU_inst->current_Payload = RTPpacket[i_k].payloadType;
|
||
|
i_ok = WebRtcNetEQ_DbGetSplitInfo(&MCU_inst->PayloadSplit_inst,
|
||
|
(enum WebRtcNetEQDecoder) MCU_inst->current_Codec,
|
||
|
RTPpacket[i_k].payloadLen);
|
||
|
if (i_ok < 0)
|
||
|
{ /* error returned */
|
||
|
return i_ok;
|
||
|
}
|
||
|
WebRtcNetEQ_PacketBufferFlush(&MCU_inst->PacketBuffer_inst);
|
||
|
MCU_inst->new_codec = 1;
|
||
|
}
|
||
|
|
||
|
/* update post-call statistics */
|
||
|
if (MCU_inst->new_codec != 1) /* not if in codec change */
|
||
|
{
|
||
|
diffTS = RTPpacket[i_k].timeStamp - MCU_inst->timeStamp; /* waiting time */
|
||
|
if (diffTS < 0x0FFFFFFF) /* guard against re-ordering */
|
||
|
{
|
||
|
/* waiting time in ms */
|
||
|
diffTS = WEBRTC_SPL_UDIV(diffTS,
|
||
|
WEBRTC_SPL_UDIV((WebRtc_UWord32) MCU_inst->fs, 1000) );
|
||
|
if (diffTS < MCU_inst->statInst.minPacketDelayMs)
|
||
|
{
|
||
|
/* new all-time low */
|
||
|
MCU_inst->statInst.minPacketDelayMs = diffTS;
|
||
|
}
|
||
|
if (diffTS > MCU_inst->statInst.maxPacketDelayMs)
|
||
|
{
|
||
|
/* new all-time high */
|
||
|
MCU_inst->statInst.maxPacketDelayMs = diffTS;
|
||
|
}
|
||
|
|
||
|
/* Update avg waiting time:
|
||
|
* avgPacketDelayMs =
|
||
|
* (avgPacketCount * avgPacketDelayMs + diffTS)/(avgPacketCount+1)
|
||
|
* with proper rounding.
|
||
|
*/
|
||
|
uw32_tmp
|
||
|
= WEBRTC_SPL_UMUL((WebRtc_UWord32) MCU_inst->statInst.avgPacketCount,
|
||
|
(WebRtc_UWord32) MCU_inst->statInst.avgPacketDelayMs);
|
||
|
uw32_tmp
|
||
|
= WEBRTC_SPL_ADD_SAT_W32(uw32_tmp,
|
||
|
(diffTS + (MCU_inst->statInst.avgPacketCount>>1)));
|
||
|
uw32_tmp = WebRtcSpl_DivU32U16(uw32_tmp,
|
||
|
(WebRtc_UWord16) (MCU_inst->statInst.avgPacketCount + 1));
|
||
|
MCU_inst->statInst.avgPacketDelayMs
|
||
|
= (WebRtc_UWord16) WEBRTC_SPL_MIN(uw32_tmp, (WebRtc_UWord32) 65535);
|
||
|
|
||
|
/* increase counter, but not to more than 65534 */
|
||
|
if (MCU_inst->statInst.avgPacketCount < (0xFFFF - 1))
|
||
|
{
|
||
|
MCU_inst->statInst.avgPacketCount++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Parse the payload and insert it into the buffer */
|
||
|
i_ok = WebRtcNetEQ_SplitAndInsertPayload(&RTPpacket[i_k],
|
||
|
&MCU_inst->PacketBuffer_inst, &MCU_inst->PayloadSplit_inst, &flushed);
|
||
|
if (i_ok < 0)
|
||
|
{
|
||
|
return i_ok;
|
||
|
}
|
||
|
if (MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF != 0)
|
||
|
{
|
||
|
/* first normal packet after CNG or DTMF */
|
||
|
MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF = -1;
|
||
|
}
|
||
|
}
|
||
|
/* Reset DSP timestamp etc. if packet buffer flushed */
|
||
|
if (flushed)
|
||
|
{
|
||
|
MCU_inst->new_codec = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Update Bandwidth Estimate
|
||
|
* Only send the main payload to BWE
|
||
|
*/
|
||
|
if ((curr_Codec = WebRtcNetEQ_DbGetCodec(&MCU_inst->codec_DB_inst,
|
||
|
RTPpacket[0].payloadType)) >= 0)
|
||
|
{
|
||
|
codecPos = MCU_inst->codec_DB_inst.position[curr_Codec];
|
||
|
if (MCU_inst->codec_DB_inst.funcUpdBWEst[codecPos] != NULL) /* codec has BWE function */
|
||
|
{
|
||
|
if (RTPpacket[0].starts_byte1) /* check for shifted byte alignment */
|
||
|
{
|
||
|
/* re-align to 16-bit alignment */
|
||
|
for (i_k = 0; i_k < RTPpacket[0].payloadLen; i_k++)
|
||
|
{
|
||
|
WEBRTC_SPL_SET_BYTE(RTPpacket[0].payload,
|
||
|
WEBRTC_SPL_GET_BYTE(RTPpacket[0].payload, i_k+1),
|
||
|
i_k);
|
||
|
}
|
||
|
RTPpacket[0].starts_byte1 = 0;
|
||
|
}
|
||
|
|
||
|
MCU_inst->codec_DB_inst.funcUpdBWEst[codecPos](
|
||
|
MCU_inst->codec_DB_inst.codec_state[codecPos],
|
||
|
(G_CONST WebRtc_UWord16 *) RTPpacket[0].payload,
|
||
|
(WebRtc_Word32) RTPpacket[0].payloadLen, RTPpacket[0].seqNumber,
|
||
|
(WebRtc_UWord32) RTPpacket[0].timeStamp, (WebRtc_UWord32) uw32_timeRec);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF == 0)
|
||
|
{
|
||
|
/* Calculate the total speech length carried in each packet */
|
||
|
temp_bufsize = MCU_inst->PacketBuffer_inst.numPacketsInBuffer - temp_bufsize;
|
||
|
temp_bufsize *= MCU_inst->PacketBuffer_inst.packSizeSamples;
|
||
|
|
||
|
if ((temp_bufsize > 0) && (MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF
|
||
|
== 0) && (temp_bufsize
|
||
|
!= MCU_inst->BufferStat_inst.Automode_inst.packetSpeechLenSamp))
|
||
|
{
|
||
|
/* Change the auto-mode parameters if packet length has changed */
|
||
|
WebRtcNetEQ_SetPacketSpeechLen(&(MCU_inst->BufferStat_inst.Automode_inst),
|
||
|
(WebRtc_Word16) temp_bufsize, MCU_inst->fs);
|
||
|
}
|
||
|
|
||
|
/* update statistics */
|
||
|
if ((WebRtc_Word32) (RTPpacket[0].timeStamp - MCU_inst->timeStamp) >= 0
|
||
|
&& !MCU_inst->new_codec)
|
||
|
{
|
||
|
/*
|
||
|
* Only update statistics if incoming packet is not older than last played out
|
||
|
* packet, and if new codec flag is not set.
|
||
|
*/
|
||
|
WebRtcNetEQ_UpdateIatStatistics(&MCU_inst->BufferStat_inst.Automode_inst,
|
||
|
MCU_inst->PacketBuffer_inst.maxInsertPositions, RTPpacket[0].seqNumber,
|
||
|
RTPpacket[0].timeStamp, MCU_inst->fs,
|
||
|
WebRtcNetEQ_DbIsMDCodec((enum WebRtcNetEQDecoder) MCU_inst->current_Codec),
|
||
|
(MCU_inst->NetEqPlayoutMode == kPlayoutStreaming));
|
||
|
}
|
||
|
}
|
||
|
else if (MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF == -1)
|
||
|
{
|
||
|
/*
|
||
|
* This is first "normal" packet after CNG or DTMF.
|
||
|
* Reset packet time counter and measure time until next packet,
|
||
|
* but don't update statistics.
|
||
|
*/
|
||
|
MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF = 0;
|
||
|
MCU_inst->BufferStat_inst.Automode_inst.packetIatCountSamp = 0;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
}
|
||
|
|
||
|
int WebRtcNetEQ_GetTimestampScaling(MCUInst_t *MCU_inst, int rtpPayloadType)
|
||
|
{
|
||
|
enum WebRtcNetEQDecoder codec;
|
||
|
int codecNumber;
|
||
|
|
||
|
codecNumber = WebRtcNetEQ_DbGetCodec(&MCU_inst->codec_DB_inst, rtpPayloadType);
|
||
|
if (codecNumber < 0)
|
||
|
{
|
||
|
/* error */
|
||
|
return codecNumber;
|
||
|
}
|
||
|
|
||
|
/* cast to enumerator */
|
||
|
codec = (enum WebRtcNetEQDecoder) codecNumber;
|
||
|
|
||
|
/*
|
||
|
* The factor obtained below is the number with which the RTP timestamp must be
|
||
|
* multiplied to get the true sample count.
|
||
|
*/
|
||
|
switch (codec)
|
||
|
{
|
||
|
case kDecoderG722:
|
||
|
{
|
||
|
/* Use timestamp scaling with factor 2 (two output samples per RTP timestamp) */
|
||
|
MCU_inst->scalingFactor = kTSscalingTwo;
|
||
|
break;
|
||
|
}
|
||
|
case kDecoderAVT:
|
||
|
case kDecoderCNG:
|
||
|
{
|
||
|
/* do not change the timestamp scaling settings */
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
/* do not use timestamp scaling */
|
||
|
MCU_inst->scalingFactor = kTSnoScaling;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
WebRtc_UWord32 WebRtcNetEQ_ScaleTimestampExternalToInternal(const MCUInst_t *MCU_inst,
|
||
|
WebRtc_UWord32 externalTS)
|
||
|
{
|
||
|
WebRtc_Word32 timestampDiff;
|
||
|
WebRtc_UWord32 internalTS;
|
||
|
|
||
|
/* difference between this and last incoming timestamp */
|
||
|
timestampDiff = externalTS - MCU_inst->externalTS;
|
||
|
|
||
|
switch (MCU_inst->scalingFactor)
|
||
|
{
|
||
|
case kTSscalingTwo:
|
||
|
{
|
||
|
/* multiply with 2 */
|
||
|
timestampDiff = WEBRTC_SPL_LSHIFT_W32(timestampDiff, 1);
|
||
|
break;
|
||
|
}
|
||
|
case kTSscalingTwoThirds:
|
||
|
{
|
||
|
/* multiply with 2/3 */
|
||
|
timestampDiff = WEBRTC_SPL_LSHIFT_W32(timestampDiff, 1);
|
||
|
timestampDiff = WebRtcSpl_DivW32W16(timestampDiff, 3);
|
||
|
break;
|
||
|
}
|
||
|
case kTSscalingFourThirds:
|
||
|
{
|
||
|
/* multiply with 4/3 */
|
||
|
timestampDiff = WEBRTC_SPL_LSHIFT_W32(timestampDiff, 2);
|
||
|
timestampDiff = WebRtcSpl_DivW32W16(timestampDiff, 3);
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
/* no scaling */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* add the scaled difference to last scaled timestamp and save ... */
|
||
|
internalTS = MCU_inst->internalTS + timestampDiff;
|
||
|
|
||
|
return internalTS;
|
||
|
}
|
||
|
|
||
|
WebRtc_UWord32 WebRtcNetEQ_ScaleTimestampInternalToExternal(const MCUInst_t *MCU_inst,
|
||
|
WebRtc_UWord32 internalTS)
|
||
|
{
|
||
|
WebRtc_Word32 timestampDiff;
|
||
|
WebRtc_UWord32 externalTS;
|
||
|
|
||
|
/* difference between this and last incoming timestamp */
|
||
|
timestampDiff = (WebRtc_Word32) internalTS - MCU_inst->internalTS;
|
||
|
|
||
|
switch (MCU_inst->scalingFactor)
|
||
|
{
|
||
|
case kTSscalingTwo:
|
||
|
{
|
||
|
/* divide by 2 */
|
||
|
timestampDiff = WEBRTC_SPL_RSHIFT_W32(timestampDiff, 1);
|
||
|
break;
|
||
|
}
|
||
|
case kTSscalingTwoThirds:
|
||
|
{
|
||
|
/* multiply with 3/2 */
|
||
|
timestampDiff = WEBRTC_SPL_MUL_32_16(timestampDiff, 3);
|
||
|
timestampDiff = WEBRTC_SPL_RSHIFT_W32(timestampDiff, 1);
|
||
|
break;
|
||
|
}
|
||
|
case kTSscalingFourThirds:
|
||
|
{
|
||
|
/* multiply with 3/4 */
|
||
|
timestampDiff = WEBRTC_SPL_MUL_32_16(timestampDiff, 3);
|
||
|
timestampDiff = WEBRTC_SPL_RSHIFT_W32(timestampDiff, 2);
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
/* no scaling */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* add the scaled difference to last scaled timestamp and save ... */
|
||
|
externalTS = MCU_inst->externalTS + timestampDiff;
|
||
|
|
||
|
return externalTS;
|
||
|
}
|