From 10265cef5e7d1a1dcc0644018a0fa6bb3ecff687 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 2 Oct 2024 13:21:40 +0300 Subject: [PATCH 001/100] Add support for IVAS rtpdump reading and writing --- apps/decoder.c | 124 +++ apps/encoder.c | 282 ++++- lib_com/bitstream.c | 52 + lib_com/common_api_types.h | 25 + lib_com/ivas_cnst.h | 23 + lib_com/ivas_prot.h | 25 + lib_com/options.h | 2 + lib_dec/lib_dec.c | 153 +++ lib_dec/lib_dec.h | 28 + lib_enc/lib_enc.c | 20 + lib_enc/lib_enc.h | 7 + lib_rend/ivas_prot_rend.h | 8 + lib_rend/ivas_rotation.c | 76 +- lib_rend/ivas_stat_rend.h | 3 + lib_util/disable_headtracking_file_reader.c | 169 +++ lib_util/disable_headtracking_file_reader.h | 86 ++ lib_util/evs_rtp_payload.c | 5 + lib_util/evs_rtp_payload.h | 5 + lib_util/g192.c | 4 + lib_util/g192.h | 8 +- lib_util/ivas_rtp_payload.c | 1081 +++++++++++++++++++ lib_util/ivas_rtp_payload.h | 421 ++++++++ lib_util/rtpdump.c | 28 + lib_util/rtpdump.h | 6 + readme.txt | 12 +- 25 files changed, 2644 insertions(+), 9 deletions(-) create mode 100644 lib_util/disable_headtracking_file_reader.c create mode 100644 lib_util/disable_headtracking_file_reader.h create mode 100644 lib_util/ivas_rtp_payload.c create mode 100644 lib_util/ivas_rtp_payload.h diff --git a/apps/decoder.c b/apps/decoder.c index b317b9a539..d06c08239c 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -37,6 +37,9 @@ #include "bitstream_reader.h" #include "evs_rtp_payload.h" #include "ism_file_writer.h" +#ifdef IVAS_RTPDUMP +#include "ivas_rtp_payload.h" +#endif #include "jbm_file_writer.h" #include "hrtf_file_reader.h" #include "ls_custom_file_reader.h" @@ -1730,10 +1733,18 @@ 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" ); +#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\n" ); #endif @@ -2936,14 +2947,25 @@ static ivas_error printBitstreamInfoVoip( bool previewFailed = true; ivas_error error = IVAS_ERR_OK; FILE *f_rtpstream = NULL; +#ifdef IVAS_RTPDUMP + IVAS_RTPDUMP_DEPACKER rtpdumpDepacker; + IVAS_RTPDUMP_DEPACKER_ERROR rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_NO_ERROR; +#else EVS_RTPDUMP_DEPACKER rtpdumpDepacker; EVS_RTPDUMP_DEPACKER_ERROR rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_NO_ERROR; +#endif uint8_t au[( IVAS_MAX_BITS_PER_FRAME + 7 ) >> 3]; int16_t auSizeBits; uint8_t *auPtr = NULL; +#ifdef IVAS_RTPDUMP + bool evsIvasModeBit; + uint16_t bitrateIndex; + bool ivasIndicatorOrQBit; +#else bool isAMRWB_IOmode; uint16_t frameTypeIndex; bool qBit; +#endif uint32_t nextPacketRcvTime_ms = 0; uint16_t rtpSequenceNumber; uint32_t rtpTimeStamp; @@ -2961,7 +2983,11 @@ static ivas_error printBitstreamInfoVoip( goto cleanup; } +#ifdef IVAS_RTPDUMP + rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_open( &rtpdumpDepacker, f_rtpstream ); +#else rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_open( &rtpdumpDepacker, f_rtpstream, arg.inputFormat == IVAS_DEC_INPUT_FORMAT_RTPDUMP_HF ); +#endif if ( rtpdumpDepackerError != EVS_RTPDUMP_DEPACKER_NO_ERROR ) { fprintf( stderr, "error in EVS_RTPDUMP_DEPACKER_open(): %d\n", rtpdumpDepackerError ); @@ -2982,12 +3008,20 @@ static ivas_error printBitstreamInfoVoip( if ( arg.inputFormat == IVAS_DEC_INPUT_FORMAT_G192 ) { error = BS_Reader_ReadVoipFrame_compact( hBsReader, au, &auSizeBits, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms ); +#ifdef IVAS_RTPDUMP + ivasIndicatorOrQBit = 1; /* good q_bit for INPUT_FORMAT_G192 */ +#else qBit = 1; /* good q_bit for INPUT_FORMAT_G192 */ +#endif } else { auPtr = au; /* might have been set to RTP packet in prev call */ +#ifdef IVAS_RTPDUMP + rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, &evsIvasModeBit, &bitrateIndex, &ivasIndicatorOrQBit, &auPtr, (uint16_t *) &auSizeBits ); +#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; @@ -2997,7 +3031,11 @@ static ivas_error printBitstreamInfoVoip( fprintf( stderr, "failed to read first RTP packet\n" ); goto cleanup; } +#ifdef IVAS_RTPDUMP + } while ( !ivasIndicatorOrQBit || auSizeBits < MIN_NUM_BITS_ACTIVE_FRAME || auSizeBits == NUM_BITS_SID_IVAS_5K2 ); +#else } while ( !qBit || auSizeBits < MIN_NUM_BITS_ACTIVE_FRAME || auSizeBits == NUM_BITS_SID_IVAS_5K2 ); +#endif BS_Reader_Rewind( hBsReader ); @@ -3011,7 +3049,11 @@ static ivas_error printBitstreamInfoVoip( cleanup: +#ifdef IVAS_RTPDUMP + IVAS_RTPDUMP_DEPACKER_close( &rtpdumpDepacker ); +#else EVS_RTPDUMP_DEPACKER_close( &rtpdumpDepacker ); +#endif if ( previewFailed && error == IVAS_ERR_OK ) { @@ -3076,12 +3118,23 @@ static ivas_error decodeVoIP( int32_t delayTimeScale = -1; int16_t i; FILE *f_rtpstream = NULL; +#ifdef IVAS_RTPDUMP + IVAS_RTPDUMP_DEPACKER rtpdumpDepacker; + IVAS_RTPDUMP_DEPACKER_ERROR rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_NO_ERROR; +#else EVS_RTPDUMP_DEPACKER rtpdumpDepacker; EVS_RTPDUMP_DEPACKER_ERROR rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_NO_ERROR; +#endif uint8_t *auPtr = NULL; +#ifdef IVAS_RTPDUMP + bool evsIvasModeBit; + uint16_t bitrateIndex; + bool ivasIndicatorOrQBit; +#else bool isAMRWB_IOmode; uint16_t frameTypeIndex; bool qBit; +#endif IVAS_DEC_BS_FORMAT bsFormat = IVAS_DEC_BS_UNKOWN; IsmFileWriter *ismWriters[IVAS_MAX_NUM_OBJECTS]; @@ -3131,12 +3184,21 @@ static ivas_error decodeVoIP( goto cleanup; } +#ifdef IVAS_RTPDUMP + rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_open( &rtpdumpDepacker, f_rtpstream ); + if ( rtpdumpDepackerError != IVAS_RTPDUMP_DEPACKER_NO_ERROR ) + { + fprintf( stderr, "error in IVAS_RTPDUMP_DEPACKER_open(): %d\n", rtpdumpDepackerError ); + goto cleanup; + } +#else rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_open( &rtpdumpDepacker, f_rtpstream, arg.inputFormat == IVAS_DEC_INPUT_FORMAT_RTPDUMP_HF ); if ( rtpdumpDepackerError != EVS_RTPDUMP_DEPACKER_NO_ERROR ) { fprintf( stderr, "error in EVS_RTPDUMP_DEPACKER_open(): %d\n", rtpdumpDepackerError ); goto cleanup; } +#endif break; case IVAS_DEC_INPUT_FORMAT_G192: auPtr = au; @@ -3173,17 +3235,29 @@ static ivas_error decodeVoIP( if ( arg.inputFormat == IVAS_DEC_INPUT_FORMAT_G192 ) { error = BS_Reader_ReadVoipFrame_compact( hBsReader, au, &auSize, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms ); +#ifdef IVAS_RTPDUMP + ivasIndicatorOrQBit = 1; /* good q_bit for INPUT_FORMAT_G192 */ +#else qBit = 1; /* good q_bit for INPUT_FORMAT_G192 */ +#endif } else { auPtr = au; /* might have been set to RTP packet in prev call */ +#ifdef IVAS_RTPDUMP + rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_readNextFrame(&rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, &evsIvasModeBit, &bitrateIndex, &ivasIndicatorOrQBit, &auPtr, (uint16_t *) &auSize ); +#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 || rtpdumpDepackerError != IVAS_RTPDUMP_DEPACKER_NO_ERROR ) +#else if ( error != IVAS_ERR_OK || rtpdumpDepackerError != EVS_RTPDUMP_DEPACKER_NO_ERROR ) +#endif { fprintf( stderr, "failed to read first RTP packet\n" ); goto cleanup; @@ -3328,7 +3402,11 @@ static ivas_error decodeVoIP( while ( nextPacketRcvTime_ms <= systemTime_ms ) { /* feed the previous read packet into the receiver now */ +#ifdef IVAS_RTPDUMP + error = IVAS_DEC_VoIP_FeedFrame(hIvasDec, auPtr, auSize, rtpSequenceNumber, rtpTimeStamp, nextPacketRcvTime_ms, ivasIndicatorOrQBit ); +#else error = IVAS_DEC_VoIP_FeedFrame( hIvasDec, auPtr, auSize, rtpSequenceNumber, rtpTimeStamp, nextPacketRcvTime_ms, qBit ); +#endif if ( error != IVAS_ERR_OK ) { fprintf( stderr, "\nError in IVAS_DEC_VoIP_FeedFrame: %s\n", IVAS_DEC_GetErrorMessage( error ) ); @@ -3341,28 +3419,66 @@ static ivas_error decodeVoIP( { error = BS_Reader_ReadVoipFrame_compact( hBsReader, au, &auSize, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms ); +#ifdef IVAS_RTPDUMP + ivasIndicatorOrQBit = 1; /* good q_bit for VOIP_G192_RTP */ +#else qBit = 1; /* good q_bit for VOIP_G192_RTP */ +#endif } else { auPtr = au; /* might have been set to RTP packet in prev call */ +#ifdef IVAS_RTPDUMP + rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, + &nextPacketRcvTime_ms, + &evsIvasModeBit, &bitrateIndex, &ivasIndicatorOrQBit, + &auPtr, (uint16_t *) &auSize ); + /* IVAS RTP payload format has timescale 16000, JBM uses 1000 internally */ + rtpTimeStamp = rtpTimeStamp / 16; + + /* parse PI data */ + error = IVAS_DEC_parsePIdata( hIvasDec, &rtpdumpDepacker.PIdataCurrentFrame, &rtpdumpDepacker.PIdataDepackerState ); + if ( error != IVAS_ERR_OK ) + { + fprintf( stderr, "\nError in IVAS_DEC_parsePIdata: %s\n", IVAS_DEC_GetErrorMessage( error ) ); + goto cleanup; + } + IVAS_DEC_resetPIdataCurrentFrame( &rtpdumpDepacker.PIdataCurrentFrame ); +#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 || rtpdumpDepackerError == IVAS_RTPDUMP_DEPACKER_EOF ) +#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; } +#ifdef IVAS_RTPDUMP + else if ( rtpdumpDepackerError != IVAS_RTPDUMP_DEPACKER_NO_ERROR ) + { + fprintf( stderr, "\nError in IVAS_RTPDUMP_DEPACKER_readNextFrame, error code: %d\n", error ); + goto cleanup; + } +#endif } /* we are finished when all packets have been received and jitter buffer is empty */ @@ -3524,6 +3640,10 @@ static ivas_error decodeVoIP( } } +#ifdef IVAS_RTPDUMP + IVAS_DEC_resetExternalOrientations(hIvasDec); +#endif + if ( !arg.quietModeEnabled ) { fprintf( stdout, "%-8d\b\b\b\b\b\b\b\b", frame ); @@ -3668,7 +3788,11 @@ static ivas_error decodeVoIP( cleanup: +#ifdef IVAS_RTPDUMP + IVAS_RTPDUMP_DEPACKER_close( &rtpdumpDepacker ); +#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 f64986ec84..258a016540 100644 --- a/apps/encoder.c +++ b/apps/encoder.c @@ -38,6 +38,12 @@ #include "ism_file_reader.h" #include "jbm_file_reader.h" #include "masa_file_reader.h" +#ifdef IVAS_RTPDUMP +#include "disable_headtracking_file_reader.h" +#include "evs_rtp_payload.h" +#include "ivas_rtp_payload.h" +#include "rotation_file_reader.h" +#endif #ifdef DEBUGGING #include "debug.h" #endif @@ -146,6 +152,12 @@ typedef struct #endif bool pca; bool ism_extended_metadata; +#ifdef IVAS_RTPDUMP + bool rtpdumpOutput; + char *sceneOrientationTrajFileName; + char *deviceOrientationTrajFileName; + char *disableHeadtrackingFileName; +#endif } EncArguments; @@ -190,6 +202,11 @@ 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; + DisableHeadtrackingFileReader *disableHeadtrackingFileReader = NULL; +#endif #ifdef DEBUGGING FILE *f_forcedModeProfile = NULL; #ifdef DEBUG_SBA @@ -205,6 +222,12 @@ int main( reset_mem( USE_BYTES ); #endif +#ifdef IVAS_RTPDUMP + FILE *f_rtpstream = NULL; + IVAS_RTPDUMP_PACKER *rtpdumpPacker = NULL; + IVAS_RTPDUMP_PACKER_ERROR rtpdumpPackerError = IVAS_RTPDUMP_PACKER_NO_ERROR; +#endif + initArgStruct( &arg ); /*------------------------------------------------------------------------------------------* @@ -235,7 +258,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,6 +620,70 @@ int main( } } +#ifdef IVAS_RTPDUMP + /*------------------------------------------------------------------------------------------* + * RTPDump + *------------------------------------------------------------------------------------------*/ + + if ( arg.rtpdumpOutput ) + { + /* Open the output file for RTPDump writing */ + f_rtpstream = fopen( arg.outputBitstreamFilename, "wb" ); + + if ( f_rtpstream == NULL ) + { + fprintf( stderr, "could not open: %s\n", arg.outputBitstreamFilename ); + goto cleanup; + } + + rtpdumpPackerError = IVAS_RTPDUMP_PACKER_open( &rtpdumpPacker, f_rtpstream ); + if ( rtpdumpPackerError != IVAS_RTPDUMP_PACKER_NO_ERROR ) + { + fprintf( stderr, "error in IVAS_RTPDUMP_PACKER_open(): %d\n", rtpdumpPackerError ); + 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; + } + } + + /*------------------------------------------------------------------------------------------* + * Open disable headtracking file + *------------------------------------------------------------------------------------------*/ + + if (arg.disableHeadtrackingFileName != NULL ) + { + if (( error = DisableHeadtrackingFileReader_open(arg.disableHeadtrackingFileName, &disableHeadtrackingFileReader ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nError: Can't open disable headtracking file %s \n\n", arg.disableHeadtrackingFileName ); + goto cleanup; + } + } +#endif + int16_t numSamplesRead = 0; uint16_t bitStream[IVAS_MAX_BITS_PER_FRAME]; uint16_t numBits = 0; @@ -763,12 +854,70 @@ int main( goto cleanup; } - /* write bitstream */ - if ( ( error = BS_Writer_WriteFrame_short( hBsWriter, bitStream, numBits, totalBitrate ) ) != IVAS_ERR_OK ) +#ifdef IVAS_RTPDUMP + if ( rtpdumpPacker->rtpdump ) { - fprintf( stderr, "\nBS_Writer_WriteFrame_short failed, error code %d\n\n", error ); - goto cleanup; + /* scene orientation */ + if ( sceneOrientationFileReader ) + { + IVAS_QUATERNION sceneOrientationQuat; + if ( ( error = HeadRotationFileReading( sceneOrientationFileReader, &sceneOrientationQuat, NULL ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nError %s while reading scene orientation from %s\n", IVAS_ENC_GetErrorMessage( error ), RotationFileReader_getFilePath( sceneOrientationFileReader ) ); + goto cleanup; + } + rtpdumpPacker->piDataPacker.sceneOrientationQuat = &sceneOrientationQuat; + } + + /* device orientation */ + if ( deviceOrientationFileReader ) + { + IVAS_QUATERNION deviceOrientationQuat; + if ( ( error = HeadRotationFileReading( deviceOrientationFileReader, &deviceOrientationQuat, NULL ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nError %s while reading device orientation from %s\n", IVAS_ENC_GetErrorMessage( error ), RotationFileReader_getFilePath( deviceOrientationFileReader ) ); + goto cleanup; + } + rtpdumpPacker->piDataPacker.deviceOrientationQuat = &deviceOrientationQuat; + } + + /* disable headtracking */ + if ( disableHeadtrackingFileReader ) + { + if ( ( error = DisableHeadtrackingFileReading( disableHeadtrackingFileReader, &rtpdumpPacker->piDataPacker.disableHeadtracking ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nError %s while reading disable headtracking data from %s\n", IVAS_ENC_GetErrorMessage( error ), DisableHeadtrackingFileReader_getFilePath( disableHeadtrackingFileReader ) ); + goto cleanup; + } + IVAS_RTPDUMP_PACKER_checkPIdisableHeadtrackingState( rtpdumpPacker ); + } + + /* PI presence */ + IVAS_RTPDUMP_PACKER_determinePIpresence( rtpdumpPacker ); + + /* write rtpdump */ + rtpdumpPackerError = IVAS_RTPDUMP_PACKER_writeNextFrame( rtpdumpPacker, bitStream, numBits, totalBitrate, + IVAS_isImmersiveFormat( hIvasEnc ), 0, false, NO_BANDWIDTH_REQUEST, NO_FORMAT_REQUEST ); + if ( rtpdumpPackerError != IVAS_RTPDUMP_PACKER_NO_ERROR ) + { + fprintf( stderr, "IVAS_RTPDUMP_PACKER_writeNextFrame() failed, error code: %d\n", rtpdumpPackerError ); + goto cleanup; + } + + IVAS_RTPDUMP_PACKER_resetPIdata( rtpdumpPacker ); } + else + { +#endif + /* 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 ) @@ -840,6 +989,28 @@ cleanup: fclose( f_bitrateProfile ); } +#ifdef IVAS_RTPDUMP + if ( rtpdumpPacker ) + { + IVAS_RTPDUMP_PACKER_close( &rtpdumpPacker ); + } + + if ( sceneOrientationFileReader ) + { + RotationFileReader_close( &sceneOrientationFileReader ); + } + + if ( deviceOrientationFileReader ) + { + RotationFileReader_close( &deviceOrientationFileReader ); + } + + if ( disableHeadtrackingFileReader ) + { + DisableHeadtrackingFileReader_close( &disableHeadtrackingFileReader ); + } +#endif + IVAS_ENC_Close( &hIvasEnc ); #ifdef WMOPS @@ -895,6 +1066,12 @@ static void initArgStruct( EncArguments *arg ) 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; + arg->disableHeadtrackingFileName = NULL; +#endif #ifdef DEBUGGING arg->forcedMode = IVAS_ENC_FORCE_UNFORCED; @@ -1705,6 +1882,73 @@ static bool parseCmdlIVAS_enc( i++; } +#ifdef IVAS_RTPDUMP + /*-----------------------------------------------------------------* + * RTPDump output + *-----------------------------------------------------------------*/ + + else if ( strcmp( argv_to_upper, "-RTPDUMP" ) == 0 ) + { + arg->rtpdumpOutput = true; + fprintf( stdout, "Output format: RTPDump\n" ); + ++i; + } + + /*-----------------------------------------------------------------* + * 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++; + } + + /*-----------------------------------------------------------------* + * Disable headtracking file reading + *-----------------------------------------------------------------*/ + + else if ( strcmp( argv_to_upper, "-DISABLE_HEADTRACKING_FILE" ) == 0 ) + { + i++; + if ( argc - i <= 4 || argv[i][0] == '-' ) + { + fprintf( stderr, "Error: Disable headtracking input file name not specified!\n\n" ); + usage_enc(); + return false; + } + + arg->disableHeadtrackingFileName = argv[i]; + i++; + } + +#endif /*-----------------------------------------------------------------* * Option not recognized *-----------------------------------------------------------------*/ @@ -1716,6 +1960,27 @@ 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; + } + if ( arg->disableHeadtrackingFileName != NULL && arg->rtpdumpOutput == false ) + { + fprintf( stderr, "Error: Disable headtracking file reading is only enabled with rtpdump output!\n\n" ); + usage_enc(); + return false; + } + +#endif /*-----------------------------------------------------------------* * Mandatory input arguments *-----------------------------------------------------------------*/ @@ -1922,6 +2187,15 @@ 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. \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" ); + fprintf( stdout, "-disable_headtracking_file : Disable headtracking input file. (1) to disable and (0) to enable headtracking. Only used with rtpdump output.\n" ); +#endif fprintf( stdout, "\n" ); return; diff --git a/lib_com/bitstream.c b/lib_com/bitstream.c index 47fbbb9db2..f5a7fbd480 100644 --- a/lib_com/bitstream.c +++ b/lib_com/bitstream.c @@ -163,6 +163,58 @@ Word16 rate2EVSmode( return rate2AMRWB_IOmode( brate ); } +#ifdef IVAS_RTPDUMP +/*-------------------------------------------------------------------* + * rate2IVASmode() + * + * Lookup IVAS mode. Note: FRAME_NO_DATA frames should be looked up with rate2EVSmode(). + *-------------------------------------------------------------------*/ +Word16 rate2IVASmode( + const Word32 brate /* i : bitrate */ +) +{ + switch ( brate ) + { + /* IVAS modes */ + case IVAS_SID_5k2: + return IVAS_TOC_SID; + case IVAS_13k2: + return IVAS_TOC_13200; + case IVAS_16k4: + return IVAS_TOC_16400; + case IVAS_24k4: + return IVAS_TOC_24400; + case IVAS_32k: + return IVAS_TOC_32000; + case IVAS_48k: + return IVAS_TOC_48000; + case IVAS_64k: + return IVAS_TOC_64000; + case IVAS_80k: + return IVAS_TOC_80000; + case IVAS_96k: + return IVAS_TOC_96000; + case IVAS_128k: + return IVAS_TOC_128000; + case IVAS_160k: + return IVAS_TOC_160000; + case IVAS_192k: + return IVAS_TOC_192000; + case IVAS_256k: + return IVAS_TOC_256000; + case IVAS_384k: + return IVAS_TOC_384000; + case IVAS_512k: + return IVAS_TOC_512000; + default: + break; + } + + return -1; +} + +#endif + /*-------------------------------------------------------------------* * ind_list_realloc() * diff --git a/lib_com/common_api_types.h b/lib_com/common_api_types.h index 8cbdfcb3cd..251e4f9003 100644 --- a/lib_com/common_api_types.h +++ b/lib_com/common_api_types.h @@ -36,6 +36,9 @@ /* options.h needed for debugging/development features * It should be stripped for delivery along with debugging switches */ #include "options.h" +#ifdef IVAS_RTPDUMP +#include +#endif #include #include #include "ivas_error.h" @@ -213,6 +216,28 @@ typedef struct _IVAS_JBM_TRACE_DATA } IVAS_JBM_TRACE_DATA; +#ifdef IVAS_RTPDUMP +typedef struct +{ + bool PIdataPresent; + char *sceneOrientation; + char *deviceOrientationCompensated; + char *deviceOrientationUncompensated; + char *acousticEnvironmentId; + char *acousticEnvironmentOnlyLateReverb; + char *acousticEnvironmentLateReverbAndEarlyReflections; + bool enableHeadTracking; + bool disableHeadTracking; +} PI_DATA_CURRENT_FRAME; + +typedef struct +{ + bool sceneOrientationSaved; + IVAS_QUATERNION sceneOrientationQuat; + bool deviceOrientationSaved; + IVAS_QUATERNION deviceOrientationQuat; +} PI_DATA_DEPACKER_STATE; +#endif /*----------------------------------------------------------------------------------* * Split rendering API constants, structures, and enums diff --git a/lib_com/ivas_cnst.h b/lib_com/ivas_cnst.h index 105c0e8057..e4dc8f8b88 100755 --- a/lib_com/ivas_cnst.h +++ b/lib_com/ivas_cnst.h @@ -1772,6 +1772,29 @@ typedef enum } STEREO_DMX_EVS_PRC; +#ifdef IVAS_RTPDUMP +/* "IVAS bit rate" column in table A.3.3.3.2-1 of TS26.253 Annex A */ +typedef enum +{ + IVAS_TOC_13200 = 0x0, // 0000 + IVAS_TOC_16400 = 0x1, // 0001 + IVAS_TOC_24400 = 0x2, // 0010 + IVAS_TOC_32000 = 0x3, // 0011 + IVAS_TOC_48000 = 0x4, // 0100 + IVAS_TOC_64000 = 0x5, // 0101 + IVAS_TOC_80000 = 0x6, // 0110 + IVAS_TOC_96000 = 0x7, // 0111 + IVAS_TOC_128000 = 0x8, // 1000 + IVAS_TOC_160000 = 0x9, // 1001 + IVAS_TOC_192000 = 0xA, // 1010 + IVAS_TOC_256000 = 0xB, // 1011 + IVAS_TOC_384000 = 0xC, // 1100 + IVAS_TOC_512000 = 0xD, // 1101 + IVAS_TOC_RESERVED = 0xE, // 1110 + IVAS_TOC_SID = 0xF // 1111 +} IVAS_TOC_BITRATE_INDEX; + +#endif #endif /* clang-format on */ /* IVAS_CNST_H */ diff --git a/lib_com/ivas_prot.h b/lib_com/ivas_prot.h index 700a9cc75c..6c3bf7f309 100755 --- a/lib_com/ivas_prot.h +++ b/lib_com/ivas_prot.h @@ -770,7 +770,32 @@ void ivas_apply_non_diegetic_panning( const int16_t output_frame /* i : output frame length per channel */ ); +#ifdef IVAS_RTPDUMP +Word16 rate2IVASmode( + const Word32 brate /* i : bitrate */ +); + +void QuaternionProduct( + const IVAS_QUATERNION q1, + const IVAS_QUATERNION q2, + IVAS_QUATERNION *const r +); + +void QuaternionInverse( + const IVAS_QUATERNION q, + IVAS_QUATERNION *const r +); +void resetPIdataCurrentFrame( + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data handle for the current audio frame */ +); + +bool ivasPayload_parseOrientationPIdata( + char *PIorientationData, /* i/o: Pointer to the PI data frame containing a single quaternion orientation */ + IVAS_QUATERNION *q /* o : Parsed quaternion value */ +); + +#endif /*----------------------------------------------------------------------------------* * JBM prototypes *----------------------------------------------------------------------------------*/ diff --git a/lib_com/options.h b/lib_com/options.h index 367583eb90..e66a459089 100644 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -162,6 +162,8 @@ #define OBJ_EDITING_PARAMISM_BIN /* Nokia: object editing for ParamISM to binaural */ #endif +#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/lib_dec.c b/lib_dec/lib_dec.c index 900b879cb6..4187a7d10e 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -3582,6 +3582,159 @@ ivas_error IVAS_DEC_Flush( } +#ifdef IVAS_RTPDUMP +/*---------------------------------------------------------------------* + * IVAS_DEC_parseSinglePIorientation( ) + * + * Parse a single orientation from PI data and feed to external orientation handle. + * Save the parsed (inverted) orientation value and use in processing in future frames, if no new PI orientation is received. + *---------------------------------------------------------------------*/ + +ivas_error IVAS_DEC_parseSinglePIorientation( + IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ + char *orientation, /* i : input PI orientation */ + bool *isOrientationSaved, /* i : flag to indicate if an orientation for this PI type was previously saved */ + IVAS_QUATERNION *savedInvOrientation /* i : previously saved (inverted) orientation for this PI type */ +) +{ + int16_t i; + ivas_error error = IVAS_ERR_OK; + if ( orientation ) + { + IVAS_QUATERNION orientationQuat, orientationQuatInv; + 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; + } + } + + /* read the orientation PI value */ + if ( !ivasPayload_parseOrientationPIdata( orientation, &orientationQuat ) ) + { + return IVAS_ERROR( IVAS_ERR_INTERNAL, "Parsing PI orientation failed" ); + } + QuaternionInverse( orientationQuat, &orientationQuatInv ); + + /* update the saved orientation value */ + *isOrientationSaved = true; + *savedInvOrientation = orientationQuatInv; + } + + if ( *isOrientationSaved ) + { + /* 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_parsePIenableDisableHeadtracking( ) + * + * Parse enable/disable headtracking from PI data + *---------------------------------------------------------------------*/ + +void IVAS_DEC_parsePIenableDisableHeadtracking( + IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* i : PI data handle for the current frame */ +) +{ + if ( PIdataCurrentFrame->enableHeadTracking ) + { + if ( hIvasDec->st_ivas->hCombinedOrientationData ) + { + hIvasDec->st_ivas->hCombinedOrientationData->enableHeadTrackingPI = 1; + } + } + if ( PIdataCurrentFrame->disableHeadTracking ) + { + if ( hIvasDec->st_ivas->hCombinedOrientationData ) + { + hIvasDec->st_ivas->hCombinedOrientationData->enableHeadTrackingPI = 0; + } + } +} + + +/*---------------------------------------------------------------------* + * IVAS_DEC_parsePIdata( ) + * + * Parse PI data for a single frame + *---------------------------------------------------------------------*/ + +ivas_error IVAS_DEC_parsePIdata( + IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame, /* i : PI data handle for the current frame */ + 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_parseSinglePIorientation( hIvasDec, PIdataCurrentFrame->sceneOrientation, &PIdataDepackerState->sceneOrientationSaved, &PIdataDepackerState->sceneOrientationQuat ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* device orientation */ + if (( error = IVAS_DEC_parseSinglePIorientation( hIvasDec, PIdataCurrentFrame->deviceOrientationUncompensated, &PIdataDepackerState->deviceOrientationSaved, &PIdataDepackerState->deviceOrientationQuat ) ) != IVAS_ERR_OK ) + { + return error; + } + + /* enable/disable headtracking */ + IVAS_DEC_parsePIenableDisableHeadtracking( hIvasDec, PIdataCurrentFrame ); + + 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 ) ); +} + + +/*---------------------------------------------------------------------* + * IVAS_DEC_resetPIdataCurrentFrame( ) + * + * Reset current PI frame struct + *---------------------------------------------------------------------*/ + +void IVAS_DEC_resetPIdataCurrentFrame( + PI_DATA_CURRENT_FRAME *piDataCurrentFrame /* i/o: PI data handle for the current frame */ +) +{ + resetPIdataCurrentFrame( piDataCurrentFrame ); +} + + +#endif /*---------------------------------------------------------------------* * IVAS_DEC_VoIP_IsEmpty( ) * diff --git a/lib_dec/lib_dec.h b/lib_dec/lib_dec.h index 50476c964b..3d3e41e6a8 100644 --- a/lib_dec/lib_dec.h +++ b/lib_dec/lib_dec.h @@ -319,6 +319,34 @@ ivas_error IVAS_DEC_Flush( int16_t *nSamplesFlushed /* o : number of samples flushed */ ); +#ifdef IVAS_RTPDUMP +ivas_error IVAS_DEC_parseSinglePIorientation( + IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ + char *orientation, /* i : input PI orientation */ + bool *isOrientationSaved, /* i : flag to indicate if an orientation for this PI type was previously saved */ + IVAS_QUATERNION *savedInvOrientation /* i : previously saved (inverted) orientation for this PI type */ +); + +void IVAS_DEC_parsePIenableDisableHeadtracking( + IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* i : PI data handle for the current frame */ +); + +ivas_error IVAS_DEC_parsePIdata( + IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame, /* i : PI data handle for the current frame */ + 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 */ +); + +void IVAS_DEC_resetPIdataCurrentFrame( + PI_DATA_CURRENT_FRAME *piDataCurrentFrame /* i/o: PI data handle for the current frame */ +); + +#endif /* Setter functions - apply changes to decoder configuration */ /*! r: error code */ diff --git a/lib_enc/lib_enc.c b/lib_enc/lib_enc.c index 23e666a7f0..b934b5c8f2 100644 --- a/lib_enc/lib_enc.c +++ b/lib_enc/lib_enc.c @@ -2417,3 +2417,23 @@ static void init_encoder_config( return; } +#ifdef IVAS_RTPDUMP + + +/*---------------------------------------------------------------------* + * IVAS_isImmersiveFormat() + * + * Check if the used format is immersive or mono + *---------------------------------------------------------------------*/ + +bool IVAS_isImmersiveFormat( + const IVAS_ENC_HANDLE hIvasEnc +) +{ + if ( hIvasEnc->st_ivas->hEncoderConfig->ivas_format == MONO_FORMAT ) + { + return false; + } + return true; +} +#endif diff --git a/lib_enc/lib_enc.h b/lib_enc/lib_enc.h index 2f40c1ab1b..ba0debf662 100644 --- a/lib_enc/lib_enc.h +++ b/lib_enc/lib_enc.h @@ -383,6 +383,13 @@ void IVAS_ENC_PrintDisclaimer( void ); +#ifdef IVAS_RTPDUMP +/*! r: true if ivas format is not mono */ +bool IVAS_isImmersiveFormat( + const IVAS_ENC_HANDLE hIvasEnc /* i : IVAS encoder handle */ +); + +#endif /* clang-format on */ #endif /* LIB_ENC_H */ diff --git a/lib_rend/ivas_prot_rend.h b/lib_rend/ivas_prot_rend.h index 8e6980b054..3ddb3a68f9 100644 --- a/lib_rend/ivas_prot_rend.h +++ b/lib_rend/ivas_prot_rend.h @@ -1292,6 +1292,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 */ ); @@ -1339,6 +1345,7 @@ ivas_error ivas_render_config_init_from_rom( * Quaternion operations *----------------------------------------------------------------------------------*/ +#ifndef IVAS_RTPDUMP void QuaternionProduct( const IVAS_QUATERNION q1, const IVAS_QUATERNION q2, @@ -1350,6 +1357,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 dc5136cd00..d11e2b6b6e 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,42 @@ 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 = (IVAS_QUATERNION) { .w = 1.0f, .x = 0.0f, .y = 0.0f, .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() * @@ -759,9 +796,14 @@ ivas_error ivas_combined_orientation_open( IVAS_VECTOR3 origo; int16_t pos_idx; +#ifdef IVAS_RTPDUMP + identity = (IVAS_QUATERNION) { .w = 1.0f, .x = 0.0f, .y = 0.0f, .z = 0.0f }; + origo = (IVAS_VECTOR3) { .x = 0.0f, .y = 0.0f, .z = 0.0f }; +#else identity.w = 1.0f; identity.x = identity.y = identity.z = 0.0f; origo.x = origo.y = origo.z = 0.0f; +#endif /* Allocate handle */ if ( ( *hCombinedOrientationData = (COMBINED_ORIENTATION_HANDLE) malloc( sizeof( COMBINED_ORIENTATION_DATA ) ) ) == NULL ) @@ -822,6 +864,9 @@ ivas_error ivas_combined_orientation_open( ( *hCombinedOrientationData )->subframe_idx = 0; ( *hCombinedOrientationData )->subframe_size = (int16_t) ( fs / ( FRAMES_PER_SEC * MAX_PARAM_SPATIAL_SUBFRAMES ) ); ( *hCombinedOrientationData )->cur_subframe_samples_rendered = 0; +#ifdef IVAS_RTPDUMP + ( *hCombinedOrientationData )->enableHeadTrackingPI = 1; +#endif return IVAS_ERR_OK; } @@ -943,9 +988,14 @@ ivas_error combine_external_and_head_orientations( IVAS_QUATERNION identity; IVAS_VECTOR3 origo; +#ifdef IVAS_RTPDUMP + identity = (IVAS_QUATERNION) { .w = 1.0f, .x = 0.0f, .y = 0.0f, .z = 0.0f }; + origo = (IVAS_VECTOR3) { .x = 0.0f, .y = 0.0f, .z = 0.0f }; +#else identity.w = 1.0f; identity.x = identity.y = identity.z = 0.0f; origo.x = origo.y = origo.z = 0.0f; +#endif /* Form combined orientations or return if no data available */ if ( hCombinedOrientationData == NULL ) @@ -983,10 +1033,24 @@ ivas_error combine_external_and_head_orientations( else if ( hExtOrientationData == NULL && headRotQuaternions != NULL ) { /* Head rotation only */ - for ( i = 0; i < hCombinedOrientationData->num_subframes; i++ ) +#ifdef IVAS_RTPDUMP + if ( hCombinedOrientationData->enableHeadTrackingPI == 1 ) + { +#endif + for ( i = 0; i < hCombinedOrientationData->num_subframes; i++ ) + { + hCombinedOrientationData->Quaternions[i] = headRotQuaternions[i]; + } +#ifdef IVAS_RTPDUMP + } + else { - hCombinedOrientationData->Quaternions[i] = headRotQuaternions[i]; + for ( i = 0; i < hCombinedOrientationData->num_subframes; i++ ) + { + hCombinedOrientationData->Quaternions[i] = identity; + } } +#endif } if ( hExtOrientationData != NULL ) @@ -1064,7 +1128,11 @@ ivas_error combine_external_and_head_orientations( hCombinedOrientationData->isHeadRotationFrozen = 0; } /* Use the most recent head rotation */ +#ifdef IVAS_RTPDUMP + if ( hExtOrientationData->enableHeadRotation[i] == 1 && hCombinedOrientationData->enableHeadTrackingPI == 1 ) +#else if ( hExtOrientationData->enableHeadRotation[i] == 1 ) +#endif { if ( hExtOrientationData->enableExternalOrientation[i] > 0 ) { @@ -1223,8 +1291,12 @@ static void external_target_interpolation( if ( hExtOrientationData->enableExternalOrientation[i - 1] == 0 ) { IVAS_QUATERNION identity; +#ifdef IVAS_RTPDUMP + identity = (IVAS_QUATERNION) { .w = 1.0f, .x = 0.0f, .y = 0.0f, .z = 0.0f }; +#else identity.w = 1.0f; identity.x = identity.y = identity.z = 0.0f; +#endif hCombinedOrientationData->Quaternions_ext_interpolation_start = identity; } else if ( hExtOrientationData->enableExternalOrientation[i - 1] == 2 ) diff --git a/lib_rend/ivas_stat_rend.h b/lib_rend/ivas_stat_rend.h index f1d777699c..64a56e85fa 100644 --- a/lib_rend/ivas_stat_rend.h +++ b/lib_rend/ivas_stat_rend.h @@ -710,6 +710,9 @@ typedef struct ivas_combined_orientation_struct int16_t cur_subframe_samples_rendered; int16_t subframe_idx_start; int16_t cur_subframe_samples_rendered_start; +#ifdef IVAS_RTPDUMP + int8_t enableHeadTrackingPI; +#endif } COMBINED_ORIENTATION_DATA, *COMBINED_ORIENTATION_HANDLE; /*----------------------------------------------------------------------------------* diff --git a/lib_util/disable_headtracking_file_reader.c b/lib_util/disable_headtracking_file_reader.c new file mode 100644 index 0000000000..985733dc83 --- /dev/null +++ b/lib_util/disable_headtracking_file_reader.c @@ -0,0 +1,169 @@ +/****************************************************************************************************** + + (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. + +*******************************************************************************************************/ + + +#include "disable_headtracking_file_reader.h" +#include +#include +#include +#include "prot.h" + +#ifdef IVAS_RTPDUMP + +struct DisableHeadtrackingFileReader +{ + FILE *inputFile; + int32_t frameCounter; + char *file_path; + bool fileRewind; +}; + + +/*-----------------------------------------------------------------------* + * DisableHeadtrackingFileReader_open() + * + * Allocate and initialize file reader + *-----------------------------------------------------------------------*/ + +ivas_error DisableHeadtrackingFileReader_open( + char *filePath, /* i : disable-headtracking input file name */ + DisableHeadtrackingFileReader **fileReader /* o : DisableHeadtrackingFileReader handle */ +) +{ + DisableHeadtrackingFileReader *self; + FILE *inputFile; + + /* Open trajectory file */ + if ( strlen( filePath ) < 1 ) + { + return IVAS_ERR_FAILED_FILE_OPEN; + } + + inputFile = fopen(filePath, "r" ); + + if ( !inputFile ) + { + return IVAS_ERR_FAILED_FILE_OPEN; + } + + self = calloc( sizeof( DisableHeadtrackingFileReader ), 1 ); + self->inputFile = inputFile; + self->frameCounter = 0; + self->file_path = calloc( sizeof( char ), strlen( filePath ) + 1 ); + strcpy( self->file_path, filePath ); + self->fileRewind = false; + + *fileReader = self; + + return IVAS_ERR_OK; +} + + +/*-----------------------------------------------------------------------* + * DisableHeadtrackingFileReading() + * + * Read values from the input file. (1) to disable and (0) to enable headtracking. + *-----------------------------------------------------------------------*/ + +ivas_error DisableHeadtrackingFileReading( + DisableHeadtrackingFileReader *fileReader, /* i/o: DisableHeadtrackingFileReader handle */ + bool *pHeadTrackingDisabled /* o : enable/disable headtracking data */ +) +{ + float disableHeadTrackingFlag; + int32_t read_values; + + /* Initial values, if they are not read from the file (enable headtracking by default) */ + disableHeadTrackingFlag = 0; + + read_values = fscanf(fileReader->inputFile, "%f", &disableHeadTrackingFlag ); + if ( ( read_values != 1 ) ) + { + if ( feof( fileReader->inputFile ) ) + { + rewind( fileReader->inputFile ); + fileReader->fileRewind = true; + return DisableHeadtrackingFileReading( fileReader, pHeadTrackingDisabled ); + } + return IVAS_ERR_FAILED_FILE_PARSE; + } + + ( fileReader->frameCounter )++; + + *pHeadTrackingDisabled = (int8_t) disableHeadTrackingFlag == 1 ? true : false; + + return IVAS_ERR_OK; +} + + +/*-----------------------------------------------------------------------* + * DisableHeadtrackingFileReader_close() + * + * Deallocates memory for the file reader + *-----------------------------------------------------------------------*/ + +void DisableHeadtrackingFileReader_close( + DisableHeadtrackingFileReader **fileReader /* i/o: DisableHeadtrackingFileReader handle */ +) +{ + if ( fileReader == NULL || *fileReader == NULL ) + { + return; + } + + fclose( ( *fileReader )->inputFile ); + free( ( *fileReader )->file_path ); + free( *fileReader ); + *fileReader = NULL; + + return; +} + + +/*-----------------------------------------------------------------------* + * DisableHeadtrackingFileReader_getFilePath() + * + * + *-----------------------------------------------------------------------*/ + +const char *DisableHeadtrackingFileReader_getFilePath( + DisableHeadtrackingFileReader *fileReader /* i/o: DisableHeadtrackingFileReader handle */ +) +{ + if ( fileReader == NULL ) + { + return NULL; + } + + return fileReader->file_path; +} +#endif diff --git a/lib_util/disable_headtracking_file_reader.h b/lib_util/disable_headtracking_file_reader.h new file mode 100644 index 0000000000..4d413b442c --- /dev/null +++ b/lib_util/disable_headtracking_file_reader.h @@ -0,0 +1,86 @@ +/****************************************************************************************************** + + (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. + +*******************************************************************************************************/ + +#ifndef IVAS_DISABLE_HEADTRACKING_FILE_READER_H +#define IVAS_DISABLE_HEADTRACKING_FILE_READER_H + +#include +#include "common_api_types.h" + +#ifdef IVAS_RTPDUMP + +typedef struct DisableHeadtrackingFileReader DisableHeadtrackingFileReader; + +/*-----------------------------------------------------------------------* + * DisableHeadtrackingFileReader_open() + * + * Allocate and initialize reader handle + *-----------------------------------------------------------------------*/ + +ivas_error DisableHeadtrackingFileReader_open( + char *filePath, /* i : disable-headtracking input file name */ + DisableHeadtrackingFileReader **fileReader /* o : DisableHeadtrackingFileReader handle */ +); + +/*-----------------------------------------------------------------------* + * DisableHeadtrackingFileReading() + * + * Read values from the input file. (1) to disable and (0) to enable headtracking. + *-----------------------------------------------------------------------*/ + +ivas_error DisableHeadtrackingFileReading( + DisableHeadtrackingFileReader *fileReader, /* i/o: DisableHeadtrackingFileReader handle */ + bool *pHeadTrackingDisabled /* o : enable/disable headtracking data */ +); + +/*-----------------------------------------------------------------------* + * DisableHeadtrackingFileReader_close() + * + * Deallocates memory for the reader handle + *-----------------------------------------------------------------------*/ + +void DisableHeadtrackingFileReader_close( + DisableHeadtrackingFileReader **fileReader /* i/o: DisableHeadtrackingFileReader handle */ +); + +/*-----------------------------------------------------------------------* + * DisableHeadtrackingFileReader_getFilePath() + * + * + *-----------------------------------------------------------------------*/ + +const char *DisableHeadtrackingFileReader_getFilePath( + DisableHeadtrackingFileReader *fileReader /* i/o: DisableHeadtrackingFileReader handle */ +); +#endif + +#endif /* IVAS_DISABLE_HEADTRACKING_FILE_READER_H */ diff --git a/lib_util/evs_rtp_payload.c b/lib_util/evs_rtp_payload.c index c122716f31..c32c77e22a 100644 --- a/lib_util/evs_rtp_payload.c +++ b/lib_util/evs_rtp_payload.c @@ -37,6 +37,7 @@ #include #include #include "evs_rtp_payload.h" +#include "options.h" static void evsPayload_unpackFrame_compact_amrWbIo( const char *payload, uint16_t payloadSizeBits, uint16_t iProtectedSize, unsigned char **framePtr, uint16_t *frameSizeInBits ) { @@ -123,7 +124,11 @@ bool evsPayload_unpackFrame( bool hf_only, char *payload, uint16_t payloadSizeBy frameFollowing, frameTypeIndex, qBit, framePtr, frameSizeInBits ); } +#ifdef IVAS_RTPDUMP +void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, 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 { bool evsModeBit = ( toc & 0x20 ) != 0; *isAMRWB_IOmode = evsModeBit; diff --git a/lib_util/evs_rtp_payload.h b/lib_util/evs_rtp_payload.h index 004ecf79b6..1ea8ad16d4 100644 --- a/lib_util/evs_rtp_payload.h +++ b/lib_util/evs_rtp_payload.h @@ -161,6 +161,11 @@ 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 ); +#ifdef IVAS_RTPDUMP + void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, 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 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 diff --git a/lib_util/g192.c b/lib_util/g192.c index 09ce7248d9..7cd222e241 100644 --- a/lib_util/g192.c +++ b/lib_util/g192.c @@ -483,7 +483,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 6d6104d453..d24e409cae 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_rtp_payload.c b/lib_util/ivas_rtp_payload.c new file mode 100644 index 0000000000..9700d47c23 --- /dev/null +++ b/lib_util/ivas_rtp_payload.c @@ -0,0 +1,1081 @@ +/****************************************************************************************************** + + (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. + +*******************************************************************************************************/ + +#include +#include +#include +#include "evs_rtp_payload.h" +#include "ivas_rtp_payload.h" +#include "ivas_prot.h" +#include "prot.h" + +#ifdef IVAS_RTPDUMP + + +/*-----------------------------------------------------------------------* + * writeByteToRtp() + * + * Write a single byte (value) to RTP packet. + *-----------------------------------------------------------------------*/ + +static void writeByteToRtp( + IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ + char value /* i : Value to be written to the RTP packet */ +) +{ + self->rtpPacket.data[self->writingIndex] = value; + ++self->writingIndex; + self->rtpPacket.payloadSize += 1; +} + + +/*-----------------------------------------------------------------------* + * ivasPayload_parseToc() + * + * Parse a single ToC byte for an IVAS payload. + *-----------------------------------------------------------------------*/ + +static void ivasPayload_parseToc( + uint8_t toc, /* i : ToC byte to be parsed */ + bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ + bool *frameFollowing, /* o : (F) bit in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + int32_t *bitrate /* o : Bitrate of the data frame indicated in the ToC byte */ +) +{ + bool isIvasToc = ( toc & 0x30 ) == 0x10; // xx01 xxxx + if ( !isIvasToc ) + { + evsHeaderFullPayload_parseToc(toc, evsIvasModeBit, frameFollowing, bitrateIndex, ivasIndicatorBit, bitrate ); + } + else + { + *evsIvasModeBit = false; + *ivasIndicatorBit = true; + *frameFollowing = ( toc & 0x40 ) != 0; + *bitrateIndex = toc & 0x0f; + *bitrate = IVASmode2rate[*bitrateIndex]; + } +} + + +/*-----------------------------------------------------------------------* + * ivasPayload_parsePItype() + * + * Parse a single PI type and set the PIdataCurrentFrame handle accordingly. + *-----------------------------------------------------------------------*/ + +static void ivasPayload_parsePItype( + char *PIframeDataBeginning, /* i : Pointer to the beginning of the PI data section in the RTP packet */ + uint16_t PIframeSizeBytes, /* i : PI data frame size in bytes */ + uint16_t parsedPIframeSizeBytes, /* i : Size of previously parsed PI frame data section in bytes */ + uint16_t PIframeType, /* i : Type of the PI data frame */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data handle for the current audio frame */ +) +{ + if ( PIframeType == PI_SCENE_ORIENTATION ) + { + PIdataCurrentFrame->sceneOrientation = PIframeDataBeginning + parsedPIframeSizeBytes; + } + else if ( PIframeType == PI_DEVICE_ORIENTATION_COMPENSATED ) + { + PIdataCurrentFrame->deviceOrientationCompensated = PIframeDataBeginning + parsedPIframeSizeBytes; + } + else if ( PIframeType == PI_DEVICE_ORIENTATION_UNCOMPENSATED ) + { + PIdataCurrentFrame->deviceOrientationUncompensated = PIframeDataBeginning + parsedPIframeSizeBytes; + } + else if ( PIframeType == PI_ACOUSTIC_ENVIRONMENT ) + { + if ( PIframeSizeBytes == PI_ACOUSTIC_ENVIRONMENT_ID_SIZE_BYTES ) + { + PIdataCurrentFrame->acousticEnvironmentId = PIframeDataBeginning + parsedPIframeSizeBytes; + } + else if ( PIframeSizeBytes == PI_ACOUSTIC_ENVIRONMENT_ONLY_LATE_REVERB_SIZE_BYTES ) + { + PIdataCurrentFrame->acousticEnvironmentOnlyLateReverb = PIframeDataBeginning + parsedPIframeSizeBytes; + } + else if ( PIframeSizeBytes == PI_ACOUSTIC_ENVIRONMENT_LATE_REVERB_AND_EARLY_REFLECTIONS_SIZE_BYTES ) + { + PIdataCurrentFrame->acousticEnvironmentLateReverbAndEarlyReflections = PIframeDataBeginning + parsedPIframeSizeBytes; + } + } + else if ( PIframeType == PI_ENABLE_HEADTRACKING ) + { + PIdataCurrentFrame->enableHeadTracking = true; + } + else if ( PIframeType == PI_DISABLE_HEADTRACKING ) + { + PIdataCurrentFrame->disableHeadTracking = true; + } + else if ( PIframeType == PI_NO_DATA ) + { + /* no data */ + } + else + { + /* reserved */ + } +} + + +/*-----------------------------------------------------------------------* + * ivasPayload_PIaddHeaderBytes() + * + * Add PI header section bytes to the PI data pointers for the current audio frame. + *-----------------------------------------------------------------------*/ + +static void ivasPayload_PIaddHeaderBytes( + uint16_t PIheaderSizeBytes, /* i : Size of the PI header section in bytes */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data handle for the current audio frame */ +) +{ + if ( PIdataCurrentFrame->sceneOrientation != NULL ) + { + PIdataCurrentFrame->sceneOrientation += PIheaderSizeBytes; + } + if ( PIdataCurrentFrame->deviceOrientationCompensated != NULL ) + { + PIdataCurrentFrame->deviceOrientationCompensated += PIheaderSizeBytes; + } + if ( PIdataCurrentFrame->deviceOrientationUncompensated != NULL ) + { + PIdataCurrentFrame->deviceOrientationUncompensated += PIheaderSizeBytes; + } + if ( PIdataCurrentFrame->acousticEnvironmentId != NULL ) + { + PIdataCurrentFrame->acousticEnvironmentId += PIheaderSizeBytes; + } + if ( PIdataCurrentFrame->acousticEnvironmentOnlyLateReverb != NULL ) + { + PIdataCurrentFrame->acousticEnvironmentOnlyLateReverb += PIheaderSizeBytes; + } + if ( PIdataCurrentFrame->acousticEnvironmentLateReverbAndEarlyReflections != NULL ) + { + PIdataCurrentFrame->acousticEnvironmentLateReverbAndEarlyReflections += PIheaderSizeBytes; + } +} + + +/*-----------------------------------------------------------------------* + * ivasPayload_parsePIdata() + * + * Parse PI data for the current audio frame. + *-----------------------------------------------------------------------*/ + +static bool ivasPayload_parsePIdata( + char *PIdataPayload, /* i : Pointer to the beginning of the PI data section in the RTP packet */ + uint16_t PIdataPayloadSizeBytes, /* i : Size of the PI data section in bytes */ + uint16_t frameIndex, /* i : Index to the current audio frame in the RTP packet */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data handle for the current audio frame */ +) +{ + // TODO(pajunen): can this accidentally include zero padding bits? + + bool somePIframeFollowing; + char *PIframeDataBeginning; + uint16_t PIframeMarker, PIframeType, tempPIframeSizeBytes, PIframeSizeBytes, totalPIframeSizeBytes, totalPIheaderSizeBytes, count_audio; + PIframeDataBeginning = PIdataPayload; + somePIframeFollowing = true; + totalPIframeSizeBytes = 0; + totalPIheaderSizeBytes = 0; + count_audio = 0; + + while ( somePIframeFollowing ) + { + if ( (int16_t) PIdataPayloadSizeBytes <= 0 ) + { + fprintf( stderr, "Error: PI payload too small\n" ); + return false; + } + + /* parse PI data header */ + somePIframeFollowing = ( *PIdataPayload & 0x80 ) != 0; + PIframeMarker = ( *PIdataPayload & 0x60 ) >> 5; + PIframeType = ( *PIdataPayload & 0x1F ); + ++PIdataPayload; + ++totalPIheaderSizeBytes; + --PIdataPayloadSizeBytes; + tempPIframeSizeBytes = ( *PIdataPayload & 0xFF ); + PIframeSizeBytes = tempPIframeSizeBytes; + while ( tempPIframeSizeBytes == 255 ) + { + ++PIdataPayload; + ++totalPIheaderSizeBytes; + --PIdataPayloadSizeBytes; + tempPIframeSizeBytes = ( *PIdataPayload & 0xFF ); + PIframeSizeBytes += tempPIframeSizeBytes; + } + ++PIdataPayload; + ++totalPIheaderSizeBytes; + --PIdataPayloadSizeBytes; + PIdataPayloadSizeBytes -= PIframeSizeBytes; + + /* general PI data for all frames */ + if ( PIframeMarker == PM_GENERAL ) + { + ivasPayload_parsePItype(PIframeDataBeginning, PIframeSizeBytes, totalPIframeSizeBytes, PIframeType, PIdataCurrentFrame ); + } + else + { + /* current frame */ + if ( ( PIframeMarker > 0 ) & ( count_audio == frameIndex ) ) + { + ivasPayload_parsePItype(PIframeDataBeginning, PIframeSizeBytes, totalPIframeSizeBytes, PIframeType, PIdataCurrentFrame ); + } + + /* increase audio frame counter */ + if ( PIframeMarker == PM_LAST ) + { + ++count_audio; + } + } + totalPIframeSizeBytes += PIframeSizeBytes; + } + ivasPayload_PIaddHeaderBytes( totalPIheaderSizeBytes, PIdataCurrentFrame ); + + return true; +} + + +/*-----------------------------------------------------------------------* + * ivasPayload_convertFromQ15() + * + * Convert a Q15 encoded value to a float value. + *-----------------------------------------------------------------------*/ + +static float ivasPayload_convertFromQ15( uint16_t Q15Value ) +{ + float temp; + temp = (float)( Q15Value & 0x7FFF ) / 0x7FFF; + return ( Q15Value & 0x8000 ) ? -temp : temp; +} + + +/*-----------------------------------------------------------------------* + * ivasPayload_convertToQ15() + * + * Convert a float value into a Q15 encoded value. + *-----------------------------------------------------------------------*/ + +static uint16_t ivasPayload_convertToQ15( float value ) +{ + uint16_t temp; + temp = (uint16_t)( fabsf( value ) * 0x7FFF ); + if ( value >= 0.0f ) + { + temp = temp & 0x7FFF; + } + else + { + temp = temp | 0x8000; + } + return temp; +} + + +/*-----------------------------------------------------------------------* + * ivasPayload_convertOrientationToQ15() + * + * Convert a float quaternion into a Q15 encoded quaternion. + *-----------------------------------------------------------------------*/ + +static void ivasPayload_convertOrientationToQ15( + IVAS_QUATERNION *orientation, /* i : Input orientation in quaternion to be converted to Q15 */ + IVAS_QUATERNION_Q15 *orientationQ15 /* o : Ouput orientation in Q15 quaternion */ +) +{ + orientationQ15->w = ivasPayload_convertToQ15( orientation->w ); + orientationQ15->x = ivasPayload_convertToQ15( orientation->x ); + orientationQ15->y = ivasPayload_convertToQ15( orientation->y ); + orientationQ15->z = ivasPayload_convertToQ15( orientation->z ); +} + + +/*-----------------------------------------------------------------------* + * ivasPayload_parseQuaternionValue() + * + * Parse a single Q15 encoded quaternion component from the RTP packet and convert it to float. + *-----------------------------------------------------------------------*/ + +static float ivasPayload_parseQuaternionComponent( + char **PIorientationData /* i/o: Pointer to the PI data frame containing a single quaternion component */ +) +{ + uint16_t tempQ15value; + tempQ15value = (uint16_t) **PIorientationData; + tempQ15value = ( tempQ15value << 8 ) | (uint16_t) *( *PIorientationData + 1 ); + *PIorientationData += 2; + return ivasPayload_convertFromQ15( tempQ15value ); +} + + +/*-----------------------------------------------------------------------* + * resetPIdataCurrentFrame() + * + * Reset the PI data handle for the current audio frame. + *-----------------------------------------------------------------------*/ + +void resetPIdataCurrentFrame( PI_DATA_CURRENT_FRAME *PIdataCurrentFrame ) +{ + PIdataCurrentFrame->PIdataPresent = false; + PIdataCurrentFrame->sceneOrientation = NULL; + PIdataCurrentFrame->deviceOrientationCompensated = NULL; + PIdataCurrentFrame->deviceOrientationUncompensated = NULL; + PIdataCurrentFrame->acousticEnvironmentId = NULL; + PIdataCurrentFrame->acousticEnvironmentOnlyLateReverb = NULL; + PIdataCurrentFrame->acousticEnvironmentLateReverbAndEarlyReflections = NULL; + PIdataCurrentFrame->enableHeadTracking = false; + PIdataCurrentFrame->disableHeadTracking = false; +} + + +/*-----------------------------------------------------------------------* + * ivasPayload_parseOrientationPIdata() + * + * Parse a single orientation in quaternions from the PI data. + *-----------------------------------------------------------------------*/ + +bool ivasPayload_parseOrientationPIdata( + char *PIorientationData, /* i/o: Pointer to the PI data frame containing a single quaternion orientation */ + IVAS_QUATERNION *q /* o : Parsed quaternion value */ +) +{ + q->w = ivasPayload_parseQuaternionComponent(&PIorientationData); + q->x = ivasPayload_parseQuaternionComponent(&PIorientationData); + q->y = ivasPayload_parseQuaternionComponent(&PIorientationData); + q->z = ivasPayload_parseQuaternionComponent(&PIorientationData); + + if ( q->w > 1.0f || q->w < -1.0f || + q->x > 1.0f || q->x < -1.0f || + q->y > 1.0f || q->y < -1.0f || + q->z > 1.0f || q->z < -1.0f ) + { + fprintf( stderr, "Error: invalid orientation PI data\n" ); + return false; + } + return true; +} + +bool ivasPayload_unpackFrame( + char *payload, /* i : RTP payload data */ + uint16_t payloadSizeBytes, /* i : RTP payload size in bytes */ + uint16_t frameIndex, /* i : Index for the data frame in the payload */ + bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ + bool *frameFollowing, /* o : (F) bit in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + unsigned char **frame, /* o : Data frame indicated by frameIndex */ + uint16_t *frameSizeInBits, /* o : Data frame size in bits */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data for frame indicated by frameIndex */ +) +{ + bool someEvsIvasModeBit, someFrameFollowing, someIvasIndicatorBit; + uint16_t someBitrateIndex, someFrameSizeInBits, fullPayloadSizeBytes, PIdataPayloadSizeBytes; + int32_t bitrate; + uint16_t iFrame; + char *PIdataPayload; + fullPayloadSizeBytes = payloadSizeBytes; + PIdataPayload = payload; + someFrameFollowing = true; + if ( payloadSizeBytes < 1 ) + { + fprintf( stderr, "Error: payload too small to parse ToC\n" ); + return false; + } + /* parse CMR/E-bytes */ + if ( *payload & E_BYTE ) // 1xxx xxxx + { + PIdataCurrentFrame->PIdataPresent = false; + while (1) + { + if ( ( *payload & 0xF0 ) == IVAS_E_CMR ) // 1111 xxxx + { + /* IVAS CMR/E-byte */ + while (1) + { + /* parse subsequent E-bytes */ + ++payload; + --payloadSizeBytes; + if ( ( *payload & 0xE0 ) == IVAS_E_BW_REQUEST ) // 100x xxxx + { + /* IVAS bandwidth request */ + if ( ( *payload & 0xFF ) == IVAS_BW_REQ_WB ) // 1000 0000 + { + /* Wideband request */ + } + else if ( ( *payload & 0xFF ) == IVAS_BW_REQ_SWB ) // 1000 0001 + { + /* Super-wideband request */ + } + else if ( ( *payload & 0xFF ) == IVAS_BW_REQ_FB ) // 1000 0010 + { + /* Fullband request */ + } + } + else if ( ( *payload & 0xE0 ) == IVAS_E_FMT_REQUEST ) // 101x xxxx + { + /* IVAS coded format request */ + if ( ( *payload & 0xFF ) == IVAS_FMT_REQ_STEREO ) // 1010 0000 + { + /* Stereo request */ + } + else if ( ( *payload & 0xFF ) == IVAS_FMT_REQ_SBA ) // 1010 0001 + { + /* SBA request */ + } + else if ( ( *payload & 0xFF ) == IVAS_FMT_REQ_MASA ) // 1010 0010 + { + /* MASA request */ + } + else if ( ( *payload & 0xFF ) == IVAS_FMT_REQ_ISM ) // 1010 0011 + { + /* ISM request */ + } + else if ( ( *payload & 0xFF ) == IVAS_FMT_REQ_MC ) // 1010 0100 + { + /* MC request */ + } + else if ( ( *payload & 0xFF ) == IVAS_FMT_REQ_OMASA ) // 1010 0101 + { + /* OMASA request */ + } + else if ( ( *payload & 0xFF ) == IVAS_FMT_REQ_OSBA ) // 1010 0110 + { + /* OSBA request */ + } + } + else if ( ( *payload & 0xE0 ) == IVAS_E_PI_INDICATION ) // 110x xxxx + { + /* IVAS PI presence */ + PIdataCurrentFrame->PIdataPresent = true; + } + else if ( ( *payload & 0xE0 ) == IVAS_E_RESERVED ) // 111x xxxx + { + /* Reserved IVAS subsequent E-byte */ + } + else + { + break; + } + + if ( payloadSizeBytes < 1 ) + { + fprintf( stderr, "Error: payload too small to parse subsequent E-bytes\n" ); + return false; + } + } + break; + } + else + { + /* EVS CMR */ + ++payload; + --payloadSizeBytes; + if ( *payload & E_BYTE ) // 1xxx xxxx + { + continue; + } + else + { + break; + } + } + } + } + + /* parse all ToC entries */ + *frame = (unsigned char *) payload; /* no need to copy frame bytes */ + for ( iFrame = 0; someFrameFollowing; ++iFrame ) + { + if ( (int16_t) payloadSizeBytes <= 0 ) + { + fprintf( stderr, "Error: payload too small\n" ); + return false; + } + if ( *payload & E_BYTE ) // 1xxx xxxx + { + fprintf( stderr, "Error: expected ToC, found CMR/E-byte\n" ); + return false; + } + ivasPayload_parseToc(*payload, &someEvsIvasModeBit, &someFrameFollowing, &someBitrateIndex, &someIvasIndicatorBit, &bitrate ); + if ( bitrate < 0 ) + { + fprintf( stderr, "Error: unexpected frameTypeIndex in ToC\n" ); + return false; + } + ++payload; + ++*frame; + someFrameSizeInBits = (uint16_t) ( bitrate / 50 ); + /* just keep/copy zero padding bits + * in case of AMRWB_IO_SID the STI bit and CMI bits following the SID_1k75 frame are also kept (A.2.2.1.3 of TS 26.445) */ + payloadSizeBytes -= 1 + ( someFrameSizeInBits + 7 ) / 8; + if ( iFrame < frameIndex ) + { + *frame += ( someFrameSizeInBits + 7 ) / 8; + if ( !someFrameFollowing ) + { + fprintf( stderr, "Error: expected ToC with F bit set\n" ); + return false; + } + } + else if ( iFrame == frameIndex ) + { + *evsIvasModeBit = someEvsIvasModeBit; + *frameFollowing = someFrameFollowing; + *bitrateIndex = someBitrateIndex; + *ivasIndicatorBit = someIvasIndicatorBit; + *frameSizeInBits = someFrameSizeInBits; + } + if ( (int16_t) payloadSizeBytes < 0 ) + { + fprintf( stderr, "Error: payload too small for frame %u data\n", frameIndex ); + return false; + } + } + + /* parse PI data */ + if ( PIdataCurrentFrame->PIdataPresent ) + { + PIdataPayloadSizeBytes = payloadSizeBytes; + PIdataPayload += ( fullPayloadSizeBytes - PIdataPayloadSizeBytes ); + if ( !ivasPayload_parsePIdata( PIdataPayload, PIdataPayloadSizeBytes, frameIndex, PIdataCurrentFrame ) ) + { + fprintf( stderr, "Error while parsing PI data\n" ); + return false; + } + } + + return true; +} + +IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_open( + IVAS_RTPDUMP_DEPACKER *self, /* o : IVAS rtpdump depacker handle */ + FILE *file /* i : Input file containing the rtpdump */ +) +{ + RTPDUMP_ERROR rtpdumpError; + self->frameFollowing = false; + rtpdumpError = RTPDUMP_OpenWithFileToRead( &self->rtpdump, file ); + if ( rtpdumpError != RTPDUMP_NO_ERROR ) + { + return IVAS_RTPDUMP_DEPACKER_RTPDUMP_ERROR; + } + resetPIdataCurrentFrame( &self->PIdataCurrentFrame ); + self->PIdataDepackerState.sceneOrientationSaved = false; + self->PIdataDepackerState.deviceOrientationSaved = false; + return IVAS_RTPDUMP_DEPACKER_NO_ERROR; +} + +IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_readNextFrame( + IVAS_RTPDUMP_DEPACKER *self, /* i/o: IVAS rtpdump depacker handle */ + uint16_t *rtpSequenceNumber, /* o : RTP sequence number of the read packet */ + uint32_t *rtpTimeStamp, /* o : RTP timestamp for the unpacked frame */ + uint32_t *rcvTime_ms, /* o : Time offset in milliseconds for the unpacked frame */ + bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ + uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + unsigned char **frame, /* o : Data frame indicated by frameIndex */ + uint16_t *frameSizeBits /* o : Data frame size in bits */ +) +{ + /* read next RTP packet from rtpdump */ + if ( !self->frameFollowing ) + { + RTPDUMP_ERROR rtpdumpError = RTPDUMP_ReadPacket( self->rtpdump, &self->rtpPacket, &self->timeoffset_ms ); + if ( rtpdumpError == RTPDUMP_READ_ENDOFFILE ) + { + return IVAS_RTPDUMP_DEPACKER_EOF; + } + else if ( rtpdumpError != RTPDUMP_NO_ERROR ) + { + return IVAS_RTPDUMP_DEPACKER_RTPDUMP_ERROR; + } + self->frameIndex = 0; + } + /* unpack next frame from RTP packet */ + if ( !ivasPayload_unpackFrame( self->rtpPacket.data + self->rtpPacket.headerSize, + self->rtpPacket.payloadSize, self->frameIndex, + evsIvasModeBit, &self->frameFollowing, bitrateIndex, ivasIndicatorBit, + frame, frameSizeBits, &self->PIdataCurrentFrame ) ) + { + return IVAS_RTPDUMP_DEPACKER_PAYLOAD_ERROR; + } + + /* return frame */ + *rtpSequenceNumber = self->rtpPacket.sequenceNumber; + *rtpTimeStamp = self->rtpPacket.timeStamp + self->frameIndex * 16000 / 50; + *rcvTime_ms = self->timeoffset_ms + self->frameIndex * 20; + ++self->frameIndex; + return IVAS_RTPDUMP_DEPACKER_NO_ERROR; +} + +void IVAS_RTPDUMP_DEPACKER_close( + IVAS_RTPDUMP_DEPACKER *self /* i : IVAS rtpdump depacker handle */ +) +{ + if ( !self ) + { + return; + } + RTPDUMP_Close( &self->rtpdump, 0 ); +} + +IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_open( + IVAS_RTPDUMP_PACKER **rtpdumpPacker, /* o : IVAS rtpdump packer handle */ + FILE *file /* i : Output file for the rtpdump stream */ +) +{ + IVAS_RTPDUMP_PACKER *self; + RTPDUMP_ERROR rtpdumpError; + self = calloc( sizeof( IVAS_RTPDUMP_PACKER ), 1 ); + rtpdumpError = RTPDUMP_OpenWithFileToWrite( &self->rtpdump, file ); + if ( rtpdumpError != RTPDUMP_NO_ERROR ) + { + return IVAS_RTPDUMP_PACKER_RTPDUMP_ERROR; + } + RTPDUMP_SetDefaultRtpPacketHeader( &self->rtpPacket ); + memset( self->rtpPacket.data, 0, sizeof( self->rtpPacket.data ) ); + self->piDataPacker.sceneOrientationQuat = NULL; + self->piDataPacker.deviceOrientationQuat = NULL; + self->piDataPacker.enableHeadtracking = false; + self->piDataPacker.disableHeadtracking = false; + self->piDataPackerState.previousDisableHeadtrackingState = false; + self->piDataPresent = false; + *rtpdumpPacker = self; + return IVAS_RTPDUMP_PACKER_NO_ERROR; +} + +void IVAS_RTPDUMP_PACKER_writeEbytes( + IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ + int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet for bidirectional signalling */ + bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS). + * Immersive bitrate CMRs follow A.3.3.3.3.2 in TS26.253. Mono bitrate CMRs follow A.2.2.1.1 in TS26.445. */ + IVAS_BANDWIDTH_REQUEST ivasBandwidthRequest, /* i : Requested bandwidth to be written as subsequent E-byte in the packet for bidirectional signalling */ + IVAS_FORMAT_REQUEST ivasFormatRequest /* i : Requested format to be written as subsequent E-byte in the packet for bidirectional signalling */ +) +{ + UWord8 initialEbyte, subsequentEbyte; + bool initialEbyteWritten; + + /* determine E-bytes */ + initialEbyte = 0; + subsequentEbyte = 0; + initialEbyteWritten = false; + if ( requestImmersiveFormatBitrate ) + { + switch ( requestedBitrate ) + { + case IVAS_13k2: + initialEbyte = IVAS_CMR_13k2; + break; + case IVAS_16k4: + initialEbyte = IVAS_CMR_16k4; + break; + case IVAS_24k4: + initialEbyte = IVAS_CMR_24k4; + break; + case IVAS_32k: + initialEbyte = IVAS_CMR_32k; + break; + case IVAS_48k: + initialEbyte = IVAS_CMR_48k; + break; + case IVAS_64k: + initialEbyte = IVAS_CMR_64k; + break; + case IVAS_80k: + initialEbyte = IVAS_CMR_80k; + break; + case IVAS_96k: + initialEbyte = IVAS_CMR_96k; + break; + case IVAS_128k: + initialEbyte = IVAS_CMR_128k; + break; + case IVAS_160k: + initialEbyte = IVAS_CMR_160k; + break; + case IVAS_192k: + initialEbyte = IVAS_CMR_192k; + break; + case IVAS_256k: + initialEbyte = IVAS_CMR_256k; + break; + case IVAS_384k: + initialEbyte = IVAS_CMR_384k; + break; + case IVAS_512k: + initialEbyte = IVAS_CMR_512k; + break; + default: + initialEbyte = IVAS_CMR_NO_REQ; + break; + } + writeByteToRtp( self, (char) initialEbyte ); + initialEbyteWritten = true; + } + else + { + /* EVS CMR */ + initialEbyteWritten = false; + } + + if ( ivasBandwidthRequest != NO_BANDWIDTH_REQUEST ) + { + if ( !initialEbyteWritten ) + { + initialEbyte = IVAS_CMR_NO_REQ; + writeByteToRtp( self, (char) initialEbyte ); + initialEbyteWritten = true; + } + + switch ( ivasBandwidthRequest ) + { + case WB_REQUEST: + subsequentEbyte = IVAS_BW_REQ_WB; + break; + case SWB_REQUEST: + subsequentEbyte = IVAS_BW_REQ_SWB; + break; + case FB_REQUEST: + subsequentEbyte = IVAS_BW_REQ_FB; + break; + default: + subsequentEbyte = IVAS_BW_NO_REQ; + break; + } + writeByteToRtp( self, (char) subsequentEbyte ); + } + + if ( ivasFormatRequest != NO_FORMAT_REQUEST ) + { + if ( !initialEbyteWritten ) + { + initialEbyte = IVAS_CMR_NO_REQ; + writeByteToRtp( self, (char) initialEbyte ); + initialEbyteWritten = true; + } + + switch ( ivasFormatRequest ) + { + case STEREO_REQUEST: + subsequentEbyte = IVAS_FMT_REQ_STEREO; + break; + case SBA_REQUEST: + subsequentEbyte = IVAS_FMT_REQ_SBA; + break; + case MASA_REQUEST: + subsequentEbyte = IVAS_FMT_REQ_MASA; + break; + case ISM_REQUEST: + subsequentEbyte = IVAS_FMT_REQ_ISM; + break; + case MC_REQUEST: + subsequentEbyte = IVAS_FMT_REQ_MC; + break; + case OMASA_REQUEST: + subsequentEbyte = IVAS_FMT_REQ_OMASA; + break; + case OSBA_REQUEST: + subsequentEbyte = IVAS_FMT_REQ_OSBA; + break; + default: + subsequentEbyte = IVAS_FMT_NO_REQ; + break; + } + writeByteToRtp( self, (char) subsequentEbyte ); + } + + if ( self->piDataPresent ) + { + if ( !initialEbyteWritten ) + { + initialEbyte = IVAS_CMR_NO_REQ; + writeByteToRtp( self, (char) initialEbyte ); + initialEbyteWritten = true; + } + subsequentEbyte = IVAS_PI_INDICATION; + writeByteToRtp( self, (char) subsequentEbyte ); + } +} + +void IVAS_RTPDUMP_PACKER_writeToc( + IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ + int32_t frameBitrate, /* i : Bitrate of the written frame */ + bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS). + * Immersive format ToCs follow A.3.3.2 in TS26.253. Mono format ToCs follow A.2.2.1.2 in TS26.445.*/ + bool someFrameFollowing /* i : Flag to indicate if another frame follows this frame in the same RTP packet. */ +) +{ + UWord8 H_bit, F_bit, EVS_IVAS_mode_bit, Q_bit, toc; + int16_t is_amr_wb; + + is_amr_wb = 0; + H_bit = 0; + F_bit = someFrameFollowing ? 1 : 0; + /* FRAME_NO_DATA frames are written as in TS26.445 */ + if ( isImmersiveFormat && frameBitrate != FRAME_NO_DATA ) + { + toc = (UWord8)( rate2IVASmode( frameBitrate ) ); + EVS_IVAS_mode_bit = 0; + Q_bit = 1; + } + else + { + toc = (UWord8)( rate2EVSmode( frameBitrate, &is_amr_wb ) ); + EVS_IVAS_mode_bit = (UWord8) is_amr_wb; + Q_bit = 0; + } + toc = ( H_bit << 7 ) | ( F_bit << 6 ) | ( EVS_IVAS_mode_bit << 5 ) | ( Q_bit << 4 ) | toc; + writeByteToRtp( self, (char) toc ); +} + +IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_writeNextFrame( + IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ + const uint16_t *bitStream, /* i : Bitstream containing the data frame to be written */ + int32_t numBits, /* i : Number of bits contained in the data frame */ + int32_t frameBitrate, /* i : Bitrate of the data frame */ + bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS) */ + int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet */ + bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS) */ + IVAS_BANDWIDTH_REQUEST ivasBandwidthRequest, /* i : Requested bandwidth to be written as subsequent E-byte in the packet */ + IVAS_FORMAT_REQUEST ivasFormatRequest /* i : Requested format to be written as subsequent E-byte in the packet */ +) +{ + + RTPDUMP_ERROR rtpdumpError; + int32_t i, k; + char tempData; + + self->writingIndex = self->rtpPacket.headerSize; + self->rtpPacket.payloadSize = 0; + memset( self->rtpPacket.data, 0, sizeof( self->rtpPacket.data ) ); + + /* payload header */ + IVAS_RTPDUMP_PACKER_writeEbytes(self, requestedBitrate, requestImmersiveFormatBitrate, ivasBandwidthRequest, ivasFormatRequest ); + IVAS_RTPDUMP_PACKER_writeToc( self, frameBitrate, isImmersiveFormat, false ); + + /* frame data */ + for (i = 0; i < numBits; i += 8) { + tempData = 0; + for (k = 0; k < 8; ++k) { + tempData = ( tempData << 1 ) | (char) bitStream[i + k]; + } + writeByteToRtp( self, tempData ); + } + + /* PI data */ + IVAS_RTPDUMP_PACKER_writePIdata( self ); + + /* write packet */ + rtpdumpError = RTPDUMP_WritePacket( self->rtpdump, &self->rtpPacket, self->timeoffset_ms ); + if ( rtpdumpError != RTPDUMP_NO_ERROR ) + { + return IVAS_RTPDUMP_PACKER_RTPDUMP_ERROR; + } + + /* adjust packetizer values */ + self->rtpPacket.sequenceNumber += 1; + self->rtpPacket.timeStamp += 320; + self->timeoffset_ms += 20; + + return IVAS_RTPDUMP_PACKER_NO_ERROR; +} + +void IVAS_RTPDUMP_PACKER_writePIdata( + IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ +) +{ + if ( !self->piDataPresent ) + { + return; + } + + UWord8 PIheaderByte; + IVAS_QUATERNION_Q15 sceneOrientationQ15, deviceOrientationQ15; + bool somePIfollowing = false; + + /* PI header section */ + + /* scene orientation */ + if ( self->piDataPacker.sceneOrientationQuat ) + { + if ( self->piDataPacker.deviceOrientationQuat || self->piDataPacker.enableHeadtracking || self->piDataPacker.disableHeadtracking ) + { + somePIfollowing = true; + } + if ( somePIfollowing ) + { + PIheaderByte = ( PF1 << 7 ) | ( PM_NOT_LAST << 5 ) | PI_SCENE_ORIENTATION; + } + else + { + PIheaderByte = ( PF0 << 7 ) | ( PM_LAST << 5 ) | PI_SCENE_ORIENTATION; + } + writeByteToRtp( self, (char) PIheaderByte ); + writeByteToRtp( self, PI_SCENE_ORIENTATION_SIZE_BYTES ); + } + + /* device orientation */ + if ( self->piDataPacker.deviceOrientationQuat ) + { + if ( self->piDataPacker.enableHeadtracking || self->piDataPacker.disableHeadtracking ) + { + somePIfollowing = true; + } + else + { + somePIfollowing = false; + } + if ( somePIfollowing ) + { + PIheaderByte = ( PF0 << 7 ) | ( PM_NOT_LAST << 5 ) | PI_DEVICE_ORIENTATION_UNCOMPENSATED; + } + else + { + PIheaderByte = ( PF0 << 7 ) | ( PM_LAST << 5 ) | PI_DEVICE_ORIENTATION_UNCOMPENSATED; + } + writeByteToRtp( self, (char) PIheaderByte ); + writeByteToRtp( self, PI_DEVICE_ORIENTATION_UNCOMPENSATED_SIZE_BYTES ); + } + + /* enable/disable headtracking */ + if ( self->piDataPacker.enableHeadtracking && !self->piDataPacker.disableHeadtracking ) + { + PIheaderByte = ( PF0 << 7 ) | ( PM_LAST << 5 ) | PI_ENABLE_HEADTRACKING; + writeByteToRtp( self, (char) PIheaderByte ); + writeByteToRtp( self, PI_ENABLE_HEADTRACKING_SIZE_BYTES ); + } + if ( self->piDataPacker.disableHeadtracking ) + { + PIheaderByte = ( PF0 << 7 ) | ( PM_LAST << 5 ) | PI_DISABLE_HEADTRACKING; + writeByteToRtp( self, (char) PIheaderByte ); + writeByteToRtp( self, PI_DISABLE_HEADTRACKING_SIZE_BYTES ); + } + + /* PI frame data section */ + + if ( self->piDataPacker.sceneOrientationQuat ) + { + ivasPayload_convertOrientationToQ15( self->piDataPacker.sceneOrientationQuat, &sceneOrientationQ15 ); + writeByteToRtp( self, (char) ( sceneOrientationQ15.w >> 8 ) ); + writeByteToRtp( self, (char) ( sceneOrientationQ15.w & 0x00FF ) ); + writeByteToRtp( self, (char) ( sceneOrientationQ15.x >> 8 ) ); + writeByteToRtp( self, (char) ( sceneOrientationQ15.x & 0x00FF ) ); + writeByteToRtp( self, (char) ( sceneOrientationQ15.y >> 8 ) ); + writeByteToRtp( self, (char) ( sceneOrientationQ15.y & 0x00FF ) ); + writeByteToRtp( self, (char) ( sceneOrientationQ15.z >> 8 ) ); + writeByteToRtp( self, (char) ( sceneOrientationQ15.z & 0x00FF ) ); + } + + if ( self->piDataPacker.deviceOrientationQuat ) + { + ivasPayload_convertOrientationToQ15( self->piDataPacker.deviceOrientationQuat, &deviceOrientationQ15 ); + writeByteToRtp( self, (char) ( deviceOrientationQ15.w >> 8 ) ); + writeByteToRtp( self, (char) ( deviceOrientationQ15.w & 0x00FF ) ); + writeByteToRtp( self, (char) ( deviceOrientationQ15.x >> 8 ) ); + writeByteToRtp( self, (char) ( deviceOrientationQ15.x & 0x00FF ) ); + writeByteToRtp( self, (char) ( deviceOrientationQ15.y >> 8 ) ); + writeByteToRtp( self, (char) ( deviceOrientationQ15.y & 0x00FF ) ); + writeByteToRtp( self, (char) ( deviceOrientationQ15.z >> 8 ) ); + writeByteToRtp( self, (char) ( deviceOrientationQ15.z & 0x00FF ) ); + } +} + +void IVAS_RTPDUMP_PACKER_checkPIdisableHeadtrackingState( + IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ +) +{ + if ( self->piDataPacker.disableHeadtracking == self->piDataPackerState.previousDisableHeadtrackingState ) + { + /* same state as before, do not send PI data */ + self->piDataPacker.disableHeadtracking = false; + } + else if ( self->piDataPacker.disableHeadtracking && self->piDataPackerState.previousDisableHeadtrackingState == false ) + { + /* headtracking was enabled, send disable headtracking PI data */ + self->piDataPackerState.previousDisableHeadtrackingState = true; + } + else if ( !self->piDataPacker.disableHeadtracking && self->piDataPackerState.previousDisableHeadtrackingState == true ) + { + /* headtracking was disabled, send enable headtracking PI data */ + self->piDataPackerState.previousDisableHeadtrackingState = false; + self->piDataPacker.enableHeadtracking = true; + } +} + +void IVAS_RTPDUMP_PACKER_determinePIpresence( + IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ +) +{ + self->piDataPresent = false; + if ( self->piDataPacker.sceneOrientationQuat || + self->piDataPacker.deviceOrientationQuat || + self->piDataPacker.enableHeadtracking || + self->piDataPacker.disableHeadtracking ) + { + self->piDataPresent = true; + } +} + +void IVAS_RTPDUMP_PACKER_resetPIdata( + IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ +) +{ + self->piDataPresent = false; + self->piDataPacker.sceneOrientationQuat = NULL; + self->piDataPacker.deviceOrientationQuat = NULL; + self->piDataPacker.enableHeadtracking = false; + self->piDataPacker.disableHeadtracking = false; +} + +void IVAS_RTPDUMP_PACKER_close( + IVAS_RTPDUMP_PACKER **self /* i : IVAS rtpdump packer handle */ +) +{ + if ( !self || !( *self ) ) + { + return; + } + RTPDUMP_Close( &( *self )->rtpdump, 0 ); + free( *self ); + *self = NULL; +} +#endif diff --git a/lib_util/ivas_rtp_payload.h b/lib_util/ivas_rtp_payload.h new file mode 100644 index 0000000000..7adf4eb4b2 --- /dev/null +++ b/lib_util/ivas_rtp_payload.h @@ -0,0 +1,421 @@ +/****************************************************************************************************** + + (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. + +*******************************************************************************************************/ + +#ifndef IVAS_RTP_PAYLOAD_H +#define IVAS_RTP_PAYLOAD_H + +#pragma once +#include +#include +#include +#include "common_api_types.h" +#include "ivas_cnst.h" +#include "rtpdump.h" + +#ifdef IVAS_RTPDUMP + +#ifdef __cplusplus +extern "C" +{ +#endif + + +static const int32_t IVASmode2rate[16] = { + 13200, + 16400, + 24400, + 32000, + 48000, + 64000, + 80000, + 96000, + 128000, + 160000, + 192000, + 256000, + 384000, + 512000, + -1, /* Reserved */ + 5200 /* SID */ +}; + +typedef enum +{ + PF0 = 0, + PF1 = 1 +} PI_PF; + +typedef enum +{ + PM_RESERVED = 0, + PM_NOT_LAST = 1, + PM_LAST = 2, + PM_GENERAL = 3 +} PI_PM; + +typedef enum +{ + PI_SCENE_ORIENTATION = 0, + PI_DEVICE_ORIENTATION_COMPENSATED = 1, + PI_DEVICE_ORIENTATION_UNCOMPENSATED = 2, + PI_ACOUSTIC_ENVIRONMENT = 3, + PI_ENABLE_HEADTRACKING = 4, + PI_DISABLE_HEADTRACKING = 5, + PI_NO_DATA = 31 +} PI_TYPE; + +typedef enum +{ + PI_SCENE_ORIENTATION_SIZE_BYTES = 8, + PI_DEVICE_ORIENTATION_COMPENSATED_SIZE_BYTES = 8, + PI_DEVICE_ORIENTATION_UNCOMPENSATED_SIZE_BYTES = 8, + PI_ACOUSTIC_ENVIRONMENT_ID_SIZE_BYTES = 1, + PI_ACOUSTIC_ENVIRONMENT_ONLY_LATE_REVERB_SIZE_BYTES = 5, + PI_ACOUSTIC_ENVIRONMENT_LATE_REVERB_AND_EARLY_REFLECTIONS_SIZE_BYTES = 8, + PI_ENABLE_HEADTRACKING_SIZE_BYTES = 0, + PI_DISABLE_HEADTRACKING_SIZE_BYTES = 0, + PI_NO_DATA_SIZE_BYTES = 0 +} PI_SIZE_BYTES; + +typedef struct +{ + IVAS_QUATERNION *sceneOrientationQuat; + IVAS_QUATERNION *deviceOrientationQuat; + bool enableHeadtracking; + bool disableHeadtracking; +} PI_DATA_PACKER; + +typedef struct +{ + bool previousDisableHeadtrackingState; +} PI_DATA_PACKER_STATE; + +typedef struct +{ + uint16_t w, x, y, z; +} IVAS_QUATERNION_Q15; + +typedef enum +{ + IVAS_CMR_13k2 = 0xF0, // 1111 0000 + IVAS_CMR_16k4 = 0xF1, // 1111 0001 + IVAS_CMR_24k4 = 0xF2, // 1111 0010 + IVAS_CMR_32k = 0xF3, // 1111 0011 + IVAS_CMR_48k = 0xF4, // 1111 0100 + IVAS_CMR_64k = 0xF5, // 1111 0101 + IVAS_CMR_80k = 0xF6, // 1111 0110 + IVAS_CMR_96k = 0xF7, // 1111 0111 + IVAS_CMR_128k = 0xF8, // 1111 1000 + IVAS_CMR_160k = 0xF9, // 1111 1001 + IVAS_CMR_192k = 0xFA, // 1111 1010 + IVAS_CMR_256k = 0xFB, // 1111 1011 + IVAS_CMR_384k = 0xFC, // 1111 1100 + IVAS_CMR_512k = 0xFD, // 1111 1101 + IVAS_CMR_RESERVED = 0xFE, // 1111 1110 + IVAS_CMR_NO_REQ = 0xFF // 1111 1111 +} IVAS_INITIAL_E_BYTE; + +typedef enum +{ + IVAS_BW_REQ_WB = 0x80, // 1000 0000 + IVAS_BW_REQ_SWB = 0x81, // 1000 0001 + IVAS_BW_REQ_FB = 0x82, // 1000 0010 + IVAS_BW_NO_REQ = 0x83, // 1000 0011 + IVAS_FMT_REQ_STEREO = 0xA0, // 1010 0000 + IVAS_FMT_REQ_SBA = 0xA1, // 1010 0001 + IVAS_FMT_REQ_MASA = 0xA2, // 1010 0010 + IVAS_FMT_REQ_ISM = 0xA3, // 1010 0011 + IVAS_FMT_REQ_MC = 0xA4, // 1010 0100 + IVAS_FMT_REQ_OMASA = 0xA5, // 1010 0101 + IVAS_FMT_REQ_OSBA = 0xA6, // 1010 0110 + IVAS_FMT_NO_REQ = 0xA7, // 1010 0111 + IVAS_PI_INDICATION = 0xC0 // 1100 0000 +} IVAS_SUBSEQUENT_E_BYTE; + +typedef enum +{ + WB_REQUEST, + SWB_REQUEST, + FB_REQUEST, + NO_BANDWIDTH_REQUEST +} IVAS_BANDWIDTH_REQUEST; + +typedef enum +{ + STEREO_REQUEST, + SBA_REQUEST, + MASA_REQUEST, + ISM_REQUEST, + MC_REQUEST, + OMASA_REQUEST, + OSBA_REQUEST, + NO_FORMAT_REQUEST +} IVAS_FORMAT_REQUEST; + +typedef enum +{ + E_BYTE = 0x80, // 1xxx xxxx + IVAS_E_CMR = 0xF0, // 1111 xxxx + IVAS_E_BW_REQUEST = 0x80, // 100x xxxx + IVAS_E_FMT_REQUEST = 0xA0, // 101x xxxx + IVAS_E_PI_INDICATION = 0xC0, // 110x xxxx + IVAS_E_RESERVED = 0xE0 // 111x xxxx +} IVAS_E_BYTE_INDICATIONS; + +typedef struct +{ + RTPDUMP_HANDLE rtpdump; + RTPDUMP_RTPPACKET rtpPacket; + uint32_t timeoffset_ms; + uint16_t frameIndex; + bool frameFollowing; + PI_DATA_CURRENT_FRAME PIdataCurrentFrame; + PI_DATA_DEPACKER_STATE PIdataDepackerState; +} IVAS_RTPDUMP_DEPACKER; + +typedef struct +{ + RTPDUMP_HANDLE rtpdump; + RTPDUMP_RTPPACKET rtpPacket; + uint32_t timeoffset_ms; + PI_DATA_PACKER piDataPacker; + PI_DATA_PACKER_STATE piDataPackerState; + bool piDataPresent; + int32_t writingIndex; +} IVAS_RTPDUMP_PACKER; + +typedef enum +{ + IVAS_RTPDUMP_DEPACKER_NO_ERROR = 0, + IVAS_RTPDUMP_DEPACKER_EOF = -1, + IVAS_RTPDUMP_DEPACKER_RTPDUMP_ERROR = 1, + IVAS_RTPDUMP_DEPACKER_PAYLOAD_ERROR +} IVAS_RTPDUMP_DEPACKER_ERROR; + +typedef enum +{ + IVAS_RTPDUMP_PACKER_NO_ERROR = 0, + IVAS_RTPDUMP_PACKER_RTPDUMP_ERROR = 1 +} IVAS_RTPDUMP_PACKER_ERROR; + + +/*-----------------------------------------------------------------------* + * ivasPayload_unpackFrame() + * + * Parse a single IVAS frame from the payload. Also read the IVAS payload header + * and parse the PI data associated with the IVAS frame. + *-----------------------------------------------------------------------*/ + +bool ivasPayload_unpackFrame( + char *payload, /* i : RTP payload data */ + uint16_t payloadSizeBytes, /* i : RTP payload size in bytes */ + uint16_t frameIndex, /* i : Index for the data frame in the payload */ + bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ + bool *frameFollowing, /* o : (F) bit in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + unsigned char **frame, /* o : Data frame indicated by frameIndex */ + uint16_t *frameSizeInBits, /* o : Data frame size in bits */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data for frame indicated by frameIndex */ +); + + +/*-----------------------------------------------------------------------* + * IVAS_RTPDUMP_DEPACKER_open() + * + * Open IVAS rtpdump depacker. + *-----------------------------------------------------------------------*/ + +IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_open( + IVAS_RTPDUMP_DEPACKER *self, /* o : IVAS rtpdump depacker handle */ + FILE *file /* i : Input file containing the rtpdump */ +); + + +/*-----------------------------------------------------------------------* + * IVAS_RTPDUMP_DEPACKER_readNextFrame() + * + * Unpack next frame from the RTP packet. If the packet is depleted, read the next RTP packet from the rtpdump. + *-----------------------------------------------------------------------*/ + +IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_readNextFrame( + IVAS_RTPDUMP_DEPACKER *self, /* i/o: IVAS rtpdump depacker handle */ + uint16_t *rtpSequenceNumber, /* o : RTP sequence number of the read packet */ + uint32_t *rtpTimeStamp, /* o : RTP timestamp for the unpacked frame */ + uint32_t *rcvTime_ms, /* o : Time offset in milliseconds for the unpacked frame */ + bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ + uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + unsigned char **frame, /* o : Data frame indicated by frameIndex */ + uint16_t *frameSizeBits /* o : Data frame size in bits */ +); + + +/*-----------------------------------------------------------------------* + * IVAS_RTPDUMP_DEPACKER_close() + * + * Close IVAS rtpdump depacker. + *-----------------------------------------------------------------------*/ + +void IVAS_RTPDUMP_DEPACKER_close( + IVAS_RTPDUMP_DEPACKER *self /* i : IVAS rtpdump depacker handle */ +); + + +/*-----------------------------------------------------------------------* + * IVAS_RTPDUMP_PACKER_open() + * + * Open IVAS rtpdump packer. + *-----------------------------------------------------------------------*/ + +IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_open( + IVAS_RTPDUMP_PACKER **rtpdumpPacker, /* o : IVAS rtpdump packer handle */ + FILE *file /* i : Output file for the rtpdump stream */ +); + + +/*-----------------------------------------------------------------------* + * IVAS_RTPDUMP_PACKER_writeEbytes() + * + * Write E-bytes (CMR and subsequent E-bytes) to the RTP packet. + *-----------------------------------------------------------------------*/ + +void IVAS_RTPDUMP_PACKER_writeEbytes( + IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ + int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet for bidirectional signalling */ + bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS). + * Immersive bitrate CMRs follow A.3.3.3.3.2 in TS26.253. Mono bitrate CMRs follow A.2.2.1.1 in TS26.445. */ + IVAS_BANDWIDTH_REQUEST ivasBandwidthRequest, /* i : Requested bandwidth to be written as subsequent E-byte in the packet for bidirectional signalling */ + IVAS_FORMAT_REQUEST ivasFormatRequest /* i : Requested format to be written as subsequent E-byte in the packet for bidirectional signalling */ +); + + +/*-----------------------------------------------------------------------* + * IVAS_RTPDUMP_PACKER_writeToc() + * + * Write a single ToC byte to the RTP packet. + *-----------------------------------------------------------------------*/ + +void IVAS_RTPDUMP_PACKER_writeToc( + IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ + int32_t frameBitrate, /* i : Bitrate of the written frame */ + bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS). + * Immersive format ToCs follow A.3.3.2 in TS26.253. Mono format ToCs follow A.2.2.1.2 in TS26.445.*/ + bool someFrameFollowing /* i : Flag to indicate if another frame follows this frame in the same RTP packet. */ +); + + +/*-----------------------------------------------------------------------* + * IVAS_RTPDUMP_PACKER_writeNextFrame() + * + * Write IVAS payload header, a single frame data and optional PI data to an RTP packet. + *-----------------------------------------------------------------------*/ + +IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_writeNextFrame( + IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ + const uint16_t *bitStream, /* i : Bitstream containing the data frame to be written */ + int32_t numBits, /* i : Number of bits contained in the data frame */ + int32_t frameBitrate, /* i : Bitrate of the data frame */ + bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS) */ + int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet */ + bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS) */ + IVAS_BANDWIDTH_REQUEST ivasBandwidthRequest, /* i : Requested bandwidth to be written as subsequent E-byte in the packet */ + IVAS_FORMAT_REQUEST ivasFormatRequest /* i : Requested format to be written as subsequent E-byte in the packet */ +); + + +/*-----------------------------------------------------------------------* + * IVAS_RTPDUMP_PACKER_writePIdata() + * + * Write PI data section (PI headers and PI frame data) to the RTP packet. + *-----------------------------------------------------------------------*/ + +void IVAS_RTPDUMP_PACKER_writePIdata( + IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ +); + + +/*-----------------------------------------------------------------------* + * IVAS_RTPDUMP_PACKER_checkPIdisableHeadtrackingState() + * + * Check for disable headtracking input and previously set disable/enable headtracking state. + * Set the current PI headtracking state accordingly. + *-----------------------------------------------------------------------*/ + +void IVAS_RTPDUMP_PACKER_checkPIdisableHeadtrackingState( + IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ +); + + +/*-----------------------------------------------------------------------* + * IVAS_RTPDUMP_PACKER_determinePIpresence() + * + * Check if there is PI data present for the current frame. + *-----------------------------------------------------------------------*/ + +void IVAS_RTPDUMP_PACKER_determinePIpresence( + IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ +); + + +/*-----------------------------------------------------------------------* + * IVAS_RTPDUMP_PACKER_resetPIdata() + * + * Reset PI data packer handle. + *-----------------------------------------------------------------------*/ + +void IVAS_RTPDUMP_PACKER_resetPIdata( + IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ +); + + +/*-----------------------------------------------------------------------* + * IVAS_RTPDUMP_PACKER_close() + * + * Close IVAS rtpdump packer. + *-----------------------------------------------------------------------*/ + +void IVAS_RTPDUMP_PACKER_close( + IVAS_RTPDUMP_PACKER **self /* i : IVAS rtpdump packer handle */ +); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif /* IVAS_RTP_PAYLOAD_H */ diff --git a/lib_util/rtpdump.c b/lib_util/rtpdump.c index 9f87849473..ad2345d236 100644 --- a/lib_util/rtpdump.c +++ b/lib_util/rtpdump.c @@ -264,6 +264,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 b97ec97d83..fa59bc9e6b 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 e6fbd729ea..d3e4a122b3 100644 --- a/readme.txt +++ b/readme.txt @@ -249,6 +249,13 @@ 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. +-disable_headtracking_file : Disable headtracking input file. (1) to disable and (0) to enable headtracking. Only used with rtpdump output. The usage of the "IVAS_dec" program is as follows: @@ -274,9 +281,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 -fec_cfg_file : Optimal channel aware configuration computed by the JBM -- GitLab From 8f8e5ba3be1ea10ee0e88c21d9c7d423c01ba5f8 Mon Sep 17 00:00:00 2001 From: Stefan Doehla Date: Mon, 17 Feb 2025 14:19:10 +0100 Subject: [PATCH 002/100] [fix] merge conflicts --- lib_com/common_api_types.h | 3 --- lib_enc/lib_enc.c | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/lib_com/common_api_types.h b/lib_com/common_api_types.h index ade2a44ab1..443963c364 100644 --- a/lib_com/common_api_types.h +++ b/lib_com/common_api_types.h @@ -213,7 +213,6 @@ typedef struct _IVAS_JBM_TRACE_DATA } IVAS_JBM_TRACE_DATA; -<<<<<<< HEAD #ifdef IVAS_RTPDUMP typedef struct { @@ -236,7 +235,6 @@ typedef struct IVAS_QUATERNION deviceOrientationQuat; } PI_DATA_DEPACKER_STATE; #endif -======= typedef enum _ivas_binaural_renderer_type { IVAS_BIN_RENDERER_TYPE_NONE, @@ -247,7 +245,6 @@ typedef enum _ivas_binaural_renderer_type IVAS_BIN_RENDERER_TYPE_DEFAULT, } IVAS_BIN_RENDERER_TYPE; ->>>>>>> main /*----------------------------------------------------------------------------------* * Split rendering API constants, structures, and enums diff --git a/lib_enc/lib_enc.c b/lib_enc/lib_enc.c index 2cd3bcc3e5..167dcfb7e4 100644 --- a/lib_enc/lib_enc.c +++ b/lib_enc/lib_enc.c @@ -2435,7 +2435,7 @@ bool IVAS_isImmersiveFormat( return false; } return true; - +} #endif #ifdef DEBUGGING -- GitLab From c4ca71cb7cd579354242e3b41b08ece8dff27bc3 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Thu, 27 Feb 2025 16:12:30 +0200 Subject: [PATCH 003/100] Update MSVC project with RTPdump files --- Workspace_msvc/lib_util.vcxproj | 4 ++++ Workspace_msvc/lib_util.vcxproj.filters | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Workspace_msvc/lib_util.vcxproj b/Workspace_msvc/lib_util.vcxproj index bead7110f3..28832eb4f0 100644 --- a/Workspace_msvc/lib_util.vcxproj +++ b/Workspace_msvc/lib_util.vcxproj @@ -108,8 +108,10 @@ + + @@ -137,8 +139,10 @@ + + diff --git a/Workspace_msvc/lib_util.vcxproj.filters b/Workspace_msvc/lib_util.vcxproj.filters index 8fc8082d1a..f3824f1fe4 100644 --- a/Workspace_msvc/lib_util.vcxproj.filters +++ b/Workspace_msvc/lib_util.vcxproj.filters @@ -82,6 +82,12 @@ util_c + + util_c + + + util_c + @@ -171,6 +177,12 @@ util_h + + util_h + + + util_h + -- GitLab From ce724717a4c44abe5007a320bd18d5bfeabd1333 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Tue, 4 Mar 2025 16:47:19 +0200 Subject: [PATCH 004/100] Use 'rb' for opening bitstream file for RTPdump reading --- apps/decoder.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/decoder.c b/apps/decoder.c index b9191c5ed7..1e2de7189e 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -3096,7 +3096,11 @@ static ivas_error decodeVoIP( { case IVAS_DEC_INPUT_FORMAT_RTPDUMP: case IVAS_DEC_INPUT_FORMAT_RTPDUMP_HF: +#ifdef IVAS_RTPDUMP + f_rtpstream = fopen( arg.inputBitstreamFilename, "rb" ); +#else f_rtpstream = fopen( arg.inputBitstreamFilename, "r" ); +#endif if ( f_rtpstream == NULL ) { -- GitLab From 3ad4e261bff94a4dd7831642f063a493e97f58f1 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Tue, 8 Apr 2025 14:19:58 +0300 Subject: [PATCH 005/100] Check also rptdumpPacker presence in if-statement --- apps/encoder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/encoder.c b/apps/encoder.c index 5aff394659..5e975ab21b 100644 --- a/apps/encoder.c +++ b/apps/encoder.c @@ -857,7 +857,7 @@ int main( } #ifdef IVAS_RTPDUMP - if ( rtpdumpPacker->rtpdump ) + if ( rtpdumpPacker && rtpdumpPacker->rtpdump ) { /* scene orientation */ if ( sceneOrientationFileReader ) -- GitLab From fb4b45b83722751467978fc2746e931ef7c61576 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Tue, 8 Apr 2025 16:01:16 +0300 Subject: [PATCH 006/100] Determine bitrate for rtpdump based on the number of encoded bits --- apps/encoder.c | 2 +- lib_util/ivas_rtp_payload.c | 20 ++++++++++++++++++-- lib_util/ivas_rtp_payload.h | 1 - 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/apps/encoder.c b/apps/encoder.c index 5e975ab21b..c0966533e5 100644 --- a/apps/encoder.c +++ b/apps/encoder.c @@ -898,7 +898,7 @@ int main( IVAS_RTPDUMP_PACKER_determinePIpresence( rtpdumpPacker ); /* write rtpdump */ - rtpdumpPackerError = IVAS_RTPDUMP_PACKER_writeNextFrame( rtpdumpPacker, bitStream, numBits, totalBitrate, + rtpdumpPackerError = IVAS_RTPDUMP_PACKER_writeNextFrame( rtpdumpPacker, bitStream, numBits, IVAS_isImmersiveFormat( hIvasEnc ), 0, false, NO_BANDWIDTH_REQUEST, NO_FORMAT_REQUEST ); if ( rtpdumpPackerError != IVAS_RTPDUMP_PACKER_NO_ERROR ) { diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index 9700d47c23..2366c65698 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -58,6 +58,21 @@ static void writeByteToRtp( } +/*-----------------------------------------------------------------------* + * determineBitrate() + * + * Determine bitrate based on the number of encoded bits. + *-----------------------------------------------------------------------*/ + +static void determineBitrate( + const int32_t numBits, /* i : Number of bits contained in the data frame */ + int32_t *frameBitrate /* o : Bitrate of the data frame */ +) +{ + *frameBitrate = numBits * IVAS_NUM_FRAMES_PER_SEC; +} + + /*-----------------------------------------------------------------------* * ivasPayload_parseToc() * @@ -871,7 +886,6 @@ IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_writeNextFrame( IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ const uint16_t *bitStream, /* i : Bitstream containing the data frame to be written */ int32_t numBits, /* i : Number of bits contained in the data frame */ - int32_t frameBitrate, /* i : Bitrate of the data frame */ bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS) */ int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet */ bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS) */ @@ -881,13 +895,15 @@ IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_writeNextFrame( { RTPDUMP_ERROR rtpdumpError; - int32_t i, k; + int32_t i, k, frameBitrate; char tempData; self->writingIndex = self->rtpPacket.headerSize; self->rtpPacket.payloadSize = 0; memset( self->rtpPacket.data, 0, sizeof( self->rtpPacket.data ) ); + determineBitrate( numBits, &frameBitrate ); + /* payload header */ IVAS_RTPDUMP_PACKER_writeEbytes(self, requestedBitrate, requestImmersiveFormatBitrate, ivasBandwidthRequest, ivasFormatRequest ); IVAS_RTPDUMP_PACKER_writeToc( self, frameBitrate, isImmersiveFormat, false ); diff --git a/lib_util/ivas_rtp_payload.h b/lib_util/ivas_rtp_payload.h index 7adf4eb4b2..159e18a384 100644 --- a/lib_util/ivas_rtp_payload.h +++ b/lib_util/ivas_rtp_payload.h @@ -348,7 +348,6 @@ IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_writeNextFrame( IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ const uint16_t *bitStream, /* i : Bitstream containing the data frame to be written */ int32_t numBits, /* i : Number of bits contained in the data frame */ - int32_t frameBitrate, /* i : Bitrate of the data frame */ bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS) */ int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet */ bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS) */ -- GitLab From 6a4215f7faa1156c8b26467538cd9c45a446d257 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Tue, 22 Apr 2025 13:01:23 +0300 Subject: [PATCH 007/100] Add rtpdump tests to self_test parameter file --- scripts/config/self_test.prm | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/scripts/config/self_test.prm b/scripts/config/self_test.prm index 7db62aef58..7dd2d2e301 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 -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 -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 -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 -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, DTX on, BINAURAL out, rtpdump, PI data +../IVAS_cod -rtpdump -scene_orientation testv/headrot.csv -device_orientation testv/headrot_case02_3000_q.csv -dtx -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_DTX_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 -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 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 ../IVAS_cod -stereo -dtx 32000 48 testv/stvST48n.wav bit @@ -1857,6 +1878,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 -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 @@ -2056,3 +2080,7 @@ networkSimulator_g192 ../scripts/dly_error_profiles/dly_error_profile_5.dat bit ../IVAS_cod -ism 4 testv/stvISM1.csv testv/stvISM2.csv testv/stvISM3.csv testv/stvISM4.csv ../scripts/switchPaths/sw_48-32k_10fr.bin 48 testv/stv4ISM48s.wav bit networkSimulator_g192 ../scripts/dly_error_profiles/dly_error_profile_5.dat bit netsimoutput tracefile_sim 2 0 ../IVAS_dec -obj_edit ../scripts/object_edit/combined_edit.txt -Tracefile tracefile_dec -VOIP BINAURAL 48 netsimoutput testv/stv4ISM48s.wav_sw_48-48_BINAURAL_OE_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 -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 -- GitLab From 9b6129e1c6807a5daf55243051ccf9190c625a40 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Tue, 22 Apr 2025 14:32:37 +0300 Subject: [PATCH 008/100] Assign identity vector components separately --- lib_rend/ivas_rotation.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/lib_rend/ivas_rotation.c b/lib_rend/ivas_rotation.c index bc87251a4d..34215225a2 100644 --- a/lib_rend/ivas_rotation.c +++ b/lib_rend/ivas_rotation.c @@ -741,7 +741,10 @@ void ivas_external_orientation_reset( int16_t i; IVAS_QUATERNION identity; - identity = (IVAS_QUATERNION) { .w = 1.0f, .x = 0.0f, .y = 0.0f, .z = 0.0f }; + 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++ ) { @@ -796,14 +799,9 @@ ivas_error ivas_combined_orientation_open( IVAS_VECTOR3 origo; int16_t pos_idx; -#ifdef IVAS_RTPDUMP - identity = (IVAS_QUATERNION) { .w = 1.0f, .x = 0.0f, .y = 0.0f, .z = 0.0f }; - origo = (IVAS_VECTOR3) { .x = 0.0f, .y = 0.0f, .z = 0.0f }; -#else identity.w = 1.0f; identity.x = identity.y = identity.z = 0.0f; origo.x = origo.y = origo.z = 0.0f; -#endif /* Allocate handle */ if ( ( *hCombinedOrientationData = (COMBINED_ORIENTATION_HANDLE) malloc( sizeof( COMBINED_ORIENTATION_DATA ) ) ) == NULL ) @@ -988,14 +986,9 @@ ivas_error combine_external_and_head_orientations( IVAS_QUATERNION identity; IVAS_VECTOR3 origo; -#ifdef IVAS_RTPDUMP - identity = (IVAS_QUATERNION) { .w = 1.0f, .x = 0.0f, .y = 0.0f, .z = 0.0f }; - origo = (IVAS_VECTOR3) { .x = 0.0f, .y = 0.0f, .z = 0.0f }; -#else identity.w = 1.0f; identity.x = identity.y = identity.z = 0.0f; origo.x = origo.y = origo.z = 0.0f; -#endif /* Form combined orientations or return if no data available */ if ( hCombinedOrientationData == NULL ) @@ -1291,12 +1284,8 @@ static void external_target_interpolation( if ( hExtOrientationData->enableExternalOrientation[i - 1] == 0 ) { IVAS_QUATERNION identity; -#ifdef IVAS_RTPDUMP - identity = (IVAS_QUATERNION) { .w = 1.0f, .x = 0.0f, .y = 0.0f, .z = 0.0f }; -#else identity.w = 1.0f; identity.x = identity.y = identity.z = 0.0f; -#endif hCombinedOrientationData->Quaternions_ext_interpolation_start = identity; } else if ( hExtOrientationData->enableExternalOrientation[i - 1] == 2 ) -- GitLab From c1595ad357432c1ffd45205c39360c6a8fa9afef Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 23 Apr 2025 13:32:53 +0300 Subject: [PATCH 009/100] Edit test cases, check also for combined orientation handle when checking orientation input --- lib_dec/ivas_binRenderer_internal.c | 4 ++++ lib_dec/ivas_dirac_dec.c | 4 ++++ lib_dec/ivas_init_dec.c | 4 ++++ lib_dec/ivas_mc_param_dec.c | 4 ++++ lib_dec/ivas_mc_paramupmix_dec.c | 4 ++++ lib_dec/ivas_mct_dec.c | 4 ++++ lib_dec/ivas_output_config.c | 21 ++++++++++++++++++++- scripts/config/self_test.prm | 14 +++++++------- 8 files changed, 51 insertions(+), 8 deletions(-) diff --git a/lib_dec/ivas_binRenderer_internal.c b/lib_dec/ivas_binRenderer_internal.c index 4b2c4c7d07..8258a47c20 100644 --- a/lib_dec/ivas_binRenderer_internal.c +++ b/lib_dec/ivas_binRenderer_internal.c @@ -1028,7 +1028,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 6ce9439d4b..c8136ad4e1 100644 --- a/lib_dec/ivas_dirac_dec.c +++ b/lib_dec/ivas_dirac_dec.c @@ -1999,7 +1999,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 aac6720870..e4fe450a4e 100644 --- a/lib_dec/ivas_init_dec.c +++ b/lib_dec/ivas_init_dec.c @@ -2153,7 +2153,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 048f26e379..49c1b8a033 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 81bd4023a4..3d5353130c 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 4c2ace3908..9bf9c9098e 100644 --- a/lib_dec/ivas_mct_dec.c +++ b/lib_dec/ivas_mct_dec.c @@ -1179,7 +1179,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 305d99ef99..2c6bce7cc1 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/scripts/config/self_test.prm b/scripts/config/self_test.prm index 7dd2d2e301..ebe07a3101 100644 --- a/scripts/config/self_test.prm +++ b/scripts/config/self_test.prm @@ -1573,9 +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, DTX on, BINAURAL out, rtpdump, PI data -../IVAS_cod -rtpdump -scene_orientation testv/headrot.csv -device_orientation testv/headrot_case02_3000_q.csv -dtx -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_DTX_BINAURAL_rtpdump_PIdata.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 -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 @@ -2022,6 +2022,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 -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 -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 @@ -2080,7 +2084,3 @@ networkSimulator_g192 ../scripts/dly_error_profiles/dly_error_profile_5.dat bit ../IVAS_cod -ism 4 testv/stvISM1.csv testv/stvISM2.csv testv/stvISM3.csv testv/stvISM4.csv ../scripts/switchPaths/sw_48-32k_10fr.bin 48 testv/stv4ISM48s.wav bit networkSimulator_g192 ../scripts/dly_error_profiles/dly_error_profile_5.dat bit netsimoutput tracefile_sim 2 0 ../IVAS_dec -obj_edit ../scripts/object_edit/combined_edit.txt -Tracefile tracefile_dec -VOIP BINAURAL 48 netsimoutput testv/stv4ISM48s.wav_sw_48-48_BINAURAL_OE_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 -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 -- GitLab From 360eef8c4cda1219c9df7a9b4dd7072aeff5121f Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 23 Apr 2025 14:47:48 +0300 Subject: [PATCH 010/100] Clang format --- apps/decoder.c | 7 +- apps/encoder.c | 4 +- lib_com/bitstream.c | 2 +- lib_dec/lib_dec.c | 24 +- lib_enc/lib_enc.c | 3 +- lib_rend/ivas_rotation.c | 2 +- lib_util/disable_headtracking_file_reader.c | 16 +- lib_util/disable_headtracking_file_reader.h | 12 +- lib_util/evs_rtp_payload.h | 304 ++++++++++---------- lib_util/ivas_rtp_payload.c | 200 ++++++------- lib_util/ivas_rtp_payload.h | 220 +++++++------- 11 files changed, 401 insertions(+), 393 deletions(-) diff --git a/apps/decoder.c b/apps/decoder.c index 020510f928..61a540e9f2 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -3186,7 +3186,7 @@ static ivas_error decodeVoIP( { auPtr = au; /* might have been set to RTP packet in prev call */ #ifdef IVAS_RTPDUMP - rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_readNextFrame(&rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, &evsIvasModeBit, &bitrateIndex, &ivasIndicatorOrQBit, &auPtr, (uint16_t *) &auSize ); + rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, &evsIvasModeBit, &bitrateIndex, &ivasIndicatorOrQBit, &auPtr, (uint16_t *) &auSize ); #else rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, &isAMRWB_IOmode, &frameTypeIndex, &qBit, &auPtr, (uint16_t *) &auSize ); #endif @@ -3351,7 +3351,7 @@ static ivas_error decodeVoIP( { /* feed the previous read packet into the receiver now */ #ifdef IVAS_RTPDUMP - error = IVAS_DEC_VoIP_FeedFrame(hIvasDec, auPtr, auSize, rtpSequenceNumber, rtpTimeStamp, nextPacketRcvTime_ms, ivasIndicatorOrQBit ); + error = IVAS_DEC_VoIP_FeedFrame( hIvasDec, auPtr, auSize, rtpSequenceNumber, rtpTimeStamp, nextPacketRcvTime_ms, ivasIndicatorOrQBit ); #else error = IVAS_DEC_VoIP_FeedFrame( hIvasDec, auPtr, auSize, rtpSequenceNumber, rtpTimeStamp, nextPacketRcvTime_ms, qBit ); #endif @@ -3400,7 +3400,6 @@ static ivas_error decodeVoIP( /* 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 || rtpdumpDepackerError == IVAS_RTPDUMP_DEPACKER_EOF ) @@ -3618,7 +3617,7 @@ static ivas_error decodeVoIP( } #ifdef IVAS_RTPDUMP - IVAS_DEC_resetExternalOrientations(hIvasDec); + IVAS_DEC_resetExternalOrientations( hIvasDec ); #endif vec_pos_update = ( vec_pos_update + 1 ) % vec_pos_len; diff --git a/apps/encoder.c b/apps/encoder.c index 01f153959c..3d1e160f7d 100644 --- a/apps/encoder.c +++ b/apps/encoder.c @@ -673,9 +673,9 @@ int main( * Open disable headtracking file *------------------------------------------------------------------------------------------*/ - if (arg.disableHeadtrackingFileName != NULL ) + if ( arg.disableHeadtrackingFileName != NULL ) { - if (( error = DisableHeadtrackingFileReader_open(arg.disableHeadtrackingFileName, &disableHeadtrackingFileReader ) ) != IVAS_ERR_OK ) + if ( ( error = DisableHeadtrackingFileReader_open( arg.disableHeadtrackingFileName, &disableHeadtrackingFileReader ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nError: Can't open disable headtracking file %s \n\n", arg.disableHeadtrackingFileName ); goto cleanup; diff --git a/lib_com/bitstream.c b/lib_com/bitstream.c index c281da1a0a..22fbcde10e 100644 --- a/lib_com/bitstream.c +++ b/lib_com/bitstream.c @@ -170,7 +170,7 @@ Word16 rate2EVSmode( * Lookup IVAS mode. Note: FRAME_NO_DATA frames should be looked up with rate2EVSmode(). *-------------------------------------------------------------------*/ Word16 rate2IVASmode( - const Word32 brate /* i : bitrate */ + const Word32 brate /* i : bitrate */ ) { switch ( brate ) diff --git a/lib_dec/lib_dec.c b/lib_dec/lib_dec.c index 786f911ada..072f274506 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -3736,10 +3736,10 @@ ivas_error IVAS_DEC_Flush( *---------------------------------------------------------------------*/ ivas_error IVAS_DEC_parseSinglePIorientation( - IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ - char *orientation, /* i : input PI orientation */ - bool *isOrientationSaved, /* i : flag to indicate if an orientation for this PI type was previously saved */ - IVAS_QUATERNION *savedInvOrientation /* i : previously saved (inverted) orientation for this PI type */ + IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ + char *orientation, /* i : input PI orientation */ + bool *isOrientationSaved, /* i : flag to indicate if an orientation for this PI type was previously saved */ + IVAS_QUATERNION *savedInvOrientation /* i : previously saved (inverted) orientation for this PI type */ ) { int16_t i; @@ -3780,8 +3780,8 @@ ivas_error IVAS_DEC_parseSinglePIorientation( /* 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]); + QuaternionProduct( hIvasDec->st_ivas->hExtOrientationData->Quaternions[i], *savedInvOrientation, + &hIvasDec->st_ivas->hExtOrientationData->Quaternions[i] ); hIvasDec->st_ivas->hExtOrientationData->enableExternalOrientation[i] = true; } hIvasDec->updateOrientation = true; @@ -3797,8 +3797,8 @@ ivas_error IVAS_DEC_parseSinglePIorientation( *---------------------------------------------------------------------*/ void IVAS_DEC_parsePIenableDisableHeadtracking( - IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* i : PI data handle for the current frame */ + IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* i : PI data handle for the current frame */ ) { if ( PIdataCurrentFrame->enableHeadTracking ) @@ -3833,13 +3833,13 @@ ivas_error IVAS_DEC_parsePIdata( ivas_error error = IVAS_ERR_OK; /* scene orientation */ - if (( error = IVAS_DEC_parseSinglePIorientation( hIvasDec, PIdataCurrentFrame->sceneOrientation, &PIdataDepackerState->sceneOrientationSaved, &PIdataDepackerState->sceneOrientationQuat ) ) != IVAS_ERR_OK ) + if ( ( error = IVAS_DEC_parseSinglePIorientation( hIvasDec, PIdataCurrentFrame->sceneOrientation, &PIdataDepackerState->sceneOrientationSaved, &PIdataDepackerState->sceneOrientationQuat ) ) != IVAS_ERR_OK ) { return error; } /* device orientation */ - if (( error = IVAS_DEC_parseSinglePIorientation( hIvasDec, PIdataCurrentFrame->deviceOrientationUncompensated, &PIdataDepackerState->deviceOrientationSaved, &PIdataDepackerState->deviceOrientationQuat ) ) != IVAS_ERR_OK ) + if ( ( error = IVAS_DEC_parseSinglePIorientation( hIvasDec, PIdataCurrentFrame->deviceOrientationUncompensated, &PIdataDepackerState->deviceOrientationSaved, &PIdataDepackerState->deviceOrientationQuat ) ) != IVAS_ERR_OK ) { return error; } @@ -3858,7 +3858,7 @@ ivas_error IVAS_DEC_parsePIdata( *---------------------------------------------------------------------*/ void IVAS_DEC_resetExternalOrientations( - IVAS_DEC_HANDLE hIvasDec /* i/o: IVAS decoder handle */ + IVAS_DEC_HANDLE hIvasDec /* i/o: IVAS decoder handle */ ) { ivas_external_orientation_reset( &( hIvasDec->st_ivas->hExtOrientationData ) ); @@ -3872,7 +3872,7 @@ void IVAS_DEC_resetExternalOrientations( *---------------------------------------------------------------------*/ void IVAS_DEC_resetPIdataCurrentFrame( - PI_DATA_CURRENT_FRAME *piDataCurrentFrame /* i/o: PI data handle for the current frame */ + PI_DATA_CURRENT_FRAME *piDataCurrentFrame /* i/o: PI data handle for the current frame */ ) { resetPIdataCurrentFrame( piDataCurrentFrame ); diff --git a/lib_enc/lib_enc.c b/lib_enc/lib_enc.c index 993e2773fd..b2ea192118 100644 --- a/lib_enc/lib_enc.c +++ b/lib_enc/lib_enc.c @@ -2427,8 +2427,7 @@ static void init_encoder_config( *---------------------------------------------------------------------*/ bool IVAS_isImmersiveFormat( - const IVAS_ENC_HANDLE hIvasEnc -) + const IVAS_ENC_HANDLE hIvasEnc ) { if ( hIvasEnc->st_ivas->hEncoderConfig->ivas_format == MONO_FORMAT ) { diff --git a/lib_rend/ivas_rotation.c b/lib_rend/ivas_rotation.c index 34215225a2..93c0cd0996 100644 --- a/lib_rend/ivas_rotation.c +++ b/lib_rend/ivas_rotation.c @@ -733,7 +733,7 @@ ivas_error ivas_external_orientation_open( *-----------------------------------------------------------------------*/ void ivas_external_orientation_reset( - EXTERNAL_ORIENTATION_HANDLE *hExtOrientationData /* i/o : external orientation handle */ + EXTERNAL_ORIENTATION_HANDLE *hExtOrientationData /* i/o : external orientation handle */ ) { if ( *hExtOrientationData != NULL ) diff --git a/lib_util/disable_headtracking_file_reader.c b/lib_util/disable_headtracking_file_reader.c index 985733dc83..992464a6f9 100644 --- a/lib_util/disable_headtracking_file_reader.c +++ b/lib_util/disable_headtracking_file_reader.c @@ -55,8 +55,8 @@ struct DisableHeadtrackingFileReader *-----------------------------------------------------------------------*/ ivas_error DisableHeadtrackingFileReader_open( - char *filePath, /* i : disable-headtracking input file name */ - DisableHeadtrackingFileReader **fileReader /* o : DisableHeadtrackingFileReader handle */ + char *filePath, /* i : disable-headtracking input file name */ + DisableHeadtrackingFileReader **fileReader /* o : DisableHeadtrackingFileReader handle */ ) { DisableHeadtrackingFileReader *self; @@ -68,7 +68,7 @@ ivas_error DisableHeadtrackingFileReader_open( return IVAS_ERR_FAILED_FILE_OPEN; } - inputFile = fopen(filePath, "r" ); + inputFile = fopen( filePath, "r" ); if ( !inputFile ) { @@ -95,8 +95,8 @@ ivas_error DisableHeadtrackingFileReader_open( *-----------------------------------------------------------------------*/ ivas_error DisableHeadtrackingFileReading( - DisableHeadtrackingFileReader *fileReader, /* i/o: DisableHeadtrackingFileReader handle */ - bool *pHeadTrackingDisabled /* o : enable/disable headtracking data */ + DisableHeadtrackingFileReader *fileReader, /* i/o: DisableHeadtrackingFileReader handle */ + bool *pHeadTrackingDisabled /* o : enable/disable headtracking data */ ) { float disableHeadTrackingFlag; @@ -105,7 +105,7 @@ ivas_error DisableHeadtrackingFileReading( /* Initial values, if they are not read from the file (enable headtracking by default) */ disableHeadTrackingFlag = 0; - read_values = fscanf(fileReader->inputFile, "%f", &disableHeadTrackingFlag ); + read_values = fscanf( fileReader->inputFile, "%f", &disableHeadTrackingFlag ); if ( ( read_values != 1 ) ) { if ( feof( fileReader->inputFile ) ) @@ -132,7 +132,7 @@ ivas_error DisableHeadtrackingFileReading( *-----------------------------------------------------------------------*/ void DisableHeadtrackingFileReader_close( - DisableHeadtrackingFileReader **fileReader /* i/o: DisableHeadtrackingFileReader handle */ + DisableHeadtrackingFileReader **fileReader /* i/o: DisableHeadtrackingFileReader handle */ ) { if ( fileReader == NULL || *fileReader == NULL ) @@ -156,7 +156,7 @@ void DisableHeadtrackingFileReader_close( *-----------------------------------------------------------------------*/ const char *DisableHeadtrackingFileReader_getFilePath( - DisableHeadtrackingFileReader *fileReader /* i/o: DisableHeadtrackingFileReader handle */ + DisableHeadtrackingFileReader *fileReader /* i/o: DisableHeadtrackingFileReader handle */ ) { if ( fileReader == NULL ) diff --git a/lib_util/disable_headtracking_file_reader.h b/lib_util/disable_headtracking_file_reader.h index 4d413b442c..1dda351565 100644 --- a/lib_util/disable_headtracking_file_reader.h +++ b/lib_util/disable_headtracking_file_reader.h @@ -47,8 +47,8 @@ typedef struct DisableHeadtrackingFileReader DisableHeadtrackingFileReader; *-----------------------------------------------------------------------*/ ivas_error DisableHeadtrackingFileReader_open( - char *filePath, /* i : disable-headtracking input file name */ - DisableHeadtrackingFileReader **fileReader /* o : DisableHeadtrackingFileReader handle */ + char *filePath, /* i : disable-headtracking input file name */ + DisableHeadtrackingFileReader **fileReader /* o : DisableHeadtrackingFileReader handle */ ); /*-----------------------------------------------------------------------* @@ -58,8 +58,8 @@ ivas_error DisableHeadtrackingFileReader_open( *-----------------------------------------------------------------------*/ ivas_error DisableHeadtrackingFileReading( - DisableHeadtrackingFileReader *fileReader, /* i/o: DisableHeadtrackingFileReader handle */ - bool *pHeadTrackingDisabled /* o : enable/disable headtracking data */ + DisableHeadtrackingFileReader *fileReader, /* i/o: DisableHeadtrackingFileReader handle */ + bool *pHeadTrackingDisabled /* o : enable/disable headtracking data */ ); /*-----------------------------------------------------------------------* @@ -69,7 +69,7 @@ ivas_error DisableHeadtrackingFileReading( *-----------------------------------------------------------------------*/ void DisableHeadtrackingFileReader_close( - DisableHeadtrackingFileReader **fileReader /* i/o: DisableHeadtrackingFileReader handle */ + DisableHeadtrackingFileReader **fileReader /* i/o: DisableHeadtrackingFileReader handle */ ); /*-----------------------------------------------------------------------* @@ -79,7 +79,7 @@ void DisableHeadtrackingFileReader_close( *-----------------------------------------------------------------------*/ const char *DisableHeadtrackingFileReader_getFilePath( - DisableHeadtrackingFileReader *fileReader /* i/o: DisableHeadtrackingFileReader handle */ + DisableHeadtrackingFileReader *fileReader /* i/o: DisableHeadtrackingFileReader handle */ ); #endif diff --git a/lib_util/evs_rtp_payload.h b/lib_util/evs_rtp_payload.h index 74c4a56a56..490f9560ff 100644 --- a/lib_util/evs_rtp_payload.h +++ b/lib_util/evs_rtp_payload.h @@ -45,161 +45,165 @@ extern "C" { #endif - static const int32_t AMRWB_IOmode2rate[16] = { - 6600, /* AMRWB_IO_6600 */ - 8850, /* AMRWB_IO_8850 */ - 12650, /* AMRWB_IO_1265 */ - 14250, /* AMRWB_IO_1425 */ - 15850, /* AMRWB_IO_1585 */ - 18250, /* AMRWB_IO_1825 */ - 19850, /* AMRWB_IO_1985 */ - 23050, /* AMRWB_IO_2305 */ - 23850, /* AMRWB_IO_2385 */ - 1750, /* AMRWB_IO_SID: SID_1k75 followed by STI bit and CMI bits (A.2.2.1.3) */ - -1, /* AMRWB_IO_FUT1 */ - -1, /* AMRWB_IO_FUT2 */ - -1, /* AMRWB_IO_FUT3 */ - -1, /* AMRWB_IO_FUT4 */ - 0, /* SPEECH_LOST */ - 0 /* NO_DATA */ - }; - - static const int32_t PRIMARYmode2rate[16] = { - 2800, /* PRIMARY_2800 */ - 7200, /* PRIMARY_7200 */ - 8000, /* PRIMARY_8000 */ - 9600, /* PRIMARY_9600 */ - 13200, /* PRIMARY_13200 */ - 16400, /* PRIMARY_16400 */ - 24400, /* PRIMARY_24400 */ - 32000, /* PRIMARY_32000 */ - 48000, /* PRIMARY_48000 */ - 64000, /* PRIMARY_64000 */ - 96000, /* PRIMARY_96000 */ - 128000, /* PRIMARY_128000 */ - 2400, /* PRIMARY_SID */ - -1, /* PRIMARY_FUT1 */ - 0, /* SPEECH_LOST */ - 0 /* NO_DATA */ - }; - - static const uint16_t evsPayloadProtectedSizes[22] = { - 48, - 56, - 136, - 144, - 160, - 184, - 192, - 256, - 264, - 288, - 320, - 328, - 368, - 400, - 464, - 480, - 488, - 640, - 960, - 1280, - 1920, - 2560 - }; - - static const bool evsPayloadProtectedSizes_isAMRWB_IOmode[22] = { - 0, - 0, /* Special case (see clause A.2.1.3) */ - 1, - 0, - 0, - 1, - 0, - 1, - 0, - 1, - 1, - 0, - 1, - 1, - 1, - 1, - 0, - 0, - 0, - 0, - 0, - 0 - }; - - static const uint16_t evsPayloadProtectedSizes_frameTypeIndex[22] = { - 12, /* PRIMARY_SID */ - 0, /* Special case (see clause A.2.1.3) */ - 0, /* AMRWB_IO_6600 */ - 1, /* PRIMARY_7200 */ - 2, /* PRIMARY_8000 */ - 1, /* AMRWB_IO_8850 */ - 3, /* PRIMARY_9600 */ - 2, /* AMRWB_IO_1265 */ - 4, /* PRIMARY_13200 */ - 3, /* AMRWB_IO_1425 */ - 4, /* AMRWB_IO_1585 */ - 5, /* PRIMARY_16400 */ - 5, /* AMRWB_IO_1825 */ - 6, /* AMRWB_IO_1985 */ - 7, /* AMRWB_IO_2305 */ - 8, /* AMRWB_IO_2385 */ - 6, /* PRIMARY_24400 */ - 7, /* PRIMARY_32000 */ - 8, /* PRIMARY_48000 */ - 9, /* PRIMARY_64000 */ - 10, /* PRIMARY_96000 */ - 11 /* PRIMARY_128000 */ - }; - - 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 ); + /* clang-format off */ + +static const int32_t AMRWB_IOmode2rate[16] = { + 6600, /* AMRWB_IO_6600 */ + 8850, /* AMRWB_IO_8850 */ + 12650, /* AMRWB_IO_1265 */ + 14250, /* AMRWB_IO_1425 */ + 15850, /* AMRWB_IO_1585 */ + 18250, /* AMRWB_IO_1825 */ + 19850, /* AMRWB_IO_1985 */ + 23050, /* AMRWB_IO_2305 */ + 23850, /* AMRWB_IO_2385 */ + 1750, /* AMRWB_IO_SID: SID_1k75 followed by STI bit and CMI bits (A.2.2.1.3) */ + -1, /* AMRWB_IO_FUT1 */ + -1, /* AMRWB_IO_FUT2 */ + -1, /* AMRWB_IO_FUT3 */ + -1, /* AMRWB_IO_FUT4 */ + 0, /* SPEECH_LOST */ + 0 /* NO_DATA */ +}; + +static const int32_t PRIMARYmode2rate[16] = { + 2800, /* PRIMARY_2800 */ + 7200, /* PRIMARY_7200 */ + 8000, /* PRIMARY_8000 */ + 9600, /* PRIMARY_9600 */ + 13200, /* PRIMARY_13200 */ + 16400, /* PRIMARY_16400 */ + 24400, /* PRIMARY_24400 */ + 32000, /* PRIMARY_32000 */ + 48000, /* PRIMARY_48000 */ + 64000, /* PRIMARY_64000 */ + 96000, /* PRIMARY_96000 */ + 128000, /* PRIMARY_128000 */ + 2400, /* PRIMARY_SID */ + -1, /* PRIMARY_FUT1 */ + 0, /* SPEECH_LOST */ + 0 /* NO_DATA */ +}; + +static const uint16_t evsPayloadProtectedSizes[22] = { + 48, + 56, + 136, + 144, + 160, + 184, + 192, + 256, + 264, + 288, + 320, + 328, + 368, + 400, + 464, + 480, + 488, + 640, + 960, + 1280, + 1920, + 2560 +}; + +static const bool evsPayloadProtectedSizes_isAMRWB_IOmode[22] = { + 0, + 0, /* Special case (see clause A.2.1.3) */ + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0 +}; + +static const uint16_t evsPayloadProtectedSizes_frameTypeIndex[22] = { + 12, /* PRIMARY_SID */ + 0, /* Special case (see clause A.2.1.3) */ + 0, /* AMRWB_IO_6600 */ + 1, /* PRIMARY_7200 */ + 2, /* PRIMARY_8000 */ + 1, /* AMRWB_IO_8850 */ + 3, /* PRIMARY_9600 */ + 2, /* AMRWB_IO_1265 */ + 4, /* PRIMARY_13200 */ + 3, /* AMRWB_IO_1425 */ + 4, /* AMRWB_IO_1585 */ + 5, /* PRIMARY_16400 */ + 5, /* AMRWB_IO_1825 */ + 6, /* AMRWB_IO_1985 */ + 7, /* AMRWB_IO_2305 */ + 8, /* AMRWB_IO_2385 */ + 6, /* PRIMARY_24400 */ + 7, /* PRIMARY_32000 */ + 8, /* PRIMARY_48000 */ + 9, /* PRIMARY_64000 */ + 10, /* PRIMARY_96000 */ + 11 /* PRIMARY_128000 */ +}; + +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 ); #ifdef IVAS_RTPDUMP - void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, int32_t *bitrate ); +void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, int32_t *bitrate ); #else - static void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, int32_t *bitrate ); +static void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, int32_t *bitrate ); #endif - 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 - { - RTPDUMP_HANDLE rtpdump; - bool hf_only; - RTPDUMP_RTPPACKET rtpPacket; - uint32_t timeoffset_ms; - uint16_t frameIndex; - bool frameFollowing; - } EVS_RTPDUMP_DEPACKER; - - typedef enum - { - EVS_RTPDUMP_DEPACKER_NO_ERROR = 0, - EVS_RTPDUMP_DEPACKER_EOF = -1, - EVS_RTPDUMP_DEPACKER_RTPDUMP_ERROR = 1, - EVS_RTPDUMP_DEPACKER_PAYLOAD_ERROR - } EVS_RTPDUMP_DEPACKER_ERROR; - - EVS_RTPDUMP_DEPACKER_ERROR EVS_RTPDUMP_DEPACKER_open( EVS_RTPDUMP_DEPACKER *self, FILE *file, bool hf_only ); - - EVS_RTPDUMP_DEPACKER_ERROR EVS_RTPDUMP_DEPACKER_readNextFrame( - EVS_RTPDUMP_DEPACKER *self, - uint16_t *rtpSequenceNumber, - uint32_t *rtpTimeStamp, - uint32_t *rcvTime_ms, - bool *isAMRWB_IOmode, - uint16_t *frameTypeIndex, - bool *qBit, - unsigned char **frame, - uint16_t *frameSizeBits ); - - void EVS_RTPDUMP_DEPACKER_close( EVS_RTPDUMP_DEPACKER *self ); +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 +{ + RTPDUMP_HANDLE rtpdump; + bool hf_only; + RTPDUMP_RTPPACKET rtpPacket; + uint32_t timeoffset_ms; + uint16_t frameIndex; + bool frameFollowing; +} EVS_RTPDUMP_DEPACKER; + +typedef enum +{ + EVS_RTPDUMP_DEPACKER_NO_ERROR = 0, + EVS_RTPDUMP_DEPACKER_EOF = -1, + EVS_RTPDUMP_DEPACKER_RTPDUMP_ERROR = 1, + EVS_RTPDUMP_DEPACKER_PAYLOAD_ERROR +} EVS_RTPDUMP_DEPACKER_ERROR; + +EVS_RTPDUMP_DEPACKER_ERROR EVS_RTPDUMP_DEPACKER_open( EVS_RTPDUMP_DEPACKER *self, FILE *file, bool hf_only ); + +EVS_RTPDUMP_DEPACKER_ERROR EVS_RTPDUMP_DEPACKER_readNextFrame( + EVS_RTPDUMP_DEPACKER *self, + uint16_t *rtpSequenceNumber, + uint32_t *rtpTimeStamp, + uint32_t *rcvTime_ms, + bool *isAMRWB_IOmode, + uint16_t *frameTypeIndex, + bool *qBit, + unsigned char **frame, + uint16_t *frameSizeBits ); + +void EVS_RTPDUMP_DEPACKER_close( EVS_RTPDUMP_DEPACKER *self ); + + /* clang-format on */ #ifdef __cplusplus } diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index 2366c65698..132e4d23e2 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -48,8 +48,8 @@ *-----------------------------------------------------------------------*/ static void writeByteToRtp( - IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ - char value /* i : Value to be written to the RTP packet */ + IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ + char value /* i : Value to be written to the RTP packet */ ) { self->rtpPacket.data[self->writingIndex] = value; @@ -65,8 +65,8 @@ static void writeByteToRtp( *-----------------------------------------------------------------------*/ static void determineBitrate( - const int32_t numBits, /* i : Number of bits contained in the data frame */ - int32_t *frameBitrate /* o : Bitrate of the data frame */ + const int32_t numBits, /* i : Number of bits contained in the data frame */ + int32_t *frameBitrate /* o : Bitrate of the data frame */ ) { *frameBitrate = numBits * IVAS_NUM_FRAMES_PER_SEC; @@ -80,20 +80,20 @@ static void determineBitrate( *-----------------------------------------------------------------------*/ static void ivasPayload_parseToc( - uint8_t toc, /* i : ToC byte to be parsed */ - bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ - bool *frameFollowing, /* o : (F) bit in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ - int32_t *bitrate /* o : Bitrate of the data frame indicated in the ToC byte */ + uint8_t toc, /* i : ToC byte to be parsed */ + bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ + bool *frameFollowing, /* o : (F) bit in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + int32_t *bitrate /* o : Bitrate of the data frame indicated in the ToC byte */ ) { bool isIvasToc = ( toc & 0x30 ) == 0x10; // xx01 xxxx if ( !isIvasToc ) { - evsHeaderFullPayload_parseToc(toc, evsIvasModeBit, frameFollowing, bitrateIndex, ivasIndicatorBit, bitrate ); + evsHeaderFullPayload_parseToc( toc, evsIvasModeBit, frameFollowing, bitrateIndex, ivasIndicatorBit, bitrate ); } else { @@ -113,11 +113,11 @@ static void ivasPayload_parseToc( *-----------------------------------------------------------------------*/ static void ivasPayload_parsePItype( - char *PIframeDataBeginning, /* i : Pointer to the beginning of the PI data section in the RTP packet */ - uint16_t PIframeSizeBytes, /* i : PI data frame size in bytes */ - uint16_t parsedPIframeSizeBytes, /* i : Size of previously parsed PI frame data section in bytes */ - uint16_t PIframeType, /* i : Type of the PI data frame */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data handle for the current audio frame */ + char *PIframeDataBeginning, /* i : Pointer to the beginning of the PI data section in the RTP packet */ + uint16_t PIframeSizeBytes, /* i : PI data frame size in bytes */ + uint16_t parsedPIframeSizeBytes, /* i : Size of previously parsed PI frame data section in bytes */ + uint16_t PIframeType, /* i : Type of the PI data frame */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data handle for the current audio frame */ ) { if ( PIframeType == PI_SCENE_ORIENTATION ) @@ -173,8 +173,8 @@ static void ivasPayload_parsePItype( *-----------------------------------------------------------------------*/ static void ivasPayload_PIaddHeaderBytes( - uint16_t PIheaderSizeBytes, /* i : Size of the PI header section in bytes */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data handle for the current audio frame */ + uint16_t PIheaderSizeBytes, /* i : Size of the PI header section in bytes */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data handle for the current audio frame */ ) { if ( PIdataCurrentFrame->sceneOrientation != NULL ) @@ -211,10 +211,10 @@ static void ivasPayload_PIaddHeaderBytes( *-----------------------------------------------------------------------*/ static bool ivasPayload_parsePIdata( - char *PIdataPayload, /* i : Pointer to the beginning of the PI data section in the RTP packet */ - uint16_t PIdataPayloadSizeBytes, /* i : Size of the PI data section in bytes */ - uint16_t frameIndex, /* i : Index to the current audio frame in the RTP packet */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data handle for the current audio frame */ + char *PIdataPayload, /* i : Pointer to the beginning of the PI data section in the RTP packet */ + uint16_t PIdataPayloadSizeBytes, /* i : Size of the PI data section in bytes */ + uint16_t frameIndex, /* i : Index to the current audio frame in the RTP packet */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data handle for the current audio frame */ ) { // TODO(pajunen): can this accidentally include zero padding bits? @@ -261,14 +261,14 @@ static bool ivasPayload_parsePIdata( /* general PI data for all frames */ if ( PIframeMarker == PM_GENERAL ) { - ivasPayload_parsePItype(PIframeDataBeginning, PIframeSizeBytes, totalPIframeSizeBytes, PIframeType, PIdataCurrentFrame ); + ivasPayload_parsePItype( PIframeDataBeginning, PIframeSizeBytes, totalPIframeSizeBytes, PIframeType, PIdataCurrentFrame ); } else { /* current frame */ if ( ( PIframeMarker > 0 ) & ( count_audio == frameIndex ) ) { - ivasPayload_parsePItype(PIframeDataBeginning, PIframeSizeBytes, totalPIframeSizeBytes, PIframeType, PIdataCurrentFrame ); + ivasPayload_parsePItype( PIframeDataBeginning, PIframeSizeBytes, totalPIframeSizeBytes, PIframeType, PIdataCurrentFrame ); } /* increase audio frame counter */ @@ -294,7 +294,7 @@ static bool ivasPayload_parsePIdata( static float ivasPayload_convertFromQ15( uint16_t Q15Value ) { float temp; - temp = (float)( Q15Value & 0x7FFF ) / 0x7FFF; + temp = (float) ( Q15Value & 0x7FFF ) / 0x7FFF; return ( Q15Value & 0x8000 ) ? -temp : temp; } @@ -308,7 +308,7 @@ static float ivasPayload_convertFromQ15( uint16_t Q15Value ) static uint16_t ivasPayload_convertToQ15( float value ) { uint16_t temp; - temp = (uint16_t)( fabsf( value ) * 0x7FFF ); + temp = (uint16_t) ( fabsf( value ) * 0x7FFF ); if ( value >= 0.0f ) { temp = temp & 0x7FFF; @@ -328,8 +328,8 @@ static uint16_t ivasPayload_convertToQ15( float value ) *-----------------------------------------------------------------------*/ static void ivasPayload_convertOrientationToQ15( - IVAS_QUATERNION *orientation, /* i : Input orientation in quaternion to be converted to Q15 */ - IVAS_QUATERNION_Q15 *orientationQ15 /* o : Ouput orientation in Q15 quaternion */ + IVAS_QUATERNION *orientation, /* i : Input orientation in quaternion to be converted to Q15 */ + IVAS_QUATERNION_Q15 *orientationQ15 /* o : Ouput orientation in Q15 quaternion */ ) { orientationQ15->w = ivasPayload_convertToQ15( orientation->w ); @@ -346,12 +346,12 @@ static void ivasPayload_convertOrientationToQ15( *-----------------------------------------------------------------------*/ static float ivasPayload_parseQuaternionComponent( - char **PIorientationData /* i/o: Pointer to the PI data frame containing a single quaternion component */ + char **PIorientationData /* i/o: Pointer to the PI data frame containing a single quaternion component */ ) { uint16_t tempQ15value; - tempQ15value = (uint16_t) **PIorientationData; - tempQ15value = ( tempQ15value << 8 ) | (uint16_t) *( *PIorientationData + 1 ); + tempQ15value = ( uint16_t ) * *PIorientationData; + tempQ15value = ( tempQ15value << 8 ) | ( uint16_t ) * ( *PIorientationData + 1 ); *PIorientationData += 2; return ivasPayload_convertFromQ15( tempQ15value ); } @@ -384,14 +384,14 @@ void resetPIdataCurrentFrame( PI_DATA_CURRENT_FRAME *PIdataCurrentFrame ) *-----------------------------------------------------------------------*/ bool ivasPayload_parseOrientationPIdata( - char *PIorientationData, /* i/o: Pointer to the PI data frame containing a single quaternion orientation */ - IVAS_QUATERNION *q /* o : Parsed quaternion value */ + char *PIorientationData, /* i/o: Pointer to the PI data frame containing a single quaternion orientation */ + IVAS_QUATERNION *q /* o : Parsed quaternion value */ ) { - q->w = ivasPayload_parseQuaternionComponent(&PIorientationData); - q->x = ivasPayload_parseQuaternionComponent(&PIorientationData); - q->y = ivasPayload_parseQuaternionComponent(&PIorientationData); - q->z = ivasPayload_parseQuaternionComponent(&PIorientationData); + q->w = ivasPayload_parseQuaternionComponent( &PIorientationData ); + q->x = ivasPayload_parseQuaternionComponent( &PIorientationData ); + q->y = ivasPayload_parseQuaternionComponent( &PIorientationData ); + q->z = ivasPayload_parseQuaternionComponent( &PIorientationData ); if ( q->w > 1.0f || q->w < -1.0f || q->x > 1.0f || q->x < -1.0f || @@ -405,18 +405,18 @@ bool ivasPayload_parseOrientationPIdata( } bool ivasPayload_unpackFrame( - char *payload, /* i : RTP payload data */ - uint16_t payloadSizeBytes, /* i : RTP payload size in bytes */ - uint16_t frameIndex, /* i : Index for the data frame in the payload */ - bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ - bool *frameFollowing, /* o : (F) bit in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ - unsigned char **frame, /* o : Data frame indicated by frameIndex */ - uint16_t *frameSizeInBits, /* o : Data frame size in bits */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data for frame indicated by frameIndex */ + char *payload, /* i : RTP payload data */ + uint16_t payloadSizeBytes, /* i : RTP payload size in bytes */ + uint16_t frameIndex, /* i : Index for the data frame in the payload */ + bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ + bool *frameFollowing, /* o : (F) bit in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + unsigned char **frame, /* o : Data frame indicated by frameIndex */ + uint16_t *frameSizeInBits, /* o : Data frame size in bits */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data for frame indicated by frameIndex */ ) { bool someEvsIvasModeBit, someFrameFollowing, someIvasIndicatorBit; @@ -436,12 +436,12 @@ bool ivasPayload_unpackFrame( if ( *payload & E_BYTE ) // 1xxx xxxx { PIdataCurrentFrame->PIdataPresent = false; - while (1) + while ( 1 ) { if ( ( *payload & 0xF0 ) == IVAS_E_CMR ) // 1111 xxxx { /* IVAS CMR/E-byte */ - while (1) + while ( 1 ) { /* parse subsequent E-bytes */ ++payload; @@ -547,7 +547,7 @@ bool ivasPayload_unpackFrame( fprintf( stderr, "Error: expected ToC, found CMR/E-byte\n" ); return false; } - ivasPayload_parseToc(*payload, &someEvsIvasModeBit, &someFrameFollowing, &someBitrateIndex, &someIvasIndicatorBit, &bitrate ); + ivasPayload_parseToc( *payload, &someEvsIvasModeBit, &someFrameFollowing, &someBitrateIndex, &someIvasIndicatorBit, &bitrate ); if ( bitrate < 0 ) { fprintf( stderr, "Error: unexpected frameTypeIndex in ToC\n" ); @@ -599,8 +599,8 @@ bool ivasPayload_unpackFrame( } IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_open( - IVAS_RTPDUMP_DEPACKER *self, /* o : IVAS rtpdump depacker handle */ - FILE *file /* i : Input file containing the rtpdump */ + IVAS_RTPDUMP_DEPACKER *self, /* o : IVAS rtpdump depacker handle */ + FILE *file /* i : Input file containing the rtpdump */ ) { RTPDUMP_ERROR rtpdumpError; @@ -617,17 +617,17 @@ IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_open( } IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_readNextFrame( - IVAS_RTPDUMP_DEPACKER *self, /* i/o: IVAS rtpdump depacker handle */ - uint16_t *rtpSequenceNumber, /* o : RTP sequence number of the read packet */ - uint32_t *rtpTimeStamp, /* o : RTP timestamp for the unpacked frame */ - uint32_t *rcvTime_ms, /* o : Time offset in milliseconds for the unpacked frame */ - bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ - uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ - unsigned char **frame, /* o : Data frame indicated by frameIndex */ - uint16_t *frameSizeBits /* o : Data frame size in bits */ + IVAS_RTPDUMP_DEPACKER *self, /* i/o: IVAS rtpdump depacker handle */ + uint16_t *rtpSequenceNumber, /* o : RTP sequence number of the read packet */ + uint32_t *rtpTimeStamp, /* o : RTP timestamp for the unpacked frame */ + uint32_t *rcvTime_ms, /* o : Time offset in milliseconds for the unpacked frame */ + bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ + uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + unsigned char **frame, /* o : Data frame indicated by frameIndex */ + uint16_t *frameSizeBits /* o : Data frame size in bits */ ) { /* read next RTP packet from rtpdump */ @@ -662,7 +662,7 @@ IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_readNextFrame( } void IVAS_RTPDUMP_DEPACKER_close( - IVAS_RTPDUMP_DEPACKER *self /* i : IVAS rtpdump depacker handle */ + IVAS_RTPDUMP_DEPACKER *self /* i : IVAS rtpdump depacker handle */ ) { if ( !self ) @@ -673,8 +673,8 @@ void IVAS_RTPDUMP_DEPACKER_close( } IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_open( - IVAS_RTPDUMP_PACKER **rtpdumpPacker, /* o : IVAS rtpdump packer handle */ - FILE *file /* i : Output file for the rtpdump stream */ + IVAS_RTPDUMP_PACKER **rtpdumpPacker, /* o : IVAS rtpdump packer handle */ + FILE *file /* i : Output file for the rtpdump stream */ ) { IVAS_RTPDUMP_PACKER *self; @@ -698,12 +698,12 @@ IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_open( } void IVAS_RTPDUMP_PACKER_writeEbytes( - IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ - int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet for bidirectional signalling */ - bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS). - * Immersive bitrate CMRs follow A.3.3.3.3.2 in TS26.253. Mono bitrate CMRs follow A.2.2.1.1 in TS26.445. */ - IVAS_BANDWIDTH_REQUEST ivasBandwidthRequest, /* i : Requested bandwidth to be written as subsequent E-byte in the packet for bidirectional signalling */ - IVAS_FORMAT_REQUEST ivasFormatRequest /* i : Requested format to be written as subsequent E-byte in the packet for bidirectional signalling */ + IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ + int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet for bidirectional signalling */ + bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS). + * Immersive bitrate CMRs follow A.3.3.3.3.2 in TS26.253. Mono bitrate CMRs follow A.2.2.1.1 in TS26.445. */ + IVAS_BANDWIDTH_REQUEST ivasBandwidthRequest, /* i : Requested bandwidth to be written as subsequent E-byte in the packet for bidirectional signalling */ + IVAS_FORMAT_REQUEST ivasFormatRequest /* i : Requested format to be written as subsequent E-byte in the packet for bidirectional signalling */ ) { UWord8 initialEbyte, subsequentEbyte; @@ -852,11 +852,11 @@ void IVAS_RTPDUMP_PACKER_writeEbytes( } void IVAS_RTPDUMP_PACKER_writeToc( - IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ - int32_t frameBitrate, /* i : Bitrate of the written frame */ - bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS). - * Immersive format ToCs follow A.3.3.2 in TS26.253. Mono format ToCs follow A.2.2.1.2 in TS26.445.*/ - bool someFrameFollowing /* i : Flag to indicate if another frame follows this frame in the same RTP packet. */ + IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ + int32_t frameBitrate, /* i : Bitrate of the written frame */ + bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS). + * Immersive format ToCs follow A.3.3.2 in TS26.253. Mono format ToCs follow A.2.2.1.2 in TS26.445.*/ + bool someFrameFollowing /* i : Flag to indicate if another frame follows this frame in the same RTP packet. */ ) { UWord8 H_bit, F_bit, EVS_IVAS_mode_bit, Q_bit, toc; @@ -868,13 +868,13 @@ void IVAS_RTPDUMP_PACKER_writeToc( /* FRAME_NO_DATA frames are written as in TS26.445 */ if ( isImmersiveFormat && frameBitrate != FRAME_NO_DATA ) { - toc = (UWord8)( rate2IVASmode( frameBitrate ) ); + toc = (UWord8) ( rate2IVASmode( frameBitrate ) ); EVS_IVAS_mode_bit = 0; Q_bit = 1; } else { - toc = (UWord8)( rate2EVSmode( frameBitrate, &is_amr_wb ) ); + toc = (UWord8) ( rate2EVSmode( frameBitrate, &is_amr_wb ) ); EVS_IVAS_mode_bit = (UWord8) is_amr_wb; Q_bit = 0; } @@ -883,14 +883,14 @@ void IVAS_RTPDUMP_PACKER_writeToc( } IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_writeNextFrame( - IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ - const uint16_t *bitStream, /* i : Bitstream containing the data frame to be written */ - int32_t numBits, /* i : Number of bits contained in the data frame */ - bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS) */ - int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet */ - bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS) */ - IVAS_BANDWIDTH_REQUEST ivasBandwidthRequest, /* i : Requested bandwidth to be written as subsequent E-byte in the packet */ - IVAS_FORMAT_REQUEST ivasFormatRequest /* i : Requested format to be written as subsequent E-byte in the packet */ + IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ + const uint16_t *bitStream, /* i : Bitstream containing the data frame to be written */ + int32_t numBits, /* i : Number of bits contained in the data frame */ + bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS) */ + int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet */ + bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS) */ + IVAS_BANDWIDTH_REQUEST ivasBandwidthRequest, /* i : Requested bandwidth to be written as subsequent E-byte in the packet */ + IVAS_FORMAT_REQUEST ivasFormatRequest /* i : Requested format to be written as subsequent E-byte in the packet */ ) { @@ -905,13 +905,15 @@ IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_writeNextFrame( determineBitrate( numBits, &frameBitrate ); /* payload header */ - IVAS_RTPDUMP_PACKER_writeEbytes(self, requestedBitrate, requestImmersiveFormatBitrate, ivasBandwidthRequest, ivasFormatRequest ); + IVAS_RTPDUMP_PACKER_writeEbytes( self, requestedBitrate, requestImmersiveFormatBitrate, ivasBandwidthRequest, ivasFormatRequest ); IVAS_RTPDUMP_PACKER_writeToc( self, frameBitrate, isImmersiveFormat, false ); /* frame data */ - for (i = 0; i < numBits; i += 8) { + for ( i = 0; i < numBits; i += 8 ) + { tempData = 0; - for (k = 0; k < 8; ++k) { + for ( k = 0; k < 8; ++k ) + { tempData = ( tempData << 1 ) | (char) bitStream[i + k]; } writeByteToRtp( self, tempData ); @@ -936,7 +938,7 @@ IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_writeNextFrame( } void IVAS_RTPDUMP_PACKER_writePIdata( - IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ + IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ ) { if ( !self->piDataPresent ) @@ -1036,7 +1038,7 @@ void IVAS_RTPDUMP_PACKER_writePIdata( } void IVAS_RTPDUMP_PACKER_checkPIdisableHeadtrackingState( - IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ + IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ ) { if ( self->piDataPacker.disableHeadtracking == self->piDataPackerState.previousDisableHeadtrackingState ) @@ -1058,7 +1060,7 @@ void IVAS_RTPDUMP_PACKER_checkPIdisableHeadtrackingState( } void IVAS_RTPDUMP_PACKER_determinePIpresence( - IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ + IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ ) { self->piDataPresent = false; @@ -1072,7 +1074,7 @@ void IVAS_RTPDUMP_PACKER_determinePIpresence( } void IVAS_RTPDUMP_PACKER_resetPIdata( - IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ + IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ ) { self->piDataPresent = false; @@ -1083,7 +1085,7 @@ void IVAS_RTPDUMP_PACKER_resetPIdata( } void IVAS_RTPDUMP_PACKER_close( - IVAS_RTPDUMP_PACKER **self /* i : IVAS rtpdump packer handle */ + IVAS_RTPDUMP_PACKER **self /* i : IVAS rtpdump packer handle */ ) { if ( !self || !( *self ) ) diff --git a/lib_util/ivas_rtp_payload.h b/lib_util/ivas_rtp_payload.h index 159e18a384..101a7805dd 100644 --- a/lib_util/ivas_rtp_payload.h +++ b/lib_util/ivas_rtp_payload.h @@ -48,6 +48,7 @@ extern "C" { #endif + /* clang-format off */ static const int32_t IVASmode2rate[16] = { 13200, @@ -64,8 +65,8 @@ static const int32_t IVASmode2rate[16] = { 256000, 384000, 512000, - -1, /* Reserved */ - 5200 /* SID */ + -1, /* Reserved */ + 5200 /* SID */ }; typedef enum @@ -229,188 +230,191 @@ typedef enum /*-----------------------------------------------------------------------* - * ivasPayload_unpackFrame() - * - * Parse a single IVAS frame from the payload. Also read the IVAS payload header - * and parse the PI data associated with the IVAS frame. - *-----------------------------------------------------------------------*/ + * ivasPayload_unpackFrame() + * + * Parse a single IVAS frame from the payload. Also read the IVAS payload header + * and parse the PI data associated with the IVAS frame. + *-----------------------------------------------------------------------*/ bool ivasPayload_unpackFrame( - char *payload, /* i : RTP payload data */ - uint16_t payloadSizeBytes, /* i : RTP payload size in bytes */ - uint16_t frameIndex, /* i : Index for the data frame in the payload */ - bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ - bool *frameFollowing, /* o : (F) bit in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ - unsigned char **frame, /* o : Data frame indicated by frameIndex */ - uint16_t *frameSizeInBits, /* o : Data frame size in bits */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data for frame indicated by frameIndex */ + char *payload, /* i : RTP payload data */ + uint16_t payloadSizeBytes, /* i : RTP payload size in bytes */ + uint16_t frameIndex, /* i : Index for the data frame in the payload */ + bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ + bool *frameFollowing, /* o : (F) bit in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + unsigned char **frame, /* o : Data frame indicated by frameIndex */ + uint16_t *frameSizeInBits, /* o : Data frame size in bits */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data for frame indicated by frameIndex */ ); /*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_DEPACKER_open() - * - * Open IVAS rtpdump depacker. - *-----------------------------------------------------------------------*/ + * IVAS_RTPDUMP_DEPACKER_open() + * + * Open IVAS rtpdump depacker. + *-----------------------------------------------------------------------*/ IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_open( - IVAS_RTPDUMP_DEPACKER *self, /* o : IVAS rtpdump depacker handle */ - FILE *file /* i : Input file containing the rtpdump */ + IVAS_RTPDUMP_DEPACKER *self, /* o : IVAS rtpdump depacker handle */ + FILE *file /* i : Input file containing the rtpdump */ ); /*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_DEPACKER_readNextFrame() - * - * Unpack next frame from the RTP packet. If the packet is depleted, read the next RTP packet from the rtpdump. - *-----------------------------------------------------------------------*/ + * IVAS_RTPDUMP_DEPACKER_readNextFrame() + * + * Unpack next frame from the RTP packet. If the packet is depleted, read the next RTP packet from the rtpdump. + *-----------------------------------------------------------------------*/ IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_readNextFrame( - IVAS_RTPDUMP_DEPACKER *self, /* i/o: IVAS rtpdump depacker handle */ - uint16_t *rtpSequenceNumber, /* o : RTP sequence number of the read packet */ - uint32_t *rtpTimeStamp, /* o : RTP timestamp for the unpacked frame */ - uint32_t *rcvTime_ms, /* o : Time offset in milliseconds for the unpacked frame */ - bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ - uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ - unsigned char **frame, /* o : Data frame indicated by frameIndex */ - uint16_t *frameSizeBits /* o : Data frame size in bits */ + IVAS_RTPDUMP_DEPACKER *self, /* i/o: IVAS rtpdump depacker handle */ + uint16_t *rtpSequenceNumber, /* o : RTP sequence number of the read packet */ + uint32_t *rtpTimeStamp, /* o : RTP timestamp for the unpacked frame */ + uint32_t *rcvTime_ms, /* o : Time offset in milliseconds for the unpacked frame */ + bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ + uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + unsigned char **frame, /* o : Data frame indicated by frameIndex */ + uint16_t *frameSizeBits /* o : Data frame size in bits */ ); /*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_DEPACKER_close() - * - * Close IVAS rtpdump depacker. - *-----------------------------------------------------------------------*/ + * IVAS_RTPDUMP_DEPACKER_close() + * + * Close IVAS rtpdump depacker. + *-----------------------------------------------------------------------*/ void IVAS_RTPDUMP_DEPACKER_close( - IVAS_RTPDUMP_DEPACKER *self /* i : IVAS rtpdump depacker handle */ + IVAS_RTPDUMP_DEPACKER *self /* i : IVAS rtpdump depacker handle */ ); /*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_open() - * - * Open IVAS rtpdump packer. - *-----------------------------------------------------------------------*/ + * IVAS_RTPDUMP_PACKER_open() + * + * Open IVAS rtpdump packer. + *-----------------------------------------------------------------------*/ IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_open( - IVAS_RTPDUMP_PACKER **rtpdumpPacker, /* o : IVAS rtpdump packer handle */ - FILE *file /* i : Output file for the rtpdump stream */ + IVAS_RTPDUMP_PACKER **rtpdumpPacker, /* o : IVAS rtpdump packer handle */ + FILE *file /* i : Output file for the rtpdump stream */ ); /*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_writeEbytes() - * - * Write E-bytes (CMR and subsequent E-bytes) to the RTP packet. - *-----------------------------------------------------------------------*/ + * IVAS_RTPDUMP_PACKER_writeEbytes() + * + * Write E-bytes (CMR and subsequent E-bytes) to the RTP packet. + *-----------------------------------------------------------------------*/ void IVAS_RTPDUMP_PACKER_writeEbytes( - IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ - int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet for bidirectional signalling */ - bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS). - * Immersive bitrate CMRs follow A.3.3.3.3.2 in TS26.253. Mono bitrate CMRs follow A.2.2.1.1 in TS26.445. */ - IVAS_BANDWIDTH_REQUEST ivasBandwidthRequest, /* i : Requested bandwidth to be written as subsequent E-byte in the packet for bidirectional signalling */ - IVAS_FORMAT_REQUEST ivasFormatRequest /* i : Requested format to be written as subsequent E-byte in the packet for bidirectional signalling */ + IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ + int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet for bidirectional signalling */ + bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS). + * Immersive bitrate CMRs follow A.3.3.3.3.2 in TS26.253. Mono bitrate CMRs follow A.2.2.1.1 in TS26.445. */ + IVAS_BANDWIDTH_REQUEST ivasBandwidthRequest, /* i : Requested bandwidth to be written as subsequent E-byte in the packet for bidirectional signalling */ + IVAS_FORMAT_REQUEST ivasFormatRequest /* i : Requested format to be written as subsequent E-byte in the packet for bidirectional signalling */ ); /*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_writeToc() - * - * Write a single ToC byte to the RTP packet. - *-----------------------------------------------------------------------*/ + * IVAS_RTPDUMP_PACKER_writeToc() + * + * Write a single ToC byte to the RTP packet. + *-----------------------------------------------------------------------*/ void IVAS_RTPDUMP_PACKER_writeToc( - IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ - int32_t frameBitrate, /* i : Bitrate of the written frame */ - bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS). - * Immersive format ToCs follow A.3.3.2 in TS26.253. Mono format ToCs follow A.2.2.1.2 in TS26.445.*/ - bool someFrameFollowing /* i : Flag to indicate if another frame follows this frame in the same RTP packet. */ + IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ + int32_t frameBitrate, /* i : Bitrate of the written frame */ + bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS). + * Immersive format ToCs follow A.3.3.2 in TS26.253. Mono format ToCs follow A.2.2.1.2 in TS26.445.*/ + bool someFrameFollowing /* i : Flag to indicate if another frame follows this frame in the same RTP packet. */ ); /*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_writeNextFrame() - * - * Write IVAS payload header, a single frame data and optional PI data to an RTP packet. - *-----------------------------------------------------------------------*/ + * IVAS_RTPDUMP_PACKER_writeNextFrame() + * + * Write IVAS payload header, a single frame data and optional PI data to an RTP packet. + *-----------------------------------------------------------------------*/ IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_writeNextFrame( - IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ - const uint16_t *bitStream, /* i : Bitstream containing the data frame to be written */ - int32_t numBits, /* i : Number of bits contained in the data frame */ - bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS) */ - int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet */ - bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS) */ - IVAS_BANDWIDTH_REQUEST ivasBandwidthRequest, /* i : Requested bandwidth to be written as subsequent E-byte in the packet */ - IVAS_FORMAT_REQUEST ivasFormatRequest /* i : Requested format to be written as subsequent E-byte in the packet */ + IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ + const uint16_t *bitStream, /* i : Bitstream containing the data frame to be written */ + int32_t numBits, /* i : Number of bits contained in the data frame */ + bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS) */ + int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet */ + bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS) */ + IVAS_BANDWIDTH_REQUEST ivasBandwidthRequest, /* i : Requested bandwidth to be written as subsequent E-byte in the packet */ + IVAS_FORMAT_REQUEST ivasFormatRequest /* i : Requested format to be written as subsequent E-byte in the packet */ ); /*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_writePIdata() - * - * Write PI data section (PI headers and PI frame data) to the RTP packet. - *-----------------------------------------------------------------------*/ + * IVAS_RTPDUMP_PACKER_writePIdata() + * + * Write PI data section (PI headers and PI frame data) to the RTP packet. + *-----------------------------------------------------------------------*/ void IVAS_RTPDUMP_PACKER_writePIdata( - IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ + IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ ); /*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_checkPIdisableHeadtrackingState() - * - * Check for disable headtracking input and previously set disable/enable headtracking state. - * Set the current PI headtracking state accordingly. - *-----------------------------------------------------------------------*/ + * IVAS_RTPDUMP_PACKER_checkPIdisableHeadtrackingState() + * + * Check for disable headtracking input and previously set disable/enable headtracking state. + * Set the current PI headtracking state accordingly. + *-----------------------------------------------------------------------*/ void IVAS_RTPDUMP_PACKER_checkPIdisableHeadtrackingState( - IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ + IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ ); /*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_determinePIpresence() - * - * Check if there is PI data present for the current frame. - *-----------------------------------------------------------------------*/ + * IVAS_RTPDUMP_PACKER_determinePIpresence() + * + * Check if there is PI data present for the current frame. + *-----------------------------------------------------------------------*/ void IVAS_RTPDUMP_PACKER_determinePIpresence( - IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ + IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ ); /*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_resetPIdata() - * - * Reset PI data packer handle. - *-----------------------------------------------------------------------*/ + * IVAS_RTPDUMP_PACKER_resetPIdata() + * + * Reset PI data packer handle. + *-----------------------------------------------------------------------*/ void IVAS_RTPDUMP_PACKER_resetPIdata( - IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ + IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ ); /*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_close() - * - * Close IVAS rtpdump packer. - *-----------------------------------------------------------------------*/ + * IVAS_RTPDUMP_PACKER_close() + * + * Close IVAS rtpdump packer. + *-----------------------------------------------------------------------*/ void IVAS_RTPDUMP_PACKER_close( - IVAS_RTPDUMP_PACKER **self /* i : IVAS rtpdump packer handle */ + IVAS_RTPDUMP_PACKER **self /* i : IVAS rtpdump packer handle */ ); + + /* clang-format on */ + #ifdef __cplusplus } #endif -- GitLab From 5b2068a77deceb65003ac7c03ea62c75bb25450e Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 23 Apr 2025 14:59:26 +0300 Subject: [PATCH 011/100] Revert clang format changes to evs_rtp_payload.h --- lib_util/evs_rtp_payload.h | 304 ++++++++++++++++++------------------- 1 file changed, 150 insertions(+), 154 deletions(-) diff --git a/lib_util/evs_rtp_payload.h b/lib_util/evs_rtp_payload.h index 490f9560ff..74c4a56a56 100644 --- a/lib_util/evs_rtp_payload.h +++ b/lib_util/evs_rtp_payload.h @@ -45,165 +45,161 @@ extern "C" { #endif - /* clang-format off */ - -static const int32_t AMRWB_IOmode2rate[16] = { - 6600, /* AMRWB_IO_6600 */ - 8850, /* AMRWB_IO_8850 */ - 12650, /* AMRWB_IO_1265 */ - 14250, /* AMRWB_IO_1425 */ - 15850, /* AMRWB_IO_1585 */ - 18250, /* AMRWB_IO_1825 */ - 19850, /* AMRWB_IO_1985 */ - 23050, /* AMRWB_IO_2305 */ - 23850, /* AMRWB_IO_2385 */ - 1750, /* AMRWB_IO_SID: SID_1k75 followed by STI bit and CMI bits (A.2.2.1.3) */ - -1, /* AMRWB_IO_FUT1 */ - -1, /* AMRWB_IO_FUT2 */ - -1, /* AMRWB_IO_FUT3 */ - -1, /* AMRWB_IO_FUT4 */ - 0, /* SPEECH_LOST */ - 0 /* NO_DATA */ -}; - -static const int32_t PRIMARYmode2rate[16] = { - 2800, /* PRIMARY_2800 */ - 7200, /* PRIMARY_7200 */ - 8000, /* PRIMARY_8000 */ - 9600, /* PRIMARY_9600 */ - 13200, /* PRIMARY_13200 */ - 16400, /* PRIMARY_16400 */ - 24400, /* PRIMARY_24400 */ - 32000, /* PRIMARY_32000 */ - 48000, /* PRIMARY_48000 */ - 64000, /* PRIMARY_64000 */ - 96000, /* PRIMARY_96000 */ - 128000, /* PRIMARY_128000 */ - 2400, /* PRIMARY_SID */ - -1, /* PRIMARY_FUT1 */ - 0, /* SPEECH_LOST */ - 0 /* NO_DATA */ -}; - -static const uint16_t evsPayloadProtectedSizes[22] = { - 48, - 56, - 136, - 144, - 160, - 184, - 192, - 256, - 264, - 288, - 320, - 328, - 368, - 400, - 464, - 480, - 488, - 640, - 960, - 1280, - 1920, - 2560 -}; - -static const bool evsPayloadProtectedSizes_isAMRWB_IOmode[22] = { - 0, - 0, /* Special case (see clause A.2.1.3) */ - 1, - 0, - 0, - 1, - 0, - 1, - 0, - 1, - 1, - 0, - 1, - 1, - 1, - 1, - 0, - 0, - 0, - 0, - 0, - 0 -}; - -static const uint16_t evsPayloadProtectedSizes_frameTypeIndex[22] = { - 12, /* PRIMARY_SID */ - 0, /* Special case (see clause A.2.1.3) */ - 0, /* AMRWB_IO_6600 */ - 1, /* PRIMARY_7200 */ - 2, /* PRIMARY_8000 */ - 1, /* AMRWB_IO_8850 */ - 3, /* PRIMARY_9600 */ - 2, /* AMRWB_IO_1265 */ - 4, /* PRIMARY_13200 */ - 3, /* AMRWB_IO_1425 */ - 4, /* AMRWB_IO_1585 */ - 5, /* PRIMARY_16400 */ - 5, /* AMRWB_IO_1825 */ - 6, /* AMRWB_IO_1985 */ - 7, /* AMRWB_IO_2305 */ - 8, /* AMRWB_IO_2385 */ - 6, /* PRIMARY_24400 */ - 7, /* PRIMARY_32000 */ - 8, /* PRIMARY_48000 */ - 9, /* PRIMARY_64000 */ - 10, /* PRIMARY_96000 */ - 11 /* PRIMARY_128000 */ -}; - -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 ); + static const int32_t AMRWB_IOmode2rate[16] = { + 6600, /* AMRWB_IO_6600 */ + 8850, /* AMRWB_IO_8850 */ + 12650, /* AMRWB_IO_1265 */ + 14250, /* AMRWB_IO_1425 */ + 15850, /* AMRWB_IO_1585 */ + 18250, /* AMRWB_IO_1825 */ + 19850, /* AMRWB_IO_1985 */ + 23050, /* AMRWB_IO_2305 */ + 23850, /* AMRWB_IO_2385 */ + 1750, /* AMRWB_IO_SID: SID_1k75 followed by STI bit and CMI bits (A.2.2.1.3) */ + -1, /* AMRWB_IO_FUT1 */ + -1, /* AMRWB_IO_FUT2 */ + -1, /* AMRWB_IO_FUT3 */ + -1, /* AMRWB_IO_FUT4 */ + 0, /* SPEECH_LOST */ + 0 /* NO_DATA */ + }; + + static const int32_t PRIMARYmode2rate[16] = { + 2800, /* PRIMARY_2800 */ + 7200, /* PRIMARY_7200 */ + 8000, /* PRIMARY_8000 */ + 9600, /* PRIMARY_9600 */ + 13200, /* PRIMARY_13200 */ + 16400, /* PRIMARY_16400 */ + 24400, /* PRIMARY_24400 */ + 32000, /* PRIMARY_32000 */ + 48000, /* PRIMARY_48000 */ + 64000, /* PRIMARY_64000 */ + 96000, /* PRIMARY_96000 */ + 128000, /* PRIMARY_128000 */ + 2400, /* PRIMARY_SID */ + -1, /* PRIMARY_FUT1 */ + 0, /* SPEECH_LOST */ + 0 /* NO_DATA */ + }; + + static const uint16_t evsPayloadProtectedSizes[22] = { + 48, + 56, + 136, + 144, + 160, + 184, + 192, + 256, + 264, + 288, + 320, + 328, + 368, + 400, + 464, + 480, + 488, + 640, + 960, + 1280, + 1920, + 2560 + }; + + static const bool evsPayloadProtectedSizes_isAMRWB_IOmode[22] = { + 0, + 0, /* Special case (see clause A.2.1.3) */ + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0 + }; + + static const uint16_t evsPayloadProtectedSizes_frameTypeIndex[22] = { + 12, /* PRIMARY_SID */ + 0, /* Special case (see clause A.2.1.3) */ + 0, /* AMRWB_IO_6600 */ + 1, /* PRIMARY_7200 */ + 2, /* PRIMARY_8000 */ + 1, /* AMRWB_IO_8850 */ + 3, /* PRIMARY_9600 */ + 2, /* AMRWB_IO_1265 */ + 4, /* PRIMARY_13200 */ + 3, /* AMRWB_IO_1425 */ + 4, /* AMRWB_IO_1585 */ + 5, /* PRIMARY_16400 */ + 5, /* AMRWB_IO_1825 */ + 6, /* AMRWB_IO_1985 */ + 7, /* AMRWB_IO_2305 */ + 8, /* AMRWB_IO_2385 */ + 6, /* PRIMARY_24400 */ + 7, /* PRIMARY_32000 */ + 8, /* PRIMARY_48000 */ + 9, /* PRIMARY_64000 */ + 10, /* PRIMARY_96000 */ + 11 /* PRIMARY_128000 */ + }; + + 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 ); #ifdef IVAS_RTPDUMP -void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, int32_t *bitrate ); + void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, int32_t *bitrate ); #else -static void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, int32_t *bitrate ); + static void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, int32_t *bitrate ); #endif -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 -{ - RTPDUMP_HANDLE rtpdump; - bool hf_only; - RTPDUMP_RTPPACKET rtpPacket; - uint32_t timeoffset_ms; - uint16_t frameIndex; - bool frameFollowing; -} EVS_RTPDUMP_DEPACKER; - -typedef enum -{ - EVS_RTPDUMP_DEPACKER_NO_ERROR = 0, - EVS_RTPDUMP_DEPACKER_EOF = -1, - EVS_RTPDUMP_DEPACKER_RTPDUMP_ERROR = 1, - EVS_RTPDUMP_DEPACKER_PAYLOAD_ERROR -} EVS_RTPDUMP_DEPACKER_ERROR; - -EVS_RTPDUMP_DEPACKER_ERROR EVS_RTPDUMP_DEPACKER_open( EVS_RTPDUMP_DEPACKER *self, FILE *file, bool hf_only ); - -EVS_RTPDUMP_DEPACKER_ERROR EVS_RTPDUMP_DEPACKER_readNextFrame( - EVS_RTPDUMP_DEPACKER *self, - uint16_t *rtpSequenceNumber, - uint32_t *rtpTimeStamp, - uint32_t *rcvTime_ms, - bool *isAMRWB_IOmode, - uint16_t *frameTypeIndex, - bool *qBit, - unsigned char **frame, - uint16_t *frameSizeBits ); - -void EVS_RTPDUMP_DEPACKER_close( EVS_RTPDUMP_DEPACKER *self ); - - /* clang-format on */ + 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 + { + RTPDUMP_HANDLE rtpdump; + bool hf_only; + RTPDUMP_RTPPACKET rtpPacket; + uint32_t timeoffset_ms; + uint16_t frameIndex; + bool frameFollowing; + } EVS_RTPDUMP_DEPACKER; + + typedef enum + { + EVS_RTPDUMP_DEPACKER_NO_ERROR = 0, + EVS_RTPDUMP_DEPACKER_EOF = -1, + EVS_RTPDUMP_DEPACKER_RTPDUMP_ERROR = 1, + EVS_RTPDUMP_DEPACKER_PAYLOAD_ERROR + } EVS_RTPDUMP_DEPACKER_ERROR; + + EVS_RTPDUMP_DEPACKER_ERROR EVS_RTPDUMP_DEPACKER_open( EVS_RTPDUMP_DEPACKER *self, FILE *file, bool hf_only ); + + EVS_RTPDUMP_DEPACKER_ERROR EVS_RTPDUMP_DEPACKER_readNextFrame( + EVS_RTPDUMP_DEPACKER *self, + uint16_t *rtpSequenceNumber, + uint32_t *rtpTimeStamp, + uint32_t *rcvTime_ms, + bool *isAMRWB_IOmode, + uint16_t *frameTypeIndex, + bool *qBit, + unsigned char **frame, + uint16_t *frameSizeBits ); + + void EVS_RTPDUMP_DEPACKER_close( EVS_RTPDUMP_DEPACKER *self ); #ifdef __cplusplus } -- GitLab From fe17179440d9dc362143313a1cf746fff54d036b Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 23 Apr 2025 15:00:51 +0300 Subject: [PATCH 012/100] Clang format --- lib_util/evs_rtp_payload.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_util/evs_rtp_payload.h b/lib_util/evs_rtp_payload.h index 74c4a56a56..21bb10f3f3 100644 --- a/lib_util/evs_rtp_payload.h +++ b/lib_util/evs_rtp_payload.h @@ -164,7 +164,7 @@ extern "C" #ifdef IVAS_RTPDUMP void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, int32_t *bitrate ); #else - static void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, int32_t *bitrate ); +static void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, int32_t *bitrate ); #endif 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 ); -- GitLab From f40f758fd02d391ea57d205b86b53262b4f3b4fd Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Thu, 24 Apr 2025 11:00:31 +0300 Subject: [PATCH 013/100] Change PI data pointers to variables --- apps/encoder.c | 10 ++++---- lib_util/ivas_rtp_payload.c | 47 ++++++++++++++++++++++++------------- lib_util/ivas_rtp_payload.h | 6 +++-- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/apps/encoder.c b/apps/encoder.c index 3d1e160f7d..13dd477363 100644 --- a/apps/encoder.c +++ b/apps/encoder.c @@ -859,25 +859,23 @@ int main( /* scene orientation */ if ( sceneOrientationFileReader ) { - IVAS_QUATERNION sceneOrientationQuat; - if ( ( error = HeadRotationFileReading( sceneOrientationFileReader, &sceneOrientationQuat, NULL ) ) != IVAS_ERR_OK ) + if ( ( error = HeadRotationFileReading( sceneOrientationFileReader, &rtpdumpPacker->piDataPacker.sceneOrientationQuat, NULL ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nError %s while reading scene orientation from %s\n", IVAS_ENC_GetErrorMessage( error ), RotationFileReader_getFilePath( sceneOrientationFileReader ) ); goto cleanup; } - rtpdumpPacker->piDataPacker.sceneOrientationQuat = &sceneOrientationQuat; + rtpdumpPacker->piDataPacker.sceneOrientationPresent = true; } /* device orientation */ if ( deviceOrientationFileReader ) { - IVAS_QUATERNION deviceOrientationQuat; - if ( ( error = HeadRotationFileReading( deviceOrientationFileReader, &deviceOrientationQuat, NULL ) ) != IVAS_ERR_OK ) + if ( ( error = HeadRotationFileReading( deviceOrientationFileReader, &rtpdumpPacker->piDataPacker.deviceOrientationQuat, NULL ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nError %s while reading device orientation from %s\n", IVAS_ENC_GetErrorMessage( error ), RotationFileReader_getFilePath( deviceOrientationFileReader ) ); goto cleanup; } - rtpdumpPacker->piDataPacker.deviceOrientationQuat = &deviceOrientationQuat; + rtpdumpPacker->piDataPacker.deviceOrientationPresent = true; } /* disable headtracking */ diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index 132e4d23e2..6bac9a066d 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -328,14 +328,14 @@ static uint16_t ivasPayload_convertToQ15( float value ) *-----------------------------------------------------------------------*/ static void ivasPayload_convertOrientationToQ15( - IVAS_QUATERNION *orientation, /* i : Input orientation in quaternion to be converted to Q15 */ + IVAS_QUATERNION orientation, /* i : Input orientation in quaternion to be converted to Q15 */ IVAS_QUATERNION_Q15 *orientationQ15 /* o : Ouput orientation in Q15 quaternion */ ) { - orientationQ15->w = ivasPayload_convertToQ15( orientation->w ); - orientationQ15->x = ivasPayload_convertToQ15( orientation->x ); - orientationQ15->y = ivasPayload_convertToQ15( orientation->y ); - orientationQ15->z = ivasPayload_convertToQ15( orientation->z ); + orientationQ15->w = ivasPayload_convertToQ15( orientation.w ); + orientationQ15->x = ivasPayload_convertToQ15( orientation.x ); + orientationQ15->y = ivasPayload_convertToQ15( orientation.y ); + orientationQ15->z = ivasPayload_convertToQ15( orientation.z ); } @@ -679,6 +679,11 @@ IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_open( { IVAS_RTPDUMP_PACKER *self; RTPDUMP_ERROR rtpdumpError; + IVAS_QUATERNION identity; + identity.w = 1.0f; + identity.x = 0.0f; + identity.y = 0.0f; + identity.z = 0.0f; self = calloc( sizeof( IVAS_RTPDUMP_PACKER ), 1 ); rtpdumpError = RTPDUMP_OpenWithFileToWrite( &self->rtpdump, file ); if ( rtpdumpError != RTPDUMP_NO_ERROR ) @@ -687,8 +692,10 @@ IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_open( } RTPDUMP_SetDefaultRtpPacketHeader( &self->rtpPacket ); memset( self->rtpPacket.data, 0, sizeof( self->rtpPacket.data ) ); - self->piDataPacker.sceneOrientationQuat = NULL; - self->piDataPacker.deviceOrientationQuat = NULL; + self->piDataPacker.sceneOrientationQuat = identity; + self->piDataPacker.deviceOrientationQuat = identity; + self->piDataPacker.sceneOrientationPresent = false; + self->piDataPacker.deviceOrientationPresent = false; self->piDataPacker.enableHeadtracking = false; self->piDataPacker.disableHeadtracking = false; self->piDataPackerState.previousDisableHeadtrackingState = false; @@ -953,9 +960,9 @@ void IVAS_RTPDUMP_PACKER_writePIdata( /* PI header section */ /* scene orientation */ - if ( self->piDataPacker.sceneOrientationQuat ) + if ( self->piDataPacker.sceneOrientationPresent ) { - if ( self->piDataPacker.deviceOrientationQuat || self->piDataPacker.enableHeadtracking || self->piDataPacker.disableHeadtracking ) + if ( self->piDataPacker.deviceOrientationPresent || self->piDataPacker.enableHeadtracking || self->piDataPacker.disableHeadtracking ) { somePIfollowing = true; } @@ -972,7 +979,7 @@ void IVAS_RTPDUMP_PACKER_writePIdata( } /* device orientation */ - if ( self->piDataPacker.deviceOrientationQuat ) + if ( self->piDataPacker.deviceOrientationPresent ) { if ( self->piDataPacker.enableHeadtracking || self->piDataPacker.disableHeadtracking ) { @@ -1010,7 +1017,7 @@ void IVAS_RTPDUMP_PACKER_writePIdata( /* PI frame data section */ - if ( self->piDataPacker.sceneOrientationQuat ) + if ( self->piDataPacker.sceneOrientationPresent ) { ivasPayload_convertOrientationToQ15( self->piDataPacker.sceneOrientationQuat, &sceneOrientationQ15 ); writeByteToRtp( self, (char) ( sceneOrientationQ15.w >> 8 ) ); @@ -1023,7 +1030,7 @@ void IVAS_RTPDUMP_PACKER_writePIdata( writeByteToRtp( self, (char) ( sceneOrientationQ15.z & 0x00FF ) ); } - if ( self->piDataPacker.deviceOrientationQuat ) + if ( self->piDataPacker.deviceOrientationPresent ) { ivasPayload_convertOrientationToQ15( self->piDataPacker.deviceOrientationQuat, &deviceOrientationQ15 ); writeByteToRtp( self, (char) ( deviceOrientationQ15.w >> 8 ) ); @@ -1064,8 +1071,8 @@ void IVAS_RTPDUMP_PACKER_determinePIpresence( ) { self->piDataPresent = false; - if ( self->piDataPacker.sceneOrientationQuat || - self->piDataPacker.deviceOrientationQuat || + if ( self->piDataPacker.sceneOrientationPresent || + self->piDataPacker.deviceOrientationPresent || self->piDataPacker.enableHeadtracking || self->piDataPacker.disableHeadtracking ) { @@ -1077,9 +1084,17 @@ void IVAS_RTPDUMP_PACKER_resetPIdata( IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ ) { + IVAS_QUATERNION identity; + identity.w = 1.0f; + identity.x = 0.0f; + identity.y = 0.0f; + identity.z = 0.0f; + self->piDataPresent = false; - self->piDataPacker.sceneOrientationQuat = NULL; - self->piDataPacker.deviceOrientationQuat = NULL; + self->piDataPacker.sceneOrientationQuat = identity; + self->piDataPacker.deviceOrientationQuat = identity; + self->piDataPacker.sceneOrientationPresent = false; + self->piDataPacker.deviceOrientationPresent = false; self->piDataPacker.enableHeadtracking = false; self->piDataPacker.disableHeadtracking = false; } diff --git a/lib_util/ivas_rtp_payload.h b/lib_util/ivas_rtp_payload.h index 101a7805dd..bbe939e036 100644 --- a/lib_util/ivas_rtp_payload.h +++ b/lib_util/ivas_rtp_payload.h @@ -109,8 +109,10 @@ typedef enum typedef struct { - IVAS_QUATERNION *sceneOrientationQuat; - IVAS_QUATERNION *deviceOrientationQuat; + IVAS_QUATERNION sceneOrientationQuat; + IVAS_QUATERNION deviceOrientationQuat; + bool sceneOrientationPresent; + bool deviceOrientationPresent; bool enableHeadtracking; bool disableHeadtracking; } PI_DATA_PACKER; -- GitLab From cb4ca3744617e8456327b4546429b605c6ff8a89 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Thu, 24 Apr 2025 11:41:04 +0300 Subject: [PATCH 014/100] Clang format --- lib_util/ivas_rtp_payload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index 6bac9a066d..12c37b4d57 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -328,7 +328,7 @@ static uint16_t ivasPayload_convertToQ15( float value ) *-----------------------------------------------------------------------*/ static void ivasPayload_convertOrientationToQ15( - IVAS_QUATERNION orientation, /* i : Input orientation in quaternion to be converted to Q15 */ + IVAS_QUATERNION orientation, /* i : Input orientation in quaternion to be converted to Q15 */ IVAS_QUATERNION_Q15 *orientationQ15 /* o : Ouput orientation in Q15 quaternion */ ) { -- GitLab From aaf31eb2cc9068714ef39a47918062b7ec176656 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Thu, 24 Apr 2025 13:59:43 +0300 Subject: [PATCH 015/100] Fix usan errors --- lib_util/ivas_rtp_payload.c | 2 +- lib_util/rtpdump.c | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index 12c37b4d57..dfa2ffd901 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -921,7 +921,7 @@ IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_writeNextFrame( tempData = 0; for ( k = 0; k < 8; ++k ) { - tempData = ( tempData << 1 ) | (char) bitStream[i + k]; + tempData = (char) ( ( tempData << 1 ) | (char) bitStream[i + k] ); } writeByteToRtp( self, tempData ); } diff --git a/lib_util/rtpdump.c b/lib_util/rtpdump.c index a01bc112a3..2ce8580a86 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; -- GitLab From 02626ffa5e7b885ccc55e8844ac8666b750b33d1 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Thu, 24 Apr 2025 15:31:02 +0300 Subject: [PATCH 016/100] Fix usan errors --- lib_util/ivas_rtp_payload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index dfa2ffd901..3e9fd71532 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -351,7 +351,7 @@ static float ivasPayload_parseQuaternionComponent( { uint16_t tempQ15value; tempQ15value = ( uint16_t ) * *PIorientationData; - tempQ15value = ( tempQ15value << 8 ) | ( uint16_t ) * ( *PIorientationData + 1 ); + tempQ15value = ( uint16_t ) ( ( tempQ15value << 8 ) | ( uint16_t ) * ( *PIorientationData + 1 ) ); *PIorientationData += 2; return ivasPayload_convertFromQ15( tempQ15value ); } -- GitLab From 1b89fe7eb4bbe1694dabe1d40ed5c03d2863314c Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Thu, 24 Apr 2025 15:34:26 +0300 Subject: [PATCH 017/100] Clang format --- lib_util/ivas_rtp_payload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index 3e9fd71532..22d160cebd 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -351,7 +351,7 @@ static float ivasPayload_parseQuaternionComponent( { uint16_t tempQ15value; tempQ15value = ( uint16_t ) * *PIorientationData; - tempQ15value = ( uint16_t ) ( ( tempQ15value << 8 ) | ( uint16_t ) * ( *PIorientationData + 1 ) ); + tempQ15value = (uint16_t) ( ( tempQ15value << 8 ) | ( uint16_t ) * ( *PIorientationData + 1 ) ); *PIorientationData += 2; return ivasPayload_convertFromQ15( tempQ15value ); } -- GitLab From c7da622a031463cd16759d5a8e99285f3c4659a9 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Thu, 12 Jun 2025 15:02:22 +0300 Subject: [PATCH 018/100] Remove disable headtracking reader files from MSVC project --- Workspace_msvc/lib_util.vcxproj | 2 -- Workspace_msvc/lib_util.vcxproj.filters | 6 ------ 2 files changed, 8 deletions(-) diff --git a/Workspace_msvc/lib_util.vcxproj b/Workspace_msvc/lib_util.vcxproj index 28832eb4f0..94561b175a 100644 --- a/Workspace_msvc/lib_util.vcxproj +++ b/Workspace_msvc/lib_util.vcxproj @@ -108,7 +108,6 @@ - @@ -139,7 +138,6 @@ - diff --git a/Workspace_msvc/lib_util.vcxproj.filters b/Workspace_msvc/lib_util.vcxproj.filters index f3824f1fe4..afaedfb5da 100644 --- a/Workspace_msvc/lib_util.vcxproj.filters +++ b/Workspace_msvc/lib_util.vcxproj.filters @@ -82,9 +82,6 @@ util_c - - util_c - util_c @@ -177,9 +174,6 @@ util_h - - util_h - util_h -- GitLab From d8f66c1b4ecc0068970c4395df23da166f0ca08a Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Thu, 12 Jun 2025 15:33:38 +0300 Subject: [PATCH 019/100] Remove enable/disable headtracking PI data --- apps/encoder.c | 58 ------- lib_com/common_api_types.h | 2 - lib_dec/lib_dec.c | 31 ---- lib_dec/lib_dec.h | 5 - lib_rend/ivas_rotation.c | 25 +-- lib_rend/ivas_stat_rend.h | 3 - lib_util/disable_headtracking_file_reader.c | 169 -------------------- lib_util/disable_headtracking_file_reader.h | 86 ---------- lib_util/ivas_rtp_payload.c | 74 +-------- lib_util/ivas_rtp_payload.h | 24 --- readme.txt | 1 - 11 files changed, 5 insertions(+), 473 deletions(-) delete mode 100644 lib_util/disable_headtracking_file_reader.c delete mode 100644 lib_util/disable_headtracking_file_reader.h diff --git a/apps/encoder.c b/apps/encoder.c index 13dd477363..fe444c4d11 100644 --- a/apps/encoder.c +++ b/apps/encoder.c @@ -39,7 +39,6 @@ #include "jbm_file_reader.h" #include "masa_file_reader.h" #ifdef IVAS_RTPDUMP -#include "disable_headtracking_file_reader.h" #include "evs_rtp_payload.h" #include "ivas_rtp_payload.h" #include "rotation_file_reader.h" @@ -156,7 +155,6 @@ typedef struct bool rtpdumpOutput; char *sceneOrientationTrajFileName; char *deviceOrientationTrajFileName; - char *disableHeadtrackingFileName; #endif } EncArguments; @@ -204,7 +202,6 @@ int main( #ifdef IVAS_RTPDUMP RotFileReader *sceneOrientationFileReader = NULL; RotFileReader *deviceOrientationFileReader = NULL; - DisableHeadtrackingFileReader *disableHeadtrackingFileReader = NULL; #endif #ifdef DEBUGGING FILE *f_forcedModeProfile = NULL; @@ -668,19 +665,6 @@ int main( goto cleanup; } } - - /*------------------------------------------------------------------------------------------* - * Open disable headtracking file - *------------------------------------------------------------------------------------------*/ - - if ( arg.disableHeadtrackingFileName != NULL ) - { - if ( ( error = DisableHeadtrackingFileReader_open( arg.disableHeadtrackingFileName, &disableHeadtrackingFileReader ) ) != IVAS_ERR_OK ) - { - fprintf( stderr, "\nError: Can't open disable headtracking file %s \n\n", arg.disableHeadtrackingFileName ); - goto cleanup; - } - } #endif int16_t numSamplesRead = 0; @@ -878,17 +862,6 @@ int main( rtpdumpPacker->piDataPacker.deviceOrientationPresent = true; } - /* disable headtracking */ - if ( disableHeadtrackingFileReader ) - { - if ( ( error = DisableHeadtrackingFileReading( disableHeadtrackingFileReader, &rtpdumpPacker->piDataPacker.disableHeadtracking ) ) != IVAS_ERR_OK ) - { - fprintf( stderr, "\nError %s while reading disable headtracking data from %s\n", IVAS_ENC_GetErrorMessage( error ), DisableHeadtrackingFileReader_getFilePath( disableHeadtrackingFileReader ) ); - goto cleanup; - } - IVAS_RTPDUMP_PACKER_checkPIdisableHeadtrackingState( rtpdumpPacker ); - } - /* PI presence */ IVAS_RTPDUMP_PACKER_determinePIpresence( rtpdumpPacker ); @@ -1007,11 +980,6 @@ cleanup: { RotationFileReader_close( &deviceOrientationFileReader ); } - - if ( disableHeadtrackingFileReader ) - { - DisableHeadtrackingFileReader_close( &disableHeadtrackingFileReader ); - } #endif IVAS_ENC_Close( &hIvasEnc ); @@ -1088,7 +1056,6 @@ static bool parseCmdlIVAS_enc( arg->rtpdumpOutput = false; arg->sceneOrientationTrajFileName = NULL; arg->deviceOrientationTrajFileName = NULL; - arg->disableHeadtrackingFileName = NULL; #endif #ifdef DEBUGGING @@ -1932,24 +1899,6 @@ static bool parseCmdlIVAS_enc( i++; } - /*-----------------------------------------------------------------* - * Disable headtracking file reading - *-----------------------------------------------------------------*/ - - else if ( strcmp( argv_to_upper, "-DISABLE_HEADTRACKING_FILE" ) == 0 ) - { - i++; - if ( argc - i <= 4 || argv[i][0] == '-' ) - { - fprintf( stderr, "Error: Disable headtracking input file name not specified!\n\n" ); - usage_enc(); - return false; - } - - arg->disableHeadtrackingFileName = argv[i]; - i++; - } - #endif /*-----------------------------------------------------------------* * Option not recognized @@ -1975,12 +1924,6 @@ static bool parseCmdlIVAS_enc( usage_enc(); return false; } - if ( arg->disableHeadtrackingFileName != NULL && arg->rtpdumpOutput == false ) - { - fprintf( stderr, "Error: Disable headtracking file reading is only enabled with rtpdump output!\n\n" ); - usage_enc(); - return false; - } #endif /*-----------------------------------------------------------------* @@ -2196,7 +2139,6 @@ static void usage_enc( void ) fprintf( stdout, " EVS RTP Payload Format is used. \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" ); - fprintf( stdout, "-disable_headtracking_file : Disable headtracking input file. (1) to disable and (0) to enable headtracking. Only used with rtpdump output.\n" ); #endif fprintf( stdout, "\n" ); diff --git a/lib_com/common_api_types.h b/lib_com/common_api_types.h index 866229f11c..d711365703 100644 --- a/lib_com/common_api_types.h +++ b/lib_com/common_api_types.h @@ -226,8 +226,6 @@ typedef struct char *acousticEnvironmentId; char *acousticEnvironmentOnlyLateReverb; char *acousticEnvironmentLateReverbAndEarlyReflections; - bool enableHeadTracking; - bool disableHeadTracking; } PI_DATA_CURRENT_FRAME; typedef struct diff --git a/lib_dec/lib_dec.c b/lib_dec/lib_dec.c index aeaf61caa3..0ef490fcc7 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -3784,34 +3784,6 @@ ivas_error IVAS_DEC_parseSinglePIorientation( } -/*---------------------------------------------------------------------* - * IVAS_DEC_parsePIenableDisableHeadtracking( ) - * - * Parse enable/disable headtracking from PI data - *---------------------------------------------------------------------*/ - -void IVAS_DEC_parsePIenableDisableHeadtracking( - IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* i : PI data handle for the current frame */ -) -{ - if ( PIdataCurrentFrame->enableHeadTracking ) - { - if ( hIvasDec->st_ivas->hCombinedOrientationData ) - { - hIvasDec->st_ivas->hCombinedOrientationData->enableHeadTrackingPI = 1; - } - } - if ( PIdataCurrentFrame->disableHeadTracking ) - { - if ( hIvasDec->st_ivas->hCombinedOrientationData ) - { - hIvasDec->st_ivas->hCombinedOrientationData->enableHeadTrackingPI = 0; - } - } -} - - /*---------------------------------------------------------------------* * IVAS_DEC_parsePIdata( ) * @@ -3838,9 +3810,6 @@ ivas_error IVAS_DEC_parsePIdata( return error; } - /* enable/disable headtracking */ - IVAS_DEC_parsePIenableDisableHeadtracking( hIvasDec, PIdataCurrentFrame ); - return error; } diff --git a/lib_dec/lib_dec.h b/lib_dec/lib_dec.h index 2170cd94a7..f71ff96e7c 100644 --- a/lib_dec/lib_dec.h +++ b/lib_dec/lib_dec.h @@ -337,11 +337,6 @@ ivas_error IVAS_DEC_parseSinglePIorientation( IVAS_QUATERNION *savedInvOrientation /* i : previously saved (inverted) orientation for this PI type */ ); -void IVAS_DEC_parsePIenableDisableHeadtracking( - IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* i : PI data handle for the current frame */ -); - ivas_error IVAS_DEC_parsePIdata( IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ PI_DATA_CURRENT_FRAME *PIdataCurrentFrame, /* i : PI data handle for the current frame */ diff --git a/lib_rend/ivas_rotation.c b/lib_rend/ivas_rotation.c index 93c0cd0996..378736e650 100644 --- a/lib_rend/ivas_rotation.c +++ b/lib_rend/ivas_rotation.c @@ -862,9 +862,6 @@ ivas_error ivas_combined_orientation_open( ( *hCombinedOrientationData )->subframe_idx = 0; ( *hCombinedOrientationData )->subframe_size = (int16_t) ( fs / ( FRAMES_PER_SEC * MAX_PARAM_SPATIAL_SUBFRAMES ) ); ( *hCombinedOrientationData )->cur_subframe_samples_rendered = 0; -#ifdef IVAS_RTPDUMP - ( *hCombinedOrientationData )->enableHeadTrackingPI = 1; -#endif return IVAS_ERR_OK; } @@ -1026,24 +1023,10 @@ ivas_error combine_external_and_head_orientations( else if ( hExtOrientationData == NULL && headRotQuaternions != NULL ) { /* Head rotation only */ -#ifdef IVAS_RTPDUMP - if ( hCombinedOrientationData->enableHeadTrackingPI == 1 ) - { -#endif - for ( i = 0; i < hCombinedOrientationData->num_subframes; i++ ) - { - hCombinedOrientationData->Quaternions[i] = headRotQuaternions[i]; - } -#ifdef IVAS_RTPDUMP - } - else + for ( i = 0; i < hCombinedOrientationData->num_subframes; i++ ) { - for ( i = 0; i < hCombinedOrientationData->num_subframes; i++ ) - { - hCombinedOrientationData->Quaternions[i] = identity; - } + hCombinedOrientationData->Quaternions[i] = headRotQuaternions[i]; } -#endif } if ( hExtOrientationData != NULL ) @@ -1121,11 +1104,7 @@ ivas_error combine_external_and_head_orientations( hCombinedOrientationData->isHeadRotationFrozen = 0; } /* Use the most recent head rotation */ -#ifdef IVAS_RTPDUMP - if ( hExtOrientationData->enableHeadRotation[i] == 1 && hCombinedOrientationData->enableHeadTrackingPI == 1 ) -#else if ( hExtOrientationData->enableHeadRotation[i] == 1 ) -#endif { if ( hExtOrientationData->enableExternalOrientation[i] > 0 ) { diff --git a/lib_rend/ivas_stat_rend.h b/lib_rend/ivas_stat_rend.h index 906f5e157d..e990c336f6 100644 --- a/lib_rend/ivas_stat_rend.h +++ b/lib_rend/ivas_stat_rend.h @@ -710,9 +710,6 @@ typedef struct ivas_combined_orientation_struct int16_t cur_subframe_samples_rendered; int16_t subframe_idx_start; int16_t cur_subframe_samples_rendered_start; -#ifdef IVAS_RTPDUMP - int8_t enableHeadTrackingPI; -#endif } COMBINED_ORIENTATION_DATA, *COMBINED_ORIENTATION_HANDLE; /*----------------------------------------------------------------------------------* diff --git a/lib_util/disable_headtracking_file_reader.c b/lib_util/disable_headtracking_file_reader.c deleted file mode 100644 index 992464a6f9..0000000000 --- a/lib_util/disable_headtracking_file_reader.c +++ /dev/null @@ -1,169 +0,0 @@ -/****************************************************************************************************** - - (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. - -*******************************************************************************************************/ - - -#include "disable_headtracking_file_reader.h" -#include -#include -#include -#include "prot.h" - -#ifdef IVAS_RTPDUMP - -struct DisableHeadtrackingFileReader -{ - FILE *inputFile; - int32_t frameCounter; - char *file_path; - bool fileRewind; -}; - - -/*-----------------------------------------------------------------------* - * DisableHeadtrackingFileReader_open() - * - * Allocate and initialize file reader - *-----------------------------------------------------------------------*/ - -ivas_error DisableHeadtrackingFileReader_open( - char *filePath, /* i : disable-headtracking input file name */ - DisableHeadtrackingFileReader **fileReader /* o : DisableHeadtrackingFileReader handle */ -) -{ - DisableHeadtrackingFileReader *self; - FILE *inputFile; - - /* Open trajectory file */ - if ( strlen( filePath ) < 1 ) - { - return IVAS_ERR_FAILED_FILE_OPEN; - } - - inputFile = fopen( filePath, "r" ); - - if ( !inputFile ) - { - return IVAS_ERR_FAILED_FILE_OPEN; - } - - self = calloc( sizeof( DisableHeadtrackingFileReader ), 1 ); - self->inputFile = inputFile; - self->frameCounter = 0; - self->file_path = calloc( sizeof( char ), strlen( filePath ) + 1 ); - strcpy( self->file_path, filePath ); - self->fileRewind = false; - - *fileReader = self; - - return IVAS_ERR_OK; -} - - -/*-----------------------------------------------------------------------* - * DisableHeadtrackingFileReading() - * - * Read values from the input file. (1) to disable and (0) to enable headtracking. - *-----------------------------------------------------------------------*/ - -ivas_error DisableHeadtrackingFileReading( - DisableHeadtrackingFileReader *fileReader, /* i/o: DisableHeadtrackingFileReader handle */ - bool *pHeadTrackingDisabled /* o : enable/disable headtracking data */ -) -{ - float disableHeadTrackingFlag; - int32_t read_values; - - /* Initial values, if they are not read from the file (enable headtracking by default) */ - disableHeadTrackingFlag = 0; - - read_values = fscanf( fileReader->inputFile, "%f", &disableHeadTrackingFlag ); - if ( ( read_values != 1 ) ) - { - if ( feof( fileReader->inputFile ) ) - { - rewind( fileReader->inputFile ); - fileReader->fileRewind = true; - return DisableHeadtrackingFileReading( fileReader, pHeadTrackingDisabled ); - } - return IVAS_ERR_FAILED_FILE_PARSE; - } - - ( fileReader->frameCounter )++; - - *pHeadTrackingDisabled = (int8_t) disableHeadTrackingFlag == 1 ? true : false; - - return IVAS_ERR_OK; -} - - -/*-----------------------------------------------------------------------* - * DisableHeadtrackingFileReader_close() - * - * Deallocates memory for the file reader - *-----------------------------------------------------------------------*/ - -void DisableHeadtrackingFileReader_close( - DisableHeadtrackingFileReader **fileReader /* i/o: DisableHeadtrackingFileReader handle */ -) -{ - if ( fileReader == NULL || *fileReader == NULL ) - { - return; - } - - fclose( ( *fileReader )->inputFile ); - free( ( *fileReader )->file_path ); - free( *fileReader ); - *fileReader = NULL; - - return; -} - - -/*-----------------------------------------------------------------------* - * DisableHeadtrackingFileReader_getFilePath() - * - * - *-----------------------------------------------------------------------*/ - -const char *DisableHeadtrackingFileReader_getFilePath( - DisableHeadtrackingFileReader *fileReader /* i/o: DisableHeadtrackingFileReader handle */ -) -{ - if ( fileReader == NULL ) - { - return NULL; - } - - return fileReader->file_path; -} -#endif diff --git a/lib_util/disable_headtracking_file_reader.h b/lib_util/disable_headtracking_file_reader.h deleted file mode 100644 index 1dda351565..0000000000 --- a/lib_util/disable_headtracking_file_reader.h +++ /dev/null @@ -1,86 +0,0 @@ -/****************************************************************************************************** - - (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. - -*******************************************************************************************************/ - -#ifndef IVAS_DISABLE_HEADTRACKING_FILE_READER_H -#define IVAS_DISABLE_HEADTRACKING_FILE_READER_H - -#include -#include "common_api_types.h" - -#ifdef IVAS_RTPDUMP - -typedef struct DisableHeadtrackingFileReader DisableHeadtrackingFileReader; - -/*-----------------------------------------------------------------------* - * DisableHeadtrackingFileReader_open() - * - * Allocate and initialize reader handle - *-----------------------------------------------------------------------*/ - -ivas_error DisableHeadtrackingFileReader_open( - char *filePath, /* i : disable-headtracking input file name */ - DisableHeadtrackingFileReader **fileReader /* o : DisableHeadtrackingFileReader handle */ -); - -/*-----------------------------------------------------------------------* - * DisableHeadtrackingFileReading() - * - * Read values from the input file. (1) to disable and (0) to enable headtracking. - *-----------------------------------------------------------------------*/ - -ivas_error DisableHeadtrackingFileReading( - DisableHeadtrackingFileReader *fileReader, /* i/o: DisableHeadtrackingFileReader handle */ - bool *pHeadTrackingDisabled /* o : enable/disable headtracking data */ -); - -/*-----------------------------------------------------------------------* - * DisableHeadtrackingFileReader_close() - * - * Deallocates memory for the reader handle - *-----------------------------------------------------------------------*/ - -void DisableHeadtrackingFileReader_close( - DisableHeadtrackingFileReader **fileReader /* i/o: DisableHeadtrackingFileReader handle */ -); - -/*-----------------------------------------------------------------------* - * DisableHeadtrackingFileReader_getFilePath() - * - * - *-----------------------------------------------------------------------*/ - -const char *DisableHeadtrackingFileReader_getFilePath( - DisableHeadtrackingFileReader *fileReader /* i/o: DisableHeadtrackingFileReader handle */ -); -#endif - -#endif /* IVAS_DISABLE_HEADTRACKING_FILE_READER_H */ diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index 22d160cebd..467de66eb9 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -147,14 +147,6 @@ static void ivasPayload_parsePItype( PIdataCurrentFrame->acousticEnvironmentLateReverbAndEarlyReflections = PIframeDataBeginning + parsedPIframeSizeBytes; } } - else if ( PIframeType == PI_ENABLE_HEADTRACKING ) - { - PIdataCurrentFrame->enableHeadTracking = true; - } - else if ( PIframeType == PI_DISABLE_HEADTRACKING ) - { - PIdataCurrentFrame->disableHeadTracking = true; - } else if ( PIframeType == PI_NO_DATA ) { /* no data */ @@ -372,8 +364,6 @@ void resetPIdataCurrentFrame( PI_DATA_CURRENT_FRAME *PIdataCurrentFrame ) PIdataCurrentFrame->acousticEnvironmentId = NULL; PIdataCurrentFrame->acousticEnvironmentOnlyLateReverb = NULL; PIdataCurrentFrame->acousticEnvironmentLateReverbAndEarlyReflections = NULL; - PIdataCurrentFrame->enableHeadTracking = false; - PIdataCurrentFrame->disableHeadTracking = false; } @@ -696,9 +686,6 @@ IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_open( self->piDataPacker.deviceOrientationQuat = identity; self->piDataPacker.sceneOrientationPresent = false; self->piDataPacker.deviceOrientationPresent = false; - self->piDataPacker.enableHeadtracking = false; - self->piDataPacker.disableHeadtracking = false; - self->piDataPackerState.previousDisableHeadtrackingState = false; self->piDataPresent = false; *rtpdumpPacker = self; return IVAS_RTPDUMP_PACKER_NO_ERROR; @@ -962,7 +949,7 @@ void IVAS_RTPDUMP_PACKER_writePIdata( /* scene orientation */ if ( self->piDataPacker.sceneOrientationPresent ) { - if ( self->piDataPacker.deviceOrientationPresent || self->piDataPacker.enableHeadtracking || self->piDataPacker.disableHeadtracking ) + if ( self->piDataPacker.deviceOrientationPresent ) { somePIfollowing = true; } @@ -981,40 +968,11 @@ void IVAS_RTPDUMP_PACKER_writePIdata( /* device orientation */ if ( self->piDataPacker.deviceOrientationPresent ) { - if ( self->piDataPacker.enableHeadtracking || self->piDataPacker.disableHeadtracking ) - { - somePIfollowing = true; - } - else - { - somePIfollowing = false; - } - if ( somePIfollowing ) - { - PIheaderByte = ( PF0 << 7 ) | ( PM_NOT_LAST << 5 ) | PI_DEVICE_ORIENTATION_UNCOMPENSATED; - } - else - { - PIheaderByte = ( PF0 << 7 ) | ( PM_LAST << 5 ) | PI_DEVICE_ORIENTATION_UNCOMPENSATED; - } + PIheaderByte = ( PF0 << 7 ) | ( PM_LAST << 5 ) | PI_DEVICE_ORIENTATION_UNCOMPENSATED; writeByteToRtp( self, (char) PIheaderByte ); writeByteToRtp( self, PI_DEVICE_ORIENTATION_UNCOMPENSATED_SIZE_BYTES ); } - /* enable/disable headtracking */ - if ( self->piDataPacker.enableHeadtracking && !self->piDataPacker.disableHeadtracking ) - { - PIheaderByte = ( PF0 << 7 ) | ( PM_LAST << 5 ) | PI_ENABLE_HEADTRACKING; - writeByteToRtp( self, (char) PIheaderByte ); - writeByteToRtp( self, PI_ENABLE_HEADTRACKING_SIZE_BYTES ); - } - if ( self->piDataPacker.disableHeadtracking ) - { - PIheaderByte = ( PF0 << 7 ) | ( PM_LAST << 5 ) | PI_DISABLE_HEADTRACKING; - writeByteToRtp( self, (char) PIheaderByte ); - writeByteToRtp( self, PI_DISABLE_HEADTRACKING_SIZE_BYTES ); - } - /* PI frame data section */ if ( self->piDataPacker.sceneOrientationPresent ) @@ -1044,37 +1002,13 @@ void IVAS_RTPDUMP_PACKER_writePIdata( } } -void IVAS_RTPDUMP_PACKER_checkPIdisableHeadtrackingState( - IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ -) -{ - if ( self->piDataPacker.disableHeadtracking == self->piDataPackerState.previousDisableHeadtrackingState ) - { - /* same state as before, do not send PI data */ - self->piDataPacker.disableHeadtracking = false; - } - else if ( self->piDataPacker.disableHeadtracking && self->piDataPackerState.previousDisableHeadtrackingState == false ) - { - /* headtracking was enabled, send disable headtracking PI data */ - self->piDataPackerState.previousDisableHeadtrackingState = true; - } - else if ( !self->piDataPacker.disableHeadtracking && self->piDataPackerState.previousDisableHeadtrackingState == true ) - { - /* headtracking was disabled, send enable headtracking PI data */ - self->piDataPackerState.previousDisableHeadtrackingState = false; - self->piDataPacker.enableHeadtracking = true; - } -} - void IVAS_RTPDUMP_PACKER_determinePIpresence( IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ ) { self->piDataPresent = false; if ( self->piDataPacker.sceneOrientationPresent || - self->piDataPacker.deviceOrientationPresent || - self->piDataPacker.enableHeadtracking || - self->piDataPacker.disableHeadtracking ) + self->piDataPacker.deviceOrientationPresent ) { self->piDataPresent = true; } @@ -1095,8 +1029,6 @@ void IVAS_RTPDUMP_PACKER_resetPIdata( self->piDataPacker.deviceOrientationQuat = identity; self->piDataPacker.sceneOrientationPresent = false; self->piDataPacker.deviceOrientationPresent = false; - self->piDataPacker.enableHeadtracking = false; - self->piDataPacker.disableHeadtracking = false; } void IVAS_RTPDUMP_PACKER_close( diff --git a/lib_util/ivas_rtp_payload.h b/lib_util/ivas_rtp_payload.h index bbe939e036..9d8bad958f 100644 --- a/lib_util/ivas_rtp_payload.h +++ b/lib_util/ivas_rtp_payload.h @@ -89,8 +89,6 @@ typedef enum PI_DEVICE_ORIENTATION_COMPENSATED = 1, PI_DEVICE_ORIENTATION_UNCOMPENSATED = 2, PI_ACOUSTIC_ENVIRONMENT = 3, - PI_ENABLE_HEADTRACKING = 4, - PI_DISABLE_HEADTRACKING = 5, PI_NO_DATA = 31 } PI_TYPE; @@ -102,8 +100,6 @@ typedef enum PI_ACOUSTIC_ENVIRONMENT_ID_SIZE_BYTES = 1, PI_ACOUSTIC_ENVIRONMENT_ONLY_LATE_REVERB_SIZE_BYTES = 5, PI_ACOUSTIC_ENVIRONMENT_LATE_REVERB_AND_EARLY_REFLECTIONS_SIZE_BYTES = 8, - PI_ENABLE_HEADTRACKING_SIZE_BYTES = 0, - PI_DISABLE_HEADTRACKING_SIZE_BYTES = 0, PI_NO_DATA_SIZE_BYTES = 0 } PI_SIZE_BYTES; @@ -113,15 +109,8 @@ typedef struct IVAS_QUATERNION deviceOrientationQuat; bool sceneOrientationPresent; bool deviceOrientationPresent; - bool enableHeadtracking; - bool disableHeadtracking; } PI_DATA_PACKER; -typedef struct -{ - bool previousDisableHeadtrackingState; -} PI_DATA_PACKER_STATE; - typedef struct { uint16_t w, x, y, z; @@ -211,7 +200,6 @@ typedef struct RTPDUMP_RTPPACKET rtpPacket; uint32_t timeoffset_ms; PI_DATA_PACKER piDataPacker; - PI_DATA_PACKER_STATE piDataPackerState; bool piDataPresent; int32_t writingIndex; } IVAS_RTPDUMP_PACKER; @@ -370,18 +358,6 @@ void IVAS_RTPDUMP_PACKER_writePIdata( ); -/*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_checkPIdisableHeadtrackingState() - * - * Check for disable headtracking input and previously set disable/enable headtracking state. - * Set the current PI headtracking state accordingly. - *-----------------------------------------------------------------------*/ - -void IVAS_RTPDUMP_PACKER_checkPIdisableHeadtrackingState( - IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ -); - - /*-----------------------------------------------------------------------* * IVAS_RTPDUMP_PACKER_determinePIpresence() * diff --git a/readme.txt b/readme.txt index a4bdba47c7..d9a8e5b414 100644 --- a/readme.txt +++ b/readme.txt @@ -255,7 +255,6 @@ EVS mono is default, for IVAS choose one of the following: -stereo, -ism, -sba, 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. --disable_headtracking_file : Disable headtracking input file. (1) to disable and (0) to enable headtracking. Only used with rtpdump output. The usage of the "IVAS_dec" program is as follows: -- GitLab From 290e40eac6ba866e0df9458371b1f01dbbbe6eb3 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Fri, 13 Jun 2025 14:04:59 +0300 Subject: [PATCH 020/100] Restructure code, move PI data parsing to utils from lib_dec --- apps/decoder.c | 7 +- lib_com/ivas_prot.h | 9 -- lib_dec/lib_dec.c | 58 +++------ lib_dec/lib_dec.h | 14 +- lib_util/ivas_rtp_payload.c | 252 +++++++++++++++++++++--------------- lib_util/ivas_rtp_payload.h | 3 +- 6 files changed, 171 insertions(+), 172 deletions(-) diff --git a/apps/decoder.c b/apps/decoder.c index 61f72dd493..cb2e08233f 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -3384,14 +3384,13 @@ static ivas_error decodeVoIP( /* IVAS RTP payload format has timescale 16000, JBM uses 1000 internally */ rtpTimeStamp = rtpTimeStamp / 16; - /* parse PI data */ - error = IVAS_DEC_parsePIdata( hIvasDec, &rtpdumpDepacker.PIdataCurrentFrame, &rtpdumpDepacker.PIdataDepackerState ); + /* feed PI data to decoder handle */ + error = IVAS_DEC_feedPIdata( hIvasDec, &rtpdumpDepacker.PIdataDepackerState ); if ( error != IVAS_ERR_OK ) { - fprintf( stderr, "\nError in IVAS_DEC_parsePIdata: %s\n", IVAS_DEC_GetErrorMessage( error ) ); + fprintf( stderr, "\nError in IVAS_DEC_feedPIdata: %s\n", IVAS_DEC_GetErrorMessage( error ) ); goto cleanup; } - IVAS_DEC_resetPIdataCurrentFrame( &rtpdumpDepacker.PIdataCurrentFrame ); #else rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, diff --git a/lib_com/ivas_prot.h b/lib_com/ivas_prot.h index 983f75e8f0..80232e5800 100755 --- a/lib_com/ivas_prot.h +++ b/lib_com/ivas_prot.h @@ -821,15 +821,6 @@ void QuaternionInverse( IVAS_QUATERNION *const r ); -void resetPIdataCurrentFrame( - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data handle for the current audio frame */ -); - -bool ivasPayload_parseOrientationPIdata( - char *PIorientationData, /* i/o: Pointer to the PI data frame containing a single quaternion orientation */ - IVAS_QUATERNION *q /* o : Parsed quaternion value */ -); - #endif /*----------------------------------------------------------------------------------* * JBM prototypes diff --git a/lib_dec/lib_dec.c b/lib_dec/lib_dec.c index 0ef490fcc7..d3ada6dd9c 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -3723,24 +3723,23 @@ ivas_error IVAS_DEC_Flush( #ifdef IVAS_RTPDUMP /*---------------------------------------------------------------------* - * IVAS_DEC_parseSinglePIorientation( ) + * IVAS_DEC_feedSinglePIorientation( ) * - * Parse a single orientation from PI data and feed to external orientation handle. - * Save the parsed (inverted) orientation value and use in processing in future frames, if no new PI orientation is received. + * Feed a single orientation PI data to external orientation handle. *---------------------------------------------------------------------*/ -ivas_error IVAS_DEC_parseSinglePIorientation( +ivas_error IVAS_DEC_feedSinglePIorientation( IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ - char *orientation, /* i : input PI orientation */ - bool *isOrientationSaved, /* i : flag to indicate if an orientation for this PI type was previously saved */ - IVAS_QUATERNION *savedInvOrientation /* i : previously saved (inverted) orientation for this PI type */ + const 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; - if ( orientation ) + IVAS_QUATERNION savedInvOrientation; + + if ( *isOrientationSaved ) { - IVAS_QUATERNION orientationQuat, orientationQuatInv; if ( !hIvasDec->st_ivas->hExtOrientationData ) { if ( ( error = ivas_external_orientation_open( &( hIvasDec->st_ivas->hExtOrientationData ), hIvasDec->st_ivas->hDecoderConfig->render_framesize ) ) != IVAS_ERR_OK ) @@ -3757,24 +3756,12 @@ ivas_error IVAS_DEC_parseSinglePIorientation( } } - /* read the orientation PI value */ - if ( !ivasPayload_parseOrientationPIdata( orientation, &orientationQuat ) ) - { - return IVAS_ERROR( IVAS_ERR_INTERNAL, "Parsing PI orientation failed" ); - } - QuaternionInverse( orientationQuat, &orientationQuatInv ); + QuaternionInverse( *savedOrientation, &savedInvOrientation ); - /* update the saved orientation value */ - *isOrientationSaved = true; - *savedInvOrientation = orientationQuatInv; - } - - if ( *isOrientationSaved ) - { /* 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, + QuaternionProduct( hIvasDec->st_ivas->hExtOrientationData->Quaternions[i], savedInvOrientation, &hIvasDec->st_ivas->hExtOrientationData->Quaternions[i] ); hIvasDec->st_ivas->hExtOrientationData->enableExternalOrientation[i] = true; } @@ -3785,27 +3772,26 @@ ivas_error IVAS_DEC_parseSinglePIorientation( /*---------------------------------------------------------------------* - * IVAS_DEC_parsePIdata( ) + * IVAS_DEC_feedPIdata( ) * - * Parse PI data for a single frame + * Feed PI data to the decoder handle *---------------------------------------------------------------------*/ -ivas_error IVAS_DEC_parsePIdata( +ivas_error IVAS_DEC_feedPIdata( IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame, /* i : PI data handle for the current frame */ 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_parseSinglePIorientation( hIvasDec, PIdataCurrentFrame->sceneOrientation, &PIdataDepackerState->sceneOrientationSaved, &PIdataDepackerState->sceneOrientationQuat ) ) != IVAS_ERR_OK ) + if ( ( error = IVAS_DEC_feedSinglePIorientation( hIvasDec, &PIdataDepackerState->sceneOrientationSaved, &PIdataDepackerState->sceneOrientationQuat ) ) != IVAS_ERR_OK ) { return error; } /* device orientation */ - if ( ( error = IVAS_DEC_parseSinglePIorientation( hIvasDec, PIdataCurrentFrame->deviceOrientationUncompensated, &PIdataDepackerState->deviceOrientationSaved, &PIdataDepackerState->deviceOrientationQuat ) ) != IVAS_ERR_OK ) + if ( ( error = IVAS_DEC_feedSinglePIorientation( hIvasDec, &PIdataDepackerState->deviceOrientationSaved, &PIdataDepackerState->deviceOrientationQuat ) ) != IVAS_ERR_OK ) { return error; } @@ -3828,20 +3814,6 @@ void IVAS_DEC_resetExternalOrientations( } -/*---------------------------------------------------------------------* - * IVAS_DEC_resetPIdataCurrentFrame( ) - * - * Reset current PI frame struct - *---------------------------------------------------------------------*/ - -void IVAS_DEC_resetPIdataCurrentFrame( - PI_DATA_CURRENT_FRAME *piDataCurrentFrame /* i/o: PI data handle for the current frame */ -) -{ - resetPIdataCurrentFrame( piDataCurrentFrame ); -} - - #endif /*---------------------------------------------------------------------* * IVAS_DEC_VoIP_IsEmpty( ) diff --git a/lib_dec/lib_dec.h b/lib_dec/lib_dec.h index f71ff96e7c..f20d6a9eb2 100644 --- a/lib_dec/lib_dec.h +++ b/lib_dec/lib_dec.h @@ -330,16 +330,14 @@ ivas_error IVAS_DEC_Flush( ); #ifdef IVAS_RTPDUMP -ivas_error IVAS_DEC_parseSinglePIorientation( +ivas_error IVAS_DEC_feedSinglePIorientation( IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ - char *orientation, /* i : input PI orientation */ - bool *isOrientationSaved, /* i : flag to indicate if an orientation for this PI type was previously saved */ - IVAS_QUATERNION *savedInvOrientation /* i : previously saved (inverted) orientation for this PI type */ + const 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_parsePIdata( +ivas_error IVAS_DEC_feedPIdata( IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame, /* i : PI data handle for the current frame */ PI_DATA_DEPACKER_STATE *PIdataDepackerState /* i/o: data handle for the PI depacker state */ ); @@ -347,10 +345,6 @@ void IVAS_DEC_resetExternalOrientations( IVAS_DEC_HANDLE hIvasDec /* i/o: IVAS decoder handle */ ); -void IVAS_DEC_resetPIdataCurrentFrame( - PI_DATA_CURRENT_FRAME *piDataCurrentFrame /* i/o: PI data handle for the current frame */ -); - #endif /* Setter functions - apply changes to decoder configuration */ diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index 467de66eb9..e9d65ea0b1 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -196,6 +196,133 @@ static void ivasPayload_PIaddHeaderBytes( } +/*-----------------------------------------------------------------------* + * ivasPayload_convertFromQ15() + * + * Convert a Q15 encoded value to a float value. + *-----------------------------------------------------------------------*/ + +static float ivasPayload_convertFromQ15( uint16_t Q15Value ) +{ + float temp; + temp = (float) ( Q15Value & 0x7FFF ) / 0x7FFF; + return ( Q15Value & 0x8000 ) ? -temp : temp; +} + + +/*-----------------------------------------------------------------------* + * ivasPayload_convertToQ15() + * + * Convert a float value into a Q15 encoded value. + *-----------------------------------------------------------------------*/ + +static uint16_t ivasPayload_convertToQ15( float value ) +{ + uint16_t temp; + temp = (uint16_t) ( fabsf( value ) * 0x7FFF ); + if ( value >= 0.0f ) + { + temp = temp & 0x7FFF; + } + else + { + temp = temp | 0x8000; + } + return temp; +} + + +/*-----------------------------------------------------------------------* + * ivasPayload_convertOrientationToQ15() + * + * Convert a float quaternion into a Q15 encoded quaternion. + *-----------------------------------------------------------------------*/ + +static void ivasPayload_convertOrientationToQ15( + IVAS_QUATERNION orientation, /* i : Input orientation in quaternion to be converted to Q15 */ + IVAS_QUATERNION_Q15 *orientationQ15 /* o : Ouput orientation in Q15 quaternion */ +) +{ + orientationQ15->w = ivasPayload_convertToQ15( orientation.w ); + orientationQ15->x = ivasPayload_convertToQ15( orientation.x ); + orientationQ15->y = ivasPayload_convertToQ15( orientation.y ); + orientationQ15->z = ivasPayload_convertToQ15( orientation.z ); +} + + +/*-----------------------------------------------------------------------* + * ivasPayload_parseQuaternionValue() + * + * Parse a single Q15 encoded quaternion component from the RTP packet and convert it to float. + *-----------------------------------------------------------------------*/ + +static float ivasPayload_parseQuaternionComponent( + char **PIorientationData /* i/o: Pointer to the PI data frame containing a single quaternion component */ +) +{ + uint16_t tempQ15value; + tempQ15value = ( uint16_t ) * *PIorientationData; + tempQ15value = (uint16_t) ( ( tempQ15value << 8 ) | ( uint16_t ) * ( *PIorientationData + 1 ) ); + *PIorientationData += 2; + return ivasPayload_convertFromQ15( tempQ15value ); +} + + +/*-----------------------------------------------------------------------* + * ivasPayload_parseOrientationPIdata() + * + * Parse a single orientation in quaternions from the PI data. + *-----------------------------------------------------------------------*/ + +static bool ivasPayload_parseOrientationPIdata( + char *PIorientationData, /* i/o: Pointer to the PI data frame containing a single quaternion orientation */ + IVAS_QUATERNION *q /* o : Parsed quaternion value */ +) +{ + q->w = ivasPayload_parseQuaternionComponent( &PIorientationData ); + q->x = ivasPayload_parseQuaternionComponent( &PIorientationData ); + q->y = ivasPayload_parseQuaternionComponent( &PIorientationData ); + q->z = ivasPayload_parseQuaternionComponent( &PIorientationData ); + + if ( q->w > 1.0f || q->w < -1.0f || + q->x > 1.0f || q->x < -1.0f || + q->y > 1.0f || q->y < -1.0f || + q->z > 1.0f || q->z < -1.0f ) + { + fprintf( stderr, "Error: invalid orientation PI data\n" ); + return false; + } + return true; +} + + +/*---------------------------------------------------------------------* + * ivasPayload_parseSinglePIorientation( ) + * + * Parse a single orientation from PI data. + * Save the parsed orientation value and use in processing in future frames, if no new PI orientation is received. + *---------------------------------------------------------------------*/ + +static bool ivasPayload_parseSinglePIorientation( + char *orientation, /* i : input PI orientation */ + bool *isOrientationSaved, /* o : flag to indicate if an orientation for this PI type was previously saved */ + IVAS_QUATERNION *savedOrientation /* o : saved orientation value for this PI type */ +) +{ + if ( orientation ) + { + /* read the orientation PI value */ + if ( !ivasPayload_parseOrientationPIdata( orientation, savedOrientation ) ) + { + fprintf( stderr, "Error: parsing PI orientation failed\n" ); + return false; + } + *isOrientationSaved = true; + } + return true; +} + + /*-----------------------------------------------------------------------* * ivasPayload_parsePIdata() * @@ -206,7 +333,8 @@ static bool ivasPayload_parsePIdata( char *PIdataPayload, /* i : Pointer to the beginning of the PI data section in the RTP packet */ uint16_t PIdataPayloadSizeBytes, /* i : Size of the PI data section in bytes */ uint16_t frameIndex, /* i : Index to the current audio frame in the RTP packet */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data handle for the current audio frame */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame,/* o : PI data handle for the current audio frame */ + PI_DATA_DEPACKER_STATE *PIdataDepackerState /* i/o: data handle for the PI depacker state */ ) { // TODO(pajunen): can this accidentally include zero padding bits? @@ -273,79 +401,21 @@ static bool ivasPayload_parsePIdata( } ivasPayload_PIaddHeaderBytes( totalPIheaderSizeBytes, PIdataCurrentFrame ); - return true; -} - - -/*-----------------------------------------------------------------------* - * ivasPayload_convertFromQ15() - * - * Convert a Q15 encoded value to a float value. - *-----------------------------------------------------------------------*/ - -static float ivasPayload_convertFromQ15( uint16_t Q15Value ) -{ - float temp; - temp = (float) ( Q15Value & 0x7FFF ) / 0x7FFF; - return ( Q15Value & 0x8000 ) ? -temp : temp; -} - - -/*-----------------------------------------------------------------------* - * ivasPayload_convertToQ15() - * - * Convert a float value into a Q15 encoded value. - *-----------------------------------------------------------------------*/ - -static uint16_t ivasPayload_convertToQ15( float value ) -{ - uint16_t temp; - temp = (uint16_t) ( fabsf( value ) * 0x7FFF ); - if ( value >= 0.0f ) + /* scene orientation */ + if ( !ivasPayload_parseSinglePIorientation( PIdataCurrentFrame->sceneOrientation, &PIdataDepackerState->sceneOrientationSaved, &PIdataDepackerState->sceneOrientationQuat ) ) { - temp = temp & 0x7FFF; + fprintf( stderr, "Error: parsing scene orientation PI data failed.\n" ); + return false; } - else + + /* device orientation */ + if ( !ivasPayload_parseSinglePIorientation( PIdataCurrentFrame->deviceOrientationUncompensated, &PIdataDepackerState->deviceOrientationSaved, &PIdataDepackerState->deviceOrientationQuat ) ) { - temp = temp | 0x8000; + fprintf( stderr, "Error: parsing device orientation PI data failed.\n" ); + return false; } - return temp; -} - - -/*-----------------------------------------------------------------------* - * ivasPayload_convertOrientationToQ15() - * - * Convert a float quaternion into a Q15 encoded quaternion. - *-----------------------------------------------------------------------*/ - -static void ivasPayload_convertOrientationToQ15( - IVAS_QUATERNION orientation, /* i : Input orientation in quaternion to be converted to Q15 */ - IVAS_QUATERNION_Q15 *orientationQ15 /* o : Ouput orientation in Q15 quaternion */ -) -{ - orientationQ15->w = ivasPayload_convertToQ15( orientation.w ); - orientationQ15->x = ivasPayload_convertToQ15( orientation.x ); - orientationQ15->y = ivasPayload_convertToQ15( orientation.y ); - orientationQ15->z = ivasPayload_convertToQ15( orientation.z ); -} - -/*-----------------------------------------------------------------------* - * ivasPayload_parseQuaternionValue() - * - * Parse a single Q15 encoded quaternion component from the RTP packet and convert it to float. - *-----------------------------------------------------------------------*/ - -static float ivasPayload_parseQuaternionComponent( - char **PIorientationData /* i/o: Pointer to the PI data frame containing a single quaternion component */ -) -{ - uint16_t tempQ15value; - tempQ15value = ( uint16_t ) * *PIorientationData; - tempQ15value = (uint16_t) ( ( tempQ15value << 8 ) | ( uint16_t ) * ( *PIorientationData + 1 ) ); - *PIorientationData += 2; - return ivasPayload_convertFromQ15( tempQ15value ); + return true; } @@ -355,7 +425,7 @@ static float ivasPayload_parseQuaternionComponent( * Reset the PI data handle for the current audio frame. *-----------------------------------------------------------------------*/ -void resetPIdataCurrentFrame( PI_DATA_CURRENT_FRAME *PIdataCurrentFrame ) +static void resetPIdataCurrentFrame( PI_DATA_CURRENT_FRAME *PIdataCurrentFrame ) { PIdataCurrentFrame->PIdataPresent = false; PIdataCurrentFrame->sceneOrientation = NULL; @@ -367,33 +437,6 @@ void resetPIdataCurrentFrame( PI_DATA_CURRENT_FRAME *PIdataCurrentFrame ) } -/*-----------------------------------------------------------------------* - * ivasPayload_parseOrientationPIdata() - * - * Parse a single orientation in quaternions from the PI data. - *-----------------------------------------------------------------------*/ - -bool ivasPayload_parseOrientationPIdata( - char *PIorientationData, /* i/o: Pointer to the PI data frame containing a single quaternion orientation */ - IVAS_QUATERNION *q /* o : Parsed quaternion value */ -) -{ - q->w = ivasPayload_parseQuaternionComponent( &PIorientationData ); - q->x = ivasPayload_parseQuaternionComponent( &PIorientationData ); - q->y = ivasPayload_parseQuaternionComponent( &PIorientationData ); - q->z = ivasPayload_parseQuaternionComponent( &PIorientationData ); - - if ( q->w > 1.0f || q->w < -1.0f || - q->x > 1.0f || q->x < -1.0f || - q->y > 1.0f || q->y < -1.0f || - q->z > 1.0f || q->z < -1.0f ) - { - fprintf( stderr, "Error: invalid orientation PI data\n" ); - return false; - } - return true; -} - bool ivasPayload_unpackFrame( char *payload, /* i : RTP payload data */ uint16_t payloadSizeBytes, /* i : RTP payload size in bytes */ @@ -406,7 +449,7 @@ bool ivasPayload_unpackFrame( * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ unsigned char **frame, /* o : Data frame indicated by frameIndex */ uint16_t *frameSizeInBits, /* o : Data frame size in bits */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data for frame indicated by frameIndex */ + PI_DATA_DEPACKER_STATE *PIdataDepackerState /* i/o: data handle for the PI depacker state */ ) { bool someEvsIvasModeBit, someFrameFollowing, someIvasIndicatorBit; @@ -417,6 +460,8 @@ bool ivasPayload_unpackFrame( fullPayloadSizeBytes = payloadSizeBytes; PIdataPayload = payload; someFrameFollowing = true; + PI_DATA_CURRENT_FRAME PIdataCurrentFrame; + resetPIdataCurrentFrame( &PIdataCurrentFrame ); if ( payloadSizeBytes < 1 ) { fprintf( stderr, "Error: payload too small to parse ToC\n" ); @@ -425,7 +470,7 @@ bool ivasPayload_unpackFrame( /* parse CMR/E-bytes */ if ( *payload & E_BYTE ) // 1xxx xxxx { - PIdataCurrentFrame->PIdataPresent = false; + PIdataCurrentFrame.PIdataPresent = false; while ( 1 ) { if ( ( *payload & 0xF0 ) == IVAS_E_CMR ) // 1111 xxxx @@ -487,7 +532,7 @@ bool ivasPayload_unpackFrame( else if ( ( *payload & 0xE0 ) == IVAS_E_PI_INDICATION ) // 110x xxxx { /* IVAS PI presence */ - PIdataCurrentFrame->PIdataPresent = true; + PIdataCurrentFrame.PIdataPresent = true; } else if ( ( *payload & 0xE0 ) == IVAS_E_RESERVED ) // 111x xxxx { @@ -574,11 +619,11 @@ bool ivasPayload_unpackFrame( } /* parse PI data */ - if ( PIdataCurrentFrame->PIdataPresent ) + if ( PIdataCurrentFrame.PIdataPresent ) { PIdataPayloadSizeBytes = payloadSizeBytes; PIdataPayload += ( fullPayloadSizeBytes - PIdataPayloadSizeBytes ); - if ( !ivasPayload_parsePIdata( PIdataPayload, PIdataPayloadSizeBytes, frameIndex, PIdataCurrentFrame ) ) + if ( !ivasPayload_parsePIdata( PIdataPayload, PIdataPayloadSizeBytes, frameIndex, &PIdataCurrentFrame, PIdataDepackerState ) ) { fprintf( stderr, "Error while parsing PI data\n" ); return false; @@ -600,7 +645,6 @@ IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_open( { return IVAS_RTPDUMP_DEPACKER_RTPDUMP_ERROR; } - resetPIdataCurrentFrame( &self->PIdataCurrentFrame ); self->PIdataDepackerState.sceneOrientationSaved = false; self->PIdataDepackerState.deviceOrientationSaved = false; return IVAS_RTPDUMP_DEPACKER_NO_ERROR; @@ -638,7 +682,7 @@ IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_readNextFrame( if ( !ivasPayload_unpackFrame( self->rtpPacket.data + self->rtpPacket.headerSize, self->rtpPacket.payloadSize, self->frameIndex, evsIvasModeBit, &self->frameFollowing, bitrateIndex, ivasIndicatorBit, - frame, frameSizeBits, &self->PIdataCurrentFrame ) ) + frame, frameSizeBits, &self->PIdataDepackerState ) ) { return IVAS_RTPDUMP_DEPACKER_PAYLOAD_ERROR; } diff --git a/lib_util/ivas_rtp_payload.h b/lib_util/ivas_rtp_payload.h index 9d8bad958f..d1ec02d200 100644 --- a/lib_util/ivas_rtp_payload.h +++ b/lib_util/ivas_rtp_payload.h @@ -190,7 +190,6 @@ typedef struct uint32_t timeoffset_ms; uint16_t frameIndex; bool frameFollowing; - PI_DATA_CURRENT_FRAME PIdataCurrentFrame; PI_DATA_DEPACKER_STATE PIdataDepackerState; } IVAS_RTPDUMP_DEPACKER; @@ -238,7 +237,7 @@ bool ivasPayload_unpackFrame( * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ unsigned char **frame, /* o : Data frame indicated by frameIndex */ uint16_t *frameSizeInBits, /* o : Data frame size in bits */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data for frame indicated by frameIndex */ + PI_DATA_DEPACKER_STATE *PIdataDepackerState /* i/o: data handle for the PI depacker state */ ); -- GitLab From a2e78779843651bbd9df3c30732863da265330f2 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Fri, 13 Jun 2025 15:58:40 +0300 Subject: [PATCH 021/100] Clang format --- lib_dec/lib_dec.c | 6 +++--- lib_util/ivas_rtp_payload.c | 36 ++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib_dec/lib_dec.c b/lib_dec/lib_dec.c index d3ada6dd9c..7bf5401729 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -3729,9 +3729,9 @@ ivas_error IVAS_DEC_Flush( *---------------------------------------------------------------------*/ ivas_error IVAS_DEC_feedSinglePIorientation( - IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ - const 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_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ + const 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; diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index e9d65ea0b1..951c9a9a0c 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -304,9 +304,9 @@ static bool ivasPayload_parseOrientationPIdata( *---------------------------------------------------------------------*/ static bool ivasPayload_parseSinglePIorientation( - char *orientation, /* i : input PI orientation */ - bool *isOrientationSaved, /* o : flag to indicate if an orientation for this PI type was previously saved */ - IVAS_QUATERNION *savedOrientation /* o : saved orientation value for this PI type */ + char *orientation, /* i : input PI orientation */ + bool *isOrientationSaved, /* o : flag to indicate if an orientation for this PI type was previously saved */ + IVAS_QUATERNION *savedOrientation /* o : saved orientation value for this PI type */ ) { if ( orientation ) @@ -330,10 +330,10 @@ static bool ivasPayload_parseSinglePIorientation( *-----------------------------------------------------------------------*/ static bool ivasPayload_parsePIdata( - char *PIdataPayload, /* i : Pointer to the beginning of the PI data section in the RTP packet */ - uint16_t PIdataPayloadSizeBytes, /* i : Size of the PI data section in bytes */ - uint16_t frameIndex, /* i : Index to the current audio frame in the RTP packet */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame,/* o : PI data handle for the current audio frame */ + char *PIdataPayload, /* i : Pointer to the beginning of the PI data section in the RTP packet */ + uint16_t PIdataPayloadSizeBytes, /* i : Size of the PI data section in bytes */ + uint16_t frameIndex, /* i : Index to the current audio frame in the RTP packet */ + PI_DATA_CURRENT_FRAME *PIdataCurrentFrame, /* o : PI data handle for the current audio frame */ PI_DATA_DEPACKER_STATE *PIdataDepackerState /* i/o: data handle for the PI depacker state */ ) { @@ -438,17 +438,17 @@ static void resetPIdataCurrentFrame( PI_DATA_CURRENT_FRAME *PIdataCurrentFrame ) bool ivasPayload_unpackFrame( - char *payload, /* i : RTP payload data */ - uint16_t payloadSizeBytes, /* i : RTP payload size in bytes */ - uint16_t frameIndex, /* i : Index for the data frame in the payload */ - bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ - bool *frameFollowing, /* o : (F) bit in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ - unsigned char **frame, /* o : Data frame indicated by frameIndex */ - uint16_t *frameSizeInBits, /* o : Data frame size in bits */ + char *payload, /* i : RTP payload data */ + uint16_t payloadSizeBytes, /* i : RTP payload size in bytes */ + uint16_t frameIndex, /* i : Index for the data frame in the payload */ + bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ + bool *frameFollowing, /* o : (F) bit in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ + bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). + * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + unsigned char **frame, /* o : Data frame indicated by frameIndex */ + uint16_t *frameSizeInBits, /* o : Data frame size in bits */ PI_DATA_DEPACKER_STATE *PIdataDepackerState /* i/o: data handle for the PI depacker state */ ) { -- GitLab From c0bf68fbe73762197f98ce62a5045c98f700b256 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 18 Jun 2025 16:20:30 +0300 Subject: [PATCH 022/100] Fix Q-bit parsing, add good frame indication, remove unused evs payload parsing functions --- apps/decoder.c | 24 +++++++++++++----------- lib_dec/jbm_jb4sb.h | 5 +++++ lib_dec/lib_dec.c | 12 ++++++++++++ lib_dec/lib_dec.h | 6 +++++- lib_util/evs_rtp_payload.c | 27 ++++++++++++++++++++++++--- lib_util/evs_rtp_payload.h | 7 +++++-- lib_util/ivas_rtp_payload.c | 11 ++++++++--- lib_util/ivas_rtp_payload.h | 8 +++++--- 8 files changed, 77 insertions(+), 23 deletions(-) diff --git a/apps/decoder.c b/apps/decoder.c index cb2e08233f..213b77985a 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -2895,7 +2895,8 @@ static ivas_error printBitstreamInfoVoip( #ifdef IVAS_RTPDUMP bool evsIvasModeBit; uint16_t bitrateIndex; - bool ivasIndicatorOrQBit; + bool ivasIndicatorBit; + bool isGoodFrame; #else bool isAMRWB_IOmode; uint16_t frameTypeIndex; @@ -2944,7 +2945,7 @@ static ivas_error printBitstreamInfoVoip( { error = BS_Reader_ReadVoipFrame_compact( hBsReader, au, &auSizeBits, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms ); #ifdef IVAS_RTPDUMP - ivasIndicatorOrQBit = 1; /* good q_bit for INPUT_FORMAT_G192 */ + isGoodFrame = 1; /* good frame for INPUT_FORMAT_G192 */ #else qBit = 1; /* good q_bit for INPUT_FORMAT_G192 */ #endif @@ -2953,7 +2954,7 @@ static ivas_error printBitstreamInfoVoip( { auPtr = au; /* might have been set to RTP packet in prev call */ #ifdef IVAS_RTPDUMP - rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, &evsIvasModeBit, &bitrateIndex, &ivasIndicatorOrQBit, &auPtr, (uint16_t *) &auSizeBits ); + rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, &evsIvasModeBit, &bitrateIndex, &ivasIndicatorBit, &isGoodFrame, &auPtr, (uint16_t *) &auSizeBits ); #else rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, &isAMRWB_IOmode, &frameTypeIndex, &qBit, &auPtr, (uint16_t *) &auSizeBits ); #endif @@ -2967,7 +2968,7 @@ static ivas_error printBitstreamInfoVoip( goto cleanup; } #ifdef IVAS_RTPDUMP - } while ( !ivasIndicatorOrQBit || auSizeBits < MIN_NUM_BITS_ACTIVE_FRAME || auSizeBits == NUM_BITS_SID_IVAS_5K2 ); + } while ( !isGoodFrame || auSizeBits < MIN_NUM_BITS_ACTIVE_FRAME || auSizeBits == NUM_BITS_SID_IVAS_5K2 ); #else } while ( !qBit || auSizeBits < MIN_NUM_BITS_ACTIVE_FRAME || auSizeBits == NUM_BITS_SID_IVAS_5K2 ); #endif @@ -3066,7 +3067,8 @@ static ivas_error decodeVoIP( #ifdef IVAS_RTPDUMP bool evsIvasModeBit; uint16_t bitrateIndex; - bool ivasIndicatorOrQBit; + bool ivasIndicatorBit; + bool isGoodFrame; #else bool isAMRWB_IOmode; uint16_t frameTypeIndex; @@ -3177,7 +3179,7 @@ static ivas_error decodeVoIP( { error = BS_Reader_ReadVoipFrame_compact( hBsReader, au, &auSize, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms ); #ifdef IVAS_RTPDUMP - ivasIndicatorOrQBit = 1; /* good q_bit for INPUT_FORMAT_G192 */ + isGoodFrame = 1; /* good frame for INPUT_FORMAT_G192 */ #else qBit = 1; /* good q_bit for INPUT_FORMAT_G192 */ #endif @@ -3186,7 +3188,7 @@ static ivas_error decodeVoIP( { auPtr = au; /* might have been set to RTP packet in prev call */ #ifdef IVAS_RTPDUMP - rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, &evsIvasModeBit, &bitrateIndex, &ivasIndicatorOrQBit, &auPtr, (uint16_t *) &auSize ); + rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, &evsIvasModeBit, &bitrateIndex, &ivasIndicatorBit, &isGoodFrame, &auPtr, (uint16_t *) &auSize ); #else rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, &isAMRWB_IOmode, &frameTypeIndex, &qBit, &auPtr, (uint16_t *) &auSize ); #endif @@ -3351,7 +3353,7 @@ static ivas_error decodeVoIP( { /* feed the previous read packet into the receiver now */ #ifdef IVAS_RTPDUMP - error = IVAS_DEC_VoIP_FeedFrame( hIvasDec, auPtr, auSize, rtpSequenceNumber, rtpTimeStamp, nextPacketRcvTime_ms, ivasIndicatorOrQBit ); + error = IVAS_DEC_VoIP_FeedFrame( hIvasDec, auPtr, auSize, rtpSequenceNumber, rtpTimeStamp, nextPacketRcvTime_ms, isGoodFrame ); #else error = IVAS_DEC_VoIP_FeedFrame( hIvasDec, auPtr, auSize, rtpSequenceNumber, rtpTimeStamp, nextPacketRcvTime_ms, qBit ); #endif @@ -3368,7 +3370,7 @@ static ivas_error decodeVoIP( error = BS_Reader_ReadVoipFrame_compact( hBsReader, au, &auSize, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms ); #ifdef IVAS_RTPDUMP - ivasIndicatorOrQBit = 1; /* good q_bit for VOIP_G192_RTP */ + isGoodFrame = 1; /* good frame for VOIP_G192_RTP */ #else qBit = 1; /* good q_bit for VOIP_G192_RTP */ #endif @@ -3379,8 +3381,8 @@ static ivas_error decodeVoIP( #ifdef IVAS_RTPDUMP rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, - &evsIvasModeBit, &bitrateIndex, &ivasIndicatorOrQBit, - &auPtr, (uint16_t *) &auSize ); + &evsIvasModeBit, &bitrateIndex, &ivasIndicatorBit, + &isGoodFrame, &auPtr, (uint16_t *) &auSize ); /* IVAS RTP payload format has timescale 16000, JBM uses 1000 internally */ rtpTimeStamp = rtpTimeStamp / 16; diff --git a/lib_dec/jbm_jb4sb.h b/lib_dec/jbm_jb4sb.h index 8155d66ae9..2ef48dbfb3 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 7bf5401729..91959047ce 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -3232,7 +3232,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 */ +#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; @@ -3274,7 +3278,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 ); @@ -3297,7 +3305,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 ); diff --git a/lib_dec/lib_dec.h b/lib_dec/lib_dec.h index f20d6a9eb2..40a5403373 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( diff --git a/lib_util/evs_rtp_payload.c b/lib_util/evs_rtp_payload.c index 89a3bdf51d..9c1f71be48 100644 --- a/lib_util/evs_rtp_payload.c +++ b/lib_util/evs_rtp_payload.c @@ -39,6 +39,7 @@ #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; @@ -123,12 +124,30 @@ 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, int32_t *bitrate ) +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 ) -#endif { bool evsModeBit = ( toc & 0x20 ) != 0; *isAMRWB_IOmode = evsModeBit; @@ -145,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; @@ -278,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 21bb10f3f3..aea968ddff 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,13 +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, int32_t *bitrate ); + 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 @@ -200,6 +202,7 @@ static void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bo uint16_t *frameSizeBits ); void EVS_RTPDUMP_DEPACKER_close( EVS_RTPDUMP_DEPACKER *self ); +#endif #ifdef __cplusplus } diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index 951c9a9a0c..e99ff585d6 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -87,18 +87,20 @@ static void ivasPayload_parseToc( uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + bool *isGoodFrame, /* o : Good frame indicator (set by the Q-bit in AMR-WB IO mode, otherwise always set to true) */ int32_t *bitrate /* o : Bitrate of the data frame indicated in the ToC byte */ ) { bool isIvasToc = ( toc & 0x30 ) == 0x10; // xx01 xxxx if ( !isIvasToc ) { - evsHeaderFullPayload_parseToc( toc, evsIvasModeBit, frameFollowing, bitrateIndex, ivasIndicatorBit, bitrate ); + evsHeaderFullPayload_parseToc( toc, evsIvasModeBit, frameFollowing, bitrateIndex, ivasIndicatorBit, isGoodFrame, bitrate ); } else { *evsIvasModeBit = false; *ivasIndicatorBit = true; + *isGoodFrame = true; /* assume good frame for IVAS */ *frameFollowing = ( toc & 0x40 ) != 0; *bitrateIndex = toc & 0x0f; *bitrate = IVASmode2rate[*bitrateIndex]; @@ -447,12 +449,13 @@ bool ivasPayload_unpackFrame( uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + bool *isGoodFrame, /* o : Good frame indicator (set by the Q-bit in AMR-WB IO mode, otherwise always set to true) */ unsigned char **frame, /* o : Data frame indicated by frameIndex */ uint16_t *frameSizeInBits, /* o : Data frame size in bits */ PI_DATA_DEPACKER_STATE *PIdataDepackerState /* i/o: data handle for the PI depacker state */ ) { - bool someEvsIvasModeBit, someFrameFollowing, someIvasIndicatorBit; + bool someEvsIvasModeBit, someFrameFollowing, someIvasIndicatorBit, someGoodFrameIndication; uint16_t someBitrateIndex, someFrameSizeInBits, fullPayloadSizeBytes, PIdataPayloadSizeBytes; int32_t bitrate; uint16_t iFrame; @@ -582,7 +585,7 @@ bool ivasPayload_unpackFrame( fprintf( stderr, "Error: expected ToC, found CMR/E-byte\n" ); return false; } - ivasPayload_parseToc( *payload, &someEvsIvasModeBit, &someFrameFollowing, &someBitrateIndex, &someIvasIndicatorBit, &bitrate ); + ivasPayload_parseToc( *payload, &someEvsIvasModeBit, &someFrameFollowing, &someBitrateIndex, &someIvasIndicatorBit, &someGoodFrameIndication, &bitrate ); if ( bitrate < 0 ) { fprintf( stderr, "Error: unexpected frameTypeIndex in ToC\n" ); @@ -609,6 +612,7 @@ bool ivasPayload_unpackFrame( *frameFollowing = someFrameFollowing; *bitrateIndex = someBitrateIndex; *ivasIndicatorBit = someIvasIndicatorBit; + *isGoodFrame = someGoodFrameIndication; *frameSizeInBits = someFrameSizeInBits; } if ( (int16_t) payloadSizeBytes < 0 ) @@ -660,6 +664,7 @@ IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_readNextFrame( uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + bool *isGoodFrame, /* o : Good frame indicator (set by the Q-bit in AMR-WB IO mode, otherwise always set to true) */ unsigned char **frame, /* o : Data frame indicated by frameIndex */ uint16_t *frameSizeBits /* o : Data frame size in bits */ ) diff --git a/lib_util/ivas_rtp_payload.h b/lib_util/ivas_rtp_payload.h index d1ec02d200..8529de51b7 100644 --- a/lib_util/ivas_rtp_payload.h +++ b/lib_util/ivas_rtp_payload.h @@ -230,11 +230,12 @@ bool ivasPayload_unpackFrame( uint16_t payloadSizeBytes, /* i : RTP payload size in bytes */ uint16_t frameIndex, /* i : Index for the data frame in the payload */ bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ + * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ bool *frameFollowing, /* o : (F) bit in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + bool *isGoodFrame, /* o : Good frame indicator (set by the Q-bit in AMR-WB IO mode, otherwise always set to true) */ unsigned char **frame, /* o : Data frame indicated by frameIndex */ uint16_t *frameSizeInBits, /* o : Data frame size in bits */ PI_DATA_DEPACKER_STATE *PIdataDepackerState /* i/o: data handle for the PI depacker state */ @@ -268,7 +269,8 @@ IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_readNextFrame( * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ + bool *isGoodFrame, /* o : Good frame indicator (set by the Q-bit in AMR-WB IO mode, otherwise always set to true) */ unsigned char **frame, /* o : Data frame indicated by frameIndex */ uint16_t *frameSizeBits /* o : Data frame size in bits */ ); -- GitLab From d98442d375f228d7fd8dc9d910c4333ce758c59c Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 18 Jun 2025 16:30:03 +0300 Subject: [PATCH 023/100] Clang format --- lib_dec/lib_dec.c | 4 ++-- lib_util/evs_rtp_payload.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib_dec/lib_dec.c b/lib_dec/lib_dec.c index 91959047ce..a476430478 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -3233,9 +3233,9 @@ ivas_error IVAS_DEC_VoIP_FeedFrame( const uint32_t rtpTimeStamp, /* i : RTP timestamp (32 bits) */ const uint32_t rcvTime_ms, /* i : receive time of the RTP packet in milliseconds */ #ifdef IVAS_RTPDUMP - const bool isGoodFrame /* i : Good frame indicator (Q bit for AMR-WB IO, otherwise set to true) */ + 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 */ + const bool qBit /* i : Q bit for AMR-WB IO */ #endif ) { diff --git a/lib_util/evs_rtp_payload.c b/lib_util/evs_rtp_payload.c index 9c1f71be48..7fc0914892 100644 --- a/lib_util/evs_rtp_payload.c +++ b/lib_util/evs_rtp_payload.c @@ -141,7 +141,7 @@ void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *fra } else { - *qBit = false; /* Q-bit is unused in EVS Primary mode */ + *qBit = false; /* Q-bit is unused in EVS Primary mode */ *isGoodFrame = true; /* assume good frame for EVS Primary */ *bitrate = PRIMARYmode2rate[*frameTypeIndex]; } -- GitLab From ba624cf8f74ed0b32a175bab627ef05a92dfdef0 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Mon, 30 Jun 2025 10:05:51 +0300 Subject: [PATCH 024/100] Add isGoodFrame parameter to function call --- lib_util/ivas_rtp_payload.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index e99ff585d6..d952450b52 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -687,7 +687,7 @@ IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_readNextFrame( if ( !ivasPayload_unpackFrame( self->rtpPacket.data + self->rtpPacket.headerSize, self->rtpPacket.payloadSize, self->frameIndex, evsIvasModeBit, &self->frameFollowing, bitrateIndex, ivasIndicatorBit, - frame, frameSizeBits, &self->PIdataDepackerState ) ) + isGoodFrame, frame, frameSizeBits, &self->PIdataDepackerState ) ) { return IVAS_RTPDUMP_DEPACKER_PAYLOAD_ERROR; } @@ -811,6 +811,7 @@ void IVAS_RTPDUMP_PACKER_writeEbytes( } else { + // TODO(pajunen): add EVS CMRs here /* EVS CMR */ initialEbyteWritten = false; } -- GitLab From e568f54d6d6f05ec232fd6d3d5eb1b64fe8e7629 Mon Sep 17 00:00:00 2001 From: "Singh, Ripinder" Date: Thu, 3 Jul 2025 12:01:55 +1000 Subject: [PATCH 025/100] Introduce an ivas rtp api for pack/unpack * Add support for pack/unpack of ivas and evs frames * Support for pi data * Support for remote requests and frame info Signed-off-by: Singh, Ripinder --- lib_util/ivas_rtp_api.h | 470 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 470 insertions(+) create mode 100644 lib_util/ivas_rtp_api.h diff --git a/lib_util/ivas_rtp_api.h b/lib_util/ivas_rtp_api.h new file mode 100644 index 0000000000..7581b183a2 --- /dev/null +++ b/lib_util/ivas_rtp_api.h @@ -0,0 +1,470 @@ +/****************************************************************************************************** + + (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. + +*******************************************************************************************************/ + +#ifndef IVAS_RTP_API_H +#define IVAS_RTP_API_H + +#pragma once +#include +#include +#include "ivas_error.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* + * +-----------------------+---------------------+--------------------+----------+ + * | 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 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_CODECS; + + /* 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; + + /* 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 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: 28; /* reserved */ + } IVAS_RTP_SPLITRENDER; + + /* Remote Requests Types (Keys) */ + typedef enum + { + IVAS_REQUEST_CODEC, /* Request codec type, value of type IVAS_RTP_CODECS */ + 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_SUBFORMAT, /* Request subFormat, value of type IVAS_RTP_SUBFORMAT */ + IVAS_REQUEST_CA_MODE, /* Request channel awareness, value of type IVAS_RTP_CA_MODE */ + IVAS_REQUEST_SR_CONFIG, /* Request spit rendering, value of type IVAS_RTP_SPLITRENDER */ + 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_CODECS 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_SUBFORMAT subFormatType; /* sub-format type when request type is IVAS_REQUEST_SUBFORMAT */ + IVAS_RTP_CA_MODE caMode; /* channel aware mode when request type is IVAS_REQUEST_CA_MODE */ + IVAS_RTP_SPLITRENDER srConfig; /* split rendering config when request type is IVAS_REQUEST_SR_CONFIG */ + } 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 { + * uint32_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 + { + uint32_t size; /* size of this structure */ + uint32_t piDataType; /* IVAS PI data type */ + uint8_t data[1]; /* Variable length array (length calculated as size - 2*sizeof(uint32_t))*/ + } IVAS_PIDATA_GENERIC; + + /* Generic data buffer for sending/receiving coded frames / rtp packets + * data buffer is owned and initialized by caller, rtp api will ensure + * buffer write does not exceed allocSize. + */ + typedef struct + { + uint32_t allocSize; /* allocated size of the data buffer */ + uint32_t length; /* length of the initialized data in the buffer */ + uint8_t *buffer; /* pointer to the packet buffer */ + } IVAS_DATA_BUFFER; + + /**********************************************/ + /* 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. + * + * - piDataRepeatIntervalMs is used to define pi data repetition interval + * + */ + typedef struct + { + uint32_t maxFramesPerPacket; /* maximum no of frame per packet desired during the session */ + uint32_t piDataRepeatIntervalMs; /* period for pi data repeat 0 = each frame, UINT_MAX=never */ + } 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 */ + ); + + /* 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_RemoteRequest( + 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_GetPacket to generate a rtp packet. + * + * It is possible to have variable frames per packet until maxFramesPerPacket frames + * if IVAS_RTP_PACK_GetPacket 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_CODECS codecId, /* i : Codec type (IVAS/EVS) */ + const IVAS_DATA_BUFFER *frameBuffer /* i : packed frame bitstream for IVAS/EVS */ + ); + + /* 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 packet with policy defined in initial configuration during call to + * IVAS_RTP_PACK_GetPacket. + * + */ + 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 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 + * + */ + 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 */ + ); + + + /**********************************************/ + /* 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 */ + typedef struct + { + uint32_t maxNumberOfPacketInFifo; /* input packet fifo 's max length */ + } 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 packet. Caller must extract RTP header and header + * extension and feed Ivas Payload alongwith RTP Timestamp and sequence number. + * + * Example usage : - + * ================== + * err = IVAS_RTP_UNPACK_PushPacket(hIvasUnpack, payload, rtpTs, seqNum, + * &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, &frame, &frameTs, &seqNum); + * 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_GetNextPiData(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 timestamp, /* i : timestamp in RTP Clock @ 16KHz from rtp header */ + uint16_t sequenceNumbers, /* 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 extension header to unpacker to extract pi data + * Caller must feed rtp extension header alongwith RTP Timestamp and sequence number. + */ + ivas_error IVAS_RTP_UNPACK_HeaderExtension ( + IVAS_RTP_UNPACK_HANDLE hIvasUnpack, /* i/o : IVAS rtp unpacker handle */ + const IVAS_DATA_BUFFER *hdrExtnBuffer, /* i : received rtp header extension buffer */ + uint32_t timestamp, /* i : timestamp in RTP Clock @ 16KHz from rtp header */ + uint16_t sequenceNumbers, /* i : sequence number from rtp header */ + uint32_t *numPiDataInPacket /* o : number of PI data received in rtp 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_PushPacket(), 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 packet alongwith rtp timestamp + * and sequence number + * + * If multiple frames per RTP packet are available, multiple calls to IVAS_RTP_UNPACK_PullFrame + * are needed. A frameBuffer.length == 0 indicates NO_DATA_FRAME + * + */ + ivas_error IVAS_RTP_UNPACK_PullFrame( + IVAS_RTP_UNPACK_HANDLE hIvasUnpack, /* i/o : IVAS rtp unpacker handle */ + IVAS_RTP_CODECS *receivedCodecId, /* i : Codec type (IVAS/EVS) */ + IVAS_DATA_BUFFER *frameBuffer, /* o : packed frame bitstream for IVAS/EVS */ + uint32_t *timestamp, /* o : timestamp in RTP Clock @ 16KHz */ + uint16_t *sequenceNumbers /* o : sequence number from rtp header */ + ); + + /* 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_GetNextPiData( + IVAS_RTP_UNPACK_HANDLE hIvasUnpack, /* i/o : IVAS rtp unpacker handle */ + IVAS_PIDATA_GENERIC *data, /* o : output data buffer for the Pi data */ + uint32_t *timestamp /* o : timestamp in RTP Clock @ 16KHz */ + ); + +#ifdef __cplusplus +} +#endif + +#endif /* IVAS_RTP_API_H */ -- GitLab From 1d0f51e9373271ef5ea3cc6feafe359ed39d8f8d Mon Sep 17 00:00:00 2001 From: "Singh, Ripinder" Date: Thu, 10 Jul 2025 14:45:38 +1000 Subject: [PATCH 026/100] Code review comments #1 Signed-off-by: Singh, Ripinder --- lib_com/options.h | 1 + lib_util/ivas_rtp_api.h | 104 +++++++++++++++++++--------------------- 2 files changed, 51 insertions(+), 54 deletions(-) diff --git a/lib_com/options.h b/lib_com/options.h index 941685105c..1f6e493889 100644 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -155,6 +155,7 @@ /* ################## 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 ################################# */ diff --git a/lib_util/ivas_rtp_api.h b/lib_util/ivas_rtp_api.h index 7581b183a2..5b008a04ca 100644 --- a/lib_util/ivas_rtp_api.h +++ b/lib_util/ivas_rtp_api.h @@ -34,8 +34,10 @@ #define IVAS_RTP_API_H #pragma once +#include #include #include +#include "options.h" #include "ivas_error.h" #ifdef __cplusplus @@ -80,7 +82,7 @@ extern "C" { IVAS_RTP_EVS, /* EVS */ IVAS_RTP_IVAS /* IVAS */ - } IVAS_RTP_CODECS; + } IVAS_RTP_CODEC; /* IVAS Bandwidth Requests */ typedef enum @@ -119,6 +121,7 @@ extern "C" IVAS_FMT_NO_REQ, /* No preference */ } IVAS_RTP_FORMAT; +#ifdef RTP_S4_251135_CR26253_0016_REV1 /* Coded Subformat Requests */ typedef enum { @@ -197,36 +200,41 @@ extern "C" uint32_t roll: 1; /* transmission metadata for correction around roll axis */ uint32_t reserved: 28; /* 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_CODECS */ + 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_SUBFORMAT, /* Request subFormat, value of type IVAS_RTP_SUBFORMAT */ 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_CODECS codec; /* codec id when request type is IVAS_REQUEST_CODEC */ + 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_SUBFORMAT subFormatType; /* sub-format type when request type is IVAS_REQUEST_SUBFORMAT */ 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 { - * uint32_t size; // sizeof(IVAS_PIDATA_SCENE_ORIENTATION) + * 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; @@ -234,20 +242,20 @@ extern "C" */ typedef struct { - uint32_t size; /* size of this structure */ + size_t size; /* size of this structure */ uint32_t piDataType; /* IVAS PI data type */ - uint8_t data[1]; /* Variable length array (length calculated as size - 2*sizeof(uint32_t))*/ + uint8_t data[1]; /* Variable length array */ } IVAS_PIDATA_GENERIC; - /* Generic data buffer for sending/receiving coded frames / rtp packets + /* 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 allocSize. + * buffer write does not exceed capacity. */ typedef struct { - uint32_t allocSize; /* allocated size of the data buffer */ - uint32_t length; /* length of the initialized data in the buffer */ - uint8_t *buffer; /* pointer to the packet buffer */ + 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; /**********************************************/ @@ -260,14 +268,10 @@ extern "C" /* Initial configuration for rtp packer * - maxFramesPerPacket is used to define if more than one frame should be packed * in the same rtp packet. - * - * - piDataRepeatIntervalMs is used to define pi data repetition interval - * */ typedef struct { uint32_t maxFramesPerPacket; /* maximum no of frame per packet desired during the session */ - uint32_t piDataRepeatIntervalMs; /* period for pi data repeat 0 = each frame, UINT_MAX=never */ } IVAS_RTP_PACK_CONFIG; /* Open an instance of the RTP packer and return a handle to rtp packer on success @@ -290,7 +294,7 @@ extern "C" * in this API. E.g. Codec ID and supported bitrates/bandwidths * will not be performed at this level. */ - ivas_error IVAS_RTP_PACK_RemoteRequest( + 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 */ @@ -299,23 +303,23 @@ extern "C" /* 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_GetPacket to generate a rtp packet. + * 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_GetPacket is invoked asyncronously w.r.t this api. + * 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_CODECS codecId, /* i : Codec type (IVAS/EVS) */ + IVAS_RTP_CODEC codecId, /* i : Codec type (IVAS/EVS) */ const IVAS_DATA_BUFFER *frameBuffer /* i : packed frame bitstream for IVAS/EVS */ ); /* 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 packet with policy defined in initial configuration during call to - * IVAS_RTP_PACK_GetPacket. + * 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( @@ -323,15 +327,15 @@ extern "C" const IVAS_PIDATA_GENERIC *data /* i : pointer to the PIData stucture */ ); - /* Generate a rtp packet using available pushed frames + /* Generate a rtp payload using available pushed frames * - * Available remote requests, pi data and frames will be packed into a rtp packet. If no + * Available remote requests, pi data and frames will be packed into a rtp payload. If no * frame is pushed before call to this api, NO_DATA_FRAME will be generated * */ - ivas_error IVAS_RTP_PACK_GetPacket( + ivas_error IVAS_RTP_PACK_GetPayload( IVAS_RTP_PACK_HANDLE hIvasPack, /* i/o : IVAS rtp packer handle */ - IVAS_DATA_BUFFER *packet /* o : encapsulated rtp packet */ + IVAS_DATA_BUFFER *payload /* o : encapsulated rtp payload */ ); @@ -345,7 +349,7 @@ extern "C" /* Initial configuration for rtp unpacker */ typedef struct { - uint32_t maxNumberOfPacketInFifo; /* input packet fifo 's max length */ + uint32_t maxNumberOfPayloadsInFifo; /* input payload fifo 's max length */ } IVAS_RTP_UNPACK_CONFIG; /* Open an instance of the RTP unpacker and return a handle to rtp unpacker on success @@ -362,16 +366,20 @@ extern "C" ); /* Push a received rtp Ivas Payload to unpacker to extract number of frames, pi data and - * any remote request present in the packet. Caller must extract RTP header and header + * 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. + * * Example usage : - * ================== - * err = IVAS_RTP_UNPACK_PushPacket(hIvasUnpack, payload, rtpTs, seqNum, - * &nFrames, &nPiData, &reqBitmap); + * err = IVAS_RTP_UNPACK_PushPayload(hIvasUnpack, payload, rtpTs, seqNum, + * &nFrames, &nPiData, &reqBitmap); * if (err != IVAS_ERR_OK) { return err; } * - * // Read the frames in packet and feed to decoder + * // Read the frames in payload and feed to decoder * while (nFrames-- > 0) { * err = IVAS_RTP_UNPACK_PullFrame(hIvasUnpack, &recCodecId, &frame, &frameTs, &seqNum); * if (err != IVAS_ERR_OK) { return err; } @@ -381,7 +389,7 @@ extern "C" * * // Read PI Data * while (nPiData-- > 0) { - * err = IVAS_RTP_UNPACK_GetNextPiData(hIvasUnpack, &piData, &piTs); + * 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) @@ -405,31 +413,20 @@ extern "C" * } * */ - ivas_error IVAS_RTP_UNPACK_PushPacket( + ivas_error IVAS_RTP_UNPACK_PushPayload( IVAS_RTP_UNPACK_HANDLE hIvasUnpack, /* i/o : IVAS rtp unpacker handle */ - const IVAS_DATA_BUFFER *packet, /* i : received rtp packet */ + const IVAS_DATA_BUFFER *payload, /* i : received rtp payload */ uint32_t timestamp, /* i : timestamp in RTP Clock @ 16KHz from rtp header */ - uint16_t sequenceNumbers, /* i : sequence number 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 extension header to unpacker to extract pi data - * Caller must feed rtp extension header alongwith RTP Timestamp and sequence number. - */ - ivas_error IVAS_RTP_UNPACK_HeaderExtension ( - IVAS_RTP_UNPACK_HANDLE hIvasUnpack, /* i/o : IVAS rtp unpacker handle */ - const IVAS_DATA_BUFFER *hdrExtnBuffer, /* i : received rtp header extension buffer */ - uint32_t timestamp, /* i : timestamp in RTP Clock @ 16KHz from rtp header */ - uint16_t sequenceNumbers, /* i : sequence number from rtp header */ - uint32_t *numPiDataInPacket /* o : number of PI data received in rtp 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_PushPacket(), remoteRequestBitmap can be used + * On call to IVAS_RTP_UNPACK_PushPayload(), remoteRequestBitmap can be used * an indicator of new request available this frame * */ @@ -439,25 +436,24 @@ extern "C" IVAS_RTP_REQUEST_VALUE *value /* o : pointer of the requested type */ ); - /* Extract a single IVAS/EVS frame from provided rtp packet alongwith rtp timestamp + /* 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. A frameBuffer.length == 0 indicates NO_DATA_FRAME - * + * are needed. */ ivas_error IVAS_RTP_UNPACK_PullFrame( IVAS_RTP_UNPACK_HANDLE hIvasUnpack, /* i/o : IVAS rtp unpacker handle */ - IVAS_RTP_CODECS *receivedCodecId, /* i : Codec type (IVAS/EVS) */ + IVAS_RTP_CODEC *receivedCodecId, /* i : Codec type (IVAS/EVS) */ IVAS_DATA_BUFFER *frameBuffer, /* o : packed frame bitstream for IVAS/EVS */ uint32_t *timestamp, /* o : timestamp in RTP Clock @ 16KHz */ - uint16_t *sequenceNumbers /* o : sequence number from rtp header */ + uint16_t *sequenceNumber /* o : sequence number from rtp header */ ); /* 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_GetNextPiData( + 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 */ uint32_t *timestamp /* o : timestamp in RTP Clock @ 16KHz */ -- GitLab From 5aa659573e23e6308fdad18c5b719f93d3d2c9b9 Mon Sep 17 00:00:00 2001 From: "Singh, Ripinder" Date: Thu, 10 Jul 2025 15:26:39 +1000 Subject: [PATCH 027/100] First draft of the RTP Pack API * Introduce RTP Packet and Payload API implementation for Pack and Unpack * Introduce general handling of PI data and data type structures * Introduce the mutex encapsulation * Introduce a buffer pool manager * Example pack implementation in Encoder wrapper * Code Review comments Signed-off-by: Singh, Ripinder --- apps/decoder.c | 259 +++- apps/encoder.c | 174 ++- lib_com/ivas_error.h | 9 + lib_dec/lib_dec.c | 8 +- lib_dec/lib_dec.h | 6 +- lib_util/ivas_bpool.c | 154 +++ lib_util/ivas_bpool.h | 58 + lib_util/ivas_queue.c | 126 ++ lib_util/ivas_queue.h | 68 + lib_util/ivas_rtp_api.h | 251 +++- lib_util/ivas_rtp_internal.h | 136 ++ lib_util/ivas_rtp_payload.c | 2387 ++++++++++++++++++++++------------ lib_util/ivas_rtp_pi_data.c | 601 +++++++++ lib_util/ivas_rtp_pi_data.h | 441 +++++++ lib_util/ivas_rtp_rom.c | 64 + lib_util/mutex.h | 104 ++ 16 files changed, 3829 insertions(+), 1017 deletions(-) create mode 100644 lib_util/ivas_bpool.c create mode 100644 lib_util/ivas_bpool.h create mode 100644 lib_util/ivas_queue.c create mode 100644 lib_util/ivas_queue.h create mode 100644 lib_util/ivas_rtp_internal.h create mode 100644 lib_util/ivas_rtp_pi_data.c create mode 100644 lib_util/ivas_rtp_pi_data.h create mode 100644 lib_util/ivas_rtp_rom.c create mode 100644 lib_util/mutex.h diff --git a/apps/decoder.c b/apps/decoder.c index 213b77985a..39c4f19824 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -39,7 +39,8 @@ #include "evs_rtp_payload.h" #include "ism_file_writer.h" #ifdef IVAS_RTPDUMP -#include "ivas_rtp_payload.h" +#include "ivas_rtp_api.h" +#include "ivas_rtp_pi_data.h" #endif #include "jbm_file_writer.h" #include "hrtf_file_reader.h" @@ -166,6 +167,170 @@ typedef struct } IVAS_DEC_HRTF_BINARY_WRAPPER; +#ifdef IVAS_RTPDUMP + +typedef struct +{ + PIDATA data; + uint32_t timestamp; +} PIDATA_TS; + +typedef struct +{ + uint8_t packet[IVAS_NOMINAL_RTP_BYTES_PER_FRAME * MAX_FRAMES_PER_RTP_PACKET]; + PIDATA_TS piData[IVAS_PI_MAX_ID * MAX_FRAMES_PER_RTP_PACKET]; + + FILE *f_rtpstream; + IVAS_RTP_CODEC codecId; + uint32_t nReadPiData; + uint32_t nProcPiData; + uint32_t numFramesInPacket; + uint32_t numPiDataInPacket; + uint32_t remoteRequestBitmap; + + IVAS_DATA_BUFFER rtpPacket; + IVAS_RTP_UNPACK_HANDLE hUnpack; + IVAS_RTP_UNPACK_CONFIG unpackCfg; + PI_DATA_DEPACKER_STATE piDataDepackerState; +} IVAS_RTP; + +static void IVAS_RTP_Term( IVAS_RTP *rtp ) +{ + if ( rtp->f_rtpstream ) + { + fclose( rtp->f_rtpstream ); + rtp->f_rtpstream = NULL; + } + IVAS_RTP_UNPACK_Close( &rtp->hUnpack ); +} + +static ivas_error IVAS_RTP_Init( IVAS_RTP *rtp, FILE *f_rtpstream ) +{ + ivas_error error = IVAS_ERR_OK; + + memset( rtp, 0, sizeof( IVAS_RTP ) ); + + error = IVAS_RTP_UNPACK_Open( &rtp->hUnpack, &rtp->unpackCfg ); + + rtp->f_rtpstream = f_rtpstream; + rtp->rtpPacket.buffer = rtp->packet; + rtp->rtpPacket.capacity = sizeof( rtp->packet ); + + return error; +} + +static ivas_error readNextFrame( IVAS_RTP *rtp, IVAS_DEC_HANDLE hIvasDec, 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 = { 0, 0, NULL }; + uint32_t packetLen = 0u; + bool speechLostIndicated = false; +#ifdef RTP_S4_251135_CR26253_0016_REV1 + IVAS_RTP_SR_INFO srInfo; +#endif + + packedFrame.buffer = au; + packedFrame.capacity = ( IVAS_MAX_BITS_PER_FRAME / 8 ); + if ( rtp->numFramesInPacket == 0 ) + { + size_t nread = fread( &packetLen, sizeof( uint32_t ), 1, rtp->f_rtpstream ); /* Read Packet Length */ + if ( nread == 0 ) + { + return IVAS_ERR_END_OF_FILE; + } + if ( packetLen > sizeof( rtp->packet ) ) + { + fprintf( stderr, "RTP packet > buffer capacity %lu bytes\n", sizeof( rtp->packet ) ); + return IVAS_ERR_INVALID_OUTPUT_BUFFER_SIZE; + } + + nread = fread( rtp->packet, sizeof( uint8_t ), packetLen, rtp->f_rtpstream ); /* Read Packet */ + if ( nread == 0 ) + { + return IVAS_ERR_END_OF_FILE; + } + + rtp->rtpPacket.buffer = rtp->packet; + rtp->rtpPacket.length = packetLen; + + 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\n" ); + 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--; + } + } + +#ifdef RTP_S4_251135_CR26253_0016_REV1 + error = IVAS_RTP_UNPACK_PullFrame( rtp->hUnpack, &rtp->codecId, &srInfo, &packedFrame, rtpTimeStamp, rtpSequenceNumber, &speechLostIndicated ); +#else + error = IVAS_RTP_UNPACK_PullFrame( rtp->hUnpack, &rtp->codecId, &packedFrame, rtpTimeStamp, rtpSequenceNumber, &speechLostIndicated ); +#endif + if ( error != IVAS_ERR_OK ) + { + fprintf( stderr, "failed to pull frame after unpack\n" ); + return error; + } + + *auSizeBits = (int16_t) ( packedFrame.length * 8 ); + *qBit = !speechLostIndicated; + rtp->numFramesInPacket--; + *nextPacketRcvTime_ms += 20; + + 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: + { + error = IVAS_DEC_feedSinglePIorientation( hIvasDec, true, &piData->data.scene.orientation ); + } + break; + + case IVAS_PI_DEVICE_ORIENTATION_COMPENSATED: + { + error = IVAS_DEC_feedSinglePIorientation( hIvasDec, true, &piData->data.deviceCompensated.orientation ); + } + break; + + default: + { + fprintf( stderr, "Unhandled PI data of type : %d\n", piDataType ); + } + break; + } + if ( error != IVAS_ERR_OK ) + { + return error; + } + } + } + + return IVAS_ERR_OK; +} +#endif /* IVAS_RTPDUMP */ /*------------------------------------------------------------------------------------------* * Local functions prototypes @@ -2883,8 +3048,7 @@ static ivas_error printBitstreamInfoVoip( ivas_error error = IVAS_ERR_OK; FILE *f_rtpstream = NULL; #ifdef IVAS_RTPDUMP - IVAS_RTPDUMP_DEPACKER rtpdumpDepacker; - IVAS_RTPDUMP_DEPACKER_ERROR rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_NO_ERROR; + IVAS_RTP ivasRtp; #else EVS_RTPDUMP_DEPACKER rtpdumpDepacker; EVS_RTPDUMP_DEPACKER_ERROR rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_NO_ERROR; @@ -2892,21 +3056,16 @@ static ivas_error printBitstreamInfoVoip( uint8_t au[( IVAS_MAX_BITS_PER_FRAME + 7 ) >> 3]; int16_t auSizeBits; uint8_t *auPtr = NULL; -#ifdef IVAS_RTPDUMP - bool evsIvasModeBit; - uint16_t bitrateIndex; - bool ivasIndicatorBit; - bool isGoodFrame; -#else bool isAMRWB_IOmode; uint16_t frameTypeIndex; bool qBit; -#endif 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: @@ -2920,15 +3079,19 @@ static ivas_error printBitstreamInfoVoip( } #ifdef IVAS_RTPDUMP - rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_open( &rtpdumpDepacker, f_rtpstream ); + if ( ( error = IVAS_RTP_Init( &ivasRtp, f_rtpstream ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "error in IVAS_RTP_Init(): %d\n", error ); + goto cleanup; + } #else rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_open( &rtpdumpDepacker, f_rtpstream, arg.inputFormat == IVAS_DEC_INPUT_FORMAT_RTPDUMP_HF ); -#endif if ( rtpdumpDepackerError != EVS_RTPDUMP_DEPACKER_NO_ERROR ) { fprintf( stderr, "error in EVS_RTPDUMP_DEPACKER_open(): %d\n", rtpdumpDepackerError ); goto cleanup; } +#endif break; case IVAS_DEC_INPUT_FORMAT_G192: auPtr = au; @@ -2944,17 +3107,13 @@ static ivas_error printBitstreamInfoVoip( if ( arg.inputFormat == IVAS_DEC_INPUT_FORMAT_G192 ) { error = BS_Reader_ReadVoipFrame_compact( hBsReader, au, &auSizeBits, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms ); -#ifdef IVAS_RTPDUMP - isGoodFrame = 1; /* good frame for INPUT_FORMAT_G192 */ -#else qBit = 1; /* good q_bit for INPUT_FORMAT_G192 */ -#endif } else { auPtr = au; /* might have been set to RTP packet in prev call */ #ifdef IVAS_RTPDUMP - rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, &evsIvasModeBit, &bitrateIndex, &ivasIndicatorBit, &isGoodFrame, &auPtr, (uint16_t *) &auSizeBits ); + error = readNextFrame( &ivasRtp, NULL, 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 @@ -2967,11 +3126,7 @@ static ivas_error printBitstreamInfoVoip( fprintf( stderr, "failed to read first RTP packet\n" ); goto cleanup; } -#ifdef IVAS_RTPDUMP - } while ( !isGoodFrame || auSizeBits < MIN_NUM_BITS_ACTIVE_FRAME || auSizeBits == NUM_BITS_SID_IVAS_5K2 ); -#else } while ( !qBit || auSizeBits < MIN_NUM_BITS_ACTIVE_FRAME || auSizeBits == NUM_BITS_SID_IVAS_5K2 ); -#endif BS_Reader_Rewind( hBsReader ); @@ -2986,7 +3141,7 @@ static ivas_error printBitstreamInfoVoip( cleanup: #ifdef IVAS_RTPDUMP - IVAS_RTPDUMP_DEPACKER_close( &rtpdumpDepacker ); + IVAS_RTP_Term( &ivasRtp ); #else EVS_RTPDUMP_DEPACKER_close( &rtpdumpDepacker ); #endif @@ -3057,23 +3212,17 @@ static ivas_error decodeVoIP( int16_t i; FILE *f_rtpstream = NULL; #ifdef IVAS_RTPDUMP - IVAS_RTPDUMP_DEPACKER rtpdumpDepacker; - IVAS_RTPDUMP_DEPACKER_ERROR rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_NO_ERROR; + IVAS_RTP ivasRtp; #else EVS_RTPDUMP_DEPACKER rtpdumpDepacker; EVS_RTPDUMP_DEPACKER_ERROR rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_NO_ERROR; #endif uint8_t *auPtr = NULL; -#ifdef IVAS_RTPDUMP - bool evsIvasModeBit; - uint16_t bitrateIndex; - bool ivasIndicatorBit; - bool isGoodFrame; -#else +#ifndef IVAS_RTPDUMP bool isAMRWB_IOmode; uint16_t frameTypeIndex; - bool qBit; #endif + bool qBit; IVAS_DEC_BS_FORMAT bsFormat = IVAS_DEC_BS_UNKOWN; IsmFileWriter *ismWriters[IVAS_MAX_NUM_OBJECTS]; @@ -3110,7 +3259,9 @@ static ivas_error decodeVoIP( delayNumSamples_orig[0] = -1; +#ifndef IVAS_RTPDUMP rtpdumpDepacker.rtpdump = NULL; +#endif switch ( arg.inputFormat ) { case IVAS_DEC_INPUT_FORMAT_RTPDUMP: @@ -3128,10 +3279,9 @@ static ivas_error decodeVoIP( } #ifdef IVAS_RTPDUMP - rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_open( &rtpdumpDepacker, f_rtpstream ); - if ( rtpdumpDepackerError != IVAS_RTPDUMP_DEPACKER_NO_ERROR ) + if ( ( error = IVAS_RTP_Init( &ivasRtp, f_rtpstream ) ) != IVAS_ERR_OK ) { - fprintf( stderr, "error in IVAS_RTPDUMP_DEPACKER_open(): %d\n", rtpdumpDepackerError ); + fprintf( stderr, "error in IVAS_RTP_Init(): %d\n", error ); goto cleanup; } #else @@ -3178,17 +3328,13 @@ static ivas_error decodeVoIP( if ( arg.inputFormat == IVAS_DEC_INPUT_FORMAT_G192 ) { error = BS_Reader_ReadVoipFrame_compact( hBsReader, au, &auSize, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms ); -#ifdef IVAS_RTPDUMP - isGoodFrame = 1; /* good frame for INPUT_FORMAT_G192 */ -#else qBit = 1; /* good q_bit for INPUT_FORMAT_G192 */ -#endif } else { auPtr = au; /* might have been set to RTP packet in prev call */ #ifdef IVAS_RTPDUMP - rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, &evsIvasModeBit, &bitrateIndex, &ivasIndicatorBit, &isGoodFrame, &auPtr, (uint16_t *) &auSize ); + error = readNextFrame( &ivasRtp, hIvasDec, auPtr, &auSize, &rtpTimeStamp, &rtpSequenceNumber, &nextPacketRcvTime_ms, &qBit ); #else rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, &isAMRWB_IOmode, &frameTypeIndex, &qBit, &auPtr, (uint16_t *) &auSize ); #endif @@ -3197,7 +3343,7 @@ static ivas_error decodeVoIP( rtpTimeStamp = rtpTimeStamp / 16; } #ifdef IVAS_RTPDUMP - if ( error != IVAS_ERR_OK || rtpdumpDepackerError != IVAS_RTPDUMP_DEPACKER_NO_ERROR ) + if ( error != IVAS_ERR_OK ) #else if ( error != IVAS_ERR_OK || rtpdumpDepackerError != EVS_RTPDUMP_DEPACKER_NO_ERROR ) #endif @@ -3352,11 +3498,7 @@ static ivas_error decodeVoIP( while ( nextPacketRcvTime_ms <= systemTime_ms ) { /* feed the previous read packet into the receiver now */ -#ifdef IVAS_RTPDUMP - error = IVAS_DEC_VoIP_FeedFrame( hIvasDec, auPtr, auSize, rtpSequenceNumber, rtpTimeStamp, nextPacketRcvTime_ms, isGoodFrame ); -#else error = IVAS_DEC_VoIP_FeedFrame( hIvasDec, auPtr, auSize, rtpSequenceNumber, rtpTimeStamp, nextPacketRcvTime_ms, qBit ); -#endif if ( error != IVAS_ERR_OK ) { fprintf( stderr, "\nError in IVAS_DEC_VoIP_FeedFrame: %s\n", IVAS_DEC_GetErrorMessage( error ) ); @@ -3369,30 +3511,16 @@ static ivas_error decodeVoIP( { error = BS_Reader_ReadVoipFrame_compact( hBsReader, au, &auSize, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms ); -#ifdef IVAS_RTPDUMP - isGoodFrame = 1; /* good frame for VOIP_G192_RTP */ -#else qBit = 1; /* good q_bit for VOIP_G192_RTP */ -#endif } else { auPtr = au; /* might have been set to RTP packet in prev call */ #ifdef IVAS_RTPDUMP - rtpdumpDepackerError = IVAS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, - &nextPacketRcvTime_ms, - &evsIvasModeBit, &bitrateIndex, &ivasIndicatorBit, - &isGoodFrame, &auPtr, (uint16_t *) &auSize ); + error = readNextFrame( &ivasRtp, hIvasDec, au, &auSize, &rtpTimeStamp, &rtpSequenceNumber, &nextPacketRcvTime_ms, &qBit ); + /* IVAS RTP payload format has timescale 16000, JBM uses 1000 internally */ rtpTimeStamp = rtpTimeStamp / 16; - - /* feed PI data to decoder handle */ - error = IVAS_DEC_feedPIdata( hIvasDec, &rtpdumpDepacker.PIdataDepackerState ); - if ( error != IVAS_ERR_OK ) - { - fprintf( stderr, "\nError in IVAS_DEC_feedPIdata: %s\n", IVAS_DEC_GetErrorMessage( error ) ); - goto cleanup; - } #else rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_readNextFrame( &rtpdumpDepacker, &rtpSequenceNumber, &rtpTimeStamp, &nextPacketRcvTime_ms, @@ -3403,7 +3531,7 @@ static ivas_error decodeVoIP( #endif } #ifdef IVAS_RTPDUMP - if ( error == IVAS_ERR_END_OF_FILE || rtpdumpDepackerError == IVAS_RTPDUMP_DEPACKER_EOF ) + if ( error == IVAS_ERR_END_OF_FILE ) #else if ( error == IVAS_ERR_END_OF_FILE || rtpdumpDepackerError == EVS_RTPDUMP_DEPACKER_EOF ) #endif @@ -3420,13 +3548,6 @@ static ivas_error decodeVoIP( fprintf( stderr, "\nError in BS_Reader_ReadVoipFrame_compact, error code: %d\n", error ); goto cleanup; } -#ifdef IVAS_RTPDUMP - else if ( rtpdumpDepackerError != IVAS_RTPDUMP_DEPACKER_NO_ERROR ) - { - fprintf( stderr, "\nError in IVAS_RTPDUMP_DEPACKER_readNextFrame, error code: %d\n", error ); - goto cleanup; - } -#endif } /* we are finished when all packets have been received and jitter buffer is empty */ @@ -3770,7 +3891,7 @@ static ivas_error decodeVoIP( cleanup: #ifdef IVAS_RTPDUMP - IVAS_RTPDUMP_DEPACKER_close( &rtpdumpDepacker ); + IVAS_RTP_Term( &ivasRtp ); #else EVS_RTPDUMP_DEPACKER_close( &rtpdumpDepacker ); #endif diff --git a/apps/encoder.c b/apps/encoder.c index fe444c4d11..861250a5a0 100644 --- a/apps/encoder.c +++ b/apps/encoder.c @@ -39,8 +39,8 @@ #include "jbm_file_reader.h" #include "masa_file_reader.h" #ifdef IVAS_RTPDUMP -#include "evs_rtp_payload.h" -#include "ivas_rtp_payload.h" +#include "ivas_rtp_api.h" +#include "ivas_rtp_pi_data.h" #include "rotation_file_reader.h" #endif #ifdef DEBUGGING @@ -48,7 +48,6 @@ #endif #include "wmc_auto.h" - #define WMC_TOOL_SKIP /*------------------------------------------------------------------------------------------* @@ -153,6 +152,7 @@ typedef struct bool ism_extended_metadata; #ifdef IVAS_RTPDUMP bool rtpdumpOutput; + uint32_t numFramesPerPacket; char *sceneOrientationTrajFileName; char *deviceOrientationTrajFileName; #endif @@ -222,8 +222,7 @@ int main( #ifdef IVAS_RTPDUMP FILE *f_rtpstream = NULL; - IVAS_RTPDUMP_PACKER *rtpdumpPacker = NULL; - IVAS_RTPDUMP_PACKER_ERROR rtpdumpPackerError = IVAS_RTPDUMP_PACKER_NO_ERROR; + IVAS_RTP_PACK_HANDLE hPack = NULL; #endif /*------------------------------------------------------------------------------------------* @@ -623,19 +622,38 @@ int main( 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 */ f_rtpstream = fopen( arg.outputBitstreamFilename, "wb" ); - if ( f_rtpstream == NULL ) { fprintf( stderr, "could not open: %s\n", arg.outputBitstreamFilename ); goto cleanup; } - rtpdumpPackerError = IVAS_RTPDUMP_PACKER_open( &rtpdumpPacker, f_rtpstream ); - if ( rtpdumpPackerError != IVAS_RTPDUMP_PACKER_NO_ERROR ) + error = IVAS_RTP_PACK_Open( &hPack, &packCfg ); + if ( error != IVAS_ERR_OK ) { - fprintf( stderr, "error in IVAS_RTPDUMP_PACKER_open(): %d\n", rtpdumpPackerError ); + 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; } } @@ -669,6 +687,7 @@ int main( int16_t numSamplesRead = 0; uint16_t bitStream[IVAS_MAX_BITS_PER_FRAME]; + uint16_t numBits = 0; #ifdef DEBUG_SBA #ifdef DEBUG_AGC @@ -710,7 +729,11 @@ int main( goto cleanup; } +#ifdef IVAS_RTPDUMP + if ( ( numSamplesRead == 0 ) && ( IVAS_RTP_PACK_GetNumFrames( hPack ) == 0 ) ) +#else if ( numSamplesRead == 0 ) +#endif { /* end of input data */ break; @@ -838,43 +861,111 @@ int main( } #ifdef IVAS_RTPDUMP - if ( rtpdumpPacker && rtpdumpPacker->rtpdump ) + if ( hPack ) { + uint8_t au[IVAS_MAX_BITS_PER_FRAME / 8]; + uint8_t packet[IVAS_NOMINAL_RTP_BYTES_PER_FRAME * MAX_FRAMES_PER_RTP_PACKET]; + uint32_t n = numBits / 8, x = 0, numFramesInPayload = 0, packetLength = 0; + uint16_t *bitstrm = bitStream; + IVAS_DATA_BUFFER packedFrame = { 0, 0, NULL }; + IVAS_DATA_BUFFER rtpPacket = { 0, 0, NULL }; + + packedFrame.capacity = sizeof( au ); + packedFrame.buffer = au; + rtpPacket.capacity = sizeof( packet ); + rtpPacket.buffer = packet; + + /* Pack Encoded Stream */ + while ( n ) + { + x = ( ( bitstrm[0] << 7 ) | ( bitstrm[1] << 6 ) | ( bitstrm[2] << 5 ) | ( bitstrm[3] << 4 ) | + ( bitstrm[4] << 3 ) | ( bitstrm[5] << 2 ) | ( bitstrm[6] << 1 ) | ( bitstrm[7] << 0 ) ); + packedFrame.buffer[packedFrame.length++] = (uint8_t) x; + bitstrm += 8; + n--; + } + + /* AMWWB-IO has non octet sized bitstream */ + if ( numBits & 0x7 ) + { + x = 0; + for ( n = 0; n < (uint32_t) ( numBits & 0x7 ); n++ ) + { + x |= (uint8_t) ( bitstrm[n] << ( 7 - n ) ); + } + packedFrame.buffer[packedFrame.length++] = (uint8_t) x; + } + + /* Push Encoded Stream to */ + if ( ( error = IVAS_RTP_PACK_PushFrame( hPack, + IVAS_isImmersiveFormat( hIvasEnc ) ? 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 ) { - if ( ( error = HeadRotationFileReading( sceneOrientationFileReader, &rtpdumpPacker->piDataPacker.sceneOrientationQuat, NULL ) ) != IVAS_ERR_OK ) + 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; } - rtpdumpPacker->piDataPacker.sceneOrientationPresent = true; + + 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 ) { - if ( ( error = HeadRotationFileReading( deviceOrientationFileReader, &rtpdumpPacker->piDataPacker.deviceOrientationQuat, NULL ) ) != IVAS_ERR_OK ) + 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; } - rtpdumpPacker->piDataPacker.deviceOrientationPresent = true; - } - /* PI presence */ - IVAS_RTPDUMP_PACKER_determinePIpresence( rtpdumpPacker ); + 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; + } + } - /* write rtpdump */ - rtpdumpPackerError = IVAS_RTPDUMP_PACKER_writeNextFrame( rtpdumpPacker, bitStream, numBits, - IVAS_isImmersiveFormat( hIvasEnc ), 0, false, NO_BANDWIDTH_REQUEST, NO_FORMAT_REQUEST ); - if ( rtpdumpPackerError != IVAS_RTPDUMP_PACKER_NO_ERROR ) + if ( ( numSamplesRead < pcmBufSize ) || IVAS_RTP_PACK_GetNumFrames( hPack ) == arg.numFramesPerPacket ) { - fprintf( stderr, "IVAS_RTPDUMP_PACKER_writeNextFrame() failed, error code: %d\n", rtpdumpPackerError ); - goto cleanup; - } + /* 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; + } - IVAS_RTPDUMP_PACKER_resetPIdata( rtpdumpPacker ); + /* File Format = Packet Length (uint32_t) + Packet bytes */ + packetLength = (uint32_t) rtpPacket.length; + fwrite( &packetLength, sizeof( packetLength ), 1, f_rtpstream ); + fwrite( rtpPacket.buffer, sizeof( uint8_t ), packetLength, f_rtpstream ); + } } else { @@ -966,9 +1057,9 @@ cleanup: } #ifdef IVAS_RTPDUMP - if ( rtpdumpPacker ) + if ( hPack ) { - IVAS_RTPDUMP_PACKER_close( &rtpdumpPacker ); + IVAS_RTP_PACK_Close( &hPack ); } if ( sceneOrientationFileReader ) @@ -980,6 +1071,11 @@ cleanup: { RotationFileReader_close( &deviceOrientationFileReader ); } + + if ( f_rtpstream ) + { + fclose( f_rtpstream ); + } #endif IVAS_ENC_Close( &hIvasEnc ); @@ -1858,9 +1954,25 @@ static bool parseCmdlIVAS_enc( else if ( strcmp( argv_to_upper, "-RTPDUMP" ) == 0 ) { + i++; arg->rtpdumpOutput = true; - fprintf( stdout, "Output format: RTPDump\n" ); - ++i; + 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 > MAX_FRAMES_PER_RTP_PACKET ) + { + fprintf( stderr, "numFramesPerPacket(%d) exceeds max frames per packet (%d) \n", arg->numFramesPerPacket, MAX_FRAMES_PER_RTP_PACKET ); + arg->numFramesPerPacket = 1; + } + } + } + fprintf( stdout, "Output format: RTPDump using %d frames/packet \n", arg->numFramesPerPacket ); } /*-----------------------------------------------------------------* @@ -2133,10 +2245,10 @@ static void usage_enc( void ) 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, "-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. \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 diff --git a/lib_com/ivas_error.h b/lib_com/ivas_error.h index a2f893ed78..24b576d696 100644 --- a/lib_com/ivas_error.h +++ b/lib_com/ivas_error.h @@ -150,6 +150,15 @@ typedef enum IVAS_ERR_LC3PLUS_INVALID_BITRATE, IVAS_ERR_INVALID_SPLIT_REND_CONFIG, + /*----------------------------------------* + * rtp errors * + *----------------------------------------*/ + IVAS_ERR_UNDERFLOW = 0x7000, + IVAS_ERR_PI_DATA_WITH_NO_INPUT_FRAME, + IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, + IVAS_ERR_UNPACK_PI_DATA, + IVAS_ERR_RTP_UNSUPPORTED_FRAME, + /*----------------------------------------* * unknown error * *----------------------------------------*/ diff --git a/lib_dec/lib_dec.c b/lib_dec/lib_dec.c index a476430478..f9c9646c9f 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -3742,7 +3742,7 @@ ivas_error IVAS_DEC_Flush( ivas_error IVAS_DEC_feedSinglePIorientation( IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ - const bool *isOrientationSaved, /* i : flag to indicate if an orientation for this PI type was previously saved */ + 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 */ ) { @@ -3750,7 +3750,7 @@ ivas_error IVAS_DEC_feedSinglePIorientation( ivas_error error = IVAS_ERR_OK; IVAS_QUATERNION savedInvOrientation; - if ( *isOrientationSaved ) + if ( isOrientationSaved ) { if ( !hIvasDec->st_ivas->hExtOrientationData ) { @@ -3797,13 +3797,13 @@ ivas_error IVAS_DEC_feedPIdata( ivas_error error = IVAS_ERR_OK; /* scene orientation */ - if ( ( error = IVAS_DEC_feedSinglePIorientation( hIvasDec, &PIdataDepackerState->sceneOrientationSaved, &PIdataDepackerState->sceneOrientationQuat ) ) != IVAS_ERR_OK ) + 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 ) + if ( ( error = IVAS_DEC_feedSinglePIorientation( hIvasDec, PIdataDepackerState->deviceOrientationSaved, &PIdataDepackerState->deviceOrientationQuat ) ) != IVAS_ERR_OK ) { return error; } diff --git a/lib_dec/lib_dec.h b/lib_dec/lib_dec.h index 40a5403373..2e98d9e0f1 100644 --- a/lib_dec/lib_dec.h +++ b/lib_dec/lib_dec.h @@ -335,9 +335,9 @@ ivas_error IVAS_DEC_Flush( #ifdef IVAS_RTPDUMP ivas_error IVAS_DEC_feedSinglePIorientation( - IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ - const 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_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( diff --git a/lib_util/ivas_bpool.c b/lib_util/ivas_bpool.c new file mode 100644 index 0000000000..cea33509ff --- /dev/null +++ b/lib_util/ivas_bpool.c @@ -0,0 +1,154 @@ +/****************************************************************************************************** + + (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. + +*******************************************************************************************************/ +#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 ) + { + 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_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 idx; + mtx_lock( &handle->lock ); + idx = handle->numFreeBuffers; + mtx_unlock( &handle->lock ); + return idx; +} diff --git a/lib_util/ivas_bpool.h b/lib_util/ivas_bpool.h new file mode 100644 index 0000000000..c4cd45a736 --- /dev/null +++ b/lib_util/ivas_bpool.h @@ -0,0 +1,58 @@ +/****************************************************************************************************** + + (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. + +*******************************************************************************************************/ + +#ifndef IVAS_BPOOL_H +#define IVAS_BPOOL_H + +#include +#include +#include "ivas_error.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 0000000000..db038938a5 --- /dev/null +++ b/lib_util/ivas_queue.c @@ -0,0 +1,126 @@ +/****************************************************************************************************** + + (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. + +*******************************************************************************************************/ + +#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 all free-up all allocated memory */ +void QUEUE_Destroy( QUEUE_HANDLE *pHandle ) +{ + if ( 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 ) +{ + return handle->front; +} + +/* returns the last element in the queue */ +NODE *QUEUE_Back( QUEUE_HANDLE handle ) +{ + return handle->back; +} + +/* return the number of elements in the queue */ +size_t QUEUE_Size( QUEUE_HANDLE handle ) +{ + return handle->size; +} diff --git a/lib_util/ivas_queue.h b/lib_util/ivas_queue.h new file mode 100644 index 0000000000..8a5d4d1017 --- /dev/null +++ b/lib_util/ivas_queue.h @@ -0,0 +1,68 @@ +/****************************************************************************************************** + + (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. + +*******************************************************************************************************/ + +#ifndef IVAS_QUEUE_H +#define IVAS_QUEUE_H + +#include +#include "ivas_error.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 index 5b008a04ca..6b8ebb470a 100644 --- a/lib_util/ivas_rtp_api.h +++ b/lib_util/ivas_rtp_api.h @@ -45,37 +45,46 @@ extern "C" { #endif -/* - * +-----------------------+---------------------+--------------------+----------+ - * | 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 - * + /* + * +-----------------------+---------------------+--------------------+----------+ + * | 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 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 IVAS_NOMINAL_RTP_BYTES_PER_FRAME ( ( IVAS_MAX_BITS_PER_FRAME + ( IVAS_MAX_BITS_PER_FRAME / 5 ) ) / 8 ) -#define NO_BITRATE_REQ ( 0u ) /* If no bitrate is requested from remote */ +#define NO_BITRATE_REQ ( 0u ) /* If no bitrate is requested from remote */ /* IVAS Codec Types */ typedef enum @@ -193,41 +202,44 @@ extern "C" } IVAS_RTP_SUBFORMAT; /* Split Rendering Requests */ - typedef struct { - 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: 28; /* reserved */ + 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 */ + 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_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 */ + 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 */ +#endif /* RTP_S4_251135_CR26253_0016_REV1 */ } IVAS_RTP_REQUEST_VALUE; /* Template for pi data types, all defined pi data follow this template @@ -258,12 +270,27 @@ extern "C" 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 */ + 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 @@ -271,9 +298,20 @@ extern "C" */ typedef struct { - uint32_t maxFramesPerPacket; /* maximum no of frame per packet desired during the session */ + uint32_t maxFramesPerPacket; /* maximum no of frame per packet desired during the session */ } IVAS_RTP_PACK_CONFIG; + /* 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 */ + ); + /* 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 */ @@ -310,11 +348,19 @@ extern "C" * */ 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) */ + 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 @@ -334,10 +380,23 @@ extern "C" * */ 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 */ + 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 */ @@ -381,7 +440,7 @@ extern "C" * * // Read the frames in payload and feed to decoder * while (nFrames-- > 0) { - * err = IVAS_RTP_UNPACK_PullFrame(hIvasUnpack, &recCodecId, &frame, &frameTs, &seqNum); + * 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; } @@ -415,14 +474,69 @@ extern "C" */ 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 */ + 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. Caller must extract RTP header and header + * extension and feed Ivas Packet 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. + * + * Example usage : - + * ================== + * err = IVAS_RTP_UNPACK_PushPacket(hIvasUnpack, packet, rtpTs, seqNum, + * &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 * @@ -444,19 +558,24 @@ extern "C" */ ivas_error IVAS_RTP_UNPACK_PullFrame( IVAS_RTP_UNPACK_HANDLE hIvasUnpack, /* i/o : IVAS rtp unpacker handle */ - IVAS_RTP_CODEC *receivedCodecId, /* i : Codec type (IVAS/EVS) */ + 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 */ uint32_t *timestamp, /* o : timestamp in RTP Clock @ 16KHz */ - uint16_t *sequenceNumber /* o : sequence number from rtp header */ + uint16_t *sequenceNumber, /* o : sequence number from rtp header */ + bool *speechLostIndicated /* o : Is current frame indicated as Lost */ ); /* 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 */ - uint32_t *timestamp /* o : timestamp in RTP Clock @ 16KHz */ + 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 */ ); #ifdef __cplusplus diff --git a/lib_util/ivas_rtp_internal.h b/lib_util/ivas_rtp_internal.h new file mode 100644 index 0000000000..b74bdbe8d5 --- /dev/null +++ b/lib_util/ivas_rtp_internal.h @@ -0,0 +1,136 @@ +/****************************************************************************************************** + + (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. + +*******************************************************************************************************/ + +#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 ) + +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 = 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 index d952450b52..7bbc86171d 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -29,1068 +29,1767 @@ the United Nations Convention on Contracts on the International Sales of Goods. *******************************************************************************************************/ - -#include #include +#include #include -#include "evs_rtp_payload.h" -#include "ivas_rtp_payload.h" -#include "ivas_prot.h" -#include "prot.h" +#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_FRAMES_IN_QUEUE ( 32 ) +#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 */ + +/* Heuristically defined max values for no of frames per packet and no of pi data per packet */ +#define MAX_SUPPORTED_PI_DATA_SIZE ( 32 ) /* Currently no PI data exceeds 32 bytes */ +#define MAX_SUPPORTED_IVAS_FRAMES_PER_PACKET ( 25u ) +#define MAX_SUPPORTED_PI_FRAMES_PER_PACKET ( 150u ) + +/* 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; -/*-----------------------------------------------------------------------* - * writeByteToRtp() - * - * Write a single byte (value) to RTP packet. - *-----------------------------------------------------------------------*/ - -static void writeByteToRtp( - IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ - char value /* i : Value to be written to the RTP packet */ -) +static void initRequests( IVAS_RTP_REQUEST_VALUE *requests ) { - self->rtpPacket.data[self->writingIndex] = value; - ++self->writingIndex; - self->rtpPacket.payloadSize += 1; + 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 */ } - -/*-----------------------------------------------------------------------* - * determineBitrate() - * - * Determine bitrate based on the number of encoded bits. - *-----------------------------------------------------------------------*/ - -static void determineBitrate( - const int32_t numBits, /* i : Number of bits contained in the data frame */ - int32_t *frameBitrate /* o : Bitrate of the data frame */ -) +static void initPiDataFrame( PIDATA_FRAME *piDataFrm ) { - *frameBitrate = numBits * IVAS_NUM_FRAMES_PER_SEC; + /* 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; + +struct IVAS_RTP_PACK +{ + mtx_t lock; /* Lock to handle concurrent API invocation */ + RTP_HEADER header; + BPOOL_HANDLE nodePool; + IVAS_RTP_PACK_CONFIG initConfig; + IVAS_RTP_REQUEST_VALUE requests[IVAS_REQUEST_MAX]; + QUEUE_HANDLE frameQ; /* frames queue */ +}; + +typedef struct +{ + IVAS_RTP_CODEC codecId; /* Coded frame type (IVAS/EVS) */ + uint32_t auNumBits; /* Actual frame size in bits */ + bool speechLostIndicated; /* Speech Lost indicated */ +#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; -/*-----------------------------------------------------------------------* - * ivasPayload_parseToc() - * - * Parse a single ToC byte for an IVAS payload. - *-----------------------------------------------------------------------*/ - -static void ivasPayload_parseToc( - uint8_t toc, /* i : ToC byte to be parsed */ - bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ - bool *frameFollowing, /* o : (F) bit in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ - bool *isGoodFrame, /* o : Good frame indicator (set by the Q-bit in AMR-WB IO mode, otherwise always set to true) */ - int32_t *bitrate /* o : Bitrate of the data frame indicated in the ToC byte */ +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 lock; /* Lock to handle concurrent API invocation */ + RTP_HEADER header; + BPOOL_HANDLE nodePool; + 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, 40 +}; + + +/* 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 */ ) { - bool isIvasToc = ( toc & 0x30 ) == 0x10; // xx01 xxxx - if ( !isIvasToc ) - { - evsHeaderFullPayload_parseToc( toc, evsIvasModeBit, frameFollowing, bitrateIndex, ivasIndicatorBit, isGoodFrame, bitrate ); - } - else + RTP_HEADER *header = &hPack->header; + + if ( numCC > 15 ) { - *evsIvasModeBit = false; - *ivasIndicatorBit = true; - *isGoodFrame = true; /* assume good frame for IVAS */ - *frameFollowing = ( toc & 0x40 ) != 0; - *bitrateIndex = toc & 0x0f; - *bitrate = IVASmode2rate[*bitrateIndex]; + 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 ); -/*-----------------------------------------------------------------------* - * ivasPayload_parsePItype() - * - * Parse a single PI type and set the PIdataCurrentFrame handle accordingly. - *-----------------------------------------------------------------------*/ - -static void ivasPayload_parsePItype( - char *PIframeDataBeginning, /* i : Pointer to the beginning of the PI data section in the RTP packet */ - uint16_t PIframeSizeBytes, /* i : PI data frame size in bytes */ - uint16_t parsedPIframeSizeBytes, /* i : Size of previously parsed PI frame data section in bytes */ - uint16_t PIframeType, /* i : Type of the PI data frame */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data handle for the current audio frame */ -) -{ - if ( PIframeType == PI_SCENE_ORIENTATION ) - { - PIdataCurrentFrame->sceneOrientation = PIframeDataBeginning + parsedPIframeSizeBytes; - } - else if ( PIframeType == PI_DEVICE_ORIENTATION_COMPENSATED ) - { - PIdataCurrentFrame->deviceOrientationCompensated = PIframeDataBeginning + parsedPIframeSizeBytes; - } - else if ( PIframeType == PI_DEVICE_ORIENTATION_UNCOMPENSATED ) - { - PIdataCurrentFrame->deviceOrientationUncompensated = PIframeDataBeginning + parsedPIframeSizeBytes; - } - else if ( PIframeType == PI_ACOUSTIC_ENVIRONMENT ) + if ( ( numExtensionBytes > 0 ) && ( extData != NULL ) ) { - if ( PIframeSizeBytes == PI_ACOUSTIC_ENVIRONMENT_ID_SIZE_BYTES ) - { - PIdataCurrentFrame->acousticEnvironmentId = PIframeDataBeginning + parsedPIframeSizeBytes; - } - else if ( PIframeSizeBytes == PI_ACOUSTIC_ENVIRONMENT_ONLY_LATE_REVERB_SIZE_BYTES ) - { - PIdataCurrentFrame->acousticEnvironmentOnlyLateReverb = PIframeDataBeginning + parsedPIframeSizeBytes; - } - else if ( PIframeSizeBytes == PI_ACOUSTIC_ENVIRONMENT_LATE_REVERB_AND_EARLY_REFLECTIONS_SIZE_BYTES ) + header->extHeaderId = extHeaderId; + header->extData = realloc( header->extData, numExtensionBytes ); + if ( header->extData == NULL ) { - PIdataCurrentFrame->acousticEnvironmentLateReverbAndEarlyReflections = PIframeDataBeginning + parsedPIframeSizeBytes; + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "failed to allocate extdata" ); } - } - else if ( PIframeType == PI_NO_DATA ) - { - /* no data */ + memcpy( header->extData, extData, numExtensionBytes ); + header->extension = true; + header->numExtUnits = numExtensionBytes / sizeof( uint32_t ); } else { - /* reserved */ + header->numExtUnits = 0; + header->extension = false; } -} - - -/*-----------------------------------------------------------------------* - * ivasPayload_PIaddHeaderBytes() - * - * Add PI header section bytes to the PI data pointers for the current audio frame. - *-----------------------------------------------------------------------*/ -static void ivasPayload_PIaddHeaderBytes( - uint16_t PIheaderSizeBytes, /* i : Size of the PI header section in bytes */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame /* o : PI data handle for the current audio frame */ -) -{ - if ( PIdataCurrentFrame->sceneOrientation != NULL ) - { - PIdataCurrentFrame->sceneOrientation += PIheaderSizeBytes; - } - if ( PIdataCurrentFrame->deviceOrientationCompensated != NULL ) - { - PIdataCurrentFrame->deviceOrientationCompensated += PIheaderSizeBytes; - } - if ( PIdataCurrentFrame->deviceOrientationUncompensated != NULL ) - { - PIdataCurrentFrame->deviceOrientationUncompensated += PIheaderSizeBytes; - } - if ( PIdataCurrentFrame->acousticEnvironmentId != NULL ) - { - PIdataCurrentFrame->acousticEnvironmentId += PIheaderSizeBytes; - } - if ( PIdataCurrentFrame->acousticEnvironmentOnlyLateReverb != NULL ) - { - PIdataCurrentFrame->acousticEnvironmentOnlyLateReverb += PIheaderSizeBytes; - } - if ( PIdataCurrentFrame->acousticEnvironmentLateReverbAndEarlyReflections != NULL ) - { - PIdataCurrentFrame->acousticEnvironmentLateReverbAndEarlyReflections += PIheaderSizeBytes; - } + return IVAS_ERR_OK; } - -/*-----------------------------------------------------------------------* - * ivasPayload_convertFromQ15() - * - * Convert a Q15 encoded value to a float value. - *-----------------------------------------------------------------------*/ - -static float ivasPayload_convertFromQ15( uint16_t Q15Value ) +static void InitRtpHeader( + RTP_HEADER *header /* RTP header structure */ +) { - float temp; - temp = (float) ( Q15Value & 0x7FFF ) / 0x7FFF; - return ( Q15Value & 0x8000 ) ? -temp : temp; + time_t t; + memset( header, 0, sizeof( *header ) ); + srand( (uint32_t) time( &t ) ); + header->version = 2; + header->seqNumber = rand() & 0xFFFF; } - -/*-----------------------------------------------------------------------* - * ivasPayload_convertToQ15() - * - * Convert a float value into a Q15 encoded value. - *-----------------------------------------------------------------------*/ - -static uint16_t ivasPayload_convertToQ15( float value ) +static ivas_error PackRtpHeader( + RTP_HEADER *header, + IVAS_DATA_BUFFER *buf ) { - uint16_t temp; - temp = (uint16_t) ( fabsf( value ) * 0x7FFF ); - if ( value >= 0.0f ) + 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 ) { - temp = temp & 0x7FFF; + return IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient output buffer for RTP header packing" ); } - else + + /* + +---+---+---+---+---+---+---+---+ + | 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 ) { - temp = temp | 0x8000; + /* 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--; } - return temp; -} + 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 ); -/*-----------------------------------------------------------------------* - * ivasPayload_convertOrientationToQ15() - * - * Convert a float quaternion into a Q15 encoded quaternion. - *-----------------------------------------------------------------------*/ + memcpy( &buf->buffer[nBytes], header->extData, header->numExtUnits * 4 ); + nBytes += header->numExtUnits; + } -static void ivasPayload_convertOrientationToQ15( - IVAS_QUATERNION orientation, /* i : Input orientation in quaternion to be converted to Q15 */ - IVAS_QUATERNION_Q15 *orientationQ15 /* o : Ouput orientation in Q15 quaternion */ -) -{ - orientationQ15->w = ivasPayload_convertToQ15( orientation.w ); - orientationQ15->x = ivasPayload_convertToQ15( orientation.x ); - orientationQ15->y = ivasPayload_convertToQ15( orientation.y ); - orientationQ15->z = ivasPayload_convertToQ15( orientation.z ); + buf->length = nBytes; + return IVAS_ERR_OK; } - -/*-----------------------------------------------------------------------* - * ivasPayload_parseQuaternionValue() - * - * Parse a single Q15 encoded quaternion component from the RTP packet and convert it to float. - *-----------------------------------------------------------------------*/ - -static float ivasPayload_parseQuaternionComponent( - char **PIorientationData /* i/o: Pointer to the PI data frame containing a single quaternion component */ +static void UpdateRtpHeader( + RTP_HEADER *header, /* RTP header structure */ + uint32_t timestampOffset /* Timestamp offset @ 16KHz clock for next frame */ ) { - uint16_t tempQ15value; - tempQ15value = ( uint16_t ) * *PIorientationData; - tempQ15value = (uint16_t) ( ( tempQ15value << 8 ) | ( uint16_t ) * ( *PIorientationData + 1 ) ); - *PIorientationData += 2; - return ivasPayload_convertFromQ15( tempQ15value ); + header->seqNumber++; + header->timestamp += timestampOffset; } - -/*-----------------------------------------------------------------------* - * ivasPayload_parseOrientationPIdata() - * - * Parse a single orientation in quaternions from the PI data. - *-----------------------------------------------------------------------*/ - -static bool ivasPayload_parseOrientationPIdata( - char *PIorientationData, /* i/o: Pointer to the PI data frame containing a single quaternion orientation */ - IVAS_QUATERNION *q /* o : Parsed quaternion value */ +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 */ ) { - q->w = ivasPayload_parseQuaternionComponent( &PIorientationData ); - q->x = ivasPayload_parseQuaternionComponent( &PIorientationData ); - q->y = ivasPayload_parseQuaternionComponent( &PIorientationData ); - q->z = ivasPayload_parseQuaternionComponent( &PIorientationData ); - - if ( q->w > 1.0f || q->w < -1.0f || - q->x > 1.0f || q->x < -1.0f || - q->y > 1.0f || q->y < -1.0f || - q->z > 1.0f || q->z < -1.0f ) + uint32_t n = 0, nByte = 0, expectedSize = 12; + uint8_t byte; + + if ( packet->length < expectedSize ) { - fprintf( stderr, "Error: invalid orientation PI data\n" ); - return false; + return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "Insufficient input buffer for RTP header unpacking" ); } - return true; -} - -/*---------------------------------------------------------------------* - * ivasPayload_parseSinglePIorientation( ) - * - * Parse a single orientation from PI data. - * Save the parsed orientation value and use in processing in future frames, if no new PI orientation is received. - *---------------------------------------------------------------------*/ - -static bool ivasPayload_parseSinglePIorientation( - char *orientation, /* i : input PI orientation */ - bool *isOrientationSaved, /* o : flag to indicate if an orientation for this PI type was previously saved */ - IVAS_QUATERNION *savedOrientation /* o : saved orientation value for this PI type */ -) -{ - if ( orientation ) + /* + +---+---+---+---+---+---+---+---+ + | 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 ) { - /* read the orientation PI value */ - if ( !ivasPayload_parseOrientationPIdata( orientation, savedOrientation ) ) - { - fprintf( stderr, "Error: parsing PI orientation failed\n" ); - return false; - } - *isOrientationSaved = true; + return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "CC indicated but insufficient input buffer" ); } - return true; -} + /* 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++] ); + } -/*-----------------------------------------------------------------------* - * ivasPayload_parsePIdata() - * - * Parse PI data for the current audio frame. - *-----------------------------------------------------------------------*/ - -static bool ivasPayload_parsePIdata( - char *PIdataPayload, /* i : Pointer to the beginning of the PI data section in the RTP packet */ - uint16_t PIdataPayloadSizeBytes, /* i : Size of the PI data section in bytes */ - uint16_t frameIndex, /* i : Index to the current audio frame in the RTP packet */ - PI_DATA_CURRENT_FRAME *PIdataCurrentFrame, /* o : PI data handle for the current audio frame */ - PI_DATA_DEPACKER_STATE *PIdataDepackerState /* i/o: data handle for the PI depacker state */ -) -{ - // TODO(pajunen): can this accidentally include zero padding bits? - - bool somePIframeFollowing; - char *PIframeDataBeginning; - uint16_t PIframeMarker, PIframeType, tempPIframeSizeBytes, PIframeSizeBytes, totalPIframeSizeBytes, totalPIheaderSizeBytes, count_audio; - PIframeDataBeginning = PIdataPayload; - somePIframeFollowing = true; - totalPIframeSizeBytes = 0; - totalPIheaderSizeBytes = 0; - count_audio = 0; - - while ( somePIframeFollowing ) + if ( header->extension ) { - if ( (int16_t) PIdataPayloadSizeBytes <= 0 ) + 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 ) { - fprintf( stderr, "Error: PI payload too small\n" ); - return false; + return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "Extension Header indicated but insufficient input buffer" ); } - /* parse PI data header */ - somePIframeFollowing = ( *PIdataPayload & 0x80 ) != 0; - PIframeMarker = ( *PIdataPayload & 0x60 ) >> 5; - PIframeType = ( *PIdataPayload & 0x1F ); - ++PIdataPayload; - ++totalPIheaderSizeBytes; - --PIdataPayloadSizeBytes; - tempPIframeSizeBytes = ( *PIdataPayload & 0xFF ); - PIframeSizeBytes = tempPIframeSizeBytes; - while ( tempPIframeSizeBytes == 255 ) + header->extData = realloc( header->extData, header->numExtUnits * 4 ); + if ( header->extData == NULL ) { - ++PIdataPayload; - ++totalPIheaderSizeBytes; - --PIdataPayloadSizeBytes; - tempPIframeSizeBytes = ( *PIdataPayload & 0xFF ); - PIframeSizeBytes += tempPIframeSizeBytes; + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "failed to allocate extdata" ); } - ++PIdataPayload; - ++totalPIheaderSizeBytes; - --PIdataPayloadSizeBytes; - PIdataPayloadSizeBytes -= PIframeSizeBytes; + memcpy( header->extData, &packet->buffer[nByte], header->numExtUnits * 4 ); + nByte += header->numExtUnits * 4; + } - /* general PI data for all frames */ - if ( PIframeMarker == PM_GENERAL ) + *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 ) +{ + 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++ ) { - ivasPayload_parsePItype( PIframeDataBeginning, PIframeSizeBytes, totalPIframeSizeBytes, PIframeType, PIdataCurrentFrame ); + 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; + } } - else + 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++ ) { - /* current frame */ - if ( ( PIframeMarker > 0 ) & ( count_audio == frameIndex ) ) + if ( evsFrameSizeInBits[n] == frameLengthBytes * 8 ) { - ivasPayload_parsePItype( PIframeDataBeginning, PIframeSizeBytes, totalPIframeSizeBytes, PIframeType, PIdataCurrentFrame ); + *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; } - - /* increase audio frame counter */ - if ( PIframeMarker == PM_LAST ) + } + /* 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 ) { - ++count_audio; + *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; } } - totalPIframeSizeBytes += PIframeSizeBytes; + return IVAS_ERR_INVALID_BITRATE; } - ivasPayload_PIaddHeaderBytes( totalPIheaderSizeBytes, PIdataCurrentFrame ); +} + +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; - /* scene orientation */ - if ( !ivasPayload_parseSinglePIorientation( PIdataCurrentFrame->sceneOrientation, &PIdataDepackerState->sceneOrientationSaved, &PIdataDepackerState->sceneOrientationQuat ) ) + if ( phPack == NULL ) { - fprintf( stderr, "Error: parsing scene orientation PI data failed.\n" ); - return false; + return IVAS_ERR_UNEXPECTED_NULL_POINTER; } - /* device orientation */ - if ( !ivasPayload_parseSinglePIorientation( PIdataCurrentFrame->deviceOrientationUncompensated, &PIdataDepackerState->deviceOrientationSaved, &PIdataDepackerState->deviceOrientationQuat ) ) + *phPack = NULL; + if ( ( hPack = (IVAS_RTP_PACK_HANDLE) calloc( 1, sizeof( struct IVAS_RTP_PACK ) ) ) == NULL ) { - fprintf( stderr, "Error: parsing device orientation PI data failed.\n" ); - return false; + return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Cannot allocate memory for IVAS rtppack handle" ); } - return true; -} + error = BPOOL_Create( &hPack->nodePool, sizeof( FRAME_NODE ), IVAS_MAX_FRAMES_IN_QUEUE ); + ERR_CHECK_RETURN( error ); + error = QUEUE_Create( &hPack->frameQ ); + ERR_CHECK_RETURN( error ); -/*-----------------------------------------------------------------------* - * resetPIdataCurrentFrame() - * - * Reset the PI data handle for the current audio frame. - *-----------------------------------------------------------------------*/ + mtx_init( &hPack->lock, 0 ); + hPack->initConfig = *config; + initRequests( hPack->requests ); + InitRtpHeader( &hPack->header ); + *phPack = hPack; + return IVAS_ERR_OK; +} -static void resetPIdataCurrentFrame( PI_DATA_CURRENT_FRAME *PIdataCurrentFrame ) +/* 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 */ +) { - PIdataCurrentFrame->PIdataPresent = false; - PIdataCurrentFrame->sceneOrientation = NULL; - PIdataCurrentFrame->deviceOrientationCompensated = NULL; - PIdataCurrentFrame->deviceOrientationUncompensated = NULL; - PIdataCurrentFrame->acousticEnvironmentId = NULL; - PIdataCurrentFrame->acousticEnvironmentOnlyLateReverb = NULL; - PIdataCurrentFrame->acousticEnvironmentLateReverbAndEarlyReflections = NULL; -} + IVAS_RTP_PACK_HANDLE hPack; + + /* Free all memory */ + if ( phPack == NULL || *phPack == NULL ) + { + return; + } + hPack = *phPack; + QUEUE_Destroy( &hPack->frameQ ); + mtx_destroy( &hPack->lock ); + BPOOL_Destroy( &hPack->nodePool ); + free ( hPack->header.extData ); + free( hPack ); + *phPack = NULL; +} -bool ivasPayload_unpackFrame( - char *payload, /* i : RTP payload data */ - uint16_t payloadSizeBytes, /* i : RTP payload size in bytes */ - uint16_t frameIndex, /* i : Index for the data frame in the payload */ - bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ - bool *frameFollowing, /* o : (F) bit in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ - bool *isGoodFrame, /* o : Good frame indicator (set by the Q-bit in AMR-WB IO mode, otherwise always set to true) */ - unsigned char **frame, /* o : Data frame indicated by frameIndex */ - uint16_t *frameSizeInBits, /* o : Data frame size in bits */ - PI_DATA_DEPACKER_STATE *PIdataDepackerState /* i/o: data handle for the PI depacker state */ +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 */ ) { - bool someEvsIvasModeBit, someFrameFollowing, someIvasIndicatorBit, someGoodFrameIndication; - uint16_t someBitrateIndex, someFrameSizeInBits, fullPayloadSizeBytes, PIdataPayloadSizeBytes; - int32_t bitrate; - uint16_t iFrame; - char *PIdataPayload; - fullPayloadSizeBytes = payloadSizeBytes; - PIdataPayload = payload; - someFrameFollowing = true; - PI_DATA_CURRENT_FRAME PIdataCurrentFrame; - resetPIdataCurrentFrame( &PIdataCurrentFrame ); - if ( payloadSizeBytes < 1 ) + if ( reqType < 0 || reqType >= IVAS_REQUEST_MAX ) { - fprintf( stderr, "Error: payload too small to parse ToC\n" ); - return false; + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Invalid request key provided" ); } - /* parse CMR/E-bytes */ - if ( *payload & E_BYTE ) // 1xxx xxxx + + /* Sanity on API */ + switch ( reqType ) { - PIdataCurrentFrame.PIdataPresent = false; - while ( 1 ) + case IVAS_REQUEST_CODEC: { - if ( ( *payload & 0xF0 ) == IVAS_E_CMR ) // 1111 xxxx - { - /* IVAS CMR/E-byte */ - while ( 1 ) - { - /* parse subsequent E-bytes */ - ++payload; - --payloadSizeBytes; - if ( ( *payload & 0xE0 ) == IVAS_E_BW_REQUEST ) // 100x xxxx - { - /* IVAS bandwidth request */ - if ( ( *payload & 0xFF ) == IVAS_BW_REQ_WB ) // 1000 0000 - { - /* Wideband request */ - } - else if ( ( *payload & 0xFF ) == IVAS_BW_REQ_SWB ) // 1000 0001 - { - /* Super-wideband request */ - } - else if ( ( *payload & 0xFF ) == IVAS_BW_REQ_FB ) // 1000 0010 - { - /* Fullband request */ - } - } - else if ( ( *payload & 0xE0 ) == IVAS_E_FMT_REQUEST ) // 101x xxxx - { - /* IVAS coded format request */ - if ( ( *payload & 0xFF ) == IVAS_FMT_REQ_STEREO ) // 1010 0000 - { - /* Stereo request */ - } - else if ( ( *payload & 0xFF ) == IVAS_FMT_REQ_SBA ) // 1010 0001 - { - /* SBA request */ - } - else if ( ( *payload & 0xFF ) == IVAS_FMT_REQ_MASA ) // 1010 0010 - { - /* MASA request */ - } - else if ( ( *payload & 0xFF ) == IVAS_FMT_REQ_ISM ) // 1010 0011 - { - /* ISM request */ - } - else if ( ( *payload & 0xFF ) == IVAS_FMT_REQ_MC ) // 1010 0100 - { - /* MC request */ - } - else if ( ( *payload & 0xFF ) == IVAS_FMT_REQ_OMASA ) // 1010 0101 - { - /* OMASA request */ - } - else if ( ( *payload & 0xFF ) == IVAS_FMT_REQ_OSBA ) // 1010 0110 - { - /* OSBA request */ - } - } - else if ( ( *payload & 0xE0 ) == IVAS_E_PI_INDICATION ) // 110x xxxx - { - /* IVAS PI presence */ - PIdataCurrentFrame.PIdataPresent = true; - } - else if ( ( *payload & 0xE0 ) == IVAS_E_RESERVED ) // 111x xxxx - { - /* Reserved IVAS subsequent E-byte */ - } - else - { - break; - } - - if ( payloadSizeBytes < 1 ) - { - fprintf( stderr, "Error: payload too small to parse subsequent E-bytes\n" ); - return false; - } - } - break; - } - else + uint32_t codec = reqValue.codec; + if ( codec != IVAS_RTP_IVAS && codec != IVAS_RTP_EVS ) { - /* EVS CMR */ - ++payload; - --payloadSizeBytes; - if ( *payload & E_BYTE ) // 1xxx xxxx - { - continue; - } - else - { - break; - } + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Unsupported codec id provided" ); } } - } - - /* parse all ToC entries */ - *frame = (unsigned char *) payload; /* no need to copy frame bytes */ - for ( iFrame = 0; someFrameFollowing; ++iFrame ) - { - if ( (int16_t) payloadSizeBytes <= 0 ) + break; + case IVAS_REQUEST_BITRATE: { - fprintf( stderr, "Error: payload too small\n" ); - return false; + uint32_t bitrate = reqValue.bitrate; + if ( bitrate < 5900 || bitrate > 512000 ) + { + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Unsupported bitrate provided" ); + } } - if ( *payload & E_BYTE ) // 1xxx xxxx + break; + case IVAS_REQUEST_BANDWIDTH: { - fprintf( stderr, "Error: expected ToC, found CMR/E-byte\n" ); - return false; + uint32_t bandwidth = reqValue.bandwidth; + if ( bandwidth > IVAS_BANDWIDTH_NO_REQ ) + { + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Unsupported bandwidth provided" ); + } } - ivasPayload_parseToc( *payload, &someEvsIvasModeBit, &someFrameFollowing, &someBitrateIndex, &someIvasIndicatorBit, &someGoodFrameIndication, &bitrate ); - if ( bitrate < 0 ) + break; + case IVAS_REQUEST_FORMAT: { - fprintf( stderr, "Error: unexpected frameTypeIndex in ToC\n" ); - return false; + uint32_t format = reqValue.formatType; + if ( format > IVAS_FMT_NO_REQ ) + { + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Unsupported format provided" ); + } } - ++payload; - ++*frame; - someFrameSizeInBits = (uint16_t) ( bitrate / 50 ); - /* just keep/copy zero padding bits - * in case of AMRWB_IO_SID the STI bit and CMI bits following the SID_1k75 frame are also kept (A.2.2.1.3 of TS 26.445) */ - payloadSizeBytes -= 1 + ( someFrameSizeInBits + 7 ) / 8; - if ( iFrame < frameIndex ) + break; + case IVAS_REQUEST_CA_MODE: { - *frame += ( someFrameSizeInBits + 7 ) / 8; - if ( !someFrameFollowing ) + uint32_t caMode = reqValue.caMode; + if ( caMode > IVAS_RTP_CA_NO_REQ ) { - fprintf( stderr, "Error: expected ToC with F bit set\n" ); - return false; + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Unsupported channel aware mode provided" ); } } - else if ( iFrame == frameIndex ) + break; +#ifdef RTP_S4_251135_CR26253_0016_REV1 + case IVAS_REQUEST_SUBFORMAT: { - *evsIvasModeBit = someEvsIvasModeBit; - *frameFollowing = someFrameFollowing; - *bitrateIndex = someBitrateIndex; - *ivasIndicatorBit = someIvasIndicatorBit; - *isGoodFrame = someGoodFrameIndication; - *frameSizeInBits = someFrameSizeInBits; + uint32_t subFormat = reqValue.subFormatType; + if ( subFormat > IVAS_SUBFMT_NO_REQ ) + { + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Unsupported subformat provided" ); + } } - if ( (int16_t) payloadSizeBytes < 0 ) + break; + case IVAS_REQUEST_SR_CONFIG: { - fprintf( stderr, "Error: payload too small for frame %u data\n", frameIndex ); - return false; + 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" ); } - /* parse PI data */ - if ( PIdataCurrentFrame.PIdataPresent ) - { - PIdataPayloadSizeBytes = payloadSizeBytes; - PIdataPayload += ( fullPayloadSizeBytes - PIdataPayloadSizeBytes ); - if ( !ivasPayload_parsePIdata( PIdataPayload, PIdataPayloadSizeBytes, frameIndex, &PIdataCurrentFrame, PIdataDepackerState ) ) - { - fprintf( stderr, "Error while parsing PI data\n" ); - return false; - } - } + hPack->requests[reqType] = reqValue; - return true; + return IVAS_ERR_OK; } -IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_open( - IVAS_RTPDUMP_DEPACKER *self, /* o : IVAS rtpdump depacker handle */ - FILE *file /* i : Input file containing the rtpdump */ -) +static bool isAmrWBIOMode( uint32_t bitrate, uint32_t *idx ) { - RTPDUMP_ERROR rtpdumpError; - self->frameFollowing = false; - rtpdumpError = RTPDUMP_OpenWithFileToRead( &self->rtpdump, file ); - if ( rtpdumpError != RTPDUMP_NO_ERROR ) + size_t n; + *idx = 0; + for ( n = 0; n < sizeof( amrWBIOFrameSizeInBits ) / sizeof( amrWBIOFrameSizeInBits[0] ); n++ ) { - return IVAS_RTPDUMP_DEPACKER_RTPDUMP_ERROR; + if ( bitrate == ( amrWBIOFrameSizeInBits[n] * IVAS_NUM_FRAMES_PER_SEC ) ) + { + *idx = n; + return true; + } } - self->PIdataDepackerState.sceneOrientationSaved = false; - self->PIdataDepackerState.deviceOrientationSaved = false; - return IVAS_RTPDUMP_DEPACKER_NO_ERROR; + return false; } -IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_readNextFrame( - IVAS_RTPDUMP_DEPACKER *self, /* i/o: IVAS rtpdump depacker handle */ - uint16_t *rtpSequenceNumber, /* o : RTP sequence number of the read packet */ - uint32_t *rtpTimeStamp, /* o : RTP timestamp for the unpacked frame */ - uint32_t *rcvTime_ms, /* o : Time offset in milliseconds for the unpacked frame */ - bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ - uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ - bool *isGoodFrame, /* o : Good frame indicator (set by the Q-bit in AMR-WB IO mode, otherwise always set to true) */ - unsigned char **frame, /* o : Data frame indicated by frameIndex */ - uint16_t *frameSizeBits /* o : Data frame size in bits */ -) +static uint32_t getBitrateIdx( const uint32_t *table, uint32_t tableLen, uint32_t bitrate ) { - /* read next RTP packet from rtpdump */ - if ( !self->frameFollowing ) + size_t n, idx = 0; + + for ( n = 0; n < tableLen; n++ ) { - RTPDUMP_ERROR rtpdumpError = RTPDUMP_ReadPacket( self->rtpdump, &self->rtpPacket, &self->timeoffset_ms ); - if ( rtpdumpError == RTPDUMP_READ_ENDOFFILE ) + if ( bitrate > ( table[n] * IVAS_NUM_FRAMES_PER_SEC ) ) { - return IVAS_RTPDUMP_DEPACKER_EOF; + idx = n; } - else if ( rtpdumpError != RTPDUMP_NO_ERROR ) + else { - return IVAS_RTPDUMP_DEPACKER_RTPDUMP_ERROR; + return idx; } - self->frameIndex = 0; - } - /* unpack next frame from RTP packet */ - if ( !ivasPayload_unpackFrame( self->rtpPacket.data + self->rtpPacket.headerSize, - self->rtpPacket.payloadSize, self->frameIndex, - evsIvasModeBit, &self->frameFollowing, bitrateIndex, ivasIndicatorBit, - isGoodFrame, frame, frameSizeBits, &self->PIdataDepackerState ) ) - { - return IVAS_RTPDUMP_DEPACKER_PAYLOAD_ERROR; - } - - /* return frame */ - *rtpSequenceNumber = self->rtpPacket.sequenceNumber; - *rtpTimeStamp = self->rtpPacket.timeStamp + self->frameIndex * 16000 / 50; - *rcvTime_ms = self->timeoffset_ms + self->frameIndex * 20; - ++self->frameIndex; - return IVAS_RTPDUMP_DEPACKER_NO_ERROR; -} - -void IVAS_RTPDUMP_DEPACKER_close( - IVAS_RTPDUMP_DEPACKER *self /* i : IVAS rtpdump depacker handle */ -) -{ - if ( !self ) - { - return; } - RTPDUMP_Close( &self->rtpdump, 0 ); + return idx; } -IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_open( - IVAS_RTPDUMP_PACKER **rtpdumpPacker, /* o : IVAS rtpdump packer handle */ - FILE *file /* i : Output file for the rtpdump stream */ +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 */ ) { - IVAS_RTPDUMP_PACKER *self; - RTPDUMP_ERROR rtpdumpError; - IVAS_QUATERNION identity; - identity.w = 1.0f; - identity.x = 0.0f; - identity.y = 0.0f; - identity.z = 0.0f; - self = calloc( sizeof( IVAS_RTPDUMP_PACKER ), 1 ); - rtpdumpError = RTPDUMP_OpenWithFileToWrite( &self->rtpdump, file ); - if ( rtpdumpError != RTPDUMP_NO_ERROR ) + /* 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 ) { - return IVAS_RTPDUMP_PACKER_RTPDUMP_ERROR; - } - RTPDUMP_SetDefaultRtpPacketHeader( &self->rtpPacket ); - memset( self->rtpPacket.data, 0, sizeof( self->rtpPacket.data ) ); - self->piDataPacker.sceneOrientationQuat = identity; - self->piDataPacker.deviceOrientationQuat = identity; - self->piDataPacker.sceneOrientationPresent = false; - self->piDataPacker.deviceOrientationPresent = false; - self->piDataPresent = false; - *rtpdumpPacker = self; - return IVAS_RTPDUMP_PACKER_NO_ERROR; -} + /* If requesting EVS/AMRWB IO from farend, only Initial E-byte present */ + IVAS_RTP_CA_MODE caMode = hPack->requests[IVAS_REQUEST_CA_MODE].caMode; -void IVAS_RTPDUMP_PACKER_writeEbytes( - IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ - int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet for bidirectional signalling */ - bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS). - * Immersive bitrate CMRs follow A.3.3.3.3.2 in TS26.253. Mono bitrate CMRs follow A.2.2.1.1 in TS26.445. */ - IVAS_BANDWIDTH_REQUEST ivasBandwidthRequest, /* i : Requested bandwidth to be written as subsequent E-byte in the packet for bidirectional signalling */ - IVAS_FORMAT_REQUEST ivasFormatRequest /* i : Requested format to be written as subsequent E-byte in the packet for bidirectional signalling */ -) -{ - UWord8 initialEbyte, subsequentEbyte; - bool initialEbyteWritten; - - /* determine E-bytes */ - initialEbyte = 0; - subsequentEbyte = 0; - initialEbyteWritten = false; - if ( requestImmersiveFormatBitrate ) - { - switch ( requestedBitrate ) + if ( caMode != IVAS_RTP_CA_NO_REQ ) { - case IVAS_13k2: - initialEbyte = IVAS_CMR_13k2; - break; - case IVAS_16k4: - initialEbyte = IVAS_CMR_16k4; - break; - case IVAS_24k4: - initialEbyte = IVAS_CMR_24k4; - break; - case IVAS_32k: - initialEbyte = IVAS_CMR_32k; - break; - case IVAS_48k: - initialEbyte = IVAS_CMR_48k; - break; - case IVAS_64k: - initialEbyte = IVAS_CMR_64k; - break; - case IVAS_80k: - initialEbyte = IVAS_CMR_80k; - break; - case IVAS_96k: - initialEbyte = IVAS_CMR_96k; - break; - case IVAS_128k: - initialEbyte = IVAS_CMR_128k; - break; - case IVAS_160k: - initialEbyte = IVAS_CMR_160k; - break; - case IVAS_192k: - initialEbyte = IVAS_CMR_192k; - break; - case IVAS_256k: - initialEbyte = IVAS_CMR_256k; - break; - case IVAS_384k: - initialEbyte = IVAS_CMR_384k; - break; - case IVAS_512k: - initialEbyte = IVAS_CMR_512k; - break; - default: - initialEbyte = IVAS_CMR_NO_REQ; - break; + 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; + } } - writeByteToRtp( self, (char) initialEbyte ); - initialEbyteWritten = true; } else { - // TODO(pajunen): add EVS CMRs here - /* EVS CMR */ - initialEbyteWritten = false; - } + /* 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 - if ( ivasBandwidthRequest != NO_BANDWIDTH_REQUEST ) - { - if ( !initialEbyteWritten ) + /* Initial E-Byte only sent if either bitrate requested or subsequent E-byte is requested */ + if ( !( ( bitrate > 0 ) || isBandwidthProvided || isFormatProvided || + isSubFormatProvided || isSRConfigProvided || piDataPresent ) ) { - initialEbyte = IVAS_CMR_NO_REQ; - writeByteToRtp( self, (char) initialEbyte ); - initialEbyteWritten = true; + return; } - switch ( ivasBandwidthRequest ) + if ( bitrate > 0 ) { - case WB_REQUEST: - subsequentEbyte = IVAS_BW_REQ_WB; - break; - case SWB_REQUEST: - subsequentEbyte = IVAS_BW_REQ_SWB; - break; - case FB_REQUEST: - subsequentEbyte = IVAS_BW_REQ_FB; - break; - default: - subsequentEbyte = IVAS_BW_NO_REQ; - break; + /* 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; + } } - writeByteToRtp( self, (char) subsequentEbyte ); - } - if ( ivasFormatRequest != NO_FORMAT_REQUEST ) - { - if ( !initialEbyteWritten ) + /* Subsequent E-bytes - Bandwidth Request */ + if ( isBandwidthProvided && nByte < maxNumEBytes ) { - initialEbyte = IVAS_CMR_NO_REQ; - writeByteToRtp( self, (char) initialEbyte ); - initialEbyteWritten = true; + uint8_t bw = ( bandwidth - IVAS_BANDWIDTH_WB ) & MASK_2BIT; + eByte[nByte++] = ( EBYTE_BANDWIDTH_REQUEST | bw ); } - switch ( ivasFormatRequest ) + /* Subsequent E-bytes - Coded Format Request */ + if ( ( isFormatProvided || isSubFormatProvided ) && nByte < maxNumEBytes ) { - case STEREO_REQUEST: - subsequentEbyte = IVAS_FMT_REQ_STEREO; - break; - case SBA_REQUEST: - subsequentEbyte = IVAS_FMT_REQ_SBA; - break; - case MASA_REQUEST: - subsequentEbyte = IVAS_FMT_REQ_MASA; - break; - case ISM_REQUEST: - subsequentEbyte = IVAS_FMT_REQ_ISM; - break; - case MC_REQUEST: - subsequentEbyte = IVAS_FMT_REQ_MC; - break; - case OMASA_REQUEST: - subsequentEbyte = IVAS_FMT_REQ_OMASA; - break; - case OSBA_REQUEST: - subsequentEbyte = IVAS_FMT_REQ_OSBA; - break; - default: - subsequentEbyte = IVAS_FMT_NO_REQ; - break; + 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 ); } - writeByteToRtp( self, (char) subsequentEbyte ); +#endif /* RTP_S4_251135_CR26253_0016_REV1 */ } - if ( self->piDataPresent ) + /* Final E-byte is the PI Indicator E-Byte */ + if ( piDataPresent && ( nByte < maxNumEBytes ) ) { - if ( !initialEbyteWritten ) - { - initialEbyte = IVAS_CMR_NO_REQ; - writeByteToRtp( self, (char) initialEbyte ); - initialEbyteWritten = true; - } - subsequentEbyte = IVAS_PI_INDICATION; - writeByteToRtp( self, (char) subsequentEbyte ); + eByte[nByte++] = EBYTE_PI_INDICATOR; /* PI Indication */ } + + *nBytesWritten = nByte; } -void IVAS_RTPDUMP_PACKER_writeToc( - IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ - int32_t frameBitrate, /* i : Bitrate of the written frame */ - bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS). - * Immersive format ToCs follow A.3.3.2 in TS26.253. Mono format ToCs follow A.2.2.1.2 in TS26.445.*/ - bool someFrameFollowing /* i : Flag to indicate if another frame follows this frame in the same RTP packet. */ + +#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 */ ) { - UWord8 H_bit, F_bit, EVS_IVAS_mode_bit, Q_bit, toc; - int16_t is_amr_wb; - - is_amr_wb = 0; - H_bit = 0; - F_bit = someFrameFollowing ? 1 : 0; - /* FRAME_NO_DATA frames are written as in TS26.445 */ - if ( isImmersiveFormat && frameBitrate != FRAME_NO_DATA ) + uint8_t bitIdx, codecId, digetic; + if ( bitrateKbps < 256000 || bitrateKbps > 512000 ) { - toc = (UWord8) ( rate2IVASmode( frameBitrate ) ); - EVS_IVAS_mode_bit = 0; - Q_bit = 1; + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Unsupported bitrate for SR" ); } - else - { - toc = (UWord8) ( rate2EVSmode( frameBitrate, &is_amr_wb ) ); - EVS_IVAS_mode_bit = (UWord8) is_amr_wb; - Q_bit = 0; - } - toc = ( H_bit << 7 ) | ( F_bit << 6 ) | ( EVS_IVAS_mode_bit << 5 ) | ( Q_bit << 4 ) | toc; - writeByteToRtp( self, (char) toc ); -} -IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_writeNextFrame( - IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ - const uint16_t *bitStream, /* i : Bitstream containing the data frame to be written */ - int32_t numBits, /* i : Number of bits contained in the data frame */ - bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS) */ - int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet */ - bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS) */ - IVAS_BANDWIDTH_REQUEST ivasBandwidthRequest, /* i : Requested bandwidth to be written as subsequent E-byte in the packet */ - IVAS_FORMAT_REQUEST ivasFormatRequest /* i : Requested format to be written as subsequent E-byte in the packet */ + 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 ivas_error addNewFrameNode( + IVAS_RTP_PACK_HANDLE hPack, /* i/o : IVAS rtp packer handle */ + FRAME_NODE **newNode ) +{ + FRAME_NODE *node = NULL; + ivas_error error = BPOOL_GetBuffer( hPack->nodePool, (void **) &node ); + ERR_CHECK_RETURN( error ); + + node->auNumBits = 0; + node->tocNumBytes = 0; + node->next = NULL; + + initPiDataFrame( &node->piDataFrame ); + + /* Add to frames FiFo */ + QUEUE_Push( hPack->frameQ, (NODE *) node ); + + *newNode = node; + return IVAS_ERR_OK; +} + +/* Push a single IVAS/EVS frame to rtp packer + * + * If multiple frames per RTP Payload are desired, multiple frames must be explicitly + * pushed before a call to IVAS_RTP_PACK_GetPacket to generate a rtp packet. + * + * It is possible to have variable frames per packet until maxFramesPerPacket frames + * if IVAS_RTP_PACK_GetPacket is invoked asyncronously w.r.t this api. + * + */ +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; + FRAME_NODE *node = (FRAME_NODE *) QUEUE_Back( hPack->frameQ ); + + /* Check if a node was added due to Generic PI Data push */ + if ( ( node == NULL ) || ( node->tocNumBytes != 0 ) ) + { + error = addNewFrameNode( hPack, &node ); + ERR_CHECK_RETURN( error ); + } + + if ( frameBuffer == NULL || frameBuffer->length == 0 ) + { + /* Indicates a NO_DATA_FRAME in case of DTX mode */ + node->toc[0] = TOC_INDICATE_NO_DATA; + node->tocNumBytes = 1; + node->auNumBits = 0; + } + else + { + error = getBitrateFromCodecAndFrameSize( codecId, frameBuffer->length, &bitrate, &frameLengthInBits, &tocByte ); + ERR_CHECK_RETURN( error ); + + node->toc[0] = tocByte; + node->tocNumBytes = 1; + node->auNumBits = frameLengthInBits; + memcpy( node->au, frameBuffer->buffer, frameBuffer->length ); + +#ifdef RTP_S4_251135_CR26253_0016_REV1 + if ( srInfo != NULL && srInfo->valid ) + { + node->toc[0] = TOC_INDICATE_SR; + error = getSRToCByte( srInfo, bitrate, &node->toc[1] ); + ERR_CHECK_RETURN( error ); + node->tocNumBytes = 2; + } +#endif + } + + return IVAS_ERR_OK; +} + +uint32_t IVAS_RTP_PACK_GetNumFrames( + IVAS_RTP_PACK_HANDLE hPack /* i/o : IVAS rtp packer handle */ +) +{ + return QUEUE_Size( hPack->frameQ ); +} + +/* 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_PI_TYPE piDataType; + ivas_error error = IVAS_ERR_OK; + FRAME_NODE *node = (FRAME_NODE *) QUEUE_Back( hPack->frameQ ); + PIDATA_FRAME *piDataFrm = NULL; + uint8_t PM = 0; + + 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" ); + } + + /* No frame in Queue to associate PI Data, create one */ + if ( node == NULL ) + { + /* PI data received before any frame in queue is generic */ + PM = PI_HEADER_PM_GENERIC; + + /* Add a new node in frame queue, toc is still zero indicating frame is expected */ + error = addNewFrameNode( hPack, &node ); + ERR_CHECK_RETURN( error ); + } + + piDataFrm = &node->piDataFrame; + piDataType = data->piDataType; + + error = PI_PackData( data, &piDataFrm->piData[piDataType], PM ); + ERR_CHECK_RETURN( error ); + + /* 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--; + } + + return IVAS_ERR_OK; +} + +/* 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 + * + */ +ivas_error IVAS_RTP_PACK_GetPayload( + IVAS_RTP_PACK_HANDLE hPack, /* i/o : IVAS rtp packer handle */ + IVAS_DATA_BUFFER *packet, /* o : encapsulated rtp packet */ + 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 *head = (FRAME_NODE *) QUEUE_Front( hPack->frameQ ); + FRAME_NODE *node = NULL; + size_t nBytes = packet->length, numEBytes = 0; + + if ( packet == NULL ) + { + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Output data buffer is NULL" ); + } + + /* Calculate number of frames and PI data present */ + node = head; + for ( n = 0; n < hPack->initConfig.maxFramesPerPacket && ( NULL != node ); n++, node = node->next ) + { + numFrame++; + numPiDataPresent += node->piDataFrame.numPiDataAvailable; + } + *numFramesInPayload = numFrame; /* numFrames in Packet */ + + /* IVAS Payload starts with E-Bytes */ + packEBytes( hPack, ( numPiDataPresent > 0 ), packet->capacity, &packet->buffer[packet->length], &numEBytes ); + nBytes += numEBytes; - RTPDUMP_ERROR rtpdumpError; - int32_t i, k, frameBitrate; - char tempData; + /* ToC bytes (atleast 1 byte per frame, 2 if SR )*/ + node = head; + for ( n = 0; n < numFrame && ( NULL != node ); n++, node = node->next ) + { + uint8_t fBit = ( n != ( numFrame - 1 ) ) ? TOC_HEADER_FOLLOWS : 0; /* Next ToC present */ + if ( nBytes < packet->capacity ) + { + packet->buffer[nBytes++] = ( node->toc[0] | fBit ); + } + else + { + return IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient memory to write RTP Payload" ); + } + + if ( node->tocNumBytes == 2 ) + { + if ( nBytes < packet->capacity ) + { + packet->buffer[nBytes++] = node->toc[1]; + } + else + { + return IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient memory to write RTP Payload" ); + } + } + } + + /* Frame Data */ + node = head; + for ( n = 0; n < numFrame && ( NULL != node ); n++, node = node->next ) + { + size_t len = ( node->auNumBits + 7 ) >> 3; /* zero padded length in bytes */ + if ( nBytes + len < packet->capacity ) + { + memcpy( &packet->buffer[nBytes], node->au, len ); + } + else + { + return IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient memory to write RTP Payload" ); + } + nBytes += len; + } - self->writingIndex = self->rtpPacket.headerSize; - self->rtpPacket.payloadSize = 0; - memset( self->rtpPacket.data, 0, sizeof( self->rtpPacket.data ) ); + packet->length = nBytes; /* update packet length before PI packing */ - determineBitrate( numBits, &frameBitrate ); + /* PI Data */ + if ( numPiDataPresent > 0 ) + { + bool skipPiData = false; + uint32_t numPiDataWritten = 0; - /* payload header */ - IVAS_RTPDUMP_PACKER_writeEbytes( self, requestedBitrate, requestImmersiveFormatBitrate, ivasBandwidthRequest, ivasFormatRequest ); - IVAS_RTPDUMP_PACKER_writeToc( self, frameBitrate, isImmersiveFormat, false ); + node = head; + for ( n = 0; n < numFrame && ( NULL != node ); n++, node = node->next ) + { + uint32_t piDataType = 0; + 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 < packet->capacity ) + { + memcpy( &packet->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 ) + { + packet->length = nBytes; /* update packet length after PI packing */ + } + } - /* frame data */ - for ( i = 0; i < numBits; i += 8 ) + /* Pop frames from Queue */ + for ( n = 0; n < numFrame; n++ ) { - tempData = 0; - for ( k = 0; k < 8; ++k ) + node = (FRAME_NODE *) QUEUE_Pop( hPack->frameQ ); + if ( node == NULL ) { - tempData = (char) ( ( tempData << 1 ) | (char) bitStream[i + k] ); + assert( 0 ); /* catastrophic error, implementation issue */ + break; } - writeByteToRtp( self, tempData ); + + error = BPOOL_FreeBuffer( hPack->nodePool, node ); + assert( error == IVAS_ERR_OK ); /* catastrophic error if this fails */ } - /* PI data */ - IVAS_RTPDUMP_PACKER_writePIdata( self ); + return IVAS_ERR_OK; +} + +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 ); - /* write packet */ - rtpdumpError = RTPDUMP_WritePacket( self->rtpdump, &self->rtpPacket, self->timeoffset_ms ); - if ( rtpdumpError != RTPDUMP_NO_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_RTPDUMP_PACKER_RTPDUMP_ERROR; + return IVAS_ERR_UNEXPECTED_NULL_POINTER; } - /* adjust packetizer values */ - self->rtpPacket.sequenceNumber += 1; - self->rtpPacket.timeStamp += 320; - self->timeoffset_ms += 20; + *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" ); + } + + error = BPOOL_Create( &hUnpack->nodePool, sizeof( UNPACK_NODE ), IVAS_MAX_FRAMES_IN_QUEUE ); + ERR_CHECK_RETURN( error ); + + error = BPOOL_Create( &hUnpack->piDataPool, sizeof( PIDATA_NODE ), IVAS_MAX_FRAMES_IN_QUEUE * IVAS_PI_MAX_ID ); + ERR_CHECK_RETURN( error ); + + error = QUEUE_Create( &hUnpack->frameQ ); + ERR_CHECK_RETURN( error ); - return IVAS_RTPDUMP_PACKER_NO_ERROR; + error = QUEUE_Create( &hUnpack->piDataQ ); + ERR_CHECK_RETURN( error ); + + hUnpack->initConfig = *config; + mtx_init( &hUnpack->lock, 0 ); + + initRequests( hUnpack->requests ); + + *phUnpack = hUnpack; + return IVAS_ERR_OK; } -void IVAS_RTPDUMP_PACKER_writePIdata( - IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ +/* 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 */ ) { - if ( !self->piDataPresent ) + IVAS_RTP_UNPACK_HANDLE hUnpack; + + /* Free all memory */ + if ( phUnpack == NULL || *phUnpack == NULL ) { return; } - UWord8 PIheaderByte; - IVAS_QUATERNION_Q15 sceneOrientationQ15, deviceOrientationQ15; - bool somePIfollowing = false; + hUnpack = *phUnpack; + mtx_destroy( &hUnpack->lock ); + QUEUE_Destroy( &hUnpack->frameQ ); + QUEUE_Destroy( &hUnpack->piDataQ ); + BPOOL_Destroy( &hUnpack->piDataPool ); + BPOOL_Destroy( &hUnpack->nodePool ); + 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 ) +{ + 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 */ + + *numFrames += 1; + memset( toc, 0, sizeof( *toc ) ); + if ( *numFrames == MAX_SUPPORTED_IVAS_FRAMES_PER_PACKET ) + { + return IVAS_ERROR( IVAS_ERR_INTERNAL, "No of frames in packet exceed max defined" ); + } + + if ( FT == TOC_INDICATE_ARMWB_Q || FT == TOC_INDICATE_AMRWB ) + { + toc->codecId = IVAS_RTP_EVS; + 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_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; +} - /* PI header section */ +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; - /* scene orientation */ - if ( self->piDataPacker.sceneOrientationPresent ) + while ( PF ) { - if ( self->piDataPacker.deviceOrientationPresent ) + uint8_t piHeader0, PM, piDataType, byte = 0; + uint32_t piSize = 0; + + if ( nBytes + 1 >= payload->length ) + { + return IVAS_ERROR( IVAS_ERR_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_UNDERFLOW, "Underflow during reading piSize" ); + } + } while ( byte == 255 ); + + if ( nBytes + piSize <= payload->length ) { - somePIfollowing = true; + 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 ); } - if ( somePIfollowing ) + else { - PIheaderByte = ( PF1 << 7 ) | ( PM_NOT_LAST << 5 ) | PI_SCENE_ORIENTATION; + return IVAS_ERROR( IVAS_ERR_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[MAX_SUPPORTED_IVAS_FRAMES_PER_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; + } + + 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 ); + 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->nodePool, (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 { - PIheaderByte = ( PF0 << 7 ) | ( PM_LAST << 5 ) | PI_SCENE_ORIENTATION; + return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "Underflow during expected frame bits" ); } - writeByteToRtp( self, (char) PIheaderByte ); - writeByteToRtp( self, PI_SCENE_ORIENTATION_SIZE_BYTES ); + + /* Add to frames FiFo */ + QUEUE_Push( hUnpack->frameQ, (NODE *) node ); } - /* device orientation */ - if ( self->piDataPacker.deviceOrientationPresent ) + if ( piDataIndicated ) { - PIheaderByte = ( PF0 << 7 ) | ( PM_LAST << 5 ) | PI_DEVICE_ORIENTATION_UNCOMPENSATED; - writeByteToRtp( self, (char) PIheaderByte ); - writeByteToRtp( self, PI_DEVICE_ORIENTATION_UNCOMPENSATED_SIZE_BYTES ); + 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; + } } - /* PI frame data section */ + if ( numFramesInPacket != NULL ) + { + *numFramesInPacket = numFrames; + } - if ( self->piDataPacker.sceneOrientationPresent ) + if ( numPiDataInPacket ) { - ivasPayload_convertOrientationToQ15( self->piDataPacker.sceneOrientationQuat, &sceneOrientationQ15 ); - writeByteToRtp( self, (char) ( sceneOrientationQ15.w >> 8 ) ); - writeByteToRtp( self, (char) ( sceneOrientationQ15.w & 0x00FF ) ); - writeByteToRtp( self, (char) ( sceneOrientationQ15.x >> 8 ) ); - writeByteToRtp( self, (char) ( sceneOrientationQ15.x & 0x00FF ) ); - writeByteToRtp( self, (char) ( sceneOrientationQ15.y >> 8 ) ); - writeByteToRtp( self, (char) ( sceneOrientationQ15.y & 0x00FF ) ); - writeByteToRtp( self, (char) ( sceneOrientationQ15.z >> 8 ) ); - writeByteToRtp( self, (char) ( sceneOrientationQ15.z & 0x00FF ) ); + *numPiDataInPacket = numPiData; } - if ( self->piDataPacker.deviceOrientationPresent ) + if ( remoteRequestBitmap ) { - ivasPayload_convertOrientationToQ15( self->piDataPacker.deviceOrientationQuat, &deviceOrientationQ15 ); - writeByteToRtp( self, (char) ( deviceOrientationQ15.w >> 8 ) ); - writeByteToRtp( self, (char) ( deviceOrientationQ15.w & 0x00FF ) ); - writeByteToRtp( self, (char) ( deviceOrientationQ15.x >> 8 ) ); - writeByteToRtp( self, (char) ( deviceOrientationQ15.x & 0x00FF ) ); - writeByteToRtp( self, (char) ( deviceOrientationQ15.y >> 8 ) ); - writeByteToRtp( self, (char) ( deviceOrientationQ15.y & 0x00FF ) ); - writeByteToRtp( self, (char) ( deviceOrientationQ15.z >> 8 ) ); - writeByteToRtp( self, (char) ( deviceOrientationQ15.z & 0x00FF ) ); + 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; } -void IVAS_RTPDUMP_PACKER_determinePIpresence( - IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ +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 */ ) { - self->piDataPresent = false; - if ( self->piDataPacker.sceneOrientationPresent || - self->piDataPacker.deviceOrientationPresent ) + 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 ) { - self->piDataPresent = true; + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Invalid request key provided" ); } + *value = hUnpack->requests[type]; + return IVAS_ERR_OK; } -void IVAS_RTPDUMP_PACKER_resetPIdata( - IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ +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 */ + 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 */ ) { - IVAS_QUATERNION identity; - identity.w = 1.0f; - identity.x = 0.0f; - identity.y = 0.0f; - identity.z = 0.0f; - - self->piDataPresent = false; - self->piDataPacker.sceneOrientationQuat = identity; - self->piDataPacker.deviceOrientationQuat = identity; - self->piDataPacker.sceneOrientationPresent = false; - self->piDataPacker.deviceOrientationPresent = false; + 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_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 ( 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; + } + + return BPOOL_FreeBuffer( hUnpack->nodePool, node ); } -void IVAS_RTPDUMP_PACKER_close( - IVAS_RTPDUMP_PACKER **self /* i : IVAS rtpdump packer handle */ +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 */ ) { - if ( !self || !( *self ) ) + 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; + return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "No more pi data in unpack fifo" ); } - RTPDUMP_Close( &( *self )->rtpdump, 0 ); - free( *self ); - *self = NULL; + + pi = (IVAS_PIDATA_GENERIC *) &node->data; + + if ( data != NULL && ( pi->size <= capacity ) ) + { + *data = *pi; + } + + if ( timestamp != NULL ) + { + *timestamp = node->timestamp; + } + + return BPOOL_FreeBuffer( hUnpack->piDataPool, node ); } -#endif + +#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 0000000000..ba33abe59e --- /dev/null +++ b/lib_util/ivas_rtp_pi_data.c @@ -0,0 +1,601 @@ +/****************************************************************************************************** + + (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. + +*******************************************************************************************************/ + +#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 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 uint16_t readInt16( const uint8_t *buffer ) +{ + return ( (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.0 ); + value = value > +32767.0 ? +32767.0 : value; + value = value < -32768.0 ? -32768.0 : 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_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_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_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_UNPACK_PI_DATA, "Incorrect size to unpack Orientation PI data" ); + } + + piData->size = sizeof( IVAS_PIDATA_ORIENTATION ); + orientation->orientation.w = ((int16_t) readInt16( &buffer[0] )) / 32768.0f; + orientation->orientation.x = ((int16_t) readInt16( &buffer[2] )) / 32768.0f; + orientation->orientation.y = ((int16_t) readInt16( &buffer[4] )) / 32768.0f; + orientation->orientation.z = ((int16_t) readInt16( &buffer[6] )) / 32768.0f; + + 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; + uint32_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 < 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_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 ); + + 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; + + /* Audio Description data is max 5 bytes, 2 bytes header */ + if ( maxDataBytes < packedSize + 2 ) + { + return IVAS_ERROR( IVAS_ERR_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 > 5 ) + { + return IVAS_ERROR( IVAS_ERR_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_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_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; +} + +#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 */ + packUnsupportedData, /* DIEGETIC_TYPE */ + packUnsupportedData, /* RESERVED13 */ + packUnsupportedData, /* RESERVED14 */ + packUnsupportedData, /* RESERVED15 */ +#ifdef RTP_S4_251135_CR26253_0016_REV1 + packOrientation, /* PLAYBACK_DEVICE_ORIENTATION */ + packOrientation, /* HEAD_ORIENTATION */ +#else + packUnsupportedData, /* PLAYBACK_DEVICE_ORIENTATION */ + packUnsupportedData, /* HEAD_ORIENTATION */ +#endif + packUnsupportedData, /* LISTENER_POSITION */ +#ifdef RTP_S4_251135_CR26253_0016_REV1 + packDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION */ + packAudioDescription, /* AUDIO_FOCUS_DIRECTION */ +#else + 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 */ + unpackUnsupportedData, /* DIEGETIC_TYPE */ + unpackUnsupportedData, /* RESERVED13 */ + unpackUnsupportedData, /* RESERVED14 */ + unpackUnsupportedData, /* RESERVED15 */ +#ifdef RTP_S4_251135_CR26253_0016_REV1 + unpackOrientation, /* PLAYBACK_DEVICE_ORIENTATION */ + unpackOrientation, /* HEAD_ORIENTATION */ +#else + unpackUnsupportedData, /* PLAYBACK_DEVICE_ORIENTATION */ + unpackUnsupportedData, /* HEAD_ORIENTATION */ +#endif + unpackUnsupportedData, /* LISTENER_POSITION */ +#ifdef RTP_S4_251135_CR26253_0016_REV1 + unpackDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION */ + unpackOrientation, /* AUDIO_FOCUS_DIRECTION */ +#else + 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_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; +} + +#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 0000000000..5780f32d8f --- /dev/null +++ b/lib_util/ivas_rtp_pi_data.h @@ -0,0 +1,441 @@ +/****************************************************************************************************** + + (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. + +*******************************************************************************************************/ + +#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[IVAS_PI_MAX_OBJECTS]; /* 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/ivas_rtp_rom.c b/lib_util/ivas_rtp_rom.c new file mode 100644 index 0000000000..4b8539fb30 --- /dev/null +++ b/lib_util/ivas_rtp_rom.c @@ -0,0 +1,64 @@ +/****************************************************************************************************** + + (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. + +*******************************************************************************************************/ + +#include "ivas_rtp_internal.h" + +#ifdef IVAS_RTPDUMP + +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/mutex.h b/lib_util/mutex.h new file mode 100644 index 0000000000..f3fe493dc6 --- /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 */ -- GitLab From a145854081c295b007196b42add0503b06cbf3ce Mon Sep 17 00:00:00 2001 From: "Singh, Ripinder" Date: Wed, 30 Jul 2025 13:25:21 +1000 Subject: [PATCH 028/100] BugFixes, Race Condition fixes, decoder restart for codec switch Signed-off-by: Singh, Ripinder --- Workspace_msvc/lib_util.vcxproj | 4 + Workspace_msvc/lib_util.vcxproj.filters | 12 + apps/decoder.c | 217 +++++++++++-- apps/encoder.c | 47 ++- lib_dec/lib_dec.c | 3 +- lib_util/ivas_bpool.c | 8 +- lib_util/ivas_bpool.h | 8 +- lib_util/ivas_queue.c | 26 +- lib_util/ivas_rtp_api.h | 46 +-- lib_util/ivas_rtp_internal.h | 1 + lib_util/ivas_rtp_payload.c | 385 +++++++++++++----------- lib_util/ivas_rtp_pi_data.c | 18 +- lib_util/mutex.h | 2 +- 13 files changed, 522 insertions(+), 255 deletions(-) diff --git a/Workspace_msvc/lib_util.vcxproj b/Workspace_msvc/lib_util.vcxproj index 94561b175a..84ff59beae 100644 --- a/Workspace_msvc/lib_util.vcxproj +++ b/Workspace_msvc/lib_util.vcxproj @@ -111,6 +111,10 @@ + + + + diff --git a/Workspace_msvc/lib_util.vcxproj.filters b/Workspace_msvc/lib_util.vcxproj.filters index afaedfb5da..776a38e3d3 100644 --- a/Workspace_msvc/lib_util.vcxproj.filters +++ b/Workspace_msvc/lib_util.vcxproj.filters @@ -85,6 +85,18 @@ util_c + + util_c + + + util_c + + + util_c + + + util_c + diff --git a/apps/decoder.c b/apps/decoder.c index 39c4f19824..c9eb700df3 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -168,6 +168,12 @@ typedef struct } IVAS_DEC_HRTF_BINARY_WRAPPER; #ifdef IVAS_RTPDUMP +#define JITTER_DURATION_MS ( 60 ) +#ifdef DEBUGGING +#define DEBUG_PRINT fprintf +#else +#define DEBUG_PRINT( ... ) +#endif typedef struct { @@ -177,8 +183,8 @@ typedef struct typedef struct { - uint8_t packet[IVAS_NOMINAL_RTP_BYTES_PER_FRAME * MAX_FRAMES_PER_RTP_PACKET]; - PIDATA_TS piData[IVAS_PI_MAX_ID * MAX_FRAMES_PER_RTP_PACKET]; + uint8_t packet[IVAS_NOMINAL_RTP_BYTES_PER_FRAME * IVAS_MAX_FRAMES_PER_RTP_PACKET]; + PIDATA_TS piData[IVAS_PI_MAX_ID * IVAS_MAX_FRAMES_PER_RTP_PACKET]; FILE *f_rtpstream; IVAS_RTP_CODEC codecId; @@ -187,11 +193,18 @@ typedef struct 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 void IVAS_RTP_Term( IVAS_RTP *rtp ) @@ -210,6 +223,8 @@ static ivas_error IVAS_RTP_Init( IVAS_RTP *rtp, FILE *f_rtpstream ) memset( rtp, 0, sizeof( IVAS_RTP ) ); + rtp->unpackCfg.jitterMarginMs = JITTER_DURATION_MS; + error = IVAS_RTP_UNPACK_Open( &rtp->hUnpack, &rtp->unpackCfg ); rtp->f_rtpstream = f_rtpstream; @@ -223,11 +238,9 @@ static ivas_error readNextFrame( IVAS_RTP *rtp, IVAS_DEC_HANDLE hIvasDec, uint8_ { ivas_error error = IVAS_ERR_OK; IVAS_DATA_BUFFER packedFrame = { 0, 0, NULL }; - uint32_t packetLen = 0u; - bool speechLostIndicated = false; -#ifdef RTP_S4_251135_CR26253_0016_REV1 - IVAS_RTP_SR_INFO srInfo; -#endif + size_t packetLen = 0u; + IVAS_RTP_CODEC codecId = IVAS_RTP_IVAS; + bool isAMRWB_IOmode = false; packedFrame.buffer = au; packedFrame.capacity = ( IVAS_MAX_BITS_PER_FRAME / 8 ); @@ -245,7 +258,7 @@ static ivas_error readNextFrame( IVAS_RTP *rtp, IVAS_DEC_HANDLE hIvasDec, uint8_ } nread = fread( rtp->packet, sizeof( uint8_t ), packetLen, rtp->f_rtpstream ); /* Read Packet */ - if ( nread == 0 ) + if ( nread < packetLen ) { return IVAS_ERR_END_OF_FILE; } @@ -257,7 +270,7 @@ static ivas_error readNextFrame( IVAS_RTP *rtp, IVAS_DEC_HANDLE hIvasDec, uint8_ &rtp->numFramesInPacket, &rtp->numPiDataInPacket, &rtp->remoteRequestBitmap ) ) != IVAS_ERR_OK ) { - fprintf( stderr, "failed to unpack RTP packet\n" ); + fprintf( stderr, "failed to unpack RTP packet error = %s\n", ivas_error_to_string( error ) ); return error; } @@ -279,9 +292,9 @@ static ivas_error readNextFrame( IVAS_RTP *rtp, IVAS_DEC_HANDLE hIvasDec, uint8_ } #ifdef RTP_S4_251135_CR26253_0016_REV1 - error = IVAS_RTP_UNPACK_PullFrame( rtp->hUnpack, &rtp->codecId, &srInfo, &packedFrame, rtpTimeStamp, rtpSequenceNumber, &speechLostIndicated ); + 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, &rtp->codecId, &packedFrame, rtpTimeStamp, rtpSequenceNumber, &speechLostIndicated ); + error = IVAS_RTP_UNPACK_PullFrame( rtp->hUnpack, &codecId, &packedFrame, auSizeBits, rtpTimeStamp, rtpSequenceNumber, &rtp->speechLostIndicated, &isAMRWB_IOmode ); #endif if ( error != IVAS_ERR_OK ) { @@ -289,9 +302,31 @@ static ivas_error readNextFrame( IVAS_RTP *rtp, IVAS_DEC_HANDLE hIvasDec, uint8_ return error; } - *auSizeBits = (int16_t) ( packedFrame.length * 8 ); - *qBit = !speechLostIndicated; + 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; while ( rtp->nProcPiData < rtp->nReadPiData && @@ -305,13 +340,17 @@ static ivas_error readNextFrame( IVAS_RTP *rtp, IVAS_DEC_HANDLE hIvasDec, uint8_ { case IVAS_PI_SCENE_ORIENTATION: { - error = IVAS_DEC_feedSinglePIorientation( hIvasDec, true, &piData->data.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: { - error = IVAS_DEC_feedSinglePIorientation( hIvasDec, true, &piData->data.deviceCompensated.orientation ); + 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; @@ -339,7 +378,6 @@ static ivas_error readNextFrame( IVAS_RTP *rtp, IVAS_DEC_HANDLE hIvasDec, uint8_ 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 ); @@ -348,6 +386,117 @@ 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 int 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 -1; + } + + 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, JITTER_DURATION_MS, 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 0; + +cleanup: + IVAS_DEC_Close( phIvasDec ); + + return -1; +} +#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() * @@ -648,7 +797,11 @@ int main( if ( arg.voipMode ) { +#ifdef IVAS_RTPDUMP + if ( ( error = IVAS_DEC_EnableVoIP( hIvasDec, JITTER_DURATION_MS, arg.inputFormat ) ) != IVAS_ERR_OK ) +#else if ( ( error = IVAS_DEC_EnableVoIP( hIvasDec, 60, arg.inputFormat ) ) != IVAS_ERR_OK ) +#endif { fprintf( stderr, "\nCould not enable VOIP: %s\n\n", IVAS_DEC_GetErrorMessage( error ) ); goto cleanup; @@ -920,7 +1073,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 { @@ -3178,7 +3335,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 */ @@ -3212,7 +3373,8 @@ static ivas_error decodeVoIP( int16_t i; FILE *f_rtpstream = NULL; #ifdef IVAS_RTPDUMP - IVAS_RTP ivasRtp; + IVAS_RTP ivasRtp = {0}; + IVAS_DEC_HANDLE hIvasDec = *phIvasDec; #else EVS_RTPDUMP_DEPACKER rtpdumpDepacker; EVS_RTPDUMP_DEPACKER_ERROR rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_NO_ERROR; @@ -3375,6 +3537,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; + int err = restartDecoder( + &hIvasDec, + newCodecInPacket, + &arg, + NULL, /* ToDo : Provide rendererConfig */ + NULL /* ToDo : Provide LS Custom Data */ + ); + if ( err < 0 ) + { + 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 ) { diff --git a/apps/encoder.c b/apps/encoder.c index 861250a5a0..14db97bc1a 100644 --- a/apps/encoder.c +++ b/apps/encoder.c @@ -223,6 +223,17 @@ int main( #ifdef IVAS_RTPDUMP FILE *f_rtpstream = NULL; IVAS_RTP_PACK_HANDLE hPack = NULL; + + uint8_t au[IVAS_MAX_BITS_PER_FRAME / 8]; + uint8_t packet[IVAS_NOMINAL_RTP_BYTES_PER_FRAME * IVAS_MAX_FRAMES_PER_RTP_PACKET]; + IVAS_DATA_BUFFER packedFrame = { 0, 0, NULL }; + IVAS_DATA_BUFFER rtpPacket = { 0, 0, NULL }; + uint32_t numFramesInPayload = 0, packetLength = 0; + + packedFrame.capacity = sizeof( au ); + packedFrame.buffer = au; + rtpPacket.capacity = sizeof( packet ); + rtpPacket.buffer = packet; #endif /*------------------------------------------------------------------------------------------* @@ -729,11 +740,7 @@ int main( goto cleanup; } -#ifdef IVAS_RTPDUMP - if ( ( numSamplesRead == 0 ) && ( IVAS_RTP_PACK_GetNumFrames( hPack ) == 0 ) ) -#else if ( numSamplesRead == 0 ) -#endif { /* end of input data */ break; @@ -863,19 +870,12 @@ int main( #ifdef IVAS_RTPDUMP if ( hPack ) { - uint8_t au[IVAS_MAX_BITS_PER_FRAME / 8]; - uint8_t packet[IVAS_NOMINAL_RTP_BYTES_PER_FRAME * MAX_FRAMES_PER_RTP_PACKET]; - uint32_t n = numBits / 8, x = 0, numFramesInPayload = 0, packetLength = 0; + uint32_t n = numBits / 8, x = 0; uint16_t *bitstrm = bitStream; - IVAS_DATA_BUFFER packedFrame = { 0, 0, NULL }; - IVAS_DATA_BUFFER rtpPacket = { 0, 0, NULL }; - - packedFrame.capacity = sizeof( au ); - packedFrame.buffer = au; - rtpPacket.capacity = sizeof( packet ); - rtpPacket.buffer = packet; /* Pack Encoded Stream */ + packedFrame.length = 0; + rtpPacket.length = 0; while ( n ) { x = ( ( bitstrm[0] << 7 ) | ( bitstrm[1] << 6 ) | ( bitstrm[2] << 5 ) | ( bitstrm[3] << 4 ) | @@ -1059,6 +1059,21 @@ cleanup: #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; + } + + /* File Format = Packet Length (uint32_t) + Packet bytes */ + packetLength = (uint32_t) rtpPacket.length; + fwrite( &packetLength, sizeof( packetLength ), 1, f_rtpstream ); + fwrite( rtpPacket.buffer, sizeof( uint8_t ), packetLength, f_rtpstream ); + } + IVAS_RTP_PACK_Close( &hPack ); } @@ -1965,9 +1980,9 @@ static bool parseCmdlIVAS_enc( else { arg->numFramesPerPacket = atoi( argv[i++] ); - if ( arg->numFramesPerPacket > MAX_FRAMES_PER_RTP_PACKET ) + if ( arg->numFramesPerPacket > IVAS_MAX_FRAMES_PER_RTP_PACKET ) { - fprintf( stderr, "numFramesPerPacket(%d) exceeds max frames per packet (%d) \n", arg->numFramesPerPacket, 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; } } diff --git a/lib_dec/lib_dec.c b/lib_dec/lib_dec.c index f9c9646c9f..11e208218a 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; } diff --git a/lib_util/ivas_bpool.c b/lib_util/ivas_bpool.c index cea33509ff..23754a0c11 100644 --- a/lib_util/ivas_bpool.c +++ b/lib_util/ivas_bpool.c @@ -89,7 +89,7 @@ ivas_error BPOOL_Create( BPOOL_HANDLE *pHandle, size_t bufferSize, uint32_t numB void BPOOL_Destroy( BPOOL_HANDLE *pHandle ) { - if ( pHandle != NULL ) + if ( ( pHandle != NULL ) && ( *pHandle != NULL ) ) { mtx_destroy( &( *pHandle )->lock ); free( *pHandle ); @@ -146,9 +146,9 @@ 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 ) { - uint32_t idx; + uint32_t numFreeBuffers; mtx_lock( &handle->lock ); - idx = handle->numFreeBuffers; + numFreeBuffers = handle->numFreeBuffers; mtx_unlock( &handle->lock ); - return idx; + return numFreeBuffers; } diff --git a/lib_util/ivas_bpool.h b/lib_util/ivas_bpool.h index c4cd45a736..d929ae7415 100644 --- a/lib_util/ivas_bpool.h +++ b/lib_util/ivas_bpool.h @@ -41,18 +41,18 @@ 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); +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 ); +ivas_error BPOOL_GetBuffer( BPOOL_HANDLE handle, void **dataPtr ); /* return the buffer back to pool */ -ivas_error BPOOL_FreeBuffer(BPOOL_HANDLE handle, void *dataPtr); +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); +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 index db038938a5..b72fcee9e4 100644 --- a/lib_util/ivas_queue.c +++ b/lib_util/ivas_queue.c @@ -64,10 +64,10 @@ ivas_error QUEUE_Create( QUEUE_HANDLE *pHandle ) return IVAS_ERR_OK; } -/* Destroy the queue and all free-up all allocated memory */ +/* Destroy the queue and free-up all allocated memory */ void QUEUE_Destroy( QUEUE_HANDLE *pHandle ) { - if ( pHandle != NULL ) + if ( ( pHandle != NULL ) && ( *pHandle != NULL ) ) { mtx_destroy( &( *pHandle )->lock ); free( *pHandle ); @@ -92,9 +92,9 @@ void QUEUE_Push( QUEUE_HANDLE handle, NODE *node ) } /* return the buffer back to pool */ -NODE * QUEUE_Pop( QUEUE_HANDLE handle ) +NODE *QUEUE_Pop( QUEUE_HANDLE handle ) { - NODE * node; + NODE *node; mtx_lock( &handle->lock ); node = handle->front; handle->front = handle->front->next; @@ -110,17 +110,29 @@ NODE * QUEUE_Pop( QUEUE_HANDLE handle ) /* returns the first element in the queue */ NODE *QUEUE_Front( QUEUE_HANDLE handle ) { - return handle->front; + 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 ) { - return handle->back; + 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 ) { - return handle->size; + uint32_t numNodes; + mtx_lock( &handle->lock ); + numNodes = handle->size; + mtx_unlock( &handle->lock ); + return numNodes; } diff --git a/lib_util/ivas_rtp_api.h b/lib_util/ivas_rtp_api.h index 6b8ebb470a..597258f9f1 100644 --- a/lib_util/ivas_rtp_api.h +++ b/lib_util/ivas_rtp_api.h @@ -75,7 +75,7 @@ extern "C" * */ -#define MAX_FRAMES_PER_RTP_PACKET ( 8 ) /* Max supported frames per RTP packet */ +#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 @@ -301,17 +301,6 @@ extern "C" uint32_t maxFramesPerPacket; /* maximum no of frame per packet desired during the session */ } IVAS_RTP_PACK_CONFIG; - /* 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 */ - ); - /* 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 */ @@ -325,6 +314,17 @@ extern "C" 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 * @@ -408,7 +408,7 @@ extern "C" /* Initial configuration for rtp unpacker */ typedef struct { - uint32_t maxNumberOfPayloadsInFifo; /* input payload fifo 's max length */ + uint32_t jitterMarginMs; /* expected max jitter duration in milliseconds */ } IVAS_RTP_UNPACK_CONFIG; /* Open an instance of the RTP unpacker and return a handle to rtp unpacker on success @@ -483,8 +483,7 @@ extern "C" ); /* Push a received rtp Ivas Packet to unpacker to extract number of frames, pi data and - * any remote request present in the Packet. Caller must extract RTP header and header - * extension and feed Ivas Packet alongwith RTP Timestamp and sequence number. + * 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 @@ -492,8 +491,7 @@ extern "C" * * Example usage : - * ================== - * err = IVAS_RTP_UNPACK_PushPacket(hIvasUnpack, packet, rtpTs, seqNum, - * &nFrames, &nPiData, &reqBitmap); + * 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 @@ -560,12 +558,14 @@ extern "C" 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 */ - 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 */ + 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 diff --git a/lib_util/ivas_rtp_internal.h b/lib_util/ivas_rtp_internal.h index b74bdbe8d5..0acfcd2161 100644 --- a/lib_util/ivas_rtp_internal.h +++ b/lib_util/ivas_rtp_internal.h @@ -75,6 +75,7 @@ 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 */ diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index 7bbc86171d..367aea06c8 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -112,14 +112,24 @@ typedef struct FRAME_NODE 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 lock; /* Lock to handle concurrent API invocation */ - RTP_HEADER header; - BPOOL_HANDLE nodePool; - IVAS_RTP_PACK_CONFIG initConfig; - IVAS_RTP_REQUEST_VALUE requests[IVAS_REQUEST_MAX]; - QUEUE_HANDLE frameQ; /* frames queue */ + 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 @@ -127,9 +137,10 @@ 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 */ + IVAS_RTP_SR_INFO srInfo; /* Split Rendering Info */ +#endif /* RTP_S4_251135_CR26253_0016_REV1 */ } TOC_INFO; typedef struct PIDATA_NODE @@ -150,9 +161,11 @@ typedef struct UNPACK_NODE struct IVAS_RTP_UNPACK { - mtx_t lock; /* Lock to handle concurrent API invocation */ + mtx_t apilock; /* Lock to handle concurrent API invocation */ + size_t maxNumberOfFrames; + size_t maxNumberOfPiData; RTP_HEADER header; - BPOOL_HANDLE nodePool; + BPOOL_HANDLE unpackNodePool; BPOOL_HANDLE piDataPool; IVAS_RTP_UNPACK_CONFIG initConfig; IVAS_RTP_REQUEST_VALUE requests[IVAS_REQUEST_MAX]; @@ -170,7 +183,7 @@ static const uint32_t evsFrameSizeInBits[] = { }; static const uint32_t amrWBIOFrameSizeInBits[] = { - 132, 177, 253, 285, 317, 365, 397, 461, 477, 40 + 132, 177, 253, 285, 317, 365, 397, 461, 477, 35 }; @@ -400,7 +413,7 @@ static ivas_error UnpackRtpPacket( return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "Extension Header indicated but insufficient input buffer" ); } - header->extData = realloc( header->extData, header->numExtUnits * 4 ); + header->extData = realloc( header->extData, header->numExtUnits * 4 ); if ( header->extData == NULL ) { return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "failed to allocate extdata" ); @@ -418,7 +431,8 @@ static ivas_error getBitrateFromCodecAndFrameSize( uint32_t frameLengthBytes, uint32_t *bitrateKbps, uint32_t *frameLenInBits, - uint8_t *tocByte ) + uint8_t *tocByte, + bool *isAmrwbIOMode ) { size_t n; *bitrateKbps = 0; @@ -457,6 +471,7 @@ static ivas_error getBitrateFromCodecAndFrameSize( 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 */ @@ -486,16 +501,17 @@ ivas_error IVAS_RTP_PACK_Open( return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Cannot allocate memory for IVAS rtppack handle" ); } - error = BPOOL_Create( &hPack->nodePool, sizeof( FRAME_NODE ), IVAS_MAX_FRAMES_IN_QUEUE ); + error = BPOOL_Create( &hPack->packNodePool, sizeof( FRAME_NODE ), IVAS_MAX_FRAMES_IN_QUEUE ); ERR_CHECK_RETURN( error ); error = QUEUE_Create( &hPack->frameQ ); ERR_CHECK_RETURN( error ); - mtx_init( &hPack->lock, 0 ); + mtx_init( &hPack->apilock, 0 ); hPack->initConfig = *config; initRequests( hPack->requests ); InitRtpHeader( &hPack->header ); + initPiDataFrame( &hPack->piDataCache ); *phPack = hPack; return IVAS_ERR_OK; } @@ -515,9 +531,9 @@ void IVAS_RTP_PACK_Close( hPack = *phPack; QUEUE_Destroy( &hPack->frameQ ); - mtx_destroy( &hPack->lock ); - BPOOL_Destroy( &hPack->nodePool ); - free ( hPack->header.extData ); + mtx_destroy( &hPack->apilock ); + BPOOL_Destroy( &hPack->packNodePool ); + free( hPack->header.extData ); free( hPack ); *phPack = NULL; } @@ -839,36 +855,28 @@ static ivas_error getSRToCByte( } #endif /* RTP_S4_251135_CR26253_0016_REV1 */ -static ivas_error addNewFrameNode( - IVAS_RTP_PACK_HANDLE hPack, /* i/o : IVAS rtp packer handle */ - FRAME_NODE **newNode ) +static void addPackedPiDataToFrame( PIDATA_FRAME *piDataFrm, const PIDATA_PACKED *packedPiData, uint32_t piDataType ) { - FRAME_NODE *node = NULL; - ivas_error error = BPOOL_GetBuffer( hPack->nodePool, (void **) &node ); - ERR_CHECK_RETURN( error ); - - node->auNumBits = 0; - node->tocNumBytes = 0; - node->next = NULL; - - initPiDataFrame( &node->piDataFrame ); + piDataFrm->piData[piDataType].size = packedPiData->size; + memcpy( piDataFrm->piData[piDataType].data, packedPiData->data, packedPiData->size ); - /* Add to frames FiFo */ - QUEUE_Push( hPack->frameQ, (NODE *) node ); + /* 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++; + } - *newNode = node; - return IVAS_ERR_OK; + /* 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--; + } } -/* Push a single IVAS/EVS frame to rtp packer - * - * If multiple frames per RTP Payload are desired, multiple frames must be explicitly - * pushed before a call to IVAS_RTP_PACK_GetPacket to generate a rtp packet. - * - * It is possible to have variable frames per packet until maxFramesPerPacket frames - * if IVAS_RTP_PACK_GetPacket is invoked asyncronously w.r.t this api. - * - */ 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) */ @@ -882,42 +890,73 @@ ivas_error IVAS_RTP_PACK_PushFrame( uint32_t bitrate = 0; uint32_t frameLengthInBits = 0; uint8_t tocByte = 0; - FRAME_NODE *node = (FRAME_NODE *) QUEUE_Back( hPack->frameQ ); - - /* Check if a node was added due to Generic PI Data push */ - if ( ( node == NULL ) || ( node->tocNumBytes != 0 ) ) - { - error = addNewFrameNode( hPack, &node ); - ERR_CHECK_RETURN( error ); - } +#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 */ - node->toc[0] = TOC_INDICATE_NO_DATA; - node->tocNumBytes = 1; - node->auNumBits = 0; + 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 ); + error = getBitrateFromCodecAndFrameSize( codecId, frameBuffer->length, &bitrate, &frameLengthInBits, &tocByte, &hPack->amrwbIOMode ); ERR_CHECK_RETURN( error ); + } - node->toc[0] = tocByte; - node->tocNumBytes = 1; - node->auNumBits = frameLengthInBits; - memcpy( node->au, frameBuffer->buffer, frameBuffer->length ); + /* 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 ) + 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++ ) { - node->toc[0] = TOC_INDICATE_SR; - error = getSRToCByte( srInfo, bitrate, &node->toc[1] ); - ERR_CHECK_RETURN( error ); - node->tocNumBytes = 2; + 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 ); + } } -#endif + initPiDataFrame( piDataFrame ); /* Reset Cache */ } + mtx_unlock( &hPack->apilock ); + + /* Add to frames FiFo */ + QUEUE_Push( hPack->frameQ, (NODE *) node ); return IVAS_ERR_OK; } @@ -926,7 +965,12 @@ uint32_t IVAS_RTP_PACK_GetNumFrames( IVAS_RTP_PACK_HANDLE hPack /* i/o : IVAS rtp packer handle */ ) { - return QUEUE_Size( hPack->frameQ ); + uint32_t nFrames = 0; + if ( hPack ) + { + nFrames = QUEUE_Size( hPack->frameQ ); + } + return nFrames; } /* Push single PI data to rtp packer @@ -941,11 +985,8 @@ ivas_error IVAS_RTP_PACK_PushPiData( const IVAS_PIDATA_GENERIC *data /* i : pointer to the PIData stucture */ ) { - IVAS_PI_TYPE piDataType; ivas_error error = IVAS_ERR_OK; - FRAME_NODE *node = (FRAME_NODE *) QUEUE_Back( hPack->frameQ ); - PIDATA_FRAME *piDataFrm = NULL; - uint8_t PM = 0; + 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 */ @@ -954,137 +995,120 @@ ivas_error IVAS_RTP_PACK_PushPiData( return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Wrong PI Data provided" ); } - /* No frame in Queue to associate PI Data, create one */ - if ( node == NULL ) - { - /* PI data received before any frame in queue is generic */ - PM = PI_HEADER_PM_GENERIC; - - /* Add a new node in frame queue, toc is still zero indicating frame is expected */ - error = addNewFrameNode( hPack, &node ); - ERR_CHECK_RETURN( error ); - } - - piDataFrm = &node->piDataFrame; - piDataType = data->piDataType; - - error = PI_PackData( data, &piDataFrm->piData[piDataType], PM ); + error = PI_PackData( data, &packedPiData, 0 ); ERR_CHECK_RETURN( error ); - /* 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 ) ) + mtx_lock( &hPack->apilock ); /* Lock to prevent access to shared cache */ { - piDataFrm->piDataBitmap &= ~( 1u << IVAS_PI_NO_DATA ); - piDataFrm->numPiDataAvailable--; + 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; } -/* 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 - * - */ +#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_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 *packet, /* o : encapsulated rtp packet */ + 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 *head = (FRAME_NODE *) QUEUE_Front( hPack->frameQ ); - FRAME_NODE *node = NULL; - size_t nBytes = packet->length, numEBytes = 0; + FRAME_NODE *availableFrameNodes[IVAS_MAX_FRAMES_PER_RTP_PACKET]; + size_t numEBytes = 0; - if ( packet == NULL ) + if ( payload == NULL ) { return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Output data buffer is NULL" ); } - /* Calculate number of frames and PI data present */ - node = head; - for ( n = 0; n < hPack->initConfig.maxFramesPerPacket && ( NULL != node ); n++, node = node->next ) + /* 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++ ) { - numFrame++; + 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; } - *numFramesInPayload = numFrame; /* numFrames in Packet */ /* IVAS Payload starts with E-Bytes */ - packEBytes( hPack, ( numPiDataPresent > 0 ), packet->capacity, &packet->buffer[packet->length], &numEBytes ); - nBytes += numEBytes; + packEBytes( hPack, ( numPiDataPresent > 0 ), payload->capacity, &payload->buffer[payload->length], &numEBytes ); + payload->length += numEBytes; /* ToC bytes (atleast 1 byte per frame, 2 if SR )*/ - node = head; - for ( n = 0; n < numFrame && ( NULL != node ); n++, node = node->next ) + for ( n = 0; n < numFrame; n++ ) { + FRAME_NODE *node = availableFrameNodes[n]; uint8_t fBit = ( n != ( numFrame - 1 ) ) ? TOC_HEADER_FOLLOWS : 0; /* Next ToC present */ - if ( nBytes < packet->capacity ) - { - packet->buffer[nBytes++] = ( node->toc[0] | fBit ); - } - else - { - return IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient memory to write RTP Payload" ); - } + WRITE_BYTE_PAYLOAD_OR_EXIT( payload, ( node->toc[0] | fBit ) ); +#ifdef RTP_S4_251135_CR26253_0016_REV1 if ( node->tocNumBytes == 2 ) { - if ( nBytes < packet->capacity ) - { - packet->buffer[nBytes++] = node->toc[1]; - } - else - { - return IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient memory to write RTP Payload" ); - } + WRITE_BYTE_PAYLOAD_OR_EXIT( payload, node->toc[1] ); } +#endif } /* Frame Data */ - node = head; - for ( n = 0; n < numFrame && ( NULL != node ); n++, node = node->next ) + for ( n = 0; n < numFrame; n++ ) { - size_t len = ( node->auNumBits + 7 ) >> 3; /* zero padded length in bytes */ - if ( nBytes + len < packet->capacity ) + FRAME_NODE *node = availableFrameNodes[n]; + size_t frameLength = ( node->auNumBits + 7 ) >> 3; /* zero padded length in bytes */ + if ( payload->length + frameLength <= payload->capacity ) { - memcpy( &packet->buffer[nBytes], node->au, len ); + memcpy( &payload->buffer[payload->length], node->au, frameLength ); } else { - return IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient memory to write RTP Payload" ); + error = IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient memory to write RTP Payload" ); + goto err_exit; } - nBytes += len; + payload->length += frameLength; } - packet->length = nBytes; /* update packet length before PI packing */ - /* PI Data */ if ( numPiDataPresent > 0 ) { bool skipPiData = false; + size_t nBytes = payload->length; uint32_t numPiDataWritten = 0; - node = head; - for ( n = 0; n < numFrame && ( NULL != node ); n++, node = node->next ) + 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 ); @@ -1098,9 +1122,9 @@ ivas_error IVAS_RTP_PACK_GetPayload( 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 < packet->capacity ) + if ( nBytes + piDataFrame->piData[piDataType].size < payload->capacity ) { - memcpy( &packet->buffer[nBytes], piDataFrame->piData[piDataType].data, piDataFrame->piData[piDataType].size ); + memcpy( &payload->buffer[nBytes], piDataFrame->piData[piDataType].data, piDataFrame->piData[piDataType].size ); nBytes += piDataFrame->piData[piDataType].size; } else @@ -1113,25 +1137,23 @@ ivas_error IVAS_RTP_PACK_GetPayload( } if ( !skipPiData ) { - packet->length = nBytes; /* update packet length after PI packing */ + payload->length = nBytes; /* update payload length after PI packing */ } } +err_exit: /* Pop frames from Queue */ for ( n = 0; n < numFrame; n++ ) { - node = (FRAME_NODE *) QUEUE_Pop( hPack->frameQ ); - if ( node == NULL ) + if ( BPOOL_FreeBuffer( hPack->packNodePool, availableFrameNodes[n] ) != IVAS_ERR_OK ) { - assert( 0 ); /* catastrophic error, implementation issue */ - break; + assert( 0 ); /* catastrophic error if this fails */ + return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "frame node could not be freed" ); } - - error = BPOOL_FreeBuffer( hPack->nodePool, node ); - assert( error == IVAS_ERR_OK ); /* catastrophic error if this fails */ + availableFrameNodes[n] = NULL; } - return IVAS_ERR_OK; + return error; } ivas_error IVAS_RTP_PACK_GetPacket( @@ -1158,6 +1180,7 @@ ivas_error IVAS_RTP_UNPACK_Open( { ivas_error error = IVAS_ERR_OK; IVAS_RTP_UNPACK_HANDLE hUnpack; + if ( phUnpack == NULL || config == NULL ) { return IVAS_ERR_UNEXPECTED_NULL_POINTER; @@ -1169,10 +1192,15 @@ ivas_error IVAS_RTP_UNPACK_Open( return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Cannot allocate memory for IVAS rtp unpack handle" ); } - error = BPOOL_Create( &hUnpack->nodePool, sizeof( UNPACK_NODE ), IVAS_MAX_FRAMES_IN_QUEUE ); + /* maxNumberOfFrames = Ping Pong RTP Packet with max frames per packet + any jitter adjustment frames */ + hUnpack->maxNumberOfFrames += IVAS_MAX_FRAMES_PER_RTP_PACKET * 2; + hUnpack->maxNumberOfFrames += ( ( config->jitterMarginMs * IVAS_NUM_FRAMES_PER_SEC + 1000 - 1 ) / 1000 ); /* Jitter in Packets + Ping/Pong */ + 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 ), IVAS_MAX_FRAMES_IN_QUEUE * IVAS_PI_MAX_ID ); + error = BPOOL_Create( &hUnpack->piDataPool, sizeof( PIDATA_NODE ), hUnpack->maxNumberOfPiData ); ERR_CHECK_RETURN( error ); error = QUEUE_Create( &hUnpack->frameQ ); @@ -1182,7 +1210,7 @@ ivas_error IVAS_RTP_UNPACK_Open( ERR_CHECK_RETURN( error ); hUnpack->initConfig = *config; - mtx_init( &hUnpack->lock, 0 ); + mtx_init( &hUnpack->apilock, 0 ); initRequests( hUnpack->requests ); @@ -1204,12 +1232,12 @@ void IVAS_RTP_UNPACK_Close( } hUnpack = *phUnpack; - mtx_destroy( &hUnpack->lock ); + mtx_destroy( &hUnpack->apilock ); QUEUE_Destroy( &hUnpack->frameQ ); QUEUE_Destroy( &hUnpack->piDataQ ); BPOOL_Destroy( &hUnpack->piDataPool ); - BPOOL_Destroy( &hUnpack->nodePool ); - free ( hUnpack->header.extData ); + BPOOL_Destroy( &hUnpack->unpackNodePool ); + free( hUnpack->header.extData ); free( hUnpack ); *phUnpack = NULL; } @@ -1394,6 +1422,7 @@ static ivas_error parseToCByte( const IVAS_DATA_BUFFER *payload, uint32_t *numBy 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 ) { @@ -1604,7 +1633,7 @@ ivas_error IVAS_RTP_UNPACK_PushPayload( UNPACK_NODE *node = NULL; /* Get a new node */ - error = BPOOL_GetBuffer( hUnpack->nodePool, (void **) &node ); + error = BPOOL_GetBuffer( hUnpack->unpackNodePool, (void **) &node ); ERR_CHECK_RETURN( error ); node->next = NULL; @@ -1613,7 +1642,7 @@ ivas_error IVAS_RTP_UNPACK_PushPayload( node->toc = toc[n]; frameSizeBytes = ( node->toc.auNumBits + 7 ) / 8; - if ( nBytes + frameSizeBytes < payload->length ) + if ( nBytes + frameSizeBytes <= payload->length ) { memcpy( node->au, &payload->buffer[nBytes], frameSizeBytes ); nBytes += frameSizeBytes; @@ -1707,12 +1736,14 @@ 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 */ - 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 */ + 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; @@ -1731,6 +1762,11 @@ ivas_error IVAS_RTP_UNPACK_PullFrame( frameBuffer->length = length; } + if ( frameSizeInBits != NULL ) + { + *frameSizeInBits = (int16_t) node->toc.auNumBits; + } + if ( receivedCodecId != NULL ) { *receivedCodecId = node->toc.codecId; @@ -1758,7 +1794,12 @@ ivas_error IVAS_RTP_UNPACK_PullFrame( *speechLostIndicated = node->toc.speechLostIndicated; } - return BPOOL_FreeBuffer( hUnpack->nodePool, node ); + if ( isAMRWB_IOmode != NULL ) + { + *isAMRWB_IOmode = node->toc.isAMRWB_IOmode; + } + + return BPOOL_FreeBuffer( hUnpack->unpackNodePool, node ); } ivas_error IVAS_RTP_UNPACK_PullNextPiData( @@ -1781,7 +1822,7 @@ ivas_error IVAS_RTP_UNPACK_PullNextPiData( if ( data != NULL && ( pi->size <= capacity ) ) { - *data = *pi; + memcpy( data, pi, pi->size ); } if ( timestamp != NULL ) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index ba33abe59e..8e918d76a2 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -59,9 +59,9 @@ static uint16_t readInt16( const uint8_t *buffer ) *-----------------------------------------------------------------------*/ static int16_t ivasPayload_convertToQ15( float value ) { - value = ( value * 32768.0 ); - value = value > +32767.0 ? +32767.0 : value; - value = value < -32768.0 ? -32768.0 : value; + value = ( value * 32768.0f ); + value = value > +32767.0f ? +32767.0f : value; + value = value < -32768.0f ? -32768.0f : value; return (int16_t) ( value ); } @@ -166,10 +166,10 @@ static ivas_error unpackOrientation( const uint8_t *buffer, uint32_t numDataByte } piData->size = sizeof( IVAS_PIDATA_ORIENTATION ); - orientation->orientation.w = ((int16_t) readInt16( &buffer[0] )) / 32768.0f; - orientation->orientation.x = ((int16_t) readInt16( &buffer[2] )) / 32768.0f; - orientation->orientation.y = ((int16_t) readInt16( &buffer[4] )) / 32768.0f; - orientation->orientation.z = ((int16_t) readInt16( &buffer[6] )) / 32768.0f; + orientation->orientation.w = ( (int16_t) readInt16( &buffer[0] ) ) / 32768.0f; + orientation->orientation.x = ( (int16_t) readInt16( &buffer[2] ) ) / 32768.0f; + orientation->orientation.y = ( (int16_t) readInt16( &buffer[4] ) ) / 32768.0f; + orientation->orientation.z = ( (int16_t) readInt16( &buffer[6] ) ) / 32768.0f; return IVAS_ERR_OK; } @@ -197,7 +197,7 @@ static uint32_t getIndexTable( const float *table, uint32_t tableLength, float v static ivas_error packAcousticEnvironment( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten ) { uint32_t nBytes = 0; - uint32_t packedSize = 1; + uint8_t packedSize = 1; const IVAS_PIDATA_ACOUSTIC_ENV *aeEnv = (const IVAS_PIDATA_ACOUSTIC_ENV *) piData; *nBytesWritten = 0; @@ -217,7 +217,7 @@ static ivas_error packAcousticEnvironment( const IVAS_PIDATA_GENERIC *piData, ui } /* Acoustic Env data is packedSize bytes, header is 2 bytes */ - if ( maxDataBytes < packedSize + 2 ) + if ( maxDataBytes < (uint32_t) packedSize + 2 ) { return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Insufficient space to pack Orientation PI data" ); } diff --git a/lib_util/mutex.h b/lib_util/mutex.h index f3fe493dc6..caf0145e2e 100644 --- a/lib_util/mutex.h +++ b/lib_util/mutex.h @@ -37,7 +37,7 @@ #ifndef _MUTEX_H #define _MUTEX_H -#if defined( __unix__ ) || defined( __linux__ ) || ( defined( __MACH__ ) && defined( __APPLE__ )) +#if defined( __unix__ ) || defined( __linux__ ) || ( defined( __MACH__ ) && defined( __APPLE__ ) ) #include typedef pthread_mutex_t mtx_t; #define _USE_POSIX ( 1 ) -- GitLab From 6dca357c0be08ef737338361cf0cec4b14a51ee5 Mon Sep 17 00:00:00 2001 From: "Singh, Ripinder" Date: Wed, 6 Aug 2025 15:51:05 +1000 Subject: [PATCH 029/100] Add pytest for PACK API - Add python version of Unpack - Check RTP Header for Timestamp, Sequenc Number, Header, SSRC - Check Payload for Codec, Bitrate and AU bytes - Check PI data for Type, Size and Pi data bytes Signed-off-by: Singh, Ripinder --- tests/rtp/ivasrtp.py | 446 ++++++++++++++++++++++++++++++++++++++++++ tests/rtp/test_rtp.py | 355 +++++++++++++++++++++++++++++++++ 2 files changed, 801 insertions(+) create mode 100644 tests/rtp/ivasrtp.py create mode 100644 tests/rtp/test_rtp.py diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py new file mode 100644 index 0000000000..ecb3924866 --- /dev/null +++ b/tests/rtp/ivasrtp.py @@ -0,0 +1,446 @@ +#!/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, ReadError +import json +import base64 +import argparse + +class CODECS(str, Enum): + AMRWB = "amrwb_io" + EVS = "evs" + IVAS = "ivas" + +class SRCODEC(str, Enum): + LCLD = "lcld" + LC3PLUS = "lc3+" + NA = "NONE" + +class BANDWIDTH(str, Enum): + NB = "narrowband" + WB = "wideband" + SWB = "super wideband" + FB = "fullband" + NREQ = "NO_REQ" + +class REQUESTS(str, Enum): + CODEC = "codec", + BR = "bitrate" + BW = "bandwidth" + CA = "ca-mode" + FMT = "format" + SUBFMT = "sub-format" + SRCFG = "sr-config" + +@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) + extensionBytes: list = field(default_factory=list) + + def __init__(self, bitstrm: ConstBitStream): + self.version = bitstrm.read(2).uint + self.padding = bitstrm.read(1).bool + self.extension = bitstrm.read(1).bool + self.csrcCount = bitstrm.read(4).int + self.marker = bitstrm.read(1).bool + self.payloadType = bitstrm.read(7).int + self.sequenceNum = bitstrm.read(16).uintbe + self.timestamp = bitstrm.read(32).uintbe + self.ssrc = bitstrm.read(32).uintbe + self.csrcList = [ bitstrm.read(32).uintbe for _ in range(self.csrcCount) ] + if self.extension: + self.extensionType = bitstrm.read(16).uintbe + self.extensionLength = bitstrm.read(16).uintbe + self.extensionBytes = [ bitstrm.read(32).uintbe for _ in range(self.extensionLength) ] + else: + self.extensionType = 0 + self.extensionLength = 0 + self.extensionBytes = list() + +@dataclass +class SRCONFIG: + enabled: bool = False + 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.NA + +@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) + +@dataclass +class ORIENTATION: + w: float = 0.0 + x: float = 0.0 + y: float = 0.0 + z: float = 0.0 + +@dataclass +class PIDATA: + timestamp: int = 0 + type: str = "NO_PI_DATA" + data: any = None + +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] +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=BANDWIDTH.NREQ, codec=CODECS.IVAS, startIdx=0, endIdx=14, bitrates = ivasBitrates), #111 = IVAS +] + +codedFormats = ["Stereo", "SBA", "MASA", "ISM", "MC", "OMASA", "OSBA", "NO_REQ"] +codedSubFormats = [ + "FOA planar", "HOA2 planar", "HOA3 planar", "FOA", "HOA2", "HOA3", "MASA1", "MASA2", "ISM1", + "ISM2", "ISM3", "ISM4", "ISM1 extended metadata", "ISM2 extended metadata", + "ISM3 extended metadata", "ISM4 extended metadata", "MC 5.1", "MC 7.1", "MC 5.1.2", "MC 5.1.4", + "MC 7.1.4", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", + "Reserved", "Reserved", "Reserved", "Reserved", "OMASA ISM1 1TC", "OMASA ISM2 1TC", + "OMASA ISM3 1TC", "OMASA ISM4 1TC", "OMASA ISM1 2TC", "OMASA ISM2 2TC", "OMASA ISM3 2TC", + "OMASA ISM4 2TC", "OSBA ISM1 FOA planar", "OSBA ISM2 FOA planar", "OSBA ISM3 FOA planar", + "OSBA ISM4 FOA planar", "OSBA ISM1 FOA", "OSBA ISM2 FOA", "OSBA ISM3 FOA", "OSBA ISM4 FOA", + "OSBA ISM1 HOA2 planar", "OSBA ISM2 HOA2 planar", "OSBA ISM3 HOA2 planar", "OSBA ISM4 HOA2 planar", + "OSBA ISM1 HOA2", "OSBA ISM2 HOA2", "OSBA ISM3 HOA2", "OSBA ISM4 HOA2", "OSBA ISM1 HOA3 planar", + "OSBA ISM2 HOA3 planar", "OSBA ISM3 HOA3 planar", "OSBA ISM4 HOA3 planar", "OSBA ISM1 HOA3", + "OSBA ISM2 HOA3", "OSBA ISM3 HOA3", "OSBA ISM4 HOA3"] +PiTypeNames = [ + "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_PI_DATA" +] + +def unpackUnsupported(bitstrm: ConstBitStream, piSize: int) -> any: + #assert False, "Unsupported PI Data" + return base64.b64encode(bitstrm.read(piSize * 8).tobytes()).decode('utf-8') + +def unpackNoPiData(bitstrm: ConstBitStream, piSize: int) -> None: + assert piSize == 0, "NO_PI_DATA should be 0 size" + +def unpackOrientation(bitstrm: ConstBitStream, piSize: int) -> ORIENTATION: + assert piSize == 8, "Incorrect PI Data Size for Orientation" + 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 + return ORIENTATION(w, x, y, z) + +PIDataUnpacker = [ + unpackOrientation, # SCENE_ORIENTATION, + unpackOrientation, # DEVICE_ORIENTATION_COMPENSATED, + unpackOrientation, # DEVICE_ORIENTATION_UNCOMPENSATED + unpackUnsupported, # ACOUSTIC_ENVIRONMENT + unpackUnsupported, # AUDIO_DESCRIPTION + unpackUnsupported, # ISM_NUM + unpackUnsupported, # ISM_ID + unpackUnsupported, # ISM_GAIN + unpackUnsupported, # ISM_ORIENTATION + unpackUnsupported, # ISM_POSITION + unpackUnsupported, # ISM_DISTANCE_ATTENUATION + unpackUnsupported, # ISM_DIRECTIVITY + unpackUnsupported, # DIEGETIC_TYPE + unpackUnsupported, # RESERVED13 + unpackUnsupported, # RESERVED14 + unpackUnsupported, # RESERVED15 + unpackUnsupported, # PLAYBACK_DEVICE_ORIENTATION + unpackUnsupported, # HEAD_ORIENTATION + unpackUnsupported, # LISTENER_POSITION + unpackUnsupported, # DYNAMIC_AUDIO_SUPPRESSION + unpackUnsupported, # AUDIO_FOCUS_DIRECTION + unpackUnsupported, # PI_LATENCY + unpackUnsupported, # R_ISM_ID + unpackUnsupported, # R_ISM_GAIN + unpackUnsupported, # R_ISM_ORIENTATION + unpackUnsupported, # R_ISM_POSITION + unpackUnsupported, # R_ISM_DIRECTION + unpackUnsupported, # RESERVED27 + unpackUnsupported, # RESERVED28 + unpackUnsupported, # RESERVED29 + unpackUnsupported, # RESERVED30 + unpackNoPiData # NO_DATA +] + +@dataclass +class IvasPayload: + lastCodec: CODECS = CODECS.IVAS # Track last frame's codec + frameList: list[FRAME] = field(default_factory=list) + piDataList: list[PIDATA] = field(default_factory=list) + requests: dict [str, any] = field(default_factory=dict) + + def __init__(self, bitstrm: ConstBitStream, rtpTimestamp, lastCodec: CODECS): + self.lastCodec = lastCodec + self.frameList = list[FRAME]() + self.piDataList = list[PIDATA]() + self.requests = dict() + + try: + piIndicated = False + 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: + self.requests[REQUESTS.CODEC] = cmrLookup[T].codec + self.requests[REQUESTS.BR] = 13200 + self.requests[REQUESTS.CA] = BR + self.requests[REQUESTS.BW] = cmrLookup[T].bandwidth + else: + raise Exception("Unsupported BR bits in CA Mode") + elif T == 7: #IVAS + if BR < 14 : + self.requests[REQUESTS.CODEC] = cmrLookup[T].codec + self.requests[REQUESTS.BR] = cmrLookup[T].bitrates[BR] + self.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: + self.requests[REQUESTS.CODEC] = cmrLookup[T].codec + self.requests[REQUESTS.BR] = cmrLookup[T].bitrates[BR] + self.requests[REQUESTS.CA] = -1 + self.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 + self.requests[REQUESTS.BW] = supportedBW[BW] + elif ET == 1 : + S = bitstrm.read(1).bool + FMT = bitstrm.read(3).uint + if not S: + self.requests[REQUESTS.FMT] = codedFormats[FMT] + self.requests[REQUESTS.SUBFMT] = "NO_REQ" + else: + reserved = bitstrm.read(2) + subFMT = bitstrm.read(6).uint + self.requests[REQUESTS.FMT] = "NO_REQ" + self.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 + self.requests[REQUESTS.SRCFG] = SRCONFIG(enabled=True, diegetic=D, yaw=Y, pitch=P, roll=R) + else: + reserved = bitstrm.read(4) + raise Exception("Unsupported subsequent EByte with ET={}".format(ET)) + + # ToC Byte parsing starts with 'F' bit as H bit is already read above + frameTimeStamps = rtpTimestamp + F = True + frmList = list() + while F: + F = bitstrm.read(1).bool + FT = bitstrm.read(2).uint + BR = bitstrm.read(4).uint + + frm = FRAME(timestamp=frameTimeStamps) + 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 self.lastCodec + 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 + frameTimeStamps += 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) + + #Frame AUs here + for (idx, frm) in enumerate(frmList): + auSize = (frm.frmSizeBits + 7) // 8 # Zero padded bytes in amrwb_io mode + frm.au = bitstrm.read(auSize * 8).tobytes() + self.frameList.append(frm) + + # PI Data if Indicated + piTimeStamps = rtpTimestamp + PF = piIndicated + 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) + + self.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)) + +@dataclass +class IvasPacket: + hdr: RTPHDR = field(default_factory=RTPHDR) + payload: IvasPayload = field(default_factory=IvasPayload) + +class IvasRtp: + def __init__(self): + self.packets = list[IvasPacket]() + self.lastCodec: CODECS = CODECS.IVAS # Track last frame's codec + + def dumpToJSON(self, jsonFileName): + with open(jsonFileName, "w") as fd: + packets = list() + for packet in self.packets: + packets.append(asdict(packet)) + json_output = json.dumps(packets, indent=4) + fd.write(json_output) + + 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(bitStrm) + payload = IvasPayload(bitStrm, rtpTimestamp=hdr.timestamp, lastCodec=self.lastCodec) + self.lastCodec = 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, required=True, help="RTP Dump to unpack") + self.parser.add_argument("-j", "--json", type=str, default="unpack.json", help="Output unpacked RTP frames to JSON file") + + def parse(self): + args = self.parser.parse_args() + return args + +if __name__ == "__main__": + args = ArgsParser().parse() + rtp = IvasRtp() + if args.rtpdump: + rtp.unpackFile(args.rtpdump) + rtp.dumpToJSON(args.json) diff --git a/tests/rtp/test_rtp.py b/tests/rtp/test_rtp.py new file mode 100644 index 0000000000..00b4da5c1b --- /dev/null +++ b/tests/rtp/test_rtp.py @@ -0,0 +1,355 @@ +#!/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 + +from tempfile import TemporaryDirectory +from pathlib import Path +from ivasrtp import * +from bitstring import ConstBitStream, BitStream + +THIS_PATH = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.join(os.path.dirname(THIS_PATH), "../../scripts")) + +from tests.conftest import EncoderFrontend, DecoderFrontend + +@pytest.mark.parametrize("dtx", [False, True]) +@pytest.mark.parametrize("bitrate", [6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 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 +): + run_rtp_bitstream_tests ( + CODECS.AMRWB, + bitrate, + "WB", + "OFF", + "MONO", + dtx, + framesPerPacket, + dut_encoder_frontend + ) + +@pytest.mark.parametrize("dtx", [False, True]) +@pytest.mark.parametrize("bitrate", [7200, 8000, 9600, 13200, 16400, 24400, 32000, 48000, 64000, 96000, 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 +): + run_rtp_bitstream_tests ( + CODECS.EVS, + bitrate, + bandwidth, + caMode, + "MONO", + dtx, + framesPerPacket, + dut_encoder_frontend + ) + + +@pytest.mark.parametrize("bitrate", [13200, 24400, 80000, 512000]) +@pytest.mark.parametrize("bandwidth", ["WB", "SWB", "FB"]) +@pytest.mark.parametrize("format", ["STEREO", "SBA", "MC", "MASA"]) +#@pytest.mark.parametrize("bandwidth, format, bitrate", [("WB", "MASA", 13200)]) +@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 +): + run_rtp_bitstream_tests ( + CODECS.IVAS, + bitrate, + bandwidth, + "OFF", + format, + False, + framesPerPacket, + dut_encoder_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, 8]) +def test_rtp_bitstream_ivas_dtx ( + test_info, + bitrate: int, + bandwidth: str, + format: str, + framesPerPacket: int, + dut_encoder_frontend: EncoderFrontend +): + run_rtp_bitstream_tests ( + CODECS.IVAS, + bitrate, + bandwidth, + "OFF", + format, + True, + framesPerPacket, + dut_encoder_frontend + ) + + +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" + + +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 = list[bytes]() + self.refBitStrm = None + with open(g192File, "rb") as fd: + self.refBitStrm = ConstBitStream(fd.read()) + while self.refBitStrm.pos < self.refBitStrm.len: + sync = hex(self.refBitStrm.read(16).intle) + nBits = self.refBitStrm.read(16).intle + assert sync == "0x6b21", "G192 syncword not found at start of packet" + writer = BitStream() + for _ in range(nBits): + bit = "0b1" if self.refBitStrm.read(16).uintle == 129 else "0b0" + writer.append(bit) + self.refPackets.append(writer.tobytes()) + + 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(THIS_PATH).joinpath("../../scripts/testv") + def __init__(self): + self.tvDict = dict() + self.sceneFile = Path(THIS_PATH).joinpath("../../scripts/trajectories/azi_plus_2-ele_plus_2-every-25-rows.csv").absolute() + self.deviceFile = Path(THIS_PATH).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 +): + 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() + + # 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) + + # 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 + ) + + ivasRtp = IvasRtp() + ivasRtp.unpackFile(rtpdumpOut) + for packet in ivasRtp.getPackets(): + validate.packet(packet) -- GitLab From de43d43ad6c169a7b8acbcf4ab2ccc0f6fc678a2 Mon Sep 17 00:00:00 2001 From: Ripinder Singh Date: Tue, 12 Aug 2025 19:15:52 +1000 Subject: [PATCH 030/100] Add dumping of PI Data in decoder, more PI support and bugfixes * API updates for Pack and Unpack APIs * Do not report NO_PI_DATA in unpack * Add support for pack/unpack Listner Position, Diegetic * Fix PI Data structure for LISTERENR POSITION * Fix shift in acoustic env pi data unpack * Add a python implementation of PACK API * Add tests to validate decoded audio and dumped PI data Signed-off-by: Ripinder Singh --- apps/decoder.c | 254 +++++++- apps/encoder.c | 2 +- lib_util/ivas_rtp_api.h | 18 +- lib_util/ivas_rtp_payload.c | 25 +- lib_util/ivas_rtp_pi_data.c | 161 ++++- lib_util/ivas_rtp_pi_data.h | 6 +- tests/requirements.txt | 2 + tests/rtp/ivasrtp.py | 1145 ++++++++++++++++++++++++++++------- tests/rtp/test_rtp.py | 197 ++++-- 9 files changed, 1508 insertions(+), 302 deletions(-) diff --git a/apps/decoder.c b/apps/decoder.c index c9eb700df3..4f2307b3dc 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -130,6 +130,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 @@ -183,10 +187,11 @@ typedef struct typedef struct { - uint8_t packet[IVAS_NOMINAL_RTP_BYTES_PER_FRAME * IVAS_MAX_FRAMES_PER_RTP_PACKET]; + 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]; FILE *f_rtpstream; + FILE *f_piDataOut; IVAS_RTP_CODEC codecId; uint32_t nReadPiData; uint32_t nProcPiData; @@ -207,6 +212,166 @@ typedef struct #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 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 ( rtp->f_rtpstream ) @@ -214,27 +379,34 @@ static void IVAS_RTP_Term( IVAS_RTP *rtp ) fclose( rtp->f_rtpstream ); rtp->f_rtpstream = NULL; } + if ( rtp->f_piDataOut ) + { + 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, FILE *f_rtpstream ) +static ivas_error IVAS_RTP_Init( IVAS_RTP *rtp, FILE *f_rtpstream, FILE *f_piDataOut ) { ivas_error error = IVAS_ERR_OK; memset( rtp, 0, sizeof( IVAS_RTP ) ); - rtp->unpackCfg.jitterMarginMs = JITTER_DURATION_MS; + rtp->unpackCfg.maxFramesPerPacket = IVAS_MAX_FRAMES_PER_RTP_PACKET; error = IVAS_RTP_UNPACK_Open( &rtp->hUnpack, &rtp->unpackCfg ); rtp->f_rtpstream = f_rtpstream; + rtp->f_piDataOut = f_piDataOut; rtp->rtpPacket.buffer = rtp->packet; rtp->rtpPacket.capacity = sizeof( rtp->packet ); return error; } -static ivas_error readNextFrame( IVAS_RTP *rtp, IVAS_DEC_HANDLE hIvasDec, uint8_t *au, int16_t *auSizeBits, uint32_t *rtpTimeStamp, uint16_t *rtpSequenceNumber, uint32_t *nextPacketRcvTime_ms, bool *qBit ) +static ivas_error 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 = { 0, 0, NULL }; @@ -289,6 +461,7 @@ static ivas_error readNextFrame( IVAS_RTP *rtp, IVAS_DEC_HANDLE hIvasDec, uint8_ rtp->nReadPiData++; rtp->numPiDataInPacket--; } + LogPiData( rtp->f_piDataOut, rtp->piData, rtp->nReadPiData ); } #ifdef RTP_S4_251135_CR26253_0016_REV1 @@ -329,8 +502,14 @@ static ivas_error readNextFrame( IVAS_RTP *rtp, IVAS_DEC_HANDLE hIvasDec, uint8_ rtp->numFramesProduced++; *nextPacketRcvTime_ms += 20; + return IVAS_ERR_OK; +} + +static ivas_error 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 ) + rtp->piData[rtp->nProcPiData].timestamp <= rtpTimeStamp ) { PIDATA_TS *piData = &rtp->piData[rtp->nProcPiData++]; if ( hIvasDec ) @@ -356,7 +535,7 @@ static ivas_error readNextFrame( IVAS_RTP *rtp, IVAS_DEC_HANDLE hIvasDec, uint8_ default: { - fprintf( stderr, "Unhandled PI data of type : %d\n", piDataType ); + fprintf( stderr, "Unhandled PI data of type : %s\n", PiDataNames[piDataType] ); } break; } @@ -366,8 +545,7 @@ static ivas_error readNextFrame( IVAS_RTP *rtp, IVAS_DEC_HANDLE hIvasDec, uint8_ } } } - - return IVAS_ERR_OK; + return error; } #endif /* IVAS_RTPDUMP */ @@ -1304,6 +1482,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; @@ -1390,6 +1572,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 ) { @@ -1979,6 +2180,8 @@ static void usage_dec( void ) 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" ); @@ -3270,7 +3473,7 @@ static ivas_error printBitstreamInfoVoip( { auPtr = au; /* might have been set to RTP packet in prev call */ #ifdef IVAS_RTPDUMP - error = readNextFrame( &ivasRtp, NULL, auPtr, &auSizeBits, &rtpTimeStamp, &rtpSequenceNumber, &nextPacketRcvTime_ms, &qBit ); + 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 @@ -3373,8 +3576,10 @@ static ivas_error decodeVoIP( int16_t i; FILE *f_rtpstream = NULL; #ifdef IVAS_RTPDUMP - IVAS_RTP ivasRtp = {0}; + IVAS_RTP ivasRtp = { 0 }; IVAS_DEC_HANDLE hIvasDec = *phIvasDec; + FILE *f_piDataOut = NULL; + int32_t initialTsOffsetSystemAndRTP = 0; #else EVS_RTPDUMP_DEPACKER rtpdumpDepacker; EVS_RTPDUMP_DEPACKER_ERROR rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_NO_ERROR; @@ -3441,7 +3646,17 @@ static ivas_error decodeVoIP( } #ifdef IVAS_RTPDUMP - if ( ( error = IVAS_RTP_Init( &ivasRtp, f_rtpstream ) ) != IVAS_ERR_OK ) + if ( arg.piOutputFilename != NULL ) + { + f_piDataOut = fopen( arg.piOutputFilename, "w" ); + if ( f_piDataOut == NULL ) + { + fprintf( stderr, "could not open: %s\n", arg.piOutputFilename ); + goto cleanup; + } + fprintf( f_piDataOut, "{\n" ); + } + if ( ( error = IVAS_RTP_Init( &ivasRtp, f_rtpstream, f_piDataOut ) ) != IVAS_ERR_OK ) { fprintf( stderr, "error in IVAS_RTP_Init(): %d\n", error ); goto cleanup; @@ -3496,7 +3711,8 @@ static ivas_error decodeVoIP( { auPtr = au; /* might have been set to RTP packet in prev call */ #ifdef IVAS_RTPDUMP - error = readNextFrame( &ivasRtp, hIvasDec, auPtr, &auSize, &rtpTimeStamp, &rtpSequenceNumber, &nextPacketRcvTime_ms, &qBit ); + error = 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 @@ -3700,7 +3916,7 @@ static ivas_error decodeVoIP( { auPtr = au; /* might have been set to RTP packet in prev call */ #ifdef IVAS_RTPDUMP - error = readNextFrame( &ivasRtp, hIvasDec, au, &auSize, &rtpTimeStamp, &rtpSequenceNumber, &nextPacketRcvTime_ms, &qBit ); + error = readNextFrame( &ivasRtp, au, &auSize, &rtpTimeStamp, &rtpSequenceNumber, &nextPacketRcvTime_ms, &qBit ); /* IVAS RTP payload format has timescale 16000, JBM uses 1000 internally */ rtpTimeStamp = rtpTimeStamp / 16; @@ -3745,6 +3961,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 = 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 diff --git a/apps/encoder.c b/apps/encoder.c index 14db97bc1a..00c7cb519a 100644 --- a/apps/encoder.c +++ b/apps/encoder.c @@ -225,7 +225,7 @@ int main( IVAS_RTP_PACK_HANDLE hPack = NULL; uint8_t au[IVAS_MAX_BITS_PER_FRAME / 8]; - uint8_t packet[IVAS_NOMINAL_RTP_BYTES_PER_FRAME * IVAS_MAX_FRAMES_PER_RTP_PACKET]; + 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, packetLength = 0; diff --git a/lib_util/ivas_rtp_api.h b/lib_util/ivas_rtp_api.h index 597258f9f1..5e269eaa43 100644 --- a/lib_util/ivas_rtp_api.h +++ b/lib_util/ivas_rtp_api.h @@ -82,7 +82,9 @@ extern "C" * 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 IVAS_NOMINAL_RTP_BYTES_PER_FRAME ( ( IVAS_MAX_BITS_PER_FRAME + ( IVAS_MAX_BITS_PER_FRAME / 5 ) ) / 8 ) +#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 */ @@ -375,8 +377,8 @@ extern "C" /* Generate a rtp payload using available pushed frames * - * Available remote requests, pi data and frames will be packed into a rtp payload. If no - * frame is pushed before call to this api, NO_DATA_FRAME will be generated + * 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( @@ -408,7 +410,7 @@ extern "C" /* Initial configuration for rtp unpacker */ typedef struct { - uint32_t jitterMarginMs; /* expected max jitter duration in milliseconds */ + 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 @@ -432,6 +434,10 @@ extern "C" * 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 else a race condition + * can arise if new packet is pushed while frames/pidata are still being pulled out. + * * Example usage : - * ================== * err = IVAS_RTP_UNPACK_PushPayload(hIvasUnpack, payload, rtpTs, seqNum, @@ -489,6 +495,10 @@ extern "C" * 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 else a race condition + * can arise if new packet is pushed while frames/pidata are still being pulled out. + * * Example usage : - * ================== * err = IVAS_RTP_UNPACK_PushPacket(hIvasUnpack, packet, &nFrames, &nPiData, &reqBitmap); diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index 367aea06c8..779c627274 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -489,6 +489,7 @@ ivas_error IVAS_RTP_PACK_Open( { ivas_error error; IVAS_RTP_PACK_HANDLE hPack; + uint32_t numFramesPerPacket = ( config->maxFramesPerPacket == 0 ) ? 1 : config->maxFramesPerPacket; if ( phPack == NULL ) { @@ -501,7 +502,7 @@ ivas_error IVAS_RTP_PACK_Open( return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Cannot allocate memory for IVAS rtppack handle" ); } - error = BPOOL_Create( &hPack->packNodePool, sizeof( FRAME_NODE ), IVAS_MAX_FRAMES_IN_QUEUE ); + error = BPOOL_Create( &hPack->packNodePool, sizeof( FRAME_NODE ), numFramesPerPacket * 4 ); ERR_CHECK_RETURN( error ); error = QUEUE_Create( &hPack->frameQ ); @@ -1192,9 +1193,7 @@ ivas_error IVAS_RTP_UNPACK_Open( return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Cannot allocate memory for IVAS rtp unpack handle" ); } - /* maxNumberOfFrames = Ping Pong RTP Packet with max frames per packet + any jitter adjustment frames */ - hUnpack->maxNumberOfFrames += IVAS_MAX_FRAMES_PER_RTP_PACKET * 2; - hUnpack->maxNumberOfFrames += ( ( config->jitterMarginMs * IVAS_NUM_FRAMES_PER_SEC + 1000 - 1 ) / 1000 ); /* Jitter in Packets + Ping/Pong */ + hUnpack->maxNumberOfFrames = ( config->maxFramesPerPacket < IVAS_MAX_FRAMES_PER_RTP_PACKET ) ? 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 ); @@ -1544,7 +1543,16 @@ static ivas_error parsePIData( IVAS_RTP_UNPACK_HANDLE hUnpack, uint32_t rtpTimes } } while ( byte == 255 ); - if ( nBytes + piSize <= payload->length ) + if ( piDataType == IVAS_PI_NO_DATA ) + { + if ( piSize > 0 ) + { + return IVAS_ERROR( IVAS_ERR_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 ); @@ -1614,6 +1622,13 @@ ivas_error IVAS_RTP_UNPACK_PushPayload( *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 */ diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index 8e918d76a2..b88bb0e661 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -40,14 +40,14 @@ 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 uint32_t writeInt16( uint8_t *buffer, uint32_t idx, int16_t val ) +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 uint16_t readInt16( const uint8_t *buffer ) +static __inline uint16_t readInt16( const uint8_t *buffer ) { return ( (uint16_t) buffer[0] << 8 ) | ( (uint16_t) buffer[1] ); } @@ -295,7 +295,7 @@ static ivas_error unpackAcousticEnvironment( const uint8_t *buffer, uint32_t num dWord <<= 8; dWord |= buffer[n]; } - dWord <<= ( 8 - numDataBytes ); + dWord <<= ( 8 - numDataBytes ) * 8; aeEnv->aeid = (uint8_t) ( ( dWord >> 57 ) & MASK_AEID ); aeEnv->rt60[IVAS_PI_AE_LOW] = mapRT60[( dWord >> 52 ) & MASK_RT60]; @@ -424,6 +424,119 @@ static ivas_error unpackDynamicSuppression( const uint8_t *buffer, uint32_t numD 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_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 / 327.68f ) ); + nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( listener->position.y / 327.68f ) ); + nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( listener->position.z / 327.68f ) ); + *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_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 = ( (int16_t) readInt16( &buffer[0] ) ) / 100.0f; + listener->position.y = ( (int16_t) readInt16( &buffer[2] ) ) / 100.0f; + listener->position.z = ( (int16_t) readInt16( &buffer[4] ) ) / 100.0f; + 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_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space to pack Diegetic Type PI data" ); + } + + for ( n = 0; n < 5; 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_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]; + for ( n = 0; n < 5; 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] = { @@ -434,7 +547,7 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { #ifdef RTP_S4_251135_CR26253_0016_REV1 packAudioDescription, /* AUDIO_DESCRIPTION */ #else - packUnsupportedData, /* AUDIO_DESCRIPTION */ + packUnsupportedData, /* AUDIO_DESCRIPTION */ #endif /* RTP_S4_251135_CR26253_0016_REV1 */ packUnsupportedData, /* ISM_NUM */ packUnsupportedData, /* ISM_ID */ @@ -443,24 +556,26 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { 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 */ -#else - packUnsupportedData, /* PLAYBACK_DEVICE_ORIENTATION */ - packUnsupportedData, /* HEAD_ORIENTATION */ -#endif - packUnsupportedData, /* LISTENER_POSITION */ -#ifdef RTP_S4_251135_CR26253_0016_REV1 + packOrientation, /* PLAYBACK_DEVICE_ORIENTATION */ + packOrientation, /* HEAD_ORIENTATION */ + packListenerPosition, /* LISTENER_POSITION */ packDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION */ packAudioDescription, /* AUDIO_FOCUS_DIRECTION */ #else - packUnsupportedData, /* DYNAMIC_AUDIO_SUPPRESSION */ - packUnsupportedData, /* AUDIO_FOCUS_DIRECTION */ + 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 */ @@ -468,7 +583,7 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { #ifdef RTP_S4_251135_CR26253_0016_REV1 packOrientation, /* R_ISM_ORIENTATION */ #else - packUnsupportedData, /* R_ISM_ORIENTATION */ + packUnsupportedData, /* R_ISM_ORIENTATION */ #endif packUnsupportedData, /* R_ISM_POSITION */ packUnsupportedData, /* R_ISM_DIRECTION */ @@ -496,22 +611,24 @@ static const UNPACK_PI_FN unpackPiDataFuntions[IVAS_PI_MAX_ID] = { 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 */ + 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 */ -#endif unpackUnsupportedData, /* LISTENER_POSITION */ -#ifdef RTP_S4_251135_CR26253_0016_REV1 - unpackDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION */ - unpackOrientation, /* AUDIO_FOCUS_DIRECTION */ -#else unpackUnsupportedData, /* DYNAMIC_AUDIO_SUPPRESSION */ unpackUnsupportedData, /* AUDIO_FOCUS_DIRECTION */ #endif diff --git a/lib_util/ivas_rtp_pi_data.h b/lib_util/ivas_rtp_pi_data.h index 5780f32d8f..ba82914df1 100644 --- a/lib_util/ivas_rtp_pi_data.h +++ b/lib_util/ivas_rtp_pi_data.h @@ -296,9 +296,9 @@ extern "C" /* Listener position */ typedef struct { - size_t size; /* sizeof(IVAS_PIDATA_LISTENER_POSITION) */ - uint32_t piDataType; /* IVAS_PI_LISTENER_POSITION */ - IVAS_COORDINATE position[IVAS_PI_MAX_OBJECTS]; /* Position of audio objects in ISM(s) */ + 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; diff --git a/tests/requirements.txt b/tests/requirements.txt index 2eb090f4fb..a8d8f7b064 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 index ecb3924866..f6923c0077 100644 --- a/tests/rtp/ivasrtp.py +++ b/tests/rtp/ivasrtp.py @@ -37,10 +37,24 @@ To configure test modules. import struct from enum import Enum from dataclasses import dataclass, field, asdict -from bitstring import ConstBitStream, ReadError +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" @@ -50,14 +64,12 @@ class CODECS(str, Enum): class SRCODEC(str, Enum): LCLD = "lcld" LC3PLUS = "lc3+" - NA = "NONE" class BANDWIDTH(str, Enum): NB = "narrowband" WB = "wideband" SWB = "super wideband" FB = "fullband" - NREQ = "NO_REQ" class REQUESTS(str, Enum): CODEC = "codec", @@ -68,6 +80,134 @@ class REQUESTS(str, Enum): 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 @@ -82,31 +222,54 @@ class RTPHDR: extensionType: int = 0 extensionLength: int = 0 csrcList: list = field(default_factory=list) - extensionBytes: list = field(default_factory=list) - - def __init__(self, bitstrm: ConstBitStream): - self.version = bitstrm.read(2).uint - self.padding = bitstrm.read(1).bool - self.extension = bitstrm.read(1).bool - self.csrcCount = bitstrm.read(4).int - self.marker = bitstrm.read(1).bool - self.payloadType = bitstrm.read(7).int - self.sequenceNum = bitstrm.read(16).uintbe - self.timestamp = bitstrm.read(32).uintbe - self.ssrc = bitstrm.read(32).uintbe - self.csrcList = [ bitstrm.read(32).uintbe for _ in range(self.csrcCount) ] + 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: - self.extensionType = bitstrm.read(16).uintbe - self.extensionLength = bitstrm.read(16).uintbe - self.extensionBytes = [ bitstrm.read(32).uintbe for _ in range(self.extensionLength) ] - else: - self.extensionType = 0 - self.extensionLength = 0 - self.extensionBytes = list() + 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: - enabled: bool = False diegetic: bool = False yaw: bool = False pitch: bool = False @@ -124,7 +287,7 @@ class CMR: class SRINFO: bitrate: int = 0 diegetic: bool = False - transportCodec: SRCODEC = SRCODEC.NA + transportCodec: SRCODEC = SRCODEC.LCLD @dataclass class FRAME: @@ -136,6 +299,7 @@ class FRAME: timestamp: int = 0 au: bytes = field(default_factory=bytes) +#PI DATA STRUCTURES @dataclass class ORIENTATION: w: float = 0.0 @@ -143,15 +307,73 @@ class ORIENTATION: 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 @@ -160,74 +382,225 @@ cmrLookup = [ 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=BANDWIDTH.NREQ, codec=CODECS.IVAS, startIdx=0, endIdx=14, bitrates = ivasBitrates), #111 = IVAS + CMR(bandwidth=NO_REQ, codec=CODECS.IVAS, startIdx=0, endIdx=14, bitrates = ivasBitrates), #111 = IVAS ] -codedFormats = ["Stereo", "SBA", "MASA", "ISM", "MC", "OMASA", "OSBA", "NO_REQ"] -codedSubFormats = [ - "FOA planar", "HOA2 planar", "HOA3 planar", "FOA", "HOA2", "HOA3", "MASA1", "MASA2", "ISM1", - "ISM2", "ISM3", "ISM4", "ISM1 extended metadata", "ISM2 extended metadata", - "ISM3 extended metadata", "ISM4 extended metadata", "MC 5.1", "MC 7.1", "MC 5.1.2", "MC 5.1.4", - "MC 7.1.4", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", - "Reserved", "Reserved", "Reserved", "Reserved", "OMASA ISM1 1TC", "OMASA ISM2 1TC", - "OMASA ISM3 1TC", "OMASA ISM4 1TC", "OMASA ISM1 2TC", "OMASA ISM2 2TC", "OMASA ISM3 2TC", - "OMASA ISM4 2TC", "OSBA ISM1 FOA planar", "OSBA ISM2 FOA planar", "OSBA ISM3 FOA planar", - "OSBA ISM4 FOA planar", "OSBA ISM1 FOA", "OSBA ISM2 FOA", "OSBA ISM3 FOA", "OSBA ISM4 FOA", - "OSBA ISM1 HOA2 planar", "OSBA ISM2 HOA2 planar", "OSBA ISM3 HOA2 planar", "OSBA ISM4 HOA2 planar", - "OSBA ISM1 HOA2", "OSBA ISM2 HOA2", "OSBA ISM3 HOA2", "OSBA ISM4 HOA2", "OSBA ISM1 HOA3 planar", - "OSBA ISM2 HOA3 planar", "OSBA ISM3 HOA3 planar", "OSBA ISM4 HOA3 planar", "OSBA ISM1 HOA3", - "OSBA ISM2 HOA3", "OSBA ISM3 HOA3", "OSBA ISM4 HOA3"] -PiTypeNames = [ - "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_PI_DATA" -] +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" - 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 - return ORIENTATION(w, x, y, z) + 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 - unpackUnsupported, # ACOUSTIC_ENVIRONMENT - unpackUnsupported, # AUDIO_DESCRIPTION + unpackAcousticEnv, # ACOUSTIC_ENVIRONMENT + unpackAudioDescription, # AUDIO_DESCRIPTION unpackUnsupported, # ISM_NUM unpackUnsupported, # ISM_ID unpackUnsupported, # ISM_GAIN - unpackUnsupported, # ISM_ORIENTATION - unpackUnsupported, # ISM_POSITION + unpackOrientations,# ISM_ORIENTATION + unpackPositions, # ISM_POSITION unpackUnsupported, # ISM_DISTANCE_ATTENUATION unpackUnsupported, # ISM_DIRECTIVITY - unpackUnsupported, # DIEGETIC_TYPE + unpackDiegetic, # DIEGETIC_TYPE unpackUnsupported, # RESERVED13 unpackUnsupported, # RESERVED14 unpackUnsupported, # RESERVED15 - unpackUnsupported, # PLAYBACK_DEVICE_ORIENTATION - unpackUnsupported, # HEAD_ORIENTATION - unpackUnsupported, # LISTENER_POSITION - unpackUnsupported, # DYNAMIC_AUDIO_SUPPRESSION - unpackUnsupported, # AUDIO_FOCUS_DIRECTION + 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 - unpackUnsupported, # R_ISM_ORIENTATION - unpackUnsupported, # R_ISM_POSITION + unpackOrientation, # R_ISM_ORIENTATION + unpackPosition, # R_ISM_POSITION unpackUnsupported, # R_ISM_DIRECTION unpackUnsupported, # RESERVED27 unpackUnsupported, # RESERVED28 @@ -236,176 +609,498 @@ PIDataUnpacker = [ 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: - lastCodec: CODECS = CODECS.IVAS # Track last frame's codec frameList: list[FRAME] = field(default_factory=list) piDataList: list[PIDATA] = field(default_factory=list) requests: dict [str, any] = field(default_factory=dict) - def __init__(self, bitstrm: ConstBitStream, rtpTimestamp, lastCodec: CODECS): - self.lastCodec = lastCodec - self.frameList = list[FRAME]() - self.piDataList = list[PIDATA]() - self.requests = 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 - try: - piIndicated = False - 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: - self.requests[REQUESTS.CODEC] = cmrLookup[T].codec - self.requests[REQUESTS.BR] = 13200 - self.requests[REQUESTS.CA] = BR - self.requests[REQUESTS.BW] = cmrLookup[T].bandwidth - else: - raise Exception("Unsupported BR bits in CA Mode") - elif T == 7: #IVAS - if BR < 14 : - self.requests[REQUESTS.CODEC] = cmrLookup[T].codec - self.requests[REQUESTS.BR] = cmrLookup[T].bitrates[BR] - self.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: - self.requests[REQUESTS.CODEC] = cmrLookup[T].codec - self.requests[REQUESTS.BR] = cmrLookup[T].bitrates[BR] - self.requests[REQUESTS.CA] = -1 - self.requests[REQUESTS.BW] = cmrLookup[T].bandwidth - else: - raise Exception("Reserved BR idx in {} EByte".format(cmrLookup[T].codec)) + @classmethod + def unpack(cls, bitstrm: ConstBitStream, rtpTimestamp: int, Codec: CODECS): + # Unpack the E-bytes + piIndicated, requests = unpackEBytes(bitstrm) - #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 - self.requests[REQUESTS.BW] = supportedBW[BW] - elif ET == 1 : - S = bitstrm.read(1).bool - FMT = bitstrm.read(3).uint - if not S: - self.requests[REQUESTS.FMT] = codedFormats[FMT] - self.requests[REQUESTS.SUBFMT] = "NO_REQ" - else: - reserved = bitstrm.read(2) - subFMT = bitstrm.read(6).uint - self.requests[REQUESTS.FMT] = "NO_REQ" - self.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 - self.requests[REQUESTS.SRCFG] = SRCONFIG(enabled=True, diegetic=D, yaw=Y, pitch=P, roll=R) - else: - reserved = bitstrm.read(4) - raise Exception("Unsupported subsequent EByte with ET={}".format(ET)) - - # ToC Byte parsing starts with 'F' bit as H bit is already read above - frameTimeStamps = rtpTimestamp - F = True - frmList = list() - while F: - F = bitstrm.read(1).bool - FT = bitstrm.read(2).uint - BR = bitstrm.read(4).uint - - frm = FRAME(timestamp=frameTimeStamps) - 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 self.lastCodec - 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 - frameTimeStamps += 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) - - #Frame AUs here - for (idx, frm) in enumerate(frmList): - auSize = (frm.frmSizeBits + 7) // 8 # Zero padded bytes in amrwb_io mode - frm.au = bitstrm.read(auSize * 8).tobytes() - self.frameList.append(frm) - - # PI Data if Indicated - piTimeStamps = rtpTimestamp - PF = piIndicated - 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) - - self.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)) + # 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): + def __init__(self, numFramesPerPacket = 4, codec: CODECS = CODECS.IVAS): + self.numFramesPerPacket = numFramesPerPacket self.packets = list[IvasPacket]() - self.lastCodec: CODECS = CODECS.IVAS # Track last frame's codec + 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: - packets.append(asdict(packet)) + 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 @@ -423,16 +1118,21 @@ class IvasRtp: def unpackPacket(self, packet) -> IvasPacket : bitStrm = ConstBitStream(packet) - hdr = RTPHDR(bitStrm) - payload = IvasPayload(bitStrm, rtpTimestamp=hdr.timestamp, lastCodec=self.lastCodec) - self.lastCodec = payload.frameList[-1].codec #Last Frame's codec for next frame for NO_DATA case + 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, required=True, help="RTP Dump to unpack") + 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() @@ -440,7 +1140,18 @@ class ArgsParser: if __name__ == "__main__": args = ArgsParser().parse() - rtp = IvasRtp() + 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 index 00b4da5c1b..342e29c8b6 100644 --- a/tests/rtp/test_rtp.py +++ b/tests/rtp/test_rtp.py @@ -38,26 +38,29 @@ import pytest import csv import os import sys +import random from tempfile import TemporaryDirectory from pathlib import Path from ivasrtp import * -from bitstring import ConstBitStream, BitStream +import soundfile as sf +import numpy as np -THIS_PATH = os.path.dirname(os.path.abspath(__file__)) -sys.path.append(os.path.join(os.path.dirname(THIS_PATH), "../../scripts")) +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, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850]) +@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_encoder_frontend: EncoderFrontend, + dut_decoder_frontend: DecoderFrontend ): run_rtp_bitstream_tests ( CODECS.AMRWB, @@ -67,11 +70,12 @@ def test_rtp_bitstream_amrwb ( "MONO", dtx, framesPerPacket, - dut_encoder_frontend + dut_encoder_frontend, + dut_decoder_frontend ) @pytest.mark.parametrize("dtx", [False, True]) -@pytest.mark.parametrize("bitrate", [7200, 8000, 9600, 13200, 16400, 24400, 32000, 48000, 64000, 96000, 128000]) +@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]) @@ -82,7 +86,8 @@ def test_rtp_bitstream_evs ( caMode: str, dtx: bool, framesPerPacket: int, - dut_encoder_frontend: EncoderFrontend + dut_encoder_frontend: EncoderFrontend, + dut_decoder_frontend: DecoderFrontend ): run_rtp_bitstream_tests ( CODECS.EVS, @@ -92,14 +97,14 @@ def test_rtp_bitstream_evs ( "MONO", dtx, framesPerPacket, - dut_encoder_frontend + dut_encoder_frontend, + dut_decoder_frontend ) -@pytest.mark.parametrize("bitrate", [13200, 24400, 80000, 512000]) +@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("bandwidth, format, bitrate", [("WB", "MASA", 13200)]) @pytest.mark.parametrize("framesPerPacket", [8]) def test_rtp_bitstream_ivas_nodtx ( test_info, @@ -107,7 +112,8 @@ def test_rtp_bitstream_ivas_nodtx ( bandwidth: str, format: str, framesPerPacket: int, - dut_encoder_frontend: EncoderFrontend + dut_encoder_frontend: EncoderFrontend, + dut_decoder_frontend: DecoderFrontend ): run_rtp_bitstream_tests ( CODECS.IVAS, @@ -117,20 +123,22 @@ def test_rtp_bitstream_ivas_nodtx ( format, False, framesPerPacket, - dut_encoder_frontend + 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, 8]) +@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_encoder_frontend: EncoderFrontend, + dut_decoder_frontend: DecoderFrontend ): run_rtp_bitstream_tests ( CODECS.IVAS, @@ -140,9 +148,39 @@ def test_rtp_bitstream_ivas_dtx ( format, True, framesPerPacket, - dut_encoder_frontend + 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" @@ -155,6 +193,36 @@ def isEqualOrientation(ref: ORIENTATION, dut: ORIENTATION): 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): @@ -197,19 +265,7 @@ class RTPVALIDATE: self.readers[PiTypeNames[1]] = CSVREADER(piFiles[1]) def setRefG192Bitstream(self, g192File: Path): - self.refPackets = list[bytes]() - self.refBitStrm = None - with open(g192File, "rb") as fd: - self.refBitStrm = ConstBitStream(fd.read()) - while self.refBitStrm.pos < self.refBitStrm.len: - sync = hex(self.refBitStrm.read(16).intle) - nBits = self.refBitStrm.read(16).intle - assert sync == "0x6b21", "G192 syncword not found at start of packet" - writer = BitStream() - for _ in range(nBits): - bit = "0b1" if self.refBitStrm.read(16).uintle == 129 else "0b0" - writer.append(bit) - self.refPackets.append(writer.tobytes()) + self.refPackets = ReadG192Bitstream(g192File) def packet(self, packet: IvasPacket): self.header(packet.hdr) @@ -255,11 +311,11 @@ class RTPVALIDATE: isEqualOrientation(ORIENTATION(w=refData[0], x=refData[1], y=refData[2], z=refData[3]), piData.data) @dataclass class TVARGS: - TVROOT = Path(THIS_PATH).joinpath("../../scripts/testv") + TVROOT = Path(ROOT_DIR).joinpath("scripts/testv") def __init__(self): self.tvDict = dict() - self.sceneFile = Path(THIS_PATH).joinpath("../../scripts/trajectories/azi_plus_2-ele_plus_2-every-25-rows.csv").absolute() - self.deviceFile = Path(THIS_PATH).joinpath("../../scripts/trajectories/headrot-1.5s.csv").absolute() + 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()) @@ -275,7 +331,7 @@ class TVARGS: 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) @@ -287,7 +343,8 @@ def run_rtp_bitstream_tests ( format: str, dtx: bool, framesPerPacket: int, - dut_encoder_frontend: EncoderFrontend + dut_encoder_frontend: EncoderFrontend, + dut_decoder_frontend: DecoderFrontend ): tvArgs = TVARGS() tvArgs.add("MONO", "stv48n.wav") @@ -316,6 +373,10 @@ def run_rtp_bitstream_tests ( 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( @@ -330,6 +391,68 @@ def run_rtp_bitstream_tests ( ) 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: @@ -349,7 +472,7 @@ def run_rtp_bitstream_tests ( add_option_list=extra_args ) - ivasRtp = IvasRtp() - ivasRtp.unpackFile(rtpdumpOut) - for packet in ivasRtp.getPackets(): + unpacker = IvasRtp() + unpacker.unpackFile(rtpdumpOut) + for packet in unpacker.getPackets(): validate.packet(packet) -- GitLab From e3fa8dc55d2ad50fc287aaac0d59287bfc851f85 Mon Sep 17 00:00:00 2001 From: Ripinder Singh Date: Thu, 21 Aug 2025 10:15:00 +1000 Subject: [PATCH 031/100] Code review comments #2 * Split rtp file writing * Update copyrights * Added additional comments * Added preprocessor defined for constants * Fix bug in pidata pack * Remove un-needed defines Signed-off-by: Ripinder Singh --- Workspace_msvc/lib_util.vcxproj | 1 + Workspace_msvc/lib_util.vcxproj.filters | 3 + apps/decoder.c | 145 +++++++++++------------- apps/encoder.c | 73 +++++------- lib_util/ivas_bpool.c | 2 +- lib_util/ivas_bpool.h | 2 +- lib_util/ivas_queue.c | 2 +- lib_util/ivas_queue.h | 2 +- lib_util/ivas_rtp_api.h | 22 ++-- lib_util/ivas_rtp_file.c | 138 ++++++++++++++++++++++ lib_util/ivas_rtp_file.h | 65 +++++++++++ lib_util/ivas_rtp_internal.h | 19 ++-- lib_util/ivas_rtp_payload.c | 35 +++--- lib_util/ivas_rtp_payload.h | 2 +- lib_util/ivas_rtp_pi_data.c | 41 ++++--- lib_util/ivas_rtp_pi_data.h | 2 +- lib_util/ivas_rtp_rom.c | 2 +- 17 files changed, 377 insertions(+), 179 deletions(-) create mode 100644 lib_util/ivas_rtp_file.c create mode 100644 lib_util/ivas_rtp_file.h diff --git a/Workspace_msvc/lib_util.vcxproj b/Workspace_msvc/lib_util.vcxproj index 84ff59beae..94f22c71ca 100644 --- a/Workspace_msvc/lib_util.vcxproj +++ b/Workspace_msvc/lib_util.vcxproj @@ -115,6 +115,7 @@ + diff --git a/Workspace_msvc/lib_util.vcxproj.filters b/Workspace_msvc/lib_util.vcxproj.filters index 776a38e3d3..f81b34b6da 100644 --- a/Workspace_msvc/lib_util.vcxproj.filters +++ b/Workspace_msvc/lib_util.vcxproj.filters @@ -97,6 +97,9 @@ util_c + + util_c + diff --git a/apps/decoder.c b/apps/decoder.c index 4f2307b3dc..8cd444ec9a 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -41,6 +41,7 @@ #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" @@ -172,7 +173,6 @@ typedef struct } IVAS_DEC_HRTF_BINARY_WRAPPER; #ifdef IVAS_RTPDUMP -#define JITTER_DURATION_MS ( 60 ) #ifdef DEBUGGING #define DEBUG_PRINT fprintf #else @@ -190,7 +190,7 @@ 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]; - FILE *f_rtpstream; + IVAS_RTP_FILE_HANDLE hReader; FILE *f_piDataOut; IVAS_RTP_CODEC codecId; uint32_t nReadPiData; @@ -223,7 +223,7 @@ static const char *PiDataNames[IVAS_PI_MAX_ID] = { }; -static void LogPiData( FILE *f_piDataOut, PIDATA_TS *piData, uint32_t nPiDataPresent ) +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 ) @@ -374,70 +374,71 @@ static void LogPiData( FILE *f_piDataOut, PIDATA_TS *piData, uint32_t nPiDataPre static void IVAS_RTP_Term( IVAS_RTP *rtp ) { - if ( rtp->f_rtpstream ) + if ( NULL != rtp ) { - fclose( rtp->f_rtpstream ); - rtp->f_rtpstream = NULL; - } - if ( rtp->f_piDataOut ) - { - fprintf( rtp->f_piDataOut, "\n}\n" ); - fclose( rtp->f_piDataOut ); - rtp->f_piDataOut = NULL; + 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 ); } - IVAS_RTP_UNPACK_Close( &rtp->hUnpack ); } -static ivas_error IVAS_RTP_Init( IVAS_RTP *rtp, FILE *f_rtpstream, FILE *f_piDataOut ) +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; + } - rtp->f_rtpstream = f_rtpstream; - rtp->f_piDataOut = f_piDataOut; - rtp->rtpPacket.buffer = rtp->packet; - rtp->rtpPacket.capacity = sizeof( rtp->packet ); + 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 readNextFrame( IVAS_RTP *rtp, uint8_t *au, int16_t *auSizeBits, uint32_t *rtpTimeStamp, uint16_t *rtpSequenceNumber, uint32_t *nextPacketRcvTime_ms, bool *qBit ) +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 = { 0, 0, NULL }; - size_t packetLen = 0u; + 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 ) { - size_t nread = fread( &packetLen, sizeof( uint32_t ), 1, rtp->f_rtpstream ); /* Read Packet Length */ - if ( nread == 0 ) + rtp->rtpPacket.length = 0; + if ( ( error = IvasRtpFile_Read( rtp->hReader, rtp->rtpPacket.buffer, &rtp->rtpPacket.length, rtp->rtpPacket.capacity ) ) != IVAS_ERR_OK ) { - return IVAS_ERR_END_OF_FILE; - } - if ( packetLen > sizeof( rtp->packet ) ) - { - fprintf( stderr, "RTP packet > buffer capacity %lu bytes\n", sizeof( rtp->packet ) ); - return IVAS_ERR_INVALID_OUTPUT_BUFFER_SIZE; - } - - nread = fread( rtp->packet, sizeof( uint8_t ), packetLen, rtp->f_rtpstream ); /* Read Packet */ - if ( nread < packetLen ) - { - return IVAS_ERR_END_OF_FILE; + return error; } - rtp->rtpPacket.buffer = rtp->packet; - rtp->rtpPacket.length = packetLen; - if ( ( error = IVAS_RTP_UNPACK_PushPacket( rtp->hUnpack, &rtp->rtpPacket, &rtp->numFramesInPacket, &rtp->numPiDataInPacket, &rtp->remoteRequestBitmap ) ) != IVAS_ERR_OK ) @@ -461,7 +462,7 @@ static ivas_error readNextFrame( IVAS_RTP *rtp, uint8_t *au, int16_t *auSizeBits rtp->nReadPiData++; rtp->numPiDataInPacket--; } - LogPiData( rtp->f_piDataOut, rtp->piData, rtp->nReadPiData ); + IVAS_RTP_LogPiData( rtp->f_piDataOut, rtp->piData, rtp->nReadPiData ); } #ifdef RTP_S4_251135_CR26253_0016_REV1 @@ -505,7 +506,7 @@ static ivas_error readNextFrame( IVAS_RTP *rtp, uint8_t *au, int16_t *auSizeBits return IVAS_ERR_OK; } -static ivas_error applyPiData( IVAS_RTP *rtp, IVAS_DEC_HANDLE hIvasDec, uint32_t rtpTimeStamp ) +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 && @@ -633,7 +634,7 @@ static int restartDecoder( if ( arg->voipMode ) { - if ( ( error = IVAS_DEC_EnableVoIP( hIvasDec, JITTER_DURATION_MS, arg->inputFormat ) ) != IVAS_ERR_OK ) + 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; @@ -975,11 +976,7 @@ int main( if ( arg.voipMode ) { -#ifdef IVAS_RTPDUMP - if ( ( error = IVAS_DEC_EnableVoIP( hIvasDec, JITTER_DURATION_MS, arg.inputFormat ) ) != IVAS_ERR_OK ) -#else if ( ( error = IVAS_DEC_EnableVoIP( hIvasDec, 60, arg.inputFormat ) ) != IVAS_ERR_OK ) -#endif { fprintf( stderr, "\nCould not enable VOIP: %s\n\n", IVAS_DEC_GetErrorMessage( error ) ); goto cleanup; @@ -3406,18 +3403,18 @@ static ivas_error printBitstreamInfoVoip( { bool previewFailed = true; ivas_error error = IVAS_ERR_OK; - FILE *f_rtpstream = NULL; #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; @@ -3430,6 +3427,13 @@ static ivas_error printBitstreamInfoVoip( { 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 ) @@ -3438,13 +3442,6 @@ static ivas_error printBitstreamInfoVoip( goto cleanup; } -#ifdef IVAS_RTPDUMP - if ( ( error = IVAS_RTP_Init( &ivasRtp, f_rtpstream ) ) != IVAS_ERR_OK ) - { - fprintf( stderr, "error in IVAS_RTP_Init(): %d\n", error ); - goto cleanup; - } -#else rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_open( &rtpdumpDepacker, f_rtpstream, arg.inputFormat == IVAS_DEC_INPUT_FORMAT_RTPDUMP_HF ); if ( rtpdumpDepackerError != EVS_RTPDUMP_DEPACKER_NO_ERROR ) { @@ -3481,7 +3478,11 @@ static ivas_error printBitstreamInfoVoip( /* 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; @@ -3574,13 +3575,12 @@ static ivas_error decodeVoIP( int16_t delayNumSamples = -1; int32_t delayTimeScale = -1; int16_t i; - FILE *f_rtpstream = NULL; #ifdef IVAS_RTPDUMP IVAS_RTP ivasRtp = { 0 }; IVAS_DEC_HANDLE hIvasDec = *phIvasDec; - FILE *f_piDataOut = NULL; int32_t initialTsOffsetSystemAndRTP = 0; #else + FILE *f_rtpstream = NULL; EVS_RTPDUMP_DEPACKER rtpdumpDepacker; EVS_RTPDUMP_DEPACKER_ERROR rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_NO_ERROR; #endif @@ -3634,34 +3634,19 @@ static ivas_error decodeVoIP( case IVAS_DEC_INPUT_FORMAT_RTPDUMP: case IVAS_DEC_INPUT_FORMAT_RTPDUMP_HF: #ifdef IVAS_RTPDUMP - f_rtpstream = fopen( arg.inputBitstreamFilename, "rb" ); + 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" ); -#endif - if ( f_rtpstream == NULL ) { fprintf( stderr, "could not open: %s\n", arg.inputBitstreamFilename ); goto cleanup; } -#ifdef IVAS_RTPDUMP - if ( arg.piOutputFilename != NULL ) - { - f_piDataOut = fopen( arg.piOutputFilename, "w" ); - if ( f_piDataOut == NULL ) - { - fprintf( stderr, "could not open: %s\n", arg.piOutputFilename ); - goto cleanup; - } - fprintf( f_piDataOut, "{\n" ); - } - if ( ( error = IVAS_RTP_Init( &ivasRtp, f_rtpstream, f_piDataOut ) ) != IVAS_ERR_OK ) - { - fprintf( stderr, "error in IVAS_RTP_Init(): %d\n", error ); - goto cleanup; - } -#else rtpdumpDepackerError = EVS_RTPDUMP_DEPACKER_open( &rtpdumpDepacker, f_rtpstream, arg.inputFormat == IVAS_DEC_INPUT_FORMAT_RTPDUMP_HF ); if ( rtpdumpDepackerError != EVS_RTPDUMP_DEPACKER_NO_ERROR ) { @@ -3711,7 +3696,7 @@ static ivas_error decodeVoIP( { auPtr = au; /* might have been set to RTP packet in prev call */ #ifdef IVAS_RTPDUMP - error = readNextFrame( &ivasRtp, auPtr, &auSize, &rtpTimeStamp, &rtpSequenceNumber, &nextPacketRcvTime_ms, &qBit ); + 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 ); @@ -3916,7 +3901,7 @@ static ivas_error decodeVoIP( { auPtr = au; /* might have been set to RTP packet in prev call */ #ifdef IVAS_RTPDUMP - error = readNextFrame( &ivasRtp, au, &auSize, &rtpTimeStamp, &rtpSequenceNumber, &nextPacketRcvTime_ms, &qBit ); + 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; @@ -3966,7 +3951,7 @@ static ivas_error decodeVoIP( { /* Rudimentry Time Mapping to map system time to rtp timestamp */ uint32_t piTs = systemTime_ms * 16 + initialTsOffsetSystemAndRTP; - if ( ( error = applyPiData( &ivasRtp, hIvasDec, piTs ) ) != IVAS_ERR_OK ) + 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; diff --git a/apps/encoder.c b/apps/encoder.c index 00c7cb519a..9fb3b60013 100644 --- a/apps/encoder.c +++ b/apps/encoder.c @@ -42,6 +42,7 @@ #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" @@ -221,14 +222,14 @@ int main( #endif #ifdef IVAS_RTPDUMP - FILE *f_rtpstream = NULL; + 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)]; + 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, packetLength = 0; + uint32_t numFramesInPayload = 0; packedFrame.capacity = sizeof( au ); packedFrame.buffer = au; @@ -639,8 +640,8 @@ int main( packCfg.maxFramesPerPacket = arg.numFramesPerPacket; /* Open the output file for RTPDump writing */ - f_rtpstream = fopen( arg.outputBitstreamFilename, "wb" ); - if ( f_rtpstream == NULL ) + error = IvasRtpFile_Open ( arg.outputBitstreamFilename, true, &hWriter ); + if ( error != IVAS_ERR_OK ) { fprintf( stderr, "could not open: %s\n", arg.outputBitstreamFilename ); goto cleanup; @@ -861,40 +862,17 @@ int main( } /* *** Encode one frame *** */ - 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; - } - #ifdef IVAS_RTPDUMP if ( hPack ) { - uint32_t n = numBits / 8, x = 0; - uint16_t *bitstrm = bitStream; - - /* Pack Encoded Stream */ - packedFrame.length = 0; - rtpPacket.length = 0; - while ( n ) + if ( ( error = IVAS_ENC_EncodeFrameToCompact( hIvasEnc, pcmBuf, pcmBufSize, packedFrame.buffer, &numBits ) ) != IVAS_ERR_OK ) { - x = ( ( bitstrm[0] << 7 ) | ( bitstrm[1] << 6 ) | ( bitstrm[2] << 5 ) | ( bitstrm[3] << 4 ) | - ( bitstrm[4] << 3 ) | ( bitstrm[5] << 2 ) | ( bitstrm[6] << 1 ) | ( bitstrm[7] << 0 ) ); - packedFrame.buffer[packedFrame.length++] = (uint8_t) x; - bitstrm += 8; - n--; + fprintf( stderr, "\nencodeFrame failed: %s\n\n", IVAS_ENC_GetErrorMessage( error ) ); + goto cleanup; } - /* AMWWB-IO has non octet sized bitstream */ - if ( numBits & 0x7 ) - { - x = 0; - for ( n = 0; n < (uint32_t) ( numBits & 0x7 ); n++ ) - { - x |= (uint8_t) ( bitstrm[n] << ( 7 - n ) ); - } - packedFrame.buffer[packedFrame.length++] = (uint8_t) x; - } + packedFrame.length = ( numBits + 7 ) / 8; + rtpPacket.length = 0; /* Push Encoded Stream to */ if ( ( error = IVAS_RTP_PACK_PushFrame( hPack, @@ -961,15 +939,22 @@ int main( goto cleanup; } - /* File Format = Packet Length (uint32_t) + Packet bytes */ - packetLength = (uint32_t) rtpPacket.length; - fwrite( &packetLength, sizeof( packetLength ), 1, f_rtpstream ); - fwrite( rtpPacket.buffer, sizeof( uint8_t ), packetLength, f_rtpstream ); + 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 { #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 ) { @@ -1068,10 +1053,11 @@ cleanup: goto cleanup; } - /* File Format = Packet Length (uint32_t) + Packet bytes */ - packetLength = (uint32_t) rtpPacket.length; - fwrite( &packetLength, sizeof( packetLength ), 1, f_rtpstream ); - fwrite( rtpPacket.buffer, sizeof( uint8_t ), packetLength, f_rtpstream ); + 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 ); @@ -1087,10 +1073,7 @@ cleanup: RotationFileReader_close( &deviceOrientationFileReader ); } - if ( f_rtpstream ) - { - fclose( f_rtpstream ); - } + IvasRtpFile_Close( &hWriter ); #endif IVAS_ENC_Close( &hIvasEnc ); diff --git a/lib_util/ivas_bpool.c b/lib_util/ivas_bpool.c index 23754a0c11..2bf707cea6 100644 --- a/lib_util/ivas_bpool.c +++ b/lib_util/ivas_bpool.c @@ -1,6 +1,6 @@ /****************************************************************************************************** - (C) 2022-2024 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + (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 diff --git a/lib_util/ivas_bpool.h b/lib_util/ivas_bpool.h index d929ae7415..ae8f114127 100644 --- a/lib_util/ivas_bpool.h +++ b/lib_util/ivas_bpool.h @@ -1,6 +1,6 @@ /****************************************************************************************************** - (C) 2022-2024 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + (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 diff --git a/lib_util/ivas_queue.c b/lib_util/ivas_queue.c index b72fcee9e4..c5806e0307 100644 --- a/lib_util/ivas_queue.c +++ b/lib_util/ivas_queue.c @@ -1,6 +1,6 @@ /****************************************************************************************************** - (C) 2022-2024 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + (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 diff --git a/lib_util/ivas_queue.h b/lib_util/ivas_queue.h index 8a5d4d1017..9f2a9a856c 100644 --- a/lib_util/ivas_queue.h +++ b/lib_util/ivas_queue.h @@ -1,6 +1,6 @@ /****************************************************************************************************** - (C) 2022-2024 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + (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 diff --git a/lib_util/ivas_rtp_api.h b/lib_util/ivas_rtp_api.h index 5e269eaa43..29ed397beb 100644 --- a/lib_util/ivas_rtp_api.h +++ b/lib_util/ivas_rtp_api.h @@ -1,6 +1,6 @@ /****************************************************************************************************** - (C) 2022-2024 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + (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 @@ -296,7 +296,7 @@ extern "C" /* Initial configuration for rtp packer * - maxFramesPerPacket is used to define if more than one frame should be packed - * in the same rtp packet. + * in the same rtp packet. If zero, will use IVAS_MAX_FRAMES_PER_RTP_PACKET. */ typedef struct { @@ -407,7 +407,11 @@ extern "C" /* 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 */ + /* 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 */ @@ -435,8 +439,10 @@ extern "C" * 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 else a race condition - * can arise if new packet is pushed while frames/pidata are still being pulled out. + * 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 : - * ================== @@ -496,8 +502,10 @@ extern "C" * 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 else a race condition - * can arise if new packet is pushed while frames/pidata are still being pulled out. + * 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 : - * ================== diff --git a/lib_util/ivas_rtp_file.c b/lib_util/ivas_rtp_file.c new file mode 100644 index 0000000000..0112fdfb54 --- /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 0000000000..47d545208a --- /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 "ivas_error.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 index 0acfcd2161..f3569ca031 100644 --- a/lib_util/ivas_rtp_internal.h +++ b/lib_util/ivas_rtp_internal.h @@ -1,6 +1,6 @@ /****************************************************************************************************** - (C) 2022-2024 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + (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 @@ -64,6 +64,9 @@ enum MASK_BITS #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]; @@ -74,14 +77,14 @@ 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_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 */ + 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 */ diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index 779c627274..f716f31eee 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -1,6 +1,6 @@ /****************************************************************************************************** - (C) 2022-2024 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + (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 @@ -45,15 +45,9 @@ #include "ivas_error_utils.h" #include "mutex.h" -#define IVAS_MAX_FRAMES_IN_QUEUE ( 32 ) #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 */ -/* Heuristically defined max values for no of frames per packet and no of pi data per packet */ -#define MAX_SUPPORTED_PI_DATA_SIZE ( 32 ) /* Currently no PI data exceeds 32 bytes */ -#define MAX_SUPPORTED_IVAS_FRAMES_PER_PACKET ( 25u ) -#define MAX_SUPPORTED_PI_FRAMES_PER_PACKET ( 150u ) - /* Generic RTP Header + Header Extension data structure */ typedef struct { @@ -489,13 +483,18 @@ ivas_error IVAS_RTP_PACK_Open( { ivas_error error; IVAS_RTP_PACK_HANDLE hPack; - uint32_t numFramesPerPacket = ( config->maxFramesPerPacket == 0 ) ? 1 : config->maxFramesPerPacket; + 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 ) { @@ -1193,7 +1192,12 @@ ivas_error IVAS_RTP_UNPACK_Open( return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Cannot allocate memory for IVAS rtp unpack handle" ); } - hUnpack->maxNumberOfFrames = ( config->maxFramesPerPacket < IVAS_MAX_FRAMES_PER_RTP_PACKET ) ? IVAS_MAX_FRAMES_PER_RTP_PACKET : config->maxFramesPerPacket; + 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 ); @@ -1390,7 +1394,7 @@ static uint32_t parseSubsequentEByte( const IVAS_DATA_BUFFER *payload, uint32_t return nBytes; } -static ivas_error parseToCByte( const IVAS_DATA_BUFFER *payload, uint32_t *numBytes, uint32_t *numFrames, TOC_INFO *toc ) +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; @@ -1411,13 +1415,14 @@ static ivas_error parseToCByte( const IVAS_DATA_BUFFER *payload, uint32_t *numBy nBytes++; /* Consume this e-byte */ - *numFrames += 1; - memset( toc, 0, sizeof( *toc ) ); - if ( *numFrames == MAX_SUPPORTED_IVAS_FRAMES_PER_PACKET ) + 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; @@ -1598,7 +1603,7 @@ ivas_error IVAS_RTP_UNPACK_PushPayload( ivas_error error = IVAS_ERR_OK; uint32_t nBytes = 0, numFrames = 0, numPiData = 0, n; bool piDataIndicated = false; - TOC_INFO toc[MAX_SUPPORTED_IVAS_FRAMES_PER_PACKET]; + TOC_INFO toc[IVAS_MAX_FRAMES_PER_RTP_PACKET]; IVAS_RTP_REQUEST_VALUE oldRequests[IVAS_REQUEST_MAX]; @@ -1638,7 +1643,7 @@ ivas_error IVAS_RTP_UNPACK_PushPayload( nBytes = parseSubsequentEByte( payload, nBytes, hUnpack->requests, &piDataIndicated ); /* Unpack the ToC Bytes => Extract number of frames in packet */ - error = parseToCByte( payload, &nBytes, &numFrames, toc ); + error = parseToCByte( payload, &nBytes, &numFrames, toc, sizeof( toc ) / sizeof( toc[0] ) ); ERR_CHECK_RETURN( error ); /* Read frame bits */ diff --git a/lib_util/ivas_rtp_payload.h b/lib_util/ivas_rtp_payload.h index 8529de51b7..92cb0e8e71 100644 --- a/lib_util/ivas_rtp_payload.h +++ b/lib_util/ivas_rtp_payload.h @@ -1,6 +1,6 @@ /****************************************************************************************************** - (C) 2022-2024 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + (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 diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index b88bb0e661..d3e8774281 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -1,6 +1,6 @@ /****************************************************************************************************** - (C) 2022-2024 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + (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 @@ -47,9 +47,9 @@ static __inline uint32_t writeInt16( uint8_t *buffer, uint32_t idx, int16_t val return idx; } -static __inline uint16_t readInt16( const uint8_t *buffer ) +static __inline int16_t readInt16( const uint8_t *buffer ) { - return ( (uint16_t) buffer[0] << 8 ) | ( (uint16_t) buffer[1] ); + return (int16_t) ( (uint16_t) buffer[0] << 8 ) | ( (uint16_t) buffer[1] ); } /*-----------------------------------------------------------------------* @@ -166,10 +166,10 @@ static ivas_error unpackOrientation( const uint8_t *buffer, uint32_t numDataByte } piData->size = sizeof( IVAS_PIDATA_ORIENTATION ); - orientation->orientation.w = ( (int16_t) readInt16( &buffer[0] ) ) / 32768.0f; - orientation->orientation.x = ( (int16_t) readInt16( &buffer[2] ) ) / 32768.0f; - orientation->orientation.y = ( (int16_t) readInt16( &buffer[4] ) ) / 32768.0f; - orientation->orientation.z = ( (int16_t) readInt16( &buffer[6] ) ) / 32768.0f; + 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; } @@ -330,6 +330,11 @@ static ivas_error packAudioDescription( const IVAS_PIDATA_GENERIC *piData, uint8 *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 ) { @@ -358,7 +363,7 @@ static ivas_error unpackAudioDescription( const uint8_t *buffer, uint32_t numDat IVAS_PIDATA_AUDIO_DESC *audioDesc = (IVAS_PIDATA_AUDIO_DESC *) piData; /* Audio Description data is max 5 bytes */ - if ( numDataBytes > 5 ) + if ( numDataBytes > ( IVAS_PI_MAX_OBJECTS + 1 ) ) { return IVAS_ERROR( IVAS_ERR_UNPACK_PI_DATA, "Incorrect size to unpack Orientation PI data" ); } @@ -449,9 +454,9 @@ static ivas_error packListenerPosition( const IVAS_PIDATA_GENERIC *piData, uint8 buffer[nBytes++] = ( listener->piDataType & MASK_5BIT ); /* PF/PM populated during final packing */ buffer[nBytes++] = 6; - nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( listener->position.x / 327.68f ) ); - nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( listener->position.y / 327.68f ) ); - nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( listener->position.z / 327.68f ) ); + 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; } @@ -468,9 +473,9 @@ static ivas_error unpackListenerPosition( const uint8_t *buffer, uint32_t numDat listener->size = sizeof( IVAS_PIDATA_LISTENER_POSITION ); listener->piDataType = IVAS_PI_LISTENER_POSITION; - listener->position.x = ( (int16_t) readInt16( &buffer[0] ) ) / 100.0f; - listener->position.y = ( (int16_t) readInt16( &buffer[2] ) ) / 100.0f; - listener->position.z = ( (int16_t) readInt16( &buffer[4] ) ) / 100.0f; + 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; } @@ -498,7 +503,8 @@ static ivas_error packDiegetic( const IVAS_PIDATA_GENERIC *piData, uint8_t *buff return IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space to pack Diegetic Type PI data" ); } - for ( n = 0; n < 5; n++ ) + /* 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] ); @@ -528,7 +534,8 @@ static ivas_error unpackDiegetic( const uint8_t *buffer, uint32_t numDataBytes, diegetic->piDataType = IVAS_PI_DIEGETIC_TYPE; byte = buffer[0]; - for ( n = 0; n < 5; n++ ) + /* 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; } @@ -569,7 +576,7 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { packOrientation, /* HEAD_ORIENTATION */ packListenerPosition, /* LISTENER_POSITION */ packDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION */ - packAudioDescription, /* AUDIO_FOCUS_DIRECTION */ + packOrientation, /* AUDIO_FOCUS_DIRECTION */ #else packUnsupportedData, /* PLAYBACK_DEVICE_ORIENTATION */ packUnsupportedData, /* HEAD_ORIENTATION */ diff --git a/lib_util/ivas_rtp_pi_data.h b/lib_util/ivas_rtp_pi_data.h index ba82914df1..3be265bb87 100644 --- a/lib_util/ivas_rtp_pi_data.h +++ b/lib_util/ivas_rtp_pi_data.h @@ -1,6 +1,6 @@ /****************************************************************************************************** - (C) 2022-2024 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + (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 diff --git a/lib_util/ivas_rtp_rom.c b/lib_util/ivas_rtp_rom.c index 4b8539fb30..016e6ce34e 100644 --- a/lib_util/ivas_rtp_rom.c +++ b/lib_util/ivas_rtp_rom.c @@ -1,6 +1,6 @@ /****************************************************************************************************** - (C) 2022-2024 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + (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 -- GitLab From bfbc2e2739ca8fa8044a21c7f1ef5f08fb8b9ac4 Mon Sep 17 00:00:00 2001 From: Ripinder Singh Date: Thu, 4 Sep 2025 18:31:37 +1000 Subject: [PATCH 032/100] Code comments Signed-off-by: Ripinder Singh --- Workspace_msvc/lib_util.vcxproj | 2 - Workspace_msvc/lib_util.vcxproj.filters | 6 - apps/decoder.c | 13 +- apps/encoder.c | 8 +- lib_com/bitstream.c | 52 --- lib_com/common_api_types.h | 9 +- lib_com/ivas_cnst.h | 23 -- lib_com/ivas_error.h | 7 +- lib_com/ivas_prot.h | 4 - lib_enc/lib_enc.c | 19 -- lib_enc/lib_enc.h | 6 - lib_util/ivas_bpool.c | 2 +- lib_util/ivas_rtp_payload.c | 46 +-- lib_util/ivas_rtp_payload.h | 403 ------------------------ lib_util/ivas_rtp_pi_data.c | 58 +++- lib_util/ivas_rtp_rom.c | 64 ---- 16 files changed, 84 insertions(+), 638 deletions(-) delete mode 100644 lib_util/ivas_rtp_payload.h delete mode 100644 lib_util/ivas_rtp_rom.c diff --git a/Workspace_msvc/lib_util.vcxproj b/Workspace_msvc/lib_util.vcxproj index 94f22c71ca..7c10648b72 100644 --- a/Workspace_msvc/lib_util.vcxproj +++ b/Workspace_msvc/lib_util.vcxproj @@ -112,7 +112,6 @@ - @@ -145,7 +144,6 @@ - diff --git a/Workspace_msvc/lib_util.vcxproj.filters b/Workspace_msvc/lib_util.vcxproj.filters index f81b34b6da..ed062708ad 100644 --- a/Workspace_msvc/lib_util.vcxproj.filters +++ b/Workspace_msvc/lib_util.vcxproj.filters @@ -88,9 +88,6 @@ util_c - - util_c - util_c @@ -189,9 +186,6 @@ util_h - - util_h - diff --git a/apps/decoder.c b/apps/decoder.c index 8cd444ec9a..a9f5818606 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -568,7 +568,7 @@ static void do_object_editing( IVAS_EDITABLE_PARAMETERS *editableParameters, Obj #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 int restartDecoder( +static ivas_error restartDecoder( IVAS_DEC_HANDLE *phIvasDec, IVAS_DEC_MODE codec, DecArguments *arg, @@ -580,7 +580,7 @@ static int restartDecoder( if ( phIvasDec == NULL ) { - return -1; + return IVAS_ERR_UNEXPECTED_NULL_POINTER; } if ( NULL != *phIvasDec ) @@ -665,12 +665,11 @@ static int restartDecoder( } } - return 0; + return IVAS_ERR_OK; cleanup: IVAS_DEC_Close( phIvasDec ); - - return -1; + 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 ); @@ -3742,14 +3741,14 @@ static ivas_error decodeVoIP( if ( ivasRtp.restartNeeded ) { IVAS_DEC_MODE newCodecInPacket = ( ivasRtp.codecId == IVAS_RTP_EVS ) ? IVAS_DEC_MODE_EVS : IVAS_DEC_MODE_IVAS; - int err = restartDecoder( + error = restartDecoder( &hIvasDec, newCodecInPacket, &arg, NULL, /* ToDo : Provide rendererConfig */ NULL /* ToDo : Provide LS Custom Data */ ); - if ( err < 0 ) + if ( error != IVAS_ERR_OK ) { fprintf( stderr, "\nFailed to restart decoder from %d to %d\n", arg.decMode, newCodecInPacket ); goto cleanup; diff --git a/apps/encoder.c b/apps/encoder.c index 9fb3b60013..4235e3d788 100644 --- a/apps/encoder.c +++ b/apps/encoder.c @@ -640,7 +640,7 @@ int main( packCfg.maxFramesPerPacket = arg.numFramesPerPacket; /* Open the output file for RTPDump writing */ - error = IvasRtpFile_Open ( arg.outputBitstreamFilename, true, &hWriter ); + error = IvasRtpFile_Open( arg.outputBitstreamFilename, true, &hWriter ); if ( error != IVAS_ERR_OK ) { fprintf( stderr, "could not open: %s\n", arg.outputBitstreamFilename ); @@ -876,7 +876,7 @@ int main( /* Push Encoded Stream to */ if ( ( error = IVAS_RTP_PACK_PushFrame( hPack, - IVAS_isImmersiveFormat( hIvasEnc ) ? IVAS_RTP_IVAS : IVAS_RTP_EVS, + 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 */ @@ -939,7 +939,7 @@ int main( goto cleanup; } - if ( ( error = IvasRtpFile_Write ( hWriter, rtpPacket.buffer, rtpPacket.length ) ) != IVAS_ERR_OK ) + 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; @@ -1053,7 +1053,7 @@ cleanup: goto cleanup; } - if ( ( error = IvasRtpFile_Write ( hWriter, rtpPacket.buffer, rtpPacket.length ) ) != IVAS_ERR_OK ) + 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; diff --git a/lib_com/bitstream.c b/lib_com/bitstream.c index 22fbcde10e..18bfba0d18 100644 --- a/lib_com/bitstream.c +++ b/lib_com/bitstream.c @@ -163,58 +163,6 @@ Word16 rate2EVSmode( return rate2AMRWB_IOmode( brate ); } -#ifdef IVAS_RTPDUMP -/*-------------------------------------------------------------------* - * rate2IVASmode() - * - * Lookup IVAS mode. Note: FRAME_NO_DATA frames should be looked up with rate2EVSmode(). - *-------------------------------------------------------------------*/ -Word16 rate2IVASmode( - const Word32 brate /* i : bitrate */ -) -{ - switch ( brate ) - { - /* IVAS modes */ - case IVAS_SID_5k2: - return IVAS_TOC_SID; - case IVAS_13k2: - return IVAS_TOC_13200; - case IVAS_16k4: - return IVAS_TOC_16400; - case IVAS_24k4: - return IVAS_TOC_24400; - case IVAS_32k: - return IVAS_TOC_32000; - case IVAS_48k: - return IVAS_TOC_48000; - case IVAS_64k: - return IVAS_TOC_64000; - case IVAS_80k: - return IVAS_TOC_80000; - case IVAS_96k: - return IVAS_TOC_96000; - case IVAS_128k: - return IVAS_TOC_128000; - case IVAS_160k: - return IVAS_TOC_160000; - case IVAS_192k: - return IVAS_TOC_192000; - case IVAS_256k: - return IVAS_TOC_256000; - case IVAS_384k: - return IVAS_TOC_384000; - case IVAS_512k: - return IVAS_TOC_512000; - default: - break; - } - - return -1; -} - -#endif - /*-------------------------------------------------------------------* * ind_list_realloc() * diff --git a/lib_com/common_api_types.h b/lib_com/common_api_types.h index d711365703..03209f3e26 100644 --- a/lib_com/common_api_types.h +++ b/lib_com/common_api_types.h @@ -36,9 +36,6 @@ /* options.h needed for debugging/development features * It should be stripped for delivery along with debugging switches */ #include "options.h" -#ifdef IVAS_RTPDUMP -#include -#endif #include #include #include "ivas_error.h" @@ -219,7 +216,7 @@ typedef struct _IVAS_JBM_TRACE_DATA #ifdef IVAS_RTPDUMP typedef struct { - bool PIdataPresent; + int16_t PIdataPresent; char *sceneOrientation; char *deviceOrientationCompensated; char *deviceOrientationUncompensated; @@ -230,9 +227,9 @@ typedef struct typedef struct { - bool sceneOrientationSaved; + int16_t sceneOrientationSaved; IVAS_QUATERNION sceneOrientationQuat; - bool deviceOrientationSaved; + int16_t deviceOrientationSaved; IVAS_QUATERNION deviceOrientationQuat; } PI_DATA_DEPACKER_STATE; #endif diff --git a/lib_com/ivas_cnst.h b/lib_com/ivas_cnst.h index f8a2ca10fd..155fa08eb2 100755 --- a/lib_com/ivas_cnst.h +++ b/lib_com/ivas_cnst.h @@ -1758,29 +1758,6 @@ typedef enum } STEREO_DMX_EVS_PRC; -#ifdef IVAS_RTPDUMP -/* "IVAS bit rate" column in table A.3.3.3.2-1 of TS26.253 Annex A */ -typedef enum -{ - IVAS_TOC_13200 = 0x0, // 0000 - IVAS_TOC_16400 = 0x1, // 0001 - IVAS_TOC_24400 = 0x2, // 0010 - IVAS_TOC_32000 = 0x3, // 0011 - IVAS_TOC_48000 = 0x4, // 0100 - IVAS_TOC_64000 = 0x5, // 0101 - IVAS_TOC_80000 = 0x6, // 0110 - IVAS_TOC_96000 = 0x7, // 0111 - IVAS_TOC_128000 = 0x8, // 1000 - IVAS_TOC_160000 = 0x9, // 1001 - IVAS_TOC_192000 = 0xA, // 1010 - IVAS_TOC_256000 = 0xB, // 1011 - IVAS_TOC_384000 = 0xC, // 1100 - IVAS_TOC_512000 = 0xD, // 1101 - IVAS_TOC_RESERVED = 0xE, // 1110 - IVAS_TOC_SID = 0xF // 1111 -} IVAS_TOC_BITRATE_INDEX; - -#endif #endif /* clang-format on */ /* IVAS_CNST_H */ diff --git a/lib_com/ivas_error.h b/lib_com/ivas_error.h index 24b576d696..5a3d3fa787 100644 --- a/lib_com/ivas_error.h +++ b/lib_com/ivas_error.h @@ -153,10 +153,9 @@ typedef enum /*----------------------------------------* * rtp errors * *----------------------------------------*/ - IVAS_ERR_UNDERFLOW = 0x7000, - IVAS_ERR_PI_DATA_WITH_NO_INPUT_FRAME, - IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, - IVAS_ERR_UNPACK_PI_DATA, + IVAS_ERR_RTP_UNDERFLOW = 0x7000, + IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, + IVAS_ERR_RTP_UNPACK_PI_DATA, IVAS_ERR_RTP_UNSUPPORTED_FRAME, /*----------------------------------------* diff --git a/lib_com/ivas_prot.h b/lib_com/ivas_prot.h index 80232e5800..262d30ca99 100755 --- a/lib_com/ivas_prot.h +++ b/lib_com/ivas_prot.h @@ -806,10 +806,6 @@ void ivas_apply_non_diegetic_panning( ); #ifdef IVAS_RTPDUMP -Word16 rate2IVASmode( - const Word32 brate /* i : bitrate */ -); - void QuaternionProduct( const IVAS_QUATERNION q1, const IVAS_QUATERNION q2, diff --git a/lib_enc/lib_enc.c b/lib_enc/lib_enc.c index b2ea192118..705f3364e4 100644 --- a/lib_enc/lib_enc.c +++ b/lib_enc/lib_enc.c @@ -2417,25 +2417,6 @@ static void init_encoder_config( return; } -#ifdef IVAS_RTPDUMP - - -/*---------------------------------------------------------------------* - * IVAS_isImmersiveFormat() - * - * Check if the used format is immersive or mono - *---------------------------------------------------------------------*/ - -bool IVAS_isImmersiveFormat( - const IVAS_ENC_HANDLE hIvasEnc ) -{ - if ( hIvasEnc->st_ivas->hEncoderConfig->ivas_format == MONO_FORMAT ) - { - return false; - } - return true; -} -#endif #ifdef DEBUGGING diff --git a/lib_enc/lib_enc.h b/lib_enc/lib_enc.h index 478133db9f..af9f68e07b 100644 --- a/lib_enc/lib_enc.h +++ b/lib_enc/lib_enc.h @@ -383,12 +383,6 @@ void IVAS_ENC_PrintDisclaimer( void ); -#ifdef IVAS_RTPDUMP -/*! r: true if ivas format is not mono */ -bool IVAS_isImmersiveFormat( - const IVAS_ENC_HANDLE hIvasEnc /* i : IVAS encoder handle */ -); -#endif #ifdef DEBUGGING int32_t IVAS_ENC_GetNoCLipping( IVAS_ENC_HANDLE hIvasEnc, /* i : IVAS encoder handle */ diff --git a/lib_util/ivas_bpool.c b/lib_util/ivas_bpool.c index 2bf707cea6..827422fac3 100644 --- a/lib_util/ivas_bpool.c +++ b/lib_util/ivas_bpool.c @@ -117,7 +117,7 @@ ivas_error BPOOL_GetBuffer( BPOOL_HANDLE handle, void **dataPtr ) if ( !isFree ) { - return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "Underflow, no free buffers in pool" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Underflow, no free buffers in pool" ); } *dataPtr = handle->freeBuffers[idx]; diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index f716f31eee..468dce0d17 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -249,7 +249,7 @@ static ivas_error PackRtpHeader( buf->length = 0; if ( packedLength > buf->capacity ) { - return IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient output buffer for RTP header packing" ); + return IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient output buffer for RTP header packing" ); } /* @@ -335,7 +335,7 @@ static ivas_error UnpackRtpPacket( if ( packet->length < expectedSize ) { - return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "Insufficient input buffer for RTP header unpacking" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Insufficient input buffer for RTP header unpacking" ); } /* @@ -381,7 +381,7 @@ static ivas_error UnpackRtpPacket( expectedSize += ( header->CC ) * 4 + ( header->extension ? 4 : 0 ); if ( packet->length < expectedSize ) { - return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "CC indicated but insufficient input buffer" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "CC indicated but insufficient input buffer" ); } /* CSRC Identifiers */ @@ -404,7 +404,7 @@ static ivas_error UnpackRtpPacket( expectedSize += header->numExtUnits * 4; if ( packet->length < expectedSize ) { - return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "Extension Header indicated but insufficient input buffer" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Extension Header indicated but insufficient input buffer" ); } header->extData = realloc( header->extData, header->numExtUnits * 4 ); @@ -1010,16 +1010,16 @@ ivas_error IVAS_RTP_PACK_PushPiData( 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_INSUFFICIENT_OUTPUT_SIZE, "Insufficient memory to write RTP Payload" ); \ - goto err_exit; \ +#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( @@ -1088,7 +1088,7 @@ ivas_error IVAS_RTP_PACK_GetPayload( } else { - error = IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient memory to write RTP Payload" ); + error = IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient memory to write RTP Payload" ); goto err_exit; } payload->length += frameLength; @@ -1465,7 +1465,7 @@ static ivas_error parseToCByte( const IVAS_DATA_BUFFER *payload, uint32_t *numBy } else { - return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "Underflow during ToC SR byte" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Underflow during ToC SR byte" ); } #else /* Reserved bit not expected */ @@ -1529,7 +1529,7 @@ static ivas_error parsePIData( IVAS_RTP_UNPACK_HANDLE hUnpack, uint32_t rtpTimes if ( nBytes + 1 >= payload->length ) { - return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "Underflow during expected PI Header read" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Underflow during expected PI Header read" ); } piHeader0 = payload->buffer[nBytes++]; @@ -1544,7 +1544,7 @@ static ivas_error parsePIData( IVAS_RTP_UNPACK_HANDLE hUnpack, uint32_t rtpTimes piSize += byte; if ( nBytes >= payload->length ) { - return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "Underflow during reading piSize" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Underflow during reading piSize" ); } } while ( byte == 255 ); @@ -1552,7 +1552,7 @@ static ivas_error parsePIData( IVAS_RTP_UNPACK_HANDLE hUnpack, uint32_t rtpTimes { if ( piSize > 0 ) { - return IVAS_ERROR( IVAS_ERR_UNPACK_PI_DATA, "NO_PI_DATA should be 0 sized" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "NO_PI_DATA should be 0 sized" ); } /* Do not add a node for NO_DATA */ @@ -1577,7 +1577,7 @@ static ivas_error parsePIData( IVAS_RTP_UNPACK_HANDLE hUnpack, uint32_t rtpTimes } else { - return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "Underflow during reading pi data" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Underflow during reading pi data" ); } if ( PM == PI_HEADER_PM_LAST ) @@ -1669,7 +1669,7 @@ ivas_error IVAS_RTP_UNPACK_PushPayload( } else { - return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "Underflow during expected frame bits" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "Underflow during expected frame bits" ); } /* Add to frames FiFo */ @@ -1772,7 +1772,7 @@ ivas_error IVAS_RTP_UNPACK_PullFrame( /* Check if a node is available in FiFo */ if ( node == NULL ) { - return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "No more frames in unpack fifo" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "No more frames in unpack fifo" ); } length = ( node->toc.auNumBits + 7 ) / 8; @@ -1835,7 +1835,7 @@ ivas_error IVAS_RTP_UNPACK_PullNextPiData( /* Check if a node is available in FiFo */ if ( node == NULL ) { - return IVAS_ERROR( IVAS_ERR_UNDERFLOW, "No more pi data in unpack fifo" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNDERFLOW, "No more pi data in unpack fifo" ); } pi = (IVAS_PIDATA_GENERIC *) &node->data; diff --git a/lib_util/ivas_rtp_payload.h b/lib_util/ivas_rtp_payload.h deleted file mode 100644 index 92cb0e8e71..0000000000 --- a/lib_util/ivas_rtp_payload.h +++ /dev/null @@ -1,403 +0,0 @@ -/****************************************************************************************************** - - (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_PAYLOAD_H -#define IVAS_RTP_PAYLOAD_H - -#pragma once -#include -#include -#include -#include "common_api_types.h" -#include "ivas_cnst.h" -#include "rtpdump.h" - -#ifdef IVAS_RTPDUMP - -#ifdef __cplusplus -extern "C" -{ -#endif - - /* clang-format off */ - -static const int32_t IVASmode2rate[16] = { - 13200, - 16400, - 24400, - 32000, - 48000, - 64000, - 80000, - 96000, - 128000, - 160000, - 192000, - 256000, - 384000, - 512000, - -1, /* Reserved */ - 5200 /* SID */ -}; - -typedef enum -{ - PF0 = 0, - PF1 = 1 -} PI_PF; - -typedef enum -{ - PM_RESERVED = 0, - PM_NOT_LAST = 1, - PM_LAST = 2, - PM_GENERAL = 3 -} PI_PM; - -typedef enum -{ - PI_SCENE_ORIENTATION = 0, - PI_DEVICE_ORIENTATION_COMPENSATED = 1, - PI_DEVICE_ORIENTATION_UNCOMPENSATED = 2, - PI_ACOUSTIC_ENVIRONMENT = 3, - PI_NO_DATA = 31 -} PI_TYPE; - -typedef enum -{ - PI_SCENE_ORIENTATION_SIZE_BYTES = 8, - PI_DEVICE_ORIENTATION_COMPENSATED_SIZE_BYTES = 8, - PI_DEVICE_ORIENTATION_UNCOMPENSATED_SIZE_BYTES = 8, - PI_ACOUSTIC_ENVIRONMENT_ID_SIZE_BYTES = 1, - PI_ACOUSTIC_ENVIRONMENT_ONLY_LATE_REVERB_SIZE_BYTES = 5, - PI_ACOUSTIC_ENVIRONMENT_LATE_REVERB_AND_EARLY_REFLECTIONS_SIZE_BYTES = 8, - PI_NO_DATA_SIZE_BYTES = 0 -} PI_SIZE_BYTES; - -typedef struct -{ - IVAS_QUATERNION sceneOrientationQuat; - IVAS_QUATERNION deviceOrientationQuat; - bool sceneOrientationPresent; - bool deviceOrientationPresent; -} PI_DATA_PACKER; - -typedef struct -{ - uint16_t w, x, y, z; -} IVAS_QUATERNION_Q15; - -typedef enum -{ - IVAS_CMR_13k2 = 0xF0, // 1111 0000 - IVAS_CMR_16k4 = 0xF1, // 1111 0001 - IVAS_CMR_24k4 = 0xF2, // 1111 0010 - IVAS_CMR_32k = 0xF3, // 1111 0011 - IVAS_CMR_48k = 0xF4, // 1111 0100 - IVAS_CMR_64k = 0xF5, // 1111 0101 - IVAS_CMR_80k = 0xF6, // 1111 0110 - IVAS_CMR_96k = 0xF7, // 1111 0111 - IVAS_CMR_128k = 0xF8, // 1111 1000 - IVAS_CMR_160k = 0xF9, // 1111 1001 - IVAS_CMR_192k = 0xFA, // 1111 1010 - IVAS_CMR_256k = 0xFB, // 1111 1011 - IVAS_CMR_384k = 0xFC, // 1111 1100 - IVAS_CMR_512k = 0xFD, // 1111 1101 - IVAS_CMR_RESERVED = 0xFE, // 1111 1110 - IVAS_CMR_NO_REQ = 0xFF // 1111 1111 -} IVAS_INITIAL_E_BYTE; - -typedef enum -{ - IVAS_BW_REQ_WB = 0x80, // 1000 0000 - IVAS_BW_REQ_SWB = 0x81, // 1000 0001 - IVAS_BW_REQ_FB = 0x82, // 1000 0010 - IVAS_BW_NO_REQ = 0x83, // 1000 0011 - IVAS_FMT_REQ_STEREO = 0xA0, // 1010 0000 - IVAS_FMT_REQ_SBA = 0xA1, // 1010 0001 - IVAS_FMT_REQ_MASA = 0xA2, // 1010 0010 - IVAS_FMT_REQ_ISM = 0xA3, // 1010 0011 - IVAS_FMT_REQ_MC = 0xA4, // 1010 0100 - IVAS_FMT_REQ_OMASA = 0xA5, // 1010 0101 - IVAS_FMT_REQ_OSBA = 0xA6, // 1010 0110 - IVAS_FMT_NO_REQ = 0xA7, // 1010 0111 - IVAS_PI_INDICATION = 0xC0 // 1100 0000 -} IVAS_SUBSEQUENT_E_BYTE; - -typedef enum -{ - WB_REQUEST, - SWB_REQUEST, - FB_REQUEST, - NO_BANDWIDTH_REQUEST -} IVAS_BANDWIDTH_REQUEST; - -typedef enum -{ - STEREO_REQUEST, - SBA_REQUEST, - MASA_REQUEST, - ISM_REQUEST, - MC_REQUEST, - OMASA_REQUEST, - OSBA_REQUEST, - NO_FORMAT_REQUEST -} IVAS_FORMAT_REQUEST; - -typedef enum -{ - E_BYTE = 0x80, // 1xxx xxxx - IVAS_E_CMR = 0xF0, // 1111 xxxx - IVAS_E_BW_REQUEST = 0x80, // 100x xxxx - IVAS_E_FMT_REQUEST = 0xA0, // 101x xxxx - IVAS_E_PI_INDICATION = 0xC0, // 110x xxxx - IVAS_E_RESERVED = 0xE0 // 111x xxxx -} IVAS_E_BYTE_INDICATIONS; - -typedef struct -{ - RTPDUMP_HANDLE rtpdump; - RTPDUMP_RTPPACKET rtpPacket; - uint32_t timeoffset_ms; - uint16_t frameIndex; - bool frameFollowing; - PI_DATA_DEPACKER_STATE PIdataDepackerState; -} IVAS_RTPDUMP_DEPACKER; - -typedef struct -{ - RTPDUMP_HANDLE rtpdump; - RTPDUMP_RTPPACKET rtpPacket; - uint32_t timeoffset_ms; - PI_DATA_PACKER piDataPacker; - bool piDataPresent; - int32_t writingIndex; -} IVAS_RTPDUMP_PACKER; - -typedef enum -{ - IVAS_RTPDUMP_DEPACKER_NO_ERROR = 0, - IVAS_RTPDUMP_DEPACKER_EOF = -1, - IVAS_RTPDUMP_DEPACKER_RTPDUMP_ERROR = 1, - IVAS_RTPDUMP_DEPACKER_PAYLOAD_ERROR -} IVAS_RTPDUMP_DEPACKER_ERROR; - -typedef enum -{ - IVAS_RTPDUMP_PACKER_NO_ERROR = 0, - IVAS_RTPDUMP_PACKER_RTPDUMP_ERROR = 1 -} IVAS_RTPDUMP_PACKER_ERROR; - - -/*-----------------------------------------------------------------------* - * ivasPayload_unpackFrame() - * - * Parse a single IVAS frame from the payload. Also read the IVAS payload header - * and parse the PI data associated with the IVAS frame. - *-----------------------------------------------------------------------*/ - -bool ivasPayload_unpackFrame( - char *payload, /* i : RTP payload data */ - uint16_t payloadSizeBytes, /* i : RTP payload size in bytes */ - uint16_t frameIndex, /* i : Index for the data frame in the payload */ - bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ - bool *frameFollowing, /* o : (F) bit in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ - bool *isGoodFrame, /* o : Good frame indicator (set by the Q-bit in AMR-WB IO mode, otherwise always set to true) */ - unsigned char **frame, /* o : Data frame indicated by frameIndex */ - uint16_t *frameSizeInBits, /* o : Data frame size in bits */ - PI_DATA_DEPACKER_STATE *PIdataDepackerState /* i/o: data handle for the PI depacker state */ -); - - -/*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_DEPACKER_open() - * - * Open IVAS rtpdump depacker. - *-----------------------------------------------------------------------*/ - -IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_open( - IVAS_RTPDUMP_DEPACKER *self, /* o : IVAS rtpdump depacker handle */ - FILE *file /* i : Input file containing the rtpdump */ -); - - -/*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_DEPACKER_readNextFrame() - * - * Unpack next frame from the RTP packet. If the packet is depleted, read the next RTP packet from the rtpdump. - *-----------------------------------------------------------------------*/ - -IVAS_RTPDUMP_DEPACKER_ERROR IVAS_RTPDUMP_DEPACKER_readNextFrame( - IVAS_RTPDUMP_DEPACKER *self, /* i/o: IVAS rtpdump depacker handle */ - uint16_t *rtpSequenceNumber, /* o : RTP sequence number of the read packet */ - uint32_t *rtpTimeStamp, /* o : RTP timestamp for the unpacked frame */ - uint32_t *rcvTime_ms, /* o : Time offset in milliseconds for the unpacked frame */ - bool *evsIvasModeBit, /* o : "EVS/IVAS mode bit" in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the "EVS mode bit" separating EVS Primary and AMR-WB IO modes (see A.2.2.1.2 in TS26.445) */ - uint16_t *bitrateIndex, /* o : Bitrate index (BR) in the ToC byte (see A.3.3.3.2 in TS26.253 for IVAS or A.2.2.1.2 in TS26.445 for EVS) */ - bool *ivasIndicatorBit, /* o : "IVAS indicator" bit in the ToC byte (see A.3.3.3.2 in TS26.253). - * In EVS mode, this is the Q-bit (see A.2.2.1.2 in TS26.445) */ - bool *isGoodFrame, /* o : Good frame indicator (set by the Q-bit in AMR-WB IO mode, otherwise always set to true) */ - unsigned char **frame, /* o : Data frame indicated by frameIndex */ - uint16_t *frameSizeBits /* o : Data frame size in bits */ -); - - -/*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_DEPACKER_close() - * - * Close IVAS rtpdump depacker. - *-----------------------------------------------------------------------*/ - -void IVAS_RTPDUMP_DEPACKER_close( - IVAS_RTPDUMP_DEPACKER *self /* i : IVAS rtpdump depacker handle */ -); - - -/*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_open() - * - * Open IVAS rtpdump packer. - *-----------------------------------------------------------------------*/ - -IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_open( - IVAS_RTPDUMP_PACKER **rtpdumpPacker, /* o : IVAS rtpdump packer handle */ - FILE *file /* i : Output file for the rtpdump stream */ -); - - -/*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_writeEbytes() - * - * Write E-bytes (CMR and subsequent E-bytes) to the RTP packet. - *-----------------------------------------------------------------------*/ - -void IVAS_RTPDUMP_PACKER_writeEbytes( - IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ - int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet for bidirectional signalling */ - bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS). - * Immersive bitrate CMRs follow A.3.3.3.3.2 in TS26.253. Mono bitrate CMRs follow A.2.2.1.1 in TS26.445. */ - IVAS_BANDWIDTH_REQUEST ivasBandwidthRequest, /* i : Requested bandwidth to be written as subsequent E-byte in the packet for bidirectional signalling */ - IVAS_FORMAT_REQUEST ivasFormatRequest /* i : Requested format to be written as subsequent E-byte in the packet for bidirectional signalling */ -); - - -/*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_writeToc() - * - * Write a single ToC byte to the RTP packet. - *-----------------------------------------------------------------------*/ - -void IVAS_RTPDUMP_PACKER_writeToc( - IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ - int32_t frameBitrate, /* i : Bitrate of the written frame */ - bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS). - * Immersive format ToCs follow A.3.3.2 in TS26.253. Mono format ToCs follow A.2.2.1.2 in TS26.445.*/ - bool someFrameFollowing /* i : Flag to indicate if another frame follows this frame in the same RTP packet. */ -); - - -/*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_writeNextFrame() - * - * Write IVAS payload header, a single frame data and optional PI data to an RTP packet. - *-----------------------------------------------------------------------*/ - -IVAS_RTPDUMP_PACKER_ERROR IVAS_RTPDUMP_PACKER_writeNextFrame( - IVAS_RTPDUMP_PACKER *self, /* i/o: IVAS rtpdump packer handle */ - const uint16_t *bitStream, /* i : Bitstream containing the data frame to be written */ - int32_t numBits, /* i : Number of bits contained in the data frame */ - bool isImmersiveFormat, /* i : Flag to indicate if the frame is in immersive format (IVAS) or in mono format (EVS) */ - int32_t requestedBitrate, /* i : Requested bitrate to be written as CMR in the packet */ - bool requestImmersiveFormatBitrate, /* i : Flag to indicate if the requested bitrate is for immersive modes (IVAS) or mono mode (EVS) */ - IVAS_BANDWIDTH_REQUEST ivasBandwidthRequest, /* i : Requested bandwidth to be written as subsequent E-byte in the packet */ - IVAS_FORMAT_REQUEST ivasFormatRequest /* i : Requested format to be written as subsequent E-byte in the packet */ -); - - -/*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_writePIdata() - * - * Write PI data section (PI headers and PI frame data) to the RTP packet. - *-----------------------------------------------------------------------*/ - -void IVAS_RTPDUMP_PACKER_writePIdata( - IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ -); - - -/*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_determinePIpresence() - * - * Check if there is PI data present for the current frame. - *-----------------------------------------------------------------------*/ - -void IVAS_RTPDUMP_PACKER_determinePIpresence( - IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ -); - - -/*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_resetPIdata() - * - * Reset PI data packer handle. - *-----------------------------------------------------------------------*/ - -void IVAS_RTPDUMP_PACKER_resetPIdata( - IVAS_RTPDUMP_PACKER *self /* i/o: IVAS rtpdump packer handle */ -); - - -/*-----------------------------------------------------------------------* - * IVAS_RTPDUMP_PACKER_close() - * - * Close IVAS rtpdump packer. - *-----------------------------------------------------------------------*/ - -void IVAS_RTPDUMP_PACKER_close( - IVAS_RTPDUMP_PACKER **self /* i : IVAS rtpdump packer handle */ -); - - - /* clang-format on */ - -#ifdef __cplusplus -} -#endif - -#endif - -#endif /* IVAS_RTP_PAYLOAD_H */ diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index d3e8774281..f006b82a88 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -30,6 +30,7 @@ *******************************************************************************************************/ +#include #include "ivas_rtp_pi_data.h" #include "ivas_error_utils.h" #include "ivas_rtp_internal.h" @@ -94,7 +95,7 @@ static ivas_error packNoPiData( const IVAS_PIDATA_GENERIC *piData, uint8_t *buff /* NO_PI_DATA is just PI header with no data */ if ( maxDataBytes < 2 ) { - return IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space in PI data buffer for NO_PI_DATA" ); + 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 */ @@ -110,7 +111,7 @@ static ivas_error unpackNoPiData( const uint8_t *buffer, uint32_t numDataBytes, if ( numDataBytes != 0 ) { - return IVAS_ERROR( IVAS_ERR_UNPACK_PI_DATA, "NO_PI_DATA must be 0 byte" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "NO_PI_DATA must be 0 byte" ); } piData->size = sizeof( IVAS_PIDATA_NO_DATA ); @@ -141,7 +142,7 @@ static ivas_error packOrientation( const IVAS_PIDATA_GENERIC *piData, uint8_t *b /* Orientation data is 8 bytes, header is 2 bytes */ if ( maxDataBytes < 8 + 2 ) { - return IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space to pack Orientation PI data" ); + 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 */ @@ -162,7 +163,7 @@ static ivas_error unpackOrientation( const uint8_t *buffer, uint32_t numDataByte /* Orientation data is 8 bytes */ if ( numDataBytes != 8 ) { - return IVAS_ERROR( IVAS_ERR_UNPACK_PI_DATA, "Incorrect size to unpack Orientation PI data" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack Orientation PI data" ); } piData->size = sizeof( IVAS_PIDATA_ORIENTATION ); @@ -275,7 +276,7 @@ static ivas_error unpackAcousticEnvironment( const uint8_t *buffer, uint32_t num /* Acooustic Env data is either 1, 5 or 8 bytes */ if ( numDataBytes != 1 && numDataBytes != 5 && numDataBytes != 8 ) { - return IVAS_ERROR( IVAS_ERR_UNPACK_PI_DATA, "Incorrect size to unpack PI data of type Acoustic Environment" ); + 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 ); @@ -338,7 +339,7 @@ static ivas_error packAudioDescription( const IVAS_PIDATA_GENERIC *piData, uint8 /* Audio Description data is max 5 bytes, 2 bytes header */ if ( maxDataBytes < packedSize + 2 ) { - return IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space in Audio Description PI data buffer" ); + 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 */ @@ -365,7 +366,7 @@ static ivas_error unpackAudioDescription( const uint8_t *buffer, uint32_t numDat /* Audio Description data is max 5 bytes */ if ( numDataBytes > ( IVAS_PI_MAX_OBJECTS + 1 ) ) { - return IVAS_ERROR( IVAS_ERR_UNPACK_PI_DATA, "Incorrect size to unpack Orientation PI data" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack Orientation PI data" ); } audioDesc->size = sizeof( IVAS_PIDATA_AUDIO_DESC ); @@ -394,7 +395,7 @@ static ivas_error packDynamicSuppression( const IVAS_PIDATA_GENERIC *piData, uin /* Dynamic Audio Suppression data is 2 bytes, 2 bytes header */ if ( maxDataBytes < 2 + 2 ) { - return IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space in DAS PI data buffer" ); + 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 */ @@ -416,7 +417,7 @@ static ivas_error unpackDynamicSuppression( const uint8_t *buffer, uint32_t numD /* Dynamic Suppression data is 2 bytes */ if ( numDataBytes != 2 ) { - return IVAS_ERROR( IVAS_ERR_UNPACK_PI_DATA, "Incorrect size to unpack DAS PI data" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack DAS PI data" ); } das->size = sizeof( IVAS_PIDATA_AUDIO_DESC ); @@ -449,7 +450,7 @@ static ivas_error packListenerPosition( const IVAS_PIDATA_GENERIC *piData, uint8 /* Position data is 6 bytes, header is 2 bytes */ if ( maxDataBytes < 6 + 2 ) { - return IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space to pack LISTENER POSITION PI data" ); + 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 */ @@ -468,7 +469,7 @@ static ivas_error unpackListenerPosition( const uint8_t *buffer, uint32_t numDat /* Position data is 6 bytes */ if ( numDataBytes != 6 ) { - return IVAS_ERROR( IVAS_ERR_UNPACK_PI_DATA, "Incorrect size to unpack LISTENER POSITION PI data" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack LISTENER POSITION PI data" ); } listener->size = sizeof( IVAS_PIDATA_LISTENER_POSITION ); @@ -500,7 +501,7 @@ static ivas_error packDiegetic( const IVAS_PIDATA_GENERIC *piData, uint8_t *buff /* Diegetic data is 1 bytes, header is 2 bytes */ if ( maxDataBytes < 1 + 2 ) { - return IVAS_ERROR( IVAS_ERR_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space to pack Diegetic Type PI data" ); + 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 */ @@ -527,7 +528,7 @@ static ivas_error unpackDiegetic( const uint8_t *buffer, uint32_t numDataBytes, /* Diegetic data is 1 bytes */ if ( numDataBytes != 1 ) { - return IVAS_ERROR( IVAS_ERR_UNPACK_PI_DATA, "Incorrect size to unpack Diegetic PI data" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack Diegetic PI data" ); } diegetic->size = sizeof( IVAS_PIDATA_DIEGETIC ); @@ -710,7 +711,7 @@ ivas_error PI_UnPackData( uint8_t piDataType, uint32_t piSize, const uint8_t *pi /* Sanitize maximum sizes for each PI Type */ if ( piSize > maxPiDataSize[piDataType] ) { - return IVAS_ERROR( IVAS_ERR_UNPACK_PI_DATA, "Max size for PI Data type exceeded" ); + return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Max size for PI Data type exceeded" ); } error = unpackPiDataFuntions[piDataType]( piDataBuffer, piSize, piData ); @@ -722,4 +723,33 @@ ivas_error PI_UnPackData( uint8_t piDataType, uint32_t piSize, const uint8_t *pi 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_rom.c b/lib_util/ivas_rtp_rom.c deleted file mode 100644 index 016e6ce34e..0000000000 --- a/lib_util/ivas_rtp_rom.c +++ /dev/null @@ -1,64 +0,0 @@ -/****************************************************************************************************** - - (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 "ivas_rtp_internal.h" - -#ifdef IVAS_RTPDUMP - -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 */ -- GitLab From 75b97ed518dc9cf96b36afee5dfe89c50bb1e303 Mon Sep 17 00:00:00 2001 From: Ripinder Singh Date: Wed, 10 Sep 2025 21:28:13 +1000 Subject: [PATCH 033/100] code review Format, fixcmdline for self_test.prn is not followed by number Signed-off-by: Ripinder Singh --- lib_com/ivas_error.h | 16 + lib_util/ivas_bpool.h | 3 +- lib_util/ivas_queue.h | 2 +- lib_util/ivas_rtp_api.h | 1064 +++++++++++++++++----------------- lib_util/ivas_rtp_file.c | 2 +- lib_util/ivas_rtp_file.h | 14 +- lib_util/ivas_rtp_pi_data.c | 16 +- scripts/config/self_test.prm | 18 +- 8 files changed, 569 insertions(+), 566 deletions(-) diff --git a/lib_com/ivas_error.h b/lib_com/ivas_error.h index 66fa262d37..4a5249934c 100644 --- a/lib_com/ivas_error.h +++ b/lib_com/ivas_error.h @@ -296,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_util/ivas_bpool.h b/lib_util/ivas_bpool.h index ae8f114127..30cf4962b5 100644 --- a/lib_util/ivas_bpool.h +++ b/lib_util/ivas_bpool.h @@ -33,9 +33,8 @@ #ifndef IVAS_BPOOL_H #define IVAS_BPOOL_H -#include #include -#include "ivas_error.h" +#include "common_api_types.h" /* Forward declaraiton of opaque buffer pool handle */ typedef struct BPOOL *BPOOL_HANDLE; diff --git a/lib_util/ivas_queue.h b/lib_util/ivas_queue.h index 9f2a9a856c..1c6b77504e 100644 --- a/lib_util/ivas_queue.h +++ b/lib_util/ivas_queue.h @@ -34,7 +34,7 @@ #define IVAS_QUEUE_H #include -#include "ivas_error.h" +#include "common_api_types.h" typedef struct NODE { diff --git a/lib_util/ivas_rtp_api.h b/lib_util/ivas_rtp_api.h index 29ed397beb..06c56cd70f 100644 --- a/lib_util/ivas_rtp_api.h +++ b/lib_util/ivas_rtp_api.h @@ -33,47 +33,39 @@ #ifndef IVAS_RTP_API_H #define IVAS_RTP_API_H -#pragma once #include #include -#include -#include "options.h" -#include "ivas_error.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - /* - * +-----------------------+---------------------+--------------------+----------+ - * | 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 - * - */ +#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 */ @@ -88,516 +80,512 @@ extern "C" #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; +/* 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; +/* 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 */ +/* 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 */ + 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; + 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; +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) */ +/**********************************************/ +/* 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) */ + 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 */ - ); - -#ifdef __cplusplus -} -#endif + 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 index 0112fdfb54..0851a998f6 100644 --- a/lib_util/ivas_rtp_file.c +++ b/lib_util/ivas_rtp_file.c @@ -77,7 +77,7 @@ ivas_error IvasRtpFile_Close( ( *phReader )->f_rtpstream = NULL; } free( *phReader ); - *phReader = NULL; + *phReader = NULL; } return IVAS_ERR_OK; diff --git a/lib_util/ivas_rtp_file.h b/lib_util/ivas_rtp_file.h index 47d545208a..3156c34cb2 100644 --- a/lib_util/ivas_rtp_file.h +++ b/lib_util/ivas_rtp_file.h @@ -33,29 +33,29 @@ #ifndef IVAS_RTP_FILE_H #define IVAS_RTP_FILE_H -#include #include -#include "ivas_error.h" +#include +#include "common_api_types.h" typedef struct IVAS_RTP_FILE *IVAS_RTP_FILE_HANDLE; -ivas_error IvasRtpFile_Open ( +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_Close( + IVAS_RTP_FILE_HANDLE *phReader /* i : pointer to an IVAS file reader handle */ ); -ivas_error IvasRtpFile_Write ( +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_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 */ diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index f006b82a88..e7dcde8998 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -555,7 +555,7 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { #ifdef RTP_S4_251135_CR26253_0016_REV1 packAudioDescription, /* AUDIO_DESCRIPTION */ #else - packUnsupportedData, /* AUDIO_DESCRIPTION */ + packUnsupportedData, /* AUDIO_DESCRIPTION */ #endif /* RTP_S4_251135_CR26253_0016_REV1 */ packUnsupportedData, /* ISM_NUM */ packUnsupportedData, /* ISM_ID */ @@ -567,7 +567,7 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { #ifdef RTP_S4_251135_CR26253_0016_REV1 packDiegetic, /* DIEGETIC_TYPE */ #else - packUnsupportedData, /* DIEGETIC_TYPE */ + packUnsupportedData, /* DIEGETIC_TYPE */ #endif packUnsupportedData, /* RESERVED13 */ packUnsupportedData, /* RESERVED14 */ @@ -579,11 +579,11 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { 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 */ + 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 */ @@ -591,7 +591,7 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { #ifdef RTP_S4_251135_CR26253_0016_REV1 packOrientation, /* R_ISM_ORIENTATION */ #else - packUnsupportedData, /* R_ISM_ORIENTATION */ + packUnsupportedData, /* R_ISM_ORIENTATION */ #endif packUnsupportedData, /* R_ISM_POSITION */ packUnsupportedData, /* R_ISM_DIRECTION */ diff --git a/scripts/config/self_test.prm b/scripts/config/self_test.prm index 7f857487b9..7bfe33e4de 100644 --- a/scripts/config/self_test.prm +++ b/scripts/config/self_test.prm @@ -307,7 +307,7 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1 ../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 -dtx -stereo ../scripts/switchPaths/sw_13k2_to_128k_10fr.bin 48 testv/stvST48c.wav bit +../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 @@ -584,7 +584,7 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1 ../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 -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_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 @@ -965,7 +965,7 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1 ../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 -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_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 @@ -1248,7 +1248,7 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1 ../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 -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_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 @@ -1574,7 +1574,7 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1 ../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 -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_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 @@ -1587,7 +1587,7 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1 ../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 -stereo_dmx_evs 24400 48 testv/stvST48c.wav bit +../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 @@ -1600,7 +1600,7 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1 ../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 13200 48 testv/stv48c.wav bit +../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 @@ -1880,7 +1880,7 @@ eid-xor -fer -vbr -bs g192 -ep g192 bit ../scripts/dly_error_profiles/ep_5pct.g1 ../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 -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_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 @@ -2024,7 +2024,7 @@ networkSimulator_g192 ../scripts/dly_error_profiles/dly_error_profile_5.dat bit ../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 -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_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 -- GitLab From 3d72a4bd319d4c7fac565cca03295019f3e724f3 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 17 Sep 2025 18:24:20 +0300 Subject: [PATCH 034/100] Apply diegetic PI data in rendering --- apps/decoder.c | 9 +++++++++ lib_dec/ivas_objectRenderer_internal.c | 12 ++++++++++++ lib_dec/lib_dec.c | 25 ++++++++++++++++++++++++ lib_dec/lib_dec.h | 7 +++++++ lib_rend/ivas_objectRenderer.c | 27 ++++++++++++++++++++++++++ lib_rend/ivas_prot_rend.h | 6 ++++++ lib_rend/ivas_rotation.c | 25 ++++++++++++++++++++++++ lib_rend/ivas_stat_rend.h | 4 ++++ 8 files changed, 115 insertions(+) diff --git a/apps/decoder.c b/apps/decoder.c index f2f0e31b3d..a28d5d6023 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -534,6 +534,15 @@ static ivas_error IVAS_RTP_ApplyPiData( IVAS_RTP *rtp, IVAS_DEC_HANDLE hIvasDec, } break; +#ifdef RTP_S4_251135_CR26253_0016_REV1 + case IVAS_PI_DIEGETIC_TYPE: + { + DEBUG_PRINT( stdout, "PI_DIEGETIC_TYPE : %d, %d, %d, %d, %d\n", piData->data.digeticIndicator.isDiegetic[0], piData->data.digeticIndicator.isDiegetic[1], piData->data.digeticIndicator.isDiegetic[2], piData->data.digeticIndicator.isDiegetic[3], piData->data.digeticIndicator.isDiegetic[4] ); + IVAS_DEC_setDiegeticInputPI( hIvasDec, piData->data.digeticIndicator.isDiegetic ); + } + break; + +#endif default: { fprintf( stderr, "Unhandled PI data of type : %s\n", PiDataNames[piDataType] ); diff --git a/lib_dec/ivas_objectRenderer_internal.c b/lib_dec/ivas_objectRenderer_internal.c index ff47b1b9a7..353c15d43c 100644 --- a/lib_dec/ivas_objectRenderer_internal.c +++ b/lib_dec/ivas_objectRenderer_internal.c @@ -206,14 +206,26 @@ ivas_error ivas_td_binaural_renderer_sf( hIsmMetaData[nS] = &ismMetaData[nS]; } +#ifdef RTP_S4_251135_CR26253_0016_REV1 + if ( ( error = TDREND_Update_object_positions( st_ivas->hBinRendererTd, nchan_ism, st_ivas->ivas_format, hIsmMetaData, + ( st_ivas->hCombinedOrientationData != NULL ) ? st_ivas->hCombinedOrientationData->isDiegeticInputPI : NULL, + ( st_ivas->hCombinedOrientationData != NULL ) ? st_ivas->hCombinedOrientationData->isDiegeticInputPISet : false ) ) != IVAS_ERR_OK ) +#else if ( ( error = TDREND_Update_object_positions( st_ivas->hBinRendererTd, nchan_ism, st_ivas->ivas_format, hIsmMetaData ) ) != IVAS_ERR_OK ) +#endif { return error; } } else { +#ifdef RTP_S4_251135_CR26253_0016_REV1 + if ( ( error = TDREND_Update_object_positions( st_ivas->hBinRendererTd, nchan_ism, st_ivas->ivas_format, st_ivas->hIsmMetaData, + ( st_ivas->hCombinedOrientationData != NULL ) ? st_ivas->hCombinedOrientationData->isDiegeticInputPI : NULL, + ( st_ivas->hCombinedOrientationData != NULL ) ? st_ivas->hCombinedOrientationData->isDiegeticInputPISet : false ) ) != IVAS_ERR_OK ) +#else if ( ( error = TDREND_Update_object_positions( st_ivas->hBinRendererTd, nchan_ism, st_ivas->ivas_format, st_ivas->hIsmMetaData ) ) != IVAS_ERR_OK ) +#endif { return error; } diff --git a/lib_dec/lib_dec.c b/lib_dec/lib_dec.c index 4d27cb168e..b46def86e4 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -3827,6 +3827,31 @@ void IVAS_DEC_resetExternalOrientations( } +#endif +#ifdef RTP_S4_251135_CR26253_0016_REV1 +/*---------------------------------------------------------------------* + * IVAS_DEC_setDiegeticInput( ) + * + * Set isDiegeticInput flag for combined orientation handle based on PI data. + *---------------------------------------------------------------------*/ + +void IVAS_DEC_setDiegeticInputPI( + IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ + const bool *diegeticPIValues /* i : diegetic values for the input stream */ +) +{ + if ( hIvasDec->st_ivas->hCombinedOrientationData != NULL ) + { + int8_t i; + for ( i = 0; i < 4; i++ ) + { + hIvasDec->st_ivas->hCombinedOrientationData->isDiegeticInputPI[i] = diegeticPIValues[i]; + } + hIvasDec->st_ivas->hCombinedOrientationData->isDiegeticInputPISet = true; + } +} + + #endif /*---------------------------------------------------------------------* * IVAS_DEC_VoIP_IsEmpty( ) diff --git a/lib_dec/lib_dec.h b/lib_dec/lib_dec.h index 2e98d9e0f1..caed7bd54b 100644 --- a/lib_dec/lib_dec.h +++ b/lib_dec/lib_dec.h @@ -349,6 +349,13 @@ void IVAS_DEC_resetExternalOrientations( IVAS_DEC_HANDLE hIvasDec /* i/o: IVAS decoder handle */ ); +#endif +#ifdef RTP_S4_251135_CR26253_0016_REV1 +void IVAS_DEC_setDiegeticInputPI( + IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ + const bool *diegeticPIValues /* i : diegetic values for the input stream */ +); + #endif /* Setter functions - apply changes to decoder configuration */ diff --git a/lib_rend/ivas_objectRenderer.c b/lib_rend/ivas_objectRenderer.c index f0fb90fe4e..c467838971 100644 --- a/lib_rend/ivas_objectRenderer.c +++ b/lib_rend/ivas_objectRenderer.c @@ -363,7 +363,13 @@ ivas_error ivas_td_binaural_renderer_unwrap( if ( subframe_idx == ism_md_subframe_update ) { /* Update object position(s) */ +#ifdef RTP_S4_251135_CR26253_0016_REV1 + if ( ( error = TDREND_Update_object_positions( hBinRendererTd, num_src, ivas_format, hIsmMetaData, + ( hCombinedOrientationData != NULL ) ? hCombinedOrientationData->isDiegeticInputPI : NULL, + ( hCombinedOrientationData != NULL ) ? hCombinedOrientationData->isDiegeticInputPISet : false ) ) != IVAS_ERR_OK ) +#else if ( ( error = TDREND_Update_object_positions( hBinRendererTd, num_src, ivas_format, hIsmMetaData ) ) != IVAS_ERR_OK ) +#endif { return error; } @@ -531,7 +537,13 @@ ivas_error TDREND_Update_object_positions( BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd, /* i/o: TD Renderer handle */ const int16_t num_src, /* i : number of sources to render */ const IVAS_FORMAT in_format, /* i : Format of input sources */ +#ifdef RTP_S4_251135_CR26253_0016_REV1 + const ISM_METADATA_HANDLE *hIsmMetaData, /* i : Input metadata for ISM objects */ + const bool *isDiegeticInputPI, /* i : diegetic values for the input streams from the PI data */ + const bool isDiegeticInputPISet /* i : is diegetic values obtained from PI data */ +#else const ISM_METADATA_HANDLE *hIsmMetaData /* i : Input metadata for ISM objects */ +#endif ) { #ifndef NONBE_1377_REND_DIRATT_CONF @@ -544,6 +556,9 @@ ivas_error TDREND_Update_object_positions( #ifndef NONBE_1377_REND_DIRATT_CONF DirAtten_p = hBinRendererTd->DirAtten_p; #endif +#ifdef RTP_S4_251135_CR26253_0016_REV1 + bool isDiegeticSrcPI; +#endif /* For each source, write the frame data to the source object*/ for ( nS = 0; nS < num_src; nS++ ) @@ -571,7 +586,19 @@ ivas_error TDREND_Update_object_positions( return error; } +#ifdef RTP_S4_251135_CR26253_0016_REV1 + isDiegeticSrcPI = false; + if ( isDiegeticInputPISet && isDiegeticInputPI != NULL ) + { + if ( isDiegeticInputPI[nS] ) + { + isDiegeticSrcPI = true; + } + } + if ( hIsmMetaData[nS]->non_diegetic_flag || !isDiegeticSrcPI ) +#else if ( hIsmMetaData[nS]->non_diegetic_flag ) +#endif { Pos[0] = 0; Pos[1] = hIsmMetaData[nS]->azimuth / 90.f; diff --git a/lib_rend/ivas_prot_rend.h b/lib_rend/ivas_prot_rend.h index 937923c338..e3d205cd25 100644 --- a/lib_rend/ivas_prot_rend.h +++ b/lib_rend/ivas_prot_rend.h @@ -691,7 +691,13 @@ ivas_error TDREND_Update_object_positions( BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd, /* i/o: TD Renderer handle */ const int16_t num_src, /* i : number of sources to render */ const IVAS_FORMAT in_format, /* i : Format of input sources */ +#ifdef RTP_S4_251135_CR26253_0016_REV1 + const ISM_METADATA_HANDLE *hIsmMetaData, /* i : Input metadata for ISM objects */ + const bool *isDiegeticInputPI, /* i : diegetic values for the input streams from the PI data */ + const bool isDiegeticInputPISet /* i : is diegetic values obtained from PI data */ +#else const ISM_METADATA_HANDLE *hIsmMetaData /* i : Input metadata for ISM objects */ +#endif ); void BSplineModelEvalDealloc( diff --git a/lib_rend/ivas_rotation.c b/lib_rend/ivas_rotation.c index 9a1d74581d..05fb92cd18 100644 --- a/lib_rend/ivas_rotation.c +++ b/lib_rend/ivas_rotation.c @@ -862,6 +862,14 @@ ivas_error ivas_combined_orientation_open( ( *hCombinedOrientationData )->subframe_idx = 0; ( *hCombinedOrientationData )->subframe_size = (int16_t) ( fs / ( FRAMES_PER_SEC * MAX_PARAM_SPATIAL_SUBFRAMES ) ); ( *hCombinedOrientationData )->cur_subframe_samples_rendered = 0; +#ifdef RTP_S4_251135_CR26253_0016_REV1 + + for ( i = 0; i < 4; i++ ) + { + ( *hCombinedOrientationData )->isDiegeticInputPI[i] = true; + } + ( *hCombinedOrientationData )->isDiegeticInputPISet = false; +#endif return IVAS_ERR_OK; } @@ -1023,10 +1031,27 @@ ivas_error combine_external_and_head_orientations( else if ( hExtOrientationData == NULL && headRotQuaternions != NULL ) { /* Head rotation only */ +#ifdef RTP_S4_251135_CR26253_0016_REV1 + if ( hCombinedOrientationData->isDiegeticInputPISet && !hCombinedOrientationData->isDiegeticInputPI[0] && !hCombinedOrientationData->isDiegeticInputPI[1] && !hCombinedOrientationData->isDiegeticInputPI[2] && !hCombinedOrientationData->isDiegeticInputPI[3] && !hCombinedOrientationData->isDiegeticInputPI[4] ) + { + for ( i = 0; i < hCombinedOrientationData->num_subframes; i++ ) + { + hCombinedOrientationData->Quaternions[i] = identity; + } + } + else + { + for ( i = 0; i < hCombinedOrientationData->num_subframes; i++ ) + { + hCombinedOrientationData->Quaternions[i] = headRotQuaternions[i]; + } + } +#else for ( i = 0; i < hCombinedOrientationData->num_subframes; i++ ) { hCombinedOrientationData->Quaternions[i] = headRotQuaternions[i]; } +#endif } if ( hExtOrientationData != NULL ) diff --git a/lib_rend/ivas_stat_rend.h b/lib_rend/ivas_stat_rend.h index 69fcb30fb8..fb8bf44b83 100644 --- a/lib_rend/ivas_stat_rend.h +++ b/lib_rend/ivas_stat_rend.h @@ -710,6 +710,10 @@ typedef struct ivas_combined_orientation_struct int16_t cur_subframe_samples_rendered; int16_t subframe_idx_start; int16_t cur_subframe_samples_rendered_start; +#ifdef RTP_S4_251135_CR26253_0016_REV1 + bool isDiegeticInputPI[5]; + bool isDiegeticInputPISet; +#endif } COMBINED_ORIENTATION_DATA, *COMBINED_ORIENTATION_HANDLE; /*----------------------------------------------------------------------------------* -- GitLab From 4aca7e15cb57d2b40dd553c056279b7502c3dd57 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 24 Sep 2025 15:18:53 +0300 Subject: [PATCH 035/100] Remove diegetic PI interpretation from individual ISM rendering --- lib_dec/ivas_objectRenderer_internal.c | 12 ------------ lib_rend/ivas_objectRenderer.c | 27 -------------------------- lib_rend/ivas_prot_rend.h | 6 ------ 3 files changed, 45 deletions(-) diff --git a/lib_dec/ivas_objectRenderer_internal.c b/lib_dec/ivas_objectRenderer_internal.c index 353c15d43c..ff47b1b9a7 100644 --- a/lib_dec/ivas_objectRenderer_internal.c +++ b/lib_dec/ivas_objectRenderer_internal.c @@ -206,26 +206,14 @@ ivas_error ivas_td_binaural_renderer_sf( hIsmMetaData[nS] = &ismMetaData[nS]; } -#ifdef RTP_S4_251135_CR26253_0016_REV1 - if ( ( error = TDREND_Update_object_positions( st_ivas->hBinRendererTd, nchan_ism, st_ivas->ivas_format, hIsmMetaData, - ( st_ivas->hCombinedOrientationData != NULL ) ? st_ivas->hCombinedOrientationData->isDiegeticInputPI : NULL, - ( st_ivas->hCombinedOrientationData != NULL ) ? st_ivas->hCombinedOrientationData->isDiegeticInputPISet : false ) ) != IVAS_ERR_OK ) -#else if ( ( error = TDREND_Update_object_positions( st_ivas->hBinRendererTd, nchan_ism, st_ivas->ivas_format, hIsmMetaData ) ) != IVAS_ERR_OK ) -#endif { return error; } } else { -#ifdef RTP_S4_251135_CR26253_0016_REV1 - if ( ( error = TDREND_Update_object_positions( st_ivas->hBinRendererTd, nchan_ism, st_ivas->ivas_format, st_ivas->hIsmMetaData, - ( st_ivas->hCombinedOrientationData != NULL ) ? st_ivas->hCombinedOrientationData->isDiegeticInputPI : NULL, - ( st_ivas->hCombinedOrientationData != NULL ) ? st_ivas->hCombinedOrientationData->isDiegeticInputPISet : false ) ) != IVAS_ERR_OK ) -#else if ( ( error = TDREND_Update_object_positions( st_ivas->hBinRendererTd, nchan_ism, st_ivas->ivas_format, st_ivas->hIsmMetaData ) ) != IVAS_ERR_OK ) -#endif { return error; } diff --git a/lib_rend/ivas_objectRenderer.c b/lib_rend/ivas_objectRenderer.c index c467838971..f0fb90fe4e 100644 --- a/lib_rend/ivas_objectRenderer.c +++ b/lib_rend/ivas_objectRenderer.c @@ -363,13 +363,7 @@ ivas_error ivas_td_binaural_renderer_unwrap( if ( subframe_idx == ism_md_subframe_update ) { /* Update object position(s) */ -#ifdef RTP_S4_251135_CR26253_0016_REV1 - if ( ( error = TDREND_Update_object_positions( hBinRendererTd, num_src, ivas_format, hIsmMetaData, - ( hCombinedOrientationData != NULL ) ? hCombinedOrientationData->isDiegeticInputPI : NULL, - ( hCombinedOrientationData != NULL ) ? hCombinedOrientationData->isDiegeticInputPISet : false ) ) != IVAS_ERR_OK ) -#else if ( ( error = TDREND_Update_object_positions( hBinRendererTd, num_src, ivas_format, hIsmMetaData ) ) != IVAS_ERR_OK ) -#endif { return error; } @@ -537,13 +531,7 @@ ivas_error TDREND_Update_object_positions( BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd, /* i/o: TD Renderer handle */ const int16_t num_src, /* i : number of sources to render */ const IVAS_FORMAT in_format, /* i : Format of input sources */ -#ifdef RTP_S4_251135_CR26253_0016_REV1 - const ISM_METADATA_HANDLE *hIsmMetaData, /* i : Input metadata for ISM objects */ - const bool *isDiegeticInputPI, /* i : diegetic values for the input streams from the PI data */ - const bool isDiegeticInputPISet /* i : is diegetic values obtained from PI data */ -#else const ISM_METADATA_HANDLE *hIsmMetaData /* i : Input metadata for ISM objects */ -#endif ) { #ifndef NONBE_1377_REND_DIRATT_CONF @@ -556,9 +544,6 @@ ivas_error TDREND_Update_object_positions( #ifndef NONBE_1377_REND_DIRATT_CONF DirAtten_p = hBinRendererTd->DirAtten_p; #endif -#ifdef RTP_S4_251135_CR26253_0016_REV1 - bool isDiegeticSrcPI; -#endif /* For each source, write the frame data to the source object*/ for ( nS = 0; nS < num_src; nS++ ) @@ -586,19 +571,7 @@ ivas_error TDREND_Update_object_positions( return error; } -#ifdef RTP_S4_251135_CR26253_0016_REV1 - isDiegeticSrcPI = false; - if ( isDiegeticInputPISet && isDiegeticInputPI != NULL ) - { - if ( isDiegeticInputPI[nS] ) - { - isDiegeticSrcPI = true; - } - } - if ( hIsmMetaData[nS]->non_diegetic_flag || !isDiegeticSrcPI ) -#else if ( hIsmMetaData[nS]->non_diegetic_flag ) -#endif { Pos[0] = 0; Pos[1] = hIsmMetaData[nS]->azimuth / 90.f; diff --git a/lib_rend/ivas_prot_rend.h b/lib_rend/ivas_prot_rend.h index e3d205cd25..937923c338 100644 --- a/lib_rend/ivas_prot_rend.h +++ b/lib_rend/ivas_prot_rend.h @@ -691,13 +691,7 @@ ivas_error TDREND_Update_object_positions( BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd, /* i/o: TD Renderer handle */ const int16_t num_src, /* i : number of sources to render */ const IVAS_FORMAT in_format, /* i : Format of input sources */ -#ifdef RTP_S4_251135_CR26253_0016_REV1 - const ISM_METADATA_HANDLE *hIsmMetaData, /* i : Input metadata for ISM objects */ - const bool *isDiegeticInputPI, /* i : diegetic values for the input streams from the PI data */ - const bool isDiegeticInputPISet /* i : is diegetic values obtained from PI data */ -#else const ISM_METADATA_HANDLE *hIsmMetaData /* i : Input metadata for ISM objects */ -#endif ); void BSplineModelEvalDealloc( -- GitLab From 116bacde489de22f0a5c202a0477335171278399 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 24 Sep 2025 16:41:10 +0300 Subject: [PATCH 036/100] Begin audio focus PI implementation --- apps/decoder.c | 36 +++++++++++-- lib_util/ivas_rtp_pi_data.c | 103 +++++++++++++++++++++++++++++++++--- lib_util/ivas_rtp_pi_data.h | 42 +++++++++++++-- tests/rtp/ivasrtp.py | 34 ++++++++++-- tests/rtp/test_rtp.py | 3 +- 5 files changed, 198 insertions(+), 20 deletions(-) diff --git a/apps/decoder.c b/apps/decoder.c index a28d5d6023..df7dcf5168 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -216,8 +216,8 @@ 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", + "AUDIO_FOCUS_INDICATION", "RESERVED15", "PLAYBACK_DEVICE_ORIENTATION", "HEAD_ORIENTATION", "LISTENER_POSITION", + "DYNAMIC_AUDIO_SUPPRESSION", "AUDIO_FOCUS_REQUEST", "PI_LATENCY", "R_ISM_ID", "R_ISM_GAIN", "R_ISM_ORIENTATION", "R_ISM_POSITION", "R_ISM_DIRECTION", "RESERVED27", "RESERVED28", "RESERVED29", "RESERVED30", "NO_DATA" }; @@ -261,7 +261,6 @@ static void IVAS_RTP_LogPiData( FILE *f_piDataOut, PIDATA_TS *piData, uint32_t n #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 { @@ -327,6 +326,21 @@ static void IVAS_RTP_LogPiData( FILE *f_piDataOut, PIDATA_TS *piData, uint32_t n fprintf( f_piDataOut, "\t\t\t]\n\t\t}" ); } break; + case IVAS_PI_AUDIO_FOCUS_INDICATION: + { + fprintf( f_piDataOut, "{\n" ); + if ( cur->data.focusIndication.availDirection ) + { + fprintf( f_piDataOut, "\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\t", + cur->data.focusIndication.direction.w, cur->data.focusIndication.direction.x, cur->data.focusIndication.direction.y, cur->data.focusIndication.direction.z ); + } + if ( cur->data.focusIndication.availLevel ) + { + fprintf( f_piDataOut, "\t\t\t\"level\": %d", cur->data.focusIndication.flvl ); + } + fprintf( f_piDataOut, "\n\t\t}" ); + } + break; case IVAS_PI_DYNAMIC_AUDIO_SUPPRESSION: { IVAS_PIDATA_DYNAMIC_SUPPRESSION *das = &cur->data.dynSuppression; @@ -339,7 +353,21 @@ static void IVAS_RTP_LogPiData( FILE *f_piDataOut, PIDATA_TS *piData, uint32_t n } break; case IVAS_PI_RESERVED13: - case IVAS_PI_RESERVED14: + case IVAS_PI_AUDIO_FOCUS_REQUEST: + { + fprintf( f_piDataOut, "{\n" ); + if ( cur->data.focusRequest.availDirection ) + { + fprintf( f_piDataOut, "\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\t", + cur->data.focusRequest.direction.w, cur->data.focusRequest.direction.x, cur->data.focusRequest.direction.y, cur->data.focusRequest.direction.z ); + } + if ( cur->data.focusRequest.availLevel ) + { + fprintf( f_piDataOut, "\t\t\t\"level\": %d", cur->data.focusRequest.flvl ); + } + fprintf( f_piDataOut, "\n\t\t}" ); + } + break; case IVAS_PI_RESERVED15: case IVAS_PI_RESERVED27: case IVAS_PI_RESERVED28: diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index e7dcde8998..24ecabe6e8 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -132,7 +132,7 @@ static ivas_error packOrientation( const IVAS_PIDATA_GENERIC *piData, uint8_t *b 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 ) + && ( piData->piDataType != IVAS_PI_PLAYBACK_DEVICE_ORIENTATION ) && ( piData->piDataType != IVAS_PI_HEAD_ORIENTATION ) #endif /* RTP_S4_251135_CR26253_0016_REV1 */ ) { @@ -544,6 +544,95 @@ static ivas_error unpackDiegetic( const uint8_t *buffer, uint32_t numDataBytes, return IVAS_ERR_OK; } +static ivas_error packAudioFocusCommon( 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_AUDIO_FOCUS *audioFocus = (const IVAS_PIDATA_AUDIO_FOCUS *) piData; + + *nBytesWritten = 0; + + if ( piData->size != sizeof( IVAS_PIDATA_AUDIO_FOCUS ) ) + { + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect size in PI data of type Audio Focus" ); + } + + if ( audioFocus->availDirection && audioFocus->availLevel ) + { + packedSize = 9; + } + else if ( audioFocus->availDirection ) + { + packedSize = 8; + } + else if ( audioFocus->availLevel ) + { + packedSize = 1; + } + else + { + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Neither direction or level is available for packing Audio Focus" ); + } + + /* Audio Focus 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 Audio Focus PI data" ); + } + + buffer[nBytes++] = ( audioFocus->piDataType & MASK_5BIT ); /* PF/PM populated during final packing */ + buffer[nBytes++] = packedSize; + + if ( packedSize == 9 || packedSize == 8 ) + { + nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( audioFocus->direction.w ) ); + nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( audioFocus->direction.x ) ); + nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( audioFocus->direction.y ) ); + nBytes = writeInt16( buffer, nBytes, ivasPayload_convertToQ15( audioFocus->direction.z ) ); + } + if ( packedSize == 9 || packedSize == 1) + { + buffer[nBytes++] = ( (uint8_t) audioFocus->flvl & MASK_4BIT ) << 4; + } + + *nBytesWritten = nBytes; + return IVAS_ERR_OK; +} + +static ivas_error unpackAudioFocusCommon( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData ) +{ + IVAS_PIDATA_AUDIO_FOCUS *audioFocus = (IVAS_PIDATA_AUDIO_FOCUS *) piData; + + /* Audio Focus data is either 1, 8 or 9 bytes */ + if ( numDataBytes != 1 && numDataBytes != 8 && numDataBytes != 9 ) + { + return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack PI data of type Audio Focus" ); + } + + piData->size = sizeof( IVAS_PIDATA_AUDIO_FOCUS ); + audioFocus->availDirection = ( numDataBytes >= 8 ); + audioFocus->availLevel = ( numDataBytes == 1 || numDataBytes == 9 ); + + if ( numDataBytes == 1 ) + { + audioFocus->flvl = ( buffer[0] >> 4 ); + } + else + { + audioFocus->direction.w = FLOAT_FROM_Q15( readInt16( &buffer[0] ) ); + audioFocus->direction.x = FLOAT_FROM_Q15( readInt16( &buffer[2] ) ); + audioFocus->direction.y = FLOAT_FROM_Q15( readInt16( &buffer[4] ) ); + audioFocus->direction.z = FLOAT_FROM_Q15( readInt16( &buffer[6] ) ); + + if ( numDataBytes == 9 ) + { + audioFocus->flvl = ( buffer[8] >> 4 ); + } + } + + return IVAS_ERR_OK; +} + #endif /* RTP_S4_251135_CR26253_0016_REV1 */ @@ -570,14 +659,14 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { packUnsupportedData, /* DIEGETIC_TYPE */ #endif packUnsupportedData, /* RESERVED13 */ - packUnsupportedData, /* RESERVED14 */ + packAudioFocusCommon,/* AUDIO_FOCUS_INDICATION */ 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 */ + packAudioFocusCommon, /* AUDIO_FOCUS_REQUEST */ #else packUnsupportedData, /* PLAYBACK_DEVICE_ORIENTATION */ packUnsupportedData, /* HEAD_ORIENTATION */ @@ -625,14 +714,14 @@ static const UNPACK_PI_FN unpackPiDataFuntions[IVAS_PI_MAX_ID] = { unpackUnsupportedData, /* DIEGETIC_TYPE */ #endif unpackUnsupportedData, /* RESERVED13 */ - unpackUnsupportedData, /* RESERVED14 */ + unpackAudioFocusCommon,/* AUDIO_FOCUS_INDICATION */ 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 */ + unpackAudioFocusCommon, /* AUDIO_FOCUS_REQUEST */ #else unpackUnsupportedData, /* PLAYBACK_DEVICE_ORIENTATION */ unpackUnsupportedData, /* HEAD_ORIENTATION */ @@ -672,13 +761,13 @@ static const uint32_t maxPiDataSize[IVAS_PI_MAX_ID] = { 8, /* IVAS_PI_ISM_DIRECTIVITY */ 1, /* IVAS_PI_DIEGETIC_TYPE */ 0, /* IVAS_PI_RESERVED13 */ - 0, /* IVAS_PI_RESERVED14 */ + 9, /* IVAS_PI_AUDIO_FOCUS_INDICATION */ 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 */ + 9, /* IVAS_PI_AUDIO_FOCUS_REQUEST */ 4, /* IVAS_PI_PI_LATENCY */ 1, /* IVAS_PI_R_ISM_ID */ 1, /* IVAS_PI_R_ISM_GAIN */ diff --git a/lib_util/ivas_rtp_pi_data.h b/lib_util/ivas_rtp_pi_data.h index 3be265bb87..aad50dd6b6 100644 --- a/lib_util/ivas_rtp_pi_data.h +++ b/lib_util/ivas_rtp_pi_data.h @@ -64,7 +64,7 @@ extern "C" 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_AUDIO_FOCUS_INDICATION, /* audio focus indication (direction in Quaternions and/or level) */ IVAS_PI_RESERVED15, /* reserved */ /* Reverse direction PI types */ @@ -72,7 +72,7 @@ extern "C" 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_AUDIO_FOCUS_REQUEST, /* direction of interest for the listener in Quaternions and/or audio focus level */ 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 */ @@ -100,7 +100,6 @@ extern "C" * - 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 @@ -293,6 +292,40 @@ extern "C" bool isDiegetic[1 + IVAS_PI_MAX_OBJECTS]; /* diegetic indication as per audio format */ } IVAS_PIDATA_DIEGETIC; + /* Audio focus direction indicates a direction of interest. + * The audio focus level indicates the amount of suppression applied to the + * directions other than the audio focus direction. + */ + typedef enum + { + IVAS_FLVL_NO_AUDIO_FOCUS = 0, /* Apply no audio focus */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_1, /* Audio focus level 1 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_2, /* Audio focus level 2 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_3, /* Audio focus level 3 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_4, /* Audio focus level 4 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_5, /* Audio focus level 5 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_6, /* Audio focus level 6 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_7, /* Audio focus level 7 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_8, /* Audio focus level 8 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_9, /* Audio focus level 9 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_10, /* Audio focus level 10 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_11, /* Audio focus level 11 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_12, /* Audio focus level 12 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_13, /* Audio focus level 13 */ + IVAS_FLVL_DEFAULT_AUDIO_FOCUS, /* Default audio focus */ + IVAS_FLVL_MAX_AUDIO_FOCUS, /* Apply max audio focus */ + } IVAS_FLVL; + + typedef struct + { + size_t size; /* sizeof(IVAS_PIDATA_AUDIO_FOCUS) */ + uint32_t piDataType; /* IVAS_PI_AUDIO_FOCUS_INDCATION or IVAS_PI_AUDIO_FOCUS_REQUEST */ + bool availDirection; /* audio focus contains direction */ + bool availLevel; /* audio focus contains level */ + IVAS_QUATERNION direction; /* direction data expressed as quarternions */ + IVAS_FLVL flvl; /* audio focus level */ + } IVAS_PIDATA_AUDIO_FOCUS; + /* Listener position */ typedef struct { @@ -416,12 +449,13 @@ extern "C" IVAS_PIDATA_ISM_ATTENUATION ismAttenuation; IVAS_PIDATA_ISM_DIRECTIVITY ismDirectivity; IVAS_PIDATA_DIEGETIC digeticIndicator; + IVAS_PIDATA_AUDIO_FOCUS focusIndication; IVAS_PIDATA_ORIENTATION playbackOrientation; IVAS_PIDATA_ORIENTATION headOrientation; IVAS_PIDATA_LISTENER_POSITION listnerPosition; IVAS_PIDATA_DYNAMIC_SUPPRESSION dynSuppression; - IVAS_PIDATA_ORIENTATION focusDirection; + IVAS_PIDATA_AUDIO_FOCUS focusRequest; IVAS_PIDATA_REVERSE_PI_LATENCY piLatency; IVAS_PIDATA_ISM_EDIT_ID ismEditId; IVAS_PIDATA_ISM_EDIT_GAIN ismEditGain; diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py index f6923c0077..8a2fd0f87d 100644 --- a/tests/rtp/ivasrtp.py +++ b/tests/rtp/ivasrtp.py @@ -170,13 +170,13 @@ class PIDATAS(str, Enum): ISM_DIRECTIVITY = "ISM_DIRECTIVITY" DIEGETIC_TYPE = "DIEGETIC_TYPE" RESERVED13 = "RESERVED13" - RESERVED14 = "RESERVED14" + AUDIO_FOCUS_INDICATION = "AUDIO_FOCUS_INDICATION" 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" + AUDIO_FOCUS_REQUEST = "AUDIO_FOCUS_REQUEST" PI_LATENCY = "PI_LATENCY" R_ISM_ID = "R_ISM_ID" R_ISM_GAIN = "R_ISM_GAIN" @@ -207,6 +207,24 @@ class SUPPRESSION_LEVEL(int, Enum): SUPPRESSION_LEVEL_14 = 14 SUPPRESSION_LEVEL_MAX = 15 +class AUDIO_FOCUS_LEVEL(int, Enum): + AUDIO_FOCUS_LEVEL_NONE = 0 + AUDIO_FOCUS_LEVEL_1 = 1 + AUDIO_FOCUS_LEVEL_2 = 2 + AUDIO_FOCUS_LEVEL_3 = 3 + AUDIO_FOCUS_LEVEL_4 = 4 + AUDIO_FOCUS_LEVEL_5 = 5 + AUDIO_FOCUS_LEVEL_6 = 6 + AUDIO_FOCUS_LEVEL_7 = 7 + AUDIO_FOCUS_LEVEL_8 = 8 + AUDIO_FOCUS_LEVEL_9 = 9 + AUDIO_FOCUS_LEVEL_10 = 10 + AUDIO_FOCUS_LEVEL_11 = 11 + AUDIO_FOCUS_LEVEL_12 = 12 + AUDIO_FOCUS_LEVEL_13 = 13 + AUDIO_FOCUS_LEVEL_DEFAULT = 14 + AUDIO_FOCUS_LEVEL_NO_PREFERENCE = 15 + @dataclass class RTPHDR: @@ -343,6 +361,14 @@ class ACOUSTIC_ENVIRONMENT: dim: tuple[float, float, float] = () abscoeff: tuple[float, float, float, float, float, float] = () +@dataclass +class AUDIO_FOCUS: + w: float = 0.0 + x: float = 0.0 + y: float = 0.0 + z: float = 0.0 + level: AUDIO_FOCUS_LEVEL = AUDIO_FOCUS_LEVEL.AUDIO_FOCUS_LEVEL_DEFAULT + @dataclass class PIDATA: timestamp: int = 0 @@ -589,13 +615,13 @@ PIDataUnpacker = [ unpackUnsupported, # ISM_DIRECTIVITY unpackDiegetic, # DIEGETIC_TYPE unpackUnsupported, # RESERVED13 - unpackUnsupported, # RESERVED14 + unpackAudioFocusCommon, # AUDIO_FOCUS_INDICATION unpackUnsupported, # RESERVED15 unpackOrientation, # PLAYBACK_DEVICE_ORIENTATION unpackOrientation, # HEAD_ORIENTATION unpackPosition, # LISTENER_POSITION unpackDAS, # DYNAMIC_AUDIO_SUPPRESSION - unpackOrientation, # AUDIO_FOCUS_DIRECTION + unpackAudioFocusCommon, # AUDIO_FOCUS_REQUEST unpackUnsupported, # PI_LATENCY unpackUnsupported, # R_ISM_ID unpackUnsupported, # R_ISM_GAIN diff --git a/tests/rtp/test_rtp.py b/tests/rtp/test_rtp.py index 342e29c8b6..d63dcb9da2 100644 --- a/tests/rtp/test_rtp.py +++ b/tests/rtp/test_rtp.py @@ -164,6 +164,7 @@ def generatePiData(startTs: int, endTs: int) -> dict: 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)) ]) + someAFocus = lambda : AUDIO_FOCUS(w=2*random.random()-1.0, x=2*random.random()-1.0, y=2*random.random()-1.0, z=2*random.random()-1.0, level=random.randint(0, 15)) for ts in range(startTs, endTs, 320): pidata = dict() @@ -172,7 +173,7 @@ def generatePiData(startTs: int, endTs: int) -> dict: pidata["DEVICE_ORIENTATION_UNCOMPENSATED"] = someOrientation() pidata["PLAYBACK_DEVICE_ORIENTATION"] = someOrientation() pidata["HEAD_ORIENTATION"] = someOrientation() - pidata["AUDIO_FOCUS_DIRECTION"] = someOrientation() + pidata["AUDIO_FOCUS_REQUEST"] = someAFocus() pidata["LISTENER_POSITION"] = somePosition() pidata["DYNAMIC_AUDIO_SUPPRESSION"] = someDAS() pidata["AUDIO_DESCRIPTION"] = [someDesc() for n in range(random.randint(1, 5))] -- GitLab From 3e3f57dee537bc1e34f73ebb403d537fe67db6f3 Mon Sep 17 00:00:00 2001 From: Kacper Sagnowski Date: Wed, 24 Sep 2025 13:33:51 +0200 Subject: [PATCH 037/100] Remove obsolete comment --- lib_dec/ivas_omasa_dec.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib_dec/ivas_omasa_dec.c b/lib_dec/ivas_omasa_dec.c index fdbca49b31..4a4e56506a 100644 --- a/lib_dec/ivas_omasa_dec.c +++ b/lib_dec/ivas_omasa_dec.c @@ -791,7 +791,6 @@ ivas_error ivas_omasa_dirac_td_binaural_jbm( { cldfbAnalysis_ts( &( p_rend_obj[n][num_cldfb_bands * slot_idx] ), Cldfb_RealBuffer, Cldfb_ImagBuffer, num_cldfb_bands, st_ivas->hSplitBinRend->splitrend.hCldfbHandles->cldfbAna[n] ); - /* note: this intentionally differs from OSBA by: no scaling by 0.5 */ #ifdef FIX_1119_SPLIT_RENDERING_VOIP ivas_CLDFB_RINGBUF_GetByIdx( st_ivas->hSplitBinRend->hMultiBinCldfbData[n], &re, &im, slot_idx - cldfb_slots ); v_add( re, Cldfb_RealBuffer, re, num_cldfb_bands ); -- GitLab From 785c7164541737334a51cd12f8e8784b4905d171 Mon Sep 17 00:00:00 2001 From: Kacper Sagnowski Date: Wed, 24 Sep 2025 15:51:20 +0200 Subject: [PATCH 038/100] Enable split rendering + VoIP tests for OSBA and OMASA --- tests/split_rendering/test_split_rendering.py | 8 ++++---- .../split_rendering/test_voip_be_splitrend_vs_binaural.py | 3 --- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/split_rendering/test_split_rendering.py b/tests/split_rendering/test_split_rendering.py index 8f4d71a406..0ff32fa290 100644 --- a/tests/split_rendering/test_split_rendering.py +++ b/tests/split_rendering/test_split_rendering.py @@ -357,7 +357,7 @@ def test_masa_external_split( """ OMASA """ -# @pytest.mark.parametrize("delay_profile", DELAY_PROFILES) # Waiting for issue #1343 to be resolved +@pytest.mark.parametrize("delay_profile", DELAY_PROFILES) @pytest.mark.parametrize("trajectory", SPLIT_REND_HR_TRAJECTORIES_TO_TEST) @pytest.mark.parametrize("render_config", RENDERER_CONFIGS_TO_TEST_OMASA) @pytest.mark.parametrize("bitrate", IVAS_BITRATES_OMASA) @@ -375,7 +375,7 @@ def test_omasa_full_chain_split( bitrate, render_config, trajectory, - delay_profile=None, + delay_profile, ): post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") @@ -437,7 +437,7 @@ def test_omasa_external_split( """ OSBA """ -# @pytest.mark.parametrize("delay_profile", DELAY_PROFILES) # Waiting for issue #1343 to be resolved +@pytest.mark.parametrize("delay_profile", DELAY_PROFILES) @pytest.mark.parametrize("trajectory", SPLIT_REND_HR_TRAJECTORIES_TO_TEST) @pytest.mark.parametrize("render_config", RENDERER_CONFIGS_TO_TEST_OSBA) @pytest.mark.parametrize("bitrate", IVAS_BITRATES_OSBA) @@ -455,7 +455,7 @@ def test_osba_full_chain_split( bitrate, render_config, trajectory, - delay_profile=None, + delay_profile, ): post_trajectory = HR_TRAJECTORY_DIR.joinpath(f"{trajectory}.csv") pre_trajectory = post_trajectory.with_stem(f"{post_trajectory.stem}_delayed") diff --git a/tests/split_rendering/test_voip_be_splitrend_vs_binaural.py b/tests/split_rendering/test_voip_be_splitrend_vs_binaural.py index fcd6cb231f..d70640b33a 100644 --- a/tests/split_rendering/test_voip_be_splitrend_vs_binaural.py +++ b/tests/split_rendering/test_voip_be_splitrend_vs_binaural.py @@ -49,9 +49,6 @@ IN_FORMATS = [ "ISM4", "FOA", "MASA2TC", - # Mixed formats to be finalised in a follow-up issue #1343 - # "OSBA_ISM3_HOA3", - # "OMASA_ISM4", ] DELAY_PROFILES = ["dly_error_profile_0.dat", "dly_error_profile_5.dat"] -- GitLab From cb0384fa2667575e09f44a90b971c5f24221a266 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Thu, 25 Sep 2025 14:07:02 +0200 Subject: [PATCH 039/100] add jobs to coverage test: - LTV tests with differing decoder framesizes - LTV jobs for renderer - Split rendering test - VoIP BE tests - BE vs EXT test - LC3plus wrapper unit test --- .gitlab-ci.yml | 54 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7a02bd69e5..bdcd7eb104 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2203,31 +2203,69 @@ coverage-test-on-main-scheduled: - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/print-common-info.sh - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/update-ltv-repo.sh - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/copy-ltv-files-to-testv-dir.sh - - make GCOV=1 -j + # compile with coverage enabled + - cmake -B cmake-build -G "Unix Makefiles" -DCOPY_EXECUTABLES_FROM_BUILD_DIR=true -DGCOV=ON + - cmake --build cmake-build -- -j - cp IVAS_rend IVAS_rend_ref # Copy exec to be able to run renderer script # -- Run short test vector suite to check coverage of that first + # codec smoke test + - bash ci/smoke_test.sh coverage + # default renderer framesize is 20ms - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v -n auto --update_ref 1 --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec - + # for different renderer framesizes, only the decoder needs to be tested, so use --decoder_only # need to ignore non-zero exit codes as limiter is active and thus the different framesiszes will not be BE in all cases - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only || true - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only || true + # run renderer smoke test - needed for REFVEC tests + - python3 -m pytest -q -n auto tests/renderer/test_renderer.py + # run renderer test only with REF creation stage - no CUT needed since executables are identical - python3 -m pytest -q -n auto tests/renderer/test_renderer.py --create_ref - - python3 -m pytest -q -n auto tests/renderer/test_renderer.py --create_cut - - lcov -c -d obj -o coverage_stv.info # extract coverage of short test vectors here - # remove apps and lib_util files from coverage + # run split rendering smoke test and REF creation + - python3 -m pytest -n auto tests/split_rendering/test_split_rendering.py + - python3 -m pytest -n auto tests/split_rendering/test_split_rendering.py --create_ref + # VoIP BE test + - python3 -m pytest tests/test_be_for_jbm_neutral_dly_profile.py + # split rendering VoIP BE test + - python3 -m pytest -n auto tests/split_rendering/test_voip_be_splitrend_vs_binaural.py + # TODO first frame SID - this script runs a `make clean` and needs to be checked/adapted + # - bash ci/run-first-frame-is-sid-test.sh + # TODO consider HRTF binary loading test, or probably already covered by PRM files + # - python3 -m pytest tests/hrtf_binary_loading + # run BE vs EXT test + - python3 -m pytest tests/test_be_for_ext_outputs.py + # run LC3plus wrapper unit test + - scripts/split_rendering/lc3plus_float/ivas_lc3plus_unit_test + # TODO consider including check-clipping, but this scales ALL testv files. could be added at end of this job + + # -- Extract coverage of short testvectors + - lcov -c -d cmake-build -o coverage_stv.info + # -- Remove apps and lib_util files from coverage - lcov -r coverage_stv.info "*apps*" -o coverage_stv.info - lcov -r coverage_stv.info "*lib_util*" -o coverage_stv.info # -- Add extended coverage of EVS test and long test vectors - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto --update_ref 1 --param_file scripts/config/self_test_evs.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec + # codec smoke test - bash ci/smoke_test.sh coverage + # default renderer framesize is 20ms - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto --update_ref 1 --param_file scripts/config/self_test_ltv.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec + # for different renderer framesizes, only the decoder needs to be tested, so use --decoder_only + # need to ignore non-zero exit codes as limiter is active and thus the different framesiszes will not be BE in all cases + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto --param_file scripts/config/self_test_ltv.prm -dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 5 || true + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto --param_file scripts/config/self_test_ltv.prm -dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 10 || true - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto --update_ref 1 --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec --use_ltv - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only --use_ltv || true - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only --use_ltv || true - - python3 -m pytest tests/test_be_for_jbm_neutral_dly_profile.py - - lcov -c -d obj -o coverage.info + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only --use_ltv || true + # run renderer smoke test - needed for REFVEC tests + - python3 -m pytest -q -n auto tests/renderer/test_renderer.py --use_ltv + # run renderer test only with REF creation stage - no CUT needed since executables are identical + - python3 -m pytest -q -n auto tests/renderer/test_renderer.py --use_ltv --create_cut + # split rendering test doesn't support LTV - skipped here + # VoIP BE tests don't support LTV - skipped here + + # -- Extract accumulated coverage of STV + EVS + LTV + - lcov -c -d cmake-build -o coverage.info # remove apps and lib_util files from coverage - lcov -r coverage.info "*apps*" -o coverage.info - lcov -r coverage.info "*lib_util*" -o coverage.info -- GitLab From 2a972eabcfce5c394b64cfa9aef12d2ebab9547c Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Thu, 25 Sep 2025 16:14:06 +0300 Subject: [PATCH 040/100] PI tests for audio focus --- apps/decoder.c | 22 ++++++++++++++----- tests/rtp/ivasrtp.py | 42 +++++++++++++++++++++++++---------- tests/rtp/test_rtp.py | 51 ++++++++++++++++++++++++++++++------------- 3 files changed, 83 insertions(+), 32 deletions(-) diff --git a/apps/decoder.c b/apps/decoder.c index df7dcf5168..da0fdd2fbd 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -328,15 +328,20 @@ static void IVAS_RTP_LogPiData( FILE *f_piDataOut, PIDATA_TS *piData, uint32_t n break; case IVAS_PI_AUDIO_FOCUS_INDICATION: { - fprintf( f_piDataOut, "{\n" ); + fprintf( f_piDataOut, "{" ); if ( cur->data.focusIndication.availDirection ) { - fprintf( f_piDataOut, "\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\t", + fprintf( f_piDataOut, "\n\t\t\t\"direction\": {\n" ); + fprintf( f_piDataOut, "\t\t\t\t\t\t\"w\": %f,\n\t\t\t\t\t\t\"x\": %f,\n\t\t\t\t\t\t\"y\": %f,\n\t\t\t\t\t\t\"z\": %f \n\t\t\t}", cur->data.focusIndication.direction.w, cur->data.focusIndication.direction.x, cur->data.focusIndication.direction.y, cur->data.focusIndication.direction.z ); + if ( cur->data.focusIndication.availLevel ) + { + fprintf( f_piDataOut, "," ); + } } if ( cur->data.focusIndication.availLevel ) { - fprintf( f_piDataOut, "\t\t\t\"level\": %d", cur->data.focusIndication.flvl ); + fprintf( f_piDataOut, "\n\t\t\t\"level\": %d", cur->data.focusIndication.flvl ); } fprintf( f_piDataOut, "\n\t\t}" ); } @@ -355,15 +360,20 @@ static void IVAS_RTP_LogPiData( FILE *f_piDataOut, PIDATA_TS *piData, uint32_t n case IVAS_PI_RESERVED13: case IVAS_PI_AUDIO_FOCUS_REQUEST: { - fprintf( f_piDataOut, "{\n" ); + fprintf( f_piDataOut, "{" ); if ( cur->data.focusRequest.availDirection ) { - fprintf( f_piDataOut, "\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\t", + fprintf( f_piDataOut, "\n\t\t\t\"direction\": {\n" ); + fprintf( f_piDataOut, "\t\t\t\t\t\t\"w\": %f,\n\t\t\t\t\t\t\"x\": %f,\n\t\t\t\t\t\t\"y\": %f,\n\t\t\t\t\t\t\"z\": %f \n\t\t\t}", cur->data.focusRequest.direction.w, cur->data.focusRequest.direction.x, cur->data.focusRequest.direction.y, cur->data.focusRequest.direction.z ); + if ( cur->data.focusRequest.availLevel ) + { + fprintf( f_piDataOut, "," ); + } } if ( cur->data.focusRequest.availLevel ) { - fprintf( f_piDataOut, "\t\t\t\"level\": %d", cur->data.focusRequest.flvl ); + fprintf( f_piDataOut, "\n\t\t\t\"level\": %d", cur->data.focusRequest.flvl ); } fprintf( f_piDataOut, "\n\t\t}" ); } diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py index 8a2fd0f87d..d5b256cf42 100644 --- a/tests/rtp/ivasrtp.py +++ b/tests/rtp/ivasrtp.py @@ -42,7 +42,7 @@ import json import base64 import argparse from pathlib import Path -from typing import cast +from typing import cast, Optional NO_REQ="NO_REQ" @@ -225,7 +225,6 @@ class AUDIO_FOCUS_LEVEL(int, Enum): AUDIO_FOCUS_LEVEL_DEFAULT = 14 AUDIO_FOCUS_LEVEL_NO_PREFERENCE = 15 - @dataclass class RTPHDR: version: int = 2 @@ -363,11 +362,8 @@ class ACOUSTIC_ENVIRONMENT: @dataclass class AUDIO_FOCUS: - w: float = 0.0 - x: float = 0.0 - y: float = 0.0 - z: float = 0.0 - level: AUDIO_FOCUS_LEVEL = AUDIO_FOCUS_LEVEL.AUDIO_FOCUS_LEVEL_DEFAULT + direction: Optional[ORIENTATION] = None + level: Optional[AUDIO_FOCUS_LEVEL] = None @dataclass class PIDATA: @@ -599,6 +595,30 @@ def packAcousticEnv(bitstrm: BitStream, data: any): absCoeff = mapNearestIndex(absorptionCoeffValues, aenv.abscoeff[n]) bitstrm.append(f'uint:2={absCoeff}') +def unpackAudioFocus(bitstrm: ConstBitStream, piSize: int) -> AUDIO_FOCUS: + assert piSize == 1 or piSize == 8 or piSize == 9, "Incorrect PI Data Size for AUDIO_FOCUS" + direction = None + level = None + if piSize == 1: + level = bitstrm.read(4).uint + _ = bitstrm.read(4) + else: + direction = unpackOrientation(bitstrm, 8) + if piSize == 9: + level = bitstrm.read(4).uint + _ = bitstrm.read(4) + + return AUDIO_FOCUS(direction=direction, level=level) + +def packAudioFocus(bitstrm: BitStream, data: any): + assert type(data) == AUDIO_FOCUS, "Diegetic type PI Data expects a data of type AUDIO_FOCUS" + auFocus = cast(AUDIO_FOCUS, data) + if auFocus.direction is not None: + packOrientations(bitstrm, [auFocus.direction]) + if auFocus.level is not None: + bitstrm.append(f'uint:4={auFocus.level}') + bitstrm.append(f'uint:4=0') + PIDataUnpacker = [ unpackOrientation, # SCENE_ORIENTATION, @@ -615,13 +635,13 @@ PIDataUnpacker = [ unpackUnsupported, # ISM_DIRECTIVITY unpackDiegetic, # DIEGETIC_TYPE unpackUnsupported, # RESERVED13 - unpackAudioFocusCommon, # AUDIO_FOCUS_INDICATION + unpackAudioFocus, # AUDIO_FOCUS_INDICATION unpackUnsupported, # RESERVED15 unpackOrientation, # PLAYBACK_DEVICE_ORIENTATION unpackOrientation, # HEAD_ORIENTATION unpackPosition, # LISTENER_POSITION unpackDAS, # DYNAMIC_AUDIO_SUPPRESSION - unpackAudioFocusCommon, # AUDIO_FOCUS_REQUEST + unpackAudioFocus, # AUDIO_FOCUS_REQUEST unpackUnsupported, # PI_LATENCY unpackUnsupported, # R_ISM_ID unpackUnsupported, # R_ISM_GAIN @@ -650,13 +670,13 @@ PIDataPacker = [ packUnsupported, # ISM_DIRECTIVITY packDiegetic, # DIEGETIC_TYPE packUnsupported, # RESERVED13 - packUnsupported, # RESERVED14 + packAudioFocus, # AUDIO_FOCUS_INDICATION packUnsupported, # RESERVED15 packOrientation, # PLAYBACK_DEVICE_ORIENTATION packOrientation, # HEAD_ORIENTATION packPosition, # LISTENER_POSITION packDAS, # DYNAMIC_AUDIO_SUPPRESSION - packOrientation, # AUDIO_FOCUS_DIRECTION + packAudioFocus, # AUDIO_FOCUS_DIRECTION packUnsupported, # PI_LATENCY packUnsupported, # R_ISM_ID packUnsupported, # R_ISM_GAIN diff --git a/tests/rtp/test_rtp.py b/tests/rtp/test_rtp.py index d63dcb9da2..1ed8340ac5 100644 --- a/tests/rtp/test_rtp.py +++ b/tests/rtp/test_rtp.py @@ -102,9 +102,14 @@ def test_rtp_bitstream_evs ( ) -@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("bitrate", [24400, 80000, 512000]) +#@pytest.mark.parametrize("bandwidth", ["WB", "SWB", "FB"]) +#@pytest.mark.parametrize("format", ["STEREO", "SBA", "MC", "MASA"]) +#@pytest.mark.parametrize("framesPerPacket", [8]) + +@pytest.mark.parametrize("bitrate", [24400]) +@pytest.mark.parametrize("bandwidth", ["SWB"]) +@pytest.mark.parametrize("format", ["MASA"]) @pytest.mark.parametrize("framesPerPacket", [8]) def test_rtp_bitstream_ivas_nodtx ( test_info, @@ -164,21 +169,24 @@ def generatePiData(startTs: int, endTs: int) -> dict: 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)) ]) - someAFocus = lambda : AUDIO_FOCUS(w=2*random.random()-1.0, x=2*random.random()-1.0, y=2*random.random()-1.0, z=2*random.random()-1.0, level=random.randint(0, 15)) + someAuFocusDirLvl = lambda : AUDIO_FOCUS(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), level=AUDIO_FOCUS_LEVEL(random.randint(0, 15))) + someAuFocusDir = lambda : AUDIO_FOCUS(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)) + someAuFocusLvl = lambda : AUDIO_FOCUS(level=AUDIO_FOCUS_LEVEL(random.randint(0, 15))) + someAuFocusList = [someAuFocusDirLvl, someAuFocusDir, someAuFocusLvl] 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_REQUEST"] = someAFocus() - 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)) + #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_REQUEST"] = random.choice(someAuFocusList)() + #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 @@ -225,6 +233,17 @@ def isEqualAcousticEnv(ref: ACOUSTIC_ENVIRONMENT, dut: ACOUSTIC_ENVIRONMENT): for r, d in zip(ref.rt60, dut.rt60): assert r == d, f"Acoustic Env PI Data mismatch in rt60 {r} != {d}" +def isEqualAudioFocus(ref: AUDIO_FOCUS, dut: AUDIO_FOCUS): + if ref.direction is not None or dut.direction is not None: + assert ref.direction is not None, "Audio Focus PI Data missing direction" + assert dut.direction is not None, "Audio Focus PI Data missing direction" + if ref.direction is not None and dut.direction is not None: + assert abs(ref.direction.w - dut.direction.w) < 0.0001, "Audio Focus PI Data mismatch in direction w" + assert abs(ref.direction.x - dut.direction.x) < 0.0001, "Audio Focus PI Data mismatch in direction x" + assert abs(ref.direction.y - dut.direction.y) < 0.0001, "Audio Focus PI Data mismatch in direction y" + assert abs(ref.direction.z - dut.direction.z) < 0.0001, "Audio Focus PI Data mismatch in direction z" + assert ref.level == dut.level, "Audio Focus PI Data mismatch in level" + class CSVREADER: def __init__(self, csvFile: Path): self.rIdx = 0 @@ -448,6 +467,8 @@ def run_rtp_bitstream_tests ( isEqualDiegetic(DIEGETIC_TYPE(**decoded), data) elif type(generatedPIData[ts][pitype]) == ACOUSTIC_ENVIRONMENT: isEqualAcousticEnv(ACOUSTIC_ENVIRONMENT(**decoded), data) + if type(generatedPIData[ts][pitype]) == AUDIO_FOCUS: + isEqualAudioFocus(AUDIO_FOCUS(**decoded), data) elif type(generatedPIData[ts][pitype]) == list: for r, d in zip(generatedPIData[ts][pitype], decodedPiData[ts][pitype]): isEqualAD(AUDIO_DESCRIPTION(**d), r) -- GitLab From 5646836cf40059c99d00d98d399637fbd812a4e0 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Thu, 25 Sep 2025 16:11:09 +0200 Subject: [PATCH 041/100] increase coverage test timeout to 12 hours --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bdcd7eb104..c301f1d624 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2198,7 +2198,7 @@ coverage-test-on-main-scheduled: rules: # only run in scheduled pipeline that passes this env vars - if: $COVERAGE_TEST - timeout: 3 hours + timeout: 12 hours script: - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/print-common-info.sh - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/update-ltv-repo.sh -- GitLab From 75d0d942b6849504d618454304a1e6d20bc8f87f Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Fri, 26 Sep 2025 08:58:20 +0200 Subject: [PATCH 042/100] [fix] missing executables for coverage job --- .gitlab-ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c301f1d624..c37375d3df 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2206,7 +2206,11 @@ coverage-test-on-main-scheduled: # compile with coverage enabled - cmake -B cmake-build -G "Unix Makefiles" -DCOPY_EXECUTABLES_FROM_BUILD_DIR=true -DGCOV=ON - cmake --build cmake-build -- -j - - cp IVAS_rend IVAS_rend_ref # Copy exec to be able to run renderer script + # copy executables to ref to be able to run some --create_ref tests + - cp IVAS_cod IVAS_cod_ref + - cp IVAS_dec IVAS_dec_ref + - cp IVAS_rend IVAS_rend_ref + - cp ISAR_post_rend ISAR_post_rend_ref # -- Run short test vector suite to check coverage of that first # codec smoke test -- GitLab From d7adc329b9383329c176abf91667484bc9d42f4a Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Fri, 26 Sep 2025 10:14:44 +0200 Subject: [PATCH 043/100] [fix] missing short testvectors for check BE to EXT --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c37375d3df..a54e8e6666 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2237,6 +2237,7 @@ coverage-test-on-main-scheduled: # TODO consider HRTF binary loading test, or probably already covered by PRM files # - python3 -m pytest tests/hrtf_binary_loading # run BE vs EXT test + - python3 tests/create_short_testvectors.py --cut_len 1.0 - python3 -m pytest tests/test_be_for_ext_outputs.py # run LC3plus wrapper unit test - scripts/split_rendering/lc3plus_float/ivas_lc3plus_unit_test -- GitLab From 2bf48e36133f091379092dc85e83a4b57f7a3240 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Fri, 26 Sep 2025 11:58:53 +0300 Subject: [PATCH 044/100] Fix audio focus tests --- tests/rtp/test_rtp.py | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/tests/rtp/test_rtp.py b/tests/rtp/test_rtp.py index 1ed8340ac5..4e394ee478 100644 --- a/tests/rtp/test_rtp.py +++ b/tests/rtp/test_rtp.py @@ -102,14 +102,9 @@ def test_rtp_bitstream_evs ( ) -#@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]) - -@pytest.mark.parametrize("bitrate", [24400]) -@pytest.mark.parametrize("bandwidth", ["SWB"]) -@pytest.mark.parametrize("format", ["MASA"]) +@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, @@ -176,17 +171,17 @@ def generatePiData(startTs: int, endTs: int) -> dict: 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["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_REQUEST"] = random.choice(someAuFocusList)() - #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)) + 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 @@ -238,10 +233,10 @@ def isEqualAudioFocus(ref: AUDIO_FOCUS, dut: AUDIO_FOCUS): assert ref.direction is not None, "Audio Focus PI Data missing direction" assert dut.direction is not None, "Audio Focus PI Data missing direction" if ref.direction is not None and dut.direction is not None: - assert abs(ref.direction.w - dut.direction.w) < 0.0001, "Audio Focus PI Data mismatch in direction w" - assert abs(ref.direction.x - dut.direction.x) < 0.0001, "Audio Focus PI Data mismatch in direction x" - assert abs(ref.direction.y - dut.direction.y) < 0.0001, "Audio Focus PI Data mismatch in direction y" - assert abs(ref.direction.z - dut.direction.z) < 0.0001, "Audio Focus PI Data mismatch in direction z" + assert abs(ref.direction['w'] - dut.direction.w) < 0.0001, "Audio Focus PI Data mismatch in direction w" + assert abs(ref.direction['x'] - dut.direction.x) < 0.0001, "Audio Focus PI Data mismatch in direction x" + assert abs(ref.direction['y'] - dut.direction.y) < 0.0001, "Audio Focus PI Data mismatch in direction y" + assert abs(ref.direction['z'] - dut.direction.z) < 0.0001, "Audio Focus PI Data mismatch in direction z" assert ref.level == dut.level, "Audio Focus PI Data mismatch in level" class CSVREADER: @@ -467,7 +462,7 @@ def run_rtp_bitstream_tests ( isEqualDiegetic(DIEGETIC_TYPE(**decoded), data) elif type(generatedPIData[ts][pitype]) == ACOUSTIC_ENVIRONMENT: isEqualAcousticEnv(ACOUSTIC_ENVIRONMENT(**decoded), data) - if type(generatedPIData[ts][pitype]) == AUDIO_FOCUS: + elif type(generatedPIData[ts][pitype]) == AUDIO_FOCUS: isEqualAudioFocus(AUDIO_FOCUS(**decoded), data) elif type(generatedPIData[ts][pitype]) == list: for r, d in zip(generatedPIData[ts][pitype], decodedPiData[ts][pitype]): -- GitLab From fc1241075fd1c3b4fd50d07172e19508301c9a0c Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Fri, 26 Sep 2025 12:21:35 +0300 Subject: [PATCH 045/100] Add flags --- lib_util/ivas_rtp_pi_data.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index 24ecabe6e8..c939c4539c 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -659,7 +659,11 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { packUnsupportedData, /* DIEGETIC_TYPE */ #endif packUnsupportedData, /* RESERVED13 */ +#ifdef RTP_S4_251135_CR26253_0016_REV1 packAudioFocusCommon,/* AUDIO_FOCUS_INDICATION */ +#else + packUnsupportedData, /* AUDIO_FOCUS_INDICATION */ +#endif packUnsupportedData, /* RESERVED15 */ #ifdef RTP_S4_251135_CR26253_0016_REV1 packOrientation, /* PLAYBACK_DEVICE_ORIENTATION */ @@ -714,7 +718,11 @@ static const UNPACK_PI_FN unpackPiDataFuntions[IVAS_PI_MAX_ID] = { unpackUnsupportedData, /* DIEGETIC_TYPE */ #endif unpackUnsupportedData, /* RESERVED13 */ +#ifdef RTP_S4_251135_CR26253_0016_REV1 unpackAudioFocusCommon,/* AUDIO_FOCUS_INDICATION */ +#else + unpackUnsupportedData, /* AUDIO_FOCUS_INDICATION */ +#endif unpackUnsupportedData, /* RESERVED15 */ #ifdef RTP_S4_251135_CR26253_0016_REV1 unpackOrientation, /* PLAYBACK_DEVICE_ORIENTATION */ -- GitLab From 6e12182e714412bb483b5c5b247a6883ef6d9b3d Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Fri, 26 Sep 2025 14:14:49 +0200 Subject: [PATCH 046/100] [fix] renderer LTV test accidentally using CUT mode --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a54e8e6666..77603623b4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2265,7 +2265,7 @@ coverage-test-on-main-scheduled: # run renderer smoke test - needed for REFVEC tests - python3 -m pytest -q -n auto tests/renderer/test_renderer.py --use_ltv # run renderer test only with REF creation stage - no CUT needed since executables are identical - - python3 -m pytest -q -n auto tests/renderer/test_renderer.py --use_ltv --create_cut + - python3 -m pytest -q -n auto tests/renderer/test_renderer.py --use_ltv --create_ref # split rendering test doesn't support LTV - skipped here # VoIP BE tests don't support LTV - skipped here -- GitLab From a9cc66bc602f952a946854d6ca72e5494c62462f Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Mon, 29 Sep 2025 10:43:25 +0300 Subject: [PATCH 047/100] Code review, add more descriptive numbering and comments, also disable headtracking with ext orientations --- lib_com/options.h | 2 +- lib_dec/lib_dec.c | 2 +- lib_rend/ivas_rotation.c | 41 +++++++++++++++++++++++++++++++++++++-- lib_rend/ivas_stat_rend.h | 2 +- tests/rtp/ivasrtp.py | 2 +- 5 files changed, 43 insertions(+), 6 deletions(-) diff --git a/lib_com/options.h b/lib_com/options.h index 58ca69d570..b1f5965027 100644 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -158,7 +158,7 @@ /* ################## Start DEVELOPMENT switches ######################### */ -//#define RTP_S4_251135_CR26253_0016_REV1 /* RTP Pack/Unpack API corresponding to CR 26253 */ +#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 ################################# */ diff --git a/lib_dec/lib_dec.c b/lib_dec/lib_dec.c index b46def86e4..c3b7e69a82 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -3843,7 +3843,7 @@ void IVAS_DEC_setDiegeticInputPI( if ( hIvasDec->st_ivas->hCombinedOrientationData != NULL ) { int8_t i; - for ( i = 0; i < 4; i++ ) + for ( i = 0; i < (1 + IVAS_MAX_NUM_OBJECTS); i++ ) { hIvasDec->st_ivas->hCombinedOrientationData->isDiegeticInputPI[i] = diegeticPIValues[i]; } diff --git a/lib_rend/ivas_rotation.c b/lib_rend/ivas_rotation.c index 05fb92cd18..8b62d7a111 100644 --- a/lib_rend/ivas_rotation.c +++ b/lib_rend/ivas_rotation.c @@ -864,7 +864,7 @@ ivas_error ivas_combined_orientation_open( ( *hCombinedOrientationData )->cur_subframe_samples_rendered = 0; #ifdef RTP_S4_251135_CR26253_0016_REV1 - for ( i = 0; i < 4; i++ ) + for ( i = 0; i < (1 + IVAS_MAX_NUM_OBJECTS); i++ ) { ( *hCombinedOrientationData )->isDiegeticInputPI[i] = true; } @@ -1030,8 +1030,8 @@ ivas_error combine_external_and_head_orientations( } else if ( hExtOrientationData == NULL && headRotQuaternions != NULL ) { - /* Head rotation only */ #ifdef RTP_S4_251135_CR26253_0016_REV1 + /* Disable head rotation if diegetic PI data indicating non-diegetic audio is received */ if ( hCombinedOrientationData->isDiegeticInputPISet && !hCombinedOrientationData->isDiegeticInputPI[0] && !hCombinedOrientationData->isDiegeticInputPI[1] && !hCombinedOrientationData->isDiegeticInputPI[2] && !hCombinedOrientationData->isDiegeticInputPI[3] && !hCombinedOrientationData->isDiegeticInputPI[4] ) { for ( i = 0; i < hCombinedOrientationData->num_subframes; i++ ) @@ -1041,12 +1041,14 @@ ivas_error combine_external_and_head_orientations( } else { + /* Head rotation only */ for ( i = 0; i < hCombinedOrientationData->num_subframes; i++ ) { hCombinedOrientationData->Quaternions[i] = headRotQuaternions[i]; } } #else + /* Head rotation only */ for ( i = 0; i < hCombinedOrientationData->num_subframes; i++ ) { hCombinedOrientationData->Quaternions[i] = headRotQuaternions[i]; @@ -1128,6 +1130,40 @@ ivas_error combine_external_and_head_orientations( hCombinedOrientationData->Quaternion_frozen_head = identity; hCombinedOrientationData->isHeadRotationFrozen = 0; } +#ifdef RTP_S4_251135_CR26253_0016_REV1 + /* Disable head rotation if diegetic PI data indicating non-diegetic audio is received */ + if ( hCombinedOrientationData->isDiegeticInputPISet && !hCombinedOrientationData->isDiegeticInputPI[0] && !hCombinedOrientationData->isDiegeticInputPI[1] && !hCombinedOrientationData->isDiegeticInputPI[2] && !hCombinedOrientationData->isDiegeticInputPI[3] && !hCombinedOrientationData->isDiegeticInputPI[4] ) + { + continue; + } + else + { + /* Use the most recent head rotation */ + if ( hExtOrientationData->enableHeadRotation[i] == 1 ) + { + if ( hExtOrientationData->enableExternalOrientation[i] > 0 ) + { + QuaternionProduct( hCombinedOrientationData->Quaternions[i], headRotQuaternions[i], &hCombinedOrientationData->Quaternions[i] ); + } + else + { + hCombinedOrientationData->Quaternions[i] = headRotQuaternions[i]; + } + } + /* Use the freezed head rotation */ + else if ( hExtOrientationData->enableHeadRotation[i] == 2 ) + { + if ( hExtOrientationData->enableExternalOrientation[i] > 0 ) + { + QuaternionProduct( hCombinedOrientationData->Quaternions[i], hCombinedOrientationData->Quaternion_frozen_head, &hCombinedOrientationData->Quaternions[i] ); + } + else + { + hCombinedOrientationData->Quaternions[i] = hCombinedOrientationData->Quaternion_frozen_head; + } + } + } +#else /* Use the most recent head rotation */ if ( hExtOrientationData->enableHeadRotation[i] == 1 ) { @@ -1152,6 +1188,7 @@ ivas_error combine_external_and_head_orientations( hCombinedOrientationData->Quaternions[i] = hCombinedOrientationData->Quaternion_frozen_head; } } +#endif /* Reset the combined orientations to identity */ if ( hExtOrientationData->enableHeadRotation[i] == 0 && hExtOrientationData->enableExternalOrientation[i] == 0 ) diff --git a/lib_rend/ivas_stat_rend.h b/lib_rend/ivas_stat_rend.h index fb8bf44b83..3e6270e229 100644 --- a/lib_rend/ivas_stat_rend.h +++ b/lib_rend/ivas_stat_rend.h @@ -711,7 +711,7 @@ typedef struct ivas_combined_orientation_struct int16_t subframe_idx_start; int16_t cur_subframe_samples_rendered_start; #ifdef RTP_S4_251135_CR26253_0016_REV1 - bool isDiegeticInputPI[5]; + bool isDiegeticInputPI[1 + IVAS_MAX_NUM_OBJECTS]; bool isDiegeticInputPISet; #endif } COMBINED_ORIENTATION_DATA, *COMBINED_ORIENTATION_HANDLE; diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py index d5b256cf42..1eec45fdce 100644 --- a/tests/rtp/ivasrtp.py +++ b/tests/rtp/ivasrtp.py @@ -611,7 +611,7 @@ def unpackAudioFocus(bitstrm: ConstBitStream, piSize: int) -> AUDIO_FOCUS: return AUDIO_FOCUS(direction=direction, level=level) def packAudioFocus(bitstrm: BitStream, data: any): - assert type(data) == AUDIO_FOCUS, "Diegetic type PI Data expects a data of type AUDIO_FOCUS" + assert type(data) == AUDIO_FOCUS, "Audio focus PI Data expects a data of type AUDIO_FOCUS" auFocus = cast(AUDIO_FOCUS, data) if auFocus.direction is not None: packOrientations(bitstrm, [auFocus.direction]) -- GitLab From 3b2049c97356bd3c5cf04e8bcaf42d76dc3ceb31 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Mon, 29 Sep 2025 11:59:00 +0300 Subject: [PATCH 048/100] Fix object editing issues 1399 and 1400 --- lib_com/options.h | 2 +- lib_dec/ivas_jbm_dec.c | 14 ++++++++++++++ lib_dec/ivas_omasa_dec.c | 10 +++++++++- lib_rend/ivas_dirac_dec_binaural_functions.c | 7 +++++++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/lib_com/options.h b/lib_com/options.h index 8b761e69b2..22910491a5 100644 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -176,7 +176,7 @@ #define NONBE_1244_FIX_SWB_BWE_MEMORY /* VA: issue 1244: fix to SWB BWE memory in case of switching from FB coding - pending a review by Huawei */ #define NONBE_1122_KEEP_EVS_MODE_UNCHANGED /* FhG: Disables fix for issue 1122 in EVS mode to keep BE tests green. This switch should be removed once the 1122 fix is added to EVS via a CR. */ #define NONBE_1328_FIX_NON_LINEARITY /* VA: Fix possible issue when computing bwe_exc_extended and previous frame were almost 0 */ - +#define NONBE_1399_1400_FIX_OBJ_EDIT_ISSUES /* Nokia: Fix for issues 1399: obj edit broken with MC/SBA output in VOIP, and 1400: negative energy estimate used for gaining. */ /* ##################### End NON-BE switches ########################### */ diff --git a/lib_dec/ivas_jbm_dec.c b/lib_dec/ivas_jbm_dec.c index 5b8668c6d3..576967b25d 100644 --- a/lib_dec/ivas_jbm_dec.c +++ b/lib_dec/ivas_jbm_dec.c @@ -2907,6 +2907,20 @@ void ivas_dec_prepare_renderer( nchan_transport_ism = st_ivas->nchan_ism; } +#ifdef NONBE_1399_1400_FIX_OBJ_EDIT_ISSUES + if ( st_ivas->hDecoderConfig->Opt_tsm && st_ivas->ism_mode == ISM_MASA_MODE_DISC && st_ivas->renderer_type == RENDERER_DIRAC) + { + /* Gain MASA part, if edited */ + if ( st_ivas->hMasaIsmData->masa_gain_is_edited ) + { + for ( int16_t ch = 0; ch < 2; ch++ ) + { + v_multc( st_ivas->hTcBuffer->tc[ch], st_ivas->hMasaIsmData->gain_masa_edited, st_ivas->hTcBuffer->tc[ch], st_ivas->hTcBuffer->n_samples_available ); + } + } + } +#endif + for ( n = 0; n < nchan_transport_ism; n++ ) { if ( st_ivas->ism_mode == ISM_MASA_MODE_DISC && st_ivas->renderer_type == RENDERER_BINAURAL_PARAMETRIC ) diff --git a/lib_dec/ivas_omasa_dec.c b/lib_dec/ivas_omasa_dec.c index fdbca49b31..346c6f591d 100644 --- a/lib_dec/ivas_omasa_dec.c +++ b/lib_dec/ivas_omasa_dec.c @@ -680,7 +680,11 @@ void ivas_omasa_dirac_rend_jbm( { mvr2r( &output_f[CPE_CHANNELS][st_ivas->hTcBuffer->n_samples_rendered], data_separated_objects[0], *nSamplesRendered ); +#ifdef NONBE_1399_1400_FIX_OBJ_EDIT_ISSUES + if ( st_ivas->ism_mode == ISM_MASA_MODE_PARAM_ONE_OBJ ) +#else if ( !st_ivas->hDecoderConfig->Opt_tsm && st_ivas->ism_mode == ISM_MASA_MODE_PARAM_ONE_OBJ ) +#endif { /* Gain separated object, if edited */ for ( n = 0; n < st_ivas->nchan_ism; n++ ) @@ -699,13 +703,17 @@ void ivas_omasa_dirac_rend_jbm( mvr2r( &output_f[n + CPE_CHANNELS][st_ivas->hTcBuffer->n_samples_rendered], data_separated_objects[n], *nSamplesRendered ); /* Gain discrete objects, if edited */ +#ifdef NONBE_1399_1400_FIX_OBJ_EDIT_ISSUES + if ( st_ivas->hMasaIsmData->ism_gain_is_edited[n] ) +#else if ( !st_ivas->hDecoderConfig->Opt_tsm && st_ivas->hMasaIsmData->ism_gain_is_edited[n] ) +#endif { v_multc( data_separated_objects[n], st_ivas->hMasaIsmData->gain_ism_edited[n], data_separated_objects[n], *nSamplesRendered ); } } - /* Gain MASA part, if edited */ + /* Gain MASA part, if edited in G192. MASA gaining with VOIP is done in ivas_dec_prepare_renderer() */ if ( !st_ivas->hDecoderConfig->Opt_tsm && st_ivas->hMasaIsmData->masa_gain_is_edited ) { for ( int16_t ch = 0; ch < 2; ch++ ) diff --git a/lib_rend/ivas_dirac_dec_binaural_functions.c b/lib_rend/ivas_dirac_dec_binaural_functions.c index 9e40f63ee8..684180e239 100644 --- a/lib_rend/ivas_dirac_dec_binaural_functions.c +++ b/lib_rend/ivas_dirac_dec_binaural_functions.c @@ -3112,6 +3112,13 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( } /* Limit target energies to non-negative values */ +#ifdef NONBE_1399_1400_FIX_OBJ_EDIT_ISSUES + for ( ch = 0; ch < 2; ch++ ) + { + totalTargetEneCh[ch] = max( totalTargetEneCh[ch], 0.0f ); + } +#endif + /* due to rounding, the sum may exceed 1.0f ever so slightly, so clip it */ ratioAccOrig = min( ratioAccOrig, 1.0f ); if ( masaGainEdited ) -- GitLab From c051fb9718adfc1e46cb412d9e9aee778de554ce Mon Sep 17 00:00:00 2001 From: lintervo Date: Mon, 29 Sep 2025 13:53:14 +0300 Subject: [PATCH 049/100] Clang formatting --- lib_dec/ivas_jbm_dec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_dec/ivas_jbm_dec.c b/lib_dec/ivas_jbm_dec.c index 576967b25d..f8240a3328 100644 --- a/lib_dec/ivas_jbm_dec.c +++ b/lib_dec/ivas_jbm_dec.c @@ -2908,7 +2908,7 @@ void ivas_dec_prepare_renderer( } #ifdef NONBE_1399_1400_FIX_OBJ_EDIT_ISSUES - if ( st_ivas->hDecoderConfig->Opt_tsm && st_ivas->ism_mode == ISM_MASA_MODE_DISC && st_ivas->renderer_type == RENDERER_DIRAC) + if ( st_ivas->hDecoderConfig->Opt_tsm && st_ivas->ism_mode == ISM_MASA_MODE_DISC && st_ivas->renderer_type == RENDERER_DIRAC ) { /* Gain MASA part, if edited */ if ( st_ivas->hMasaIsmData->masa_gain_is_edited ) -- GitLab From 84654f5a1bc87af415974c875a1e7d302a6a9596 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Mon, 29 Sep 2025 14:43:29 +0200 Subject: [PATCH 050/100] try debugging failed commands --- .gitlab-ci.yml | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 77603623b4..1b67ef29db 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2200,6 +2200,8 @@ coverage-test-on-main-scheduled: - if: $COVERAGE_TEST timeout: 12 hours script: + - set -e + - "trap 'echo \"Command failed at line $LINENO: $BASH_COMMAND\"' ERR" - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/print-common-info.sh - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/update-ltv-repo.sh - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/copy-ltv-files-to-testv-dir.sh @@ -2216,29 +2218,29 @@ coverage-test-on-main-scheduled: # codec smoke test - bash ci/smoke_test.sh coverage # default renderer framesize is 20ms - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v -n auto --update_ref 1 --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v -n auto -q --tb=no --update_ref 1 --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec # for different renderer framesizes, only the decoder needs to be tested, so use --decoder_only # need to ignore non-zero exit codes as limiter is active and thus the different framesiszes will not be BE in all cases - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only || true - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only || true + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only || true + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only || true # run renderer smoke test - needed for REFVEC tests - - python3 -m pytest -q -n auto tests/renderer/test_renderer.py + - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py # run renderer test only with REF creation stage - no CUT needed since executables are identical - - python3 -m pytest -q -n auto tests/renderer/test_renderer.py --create_ref + - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --create_ref # run split rendering smoke test and REF creation - - python3 -m pytest -n auto tests/split_rendering/test_split_rendering.py - - python3 -m pytest -n auto tests/split_rendering/test_split_rendering.py --create_ref + - python3 -m pytest -n auto -q --tb=no tests/split_rendering/test_split_rendering.py + - python3 -m pytest -n auto -q --tb=no tests/split_rendering/test_split_rendering.py --create_ref # VoIP BE test - - python3 -m pytest tests/test_be_for_jbm_neutral_dly_profile.py + - python3 -m pytest -q --tb=no tests/test_be_for_jbm_neutral_dly_profile.py # split rendering VoIP BE test - - python3 -m pytest -n auto tests/split_rendering/test_voip_be_splitrend_vs_binaural.py + - python3 -m pytest -n auto -q --tb=no tests/split_rendering/test_voip_be_splitrend_vs_binaural.py # TODO first frame SID - this script runs a `make clean` and needs to be checked/adapted # - bash ci/run-first-frame-is-sid-test.sh # TODO consider HRTF binary loading test, or probably already covered by PRM files - # - python3 -m pytest tests/hrtf_binary_loading + # - python3 -m pytest -q --tb=no tests/hrtf_binary_loading # run BE vs EXT test - python3 tests/create_short_testvectors.py --cut_len 1.0 - - python3 -m pytest tests/test_be_for_ext_outputs.py + - python3 -m pytest -q --tb=no tests/test_be_for_ext_outputs.py # run LC3plus wrapper unit test - scripts/split_rendering/lc3plus_float/ivas_lc3plus_unit_test # TODO consider including check-clipping, but this scales ALL testv files. could be added at end of this job @@ -2250,22 +2252,22 @@ coverage-test-on-main-scheduled: - lcov -r coverage_stv.info "*lib_util*" -o coverage_stv.info # -- Add extended coverage of EVS test and long test vectors - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto --update_ref 1 --param_file scripts/config/self_test_evs.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_evs.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec # codec smoke test - bash ci/smoke_test.sh coverage # default renderer framesize is 20ms - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto --update_ref 1 --param_file scripts/config/self_test_ltv.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_ltv.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec # for different renderer framesizes, only the decoder needs to be tested, so use --decoder_only # need to ignore non-zero exit codes as limiter is active and thus the different framesiszes will not be BE in all cases - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto --param_file scripts/config/self_test_ltv.prm -dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 5 || true - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto --param_file scripts/config/self_test_ltv.prm -dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 10 || true - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto --update_ref 1 --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec --use_ltv - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only --use_ltv || true - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only --use_ltv || true + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --param_file scripts/config/self_test_ltv.prm -dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 5 || true + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --param_file scripts/config/self_test_ltv.prm -dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 10 || true + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --update_ref 1 --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec --use_ltv + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only --use_ltv || true + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only --use_ltv || true # run renderer smoke test - needed for REFVEC tests - - python3 -m pytest -q -n auto tests/renderer/test_renderer.py --use_ltv + - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --use_ltv # run renderer test only with REF creation stage - no CUT needed since executables are identical - - python3 -m pytest -q -n auto tests/renderer/test_renderer.py --use_ltv --create_ref + - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --use_ltv --create_ref # split rendering test doesn't support LTV - skipped here # VoIP BE tests don't support LTV - skipped here -- GitLab From 4a36b9fcfb1789fd2dc8dd105237fbd0aff7d87f Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Tue, 30 Sep 2025 08:49:55 +0200 Subject: [PATCH 051/100] fix pytest call argument --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1b67ef29db..178d02f7ac 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2259,8 +2259,8 @@ coverage-test-on-main-scheduled: - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_ltv.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec # for different renderer framesizes, only the decoder needs to be tested, so use --decoder_only # need to ignore non-zero exit codes as limiter is active and thus the different framesiszes will not be BE in all cases - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --param_file scripts/config/self_test_ltv.prm -dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 5 || true - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --param_file scripts/config/self_test_ltv.prm -dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 10 || true + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --param_file scripts/config/self_test_ltv.prm --dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 5 || true + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --param_file scripts/config/self_test_ltv.prm --dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 10 || true - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --update_ref 1 --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec --use_ltv - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only --use_ltv || true - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only --use_ltv || true -- GitLab From eb28fe8b80b786274a899914e238a174724125b3 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Tue, 30 Sep 2025 08:50:54 +0200 Subject: [PATCH 052/100] add 2 debug printouts --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 178d02f7ac..297f40275d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2205,6 +2205,7 @@ coverage-test-on-main-scheduled: - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/print-common-info.sh - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/update-ltv-repo.sh - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/copy-ltv-files-to-testv-dir.sh + - ls -altr scripts\testv # compile with coverage enabled - cmake -B cmake-build -G "Unix Makefiles" -DCOPY_EXECUTABLES_FROM_BUILD_DIR=true -DGCOV=ON - cmake --build cmake-build -- -j @@ -2265,6 +2266,7 @@ coverage-test-on-main-scheduled: - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only --use_ltv || true - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only --use_ltv || true # run renderer smoke test - needed for REFVEC tests + - ls -altr scripts\testv - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --use_ltv # run renderer test only with REF creation stage - no CUT needed since executables are identical - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --use_ltv --create_ref -- GitLab From 25138faadca1ab9d804fe15ad1d4665d9677fae1 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Tue, 30 Sep 2025 09:15:35 +0200 Subject: [PATCH 053/100] fix typo: backslash - back = slash --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 297f40275d..0086e802e6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2205,7 +2205,7 @@ coverage-test-on-main-scheduled: - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/print-common-info.sh - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/update-ltv-repo.sh - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/copy-ltv-files-to-testv-dir.sh - - ls -altr scripts\testv + - ls -altr scripts/testv # compile with coverage enabled - cmake -B cmake-build -G "Unix Makefiles" -DCOPY_EXECUTABLES_FROM_BUILD_DIR=true -DGCOV=ON - cmake --build cmake-build -- -j @@ -2266,7 +2266,7 @@ coverage-test-on-main-scheduled: - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only --use_ltv || true - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only --use_ltv || true # run renderer smoke test - needed for REFVEC tests - - ls -altr scripts\testv + - ls -altr scripts/testv - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --use_ltv # run renderer test only with REF creation stage - no CUT needed since executables are identical - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --use_ltv --create_ref -- GitLab From 57bb26d3f1b4dbc69cf6839e4af29e8a6bc2868f Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Tue, 30 Sep 2025 13:18:08 +0200 Subject: [PATCH 054/100] [fix] whitespace in metadata filename --- tests/renderer/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/renderer/constants.py b/tests/renderer/constants.py index 85affb1b3a..2d6a4daee4 100644 --- a/tests/renderer/constants.py +++ b/tests/renderer/constants.py @@ -332,7 +332,7 @@ FORMAT_TO_METADATA_FILES_LTV = { "ISM2MASA1": [ str(LTV_DIR.joinpath("ltvISM1.csv")), str(LTV_DIR.joinpath("ltvISM2.csv")), - str(LTV_DIR.joinpath("ltv48_OMASA_2ISM_1TC.met ")), + str(LTV_DIR.joinpath("ltv48_OMASA_2ISM_1TC.met")), ], "ISM3MASA1": [ str(LTV_DIR.joinpath("ltvISM1.csv")), -- GitLab From d68051e43a846906a2a646b1a145e51e7ffcd058 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Tue, 30 Sep 2025 13:30:08 +0200 Subject: [PATCH 055/100] split up coverage into two jobs + light formatting --- .gitlab-ci.yml | 253 +++++++++++++++++++++++++------------------------ 1 file changed, 128 insertions(+), 125 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0086e802e6..aed699e6d5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,43 +11,43 @@ include: # This sets when pipelines are created. Jobs have more specific rules to restrict them. workflow: - name: '$IVAS_PIPELINE_NAME' + name: "$IVAS_PIPELINE_NAME" rules: # see https://docs.gitlab.com/ee/ci/yaml/workflow.html#switch-between-branch-pipelines-and-merge-request-pipelines - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push" when: never - if: $CI_PIPELINE_SOURCE == 'merge_request_event' variables: - IVAS_PIPELINE_NAME: 'MR pipeline: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME' + IVAS_PIPELINE_NAME: "MR pipeline: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME" - if: $CI_PIPELINE_SOURCE == 'push' && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Pushes to main variables: - IVAS_PIPELINE_NAME: 'Push pipeline: $CI_COMMIT_BRANCH' + IVAS_PIPELINE_NAME: "Push pipeline: $CI_COMMIT_BRANCH" - if: $CI_PIPELINE_SOURCE == 'schedule' && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Scheduled in main variables: - IVAS_PIPELINE_NAME: 'Scheduled pipeline: $CI_COMMIT_BRANCH' + IVAS_PIPELINE_NAME: "Scheduled pipeline: $CI_COMMIT_BRANCH" - if: $CI_PIPELINE_SOURCE == 'web' && $MANUAL_PIPELINE_TYPE == 'default' # for testing variables: - IVAS_PIPELINE_NAME: 'Web run pipeline: $CI_COMMIT_BRANCH' + IVAS_PIPELINE_NAME: "Web run pipeline: $CI_COMMIT_BRANCH" - if: $CI_PIPELINE_SOURCE == 'web' && $MANUAL_PIPELINE_TYPE == 'test-be-release' variables: - IVAS_PIPELINE_NAME: 'Test BE to release pipeline: $CI_COMMIT_BRANCH' + IVAS_PIPELINE_NAME: "Test BE to release pipeline: $CI_COMMIT_BRANCH" - if: $CI_PIPELINE_SOURCE == 'web' && $MANUAL_PIPELINE_TYPE == 'test-long-self-test' variables: - IVAS_PIPELINE_NAME: 'Test long self-test against main pipeline: $CI_COMMIT_BRANCH' + IVAS_PIPELINE_NAME: "Test long self-test against main pipeline: $CI_COMMIT_BRANCH" - if: $CI_PIPELINE_SOURCE == 'web' && $MANUAL_PIPELINE_TYPE == 'ivas-conformance' variables: - IVAS_PIPELINE_NAME: 'Draft IVAS Conformance test: $CI_COMMIT_BRANCH' + IVAS_PIPELINE_NAME: "Draft IVAS Conformance test: $CI_COMMIT_BRANCH" - if: $CI_PIPELINE_SOURCE == 'trigger' - if: $CI_PIPELINE_SOURCE == 'web' && $MANUAL_PIPELINE_TYPE == 'ivas-conformance-linux' variables: - IVAS_PIPELINE_NAME: 'Draft IVAS Conformance test -- Linux: $CI_COMMIT_BRANCH' + IVAS_PIPELINE_NAME: "Draft IVAS Conformance test -- Linux: $CI_COMMIT_BRANCH" - if: $CI_PIPELINE_SOURCE == 'trigger' - if: $CI_PIPELINE_SOURCE == 'web' && $MANUAL_PIPELINE_TYPE == 'check-clipping' variables: - IVAS_PIPELINE_NAME: 'Check core input clipping: $CI_COMMIT_BRANCH' + IVAS_PIPELINE_NAME: "Check core input clipping: $CI_COMMIT_BRANCH" - if: $CI_PIPELINE_SOURCE == 'web' && $MANUAL_PIPELINE_TYPE == 'test-branch-vs-input-passthrough' variables: - IVAS_PIPELINE_NAME: 'Pass-through comparison vs input: $CI_COMMIT_BRANCH' + IVAS_PIPELINE_NAME: "Pass-through comparison vs input: $CI_COMMIT_BRANCH" # --------------------------------------------------------------- # Generic script anchors @@ -71,8 +71,8 @@ workflow: - cp ivas-basop/IVAS_cod ./ - cp ivas-basop/IVAS_dec ./ -.merge-request-comparison-setup-codec: &merge-request-comparison-setup-codec - ### build test binaries, initial clean for paranoia reasons +.merge-request-comparison-setup-codec: + &merge-request-comparison-setup-codec ### build test binaries, initial clean for paranoia reasons - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/disable-debugging-macro.sh - make clean - mkdir build @@ -125,9 +125,9 @@ workflow: - Pop-Location .copy-ltv-files-to-testv-dir-win: ©-ltv-files-to-testv-dir-win - - cp $LTV_DIR_WIN\*.wav scripts\testv - - cp $LTV_DIR_WIN\*.met scripts\testv - - cp $LTV_DIR_WIN\*.csv scripts\testv + - cp $LTV_DIR_WIN\*.wav scripts\testv + - cp $LTV_DIR_WIN\*.met scripts\testv + - cp $LTV_DIR_WIN\*.csv scripts\testv .activate-WX-windows: &activate-WX-windows - (Get-Content -Path "CMakeLists.txt") -replace '# \(add_compile_options\("\/WX"\)\)', '$1' | Set-Content -Path "CMakeLists.txt" @@ -160,7 +160,6 @@ workflow: - if [ $exit_code20 -ne 0 ] || [ $exit_code10 -ne 0 ] || [ $exit_code5 -ne 0 ]; then exit 1; fi - # --------------------------------------------------------------- # .pre jobs for setting up things # --------------------------------------------------------------- @@ -359,7 +358,6 @@ build-codec-instrumented-linux: paths: - wmc_tool_output.txt - # make sure that the codec builds with msan, asan and usan build-codec-sanitizers-linux: extends: @@ -402,7 +400,13 @@ codec-smoke-test: tags: - ivas-linux-fast stage: test - needs: ["build-codec-linux-cmake", "build-codec-linux-make", "build-codec-instrumented-linux", "build-codec-sanitizers-linux"] + needs: + [ + "build-codec-linux-cmake", + "build-codec-linux-make", + "build-codec-instrumented-linux", + "build-codec-sanitizers-linux", + ] script: - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/print-common-info.sh # LTV update needed as ltv ISM metadata files are used @@ -411,11 +415,11 @@ codec-smoke-test: # for MRs to basop-ci-branch, we want to test with the BASOP float reference build # per default, the smoke test builds the current repo again with WMOPS activated to catch unbalanced instrumentation macros - if [ "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" = "basop-ci-branch" ]; then - - python3 scripts/smoketest-basop-filter.py scripts/config/ivas_modes.json --inline - - *get-basop-float-reference - - bash ci/smoke_test.sh coverage + - python3 scripts/smoketest-basop-filter.py scripts/config/ivas_modes.json --inline + - *get-basop-float-reference + - bash ci/smoke_test.sh coverage - else - - bash ci/smoke_test.sh + - bash ci/smoke_test.sh - fi ### analyze for failures @@ -481,7 +485,13 @@ pytest-compare-20ms-and-5ms-rendering: - .test-job-linux - .rules-merge-request-to-main stage: test - needs: ["build-codec-linux-cmake", "build-codec-linux-make", "build-codec-instrumented-linux", "build-codec-sanitizers-linux"] + needs: + [ + "build-codec-linux-cmake", + "build-codec-linux-make", + "build-codec-instrumented-linux", + "build-codec-sanitizers-linux", + ] script: - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/print-common-info.sh - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/disable-limiter.sh @@ -583,7 +593,6 @@ renderer-asan: artifacts: expose_as: "renderer asan result" - # test renderer executable with cmake + msan renderer-msan: extends: @@ -595,7 +604,6 @@ renderer-msan: artifacts: expose_as: "renderer msan result" - # test renderer executable with cmake + usan renderer-usan: extends: @@ -607,7 +615,6 @@ renderer-usan: artifacts: expose_as: "renderer usan result" - # compare renderer bitexactness between target and source branch renderer-pytest-on-merge-request: extends: @@ -846,7 +853,6 @@ ivas-pytest-on-merge-request: junit: - report-junit.xml - # Check interop IVAS_cod_test -> IVAS_dec_ref ivas-interop-on-merge-request: extends: @@ -1093,7 +1099,6 @@ check-bitexactness-ext-and-transport-format: expose_as: "logs-ext-sanity-check" expire_in: "5 days" - # --------------------------------------------------------------- # Test jobs for main branch # --------------------------------------------------------------- @@ -1230,7 +1235,6 @@ codec-comparison-on-main-push: reports: junit: report-junit.xml - # --------------------------------------------------------------- # Manual jobs # --------------------------------------------------------------- @@ -1244,7 +1248,6 @@ test-be-to-release: rules: - if: $CI_PIPELINE_SOURCE == 'web' && $MANUAL_PIPELINE_TYPE == 'test-be-release' script: - - echo "$CI_COMMIT_BRANCH" - MSBuild.exe -maxcpucount .\Workspace_msvc\Workspace_msvc.sln /property:Configuration=Debug /p:Platform=win32 /m | tee -variable winoutdata @@ -1261,7 +1264,6 @@ test-be-to-release: - logs/ expose_as: "test-be-to-release results" - ivas-conformance: tags: - ivas-windows @@ -1274,13 +1276,13 @@ ivas-conformance: exit_codes: - 123 script: - - *print-common-info-windows - - MSBuild.exe -maxcpucount .\Workspace_msvc\Workspace_msvc.sln /property:Configuration=Debug + - *print-common-info-windows + - MSBuild.exe -maxcpucount .\Workspace_msvc\Workspace_msvc.sln /property:Configuration=Debug - cp -force IVAS_cod.exe IVAS_cod_ref.exe - cp -force IVAS_dec.exe IVAS_dec_ref.exe - - cp -force IVAS_rend.exe IVAS_rend_ref.exe + - cp -force IVAS_rend.exe IVAS_rend_ref.exe - cp -force ISAR_post_rend.exe ISAR_post_rend_ref.exe - + # Reference creation - python scripts/prepare_combined_format_inputs.py - $TEST_SET = "tests/codec_be_on_mr_nonselection", "tests/renderer/test_renderer.py", "tests/split_rendering/test_split_rendering.py" @@ -1289,21 +1291,21 @@ ivas-conformance: # Output creation - python -m pytest $TEST_SET -v -n auto --keep_files --create_cut --html=report_cmd.html --self-contained-html - python scripts/parse_commands.py report_cmd.html Readme_IVAS.txt - + # Copy input data and output ref data - if (Test-Path testvec) {rm -r -force testvec} - if (Test-Path TMP_DEC) {rm -r -force TMP_DEC} - - if (Test-Path TMP_ENC) {rm -r -force TMP_ENC} + - if (Test-Path TMP_ENC) {rm -r -force TMP_ENC} - if (Test-Path TMP_JBM) {rm -r -force TMP_JBM} - if (Test-Path TMP_REND) {rm -r -force TMP_REND} - - if (Test-Path TMP_ISAR_POST_REND) {rm -r -force TMP_ISAR_POST_REND} - - if (Test-Path TMP_DEC_ISAR) {rm -r -force TMP_DEC_ISAR} + - if (Test-Path TMP_ISAR_POST_REND) {rm -r -force TMP_ISAR_POST_REND} + - if (Test-Path TMP_DEC_ISAR) {rm -r -force TMP_DEC_ISAR} - mkdir testvec - mkdir testvec/binauralRenderer_interface - mkdir testvec/testv - mkdir testvec/testv/renderer - - mkdir testvec/testv/split_rendering - - mkdir testvec/bin + - mkdir testvec/testv/split_rendering + - mkdir testvec/bin - cp -force -ErrorAction Ignore scripts/testv/* testvec/testv - cp -r -force -ErrorAction Ignore scripts/ls_layouts testvec - cp -r -force -ErrorAction Ignore scripts/object_edit testvec @@ -1313,29 +1315,28 @@ ivas-conformance: - cp -r -force -ErrorAction Ignore tests/ref testvec/testv/ref - cp -r -force -ErrorAction Ignore tests/dut/* testvec/testv/ref - cp -r -force -ErrorAction Ignore tests/renderer/cut testvec/testv/renderer/ref - - cp -r -force -ErrorAction Ignore tests/split_rendering/cut testvec/testv/split_rendering/ref - - cp -r -force -ErrorAction Ignore tests/split_rendering/renderer_configs testvec/testv/split_rendering/renderer_configs - - cp -r -force -ErrorAction Ignore tests/split_rendering/error_patterns testvec/testv/split_rendering/error_patterns - - # Remove redundant files + - cp -r -force -ErrorAction Ignore tests/split_rendering/cut testvec/testv/split_rendering/ref + - cp -r -force -ErrorAction Ignore tests/split_rendering/renderer_configs testvec/testv/split_rendering/renderer_configs + - cp -r -force -ErrorAction Ignore tests/split_rendering/error_patterns testvec/testv/split_rendering/error_patterns + + # Remove redundant files - python scripts/cleanup_26252.py # Copy test script files - cp -r -force -ErrorAction Ignore tests/conformance-test testvec/ - cp Readme_IVAS_dec.txt testvec - - cp Readme_IVAS_enc.txt testvec + - cp Readme_IVAS_enc.txt testvec - cp Readme_IVAS_rend.txt testvec - cp Readme_IVAS_JBM_dec.txt testvec - - cp Readme_IVAS_ISAR_dec.txt testvec - - cp Readme_IVAS_ISAR_post_rend.txt testvec + - cp Readme_IVAS_ISAR_dec.txt testvec + - cp Readme_IVAS_ISAR_post_rend.txt testvec - cp IVAS_cod.exe testvec/bin - cp IVAS_dec.exe testvec/bin - cp IVAS_rend.exe testvec/bin - - cp ISAR_post_rend.exe testvec/bin - - + - cp ISAR_post_rend.exe testvec/bin + # Test run generated scripts in testvec - - cd testvec + - cd testvec - python -m pytest conformance-test/test_26252.py --junit-xml=report-junit.xml --html=report.html --self-contained-html - mv report.html .. - mv report-junit.xml .. @@ -1348,12 +1349,12 @@ ivas-conformance: - report_cmd.html - report-junit.xml - report.html - - Readme_IVAS_dec.txt - - Readme_IVAS_enc.txt + - Readme_IVAS_dec.txt + - Readme_IVAS_enc.txt - Readme_IVAS_rend.txt - Readme_IVAS_JBM_dec.txt - - Readme_IVAS_ISAR_dec.txt - - Readme_IVAS_ISAR_post_rend.txt + - Readme_IVAS_ISAR_dec.txt + - Readme_IVAS_ISAR_post_rend.txt expose_as: "Draft IVAS conformance" reports: junit: report-junit.xml @@ -1375,7 +1376,7 @@ ivas-conformance-linux: - cp IVAS_dec IVAS_dec_ref - cp IVAS_rend IVAS_rend_ref - cp ISAR_post_rend ISAR_post_rend_ref - + # Reference creation - python3 scripts/prepare_combined_format_inputs.py - TEST_SET="tests/codec_be_on_mr_nonselection tests/renderer/test_renderer.py tests/split_rendering/test_split_rendering.py" @@ -1384,15 +1385,15 @@ ivas-conformance-linux: # Output creation - python3 -m pytest $TEST_SET -v -n auto --keep_files --create_cut --html=report_cmd.html --self-contained-html - python3 scripts/parse_commands.py report_cmd.html Readme_IVAS.txt - + # Copy input data and output ref data - rm -rf testvec - mkdir testvec - mkdir testvec/binauralRenderer_interface - mkdir testvec/testv - mkdir testvec/testv/renderer - - mkdir testvec/testv/split_rendering - - mkdir testvec/bin + - mkdir testvec/testv/split_rendering + - mkdir testvec/bin - cp -r scripts/testv/* testvec/testv - cp -r scripts/ls_layouts testvec - cp -r scripts/object_edit testvec @@ -1402,34 +1403,34 @@ ivas-conformance-linux: - cp -r tests/ref testvec/testv/ref - cp -r tests/dut/* testvec/testv/ref - cp -r tests/renderer/cut testvec/testv/renderer/ref - - cp -r tests/split_rendering/cut testvec/testv/split_rendering/ref - - cp -r tests/split_rendering/renderer_configs testvec/testv/split_rendering/renderer_configs - - cp -r tests/split_rendering/error_patterns testvec/testv/split_rendering/error_patterns - - # Remove redundant files + - cp -r tests/split_rendering/cut testvec/testv/split_rendering/ref + - cp -r tests/split_rendering/renderer_configs testvec/testv/split_rendering/renderer_configs + - cp -r tests/split_rendering/error_patterns testvec/testv/split_rendering/error_patterns + + # Remove redundant files - python3 scripts/cleanup_26252.py - + # Copy test script files - cp -r tests/conformance-test testvec/ - cp Readme_IVAS_dec.txt testvec - - cp Readme_IVAS_enc.txt testvec + - cp Readme_IVAS_enc.txt testvec - cp Readme_IVAS_rend.txt testvec - cp Readme_IVAS_JBM_dec.txt testvec - - cp Readme_IVAS_ISAR_dec.txt testvec - - cp Readme_IVAS_ISAR_post_rend.txt testvec - + - cp Readme_IVAS_ISAR_dec.txt testvec + - cp Readme_IVAS_ISAR_post_rend.txt testvec + # Create GCOV execs for coverage analysis - make clean - make GCOV=1 -j - + - cp IVAS_cod testvec/bin - cp IVAS_dec testvec/bin - cp IVAS_rend testvec/bin - - cp ISAR_post_rend testvec/bin - + - cp ISAR_post_rend testvec/bin + # Test run generated scripts in testvec - - cd testvec - - exit_code=0 + - cd testvec + - exit_code=0 - python3 -m pytest conformance-test/test_26252.py --junit-xml=report-junit.xml --html=report.html --self-contained-html || exit_code=$? - mv report.html .. - mv report-junit.xml .. @@ -1444,7 +1445,7 @@ ivas-conformance-linux: # Check for failures - if [ $exit_code -ne 0 ]; then echo "Test failures encountered"; exit $EXIT_CODE_FAIL; fi - + artifacts: name: "ivas-conformance-linux-$CI_COMMIT_SHORT_SHA" expire_in: 1 week @@ -1453,19 +1454,18 @@ ivas-conformance-linux: - report_cmd.html - report-junit.xml - report.html - - Readme_IVAS_dec.txt - - Readme_IVAS_enc.txt + - Readme_IVAS_dec.txt + - Readme_IVAS_enc.txt - Readme_IVAS_rend.txt - Readme_IVAS_JBM_dec.txt - - Readme_IVAS_ISAR_dec.txt - - Readme_IVAS_ISAR_post_rend.txt + - Readme_IVAS_ISAR_dec.txt + - Readme_IVAS_ISAR_post_rend.txt - coverage.info - coverage expose_as: "Draft IVAS conformance -- Linux" reports: junit: report-junit.xml - test-long-self-test: extends: - .job-linux @@ -1523,7 +1523,6 @@ test-long-self-test: - if [ $exit_code -ne 0 ]; then echo "Non-bitexact cases encountered!"; exit $EXIT_CODE_NON_BE; fi - exit 0 - artifacts: name: "test-long-self-test--sha-$CI_COMMIT_SHORT_SHA--results" when: always @@ -1972,7 +1971,6 @@ sanitizer-test-omasa-ism4: - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/update-ltv-repo.sh - python3 ci/run_scheduled_sanitizer_test.py MASA-ISM4 $OUT_FORMATS_ALL --tests $SANITIZER_TESTS - ### --- sanitizer schedule C --- .sanitizer-test-schedule-C: @@ -2185,10 +2183,8 @@ sanitizer-test-osba-planar-hoa3-ism4: - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/update-ltv-repo.sh - python3 ci/run_scheduled_sanitizer_test.py PlanarHOA3-ISM4 $OUT_FORMATS_ALL --tests $SANITIZER_TESTS - - # GCOV/LCOV coverage analysis of self_test suite -coverage-test-on-main-scheduled: +.coverage-test-template: extends: - .test-job-linux-needs-testv-dir - .rules-main-scheduled @@ -2198,10 +2194,10 @@ coverage-test-on-main-scheduled: rules: # only run in scheduled pipeline that passes this env vars - if: $COVERAGE_TEST - timeout: 12 hours - script: + timeout: 3 hours + before_script: - set -e - - "trap 'echo \"Command failed at line $LINENO: $BASH_COMMAND\"' ERR" + - 'trap ''echo "Command failed at line $LINENO: $BASH_COMMAND"'' ERR' - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/print-common-info.sh - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/update-ltv-repo.sh - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/copy-ltv-files-to-testv-dir.sh @@ -2214,7 +2210,25 @@ coverage-test-on-main-scheduled: - cp IVAS_dec IVAS_dec_ref - cp IVAS_rend IVAS_rend_ref - cp ISAR_post_rend ISAR_post_rend_ref + after_script: + - lcov -c -d cmake-build -o $COVERAGE_OUTPUT_FILE + # remove apps and lib_util files from coverage + - lcov -r coverage.info "*apps*" -o $COVERAGE_OUTPUT_FILE + - lcov -r coverage.info "*lib_util*" -o $COVERAGE_OUTPUT_FILE + - commit_sha=$(git rev-parse HEAD) + - genhtml $COVERAGE_OUTPUT_FILE -o $COVERAGE_OUTPUT_DIR -t "Coverage on main -- $COVERAGE_TITLE @ $commit_sha" + artifacts: + name: "main-coverage-sha-$CI_COMMIT_SHORT_SHA" + when: always + expire_in: 1 week +coverage-test-on-main-scheduled-stv: + extends: .coverage-test-template + variables: + COVERAGE_OUTPUT_FILE: "coverage_stv.info" + COVERAGE_OUTPUT_DIR: "coverage_stv" + COVERAGE_TITLE: "short test vectors" + script: # -- Run short test vector suite to check coverage of that first # codec smoke test - bash ci/smoke_test.sh coverage @@ -2245,17 +2259,20 @@ coverage-test-on-main-scheduled: # run LC3plus wrapper unit test - scripts/split_rendering/lc3plus_float/ivas_lc3plus_unit_test # TODO consider including check-clipping, but this scales ALL testv files. could be added at end of this job + artifacts: + paths: + - coverage_stv.info + - coverage_stv - # -- Extract coverage of short testvectors - - lcov -c -d cmake-build -o coverage_stv.info - # -- Remove apps and lib_util files from coverage - - lcov -r coverage_stv.info "*apps*" -o coverage_stv.info - - lcov -r coverage_stv.info "*lib_util*" -o coverage_stv.info - +coverage-test-on-main-scheduled-ltv: + extends: .coverage-test-template + variables: + COVERAGE_OUTPUT_FILE: "coverage_ltv.info" + COVERAGE_OUTPUT_DIR: "coverage_ltv" + COVERAGE_TITLE: "long test vectors" + script: # -- Add extended coverage of EVS test and long test vectors - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_evs.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec - # codec smoke test - - bash ci/smoke_test.sh coverage # default renderer framesize is 20ms - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_ltv.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec # for different renderer framesizes, only the decoder needs to be tested, so use --decoder_only @@ -2272,24 +2289,10 @@ coverage-test-on-main-scheduled: - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --use_ltv --create_ref # split rendering test doesn't support LTV - skipped here # VoIP BE tests don't support LTV - skipped here - - # -- Extract accumulated coverage of STV + EVS + LTV - - lcov -c -d cmake-build -o coverage.info - # remove apps and lib_util files from coverage - - lcov -r coverage.info "*apps*" -o coverage.info - - lcov -r coverage.info "*lib_util*" -o coverage.info - - commit_sha=$(git rev-parse HEAD) - - genhtml coverage.info -o coverage -t "Coverage on main @ $commit_sha" - - genhtml coverage_stv.info -o coverage_stv -t "Coverage on main -- short test vectors @ $commit_sha" artifacts: - name: "main-coverage-sha-$CI_COMMIT_SHORT_SHA" - when: always - expire_in: 1 week paths: - - coverage.info - - coverage_stv.info - - coverage - - coverage_stv + - coverage_ltv.info + - coverage_ltv # --------------------------------------------------------------- # Complexity measurement jobs @@ -2309,20 +2312,20 @@ coverage-test-on-main-scheduled: # 1. check for public_dir being there as this might not be the case when artifact download failed # 2. check for public dir not being empty - handle job failures in prev job that happen after the dir is created. In that case, the empty dir is in the artifacts - if [ -d $public_dir ] && [ ! -z "$( ls -A $public_dir )" ]; then - - mv $public_dir/* wmops/ + - mv $public_dir/* wmops/ # check here if we have the split-by-levels files present - if not, fake them up with the existing global one # this is needed for the first run with split graphs on a branch where the global version did run previously # NOTE: checking only for level_1 file here as this should already be sufficient # NOTE2: also not chechking for RAM for same reason - - wmops_all_global="wmops/log_wmops_all.txt" - - ram_all_global="wmops/log_ram_all.txt" - - if [ -f "${wmops_all_global}" ] && [ ! -f "wmops/log_wmops_all_level_1.txt" ]; then - - declare -a suffixes=("level_1" "level_2" "level_3" "rate_sw") - - for suffix in "${suffixes[@]}"; do - - cp ${wmops_all_global} wmops/log_wmops_all_${suffix}.txt - - cp ${ram_all_global} wmops/log_ram_all_${suffix}.txt - - done - - fi + - wmops_all_global="wmops/log_wmops_all.txt" + - ram_all_global="wmops/log_ram_all.txt" + - if [ -f "${wmops_all_global}" ] && [ ! -f "wmops/log_wmops_all_level_1.txt" ]; then + - declare -a suffixes=("level_1" "level_2" "level_3" "rate_sw") + - for suffix in "${suffixes[@]}"; do + - cp ${wmops_all_global} wmops/log_wmops_all_${suffix}.txt + - cp ${ram_all_global} wmops/log_ram_all_${suffix}.txt + - done + - fi - fi - rm artifacts.zip -- GitLab From ec9ca95f5727dc05d6076224c1639b2230d47f7f Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Tue, 30 Sep 2025 13:39:10 +0200 Subject: [PATCH 056/100] revert some whitespace changes and fix before_script --- .gitlab-ci.yml | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index aed699e6d5..a483a7272a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -71,8 +71,8 @@ workflow: - cp ivas-basop/IVAS_cod ./ - cp ivas-basop/IVAS_dec ./ -.merge-request-comparison-setup-codec: - &merge-request-comparison-setup-codec ### build test binaries, initial clean for paranoia reasons +.merge-request-comparison-setup-codec: &merge-request-comparison-setup-codec + ### build test binaries, initial clean for paranoia reasons - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/disable-debugging-macro.sh - make clean - mkdir build @@ -415,11 +415,11 @@ codec-smoke-test: # for MRs to basop-ci-branch, we want to test with the BASOP float reference build # per default, the smoke test builds the current repo again with WMOPS activated to catch unbalanced instrumentation macros - if [ "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" = "basop-ci-branch" ]; then - - python3 scripts/smoketest-basop-filter.py scripts/config/ivas_modes.json --inline - - *get-basop-float-reference - - bash ci/smoke_test.sh coverage + - python3 scripts/smoketest-basop-filter.py scripts/config/ivas_modes.json --inline + - *get-basop-float-reference + - bash ci/smoke_test.sh coverage - else - - bash ci/smoke_test.sh + - bash ci/smoke_test.sh - fi ### analyze for failures @@ -2196,6 +2196,7 @@ sanitizer-test-osba-planar-hoa3-ism4: - if: $COVERAGE_TEST timeout: 3 hours before_script: + - !reference [.job-linux, before_script] - set -e - 'trap ''echo "Command failed at line $LINENO: $BASH_COMMAND"'' ERR' - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/print-common-info.sh @@ -2312,20 +2313,20 @@ coverage-test-on-main-scheduled-ltv: # 1. check for public_dir being there as this might not be the case when artifact download failed # 2. check for public dir not being empty - handle job failures in prev job that happen after the dir is created. In that case, the empty dir is in the artifacts - if [ -d $public_dir ] && [ ! -z "$( ls -A $public_dir )" ]; then - - mv $public_dir/* wmops/ + - mv $public_dir/* wmops/ # check here if we have the split-by-levels files present - if not, fake them up with the existing global one # this is needed for the first run with split graphs on a branch where the global version did run previously # NOTE: checking only for level_1 file here as this should already be sufficient # NOTE2: also not chechking for RAM for same reason - - wmops_all_global="wmops/log_wmops_all.txt" - - ram_all_global="wmops/log_ram_all.txt" - - if [ -f "${wmops_all_global}" ] && [ ! -f "wmops/log_wmops_all_level_1.txt" ]; then - - declare -a suffixes=("level_1" "level_2" "level_3" "rate_sw") - - for suffix in "${suffixes[@]}"; do - - cp ${wmops_all_global} wmops/log_wmops_all_${suffix}.txt - - cp ${ram_all_global} wmops/log_ram_all_${suffix}.txt - - done - - fi + - wmops_all_global="wmops/log_wmops_all.txt" + - ram_all_global="wmops/log_ram_all.txt" + - if [ -f "${wmops_all_global}" ] && [ ! -f "wmops/log_wmops_all_level_1.txt" ]; then + - declare -a suffixes=("level_1" "level_2" "level_3" "rate_sw") + - for suffix in "${suffixes[@]}"; do + - cp ${wmops_all_global} wmops/log_wmops_all_${suffix}.txt + - cp ${ram_all_global} wmops/log_ram_all_${suffix}.txt + - done + - fi - fi - rm artifacts.zip -- GitLab From ba81f075c9a85142c9ee9a21c5b9a0bb9cfcd3fd Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Tue, 30 Sep 2025 13:50:06 +0200 Subject: [PATCH 057/100] remove ls command from debugging --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a483a7272a..dd91e1d752 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2202,7 +2202,6 @@ sanitizer-test-osba-planar-hoa3-ism4: - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/print-common-info.sh - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/update-ltv-repo.sh - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/copy-ltv-files-to-testv-dir.sh - - ls -altr scripts/testv # compile with coverage enabled - cmake -B cmake-build -G "Unix Makefiles" -DCOPY_EXECUTABLES_FROM_BUILD_DIR=true -DGCOV=ON - cmake --build cmake-build -- -j -- GitLab From 0d3c9d1beaa00a44c8e74e07a93b18088bc3d76b Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Tue, 30 Sep 2025 16:53:30 +0200 Subject: [PATCH 058/100] [fix] remove redundant second call to renderer LTV test --- .gitlab-ci.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dd91e1d752..21997fe6f8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2213,8 +2213,8 @@ sanitizer-test-osba-planar-hoa3-ism4: after_script: - lcov -c -d cmake-build -o $COVERAGE_OUTPUT_FILE # remove apps and lib_util files from coverage - - lcov -r coverage.info "*apps*" -o $COVERAGE_OUTPUT_FILE - - lcov -r coverage.info "*lib_util*" -o $COVERAGE_OUTPUT_FILE + - lcov -r $COVERAGE_OUTPUT_FILE "*apps*" -o $COVERAGE_OUTPUT_FILE + - lcov -r $COVERAGE_OUTPUT_FILE "*lib_util*" -o $COVERAGE_OUTPUT_FILE - commit_sha=$(git rev-parse HEAD) - genhtml $COVERAGE_OUTPUT_FILE -o $COVERAGE_OUTPUT_DIR -t "Coverage on main -- $COVERAGE_TITLE @ $commit_sha" artifacts: @@ -2284,9 +2284,8 @@ coverage-test-on-main-scheduled-ltv: - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only --use_ltv || true # run renderer smoke test - needed for REFVEC tests - ls -altr scripts/testv + # LTV smoketest is the same as REF creation stage for renderer - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --use_ltv - # run renderer test only with REF creation stage - no CUT needed since executables are identical - - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --use_ltv --create_ref # split rendering test doesn't support LTV - skipped here # VoIP BE tests don't support LTV - skipped here artifacts: -- GitLab From b7239dbf485309c136b02730ff08f0a566b88983 Mon Sep 17 00:00:00 2001 From: Tapani Pihlajakuja Date: Wed, 1 Oct 2025 08:43:13 +0300 Subject: [PATCH 059/100] Revert "temporarily skip MASA prerendering cases at non-48kHz SR until #1334 is resolved" This reverts commit e151ef1b77b7f0e7de20a685986a28443a7e0d5f. --- tests/renderer/test_renderer.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/renderer/test_renderer.py b/tests/renderer/test_renderer.py index 8b68e47541..392ee07c2a 100644 --- a/tests/renderer/test_renderer.py +++ b/tests/renderer/test_renderer.py @@ -677,8 +677,6 @@ def test_masa_prerend_scenes( get_odg_bin, split_comparison, ): - if fs != "48kHz": - pytest.skip("MASA Prerendering at 16 and 32 kHz WIP") run_renderer( record_property, props_to_record, -- GitLab From 7f775d5aa1d9c706c3dcad7e13a2bcb4728804a3 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Wed, 1 Oct 2025 11:47:36 +0200 Subject: [PATCH 060/100] [fix] update artifact name and replace hardcoded paths in .info files with relative paths --- .gitlab-ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 21997fe6f8..ae97cf7153 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2216,9 +2216,11 @@ sanitizer-test-osba-planar-hoa3-ism4: - lcov -r $COVERAGE_OUTPUT_FILE "*apps*" -o $COVERAGE_OUTPUT_FILE - lcov -r $COVERAGE_OUTPUT_FILE "*lib_util*" -o $COVERAGE_OUTPUT_FILE - commit_sha=$(git rev-parse HEAD) + # replace hardcoded path with relative path to allow easy merging of .info files + - sed -i "s|$(pwd)|..|g" $COVERAGE_OUTPUT_FILE - genhtml $COVERAGE_OUTPUT_FILE -o $COVERAGE_OUTPUT_DIR -t "Coverage on main -- $COVERAGE_TITLE @ $commit_sha" artifacts: - name: "main-coverage-sha-$CI_COMMIT_SHORT_SHA" + name: "main-$COVERAGE_OUTPUT_DIR-sha-$CI_COMMIT_SHORT_SHA" when: always expire_in: 1 week -- GitLab From 52854f012bf3c5504136aaacee8a8a2c9a6e4755 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Wed, 1 Oct 2025 11:55:54 +0200 Subject: [PATCH 061/100] try to merge coverage reports --- .gitlab-ci.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ae97cf7153..985532c3a9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2224,6 +2224,36 @@ sanitizer-test-osba-planar-hoa3-ism4: when: always expire_in: 1 week +coverage-merge: + extends: .rules-main-scheduled + tags: + - ivas-linux-fast + stage: test + rules: + # only run in scheduled pipeline that passes this env vars + - if: $COVERAGE_TEST + needs: + - job: coverage-test-on-main-scheduled-stv + artifacts: true + - job: coverage-test-on-main-scheduled-ltv + artifacts: true + - job: ivas-conformance-linux + artifacts: true + # before_script: + # - !reference [.job-linux, before_script] # check if this is really needed + script: + - echo "Merging coverage reports..." + - lcov -a *.info -o coverage_merged.info + - commit_sha=$(git rev-parse HEAD) + - genhtml coverage_merged.info -o coverage_merged -t "Merged Coverage Report @ $commit_sha" + artifacts: + name: "merged-coverage-sha-$CI_COMMIT_SHORT_SHA" + when: always + expire_in: 1 week + paths: + - coverage_merged.info + - coverage_merged + coverage-test-on-main-scheduled-stv: extends: .coverage-test-template variables: -- GitLab From 2d3df7ed6461e42c8e1b8e88180481ae3ed075e9 Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Wed, 1 Oct 2025 12:57:46 +0200 Subject: [PATCH 062/100] [fix] substitute path also for ivas-conformance-linux and replace glob with explicit file list (lcov doesn't like multiple arguments for -a) --- .gitlab-ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 985532c3a9..9c80573454 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1441,6 +1441,8 @@ ivas-conformance-linux: - lcov -r coverage.info "*apps*" -o coverage.info - lcov -r coverage.info "*lib_util*" -o coverage.info - commit_sha=$(git rev-parse HEAD) + # replace hardcoded path with relative path to allow easy merging of .info files + - sed -i "s|$(pwd)|..|g" coverage.info - genhtml coverage.info -o coverage -t "Coverage on main @ $commit_sha" # Check for failures @@ -2243,7 +2245,7 @@ coverage-merge: # - !reference [.job-linux, before_script] # check if this is really needed script: - echo "Merging coverage reports..." - - lcov -a *.info -o coverage_merged.info + - lcov -a coverage.info -a coverage_stv.info -a coverage_ltv.info -o coverage_merged.info - commit_sha=$(git rev-parse HEAD) - genhtml coverage_merged.info -o coverage_merged -t "Merged Coverage Report @ $commit_sha" artifacts: -- GitLab From 89596ea7a938a6b232c9cfa9f16a51c3936ee07d Mon Sep 17 00:00:00 2001 From: Archit Tamarapu Date: Wed, 1 Oct 2025 15:08:27 +0200 Subject: [PATCH 063/100] cleanup coverage merge job --- .gitlab-ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9c80573454..6e68613b54 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2241,8 +2241,6 @@ coverage-merge: artifacts: true - job: ivas-conformance-linux artifacts: true - # before_script: - # - !reference [.job-linux, before_script] # check if this is really needed script: - echo "Merging coverage reports..." - lcov -a coverage.info -a coverage_stv.info -a coverage_ltv.info -o coverage_merged.info -- GitLab From 1537ee94203bef414cf257c712b3de2b82439e96 Mon Sep 17 00:00:00 2001 From: Ripinder Singh Date: Thu, 2 Oct 2025 14:12:32 +1000 Subject: [PATCH 064/100] Code review comments, reorganize file api Signed-off-by: Ripinder Singh --- CMakeLists.txt | 2 +- apps/decoder.c | 459 ++-------------------------------- apps/encoder.c | 155 +++--------- lib_com/common_api_types.h | 20 -- lib_dec/jbm_jb4sb.h | 5 - lib_dec/lib_dec.c | 279 +++++++++++---------- lib_dec/lib_dec.h | 35 +-- lib_rend/ivas_rotation.c | 2 +- lib_util/ivas_rtp_file.c | 473 +++++++++++++++++++++++++++++++++++- lib_util/ivas_rtp_file.h | 61 +++-- lib_util/ivas_rtp_pi_data.h | 18 +- lib_util/rtpdump.c | 28 --- lib_util/rtpdump.h | 6 - readme.txt | 4 +- tests/rtp/ivasrtp.py | 3 +- 15 files changed, 724 insertions(+), 826 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index de4727bec8..4dfb8a894c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -165,7 +165,7 @@ file(GLOB libDecSrcs "lib_dec/*.c") file(GLOB libDecHeaders "lib_dec/*.h") add_library(lib_dec ${libDecSrcs} ${libDecHeaders}) target_link_libraries(lib_dec lib_com lib_rend lib_debug lib_isar) -target_include_directories(lib_dec PUBLIC lib_dec lib_rend PRIVATE lib_enc lib_isar) +target_include_directories(lib_dec PUBLIC lib_dec lib_rend PRIVATE lib_enc lib_isar lib_util) file(GLOB libUtilSrcs "lib_util/*.c") file(GLOB libUtilHeaders "lib_util/*.h") diff --git a/apps/decoder.c b/apps/decoder.c index da0fdd2fbd..0f5d0dd9a8 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -39,8 +39,6 @@ #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" @@ -172,430 +170,6 @@ 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", - "AUDIO_FOCUS_INDICATION", "RESERVED15", "PLAYBACK_DEVICE_ORIENTATION", "HEAD_ORIENTATION", "LISTENER_POSITION", - "DYNAMIC_AUDIO_SUPPRESSION", "AUDIO_FOCUS_REQUEST", "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_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_AUDIO_FOCUS_INDICATION: - { - fprintf( f_piDataOut, "{" ); - if ( cur->data.focusIndication.availDirection ) - { - fprintf( f_piDataOut, "\n\t\t\t\"direction\": {\n" ); - fprintf( f_piDataOut, "\t\t\t\t\t\t\"w\": %f,\n\t\t\t\t\t\t\"x\": %f,\n\t\t\t\t\t\t\"y\": %f,\n\t\t\t\t\t\t\"z\": %f \n\t\t\t}", - cur->data.focusIndication.direction.w, cur->data.focusIndication.direction.x, cur->data.focusIndication.direction.y, cur->data.focusIndication.direction.z ); - if ( cur->data.focusIndication.availLevel ) - { - fprintf( f_piDataOut, "," ); - } - } - if ( cur->data.focusIndication.availLevel ) - { - fprintf( f_piDataOut, "\n\t\t\t\"level\": %d", cur->data.focusIndication.flvl ); - } - fprintf( f_piDataOut, "\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_AUDIO_FOCUS_REQUEST: - { - fprintf( f_piDataOut, "{" ); - if ( cur->data.focusRequest.availDirection ) - { - fprintf( f_piDataOut, "\n\t\t\t\"direction\": {\n" ); - fprintf( f_piDataOut, "\t\t\t\t\t\t\"w\": %f,\n\t\t\t\t\t\t\"x\": %f,\n\t\t\t\t\t\t\"y\": %f,\n\t\t\t\t\t\t\"z\": %f \n\t\t\t}", - cur->data.focusRequest.direction.w, cur->data.focusRequest.direction.x, cur->data.focusRequest.direction.y, cur->data.focusRequest.direction.z ); - if ( cur->data.focusRequest.availLevel ) - { - fprintf( f_piDataOut, "," ); - } - } - if ( cur->data.focusRequest.availLevel ) - { - fprintf( f_piDataOut, "\n\t\t\t\"level\": %d", cur->data.focusRequest.flvl ); - } - fprintf( f_piDataOut, "\n\t\t}" ); - } - break; - 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; - -#ifdef RTP_S4_251135_CR26253_0016_REV1 - case IVAS_PI_DIEGETIC_TYPE: - { - DEBUG_PRINT( stdout, "PI_DIEGETIC_TYPE : %d, %d, %d, %d, %d\n", piData->data.digeticIndicator.isDiegetic[0], piData->data.digeticIndicator.isDiegetic[1], piData->data.digeticIndicator.isDiegetic[2], piData->data.digeticIndicator.isDiegetic[3], piData->data.digeticIndicator.isDiegetic[4] ); - IVAS_DEC_setDiegeticInputPI( hIvasDec, piData->data.digeticIndicator.isDiegetic ); - } - break; - -#endif - 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 @@ -3463,16 +3037,19 @@ static ivas_error printBitstreamInfoVoip( ivas_error error = IVAS_ERR_OK; #ifdef IVAS_RTPDUMP IVAS_RTP ivasRtp; + uint8_t au[( IVAS_MAX_BITS_PER_FRAME + 7 ) >> 3]; + int16_t auSizeBits; + uint8_t *auPtr = NULL; #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; +#endif bool qBit; uint32_t nextPacketRcvTime_ms = 0; uint16_t rtpSequenceNumber; @@ -3486,9 +3063,9 @@ static ivas_error printBitstreamInfoVoip( 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 ) + if ( ( error = IVAS_RTP_READER_Init( &ivasRtp, arg.inputBitstreamFilename, arg.piOutputFilename ) ) != IVAS_ERR_OK ) { - fprintf( stderr, "error in IVAS_RTP_Init(): %d\n", error ); + fprintf( stderr, "error in IVAS_RTP_READER_Init(): %d\n", error ); goto cleanup; } #else @@ -3528,7 +3105,7 @@ static ivas_error printBitstreamInfoVoip( { 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 ); + error = IVAS_RTP_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 @@ -3637,6 +3214,7 @@ static ivas_error decodeVoIP( IVAS_RTP ivasRtp = { 0 }; IVAS_DEC_HANDLE hIvasDec = *phIvasDec; int32_t initialTsOffsetSystemAndRTP = 0; + uint32_t nProcPiData = 0; #else FILE *f_rtpstream = NULL; EVS_RTPDUMP_DEPACKER rtpdumpDepacker; @@ -3692,13 +3270,14 @@ static ivas_error decodeVoIP( 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 ) + if ( ( error = IVAS_RTP_READER_Init( &ivasRtp, arg.inputBitstreamFilename, arg.piOutputFilename ) ) != IVAS_ERR_OK ) { - fprintf( stderr, "error in IVAS_RTP_Init(): %d\n", error ); + fprintf( stderr, "error in IVAS_RTP_READER_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 ); @@ -4009,11 +3588,21 @@ static ivas_error decodeVoIP( { /* 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 ) + uint32_t numPiData = 0; + + while ( nProcPiData + numPiData < ivasRtp.nReadPiData && + ivasRtp.piData[nProcPiData + numPiData].timestamp <= piTs ) + { + numPiData++; + } + + if ( ( error = IVAS_RTP_ApplyPiData( hIvasDec, &ivasRtp.piData[nProcPiData], numPiData ) ) != IVAS_ERR_OK ) { fprintf( stderr, "\nError in IVAS_DEC_VoIP_GetSamples: %s\n", IVAS_DEC_GetErrorMessage( error ) ); goto cleanup; } + + nProcPiData += numPiData; } #endif #ifdef SUPPORT_JBM_TRACEFILE @@ -4192,10 +3781,6 @@ 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 ) { diff --git a/apps/encoder.c b/apps/encoder.c index 147512afc9..66836a1726 100644 --- a/apps/encoder.c +++ b/apps/encoder.c @@ -40,8 +40,6 @@ #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 @@ -50,6 +48,7 @@ #endif #include "wmc_auto.h" + #define WMC_TOOL_SKIP /*------------------------------------------------------------------------------------------* @@ -223,19 +222,8 @@ int main( #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; + IVAS_RTP ivasRtp = { 0 }; #endif /*------------------------------------------------------------------------------------------* @@ -635,38 +623,9 @@ int main( 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 ) + if ( ( error = IVAS_RTP_WRITER_Init( &ivasRtp, arg.outputBitstreamFilename, arg.numFramesPerPacket ) ) != 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 ); + fprintf( stderr, "\nError: Can't open output bitstream file for RTP output %s \n\n", arg.outputBitstreamFilename ); goto cleanup; } } @@ -700,7 +659,6 @@ int main( int16_t numSamplesRead = 0; uint16_t bitStream[IVAS_MAX_BITS_PER_FRAME]; - uint16_t numBits = 0; #ifdef DEBUG_SBA #ifdef DEBUG_AGC @@ -864,47 +822,26 @@ int main( /* *** Encode one frame *** */ #ifdef IVAS_RTPDUMP - if ( hPack ) + if ( ivasRtp.hPack ) { - 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; - } - - packedFrame.length = ( numBits + 7 ) / 8; - rtpPacket.length = 0; + bool isMono = ( arg.inputFormat == IVAS_ENC_INPUT_MONO ); + bool forcePacket = ( numSamplesRead < pcmBufSize ); /* If EoF force Packet generation */ - /* 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; - } + ivasRtp.nWrittenPiData = 0; /* 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 } - }; + PIDATA_TS *piDataTs = &ivasRtp.piData[ivasRtp.nWrittenPiData++]; + IVAS_PIDATA_ORIENTATION *scene = &piDataTs->data.scene; - 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; - } + memset( piDataTs, 0, sizeof( PIDATA_TS ) ); + scene->size = sizeof( IVAS_PIDATA_ORIENTATION ); + scene->piDataType = IVAS_PI_SCENE_ORIENTATION; - if ( ( error = IVAS_RTP_PACK_PushPiData( hPack, (const IVAS_PIDATA_GENERIC *) &sceneOrientation ) ) != IVAS_ERR_OK ) + if ( ( error = HeadRotationFileReading( sceneOrientationFileReader, &scene->orientation, NULL ) ) != IVAS_ERR_OK ) { - fprintf( stderr, "\nError %s while pushing scene orientation\n", IVAS_ENC_GetErrorMessage( error ) ); + fprintf( stderr, "\nError %s while reading scene orientation from %s\n", IVAS_ENC_GetErrorMessage( error ), RotationFileReader_getFilePath( sceneOrientationFileReader ) ); goto cleanup; } } @@ -912,39 +849,30 @@ int main( /* 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 } - }; + PIDATA_TS *piDataTs = &ivasRtp.piData[ivasRtp.nWrittenPiData++]; + IVAS_PIDATA_ORIENTATION *device = &piDataTs->data.deviceUnCompensated; - 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; - } + memset( piDataTs, 0, sizeof( PIDATA_TS ) ); + device->size = sizeof( IVAS_PIDATA_ORIENTATION ); + device->piDataType = IVAS_PI_DEVICE_ORIENTATION_COMPENSATED; - if ( ( error = IVAS_RTP_PACK_PushPiData( hPack, (const IVAS_PIDATA_GENERIC *) &deviceOrientation ) ) != IVAS_ERR_OK ) + if ( ( error = HeadRotationFileReading( deviceOrientationFileReader, &device->orientation, NULL ) ) != IVAS_ERR_OK ) { - fprintf( stderr, "\nError %s while pushing scene orientation\n", IVAS_ENC_GetErrorMessage( error ) ); + fprintf( stderr, "\nError %s while reading device orientation from %s\n", IVAS_ENC_GetErrorMessage( error ), RotationFileReader_getFilePath( deviceOrientationFileReader ) ); goto cleanup; } } - if ( ( numSamplesRead < pcmBufSize ) || IVAS_RTP_PACK_GetNumFrames( hPack ) == arg.numFramesPerPacket ) + if ( ( error = IVAS_ENC_EncodeFrameToCompact( hIvasEnc, pcmBuf, pcmBufSize, au, &numBits ) ) != IVAS_ERR_OK ) { - /* 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; - } + fprintf( stderr, "\nencodeFrame failed: %s\n\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; - } + if ( ( error = IVAS_RTP_WriteNextFrame( &ivasRtp, au, numBits, isMono, forcePacket ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nError %s while pushing audio frame to RTP pack\n", IVAS_ENC_GetErrorMessage( error ) ); + goto cleanup; } } else @@ -1043,27 +971,6 @@ cleanup: } #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 ); @@ -1074,7 +981,7 @@ cleanup: RotationFileReader_close( &deviceOrientationFileReader ); } - IvasRtpFile_Close( &hWriter ); + IVAS_RTP_Term( &ivasRtp ); #endif IVAS_ENC_Close( &hIvasEnc ); diff --git a/lib_com/common_api_types.h b/lib_com/common_api_types.h index ce2982b63b..f5b13c0dea 100644 --- a/lib_com/common_api_types.h +++ b/lib_com/common_api_types.h @@ -212,26 +212,6 @@ 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_dec/jbm_jb4sb.h b/lib_dec/jbm_jb4sb.h index 2ef48dbfb3..8155d66ae9 100644 --- a/lib_dec/jbm_jb4sb.h +++ b/lib_dec/jbm_jb4sb.h @@ -63,13 +63,8 @@ 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 c3b7e69a82..5c1edb06fc 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -338,10 +338,16 @@ void IVAS_DEC_Close( ( *phIvasDec )->hVoIP = NULL; } +#ifndef IVAS_RTPDUMP + ivas_destroy_handle_isar( &( *phIvasDec )->st_ivas->hSplitBinRend ); + +#endif /* destroy Split binaural renderer (ISAR) handle */ if ( ( *phIvasDec )->st_ivas ) { +#ifdef IVAS_RTPDUMP ivas_destroy_handle_isar( &( *phIvasDec )->st_ivas->hSplitBinRend ); +#endif ivas_destroy_dec( ( *phIvasDec )->st_ivas ); ( *phIvasDec )->st_ivas = NULL; } @@ -3233,11 +3239,7 @@ 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 */ -#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 + const bool qBit /* i : Q bit for AMR-WB IO */ ) { JB4_DATAUNIT_HANDLE dataUnit; @@ -3279,11 +3281,7 @@ 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 ); @@ -3306,11 +3304,7 @@ 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 ); @@ -3734,125 +3728,6 @@ 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 -#ifdef RTP_S4_251135_CR26253_0016_REV1 -/*---------------------------------------------------------------------* - * IVAS_DEC_setDiegeticInput( ) - * - * Set isDiegeticInput flag for combined orientation handle based on PI data. - *---------------------------------------------------------------------*/ - -void IVAS_DEC_setDiegeticInputPI( - IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ - const bool *diegeticPIValues /* i : diegetic values for the input stream */ -) -{ - if ( hIvasDec->st_ivas->hCombinedOrientationData != NULL ) - { - int8_t i; - for ( i = 0; i < (1 + IVAS_MAX_NUM_OBJECTS); i++ ) - { - hIvasDec->st_ivas->hCombinedOrientationData->isDiegeticInputPI[i] = diegeticPIValues[i]; - } - hIvasDec->st_ivas->hCombinedOrientationData->isDiegeticInputPISet = true; - } -} - - -#endif /*---------------------------------------------------------------------* * IVAS_DEC_VoIP_IsEmpty( ) * @@ -5157,3 +5032,143 @@ ivas_error IVAS_DEC_is_split_rendering_coded_out( return IVAS_ERR_OK; } + +#ifdef IVAS_RTPDUMP +#ifdef DEBUGGING +#define DEBUG_PRINT fprintf +#else +#define DEBUG_PRINT( ... ) +#endif + +/*---------------------------------------------------------------------* + * IVAS_DEC_feedSinglePIorientation( ) + * + * Feed a single orientation PI data to external orientation handle. + *---------------------------------------------------------------------*/ + +static 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_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 ) ); +} + +#ifdef RTP_S4_251135_CR26253_0016_REV1 +/*---------------------------------------------------------------------* + * IVAS_DEC_setDiegeticInput( ) + * + * Set isDiegeticInput flag for combined orientation handle based on PI data. + *---------------------------------------------------------------------*/ + +static void IVAS_DEC_setDiegeticInputPI( + IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ + const bool *diegeticPIValues /* i : diegetic values for the input stream */ +) +{ + if ( hIvasDec->st_ivas->hCombinedOrientationData != NULL ) + { + int8_t i; + for ( i = 0; i < ( 1 + IVAS_MAX_NUM_OBJECTS ); i++ ) + { + hIvasDec->st_ivas->hCombinedOrientationData->isDiegeticInputPI[i] = diegeticPIValues[i]; + } + hIvasDec->st_ivas->hCombinedOrientationData->isDiegeticInputPISet = true; + } +} +#endif + +ivas_error IVAS_RTP_ApplyPiData( IVAS_DEC_HANDLE hIvasDec, PIDATA_TS *piData, uint32_t numPiData ) +{ + ivas_error error = IVAS_ERR_OK; + while ( numPiData-- ) + { + uint32_t piDataType = piData->data.noPiData.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; + +#ifdef RTP_S4_251135_CR26253_0016_REV1 + case IVAS_PI_DIEGETIC_TYPE: + { + DEBUG_PRINT( stdout, "PI_DIEGETIC_TYPE : %d, %d, %d, %d, %d\n", piData->data.digeticIndicator.isDiegetic[0], piData->data.digeticIndicator.isDiegetic[1], piData->data.digeticIndicator.isDiegetic[2], piData->data.digeticIndicator.isDiegetic[3], piData->data.digeticIndicator.isDiegetic[4] ); + IVAS_DEC_setDiegeticInputPI( hIvasDec, piData->data.digeticIndicator.isDiegetic ); + } + break; +#endif + + default: + { + /* NOT HANDLED PI DATA - DO NOTHING */ + } + break; + } + if ( error != IVAS_ERR_OK ) + { + return error; + } + } + return error; +} +#endif \ No newline at end of file diff --git a/lib_dec/lib_dec.h b/lib_dec/lib_dec.h index caed7bd54b..242b601942 100644 --- a/lib_dec/lib_dec.h +++ b/lib_dec/lib_dec.h @@ -285,11 +285,7 @@ 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 */ -#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 + const bool qBit /* i : Q bit for AMR-WB IO */ ); ivas_error IVAS_DEC_VoIP_SetScale( @@ -333,30 +329,6 @@ 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 -#ifdef RTP_S4_251135_CR26253_0016_REV1 -void IVAS_DEC_setDiegeticInputPI( - IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ - const bool *diegeticPIValues /* i : diegetic values for the input stream */ -); - -#endif /* Setter functions - apply changes to decoder configuration */ /*! r: error code */ @@ -581,6 +553,11 @@ void IVAS_DEC_PrintDisclaimer( void ); +#ifdef IVAS_RTPDUMP +#include "ivas_rtp_pi_data.h" +ivas_error IVAS_RTP_ApplyPiData( IVAS_DEC_HANDLE hIvasDec, PIDATA_TS *piData, uint32_t numPiData ); +#endif + /* clang-format on */ #endif diff --git a/lib_rend/ivas_rotation.c b/lib_rend/ivas_rotation.c index 8b62d7a111..6c6acb2653 100644 --- a/lib_rend/ivas_rotation.c +++ b/lib_rend/ivas_rotation.c @@ -864,7 +864,7 @@ ivas_error ivas_combined_orientation_open( ( *hCombinedOrientationData )->cur_subframe_samples_rendered = 0; #ifdef RTP_S4_251135_CR26253_0016_REV1 - for ( i = 0; i < (1 + IVAS_MAX_NUM_OBJECTS); i++ ) + for ( i = 0; i < ( 1 + IVAS_MAX_NUM_OBJECTS ); i++ ) { ( *hCombinedOrientationData )->isDiegeticInputPI[i] = true; } diff --git a/lib_util/ivas_rtp_file.c b/lib_util/ivas_rtp_file.c index 0851a998f6..1c45869e69 100644 --- a/lib_util/ivas_rtp_file.c +++ b/lib_util/ivas_rtp_file.c @@ -31,8 +31,10 @@ *******************************************************************************************************/ #include #include +#include #include +#include "lib_dec.h" #include "ivas_rtp_file.h" #include "ivas_error_utils.h" @@ -42,7 +44,7 @@ struct IVAS_RTP_FILE FILE *f_rtpstream; }; -ivas_error IvasRtpFile_Open( +static 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 */ @@ -65,7 +67,7 @@ ivas_error IvasRtpFile_Open( return IVAS_ERR_OK; } -ivas_error IvasRtpFile_Close( +static ivas_error IvasRtpFile_Close( IVAS_RTP_FILE_HANDLE *phReader /* i : pointer to an IVAS file reader handle */ ) { @@ -83,17 +85,17 @@ ivas_error IvasRtpFile_Close( return IVAS_ERR_OK; } -ivas_error IvasRtpFile_Write( - IVAS_RTP_FILE_HANDLE hReader, /* i : pointer to an IVAS file writer handle */ +static ivas_error IvasRtpFile_Write( + IVAS_RTP_FILE_HANDLE hRtpFile, /* i : pointer to an IVAS file writer handle */ const uint8_t *packet, size_t numBytes ) { ivas_error error = IVAS_ERR_OK; - if ( hReader->isFileWriter ) + if ( hRtpFile->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 ); + fwrite( &length, sizeof( uint32_t ), 1, hRtpFile->f_rtpstream ); + fwrite( packet, sizeof( uint8_t ), numBytes, hRtpFile->f_rtpstream ); } else { @@ -102,20 +104,20 @@ ivas_error IvasRtpFile_Write( return error; } -ivas_error IvasRtpFile_Read( - IVAS_RTP_FILE_HANDLE hReader, /* i : pointer to an IVAS file writer handle */ +static ivas_error IvasRtpFile_Read( + IVAS_RTP_FILE_HANDLE hRtpFile, /* 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 ) + if ( hRtpFile->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 */ + nread = fread( &length, sizeof( uint32_t ), 1, hRtpFile->f_rtpstream ); /* Read Packet Length */ if ( nread == 0 ) { return IVAS_ERR_END_OF_FILE; @@ -128,7 +130,7 @@ ivas_error IvasRtpFile_Read( return IVAS_ERR_INVALID_OUTPUT_BUFFER_SIZE; } - nread = fread( packet, sizeof( uint8_t ), ( *numBytes ), hReader->f_rtpstream ); /* Read Packet */ + nread = fread( packet, sizeof( uint8_t ), ( *numBytes ), hRtpFile->f_rtpstream ); /* Read Packet */ if ( nread < ( *numBytes ) ) { return IVAS_ERR_END_OF_FILE; @@ -136,3 +138,450 @@ ivas_error IvasRtpFile_Read( return IVAS_ERR_OK; } + +static const char *const 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", + "AUDIO_FOCUS_INDICATION", "RESERVED15", "PLAYBACK_DEVICE_ORIENTATION", "HEAD_ORIENTATION", "LISTENER_POSITION", + "DYNAMIC_AUDIO_SUPPRESSION", "AUDIO_FOCUS_REQUEST", "PI_LATENCY", "R_ISM_ID", "R_ISM_GAIN", + "R_ISM_ORIENTATION", "R_ISM_POSITION", "R_ISM_DIRECTION", "RESERVED27", "RESERVED28", "RESERVED29", + "RESERVED30", "NO_DATA" +}; + +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_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_AUDIO_FOCUS_INDICATION: + { + fprintf( f_piDataOut, "{" ); + if ( cur->data.focusIndication.availDirection ) + { + fprintf( f_piDataOut, "\n\t\t\t\"direction\": {\n" ); + fprintf( f_piDataOut, "\t\t\t\t\t\t\"w\": %f,\n\t\t\t\t\t\t\"x\": %f,\n\t\t\t\t\t\t\"y\": %f,\n\t\t\t\t\t\t\"z\": %f \n\t\t\t}", + cur->data.focusIndication.direction.w, cur->data.focusIndication.direction.x, cur->data.focusIndication.direction.y, cur->data.focusIndication.direction.z ); + if ( cur->data.focusIndication.availLevel ) + { + fprintf( f_piDataOut, "," ); + } + } + if ( cur->data.focusIndication.availLevel ) + { + fprintf( f_piDataOut, "\n\t\t\t\"level\": %d", cur->data.focusIndication.flvl ); + } + fprintf( f_piDataOut, "\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_AUDIO_FOCUS_REQUEST: + { + fprintf( f_piDataOut, "{" ); + if ( cur->data.focusRequest.availDirection ) + { + fprintf( f_piDataOut, "\n\t\t\t\"direction\": {\n" ); + fprintf( f_piDataOut, "\t\t\t\t\t\t\"w\": %f,\n\t\t\t\t\t\t\"x\": %f,\n\t\t\t\t\t\t\"y\": %f,\n\t\t\t\t\t\t\"z\": %f \n\t\t\t}", + cur->data.focusRequest.direction.w, cur->data.focusRequest.direction.x, cur->data.focusRequest.direction.y, cur->data.focusRequest.direction.z ); + if ( cur->data.focusRequest.availLevel ) + { + fprintf( f_piDataOut, "," ); + } + } + if ( cur->data.focusRequest.availLevel ) + { + fprintf( f_piDataOut, "\n\t\t\t\"level\": %d", cur->data.focusRequest.flvl ); + } + fprintf( f_piDataOut, "\n\t\t}" ); + } + break; + 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}" ); +} + +void IVAS_RTP_Term( IVAS_RTP *rtp ) +{ + if ( NULL != rtp ) + { + if ( rtp->hPack != NULL ) + { + /* Complete the last packet */ + if ( IVAS_RTP_PACK_GetNumFrames( rtp->hPack ) != 0 ) + { + ivas_error error = IVAS_ERR_OK; + uint32_t numFramesInPayload = 0; + + if ( ( error = IVAS_RTP_PACK_GetPacket( rtp->hPack, &rtp->rtpPacket, &numFramesInPayload ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nError %s while packing RTP Packet\n", ivas_error_to_string( error ) ); + } + else if ( numFramesInPayload > 0 ) + { + if ( ( error = IvasRtpFile_Write( rtp->hRtpFile, rtp->rtpPacket.buffer, rtp->rtpPacket.length ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nError %s while writing RTP packet\n", ivas_error_to_string( error ) ); + } + } + } + IVAS_RTP_PACK_Close( &rtp->hPack ); + } + + if ( rtp->hUnpack != NULL ) + { + IVAS_RTP_UNPACK_Close( &rtp->hUnpack ); + } + + if ( rtp->f_piDataOut != NULL ) + { + fprintf( rtp->f_piDataOut, "\n}\n" ); + fclose( rtp->f_piDataOut ); + rtp->f_piDataOut = NULL; + } + + if ( rtp->hRtpFile != NULL ) + { + IvasRtpFile_Close( &rtp->hRtpFile ); + } + } +} + +ivas_error IVAS_RTP_WRITER_Init( IVAS_RTP *rtp, const char *outputBitstreamFilename, uint32_t numFramesPerPacket ) +{ + uint32_t SSRC = ( rand() & 0xFFFF ) | ( (uint32_t) rand() << 16 ); + ivas_error error = IVAS_ERR_OK; + + memset( rtp, 0, sizeof( IVAS_RTP ) ); + + rtp->packCfg.maxFramesPerPacket = numFramesPerPacket; + rtp->rtpPacket.buffer = rtp->packet; + rtp->rtpPacket.capacity = sizeof( rtp->packet ); + + error = IVAS_RTP_PACK_Open( &rtp->hPack, &rtp->packCfg ); + if ( error == IVAS_ERR_OK ) + { + /* Open the output file for RTPDump writing */ + error = IvasRtpFile_Open( outputBitstreamFilename, true, &rtp->hRtpFile ); + if ( error != IVAS_ERR_OK ) + { + return error; + } + + error = IVAS_RTP_PACK_UpdateHeader( rtp->hPack, SSRC, 0, NULL, 0, 0, NULL ); + if ( error != IVAS_ERR_OK ) + { + fprintf( stderr, "error in IVAS_RTP_PACK_UpdateHeader(): %d\n", error ); + } + } + + return error; +} + +ivas_error IVAS_RTP_READER_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->hRtpFile ); + 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; +} + +ivas_error IVAS_RTP_WriteNextFrame( IVAS_RTP *rtp, uint8_t *au, int16_t auSizeBits, bool isMono, bool forcePacket ) +{ + ivas_error error = IVAS_ERR_OK; + uint32_t nProcPiData = 0; + IVAS_DATA_BUFFER packedFrame = { 0, 0, NULL }; + + packedFrame.capacity = ( auSizeBits + 7 ) / 8; + packedFrame.length = ( auSizeBits + 7 ) / 8; + packedFrame.buffer = (uint8_t *) au; + rtp->rtpPacket.length = 0; + + /* Push Encoded Stream to */ + error = IVAS_RTP_PACK_PushFrame( rtp->hPack, + isMono ? IVAS_RTP_EVS : IVAS_RTP_IVAS, +#ifdef RTP_S4_251135_CR26253_0016_REV1 + NULL, +#endif /* RTP_S4_251135_CR26253_0016_REV1 */ + &packedFrame ); + if ( error != IVAS_ERR_OK ) + { + return error; + } + + while ( rtp->nWrittenPiData-- > 0 ) + { + PIDATA_TS *piDataTs = &rtp->piData[nProcPiData++]; + if ( ( error = IVAS_RTP_PACK_PushPiData( rtp->hPack, (const IVAS_PIDATA_GENERIC *) &piDataTs->data ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nError %s while pushing scene orientation\n", ivas_error_to_string( error ) ); + return error; + } + } + + if ( forcePacket || IVAS_RTP_PACK_GetNumFrames( rtp->hPack ) == rtp->packCfg.maxFramesPerPacket ) + { + uint32_t numFramesInPayload = 0; + + /* Generate RTP Packet */ + if ( ( error = IVAS_RTP_PACK_GetPacket( rtp->hPack, &rtp->rtpPacket, &numFramesInPayload ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nError %s while packing RTP Header\n", ivas_error_to_string( error ) ); + return error; + } + + if ( ( error = IvasRtpFile_Write( rtp->hRtpFile, rtp->rtpPacket.buffer, rtp->rtpPacket.length ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "\nError %s while writing RTP packet\n", ivas_error_to_string( error ) ); + return error; + } + } + + return error; +} + +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->hRtpFile, 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; +} diff --git a/lib_util/ivas_rtp_file.h b/lib_util/ivas_rtp_file.h index 3156c34cb2..5d3e9df1ab 100644 --- a/lib_util/ivas_rtp_file.h +++ b/lib_util/ivas_rtp_file.h @@ -37,29 +37,46 @@ #include #include "common_api_types.h" +#include "ivas_rtp_api.h" +#include "ivas_rtp_pi_data.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 */ -); +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 hRtpFile; + FILE *f_piDataOut; + IVAS_RTP_CODEC codecId; + uint32_t nWrittenPiData; + 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_PACK_HANDLE hPack; + IVAS_RTP_UNPACK_HANDLE hUnpack; + IVAS_RTP_PACK_CONFIG packCfg; + IVAS_RTP_UNPACK_CONFIG unpackCfg; +#ifdef RTP_S4_251135_CR26253_0016_REV1 + IVAS_RTP_SR_INFO srInfo; +#endif +} IVAS_RTP; + +ivas_error IVAS_RTP_WRITER_Init( IVAS_RTP *rtp, const char *outputBitstreamFilename, uint32_t numFramesPerPacket ); +ivas_error IVAS_RTP_READER_Init( IVAS_RTP *rtp, const char *inputBitstreamFilename, const char *piOutputFilename ); +void IVAS_RTP_Term( IVAS_RTP *rtp ); +ivas_error IVAS_RTP_WriteNextFrame( IVAS_RTP *rtp, uint8_t *au, int16_t auSizeBits, bool isMono, bool forcePacket ); +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 ); +void IVAS_RTP_LogPiData( FILE *f_piDataOut, PIDATA_TS *piData, uint32_t nPiDataPresent ); + #endif /* IVAS_RTP_FILE_H */ diff --git a/lib_util/ivas_rtp_pi_data.h b/lib_util/ivas_rtp_pi_data.h index aad50dd6b6..3787861f00 100644 --- a/lib_util/ivas_rtp_pi_data.h +++ b/lib_util/ivas_rtp_pi_data.h @@ -318,12 +318,12 @@ extern "C" typedef struct { - size_t size; /* sizeof(IVAS_PIDATA_AUDIO_FOCUS) */ - uint32_t piDataType; /* IVAS_PI_AUDIO_FOCUS_INDCATION or IVAS_PI_AUDIO_FOCUS_REQUEST */ - bool availDirection; /* audio focus contains direction */ - bool availLevel; /* audio focus contains level */ - IVAS_QUATERNION direction; /* direction data expressed as quarternions */ - IVAS_FLVL flvl; /* audio focus level */ + size_t size; /* sizeof(IVAS_PIDATA_AUDIO_FOCUS) */ + uint32_t piDataType; /* IVAS_PI_AUDIO_FOCUS_INDCATION or IVAS_PI_AUDIO_FOCUS_REQUEST */ + bool availDirection; /* audio focus contains direction */ + bool availLevel; /* audio focus contains level */ + IVAS_QUATERNION direction; /* direction data expressed as quarternions */ + IVAS_FLVL flvl; /* audio focus level */ } IVAS_PIDATA_AUDIO_FOCUS; /* Listener position */ @@ -466,6 +466,12 @@ extern "C" IVAS_PIDATA_NO_DATA noPiData; } PIDATA; + typedef struct + { + PIDATA data; + uint32_t timestamp; + } PIDATA_TS; + #endif /* IVAS_RTPDUMP */ #ifdef __cplusplus diff --git a/lib_util/rtpdump.c b/lib_util/rtpdump.c index 7129a514fd..f89b36f82a 100644 --- a/lib_util/rtpdump.c +++ b/lib_util/rtpdump.c @@ -276,34 +276,6 @@ 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 77e284eb84..5b8b31e8de 100644 --- a/lib_util/rtpdump.h +++ b/lib_util/rtpdump.h @@ -37,7 +37,6 @@ #pragma once #include #include -#include "options.h" #ifdef __cplusplus extern "C" @@ -84,11 +83,6 @@ 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 9bd1c4d6fb..54b602c995 100644 --- a/readme.txt +++ b/readme.txt @@ -249,10 +249,10 @@ 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 +-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. + EVS RTP Payload Format is used. Optional N represents number of frames per RTP packet -scene_orientation : Scene orientation trajectory file. Only used with rtpdump output. -device_orientation : Device orientation trajectory file. Only used with rtpdump output. diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py index 1eec45fdce..8d979a7e6d 100644 --- a/tests/rtp/ivasrtp.py +++ b/tests/rtp/ivasrtp.py @@ -616,7 +616,8 @@ def packAudioFocus(bitstrm: BitStream, data: any): if auFocus.direction is not None: packOrientations(bitstrm, [auFocus.direction]) if auFocus.level is not None: - bitstrm.append(f'uint:4={auFocus.level}') + level = int(auFocus.level) + bitstrm.append(f'uint:4={level}') bitstrm.append(f'uint:4=0') -- GitLab From 0527617eecee4d681ced10d2868c512eb1c786b2 Mon Sep 17 00:00:00 2001 From: Tapani Pihlajakuja Date: Thu, 2 Oct 2025 15:29:21 +0300 Subject: [PATCH 065/100] Apply 1 suggestion(s) to 1 file(s) Co-authored-by: Archit Tamarapu --- tests/renderer/test_renderer.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/renderer/test_renderer.py b/tests/renderer/test_renderer.py index 392ee07c2a..1f09782367 100644 --- a/tests/renderer/test_renderer.py +++ b/tests/renderer/test_renderer.py @@ -641,8 +641,6 @@ def test_masa_prerend( get_odg_bin, split_comparison, ): - if fs != "48kHz": - pytest.skip("MASA Prerendering at 16 and 32 kHz WIP") run_renderer( record_property, props_to_record, -- GitLab From 6421effed9a2bc982582086b4122f03ef161d282 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Thu, 2 Oct 2025 16:26:06 +0300 Subject: [PATCH 066/100] Remove PI appliance --- apps/decoder.c | 30 ------ lib_com/ivas_prot.h | 12 --- lib_dec/ivas_binRenderer_internal.c | 4 - lib_dec/ivas_dirac_dec.c | 4 - lib_dec/ivas_init_dec.c | 4 - lib_dec/ivas_mc_param_dec.c | 4 - lib_dec/ivas_mc_paramupmix_dec.c | 4 - lib_dec/ivas_mct_dec.c | 4 - lib_dec/ivas_output_config.c | 20 ---- lib_dec/lib_dec.c | 140 ---------------------------- lib_dec/lib_dec.h | 4 - lib_rend/ivas_prot_rend.h | 8 -- lib_rend/ivas_rotation.c | 102 -------------------- lib_rend/ivas_stat_rend.h | 4 - lib_util/evs_rtp_payload.c | 4 +- lib_util/evs_rtp_payload.h | 2 +- 16 files changed, 2 insertions(+), 348 deletions(-) diff --git a/apps/decoder.c b/apps/decoder.c index 0f5d0dd9a8..07ee7ce18d 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -130,7 +130,6 @@ typedef struct bool tsmEnabled; IVAS_RENDER_FRAMESIZE renderFramesize; #ifdef IVAS_RTPDUMP - bool applyPiData; char *piOutputFilename; #endif #ifdef DEBUGGING @@ -1100,7 +1099,6 @@ static bool parseCmdlIVAS_dec( arg->enableExternalOrientation = false; arg->externalOrientationTrajFileName = NULL; #ifdef IVAS_RTPDUMP - arg->applyPiData = false; arg->piOutputFilename = NULL; #endif @@ -1202,11 +1200,6 @@ static bool parseCmdlIVAS_dec( 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 ) @@ -1810,7 +1803,6 @@ static void usage_dec( void ) 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" ); @@ -3583,28 +3575,6 @@ 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; - uint32_t numPiData = 0; - - while ( nProcPiData + numPiData < ivasRtp.nReadPiData && - ivasRtp.piData[nProcPiData + numPiData].timestamp <= piTs ) - { - numPiData++; - } - - if ( ( error = IVAS_RTP_ApplyPiData( hIvasDec, &ivasRtp.piData[nProcPiData], numPiData ) ) != IVAS_ERR_OK ) - { - fprintf( stderr, "\nError in IVAS_DEC_VoIP_GetSamples: %s\n", IVAS_DEC_GetErrorMessage( error ) ); - goto cleanup; - } - - nProcPiData += numPiData; - } -#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 diff --git a/lib_com/ivas_prot.h b/lib_com/ivas_prot.h index ad1b34d2e2..895cb3ac07 100755 --- a/lib_com/ivas_prot.h +++ b/lib_com/ivas_prot.h @@ -806,19 +806,7 @@ 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_dec/ivas_binRenderer_internal.c b/lib_dec/ivas_binRenderer_internal.c index 426d8e9a47..8cda55017c 100644 --- a/lib_dec/ivas_binRenderer_internal.c +++ b/lib_dec/ivas_binRenderer_internal.c @@ -1030,11 +1030,7 @@ 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 cdc22f80f6..88141656c8 100644 --- a/lib_dec/ivas_dirac_dec.c +++ b/lib_dec/ivas_dirac_dec.c @@ -2072,11 +2072,7 @@ 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 400243e877..2c2f814ab8 100644 --- a/lib_dec/ivas_init_dec.c +++ b/lib_dec/ivas_init_dec.c @@ -2145,11 +2145,7 @@ 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 b43af8b70b..ed7c9fb0b4 100644 --- a/lib_dec/ivas_mc_param_dec.c +++ b/lib_dec/ivas_mc_param_dec.c @@ -401,11 +401,7 @@ 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 8df57ff8eb..5753d262a0 100644 --- a/lib_dec/ivas_mc_paramupmix_dec.c +++ b/lib_dec/ivas_mc_paramupmix_dec.c @@ -349,11 +349,7 @@ 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 3a0ba42df4..1032d9d389 100644 --- a/lib_dec/ivas_mct_dec.c +++ b/lib_dec/ivas_mct_dec.c @@ -1143,11 +1143,7 @@ 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 febcf78d15..d8d6c93608 100644 --- a/lib_dec/ivas_output_config.c +++ b/lib_dec/ivas_output_config.c @@ -70,11 +70,7 @@ 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; } @@ -158,11 +154,7 @@ 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 ); @@ -206,17 +198,9 @@ 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; @@ -232,11 +216,7 @@ 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/lib_dec.c b/lib_dec/lib_dec.c index 5c1edb06fc..7143c2f652 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -5032,143 +5032,3 @@ ivas_error IVAS_DEC_is_split_rendering_coded_out( return IVAS_ERR_OK; } - -#ifdef IVAS_RTPDUMP -#ifdef DEBUGGING -#define DEBUG_PRINT fprintf -#else -#define DEBUG_PRINT( ... ) -#endif - -/*---------------------------------------------------------------------* - * IVAS_DEC_feedSinglePIorientation( ) - * - * Feed a single orientation PI data to external orientation handle. - *---------------------------------------------------------------------*/ - -static 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_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 ) ); -} - -#ifdef RTP_S4_251135_CR26253_0016_REV1 -/*---------------------------------------------------------------------* - * IVAS_DEC_setDiegeticInput( ) - * - * Set isDiegeticInput flag for combined orientation handle based on PI data. - *---------------------------------------------------------------------*/ - -static void IVAS_DEC_setDiegeticInputPI( - IVAS_DEC_HANDLE hIvasDec, /* i/o: IVAS decoder handle */ - const bool *diegeticPIValues /* i : diegetic values for the input stream */ -) -{ - if ( hIvasDec->st_ivas->hCombinedOrientationData != NULL ) - { - int8_t i; - for ( i = 0; i < ( 1 + IVAS_MAX_NUM_OBJECTS ); i++ ) - { - hIvasDec->st_ivas->hCombinedOrientationData->isDiegeticInputPI[i] = diegeticPIValues[i]; - } - hIvasDec->st_ivas->hCombinedOrientationData->isDiegeticInputPISet = true; - } -} -#endif - -ivas_error IVAS_RTP_ApplyPiData( IVAS_DEC_HANDLE hIvasDec, PIDATA_TS *piData, uint32_t numPiData ) -{ - ivas_error error = IVAS_ERR_OK; - while ( numPiData-- ) - { - uint32_t piDataType = piData->data.noPiData.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; - -#ifdef RTP_S4_251135_CR26253_0016_REV1 - case IVAS_PI_DIEGETIC_TYPE: - { - DEBUG_PRINT( stdout, "PI_DIEGETIC_TYPE : %d, %d, %d, %d, %d\n", piData->data.digeticIndicator.isDiegetic[0], piData->data.digeticIndicator.isDiegetic[1], piData->data.digeticIndicator.isDiegetic[2], piData->data.digeticIndicator.isDiegetic[3], piData->data.digeticIndicator.isDiegetic[4] ); - IVAS_DEC_setDiegeticInputPI( hIvasDec, piData->data.digeticIndicator.isDiegetic ); - } - break; -#endif - - default: - { - /* NOT HANDLED PI DATA - DO NOTHING */ - } - break; - } - if ( error != IVAS_ERR_OK ) - { - return error; - } - } - return error; -} -#endif \ No newline at end of file diff --git a/lib_dec/lib_dec.h b/lib_dec/lib_dec.h index 242b601942..dac3f43a77 100644 --- a/lib_dec/lib_dec.h +++ b/lib_dec/lib_dec.h @@ -553,10 +553,6 @@ void IVAS_DEC_PrintDisclaimer( void ); -#ifdef IVAS_RTPDUMP -#include "ivas_rtp_pi_data.h" -ivas_error IVAS_RTP_ApplyPiData( IVAS_DEC_HANDLE hIvasDec, PIDATA_TS *piData, uint32_t numPiData ); -#endif /* clang-format on */ diff --git a/lib_rend/ivas_prot_rend.h b/lib_rend/ivas_prot_rend.h index 937923c338..32c2992c1a 100644 --- a/lib_rend/ivas_prot_rend.h +++ b/lib_rend/ivas_prot_rend.h @@ -1305,12 +1305,6 @@ 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 */ ); @@ -1358,7 +1352,6 @@ ivas_error ivas_render_config_init_from_rom( * Quaternion operations *----------------------------------------------------------------------------------*/ -#ifndef IVAS_RTPDUMP void QuaternionProduct( const IVAS_QUATERNION q1, const IVAS_QUATERNION q2, @@ -1370,7 +1363,6 @@ 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 6c6acb2653..9385bc753e 100644 --- a/lib_rend/ivas_rotation.c +++ b/lib_rend/ivas_rotation.c @@ -693,7 +693,6 @@ ivas_error ivas_external_orientation_open( const int16_t num_subframes /* i : number of subframes */ ) { -#ifndef IVAS_RTPDUMP int16_t i; IVAS_QUATERNION identity; @@ -701,16 +700,12 @@ 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++ ) { @@ -720,45 +715,10 @@ 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() * @@ -862,14 +822,6 @@ ivas_error ivas_combined_orientation_open( ( *hCombinedOrientationData )->subframe_idx = 0; ( *hCombinedOrientationData )->subframe_size = (int16_t) ( fs / ( FRAMES_PER_SEC * MAX_PARAM_SPATIAL_SUBFRAMES ) ); ( *hCombinedOrientationData )->cur_subframe_samples_rendered = 0; -#ifdef RTP_S4_251135_CR26253_0016_REV1 - - for ( i = 0; i < ( 1 + IVAS_MAX_NUM_OBJECTS ); i++ ) - { - ( *hCombinedOrientationData )->isDiegeticInputPI[i] = true; - } - ( *hCombinedOrientationData )->isDiegeticInputPISet = false; -#endif return IVAS_ERR_OK; } @@ -1030,30 +982,11 @@ ivas_error combine_external_and_head_orientations( } else if ( hExtOrientationData == NULL && headRotQuaternions != NULL ) { -#ifdef RTP_S4_251135_CR26253_0016_REV1 - /* Disable head rotation if diegetic PI data indicating non-diegetic audio is received */ - if ( hCombinedOrientationData->isDiegeticInputPISet && !hCombinedOrientationData->isDiegeticInputPI[0] && !hCombinedOrientationData->isDiegeticInputPI[1] && !hCombinedOrientationData->isDiegeticInputPI[2] && !hCombinedOrientationData->isDiegeticInputPI[3] && !hCombinedOrientationData->isDiegeticInputPI[4] ) - { - for ( i = 0; i < hCombinedOrientationData->num_subframes; i++ ) - { - hCombinedOrientationData->Quaternions[i] = identity; - } - } - else - { - /* Head rotation only */ - for ( i = 0; i < hCombinedOrientationData->num_subframes; i++ ) - { - hCombinedOrientationData->Quaternions[i] = headRotQuaternions[i]; - } - } -#else /* Head rotation only */ for ( i = 0; i < hCombinedOrientationData->num_subframes; i++ ) { hCombinedOrientationData->Quaternions[i] = headRotQuaternions[i]; } -#endif } if ( hExtOrientationData != NULL ) @@ -1130,40 +1063,6 @@ ivas_error combine_external_and_head_orientations( hCombinedOrientationData->Quaternion_frozen_head = identity; hCombinedOrientationData->isHeadRotationFrozen = 0; } -#ifdef RTP_S4_251135_CR26253_0016_REV1 - /* Disable head rotation if diegetic PI data indicating non-diegetic audio is received */ - if ( hCombinedOrientationData->isDiegeticInputPISet && !hCombinedOrientationData->isDiegeticInputPI[0] && !hCombinedOrientationData->isDiegeticInputPI[1] && !hCombinedOrientationData->isDiegeticInputPI[2] && !hCombinedOrientationData->isDiegeticInputPI[3] && !hCombinedOrientationData->isDiegeticInputPI[4] ) - { - continue; - } - else - { - /* Use the most recent head rotation */ - if ( hExtOrientationData->enableHeadRotation[i] == 1 ) - { - if ( hExtOrientationData->enableExternalOrientation[i] > 0 ) - { - QuaternionProduct( hCombinedOrientationData->Quaternions[i], headRotQuaternions[i], &hCombinedOrientationData->Quaternions[i] ); - } - else - { - hCombinedOrientationData->Quaternions[i] = headRotQuaternions[i]; - } - } - /* Use the freezed head rotation */ - else if ( hExtOrientationData->enableHeadRotation[i] == 2 ) - { - if ( hExtOrientationData->enableExternalOrientation[i] > 0 ) - { - QuaternionProduct( hCombinedOrientationData->Quaternions[i], hCombinedOrientationData->Quaternion_frozen_head, &hCombinedOrientationData->Quaternions[i] ); - } - else - { - hCombinedOrientationData->Quaternions[i] = hCombinedOrientationData->Quaternion_frozen_head; - } - } - } -#else /* Use the most recent head rotation */ if ( hExtOrientationData->enableHeadRotation[i] == 1 ) { @@ -1188,7 +1087,6 @@ ivas_error combine_external_and_head_orientations( hCombinedOrientationData->Quaternions[i] = hCombinedOrientationData->Quaternion_frozen_head; } } -#endif /* Reset the combined orientations to identity */ if ( hExtOrientationData->enableHeadRotation[i] == 0 && hExtOrientationData->enableExternalOrientation[i] == 0 ) diff --git a/lib_rend/ivas_stat_rend.h b/lib_rend/ivas_stat_rend.h index 3e6270e229..69fcb30fb8 100644 --- a/lib_rend/ivas_stat_rend.h +++ b/lib_rend/ivas_stat_rend.h @@ -710,10 +710,6 @@ typedef struct ivas_combined_orientation_struct int16_t cur_subframe_samples_rendered; int16_t subframe_idx_start; int16_t cur_subframe_samples_rendered_start; -#ifdef RTP_S4_251135_CR26253_0016_REV1 - bool isDiegeticInputPI[1 + IVAS_MAX_NUM_OBJECTS]; - bool isDiegeticInputPISet; -#endif } COMBINED_ORIENTATION_DATA, *COMBINED_ORIENTATION_HANDLE; /*----------------------------------------------------------------------------------* diff --git a/lib_util/evs_rtp_payload.c b/lib_util/evs_rtp_payload.c index 7fc0914892..809b6cc6f3 100644 --- a/lib_util/evs_rtp_payload.c +++ b/lib_util/evs_rtp_payload.c @@ -127,7 +127,7 @@ bool evsPayload_unpackFrame( bool hf_only, char *payload, uint16_t payloadSizeBy #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 ) +void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, int32_t *bitrate ) { bool evsModeBit = ( toc & 0x20 ) != 0; *isAMRWB_IOmode = evsModeBit; @@ -136,13 +136,11 @@ void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *fra 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]; } } diff --git a/lib_util/evs_rtp_payload.h b/lib_util/evs_rtp_payload.h index aea968ddff..2905d717ef 100644 --- a/lib_util/evs_rtp_payload.h +++ b/lib_util/evs_rtp_payload.h @@ -163,7 +163,7 @@ extern "C" #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 ); + void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bool *frameFollowing, uint16_t *frameTypeIndex, bool *qBit, 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 -- GitLab From 5de2f27ba7ac5749f94dd3c64b083568d6bdc362 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Thu, 2 Oct 2025 16:33:08 +0300 Subject: [PATCH 067/100] Clean up --- lib_dec/ivas_mct_dec.c | 1 + lib_dec/ivas_output_config.c | 1 + lib_dec/lib_dec.h | 1 - 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib_dec/ivas_mct_dec.c b/lib_dec/ivas_mct_dec.c index 1032d9d389..eaa094b68b 100644 --- a/lib_dec/ivas_mct_dec.c +++ b/lib_dec/ivas_mct_dec.c @@ -1143,6 +1143,7 @@ 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 ); + if ( ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation ) ) { efap_free_data( &st_ivas->hEFAPdata ); diff --git a/lib_dec/ivas_output_config.c b/lib_dec/ivas_output_config.c index d8d6c93608..b0acefc17e 100644 --- a/lib_dec/ivas_output_config.c +++ b/lib_dec/ivas_output_config.c @@ -70,6 +70,7 @@ void ivas_renderer_select( /*-----------------------------------------------------------------* * Binaural rendering configurations *-----------------------------------------------------------------*/ + if ( st_ivas->hDecoderConfig->Opt_Headrotation || st_ivas->hDecoderConfig->Opt_ExternalOrientation ) { st_ivas->hCombinedOrientationData->shd_rot_max_order = -1; diff --git a/lib_dec/lib_dec.h b/lib_dec/lib_dec.h index dac3f43a77..f28279d86b 100644 --- a/lib_dec/lib_dec.h +++ b/lib_dec/lib_dec.h @@ -553,7 +553,6 @@ void IVAS_DEC_PrintDisclaimer( void ); - /* clang-format on */ #endif -- GitLab From a630e5065803614bdfb17a890b1a2e1fb0fbff26 Mon Sep 17 00:00:00 2001 From: Ripinder Singh Date: Fri, 3 Oct 2025 00:33:24 +1000 Subject: [PATCH 068/100] Undo CMAKE and warning fixes Signed-off-by: Ripinder Singh --- CMakeLists.txt | 2 +- apps/decoder.c | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4dfb8a894c..de4727bec8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -165,7 +165,7 @@ file(GLOB libDecSrcs "lib_dec/*.c") file(GLOB libDecHeaders "lib_dec/*.h") add_library(lib_dec ${libDecSrcs} ${libDecHeaders}) target_link_libraries(lib_dec lib_com lib_rend lib_debug lib_isar) -target_include_directories(lib_dec PUBLIC lib_dec lib_rend PRIVATE lib_enc lib_isar lib_util) +target_include_directories(lib_dec PUBLIC lib_dec lib_rend PRIVATE lib_enc lib_isar) file(GLOB libUtilSrcs "lib_util/*.c") file(GLOB libUtilHeaders "lib_util/*.h") diff --git a/apps/decoder.c b/apps/decoder.c index 07ee7ce18d..07b11d4f47 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -3205,8 +3205,6 @@ static ivas_error decodeVoIP( #ifdef IVAS_RTPDUMP IVAS_RTP ivasRtp = { 0 }; IVAS_DEC_HANDLE hIvasDec = *phIvasDec; - int32_t initialTsOffsetSystemAndRTP = 0; - uint32_t nProcPiData = 0; #else FILE *f_rtpstream = NULL; EVS_RTPDUMP_DEPACKER rtpdumpDepacker; @@ -3326,7 +3324,6 @@ static ivas_error decodeVoIP( 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 -- GitLab From aafe02741207a80467472286ac7e3f28377e3f88 Mon Sep 17 00:00:00 2001 From: Ripinder Singh Date: Fri, 3 Oct 2025 11:00:18 +1000 Subject: [PATCH 069/100] Fix for build errors Signed-off-by: Ripinder Singh --- lib_dec/lib_dec.c | 2 +- lib_util/evs_rtp_payload.c | 26 +------------------------- lib_util/evs_rtp_payload.h | 10 +--------- 3 files changed, 3 insertions(+), 35 deletions(-) diff --git a/lib_dec/lib_dec.c b/lib_dec/lib_dec.c index 2ce919a2c8..87d4d06d65 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -346,11 +346,11 @@ void IVAS_DEC_Close( ( *phIvasDec )->hVoIP = NULL; } + /* destroy Split binaural renderer (ISAR) handle */ #ifndef IVAS_RTPDUMP ivas_destroy_handle_isar( &( *phIvasDec )->st_ivas->hSplitBinRend ); #endif - /* destroy Split binaural renderer (ISAR) handle */ if ( ( *phIvasDec )->st_ivas ) { #ifdef IVAS_RTPDUMP diff --git a/lib_util/evs_rtp_payload.c b/lib_util/evs_rtp_payload.c index 292da1c478..a0c514bad3 100644 --- a/lib_util/evs_rtp_payload.c +++ b/lib_util/evs_rtp_payload.c @@ -37,9 +37,7 @@ #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; @@ -124,27 +122,7 @@ 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, 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; - *bitrate = AMRWB_IOmode2rate[*frameTypeIndex]; - } - else - { - *qBit = false; /* Q-bit is unused in EVS Primary mode */ - *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; @@ -162,9 +140,8 @@ 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; @@ -296,4 +273,3 @@ 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 2905d717ef..357d985578 100644 --- a/lib_util/evs_rtp_payload.h +++ b/lib_util/evs_rtp_payload.h @@ -83,7 +83,6 @@ extern "C" 0 /* NO_DATA */ }; -#ifndef IVAS_RTPDUMP static const uint16_t evsPayloadProtectedSizes[22] = { 48, 56, @@ -160,14 +159,8 @@ 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, 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 @@ -202,7 +195,6 @@ static void evsHeaderFullPayload_parseToc( uint8_t toc, bool *isAMRWB_IOmode, bo uint16_t *frameSizeBits ); void EVS_RTPDUMP_DEPACKER_close( EVS_RTPDUMP_DEPACKER *self ); -#endif #ifdef __cplusplus } -- GitLab From e588a2be09f0381ebfd84ba2b13ef2b930f239c1 Mon Sep 17 00:00:00 2001 From: lintervo Date: Fri, 3 Oct 2025 09:34:51 +0300 Subject: [PATCH 070/100] Resolve MR comments --- lib_dec/ivas_jbm_dec.c | 4 +- lib_dec/ivas_omasa_dec.c | 4 +- lib_rend/ivas_dirac_dec_binaural_functions.c | 60 ++++++++++---------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/lib_dec/ivas_jbm_dec.c b/lib_dec/ivas_jbm_dec.c index 23fc1e041e..91522f3f3d 100644 --- a/lib_dec/ivas_jbm_dec.c +++ b/lib_dec/ivas_jbm_dec.c @@ -2907,9 +2907,9 @@ void ivas_dec_prepare_renderer( /* Gain MASA part, if edited */ if ( st_ivas->hMasaIsmData->masa_gain_is_edited ) { - for ( int16_t ch = 0; ch < 2; ch++ ) + for ( n = 0; n < CPE_CHANNELS; n++ ) { - v_multc( st_ivas->hTcBuffer->tc[ch], st_ivas->hMasaIsmData->gain_masa_edited, st_ivas->hTcBuffer->tc[ch], st_ivas->hTcBuffer->n_samples_available ); + v_multc( st_ivas->hTcBuffer->tc[n], st_ivas->hMasaIsmData->gain_masa_edited, st_ivas->hTcBuffer->tc[n], st_ivas->hTcBuffer->n_samples_available ); } } } diff --git a/lib_dec/ivas_omasa_dec.c b/lib_dec/ivas_omasa_dec.c index 346c6f591d..90b7c4632d 100644 --- a/lib_dec/ivas_omasa_dec.c +++ b/lib_dec/ivas_omasa_dec.c @@ -716,9 +716,9 @@ void ivas_omasa_dirac_rend_jbm( /* Gain MASA part, if edited in G192. MASA gaining with VOIP is done in ivas_dec_prepare_renderer() */ if ( !st_ivas->hDecoderConfig->Opt_tsm && st_ivas->hMasaIsmData->masa_gain_is_edited ) { - for ( int16_t ch = 0; ch < 2; ch++ ) + for ( n = 0; n < CPE_CHANNELS; n++ ) { - v_multc( output_f[ch], st_ivas->hMasaIsmData->gain_masa_edited, output_f[ch], *nSamplesRendered ); + v_multc( output_f[n], st_ivas->hMasaIsmData->gain_masa_edited, output_f[n], *nSamplesRendered ); } } } diff --git a/lib_rend/ivas_dirac_dec_binaural_functions.c b/lib_rend/ivas_dirac_dec_binaural_functions.c index 684180e239..6a162142e8 100644 --- a/lib_rend/ivas_dirac_dec_binaural_functions.c +++ b/lib_rend/ivas_dirac_dec_binaural_functions.c @@ -2766,7 +2766,7 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( } /* MASA gaining */ - for ( ch = 0; ch < 2; ch++ ) + for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { if ( masaGainEdited ) { @@ -2794,7 +2794,7 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( nSlotDiv = 1.0f / ( (float) nSlots ); /* Use diagonal mixing matrix as the instant mixing matrix, to slowly fade away the editing during dtx */ - for ( ch = 0; ch < 2; ch++ ) + for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { ismPreprocMtxNew[ch][ch] = 1.0f; ismPreprocMtxNew[1 - ch][ch] = 0.0f; @@ -2808,12 +2808,12 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( } /* Init out array */ - for ( int k = 0; k < nSlots; k++ ) + for ( slot = 0; slot < nSlots; slot++ ) { for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { - set_zero( outSlotRe[ch][k], CLDFB_NO_CHANNELS_MAX ); - set_zero( outSlotIm[ch][k], CLDFB_NO_CHANNELS_MAX ); + set_zero( outSlotRe[ch][slot], CLDFB_NO_CHANNELS_MAX ); + set_zero( outSlotIm[ch][slot], CLDFB_NO_CHANNELS_MAX ); } } @@ -2827,7 +2827,7 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( totalTargetEne = 0.0f; for ( slot = 0; slot < nSlots; slot++ ) { - for ( ch = 0; ch < 2; ch++ ) + for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { for ( bin = bin_lo; bin < bin_hi; bin++ ) { @@ -2838,9 +2838,9 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( } /* Get increment value for temporal interpolation */ - for ( inCh = 0; inCh < 2; inCh++ ) + for ( inCh = 0; inCh < BINAURAL_CHANNELS; inCh++ ) { - for ( outCh = 0; outCh < 2; outCh++ ) + for ( outCh = 0; outCh < BINAURAL_CHANNELS; outCh++ ) { ismPreprocMtxIncrement[outCh][inCh] = ( ismPreprocMtxNew[outCh][inCh] - hMasaIsmData->ismPreprocMatrix[outCh][inCh][band_idx] ) * nSlotDiv; } @@ -2851,11 +2851,11 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( hMasaIsmData->preprocEneRealized[band_idx] *= STEREO_PREPROCESS_IIR_FACTOR; hMasaIsmData->preprocEneTarget[band_idx] += totalTargetEne; - for ( outCh = 0; outCh < 2; outCh++ ) + for ( outCh = 0; outCh < BINAURAL_CHANNELS; outCh++ ) { for ( slot = 0; slot < nSlots; slot++ ) { - for ( inCh = 0; inCh < 2; inCh++ ) + for ( inCh = 0; inCh < BINAURAL_CHANNELS; inCh++ ) { hMasaIsmData->ismPreprocMatrix[outCh][inCh][band_idx] += ismPreprocMtxIncrement[outCh][inCh]; for ( bin = bin_lo; bin < bin_hi; bin++ ) @@ -2874,7 +2874,7 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( eqVal = fminf( 4.0f, sqrtf( hMasaIsmData->preprocEneTarget[band_idx] / fmaxf( 1e-12f, hMasaIsmData->preprocEneRealized[band_idx] ) ) ); - for ( ch = 0; ch < 2; ch++ ) + for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { for ( slot = 0; slot < nSlots; slot++ ) { @@ -2950,14 +2950,14 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( else { /* When not edited, input and output pan gains are the same */ - for ( ch = 0; ch < 2; ch++ ) + for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { panGainsOut[ismDirIndex][ch] = panGainsIn[ismDirIndex][ch]; } } /* Determine pan enes */ - for ( ch = 0; ch < 2; ch++ ) + for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { panEnesOut[ismDirIndex][ch] = panGainsOut[ismDirIndex][ch] * panGainsOut[ismDirIndex][ch]; panEnesIn[ismDirIndex][ch] = panGainsIn[ismDirIndex][ch] * panGainsIn[ismDirIndex][ch]; @@ -2982,12 +2982,12 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( } /* Init out array */ - for ( int k = 0; k < nSlots; k++ ) + for ( slot = 0; slot < nSlots; slot++ ) { for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { - set_zero( outSlotRe[ch][k], CLDFB_NO_CHANNELS_MAX ); - set_zero( outSlotIm[ch][k], CLDFB_NO_CHANNELS_MAX ); + set_zero( outSlotRe[ch][slot], CLDFB_NO_CHANNELS_MAX ); + set_zero( outSlotIm[ch][slot], CLDFB_NO_CHANNELS_MAX ); } } @@ -3017,7 +3017,7 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( /* Determine transport normalized energies and subframe energy */ for ( slot = 0; slot < nSlots; slot++ ) { - for ( ch = 0; ch < 2; ch++ ) + for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { for ( bin = bin_lo; bin < bin_hi; bin++ ) { @@ -3040,7 +3040,7 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( ratioAccOrig += ratio; /* Calculate MASA energy as a residual of original channel energies subtracted with ISM energies */ - for ( ch = 0; ch < 2; ch++ ) + for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { masaEneThisCh[ch] -= panEnesIn[ismDirIndex][ch] * ratio * subframeEne; } @@ -3058,7 +3058,7 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( ratio *= gainIsmThis * gainIsmThis; /* Determine panning energies and channel target energies */ - for ( ch = 0; ch < 2; ch++ ) + for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { ismTargetEneThisCh[ch] = panEnesIn[ismDirIndex][ch] * ismTargetEneThis; /* Ism target energy per channel */ totalTargetEneCh[ch] -= panEnesIn[ismDirIndex][ch] * ismEneThis; /* Reduce original ism energy */ @@ -3099,7 +3099,7 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( ratio *= gainMasaPow2; /* Calculate MASA target energies and add to total target energy estimation */ - for ( ch = 0; ch < 2; ch++ ) + for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { masaEneThisCh[ch] = fmaxf( masaEneThisCh[ch], 0.0f ); /* MASA original energy per channel */ masaTargetEneThisCh[ch] = gainMasaPow2 * masaEneThisCh[ch]; /* MASA target energy per channel */ @@ -3113,7 +3113,7 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( /* Limit target energies to non-negative values */ #ifdef NONBE_1399_1400_FIX_OBJ_EDIT_ISSUES - for ( ch = 0; ch < 2; ch++ ) + for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { totalTargetEneCh[ch] = max( totalTargetEneCh[ch], 0.0f ); } @@ -3183,14 +3183,14 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( if ( enableCentering ) { centeringFactor = fmaxf( 0.0f, 2.0f * fabsf( panEnesIn[ismDirIndex][0] - panEnesOut[ismDirIndex][0] ) - 1.0f ); - for ( ch = 0; ch < 2; ch++ ) + for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { panEnesOut[ismDirIndex][ch] *= ( 1.0f - centeringFactor ); panEnesOut[ismDirIndex][ch] += 0.5f * centeringFactor; } } - for ( ch = 0; ch < 2; ch++ ) + for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { eneMoveThis = fmaxf( 0.0f, panEnesIn[ismDirIndex][ch] - panEnesOut[ismDirIndex][ch] ); enePreserveThis = panEnesIn[ismDirIndex][ch] - eneMoveThis; @@ -3207,7 +3207,7 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( remainderNormEne = fmaxf( 0.0f, ( 1.0f - ismRatioAcc ) - normEnes[0] - normEnes[1] ); /* Normalize */ - for ( ch = 0; ch < 2; ch++ ) + for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { enePreserve[ch] += fmaxf( 0.0f, normEnes[ch] + remainderNormEne / 2.0f ); normVal = 1.0f / fmaxf( EPSILON, eneMove[ch] + enePreserve[ch] ); @@ -3222,7 +3222,7 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( /* Temporally average energy moving and preserving, and generate the transport signal preprocessing matrix for * gaining objects and moving objects between left and right */ - for ( ch = 0; ch < 2; ch++ ) + for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { hMasaIsmData->eneMoveIIR[ch][band_idx] *= STEREO_PREPROCESS_IIR_FACTOR; hMasaIsmData->eneMoveIIR[ch][band_idx] += eneMove[ch] * totalTargetEne; @@ -3234,9 +3234,9 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( } /* Get increment value for temporal interpolation */ - for ( inCh = 0; inCh < 2; inCh++ ) + for ( inCh = 0; inCh < BINAURAL_CHANNELS; inCh++ ) { - for ( outCh = 0; outCh < 2; outCh++ ) + for ( outCh = 0; outCh < BINAURAL_CHANNELS; outCh++ ) { ismPreprocMtxIncrement[outCh][inCh] = ( ismPreprocMtxNew[outCh][inCh] - hMasaIsmData->ismPreprocMatrix[outCh][inCh][band_idx] ) * nSlotDiv; } @@ -3247,11 +3247,11 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( hMasaIsmData->preprocEneRealized[band_idx] *= STEREO_PREPROCESS_IIR_FACTOR; hMasaIsmData->preprocEneTarget[band_idx] += totalTargetEne; - for ( outCh = 0; outCh < 2; outCh++ ) + for ( outCh = 0; outCh < BINAURAL_CHANNELS; outCh++ ) { for ( slot = 0; slot < nSlots; slot++ ) { - for ( inCh = 0; inCh < 2; inCh++ ) + for ( inCh = 0; inCh < BINAURAL_CHANNELS; inCh++ ) { hMasaIsmData->ismPreprocMatrix[outCh][inCh][band_idx] += ismPreprocMtxIncrement[outCh][inCh]; for ( bin = bin_lo; bin < bin_hi; bin++ ) @@ -3270,7 +3270,7 @@ void ivas_omasa_preProcessStereoTransportsForEditedObjects( eqVal = fminf( 4.0f, sqrtf( hMasaIsmData->preprocEneTarget[band_idx] / fmaxf( 1e-12f, hMasaIsmData->preprocEneRealized[band_idx] ) ) ); - for ( ch = 0; ch < 2; ch++ ) + for ( ch = 0; ch < BINAURAL_CHANNELS; ch++ ) { for ( slot = 0; slot < nSlots; slot++ ) { -- GitLab From 38f52b41c0e7a1190cdbef74ec4a4009ea0187e9 Mon Sep 17 00:00:00 2001 From: Markus Multrus Date: Sun, 5 Oct 2025 20:38:44 +0200 Subject: [PATCH 071/100] fix for TNS crash --- lib_com/options.h | 1 + lib_enc/cod_tcx.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/lib_com/options.h b/lib_com/options.h index e69dd22ab8..47ad9f8459 100755 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -173,6 +173,7 @@ #define FIX_1383_HEAD_TRACK_SANITIZER /* Nok: issue 1383: Fix head tracking struc values reading in renderer */ #define FIX_1385_INIT_IGF_STOP_FREQ /* FhG: Initialize infoIGFStopFreq in init_igf_dec() */ #define FIX_1387_INIT_PRM_SQQ /* FhG: initialize pointer prm_sqQ, which might be uninitialized in case of bfi == 1 */ +#define FIX_1349_TNS_CRASH /* FhG: Fix crash in TNS entropy coding, in case order of joint TNS coding is reduced to 0 */ /* #################### End BE switches ################################## */ diff --git a/lib_enc/cod_tcx.c b/lib_enc/cod_tcx.c index 89282ed8b4..fb2e7d9098 100644 --- a/lib_enc/cod_tcx.c +++ b/lib_enc/cod_tcx.c @@ -332,6 +332,15 @@ void TNSAnalysisStereo( pFilter[0]->order = pFilter[1]->order = maxOrder; } +#ifdef FIX_1349_TNS_CRASH + else + { + pFilter[0]->filterType = TNS_FILTER_OFF; + pFilter[1]->filterType = TNS_FILTER_OFF; + sts[0]->hTcxEnc->tnsData[k].nFilters = 0; + sts[1]->hTcxEnc->tnsData[k].nFilters = 0; + } +#endif } } } -- GitLab From d46964a3bae8384ed556613a687825dd72382680 Mon Sep 17 00:00:00 2001 From: Markus Multrus Date: Sun, 5 Oct 2025 20:58:13 +0200 Subject: [PATCH 072/100] formatting --- lib_enc/cod_tcx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_enc/cod_tcx.c b/lib_enc/cod_tcx.c index fb2e7d9098..9a890ae3c9 100644 --- a/lib_enc/cod_tcx.c +++ b/lib_enc/cod_tcx.c @@ -332,7 +332,7 @@ void TNSAnalysisStereo( pFilter[0]->order = pFilter[1]->order = maxOrder; } -#ifdef FIX_1349_TNS_CRASH +#ifdef FIX_1349_TNS_CRASH else { pFilter[0]->filterType = TNS_FILTER_OFF; -- GitLab From f294a1fcf923682bf145f1bc68e10712a0fd9d73 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 6 Oct 2025 09:39:27 +0200 Subject: [PATCH 073/100] reduce pytest verbosity in conformance test otherwise, too much is being printed and the results are not shown --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6e68613b54..91017da125 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1380,10 +1380,10 @@ ivas-conformance-linux: # Reference creation - python3 scripts/prepare_combined_format_inputs.py - TEST_SET="tests/codec_be_on_mr_nonselection tests/renderer/test_renderer.py tests/split_rendering/test_split_rendering.py" - - python3 -m pytest $TEST_SET -v -n auto --update_ref 1 --create_ref --keep_files + - python3 -m pytest -q $TEST_SET -v -n auto --update_ref 1 --create_ref --keep_files # Output creation - - python3 -m pytest $TEST_SET -v -n auto --keep_files --create_cut --html=report_cmd.html --self-contained-html + - python3 -m pytest -q $TEST_SET -v -n auto --keep_files --create_cut --html=report_cmd.html --self-contained-html - python3 scripts/parse_commands.py report_cmd.html Readme_IVAS.txt # Copy input data and output ref data @@ -1431,7 +1431,7 @@ ivas-conformance-linux: # Test run generated scripts in testvec - cd testvec - exit_code=0 - - python3 -m pytest conformance-test/test_26252.py --junit-xml=report-junit.xml --html=report.html --self-contained-html || exit_code=$? + - python3 -m pytest -q conformance-test/test_26252.py --junit-xml=report-junit.xml --html=report.html --self-contained-html || exit_code=$? - mv report.html .. - mv report-junit.xml .. -- GitLab From 4a5bdb48afec429d7fc0b39d6d5e25db294faf69 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Mon, 6 Oct 2025 11:48:45 +0300 Subject: [PATCH 074/100] Add xfail for rtpdump tests --- tests/codec_be_on_mr_nonselection/test_param_file.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/codec_be_on_mr_nonselection/test_param_file.py b/tests/codec_be_on_mr_nonselection/test_param_file.py index d2acacd39e..0e0e082c63 100644 --- a/tests/codec_be_on_mr_nonselection/test_param_file.py +++ b/tests/codec_be_on_mr_nonselection/test_param_file.py @@ -228,6 +228,9 @@ def test_param_file_tests( ): enc_opts, dec_opts, sim_opts, eid_opts = param_file_test_dict[test_tag] + if "rtpdump" in test_tag: + pytest.xfail("Skip RTP tests for now") + run_test( test_info, props_to_record, -- GitLab From a5f96a1cae65296945a89b1a5c90b4ef77cf41e9 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 6 Oct 2025 10:55:08 +0200 Subject: [PATCH 075/100] add pl type "coverage" + trigger conformance test with that --- .gitlab-ci.yml | 9 +++++++-- .gitlab-ci/variables.yml | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 91017da125..2053cf2688 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,6 +48,9 @@ workflow: - if: $CI_PIPELINE_SOURCE == 'web' && $MANUAL_PIPELINE_TYPE == 'test-branch-vs-input-passthrough' variables: IVAS_PIPELINE_NAME: "Pass-through comparison vs input: $CI_COMMIT_BRANCH" + - if: $MANUAL_PIPELINE_TYPE == 'coverage' + variables: + IVAS_PIPELINE_NAME: "Coverage measurement" # --------------------------------------------------------------- # Generic script anchors @@ -1366,6 +1369,8 @@ ivas-conformance-linux: timeout: "90 minutes" rules: - if: ($CI_PIPELINE_SOURCE == 'web' || $CI_PIPELINE_SOURCE == 'trigger') && $MANUAL_PIPELINE_TYPE == 'ivas-conformance-linux' + - if: $MANUAL_PIPELINE_TYPE == "coverage" + - if: $COVERAGE_TEST allow_failure: exit_codes: - 123 @@ -2194,8 +2199,8 @@ sanitizer-test-osba-planar-hoa3-ism4: - ivas-linux-fast stage: test rules: - # only run in scheduled pipeline that passes this env vars - if: $COVERAGE_TEST + - if: $MANUAL_PIPELINE_TYPE == "coverage" timeout: 3 hours before_script: - !reference [.job-linux, before_script] @@ -2232,8 +2237,8 @@ coverage-merge: - ivas-linux-fast stage: test rules: - # only run in scheduled pipeline that passes this env vars - if: $COVERAGE_TEST + - if: $MANUAL_PIPELINE_TYPE == "coverage" needs: - job: coverage-test-on-main-scheduled-stv artifacts: true diff --git a/.gitlab-ci/variables.yml b/.gitlab-ci/variables.yml index 9e60194a6c..52b811a001 100644 --- a/.gitlab-ci/variables.yml +++ b/.gitlab-ci/variables.yml @@ -18,6 +18,7 @@ variables: - 'ivas-conformance-linux' - 'check-clipping' - 'test-branch-vs-input-passthrough' + - 'coverage' PYTEST_ARGS: "" LONG_TEST_SUITE: "tests/codec_be_on_mr_nonselection tests/renderer --param_file scripts/config/self_test_ltv.prm --use_ltv" -- GitLab From b0e2ba6357c8478edcf8ce780d8150b09ab42e8e Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 6 Oct 2025 12:51:11 +0200 Subject: [PATCH 076/100] use variables for coverage files/dirs + fix pwd stripping --- .gitlab-ci.yml | 44 ++++++++++++++++++++++++---------------- .gitlab-ci/variables.yml | 4 ++++ 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2053cf2688..f9e6e15c80 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1374,6 +1374,10 @@ ivas-conformance-linux: allow_failure: exit_codes: - 123 + variables: + COVERAGE_OUTPUT_FILE: "$COVERAGE_OUTPUT_FILE_CONFORMANCE" + COVERAGE_OUTPUT_DIR: "coverage_conformance" + COVERAGE_TITLE: "conformance test test_26252.py" script: - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/print-common-info.sh - make -j @@ -1442,13 +1446,13 @@ ivas-conformance-linux: # Collect coverage - cd - - - lcov -c -d obj -o coverage.info - - lcov -r coverage.info "*apps*" -o coverage.info - - lcov -r coverage.info "*lib_util*" -o coverage.info + - lcov -c -d obj -o $COVERAGE_OUTPUT_FILE + - lcov -r $COVERAGE_OUTPUT_FILE "*apps*" -o $COVERAGE_OUTPUT_FILE + - lcov -r $COVERAGE_OUTPUT_FILE "*lib_util*" -o $COVERAGE_OUTPUT_FILE - commit_sha=$(git rev-parse HEAD) # replace hardcoded path with relative path to allow easy merging of .info files - - sed -i "s|$(pwd)|..|g" coverage.info - - genhtml coverage.info -o coverage -t "Coverage on main @ $commit_sha" + - sed -i "s|$(pwd)|.|g" coverage.info + - genhtml $COVERAGE_OUTPUT_FILE -o $COVERAGE_OUTPUT_DIR -t "Coverage on main -- $COVERAGE_TITLE @ $commit_sha" # Check for failures - if [ $exit_code -ne 0 ]; then echo "Test failures encountered"; exit $EXIT_CODE_FAIL; fi @@ -1467,8 +1471,8 @@ ivas-conformance-linux: - Readme_IVAS_JBM_dec.txt - Readme_IVAS_ISAR_dec.txt - Readme_IVAS_ISAR_post_rend.txt - - coverage.info - - coverage + - $COVERAGE_OUTPUT_FILE + - $COVERAGE_OUTPUT_DIR expose_as: "Draft IVAS conformance -- Linux" reports: junit: report-junit.xml @@ -2224,7 +2228,7 @@ sanitizer-test-osba-planar-hoa3-ism4: - lcov -r $COVERAGE_OUTPUT_FILE "*lib_util*" -o $COVERAGE_OUTPUT_FILE - commit_sha=$(git rev-parse HEAD) # replace hardcoded path with relative path to allow easy merging of .info files - - sed -i "s|$(pwd)|..|g" $COVERAGE_OUTPUT_FILE + - sed -i "s|$(pwd)|.|g" $COVERAGE_OUTPUT_FILE - genhtml $COVERAGE_OUTPUT_FILE -o $COVERAGE_OUTPUT_DIR -t "Coverage on main -- $COVERAGE_TITLE @ $commit_sha" artifacts: name: "main-$COVERAGE_OUTPUT_DIR-sha-$CI_COMMIT_SHORT_SHA" @@ -2246,23 +2250,27 @@ coverage-merge: artifacts: true - job: ivas-conformance-linux artifacts: true + variables: + COVERAGE_OUTPUT_FILE: "$COVERAGE_OUTPUT_FILE_MERGED" + COVERAGE_OUTPUT_DIR: "coverage-merged" + COVERAGE_TITLE: "merged total coverage" script: - echo "Merging coverage reports..." - - lcov -a coverage.info -a coverage_stv.info -a coverage_ltv.info -o coverage_merged.info + - lcov -a $COVERAGE_OUTPUT_FILE_CONFORMANCE -a $COVERAGE_OUTPUT_FILE_STV -a $COVERAGE_OUTPUT_FILE_LTV -o "$COVERAGE_OUTPUT_FILE_MERGED" - commit_sha=$(git rev-parse HEAD) - - genhtml coverage_merged.info -o coverage_merged -t "Merged Coverage Report @ $commit_sha" + - genhtml $COVERAGE_OUTPUT_FILE_MERGED -o $COVERAGE_OUTPUT_DIR -t "Coverage on main -- $COVERAGE_TITLE @ $commit_sha" artifacts: name: "merged-coverage-sha-$CI_COMMIT_SHORT_SHA" when: always expire_in: 1 week paths: - - coverage_merged.info - - coverage_merged + - $COVERAGE_OUTPUT_FILE + - $COVERAGE_OUTPUT_DIR coverage-test-on-main-scheduled-stv: extends: .coverage-test-template variables: - COVERAGE_OUTPUT_FILE: "coverage_stv.info" + COVERAGE_OUTPUT_FILE: "$COVERAGE_OUTPUT_FILE_STV" COVERAGE_OUTPUT_DIR: "coverage_stv" COVERAGE_TITLE: "short test vectors" script: @@ -2298,13 +2306,13 @@ coverage-test-on-main-scheduled-stv: # TODO consider including check-clipping, but this scales ALL testv files. could be added at end of this job artifacts: paths: - - coverage_stv.info - - coverage_stv + - $COVERAGE_OUTPUT_FILE + - $COVERAGE_OUTPUT_DIR coverage-test-on-main-scheduled-ltv: extends: .coverage-test-template variables: - COVERAGE_OUTPUT_FILE: "coverage_ltv.info" + COVERAGE_OUTPUT_FILE: "$COVERAGE_OUTPUT_FILE_LTV" COVERAGE_OUTPUT_DIR: "coverage_ltv" COVERAGE_TITLE: "long test vectors" script: @@ -2327,8 +2335,8 @@ coverage-test-on-main-scheduled-ltv: # VoIP BE tests don't support LTV - skipped here artifacts: paths: - - coverage_ltv.info - - coverage_ltv + - $COVERAGE_OUTPUT_FILE + - $COVERAGE_OUTPUT_DIR # --------------------------------------------------------------- # Complexity measurement jobs diff --git a/.gitlab-ci/variables.yml b/.gitlab-ci/variables.yml index 52b811a001..b70de66596 100644 --- a/.gitlab-ci/variables.yml +++ b/.gitlab-ci/variables.yml @@ -29,3 +29,7 @@ variables: TEST_SUITE: "" # note: currently overwrites default value from ci repo TESTCASE_TIMEOUT_STV_SANITIZERS: 240 + COVERAGE_OUTPUT_FILE_STV: "coverage-stv.info" + COVERAGE_OUTPUT_FILE_LTV: "coverage-ltv.info" + COVERAGE_OUTPUT_FILE_CONFORMANCE: "coverage-conformance.info" + COVERAGE_OUTPUT_FILE_MERGED: "coverage-merged.info" -- GitLab From 40a41ba041a2d9941cc83bcb7a748f33da3fdb61 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 6 Oct 2025 13:28:35 +0200 Subject: [PATCH 077/100] fix - use variable also in this spot --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f9e6e15c80..5682b3205e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1451,7 +1451,7 @@ ivas-conformance-linux: - lcov -r $COVERAGE_OUTPUT_FILE "*lib_util*" -o $COVERAGE_OUTPUT_FILE - commit_sha=$(git rev-parse HEAD) # replace hardcoded path with relative path to allow easy merging of .info files - - sed -i "s|$(pwd)|.|g" coverage.info + - sed -i "s|$(pwd)|.|g" $COVERAGE_OUTPUT_FILE - genhtml $COVERAGE_OUTPUT_FILE -o $COVERAGE_OUTPUT_DIR -t "Coverage on main -- $COVERAGE_TITLE @ $commit_sha" # Check for failures -- GitLab From 8345f5f0d2b2fbd41ddca93aa035202b480298bd Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 6 Oct 2025 13:30:47 +0200 Subject: [PATCH 078/100] [revert-me] reduce number of tests for faster iteration --- .gitlab-ci.yml | 86 +++++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5682b3205e..9b5b45dd0d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1389,10 +1389,10 @@ ivas-conformance-linux: # Reference creation - python3 scripts/prepare_combined_format_inputs.py - TEST_SET="tests/codec_be_on_mr_nonselection tests/renderer/test_renderer.py tests/split_rendering/test_split_rendering.py" - - python3 -m pytest -q $TEST_SET -v -n auto --update_ref 1 --create_ref --keep_files + - python3 -m pytest -k stereo -q $TEST_SET -v -n auto --update_ref 1 --create_ref --keep_files # Output creation - - python3 -m pytest -q $TEST_SET -v -n auto --keep_files --create_cut --html=report_cmd.html --self-contained-html + - python3 -m pytest -k stereo -q $TEST_SET -v -n auto --keep_files --create_cut --html=report_cmd.html --self-contained-html - python3 scripts/parse_commands.py report_cmd.html Readme_IVAS.txt # Copy input data and output ref data @@ -1440,7 +1440,7 @@ ivas-conformance-linux: # Test run generated scripts in testvec - cd testvec - exit_code=0 - - python3 -m pytest -q conformance-test/test_26252.py --junit-xml=report-junit.xml --html=report.html --self-contained-html || exit_code=$? + - python3 -m pytest -k stereo -q conformance-test/test_26252.py --junit-xml=report-junit.xml --html=report.html --self-contained-html || exit_code=$? - mv report.html .. - mv report-junit.xml .. @@ -2276,33 +2276,33 @@ coverage-test-on-main-scheduled-stv: script: # -- Run short test vector suite to check coverage of that first # codec smoke test - - bash ci/smoke_test.sh coverage + # - bash ci/smoke_test.sh coverage # default renderer framesize is 20ms - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v -n auto -q --tb=no --update_ref 1 --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec - # for different renderer framesizes, only the decoder needs to be tested, so use --decoder_only - # need to ignore non-zero exit codes as limiter is active and thus the different framesiszes will not be BE in all cases - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only || true - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only || true - # run renderer smoke test - needed for REFVEC tests - - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py - # run renderer test only with REF creation stage - no CUT needed since executables are identical - - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --create_ref - # run split rendering smoke test and REF creation - - python3 -m pytest -n auto -q --tb=no tests/split_rendering/test_split_rendering.py - - python3 -m pytest -n auto -q --tb=no tests/split_rendering/test_split_rendering.py --create_ref - # VoIP BE test - - python3 -m pytest -q --tb=no tests/test_be_for_jbm_neutral_dly_profile.py - # split rendering VoIP BE test - - python3 -m pytest -n auto -q --tb=no tests/split_rendering/test_voip_be_splitrend_vs_binaural.py - # TODO first frame SID - this script runs a `make clean` and needs to be checked/adapted - # - bash ci/run-first-frame-is-sid-test.sh - # TODO consider HRTF binary loading test, or probably already covered by PRM files - # - python3 -m pytest -q --tb=no tests/hrtf_binary_loading - # run BE vs EXT test - - python3 tests/create_short_testvectors.py --cut_len 1.0 - - python3 -m pytest -q --tb=no tests/test_be_for_ext_outputs.py - # run LC3plus wrapper unit test - - scripts/split_rendering/lc3plus_float/ivas_lc3plus_unit_test + - python3 -m pytest -k stereo $TESTS_DIR_CODEC_BE_ON_MR -v -n auto -q --tb=no --update_ref 1 --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec + # # for different renderer framesizes, only the decoder needs to be tested, so use --decoder_only + # # need to ignore non-zero exit codes as limiter is active and thus the different framesiszes will not be BE in all cases + # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only || true + # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only || true + # # run renderer smoke test - needed for REFVEC tests + # - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py + # # run renderer test only with REF creation stage - no CUT needed since executables are identical + # - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --create_ref + # # run split rendering smoke test and REF creation + # - python3 -m pytest -n auto -q --tb=no tests/split_rendering/test_split_rendering.py + # - python3 -m pytest -n auto -q --tb=no tests/split_rendering/test_split_rendering.py --create_ref + # # VoIP BE test + # - python3 -m pytest -q --tb=no tests/test_be_for_jbm_neutral_dly_profile.py + # # split rendering VoIP BE test + # - python3 -m pytest -n auto -q --tb=no tests/split_rendering/test_voip_be_splitrend_vs_binaural.py + # # TODO first frame SID - this script runs a `make clean` and needs to be checked/adapted + # # - bash ci/run-first-frame-is-sid-test.sh + # # TODO consider HRTF binary loading test, or probably already covered by PRM files + # # - python3 -m pytest -q --tb=no tests/hrtf_binary_loading + # # run BE vs EXT test + # - python3 tests/create_short_testvectors.py --cut_len 1.0 + # - python3 -m pytest -q --tb=no tests/test_be_for_ext_outputs.py + # # run LC3plus wrapper unit test + # - scripts/split_rendering/lc3plus_float/ivas_lc3plus_unit_test # TODO consider including check-clipping, but this scales ALL testv files. could be added at end of this job artifacts: paths: @@ -2317,20 +2317,20 @@ coverage-test-on-main-scheduled-ltv: COVERAGE_TITLE: "long test vectors" script: # -- Add extended coverage of EVS test and long test vectors - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_evs.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec - # default renderer framesize is 20ms - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_ltv.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec - # for different renderer framesizes, only the decoder needs to be tested, so use --decoder_only - # need to ignore non-zero exit codes as limiter is active and thus the different framesiszes will not be BE in all cases - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --param_file scripts/config/self_test_ltv.prm --dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 5 || true - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --param_file scripts/config/self_test_ltv.prm --dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 10 || true - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --update_ref 1 --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec --use_ltv - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only --use_ltv || true - - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only --use_ltv || true - # run renderer smoke test - needed for REFVEC tests - - ls -altr scripts/testv - # LTV smoketest is the same as REF creation stage for renderer - - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --use_ltv + - python3 -m pytest -k stereo $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_evs.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec + # # default renderer framesize is 20ms + # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_ltv.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec + # # for different renderer framesizes, only the decoder needs to be tested, so use --decoder_only + # # need to ignore non-zero exit codes as limiter is active and thus the different framesiszes will not be BE in all cases + # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --param_file scripts/config/self_test_ltv.prm --dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 5 || true + # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --param_file scripts/config/self_test_ltv.prm --dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 10 || true + # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --update_ref 1 --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec --use_ltv + # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only --use_ltv || true + # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only --use_ltv || true + # # run renderer smoke test - needed for REFVEC tests + # - ls -altr scripts/testv + # # LTV smoketest is the same as REF creation stage for renderer + # - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --use_ltv # split rendering test doesn't support LTV - skipped here # VoIP BE tests don't support LTV - skipped here artifacts: -- GitLab From 0f6023ff51c0f8802f1e8894b28595e8d8767221 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 6 Oct 2025 13:40:02 +0200 Subject: [PATCH 079/100] fix reduction of testcase in LTV test --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9b5b45dd0d..1fcf22f5c1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2317,9 +2317,9 @@ coverage-test-on-main-scheduled-ltv: COVERAGE_TITLE: "long test vectors" script: # -- Add extended coverage of EVS test and long test vectors - - python3 -m pytest -k stereo $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_evs.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec + # - python3 -m pytest -k stereo $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_evs.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec # # default renderer framesize is 20ms - # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_ltv.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec + - python3 -m pytest -k stereo $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_ltv.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec # # for different renderer framesizes, only the decoder needs to be tested, so use --decoder_only # # need to ignore non-zero exit codes as limiter is active and thus the different framesiszes will not be BE in all cases # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --param_file scripts/config/self_test_ltv.prm --dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 5 || true -- GitLab From 8a9bdb93103c565c823b059ff42b23c8f335b1e4 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 6 Oct 2025 14:18:11 +0200 Subject: [PATCH 080/100] use ci snippet for coverage collection --- .gitlab-ci.yml | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1fcf22f5c1..1710466465 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1446,13 +1446,7 @@ ivas-conformance-linux: # Collect coverage - cd - - - lcov -c -d obj -o $COVERAGE_OUTPUT_FILE - - lcov -r $COVERAGE_OUTPUT_FILE "*apps*" -o $COVERAGE_OUTPUT_FILE - - lcov -r $COVERAGE_OUTPUT_FILE "*lib_util*" -o $COVERAGE_OUTPUT_FILE - - commit_sha=$(git rev-parse HEAD) - # replace hardcoded path with relative path to allow easy merging of .info files - - sed -i "s|$(pwd)|.|g" $COVERAGE_OUTPUT_FILE - - genhtml $COVERAGE_OUTPUT_FILE -o $COVERAGE_OUTPUT_DIR -t "Coverage on main -- $COVERAGE_TITLE @ $commit_sha" + - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/collect-coverage.sh --obj-dir obj --output-file $COVERAGE_OUTPUT_FILE --output-dir $COVERAGE_OUTPUT_DIR --title "Coverage on main -- $COVERAGE_TITLE" # Check for failures - if [ $exit_code -ne 0 ]; then echo "Test failures encountered"; exit $EXIT_CODE_FAIL; fi @@ -2222,14 +2216,7 @@ sanitizer-test-osba-planar-hoa3-ism4: - cp IVAS_rend IVAS_rend_ref - cp ISAR_post_rend ISAR_post_rend_ref after_script: - - lcov -c -d cmake-build -o $COVERAGE_OUTPUT_FILE - # remove apps and lib_util files from coverage - - lcov -r $COVERAGE_OUTPUT_FILE "*apps*" -o $COVERAGE_OUTPUT_FILE - - lcov -r $COVERAGE_OUTPUT_FILE "*lib_util*" -o $COVERAGE_OUTPUT_FILE - - commit_sha=$(git rev-parse HEAD) - # replace hardcoded path with relative path to allow easy merging of .info files - - sed -i "s|$(pwd)|.|g" $COVERAGE_OUTPUT_FILE - - genhtml $COVERAGE_OUTPUT_FILE -o $COVERAGE_OUTPUT_DIR -t "Coverage on main -- $COVERAGE_TITLE @ $commit_sha" + - bash "${CI_PROJECT_DIR}"/ivas-codec-ci/snippets/collect-coverage.sh --obj-dir cmake-build --output-file $COVERAGE_OUTPUT_FILE --output-dir $COVERAGE_OUTPUT_DIR --title "Coverage on main -- $COVERAGE_TITLE" artifacts: name: "main-$COVERAGE_OUTPUT_DIR-sha-$CI_COMMIT_SHORT_SHA" when: always -- GitLab From cecfb716842d67174996a8e8a984a91a4bef775d Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 6 Oct 2025 14:18:51 +0200 Subject: [PATCH 081/100] [revert-me] change CI ref for testing new snippet --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1710466465..22e926b7a9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,7 @@ variables: # note: GitLab cannot reference variables defined by users in the include ref:, we need to use a YAML anchor for this # see https://docs.gitlab.com/ci/yaml/includes/#use-variables-with-include for more information - IVAS_CODEC_CI_REF: &IVAS_CODEC_CI_REF main + IVAS_CODEC_CI_REF: &IVAS_CODEC_CI_REF add-snippet-for-coverage-collection include: - local: .gitlab-ci/variables.yml -- GitLab From 511469b346da152b37873b8151d190cea04c5722 Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Mon, 6 Oct 2025 15:03:20 +0300 Subject: [PATCH 082/100] Fix windows build issues --- Workspace_msvc/lib_dec.vcxproj | 2 +- Workspace_msvc/lib_util.vcxproj | 15 ++++++--- Workspace_msvc/lib_util.vcxproj.filters | 43 ++++++++++++++++++------- 3 files changed, 44 insertions(+), 16 deletions(-) diff --git a/Workspace_msvc/lib_dec.vcxproj b/Workspace_msvc/lib_dec.vcxproj index 3c7ea3e597..a1710ca86b 100644 --- a/Workspace_msvc/lib_dec.vcxproj +++ b/Workspace_msvc/lib_dec.vcxproj @@ -68,7 +68,7 @@ Disabled - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_isar;..\lib_rend;..\lib_lc3plus;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_isar;..\lib_rend;..\lib_lc3plus;..\lib_util;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);WIN32;%(PreprocessorDefinitions) EnableFastChecks diff --git a/Workspace_msvc/lib_util.vcxproj b/Workspace_msvc/lib_util.vcxproj index 7c10648b72..85859e257c 100644 --- a/Workspace_msvc/lib_util.vcxproj +++ b/Workspace_msvc/lib_util.vcxproj @@ -110,11 +110,12 @@ - - + + + @@ -128,7 +129,6 @@ - @@ -144,6 +144,14 @@ + + + + + + + + @@ -157,7 +165,6 @@ - diff --git a/Workspace_msvc/lib_util.vcxproj.filters b/Workspace_msvc/lib_util.vcxproj.filters index ed062708ad..07c8fdfe43 100644 --- a/Workspace_msvc/lib_util.vcxproj.filters +++ b/Workspace_msvc/lib_util.vcxproj.filters @@ -67,9 +67,6 @@ util_c - - util_c - util_c @@ -82,19 +79,22 @@ util_c - + util_c - + util_c - + util_c - + util_c - + + util_c + + util_c @@ -165,9 +165,6 @@ util_h - - util_h - util_h @@ -186,6 +183,30 @@ util_h + + util_h + + + util_h + + + util_h + + + util_h + + + util_h + + + util_h + + + util_h + + + util_h + -- GitLab From c6ebdc86dec5efbb7676e32cef83d4457b8a1e7c Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Mon, 6 Oct 2025 15:24:45 +0300 Subject: [PATCH 083/100] Remove lib_util from include directories for lib_dec windows build --- Workspace_msvc/lib_dec.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Workspace_msvc/lib_dec.vcxproj b/Workspace_msvc/lib_dec.vcxproj index a1710ca86b..3c7ea3e597 100644 --- a/Workspace_msvc/lib_dec.vcxproj +++ b/Workspace_msvc/lib_dec.vcxproj @@ -68,7 +68,7 @@ Disabled - ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_isar;..\lib_rend;..\lib_lc3plus;..\lib_util;%(AdditionalIncludeDirectories) + ..\lib_com;..\lib_debug;..\lib_dec;..\lib_enc;..\lib_isar;..\lib_rend;..\lib_lc3plus;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;$(Macros);WIN32;%(PreprocessorDefinitions) EnableFastChecks -- GitLab From d8e91ec508be3476ab07e5bfa488b6cfd2715a6c Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 6 Oct 2025 15:07:58 +0200 Subject: [PATCH 084/100] Revert "[revert-me] change CI ref for testing new snippet" This reverts commit cecfb716842d67174996a8e8a984a91a4bef775d. --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 22e926b7a9..1710466465 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,7 @@ variables: # note: GitLab cannot reference variables defined by users in the include ref:, we need to use a YAML anchor for this # see https://docs.gitlab.com/ci/yaml/includes/#use-variables-with-include for more information - IVAS_CODEC_CI_REF: &IVAS_CODEC_CI_REF add-snippet-for-coverage-collection + IVAS_CODEC_CI_REF: &IVAS_CODEC_CI_REF main include: - local: .gitlab-ci/variables.yml -- GitLab From 3cd7de857159c593d28d6106ebf3f83745bd9cd9 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 6 Oct 2025 15:09:18 +0200 Subject: [PATCH 085/100] Revert "fix reduction of testcase in LTV test" This reverts commit 0f6023ff51c0f8802f1e8894b28595e8d8767221. --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1710466465..68df34351a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2304,9 +2304,9 @@ coverage-test-on-main-scheduled-ltv: COVERAGE_TITLE: "long test vectors" script: # -- Add extended coverage of EVS test and long test vectors - # - python3 -m pytest -k stereo $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_evs.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec + - python3 -m pytest -k stereo $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_evs.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec # # default renderer framesize is 20ms - - python3 -m pytest -k stereo $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_ltv.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec + # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_ltv.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec # # for different renderer framesizes, only the decoder needs to be tested, so use --decoder_only # # need to ignore non-zero exit codes as limiter is active and thus the different framesiszes will not be BE in all cases # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --param_file scripts/config/self_test_ltv.prm --dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 5 || true -- GitLab From fd6dda87d84ac0300ae107a6fd9d2b057e4e7c65 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 6 Oct 2025 15:09:26 +0200 Subject: [PATCH 086/100] Revert "[revert-me] reduce number of tests for faster iteration" This reverts commit 8345f5f0d2b2fbd41ddca93aa035202b480298bd. --- .gitlab-ci.yml | 86 +++++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 68df34351a..8036827b99 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1389,10 +1389,10 @@ ivas-conformance-linux: # Reference creation - python3 scripts/prepare_combined_format_inputs.py - TEST_SET="tests/codec_be_on_mr_nonselection tests/renderer/test_renderer.py tests/split_rendering/test_split_rendering.py" - - python3 -m pytest -k stereo -q $TEST_SET -v -n auto --update_ref 1 --create_ref --keep_files + - python3 -m pytest -q $TEST_SET -v -n auto --update_ref 1 --create_ref --keep_files # Output creation - - python3 -m pytest -k stereo -q $TEST_SET -v -n auto --keep_files --create_cut --html=report_cmd.html --self-contained-html + - python3 -m pytest -q $TEST_SET -v -n auto --keep_files --create_cut --html=report_cmd.html --self-contained-html - python3 scripts/parse_commands.py report_cmd.html Readme_IVAS.txt # Copy input data and output ref data @@ -1440,7 +1440,7 @@ ivas-conformance-linux: # Test run generated scripts in testvec - cd testvec - exit_code=0 - - python3 -m pytest -k stereo -q conformance-test/test_26252.py --junit-xml=report-junit.xml --html=report.html --self-contained-html || exit_code=$? + - python3 -m pytest -q conformance-test/test_26252.py --junit-xml=report-junit.xml --html=report.html --self-contained-html || exit_code=$? - mv report.html .. - mv report-junit.xml .. @@ -2263,33 +2263,33 @@ coverage-test-on-main-scheduled-stv: script: # -- Run short test vector suite to check coverage of that first # codec smoke test - # - bash ci/smoke_test.sh coverage + - bash ci/smoke_test.sh coverage # default renderer framesize is 20ms - - python3 -m pytest -k stereo $TESTS_DIR_CODEC_BE_ON_MR -v -n auto -q --tb=no --update_ref 1 --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec - # # for different renderer framesizes, only the decoder needs to be tested, so use --decoder_only - # # need to ignore non-zero exit codes as limiter is active and thus the different framesiszes will not be BE in all cases - # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only || true - # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only || true - # # run renderer smoke test - needed for REFVEC tests - # - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py - # # run renderer test only with REF creation stage - no CUT needed since executables are identical - # - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --create_ref - # # run split rendering smoke test and REF creation - # - python3 -m pytest -n auto -q --tb=no tests/split_rendering/test_split_rendering.py - # - python3 -m pytest -n auto -q --tb=no tests/split_rendering/test_split_rendering.py --create_ref - # # VoIP BE test - # - python3 -m pytest -q --tb=no tests/test_be_for_jbm_neutral_dly_profile.py - # # split rendering VoIP BE test - # - python3 -m pytest -n auto -q --tb=no tests/split_rendering/test_voip_be_splitrend_vs_binaural.py - # # TODO first frame SID - this script runs a `make clean` and needs to be checked/adapted - # # - bash ci/run-first-frame-is-sid-test.sh - # # TODO consider HRTF binary loading test, or probably already covered by PRM files - # # - python3 -m pytest -q --tb=no tests/hrtf_binary_loading - # # run BE vs EXT test - # - python3 tests/create_short_testvectors.py --cut_len 1.0 - # - python3 -m pytest -q --tb=no tests/test_be_for_ext_outputs.py - # # run LC3plus wrapper unit test - # - scripts/split_rendering/lc3plus_float/ivas_lc3plus_unit_test + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v -n auto -q --tb=no --update_ref 1 --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec + # for different renderer framesizes, only the decoder needs to be tested, so use --decoder_only + # need to ignore non-zero exit codes as limiter is active and thus the different framesiszes will not be BE in all cases + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only || true + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only || true + # run renderer smoke test - needed for REFVEC tests + - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py + # run renderer test only with REF creation stage - no CUT needed since executables are identical + - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --create_ref + # run split rendering smoke test and REF creation + - python3 -m pytest -n auto -q --tb=no tests/split_rendering/test_split_rendering.py + - python3 -m pytest -n auto -q --tb=no tests/split_rendering/test_split_rendering.py --create_ref + # VoIP BE test + - python3 -m pytest -q --tb=no tests/test_be_for_jbm_neutral_dly_profile.py + # split rendering VoIP BE test + - python3 -m pytest -n auto -q --tb=no tests/split_rendering/test_voip_be_splitrend_vs_binaural.py + # TODO first frame SID - this script runs a `make clean` and needs to be checked/adapted + # - bash ci/run-first-frame-is-sid-test.sh + # TODO consider HRTF binary loading test, or probably already covered by PRM files + # - python3 -m pytest -q --tb=no tests/hrtf_binary_loading + # run BE vs EXT test + - python3 tests/create_short_testvectors.py --cut_len 1.0 + - python3 -m pytest -q --tb=no tests/test_be_for_ext_outputs.py + # run LC3plus wrapper unit test + - scripts/split_rendering/lc3plus_float/ivas_lc3plus_unit_test # TODO consider including check-clipping, but this scales ALL testv files. could be added at end of this job artifacts: paths: @@ -2304,20 +2304,20 @@ coverage-test-on-main-scheduled-ltv: COVERAGE_TITLE: "long test vectors" script: # -- Add extended coverage of EVS test and long test vectors - - python3 -m pytest -k stereo $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_evs.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec - # # default renderer framesize is 20ms - # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_ltv.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec - # # for different renderer framesizes, only the decoder needs to be tested, so use --decoder_only - # # need to ignore non-zero exit codes as limiter is active and thus the different framesiszes will not be BE in all cases - # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --param_file scripts/config/self_test_ltv.prm --dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 5 || true - # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --param_file scripts/config/self_test_ltv.prm --dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 10 || true - # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --update_ref 1 --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec --use_ltv - # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only --use_ltv || true - # - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only --use_ltv || true - # # run renderer smoke test - needed for REFVEC tests - # - ls -altr scripts/testv - # # LTV smoketest is the same as REF creation stage for renderer - # - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --use_ltv + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_evs.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec + # default renderer framesize is 20ms + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --update_ref 1 --param_file scripts/config/self_test_ltv.prm --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec + # for different renderer framesizes, only the decoder needs to be tested, so use --decoder_only + # need to ignore non-zero exit codes as limiter is active and thus the different framesiszes will not be BE in all cases + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --param_file scripts/config/self_test_ltv.prm --dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 5 || true + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_param_file.py -v -n auto -q --tb=no --param_file scripts/config/self_test_ltv.prm --dut_decoder_path ./IVAS_dec --decoder_only --dut_fr 10 || true + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --update_ref 1 --ref_encoder_path ./IVAS_cod --ref_decoder_path ./IVAS_dec --use_ltv + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 5 --decoder_only --use_ltv || true + - python3 -m pytest $TESTS_DIR_CODEC_BE_ON_MR/test_sba.py -v -n auto -q --tb=no --dut_encoder_path ./IVAS_cod --dut_decoder_path ./IVAS_dec --dut_fr 10 --decoder_only --use_ltv || true + # run renderer smoke test - needed for REFVEC tests + - ls -altr scripts/testv + # LTV smoketest is the same as REF creation stage for renderer + - python3 -m pytest -n auto -q --tb=no tests/renderer/test_renderer.py --use_ltv # split rendering test doesn't support LTV - skipped here # VoIP BE tests don't support LTV - skipped here artifacts: -- GitLab From 35f451dcab00bec6534df1fc19f7ec203f33afa1 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 6 Oct 2025 16:15:42 +0200 Subject: [PATCH 087/100] integrate new coverage jobs into gtlab pages setup --- ci/index-pages.html | 5 +---- ci/setup_pages.py | 35 +++++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/ci/index-pages.html b/ci/index-pages.html index 510e203115..390f06631d 100644 --- a/ci/index-pages.html +++ b/ci/index-pages.html @@ -11,9 +11,6 @@

Test Coverage

- + {} diff --git a/ci/setup_pages.py b/ci/setup_pages.py index df350d8568..1a1c1ea735 100755 --- a/ci/setup_pages.py +++ b/ci/setup_pages.py @@ -40,7 +40,10 @@ JOBS_FLOAT_REPO = { "complexity-osba-in-binaural_room_ir-out": "OSBA in, BINAURAL_ROOM_IR out", "complexity-StereoDmxEVS-stereo-in-mono-out": "StereoDmxEVS, Stereo in, Mono out", # "timeless" jobs (not complexity) - "coverage-test-on-main-scheduled": "Coverage", + "coverage-test-on-main-scheduled-stv": "Short test vector coverage (~ merge request pipelines)", + "coverage-test-on-main-scheduled-ltv": "Long test vector coverage (for test_param_file.py, test_sba.py and test_renderer.py)", + "ivas-conformance-linux": "Conformance test coverage", + "coverage-merge": "Merged coverage (all of the above combined)", } JOBS_BASOP_REPO = { "ivas-pytest-compare_ref-long-dec": "Pytest decoder compare to ref LTV", @@ -77,6 +80,13 @@ JOBS_FOR_PROJECT_ID = { PROJECT_ID_BASOP_REPO: JOBS_BASOP_REPO, } +ARTIFACT_FOLDER_4_COVERAGE_JOBS = { + "coverage-test-on-main-scheduled-stv": "coverage_stv", + "coverage-test-on-main-scheduled-ltv": "coverage_ltv", + "ivas-conformance-linux": "coverage_conformance", + "coverage-merge": "coverage-merged", +} + ARTIFACTS = "artifacts.zip" API_URL_BASE = "https://forge.3gpp.org/rep/api/v4/projects/{}/jobs" PUBLIC_FOLDER = pathlib.Path("./public").absolute() @@ -104,15 +114,20 @@ def main(): def create_landing_page(jobs, index_html, project_id): # dynamically create the complexity links on the landing page - link_html = list() - link_html = ["
    "] + link_html_complexity = ["
      "] + link_html_coverage = ["
        "] for job, link_text in jobs.items(): if job.startswith("complexity"): line = f'
      • {link_text}
      • ' - link_html.append(line) - link_html.append("
      ") - link_html_text = "\n".join(link_html) + link_html_complexity.append(line) + elif job.startswith("coverage") or job == "ivas-conformance-linux": + artifact_folder = ARTIFACT_FOLDER_4_COVERAGE_JOBS[job] + line = f'
    • {link_text}
    • ' + link_html_coverage.append(line) + + link_html_complexity.append("
    ") + link_html_coverage.append("
") if project_id == PROJECT_ID_FLOAT_REPO: index_template = "index-pages.html" @@ -126,14 +141,18 @@ def create_landing_page(jobs, index_html, project_id): with open(index_pages_tmpl_path) as f: index_pages_tmpl = f.read() - index_pages_tmpl = index_pages_tmpl.format(link_html_text) + link_html_complexity_text = "\n".join(link_html_complexity) + link_html_coverage_text = "\n".join(link_html_coverage) + index_pages_tmpl = index_pages_tmpl.format( + link_html_complexity_text, link_html_coverage_text + ) with open(index_html, "w") as f: f.write(index_pages_tmpl) def get_artifacts_for_jobs_and_return_num_failed( - jobs: list, project_id: int, success_only: bool + jobs: dict, project_id: int, success_only: bool ) -> int: """ Get specified artifact folders for all jobs given and put them into the public folder. -- GitLab From 96f25dcd72cdcb477426a6600605838e56a14eb0 Mon Sep 17 00:00:00 2001 From: Ripinder Singh Date: Tue, 7 Oct 2025 17:59:32 +1100 Subject: [PATCH 088/100] Remove restart functionality and bugfix in lib_dec free and code review Signed-off-by: Ripinder Singh --- apps/decoder.c | 151 ++---------------------------------- lib_dec/lib_dec.c | 5 -- lib_util/ivas_bpool.c | 1 - lib_util/ivas_queue.c | 1 - lib_util/ivas_rtp_file.c | 67 +++++++++++----- lib_util/ivas_rtp_file.h | 3 +- lib_util/ivas_rtp_payload.c | 2 - lib_util/ivas_rtp_pi_data.c | 10 +-- lib_util/ivas_rtp_pi_data.h | 51 ++++++------ lib_util/mutex.h | 2 +- 10 files changed, 82 insertions(+), 211 deletions(-) diff --git a/apps/decoder.c b/apps/decoder.c index 6e04fadb7f..ed96f79196 100644 --- a/apps/decoder.c +++ b/apps/decoder.c @@ -179,16 +179,11 @@ 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, #ifdef FIX_1119_SPLIT_RENDERING_VOIP - ISAR_SPLIT_REND_BITS_DATA *splitRendBits, -#endif -#ifdef IVAS_RTPDUMP - IVAS_DEC_HANDLE *phIvasDec, +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, ISAR_SPLIT_REND_BITS_DATA *splitRendBits, IVAS_DEC_HANDLE hIvasDec, int16_t *pcmBuf ); #else - IVAS_DEC_HANDLE hIvasDec, +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 - 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 ); @@ -196,112 +191,6 @@ static int16_t app_own_random( int16_t *seed ); #endif static void do_object_editing( IVAS_EDITABLE_PARAMETERS *editableParameters, ObjectEditFileReader *objectEditFileReader ); -#ifdef IVAS_RTPDUMP -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; -} -#endif - /*------------------------------------------------------------------------------------------* * main() * @@ -855,16 +744,12 @@ int main( if ( arg.voipMode ) { - error = decodeVoIP( arg, hBsReader, &hHrtfBinary, headRotReader, externalOrientationFileReader, refRotReader, referenceVectorReader, objectEditFileReader, + #ifdef FIX_1119_SPLIT_RENDERING_VOIP - &splitRendBits, -#endif -#ifdef IVAS_RTPDUMP - &hIvasDec, + error = decodeVoIP( arg, hBsReader, &hHrtfBinary, headRotReader, externalOrientationFileReader, refRotReader, referenceVectorReader, objectEditFileReader, &splitRendBits, hIvasDec, pcmBuf ); #else - hIvasDec, + error = decodeVoIP( arg, hBsReader, &hHrtfBinary, headRotReader, externalOrientationFileReader, refRotReader, referenceVectorReader, objectEditFileReader, hIvasDec, pcmBuf ); #endif - pcmBuf ); } else { @@ -3171,11 +3056,7 @@ static ivas_error decodeVoIP( #ifdef FIX_1119_SPLIT_RENDERING_VOIP ISAR_SPLIT_REND_BITS_DATA *splitRendBits, #endif -#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 */ @@ -3209,7 +3090,6 @@ static ivas_error decodeVoIP( int16_t i; #ifdef IVAS_RTPDUMP IVAS_RTP ivasRtp = { 0 }; - IVAS_DEC_HANDLE hIvasDec = *phIvasDec; #else FILE *f_rtpstream = NULL; EVS_RTPDUMP_DEPACKER rtpdumpDepacker; @@ -3392,27 +3272,6 @@ 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 ) { diff --git a/lib_dec/lib_dec.c b/lib_dec/lib_dec.c index 87d4d06d65..e0fc232dc2 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -347,15 +347,10 @@ void IVAS_DEC_Close( } /* destroy Split binaural renderer (ISAR) handle */ -#ifndef IVAS_RTPDUMP ivas_destroy_handle_isar( &( *phIvasDec )->st_ivas->hSplitBinRend ); -#endif if ( ( *phIvasDec )->st_ivas ) { -#ifdef IVAS_RTPDUMP - ivas_destroy_handle_isar( &( *phIvasDec )->st_ivas->hSplitBinRend ); -#endif ivas_destroy_dec( ( *phIvasDec )->st_ivas ); ( *phIvasDec )->st_ivas = NULL; } diff --git a/lib_util/ivas_bpool.c b/lib_util/ivas_bpool.c index 827422fac3..9db909f535 100644 --- a/lib_util/ivas_bpool.c +++ b/lib_util/ivas_bpool.c @@ -29,7 +29,6 @@ the United Nations Convention on Contracts on the International Sales of Goods. *******************************************************************************************************/ -#include #include #include #include "ivas_bpool.h" diff --git a/lib_util/ivas_queue.c b/lib_util/ivas_queue.c index c5806e0307..b17cf3e21a 100644 --- a/lib_util/ivas_queue.c +++ b/lib_util/ivas_queue.c @@ -30,7 +30,6 @@ *******************************************************************************************************/ -#include #include #include "ivas_queue.h" #include "ivas_error_utils.h" diff --git a/lib_util/ivas_rtp_file.c b/lib_util/ivas_rtp_file.c index 1c45869e69..8555480efd 100644 --- a/lib_util/ivas_rtp_file.c +++ b/lib_util/ivas_rtp_file.c @@ -29,12 +29,8 @@ the United Nations Convention on Contracts on the International Sales of Goods. *******************************************************************************************************/ -#include #include #include -#include - -#include "lib_dec.h" #include "ivas_rtp_file.h" #include "ivas_error_utils.h" @@ -86,9 +82,9 @@ static ivas_error IvasRtpFile_Close( } static ivas_error IvasRtpFile_Write( - IVAS_RTP_FILE_HANDLE hRtpFile, /* i : pointer to an IVAS file writer handle */ - const uint8_t *packet, - size_t numBytes ) + IVAS_RTP_FILE_HANDLE hRtpFile, /* i : pointer to an IVAS file writer handle */ + const uint8_t *packet, /* i : rtp packet to be written to rtpdump file */ + size_t numBytes ) /* i : size in bytes of the rtp packet */ { ivas_error error = IVAS_ERR_OK; if ( hRtpFile->isFileWriter ) @@ -105,10 +101,11 @@ static ivas_error IvasRtpFile_Write( } static ivas_error IvasRtpFile_Read( - IVAS_RTP_FILE_HANDLE hRtpFile, /* i : pointer to an IVAS file writer handle */ - uint8_t *packet, - size_t *numBytes, - size_t capacity ) + IVAS_RTP_FILE_HANDLE hRtpFile, /* i : pointer to an IVAS file reader handle */ + uint8_t *packet, /* o : read rtp packet */ + size_t *numBytes, /* o : no of bytes in packet */ + size_t capacity /* i : max capacity of the packet buffer */ +) { size_t nread = 0; uint32_t length = 0; @@ -149,7 +146,11 @@ static const char *const PiDataNames[IVAS_PI_MAX_ID] = { "RESERVED30", "NO_DATA" }; -void IVAS_RTP_LogPiData( FILE *f_piDataOut, PIDATA_TS *piData, uint32_t nPiDataPresent ) +void IVAS_RTP_LogPiData( + FILE *f_piDataOut, /* i/o : Output json file handle to dump PI data for debug/test */ + const PIDATA_TS *piData, /* i : PI Data + Timestamp array containing all PI data in current packet */ + uint32_t nPiDataPresent /* i : Number of valid elements in the piData array */ +) { uint32_t timestamp = ~0u; if ( f_piDataOut == NULL || piData == NULL || nPiDataPresent == 0 ) @@ -164,7 +165,7 @@ void IVAS_RTP_LogPiData( FILE *f_piDataOut, PIDATA_TS *piData, uint32_t nPiDataP while ( nPiDataPresent-- > 0 ) { - PIDATA_TS *cur = piData++; + const PIDATA_TS *cur = piData++; if ( timestamp != ( ~0u ) && timestamp != cur->timestamp ) { @@ -222,7 +223,7 @@ void IVAS_RTP_LogPiData( FILE *f_piDataOut, PIDATA_TS *piData, uint32_t nPiDataP case IVAS_PI_AUDIO_DESCRIPTION: { uint32_t nEntries = cur->data.audioDesc.nValidEntries; - IVAS_AUDIO_ID *audioId = cur->data.audioDesc.audioId; + const IVAS_AUDIO_ID *audioId = cur->data.audioDesc.audioId; fprintf( f_piDataOut, "[\n" ); while ( nEntries-- > 0 ) @@ -241,7 +242,7 @@ void IVAS_RTP_LogPiData( FILE *f_piDataOut, PIDATA_TS *piData, uint32_t nPiDataP break; case IVAS_PI_DIEGETIC_TYPE: { - bool *isDiegetic = cur->data.digeticIndicator.isDiegetic; + const 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" ); @@ -274,7 +275,7 @@ void IVAS_RTP_LogPiData( FILE *f_piDataOut, PIDATA_TS *piData, uint32_t nPiDataP break; case IVAS_PI_DYNAMIC_AUDIO_SUPPRESSION: { - IVAS_PIDATA_DYNAMIC_SUPPRESSION *das = &cur->data.dynSuppression; + const 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" ); @@ -336,7 +337,9 @@ void IVAS_RTP_LogPiData( FILE *f_piDataOut, PIDATA_TS *piData, uint32_t nPiDataP fprintf( f_piDataOut, "\n\t}" ); } -void IVAS_RTP_Term( IVAS_RTP *rtp ) +void IVAS_RTP_Term( + IVAS_RTP *rtp /* i/o : IVAS RTP File reader/writer handle */ +) { if ( NULL != rtp ) { @@ -382,7 +385,11 @@ void IVAS_RTP_Term( IVAS_RTP *rtp ) } } -ivas_error IVAS_RTP_WRITER_Init( IVAS_RTP *rtp, const char *outputBitstreamFilename, uint32_t numFramesPerPacket ) +ivas_error IVAS_RTP_WRITER_Init( + IVAS_RTP *rtp, /* i/o : IVAS RTP File writer handle */ + const char *outputBitstreamFilename, /* i : RTP Dump filename */ + uint32_t numFramesPerPacket /* i : No. of frames per packet desired */ +) { uint32_t SSRC = ( rand() & 0xFFFF ) | ( (uint32_t) rand() << 16 ); ivas_error error = IVAS_ERR_OK; @@ -413,7 +420,11 @@ ivas_error IVAS_RTP_WRITER_Init( IVAS_RTP *rtp, const char *outputBitstreamFilen return error; } -ivas_error IVAS_RTP_READER_Init( IVAS_RTP *rtp, const char *inputBitstreamFilename, const char *piOutputFilename ) +ivas_error IVAS_RTP_READER_Init( + IVAS_RTP *rtp, /* i/o : IVAS RTP File reader handle */ + const char *inputBitstreamFilename, /* i : Input rtpdump filename */ + const char *piOutputFilename /* i : Output PI data json filename */ +) { ivas_error error = IVAS_ERR_OK; @@ -447,7 +458,13 @@ ivas_error IVAS_RTP_READER_Init( IVAS_RTP *rtp, const char *inputBitstreamFilena return error; } -ivas_error IVAS_RTP_WriteNextFrame( IVAS_RTP *rtp, uint8_t *au, int16_t auSizeBits, bool isMono, bool forcePacket ) +ivas_error IVAS_RTP_WriteNextFrame( + IVAS_RTP *rtp, /* i/o : IVAS RTP File writer handle */ + uint8_t *au, /* i : IVAS Compressed AU (Packed frame) */ + int16_t auSizeBits, /* i : Frame size in bits */ + bool isMono, /* i : input was evs(true) or ivas(false) */ + bool forcePacket /* i : force packets with whatever frames pushed so far */ +) { ivas_error error = IVAS_ERR_OK; uint32_t nProcPiData = 0; @@ -501,7 +518,15 @@ ivas_error IVAS_RTP_WriteNextFrame( IVAS_RTP *rtp, uint8_t *au, int16_t auSizeBi return error; } -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 IVAS_RTP_ReadNextFrame( + IVAS_RTP *rtp, /* i/o : IVAS RTP File reader handle */ + uint8_t *au, /* o : Read next IVAS Compressed AU (Packed frame) */ + int16_t *auSizeBits, /* o : Reported Frame size in bits */ + uint32_t *rtpTimeStamp, /* o : RTP Timestamp for this frame */ + uint16_t *rtpSequenceNumber, /* o : RTP sequence number for this packet */ + uint32_t *nextPacketRcvTime_ms, /* i/o : Clock indicating packet receive times need in JBM */ + bool *qBit /* o : AMRWB Q bite as indicated in the RTP packet */ +) { ivas_error error = IVAS_ERR_OK; IVAS_DATA_BUFFER packedFrame; diff --git a/lib_util/ivas_rtp_file.h b/lib_util/ivas_rtp_file.h index 5d3e9df1ab..c0757834f0 100644 --- a/lib_util/ivas_rtp_file.h +++ b/lib_util/ivas_rtp_file.h @@ -33,7 +33,6 @@ #ifndef IVAS_RTP_FILE_H #define IVAS_RTP_FILE_H -#include #include #include "common_api_types.h" @@ -76,7 +75,7 @@ ivas_error IVAS_RTP_READER_Init( IVAS_RTP *rtp, const char *inputBitstreamFilena void IVAS_RTP_Term( IVAS_RTP *rtp ); ivas_error IVAS_RTP_WriteNextFrame( IVAS_RTP *rtp, uint8_t *au, int16_t auSizeBits, bool isMono, bool forcePacket ); 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 ); -void IVAS_RTP_LogPiData( FILE *f_piDataOut, PIDATA_TS *piData, uint32_t nPiDataPresent ); +void IVAS_RTP_LogPiData( FILE *f_piDataOut, const PIDATA_TS *piData, uint32_t nPiDataPresent ); #endif /* IVAS_RTP_FILE_H */ diff --git a/lib_util/ivas_rtp_payload.c b/lib_util/ivas_rtp_payload.c index 468dce0d17..4e4b11f2aa 100644 --- a/lib_util/ivas_rtp_payload.c +++ b/lib_util/ivas_rtp_payload.c @@ -37,8 +37,6 @@ #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" diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index a3d8c5fe63..7150cfe4a8 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -30,8 +30,6 @@ *******************************************************************************************************/ -#include -#include "ivas_rtp_pi_data.h" #include "ivas_error_utils.h" #include "ivas_rtp_internal.h" @@ -331,7 +329,7 @@ static ivas_error packAudioDescription( const IVAS_PIDATA_GENERIC *piData, uint8 *nBytesWritten = 0; - if ( audioDesc->nValidEntries > ( IVAS_PI_MAX_OBJECTS + 1 ) ) + if ( audioDesc->nValidEntries > ( IVAS_MAX_NUM_OBJECTS + 1 ) ) { return IVAS_ERROR( IVAS_ERR_RTP_UNSUPPORTED_FRAME, "Audio Description cannot have more than 5 entries" ); } @@ -364,7 +362,7 @@ static ivas_error unpackAudioDescription( const uint8_t *buffer, uint32_t numDat IVAS_PIDATA_AUDIO_DESC *audioDesc = (IVAS_PIDATA_AUDIO_DESC *) piData; /* Audio Description data is max 5 bytes */ - if ( numDataBytes > ( IVAS_PI_MAX_OBJECTS + 1 ) ) + if ( numDataBytes > ( IVAS_MAX_NUM_OBJECTS + 1 ) ) { return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack Orientation PI data" ); } @@ -505,7 +503,7 @@ static ivas_error packDiegetic( const IVAS_PIDATA_GENERIC *piData, uint8_t *buff } /* Valid bits must be based on active bits defined for the input format */ - for ( n = 0; n < ( IVAS_PI_MAX_OBJECTS + 1 ); n++ ) + for ( n = 0; n < ( IVAS_MAX_NUM_OBJECTS + 1 ); n++ ) { byte <<= 1; byte |= ( diegetic->isDiegetic[n] ); @@ -536,7 +534,7 @@ static ivas_error unpackDiegetic( const uint8_t *buffer, uint32_t numDataBytes, 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++ ) + for ( n = 0; n < ( IVAS_MAX_NUM_OBJECTS + 1 ); n++ ) { diegetic->isDiegetic[n] = ( ( byte >> ( 7 - n ) ) & 1 ) != 0; } diff --git a/lib_util/ivas_rtp_pi_data.h b/lib_util/ivas_rtp_pi_data.h index 3787861f00..6a84b8f2e3 100644 --- a/lib_util/ivas_rtp_pi_data.h +++ b/lib_util/ivas_rtp_pi_data.h @@ -43,7 +43,6 @@ extern "C" #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 @@ -183,10 +182,10 @@ extern "C" 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 */ + 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_MAX_NUM_OBJECTS]; /* audio id as per format */ } IVAS_PIDATA_AUDIO_DESC; /* ISM specific PI data related to PI types : - @@ -211,33 +210,33 @@ extern "C" /* 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 */ + size_t size; /* sizeof(IVAS_PIDATA_ISM_ID) */ + uint32_t piDataType; /* IVAS_PI_ISM_ID */ + uint8_t id[IVAS_MAX_NUM_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] */ + size_t size; /* sizeof(IVAS_PIDATA_ISM_GAIN) */ + uint32_t piDataType; /* IVAS_PI_ISM_GAIN */ + int8_t dB[IVAS_MAX_NUM_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) */ + size_t size; /* sizeof(IVAS_PIDATA_ISM_ORIENTATION) */ + uint32_t piDataType; /* IVAS_PI_ISM_ORIENTATION */ + IVAS_QUATERNION orientation[IVAS_MAX_NUM_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) */ + size_t size; /* sizeof(IVAS_PIDATA_ISM_POSITION) */ + uint32_t piDataType; /* IVAS_PI_ISM_POSITION */ + IVAS_COORDINATE position[IVAS_MAX_NUM_OBJECTS]; /* Position of audio objects in ISM(s) */ } IVAS_PIDATA_ISM_POSITION; /* ISM distance attenuation comprising of following gains per ISM @@ -254,9 +253,9 @@ extern "C" 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 */ + size_t size; /* sizeof(IVAS_PIDATA_ISM_ATTENUATION) */ + uint32_t piDataType; /* IVAS_PI_ISM_DISTANCE_ATTENUATION */ + IVAS_DIST_ATTEN distAtten[IVAS_MAX_NUM_OBJECTS]; /* Distance attenuation of audio objects */ } IVAS_PIDATA_ISM_ATTENUATION; /* ISM Directivity comprising of following per ISM :- @@ -273,9 +272,9 @@ extern "C" 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 */ + size_t size; /* sizeof(IVAS_PIDATA_ISM_DIRECTIVITY) */ + uint32_t piDataType; /* IVAS_PI_ISM_DIRECTIVITY */ + IVAS_ISM_DIRECTIVITY directivity[IVAS_MAX_NUM_OBJECTS]; /* Directivity of audio objects */ } IVAS_PIDATA_ISM_DIRECTIVITY; /* Diegetic and non-diegetic indication flag as per audio format @@ -287,9 +286,9 @@ extern "C" */ 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 */ + size_t size; /* sizeof(IVAS_PIDATA_DIEGETIC) */ + uint32_t piDataType; /* IVAS_PI_DIEGETIC_TYPE */ + bool isDiegetic[1 + IVAS_MAX_NUM_OBJECTS]; /* diegetic indication as per audio format */ } IVAS_PIDATA_DIEGETIC; /* Audio focus direction indicates a direction of interest. diff --git a/lib_util/mutex.h b/lib_util/mutex.h index caf0145e2e..82094b61c8 100644 --- a/lib_util/mutex.h +++ b/lib_util/mutex.h @@ -1,6 +1,6 @@ /****************************************************************************************************** - (C) 2022-2024 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB, + (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 -- GitLab From 17ecc8ea85dc59ba2ec3408bca8d95528fe1f90f Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Tue, 7 Oct 2025 14:16:06 +0300 Subject: [PATCH 089/100] Add rtpdump test to pipeline --- .gitlab-ci.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9c53be7cae..f0369c2eff 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -697,6 +697,28 @@ split-rendering-voip-be-to-binaural: junit: - report-junit.xml +# test rtpdump +rtpdump-test: + extends: + - .test-job-linux + - .rules-merge-request-to-main + needs: ["build-codec-linux-make"] + stage: test + script: + - make -j + - testcase_timeout=30 + - python3 -m pytest -q -n auto -rA --junit-xml=report-junit.xml tests/rtp/test_rtp.py --testcase_timeout=$testcase_timeout + artifacts: + name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--job-$CI_JOB_NAME--results" + expire_in: 1 week + when: always + paths: + - report-junit.xml + expose_as: "rtpdump pytest results" + reports: + junit: + - report-junit.xml + lc3-wrapper-unit-test: extends: - .test-job-linux -- GitLab From 02b494f06479926b36ee2b8c375c802f4b46b55e Mon Sep 17 00:00:00 2001 From: Ripinder Singh Date: Wed, 8 Oct 2025 10:14:03 +1100 Subject: [PATCH 090/100] Remove soundfile, replace with audiofile from pyaudio3dtools Signed-off-by: Ripinder Singh --- tests/requirements.txt | 1 - tests/rtp/test_rtp.py | 27 +++++++++++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/tests/requirements.txt b/tests/requirements.txt index a8d8f7b064..00100fae88 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -3,4 +3,3 @@ 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/test_rtp.py b/tests/rtp/test_rtp.py index 4e394ee478..c1832202bb 100644 --- a/tests/rtp/test_rtp.py +++ b/tests/rtp/test_rtp.py @@ -31,7 +31,24 @@ the United Nations Convention on Contracts on the International Sales of Goods. """ __doc__ = """ -To configure test modules. +This test does the following procedure:- + +DECODER TEST +============ +* Encode a input for given format, bitrate and dtx info using IVAS Encoder to g192 +* Generate random PI data to be used for RTP packaging +* Use g192 and random PI data to pack a stream into RTPDump stream using the reference rtp packer implementation in python +* Provide the rtpdump stream to IVAS decoder to decode the stream and generate a PI data dump in a JSON file +* Decode G192 stream using IVAS decoder and compare against output of RTPdump for bit-exactness +* Validate dumped JSON agains original random PI data to check for similar data after deocde + +ENCODER TEST +============ +* Encode the input for given format, bitrate and dtx info using IVAS Encoder to rtpdump directly +* Validate the generated rtp dump using reference python implementation of RTP depacker for following:- + * RTP Header consistency (Timestamp, seq number, etc) + * IVAS Payload (no of frames in packet, bitrate indicated, bitexactness of frames in packet) + * PI Data verification (Timestamp, Pidata Type, similarity of PI data) """ import pytest @@ -43,8 +60,8 @@ import random from tempfile import TemporaryDirectory from pathlib import Path from ivasrtp import * -import soundfile as sf import numpy as np +from pyaudio3dtools.audiofile import readfile ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")) sys.path.append(ROOT_DIR) @@ -433,8 +450,10 @@ def run_rtp_bitstream_tests ( add_option_list= ["-VOIP_HF_ONLY=1", "-PiDataFile", str(piDataOutJson)] ) - decAudio, fs = sf.read(pcmOut) - g192Audio, Fs = sf.read(pcmOutG192) + decAudio, fs = readfile(pcmOut) + g192Audio, Fs = readfile(pcmOutG192) + decAudio /= 32768.0 # readfile reuturns 16 bit int + g192Audio /= 32768.0 # readfile reuturns 16 bit int 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]) -- GitLab From c982c3d232b62ea702a77e6e125707ee974fcf00 Mon Sep 17 00:00:00 2001 From: Markus Multrus Date: Wed, 8 Oct 2025 11:42:43 +0200 Subject: [PATCH 091/100] first version of script to detect FLP assembled instructions in the executable --- scripts/find_fp_insn.sh | 93 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100755 scripts/find_fp_insn.sh diff --git a/scripts/find_fp_insn.sh b/scripts/find_fp_insn.sh new file mode 100755 index 0000000000..fe0090a55e --- /dev/null +++ b/scripts/find_fp_insn.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash + +# +# (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. +# + +# fp_insn_finder.sh +# Usage: ./find_fp_insn.sh +# disassembles executable, lists common floating-point instructions + +set -euo pipefail + +if [ $# -ne 1 ]; then + echo "Usage: $0 " + echo "Find floating-point instructions in an executable based on assembler instructions" + echo "Pre-requisites: Perl or gawk" + exit 1 +fi + +EXE="$1" +if [ ! -x "$EXE" ]; then + echo "Error: '$EXE' is no executable" + exit 2 +fi + +perl_avail=0 +# check, whether we have perl installed +if command -v perl >/dev/null 2>&1; then + perl_avail=1 +fi + +gawk_avail=0 +awk_option="" +# check, whether we have awk (POSIX) or gawk (GNU) on the system +if command -v gawk >/dev/null 2>&1; then + gawk_avail=1 + awk_option="--posix" +fi + + +if [ $perl_avail -ne 0 ]; then + + objdump -d -Mintel "$EXE" \ + | perl -nE ' + # Regex for floating-point instructions + our $fp = qr/\b(?:fadd|fsub|fmul|fdiv|fld|fst|movss|movsd|addss|addsd|subss|subsd|mulss|mulsd|divss|divsd)\b/; + if (/^[0-9a-f]+ <([^>]+)>:/) { $func = $1 } + elsif (/$fp/) { say "$func: $_" } + ' + +else + + objdump -d -Mintel "$EXE" | + awk ${awk_option} ' + /^[0-9a-f]+ :.*$/, "") + func = $0 + next + } + /(^|[^[:alnum:]_])(fadd|fsub|fmul|fdiv|fld|fst|movss|movsd|addss|addsd|subss|subsd|mulss|mulsd|divss|divsd)($|[^[:alnum:]_])/ { + print func ": " $0 + } + ' + +fi -- GitLab From 02b5cc7c6655d9dc3e29eb0e337667752a563dab Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Wed, 8 Oct 2025 13:41:13 +0300 Subject: [PATCH 092/100] Move rtpdump tests to the end of the self_test_prm file --- scripts/config/self_test.prm | 73 ++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/scripts/config/self_test.prm b/scripts/config/self_test.prm index 7bfe33e4de..496a23e70f 100644 --- a/scripts/config/self_test.prm +++ b/scripts/config/self_test.prm @@ -306,10 +306,6 @@ 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 ../IVAS_cod -ism 1 testv/stvISM1.csv 13200 48 testv/stv1ISM48s.wav bit @@ -583,10 +579,6 @@ 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 ../IVAS_cod -max_band testv/ivas_bws_20fr_start_WB.txt -sba 3 13200 32 testv/stv3OA32c.wav bit @@ -964,10 +956,6 @@ 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 ../IVAS_cod -max_band testv/ivas_bws_20fr_start_SWB.txt -masa 1 testv/stv1MASA1TC48c.met 13200 48 testv/stv1MASA1TC48c.wav bit @@ -1247,10 +1235,6 @@ 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 ../IVAS_cod -mc 5_1 13200 48 testv/stv51MC48c.wav bit @@ -1573,10 +1557,6 @@ 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 ../IVAS_cod -stereo_dmx_evs 13200 32 testv/stvST32c.wav bit @@ -1586,10 +1566,6 @@ 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 ../IVAS_cod 64000 48 testv/stv48c.wav bit @@ -1599,10 +1575,6 @@ 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 ../IVAS_cod -stereo -dtx 32000 48 testv/stvST48n.wav bit @@ -1879,10 +1851,6 @@ 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 ../IVAS_cod -ism_sba 1 1 testv/stvISM1.csv 32000 48 testv/stvOSBA_1ISM_FOA48c.wav bit @@ -2023,10 +1991,6 @@ 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 @@ -2086,3 +2050,40 @@ networkSimulator_g192 ../scripts/dly_error_profiles/dly_error_profile_5.dat bit ../IVAS_cod -ism 4 testv/stvISM1.csv testv/stvISM2.csv testv/stvISM3.csv testv/stvISM4.csv ../scripts/switchPaths/sw_48-32k_10fr.bin 48 testv/stv4ISM48s.wav bit networkSimulator_g192 ../scripts/dly_error_profiles/dly_error_profile_5.dat bit netsimoutput tracefile_sim 2 0 ../IVAS_dec -obj_edit ../scripts/object_edit/combined_edit.txt -no_delay_cmp -Tracefile tracefile_dec -VOIP BINAURAL 48 netsimoutput testv/stv4ISM48s.wav_sw_48-48_BINAURAL_OE_JBM5.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 + +// 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 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 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 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 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 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 + +// 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 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 -- GitLab From 39422bbbdcba18afa8211d4f4897f76936d030f8 Mon Sep 17 00:00:00 2001 From: Kacper Sagnowski Date: Wed, 8 Oct 2025 13:55:03 +0200 Subject: [PATCH 093/100] Update wav-diff to v0.4.0 --- scripts/tools/Darwin/.gitattributes | 1 + .../licenses_wav-diff.html | 271 +----------------- scripts/tools/Darwin/wav-diff | Bin 1701424 -> 132 bytes scripts/tools/Linux/.gitattributes | 1 + .../licenses_wav-diff.html | 271 +----------------- scripts/tools/Linux/wav-diff | Bin 944536 -> 131 bytes scripts/tools/Win32/.gitattributes | 1 + .../licenses_wav-diff.html | 271 +----------------- scripts/tools/Win32/wav-diff.exe | 4 +- 9 files changed, 44 insertions(+), 776 deletions(-) create mode 100644 scripts/tools/Darwin/.gitattributes create mode 100644 scripts/tools/Linux/.gitattributes create mode 100644 scripts/tools/Win32/.gitattributes diff --git a/scripts/tools/Darwin/.gitattributes b/scripts/tools/Darwin/.gitattributes new file mode 100644 index 0000000000..4cbca5d865 --- /dev/null +++ b/scripts/tools/Darwin/.gitattributes @@ -0,0 +1 @@ +wav-diff filter=lfs diff=lfs merge=lfs -text diff --git a/scripts/tools/Darwin/thirdPartyLegalnotices/licenses_wav-diff.html b/scripts/tools/Darwin/thirdPartyLegalnotices/licenses_wav-diff.html index 0b17bd169b..dd386d5ea2 100644 --- a/scripts/tools/Darwin/thirdPartyLegalnotices/licenses_wav-diff.html +++ b/scripts/tools/Darwin/thirdPartyLegalnotices/licenses_wav-diff.html @@ -44,8 +44,8 @@

Overview of licenses:

@@ -678,199 +678,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - - -
  • -

    Apache License 2.0

    -

    Used by:

    - -
                                  Apache License
    -                        Version 2.0, January 2004
    -                     http://www.apache.org/licenses/
    -
    -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
    -
    -1. Definitions.
    -
    -   "License" shall mean the terms and conditions for use, reproduction,
    -   and distribution as defined by Sections 1 through 9 of this document.
    -
    -   "Licensor" shall mean the copyright owner or entity authorized by
    -   the copyright owner that is granting the License.
    -
    -   "Legal Entity" shall mean the union of the acting entity and all
    -   other entities that control, are controlled by, or are under common
    -   control with that entity. For the purposes of this definition,
    -   "control" means (i) the power, direct or indirect, to cause the
    -   direction or management of such entity, whether by contract or
    -   otherwise, or (ii) ownership of fifty percent (50%) or more of the
    -   outstanding shares, or (iii) beneficial ownership of such entity.
    -
    -   "You" (or "Your") shall mean an individual or Legal Entity
    -   exercising permissions granted by this License.
    -
    -   "Source" form shall mean the preferred form for making modifications,
    -   including but not limited to software source code, documentation
    -   source, and configuration files.
    -
    -   "Object" form shall mean any form resulting from mechanical
    -   transformation or translation of a Source form, including but
    -   not limited to compiled object code, generated documentation,
    -   and conversions to other media types.
    -
    -   "Work" shall mean the work of authorship, whether in Source or
    -   Object form, made available under the License, as indicated by a
    -   copyright notice that is included in or attached to the work
    -   (an example is provided in the Appendix below).
    -
    -   "Derivative Works" shall mean any work, whether in Source or Object
    -   form, that is based on (or derived from) the Work and for which the
    -   editorial revisions, annotations, elaborations, or other modifications
    -   represent, as a whole, an original work of authorship. For the purposes
    -   of this License, Derivative Works shall not include works that remain
    -   separable from, or merely link (or bind by name) to the interfaces of,
    -   the Work and Derivative Works thereof.
    -
    -   "Contribution" shall mean any work of authorship, including
    -   the original version of the Work and any modifications or additions
    -   to that Work or Derivative Works thereof, that is intentionally
    -   submitted to Licensor for inclusion in the Work by the copyright owner
    -   or by an individual or Legal Entity authorized to submit on behalf of
    -   the copyright owner. For the purposes of this definition, "submitted"
    -   means any form of electronic, verbal, or written communication sent
    -   to the Licensor or its representatives, including but not limited to
    -   communication on electronic mailing lists, source code control systems,
    -   and issue tracking systems that are managed by, or on behalf of, the
    -   Licensor for the purpose of discussing and improving the Work, but
    -   excluding communication that is conspicuously marked or otherwise
    -   designated in writing by the copyright owner as "Not a Contribution."
    -
    -   "Contributor" shall mean Licensor and any individual or Legal Entity
    -   on behalf of whom a Contribution has been received by Licensor and
    -   subsequently incorporated within the Work.
    -
    -2. Grant of Copyright License. Subject to the terms and conditions of
    -   this License, each Contributor hereby grants to You a perpetual,
    -   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    -   copyright license to reproduce, prepare Derivative Works of,
    -   publicly display, publicly perform, sublicense, and distribute the
    -   Work and such Derivative Works in Source or Object form.
    -
    -3. Grant of Patent License. Subject to the terms and conditions of
    -   this License, each Contributor hereby grants to You a perpetual,
    -   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    -   (except as stated in this section) patent license to make, have made,
    -   use, offer to sell, sell, import, and otherwise transfer the Work,
    -   where such license applies only to those patent claims licensable
    -   by such Contributor that are necessarily infringed by their
    -   Contribution(s) alone or by combination of their Contribution(s)
    -   with the Work to which such Contribution(s) was submitted. If You
    -   institute patent litigation against any entity (including a
    -   cross-claim or counterclaim in a lawsuit) alleging that the Work
    -   or a Contribution incorporated within the Work constitutes direct
    -   or contributory patent infringement, then any patent licenses
    -   granted to You under this License for that Work shall terminate
    -   as of the date such litigation is filed.
    -
    -4. Redistribution. You may reproduce and distribute copies of the
    -   Work or Derivative Works thereof in any medium, with or without
    -   modifications, and in Source or Object form, provided that You
    -   meet the following conditions:
    -
    -   (a) You must give any other recipients of the Work or
    -       Derivative Works a copy of this License; and
    -
    -   (b) You must cause any modified files to carry prominent notices
    -       stating that You changed the files; and
    -
    -   (c) You must retain, in the Source form of any Derivative Works
    -       that You distribute, all copyright, patent, trademark, and
    -       attribution notices from the Source form of the Work,
    -       excluding those notices that do not pertain to any part of
    -       the Derivative Works; and
    -
    -   (d) If the Work includes a "NOTICE" text file as part of its
    -       distribution, then any Derivative Works that You distribute must
    -       include a readable copy of the attribution notices contained
    -       within such NOTICE file, excluding those notices that do not
    -       pertain to any part of the Derivative Works, in at least one
    -       of the following places: within a NOTICE text file distributed
    -       as part of the Derivative Works; within the Source form or
    -       documentation, if provided along with the Derivative Works; or,
    -       within a display generated by the Derivative Works, if and
    -       wherever such third-party notices normally appear. The contents
    -       of the NOTICE file are for informational purposes only and
    -       do not modify the License. You may add Your own attribution
    -       notices within Derivative Works that You distribute, alongside
    -       or as an addendum to the NOTICE text from the Work, provided
    -       that such additional attribution notices cannot be construed
    -       as modifying the License.
    -
    -   You may add Your own copyright statement to Your modifications and
    -   may provide additional or different license terms and conditions
    -   for use, reproduction, or distribution of Your modifications, or
    -   for any such Derivative Works as a whole, provided Your use,
    -   reproduction, and distribution of the Work otherwise complies with
    -   the conditions stated in this License.
    -
    -5. Submission of Contributions. Unless You explicitly state otherwise,
    -   any Contribution intentionally submitted for inclusion in the Work
    -   by You to the Licensor shall be under the terms and conditions of
    -   this License, without any additional terms or conditions.
    -   Notwithstanding the above, nothing herein shall supersede or modify
    -   the terms of any separate license agreement you may have executed
    -   with Licensor regarding such Contributions.
    -
    -6. Trademarks. This License does not grant permission to use the trade
    -   names, trademarks, service marks, or product names of the Licensor,
    -   except as required for reasonable and customary use in describing the
    -   origin of the Work and reproducing the content of the NOTICE file.
    -
    -7. Disclaimer of Warranty. Unless required by applicable law or
    -   agreed to in writing, Licensor provides the Work (and each
    -   Contributor provides its Contributions) on an "AS IS" BASIS,
    -   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    -   implied, including, without limitation, any warranties or conditions
    -   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
    -   PARTICULAR PURPOSE. You are solely responsible for determining the
    -   appropriateness of using or redistributing the Work and assume any
    -   risks associated with Your exercise of permissions under this License.
    -
    -8. Limitation of Liability. In no event and under no legal theory,
    -   whether in tort (including negligence), contract, or otherwise,
    -   unless required by applicable law (such as deliberate and grossly
    -   negligent acts) or agreed to in writing, shall any Contributor be
    -   liable to You for damages, including any direct, indirect, special,
    -   incidental, or consequential damages of any character arising as a
    -   result of this License or out of the use or inability to use the
    -   Work (including but not limited to damages for loss of goodwill,
    -   work stoppage, computer failure or malfunction, or any and all
    -   other commercial damages or losses), even if such Contributor
    -   has been advised of the possibility of such damages.
    -
    -9. Accepting Warranty or Additional Liability. While redistributing
    -   the Work or Derivative Works thereof, You may choose to offer,
    -   and charge a fee for, acceptance of support, warranty, indemnity,
    -   or other liability obligations and/or rights consistent with this
    -   License. However, in accepting such obligations, You may act only
    -   on Your own behalf and on Your sole responsibility, not on behalf
    -   of any other Contributor, and only if You agree to indemnify,
    -   defend, and hold each Contributor harmless for any liability
    -   incurred by, or claims asserted against, such Contributor by reason
    -   of your accepting any such warranty or additional liability.
    -
    -END OF TERMS AND CONDITIONS
     
  • @@ -1725,19 +1532,6 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - -
  • -
  • -

    Apache License 2.0

    -

    Used by:

    - -
    // Licensed under the Apache License, Version 2.0
    -// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
    -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
    -// All files in the project carrying such notice may not be copied, modified, or distributed
    -// except according to those terms.
     
  • @@ -1746,6 +1540,17 @@ limitations under the License. @@ -1822,44 +1627,6 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - -
  • -
  • -

    Apache License 2.0

    -

    Used by:

    - -
    Copyright 2015 Nicholas Allegra (comex).
    -
    -Licensed under the Apache License, Version 2.0 (the "License");
    -you may not use this file except in compliance with the License.
    -You may obtain a copy of the License at
    -
    -    http://www.apache.org/licenses/LICENSE-2.0
    -
    -Unless required by applicable law or agreed to in writing, software
    -distributed under the License is distributed on an "AS IS" BASIS,
    -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    -See the License for the specific language governing permissions and
    -limitations under the License.
    -
    -
  • -
  • -

    Apache License 2.0

    -

    Used by:

    - -
    Licensed under the Apache License, Version 2.0
    -<LICENSE-APACHE or
    -http://www.apache.org/licenses/LICENSE-2.0> or the MIT
    -license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
    -at your option. All files in the project carrying such
    -notice may not be copied, modified, or distributed except
    -according to those terms.
     
  • @@ -2037,18 +1804,6 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -
  • -
  • -

    MIT License

    -

    Used by:

    - -
    This project is dual-licensed under the Unlicense and MIT licenses.
    -
    -You may use this code under the terms of either license.
     
  • diff --git a/scripts/tools/Darwin/wav-diff b/scripts/tools/Darwin/wav-diff index 8cc94205eef6c3df56a89a3a7041b10815ed6635..e1cc92d32e32cd5f05328098218046a35d39533a 100755 GIT binary patch literal 132 zcmXR*Eh^5;&r`@KDJdwn($`PVEYZzLE7nWTEXgQM(o4?I)h{kcP1Y|ndG jNHI=HHcvH9OEWe%Ni?-c<0{UqN>wm4H#D~}H{t>SK1?Vp literal 1701424 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 z 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 Y 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%0A&Tx_=`tx4I8KtbHSt6h4}$!$nAkg=dIUq;P#6v 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%0A^|`M`tU6?6vbf|u!_T`vcpD|j}aX95*ZlA9S{ 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

    G?&OB^kM(+@q^& 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�mebnhLQ%djP!%U&#FC4|Gkz_9S`nRC*IF2nc> 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 z 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 @@ -678,199 +678,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - -

    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 z∨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 z

    1|2`ZajGIOqc>F)6Ja8%|Y*pA3!7 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%0A+5s71~F(_h4FmA}$CR{n_RT=^}WYvmVz?v;P5 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

    +zfBHKqD34 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ⅆ)m7 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 of licenses:

  • -
  • -

    Apache License 2.0

    -

    Used by:

    - -
                                  Apache License
    -                        Version 2.0, January 2004
    -                     http://www.apache.org/licenses/
    -
    -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
    -
    -1. Definitions.
    -
    -   "License" shall mean the terms and conditions for use, reproduction,
    -   and distribution as defined by Sections 1 through 9 of this document.
    -
    -   "Licensor" shall mean the copyright owner or entity authorized by
    -   the copyright owner that is granting the License.
    -
    -   "Legal Entity" shall mean the union of the acting entity and all
    -   other entities that control, are controlled by, or are under common
    -   control with that entity. For the purposes of this definition,
    -   "control" means (i) the power, direct or indirect, to cause the
    -   direction or management of such entity, whether by contract or
    -   otherwise, or (ii) ownership of fifty percent (50%) or more of the
    -   outstanding shares, or (iii) beneficial ownership of such entity.
    -
    -   "You" (or "Your") shall mean an individual or Legal Entity
    -   exercising permissions granted by this License.
    -
    -   "Source" form shall mean the preferred form for making modifications,
    -   including but not limited to software source code, documentation
    -   source, and configuration files.
    -
    -   "Object" form shall mean any form resulting from mechanical
    -   transformation or translation of a Source form, including but
    -   not limited to compiled object code, generated documentation,
    -   and conversions to other media types.
    -
    -   "Work" shall mean the work of authorship, whether in Source or
    -   Object form, made available under the License, as indicated by a
    -   copyright notice that is included in or attached to the work
    -   (an example is provided in the Appendix below).
    -
    -   "Derivative Works" shall mean any work, whether in Source or Object
    -   form, that is based on (or derived from) the Work and for which the
    -   editorial revisions, annotations, elaborations, or other modifications
    -   represent, as a whole, an original work of authorship. For the purposes
    -   of this License, Derivative Works shall not include works that remain
    -   separable from, or merely link (or bind by name) to the interfaces of,
    -   the Work and Derivative Works thereof.
    -
    -   "Contribution" shall mean any work of authorship, including
    -   the original version of the Work and any modifications or additions
    -   to that Work or Derivative Works thereof, that is intentionally
    -   submitted to Licensor for inclusion in the Work by the copyright owner
    -   or by an individual or Legal Entity authorized to submit on behalf of
    -   the copyright owner. For the purposes of this definition, "submitted"
    -   means any form of electronic, verbal, or written communication sent
    -   to the Licensor or its representatives, including but not limited to
    -   communication on electronic mailing lists, source code control systems,
    -   and issue tracking systems that are managed by, or on behalf of, the
    -   Licensor for the purpose of discussing and improving the Work, but
    -   excluding communication that is conspicuously marked or otherwise
    -   designated in writing by the copyright owner as "Not a Contribution."
    -
    -   "Contributor" shall mean Licensor and any individual or Legal Entity
    -   on behalf of whom a Contribution has been received by Licensor and
    -   subsequently incorporated within the Work.
    -
    -2. Grant of Copyright License. Subject to the terms and conditions of
    -   this License, each Contributor hereby grants to You a perpetual,
    -   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    -   copyright license to reproduce, prepare Derivative Works of,
    -   publicly display, publicly perform, sublicense, and distribute the
    -   Work and such Derivative Works in Source or Object form.
    -
    -3. Grant of Patent License. Subject to the terms and conditions of
    -   this License, each Contributor hereby grants to You a perpetual,
    -   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    -   (except as stated in this section) patent license to make, have made,
    -   use, offer to sell, sell, import, and otherwise transfer the Work,
    -   where such license applies only to those patent claims licensable
    -   by such Contributor that are necessarily infringed by their
    -   Contribution(s) alone or by combination of their Contribution(s)
    -   with the Work to which such Contribution(s) was submitted. If You
    -   institute patent litigation against any entity (including a
    -   cross-claim or counterclaim in a lawsuit) alleging that the Work
    -   or a Contribution incorporated within the Work constitutes direct
    -   or contributory patent infringement, then any patent licenses
    -   granted to You under this License for that Work shall terminate
    -   as of the date such litigation is filed.
    -
    -4. Redistribution. You may reproduce and distribute copies of the
    -   Work or Derivative Works thereof in any medium, with or without
    -   modifications, and in Source or Object form, provided that You
    -   meet the following conditions:
    -
    -   (a) You must give any other recipients of the Work or
    -       Derivative Works a copy of this License; and
    -
    -   (b) You must cause any modified files to carry prominent notices
    -       stating that You changed the files; and
    -
    -   (c) You must retain, in the Source form of any Derivative Works
    -       that You distribute, all copyright, patent, trademark, and
    -       attribution notices from the Source form of the Work,
    -       excluding those notices that do not pertain to any part of
    -       the Derivative Works; and
    -
    -   (d) If the Work includes a "NOTICE" text file as part of its
    -       distribution, then any Derivative Works that You distribute must
    -       include a readable copy of the attribution notices contained
    -       within such NOTICE file, excluding those notices that do not
    -       pertain to any part of the Derivative Works, in at least one
    -       of the following places: within a NOTICE text file distributed
    -       as part of the Derivative Works; within the Source form or
    -       documentation, if provided along with the Derivative Works; or,
    -       within a display generated by the Derivative Works, if and
    -       wherever such third-party notices normally appear. The contents
    -       of the NOTICE file are for informational purposes only and
    -       do not modify the License. You may add Your own attribution
    -       notices within Derivative Works that You distribute, alongside
    -       or as an addendum to the NOTICE text from the Work, provided
    -       that such additional attribution notices cannot be construed
    -       as modifying the License.
    -
    -   You may add Your own copyright statement to Your modifications and
    -   may provide additional or different license terms and conditions
    -   for use, reproduction, or distribution of Your modifications, or
    -   for any such Derivative Works as a whole, provided Your use,
    -   reproduction, and distribution of the Work otherwise complies with
    -   the conditions stated in this License.
    -
    -5. Submission of Contributions. Unless You explicitly state otherwise,
    -   any Contribution intentionally submitted for inclusion in the Work
    -   by You to the Licensor shall be under the terms and conditions of
    -   this License, without any additional terms or conditions.
    -   Notwithstanding the above, nothing herein shall supersede or modify
    -   the terms of any separate license agreement you may have executed
    -   with Licensor regarding such Contributions.
    -
    -6. Trademarks. This License does not grant permission to use the trade
    -   names, trademarks, service marks, or product names of the Licensor,
    -   except as required for reasonable and customary use in describing the
    -   origin of the Work and reproducing the content of the NOTICE file.
    -
    -7. Disclaimer of Warranty. Unless required by applicable law or
    -   agreed to in writing, Licensor provides the Work (and each
    -   Contributor provides its Contributions) on an "AS IS" BASIS,
    -   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    -   implied, including, without limitation, any warranties or conditions
    -   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
    -   PARTICULAR PURPOSE. You are solely responsible for determining the
    -   appropriateness of using or redistributing the Work and assume any
    -   risks associated with Your exercise of permissions under this License.
    -
    -8. Limitation of Liability. In no event and under no legal theory,
    -   whether in tort (including negligence), contract, or otherwise,
    -   unless required by applicable law (such as deliberate and grossly
    -   negligent acts) or agreed to in writing, shall any Contributor be
    -   liable to You for damages, including any direct, indirect, special,
    -   incidental, or consequential damages of any character arising as a
    -   result of this License or out of the use or inability to use the
    -   Work (including but not limited to damages for loss of goodwill,
    -   work stoppage, computer failure or malfunction, or any and all
    -   other commercial damages or losses), even if such Contributor
    -   has been advised of the possibility of such damages.
    -
    -9. Accepting Warranty or Additional Liability. While redistributing
    -   the Work or Derivative Works thereof, You may choose to offer,
    -   and charge a fee for, acceptance of support, warranty, indemnity,
    -   or other liability obligations and/or rights consistent with this
    -   License. However, in accepting such obligations, You may act only
    -   on Your own behalf and on Your sole responsibility, not on behalf
    -   of any other Contributor, and only if You agree to indemnify,
    -   defend, and hold each Contributor harmless for any liability
    -   incurred by, or claims asserted against, such Contributor by reason
    -   of your accepting any such warranty or additional liability.
    -
    -END OF TERMS AND CONDITIONS
     
  • @@ -1725,19 +1532,6 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - -
  • -
  • -

    Apache License 2.0

    -

    Used by:

    - -
    // Licensed under the Apache License, Version 2.0
    -// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
    -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
    -// All files in the project carrying such notice may not be copied, modified, or distributed
    -// except according to those terms.
     
  • @@ -1746,6 +1540,17 @@ limitations under the License. @@ -1822,44 +1627,6 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - -
  • -
  • -

    Apache License 2.0

    -

    Used by:

    - -
    Copyright 2015 Nicholas Allegra (comex).
    -
    -Licensed under the Apache License, Version 2.0 (the "License");
    -you may not use this file except in compliance with the License.
    -You may obtain a copy of the License at
    -
    -    http://www.apache.org/licenses/LICENSE-2.0
    -
    -Unless required by applicable law or agreed to in writing, software
    -distributed under the License is distributed on an "AS IS" BASIS,
    -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    -See the License for the specific language governing permissions and
    -limitations under the License.
    -
    -
  • -
  • -

    Apache License 2.0

    -

    Used by:

    - -
    Licensed under the Apache License, Version 2.0
    -<LICENSE-APACHE or
    -http://www.apache.org/licenses/LICENSE-2.0> or the MIT
    -license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
    -at your option. All files in the project carrying such
    -notice may not be copied, modified, or distributed except
    -according to those terms.
     
  • @@ -2037,18 +1804,6 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -
  • -
  • -

    MIT License

    -

    Used by:

    - -
    This project is dual-licensed under the Unlicense and MIT licenses.
    -
    -You may use this code under the terms of either license.
     
  • diff --git a/scripts/tools/Linux/wav-diff b/scripts/tools/Linux/wav-diff index ca9113cdb32624d8d1cb32c65346718b68fc0994..29e63f3d9a4bdf15321e09c65ba87a3f437436d5 100755 GIT binary patch literal 131 zcmXR*Eh^5;&r`@KDJdwn($`PVEYZzLE7nWTEXgQM(o4?I)h{kcP1Y|n#8lGcYzZ;sO90cqf?v literal 944536 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
    1l%8uj!*w0BSXW4S*K>9^Ka9!Pa-6Gk=<^ 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�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 TE}Gy8Zh~a7;Xy% z%Ylt40fpHgxbqtzMzk7(hAEr>2Z45ULezmm%FQ1Z!x3!okm3Lt)_MPhE9f$C(0~ko z>sOG=Ktn|>FaG@h|KD5U99^IfZ{1Q8l?s5)r4Pp7A*pPpl2xIdv?-JwYUp}DTumht{r@Mfs z<%eQZrv@9w_)U$A({sfyzhv0tkCD0hg zX6SH1iIPXJ>Ei%sM1iI#I*-3dW&z*g2^|jLg?61ly(sW_07rD}VW^ql&h!>=op=oD z{4zFJ`yUiup!y9IpUrOsK!$_Ej=u$Tf(pun2WSKsRMWvHJY2e+B|uwxI-MmP8-IYR zk@9-iUibZu%|9&o+g(B9HPO%9Xq%H0F7&b*2#dz2Kie!Kx;I;C0>F$cF2acgA4-|w=c{QhTQ{|5s;(} zswQ5Z2Jw4AvwrtpT;K+s`%rrUoZVGGovQBA8!w`Gz=y6e^0$IUeZlLCvOsdpzZm&j z6F}NwB}*=746oO8r#CcXzPtviz+kfe5ZSj}tPC%AfF>b2zc{pfD^*2yvI5A-phnk= z7AA!ML3Y3@NuQ^1abb`+xY8;K_UQH&@UZsgC|2X&#=-%bVC?i3X#Nq*-(tkVz|j07 zgumr7Gl&t&-y(;j9P?XOJ;F zIH7)dAp)VpJz5WxPJ?QIoV4!IYih>H%J6bC69WTi zAP}VZJUau!OCL~?Z27BLy1_;;wNw^twjIM2ht@KOg-1b++YXnK!c)17QE zyK4Xc`wz

    Y$S-TtKrfSVHO*+*Ut?tzSXG4lx%TQdw{_H4%y~fE0D!f8hq&JPN5U z_x}C=-?illf9rfuj|E}?f6F8gr`L1_+>j^#{{4Sh_#1p%d+iNSN&wAS3WMe=^*wq` zxj{|>l|7*O1;|8_5~x@Jt*?Et;vYN!{Xq_Z6i48i)&|7yu2$&0_96(>JmLr03v&c7 zh|_EO4de*WaYZl7kWvGv>CnUq3pdw)kZ^kss>wU=zc2+I*aKRza1SH^Gf)Jizt^xPd4EH%NLw_JJF5@Bczfoe46e^ZpBcKIqJ92S@;-g}-GVNPn+s1V|gW zrN86vzyB{6fbPeX2anf+>l^T82F-6IJi6l*JX&v;gn=)&`Qy=R`V!^~P!_8B`|rQw zc2I}a!ngHpiK}aOiHe4==DXrpP=^SVq(MOnQW*qxT&bWZ^ll}`V=gKjpsUwiI)8xI z3^>L(LI$ot{nIogOHgHBa{m4I-?RCjRI#LE^RYyi&e9_uy|qU`jeY$HQ2+G!>$M)e zrm9E=ytwuU6fmVzp_dr)2K0h%ef%e&&cIMqvLCc|$zvb(`wKzi7~h{E?k{u&9Y%S; zHS;A`$e>;z7noFmT?EyE?0d{FFoi4VAKx=Co z|ADSB^z6J=zWOz@Pv31L5)ayZ?$LP>8jYr%prc?Ke}SSAGE(mdk=YNA z%L(A|nCFQ2bL>3nsCm#e^CBp~9S^<|cFaDM0utfhe$uno0Th2CVQFriA*`VI-1qg)3mP%oFde{)u0DYbKKI6B|4;jdJ2gd_j9qdP&P zEBuQO7JGq*Qh2+Vz>9!DOCa<2u`zgbn{a?UW&9JILSHkx8ve&D5qx@CgZ6^v8os~C zvH>k!wY|KTjp4;SE99{z(D*&Ly&MJ_4H^#fp|uQ%!kE*!T@FFH2-j4xI3>^$MAd7(%TRJ^b?*a+;eQ}zJO zf3W+2=Fq^s#>1eV7_?Ic>2w|T>1BPl3p9Im`~@2*U4h3x!RZNf&09#YN8=mN0H+6N zw(6fpFR#u&76zYQ@I57t;DJk@&i_83MP0XEJPY~%A9NiNX#U_isQa8M0_wWBo&b$a zzG?o!SgHuQd*X(N<_VAH9|GkHU=utnPnXIXUV3>AlA$d^4ZJn$0K7w}>H z?8Erc@DgminSYz1L(8`cP?zF$F~2-R=lAC4i1{mrhJW|Vj9mD)If7@f9Gf55yKX3+MUHf3IacKr>X& zc^o-^xO5%^jUFJ)x4QJY?RRW`aNn`{09S9_h30=&j{MF)n*R$FtAbCe2bzvGa>ZcRhHm@7K%f+C1{0`qs{M$-cPrL@7riUC}{M&LbyYO#I1toD8(BZRhilv?Sx0R@# z@ce%S#o&_1ucan{UF~Z4w)vkDWG~1|70~`R*jgJ2P)85eYC=o~Du9CrwNCl}0A4KILx$RqLSJN4T9DY}EJj_i=)1{%=Pa z`CIpc+5?RLJsJP_bfzxw>C9c>(^(7N$^o9*KJIz|T;cV`E-( zZ5bH^8A>aByURf9p!i!3g3Ro$Gw|$ov)BiX_|4!Ym@7f6V({8iyWmA0$YhX_{H>sA zTu-P;9^DQKVADLBk60i={Qm>kIGMOdujwN@SQ{YY)4%^9OG_<0kAp@c861zfaWK5r zfT@=E=rvvZi-lnqXe0sE$d@{w{{07EC52+sVV~aA1)%f|_BMYDsC;PtpH^p8eXelOVLkYK82*{2A9qU$SK;g z*Mkv}qCu$_l!R+9`C2|NmIWnV+Y_GukE57T^7y6sf6%!?@4-&*eEvd+g@FMaUHq+P z|NsAgIUlsTt(UcWE2vt3;)^t zd8$60{~^x1@*38vciL4V(o$EI$-;HP|q^LPjNhTmP4s zfx6tF-nK4y2-&08bnAB(h8Mo?LBY!3+5<8TI<2u9RM~+$8DNw6n?b828*CVh`CCB; z)_C+*FnV-W3wT(5FBWdFv5YU}g^kg?1Wn?DH!Q%z&jYl-8MH=*f18LUbcomKf@8xE zt|ASWUMEmlB~{D?TCyevjvtp^r~Qr%Kcx7ZLAwXRi&QUo{67w|(DVBf{sz!MvLpYt z3!w4d#V@ymoeCcA;%@2?P- zh6EXWG><{|tagig_3|)*wo7!Db9jPIENJ}*nayKz?e*C2*!=LmWAi~S$6kjEj?F(n zEs%rFKLq%jL1Uc!+kzOutB|3JTzZ`@H2<=4<##^l2v)S8iGkt30~SY)0~O9L9jkZJwRiL7J7l`oa1>K%d`UX@H`Ro1v|IKv)V~PF_P<8Y& zfQf+t%yMk_ae%)Gw5Hmz;l~5Wv@$sA3=h2Q2B)Fg6`*TfJvwVAfUo}RH33aq^S6Ms z@NZ*pIZ(mr(HqYA@)M{Fez5r=s7dD!UXo;UpTBJ*WErzNa-+`X1Ah}}bEQXbI1B$a zcjo4Y><1r0nspECT{sVLo_M(wG(Okx;{$(l9V7Ijj17#aUiRtredE*Vd&8qM^npk7 zKPLVrCD8m~Zy=LTuirnPPH~@3f6yt)l8(L6`|3`v2VfQ z)C8&%Ji!MIuLjo+o-97SZhw3_`9TeL7yfO5Os>6A`yHDf-**Jt>~+Br5|^hP!N);> zwiCJVZ;NEc=R8oM;&|X8iz~+|myXbPpe7+`_!6A1nn1^(dGtE6c{CsU<)4d)TAb_M|(gaunUWmJM=+?~C0U;7aqzS~i9k{n|hOH&`*eSPq&&=ybi{ z(d&A{qtkVPM=xs$XgsCUb%jT-D=5Wn@aSco_<{+v$ZLm3FKC6V>j95m*1W6#{(}~R zfzGpB3+f7i&+za#-U>SA3cPL%dSRwVC-^KQkK^F=cA&vWk6zOmAT5v%(TgwYNbPw@ zf4uW^^FQ@+na&#?y}YhgNT&{`9cPa*0B;3r{vA`w1>(lMe8RxM(D?g5=zzbr_n=l? z=fBSTFYY>miZ}k2w+svnoj00KF!Jv`(ELH>;0q>?UXOprT%R$d9d~`gz|eW`;5!kI zUQt_9&>3+iVh$ap&p~1W&CmGxw}JUDKztqq|0Rgef#AOa@mUc3*UgXl`L~G(cZ9w< z=K8ia?YIl;qnOfTF04aphE^~JoeJsN+54g&G% z{9f$pahyd3d=*-+>Dqcw-<$VaJ*X$jE7-`wunTnil;aN2{o$VA5aV_NFG~hB3_*?K zW9$u`@hq>WgUn-MDAocQ_6$@R_nHRPfDKz*12!zDmW5%L4(PzR9iX|^*IUx~^A3Zr zyG!H$$9mL|g`u~Zf#L6eTe)BVK~qD$-5cKg`QQBWH~*xAy^|UKfBfJ0YlG6)|7ra1 zS(h8KFr@LPv932{Vc?&1h(F*%>o*7fDaRBJDqM6>_}K81u~eh;hU$a=@(i{QmN7Au zNHzX`&&bD6F4TEL^&d!7ZU++sf2%#{L=Y^oehU=qHxRMzZVZq0+n`uPiuF4nK2ogT z1@Vz${T_&q6zlilvHswg>qBs?UjxVb4N$B5dHonzdQr~b`kb8{=7&0+e6ry z()jaUrSYdcO5;y>@`=CT$(1w+arHs6oj zpU?2<{Or?h?$ce*0onHl-nuyZEwnfEKR^P!P-QMC;epn{@wav|Fff3}6$PMeC(vE5 zj-5we7sGnX9=)d1eV_wn;4Mz25yxDwGx7^K8}MtoZt&=Jz3$No+VREAFW@b}FA%K2 zuL-^jj$hNafnO7J-j3@AXgO&z8&vFp%E_$-pqj7q`wL4(&~gX<7C}(=xcNV0sj*Av zi;bWu56}uQ@EC(fuj$P<2rqOV>^$Mod?W+Zz=n3tJ^vr`{D0o3cPYr!*CHDjq4Dqe z|CnRvQP2P9OV(~+bo_RdvGgt2v*s|*ftw8;y{2VvSQ!2XNI=&UoPdS}D4bsJMv4!z zJ#IY<;&JHsy9;=Y2Y+iYXbP&kc7aE?>k61t-V}qK@)C6O2Izz}keo-a>D6Ku$mY=h z0TM5x!P7~Q@f_&DS+|?Y3V~9m6$<>Vb)ZAix3POP|489)`Ud8l=oRVg<+*t9B~SLn z6pah~+YWg2imoVTVc_4+;*{3u+URO{vh&2t%V5Rq`x`+Oz@fAW9-TiAykhA*?$Gd@ z-=){xz@^te+@aw=mm~kSAFlk{KzCmscj;*1tpoX^xAMOYf9HkrQ?LE_x53Q-b-)n) z2Tb$K37hB9eDI%7FN>yU^MMA(oglY)YX0=-75(;#g<&VCc=v#HBX!RI`wzXjJ@&98 zSk8v4n3KoAsl$c0;kBqoFYo3eu%{hCo`#%z^63uq@aXnS@PTYn@a#P33A*^>6sRWXuCwsz zE{pK#_5APC>AS$EJ5Ir;JC6gjk=~>8w2$U#kIvW~o{SfLI(;|z^zv}|fbK)-yy&a> z61+RacZF}SijZgLf6vZ~pe5d~%Ny#X7)n4JVn8AB@;0R9?k3^VdBB9R!%f1YI}EfP z2IQUq@bV3i7d=4xjzGtjfQE1R`nPb}xX|DtEeB_;w!m z>2_V<%Xk9pkyg;+7Ryuot)ML%?)=+Wn0)ycA1+SxwfyVb`NNmr{isLt|Np-HE=PSk z4J6)06SCr{!V(=1B0;@kmC{b`m~kD;b`WyFSdmD||ZrG(aZsgWEr# z^~A3m!2L;|Zr=-V$(Bs;Slt1T;L9*j3Iti&`P*>^XoSm`-{ppH=SxuXLzJh!mZ$lf z!3pU9Nl(j@{LO4&2e1f&z4aV&M2rl`B}^rkeY$-&fGqRrcHIGT4agn*t)RQrU%m%j z01etEy#_q)b<(4|_P}f2v~_82(dh>EW(kMKaW~MiF9z^^fX6}3XYl9@;DC%`dkDM)?UUhejRZ|CTb?Qv zb!|CNs(8#ziqYdZJ2=)sLxP|o{FajyR<5AN2xyOhM`t*PM`yTz2V}1lcz_#p?kU){ zV26NKy&4_>GpB&6gwB(Wkj_wy0Hb5`WBZ1m_sgtZ__u`!FgYH4%;wnq$ljInR>RK^ z{7qfplEO!T*|GT{yCb+S^oXtbp}h;|MHkMKAO|+Q76$F>aOJ$|(g_maZ_WPy|37#j zzVRn$sJ}=Cyf>loC+PIFBL2popf=bJa67p91_P+Z1GV|Uo^S_wf)UiULmjyD>^%6g z7PQ{u;6ol)&QmU(7eT&nWdd#EnCi)R!N>A|55GI8th;p#G!S+ibcj@d45&>nx&a|# z2N7uli<}3ge;pZ60~;~^1sbmjKw2IK>Ixx`&-gajFuIqzBaNo;H~(e;Eg`jx=WqQ8 zcKHD&&>B+E{y=aaA9VV;qDSX_@camTcK)R&4`@IUGzb9dCD(0%vR6Sx;FQd^?}MmP(s|tf~O2>J6y* z|Nkt13op2M0uR5GTm#oept+TwcR*txB}ZQ#0eJ#8!MX*+={2=}3aysWJaOa>s6_?Y z&y1Y^L8D9ou(6|R7uddL4fwuhKJeHPe16IWV}mnv>1#jaGegdt2U%a>t3Xswrppc%!#lYaA znXG~8{ljqg!^U5l-za!=n|pw!{Xx4leY)MD`$rEJD>m5hVckCZ6SQ#(GBlV&;VSi3+5g~PEa6L1iL%{0*`P(O7aP%+^M z-7~uQWjiQRA%mg(Ef+zYUen0Oh!_UIdG> zD+AfjD+*pk=qG@EPbh4D5VQd}z>$BO2$}mqp_X{!*bfSo1_v{EKPY?~D71YGazD5| z0w14G^@NXqKO{K*?T%~w`zAPvgO4!yc@|Wdlp+m%?;?Ka8}2^z@-rA*)_|72Ku4uP z8^l0mD1R%c(T!4$hJ%;lgUV4~kS_3^U1!iZJ80J)B#V}oyBhxY=`}t6fCY4c3!=RV zUC$32KL?%l?b-aEu}BV9Xo!MuwDsxy_CnwW$kY6-;0;}!_g^G1fjcIx6(CE&gXyIk zUV(sieA&@3b3;I=ym9t4gOZp9xmik_t#BOaPqgl2A9pM|L%jz z=C7~iAmP)Qe|#8RRGzBXIZs{R^+d5Z{1C!9Y#~+b9a!uL&LYWCl5~qBDZLJ`jBVD71}G5`^47aOK~|!q@!6)3ft`NpSNIr&1H2UQb3)?F()UfEFx+ zwzkLfw}SS^d-OUod31vIaDa}WXbNFuV0bwlvFl>rc?IKBQn|+zeU{Ua}ILkxcb& zg9>1z@;6Yx!#a=yRQ?LM^0&l-$}-R%7)6inO7I>S32;ILmA?}ZCd+}6ZRhzHy5PEp zzZG_QINA4 zs?gg-1sSk5(fZf1;9fVhsS^Myhb&JNd!V*+tikP^k{U!C=K{Yb=t4a3xfq}}jtFSH zM#J?0zlQGx=z$n7%l`fUkNp@7&_u*daLeY6EU0DU!SDJVbSz8WMNruPZ#huHzM1Kz z38;k%>Dqyg!MOcW86@!kkVj{rz)NwEh3M-~8{dFhfk?d=E%5#V&{j@REqtL^yupSs zv{Vq}%T8wj$1RK=mKTb+UR$^tKJl=;P;Ag(!{`m#9$UcZ(Fxi78QEZC=v*2Mn=*#X zh5Pi{e!0%V@H*GEjIDNUJsD@ znx{M&&wF&Xf;hgdZ%eITdobDf9e4)*ACF7 zi=YF_e3-#51I=9X`*gksb`QMJ;1LSx&bx|-cWmyfxl%NBj^U}cc5dAK#hh8*P#_9XeZjgb0E`8*f%h~oC6AL zXoU)j!7PwEPzn9w<~c+RmVs3enoATTBX;G+_JT zzP)Gx+g#cPs$VOhH3Ve2KgjUX9fq*pT8X(wujy_P=dV6BiQ9>}*3#b7PF*JlyFg*FsD8jpao6;fSu z>=1ZkMClBW$6Q;!@wXU(3*@;{2J64b~Stpj?q>_(A+L)Ou2V2=zcek-qHmxT-N;m|E(3oWh~M5 z>0S!HIj6f7vcJ}Mf~V#gaME<007{p3uQ$W`HxLu=|NZ~}nCnGGpYGZTKHaG^d^P`o zQ|1Lw$~^S<|9>Cm+6Ca7k;_39H)xy&vWOz>3Jb%F9cMtPh`)6kNMSeF!CjKn@7d0i|Tnev;V#575@O+j<35 zF7E~9Psbgg)n^`^r59d=YJq|mB*<8jb21XFRIA{mu$VD62=F5n|wETUf84auurG!0Uyo79-Xl#UV?f@K9HF8 z>0Jx55_x37oC%^lB&^Wk9=l!$^9+t<8tvx&cdN7^``}zy`X1ZEN zcm8cATJBaR8YOpqG=KUqzV!GHGT5g(cgO36NaY8}f8Dt|;QbFz%isLX`k=z~|9KC~ z<|Da75;3clz z$l>qNy%$t^dUWmu&BS>2*8lehoqqoRv`=U30gukw6R!n47!Se?Sj)h`;07HEKUn+} z=n7gN}-CmI3=e8ti{5cPkSKsQ+(z{6F}53D`$FL01*` zvhe$K=N|Bd8VGOics9RgOf3#WHyQKkM6D@E_!F-_8#uH$0fZ zH{*G9*PZ}{=}R|IMb{mB!L#`fbFs1GKF}btXY(H>{#HE>28J}pM(Zd){?=ch6XTB` z{rCU>e@De7CH&i&P2;1$*Ccu~HGlL=<2+bm2-VmE>VPbkI$3;A>UDN?ZSedVOH6;2YTZTgyPj2IDR7UKZuo z3LgItftEr4=W^hmbiDc4f#!eg4*ZjjIdJ^rZvmxi&__sMRfrh1#7i*jHZ!2QPT&N9Mz+9pUIurYK2k5}{gU=z0wqJtEIgjQ;%m-oX zwqK@#(p2lo5>p?`hmaLXjtw^4`}zC8vkjeRUQPrJQ#VvI?dR{C$<6=?N>51Oz7~VT zz{`zn3=ED9)y(_(`})~I_40FH#=ky{_q=*}^gwsCFnC%XE#Ysl0j=3|V66Q360}eQ zT$qB^bGYztV{>geP@(40TgT|qdFbUXPzS6<0#qOLvKsclva;c&7cE>r|GRV^YJMc| z!oSTA96pW>Kkk=h@^5nk2TwC-RnrN_h94h_ay)wLSX}tGxiL3CVn6su=->l3P~bUo z9&qHm@OrxE@uQ%P_25HwJ)8fq78yD=RI}{o@3#Y`v0hfj4mQa7hd!Wl55VOJ8y9E* zzK-eOBQ_V#Q?JDw!Pox06aWQbZyoc&M?9PtIuAAfa^-I+Vq#!`?uu=#0VSB`|I9_H zpmY`=#NRRllvo<9gGw?X0qEKMpP9cU8+6(#IDN*$(`Qf`=Ry9~K(Ndwtz1d8j$B%$=5<($=`$15Pm+=oMY9aNLr{$3nLC1y~ zR?z9V4vdxWU(N#^8QTdi=zThuf*PnE$6G-yQ&4>dSvdq^c_1$Rfh-yV3-*F5D#LFt zL{>n$c0WPul)Ay99BvGk4~^w zeqo3le&?nxhd9>@i*tLKL3c%x<6O|)7+*kDujNJlwx{4R67l`~{SQIi-CowlcF^SK zxfhx2pqbAn9-2QvBP*>}K%M84FF_~Ix*9%l+yOce546Vd2Pc1f{$Efx^tF)3|Kl#5 zm!QeCCGzk8|KOR8=AT^r&7e&Up5GtvH#mWWJ5PBqUf^#s12t|e&-1tFf&}@uiMzI3 zs@&<)c?pss=c@ce(km4Dkwm(Ej-kM94M zXJ|gOzxkIG|D;2%y@8;`nq1&bp}mp!L90MP8@OCKE<18uF6DRZ4LsKTORZeegMabm zVqs6q10_UrYc=^s;s| zvoY*~^fm7Lbp8WvaahX?J|(dYBm-*FfU6v^%w%Rzm1Em~9MpX0z1slZ{7}{msz|4T zxE zHx(Y;>^|KZ;3|AS==_Mz(hDBl9-xa4PIz=XfKFWLblvdU5Y`_9oyi6}8XmkW^%rc)9`J(a?$Q#)FNm-_$lv111-_i8hLzEw;s1XAzI-lF@;&GI{{YB}V~(9a9UC6XgHp3Q|F#fT zM$qB{hlc<6%e>wBx5cnR@`5?nb?fv!#iZC8?h zd5RNuz6+>ebmhG0+Ib2xzk=e{wh&OM0lHp6^CN%DOHgN_p@x@XKY!mtPSBwzKN(Bq zH!(K<(B^Le-JlJ=;h_14E`KX%KJ#TUCj-NThF{vH*`EK8dVubQYW^wY(Rr{a+tm=f zfDn{leLx}6$H2g_3si4-e1GJ~KjDx&|27|12FK=u7aW^GM=PpxGB9jna&5W9-zvw+ zz~I_>613}^@kH^#mwX_fAoYJdntuuw&qtI;pflW%`+u(d+gwB$9Gee-R_b%|x1Rys zlh@1A;M#e>vzNue&P1{K~;vW3)=ol zP?cc`J|YycwGvce9OC>1sW13j^1+AFA7pvmgtXQ2*ULgsd?W3)JoFOO+CdIK&>I1gm2a`)@>YgI*v_|WA758jb5p>XY+qX{#MWx>d4s{T$FkA zn)03pFaHBA;wfTdV0if%a##_hedyiGVrTf@r}HCp|D;E6A!FyS=3grOEek>OVx9Lr zG|zi5e)s77*!)Y6zXg0K9ViVR1|6Q!`I~>6B`Eiu===p~X?TL0{@`q9jvmgSTvue~ z(ObyEzpWD5_6P4{1s&9SoD*bgY2RyIkKRI1&F8{-3gnE|O3>0936I`FP+P)<^AK1N zTn~ft19;OtsMF#D>bSFkI+6`Ga{KxFwy`lVAcqEIr!Tm0<8S={YIir-$Q|JCWdcoT ztb_y!s6K4|&sf6k*iZ*bczZzM-|PP0w>OQ^r*kQ2%+mvOP33V{(6uC>#rPABxh`Pz z*a12T6ofrMS5-n*)O+-91*v}_Jrgv21>SWGI_w&B5o&iYco?Ah=YL4o0(8(fC<;OL z+NkX3?*na^^a0(8aTVMhv0*#F-?N1k)Pj#>dU+ibE#0mckV~0qI25;L`~z=6yPUgJxYomtA;j-uGm@=hON5JzLjIbOe0ZP#!2ViNebqWgu1E||n1PVo_1N=RwSV02E|9Lha zV*=S=@)y*GdIB=3^EBwza?qI_&4-yvY<)U^cxc`z<8cH2o0Uo`koA$FX@V8Wh<|Q2)ZB(21D*2+77GH2rT+uj3%)U=^9IDREuhu9FDrli|L@4Z zjYY+y*OY%h3q$h{XMS+8*!-iEzcu;?sI3$D-?P_^$&>MnN9Xa^ddTJX|KpCm1s9tC zfd(*+IW`}d@7N66-TQ&R1#}Ll!!|}A%jf(p>p`>c{M*=FTMks7aP2&Z(v}8y)}7HC z)8Ov9g9~VX5^{4I)LVDuyx_`t!m;rOXeU)km`C$*mX}4~;t<}JcI`X?E)P8r?P%A| z3(&zbq%;I-a)C3ZPj~GB{uYtn|Np=A1KnTo&7=7zQ;8}l=J{KKzW@KuV4=(wdf9jS-yp;J4x|Fo}p}Zsiwlb#X zXZE1|Sh}EMf`3~W^TB7})1W_rHe;ng1l?F5JFyNvVrzb2&v}9K1SsLRu>bx4AG~PN zqxlzSv4i1%!;|3lvkU(=9t+pr!2OQRkM27*AL4TDbp+MIpq*2P9KoB8W`6ts|KLLw z7f$ec2FqWir(b)KqCfK6|No6YK^+hNc3*U}_II9u9v}=m_G4g`w1wcJzNKAD7 z1RZ6}z`$_uAsgq%PDn`n1r_{A#R)jDL6>TrfENK1!Npb`;{pDj&7k?&|K~mb9|L85 z%b%d2;Nd*gc>x@%8-M@*|9TUomIohP_5ZL>Z^i$YTtEN+cQt(S+LB+ufrDQVylB@0 zTIwEm1uY+7@aQa^aolwQbW{RV)Ph$lzNnrKI#cL~OXmZR<|7q|Ih4bWTNq!ze9`mc z$A6DrTiG%;h8F_ge*E9W*fABP*`wF?qbO*r7Ozn$xGC>k25!oOcQLx*G-2&M%hBd`N}{&mhV2D{|qmAbl!ZS1`!S~V`F&D?$gW42AB8fyano~ zutF5*m9jCsHt^|XjRmQ6^yy{&0ur(C>18!WIO4fS=eZY`zk%J>gXA6X)!qkR?1ad8 zAvAy-0d_HkTcth z`5Zb*|9}(XOVGW9ogW(iftCOlOCNl}#&e*<^^ZftGkb@Ia}5q1rGLS7*6Re&U@Rj; zvHHOmJUkCNT>o@_IQW81#h$03!}Twc@_*ny*lWXsFNAnLK$Y=8l_6>Qhot2{c$*-S zFHdxyXnw%x(aFAn@!LVhQehNN_Llzn9{@VFUm*4{WIq7t*2P}aS4C_Lu`heJ{X^QF z1d)FMn&^i1r@-Oe{6+w@Qb5r4gGYC`fJd*XUMnc^)_wr@FkB4}y!ZvGNkQ!;kb0E$ zL7<}(8Nltw4WN3q^KfVB9uLq?flk*IKAp$^i&iaXWAN-edffF2B(IfT@#wt&U(_2c ze5ASd3ZrN99nf7j|3#lK10`qB&P3N0;QcwBt~02^Kv5_}X> zx9b7WAy2IbN~K>u0?oenvMNH9&G;Xn0g);(aq0Zf{D8mt2M>Qc=&~x0US1P~@u1@% zTp2x^k8-};3LZ2$&hFX#{D0~Z^u76~g9pF!cMr}F zuf06^_k-0$FNd}qD;*$(Aoy8{^Vg_j2 zKg5g)*z`9aWOr@3RPn?U5=EYupE)-Ca&QE-l^A^aojx}IbnxYO{^-m39ptGN@Mtb? zG6RyQTn#Tl!_S3(n~`hhSI6e3^`4iXg5CED;=XSck6-QqH@UWh+{om@za8XA5N-5Y z#D{-BSk#AqKUma_`DS<$n=-HqZlk*3H#Qf% zd-)nP*4p~5jtg8+y#%dO0H?t2&^_M{G4i)81$72`S@)c0Vd!Sr@bWe2C>zkKc+jw` zN4M(!6z;k9lxjE2hdA55n{siR?(?^Ryh30+3Zyw#D8yx>% z^MD>7YIq640ktLmi~1~PW7q*I_h0jX_qe=f{dS0>1awmo@^V11J0O;p^uK)i@BjZ7 z@816Sk6P$AzWD)~w+A0(P&&h-GxWpQjm#{I^Ri!l8sGj)$|33qN3#gy@Kg__R z^*|P5qzAw22@igk3m(0#pt)<0P7d&HqF&z<9=%gvfTF0A%cGa~9H`UX>BZsE=_KIM z%gSF5s)n|L&dK)ZWb-)g#sM1X^k_~~03FO9xE{25-ATfuGfd#MpGU9j36I`hh*^vt zy{t`Ovm7}fJK!Bb=JnU%Gfxq0o}+|ECuotqn*^wZ;?Y}s{RL=Q1!%P!XjJHhH3w)w z?3iQc`4o@;EUE|o|NozIj0Nmz#%@;C#cT|=fB*lt=WpM`!obk$!T5oHBQO7AHU|FI z)hrAQU98-T*%&;UZ9x=6sewm#>w=B zF~0%rG$9=)u;YS1Gc)L-;C?j``r zP~h8`-6UQLgXW|mju%eUs$aL`cQ)AZRUcRR9^$?99p-q@5-X6)n-6eg2}HiM`v3nw z$g<k4{hmYy|~Rng@U02@r8Gi!svh04QC&UI0oLlHkjK zat?ZQv*v+(dGUcq>wz@>de;s7txC+G+k>BUu^w3f>Q$@-Wd-n!Nj{zLL6;_doeTCH zXu`m!QxB9wz^86ErwK54^n#a(@N2kk;MZjL=!J%{yMRZp=rwi*h8GV(L$R#~O3MuY zzj(^Rz~ItZy1}P=D`*LpXX8Ic&`Au<&q2EpTfadTyc+&@=`P*yI>V#aw0axJmwQ3Z z$>NK8Q8tr_!K1qs#P!$-D)#>W|6dPUyJmTUzZEo4=(vq(BlAnppvec1|BfH(gZa5Z zIVum_W)lquH^9Ko_2@0V{sMIA8A#e6GHZRzqnCHVPf)4H(&)i>;y>tYyh2bi=wgp~x@C>$urc^_+dcv9C*K7N0#D0pp8QVNTsoiobiVt48N6*q(gQR` z4Z3H+r}LwS=D{KrPsXF3o#$Q)IR3xxk<8%H{DXzRH3+l{vh`c(X2bt41Hpb3tt_^nc8}wgzdkm=4>{G zjf|iw_rM4K4ZKD3*ckX*JpO==(Pp+`;cvG6^Z);W2h5K9m>C$EIuANte)m!f6hOVU z;q%xSUaWfcH$M8TKE9+b~hL!`Ra*q7l zzIgQ73V}3!1jlM^!wXQ&`?3qv?eAs1QVveAkYXBKq+`jAU`w1B85myA^ysxcI+u+B zoE_5m>wOozIQ9C+|1|!5-vw#>`K}vY9{ThD|BG#Je*AarJmT2!h4JNuKmY%`bk|-0 zrBzTsdGy-4*RwIaTn9=52OlVSG@oGd=sb?BIS~vt+7+Qbk$Nv|V&wn8GxIk5>2k0W>Zgxnw zw#>q#*8_B)*#(d01E3>Ix>kUyvCtbHoxTq|dU6QU{kPG+-J+OFh=zPZvcwXUc}hV2ry_*?kl1qF053aTcX;%g zvVl~9Zn=E>@W+3CZcwM3x%nq!sfkB##DAaeivK>{3G7Ih!2kb`bP4?b|DtU(KsMa} zFIqc;jlmGqDscdfynwd@cy=B`+y(#tzi1RpZQu;>Ajb>N(?7shL^apmU?>5fEqfi* zfrhV`fS zfr^U{{0B{+&thZvf3EWbX!d*m_ZtOFv7kkx7iWPw@}EC|Mo1v*MVsFQyeO{!|G(4q zhexmL506gQ7aqN=i4Q<2$Mu6pFRK`6@Tt>vhX?FX#pABfxON34tP>u`T`z!RvbXlX zM{ntl7gs?Up!4{PyPzgt1Zb>K9#m{IKVpFuz8zyM<@e}joeMf;0$ib$8WV@(PvX3Q8@1ea#{kl|54oI&Ox$+dLUN7Qqh0a>Zy1f z6bDiLUo;fzAYL>Ff#VAnKFx1%(EOga1Y3UB$OCyBQr>`5$#L}jE(THo%I|;g zl9k`5O##_(|G#MO6oUCZ3#K-43Mdl3zmPnRp5H+SYJrOE|Hpp(fARAnavXu;2VDMw z4mRfpoj2#vT`%C#>3hMWmzRGrD93TI*J< z!RG+Li(cOg9-Y2OMQ`Z>kIv)&FM)1&Mn4b7qxnsMLr3XI!Prf=c*a z)~wgepymoVuR=o`M_w)6@j@5m#O5ObpbQ5(u>cX@;Qe}#_2C|!pZT}3ceq~faJ|aE zjn#5G8-pXbTH6n*+y09dfI<}12mxIY@7Z}Awp5P^baNeetQ@QwKnjldcD@5QNS=a>1nort4gEEE zS{?&!WxLMb3OWq#lKeq*9jiIklg^_8E@BX;Njk0h!LHw zpj`wWy{y;44IfCyrPmg83xP*3cxTjq(UJ*l3=Or{7)rE!x=UAhUVH}HFT~~1UAy7G zsKo>}h7F9EYrkAu{+CK41rO(X7KU9Bu;3}(20qsaTv?SsO?uZ4DxO|%0d>1y{sJ|M zyJJth1YMT0ACy(W+2qC4!$1CmxuAnPI}U>iM32tHh_=B0|DwylyB5B`HuUH`{94na z^DvTC${IW4;_g^R-`tct!e(wP~yy3Vj=+p%UkIvc` z9^G~x-EJHny`>NS2Pl{@@^3rP%K~adDa0Or!3|n+&Hdu>9Z>TDdOlP08v~DC)=Y5O z=6b>7xa$p&Z$QUhBARtCJbF#TK`MG{FT4=D0cvy0L)(+FFG2fEAmgi`@)ca)K+7M{ zdRy>*KK2F+hEhR~Zf_2cX3LZ^cEbZNIT%5^%C0l`bY~0rbSGWr*kc67=T~ES%O~>oS5KCCqhqnbcQx~be49w@Ncv8=`Q2&>HGlNLR`w})A{|s zXgw&XdMo~e1_StAet7gk5)$M@L?v*F2A%X(y5I$9)UToTIzwr?V?!+iLrH>9XDYY^ z24x7ai9BEvb^Af(*)fmfV3WX&?BlKrKwW+4ttKA5wL3g|!Q10JdVLr8bbbR(mn`!G zEgtbb(ZT2eI;!WtgM>%7Gl$22(29~1{M#5zK&cZHRG@kvdSaMIx9<**|DfTg<1fq( zAcr?{_^trm-qg9(;P?Oko}GUU4|rI!F6?Jx;BVvuFPdicnaIYlUxAZ>;lF5SAGqk| zZ_#mQ0Oi_%fB*l3>#)w&2vG0vcx%GH|Dc%^kmQ8laORBP|Nn#1fyeRI1qjg%zu*dX z{DLbu022ilm)k%c)y~!nzyJR~-g*P7H|==q9We74ScuW1S9JbX@aTcSchJ&=tsvz) z`9Y^W%SSLUd}{>>Gx~JqUf^%h0U6lcDgm8@2lpwAe0o96yo&!G-4X1NE$$_(ptA9T zM{_j?LkTB{EouVZT=4zBsCEwltq*?x|IaVb3JM>7O|aKII_JLl{r|sD=T^`KO+KAVe}KET zwg+QCD{R2t`7gSrn~i~A1FW836YM3AZm?M%ovjMrK^sW7f(-Gs{KwxS1NP-yuxZ_U zL8gIMse(@Q@##(G@afh~>t+x|7ra7tqxGy07@%BX#ptB0Hr~`0jURtABYB<+6xXHa3NjX z!^Y73<3E4P0(J%lpYE+7eJ|&Oc0ja#^XbmL0F9ZPE^s)4(gZB{yTJj7h#FA3<`--Q z1*b9#HDVPo)Z{a@nf)0?c|)2;ib zla1lEFk;Hsv-yYx=(IjXP*^`;DB$N$qWULRs$@ag>j`k6X};79NZT4gY&|9`fmSQt;`H((vg{GVo|VWZ`-6F$>s%@cmrAt+z|G zeLD|&HXji1?H2T9eB{}D_=B(ItMaG5%z+%fmaoe0ycS1<7wE_h%TuMxUJBcSqSaKV z8x+3ZN(?-@WhJ`V7(6r2fqZIs$<^>bC_Q^v+6eQvfDRY~&3-l>0!e~agg*DM{Kwzc z=mct#)}HXlK5yaC9mB!l(_Oqz;S~!iG(ZB3{4H~BK>pkcDtkP- zr-H%=RCFVXIZ(j|DhxnpYJTRghoqL{tp)$!C0YecG0YOsWzwNLUO2}yAT`~5EWs%m zR7!!`UHq+}!@*zP6bJ7Y=Wi*H1w~@&2H(u*AP;%GlJe!uR^_}w$}I!FMtH}1cva3>psZ*SdypHABcpmb$zd$N;_p;XwT z*VZ75jp4=Z{U9I9?&$<|>2`zk!L)bY`!9O610=i^ZY#L73+MnfM@=VovN60oCIean z%EaF)3mVqy1{cw=PAq?m918=(OK;F1Z8tdMg9pn?s;0~G@Nnym~F9;hhb z*KGap>;M1H{E!s;8J1!NASqU0E-1hAYtDV}>;Hd#&8ZiDflHTOP<;X_EkK2{M=!XH zexaEPxz(&i5af{Fy#b)&w)5DFiD?W-dE3LX6(qsm@(1J*XyoX0MU^F;eCMcDj>WI5FY3nH-6354G`W8 zh#crjFMfg66%Zcy8aIK~9T46Dh)Mm@b8^%=iF>jMZ6T%QTFo`C2x z0L^}M@4W$T;m!r$N88cs;uJf*LgcMHySz7(nwghtoXy^UkO7=bT96ukU5} z{{R0=1`%-50VVV9+6{)^z&+6aqRT;eDJ$~W4Qei` zfmeq7=XBfyDt3JNouB)*KH=|(2d|Z>Jpk!_gNiFq%L7_Ic=S#M6=y!3&tB}^_Tzsu zxX@taZ_xrT#09s8JUYSkx=-g)P)+aC`2)1z&a?6V|KI=rm#cX;9%lgW();Pr`0&3x z14D`AE(Qe#1`p7I57+r89sG8Xv4qWWKS&JRmv;iKmb~rLxfIm$_UU})YWVFX=$PvN zq9V;~46l9ui}EzHF?2I{K*!HvB}6H4jj`oTpaSRnf6;kO;Mo+2&7dExhpI;iG%J@N8AC?A6^ftv;%%>_HnqZ_Q=qkAr>Df4m(sNn=|d4ig49-ZJ4 zz^8LBDEoPI&IRR3k8W@g177C6Ul3GOw!Q$3p+NhXpiz(&g5b!#@e9-{Tlxbe+r1Z5 zl=*an%P@~_aFO7_2pVWz;BoLVn@8tfNI-%6tFI-I)?0WsA9L_%K74?`B~Xxo!L?h@ zm+@)QE6>jVzMAig9{E@vFTd-<4BkBH(an0inT-Loc>bM7cj$uu7eL*XZiva}vQHG}bcZfJgH$KK`i(IS!Ui_3Uv;5lA7dUc*3*Wm(62W zYEo%>PG(-Vhv7vJ&=%7y@K&li77qTFeF6*&p51N=9^Jj50@t&4CbvTe*OP%_`ku%j)A{L3v|8Y|KlE(KT6-dED~T~Fud&ny(H-+ z=pqAG!*8C=e-!weR6)fFJTV#G_UYDbX=G!7bfUls9lG+;rxQF><6-%^)Y`)ue7;Gk zQFrYI4@kOrQI`fw6#T9CLHGWG(!&lA10^ANc7E~HyaQSaUaIQTTc_aB%VOqZ`NN0b z{RgNe-O&q5C#~N+I!kwyN*W$`tq)?sobJ=T71Tt2UFy*d&Ql(c4va_VR#5X{2e|3n z39cK!t0SO!)Ti^8M{h4Eo4&BTjFd<9L5_n4icj}eP|wAu6KsJ`=Uz}+_wD@Sqn*0L zr?YkkIL|WP^R@f~Di=KWgQfvM6Q^JSr$6BJSKYN6Als@O|6c`7AC`*z7i|D7i-1fN zmfU|i8PuI&JnYd4&J*A?RJwz|1+?wSquY0Zhvh*Jezy~#vlTQCd31&@fNZw?f859N z0cblm1HbdHPVl~1=(w3{%Quk2_*>Hc{r}$#mSF<<_V{s-`#ku4ulra&^yGKB?$c`{ zUsiENcwc= zUhwJO3yKQRBs3@&LMn0aPP$JoE}#1M-=i7a@M9oqq+731Ahx>9I^98^8;p|UeyW9K}+HpOZ|L$i~sv{SO53uj>g?@{8P)uumjvf z>~sYky8U1DRV}FSO5Nex`NpFe9EA*}GOyK<6rsqKfGQM_FaC>esAXdS_jS@i>rT4o zf~pyx?yaCU4yd!BfCkKvN60CfUd{%Y(5_0`TtxAzh`s(4~CKtp56AKp4|)I z&Zj=zsV988OLurct1kXlhmZgNzjXQd|G&p^aK8YQwR}1ut>0d7^7ZN53rfX4o#5W4 z$MIHBe*vlt)J5_DZyyG41?y}DHOY>Fn>UPU$6G3rs)dCa5pz6ay+*R~CN z1^<8+2(^NanmE=9;xKx2gCo|X8{&4&f1uM`z%BqC=<*q~P=Oij5zx{3%`X^>Sq#6u z)^4z2=PFh8Xg>B2G(yeaU}Me;UW4bs?{fY%li^8^){`Fmu0K8aT|Rhp{`Tzr^1?2b ziNUk;m`5k1N8ULVls`R=gFBRr$677E|NqbEu|t7@0aQ&gfNES& z-@142fiK`;3y_ar)TV+w4-#Q4Q2|A&yifPi6CefMtrxz41_{1}8yRN}6M} zksPZDa;yq=$3lh-H4poAmL7P`2X!g9qBgt@D)d{V!Hxwjtpb-}p!H%Z#Ti`2lwzM%9uFA5Zz$2~i*Q7JUMp7iMs z-NE0|1X>={V5MDZ=Ghy>=+RsF-vhkk^<$CV>jIDxNB(U_&HuFdTOwE(7{E1pF zG{{51oeSV6dE)o9yvE-SS~up2X4D?=7<+dsr1$`@_48mn;nCR( zDn30rTfq_C3rS0z_kDVog2X|cAc;uuxFk4{f$mIiWoBT24hp+20B@st_Tozfs+vTE z8gRcIs_ZP9GAkryV0VF!gn6+HO_?xA*<4U%;nVs2g-0Y41IQhRm>C#4=Ysm`9&l%S zbc60o3!U)da3m8$1Gute;BN)ZuXsS547!;FR26oEO9PMY(1sT?qCjp357K}SYfX#Pkp+Vf^v}ur1E)@4mKMUJPiD;&7gt9Zb;}sdo>Vc;9-wPFRYLihk;hu zLu)9=V3Y@Bw$!70Dkyiq-~p=!1vLYIt0Jh~40ATPN8-`D*8*I-mG1E92Iu}4QDE24 zg$TDgfMh`{Pr7?OKukpM%DaDs)c;MNVf3ql!;w4WounX9=T$M|bD}kUk6Op@tx~11K6m3=fa)sgO0eA&dc@ z|Bv`|E(NvLpiLGJNSg}U1K@A@_U-@w*RdYGtY_!4Fzf^!#R@tbzti^wD4`=Y7x`OX ze*ORd!%IPsdELFBJn@nhq#fjck6zY;Anjj3 z^GlsSJ(?f>@#qa?@n}BC;bD1^zsVMqRxJwo|M?*#2N^XO%j0|{t>y0YD=J6@`QyacfpG|&J#N~#;& z#0FLS{LnFi1`B=uDF-r0>NI7UkyK5_$M9oXg=^0JUKJzfWiqI#?nG)(7iYe8YKm7 zF7Je-VDNTs@UTd8Z39C|Ft~b#9w*nU!{OO{RDi!jL63ofzvYlB$VOJdQZ@$A`OXaB zelF;U(s~ilEaP$T(uu{Y3=A(n=`t{Qz)c2S^9$=AlNqbucK>v5eBTM!f>HyTLs$kKSI;td9q(R)s>4 z*4zt-#NyEj2}hWPTfw?|r-Jg7M{h4EjD32+lVvZAD?pmD*?S2zgx7h@qkArB_Q0om zFKF5n)Q-}H&d#04XJdHH4{1MmHXo4y%@+wm=ZD_I=7*knH17r3&Hx(Ty8mBvaz1E= zW-e&v0W{kWzP{yui7M2{rv+>b;Ql{oy6J~!w+U!|&$Ic6fam}7rLR4j_hx{mDWFDq zfQ_m>;L%;WivW?_TDO847yKIF z{uI9;xN*U+;ktldu=U0luwKykCuq_Y!UNTr{DQ4NAUseR&M(-?@fEBRRFv}zwn~6` zt`qnLTQwle27bX-3osKh7b4i|0pWo<75sv&5fC1z3I{C&f$+d{A%d+H5FU6gM6k63 z!UNBR2)53E@W68+f~_kcJkSgWzhLVQ2oD^of~_YYJkT+JAfG{a;5iM!))x>ScuqsG z6|}{jU!xT~rykJ4F9HoM-E53nd&hlG9sTwq+x#Qda z|DeV+C>py}*XDwzqHU+;vN3oz|6wVO@i@*pHJ6P6G&QmB2B`DNY}yD~7y~IGp)-^E zIiN*mAT>xusU>&}v;@>t1I<^wa9RQ?Fjc)FHh|WP`Se1DZ+gM0XO{!Gu?Jc=oee7L zTu(qpR(&o1l=67=+Aj2EV|XF67&Im?`!@$PP5h)p+@lvdhj|4wmFWSR!xRLq0)teJ zVE2LNFnhAu7{CoQ)l)ejFQ#tr)qLvFz4-wn0|RKk0fR^DH_+@XbQ*xab6L>Qg=tAsTWd;V1%i2&EP&HNAnL>P~FDg zk}LyCM7^Nm$D^|sRD5`J_kxNaP(P#B<8Sj%Ch(?xey5AAm-t&icTjY1Jp$^UbZ!L| zPcD$|lS}u~Ga$k4t)QVh@Ek$+R#188(Fy9*ZG8Zi-1`DVb?ya~C?4H=KR|e(a>=86 zFT+<*?%NCMN_lke6#(;U7kKp6C3tl11(jqTouv&PjmJPOeUHwqpfbv%8@!qew1^Kf zt>n=i$I<)?bYKvuVO5&u(Of%$fq%*Y&|*dbEN;2mh8;$>MzhL@&dph=vU4AP+G3a%$Q5BPL1 z1qBGmDo@TMurAWUM+zRyp(nsiNK0^G?Ez_J^0yoU&x}_5aO`&1@Hp_-F09+9%$PcEC(vD9h>bL`CDT|K@r#r3ZP~Sef}07QP4z{I|pbK4BXFf zZTQx(fsuiMk-zns2xyrVY`hqx<%Ku{BZE(OX#=!p2A@Cu=F!~?il!H0tc(nv&HF$m zFz~lzLQDXqv==fmj0`U)g9M-h6TKavb=CaaCV(i9I8v|nf45-ourE$=L2~aBJ7nll4cKn)CLHlMw6Vjjsx}Yju0d%rC*idMn zymu=oE&jjU03PoY<8QgZ%)rnLPKyluEr-E_3UfiaJi2?qOMp91c^rJD06L~m@sLOJ zDMrx1_z7_FgK&?s>jsZro1>r*@ac9v;i>t9zo`N=PSpH^gTE<{nSsHlJ9Y=?ls9X3 zSN`oj)-104+d{0_91lJc03CtXZDZ|u@R5ec!RH)4oY#H$*Bs~YusmNP+x(8vk$=(w z@UWH!$Z3ohTw4y5On4~`;&k_dg4CniQ^Dil111m6gZxd*Ao1p(Ec{JBLBR)CA<@mk z2%5!x%*4R(|AYrb+}hEHzxg7vxS zPt6bfO`vN9n*Ve1Hy4A-6wm?rIiOk3Zm22`4Nu089*oC5mpy;1vkD=6=R#lh(w!U7c|5EdxoKvjO-IvaqYb0+WFnn@-Kg@Ht4iAHi&wc&RZVcTS3v|(Y*~ckmR9x zjlby^=uCvp@1XTzO`tQNB$PVMv*9D;C4x2!tF5Qp;M@U3^ zbT@$ZSomn3;BQ*Rz`y|M7khMqrb|!oH%$i%fjr^?R}Wge4BesZp?RXHyxVsIs1N{^ zPL?N%GP->m5NajB6)t4VO7ldKcQ>R?>hzu9VR@p+rn?tZY9W+^?kx4_^lbo{E8pD< z3UOF9W_g0YIhGH6cw`GGzClZML46(&+v7NR9WQu-8nKoaG@bqb|9{`sZ$7O5wB`2t$0u73ocy!MNU8w+? z=;&|**{j9BjdgVj$gunWMPDVt?Ui^bEeSgA(FL^G`7QLUYmmQ?95Fi)q;D>$=kC)9 zUa{lRIrji$niv!!9=%&ZaxYdd1yv>Bt~%&&XwaxlFJ#Q1^WFzzx$yAT#(|LcnzkSefU+CoEpwB?=$Bx*jYyCi`VLDqu85BIQ=hOKdJT%=a zy0eXi;YBOR51pY4pajxG z6W9pw8QLCD{g9D%un}M;)D%#H#$rkU$do^z=z=sR;HH3zPKZOmW6n@hK;_6z$XFeC zSRLveUyv!=Af|wGEX*xn4#X`WCe##Ap|n#BJmQCJiYUmG0GrTZ{Xl?-Q<7hqL(e2vc!S8YywE4x8-{l?_hbOeMFueG?1av~@ zQP7PZtp|MgT^{)IyS#%Aydt~L5v2G6Sn)B>&g)oI7=cu*TLM~u`pu{FBX|tYgWu(x zFTV?DdK`TC2EWTOkM6CY)Z^3br{Dv=BzhlcE3r>^8>o=<=-vj}R02A4lVO; zYbj8c2G4Q$bj}4YCFqV_-~(F$-wRq!;L|x3yp-TLcmV+@+j>BUHbE!9K*}?p&R)=Z z0v|{z?93!XMDuF5d>9&R)=Z zOP|iQ1t5{mrJ(he9-X}nVBTKPdP|Say%WH^R?vD&kIvQwphc0LQ$g!3Jvz5;0P&84 z*IR-d3+6L0eCE#w4~c?Wkl?kk-L2qxn$Fe)(4L0wtzg04)(G$- zTkzHr#v01xgS zZ_W4#-8cdnV({qQ3X*ss3@Sb#%S2rGx9tT5lt=H@o4^16@8;_cWA5zr`1k*R=TvV{ z0p1Nxd7xqM(vt30kVJPcNCL7_tF!m=@BjZlc>ec=g%v;erbzI)TVPk7V_;wa%k#JH zhGcz^hh9vW$H4FsRQ)?{_cpxk*Z|(n;e)gbwN|5*X z^WFGA^XL1?^9zLO^J`88EwTkQ%R%)aC{RH)Ve=0r{?>J%^B_S6gKdV!A~-TY!Sq6S z9%wEXVr6NsYX_JG36E~Dha5ZGKxc3`cJ{G=u9@hZ#{t^B$iEE|3Eriup54Cw&P(R@GvbVSxhCeO}muiZVn!Rj6PxAlP(J9bawVPatDoGSeN|9{8sX`EmV zsF?s>(h4=`wGe0{1tBrsa#^D29 zXw(Z{$OYQ12kIU}*0a7axD4Kh1gd^P8OEcxHvlBt?U>=w2_6OD7jTRKEx`T`8nMlH zl=#e_@2SBr5NN?K5b43MIoIJkc(J&_chHbmeJ^Nc6cpqj=R*f9pbH0mIv>4An$Ezm z3*2gijV*wOIXt>$10vZNK;0zhz(VJz|NmbudJpQjw}P4wE*;?J1#&|3=$*Rr*Z=>W zUd)|Lux3Yh>j6**bvoIDra3^-4%(IhN(THI;8ejc7^MH1zurs!Gk?7kzh`eLeB%OS z0^$Y3N>H^Ab`>HEOa*5Frfx6h&Q=Fd7U*?G&I13KN(H;Y65U{lm!Or{9-XBN__sUR zr**bAfR=s>IPvofc**k%1c6d0WE{K~G;0A0T#$n8tx}*Fs7}`jyG%fBRp?Z3?^YSm z%_*Y#5o`?n+e;^Sbhm;QWy1##T0x7lL2(6YO?FQ$`1}7qvRgV^PyGJ>zq9xB@BjbP zn*XsDb9i=7QCR>w!Lz$X1#~V8bY#G{dx^>f(2QN@TG0A1@VEjbUAc6Cmw9!AP3i`l zGyN zRr>S)Kg_=>pmMwyJm>DyZEF|K#^Ct>x<@y-vj#ek2(AzK!)tBpbti(D=fCQNeI%>He4>G*)(a7f2XUx;y^A?zr6>S{}d(9sZVI zzyJS#*#t^akS?r8_e9WOibr=NsDk$BYz0?t-JRf3hVG3FplE|mo_041fO!zdcQ<|j zm2se{!0tu`hyD~!yD)@9S1f9kXy5!KKyI8}gdp5|mKHZZ+%?Y3GMhB3M-JPKQ-ag%%K{`M^1FjFA zpvJ{L(0Xjh%0*Be=>gd@2xfLd_U?4<1?}qc>0AqHXLxjO1+_DLAPW^eI(tDIF+94b z9suQ!&Z(gGg-0iNKL)f#0Zmt+=7ditxH;j`*$Zk)_;l_Cw;wu}f*KCswpu56gPTV; zxK|EJ0HE~I4c>YIN&pZZsO{m=*$QexfC@-ZF6e9pwIn>?Nd?@XfF~nRyTYT}b%IA{ zE2wSZ(>WK?wwMZTTXcgv{;(!SH#inNI>9?Te28di@NZ*{3x~HUW`{si-7!9JlMX@1T_ozTb_b;HoO6i!h)M&i2kSsDAFK}e-BU(x#Q#i|B!~4M<+z7;#YUmT-U$s949&2-*VN;Bg!>n#llZkbx>N z(10JPLi_B^Q*4z~If_dXN=VvoxFlMMvv_(!+))!Fi$i0Ave8x9bM}ZBs#Q?~V%|ovs@| zL-_pLrh<|;Sm1C;Ne5T;Zc?|4Z$VfGR3+Q$@pKizywPUwGXeb&~ zvVvChf;xt+2ORjP9*6EhZ#~IB)%AqqH+Dw;7DLd8BP5T42c;F685kTo_JV@Vvl~1% z4GJ(&xI@RLp=}WU)3rt_8rgyjO;^8TVDN0-3#ua+ z_*?ivF$QT?fx|xpG`i5e7PQI++>!;)DRqJeBRzT}I9}I+(-rs(g%ksmT7`qNYaJO4}d13UTk7zWOxZWx)n4c4Brpx(R^6K^Z!}?7SJgc9?jK1 z7)suFbX)%yJ?zKE@cN+#q`T(<%}6Yuj0ARKi6k@|f%DMy|Du!qKzZnwN9PT+43xVA zy9$+f|!dj%-TgTrPvs4nY-j2!nya=hjR?=OY8 zx1E}ii6zx6f{8tN_nCjAg6S%1+B0H8+0FJ5PX+0IM;!~8fKn?=l`=MLLSZ5 z3?&~ux~qTu7d`IF#_$?p-VR?jhL^LzhU^9D{x7=B7t|#KI~Ozo2)Z!W50r2`x*>TK zDMrOX!HpPy_h>$hX>25WHycCC?NS#HNbUg5o=s(91RslI0GeR} zH`**cx;;4{jTuOV0G=-QfHq_x6Po`66+9Y`fR1oLI`;v+Xnh&}HfUenu4%@B;_4PrUjX=$l$RY9HU6etR`4jRcdw1U2dL8Qo(mEO5A>S*fQ~!yWW4LsxfV3dR3hZv%cJk{ z|D;Fj$r82(OMU*S2Rxc<7l1b4Kt^&JEcHuST@By9mhtHB1zFs3phO;gg$^$p1B3H1 z7JYrs|3{(5L6!JuzVxv6UBKTY2Ck)G$G}YRWqj|``QM`(QssBYE&#h}u{Rq7l8bmi z%_-z%cHm)n@ItQ6wV=hP$H3zmY#psdU;h73JKkFG<^O*M&;dUs)Lf*u)R}>ylw!<+U{E5LZyb&~XFRykJ4l-6#p5EQE*z4{LB>^EU;sfF@o+e(~tFkpRU5boZ4{ z=coUo>%Bl_<2Mh+dmf!z!4Y8rR?7InqH$X3*uyX^zKT%vnm)9gnk^>!qY7`Sg~t zf%dC`(oQdnxkvK>Hqcdi9H0=NdJw#+1{VL|eo%u2IR3wZ;vejsmXo0P|G?h@s;Hfg zv6$dt_%2^wn8dc$XulB1Ruul=#jt76BPOPU@ijP_T2gH#R(U1 z;A=nwA9|9|Xa0Q9F_GXQE6}Ee)}-tI|G&(-4!Zu2m)D1lq2**L{|g^i28NAH8V5Ug ze|dw-WN=J1{AA>By@M82&A)m1+kP^G<7r1`l70$kW2Fap+Q&tb&0~jtX>pN$PG*uv zCyS&<^I<;Fst0heiHaw&Kz18=bk~AfqM-csVzDczSq_?J;co%mE(+?_f_eswH$VaG z3JqX;k6zK8Qfv&Mdvh6H?4R)C|H~aL3=E*<0-*K^X#J5#uc*8O$mx5*yv} z|8gQIMV$oIEL@Ha_WGqPhPOeDLRh3Id05`yZ#e-Dan}jQSS0j4{)6@mf>IYWUOY5! z_*lN+Zvw4}_vzjWjuz0-#Eg*Z<6iw275BhWKIoBGJ`^*9%LhF?pO92~izY}IPRzFJxzc&MSjvM{t?@G8rA+J)vX(7^Tasi2U+a31)=Qe5Hp?!`n5yIw~B`TyVWwnwk% zSy|BR>wQoSH=*pu|JQ0By|#?&K}QJjN{N8R6 zgPQ!Ehr#3h{H;+S8+!MG2m9697#Lo9f)qwWjR%beFo3kDf<#@RqR1y$6*)pfD$|jT z;dLC?UZexAf??8Lj%*AsL3eY3W*#APEHB(Z_MHJ8sr4FE0CgUQOr-x8?F9t_Xz2Ad z=Sxsu^0mfm(BYF8U^|5Rifi|T47d_(uizet|fETMk z>mR#etI=hgKvh-kg%`=ikXU*72^>nU7hqd{PJu>Ad-s9{&|lbr4jq6jDtK{7fPvwq zJ0knR-nv5?C0( zl@0jHHvZOk;7yJ73XJ@%;EAqoc8^}(&}pDUQNcY5k6zJqU(h+N;A?R~^TMF}B|v5# zZ?*XU|Gy}N2^!-7oy+F|S%d{D6+IvaS3;SfRv4%w^N|!7Fc+I4*p)` z^Y1^XtuqfaP1pbq&n=*JVW9AYu0Mi?C+~O2QW0-i1Y7*rcIk|RG3TEK?w-#LkA0wUQ^B(7KUA%u+uvETV{ZE zvTOvY2Bl5NMJ|r~0xl{J{F<&49Qg%UR2=yQd{hJ+`2|8$6dd^lVpI(HHDeq2HNkB! zeogR-Fwm6P|Ns9%cEMsF7EBwEf~lAH&}2}OghUY}G+aRg)(jrK5cSa0fWcB=S3^C< z1@Tx5C}+I{oncBPNB#f}r}y%5LmUYj$pp>MfW!5;D`;sW1L*W`a3b6ZO6=e?*y(%X znCk^*k6zx-lR(~qIQ=-dsQ`9)ukQkn&R)$V}Hy5JPP;elPf_QYdf{Ko23nu=l2fBMf zg^I$#hM%C5AwWZD-L0StHTYXW4c5+9kYhk45+h_jA7ooEuM(1ndPPktv-?g|-j1qTK+LHzUp1rmSjSJ0$FH#;O3fCK9N1ZIX^p!G%IfZ}hx3|3Lk z;o0pj;Mr~N(Fxfz+&dL?LOW=QJ7{=RfDyD;22>t_E~n@O-wpdeKmk+~Oa|*`_vnR2 zpYH;nUQ?^4fB(Vf1^24{B^JeKWthlJktzmQA|b_+Q4 znpYu`S?9qGj4dajBfR`AE})Aj+yDNDoWJiF7VOdZ2C_8!vqv}kXa0OPm+mqZm(Gul z-;S}A$h&lYY(B}6=D}Zg$fNaADZj^Y(6y@!9><+IKuIT!zuxsh^GTLYZV_fWE^!j`S*nOZo+`(t~^S6SQwfl6Jdw{olw1B4<>K%~An|o^~ zKu_FBZ2tHEo9hHd{uURovfLT`+hP~E@NY|92)@%0)adc(HLVI`VR(`12#FPaCI*IY zhe2nRfi8|=eCliYj=#wW%zp2y`L1-qOF{pmjVRo#0~0qnFjO5xisrRPlTCvMTq1$}`sq z9>-llH3I{DGeR#!J?Ic%c%C&q%LwYNfDZ`w=nb9lBGvWZfBtQ~paa8OPVl#arkg++ z2sC{6?I0sRq#My|8|w+~^6)c)yF4ANAVWeY>@x?ow2K-+ovv~Pkf*xCH9&Vq`*ar= zcyv2hcyvcdfQ}Qs3`v&!El(I27+!LMx+c&gwtHEN8^FGSl)SDJ!0`<|Y8dVn@L@EN za@=*tG1mjk9=)vEy&!)>{0OPF!3A?Kc+wfv_JE%54iN>nm_XCpp!A2-QtCC`%>eQY zG(@I?*6qFsa6t}~7Vu4KNEf$y^qN|FurTZbZ6pU5SfwWDp7!i6H}LEZw*Y${G)P)H z19a3GDCN8`vIiv@(5;=|{nCv`KuHz3M(7otE(|K4-urZZd!bSbYO_MluSPi!8Pq20 zJP$f@y0dhLN8`_WMg|7{_Ac;}DbNyV3-Efu=N_HcK$ljFhM2N3I2~)T1qp-iJM?V+ z&sVNucnNy`Qo|lmuoQm_XwL)aN;k_3rLSH-1!be&UVBiT+x+l{kLJS?HIN<#gnH1m zR$%oPOJ8~RhJW<1yjaHeIuy*@*8l|``xzJ^0BWC4cMC7bLZ9v)UeIzM&(2@InumNj z4|#U}@YOuw%Xz|6@sqFOAy3OsrJNqk2L(XJp7PK+)Sgmx$A&s?hEfB^hB_XGQcc4HjtzCZ4B&&CN~K?G zK{b{LfR&XmkN4-Yf{kC-It)!)9|Dt|F$q*uqhs$u?I?oS}v6e@oziX{E+eB zL(b-t4zER8E|swF1U1rLbHXJ!PdM)X^Z)yQS7kd#&f|`a=NT9{7+kx{SbTbyf`Zbg zTX%*rsBb1KX~f1b!Kd@NOXokA&QFF19RFW!{_($5<^Sbw@Fh(pj}d2_K|{-Fx5ji2@({2s|$7J6J0h2SHu6VhHqbLf~p+w z`g)L!p8wDJSQbf?oFW zK7jqt0rh{k>kMDZuOM%i=D@sL8VI@y-KRT&NUt)%y!ulI=GE7Zkb5azFZguVE_iJY z4rY)mAR+Nu4I~WWzm@?{K6`Z5UhwIB2Cisd>#{NUcK!vq$)oiW_!vRY&U-$fv-nzX zd+@v7f-+0=96Rqg9(=;Wzm2uu02JEK|BIf}g)U|TZ;fjH%~;B72s-rZ#Oq*J!%MF- zJ8v~U0$l?aYf={>m2A^*K6F!}8FMK-vKKOLIzwqhi_vv)|;nT_I z(R`4}qxqPENAvLl56e@f7rz~5ERpuezG&guT_Psn*&QOL0ouvq(P_fr(HX+Q;nSHr z0n|M8==7c8(^{__#gjYi-OK$>lIZ|0R<80+VmIf@t^_r*9^$%)m9rM{r|tHi8dR<3!@6q>{IQ8 z7jE243@=nFe*6dJRDMwN>%XWFNQFSfkN+>YD?r6Jw*24t2DFaTqw^y8-nie8%}Lup z*$=W}+neD;Qpava(Be=^2k+(K+^@Mmc!M(WUbn<<^v6(f7J z!Pgd)B>8mj1+~>*D>eUMDis4A?)Crwf0*A&VqbQHcSeBMp|~180iE*u58N;U_vBnV zKe!s6{4aV9hZv>LJlw#3xB=of9sxxyQgQ<=%xXsH{;UDj{Yrz40jzBy zNL%Oq7c-;5Bi5h;ZXkC3|1WwJtor+Z(LEZV#^e1LB@jhmjTuml3DG~$t`cs3QvqtZ zH~(@dmVqQLP?74<$?Va5qyoupo}J$t?dn~9+)8gcHq^5)m0oh>-{#Kx?}QCw`EkdF zdNxMzo-R**m-D6D8!B0uN;ZPdQ0y+$aNG+jPr=8knKCjkxbSZewBg_GXypq!2;eaT z14CN#4~NobpUzJnnx8x^zwtMffR1+Ryx?*00h@>AA^xTm&~D0J8FT*ao_3Aky)30H zj@{lGtta_gK;5Lylb)8R_}gqjJ*tBb*<1~uI5yXCvv5H7iHA;rF*-`*eHd?gbO%a! zbXO{PbcS|#FkS~ukMcKxM(=z({}}%F?Y!yOT*<=3-_j16KKg&$v-zNdNArOLr7wIS zZu0HCVff#%xq^k8zl9$((+pB`0ICLbuOBEk`gEqw@ac>_;L{m;;&ma&i~Ox~z#Sow zH$nTZn}0bt@J~AKz;T?vwFz|iPrbs09DEK$eC_{0)cxdNdz+(eUeE(cQG>N;Za)YmWQ^t{GTkQ_JSduXYlAd?5bFB zT?@TJENz0XeM=N)s0PT=L4Zf+36JlOKtp04 zy)2+z*$f`NEDj!^dx5z@SI|Nhit@KS2ZfZS>k0n0FQ7^EgAdqzx?K-AHrFt)F!O_t zP4i%OJ>b*rAmP*Pq2SZ$y1|$6piigo4*sUYphbP1wHIE3_ICMpXGnO0RebQ#e1YPy z*&u_7aaa-9K!l$iyB$Ew!ueb7KppAP?R$W~%^PIi!3S*3HH;uP+JLx>Cp@}6Bz(FZ z6hJdZo{T4aI$d}0Hz|NktiAAZ6UYSEF&W2QVYvmIOMDo?wS`wNi=9XF0S%vS-xHqT z0_D&jpM|NB}#EINHx2*&@s~ddX*2^eR zwBc}>FXLaBt30}WPk0=B$Y%KMHLnNgtb5Ck{4H{S{{L^TF}NdC`o^W3#lXk%c-!k|aP2(h*!+l@zf}N_`cQNDmDxa-86wX|<2c`~T7scO9JJaAt?wJFh^Ma!T7T=& z?QZ~HA)U?$xx5uISk_$*YTL4TFn;s^E$;^{yXbaT0CnhIiy_9Z!Et(yzeS3XfuXsM zg}wBxM|YfrPj?!qJUQW^d4qq_As@>J;LKin(WBeVz;QRYRBisjTEyeH8&qC-H2>fz z-Rj%=q-28!bL<7*-ePXw&ikIe2&eZLem^MfANFwA8fARVCHZ21GNnp!3R7dwN4BV9EOJeAz`pnV~rN*Yr9^wu8O1vD5Dpj;h$!NWTC0BFCY zsfZA0Si1JWi_hji|3j{~bUguIz8HG}(!TTQO+5hFKjLfouEg7!f18Yc=M9*H8h)d= z2Xx|s>KjIehR6K9&IY~S;SIl090Fdj{QL#e8%BoL8$7yQLFX!7fGnV8Ewcb$)7R~K zBJH?~0ZVE6aTWv6x>=9rV{9Isr58Lp3kCQETo3RIGI}&0(LhQhAn$>fJs<`13%LSN zAp7nBZMb;7(xcbb^&cz4iyh`aVHZk1dtn~<<3DJP&S8({+5@1W-R{~G|3yO~>#<(& z1|VN{%P)^Q{^rs6<_8OCt;lI`;`{2^`NyZblEb4r5VRE)bPo#XWctp~9WPivfzAmB zi7>*NKWjlZVSz5__y<}exfOJqlSlVd@bxIi!RK#5T70c9pm%4%uG9i;TK6~(-irt> z3yy;)cEO5!!JRaZ&e9nkouFeDVi&x4^ZEaO#EGIlkmFuJjf?L$3LQK9+0)u zplx|z-+|}9Jvv)=KzQI;?oP-ywr<#z323y|qZ`~y^ymgpka%=Mmy7hy0PE`oPs>8L z$U)Y}dvyA);1>YjVhL)x{snJr1~&si8<#;FXh9qPK)PEG@J~4a?i93M;-7Mm;~?ag zTp!T2Vco7PJi2{5e0o(FeY&@<0L|(ie8%R}y%yxA*HS3;N%K($&*ozX_*?!kFfcfF z$1->@gOA`TdJ4LrT=Ra>T@UM4kbBFocreFycyzNm%Ca$lZiBn$)A_!;Ulom)ZIO2Ib8bhb_atr_la z?EpniXDjHEDUWVQ0_kjB0FeW4r0r}4T{#81FcPA214IrKOP~}5=79!^S`UDk;IIcB z&uH6E43P5*NY4n1U2)`iY)-J(b&{bZb#@cF$0pq07`aO&*^4X*g~f=@>E=?3Mn*aa`% zDuA}SfpZ!k?a@6KR9g6SgHMqHmDevkdbffcwTl5XdJnYd4y2J!jK!^%Su)&Uq>MlKj$c8?hk32f>z5ERd)6Ts& zz)1yC-gk%Ycu@l0>oXUeW-MQq21Co^&V$VlLASc{x2^^0>y7yD(+wJ*M;@C8-=+;6 zlK=l-G)fHIi2pAdCs%!&EHYL%)n6M&cBUyl_W}Ndvwg0eMu{bW0+@zxDm!W=Kk#_+NNwB!vuss%dS5tMMwY7`VwpfVnj7D3?#&JC}9J(|IZ5ZpBbo&PQZYE$1vQ{D|e;Rw7vWXFHe zAHtwE=U$Mp9^G3(naZQ{)(a)@k^Ep$U(5HPk^a{&mw`?*@7~G*O6=WJLGJSC-3v}E zouxZoYKnuiW^yz%f-wNvd`*bgbR4jABHA=6k^*2@qk6v(z@IpQc)b9m1oqReK zJUSydKqo1|mSsU&PoT?`I&Zz$0@+#)3IdFoUhY6ODP;B6r`hL`I= z++J|y;?eDq06G^BRQU)v1b~)PLYNMq`yu~<8$6&>hdc!M1p*ZK1tJXiH6fLgCb+5s zot*;;ICw;Metz8n^#f9p17Cvn=*8O<^hEa(v=9t3H^RXUYVv&t6?Ly&fBb(f1`+ITKJ;gCz@VkM6yoO!8lpQHYJtjdb^bCwV7w^%Bf93&08stml|9&~`kf6*q8O}*giFm8`d(7{_^ z{ow8Gpi=>$*VMZUf_(V?#eZki-T?HzcJL5D2x!e4=nNLnz&EEW{Ex5P-tdx7uPw`4R?rXzeEhayk0@9fe+zht z%>P3kmKRE2L1r;P8#h`(D_&oMPn78GO$X(ZX3+APpC#&`H7gFF^)pcYpexzI>sBs; z*3X0?*3X1}^uWJ<24nze{S4?#eB##6K-}lkYis(3l>xMvCK9@s24s}sC8YKT+Ikv8 z@Oqk0r81Da(7?*S{TKZvz{cR&c^owUB=Xu0Zu<4tte|~nB|1Kx)f`Cep-$$PlRyK3 zo!>!An>tf}{P*lU;@No}r2h&iDOrH;F#iEsh|_wYqSCYTScx_NHr9ObdZ_RJMc0At zI|8aNG{G~kAai(~fBgS1n#_+pXXMf8%<;Mv>Y&c&usXs|0F+?<{}<)u2QiF&I+Hnk zI*SE78jpaI2&f+w=NKP**rV5WGdml@3pLH3(EE{4$2S_^fG)B3=&U`#zb($9;Wu-s zBp#jYmLIAd{d=gFHLG`Sh;!1P!!oa}(%rz0SYwK&R^wk8U;(&=~19H%l94 z{waqn4)aex=+j-t!Ey1Mn-wE}3#c$^Ia#{gr*|oM!c8s>xxeFh%*~Q<1EWvpf6vbU z7Kh6lY?%04CxgdJzQ1^Xi;=<8@_5-TACT9-y?B3pl@(cKamWPKb zK;{K~C-7^AF7WJj)%WOhUGLNR-$NTb$;S__tvoyHI5sdf{NyT`0-DwC;NsVCZQ$4N zoxraVx`1EMiIHE>OORhMNYSI)b-hQYlfldH|Nj4PImr+1BKvm!c`cYW0qS0h!~E@+ z{{8=N!&p+wzwH2Mn@B5YQ6OkrkR{mFN6M~v^nz^%#jPnxF8A$x?gMjqA?Vz5u*-cr z|9~9d^3o0z2hi~_kb4*m4|Mw;Y5vI!ne1x*$;#gn{r~^}=AUf*t)R^-paz1DXY(IM z{wW7RmrU?)W3$oYpL&SnREd<09_VOlh+NCT5|NjnT{o_VPhN91)G_Mww}5tk7#?`Z z4YG6Ffz|`1irub9JbHP}vzQq?l35x-S3q@_?)d+}6Lc|Y^8uFEpn+Qw9vy0WHPg7 zx0?ZY7`xLEv@5_((2-xjPmy2HQ;=WKk&%C!>-7#skIq63pUz5ymnMJz{|D=OsSL8Z z`8{KiE5bqSFGc?T|L@!R$D>y?=ou>msOsSg1r1Vz)=N1+MSg^Urr|{;!QB6b-(Eb2 z2z+=7(g6~<76M*^gIr#N)-QQ9z5$JScy!ByMy2_;xeGM^U@K7skMVZ)`h$X0z+I4E zz+aJH6TD_0HhsO#-^zu5TfGJUws1?2&e#snroj%^8T=ZM*=-GoVnNrL{DQtKJ-WAo zi~^l^-r;-5rK9#hN9aMvhW|{E77M6_*;2&Fz+l5%?C8~NVr=*yrF^v3FX8g|f85jZ zU+JgUH6E@1OUpg}9|rYwu7T>`&d(m5r5&%+q3az%nvwHwgN?O*i4;sf=v?~mAR~lc zOGC!Kjn~X%YO&{smB~Ru9dJDUV6!FbKn@>^LV$SBwAAQe*& zfvSIx-f~8d*8d*-F5f{RSQO#YyB9Q?U9xJ^J_R|f}+;rm+P(nZ|O#50+H#}d zBO_>x4K$zdniJGdYz_SX|37$z6UfEjp?=tg;9k*#si4w|r^|!!;{S)B!s8_ii zm!2d4)B}zjA4)_Tet`;9hlX#ApcKjXvi0x({|)x~{H-;A|NjT~zre1l{QLiZ=P{37 zTM@8>OtyP4{`>#Tqq}s4NAoe3mocEys`EH#-6uHK(kA#YJ^+XJ7H~O*65bCl!Q;5p z2|276g2p>KODA~r@;*&rX7Ea8+3CS};{O9sA^dtB=!lrtD|Udq`+6Zb_+QU5Jn(v& zZ|A$0exOkakYl=Qclh+WgR+7TzsqqS#$z7M$CzI31^3cRSAcQ~hBU{^d4K-@-v=`H zQR>+Z8MfM&!Q zKy~IvzaP;39uIw8jB+>?XumA_{PyZgGA{8SZZ3!oSVm(xcmse_NP9 z!!NcHMW60C1E0>h^5Ej%O%PmpX@W=N_%*?OM;HEWaaR1>%Pf34LuY`x4IQp4FpB<_ z{DQtaJ-S;VMZfC_mk!qpE*-uXJA5yBbnXR>Wcxsl7wnh{+C{*x0XCXn17eY2YbMBg z!QM)6f^P*m#-r1j29=r{{P+Qe{VN<7Z~U|32=84+}r0D0FPnt z3rqzKGlK3+0Lf{B5847P=>l~>pyx<+Ivel{fKS}x7XY6r%r7t%G@Q#X7_7iA=qjxHPlO>)b{-g!bh!9pb+QJvVJh_m-QtC`%B@E>gj z-FMJJuo@M}>a6eoMb(+Xo!I}Pa?GFuJYEa$VEDkmpbVatuw`gq0NpO)(Rc)8JIICL zrax%5pc%=G$4pQ&ZZUzA^@~ZSKmMmpfcFOw?(a4A)dsJ~0~L|GK!+#1C_707UnIg@ z5W$4S7Y;=ELKbF*2-Fv6Opv?GNdAGgzd%!^p!2Xr8tnBQL9-yOCrboC)y0$puen?e z4}ezMe=s}%x&#E2x;lF${(*-U|NZ~Z*3s$(8k#@e3Occvfyt+L>BWEl|8Hd5#N=vt zvf(FNiE`Qmk8aof9-ZL)!!O{vfM39O13yY;_UNpg;nBGlG)Car`OTvfJRa5Id%XiZ ztmDxQ*6P{mq9Tu)MIHGC!Fyi?SyWs*kGgcU{`&X-zia1tmk#h|z|PAZQ-6Y7+`AV% z!_f;qrSL`WVMfqs6zDWnkKU~UpvjRakPv+40~DwH0^nH@egW{j7r($%&|C+RT0SG%=zF)%RL zaPhaaf%?n6dqE53JbJf+8cr_~kAX5Ke+zgybT>GJpu=_i+rWW?v*pBt{Bu^ z<0{pI#F%S8_&ziZP;~jimXvn8p6GPFz`xD)Oo!_g$A-U5rJ!NfVvlaue$ai#pb}F9 ze55_UhHnGEV6P%bM6eaqR_J!U(AjDY;%{@k($Q)U3a)O~8PEfBeLBID3*f#9`0ziM z4sheKW3DA=_kG7&E0B6f@VwXwno?~%0!q1{+zjo0L)+i*{0dFq;04yLm-st6K~rX( zm%(YQb_MkQ3GjgRPgtI@0BsQi)n)Z;pw0hBz>@;sz;)f<|Evt4Ys`H*Q!n^*f>tWT zo&b+7PdVV(dH=Nc|NjjSboy>VuKz&yXx2D3 z*s$n(^g1w>e(>l8&v1dxy94K5*4aB58K9}eqnGvXbx=Hj`x408v>_Vs%}vgXzVKwc`dS`z)K=#)L(nO9K2X!-UT$RoAA<+l zLC|>-VGn=H98gKqdeVd6<%1{VS6|Jq{8JA2TD}L*$(JhmTD~vicW$WB`_E7*>fB(X z$56=!X`}dbzIw548zX~n=RaS|_eJbJow+AKt?JGnpaah=Z}7Jng7(2$e&=tw17d*o zMz~&ZI>ut42RN3(gXE^lcTyclbSwbxpb&IS;1_Uh;1_fZ;1__DrGkzQ`~u)4&o2NO*btZsp5PV$-(tis z7-_&S2(Mtkc9!)7ZX&llt1}J(oeL!;<5XIeK#g4~WWb{(X z3p|<+vv@#Sdkp+j4!*ntTA$gQ&FItl!H4k$J=Gm(E~4wL8FlRupyrUg$#=`E=)kcF%y; zvt4-k4Rpp7Xt+%CGAqLiBb^`rUn_X@+FE~PWq7ep2)Yu=qgPbxBdBP*_d-AiG=&2m z^#+BexXzFNhW}sR0}B}5eti{m3m?eekoox6%OI;kL2<}XV(8HgF1B7Pc_7Y2s{F~y z@LJrX^YCjSkIutL+3Ul1R?r#HJ0PpB4*VCr4-)9S|6;lp_VuG4jc+nI7#MsyTR~Y3 zdZaLT>D70S&b=?dAtQR4mxW;`FSv{S-KTRaXw&bDZ+4J5V~^GY;BfQkJO&Or*ijt( zE}uL)w}R$3KueE3_+3tTbow6f=oRe-8O9AW?B9#E5Q8>&fG7UI36URk@g_K5cy#-2 zfa=}g(JLAS(hF*yL&~ogg%G_fFdgAL!vo?1a02i4T>&*^g-5TbA;=UY7idFFSpaqc zNF&I4-x(g=zM#v6K<-}P(JRV^rt7OMH~=~@?FNt5L#+qR(}5O3L9Orb=oLM|!@{tW z9}xg+zy^VvqF@(5wSyc1VM2~6g_r<2m!ennILHt#gdyn=LqOwLm=5v<-3kPX4p1K% zHbMlB5y&|}y`oJZ<3I;RKm%3+Y+UFCu!BI_L9X$I>?;Fn^u6HGE1C<^#e;CfBO6eV zfRC!dbOiX64``@>mhZq0;szVn3tqs}E4rJTg<&U>wd_4~0pt`26SS}! ztljs7N3UonNH0>jhJf`>1!W_T)&tO0m;A0TKZ18Bk?ZfZ#7?Sk)U2cK`43vENT`qw3ZwaAQ zw}LZ5XKM#|_T3dU!rlR4 zE&weK1T(=~*gQI0Pk?#g#ap0TdOf-!$1HTV-T+HL=D#~zL3j3ouF3#~Lucy?h#WZa zbhd(S@C7X^0yWsc5}>X0t)QEHK`X2vIyfM{0>@fss{n)tYVCqns(^W*SZY-Ot)uT; z3%WkVqZ?e`fl7UleCJ#Xu-sAyFt67GOic{{QO8>geu0+Zfp+FqfLK1zlhQ%+Zjg}t zeq#e;C%CcA-|z*r-ltbojT5Pm>IO%SPv^fEepa9~8F~Oqf`pVc;BpFDN*sV>GH^Nd zn1h93CsHZF3NeVNa%vMuFH$*m(h}?leB~6hpg=CCIzgr&xu6+h3W0Je4o#OYL>GZ_ z3Q{uoc0dCFR8A>?3_?ovEMS93ET`l_h9G6={T85*fE;p!QFDL_8gO(#%Lj0Rgk)1N zvsd&MI}5{3q;jeNY#gC->L^GTQaNP>);u5m!zDaL|8l7tE<&;=zP$6FG&);Br!hi{E%1Gh-QZ#ibh#0HZCC3Ghz@A6 z1-b{zqZ>S=2wL_EmID`Cpj(oBI@f}(20|2DpmP;HI_E-ejaUjg)7_)97j)N)N9R=V zT`$L51^&Q_Ezn(Y4E)8Ey~pygveoom61#*TxRA%hh8w!ZZNU(TJog1_YyXr=dlkTHh0!5i&C4S3KL zya{N05j>Cv5&^Bg^Xts9_~F-SVgWwM5i$(l4K^IKJQcJ%ya;r-DJb@O!Rw)YIv>5L z$OTOlIsX3tpSi@{M;ol%r+X_X9s72^@nK#HTFU0rz19NMUFih#Jgnz}20cr=J$mys$N9V0a0t+I+gf2OPcxU)a+8o5`oUbOV3$3_5gZ5K zn*&t;d34VOnd<{N@Za=KtXHI<`xIP8j-s{>ATfMh2hG@2{VEG=ner zhV^GRzXo+4z{64u;8k9rq09fGb0ET?Fo6hzQow)FR*-P_Tu=f5UCskO>l4Iz@kSr) zN>G2or`y7#yA|AvfOqaaIypQqJ_jus5`^7d)D249rmtBU8o;YZ7)k`dTf{Fu0G*`K znji!=57g@bUHkyc46Gpg8;^i`FGypC;0{CQDUaqu0-ytAe0rCH3M`-Atqq_n+q)-% z_78b8nOg5tracP}U&UTZ=b zknr{B1f4ei;;SjxnILuG$Z1Iitr6{B3o1=PZSLQo!{0(w3_LogE&$zR$B%Z}BfsX< z3E<1(dO=PF?f3`tS`B`JE;1Bg11<$CD6ue3wv>vA=RFDB& z#z4!o6CTYcSU`vH@VA76*55+EF!O*?6TE%WH@1+5O{ z7i0wOEC5S88t`jCEY<*9%rEGvz%S@106OXhe81{ckclrIS%L1V1%&_;e+xJ@cGgbV z1s;;<-COV-bcGV=mcsv{jjuqXKvPeE*JL^gcyw=_0gj%%ppXF_;tIBPFUV~^o#6GW z9-XZqM|m6vFQo;!9efY>Q$Yp>pI-O>KD~a7h6g~yleTYPfS3EF!N*Ho`L_lBZ~kdt z%H;@JX3@M#kb%LKf7>P3&dZL?kLx`zKlb6g;o5SkQn2|TyXXIdp3MjT7oB=J6J&HJ zc>jV=XDi4lptWliA3XkZmGXFW?*%#jMS%@ymgvIw|Nr&*Tb_Yj-Mm-e$N&G}{`X#q zAOHV@?r3Y?s{t1?fQvc6#XR6*5g;-CmN_7ky7wkP#9#J;xV;|#J*>SHin%eEcvz`C^2v~Jn3QCa{U1d z1A`;~)Ps(@{(}Y+`Q1->{6FN`eBg&8Xb;|1NB(UeTfddEg2om;c{IOd-iqphYCCxk2ln3F_v;eBLmb_co8!Tba*8s-GTPMY+?mnQN05kt6K#i-G~m* zjWJ6+8Tbu-!kP zd18;wUeE+L|Mr8;KZN-E4>B+?H2)CfZvpMJ1c|hQ7M4Kl0V#jM@(N)AsHM=z1k%8c z>I?oBf3PqQp{86IENT}<1}$B&+2_Id{r__hOYrm&f6Gr^kb8O|amK><(v%tG9%23#(5c|fKSKFi zXM^S!I`4z-)do!iI==*K0F_6eR0L`vH2)OiZ;1fy6aFc|-`WaZSv$O$8uvc<`~N=!2Ll6t>qL;o=AZ2RZ7Cof-Fv}1ptkw2v-58c zVQ1mrR>ID9@Rse6hM-JUrJYapGc*YjGr@_)Fp}PYl)N9J%ge`PugCx5Vp&J77A|fomKL^_e z4$I~rYW%Gdpv_*}dqLKxb@t8zO*gWFoX8BmadRVMXD=v&K=+BgWc>U8Kd8qJ>DWP% zL}x2#FW}2(fByf69M22NO)szifv5y!G+3;4wl@6z|NrGFs1&5H3Nr*WllyWZXw401 zIcVEuZjeKJK@rl;!iXA*9?Y%axQ1+A1Gxv}#+OA9&5ZnQ(;%9`No+d{c4g4YC{* z7!U`(d=E*SjQnlF2-W;ZPWZz`i4*pLHuyjy7vcm^pn;qK3QDLGa(;v20W?|$iU(K2 z1K5f^Xkqt~iIIWf8+e;3BY(>~1_lO0Xt~Wl`M|*kEFRsV8$7yOm_QSzpnDBL2}AP( zf0G#(14HL|!;_8wL1jGur-GXE9^F&HB~Is5P{9YfEfb`v6Wkq5>)Z?KhjxRjmbA`R zFOXF4UQp=<8izl82$WPHduBn+r&fN@gmQN;Xvb#5PjCL#c1}>Q{=~r-9L*0HVP0R( z!oc7Hxl+}qyBE|2@@+j?lI772?zMO}|6~MRFLmj)mQN@64&rkj7vK5xiir7iUiUco zoW;jd$HIr-{gQ{~N1x719?d@)%WwO1UIfooS?X|fq zPkHnPe(^ys`;^7?hETS4#I+Zs{pD@&U0auYEl=^cfX>YIJou2s zlkuSE!AC5fjE7)b`9asAgO)>DYVo&Pf!3aOf|p!*b_aphJ+JW4yujb&$pH#p-x(f^ z7d<+AA%PC9kKjf0g?M;BjlV?#G!fAa&ey)6l5j70QW4w(a^c@L*WowlUZh@^-~azR z_p(TXq8c>4eu9772cK@R^&XvDL0x;F&e#(^&{-3yZgH1xe-=;^$D{c;hX>~cAI=l} zt&2c&fR-Qlo033B&o}?);BTMB&cNW(`NN}^_rDFOfCR7e{{O(I^T+F2uyZQe85lhO zpWxrd?(zTl>rBt)AI$u1DIme-1An^NJ(`cRybksR-Tm0+2NC_pza1pZ^xDR=`3DDo zlQ~5A2mdyIcK+?*Ed1Nb*}xh(z?B7Z0n!cj3An;=?DhT+s%zSRfRbmgXbaeR(1o3z zpoH6Wj}5fvPu#Qlz(3I1J4TP@<4i9>jULbDA1wS$`$5v%LDGL*__zBrft>pCBWUL( z*jWqMK*QMwJ(?f>0GD?y{b2R}Ed1NT*$zGu;5_BhE$`Cp&jE6`$H7M$o(CUucyeC$ z}|!?XFo2mWpT{QTR)dHA=L^LaEM7XbUlr}LIickBs}r#ufnDkRGDytJPX_X2tH-TrxV-`^XcpbWq44g%k}Br3rgQU z*5FQSsW5Ctx!2TvBP+v;rWv3VI29BSux!|&+oKy?*Fo2qIWt2!%I=nGVlXPJ-ro-{H*~X#m#%6y%i@A4_v{61YWe2 zGJuv*fwO7vR#0aReB-C@ffqeh;1Lc`+Y{8(=NAB%nEV30paotYz2L49zo3TzzaRr> zpD?JA#NYA+)O_d$ch*3i_?9bRKDcA)0Xgs;dKkP%=P8eak61lAr-JHYkAn|bT|qnh zT~E9Qg#~B?!h;!H(eOjoAvy93vao~hH1q8AVHfZ`?!pd^EDzAdivJISu2}y6|G#JR zaRt!QUmwdK{4GIXtHE6pCw>7J3s5H-R7`-*0r9W~FR3b>f=D^4>sc9IcuZqpcv%DL z6F^TqdYz4yZn{G+fYVMlWEdHA@*)vQ2r_`+fvAJ*_kfZpI7C6qN%&i8K+S$oA=3H? zRBJ+4v2{ZN+ww&5VGmeO19ZeCIC(;oKYwc^$ieW`(6f$};YIya28Nf8kj_7-e24l1 zbb1H)-1mp99^K$ubU~i+>8!z)5x}Q=fmd@OB`9c;2KVkEtyxf-`7gTc7Aph40Cu|fM+Gz*02+`5W&h3=l^viFtj@Iu{($af2Q@y@I$JfsP0LnL zn=-AlRSOgh$6G%j##TYMn1e@pt-+&e;E^ZL)yXfMVj01Qt9oYs1&wuiFoXAW?*g+y zyCAu4u`+;9n~?{>|z zGlXBlqxql$=+K8}KHXbEK2c`y3F8xPo(;DI;9 z20hS<`IeG5pdpsh1)y;?kV&0jllV2j=JIQTZ9&{=&fn4s${EMOi4WA~^EeK!o}ojN zpxg}-g$+%D50E(C3L19;2_wc_K@u;%D}mE7w12?N-wK)$g{Jctzv4jgvJ^Zb)C~@M za4bBBjGuWn|7GNFIR=_W?%fWGE6|z3;lYqG%11tsAsCPDR?vV8_*Nm+*W#efBAt-# zFp>m(Smve{d{_n=HT*5NU;Y0N+F1=6VF8bBf<|Zli|)Gy9`u9^(15NV0u9jo7hMh! zhK$jGgh698|3xQ2grUPU9Uvni!!-XzORupqyetEWz(-ZV=kdmZwbU+PfV<=@Xd`sz z|No*+AT9erT8@K9e!<~-9K2E$6bZ0_@f9A>L(W_cpZIhx1uZG}=!6VNcK!q(kq7E- zw|xg?rS8%h;N4H4Ei>T8^1)}Uo(G???gAMI-YN6(#p+7XSj|CDx!ih)k%0j`4iDOX zP{;Y>|I21B1_saOV*;>qw?Vdp+UE>Dy=y_y>jT;wr3ERhY(Z9bzW3PKf|~0{D_bGA+wf~b3JFbcA;GT+-iz+p?E@Mi?FB7F2MxE)@PN!^ zfI@~}Gj;>NAlNWa#}HhAxTqNLYe3A=fS98JHph`)5PX3Sd_ghj_(JdiDX zaGvq#bX@^DF2FPMALv#^59ZnxyFd(|&UgPsCtn2>JI8#$mym-OE&FtrcDy_c8m)j{ zJ`LI`1@dEqhvh;3mJ6VE0qAm`&R)=XGw3)P=(#^W-Khs&g2p1jyKR}Surk1+>3|31 z-fy4owUB(a6;z7&Sbiw;18tSJ^2q!E&Rd!1Kom4Nya!!W@nUv4{3r({a9Z%_F70?x z$o}L1%Q8@t9CQxd^vkRaj^L9WK_l!wmJiFqeL5e2c3IkZWPSvB)g$u+hyt4o+U5m1 zP4I<7IVh>Qf+oI8)nI0Ccp(ZhTMFDis9o`2)ao)T10u0^gO?(KrVd(f^S2a$n<;Y* zK(zp52GRn;1TBMb096H@Ydyf!)&PhEXwESLEV48KBmxgxPs`){tva9v|5niHw_wTc zUhwQhXX6jhA>RV6AbUV-^1umqD#%2B!HF+Ga)O;Vz*Hk>l`?4I257`wfPr5C>6ie4 z1P6YBi~xRtf&`ClhXU{tKtV>xI&si)KG3qhL&f{A zXRc&mU~uW!2%h8UZsiB{b~_s%fRt#oGJtwdf{ma%OhCuz`~;gD0H&rUfT_6!AWEBx_vqqc0e@G z@aSv=T>|LS*$HY&_;k)(0FvmOxdKGN)|NDaQlQ82Mo_B))K>L4-UzyD9h|cuQ%n3U zC!T`Nh3Ey%uXe+(X=r9jY;uwGI9=P+|XGe+NDYzFHDcPczO zAqT%&=1sw4fKFs1K|NbQQuO(ApwUp!2&N0!@yBZ22=0-4hSud@S>sX&%ifSKK(r5~WlZ_vdxq8Vt~IU(A?v+!W; zU=~C>h`9?C*kJd7a=i!GfsjPdD;f_n3$zvn>Xf}=pzwmsw1Le6jpIX>oj_KjftUP& zodR0&rw7swx1{6e~RbgwsF*EE$vfd7&AG)&b z0>on93m)Jlf?!)gO9Wd$x`Yw2zyZ-U!=n>aZG!7uewTxw`EJNUdry9sGoT|yKuM`r zG!jksDN)cI`&EzD13vsNFMRl2A9(bNepO{=*a^Bh3+l0@qM&d&?9;gx)L-@KTnb8H zAYbykT=3`wFAsrSG~mhaau3$}>IUyC0bNk@Vrn=egA4yQa2@T~@K=ljw|$c|#y0yIaA-XPvF|8mwCYEAO*mKY5bb4;E73rR?vbDe$7_! zY^6YJ0#qMpss}P%4aybl5Z8bQ?Hn8aiIqBGn+W!8{SR#wm7q=rYn%liF#{@%U(9i5 zWO(`LAt=|voctff$)KJoXgM{+Ar7EY4DMub2?uvFxS)eO8C>2$TMwGx0@9Er`Zwv-^H@izO z_;mMzl8R6FQqX|1PdE6)s+ZEBeq{Gv&|JMo^KVi9X7CoogO6D}7=L^8h70U~B)xyH zp*rEdx6fQ=#HHM8f={w2;nsY`}h4nKQ~BMfFC2n%enVJ-J1U%5R;$@=7nki zBg4y{PyheFECF4s2s$F|wcKkNk51PO(A@Dt2$XW7K=-jumw+5e>AK;y08|C274_mm z19;Q})DnF0MV67_r4Fb%>2%%j@(j4$0X7TN276%^1nQ8y2c>OkH%10m!*8!8UN}gA z&Xor%0(U?Y0~r|{w=sfFT4}DG!B8stTK0tuRE^^{MxV~w1CCo5LFZ@v7j-?w$^c(D z0cu#k*v!Yk;M(%7gvSFiOz`4RAR~io%M<<<8PIUf?bih$r*CF_odKGk1=|YNciWSZ z;kDa~&Eg2heEq1f&JAP zz{t>Yo4>UlbO{(}f7=T;B+Ec^ZRw!c&;tdv4pinP=r~`Hk+9fE;bUNU3EC9*f)lC) z+W-51wnW6UcOIx+3OYU{E3fabDDdC+@&)W{++L5AdQH zu%Ez96|kRf9Y-v!QHHg6P}Cy%|2U|~fscWJO#&~y0h=@dEL*$bh5N7X|6e$Y{`mjG zMihBFDr|o+^nR$$-yYq(r@_(o^FMUoBj^Y~Jaq+n(QPLjW zqIDocGf#q4gBJRL2J>KDp%WhHH${0$L2rsuh1?Xy>TnXYy3N!OWC+rQQJuG5r2P8+ z-=iCPSybn(7a_mC|3{4FfX=H1??(&^2HgVDcpTIjc=5}DkpXo0B4}w+HOGI^tYe@s z+zmOOaWm*>#{Z(nKq1$7SO8qC+y~uI_0o9B|No$40h+DB%SAvs4Y26!LDBi*?_yAC z`2EGvAK(9j&*uQUCmecz6zKe1Z~_l)@azO_;ed>Ef)9Q0=w|fj1RtI00WRcSJNUQt zfgFjtKds@X4}a?h&=tqu+yfZ-TX;b06FRqomcaRTZwEEhK^;my&+c|eOC8*R_3UI5II3tBq@n!N$J6>@7UsFCi`Tf4!d z7kt#_i=_&nlaE|C*m#%vf(8shdRq^a#HKkm*L$%#?kQtnVAyG(2s--O>7fT?RNbe0 zEvPx__|27pvBb)=J66KCJ6GVf6Uc+`^+5dFVmEgCZgSz@p1RHR;1dN;&NHChJukN~ zGB9j#dbt)fHPju;;nDn?jlUUmu!?8%Q2|g6_vrN90lwb2Gxmf>FL+e~XkGwxa$gJR zC~UC0|EE2gfB)xixd1Yz*YyLx0BA7>s4ouQ^wjMj0b0$`TRXv{w{!#aY}Xf@TS22| zt`j_(f9UbIfL8VKZ>yc*!oSURrc1Z)EdK4K^FVDpP$L!Q0H|eYj+SRidp!<5Q}E$D z=g?8R!==M@XJ_dykAv?N(mG4`LGlKu?*-Z;0kQRk)(Mbxz7zPjxz6C<7CW;$bQUNy z=0QT^icfd$0*~gO`ur`RV>Li6`VQ9}9kDxII#YM~bUyGn_(CDA({&%%CCxwe_**~+ zrg?VvgSu*t{M*7kJ$m;-3ZK#rpI+AuFS@!w_JQXgK}&N$=QM#8F!HxrGBJP-XG7jU z>d3#XACxM==`bHOk>U<_s4ucZLD9A#Z36g2?$ZY!a(Hka1Uv76fCuLZpU!tE2C4o1 z|KG8r9b_=55r6O{hezijkAtr)96P47gEJ+Fr;+B^Ih_?WM9|sK2wE=LxgC^!I}g4D zEmHu+;5P>i#!_L>Ac%)HhsSXTP*<42qtn9xba@_lp6Y|ge|G*pP~HHYo#)Z*+W_AA z)V&pa1qSHKN7$T!R|MFcPA?CSZqQYGUI8ARR`4U5y*PY2iv&Enp=UIENqBTRDZtk& zJ8AfIRvGwoR#|v-7CF4U59;M_JJ|fgyHv@uyC39e(1zeNk8VrQjGRaFK@RZ2C*X61 zVm-U}g95^le_K09r6d3Le$Z((j{MuU^D{9pfadScId)Iy1ceKD`N|^>56(~Eop`Sm z9FKuR(cAMlSTiW!8XhpbZ3k$MwG(u>ZfFBI^>t5$csz6mXk{O0(5n+X zT?g9&+zGjXy?ZXWsOqiefaHv+pd|ESy)|g^3A8O4+g3|RsRW&M1BZJ*=x6|bZjWA3 z-*#4peXOAJrStv^4KC33H_%={^eYA$-+*Eew9ls#9D5$kb_|f4x%may_yzm~_yxig zpf>OehAZ$3`U~(2vhxeL8So4EIq(aF1@LQ51?6vkP4E_ee$7_U&^g91&}fg(6Rg=hJ;6_>jdx# z0s$Sa2YkD0R4hC?OBeWb`*C#p33R)OcyxzJcyxx&@ac|IX#UCE>Dt5J!UjIrD7L|) zJ50l)6CAs~tta_gK}o6kClh}wXtwg3>j6go*7J-E48E3s`CE>Gio@pPpy_nbSwx_| z%WGk1`>)si0cf{!%UuRgH~F}SWrReTm#=j>NAVL+YyPtP$6c?05`b@Kjf#b5cfEi| zcaegJ<#+yO(5b*4na3qS$&}ei0yGHg(F-1r_38ZiS{ZtN0J3(+V=gKIjK^FLFnaX5 zt}s05(a8ZmiKz8kNgnjH6pwDmS^~(jm>2WFYgD0UwKxQT#?<)*-2(Up931!s-5fy0 zJMsviV4MNJV3-2Gpq~K0AR8nxtpm+xulxJ|Ke9_)4Zpc``(AN@EXs4~_U&=$>^%dz zhZCHNI$N)RmJfG=7j=7d&IKhfpHA>0G0?r5KAqsnBacq-fzckFQ$aJ~9*|RXJvzY$ zgnIN&1+Dn==ynS5fF14*+Jx-nz%RhXFAyZaFA$}`FOX!w5899{m}tN+7^%Q77%0Fm z$a$<0bhZkUN8=Gt{y(da0?KT@o#()3OL-i31znB{ zy0T#dXzBrWnu2e4rU2wZ9!5~o^Xzuy0HsCn=&!5clTP0&9-S9`I>BY0PxoF>qvN#> z=!zfjUY;`0A{h^6-w9rwJP$noAMxo--2vJZ(CvG`L-QtR>dlk!E@(v!sJt!l1m7vr z4qBUZ`?WaId0`%%pFK1$g4C6%gJz#EzVhfbk@D>P;Cb)?t9P%BhQDPBt3dhfqH8{u z_GK4eUoyPy(RmZJSiq}SMAO$Yg;${5xX94Q(yCm?$Fho}OwF^~hgZV0^PGpZ?}_3{ zAM4Z={H;Eq_DXNyW;J`8FV_d-<5 z889-u=6G?919al(F;Ls8L;#`>axv(?U0@%-*!K22@_C<-_IY^9}<%IzM}M{`2Xs6Y%J+=kV>$6Yy<) z%Rl9yS1*gc$Nv-jQ$R;f@`6^TCv$-IzYD&WZm`hjpL)=z8!S-53mhw(k^!b+>%tPH-4e?bbs z-4Ail`A=xq@7&u7n%_jZdKNrT>j)k>25HOt!@DYp0PQ<0br^=H+Q-q-NYD7J| z{Wv^83lBR%?I_n1;PM1CNbb>FdjWKhG5DZ_7mhrje&7Yh642_98=jqR9KM})9?eHA zq8-5}NP$+K+OOMu66H zf?XAQ0(^}~w>zlzJpjIVw7c8@JX;G|j1OvDdUShOfQzjwpgh-I;Nj6-5#Z77;NSte z3m$x_Jc}i$-1P1I?s@Pz8)!PvqqB5{M`!JZ*P_t+&a?T5gJ<*c1N<$Z64v2TQ~a4}fC6@koR= z${`yKRqXru`yxSylC**vod-&;p@d%~Xc({A^#r>|cYuROXXpZ-?hFr~?t}o)?IvEm zEQ+B1oae!3Y_GW|cr+ge-#=0^xm(_c@paLA-_GZrn)i#I`&g$gD1Yd|9J|7!oAn&X zmz}i-KuP2tXniXKFg?10K!4IQSRFGdhEV=oo z9`xuf`tM;~x}&tO<3#I$(hiU2Lq9y44|6~R^S|ii?cjhs(0YKsH67GRs%PQgZvh>G ztzu5O2!%$1@PLUZa;8CQv$Sn9(4QG zPpC_EK+a)ylYl0R7CEp>{!0|I8yHO*0dBeB! z;|tE5fB$`q_d=vTco@4j_;CIJ&kK9-yWVKn3#w;9lXW+IL1QL2UV?Wp_OhOu&dRWh z9h?K?~IT~UU^(Dfe2T(>c#9e3Trz|bAK z+2i0N1&`)a!akiRpdDDx&I_IgUkLbi9`tN{$iVRbzhmbW$Hph1jWzt+F1>sPx^-a_ z3-}tc6U9+bO}jv|TCRo%Tn(SRJP2--gI1=3&N%exb^PMl>&OGzFU9T3zde$}k$+nx z*TKgEE}XYryFFPv4nEd^EaklE%fIF#hvOy=U(FLm%lvv}ID9(gd^+7YK+B)Yl6-p$ z|9SLAFuCw=mvHU8@7R3&Uz%g{f9B!}$L7Bu_?xDJ3jXGQ?EGz@U3Z?nk>7lJE4W?(!r;*v&f(LUF5uA_F9Gi8bowiJbh>N!bk-aAboyKPbe21~bmV)ubcB1Rxpc;R zy{-l~E?;th9SeyVkIoyfH5~c3AMQMO@FmAC&}=YhZ>%VI%_@pYpUxkyh9_P5w;%B3 zJmGopfq*aP15eHi9tR(C@NYZd!Fj^tI2&kg*5kMvs1?rO)9KFP(diCyEvQR{)U0>m z-&Voo*&F%Cg@0QCGbo;lf<2mlv-7vI-f@~?T$ z0rDS6J%7_FaQxSvVCQcG4Sj%2^=Ll)0h9#!L8kGzK+I3z^X$Ce9RVti1VCoHFsGgX zne1`!F^3Q5V;}xCuQ@zAV^6#Uo#gJ(e1PR;H)s`4bL|0k{HNAuwyFt>uj z59(HzZVk{`;2>jNIsbr6^*s2L!7L+jefbzj>1(5B`W$IW}fI6^XpL=x2 z?s%yHHtzt(%R|5ZBTWHdwEr|f8!AA@5dQV(c5?9O?gQ;A_UTpw-3S0W?7g$Jfxo2{ zRDN_%19e9ncY~UHF8tdYg%}waTssds9(>5`ar`)FuO?`I!MF8fiMFfZB@fG%pP+?z zj{H*(x$?VT2Aw$GeDH^3!!O2?+phfE4!O1-C}n*u?9+MNqxltMk*Y^`&ri@go!5&& z8%;cVSu7iDEWJx3e7X}jK6w6TQK?Vl=LRi;YCZ=V;oJwR2@Fp#l1;h%Sj zqiaVghhwKNC!|wv;Mn2H?{V;@LYiZzFRyQBs(|NlSJ1#A!^Vf{s|=7jRbK7w|UV*X#wI#sFG508UK44gA|Cf;ZFm*8Md+ z>Cyb3iGR{TegS6Cx&x4k&aDk#sugn3L$OEWKhU-z2mZ;&9XmfbHa-B=bgqUc9UFgu zw&gkUPXPy|Z?7YVXY)ZGSHnvVE!TbdT@Ly3yPWdq+`0p@+5ZG6UXQne7TJMn56~`r z=-Oh?@@6O#wB;9~jsdiwodML!=;Z))!8>~eK-uDWE9gK7eNeP^_JR(K@aSv>9T>qc z&^iOO)w}tK0CeKRqqnsH;t$XSpHJsf@IcLR@caSP0if5XV%24z&eUd7v{Tp`!fZ2lxeBK}$swA%u(}#vdL852 zdH`}qEPu;%A&_Go{`&M*G4pTpV(L8C{H*@qGj{&%|2e<6oUAke?^Y@l0`2tP#-9di z<+PWIFfe#D9|Bd;fB3hbJou0gr24#P=XKA{18J@Q`CC2;fgE&zzr_Ngk==%gzxkyQ z149RRaJjQ~h3Eg{p8rpPLK!-7cO1N>7o2X7gAbMhF^_}K(g(4SPAf$0@AN>NUxJwS z14WD`WL#XZ^$+Mw(c54@lnF2}`1Th3_w4m&bmZSA-g%+2X@HspG_5++hS}s-UgH<{SfVM|*LV`j`2(-TWIJ0NxH_zr{ ze_^3<05oOvGYz!wtRK>?=n6ibACI(<*@Z@&dv02?UCz+ia$b-Ce5-_CzNo&S70&--+qH@y8? z9@^gr)ga9e>pd?&^!R_whx2svL3Y=c1C`1i&7kW@xOhP`q}$j)iS?>u^N;$H^RBH2 zN>07x;Add)Jbn;V>v&#%=-BYX!I$6ZRP#>vNF7o3HtZn2a>0tdr!di#X}a4 zPEfBQg@Xfplqh(6$xBdoz@r;7I|OP3ww?n|Yq4ssW@XqPz`(%pUo>q6^cbC9(3-?v z$TW(8qXWO7g9E<+Bfo&B0KY(>0>40{0l#2`0l#2?0&H^<ip6-;f@aask;PChlo*{KL zyyVmEdIFRITL1s~|NmtHXphP9R?w<$P__c6Xpoq1Z{2?n&|->C*Ax8P1fiM32V%ZY zZ!fsk?A;4G5!<78E2s*pOakAG_EQj)aO?hg^ae6{bcUYb-xkQ|*m=pZ`BA;&!AGFH zn%a+0HK$HYCyDWK(uRswexGv1ux;&oaz8lso4wa zIDpPp0M+2lKbSqbV>|d;lp$t<*Mq}PLQw!|(12*rfN0l%8a5Za;81fa=sW<>q#sD7 zW-I6vmhN8A+Q?4u!2$f+dP9Ey|KHJD0Iy`c3=KAl@ZQR>sV7qm^mgq?a}Gl0aXId9Z%|9gg+eILz)iPk3CbKIr($%1FPdonAplyf51u#h zfUJ8y@e(w|2ws0=YpBS;z~Azkfq}u+NRfe|#LU(hB+|MV)T#uvKcBtWu@vT9ZvNJ8 zka#!DxuEv-OVEy9sLy)gO2zrX%iTatO;BWXwt^boFMUC}A*LoRML4_DwF4!ZL7V4Z zDk9XHA*%(a7KFK=lgeIx1>12Sd>cR}KC*MhnLFHeDln!%Hk4E(KoKs<

    sb1qS{W&>`)<-DwV<-C-UcnxBe;cYwOt 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�sM{qf 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

    &MufTO zUOV=iSZlye08iSLS%AtFi5I6J!{(q^IBa<7`^V0M9=mSC@Ay37@%@p2vFE0fSU=&T~sPy;Re2$C<7!1T9v~9nG%F}DB{IW z$k095Lms`hyM8k!q(eOhJ?5AXmKR^4QU* zIY0Tf)m#~m&ikN2rk5(9Gj)D2mX>>T7Yle;ekv~W=ynFpp-ut~YI$_~2!OWp2xxdT z9|YYA&Fs-D;$V0HdVWjmr4mPv-a-kF=0h($nkzLJJUTy?y!7aHN!=sxWl+M8?%77RGprQa=e1Njh zt_P6fL&2lhM+JNtyaq^-fk!WE`bSXcf)*!%PQUM|Y5e%SY+zV<&ce44)GEIIf9?2!F9j{mQN#w)GCW3k`=i>5P!;-FZ=r#smIWY}w7r1tGm30(TC6<`La zK@6|}-OjD$(_QW0+072>N3!~Ke*b^Lv$>vwp+v~1^E

    @?Z5c6LQY*=sXNj#R<|O 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

    Q5 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 VhS?1ghvC&wcc{TMKqb()3;-i4ldk{( diff --git a/scripts/tools/Win32/.gitattributes b/scripts/tools/Win32/.gitattributes new file mode 100644 index 0000000000..bdde0e8fc1 --- /dev/null +++ b/scripts/tools/Win32/.gitattributes @@ -0,0 +1 @@ +wav-diff.exe filter=lfs diff=lfs merge=lfs -text diff --git a/scripts/tools/Win32/thirdPartyLegalnotices/licenses_wav-diff.html b/scripts/tools/Win32/thirdPartyLegalnotices/licenses_wav-diff.html index 0b17bd169b..dd386d5ea2 100644 --- a/scripts/tools/Win32/thirdPartyLegalnotices/licenses_wav-diff.html +++ b/scripts/tools/Win32/thirdPartyLegalnotices/licenses_wav-diff.html @@ -44,8 +44,8 @@

    Overview of licenses:

    @@ -678,199 +678,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - -
  • -
  • -

    Apache License 2.0

    -

    Used by:

    - -
                                  Apache License
    -                        Version 2.0, January 2004
    -                     http://www.apache.org/licenses/
    -
    -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
    -
    -1. Definitions.
    -
    -   "License" shall mean the terms and conditions for use, reproduction,
    -   and distribution as defined by Sections 1 through 9 of this document.
    -
    -   "Licensor" shall mean the copyright owner or entity authorized by
    -   the copyright owner that is granting the License.
    -
    -   "Legal Entity" shall mean the union of the acting entity and all
    -   other entities that control, are controlled by, or are under common
    -   control with that entity. For the purposes of this definition,
    -   "control" means (i) the power, direct or indirect, to cause the
    -   direction or management of such entity, whether by contract or
    -   otherwise, or (ii) ownership of fifty percent (50%) or more of the
    -   outstanding shares, or (iii) beneficial ownership of such entity.
    -
    -   "You" (or "Your") shall mean an individual or Legal Entity
    -   exercising permissions granted by this License.
    -
    -   "Source" form shall mean the preferred form for making modifications,
    -   including but not limited to software source code, documentation
    -   source, and configuration files.
    -
    -   "Object" form shall mean any form resulting from mechanical
    -   transformation or translation of a Source form, including but
    -   not limited to compiled object code, generated documentation,
    -   and conversions to other media types.
    -
    -   "Work" shall mean the work of authorship, whether in Source or
    -   Object form, made available under the License, as indicated by a
    -   copyright notice that is included in or attached to the work
    -   (an example is provided in the Appendix below).
    -
    -   "Derivative Works" shall mean any work, whether in Source or Object
    -   form, that is based on (or derived from) the Work and for which the
    -   editorial revisions, annotations, elaborations, or other modifications
    -   represent, as a whole, an original work of authorship. For the purposes
    -   of this License, Derivative Works shall not include works that remain
    -   separable from, or merely link (or bind by name) to the interfaces of,
    -   the Work and Derivative Works thereof.
    -
    -   "Contribution" shall mean any work of authorship, including
    -   the original version of the Work and any modifications or additions
    -   to that Work or Derivative Works thereof, that is intentionally
    -   submitted to Licensor for inclusion in the Work by the copyright owner
    -   or by an individual or Legal Entity authorized to submit on behalf of
    -   the copyright owner. For the purposes of this definition, "submitted"
    -   means any form of electronic, verbal, or written communication sent
    -   to the Licensor or its representatives, including but not limited to
    -   communication on electronic mailing lists, source code control systems,
    -   and issue tracking systems that are managed by, or on behalf of, the
    -   Licensor for the purpose of discussing and improving the Work, but
    -   excluding communication that is conspicuously marked or otherwise
    -   designated in writing by the copyright owner as "Not a Contribution."
    -
    -   "Contributor" shall mean Licensor and any individual or Legal Entity
    -   on behalf of whom a Contribution has been received by Licensor and
    -   subsequently incorporated within the Work.
    -
    -2. Grant of Copyright License. Subject to the terms and conditions of
    -   this License, each Contributor hereby grants to You a perpetual,
    -   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    -   copyright license to reproduce, prepare Derivative Works of,
    -   publicly display, publicly perform, sublicense, and distribute the
    -   Work and such Derivative Works in Source or Object form.
    -
    -3. Grant of Patent License. Subject to the terms and conditions of
    -   this License, each Contributor hereby grants to You a perpetual,
    -   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    -   (except as stated in this section) patent license to make, have made,
    -   use, offer to sell, sell, import, and otherwise transfer the Work,
    -   where such license applies only to those patent claims licensable
    -   by such Contributor that are necessarily infringed by their
    -   Contribution(s) alone or by combination of their Contribution(s)
    -   with the Work to which such Contribution(s) was submitted. If You
    -   institute patent litigation against any entity (including a
    -   cross-claim or counterclaim in a lawsuit) alleging that the Work
    -   or a Contribution incorporated within the Work constitutes direct
    -   or contributory patent infringement, then any patent licenses
    -   granted to You under this License for that Work shall terminate
    -   as of the date such litigation is filed.
    -
    -4. Redistribution. You may reproduce and distribute copies of the
    -   Work or Derivative Works thereof in any medium, with or without
    -   modifications, and in Source or Object form, provided that You
    -   meet the following conditions:
    -
    -   (a) You must give any other recipients of the Work or
    -       Derivative Works a copy of this License; and
    -
    -   (b) You must cause any modified files to carry prominent notices
    -       stating that You changed the files; and
    -
    -   (c) You must retain, in the Source form of any Derivative Works
    -       that You distribute, all copyright, patent, trademark, and
    -       attribution notices from the Source form of the Work,
    -       excluding those notices that do not pertain to any part of
    -       the Derivative Works; and
    -
    -   (d) If the Work includes a "NOTICE" text file as part of its
    -       distribution, then any Derivative Works that You distribute must
    -       include a readable copy of the attribution notices contained
    -       within such NOTICE file, excluding those notices that do not
    -       pertain to any part of the Derivative Works, in at least one
    -       of the following places: within a NOTICE text file distributed
    -       as part of the Derivative Works; within the Source form or
    -       documentation, if provided along with the Derivative Works; or,
    -       within a display generated by the Derivative Works, if and
    -       wherever such third-party notices normally appear. The contents
    -       of the NOTICE file are for informational purposes only and
    -       do not modify the License. You may add Your own attribution
    -       notices within Derivative Works that You distribute, alongside
    -       or as an addendum to the NOTICE text from the Work, provided
    -       that such additional attribution notices cannot be construed
    -       as modifying the License.
    -
    -   You may add Your own copyright statement to Your modifications and
    -   may provide additional or different license terms and conditions
    -   for use, reproduction, or distribution of Your modifications, or
    -   for any such Derivative Works as a whole, provided Your use,
    -   reproduction, and distribution of the Work otherwise complies with
    -   the conditions stated in this License.
    -
    -5. Submission of Contributions. Unless You explicitly state otherwise,
    -   any Contribution intentionally submitted for inclusion in the Work
    -   by You to the Licensor shall be under the terms and conditions of
    -   this License, without any additional terms or conditions.
    -   Notwithstanding the above, nothing herein shall supersede or modify
    -   the terms of any separate license agreement you may have executed
    -   with Licensor regarding such Contributions.
    -
    -6. Trademarks. This License does not grant permission to use the trade
    -   names, trademarks, service marks, or product names of the Licensor,
    -   except as required for reasonable and customary use in describing the
    -   origin of the Work and reproducing the content of the NOTICE file.
    -
    -7. Disclaimer of Warranty. Unless required by applicable law or
    -   agreed to in writing, Licensor provides the Work (and each
    -   Contributor provides its Contributions) on an "AS IS" BASIS,
    -   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    -   implied, including, without limitation, any warranties or conditions
    -   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
    -   PARTICULAR PURPOSE. You are solely responsible for determining the
    -   appropriateness of using or redistributing the Work and assume any
    -   risks associated with Your exercise of permissions under this License.
    -
    -8. Limitation of Liability. In no event and under no legal theory,
    -   whether in tort (including negligence), contract, or otherwise,
    -   unless required by applicable law (such as deliberate and grossly
    -   negligent acts) or agreed to in writing, shall any Contributor be
    -   liable to You for damages, including any direct, indirect, special,
    -   incidental, or consequential damages of any character arising as a
    -   result of this License or out of the use or inability to use the
    -   Work (including but not limited to damages for loss of goodwill,
    -   work stoppage, computer failure or malfunction, or any and all
    -   other commercial damages or losses), even if such Contributor
    -   has been advised of the possibility of such damages.
    -
    -9. Accepting Warranty or Additional Liability. While redistributing
    -   the Work or Derivative Works thereof, You may choose to offer,
    -   and charge a fee for, acceptance of support, warranty, indemnity,
    -   or other liability obligations and/or rights consistent with this
    -   License. However, in accepting such obligations, You may act only
    -   on Your own behalf and on Your sole responsibility, not on behalf
    -   of any other Contributor, and only if You agree to indemnify,
    -   defend, and hold each Contributor harmless for any liability
    -   incurred by, or claims asserted against, such Contributor by reason
    -   of your accepting any such warranty or additional liability.
    -
    -END OF TERMS AND CONDITIONS
     
  • @@ -1725,19 +1532,6 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - -
  • -
  • -

    Apache License 2.0

    -

    Used by:

    - -
    // Licensed under the Apache License, Version 2.0
    -// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
    -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
    -// All files in the project carrying such notice may not be copied, modified, or distributed
    -// except according to those terms.
     
  • @@ -1746,6 +1540,17 @@ limitations under the License. @@ -1822,44 +1627,6 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - -
  • -
  • -

    Apache License 2.0

    -

    Used by:

    - -
    Copyright 2015 Nicholas Allegra (comex).
    -
    -Licensed under the Apache License, Version 2.0 (the "License");
    -you may not use this file except in compliance with the License.
    -You may obtain a copy of the License at
    -
    -    http://www.apache.org/licenses/LICENSE-2.0
    -
    -Unless required by applicable law or agreed to in writing, software
    -distributed under the License is distributed on an "AS IS" BASIS,
    -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    -See the License for the specific language governing permissions and
    -limitations under the License.
    -
    -
  • -
  • -

    Apache License 2.0

    -

    Used by:

    - -
    Licensed under the Apache License, Version 2.0
    -<LICENSE-APACHE or
    -http://www.apache.org/licenses/LICENSE-2.0> or the MIT
    -license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
    -at your option. All files in the project carrying such
    -notice may not be copied, modified, or distributed except
    -according to those terms.
     
  • @@ -2037,18 +1804,6 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -
  • -
  • -

    MIT License

    -

    Used by:

    - -
    This project is dual-licensed under the Unlicense and MIT licenses.
    -
    -You may use this code under the terms of either license.
     
  • diff --git a/scripts/tools/Win32/wav-diff.exe b/scripts/tools/Win32/wav-diff.exe index c06f7bb826..8ed1329aa2 100644 --- a/scripts/tools/Win32/wav-diff.exe +++ b/scripts/tools/Win32/wav-diff.exe @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fbefe8e53d825cff13c5a88cd286b5f28307e60849123fde73ea7527f54e2765 -size 872448 +oid sha256:edb7f677278f8914109247673fca07c3a79ddede41a0cede61de8a435809b911 +size 880128 -- GitLab From 86f102421f32888f667736f2cf74e49190df40a3 Mon Sep 17 00:00:00 2001 From: Ripinder Singh Date: Thu, 9 Oct 2025 09:47:16 +1100 Subject: [PATCH 094/100] Run python black formatter and some more include changes Signed-off-by: Ripinder Singh --- lib_util/ivas_rtp_file.h | 2 - lib_util/ivas_rtp_pi_data.h | 841 +++++++++++++++--------------- tests/rtp/ivasrtp.py | 984 ++++++++++++++++++++++++++---------- tests/rtp/test_rtp.py | 321 ++++++++---- 4 files changed, 1369 insertions(+), 779 deletions(-) diff --git a/lib_util/ivas_rtp_file.h b/lib_util/ivas_rtp_file.h index c0757834f0..2a58dc1c96 100644 --- a/lib_util/ivas_rtp_file.h +++ b/lib_util/ivas_rtp_file.h @@ -33,9 +33,7 @@ #ifndef IVAS_RTP_FILE_H #define IVAS_RTP_FILE_H -#include #include "common_api_types.h" - #include "ivas_rtp_api.h" #include "ivas_rtp_pi_data.h" diff --git a/lib_util/ivas_rtp_pi_data.h b/lib_util/ivas_rtp_pi_data.h index 6a84b8f2e3..3fc95f8d8d 100644 --- a/lib_util/ivas_rtp_pi_data.h +++ b/lib_util/ivas_rtp_pi_data.h @@ -35,446 +35,437 @@ #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 */ - /* 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 */ +/* 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_AUDIO_FOCUS_INDICATION, /* audio focus indication (direction in Quaternions and/or level) */ - 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_REQUEST, /* direction of interest for the listener in Quaternions and/or audio focus level */ - 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_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; + 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_AUDIO_FOCUS_INDICATION, /* audio focus indication (direction in Quaternions and/or level) */ + 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_REQUEST, /* direction of interest for the listener in Quaternions and/or audio focus level */ + 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_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_MAX_NUM_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_MAX_NUM_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_MAX_NUM_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_MAX_NUM_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_MAX_NUM_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_MAX_NUM_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_MAX_NUM_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_MAX_NUM_OBJECTS]; /* diegetic indication as per audio format */ - } IVAS_PIDATA_DIEGETIC; - - /* Audio focus direction indicates a direction of interest. - * The audio focus level indicates the amount of suppression applied to the - * directions other than the audio focus direction. - */ - typedef enum - { - IVAS_FLVL_NO_AUDIO_FOCUS = 0, /* Apply no audio focus */ - IVAS_FLVL_FOCUS_LEVEL_LEVEL_1, /* Audio focus level 1 */ - IVAS_FLVL_FOCUS_LEVEL_LEVEL_2, /* Audio focus level 2 */ - IVAS_FLVL_FOCUS_LEVEL_LEVEL_3, /* Audio focus level 3 */ - IVAS_FLVL_FOCUS_LEVEL_LEVEL_4, /* Audio focus level 4 */ - IVAS_FLVL_FOCUS_LEVEL_LEVEL_5, /* Audio focus level 5 */ - IVAS_FLVL_FOCUS_LEVEL_LEVEL_6, /* Audio focus level 6 */ - IVAS_FLVL_FOCUS_LEVEL_LEVEL_7, /* Audio focus level 7 */ - IVAS_FLVL_FOCUS_LEVEL_LEVEL_8, /* Audio focus level 8 */ - IVAS_FLVL_FOCUS_LEVEL_LEVEL_9, /* Audio focus level 9 */ - IVAS_FLVL_FOCUS_LEVEL_LEVEL_10, /* Audio focus level 10 */ - IVAS_FLVL_FOCUS_LEVEL_LEVEL_11, /* Audio focus level 11 */ - IVAS_FLVL_FOCUS_LEVEL_LEVEL_12, /* Audio focus level 12 */ - IVAS_FLVL_FOCUS_LEVEL_LEVEL_13, /* Audio focus level 13 */ - IVAS_FLVL_DEFAULT_AUDIO_FOCUS, /* Default audio focus */ - IVAS_FLVL_MAX_AUDIO_FOCUS, /* Apply max audio focus */ - } IVAS_FLVL; - - typedef struct - { - size_t size; /* sizeof(IVAS_PIDATA_AUDIO_FOCUS) */ - uint32_t piDataType; /* IVAS_PI_AUDIO_FOCUS_INDCATION or IVAS_PI_AUDIO_FOCUS_REQUEST */ - bool availDirection; /* audio focus contains direction */ - bool availLevel; /* audio focus contains level */ - IVAS_QUATERNION direction; /* direction data expressed as quarternions */ - IVAS_FLVL flvl; /* audio focus level */ - } IVAS_PIDATA_AUDIO_FOCUS; - - /* 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; +/* 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_MAX_NUM_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_MAX_NUM_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_MAX_NUM_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_MAX_NUM_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_MAX_NUM_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_MAX_NUM_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_MAX_NUM_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_MAX_NUM_OBJECTS]; /* diegetic indication as per audio format */ +} IVAS_PIDATA_DIEGETIC; + +/* Audio focus direction indicates a direction of interest. + * The audio focus level indicates the amount of suppression applied to the + * directions other than the audio focus direction. + */ +typedef enum +{ + IVAS_FLVL_NO_AUDIO_FOCUS = 0, /* Apply no audio focus */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_1, /* Audio focus level 1 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_2, /* Audio focus level 2 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_3, /* Audio focus level 3 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_4, /* Audio focus level 4 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_5, /* Audio focus level 5 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_6, /* Audio focus level 6 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_7, /* Audio focus level 7 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_8, /* Audio focus level 8 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_9, /* Audio focus level 9 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_10, /* Audio focus level 10 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_11, /* Audio focus level 11 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_12, /* Audio focus level 12 */ + IVAS_FLVL_FOCUS_LEVEL_LEVEL_13, /* Audio focus level 13 */ + IVAS_FLVL_DEFAULT_AUDIO_FOCUS, /* Default audio focus */ + IVAS_FLVL_MAX_AUDIO_FOCUS, /* Apply max audio focus */ +} IVAS_FLVL; + +typedef struct +{ + size_t size; /* sizeof(IVAS_PIDATA_AUDIO_FOCUS) */ + uint32_t piDataType; /* IVAS_PI_AUDIO_FOCUS_INDCATION or IVAS_PI_AUDIO_FOCUS_REQUEST */ + bool availDirection; /* audio focus contains direction */ + bool availLevel; /* audio focus contains level */ + IVAS_QUATERNION direction; /* direction data expressed as quarternions */ + IVAS_FLVL flvl; /* audio focus level */ +} IVAS_PIDATA_AUDIO_FOCUS; + +/* 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 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; +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_AUDIO_FOCUS focusIndication; - - IVAS_PIDATA_ORIENTATION playbackOrientation; - IVAS_PIDATA_ORIENTATION headOrientation; - IVAS_PIDATA_LISTENER_POSITION listnerPosition; - IVAS_PIDATA_DYNAMIC_SUPPRESSION dynSuppression; - IVAS_PIDATA_AUDIO_FOCUS focusRequest; - 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; + 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_AUDIO_FOCUS focusIndication; + + IVAS_PIDATA_ORIENTATION playbackOrientation; + IVAS_PIDATA_ORIENTATION headOrientation; + IVAS_PIDATA_LISTENER_POSITION listnerPosition; + IVAS_PIDATA_DYNAMIC_SUPPRESSION dynSuppression; + IVAS_PIDATA_AUDIO_FOCUS focusRequest; + 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; + IVAS_PIDATA_NO_DATA noPiData; +} PIDATA; - typedef struct - { - PIDATA data; - uint32_t timestamp; - } PIDATA_TS; +typedef struct +{ + PIDATA data; + uint32_t timestamp; +} PIDATA_TS; #endif /* IVAS_RTPDUMP */ -#ifdef __cplusplus -} -#endif - #endif /* IVAS_RTP_PI_DATA_H */ diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py index 8d979a7e6d..bd13acaf56 100644 --- a/tests/rtp/ivasrtp.py +++ b/tests/rtp/ivasrtp.py @@ -44,7 +44,8 @@ import argparse from pathlib import Path from typing import cast, Optional -NO_REQ="NO_REQ" +NO_REQ = "NO_REQ" + class CAMODE(str, Enum): CA_LO_02 = "CA-LO-O2" @@ -56,23 +57,27 @@ class CAMODE(str, Enum): 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", + CODEC = ("codec",) BR = "bitrate" BW = "bandwidth" CA = "ca-mode" @@ -80,14 +85,16 @@ class REQUESTS(str, Enum): SUBFMT = "sub-format" SRCFG = "sr-config" + class FORMATS(str, Enum): - STEREO="Stereo" - SBA="SBA" - MASA="MASA" - ISM="ISM" - MC="MC" - OMASA="OMASA" - OSBA="OSBA" + STEREO = "Stereo" + SBA = "SBA" + MASA = "MASA" + ISM = "ISM" + MC = "MC" + OMASA = "OMASA" + OSBA = "OSBA" + class SUBFORMATS(str, Enum): FOA_PLANAR = "FOA planar" @@ -155,6 +162,7 @@ class SUBFORMATS(str, Enum): 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" @@ -189,6 +197,7 @@ class PIDATAS(str, Enum): RESERVED30 = "RESERVED30" NO_PI_DATA = "NO_PI_DATA" + class SUPPRESSION_LEVEL(int, Enum): SUPPRESSION_LEVEL_NONE = 0 SUPPRESSION_LEVEL_1 = 1 @@ -207,6 +216,7 @@ class SUPPRESSION_LEVEL(int, Enum): SUPPRESSION_LEVEL_14 = 14 SUPPRESSION_LEVEL_MAX = 15 + class AUDIO_FOCUS_LEVEL(int, Enum): AUDIO_FOCUS_LEVEL_NONE = 0 AUDIO_FOCUS_LEVEL_1 = 1 @@ -225,6 +235,7 @@ class AUDIO_FOCUS_LEVEL(int, Enum): AUDIO_FOCUS_LEVEL_DEFAULT = 14 AUDIO_FOCUS_LEVEL_NO_PREFERENCE = 15 + @dataclass class RTPHDR: version: int = 2 @@ -246,24 +257,28 @@ class RTPHDR: 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" + 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}') + 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" + 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}') + bitstrm.append(f"uintbe:32={ext}") @classmethod def unpack(cls, bitstrm: ConstBitStream): @@ -278,13 +293,16 @@ class RTPHDR: 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) ] + 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) ] + hdr.extensionWords = [ + bitstrm.read(32).uintbe for _ in range(hdr.extensionLength) + ] return hdr + @dataclass class SRCONFIG: diegetic: bool = False @@ -292,6 +310,7 @@ class SRCONFIG: pitch: bool = False roll: bool = False + @dataclass class CMR: bandwidth: BANDWIDTH @@ -300,12 +319,14 @@ class CMR: 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 @@ -316,7 +337,8 @@ class FRAME: timestamp: int = 0 au: bytes = field(default_factory=bytes) -#PI DATA STRUCTURES + +# PI DATA STRUCTURES @dataclass class ORIENTATION: w: float = 0.0 @@ -324,15 +346,18 @@ class ORIENTATION: 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 @@ -341,6 +366,7 @@ class AUDIO_DESCRIPTION: isEditable: bool = False isBinaural: bool = False + @dataclass class DYNAMIC_AUDIO_SUPPRESSION: preferSpeech: bool = False @@ -348,10 +374,12 @@ class DYNAMIC_AUDIO_SUPPRESSION: 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 @@ -360,70 +388,288 @@ class ACOUSTIC_ENVIRONMENT: dim: tuple[float, float, float] = () abscoeff: tuple[float, float, float, float, float, float] = () + @dataclass class AUDIO_FOCUS: direction: Optional[ORIENTATION] = None level: Optional[AUDIO_FOCUS_LEVEL] = None + @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] +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 + 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))) +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') + # 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]" + 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 @@ -434,14 +680,18 @@ def unpackOrientations(bitstrm: ConstBitStream, piSize: int) -> list[ORIENTATION 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)}') + 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" @@ -454,15 +704,19 @@ def unpackPositions(bitstrm: ConstBitStream, piSize: int) -> list[POSITION]: 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)}') + 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" @@ -470,23 +724,31 @@ def unpackOrientation(bitstrm: ConstBitStream, piSize: int) -> ORIENTATION: 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" + 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]: + +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): @@ -496,20 +758,30 @@ def unpackAudioDescription(bitstrm: ConstBitStream, piSize: int) -> list[AUDIO_D 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)) + 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]" + 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]" + 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') + 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" @@ -519,38 +791,52 @@ def unpackDAS(bitstrm: ConstBitStream, piSize: int) -> list[AUDIO_DESCRIPTION]: _ = bitstrm.read(5) SLI = bitstrm.read(4).uint _ = bitstrm.read(4) - return DYNAMIC_AUDIO_SUPPRESSION(preferSpeech=V, preferMusic=M, preferAmbiance=A, level=SLI) + 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" + 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') + 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 + 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" + 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)" + 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}') + bitstrm.append(f"bool={isDigetic}") nPad = 8 - (bitstrm.pos % 8) if nPad > 0: - bitstrm.append(f'uint:{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" + assert ( + piSize == 1 or piSize == 5 or piSize == 8 + ), "Incorrect PI Data Size for ACOUSTIC_ENVIRONMENT" rt60 = list() dsr = list() dim = list() @@ -571,32 +857,46 @@ def unpackAcousticEnv(bitstrm: ConstBitStream, piSize: int) -> DIEGETIC_TYPE: 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)) + 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" + 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}') + 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}') + 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}') + 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}') + bitstrm.append(f"uint:4={dim}") for n in range(6): absCoeff = mapNearestIndex(absorptionCoeffValues, aenv.abscoeff[n]) - bitstrm.append(f'uint:2={absCoeff}') + bitstrm.append(f"uint:2={absCoeff}") + def unpackAudioFocus(bitstrm: ConstBitStream, piSize: int) -> AUDIO_FOCUS: - assert piSize == 1 or piSize == 8 or piSize == 9, "Incorrect PI Data Size for AUDIO_FOCUS" + assert ( + piSize == 1 or piSize == 8 or piSize == 9 + ), "Incorrect PI Data Size for AUDIO_FOCUS" direction = None level = None if piSize == 1: @@ -610,87 +910,91 @@ def unpackAudioFocus(bitstrm: ConstBitStream, piSize: int) -> AUDIO_FOCUS: return AUDIO_FOCUS(direction=direction, level=level) + def packAudioFocus(bitstrm: BitStream, data: any): - assert type(data) == AUDIO_FOCUS, "Audio focus PI Data expects a data of type AUDIO_FOCUS" + assert ( + type(data) == AUDIO_FOCUS + ), "Audio focus PI Data expects a data of type AUDIO_FOCUS" auFocus = cast(AUDIO_FOCUS, data) if auFocus.direction is not None: packOrientations(bitstrm, [auFocus.direction]) if auFocus.level is not None: level = int(auFocus.level) - bitstrm.append(f'uint:4={level}') - bitstrm.append(f'uint:4=0') + bitstrm.append(f"uint:4={level}") + bitstrm.append(f"uint:4=0") 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 + 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 unpackAudioFocus, # AUDIO_FOCUS_INDICATION - unpackUnsupported, # RESERVED15 - unpackOrientation, # PLAYBACK_DEVICE_ORIENTATION - unpackOrientation, # HEAD_ORIENTATION - unpackPosition, # LISTENER_POSITION - unpackDAS, # DYNAMIC_AUDIO_SUPPRESSION + unpackUnsupported, # RESERVED15 + unpackOrientation, # PLAYBACK_DEVICE_ORIENTATION + unpackOrientation, # HEAD_ORIENTATION + unpackPosition, # LISTENER_POSITION + unpackDAS, # DYNAMIC_AUDIO_SUPPRESSION unpackAudioFocus, # AUDIO_FOCUS_REQUEST - 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 + 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 + 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 packAudioFocus, # AUDIO_FOCUS_INDICATION - packUnsupported, # RESERVED15 - packOrientation, # PLAYBACK_DEVICE_ORIENTATION - packOrientation, # HEAD_ORIENTATION - packPosition, # LISTENER_POSITION - packDAS, # DYNAMIC_AUDIO_SUPPRESSION + packUnsupported, # RESERVED15 + packOrientation, # PLAYBACK_DEVICE_ORIENTATION + packOrientation, # HEAD_ORIENTATION + packPosition, # LISTENER_POSITION + packDAS, # DYNAMIC_AUDIO_SUPPRESSION packAudioFocus, # 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 + 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: @@ -706,6 +1010,7 @@ def ReadG192Bitstream(g192File: Path) -> list[bytes]: refPackets.append(writer.tobytes()) return refPackets + def unpackEBytes(bitstrm: ConstBitStream) -> tuple[bool, dict]: piIndicated = False requests = dict() @@ -713,7 +1018,7 @@ def unpackEBytes(bitstrm: ConstBitStream) -> tuple[bool, dict]: if bitstrm.read(1).bool: T = bitstrm.read(3).uint BR = bitstrm.read(4).uint - if T in [5, 6]:#CA MODES + if T in [5, 6]: # CA MODES if BR < 8: requests[REQUESTS.CODEC] = cmrLookup[T].codec requests[REQUESTS.BR] = 13200 @@ -721,12 +1026,12 @@ def unpackEBytes(bitstrm: ConstBitStream) -> tuple[bool, dict]: requests[REQUESTS.BW] = cmrLookup[T].bandwidth else: raise Exception("Unsupported BR bits in CA Mode") - elif T == 7: #IVAS - if BR < 14 : + 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 : + elif BR == 14: raise Exception("Reserved BR idx in IVAS EByte") else: if BR >= cmrLookup[T].startIdx and BR < cmrLookup[T].endIdx: @@ -735,16 +1040,23 @@ def unpackEBytes(bitstrm: ConstBitStream) -> tuple[bool, dict]: 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 + 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] + 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 : + elif ET == 1: S = bitstrm.read(1).bool FMT = bitstrm.read(3).uint if not S: @@ -759,30 +1071,48 @@ def unpackEBytes(bitstrm: ConstBitStream) -> tuple[bool, dict]: 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) + 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)) + raise Exception( + "Unsupported subsequent EByte with ET={}".format(ET) + ) except ReadError as error: - print ("Underflow in E-Bytes parsing during unpacking, error = {}".format(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): + +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 + 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 + 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 @@ -795,19 +1125,27 @@ def packEBytes(bitstrm: BitStream, requests: dict[str: any], piIndication: bool 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] + 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" + 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}') + 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') + bitstrm.append("hex:8=A0") if codec != CODECS.IVAS: return @@ -815,43 +1153,50 @@ def packEBytes(bitstrm: BitStream, requests: dict[str: any], piIndication: bool # 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}') + 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}') + 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}') + 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}') + 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 + # 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)) + 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]: + +def unpackToCBytes( + bitstrm: ConstBitStream, rtpTimestamp: int, Codec: CODECS +) -> list[FRAME]: F = True frmList = list() @@ -861,9 +1206,9 @@ def unpackToCBytes(bitstrm: ConstBitStream, rtpTimestamp: int, Codec: CODECS) -> FT = bitstrm.read(2).uint BR = bitstrm.read(4).uint frm = FRAME(timestamp=rtpTimestamp) - if FT == 1 : + if FT == 1: frm.codec = CODECS.IVAS - if BR == 14 : + if BR == 14: supportedBitrates = [-1, 256000, 384000, 512000] reserved = bitstrm.read(1) D = bitstrm.read(1).bool @@ -872,8 +1217,11 @@ def unpackToCBytes(bitstrm: ConstBitStream, rtpTimestamp: int, Codec: CODECS) -> 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) + 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: @@ -881,7 +1229,7 @@ def unpackToCBytes(bitstrm: ConstBitStream, rtpTimestamp: int, Codec: CODECS) -> 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.speechLost = BR == 14 frm.bitrate = evsBitrates[BR] if BR < 13 else 0 else: frm.codec = CODECS.AMRWB @@ -893,25 +1241,28 @@ def unpackToCBytes(bitstrm: ConstBitStream, rtpTimestamp: int, Codec: CODECS) -> rtpTimestamp += 320 frmList.append(frm) if F: - #skip all frame specific E-bytes before next header - while (bitstrm.read(1).bool): + # 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)) + 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)) + 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 + 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) @@ -924,17 +1275,18 @@ def packToCBytes(bitstrm: BitStream, frameList: list[FRAME]): 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 + 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') + 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]() @@ -942,7 +1294,7 @@ def unpackPiData(bitstrm: ConstBitStream, rtpTimestamp: int) -> list[PIDATA]: # PI Data if Indicated PF = True piTimeStamps = rtpTimestamp - while PF : + while PF: PF = bitstrm.read(1).bool PM = bitstrm.read(2).uint PiType = bitstrm.read(5).uint @@ -956,17 +1308,32 @@ def unpackPiData(bitstrm: ConstBitStream, rtpTimestamp: int) -> list[PIDATA]: if PiTypeNames[PiType] != PIDATAS.NO_PI_DATA: piDataList.append( - PIDATA(timestamp=piTimeStamps if PM != 3 else rtpTimestamp, # Generic Pi has base timestamp + PIDATA( + timestamp=( + piTimeStamps if PM != 3 else rtpTimestamp + ), # Generic Pi has base timestamp type=PiTypeNames[PiType], - data=PiFrameData)) + data=PiFrameData, + ) + ) piTimeStamps += 320 if PM == 2 else 0 except ReadError as error: - print ("Underflow before completion of unpacking, error = {}".format(error)) + print("Underflow before completion of unpacking, error = {}".format(error)) return piDataList -def packPiData(bitstrm: BitStream, rtpTimestampBounds: tuple[int, int], piDataList: list[PIDATA]): + +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])] + 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) @@ -982,7 +1349,9 @@ def packPiData(bitstrm: BitStream, rtpTimestampBounds: tuple[int, int], piDataLi 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 + 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): @@ -993,12 +1362,14 @@ def packPiData(bitstrm: BitStream, rtpTimestampBounds: tuple[int, int], piDataLi 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}') + 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 @@ -1009,7 +1380,7 @@ def packPiData(bitstrm: BitStream, rtpTimestampBounds: tuple[int, int], piDataLi class IvasPayload: frameList: list[FRAME] = field(default_factory=list) piDataList: list[PIDATA] = field(default_factory=list) - requests: dict [str, any] = field(default_factory=dict) + requests: dict[str, any] = field(default_factory=dict) def pack(self, bitstrm: BitStream) -> int: piIndication = len(self.piDataList) > 0 @@ -1018,7 +1389,11 @@ class IvasPayload: packToCBytes(bitstrm, self.frameList) packAUFrames(bitstrm, self.frameList) if piIndication: - packPiData(bitstrm, (self.frameList[0].timestamp, self.frameList[-1].timestamp + 320), self.piDataList) + packPiData( + bitstrm, + (self.frameList[0].timestamp, self.frameList[-1].timestamp + 320), + self.piDataList, + ) return numFrames @classmethod @@ -1039,21 +1414,23 @@ class IvasPayload: 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) : + 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): + 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.Codec: CODECS = codec # Track last frame's codec self.requests = dict() self.piData = dict() @@ -1062,8 +1439,8 @@ class IvasRtp: 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') + 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) @@ -1092,7 +1469,10 @@ class IvasRtp: data = list() for orientation in dataDict: data.append(ORIENTATION(**orientation)) - elif "ORIENTATION" in piTypes or piTypes == PIDATAS.AUDIO_FOCUS_DIRECTION: + elif ( + "ORIENTATION" in piTypes + or piTypes == PIDATAS.AUDIO_FOCUS_DIRECTION + ): data = ORIENTATION(**dataDict) elif piTypes == PIDATAS.ACOUSTIC_ENVIRONMENT: data = ACOUSTIC_ENVIRONMENT(**dataDict) @@ -1102,19 +1482,30 @@ class IvasRtp: 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: + 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)) + 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): + def packG192File( + self, + g192File: Path, + rtpDumpOut: Path, + piData: dict = None, + requestsData: dict = None, + ): packet = IvasPacket() - packet.hdr.sequenceNum=int("0xFFFF", 16) + packet.hdr.sequenceNum = int("0xFFFF", 16) packet.hdr.timestamp = 0 packet.hdr.ssrc = int("0xDEADBEEF", 16) @@ -1130,61 +1521,115 @@ class IvasRtp: 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" + 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): + 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.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(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: + with open(rtpDumpFile, mode="rb") as fd: while True: size = fd.read(4) if not size: break - size = struct.unpack( 'i', size)[0] + 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 : + 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 + 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") + 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) @@ -1201,4 +1646,9 @@ if __name__ == "__main__": 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) + 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 index c1832202bb..69b85dc9af 100644 --- a/tests/rtp/test_rtp.py +++ b/tests/rtp/test_rtp.py @@ -68,18 +68,19 @@ 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 ( +def test_rtp_bitstream_amrwb( test_info, bitrate: int, dtx: bool, framesPerPacket: int, dut_encoder_frontend: EncoderFrontend, - dut_decoder_frontend: DecoderFrontend + dut_decoder_frontend: DecoderFrontend, ): - run_rtp_bitstream_tests ( + run_rtp_bitstream_tests( CODECS.AMRWB, bitrate, "WB", @@ -88,15 +89,16 @@ def test_rtp_bitstream_amrwb ( dtx, framesPerPacket, dut_encoder_frontend, - dut_decoder_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 ( +def test_rtp_bitstream_evs( test_info, bitrate: int, bandwidth: str, @@ -104,9 +106,9 @@ def test_rtp_bitstream_evs ( dtx: bool, framesPerPacket: int, dut_encoder_frontend: EncoderFrontend, - dut_decoder_frontend: DecoderFrontend + dut_decoder_frontend: DecoderFrontend, ): - run_rtp_bitstream_tests ( + run_rtp_bitstream_tests( CODECS.EVS, bitrate, bandwidth, @@ -115,7 +117,7 @@ def test_rtp_bitstream_evs ( dtx, framesPerPacket, dut_encoder_frontend, - dut_decoder_frontend + dut_decoder_frontend, ) @@ -123,16 +125,16 @@ def test_rtp_bitstream_evs ( @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 ( +def test_rtp_bitstream_ivas_nodtx( test_info, bitrate: int, bandwidth: str, format: str, framesPerPacket: int, dut_encoder_frontend: EncoderFrontend, - dut_decoder_frontend: DecoderFrontend + dut_decoder_frontend: DecoderFrontend, ): - run_rtp_bitstream_tests ( + run_rtp_bitstream_tests( CODECS.IVAS, bitrate, bandwidth, @@ -141,23 +143,24 @@ def test_rtp_bitstream_ivas_nodtx ( False, framesPerPacket, dut_encoder_frontend, - dut_decoder_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 ( +def test_rtp_bitstream_ivas_dtx( test_info, bitrate: int, bandwidth: str, format: str, framesPerPacket: int, dut_encoder_frontend: EncoderFrontend, - dut_decoder_frontend: DecoderFrontend + dut_decoder_frontend: DecoderFrontend, ): - run_rtp_bitstream_tests ( + run_rtp_bitstream_tests( CODECS.IVAS, bitrate, bandwidth, @@ -166,24 +169,63 @@ def test_rtp_bitstream_ivas_dtx ( True, framesPerPacket, dut_encoder_frontend, - dut_decoder_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)) ]) - someAuFocusDirLvl = lambda : AUDIO_FOCUS(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), level=AUDIO_FOCUS_LEVEL(random.randint(0, 15))) - someAuFocusDir = lambda : AUDIO_FOCUS(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)) - someAuFocusLvl = lambda : AUDIO_FOCUS(level=AUDIO_FOCUS_LEVEL(random.randint(0, 15))) + 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))] + ) + someAuFocusDirLvl = lambda: AUDIO_FOCUS( + 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, + ), + level=AUDIO_FOCUS_LEVEL(random.randint(0, 15)), + ) + someAuFocusDir = lambda: AUDIO_FOCUS( + 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, + ) + ) + someAuFocusLvl = lambda: AUDIO_FOCUS(level=AUDIO_FOCUS_LEVEL(random.randint(0, 15))) someAuFocusList = [someAuFocusDirLvl, someAuFocusDir, someAuFocusLvl] for ts in range(startTs, endTs, 320): @@ -198,7 +240,9 @@ def generatePiData(startTs: int, endTs: int) -> dict: 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)) + pidata["ACOUSTIC_ENVIRONMENT"] = ACOUSTIC_ENVIRONMENT( + aeid=random.randint(0, 127) + ) data[str(ts)] = pidata return data @@ -206,7 +250,10 @@ def generatePiData(startTs: int, endTs: int) -> dict: 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" + 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" @@ -214,54 +261,85 @@ def isEqualOrientation(ref: ORIENTATION, dut: ORIENTATION): 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.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" + 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.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)" + 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}" + def isEqualAudioFocus(ref: AUDIO_FOCUS, dut: AUDIO_FOCUS): if ref.direction is not None or dut.direction is not None: assert ref.direction is not None, "Audio Focus PI Data missing direction" assert dut.direction is not None, "Audio Focus PI Data missing direction" if ref.direction is not None and dut.direction is not None: - assert abs(ref.direction['w'] - dut.direction.w) < 0.0001, "Audio Focus PI Data mismatch in direction w" - assert abs(ref.direction['x'] - dut.direction.x) < 0.0001, "Audio Focus PI Data mismatch in direction x" - assert abs(ref.direction['y'] - dut.direction.y) < 0.0001, "Audio Focus PI Data mismatch in direction y" - assert abs(ref.direction['z'] - dut.direction.z) < 0.0001, "Audio Focus PI Data mismatch in direction z" + assert ( + abs(ref.direction["w"] - dut.direction.w) < 0.0001 + ), "Audio Focus PI Data mismatch in direction w" + assert ( + abs(ref.direction["x"] - dut.direction.x) < 0.0001 + ), "Audio Focus PI Data mismatch in direction x" + assert ( + abs(ref.direction["y"] - dut.direction.y) < 0.0001 + ), "Audio Focus PI Data mismatch in direction y" + assert ( + abs(ref.direction["z"] - dut.direction.z) < 0.0001 + ), "Audio Focus PI Data mismatch in direction z" assert ref.level == dut.level, "Audio Focus PI Data mismatch in level" + 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) ] + 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]: @@ -269,14 +347,14 @@ class CSVREADER: self.rIdx += 1 if self.rIdx == self.count: self.rIdx = 0 - return [ float(x) for x in row ] + return [float(x) for x in row] -class RTPVALIDATE: - DTX_BITRATES = { CODECS.IVAS: 5200, CODECS.EVS: 2400, CODECS.AMRWB: 1750 } +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): + def __init__(self, codec=CODECS.IVAS, bitrate=24400, framesPerPacket=1, dtx=False): self.framesPerPacket = framesPerPacket self.dtx = dtx self.codec = codec @@ -288,8 +366,8 @@ class RTPVALIDATE: self.validatePiData = False self.g192File = None self.frameIdx = 0 - #PI DATA - self.readers: dict[str : CSVREADER] = dict() + # PI DATA + self.readers: dict[str:CSVREADER] = dict() def setPiDataFiles(self, piFiles: tuple[Path]): self.validatePiData = True @@ -316,12 +394,18 @@ class RTPVALIDATE: 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" + 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" + 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" @@ -335,21 +419,38 @@ class RTPVALIDATE: 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 ( + 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 + # 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) + 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() + 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] = []): + 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()) @@ -359,15 +460,21 @@ class TVARGS: return self.tvDict[fmt][0] def args(self, fmt, addPI=False) -> list[str]: - args = [ x for x in self.tvDict[fmt][1] ] + 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) ] + 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 ( + +def run_rtp_bitstream_tests( codec: CODECS, bitrate: int, bandwidth: str, @@ -376,12 +483,12 @@ def run_rtp_bitstream_tests ( dtx: bool, framesPerPacket: int, dut_encoder_frontend: EncoderFrontend, - dut_decoder_frontend: DecoderFrontend + dut_decoder_frontend: DecoderFrontend, ): tvArgs = TVARGS() tvArgs.add("MONO", "stv48n.wav") - if dtx: #use bigger file for dtx stereo + if dtx: # use bigger file for dtx stereo tvArgs.add("STEREO", "stvST48n.wav", ["-stereo"]) else: tvArgs.add("STEREO", "stv2MASA2TC48c.wav", ["-stereo"]) @@ -390,25 +497,52 @@ def run_rtp_bitstream_tests ( 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): + 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)) + 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 + 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() + 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( @@ -419,7 +553,7 @@ def run_rtp_bitstream_tests ( sba_order=None, dtx_mode=dtx, max_band=bandwidth, - add_option_list=tvArgs.args(format) + add_option_list=tvArgs.args(format), ) validate.setRefG192Bitstream(g192File=g192Out) @@ -432,14 +566,19 @@ def run_rtp_bitstream_tests ( outMode = "" generatedPIData = dict() - packer.packG192File(g192File=g192Out, rtpDumpOut=rtpdumpIn, piData=generatedPIData, requestsData=generateRequests(0, 1600)) + 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= [] + add_option_list=[], ) dut_decoder_frontend.run( @@ -447,26 +586,36 @@ def run_rtp_bitstream_tests ( output_sampling_rate=48, input_bitstream_path=rtpdumpIn, output_path=pcmOut, - add_option_list= ["-VOIP_HF_ONLY=1", "-PiDataFile", str(piDataOutJson)] + add_option_list=["-VOIP_HF_ONLY=1", "-PiDataFile", str(piDataOutJson)], ) decAudio, fs = readfile(pcmOut) g192Audio, Fs = readfile(pcmOutG192) - decAudio /= 32768.0 # readfile reuturns 16 bit int - g192Audio /= 32768.0 # readfile reuturns 16 bit int - decAudio = decAudio[4*960:] - assert abs(decAudio.shape[0] - g192Audio.shape[0]) <= (4 * 960), "Decoded PCM Audio is not same length as input" + decAudio /= 32768.0 # readfile reuturns 16 bit int + g192Audio /= 32768.0 # readfile reuturns 16 bit int + 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) + 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" + 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()}" + 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] @@ -484,7 +633,9 @@ def run_rtp_bitstream_tests ( elif type(generatedPIData[ts][pitype]) == AUDIO_FOCUS: isEqualAudioFocus(AUDIO_FOCUS(**decoded), data) elif type(generatedPIData[ts][pitype]) == list: - for r, d in zip(generatedPIData[ts][pitype], decodedPiData[ts][pitype]): + for r, d in zip( + generatedPIData[ts][pitype], decodedPiData[ts][pitype] + ): isEqualAD(AUDIO_DESCRIPTION(**d), r) else: assert False, "Unsupported PI data found" @@ -505,7 +656,7 @@ def run_rtp_bitstream_tests ( sba_order=None, dtx_mode=dtx, max_band=bandwidth, - add_option_list=extra_args + add_option_list=extra_args, ) unpacker = IvasRtp() -- GitLab From 6880621b6dacbda2ce5d29b7bf3cbf3183da83e0 Mon Sep 17 00:00:00 2001 From: Ripinder Singh Date: Thu, 9 Oct 2025 11:17:02 +1100 Subject: [PATCH 095/100] Fix for crash in decoder on free Signed-off-by: Ripinder Singh --- lib_dec/lib_dec.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib_dec/lib_dec.c b/lib_dec/lib_dec.c index e0fc232dc2..4f6c1f75ab 100644 --- a/lib_dec/lib_dec.c +++ b/lib_dec/lib_dec.c @@ -346,11 +346,17 @@ void IVAS_DEC_Close( ( *phIvasDec )->hVoIP = NULL; } +#ifndef IVAS_RTPDUMP /* destroy Split binaural renderer (ISAR) handle */ ivas_destroy_handle_isar( &( *phIvasDec )->st_ivas->hSplitBinRend ); +#endif if ( ( *phIvasDec )->st_ivas ) { +#ifdef IVAS_RTPDUMP + /* destroy Split binaural renderer (ISAR) handle */ + ivas_destroy_handle_isar( &( *phIvasDec )->st_ivas->hSplitBinRend ); +#endif ivas_destroy_dec( ( *phIvasDec )->st_ivas ); ( *phIvasDec )->st_ivas = NULL; } -- GitLab From ae6af7da4011d99f1c2eed17027362ee2d97edda Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Thu, 9 Oct 2025 11:34:09 +0300 Subject: [PATCH 096/100] Remove rtpdump test --- .gitlab-ci.yml | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ef260a2b99..0b735a6df4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -707,28 +707,6 @@ split-rendering-voip-be-to-binaural: junit: - report-junit.xml -# test rtpdump -rtpdump-test: - extends: - - .test-job-linux - - .rules-merge-request-to-main - needs: ["build-codec-linux-make"] - stage: test - script: - - make -j - - testcase_timeout=30 - - python3 -m pytest -q -n auto -rA --junit-xml=report-junit.xml tests/rtp/test_rtp.py --testcase_timeout=$testcase_timeout - artifacts: - name: "mr-$CI_MERGE_REQUEST_IID--sha-$CI_COMMIT_SHORT_SHA--job-$CI_JOB_NAME--results" - expire_in: 1 week - when: always - paths: - - report-junit.xml - expose_as: "rtpdump pytest results" - reports: - junit: - - report-junit.xml - lc3-wrapper-unit-test: extends: - .test-job-linux -- GitLab From 600054fc0decabd26bce6905d7e2b705cb105ece Mon Sep 17 00:00:00 2001 From: Lauros Pajunen Date: Thu, 9 Oct 2025 12:04:11 +0300 Subject: [PATCH 097/100] Remove rtpdump python tests and requirement --- tests/requirements.txt | 1 - tests/rtp/ivasrtp.py | 1654 ---------------------------------------- tests/rtp/test_rtp.py | 665 ---------------- 3 files changed, 2320 deletions(-) delete mode 100644 tests/rtp/ivasrtp.py delete mode 100644 tests/rtp/test_rtp.py diff --git a/tests/requirements.txt b/tests/requirements.txt index 00100fae88..2eb090f4fb 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -2,4 +2,3 @@ pytest>=5.3.5 pytest-xdist>=1.31.0 scipy>=1.5.2 numpy>=1.19.2 -bitstring>=4.3.1 diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py deleted file mode 100644 index bd13acaf56..0000000000 --- a/tests/rtp/ivasrtp.py +++ /dev/null @@ -1,1654 +0,0 @@ -#!/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, Optional - -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" - AUDIO_FOCUS_INDICATION = "AUDIO_FOCUS_INDICATION" - RESERVED15 = "RESERVED15" - PLAYBACK_DEVICE_ORIENTATION = "PLAYBACK_DEVICE_ORIENTATION" - HEAD_ORIENTATION = "HEAD_ORIENTATION" - LISTENER_POSITION = "LISTENER_POSITION" - DYNAMIC_AUDIO_SUPPRESSION = "DYNAMIC_AUDIO_SUPPRESSION" - AUDIO_FOCUS_REQUEST = "AUDIO_FOCUS_REQUEST" - 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 - - -class AUDIO_FOCUS_LEVEL(int, Enum): - AUDIO_FOCUS_LEVEL_NONE = 0 - AUDIO_FOCUS_LEVEL_1 = 1 - AUDIO_FOCUS_LEVEL_2 = 2 - AUDIO_FOCUS_LEVEL_3 = 3 - AUDIO_FOCUS_LEVEL_4 = 4 - AUDIO_FOCUS_LEVEL_5 = 5 - AUDIO_FOCUS_LEVEL_6 = 6 - AUDIO_FOCUS_LEVEL_7 = 7 - AUDIO_FOCUS_LEVEL_8 = 8 - AUDIO_FOCUS_LEVEL_9 = 9 - AUDIO_FOCUS_LEVEL_10 = 10 - AUDIO_FOCUS_LEVEL_11 = 11 - AUDIO_FOCUS_LEVEL_12 = 12 - AUDIO_FOCUS_LEVEL_13 = 13 - AUDIO_FOCUS_LEVEL_DEFAULT = 14 - AUDIO_FOCUS_LEVEL_NO_PREFERENCE = 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 AUDIO_FOCUS: - direction: Optional[ORIENTATION] = None - level: Optional[AUDIO_FOCUS_LEVEL] = None - - -@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}") - - -def unpackAudioFocus(bitstrm: ConstBitStream, piSize: int) -> AUDIO_FOCUS: - assert ( - piSize == 1 or piSize == 8 or piSize == 9 - ), "Incorrect PI Data Size for AUDIO_FOCUS" - direction = None - level = None - if piSize == 1: - level = bitstrm.read(4).uint - _ = bitstrm.read(4) - else: - direction = unpackOrientation(bitstrm, 8) - if piSize == 9: - level = bitstrm.read(4).uint - _ = bitstrm.read(4) - - return AUDIO_FOCUS(direction=direction, level=level) - - -def packAudioFocus(bitstrm: BitStream, data: any): - assert ( - type(data) == AUDIO_FOCUS - ), "Audio focus PI Data expects a data of type AUDIO_FOCUS" - auFocus = cast(AUDIO_FOCUS, data) - if auFocus.direction is not None: - packOrientations(bitstrm, [auFocus.direction]) - if auFocus.level is not None: - level = int(auFocus.level) - bitstrm.append(f"uint:4={level}") - bitstrm.append(f"uint:4=0") - - -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 - unpackAudioFocus, # AUDIO_FOCUS_INDICATION - unpackUnsupported, # RESERVED15 - unpackOrientation, # PLAYBACK_DEVICE_ORIENTATION - unpackOrientation, # HEAD_ORIENTATION - unpackPosition, # LISTENER_POSITION - unpackDAS, # DYNAMIC_AUDIO_SUPPRESSION - unpackAudioFocus, # AUDIO_FOCUS_REQUEST - 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 - packAudioFocus, # AUDIO_FOCUS_INDICATION - packUnsupported, # RESERVED15 - packOrientation, # PLAYBACK_DEVICE_ORIENTATION - packOrientation, # HEAD_ORIENTATION - packPosition, # LISTENER_POSITION - packDAS, # DYNAMIC_AUDIO_SUPPRESSION - packAudioFocus, # 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 deleted file mode 100644 index 69b85dc9af..0000000000 --- a/tests/rtp/test_rtp.py +++ /dev/null @@ -1,665 +0,0 @@ -#!/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__ = """ -This test does the following procedure:- - -DECODER TEST -============ -* Encode a input for given format, bitrate and dtx info using IVAS Encoder to g192 -* Generate random PI data to be used for RTP packaging -* Use g192 and random PI data to pack a stream into RTPDump stream using the reference rtp packer implementation in python -* Provide the rtpdump stream to IVAS decoder to decode the stream and generate a PI data dump in a JSON file -* Decode G192 stream using IVAS decoder and compare against output of RTPdump for bit-exactness -* Validate dumped JSON agains original random PI data to check for similar data after deocde - -ENCODER TEST -============ -* Encode the input for given format, bitrate and dtx info using IVAS Encoder to rtpdump directly -* Validate the generated rtp dump using reference python implementation of RTP depacker for following:- - * RTP Header consistency (Timestamp, seq number, etc) - * IVAS Payload (no of frames in packet, bitrate indicated, bitexactness of frames in packet) - * PI Data verification (Timestamp, Pidata Type, similarity of PI data) -""" - -import pytest -import csv -import os -import sys -import random - -from tempfile import TemporaryDirectory -from pathlib import Path -from ivasrtp import * -import numpy as np -from pyaudio3dtools.audiofile import readfile - -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))] - ) - someAuFocusDirLvl = lambda: AUDIO_FOCUS( - 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, - ), - level=AUDIO_FOCUS_LEVEL(random.randint(0, 15)), - ) - someAuFocusDir = lambda: AUDIO_FOCUS( - 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, - ) - ) - someAuFocusLvl = lambda: AUDIO_FOCUS(level=AUDIO_FOCUS_LEVEL(random.randint(0, 15))) - someAuFocusList = [someAuFocusDirLvl, someAuFocusDir, someAuFocusLvl] - - 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_REQUEST"] = random.choice(someAuFocusList)() - 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}" - - -def isEqualAudioFocus(ref: AUDIO_FOCUS, dut: AUDIO_FOCUS): - if ref.direction is not None or dut.direction is not None: - assert ref.direction is not None, "Audio Focus PI Data missing direction" - assert dut.direction is not None, "Audio Focus PI Data missing direction" - if ref.direction is not None and dut.direction is not None: - assert ( - abs(ref.direction["w"] - dut.direction.w) < 0.0001 - ), "Audio Focus PI Data mismatch in direction w" - assert ( - abs(ref.direction["x"] - dut.direction.x) < 0.0001 - ), "Audio Focus PI Data mismatch in direction x" - assert ( - abs(ref.direction["y"] - dut.direction.y) < 0.0001 - ), "Audio Focus PI Data mismatch in direction y" - assert ( - abs(ref.direction["z"] - dut.direction.z) < 0.0001 - ), "Audio Focus PI Data mismatch in direction z" - assert ref.level == dut.level, "Audio Focus PI Data mismatch in level" - - -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 = readfile(pcmOut) - g192Audio, Fs = readfile(pcmOutG192) - decAudio /= 32768.0 # readfile reuturns 16 bit int - g192Audio /= 32768.0 # readfile reuturns 16 bit int - 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]) == AUDIO_FOCUS: - isEqualAudioFocus(AUDIO_FOCUS(**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) -- GitLab From db4b253e38b46f47a19e459014e19e9ee60b2a73 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 13 Oct 2025 10:34:53 +0200 Subject: [PATCH 098/100] increase coverage test timeout --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0b735a6df4..ef4908c45b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2195,7 +2195,7 @@ sanitizer-test-osba-planar-hoa3-ism4: rules: - if: $COVERAGE_TEST - if: $MANUAL_PIPELINE_TYPE == "coverage" - timeout: 3 hours + timeout: 6 hours before_script: - !reference [.job-linux, before_script] - set -e -- GitLab From 1468278260411740c282d7a3090fbaa3c69e51d8 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Mon, 13 Oct 2025 10:46:51 +0200 Subject: [PATCH 099/100] add command line logging for renderer tests --- tests/renderer/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/renderer/utils.py b/tests/renderer/utils.py index 657b1aa616..2a0d50568c 100644 --- a/tests/renderer/utils.py +++ b/tests/renderer/utils.py @@ -474,6 +474,7 @@ def run_renderer( record_property(k, v) if output_differs_parts[0]: + logging.error(f"Command line was: {' '.join(cmd)}") pytest.fail(f"Output differs: ({reason_parts[0]})") # compare metadata files in case of MASA prerendering -- GitLab From 9034de3b5076ee0b7126e8278666c1e30829fd92 Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Tue, 14 Oct 2025 09:37:38 +0200 Subject: [PATCH 100/100] add rtpdump testcases to the remove list --- ci/remove_unsupported_testcases.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ci/remove_unsupported_testcases.py b/ci/remove_unsupported_testcases.py index b83b9c3271..0fdf8c42fa 100644 --- a/ci/remove_unsupported_testcases.py +++ b/ci/remove_unsupported_testcases.py @@ -56,6 +56,16 @@ TESTCASES = [ "OMASA 2TC 4ISM at 80 kbps, 48kHz in, 48kHz out, BINAURAL out, default object editing, 1SEP-PARAM", "OMASA 2TC 4ISM at 256 kbps, 48kHz in, 48kHz out, BINAURAL out, object editing, JBM Prof 5, DISC", "OMASA 2TC 2ISM br sw techs 13.2 to 512 kbps start 48 kbps, 48kHz in, 48kHz out, BINAURAL out, object editing", + # rtpdump tests (also skipped in pytest code, but for documentation purposes added here as well) + "stereo bitrate switching from 13.2 kbps to 128 kbps, 48kHz in, 48kHz out, DTX on, EXT out, rtpdump", + "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", + "SBA FOA bitrate switching from 13.2 kbps to 512 kbps, 48kHz in, 48kHz out, DTX on, BINAURAL out, rtpdump, PI data", + "MASA 2dir 2TC bitrate switching from 13.2 kbps to 512 kbps, 48kHz in, 48kHz out, DTX on, BINAURAL out, rtpdump, PI data", + "Multi-channel 5_1 bitrate switching from 13.2 kbps to 512 kbps, 48kHz in, 48kHz out, BINAURAL out, rtpdump, PI data", + "Stereo downmix to bit-exact EVS at 24400 kbps, 48kHz in, 48kHz out, rtpdump", + "EVS at 13.2 kbps, 48kHz in, 48kHz out, STEREO out, rtpdump", + "OMASA 2Dir2TC 3ISM at br sw techs 13.2 to 512 kbps start 160 kbps, 48kHz in, 48kHz out, BINAURAL out, rtpdump, PI data", + "OSBA 2ISM 2OA at bitrate switching 13.2 to 512 kbps, 48kHz in, 48kHz out, BINAURAL out, rtpdump, PI data", ] -- GitLab