diff --git a/Workspace_msvc/lib_util.vcxproj b/Workspace_msvc/lib_util.vcxproj
index bead7110f33c5f14f0318980c52e315d490a5d9b..7c10648b72aedff8ba337d2b4f9f98b3e1716f24 100644
--- a/Workspace_msvc/lib_util.vcxproj
+++ b/Workspace_msvc/lib_util.vcxproj
@@ -110,6 +110,11 @@
+
+
+
+
+
diff --git a/Workspace_msvc/lib_util.vcxproj.filters b/Workspace_msvc/lib_util.vcxproj.filters
index 8fc8082d1a4d9a3ab8790074ae02daf283a9ba20..ed062708ad3807d35d62c1c9b4b24dfe06f344ed 100644
--- a/Workspace_msvc/lib_util.vcxproj.filters
+++ b/Workspace_msvc/lib_util.vcxproj.filters
@@ -82,6 +82,21 @@
util_c
+
+ util_c
+
+
+ util_c
+
+
+ util_c
+
+
+ util_c
+
+
+ util_c
+
diff --git a/apps/decoder.c b/apps/decoder.c
index 4a28b4470b7c26a1673b05209a6c3d834a0882df..f2f0e31b3dfbdbed85b3ba823f62d44281a80b3d 100644
--- a/apps/decoder.c
+++ b/apps/decoder.c
@@ -38,6 +38,11 @@
#include "bitstream_reader.h"
#include "evs_rtp_payload.h"
#include "ism_file_writer.h"
+#ifdef IVAS_RTPDUMP
+#include "ivas_rtp_api.h"
+#include "ivas_rtp_pi_data.h"
+#include "ivas_rtp_file.h"
+#endif
#include "jbm_file_writer.h"
#include "hrtf_file_reader.h"
#include "ls_custom_file_reader.h"
@@ -126,6 +131,10 @@ typedef struct
IVAS_DEC_COMPLEXITY_LEVEL complexityLevel;
bool tsmEnabled;
IVAS_RENDER_FRAMESIZE renderFramesize;
+#ifdef IVAS_RTPDUMP
+ bool applyPiData;
+ char *piOutputFilename;
+#endif
#ifdef DEBUGGING
IVAS_DEC_FORCED_REND_MODE forcedRendMode;
#ifdef DEBUG_FOA_AGC
@@ -163,6 +172,383 @@ typedef struct
} IVAS_DEC_HRTF_BINARY_WRAPPER;
+#ifdef IVAS_RTPDUMP
+#ifdef DEBUGGING
+#define DEBUG_PRINT fprintf
+#else
+#define DEBUG_PRINT( ... )
+#endif
+
+typedef struct
+{
+ PIDATA data;
+ uint32_t timestamp;
+} PIDATA_TS;
+
+typedef struct
+{
+ uint8_t packet[NOMINAL_BUFFER_SIZE( IVAS_MAX_FRAMES_PER_RTP_PACKET )];
+ PIDATA_TS piData[IVAS_PI_MAX_ID * IVAS_MAX_FRAMES_PER_RTP_PACKET];
+
+ IVAS_RTP_FILE_HANDLE hReader;
+ FILE *f_piDataOut;
+ IVAS_RTP_CODEC codecId;
+ uint32_t nReadPiData;
+ uint32_t nProcPiData;
+ uint32_t numFramesInPacket;
+ uint32_t numPiDataInPacket;
+ uint32_t remoteRequestBitmap;
+ bool speechLostIndicated;
+ bool restartNeeded;
+ bool isAMRWB_IOmode;
+ size_t numFramesProduced;
+
+ IVAS_DATA_BUFFER rtpPacket;
+ IVAS_RTP_UNPACK_HANDLE hUnpack;
+ IVAS_RTP_UNPACK_CONFIG unpackCfg;
+ PI_DATA_DEPACKER_STATE piDataDepackerState;
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ IVAS_RTP_SR_INFO srInfo;
+#endif
+} IVAS_RTP;
+
+static const char *PiDataNames[IVAS_PI_MAX_ID] = {
+ "SCENE_ORIENTATION", "DEVICE_ORIENTATION_COMPENSATED", "DEVICE_ORIENTATION_UNCOMPENSATED",
+ "ACOUSTIC_ENVIRONMENT", "AUDIO_DESCRIPTION", "ISM_NUM", "ISM_ID", "ISM_GAIN", "ISM_ORIENTATION",
+ "ISM_POSITION", "ISM_DISTANCE_ATTENUATION", "ISM_DIRECTIVITY", "DIEGETIC_TYPE", "RESERVED13",
+ "RESERVED14", "RESERVED15", "PLAYBACK_DEVICE_ORIENTATION", "HEAD_ORIENTATION", "LISTENER_POSITION",
+ "DYNAMIC_AUDIO_SUPPRESSION", "AUDIO_FOCUS_DIRECTION", "PI_LATENCY", "R_ISM_ID", "R_ISM_GAIN",
+ "R_ISM_ORIENTATION", "R_ISM_POSITION", "R_ISM_DIRECTION", "RESERVED27", "RESERVED28", "RESERVED29",
+ "RESERVED30", "NO_DATA"
+};
+
+
+static void IVAS_RTP_LogPiData( FILE *f_piDataOut, PIDATA_TS *piData, uint32_t nPiDataPresent )
+{
+ uint32_t timestamp = ~0u;
+ if ( f_piDataOut == NULL || piData == NULL || nPiDataPresent == 0 )
+ {
+ return;
+ }
+
+ if ( ftell( f_piDataOut ) > 2 )
+ {
+ fprintf( f_piDataOut, ",\n" );
+ }
+
+ while ( nPiDataPresent-- > 0 )
+ {
+ PIDATA_TS *cur = piData++;
+
+ if ( timestamp != ( ~0u ) && timestamp != cur->timestamp )
+ {
+ fprintf( f_piDataOut, "\n\t},\n\t\"%d\": {\n", cur->timestamp );
+ }
+ else if ( timestamp != cur->timestamp )
+ {
+ fprintf( f_piDataOut, "\t\"%d\": {\n", cur->timestamp );
+ }
+ else
+ {
+ fprintf( f_piDataOut, ",\n" );
+ }
+ fprintf( f_piDataOut, "\t\t\"%s\" : ", PiDataNames[cur->data.noPiData.piDataType] );
+ switch ( cur->data.noPiData.piDataType )
+ {
+ case IVAS_PI_SCENE_ORIENTATION:
+ case IVAS_PI_DEVICE_ORIENTATION_COMPENSATED:
+ case IVAS_PI_DEVICE_ORIENTATION_UNCOMPENSATED:
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ case IVAS_PI_PLAYBACK_DEVICE_ORIENTATION:
+ case IVAS_PI_HEAD_ORIENTATION:
+ case IVAS_PI_AUDIO_FOCUS_DIRECTION:
+ case IVAS_PI_R_ISM_ORIENTATION:
+#endif
+ {
+ fprintf( f_piDataOut, "{\n\t\t\t\"w\": %f,\n\t\t\t\"x\": %f,\n\t\t\t\"y\": %f,\n\t\t\t\"z\": %f \n\t\t}",
+ cur->data.scene.orientation.w, cur->data.scene.orientation.x, cur->data.scene.orientation.y, cur->data.scene.orientation.z );
+ }
+ break;
+
+ case IVAS_PI_ACOUSTIC_ENVIRONMENT:
+ {
+ fprintf( f_piDataOut, "{\n\t\t\t\"aeid\": %d", cur->data.acousticEnv.aeid );
+ if ( cur->data.acousticEnv.availLateReverb )
+ {
+ fprintf( f_piDataOut, ",\n\t\t\t\"rt60\": [ %f, %f, %f ],\n", cur->data.acousticEnv.rt60[0], cur->data.acousticEnv.rt60[1], cur->data.acousticEnv.rt60[2] );
+ fprintf( f_piDataOut, "\t\t\t\"dsr\": [ %f, %f, %f ]", cur->data.acousticEnv.dsr[0], cur->data.acousticEnv.dsr[1], cur->data.acousticEnv.dsr[2] );
+ }
+ if ( cur->data.acousticEnv.availEarlyReflections )
+ {
+ fprintf( f_piDataOut, ",\n\t\t\t\"dim\": [ %f, %f, %f ],\n", cur->data.acousticEnv.roomDimensions.x, cur->data.acousticEnv.roomDimensions.y, cur->data.acousticEnv.roomDimensions.z );
+ fprintf( f_piDataOut, "\t\t\t\"abscoeff\": [ %f, %f, %f, %f, %f, %f ]", cur->data.acousticEnv.absorbCoeffs[0], cur->data.acousticEnv.absorbCoeffs[1], cur->data.acousticEnv.absorbCoeffs[2], cur->data.acousticEnv.absorbCoeffs[3], cur->data.acousticEnv.absorbCoeffs[4], cur->data.acousticEnv.absorbCoeffs[5] );
+ }
+ fprintf( f_piDataOut, "\n\t\t}" );
+ }
+ break;
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ case IVAS_PI_LISTENER_POSITION:
+ case IVAS_PI_R_ISM_POSITION:
+ {
+ fprintf( f_piDataOut, "{\n\t\t\t\"x\": %f,\n\t\t\t\"y\": %f,\n\t\t\t\"z\": %f \n\t\t}",
+ cur->data.listnerPosition.position.x, cur->data.listnerPosition.position.y, cur->data.listnerPosition.position.z );
+ }
+ break;
+ case IVAS_PI_AUDIO_DESCRIPTION:
+ {
+ uint32_t nEntries = cur->data.audioDesc.nValidEntries;
+ IVAS_AUDIO_ID *audioId = cur->data.audioDesc.audioId;
+
+ fprintf( f_piDataOut, "[\n" );
+ while ( nEntries-- > 0 )
+ {
+ fprintf( f_piDataOut, "\t\t\t{\n" );
+ fprintf( f_piDataOut, "\t\t\t\t\"isSpeech\": %s,\n", audioId->speech ? "true" : "false" );
+ fprintf( f_piDataOut, "\t\t\t\t\"isMusic\": %s,\n", audioId->music ? "true" : "false" );
+ fprintf( f_piDataOut, "\t\t\t\t\"isAmbiance\": %s,\n", audioId->ambiance ? "true" : "false" );
+ fprintf( f_piDataOut, "\t\t\t\t\"isEditable\": %s,\n", audioId->editable ? "true" : "false" );
+ fprintf( f_piDataOut, "\t\t\t\t\"isBinaural\": %s\n", audioId->binaural ? "true" : "false" );
+ fprintf( f_piDataOut, "\t\t\t}%c\n", ( nEntries == 0 ) ? ' ' : ',' );
+ audioId++;
+ }
+ fprintf( f_piDataOut, "\t\t]" );
+ }
+ break;
+ case IVAS_PI_DIEGETIC_TYPE:
+ {
+ bool *isDiegetic = cur->data.digeticIndicator.isDiegetic;
+ fprintf( f_piDataOut, "{\n" );
+ fprintf( f_piDataOut, "\t\t\t\"isDigetic\": [\n" );
+ fprintf( f_piDataOut, "\t\t\t\t%s,\n", isDiegetic[0] ? "true" : "false" );
+ fprintf( f_piDataOut, "\t\t\t\t%s,\n", isDiegetic[1] ? "true" : "false" );
+ fprintf( f_piDataOut, "\t\t\t\t%s,\n", isDiegetic[2] ? "true" : "false" );
+ fprintf( f_piDataOut, "\t\t\t\t%s,\n", isDiegetic[3] ? "true" : "false" );
+ fprintf( f_piDataOut, "\t\t\t\t%s\n", isDiegetic[4] ? "true" : "false" );
+ fprintf( f_piDataOut, "\t\t\t]\n\t\t}" );
+ }
+ break;
+ case IVAS_PI_DYNAMIC_AUDIO_SUPPRESSION:
+ {
+ IVAS_PIDATA_DYNAMIC_SUPPRESSION *das = &cur->data.dynSuppression;
+ fprintf( f_piDataOut, "{\n" );
+ fprintf( f_piDataOut, "\t\t\t\"preferSpeech\": %s,\n", das->speech ? "true" : "false" );
+ fprintf( f_piDataOut, "\t\t\t\"preferMusic\": %s,\n", das->music ? "true" : "false" );
+ fprintf( f_piDataOut, "\t\t\t\"preferAmbiance\": %s,\n", das->ambiance ? "true" : "false" );
+ fprintf( f_piDataOut, "\t\t\t\"level\": %d", das->sli );
+ fprintf( f_piDataOut, "\n\t\t}" );
+ }
+ break;
+ case IVAS_PI_RESERVED13:
+ case IVAS_PI_RESERVED14:
+ case IVAS_PI_RESERVED15:
+ case IVAS_PI_RESERVED27:
+ case IVAS_PI_RESERVED28:
+ case IVAS_PI_RESERVED29:
+ case IVAS_PI_RESERVED30:
+ {
+ fprintf( f_piDataOut, "{}" );
+ }
+ break;
+ case IVAS_PI_ISM_NUM:
+ case IVAS_PI_ISM_ID:
+ case IVAS_PI_ISM_GAIN:
+ case IVAS_PI_ISM_ORIENTATION:
+ case IVAS_PI_ISM_POSITION:
+ case IVAS_PI_ISM_DISTANCE_ATTENUATION:
+ case IVAS_PI_ISM_DIRECTIVITY:
+ case IVAS_PI_PI_LATENCY:
+ case IVAS_PI_R_ISM_ID:
+ case IVAS_PI_R_ISM_GAIN:
+ case IVAS_PI_R_ISM_DIRECTION:
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+ case IVAS_PI_NO_DATA:
+ {
+ fprintf( f_piDataOut, "{}" );
+ }
+ break;
+ }
+ timestamp = cur->timestamp;
+ }
+ fprintf( f_piDataOut, "\n\t}" );
+}
+
+static void IVAS_RTP_Term( IVAS_RTP *rtp )
+{
+ if ( NULL != rtp )
+ {
+ IvasRtpFile_Close( &rtp->hReader );
+ if ( rtp->f_piDataOut != NULL )
+ {
+ fprintf( rtp->f_piDataOut, "\n}\n" );
+ fclose( rtp->f_piDataOut );
+ rtp->f_piDataOut = NULL;
+ }
+ IVAS_RTP_UNPACK_Close( &rtp->hUnpack );
+ }
+}
+
+static ivas_error IVAS_RTP_Init( IVAS_RTP *rtp, const char *inputBitstreamFilename, const char *piOutputFilename )
+{
+ ivas_error error = IVAS_ERR_OK;
+
+ memset( rtp, 0, sizeof( IVAS_RTP ) );
+
+ rtp->unpackCfg.maxFramesPerPacket = IVAS_MAX_FRAMES_PER_RTP_PACKET;
+ rtp->rtpPacket.buffer = rtp->packet;
+ rtp->rtpPacket.capacity = sizeof( rtp->packet );
+
+ error = IVAS_RTP_UNPACK_Open( &rtp->hUnpack, &rtp->unpackCfg );
+ if ( error == IVAS_ERR_OK )
+ {
+ error = IvasRtpFile_Open( inputBitstreamFilename, false, &rtp->hReader );
+ if ( error != IVAS_ERR_OK )
+ {
+ return error;
+ }
+
+ if ( piOutputFilename != NULL )
+ {
+ rtp->f_piDataOut = fopen( piOutputFilename, "w" );
+ if ( rtp->f_piDataOut == NULL )
+ {
+ fprintf( stderr, "could not open: %s\n", piOutputFilename );
+ return IVAS_ERR_FAILED_FILE_OPEN;
+ }
+ fprintf( rtp->f_piDataOut, "{\n" );
+ }
+ }
+
+ return error;
+}
+
+static ivas_error IVAS_RTP_ReadNextFrame( IVAS_RTP *rtp, uint8_t *au, int16_t *auSizeBits, uint32_t *rtpTimeStamp, uint16_t *rtpSequenceNumber, uint32_t *nextPacketRcvTime_ms, bool *qBit )
+{
+ ivas_error error = IVAS_ERR_OK;
+ IVAS_DATA_BUFFER packedFrame;
+ IVAS_RTP_CODEC codecId = IVAS_RTP_IVAS;
+ bool isAMRWB_IOmode = false;
+
+ packedFrame.length = 0;
+ packedFrame.buffer = au;
+ packedFrame.capacity = ( IVAS_MAX_BITS_PER_FRAME / 8 );
+ if ( rtp->numFramesInPacket == 0 )
+ {
+ rtp->rtpPacket.length = 0;
+ if ( ( error = IvasRtpFile_Read( rtp->hReader, rtp->rtpPacket.buffer, &rtp->rtpPacket.length, rtp->rtpPacket.capacity ) ) != IVAS_ERR_OK )
+ {
+ return error;
+ }
+
+ if ( ( error = IVAS_RTP_UNPACK_PushPacket( rtp->hUnpack, &rtp->rtpPacket,
+ &rtp->numFramesInPacket, &rtp->numPiDataInPacket,
+ &rtp->remoteRequestBitmap ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "failed to unpack RTP packet error = %s\n", ivas_error_to_string( error ) );
+ return error;
+ }
+
+ rtp->nReadPiData = 0;
+ rtp->nProcPiData = 0;
+
+ /* Pre-read all PI data */
+ while ( rtp->numPiDataInPacket != 0 )
+ {
+ PIDATA_TS *piData = &rtp->piData[rtp->nReadPiData];
+ if ( ( error = IVAS_RTP_UNPACK_PullNextPiData( rtp->hUnpack, (IVAS_PIDATA_GENERIC *) &piData->data, sizeof( piData->data ), &piData->timestamp ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "failed to pull PI Data, error = %s\n", ivas_error_to_string( error ) );
+ return error;
+ }
+ rtp->nReadPiData++;
+ rtp->numPiDataInPacket--;
+ }
+ IVAS_RTP_LogPiData( rtp->f_piDataOut, rtp->piData, rtp->nReadPiData );
+ }
+
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ error = IVAS_RTP_UNPACK_PullFrame( rtp->hUnpack, &codecId, &rtp->srInfo, &packedFrame, auSizeBits, rtpTimeStamp, rtpSequenceNumber, &rtp->speechLostIndicated, &isAMRWB_IOmode );
+#else
+ error = IVAS_RTP_UNPACK_PullFrame( rtp->hUnpack, &codecId, &packedFrame, auSizeBits, rtpTimeStamp, rtpSequenceNumber, &rtp->speechLostIndicated, &isAMRWB_IOmode );
+#endif
+ if ( error != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "failed to pull frame after unpack\n" );
+ return error;
+ }
+
+ rtp->restartNeeded = false;
+ if ( *auSizeBits == 0 )
+ {
+ /* NO_DATA_FRAME/SPEECH_LOST for IVAS and EVS is indicated by same bits
+ Do not restart decoder on codec/amrwb mode change in this case */
+ }
+ else
+ {
+ rtp->restartNeeded = ( rtp->codecId != codecId ) ||
+ ( codecId == IVAS_RTP_EVS && ( rtp->isAMRWB_IOmode != isAMRWB_IOmode ) );
+
+ if ( rtp->restartNeeded )
+ {
+ fprintf( stdout, "\nRTP packet codec changed %s -> %s\n",
+ ( rtp->codecId == IVAS_RTP_EVS ) ? ( rtp->isAMRWB_IOmode ? "AMRWB_IO" : "EVS" ) : "IVAS",
+ ( codecId == IVAS_RTP_EVS ) ? ( isAMRWB_IOmode ? "AMRWB_IO" : "EVS" ) : "IVAS" );
+ }
+
+ rtp->codecId = codecId;
+ rtp->isAMRWB_IOmode = isAMRWB_IOmode;
+ }
+
+ *qBit = !rtp->speechLostIndicated;
+ rtp->numFramesInPacket--;
+ rtp->numFramesProduced++;
+ *nextPacketRcvTime_ms += 20;
+
+ return IVAS_ERR_OK;
+}
+
+static ivas_error IVAS_RTP_ApplyPiData( IVAS_RTP *rtp, IVAS_DEC_HANDLE hIvasDec, uint32_t rtpTimeStamp )
+{
+ ivas_error error = IVAS_ERR_OK;
+ while ( rtp->nProcPiData < rtp->nReadPiData &&
+ rtp->piData[rtp->nProcPiData].timestamp <= rtpTimeStamp )
+ {
+ PIDATA_TS *piData = &rtp->piData[rtp->nProcPiData++];
+ if ( hIvasDec )
+ {
+ uint32_t piDataType = ( (IVAS_PIDATA_GENERIC *) &piData->data )->piDataType;
+ switch ( piDataType )
+ {
+ case IVAS_PI_SCENE_ORIENTATION:
+ {
+ IVAS_QUATERNION *quat = &piData->data.scene.orientation;
+ DEBUG_PRINT( stdout, "PI_SCENE_ORIENTATION : %f, %f, %f, %f\n", quat->w, quat->x, quat->y, quat->z );
+ error = IVAS_DEC_feedSinglePIorientation( hIvasDec, true, quat );
+ }
+ break;
+
+ case IVAS_PI_DEVICE_ORIENTATION_COMPENSATED:
+ {
+ IVAS_QUATERNION *quat = &piData->data.deviceCompensated.orientation;
+ DEBUG_PRINT( stdout, "PI_DEVICE_ORIENTATION : %f, %f, %f, %f\n", quat->w, quat->x, quat->y, quat->z );
+ error = IVAS_DEC_feedSinglePIorientation( hIvasDec, true, quat );
+ }
+ break;
+
+ default:
+ {
+ fprintf( stderr, "Unhandled PI data of type : %s\n", PiDataNames[piDataType] );
+ }
+ break;
+ }
+ if ( error != IVAS_ERR_OK )
+ {
+ return error;
+ }
+ }
+ }
+ return error;
+}
+#endif /* IVAS_RTPDUMP */
/*------------------------------------------------------------------------------------------*
* Local functions prototypes
@@ -171,7 +557,6 @@ typedef struct
static bool parseCmdlIVAS_dec( int16_t argc, char **argv, DecArguments *arg );
static void usage_dec( void );
static ivas_error decodeG192( DecArguments arg, BS_READER_HANDLE hBsReader, IVAS_DEC_HRTF_BINARY_WRAPPER *hHrtf, RotFileReader *headRotReader, RotFileReader *externalOrientationFileReader, RotFileReader *refRotReader, Vector3PairFileReader *referenceVectorReader, ObjectEditFileReader *objectEditFileReader, ISAR_SPLIT_REND_BITS_DATA *splitRendBits, IVAS_DEC_HANDLE hIvasDec, int16_t *pcmBuf );
-static ivas_error decodeVoIP( DecArguments arg, BS_READER_HANDLE hBsReader, IVAS_DEC_HRTF_BINARY_WRAPPER *hHrtf, RotFileReader *headRotReader, RotFileReader *externalOrientationFileReader, RotFileReader *refRotReader, Vector3PairFileReader *referenceVectorReader, ObjectEditFileReader *objectEditFileReader, IVAS_DEC_HANDLE hIvasDec, int16_t *pcmBuf );
static ivas_error load_hrtf_from_file( IVAS_DEC_HRTF_BINARY_WRAPPER *hHrtfBinary, IVAS_DEC_HANDLE hIvasDec, const IVAS_AUDIO_CONFIG OutputConfig, const int32_t output_Fs );
#ifdef DEBUGGING
static ivas_error printBitstreamInfoVoip( DecArguments arg, BS_READER_HANDLE hBsReader, IVAS_DEC_HANDLE hIvasDec );
@@ -180,6 +565,116 @@ static IVAS_DEC_FORCED_REND_MODE parseForcedRendModeDec( char *forcedRendModeCha
#endif
static void do_object_editing( IVAS_EDITABLE_PARAMETERS *editableParameters, ObjectEditFileReader *objectEditFileReader );
+#ifdef IVAS_RTPDUMP
+static ivas_error decodeVoIP( DecArguments arg, BS_READER_HANDLE hBsReader, IVAS_DEC_HRTF_BINARY_WRAPPER *hHrtf, RotFileReader *headRotReader, RotFileReader *externalOrientationFileReader, RotFileReader *refRotReader, Vector3PairFileReader *referenceVectorReader, ObjectEditFileReader *objectEditFileReader, IVAS_DEC_HANDLE *phIvasDec, int16_t *pcmBuf );
+
+static ivas_error restartDecoder(
+ IVAS_DEC_HANDLE *phIvasDec,
+ IVAS_DEC_MODE codec,
+ DecArguments *arg,
+ IVAS_RENDER_CONFIG_DATA *renderConfig,
+ IVAS_CUSTOM_LS_DATA *hLsCustomData )
+{
+ ivas_error error = IVAS_ERR_OK;
+ IVAS_DEC_HANDLE hIvasDec;
+
+ if ( phIvasDec == NULL )
+ {
+ return IVAS_ERR_UNEXPECTED_NULL_POINTER;
+ }
+
+ if ( NULL != *phIvasDec )
+ {
+ IVAS_DEC_Close( phIvasDec );
+ }
+
+ if ( ( error = IVAS_DEC_Open( phIvasDec, codec ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "Open failed: %s\n", IVAS_DEC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+
+ arg->decMode = codec;
+
+ hIvasDec = *phIvasDec;
+
+ uint16_t aeID = arg->aeSequence.count > 0 ? arg->aeSequence.pID[0] : 65535;
+
+ IVAS_AUDIO_CONFIG outputConfig = ( codec == IVAS_DEC_MODE_IVAS ) ? arg->outputConfig : IVAS_AUDIO_CONFIG_MONO;
+ if ( ( error = IVAS_DEC_Configure( hIvasDec, arg->output_Fs, outputConfig, arg->renderFramesize, arg->customLsOutputEnabled, arg->hrtfReaderEnabled,
+ arg->enableHeadRotation, arg->enableExternalOrientation, arg->orientation_tracking, arg->renderConfigEnabled, arg->non_diegetic_pan_enabled,
+ arg->non_diegetic_pan_gain, arg->dpidEnabled, aeID, arg->objEditEnabled, arg->delayCompensationEnabled ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nConfigure failed: %s\n\n", IVAS_DEC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+
+ if ( ( error = IVAS_DEC_GetRenderFramesize( hIvasDec, &arg->renderFramesize ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nConfigure failed: %s\n\n", IVAS_DEC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+
+ if ( arg->outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED || arg->outputConfig == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM )
+ {
+ if ( ( error = IVAS_DEC_EnableSplitRendering( hIvasDec ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nSplit rendering configure failed: %s\n\n", IVAS_DEC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+
+ if ( ( error = IVAS_DEC_GetRenderFramesize( hIvasDec, &arg->renderFramesize ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nConfigure failed: %s\n\n", IVAS_DEC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+
+ arg->enableHeadRotation = true;
+ }
+
+ if ( arg->voipMode )
+ {
+ if ( ( error = IVAS_DEC_EnableVoIP( hIvasDec, 60, arg->inputFormat ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nCould not enable VOIP: %s\n\n", IVAS_DEC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+ }
+
+ if ( ( error = IVAS_DEC_PrintConfig( hIvasDec, 1, arg->voipMode ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nIVAS_DEC_PrintConfig failed: %s\n\n", IVAS_DEC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+
+ if ( arg->renderConfigEnabled && renderConfig != NULL )
+ {
+ if ( ( error = IVAS_DEC_FeedRenderConfig( hIvasDec, *renderConfig ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nIVAS_DEC_FeedRenderConfig failed: %s\n\n", IVAS_DEC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+ }
+
+ if ( arg->customLsOutputEnabled && hLsCustomData != NULL )
+ {
+ if ( ( error = IVAS_DEC_FeedCustomLsData( hIvasDec, *hLsCustomData ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nIVAS_DEC_FeedCustomLsData failed: %s\n", IVAS_DEC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+ }
+
+ return IVAS_ERR_OK;
+
+cleanup:
+ IVAS_DEC_Close( phIvasDec );
+ return error;
+}
+#else
+static ivas_error decodeVoIP( DecArguments arg, BS_READER_HANDLE hBsReader, IVAS_DEC_HRTF_BINARY_WRAPPER *hHrtf, RotFileReader *headRotReader, RotFileReader *externalOrientationFileReader, RotFileReader *refRotReader, Vector3PairFileReader *referenceVectorReader, ObjectEditFileReader *objectEditFileReader, IVAS_DEC_HANDLE hIvasDec, int16_t *pcmBuf );
+#endif
+
/*------------------------------------------------------------------------------------------*
* main()
*
@@ -752,7 +1247,11 @@ int main(
if ( arg.voipMode )
{
+#ifdef IVAS_RTPDUMP
+ error = decodeVoIP( arg, hBsReader, &hHrtfBinary, headRotReader, externalOrientationFileReader, refRotReader, referenceVectorReader, objectEditFileReader, &hIvasDec, pcmBuf );
+#else
error = decodeVoIP( arg, hBsReader, &hHrtfBinary, headRotReader, externalOrientationFileReader, refRotReader, referenceVectorReader, objectEditFileReader, hIvasDec, pcmBuf );
+#endif
}
else
{
@@ -979,6 +1478,10 @@ static bool parseCmdlIVAS_dec(
arg->referenceVectorTrajFileName = NULL;
arg->enableExternalOrientation = false;
arg->externalOrientationTrajFileName = NULL;
+#ifdef IVAS_RTPDUMP
+ arg->applyPiData = false;
+ arg->piOutputFilename = NULL;
+#endif
#ifdef SUPPORT_JBM_TRACEFILE
arg->jbmTraceFilename = NULL;
@@ -1065,6 +1568,25 @@ static bool parseCmdlIVAS_dec(
arg->inputFormat = IVAS_DEC_INPUT_FORMAT_RTPDUMP_HF;
i++;
}
+#ifdef IVAS_RTPDUMP
+ else if ( strcmp( argv_to_upper, "-PIDATAFILE" ) == 0 )
+ {
+ i++;
+ if ( argc - i <= 3 || argv[i][0] == '-' )
+ {
+ fprintf( stderr, "Error: PI Data Output file name not specified!\n\n" );
+ usage_dec();
+ return false;
+ }
+
+ arg->piOutputFilename = argv[i++];
+ }
+ else if ( strcmp( argv_to_upper, "-APPLYPIDATA" ) == 0 )
+ {
+ arg->applyPiData = true;
+ i++;
+ }
+#endif
#ifdef SUPPORT_JBM_TRACEFILE
else if ( strcmp( argv_to_upper, "-TRACEFILE" ) == 0 )
{
@@ -1660,10 +2182,20 @@ static void usage_dec( void )
fprintf( stdout, "--------\n" );
fprintf( stdout, "-VOIP : VoIP mode: RTP in G192\n" );
fprintf( stdout, "-VOIP_hf_only=0 : VoIP mode: EVS RTP Payload Format hf_only=0 in rtpdump\n" );
+#ifdef IVAS_RTPDUMP
+ fprintf( stdout, "-VOIP_hf_only=1 : VoIP mode: EVS or IVAS RTP Payload Format hf_only=1 in rtpdump\n" );
+ fprintf( stdout, " The decoder may read rtpdump files containing TS26.445 Annex A.2.2\n" );
+ fprintf( stdout, " EVS RTP Payload Format or rtpdump files containing TS26.253 Annex A\n" );
+ fprintf( stdout, " IVAS RTP Payload Format. The SDP parameter hf_only is required.\n" );
+ fprintf( stdout, " Reading RFC4867 AMR/AMR-WB RTP payload format is not supported.\n" );
+ fprintf( stdout, "-PiDataFile PF Log the timestampped PI data.\n" );
+ fprintf( stdout, "-ApplyPiData Apply the PI data found in the rtp packet.\n" );
+#else
fprintf( stdout, "-VOIP_hf_only=1 : VoIP mode: EVS RTP Payload Format hf_only=1 in rtpdump\n" );
fprintf( stdout, " The decoder may read rtpdump files containing TS26.445 Annex A.2.2\n" );
fprintf( stdout, " EVS RTP Payload Format. The SDP parameter hf_only is required.\n" );
fprintf( stdout, " Reading RFC4867 AMR/AMR-WB RTP payload format is not supported.\n" );
+#endif
#ifdef SUPPORT_JBM_TRACEFILE
fprintf( stdout, "-Tracefile TF : VoIP mode: Generate trace file named TF. Requires -no_delay_cmp to\n" );
fprintf( stdout, " be enabled so that trace contents remain in sync with audio output.\n" );
@@ -2882,24 +3414,37 @@ static ivas_error printBitstreamInfoVoip(
{
bool previewFailed = true;
ivas_error error = IVAS_ERR_OK;
+#ifdef IVAS_RTPDUMP
+ IVAS_RTP ivasRtp;
+#else
FILE *f_rtpstream = NULL;
EVS_RTPDUMP_DEPACKER rtpdumpDepacker;
EVS_RTPDUMP_DEPACKER_ERROR rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_NO_ERROR;
+ bool isAMRWB_IOmode;
+ uint16_t frameTypeIndex;
+#endif
uint8_t au[( IVAS_MAX_BITS_PER_FRAME + 7 ) >> 3];
int16_t auSizeBits;
uint8_t *auPtr = NULL;
- bool isAMRWB_IOmode;
- uint16_t frameTypeIndex;
bool qBit;
uint32_t nextPacketRcvTime_ms = 0;
uint16_t rtpSequenceNumber;
uint32_t rtpTimeStamp;
+#ifndef IVAS_RTPDUMP
rtpdumpDepacker.rtpdump = NULL;
+#endif
switch ( arg.inputFormat )
{
case IVAS_DEC_INPUT_FORMAT_RTPDUMP:
case IVAS_DEC_INPUT_FORMAT_RTPDUMP_HF:
+#ifdef IVAS_RTPDUMP
+ if ( ( error = IVAS_RTP_Init( &ivasRtp, arg.inputBitstreamFilename, NULL ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "error in IVAS_RTP_Init(): %d\n", error );
+ goto cleanup;
+ }
+#else
f_rtpstream = fopen( arg.inputBitstreamFilename, "r" );
if ( f_rtpstream == NULL )
@@ -2914,6 +3459,7 @@ static ivas_error printBitstreamInfoVoip(
fprintf( stderr, "error in EVS_RTPDUMP_DEPACKER_open(): %d\n", rtpdumpDepackerError );
goto cleanup;
}
+#endif
break;
case IVAS_DEC_INPUT_FORMAT_G192:
auPtr = au;
@@ -2934,12 +3480,20 @@ static ivas_error printBitstreamInfoVoip(
else
{
auPtr = au; /* might have been set to RTP packet in prev call */
+#ifdef IVAS_RTPDUMP
+ error = readNextFrame( &ivasRtp, auPtr, &auSizeBits, &rtpTimeStamp, &rtpSequenceNumber, &nextPacketRcvTime_ms, &qBit );
+#else
rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, &isAMRWB_IOmode, &frameTypeIndex, &qBit, &auPtr, (uint16_t *) &auSizeBits );
+#endif
/* EVS RTP payload format has timescale 16000, JBM uses 1000 internally */
rtpTimeStamp = rtpTimeStamp / 16;
}
+#ifdef IVAS_RTPDUMP
+ if ( error != IVAS_ERR_OK )
+#else
if ( error != IVAS_ERR_OK || rtpdumpDepackerError != EVS_RTPDUMP_DEPACKER_NO_ERROR )
+#endif
{
fprintf( stderr, "failed to read first RTP packet\n" );
goto cleanup;
@@ -2958,7 +3512,11 @@ static ivas_error printBitstreamInfoVoip(
cleanup:
+#ifdef IVAS_RTPDUMP
+ IVAS_RTP_Term( &ivasRtp );
+#else
EVS_RTPDUMP_DEPACKER_close( &rtpdumpDepacker );
+#endif
if ( previewFailed && error == IVAS_ERR_OK )
{
@@ -2992,7 +3550,11 @@ static ivas_error decodeVoIP(
RotFileReader *refRotReader,
Vector3PairFileReader *referenceVectorReader,
ObjectEditFileReader *objectEditFileReader,
+#ifdef IVAS_RTPDUMP
+ IVAS_DEC_HANDLE *phIvasDec,
+#else
IVAS_DEC_HANDLE hIvasDec,
+#endif
int16_t *pcmBuf )
{
bool decodingFailed = true; /* Assume failure until cleanup is reached without errors */
@@ -3024,12 +3586,20 @@ static ivas_error decodeVoIP(
int16_t delayNumSamples = -1;
int32_t delayTimeScale = -1;
int16_t i;
+#ifdef IVAS_RTPDUMP
+ IVAS_RTP ivasRtp = { 0 };
+ IVAS_DEC_HANDLE hIvasDec = *phIvasDec;
+ int32_t initialTsOffsetSystemAndRTP = 0;
+#else
FILE *f_rtpstream = NULL;
EVS_RTPDUMP_DEPACKER rtpdumpDepacker;
EVS_RTPDUMP_DEPACKER_ERROR rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_NO_ERROR;
+#endif
uint8_t *auPtr = NULL;
+#ifndef IVAS_RTPDUMP
bool isAMRWB_IOmode;
uint16_t frameTypeIndex;
+#endif
bool qBit;
IVAS_DEC_BS_FORMAT bsFormat = IVAS_DEC_BS_UNKOWN;
@@ -3067,13 +3637,21 @@ static ivas_error decodeVoIP(
delayNumSamples_orig[0] = -1;
+#ifndef IVAS_RTPDUMP
rtpdumpDepacker.rtpdump = NULL;
+#endif
switch ( arg.inputFormat )
{
case IVAS_DEC_INPUT_FORMAT_RTPDUMP:
case IVAS_DEC_INPUT_FORMAT_RTPDUMP_HF:
+#ifdef IVAS_RTPDUMP
+ if ( ( error = IVAS_RTP_Init( &ivasRtp, arg.inputBitstreamFilename, arg.piOutputFilename ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "error in IVAS_RTP_Init(): %d\n", error );
+ goto cleanup;
+ }
+#else
f_rtpstream = fopen( arg.inputBitstreamFilename, "r" );
-
if ( f_rtpstream == NULL )
{
fprintf( stderr, "could not open: %s\n", arg.inputBitstreamFilename );
@@ -3086,6 +3664,7 @@ static ivas_error decodeVoIP(
fprintf( stderr, "error in EVS_RTPDUMP_DEPACKER_open(): %d\n", rtpdumpDepackerError );
goto cleanup;
}
+#endif
break;
case IVAS_DEC_INPUT_FORMAT_G192:
auPtr = au;
@@ -3127,12 +3706,21 @@ static ivas_error decodeVoIP(
else
{
auPtr = au; /* might have been set to RTP packet in prev call */
+#ifdef IVAS_RTPDUMP
+ error = IVAS_RTP_ReadNextFrame( &ivasRtp, auPtr, &auSize, &rtpTimeStamp, &rtpSequenceNumber, &nextPacketRcvTime_ms, &qBit );
+ initialTsOffsetSystemAndRTP = rtpTimeStamp - systemTime_ms * 16; /* For time mapping */
+#else
rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, &isAMRWB_IOmode, &frameTypeIndex, &qBit, &auPtr, (uint16_t *) &auSize );
+#endif
/* EVS RTP payload format has timescale 16000, JBM uses 1000 internally */
rtpTimeStamp = rtpTimeStamp / 16;
}
+#ifdef IVAS_RTPDUMP
+ if ( error != IVAS_ERR_OK )
+#else
if ( error != IVAS_ERR_OK || rtpdumpDepackerError != EVS_RTPDUMP_DEPACKER_NO_ERROR )
+#endif
{
fprintf( stderr, "failed to read first RTP packet\n" );
goto cleanup;
@@ -3161,6 +3749,27 @@ static ivas_error decodeVoIP(
{
nSamplesRendered = 0;
+#ifdef IVAS_RTPDUMP
+ if ( ivasRtp.restartNeeded )
+ {
+ IVAS_DEC_MODE newCodecInPacket = ( ivasRtp.codecId == IVAS_RTP_EVS ) ? IVAS_DEC_MODE_EVS : IVAS_DEC_MODE_IVAS;
+ error = restartDecoder(
+ &hIvasDec,
+ newCodecInPacket,
+ &arg,
+ NULL, /* ToDo : Provide rendererConfig */
+ NULL /* ToDo : Provide LS Custom Data */
+ );
+ if ( error != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nFailed to restart decoder from %d to %d\n", arg.decMode, newCodecInPacket );
+ goto cleanup;
+ }
+ *phIvasDec = hIvasDec; /* Update for main()' s free */
+ ivasRtp.restartNeeded = false;
+ }
+#endif
+
/* reference vector */
if ( arg.enableReferenceVectorTracking && vec_pos_update == 0 )
{
@@ -3302,19 +3911,34 @@ static ivas_error decodeVoIP(
else
{
auPtr = au; /* might have been set to RTP packet in prev call */
+#ifdef IVAS_RTPDUMP
+ error = IVAS_RTP_ReadNextFrame( &ivasRtp, au, &auSize, &rtpTimeStamp, &rtpSequenceNumber, &nextPacketRcvTime_ms, &qBit );
+
+ /* IVAS RTP payload format has timescale 16000, JBM uses 1000 internally */
+ rtpTimeStamp = rtpTimeStamp / 16;
+#else
rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp,
&nextPacketRcvTime_ms,
&isAMRWB_IOmode, &frameTypeIndex, &qBit,
&auPtr, (uint16_t *) &auSize );
/* EVS RTP payload format has timescale 16000, JBM uses 1000 internally */
rtpTimeStamp = rtpTimeStamp / 16;
+#endif
}
+#ifdef IVAS_RTPDUMP
+ if ( error == IVAS_ERR_END_OF_FILE )
+#else
if ( error == IVAS_ERR_END_OF_FILE || rtpdumpDepackerError == EVS_RTPDUMP_DEPACKER_EOF )
+#endif
{
/* finished reading */
nextPacketRcvTime_ms = (uint32_t) -1;
}
+#ifdef IVAS_RTPDUMP
+ else if ( error != IVAS_ERR_OK )
+#else
else if ( error != IVAS_ERR_OK || rtpdumpDepackerError != EVS_RTPDUMP_DEPACKER_NO_ERROR )
+#endif
{
fprintf( stderr, "\nError in BS_Reader_ReadVoipFrame_compact, error code: %d\n", error );
goto cleanup;
@@ -3333,6 +3957,18 @@ static ivas_error decodeVoIP(
/* decode and get samples */
while ( nSamplesRendered < nOutSamples )
{
+#ifdef IVAS_RTPDUMP
+ if ( arg.applyPiData )
+ {
+ /* Rudimentry Time Mapping to map system time to rtp timestamp */
+ uint32_t piTs = systemTime_ms * 16 + initialTsOffsetSystemAndRTP;
+ if ( ( error = IVAS_RTP_ApplyPiData( &ivasRtp, hIvasDec, piTs ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nError in IVAS_DEC_VoIP_GetSamples: %s\n", IVAS_DEC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+ }
+#endif
#ifdef SUPPORT_JBM_TRACEFILE
if ( ( error = IVAS_DEC_VoIP_GetSamples( hIvasDec, nOutSamples, IVAS_DEC_PCM_INT16, (void *) pcmBuf, writeJbmTraceFileFrameWrapper, jbmTraceWriter, &bitstreamReadDone, &nSamplesRendered, ¶metersAvailableForEditing, systemTime_ms ) ) != IVAS_ERR_OK )
#else
@@ -3509,6 +4145,10 @@ static ivas_error decodeVoIP(
}
}
+#ifdef IVAS_RTPDUMP
+ IVAS_DEC_resetExternalOrientations( hIvasDec );
+#endif
+
vec_pos_update = ( vec_pos_update + 1 ) % vec_pos_len;
if ( vec_pos_update == 0 )
{
@@ -3657,7 +4297,11 @@ static ivas_error decodeVoIP(
cleanup:
+#ifdef IVAS_RTPDUMP
+ IVAS_RTP_Term( &ivasRtp );
+#else
EVS_RTPDUMP_DEPACKER_close( &rtpdumpDepacker );
+#endif
AudioFileWriter_close( &afWriter );
JbmOffsetFileWriter_close( &jbmOffsetWriter );
#ifdef SUPPORT_JBM_TRACEFILE
diff --git a/apps/encoder.c b/apps/encoder.c
index 61bb8ebb3f0d24c4835755b0b69ced48636add72..147512afc994a4504eeec140416468d11319b60a 100644
--- a/apps/encoder.c
+++ b/apps/encoder.c
@@ -39,12 +39,17 @@
#include "ism_file_reader.h"
#include "jbm_file_reader.h"
#include "masa_file_reader.h"
+#ifdef IVAS_RTPDUMP
+#include "ivas_rtp_api.h"
+#include "ivas_rtp_pi_data.h"
+#include "rotation_file_reader.h"
+#include "ivas_rtp_file.h"
+#endif
#ifdef DEBUGGING
#include "debug.h"
#endif
#include "wmc_auto.h"
-
#define WMC_TOOL_SKIP
/*------------------------------------------------------------------------------------------*
@@ -147,6 +152,12 @@ typedef struct
#endif
bool pca;
bool ism_extended_metadata;
+#ifdef IVAS_RTPDUMP
+ bool rtpdumpOutput;
+ uint32_t numFramesPerPacket;
+ char *sceneOrientationTrajFileName;
+ char *deviceOrientationTrajFileName;
+#endif
} EncArguments;
@@ -190,6 +201,10 @@ int main(
MasaFileReader *masaReader = NULL;
IsmFileReader *ismReaders[IVAS_MAX_NUM_OBJECTS] = { NULL, NULL, NULL, NULL };
int16_t *pcmBuf = NULL;
+#ifdef IVAS_RTPDUMP
+ RotFileReader *sceneOrientationFileReader = NULL;
+ RotFileReader *deviceOrientationFileReader = NULL;
+#endif
#ifdef DEBUGGING
FILE *f_forcedModeProfile = NULL;
#ifdef DEBUG_SBA
@@ -207,6 +222,22 @@ int main(
reset_mem( USE_BYTES );
#endif
+#ifdef IVAS_RTPDUMP
+ IVAS_RTP_FILE_HANDLE hWriter = NULL;
+ IVAS_RTP_PACK_HANDLE hPack = NULL;
+
+ uint8_t au[IVAS_MAX_BITS_PER_FRAME / 8];
+ uint8_t packet[NOMINAL_BUFFER_SIZE( IVAS_MAX_FRAMES_PER_RTP_PACKET )];
+ IVAS_DATA_BUFFER packedFrame = { 0, 0, NULL };
+ IVAS_DATA_BUFFER rtpPacket = { 0, 0, NULL };
+ uint32_t numFramesInPayload = 0;
+
+ packedFrame.capacity = sizeof( au );
+ packedFrame.buffer = au;
+ rtpPacket.capacity = sizeof( packet );
+ rtpPacket.buffer = packet;
+#endif
+
/*------------------------------------------------------------------------------------------*
* Parse command-line arguments
*------------------------------------------------------------------------------------------*/
@@ -235,7 +266,11 @@ int main(
const BS_WRITER_FORMAT bsWriterFormat = arg.mimeOutput ? BS_WRITER_FORMAT_MIME : BS_WRITER_FORMAT_G192;
+#ifdef IVAS_RTPDUMP
+ if ( !arg.rtpdumpOutput && BS_Writer_Open_filename( &hBsWriter, arg.outputBitstreamFilename, bsWriterFormat ) != IVAS_ERR_OK )
+#else
if ( BS_Writer_Open_filename( &hBsWriter, arg.outputBitstreamFilename, bsWriterFormat ) != IVAS_ERR_OK )
+#endif
{
fprintf( stderr, "\nCan't open %s\n\n", arg.outputBitstreamFilename );
goto cleanup;
@@ -593,8 +628,79 @@ int main(
}
}
+#ifdef IVAS_RTPDUMP
+ /*------------------------------------------------------------------------------------------*
+ * RTPDump
+ *------------------------------------------------------------------------------------------*/
+
+ if ( arg.rtpdumpOutput )
+ {
+ IVAS_RTP_PACK_CONFIG packCfg;
+ uint32_t SSRC = ( rand() & 0xFFFF ) | ( (uint32_t) rand() << 16 );
+
+ packCfg.maxFramesPerPacket = arg.numFramesPerPacket;
+
+ /* Open the output file for RTPDump writing */
+ error = IvasRtpFile_Open( arg.outputBitstreamFilename, true, &hWriter );
+ if ( error != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "could not open: %s\n", arg.outputBitstreamFilename );
+ goto cleanup;
+ }
+
+ error = IVAS_RTP_PACK_Open( &hPack, &packCfg );
+ if ( error != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "error in IVAS_RTP_PACK_Open(): %d\n", error );
+ goto cleanup;
+ }
+
+
+ error = IVAS_RTP_PACK_UpdateHeader(
+ hPack,
+ SSRC,
+ 0,
+ NULL,
+ 0,
+ 0,
+ NULL );
+ if ( error != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "error in IVAS_RTP_PACK_UpdateHeader(): %d\n", error );
+ goto cleanup;
+ }
+ }
+
+ /*------------------------------------------------------------------------------------------*
+ * Open scene orientation file
+ *------------------------------------------------------------------------------------------*/
+
+ if ( arg.sceneOrientationTrajFileName != NULL )
+ {
+ if ( ( error = RotationFileReader_open( arg.sceneOrientationTrajFileName, &sceneOrientationFileReader ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nError: Can't open scene orientation file %s \n\n", arg.sceneOrientationTrajFileName );
+ goto cleanup;
+ }
+ }
+
+ /*------------------------------------------------------------------------------------------*
+ * Open device orientation file
+ *------------------------------------------------------------------------------------------*/
+
+ if ( arg.deviceOrientationTrajFileName != NULL )
+ {
+ if ( ( error = RotationFileReader_open( arg.deviceOrientationTrajFileName, &deviceOrientationFileReader ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nError: Can't open device orientation file %s \n\n", arg.deviceOrientationTrajFileName );
+ goto cleanup;
+ }
+ }
+#endif
+
int16_t numSamplesRead = 0;
uint16_t bitStream[IVAS_MAX_BITS_PER_FRAME];
+
uint16_t numBits = 0;
#ifdef DEBUG_SBA
#ifdef DEBUG_AGC
@@ -757,18 +863,108 @@ int main(
}
/* *** Encode one frame *** */
- if ( ( error = IVAS_ENC_EncodeFrameToSerial( hIvasEnc, pcmBuf, pcmBufSize, bitStream, &numBits ) ) != IVAS_ERR_OK )
+#ifdef IVAS_RTPDUMP
+ if ( hPack )
{
- fprintf( stderr, "\nencodeFrame failed: %s\n\n", IVAS_ENC_GetErrorMessage( error ) );
- goto cleanup;
- }
+ if ( ( error = IVAS_ENC_EncodeFrameToCompact( hIvasEnc, pcmBuf, pcmBufSize, packedFrame.buffer, &numBits ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nencodeFrame failed: %s\n\n", IVAS_ENC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
- /* write bitstream */
- if ( ( error = BS_Writer_WriteFrame_short( hBsWriter, bitStream, numBits, totalBitrate ) ) != IVAS_ERR_OK )
+ packedFrame.length = ( numBits + 7 ) / 8;
+ rtpPacket.length = 0;
+
+ /* Push Encoded Stream to */
+ if ( ( error = IVAS_RTP_PACK_PushFrame( hPack,
+ arg.inputFormat != IVAS_ENC_INPUT_MONO ? IVAS_RTP_IVAS : IVAS_RTP_EVS,
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ NULL,
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+ &packedFrame ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nError %s while pushing audio frame to RTP pack\n", IVAS_ENC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+
+ /* scene orientation */
+ if ( sceneOrientationFileReader )
+ {
+ IVAS_PIDATA_ORIENTATION sceneOrientation = {
+ .size = sizeof( IVAS_PIDATA_ORIENTATION ),
+ .piDataType = IVAS_PI_SCENE_ORIENTATION,
+ .orientation = { 0.0f, 0.0f, 0.0f, 0.0f }
+ };
+
+ if ( ( error = HeadRotationFileReading( sceneOrientationFileReader, &sceneOrientation.orientation, NULL ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nError %s while reading scene orientation from %s\n", IVAS_ENC_GetErrorMessage( error ), RotationFileReader_getFilePath( sceneOrientationFileReader ) );
+ goto cleanup;
+ }
+
+ if ( ( error = IVAS_RTP_PACK_PushPiData( hPack, (const IVAS_PIDATA_GENERIC *) &sceneOrientation ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nError %s while pushing scene orientation\n", IVAS_ENC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+ }
+
+ /* device orientation */
+ if ( deviceOrientationFileReader )
+ {
+ IVAS_PIDATA_ORIENTATION deviceOrientation = {
+ .size = sizeof( IVAS_PIDATA_ORIENTATION ),
+ .piDataType = IVAS_PI_DEVICE_ORIENTATION_COMPENSATED,
+ .orientation = { 0.0f, 0.0f, 0.0f, 0.0f }
+ };
+
+ if ( ( error = HeadRotationFileReading( deviceOrientationFileReader, &deviceOrientation.orientation, NULL ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nError %s while reading device orientation from %s\n", IVAS_ENC_GetErrorMessage( error ), RotationFileReader_getFilePath( deviceOrientationFileReader ) );
+ goto cleanup;
+ }
+
+ if ( ( error = IVAS_RTP_PACK_PushPiData( hPack, (const IVAS_PIDATA_GENERIC *) &deviceOrientation ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nError %s while pushing scene orientation\n", IVAS_ENC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+ }
+
+ if ( ( numSamplesRead < pcmBufSize ) || IVAS_RTP_PACK_GetNumFrames( hPack ) == arg.numFramesPerPacket )
+ {
+ /* Generate RTP Packet */
+ if ( ( error = IVAS_RTP_PACK_GetPacket( hPack, &rtpPacket, &numFramesInPayload ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nError %s while packing RTP Header\n", IVAS_ENC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+
+ if ( ( error = IvasRtpFile_Write( hWriter, rtpPacket.buffer, rtpPacket.length ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nError %s while writing RTP packet\n", IVAS_ENC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+ }
+ }
+ else
{
- fprintf( stderr, "\nBS_Writer_WriteFrame_short failed, error code %d\n\n", error );
- goto cleanup;
+#endif
+ if ( ( error = IVAS_ENC_EncodeFrameToSerial( hIvasEnc, pcmBuf, pcmBufSize, bitStream, &numBits ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nencodeFrame failed: %s\n\n", IVAS_ENC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+
+ /* write bitstream */
+ if ( ( error = BS_Writer_WriteFrame_short( hBsWriter, bitStream, numBits, totalBitrate ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nBS_Writer_WriteFrame_short failed, error code %d\n\n", error );
+ goto cleanup;
+ }
+#ifdef IVAS_RTPDUMP
}
+#endif
frame++;
if ( !arg.quietModeEnabled )
@@ -846,6 +1042,41 @@ cleanup:
fclose( f_bitrateProfile );
}
+#ifdef IVAS_RTPDUMP
+ if ( hPack )
+ {
+ /* Complete the last packet */
+ if ( IVAS_RTP_PACK_GetNumFrames( hPack ) != 0 )
+ {
+ if ( ( error = IVAS_RTP_PACK_GetPacket( hPack, &rtpPacket, &numFramesInPayload ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nError %s while packing RTP Packet\n", IVAS_ENC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+
+ if ( ( error = IvasRtpFile_Write( hWriter, rtpPacket.buffer, rtpPacket.length ) ) != IVAS_ERR_OK )
+ {
+ fprintf( stderr, "\nError %s while writing RTP packet\n", IVAS_ENC_GetErrorMessage( error ) );
+ goto cleanup;
+ }
+ }
+
+ IVAS_RTP_PACK_Close( &hPack );
+ }
+
+ if ( sceneOrientationFileReader )
+ {
+ RotationFileReader_close( &sceneOrientationFileReader );
+ }
+
+ if ( deviceOrientationFileReader )
+ {
+ RotationFileReader_close( &deviceOrientationFileReader );
+ }
+
+ IvasRtpFile_Close( &hWriter );
+#endif
+
IVAS_ENC_Close( &hIvasEnc );
#ifdef WMOPS
@@ -916,6 +1147,11 @@ static bool parseCmdlIVAS_enc(
arg->mimeOutput = false;
arg->ism_extended_metadata = false;
arg->complexityLevel = IVAS_ENC_COMPLEXITY_LEVEL_THREE;
+#ifdef IVAS_RTPDUMP
+ arg->rtpdumpOutput = false;
+ arg->sceneOrientationTrajFileName = NULL;
+ arg->deviceOrientationTrajFileName = NULL;
+#endif
#ifdef DEBUGGING
arg->forcedMode = IVAS_ENC_FORCE_UNFORCED;
@@ -1710,6 +1946,71 @@ static bool parseCmdlIVAS_enc(
i++;
}
+#ifdef IVAS_RTPDUMP
+ /*-----------------------------------------------------------------*
+ * RTPDump output
+ *-----------------------------------------------------------------*/
+
+ else if ( strcmp( argv_to_upper, "-RTPDUMP" ) == 0 )
+ {
+ i++;
+ arg->rtpdumpOutput = true;
+ if ( i < argc - 4 )
+ {
+ if ( !is_digits_only( argv[i] ) )
+ {
+ arg->numFramesPerPacket = 1; /* Default to 1 frame per packet */
+ }
+ else
+ {
+ arg->numFramesPerPacket = atoi( argv[i++] );
+ if ( arg->numFramesPerPacket > IVAS_MAX_FRAMES_PER_RTP_PACKET )
+ {
+ fprintf( stderr, "numFramesPerPacket(%d) exceeds max frames per packet (%d) \n", arg->numFramesPerPacket, IVAS_MAX_FRAMES_PER_RTP_PACKET );
+ arg->numFramesPerPacket = 1;
+ }
+ }
+ }
+ fprintf( stdout, "Output format: RTPDump using %d frames/packet \n", arg->numFramesPerPacket );
+ }
+
+ /*-----------------------------------------------------------------*
+ * Scene orientation
+ *-----------------------------------------------------------------*/
+
+ else if ( strcmp( argv_to_upper, "-SCENE_ORIENTATION" ) == 0 )
+ {
+ i++;
+ if ( argc - i <= 4 || argv[i][0] == '-' )
+ {
+ fprintf( stderr, "Error: Scene orientation file name not specified!\n\n" );
+ usage_enc();
+ return false;
+ }
+
+ arg->sceneOrientationTrajFileName = argv[i];
+ i++;
+ }
+
+ /*-----------------------------------------------------------------*
+ * Device orientation
+ *-----------------------------------------------------------------*/
+
+ else if ( strcmp( argv_to_upper, "-DEVICE_ORIENTATION" ) == 0 )
+ {
+ i++;
+ if ( argc - i <= 4 || argv[i][0] == '-' )
+ {
+ fprintf( stderr, "Error: Device orientation file name not specified!\n\n" );
+ usage_enc();
+ return false;
+ }
+
+ arg->deviceOrientationTrajFileName = argv[i];
+ i++;
+ }
+
+#endif
/*-----------------------------------------------------------------*
* Option not recognized
*-----------------------------------------------------------------*/
@@ -1721,6 +2022,21 @@ static bool parseCmdlIVAS_enc(
}
} /* end of while */
+#ifdef IVAS_RTPDUMP
+ if ( arg->sceneOrientationTrajFileName != NULL && arg->rtpdumpOutput == false )
+ {
+ fprintf( stderr, "Error: Scene orientations are only enabled with rtpdump output!\n\n" );
+ usage_enc();
+ return false;
+ }
+ if ( arg->deviceOrientationTrajFileName != NULL && arg->rtpdumpOutput == false )
+ {
+ fprintf( stderr, "Error: Device orientations are only enabled with rtpdump output!\n\n" );
+ usage_enc();
+ return false;
+ }
+
+#endif
/*-----------------------------------------------------------------*
* Mandatory input arguments
*-----------------------------------------------------------------*/
@@ -1927,6 +2243,14 @@ static void usage_enc( void )
#endif
fprintf( stdout, "-q : Quiet mode, no frame counters\n" );
fprintf( stdout, " default is deactivated\n" );
+#ifdef IVAS_RTPDUMP
+ fprintf( stdout, "-rtpdump : RTPDump output, hf_only=1 by default. The encoder will packetize the \n" );
+ fprintf( stdout, " bitstream frames into TS26.253 Annex A IVAS RTP Payload Format packets and \n" );
+ fprintf( stdout, " writes those to the output file. In EVS mono operating mode, TS26.445 Annex A.2.2 \n" );
+ fprintf( stdout, " EVS RTP Payload Format is used. Optional N represents number of frames per RTP packet\n" );
+ fprintf( stdout, "-scene_orientation : Scene orientation trajectory file. Only used with rtpdump output.\n" );
+ fprintf( stdout, "-device_orientation : Device orientation trajectory file. Only used with rtpdump output.\n" );
+#endif
fprintf( stdout, "\n" );
return;
diff --git a/lib_com/common_api_types.h b/lib_com/common_api_types.h
index f5b13c0dea05c0ffb7dec08f670352afe428112d..ce2982b63bf22b020338188aecb8d230608aced5 100644
--- a/lib_com/common_api_types.h
+++ b/lib_com/common_api_types.h
@@ -212,6 +212,26 @@ typedef struct _IVAS_JBM_TRACE_DATA
} IVAS_JBM_TRACE_DATA;
+#ifdef IVAS_RTPDUMP
+typedef struct
+{
+ int16_t PIdataPresent;
+ char *sceneOrientation;
+ char *deviceOrientationCompensated;
+ char *deviceOrientationUncompensated;
+ char *acousticEnvironmentId;
+ char *acousticEnvironmentOnlyLateReverb;
+ char *acousticEnvironmentLateReverbAndEarlyReflections;
+} PI_DATA_CURRENT_FRAME;
+
+typedef struct
+{
+ int16_t sceneOrientationSaved;
+ IVAS_QUATERNION sceneOrientationQuat;
+ int16_t deviceOrientationSaved;
+ IVAS_QUATERNION deviceOrientationQuat;
+} PI_DATA_DEPACKER_STATE;
+#endif
typedef enum _ivas_binaural_renderer_type
{
IVAS_BIN_RENDERER_TYPE_NONE,
diff --git a/lib_com/ivas_error.h b/lib_com/ivas_error.h
index 563b10e39112c34c9c5093d1d50bde1242ca558a..4a5249934ce8cd4167a9d3a39b410dc9c9bee7b2 100644
--- a/lib_com/ivas_error.h
+++ b/lib_com/ivas_error.h
@@ -151,6 +151,14 @@ typedef enum
IVAS_ERR_LC3PLUS_INVALID_BITRATE,
IVAS_ERR_INVALID_SPLIT_REND_CONFIG,
+ /*----------------------------------------*
+ * rtp errors *
+ *----------------------------------------*/
+ IVAS_ERR_RTP_UNDERFLOW = 0x7000,
+ IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE,
+ IVAS_ERR_RTP_UNPACK_PI_DATA,
+ IVAS_ERR_RTP_UNSUPPORTED_FRAME,
+
/*----------------------------------------*
* unknown error *
*----------------------------------------*/
@@ -288,6 +296,22 @@ static inline const char *ivas_error_to_string( ivas_error error_code )
{
return "data error";
}
+ if ( ( error_code & 0x7000 ) == 0x7000 )
+ {
+ switch ( error_code )
+ {
+ case IVAS_ERR_RTP_UNDERFLOW:
+ return "RTP Undeflow in reading frame/packet";
+ case IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE:
+ return "Output buffer size is insufficient";
+ case IVAS_ERR_RTP_UNPACK_PI_DATA:
+ return "Unpacking PI data failure";
+ case IVAS_ERR_RTP_UNSUPPORTED_FRAME:
+ return "Unsupported RTP frame";
+ default:
+ return "rtp error";
+ }
+ }
return "Unknown error";
}
diff --git a/lib_com/ivas_prot.h b/lib_com/ivas_prot.h
index 895cb3ac07a1680669d4de9edae8509c1548e7e4..ad1b34d2e2ccd823dc43a299de8a46c2e0c6fcbb 100755
--- a/lib_com/ivas_prot.h
+++ b/lib_com/ivas_prot.h
@@ -806,7 +806,19 @@ void ivas_apply_non_diegetic_panning(
const int16_t output_frame /* i : output frame length per channel */
);
+#ifdef IVAS_RTPDUMP
+void QuaternionProduct(
+ const IVAS_QUATERNION q1,
+ const IVAS_QUATERNION q2,
+ IVAS_QUATERNION *const r
+);
+
+void QuaternionInverse(
+ const IVAS_QUATERNION q,
+ IVAS_QUATERNION *const r
+);
+#endif
/*----------------------------------------------------------------------------------*
* JBM prototypes
*----------------------------------------------------------------------------------*/
diff --git a/lib_com/options.h b/lib_com/options.h
index ee846da36c4aa5ed7a073d09e3096bf3e48aba1c..58ca69d57098444617041c3ba987e8c7ae98bca3 100644
--- a/lib_com/options.h
+++ b/lib_com/options.h
@@ -158,6 +158,9 @@
/* ################## Start DEVELOPMENT switches ######################### */
+//#define RTP_S4_251135_CR26253_0016_REV1 /* RTP Pack/Unpack API corresponding to CR 26253 */
+#define IVAS_RTPDUMP /* RTPDUMP writing and reading for IVAS payloads */
+
/* ################### Start BE switches ################################# */
/* only BE switches wrt selection floating point code */
diff --git a/lib_dec/ivas_binRenderer_internal.c b/lib_dec/ivas_binRenderer_internal.c
index 8cda55017c4e8c4a8be270e89da91b594acade35..426d8e9a4705ee8cb315b5ecdcb49060bdc87d13 100644
--- a/lib_dec/ivas_binRenderer_internal.c
+++ b/lib_dec/ivas_binRenderer_internal.c
@@ -1030,7 +1030,11 @@ ivas_error ivas_binRenderer_open(
ivas_dirac_dec_get_response( (int16_t) ls_azimuth_CICP19[k], (int16_t) ls_elevation_CICP19[k], hBinRenderer->hReverb->foa_enc[k], 1 );
}
}
+#ifdef IVAS_RTPDUMP
+ else if ( st_ivas->ivas_format == MC_FORMAT && ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation || st_ivas->hCombinedOrientationData ) )
+#else
else if ( st_ivas->ivas_format == MC_FORMAT && ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation ) )
+#endif
{
if ( ( error = efap_init_data( &( st_ivas->hEFAPdata ), st_ivas->hIntSetup.ls_azimuth, st_ivas->hIntSetup.ls_elevation, st_ivas->hIntSetup.nchan_out_woLFE, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK )
{
diff --git a/lib_dec/ivas_dirac_dec.c b/lib_dec/ivas_dirac_dec.c
index 88141656c84a071de48ceba2cf6d07a15bcdf32b..cdc22f80f6fe6a6146481f2f5c263b8cced64510 100644
--- a/lib_dec/ivas_dirac_dec.c
+++ b/lib_dec/ivas_dirac_dec.c
@@ -2072,7 +2072,11 @@ void ivas_dirac_dec_render_sf(
{
mvs2s( &hSpatParamRendCom->azimuth[md_idx][hDirAC->hConfig->enc_param_start_band], &azimuth[hDirAC->hConfig->enc_param_start_band], hSpatParamRendCom->num_freq_bands - hDirAC->hConfig->enc_param_start_band );
mvs2s( &hSpatParamRendCom->elevation[md_idx][hDirAC->hConfig->enc_param_start_band], &elevation[hDirAC->hConfig->enc_param_start_band], hSpatParamRendCom->num_freq_bands - hDirAC->hConfig->enc_param_start_band );
+#ifdef IVAS_RTPDUMP
+ if ( ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation || st_ivas->hCombinedOrientationData ) && st_ivas->hCombinedOrientationData->shd_rot_max_order == 0 )
+#else
if ( ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation ) && st_ivas->hCombinedOrientationData->shd_rot_max_order == 0 )
+#endif
{
num_freq_bands = hDirAC->band_grouping[hDirAC->hConfig->enc_param_start_band];
rotateAziEle_DirAC( azimuth, elevation, num_freq_bands, hSpatParamRendCom->num_freq_bands, p_Rmat );
diff --git a/lib_dec/ivas_init_dec.c b/lib_dec/ivas_init_dec.c
index 2c2f814ab88b811d47b6fe7f01dfddf3b901f8de..400243e877f6ee7a8025b7cc3eb8ae7ff8c85b6a 100644
--- a/lib_dec/ivas_init_dec.c
+++ b/lib_dec/ivas_init_dec.c
@@ -2145,7 +2145,11 @@ ivas_error ivas_init_decoder(
}
else if ( st_ivas->renderer_type == RENDERER_BINAURAL_MIXER_CONV || st_ivas->renderer_type == RENDERER_BINAURAL_MIXER_CONV_ROOM )
{
+#ifdef IVAS_RTPDUMP
+ if ( st_ivas->renderer_type == RENDERER_BINAURAL_MIXER_CONV_ROOM && st_ivas->ivas_format == MC_FORMAT && ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation || st_ivas->hCombinedOrientationData ) )
+#else
if ( st_ivas->renderer_type == RENDERER_BINAURAL_MIXER_CONV_ROOM && st_ivas->ivas_format == MC_FORMAT && ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation ) )
+#endif
{
if ( ( error = efap_init_data( &( st_ivas->hEFAPdata ), st_ivas->hIntSetup.ls_azimuth, st_ivas->hIntSetup.ls_elevation, st_ivas->hIntSetup.nchan_out_woLFE, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK )
{
diff --git a/lib_dec/ivas_mc_param_dec.c b/lib_dec/ivas_mc_param_dec.c
index ed7c9fb0b4838961b52c6acc6fe3aa07d9931205..b43af8b70b43af306af68c27eb5183e841b59b84 100644
--- a/lib_dec/ivas_mc_param_dec.c
+++ b/lib_dec/ivas_mc_param_dec.c
@@ -401,7 +401,11 @@ ivas_error ivas_param_mc_dec_open(
ivas_param_mc_dec_compute_interpolator( 0, 0, DEFAULT_JBM_CLDFB_TIMESLOTS, hParamMC->h_output_synthesis_params.interpolator );
/* Head or external rotation */
+#ifdef IVAS_RTPDUMP
+ if ( ( st_ivas->renderer_type == RENDERER_BINAURAL_FASTCONV || st_ivas->renderer_type == RENDERER_BINAURAL_FASTCONV_ROOM ) && ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation || st_ivas->hCombinedOrientationData ) )
+#else
if ( ( st_ivas->renderer_type == RENDERER_BINAURAL_FASTCONV || st_ivas->renderer_type == RENDERER_BINAURAL_FASTCONV_ROOM ) && ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation ) )
+#endif
{
if ( ( hParamMC->hoa_encoder = (float *) malloc( st_ivas->hTransSetup.nchan_out_woLFE * MAX_INTERN_CHANNELS * sizeof( float ) ) ) == NULL )
{
diff --git a/lib_dec/ivas_mc_paramupmix_dec.c b/lib_dec/ivas_mc_paramupmix_dec.c
index 5753d262a078261ae49ea0abb9c6247a1ce377d0..8df57ff8ebe185c88a583d51a9ed1b5a38bf447c 100644
--- a/lib_dec/ivas_mc_paramupmix_dec.c
+++ b/lib_dec/ivas_mc_paramupmix_dec.c
@@ -349,7 +349,11 @@ ivas_error ivas_mc_paramupmix_dec_open(
}
/* Head or external rotation */
+#ifdef IVAS_RTPDUMP
+ if ( ( st_ivas->renderer_type == RENDERER_BINAURAL_FASTCONV || st_ivas->renderer_type == RENDERER_BINAURAL_FASTCONV_ROOM ) && ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation || st_ivas->hCombinedOrientationData ) )
+#else
if ( ( st_ivas->renderer_type == RENDERER_BINAURAL_FASTCONV || st_ivas->renderer_type == RENDERER_BINAURAL_FASTCONV_ROOM ) && ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation ) )
+#endif
{
if ( ( hMCParamUpmix->hoa_encoder = (float *) malloc( st_ivas->hTransSetup.nchan_out_woLFE * MAX_INTERN_CHANNELS * sizeof( float ) ) ) == NULL )
{
diff --git a/lib_dec/ivas_mct_dec.c b/lib_dec/ivas_mct_dec.c
index eaa094b68bbedb956823e0b04baa002be3e19a2f..3a0ba42df4a8858beaeca8bcd297b08c7ec89585 100644
--- a/lib_dec/ivas_mct_dec.c
+++ b/lib_dec/ivas_mct_dec.c
@@ -1143,8 +1143,11 @@ static ivas_error ivas_mc_dec_reconfig(
if ( st_ivas->hBinRenderer != NULL && ( st_ivas->renderer_type != RENDERER_BINAURAL_FASTCONV && st_ivas->renderer_type != RENDERER_BINAURAL_FASTCONV_ROOM ) )
{
ivas_binRenderer_close( &st_ivas->hBinRenderer );
-
+#ifdef IVAS_RTPDUMP
+ if ( ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation || st_ivas->hCombinedOrientationData ) )
+#else
if ( ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation ) )
+#endif
{
efap_free_data( &st_ivas->hEFAPdata );
}
diff --git a/lib_dec/ivas_output_config.c b/lib_dec/ivas_output_config.c
index b0acefc17eb7815e28206ff7495a974e839a323f..febcf78d15fec3f71eddca16aedb32980b7e683f 100644
--- a/lib_dec/ivas_output_config.c
+++ b/lib_dec/ivas_output_config.c
@@ -70,8 +70,11 @@ void ivas_renderer_select(
/*-----------------------------------------------------------------*
* Binaural rendering configurations
*-----------------------------------------------------------------*/
-
+#ifdef IVAS_RTPDUMP
+ if ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation || st_ivas->hCombinedOrientationData )
+#else
if ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation )
+#endif
{
st_ivas->hCombinedOrientationData->shd_rot_max_order = -1;
}
@@ -155,7 +158,11 @@ void ivas_renderer_select(
*internal_config = IVAS_AUDIO_CONFIG_7_1_4;
}
+#ifdef IVAS_RTPDUMP
+ if ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation || st_ivas->hCombinedOrientationData )
+#else
if ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation )
+#endif
{
nchan_internal = ivas_sba_get_nchan_metadata( st_ivas->sba_analysis_order, st_ivas->hDecoderConfig->ivas_total_brate );
@@ -199,9 +206,17 @@ void ivas_renderer_select(
if ( output_config == IVAS_AUDIO_CONFIG_BINAURAL || output_config == IVAS_AUDIO_CONFIG_BINAURAL_ROOM_REVERB || output_config == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_PCM || output_config == IVAS_AUDIO_CONFIG_BINAURAL_SPLIT_CODED )
{
#ifdef DEBUGGING
+#ifdef IVAS_RTPDUMP
+ if ( ( ( ( st_ivas->transport_config == IVAS_AUDIO_CONFIG_5_1 || st_ivas->transport_config == IVAS_AUDIO_CONFIG_7_1 ) && ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation || st_ivas->hCombinedOrientationData ) ) || ( st_ivas->hDecoderConfig->force_rend == FORCE_TD_RENDERER ) ) && st_ivas->mc_mode == MC_MODE_MCT && st_ivas->hDecoderConfig->force_rend != FORCE_CLDFB_RENDERER )
+#else
if ( ( ( ( st_ivas->transport_config == IVAS_AUDIO_CONFIG_5_1 || st_ivas->transport_config == IVAS_AUDIO_CONFIG_7_1 ) && ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation ) ) || ( st_ivas->hDecoderConfig->force_rend == FORCE_TD_RENDERER ) ) && st_ivas->mc_mode == MC_MODE_MCT && st_ivas->hDecoderConfig->force_rend != FORCE_CLDFB_RENDERER )
+#endif
+#else
+#ifdef IVAS_RTPDUMP
+ if ( ( st_ivas->transport_config == IVAS_AUDIO_CONFIG_5_1 || st_ivas->transport_config == IVAS_AUDIO_CONFIG_7_1 ) && ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation || st_ivas->hCombinedOrientationData ) && st_ivas->mc_mode == MC_MODE_MCT )
#else
if ( ( st_ivas->transport_config == IVAS_AUDIO_CONFIG_5_1 || st_ivas->transport_config == IVAS_AUDIO_CONFIG_7_1 ) && ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation ) && st_ivas->mc_mode == MC_MODE_MCT )
+#endif
#endif
{
*renderer_type = RENDERER_BINAURAL_OBJECTS_TD;
@@ -217,7 +232,11 @@ void ivas_renderer_select(
*renderer_type = RENDERER_BINAURAL_FASTCONV;
}
+#ifdef IVAS_RTPDUMP
+ if ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation || st_ivas->hCombinedOrientationData )
+#else
if ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation )
+#endif
{
/* force HOA3 domain for rotation*/
*internal_config = IVAS_AUDIO_CONFIG_HOA3;
diff --git a/lib_dec/jbm_jb4sb.h b/lib_dec/jbm_jb4sb.h
index 8155d66ae9fafd953e854cb501aeab433b8fb28d..2ef48dbfb32b09614cc9a72b4261836f1bb7390a 100644
--- a/lib_dec/jbm_jb4sb.h
+++ b/lib_dec/jbm_jb4sb.h
@@ -63,8 +63,13 @@ struct JB4_DATAUNIT
uint32_t rcvTime;
/** true, if the data unit contains only silence */
bool silenceIndicator;
+#ifdef IVAS_RTPDUMP
+ /** good frame indicator (Q bit for AMR-WB IO, otherwise set to true) */
+ Word16 isGoodFrame;
+#else
/** Q bit for AMR-WB IO */
Word16 qBit;
+#endif
/** the binary encoded access unit */
uint8_t *data;
diff --git a/lib_dec/lib_dec.c b/lib_dec/lib_dec.c
index 41feeafec2cb0add778f6379f96d2170430007a7..4d27cb168ed20aeda3d0108269b961615f0d59f5 100644
--- a/lib_dec/lib_dec.c
+++ b/lib_dec/lib_dec.c
@@ -339,10 +339,9 @@ void IVAS_DEC_Close(
}
/* destroy Split binaural renderer (ISAR) handle */
- ivas_destroy_handle_isar( &( *phIvasDec )->st_ivas->hSplitBinRend );
-
if ( ( *phIvasDec )->st_ivas )
{
+ ivas_destroy_handle_isar( &( *phIvasDec )->st_ivas->hSplitBinRend );
ivas_destroy_dec( ( *phIvasDec )->st_ivas );
( *phIvasDec )->st_ivas = NULL;
}
@@ -3234,7 +3233,11 @@ ivas_error IVAS_DEC_VoIP_FeedFrame(
const uint16_t rtpSequenceNumber, /* i : RTP sequence number (16 bits) */
const uint32_t rtpTimeStamp, /* i : RTP timestamp (32 bits) */
const uint32_t rcvTime_ms, /* i : receive time of the RTP packet in milliseconds */
- const bool qBit /* i : Q bit for AMR-WB IO */
+#ifdef IVAS_RTPDUMP
+ const bool isGoodFrame /* i : Good frame indicator (Q bit for AMR-WB IO, otherwise set to true) */
+#else
+ const bool qBit /* i : Q bit for AMR-WB IO */
+#endif
)
{
JB4_DATAUNIT_HANDLE dataUnit;
@@ -3276,7 +3279,11 @@ ivas_error IVAS_DEC_VoIP_FeedFrame(
dataUnit->timeStamp = rtpTimeStamp;
dataUnit->partial_frame = 0;
dataUnit->partialCopyOffset = partialCopyOffset;
+#ifdef IVAS_RTPDUMP
+ dataUnit->isGoodFrame = isGoodFrame;
+#else
dataUnit->qBit = qBit;
+#endif
/* add the frame to the JBM */
result = JB4_PushDataUnit( hIvasDec->hVoIP->hJBM, dataUnit, rcvTime_ms );
@@ -3299,7 +3306,11 @@ ivas_error IVAS_DEC_VoIP_FeedFrame(
dataUnit->timeStamp = rtpTimeStamp - partialCopyOffset * dataUnit->duration;
dataUnit->partial_frame = 1;
dataUnit->partialCopyOffset = partialCopyOffset;
+#ifdef IVAS_RTPDUMP
+ dataUnit->isGoodFrame = isGoodFrame;
+#else
dataUnit->qBit = qBit;
+#endif
/* add the frame to the JBM */
result = JB4_PushDataUnit( hIvasDec->hVoIP->hJBM, dataUnit, rcvTime_ms );
@@ -3723,6 +3734,100 @@ ivas_error IVAS_DEC_Flush(
}
+#ifdef IVAS_RTPDUMP
+/*---------------------------------------------------------------------*
+ * IVAS_DEC_feedSinglePIorientation( )
+ *
+ * Feed a single orientation PI data to external orientation handle.
+ *---------------------------------------------------------------------*/
+
+ivas_error IVAS_DEC_feedSinglePIorientation(
+ IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */
+ bool isOrientationSaved, /* i : flag to indicate if an orientation for this PI type was previously saved */
+ IVAS_QUATERNION *savedOrientation /* i : previously saved orientation for this PI type */
+)
+{
+ int16_t i;
+ ivas_error error = IVAS_ERR_OK;
+ IVAS_QUATERNION savedInvOrientation;
+
+ if ( isOrientationSaved )
+ {
+ if ( !hIvasDec->st_ivas->hExtOrientationData )
+ {
+ if ( ( error = ivas_external_orientation_open( &( hIvasDec->st_ivas->hExtOrientationData ), hIvasDec->st_ivas->hDecoderConfig->render_framesize ) ) != IVAS_ERR_OK )
+ {
+ return error;
+ }
+ }
+
+ if ( !hIvasDec->st_ivas->hCombinedOrientationData )
+ {
+ if ( ( error = ivas_combined_orientation_open( &( hIvasDec->st_ivas->hCombinedOrientationData ), hIvasDec->st_ivas->hDecoderConfig->output_Fs, hIvasDec->st_ivas->hDecoderConfig->render_framesize ) ) != IVAS_ERR_OK )
+ {
+ return error;
+ }
+ }
+
+ QuaternionInverse( *savedOrientation, &savedInvOrientation );
+
+ /* use the new PI orientation or the previously saved orientation in processing */
+ for ( i = 0; i < hIvasDec->st_ivas->hExtOrientationData->num_subframes; i++ )
+ {
+ QuaternionProduct( hIvasDec->st_ivas->hExtOrientationData->Quaternions[i], savedInvOrientation,
+ &hIvasDec->st_ivas->hExtOrientationData->Quaternions[i] );
+ hIvasDec->st_ivas->hExtOrientationData->enableExternalOrientation[i] = true;
+ }
+ hIvasDec->updateOrientation = true;
+ }
+ return error;
+}
+
+
+/*---------------------------------------------------------------------*
+ * IVAS_DEC_feedPIdata( )
+ *
+ * Feed PI data to the decoder handle
+ *---------------------------------------------------------------------*/
+
+ivas_error IVAS_DEC_feedPIdata(
+ IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */
+ PI_DATA_DEPACKER_STATE *PIdataDepackerState /* i/o: data handle for the PI depacker state */
+)
+{
+ ivas_error error = IVAS_ERR_OK;
+
+ /* scene orientation */
+ if ( ( error = IVAS_DEC_feedSinglePIorientation( hIvasDec, PIdataDepackerState->sceneOrientationSaved, &PIdataDepackerState->sceneOrientationQuat ) ) != IVAS_ERR_OK )
+ {
+ return error;
+ }
+
+ /* device orientation */
+ if ( ( error = IVAS_DEC_feedSinglePIorientation( hIvasDec, PIdataDepackerState->deviceOrientationSaved, &PIdataDepackerState->deviceOrientationQuat ) ) != IVAS_ERR_OK )
+ {
+ return error;
+ }
+
+ return error;
+}
+
+
+/*---------------------------------------------------------------------*
+ * IVAS_DEC_resetExternalOrientations( )
+ *
+ * Reset external orientations
+ *---------------------------------------------------------------------*/
+
+void IVAS_DEC_resetExternalOrientations(
+ IVAS_DEC_HANDLE hIvasDec /* i/o: IVAS decoder handle */
+)
+{
+ ivas_external_orientation_reset( &( hIvasDec->st_ivas->hExtOrientationData ) );
+}
+
+
+#endif
/*---------------------------------------------------------------------*
* IVAS_DEC_VoIP_IsEmpty( )
*
diff --git a/lib_dec/lib_dec.h b/lib_dec/lib_dec.h
index f28279d86b17e8142d579461c1c09b9e84b9ad56..2e98d9e0f1e5547ba8f73c6b5db3d96ea43f30a6 100644
--- a/lib_dec/lib_dec.h
+++ b/lib_dec/lib_dec.h
@@ -285,7 +285,11 @@ ivas_error IVAS_DEC_VoIP_FeedFrame(
const uint16_t rtpSequenceNumber, /* i : RTP sequence number (16 bits) */
const uint32_t rtpTimeStamp, /* i : RTP timestamp (32 bits) */
const uint32_t rcvTime_ms, /* i : receive time of the RTP packet in milliseconds */
- const bool qBit /* i : Q bit for AMR-WB IO */
+#ifdef IVAS_RTPDUMP
+ const bool isGoodFrame /* i : Good frame indicator (Q bit for AMR-WB IO, otherwise set to true) */
+#else
+const bool qBit /* i : Q bit for AMR-WB IO */
+#endif
);
ivas_error IVAS_DEC_VoIP_SetScale(
@@ -329,6 +333,23 @@ ivas_error IVAS_DEC_Flush(
int16_t *nSamplesFlushed /* o : number of samples flushed */
);
+#ifdef IVAS_RTPDUMP
+ivas_error IVAS_DEC_feedSinglePIorientation(
+ IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */
+ bool isOrientationSaved, /* i : flag to indicate if an orientation for this PI type was previously saved */
+ IVAS_QUATERNION *savedOrientation /* i : previously saved orientation for this PI type */
+);
+
+ivas_error IVAS_DEC_feedPIdata(
+ IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */
+ PI_DATA_DEPACKER_STATE *PIdataDepackerState /* i/o: data handle for the PI depacker state */
+);
+
+void IVAS_DEC_resetExternalOrientations(
+ IVAS_DEC_HANDLE hIvasDec /* i/o: IVAS decoder handle */
+);
+
+#endif
/* Setter functions - apply changes to decoder configuration */
/*! r: error code */
diff --git a/lib_rend/ivas_prot_rend.h b/lib_rend/ivas_prot_rend.h
index 32c2992c1a2449e6e329d666e821248b1e5bd426..937923c3386d8e44e806692a29920d1a15b1dc18 100644
--- a/lib_rend/ivas_prot_rend.h
+++ b/lib_rend/ivas_prot_rend.h
@@ -1305,6 +1305,12 @@ ivas_error ivas_external_orientation_open(
const int16_t num_subframes /* i : number of subframes */
);
+#ifdef IVAS_RTPDUMP
+void ivas_external_orientation_reset(
+ EXTERNAL_ORIENTATION_HANDLE *hExtOrientationData /* i/o : external orientation handle */
+);
+
+#endif
void ivas_external_orientation_close(
EXTERNAL_ORIENTATION_HANDLE *hExtOrientationData /* i/o: external orientation handle */
);
@@ -1352,6 +1358,7 @@ ivas_error ivas_render_config_init_from_rom(
* Quaternion operations
*----------------------------------------------------------------------------------*/
+#ifndef IVAS_RTPDUMP
void QuaternionProduct(
const IVAS_QUATERNION q1,
const IVAS_QUATERNION q2,
@@ -1363,6 +1370,7 @@ void QuaternionInverse(
IVAS_QUATERNION *const r
);
+#endif
void QuaternionSlerp(
const IVAS_QUATERNION q1,
const IVAS_QUATERNION q2,
diff --git a/lib_rend/ivas_rotation.c b/lib_rend/ivas_rotation.c
index 9385bc753e6d2b62dd5f216d1b0df3f52bc68cc7..9a1d74581da922928edefa6bb34ff03f69a6dea7 100644
--- a/lib_rend/ivas_rotation.c
+++ b/lib_rend/ivas_rotation.c
@@ -693,6 +693,7 @@ ivas_error ivas_external_orientation_open(
const int16_t num_subframes /* i : number of subframes */
)
{
+#ifndef IVAS_RTPDUMP
int16_t i;
IVAS_QUATERNION identity;
@@ -700,12 +701,16 @@ ivas_error ivas_external_orientation_open(
identity.w = 1.0f;
identity.x = identity.y = identity.z = 0.0f;
+#endif
/* Allocate handle */
if ( ( *hExtOrientationData = (EXTERNAL_ORIENTATION_HANDLE) malloc( sizeof( EXTERNAL_ORIENTATION_DATA ) ) ) == NULL )
{
return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for external orientation memory\n" ) );
}
( *hExtOrientationData )->num_subframes = num_subframes;
+#ifdef IVAS_RTPDUMP
+ ivas_external_orientation_reset( hExtOrientationData );
+#else
/* Enable head rotation and disable external orientation as default */
for ( i = 0; i < MAX_PARAM_SPATIAL_SUBFRAMES; i++ )
{
@@ -715,10 +720,45 @@ ivas_error ivas_external_orientation_open(
( *hExtOrientationData )->numFramesToTargetOrientation[i] = 0;
( *hExtOrientationData )->Quaternions[i] = identity;
}
+#endif
return IVAS_ERR_OK;
}
+#ifdef IVAS_RTPDUMP
+/*-----------------------------------------------------------------------*
+ * ivas_external_orientation_reset()
+ *
+ * Reset external orientation values
+ *-----------------------------------------------------------------------*/
+
+void ivas_external_orientation_reset(
+ EXTERNAL_ORIENTATION_HANDLE *hExtOrientationData /* i/o : external orientation handle */
+)
+{
+ if ( *hExtOrientationData != NULL )
+ {
+ int16_t i;
+ IVAS_QUATERNION identity;
+
+ identity.w = 1.0f;
+ identity.x = 0.0f;
+ identity.y = 0.0f;
+ identity.z = 0.0f;
+ /* Enable head rotation and disable external orientation as default */
+ for ( i = 0; i < MAX_PARAM_SPATIAL_SUBFRAMES; i++ )
+ {
+ ( *hExtOrientationData )->enableHeadRotation[i] = 1;
+ ( *hExtOrientationData )->enableExternalOrientation[i] = 0;
+ ( *hExtOrientationData )->enableRotationInterpolation[i] = 0;
+ ( *hExtOrientationData )->numFramesToTargetOrientation[i] = 0;
+ ( *hExtOrientationData )->Quaternions[i] = identity;
+ }
+ }
+}
+
+
+#endif
/*-----------------------------------------------------------------------*
* ivas_external_orientation_close()
*
diff --git a/lib_util/evs_rtp_payload.c b/lib_util/evs_rtp_payload.c
index a0c514bad3ac1495178debf42f7b6736ec770bd3..7fc0914892d26037504f5834d3f0ec96840a02ef 100644
--- a/lib_util/evs_rtp_payload.c
+++ b/lib_util/evs_rtp_payload.c
@@ -37,7 +37,9 @@
#include
#include
#include "evs_rtp_payload.h"
+#include "options.h"
+#ifndef IVAS_RTPDUMP
static void evsPayload_unpackFrame_compact_amrWbIo( const char *payload, uint16_t payloadSizeBits, uint16_t iProtectedSize, unsigned char **framePtr, uint16_t *frameSizeInBits )
{
uint16_t i, iBit0;
@@ -122,7 +124,29 @@ bool evsPayload_unpackFrame( bool hf_only, char *payload, uint16_t payloadSizeBy
return evsHeaderFullPayload_unpackFrame( payload, payloadSizeBytes, frameIndex, isAMRWB_IOmode,
frameFollowing, frameTypeIndex, qBit, framePtr, frameSizeInBits );
}
+#endif
+#ifdef IVAS_RTPDUMP
+void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, bool *isGoodFrame, int32_t *bitrate )
+{
+ bool evsModeBit = ( toc & 0x20 ) != 0;
+ *isAMRWB_IOmode = evsModeBit;
+ *frameFollowing = ( toc & 0x40 ) != 0;
+ *frameTypeIndex = toc & 0x0f;
+ if ( *isAMRWB_IOmode )
+ {
+ *qBit = ( toc & 0x10 ) != 0;
+ *isGoodFrame = *qBit;
+ *bitrate = AMRWB_IOmode2rate[*frameTypeIndex];
+ }
+ else
+ {
+ *qBit = false; /* Q-bit is unused in EVS Primary mode */
+ *isGoodFrame = true; /* assume good frame for EVS Primary */
+ *bitrate = PRIMARYmode2rate[*frameTypeIndex];
+ }
+}
+#else
static void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, int32_t *bitrate )
{
bool evsModeBit = ( toc & 0x20 ) != 0;
@@ -140,8 +164,9 @@ static void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bo
*bitrate = AMRWB_IOmode2rate[*frameTypeIndex];
}
}
+#endif
-
+#ifndef IVAS_RTPDUMP
bool evsHeaderFullPayload_unpackFrame( char *payload, uint16_t payloadSizeBytes, uint16_t frameIndex, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, unsigned char **frame, uint16_t *frameSizeInBits )
{
bool someIsAMRWB_IOmode, someFrameFollowing = true, someQBit;
@@ -273,3 +298,4 @@ void EVS_RTPDUMP_DEPACKER_close( EVS_RTPDUMP_DEPACKER *self )
}
RTPDUMP_Close( &self->rtpdump, 0 );
}
+#endif
diff --git a/lib_util/evs_rtp_payload.h b/lib_util/evs_rtp_payload.h
index 357d985578ba9616980b33e62b75c62ee6ad5604..aea968ddffd154b05e855dd19acec76b73ee44ce 100644
--- a/lib_util/evs_rtp_payload.h
+++ b/lib_util/evs_rtp_payload.h
@@ -83,6 +83,7 @@ extern "C"
0 /* NO_DATA */
};
+#ifndef IVAS_RTPDUMP
static const uint16_t evsPayloadProtectedSizes[22] = {
48,
56,
@@ -159,8 +160,14 @@ extern "C"
};
bool evsPayload_unpackFrame( bool hf_only, char *payload, uint16_t payloadSizeBytes, uint16_t frameIndex, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, unsigned char **framePtr, uint16_t *frameSizeBits );
+#endif
-
+#ifdef IVAS_RTPDUMP
+ void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, bool *isGoodFrame, int32_t *bitrate );
+#else
+static void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, int32_t *bitrate );
+#endif
+#ifndef IVAS_RTPDUMP
bool evsHeaderFullPayload_unpackFrame( char *payload, uint16_t payloadSizeBytes, uint16_t frameIndex, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, unsigned char **frame, uint16_t *frameSizeBits );
typedef struct
@@ -195,6 +202,7 @@ extern "C"
uint16_t *frameSizeBits );
void EVS_RTPDUMP_DEPACKER_close( EVS_RTPDUMP_DEPACKER *self );
+#endif
#ifdef __cplusplus
}
diff --git a/lib_util/g192.c b/lib_util/g192.c
index a96a8ee56dc1ea4997e164f35c70beedd178dad7..a5eb030a4fd4bb37dcb9fbd96d7f8b67d0e61121 100644
--- a/lib_util/g192.c
+++ b/lib_util/g192.c
@@ -482,7 +482,11 @@ G192_ERROR G192_WriteVoipFrame_short(
const uint16_t *serial,
const int16_t numBits,
uint16_t const rtpSequenceNumber,
+#ifdef IVAS_RTPDUMP
+ uint32_t const rtpTimeStamp,
+#else
uint16_t const rtpTimeStamp,
+#endif
uint32_t const rcvTime_ms )
{
int16_t G192_HEADER[2], G192_DATA[IVAS_MAX_BITS_PER_FRAME];
diff --git a/lib_util/g192.h b/lib_util/g192.h
index 751f1324fb286131ba1cd8fd23d68622e69fce3c..0c22b739b9ff372d2939c423615a13458d31e7fd 100644
--- a/lib_util/g192.h
+++ b/lib_util/g192.h
@@ -93,7 +93,13 @@ G192_ERROR G192_Writer_Open_filename( G192_HANDLE *phG192, const char *filename
G192_ERROR G192_WriteFrame( G192_HANDLE const hG192, const uint16_t *serial, const int16_t numBits );
-G192_ERROR G192_WriteVoipFrame_short( G192_HANDLE const hG192, const uint16_t *serial, const int16_t num_bits, uint16_t const rtpSequenceNumber, uint16_t const rtpTimeStamp, uint32_t const rcvTime_ms );
+G192_ERROR G192_WriteVoipFrame_short( G192_HANDLE const hG192, const uint16_t *serial, const int16_t num_bits, uint16_t const rtpSequenceNumber,
+#ifdef IVAS_RTPDUMP
+ uint32_t const rtpTimeStamp,
+#else
+ uint16_t const rtpTimeStamp,
+#endif
+ uint32_t const rcvTime_ms );
G192_ERROR G192_Writer_Close( G192_HANDLE *phG192 );
diff --git a/lib_util/ivas_bpool.c b/lib_util/ivas_bpool.c
new file mode 100644
index 0000000000000000000000000000000000000000..827422fac3ae43646a03be653c4a0d42ff4a15bc
--- /dev/null
+++ b/lib_util/ivas_bpool.c
@@ -0,0 +1,154 @@
+/******************************************************************************************************
+
+ (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository. All Rights Reserved.
+
+ This software is protected by copyright law and by international treaties.
+ The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository retain full ownership rights in their respective contributions in
+ the software. This notice grants no license of any kind, including but not limited to patent
+ license, nor is any license granted by implication, estoppel or otherwise.
+
+ Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
+ contributions.
+
+ This software is provided "AS IS", without any express or implied warranties. The software is in the
+ development stage. It is intended exclusively for experts who have experience with such software and
+ solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
+ and fitness for a particular purpose are hereby disclaimed and excluded.
+
+ Any dispute, controversy or claim arising under or in relation to providing this software shall be
+ submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
+ accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
+ the United Nations Convention on Contracts on the International Sales of Goods.
+
+*******************************************************************************************************/
+#include
+#include
+#include
+#include "ivas_bpool.h"
+#include "ivas_error_utils.h"
+#include "mutex.h"
+
+struct BPOOL
+{
+ mtx_t lock;
+ uint32_t bufferSize;
+ uint32_t numBuffers;
+ uint32_t numFreeBuffers;
+ void **freeBuffers;
+};
+
+ivas_error BPOOL_Create( BPOOL_HANDLE *pHandle, size_t bufferSize, uint32_t numBuffers )
+{
+ uint32_t n;
+ uint8_t *base = NULL;
+ BPOOL_HANDLE handle;
+ size_t allocSize = sizeof( struct BPOOL );
+
+ if ( pHandle == NULL )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Invalid pointer to Buffer Pool Handle" );
+ }
+
+ *pHandle = NULL;
+
+ allocSize += bufferSize * numBuffers; /* pool memory */
+ allocSize += sizeof( void * ) * numBuffers; /* free buffers stack */
+
+ base = calloc( allocSize, sizeof( uint8_t ) );
+ if ( base == NULL )
+ {
+ return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Couldn't allocate Buffer pool" );
+ }
+
+ handle = (BPOOL_HANDLE) base;
+ base += sizeof( struct BPOOL );
+
+ mtx_init( &handle->lock, 0 );
+ handle->bufferSize = bufferSize;
+ handle->numBuffers = numBuffers;
+ handle->numFreeBuffers = numBuffers;
+ handle->freeBuffers = (void **) base;
+ base += ( sizeof( void * ) * numBuffers );
+ for ( n = 0; n < numBuffers; n++ )
+ {
+ handle->freeBuffers[n] = base;
+ base += bufferSize;
+ }
+
+ *pHandle = handle;
+ return IVAS_ERR_OK;
+}
+
+void BPOOL_Destroy( BPOOL_HANDLE *pHandle )
+{
+ if ( ( pHandle != NULL ) && ( *pHandle != NULL ) )
+ {
+ mtx_destroy( &( *pHandle )->lock );
+ free( *pHandle );
+ *pHandle = NULL;
+ }
+}
+
+ivas_error BPOOL_GetBuffer( BPOOL_HANDLE handle, void **dataPtr )
+{
+ uint32_t idx = 0;
+ bool isFree = false;
+
+ if ( handle == NULL || dataPtr == NULL )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Invalid pointer args in GetBuffer" );
+ }
+
+ mtx_lock( &handle->lock );
+ isFree = ( handle->numFreeBuffers > 0 );
+ if ( isFree )
+ {
+ idx = --handle->numFreeBuffers;
+ }
+ mtx_unlock( &handle->lock );
+
+ if ( !isFree )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Underflow, no free buffers in pool" );
+ }
+
+ *dataPtr = handle->freeBuffers[idx];
+ return IVAS_ERR_OK;
+}
+
+/* return the buffer back to pool */
+ivas_error BPOOL_FreeBuffer( BPOOL_HANDLE handle, void *dataPtr )
+{
+ uint32_t idx;
+
+ if ( handle == NULL || dataPtr == NULL )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Invalid pointer args in GetBuffer" );
+ }
+
+ mtx_lock( &handle->lock );
+ idx = handle->numFreeBuffers++;
+ mtx_unlock( &handle->lock );
+
+ handle->freeBuffers[idx] = dataPtr;
+
+ return IVAS_ERR_OK;
+}
+
+/* return the number of free buffers available atm in the pool */
+uint32_t BPOOL_AvailableBuffers( BPOOL_HANDLE handle )
+{
+ uint32_t numFreeBuffers;
+ mtx_lock( &handle->lock );
+ numFreeBuffers = handle->numFreeBuffers;
+ mtx_unlock( &handle->lock );
+ return numFreeBuffers;
+}
diff --git a/lib_util/ivas_bpool.h b/lib_util/ivas_bpool.h
new file mode 100644
index 0000000000000000000000000000000000000000..30cf4962b5198fd2a22a522061e8ce4cff2be915
--- /dev/null
+++ b/lib_util/ivas_bpool.h
@@ -0,0 +1,57 @@
+/******************************************************************************************************
+
+ (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository. All Rights Reserved.
+
+ This software is protected by copyright law and by international treaties.
+ The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository retain full ownership rights in their respective contributions in
+ the software. This notice grants no license of any kind, including but not limited to patent
+ license, nor is any license granted by implication, estoppel or otherwise.
+
+ Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
+ contributions.
+
+ This software is provided "AS IS", without any express or implied warranties. The software is in the
+ development stage. It is intended exclusively for experts who have experience with such software and
+ solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
+ and fitness for a particular purpose are hereby disclaimed and excluded.
+
+ Any dispute, controversy or claim arising under or in relation to providing this software shall be
+ submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
+ accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
+ the United Nations Convention on Contracts on the International Sales of Goods.
+
+*******************************************************************************************************/
+
+#ifndef IVAS_BPOOL_H
+#define IVAS_BPOOL_H
+
+#include
+#include "common_api_types.h"
+
+/* Forward declaraiton of opaque buffer pool handle */
+typedef struct BPOOL *BPOOL_HANDLE;
+
+/* Create a buffer pool with given element size and max number of buffers */
+ivas_error BPOOL_Create( BPOOL_HANDLE *pHandle, size_t bufferSize, uint32_t numBuffers );
+
+/* Destroy the buffer pool and all free-up all allocated memory */
+void BPOOL_Destroy( BPOOL_HANDLE *pHandle );
+
+/* request a buffer from the pool */
+ivas_error BPOOL_GetBuffer( BPOOL_HANDLE handle, void **dataPtr );
+
+/* return the buffer back to pool */
+ivas_error BPOOL_FreeBuffer( BPOOL_HANDLE handle, void *dataPtr );
+
+/* return the number of free buffers available atm in the pool */
+uint32_t BPOOL_AvailableBuffers( BPOOL_HANDLE handle );
+
+#endif /* IVAS_BPOOL_H */
diff --git a/lib_util/ivas_queue.c b/lib_util/ivas_queue.c
new file mode 100644
index 0000000000000000000000000000000000000000..c5806e0307234def2bf5aed26e2f10d3279864d3
--- /dev/null
+++ b/lib_util/ivas_queue.c
@@ -0,0 +1,138 @@
+/******************************************************************************************************
+
+ (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository. All Rights Reserved.
+
+ This software is protected by copyright law and by international treaties.
+ The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository retain full ownership rights in their respective contributions in
+ the software. This notice grants no license of any kind, including but not limited to patent
+ license, nor is any license granted by implication, estoppel or otherwise.
+
+ Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
+ contributions.
+
+ This software is provided "AS IS", without any express or implied warranties. The software is in the
+ development stage. It is intended exclusively for experts who have experience with such software and
+ solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
+ and fitness for a particular purpose are hereby disclaimed and excluded.
+
+ Any dispute, controversy or claim arising under or in relation to providing this software shall be
+ submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
+ accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
+ the United Nations Convention on Contracts on the International Sales of Goods.
+
+*******************************************************************************************************/
+
+#include
+#include
+#include "ivas_queue.h"
+#include "ivas_error_utils.h"
+#include "mutex.h"
+
+struct QUEUE
+{
+ mtx_t lock;
+ NODE *front;
+ NODE *back;
+ uint32_t size;
+};
+
+ivas_error QUEUE_Create( QUEUE_HANDLE *pHandle )
+{
+ QUEUE_HANDLE handle = NULL;
+ *pHandle = NULL;
+
+ if ( pHandle == NULL )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Invalid pointer to Buffer Pool Handle" );
+ }
+
+ handle = calloc( 1, sizeof( struct QUEUE ) );
+ if ( handle != NULL )
+ {
+ mtx_init( &handle->lock, 0 );
+ }
+
+ *pHandle = handle;
+ return IVAS_ERR_OK;
+}
+
+/* Destroy the queue and free-up all allocated memory */
+void QUEUE_Destroy( QUEUE_HANDLE *pHandle )
+{
+ if ( ( pHandle != NULL ) && ( *pHandle != NULL ) )
+ {
+ mtx_destroy( &( *pHandle )->lock );
+ free( *pHandle );
+ *pHandle = NULL;
+ }
+}
+
+void QUEUE_Push( QUEUE_HANDLE handle, NODE *node )
+{
+ mtx_lock( &handle->lock );
+ if ( handle->back == NULL )
+ {
+ handle->front = node;
+ }
+ else
+ {
+ handle->back->next = node;
+ }
+ handle->back = node;
+ handle->size++;
+ mtx_unlock( &handle->lock );
+}
+
+/* return the buffer back to pool */
+NODE *QUEUE_Pop( QUEUE_HANDLE handle )
+{
+ NODE *node;
+ mtx_lock( &handle->lock );
+ node = handle->front;
+ handle->front = handle->front->next;
+ if ( NULL == handle->front )
+ {
+ handle->back = NULL;
+ }
+ handle->size--;
+ mtx_unlock( &handle->lock );
+ return node;
+}
+
+/* returns the first element in the queue */
+NODE *QUEUE_Front( QUEUE_HANDLE handle )
+{
+ NODE *node;
+ mtx_lock( &handle->lock );
+ node = handle->front;
+ mtx_unlock( &handle->lock );
+ return node;
+}
+
+/* returns the last element in the queue */
+NODE *QUEUE_Back( QUEUE_HANDLE handle )
+{
+ NODE *node;
+ mtx_lock( &handle->lock );
+ node = handle->back;
+ mtx_unlock( &handle->lock );
+ return node;
+}
+
+/* return the number of elements in the queue */
+size_t QUEUE_Size( QUEUE_HANDLE handle )
+{
+ uint32_t numNodes;
+ mtx_lock( &handle->lock );
+ numNodes = handle->size;
+ mtx_unlock( &handle->lock );
+ return numNodes;
+}
diff --git a/lib_util/ivas_queue.h b/lib_util/ivas_queue.h
new file mode 100644
index 0000000000000000000000000000000000000000..1c6b77504e501b6f0ec680839cd52f3f10b04ca8
--- /dev/null
+++ b/lib_util/ivas_queue.h
@@ -0,0 +1,68 @@
+/******************************************************************************************************
+
+ (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository. All Rights Reserved.
+
+ This software is protected by copyright law and by international treaties.
+ The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository retain full ownership rights in their respective contributions in
+ the software. This notice grants no license of any kind, including but not limited to patent
+ license, nor is any license granted by implication, estoppel or otherwise.
+
+ Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
+ contributions.
+
+ This software is provided "AS IS", without any express or implied warranties. The software is in the
+ development stage. It is intended exclusively for experts who have experience with such software and
+ solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
+ and fitness for a particular purpose are hereby disclaimed and excluded.
+
+ Any dispute, controversy or claim arising under or in relation to providing this software shall be
+ submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
+ accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
+ the United Nations Convention on Contracts on the International Sales of Goods.
+
+*******************************************************************************************************/
+
+#ifndef IVAS_QUEUE_H
+#define IVAS_QUEUE_H
+
+#include
+#include "common_api_types.h"
+
+typedef struct NODE
+{
+ struct NODE *next;
+} NODE;
+
+/* Forward declaraiton of opaque queue handle */
+typedef struct QUEUE *QUEUE_HANDLE;
+
+/* Create a queue with given element size and max number of buffers */
+ivas_error QUEUE_Create( QUEUE_HANDLE *pHandle );
+
+/* Destroy the queue and all free-up all allocated memory */
+void QUEUE_Destroy( QUEUE_HANDLE *pHandle );
+
+/* push a buffer to a queue */
+void QUEUE_Push( QUEUE_HANDLE handle, NODE *data );
+
+/* pop the buffer from the front */
+NODE *QUEUE_Pop( QUEUE_HANDLE handle );
+
+/* returns the first element from the front */
+NODE *QUEUE_Front( QUEUE_HANDLE handle );
+
+/* returns the last element from the back */
+NODE *QUEUE_Back( QUEUE_HANDLE handle );
+
+/* return the number of elements in the queue */
+size_t QUEUE_Size( QUEUE_HANDLE handle );
+
+#endif /* IVAS_QUEUE_H */
diff --git a/lib_util/ivas_rtp_api.h b/lib_util/ivas_rtp_api.h
new file mode 100644
index 0000000000000000000000000000000000000000..06c56cd70f15f1ec34f4d5a464f1fe572d5e1a07
--- /dev/null
+++ b/lib_util/ivas_rtp_api.h
@@ -0,0 +1,591 @@
+/******************************************************************************************************
+
+ (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository. All Rights Reserved.
+
+ This software is protected by copyright law and by international treaties.
+ The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository retain full ownership rights in their respective contributions in
+ the software. This notice grants no license of any kind, including but not limited to patent
+ license, nor is any license granted by implication, estoppel or otherwise.
+
+ Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
+ contributions.
+
+ This software is provided "AS IS", without any express or implied warranties. The software is in the
+ development stage. It is intended exclusively for experts who have experience with such software and
+ solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
+ and fitness for a particular purpose are hereby disclaimed and excluded.
+
+ Any dispute, controversy or claim arising under or in relation to providing this software shall be
+ submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
+ accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
+ the United Nations Convention on Contracts on the International Sales of Goods.
+
+*******************************************************************************************************/
+
+#ifndef IVAS_RTP_API_H
+#define IVAS_RTP_API_H
+
+#include
+#include
+#include "common_api_types.h"
+
+/*
+ * +-----------------------+---------------------+--------------------+----------+
+ * | RTP Header (+ HDREXT) | payload header | frame data | PI data |
+ * +-----------------------+---------------------+--------------------+----------+
+ * \--------------------\ /------------------------------/
+ * IVAS payload
+ *
+ * This api provides a mechanism to generate/unpack the IVAS payload. The RTP Header
+ * and header extension fields must be handled by caller.
+ *
+ * IVAS General Payload structure
+ * ===============================
+ *
+ * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+ * H H H H F H
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |1| T | D |1| ET1 |x x x x|1| ET2 |x x x x|0|1|0 1| BR |1| ET3 |x x x x|…
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * \--------------/\--------------/\--------------/\--------------/\--------------/
+ * Initial E byte Subsqnt Ebyte1 Subsqnt Ebyte2 ToC1 Subsqnt Ebyte3
+ *
+ * H F
+ * +-+-+-+-+-+-+-+-+-------------------------- ---+-------------------- ---+-------+
+ * …|0|0|0 1| BR | IVAS frame 1 ... | IVAS frame 2 ... |PI data|
+ * +-+-+-+-+-+-+-+-+-------------------------- ---+-------------------- ---+-------+
+ * \--------------/
+ * ToC2
+ *
+ */
+
+#define IVAS_MAX_FRAMES_PER_RTP_PACKET ( 8 ) /* Max supported frames per RTP packet */
+
+/* It is difficult to decide the RTP Payload buffer's capacity intrinsically however computing
+ * using the maximum frame size and all currently supported PI data present gives a crude
+ * estimate or RTP packet size per frame. The additional PI data is assumed to add upto a 20%
+ * overhead in bitrate for the computation.
+ */
+#define NOMINAL_BUFFER_SIZE( numFramesPerPacket ) ( ( IVAS_MAX_BITS_PER_FRAME + ( IVAS_MAX_BITS_PER_FRAME / 5 ) ) * ( numFramesPerPacket ) / 8 )
+
+#define DEFAULT_MAX_PACKET_BYTES ( 1400 ) /* Typical MTU size of 4G/5G Cellular Interfaces */
+
+#define NO_BITRATE_REQ ( 0u ) /* If no bitrate is requested from remote */
+
+/* IVAS Codec Types */
+typedef enum
+{
+ IVAS_RTP_EVS, /* EVS */
+ IVAS_RTP_IVAS /* IVAS */
+} IVAS_RTP_CODEC;
+
+/* IVAS Bandwidth Requests */
+typedef enum
+{
+ IVAS_BANDWIDTH_NB, /* Narrowband */
+ IVAS_BANDWIDTH_WB, /* Wideband*/
+ IVAS_BANDWIDTH_SWB, /* SuperWideband*/
+ IVAS_BANDWIDTH_FB, /* Fullband */
+ IVAS_BANDWIDTH_NO_REQ, /* No Preference */
+} IVAS_RTP_BANDWIDTH;
+
+/* Channel Aware Coding */
+typedef enum
+{
+ IVAS_RTP_CA_LO_O2, /* FER=LO, OFFSET=2 */
+ IVAS_RTP_CA_LO_O3, /* FER=LO, OFFSET=3 */
+ IVAS_RTP_CA_LO_O5, /* FER=LO, OFFSET=5 */
+ IVAS_RTP_CA_LO_O7, /* FER=LO, OFFSET=7 */
+ IVAS_RTP_CA_HI_O2, /* FER=HI, OFFSET=2 */
+ IVAS_RTP_CA_HI_O3, /* FER=HI, OFFSET=3 */
+ IVAS_RTP_CA_HI_O5, /* FER=HI, OFFSET=5 */
+ IVAS_RTP_CA_HI_O7, /* FER=HI, OFFSET=7 */
+ IVAS_RTP_CA_NO_REQ /* No request */
+} IVAS_RTP_CA_MODE;
+
+/* Coded Format Requests */
+typedef enum
+{
+ IVAS_FMT_STEREO, /* Stereo */
+ IVAS_FMT_SBA, /* Scene Based Audio */
+ IVAS_FMT_MASA, /* Metadata Assisted Spatial Audio */
+ IVAS_FMT_ISM, /* Object Based Audio */
+ IVAS_FMT_MC, /* Multichannel Audio */
+ IVAS_FMT_OMASA, /* Object + MASA */
+ IVAS_FMT_OSBA, /* Object + SBA */
+ IVAS_FMT_NO_REQ, /* No preference */
+} IVAS_RTP_FORMAT;
+
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+/* Coded Subformat Requests */
+typedef enum
+{
+ IVAS_SUBFMT_FOA_PLANAR,
+ IVAS_SUBFMT_HOA2_PLANAR,
+ IVAS_SUBFMT_HOA3_PLANAR,
+ IVAS_SUBFMT_FOA,
+ IVAS_SUBFMT_HOA2,
+ IVAS_SUBFMT_HOA3,
+ IVAS_SUBFMT_MASA1,
+ IVAS_SUBFMT_MASA2,
+ IVAS_SUBFMT_ISM1,
+ IVAS_SUBFMT_ISM2,
+ IVAS_SUBFMT_ISM3,
+ IVAS_SUBFMT_ISM4,
+ IVAS_SUBFMT_ISM1_EXTENDED_METADATA,
+ IVAS_SUBFMT_ISM2_EXTENDED_METADATA,
+ IVAS_SUBFMT_ISM3_EXTENDED_METADATA,
+ IVAS_SUBFMT_ISM4_EXTENDED_METADATA,
+ IVAS_SUBFMT_MC_5_1,
+ IVAS_SUBFMT_MC_7_1,
+ IVAS_SUBFMT_MC_5_1_2,
+ IVAS_SUBFMT_MC_5_1_4,
+ IVAS_SUBFMT_MC_7_1_4,
+ IVAS_SUBFMT_RESERVED_21,
+ IVAS_SUBFMT_RESERVED_22,
+ IVAS_SUBFMT_RESERVED_23,
+ IVAS_SUBFMT_RESERVED_24,
+ IVAS_SUBFMT_RESERVED_25,
+ IVAS_SUBFMT_RESERVED_26,
+ IVAS_SUBFMT_RESERVED_27,
+ IVAS_SUBFMT_RESERVED_28,
+ IVAS_SUBFMT_RESERVED_29,
+ IVAS_SUBFMT_RESERVED_30,
+ IVAS_SUBFMT_RESERVED_31,
+ IVAS_SUBFMT_OMASA_ISM1_1TC,
+ IVAS_SUBFMT_OMASA_ISM2_1TC,
+ IVAS_SUBFMT_OMASA_ISM3_1TC,
+ IVAS_SUBFMT_OMASA_ISM4_1TC,
+ IVAS_SUBFMT_OMASA_ISM1_2TC,
+ IVAS_SUBFMT_OMASA_ISM2_2TC,
+ IVAS_SUBFMT_OMASA_ISM3_2TC,
+ IVAS_SUBFMT_OMASA_ISM4_2TC,
+ IVAS_SUBFMT_OSBA_ISM1_FOA_PLANAR,
+ IVAS_SUBFMT_OSBA_ISM2_FOA_PLANAR,
+ IVAS_SUBFMT_OSBA_ISM3_FOA_PLANAR,
+ IVAS_SUBFMT_OSBA_ISM4_FOA_PLANAR,
+ IVAS_SUBFMT_OSBA_ISM1_FOA,
+ IVAS_SUBFMT_OSBA_ISM2_FOA,
+ IVAS_SUBFMT_OSBA_ISM3_FOA,
+ IVAS_SUBFMT_OSBA_ISM4_FOA,
+ IVAS_SUBFMT_OSBA_ISM1_HOA2_PLANAR,
+ IVAS_SUBFMT_OSBA_ISM2_HOA2_PLANAR,
+ IVAS_SUBFMT_OSBA_ISM3_HOA2_PLANAR,
+ IVAS_SUBFMT_OSBA_ISM4_HOA2_PLANAR,
+ IVAS_SUBFMT_OSBA_ISM1_HOA2,
+ IVAS_SUBFMT_OSBA_ISM2_HOA2,
+ IVAS_SUBFMT_OSBA_ISM3_HOA2,
+ IVAS_SUBFMT_OSBA_ISM4_HOA2,
+ IVAS_SUBFMT_OSBA_ISM1_HOA3_PLANAR,
+ IVAS_SUBFMT_OSBA_ISM2_HOA3_PLANAR,
+ IVAS_SUBFMT_OSBA_ISM3_HOA3_PLANAR,
+ IVAS_SUBFMT_OSBA_ISM4_HOA3_PLANAR,
+ IVAS_SUBFMT_OSBA_ISM1_HOA3,
+ IVAS_SUBFMT_OSBA_ISM2_HOA3,
+ IVAS_SUBFMT_OSBA_ISM3_HOA3,
+ IVAS_SUBFMT_OSBA_ISM4_HOA3,
+ IVAS_SUBFMT_NO_REQ
+} IVAS_RTP_SUBFORMAT;
+
+/* Split Rendering Requests */
+typedef struct
+{
+ uint32_t valid : 1; /* is split rendering request valid */
+ uint32_t diegetic : 1; /* enabling diegetic support for Split Rendering */
+ uint32_t yaw : 1; /* transmission metadata for correction around yaw axis */
+ uint32_t pitch : 1; /* transmission metadata for correction around pitch axis */
+ uint32_t roll : 1; /* transmission metadata for correction around roll axis */
+ uint32_t reserved : 27; /* reserved */
+} IVAS_RTP_SPLITRENDER;
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+
+/* Remote Requests Types (Keys) */
+typedef enum
+{
+ IVAS_REQUEST_CODEC, /* Request codec type, value of type IVAS_RTP_CODEC */
+ IVAS_REQUEST_BITRATE, /* Request bitrate, value of type uint32_t in kbps */
+ IVAS_REQUEST_BANDWIDTH, /* Request bandwidth, value of type IVAS_RTP_BANDWIDTH */
+ IVAS_REQUEST_FORMAT, /* Request format, value of type IVAS_RTP_FORMAT */
+ IVAS_REQUEST_CA_MODE, /* Request channel awareness, value of type IVAS_RTP_CA_MODE */
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ IVAS_REQUEST_SUBFORMAT, /* Request subFormat, value of type IVAS_RTP_SUBFORMAT */
+ IVAS_REQUEST_SR_CONFIG, /* Request spit rendering, value of type IVAS_RTP_SPLITRENDER */
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+ IVAS_REQUEST_MAX /* Max number of requests */
+} IVAS_RTP_REQUEST_TYPE;
+
+/* Remote Request Values */
+typedef union
+{
+ uint32_t bitrate; /* bitrate in kbps when request type is IVAS_REQUEST_BITRATE */
+ IVAS_RTP_CODEC codec; /* codec id when request type is IVAS_REQUEST_CODEC */
+ IVAS_RTP_BANDWIDTH bandwidth; /* badwidth when request type is IVAS_REQUEST_BANDWIDTH */
+ IVAS_RTP_FORMAT formatType; /* format type when request type is IVAS_REQUEST_FORMAT */
+ IVAS_RTP_CA_MODE caMode; /* channel aware mode when request type is IVAS_REQUEST_CA_MODE */
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ IVAS_RTP_SUBFORMAT subFormatType; /* sub-format type when request type is IVAS_REQUEST_SUBFORMAT */
+ IVAS_RTP_SPLITRENDER srConfig; /* split rendering config when request type is IVAS_REQUEST_SR_CONFIG */
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+} IVAS_RTP_REQUEST_VALUE;
+
+/* Template for pi data types, all defined pi data follow this template
+ * for example scene orientation pi data can be represented as :-
+ *
+ * typedef struct {
+ * size_t size; // sizeof(IVAS_PIDATA_SCENE_ORIENTATION)
+ * uint32_t piDataType; // IVAS_PI_SCENE_ORIENTATION
+ * float w, x, y, z; // pi data of scene orientation in quaternions
+ * } IVAS_PIDATA_SCENE_ORIENTATION;
+ *
+ */
+typedef struct
+{
+ size_t size; /* size of this structure */
+ uint32_t piDataType; /* IVAS PI data type */
+ uint8_t data[1]; /* Variable length array */
+} IVAS_PIDATA_GENERIC;
+
+/* Generic data buffer for sending/receiving coded frames / rtp payloads
+ * data buffer is owned and initialized by caller, rtp api will ensure
+ * buffer write does not exceed capacity.
+ */
+typedef struct
+{
+ size_t capacity; /* allocated size of the data buffer */
+ size_t length; /* length of the initialized data in the buffer */
+ uint8_t *buffer; /* pointer to the payload buffer */
+} IVAS_DATA_BUFFER;
+
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+typedef enum
+{
+ IVAS_SR_TRANSPORT_LCLD,
+ IVAS_SR_TRANSPORT_LC3PLUS
+} IVAS_RTP_SR_TRANSPORT;
+
+typedef struct
+{
+ bool valid; /* Valid Split Rendering Info for/in the ToC */
+ bool diegetic; /* SR content digetic */
+ IVAS_RTP_SR_TRANSPORT codec; /* SR Transport Codec used*/
+} IVAS_RTP_SR_INFO;
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+
+/**********************************************/
+/* IVAS RTP PACKER API */
+/**********************************************/
+
+/* Forward declaration of rtp pack/unpack handle types */
+typedef struct IVAS_RTP_PACK *IVAS_RTP_PACK_HANDLE; /* rtp packer handle type */
+
+/* Initial configuration for rtp packer
+ * - maxFramesPerPacket is used to define if more than one frame should be packed
+ * in the same rtp packet. If zero, will use IVAS_MAX_FRAMES_PER_RTP_PACKET.
+ */
+typedef struct
+{
+ uint32_t maxFramesPerPacket; /* maximum no of frame per packet desired during the session */
+} IVAS_RTP_PACK_CONFIG;
+
+/* Open an instance of the RTP packer and return a handle to rtp packer on success
+ * error code is retured on failure and handle is set to NULL
+ */
+ivas_error IVAS_RTP_PACK_Open(
+ IVAS_RTP_PACK_HANDLE *phIvasPack, /* i/o: pointer to an IVAS rtp packer handle to be opened */
+ const IVAS_RTP_PACK_CONFIG *config /* i : pointer to initial config for RTP Packer */
+);
+
+/* Close and free an existing instance of rtp packer */
+void IVAS_RTP_PACK_Close(
+ IVAS_RTP_PACK_HANDLE *phIvasPack /* i/o : pointer to an IVAS rtp packer handle to be closed */
+);
+
+/* Update the RTP Header structure */
+ivas_error IVAS_RTP_PACK_UpdateHeader(
+ IVAS_RTP_PACK_HANDLE hIvasPack, /* i/o: pointer to an IVAS rtp packer handle to be opened */
+ uint32_t ssrc, /* i : Unique 32-bit Source ID as negotiated during SDP */
+ uint8_t numCC, /* i : numCC indicates no. of contributing sources */
+ uint32_t *csrc, /* i : SSRCs of contributing Sources for a mixed stream */
+ uint16_t extHeaderId, /* i : extension header ID */
+ uint16_t numExtensionBytes, /* i : length of the extension data */
+ uint8_t *extData /* i : extension data pointer */
+);
+
+/* Add requests for remote sender using a key value pair api
+ * each key must be provided with a corresponding value type
+ *
+ * Cross validation of some key,value pairs will not be done
+ * in this API. E.g. Codec ID and supported bitrates/bandwidths
+ * will not be performed at this level.
+ */
+ivas_error IVAS_RTP_PACK_PushRemoteRequest(
+ IVAS_RTP_PACK_HANDLE hIvasPack, /* i/o : IVAS rtp packer handle */
+ IVAS_RTP_REQUEST_TYPE reqType, /* i : remote request type */
+ IVAS_RTP_REQUEST_VALUE reqValue /* i : value of the requested type */
+);
+
+/* Push a single IVAS/EVS frame to rtp packer
+ *
+ * If multiple frames per RTP packet are desired, multiple frames must be explicitly
+ * pushed before a call to IVAS_RTP_PACK_GetPayload to generate a rtp payload.
+ *
+ * It is possible to have variable frames per packet until maxFramesPerPacket frames
+ * if IVAS_RTP_PACK_GetPayload is invoked asyncronously w.r.t this api.
+ *
+ */
+ivas_error IVAS_RTP_PACK_PushFrame(
+ IVAS_RTP_PACK_HANDLE hIvasPack, /* i/o : IVAS rtp packer handle */
+ IVAS_RTP_CODEC codecId, /* i : Codec type (IVAS/EVS) */
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ IVAS_RTP_SR_INFO *srInfo, /* i : Split Rendering Info (NULL if absent) */
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+ const IVAS_DATA_BUFFER *frameBuffer /* i : packed frame bitstream for IVAS/EVS */
+);
+
+/* Get the number of frames in the FiFo currently */
+uint32_t IVAS_RTP_PACK_GetNumFrames(
+ IVAS_RTP_PACK_HANDLE hIvasPack /* i/o : IVAS rtp packer handle */
+);
+
+/* Push single PI data to rtp packer
+ *
+ * Provide PI data for a current RTP packet. All PI data is locally cached in the packer
+ * and set to the rtp payload with policy defined in initial configuration during call to
+ * IVAS_RTP_PACK_GetPayload.
+ *
+ */
+ivas_error IVAS_RTP_PACK_PushPiData(
+ IVAS_RTP_PACK_HANDLE hIvasPack, /* i/o : IVAS rtp packer handle */
+ const IVAS_PIDATA_GENERIC *data /* i : pointer to the PIData stucture */
+);
+
+/* Generate a rtp payload using available pushed frames
+ *
+ * Available remote requests, pi data and frames will be packed into a rtp payload. The
+ * capacity field of payload is used to limits the maximum RTP packet size.
+ *
+ */
+ivas_error IVAS_RTP_PACK_GetPayload(
+ IVAS_RTP_PACK_HANDLE hIvasPack, /* i/o : IVAS rtp packer handle */
+ IVAS_DATA_BUFFER *payload, /* o : encapsulated rtp payload */
+ uint32_t *numFramesInPayload /* o : no. of frames in payload */
+);
+
+/* Generate a rtp packet using available pushed frames
+ *
+ * Available remote requests, pi data and frames will be packed into a rtp packet. If no
+ * frame is pushed before call to this api, NO_DATA_FRAME will be generated
+ * Takes care of updates to the RTP Header
+ *
+ */
+ivas_error IVAS_RTP_PACK_GetPacket(
+ IVAS_RTP_PACK_HANDLE hIvasPack, /* i/o : IVAS rtp packer handle */
+ IVAS_DATA_BUFFER *packet, /* o : encapsulated rtp packet */
+ uint32_t *numFramesInPacket /* o : no. of frames in packet */
+);
+
+/**********************************************/
+/* IVAS RTP UNPACKER API */
+/**********************************************/
+
+/* Forward declaration of rtp unpack handle types */
+typedef struct IVAS_RTP_UNPACK *IVAS_RTP_UNPACK_HANDLE; /* rtp unpacker handle type */
+
+/* Initial configuration for rtp unpacker
+ * - maxFramesPerPacket is used to define maximum supported frames per rtp packet
+ * to allow for internal memory allocaton, if zero, will use IVAS_MAX_FRAMES_PER_RTP_PACKET
+ */
+
+typedef struct
+{
+ uint32_t maxFramesPerPacket; /* maximum no of frame per packet expected during the session */
+} IVAS_RTP_UNPACK_CONFIG;
+
+/* Open an instance of the RTP unpacker and return a handle to rtp unpacker on success
+ * error code is retured on failure and handle is set to NULL
+ */
+ivas_error IVAS_RTP_UNPACK_Open(
+ IVAS_RTP_UNPACK_HANDLE *phIvasUnpack, /* i/o : rtp unpacker handle */
+ const IVAS_RTP_UNPACK_CONFIG *config /* i : initial configuration for rtp unpacker */
+);
+
+/* Close and free an existing instance of rtp unpacker */
+void IVAS_RTP_UNPACK_Close(
+ IVAS_RTP_UNPACK_HANDLE *phIvasUnpack /* i/o : IVAS rtp unpacker handle */
+);
+
+/* Push a received rtp Ivas Payload to unpacker to extract number of frames, pi data and
+ * any remote request present in the payload. Caller must extract RTP header and header
+ * extension and feed Ivas Payload alongwith RTP Timestamp and sequence number.
+ *
+ * In case of DTX transmission modes, the number of frames in packet will be reduced by
+ * the number of NO_DATA frame received. All PullFrame calls for non NO_DATA frames shall
+ * be reported with timestamp jump indicating missing/NO_DATA IVAS frames.
+ *
+ * It is important to ensure IVAS_RTP_UNPACK_PushPayload, IVAS_RTP_UNPACK_PullFrame and
+ * IVAS_RTP_UNPACK_PullNextPiData API are invoked in same thread context or in a thread
+ * safe manner else a race condition can arise if new packet is pushed while frames/pidata
+ * are still being pulled out. The IVAS_RTP_UNPACK_PushPayload will gererate an error if
+ * new paylod is pushed before all frames/pidata are pulled out.
+ *
+ * Example usage : -
+ * ==================
+ * err = IVAS_RTP_UNPACK_PushPayload(hIvasUnpack, payload, rtpTs, seqNum,
+ * &nFrames, &nPiData, &reqBitmap);
+ * if (err != IVAS_ERR_OK) { return err; }
+ *
+ * // Read the frames in payload and feed to decoder
+ * while (nFrames-- > 0) {
+ * err = IVAS_RTP_UNPACK_PullFrame(hIvasUnpack, &recCodecId, &srInfo, &frame, &frameTs, &seqNum, &SpeechLostIndicated);
+ * if (err != IVAS_ERR_OK) { return err; }
+ * err = IVAS_DEC_VoIP_FeedFrame(hIvasDec, frame.buffer, frame.length, seqNum, frameTs, rcvTime, isGoodFrame);
+ * if (err != IVAS_ERR_OK) { return err; }
+ * }
+ *
+ * // Read PI Data
+ * while (nPiData-- > 0) {
+ * err = IVAS_RTP_UNPACK_PullNextPiData(hIvasUnpack, &piData, &piTs);
+ * if (err != IVAS_ERR_OK) { return err; }
+ * // handle pi data based on fwd/rev pi data types
+ * handlePIData(&piData, piTs)
+ * }
+ *
+ * // Read remote requests
+ * for (req = 0; req < IVAS_REQUEST_MAX; req++) {
+ * if (reqBitmap & (1u << req)) {
+ * err = IVAS_RTP_UNPACK_GetRequest(hIvasUnpack, req, &value);
+ * if (err != IVAS_ERR_OK) { return err; }
+ * switch(req) {
+ * case IVAS_REQUEST_CODEC : handleCodec(value.codec); break;
+ * case IVAS_REQUEST_BITRATE : handleBitrate(value.bitrate); break;
+ * case IVAS_REQUEST_BANDWIDTH : handleBandwidth(value.bandwidth); break;
+ * case IVAS_REQUEST_FORMAT : handleFormat(value.formatType); break;
+ * case IVAS_REQUEST_SUBFORMAT : handleSubFormat(value.subFormatType); break;
+ * case IVAS_REQUEST_CA_MODE : handleCAModevalue.caMode); break;
+ * case IVAS_REQUEST_SR_CONFIG : handleSRConfig(value.srConfig); break;
+ * }
+ * }
+ * }
+ *
+ */
+ivas_error IVAS_RTP_UNPACK_PushPayload(
+ IVAS_RTP_UNPACK_HANDLE hIvasUnpack, /* i/o : IVAS rtp unpacker handle */
+ const IVAS_DATA_BUFFER *payload, /* i : received rtp payload */
+ uint32_t timestamp, /* i : timestamp in RTP Clock @ 16KHz from rtp header */
+ uint16_t sequenceNumber, /* i : sequence number from rtp header */
+ uint32_t *numFramesInPacket, /* o : number of IVAS/EVS frames in rtp packet */
+ uint32_t *numPiDataInPacket, /* o : number of PI data received in rtp packet */
+ uint32_t *remoteRequestBitmap /* o : bitmap of available request in this packet */
+);
+
+/* Push a received rtp Ivas Packet to unpacker to extract number of frames, pi data and
+ * any remote request present in the Packet.
+ *
+ * In case of DTX transmission modes, the number of frames in packet will be reduced by
+ * the number of NO_DATA frame received. All PullFrame calls for non NO_DATA frames shall
+ * be reported with timestamp jump indicating missing/NO_DATA IVAS frames.
+ *
+ * It is important to ensure IVAS_RTP_UNPACK_PushPacket, IVAS_RTP_UNPACK_PullFrame and
+ * IVAS_RTP_UNPACK_PullNextPiData API are invoked in same thread context or in a thread
+ * safe manner else a race condition can arise if new packet is pushed while frames/pidata
+ * are still being pulled out. The IVAS_RTP_UNPACK_PushPacket will gererate an error if
+ * new packet is pushed before all frames/pidata are pulled out.
+ *
+ * Example usage : -
+ * ==================
+ * err = IVAS_RTP_UNPACK_PushPacket(hIvasUnpack, packet, &nFrames, &nPiData, &reqBitmap);
+ * if (err != IVAS_ERR_OK) { return err; }
+ *
+ * // Read the frames in packet and feed to decoder
+ * while (nFrames-- > 0) {
+ * err = IVAS_RTP_UNPACK_PullFrame(hIvasUnpack, &recCodecId, &srInfo, &frame, &frameTs, &seqNum, &SpeechLostIndicated);
+ * if (err != IVAS_ERR_OK) { return err; }
+ * err = IVAS_DEC_VoIP_FeedFrame(hIvasDec, frame.buffer, frame.length, seqNum, frameTs, rcvTime, isGoodFrame);
+ * if (err != IVAS_ERR_OK) { return err; }
+ * }
+ *
+ * // Read PI Data
+ * while (nPiData-- > 0) {
+ * err = IVAS_RTP_UNPACK_PullNextPiData(hIvasUnpack, &piData, &piTs);
+ * if (err != IVAS_ERR_OK) { return err; }
+ * // handle pi data based on fwd/rev pi data types
+ * handlePIData(&piData, piTs)
+ * }
+ *
+ * // Read remote requests
+ * for (req = 0; req < IVAS_REQUEST_MAX; req++) {
+ * if (reqBitmap & (1u << req)) {
+ * err = IVAS_RTP_UNPACK_GetRequest(hIvasUnpack, req, &value);
+ * if (err != IVAS_ERR_OK) { return err; }
+ * switch(req) {
+ * case IVAS_REQUEST_CODEC : handleCodec(value.codec); break;
+ * case IVAS_REQUEST_BITRATE : handleBitrate(value.bitrate); break;
+ * case IVAS_REQUEST_BANDWIDTH : handleBandwidth(value.bandwidth); break;
+ * case IVAS_REQUEST_FORMAT : handleFormat(value.formatType); break;
+ * case IVAS_REQUEST_SUBFORMAT : handleSubFormat(value.subFormatType); break;
+ * case IVAS_REQUEST_CA_MODE : handleCAModevalue.caMode); break;
+ * case IVAS_REQUEST_SR_CONFIG : handleSRConfig(value.srConfig); break;
+ * }
+ * }
+ * }
+ */
+ivas_error IVAS_RTP_UNPACK_PushPacket(
+ IVAS_RTP_UNPACK_HANDLE hIvasUnpack, /* i/o : IVAS rtp unpacker handle */
+ const IVAS_DATA_BUFFER *packet, /* i : received rtp Packet */
+ uint32_t *numFramesInPacket, /* o : number of IVAS/EVS frames in rtp packet */
+ uint32_t *numPiDataInPacket, /* o : number of PI data received in rtp packet */
+ uint32_t *remoteRequestBitmap /* o : bitmap of available request in this packet */
+);
+
+/* Fetch requests from sender using a key value pair api
+ * each key must be provided with a corresponding value storage type
+ *
+ * On call to IVAS_RTP_UNPACK_PushPayload(), remoteRequestBitmap can be used
+ * an indicator of new request available this frame
+ *
+ */
+ivas_error IVAS_RTP_UNPACK_GetRequest(
+ IVAS_RTP_UNPACK_HANDLE hIvasUnpack, /* i/o : IVAS rtp packer handle */
+ IVAS_RTP_REQUEST_TYPE type, /* i : remote request type */
+ IVAS_RTP_REQUEST_VALUE *value /* o : pointer of the requested type */
+);
+
+/* Extract a single IVAS/EVS frame from provided rtp payload alongwith rtp timestamp
+ * and sequence number
+ *
+ * If multiple frames per RTP packet are available, multiple calls to IVAS_RTP_UNPACK_PullFrame
+ * are needed.
+ */
+ivas_error IVAS_RTP_UNPACK_PullFrame(
+ IVAS_RTP_UNPACK_HANDLE hIvasUnpack, /* i/o : IVAS rtp unpacker handle */
+ IVAS_RTP_CODEC *receivedCodecId, /* o : Codec type (IVAS/EVS) */
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ IVAS_RTP_SR_INFO *srInfo, /* o : Split Rendering Info */
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+ IVAS_DATA_BUFFER *frameBuffer, /* o : packed frame bitstream for IVAS/EVS */
+ int16_t *frameSizeInBits, /* o : exact frame size in bits (AMRWB IO) */
+ uint32_t *timestamp, /* o : timestamp in RTP Clock @ 16KHz */
+ uint16_t *sequenceNumber, /* o : sequence number from rtp header */
+ bool *speechLostIndicated, /* o : Is current frame indicated as Lost */
+ bool *isAMRWB_IOmode /* o : Is AMRWB_IO mode EVS frame */
+);
+
+/* Pull a single PI data from rtp unpacker instance for current packet
+ * Each Pi data is accompanied with a corresponding timestamp
+ */
+ivas_error IVAS_RTP_UNPACK_PullNextPiData(
+ IVAS_RTP_UNPACK_HANDLE hIvasUnpack, /* i/o : IVAS rtp unpacker handle */
+ IVAS_PIDATA_GENERIC *data, /* o : output data buffer for the Pi data */
+ size_t capacity, /* i : capacity of pi data buffer in bytes */
+ uint32_t *timestamp /* o : timestamp in RTP Clock @ 16KHz */
+);
+
+#endif /* IVAS_RTP_API_H */
diff --git a/lib_util/ivas_rtp_file.c b/lib_util/ivas_rtp_file.c
new file mode 100644
index 0000000000000000000000000000000000000000..0851a998f68b0282eb00bff28ff77d59e4a16fa8
--- /dev/null
+++ b/lib_util/ivas_rtp_file.c
@@ -0,0 +1,138 @@
+/******************************************************************************************************
+
+ (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository. All Rights Reserved.
+
+ This software is protected by copyright law and by international treaties.
+ The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository retain full ownership rights in their respective contributions in
+ the software. This notice grants no license of any kind, including but not limited to patent
+ license, nor is any license granted by implication, estoppel or otherwise.
+
+ Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
+ contributions.
+
+ This software is provided "AS IS", without any express or implied warranties. The software is in the
+ development stage. It is intended exclusively for experts who have experience with such software and
+ solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
+ and fitness for a particular purpose are hereby disclaimed and excluded.
+
+ Any dispute, controversy or claim arising under or in relation to providing this software shall be
+ submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
+ accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
+ the United Nations Convention on Contracts on the International Sales of Goods.
+
+*******************************************************************************************************/
+#include
+#include
+#include
+
+#include "ivas_rtp_file.h"
+#include "ivas_error_utils.h"
+
+struct IVAS_RTP_FILE
+{
+ bool isFileWriter;
+ FILE *f_rtpstream;
+};
+
+ivas_error IvasRtpFile_Open(
+ const char *filePath, /* i : path to CA config file */
+ bool isFileWriter, /* i : instance is a file writer else reader */
+ IVAS_RTP_FILE_HANDLE *phRtpFile /* o : pointer to an IVAS file reader handle */
+)
+{
+ const char *mode = isFileWriter ? "wb" : "rb";
+ FILE *f_rtpstream = fopen( filePath, mode );
+ if ( f_rtpstream == NULL )
+ {
+ return IVAS_ERROR( IVAS_ERR_FAILED_FILE_OPEN, "could not open: %s\n", filePath );
+ }
+
+ *phRtpFile = calloc( 1, sizeof( struct IVAS_RTP_FILE ) );
+ if ( *phRtpFile != NULL )
+ {
+ ( *phRtpFile )->isFileWriter = isFileWriter;
+ ( *phRtpFile )->f_rtpstream = f_rtpstream;
+ }
+
+ return IVAS_ERR_OK;
+}
+
+ivas_error IvasRtpFile_Close(
+ IVAS_RTP_FILE_HANDLE *phReader /* i : pointer to an IVAS file reader handle */
+)
+{
+ if ( phReader != NULL && *phReader != NULL )
+ {
+ if ( ( *phReader )->f_rtpstream != NULL )
+ {
+ fclose( ( *phReader )->f_rtpstream );
+ ( *phReader )->f_rtpstream = NULL;
+ }
+ free( *phReader );
+ *phReader = NULL;
+ }
+
+ return IVAS_ERR_OK;
+}
+
+ivas_error IvasRtpFile_Write(
+ IVAS_RTP_FILE_HANDLE hReader, /* i : pointer to an IVAS file writer handle */
+ const uint8_t *packet,
+ size_t numBytes )
+{
+ ivas_error error = IVAS_ERR_OK;
+ if ( hReader->isFileWriter )
+ {
+ uint32_t length = (uint32_t) numBytes; /* Max packet length is < 32 bits*/
+ fwrite( &length, sizeof( uint32_t ), 1, hReader->f_rtpstream );
+ fwrite( packet, sizeof( uint8_t ), numBytes, hReader->f_rtpstream );
+ }
+ else
+ {
+ error = IVAS_ERR_WRONG_PARAMS;
+ }
+ return error;
+}
+
+ivas_error IvasRtpFile_Read(
+ IVAS_RTP_FILE_HANDLE hReader, /* i : pointer to an IVAS file writer handle */
+ uint8_t *packet,
+ size_t *numBytes,
+ size_t capacity )
+{
+ size_t nread = 0;
+ uint32_t length = 0;
+ if ( hReader->isFileWriter )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "File open for writing cannot be read" );
+ }
+
+ nread = fread( &length, sizeof( uint32_t ), 1, hReader->f_rtpstream ); /* Read Packet Length */
+ if ( nread == 0 )
+ {
+ return IVAS_ERR_END_OF_FILE;
+ }
+
+ *numBytes = length;
+ if ( ( *numBytes ) > capacity )
+ {
+ fprintf( stderr, "RTP packet > buffer capacity %lu bytes\n", capacity );
+ return IVAS_ERR_INVALID_OUTPUT_BUFFER_SIZE;
+ }
+
+ nread = fread( packet, sizeof( uint8_t ), ( *numBytes ), hReader->f_rtpstream ); /* Read Packet */
+ if ( nread < ( *numBytes ) )
+ {
+ return IVAS_ERR_END_OF_FILE;
+ }
+
+ return IVAS_ERR_OK;
+}
diff --git a/lib_util/ivas_rtp_file.h b/lib_util/ivas_rtp_file.h
new file mode 100644
index 0000000000000000000000000000000000000000..3156c34cb21dd9fb64b85c5ba65e21ea980dbf51
--- /dev/null
+++ b/lib_util/ivas_rtp_file.h
@@ -0,0 +1,65 @@
+/******************************************************************************************************
+
+ (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository. All Rights Reserved.
+
+ This software is protected by copyright law and by international treaties.
+ The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository retain full ownership rights in their respective contributions in
+ the software. This notice grants no license of any kind, including but not limited to patent
+ license, nor is any license granted by implication, estoppel or otherwise.
+
+ Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
+ contributions.
+
+ This software is provided "AS IS", without any express or implied warranties. The software is in the
+ development stage. It is intended exclusively for experts who have experience with such software and
+ solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
+ and fitness for a particular purpose are hereby disclaimed and excluded.
+
+ Any dispute, controversy or claim arising under or in relation to providing this software shall be
+ submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
+ accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
+ the United Nations Convention on Contracts on the International Sales of Goods.
+
+*******************************************************************************************************/
+
+#ifndef IVAS_RTP_FILE_H
+#define IVAS_RTP_FILE_H
+
+#include
+#include
+#include "common_api_types.h"
+
+typedef struct IVAS_RTP_FILE *IVAS_RTP_FILE_HANDLE;
+
+ivas_error IvasRtpFile_Open(
+ const char *filePath, /* i : path to CA config file */
+ bool isFileWriter, /* i : instance is a file writer else reader */
+ IVAS_RTP_FILE_HANDLE *phRtpFile /* o : pointer to an IVAS file reader handle */
+);
+
+ivas_error IvasRtpFile_Close(
+ IVAS_RTP_FILE_HANDLE *phReader /* i : pointer to an IVAS file reader handle */
+);
+
+ivas_error IvasRtpFile_Write(
+ IVAS_RTP_FILE_HANDLE hReader, /* i : pointer to an IVAS file writer handle */
+ const uint8_t *packet, /* i : pointer to packed rtp packet */
+ size_t numBytes /* i : length of the packet in bytes */
+);
+
+ivas_error IvasRtpFile_Read(
+ IVAS_RTP_FILE_HANDLE hReader, /* i : pointer to an IVAS file writer handle */
+ uint8_t *packet, /* o : buffer where packet is to be written */
+ size_t *numBytes, /* o : length of the packet in bytes */
+ size_t capacity /* i : buffer capacity of the packet buffer */
+);
+
+#endif /* IVAS_RTP_FILE_H */
diff --git a/lib_util/ivas_rtp_internal.h b/lib_util/ivas_rtp_internal.h
new file mode 100644
index 0000000000000000000000000000000000000000..f3569ca031ac7c8c60d0d31efb7f60b06644f9d7
--- /dev/null
+++ b/lib_util/ivas_rtp_internal.h
@@ -0,0 +1,140 @@
+/******************************************************************************************************
+
+ (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository. All Rights Reserved.
+
+ This software is protected by copyright law and by international treaties.
+ The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository retain full ownership rights in their respective contributions in
+ the software. This notice grants no license of any kind, including but not limited to patent
+ license, nor is any license granted by implication, estoppel or otherwise.
+
+ Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
+ contributions.
+
+ This software is provided "AS IS", without any express or implied warranties. The software is in the
+ development stage. It is intended exclusively for experts who have experience with such software and
+ solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
+ and fitness for a particular purpose are hereby disclaimed and excluded.
+
+ Any dispute, controversy or claim arising under or in relation to providing this software shall be
+ submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
+ accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
+ the United Nations Convention on Contracts on the International Sales of Goods.
+
+*******************************************************************************************************/
+
+#ifndef IVAS_RTP_INTERNAL_H
+#define IVAS_RTP_INTERNAL_H
+
+#include "ivas_rtp_api.h"
+#include "ivas_rtp_pi_data.h"
+
+#ifdef IVAS_RTPDUMP
+
+enum MASK_BITS
+{
+ MASK_1BIT = 0x1,
+ MASK_2BIT = 0x3,
+ MASK_3BIT = 0x7,
+ MASK_4BIT = 0xF,
+ MASK_5BIT = 0x1F,
+ MASK_6BIT = 0x3F,
+ MASK_7BIT = 0x7F,
+ MASK_8BIT = 0xFF,
+
+};
+
+/* tables */
+#define NBITS_AEID ( 7 )
+#define NBITS_RT60 ( 5 )
+#define NBITS_DSR ( 6 )
+#define NBITS_DIM ( 4 )
+#define NBITS_ABS ( 2 )
+
+#define MASK_AEID ( ( 1u << NBITS_AEID ) - 1 )
+#define MASK_RT60 ( ( 1u << NBITS_RT60 ) - 1 )
+#define MASK_DSR ( ( 1u << NBITS_DSR ) - 1 )
+#define MASK_DIM ( ( 1u << NBITS_DIM ) - 1 )
+#define MASK_ABS ( ( 1u << NBITS_ABS ) - 1 )
+
+#define MAX_PI_POSITION_METERS ( 327.68f )
+#define FLOAT_FROM_Q15( q15Val ) ( (float) ( q15Val ) / 32768.0f )
+
+extern const float mapDSR[1u << NBITS_DSR];
+extern const float mapRT60[1u << NBITS_RT60];
+extern const float mapRoomDims[1u << NBITS_DIM];
+extern const float mapAbsorbtion[1u << NBITS_ABS];
+
+enum IVAS_RTP_HEADER_BITS
+{
+
+ EBYTE_TOC_HEADER_BIT = 0x80, /* Ebyte/ToC indicator H bit, 1 = EByte, 0 = ToC */
+
+ TOC_HEADER_FOLLOWS = 0x40, /* ToC header byte is followed by another header byte */
+ TOC_INDICATE_NO_DATA_AMRWB = 0x3F, /* ToC byte indicates NO_DATA in DTX mode for AMRWB */
+ TOC_INDICATE_NO_DATA = 0x0F, /* ToC header byte indicates NO_DATA in DTX mode */
+ TOC_INDICATE_IVAS = 0x10, /* ToC header byte indicates IVAS data */
+ TOC_INDICATE_EVS = 0x00, /* ToC header byte indicates EVS data */
+ TOC_INDICATE_AMRWB = 0x30, /* ToC header byte indicates AMR-WB IO data */
+ TOC_INDICATE_ARMWB_Q = 0x20, /* ToC header byte indicates AMR-WB IO lost frame */
+ TOC_INDICATE_SR = 0x1E, /* Indicate Split Rendering in ToC bytes */
+
+ EBYTE_CMR_T_EVS_NB = 0x80, /* Initial E-byte indicating EVS NarrowBand modes */
+ EBYTE_CMR_T_EVS_IO = 0x90, /* Initial E-byte indicating EVS AMRWB IO modes */
+ EBYTE_CMR_T_EVS_WB = 0xA0, /* Initial E-byte indicating EVS WideBand modes */
+ EBYTE_CMR_T_EVS_SWB = 0xB0, /* Initial E-byte indicating EVS SuperWideBand modes */
+ EBYTE_CMR_T_EVS_FB = 0xC0, /* Initial E-byte indicating EVS FullBand modes */
+ EBYTE_CMR_T_EVS_CA_WB = 0xD0, /* Initial E-byte indicating EVS CA WideBand modes */
+ EBYTE_CMR_T_EVS_CA_SWB = 0xE0, /* Initial E-byte indicating EVS CA SuperWideBand modes */
+ EBYTE_CMR_T_IVAS = 0xF0, /* Initial E-byte indicating IVAS modes */
+ EBYTE_CMR_T_NO_REQ = 0xFF, /* If no bitrate and no CA mode requested for IVAS/EVS */
+
+ EBYTE_BANDWIDTH_REQUEST = 0x80, /* Subsequent E-byte for Bandwidth Request */
+ EBYTE_FORMAT_REQUEST = 0x90, /* Subsequent E-byte for Format Request */
+ EBYTE_SUBFORMAT_REQUEST = 0x9F, /* Subsequent E-byte for SubFormat Request */
+ EBYTE_PI_INDICATOR = 0xA0, /* Subsequent E-byte for PI Indicator */
+ EBYTE_SR_REQUEST = 0xB0, /* Subsequent E-byte for Split Rendering Request */
+
+ PI_HEADER_PF_LAST = 0x00, /* Last PI header of the Payload in PI Header */
+ PI_HEADER_PF_NOT_LAST = 0x80, /* Another PI header follows after this in PI Header */
+ PI_HEADER_PM_NOT_LAST = 0x20, /* PI Frame Marker, not the last PI Header in current frame */
+ PI_HEADER_PM_LAST = 0x40, /* PI Frame Marker, the last PI Header in current frame */
+ PI_HEADER_PM_GENERIC = 0x60, /* PI Frame Marker, Generic applied to all frames */
+
+ PI_AD_SPEECH_INDICATED = 0x80, /* Audio Description indicate Speech in Audio data */
+ PI_AD_MUSIC_INDICATED = 0x40, /* Audio Description indicate Misuc in Audio data */
+ PI_AD_AMBIANCE_INDICATED = 0x20, /* Audio Description indicate Ambiance in Audio data */
+ PI_AD_EDITABLE_INDICATED = 0x10, /* Audio Description indicate metadata is editable */
+ PI_AD_BINAURAL_INDICATED = 0x8, /* Audio Description indicate Stereo stream in Binaural */
+
+};
+
+
+#define ERR_CHECK_RETURN( err ) \
+ { \
+ if ( ( err ) != IVAS_ERR_OK ) \
+ { \
+ return ( err ); \
+ } \
+ }
+
+
+typedef struct
+{
+ uint32_t size;
+ uint8_t data[IVAS_PI_MAX_DATA_SIZE];
+} PIDATA_PACKED;
+
+ivas_error PI_PackData( const IVAS_PIDATA_GENERIC *piData, PIDATA_PACKED *packed, uint8_t pmBits );
+ivas_error PI_UnPackData( uint8_t piDataType, uint32_t piSize, const uint8_t *piDataBuffer, IVAS_PIDATA_GENERIC *piData );
+
+#endif /* IVAS_RTPDUMP */
+
+#endif /* IVAS_RTP_INTERNAL_H */
diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c
new file mode 100644
index 0000000000000000000000000000000000000000..468dce0d173187739fb733104efdb454afb34973
--- /dev/null
+++ b/lib_util/ivas_rtp_payload.c
@@ -0,0 +1,1856 @@
+/******************************************************************************************************
+
+ (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository. All Rights Reserved.
+
+ This software is protected by copyright law and by international treaties.
+ The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository retain full ownership rights in their respective contributions in
+ the software. This notice grants no license of any kind, including but not limited to patent
+ license, nor is any license granted by implication, estoppel or otherwise.
+
+ Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
+ contributions.
+
+ This software is provided "AS IS", without any express or implied warranties. The software is in the
+ development stage. It is intended exclusively for experts who have experience with such software and
+ solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
+ and fitness for a particular purpose are hereby disclaimed and excluded.
+
+ Any dispute, controversy or claim arising under or in relation to providing this software shall be
+ submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
+ accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
+ the United Nations Convention on Contracts on the International Sales of Goods.
+
+*******************************************************************************************************/
+#include
+#include
+#include
+#include
+#include "common_api_types.h"
+
+#ifdef IVAS_RTPDUMP
+
+#include "ivas_rtp_api.h"
+#include "ivas_rtp_pi_data.h"
+#include "ivas_rtp_internal.h"
+#include "ivas_bpool.h"
+#include "ivas_queue.h"
+#include "ivas_error_utils.h"
+#include "mutex.h"
+
+#define IVAS_MAX_BYTES_PER_FRAME ( ( IVAS_MAX_BITS_PER_FRAME + 7 ) >> 3 )
+#define MAX_TOC_PER_FRAME ( 2u ) /* Main ToC Byte + SR-ToC byte */
+
+/* Generic RTP Header + Header Extension data structure */
+typedef struct
+{
+ uint32_t ssrc; /* synchronization source id as negotiated in SDP */
+ uint8_t version; /* version of RTP protocol, currently 2 */
+ bool padding; /* false = no padding, true = zeoo padded payload (last byte = padding count) */
+ bool extension; /* true = extension header is present before payload */
+ uint8_t CC; /* if some streams mixed together, CC indicates no. of contributing sources (4 bits) */
+ bool marker; /* Marker bit field */
+ uint8_t payloadType; /* 7-bit payload type field indicating IVAS */
+ uint16_t seqNumber; /* 16-bit sequence number for RTP packets */
+ uint32_t timestamp; /* timer ticks @ 16KHz clock corrsponding to 1st frame in the Payload */
+ uint32_t CSRC[15]; /* SSRC of contributing Sources for a mixed stream */
+ uint16_t extHeaderId; /* extension header ID */
+ uint16_t numExtUnits; /* length of the extension data in 32-bit words */
+ uint8_t *extData; /* Extension data pointer */
+} RTP_HEADER;
+
+typedef struct
+{
+ PIDATA_PACKED piData[IVAS_PI_MAX_ID]; /* pi data per frame */
+ uint32_t piDataBitmap; /* bitmap showing available pi */
+ uint32_t numPiDataAvailable; /* number of pi data available */
+} PIDATA_FRAME;
+
+static void initRequests( IVAS_RTP_REQUEST_VALUE *requests )
+{
+ requests[IVAS_REQUEST_CODEC].codec = IVAS_RTP_IVAS;
+ requests[IVAS_REQUEST_BITRATE].bitrate = 0;
+ requests[IVAS_REQUEST_BANDWIDTH].bandwidth = IVAS_BANDWIDTH_NO_REQ;
+ requests[IVAS_REQUEST_FORMAT].formatType = IVAS_FMT_NO_REQ;
+ requests[IVAS_REQUEST_CA_MODE].caMode = IVAS_RTP_CA_NO_REQ;
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ requests[IVAS_REQUEST_SUBFORMAT].subFormatType = IVAS_SUBFMT_NO_REQ;
+ requests[IVAS_REQUEST_SR_CONFIG].srConfig.valid = false;
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+}
+
+static void initPiDataFrame( PIDATA_FRAME *piDataFrm )
+{
+ /* Add NO_PI_DATA by default */
+ piDataFrm->numPiDataAvailable = 1;
+ piDataFrm->piDataBitmap = ( 1u << IVAS_PI_NO_DATA );
+ piDataFrm->piData[IVAS_PI_NO_DATA].size = 2;
+ piDataFrm->piData[IVAS_PI_NO_DATA].data[0] = ( IVAS_PI_NO_DATA ); /* PF/PM populated during final packing */
+ piDataFrm->piData[IVAS_PI_NO_DATA].data[1] = 0; /* NO_PI_DATA is 0 bytes */
+}
+
+typedef struct FRAME_NODE
+{
+ struct FRAME_NODE *next; /* next node */
+ PIDATA_FRAME piDataFrame; /* PI data for this frame */
+ uint8_t au[IVAS_MAX_BYTES_PER_FRAME]; /* ivas/evs packed frame (AU) */
+ uint8_t toc[MAX_TOC_PER_FRAME]; /* ToC bytes for this frame */
+ uint32_t auNumBits; /* Actual frame size in bits */
+ uint32_t tocNumBytes; /* valid ToC bytes (1 or 2) */
+} FRAME_NODE;
+
+static void initFrameNode( FRAME_NODE *node )
+{
+ node->next = NULL;
+ initPiDataFrame( &node->piDataFrame );
+ node->auNumBits = 0;
+ node->tocNumBytes = 0;
+}
+
+struct IVAS_RTP_PACK
+{
+ mtx_t apilock; /* Lock to handle concurrent API invocation */
+ bool amrwbIOMode; /* If EVS is an AMRWB-IO mode frame */
+ RTP_HEADER header; /* RTP Header Params */
+ BPOOL_HANDLE packNodePool; /* Buffer pool to allocate FRAME_NODEs */
+ PIDATA_FRAME piDataCache; /* temporary cache for PI data pushed before any frame is available in Queue */
+ IVAS_RTP_PACK_CONFIG initConfig; /* Initial Configuration for Unpacker */
+ IVAS_RTP_REQUEST_VALUE requests[IVAS_REQUEST_MAX]; /* Requests Storage */
+ QUEUE_HANDLE frameQ; /* frames queue for FRAME_NODEs */
+};
+
+typedef struct
+{
+ IVAS_RTP_CODEC codecId; /* Coded frame type (IVAS/EVS) */
+ uint32_t auNumBits; /* Actual frame size in bits */
+ bool speechLostIndicated; /* Speech Lost indicated */
+ bool isAMRWB_IOmode; /* EVS frame is AMRWB_IO */
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ IVAS_RTP_SR_INFO srInfo; /* Split Rendering Info */
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+} TOC_INFO;
+
+typedef struct PIDATA_NODE
+{
+ struct PIDATA_NODE *next; /* next node is first element */
+ PIDATA data; /* unpacked pi data per frame */
+ uint32_t timestamp; /* rtp timestamp of this frame */
+} PIDATA_NODE;
+
+typedef struct UNPACK_NODE
+{
+ struct UNPACK_NODE *next; /* next node is first element */
+ uint8_t au[IVAS_MAX_BYTES_PER_FRAME]; /* ivas/evs packed frame (AU) */
+ TOC_INFO toc; /* unpacked ToC information */
+ uint32_t timestamp; /* timestamp of this frame */
+ uint16_t seqNumber; /* sequence number of the packet */
+} UNPACK_NODE;
+
+struct IVAS_RTP_UNPACK
+{
+ mtx_t apilock; /* Lock to handle concurrent API invocation */
+ size_t maxNumberOfFrames;
+ size_t maxNumberOfPiData;
+ RTP_HEADER header;
+ BPOOL_HANDLE unpackNodePool;
+ BPOOL_HANDLE piDataPool;
+ IVAS_RTP_UNPACK_CONFIG initConfig;
+ IVAS_RTP_REQUEST_VALUE requests[IVAS_REQUEST_MAX];
+ uint32_t numPiDataAvailable; /* number of pi data available */
+ QUEUE_HANDLE frameQ; /* frame queue */
+ QUEUE_HANDLE piDataQ; /* pi data queue */
+};
+
+static const uint32_t ivasFrameSizeInBits[] = {
+ 264, 328, 488, 640, 960, 1280, 1600, 1920, 2560, 3200, 3840, 5120, 7680, 10240, 0, 104
+};
+
+static const uint32_t evsFrameSizeInBits[] = {
+ 56, 144, 160, 192, 264, 328, 488, 640, 960, 1280, 1920, 2560, 48
+};
+
+static const uint32_t amrWBIOFrameSizeInBits[] = {
+ 132, 177, 253, 285, 317, 365, 397, 461, 477, 35
+};
+
+
+/* Update the RTP Header structure */
+ivas_error IVAS_RTP_PACK_UpdateHeader(
+ IVAS_RTP_PACK_HANDLE hPack, /* i/o: pointer to an IVAS rtp packer handle to be opened */
+ uint32_t ssrc, /* i : Unique 32-bit Source ID as negotiated during SDP */
+ uint8_t numCC, /* i : numCC indicates no. of contributing sources */
+ uint32_t *csrc, /* i : SSRCs of contributing Sources for a mixed stream */
+ uint16_t extHeaderId, /* i : extension header ID */
+ uint16_t numExtensionBytes, /* i : length of the extension data */
+ uint8_t *extData /* i : extension data pointer */
+)
+{
+ RTP_HEADER *header = &hPack->header;
+
+ if ( numCC > 15 )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "CC must be less than 16" );
+ }
+
+ header->ssrc = ssrc;
+ header->CC = numCC;
+ memcpy( header->CSRC, csrc, numCC & 0xF );
+
+ if ( ( numExtensionBytes > 0 ) && ( extData != NULL ) )
+ {
+ header->extHeaderId = extHeaderId;
+ header->extData = realloc( header->extData, numExtensionBytes );
+ if ( header->extData == NULL )
+ {
+ return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "failed to allocate extdata" );
+ }
+ memcpy( header->extData, extData, numExtensionBytes );
+ header->extension = true;
+ header->numExtUnits = numExtensionBytes / sizeof( uint32_t );
+ }
+ else
+ {
+ header->numExtUnits = 0;
+ header->extension = false;
+ }
+
+ return IVAS_ERR_OK;
+}
+
+static void InitRtpHeader(
+ RTP_HEADER *header /* RTP header structure */
+)
+{
+ time_t t;
+ memset( header, 0, sizeof( *header ) );
+ srand( (uint32_t) time( &t ) );
+ header->version = 2;
+ header->seqNumber = rand() & 0xFFFF;
+}
+
+static ivas_error PackRtpHeader(
+ RTP_HEADER *header,
+ IVAS_DATA_BUFFER *buf )
+{
+ uint32_t nBytes = 0;
+ uint8_t CC = header->CC & MASK_4BIT;
+ uint32_t *CSRC = header->CSRC;
+ bool extension = header->extension && ( NULL != header->extData );
+ uint32_t extensionLength = header->extension ? ( 1 + header->numExtUnits ) : 0; /* in u32 words */
+ uint32_t packedLength = 12 + ( 4 * CC ) + ( 4 * extensionLength );
+
+ buf->length = 0;
+ if ( packedLength > buf->capacity )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient output buffer for RTP header packing" );
+ }
+
+ /*
+ +---+---+---+---+---+---+---+---+
+ | V | V | P | X | CC (4 - bits) |
+ +---+---+---+---+---+---+---+---+
+ */
+ buf->buffer[nBytes++] = ( header->version << 6 ) |
+ ( (uint8_t) header->padding << 5 ) |
+ ( (uint8_t) extension << 4 ) |
+ CC;
+ /*
+ +---+---+---+---+---+---+---+---+
+ | M | PT (7 - bits) |
+ +---+---+---+---+---+---+---+---+
+ */
+ buf->buffer[nBytes++] = ( (uint8_t) header->marker << 7 ) |
+ ( header->payloadType & MASK_7BIT );
+ /*
+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ | SEQUENCE NUMBER (16 bit) |
+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ */
+ buf->buffer[nBytes++] = (uint8_t) ( header->seqNumber >> 8 );
+ buf->buffer[nBytes++] = (uint8_t) ( header->seqNumber );
+
+ /* Timestamp (32-bit) */
+ buf->buffer[nBytes++] = (uint8_t) ( header->timestamp >> 24 );
+ buf->buffer[nBytes++] = (uint8_t) ( header->timestamp >> 16 );
+ buf->buffer[nBytes++] = (uint8_t) ( header->timestamp >> 8 );
+ buf->buffer[nBytes++] = (uint8_t) ( header->timestamp >> 0 );
+
+ /* SSRC (32-bit) */
+ buf->buffer[nBytes++] = (uint8_t) ( header->ssrc >> 24 );
+ buf->buffer[nBytes++] = (uint8_t) ( header->ssrc >> 16 );
+ buf->buffer[nBytes++] = (uint8_t) ( header->ssrc >> 8 );
+ buf->buffer[nBytes++] = (uint8_t) ( header->ssrc >> 0 );
+
+ /* CSRC Identifiers */
+ while ( CC )
+ {
+ /* CSRC[n] (32-bit) */
+ uint32_t id = *CSRC++;
+ buf->buffer[nBytes++] = (uint8_t) ( id >> 24 );
+ buf->buffer[nBytes++] = (uint8_t) ( id >> 16 );
+ buf->buffer[nBytes++] = (uint8_t) ( id >> 8 );
+ buf->buffer[nBytes++] = (uint8_t) ( id >> 0 );
+ CC--;
+ }
+
+ if ( extension )
+ {
+ buf->buffer[nBytes++] = (uint8_t) ( header->extHeaderId >> 8 );
+ buf->buffer[nBytes++] = (uint8_t) ( header->extHeaderId >> 0 );
+ buf->buffer[nBytes++] = (uint8_t) ( header->numExtUnits >> 8 );
+ buf->buffer[nBytes++] = (uint8_t) ( header->numExtUnits >> 0 );
+
+ memcpy( &buf->buffer[nBytes], header->extData, header->numExtUnits * 4 );
+ nBytes += header->numExtUnits;
+ }
+
+ buf->length = nBytes;
+ return IVAS_ERR_OK;
+}
+
+static void UpdateRtpHeader(
+ RTP_HEADER *header, /* RTP header structure */
+ uint32_t timestampOffset /* Timestamp offset @ 16KHz clock for next frame */
+)
+{
+ header->seqNumber++;
+ header->timestamp += timestampOffset;
+}
+
+static ivas_error UnpackRtpPacket(
+ const IVAS_DATA_BUFFER *packet, /* Input buffer with RTP Packet */
+ RTP_HEADER *header, /* RTP header structure */
+ uint32_t *numHeaderBytes /* No. of rtp header bytes */
+)
+{
+ uint32_t n = 0, nByte = 0, expectedSize = 12;
+ uint8_t byte;
+
+ if ( packet->length < expectedSize )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Insufficient input buffer for RTP header unpacking" );
+ }
+
+ /*
+ +---+---+---+---+---+---+---+---+
+ | V | V | P | X | CC (4 - bits) |
+ +---+---+---+---+---+---+---+---+
+ */
+ byte = packet->buffer[nByte++];
+ header->version = ( byte >> 6 ) & MASK_2BIT;
+ header->padding = ( byte >> 5 ) & MASK_1BIT;
+ header->extension = ( byte >> 4 ) & MASK_1BIT;
+ header->CC = ( byte & MASK_4BIT );
+
+ /*
+ +---+---+---+---+---+---+---+---+
+ | M | PT (7 - bits) |
+ +---+---+---+---+---+---+---+---+
+ */
+ byte = packet->buffer[nByte++];
+ header->marker = ( byte >> 7 ) & MASK_1BIT;
+ header->payloadType = byte & MASK_7BIT;
+
+ /*
+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ | SEQUENCE NUMBER (16 bit) |
+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ */
+ header->seqNumber = (uint16_t) packet->buffer[nByte++] << 8;
+ header->seqNumber |= (uint16_t) packet->buffer[nByte++] << 8;
+
+ /* Timestamp (32-bit) */
+ header->timestamp = ( (uint32_t) packet->buffer[nByte++] << 24 );
+ header->timestamp |= ( (uint32_t) packet->buffer[nByte++] << 16 );
+ header->timestamp |= ( (uint32_t) packet->buffer[nByte++] << 8 );
+ header->timestamp |= ( (uint32_t) packet->buffer[nByte++] );
+
+ /* SSRC (32-bit) */
+ header->ssrc = ( (uint32_t) packet->buffer[nByte++] << 24 );
+ header->ssrc |= ( (uint32_t) packet->buffer[nByte++] << 16 );
+ header->ssrc |= ( (uint32_t) packet->buffer[nByte++] << 8 );
+ header->ssrc |= ( (uint32_t) packet->buffer[nByte++] );
+
+ expectedSize += ( header->CC ) * 4 + ( header->extension ? 4 : 0 );
+ if ( packet->length < expectedSize )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "CC indicated but insufficient input buffer" );
+ }
+
+ /* CSRC Identifiers */
+ for ( n = 0; n < header->CC; n++ )
+ {
+ /* CSRC[n] (32-bit) */
+ header->CSRC[n] = ( (uint32_t) packet->buffer[nByte++] << 24 );
+ header->CSRC[n] |= ( (uint32_t) packet->buffer[nByte++] << 16 );
+ header->CSRC[n] |= ( (uint32_t) packet->buffer[nByte++] << 8 );
+ header->CSRC[n] |= ( (uint32_t) packet->buffer[nByte++] );
+ }
+
+ if ( header->extension )
+ {
+ header->extHeaderId = ( (uint32_t) packet->buffer[nByte++] << 8 );
+ header->extHeaderId |= ( (uint32_t) packet->buffer[nByte++] );
+ header->numExtUnits = ( (uint32_t) packet->buffer[nByte++] << 8 );
+ header->numExtUnits |= ( (uint32_t) packet->buffer[nByte++] );
+
+ expectedSize += header->numExtUnits * 4;
+ if ( packet->length < expectedSize )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Extension Header indicated but insufficient input buffer" );
+ }
+
+ header->extData = realloc( header->extData, header->numExtUnits * 4 );
+ if ( header->extData == NULL )
+ {
+ return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "failed to allocate extdata" );
+ }
+ memcpy( header->extData, &packet->buffer[nByte], header->numExtUnits * 4 );
+ nByte += header->numExtUnits * 4;
+ }
+
+ *numHeaderBytes = nByte;
+ return IVAS_ERR_OK;
+}
+
+static ivas_error getBitrateFromCodecAndFrameSize(
+ IVAS_RTP_CODEC codecId,
+ uint32_t frameLengthBytes,
+ uint32_t *bitrateKbps,
+ uint32_t *frameLenInBits,
+ uint8_t *tocByte,
+ bool *isAmrwbIOMode )
+{
+ size_t n;
+ *bitrateKbps = 0;
+ *frameLenInBits = 0;
+ if ( codecId == IVAS_RTP_IVAS )
+ {
+ /* Validate the framelength is supported for ivas frame */
+ for ( n = 0; n < sizeof( ivasFrameSizeInBits ) / sizeof( ivasFrameSizeInBits[0] ); n++ )
+ {
+ if ( ivasFrameSizeInBits[n] == frameLengthBytes * 8 )
+ {
+ *bitrateKbps = ( frameLengthBytes * IVAS_NUM_FRAMES_PER_SEC * 8 ); /* bitrate in kbps */
+ *frameLenInBits = frameLengthBytes * 8; /* frame length in bits */
+ *tocByte = TOC_INDICATE_IVAS | ( n & MASK_4BIT ); /* bitrate index */
+ return IVAS_ERR_OK;
+ }
+ }
+ return IVAS_ERR_INVALID_BITRATE;
+ }
+ else
+ {
+ /* Try if frameLength is a supported EVS frame length */
+ for ( n = 0; n < sizeof( evsFrameSizeInBits ) / sizeof( evsFrameSizeInBits[0] ); n++ )
+ {
+ if ( evsFrameSizeInBits[n] == frameLengthBytes * 8 )
+ {
+ *bitrateKbps = ( frameLengthBytes * IVAS_NUM_FRAMES_PER_SEC * 8 ); /* bitrate in kbps */
+ *frameLenInBits = frameLengthBytes * 8; /* frame length in bits */
+ *tocByte = TOC_INDICATE_EVS | ( n & MASK_4BIT ); /* bitrate index */
+ return IVAS_ERR_OK;
+ }
+ }
+ /* Try if frameLength is a supported AMRWB-IO frame length */
+ for ( n = 0; n < sizeof( amrWBIOFrameSizeInBits ) / sizeof( amrWBIOFrameSizeInBits[0] ); n++ )
+ {
+ uint32_t lengthInBits = amrWBIOFrameSizeInBits[n];
+ if ( ( ( lengthInBits + 7 ) / 8 ) == frameLengthBytes )
+ {
+ *isAmrwbIOMode = true;
+ *bitrateKbps = ( lengthInBits * IVAS_NUM_FRAMES_PER_SEC ); /* bitrate in kbps */
+ *frameLenInBits = lengthInBits; /* frame length in bits */
+ *tocByte = TOC_INDICATE_AMRWB | ( n & MASK_4BIT ); /* bitrate index */
+ return IVAS_ERR_OK;
+ }
+ }
+ return IVAS_ERR_INVALID_BITRATE;
+ }
+}
+
+ivas_error IVAS_RTP_PACK_Open(
+ IVAS_RTP_PACK_HANDLE *phPack, /* i/o: pointer to an IVAS rtp packer handle to be opened */
+ const IVAS_RTP_PACK_CONFIG *config /* i : pointer to initial config for RTP Packer */
+)
+{
+ ivas_error error;
+ IVAS_RTP_PACK_HANDLE hPack;
+ uint32_t numFramesPerPacket = ( config->maxFramesPerPacket == 0 ) ? IVAS_MAX_FRAMES_PER_RTP_PACKET : config->maxFramesPerPacket;
+
+ if ( phPack == NULL )
+ {
+ return IVAS_ERR_UNEXPECTED_NULL_POINTER;
+ }
+
+ if ( numFramesPerPacket > IVAS_MAX_FRAMES_PER_RTP_PACKET )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Max frame per packet exeeds %d", IVAS_MAX_FRAMES_PER_RTP_PACKET );
+ }
+
+ *phPack = NULL;
+ if ( ( hPack = (IVAS_RTP_PACK_HANDLE) calloc( 1, sizeof( struct IVAS_RTP_PACK ) ) ) == NULL )
+ {
+ return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Cannot allocate memory for IVAS rtppack handle" );
+ }
+
+ error = BPOOL_Create( &hPack->packNodePool, sizeof( FRAME_NODE ), numFramesPerPacket * 4 );
+ ERR_CHECK_RETURN( error );
+
+ error = QUEUE_Create( &hPack->frameQ );
+ ERR_CHECK_RETURN( error );
+
+ mtx_init( &hPack->apilock, 0 );
+ hPack->initConfig = *config;
+ initRequests( hPack->requests );
+ InitRtpHeader( &hPack->header );
+ initPiDataFrame( &hPack->piDataCache );
+ *phPack = hPack;
+ return IVAS_ERR_OK;
+}
+
+/* Close and free an existing instance of rtp packer */
+void IVAS_RTP_PACK_Close(
+ IVAS_RTP_PACK_HANDLE *phPack /* i/o : pointer to an IVAS rtp packer handle to be closed */
+)
+{
+ IVAS_RTP_PACK_HANDLE hPack;
+
+ /* Free all memory */
+ if ( phPack == NULL || *phPack == NULL )
+ {
+ return;
+ }
+
+ hPack = *phPack;
+ QUEUE_Destroy( &hPack->frameQ );
+ mtx_destroy( &hPack->apilock );
+ BPOOL_Destroy( &hPack->packNodePool );
+ free( hPack->header.extData );
+ free( hPack );
+ *phPack = NULL;
+}
+
+ivas_error IVAS_RTP_PACK_PushRemoteRequest(
+ IVAS_RTP_PACK_HANDLE hPack, /* i/o : IVAS rtp packer handle */
+ IVAS_RTP_REQUEST_TYPE reqType, /* i : remote request type */
+ IVAS_RTP_REQUEST_VALUE reqValue /* i : value of the requested type */
+)
+{
+ if ( reqType < 0 || reqType >= IVAS_REQUEST_MAX )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Invalid request key provided" );
+ }
+
+ /* Sanity on API */
+ switch ( reqType )
+ {
+ case IVAS_REQUEST_CODEC:
+ {
+ uint32_t codec = reqValue.codec;
+ if ( codec != IVAS_RTP_IVAS && codec != IVAS_RTP_EVS )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Unsupported codec id provided" );
+ }
+ }
+ break;
+ case IVAS_REQUEST_BITRATE:
+ {
+ uint32_t bitrate = reqValue.bitrate;
+ if ( bitrate < 5900 || bitrate > 512000 )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Unsupported bitrate provided" );
+ }
+ }
+ break;
+ case IVAS_REQUEST_BANDWIDTH:
+ {
+ uint32_t bandwidth = reqValue.bandwidth;
+ if ( bandwidth > IVAS_BANDWIDTH_NO_REQ )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Unsupported bandwidth provided" );
+ }
+ }
+ break;
+ case IVAS_REQUEST_FORMAT:
+ {
+ uint32_t format = reqValue.formatType;
+ if ( format > IVAS_FMT_NO_REQ )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Unsupported format provided" );
+ }
+ }
+ break;
+ case IVAS_REQUEST_CA_MODE:
+ {
+ uint32_t caMode = reqValue.caMode;
+ if ( caMode > IVAS_RTP_CA_NO_REQ )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Unsupported channel aware mode provided" );
+ }
+ }
+ break;
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ case IVAS_REQUEST_SUBFORMAT:
+ {
+ uint32_t subFormat = reqValue.subFormatType;
+ if ( subFormat > IVAS_SUBFMT_NO_REQ )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Unsupported subformat provided" );
+ }
+ }
+ break;
+ case IVAS_REQUEST_SR_CONFIG:
+ {
+ IVAS_RTP_SPLITRENDER srConfig = reqValue.srConfig;
+ if ( srConfig.reserved != 0 )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Unsupported reserved bits in SR config provided" );
+ }
+ }
+ break;
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+ default:
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Unsupported request type" );
+ }
+
+ hPack->requests[reqType] = reqValue;
+
+ return IVAS_ERR_OK;
+}
+
+static bool isAmrWBIOMode( uint32_t bitrate, uint32_t *idx )
+{
+ size_t n;
+ *idx = 0;
+ for ( n = 0; n < sizeof( amrWBIOFrameSizeInBits ) / sizeof( amrWBIOFrameSizeInBits[0] ); n++ )
+ {
+ if ( bitrate == ( amrWBIOFrameSizeInBits[n] * IVAS_NUM_FRAMES_PER_SEC ) )
+ {
+ *idx = n;
+ return true;
+ }
+ }
+ return false;
+}
+
+static uint32_t getBitrateIdx( const uint32_t *table, uint32_t tableLen, uint32_t bitrate )
+{
+ size_t n, idx = 0;
+
+ for ( n = 0; n < tableLen; n++ )
+ {
+ if ( bitrate > ( table[n] * IVAS_NUM_FRAMES_PER_SEC ) )
+ {
+ idx = n;
+ }
+ else
+ {
+ return idx;
+ }
+ }
+ return idx;
+}
+
+static void packEBytes(
+ IVAS_RTP_PACK_HANDLE hPack, /* i/o : IVAS rtp packer handle */
+ bool piDataPresent, /* i : Signals if PI data present */
+ size_t maxNumEBytes, /* i : max capacity of eByte buffer */
+ uint8_t *eByte, /* o : output buffer for E-Bytes */
+ size_t *nBytesWritten /* o : max capacity of eByte buffer */
+)
+{
+ /* Initial E-byte structure
+ * 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+
+ * |H|T|T|T| BR |
+ * +-+-+-+-+-+-+-+-+
+ */
+ uint32_t nByte = 0;
+ uint8_t T = EBYTE_CMR_T_IVAS; /* IVAS */
+ uint8_t BR = 0;
+
+ IVAS_RTP_CODEC codec = hPack->requests[IVAS_REQUEST_CODEC].codec;
+ IVAS_RTP_BANDWIDTH bandwidth = hPack->requests[IVAS_REQUEST_BANDWIDTH].bandwidth;
+ uint32_t bitrate = hPack->requests[IVAS_REQUEST_BITRATE].bitrate;
+ uint32_t bitrateIdx = 0;
+
+ *nBytesWritten = 0;
+
+ if ( codec == IVAS_RTP_EVS )
+ {
+ /* If requesting EVS/AMRWB IO from farend, only Initial E-byte present */
+ IVAS_RTP_CA_MODE caMode = hPack->requests[IVAS_REQUEST_CA_MODE].caMode;
+
+ if ( caMode != IVAS_RTP_CA_NO_REQ )
+ {
+ T = ( bandwidth == IVAS_BANDWIDTH_SWB ) ? EBYTE_CMR_T_EVS_CA_SWB : EBYTE_CMR_T_EVS_CA_WB;
+ BR = caMode & MASK_3BIT; /* only 8 valid values */
+ if ( nByte < maxNumEBytes )
+ {
+ eByte[nByte++] = ( T | BR );
+ }
+ }
+ else if ( bitrate > 0 && isAmrWBIOMode( bitrate, &bitrateIdx ) )
+ {
+ /* AMR WB IO Mode */
+ T = EBYTE_CMR_T_EVS_IO;
+ BR = (uint8_t) bitrateIdx & MASK_4BIT;
+ if ( nByte < maxNumEBytes )
+ {
+ eByte[nByte++] = ( T | BR );
+ }
+ }
+ else if ( bitrate > 0 )
+ {
+ /* EVS Modes */
+ bitrate = ( bitrate > 128000 ) ? 128000 : bitrate;
+ bitrate = ( bitrate < 5900 ) ? 5900 : bitrate;
+ bitrateIdx = getBitrateIdx( evsFrameSizeInBits, sizeof( evsFrameSizeInBits ) / sizeof( evsFrameSizeInBits[0] ), bitrate );
+ BR = (uint8_t) bitrateIdx & MASK_4BIT;
+
+ /* If a bandwidth choice cannot be signalled for a given bitrate
+ preference is given to bitrate choice, bandwidth req is modified */
+ switch ( bandwidth )
+ {
+ case IVAS_BANDWIDTH_NB:
+ T = ( bitrate <= 24400u ) ? EBYTE_CMR_T_EVS_NB : EBYTE_CMR_T_EVS_SWB;
+ break;
+ case IVAS_BANDWIDTH_WB:
+ T = EBYTE_CMR_T_EVS_WB;
+ break;
+ case IVAS_BANDWIDTH_SWB:
+ T = ( bitrate >= 9600u ) ? EBYTE_CMR_T_EVS_SWB : EBYTE_CMR_T_EVS_WB;
+ break;
+ case IVAS_BANDWIDTH_FB:
+ T = ( bitrate >= 16400u ) ? EBYTE_CMR_T_EVS_FB : EBYTE_CMR_T_EVS_WB;
+ break;
+ default: /*IVAS_BANDWIDTH_NO_REQ */
+ T = ( bitrate >= 9600u ) ? EBYTE_CMR_T_EVS_SWB : EBYTE_CMR_T_EVS_WB;
+ break;
+ }
+ if ( nByte < maxNumEBytes )
+ {
+ eByte[nByte++] = ( T | BR );
+ }
+ }
+ else if ( piDataPresent )
+ {
+ /* Send an initial E-byte to allow for subsequent PI indication E-byte */
+ if ( nByte < maxNumEBytes )
+ {
+ eByte[nByte++] = EBYTE_CMR_T_NO_REQ;
+ }
+ }
+ }
+ else
+ {
+ /* Requesting IVAS from farend, Subsequent E-byte indicate BW, CFR, SR */
+ IVAS_RTP_FORMAT format = hPack->requests[IVAS_REQUEST_FORMAT].formatType;
+ bool isBandwidthProvided = ( bandwidth != IVAS_BANDWIDTH_NO_REQ && bandwidth != IVAS_BANDWIDTH_NB );
+ bool isFormatProvided = ( format != IVAS_FMT_NO_REQ );
+ bool isSubFormatProvided = false;
+ bool isSRConfigProvided = false;
+
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ IVAS_RTP_SUBFORMAT subFormat = hPack->requests[IVAS_REQUEST_SUBFORMAT].subFormatType;
+ IVAS_RTP_SPLITRENDER srConfig = hPack->requests[IVAS_REQUEST_SR_CONFIG].srConfig;
+
+ isSubFormatProvided = ( subFormat != IVAS_SUBFMT_NO_REQ );
+ isSRConfigProvided = srConfig.valid;
+#endif
+
+ /* Initial E-Byte only sent if either bitrate requested or subsequent E-byte is requested */
+ if ( !( ( bitrate > 0 ) || isBandwidthProvided || isFormatProvided ||
+ isSubFormatProvided || isSRConfigProvided || piDataPresent ) )
+ {
+ return;
+ }
+
+ if ( bitrate > 0 )
+ {
+ /* Initial E-Byte */
+ bitrate = ( bitrate > 512000 ) ? 512000 : bitrate;
+ bitrate = ( bitrate < 13200 ) ? 13200 : bitrate;
+ bitrateIdx = getBitrateIdx( ivasFrameSizeInBits, sizeof( ivasFrameSizeInBits ) / sizeof( ivasFrameSizeInBits[0] ), bitrate );
+ BR = (uint8_t) bitrateIdx & MASK_4BIT;
+ if ( nByte < maxNumEBytes )
+ {
+ eByte[nByte++] = ( EBYTE_CMR_T_IVAS | BR );
+ }
+ }
+ else
+ {
+ if ( nByte < maxNumEBytes )
+ {
+ eByte[nByte++] = EBYTE_CMR_T_NO_REQ;
+ }
+ }
+
+ /* Subsequent E-bytes - Bandwidth Request */
+ if ( isBandwidthProvided && nByte < maxNumEBytes )
+ {
+ uint8_t bw = ( bandwidth - IVAS_BANDWIDTH_WB ) & MASK_2BIT;
+ eByte[nByte++] = ( EBYTE_BANDWIDTH_REQUEST | bw );
+ }
+
+ /* Subsequent E-bytes - Coded Format Request */
+ if ( ( isFormatProvided || isSubFormatProvided ) && nByte < maxNumEBytes )
+ {
+ uint8_t fmtEByte = ( EBYTE_FORMAT_REQUEST | ( (uint8_t) format & MASK_3BIT ) );
+ eByte[nByte++] = isSubFormatProvided ? EBYTE_SUBFORMAT_REQUEST : fmtEByte;
+ }
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ if ( isSubFormatProvided && nByte < maxNumEBytes )
+ {
+ eByte[nByte++] = ( (uint8_t) subFormat & MASK_6BIT ); /* Requested Coded subFormat */
+ }
+ /* Subsequent E-bytes - Split Renderer Configuration Request */
+ if ( isSRConfigProvided && nByte < maxNumEBytes )
+ {
+ eByte[nByte++] = EBYTE_SR_REQUEST |
+ ( (uint8_t) srConfig.diegetic << 3 ) | ( (uint8_t) srConfig.yaw << 2 ) |
+ ( (uint8_t) srConfig.pitch << 1 ) | ( (uint8_t) srConfig.roll << 0 );
+ }
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+ }
+
+ /* Final E-byte is the PI Indicator E-Byte */
+ if ( piDataPresent && ( nByte < maxNumEBytes ) )
+ {
+ eByte[nByte++] = EBYTE_PI_INDICATOR; /* PI Indication */
+ }
+
+ *nBytesWritten = nByte;
+}
+
+
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+static ivas_error getSRToCByte(
+ IVAS_RTP_SR_INFO *srInfo, /* i : Split Rendering Info */
+ uint32_t bitrateKbps, /* i : Bitrate in kbps */
+ uint8_t *tocByte /* o : toc byte 2 */
+)
+{
+ uint8_t bitIdx, codecId, digetic;
+ if ( bitrateKbps < 256000 || bitrateKbps > 512000 )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Unsupported bitrate for SR" );
+ }
+
+ bitIdx = ( ( bitrateKbps / 128000u ) - 1 ) & MASK_2BIT;
+ codecId = (uint8_t) srInfo->codec;
+ digetic = srInfo->diegetic ? 1 : 0;
+
+ *tocByte = ( digetic << 6 ) | ( codecId << 5 ) | ( bitIdx << 3 );
+
+ return IVAS_ERR_OK;
+}
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+
+static void addPackedPiDataToFrame( PIDATA_FRAME *piDataFrm, const PIDATA_PACKED *packedPiData, uint32_t piDataType )
+{
+ piDataFrm->piData[piDataType].size = packedPiData->size;
+ memcpy( piDataFrm->piData[piDataType].data, packedPiData->data, packedPiData->size );
+
+ /* Indicate th PI data type presense in bitmap, If the same pi data type is
+ already pushed for this frame, it is overwritten with the newer data */
+ if ( !( piDataFrm->piDataBitmap & ( 1u << piDataType ) ) )
+ {
+ piDataFrm->piDataBitmap |= ( 1u << piDataType );
+ piDataFrm->numPiDataAvailable++;
+ }
+
+ /* Atleast one valid PI data is now pushed for this frame, clear No PI data
+ for this frame */
+ if ( piDataFrm->piDataBitmap & ( 1u << IVAS_PI_NO_DATA ) )
+ {
+ piDataFrm->piDataBitmap &= ~( 1u << IVAS_PI_NO_DATA );
+ piDataFrm->numPiDataAvailable--;
+ }
+}
+
+ivas_error IVAS_RTP_PACK_PushFrame(
+ IVAS_RTP_PACK_HANDLE hPack, /* i/o : IVAS rtp packer handle */
+ IVAS_RTP_CODEC codecId, /* i : Codec type (IVAS/EVS) */
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ IVAS_RTP_SR_INFO *srInfo, /* i : Split Rendering Info */
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+ const IVAS_DATA_BUFFER *frameBuffer /* i : packed frame bitstream for IVAS/EVS */
+)
+{
+ ivas_error error = IVAS_ERR_OK;
+ uint32_t bitrate = 0;
+ uint32_t frameLengthInBits = 0;
+ uint8_t tocByte = 0;
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ uint8_t tocByte1 = 0;
+#endif
+ FRAME_NODE *node = NULL;
+ PIDATA_FRAME *piDataFrame = &hPack->piDataCache;
+ uint32_t piDataType = 0;
+
+ if ( frameBuffer == NULL || frameBuffer->length == 0 )
+ {
+ /* Indicates a NO_DATA_FRAME in case of DTX mode */
+ tocByte = ( hPack->amrwbIOMode ) ? TOC_INDICATE_NO_DATA_AMRWB : TOC_INDICATE_NO_DATA;
+ }
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ else if ( srInfo != NULL && srInfo->valid )
+ {
+ tocByte = TOC_INDICATE_SR;
+ error = getSRToCByte( srInfo, bitrate, &tocByte1 );
+ ERR_CHECK_RETURN( error );
+ }
+#endif
+ else
+ {
+ error = getBitrateFromCodecAndFrameSize( codecId, frameBuffer->length, &bitrate, &frameLengthInBits, &tocByte, &hPack->amrwbIOMode );
+ ERR_CHECK_RETURN( error );
+ }
+
+ /* Allocate a new frame node for this frame */
+ error = BPOOL_GetBuffer( hPack->packNodePool, (void **) &node );
+ ERR_CHECK_RETURN( error );
+
+ initFrameNode( node );
+
+ /* Set ToC byte & frame */
+ node->toc[node->tocNumBytes++] = tocByte;
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ if ( srInfo != NULL && srInfo->valid )
+ {
+ node->toc[node->tocNumBytes++] = tocByte1;
+ }
+#endif
+
+ node->auNumBits = frameLengthInBits;
+ if ( frameBuffer != NULL )
+ {
+ memcpy( node->au, frameBuffer->buffer, frameBuffer->length );
+ }
+
+ /* If some Pi data is is Cache add to Frame Node's associated Pi Data */
+ mtx_lock( &hPack->apilock ); /* Lock to prevent access to shared cache */
+ if ( piDataFrame->numPiDataAvailable )
+ {
+ uint32_t bitmap = piDataFrame->piDataBitmap;
+ for ( piDataType = 0; piDataType < 32 && ( bitmap != 0 ); piDataType++ )
+ {
+ uint32_t mask = ( 1u << piDataType );
+ if ( bitmap & mask )
+ {
+ bitmap &= ~mask; /* Mask out this pi type to indicate processed */
+ addPackedPiDataToFrame( &node->piDataFrame, &piDataFrame->piData[piDataType], piDataType );
+ }
+ }
+ initPiDataFrame( piDataFrame ); /* Reset Cache */
+ }
+ mtx_unlock( &hPack->apilock );
+
+ /* Add to frames FiFo */
+ QUEUE_Push( hPack->frameQ, (NODE *) node );
+
+ return IVAS_ERR_OK;
+}
+
+uint32_t IVAS_RTP_PACK_GetNumFrames(
+ IVAS_RTP_PACK_HANDLE hPack /* i/o : IVAS rtp packer handle */
+)
+{
+ uint32_t nFrames = 0;
+ if ( hPack )
+ {
+ nFrames = QUEUE_Size( hPack->frameQ );
+ }
+ return nFrames;
+}
+
+/* Push single PI data to rtp packer
+ *
+ * Provide PI data for a current RTP Payload. All PI data is locally cached in the packer
+ * and set to the rtp packet with policy defined in initial configuration during call to
+ * IVAS_RTP_PACK_GetPacket.
+ *
+ */
+ivas_error IVAS_RTP_PACK_PushPiData(
+ IVAS_RTP_PACK_HANDLE hPack, /* i/o : IVAS rtp packer handle */
+ const IVAS_PIDATA_GENERIC *data /* i : pointer to the PIData stucture */
+)
+{
+ ivas_error error = IVAS_ERR_OK;
+ PIDATA_PACKED packedPiData;
+
+ if ( data == NULL ||
+ data->piDataType >= IVAS_PI_NO_DATA || /* NO_PI_DATA cannot be provided by user, it is generated in packing */
+ data->size > sizeof( PIDATA ) )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Wrong PI Data provided" );
+ }
+
+ error = PI_PackData( data, &packedPiData, 0 );
+ ERR_CHECK_RETURN( error );
+
+ mtx_lock( &hPack->apilock ); /* Lock to prevent access to shared cache */
+ {
+ FRAME_NODE *node = (FRAME_NODE *) QUEUE_Back( hPack->frameQ );
+ /* use cache if no frame in Queue to associate PI data */
+ PIDATA_FRAME *piDataFrm = ( node != NULL ) ? &node->piDataFrame : &hPack->piDataCache;
+ addPackedPiDataToFrame( piDataFrm, &packedPiData, data->piDataType );
+ }
+ mtx_unlock( &hPack->apilock );
+
+ return IVAS_ERR_OK;
+}
+
+#define WRITE_BYTE_PAYLOAD_OR_EXIT( payload, byte ) \
+ if ( payload->length < payload->capacity ) \
+ { \
+ uint8_t _byte = ( byte ); \
+ payload->buffer[payload->length++] = _byte; \
+ } \
+ else \
+ { \
+ error = IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient memory to write RTP Payload" ); \
+ goto err_exit; \
+ }
+
+ivas_error IVAS_RTP_PACK_GetPayload(
+ IVAS_RTP_PACK_HANDLE hPack, /* i/o : IVAS rtp packer handle */
+ IVAS_DATA_BUFFER *payload, /* o : encapsulated rtp payload */
+ uint32_t *numFramesInPayload /* o : no. of frames in payload */
+)
+{
+ uint32_t n = 0, numFrame = 0;
+ ivas_error error = IVAS_ERR_OK;
+ uint32_t numPiDataPresent = 0;
+ FRAME_NODE *availableFrameNodes[IVAS_MAX_FRAMES_PER_RTP_PACKET];
+ size_t numEBytes = 0;
+
+ if ( payload == NULL )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Output data buffer is NULL" );
+ }
+
+ /* Calculate number of frames to pack in this payload */
+ numFrame = QUEUE_Size( hPack->frameQ );
+ numFrame = ( numFrame > hPack->initConfig.maxFramesPerPacket ) ? hPack->initConfig.maxFramesPerPacket : numFrame;
+ *numFramesInPayload = numFrame; /* numFrames in Packet */
+
+ /* Collect all the frame nodes from FiFo */
+ for ( n = 0; n < numFrame; n++ )
+ {
+ FRAME_NODE *node = (FRAME_NODE *) QUEUE_Pop( hPack->frameQ );
+ if ( node == NULL )
+ {
+ assert( 0 ); /* catastrophic error, implementation issue somewhere */
+ return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "NULL found in frame nodes" );
+ }
+ /* Calculate number of PI data present in total */
+ numPiDataPresent += node->piDataFrame.numPiDataAvailable;
+ availableFrameNodes[n] = node;
+ }
+
+ /* IVAS Payload starts with E-Bytes */
+ packEBytes( hPack, ( numPiDataPresent > 0 ), payload->capacity, &payload->buffer[payload->length], &numEBytes );
+ payload->length += numEBytes;
+
+ /* ToC bytes (atleast 1 byte per frame, 2 if SR )*/
+ for ( n = 0; n < numFrame; n++ )
+ {
+ FRAME_NODE *node = availableFrameNodes[n];
+ uint8_t fBit = ( n != ( numFrame - 1 ) ) ? TOC_HEADER_FOLLOWS : 0; /* Next ToC present */
+
+ WRITE_BYTE_PAYLOAD_OR_EXIT( payload, ( node->toc[0] | fBit ) );
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ if ( node->tocNumBytes == 2 )
+ {
+ WRITE_BYTE_PAYLOAD_OR_EXIT( payload, node->toc[1] );
+ }
+#endif
+ }
+
+ /* Frame Data */
+ for ( n = 0; n < numFrame; n++ )
+ {
+ FRAME_NODE *node = availableFrameNodes[n];
+ size_t frameLength = ( node->auNumBits + 7 ) >> 3; /* zero padded length in bytes */
+ if ( payload->length + frameLength <= payload->capacity )
+ {
+ memcpy( &payload->buffer[payload->length], node->au, frameLength );
+ }
+ else
+ {
+ error = IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient memory to write RTP Payload" );
+ goto err_exit;
+ }
+ payload->length += frameLength;
+ }
+
+ /* PI Data */
+ if ( numPiDataPresent > 0 )
+ {
+ bool skipPiData = false;
+ size_t nBytes = payload->length;
+ uint32_t numPiDataWritten = 0;
+
+ for ( n = 0; n < numFrame; n++ )
+ {
+ uint32_t piDataType = 0;
+ FRAME_NODE *node = availableFrameNodes[n];
+ PIDATA_FRAME *piDataFrame = &node->piDataFrame;
+ uint32_t bitmap = piDataFrame->piDataBitmap;
+ uint8_t PM = 0, PF = 0;
+
+ for ( piDataType = 0; piDataType < 32 && ( bitmap != 0 ); piDataType++ )
+ {
+ uint32_t mask = ( 1u << piDataType );
+ if ( bitmap & mask )
+ {
+ bitmap &= ~mask; /* Mask out this pi type to indicate processing */
+ /* Check if last PI data this frame */
+ PM = piDataFrame->piData[piDataType].data[0] & 0x60;
+ PM = (uint8_t) ( ( bitmap == 0 && PM != PI_HEADER_PM_GENERIC ) ? PI_HEADER_PM_LAST : PM );
+ /* Check if last PI data all frames */
+ PF = (uint8_t) ( ( bitmap == 0 && ( numPiDataWritten + 1 == numPiDataPresent ) ) ? PI_HEADER_PF_LAST : PI_HEADER_PF_NOT_LAST ); /* Last PI in Payload */
+ /* Update the first byte of PI Header with PF/PM */
+ piDataFrame->piData[piDataType].data[0] |= ( PF | PM );
+ if ( nBytes + piDataFrame->piData[piDataType].size < payload->capacity )
+ {
+ memcpy( &payload->buffer[nBytes], piDataFrame->piData[piDataType].data, piDataFrame->piData[piDataType].size );
+ nBytes += piDataFrame->piData[piDataType].size;
+ }
+ else
+ {
+ skipPiData = true; /* Not enough bytes in output for PI data */
+ }
+ numPiDataWritten++;
+ }
+ }
+ }
+ if ( !skipPiData )
+ {
+ payload->length = nBytes; /* update payload length after PI packing */
+ }
+ }
+
+err_exit:
+ /* Pop frames from Queue */
+ for ( n = 0; n < numFrame; n++ )
+ {
+ if ( BPOOL_FreeBuffer( hPack->packNodePool, availableFrameNodes[n] ) != IVAS_ERR_OK )
+ {
+ assert( 0 ); /* catastrophic error if this fails */
+ return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "frame node could not be freed" );
+ }
+ availableFrameNodes[n] = NULL;
+ }
+
+ return error;
+}
+
+ivas_error IVAS_RTP_PACK_GetPacket(
+ IVAS_RTP_PACK_HANDLE hPack, /* i/o : IVAS rtp packer handle */
+ IVAS_DATA_BUFFER *packet, /* o : encapsulated rtp packet */
+ uint32_t *numFramesInPacket /* o : no. of frames in packet */
+)
+{
+ ivas_error error = PackRtpHeader( &hPack->header, packet );
+ ERR_CHECK_RETURN( error );
+
+ error = IVAS_RTP_PACK_GetPayload( hPack, packet, numFramesInPacket );
+ ERR_CHECK_RETURN( error );
+
+ UpdateRtpHeader( &hPack->header, ( *numFramesInPacket ) * 320 );
+
+ return IVAS_ERR_OK;
+}
+
+ivas_error IVAS_RTP_UNPACK_Open(
+ IVAS_RTP_UNPACK_HANDLE *phUnpack, /* i/o : rtp unpacker handle */
+ const IVAS_RTP_UNPACK_CONFIG *config /* i : initial configuration for rtp unpacker */
+)
+{
+ ivas_error error = IVAS_ERR_OK;
+ IVAS_RTP_UNPACK_HANDLE hUnpack;
+
+ if ( phUnpack == NULL || config == NULL )
+ {
+ return IVAS_ERR_UNEXPECTED_NULL_POINTER;
+ }
+
+ *phUnpack = NULL;
+ if ( ( hUnpack = (IVAS_RTP_UNPACK_HANDLE) calloc( 1, sizeof( struct IVAS_RTP_UNPACK ) ) ) == NULL )
+ {
+ return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Cannot allocate memory for IVAS rtp unpack handle" );
+ }
+
+ if ( config->maxFramesPerPacket > IVAS_MAX_FRAMES_PER_RTP_PACKET )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Max frame per packet exeeds %d", IVAS_MAX_FRAMES_PER_RTP_PACKET );
+ }
+
+ hUnpack->maxNumberOfFrames = ( config->maxFramesPerPacket == 0 ) ? IVAS_MAX_FRAMES_PER_RTP_PACKET : config->maxFramesPerPacket;
+ hUnpack->maxNumberOfPiData = hUnpack->maxNumberOfFrames * IVAS_PI_MAX_ID;
+
+ error = BPOOL_Create( &hUnpack->unpackNodePool, sizeof( UNPACK_NODE ), hUnpack->maxNumberOfFrames );
+ ERR_CHECK_RETURN( error );
+
+ error = BPOOL_Create( &hUnpack->piDataPool, sizeof( PIDATA_NODE ), hUnpack->maxNumberOfPiData );
+ ERR_CHECK_RETURN( error );
+
+ error = QUEUE_Create( &hUnpack->frameQ );
+ ERR_CHECK_RETURN( error );
+
+ error = QUEUE_Create( &hUnpack->piDataQ );
+ ERR_CHECK_RETURN( error );
+
+ hUnpack->initConfig = *config;
+ mtx_init( &hUnpack->apilock, 0 );
+
+ initRequests( hUnpack->requests );
+
+ *phUnpack = hUnpack;
+ return IVAS_ERR_OK;
+}
+
+/* Close and free an existing instance of rtp unpacker */
+void IVAS_RTP_UNPACK_Close(
+ IVAS_RTP_UNPACK_HANDLE *phUnpack /* i/o : IVAS rtp unpacker handle */
+)
+{
+ IVAS_RTP_UNPACK_HANDLE hUnpack;
+
+ /* Free all memory */
+ if ( phUnpack == NULL || *phUnpack == NULL )
+ {
+ return;
+ }
+
+ hUnpack = *phUnpack;
+ mtx_destroy( &hUnpack->apilock );
+ QUEUE_Destroy( &hUnpack->frameQ );
+ QUEUE_Destroy( &hUnpack->piDataQ );
+ BPOOL_Destroy( &hUnpack->piDataPool );
+ BPOOL_Destroy( &hUnpack->unpackNodePool );
+ free( hUnpack->header.extData );
+ free( hUnpack );
+ *phUnpack = NULL;
+}
+
+static void setEVSRequests( IVAS_RTP_BANDWIDTH bandwidth, uint32_t bitrate, IVAS_RTP_CA_MODE caMode, IVAS_RTP_REQUEST_VALUE *requests )
+{
+ requests[IVAS_REQUEST_CODEC].codec = IVAS_RTP_EVS;
+ requests[IVAS_REQUEST_CA_MODE].caMode = caMode;
+ requests[IVAS_REQUEST_BITRATE].bitrate = bitrate;
+ requests[IVAS_REQUEST_BANDWIDTH].bandwidth = bandwidth;
+}
+
+static uint32_t parseInitialEByte( const IVAS_DATA_BUFFER *payload, uint32_t nBytes, IVAS_RTP_REQUEST_VALUE *requests )
+{
+ if ( nBytes < payload->length )
+ {
+ uint8_t byte = payload->buffer[nBytes];
+ uint8_t EvsIvasIndicator = ( byte & ( ~MASK_4BIT ) );
+ uint8_t BR = ( byte & MASK_4BIT );
+
+ if ( ( byte & EBYTE_TOC_HEADER_BIT ) == 0 )
+ {
+ return nBytes;
+ }
+
+ nBytes++; /* Consume this e-byte */
+
+ switch ( EvsIvasIndicator )
+ {
+ case EBYTE_CMR_T_EVS_NB:
+ if ( BR < 7 )
+ {
+ uint32_t bitrate = evsFrameSizeInBits[BR] * IVAS_NUM_FRAMES_PER_SEC;
+ setEVSRequests( IVAS_BANDWIDTH_NB, bitrate, IVAS_RTP_CA_NO_REQ, requests );
+ }
+ break;
+ case EBYTE_CMR_T_EVS_IO: /* AMRWB-IO */
+ if ( BR < 9 )
+ {
+ uint32_t bitrate = amrWBIOFrameSizeInBits[BR] * IVAS_NUM_FRAMES_PER_SEC;
+ setEVSRequests( IVAS_BANDWIDTH_NO_REQ, bitrate, IVAS_RTP_CA_NO_REQ, requests );
+ }
+ break;
+ case EBYTE_CMR_T_EVS_CA_WB:
+ if ( BR < 8 )
+ {
+ uint32_t bitrate = 13200; /* Fixed in CA Mode */
+ setEVSRequests( IVAS_BANDWIDTH_WB, bitrate, BR, requests );
+ }
+ break;
+ case EBYTE_CMR_T_EVS_CA_SWB:
+ if ( BR < 8 )
+ {
+ uint32_t bitrate = 13200; /* Fixed in CA Mode */
+ setEVSRequests( IVAS_BANDWIDTH_SWB, bitrate, BR, requests );
+ }
+ break;
+ case EBYTE_CMR_T_EVS_WB:
+ if ( BR < 12 )
+ {
+ uint32_t bitrate = evsFrameSizeInBits[BR] * IVAS_NUM_FRAMES_PER_SEC;
+ setEVSRequests( IVAS_BANDWIDTH_WB, bitrate, IVAS_RTP_CA_NO_REQ, requests );
+ }
+ break;
+ case EBYTE_CMR_T_EVS_SWB: /* Intentional fall through */
+ if ( BR < 12 && BR > 2 )
+ {
+ uint32_t bitrate = evsFrameSizeInBits[BR] * IVAS_NUM_FRAMES_PER_SEC;
+ setEVSRequests( IVAS_BANDWIDTH_SWB, bitrate, IVAS_RTP_CA_NO_REQ, requests );
+ }
+ break;
+ case EBYTE_CMR_T_EVS_FB:
+ if ( BR < 12 && BR > 4 )
+ {
+ uint32_t bitrate = evsFrameSizeInBits[BR] * IVAS_NUM_FRAMES_PER_SEC;
+ setEVSRequests( IVAS_BANDWIDTH_FB, bitrate, IVAS_RTP_CA_NO_REQ, requests );
+ }
+ break;
+ case EBYTE_CMR_T_IVAS: /* IVAS */
+ if ( BR != 14 )
+ {
+ requests[IVAS_REQUEST_CODEC].codec = IVAS_RTP_IVAS;
+ requests[IVAS_REQUEST_BITRATE].bitrate = ( BR == 0xF ) ? 0 : ivasFrameSizeInBits[BR] * IVAS_NUM_FRAMES_PER_SEC;
+ requests[IVAS_REQUEST_CA_MODE].caMode = IVAS_RTP_CA_NO_REQ;
+ }
+ break;
+ }
+ }
+
+ return nBytes;
+}
+
+static uint32_t parseSubsequentEByte( const IVAS_DATA_BUFFER *payload, uint32_t nBytes, IVAS_RTP_REQUEST_VALUE *requests, bool *piDataIndicated )
+{
+ while ( nBytes < payload->length )
+ {
+ uint8_t byte = payload->buffer[nBytes];
+ uint8_t ET = ( byte & ( ~MASK_4BIT ) );
+
+ if ( ( byte & EBYTE_TOC_HEADER_BIT ) == 0 )
+ {
+ return nBytes;
+ }
+
+ nBytes++; /* Consume this e-byte */
+
+ switch ( ET )
+ {
+ case EBYTE_BANDWIDTH_REQUEST: /* Bandwidth Request */
+ {
+ requests[IVAS_REQUEST_BANDWIDTH].bandwidth = IVAS_BANDWIDTH_WB + ( byte & MASK_2BIT );
+ }
+ break;
+ case EBYTE_FORMAT_REQUEST: /* Format Request */
+ {
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ bool S = ( byte >> 3 ) & MASK_1BIT;
+ if ( S )
+ {
+ /* Use the next byte to extract SubFormat */
+ if ( nBytes < payload->length )
+ {
+ byte = payload->buffer[nBytes++];
+ requests[IVAS_REQUEST_SUBFORMAT].subFormatType = byte & MASK_6BIT;
+ }
+ }
+#endif
+ requests[IVAS_REQUEST_FORMAT].formatType = byte & MASK_3BIT;
+ }
+ break;
+ case EBYTE_PI_INDICATOR: /* PI Indication */
+ *piDataIndicated = true;
+ break;
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ case EBYTE_SR_REQUEST: /* Split Rendering Request */
+ {
+ IVAS_RTP_SPLITRENDER *srConfig = &requests[IVAS_REQUEST_SR_CONFIG].srConfig;
+ srConfig->diegetic = ( byte >> 3 ) & MASK_1BIT;
+ srConfig->yaw = ( byte >> 2 ) & MASK_1BIT;
+ srConfig->pitch = ( byte >> 1 ) & MASK_1BIT;
+ srConfig->roll = byte & MASK_1BIT;
+ srConfig->valid = true;
+ }
+ break;
+#endif
+ default: /* Reserved for future use - unhandled atm */
+ assert( 0 );
+ }
+ }
+
+ return nBytes;
+}
+
+static ivas_error parseToCByte( const IVAS_DATA_BUFFER *payload, uint32_t *numBytes, uint32_t *numFrames, TOC_INFO *toc, uint32_t maxNumberOfToCBytes )
+{
+ bool headerFollows = true;
+ uint32_t nBytes = *numBytes;
+
+ *numFrames = 0;
+ while ( nBytes < payload->length && headerFollows )
+ {
+ uint8_t byte = payload->buffer[nBytes];
+ uint8_t BR = byte & MASK_4BIT;
+ uint8_t FT = byte & ( ( ~MASK_4BIT ) & MASK_6BIT );
+
+ headerFollows = ( byte & ( ~MASK_6BIT ) ) == TOC_HEADER_FOLLOWS;
+
+ if ( ( byte & EBYTE_TOC_HEADER_BIT ) != 0 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNSUPPORTED_FRAME, "Expected ToC byte missing" );
+ }
+
+ nBytes++; /* Consume this e-byte */
+
+ if ( *numFrames == maxNumberOfToCBytes )
+ {
+ return IVAS_ERROR( IVAS_ERR_INTERNAL, "No of frames in packet exceed max defined" );
+ }
+
+ *numFrames += 1;
+ memset( toc, 0, sizeof( *toc ) );
+
+ if ( FT == TOC_INDICATE_ARMWB_Q || FT == TOC_INDICATE_AMRWB )
+ {
+ toc->codecId = IVAS_RTP_EVS;
+ toc->isAMRWB_IOmode = true;
+ toc->speechLostIndicated = ( FT == TOC_INDICATE_ARMWB_Q ) ? true : false; /* Q bit = 0 for AMRWB, BR is valid */
+ if ( BR <= 9 )
+ {
+ toc->auNumBits = amrWBIOFrameSizeInBits[BR];
+ }
+ else if ( BR < 14 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNSUPPORTED_FRAME, "Reserved bitrate provided in AMRWB ToC" );
+ }
+ else
+ {
+ toc->speechLostIndicated = ( BR == 14 ); /* SPEECH_LOST */
+ toc->auNumBits = 0;
+ }
+ }
+ else if ( FT == TOC_INDICATE_IVAS )
+ {
+ toc->codecId = IVAS_RTP_IVAS;
+ if ( BR == 14 )
+ {
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ /* Read Unconditional SR-ToC byte */
+ if ( nBytes < payload->length )
+ {
+ uint8_t SR_BR;
+ byte = payload->buffer[nBytes++];
+ SR_BR = ( byte >> 3 ) & MASK_2BIT;
+ if ( SR_BR == 0 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNSUPPORTED_FRAME, "Reserved bitrate provided in SR ToC" );
+ }
+ toc->srInfo.valid = true;
+ toc->srInfo.diegetic = ( byte >> 6 ) & MASK_1BIT;
+ toc->srInfo.codec = IVAS_SR_TRANSPORT_LCLD + ( ( byte >> 5 ) & MASK_1BIT );
+ toc->auNumBits = ( SR_BR + 1 ) * 128000u / IVAS_NUM_FRAMES_PER_SEC;
+ }
+ else
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Underflow during ToC SR byte" );
+ }
+#else
+ /* Reserved bit not expected */
+ return IVAS_ERROR( IVAS_ERR_RTP_UNSUPPORTED_FRAME, "Reserved BR idx reported in ToC" );
+#endif
+ }
+ else
+ {
+ toc->auNumBits = ivasFrameSizeInBits[BR];
+ }
+ }
+ else /* EVS */
+ {
+ toc->codecId = IVAS_RTP_EVS;
+ toc->speechLostIndicated = ( FT == TOC_INDICATE_ARMWB_Q ) ? true : false; /* Q bit = 0 for AMRWB, BR is valid */
+ if ( BR < 13 )
+ {
+ toc->auNumBits = evsFrameSizeInBits[BR];
+ }
+ else if ( BR == 13 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNSUPPORTED_FRAME, "Reserved bitrate provided in EVS ToC" );
+ }
+ else
+ {
+ toc->speechLostIndicated = ( BR == 14 ); /* SPEECH_LOST */
+ toc->auNumBits = 0;
+ }
+ }
+
+ toc++;
+
+ /* Handle any frame specific E-Bytes here currently there are none, so we skip any E-Bytes here after */
+ if ( headerFollows )
+ {
+ while ( nBytes < payload->length )
+ {
+ byte = payload->buffer[nBytes];
+ if ( ( byte & EBYTE_TOC_HEADER_BIT ) == 0 )
+ {
+ break;
+ }
+ nBytes++;
+ }
+ }
+ }
+
+ *numBytes = nBytes;
+ return IVAS_ERR_OK;
+}
+
+static ivas_error parsePIData( IVAS_RTP_UNPACK_HANDLE hUnpack, uint32_t rtpTimestamp, const IVAS_DATA_BUFFER *payload, uint32_t *numBytes, uint32_t *numPiDataInPacket )
+{
+ bool PF = true;
+ uint32_t nBytes = *numBytes;
+
+ while ( PF )
+ {
+ uint8_t piHeader0, PM, piDataType, byte = 0;
+ uint32_t piSize = 0;
+
+ if ( nBytes + 1 >= payload->length )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Underflow during expected PI Header read" );
+ }
+
+ piHeader0 = payload->buffer[nBytes++];
+
+ PF = ( piHeader0 >> 7 ) & MASK_1BIT; /* New PI header follows this PI Data Frame */
+ PM = ( piHeader0 & ( ~MASK_5BIT ) ) & MASK_7BIT; /* PI Marker Bits */
+ piDataType = ( piHeader0 & MASK_5BIT );
+
+ do
+ {
+ byte = payload->buffer[nBytes++];
+ piSize += byte;
+ if ( nBytes >= payload->length )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Underflow during reading piSize" );
+ }
+ } while ( byte == 255 );
+
+ if ( piDataType == IVAS_PI_NO_DATA )
+ {
+ if ( piSize > 0 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "NO_PI_DATA should be 0 sized" );
+ }
+
+ /* Do not add a node for NO_DATA */
+ }
+ else if ( nBytes + piSize <= payload->length )
+ {
+ PIDATA_NODE *node = NULL;
+ ivas_error error = BPOOL_GetBuffer( hUnpack->piDataPool, (void **) &node );
+ ERR_CHECK_RETURN( error );
+
+ node->next = NULL;
+
+ error = PI_UnPackData( piDataType, piSize, &payload->buffer[nBytes], (IVAS_PIDATA_GENERIC *) &node->data );
+ ERR_CHECK_RETURN( error );
+
+ node->timestamp = rtpTimestamp;
+
+ nBytes += piSize;
+ *numPiDataInPacket += 1;
+
+ QUEUE_Push( hUnpack->piDataQ, (NODE *) node );
+ }
+ else
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Underflow during reading pi data" );
+ }
+
+ if ( PM == PI_HEADER_PM_LAST )
+ {
+ rtpTimestamp += 16000 / IVAS_NUM_FRAMES_PER_SEC;
+ }
+ }
+
+ *numBytes = nBytes;
+ return IVAS_ERR_OK;
+}
+
+ivas_error IVAS_RTP_UNPACK_PushPayload(
+ IVAS_RTP_UNPACK_HANDLE hUnpack, /* i/o : IVAS rtp unpacker handle */
+ const IVAS_DATA_BUFFER *payload, /* i : received rtp payload */
+ uint32_t timestamp, /* i : timestamp in RTP Clock @ 16KHz from rtp header */
+ uint16_t sequenceNumber, /* i : sequence number from rtp header */
+ uint32_t *numFramesInPacket, /* o : number of IVAS/EVS frames in rtp packet */
+ uint32_t *numPiDataInPacket, /* o : number of PI data received in rtp packet */
+ uint32_t *remoteRequestBitmap /* o : bitmap of available request in this packet */
+)
+{
+ ivas_error error = IVAS_ERR_OK;
+ uint32_t nBytes = 0, numFrames = 0, numPiData = 0, n;
+ bool piDataIndicated = false;
+ TOC_INFO toc[IVAS_MAX_FRAMES_PER_RTP_PACKET];
+
+ IVAS_RTP_REQUEST_VALUE oldRequests[IVAS_REQUEST_MAX];
+
+ if ( hUnpack == NULL || payload == NULL )
+ {
+ return IVAS_ERR_UNEXPECTED_NULL_POINTER;
+ }
+
+ if ( remoteRequestBitmap != NULL )
+ {
+ *remoteRequestBitmap = 0;
+ }
+
+ if ( numFramesInPacket != NULL )
+ {
+ *numFramesInPacket = 0;
+ }
+
+ if ( numPiDataInPacket != NULL )
+ {
+ *numPiDataInPacket = 0;
+ }
+
+ /* Sanity check if any frame or PI data from last packet is still not pulled */
+ if ( QUEUE_Size( hUnpack->frameQ ) > 0 || QUEUE_Size( hUnpack->piDataQ ) > 0 )
+ {
+ assert( 0 );
+ return IVAS_ERROR( IVAS_ERR_INTERNAL, "Previous packet data should be consumed before next packet is pushed" );
+ }
+
+ memcpy( oldRequests, hUnpack->requests, sizeof( oldRequests ) );
+
+ /* Unpack IVAS Payload, starting with the E-Bytes */
+ nBytes = parseInitialEByte( payload, nBytes, hUnpack->requests );
+
+ /* Unpack any subsequent E-bytes */
+ nBytes = parseSubsequentEByte( payload, nBytes, hUnpack->requests, &piDataIndicated );
+
+ /* Unpack the ToC Bytes => Extract number of frames in packet */
+ error = parseToCByte( payload, &nBytes, &numFrames, toc, sizeof( toc ) / sizeof( toc[0] ) );
+ ERR_CHECK_RETURN( error );
+
+ /* Read frame bits */
+ for ( n = 0; n < numFrames; n++ )
+ {
+ uint32_t frameSizeBytes;
+ UNPACK_NODE *node = NULL;
+
+ /* Get a new node */
+ error = BPOOL_GetBuffer( hUnpack->unpackNodePool, (void **) &node );
+ ERR_CHECK_RETURN( error );
+
+ node->next = NULL;
+ node->timestamp = timestamp + ( n * 320 );
+ node->seqNumber = sequenceNumber;
+ node->toc = toc[n];
+
+ frameSizeBytes = ( node->toc.auNumBits + 7 ) / 8;
+ if ( nBytes + frameSizeBytes <= payload->length )
+ {
+ memcpy( node->au, &payload->buffer[nBytes], frameSizeBytes );
+ nBytes += frameSizeBytes;
+ }
+ else
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Underflow during expected frame bits" );
+ }
+
+ /* Add to frames FiFo */
+ QUEUE_Push( hUnpack->frameQ, (NODE *) node );
+ }
+
+ if ( piDataIndicated )
+ {
+ error = parsePIData( hUnpack, timestamp, payload, &nBytes, &numPiData );
+ if ( error != IVAS_ERR_OK )
+ {
+ /* PI Parsing errors are not fatal => indicate no PI data in packet as workaround */
+ numPiData = 0;
+ }
+ }
+
+ if ( numFramesInPacket != NULL )
+ {
+ *numFramesInPacket = numFrames;
+ }
+
+ if ( numPiDataInPacket )
+ {
+ *numPiDataInPacket = numPiData;
+ }
+
+ if ( remoteRequestBitmap )
+ {
+ for ( n = 0; n < IVAS_REQUEST_MAX; n++ )
+ {
+ bool changed = ( memcmp( &hUnpack->requests[n], &oldRequests[n], sizeof( IVAS_RTP_REQUEST_VALUE ) ) != 0 );
+ *remoteRequestBitmap |= changed ? ( 1u << n ) : 0;
+ }
+ }
+
+ return IVAS_ERR_OK;
+}
+
+ivas_error IVAS_RTP_UNPACK_PushPacket(
+ IVAS_RTP_UNPACK_HANDLE hUnpack, /* i/o : IVAS rtp unpacker handle */
+ const IVAS_DATA_BUFFER *packet, /* i : received rtp Packet */
+ uint32_t *numFramesInPacket, /* o : number of IVAS/EVS frames in rtp packet */
+ uint32_t *numPiDataInPacket, /* o : number of PI data received in rtp packet */
+ uint32_t *remoteRequestBitmap /* o : bitmap of available request in this packet */
+)
+{
+ ivas_error error = IVAS_ERR_OK;
+ uint32_t numHeaderBytes = 0;
+ IVAS_DATA_BUFFER payload;
+
+ error = UnpackRtpPacket( packet, &hUnpack->header, &numHeaderBytes );
+ ERR_CHECK_RETURN( error );
+
+ /* Offset to RTP Payload */
+ payload.capacity = packet->capacity;
+ payload.buffer = packet->buffer + numHeaderBytes;
+ payload.length = packet->length - numHeaderBytes;
+
+ return IVAS_RTP_UNPACK_PushPayload(
+ hUnpack,
+ &payload,
+ hUnpack->header.timestamp,
+ hUnpack->header.seqNumber,
+ numFramesInPacket,
+ numPiDataInPacket,
+ remoteRequestBitmap );
+}
+
+ivas_error IVAS_RTP_UNPACK_GetRequest(
+ IVAS_RTP_UNPACK_HANDLE hUnpack, /* i/o : IVAS rtp packer handle */
+ IVAS_RTP_REQUEST_TYPE type, /* i : remote request type */
+ IVAS_RTP_REQUEST_VALUE *value /* o : pointer of the requested type */
+)
+{
+ if ( type < 0 || type >= IVAS_REQUEST_MAX )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Invalid request key provided" );
+ }
+ *value = hUnpack->requests[type];
+ return IVAS_ERR_OK;
+}
+
+ivas_error IVAS_RTP_UNPACK_PullFrame(
+ IVAS_RTP_UNPACK_HANDLE hUnpack, /* i/o : IVAS rtp unpacker handle */
+ IVAS_RTP_CODEC *receivedCodecId, /* o : Codec type (IVAS/EVS) */
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ IVAS_RTP_SR_INFO *srInfo, /* o : Split Rendering Info */
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+ IVAS_DATA_BUFFER *frameBuffer, /* o : packed frame bitstream for IVAS/EVS */
+ int16_t *frameSizeInBits, /* o : exact frame size in bits (AMRWB IO) */
+ uint32_t *timestamp, /* o : timestamp in RTP Clock @ 16KHz */
+ uint16_t *sequenceNumber, /* o : sequence number from rtp header */
+ bool *speechLostIndicated, /* o : Is current frame indicated as Lost */
+ bool *isAMRWB_IOmode /* o : Is AMRWB_IO mode EVS frame */
+)
+{
+ size_t length = 0;
+ UNPACK_NODE *node = (UNPACK_NODE *) QUEUE_Pop( hUnpack->frameQ );
+
+ /* Check if a node is available in FiFo */
+ if ( node == NULL )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "No more frames in unpack fifo" );
+ }
+
+ length = ( node->toc.auNumBits + 7 ) / 8;
+ if ( frameBuffer != NULL && ( length <= frameBuffer->capacity ) )
+ {
+ memcpy( frameBuffer->buffer, node->au, length );
+ frameBuffer->length = length;
+ }
+
+ if ( frameSizeInBits != NULL )
+ {
+ *frameSizeInBits = (int16_t) node->toc.auNumBits;
+ }
+
+ if ( receivedCodecId != NULL )
+ {
+ *receivedCodecId = node->toc.codecId;
+ }
+
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ if ( srInfo != NULL )
+ {
+ *srInfo = node->toc.srInfo;
+ }
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+
+ if ( timestamp != NULL )
+ {
+ *timestamp = node->timestamp;
+ }
+
+ if ( sequenceNumber != NULL )
+ {
+ *sequenceNumber = node->seqNumber;
+ }
+
+ if ( speechLostIndicated != NULL )
+ {
+ *speechLostIndicated = node->toc.speechLostIndicated;
+ }
+
+ if ( isAMRWB_IOmode != NULL )
+ {
+ *isAMRWB_IOmode = node->toc.isAMRWB_IOmode;
+ }
+
+ return BPOOL_FreeBuffer( hUnpack->unpackNodePool, node );
+}
+
+ivas_error IVAS_RTP_UNPACK_PullNextPiData(
+ IVAS_RTP_UNPACK_HANDLE hUnpack, /* i/o : IVAS rtp unpacker handle */
+ IVAS_PIDATA_GENERIC *data, /* o : output data buffer for the Pi data */
+ size_t capacity, /* i : capacity of pi data buffer in bytes */
+ uint32_t *timestamp /* o : timestamp in RTP Clock @ 16KHz */
+)
+{
+ IVAS_PIDATA_GENERIC *pi = NULL;
+ PIDATA_NODE *node = (PIDATA_NODE *) QUEUE_Pop( hUnpack->piDataQ );
+
+ /* Check if a node is available in FiFo */
+ if ( node == NULL )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "No more pi data in unpack fifo" );
+ }
+
+ pi = (IVAS_PIDATA_GENERIC *) &node->data;
+
+ if ( data != NULL && ( pi->size <= capacity ) )
+ {
+ memcpy( data, pi, pi->size );
+ }
+
+ if ( timestamp != NULL )
+ {
+ *timestamp = node->timestamp;
+ }
+
+ return BPOOL_FreeBuffer( hUnpack->piDataPool, node );
+}
+
+#endif /* IVAS_RTPDUMP */
diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c
new file mode 100644
index 0000000000000000000000000000000000000000..e7dcde89983700384e447221e9431340bb2cac71
--- /dev/null
+++ b/lib_util/ivas_rtp_pi_data.c
@@ -0,0 +1,755 @@
+/******************************************************************************************************
+
+ (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository. All Rights Reserved.
+
+ This software is protected by copyright law and by international treaties.
+ The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository retain full ownership rights in their respective contributions in
+ the software. This notice grants no license of any kind, including but not limited to patent
+ license, nor is any license granted by implication, estoppel or otherwise.
+
+ Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
+ contributions.
+
+ This software is provided "AS IS", without any express or implied warranties. The software is in the
+ development stage. It is intended exclusively for experts who have experience with such software and
+ solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
+ and fitness for a particular purpose are hereby disclaimed and excluded.
+
+ Any dispute, controversy or claim arising under or in relation to providing this software shall be
+ submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
+ accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
+ the United Nations Convention on Contracts on the International Sales of Goods.
+
+*******************************************************************************************************/
+
+#include
+#include "ivas_rtp_pi_data.h"
+#include "ivas_error_utils.h"
+#include "ivas_rtp_internal.h"
+
+#ifdef IVAS_RTPDUMP
+
+/* Generic PI data packing/unpacking functions */
+typedef ivas_error ( *PACK_PI_FN )( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten );
+typedef ivas_error ( *UNPACK_PI_FN )( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData );
+
+static __inline uint32_t writeInt16( uint8_t *buffer, uint32_t idx, int16_t val )
+{
+ buffer[idx++] = (uint8_t) ( val >> 8 );
+ buffer[idx++] = (uint8_t) ( val & 0x00FF );
+ return idx;
+}
+
+static __inline int16_t readInt16( const uint8_t *buffer )
+{
+ return (int16_t) ( (uint16_t) buffer[0] << 8 ) | ( (uint16_t) buffer[1] );
+}
+
+/*-----------------------------------------------------------------------*
+ * ivasPayload_convertToQ15()
+ *
+ * Convert a float value into a Q15 encoded value.
+ *-----------------------------------------------------------------------*/
+static int16_t ivasPayload_convertToQ15( float value )
+{
+ value = ( value * 32768.0f );
+ value = value > +32767.0f ? +32767.0f : value;
+ value = value < -32768.0f ? -32768.0f : value;
+ return (int16_t) ( value );
+}
+
+static ivas_error packUnsupportedData( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten )
+{
+ (void) piData;
+ (void) buffer;
+ (void) maxDataBytes;
+ /* Skip packing */
+ *nBytesWritten = 0;
+ return IVAS_ERR_OK;
+}
+
+static ivas_error unpackUnsupportedData( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData )
+{
+ (void) piData;
+ (void) buffer;
+ (void) numDataBytes;
+ /* Skip unpacking */
+ return IVAS_ERR_OK;
+}
+
+static ivas_error packNoPiData( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten )
+{
+ uint32_t nBytes = 0;
+ (void) piData;
+
+ *nBytesWritten = 0;
+
+ /* NO_PI_DATA is just PI header with no data */
+ if ( maxDataBytes < 2 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space in PI data buffer for NO_PI_DATA" );
+ }
+
+ buffer[nBytes++] = ( IVAS_PI_NO_DATA ); /* PF/PM populated during final packing */
+ buffer[nBytes++] = 0; /* NO_PI_DATA is 0 bytes */
+
+ *nBytesWritten = nBytes;
+ return IVAS_ERR_OK;
+}
+
+static ivas_error unpackNoPiData( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData )
+{
+ (void) buffer;
+
+ if ( numDataBytes != 0 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "NO_PI_DATA must be 0 byte" );
+ }
+
+ piData->size = sizeof( IVAS_PIDATA_NO_DATA );
+ return IVAS_ERR_OK;
+}
+
+static ivas_error packOrientation( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten )
+{
+ uint32_t nBytes = 0;
+ const IVAS_PIDATA_ORIENTATION *orientation = (const IVAS_PIDATA_ORIENTATION *) piData;
+
+ *nBytesWritten = 0;
+
+ if ( piData->size != sizeof( IVAS_PIDATA_ORIENTATION ) )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect size in Orientation PI data" );
+ }
+
+ if ( ( piData->piDataType != IVAS_PI_SCENE_ORIENTATION ) && ( piData->piDataType != IVAS_PI_DEVICE_ORIENTATION_COMPENSATED ) && ( piData->piDataType != IVAS_PI_DEVICE_ORIENTATION_UNCOMPENSATED )
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ && ( piData->piDataType != IVAS_PI_PLAYBACK_DEVICE_ORIENTATION ) && ( piData->piDataType != IVAS_PI_HEAD_ORIENTATION ) && ( piData->piDataType != IVAS_PI_AUDIO_FOCUS_DIRECTION )
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+ )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect PI ID in Orientation PI data" );
+ }
+
+ /* Orientation data is 8 bytes, header is 2 bytes */
+ if ( maxDataBytes < 8 + 2 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space to pack Orientation PI data" );
+ }
+
+ buffer[nBytes++] = ( orientation->piDataType & MASK_5BIT ); /* PF/PM populated during final packing */
+ buffer[nBytes++] = 8;
+ nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation.w ) );
+ nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation.x ) );
+ nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation.y ) );
+ nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( orientation->orientation.z ) );
+
+ *nBytesWritten = nBytes;
+ return IVAS_ERR_OK;
+}
+
+static ivas_error unpackOrientation( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData )
+{
+ IVAS_PIDATA_ORIENTATION *orientation = (IVAS_PIDATA_ORIENTATION *) piData;
+
+ /* Orientation data is 8 bytes */
+ if ( numDataBytes != 8 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack Orientation PI data" );
+ }
+
+ piData->size = sizeof( IVAS_PIDATA_ORIENTATION );
+ orientation->orientation.w = FLOAT_FROM_Q15( readInt16( &buffer[0] ) );
+ orientation->orientation.x = FLOAT_FROM_Q15( readInt16( &buffer[2] ) );
+ orientation->orientation.y = FLOAT_FROM_Q15( readInt16( &buffer[4] ) );
+ orientation->orientation.z = FLOAT_FROM_Q15( readInt16( &buffer[6] ) );
+
+ return IVAS_ERR_OK;
+}
+
+static uint32_t getIndexTable( const float *table, uint32_t tableLength, float value )
+{
+ uint32_t idx = 0;
+ if ( value <= table[0] )
+ {
+ return 0;
+ }
+
+ for ( idx = 1; idx < tableLength; idx++ )
+ {
+ if ( value < table[idx] )
+ {
+ break;
+ }
+ }
+ return idx - 1;
+}
+
+#define GET_IDX( table, nBits, value ) getIndexTable( table, ( 1u << ( nBits ) ), ( value ) )
+
+static ivas_error packAcousticEnvironment( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten )
+{
+ uint32_t nBytes = 0;
+ uint8_t packedSize = 1;
+ const IVAS_PIDATA_ACOUSTIC_ENV *aeEnv = (const IVAS_PIDATA_ACOUSTIC_ENV *) piData;
+
+ *nBytesWritten = 0;
+
+ if ( piData->size != sizeof( IVAS_PIDATA_ACOUSTIC_ENV ) )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect size in PI data of type Acoustic Environment" );
+ }
+
+ if ( aeEnv->availEarlyReflections )
+ {
+ packedSize = 8;
+ }
+ else if ( aeEnv->availLateReverb )
+ {
+ packedSize = 5;
+ }
+
+ /* Acoustic Env data is packedSize bytes, header is 2 bytes */
+ if ( maxDataBytes < (uint32_t) packedSize + 2 )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Insufficient space to pack Orientation PI data" );
+ }
+
+ buffer[nBytes++] = ( IVAS_PI_ACOUSTIC_ENVIRONMENT ); /* PF/PM populated during final packing */
+ buffer[nBytes++] = packedSize;
+
+ if ( packedSize == 1 )
+ {
+ buffer[nBytes++] = aeEnv->aeid & 0x7F;
+ }
+ else
+ {
+ uint64_t dWord = (uint64_t) aeEnv->aeid << 57;
+
+ dWord |= (uint64_t) GET_IDX( mapRT60, NBITS_RT60, aeEnv->rt60[IVAS_PI_AE_LOW] ) << 52;
+ dWord |= (uint64_t) GET_IDX( mapDSR, NBITS_DSR, aeEnv->dsr[IVAS_PI_AE_LOW] ) << 46;
+ dWord |= (uint64_t) GET_IDX( mapRT60, NBITS_RT60, aeEnv->rt60[IVAS_PI_AE_MID] ) << 41;
+ dWord |= (uint64_t) GET_IDX( mapDSR, NBITS_DSR, aeEnv->dsr[IVAS_PI_AE_MID] ) << 35;
+ dWord |= (uint64_t) GET_IDX( mapRT60, NBITS_RT60, aeEnv->rt60[IVAS_PI_AE_HIGH] ) << 30;
+ dWord |= (uint64_t) GET_IDX( mapDSR, NBITS_DSR, aeEnv->dsr[IVAS_PI_AE_HIGH] ) << 24;
+
+ buffer[nBytes++] = (uint8_t) ( dWord >> 56 );
+ buffer[nBytes++] = (uint8_t) ( dWord >> 48 );
+ buffer[nBytes++] = (uint8_t) ( dWord >> 40 );
+ buffer[nBytes++] = (uint8_t) ( dWord >> 32 );
+ buffer[nBytes++] = (uint8_t) ( dWord >> 24 );
+
+ if ( packedSize > 5 )
+ {
+ dWord |= (uint64_t) GET_IDX( mapRoomDims, NBITS_DIM, aeEnv->roomDimensions.x ) << 20;
+ dWord |= (uint64_t) GET_IDX( mapRoomDims, NBITS_DIM, aeEnv->roomDimensions.y ) << 16;
+ dWord |= (uint64_t) GET_IDX( mapRoomDims, NBITS_DIM, aeEnv->roomDimensions.z ) << 12;
+ dWord |= (uint64_t) GET_IDX( mapAbsorbtion, NBITS_ABS, aeEnv->absorbCoeffs[IVAS_PI_AE_FRONT] ) << 10;
+ dWord |= (uint64_t) GET_IDX( mapAbsorbtion, NBITS_ABS, aeEnv->absorbCoeffs[IVAS_PI_AE_BACK] ) << 8;
+ dWord |= (uint64_t) GET_IDX( mapAbsorbtion, NBITS_ABS, aeEnv->absorbCoeffs[IVAS_PI_AE_LEFT] ) << 6;
+ dWord |= (uint64_t) GET_IDX( mapAbsorbtion, NBITS_ABS, aeEnv->absorbCoeffs[IVAS_PI_AE_RIGHT] ) << 4;
+ dWord |= (uint64_t) GET_IDX( mapAbsorbtion, NBITS_ABS, aeEnv->absorbCoeffs[IVAS_PI_AE_CEILING] ) << 2;
+ dWord |= (uint64_t) GET_IDX( mapAbsorbtion, NBITS_ABS, aeEnv->absorbCoeffs[IVAS_PI_AE_FLOOR] );
+
+ buffer[nBytes++] = (uint8_t) ( dWord >> 16 );
+ buffer[nBytes++] = (uint8_t) ( dWord >> 8 );
+ buffer[nBytes++] = (uint8_t) ( dWord );
+ }
+ }
+
+ *nBytesWritten = nBytes;
+ return IVAS_ERR_OK;
+}
+
+static ivas_error unpackAcousticEnvironment( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData )
+{
+ IVAS_PIDATA_ACOUSTIC_ENV *aeEnv = (IVAS_PIDATA_ACOUSTIC_ENV *) piData;
+
+ /* Acooustic Env data is either 1, 5 or 8 bytes */
+ if ( numDataBytes != 1 && numDataBytes != 5 && numDataBytes != 8 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack PI data of type Acoustic Environment" );
+ }
+
+ piData->size = sizeof( IVAS_PIDATA_ACOUSTIC_ENV );
+ aeEnv->availLateReverb = ( numDataBytes >= 5 );
+ aeEnv->availEarlyReflections = ( numDataBytes == 8 );
+
+ if ( numDataBytes == 1 )
+ {
+ aeEnv->aeid = buffer[0];
+ }
+ else
+ {
+ uint64_t dWord = 0ull;
+ uint32_t n;
+ for ( n = 0; n < numDataBytes; n++ )
+ {
+ dWord <<= 8;
+ dWord |= buffer[n];
+ }
+ dWord <<= ( 8 - numDataBytes ) * 8;
+
+ aeEnv->aeid = (uint8_t) ( ( dWord >> 57 ) & MASK_AEID );
+ aeEnv->rt60[IVAS_PI_AE_LOW] = mapRT60[( dWord >> 52 ) & MASK_RT60];
+ aeEnv->dsr[IVAS_PI_AE_LOW] = mapDSR[( dWord >> 46 ) & MASK_DSR];
+ aeEnv->rt60[IVAS_PI_AE_MID] = mapRT60[( dWord >> 41 ) & MASK_RT60];
+ aeEnv->dsr[IVAS_PI_AE_MID] = mapDSR[( dWord >> 35 ) & MASK_DSR];
+ aeEnv->rt60[IVAS_PI_AE_HIGH] = mapRT60[( dWord >> 30 ) & MASK_RT60];
+ aeEnv->dsr[IVAS_PI_AE_HIGH] = mapDSR[( dWord >> 24 ) & MASK_DSR];
+
+ aeEnv->roomDimensions.x = mapRoomDims[( dWord >> 20 ) & MASK_DIM];
+ aeEnv->roomDimensions.y = mapRoomDims[( dWord >> 16 ) & MASK_DIM];
+ aeEnv->roomDimensions.z = mapRoomDims[( dWord >> 12 ) & MASK_DIM];
+
+ aeEnv->absorbCoeffs[IVAS_PI_AE_FRONT] = mapAbsorbtion[( dWord >> 10 ) & MASK_ABS];
+ aeEnv->absorbCoeffs[IVAS_PI_AE_BACK] = mapAbsorbtion[( dWord >> 8 ) & MASK_ABS];
+ aeEnv->absorbCoeffs[IVAS_PI_AE_LEFT] = mapAbsorbtion[( dWord >> 6 ) & MASK_ABS];
+ aeEnv->absorbCoeffs[IVAS_PI_AE_RIGHT] = mapAbsorbtion[( dWord >> 4 ) & MASK_ABS];
+ aeEnv->absorbCoeffs[IVAS_PI_AE_CEILING] = mapAbsorbtion[( dWord >> 2 ) & MASK_ABS];
+ aeEnv->absorbCoeffs[IVAS_PI_AE_FLOOR] = mapAbsorbtion[( dWord >> 0 ) & MASK_ABS];
+ }
+
+ return IVAS_ERR_OK;
+}
+
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+static ivas_error packAudioDescription( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten )
+{
+ uint32_t n;
+ uint32_t nBytes = 0;
+ const IVAS_PIDATA_AUDIO_DESC *audioDesc = (const IVAS_PIDATA_AUDIO_DESC *) piData;
+ uint32_t packedSize = audioDesc->nValidEntries; /* Each Entry is 1 byte */
+
+ *nBytesWritten = 0;
+
+ if ( audioDesc->nValidEntries > ( IVAS_PI_MAX_OBJECTS + 1 ) )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNSUPPORTED_FRAME, "Audio Description cannot have more than 5 entries" );
+ }
+
+ /* Audio Description data is max 5 bytes, 2 bytes header */
+ if ( maxDataBytes < packedSize + 2 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space in Audio Description PI data buffer" );
+ }
+
+ buffer[nBytes++] = ( IVAS_PI_AUDIO_DESCRIPTION ); /* PF/PM populated during final packing */
+ buffer[nBytes++] = (uint8_t) packedSize;
+
+ for ( n = 0; n < audioDesc->nValidEntries; n++ )
+ {
+ buffer[nBytes++] = ( audioDesc->audioId[n].speech ? PI_AD_SPEECH_INDICATED : 0 ) |
+ ( audioDesc->audioId[n].music ? PI_AD_MUSIC_INDICATED : 0 ) |
+ ( audioDesc->audioId[n].ambiance ? PI_AD_AMBIANCE_INDICATED : 0 ) |
+ ( audioDesc->audioId[n].editable ? PI_AD_EDITABLE_INDICATED : 0 ) |
+ ( audioDesc->audioId[n].binaural ? PI_AD_BINAURAL_INDICATED : 0 );
+ }
+
+ *nBytesWritten = nBytes;
+ return IVAS_ERR_OK;
+}
+
+static ivas_error unpackAudioDescription( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData )
+{
+ uint32_t n;
+ IVAS_PIDATA_AUDIO_DESC *audioDesc = (IVAS_PIDATA_AUDIO_DESC *) piData;
+
+ /* Audio Description data is max 5 bytes */
+ if ( numDataBytes > ( IVAS_PI_MAX_OBJECTS + 1 ) )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack Orientation PI data" );
+ }
+
+ audioDesc->size = sizeof( IVAS_PIDATA_AUDIO_DESC );
+ audioDesc->nValidEntries = numDataBytes;
+ audioDesc->piDataType = IVAS_PI_AUDIO_DESCRIPTION;
+
+ for ( n = 0; n < audioDesc->nValidEntries; n++ )
+ {
+ audioDesc->audioId[n].speech = ( buffer[n] & PI_AD_SPEECH_INDICATED ) != 0;
+ audioDesc->audioId[n].music = ( buffer[n] & PI_AD_MUSIC_INDICATED ) != 0;
+ audioDesc->audioId[n].ambiance = ( buffer[n] & PI_AD_AMBIANCE_INDICATED ) != 0;
+ audioDesc->audioId[n].editable = ( buffer[n] & PI_AD_EDITABLE_INDICATED ) != 0;
+ audioDesc->audioId[n].binaural = ( buffer[n] & PI_AD_BINAURAL_INDICATED ) != 0;
+ }
+
+ return IVAS_ERR_OK;
+}
+
+static ivas_error packDynamicSuppression( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten )
+{
+ uint32_t nBytes = 0;
+ const IVAS_PIDATA_DYNAMIC_SUPPRESSION *das = (const IVAS_PIDATA_DYNAMIC_SUPPRESSION *) piData;
+
+ *nBytesWritten = 0;
+
+ /* Dynamic Audio Suppression data is 2 bytes, 2 bytes header */
+ if ( maxDataBytes < 2 + 2 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space in DAS PI data buffer" );
+ }
+
+ buffer[nBytes++] = ( IVAS_PI_DYNAMIC_AUDIO_SUPPRESSION ); /* PF/PM populated during final packing */
+ buffer[nBytes++] = 2u;
+
+ buffer[nBytes++] = ( das->speech ? PI_AD_SPEECH_INDICATED : 0 ) |
+ ( das->music ? PI_AD_MUSIC_INDICATED : 0 ) |
+ ( das->ambiance ? PI_AD_AMBIANCE_INDICATED : 0 );
+ buffer[nBytes++] = ( (uint8_t) das->sli & MASK_4BIT ) << 4;
+
+ *nBytesWritten = nBytes;
+ return IVAS_ERR_OK;
+}
+
+static ivas_error unpackDynamicSuppression( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData )
+{
+ IVAS_PIDATA_DYNAMIC_SUPPRESSION *das = (IVAS_PIDATA_DYNAMIC_SUPPRESSION *) piData;
+
+ /* Dynamic Suppression data is 2 bytes */
+ if ( numDataBytes != 2 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack DAS PI data" );
+ }
+
+ das->size = sizeof( IVAS_PIDATA_AUDIO_DESC );
+ das->piDataType = IVAS_PI_DYNAMIC_AUDIO_SUPPRESSION;
+ das->speech = ( buffer[0] & PI_AD_SPEECH_INDICATED ) != 0;
+ das->music = ( buffer[0] & PI_AD_MUSIC_INDICATED ) != 0;
+ das->ambiance = ( buffer[0] & PI_AD_AMBIANCE_INDICATED ) != 0;
+ das->sli = ( buffer[1] >> 4 );
+
+ return IVAS_ERR_OK;
+}
+
+static ivas_error packListenerPosition( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten )
+{
+ uint32_t nBytes = 0;
+ const IVAS_PIDATA_LISTENER_POSITION *listener = (const IVAS_PIDATA_LISTENER_POSITION *) piData;
+
+ *nBytesWritten = 0;
+
+ if ( piData->size != sizeof( IVAS_PIDATA_LISTENER_POSITION ) )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect size in LISTENER POSITION PI data" );
+ }
+
+ if ( piData->piDataType != IVAS_PI_LISTENER_POSITION )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect PI ID in LISTENER POSITION PI data" );
+ }
+
+ /* Position data is 6 bytes, header is 2 bytes */
+ if ( maxDataBytes < 6 + 2 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space to pack LISTENER POSITION PI data" );
+ }
+
+ buffer[nBytes++] = ( listener->piDataType & MASK_5BIT ); /* PF/PM populated during final packing */
+ buffer[nBytes++] = 6;
+ nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( listener->position.x / MAX_PI_POSITION_METERS ) );
+ nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( listener->position.y / MAX_PI_POSITION_METERS ) );
+ nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( listener->position.z / MAX_PI_POSITION_METERS ) );
+ *nBytesWritten = nBytes;
+ return IVAS_ERR_OK;
+}
+
+static ivas_error unpackListenerPosition( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData )
+{
+ IVAS_PIDATA_LISTENER_POSITION *listener = (IVAS_PIDATA_LISTENER_POSITION *) piData;
+
+ /* Position data is 6 bytes */
+ if ( numDataBytes != 6 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack LISTENER POSITION PI data" );
+ }
+
+ listener->size = sizeof( IVAS_PIDATA_LISTENER_POSITION );
+ listener->piDataType = IVAS_PI_LISTENER_POSITION;
+ listener->position.x = FLOAT_FROM_Q15( readInt16( &buffer[0] ) ) * MAX_PI_POSITION_METERS;
+ listener->position.y = FLOAT_FROM_Q15( readInt16( &buffer[2] ) ) * MAX_PI_POSITION_METERS;
+ listener->position.z = FLOAT_FROM_Q15( readInt16( &buffer[4] ) ) * MAX_PI_POSITION_METERS;
+ return IVAS_ERR_OK;
+}
+
+static ivas_error packDiegetic( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten )
+{
+ uint32_t nBytes = 0, n;
+ uint8_t byte = 0;
+ const IVAS_PIDATA_DIEGETIC *diegetic = (const IVAS_PIDATA_DIEGETIC *) piData;
+
+ *nBytesWritten = 0;
+
+ if ( piData->size != sizeof( IVAS_PIDATA_DIEGETIC ) )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect size in Diegetic Type PI data" );
+ }
+
+ if ( piData->piDataType != IVAS_PI_DIEGETIC_TYPE )
+ {
+ return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect PI ID in Diegetic Type PI data" );
+ }
+
+ /* Diegetic data is 1 bytes, header is 2 bytes */
+ if ( maxDataBytes < 1 + 2 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space to pack Diegetic Type PI data" );
+ }
+
+ /* Valid bits must be based on active bits defined for the input format */
+ for ( n = 0; n < ( IVAS_PI_MAX_OBJECTS + 1 ); n++ )
+ {
+ byte <<= 1;
+ byte |= ( diegetic->isDiegetic[n] );
+ }
+ byte <<= 3;
+
+ buffer[nBytes++] = ( diegetic->piDataType & MASK_5BIT ); /* PF/PM populated during final packing */
+ buffer[nBytes++] = 1;
+ buffer[nBytes++] = byte;
+ *nBytesWritten = nBytes;
+ return IVAS_ERR_OK;
+}
+
+static ivas_error unpackDiegetic( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData )
+{
+ uint32_t n;
+ IVAS_PIDATA_DIEGETIC *diegetic = (IVAS_PIDATA_DIEGETIC *) piData;
+ uint8_t byte;
+
+ /* Diegetic data is 1 bytes */
+ if ( numDataBytes != 1 )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack Diegetic PI data" );
+ }
+
+ diegetic->size = sizeof( IVAS_PIDATA_DIEGETIC );
+ diegetic->piDataType = IVAS_PI_DIEGETIC_TYPE;
+
+ byte = buffer[0];
+ /* Valid bits must be based on active bits defined for the input format */
+ for ( n = 0; n < ( IVAS_PI_MAX_OBJECTS + 1 ); n++ )
+ {
+ diegetic->isDiegetic[n] = ( ( byte >> ( 7 - n ) ) & 1 ) != 0;
+ }
+
+ return IVAS_ERR_OK;
+}
+
+
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+
+static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = {
+ packOrientation, /* SCENE_ORIENTATION */
+ packOrientation, /* DEVICE_ORIENTATION_COMPENSATED */
+ packOrientation, /* DEVICE_ORIENTATION_UNCOMPENSATED */
+ packAcousticEnvironment, /* ACOUSTIC_ENVIRONMENT */
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ packAudioDescription, /* AUDIO_DESCRIPTION */
+#else
+ packUnsupportedData, /* AUDIO_DESCRIPTION */
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+ packUnsupportedData, /* ISM_NUM */
+ packUnsupportedData, /* ISM_ID */
+ packUnsupportedData, /* ISM_GAIN */
+ packUnsupportedData, /* ISM_ORIENTATION */
+ packUnsupportedData, /* ISM_POSITION */
+ packUnsupportedData, /* ISM_DISTANCE_ATTENUATION */
+ packUnsupportedData, /* ISM_DIRECTIVITY */
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ packDiegetic, /* DIEGETIC_TYPE */
+#else
+ packUnsupportedData, /* DIEGETIC_TYPE */
+#endif
+ packUnsupportedData, /* RESERVED13 */
+ packUnsupportedData, /* RESERVED14 */
+ packUnsupportedData, /* RESERVED15 */
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ packOrientation, /* PLAYBACK_DEVICE_ORIENTATION */
+ packOrientation, /* HEAD_ORIENTATION */
+ packListenerPosition, /* LISTENER_POSITION */
+ packDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION */
+ packOrientation, /* AUDIO_FOCUS_DIRECTION */
+#else
+ packUnsupportedData, /* PLAYBACK_DEVICE_ORIENTATION */
+ packUnsupportedData, /* HEAD_ORIENTATION */
+ packUnsupportedData, /* LISTENER_POSITION */
+ packUnsupportedData, /* DYNAMIC_AUDIO_SUPPRESSION */
+ packUnsupportedData, /* AUDIO_FOCUS_DIRECTION */
+#endif
+ packUnsupportedData, /* PI_LATENCY */
+ packUnsupportedData, /* R_ISM_ID */
+ packUnsupportedData, /* R_ISM_GAIN */
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ packOrientation, /* R_ISM_ORIENTATION */
+#else
+ packUnsupportedData, /* R_ISM_ORIENTATION */
+#endif
+ packUnsupportedData, /* R_ISM_POSITION */
+ packUnsupportedData, /* R_ISM_DIRECTION */
+ packUnsupportedData, /* RESERVED27 */
+ packUnsupportedData, /* RESERVED28 */
+ packUnsupportedData, /* RESERVED29 */
+ packUnsupportedData, /* RESERVED30 */
+ packNoPiData /* NO_DATA */
+};
+
+static const UNPACK_PI_FN unpackPiDataFuntions[IVAS_PI_MAX_ID] = {
+ unpackOrientation, /* SCENE_ORIENTATION */
+ unpackOrientation, /* DEVICE_ORIENTATION_COMPENSATED */
+ unpackOrientation, /* DEVICE_ORIENTATION_UNCOMPENSATED */
+ unpackAcousticEnvironment, /* ACOUSTIC_ENVIRONMENT */
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ unpackAudioDescription, /* AUDIO_DESCRIPTION */
+#else
+ unpackUnsupportedData, /* AUDIO_DESCRIPTION */
+#endif
+ unpackUnsupportedData, /* ISM_NUM */
+ unpackUnsupportedData, /* ISM_ID */
+ unpackUnsupportedData, /* ISM_GAIN */
+ unpackUnsupportedData, /* ISM_ORIENTATION */
+ unpackUnsupportedData, /* ISM_POSITION */
+ unpackUnsupportedData, /* ISM_DISTANCE_ATTENUATION */
+ unpackUnsupportedData, /* ISM_DIRECTIVITY */
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ unpackDiegetic, /* DIEGETIC_TYPE */
+#else
+ unpackUnsupportedData, /* DIEGETIC_TYPE */
+#endif
+ unpackUnsupportedData, /* RESERVED13 */
+ unpackUnsupportedData, /* RESERVED14 */
+ unpackUnsupportedData, /* RESERVED15 */
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ unpackOrientation, /* PLAYBACK_DEVICE_ORIENTATION */
+ unpackOrientation, /* HEAD_ORIENTATION */
+ unpackListenerPosition, /* LISTENER_POSITION */
+ unpackDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION */
+ unpackOrientation, /* AUDIO_FOCUS_DIRECTION */
+#else
+ unpackUnsupportedData, /* PLAYBACK_DEVICE_ORIENTATION */
+ unpackUnsupportedData, /* HEAD_ORIENTATION */
+ unpackUnsupportedData, /* LISTENER_POSITION */
+ unpackUnsupportedData, /* DYNAMIC_AUDIO_SUPPRESSION */
+ unpackUnsupportedData, /* AUDIO_FOCUS_DIRECTION */
+#endif
+ unpackUnsupportedData, /* PI_LATENCY */
+ unpackUnsupportedData, /* R_ISM_ID */
+ unpackUnsupportedData, /* R_ISM_GAIN */
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ unpackOrientation, /* R_ISM_ORIENTATION */
+#else
+ unpackUnsupportedData, /* R_ISM_ORIENTATION */
+#endif
+ unpackUnsupportedData, /* R_ISM_POSITION */
+ unpackUnsupportedData, /* R_ISM_DIRECTION */
+ unpackUnsupportedData, /* RESERVED27 */
+ unpackUnsupportedData, /* RESERVED28 */
+ unpackUnsupportedData, /* RESERVED29 */
+ unpackUnsupportedData, /* RESERVED30 */
+ unpackNoPiData /* NO_DATA */
+};
+
+static const uint32_t maxPiDataSize[IVAS_PI_MAX_ID] = {
+ 8, /* IVAS_PI_SCENE_ORIENTATION */
+ 8, /* IVAS_PI_DEVICE_ORIENTATION_COMPENSATED */
+ 8, /* IVAS_PI_DEVICE_ORIENTATION_UNCOMPENSATED */
+ 8, /* IVAS_PI_ACOUSTIC_ENVIRONMENT */
+ 5, /* IVAS_PI_AUDIO_DESCRIPTION */
+ 1, /* IVAS_PI_ISM_NUM */
+ 4, /* IVAS_PI_ISM_ID */
+ 4, /* IVAS_PI_ISM_GAIN */
+ 32, /* IVAS_PI_ISM_ORIENTATION */
+ 24, /* IVAS_PI_ISM_POSITION */
+ 12, /* IVAS_PI_ISM_DISTANCE_ATTENUATION */
+ 8, /* IVAS_PI_ISM_DIRECTIVITY */
+ 1, /* IVAS_PI_DIEGETIC_TYPE */
+ 0, /* IVAS_PI_RESERVED13 */
+ 0, /* IVAS_PI_RESERVED14 */
+ 0, /* IVAS_PI_RESERVED15 */
+ 8, /* IVAS_PI_PLAYBACK_DEVICE_ORIENTATION */
+ 8, /* IVAS_PI_HEAD_ORIENTATION */
+ 6, /* IVAS_PI_LISTENER_POSITION */
+ 2, /* IVAS_PI_DYNAMIC_AUDIO_SUPPRESSION */
+ 8, /* IVAS_PI_AUDIO_FOCUS_DIRECTION */
+ 4, /* IVAS_PI_PI_LATENCY */
+ 1, /* IVAS_PI_R_ISM_ID */
+ 1, /* IVAS_PI_R_ISM_GAIN */
+ 8, /* IVAS_PI_R_ISM_ORIENTATION */
+ 6, /* IVAS_PI_R_ISM_POSITION */
+ 2, /* IVAS_PI_R_ISM_DIRECTION */
+ 0, /* IVAS_PI_RESERVED27 */
+ 0, /* IVAS_PI_RESERVED28 */
+ 0, /* IVAS_PI_RESERVED29 */
+ 0, /* IVAS_PI_RESERVED30 */
+ 0, /* IVAS_PI_NO_DATA = 31 */
+};
+
+ivas_error PI_PackData( const IVAS_PIDATA_GENERIC *piData, PIDATA_PACKED *packed, uint8_t pmBits )
+{
+ uint32_t type = (IVAS_PI_TYPE) ( piData->piDataType & MASK_5BIT );
+ ivas_error error = packPiDataFuntions[type]( piData, packed->data, sizeof( packed->data ), &packed->size );
+ if ( error == IVAS_ERR_OK )
+ {
+ packed->data[0] |= pmBits; /* Update the PM bits */
+ }
+ assert( packed->size != 0 );
+ return error;
+}
+
+ivas_error PI_UnPackData( uint8_t piDataType, uint32_t piSize, const uint8_t *piDataBuffer, IVAS_PIDATA_GENERIC *piData )
+{
+ ivas_error error;
+
+ /* Sanitize maximum sizes for each PI Type */
+ if ( piSize > maxPiDataSize[piDataType] )
+ {
+ return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Max size for PI Data type exceeded" );
+ }
+
+ error = unpackPiDataFuntions[piDataType]( piDataBuffer, piSize, piData );
+ ERR_CHECK_RETURN( error );
+
+ /* since some pi data share piData structure, pi id are re-filled after unpacking */
+ piData->piDataType = piDataType;
+
+ return IVAS_ERR_OK;
+}
+
+/* PIDATA Tables */
+const float mapRT60[1u << NBITS_RT60] = {
+ 0.01f, 0.0126f, 0.0159f, 0.02f, 0.0252f, 0.0317f, 0.04f, 0.0504f,
+ 0.0635f, 0.08f, 0.1008f, 0.1270f, 0.16f, 0.2016f, 0.2540f, 0.32f,
+ 0.4032f, 0.5080f, 0.64f, 0.8063f, 1.0159f, 1.28f, 1.6127f, 2.0319f,
+ 2.56f, 3.2254f, 4.0637f, 5.12f, 6.4508f, 8.1275f, 10.24f, 12.9016f
+};
+
+const float mapDSR[1u << NBITS_DSR] = {
+ -20.f, -21.f, -22.f, -23.f, -24.f, -25.f, -26.f, -27.f,
+ -28.f, -29.f, -30.f, -31.f, -32.f, -33.f, -34.f, -35.f,
+ -36.f, -37.f, -38.f, -39.f, -40.f, -41.f, -42.f, -43.f,
+ -44.f, -45.f, -46.f, -47.f, -48.f, -49.f, -50.f, -51.f,
+ -52.f, -53.f, -54.f, -55.f, -56.f, -57.f, -58.f, -59.f,
+ -60.f, -61.f, -62.f, -63.f, -64.f, -65.f, -66.f, -67.f,
+ -68.f, -69.f, -70.f, -71.f, -72.f, -73.f, -74.f, -75.f,
+ -76.f, -77.f, -78.f, -79.f, -80.f, -81.f, -82.f, -83.f
+};
+
+const float mapRoomDims[1u << NBITS_DIM] = {
+ 0.5f, 0.707f, 1.f, 1.4141f, 2, 2.8282f, 4.f, 5.6568f,
+ 8.f, 11.314f, 16.f, 22.627f, 32.f, 45.255f, 64.f, 90.51f
+};
+
+const float mapAbsorbtion[1u << NBITS_ABS] = {
+ 0.0800f, 0.1656f, 0.3430f, 0.7101f
+};
+
+
+#endif /* IVAS_RTPDUMP */
diff --git a/lib_util/ivas_rtp_pi_data.h b/lib_util/ivas_rtp_pi_data.h
new file mode 100644
index 0000000000000000000000000000000000000000..3be265bb87a46b2f9dad7a5d6020323b59f111cc
--- /dev/null
+++ b/lib_util/ivas_rtp_pi_data.h
@@ -0,0 +1,441 @@
+/******************************************************************************************************
+
+ (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository. All Rights Reserved.
+
+ This software is protected by copyright law and by international treaties.
+ The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository retain full ownership rights in their respective contributions in
+ the software. This notice grants no license of any kind, including but not limited to patent
+ license, nor is any license granted by implication, estoppel or otherwise.
+
+ Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
+ contributions.
+
+ This software is provided "AS IS", without any express or implied warranties. The software is in the
+ development stage. It is intended exclusively for experts who have experience with such software and
+ solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
+ and fitness for a particular purpose are hereby disclaimed and excluded.
+
+ Any dispute, controversy or claim arising under or in relation to providing this software shall be
+ submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
+ accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
+ the United Nations Convention on Contracts on the International Sales of Goods.
+
+*******************************************************************************************************/
+
+#ifndef IVAS_RTP_PI_DATA_H
+#define IVAS_RTP_PI_DATA_H
+
+#include "common_api_types.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef IVAS_RTPDUMP
+
+#define IVAS_PI_MAX_DATA_SIZE ( 32 + 2 ) /* max packed PI data bytes + pi header bytes */
+#define IVAS_PI_MAX_OBJECTS ( 4 ) /* max ISM objects in PI data */
+
+ /* IVAS PI Data Types */
+ typedef enum
+ {
+ /* Forward direction PI types */
+ IVAS_PI_SCENE_ORIENTATION, /* orientation of audio scene in unit quaternions */
+ IVAS_PI_DEVICE_ORIENTATION_COMPENSATED, /* orientation of device in unit quaternions (compensated) */
+ IVAS_PI_DEVICE_ORIENTATION_UNCOMPENSATED, /* orientation of device in unit quaternions (un-compensated) */
+ IVAS_PI_ACOUSTIC_ENVIRONMENT, /* describe the acoustic environment */
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ IVAS_PI_AUDIO_DESCRIPTION, /* audio content description (voice/music/ambiance) */
+ IVAS_PI_ISM_NUM, /* Number of objects */
+ IVAS_PI_ISM_ID, /* id of each object */
+ IVAS_PI_ISM_GAIN, /* gain of each object */
+ IVAS_PI_ISM_ORIENTATION, /* orientation of each object */
+ IVAS_PI_ISM_POSITION, /* position of each object */
+ IVAS_PI_ISM_DISTANCE_ATTENUATION, /* distance attenuation for each object */
+ IVAS_PI_ISM_DIRECTIVITY, /* directivity of each object */
+ IVAS_PI_DIEGETIC_TYPE, /* digetic audio indication */
+ IVAS_PI_RESERVED13, /* reserved */
+ IVAS_PI_RESERVED14, /* reserved */
+ IVAS_PI_RESERVED15, /* reserved */
+
+ /* Reverse direction PI types */
+ IVAS_PI_PLAYBACK_DEVICE_ORIENTATION, /* orientation of the playback device in quaternions */
+ IVAS_PI_HEAD_ORIENTATION, /* head orientation of the listener in Quaternions */
+ IVAS_PI_LISTENER_POSITION, /* position of the listener in 3D space */
+ IVAS_PI_DYNAMIC_AUDIO_SUPPRESSION, /* receiver’s preference with respect to audio suppression */
+ IVAS_PI_AUDIO_FOCUS_DIRECTION, /* direction of interest for the listener in Quaternions */
+ IVAS_PI_PI_LATENCY, /* round-trip latency for PI frames */
+ IVAS_PI_R_ISM_ID, /* id of an object for editing */
+ IVAS_PI_R_ISM_GAIN, /* editing request for gain factor for received object */
+ IVAS_PI_R_ISM_ORIENTATION, /* editing request for orientation for received object */
+ IVAS_PI_R_ISM_POSITION, /* editing request for position for received object */
+ IVAS_PI_R_ISM_DIRECTION, /* editing request for direction for received object */
+ IVAS_PI_RESERVED27, /* reserved */
+ IVAS_PI_RESERVED28, /* reserved */
+ IVAS_PI_RESERVED29, /* reserved */
+ IVAS_PI_RESERVED30, /* reserved */
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+ IVAS_PI_NO_DATA = 31, /* Indicates an empty PI data frame */
+ IVAS_PI_MAX_ID /* Max number of PI data IDs supprted */
+ } IVAS_PI_TYPE;
+
+ /* cartesian coordinates (X,Y,Z) in 3D space */
+ typedef struct
+ {
+ float x, y, z;
+ } IVAS_COORDINATE;
+
+ /* orientation data corresponding to any of the following pi data types :-
+ * - IVAS_PI_SCENE_ORIENTATION
+ * - IVAS_PI_DEVICE_ORIENTATION_COMPENSATED
+ * - IVAS_PI_DEVICE_ORIENTATION_UNCOMPENSATED
+ * - IVAS_PI_PLAYBACK_DEVICE_ORIENTATION
+ * - IVAS_PI_HEAD_ORIENTATION
+ * - IVAS_PI_AUDIO_FOCUS_DIRECTION
+ * - IVAS_PI_R_ISM_ORIENTATION
+ *
+ * piDataType is used to identify the correct pi data type contained here
+ */
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_SCENE_ORIENTATION) */
+ uint32_t piDataType; /* one of supported orientation data types */
+ IVAS_QUATERNION orientation; /* orientation data expressed as quartenions */
+ } IVAS_PIDATA_ORIENTATION;
+
+ /* Acoustic environment corresponding to IVAS_PI_ACOUSTIC_ENVIRONMENT
+ *
+ * acoustic environment ID
+ * late reverb parameters
+ * - RT60 – indicating the time that it takes for the reflections to reduce 60 dB in energy level, per frequency band
+ * - DSR – diffuse to source signal energy ratio, per frequency band
+ * - Pre-delay – delay at which the computation of DSR values was performed
+ * early reflections
+ * - 3D rectangular virtual room dimensions
+ * - Broadband energy absorption coefficient per wall surface
+ */
+ typedef enum
+ {
+ IVAS_PI_AE_LOW, /* center frequency 25 Hz */
+ IVAS_PI_AE_MID, /* center frequency 250 Hz */
+ IVAS_PI_AE_HIGH, /* center frequency 2.5 kHz */
+ IVAS_PI_AE_NUM_BANDS /* number of ae bands */
+ } IVAS_PI_AE_BANDS;
+
+ typedef enum
+ {
+ IVAS_PI_AE_FRONT,
+ IVAS_PI_AE_BACK,
+ IVAS_PI_AE_LEFT,
+ IVAS_PI_AE_RIGHT,
+ IVAS_PI_AE_CEILING,
+ IVAS_PI_AE_FLOOR,
+ IVAS_PI_AE_NUM_SURFACE
+ } IVAS_PI_AE_SURFACE;
+
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_ACOUSTIC_ENV) */
+ uint32_t piDataType; /* IVAS_PI_ACOUSTIC_ENVIRONMENT */
+ bool availLateReverb; /* AE contains only late reverb parameters */
+ bool availEarlyReflections; /* AE containing late reverb and simplified early reflections */
+ uint8_t aeid; /* seven-bit acoustic environment identifier */
+
+ /* only valid if availLateReverb==true or availEarlyReflections==true */
+ float rt60[IVAS_PI_AE_NUM_BANDS]; /* time for the reflections to reduce 60 dB per band in seconds */
+ float dsr[IVAS_PI_AE_NUM_BANDS]; /* diffuse to source signal energy ratio per band in dB */
+
+ /* only valid if availEarlyReflections==true */
+ IVAS_COORDINATE roomDimensions; /* room dimensions in meters length (x), width (y), height (z) */
+ float absorbCoeffs[IVAS_PI_AE_NUM_SURFACE]; /* absorption coefficients for all surfaces */
+ } IVAS_PIDATA_ACOUSTIC_ENV;
+
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ /* Audio Description corresponding to IVAS_PI_AUDIO_DESCRIPTION
+ * Describe the following audio decriptors per object/type :-
+ * - audio content type is speech/music/ambiance
+ * - if audio rendering is editable
+ * - if stereo audio is binaural
+ *
+ * number of valid entries decide on basis of audio format:-
+ * - Stereo/SBA/MASA = 1 entry
+ * - MultiChannel = 2 entries (1 for center channel + 1 for all other channels)
+ * - ISM = Number of Object entries ( 1 per object )
+ * - OMASA/OSBA = 1 + Num Discrete Coded Objects
+ *
+ */
+ typedef struct
+ {
+ bool speech; /* audio has voice/speech */
+ bool music; /* audio has music */
+ bool ambiance; /* audio has background ambiance */
+ bool editable; /* rendering audio metadata is editable */
+ bool binaural; /* stereo stream is binaural */
+ } IVAS_AUDIO_ID;
+
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_AUDIO_DESC) */
+ uint32_t piDataType; /* IVAS_PI_AUDIO_DESCRIPTION */
+ uint32_t nValidEntries; /* Number of valid audio IDs */
+ IVAS_AUDIO_ID audioId[1 + IVAS_PI_MAX_OBJECTS]; /* audio id as per format */
+ } IVAS_PIDATA_AUDIO_DESC;
+
+ /* ISM specific PI data related to PI types : -
+ *
+ * - IVAS_PI_ISM_NUM
+ * - IVAS_PI_ISM_ID
+ * - IVAS_PI_ISM_GAIN
+ * - IVAS_PI_ISM_ORIENTATION
+ * - IVAS_PI_ISM_POSITION
+ * - IVAS_PI_ISM_DISTANCE_ATTENUATION
+ * - IVAS_PI_ISM_DIRECTIVITY
+ */
+
+ /* Number of ISMs */
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_ISM_NUM) */
+ uint32_t piDataType; /* IVAS_PI_ISM_NUM */
+ uint32_t numObjects; /* Number of ISM */
+ } IVAS_PIDATA_ISM_NUM;
+
+ /* ISM ID */
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_ISM_ID) */
+ uint32_t piDataType; /* IVAS_PI_ISM_ID */
+ uint8_t id[IVAS_PI_MAX_OBJECTS]; /* 8-bit ISM id of object */
+ } IVAS_PIDATA_ISM_ID;
+
+ /* ISM gain */
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_ISM_GAIN) */
+ uint32_t piDataType; /* IVAS_PI_ISM_GAIN */
+ int8_t dB[IVAS_PI_MAX_OBJECTS]; /* ISM gain in dB per object [-96, +3] */
+ } IVAS_PIDATA_ISM_GAIN;
+
+ /* ISM orientation */
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_ISM_ORIENTATION) */
+ uint32_t piDataType; /* IVAS_PI_ISM_ORIENTATION */
+ IVAS_QUATERNION orientation[IVAS_PI_MAX_OBJECTS]; /* Orientation of audio objects in ISM(s) */
+ } IVAS_PIDATA_ISM_ORIENTATION;
+
+ /* ISM position */
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_ISM_POSITION) */
+ uint32_t piDataType; /* IVAS_PI_ISM_POSITION */
+ IVAS_COORDINATE position[IVAS_PI_MAX_OBJECTS]; /* Position of audio objects in ISM(s) */
+ } IVAS_PIDATA_ISM_POSITION;
+
+ /* ISM distance attenuation comprising of following gains per ISM
+ * - reference distance
+ * - maximum distance
+ * - roll-off factor
+ */
+ typedef struct
+ {
+ float ref_dist; /* reference distance in meters */
+ float max_dist; /* maximum distance in meters */
+ float roll; /* roll-off factor values */
+ } IVAS_DIST_ATTEN;
+
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_ISM_ATTENUATION) */
+ uint32_t piDataType; /* IVAS_PI_ISM_DISTANCE_ATTENUATION */
+ IVAS_DIST_ATTEN distAtten[IVAS_PI_MAX_OBJECTS]; /* Distance attenuation of audio objects */
+ } IVAS_PIDATA_ISM_ATTENUATION;
+
+ /* ISM Directivity comprising of following per ISM :-
+ * - inner cone angle determines the size of the main cone directed to the front of the object
+ * - outer cone angle determines the size of the outer (back) cone
+ * - outer attenuation gain determines the attenuation outside the outer cone
+ */
+ typedef struct
+ {
+ uint16_t innerConeAngle; /* inner cone angle in degrees (0 - 360) */
+ uint16_t outerConeAngle; /* outer cone angle in degrees (0 - 360) */
+ float outerAttenuationdB; /* attenuation outside the outer cone in dB */
+ } IVAS_ISM_DIRECTIVITY;
+
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_ISM_DIRECTIVITY) */
+ uint32_t piDataType; /* IVAS_PI_ISM_DIRECTIVITY */
+ IVAS_ISM_DIRECTIVITY directivity[IVAS_PI_MAX_OBJECTS]; /* Directivity of audio objects */
+ } IVAS_PIDATA_ISM_DIRECTIVITY;
+
+ /* Diegetic and non-diegetic indication flag as per audio format
+ *
+ * number of valid entries decided on basis of audio format:-
+ * - Stereo/SBA/MASA/MultiChannel = 1 entry
+ * - ISM = Number of Object entries ( 1 per object )
+ * - OMASA/OSBA = 1 (last) + Num Discrete Coded Objects
+ */
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_DIEGETIC) */
+ uint32_t piDataType; /* IVAS_PI_DIEGETIC_TYPE */
+ bool isDiegetic[1 + IVAS_PI_MAX_OBJECTS]; /* diegetic indication as per audio format */
+ } IVAS_PIDATA_DIEGETIC;
+
+ /* Listener position */
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_LISTENER_POSITION) */
+ uint32_t piDataType; /* IVAS_PI_LISTENER_POSITION */
+ IVAS_COORDINATE position; /* Position of audio objects in ISM(s) */
+ } IVAS_PIDATA_LISTENER_POSITION;
+
+
+ /* Dynamic Audio Suppression describes receiver’s preference with respect to the
+ * type of audio content that should be enhanced and the amount of suppression to
+ * be applied to the background noise
+ */
+ typedef enum
+ {
+ IVAS_SLI_NO_SUPPRESSION = 0, /* Apply no suppression */
+ IVAS_SLI_SUPPRESSION_LEVEL_1, /* Suppression level 1 */
+ IVAS_SLI_SUPPRESSION_LEVEL_2, /* Suppression level 2 */
+ IVAS_SLI_SUPPRESSION_LEVEL_3, /* Suppression level 3 */
+ IVAS_SLI_SUPPRESSION_LEVEL_4, /* Suppression level 4 */
+ IVAS_SLI_SUPPRESSION_LEVEL_5, /* Suppression level 5 */
+ IVAS_SLI_SUPPRESSION_LEVEL_6, /* Suppression level 6 */
+ IVAS_SLI_SUPPRESSION_LEVEL_7, /* Suppression level 7 */
+ IVAS_SLI_SUPPRESSION_LEVEL_8, /* Suppression level 8 */
+ IVAS_SLI_SUPPRESSION_LEVEL_9, /* Suppression level 9 */
+ IVAS_SLI_SUPPRESSION_LEVEL_10, /* Suppression level 10 */
+ IVAS_SLI_SUPPRESSION_LEVEL_11, /* Suppression level 11 */
+ IVAS_SLI_SUPPRESSION_LEVEL_12, /* Suppression level 12 */
+ IVAS_SLI_SUPPRESSION_LEVEL_13, /* Suppression level 13 */
+ IVAS_SLI_SUPPRESSION_LEVEL_14, /* Suppression level 14 */
+ IVAS_SLI_MAX_SUPPRESSION, /* Apply max suppression */
+ } IVAS_SLI;
+
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_DYNAMIC_SUPPRESSION) */
+ uint32_t piDataType; /* IVAS_PI_DYNAMIC_AUDIO_SUPPRESSION */
+ bool speech; /* receiver's preference is voice/speech */
+ bool music; /* receiver's preference is music */
+ bool ambiance; /* receiver's preference is background ambiance */
+ IVAS_SLI sli; /* suppression level indicator [0, 15] */
+ } IVAS_PIDATA_DYNAMIC_SUPPRESSION;
+
+ /* Reverse PI latency calculated as the elapsed time between the sent reverse PI data
+ * and received forward PI data. It is based on the receiving device experiencing the
+ * result of its sent data by receiving the corresponding data in forward direction as
+ * forward PI data
+ */
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_REVERSE_PI_LATENCY) */
+ uint32_t piDataType; /* IVAS_PI_PI_LATENCY */
+ IVAS_PI_TYPE type; /* Reverse PI used for computation of Latency */
+ int32_t latency; /* Latency as 27-bit int on RTP Clock @ 16KHz */
+ } IVAS_PIDATA_REVERSE_PI_LATENCY;
+
+ /* ISM specific PI data editing requests */
+
+ /* ISM ID in editing requests */
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_ISM_EDIT_ID) */
+ uint32_t piDataType; /* IVAS_PI_R_ISM_ID */
+ uint8_t id; /* 8-bit ISM id of object to edit */
+ } IVAS_PIDATA_ISM_EDIT_ID;
+
+ /* Editing request for ISM gain */
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_ISM_EDIT_GAIN) */
+ uint32_t piDataType; /* IVAS_PI_R_ISM_GAIN */
+ int8_t dB; /* Preferred ISM gain in dB [-96, +3] */
+ } IVAS_PIDATA_ISM_EDIT_GAIN;
+
+ /* Editing request for ISM orientation */
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_ISM_EDIT_ORIENTATION) */
+ uint32_t piDataType; /* IVAS_PI_R_ISM_ORIENTATION */
+ IVAS_QUATERNION orientation; /* orientation editing request for received ISM */
+ } IVAS_PIDATA_ISM_EDIT_ORIENTATION;
+
+ /* Editing request for ISM position */
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_ISM_EDIT_POSITION) */
+ uint32_t piDataType; /* IVAS_PI_R_ISM_POSITION */
+ IVAS_COORDINATE position; /* Positional editing request for received ISM */
+ } IVAS_PIDATA_ISM_EDIT_POSITION;
+
+ /* Editing request for ISM direction */
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_ISM_EDIT_DIRECTION) */
+ uint32_t piDataType; /* IVAS_PI_R_ISM_DIRECTION */
+ float azimuth; /* azimuth angle in degrees [-180, 180] */
+ float elevation; /* elevation angle in degrees [-90°, 90°] */
+ } IVAS_PIDATA_ISM_EDIT_DIRECTION;
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+
+ typedef struct
+ {
+ size_t size; /* sizeof(IVAS_PIDATA_NO_DATA) */
+ uint32_t piDataType; /* IVAS_PI_NO_DATA */
+ } IVAS_PIDATA_NO_DATA;
+
+
+ typedef union
+ {
+ IVAS_PIDATA_ORIENTATION scene;
+ IVAS_PIDATA_ORIENTATION deviceCompensated;
+ IVAS_PIDATA_ORIENTATION deviceUnCompensated;
+ IVAS_PIDATA_ACOUSTIC_ENV acousticEnv;
+#ifdef RTP_S4_251135_CR26253_0016_REV1
+ IVAS_PIDATA_AUDIO_DESC audioDesc;
+ IVAS_PIDATA_ISM_NUM ismNum;
+ IVAS_PIDATA_ISM_ID ismId;
+ IVAS_PIDATA_ISM_GAIN ismGain;
+ IVAS_PIDATA_ISM_ORIENTATION ismOrientation;
+ IVAS_PIDATA_ISM_POSITION ismPosition;
+ IVAS_PIDATA_ISM_ATTENUATION ismAttenuation;
+ IVAS_PIDATA_ISM_DIRECTIVITY ismDirectivity;
+ IVAS_PIDATA_DIEGETIC digeticIndicator;
+
+ IVAS_PIDATA_ORIENTATION playbackOrientation;
+ IVAS_PIDATA_ORIENTATION headOrientation;
+ IVAS_PIDATA_LISTENER_POSITION listnerPosition;
+ IVAS_PIDATA_DYNAMIC_SUPPRESSION dynSuppression;
+ IVAS_PIDATA_ORIENTATION focusDirection;
+ IVAS_PIDATA_REVERSE_PI_LATENCY piLatency;
+ IVAS_PIDATA_ISM_EDIT_ID ismEditId;
+ IVAS_PIDATA_ISM_EDIT_GAIN ismEditGain;
+ IVAS_PIDATA_ISM_EDIT_ORIENTATION ismEditOrientation;
+ IVAS_PIDATA_ISM_EDIT_POSITION ismEditPosition;
+ IVAS_PIDATA_ISM_EDIT_DIRECTION ismEditDirection;
+#endif /* RTP_S4_251135_CR26253_0016_REV1 */
+ IVAS_PIDATA_NO_DATA noPiData;
+ } PIDATA;
+
+#endif /* IVAS_RTPDUMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* IVAS_RTP_PI_DATA_H */
diff --git a/lib_util/mutex.h b/lib_util/mutex.h
new file mode 100644
index 0000000000000000000000000000000000000000..caf0145e2e6ebef39a474de45738108958d94137
--- /dev/null
+++ b/lib_util/mutex.h
@@ -0,0 +1,104 @@
+/******************************************************************************************************
+
+ (C) 2022-2024 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository. All Rights Reserved.
+
+ This software is protected by copyright law and by international treaties.
+ The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
+ Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+ Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+ Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+ contributors to this repository retain full ownership rights in their respective contributions in
+ the software. This notice grants no license of any kind, including but not limited to patent
+ license, nor is any license granted by implication, estoppel or otherwise.
+
+ Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
+ contributions.
+
+ This software is provided "AS IS", without any express or implied warranties. The software is in the
+ development stage. It is intended exclusively for experts who have experience with such software and
+ solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
+ and fitness for a particular purpose are hereby disclaimed and excluded.
+
+ Any dispute, controversy or claim arising under or in relation to providing this software shall be
+ submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
+ accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
+ the United Nations Convention on Contracts on the International Sales of Goods.
+
+*******************************************************************************************************/
+
+/*====================================================================================
+ EVS Codec 3GPP TS26.443 Nov 04, 2021. Version 12.14.0 / 13.10.0 / 14.6.0 / 15.4.0 / 16.3.0
+ ====================================================================================*/
+
+#ifndef _MUTEX_H
+#define _MUTEX_H
+
+#if defined( __unix__ ) || defined( __linux__ ) || ( defined( __MACH__ ) && defined( __APPLE__ ) )
+#include
+typedef pthread_mutex_t mtx_t;
+#define _USE_POSIX ( 1 )
+#elif defined( _WIN32 ) || defined( _WIN64 )
+#include
+typedef CRITICAL_SECTION mtx_t;
+#define _USE_WIN ( 1 )
+#else
+typedef int mtx_t;
+#warning Mutex implementation to be defined for this platform here.
+#endif
+
+static __inline int mtx_init( mtx_t *mutex, int type )
+{
+ int err = 0;
+ (void) type;
+#ifdef _USE_POSIX
+ err = pthread_mutex_init( mutex, NULL );
+#elif defined( _USE_WIN )
+ InitializeCriticalSection( mutex );
+#else
+ (void) mutex;
+#endif
+ return err;
+}
+
+static __inline void mtx_destroy( mtx_t *mutex )
+{
+#if _USE_POSIX
+ pthread_mutex_destroy( mutex );
+#elif defined( _USE_WIN )
+ DeleteCriticalSection( mutex );
+#else
+ (void) mutex;
+#endif
+}
+
+static __inline int mtx_lock( mtx_t *mutex )
+{
+ int err = 0;
+#if _USE_POSIX
+ err = pthread_mutex_lock( mutex );
+#elif defined( _USE_WIN )
+ EnterCriticalSection( mutex );
+#else
+ (void) mutex;
+#endif
+ return err;
+}
+
+static __inline int mtx_unlock( mtx_t *mutex )
+{
+ int err = 0;
+#if _USE_POSIX
+ err = pthread_mutex_unlock( mutex );
+#elif defined( _USE_WIN )
+ LeaveCriticalSection( mutex );
+#else
+ (void) mutex;
+#endif
+ return err;
+}
+
+#endif /* _MUTEX_H */
diff --git a/lib_util/rtpdump.c b/lib_util/rtpdump.c
index 9b25633a9385f57d14922a659a9bfe932a5dd1d4..7129a514fdce695de3de05bbdc20065987e7d407 100644
--- a/lib_util/rtpdump.c
+++ b/lib_util/rtpdump.c
@@ -110,10 +110,17 @@ static int readShort( FILE *file, unsigned short *value )
static int writeLong( FILE *file, unsigned int value )
{
char buffer[4] = { 0 };
+#ifdef IVAS_RTPDUMP
+ buffer[3] = (char) ( value & 0xff );
+ buffer[2] = (char) ( ( value >> 8 ) & 0xff );
+ buffer[1] = (char) ( ( value >> 16 ) & 0xff );
+ buffer[0] = (char) ( ( value >> 24 ) & 0xff );
+#else
buffer[3] = value & 0xff;
buffer[2] = ( value >> 8 ) & 0xff;
buffer[1] = ( value >> 16 ) & 0xff;
buffer[0] = ( value >> 24 ) & 0xff;
+#endif
if ( fwrite( buffer, 4, 1, file ) != 1U )
{
return -1;
@@ -125,8 +132,13 @@ static int writeLong( FILE *file, unsigned int value )
static int writeShort( FILE *file, unsigned short value )
{
char buffer[2] = { 0 };
+#ifdef IVAS_RTPDUMP
+ buffer[1] = (char) ( value & 0xff );
+ buffer[0] = (char) ( ( value >> 8 ) & 0xff );
+#else
buffer[1] = value & 0xff;
buffer[0] = ( value >> 8 ) & 0xff;
+#endif
if ( fwrite( buffer, 2, 1, file ) != 1U )
{
return -1;
@@ -264,6 +276,34 @@ RTPDUMP_OpenForWriting( RTPDUMP_HANDLE *phRTPDUMP, const char *filename )
return RTPDUMP_NO_ERROR;
}
+
+#ifdef IVAS_RTPDUMP
+RTPDUMP_ERROR
+RTPDUMP_OpenWithFileToWrite( RTPDUMP_HANDLE *phRTPDUMP, FILE *file )
+{
+ *phRTPDUMP = (RTPDUMP_HANDLE) calloc( 1, sizeof( struct RTPDUMP ) );
+ if ( !phRTPDUMP || !( *phRTPDUMP ) )
+ {
+ return RTPDUMP_MEMORY_ERROR;
+ }
+
+ /* open file stream */
+ ( *phRTPDUMP )->file = file;
+ if ( ( *phRTPDUMP )->file == NULL )
+ {
+ return RTPDUMP_FILE_NOT_FOUND;
+ }
+
+ if ( writeHeader( *phRTPDUMP ) != 0 )
+ {
+ return RTPDUMP_INIT_ERROR;
+ }
+
+ return RTPDUMP_NO_ERROR;
+}
+
+
+#endif
RTPDUMP_ERROR
RTPDUMP_ReadPacket( RTPDUMP_HANDLE hRTPDUMP,
RTPDUMP_RTPPACKET *packet,
diff --git a/lib_util/rtpdump.h b/lib_util/rtpdump.h
index 5b8b31e8de3b7a6eda80cd7e6d8e808c6e9446b5..77e284eb84999d1f0f9d5209dd5260c30ba6a742 100644
--- a/lib_util/rtpdump.h
+++ b/lib_util/rtpdump.h
@@ -37,6 +37,7 @@
#pragma once
#include
#include
+#include "options.h"
#ifdef __cplusplus
extern "C"
@@ -83,6 +84,11 @@ extern "C"
RTPDUMP_ERROR
RTPDUMP_OpenForWriting( RTPDUMP_HANDLE *phRTPDUMP, const char *filename );
+#ifdef IVAS_RTPDUMP
+ RTPDUMP_ERROR
+ RTPDUMP_OpenWithFileToWrite( RTPDUMP_HANDLE *phRTPDUMP, FILE *file );
+
+#endif
RTPDUMP_ERROR
RTPDUMP_ReadPacket( RTPDUMP_HANDLE hRTPDUMP,
RTPDUMP_RTPPACKET *packet,
diff --git a/readme.txt b/readme.txt
index 8e471c429bb5a677a6671b17057ac8782e673a4f..9bd1c4d6fb5861f67c10f24844673e50a4650977 100644
--- a/readme.txt
+++ b/readme.txt
@@ -249,6 +249,12 @@ EVS mono is default, for IVAS choose one of the following: -stereo, -ism, -sba,
-level level : Complexity level, level = (1, 2, 3), will be defined after characterisation.
Currently, all values default to level 3 (full functionality).
-q : Quiet mode, limit printouts to terminal, default is deactivated
+-rtpdump : RTPDump output, hf_only=1 by default. The encoder will packetize the
+ bitstream frames into TS26.253 Annex A IVAS RTP Payload Format packets and
+ writes those to the output file. In EVS mono operating mode, TS26.445 Annex A.2.2
+ EVS RTP Payload Format is used.
+-scene_orientation : Scene orientation trajectory file. Only used with rtpdump output.
+-device_orientation : Device orientation trajectory file. Only used with rtpdump output.
The usage of the "IVAS_dec" program is as follows:
@@ -274,9 +280,10 @@ Options:
--------
-VOIP : VoIP mode: RTP in G192
-VOIP_hf_only=0 : VoIP mode: EVS RTP Payload Format hf_only=0 in rtpdump
--VOIP_hf_only=1 : VoIP mode: EVS RTP Payload Format hf_only=1 in rtpdump
+-VOIP_hf_only=1 : VoIP mode: EVS or IVAS RTP Payload Format hf_only=1 in rtpdump
The decoder may read rtpdump files containing TS26.445 Annex A.2.2
- EVS RTP Payload Format. The SDP parameter hf_only is required.
+ EVS RTP Payload Format or rtpdump files containing TS26.253 Annex A
+ IVAS RTP Payload Format. The SDP parameter hf_only is required.
Reading RFC4867 AMR/AMR-WB RTP payload format is not supported.
-Tracefile TF : VoIP mode: Generate trace file named TF. Requires -no_delay_cmp to
be enabled so that trace contents remain in sync with audio output.
diff --git a/scripts/config/self_test.prm b/scripts/config/self_test.prm
index 28254ed77c2bd2dddb7b0857cf728d187f83ba3a..7bfe33e4deb5c541eccad48046557a570bb8d59c 100644
--- a/scripts/config/self_test.prm
+++ b/scripts/config/self_test.prm
@@ -306,6 +306,9 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1
../IVAS_cod -dtx -stereo ../scripts/switchPaths/sw_13k2_to_128k_10fr.bin 48 testv/stvST48c.wav bit
../IVAS_dec EXT 48 bit testv/stvST48c.wav_stereo_sw_48-48_DTX_EXT.tst
+// stereo bitrate switching from 13.2 kbps to 128 kbps, 48kHz in, 48kHz out, DTX on, EXT out, rtpdump
+../IVAS_cod -rtpdump 3 -dtx -stereo ../scripts/switchPaths/sw_13k2_to_128k_10fr.bin 48 testv/stvST48c.wav bit
+../IVAS_dec -VOIP_hf_only=1 EXT 48 bit testv/stvST48c.wav_stereo_sw_48-48_DTX_EXT_rtpdump.tst
// 1 ISM with metadata at 13.2 kbps, 48 kHz in, 48 kHz out, EXT out
@@ -580,6 +583,9 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1
../IVAS_cod -ism +4 testv/stvISM1.csv testv/stvISM2.csv testv/stvISM3.csv testv/stvISM4.csv 128000 48 testv/stv4ISM48n.wav bit
../IVAS_dec BINAURAL_ROOM_REVERB 48 bit testv/stv4ISM48n.wav_BINAURAL_ROOM_REVERB_128000_48-48.tst
+// 4 ISM with metadata bitrate switching from 24.4 kbps to 512 kbps, 48 kHz in, 48 kHz out, DTX on, BINAURAL out, rtpdump, PI data
+../IVAS_cod -rtpdump 3 -scene_orientation testv/headrot.csv -device_orientation testv/headrot_case00_3000_q.csv -dtx -ism 4 testv/stvISM1.csv testv/stvISM2.csv testv/stvISM3.csv testv/stvISM4.csv ../scripts/switchPaths/sw_24k4_512k.bin 48 testv/stv4ISM48s.wav bit
+../IVAS_dec -VOIP_hf_only=1 BINAURAL 48 bit testv/stv4ISM48s.wav_brate_sw_48-48_DTX_BINAURAL_rtpdump_PIdata.tst
// SBA at 13.2 kbps, 32kHz in, 32kHz out, HOA3 out, bandwidth switching
@@ -958,6 +964,9 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1
../IVAS_cod -sba 3 96000 48 testv/stv3OA48c.wav bit
../IVAS_dec -render_config testv/rend_config_recreation.cfg BINAURAL_ROOM_REVERB 48 bit testv/stv3OA48c.wav_BINAURAL_ROOM_REVERB_96000_48-48_custom_configuration.tst
+// SBA FOA bitrate switching from 13.2 kbps to 512 kbps, 48kHz in, 48kHz out, DTX on, BINAURAL out, rtpdump, PI data
+../IVAS_cod -rtpdump 3 -scene_orientation testv/headrot_case01_3000_q.csv -device_orientation testv/headrot_case02_3000_q.csv -dtx -sba 1 -max_band fb ../scripts/switchPaths/sw_13k2_512k.bin 48 testv/stvFOA48c.wav bit
+../IVAS_dec -VOIP_hf_only=1 FOA 48 bit testv/stvFOA48c.wav_sw_48-48_DTX_BINAURAL_rtpdump_PIdata.tst
// MASA 1dir 1TC at 13.2 kbps, 48kHz in, 48kHz out, BINAURAL out, bandwidth switching
@@ -1238,6 +1247,9 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1
../IVAS_cod -masa 1 testv/stv1MASA1TC48c.met 256000 48 testv/stv1MASA1TC48c.wav bit
../IVAS_dec -render_config testv/rend_config_combined.cfg -t testv/headrot.csv BINAURAL_ROOM_REVERB 48 bit testv/stv1MASA1TC48c.wav_BINAURAL_ROOM_REVERB_256000_48-48_Headrot_custom_config.tst
+// MASA 2dir 2TC bitrate switching from 13.2 kbps to 512 kbps, 48kHz in, 48kHz out, DTX on, BINAURAL out, rtpdump, PI data
+../IVAS_cod -rtpdump 3 -scene_orientation testv/headrot_case03_3000_q.csv -device_orientation testv/headrot.csv -dtx -masa 2 testv/stv2MASA2TC48c.met ../scripts/switchPaths/sw_13k2_512k.bin 48 testv/stv2MASA2TC48c.wav bit
+../IVAS_dec -VOIP_hf_only=1 BINAURAL 48 bit testv/stv2MASA2TC48c.wav_sw_48-48_DTX_BINAURAL_rtpdump_PIdata.tst
// Multi-channel 5_1 at 13.2 kbps, 48kHz in, 48kHz out
@@ -1561,6 +1573,9 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1
../IVAS_cod -mc 7_1_4 ../scripts/switchPaths/sw_mctech_5fr.bin 48 testv/stv714MC48c.wav bit
../IVAS_dec EXT 48 bit testv/stv714MC48c.wav_sw_48-48_EXT.tst
+// Multi-channel 5_1 bitrate switching from 13.2 kbps to 512 kbps, 48kHz in, 48kHz out, BINAURAL out, rtpdump, PI data
+../IVAS_cod -rtpdump 3 -scene_orientation testv/headrot.csv -device_orientation testv/headrot_case02_3000_q.csv -mc 5_1 ../scripts/switchPaths/sw_mctech_5fr.bin 48 testv/stv51MC48c.wav bit
+../IVAS_dec -VOIP_hf_only=1 BINAURAL 48 bit testv/stv51MC48c.wav_sw_48-48_BINAURAL_rtpdump_PIdata.tst
// Stereo downmix to bit-exact EVS at 13200 kbps, 32kHz in, 32kHz out
@@ -1571,6 +1586,9 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1
../IVAS_cod -stereo_dmx_evs 24400 48 testv/stvST48c.wav bit
../IVAS_dec 48 bit testv/stvST48c.wav_StereoDmxEVS_24400_48-48.tst
+// Stereo downmix to bit-exact EVS at 24400 kbps, 48kHz in, 48kHz out, rtpdump
+../IVAS_cod -rtpdump 3 -stereo_dmx_evs 24400 48 testv/stvST48c.wav bit
+../IVAS_dec -VOIP_hf_only=1 48 bit testv/stvST48c.wav_StereoDmxEVS_24400_48-48_rtpdump.tst
// EVS non-diegetic panning at 64 kbps, 48kHz in, 48kHz out, STEREO out
@@ -1581,6 +1599,9 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1
../IVAS_cod -ism 1 testv/stvISM1.csv 32000 48 testv/stv1ISM48s.wav bit
../IVAS_dec -non_diegetic_pan 80 STEREO 48 bit testv/stv1ISM48s.pcm_ISM_32000_48-48_STEREO_NON-DIEGETIC-PAN_80.tst
+// EVS at 13.2 kbps, 48kHz in, 48kHz out, STEREO out, rtpdump
+../IVAS_cod -rtpdump 3 13200 48 testv/stv48c.wav bit
+../IVAS_dec -VOIP_hf_only=1 48 bit testv/stv48c.pcm_EVS_13200_48-48_STEREO_rtpdump.tst
// stereo at 32 kbps, 48 kHz in, 32 kHz out, DTX on, JBM Prof 0
@@ -1858,6 +1879,9 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1
../IVAS_cod -ism_masa 4 2 testv/stvISM1.csv NULL testv/stvISM3.csv testv/stvISM4.csv testv/stv2MASA2TC48c.met ../scripts/switchPaths/sw_13k2_512k_2fr_start_80k_omasatechs_4ism.bin 48 testv/stvOMASA_4ISM_2MASA2TC48c.wav bit
../IVAS_dec EXT 48 bit testv/stvOMASA_4ISM_2MASA2TC48c.wav_EXT_sw_48-48.tst
+// OMASA 2Dir2TC 3ISM at br sw techs 13.2 to 512 kbps start 160 kbps, 48kHz in, 48kHz out, BINAURAL out, rtpdump, PI data
+../IVAS_cod -rtpdump 3 -scene_orientation testv/headrot_case01_3000_q.csv -device_orientation testv/headrot_case00_3000_q.csv -ism_masa 3 2 testv/stvISM1.csv testv/stvISM2.csv testv/stvISM3.csv testv/stv2MASA2TC48c.met ../scripts/switchPaths/sw_13k2_512k_2fr_start_160k_omasatechs_3ism.bin 48 testv/stvOMASA_3ISM_2MASA2TC48c.wav bit
+../IVAS_dec -VOIP_hf_only=1 BINAURAL 48 bit testv/stvOMASA_3ISM_2MASA2TC48c.wav_BINAURAL_sw_48-48_rtpdump_PIdata.tst
// OSBA FOA 1ISM at 32 kbps, 48kHz in, 48kHz out, BINAURAL out
@@ -1999,6 +2023,10 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_10pct.g
networkSimulator_g192 ../scripts/dly_error_profiles/dly_error_profile_5.dat bit netsimoutput tracefile_sim 2 0
../IVAS_dec -no_delay_cmp -Tracefile tracefile_dec -VOIP BINAURAL_ROOM_REVERB 48 netsimoutput testv/stvOSBA_2ISM_2OA32c.wav_BINAURAL_brsw_32-48_JBM5.tst
+// OSBA 2ISM 2OA at bitrate switching 13.2 to 512 kbps, 48kHz in, 48kHz out, BINAURAL out, rtpdump, PI data
+../IVAS_cod -rtpdump 3 -scene_orientation testv/headrot_case00_3000_q.csv -device_orientation testv/headrot_case03_3000_q.csv -ism_sba 2 2 testv/stvISM1.csv testv/stvISM2.csv ../scripts/switchPaths/sw_13k2_512k.bin 48 testv/stvOSBA_2ISM_2OA48c.wav bit
+../IVAS_dec -VOIP_hf_only=1 BINAURAL 48 bit testv/stvOSBA_2ISM_2OA48c.wav_BINAURAL_sw_48-48_rtpdump_PIdata.tst
+
// OMASA 2Dir2TC 4ISM at 80 kbps, 48kHz in, 48kHz out, BINAURAL out, default object editing, 1SEP-PARAM
../IVAS_cod -ism_masa 4 2 testv/stvISM1.csv testv/stvISM2.csv testv/stvISM3.csv testv/stvISM4.csv testv/stv2MASA2TC48c.met 80000 48 testv/stvOMASA_4ISM_2MASA2TC48c.wav bit
diff --git a/tests/requirements.txt b/tests/requirements.txt
index 2eb090f4fb56da7587e0a3eb19b7e287870361c1..a8d8f7b06447230f83eb69871de39e19f0f78a40 100644
--- a/tests/requirements.txt
+++ b/tests/requirements.txt
@@ -2,3 +2,5 @@ pytest>=5.3.5
pytest-xdist>=1.31.0
scipy>=1.5.2
numpy>=1.19.2
+bitstring>=4.3.1
+soundfile>=0.13
diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py
new file mode 100644
index 0000000000000000000000000000000000000000..f6923c007753f1cc3eee9ee84064fd4d3e7aa1b6
--- /dev/null
+++ b/tests/rtp/ivasrtp.py
@@ -0,0 +1,1157 @@
+#!/usr/bin/env python3
+
+__copyright__ = """
+(C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
+Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+contributors to this repository. All Rights Reserved.
+
+This software is protected by copyright law and by international treaties.
+The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
+Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+contributors to this repository retain full ownership rights in their respective contributions in
+the software. This notice grants no license of any kind, including but not limited to patent
+license, nor is any license granted by implication, estoppel or otherwise.
+
+Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
+contributions.
+
+This software is provided "AS IS", without any express or implied warranties. The software is in the
+development stage. It is intended exclusively for experts who have experience with such software and
+solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
+and fitness for a particular purpose are hereby disclaimed and excluded.
+
+Any dispute, controversy or claim arising under or in relation to providing this software shall be
+submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
+accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
+the United Nations Convention on Contracts on the International Sales of Goods.
+"""
+
+__doc__ = """
+To configure test modules.
+"""
+
+import struct
+from enum import Enum
+from dataclasses import dataclass, field, asdict
+from bitstring import ConstBitStream, BitStream, ReadError
+import json
+import base64
+import argparse
+from pathlib import Path
+from typing import cast
+
+NO_REQ="NO_REQ"
+
+class CAMODE(str, Enum):
+ CA_LO_02 = "CA-LO-O2"
+ CA_LO_03 = "CA-LO-O3"
+ CA_LO_05 = "CA-LO-O5"
+ CA_LO_07 = "CA-LO-O7"
+ CA_HI_02 = "CA-HI-O2"
+ CA_HI_03 = "CA-HI-O3"
+ CA_HI_05 = "CA-HI-O5"
+ CA_HI_07 = "CA-HI-O7"
+
+class CODECS(str, Enum):
+ AMRWB = "amrwb_io"
+ EVS = "evs"
+ IVAS = "ivas"
+
+class SRCODEC(str, Enum):
+ LCLD = "lcld"
+ LC3PLUS = "lc3+"
+
+class BANDWIDTH(str, Enum):
+ NB = "narrowband"
+ WB = "wideband"
+ SWB = "super wideband"
+ FB = "fullband"
+
+class REQUESTS(str, Enum):
+ CODEC = "codec",
+ BR = "bitrate"
+ BW = "bandwidth"
+ CA = "ca-mode"
+ FMT = "format"
+ SUBFMT = "sub-format"
+ SRCFG = "sr-config"
+
+class FORMATS(str, Enum):
+ STEREO="Stereo"
+ SBA="SBA"
+ MASA="MASA"
+ ISM="ISM"
+ MC="MC"
+ OMASA="OMASA"
+ OSBA="OSBA"
+
+class SUBFORMATS(str, Enum):
+ FOA_PLANAR = "FOA planar"
+ HOA2_PLANAR = "HOA2 planar"
+ HOA3_PLANAR = "HOA3 planar"
+ FOA = "FOA"
+ HOA2 = "HOA2"
+ HOA3 = "HOA3"
+ MASA1 = "MASA1"
+ MASA2 = "MASA2"
+ ISM1 = "ISM1"
+ ISM2 = "ISM2"
+ ISM3 = "ISM3"
+ ISM4 = "ISM4"
+ ISM1_EXTENDED_METADATA = "ISM1 extended metadata"
+ ISM2_EXTENDED_METADATA = "ISM2 extended metadata"
+ ISM3_EXTENDED_METADATA = "ISM3 extended metadata"
+ ISM4_EXTENDED_METADATA = "ISM4 extended metadata"
+ MC_5_1 = "MC 5.1"
+ MC_7_1 = "MC 7.1"
+ MC_5_1_2 = "MC 5.1.2"
+ MC_5_1_4 = "MC 5.1.4"
+ MC_7_1_4 = "MC 7.1.4"
+ Reserved22 = "Reserved22"
+ Reserved23 = "Reserved23"
+ Reserved24 = "Reserved24"
+ Reserved25 = "Reserved25"
+ Reserved26 = "Reserved26"
+ Reserved27 = "Reserved27"
+ Reserved28 = "Reserved28"
+ Reserved29 = "Reserved29"
+ Reserved30 = "Reserved30"
+ Reserved31 = "Reserved31"
+ Reserved32 = "Reserved32"
+ OMASA_ISM1_1TC = "OMASA ISM1 1TC"
+ OMASA_ISM2_1TC = "OMASA ISM2 1TC"
+ OMASA_ISM3_1TC = "OMASA ISM3 1TC"
+ OMASA_ISM4_1TC = "OMASA ISM4 1TC"
+ OMASA_ISM1_2TC = "OMASA ISM1 2TC"
+ OMASA_ISM2_2TC = "OMASA ISM2 2TC"
+ OMASA_ISM3_2TC = "OMASA ISM3 2TC"
+ OMASA_ISM4_2TC = "OMASA ISM4 2TC"
+ OSBA_ISM1_FOA_PLANAR = "OSBA ISM1 FOA planar"
+ OSBA_ISM2_FOA_PLANAR = "OSBA ISM2 FOA planar"
+ OSBA_ISM3_FOA_PLANAR = "OSBA ISM3 FOA planar"
+ OSBA_ISM4_FOA_PLANAR = "OSBA ISM4 FOA planar"
+ OSBA_ISM1_FOA = "OSBA ISM1 FOA"
+ OSBA_ISM2_FOA = "OSBA ISM2 FOA"
+ OSBA_ISM3_FOA = "OSBA ISM3 FOA"
+ OSBA_ISM4_FOA = "OSBA ISM4 FOA"
+ OSBA_ISM1_HOA2_PLANAR = "OSBA ISM1 HOA2 planar"
+ OSBA_ISM2_HOA2_PLANAR = "OSBA ISM2 HOA2 planar"
+ OSBA_ISM3_HOA2_PLANAR = "OSBA ISM3 HOA2 planar"
+ OSBA_ISM4_HOA2_PLANAR = "OSBA ISM4 HOA2 planar"
+ OSBA_ISM1_HOA2 = "OSBA ISM1 HOA2"
+ OSBA_ISM2_HOA2 = "OSBA ISM2 HOA2"
+ OSBA_ISM3_HOA2 = "OSBA ISM3 HOA2"
+ OSBA_ISM4_HOA2 = "OSBA ISM4 HOA2"
+ OSBA_ISM1_HOA3_PLANAR = "OSBA ISM1 HOA3 planar"
+ OSBA_ISM2_HOA3_PLANAR = "OSBA ISM2 HOA3 planar"
+ OSBA_ISM3_HOA3_PLANAR = "OSBA ISM3 HOA3 planar"
+ OSBA_ISM4_HOA3_PLANAR = "OSBA ISM4 HOA3 planar"
+ OSBA_ISM1_HOA3 = "OSBA ISM1 HOA3"
+ OSBA_ISM2_HOA3 = "OSBA ISM2 HOA3"
+ OSBA_ISM3_HOA3 = "OSBA ISM3 HOA3"
+ OSBA_ISM4_HOA3 = "OSBA ISM4 HOA3"
+
+class PIDATAS(str, Enum):
+ SCENE_ORIENTATION = "SCENE_ORIENTATION"
+ DEVICE_ORIENTATION_COMPENSATED = "DEVICE_ORIENTATION_COMPENSATED"
+ DEVICE_ORIENTATION_UNCOMPENSATED = "DEVICE_ORIENTATION_UNCOMPENSATED"
+ ACOUSTIC_ENVIRONMENT = "ACOUSTIC_ENVIRONMENT"
+ AUDIO_DESCRIPTION = "AUDIO_DESCRIPTION"
+ ISM_NUM = "ISM_NUM"
+ ISM_ID = "ISM_ID"
+ ISM_GAIN = "ISM_GAIN"
+ ISM_ORIENTATION = "ISM_ORIENTATION"
+ ISM_POSITION = "ISM_POSITION"
+ ISM_DISTANCE_ATTENUATION = "ISM_DISTANCE_ATTENUATION"
+ ISM_DIRECTIVITY = "ISM_DIRECTIVITY"
+ DIEGETIC_TYPE = "DIEGETIC_TYPE"
+ RESERVED13 = "RESERVED13"
+ RESERVED14 = "RESERVED14"
+ RESERVED15 = "RESERVED15"
+ PLAYBACK_DEVICE_ORIENTATION = "PLAYBACK_DEVICE_ORIENTATION"
+ HEAD_ORIENTATION = "HEAD_ORIENTATION"
+ LISTENER_POSITION = "LISTENER_POSITION"
+ DYNAMIC_AUDIO_SUPPRESSION = "DYNAMIC_AUDIO_SUPPRESSION"
+ AUDIO_FOCUS_DIRECTION = "AUDIO_FOCUS_DIRECTION"
+ PI_LATENCY = "PI_LATENCY"
+ R_ISM_ID = "R_ISM_ID"
+ R_ISM_GAIN = "R_ISM_GAIN"
+ R_ISM_ORIENTATION = "R_ISM_ORIENTATION"
+ R_ISM_POSITION = "R_ISM_POSITION"
+ R_ISM_DIRECTION = "R_ISM_DIRECTION"
+ RESERVED27 = "RESERVED27"
+ RESERVED28 = "RESERVED28"
+ RESERVED29 = "RESERVED29"
+ RESERVED30 = "RESERVED30"
+ NO_PI_DATA = "NO_PI_DATA"
+
+class SUPPRESSION_LEVEL(int, Enum):
+ SUPPRESSION_LEVEL_NONE = 0
+ SUPPRESSION_LEVEL_1 = 1
+ SUPPRESSION_LEVEL_2 = 2
+ SUPPRESSION_LEVEL_3 = 3
+ SUPPRESSION_LEVEL_4 = 4
+ SUPPRESSION_LEVEL_5 = 5
+ SUPPRESSION_LEVEL_6 = 6
+ SUPPRESSION_LEVEL_7 = 7
+ SUPPRESSION_LEVEL_8 = 8
+ SUPPRESSION_LEVEL_9 = 9
+ SUPPRESSION_LEVEL_10 = 10
+ SUPPRESSION_LEVEL_11 = 11
+ SUPPRESSION_LEVEL_12 = 12
+ SUPPRESSION_LEVEL_13 = 13
+ SUPPRESSION_LEVEL_14 = 14
+ SUPPRESSION_LEVEL_MAX = 15
+
+
+@dataclass
+class RTPHDR:
+ version: int = 2
+ padding: bool = False
+ extension: bool = False
+ csrcCount: int = 0
+ marker: bool = False
+ payloadType: int = 0
+ sequenceNum: int = 0
+ timestamp: int = 0
+ ssrc: int = 0
+ extensionType: int = 0
+ extensionLength: int = 0
+ csrcList: list = field(default_factory=list)
+ extensionWords: list = field(default_factory=list)
+
+ def updateHeader(self, numFrames: int):
+ self.sequenceNum = (self.sequenceNum + 1) % 65536
+ self.timestamp += 320 * numFrames
+
+ def pack(self, bitstrm: BitStream):
+ bitstrm.append(f'uint:2={self.version}')
+ bitstrm.append(f'bool={self.padding}')
+ bitstrm.append(f'bool={self.extension}')
+ bitstrm.append(f'uint:4={self.csrcCount}')
+ bitstrm.append(f'bool={self.marker}')
+ bitstrm.append(f'uint:7={self.payloadType}')
+ bitstrm.append(f'uintbe:16={self.sequenceNum}')
+ bitstrm.append(f'uintbe:32={self.timestamp}')
+ bitstrm.append(f'uintbe:32={self.ssrc}')
+ assert len(self.csrcList) == self.csrcCount, "csrcList must be of length csrcCount"
+ for csrc in self.csrcList:
+ bitstrm.append(f'uintbe:32={csrc}')
+ if self.extension:
+ bitstrm.append(f'uintbe:16={self.extensionType}')
+ bitstrm.append(f'uintbe:16={self.extensionLength}')
+ assert len(self.extensionWords) == self.extensionLength, "extensionWords must be of extensionLength csrcCount"
+ for ext in self.extensionWords:
+ bitstrm.append(f'uintbe:32={ext}')
+
+ @classmethod
+ def unpack(cls, bitstrm: ConstBitStream):
+ hdr = cls()
+ hdr.version = bitstrm.read(2).uint
+ hdr.padding = bitstrm.read(1).bool
+ hdr.extension = bitstrm.read(1).bool
+ hdr.csrcCount = bitstrm.read(4).int
+ hdr.marker = bitstrm.read(1).bool
+ hdr.payloadType = bitstrm.read(7).int
+ hdr.sequenceNum = bitstrm.read(16).uintbe
+ hdr.timestamp = bitstrm.read(32).uintbe
+ hdr.ssrc = bitstrm.read(32).uintbe
+ if hdr.csrcCount:
+ hdr.csrcList = [ bitstrm.read(32).uintbe for _ in range(hdr.csrcCount) ]
+ if hdr.extension:
+ hdr.extensionType = bitstrm.read(16).uintbe
+ hdr.extensionLength = bitstrm.read(16).uintbe
+ hdr.extensionWords = [ bitstrm.read(32).uintbe for _ in range(hdr.extensionLength) ]
+ return hdr
+
+@dataclass
+class SRCONFIG:
+ diegetic: bool = False
+ yaw: bool = False
+ pitch: bool = False
+ roll: bool = False
+
+@dataclass
+class CMR:
+ bandwidth: BANDWIDTH
+ codec: CODECS = CODECS.IVAS
+ startIdx: int = 0
+ endIdx: int = 0
+ bitrates: list = field(default_factory=list)
+
+@dataclass
+class SRINFO:
+ bitrate: int = 0
+ diegetic: bool = False
+ transportCodec: SRCODEC = SRCODEC.LCLD
+
+@dataclass
+class FRAME:
+ codec: CODECS = CODECS.IVAS
+ frmSizeBits: int = 0
+ bitrate: int = 0
+ speechLost: bool = False
+ srInfo: SRINFO = None
+ timestamp: int = 0
+ au: bytes = field(default_factory=bytes)
+
+#PI DATA STRUCTURES
+@dataclass
+class ORIENTATION:
+ w: float = 0.0
+ x: float = 0.0
+ y: float = 0.0
+ z: float = 0.0
+
+@dataclass
+class POSITION:
+ x: float = 0.0
+ y: float = 0.0
+ z: float = 0.0
+
+class ISM_POSITIONS:
+ positions: list[POSITION]
+
+@dataclass
+class AUDIO_DESCRIPTION:
+ isSpeech: bool = False
+ isMusic: bool = False
+ isAmbiance: bool = False
+ isEditable: bool = False
+ isBinaural: bool = False
+
+@dataclass
+class DYNAMIC_AUDIO_SUPPRESSION:
+ preferSpeech: bool = False
+ preferMusic: bool = False
+ preferAmbiance: bool = False
+ level: SUPPRESSION_LEVEL = SUPPRESSION_LEVEL.SUPPRESSION_LEVEL_MAX
+
+@dataclass
+class DIEGETIC_TYPE:
+ isDigetic: list[bool]
+
+@dataclass
+class ACOUSTIC_ENVIRONMENT:
+ aeid: int = 0
+ rt60: tuple[float, float, float] = ()
+ dsr: tuple[float, float, float] = ()
+ dim: tuple[float, float, float] = ()
+ abscoeff: tuple[float, float, float, float, float, float] = ()
+
+@dataclass
+class PIDATA:
+ timestamp: int = 0
+ type: str = "NO_PI_DATA"
+ data: any = None
+
+MAX_PACKED_PI_SIZE = 32
+ivasBitrates = [13200, 16400, 24400, 32000, 48000, 64000, 80000, 96000, 128000, 160000, 192000, 256000, 384000, 512000, -1, 5200]
+evsBitrates = [5900, 7200, 8000, 9600, 13200, 16400, 24400, 32000, 48000, 64000, 96000, 128000, 2400, -1, -1, -1]
+amrwbBitrates = [6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850, 1750, -1, -1, -1, -1, -1, -1]
+requestBitratesForCodec = {CODECS.AMRWB: amrwbBitrates[0:9], CODECS.EVS: evsBitrates[0:12], CODECS.IVAS: ivasBitrates[0:14]}
+rt60Value = [0.01, 0.0126, 0.0159, 0.02, 0.0252, 0.0317, 0.04, 0.0504, 0.0635, 0.08, 0.1008, 0.1270, 0.16, 0.2016, 0.2540, 0.32, 0.4032,
+ 0.5080, 0.64, 0.8063, 1.0159, 1.28, 1.6127, 2.0319, 2.56, 3.2254, 4.0637, 5.12, 6.4508, 8.1275, 10.24, 12.9016 ]
+dsrValue = [ -20.0, -21.0, -22.0, -23.0, -24.0, -25.0, -26.0, -27.0, -28.0, -29.0, -30.0, -31.0, -32.0, -33.0, -34.0, -35.0, -36.0, -37.0,
+ -38.0, -39.0, -40.0, -41.0, -42.0, -43.0, -44.0, -45.0, -46.0, -47.0, -48.0, -49.0, -50.0, -51.0, -52.0, -53.0, -54.0, -55.0,
+ -56.0, -57.0, -58.0, -59.0, -60.0, -61.0, -62.0, -63.0, -64.0, -65.0, -66.0, -67.0, -68.0, -69.0, -70.0, -71.0, -72.0, -73.0,
+ -74.0, -75.0, -76.0, -77.0, -78.0, -79.0, -80.0, -81.0, -82.0, -83.0 ]
+roomDimensionValue = [0.5, 0.707, 1.0, 1.4141, 2.0, 2.8282, 4.0, 5.6568, 8.0, 11.314, 16.0, 22.627, 32.0, 45.255, 64.0, 90.51]
+absorptionCoeffValues = [0.0800, 0.1656, 0.3430, 0.7101]
+codedFormats = list(FORMATS)
+codedSubFormats = list(SUBFORMATS)
+PiTypeNames = list(PIDATAS)
+
+def mapNearestIndex(table: list, val: float) -> int:
+ for idx, entry in enumerate(table):
+ if abs(entry) >= abs(val):
+ return idx
+ return len(table) - 1
+
+getListIndex = lambda mylist, val: mylist.index(val) if val in mylist else -1
+
+cmrLookup = [
+ CMR(bandwidth=BANDWIDTH.NB, codec=CODECS.EVS, startIdx=0, endIdx=7, bitrates = evsBitrates), #000 = NB-EVS
+ CMR(bandwidth=BANDWIDTH.WB, codec=CODECS.AMRWB, startIdx=0, endIdx=9, bitrates = amrwbBitrates), #001 = AMRWB IO
+ CMR(bandwidth=BANDWIDTH.WB, codec=CODECS.EVS, startIdx=0, endIdx=12, bitrates = evsBitrates), #010 = WB-EVS
+ CMR(bandwidth=BANDWIDTH.SWB, codec=CODECS.EVS, startIdx=3, endIdx=12, bitrates = evsBitrates), #011 = SWB-EVS
+ CMR(bandwidth=BANDWIDTH.FB, codec=CODECS.EVS, startIdx=5, endIdx=12, bitrates = evsBitrates), #100 = FB-EVS
+ CMR(bandwidth=BANDWIDTH.WB, codec=CODECS.EVS, startIdx=0, endIdx=0, bitrates = []), #101 = WB-CA
+ CMR(bandwidth=BANDWIDTH.SWB, codec=CODECS.EVS, startIdx=0, endIdx=0, bitrates = []), #110 = SWB-CA
+ CMR(bandwidth=NO_REQ, codec=CODECS.IVAS, startIdx=0, endIdx=14, bitrates = ivasBitrates), #111 = IVAS
+]
+
+q15 = lambda x : int(min(32767.0, max(-32768.0, x * 32768.0)))
+
+def unpackUnsupported(bitstrm: ConstBitStream, piSize: int) -> any:
+ #assert False, "Unsupported PI Data"
+ return base64.b64encode(bitstrm.read(piSize * 8).tobytes()).decode('utf-8')
+
+def packUnsupported(bitstrm: ConstBitStream, data: any) -> any:
+ assert False, f"unsupported PI Data of type : {type(data)}"
+
+def unpackNoPiData(bitstrm: ConstBitStream, piSize: int) -> None:
+ assert piSize == 0, "NO_PI_DATA should be 0 size"
+
+def packNoPiData(bitstrm: BitStream, data: any = None):
+ pass
+
+def unpackOrientations(bitstrm: ConstBitStream, piSize: int) -> list[ORIENTATION]:
+ assert (piSize % 8) == 0 and piSize <= 32, "Incorrect PI Data Size for list[ORIENTATION]"
+ orientations = list()
+ while piSize > 0:
+ w = bitstrm.read(16).int / 32768.0
+ x = bitstrm.read(16).int / 32768.0
+ y = bitstrm.read(16).int / 32768.0
+ z = bitstrm.read(16).int / 32768.0
+ orientations.append(ORIENTATION(w, x, y, z))
+ piSize -= 8
+ return orientations
+
+def packOrientations(bitstrm: BitStream, data: any):
+ assert type(data) == list, "Orientation PI Data expects a data of type list"
+ for orientation in cast(list, data):
+ assert type(orientation) == ORIENTATION, "Orientation PI Data expects a data of type list[ORIENTATION]"
+ bitstrm.append(f'intbe:16={q15(orientation.w)}')
+ bitstrm.append(f'intbe:16={q15(orientation.x)}')
+ bitstrm.append(f'intbe:16={q15(orientation.y)}')
+ bitstrm.append(f'intbe:16={q15(orientation.z)}')
+
+def unpackPositions(bitstrm: ConstBitStream, piSize: int) -> list[POSITION]:
+ assert piSize <= 24 and (piSize % 6) == 0, "Incorrect PI Data Size for Positions"
+ positions = list()
+ while piSize > 0:
+ x = bitstrm.read(16).int / 100.0
+ y = bitstrm.read(16).int / 100.0
+ z = bitstrm.read(16).int / 100.0
+ positions.append(POSITION(x, y, z))
+ piSize -= 6
+ return positions
+
+def packPositions(bitstrm: BitStream, data: any):
+ assert type(data) == list, "Position PI Data expects a data of type list"
+ positions = cast(list, data)
+ assert len(positions) <= 4, "Max one position per ISM object"
+ for position in positions:
+ assert type(position) == POSITION, "Position PI Data expects a data of type list[POSITIONS]"
+ bitstrm.append(f'intbe:16={q15(position.x / 327.68)}')
+ bitstrm.append(f'intbe:16={q15(position.y / 327.68)}')
+ bitstrm.append(f'intbe:16={q15(position.z / 327.68)}')
+
+def unpackOrientation(bitstrm: ConstBitStream, piSize: int) -> ORIENTATION:
+ assert piSize == 8, "Incorrect PI Data Size for ORIENTATION"
+ orientations = unpackOrientations(bitstrm, piSize)
+ assert len(orientations) == 1
+ return orientations[0]
+
+def packOrientation(bitstrm: BitStream, data: any):
+ assert type(data) == ORIENTATION, "Orientation PI Data expects a data of type ORIENTATION"
+ orientation = cast(ORIENTATION, data)
+ packOrientations(bitstrm, [orientation])
+
+def unpackPosition(bitstrm: ConstBitStream, piSize: int) -> POSITION:
+ assert piSize == 6, "Incorrect PI Data Size for POSITION"
+ positions = unpackPositions(bitstrm, piSize)
+ assert len(positions) == 1
+ return positions[0]
+
+def packPosition(bitstrm: BitStream, data: any):
+ assert type(data) == POSITION, "Position PI Data expects a data of type POSITION"
+ position = cast(POSITION, data)
+ packPositions(bitstrm, [position])
+
+def unpackAudioDescription(bitstrm: ConstBitStream, piSize: int) -> list[AUDIO_DESCRIPTION]:
+ assert piSize <= 5, "Incorrect PI Data Size for AUDIO_DESCRIPTION"
+ ad = list()
+ for byte in range(piSize):
+ V = bitstrm.read(1).bool
+ M = bitstrm.read(1).bool
+ A = bitstrm.read(1).bool
+ E = bitstrm.read(1).bool
+ B = bitstrm.read(1).bool
+ _ = bitstrm.read(3)
+ ad.append(AUDIO_DESCRIPTION(isSpeech=V, isMusic=M, isAmbiance=A, isEditable=E, isBinaural=B))
+ return ad
+
+def packAudioDescription(bitstrm: BitStream, data: any):
+ assert type(data) == list, "Audio Description PI Data expects a data of type list[AUDIO_DESCRIPTION]"
+ for desc in cast(list, data):
+ assert type(desc) == AUDIO_DESCRIPTION, "Audio Description PI Data expects a data of type list[AUDIO_DESCRIPTION]"
+ ad = cast(AUDIO_DESCRIPTION, desc)
+ bitstrm.append(f'bool={ad.isSpeech}')
+ bitstrm.append(f'bool={ad.isMusic}')
+ bitstrm.append(f'bool={ad.isAmbiance}')
+ bitstrm.append(f'bool={ad.isEditable}')
+ bitstrm.append(f'bool={ad.isBinaural}')
+ bitstrm.append(f'uint:3=0')
+
+def unpackDAS(bitstrm: ConstBitStream, piSize: int) -> list[AUDIO_DESCRIPTION]:
+ assert piSize == 2, "Incorrect PI Data Size for DYNAMIC_AUDIO_SUPPRESSION"
+ V = bitstrm.read(1).bool
+ M = bitstrm.read(1).bool
+ A = bitstrm.read(1).bool
+ _ = bitstrm.read(5)
+ SLI = bitstrm.read(4).uint
+ _ = bitstrm.read(4)
+ return DYNAMIC_AUDIO_SUPPRESSION(preferSpeech=V, preferMusic=M, preferAmbiance=A, level=SLI)
+
+def packDAS(bitstrm: BitStream, data: any):
+ assert type(data) == DYNAMIC_AUDIO_SUPPRESSION, "Dynamic Audio Suppression PI Data expects a data of type DYNAMIC_AUDIO_SUPPRESSION"
+ das = cast(DYNAMIC_AUDIO_SUPPRESSION, data)
+ bitstrm.append(f'bool={das.preferSpeech}')
+ bitstrm.append(f'bool={das.preferMusic}')
+ bitstrm.append(f'bool={das.preferAmbiance}')
+ bitstrm.append(f'uint:5=0')
+ bitstrm.append(f'uint:4={das.level}')
+ bitstrm.append(f'uint:4=0')
+
+def unpackDiegetic(bitstrm: ConstBitStream, piSize: int) -> DIEGETIC_TYPE:
+ assert piSize == 1, "Incorrect PI Data Size for DIEGETIC_TYPE"
+ digType = list()
+ for _ in range(5): # no way to know how many bits are valid bits, so all 5 read
+ digType.append(bitstrm.read(1).bool)
+ bitstrm.bytealign()
+ return DIEGETIC_TYPE(isDigetic=digType)
+
+def packDiegetic(bitstrm: BitStream, data: any):
+ assert type(data) == DIEGETIC_TYPE, "Diegetic type PI Data expects a data of type DIEGETIC_TYPE"
+ diegetic = cast(DIEGETIC_TYPE, data)
+ assert len(diegetic.isDigetic) <= 5, "Maximum 1 bit per object + 1 bit for SBA/MASA is required (max 5)"
+ for isDigetic in diegetic.isDigetic:
+ bitstrm.append(f'bool={isDigetic}')
+ nPad = 8 - (bitstrm.pos % 8)
+ if nPad > 0:
+ bitstrm.append(f'uint:{nPad}=0')
+
+def unpackAcousticEnv(bitstrm: ConstBitStream, piSize: int) -> DIEGETIC_TYPE:
+ assert piSize == 1 or piSize == 5 or piSize == 8, "Incorrect PI Data Size for ACOUSTIC_ENVIRONMENT"
+ rt60 = list()
+ dsr = list()
+ dim = list()
+ absCoeff = list()
+
+ if piSize == 1:
+ bitstrm.read(1)
+
+ aeid = bitstrm.read(7).uint
+
+ if piSize >= 5:
+ for _ in range(3):
+ rt60.append(rt60Value[bitstrm.read(5).uint])
+ dsr.append(dsrValue[bitstrm.read(6).uint])
+ if piSize == 8:
+ for _ in range(3):
+ dim.append(roomDimensionValue[bitstrm.read(4).uint])
+ for _ in range(6):
+ absCoeff.append(absorptionCoeffValues[bitstrm.read(2).uint])
+
+ return ACOUSTIC_ENVIRONMENT(aeid=aeid, rt60=tuple(rt60), dsr=tuple(dsr), dim=tuple(dim), abscoeff=tuple(absCoeff))
+
+def packAcousticEnv(bitstrm: BitStream, data: any):
+ assert type(data) == ACOUSTIC_ENVIRONMENT, "Diegetic type PI Data expects a data of type ACOUSTIC_ENVIRONMENT"
+ aenv = cast(ACOUSTIC_ENVIRONMENT, data)
+ if not aenv.rt60 and not aenv.dsr:
+ bitstrm.append(f'uint:8={aenv.aeid % 128}')
+ else:
+ assert len(aenv.rt60) == 3 and len(aenv.dsr) == 3, "Lo, Mi, Hi only required for RT60 and DSR values"
+ bitstrm.append(f'uint:7={aenv.aeid % 128}')
+ for n in range(3):
+ rt60 = mapNearestIndex(rt60Value, aenv.rt60[n])
+ dsr = mapNearestIndex(dsrValue, aenv.dsr[n])
+ bitstrm.append(f'uint:5={rt60}')
+ bitstrm.append(f'uint:6={dsr}')
+ if aenv.abscoeff and aenv.dim:
+ assert len(aenv.abscoeff) == 6 and len(aenv.dim) == 3
+ for n in range(3):
+ dim = mapNearestIndex(roomDimensionValue, aenv.dim[n])
+ bitstrm.append(f'uint:4={dim}')
+ for n in range(6):
+ absCoeff = mapNearestIndex(absorptionCoeffValues, aenv.abscoeff[n])
+ bitstrm.append(f'uint:2={absCoeff}')
+
+
+PIDataUnpacker = [
+ unpackOrientation, # SCENE_ORIENTATION,
+ unpackOrientation, # DEVICE_ORIENTATION_COMPENSATED,
+ unpackOrientation, # DEVICE_ORIENTATION_UNCOMPENSATED
+ unpackAcousticEnv, # ACOUSTIC_ENVIRONMENT
+ unpackAudioDescription, # AUDIO_DESCRIPTION
+ unpackUnsupported, # ISM_NUM
+ unpackUnsupported, # ISM_ID
+ unpackUnsupported, # ISM_GAIN
+ unpackOrientations,# ISM_ORIENTATION
+ unpackPositions, # ISM_POSITION
+ unpackUnsupported, # ISM_DISTANCE_ATTENUATION
+ unpackUnsupported, # ISM_DIRECTIVITY
+ unpackDiegetic, # DIEGETIC_TYPE
+ unpackUnsupported, # RESERVED13
+ unpackUnsupported, # RESERVED14
+ unpackUnsupported, # RESERVED15
+ unpackOrientation, # PLAYBACK_DEVICE_ORIENTATION
+ unpackOrientation, # HEAD_ORIENTATION
+ unpackPosition, # LISTENER_POSITION
+ unpackDAS, # DYNAMIC_AUDIO_SUPPRESSION
+ unpackOrientation, # AUDIO_FOCUS_DIRECTION
+ unpackUnsupported, # PI_LATENCY
+ unpackUnsupported, # R_ISM_ID
+ unpackUnsupported, # R_ISM_GAIN
+ unpackOrientation, # R_ISM_ORIENTATION
+ unpackPosition, # R_ISM_POSITION
+ unpackUnsupported, # R_ISM_DIRECTION
+ unpackUnsupported, # RESERVED27
+ unpackUnsupported, # RESERVED28
+ unpackUnsupported, # RESERVED29
+ unpackUnsupported, # RESERVED30
+ unpackNoPiData # NO_DATA
+]
+
+PIDataPacker = [
+ packOrientation, # SCENE_ORIENTATION,
+ packOrientation, # DEVICE_ORIENTATION_COMPENSATED,
+ packOrientation, # DEVICE_ORIENTATION_UNCOMPENSATED
+ packAcousticEnv, # ACOUSTIC_ENVIRONMENT
+ packAudioDescription, # AUDIO_DESCRIPTION
+ packUnsupported, # ISM_NUM
+ packUnsupported, # ISM_ID
+ packUnsupported, # ISM_GAIN
+ packOrientations,# ISM_ORIENTATION
+ packPositions, # ISM_POSITION
+ packUnsupported, # ISM_DISTANCE_ATTENUATION
+ packUnsupported, # ISM_DIRECTIVITY
+ packDiegetic, # DIEGETIC_TYPE
+ packUnsupported, # RESERVED13
+ packUnsupported, # RESERVED14
+ packUnsupported, # RESERVED15
+ packOrientation, # PLAYBACK_DEVICE_ORIENTATION
+ packOrientation, # HEAD_ORIENTATION
+ packPosition, # LISTENER_POSITION
+ packDAS, # DYNAMIC_AUDIO_SUPPRESSION
+ packOrientation, # AUDIO_FOCUS_DIRECTION
+ packUnsupported, # PI_LATENCY
+ packUnsupported, # R_ISM_ID
+ packUnsupported, # R_ISM_GAIN
+ packOrientation, # R_ISM_ORIENTATION
+ packPosition, # R_ISM_POSITION
+ packUnsupported, # R_ISM_DIRECTION
+ packUnsupported, # RESERVED27
+ packUnsupported, # RESERVED28
+ packUnsupported, # RESERVED29
+ packUnsupported, # RESERVED30
+ packNoPiData # NO_DATA
+]
+
+def ReadG192Bitstream(g192File: Path) -> list[bytes]:
+ refPackets = list[bytes]()
+ with open(g192File, "rb") as fd:
+ refBitStrm = ConstBitStream(fd.read())
+ while refBitStrm.pos < refBitStrm.len:
+ sync = hex(refBitStrm.read(16).intle)
+ nBits = refBitStrm.read(16).intle
+ assert sync == "0x6b21", "G192 syncword not found at start of packet"
+ writer = BitStream()
+ for _ in range(nBits):
+ bit = "0b1" if refBitStrm.read(16).uintle == 129 else "0b0"
+ writer.append(bit)
+ refPackets.append(writer.tobytes())
+ return refPackets
+
+def unpackEBytes(bitstrm: ConstBitStream) -> tuple[bool, dict]:
+ piIndicated = False
+ requests = dict()
+ try:
+ if bitstrm.read(1).bool:
+ T = bitstrm.read(3).uint
+ BR = bitstrm.read(4).uint
+ if T in [5, 6]:#CA MODES
+ if BR < 8:
+ requests[REQUESTS.CODEC] = cmrLookup[T].codec
+ requests[REQUESTS.BR] = 13200
+ requests[REQUESTS.CA] = BR
+ requests[REQUESTS.BW] = cmrLookup[T].bandwidth
+ else:
+ raise Exception("Unsupported BR bits in CA Mode")
+ elif T == 7: #IVAS
+ if BR < 14 :
+ requests[REQUESTS.CODEC] = cmrLookup[T].codec
+ requests[REQUESTS.BR] = cmrLookup[T].bitrates[BR]
+ requests[REQUESTS.CA] = -1
+ elif BR == 14 :
+ raise Exception("Reserved BR idx in IVAS EByte")
+ else:
+ if BR >= cmrLookup[T].startIdx and BR < cmrLookup[T].endIdx:
+ requests[REQUESTS.CODEC] = cmrLookup[T].codec
+ requests[REQUESTS.BR] = cmrLookup[T].bitrates[BR]
+ requests[REQUESTS.CA] = -1
+ requests[REQUESTS.BW] = cmrLookup[T].bandwidth
+ else:
+ raise Exception("Reserved BR idx in {} EByte".format(cmrLookup[T].codec))
+ #Try to get all subsequent E-bytes
+ while bitstrm.read(1).bool:
+ ET = bitstrm.read(3).uint
+ if ET == 0 :
+ supportedBW = [BANDWIDTH.WB, BANDWIDTH.SWB, BANDWIDTH.FB, BANDWIDTH.NREQ]
+ reserved = bitstrm.read(2)
+ BW = bitstrm.read(2).uint
+ requests[REQUESTS.BW] = supportedBW[BW]
+ elif ET == 1 :
+ S = bitstrm.read(1).bool
+ FMT = bitstrm.read(3).uint
+ if not S:
+ requests[REQUESTS.FMT] = codedFormats[FMT]
+ requests[REQUESTS.SUBFMT] = NO_REQ
+ else:
+ reserved = bitstrm.read(2)
+ subFMT = bitstrm.read(6).uint
+ requests[REQUESTS.FMT] = NO_REQ
+ requests[REQUESTS.SUBFMT] = codedSubFormats[subFMT]
+ elif ET == 2:
+ reserved = bitstrm.read(4)
+ piIndicated = True
+ elif ET == 3:
+ D = bitstrm.read(1).bool
+ Y = bitstrm.read(1).bool
+ P = bitstrm.read(1).bool
+ R = bitstrm.read(1).bool
+ requests[REQUESTS.SRCFG] = SRCONFIG(diegetic=D, yaw=Y, pitch=P, roll=R)
+ else:
+ reserved = bitstrm.read(4)
+ raise Exception("Unsupported subsequent EByte with ET={}".format(ET))
+ except ReadError as error:
+ print ("Underflow in E-Bytes parsing during unpacking, error = {}".format(error))
+ return piIndicated, requests
+
+def packEBytes(bitstrm: BitStream, requests: dict[str: any], piIndication: bool = False):
+ codec = requests[REQUESTS.CODEC] if REQUESTS.CODEC in requests.keys() else NO_REQ
+ bitrate = requests[REQUESTS.BR] if REQUESTS.BR in requests.keys() else 0
+ bandwidth = requests[REQUESTS.BW] if REQUESTS.BW in requests.keys() else NO_REQ
+ camode = requests[REQUESTS.CA] if REQUESTS.CA in requests.keys() else NO_REQ
+ format = requests[REQUESTS.FMT] if REQUESTS.FMT in requests.keys() else NO_REQ
+ subFormat = requests[REQUESTS.SUBFMT] if REQUESTS.SUBFMT in requests.keys() else NO_REQ
+ srcfg = SRCONFIG(requests[REQUESTS.SRCFG]) if REQUESTS.SRCFG in requests.keys() else None
+
+ # Check if any request needs to be sent
+ isInitialEByteNeeded = piIndication or bitrate != 0 or bandwidth != NO_REQ or camode != NO_REQ or \
+ format != NO_REQ or subFormat != NO_REQ or srcfg != None
+
+ if not isInitialEByteNeeded:
+ return
+
+ if camode != NO_REQ:
+ T = 6 if bandwidth == BANDWIDTH.SWB else 5
+ BR = getListIndex(list(CAMODE), camode)
+ assert BR > 0, "Channel Aware Mode not supported"
+ elif bitrate == 0:
+ T = 7
+ BR = 15
+ else:
+ mapBandwidthToTBit = {BANDWIDTH.NB: 0, BANDWIDTH.WB: 2, BANDWIDTH.SWB: 3, BANDWIDTH.FB: 4, NO_REQ: 2}[bandwidth]
+ T = {CODECS.AMRWB: 1, CODECS.IVAS: 7, CODECS.EVS: mapBandwidthToTBit} [codec]
+ BR = getListIndex(requestBitratesForCodec[codec], bitrate)
+ assert BR >= cmrLookup [T].startIdx and BR < cmrLookup [T].endIdx, "EVS Bitrate Index and Bandwidth Combination cannot be requested"
+
+ # Write the Initial E-Byte
+ bitstrm.append(f'bool={True}') #E-Byte H=1
+ bitstrm.append(f'uint:3={T}')
+ bitstrm.append(f'uint:4={BR}')
+
+ # Subsequent E-bytes follow
+ if piIndication:
+ bitstrm.append('hex:8=A0')
+
+ if codec != CODECS.IVAS:
+ return
+
+ # Bandwidth E-Byte
+ if bandwidth != NO_REQ:
+ bw = {BANDWIDTH.WB: 0, BANDWIDTH.SWB: 1, BANDWIDTH.FB: 2}[bandwidth]
+ bitstrm.append('hex:4=8')
+ bitstrm.append(f'uint:4={bw}')
+
+ # Coded Format/SubFormat Request E-Byte
+ if subFormat in SUBFORMATS:
+ subFmt = getListIndex(codedSubFormats, subFormat)
+ bitstrm.append('hex:8=9F') #S=0, FMT=111
+ bitstrm.append(f'uint:8={subFmt}')
+ elif format in FORMATS:
+ fmt = getListIndex(codedFormats, format)
+ bitstrm.append('hex:4=9')
+ bitstrm.append(f'bool={False}') # S=0
+ bitstrm.append(f'uint:3={fmt}')
+
+ # SR Config E-Byte
+ with srcfg:
+ bitstrm.append('hex:4=B')
+ bitstrm.append(f'bool={srcfg.diegetic}')
+ bitstrm.append(f'bool={srcfg.yaw}')
+ bitstrm.append(f'bool={srcfg.pitch}')
+ bitstrm.append(f'bool={srcfg.roll}')
+
+def unpackAUFrames(bitstrm: ConstBitStream, frameList: list[FRAME]):
+ try:
+ #Unpack Frame AUs here
+ for (idx, frm) in enumerate(frameList):
+ auSize = (frm.frmSizeBits + 7) // 8 # Zero padded bytes in amrwb_io mode
+ frm.au = bitstrm.read(auSize * 8).tobytes()
+ except ReadError as error:
+ print ("Underflow in AU Frames parsing during unpacking, error = {}".format(error))
+
+def packAUFrames(bitstrm: BitStream, frameList: list[FRAME]):
+ for frm in frameList:
+ bitstrm.append(frm.au)
+ bitstrm.bytealign()
+
+def unpackToCBytes(bitstrm: ConstBitStream, rtpTimestamp: int, Codec: CODECS) -> list[FRAME]:
+ F = True
+ frmList = list()
+
+ try:
+ while F:
+ F = bitstrm.read(1).bool
+ FT = bitstrm.read(2).uint
+ BR = bitstrm.read(4).uint
+ frm = FRAME(timestamp=rtpTimestamp)
+ if FT == 1 :
+ frm.codec = CODECS.IVAS
+ if BR == 14 :
+ supportedBitrates = [-1, 256000, 384000, 512000]
+ reserved = bitstrm.read(1)
+ D = bitstrm.read(1).bool
+ C = bitstrm.read(1).bool
+ SR_BR = bitstrm.read(2).uint
+ reserved = bitstrm.read(3)
+ if SR_BR == 0:
+ raise Exception("Reserved bitrate in SR Config ToC Indicated")
+ frm.srInfo = SRINFO(bitrate=supportedBitrates[SR_BR], diegetic=D,
+ transportCodec= SRCODEC.LC3PLUS if C else SRCODEC.LCLD)
+ else:
+ frm.bitrate = ivasBitrates[BR]
+ elif FT == 0:
+ # Codec switch only if not NO_DATA_FRAME, as IVAS/EVS signal using this
+ frm.codec = CODECS.EVS if BR != 15 else Codec
+ if BR == 13:
+ raise Exception("Reserved bitrate in EVS ToC Indicated")
+ frm.speechLost = (BR == 14)
+ frm.bitrate = evsBitrates[BR] if BR < 13 else 0
+ else:
+ frm.codec = CODECS.AMRWB
+ if BR >= 10 and BR <= 13:
+ raise Exception("Reserved bitrate in AMRWB-IO ToC Indicated")
+ frm.speechLost = (BR == 14) or (FT == 2)
+ frm.bitrate = amrwbBitrates[BR] if BR < 10 else 0
+ frm.frmSizeBits = frm.bitrate // 50
+ rtpTimestamp += 320
+ frmList.append(frm)
+ if F:
+ #skip all frame specific E-bytes before next header
+ while (bitstrm.read(1).bool):
+ print("Skipping unsupported frame specific subsequent Ebytes")
+ reserved = bitstrm.read(7)
+ except ReadError as error:
+ print ("Underflow in ToC parsing during unpacking, error = {}".format(error))
+
+ return frmList
+
+def packToCBytes(bitstrm: BitStream, frameList: list[FRAME]):
+ numFrames = len(frameList)
+
+ for idx, frame in enumerate(frameList):
+ F = (idx != (numFrames - 1))
+ FT = 0
+ BR = 0
+ if frame.frmSizeBits == 0:
+ FT = 3 if frame.codec == CODECS.AMRWB else 0 # Only AMRWB or EVS support 0 frame case
+ BR = 14 if frame.speechLost else 15 #SPEECH_LOST or NO_DATA
+ elif frame.codec == CODECS.AMRWB:
+ FT = 3
+ BR = getListIndex(amrwbBitrates[0:10], frame.bitrate)
+ elif frame.codec == CODECS.IVAS:
+ FT = 1
+ BR = 14 if frame.srInfo else getListIndex(ivasBitrates, frame.bitrate)
+ else:
+ FT = 0
+ BR = getListIndex(evsBitrates[0:13], frame.bitrate)
+
+ assert BR >= 0, "Index for table not found"
+
+ bitstrm.append(f'bool={False}') #ToC 0 bit
+ bitstrm.append(f'bool={F}') #Frame follows bit
+ bitstrm.append(f'uint:2={FT}') #Frame Type
+ bitstrm.append(f'uint:4={BR}') #Frame Type
+ if frame.srInfo and frame.codec == CODECS.IVAS:
+ SRBR = (frame.srInfo.bitrate//128000) - 1
+ bitstrm.append(f'bool={False}') #ToC 0 bit
+ bitstrm.append(f'bool={frame.srInfo.diegetic}')
+ bitstrm.append(f'bool={frame.srInfo.transportCodec == SRCODEC.LC3PLUS}')
+ bitstrm.append(f'uint:2={SRBR}')
+ bitstrm.append('uint:3=0')
+
+def unpackPiData(bitstrm: ConstBitStream, rtpTimestamp: int) -> list[PIDATA]:
+ piDataList = list[PIDATA]()
+ try:
+ # PI Data if Indicated
+ PF = True
+ piTimeStamps = rtpTimestamp
+ while PF :
+ PF = bitstrm.read(1).bool
+ PM = bitstrm.read(2).uint
+ PiType = bitstrm.read(5).uint
+ PiSize = 0
+ byte = 255
+ while byte == 255:
+ byte = bitstrm.read(8).uint
+ PiSize += byte
+
+ PiFrameData = PIDataUnpacker[PiType](bitstrm, PiSize)
+
+ if PiTypeNames[PiType] != PIDATAS.NO_PI_DATA:
+ piDataList.append(
+ PIDATA(timestamp=piTimeStamps if PM != 3 else rtpTimestamp, # Generic Pi has base timestamp
+ type=PiTypeNames[PiType],
+ data=PiFrameData))
+ piTimeStamps += 320 if PM == 2 else 0
+ except ReadError as error:
+ print ("Underflow before completion of unpacking, error = {}".format(error))
+ return piDataList
+
+def packPiData(bitstrm: BitStream, rtpTimestampBounds: tuple[int, int], piDataList: list[PIDATA]):
+ # sort the piDataList by timestamp and eliminate data where timestamp is OOB for this packet
+ piDataList = [data for data in piDataList if (data.timestamp >= rtpTimestampBounds[0] and data.timestamp < rtpTimestampBounds[1])]
+ sorted(piDataList, key=lambda data: data.timestamp)
+ numPiData = len(piDataList)
+
+ # Group PI data by timestamps
+ piDataDict = dict[int, list[PIDATA]]()
+ for data in piDataList:
+ ts = (data.timestamp // 320) * 320
+ if ts not in piDataDict.keys():
+ piDataDict[ts] = list()
+ piDataDict[ts].append(data)
+
+ curTimestamp = rtpTimestampBounds[0]
+ for ts, dataList in piDataDict.items():
+ while curTimestamp < ts:
+ # Inset NO_PI_DATA till current Timestamp is reached
+ bitstrm.append('hex:16=DF00') #PI Frame follows, Last PI header for this frame, NO_PI_DATA, size=0
+ curTimestamp += 320
+
+ for idx, data in enumerate(dataList):
+ pack = BitStream()
+ PM = 2 if idx == len(dataList) - 1 else 1
+ PF = 0 if numPiData == 1 else 1
+ TYPE = PiTypeNames.index(data.type)
+ PIDataPacker[TYPE](pack, data.data)
+ assert (pack.pos % 8) == 0, "PI data must be byte aligned"
+ SIZE = pack.pos // 8
+ assert SIZE < MAX_PACKED_PI_SIZE, f"Packed PI Size should be less than MAX_PACKED_PI_SIZE ({MAX_PACKED_PI_SIZE})"
+
+ bitstrm.append(f'uint:1={PF}')
+ bitstrm.append(f'uint:2={PM}')
+ bitstrm.append(f'uint:5={TYPE}')
+ bitstrm.append(f'uint:8={SIZE}')
+ bitstrm.append(pack)
+ numPiData -= 1
+ curTimestamp += 320
+ assert numPiData == 0, "Not all PI data was packed due to internal error"
+
+
+@dataclass
+class IvasPayload:
+ frameList: list[FRAME] = field(default_factory=list)
+ piDataList: list[PIDATA] = field(default_factory=list)
+ requests: dict [str, any] = field(default_factory=dict)
+
+ def pack(self, bitstrm: BitStream) -> int:
+ piIndication = len(self.piDataList) > 0
+ numFrames = len(self.frameList)
+ packEBytes(bitstrm, self.requests, piIndication)
+ packToCBytes(bitstrm, self.frameList)
+ packAUFrames(bitstrm, self.frameList)
+ if piIndication:
+ packPiData(bitstrm, (self.frameList[0].timestamp, self.frameList[-1].timestamp + 320), self.piDataList)
+ return numFrames
+
+ @classmethod
+ def unpack(cls, bitstrm: ConstBitStream, rtpTimestamp: int, Codec: CODECS):
+ # Unpack the E-bytes
+ piIndicated, requests = unpackEBytes(bitstrm)
+
+ # ToC Byte parsing starts with 'F' bit as H bit is already read for E-byte parsing
+ frameList = unpackToCBytes(bitstrm, rtpTimestamp, Codec)
+
+ # Extract packed AU
+ unpackAUFrames(bitstrm, frameList)
+
+ if piIndicated:
+ piDataList = unpackPiData(bitstrm, rtpTimestamp)
+ else:
+ piDataList = list()
+
+ return cls(frameList=frameList, piDataList=piDataList, requests=requests)
+
+@dataclass
+class IvasPacket:
+ hdr: RTPHDR = field(default_factory=RTPHDR)
+ payload: IvasPayload = field(default_factory=IvasPayload)
+
+ def pack(self, bitstrm: BitStream) :
+ self.hdr.pack(bitstrm)
+ numFrames = self.payload.pack(bitstrm)
+ self.hdr.updateHeader(numFrames)
+
+class IvasRtp:
+ def __init__(self, numFramesPerPacket = 4, codec: CODECS = CODECS.IVAS):
+ self.numFramesPerPacket = numFramesPerPacket
+ self.packets = list[IvasPacket]()
+ self.Codec: CODECS = codec# Track last frame's codec
+ self.requests = dict()
+ self.piData = dict()
+
+ def dumpToJSON(self, jsonFileName):
+ with open(jsonFileName, "w") as fd:
+ packets = list()
+ for packet in self.packets:
+ packetDict = asdict(packet)
+ for frame in packetDict['payload']['frameList']:
+ frame['au'] = base64.b64encode(frame['au']).decode('utf-8')
+ packets.append(packetDict)
+ json_output = json.dumps(packets, indent=4)
+ fd.write(json_output)
+
+ def requestReader(self, timestamp: int) -> dict[str, any]:
+ tsList = sorted(self.requests.keys())
+ if len(tsList) > 0:
+ lastTs = int(tsList[0])
+ for ts in tsList:
+ if timestamp >= lastTs and timestamp < int(ts):
+ return self.requests[str(lastTs)]
+ lastTs = int(ts)
+ return dict()
+
+ def piDataReader(self, startTimestamp: int, endTimestamp: int) -> list[PIDATA]:
+
+ piDataList = list()
+ while startTimestamp < endTimestamp:
+ ts = str(startTimestamp)
+ if ts in self.piData.keys():
+ for piTypes in self.piData[ts].keys():
+ dataDict = self.piData[ts][piTypes]
+ if type(dataDict) != dict:
+ data = dataDict
+ elif piTypes == PIDATAS.ISM_ORIENTATION:
+ data = list()
+ for orientation in dataDict:
+ data.append(ORIENTATION(**orientation))
+ elif "ORIENTATION" in piTypes or piTypes == PIDATAS.AUDIO_FOCUS_DIRECTION:
+ data = ORIENTATION(**dataDict)
+ elif piTypes == PIDATAS.ACOUSTIC_ENVIRONMENT:
+ data = ACOUSTIC_ENVIRONMENT(**dataDict)
+ elif piTypes == PIDATAS.AUDIO_DESCRIPTION:
+ data = list()
+ for desc in dataDict:
+ data.append(AUDIO_DESCRIPTION(**desc))
+ elif piTypes == PIDATAS.DIEGETIC_TYPE:
+ data = DIEGETIC_TYPE(**dataDict)
+ elif piTypes == PIDATAS.LISTENER_POSITION or piTypes == PIDATAS.R_ISM_POSITION:
+ data = POSITION(**dataDict)
+ elif piTypes == PIDATAS.DYNAMIC_AUDIO_SUPPRESSION:
+ data = DYNAMIC_AUDIO_SUPPRESSION(**dataDict)
+ else:
+ assert False, "Unhandled PI Data"
+ piDataList.append(PIDATA(timestamp=startTimestamp, type=piTypes, data=data))
+ startTimestamp += 320
+ return piDataList
+
+ def packG192File(self, g192File: Path, rtpDumpOut: Path, piData: dict = None, requestsData: dict = None):
+ packet = IvasPacket()
+ packet.hdr.sequenceNum=int("0xFFFF", 16)
+ packet.hdr.timestamp = 0
+ packet.hdr.ssrc = int("0xDEADBEEF", 16)
+
+ self.piData = piData
+ self.requests = requestsData
+
+ timestamp = packet.hdr.timestamp
+ piTimestamps = 0
+ with open(g192File, "rb") as fin:
+ with open(rtpDumpOut, "wb") as fout:
+ refBitStrm = ConstBitStream(fin.read())
+ frames = list[FRAME]()
+ while refBitStrm.pos < refBitStrm.len:
+ sync = hex(refBitStrm.read(16).intle)
+ nBits = refBitStrm.read(16).intle
+ assert sync == "0x6b21", "G192 syncword not found at start of packet"
+ writer = BitStream()
+ for _ in range(nBits):
+ bit = "0b1" if refBitStrm.read(16).uintle == 129 else "0b0"
+ writer.append(bit)
+ frames.append(FRAME(codec=self.Codec, frmSizeBits=nBits, bitrate=nBits * 50, speechLost=False, timestamp=timestamp, au=writer.tobytes()))
+ if (len(frames) == self.numFramesPerPacket) or (refBitStrm.pos == refBitStrm.len):
+ rtpBitstrm = BitStream()
+ numFrames = len(frames)
+ packet.payload = IvasPayload(frameList=frames, piDataList=self.piDataReader(piTimestamps, piTimestamps + (numFrames * 320)), requests=self.requestReader(piTimestamps))
+ packet.pack(bitstrm=rtpBitstrm)
+ fout.write(struct.pack('i', rtpBitstrm.bytepos))
+ fout.write(rtpBitstrm.tobytes())
+ frames = list()
+ piTimestamps += numFrames * 320
+ timestamp += 320
+
+
+ def getPackets(self):
+ return self.packets
+
+ def unpackFile(self, rtpDumpFile):
+ with open (rtpDumpFile, mode="rb") as fd:
+ while True:
+ size = fd.read(4)
+ if not size:
+ break
+ size = struct.unpack( 'i', size)[0]
+ packet = fd.read(size)
+ if not packet:
+ break
+ self.packets.append(self.unpackPacket(packet))
+
+ def unpackPacket(self, packet) -> IvasPacket :
+ bitStrm = ConstBitStream(packet)
+ hdr = RTPHDR.unpack(bitStrm)
+ payload = IvasPayload.unpack(bitStrm, rtpTimestamp=hdr.timestamp, Codec=self.Codec)
+ self.Codec = payload.frameList[-1].codec #Last Frame's codec for next frame for NO_DATA case
+ return IvasPacket(hdr=hdr, payload=payload)
+
+class ArgsParser:
+ def __init__(self):
+ self.parser = argparse.ArgumentParser()
+ self.parser.add_argument("-r", "--rtpdump", type=str, default=None, help="RTP Dump to unpack")
+ self.parser.add_argument("-j", "--json", type=str, default="unpack.json", help="Output unpacked RTP frames to JSON file")
+ self.parser.add_argument("-g", "--g192", type=str, default=None, help="G192 bitstream input for RTP Packing")
+ self.parser.add_argument("-f", "--framesPerPacket", type=int, default=1, help="Number of IVAS frames per RTP Packet")
+ self.parser.add_argument("-o", "--outrtpdump", type=str, default="output.rtpdump", help="Output RTP Dump file")
+ self.parser.add_argument("-p", "--piDataJson", type=str, default=None, help="piData to be packed")
+ self.parser.add_argument("-x", "--requestsJson", type=str, default=None, help="Requests to be packed")
+
+ def parse(self):
+ args = self.parser.parse_args()
+ return args
+
+if __name__ == "__main__":
+ args = ArgsParser().parse()
+ rtp = IvasRtp(numFramesPerPacket=args.framesPerPacket)
+
+ if args.rtpdump:
+ rtp.unpackFile(args.rtpdump)
+ rtp.dumpToJSON(args.json)
+ elif args.g192:
+ piData = dict()
+ requestsData = dict()
+ if args.piDataJson:
+ with open(args.piDataJson) as f:
+ piData = json.load(f)
+ if args.requestsJson:
+ with open(args.requestsJson) as f:
+ requestsData = json.load(f)
+ rtp.packG192File(g192File=args.g192, rtpDumpOut=args.outrtpdump, piData=piData, requestsData=requestsData)
diff --git a/tests/rtp/test_rtp.py b/tests/rtp/test_rtp.py
new file mode 100644
index 0000000000000000000000000000000000000000..342e29c8b64f3afde799ba22eab8230afeb670fe
--- /dev/null
+++ b/tests/rtp/test_rtp.py
@@ -0,0 +1,478 @@
+#!/usr/bin/env python3
+
+__copyright__ = """
+(C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
+Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+contributors to this repository. All Rights Reserved.
+
+This software is protected by copyright law and by international treaties.
+The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
+Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
+Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
+Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
+contributors to this repository retain full ownership rights in their respective contributions in
+the software. This notice grants no license of any kind, including but not limited to patent
+license, nor is any license granted by implication, estoppel or otherwise.
+
+Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
+contributions.
+
+This software is provided "AS IS", without any express or implied warranties. The software is in the
+development stage. It is intended exclusively for experts who have experience with such software and
+solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
+and fitness for a particular purpose are hereby disclaimed and excluded.
+
+Any dispute, controversy or claim arising under or in relation to providing this software shall be
+submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
+accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
+the United Nations Convention on Contracts on the International Sales of Goods.
+"""
+
+__doc__ = """
+To configure test modules.
+"""
+
+import pytest
+import csv
+import os
+import sys
+import random
+
+from tempfile import TemporaryDirectory
+from pathlib import Path
+from ivasrtp import *
+import soundfile as sf
+import numpy as np
+
+ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
+sys.path.append(ROOT_DIR)
+
+from tests.conftest import EncoderFrontend, DecoderFrontend
+
+@pytest.mark.parametrize("dtx", [False, True])
+@pytest.mark.parametrize("bitrate", [6600, 12650, 23850])
+@pytest.mark.parametrize("framesPerPacket", [1, 3, 8])
+def test_rtp_bitstream_amrwb (
+ test_info,
+ bitrate: int,
+ dtx: bool,
+ framesPerPacket: int,
+ dut_encoder_frontend: EncoderFrontend,
+ dut_decoder_frontend: DecoderFrontend
+):
+ run_rtp_bitstream_tests (
+ CODECS.AMRWB,
+ bitrate,
+ "WB",
+ "OFF",
+ "MONO",
+ dtx,
+ framesPerPacket,
+ dut_encoder_frontend,
+ dut_decoder_frontend
+ )
+
+@pytest.mark.parametrize("dtx", [False, True])
+@pytest.mark.parametrize("bitrate", [9600, 24400, 128000])
+@pytest.mark.parametrize("bandwidth", ["NB", "WB", "SWB", "FB"])
+@pytest.mark.parametrize("caMode", ["OFF", "LO", "HI"])
+@pytest.mark.parametrize("framesPerPacket", [1, 3, 8])
+def test_rtp_bitstream_evs (
+ test_info,
+ bitrate: int,
+ bandwidth: str,
+ caMode: str,
+ dtx: bool,
+ framesPerPacket: int,
+ dut_encoder_frontend: EncoderFrontend,
+ dut_decoder_frontend: DecoderFrontend
+):
+ run_rtp_bitstream_tests (
+ CODECS.EVS,
+ bitrate,
+ bandwidth,
+ caMode,
+ "MONO",
+ dtx,
+ framesPerPacket,
+ dut_encoder_frontend,
+ dut_decoder_frontend
+ )
+
+
+@pytest.mark.parametrize("bitrate", [24400, 80000, 512000])
+@pytest.mark.parametrize("bandwidth", ["WB", "SWB", "FB"])
+@pytest.mark.parametrize("format", ["STEREO", "SBA", "MC", "MASA"])
+@pytest.mark.parametrize("framesPerPacket", [8])
+def test_rtp_bitstream_ivas_nodtx (
+ test_info,
+ bitrate: int,
+ bandwidth: str,
+ format: str,
+ framesPerPacket: int,
+ dut_encoder_frontend: EncoderFrontend,
+ dut_decoder_frontend: DecoderFrontend
+):
+ run_rtp_bitstream_tests (
+ CODECS.IVAS,
+ bitrate,
+ bandwidth,
+ "OFF",
+ format,
+ False,
+ framesPerPacket,
+ dut_encoder_frontend,
+ dut_decoder_frontend
+ )
+
+@pytest.mark.parametrize("bitrate", [13200, 24400, 80000])
+@pytest.mark.parametrize("bandwidth", ["WB", "SWB", "FB"])
+@pytest.mark.parametrize("format", ["STEREO", "SBA"])
+@pytest.mark.parametrize("framesPerPacket", [1, 3])
+def test_rtp_bitstream_ivas_dtx (
+ test_info,
+ bitrate: int,
+ bandwidth: str,
+ format: str,
+ framesPerPacket: int,
+ dut_encoder_frontend: EncoderFrontend,
+ dut_decoder_frontend: DecoderFrontend
+):
+ run_rtp_bitstream_tests (
+ CODECS.IVAS,
+ bitrate,
+ bandwidth,
+ "OFF",
+ format,
+ True,
+ framesPerPacket,
+ dut_encoder_frontend,
+ dut_decoder_frontend
+ )
+
+def generateRequests(startTs: int, endTs: int) -> dict:
+ requests = dict()
+ return requests
+
+def generatePiData(startTs: int, endTs: int) -> dict:
+ data = dict()
+
+ someOrientation = lambda : ORIENTATION(w=2*random.random()-1.0, x=2*random.random()-1.0, y=2*random.random()-1.0, z=2*random.random()-1.0)
+ somePosition = lambda : POSITION( x=random.randint(-32788, 32767)/100.0, y=random.randint(-32788, 32767)/100.0, z=random.randint(-32788, 32767)/100.0)
+ someDesc = lambda : AUDIO_DESCRIPTION(isSpeech=bool(random.getrandbits(1)), isMusic=bool(random.getrandbits(1)), isAmbiance=bool(random.getrandbits(1)), isEditable=bool(random.getrandbits(1)), isBinaural=bool(random.getrandbits(1)))
+ someDAS = lambda : DYNAMIC_AUDIO_SUPPRESSION(preferSpeech=bool(random.getrandbits(1)), preferMusic=bool(random.getrandbits(1)), preferAmbiance=bool(random.getrandbits(1)), level=random.randint(0, 15))
+ someDIG = lambda : DIEGETIC_TYPE(isDigetic=[ bool(random.getrandbits(1)) for _ in range(random.randint(1, 5)) ])
+
+ for ts in range(startTs, endTs, 320):
+ pidata = dict()
+ pidata["SCENE_ORIENTATION"] = someOrientation()
+ pidata["DEVICE_ORIENTATION_COMPENSATED"] = someOrientation()
+ pidata["DEVICE_ORIENTATION_UNCOMPENSATED"] = someOrientation()
+ pidata["PLAYBACK_DEVICE_ORIENTATION"] = someOrientation()
+ pidata["HEAD_ORIENTATION"] = someOrientation()
+ pidata["AUDIO_FOCUS_DIRECTION"] = someOrientation()
+ pidata["LISTENER_POSITION"] = somePosition()
+ pidata["DYNAMIC_AUDIO_SUPPRESSION"] = someDAS()
+ pidata["AUDIO_DESCRIPTION"] = [someDesc() for n in range(random.randint(1, 5))]
+ pidata["DIEGETIC_TYPE"] = someDIG()
+ pidata["ACOUSTIC_ENVIRONMENT"] = ACOUSTIC_ENVIRONMENT(aeid=random.randint(0, 127))
+ data[str(ts)] = pidata
+ return data
+
+
+def isEqualFrame(refFrame: bytes, dutFrame: bytes):
+ assert len(refFrame) == len(dutFrame), "Encoded frame size is different"
+ for refByte, dutByte in zip(refFrame, dutFrame):
+ assert refByte == dutByte, "Encoded frames should be bitexact between ref and rtpdump"
+
+def isEqualOrientation(ref: ORIENTATION, dut: ORIENTATION):
+ assert abs(ref.w - dut.w) < 0.0001, "Scene Orientation PI Data mismatch in w"
+ assert abs(ref.x - dut.x) < 0.0001, "Scene Orientation PI Data mismatch in x"
+ assert abs(ref.y - dut.y) < 0.0001, "Scene Orientation PI Data mismatch in y"
+ assert abs(ref.z - dut.z) < 0.0001, "Scene Orientation PI Data mismatch in z"
+
+def isEqualPosition(ref: POSITION, dut: POSITION):
+ assert abs(ref.x - dut.x) < 0.3, "Position PI Data mismatch in x"
+ assert abs(ref.y - dut.y) < 0.3, "Position PI Data mismatch in y"
+ assert abs(ref.z - dut.z) < 0.3, "Position PI Data mismatch in z"
+
+def isEqualAD(ref: AUDIO_DESCRIPTION, dut: AUDIO_DESCRIPTION):
+ assert ref.isSpeech == dut.isSpeech, "Audio Description PI Data mismatch in isSpeech"
+ assert ref.isMusic == dut.isMusic, "Audio Description PI Data mismatch in isMusic"
+ assert ref.isAmbiance == dut.isAmbiance, "Audio Description PI Data mismatch in isAmbiance"
+ assert ref.isEditable == dut.isEditable, "Audio Description PI Data mismatch in isEditable"
+ assert ref.isBinaural == dut.isBinaural, "Audio Description PI Data mismatch in isBinaural"
+
+def isEqualDAS(ref: DYNAMIC_AUDIO_SUPPRESSION, dut: DYNAMIC_AUDIO_SUPPRESSION):
+ assert ref.preferSpeech == dut.preferSpeech, "Dynamic Audio Suppression PI Data mismatch in preferSpeech"
+ assert ref.preferMusic == dut.preferMusic, "Dynamic Audio Suppression PI Data mismatch in preferMusic"
+ assert ref.preferAmbiance == dut.preferAmbiance, "Dynamic Audio Suppression PI Data mismatch in preferAmbiance"
+ assert ref.level == dut.level, "Dynamic Audio Suppression PI Data mismatch in level"
+
+def isEqualDiegetic(ref: DIEGETIC_TYPE, dut: DIEGETIC_TYPE):
+ for r, d in zip(ref.isDigetic, dut.isDigetic):
+ assert r == d, f"Diegetic PI Data mismatch {r} != {d}"
+
+def isEqualAcousticEnv(ref: ACOUSTIC_ENVIRONMENT, dut: ACOUSTIC_ENVIRONMENT):
+ assert ref.aeid == dut.aeid, "Acoustic Env PI Data mismatch in Acoustic Identifier"
+ assert len(ref.rt60) == len(dut.rt60), "Acoustic Env PI Data mismatch in len(rt60)"
+ assert len(ref.dsr) == len(dut.dsr), "Acoustic Env PI Data mismatch in len(dsr)"
+ assert len(ref.dim) == len(dut.dim), "Acoustic Env PI Data mismatch in len(dim)"
+ assert len(ref.abscoeff) == len(dut.abscoeff), "Acoustic Env PI Data mismatch in len(abscoeff)"
+ for r, d in zip(ref.rt60, dut.rt60):
+ assert r == d, f"Acoustic Env PI Data mismatch in rt60 {r} != {d}"
+
+class CSVREADER:
+ def __init__(self, csvFile: Path):
+ self.rIdx = 0
+ self.rows = []
+ with open(csvFile, 'r') as fd:
+ self.rows = [ row for row in csv.reader(fd) ]
+ self.count = len(self.rows)
+
+ def next(self) -> list[float]:
+ row = self.rows[self.rIdx]
+ self.rIdx += 1
+ if self.rIdx == self.count:
+ self.rIdx = 0
+ return [ float(x) for x in row ]
+
+class RTPVALIDATE:
+
+ DTX_BITRATES = { CODECS.IVAS: 5200, CODECS.EVS: 2400, CODECS.AMRWB: 1750 }
+
+
+ def __init__(self, codec = CODECS.IVAS, bitrate = 24400, framesPerPacket = 1, dtx = False):
+ self.framesPerPacket = framesPerPacket
+ self.dtx = dtx
+ self.codec = codec
+ self.bitrate = bitrate
+ self.timestamp = 0
+ self.seqnum = -1
+ self.ssrc = -1
+ self.numFrames = 0
+ self.validatePiData = False
+ self.g192File = None
+ self.frameIdx = 0
+ #PI DATA
+ self.readers: dict[str : CSVREADER] = dict()
+
+ def setPiDataFiles(self, piFiles: tuple[Path]):
+ self.validatePiData = True
+ self.readers[PiTypeNames[0]] = CSVREADER(piFiles[0])
+ self.readers[PiTypeNames[1]] = CSVREADER(piFiles[1])
+
+ def setRefG192Bitstream(self, g192File: Path):
+ self.refPackets = ReadG192Bitstream(g192File)
+
+ def packet(self, packet: IvasPacket):
+ self.header(packet.hdr)
+ self.payload(packet.payload)
+ self.seqnum = (self.seqnum + 1) % 65536
+ self.timestamp += 320 * self.numFrames
+
+ def header(self, hdr: RTPHDR):
+ if self.timestamp == 0:
+ self.seqnum = hdr.sequenceNum
+ self.ssrc = hdr.ssrc
+ assert hdr.version == 2, "RTP Header Version must be 2"
+ assert self.ssrc == hdr.ssrc, "SSRC changed mid-stream in RTP Header"
+ assert self.timestamp == hdr.timestamp, "Timestamp mismatch in RTP Header"
+ assert self.seqnum == hdr.sequenceNum, "Sequence number mismatch in RTP Header"
+
+ def payload(self, payload: IvasPayload):
+ self.numFrames = len(payload.frameList)
+ assert self.numFrames >= 1 and self.numFrames <= self.framesPerPacket, f"Packet must have atleast 1 frame and atmost {self.framesPerPacket} frames"
+
+ for frame in payload.frameList:
+ assert self.codec == frame.codec, "Codec mismatch in RTP Payload"
+ if self.dtx:
+ assert frame.bitrate in (self.bitrate, RTPVALIDATE.DTX_BITRATES[self.codec], 0), "Bitrate mismatch in RTP Payload in DTX mode"
+ else:
+ assert frame.bitrate == self.bitrate, "Bitrate mismatch in RTP Payload"
+
+ assert self.frameIdx < len(self.refPackets), "No. of frames mismatch"
+ isEqualFrame(frame.au, self.refPackets[self.frameIdx])
+ self.frameIdx += 1
+
+ # Vallidate the PI Data
+ if self.validatePiData:
+ self.piData(payload.piDataList)
+
+ def piData(self, piDataList: list[PIDATA]):
+ for piData in piDataList:
+ assert piData.timestamp >= self.timestamp and piData.timestamp < self.timestamp + (self.numFrames * 320), "PI Data Time stamp is OOB"
+ assert piData.type == PiTypeNames[0] or piData.type == PiTypeNames[1], "PI Data is neither Scene nor Device Orientation"
+ assert type(piData.data) == ORIENTATION, "Orientation type data expected"
+ #validate the PI Data provided is the PI data in the packet
+ refData = self.readers[piData.type].next()
+ isEqualOrientation(ORIENTATION(w=refData[0], x=refData[1], y=refData[2], z=refData[3]), piData.data)
+@dataclass
+class TVARGS:
+ TVROOT = Path(ROOT_DIR).joinpath("scripts/testv")
+ def __init__(self):
+ self.tvDict = dict()
+ self.sceneFile = Path(ROOT_DIR).joinpath("scripts/trajectories/azi_plus_2-ele_plus_2-every-25-rows.csv").absolute()
+ self.deviceFile = Path(ROOT_DIR).joinpath("scripts/trajectories/headrot-1.5s.csv").absolute()
+
+ def add(self, fmt:str, inputFile:str, args:list[str] = []):
+ inputFile = str(TVARGS.TVROOT.joinpath(inputFile).absolute())
+ if fmt == "MASA":
+ args[2] = str(TVARGS.TVROOT.joinpath(args[2]).absolute())
+ self.tvDict[fmt] = (inputFile, args)
+
+ def input(self, fmt):
+ return self.tvDict[fmt][0]
+
+ def args(self, fmt, addPI=False) -> list[str]:
+ args = [ x for x in self.tvDict[fmt][1] ]
+ if addPI and fmt != "MONO":
+ args += [ "-scene_orientation", str(self.sceneFile), "-device_orientation", str(self.deviceFile) ]
+ return args
+
+ def piFiles(self) -> tuple[Path]:
+ return (self.sceneFile, self.deviceFile)
+
+def run_rtp_bitstream_tests (
+ codec: CODECS,
+ bitrate: int,
+ bandwidth: str,
+ caMode: str,
+ format: str,
+ dtx: bool,
+ framesPerPacket: int,
+ dut_encoder_frontend: EncoderFrontend,
+ dut_decoder_frontend: DecoderFrontend
+):
+ tvArgs = TVARGS()
+ tvArgs.add("MONO", "stv48n.wav")
+
+ if dtx: #use bigger file for dtx stereo
+ tvArgs.add("STEREO", "stvST48n.wav", ["-stereo"])
+ else:
+ tvArgs.add("STEREO", "stv2MASA2TC48c.wav", ["-stereo"])
+
+ tvArgs.add("MC", "stv51MC48c.wav", ["-mc", "5_1"])
+ tvArgs.add("MASA", "stv2MASA2TC48c.wav", ["-masa", "2", "stv2MASA2TC48c.met"])
+ tvArgs.add("SBA", "stvFOA48c.wav", ["-sba", "+1"])
+
+ if (bitrate > 24400 and bandwidth == "NB") or (format == "STEREO" and bitrate > 256000):
+ pytest.skip()
+
+ print("Test: dut_encoder_frontend={}, dtx={}, codec:={}, bitrate={}, bandwidth={}, caMode={}, format={},".format(dut_encoder_frontend._path, dtx, codec, bitrate, bandwidth, caMode, format))
+
+ validate = RTPVALIDATE(
+ codec=codec,
+ bitrate=bitrate,
+ framesPerPacket=framesPerPacket,
+ dtx=dtx
+ )
+
+ with TemporaryDirectory() as tmp_dir:
+ g192Out = Path(tmp_dir).joinpath(f"output-{codec}-{bitrate}-{caMode}-{format}-{dtx}.g192").absolute()
+ rtpdumpOut = Path(tmp_dir).joinpath(f"output-{codec}-{bitrate}-{caMode}-{format}-{dtx}.rtpdump").absolute()
+ rtpdumpIn = Path(tmp_dir).joinpath(f"input-{codec}-{bitrate}-{caMode}-{format}-{dtx}.rtpdump").absolute()
+ pcmOut = Path(tmp_dir).joinpath(f"output-{codec}-{bitrate}-{caMode}-{format}-{dtx}.wav").absolute()
+ pcmOutG192 = Path(tmp_dir).joinpath(f"output_g192-{codec}-{bitrate}-{caMode}-{format}-{dtx}.wav").absolute()
+ piDataOutJson = Path(tmp_dir).joinpath(f"piData-{codec}-{bitrate}-{caMode}-{format}-{dtx}.json").absolute()
+
+ # Run WITHOUT rtpdump first to generate reference bitstream
+ dut_encoder_frontend.run(
+ bitrate=bitrate,
+ input_sampling_rate=48,
+ input_path=tvArgs.input(format),
+ output_bitstream_path=g192Out,
+ sba_order=None,
+ dtx_mode=dtx,
+ max_band=bandwidth,
+ add_option_list=tvArgs.args(format)
+ )
+ validate.setRefG192Bitstream(g192File=g192Out)
+
+ packer = IvasRtp(numFramesPerPacket=framesPerPacket, codec=codec)
+
+ if codec == CODECS.IVAS:
+ outMode = "STEREO" if format == "STEREO" else "BINAURAL"
+ generatedPIData = generatePiData(0, 16000)
+ else:
+ outMode = ""
+ generatedPIData = dict()
+
+ packer.packG192File(g192File=g192Out, rtpDumpOut=rtpdumpIn, piData=generatedPIData, requestsData=generateRequests(0, 1600))
+
+ dut_decoder_frontend.run(
+ output_config=outMode,
+ output_sampling_rate=48,
+ input_bitstream_path=g192Out,
+ output_path=pcmOutG192,
+ add_option_list= []
+ )
+
+ dut_decoder_frontend.run(
+ output_config=outMode,
+ output_sampling_rate=48,
+ input_bitstream_path=rtpdumpIn,
+ output_path=pcmOut,
+ add_option_list= ["-VOIP_HF_ONLY=1", "-PiDataFile", str(piDataOutJson)]
+ )
+
+ decAudio, fs = sf.read(pcmOut)
+ g192Audio, Fs = sf.read(pcmOutG192)
+ decAudio = decAudio[4*960:]
+ assert abs(decAudio.shape[0] - g192Audio.shape[0]) <= (4 * 960), "Decoded PCM Audio is not same length as input"
+ minSamples = min(decAudio.shape[0], g192Audio.shape[0])
+ rmsdB = 10.0 * np.log10(np.finfo(float).eps + np.sum(np.abs(g192Audio[:minSamples] - decAudio[:minSamples])**2)/minSamples)
+
+ if dtx:
+ assert rmsdB < -60.0, "Bitdiff in the RTP unpacked and G192 streams for DTX stream"
+ else:
+ assert rmsdB < -96.0, "Bitdiff in the RTP unpacked and G192 streams"
+
+ with open(piDataOutJson, "r") as fd:
+ decodedPiData = json.load(fd)
+ assert decodedPiData.keys() == generatedPIData.keys(), f"Timestamp of PI data {generatedPIData.keys()} not found in Decoded PI Data {decodedPiData.keys()}"
+ for ts in generatedPIData.keys():
+ for pitype in generatedPIData[ts]:
+ data = generatedPIData[ts][pitype]
+ decoded = decodedPiData[ts][pitype]
+ if type(generatedPIData[ts][pitype]) == ORIENTATION:
+ isEqualOrientation(ORIENTATION(**decoded), data)
+ elif type(generatedPIData[ts][pitype]) == POSITION:
+ isEqualPosition(POSITION(**decoded), data)
+ elif type(generatedPIData[ts][pitype]) == DYNAMIC_AUDIO_SUPPRESSION:
+ isEqualDAS(DYNAMIC_AUDIO_SUPPRESSION(**decoded), data)
+ elif type(generatedPIData[ts][pitype]) == DIEGETIC_TYPE:
+ isEqualDiegetic(DIEGETIC_TYPE(**decoded), data)
+ elif type(generatedPIData[ts][pitype]) == ACOUSTIC_ENVIRONMENT:
+ isEqualAcousticEnv(ACOUSTIC_ENVIRONMENT(**decoded), data)
+ elif type(generatedPIData[ts][pitype]) == list:
+ for r, d in zip(generatedPIData[ts][pitype], decodedPiData[ts][pitype]):
+ isEqualAD(AUDIO_DESCRIPTION(**d), r)
+ else:
+ assert False, "Unsupported PI data found"
+
+ # Generate RTPDUMP
+ addPI = False if format == "MONO" else True
+ if addPI:
+ # Add PI Data to Pack in RTP
+ validate.setPiDataFiles(tvArgs.piFiles())
+
+ extra_args = tvArgs.args(format, addPI)
+ extra_args += ["-rtpdump", str(framesPerPacket)]
+ dut_encoder_frontend.run(
+ bitrate=bitrate,
+ input_sampling_rate=48,
+ input_path=tvArgs.input(format),
+ output_bitstream_path=rtpdumpOut,
+ sba_order=None,
+ dtx_mode=dtx,
+ max_band=bandwidth,
+ add_option_list=extra_args
+ )
+
+ unpacker = IvasRtp()
+ unpacker.unpackFile(rtpdumpOut)
+ for packet in unpacker.getPackets():
+ validate.packet(packet)