diff --git a/lib_com/options.h b/lib_com/options.h index 1f1a3eb7f7847a4e556412b1106c9fd77557d24f..f3930db6da9047c70501f14adce12329cae1907a 100644 --- a/lib_com/options.h +++ b/lib_com/options.h @@ -163,6 +163,7 @@ #define IVAS_RTPDUMP /* RTPDUMP writing and reading for IVAS payloads */ #define IVAS_RTPDUMP_ACOUSTIC_ENVIRONMENT /* RTPDUMP acoustic environment */ #define FIXED_RTP_SEQUENCE_NUM /* Remove random sequence number initialization */ +#define PI_LATENCY /* Pi latency PI frame */ #define ISM_PI_DATA /* Add reading and packing/unpacking of ISM PI data */ #define REVERSE_ISM_PI_DATA /* Add reading and packing/unpacking of reverse ISM PI data */ diff --git a/lib_util/ivas_rtp_file.c b/lib_util/ivas_rtp_file.c index e1b54c14406be26318844349663946b0b11bcdeb..e306cc311193a68d27242435c5b5bd044789ccbb 100644 --- a/lib_util/ivas_rtp_file.c +++ b/lib_util/ivas_rtp_file.c @@ -444,8 +444,19 @@ void IVAS_RTP_LogPiData( case IVAS_PI_ISM_POSITION: case IVAS_PI_ISM_DISTANCE_ATTENUATION: case IVAS_PI_ISM_DIRECTIVITY: + { + fprintf( f_piDataOut, "{}" ); + } + break; #endif case IVAS_PI_PI_LATENCY: + { + fprintf( f_piDataOut, "{" ); + fprintf( f_piDataOut, "\n\t\t\t\"reverseType\": \"%s\",", PiDataNames[cur->data.piLatency.type] ); + fprintf( f_piDataOut, "\n\t\t\t\"latency\": %d", cur->data.piLatency.latency ); + fprintf( f_piDataOut, "\n\t\t}" ); + } + break; case IVAS_PI_R_ISM_ID: #ifdef REVERSE_ISM_PI_DATA { @@ -707,7 +718,7 @@ void IVAS_RTP_WriteExtPiData( break; case IVAS_PI_PI_LATENCY: { - fprintf( f_piDataOut, "%d", cur->data.piLatency.latency ); + fprintf( f_piDataOut, "%s,%d", PiDataNames[cur->data.piLatency.type], cur->data.piLatency.latency ); } break; case IVAS_PI_R_ISM_ID: diff --git a/lib_util/ivas_rtp_pi_data.c b/lib_util/ivas_rtp_pi_data.c index 0993386c217cacf27d61a6e52c7c0c50cef9a331..710121db6822e0042df38728e01d878d62601fbd 100644 --- a/lib_util/ivas_rtp_pi_data.c +++ b/lib_util/ivas_rtp_pi_data.c @@ -938,7 +938,75 @@ static ivas_error unpackISMID( const uint8_t *buffer, uint32_t numDataBytes, IVA return IVAS_ERR_OK; } +#endif +#ifdef PI_LATENCY +static ivas_error packPiLatency( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten ) +{ + uint32_t typeBits; + uint32_t latencyBits; + uint32_t word; + uint32_t nBytes = 0; + const IVAS_PIDATA_REVERSE_PI_LATENCY *p = (const IVAS_PIDATA_REVERSE_PI_LATENCY *) piData; + *nBytesWritten = 0; + if ( piData->size != sizeof( IVAS_PIDATA_REVERSE_PI_LATENCY ) ) + { + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect size for PI_LATENCY data" ); + } + if ( piData->piDataType != IVAS_PI_PI_LATENCY ) + { + return IVAS_ERROR( IVAS_ERR_WRONG_PARAMS, "Incorrect PI ID for PI_LATENCY data" ); + } + if ( maxDataBytes < 2 + 4 ) + { + return IVAS_ERROR( IVAS_ERR_RTP_INSUFFICIENT_OUTPUT_SIZE, "Insufficient space to pack PI_LATENCY data" ); + } + + buffer[nBytes++] = ( p->piDataType & MASK_5BIT ); + buffer[nBytes++] = 4; + + typeBits = (uint32_t) ( p->type & MASK_5BIT ); + latencyBits = (uint32_t) ( p->latency & 0x07FFFFFF ); + word = ( typeBits << 27 ) | latencyBits; + + buffer[nBytes++] = (uint8_t) ( word >> 24 ); + buffer[nBytes++] = (uint8_t) ( word >> 16 ); + buffer[nBytes++] = (uint8_t) ( word >> 8 ); + buffer[nBytes++] = (uint8_t) ( word ); + *nBytesWritten = nBytes; + + return IVAS_ERR_OK; +} + +static ivas_error unpackPiLatency( const uint8_t *buffer, uint32_t numDataBytes, IVAS_PIDATA_GENERIC *piData ) +{ + uint32_t word; + uint32_t lat; + IVAS_PIDATA_REVERSE_PI_LATENCY *p = (IVAS_PIDATA_REVERSE_PI_LATENCY *) piData; + + if ( numDataBytes != 4 ) + { + return IVAS_ERROR( IVAS_ERR_RTP_UNPACK_PI_DATA, "Incorrect size to unpack PI_LATENCY data" ); + } + + p->size = sizeof( IVAS_PIDATA_REVERSE_PI_LATENCY ); + p->piDataType = IVAS_PI_PI_LATENCY; + + word = ( (uint32_t) buffer[0] << 24 ) | ( (uint32_t) buffer[1] << 16 ) | + ( (uint32_t) buffer[2] << 8 ) | (uint32_t) buffer[3]; + p->type = (IVAS_PI_TYPE) ( ( word >> 27 ) & MASK_5BIT ); + lat = word & 0x07FFFFFF; + + /* Sign-extend 27-bit value */ + if ( lat & ( 1u << 26 ) ) + p->latency = (int32_t) ( lat | ~0x07FFFFFF ); + else + p->latency = (int32_t) lat; + + return IVAS_ERR_OK; +} +#endif +#ifdef ISM_PI_DATA static ivas_error packISMGain( const IVAS_PIDATA_GENERIC *piData, uint8_t *buffer, uint32_t maxDataBytes, uint32_t *nBytesWritten ) { uint32_t nBytes = 0, n, idx; @@ -1432,14 +1500,19 @@ static const PACK_PI_FN packPiDataFuntions[IVAS_PI_MAX_ID] = { #endif packDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION */ packAudioFocusCommon, /* AUDIO_FOCUS_REQUEST */ +#ifdef PI_LATENCY + packPiLatency, /* PI_LATENCY */ +#else + packUnsupportedData, /* PI_LATENCY */ +#endif #else packUnsupportedData, /* PLAYBACK_DEVICE_ORIENTATION */ packUnsupportedData, /* HEAD_ORIENTATION */ packUnsupportedData, /* LISTENER_POSITION */ packUnsupportedData, /* DYNAMIC_AUDIO_SUPPRESSION */ packUnsupportedData, /* AUDIO_FOCUS_DIRECTION */ + packUnsupportedData, /* PI_LATENCY */ #endif - packUnsupportedData, /* PI_LATENCY */ #ifdef REVERSE_ISM_PI_DATA packReverseISMID, /* R_ISM_ID */ packReverseISMGain, /* R_ISM_GAIN */ @@ -1515,14 +1588,19 @@ static const UNPACK_PI_FN unpackPiDataFuntions[IVAS_PI_MAX_ID] = { #endif unpackDynamicSuppression, /* DYNAMIC_AUDIO_SUPPRESSION */ unpackAudioFocusCommon, /* AUDIO_FOCUS_REQUEST */ +#ifdef PI_LATENCY + unpackPiLatency, /* PI_LATENCY */ +#else + unpackUnsupportedData, /* PI_LATENCY */ +#endif #else unpackUnsupportedData, /* PLAYBACK_DEVICE_ORIENTATION */ unpackUnsupportedData, /* HEAD_ORIENTATION */ unpackUnsupportedData, /* LISTENER_POSITION */ unpackUnsupportedData, /* DYNAMIC_AUDIO_SUPPRESSION */ + unpackUnsupportedData, /* PI_LATENCY */ unpackUnsupportedData, /* AUDIO_FOCUS_DIRECTION */ #endif - unpackUnsupportedData, /* PI_LATENCY */ #ifdef REVERSE_ISM_PI_DATA unpackReverseISMID, /* R_ISM_ID */ unpackReverseISMGain, /* R_ISM_GAIN */ diff --git a/tests/rtp/ivasrtp.py b/tests/rtp/ivasrtp.py index d57adb67b6d81f448e2e91789d469849c301f6c3..224df7472e22197c14ec8366b63c1079d679311a 100644 --- a/tests/rtp/ivasrtp.py +++ b/tests/rtp/ivasrtp.py @@ -447,6 +447,11 @@ class AUDIO_FOCUS: direction: Optional[ORIENTATION] = None level: Optional[AUDIO_FOCUS_LEVEL] = None +@dataclass +class PI_LATENCY: + reverseType: PIDATAS + latency: int + @dataclass class PIDATA: @@ -998,6 +1003,24 @@ def packAudioFocus(bitstrm: BitStream, data: any): bitstrm.append(f"uint:4={level}") bitstrm.append(f"uint:4=0") +def unpackPiLatency(bitstrm: ConstBitStream, piSize: int) -> PI_LATENCY: + assert piSize == 4, "PI_LATENCY must be 4 bytes" + word = bitstrm.read(32).uint + typeBits = (word >> 27) & 0x1F + reverseType = PiTypeNames[typeBits] + raw = word & 0x07FFFFFF + # Sign-extend 27-bit + if raw & (1 << 26): + raw = raw | ~0x07FFFFFF + return PI_LATENCY(reverseType, int(raw)) + +def packPiLatency(bitstrm: BitStream, data: any) -> None: + assert type(data) == PI_LATENCY, "PI_LATENCY pack expects PI_LATENCY data" + idx = PiTypeNames.index(data.reverseType) + latency = data.latency & 0x07FFFFFF + word = (idx << 27) | latency + bitstrm.append(f"uint:32={word}") + def unpackISMNum(bitstrm: ConstBitStream, piSize: int) -> ISM_NUM: assert piSize == 1, "Incorrect PI Data Size for ISM_NUM" numISM = bitstrm.read(2).uint + 1 @@ -1154,7 +1177,7 @@ PIDataUnpacker = [ unpackPosition, # LISTENER_POSITION unpackDAS, # DYNAMIC_AUDIO_SUPPRESSION_REQUEST unpackAudioFocus, # AUDIO_FOCUS_REQUEST - unpackUnsupported, # PI_LATENCY + unpackPiLatency, # PI_LATENCY unpackReverseISMID, # R_ISM_ID unpackReverseISMGain, # R_ISM_GAIN unpackOrientation, # R_ISM_ORIENTATION @@ -1189,7 +1212,7 @@ PIDataPacker = [ packPosition, # LISTENER_POSITION packDAS, # DYNAMIC_AUDIO_SUPPRESSION_REQUEST packAudioFocus, # AUDIO_FOCUS_DIRECTION - packUnsupported, # PI_LATENCY + packPiLatency, # PI_LATENCY packReverseISMID, # R_ISM_ID packReverseISMGain, # R_ISM_GAIN packOrientation, # R_ISM_ORIENTATION diff --git a/tests/rtp/test_rtp.py b/tests/rtp/test_rtp.py index 4207e92364cba347e51208960a8a70206f2a2527..b8a542a4ed07d8ee271cb4f1643d4c8d30200f8a 100644 --- a/tests/rtp/test_rtp.py +++ b/tests/rtp/test_rtp.py @@ -228,7 +228,16 @@ def generatePiData(startTs: int, endTs: int) -> dict: ) someAuFocusLvl = lambda: AUDIO_FOCUS(level=AUDIO_FOCUS_LEVEL(random.randint(0, 15))) someAuFocusList = [someAuFocusDirLvl, someAuFocusDir, someAuFocusLvl] - + someLatency = lambda: PI_LATENCY( + reverseType=random.choice([ + "PLAYBACK_DEVICE_ORIENTATION", + "HEAD_ORIENTATION", + "LISTENER_POSITION", + "DYNAMIC_AUDIO_SUPPRESSION_REQUEST", + "AUDIO_FOCUS_REQUEST" + ]), + latency=random.randint(- (1 << 26), (1 << 26) - 1) + ) someAcousticEnvAEID = lambda: ACOUSTIC_ENVIRONMENT( aeid=random.randint(0, 127) ) @@ -286,6 +295,7 @@ def generatePiData(startTs: int, endTs: int) -> dict: pidata["AUDIO_DESCRIPTION"] = [someDesc() for n in range(random.randint(1, 5))] pidata["DIEGETIC_TYPE"] = someDIG() pidata["ACOUSTIC_ENVIRONMENT"] = random.choice(someAcousticEnvList)() + pidata["PI_LATENCY"] = someLatency() pidata["ISM_NUM"] = someNumISM() pidata["ISM_ID"] = someISMIds(pidata["ISM_NUM"].num) pidata["ISM_GAIN"] = someISMGains(pidata["ISM_NUM"].num) @@ -395,6 +405,12 @@ def isEqualAudioFocus(ref: AUDIO_FOCUS, dut: AUDIO_FOCUS): ), "Audio Focus PI Data mismatch in direction z" assert ref.level == dut.level, "Audio Focus PI Data mismatch in level" +def isEqualPILatency(ref: PI_LATENCY, dut: PI_LATENCY): + assert ref.reverseType == dut.reverseType, \ + f"PI_LATENCY type mismatch: {dut.reverseType} != {ref.reverseType}" + assert ref.latency == dut.latency, \ + f"PI_LATENCY latency mismatch: {dut.latency} != {ref.latency}" + def isEqualISMNum(ref: ISM_NUM, dut: ISM_NUM): assert ref.num == dut.num, "ISM NUM PI Data mismatch" @@ -725,6 +741,8 @@ def run_rtp_bitstream_tests( isEqualAcousticEnv(ACOUSTIC_ENVIRONMENT(**decoded), data) elif type(generatedPIData[ts][pitype]) == AUDIO_FOCUS: isEqualAudioFocus(AUDIO_FOCUS(**decoded), data) + elif type(generatedPIData[ts][pitype]) == PI_LATENCY: + isEqualPILatency(PI_LATENCY(**decoded), data) elif type(generatedPIData[ts][pitype]) == list: if pitype == "AUDIO_DESCRIPTION": for r, d in zip(generatedPIData[ts][pitype], decodedPiData[ts][pitype]):