From e568f54d6d6f05ec232fd6d3d5eb1b64fe8e7629 Mon Sep 17 00:00:00 2001 From: "Singh, Ripinder" Date: Thu, 3 Jul 2025 12:01:55 +1000 Subject: [PATCH 1/7] 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 2/7] 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 3/7] 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 4/7] 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 5/7] 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 6/7] 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 7/7] 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